[
  {
    "path": ".cz-config.js",
    "content": "'use strict';\nmodule.exports = {\n\n  types: [\n   {value: ':sparkles:',     name: '特性:    一个新的特性'},\n   {value: ':bug:',     name: '修复:    修复一个Bug'},\n   {value: ':books:',     name: '文档:    变更的只有文档'},\n   {value: ':racehorse:',     name: '性能:    提升性能'},\n   {value: ':rotating_light:',     name: '测试:    添加一个测试'},\n   {value: ':hammer:',     name: '回滚:    代码回退'},\n//   {value: ':tada:',     name: '回滚:    代码回退'}\n  ],\n\n  scopes: [\n    {name: 'app'},\n//    {name: 'Demo'},\n//    {name: 'Frame'},\n//    {name: 'KeepassApi'},\n//    {name: 'IBaseApi'},\n//    {name: 'HWImp'},\n//    {name: 'PlayImp'}\n  ],\n\n  allowTicketNumber: false,\n  isTicketNumberRequired: false,\n  ticketNumberPrefix: 'TICKET-',\n  ticketNumberRegExp: '\\\\d{1,5}',\n\n  // it needs to match the value for field type. Eg.: 'fix'\n  /*\n  scopeOverrides: {\n    fix: [\n      {name: 'merge'},\n      {name: 'style'},\n      {name: 'e2eTest'},\n      {name: 'unitTest'}\n    ]\n  },\n  */\n  // override the messages, defaults are as follows\n  messages: {\n    type:         '选择一种你的提交类型:',\n    scope:        '选择一个scope (可选):',\n    // used if allowCustomScopes is true\n    customScope:  'Denote the SCOPE of this change:',\n    subject:      '短说明:\\n',\n    body:         '长说明，使用\"|\"换行(可选)：\\n',\n    breaking:     '非兼容性说明 (可选):\\n',\n    footer:       '关联关闭的issue，例如：#31, #34(可选):\\n',\n    confirmCommit:'确定提交说明?'\n  },\n\n  allowCustomScopes: true,\n  allowBreakingChanges: ['特性', '修复'],\n  // skip any questions you want\n  skipQuestions: ['body'],\n\n  // limit subject length\n  subjectLimit: 100\n\n};"
  },
  {
    "path": ".gitignore",
    "content": "*.iml\n.gradle\n.idea\n/local.properties\n/.idea/caches\n/.idea/libraries\n/.idea/modules.xml\n/.idea/workspace.xml\n/.idea/navEditor.xml\n/.idea/assetWizardSettings.xml\n.DS_Store\n/build\n/captures\n.externalNativeBuild\n.cxx\napp/build\nFrame/build\nKeepassLib/build\nnode_modules\ncp_buglyQqUploadSymbolLib.jar\n/buglybin\nlocal.properties\n/VersionManager/build/\napp/google-services.json\n"
  },
  {
    "path": "KeepassA_privacy_policy.html",
    "content": "<!DOCTYPE html>\n<!-- saved from url=(0061)https://aria.laoyuyu.me/keepassA/KeepassA_privacy_policy.html -->\n<html><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\"><link rel=\"stylesheet\" type=\"text/css\" href=\"file:///Applications/WizNote.app/Contents/Resources/files/wizeditor/wizDarkMode.css\" name=\"wiz_dark_mode_style\" wiz_style=\"unsave\" charset=\"utf-8\"><style id=\"wiz_custom_css\">html, .wiz-editor-body {font-size: 12pt;}.wiz-editor-body {font-family: Helvetica, \"Hiragino Sans GB\", \"微软雅黑\", \"Microsoft YaHei UI\", SimSun, SimHei, arial, sans-serif;line-height: 1.7;margin: 0 auto;position:relative;padding: 20px 16px;padding: 1.25rem 1rem;}.wiz-editor-body h1,.wiz-editor-body h2,.wiz-editor-body h3,.wiz-editor-body h4,.wiz-editor-body h5,.wiz-editor-body h6 {margin:20px 0 10px;margin:1.25rem 0 0.625rem;padding: 0;font-weight: bold;}.wiz-editor-body h1 {font-size:20pt;font-size:1.67rem;}.wiz-editor-body h2 {font-size:18pt;font-size:1.5rem;}.wiz-editor-body h3 {font-size:15pt;font-size:1.25rem;}.wiz-editor-body h4 {font-size:14pt;font-size:1.17rem;}.wiz-editor-body h5 {font-size:12pt;font-size:1rem;}.wiz-editor-body h6 {font-size:12pt;font-size:1rem;color: #777777;margin: 1rem 0;}.wiz-editor-body div,.wiz-editor-body p,.wiz-editor-body ul,.wiz-editor-body ol,.wiz-editor-body dl,.wiz-editor-body li {margin:8px 0;}.wiz-editor-body blockquote,.wiz-editor-body table,.wiz-editor-body pre,.wiz-editor-body code {margin:8px 0;}.wiz-editor-body .CodeMirror pre {margin:0;}.wiz-editor-body a {word-wrap: break-word;text-decoration-skip-ink: none;}.wiz-editor-body ul,.wiz-editor-body ol {padding-left:32px;padding-left:2rem;}.wiz-editor-body ol.wiz-list-level1 > li {list-style-type:decimal;}.wiz-editor-body ol.wiz-list-level2 > li {list-style-type:lower-latin;}.wiz-editor-body ol.wiz-list-level3 > li {list-style-type:lower-roman;}.wiz-editor-body li.wiz-list-align-style {list-style-position: inside; margin-left: -1em;}.wiz-editor-body blockquote {padding: 0 12px;}.wiz-editor-body blockquote > :first-child {margin-top:0;}.wiz-editor-body blockquote > :last-child {margin-bottom:0;}.wiz-editor-body img {border:0;max-width:100%;height:auto !important;margin:2px 0;}.wiz-editor-body table {border-collapse:collapse;border:1px solid #bbbbbb;}.wiz-editor-body td,.wiz-editor-body th {padding:4px 8px;border-collapse:collapse;border:1px solid #bbbbbb;min-height:28px;word-break:break-word;box-sizing: border-box;}.wiz-editor-body td > div:first-child {margin-top:0;}.wiz-editor-body td > div:last-child {margin-bottom:0;}.wiz-editor-body img.wiz-svg-image {box-shadow:1px 1px 4px #E8E8E8;}.wiz-hide {display:none !important;}\nhtml, .wiz-editor-body {\n  font-family:'Helvetica Neue';\n  font-size:15px;\n  background-color:#272727;\n  line-height:1.7;\n} \nbody.wiz-editor-body div, body.wiz-editor-body p, body.wiz-editor-body ul, body.wiz-editor-body ol, body.wiz-editor-body dl, body.wiz-editor-body li {\n  margin-top:8px; margin-bottom:8px\n}</style><link rel=\"stylesheet\" charset=\"utf-8\" name=\"wiz_tmp_editor_style\" href=\"./KeepassA_privacy_policy_files/fonts.css\" id=\"wiz_/Applications/WizNote.app/Contents/Resources/files/wizeditor/dependency/fonts.css?v=1.0.40\"><style name=\"wiz_tmp_editor_style\" id=\"wiz_tmp_style_editor_common\">html {height:100%;} body, .wiz-editor-body {-webkit-tap-highlight-color:rgba(255,255,255,0);}.wiz-editor-body {min-height:100%;box-sizing:border-box;word-wrap: break-word !important;outline:none;}.wiz-editor-body img::selection {background-color: rgba(0, 0, 255, 0.3);}.wiz-editor-body input:disabled, .wiz-editor-body textarea:disabled {opacity: 1;}a {cursor:pointer;}::-ms-clear,::-ms-reveal{display:none;}wiz_tmp_tag {line-height: 1.7;}.wiz-editor-body .wiz-table-container {border:0px !important;}.wiz-editor-body .wiz-table-body {border:0px !important;position:relative;overflow-x:auto;overflow-y:hidden;-webkit-overflow-scrolling:touch;outline:none;}.wiz-editor-body .wiz-table-body table {margin:0;outline:none;}.wiz-editor-body td,.wiz-editor-body th {outline:none;}.wiz-editor-body .wiz-select-plugin-container {position:relative;display:inline-block;width:160px;height:28px;border-radius:4px;padding:0;margin-left:5px;cursor:pointer;}.wiz-editor-body .wiz-select-plugin-container.more {width:28px;}.wiz-editor-body .wiz-select-plugin-container, .wiz-editor-body .wiz-select-plugin-options {box-sizing:border-box;background:white;border:1px solid #e7e7e7;color:#333;box-shadow: 1px 1px 5px #d0d0d0;}.wiz-editor-body .wiz-select-plugin-header {line-height:28px;font-size:14px;padding: 0 0 0 5px;overflow:hidden;margin-right:27px;white-space:nowrap;}.wiz-editor-body .wiz-select-plugin-header i {position:absolute;top:0;right:0;line-height:26px;padding:0;text-align:center;min-width:27px;border-left:1px solid #e7e7e7;box-sizing:border-box;}.wiz-editor-body .wiz-select-plugin-header i.icon-more {font-size:16px;border:0;width:100%;height:100%}.wiz-editor-body .wiz-select-plugin-options {display:none;background:white;list-style:none;padding:0;margin:0;white-space:nowrap;max-height:200px;min-width:160px;max-width:260px;overflow-x:hidden;overflow-y:auto;position:absolute;top:30px;right:-1px;}.wiz-editor-body .wiz-select-plugin-options-item {font-size:14px;padding:2px 20px;line-height:normal;margin-top: 8px !important;}.wiz-editor-body .wiz-select-plugin-options-item.disabled {font-size:12px; color:#aaaaaa;}.wiz-editor-body .wiz-select-plugin-options-item.selected {background:#448aff;color:white;}.wiz-editor-body .wiz-select-plugin-options-item:hover {background:#448aff !important;color:white !important;}.wiz-editor-body .wiz-select-plugin-options-item .icon-checkmark {visibility: hidden}.wiz-editor-body .wiz-select-plugin-options-item .icon-checkmark.active {visibility: visible}.wiz-editor-body .wiz-select-plugin-container.active .wiz-select-plugin-options {display:block;}.wiz-editor-body .wiz-code-container-paste textarea {visibility: hidden;}.wiz-img-resize-handle {position: absolute;z-index: 1000;border: 1px solid black;background-color: white;}.wiz-img-resize-handle {width:5px;height:5px;margin:0 !important;}.wiz-img-resize-handle.lt {cursor: nw-resize;}.wiz-img-resize-handle.tm {cursor: n-resize;}.wiz-img-resize-handle.rt {cursor: ne-resize;}.wiz-img-resize-handle.lm {cursor: w-resize;}.wiz-img-resize-handle.rm {cursor: e-resize;}.wiz-img-resize-handle.lb {cursor: sw-resize;}.wiz-img-resize-handle.bm {cursor: s-resize;}.wiz-img-resize-handle.rb {cursor: se-resize;}.wiz-editor-body .wiz-tooltip i.editor-icon {top:0;}.wiz-editor-body .wiz-tooltip {margin: 0;position: absolute;display: none;min-width: 125px;padding: 5px 0;background: #fff;border-radius: 3px;border: 1px solid #E0E0E0;top:28px;left:-9px;box-shadow: 1px 1px 5px #d0d0d0;max-width: none;}.wiz-editor-body .wiz-tooltip.active {display:block;}.wiz-editor-body .wiz-tooltip > div {font-size:15px;min-width:63px;}.wiz-editor-body .wiz-tooltip div {margin:0;}.wiz-editor-body .wiz-tooltip .wiz-tooltip-arrow-bg,.wiz-editor-body .wiz-tooltip .wiz-tooltip-arrow {position: absolute;border-style: solid;border-color: transparent;border-bottom-color: #cccccc;left: 22px;margin-left: -14px;top: -8px;border-width: 0 8px 8px 8px;z-index:10;}.wiz-editor-body .wiz-tooltip .wiz-tooltip-arrow {border-bottom-color: #ffffff;top: -7px;}.wiz-editor-body .wiz-tooltip.top .wiz-tooltip-arrow-bg {top: auto;bottom: -8px;transform:rotate(180deg);}.wiz-editor-body .wiz-tooltip.top .wiz-tooltip-arrow {top: auto;bottom: -7px;transform:rotate(180deg);}.wiz-editor-body .wiz-tooltip-item {margin:0;padding: 4px 12px;font-size: 14px;}.wiz-editor-body .wiz-tooltip-item.split {border-top: 1px solid #E0E0E0;}.wiz-editor-body .wiz-tooltip-item:hover {background-color: #ececec;}.wiz-editor-body .wiz-tooltip-item.disabled {color: #bbbbbb;cursor: default;}.wiz-editor-body .wiz-tooltip-item.disabled:hover {background-color: transparent;}.wiz-editing .wiz-table-body.wiz-table-moving *, .wiz-editing .wiz-table-body.wiz-table-moving *:before, .wiz-editing .wiz-table-body.wiz-table-moving *:after {cursor:default !important;} .wiz-editing.wiz-editor-body td, .wiz-editing.wiz-editor-body th {position:relative;}#wiz-table-range-border {display: none;width: 0;height: 0;position: absolute;top: 0;left: 0; z-index:105}#wiz-table-col-line, #wiz-table-row-line {margin:0;display: none;background-color: #448aff;position: absolute;z-index:120;}#wiz-table-col-line {width: 1px;cursor:col-resize;}#wiz-table-row-line {height: 1px;cursor:row-resize;}#wiz-table-range-border_start, #wiz-table-range-border_range {display: none;width: 0;height: 0;margin: 0;position: absolute;}#wiz-table-range-border_start div, #wiz-table-range-border_range div {margin: 0;}#wiz-table-range-border_start_top, #wiz-table-range-border_range_top {height: 2px;background-color: #448aff;position: absolute;top: 0;left: 0;}#wiz-table-range-border_range_top {height: 1px;}#wiz-table-range-border_start_right, #wiz-table-range-border_range_right {width: 2px;background-color: #448aff;position: absolute;top: 0;}#wiz-table-range-border_range_right {width: 1px;}#wiz-table-range-border_start_bottom, #wiz-table-range-border_range_bottom {height: 2px;background-color: #448aff;position: absolute;top: 0;}#wiz-table-range-border_range_bottom {height: 1px;}#wiz-table-range-border_start_left, #wiz-table-range-border_range_left {width: 2px;background-color: #448aff;position: absolute;top: 0;left: 0;}#wiz-table-range-border_range_left {width: 1px;}#wiz-table-range-border_start_dot, #wiz-table-range-border_range_dot {width: 5px;height: 5px;border: 2px solid rgb(255, 255, 255);background-color: #448aff;cursor: crosshair;position: absolute;z-index:110;}.wiz-table-tools {display: block;background-color:#fff;position: absolute;left: 0px;border: 1px solid #ddd;-webkit-border-radius: 5px;-moz-border-radius: 5px;border-radius: 5px;z-index:130;}.wiz-table-tools ul {margin:0 !important;}.wiz-table-tools ul {list-style: none !important;padding: 0;width:auto;}.wiz-table-tools .wiz-table-menu-item {position: relative;float: left;margin:4px 2px 2px 8px;clear: initial;text-align: left;}.wiz-table-tools .wiz-table-menu-item .wiz-table-menu-button {margin:0; font-size:15px;width:20px;height:20px;line-height:20px;cursor: pointer;position:relative;}.wiz-table-tools i.editor-icon{font-size: 15px;color: #455a64; position: absolute;top: 1px;left: 0;z-index:1}.wiz-editor-body .wiz-table-menu-item.active .wiz-tooltip {display: block}.wiz-table-tools .wiz-table-menu-item .wiz-table-menu-button i#wiz-menu-bg-demo{position: absolute;top:1px;left:0;}.wiz-table-tools .wiz-table-menu-item.wiz-table-cell-bg:hover .wiz-table-color-pad {display: block;}.wiz-table-tools .wiz-table-color-pad {display: none;padding: 10px;box-sizing: border-box;min-width: 85px;height: 88px;background-color: #fff;cursor: default;}.wiz-table-tools .wiz-table-color-pad > div{font-size:15px;}.wiz-table-tools .wiz-table-color-pad .wiz-table-color-pad-item {display: inline-block;width: 15px;height: 15px;margin-right: 9px;position: relative;}.wiz-table-tools .wiz-table-color-pad .wiz-table-color-pad-item .icon-oblique_line{color: #cc0000;}.wiz-table-tools .wiz-table-color-pad .wiz-table-color-pad-item:last-child {margin-right: 0;}.wiz-table-tools .wiz-table-color-pad .wiz-table-color-pad-item.active i.editor-icon.icon-box {color: #448aff;}.wiz-table-tools .wiz-table-cell-align {display: none;padding: 10px;box-sizing: border-box;min-width: 85px;height: 65px;background-color: #fff;cursor: default;}.wiz-table-tools .wiz-table-cell-align .wiz-table-cell-align-item {display: inline-block;width: 15px;height: 15px;margin-right: 9px;position: relative;}.wiz-table-tools .wiz-table-cell-align .wiz-table-cell-align-item[data-align-type=align] {margin-right:8px}.wiz-table-tools .wiz-table-cell-align .wiz-table-cell-align-item:last-child {margin-right:0}.wiz-table-tools .wiz-table-cell-align .wiz-table-cell-align-item i.valign{color: #d2d2d2;}.wiz-table-tools .wiz-table-cell-align-item i.editor-icon.align {font-size:17px;}.wiz-table-tools .wiz-table-cell-align-item.active i.editor-icon.valign {color: #a1c4ff;}.wiz-table-tools .wiz-table-cell-align-item.active i.editor-icon.icon-box,.wiz-table-tools .wiz-table-cell-align-item.active i.editor-icon.align {color: #448aff;}.wiz-table-tools .wiz-table-color-pad .wiz-table-color-pad-item:last-child,.wiz-table-tools .wiz-table-cell-align .wiz-table-cell-align-item:last-child {margin-right: 0;}.wiz-editing.wiz-editor-body th.wiz-selected-cell-multi,.wiz-editing.wiz-editor-body td.wiz-selected-cell-multi {background: rgba(0,102,255,.05);}.wiz-editing.wiz-editor-body th:before,.wiz-editing.wiz-editor-body td:before,#wiz-table-col-line:before,#wiz-table-range-border_start_right:before,#wiz-table-range-border_range_right:before {content: \" \";position: absolute;top: 0;bottom: 0;right: -5px;width: 9px;cursor: col-resize;background: transparent;z-index:100;}.wiz-editing.wiz-editor-body th:after,.wiz-editing.wiz-editor-body td:after,#wiz-table-row-line:before,#wiz-table-range-border_start_bottom:before,#wiz-table-range-border_range_bottom:before {content: \" \";position: absolute;left: 0;right: 0;bottom: -5px;height: 9px;cursor: row-resize;background: transparent;z-index:100;}</style><style name=\"wiz_tmp_editor_style\" id=\"wiz_tmp_style_editor_block_scroll\">.wiz-block-scroll::-webkit-scrollbar {width: 7px;height: 7px;}.wiz-block-scroll::-webkit-scrollbar-thumb {background-color: #7f7f7f;border-radius: 7px;}.wiz-block-scroll::-webkit-scrollbar-button {display: none;}</style><style name=\"wiz_tmp_editor_style\" id=\"wiz-tmp-bottom-style\">.wiz-editing.wiz-editor-body {margin-bottom: 99px;}</style><style name=\"wiz_tmp_editor_style\" id=\"wiz_tmp_style_reader_common\">html {height:100%;} body, .wiz-editor-body {-webkit-tap-highlight-color:rgba(255,255,255,0);}.wiz-editor-body {min-height:100%;box-sizing:border-box;word-wrap: break-word !important;outline:none;}.wiz-editor-body img::selection {background-color: rgba(0, 0, 255, 0.3);}.wiz-editor-body input:disabled, .wiz-editor-body textarea:disabled {opacity: 1;}a {cursor:pointer;}::-ms-clear,::-ms-reveal{display:none;}wiz_tmp_tag {line-height: 1.7;}.wiz-editor-body .wiz-table-container {border:0px !important;}.wiz-editor-body .wiz-table-body {border:0px !important;position:relative;overflow-x:auto;overflow-y:hidden;-webkit-overflow-scrolling:touch;outline:none;}.wiz-editor-body .wiz-table-body table {margin:0;outline:none;}.wiz-editor-body td,.wiz-editor-body th {outline:none;}.wiz-editor-body .wiz-select-plugin-container {position:relative;display:inline-block;width:160px;height:28px;border-radius:4px;padding:0;margin-left:5px;cursor:pointer;}.wiz-editor-body .wiz-select-plugin-container.more {width:28px;}.wiz-editor-body .wiz-select-plugin-container, .wiz-editor-body .wiz-select-plugin-options {box-sizing:border-box;background:white;border:1px solid #e7e7e7;color:#333;box-shadow: 1px 1px 5px #d0d0d0;}.wiz-editor-body .wiz-select-plugin-header {line-height:28px;font-size:14px;padding: 0 0 0 5px;overflow:hidden;margin-right:27px;white-space:nowrap;}.wiz-editor-body .wiz-select-plugin-header i {position:absolute;top:0;right:0;line-height:26px;padding:0;text-align:center;min-width:27px;border-left:1px solid #e7e7e7;box-sizing:border-box;}.wiz-editor-body .wiz-select-plugin-header i.icon-more {font-size:16px;border:0;width:100%;height:100%}.wiz-editor-body .wiz-select-plugin-options {display:none;background:white;list-style:none;padding:0;margin:0;white-space:nowrap;max-height:200px;min-width:160px;max-width:260px;overflow-x:hidden;overflow-y:auto;position:absolute;top:30px;right:-1px;}.wiz-editor-body .wiz-select-plugin-options-item {font-size:14px;padding:2px 20px;line-height:normal;margin-top: 8px !important;}.wiz-editor-body .wiz-select-plugin-options-item.disabled {font-size:12px; color:#aaaaaa;}.wiz-editor-body .wiz-select-plugin-options-item.selected {background:#448aff;color:white;}.wiz-editor-body .wiz-select-plugin-options-item:hover {background:#448aff !important;color:white !important;}.wiz-editor-body .wiz-select-plugin-options-item .icon-checkmark {visibility: hidden}.wiz-editor-body .wiz-select-plugin-options-item .icon-checkmark.active {visibility: visible}.wiz-editor-body .wiz-select-plugin-container.active .wiz-select-plugin-options {display:block;}</style><style name=\"wiz_tmp_editor_style\" id=\"wiz_tmp_style_reader_block_scroll\">.wiz-block-scroll::-webkit-scrollbar {width: 7px;height: 7px;}.wiz-block-scroll::-webkit-scrollbar-thumb {background-color: #7f7f7f;border-radius: 7px;}.wiz-block-scroll::-webkit-scrollbar-button {display: none;}</style><link rel=\"stylesheet\" charset=\"utf-8\" name=\"wiz_tmp_editor_style\" href=\"./KeepassA_privacy_policy_files/github2.css\" id=\"wiz_/Applications/WizNote.app/Contents/Resources/files/wizeditor/dependency/github2.css?v=1.0.40\"><link rel=\"stylesheet\" charset=\"utf-8\" name=\"wiz_tmp_editor_style\" href=\"./KeepassA_privacy_policy_files/wizToc.css\" id=\"wiz_/Applications/WizNote.app/Contents/Resources/files/wizeditor/dependency/wizToc.css?v=1.0.40\"><style name=\"wiz_tmp_editor_style\" id=\"wiz_tmp_style_code_common\">.wiz-code-tools {display:none; align-items: center;position: absolute; top: -32px; right: 0; opacity: .95; z-index: 110;}.wiz-editor-body .wiz-code-container .wiz-code-tools {opacity:0;display:flex;transition: opacity 500ms;}.wiz-editor-body .wiz-code-container:hover .wiz-code-tools {opacity:1; }.wiz-editing .CodeMirror-show-tools .wiz-code-tools {z-index: 220;}.wiz-readonly .wiz-code-tools.mobile,.wiz-editing .CodeMirror-show-tools .wiz-code-tools,.wiz-editing .CodeMirror-focused .wiz-code-tools {opacity:1; display: flex;}.CodeMirror-sizer {border-right: 0 !important;}.wiz-editor-body pre.prettyprint {padding:0;}.wiz-editor-body pre.prettyprint code {white-space: pre-wrap;}.wiz-editor-body pre.prettyprint.linenums {box-shadow:none; overflow: auto;-webkit-overflow-scrolling: touch;}.wiz-editor-body pre.prettyprint.linenums ol.linenums {box-shadow: 40px 0 0 #FBFBFC inset, 41px 0 0 #ECECF0 inset; padding: 10px 10px 10px 40px !important;}.wiz-editing .CodeMirror-activeline-background {display: none;}.wiz-editing .CodeMirror-focused .CodeMirror-activeline-background {display: block;}</style><style name=\"wiz_tmp_editor_style\" id=\"wiz_tmp_style_code_reader\">.wiz-readonly .CodeMirror-cursors {visibility: hidden !important;}</style><style name=\"wiz_tmp_editor_style\" id=\"wiz_night_mode_style\">div, p, .wiz\\-editor\\-body, .wiz\\-readonly, .markdown\\-body, body, br, .wiz\\-list\\-level1, ol, li{color:#a6a6a6 !important; background-color:#272727 !important; background-image: none !important; box-shadow: none !important; border-color:#a6a6a6 !important; }iframe#wiz-content-iframe-viewer {filter:none;-webkit-filter:none;}wiz_tmp_highlight_tag{background-color: #50a9fb !important;  color: black !important;}a, a:visited, a:active {color: #448aff !important;}div.wiz-code-container .CodeMirror {border: 1px solid rgba(255, 255, 255, 0.2) !important; box-shadow: 0px 3px 6px rgba(0, 0, 0, 0.2) !important;}div.CodeMirror-selected {background-color: #49483E !important;}.wiz-editor-body .wiz_toc_layer {background-color: #2e2e2e !important;}.wiz-editor-body .wiz_toc_layer a, .wiz-editor-body .wiz_toc_layer a:visited, .wiz-editor-body .wiz_toc_layer a:active {background-color: #2e2e2e !important; color: #448aff !important;}.wiz-editor-body .wiz-tooltip, .wiz-table-tools .wiz-table-color-pad, .wiz-table-tools .wiz-table-cell-align {background-color: #22272F !important; border: solid 1px #3E495B !important; box-shadow: none;}.wiz-editor-body .wiz-tooltip .wiz-tooltip-arrow-bg {border-color: transparent !important;border-bottom-color: #3E495B !important;background-color: transparent !important;}.wiz-editor-body .wiz-tooltip .wiz-tooltip-arrow {display: none !important;}.wiz-editor-body .wiz-tooltip div {background-color: transparent !important;}.wiz-editor-body .wiz-tooltip input, .wiz-editor-body .wiz-tooltip-item {color: #97A3B8 !important;}.wiz-editor-body .wiz-tooltip-item {background-color: #22272F !important;}.wiz-editor-body .wiz-tooltip-item:hover {background-color: #2E353F !important;}.wiz-editor-body .wiz-tooltip-item.disabled {color: #484E5C !important;}.wiz-editor-body .wiz-tooltip-item.split {border-top-color: #3E495B !important;}.wiz-editor-body .wiz-tooltip-item.disabled:hover, .wiz-editor-body .wiz-tooltip > div, .wiz-table-tools .wiz-table-menu-item, .wiz-table-tools .wiz-table-menu-item .wiz-table-menu-button, .wiz-table-tools .wiz-table-cell-align .wiz-table-cell-align-item, .wiz-table-tools .wiz-table-color-pad .wiz-table-color-pad-item {background-color: transparent !important;}.wiz-table-tools {background-color:#22272F !important; border-color: #3E495B !important;}.wiz-table-tools i.editor-icon {color: #97A3B8;}#wiz-table-col-line, #wiz-table-row-line, #wiz-table-range-border_start_top, #wiz-table-range-border_range_top, #wiz-table-range-border_start_right, #wiz-table-range-border_range_right, #wiz-table-range-border_start_bottom, #wiz-table-range-border_range_bottom, #wiz-table-range-border_start_left, #wiz-table-range-border_range_left,#wiz-table-range-border_start_dot, #wiz-table-range-border_range_dot  {background-color: #448aff !important;}.markdown-body table tr {background-color:transparent !important;}.markdown-body code, .markdown-body tt {background-color:#4c3d40 !important; color:#cf506f !important;}.wiz-editor-body .wiz-select-plugin-header  {background-color: transparent !important;}.wiz-editor-body .wiz-select-plugin-container, .wiz-editor-body .wiz-select-plugin-options, .wiz-editor-body .wiz-select-plugin-options-item, .wiz-editor-body .wiz-select-plugin-header-text  {background-color:#22272F !important; border-color: #3E495B !important;color: #97A3B8 !important;box-shadow:none;}.wiz-editor-body .wiz-select-plugin-header  {color: #97A3B8 !important;}.wiz-editor-body .wiz-select-plugin-options .wiz-select-plugin-options-item.selected, .wiz-editor-body .wiz-select-plugin-options .wiz-select-plugin-options-item:hover  {background-color: #2E353F !important;}.wiz-editor-body .wiz-select-plugin-header i  {border-left-color: #3E495B !important;}.wiz-editor-body .wiz-select-plugin-options .wiz-select-plugin-options-item div, .wiz-editor-body .wiz-select-plugin-options .wiz-select-plugin-options-item span  {background-color: transparent !important;}.wiz-editor-body .wiz-select-plugin-options .wiz-select-plugin-options-item:hover div  {color: #FFFFFF !important;}.wiz-editor-body .CodeMirror-activeline .CodeMirror-activeline-gutter, .wiz-editor-body #wiz-painter-root, .wiz-editor-body #wiz-painter-root div  {background-color: transparent !important;}.wiz-editor-body #wiz-painter-root svg *[stroke=\"#333333\"]  {stroke:#a6a6a6;}.wiz-content-outer  {border-color: transparent !important;}</style><style type=\"text/css\">.simpread-theme-root{font-size:62.5%!important}sr-rd-content,sr-rd-desc,sr-rd-title{width:100%}sr-rd-title{display:-webkit-box;margin:1em 0 .5em;overflow:hidden;text-overflow:ellipsis;text-rendering:optimizelegibility;-webkit-line-clamp:3;-webkit-box-orient:vertical}sr-rd-content{text-align:left;word-break:break-word}sr-rd-desc{text-align:justify;line-height:2.4;margin:0 0 1.2em;box-sizing:border-box}sr-rd-content{font-size:25.6px;font-size:1.6rem;line-height:1.6}sr-rd-content h1,sr-rd-content h1 *,sr-rd-content h2,sr-rd-content h2 *,sr-rd-content h3,sr-rd-content h3 *,sr-rd-content h4,sr-rd-content h4 *,sr-rd-content h5,sr-rd-content h5 *,sr-rd-content h6,sr-rd-content h6 *{word-break:break-all}sr-rd-content div,sr-rd-content p{display:block;float:inherit;line-height:1.6;font-size:25.6px;font-size:1.6rem}sr-rd-content div,sr-rd-content p,sr-rd-content pre,sr-rd-content sr-blockquote{margin:0 0 1.2em;word-break:break-word}sr-rd-content a{padding:0 5px;vertical-align:baseline;vertical-align:initial}sr-rd-content a,sr-rd-content a:link{color:inherit;font-size:inherit;font-weight:inherit;border:none}sr-rd-content a:hover{background:transparent}sr-rd-content img{margin:10px;padding:5px;max-width:100%;background:#fff;border:1px solid #bbb;box-shadow:1px 1px 3px #d4d4d4}sr-rd-content figcaption{text-align:center;font-size:14px}sr-rd-content sr-blockquote{display:block;position:relative;padding:15px 25px;text-align:left;line-height:inherit}sr-rd-content sr-blockquote:before{position:absolute}sr-rd-content sr-blockquote *{margin:0;font-size:inherit}sr-rd-content table{width:100%;margin:0 0 1.2em;word-break:keep-all;word-break:normal;overflow:auto;border:none}sr-rd-content table td,sr-rd-content table th{border:none}sr-rd-content ul{margin:0 0 1.2em;margin-left:1.3em;padding:0;list-style:disc}sr-rd-content ol{list-style:decimal;margin:0;padding:0}sr-rd-content ol li,sr-rd-content ul li{font-size:inherit;list-style:disc;margin:0 0 1.2em}sr-rd-content ol li{list-style:decimal;margin-left:1.3em}sr-rd-content ol li *,sr-rd-content ul li *{margin:0;text-align:left;text-align:initial}sr-rd-content li ol,sr-rd-content li ul{margin-bottom:.8em;margin-left:2em}sr-rd-content li ul{list-style:circle}sr-rd-content pre{font-family:Consolas,Monaco,Andale Mono,Source Code Pro,Liberation Mono,Courier,monospace;display:block;padding:15px;line-height:1.5;word-break:break-all;word-wrap:break-word;white-space:pre;overflow:auto}sr-rd-content pre,sr-rd-content pre *,sr-rd-content pre div{font-size:17.6px;font-size:1.1rem}sr-rd-content li pre code,sr-rd-content p pre code,sr-rd-content pre{background-color:transparent;border:none}sr-rd-content pre code{margin:0;padding:0}sr-rd-content pre code,sr-rd-content pre code *{font-size:17.6px;font-size:1.1rem}sr-rd-content pre p{margin:0;padding:0;color:inherit;font-size:inherit;line-height:inherit}sr-rd-content li code,sr-rd-content p code{margin:0 4px;padding:2px 4px;font-size:17.6px;font-size:1.1rem}sr-rd-content mark{margin:0 5px;padding:2px;background:#fffdd1;border-bottom:1px solid #ffedce}.sr-rd-content-img{width:90%;height:auto}.sr-rd-content-img-load{width:48px;height:48px;margin:0;padding:0;border-style:none;border-width:0;background-repeat:no-repeat;background-image:url(data:image/gif;base64,R0lGODlhMAAwAPcAAAAAABMTExUVFRsbGx0dHSYmJikpKS8vLzAwMDc3Nz4+PkJCQkRERElJSVBQUFdXV1hYWFxcXGNjY2RkZGhoaGxsbHFxcXZ2dnl5eX9/f4GBgYaGhoiIiI6OjpKSkpaWlpubm56enqKioqWlpampqa6urrCwsLe3t7q6ur6+vsHBwcfHx8vLy8zMzNLS0tXV1dnZ2dzc3OHh4eXl5erq6u7u7vLy8vf39/n5+f///wEBAQQEBA4ODhkZGSEhIS0tLTk5OUNDQ0pKSk1NTV9fX2lpaXBwcHd3d35+foKCgoSEhIuLi4yMjJGRkZWVlZ2dnaSkpKysrLOzs7u7u7y8vMPDw8bGxsnJydvb293d3eLi4ubm5uvr6+zs7Pb29gYGBg8PDyAgICcnJzU1NTs7O0ZGRkxMTFRUVFpaWmFhYWVlZWtra21tbXNzc3V1dXh4eIeHh4qKipCQkJSUlJiYmJycnKampqqqqrW1tcTExMrKys7OztPT09fX19jY2Ojo6PPz8/r6+hwcHCUlJTQ0NDg4OEFBQU9PT11dXWBgYGZmZm9vb3Jycnp6en19fYCAgIWFhaurq8DAwMjIyM3NzdHR0dTU1ODg4OTk5Onp6fDw8PX19fv7+xgYGB8fHz8/P0VFRVZWVl5eXmpqanR0dImJiaCgoKenp6+vr9/f3+fn5+3t7fHx8QUFBQgICBYWFioqKlVVVWJiYo+Pj5eXl6ioqLa2trm5udbW1vT09C4uLkdHR1FRUVtbW3x8fJmZmcXFxc/Pz42Njb+/v+/v7/j4+EtLS5qamri4uL29vdDQ0N7e3jIyMpOTk6Ojo7GxscLCwisrK1NTU1lZWW5ubkhISAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh/i1NYWRlIGJ5IEtyYXNpbWlyYSBOZWpjaGV2YSAod3d3LmxvYWRpbmZvLm5ldCkAIfkEAAoA/wAsAAAAADAAMAAABv/AnHBILBqPyKRySXyNSC+mdFqEAAARqpaIux0dVwduq2VJLN7iI3ys0cZkosogIJSKODBAXLzJYjJpcTkuCAIBDTRceg5GNDGAcIM5GwKWHkWMkjk2kDI1k0MzCwEBCTBEeg9cM5AzoUQjAwECF5KaQzWQMYKwNhClBStDjEM4fzGKZCxRRioFpRA2OXlsQrqAvUM300gsCgofr0UWhwMjQhgHBxhjfpCgeDMtLtpCOBYG+g4lvS8JAQZoEHKjRg042GZsylHjBYuHMY7gyHBAn4EDE1ZI8tCAhL1tNLoJsQGDxYoVEJHcOPHAooEEGSLmKKjlWIuHKF/ES0IjxAL/lwxCfFRCwwVKlC4UTomxIYFFaVtKomzBi8yKCetMkKnxEIZIMjdKdBi6ZIYyWAthSZGUVu0RGRsyyJ07V0SoGC3yutCrN40KcIADK6hAlgmLE4hNIF58QlmKBYIDV2g75bBixouVydCAAUOGzp87h6AsBQa9vfTy0uuFA86Y1m5jyyaDQwUJ0kpexMC95AWHBw9YkJlBYoSKs1RmhJDgoIGDDIWN1BZBvUSLr0psmKDgoLuDCSZ4G4FhgrqIESZeFMbBAsOD7g0ifJBxT7wkGyxImB+Bgr7EEA8418ADGrhARAodtKCEDNYRQYNt+wl3RAfNOWBBCr3MkMEEFZxg3YwkLXjQQQg7URPDCSNQN8wRMEggwQjICUECBRNQoIIQKYAAQgpCvOABBx2ksNANLpRQQolFuCBTETBYQOMHaYxwwQV2UVMCkPO1MY4WN3wwwQQWNJPDCJ2hI4QMH3TQQXixsVDBlyNIIiUGZuKopgdihmLDBjVisOWYGFxQJ0MhADkCdnGcQCMFHsZyAQZVDhEikCtOIsMFNXKAHZmQ9kFCBxyAEGNUmFYgIREiTDmoEDCICMKfccQAgghpiRDoqtSkcAKsk7RlK51IiAcLCZ2RMJsWRbkw6rHMFhEEACH5BAAKAP8ALAAAAAAwADAAAAf/gDmCg4SFhoeIiYqLhFhRUViMkpOFEwICE5SahDg4hjgSAQJEh16em4ctRklehkQBAaSFXhMPVaiFVwoGPyeFOK+xp4MkOzoCVLiDL7sGEF2cwbKDW0A6Oj0tyoNOBt5PhUQCwoRL1zpI29QO3gxZhNLDLz7XP1rqg1E/3kmDwLDTcBS5tgMcPkG0vCW4MkjaICoBrgmxgcrFO0NWEnib0OofORtDrvGYcqhTIhcOHIjgYgiJtx9RcuBQEiSIEkFPjOnIZMiGFi3DCiVRQFTClFaDsDDg1UQQDhs2kB4x1uPFrC1ZsrL8tCQIUQVBMLgY9uSBFKSGvEABwoSQFy5Z/7NqgVZqygSvRIU0uSeTrqIuSHF00RI3yxa0iLqIePBVwYMoQSX5LKyF4qQsTIR8NYJYEla5XSIzwnHFSBAGtzZ5IcylsyYvJ564lmz5oO3buAttabKEie/fS5bE3LYFi/Hjx7MgtZKyefMhQzCIpvTiipUr2LNjp8vcuXck0ydVt649O90tTIIrUbKEfXsS4T0jn6+ck0x/8XPr34/Dyon8iRimDhZOFFGBC6hwMcUULfhFCRckGFHEBEUwAeAvLUhxwglUYDFbXRgUMeEEGExxYSFaULHhhlUApQgOLSwh4gQTGCECXyYtMowNL6i44hVcTIcDCRXQOEEFTVg1SPAVT0SSyBZVKClIFy1MIYWGUzhpyBM0FpGEFYhxscQRSKTmiTwkiCBFbTJt4d+GCB6CxRFHROGgTFLQiYQ2OVxBAgkM5ZAFFCKIECgnWVBBBZuFvMBXIVkkcQQGIpwiRXBSOFVFoSRsVYgNd0qCwxMYHJHERTlcykSmgkBYaBUnStICEhhgIMUwly7BqiBXFAoFqurY0ASdS3iaam+75mCDFIWe8KEmVJSKQWqD5JpsDi8QCoWUymwxJgZOMGrtL1QUaqc6WShBJreCjItimlEYi4sWUNxqiLu5WCHvNtPhu98iJ/hG0r+MdGFcqAQTHAgAIfkEAAoA/wAsAAAAADAAMAAACP8AcwgcSLCgwYMIEypcSDALHjxZGEqcWNCNAQNvKGokGCjQQTYX2Ry84XHjQT4a5JQk2CakwRtu1OQxWXCPAwVlqhQMBNJAm5UCoxAIcEAnTYF+bipYU4NjSwNsgP5pEIAon6MD6yjYeqdgzzYF5QgIIAAO1oF/0mxFI4NgT5ED/YypuqDtWYFSFmyVMzDQ06gCA7kZO8DO3YGA2mw1c1Xg24FVxIxFA8hkH7sF9TTY+uZGDr8XweYAhKaqGCoH96BG2CeNmihNOTLZugCFQCYOHDARaGcAWdEEZ2QYIMCoQTlmcrep4nlgljM4RQQGBKi5Bt9j+hAEVAcBgO9ngAb/pnMmt4MzcLQPtMOmiviBN6KU4RuYSoMv3wF8UdN8ZxU35jkQAR0zCHRDZQvVUFIfaoCRHwBk3PEeQTVEoUaAa+AxYUI3xEHAg2HE8cdEM8yBRm5mZNCfRDWQkR8Ya6inEUoOoKGHSXZ88UUDVGzI0A0oSGgSIG/UseJhG/k4kZJIolUHHXQ8CeWUGmIFyB9YZvlHDVuWpMcaa6ihRphgihkHkwr9kcWabLbZ3B5hihnnmGowgWZCM7SpZxYIzkDHHHP8CeigUpzFpZaIirfSnU026ihHexi30QyxHZVFHW9k4IdJNeyhhx8IalSDFHC8YWodjA7Uhx6s7iEDozdU/8HEG26YGoekE/3hKat68FGgQoHwMYeptGogxYiBaXRDFp7mwSqoCAUiRQbEZiBCRAPtIQW2CP2hB2aj+cErq+ASZAexcuwBVA11MJFuXytlgQIezBX0x6qscltQFnDEQUWoA1HBhLvq8YECCurNMC8Km+40wx57HNnQrwXJMMfAUngUSBUiiGBUIHs8REWl2wG8pBRMxDEHZhx7XFINVOCBgrpN9iHHwJK2LGkfD6FA8Vk32DFwHSTrTNANMeOhR6oJ6THwuwQZ3VDP+tL0Bx0D33Gk1H3p8VAVJm8kA9ZyVJ0DFR3jmoPCUox81x94rFYQx3WonYMffIR91IRcPxHKUB522DGT3xIBsqbehCceEAAh+QQACgD/ACwAAAAAMAAwAAAI/wBzCBxIsKDBgwgTKlxI8BIVSZcYSpxIkNMjBQo4UNxYkNNBRxgfHdzkkeNBLB3qlBzIqRFGRwY5OVpEyWRBS4kcPJjU0aUCmAXxIDCggKdNgVkQOXDgSFNFn0AHdkFjgKilowOhLHUgpaBPkQTrVDUwB+vATIuWrsHE8itBLAyqOmBrViCVpYfqEITK8lHVH13rCtz0aCmiqzlahhy4olBVRU45YqFbsBKapZA8KlYAdtOaqoRWHKwkaWVBLG7c4IlMcI6DQw8kCQSxaI0IgSV+VI06EBOHHz9EHwShqDikSaYvKYIdSSAnkiU76GaAheAmKIYECAigyLRzKGuKK/9aMwfLyhKOkCPcJOWBXueS0AgKEECAIEbenU+CFL44IyiZOLcJQ5oMmAMWjAxCn3YMSGEgQprg0Yh4azQyRX4KceIBIdvVR4gHAUqECRSMiNcBhgl1IUSHgzBSHUeWeLAGTSZFIoggaKyAIkObSCLFjgkRJgJrghVpJEeaJaakaV1EIgIUUD4JhQgiUIFVS4dspaUDaCBWSSNugNnImGG6AQKQCnWBgA5stulmczl8KWaYYjZy5lFquqmnDnA2KSWUU05p5VFY4rVllxkeyUlJSaJ5ZF2cWEKJowcVaBYmUngwRxYmbXLJJZk8SJEmVMzBQQcclEApQZlk4eolXVD/tMkkdXRgqwd11MSRJp++egmRCGURiQeocjCHJLEmtqpzXVziahagiloQFR5wcKoHUkQ0EBZUUFbpZBVh8iy0yRqEx6kdQIHYQJpIIUIk6yopECaUTFKJtJuI62q5BWECAgiTAJsDJYBymkMWK6xgcBf1UqJtRbxesiOoB2XipAilCUQJHnjoeuAk9krr3LIsSUJlJCHGybHHmtQ7yYtFXjKlCB6r3HFDIFPCL1ab4EGlFERujEcl1lUCcrxYWRIo0pWs3C/Ik3hrUxclUHlhZU5XhEW995qVSdWRPDyQ0EQX1AXIlQjMUSYrGFUQ2Qc5KzKho3Fc9qMTNY0H0ngrCrRJJqH2LXhCAQEAIfkEAAoA/wAsAAAAADAAMAAACP8AcwgcSLCgwYMIEypcSFBVlTyqGEqcSJBTBwdmPFDcWJDTwVIOHHQ4yMkjx4Op6pwySXBDyFIGvZTS8OJkQRikFFXY0xGkA5gFpxj6ZIaPzYGXcioqxaqiS5EFVyn6ZCgUjKMDTShSNGpKQZ9AB5r6RLYO1oGrNGx1FFEgJ58jB6ZyQFYRjbMDq4zaGokgSDMdTFokC8orXoFePGy1cDUHp6dxc7BoQPZNU46p2hZ8YWHrBy8C4SK2QLYBT4MvWLAsmGpDqRSXB3IytXcUC4GR3rzpm8OEoaEaC9L4QPb2wVO633jYs1rVG50m3HopKbAOqE+hUhFkhcqBge8VVrv/NeEouSNTqVie6MBHvOwqFXg7zqPowHcDCRy5d8znQ/I3GqByl2OgLTSdQKloUMh9BoRyQoEIsVJFB/+Vksd+CXFShyEMGlLHKhPRYIIGydWBIUKriHJfAhpoh5kpjtB0EioHHKCIakd5sceFJ7HSASoQHibkkBx5ZKRjSKJ1gglLMumkCcbZ5MUGolRppZWKNAZDBx2UUkqXXX4ZyYkLsQJKAGimKQCaAqAi0JZfesllmPKdtIoha66ZJptu5rDKFCYw2WSgJ+SB1WNXJpqlQmRuZOSjbhEpqUGcpFJTj2/UEdtJNFRxyimaUWTKF1+YkUKjBrGyRySmtJoCR6t8/wLArAGMcilDXrxgwimtnmLCrRPJ5Mmss3pSyoAIcXLJFLzyGgkLsaFK0AuK8EAsAIVEEiRBe/DaaxXI5pAKC+HGpEq0KTTwBbFfKLKtQFX0ekJ626VwwhQupnpJKpesxkodBxAbyn40oIIKH+++cMK9bV3ywgttsZLKxCAWdIkGnXRSRUI0VCycvSeclgMMeeSRryoTX/JuDnucehILC6fg8bgsNJaDF/umUu5ZqgB6gs0js1AzQaukvPJJXuSxcBWbwsCCyRXtC4Mq0i6UysInXHKT0PkKVPTEm9rEir1Qiud0HkALhDK/VaNYhQlT7Oz00AVJzO/RFK3CR9pvPhndNVo0tG0TyXRPKhHNfxue4Sqr4K244QEBACH5BAAKAP8ALAAAAAAwADAAAAj/AHMIHEiwoMGDCBMqXEhwBgsWNBhKnFjwiRo1pihqLMjpIK2LdA7m6rjxoJYRJkgS/KgmZMFctGZhKVkwy4Y3jnBxZOmS4IpYh2TppClwxs03dDQV/Eihp8BVRxw4UKOF6MAUb7KuIMiJliw1TwqikuqgltWBmjxknRVRYFeQBLXIknpk1dmBlBxlNbHyYtiBtKTGUnF3ICdTR45oyAL4a08XaKRuyFVyRtuaGrI+6fgWrMBcGqRGGFoQF6WEM2jRWUFZbFZHp3OYWLKEb44UQB04FUiDjlQXCG3RnjUCl8ocNJbgJJyDk/OBtWI5oFB1YC4TsgwpULABYQoPS2aF/0dVXaCKJzMRcmLhyJZhFm20bzfk4bhhLLXEi6eVwm5z+yKRlMUSQmyngCEUqAAgQblQ8oR44dFByYIJcTKCAwYqgEYtSkm0Sgq0hDcLKhQilMsi8h3iQXkUzWDCLB4wtpEKZRjyBnBEcWJaiRWacktrhQUpZEmcNefWcwJpsoIKS6rApJMqkEbkLItUaWUbbSxyhIwnmWLKCF6G6aNVmjgAy5kFoHkmLO7l0KWXYIp5C5lmrmnnmW0qCeWTT+JIEydUWiloG1sOuRCSziFp6KKGzSDjRppoMAKQJa1CyS23XEYRKoIIgoaCkGKRgi2ksgCpEAGkWsARUirESRYqkP9KqgosSgQTAq+kGkACHmhqECcOyXpLClgAyeNTrWHRRgG6viKECZQShMUtwlLiH2+4XGtQLiMksIRhKqAhiK6CtLGgC6TessIMxzXIAiUzIPRGKwD44GcOmoxgSK4ByLLgKk5mAaAWD7Hg3yozzODfE/QCoIZ9Rh1wwFYIrdJhQZaysEJ6yGWRRVuaHAIAAGCkcJALzG2ExUOUXEyDx5elAMbIQlx81yoas8Diyx8bpsbIrfx1FycurMCCC5TyrCkuPoyMQK00zWA0RAU52jNBS4wMgCN35eKCxsYVpHTVQIzcQ2xEaULJQ9ryBrNBtbgCwCsmn5VLFlB3fDWDFAwUxihBY297bGGB/31oLiMZrnhBAQEAIfkEAAoA/wAsAAAAADAAMAAACP8AcwgcSLCgwYMIEypcSDCTCxeZGEqcWPDOmzd3KGosyOmgnQtv7Bzk1HHjQVW2qJQk+PGCyII3RPxKZbKgql9MmtAsaOeiCIMs2Ci64KfmwEw4mdy5UVDExZcDWUFSNFSV0YEsmGhlQZDTxzc/CdqiusbW1ah2tIqowfIpQVVvqEJidXbgiyZaqbAEKaIkJxFU2QCrO5CTCa1OLg38CvWFBapOVlLMxNbgJSdaTXT06jYHpyZULbw4mMpFwkwlSrhgWpCK1iajc1D59UtvDhVrqEIdWEOEBAlFDwITIcKOrVSSe+cMVnilCaG+rA68QYUNrwa8miBkYYd4cRURBwb/K7FzZDAmtgW60PCA1/UHvyQTvISiO/E7LOh6ln+QdY7LETSA3QNvsMBfVy+Y4J0dJvhxYEKclCCBe+4pYoJ+DLESzB3epTfRDb5gx0sEv0inUSYq2HGHYhux0B4TsdXESSoxahShCv4RpuOOJpHk2Y+S3eBCMEMGY2SR5dUUAkhv+HKRk29owGImKJhggi1YYnklMA8ydAMbCoQp5gJhLmAbSlnacqWatgxm1JdixlmmbUIaeeSdSW70ly++aNCnn3wywSKPhBZaVyYmanQDEyVgaBIrfgTDQmUamaCLLooYuNENqUjKAjDBUVRDLwaUmoAGeUKoigufAsMCRJuG/7BLqaXuEkJ4CdXwAgutBnNJlwfVwJofGiRAqwEPoJAjQanw6ioLqTjKiirLEnTDHbtoJxAnwCiiC60I+HJgs66+UINknFySSrQC3cDKuQJpMEAACdR4gwkN0GrBgaw8pAp/mazLLidvXHqBQHbMK4AFBqniRJhcIcRKtTncoG4q4XHCCwAA8CIQK70EEIAYKhy0K7AIBZzKrwNt3HFJKoghci+OnsXKupdQqjHHHg9kgQABDLDbWar4sfJKO3dMkB8JiLxAokbVILCjSfc8UBNAB8BEXemm4gfUVUuWSQMi68LcVRavvGzYBZVAgAC6lHwWJ5Qd5LLV01kggZuGehZ2d38oE9YLxxH0LdELdthRo+GM5xAQACH5BAAKAP8ALAAAAAAwADAAAAj/AHMIHEiwoMGDCBMqXEiQGAwYxBhKnFgQhTBhKChqLFjsoIklwkwc7LgRYSZgVw7iuSiSowk7l0oWzFRCBEyDJlga5JMBg5IsMgcSMyFCBAqSA3OGLGjjiRufM4IO5GPHJq6CSvEUlISh6zCpA3OhKGrCBsGcS1oKzLSkqxyzYAVeqiqCEkE8ILUmdeMmg924AotJKloi08CVS/TmyKKk6xOkFInBnRmpqCSSaFsWE9E1CVCDl2AkJCZpWBbIAq8UtfP5SqRIKXNQyvBUrVATfD/vxMMb2AzINohGuhoYqaSeSwwPFJxEkfPHB2Gg4I0HBaWIA2FIioqwGIwnkgji/5JTxLmiIpESZroynfcwXLmWM0Q6t4L5IksooeZ4SRJ1FJLEtBEKbtyHwTCTLZQLDMO0d8V+ChUjjHmM2KGcRsRQggIKF1JESQUVOKGbTJmMSFExeAADIWAstjgRSTBCVkwWD2VBIww3cidTMZEoscQSPgL5oxzcEXPFkUgmSdyOGTgwhANQRvkkMAIZmeSVS5ZUDAZRSjnEEKFQmcOMONqIY406yhQJSBe1CRKRLkq0Ypx0DmRDgic+YUJ8QeWSySWX8KmRJAww4IZ+GxVDzCU2ZpGmRLm4ocCkQixhYkLF2DBDo47iOV8koUw6aSgiYJdQLps2egkxJOXiqUE28P95iRxDiBqEIigIWtCiqmYCmTCFiKArQcWYEMoTBFGCQRC2LgFhiTbOMCwuPejQihsCuWoDScL8YAADI4olgahJdDfDJZ4Wo4gO1iKbgxJBBKGEQCV4a0ASqBEjApRZcgQhCjywOwRcRAQQABHZKmKAAQmIWVAWf2lkgxDsBvBVDrkUfDBJVySwsCLDSvVEK+wWAaPGRCCVxMI/lMDiJT+w60OWKBOUBQMLO/CoTBmwq8MSxBb8CsIEPbGwAU7ERckr7BbSYQ4oQ0YMEQsr0O9GwzDdSnpBG0z0WQgYoEBsUkkSiiKeRl1QLhkwQjZYxYRcDBGvHDzSnC0qUrcieNcLmV0JJYjm9+AGBQQAIfkEAAoA/wAsAAAAADAAMAAACP8AcwgcSLCgwYMIEypcSBCQlmWAGEqcWHAFFBErKGqUKEmECEkHA21MCEhZn4OSLoI0mOzElpEFa7RE9rJgx48Gl8lZcqwmzByAJJ04sUIkwZsrB3qpxYTnn58Dlw09scymx4wEW8hhwuQK1IGBVpyQIsnLUY9Jc9R4whWK2a8C/yAbenIgUoLJuMqpCzdHoBZDkdUYuALtQC20mpYwqhHQ24KAWp5oYfQm1kBSuNLScnBLVYQllW1hPLDP1JrKkCFTJrDPTibJDEbesIHzwWVXcisbTNCLUGSfDV5J/IS3wL9yMCiHglBL7ucQCTp/mlBLiRYEl4lAohwDEimkCdb/gPH8SotljyUy/iMliRs3ymkpC2/wj7Lyyv7QXyhpSXcMS5Q1USBatLBCbjBsFMgTGMCXhBTUNYZbC8ZR1AcSSIgQHEw1RLiRJFfs19eIJKoH1nGkBfLHiiy2WOFIJdAioxwy1vhETV4so+OOPPo0UiBLKCLkkERil4MXD/HYI1RAEulkEUaq2OKUL2oUyAm0HHNMllweI4KHJYYp5k+AMBiRgrUkk56VyRjzxRcijHTFA7wkwdpGfRQBBgB8klGlQl4kwcugEBxjG0N/LOEDn3x6ssSaC12pCC9mUCpBCX8qVQsZjAIAhiJ1eZFpb0ZtcQwElFbqhiT7eaHIF4x+/2EMMozJYUwJkB4nCRvMlbYEnYM+cAx9gTzAKAJPnNnaGAF0ksRxgABilAigKPDAhr4ZQSkvTOwnSSedIOGjX0YIEIAnzAXCxKBMCITMAgoosER4NZQggQQJIpSMkTYVEEAAEJxphAEGsCGQFxjEawxWBS3DF0WAQPBvAQwPbIARRiljRrxG5AoTFJ0IIIAbRgVisREEyRHvAieMuMUCIo+Rr0AnSwdBvBGACdMS/wogR0E1E1RLvAo8AZcyB/xrjIcmE4yxeGzEy8vMMElygACelFBQ0xeHJ0m1vPD70woSdGxQ0AQFIoedIwaSKxsEG2xQICKWiEEBBmAw5kRSSQex4d6ADxQQACH5BAAKAP8ALAAAAAAwADAAAAj/AHMIHEiwoMGDCBMqXEhwE5ctmxhKnFgQFx48lShqlEjpYkaDxTYm3JQly8FKFymBpGSFi8iCmihdoVTDYEc8KgtqseMMlcuXAjdVunIFV0iCNz8OLIbCWc+aQAVyIXrl58CkBf04taM0ajFcRCtFHIgSJ8Eaz5ziGRtVYA2ZV7Qg9Yh0q8m2BLMQpaSJLF2pkZwOO6qxGGGCMYn6ufq32DCnkawS5CIXYTEtWvoa1LL3p94ri3Nk4eksZ0MrIEBsQcilZJYtmpcOpbRa4GFcgZ/FzvHVTocOHPAgrKHFdRYubHNwwQUV4ZZhuAhuQdWMA/Bmw0ZuMa6lxmGGhGtA/5vDwXqHSFm+G9S03XV3kZSe/Lb+hFJyhcWIu65NsRgq83MM0xxFDmF2n0RZNNPMM/y9tMluGhWlHl4UWmYbb7xN+NKEhOGCBi8ghhhiIwdS9BhPKDpjhx2RCRSJDjDGKCMzAxYGQiMX4Ihjjjl+ZIeMQOpAI1DFgMCjjhfk2MhHHooo4iGNaCgRNE5tpSJkkhmGYYYVdumlSJrYkUSJCxWDBzRkTomGIIJEAt8iozQT3UZ+XDBIAHgKUWOZzUzgZxt2NKgQF80QIgCeAhAyR5oHOdbIKH5O0AgeezaECigCHCrAIG2E9iBDmxzFhR1tRDqKEldweIEgmQYgyAPQEP/2xAPPkFnMFY6gQpAfcywyAaSjONPoBIgaYsdufoACywEd2BbqUZE8wMsEldl2hRKQTgDChFYccAAHguaQBCyDHKBrDs4sssgTAkHzwCGHzPFdDXjkeNdB0HQ1kBWEwALLBGM5ooACUfLGAS+HoKGvQFuEppEmE/hbyBUDCUzwQLhEAOKYXaLCjL9JEJbEwI0Q9ESI2VG4BS/+gnJvDhYXzPAEh/CyiGRAzeEvLOwSNPLFBOGBMC924IWLAv4+gLPFjhymSSMgRvCySFYgfYBwBcX83RXSprHwRlcswnHWJIMEQgcOt6WlQTE3+iVCHAwc8tsTaTHMMNXSrbdBAQEAIfkEAAoA/wAsAAAAADAAMAAACP8AcwgcSLCgwYMIEypcSPDGqlWcGEqcWDDLlStZKGqUaPEKlo0bOWXKdBDLFSsfDWJRZgNkwRtasmi5ofJkSoKZUOBRscrlQE4xs5AsaNJjQU5X8OBJ0dKnQBtZovYkWPSmQC1KUWR0KpDTlqhaIg6s2lCFUis0uT6NmmWqQLJjleLZohYn2LQ54OawkUIKnmBiNaYIdhBoVLpvL95UpjSFW4Krhh5U0amTBi0GV7FNu8WSJcRbdOKxZPCGshIlHv8MBaC1rhBNu37VonpgFp0q8ObglAUPFCjOrBy8oehLawBfGqQIbGOLboOZrmAemEkFcGfOoBAeXqvQcQA8FJH/psj8Si3s2FGEVZiplI/vPko9Z2hJCvYQUKRYCrzQkqIAxyVQm0KcqIBeLVfERlEKDXzxhTMgbVELFCpIBpINIbyhIEWWbKUWf3UlxMmIu0VEYogLYaGIKKKsyOKLkICo0RVS1FgjHjbiMZUUAfTo44+gDDhRLaUU2UGRpRzZQUol/OhkAKBsSF4tRxqJZAdLvuUiixO8KAok802ElI1k3uiWiSWSKCOKbLaJ0A0ldBDmQgUC5pQViugSjRQgWaJBBiF4SBEWGiRgQDTRTCMlgRm+8YYGUljIXghBGHBoNEGEMGdCVpTiqKMdqLDoQDfgMQ2iiCaQwU2bkipWJlJo//DpG07YaRAnGegZjQG6KGJFYLVQo8KauwXTAR4EZRFCBqQ4moEUMnLCCKoNlKAbFtOAkmlXuw2EBzWKvDFdV8E0IesbUCCkDBmFOCFpDk2wGwSfOUDxBinp5mAFuIo4AyJfkEAyrkFWKHNQMA2QAQopaXUgjTQx5nCDE4oowojBBn0F0g1vFFJIA1cMVIoZ0pQyFiMVN9GqRiiA4nETgZUijRkmDwRFxWsIV1cmiigciqAdkByxQJlkULEGQmrkjMug5Cvyw0MLlMIaFdPrVBbSeKyIpA6bAUlBNpRSMSmCgqRMKIWAgoJBI5dsUDBrUMOIVS4po0EpMsoMMYicQB7hRNk+nVhQ11/f6uZBTZDcweETbWGFFQMzLvlAAQEAIfkEAAoA/wAsAAAAADAAMAAACP8AcwgcSLCgwYMIEypcSLDYjRvFGEqcWPBPqlR/KGpseOOgRYwbN6oINaFjxYsZDWpJZTLkwGQEALiqZfBjSoJd9kyqBMjlwD2CAAAAclPgR0wGYUyatKelTyRCAXA4CZIgJp2TkPocqAWBUB8wCNpsWGmppYhbBz5pJZQC2hxjuS7d0yUtQUDVhAZINjBujhtYw4bMU+lgMh5Ch/SEi3JgqqWTFhe8URfhpB8/OGgdWIyC0FZPBHbBhKnyH8ipDBZLlUyF5IYTAgR4tcDO60oxWzVCiKlsJadw89gaXlh1GwKyAxCAoOItByC2EwKCUbRLpVvDbd2yhPCGiWqvkg//ciOYssYbMJJlv5V1IaZmhMLPJvTh7UQtKtarSGVfIQw3g4T3SjWVTVTMHtklYwlwDBWjAgQECELTRn/ccgtdWwFihwYMSpQKJv25FKJdCkX01ogkGpSKG9RQ04aLL7Y4S4cTWaLCjTjimMdithjg44+D/CjNaxvdIsKRSCJphxYC9fjjkz6GQiRFxSST5JVLCpRKIy3G2KKMNEpkY4457thQDvahmOKabCp0g5FhJnTgWVtV0sgCDKgQkhbNNGPCZhTxWc0nhLYRp2qozMLBLB8kU+BCgNQCAaGESmOHmgjtccwsis7yRFMlqkDBApRWw0FqaGIq0FtdJPNBp7PU/8LfQcU0wwClC7QxCUEmILFrQjA8oedAmJjQzKIcNMOXahpQGoEtr2lBgTShTGjiQCog0QgHRRVjiQiccnALQpVIM8QTRQl0zBDSSDNuDrZwwIEJAu2hbSP0TpbHMccAWtAe3BlkSQTscqguBRN8sKoIjbihAaoVMbnRDRu0C0FxORwzQcJopaKBG26IcChFI7GrsFoTUHCyQCY00ggSe6TYhRvsyiKxuhsfI9YsbjTSzJQh1WKuNKgUdAzCKwukgsuNLLuVFhOY68ajGW+c9F8f9KxZWpbIMkQowxKkMccFWYKEGxvc7BMMsxwT4thXo2lCliQWM6LGKtPaJkIipA8c2t4T/bHHHv4CbjhBAQEAOw==)}.sr-rd-content-center{text-align:center;display:-webkit-box;-webkit-box-align:center;-webkit-box-pack:center;-webkit-box-orient:vertical}.sr-rd-content-center-small{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex}.sr-rd-content-center-small img{margin:0;padding:0;border:0;box-shadow:none}img.simpread-img-broken{cursor:pointer}.sr-rd-content-nobeautify{margin:0;padding:0;border:0;box-shadow:0 0 0}sr-rd-mult{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;margin:0 0 16px;padding:16px 0 24px;width:100%;background-color:#fff;border-radius:4px;box-shadow:0 1px 2px 0 rgba(60,64,67,.3),0 2px 6px 2px rgba(60,64,67,.15)}sr-rd-mult:hover{-webkit-transition:all .45s 0ms;transition:all .45s 0ms;box-shadow:1px 1px 8px rgba(0,0,0,.16)}sr-rd-mult sr-rd-mult-content{padding:0 16px;overflow:auto}sr-rd-mult sr-rd-mult-avatar,sr-rd-mult sr-rd-mult-content{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}sr-rd-mult sr-rd-mult-avatar{-webkit-box-align:center;-ms-flex-align:center;align-items:center;margin:0 15px}sr-rd-mult sr-rd-mult-avatar span{display:-webkit-box;-webkit-line-clamp:1;-webkit-box-orient:vertical;max-width:75px;overflow:hidden;text-overflow:ellipsis;text-align:left;font-size:16px;font-size:1rem}sr-rd-mult sr-rd-mult-avatar img{margin-bottom:0;max-width:50px;max-height:50px;width:50px;height:50px;border-radius:50%}sr-rd-mult sr-rd-mult-content img{max-width:80%}sr-rd-mult sr-rd-mult-avatar .sr-rd-content-center{margin:0}sr-page{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;width:100%}</style><style type=\"text/css\">sr-rd-theme-github{display:none}sr-rd-content h1,sr-rd-content h2,sr-rd-content h3,sr-rd-content h4,sr-rd-content h5,sr-rd-content h6{position:relative;margin-top:1em;margin-bottom:1pc;font-weight:700;line-height:1.4;text-align:left;color:#363636}sr-rd-content h1{padding-bottom:.3em;font-size:57.6px;font-size:3.6rem;line-height:1.2}sr-rd-content h2{padding-bottom:.3em;font-size:44.8px;font-size:2.8rem;line-height:1.225}sr-rd-content h3{font-size:38.4px;font-size:2.4rem;line-height:1.43}sr-rd-content h4{font-size:32px;font-size:2rem}sr-rd-content h5,sr-rd-content h6{font-size:25.6px;font-size:1.6rem}sr-rd-content h6{color:#777}sr-rd-content ol,sr-rd-content ul{list-style-type:disc;padding:0;padding-left:2em}sr-rd-content ol ol,sr-rd-content ul ol{list-style-type:lower-roman}sr-rd-content ol ol ol,sr-rd-content ol ul ol,sr-rd-content ul ol ol,sr-rd-content ul ul ol{list-style-type:lower-alpha}sr-rd-content table{width:100%;overflow:auto;word-break:normal;word-break:keep-all}sr-rd-content table th{font-weight:700}sr-rd-content table td,sr-rd-content table th{padding:6px 13px;border:1px solid #ddd}sr-rd-content table tr{background-color:#fff;border-top:1px solid #ccc}sr-rd-content table tr:nth-child(2n){background-color:#f8f8f8}sr-rd-content sr-blockquote{border-left:4px solid #ddd}.simpread-theme-root{background-color:#fff;color:#333}sr-rd-title{font-family:PT Sans,SF UI Display,\\.PingFang SC,PingFang SC,Neue Haas Grotesk Text Pro,Arial Nova,Segoe UI,Microsoft YaHei,Microsoft JhengHei,Helvetica Neue,Source Han Sans SC,Noto Sans CJK SC,Source Han Sans CN,Noto Sans SC,Source Han Sans TC,Noto Sans CJK TC,Hiragino Sans GB,sans-serif;font-size:54.4px;font-size:3.4rem;font-weight:700;line-height:1.3}sr-rd-desc{position:relative;margin:0;margin-bottom:30px;padding:25px;padding-left:56px;font-size:28.8px;font-size:1.8rem;color:#777;background-color:rgba(0,0,0,.05);box-sizing:border-box}sr-rd-desc:before{content:\"\\201C\";position:absolute;top:-28px;left:16px;font-size:80px;font-family:Arial;color:rgba(0,0,0,.15)}sr-rd-content,sr-rd-content *,sr-rd-content div,sr-rd-content p{color:#363636;font-weight:400;line-height:1.8}sr-rd-content b *,sr-rd-content strong,sr-rd-content strong * sr-rd-content b{-webkit-animation:none 0s ease 0s 1 normal none running;animation:none 0s ease 0s 1 normal none running;-webkit-backface-visibility:visible;backface-visibility:visible;background:transparent none repeat 0 0/auto auto padding-box border-box scroll;border:medium none currentColor;border-collapse:separate;-o-border-image:none;border-image:none;border-radius:0;border-spacing:0;bottom:auto;box-shadow:none;box-sizing:content-box;caption-side:top;clear:none;clip:auto;color:#000;-webkit-columns:auto;-moz-columns:auto;columns:auto;-webkit-column-count:auto;-moz-column-count:auto;column-count:auto;-webkit-column-fill:balance;-moz-column-fill:balance;column-fill:balance;-webkit-column-gap:normal;-moz-column-gap:normal;column-gap:normal;-webkit-column-rule:medium none currentColor;-moz-column-rule:medium none currentColor;column-rule:medium none currentColor;-webkit-column-span:1;-moz-column-span:1;column-span:1;-webkit-column-width:auto;-moz-column-width:auto;column-width:auto;content:normal;counter-increment:none;counter-reset:none;cursor:auto;direction:ltr;display:inline;empty-cells:show;float:none;font-family:serif;font-size:medium;font-style:normal;font-variant:normal;font-weight:400;font-stretch:normal;line-height:normal;height:auto;-webkit-hyphens:none;-ms-hyphens:none;hyphens:none;left:auto;letter-spacing:normal;list-style:disc outside none;margin:0;max-height:none;max-width:none;min-height:0;min-width:0;opacity:1;orphans:2;outline:medium none invert;overflow:visible;overflow-x:visible;overflow-y:visible;padding:0;page-break-after:auto;page-break-before:auto;page-break-inside:auto;-webkit-perspective:none;perspective:none;-webkit-perspective-origin:50% 50%;perspective-origin:50% 50%;position:static;right:auto;-moz-tab-size:8;-o-tab-size:8;tab-size:8;table-layout:auto;text-align:left;text-align-last:auto;text-decoration:none;text-indent:0;text-shadow:none;text-transform:none;top:auto;-webkit-transform:none;transform:none;-webkit-transform-origin:50% 50% 0;transform-origin:50% 50% 0;-webkit-transform-style:flat;transform-style:flat;-webkit-transition:none 0s ease 0s;transition:none 0s ease 0s;unicode-bidi:normal;vertical-align:baseline;visibility:visible;white-space:normal;widows:2;width:auto;word-spacing:normal;z-index:auto;all:initial}sr-rd-content a,sr-rd-content a:link{color:#4183c4;text-decoration:none}sr-rd-content a:active,sr-rd-content a:focus,sr-rd-content a:hover{color:#4183c4;text-decoration:underline}sr-rd-content pre{background-color:#f7f7f7;border-radius:3px}sr-rd-content li code,sr-rd-content p code{background-color:rgba(0,0,0,.04);border-radius:3px}.simpread-multi-root{background:#f8f9fa}</style><style type=\"text/css\">sr-rd-theme-newsprint{display:none}sr-rd-content h1,sr-rd-content h2,sr-rd-content h3,sr-rd-content h4,sr-rd-content h5,sr-rd-content h6{font-weight:700}sr-rd-content h1{font-size:48px;font-size:3rem;line-height:1.6em;margin-top:2em}sr-rd-content h2,sr-rd-content h3{font-size:32px;font-size:2rem;line-height:1.15;margin-top:2.285714em;margin-bottom:1.15em}sr-rd-content h3{font-weight:400}sr-rd-content h4{font-size:28.8px;font-size:1.8rem;margin-top:2.67em}sr-rd-content h5,sr-rd-content h6{font-size:25.6px;font-size:1.6rem}sr-rd-content h1{border-bottom:1px solid;margin-bottom:1.875em;padding-bottom:.8125em}sr-rd-content ol,sr-rd-content ul{margin:0 0 1.5em 1.5em}sr-rd-content ol li{list-style-type:decimal;list-style-position:outside}sr-rd-content ul li{list-style-type:disc;list-style-position:outside}sr-rd-content table{width:100%;margin-bottom:1.5em;font-size:25.6px;font-size:1.6rem}sr-rd-content thead th,tfoot th{padding:.25em .25em .25em .4em;text-transform:uppercase}sr-rd-content th{text-align:left}sr-rd-content td{vertical-align:top;padding:.25em .25em .25em .4em}sr-rd-content thead{background-color:#dadada}sr-rd-content tr:nth-child(2n){background:#e8e7e7}sr-rd-content sr-blockquote{padding:10px 15px;border-left-style:solid;border-left-width:10px;border-color:#d6dbdf;background:none repeat scroll 0 0 rgba(102,128,153,.05);text-align:left}sr-rd-content sr-blockquote:before{content:\"\"}.simpread-multi-root,.simpread-theme-root{background-color:#f3f2ee;color:#2c3e50}sr-rd-title{font-family:PingFang SC,Hiragino Sans GB,Microsoft Yahei,WenQuanYi Micro Hei,sans-serif;line-height:1.5;font-weight:500;font-size:48px;font-size:3rem;color:#07b;border-bottom:1px solid;margin-bottom:1.875em;padding-bottom:.8125em}sr-rd-desc{color:rgba(102,128,153,.6);background-color:rgba(102,128,153,.075);border-radius:4px;margin-bottom:1em;padding:15px;font-size:32px;font-size:2rem;line-height:1.5;text-align:center}sr-rd-content,sr-rd-content *,sr-rd-content div,sr-rd-content p{line-height:1.8;color:#2c3e50}sr-rd-content a,sr-rd-content a:link{color:#08c;text-decoration:none}sr-rd-content a:active,sr-rd-content a:focus,sr-rd-content a:hover{color:#5ba4e5}sr-rd-content li code,sr-rd-content p code,sr-rd-content pre{background-color:#dadada}sr-rd-mult{background-color:rgba(102,128,153,.075)}</style><style type=\"text/css\">sr-rd-theme-gothic{display:none}sr-rd-content h1{line-height:64px;line-height:4rem;margin:64px 0 28px;margin:4rem 0 1.75rem;padding:20px 30px}sr-rd-content h1,sr-rd-content h2{font-weight:400;text-align:center;text-transform:uppercase}sr-rd-content h2{line-height:48px;line-height:3rem;margin:48px 0 31px;margin:3rem 0 1.9375rem;padding:0 30px}sr-rd-content h3,sr-rd-content h4,sr-rd-content h5{font-weight:400}sr-rd-content h6{font-weight:700}sr-rd-content h1{font-size:57.6px;font-size:3.6rem}sr-rd-content h2{font-size:51.2px;font-size:3.2rem}sr-rd-content h3{font-size:40px;font-size:2.5rem}sr-rd-content h4{font-size:35.2px;font-size:2.2rem}sr-rd-content h5{font-size:30.4px;font-size:1.9rem}sr-rd-content h6{font-size:27.2px;font-size:1.7rem}sr-rd-content h1,sr-rd-content h2,sr-rd-content h3,sr-rd-content h4,sr-rd-content h5,sr-rd-content h6{margin-top:1.2em;margin-bottom:.6em;color:#111}sr-rd-content ol,sr-rd-content ul{list-style-type:disc;margin-left:3em}sr-rd-content ol ol,sr-rd-content ul ol{list-style-type:lower-roman}sr-rd-content ol ol ol,sr-rd-content ol ul ol,sr-rd-content ul ol ol,sr-rd-content ul ul ol{list-style-type:lower-alpha}sr-rd-content table{margin-bottom:20px}sr-rd-content table td,sr-rd-content table th{padding:8px;line-height:20px;line-height:1.25rem;vertical-align:top;border-top:1px solid #ddd}sr-rd-content table th{font-weight:700}sr-rd-content table thead th{vertical-align:bottom}sr-rd-content table caption+thead tr:first-child td,sr-rd-content table caption+thead tr:first-child th,sr-rd-content table colgroup+thead tr:first-child td,sr-rd-content table colgroup+thead tr:first-child th,sr-rd-content table thead:first-child tr:first-child td,sr-rd-content table thead:first-child tr:first-child th{border-top:0}sr-rd-content table tbody+tbody{border-top:2px solid #ddd}sr-rd-content sr-blockquote{margin:0 0 17.777px;margin:0 0 1.11111rem;padding:8px 17.777px 0 16.888px;padding:.5rem 1.11111rem 0 1.05556rem;border-left:1px solid gray}sr-rd-content sr-blockquote,sr-rd-content sr-blockquote p{line-height:2;color:#6f6f6f}.simpread-multi-root,.simpread-theme-root{background:#fcfcfc;color:#333}sr-rd-title{font-weight:400;line-height:64px;line-height:4rem;text-align:center;text-transform:uppercase;color:#111;font-size:51.2px;font-size:3.2rem}sr-rd-desc{margin:0 0 17.777px;margin:0 0 1.11111rem;padding:8px 17.777px 0 16.888px;padding:.5rem 1.11111rem 0 1.05556rem;font-size:32px;font-size:2rem;line-height:2;color:#6f6f6f;border-left:1px solid gray}sr-rd-content{font-weight:400;color:#333}sr-rd-content *,sr-rd-content div,sr-rd-content p{color:#333}sr-rd-content a,sr-rd-content a:link{color:#900;text-decoration:none}sr-rd-content a:active,sr-rd-content a:focus,sr-rd-content a:hover{color:#900;text-decoration:underline}sr-rd-content li code,sr-rd-content p code,sr-rd-content pre{background-color:transparent;border:1px solid #ccc}sr-rd-mult{background-color:#f2f2f2}</style><style type=\"text/css\">sr-rd-theme-engwrite{display:none}sr-rd-content h1,sr-rd-content h2,sr-rd-content h3,sr-rd-content h4,sr-rd-content h5,sr-rd-content h6{margin:20px 0 10px;padding:0;font-weight:500;-webkit-font-smoothing:antialiased}sr-rd-content h1{font-weight:300;text-align:center;font-size:44.8px;font-size:2.8rem;color:#933d3f}sr-rd-content h2{font-size:38.4px;font-size:2.4rem;border-bottom:1px solid #ccc;color:#000}sr-rd-content h3{font-size:28.8px;font-size:1.8rem}sr-rd-content h4,sr-rd-content h5,sr-rd-content h6{font-size:25.6px;font-size:1.6rem}sr-rd-content h6{color:#777}sr-rd-content ol,sr-rd-content ul{padding-left:30px}sr-rd-content ol li>:first-child,sr-rd-content ol li ol:first-of-type,sr-rd-content ol li ul:first-of-type,sr-rd-content ul li>:first-child,sr-rd-content ul li ol:first-of-type,sr-rd-content ul li ul:first-of-type{margin-top:0}sr-rd-content ol ol,sr-rd-content ol ul,sr-rd-content ul ol,sr-rd-content ul ul{margin-bottom:0}sr-rd-content table th{font-weight:700}sr-rd-content table td,sr-rd-content table th{border:1px solid #ccc;padding:6px 13px}sr-rd-content table tr{border-top:1px solid #ccc;background-color:#fff}sr-rd-content table tr:nth-child(2n){background-color:#f8f8f8}sr-rd-content sr-blockquote{text-align:left;border-top:1px dotted #cdc7bc;border-bottom:1px dotted #cdc7bc;background-color:#f8edda;color:#777}sr-blockquote>:first-child{margin-top:0}sr-blockquote>:last-child{margin-bottom:0}.simpread-multi-root,.simpread-theme-root{background-color:#fcf5ed;color:#333}sr-rd-title{font-weight:300;text-align:center;font-size:44.8px;font-size:2.8rem;color:#933d3f}sr-rd-desc{padding:10px;background-color:#f8edda;color:#777;font-size:32px;font-size:2rem;text-align:center;border-top:1px dotted #cdc7bc;border-bottom:1px dotted #cdc7bc}sr-rd-content{padding:20px 0;margin:0 auto}sr-rd-content,sr-rd-content *,sr-rd-content div,sr-rd-content p{color:#333;line-height:1.8}sr-rd-content a,sr-rd-content a:link{color:#ae3737;text-decoration:none}sr-rd-content a:active,sr-rd-content a:focus,sr-rd-content a:hover{text-decoration:underline}sr-rd-content pre{background-color:transparent;border:1px solid #ccc;border-radius:3px}sr-rd-content li code,sr-rd-content p code{border:1px solid #eaeaea;background-color:#f4ece3;border-radius:3px}sr-rd-mult{background-color:#f8edda}</style><style type=\"text/css\">sr-rd-theme-octopress{display:none}sr-rd-content h1{font-size:56.32px;font-size:3.52rem;line-height:30.72px;line-height:1.92rem}sr-rd-content h1,sr-rd-content h2,sr-rd-content h3,sr-rd-content h4,sr-rd-content h5,sr-rd-content h6{text-rendering:optimizelegibility;margin-bottom:20.8px;margin-bottom:1.3rem;font-weight:700}sr-rd-content h2{font-size:38.4px;font-size:2.4rem}sr-rd-content h3{font-size:33.28px;font-size:2.08rem}sr-rd-content h4{font-size:28.8px;font-size:1.8rem}sr-rd-content h5,sr-rd-content h6{font-size:25.6px;font-size:1.6rem}sr-rd-content h1,sr-rd-content h2{padding-top:27.2px;padding-top:1.7rem;padding-bottom:19.2px;padding-bottom:1.2rem;background:url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAABCAYAAACsXeyTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAFUlEQVQIHWNIS0sr/v//PwMMDzY+ADqMahlW4J91AAAAAElFTkSuQmCC\") 0 100% repeat-x}sr-rd-content h2{padding-top:20.8px;padding-top:1.3rem;padding-bottom:0}sr-rd-content ul{list-style-type:disc}sr-rd-content ul ul{list-style-type:circle;margin-bottom:0}sr-rd-content ul ul ul{list-style-type:square;margin-bottom:0}sr-rd-content ol{list-style-type:decimal}sr-rd-content ol ol{list-style-type:lower-alpha;margin-bottom:0}sr-rd-content ol ol ol{list-style-type:lower-roman;margin-bottom:0}sr-rd-content ol,sr-rd-content ol ol,sr-rd-content ol ul,sr-rd-content ul,sr-rd-content ul ol,sr-rd-content ul ul{margin-left:1.3em}sr-rd-content ol ol,sr-rd-content ol ul,sr-rd-content ul ol,sr-rd-content ul ul{margin-bottom:0}sr-rd-content table{width:100%;overflow:auto;word-break:normal;word-break:keep-all}sr-rd-content table th{font-weight:700}sr-rd-content table td,sr-rd-content table th{padding:6px 13px;border:1px solid #ddd}sr-rd-content table tr{background-color:#fff;border-top:1px solid #ccc}sr-rd-content table tr:nth-child(2n){background-color:#f8f8f8}sr-rd-content sr-blockquote{font-style:italic;font-size:inherit;line-height:2;padding-left:1em;border-left:4px solid hsla(0,0%,67%,.5)}.simpread-multi-root,.simpread-theme-root{background:#f8f8f8 url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAADICAQAAAAHUWYVAABFFUlEQVQYGbzBCeDVU/74/6fj9HIcx/FRHx9JCFmzMyGRURhLZIkUsoeRfUjS2FNDtr6WkMhO9sm+S8maJfu+Jcsg+/o/c+Z4z/t97/vezy3z+z8ekGlnYICG/o7gdk+wmSHZ1z4pJItqapjoKXWahm8NmV6eOTbWUOp6/6a/XIg6GQqmenJ2lDHyvCFZ2cBDbmtHA043VFhHwXxClWmeYAdLhV00Bd85go8VmaFCkbVkzlQENzfBDZ5gtN7HwF0KDrTwJ0dypSOzpaKCMwQHKTIreYIxlmhXTzTWkVm+LTynZhiSBT3RZQ7aGfjGEd3qyXQ1FDymqbKxpspERQN2MiRjNZlFFQXfCNFm9nM1zpAsoYjmtRTc5ajwuaXc5xrWskT97RaKzAGe5ARHhVUsDbjKklziiX5WROcJwSNCNI+9w1Jwv4Zb2r7lCMZ4oq5C0EdTx+2GzNuKpJ+iFf38JEWkHJn9DNF7mmBDITrWEg0VWL3pHU20tSZnuqWu+R3BtYa8XxV1HO7GyD32UkOpL/yDloINFTmvtId+nmAjxRw40VMwVKiwrKLE4bK5UOVntYwhOcSSXKrJHKPJedocpGjVz/ZMIbnYUPB10/eKCrs5apqpgVmWzBYWpmtKHecJPjaUuEgRDDaU0oZghCJ6zNMQ5ZhDYx05r5v2muQdM0EILtXUsaKiQX9WMEUotagQzFbUNN6NUPC2nm5pxEWGCjMc3GdJHjSU2kORLK/JGSrkfGEIjncU/CYUnOipoYemwj8tST9NsJmB7TUVXtbUtXATJVZXBMvYeTXJfobgJUPmGMP/yFaWonaa6BcFO3nqcIqCozSZoZoSr1g4zJOzuyGnxTEX3lUEJ7WcZgme8ddaWvWJo2AJR9DZU3CUIbhCSG6ybSwN6qtJVnCU2svDTP2ZInOw2cBTrqtQahtNZn9NcJ4l2NaSmSkkP1noZWnVwkLmdUPOwLZEwy2Z3S3R+4rIG9hcbpPXHFVWcQdZkn2FOta3cKWQnNRC5g1LsJah4GCzSVsKnCOY5OAFRTBekyyryeyilhFKva75r4Mc0aWanGEaThcy31s439KKxTzJYY5WTHPU1FtIHjQU3Oip4xlNzj/lBw23dYZVliQa7WAXf4shetcQfatI+jWRDBPmyNeW6A1P5kdDgyYJlba0BIM8BZu1JfrFwItyjcAMR3K0BWOIrtMEXyhyrlVEx3ui5dUBjmB/Q3CXW85R4mBD0s7B+4q5tKUjOlb9qqmhi5AZ6GFIC5HXtOobdYGlVdMVbNJ8toNTFcHxnoL+muBagcctjWnbNMuR00uI7nQESwg5q2qqrKWIfrNUmeQocY6HuyxJV02wj36w00yhpmUFenv4p6fUkZYqLyuinx2RGOjhCXYyJF84oiU00YMOOhhquNdfbOB7gU88pY4xJO8LVdp6/q2voeB4R04vIdhSE40xZObx1HGGJ/ja0LBthFInKaLPPFzuCaYaoj8JjPME8yoyxo6zlBqkiUZYgq00OYMswbWO5NGmq+xhipxHLRW29ARjNKXO0wRnear8XSg4XFPLKEPUS1GqvyLwiuBUoa7zpZ0l5xxFwWmWZC1H5h5FwU8eQ7K+g8UcVY6TMQreVQT/8uQ8Z+ALIXnSEa2pYZQneE9RZbSBNYXfWYJzW/h/4j4Dp1tYVcFIC5019Vyi4ThPqSFCzjGWaHQTBU8q6vrVwgxP9Lkm840imWKpcLCjYTtrKuwvsKSnrvHCXGkSMk9p6lhckfRpIeis+N2PiszT+mFLspyGleUhDwcLrZqmyeylxwjBcKHEapqkmyangyLZRVOijwOtCY5SsG5zL0OwlCJ4y5KznF3EUNDDrinwiyLZRzOXtlBbK5ITHFGLp8Q0R6ab6mS7enI2cFrxOyHvOCFaT1HThS1krjCwqWeurCkk+willhCC+RSZnRXBiZaC5RXRIZYKp2lyfrHwiKPKR0JDzrdU2EFgpidawlFDR6FgXUMNa+g1FY3bUQh2cLCwosRdnuQTS/S+JVrGLeWIvtQUvONJxlqSQYYKpwoN2kaocLjdVsis4Mk80ESF2YpSkzwldjHkjFCUutI/r+EHDU8oCs6yzL3PhWiEooZdFMkymlas4AcI3KmoMMNSQ3tHzjGWCrcJJdYyZC7QFGwjRL9p+MrRkAGWzIaWCn9W0F3TsK01c2ZvQw0byvxuQU0r1lM0qJO7wW0kRIMdDTtXEdzi4VIh+EoIHm0mWtAtpCixlabgn83fKTI7anJe9ST7WIK1DMGpQmYeA58ImV6ezOGOzK2Kgq01pd60cKWiUi9Lievb/0vIDPHQ05Kzt4ddPckQBQtoaurjyHnek/nKzpQLrVgKPjIkh2v4uyezpv+Xoo7fPFXaGFp1vaLKxQ4uUpQQS5VuQs7BCq4xRJv7fwpVvvFEB3j+620haOuocqMhWd6TTPAEx+mdFNGHdranFe95WrWmIvlY4F1Dle2ECgc6cto7SryuqGGGha0tFQ5V53migUKmg6XKAo4qS3mik+0OZpAhOLeZKicacgaYcyx5hypYQE02ZA4xi/pNhOQxR4klNKyqacj+mpxnLTnnGSo85++3ZCZq6lrZkXlGEX3o+C9FieccJbZWVFjC0Yo1FZnJhoYMFoI1hEZ9r6hwg75HwzBNhbZCdJEfJwTPGzJvaKImw1yYX1HDAmpXR+ZJQ/SmgqMNVQb5vgamGwLtt7VwvP7Qk1xpiM5x5Cyv93E06MZmgs0Nya2azIKOYKCGBQQW97RmhKNKF02JZqHEJ4o58qp7X5EcZmc56trXEqzjCBZ1MFGR87Ql2tSTs6CGxS05PTzRQorkbw7aKoKXFDXsYW42VJih/q+FP2BdTzDTwVqOYB13liM50vG7wy28qagyuIXMeQI/Oqq8bcn5wJI50xH00CRntyfpL1T4hydYpoXgNiFzoIUTDZnLNRzh4TBHwbYGDvZkxmlyJloyr6tRihpeUG94GnKtIznREF0tzJG/OOr73JBcrSh1k6WuTprgLU+mnSGnv6Zge0NNz+kTDdH8nuAuTdJDCNb21LCiIuqlYbqGzT3RAoZofQfjFazkqeNWdYaGvYTM001EW2oKPvVk1ldUGSgUtHFwjKM1h9jnFcmy5lChoLNaQMGGDsYbKixlaMBmmsx1QjCfflwTfO/gckW0ruZ3jugKR3R5W9hGUWqCgxuFgsuaCHorotGKzGaeZB9DMsaTnKCpMtwTvOzhYk0rdrArKCqcaWmVk1+F372ur1YkKxgatI8Qfe1gIX9wE9FgS8ESmuABIXnRUbCapcKe+nO7slClSZFzpV/LkLncEb1qiO42fS3R855Su2mCLh62t1SYZZYVmKwIHjREF2uihTzB20JOkz7dkxzYQnK0UOU494wh+VWRc6Un2kpTaVgLDFEkJ/uhzRcI0YKGgpGWOlocBU/a4fKoJ/pEaNV6jip3+Es9VXY078rGnmAdf7t9ylPXS34RBSuYPs1UecZTU78WanhBCHpZ5sAoTz0LGZKjPf9TRypqWEiTvOFglL1fCEY3wY/++rbk7C8bWebA6p6om6PgOL2kp44TFJlVNBXae2rqqdZztOJpT87GQsE9jqCPIe9VReZuQ/CIgacsyZdCpIScSYqcZk8r+nsyCzhyfhOqHGOIvrLknC8wTpFcaYiGC/RU1NRbUeUpocQOnkRpGOrIOcNRx+1uA0UrzhSSt+VyS3SJpnFWkzNDqOFGIWcfR86DnmARTQ1HKIL33ExPiemeOhYSSjzlSUZZuE4TveoJLnBUOFof6KiysCbnAEcZgcUNTDOwkqWu3RWtmGpZwlHhJENdZ3miGz0lJlsKnjbwqSHQjpxnFDlTLLwqJPMZMjd7KrzkSG7VsxXBZE+F8YZkb01Oe00yyRK9psh5SYh29ySPKBo2ylNht7ZkZnsKenjKNJu9PNEyZpaCHv4Kt6RQsLvAVp7M9kIimmCUwGeWqLMmGuIotYMmWNpSahkhZw9FqZsVnKJhsjAHvtHMsTM9fCI06Dx/u3vfUXCqfsKRc4oFY2jMsoo/7DJDwZ1CsIKnJu+J9ldkpmiCxQx1rWjI+T9FwcWWzOuaYH0Hj7klNRVWEQpmaqosakiGNTFHdjS/qnUdmf0NJW5xsL0HhimCCZZSRzmSPTXJQ4aaztAwtZnoabebJ+htCaZ7Cm535ByoqXKbX1WRc4Eh2MkRXWzImVc96Cj4VdOKVxR84VdQsIUM8Psoou2byVHyZFuq7O8otbSQ2UAoeEWTudATLGSpZzVLlXVkPU2Jc+27lsw2jmg5T5VhbeE3BT083K9WsTTkFU/Osi0rC5lRlpwRHUiesNS0sOvmqGML1aRbPAxTJD9ZKtxuob+hhl8cwYGWpJ8nub7t5p6coYbMovZ1BTdaKn1jYD6h4GFDNFyT/Kqe1XCXphXHOKLZmuRSRdBPEfVUXQzJm5YGPGGJdvAEr7hHNdGZnuBvrpciGmopOLf5N0uVMy0FfYToJk90uUCbJupaVpO53UJXR2bVpoU00V2KOo4zMFrBd0Jtz2pa0clT5Q5L8IpQ177mWQejPMEJhuQjS10ref6HHjdEhy1P1EYR7GtO0uSsKJQYLiTnG1rVScj5lyazpqWGl5uBbRWl7m6ixGOOnEsMJR7z8J0n6KMnCdxhiNYQCoZ6CmYLnO8omC3MkW3bktlPmEt/VQQHejL3+dOE5FlPdK/Mq8hZxxJtLyRrepLThYKbLZxkSb5W52vYxNOaOxUF0yxMUPwBTYqCzy01XayYK0sJyWBLqX0MwU5CzoymRzV0EjjeUeLgDpTo6ij42ZAzvD01dHUUTPLU96MdLbBME8nFBn7zJCMtJcZokn8YoqU0FS5WFKyniHobguMcmW8N0XkWZjkyN3hqOMtS08r+/xTBwpZSZ3qiVRX8SzMHHjfUNFjgHEPmY9PL3ykEzxkSre/1ZD6z/NuznuB0RcE1TWTm9zRgfUWVJiG6yrzgmWPXC8EAR4Wxhlad0ZbgQyEz3pG5RVEwwDJH2mgKpjcTiCOzn1lfUWANFbZ2BA8balnEweJC9J0iuaeZoI+ippFCztEKVvckR2iice1JvhVytrQwUAZpgsubCPaU7xUe9vWnaOpaSBEspalykhC9bUlOMpT42ZHca6hyrqKmw/wMR8H5ZmdFoBVJb03O4UL0tSNnvIeRmkrLWqrs78gcrEn2tpcboh0UPOW3UUR9PMk4T4nnNKWmCjlrefhCwxRNztfmIQVdDElvS4m1/WuOujoZCs5XVOjtKPGokJzsYCtFYoWonSPT21DheU/wWhM19FcElwqNGOsp9Q8N/cwXaiND1MmeL1Q5XROtYYgGeFq1aTMsoMmcrKjQrOFQTQ1fmBYhmW6o8Jkjc7iDJRTBIo5kgJD5yMEYA3srCg7VFKwiVJkmRCc5ohGOKhsYMn/XBLdo5taZjlb9YAlGWRimqbCsoY7HFAXLa5I1HPRxMMsQDHFkWtRNniqT9UEeNjcE7RUlrCJ4R2CSJuqlKHWvJXjAUNcITYkenuBRB84TbeepcqTj3zZyFJzgYQdHnqfgI0ddUwS6GqWpsKWhjq9cV0vBAEMN2znq+EBfIWT+pClYw5xsTlJU6GeIBsjGmmANTzJZiIYpgrM0Oa8ZMjd7NP87jxhqGOhJlnQtjuQpB+8aEE00wZFznSJPyHxgH3HkPOsJFvYk8zqCHzTs1BYOa4J3PFU+UVRZxlHDM4YavlNUuMoRveiZA2d7grMNc2g+RbSCEKzmgYsUmWmazFJyoiOZ4KnyhKOGRzWJa0+moyV4TVHDzn51Awtqaphfk/lRQ08FX1iiqxTB/kLwd0VynKfEvI6cd4XMV5bMhZ7gZUWVzYQ6Nm2BYzxJbw3bGthEUUMfgbGeorae6DxHtJoZ6alhZ0+ytiVoK1R4z5PTrOECT/SugseEOlb1MMNR4VRNcJy+V1Hg9ONClSZFZjdHlc6W6FBLdJja2MC5hhpu0DBYEY1TFGwiFAxRRCsYkiM9JRb0JNMVkW6CZYT/2EiTGWmo8k+h4FhDNE7BvppoTSFnmCV5xZKzvcCdDo7VVPnIU+I+Rc68juApC90MwcFCsJ5hDqxgScYKreruyQwTqrzoqDCmhWi4IbhB0Yrt3RGa6GfDv52rKXWhh28dyZaWUvcZeMTBaZoSGyiCtRU5J8iviioHaErs7Jkj61syVzTTgOcUOQ8buFBTYWdL5g3T4qlpe0+wvD63heAXRfCCIed9RbCsp2CiI7raUOYOTU13N8PNHvpaGvayo4a3LLT1lDrVEPT2zLUlheB1R+ZTRfKWJ+dcocLJfi11vyJ51lLqJ0WD7tRwryezjiV5W28uJO9qykzX8JDe2lHl/9oyBwa2UMfOngpXCixvKdXTk3wrsKmiVYdZIqsoWEERjbcUNDuiaQomGoIbFdEHmsyWnuR+IeriKDVLnlawlyNHKwKlSU631PKep8J4Q+ayjkSLKYLhalNHlYvttb6fHm0p6OApsZ4l2VfdqZkjuysy6ysKLlckf1KUutCTs39bmCgEyyoasIWlVaMF7mgmWtBT8Kol5xpH9IGllo8cJdopcvZ2sImlDmMIbtDk3KIpeNiS08lQw11NFPTwVFlPP6pJ2gvRfI7gQUfmNAtf6Gs0wQxDsKGlVBdF8rCa3jzdwMaGHOsItrZk7hAyOzpK9VS06j5F49b0VNGOOfKs3lDToMsMBe9ZWtHFEgxTJLs7qrygKZjUnmCYoeAqeU6jqWuLJup4WghOdvCYJnrSkSzoyRkm5M2StQwVltPkfCAk58tET/CSg+8MUecmotMEnhBKfWBIZsg2ihruMJQaoIm+tkTLKEqspMh00w95gvFCQRtDwTT1gVDDSEVdlwqZfxoQRbK0g+tbiBZxzKlpnpypejdDwTaeOvorMk/IJE10h9CqRe28hhLbe0pMsdSwv4ZbhKivo2BjDWfL8UKJgeavwlwb5KlwhyE4u4XkGE2ytZCznKLCDZZq42VzT8HLCrpruFbIfOIINmh/qCdZ1ZBc65kLHR1Bkyf5zn6pN3SvGKIlFNGplhrO9QSXanLOMQTLCa0YJCRrCZm/CZmrLTm7WzCK4GJDiWUdFeYx1LCFg3NMd0XmCuF3Y5rITLDUsYS9zoHVzwnJoYpSTQoObyEzr4cFBNqYTopoaU/wkyLZ2lPhX/5Y95ulxGTV7KjhWrOZgl8MyUUafjYraNjNU1N3IWcjT5WzWqjwtoarHSUObGYO3GCJZpsBlnJGPd6ZYLyl1GdCA2625IwwJDP8GUKymbzuyPlZlvTUsaUh5zFDhRWFzPKKZLAlWdcQbObgF9tOqOsmB1dqcqYJmWstFbZRRI9poolmqiLnU0POvxScpah2iSL5UJNzgScY5+AuIbpO0YD3NCW+dLMszFSdFCWGqG6eVq2uYVNDdICGD6W7EPRWZEY5gpsE9rUkS3mijzzJnm6UpUFXG1hCUeVoS5WfNcFpblELL2qqrCvMvRfd45oalvKU2tiQ6ePJOVMRXase9iTtLJztPxJKLWpo2CRDcJwn2sWSLKIO1WQWNTCvpVUvOZhgSC40JD0dOctaSqzkCRbXsKlb11Oip6PCJ0IwSJM31j3akRxlP7Rwn6aGaUL0qiLnJkvB3xWZ2+Q1TfCwpQH3G0o92UzmX4o/oJNQMMSQc547wVHhdk+VCw01DFYEnTxzZKAm74QmeNNR1w6WzEhNK15VJzuCdxQ53dRUDws5KvwgBMOEgpcVNe0hZI6RXT1Jd0cyj5nsaEAHgVmGaJIlWdsc5Ui2ElrRR6jrRAttNMEAIWrTDFubkZaok7/AkzfIwfuWVq0jHzuCK4QabtLUMVPB3kJ0oyHTSVFlqMALilJf2Rf8k5aaHtMfayocLBS8L89oKoxpJvnAkDPa0qp5DAUTHKWmCcnthlou8iCKaFFLHWcINd1nyIwXqrSxMNmSs6KmoL2QrKuWtlQ5V0120xQ5vRyZS1rgFkWwhiOwiuQbR0OOVhQM9iS3tiXp4RawRPMp5tDletOOBL95MpM01dZTBM9pkn5qF010rIeHFcFZhmSGpYpTsI6nwhqe5C9ynhlpp5ophuRb6WcJFldkVnVEwwxVfrVkvnWUuNLCg5bgboFHPDlDPDmnK7hUrWiIbjadDclujlZcaokOFup4Ri1kacV6jmrrK1hN9bGwpKEBQ4Q6DvIUXOmo6U5LqQM6EPyiKNjVkPnJkDPNEaxhiFay5ExW1NXVUGqcpYYdPcGiCq7z/TSlbhL4pplWXKd7NZO5QQFrefhRQW/NHOsqcIglc4UhWklR8K0QzbAw08CBDnpbgqXdeD/QUsM4RZXDFBW6WJKe/mFPdH0LtBgiq57wFLzlyQzz82qYx5D5WJP5yVJDW01BfyHnS6HKO/reZqId1WGa4Hkh2kWodJ8i6KoIPlAj2hPt76CzXsVR6koPRzWTfKqIentatYpQw2me4AA3y1Kind3SwoOKZDcFXTwl9tWU6mfgRk9d71sKtlNwrjnYw5tC5n5LdKiGry3JKNlHEd3oaMCFHrazBPMp/uNJ+V7IudcSbeOIdjUEdwl0VHCOZo5t6YluEuaC9mQeMgSfOyKnYGFHcIeQ84yQWbuJYJpZw5CzglDH7gKnWqqM9ZTaXcN0TeYhR84eQtJT76JJ1lREe7WnnvsMmRc9FQ7SBBM9mV3lCUdmHk/S2RAMt0QjFNFqQpWjDPQ01DXWUdDBkXziKPjGEP3VP+zIWU2t7im41FOloyWzn/L6dkUy3VLDaZ6appgDLHPjJEsyvJngWEPUyVBiAaHCTEXwrLvSEbV1e1gKJniicWorC1MUrVjB3uDhJE/wgSOzk1DXpk0k73qCM8xw2UvD5kJmDUfOomqMpWCkJRlvKXGmoeBm18USjVIk04SClxTB6YrgLAPLWYK9HLUt5cmc0vYES8GnTeRc6skZbQkWdxRsIcyBRzx1DbTk9FbU0caTPOgJHhJKnOGIVhQqvKmo0llRw9sabrZkDtdg3PqaKi9oatjY8B+G371paMg6+mZFNNtQ04mWBq3rYLOmtWWQp8KJnpy9DdFensyjdqZ+yY40VJlH8wcdLzC8PZnvHMFUTZUrDTkLyQaGus5X5LzpYAf3i+e/ZlhqGqWhh6Ou6xTR9Z6oi5AZZtp7Mj2EEm8oSpxiYZCHU/1fbGdNNNRRoZMhmilEb2gqHOEJDtXkHK/JnG6IrvbPCwV3NhONVdS1thBMs1T4QOBcTWa2IzhMk2nW5Kyn9tXUtpv9RsG2msxk+ZsQzRQacJncpgke0+T8y5Fzj8BiGo7XlJjaTIlpQs7KFjpqGnKuoyEPeIKnFMkZHvopgh81ySxNFWvJWcKRs70j2FOT012IllEEO1n4pD1513Yg2ssQPOThOkvyrqHUdEXOSEsihmBbTbKX1kLBPWqWkLOqJbjB3GBIZmoa8qWl4CG/iZ7oiA72ZL7TJNeZUY7kFQftDcHHluBzRbCegzMtrRjVQpX2lgoPKKLJAkcbMl01XK2p7yhL8pCBbQ3BN2avJgKvttcrWDK3CiUOVxQ8ZP+pqXKyIxnmBymCg5vJjNfkPK4+c8cIfK8ocVt7kmfd/I5SR1hKvCzUtb+lhgc00ZaO6CyhIQP1Uv4yIZjload72PXX0OIJvnFU+0Zf6MhsJwTfW0r0UwQfW4LNLZl5HK261JCZ4qnBaAreVAS3WrjV0LBnNDUNNDToCEeFfwgcb4gOEqLRhirWkexrCEYKVV711DLYEE1XBEsp5tpTGjorkomKYF9FDXv7fR3BGwbettSxnyL53MBPjsxDZjMh+VUW9NRxq1DhVk+FSxQcaGjV9Pawv6eGByw5qzoy7xk4RsOShqjJwWKe/1pEEfzkobeD/dQJmpqedcyBTy2sr4nGNRH0c0SPWTLrqAc0OQcb/gemKgqucQT7ySWKCn2EUotoCvpZct7RO2sy/QW0IWcXd7pQRQyZVwT2USRO87uhjioTLKV2brpMUcMQRbKH/N2T+UlTpaMls6cmc6CCNy3JdYYSUzzJQ4oSD3oKLncULOiJvjBEC2oqnCJkJluCYy2ZQ5so9YYlZ1VLlQU1mXEW1jZERwj/MUSRc24TdexlqLKfQBtDTScJUV8FszXBEY5ktpD5Ur9hYB4Nb1iikw3JoYpkKX+RodRKFt53MMuRnKSpY31PwYaGaILh3wxJGz9TkTPEETxoCWZrgvOlmyMzxFEwVJE5xZKzvyJ4WxEc16Gd4Xe3Weq4XH2jKRikqOkGQ87hQnC7wBmGYLAnesX3M+S87eFATauuN+Qcrh7xIxXJbUIdMw3JGE3ylCWzrieaqCn4zhGM19TQ3z1oH1AX+pWEqIc7wNGAkULBo/ZxRaV9NNyh4Br3rCHZzbzmSfawBL0dNRwpW1kK9mxPXR9povcdrGSZK9c2k0xwFGzjuniCtRSZCZ6ccZ7gaktmgAOtKbG/JnOkJrjcQTdFMsxRQ2cLY3WTIrlCw1eWKn8R6pvt4GFDso3QoL4a3nLk3G6JrtME3dSenpx7PNFTmga0EaJTLQ061sEeQoWXhSo9LTXsaSjoJQRXeZLtDclbCrYzfzHHeaKjHCVOUkQHO3JeEepr56mhiyaYYKjjNU+Fed1wS5VlhWSqI/hYUdDOkaxiKehoyOnrCV5yBHtbWFqTHCCwtpDcYolesVR5yUzTZBb3RNMd0d6WP+SvhuBmRcGxnuQzT95IC285cr41cLGQ6aJJhmi4TMGempxeimBRQw1tFKV+8jd6KuzoSTqqDxzRtpZkurvKEHxlqXKRIjjfUNNXQsNOsRScoWFLT+YeRZVD3GRN0MdQcKqQjHDMrdGGVu3iYJpQx3WGUvfbmxwFfR20WBq0oYY7LMFhhgYtr8jpaEnaOzjawWWaTP8mMr0t/EPDPoqcnxTBI5o58L7uoWnMrpoqPwgVrlAUWE+V+TQl9rawoyP6QGAlQw2TPRX+YSkxyBC8Z6jhHkXBgQL7WII3DVFnRfCrBfxewv9D6xsyjys4VkhWb9pUU627JllV0YDNHMku/ldNMMXDEo4aFnAkk4U6frNEU4XgZUPmEKHUl44KrzmYamjAbh0JFvGnaTLPu1s9jPCwjFpYiN7z1DTOk/nc07CfDFzmCf7i+bfNHXhDtLeBXzTBT5rkMvWOIxpl4EMh2LGJBu2syDnAEx2naEhHDWMMzPZEhygyS1mS5RTJr5ZkoKbEUoYqr2kqdDUE8ztK7OaIntJkFrIECwv8LJTaVx5XJE86go8dFeZ3FN3rjabCAYpoYEeC9zzJVULBbmZhDyd7ko09ydpNZ3nm2Kee4FPPXHnYEF1nqOFEC08LUVcDvYXkJHW8gTaKCk9YGOeIJhqiE4ToPEepdp7IWFjdwnWaufGMwJJCMtUTTBBK9BGCOy2tGGrJTHIwyEOzp6aPzNMOtlZkDvcEWpP5SVNhfkvDxhmSazTJXYrM9U1E0xwFVwqZQwzJxw6+kGGGUj2FglGGmnb1/G51udRSMNlTw6GGnCcUwVcOpmsqTHa06o72sw1RL02p9z0VbnMLOaIX3QKaYKSCFQzBKEUNHTSc48k53RH9wxGMtpQa5KjjW0W0n6XCCCG4yxNNdhQ4R4l1Ff+2sSd6UFHiIEOyqqFgT01mEUMD+joy75jPhOA+oVVLm309FR4yVOlp4RhLiScNmSmaYF5Pw0STrOIoWMSR2UkRXOMp+M4SHW8o8Zoi6OZgjKOaFar8zZDzkWzvKOjkKBjmCXby8JahhjXULY4KlzgKLvAwxVGhvyd4zxB1d9T0piazmKLCVZY5sKiD0y2ZSYrkUEPUbIk+dlQ4SJHTR50k1DPaUWIdTZW9NJwnJMOECgd7ou/MnppMJ02O1VT4Wsh85MnZzcFTngpXGKo84qmwgKbCL/orR/SzJ2crA+t6Mp94KvxJUeIbT3CQu1uIdlQEOzlKfS3UMcrTiFmOuroocrZrT2AcmamOKg8YomeEKm/rlT2sociMaybaUlFhuqHCM2qIJ+rg4EcDFymiDSxzaHdPcpE62pD5kyM5SBMoA1PaUtfIthS85ig1VPiPPYXgYEMNk4Qq7TXBgo7oT57gPUdwgCHzhIVFPFU6OYJzHAX9m5oNrVjeE61miDrqQ4VSa1oiURTsKHC0IfjNwU2WzK6eqK8jWln4g15TVBnqmDteCJ501PGAocJhhqjZdtBEB6lnhLreFJKxmlKbeGrqLiSThVIbCdGzloasa6lpMQXHCME2boLpJgT7yWaemu6wBONbqGNVRS0PKIL7LckbjmQtR7K8I5qtqel+T/ChJTNIKLjdUMNIRyvOEko9YYl2cwQveBikCNawJKcLBbc7+JM92mysNvd/Fqp8a0k6CNEe7cnZrxlW0wQXaXjaktnRwNOGZKYiONwS7a1JVheq3WgJHlQUGKHKmp4KAxXR/ULURcNgoa4zhKSLpZR3kxRRb0NmD0OFn+UCS7CzI1nbP6+o4x47QZE5xRCt3ZagnYcvmpYQktXdk5YKXTzBC57kKEe0VVuiSYqapssMS3C9p2CKkHOg8B8Pa8p5atrIw3qezIWanMGa5HRDNF6RM9wcacl0N+Q8Z8hsIkSnaIIdHRUOEebAPy1zbCkhM062FCJtif7PU+UtoVXzWKqM1PxXO8cfdruhFQ/a6x3JKYagvVDhQEtNiyiiSQ7OsuRsZUku0CRNDs4Sog6KKjsZgk2bYJqijgsEenoKeniinRXBn/U3lgpPdyDZynQx8IiioMnCep5Ky8mjGs6Wty0l1hUQTcNWswS3WRp2kCNZwJG8omG8JphPUaFbC8lEfabwP7VtM9yoaNCAjpR41VNhrD9LkbN722v0CoZMByFzhaW+MyzRYEWFDQwN2M4/JiT76PuljT3VU/A36eaIThb+R9oZGOAJ9tewkgGvqOMNRWYjT/Cwu99Q8LqDE4TgbLWxJ1jaDDAERsFOFrobgjUsBScaguXU8kKm2RL19tRypSHnHNlHiIZqgufs4opgQdVdwxBNNFBR6kVFqb8ogimOzB6a6HTzrlDHEpYaxjiiA4TMQobkDg2vejjfwJGWmnbVFAw3H3hq2NyQfG7hz4aC+w3BbwbesG0swYayvpAs6++Ri1Vfzx93mFChvyN5xVHTS+0p9aqCAxyZ6ZacZyw5+7uuQkFPR9DDk9NOiE7X1PCYJVjVUqq7JlrHwWALF5nfHNGjApdpqgzx5OwilDhCiDYTgnc9waGW4BdLNNUQvOtpzDOWHDH8D7TR/A/85KljEQu3NREc4Pl/6B1Hhc8Umb5CsKMmGC9EPcxoT2amwHNCmeOEnOPbklnMkbOgIvO5UMOpQrS9UGVdt6iH/fURjhI/WOpaW9OKLYRod6HCUEdOX000wpDZQ6hwg6LgZfOqo1RfT/CrJzjekXOGhpc1VW71ZLbXyyp+93ILbC1kPtIEYx0FIx1VDrLoVzXRKRYWk809yYlC9ImcrinxtabKnzRJk3lAU1OLEN1j2zrYzr2myHRXJFf4h4QKT1qSTzTB5+ZNTzTRkAxX8FcLV2uS8eoQQ2aAkFzvCM72sJIcJET3WPjRk5wi32uSS9rfZajpWEvj9hW42F4o5NytSXYy8IKHay10VYdrcl4SkqscrXpMwyGOgtkajheSxdQqmpxP1L3t4R5PqasFnrQEjytq6qgp9Y09Qx9o4S1FzhUCn1kyHSzBWLemoSGvOqLNhZyBjmCaAUYpMgt4Ck7wBBMMwWKWgjsUwTaGVsxWC1mYoKiyqqeGKYqonSIRQ3KIkHO0pmAxTdBHkbOvfllfr+AA+7gnc50huVKYK393FOyg7rbPO/izI7hE4CnHHHnJ0ogNPRUGeUpsrZZTBJcrovUcJe51BPsr6GkJdhCCsZ6aTtMEb2pqWkqeVtDXE/QVggsU/Nl86d9RMF3DxvZTA58agu810RWawCiSzzXBeU3MMW9oyJUedvNEvQyNu1f10BSMddR1vaLCYpYa/mGocLSiYDcLbQz8aMn5iyF4xBNMs1P0QEOV7o5gaWGuzSeLue4tt3ro7y4Tgm4G/mopdZgl6q0o6KzJWE3mMksNr3r+a6CbT8g5wZNzT9O7fi/zpaOmnz3BRoqos+tv9zMbdpxsqDBOEewtJLt7cg5wtKKbvldpSzRRCD43VFheCI7yZLppggMVBS/KMAdHODJvOwq2NQSbKKKPLdFWQs7Fqo+mpl01JXYRgq8dnGLhTiFzqmWsUMdpllZdbKlyvSdYxhI9YghOtxR8LgSLWHK62mGGVoxzBE8LNWzqH9CUesQzFy5RQzTc56mhi6fgXEWwpKfE5Z7M05ZgZUPmo6auiv8YKzDYwWBLMErIbKHJvOwIrvEdhOBcQ9JdU1NHQ7CXn2XIDFBKU2WAgcX9UAUzDXWd5alwuyJ41Z9rjKLCL4aCp4WarhPm2rH+SaHUYE001JDZ2ZAzXPjdMpZWvC9wmqIB2lLhQ01D5jO06hghWMndbM7yRJMsoCj1vYbnFQVrW9jak3OlEJ3s/96+p33dEPRV5GxiqaGjIthUU6FFEZyqCa5qJrpBdzSw95IUnOPIrCUUjRZQFrbw5PR0R1qiYx3cb6nrWUMrBmmiBQxVHtTew5ICP/ip6g4hed/Akob/32wvBHsIOX83cI8hGeNeNPCIkPmXe8fPKx84OMSRM1MTdXSwjCZ4S30jVGhvqTRak/OVhgGazHuOCud5onEO1lJr6ecVyaOK6H7zqlBlIaHE0oroCgfvGJIdPcmfLNGLjpz7hZwZQpUbFME0A1cIJa7VNORkgfsMBatbKgwwJM9bSvQXeNOvbIjelg6WWvo5kvbKaJJNHexkKNHL9xRyFlH8Ti2riB5wVPhUk7nGkJnoCe428LR/wRGdYIlmWebCyxou1rCk4g/ShugBDX0V0ZQWkh0dOVsagkM0yV6OoLd5ye+pRlsCr0n+KiQrGuq5yJDzrTAXHtLUMduTDBVKrSm3eHL+6ijxhFDX9Z5gVU/wliHYTMiMFpKLNMEywu80wd3meoFmt6VbRMPenhrOc6DVe4pgXU8DnnHakLOIIrlF4FZPIw6R+zxBP0dyq6OOZ4Q5sLKCcz084ok+VsMMyQhNZmmBgX5xIXOEJTmi7VsGTvMTNdHHhpzdbE8Du2oKxgvBqQKdDDnTFOylCFaxR1syz2iqrOI/FEpNc3C6f11/7+ASS6l2inq2ciTrCCzgyemrCL5SVPjQkdPZUmGy2c9Sw9FtR1sS30RmsKPCS4rkIC/2U0MduwucYolGaPjKEyhzmiPYXagyWbYz8LWBDdzRimAXzxx4z8K9hpzlhLq+NiQ97HuKorMUfK/OVvC2JfiHUPCQI/q7J2gjK+tTDNxkCc4TMssqCs4TGtLVwQihyoAWgj9bosU80XGW6Ac9TJGziaUh5+hnFcHOnlaM1iRn29NaqGENTTTSUHCH2tWTeV0osUhH6psuVLjRUmGWhm6OZEshGeNowABHcJ2Bpy2ZszRcKkRXd2QuKVEeXnbfaEq825FguqfgfE2whlChSRMdron+LATTPQ2Z369t4B9C5gs/ylzv+CMmepIDPclFQl13W0rspPd1JOcbghGOEutqCv5qacURQl3dDKyvyJlqKXGPgcM9FfawJAMVmdcspcYKOZc4GjDYkFlK05olNMHyHn4zFNykyOxt99RkHlfwmiHo60l2EKI+mhreEKp080Tbug08BVPcgoqC5zWt+NLDTZ7oNSF51N1qie7Va3uCCwyZbkINf/NED6jzOsBdZjFN8oqG3wxVunqCSYYKf3EdhJyf9YWGf7tRU2oH3VHgPr1fe5J9hOgHd7xQ0y7qBwXr23aGErP0cm64JVjZwsOGqL+mhNgZmhJLW2oY4UhedsyBgzrCKrq7BmcpNVhR6jBPq64Vgi+kn6XE68pp8J5/+0wRHGOpsKenQn9DZntPzjRLZpDAdD2fnSgkG9tmIXnUwQ6WVighs7Yi2MxQ0N3CqYaCXkJ0oyOztMDJjmSSpcpvlrk0RMMOjmArQ04PRV1DO1FwhCVaUVPpKUM03JK5SxPsIWRu8/CGHi8UHChiqGFDTbSRJWeYUDDcH6vJWUxR4k1FXbMUwV6e4AJFXS8oMqsZKqzvYQ9DDQdZckY4aGsIhtlubbd2r3j4QBMoTamdPZk7O/Bf62lacZwneNjQoGcdVU7zJOd7ghsUHOkosagic6cnWc8+4gg285R6zZP5s1/LUbCKIznTwK36PkdwlOrl4U1LwfdCCa+IrvFkmgw1PCAUXKWo0sURXWcI2muKJlgyFzhynCY4RBOsqCjoI1R5zREco0n2Vt09BQtYSizgKNHfUmUrQ5UOCh51BFcLmY7umhYqXKQomOop8bUnWNNQcIiBcYaC6xzMNOS8JQQfeqKBmmglB+97ok/lfk3ygaHSyZaCRTzRxQo6GzLfa2jWBPepw+UmT7SQEJyiyRkhBLMVOfcoMjcK0eZChfUNzFAUzCsEN5vP/X1uP/n/aoMX+K+nw/Hjr/9xOo7j7Pju61tLcgvJpTWXNbfN5jLpi6VfCOviTktKlFusQixdEKWmEBUKNaIpjZRSSOXSgzaaKLdabrm1/9nZ+/f+vd/vz/v9+Xy+zZ7PRorYoZqyLrCwQdEAixxVOEXNNnjX2nUSRlkqGmWowk8lxR50JPy9Bo6qJXaXwNvREBvnThPEPrewryLhcAnj5WE15Fqi8W7R1sAuEu86S4ENikItFN4xkv9Af4nXSnUVcLiA9xzesFpivRRVeFKtsMRaKBhuSbjOELnAUtlSQUpXgdfB4Z1oSbnFEetbQ0IrAe+Y+pqnDcEJFj6S8LDZzZHwY4e3XONNlARraomNEt2bkvGsosA3ioyHm+6jCMbI59wqt4eeara28IzEmyPgoRaUOEDhTVdEJhmCoTWfC0p8aNkCp0oYqih2iqGi4yXeMkOsn4LdLLnmKfh/YogjNsPebeFGR4m9BJHLzB61XQ3BtpISfS2FugsK9FAtLWX1dCRcrCnUp44CNzuCowUZmxSRgYaE6Za0W2u/E7CVXCiI/UOR8aAm1+OSyE3mOUcwyc1zBBeoX1kiKy0Zfxck1Gsyulti11i83QTBF5Kg3pDQThFMVHiPSlK+0cSedng/VaS8bOZbtsBcTcZAR8JP5KeqQ1OYKAi20njdNNRpgnsU//K+JnaXJaGTomr7aYIphoRn9aeShJWKEq9LcozSF7QleEfDI5LYm5bgVkFkRwVDBCVu0DDIkGupo8TZBq+/pMQURYErJQmPKGKjNDkWOLx7Jd5QizdUweIaKrlP7SwJDhZvONjLkOsBBX9UpGxnydhXkfBLQ8IxgojQbLFnJf81JytSljclYYyEFyx0kVBvKWOFJmONpshGAcsduQY5giVNCV51eOdJYo/pLhbvM0uDHSevNKRcrKZIqnCtJeEsO95RoqcgGK4ocZcho1tTYtcZvH41pNQ7vA0WrhIfOSraIIntIAi+NXWCErdbkvrWwjRLrt0NKUdL6KSOscTOdMSOUtBHwL6OLA0vNSdynaWQEnCpIvKaIrJJEbvHkmuNhn6OjM8VkSGSqn1uYJCGHnq9I3aLhNME3t6GjIkO7xrNFumpyTNX/NrwX7CrIRiqqWijI9JO4d1iieykyfiposQIQ8YjjsjlBh6oHWbwRjgYJQn2NgSnNycmJAk3NiXhx44Sxykihxm8ybUwT1OVKySc7vi3OXVkdBJ4AyXBeksDXG0IhgtYY0lY5ahCD0ehborIk5aUWRJviMA7Xt5kyRjonrXENkm8yYqgs8VzgrJmClK20uMM3jRJ0FiQICQF9hdETlLQWRIb5ki6WDfWRPobvO6a4GP5mcOrNzDFELtTkONLh9dXE8xypEg7z8A9jkhrQ6Fhjlg/QVktJXxt4WXzT/03Q8IaQWSqIuEvloQ2mqC9Jfi7wRul4RX3pSPlzpoVlmCtI2jvKHCFhjcM3sN6lqF6HxnKelLjXWbwrpR4xzuCrTUZx2qq9oAh8p6ixCUGr78g8oyjRAtB5CZFwi80VerVpI0h+IeBxa6Zg6kWvpDHaioYYuEsRbDC3eOmC2JvGYLeioxGknL2UATNJN6hmtj1DlpLvDVmocYbrGCVJKOrg4X6DgddLA203BKMFngdJJFtFd7vJLm6KEpc5yjQrkk7M80SGe34X24nSex1Ra5Omgb71JKyg8SrU3i/kARKwWpH0kOGhKkObyfd0ZGjvyXlAkVZ4xRbYJ2irFMkFY1SwyWxr2oo4zlNiV+7zmaweFpT4kR3kaDAFW6xpSqzJay05FtYR4HmZhc9UxKbbfF2V8RG1MBmSaE+kmC6JnaRXK9gsiXhJHl/U0qM0WTcbyhwkYIvFGwjSbjfwhiJt8ZSQU+Bd5+marPMOkVkD0muxYLIfEuhh60x/J92itguihJSEMySVPQnTewnEm+620rTQEMsOfo4/kP/0ARvWjitlpSX7GxBgcMEsd3EEeYWvdytd+Saawi6aCIj1CkGb6Aj9rwhx16Cf3vAwFy5pyLhVonXzy51FDpdEblbkdJbUcEPDEFzQ8qNmhzzLTmmKWKbFCXeEuRabp6rxbvAtLF442QjQ+wEA9eL1xSR7Q0JXzlSHjJ4exq89yR0laScJ/FW6z4a73pFMEfDiRZvuvijIt86RaSFOl01riV2mD1UEvxGk/Geg5aWwGki1zgKPG9J2U8PEg8qYvMsZeytiTRXBMslCU8JSlxi8EabjwUldlDNLfzTUmCgxWsjqWCOHavYAqsknKFIO0yQ61VL5AVFxk6WhEaCAkdJgt9aSkzXlKNX2jEa79waYuc7gq0N3GDJGCBhoiTXUEPsdknCUE1CK0fwsiaylSF2uiDyO4XX3pFhNd7R4itFGc0k/ElBZwWvq+GC6szVeEoS/MZ+qylwpKNKv9Z469UOjqCjwlusicyTxG6VpNxcQ8IncoR4RhLbR+NdpGGmJWOcIzJGUuKPGpQg8rrG21dOMqQssJQ4RxH5jaUqnZuQ0F4Q+cjxLwPtpZbIAk3QTJHQWBE5S1BokoVtDd6lhqr9UpHSUxMcIYl9pojsb8h4SBOsMQcqvOWC2E8EVehqiJ1hrrAEbQxeK0NGZ0Gkq+guSRgniM23bIHVkqwx4hiHd7smaOyglyIyQuM978j4VS08J/A2G1KeMBRo4fBaSNhKUEZfQewVQ/C1I+MgfbEleEzCUw7mKXI0M3hd1EESVji8x5uQ41nxs1q4RMJCCXs7Iq9acpxn22oSDnQ/sJTxsCbHIYZiLyhY05TY0ZLIOQrGaSJDDN4t8pVaIrsqqFdEegtizc1iTew5Q4ayBDMUsQMkXocaYkc0hZua412siZ1rSXlR460zRJ5SlHGe5j801RLMlJTxtaOM3Q1pvxJ45zUlWFD7rsAbpfEm1JHxG0eh8w2R7QQVzBUw28FhFp5QZzq8t2rx2joqulYTWSuJdTYfWwqMFMcovFmSyJPNyLhE4E10pHzYjOC3huArRa571ZsGajQpQx38SBP5pyZB6lMU3khDnp0MBV51BE9o2E+TY5Ml2E8S7C0o6w1xvCZjf0HkVEHCzFoyNmqC+9wdcqN+Tp7jSDheE9ws8Y5V0NJCn2bk2tqSY4okdrEhx1iDN8cSudwepWmAGXKcJXK65H9to8jYQRH7SBF01ESUJdd0TayVInaWhLkOjlXE5irKGOnI6GSWGCJa482zBI9rCr0jyTVcEuzriC1vcr6mwFGSiqy5zMwxBH/TJHwjSPhL8+01kaaSUuMFKTcLEvaUePcrSmwn8DZrgikWb7CGPxkSjhQwrRk57tctmxLsb9sZvL9LSlyuSLlWkqOjwduo8b6Uv1DkmudIeFF2dHCgxVtk8dpIvHpBxhEOdhKk7OLIUSdJ+cSRY57B+0DgGUUlNfpthTfGkauzxrvTsUUaCVhlKeteTXCoJDCa2NOKhOmC4G1H8JBd4OBZReSRGkqcb/CO1PyLJTLB4j1q8JYaIutEjSLX8YKM+a6phdMsdLFUoV5RTm9JSkuDN8WcIon0NZMNZWh1q8C7SJEwV5HxrmnnTrf3KoJBlmCYI2ilSLlfEvlE4011NNgjgthzEua0oKK7JLE7HZHlEl60BLMVFewg4EWNt0ThrVNEVkkiTwpKXSWJzdRENgvKGq4IhjsiezgSFtsfCUq8qki5S1LRQeYQQ4nemmCkImWMw3tFUoUBZk4NOeZYEp4XRKTGa6wJjrWNHBVJR4m3FCnbuD6aak2WsMTh3SZImGCIPKNgsDpVwnsa70K31lCFJZYcwwSMFcQulGTsZuEaSdBXkPGZhu0FsdUO73RHjq8MPGGIfaGIbVTk6iuI3GFgucHrIQkmWSJdBd7BBu+uOryWAhY7+Lki9rK5wtEQzWwvtbqGhIMFwWRJsElsY4m9IIg9L6lCX0VklaPAYkfkZEGDnOWowlBJjtMUkcGK4Lg6EtoZInMUBVYLgn0UsdmCyCz7gIGHFfk+k1QwTh5We7A9x+IdJ6CvIkEagms0hR50eH9UnTQJ+2oiKyVlLFUE+8gBGu8MQ3CppUHesnjTHN4QB/UGPhCTHLFPHMFrCqa73gqObUJGa03wgbhHkrCfpEpzNLE7JDS25FMKhlhKKWKfCgqstLCPu1zBXy0J2ztwjtixBu8UTRn9LVtkmCN2iyFhtME70JHRQ1KVZXqKI/KNIKYMCYs1GUMEKbM1bKOI9LDXC7zbHS+bt+1MTWS9odA9DtrYtpbImQJ2VHh/lisEwaHqUk1kjKTAKknkBEXkbkdMGwq0dnhzLJF3NJH3JVwrqOB4Sca2hti75nmJN0WzxS6UxDYoEpxpa4htVlRjkYE7DZGzJVU72uC9IyhQL4i8YfGWSYLLNcHXloyz7QhNifmKSE9JgfGmuyLhc403Xm9vqcp6gXe3xuuv8F6VJNxkyTHEkHG2g0aKXL0MsXc1bGfgas2//dCONXiNLCX+5mB7eZIl1kHh7ajwpikyzlUUWOVOsjSQlsS+M0R+pPje/dzBXRZGO0rMtgQrLLG9VSu9n6CMXS3BhwYmSoIBhsjNBmZbgusE9BCPCP5triU4VhNbJfE+swSP27aayE8tuTpYYjtrYjMVGZdp2NpS1s6aBnKSHDsbKuplKbHM4a0wMFd/5/DmGyKrJSUaW4IBrqUhx0vyfzTBBLPIUcnZdrAkNsKR0sWRspumSns6Ch0v/qqIbBYUWKvPU/CFoyrDJGwSNFhbA/MlzKqjrO80hRbpKx0Jewsi/STftwGSlKc1JZyAzx05dhLEdnfQvhZOqiHWWEAHC7+30FuRcZUgaO5gpaIK+xsiHRUsqaPElTV40xQZQ107Q9BZE1nryDVGU9ZSQ47bmhBpLcYpUt7S+xuK/FiT8qKjwXYw5ypS2iuCv7q1gtgjhuBuB8LCFY5cUuCNtsQOFcT+4Ih9JX+k8Ea6v0iCIRZOtCT0Et00JW5UeC85Cg0ScK0k411HcG1zKtre3SeITBRk7WfwDhEvaYLTHP9le0m8By0JDwn4TlLW/aJOvGHxdjYUes+ScZigCkYQdNdEOhkiezgShqkx8ueKjI8lDfK2oNiOFvrZH1hS+tk7NV7nOmLHicGWEgubkXKdwdtZknCLJXaCpkrjZBtLZFsDP9CdxWsSr05Sxl6CMmoFbCOgryX40uDtamB7SVmXW4Ihlgpmq+00tBKUUa83WbjLUNkzDmY7cow1JDygyPGlhgGKYKz4vcV7QBNbJIgM11TUqZaMdwTeSguH6rOaw1JRKzaaGyxVm2EJ/uCIrVWUcZUkcp2grMsEjK+DMwS59jQk3Kd6SEq1d0S6uVmO4Bc1lDXTUcHjluCXEq+1OlBDj1pi9zgiXxnKuE0SqTXwhqbETW6RggMEnGl/q49UT2iCzgJvRwVXS2K/d6+ZkyUl7jawSVLit46EwxVljDZwoSQ20sDBihztHfk2yA8NVZghiXwrYHQdfKAOtzsayjhY9bY0yE2CWEeJ9xfzO423xhL5syS2TFJofO2pboHob0nY4GiAgRrvGQEDa/FWSsoaaYl0syRsEt3kWoH3B01shCXhTUWe9w3Bt44SC9QCh3eShQctwbaK2ApLroGCMlZrYqvlY3qYhM0aXpFkPOuoqJ3Dm6fxXrGwVF9gCWZagjPqznfkuMKQ8DPTQRO8ZqG1hPGKEm9IgpGW4DZDgTNriTxvFiq+Lz+0cKfp4wj6OCK9JSnzNSn9LFU7UhKZZMnYwcJ8s8yRsECScK4j5UOB95HFO0CzhY4xJxuCix0lDlEUeMdS6EZBkTsUkZ4K74dugyTXS7aNgL8aqjDfkCE0ZbwkCXpaWCKhl8P7VD5jxykivSyxyZrYERbe168LYu9ZYh86IkscgVLE7tWPKmJv11CgoyJltMEbrohtVAQfO4ImltiHEroYEs7RxAarVpY8AwXMcMReFOTYWe5iiLRQxJ5Q8DtJ8LQhWOhIeFESPGsILhbNDRljNbHzNRlTFbk2S3L0NOS6V1KFJYKUbSTcIIhM0wQ/s2TM0SRMNcQmSap3jCH4yhJZKSkwyRHpYYgsFeQ4U7xoCB7VVOExhXepo9ABBsYbvGWKXPME3lyH95YioZ0gssQRWWbI+FaSMkXijZXwgiTlYdPdkNLaETxlyDVIwqeaEus0aTcYcg0RVOkpR3CSJqIddK+90JCxzsDVloyrFd5ZAr4TBKfaWa6boEA7C7s6EpYaeFPjveooY72mjIccLHJ9HUwVlDhKkmutJDJBwnp1rvulJZggKDRfbXAkvC/4l3ozQOG9a8lxjx0i7nV4jSXc7vhe3OwIxjgSHjdEhhsif9YkPGlus3iLFDnWOFhtCZbJg0UbQcIaR67JjthoCyMEZRwhiXWyxO5QxI6w5NhT4U1WsJvDO60J34fW9hwzwlKij6ZAW9ne4L0s8C6XeBMEkd/LQy1VucBRot6QMlbivaBhoBgjqGiCJNhsqVp/S2SsG6DIONCR0dXhvWbJ+MRRZJkkuEjgDXJjFQW6SSL7GXK8Z2CZg7cVsbWGoKmEpzQ5elpiy8Ryg7dMkLLUEauzeO86CuwlSOlgYLojZWeJ9xM3S1PWfEfKl5ISLQ0MEKR8YOB2QfCxJBjrKPCN4f9MkaSsqoVXJBmP7EpFZ9UQfOoOFwSzBN4MQ8LsGrymlipcJQhmy0GaQjPqCHaXRwuCZwRbqK2Fg9wlClZqYicrIgMdZfxTQ0c7TBIbrChxmuzoKG8XRaSrIhhiyNFJkrC7oIAWMEOQa5aBekPCRknCo4IKPrYkvCDI8aYmY7WFtprgekcJZ3oLIqssCSMtFbQTJKwXYy3BY5oCh2iKPCpJOE+zRdpYgi6O2KmOAgvVCYaU4ySRek1sgyFhJ403QFHiVEmJHwtybO1gs8Hr5+BETQX3War0qZngYGgtVZtoqd6vFSk/UwdZElYqyjrF4HXUeFspIi9IGKf4j92pKGAdCYMVsbcV3kRF0N+R8LUd5PCsIGWoxDtBkCI0nKofdJQxT+LtZflvuc8Q3CjwWkq8KwUpHzkK/NmSsclCL0nseQdj5FRH5CNHSgtLiW80Of5HU9Hhlsga9bnBq3fEVltKfO5IaSTmGjjc4J0otcP7QsJUSQM8pEj5/wCuUuC2DWz8AAAAAElFTkSuQmCC\") 0 0;color:#333}sr-rd-title{font-size:56.32px;font-size:3.52rem;line-height:64px;line-height:4rem;font-weight:700;background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAABCAYAAACsXeyTAAAACXBIW…sTAAALEwEAmpwYAAAAFUlEQVQIHWNIS0sr/v//PwMMDzY+ADqMahlW4J91AAAAAElFTkSuQmCC) 0 100% repeat-x}sr-rd-desc{font-style:italic;font-size:30.72px;font-size:1.92rem;line-height:2;padding-left:1em;border-left:4px solid hsla(0,0%,67%,.5)}sr-rd-content{margin:0 auto;padding:1em 0}sr-rd-content,sr-rd-content *,sr-rd-content div,sr-rd-content p{line-height:2;color:#333}sr-rd-content a,sr-rd-content a:link{color:#1863a1;text-decoration:underline}sr-rd-content a:active,sr-rd-content a:focus,sr-rd-content a:hover{color:#0181eb;text-decoration:underline}sr-rd-content pre{color:#586e75;background-color:#fdf6e3;border-radius:.4em;border:1px solid #e7dec3}sr-rd-content li code,sr-rd-content p code{color:#555;background-color:transparent;border:1px solid #ddd}sr-rd-mult{background-color:#ededed}</style><style type=\"text/css\">sr-rd-theme-pixyii{display:none}sr-rd-content h1,sr-rd-content h1 *,sr-rd-content h2,sr-rd-content h2 *,sr-rd-content h3,sr-rd-content h3 *,sr-rd-content h4,sr-rd-content h4 *,sr-rd-content h5,sr-rd-content h5 *,sr-rd-content h6,sr-rd-content h6 *{color:inherit;font-weight:900;line-height:1.2;margin:1em 0}sr-rd-content h1,sr-rd-content h1 *{font-size:62.72px;font-size:3.92rem}sr-rd-content h2,sr-rd-content h2 *{font-size:58.24px;font-size:3.64rem}sr-rd-content h3,sr-rd-content h3 *{font-size:36.4px;font-size:2.275rem}sr-rd-content h4,sr-rd-content h4 *{font-size:29.12px;font-size:1.82rem}sr-rd-content h5,sr-rd-content h5 *,sr-rd-content h6,sr-rd-content h6 *{font-size:25.168px;font-size:1.573rem}sr-rd-content ol,sr-rd-content ul{font-size:28px;font-size:1.75rem;line-height:24px;line-height:1.5rem}sr-rd-content li{font-size:25.2px;font-size:1.575rem;line-height:1.8;margin:0;position:relative}sr-rd-content table{width:100%;font-size:25.2px;font-size:1.575rem}sr-rd-content table>tbody>tr>td,sr-rd-content table>tbody>tr>th,sr-rd-content table>tfoot>tr>td,sr-rd-content table>tfoot>tr>th,sr-rd-content table>thead>tr>td,sr-rd-content table>thead>tr>th{padding:12px;line-height:1.2;vertical-align:top;border-top:1px solid #333}sr-rd-content table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #333}sr-rd-content table>caption+thead>tr:first-child>td,sr-rd-content table>caption+thead>tr:first-child>th,sr-rd-content table>colgroup+thead>tr:first-child>td,sr-rd-content table>colgroup+thead>tr:first-child>th,sr-rd-content table>thead:first-child>tr:first-child>td,sr-rd-content table>thead:first-child>tr:first-child>th{border-top:0}sr-rd-content table>tbody+tbody{border-top:2px solid #333}sr-rd-content sr-blockquote{margin:16px 0;margin:1rem 0;padding:1.33em;font-style:italic;border-left:5px solid #7a7a7a;color:#555}.simpread-theme-root{background-color:#fff;color:#555}sr-rd-title{font-family:PingFang SC,Hiragino Sans GB,Microsoft Yahei,WenQuanYi Micro Hei,sans-serif;font-size:67.2px;font-size:4.2rem;font-weight:900;line-height:1.2}sr-rd-desc{margin:16px 0;margin:1rem 0;padding:1.33em;font-style:italic;font-size:32px;font-size:2rem;line-height:2;border-left:5px solid #7a7a7a;color:#555}sr-rd-content{font-size:33.6px;font-size:2.1rem;line-height:1.8;font-weight:400;color:#555}sr-rd-content *,sr-rd-content div,sr-rd-content p{color:#555;font-size:28px;font-size:1.75rem;line-height:1.8;font-weight:300}sr-rd-content b,sr-rd-content b *,sr-rd-content strong,sr-rd-content strong *{font-weight:700}sr-rd-content a,sr-rd-content a:active,sr-rd-content a:focus,sr-rd-content a:hover,sr-rd-content a:link{color:#463f5c;text-decoration:underline}sr-rd-content sr-blockquote code{font-size:inherit}sr-rd-content pre{border:1px solid #7a7a7a}sr-rd-content li code,sr-rd-content p code,sr-rd-content pre{color:#7a7a7a;background-color:transparent}.simpread-multi-root{background:#f8f9fa}</style><style type=\"text/css\">sr-rd-theme-monospace{display:none}sr-rd-content h1,sr-rd-content h2,sr-rd-content h3,sr-rd-content h4,sr-rd-content h5,sr-rd-content h6{font-weight:700;color:#6363ac}sr-rd-content h1{font-size:35.2px;font-size:2.2rem}sr-rd-content h2{font-size:32px;font-size:2rem}sr-rd-content h3{font-size:28.8px;font-size:1.8rem}sr-rd-content h4{font-size:25.6px;font-size:1.6rem}sr-rd-content h5{font-size:22.4px;font-size:1.4rem}sr-rd-content h6{font-size:20.8px;font-size:1.3rem}sr-rd-content strong{color:#b5302e}sr-rd-content em{font-style:italic;color:#400469}sr-rd-content ol,sr-rd-content ul{list-style-type:none}sr-rd-content ol li,sr-rd-content ul li{margin:0}sr-rd-content table{line-height:25.6px;line-height:1.6rem;border-collapse:collapse;border-spacing:0;empty-cells:show;border:1px solid #cbcbcb}sr-rd-content thead{background-color:#e0e0e0;color:#000;text-align:left;vertical-align:bottom}sr-rd-content td,sr-rd-content th{border-left:1px solid #cbcbcb;border-width:0 0 0 1px;margin:0;overflow:visible;padding:.5em 1em}sr-rd-content sr-blockquote{background-color:hsla(0,0%,50%,.05);border-top-right-radius:5px;border-bottom-right-radius:5px;border-left:8px solid #979797;line-height:2}sr-rd-content sr-blockquote *{line-height:inherit}.simpread-theme-root{color:#333;background:#fff}sr-rd-title{font-size:44.8px;font-size:2.8rem;line-height:1.2;font-weight:700;color:#6363ac}sr-rd-desc{padding:10px;background-color:hsla(0,0%,50%,.05);font-size:28.8px;font-size:1.8rem;text-align:center;border-top-right-radius:5px;border-bottom-right-radius:5px;border-left:8px solid #979797}sr-rd-content{color:#333}sr-rd-content,sr-rd-content *,sr-rd-content div,sr-rd-content p{line-height:1.7}sr-rd-content a,sr-rd-content a:link{color:#005dad;text-decoration:underline}sr-rd-content a:active,sr-rd-content a:focus,sr-rd-content a:hover{color:#fff;background-color:#2a6496;text-decoration:none}sr-rd-content pre{color:#e9eded;background-color:#263238}sr-rd-content li code,sr-rd-content p code{color:#949415;background-color:transparent}.simpread-multi-root{background:#f8f9fa}</style><style type=\"text/css\">sr-rd-theme-night{display:none}sr-rd-content h1{margin-top:2em}sr-rd-content h1,sr-rd-content h2,sr-rd-content h3,sr-rd-content h4,sr-rd-content h5,sr-rd-content h6{color:#dedede;font-weight:400;clear:both;-ms-word-wrap:break-word;word-wrap:break-word;margin:0;padding:0}sr-rd-content h1{font-size:57.6px;font-size:3.6rem;line-height:64px;line-height:4rem;margin-bottom:38.4px;margin-bottom:2.4rem;letter-spacing:-1.5px}sr-rd-content h2{font-size:38.4px;font-size:2.4rem;line-height:48px;line-height:3rem}sr-rd-content h2,sr-rd-content h3{margin-bottom:38.4px;margin-bottom:2.4rem;letter-spacing:-1px}sr-rd-content h3{font-size:28.8px;font-size:1.8rem;line-height:38.4px;line-height:2.4rem}sr-rd-content h4{font-size:25.6px;font-size:1.6rem;line-height:35.2px;line-height:2.2rem;margin-bottom:38.4px;margin-bottom:2.4rem}sr-rd-content h5{font-size:16px;font-size:1rem;line-height:20px;line-height:1.25rem;margin-bottom:24px;margin-bottom:1.5rem}sr-rd-content h6{font-size:25.6px;font-size:1.6rem;line-height:25.6px;line-height:1.6rem;margin-bottom:12px;margin-bottom:.75rem;font-weight:700}sr-rd-content ol,sr-rd-content ul{padding:0 0 0 30px;padding:0 0 0 1.875rem}sr-rd-content ul{list-style:square}sr-rd-content ol{list-style:decimal}sr-rd-content ol ol,sr-rd-content ol ul,sr-rd-content ul ol,sr-rd-content ul ul{margin:0}sr-rd-content li div{padding-top:0}sr-rd-content li,sr-rd-content li p{margin:0;position:relative}sr-rd-content table{margin-top:0;margin-bottom:24px;margin-bottom:1.5rem;border-collapse:collapse;border-spacing:0;page-break-inside:auto;text-align:left}sr-rd-content table a{color:#dedede}sr-rd-content thead{display:table-header-group}sr-rd-content table td,sr-rd-content table th{border:1px solid #474d54}sr-rd-content sr-blockquote{margin:0 0 30px 30px;margin:0 0 1.875rem 1.875rem;border-left:2px solid #474d54;padding-left:30px;margin-top:35px;line-height:2}.simpread-multi-root,.simpread-theme-root{background:#363b40;color:#b8bfc6}sr-rd-title{color:#dedede;font-size:50.4px;font-size:3.15rem;line-height:56px;line-height:3.5rem;letter-spacing:-1.5px}sr-rd-desc{margin:35px;margin-left:0;padding-left:30px;padding-left:1.875rem;font-size:32px;font-size:2rem;line-height:2;border-left:2px solid #474d54}sr-rd-content,sr-rd-desc{color:#b8bfc6}sr-rd-content *,sr-rd-content div,sr-rd-content p{color:#b8bfc6;margin-top:0;line-height:2}sr-rd-content a,sr-rd-content a:link{color:#e0e0e0;text-decoration:underline;-webkit-transition:all .2s ease-in-out;transition:all .2s ease-in-out}sr-rd-content a:active,sr-rd-content a:focus,sr-rd-content a:hover{color:#fff}sr-rd-content pre{background-color:transparent;border:1px solid}sr-rd-content li code,sr-rd-content p code{background:rgba(0,0,0,.05)}sr-rd-mult{background-color:#2d3034}panel{background-color:#2e2e2e!important}panel panel-tab span{color:#fff}panel sr-opt-gp sr-opt-label{color:rgba(108,255,240,.8);font-weight:400}panel text-field-float{color:rgba(108,255,240,.8)!important;font-weight:400!important}panel list-field{background-color:transparent!important}panel list-field:hover list-field-name{color:#fff!important;font-weight:700}panel input,panel list-field-name{color:hsla(35,10%,76%,.87)!important}panel list-view{background-color:#2e2e2e!important}panel sr-opt-gp action-label,panel switch content *{color:#fff!important}panel-tabs{border-bottom-color:#393d40!important}sr-annote{color:#333}sr-annote-sidebar *{color:hsla(36,10%,90%,.9)!important}sr-annote-sidebar-card[type=unread]{background-color:#19896e!important}sr-annote-sidebar-card:hover{box-shadow:0 10px 20px 0 rgba(60,73,82,.6)!important}sr-annote-sidebar-note,sr-annote-sidebar-options,sr-annote-sidebar-toolbars{background-color:#282b2d!important}sr-rd-theme-night-comp{display:none;opacity:0}dialog-content,dialog-footer{color:#b8bfc6!important;background-color:#2e2e2e!important}dialog-content *{color:#b8bfc6!important}dialog-content svg path{fill:#b8bfc6!important}dialog-content button-text{color:#b8bfc6!important}dialog-content text-field-state{border-top:none #b8bfc6!important;border-left:none #b8bfc6!important;border-right:none #b8bfc6!important;border-bottom:2px solid #b8bfc6!important}dialog-footer button-text{color:#b8bfc6!important}auto-complete list-view,dropdown list-view{color:#b8bfc6!important;background-color:#2e2e2e!important}auto-complete list-field,dropdown list-field{-webkit-transition:all .45s cubic-bezier(.23,1,.32,1) 0ms;transition:all .45s cubic-bezier(.23,1,.32,1) 0ms}auto-complete list-field[active=true],dropdown list-field[active=true]{color:#b8bfc6!important;background-color:#1b1b1b!important}auto-complete tag{background-color:#1b1b1b!important}sr-rd-theme-night-comp{display:none}</style><style type=\"text/css\">sr-rd-theme-dark{display:none}sr-rd-content h1,sr-rd-content h2,sr-rd-content h3,sr-rd-content h4,sr-rd-content h5,sr-rd-content h6{font-weight:700;color:#dbdbfd}sr-rd-content h1{font-size:48px;font-size:3rem}sr-rd-content h2{font-size:44.8px;font-size:2.8rem}sr-rd-content h3{font-size:40px;font-size:2.5rem}sr-rd-content h4,sr-rd-content h5,sr-rd-content h6{color:#549ad8}sr-rd-content h5{font-size:32px;font-size:2rem}sr-rd-content h6{font-size:28.8px;font-size:1.8rem}sr-rd-content strong{color:#ffffc5}sr-rd-content em{color:#c885f5}sr-rd-content table{width:100%;line-height:25.6px;line-height:1.6rem;border-collapse:collapse;border-spacing:0;empty-cells:show;border:1px solid #cbcbcb}sr-rd-content thead{background-color:#263238;color:#f5f5f5;text-align:left;vertical-align:bottom}sr-rd-content table td,sr-rd-content table th{border-left:1px solid #cbcbcb;border-width:0 0 0 1px;margin:0;overflow:visible;padding:.5em 1em}sr-rd-content sr-blockquote{background-color:hsla(0,0%,50%,.05);border-top-right-radius:5px;border-bottom-right-radius:5px;border-left:8px solid #979797;color:#ebebeb}.simpread-multi-root,.simpread-theme-root{color:#ebebeb;background:#222}sr-rd-title{padding-bottom:.3em;font-size:44.8px;font-size:2.8rem;font-weight:700;line-height:1.2;color:#dbdbfd;border-bottom:1px solid #eee}sr-rd-desc{margin:20px;margin-left:0;padding:5px 20px;font-size:28.8px;font-size:1.8rem;background-color:hsla(0,0%,50%,.05);color:#ebebeb;border-top-right-radius:5px;border-bottom-right-radius:5px;border-left:8px solid #979797}sr-rd-content,sr-rd-content *,sr-rd-content div,sr-rd-content p{line-height:1.7;color:#ebebeb}sr-rd-content a,sr-rd-content a:link{color:#8ac9ff;text-decoration:underline}sr-rd-content a:active,sr-rd-content a:focus,sr-rd-content a:hover{background-color:#2a6496;color:#fff;text-decoration:none}sr-rd-content pre{color:#e9eded;background-color:#263238}sr-rd-content li code,sr-rd-content p code{color:#caca16;background-color:transparent}sr-rd-mult{background-color:hsla(0,0%,50%,.1)}panel{background-color:#222!important}panel panel-tab span{color:#fff}panel sr-opt-gp sr-opt-label{color:rgba(108,255,240,.8);font-weight:400}panel text-field-float{color:rgba(108,255,240,.8)!important;font-weight:400!important}panel list-field{background-color:transparent!important}panel list-field:hover list-field-name{color:#fff!important;font-weight:700}panel input,panel list-field-name{color:hsla(35,10%,76%,.87)!important}panel list-view{background-color:#222!important}panel text-field input{color:rgba(108,255,240,.8)!important}panel sr-opt-gp action-label,panel switch content *{color:#fff!important}panel-tabs{border-bottom-color:#393d40!important}sr-annote{color:#333}sr-annote-sidebar *{color:hsla(36,10%,90%,.9)!important}sr-annote-sidebar-card[type=unread]{background-color:#19896e!important}sr-annote-sidebar-card:hover{box-shadow:0 10px 20px 0 rgba(60,73,82,.6)!important}sr-annote-sidebar-note,sr-annote-sidebar-options,sr-annote-sidebar-toolbars{background-color:#2a2a2a!important}sr-rd-theme-dark-comp{display:none;opacity:0}dialog-content,dialog-footer{color:#dbdbfd!important;background-color:#222!important}dialog-content *{color:#dbdbfd!important}dialog-content svg path{fill:#dbdbfd!important}dialog-content button-text{color:#dbdbfd!important}dialog-content text-field-state{border-top:none #dbdbfd!important;border-left:none #dbdbfd!important;border-right:none #dbdbfd!important;border-bottom:2px solid #dbdbfd!important}dialog-footer button-text{color:#dbdbfd!important}auto-complete list-view,dropdown list-view{color:#dbdbfd!important;background-color:#222!important}auto-complete list-field,dropdown list-field{-webkit-transition:all .45s cubic-bezier(.23,1,.32,1) 0ms;transition:all .45s cubic-bezier(.23,1,.32,1) 0ms}auto-complete list-field[active=true],dropdown list-field[active=true]{color:#dbdbfd!important;background-color:#1b1b1b!important}auto-complete tag{background-color:#1b1b1b!important}sr-rd-theme-dark-comp{display:none}</style><style type=\"text/css\">sr-rd-theme-mail{display:none}sr-rd-content h1,sr-rd-content h2,sr-rd-content h3,sr-rd-content h4,sr-rd-content h5,sr-rd-content h6{position:relative;margin-top:1em;margin-bottom:1pc;font-weight:700;line-height:1.4;text-align:left;color:#363636}sr-rd-content h1{padding-bottom:.3em;font-size:36p;line-height:1.2}sr-rd-content h2{padding-bottom:.3em;font-size:28p;line-height:1.225}sr-rd-content h3{font-size:24p;line-height:1.43}sr-rd-content h4{font-size:2p}sr-rd-content h5{font-size:16px}sr-rd-content h6{font-size:16px;color:#777}sr-rd-content ol,sr-rd-content ul{list-style-type:disc;padding:0;padding-left:2em}sr-rd-content ol ol,sr-rd-content ul ol{list-style-type:lower-roman}sr-rd-content ol ol ol,sr-rd-content ol ul ol,sr-rd-content ul ol ol,sr-rd-content ul ul ol{list-style-type:lower-alpha}sr-rd-content table{width:100%;overflow:auto;word-break:normal;word-break:keep-all}sr-rd-content table th{font-weight:700}sr-rd-content table td,sr-rd-content table th{padding:6px 13px;border:1px solid #ddd}sr-rd-content table tr{background-color:#fff;border-top:1px solid #ccc}sr-rd-content table tr:nth-child(2n){background-color:#f8f8f8}sr-rd-content sr-blockquote{border-left:4px solid #ddd}.simpread-theme-root{background-color:#fff;color:#333}.sr-header{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-pack:left;-ms-flex-pack:left;justify-content:left;width:100%;margin:10px 0;height:41px;border-bottom:1px solid #e0e0e0;padding-bottom:10px}.sr-header,.sr-header a{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;-webkit-box-align:center;-ms-flex-align:center;align-items:center;color:#195bf7}.sr-header a{display:-webkit-box;display:-ms-flexbox;display:flex;text-decoration:none}.sr-header .sr-name{height:41px;line-height:41px;font-size:20px;font-weight:700;text-decoration:none}.sr-header .sr-logo{display:block;width:41px;height:41px;background-repeat:no-repeat;background-position:50%;background-image:url(https://simpread-1254315611.file.myqcloud.com/favicon/favicon-32x32.png);margin-right:5px}.sr-header .sr-slogan{height:41px;line-height:44px;font-weight:700;font-size:15px}.sr-rd-footer{font-size:14px;text-align:center;color:#363636}.sr-rd-footer-group{display:-webkit-box;display:-ms-flexbox;display:flex;height:20px}.sr-rd-footer-line{width:100%;border-top:1px solid #e0e0e0}.sr-rd-footer-text{min-width:150px;line-height:0;text-align:center}.sr-rd-footer-copywrite{margin:10px 0 0;color:#363636}.sr-rd-footer-copywrite abbr{-webkit-font-feature-settings:normal;font-feature-settings:normal;font-variant:normal;text-decoration:none}.sr-rd-footer-copywrite .second{margin:10px 0}.sr-rd-footer-copywrite .third a:hover{border:none!important}.sr-rd-footer-copywrite .third a:first-child{margin-right:50px}.sr-rd-footer-copywrite .sr-icon{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;width:33px;height:33px;opacity:.8;-webkit-transition:opacity .5s ease;transition:opacity .5s ease;cursor:pointer}.sr-rd-footer-copywrite .sr-icon:hover{opacity:1}.sr-rd-footer-copywrite a,.sr-rd-footer-copywrite a:link,.sr-rd-footer-copywrite a:visited{margin:0;padding:0;color:inherit;background-color:transparent;font-size:inherit!important;line-height:normal;text-decoration:none;vertical-align:baseline;vertical-align:initial;border:none!important;box-sizing:border-box}.sr-rd-footer-copywrite a:focus,.sr-rd-footer-copywrite a:hover,.sr-rd-footer a:active{color:inherit;text-decoration:none;border-bottom:1px dotted!important}.sr-rd-content-desc{margin:0;padding:0 0 0 1em;color:#363636;line-height:2;font-size:18px;border-left:4px solid hsla(0,0%,67%,.5)}sr-rd-content{font-size:16px;line-height:1.6}sr-rd-content h1,sr-rd-content h1 *,sr-rd-content h2,sr-rd-content h2 *,sr-rd-content h3,sr-rd-content h3 *,sr-rd-content h4,sr-rd-content h4 *,sr-rd-content h5,sr-rd-content h5 *,sr-rd-content h6,sr-rd-content h6 *{word-break:break-all}sr-rd-content div,sr-rd-content p{display:block;float:inherit;line-height:1.6;font-size:16px}sr-rd-content,sr-rd-content *,sr-rd-content div,sr-rd-content p{color:#363636;font-weight:400;line-height:1.8}sr-rd-content strong,sr-rd-content strong *{-webkit-animation:none 0s ease 0s 1 normal none running;animation:none 0s ease 0s 1 normal none running;-webkit-backface-visibility:visible;backface-visibility:visible;background:transparent none repeat 0 0/auto auto padding-box border-box scroll;border:medium none currentColor;border-collapse:separate;-o-border-image:none;border-image:none;border-radius:0;border-spacing:0;bottom:auto;box-shadow:none;box-sizing:content-box;caption-side:top;clear:none;clip:auto;color:#000;-webkit-columns:auto;-moz-columns:auto;columns:auto;-webkit-column-count:auto;-moz-column-count:auto;column-count:auto;-webkit-column-fill:balance;-moz-column-fill:balance;column-fill:balance;-webkit-column-gap:normal;-moz-column-gap:normal;column-gap:normal;-webkit-column-rule:medium none currentColor;-moz-column-rule:medium none currentColor;column-rule:medium none currentColor;-webkit-column-span:1;-moz-column-span:1;column-span:1;-webkit-column-width:auto;-moz-column-width:auto;column-width:auto;content:normal;counter-increment:none;counter-reset:none;cursor:auto;direction:ltr;display:inline;empty-cells:show;float:none;font-family:serif;font-size:medium;font-style:normal;font-variant:normal;font-weight:400;font-stretch:normal;line-height:normal;height:auto;-webkit-hyphens:none;-ms-hyphens:none;hyphens:none;left:auto;letter-spacing:normal;list-style:disc outside none;margin:0;max-height:none;max-width:none;min-height:0;min-width:0;opacity:1;orphans:2;outline:medium none invert;overflow:visible;overflow-x:visible;overflow-y:visible;padding:0;page-break-after:auto;page-break-before:auto;page-break-inside:auto;-webkit-perspective:none;perspective:none;-webkit-perspective-origin:50% 50%;perspective-origin:50% 50%;position:static;right:auto;-moz-tab-size:8;-o-tab-size:8;tab-size:8;table-layout:auto;text-align:left;text-align-last:auto;text-decoration:none;text-indent:0;text-shadow:none;text-transform:none;top:auto;-webkit-transform:none;transform:none;-webkit-transform-origin:50% 50% 0;transform-origin:50% 50% 0;-webkit-transform-style:flat;transform-style:flat;-webkit-transition:none 0s ease 0s;transition:none 0s ease 0s;unicode-bidi:normal;vertical-align:baseline;visibility:visible;white-space:normal;widows:2;width:auto;word-spacing:normal;z-index:auto;all:initial}sr-rd-content a,sr-rd-content a:link{color:#4183c4;text-decoration:none}sr-rd-content a:active,sr-rd-content a:focus,sr-rd-content a:hover{color:#4183c4;text-decoration:underline}sr-rd-content figure{margin:0;padding:0}sr-rd-content img{display:inline-block;padding:0;height:auto;line-height:100%;max-width:50%;text-decoration:none;vertical-align:text-bottom;border-radius:10px;outline:none}sr-rd-content pre{background-color:#f7f7f7;border-radius:3px}sr-rd-content pre *{font-size:1.1px}sr-rd-content li code,sr-rd-content p code{background-color:rgba(0,0,0,.04);border-radius:3px}.simpread-multi-root{background:#f8f9fa}.sr-rd-mult{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;margin:0 0 16px;padding:16px 0 24px;width:100%;background-color:#fff;border-bottom:1px solid #e0e0e0}.sr-rd-mult .sr-rd-mult-content{padding:0 16px;overflow:auto}.sr-rd-mult .sr-rd-mult-avatar{margin:0 15px}.sr-rd-mult .sr-rd-mult-avatar span{display:-webkit-box;-webkit-line-clamp:1;-webkit-box-orient:vertical;max-width:75px;overflow:hidden;text-overflow:ellipsis;text-align:left;font-size:16px;font-size:1rem}.sr-rd-mult .sr-rd-mult-avatar img{margin-bottom:0;max-width:50px;max-height:50px;width:50px;height:50px;border-radius:50%}.sr-rd-mult .sr-rd-mult-avatar .sr-rd-content-center{margin:0}sr-rd-content.embed *{font-size:medium}sr-rd-content.embed img{max-width:100%}sr-rd-content.embed a,sr-rd-content.embed a:hover{color:inherit;font-size:medium}sr-rd-content.embed a:hover{background-color:inherit}sr-rd-content.embed .MathJax_Processed,sr-rd-content.embed math{display:none}sr-rd-content.embed pre{color:#000;color:initial;background-color:transparent}sr-rd-content.embed pre,sr-rd-content.embed pre *{font-size:13px!important}</style><style type=\"text/css\">@media (pointer:coarse){sr-read{margin:20px 5%!important;min-width:0!important;max-width:90%!important}sr-rd-title{margin-top:0;font-size:2.7rem}sr-rd-content sr-blockquote,sr-rd-desc{margin:10 0!important;padding:0 0 0 10px!important;width:90%;font-size:1.8rem;font-style:normal;line-height:1.7;text-align:justify}sr-rd-content{font-size:1.75rem;font-weight:300}sr-rd-content figure{margin:0;padding:0;text-align:center}sr-rd-content a,sr-rd-content a:link,sr-rd-content li code,sr-rd-content p code{font-size:inherit}sr-rd-footer{margin-top:20px}sr-blockquote,sr-blockquote *{margin:5px!important;padding:5px!important}sr-rd-content h1,sr-rd-content h2,sr-rd-content h3,sr-rd-content h4,sr-rd-content h5,sr-rd-content h6,sr-rd-title{font-family:PingFang SC,Verdana,Helvetica Neue,Microsoft Yahei,Hiragino Sans GB,Microsoft Sans Serif,WenQuanYi Micro Hei,sans-serif;color:#000;font-weight:100;line-height:1.35}sr-rd-content-h1,sr-rd-content-h2,sr-rd-content-h3,sr-rd-content-h4,sr-rd-content-h5,sr-rd-content-h6,sr-rd-content h1,sr-rd-content h2,sr-rd-content h3,sr-rd-content h4,sr-rd-content h5,sr-rd-content h6{margin-top:1.2em;margin-bottom:.6em;line-height:1.35}sr-rd-content-h1,sr-rd-content h1{font-size:1.8em}sr-rd-content-h2,sr-rd-content h2{font-size:1.6em}sr-rd-content-h3,sr-rd-content h3{font-size:1.4em}sr-rd-content-h4,sr-rd-content-h5,sr-rd-content-h6,sr-rd-content h4,sr-rd-content h5,sr-rd-content h6{font-size:1.2em}sr-rd-content-ul,sr-rd-content ul{margin-left:1.3em!important;list-style:disc}sr-rd-content-ol,sr-rd-content ol{list-style:decimal;margin-left:1.9em!important}sr-rd-content-ol ol,sr-rd-content-ol ul,sr-rd-content-ul ol,sr-rd-content-ul ul,sr-rd-content li ol,sr-rd-content li ul{margin-bottom:.8em;margin-left:2em!important}sr-rd-content img{margin:0;padding:0;border:0;max-width:100%!important;height:auto;box-shadow:0 20px 20px -10px rgba(0,0,0,.1)}sr-rd-mult{min-width:0;background-color:#fff;box-shadow:0 1px 6px rgba(32,33,36,.28);border-radius:8px}sr-rd-mult sr-rd-mult-avatar div{margin:0}sr-rd-mult sr-rd-mult-avatar .sr-rd-content-center-small{margin:7px 0!important}sr-rd-mult sr-rd-mult-avatar span{display:block}sr-rd-mult sr-rd-mult-content{padding-left:0}@media only screen and (max-device-width:1024px){.simpread-theme-root,html.simpread-theme-root{font-size:80%!important}sr-rd-mult sr-rd-mult-avatar img{width:50px;height:50px;min-width:50px;min-height:50px}toc-bg toc{width:10px!important}toc-bg:hover toc{width:auto!important}}@media only screen and (max-device-width:414px){.simpread-theme-root,html.simpread-theme-root{font-size:70%!important}sr-rd-mult sr-rd-mult-avatar img{width:30px;height:30px;min-width:30px;min-height:30px}}@media only screen and (max-device-width:320px){.simpread-theme-root,html.simpread-theme-root{font-size:90%!important}sr-rd-content p{margin-bottom:.5em}}}</style><style type=\"text/css\">button[aria-label][data-balloon-pos]{overflow:visible}[aria-label][data-balloon-pos]{position:relative;cursor:pointer}[aria-label][data-balloon-pos]:after{text-indent:0;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Open Sans,Helvetica Neue,sans-serif;font-weight:400;font-style:normal;text-shadow:none;font-size:12px;background:hsla(0,0%,6%,.95);border-radius:2px;color:#fff;content:attr(aria-label);padding:.5em 1em;white-space:nowrap}[aria-label][data-balloon-pos]:after,[aria-label][data-balloon-pos]:before{opacity:0;pointer-events:none;-webkit-transition:all .18s ease-out .18s;transition:all .18s ease-out .18s;position:absolute;z-index:10}[aria-label][data-balloon-pos]:before{width:0;height:0;border:5px solid transparent;border-top-color:hsla(0,0%,6%,.95);content:\"\"}[aria-label][data-balloon-pos]:hover:after,[aria-label][data-balloon-pos]:hover:before,[aria-label][data-balloon-pos]:not([data-balloon-nofocus]):focus:after,[aria-label][data-balloon-pos]:not([data-balloon-nofocus]):focus:before,[aria-label][data-balloon-pos][data-balloon-visible]:after,[aria-label][data-balloon-pos][data-balloon-visible]:before{opacity:1;pointer-events:none}[aria-label][data-balloon-pos].font-awesome:after{font-family:FontAwesome,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Open Sans,Helvetica Neue,sans-serif}[aria-label][data-balloon-pos][data-balloon-break]:after{white-space:pre}[aria-label][data-balloon-pos][data-balloon-break][data-balloon-length]:after{white-space:pre-line;word-break:break-word}[aria-label][data-balloon-pos][data-balloon-blunt]:after,[aria-label][data-balloon-pos][data-balloon-blunt]:before{-webkit-transition:none;transition:none}[aria-label][data-balloon-pos][data-balloon-pos=up]:after{margin-bottom:10px}[aria-label][data-balloon-pos][data-balloon-pos=up]:after,[aria-label][data-balloon-pos][data-balloon-pos=up]:before{bottom:100%;left:50%;-webkit-transform:translate(-50%,4px);transform:translate(-50%,4px);-webkit-transform-origin:top;transform-origin:top}[aria-label][data-balloon-pos][data-balloon-pos=up]:hover:after,[aria-label][data-balloon-pos][data-balloon-pos=up]:hover:before,[aria-label][data-balloon-pos][data-balloon-pos=up][data-balloon-visible]:after,[aria-label][data-balloon-pos][data-balloon-pos=up][data-balloon-visible]:before{-webkit-transform:translate(-50%);transform:translate(-50%)}[aria-label][data-balloon-pos][data-balloon-pos=up-left]:after{bottom:100%;left:0;margin-bottom:10px;-webkit-transform:translateY(4px);transform:translateY(4px);-webkit-transform-origin:top;transform-origin:top}[aria-label][data-balloon-pos][data-balloon-pos=up-left]:before{bottom:100%;left:5px;-webkit-transform:translateY(4px);transform:translateY(4px);-webkit-transform-origin:top;transform-origin:top}[aria-label][data-balloon-pos][data-balloon-pos=up-left]:hover:after,[aria-label][data-balloon-pos][data-balloon-pos=up-left]:hover:before,[aria-label][data-balloon-pos][data-balloon-pos=up-left][data-balloon-visible]:after,[aria-label][data-balloon-pos][data-balloon-pos=up-left][data-balloon-visible]:before{-webkit-transform:translate(0);transform:translate(0)}[aria-label][data-balloon-pos][data-balloon-pos=up-right]:after{bottom:100%;right:0;margin-bottom:10px;-webkit-transform:translateY(4px);transform:translateY(4px);-webkit-transform-origin:top;transform-origin:top}[aria-label][data-balloon-pos][data-balloon-pos=up-right]:before{bottom:100%;right:5px;-webkit-transform:translateY(4px);transform:translateY(4px);-webkit-transform-origin:top;transform-origin:top}[aria-label][data-balloon-pos][data-balloon-pos=up-right]:hover:after,[aria-label][data-balloon-pos][data-balloon-pos=up-right]:hover:before,[aria-label][data-balloon-pos][data-balloon-pos=up-right][data-balloon-visible]:after,[aria-label][data-balloon-pos][data-balloon-pos=up-right][data-balloon-visible]:before{-webkit-transform:translate(0);transform:translate(0)}[aria-label][data-balloon-pos][data-balloon-pos=down]:after{left:50%;margin-top:10px;top:100%;-webkit-transform:translate(-50%,-4px);transform:translate(-50%,-4px)}[aria-label][data-balloon-pos][data-balloon-pos=down]:before{width:0;height:0;border:5px solid transparent;border-bottom-color:hsla(0,0%,6%,.95);left:50%;top:100%;-webkit-transform:translate(-50%,-4px);transform:translate(-50%,-4px)}[aria-label][data-balloon-pos][data-balloon-pos=down]:hover:after,[aria-label][data-balloon-pos][data-balloon-pos=down]:hover:before,[aria-label][data-balloon-pos][data-balloon-pos=down][data-balloon-visible]:after,[aria-label][data-balloon-pos][data-balloon-pos=down][data-balloon-visible]:before{-webkit-transform:translate(-50%);transform:translate(-50%)}[aria-label][data-balloon-pos][data-balloon-pos=down-left]:after{left:0;margin-top:10px;top:100%;-webkit-transform:translateY(-4px);transform:translateY(-4px)}[aria-label][data-balloon-pos][data-balloon-pos=down-left]:before{width:0;height:0;border:5px solid transparent;border-bottom-color:hsla(0,0%,6%,.95);left:5px;top:100%;-webkit-transform:translateY(-4px);transform:translateY(-4px)}[aria-label][data-balloon-pos][data-balloon-pos=down-left]:hover:after,[aria-label][data-balloon-pos][data-balloon-pos=down-left]:hover:before,[aria-label][data-balloon-pos][data-balloon-pos=down-left][data-balloon-visible]:after,[aria-label][data-balloon-pos][data-balloon-pos=down-left][data-balloon-visible]:before{-webkit-transform:translate(0);transform:translate(0)}[aria-label][data-balloon-pos][data-balloon-pos=down-right]:after{right:0;margin-top:10px;top:100%;-webkit-transform:translateY(-4px);transform:translateY(-4px)}[aria-label][data-balloon-pos][data-balloon-pos=down-right]:before{width:0;height:0;border:5px solid transparent;border-bottom-color:hsla(0,0%,6%,.95);right:5px;top:100%;-webkit-transform:translateY(-4px);transform:translateY(-4px)}[aria-label][data-balloon-pos][data-balloon-pos=down-right]:hover:after,[aria-label][data-balloon-pos][data-balloon-pos=down-right]:hover:before,[aria-label][data-balloon-pos][data-balloon-pos=down-right][data-balloon-visible]:after,[aria-label][data-balloon-pos][data-balloon-pos=down-right][data-balloon-visible]:before{-webkit-transform:translate(0);transform:translate(0)}[aria-label][data-balloon-pos][data-balloon-pos=left]:after{margin-right:10px;right:100%;top:50%;-webkit-transform:translate(4px,-50%);transform:translate(4px,-50%)}[aria-label][data-balloon-pos][data-balloon-pos=left]:before{width:0;height:0;border:5px solid transparent;border-left-color:hsla(0,0%,6%,.95);right:100%;top:50%;-webkit-transform:translate(4px,-50%);transform:translate(4px,-50%)}[aria-label][data-balloon-pos][data-balloon-pos=left]:hover:after,[aria-label][data-balloon-pos][data-balloon-pos=left]:hover:before,[aria-label][data-balloon-pos][data-balloon-pos=left][data-balloon-visible]:after,[aria-label][data-balloon-pos][data-balloon-pos=left][data-balloon-visible]:before{-webkit-transform:translateY(-50%);transform:translateY(-50%)}[aria-label][data-balloon-pos][data-balloon-pos=right]:after{left:100%;margin-left:10px;top:50%;-webkit-transform:translate(-4px,-50%);transform:translate(-4px,-50%)}[aria-label][data-balloon-pos][data-balloon-pos=right]:before{width:0;height:0;border:5px solid transparent;border-right-color:hsla(0,0%,6%,.95);left:100%;top:50%;-webkit-transform:translate(-4px,-50%);transform:translate(-4px,-50%)}[aria-label][data-balloon-pos][data-balloon-pos=right]:hover:after,[aria-label][data-balloon-pos][data-balloon-pos=right]:hover:before,[aria-label][data-balloon-pos][data-balloon-pos=right][data-balloon-visible]:after,[aria-label][data-balloon-pos][data-balloon-pos=right][data-balloon-visible]:before{-webkit-transform:translateY(-50%);transform:translateY(-50%)}[aria-label][data-balloon-pos][data-balloon-length=small]:after{white-space:normal;width:80px}[aria-label][data-balloon-pos][data-balloon-length=medium]:after{white-space:normal;width:150px}[aria-label][data-balloon-pos][data-balloon-length=large]:after{white-space:normal;width:260px}[aria-label][data-balloon-pos][data-balloon-length=xlarge]:after{white-space:normal;width:380px}@media screen and (max-width:768px){[aria-label][data-balloon-pos][data-balloon-length=xlarge]:after{white-space:normal;width:90vw}}[aria-label][data-balloon-pos]:before{display:none}[aria-label][data-balloon-pos]:after{box-shadow:0 0 10px rgba(0,0,0,.3);border-radius:5px;font-weight:700;font-size:10px}[aria-label][data-balloon-pos][data-balloon-pos=up]:after{line-height:21px}[aria-label][data-balloon-pos][data-balloon-order=downleft]:after{left:120%}[aria-label][data-balloon-pos][data-balloon-order=downright]:after{right:-22px}[aria-label][data-balloon-pos][data-balloon-order=upright]:after{left:10%}</style><style type=\"text/css\">/*!\n * Waves v0.7.5\n * http://fian.my.id/Waves\n * \n * Copyright 2014-2016 Alfiana E. Sibuea and other contributors\n * Released under the MIT license\n * https://github.com/fians/Waves/blob/master/LICENSE\n */.md-waves-effect{position:relative;cursor:pointer;display:inline-block;overflow:hidden;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-tap-highlight-color:transparent}.md-waves-effect .md-waves-ripple{position:absolute;border-radius:50%;width:100px;height:100px;margin-top:-50px;margin-left:-50px;opacity:0;background:rgba(0,0,0,.2);background:-webkit-radial-gradient(rgba(0,0,0,.2) 0,rgba(0,0,0,.3) 40%,rgba(0,0,0,.4) 50%,rgba(0,0,0,.5) 60%,hsla(0,0%,100%,0) 70%);background:radial-gradient(rgba(0,0,0,.2) 0,rgba(0,0,0,.3) 40%,rgba(0,0,0,.4) 50%,rgba(0,0,0,.5) 60%,hsla(0,0%,100%,0) 70%);-webkit-transition:all .5s ease-out;transition:all .5s ease-out;-webkit-transition-property:-webkit-transform,opacity;-webkit-transition-property:opacity,-webkit-transform;transition-property:opacity,-webkit-transform;transition-property:transform,opacity;transition-property:transform,opacity,-webkit-transform;-webkit-transform:scale(0) translate(0);transform:scale(0) translate(0);pointer-events:none}.md-waves-effect.md-waves-light .md-waves-ripple{background:hsla(0,0%,100%,.4);background:-webkit-radial-gradient(hsla(0,0%,100%,.2) 0,hsla(0,0%,100%,.3) 40%,hsla(0,0%,100%,.4) 50%,hsla(0,0%,100%,.5) 60%,hsla(0,0%,100%,0) 70%);background:radial-gradient(hsla(0,0%,100%,.2) 0,hsla(0,0%,100%,.3) 40%,hsla(0,0%,100%,.4) 50%,hsla(0,0%,100%,.5) 60%,hsla(0,0%,100%,0) 70%)}.md-waves-effect.md-waves-classic .md-waves-ripple{background:rgba(0,0,0,.2)}.md-waves-effect.md-waves-classic.md-waves-light .md-waves-ripple{background:hsla(0,0%,100%,.4)}.md-waves-notransition{-webkit-transition:none!important;transition:none!important}.md-waves-button,.md-waves-circle{-webkit-transform:translateZ(0);transform:translateZ(0);-webkit-mask-image:-webkit-radial-gradient(circle,#fff 100%,#000 0)}.md-waves-button,.md-waves-button-input,.md-waves-button:hover,.md-waves-button:visited{white-space:nowrap;vertical-align:middle;cursor:pointer;border:none;outline:none;color:inherit;background-color:transparent;font-size:1em;line-height:1em;text-align:center;text-decoration:none;z-index:1}.md-waves-button{padding:.85em 1.1em;border-radius:.2em}.md-waves-button-input{margin:0;padding:.85em 1.1em}.md-waves-input-wrapper{border-radius:.2em;vertical-align:bottom}.md-waves-input-wrapper.md-waves-button{padding:0}.md-waves-input-wrapper .md-waves-button-input{position:relative;top:0;left:0;z-index:1}.md-waves-circle{text-align:center;width:2.5em;height:2.5em;line-height:2.5em;border-radius:50%}.md-waves-float{-webkit-mask-image:none;box-shadow:0 1px 1.5px 1px rgba(0,0,0,.12);-webkit-transition:all .3s;transition:all .3s}.md-waves-float:active{box-shadow:0 8px 20px 1px rgba(0,0,0,.3)}.md-waves-block{display:block}</style><style type=\"text/css\">.simpread-font{font:300 16px/1.8 -apple-system,PingFang SC,Microsoft Yahei,Lantinghei SC,Hiragino Sans GB,Microsoft Sans Serif,WenQuanYi Micro Hei,sans-serif;color:#333;text-rendering:optimizelegibility;-webkit-text-size-adjust:100%;-webkit-font-smoothing:antialiased}.simpread-hidden{display:none}.simpread-read-root{display:-webkit-flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;position:relative;margin:0;top:-1000px;left:0;width:100%;z-index:2147483646;overflow-x:hidden;opacity:0;-webkit-transition:all 1s cubic-bezier(.23,1,.32,1) .1s;transition:all 1s cubic-bezier(.23,1,.32,1) .1s}.simpread-read-root-show{top:0}.simpread-read-root-hide{top:1000px}sr-read{display:-webkit-flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-flow:column nowrap;flex-flow:column;margin:20px 20%;min-width:400px;min-height:400px;text-align:center}read-process{position:fixed;top:0;left:0;height:3px;width:100%;background-color:#64b5f6;-webkit-transition:width 2s;transition:width 2s;z-index:20000}sr-rd-content-error{display:block;position:relative;margin:0;margin-bottom:30px;padding:25px;background-color:rgba(0,0,0,.05)}sr-rd-footer{-webkit-box-orient:vertical;-ms-flex-direction:column;flex-direction:column;font-size:14px}sr-rd-footer,sr-rd-footer-group{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-direction:normal}sr-rd-footer-group{-webkit-box-orient:horizontal;-ms-flex-direction:row;flex-direction:row;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center}sr-rd-footer-line{width:100%;border-top:1px solid #e0e0e0}sr-rd-footer-text{min-width:150px}sr-rd-footer-copywrite{margin:10px 0 0;color:inherit}sr-rd-footer-copywrite abbr{-webkit-font-feature-settings:normal;font-feature-settings:normal;font-variant:normal;text-decoration:none}sr-rd-footer-copywrite .second{margin:10px 0}sr-rd-footer-copywrite .third a:hover{border:none!important}sr-rd-footer-copywrite .third a:first-child{margin-right:50px}sr-rd-footer-copywrite .sr-icon{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;width:33px;height:33px;opacity:.8;-webkit-transition:opacity .5s ease;transition:opacity .5s ease;cursor:pointer}sr-rd-footer-copywrite .sr-icon:hover{opacity:1}sr-rd-footer-copywrite a,sr-rd-footer-copywrite a:link,sr-rd-footer-copywrite a:visited{margin:0;padding:0;color:inherit;background-color:transparent;font-size:inherit!important;line-height:normal;text-decoration:none;vertical-align:baseline;vertical-align:initial;border:none!important;box-sizing:border-box}sr-rd-footer-copywrite a:focus,sr-rd-footer-copywrite a:hover,sr-rd-footer a:active{color:inherit;text-decoration:none;border-bottom:1px dotted!important}.simpread-blocks{text-decoration:none!important}.simpread-blocks *{margin:0}.simpread-blocks a{padding:0;text-decoration:none!important}.simpread-blocks img{margin:0;padding:0;border:0;background:transparent;box-shadow:none}.simpread-focus-root{display:block;position:fixed;top:0;left:0;right:0;bottom:0;background-color:hsla(0,0%,92%,.9);z-index:2147483645;opacity:0;-webkit-transition:opacity 1s cubic-bezier(.23,1,.32,1) 0ms;transition:opacity 1s cubic-bezier(.23,1,.32,1) 0ms}.simpread-focus-highlight{position:relative;box-shadow:0 0 0 20px #fff;background-color:#fff;overflow:visible;z-index:2147483646}.sr-controlbar-bg sr-rd-crlbar,.sr-controlbar-bg sr-rd-crlbar fab{z-index:2147483647}sr-rd-crlbar.controlbar{position:fixed;right:0;bottom:0;width:100px;height:100%;opacity:0;-webkit-transition:opacity .5s ease;transition:opacity .5s ease}sr-rd-crlbar.controlbar:hover{opacity:1}sr-rd-crlbar fap *{box-sizing:border-box}@media (max-height:620px){fab{zoom:.8}}@media (max-height:783px){dialog-gp dialog-content{max-height:580px}dialog-gp dialog-footer{border-top:1px solid #e0e0e0}}.simpread-highlight-selector{outline:3px dashed #1976d2!important;cursor:pointer!important}.simpread-highlight-controlbar,.simpread-highlight-selector{background-color:#fafafa!important;opacity:.8!important;-webkit-transition:opacity .5s ease!important;transition:opacity .5s ease!important}.simpread-highlight-controlbar{position:relative!important;border:3px dashed #1976d2!important}simpread-highlight,sr-snapshot-ctlbar{position:fixed;top:0;left:0;right:0;padding:15px;height:50px;background-color:rgba(50,50,50,.9);box-shadow:0 2px 5px rgba(0,0,0,.26);box-sizing:border-box;z-index:2147483640}simpread-highlight,sr-highlight-ctl,sr-snapshot-ctlbar{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center}sr-highlight-ctl{margin:0 5px;width:50px;height:20px;color:#fff;background-color:#1976d2;border-radius:4px;box-shadow:0 3px 1px -2px rgba(0,0,0,.2),0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12);cursor:pointer}toc-bg{position:fixed;left:0;top:0;width:50px;height:200px;font-size:medium}toc-bg:hover{z-index:3}.toc-bg-hidden{opacity:0;-webkit-transition:opacity .5s ease;transition:opacity .5s ease}.toc-bg-hidden:hover{opacity:1;z-index:3}.toc-bg-hidden:hover toc{width:180px}toc *{all:unset}toc{position:fixed;left:0;top:100px;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;-webkit-box-align:start;-ms-flex-align:start;align-items:flex-start;padding:10px;width:0;max-width:200px;max-height:500px;overflow-x:hidden;overflow-y:hidden;cursor:pointer;border:1px solid hsla(0,0%,62%,.22);-webkit-transition:width .5s;transition:width .5s}toc:hover{overflow-y:auto}toc.mini:hover{width:200px!important}toc::-webkit-scrollbar{width:3px}toc::-webkit-scrollbar-thumb{border-radius:10px;background-color:hsla(36,2%,54%,.5)}toc outline{position:relative;display:-webkit-box;-webkit-line-clamp:1;-webkit-box-orient:vertical;overflow:hidden;text-overflow:ellipsis;padding:2px 0;min-height:21px;line-height:21px;text-align:left}toc outline a,toc outline a:active,toc outline a:focus,toc outline a:visited{display:block;width:100%;color:inherit;font-size:11px;text-decoration:none!important;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}toc outline a:hover{font-weight:700!important}toc outline a.toc-outline-theme-dark,toc outline a.toc-outline-theme-night{color:#fff!important}.toc-level-h1{padding-left:5px}.toc-level-h2{padding-left:15px}.toc-level-h3{padding-left:25px}.toc-level-h4{padding-left:35px}.toc-outline-active{border-left:2px solid #f44336}toc outline active{position:absolute;left:0;top:0;bottom:0;padding:0 0 0 3px;border-left:2px solid #e8e8e8}sr-kbd{background:-webkit-gradient(linear,0 0,0 100%,from(#fff785),to(#ffc542));border:1px solid #e3be23;-o-border-image:none;border-image:none;-o-border-image:initial;border-image:initial;position:absolute;left:0;padding:1px 3px 0;font-size:11px!important;font-weight:700;box-shadow:0 3px 7px 0 rgba(0,0,0,.3);overflow:hidden;border-radius:3px}.sr-kbd-a{position:relative}kbd-mapping{position:fixed;left:5px;bottom:5px;-ms-flex-flow:row;flex-flow:row;width:250px;height:500px;background-color:#fff;border:1px solid hsla(0,0%,62%,.22);box-shadow:0 2px 5px rgba(0,0,0,.26);border-radius:3px}kbd-mapping,kbd-maps{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}kbd-maps{margin:40px 0 20px;width:100%;overflow-x:auto}kbd-maps::-webkit-scrollbar-thumb{background-clip:padding-box;border-radius:10px;border:2px solid transparent;background-color:rgba(85,85,85,.55)}kbd-maps::-webkit-scrollbar{width:10px;-webkit-transition:width .7s cubic-bezier(.4,0,.2,1);transition:width .7s cubic-bezier(.4,0,.2,1)}kbd-mapping kbd-map-title{position:absolute;margin:5px 0;width:100%;font-size:14px;font-weight:700}kbd-maps-group{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;-webkit-box-align:start;-ms-flex-align:start;align-items:flex-start}kbd-maps-title{margin:5px 0;padding-left:53px;font-size:12px;font-weight:700}kbd-map kbd{display:inline-block;padding:3px 5px;font-size:11px;line-height:10px;color:#444d56;vertical-align:middle;background-color:#fafbfc;border:1px solid #c6cbd1;border-bottom-color:#959da5;border-radius:3px;box-shadow:inset 0 -1px 0 #959da5}kbd-map kbd-name{display:inline-block;text-align:right;width:50px}kbd-map kbd-desc{padding-left:3px}sharecard-bg{position:fixed;top:0;left:0;width:100%;height:100%;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;background-color:rgba(0,0,0,.4);z-index:2147483647}sharecard{max-width:450px;background-color:#64b5f6}sharecard,sharecard-head{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}sharecard-head{margin:25px;color:#fff;border-radius:10px;box-shadow:0 2px 6px 0 rgba(0,0,0,.2),0 25px 50px 0 rgba(0,0,0,.15)}sharecard-card{-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}sharecard-card,sharecard-top{display:-webkit-box;display:-ms-flexbox;display:flex}sharecard-top{-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;padding-right:5px;height:65px;background-color:#fff;color:#878787;font-size:25px;font-weight:500;border-top-left-radius:10px;border-top-right-radius:10px}sharecard-top span.logos{display:block;width:48px;height:48px;margin:5px;background-repeat:no-repeat;background-position:50%;background-image:url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAMAAABg3Am1AAABU1BMVEUAAAAnNJMnNZI3Q5onNJInNJMnNJInNJMnNJI8SJ0tOZY/S55EUKAoNJI6RpwoNJNIU6InNJInNJImNJI7SJwmNJJ2fLUiMJFKVaNCTJ9faK1HUaJOWKVSXaUnNJNYY6pye7cmM5JXYKhwebMjMI8mL4719fW9vb0oNZP/UlLz8/QqN5TAwMAnNJPv7+/Pz8/q6+/p6enNzc3Kysry8vMsOJXc3env7/LU1uXo29vR0dHOzs7ExMTwjo73bW37XV3Aj1TCYELl5u3n5+fW2Obn6O7f4OrZ2+g0QJkxPpgvO5bh4uvS1OTP0ePCwsJQW6ZLVqTs7fHd3d3V1dXqv79VX6lET6A1TIxXUIBSgHxWQnpelHf+WVnopkXqbC7j5Ozi4uLDw8NGUaFATJ9SgH3r6+vGyd7BxNva2trX19ejqM2gpczHx8dze7Zha67Z2dlTgH1aXQeSAAAAJnRSTlMA6ff+497Y8NL+/fv49P379sqab/BeOiX06tzVy8m/tKqpalA7G6oKj0EAAAJlSURBVEjHndNXWxpBFIDhcS2ICRLAkt4Dx4WhLk0E6R0MYoIISrWX5P9f5cwSIRC2+T1czMV5n2FnZwn2eWONUqCAv3H2Uf5Ra1hx4+0WEXtDQW0fCPYJ1EffEfIV4CSROAE4jsePoTFsNmTJF/IeIHF2lgCIn57GodlqDWXBK7IwBYatVlMWFAildPKX7I3m74Z9fsCiQChoimoFQAz04Ad2gH1n9fv9n9hgMNDr9euLWD6fLxQKxaLfb7dTSlahbFVdEPwIQtrAihZQgyKCtCagbQe3xh0QFMgy5MR11+ewYY5/qlZ7vT2xu93ULKjbFLpiUxnIIwjgKmVTLDUFXMrAi2NJWCRLIthTBo4xyOLKpwyqU6CuDCI41hFBCVdOhyLw4FgJ1skCAiyl9BSHbCorgo6VJXTru5hrVCQS8Yr5xLzX59YJSFpVFwD9U0BGC3hGdFpATgRupTGe9R9I1b1ePBvXKDyvq/O/44LT4/E4BUbSCAwj8Evq6HlnOBprx6JhJz8Gktc7xeaP9ndY+0coQvCccFBD4JW60UIY50ciLOAODAQRVOeCHm4Q3Xks6uRDY+CQ+AR4T2wMYh6+jMCIQOp78CFoj0H7EQgIuhI3dGaHCrwgADwCPjJvA372GRigCJg49FUdk3D87pq3zp4SA5zc1Zh9DxfwkpjgUg5Mv+lbeE3McC8Lpu7SA3wk2xzcqL2tN5DfIsQC8HB7UamUy6FQOpTO5QKBQDZbKnWSyUzGjdWCwaDA8+7Le4BNgm3qQGWchYh9s5hNq6wVbBlbwhZYOp3OYOA4zmgEypnM2zj8ByIdedKrH8vDAAAAAElFTkSuQmCC\");zoom:.8}sharecard-content{padding:15px;max-height:500px;font-size:20px;text-align:justify;background-color:#2196f3;overflow-x:hidden;overflow-y:auto}sharecard-via{padding:10px;font-size:10px;background-color:#2196f3}sharecard-footer{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;padding-right:5px;height:100px;background-color:#fff;color:#878787;font-size:15px;font-weight:500;border-bottom-left-radius:10px;border-bottom-right-radius:10px}sharecard-footer,sharecard-footer div{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center}sharecard-footer span.qrcode{display:block;width:100px;height:100px;margin:5px;background-repeat:no-repeat;background-position:50%;background-image:url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAA8CAMAAAANIilAAAAA7VBMVEUAAAD///8ZGRnw8PBWVlb4+PgeHh719fVEREQlJSUODg6Ojo7Ly8v9/f1NTU2VlZUFBQV6enrCwsLy8vLh4eE0NDQLCwu8vLyXl5dxcXHa2trAwMCFhYVDQ0OysrKampo7OzssLCwICAioqKjJyckhISHu7u4/Pz9TU1NQUFBLS0tAQED6+vrS0tKRkZFISEgvLy+goKB+fn5vb29nZ2fm5uYbGxvk5OTX19d2dnZaWlre3t5hYWEyMjK5ubkoKCgVFRXQ0NDMzMzFxcW0tLSsrKykpKSMjIzq6urU1NSAgICvr6+cnJyHh4dsbGyfc25QAAAFkElEQVRIx4WXB3faMBCA74wHxgMMGAOh1MyyVyBAmtVmd/3/n1OdDtWstt/Li5JD35MtnU4CMnCIlkLEOpSKuMPhuI4FE444L9+dyv3zciad/rAjfU/yOPpGcjWS1NKSGsk29WTSD1IeYsIPkvsAJF+C5BIZkieYMNjJnsF4+JHk61wOSlVDyOIPqKBgZIxYTrqy/AmNtC1ps7yqlgE6dgYa1WqD5aV9SbKDbe6ZNnCq5A5ILlhGjEASIoawJdmHHo98AZLOnmxzKM9yK9Aht63FoAWBBmEgsEHnkfMgsZU8PJbpbXJd3MIep/Lg/MjbRqMRRuNtQ4Tthga5RiNzfmSWD97ZExQ64HhtgLb3CmbBGyj54vixjyeMsKjnV4AvOAHTwsHphKnH9toXki7Li3jLsgswizskv+c3PHKXe7ZHu5E/YMKcM2yqZIJkAclZTPClbJezivI1yTr4f+TeMib5qXxHsr7XtUFJDIc8pLAHA7Su4JXkd8ySnKZddQXH2NohswIutRu0Qu0jyS46JPugU+gISB1R8NBKWegVUsahTKEjAP9Bm5bKAc3DPlzjKaDvscE7PeEJu63WUg9a82tdA1O/ESupL9Cr6C1cXetjIe9zgQ4kvKLgHi6xC5LcGq9hhmiK0AYgUvLQtzm3n/x0jnum/Vo9j1jxg/pL3/f9W8isMOsv6/Ubf47rvl+u17nnZ1xQ+4iIXa6IpRVeimEEE3pnxO8kIz4CbFDyidVSpooBCCLLsj5noFlqUg2rwK0l+Anmm+VhCzKfrRHtqjGOLANxUKIUiYvFEcuaaZpXANniD5ZzpiBDTSRkuDJfWM6awxGuikUArp7fIOE7RlB6OygGTyhfsMyrtwDNIAkcp1YRhKC9Oh2IHUFQ6UPupnLL3icODaD5zVlUto7zhm1n7kmZ5kDSQBxykfZhD66eaQBoWriEGPcA182C4Crst90Z9NwvHgahDTALw/Ae4D500Pjq3oj/4sjtwcwVrCkkgB01HB8cdM0iIlWSr6IpNqFO+1mDHQFWORtO5EIi08b4jxy6giBsgKDnRnEYYdd9nIYvaLmuhS/h9DF5bEFr/7HTKAjUqWY1oUUBKgYEZdgIP6gJE2xQIWVvLhZBcAWx843kz87PDDi4cgR92s8/1FLpAGNeKiUbGtRQEIPkGb9TM1EF8MpCVEni7pIkkUdDs1ZcI/ZUer6YZg4WxTtqMmYsZJWebbOzEekZV4sCKaNhBaXQQ0NtjL71ZooNE1vWLfyyyFUbw7MsD0fWOFMSqAnbwj1Kuk0Aqp4aJ91MZhhvyS7+oQoMy5v63Jfoz/UYfPSiep2KQb5e4/gt1Ycdc7Se6jNyVbpuQNI08FrICQ6ccKnSXddrKCnqkqWFupJFAewKudSTBVAyBEjrLSXjCYnc5rrdQVl6VaiKqOTToi/kaSrlcW5fpGpgrlJTLvoGVxKDOg7PHzc6NLXOmuUHTZQhTWvS4T7T5ixPqGPz/EHXp/azkMeQoGOqBBOSq1gD4vwRe1culz8W8HlZKQt6Sjbm5XeS9eWizJw73HcsOW8mSpa0eT8zfK1w85LdtWKTf5dWfCPzMg5J+MBdsvvy6Q2QD/d91sfzouRz9zAdBp6HCcUzskccyBdKzjTC9ZE8HT8+JHLxtiE4d33Ud0uleOObvpXZk4E4/9h2sKD9t6oxgaCFxs9AHiI3wYJCndMbIMs9lLi7vEHFLxAUURyciOnTyzrLH6qSJwo+8CWuQIFL2wSoVyvQea/qtk2yvPtb4mekZMhJQkPwyvIzBbJGJD+jX3eGcfIFhWVmxsVAG5FMgSzm9y4wKL8aJdzvyctoTqEgep6K5lckWGM3uuuA5DadFvIhiTzBL1xzVtT0UDEDxd9ldeutcJLoyvUaoPgNdiqckZLamd0AAAAASUVORK5CYII=\")}sharecard-control{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;-webkit-box-align:center;-ms-flex-align:center;align-items:center;padding:0 19px;height:80px;background-color:#fff}simpread-snapshot{width:100%;height:100%;cursor:move;z-index:2147483645}simpread-snapshot,sr-mask{position:fixed;left:0;top:0}sr-mask{background-color:rgba(0,0,0,.1)}.simpread-feedback,.simpread-urlscheme{position:fixed;right:20px;bottom:20px;z-index:2147483646}simpread-feedback,simpread-urlscheme{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:start;-ms-flex-align:start;align-items:flex-start;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;padding:20px 20px 0;width:500px;color:rgba(51,51,51,.87);background-color:#fff;border-radius:3px;box-shadow:0 0 2px rgba(0,0,0,.12),0 2px 2px rgba(0,0,0,.26);overflow:hidden;-webkit-transform-origin:bottom;transform-origin:bottom;-webkit-transition:all .6s ease;transition:all .6s ease}simpread-feedback *,simpread-urlscheme *{font-size:12px!important;box-sizing:border-box}simpread-feedback.active,simpread-urlscheme.active{-webkit-animation-name:srFadeInUp;animation-name:srFadeInUp;-webkit-animation-duration:.45s;animation-duration:.45s;-webkit-animation-fill-mode:both;animation-fill-mode:both}simpread-feedback.hide,simpread-urlscheme.hide{-webkit-animation-name:srFadeInDown;animation-name:srFadeInDown;-webkit-animation-duration:.45s;animation-duration:.45s;-webkit-animation-fill-mode:both;animation-fill-mode:both}simpread-feedback sr-fb-label,simpread-urlscheme sr-urls-label{width:100%}simpread-feedback sr-fb-head,simpread-urlscheme sr-urls-head{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;margin-bottom:5px;width:100%}simpread-feedback sr-fb-content,simpread-urlscheme sr-urls-content{margin-bottom:5px;width:100%}simpread-feedback sr-urls-footer,simpread-urlscheme sr-urls-footer{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:end;-ms-flex-pack:end;justify-content:flex-end;width:100%}simpread-feedback sr-fb-a,simpread-urlscheme sr-urls-a{color:#2163f7;cursor:pointer}simpread-feedback text-field-state,simpread-urlscheme text-field-state{border-top:none rgba(34,101,247,.8)!important;border-left:none rgba(34,101,247,.8)!important;border-right:none rgba(34,101,247,.8)!important;border-bottom:2px solid rgba(34,101,247,.8)!important}simpread-feedback switch,simpread-urlscheme switch{margin-top:0!important}@-webkit-keyframes srFadeInUp{0%{opacity:0;-webkit-transform:translateY(100px);transform:translateY(100px)}to{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}}@keyframes srFadeInUp{0%{opacity:0;-webkit-transform:translateY(100px);transform:translateY(100px)}to{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}}@-webkit-keyframes srFadeInDown{0%{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}to{opacity:0;-webkit-transform:translateY(100px);transform:translateY(100px)}}@keyframes srFadeInDown{0%{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}to{opacity:0;-webkit-transform:translateY(100px);transform:translateY(100px)}}simpread-feedback sr-fb-head{font-weight:700}simpread-feedback sr-fb-content{-webkit-box-orient:vertical;-ms-flex-direction:column;flex-direction:column}simpread-feedback sr-fb-content,simpread-feedback sr-fb-footer{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-direction:normal}simpread-feedback sr-fb-footer{-webkit-box-orient:horizontal;-ms-flex-direction:row;flex-direction:row;-webkit-box-pack:end;-ms-flex-pack:end;justify-content:flex-end;width:100%}simpread-feedback sr-close{position:absolute;right:20px;cursor:pointer;-webkit-transition:all 1s cubic-bezier(.23,1,.32,1) .1s;transition:all 1s cubic-bezier(.23,1,.32,1) .1s;z-index:200}simpread-feedback sr-close:hover{-webkit-transform:rotate(-15deg) scale(1.3);transform:rotate(-15deg) scale(1.3)}simpread-feedback sr-stars{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;margin-top:10px}simpread-feedback sr-stars i{margin-right:10px;cursor:pointer}simpread-feedback sr-stars i svg{-webkit-transition:all 1s cubic-bezier(.23,1,.32,1) .1s;transition:all 1s cubic-bezier(.23,1,.32,1) .1s}simpread-feedback sr-stars i svg:hover{-webkit-transform:rotate(-15deg) scale(1.3);transform:rotate(-15deg) scale(1.3)}simpread-feedback sr-stars i.active svg{-webkit-transform:rotate(0) scale(1);transform:rotate(0) scale(1)}simpread-feedback sr-emojis{display:block;height:100px;overflow:hidden}simpread-feedback sr-emoji{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-transition:.3s;transition:.3s}simpread-feedback sr-emoji>svg{margin:15px 0;width:70px;height:70px;-ms-flex-negative:0;flex-shrink:0}simpread-feedback sr-stars-footer{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;margin:10px 0 20px}</style><style type=\"text/css\">sr-opt-focus,sr-opt-read{-webkit-box-orient:vertical;-ms-flex-direction:column;flex-direction:column}sr-opt-focus,sr-opt-gp,sr-opt-read{display:-webkit-flex;-webkit-box-direction:normal;width:100%}sr-opt-gp{position:relative;-webkit-box-orient:horizontal;-ms-flex-flow:row nowrap;flex-flow:row;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start;margin-bottom:25px;font-size:15px}sr-opt-gp input,sr-opt-gp textarea{font-family:DIN-Regular,Bookerly,Georgia,-apple-system,PingFang SC,Microsoft Yahei,Lantinghei SC,Hiragino Sans GB,Microsoft Sans Serif,WenQuanYi Micro Hei,sans-serif!important}sr-opt-gp sr-opt-label{display:block;position:absolute;margin:-8px 0 0;font-size:14px;font-weight:700;color:rgba(0,137,123,.8);-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;pointer-events:none;-webkit-transform:scale(.75) translateY(-8px);transform:scale(.75) translateY(-8px);-webkit-transform-origin:left top 0;transform-origin:left top 0}sr-opt-themes{display:-webkit-flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row nowrap;flex-flow:row;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;width:100%;margin:8px 0 17px;padding:0}sr-opt-theme{width:40px;height:20px;cursor:pointer;list-style:none;border-radius:3px;border:1px solid #212121;box-sizing:border-box;opacity:1;-webkit-transition:all .5s cubic-bezier(.23,1,.32,1) 0ms;transition:all .5s cubic-bezier(.23,1,.32,1) 0ms}sr-opt-theme:hover{-webkit-transform:translateY(-1px);transform:translateY(-1px);box-shadow:0 5px 10px rgba(0,0,0,.2)}sr-opt-theme:not(:first-child){margin-left:5px}sr-opt-theme[sr-type=active]{box-shadow:0 5px 10px rgba(0,0,0,.2);border:none}sr-opt-focus text-field-float,sr-opt-read text-field-float{color:rgba(0,137,123,.8)!important}sr-opt-focus text-field-state,sr-opt-read text-field-state{border-bottom-color:rgba(0,137,123,.8)!important}sr-opt-gp svg{-webkit-animation:none 0s ease 0s 1 normal none running;animation:none 0s ease 0s 1 normal none running;-webkit-backface-visibility:visible;backface-visibility:visible;background:transparent none repeat 0 0/auto auto padding-box border-box scroll;border:medium none currentColor;border-collapse:separate;-o-border-image:none;border-image:none;border-radius:0;border-spacing:0;bottom:auto;box-shadow:none;box-sizing:content-box;caption-side:top;clear:none;clip:auto;color:#000;-webkit-columns:auto;-moz-columns:auto;columns:auto;-webkit-column-count:auto;-moz-column-count:auto;column-count:auto;-webkit-column-fill:balance;-moz-column-fill:balance;column-fill:balance;-webkit-column-gap:normal;-moz-column-gap:normal;column-gap:normal;-webkit-column-rule:medium none currentColor;-moz-column-rule:medium none currentColor;column-rule:medium none currentColor;-webkit-column-span:1;-moz-column-span:1;column-span:1;-webkit-column-width:auto;-moz-column-width:auto;column-width:auto;content:normal;counter-increment:none;counter-reset:none;cursor:auto;direction:ltr;display:inline;empty-cells:show;float:none;font-family:serif;font-size:medium;font-style:normal;font-variant:normal;font-weight:400;font-stretch:normal;line-height:normal;height:auto;-webkit-hyphens:none;-ms-hyphens:none;hyphens:none;left:auto;letter-spacing:normal;list-style:disc outside none;margin:0;max-height:none;max-width:none;min-height:0;min-width:0;opacity:1;orphans:2;outline:medium none invert;overflow:visible;overflow-x:visible;overflow-y:visible;padding:0;page-break-after:auto;page-break-before:auto;page-break-inside:auto;-webkit-perspective:none;perspective:none;-webkit-perspective-origin:50% 50%;perspective-origin:50% 50%;position:static;right:auto;-moz-tab-size:8;-o-tab-size:8;tab-size:8;table-layout:auto;text-align:left;text-align-last:auto;text-decoration:none;text-indent:0;text-shadow:none;text-transform:none;top:auto;-webkit-transform:none;transform:none;-webkit-transform-origin:50% 50% 0;transform-origin:50% 50% 0;-webkit-transform-style:flat;transform-style:flat;-webkit-transition:none 0s ease 0s;transition:none 0s ease 0s;unicode-bidi:normal;vertical-align:baseline;visibility:visible;white-space:normal;widows:2;width:auto;word-spacing:normal;z-index:auto;all:initial}sr-opt-gp actions{display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;width:100%}sr-opt-gp action-item{display:-webkit-box!important;display:-ms-flexbox!important;display:flex!important;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;-webkit-box-align:center;-ms-flex-align:center;align-items:center;width:100%;padding:5px 0}sr-opt-gp action-item:first-child{padding-top:10px}sr-opt-gp action-label{font-size:12px;font-weight:400;margin-left:10px}sr-opt-gp action-icon{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}sr-opt-gp action-icon,sr-opt-gp action-rect{-webkit-box-align:center;-ms-flex-align:center;align-items:center}sr-opt-gp action-rect{display:-webkit-box!important;display:-ms-flexbox!important;display:flex!important;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row}sr-opt-gp export-icon{display:block;padding:3px;width:21px;height:21px;background-size:64%;background-position:50%;background-repeat:no-repeat;border:none;border-radius:4px}sr-opt-gp export-icon.symbol{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center}sr-opt-gp export-icon i{color:#fff;font-size:12px}sr-opt-gp action-item[action=joplin] export-icon{background-size:50%}sr-opt-gp action-item[action=focusnote] export-icon{background-size:42%}style-bar{margin-bottom:10px}style-bar sr-opt-read-adv sr-opt-gp{margin:10px 0 0}style-bar sr-opt-read-adv sr-opt-gp.large{position:relative;margin-top:25px}sr-opt-read-adv sr-opt-gp:last-child{margin-bottom:0}style-bar sr-opt-gp:last-child{margin-bottom:10px}style-bar sr-opt-read-adv sr-opt-label.row{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;pointer-events:auto;cursor:pointer}style-bar sr-opt-read-adv sr-opt-label.row,style-bar sr-opt-read-adv sr-opt-label sr-opt-icon{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}style-bar sr-opt-read-adv sr-opt-label sr-opt-icon{-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-transform:rotate(90deg);transform:rotate(90deg);-webkit-transition:all .25s ease-out;transition:all .25s ease-out}style-bar sr-opt-read-adv sr-opt-label sr-opt-icon.open{-webkit-transform:rotate(270deg);transform:rotate(270deg)}style-bar sr-opt-read-adv.dividers{border-top:1px solid #eaeaea;border-bottom:1px solid #eaeaea}style-bar switch{margin:0!important}style-bar switch span{font-size:13px!important;font-weight:700!important}style-bar switch subtitle{font-size:12px!important}style-bar text-field textarea{resize:vertical!important}style-bar sr-opt-button{position:absolute!important;right:12px;bottom:14px;display:-webkit-box!important;display:-ms-flexbox!important;display:flex!important;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;padding:3px;border-radius:50%;-webkit-transition:all .25s ease-out;transition:all .25s ease-out}style-bar sr-opt-button,style-bar sr-opt-button svg{cursor:pointer!important}style-bar text-field.hidden+sr-opt-button{opacity:0}</style><style type=\"text/css\">notify-gp{font:300 14px -apple-system,PingFang SC,Microsoft Yahei,Lantinghei SC,Hiragino Sans GB,Microsoft Sans Serif,WenQuanYi Micro Hei,sans-serif;text-rendering:optimizelegibility;-webkit-text-size-adjust:100%;-webkit-font-smoothing:antialiased;display:-webkit-flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-flow:column nowrap;flex-flow:column;-webkit-box-align:end;-ms-flex-align:end;align-items:flex-end;position:fixed;top:0;right:0;margin:0 15px 0 0;padding:0;text-transform:none;pointer-events:none}notify-gp notify{display:-webkit-flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;margin:0;margin-top:15px;padding:14px 24px;min-width:288px;max-width:568px;min-height:48px;color:hsla(0,0%,100%,.9);background-color:#000;box-sizing:border-box;border-radius:4px;pointer-events:auto;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;opacity:0;-webkit-transform:scaleY(0);transform:scaleY(0);-webkit-transform-origin:left top 0;transform-origin:left top 0;-webkit-transition:all .45s cubic-bezier(.23,1,.32,1) 0ms,opacity 1s cubic-bezier(.23,1,.32,1) 0ms;transition:all .45s cubic-bezier(.23,1,.32,1) 0ms,opacity 1s cubic-bezier(.23,1,.32,1) 0ms;box-shadow:0 3px 5px -1px rgba(0,0,0,.2),0 6px 10px 0 rgba(0,0,0,.14),0 1px 18px 0 rgba(0,0,0,.12)}notify-gp notify-title{font-size:13px;font-weight:700}notify-gp notify-content{display:block;font-size:14px;font-weight:400;text-align:left;overflow:hidden}notify-gp notify-content a,notify-gp notify-content a:active,notify-gp notify-content a:link,notify-gp notify-content a:visited{margin:inherit;padding-bottom:5px;color:#fff;font-size:inherit;text-decoration:none;-webkit-transition:color .5s;transition:color .5s}notify-gp notify-content a:hover{margin:0;margin:initial;padding:0;padding:initial;color:inherit;font-size:inherit;text-decoration:none}notify-gp notify-i{display:none;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;margin:0 10px 0 0;width:24px;height:24px;background-position:50%;background-repeat:no-repeat}notify-gp notify-action,notify-gp notify-cancel{display:none;margin:0 8px;max-width:80px;min-width:56px;height:36px;line-height:34px;color:#bb86fc;font-weight:500;font-size:inherit;text-transform:uppercase;text-align:center;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;-webkit-transition:all .5s cubic-bezier(.23,1,.32,1) 0ms;transition:all .5s cubic-bezier(.23,1,.32,1) 0ms;cursor:pointer}notify-gp .notify-error notify-action,notify-gp .notify-error notify-cancel,notify-gp .notify-success notify-action,notify-gp .notify-success notify-cancel,notify-gp .notify-warning notify-action,notify-gp .notify-warning notify-cancel{color:#fff}notify-gp notify-action:active,notify-gp notify-cancel:active{border-radius:4px;background-color:rgba(98,0,238,.3)}notify-gp notify-cancel{margin:0}notify-gp notify-a{display:block;position:absolute;top:5px;right:5px;cursor:pointer}notify-gp notify-exit{display:none;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;margin-left:5px;width:36px;height:36px;min-width:36px;min-height:36px;background-color:transparent;border-radius:50%;-webkit-transition:all .5s cubic-bezier(.23,1,.32,1) 0ms;transition:all .5s cubic-bezier(.23,1,.32,1) 0ms;cursor:pointer}notify-gp notify-exit:hover{background-color:hsla(0,0%,100%,.4)}notify-gp notify-exit:active{background-color:hsla(0,0%,100%,.2)}notify-gp notify-a notify-span{display:block;width:16px;height:16px;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABDklEQVQ4T6VT0VFCQQzcrQA7ECoRK1AqEDugA6ECsQPsADvgVSAlaAlWEGdvkjchczI45Osud9nc7m2IEmY2BfAEYA5A6xsARwAHAB8ktR6DeWNmKwAvXlSxY78i+RabEcDM9gAe/qoq+T3JhXINwDu/Xlgc1zYk13TOn+XZA4C7AvgN4LbkZgJYO+84O5C8N7Odi6n8O8llh+ZGAD3uO5LPDgIvzoDRbBDAV+dputBAXKNecQM5B9CefQlAj0JwVmdLdGSwHI1CFXEgOS8ihia1WRNRdpU9JwlatpWVc0gr3c0xu95IAfdPK2uoHkcrJxANkzTJdPKTf3ROchvJk2n0LxNPfV9vnDVEJ+P8C6jMhLeGEqMKAAAAAElFTkSuQmCC);opacity:.9}notify-gp notify-i.holdon{display:block;margin:0 0 0 24px;width:20px;height:20px;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAQAAAAngNWGAAAATUlEQVR4AWMYSuB/4P+V/1lRRFiBIoEYCoGC//+vAypFKFsHFFkJV4AsAVGKzsOjFFUZHqUElCGUwpRRrpCw1YQ9Qzh4SA5wwlE4hAAAiFGQefYhNJkAAAAASUVORK5CYII=);cursor:pointer}notify-gp .notify-show{opacity:1;-webkit-transform:scaleY(1)!important;transform:scaleY(1)!important}notify-gp .notify-hide{-webkit-animation-name:fadeOutUp;animation-name:fadeOutUp;-webkit-animation-duration:.45s;animation-duration:.45s;-webkit-animation-fill-mode:both;animation-fill-mode:both}notify-gp .notify-success{background-color:#4caf50}notify-gp .notify-warning{background-color:#ffa000}notify-gp .notify-error{background-color:#ef5350}notify-gp .notify-info{background-color:#1976d2}notify-gp .notify-modal{-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-flow:column nowrap;flex-flow:column;-webkit-box-align:start;-ms-flex-align:start;align-items:flex-start;height:auto;max-height:200px;box-shadow:0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12),0 3px 1px -2px rgba(0,0,0,.2)}notify-gp .notify-modal .notify-modal-content{margin-top:5px;font-size:13px;white-space:normal}notify-gp .notify-modal .notify-modal-content a{margin:0;padding:0;color:inherit;font-size:inherit;text-decoration:underline;cursor:pointer}notify-gp .notify-modal .notify-modal-content a:active,notify-gp .notify-modal .notify-modal-content a:focus,notify-gp .notify-modal .notify-modal-content a:hover,notify-gp .notify-modal .notify-modal-content a:visited{color:inherit}notify-gp .notify-snackbar{position:fixed;bottom:0;left:50%;margin-bottom:5px;-webkit-transform-origin:left bottom 0;transform-origin:left bottom 0}.notify-position-lt-corner{-webkit-box-align:start;-ms-flex-align:start;align-items:flex-start;margin:0 0 0 15px;left:0;right:auto}.notify-position-lb-corner{margin:0 0 15px 15px;right:auto;left:0}.notify-position-lb-corner,.notify-position-rb-corner{-webkit-box-orient:vertical;-webkit-box-direction:reverse;-ms-flex-flow:column-reverse wrap-reverse;flex-flow:column-reverse wrap-reverse;top:auto;bottom:0}.notify-position-rb-corner{-webkit-box-align:start;-ms-flex-align:start;align-items:flex-start;margin:0 15px 15px 0;left:auto;right:0}@-webkit-keyframes fadeOutUp{0%{opacity:1}to{margin-top:0;padding:0;height:0;min-height:0;opacity:0;-webkit-transform:scaleY(0);transform:scaleY(0)}}@keyframes fadeOutUp{0%{opacity:1}to{margin-top:0;padding:0;height:0;min-height:0;opacity:0;-webkit-transform:scaleY(0);transform:scaleY(0)}}@media (pointer:coarse){notify-gp{top:auto;bottom:0;left:0;margin:0 10px 10px}notify-gp notify{width:100%;max-width:600px}notify-gp .notify-hide,notify-gp .notify-show{-webkit-transform-origin:bottom!important;transform-origin:bottom!important}notify-gp .notify-snackbar{position:static}}</style><style type=\"text/css\">dialog-gp .carousel,welcome .carousel{position:relative;width:100%;height:400px;-webkit-perspective:500px;perspective:500px;-webkit-transform-style:preserve-3d;transform-style:preserve-3d;-webkit-transform-origin:0 50%;transform-origin:0 50%;overflow:hidden}dialog-gp .carousel.carousel-slider,welcome .carousel.carousel-slider{top:0;left:0;height:100%}dialog-gp .carousel.carousel-slider .carousel-item,welcome .carousel.carousel-slider .carousel-item{position:absolute;top:0;left:0;width:100%;height:100%;min-height:400px}dialog-gp .carousel .carousel-item,welcome .carousel .carousel-item{display:none;position:absolute;top:0;left:0;width:200px;height:200px}dialog-gp .carousel .carousel-item>img,welcome .carousel .carousel-item>img{width:100%}dialog-gp .carousel .indicators,welcome .carousel .indicators{position:absolute;margin:0;padding:0;left:0;right:0;bottom:0;text-align:center}dialog-gp .carousel .indicators .indicator-item,welcome .carousel .indicators .indicator-item{display:inline-block;position:relative;margin:14px 4px;height:10px;width:10px;background-color:#e0e0e0;-webkit-transition:background-color .3s;transition:background-color .3s;border-radius:50%;cursor:pointer}dialog-gp .carousel .indicators .indicator-item.active,welcome .carousel .indicators .indicator-item.active{background-color:#4caf50}dialog-gp .carousel .carousel-item:not(.active) .materialboxed,dialog-gp .carousel.scrolling .carousel-item .materialboxed,welcome .carousel .carousel-item:not(.active) .materialboxed,welcome .carousel.scrolling .carousel-item .materialboxed{pointer-events:none}</style><style type=\"text/css\">.simpread-upgrade-root *{box-sizing:border-box}.simpread-upgrade-root{-webkit-transition:all .25s ease-out;transition:all .25s ease-out}.simpread-upgrade-root.open{background-color:rgba(51,51,51,.8)}.simpread-upgrade-root dialog-gp{position:relative}.simpread-upgrade-root dialog-gp .close{position:fixed;top:0;right:0;z-index:2}.simpread-upgrade-root dialog-gp .close:hover{-webkit-transform:rotate(270deg);transform:rotate(270deg);-webkit-transition:all .25s ease-out;transition:all .25s ease-out}.simpread-upgrade-root dialog-content{padding-bottom:80px!important;overflow-y:hidden}.simpread-upgrade-root dialog-content:hover{overflow-y:overlay}.simpread-upgrade-root dialog-content::-webkit-scrollbar-track{background-color:transparent}.simpread-upgrade-root dialog-content::-webkit-scrollbar{width:12px}.simpread-upgrade-root dialog-content::-webkit-scrollbar-thumb{background-clip:padding-box;padding-top:80px;background-color:#ddd;border:3px solid transparent;border-radius:8px}.simpread-upgrade-root .floating{position:absolute;left:0;right:0;bottom:0;height:80px;overflow-y:hidden}.simpread-upgrade-root .floating,.simpread-upgrade-root .floating .billing{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.simpread-upgrade-root .floating .billing{-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;padding:1px 40px;color:#fff;background-color:#4dbb7c;font-size:15px;font-weight:400;opacity:0;border-radius:30px;-webkit-transition:all .5s cubic-bezier(.23,1,.32,1) 0ms;transition:all .5s cubic-bezier(.23,1,.32,1) 0ms;box-shadow:0 12px 18px -6px rgba(0,0,0,.3)}.simpread-upgrade-root .floating .billing.open{-webkit-animation-name:fadeInUp;animation-name:fadeInUp;-webkit-animation-duration:.3s;animation-duration:.3s;-webkit-animation-fill-mode:both;animation-fill-mode:both}.simpread-upgrade-root .floating .billing .sales{font-size:12px}.simpread-upgrade-root .floating .billing .rate{margin:0 5px}.simpread-upgrade-root .floating .billing .price{margin-left:2px;margin-right:5px}.upgrade{position:relative;color:rgba(51,51,51,.87);font-family:Hiragino Sans GB,Microsoft Yahei;text-shadow:none}.upgrade .head{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.upgrade .head img{margin-bottom:5px;width:60px;border-radius:9px;box-shadow:0 12px 18px -6px rgba(0,0,0,.3)}.upgrade .head .title{margin:10px 0;font-weight:700;font-size:15px}.upgrade .head .desc{width:70%;text-align:center;font-size:13px}.upgrade .features{position:relative;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;-ms-flex-pack:distribute;justify-content:space-around;-webkit-transition:height .2s cubic-bezier(.23,1,.32,1) 0ms;transition:height .2s cubic-bezier(.23,1,.32,1) 0ms}.upgrade .features.init{height:100px}.upgrade .features.init .base,.upgrade .features.init .pro{opacity:0}.upgrade .loading{position:absolute;left:0;top:0;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;width:100%;height:100%;background-color:#fff;z-index:1}.upgrade .loading span{width:50px;height:50px;opacity:.87}.features.error{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;color:inherit;font-size:14px}.features.error img{margin:10px 0;width:300px}.upgrade .base,.upgrade .pro{margin:20px 20px 13px;width:100%;text-align:center;opacity:1;-webkit-transition:opacity .2s cubic-bezier(.23,1,.32,1) 0ms;transition:opacity .2s cubic-bezier(.23,1,.32,1) 0ms}.upgrade .pricecard{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;border-radius:4px;border:1px solid #eef1f4;position:relative}.upgrade .pro .pricecard{border:2px solid #4dbb7c;box-shadow:0 12px 18px -6px rgba(0,0,0,.3)}.upgrade .pricecard .mode{margin:20px 10px 10px;font-size:18px;font-weight:700}.upgrade .pricecard .sales{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;height:92px;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.upgrade .pricecard .discountrate{position:absolute;top:-12px;left:0;right:0}.upgrade .pricecard .discountrate .rate{padding:3px 10px;color:#fff;background-color:#4dbb7c;font-weight:700;font-size:13px;border-radius:10px}.upgrade .pricecard .desc{font-size:30px}.upgrade .pricecard .desc del{font-size:15px;font-weight:700;text-decoration:line-through}.upgrade .pricecard .price{position:relative;color:#4dbb7c;font-size:30px;font-weight:700}.upgrade .pricecard .message{position:relative;font-size:11px;font-weight:400;background-image:-webkit-linear-gradient(top,hsla(0,0%,100%,0) 50%,#ffeb3b 0);background-image:linear-gradient(180deg,hsla(0,0%,100%,0) 50%,#ffeb3b 0)}.upgrade .pricecard .countdown{margin-top:5px}.upgrade .pricecard .billing{position:relative;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;margin:30px 20%;padding:5px;color:#333;background-color:#e2e2e2;font-size:15px;font-style:normal;font-weight:700;border-radius:4px;cursor:pointer}.upgrade .pricecard .billing,.upgrade .pricecard .billing i{-webkit-transition:all .5s cubic-bezier(.23,1,.32,1) 0ms;transition:all .5s cubic-bezier(.23,1,.32,1) 0ms}.upgrade .pricecard .billing i{height:27px;line-height:22px;margin-left:5px}.upgrade .pricecard .billing:hover{box-shadow:0 3px 1px -2px rgba(0,0,0,.2),0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12)}.upgrade .pricecard .billing:hover i{-webkit-transform:rotate(180deg);transform:rotate(180deg)}.upgrade .pricecard .billing:hover .dropdown-price{opacity:1;-webkit-transform:scale(1);transform:scale(1)}.upgrade .pricecard .billing .dropdown-price{position:absolute;left:-41px;top:38px;-webkit-box-orient:vertical;-ms-flex-direction:column;flex-direction:column;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;width:270px;color:rgba(51,51,51,.87);font-size:12px;text-shadow:none;box-sizing:border-box;border-radius:4px;box-shadow:0 8px 10px 1px rgba(0,0,0,.14),0 3px 14px 2px rgba(0,0,0,.12),0 5px 5px -3px rgba(0,0,0,.2);opacity:0;-webkit-transform:scaleY(0);transform:scaleY(0);-webkit-transform-origin:left top 0;transform-origin:left top 0;-webkit-transition:opacity 1s cubic-bezier(.23,1,.32,1) 0ms,-webkit-transform .45s cubic-bezier(.23,1,.32,1) 0ms;transition:opacity 1s cubic-bezier(.23,1,.32,1) 0ms,-webkit-transform .45s cubic-bezier(.23,1,.32,1) 0ms;transition:transform .45s cubic-bezier(.23,1,.32,1) 0ms,opacity 1s cubic-bezier(.23,1,.32,1) 0ms;transition:transform .45s cubic-bezier(.23,1,.32,1) 0ms,opacity 1s cubic-bezier(.23,1,.32,1) 0ms,-webkit-transform .45s cubic-bezier(.23,1,.32,1) 0ms;z-index:1}.upgrade .pricecard .billing .dropdown-price,.upgrade .pricecard .billing .dropdown-price .store{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-direction:normal;-webkit-box-align:center;-ms-flex-align:center;align-items:center;background-color:#fff}.upgrade .pricecard .billing .dropdown-price .store{-webkit-box-orient:horizontal;-ms-flex-direction:row;flex-direction:row;padding:8px 24px 8px 16px;width:100%;-webkit-transition:all 1s cubic-bezier(.23,1,.32,1) 0ms;transition:all 1s cubic-bezier(.23,1,.32,1) 0ms;cursor:pointer}.upgrade .pricecard .billing .dropdown-price .store:hover{background-color:#eee}.upgrade .pricecard .billing .dropdown-price .store:hover i{-webkit-transform:rotate(270deg) translateY(7px);transform:rotate(270deg) translateY(7px)}.upgrade .pricecard .billing .dropdown-price .store .names{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;-webkit-box-align:start;-ms-flex-align:start;align-items:flex-start;width:135px}.upgrade .pricecard .billing .dropdown-price .store .des{width:100%;color:rgba(51,51,51,.56);text-align:left;font-size:10px;-webkit-transform:scale(.8) translateX(-17px);transform:scale(.8) translateX(-17px)}.upgrade .pricecard .billing .dropdown-price .store .num{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;width:46px}.upgrade .pricecard .billing .dropdown-price .store .tips{-webkit-transform:scale(.8);transform:scale(.8);color:#4dbb7c}.upgrade .pricecard .billing .dropdown-price .store i{-webkit-transform:rotate(270deg);transform:rotate(270deg)}.upgrade .base[data-enable=false] .pricecard,.upgrade .pro[data-enable=true] .pricecard{-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;height:210px}.upgrade .base[data-enable=false] .pricecard .mode,.upgrade .pro[data-enable=true] .pricecard .mode{font-size:25px}.upgrade .base[data-enable=false] .pricecard .billing,.upgrade .base[data-enable=false] .pricecard .sales,.upgrade .pro[data-enable=true] .pricecard .billing .dropdown-price,.upgrade .pro[data-enable=true] .pricecard .billing i,.upgrade .pro[data-enable=true] .pricecard .discountrate,.upgrade .pro[data-enable=true] .pricecard .sales{display:none}.upgrade .pro[data-enable=true] .pricecard .billing{position:absolute;top:-28px;left:0;right:0;display:inherit;margin:10px 20%;border-radius:30px}.upgrade .pro .billing{color:#fff;background-color:#4dbb7c}.upgrade .features.diff{-webkit-box-orient:vertical;-ms-flex-direction:column;flex-direction:column;margin-top:20px}.upgrade .features.diff,.upgrade .features .feature{-webkit-box-direction:normal;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.upgrade .features .feature{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-ms-flex-direction:row;flex-direction:row;-webkit-box-pack:unset;-ms-flex-pack:unset;justify-content:unset;margin:14px 20px 0;font-size:15px}.upgrade .features .feature.empty{height:27px}.upgrade .features .icon{margin-right:10px;width:15px}.upgrade .features .label{width:120px;font-size:15px;text-align:left}.upgrade .features a{color:inherit;cursor:auto}.upgrade .features a.active{padding-bottom:5px;border-bottom:1px dotted;-webkit-transition:all .5s cubic-bezier(.23,1,.32,1) 0ms;transition:all .5s cubic-bezier(.23,1,.32,1) 0ms;cursor:pointer}.upgrade .features a.active:hover{color:#4285f4}.upgrade .features .label .remark{margin-left:5px;padding:2px 5px;background-color:#ffeb3b;font-size:12px;font-weight:400;border-radius:4px}.upgrade .features .label .remark.roadmap{background-color:#e2e2e2}.upgrade .ticket{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;font-size:13px}.upgrade .ticket .message{width:70%;text-align:center}.upgrade .ticket .line{margin:7px 0 0;width:100%;height:1px;background-image:-webkit-linear-gradient(.1deg,rgba(255,18,18,0) -2.8%,#e2e2e2 50.8%,rgba(0,159,8,0) 107.9%);background-image:linear-gradient(89.9deg,rgba(255,18,18,0) -2.8%,#e2e2e2 50.8%,rgba(0,159,8,0) 107.9%)}.upgrade .ticket .notice{margin:20px 20%;padding:5px 20px;color:#333;background-color:#e2e2e2;font-size:15px;font-weight:400;border-radius:4px}.upgrade .ticket .content{margin:0 0 13px;width:80%}.upgrade .ticket .content li{margin-bottom:6px}.upgrade .ticket .content li:last-child{margin-bottom:0}.upgrade .ticket .last{display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-pack:distribute;justify-content:space-around;-webkit-box-align:center;-ms-flex-align:center;align-items:center;margin:20px 0;width:100%}.upgrade .carousels{margin:20px 20px 13px}.upgrade .carousel.carousel-slider{height:420px;border-radius:4px;box-shadow:0 12px 18px -6px rgba(0,0,0,.3)}.upgrade .carousels setion{position:relative}.upgrade .carousels setion img{margin-top:-82px;width:100%}.upgrade .carousels .descr{position:absolute;left:0;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;height:80px;width:100%;padding-bottom:10px;background-color:#fff;font-size:17px}.simpread-upgrade-root.mini dialog-gp{border-radius:15px!important}.simpread-upgrade-root.mini dialog-content{padding:0!important;width:650px!important}.simpread-upgrade-root.mini dialog-gp .close{position:absolute}.simpread-upgrade-root.mini .upgrade .carousels{margin:0}.simpread-upgrade-root.mini .upgrade .carousels .descr{padding-bottom:70px;height:130px}.simpread-upgrade-root.mini .upgrade .carousel.carousel-slider{height:450px}.simpread-upgrade-root.mini .floating .billing{margin-bottom:30px;min-height:40px}.simpread-upgrade-root.mini footer{position:absolute;top:199px;left:-60px;right:-60px;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;-webkit-box-align:center;-ms-flex-align:center;align-items:center}</style><style type=\"text/css\">:root{--sr-annote-color-0:#b4d9fb;--sr-annote-color-1:#ffeb3b;--sr-annote-color-2:#a2e9f2;--sr-annote-color-3:#a1e0ff;--sr-annote-color-4:#a8ea68;--sr-annote-color-5:#ffb7da}[sr-annote-bg-color]{color:inherit}[sr-annote-bg-color][data-color-type=\"0\"]{background-color:var(--sr-annote-color-0)}[sr-annote-bg-color][data-color-type=\"1\"]{background-color:var(--sr-annote-color-1)}[sr-annote-bg-color][data-color-type=\"2\"]{background-color:var(--sr-annote-color-2)}[sr-annote-bg-color][data-color-type=\"3\"]{background-color:var(--sr-annote-color-3)}[sr-annote-bg-color][data-color-type=\"4\"]{background-color:var(--sr-annote-color-4)}[sr-annote-bg-color][data-color-type=\"5\"]{background-color:var(--sr-annote-color-5)}[sr-annote-bb-color][data-color-type=\"1\"]{border-bottom-color:var(--sr-annote-color-1)}[sr-annote-bb-color][data-color-type=\"2\"]{border-bottom-color:var(--sr-annote-color-2)}[sr-annote-bb-color][data-color-type=\"3\"]{border-bottom-color:var(--sr-annote-color-3)}[sr-annote-bb-color][data-color-type=\"4\"]{border-bottom-color:var(--sr-annote-color-4)}[sr-annote-bb-color][data-color-type=\"5\"]{border-bottom-color:var(--sr-annote-color-5)}[sr-annote-bl-color][data-color-type=\"1\"]{border-left:5px solid var(--sr-annote-color-1)}[sr-annote-bl-color][data-color-type=\"2\"]{border-left:5px solid var(--sr-annote-color-2)}[sr-annote-bl-color][data-color-type=\"3\"]{border-left:5px solid var(--sr-annote-color-3)}[sr-annote-bl-color][data-color-type=\"4\"]{border-left:5px solid var(--sr-annote-color-4)}[sr-annote-bl-color][data-color-type=\"5\"]{border-left:5px solid var(--sr-annote-color-5)}[data-color-style=\"1\"]{background-color:transparent!important;background-repeat:no-repeat;background-size:100% 100%}[data-color-style=\"1\"][data-color-type=\"1\"]{background-color:transparent!important;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGAAAAAaCAMAAAB//6mtAAAAPFBMVEVHcEz/6zv/6zv/6zv/6zv/6zv/6zv/6zv/6zv/6zv/6zv/6zv/6zv/6zv/6zv/6zv/6zv/6zv/6zv/6zsmGdgxAAAAE3RSTlMAJJGnQ3Bh/ANS6An0u9o1FMp/ufrp4AAAAgZJREFUSMe1Vou2oyAM5BEIbxD+/183gFq99bZ7dnVOe6oWZwgJGRj7DcaY+et5BqtVFcA4eLraoLUFgJJLHzvBvuM00nPIzA4qazXkDuKka0VPIHNeiuee/RXO+qaAFjGFlpQQMi7JORcQ24Z+hRhcWpYYo5SikiQpkuBnldLXQwm5hANT53pxz9v1eZIdpEFCsmpLgXF+tVKGg640ZZrmDxacaNcIzqVE3EIoC7yYnjHuLxQ45cvquDP/RvgRiG4R2uYewlXKvXbo2r8x71NyFI4EZmZaj2vEShFJpnYHQopSAT/lgHEhZY2L+1/yVzAooJyyoEGEgLeEMFWC4K9NwHiVuFXlPSLNWbZ2g/61IuKN9AMyrwEUK5a7yQlLnUnwyr1v1luKCZsaq69kwwcCaC3ZWUZZPsFOkxaUA2pt8pHZE6KyhmnXbi3O1/xDSLkXqM8QH1FYwI+26nV1TwTQljy3ma8inSLYWiN+bp74ZX16hc4mnWkb7P0Jv9FejEB8+wsXlTfvLGIbthVvFN3jAbR0l34WUodzw6avJtIp1M6vya61EuTB1EqT3JW5jYfXiWw6Y626eOP7p5Th4FbrfqiZR4PxBjq5rv/BD8jplR12bcYBw44k4KCVtR9Q/Kszni/2W+MLh66n4WjLwz0LX5vqtDnqH4nitD8M/P10ZQ44q634A66hf2zVV84fAAAAAElFTkSuQmCC)}[data-color-style=\"1\"][data-color-type=\"2\"]{background-color:transparent!important;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGAAAAAaCAMAAAB//6mtAAAANlBMVEVHcEyA3uqA3uqA3uqA3uqA3uqA3uqA3uqA3uqA3uqA3uqA3uqA3uqA3uqA3uqA3uqA3uqA3urNaunbAAAAEXRSTlMAe+8uaFLb/AQCG0ANkralydBaCTIAAAIBSURBVEjHtVaJduMwCJRA6ED3///sIstn6qbd13hy+NlxRjBYDEp9h+B9WI6JMRIZnSnEyKQPGKKIjOwnwoD6CXLPuHmeeI5RkREi+dKEEeMKuRKFnJPAq98gvKyf0ORiXbc619KaBXDy7r2Pzwq5Ym0TlFqz5BMROb1fxi96CGeDlcPNz855wji1NddaSpOFcl5kMxHvFvEoKtdi4WDbONbjLdxMokgKelWMGZP/olpgGrKWGfA7yrdw0EQpRMlh1Py1CMnYDv/P7MZri8lJQiXTRnnmV4lzG2X9C9bMQVTTkf2FH6voWJvtf8Whb76Um41GDeD6J+BG9Ttk3B8irziXz5AftbFmrcI4JMrNfXqFGnd5su2fZR8omnhJQMOlNp+Ekaap0NT+CHu3cT5GWPsT/M6Np0haW30ketlu1VBQBvpD+gAAygZWjFgeWaFQSmMbJKPhiQL0FtWyzbzO9ss+d66/dQHnfrAJ+U0rv1qNdOn93ndi3dI692Jy86o1cWt0Sb/0QCh5uHw0ptp+xyktfwXAfRitac17l1hQYbbAYpBng+JYzxHZ4SQ1C0zyczwIw8GJ5siRx2ywNWNopPzJymaps4ljJghjIvKKlr0nBlXEcLXYLX8/nYR9lBJDphEvnWaZZQwKjNvZDE6hUFfCqyvNAevis/7A+s+rFf8Do0ByAiavdJkAAAAASUVORK5CYII=)}[data-color-style=\"1\"][data-color-type=\"3\"]{background-color:transparent!important;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGAAAAAaCAMAAAB//6mtAAAANlBMVEVHcEyF0faF0faF0faF0faF0faF0faF0faF0faF0faF0faF0faF0faF0faF0faF0faF0faF0fYOABKzAAAAEXRSTlMA3e8uaFJ7/AQCGz8Nt5KlzvXM15AAAAIFSURBVEjHtVaNesIgDARCCA0/gfd/2YXSWuvUbd/sfWqlpQdcIBdjXmGJcVmvmSEh+lBxSYkxHPCICRg4TiwD5idon9F5NiKnZNArkf4EhARpg95JSs5ZEc1vsDyMn8HXYqnbUKWUZp0j/fTex3eD3rG2tVaKSNX1JADO74eJqx7K2dzGQSdKbR8YTStVxgSabbWusvkEzwaJoCpLsY4mHU2unbK/AI1FjCUUCZtizJDjN9UWxiFrIdon2f8E2jUrqhSArmHE/HHXZG+7+yPzvW5zCGtLxT2u9/wmc20jrP/CDJnKVkLieOIHERlR6P8FTZ31Xz2Fm32A4Bz1T2AopnpXuG2iaLiWz5Afalm/RWFcMtZGnx5B0k2e2vqH6RUlIK8LCG6Pzcfhdfsb8NIvYe8W5zYC6VfwE41dpKlNLpm9HjfxuBjv+kX6OOdAD7BhgHLJCAVzHscg++CuCEBvyazHLIZqnyTH/tYFiH6wCX0WTNysRrP0re87sZ7SEj2Y3ETzaU90ORw5cAan1OHyyXux/RmnpvwNzj2fRmsh8C1LrJB1M6kj+cQzQTHKQUxKpjleqsLnOMuDZTg44iw5qtYGWhrMN1xDE+/qiRnqqtx5q4iiwfXskZt+q3bLr6uTm3ENQ8YxX7yrZdYyaGHYW3NyJim1IJxdaRZYp2InHtjePFvxF2YVcjg/uCn9AAAAAElFTkSuQmCC)}[data-color-style=\"1\"][data-color-type=\"4\"]{background-color:transparent!important;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGAAAAAaCAMAAAB//6mtAAAAMFBMVEWM2EJHcEyM2EKM2EKM2EKM2EKM2EKM2EKM2EKM2EKM2EKM2EKM2EKM2EKM2EKM2EKKz/w5AAAAEHRSTlP/AANRaHvzGy3WqT4Nk77kx+qVTgAAAb9JREFUSMe1VtlyxDAIwwYDvv//b4udY5Nus9NOE71kJocA4SDAXcEb5rWIZiQKFX3OguEFQswqKv6FNxr4iXd/0UvOzsgRRwTUrHmF3clGLsXg3QfAifjwoCjV1hl6qCm12HkAvoF7jzG21lKqVk9WlfK5gkUP42wb2RvpGTHVkYBl0GqdslHW8kMAr0jBMj3y8W9CjCJmCWFVTFTLQQvYxMaB9Lu0r6NxW5Qq+xmBozzUocM/YQWliltfj012RWpsEe4Ax2aSiT/2wKk1y7rQ4T5Uk+pVgVDQwHwbO3fgqnuTvZPa4FbYP0R+CTC0Knh3AEPKawVCNcL9aAFlBPCB4SkQmPpK6SH6jmVKpE8FGKfIRttT9JwIPdBj+jN3dR6cTb8GT0RpOLwIXKHQH6kgZjeHnQ/v/wDf8R8Htww7rxT/wMhXN88PIuWyGk4J3w9vqsPlM1GKVyN/Qe98Na1DkNXRhCbSTILtZC1j3MYHpuMnnc3dzYBrpc0R/XDw6YRjqalzNdheR+d3y5yvW6urcZd9I8I9meG3ZrdyvZ3sT4pYxJEv7rsMrItQ0X17WT7Jw5VQT7Tvy5U/4RRsxRfv9RDt9/UjOgAAAABJRU5ErkJggg==)}[data-color-style=\"1\"][data-color-type=\"5\"]{background-color:transparent!important;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGAAAAAaCAMAAAB//6mtAAAAOVBMVEVHcEz/t9r/t9r/t9r/t9r/t9r/t9r/t9r/t9r/t9r/t9r/t9r/t9r/t9r/t9r/t9r/t9r/t9r/t9r20adnAAAAEnRSTlMAVQ062Hwb/ARpK/Olk7XoScQnLSnCAAAB6ElEQVRIx7VW2Y7DIAzkMBCDOf//Y9cQmiZt2q12m1EegoTGF/ZYiFdYGONHWV+kRBOL0NryzwQalEV76+1yh/gAu4uLLVoURMkG0EivvZ6QsnRyqxgfse48XqE0xgSuZRMpVcg5MFw7wIUMALXWRBRnPOoXK5wPLZmz5snh5nfj3KGfgSJR6h7UGEfipNZnRhZf0FCCcOTaKNs53AiCQ0hkypox6716rgUnmwspU2d6y/geIdQeApsQzxVfhEJo+S/Mu5SFnCFFeSvrkd9G6GX9JzpBgJpMsccQPBEZqtD+bWHLb9yVexEWjTdPL/HvRkJuIfp7Ewgb03fI77nKeGvY3lgyVvdtC6RnABYjtO+ydyQj7QgAw6E23wT2/Hikdgl7g6LmC21X8DvXXxGPNrrEe263hNzSPf/X5CeEPNqA53O6xEKSalRAoclXFIArPLvMRDgZju+e7UFyXl1oRixTanhKb3ffJeuU1rlHkVsfKG6DTpmHGZhTl1cWICRoZ5xdiAdYqM/dgGqM3abEAIVV/xLqOcZtoTtxgBwAEkUG3hRx6QrO68VYOSLvBnzJTRekeNAzLnVkbnVbMIQcvedYn7restzaD7aTxVotu79lv8uMPUj57bDOV82qRNIfaM+Wq+WXresHSSV3cD8ocu8AAAAASUVORK5CYII=)}[data-color-style=\"2\"]{background-color:transparent!important}[data-color-style=\"2\"][data-color-type=\"1\"]{background-color:transparent!important;background-image:linear-gradient(180deg,hsla(0,0%,100%,0) 50%,var(--sr-annote-color-1) 0)}[data-color-style=\"2\"][data-color-type=\"2\"]{background-color:transparent!important;background-image:linear-gradient(180deg,hsla(0,0%,100%,0) 50%,var(--sr-annote-color-2) 0)}[data-color-style=\"2\"][data-color-type=\"3\"]{background-color:transparent!important;background-image:linear-gradient(180deg,hsla(0,0%,100%,0) 50%,var(--sr-annote-color-3) 0)}[data-color-style=\"2\"][data-color-type=\"4\"]{background-color:transparent!important;background-image:linear-gradient(180deg,hsla(0,0%,100%,0) 50%,var(--sr-annote-color-4) 0)}[data-color-style=\"2\"][data-color-type=\"5\"]{background-color:transparent!important;background-image:linear-gradient(180deg,hsla(0,0%,100%,0) 50%,var(--sr-annote-color-5) 0)}[data-color-style=\"3\"]{position:relative;background-color:transparent!important}[data-color-style=\"3\"]:after{content:\"\";position:absolute;left:0;bottom:25px;height:8px;width:58px;border-radius:4px;opacity:.8;transition:all .3s}[data-color-style=\"3\"][data-color-type=\"1\"]{background-color:transparent!important;background-image:linear-gradient(180deg,hsla(0,0%,100%,0) 85%,var(--sr-annote-color-1) 0)}[data-color-style=\"3\"][data-color-type=\"2\"]{background-color:transparent!important;background-image:linear-gradient(180deg,hsla(0,0%,100%,0) 85%,var(--sr-annote-color-2) 0)}[data-color-style=\"3\"][data-color-type=\"3\"]{background-color:transparent!important;background-image:linear-gradient(180deg,hsla(0,0%,100%,0) 85%,var(--sr-annote-color-3) 0)}[data-color-style=\"3\"][data-color-type=\"4\"]{background-color:transparent!important;background-image:linear-gradient(180deg,hsla(0,0%,100%,0) 85%,var(--sr-annote-color-4) 0)}[data-color-style=\"3\"][data-color-type=\"5\"]{background-color:transparent!important;background-image:linear-gradient(180deg,hsla(0,0%,100%,0) 85%,var(--sr-annote-color-5) 0)}sr-annote-note{position:relative;bottom:-5px;padding:0 4px;color:#fff;background-color:#333;font-weight:700;font-style:normal;font-family:arial,helvetica,clean,sans-serif;border-radius:5px;opacity:.8;cursor:pointer}sr-annote-note:after{content:\"N\"}pre.sr-annote+sr-annote-note{bottom:25px;right:25px}sr-annote-note:hover{opacity:1}sr-annote-note sr-annote-note-tip{position:absolute;left:0;top:0;padding:.5em 1em;max-width:400px;color:#fff;background:#101010;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Open Sans,Helvetica Neue,sans-serif;font-weight:400;font-style:normal;text-shadow:none;font-size:12px;text-indent:0;white-space:pre;z-index:10;border-radius:5px;box-shadow:0 0 10px rgba(0,0,0,.3);opacity:0;overflow:auto;pointer-events:none;z-index:20000;transition:all .18s ease-out .18s}sr-annote-note:hover sr-annote-note-tip{opacity:1;pointer-events:auto}sr-annote-note sr-annote-note-tip{overflow:hidden}sr-annote-note sr-annote-note-tip:hover{overflow:overlay}sr-annote-note sr-annote-note-tip::-webkit-scrollbar-track{background-color:transparent}sr-annote-note sr-annote-note-tip::-webkit-scrollbar{width:12px}sr-annote-note sr-annote-note-tip::-webkit-scrollbar-thumb{background-clip:padding-box;padding-top:80px;background-color:#ddd;border:3px solid transparent;border-radius:8px}</style><style type=\"text/css\">.sr-annote-hideall{background-color:transparent!important;pointer-events:none}sr-annote-trigger{position:fixed!important;bottom:52px;right:32px;display:-webkit-box!important;display:-ms-flexbox!important;display:flex!important;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;margin:0 0 15px;padding:0;width:40px!important;height:40px!important;line-height:40px!important;color:#fff;background-color:rgba(245,82,70,.8);border-radius:50%;cursor:pointer;box-shadow:0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12),0 3px 1px -2px rgba(0,0,0,.2);-webkit-transition:all .5s ease-in-out 0ms;transition:all .5s ease-in-out 0ms;overflow:visible!important;overflow:initial!important}sr-annote-trigger.open{right:95px}sr-annote-trigger.off{background-color:#bdbdbd}sr-annote-trigger sr-i{display:-webkit-box!important;display:-ms-flexbox!important;display:flex!important;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;width:100%;height:100%;border-radius:50%}sr-annote{padding:6px 0;background-color:transparent;font-size:inherit;cursor:pointer}.sr-annote[data-type=code],.sr-annote[data-type=img]{border-bottom-width:5px;border-bottom-style:solid}sr-annote[data-color-type=\"0\"]{padding:7px 0}sr-annote-floating{position:fixed;color:#fff;background:hsla(0,0%,6%,.95);font-weight:700;border-radius:5px;box-shadow:0 0 10px rgba(0,0,0,.3);opacity:0;-webkit-animation-delay:.2s;animation-delay:.2s;-webkit-animation-duration:.5s;animation-duration:.5s;-webkit-animation-fill-mode:both;animation-fill-mode:both;-webkit-animation-name:sr-annote-slideInUp;animation-name:sr-annote-slideInUp;z-index:2000}sr-annote-floating.fast{-webkit-animation-duration:.2s!important;animation-duration:.2s!important}sr-annote-floating.effect{-webkit-transition:all 1s cubic-bezier(.23,1,.32,1) .1s;transition:all 1s cubic-bezier(.23,1,.32,1) .1s}sr-annote-floating.hidden{-webkit-animation-duration:.5s;animation-duration:.5s;-webkit-animation-fill-mode:both;animation-fill-mode:both;-webkit-animation-name:sr-annote-slideInDown;animation-name:sr-annote-slideInDown;pointer-events:none}.sr-annote-floatingbar-hiden{display:none}@-webkit-keyframes sr-annote-slideInUp{0%{opacity:0;-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0);visibility:visible}to{opacity:1;-webkit-transform:translateZ(0);transform:translateZ(0)}}@keyframes sr-annote-slideInUp{0%{opacity:0;-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0);visibility:visible}to{opacity:1;-webkit-transform:translateZ(0);transform:translateZ(0)}}@-webkit-keyframes sr-annote-slideInDown{0%{opacity:1;visibility:visible}to{opacity:0;-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)}}@keyframes sr-annote-slideInDown{0%{opacity:1;visibility:visible}to{opacity:0;-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)}}sr-annote-floatingbar{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;margin:5px}sr-annote-floatingbar,sr-annote-floatingbar sr-anote-fb-item{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center}sr-annote-floatingbar sr-anote-fb-item{margin-right:5px;cursor:pointer}sr-annote-floatingbar sr-anote-fb-item:last-child{margin-right:0}sr-annote-floatingbar sr-anote-fb-item{width:20px;height:20px;border-radius:50%;box-sizing:border-box}sr-annote-floatingbar sr-anote-fb-item[type=copy],sr-annote-floatingbar sr-anote-fb-item[type=export],sr-annote-floatingbar sr-anote-fb-item[type=link],sr-annote-floatingbar sr-anote-fb-item[type=note],sr-annote-floatingbar sr-anote-fb-item[type=remove],sr-annote-floatingbar sr-anote-fb-item[type=style],sr-annote-floatingbar sr-anote-fb-item[type=tag]{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;background-color:#fff;border-radius:50%}sr-annote-floatingbar sr-anote-fb-item[type=style]{background-color:#f73859!important}sr-annote-floatingbar sr-anote-fb-item[type=export]{background-color:#cc0e74}sr-annote-floatingbar sr-anote-fb-item[type=copy]{background-color:#a78674}sr-annote-floatingbar sr-anote-fb-item[type=link]{background-color:#7f39fb}sr-annote-floatingbar sr-anote-fb-item[type=remove]{background-color:#f44336}sr-annote-floatingbar sr-anote-fb-item[remove=confirm]{-webkit-transform:rotate(270deg);transform:rotate(270deg);-webkit-transition:all .2s ease-in-out 0s;transition:all .2s ease-in-out 0s}sr-annote-sidebar-bg{position:fixed;top:0;right:0;bottom:180px;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;width:260px;font-size:22.4px;font-size:1.4rem;font-weight:500;opacity:0;-webkit-transform:translateX(256px);transform:translateX(256px);-webkit-transition:all .45s cubic-bezier(.23,1,.32,1) 0ms;transition:all .45s cubic-bezier(.23,1,.32,1) 0ms}sr-annote-sidebar-bg.mini{pointer-events:none}sr-annote-sidebar-bg:hover{z-index:2147483647}sr-annote-sidebar-bg.open{opacity:1;-webkit-transform:translateX(0);transform:translateX(0)}sr-annote-sidebar{margin:3px 4px 0;padding-left:20px;height:100%;overflow-x:hidden}sr-annote-sidebar.mini{pointer-events:none}sr-annote-sidebar *{color:rgba(0,0,0,.6);word-break:break-all;box-sizing:border-box}sr-annote-sidebar{overflow-y:hidden}sr-annote-sidebar:hover{overflow-y:overlay}sr-annote-sidebar::-webkit-scrollbar-track{background-color:transparent}sr-annote-sidebar::-webkit-scrollbar{width:12px}sr-annote-sidebar::-webkit-scrollbar-thumb{padding-top:80px;background-clip:padding-box;background-color:#ddd;border:3px solid transparent;border-radius:8px;border-radius:10px;border:6px solid transparent;background-color:rgba(85,85,85,.55)}sr-annote-sidebar::-webkit-scrollbar{width:0;-webkit-transition:width .7s cubic-bezier(.4,0,.2,1);transition:width .7s cubic-bezier(.4,0,.2,1)}sr-annote-sidebar:hover::-webkit-scrollbar{width:16px}sr-annote-sidebar[srcoll=on]:hover sr-annote-sidebar-card[type=option].mini{-webkit-transform:translateX(-16px);transform:translateX(-16px)}sr-annote-sidebar[srcoll=on]:hover sr-annote-sidebar-card.off{-webkit-transform:translateX(190px);transform:translateX(190px)}sr-annote-sidebar-cards{display:block}sr-annote-sidebar-card{position:relative;display:block!important;margin:12px 7px 12px 12px;color:rgba(51,51,51,.87);background-color:#fff;border-radius:4px;box-shadow:0 2px 5px rgba(0,0,0,.08);-webkit-transition:all .45s cubic-bezier(.23,1,.32,1) 0ms;transition:all .45s cubic-bezier(.23,1,.32,1) 0ms;pointer-events:auto}sr-annote-sidebar-card:hover{box-shadow:0 10px 20px 0 rgba(168,182,191,.6);-webkit-transform:translateY(-1px);transform:translateY(-1px)}sr-annote-sidebar-card:last-child{margin-bottom:30px}sr-annote-sidebar-card.off{display:block;-webkit-transform:translateX(210px);transform:translateX(210px);-webkit-transition:all .25s ease-out;transition:all .25s ease-out}sr-annote-sidebar-card.off:hover{-webkit-transform:translateX(120px)!important;transform:translateX(120px)!important}sr-annote-sidebar-card.hide{display:block;-webkit-transform:translateX(256px);transform:translateX(256px);-webkit-transition:all .25s ease-out;transition:all .25s ease-out}sr-annote-sidebar-card-anchor{position:absolute;left:0;top:0;width:90%;height:100%}sr-annote-sidebar-card-action{position:absolute;top:10px;right:3px;display:block;width:12px;height:12px;line-height:12px;-webkit-transition:all .25s ease-out;transition:all .25s ease-out;cursor:pointer;z-index:20000}sr-annote-sidebar-card-action.open{-webkit-transform:rotate(90deg);transform:rotate(90deg)}sr-annote-sidebar-card[mode=mini]{overflow:hidden}sr-annote-sidebar-card[mode=mini] sr-annote-sidebar-preview{display:block;padding:6px 12px 5px 10px;height:32px;font-size:13px;font-weight:400;white-space:nowrap;text-align:left;text-overflow:ellipsis;-webkit-transition:all .25s ease-out;transition:all .25s ease-out;overflow:hidden}sr-annote-sidebar-card[mode=mini][type=img] sr-annote-sidebar-preview{text-align:center}sr-annote-sidebar-card[mode=mini] sr-annote-sidebar-detail{padding:0 15px;height:0}sr-annote-sidebar-card[mode=mini] sr-annote-sidebar-note,sr-annote-sidebar-card[mode=mini] sr-annote-sidebar-toolbars{display:none}sr-annote-sidebar-card[mode=mini] pre{margin:0!important;padding:0!important;white-space:nowrap;text-overflow:ellipsis;overflow:hidden}sr-annote-sidebar-card[mode=normal] sr-annote-sidebar-preview{display:none}sr-annote-sidebar-card[mode=normal] sr-annote-sidebar-detail{margin-bottom:15px;padding:15px 15px 0;height:auto;overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical}sr-annote-sidebar-card[data-color-type=\"0\"]{display:none!important}sr-annote-sidebar-card pre{margin:0!important;padding:0!important;background-color:transparent!important;max-height:200px;font-size:10px;overflow:hidden}sr-annote-sidebar-card input,sr-annote-sidebar-card textarea{font-size:12px!important}sr-annote-sidebar-card img{margin:0;padding:0;max-height:100px;max-width:80%;background:#fff;border:0;border-radius:6px}sr-annote-sidebar-detail{display:block;padding:15px;width:100%;color:#fff;font-size:10px;text-align:justify;border-top-left-radius:4px;border-top-right-radius:4px;-webkit-transition:all .25s ease-out;transition:all .25s ease-out}sr-annote-sidebar-card[type=img] sr-annote-sidebar-detail{text-align:center}sr-annote-sidebar-tags{-ms-flex-wrap:wrap;flex-wrap:wrap;margin-top:15px}sr-annote-sidebar-tag,sr-annote-sidebar-tags{display:-webkit-box;display:-ms-flexbox;display:flex}sr-annote-sidebar-tag{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:end;-ms-flex-pack:end;justify-content:flex-end;padding:4px 12px 12px 0;height:22px;font-size:14px;font-size:.875rem;font-weight:700;white-space:nowrap;border-radius:16px;outline:none;cursor:pointer;overflow:hidden;-webkit-transition:all .2s ease-in-out 0s;transition:all .2s ease-in-out 0s}sr-annote-sidebar-note{display:block;padding:15px 15px 0;width:100%;background-color:#fff;text-align:justify;font-size:13px;overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-line-clamp:4;-webkit-box-orient:vertical}sr-annote-sidebar-toolbars{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;-webkit-box-pack:end;-ms-flex-pack:end;justify-content:flex-end;padding-right:10px;height:32px;background-color:#fff;border-bottom-left-radius:4px;border-bottom-right-radius:4px}sr-annote-sidebar-toolbar,sr-annote-sidebar-toolbars{-webkit-box-align:center;-ms-flex-align:center;align-items:center}sr-annote-sidebar-toolbar{display:-webkit-box!important;display:-ms-flexbox!important;display:flex!important;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;margin-left:5px;width:20px;height:20px;line-height:20px;border-radius:50%}sr-annote-sidebar-toolbar[remove=confirm]{-webkit-transform:rotate(270deg);transform:rotate(270deg);-webkit-transition:all .2s ease-in-out 0s;transition:all .2s ease-in-out 0s}sr-annote-sidebar-toolbar[remove=confirm] svg path{fill:#f44336}sr-annote-sidebar-card[type=unread]{background-color:#1fab89}sr-annote-sidebar-card[type=unread] sr-annote-sidebar-detail.title{padding:0;text-align:left;font-size:14px}sr-annote-sidebar-card[type=unread] sr-annote-sidebar-detail.desc{padding:0 0 0 10px;overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-line-clamp:4;-webkit-box-orient:vertical;border-left:1px outset #fff;border-top-left-radius:0;border-top-right-radius:0}sr-annote-sidebar-card[type=unread] sr-annote-sidebar-tag{color:#fff}sr-annote-sidebar-card[type=unread][mode=mini] sr-annote-sidebar-preview{color:#fff;font-size:13px}sr-annote-sidebar-card[type=option]{height:32px;background-color:transparent;box-shadow:none;overflow:visible;overflow:initial}sr-annote-sidebar-card[type=option]:hover{-webkit-transform:translateY(0);transform:translateY(0)}sr-annote-sidebar-card[type=option].mini{margin-right:0}sr-annote-sidebar-card[type=option] sr-annote-sidebar-card-action,sr-annote-sidebar-card[type=option] sr-annote-sidebar-card-anchor{display:none}sr-annote-sidebar-card[type=option] sr-annote-sidebar-preview{background-color:transparent}sr-annote-sidebar-options{position:absolute;top:0;right:0;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;padding-right:5px;height:100%;background-color:#3a3a3a;border-left:10px outset #222;border-radius:4px}sr-annote-sidebar-option,sr-annote-sidebar-options{-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center}sr-annote-sidebar-option{display:-webkit-box!important;display:-ms-flexbox!important;display:flex!important;margin-left:5px;width:20px;height:20px;border-radius:50%;-webkit-transition:all .25s ease-out;transition:all .25s ease-out;cursor:pointer}sr-annote-sidebar-option[off=true],sr-annote-sidebar-option[side=false]:not(:first-child){width:0;margin-left:0}sr-annote-sidebar-option[off=true]~sr-annote-sidebar-option:last-child svg{-webkit-transform:rotate(180deg);transform:rotate(180deg)}sr-annote-sidebar-option[type=drag][state=on] svg path,sr-annote-sidebar-option[type=goon][state=on] svg path,sr-annote-sidebar-option[type=save][state=on] svg path{fill:#1fab89}sr-annote-sidebar-option[type=collapse][state=on] svg{-webkit-transform:rotate(90deg);transform:rotate(90deg)}sr-annote-sidebar-option[lock=true] svg path{fill:#f55246!important}sr-annote-sidebar-card[type=option]:hover~sr-annote-sidebar-card[type=unread]{z-index:-1}</style><style type=\"text/css\">@-webkit-keyframes fadeInUp{0%{opacity:0;-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)}to{opacity:1;-webkit-transform:translateZ(0);transform:translateZ(0)}}@keyframes fadeInUp{0%{opacity:0;-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)}to{opacity:1;-webkit-transform:translateZ(0);transform:translateZ(0)}}@-webkit-keyframes fadeOutDown{0%{opacity:1;-webkit-transform:translateZ(0);transform:translateZ(0)}to{opacity:0;-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)}}@keyframes fadeOutDown{0%{opacity:1;-webkit-transform:translateZ(0);transform:translateZ(0)}to{opacity:0;-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)}}.sr-alertgp{position:fixed;left:0;top:0;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;width:100%;height:100%;background-color:rgba(51,51,51,.8);z-index:2147483647}.sr-alertgp .alert{min-width:400px;min-height:400px;width:650px;background-color:#fff;border-radius:4px;box-shadow:0 14px 45px rgba(0,0,0,.247059);-webkit-animation-name:fadeInUp;animation-name:fadeInUp;-webkit-animation-duration:.8s;animation-duration:.8s;-webkit-animation-fill-mode:both;animation-fill-mode:both}.sr-alertgp .alert,.sr-alertgp .alert .loading{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.sr-alertgp .alert .loading{position:relative;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;width:100%;height:35%;background-color:transparent}.alert .loading .progress{display:block;margin:10px auto;max-width:80%;max-height:250px;width:20%}.alert .loading .progress .percentage{fill:#666;font-family:sans-serif;font-size:.5em;text-anchor:middle}.alert .loading .progress .circle-bg{fill:none;stroke:#eee;stroke-width:3.8}.alert .loading .progress .circle{fill:none;stroke-width:2.8;stroke-linecap:round;stroke:#1dba90;-webkit-transition:all .2s ease-in-out;transition:all .2s ease-in-out}@-webkit-keyframes scaleAnimation{0%{opacity:0;-webkit-transform:scale(1.5);transform:scale(1.5)}to{opacity:1;-webkit-transform:scale(1);transform:scale(1)}}@keyframes scaleAnimation{0%{opacity:0;-webkit-transform:scale(1.5);transform:scale(1.5)}to{opacity:1;-webkit-transform:scale(1);transform:scale(1)}}@-webkit-keyframes fadeOut{0%{opacity:1}to{opacity:0}}@keyframes fadeOut{0%{opacity:1}to{opacity:0}}@-webkit-keyframes fadeIn{0%{opacity:0}to{opacity:1}}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}.sr-alertgp .alert .close{position:absolute;top:0;right:0;z-index:2}.sr-alertgp .alert .close:hover{-webkit-transform:rotate(270deg);transform:rotate(270deg);-webkit-transition:all .25s ease-out;transition:all .25s ease-out}.sr-alertgp .alert .sr-alert-icon img{max-width:650px;width:100%;-webkit-transform:scale(.7);transform:scale(.7);-webkit-transition:all .5s ease-out;transition:all .5s ease-out}.sr-alertgp .alert .actionbar{position:relative;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;-webkit-box-align:center;-ms-flex-align:center;align-items:center;margin:20px;width:80%;height:50px;-webkit-transition:all 1s cubic-bezier(.23,1,.32,1) 0ms;transition:all 1s cubic-bezier(.23,1,.32,1) 0ms}.sr-alertgp .alert .actionbar,.sr-alertgp .alert.notification{-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.sr-alertgp .alert.notification{width:500px;min-height:350px}.sr-alertgp .alert.notification[data-state=siren]{background-image:-webkit-radial-gradient(circle farthest-corner at 10% 20%,#cd212a 0,#ec5f05 90%);background-image:radial-gradient(circle farthest-corner at 10% 20%,#cd212a 0,#ec5f05 90%)}.sr-alertgp .alert.notification[data-state=lock]{background-image:-webkit-radial-gradient(circle farthest-corner at 10% 20%,#8451a1 0,rgba(132,81,161,.83) 90%);background-image:radial-gradient(circle farthest-corner at 10% 20%,#8451a1 0,rgba(132,81,161,.83) 90%)}.sr-alertgp .alert.notification[data-state=warning]{background-image:-webkit-linear-gradient(left,#f2709c,#ff9472);background-image:linear-gradient(90deg,#f2709c,#ff9472)}.sr-alertgp .alert.notification[data-state=bug]{background-image:-webkit-linear-gradient(bottom,#ad5389,#3c1053);background-image:linear-gradient(0deg,#ad5389,#3c1053)}.sr-alertgp .alert.notification[data-state=safe],.sr-alertgp .alert.notification[data-state=server]{background-color:#8ec5fc;background-image:-webkit-linear-gradient(28deg,#8ec5fc,#e0c3fc);background-image:linear-gradient(62deg,#8ec5fc,#e0c3fc)}.sr-alertgp .alert.notification .sr-alert-icon{position:relative;width:100%}.sr-alertgp .alert.notification .loading .progress{padding:5px;background-color:#fff;border-radius:50%}.sr-alertgp .alert.notification .loading .progress .circle-bg{stroke:transparent}.sr-alertgp .alert.notification .loading .progress .circle{stroke-width:1}.sr-alertgp .alert.notification[data-state=siren] .loading .progress .circle{stroke:#cd212a}.sr-alertgp .alert.notification[data-state=lock] .loading .progress .circle{stroke:#8451a1}.sr-alertgp .alert.notification[data-state=warning] .loading .progress .circle{stroke:#f2709c}.sr-alertgp .alert.notification[data-state=bug] .loading .progress .circle{stroke:#ad5389}.sr-alertgp .alert.notification[data-state=safe] .loading .progress .circle,.sr-alertgp .alert.notification[data-state=server] .loading .progress .circle{stroke:#8ec5fc}.sr-alertgp .alert.notification .content{padding:10px 70px;width:100%;color:#fff;text-align:center;font-size:28.8px;font-size:1.8rem;box-sizing:border-box}.sr-alertgp .alert.notification .flag{position:absolute;left:0;top:0;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;width:100%;height:100%;z-index:100000}.sr-alertgp .alert.notification .flag img{width:50px}.sr-alertgp .alert.notification[data-state=lock] .flag img{width:30px}.sr-alertgp .alert.notification .flag img.swing{-webkit-transform-origin:center;transform-origin:center;-webkit-animation-name:swing;animation-name:swing;-webkit-animation-duration:1s;animation-duration:1s}.sr-alertgp .alert.notification .actionbar{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;-ms-flex-pack:distribute;justify-content:space-around;margin:0}.sr-alertgp .alert.notification .return{padding:5px 32px;color:#333;background-color:#fff;font-size:15px;font-weight:700;border-radius:56px}.sr-alertgp .alert.notification .return:hover{box-shadow:0 3px 1px -2px rgba(0,0,0,.2),0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12)}@-webkit-keyframes swing{20%{-webkit-transform:rotate(15deg);transform:rotate(15deg)}40%{-webkit-transform:rotate(-10deg);transform:rotate(-10deg)}60%{-webkit-transform:rotate(5deg);transform:rotate(5deg)}80%{-webkit-transform:rotate(-5deg);transform:rotate(-5deg)}to{-webkit-transform:rotate(0deg);transform:rotate(0deg)}}@keyframes swing{20%{-webkit-transform:rotate(15deg);transform:rotate(15deg)}40%{-webkit-transform:rotate(-10deg);transform:rotate(-10deg)}60%{-webkit-transform:rotate(5deg);transform:rotate(5deg)}80%{-webkit-transform:rotate(-5deg);transform:rotate(-5deg)}to{-webkit-transform:rotate(0deg);transform:rotate(0deg)}}</style><style type=\"text/css\">@-webkit-keyframes fadeInUp{0%{opacity:0;-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)}to{opacity:1;-webkit-transform:translateZ(0);transform:translateZ(0)}}@keyframes fadeInUp{0%{opacity:0;-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)}to{opacity:1;-webkit-transform:translateZ(0);transform:translateZ(0)}}@-webkit-keyframes fadeOutDown{0%{opacity:1;-webkit-transform:translateZ(0);transform:translateZ(0)}to{opacity:0;-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)}}@keyframes fadeOutDown{0%{opacity:1;-webkit-transform:translateZ(0);transform:translateZ(0)}to{opacity:0;-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)}}sr-promo-bg{position:fixed;right:15px;bottom:15px;z-index:2147483646}sr-promo,sr-promo-notice{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:start;-ms-flex-align:start;align-items:flex-start;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;padding:10px;color:rgba(51,51,51,.87);background-color:#fff;border-radius:4px;box-shadow:0 12px 18px -6px rgba(0,0,0,.3);overflow:hidden;-webkit-transform-origin:bottom;transform-origin:bottom;-webkit-transition:all .6s ease;transition:all .6s ease;-webkit-animation-name:fadeInUp;animation-name:fadeInUp;-webkit-animation-duration:.5s;animation-duration:.5s;-webkit-animation-fill-mode:both;animation-fill-mode:both}sr-promo img{width:220px;cursor:pointer}sr-promo-footer{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;padding:10px 0 0;width:100%}sr-promo-a{padding:5px 10px;color:#fff;font-size:12px;font-weight:700;border-radius:2px;box-shadow:0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12),0 3px 1px -2px rgba(0,0,0,.2);cursor:pointer}sr-promo-a.later{background-color:#2196f3}sr-promo-a.cancel{background-color:#757575}sr-promo-tip{font-size:12px;padding:5px 10px;border-radius:2px}sr-promo-notice{position:absolute;top:10px;left:10px;right:10px;height:293px;padding-bottom:0;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start;font-size:12px;border-radius:0;box-shadow:none;overflow-y:hidden}sr-promo-notice:hover{overflow-y:overlay}sr-promo-notice-title{font-weight:700;text-align:center;margin-bottom:5px;width:100%;font-size:14px}sr-promo-notice-content{margin-top:5px}</style><style type=\"text/css\">.simpread-tipsalert-root dialog-gp{position:absolute}.simpread-tipsalert-root dialog-gp .close{position:absolute;top:0;right:0;z-index:2}.simpread-tipsalert-root dialog-content{padding:0!important;width:650px!important}.simpread-tipsalert-root .details{position:relative;color:rgba(51,51,51,.87);font-family:Hiragino Sans GB,Microsoft Yahei;text-shadow:none}.simpread-tipsalert-root .details .carousel.carousel-slider{height:450px;border-radius:4px;box-shadow:0 12px 18px -6px rgba(0,0,0,.3)}.simpread-tipsalert-root .details .carousels setion sr-div-center{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;height:321px;box-sizing:border-box}.simpread-tipsalert-root .details .carousels setion sr-div-center.hidden{display:none}.simpread-tipsalert-root .details .carousels setion sr-div-center img{margin-top:20px!important;height:321px;width:auto!important}.simpread-tipsalert-root .details .carousels setion sr-div-center sr-video{position:absolute;left:0;right:0;width:100%;height:321px;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;z-index:2}.simpread-tipsalert-root .details .carousels setion .tipsimg.error{width:0!important;height:0!important}.simpread-tipsalert-root .details .carousels setion .tipsimg:after{content:\"\\F1C5\" \" Sorry, the image below is broken :(\";font-family:Font Awesome\\ 5 Free;font-weight:900;position:absolute;top:0;left:0;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;width:650px;height:100%;color:#646464;background-color:#fff;z-index:2}.simpread-tipsalert-root .details .carousels setion sr-div-center sr-video+img{opacity:.5}.simpread-tipsalert-root .details .carousels setion img{margin-top:-82px;width:100%}.simpread-tipsalert-root .details .carousels setion video{height:321px}.simpread-tipsalert-root .details .carousels setion video.active{display:block}.simpread-tipsalert-root .details .carousels .descr{position:absolute;left:0;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;height:80px;width:100%;padding-bottom:10px;background-color:#fff;font-size:14px}.simpread-tipsalert-root .details .carousels .descr b{margin:0 3px}.simpread-tipsalert-root .details .carousels .descr.large{font-size:17px}.simpread-tipsalert-root .floating{position:absolute;left:0;right:0;bottom:14px;height:80px;overflow-y:hidden}.simpread-tipsalert-root .floating,.simpread-tipsalert-root .floating .docs{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.simpread-tipsalert-root .floating .docs{-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;padding:1px 40px;height:30px;color:#fff;background-color:#4dbb7c;font-size:15px;font-weight:400;border-radius:30px;-webkit-transition:all .5s cubic-bezier(.23,1,.32,1) 0ms;transition:all .5s cubic-bezier(.23,1,.32,1) 0ms;box-shadow:0 12px 18px -6px rgba(0,0,0,.3)}.simpread-tipsalert-root footer{position:absolute;top:199px;left:-60px;right:-60px;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;-webkit-box-align:center;-ms-flex-align:center;align-items:center}</style><style type=\"text/css\">[customscroll],[data-customscroll]{overflow-y:hidden}[customscroll]:hover,[data-customscroll]:hover{overflow-y:overlay}[customscroll]::-webkit-scrollbar-track,[data-customscroll]::-webkit-scrollbar-track{background-color:transparent}[customscroll]::-webkit-scrollbar,[data-customscroll]::-webkit-scrollbar{width:12px}[customscroll]::-webkit-scrollbar-thumb,[data-customscroll]::-webkit-scrollbar-thumb{background-clip:padding-box;padding-top:80px;background-color:#ddd;border:3px solid transparent;border-radius:8px}sr-annote-popup{display:block;padding-right:20px;width:480px;height:100%}sr-annote-popup-gp{position:relative;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;margin-bottom:25px}sr-annote-popup-div{width:100%;color:var(--text-color);text-align:left;font-weight:400}sr-annote-popup-label{color:rgba(0,137,123,.8);font-size:14px;font-weight:700;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;pointer-events:none;-webkit-transition:all .45s cubic-bezier(.23,1,.32,1) 0ms;transition:all .45s cubic-bezier(.23,1,.32,1) 0ms;-webkit-transform:scale(.75) translateY(-8px);transform:scale(.75) translateY(-8px);-webkit-transform-origin:left top 0;transform-origin:left top 0}sr-annote-popup-desc{position:relative;margin:10px 0;padding:10px;background-color:var(--background-hover-color);font-size:15px;text-align:justify;font-weight:400;line-height:1.6;border-radius:4px}sr-annote-popup-desc sr-annote{background-color:transparent!important}sr-annote-popup-gp[type=img]{-webkit-box-align:center;-ms-flex-align:center;align-items:center}sr-annote-popup-gp svg path{fill:var(--active-color)!important}sr-annote-popup-gp ol,sr-annote-popup-gp ul{margin:0 0 1.2em;margin-left:1.3em;padding:0;list-style:disc}sr-annote-popup-gp ol li,sr-annote-popup-gp ul li{margin:0 0 1.2em;font-size:15px;list-style:disc}sr-annote-popup-gp a{padding:0 5px;vertical-align:baseline;vertical-align:initial}sr-annote-popup-gp a,sr-annote-popup-gp a:link{color:#463f5c;font-size:inherit;font-weight:inherit;text-decoration:underline;border:none}sr-annote-popup-gp a:hover{background:transparent}sr-annote-popup-gp img{margin:0;padding:0;max-width:50%;height:auto;background:#fff;border:0;border-radius:6px;box-shadow:0 20px 20px -10px rgba(0,0,0,.1)}sr-annote-popup-gp pre{padding:10px!important;background-color:transparent!important;max-height:400px;white-space:pre-line;word-break:break-all;border-radius:6px!important;overflow-x:hidden!important}sr-annote-popup auto-complete list-view{max-height:150px!important}sr-annote-popup list-view::-webkit-scrollbar-thumb{background-clip:padding-box;border-radius:10px;border:2px solid transparent;background-color:rgba(85,85,85,.55)}sr-annote-popup list-view::-webkit-scrollbar{width:10px;-webkit-transition:width .7s cubic-bezier(.4,0,.2,1);transition:width .7s cubic-bezier(.4,0,.2,1)}sr-annote-popup-gp sr-annote-floatingbar{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between}sr-annote-popup-gp sr-annote-floatingbar sr-anote-fb-item[data-color-type]{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;margin-right:20px;width:65px;height:20px;border-radius:5px}sr-annote-popup-gp sr-annote-floatingbar sr-anote-fb-item:last-child{margin-right:0}sr-annote-popup-gp sr-anote-item{display:-webkit-box!important;display:-ms-flexbox!important;display:flex!important;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;margin-right:20px;padding:0 5px;width:65px;height:20px;font-size:13px;font-weight:400;border-radius:5px;-webkit-transition:all .5s ease-in-out 0ms;transition:all .5s ease-in-out 0ms}sr-annote-popup-gp sr-anote-item:last-child{margin-right:0}sr-annote-popup-gp sr-anote-item.hidden{opacity:0;pointer-events:none}sr-annote-popup-gp sr-anote-lock{position:absolute;left:0;top:-3px;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;width:100%;height:100%;background-color:hsla(0,0%,100%,.8);z-index:1}sr-annote-popup-gp sr-anote-lock svg{margin-top:3px;cursor:pointer}</style><style type=\"text/css\">.gu-mirror{position:fixed!important;margin:0!important;z-index:9999!important;opacity:.8;-ms-filter:\"progid:DXImageTransform.Microsoft.Alpha(Opacity=80)\";filter:alpha(opacity=80)}.gu-hide{display:none!important}.gu-unselectable{-webkit-user-select:none!important;-moz-user-select:none!important;-ms-user-select:none!important;user-select:none!important}.gu-transit{opacity:.2;-ms-filter:\"progid:DXImageTransform.Microsoft.Alpha(Opacity=20)\";filter:alpha(opacity=20)}</style><style type=\"text/css\">sr-search{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;margin-bottom:10px;padding:10px;font-size:13px;border:1px solid #dfe1e5;border-radius:8px}sr-search.floating{position:fixed;top:170px;right:10px;width:400px;max-height:500px;z-index:2000}sr-search.bing{margin-left:-20px;margin-right:-20px}sr-search sr-search-header{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;-webkit-box-align:center;-ms-flex-align:center;align-items:center;margin-bottom:10px}sr-search sr-search-header img{margin-right:10px;width:22px}sr-search sr-search-header sr-search-span{font-weight:700}sr-search-unreader-group{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;border-bottom:1px solid #dfe1e5;margin-bottom:5px}sr-search-content{max-height:666px;overflow-x:hidden;overflow-y:auto}sr-search-unreader-title{font-weight:700;font-size:15px;margin-bottom:5px}sr-search-unreader-create{margin-bottom:5px;color:#70757a}sr-search-unreader-desc{margin-bottom:5px}sr-search-unreader-tags{margin-bottom:5px;color:#70757a;font-size:11px;font-style:italic}sr-search-unreader-tag{margin-right:5px}sr-search-paging{width:100%;margin:10px}sr-search-paging,sr-search-paging sr-search-more{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center}sr-search-paging sr-search-more{width:36px;height:36px;box-shadow:0 0 0 1px rgba(0,0,0,.04),0 4px 8px 0 rgba(0,0,0,.2);border-radius:50%;opacity:.9;cursor:pointer}sr-search-paging sr-search-more:hover{opacity:1}sr-search-paging sr-search-more.disable{cursor:no-drop}sr-search-paging sr-search-more svg{width:24px;height:24px;fill:#757575}sr-search-info{text-align:center}</style></head><body class=\"wiz-editor-body wiz-readonly markdown-body\" spellcheck=\"false\" contenteditable=\"false\" style=\"opacity: 1;\"><p>隐私政策 <br>\n本应用尊重并保护所有使用服务用户的个人隐私权。为了给您提供更准确、更有个性化的服务，本应用会按照本隐私权政策的规定使用和披露您的个人信息。但本应用将以高度的勤勉、审慎义务对待这些信息。除本隐私权政策另有规定外，在未征得您事先许可的情况下，本应用不会将这些信息对外披露或向第三方提供。本应用会不时更新本隐私权政策。 您在同意本应用服务使用协议之时，即视为您已经同意本隐私权政策全部内容。本隐私权政策属于本应用服务使用协议不可分割的一部分。</p>\n\n<ol class=\"wiz-list-level1\">\n<li>适用范围 <br>\n(a) 在您注册本应用帐号时，您根据本应用要求提供的个人注册信息； <br>\n(b) 在您使用本应用网络服务，或访问本应用平台网页时，本应用自动接收并记录的您的浏览器和计算机上的信息，包括但不限于您的 IP 地址、浏览器的类型、使用的语言、访问日期和时间、软硬件特征信息及您需求的网页记录等数据； <br>\n(c) 本应用通过合法途径从商业伙伴处取得的用户个人数据。 <br>\n您了解并同意，以下信息不适用本隐私权政策： <br>\n(a) 您在使用本应用平台提供的搜索服务时输入的关键字信息； <br>\n(b) 本应用收集到的您在本应用发布的有关信息数据，包括但不限于参与活动、成交信息及评价详情； <br>\n(c) 违反法律规定或违反本应用规则行为及本应用已对您采取的措施。</li>\n<li>信息使用 <br>\n(a) 本应用不会向任何无关第三方提供、出售、出租、分享或交易您的个人信息，除非事先得到您的许可，或该第三方和本应用（含本应用关联公司）单独或共同为您提供服务，且在该服务结束后，其将被禁止访问包括其以前能够访问的所有这些资料。 <br>\n(b) 本应用亦不允许任何第三方以任何手段收集、编辑、出售或者无偿传播您的个人信息。任何本应用平台用户如从事上述活动，一经发现，本应用有权立即终止与该用户的服务协议。 <br>\n(c) 为服务用户的目的，本应用可能通过使用您的个人信息，向您提供您感兴趣的信息，包括但不限于向您发出产品和服务信息，或者与本应用合作伙伴共享信息以便他们向您发送有关其产品和服务的信息（后者需要您的事先同意）。</li>\n<li>信息披露 <br>\n在如下情况下，本应用将依据您的个人意愿或法律的规定全部或部分的披露您的个人信息： <br>\n(a) 经您事先同意，向第三方披露； <br>\n(b) 为提供您所要求的产品和服务，而必须和第三方分享您的个人信息； <br>\n(c) 根据法律的有关规定，或者行政或司法机构的要求，向第三方或者行政、司法机构披露； <br>\n(d) 如您出现违反中国有关法律、法规或者本应用服务协议或相关规则的情况，需要向第三方披露； <br>\n(e) 如您是适格的知识产权投诉人并已提起投诉，应被投诉人要求，向被投诉人披露，以便双方处理可能的权利纠纷； <br>\n(f) 在本应用平台上创建的某一交易中，如交易任何一方履行或部分履行了交易义务并提出信息披露请求的，本应用有权决定向该用户提供其交易对方的联络方式等必要信息，以促成交易的完成或纠纷的解决。 <br>\n(g) 其它本应用根据法律、法规或者网站政策认为合适的披露。</li>\n<li>信息存储和交换 <br>\n本应用收集的有关您的信息和资料将保存在本应用及（或）其关联公司的服务器上，这些信息和资料可能传送至您所在国家、地区或本应用收集信息和资料所在地的境外并在境外被访问、存储和展示。</li>\n<li>Cookie 的使用 <br>\n(a) 在您未拒绝接受 cookies 的情况下，本应用会在您的计算机上设定或取用 cookies ，以便您能登录或使用依赖于 cookies 的本应用平台服务或功能。本应用使用 cookies 可为您提供更加周到的个性化服务，包括推广服务。 <br>\n(b) 您有权选择接受或拒绝接受 cookies。您可以通过修改浏览器设置的方式拒绝接受 cookies。但如果您选择拒绝接受 cookies，则您可能无法登录或使用依赖于 cookies 的本应用网络服务或功能。 <br>\n(c) 通过本应用所设 cookies 所取得的有关信息，将适用本政策。</li>\n<li>信息安全 <br>\n(a) 本应用帐号均有安全保护功能，请妥善保管您的用户名及密码信息。本应用将通过对用户密码进行加密等安全措施确保您的信息不丢失，不被滥用和变造。尽管有前述安全措施，但同时也请您注意在信息网络上不存在 “完善的安全措施”。 <br>\n(b) 在使用本应用网络服务进行网上交易时，您不可避免的要向交易对方或潜在的交易对</li>\n<li>本隐私政策的更改 <br>\n(a) 如果决定更改隐私政策，我们会在本政策中、本公司网站中以及我们认为适当的位置发布这些更改，以便您了解我们如何收集、使用您的个人信息，哪些人可以访问这些信息，以及在什么情况下我们会透露这些信息。 <br>\n(b) 本公司保留随时修改本政策的权利，因此请经常查看。如对本政策作出重大更改，本公司会通过网站通知的形式告知。 <br>\n方披露自己的个人信息，如联络方式或者邮政地址。请您妥善保护自己的个人信息，仅在必要的情形下向他人提供。如您发现自己的个人信息泄密，尤其是本应用用户名及密码发生泄露，请您立即联络本应用客服，以便本应用采取相应措施。</li>\n</ol></body></html>"
  },
  {
    "path": "LICENSE",
    "content": "Mozilla Public License Version 2.0\n==================================\n\n1. Definitions\n--------------\n\n1.1. \"Contributor\"\n    means each individual or legal entity that creates, contributes to\n    the creation of, or owns Covered Software.\n\n1.2. \"Contributor Version\"\n    means the combination of the Contributions of others (if any) used\n    by a Contributor and that particular Contributor's Contribution.\n\n1.3. \"Contribution\"\n    means Covered Software of a particular Contributor.\n\n1.4. \"Covered Software\"\n    means Source Code Form to which the initial Contributor has attached\n    the notice in Exhibit A, the Executable Form of such Source Code\n    Form, and Modifications of such Source Code Form, in each case\n    including portions thereof.\n\n1.5. \"Incompatible With Secondary Licenses\"\n    means\n\n    (a) that the initial Contributor has attached the notice described\n        in Exhibit B to the Covered Software; or\n\n    (b) that the Covered Software was made available under the terms of\n        version 1.1 or earlier of the License, but not also under the\n        terms of a Secondary License.\n\n1.6. \"Executable Form\"\n    means any form of the work other than Source Code Form.\n\n1.7. \"Larger Work\"\n    means a work that combines Covered Software with other material, in\n    a separate file or files, that is not Covered Software.\n\n1.8. \"License\"\n    means this document.\n\n1.9. \"Licensable\"\n    means having the right to grant, to the maximum extent possible,\n    whether at the time of the initial grant or subsequently, any and\n    all of the rights conveyed by this License.\n\n1.10. \"Modifications\"\n    means any of the following:\n\n    (a) any file in Source Code Form that results from an addition to,\n        deletion from, or modification of the contents of Covered\n        Software; or\n\n    (b) any new file in Source Code Form that contains any Covered\n        Software.\n\n1.11. \"Patent Claims\" of a Contributor\n    means any patent claim(s), including without limitation, method,\n    process, and apparatus claims, in any patent Licensable by such\n    Contributor that would be infringed, but for the grant of the\n    License, by the making, using, selling, offering for sale, having\n    made, import, or transfer of either its Contributions or its\n    Contributor Version.\n\n1.12. \"Secondary License\"\n    means either the GNU General Public License, Version 2.0, the GNU\n    Lesser General Public License, Version 2.1, the GNU Affero General\n    Public License, Version 3.0, or any later versions of those\n    licenses.\n\n1.13. \"Source Code Form\"\n    means the form of the work preferred for making modifications.\n\n1.14. \"You\" (or \"Your\")\n    means an individual or a legal entity exercising rights under this\n    License. For legal entities, \"You\" includes any entity that\n    controls, is controlled by, or is under common control with You. For\n    purposes of this definition, \"control\" means (a) the power, direct\n    or indirect, to cause the direction or management of such entity,\n    whether by contract or otherwise, or (b) ownership of more than\n    fifty percent (50%) of the outstanding shares or beneficial\n    ownership of such entity.\n\n2. License Grants and Conditions\n--------------------------------\n\n2.1. Grants\n\nEach Contributor hereby grants You a world-wide, royalty-free,\nnon-exclusive license:\n\n(a) under intellectual property rights (other than patent or trademark)\n    Licensable by such Contributor to use, reproduce, make available,\n    modify, display, perform, distribute, and otherwise exploit its\n    Contributions, either on an unmodified basis, with Modifications, or\n    as part of a Larger Work; and\n\n(b) under Patent Claims of such Contributor to make, use, sell, offer\n    for sale, have made, import, and otherwise transfer either its\n    Contributions or its Contributor Version.\n\n2.2. Effective Date\n\nThe licenses granted in Section 2.1 with respect to any Contribution\nbecome effective for each Contribution on the date the Contributor first\ndistributes such Contribution.\n\n2.3. Limitations on Grant Scope\n\nThe licenses granted in this Section 2 are the only rights granted under\nthis License. No additional rights or licenses will be implied from the\ndistribution or licensing of Covered Software under this License.\nNotwithstanding Section 2.1(b) above, no patent license is granted by a\nContributor:\n\n(a) for any code that a Contributor has removed from Covered Software;\n    or\n\n(b) for infringements caused by: (i) Your and any other third party's\n    modifications of Covered Software, or (ii) the combination of its\n    Contributions with other software (except as part of its Contributor\n    Version); or\n\n(c) under Patent Claims infringed by Covered Software in the absence of\n    its Contributions.\n\nThis License does not grant any rights in the trademarks, service marks,\nor logos of any Contributor (except as may be necessary to comply with\nthe notice requirements in Section 3.4).\n\n2.4. Subsequent Licenses\n\nNo Contributor makes additional grants as a result of Your choice to\ndistribute the Covered Software under a subsequent version of this\nLicense (see Section 10.2) or under the terms of a Secondary License (if\npermitted under the terms of Section 3.3).\n\n2.5. Representation\n\nEach Contributor represents that the Contributor believes its\nContributions are its original creation(s) or it has sufficient rights\nto grant the rights to its Contributions conveyed by this License.\n\n2.6. Fair Use\n\nThis License is not intended to limit any rights You have under\napplicable copyright doctrines of fair use, fair dealing, or other\nequivalents.\n\n2.7. Conditions\n\nSections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted\nin Section 2.1.\n\n3. Responsibilities\n-------------------\n\n3.1. Distribution of Source Form\n\nAll distribution of Covered Software in Source Code Form, including any\nModifications that You create or to which You contribute, must be under\nthe terms of this License. You must inform recipients that the Source\nCode Form of the Covered Software is governed by the terms of this\nLicense, and how they can obtain a copy of this License. You may not\nattempt to alter or restrict the recipients' rights in the Source Code\nForm.\n\n3.2. Distribution of Executable Form\n\nIf You distribute Covered Software in Executable Form then:\n\n(a) such Covered Software must also be made available in Source Code\n    Form, as described in Section 3.1, and You must inform recipients of\n    the Executable Form how they can obtain a copy of such Source Code\n    Form by reasonable means in a timely manner, at a charge no more\n    than the cost of distribution to the recipient; and\n\n(b) You may distribute such Executable Form under the terms of this\n    License, or sublicense it under different terms, provided that the\n    license for the Executable Form does not attempt to limit or alter\n    the recipients' rights in the Source Code Form under this License.\n\n3.3. Distribution of a Larger Work\n\nYou may create and distribute a Larger Work under terms of Your choice,\nprovided that You also comply with the requirements of this License for\nthe Covered Software. If the Larger Work is a combination of Covered\nSoftware with a work governed by one or more Secondary Licenses, and the\nCovered Software is not Incompatible With Secondary Licenses, this\nLicense permits You to additionally distribute such Covered Software\nunder the terms of such Secondary License(s), so that the recipient of\nthe Larger Work may, at their option, further distribute the Covered\nSoftware under the terms of either this License or such Secondary\nLicense(s).\n\n3.4. Notices\n\nYou may not remove or alter the substance of any license notices\n(including copyright notices, patent notices, disclaimers of warranty,\nor limitations of liability) contained within the Source Code Form of\nthe Covered Software, except that You may alter any license notices to\nthe extent required to remedy known factual inaccuracies.\n\n3.5. Application of Additional Terms\n\nYou may choose to offer, and to charge a fee for, warranty, support,\nindemnity or liability obligations to one or more recipients of Covered\nSoftware. However, You may do so only on Your own behalf, and not on\nbehalf of any Contributor. You must make it absolutely clear that any\nsuch warranty, support, indemnity, or liability obligation is offered by\nYou alone, and You hereby agree to indemnify every Contributor for any\nliability incurred by such Contributor as a result of warranty, support,\nindemnity or liability terms You offer. You may include additional\ndisclaimers of warranty and limitations of liability specific to any\njurisdiction.\n\n4. Inability to Comply Due to Statute or Regulation\n---------------------------------------------------\n\nIf it is impossible for You to comply with any of the terms of this\nLicense with respect to some or all of the Covered Software due to\nstatute, judicial order, or regulation then You must: (a) comply with\nthe terms of this License to the maximum extent possible; and (b)\ndescribe the limitations and the code they affect. Such description must\nbe placed in a text file included with all distributions of the Covered\nSoftware under this License. Except to the extent prohibited by statute\nor regulation, such description must be sufficiently detailed for a\nrecipient of ordinary skill to be able to understand it.\n\n5. Termination\n--------------\n\n5.1. The rights granted under this License will terminate automatically\nif You fail to comply with any of its terms. However, if You become\ncompliant, then the rights granted under this License from a particular\nContributor are reinstated (a) provisionally, unless and until such\nContributor explicitly and finally terminates Your grants, and (b) on an\nongoing basis, if such Contributor fails to notify You of the\nnon-compliance by some reasonable means prior to 60 days after You have\ncome back into compliance. Moreover, Your grants from a particular\nContributor are reinstated on an ongoing basis if such Contributor\nnotifies You of the non-compliance by some reasonable means, this is the\nfirst time You have received notice of non-compliance with this License\nfrom such Contributor, and You become compliant prior to 30 days after\nYour receipt of the notice.\n\n5.2. If You initiate litigation against any entity by asserting a patent\ninfringement claim (excluding declaratory judgment actions,\ncounter-claims, and cross-claims) alleging that a Contributor Version\ndirectly or indirectly infringes any patent, then the rights granted to\nYou by any and all Contributors for the Covered Software under Section\n2.1 of this License shall terminate.\n\n5.3. In the event of termination under Sections 5.1 or 5.2 above, all\nend user license agreements (excluding distributors and resellers) which\nhave been validly granted by You or Your distributors under this License\nprior to termination shall survive termination.\n\n************************************************************************\n*                                                                      *\n*  6. Disclaimer of Warranty                                           *\n*  -------------------------                                           *\n*                                                                      *\n*  Covered Software is provided under this License on an \"as is\"       *\n*  basis, without warranty of any kind, either expressed, implied, or  *\n*  statutory, including, without limitation, warranties that the       *\n*  Covered Software is free of defects, merchantable, fit for a        *\n*  particular purpose or non-infringing. The entire risk as to the     *\n*  quality and performance of the Covered Software is with You.        *\n*  Should any Covered Software prove defective in any respect, You     *\n*  (not any Contributor) assume the cost of any necessary servicing,   *\n*  repair, or correction. This disclaimer of warranty constitutes an   *\n*  essential part of this License. No use of any Covered Software is   *\n*  authorized under this License except under this disclaimer.         *\n*                                                                      *\n************************************************************************\n\n************************************************************************\n*                                                                      *\n*  7. Limitation of Liability                                          *\n*  --------------------------                                          *\n*                                                                      *\n*  Under no circumstances and under no legal theory, whether tort      *\n*  (including negligence), contract, or otherwise, shall any           *\n*  Contributor, or anyone who distributes Covered Software as          *\n*  permitted above, be liable to You for any direct, indirect,         *\n*  special, incidental, or consequential damages of any character      *\n*  including, without limitation, damages for lost profits, loss of    *\n*  goodwill, work stoppage, computer failure or malfunction, or any    *\n*  and all other commercial damages or losses, even if such party      *\n*  shall have been informed of the possibility of such damages. This   *\n*  limitation of liability shall not apply to liability for death or   *\n*  personal injury resulting from such party's negligence to the       *\n*  extent applicable law prohibits such limitation. Some               *\n*  jurisdictions do not allow the exclusion or limitation of           *\n*  incidental or consequential damages, so this exclusion and          *\n*  limitation may not apply to You.                                    *\n*                                                                      *\n************************************************************************\n\n8. Litigation\n-------------\n\nAny litigation relating to this License may be brought only in the\ncourts of a jurisdiction where the defendant maintains its principal\nplace of business and such litigation shall be governed by laws of that\njurisdiction, without reference to its conflict-of-law provisions.\nNothing in this Section shall prevent a party's ability to bring\ncross-claims or counter-claims.\n\n9. Miscellaneous\n----------------\n\nThis License represents the complete agreement concerning the subject\nmatter hereof. If any provision of this License is held to be\nunenforceable, such provision shall be reformed only to the extent\nnecessary to make it enforceable. Any law or regulation which provides\nthat the language of a contract shall be construed against the drafter\nshall not be used to construe this License against a Contributor.\n\n10. Versions of the License\n---------------------------\n\n10.1. New Versions\n\nMozilla Foundation is the license steward. Except as provided in Section\n10.3, no one other than the license steward has the right to modify or\npublish new versions of this License. Each version will be given a\ndistinguishing version number.\n\n10.2. Effect of New Versions\n\nYou may distribute the Covered Software under the terms of the version\nof the License under which You originally received the Covered Software,\nor under the terms of any subsequent version published by the license\nsteward.\n\n10.3. Modified Versions\n\nIf you create software not governed by this License, and you want to\ncreate a new license for such software, you may create and use a\nmodified version of this License if you rename the license and remove\nany references to the name of the license steward (except to note that\nsuch modified license differs from this License).\n\n10.4. Distributing Source Code Form that is Incompatible With Secondary\nLicenses\n\nIf You choose to distribute Source Code Form that is Incompatible With\nSecondary Licenses under the terms of this version of the License, the\nnotice described in Exhibit B of this License must be attached.\n\nExhibit A - Source Code Form License Notice\n-------------------------------------------\n\n  This Source Code Form is subject to the terms of the Mozilla Public\n  License, v. 2.0. If a copy of the MPL was not distributed with this\n  file, You can obtain one at http://mozilla.org/MPL/2.0/.\n\nIf it is not possible or desirable to put the notice in a particular\nfile, then You may include the notice in a location (such as a LICENSE\nfile in a relevant directory) where a recipient would be likely to look\nfor such a notice.\n\nYou may add additional accurate notices of copyright ownership.\n\nExhibit B - \"Incompatible With Secondary Licenses\" Notice\n---------------------------------------------------------\n\n  This Source Code Form is \"Incompatible With Secondary Licenses\", as\n  defined by the Mozilla Public License, v. 2.0.\n"
  },
  {
    "path": "README.md",
    "content": "# KeepassA\n![KeepassA](https://github.com/AriaLyy/KeepassA/blob/master/app/src/main/res/mipmap-xxhdpi/ic_launcher.png)</br>\n<img src=\"https://github.com/AriaLyy/KeepassA/blob/master/img/Screenshot/Screenshot_1616425959.png\" height=500, width=300>\n<img src=\"https://github.com/AriaLyy/KeepassA/blob/master/img/Screenshot/Screenshot_1616425970.png\" height=500, width=300>\n<img src=\"https://github.com/AriaLyy/KeepassA/blob/master/img/Screenshot/Screenshot_1616426133.png\" height=500, width=300>\n<img src=\"https://github.com/AriaLyy/KeepassA/blob/master/img/Screenshot/Screenshot_1616426147.png\" height=500, width=300>\n<img src=\"https://github.com/AriaLyy/KeepassA/blob/master/img/Screenshot/Screenshot_1616426150.png\" height=500, width=300>\n<img src=\"https://github.com/AriaLyy/KeepassA/blob/master/img/Screenshot/Screenshot_1616426345.png\" height=500, width=300>\n<img src=\"https://github.com/AriaLyy/KeepassA/blob/master/img/Screenshot/Screenshot_1616426364.png\" height=500, width=300>\n<img src=\"https://github.com/AriaLyy/KeepassA/blob/master/img/Screenshot/Screenshot_1616426501.png\" height=500, width=300>\n\n\n## Features\nA password-management software with a simple interface and operation. \\\nFully compatible with the KeePass 2.x databases. \\\nThese features were added:\n\n1. Auto-fill service. For passwords, based on the native Android framework.\n2. Fingerprint unlock. Use the first fingerprint key to unlock the database.\n3. Quick unlock. No need to enter a long database password every time.\n4. Dropbox/WebDAV syncing. Sync the KeePass database between Linux, macOS, Windows, Android, iOS and so on.\n5. TOTP and OTP secondary verification codes. Get the second verification code with a OTP app.\n6. Secondary verification codes from Steam.\n7. Open historical records. Quickly find the last opened record.\n8. Shortcuts. Quickly open the search and create items on the homescreen.\n\n## Contributions\n* Add features by making a **[pull request](https://help.github.com/articles/about-pull-requests/)**.\n* Help **[translate](https://hosted.weblate.org/projects/keepassa/string/)** KeepassA to your language (on [Hosted Weblate](https://hosted.weblate.org/projects/keepassa/) or by sending a [pull request](https://help.github.com/articles/about-pull-requests/)).\n* Help translate KeePassA by sending a [pull request](https://help.github.com/articles/about-pull-requests/).\n\n## Contributor\n[@DominicDesbiens](https://github.com/DominicDesbiens)\n\n## Donate\nfor a better service and a quicker development of features you want.</br>\n<img src=\"https://raw.githubusercontent.com/AriaLyy/Aria/master/img/ali_pay.png\" width=200 height=200/>\n<img src=\"https://raw.githubusercontent.com/AriaLyy/Aria/master/img/wx_pay.png\" width=200 height=200/>\n\n\n## Download\n[<img src=\"https://gitlab.com/IzzyOnDroid/repo/-/raw/master/assets/IzzyOnDroid.png\"\n      alt=\"Get it on IzzyOnDroid\"\n      height=\"80\">](https://apt.izzysoft.de/fdroid/index/apk/com.lyy.keepassa)\n[<img src=\"https://play.google.com/intl/en_us/badges/images/generic/en_badge_web_generic.png\"\n      alt=\"Get it on Google Play\"\n      height=\"80\">](https://play.google.com/store/apps/details?id=com.lyy.keepassa)\n\n## Q&A\nOther questions? Maybe they are answered by reading the [issues](https://github.com/AriaLyy/KeepassA/issues)?\n\n## Other KeePass software\n* [KeePass](https://keepass.info/). The original and official project for the desktop.\n* [KeePassDX](https://www.keepassdx.com/). An alternative for Android.\n* [KeePassXC](https://keepassxc.org/). An alternative for Linux, Windows, and Android.\n* [KeeWeb](https://keeweb.info/). A web version compatible with KeePass files.\n\n## License\n```\n Copyright (C) 2020–2022 AriaLyy(https://github.com/AriaLyy/KeepassA)\n \n This Source Code Form is subject to the terms of the Mozilla Public\n License, v. 2.0. If a copy of the MPL was not distributed with this\n file, you can obtain one at http://mozilla.org/MPL/2.0/.\n```\n"
  },
  {
    "path": "VersionManager/build.gradle",
    "content": "/*\n * This file was generated by the Gradle 'init' task.\n *\n * This generated file contains a sample Gradle plugin project to get you started.\n * For more details take a look at the Writing Custom Plugins chapter in the Gradle\n * User Manual available at https://docs.gradle.org/7.3.3/userguide/custom_plugins.html\n * This project uses @Incubating APIs which are subject to change.\n */\n\nplugins {\n  // Apply the Java Gradle plugin development plugin to add support for developing Gradle plugins\n  id 'groovy'\n  id 'java-gradle-plugin'\n  id 'version-catalog'\n  id 'maven-publish'\n}\n\n// repositories {\n//     // Use Maven Central for resolving dependencies.\n//     mavenCentral()\n// }\n\ndependencies {\n  implementation gradleApi()\n  implementation localGroovy()\n  implementation(libs.groovy.core)\n  //    implementation fileTree(dir: 'libs', include: ['*.jar'])\n}\n\ntesting {\n  suites {\n    // Configure the built-in test suite\n    test {\n      // Use JUnit Jupiter test framework\n      useJUnitJupiter('5.7.2')\n    }\n\n    // Create a new test suite\n    functionalTest(JvmTestSuite) {\n      dependencies {\n        // functionalTest test suite depends on the production code in tests\n        implementation project\n      }\n\n      targets {\n        all {\n          // This test suite should run after the built-in test suite has run its tests\n          testTask.configure { shouldRunAfter(test) }\n        }\n      }\n    }\n  }\n}\n\ngradlePlugin {\n  // Define the plugin\n  plugins {\n    greeting {\n      id = 'com.alg.plugin.version.greeting'\n      implementationClass = 'com.alg.plugin.version.VersionManagerPlugin'\n    }\n  }\n}\n\ngradlePlugin.testSourceSets(sourceSets.functionalTest)\n\ntasks.named('check') {\n  // Include functionalTest as part of the check lifecycle\n  dependsOn(testing.suites.functionalTest)\n}\n\ncatalog {\n  // declare the aliases, bundles and versions in this block\n  versionCatalog {\n    from files('../libs.versions.toml')\n  }\n}\n\npublishing {\n  publications {\n    maven(MavenPublication) {\n      groupId = 'com.lyy.kpa.version'\n      artifactId = 'catalog'\n      version = '0.0.3'\n      from components.versionCatalog\n    }\n  }\n}\n"
  },
  {
    "path": "VersionManager/src/functionalTest/java/com/alg/plugin/version/VersionManagerPluginFunctionalTest.java",
    "content": "/*\n * This Java source file was generated by the Gradle 'init' task.\n */\npackage com.alg.plugin.version;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.Writer;\nimport java.io.FileWriter;\nimport java.nio.file.Files;\nimport org.gradle.testkit.runner.GradleRunner;\nimport org.gradle.testkit.runner.BuildResult;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.io.TempDir;\nimport static org.junit.jupiter.api.Assertions.*;\n\n/**\n * A simple functional test for the 'com.alg.plugin.version.greeting' plugin.\n */\nclass VersionManagerPluginFunctionalTest {\n    @TempDir\n    File projectDir;\n\n    private File getBuildFile() {\n        return new File(projectDir, \"build.gradle\");\n    }\n\n    private File getSettingsFile() {\n        return new File(projectDir, \"settings.gradle\");\n    }\n\n    @Test void canRunTask() throws IOException {\n        writeString(getSettingsFile(), \"\");\n        writeString(getBuildFile(),\n            \"plugins {\" +\n            \"  id('com.alg.plugin.version.greeting')\" +\n            \"}\");\n\n        // Run the build\n        GradleRunner runner = GradleRunner.create();\n        runner.forwardOutput();\n        runner.withPluginClasspath();\n        runner.withArguments(\"greeting\");\n        runner.withProjectDir(projectDir);\n        BuildResult result = runner.build();\n\n        // Verify the result\n        assertTrue(result.getOutput().contains(\"Hello from plugin 'com.alg.plugin.version.greeting'\"));\n    }\n\n    private void writeString(File file, String string) throws IOException {\n        try (Writer writer = new FileWriter(file)) {\n            writer.write(string);\n        }\n    }\n}\n"
  },
  {
    "path": "VersionManager/src/main/java/com/alg/plugin/version/VersionManagerPlugin.java",
    "content": "/*\n * This Java source file was generated by the Gradle 'init' task.\n */\npackage com.alg.plugin.version;\n\nimport org.gradle.api.Project;\nimport org.gradle.api.Plugin;\n\n/**\n * A simple 'hello world' plugin.\n */\npublic class VersionManagerPlugin implements Plugin<Project> {\n    public void apply(Project project) {\n        // Register a task\n        project.getTasks().register(\"greeting\", task -> {\n            task.doLast(s -> System.out.println(\"Hello from plugin 'com.alg.plugin.version.greeting'\"));\n        });\n    }\n}\n"
  },
  {
    "path": "VersionManager/src/test/java/com/alg/plugin/version/VersionManagerPluginTest.java",
    "content": "/*\n * This Java source file was generated by the Gradle 'init' task.\n */\npackage com.alg.plugin.version;\n\nimport org.gradle.testfixtures.ProjectBuilder;\nimport org.gradle.api.Project;\nimport org.junit.jupiter.api.Test;\nimport static org.junit.jupiter.api.Assertions.*;\n\n/**\n * A simple unit test for the 'com.alg.plugin.version.greeting' plugin.\n */\nclass VersionManagerPluginTest {\n    @Test void pluginRegistersATask() {\n        // Create a test project and apply the plugin\n        Project project = ProjectBuilder.builder().build();\n        project.getPlugins().apply(\"com.alg.plugin.version.greeting\");\n\n        // Verify the result\n        assertNotNull(project.getTasks().findByName(\"greeting\"));\n    }\n}\n"
  },
  {
    "path": "app/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "app/AndResGuard.gradle",
    "content": "apply plugin: 'AndResGuard'\n\nbuildscript {\n  repositories {\n    jcenter()\n    google()\n  }\n  dependencies {\n    classpath 'com.tencent.mm:AndResGuard-gradle-plugin:1.2.21'\n  }\n}\n\nandResGuard {\n  mappingFile = file(\"resource_mapping.txt\")\n  mappingFile = null\n  // 对于发布于Google Play的APP，建议不要使用7Zip压缩，因为这个会导致Google Play的优化Patch算法失效. https://github.com/shwenzhang/AndResGuard/issues/233\n  use7zip = false\n  useSign = true\n  // 打开这个开关，会keep住所有资源的原始路径，只混淆资源的名字\n  keepRoot = false\n  // 设置这个值，会把arsc name列混淆成相同的名字，减少string常量池的大小\n  fixedResName = \"arg\"\n  // 打开这个开关会合并所有哈希值相同的资源，但请不要过度依赖这个功能去除去冗余资源\n  mergeDuplicatedRes = true\n  whiteList = [\n      // for your icon\n      \"R.drawable.icon\",\n      // for fabric\n      \"R.string.com.crashlytics.*\",\n      // for google-services\n      \"R.string.google_app_id\",\n      \"R.string.gcm_defaultSenderId\",\n      \"R.string.default_web_client_id\",\n      \"R.string.ga_trackingId\",\n      \"R.string.firebase_database_url\",\n      \"R.string.google_api_key\",\n      \"R.string.google_crash_reporting_api_key\",\n      // 不混淆矢量图\n      \"R.drawable.cc*\",\n      // 不混淆启动图\n      \"R.drawable.ic_launcher_foreground\",\n      \"R.mipmap.ic_launcher\"\n  ]\n  compressFilePattern = [\n      \"*.png\",\n      \"*.jpg\",\n      \"*.jpeg\",\n      \"*.gif\",\n  ]\n  sevenzip {\n    artifact = 'com.tencent.mm:SevenZip:1.2.21'\n    //path = \"/usr/local/bin/7za\"\n  }\n\n  /**\n   * 可选： 如果不设置则会默认覆盖assemble输出的apk\n   **/\n   finalApkBackupPath = \"${project.buildDir}/AndResGuardFinal.apk\"\n\n  /**\n   * 可选: 指定v1签名时生成jar文件的摘要算法\n   * 默认值为“SHA-1”\n   **/\n  // digestalg = \"SHA-256\"\n}"
  },
  {
    "path": "app/build.gradle",
    "content": "plugins {\n  id 'com.android.application'\n  id 'kotlin-android'\n  id 'kotlin-kapt'\n  id 'com.tencent.vasdolly'\n  id 'com.alibaba.arouter'\n  id 'kotlin-parcelize'\n  id 'com.google.gms.google-services'\n  id 'com.google.firebase.crashlytics'\n}\n\nandroid {\n  compileSdkVersion libs.versions.compilesdk.get().toInteger()\n  buildToolsVersion libs.versions.buildtools.get()\n\n  compileOptions {\n    // Flag to enable support for the new language APIs\n//    coreLibraryDesugaringEnabled true\n    sourceCompatibility JavaVersion.VERSION_11\n    targetCompatibility JavaVersion.VERSION_11\n  }\n\n  kotlinOptions {\n    jvmTarget = \"1.8\"\n  }\n\n  defaultConfig {\n    applicationId \"com.lyy.keepassa\"\n    minSdkVersion libs.versions.minSdk.get().toInteger()\n    targetSdkVersion libs.versions.targetsdk.get().toInteger()\n    versionCode 71\n    versionName \"2.4.7\"\n    multiDexEnabled true\n\n    testInstrumentationRunner \"androidx.test.runner.AndroidJUnitRunner\"\n\n    // so 版本\n    ndk.abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'\n\n    // 只保留以下语言包\n    resConfigs \"zh-rCN\", \"zh-rTW\", \"en\", \"fr-rCA\", \"nb-rNO\", \"ru-rRU\", \"fr\", \"de-rDE\", \"pl\", \"tr\",\n        \"uk-rUA\", \"es\"\n  }\n\n  signingConfigs {\n    release {\n      def properties = new Properties()\n      def inputStream = project.rootProject.file('local.properties').newDataInputStream()\n      properties.load(inputStream)\n\n      storeFile file(properties.getProperty('storeFile'))\n      storePassword properties.getProperty('storePassword')\n      keyAlias properties.getProperty('keyAlias')\n      keyPassword properties.getProperty('keyPassword')\n      v1SigningEnabled true // v1 签名\n      v2SigningEnabled true // v2 签名\n    }\n  }\n\n  buildTypes {\n\n    debug {\n      debuggable = true\n      // 下面两个是debug-db的参数\n      resValue(\"string\", \"PORT_NUMBER\", \"10086\") // 端口\n      resValue(\"string\", \"DB_PASSWORD_keepassA.db\", \"stVz7QxFgzA7yMnH\") // sqlcipher 加密密码\n      signingConfig signingConfigs.release\n\n      firebaseCrashlytics {\n        mappingFileUploadEnabled false\n      }\n    }\n\n    release {\n      //zipAlignEnabled true //开启Zipalign优化\n      debuggable false\n//      debuggable true\n      minifyEnabled true\n      shrinkResources true // 移除无用资源\n      //      multiDexKeepFile file('multidex-config.txt')\n      proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'\n      // wcdb 需要增加这个混淆\n      signingConfig signingConfigs.release\n\n      firebaseCrashlytics {\n        mappingFileUploadEnabled true\n      }\n    }\n  }\n\n  buildFeatures {\n    dataBinding = true\n    // for view binding :\n    viewBinding = true\n  }\n\n  testOptions {\n    unitTests.includeAndroidResources = true\n    unitTests.returnDefaultValues = true\n  }\n\n  externalNativeBuild {\n    cmake {\n      path \"src/main/jni/CMakeLists.txt\"\n    }\n  }\n\n  lintOptions {\n    checkReleaseBuilds false\n    // Or, if you prefer, you can continue to check for errors in release builds,\n    // but continue the build even when errors are found:\n    //    abortOnError false\n  }\n\n  flavorDimensions \"app\"\n  //   渠道配置 https://developer.android.google.cn/studio/build/build-variants.html?hl=zh-cn\n  productFlavors {\n    dev {\n      dimension \"app\"\n    }\n  }\n\n\n  ndkVersion '21.4.7075529'\n}\n\nkapt {\n  arguments {\n    arg('room.schemaLocation', \"$projectDir/schemas\") //指定room.schemaLocation生成的文件路径，用于版本升级\n    arg('AROUTER_MODULE_NAME', project.getName() + \"_kpa\") // arouter\n    // 生成的文档路径 : build/generated/source/apt/(debug or release)/com/alibaba/android/arouter/docs/arouter-map-of-${moduleName}.json\n    //    arg('AROUTER_GENERATE_DOC', \"enable\") // arouter,release 不能打开这个\n    // eventbus索引，https://greenrobot.org/eventbus/documentation/subscriber-index/\n    arg('eventBusIndex', 'com.lyy.keepassa.KpaEventBusIndex') // 将会自动创建KpaEventBusIndex\n  }\n}\n\nconfigurations {\n  // 配置不同的渠道加载不同的module\n  playImplementation {}\n}\n\n/**\n * 根据已有基础包重新生成多渠道包\n * 如果是自动生成渠道包，见https://github.com/Tencent/VasDolly\n */\nrebuildChannel {\n  //指定渠道文件\n  channelFile = file(\"${project.getProjectDir()}/channel\")\n  //  baseDebugApk = 已有Debug APK\n  // 已有Release APK\n  //  baseReleaseApk = new File(\"${project.buildDir}/AndResGuardFinal.apk\")\n  baseApk = new File(\"${project.buildDir}/outputs/apk/dev/release/app-dev-release.apk\")\n  //默认为new File(project.buildDir, \"rebuildChannel/debug\")\n  //  debugOutputDir = Debug渠道包输出目录\n  //默认为new File(project.buildDir, \"rebuildChannel/release\")\n  // Release渠道包输出目录\n  outputDir = new File(\"${project.buildDir}/outputs/channels\")\n  //快速模式：生成渠道包时不进行校验（速度可以提升10倍以上，默认为false）\n  fastMode = false\n  //低内存模式（仅针对V2签名，默认为false）：只把签名块、中央目录和EOCD读取到内存，不把最大头的内容块读取到内存，在手机上合成APK时，可以使用该模式\n  lowMemory = false\n}\n\n// 配置 AndResGuard 于 channel 前执行\n// 这几行代码表示reBuildChannel任务是依赖于resguardRelease任务的，也就是说先执行完资源混淆后再执行多渠道打包。\nafterEvaluate {\n  tasks.getByName('reBuildChannel') {\n    dependsOn(assembleRelease) // 暂时使用这个\n\n    // 不要使用，1.2.21打包出来的apk，消息对话框布局错乱\n    //    dependsOn('resguardRelease')\n  }\n}\n\nboolean isReleaseBuildType() {\n  for (String s : gradle.startParameter.taskNames) {\n    println(\"builtType: \" + s)\n    if (s.contains(\"Release\") | s.contains(\"release\") | s.contains(\"reBuildChannel\")) {\n      return true\n    }\n  }\n  return false\n}\n\n// 替换AndroidManifest 参数\n// https://developer.android.com/studio/known-issues#variant_api\nandroid.applicationVariants.all { variant ->\n  variant.outputs.all { output ->\n    output.processManifest.doLast {\n      // Stores the path to the maifest.\n      String manifestPath = \"${multiApkManifestOutputDirectory.asFile.get()}/AndroidManifest.xml\"\n      println(\"版本类型：\" + isReleaseBuildType())\n      //      def keyHash = \"5qMkIKbIM5Lx7VbajX8mT3rKXpE=\"\n      //      if (isReleaseBuildType()) {\n      //        keyHash = \"xv3czaACtp98KvuSqP3GRLCxjrc=\"\n      //      }\n      def keyHash = \"xv3czaACtp98KvuSqP3GRLCxjrc=\"\n\n      // 替换one drive 配置\n      def updatedContent = file(manifestPath).getText('UTF-8')\n          .replaceAll(\"msal_package_name\", \"com.lyy.keepassa\") // 包名\n          .replaceAll(\"msal_signature_hash\", keyHash)\n      // 当前签名hash\n      file(manifestPath).write(updatedContent, 'UTF-8')\n    }\n  }\n}\n\ndependencies {\n  implementation fileTree(dir: 'libs', include: ['*.jar'])\n\n  // 测试模块\n  testImplementation libs.test.junit\n  androidTestImplementation libs.bundles.android.test\n\n  //      debugImplementation 'com.amitshekhar.android:debug-db:1.0.6'\n  // 数据库如果加密的话使用这个\n  //  debugImplementation 'com.amitshekhar.android:debug-db-encrypt:1.0.6'\n\n  // keepassA组件\n  implementation 'me.laoyuyu.keepassa:KeepassIcon:0.4.0'\n  implementation 'me.laoyuyu.keepassa:KeepassLib:0.4.1'\n  //  implementation project(path: ':KpaLib')\n  implementation 'me.laoyuyu.keepassa:KPAFrame:0.4.1'\n  implementation 'me.laoyuyu.keepassa:UIWidget:0.4.2'\n\n\n  // kotlin\n  implementation(libs.bundles.android.kotlin)\n\n  // =========================== android 组件 start =====================================\n  implementation(libs.bundles.jetpack.ui) // jetpack-ui\n  implementation(libs.bundles.jetpack.lifecycle) // lifecycle\n  implementation(libs.jetpack.workmanager) // workmanager\n  implementation(libs.jetpack.room.runtime) // room\n  implementation(libs.jetpack.room.ktx) // room\n  kapt(libs.jetpack.room.compiler)\n  implementation(libs.google.tink)\n  implementation(libs.jetpack.autofill) // 自动填充工具\n  implementation(libs.jetpack.biometric) // 生物识别\n  implementation(libs.jetpack.multidex)\n  // =========================== android 组件 end =====================================\n\n  // =========================== 三方库 start =====================================\n  //  implementation libs.third\n  implementation(libs.third.jodatime)\n  implementation(libs.third.richtext) // 富文本\n  implementation(libs.third.licensesdialog) //开源许可对话框\n  implementation(libs.third.immersionbar) // 状态栏\n  implementation(libs.third.subsampling) // 大图浏览\n  implementation(libs.third.eventbus)\n  kapt(libs.third.eventbus.compiler)\n  implementation(libs.third.lottie) // json 动画库\n  implementation(libs.third.glide)\n  kapt(libs.third.glide.compiler)\n  implementation(libs.third.dropbox)\n  implementation(libs.third.webdav)\n  implementation(libs.bundles.tencent.wcdb)\n  implementation(libs.tencent.vasdolly)\n  implementation(libs.third.protector) // XP/调试/多开/模拟器/root 判断\n  implementation(libs.third.autosize)\n  implementation(libs.google.gson)\n  implementation(libs.third.freereflection)\n  implementation(libs.microsoft.msal)\n  implementation(libs.squareup.retrofit)\n  kapt(libs.ali.third.arouter.compiler)\n  implementation(libs.third.zxing.embedded) { transitive = false }\n  implementation(libs.google.zxing)\n  implementation(libs.jetpack.palette.ktx)\n  // mmkv\n  implementation(libs.tencent.mmkv)\n\n  // xlog\n  implementation 'com.tencent.mars:mars-xlog:1.2.5'\n  // // =========================== 三方库 end =====================================\n  implementation(libs.google.gms.billing)\n  implementation(libs.google.gms.base)\n\n  //firebase\n  implementation platform('com.google.firebase:firebase-bom:32.7.1')\n  implementation 'com.google.firebase:firebase-crashlytics-ktx'\n}\n\n\ngradle.startParameter.taskNames.forEach {\n  if (it.contains(\"Test\")){\n    apply from: \"${rootDir.path}/gradle/testConfig.gradle\"\n  }\n}\n\n//apply from: 'firebase.gradle'"
  },
  {
    "path": "app/channel",
    "content": "play\nxiaomi\ncoolapk\nhuawei\ngithub\n"
  },
  {
    "path": "app/firebase.gradle",
    "content": "apply {\n  plugin \"com.android.application\"\n}\n\nandroid.applicationVariants.configureEach { variant ->\n  tasks.configureEach {\n    if (it.name.contains(\"assemble\") && it.name.contains(\"Release\")) {\n      println(\"开始上传符号表\")\n      it.doLast {\n\n        def cmd = \"${project.rootDir.absolutePath}/gradlew uploadCrashlyticsMappingFileDevRelease\"\n        def p = cmd.execute()\n        p.waitForProcessOutput(System.out, System.err)\n      }\n    }\n  }\n}"
  },
  {
    "path": "app/multidex-config.txt",
    "content": "org/greenrobot/eventbus/EventBus.class"
  },
  {
    "path": "app/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\n################################### 混淆配置 start ###########################################\n#指定代码的压缩级别\n-optimizationpasses 5\n#包明不混合大小写\n-dontusemixedcaseclassnames\n#不去忽略非公共的库类\n-dontskipnonpubliclibraryclasses\n#优化  不优化输入的类文件\n-dontoptimize\n#预校验\n-dontpreverify\n#混淆时是否记录日志\n-verbose\n# 混淆时所采用的算法\n-optimizations !code/simplification/arithmetic,!code/simplification/cast,!field/*,!class/merging/*\n#-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*\n#忽略警告\n#-ignorewarning\n-keepattributes SourceFile,LineNumberTable   # Keep file names and line numbers.\n-keep public class * extends java.lang.Exception  # Optional: Keep custom exceptions.\n################################### 混淆配置 end ############################################\n\n\n################## 记录生成的日志数据,gradle build时在本项目根目录输出         #################\n####### 输出文件夹 build/outputs/mapping\n#apk 包内所有 class 的内部结构\n-dump build/outputs/mapping/class_files.txt\n#未混淆的类和成员\n-printseeds build/outputs/mapping/kpa_seeds.txt\n#列出从 apk 中删除的代码\n-printusage build/outputs/mapping/kpa_unused.txt\n#混淆前后的映射\n-printmapping build/outputs/mapping/kpa_mapping.txt\n################## 记录生成的日志数据，gradle build时 在本项目根目录输出-end    #################\n\n################## 常用属性配置-start  ##################\n# 保护注解\n-keepattributes *Annotation*\n# 保护support v4 包\n-dontwarn android.support.v4.app.**\n\n# *\t匹配任意长度字符，不包含包名分隔符 (.)\n# **\t匹配任意长度字符，包含包名分隔符 (.)\n# ***\t匹配任意参数类型\n-keep class android.support.v4.app.**{ *; }\n# 保护andorid x\n-keep class com.google.android.material.** {*;}\n-keep class androidx.** {*;}\n-keep public class * extends androidx.**\n-keep interface androidx.** {*;}\n-dontwarn com.google.android.material.**\n-dontnote com.google.android.material.**\n-dontwarn androidx.**\n# 保护一些奇葩的问题\n-dontwarn org.xmlpull.v1.XmlPullParser\n-dontwarn org.xmlpull.v1.XmlSerializer\n-keep class org.xmlpull.v1.* {*;}\n\n# 保护JS接口\n-keepclassmembers class * {\n    @android.webkit.JavascriptInterface <methods>;\n}\n##保持 native 方法不被混淆\n-keepclasseswithmembernames class * {\n    native <methods>;\n}\n##保持 Parcelable 不被混淆\n-keep class * implements android.os.Parcelable {\n  public static final android.os.Parcelable$Creator *;\n   *;\n}\n##保持 Serializable 不被混淆\n-keepnames class * implements java.io.Serializable\n-keep class * implements java.io.Serializable{\n  *;\n}\n#\n#保持 Serializable 不被混淆并且enum 类也不被混淆\n-keepclassmembers class * implements java.io.Serializable {\n    static final long serialVersionUID;\n    private static final java.io.ObjectStreamField[] serialPersistentFields;\n    private void writeObject(java.io.ObjectOutputStream);\n    private void readObject(java.io.ObjectInputStream);\n    java.lang.Object writeReplace();\n    java.lang.Object readResolve();\n}\n#避免混淆泛型 如果混淆报错建议关掉\n#–keepattributes Signature\n# webview + js\n-keepattributes *JavascriptInterface*\n\n################## 常用属性配置-end  ##################\n\n################## kotlin-start  ##################\n-keep class kotlin.** { *; }\n-keep class kotlin.Metadata { *; }\n-dontwarn kotlin.**\n-keepclassmembers class **$WhenMappings {\n    <fields>;\n}\n-keepclassmembers class kotlin.Metadata {\n    public <methods>;\n}\n-assumenosideeffects class kotlin.jvm.internal.Intrinsics {\n    static void checkParameterIsNotNull(java.lang.Object, java.lang.String);\n}\n################## kotlin-end  ##################\n\n################# eventbus-start ##################\n-keepclassmembers class * {\n    @org.greenrobot.eventbus.Subscribe <methods>;\n}\n-keep enum org.greenrobot.eventbus.ThreadMode { *; }\n\n# And if you use AsyncExecutor:\n-keepclassmembers class * extends org.greenrobot.eventbus.util.ThrowableFailureEvent {\n    <init>(java.lang.Throwable);\n}\n################# eventbus-end ##################\n\n################# glide-start ##################\n-keep public class * implements com.bumptech.glide.module.GlideModule\n-keep public class * extends com.bumptech.glide.module.AppGlideModule\n-keep public enum com.bumptech.glide.load.ImageHeaderParser$** {\n  **[] $VALUES;\n  public *;\n}\n\n# for DexGuard only\n#-keepresourcexmlelements manifest/application/meta-data@value=GlideModule\n################# glide-end ##################\n\n################# immersionbar-start ##################\n-keep class com.gyf.immersionbar.* {*;}\n-dontwarn com.gyf.immersionbar.**\n################# immersionbar-end ##################\n\n################# room-start ##################\n-keep class * extends android.arch.persistence.room.RoomDatabase { *; } # suport\n-keep class * extends androidx.room.RoomDatabase { *; } # androidx\n################# room-end ##################\n\n################# bugly-start ##################\n-dontwarn com.tencent.bugly.**\n-keep public class com.tencent.bugly.**{*;}\n################# bugly-end ##################\n\n################# sqlcipher-start ##################\n-dontwarn net.sqlcipher.**\n-keep class net.sqlcipher.** {*;}\n################# sqlcipher-end ##################\n\n################# wcdb-start ##################\n# https://github.com/Tencent/wcdb/blob/master/android/wcdb/proguard-rules.pro\n# Keep all native methods, their classes and any classes in their descriptors\n-keepclasseswithmembers,includedescriptorclasses class com.tencent.wcdb.** {\n    native <methods>;\n}\n\n# Keep all exception classes\n-keep class com.tencent.wcdb.**.*Exception\n\n# Keep classes referenced in JNI code\n-keep class com.tencent.wcdb.database.WCDBInitializationProbe { <fields>; }\n-keep,includedescriptorclasses class com.tencent.wcdb.database.SQLiteCustomFunction { *; }\n-keep class com.tencent.wcdb.database.SQLiteDebug$* { *; }\n-keep class com.tencent.wcdb.database.SQLiteCipherSpec { <fields>; }\n-keep interface com.tencent.wcdb.support.Log$* { *; }\n\n# Keep methods used as callbacks from JNI code\n-keep class com.tencent.wcdb.repair.RepairKit { int onProgress(java.lang.String, int, long); }\n-keep class com.tencent.wcdb.database.SQLiteConnection {\n    void notifyCheckpoint(java.lang.String, int);\n    void notifyChange(java.lang.String, java.lang.String, long[], long[], long[]);\n}\n################# wcdb-end ##################\n\n################# webdav-start ##################\n-dontwarn org.simpleframework.xml.stream.**\n-keep class org.simpleframework.xml.**{ *; }\n-keepclassmembers,allowobfuscation class * {\n    @org.simpleframework.xml.* <fields>;\n    @org.simpleframework.xml.* <init>(...);\n}\n\n## Sardine Android model classes: needed for XML serialization\n-keep class com.thegrizzlylabs.sardineandroid.model.**{ *; }\n## 防止某些奇葩情况下导致的崩溃问题\n-keep class javax.xml.namespace.**{\n    public <methods>;\n}\n\n## OkHTTP\n-dontwarn okhttp3.internal.platform.ConscryptPlatform\n################# webdav-end ##################\n\n################# arouter-start ##################\n-keep public class com.alibaba.android.arouter.routes.**{*;}\n-keep public class com.alibaba.android.arouter.facade.**{*;}\n-keep class * implements com.alibaba.android.arouter.facade.template.ISyringe{*;}\n\n# 如果使用了 byType 的方式获取 Service，需添加下面规则，保护接口\n-keep interface * implements com.alibaba.android.arouter.facade.template.IProvider\n\n# 如果使用了 单类注入，即不定义接口实现 IProvider，需添加下面规则，保护实现\n# -keep class * implements com.alibaba.android.arouter.facade.template.IProvider\n################# arouter-end ##################\n\n################# xlog-start ##################\n-keep class com.tencent.mars.** { *; }\n################# xlog-end ##################\n\n################# viewbinding-start ##################\n-keep class * implements androidx.viewbinding.ViewBinding {\n  public * inflate(android.view.LayoutInflater, android.view.ViewGroup, boolean);\n}\n################# viewbinding-end ##################\n\n-keep class com.com.lyy.keepassa.baseapi.*{ *; }\n-dontwarn com.com.lyy.keepassa.baseapi.**\n-keep class * implements com.lyy.keepassa.baseapi.INotFreeLibService{ *; }\n-keep class com.lyy.keepassa.view.setting.UISettingFragment\n-keep class com.lyy.keepassa.service.autofill.AutofillService{ *; }"
  },
  {
    "path": "app/schemas/com.lyy.keepassa.dao.AppDatabase/1.json",
    "content": "{\n  \"formatVersion\": 1,\n  \"database\": {\n    \"version\": 1,\n    \"identityHash\": \"2d9d3ab7e899171e967d44eb971ec5bb\",\n    \"entities\": [\n      {\n        \"tableName\": \"OpenDbRecord\",\n        \"createSql\": \"CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `time` INTEGER NOT NULL, `type` TEXT NOT NULL DEFAULT 'AFS', `uri` TEXT NOT NULL, `keyUri` TEXT NOT NULL)\",\n        \"fields\": [\n          {\n            \"fieldPath\": \"uid\",\n            \"columnName\": \"uid\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"time\",\n            \"columnName\": \"time\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"type\",\n            \"columnName\": \"type\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true,\n            \"defaultValue\": \"'AFS'\"\n          },\n          {\n            \"fieldPath\": \"uri\",\n            \"columnName\": \"uri\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"keyUri\",\n            \"columnName\": \"keyUri\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          }\n        ],\n        \"primaryKey\": {\n          \"columnNames\": [\n            \"uid\"\n          ],\n          \"autoGenerate\": true\n        },\n        \"indices\": [],\n        \"foreignKeys\": []\n      },\n      {\n        \"tableName\": \"EntryRecord\",\n        \"createSql\": \"CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `userName` TEXT NOT NULL, `title` TEXT NOT NULL, `uuid` BLOB NOT NULL, `time` INTEGER NOT NULL)\",\n        \"fields\": [\n          {\n            \"fieldPath\": \"uid\",\n            \"columnName\": \"uid\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"userName\",\n            \"columnName\": \"userName\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"title\",\n            \"columnName\": \"title\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"uuid\",\n            \"columnName\": \"uuid\",\n            \"affinity\": \"BLOB\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"time\",\n            \"columnName\": \"time\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          }\n        ],\n        \"primaryKey\": {\n          \"columnNames\": [\n            \"uid\"\n          ],\n          \"autoGenerate\": true\n        },\n        \"indices\": [],\n        \"foreignKeys\": []\n      },\n      {\n        \"tableName\": \"SearchRecord\",\n        \"createSql\": \"CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `title` TEXT NOT NULL, `time` INTEGER NOT NULL)\",\n        \"fields\": [\n          {\n            \"fieldPath\": \"uid\",\n            \"columnName\": \"uid\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"title\",\n            \"columnName\": \"title\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"time\",\n            \"columnName\": \"time\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          }\n        ],\n        \"primaryKey\": {\n          \"columnNames\": [\n            \"uid\"\n          ],\n          \"autoGenerate\": true\n        },\n        \"indices\": [],\n        \"foreignKeys\": []\n      }\n    ],\n    \"views\": [],\n    \"setupQueries\": [\n      \"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)\",\n      \"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '2d9d3ab7e899171e967d44eb971ec5bb')\"\n    ]\n  }\n}"
  },
  {
    "path": "app/schemas/com.lyy.keepassa.dao.AppDatabase/2.json",
    "content": "{\n  \"formatVersion\": 1,\n  \"database\": {\n    \"version\": 2,\n    \"identityHash\": \"60d6ebad122bd7d8a8f58987ecdcd485\",\n    \"entities\": [\n      {\n        \"tableName\": \"DbRecord\",\n        \"createSql\": \"CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `time` INTEGER NOT NULL, `type` TEXT NOT NULL DEFAULT 'AFS', `localDbUri` TEXT NOT NULL, `cloudDiskPath` TEXT, `keyUri` TEXT NOT NULL, `dbName` TEXT NOT NULL)\",\n        \"fields\": [\n          {\n            \"fieldPath\": \"uid\",\n            \"columnName\": \"uid\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"time\",\n            \"columnName\": \"time\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"type\",\n            \"columnName\": \"type\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true,\n            \"defaultValue\": \"'AFS'\"\n          },\n          {\n            \"fieldPath\": \"localDbUri\",\n            \"columnName\": \"localDbUri\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"cloudDiskPath\",\n            \"columnName\": \"cloudDiskPath\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"keyUri\",\n            \"columnName\": \"keyUri\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"dbName\",\n            \"columnName\": \"dbName\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          }\n        ],\n        \"primaryKey\": {\n          \"columnNames\": [\n            \"uid\"\n          ],\n          \"autoGenerate\": true\n        },\n        \"indices\": [],\n        \"foreignKeys\": []\n      },\n      {\n        \"tableName\": \"EntryRecord\",\n        \"createSql\": \"CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `dbFileUri` TEXT NOT NULL, `userName` TEXT NOT NULL, `title` TEXT NOT NULL, `uuid` BLOB NOT NULL, `time` INTEGER NOT NULL)\",\n        \"fields\": [\n          {\n            \"fieldPath\": \"uid\",\n            \"columnName\": \"uid\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"dbFileUri\",\n            \"columnName\": \"dbFileUri\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"userName\",\n            \"columnName\": \"userName\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"title\",\n            \"columnName\": \"title\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"uuid\",\n            \"columnName\": \"uuid\",\n            \"affinity\": \"BLOB\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"time\",\n            \"columnName\": \"time\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          }\n        ],\n        \"primaryKey\": {\n          \"columnNames\": [\n            \"uid\"\n          ],\n          \"autoGenerate\": true\n        },\n        \"indices\": [],\n        \"foreignKeys\": []\n      },\n      {\n        \"tableName\": \"SearchRecord\",\n        \"createSql\": \"CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `title` TEXT NOT NULL, `time` INTEGER NOT NULL)\",\n        \"fields\": [\n          {\n            \"fieldPath\": \"uid\",\n            \"columnName\": \"uid\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"title\",\n            \"columnName\": \"title\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"time\",\n            \"columnName\": \"time\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          }\n        ],\n        \"primaryKey\": {\n          \"columnNames\": [\n            \"uid\"\n          ],\n          \"autoGenerate\": true\n        },\n        \"indices\": [],\n        \"foreignKeys\": []\n      },\n      {\n        \"tableName\": \"CloudServiceInfo\",\n        \"createSql\": \"CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `userName` TEXT, `password` TEXT, `cloudPath` TEXT NOT NULL)\",\n        \"fields\": [\n          {\n            \"fieldPath\": \"uid\",\n            \"columnName\": \"uid\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"userName\",\n            \"columnName\": \"userName\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"password\",\n            \"columnName\": \"password\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"cloudPath\",\n            \"columnName\": \"cloudPath\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          }\n        ],\n        \"primaryKey\": {\n          \"columnNames\": [\n            \"uid\"\n          ],\n          \"autoGenerate\": true\n        },\n        \"indices\": [],\n        \"foreignKeys\": []\n      }\n    ],\n    \"views\": [],\n    \"setupQueries\": [\n      \"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)\",\n      \"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '60d6ebad122bd7d8a8f58987ecdcd485')\"\n    ]\n  }\n}"
  },
  {
    "path": "app/schemas/com.lyy.keepassa.dao.AppDatabase/3.json",
    "content": "{\n  \"formatVersion\": 1,\n  \"database\": {\n    \"version\": 3,\n    \"identityHash\": \"974ef59b197782c4a7327ff2238e3160\",\n    \"entities\": [\n      {\n        \"tableName\": \"DbHistoryRecord\",\n        \"createSql\": \"CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `time` INTEGER NOT NULL, `type` TEXT NOT NULL DEFAULT 'AFS', `localDbUri` TEXT NOT NULL, `cloudDiskPath` TEXT, `keyUri` TEXT NOT NULL, `dbName` TEXT NOT NULL)\",\n        \"fields\": [\n          {\n            \"fieldPath\": \"uid\",\n            \"columnName\": \"uid\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"time\",\n            \"columnName\": \"time\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"type\",\n            \"columnName\": \"type\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true,\n            \"defaultValue\": \"'AFS'\"\n          },\n          {\n            \"fieldPath\": \"localDbUri\",\n            \"columnName\": \"localDbUri\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"cloudDiskPath\",\n            \"columnName\": \"cloudDiskPath\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"keyUri\",\n            \"columnName\": \"keyUri\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"dbName\",\n            \"columnName\": \"dbName\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          }\n        ],\n        \"primaryKey\": {\n          \"columnNames\": [\n            \"uid\"\n          ],\n          \"autoGenerate\": true\n        },\n        \"indices\": [],\n        \"foreignKeys\": []\n      },\n      {\n        \"tableName\": \"EntryRecord\",\n        \"createSql\": \"CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `dbFileUri` TEXT NOT NULL, `userName` TEXT NOT NULL, `title` TEXT NOT NULL, `uuid` BLOB NOT NULL, `time` INTEGER NOT NULL)\",\n        \"fields\": [\n          {\n            \"fieldPath\": \"uid\",\n            \"columnName\": \"uid\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"dbFileUri\",\n            \"columnName\": \"dbFileUri\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"userName\",\n            \"columnName\": \"userName\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"title\",\n            \"columnName\": \"title\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"uuid\",\n            \"columnName\": \"uuid\",\n            \"affinity\": \"BLOB\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"time\",\n            \"columnName\": \"time\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          }\n        ],\n        \"primaryKey\": {\n          \"columnNames\": [\n            \"uid\"\n          ],\n          \"autoGenerate\": true\n        },\n        \"indices\": [],\n        \"foreignKeys\": []\n      },\n      {\n        \"tableName\": \"SearchRecord\",\n        \"createSql\": \"CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `title` TEXT NOT NULL, `time` INTEGER NOT NULL)\",\n        \"fields\": [\n          {\n            \"fieldPath\": \"uid\",\n            \"columnName\": \"uid\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"title\",\n            \"columnName\": \"title\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"time\",\n            \"columnName\": \"time\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          }\n        ],\n        \"primaryKey\": {\n          \"columnNames\": [\n            \"uid\"\n          ],\n          \"autoGenerate\": true\n        },\n        \"indices\": [],\n        \"foreignKeys\": []\n      },\n      {\n        \"tableName\": \"CloudServiceInfo\",\n        \"createSql\": \"CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `userName` TEXT, `password` TEXT, `cloudPath` TEXT NOT NULL)\",\n        \"fields\": [\n          {\n            \"fieldPath\": \"uid\",\n            \"columnName\": \"uid\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"userName\",\n            \"columnName\": \"userName\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"password\",\n            \"columnName\": \"password\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"cloudPath\",\n            \"columnName\": \"cloudPath\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          }\n        ],\n        \"primaryKey\": {\n          \"columnNames\": [\n            \"uid\"\n          ],\n          \"autoGenerate\": true\n        },\n        \"indices\": [],\n        \"foreignKeys\": []\n      },\n      {\n        \"tableName\": \"QuickUnLockRecord\",\n        \"createSql\": \"CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `dbUri` TEXT NOT NULL, `dbPass` TEXT NOT NULL, `keyPath` TEXT, `isUseKey` INTEGER NOT NULL, `isUseFingerprint` INTEGER NOT NULL, `passIv` BLOB)\",\n        \"fields\": [\n          {\n            \"fieldPath\": \"uid\",\n            \"columnName\": \"uid\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"dbUri\",\n            \"columnName\": \"dbUri\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"dbPass\",\n            \"columnName\": \"dbPass\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"keyPath\",\n            \"columnName\": \"keyPath\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"isUseKey\",\n            \"columnName\": \"isUseKey\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"isUseFingerprint\",\n            \"columnName\": \"isUseFingerprint\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"passIv\",\n            \"columnName\": \"passIv\",\n            \"affinity\": \"BLOB\",\n            \"notNull\": false\n          }\n        ],\n        \"primaryKey\": {\n          \"columnNames\": [\n            \"uid\"\n          ],\n          \"autoGenerate\": true\n        },\n        \"indices\": [],\n        \"foreignKeys\": []\n      }\n    ],\n    \"views\": [],\n    \"setupQueries\": [\n      \"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)\",\n      \"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '974ef59b197782c4a7327ff2238e3160')\"\n    ]\n  }\n}"
  },
  {
    "path": "app/schemas/com.lyy.keepassa.dao.AppDatabase/4.json",
    "content": "{\n  \"formatVersion\": 1,\n  \"database\": {\n    \"version\": 4,\n    \"identityHash\": \"974ef59b197782c4a7327ff2238e3160\",\n    \"entities\": [\n      {\n        \"tableName\": \"DbHistoryRecord\",\n        \"createSql\": \"CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `time` INTEGER NOT NULL, `type` TEXT NOT NULL DEFAULT 'AFS', `localDbUri` TEXT NOT NULL, `cloudDiskPath` TEXT, `keyUri` TEXT NOT NULL, `dbName` TEXT NOT NULL)\",\n        \"fields\": [\n          {\n            \"fieldPath\": \"uid\",\n            \"columnName\": \"uid\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"time\",\n            \"columnName\": \"time\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"type\",\n            \"columnName\": \"type\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true,\n            \"defaultValue\": \"'AFS'\"\n          },\n          {\n            \"fieldPath\": \"localDbUri\",\n            \"columnName\": \"localDbUri\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"cloudDiskPath\",\n            \"columnName\": \"cloudDiskPath\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"keyUri\",\n            \"columnName\": \"keyUri\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"dbName\",\n            \"columnName\": \"dbName\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          }\n        ],\n        \"primaryKey\": {\n          \"columnNames\": [\n            \"uid\"\n          ],\n          \"autoGenerate\": true\n        },\n        \"indices\": [],\n        \"foreignKeys\": []\n      },\n      {\n        \"tableName\": \"EntryRecord\",\n        \"createSql\": \"CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `dbFileUri` TEXT NOT NULL, `userName` TEXT NOT NULL, `title` TEXT NOT NULL, `uuid` BLOB NOT NULL, `time` INTEGER NOT NULL)\",\n        \"fields\": [\n          {\n            \"fieldPath\": \"uid\",\n            \"columnName\": \"uid\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"dbFileUri\",\n            \"columnName\": \"dbFileUri\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"userName\",\n            \"columnName\": \"userName\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"title\",\n            \"columnName\": \"title\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"uuid\",\n            \"columnName\": \"uuid\",\n            \"affinity\": \"BLOB\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"time\",\n            \"columnName\": \"time\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          }\n        ],\n        \"primaryKey\": {\n          \"columnNames\": [\n            \"uid\"\n          ],\n          \"autoGenerate\": true\n        },\n        \"indices\": [],\n        \"foreignKeys\": []\n      },\n      {\n        \"tableName\": \"SearchRecord\",\n        \"createSql\": \"CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `title` TEXT NOT NULL, `time` INTEGER NOT NULL)\",\n        \"fields\": [\n          {\n            \"fieldPath\": \"uid\",\n            \"columnName\": \"uid\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"title\",\n            \"columnName\": \"title\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"time\",\n            \"columnName\": \"time\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          }\n        ],\n        \"primaryKey\": {\n          \"columnNames\": [\n            \"uid\"\n          ],\n          \"autoGenerate\": true\n        },\n        \"indices\": [],\n        \"foreignKeys\": []\n      },\n      {\n        \"tableName\": \"CloudServiceInfo\",\n        \"createSql\": \"CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `userName` TEXT, `password` TEXT, `cloudPath` TEXT NOT NULL)\",\n        \"fields\": [\n          {\n            \"fieldPath\": \"uid\",\n            \"columnName\": \"uid\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"userName\",\n            \"columnName\": \"userName\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"password\",\n            \"columnName\": \"password\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"cloudPath\",\n            \"columnName\": \"cloudPath\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          }\n        ],\n        \"primaryKey\": {\n          \"columnNames\": [\n            \"uid\"\n          ],\n          \"autoGenerate\": true\n        },\n        \"indices\": [],\n        \"foreignKeys\": []\n      },\n      {\n        \"tableName\": \"QuickUnLockRecord\",\n        \"createSql\": \"CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `dbUri` TEXT NOT NULL, `dbPass` TEXT NOT NULL, `keyPath` TEXT, `isUseKey` INTEGER NOT NULL, `isUseFingerprint` INTEGER NOT NULL, `passIv` BLOB)\",\n        \"fields\": [\n          {\n            \"fieldPath\": \"uid\",\n            \"columnName\": \"uid\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"dbUri\",\n            \"columnName\": \"dbUri\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"dbPass\",\n            \"columnName\": \"dbPass\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"keyPath\",\n            \"columnName\": \"keyPath\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"isUseKey\",\n            \"columnName\": \"isUseKey\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"isUseFingerprint\",\n            \"columnName\": \"isUseFingerprint\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"passIv\",\n            \"columnName\": \"passIv\",\n            \"affinity\": \"BLOB\",\n            \"notNull\": false\n          }\n        ],\n        \"primaryKey\": {\n          \"columnNames\": [\n            \"uid\"\n          ],\n          \"autoGenerate\": true\n        },\n        \"indices\": [],\n        \"foreignKeys\": []\n      }\n    ],\n    \"views\": [],\n    \"setupQueries\": [\n      \"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)\",\n      \"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '974ef59b197782c4a7327ff2238e3160')\"\n    ]\n  }\n}"
  },
  {
    "path": "app/src/androidTest/java/com/lyy/keepassa/AutoFillTest.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa\n\nimport androidx.autofill.HintConstants\nimport androidx.test.ext.junit.runners.AndroidJUnit4\nimport org.junit.Test\nimport org.junit.runner.RunWith\nimport kotlin.reflect.full.memberProperties\n\n@RunWith(AndroidJUnit4::class)\nclass AutoFillTest {\n\n  @Test\n  fun getMember() {\n    val hintClazz = HintConstants::class\n    hintClazz.memberProperties.forEach { member->\n      println(\"${member.name} -> ${member.call()}\")\n    }\n  }\n}"
  },
  {
    "path": "app/src/androidTest/java/com/lyy/keepassa/ComposeKeeTrayTotpTest.kt",
    "content": "package com.lyy.keepassa\n\nimport androidx.test.ext.junit.runners.AndroidJUnit4\nimport com.keepassdroid.database.PwEntryV4\nimport com.keepassdroid.database.security.ProtectedString\nimport com.lyy.keepassa.util.totp.ComposeKeeTrayTotp\nimport org.junit.Test\nimport org.junit.runner.RunWith\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 1:50 PM 2024/2/22\n **/\n@RunWith(AndroidJUnit4::class)\nclass ComposeKeeTrayTotpTest {\n\n  @Test\n  fun testGetOtpPass() {\n    val entry = PwEntryV4()\n    entry.strings = hashMapOf<String?, ProtectedString?>().apply {\n      put(\"STR_URL\", ProtectedString(false, \"sss\"))\n      put(\"TOTP Settings\", ProtectedString(false, \"30;6\"))\n      put(\"TOTP Seed\", ProtectedString(true, \"123\"))\n    }\n\n    val token = ComposeKeeTrayTotp.getOtpPass(entry)\n    println(\"token = $token\")\n  }\n}"
  },
  {
    "path": "app/src/androidTest/java/com/lyy/keepassa/ComposeKeepassTest.kt",
    "content": "package com.lyy.keepassa\n\nimport androidx.test.ext.junit.runners.AndroidJUnit4\nimport com.lyy.keepassa.entity.TimeOtp2Bean\nimport com.lyy.keepassa.util.totp.ComposeKeepass\nimport com.lyy.keepassa.util.totp.SecretHexType\nimport com.lyy.keepassa.util.totp.TokenCalculator.HashAlgorithm\nimport io.mockk.MockKAnnotations\nimport io.mockk.every\nimport io.mockk.impl.annotations.MockK\nimport io.mockk.mockk\nimport org.junit.Before\nimport org.junit.Test\nimport org.junit.runner.RunWith\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 9:57 AM 2024/2/22\n **/\n@RunWith(AndroidJUnit4::class)\nclass ComposeKeepassTest {\n\n  // @MockK\n  // lateinit var timeOtp2Bean: TimeOtp2Bean\n\n  @Before\n  fun setUp() {\n    // 在每个测试方法开始前初始化所有标注了@MockK的字段\n    MockKAnnotations.init(this)\n  }\n\n  @Test\n  fun testHandleTotp() {\n    val timeOtp2Bean = mockk<TimeOtp2Bean>()\n    every { timeOtp2Bean.secret } returns \"123\"\n    every { timeOtp2Bean.secretType } returns SecretHexType.BASE_32\n    every { timeOtp2Bean.period } returns 30\n    every { timeOtp2Bean.digits } returns 6\n    every { timeOtp2Bean.algorithm } returns HashAlgorithm.SHA1\n    val instance = ComposeKeepass::class.java.getField(\"INSTANCE\").get(null)\n    val method =\n      ComposeKeepass::class.java.getDeclaredMethod(\"handleTotp\", TimeOtp2Bean::class.java)\n    method.isAccessible = true\n    val token = method.invoke(instance, timeOtp2Bean)\n\n    println(\"otp: $token\")\n  }\n}"
  },
  {
    "path": "app/src/androidTest/java/com/lyy/keepassa/KeepassDbTest.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa\n\nimport android.content.Context\nimport android.net.Uri\nimport android.util.Log\nimport androidx.test.core.app.ApplicationProvider\nimport androidx.test.ext.junit.runners.AndroidJUnit4\nimport com.keepassdroid.Database\nimport com.keepassdroid.database.PwEntry\nimport com.keepassdroid.database.PwEntryV4\nimport com.keepassdroid.database.PwGroup\nimport com.keepassdroid.database.PwGroupId\nimport com.keepassdroid.database.PwGroupV4\nimport com.keepassdroid.database.helper.CreateDBHelper\nimport com.keepassdroid.database.helper.KDBHandlerHelper\nimport com.lyy.keepassa.util.QuickUnLockUtil\nimport org.junit.Test\nimport org.junit.runner.RunWith\nimport java.io.File\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 KeepassDbTest {\n  private val TAG = \"KeepassDbTest\"\n  private val context: Context = ApplicationProvider.getApplicationContext()\n\n//  private val dbName = \"test.kdbx\"\n//  private val pass = \"123456\"\n//  private val keyName = \"db.key\"\n  private val dbName = \"yuyu_pw_db.kdbx\"\n  private val pass = \"Xiaotaiyan123\"\n  private val keyName = \"yuyu_pw_db.key\"\n\n  @Test\n  fun useAppContext() {\n    Log.d(TAG, \"pkgName = ${context.packageName}\")\n    val f = context.filesDir\n    Log.d(TAG, \"fileName = ${f.name}\")\n//    EditHelper.getInstance(context)\n//        .openDb(\"test.kdbx\", f.path, \"123456\", \"/Users/aria/Downloads/kpa/db.key\")\n  }\n\n  @Test\n  fun createDb() {\n    val dir = context.filesDir\n    val cdb = CreateDBHelper(context, dbName, Uri.fromFile(File(dir, dbName)))\n    cdb.setPass(pass, null)\n    cdb.setKeyFile(Uri.fromFile(File(dir, keyName)))\n    val db = cdb.build()\n    // 创建完成后可以直接读取数据库\n    Log.d(TAG, \"create db ${if (db != null) \"success\" else \"fail\"}\")\n//    if (db != null) {\n//      readDb(db)\n//    }\n  }\n\n  @Test\n  fun openDb() {\n    val dir = context.filesDir.path + \"/\"\n    val db = KDBHandlerHelper.getInstance(context)\n        .openDb(dbName, dir + dbName, pass, dir + keyName)\n//        .openDb(dbName, dir + dbName, pass, null)\n    Log.d(TAG, \"open db ${if (db != null) \"success\" else \"fail\"}\")\n//    if (db != null) {\n//      readDb(db)\n//    }\n  }\n\n  /**\n   * 增加组\n   */\n  @Test\n  fun addGroup() {\n//    val db = getDb()\n//    val group = KDBHandlerHelper.getInstance(context)\n//        .createGroup(db, \"test\", 45, null)\n//    Log.d(TAG, \"创建组：${if (group != null) \"成功\" else \"失败\"}\")\n//    readDb(db)\n  }\n\n  @Test\n  fun addEntry() {\n    val db = getDb()\n    val entry = PwEntryV4()\n  }\n\n  @Test\n  fun testCustomData() {\n    val dir = context.filesDir\n    val cdb = CreateDBHelper(context, dbName, Uri.fromFile(File(dir, dbName)))\n    cdb.setPass(pass, null)\n    cdb.setKeyFile(Uri.fromFile(File(dir, keyName)))\n    val db = cdb.build()\n    // 创建自定义数据\n    val entryV4 = PwEntryV4()\n    entryV4.parent = db.pm.rootGroup as PwGroupV4\n    entryV4.setString(PwEntryV4.STR_TITLE, \"title\", false)\n    entryV4.customData[\"sss\"] = \"ggggg\"\n    KDBHandlerHelper.getInstance(context).saveEntry(db, entryV4)\n  }\n\n  private fun getDb(): Database {\n    val dir = context.filesDir.path + \"/\"\n    return KDBHandlerHelper.getInstance(context)\n        .openDb(dbName, dir + dbName, pass, dir + keyName)\n  }\n\n  /**\n   * 读取数据库信息\n   */\n  private fun readDb(db: Database) {\n    val pm = db.pm\n    for (i in pm.groups) {\n      readGroup(i.key, i.value)\n    }\n    for (i in pm.entries) {\n      readEntry(i.value)\n    }\n  }\n\n  /**\n   * 读取组信息\n   */\n  private fun readGroup(\n    id: PwGroupId,\n    group: PwGroup\n  ) {\n    Log.d(\n        TAG, \"groupId = ${id}, groupName = ${group.name}, \" +\n        \"parent = ${if (group.parent == null) \"root\" else group.parent.name}\"\n    )\n    for (childGroup in group.childGroups) {\n      readGroup(childGroup.id, childGroup)\n    }\n    for (entry in group.childEntries) {\n      readEntry(entry)\n    }\n  }\n\n  /**\n   * 读取条目\n   */\n  private fun readEntry(entry: PwEntry) {\n    Log.d(\n        TAG,\n        \"userName = ${entry.username}, url = ${entry.url}, \" +\n            \"title = ${entry.title}, des = ${entry.notes}, \" +\n            \"parent = ${entry.parent}, password = ${entry.password}\"\n    )\n  }\n\n  /**\n   * 测试加密解密\n   */\n  @Test\n  fun encryptStr(){\n    val eStr = QuickUnLockUtil.encryptStr(\"gaJj07uacPAAAAAAAAAANB5oOxdcoY0cwIsjYnwH1yPyws3YUyciD_nfBMoabgG7\")\n    println(\"加密字符串：$eStr\")\n//    val dStr = QuickUnLockUtil.decryption(\"gaJj07uacPAAAAAAAAAANB5oOxdcoY0cwIsjYnwH1yPyws3YUyciD_nfBMoabgG7\")\n    val dStr = QuickUnLockUtil.decryption(eStr)\n    println(\"解密字符串：$dStr\")\n  }\n\n}"
  },
  {
    "path": "app/src/androidTest/java/com/lyy/keepassa/UrlTest.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa\n\nimport android.net.Uri\nimport androidx.test.ext.junit.runners.AndroidJUnit4\nimport com.arialyy.frame.util.RegularRule\nimport org.junit.Test\nimport org.junit.runner.RunWith\nimport java.io.IOException\nimport java.net.URL\n\n@RunWith(AndroidJUnit4::class)\nclass UrlTest {\n\n  @Test\n  fun domainTest(){\n    val temp = \"twitter.com\"\n    val topDomain = Regex(RegularRule.DOMAIN_TOP, RegexOption.IGNORE_CASE).find(temp)\n    println(\"topDomain = ${topDomain?.value}\")\n  }\n\n  @Test\n  fun testUrl(){\n    try {\n      val url = URL(\"http://www.runoob.com/ssss/ssxxx/index.html?language=cn#j2se\")\n      System.out.println(\"URL 为：\" + url.toString())\n      System.out.println(\"协议为：\" + url.getProtocol())\n      System.out.println(\"验证信息：\" + url.getAuthority())\n      System.out.println(\"文件名及请求参数：\" + url.getFile())\n      System.out.println(\"主机名：\" + url.getHost())\n      System.out.println(\"路径：\" + url.getPath())\n      System.out.println(\"端口：\" + url.getPort())\n      System.out.println(\"默认端口：\" + url.getDefaultPort())\n      System.out.println(\"请求参数：\" + url.getQuery())\n      System.out.println(\"定位位置：\" + url.getRef())\n    } catch (e: IOException) {\n      e.printStackTrace()\n    }\n  }\n\n  @Test\n  fun testUri(){\n    try {\n      val url = Uri.parse(\"http://www.runoob.com/ssss/ssxxx/index.html?language=cn&q=1#j2se\")\n      println(\"URL 为：$url\")\n      println(\"协议为：\" + url.scheme)\n      println(\"验证信息：\" + url.authority)\n      println(\"文件名及请求参数：\" + url.query)\n      println(\"主机名：\" + url.host)\n      println(\"路径：\" + url.path)\n      println(\"最后一段路径：\" + url.lastPathSegment)\n      println(\"端口：\" + url.port)\n      println(\"请求参数：\" + url.query)\n      println(\"请求参数key：\" + url.queryParameterNames)\n      println(\"请求参数language：\" + url.getQueryParameters(\"language\"))\n      println(\"定位位置：\" + url.fragment)\n    } catch (e: IOException) {\n      e.printStackTrace()\n    }\n  }\n}"
  },
  {
    "path": "app/src/androidTest/java/com/lyy/keepassa/UtilTest.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa\n\nimport com.arialyy.frame.util.KeyStoreUtil\nimport com.blankj.utilcode.util.EncryptUtils\nimport com.lyy.keepassa.view.StorageType\nimport org.junit.Test\n\nclass UtilTest {\n\n  @Test\n  fun emunTEst(){\n    println(StorageType.AFS.name)\n  }\n\n  @Test\n  fun keyStoreUtil(){\n    val keyStoreUtil = KeyStoreUtil()\n\n    val p = keyStoreUtil.encryptData(keyStoreUtil.getEncryptCipher(), \"123456\")\n    println(\"密文： ${p.first}\")\n    val end = keyStoreUtil.decryptData(keyStoreUtil.getDecryptCipher(p.second), p.first)\n    println(\"明文：$end\")\n\n  }\n}"
  },
  {
    "path": "app/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    package=\"com.lyy.keepassa\">\n\n  <uses-permission android:name=\"android.permission.USE_FULL_SCREEN_INTENT\" />\n  <uses-permission android:name=\"android.permission.ACTION_OPEN_DOCUMENT\" />\n  <uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\" />\n  <uses-permission android:name=\"android.permission.INTERNET\" />\n  <uses-permission android:name=\"android.permission.VIBRATE\" />\n  <uses-permission android:name=\"android.permission.FOREGROUND_SERVICE\" />\n\n  <application\n      android:name=\".base.BaseApp\"\n      android:allowBackup=\"true\"\n      android:extractNativeLibs=\"true\"\n      android:hardwareAccelerated=\"true\"\n      android:icon=\"@mipmap/ic_launcher\"\n      android:label=\"@string/app_name\"\n      android:networkSecurityConfig=\"@xml/network_security_config\"\n      android:roundIcon=\"@mipmap/ic_launcher_round\"\n      android:supportsRtl=\"true\"\n      android:theme=\"@style/AppTheme.NoActionBar\"\n      tools:ignore=\"GoogleAppIndexingWarning\">\n    <activity\n        android:exported=\"true\"\n        android:name=\".view.launcher.LauncherActivity\"\n        android:configChanges=\"layoutDirection|locale\"\n        android:screenOrientation=\"portrait\"\n        android:launchMode=\"singleTask\"\n        android:windowSoftInputMode=\"adjustResize|stateVisible\">\n      <!-- taskAffinity 指定activity栈，可以使用finishAffinity关闭该栈中的activity -->\n      <!--        android:windowSoftInputMode=\"stateVisible|adjustResize\"-->\n      <intent-filter>\n        <action android:name=\"android.intent.action.MAIN\" />\n        <category android:name=\"android.intent.category.LAUNCHER\" />\n      </intent-filter>\n\n      <intent-filter>\n        <action android:name=\"android.intent.action.VIEW\" />\n\n        <category android:name=\"android.intent.category.DEFAULT\" />\n        <category android:name=\"android.intent.category.BROWSABLE\" />\n\n        <data android:scheme=\"file\" />\n        <data android:mimeType=\"*/*\" />\n        <data android:host=\"*\" />\n        <data android:pathPattern=\".*\\\\.kdb\" />\n        <data android:pathPattern=\".*\\\\..*\\\\.kdb\" />\n        <data android:pathPattern=\".*\\\\..*\\\\..*\\\\.kdb\" />\n        <data android:pathPattern=\".*\\\\..*\\\\..*\\\\..*\\\\.kdb\" />\n        <data android:pathPattern=\".*\\\\..*\\\\..*\\\\..*\\\\..*\\\\.kdb\" />\n        <data android:pathPattern=\".*\\\\..*\\\\..*\\\\..*\\\\..*\\\\..*\\\\.kdb\" />\n        <data android:pathPattern=\".*\\\\..*\\\\..*\\\\..*\\\\..*\\\\..*\\\\..*\\\\.kdb\" />\n        <data android:pathPattern=\".*\\\\..*\\\\..*\\\\..*\\\\..*\\\\..*\\\\..*\\\\..*\\\\.kdb\" />\n        <data android:pathPattern=\".*\\\\..*\\\\..*\\\\..*\\\\..*\\\\..*\\\\..*\\\\..*\\\\..*\\\\.kdb\" />\n        <data android:pathPattern=\".*\\\\..*\\\\..*\\\\..*\\\\..*\\\\..*\\\\..*\\\\..*\\\\..*\\\\..*\\\\.kdb\" />\n        <data android:pathPattern=\".*\\\\.kdbx\" />\n        <data android:pathPattern=\".*\\\\..*\\\\.kdbx\" />\n        <data android:pathPattern=\".*\\\\..*\\\\..*\\\\.kdbx\" />\n        <data android:pathPattern=\".*\\\\..*\\\\..*\\\\..*\\\\.kdbx\" />\n        <data android:pathPattern=\".*\\\\..*\\\\..*\\\\..*\\\\..*\\\\.kdbx\" />\n        <data android:pathPattern=\".*\\\\..*\\\\..*\\\\..*\\\\..*\\\\..*\\\\.kdbx\" />\n        <data android:pathPattern=\".*\\\\..*\\\\..*\\\\..*\\\\..*\\\\..*\\\\..*\\\\.kdbx\" />\n        <data android:pathPattern=\".*\\\\..*\\\\..*\\\\..*\\\\..*\\\\..*\\\\..*\\\\..*\\\\.kdbx\" />\n        <data android:pathPattern=\".*\\\\..*\\\\..*\\\\..*\\\\..*\\\\..*\\\\..*\\\\..*\\\\..*\\\\.kdbx\" />\n        <data android:pathPattern=\".*\\\\..*\\\\..*\\\\..*\\\\..*\\\\..*\\\\..*\\\\..*\\\\..*\\\\..*\\\\.kdbx\" />\n      </intent-filter>\n\n      <intent-filter tools:ignore=\"AppLinkUrlError\">\n        <action android:name=\"android.intent.action.VIEW\" />\n\n        <category android:name=\"android.intent.category.DEFAULT\" />\n        <category android:name=\"android.intent.category.BROWSABLE\" />\n\n        <data android:scheme=\"file\" />\n        <data android:scheme=\"content\" />\n        <data android:mimeType=\"application/octet-stream\" />\n        <data android:mimeType=\"application/x-kdb\" />\n        <data android:mimeType=\"application/x-kdbx\" />\n        <data android:mimeType=\"application/x-keepass\" />\n        <data android:host=\"*\" />\n        <data android:pathPattern=\".*\" />\n        <data android:pathPattern=\".*\\\\.*\" />\n        <data android:pathPattern=\".*\\\\..*\\\\.*\" />\n        <data android:pathPattern=\".*\\\\..*\\\\..*\\\\.*\" />\n        <data android:pathPattern=\".*\\\\..*\\\\..*\\\\..*\\\\.*\" />\n        <data android:pathPattern=\".*\\\\..*\\\\..*\\\\..*\\\\..*\\\\.*\" />\n        <data android:pathPattern=\".*\\\\..*\\\\..*\\\\..*\\\\..*\\\\..*\\\\.*\" />\n        <data android:pathPattern=\".*\\\\..*\\\\..*\\\\..*\\\\..*\\\\..*\\\\..*\\\\.*\" />\n        <data android:pathPattern=\".*\\\\..*\\\\..*\\\\..*\\\\..*\\\\..*\\\\..*\\\\..*\\\\.*\" />\n        <data android:pathPattern=\".*\\\\..*\\\\..*\\\\..*\\\\..*\\\\..*\\\\..*\\\\..*\\\\..*\\\\.*\" />\n        <data android:pathPattern=\".*\\\\..*\\\\..*\\\\..*\\\\..*\\\\..*\\\\..*\\\\..*\\\\..*\\\\..*\\\\.*\" />\n      </intent-filter>\n\n      <!--      桌面快捷菜单-->\n      <meta-data\n          android:name=\"android.app.shortcuts\"\n          android:resource=\"@xml/shortcuts\" />\n    </activity>\n\n    <activity\n        android:name=\".view.search.CommonSearchActivity\"\n        android:launchMode=\"singleInstance\"\n        android:screenOrientation=\"portrait\"\n        android:theme=\"@style/DialogActivityStyle\" />\n\n\n    <activity\n        android:name=\".service.input.EntryOtherInfoDialog\"\n        android:launchMode=\"singleInstance\"\n        android:screenOrientation=\"portrait\"\n        android:theme=\"@style/DialogActivityStyle\" />\n\n    <activity\n        android:name=\".view.search.AutoFillEntrySearchActivity\"\n        android:screenOrientation=\"portrait\" />\n\n    <activity\n        android:name=\".view.create.CreateDbActivity\"\n        android:launchMode=\"singleTask\"\n        android:screenOrientation=\"portrait\" />\n\n    <activity\n        android:name=\".view.main.MainActivity\"\n        android:launchMode=\"singleTask\"\n        android:screenOrientation=\"portrait\" />\n\n    <activity\n        android:name=\".view.MarkDownEditorActivity\"\n        android:screenOrientation=\"portrait\"\n        android:windowSoftInputMode=\"stateVisible|adjustResize\" />\n    <activity\n        android:name=\".view.main.MainSettingActivity\"\n        android:screenOrientation=\"portrait\" />\n    <activity\n        android:name=\".view.detail.GroupDetailActivity\"\n        android:screenOrientation=\"portrait\" />\n    <activity\n        android:name=\".view.detail.EntryDetailActivity\"\n        android:screenOrientation=\"portrait\" />\n    <activity\n        android:name=\".view.detail.EntryDetailActivityNew\"\n        android:screenOrientation=\"portrait\" />\n    <activity\n        android:name=\".view.dir.ChooseGroupActivity\"\n        android:screenOrientation=\"portrait\" />\n    <activity\n        android:name=\".view.setting.SettingActivity\"\n        android:screenOrientation=\"portrait\" />\n    <activity\n        android:name=\".view.fingerprint.FingerprintActivity\"\n        android:screenOrientation=\"portrait\" />\n    <activity\n        android:name=\".view.create.CreateEntryActivityOld\"\n        android:screenOrientation=\"portrait\" />\n\n    <activity\n        android:name=\".view.create.entry.CreateEntryActivity\"\n        android:screenOrientation=\"portrait\" />\n    <activity\n        android:name=\".view.create.GeneratePassActivity\"\n        android:screenOrientation=\"portrait\" />\n    <activity\n        android:name=\".view.main.QuickUnlockActivity\"\n        android:launchMode=\"singleTask\"\n        android:screenOrientation=\"portrait\" />\n    <activity\n        android:name=\".view.launcher.OpenDbHistoryActivity\"\n        android:screenOrientation=\"portrait\" />\n\n    <!-- dropbox -->\n    <activity\n        android:exported=\"true\"\n        android:name=\"com.dropbox.core.android.AuthActivity\"\n        android:configChanges=\"orientation|keyboard\"\n        android:launchMode=\"singleTask\"\n        android:screenOrientation=\"portrait\">\n      <intent-filter>\n\n        <!-- Change this to be db- followed by your app key -->\n        <data android:scheme=\"db-ib45r6jnfz3oakq\" />\n\n        <action android:name=\"android.intent.action.VIEW\" />\n\n        <category android:name=\"android.intent.category.BROWSABLE\" />\n        <category android:name=\"android.intent.category.DEFAULT\" />\n      </intent-filter>\n    </activity>\n\n    <!--   onedrive -->\n    <activity\n        android:exported=\"true\"\n        android:name=\"com.microsoft.identity.client.BrowserTabActivity\"\n        android:screenOrientation=\"portrait\">\n      <intent-filter>\n        <action android:name=\"android.intent.action.VIEW\" />\n\n        <category android:name=\"android.intent.category.DEFAULT\" />\n        <category android:name=\"android.intent.category.BROWSABLE\" />\n\n        <data\n            android:host=\"msal_package_name\"\n            android:path=\"/msal_signature_hash\"\n            android:scheme=\"msauth\" />\n      </intent-filter>\n    </activity>\n\n    <activity\n        android:exported=\"true\"\n        android:name=\".router.DeeplinkActivity\"\n        android:noHistory=\"true\">\n      <intent-filter>\n        <action android:name=\"android.intent.action.VIEW\" />\n\n        <category android:name=\"android.intent.category.DEFAULT\" />\n        <category android:name=\"android.intent.category.BROWSABLE\" />\n\n        <data\n            android:host=\"com.lyy.keepassa\"\n            android:scheme=\"kpa-router\" />\n      </intent-filter>\n    </activity>\n\n    <activity\n        android:name=\".view.QrCodeScannerActivity\"\n        android:screenOrientation=\"portrait\" />\n\n    <activity\n        android:name=\".view.collection.CollectionActivity\"\n        android:screenOrientation=\"portrait\" />\n\n    <service\n        android:exported=\"true\"\n        android:name=\".service.autofill.AutoFillService\"\n        android:permission=\"android.permission.BIND_AUTOFILL_SERVICE\">\n      <meta-data\n          android:name=\"android.autofill\"\n          android:resource=\"@xml/auto_fill_service_configuration\" />\n      <intent-filter>\n        <action android:name=\"android.service.autofill.AutofillService\" />\n      </intent-filter>\n    </service>\n\n    <service android:name=\".service.DbOpenNotificationService\" />\n\n    <service\n        android:exported=\"true\"\n        android:name=\".service.input.InputIMEService\"\n        android:label=\"@string/ime_label\"\n        android:permission=\"android.permission.BIND_INPUT_METHOD\">\n      <intent-filter>\n        <action android:name=\"android.view.InputMethod\" />\n      </intent-filter>\n      <meta-data\n          android:name=\"android.view.im\"\n          android:resource=\"@xml/input_method\" />\n    </service>\n\n\n    <provider\n        android:name=\"androidx.core.content.FileProvider\"\n        android:authorities=\"${applicationId}.provider\"\n        android:exported=\"false\"\n        android:grantUriPermissions=\"true\">\n      <meta-data\n          android:name=\"android.support.FILE_PROVIDER_PATHS\"\n          android:resource=\"@xml/aria_fileprovider_paths\" />\n    </provider>\n\n    <!--适配华为（huawei）刘海屏-->\n    <meta-data\n        android:name=\"android.notch_support\"\n        android:value=\"true\" />\n    <!--适配小米（xiaomi）刘海屏-->\n    <meta-data\n        android:name=\"notch.config\"\n        android:value=\"portrait|landscape\" />\n    <!--全面屏-->\n    <meta-data\n        android:name=\"android.max_aspect\"\n        android:value=\"2.4\" />\n\n    <meta-data\n        android:name=\"design_width_in_dp\"\n        android:value=\"411\" />\n    <meta-data\n        android:name=\"design_height_in_dp\"\n        android:value=\"731\" />\n\n    <meta-data\n        android:name=\"host_app\"\n        android:value=\"app\" />\n  </application>\n\n</manifest>"
  },
  {
    "path": "app/src/main/assets/fingerprint_anim.json",
    "content": "{\"v\":\"4.6.3\",\"fr\":25,\"ip\":0,\"op\":39,\"w\":650,\"h\":230,\"nm\":\"Fingerprint\",\"ddd\":0,\"assets\":[],\"layers\":[{\"ddd\":0,\"ind\":1,\"ty\":4,\"nm\":\"Shape Layer 3\",\"ks\":{\"o\":{\"a\":0,\"k\":100},\"r\":{\"a\":0,\"k\":0},\"p\":{\"a\":0,\"k\":[325,115,0]},\"a\":{\"a\":0,\"k\":[0,0,0]},\"s\":{\"a\":0,\"k\":[100,100,100]}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[20.666,6.495],[-24.5,6],[-12.496,-7.37],[4.5,18.5],[32.5,-6.5],[-19,-58]],\"o\":[[-52.5,-16.5],[28.289,-6.928],[19.5,11.5],[-7.796,-32.05],[-27.263,5.453],[19.074,58.225]],\"v\":[[26,78],[-6.5,1.5],[30,38.5],[60.5,9.5],[-15.5,-36],[-55,53.5]],\"c\":false}},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\"},{\"ty\":\"st\",\"c\":{\"a\":0,\"k\":[0.3058823529411765,0.5215686274509804,0.8588235294117647,1]},\"o\":{\"a\":0,\"k\":100},\"w\":{\"a\":0,\"k\":9},\"lc\":2,\"lj\":1,\"ml\":4,\"nm\":\"Stroke 1\",\"mn\":\"ADBE Vector Graphic - Stroke\"},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[0,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Shape 1\",\"np\":3,\"cix\":2,\"ix\":1,\"mn\":\"ADBE Vector Group\"},{\"ty\":\"tm\",\"s\":{\"a\":0,\"k\":0,\"ix\":1},\"e\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0],\"y\":[1]},\"o\":{\"x\":[0.333],\"y\":[0]},\"n\":[\"0_1_0p333_0\"],\"t\":1,\"s\":[0],\"e\":[100]},{\"t\":28}],\"ix\":2},\"o\":{\"a\":0,\"k\":0,\"ix\":3},\"m\":1,\"ix\":2,\"nm\":\"Trim Paths 1\",\"mn\":\"ADBE Vector Filter - Trim\"}],\"ip\":0,\"op\":39,\"st\":0,\"bm\":0,\"sr\":1},{\"ddd\":0,\"ind\":2,\"ty\":4,\"nm\":\"Shape Layer 2\",\"ks\":{\"o\":{\"a\":0,\"k\":100},\"r\":{\"a\":0,\"k\":0},\"p\":{\"a\":0,\"k\":[325,115,0]},\"a\":{\"a\":0,\"k\":[0,0,0]},\"s\":{\"a\":0,\"k\":[100,100,100]}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[24,-3.5],[3,40.5]],\"o\":[[-24,3.5],[-3,-40.5]],\"v\":[[51.5,58.5],[-1,17.5]],\"c\":false}},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\"},{\"ty\":\"st\",\"c\":{\"a\":0,\"k\":[0.3058823529411765,0.5215686274509804,0.8588235294117647,1]},\"o\":{\"a\":0,\"k\":100},\"w\":{\"a\":0,\"k\":9},\"lc\":2,\"lj\":1,\"ml\":4,\"nm\":\"Stroke 1\",\"mn\":\"ADBE Vector Graphic - Stroke\"},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[0,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[102.868,99.902],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Shape 2\",\"np\":3,\"cix\":2,\"ix\":1,\"mn\":\"ADBE Vector Group\"},{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[43.527,56.362],[27.5,-42.5]],\"o\":[[-39,-50.5],[-27.5,42.5]],\"v\":[[68.5,-22.5],[-69,-20.5]],\"c\":false}},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\"},{\"ty\":\"st\",\"c\":{\"a\":0,\"k\":[0.3058823529411765,0.5215686274509804,0.8588235294117647,1]},\"o\":{\"a\":0,\"k\":100},\"w\":{\"a\":0,\"k\":9},\"lc\":2,\"lj\":1,\"ml\":4,\"nm\":\"Stroke 1\",\"mn\":\"ADBE Vector Graphic - Stroke\"},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[0,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Shape 1\",\"np\":3,\"cix\":2,\"ix\":2,\"mn\":\"ADBE Vector Group\"},{\"ty\":\"tm\",\"s\":{\"a\":0,\"k\":0,\"ix\":1},\"e\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0],\"y\":[1]},\"o\":{\"x\":[0.333],\"y\":[0]},\"n\":[\"0_1_0p333_0\"],\"t\":5,\"s\":[0],\"e\":[100]},{\"t\":19}],\"ix\":2},\"o\":{\"a\":0,\"k\":0,\"ix\":3},\"m\":1,\"ix\":3,\"nm\":\"Trim Paths 1\",\"mn\":\"ADBE Vector Filter - Trim\"}],\"ip\":0,\"op\":39,\"st\":0,\"bm\":0,\"sr\":1},{\"ddd\":0,\"ind\":3,\"ty\":4,\"nm\":\"Shape Layer 1\",\"ks\":{\"o\":{\"a\":0,\"k\":100},\"r\":{\"a\":0,\"k\":0},\"p\":{\"a\":0,\"k\":[325,115,0]},\"a\":{\"a\":0,\"k\":[0,0,0]},\"s\":{\"a\":0,\"k\":[100,100,100]}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[0,0],[48.5,-7.5],[-33,-25]],\"o\":[[0,0],[-44.485,6.879],[35.509,26.901]],\"v\":[[41,24],[-8.5,-17],[-16,76.5]],\"c\":false}},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\"},{\"ty\":\"st\",\"c\":{\"a\":0,\"k\":[0.3058823529411765,0.5215686274509804,0.8588235294117647,1]},\"o\":{\"a\":0,\"k\":100},\"w\":{\"a\":0,\"k\":9},\"lc\":2,\"lj\":1,\"ml\":4,\"nm\":\"Stroke 1\",\"mn\":\"ADBE Vector Graphic - Stroke\"},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[0,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Shape 2\",\"np\":3,\"cix\":2,\"ix\":1,\"mn\":\"ADBE Vector Group\"},{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[0,0],[58,-30]],\"o\":[[0,0],[-58,30]],\"v\":[[49.5,-62.5],[-50,-62.5]],\"c\":false}},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\"},{\"ty\":\"st\",\"c\":{\"a\":0,\"k\":[0.3058823529411765,0.5215686274509804,0.8588235294117647,1]},\"o\":{\"a\":0,\"k\":100},\"w\":{\"a\":0,\"k\":9},\"lc\":2,\"lj\":1,\"ml\":4,\"nm\":\"Stroke 1\",\"mn\":\"ADBE Vector Graphic - Stroke\"},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[0,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Shape 1\",\"np\":3,\"cix\":2,\"ix\":2,\"mn\":\"ADBE Vector Group\"},{\"ty\":\"tm\",\"s\":{\"a\":0,\"k\":0,\"ix\":1},\"e\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0],\"y\":[1]},\"o\":{\"x\":[0.333],\"y\":[0]},\"n\":[\"0_1_0p333_0\"],\"t\":0,\"s\":[0],\"e\":[100]},{\"t\":19}],\"ix\":2},\"o\":{\"a\":0,\"k\":0,\"ix\":3},\"m\":1,\"ix\":3,\"nm\":\"Trim Paths 1\",\"mn\":\"ADBE Vector Filter - Trim\"}],\"ip\":0,\"op\":39,\"st\":0,\"bm\":0,\"sr\":1}]}"
  },
  {
    "path": "app/src/main/assets/headAnim.json",
    "content": "{\"v\":\"5.3.4\",\"fr\":30,\"ip\":0,\"op\":69,\"w\":150,\"h\":150,\"nm\":\"loading\",\"ddd\":0,\"assets\":[],\"layers\":[{\"ddd\":0,\"ind\":1,\"ty\":4,\"nm\":\"mask\",\"td\":1,\"sr\":1,\"ks\":{\"o\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0.667],\"y\":[1]},\"o\":{\"x\":[0.333],\"y\":[0]},\"n\":[\"0p667_1_0p333_0\"],\"t\":0,\"s\":[100],\"e\":[60]},{\"i\":{\"x\":[0.667],\"y\":[1]},\"o\":{\"x\":[0.333],\"y\":[0]},\"n\":[\"0p667_1_0p333_0\"],\"t\":15,\"s\":[60],\"e\":[100]},{\"i\":{\"x\":[0.667],\"y\":[1]},\"o\":{\"x\":[0.333],\"y\":[0]},\"n\":[\"0p667_1_0p333_0\"],\"t\":30,\"s\":[100],\"e\":[60]},{\"i\":{\"x\":[0.667],\"y\":[1]},\"o\":{\"x\":[0.333],\"y\":[0]},\"n\":[\"0p667_1_0p333_0\"],\"t\":45,\"s\":[60],\"e\":[100]},{\"i\":{\"x\":[0.667],\"y\":[1]},\"o\":{\"x\":[0.333],\"y\":[0]},\"n\":[\"0p667_1_0p333_0\"],\"t\":60,\"s\":[100],\"e\":[60]},{\"t\":75.0000030548126}],\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":0,\"k\":[27,14.499999999999998,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,-60,0],\"ix\":1},\"s\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0,0,0.667],\"y\":[1,1,1]},\"o\":{\"x\":[0,0,0.333],\"y\":[0,0,0]},\"n\":[\"0_1_0_0\",\"0_1_0_0\",\"0p667_1_0p333_0\"],\"t\":0,\"s\":[100,0,100],\"e\":[100,99,100]},{\"i\":{\"x\":[0.833,0.833,0.833],\"y\":[1,1,1]},\"o\":{\"x\":[1,1,0.167],\"y\":[0,0,0]},\"n\":[\"0p833_1_1_0\",\"0p833_1_1_0\",\"0p833_1_0p167_0\"],\"t\":40,\"s\":[100,99,100],\"e\":[100,0,100]},{\"t\":57.0000023216576}],\"ix\":6}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"ty\":\"rc\",\"d\":1,\"s\":{\"a\":0,\"k\":[80,120],\"ix\":2},\"p\":{\"a\":0,\"k\":[0,0],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":4},\"nm\":\"path-mask-1\",\"mn\":\"ADBE Vector Shape - Rect\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[0,0,0,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"nm\":\"fill-mask-1\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[0,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"トランスフォーム\"}],\"nm\":\"rect-mask-1\",\"np\":2,\"cix\":2,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false}],\"ip\":0,\"op\":180.00000733155,\"st\":0,\"bm\":0},{\"ddd\":0,\"ind\":2,\"ty\":4,\"nm\":\"1\",\"tt\":1,\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":0,\"k\":[31.28,74.311,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[31.53,59.114,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100,100],\"ix\":6}},\"ao\":0,\"shapes\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[0,0],[0,0],[0,0],[0,0]],\"o\":[[0,0],[0,0],[0,0],[0,0]],\"v\":[[30.902,0.376],[62.81,0.25],[32.158,117.851],[0.25,117.978]],\"c\":true},\"ix\":2},\"nm\":\"rect\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"gf\",\"o\":{\"a\":0,\"k\":100,\"ix\":10},\"r\":1,\"g\":{\"p\":3,\"k\":{\"a\":0,\"k\":[0,1,0.612,1,0.5,0.5,0.306,1,1,0,0,1],\"ix\":9}},\"s\":{\"a\":0,\"k\":[0,110],\"ix\":5},\"e\":{\"a\":0,\"k\":[0,0],\"ix\":6},\"t\":1,\"nm\":\"fill-1\",\"mn\":\"ADBE Vector Graphic - G-Fill\",\"hd\":false}],\"ip\":0,\"op\":91.000003706506,\"st\":0,\"bm\":0},{\"ddd\":0,\"ind\":3,\"ty\":4,\"nm\":\"mask 2\",\"td\":1,\"sr\":1,\"ks\":{\"o\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0.667],\"y\":[1]},\"o\":{\"x\":[0.333],\"y\":[0]},\"n\":[\"0p667_1_0p333_0\"],\"t\":7,\"s\":[100],\"e\":[60]},{\"i\":{\"x\":[0.667],\"y\":[1]},\"o\":{\"x\":[0.333],\"y\":[0]},\"n\":[\"0p667_1_0p333_0\"],\"t\":22,\"s\":[60],\"e\":[100]},{\"i\":{\"x\":[0.667],\"y\":[1]},\"o\":{\"x\":[0.333],\"y\":[0]},\"n\":[\"0p667_1_0p333_0\"],\"t\":37,\"s\":[100],\"e\":[60]},{\"i\":{\"x\":[0.667],\"y\":[1]},\"o\":{\"x\":[0.333],\"y\":[0]},\"n\":[\"0p667_1_0p333_0\"],\"t\":52,\"s\":[60],\"e\":[100]},{\"i\":{\"x\":[0.667],\"y\":[1]},\"o\":{\"x\":[0.333],\"y\":[0]},\"n\":[\"0p667_1_0p333_0\"],\"t\":67,\"s\":[100],\"e\":[60]},{\"t\":82.0000033399285}],\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":0,\"k\":[72.5,32.5,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,-60,0],\"ix\":1},\"s\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0,0,0.667],\"y\":[1,1,1]},\"o\":{\"x\":[0,0,0.333],\"y\":[0,0,0]},\"n\":[\"0_1_0_0\",\"0_1_0_0\",\"0p667_1_0p333_0\"],\"t\":7,\"s\":[100,0,100],\"e\":[100,99,100]},{\"i\":{\"x\":[0.833,0.833,0.833],\"y\":[1,1,1]},\"o\":{\"x\":[1,1,0.167],\"y\":[0,0,0]},\"n\":[\"0p833_1_1_0\",\"0p833_1_1_0\",\"0p833_1_0p167_0\"],\"t\":43,\"s\":[100,99,100],\"e\":[100,0,100]},{\"t\":60.0000024438501}],\"ix\":6}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"ty\":\"rc\",\"d\":1,\"s\":{\"a\":0,\"k\":[80,120],\"ix\":2},\"p\":{\"a\":0,\"k\":[0,0],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":4},\"nm\":\"path-mask-2\",\"mn\":\"ADBE Vector Shape - Rect\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[0,0,0,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"nm\":\"fill-mask-2\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[0,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"トランスフォーム\"}],\"nm\":\"rect-mask-2\",\"np\":2,\"cix\":2,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false}],\"ip\":7.00000028511585,\"op\":187.000007616666,\"st\":7.00000028511585,\"bm\":0},{\"ddd\":0,\"ind\":4,\"ty\":4,\"nm\":\"2\",\"tt\":1,\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":0,\"k\":[68.526,91.757,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[31.474,59.114,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100,100],\"ix\":6}},\"ao\":0,\"shapes\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[0,0],[0,0],[0,0],[0,0]],\"o\":[[0,0],[0,0],[0,0],[0,0]],\"v\":[[30.902,0.376],[62.699,0.25],[32.047,117.852],[0.25,117.977]],\"c\":true},\"ix\":2},\"nm\":\"rect2\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"gf\",\"o\":{\"a\":0,\"k\":100,\"ix\":10},\"r\":1,\"g\":{\"p\":3,\"k\":{\"a\":0,\"k\":[0,0.953,1,0.498,0.5,0.976,0.806,0.714,1,1,0.612,0.929],\"ix\":9}},\"s\":{\"a\":0,\"k\":[0,110],\"ix\":5},\"e\":{\"a\":0,\"k\":[0,10],\"ix\":6},\"t\":1,\"nm\":\"fill2\",\"mn\":\"ADBE Vector Graphic - G-Fill\",\"hd\":false}],\"ip\":0,\"op\":91.000003706506,\"st\":0,\"bm\":0},{\"ddd\":0,\"ind\":5,\"ty\":4,\"nm\":\"mask 3\",\"td\":1,\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":0,\"k\":[123,0.5,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,-60,0],\"ix\":1},\"s\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0,0,0.667],\"y\":[1,1,1]},\"o\":{\"x\":[0,0,0.333],\"y\":[0,0,0]},\"n\":[\"0_1_0_0\",\"0_1_0_0\",\"0p667_1_0p333_0\"],\"t\":15,\"s\":[100,0,100],\"e\":[100,99,100]},{\"i\":{\"x\":[0.833,0.833,0.833],\"y\":[1,1,1]},\"o\":{\"x\":[1,1,0.167],\"y\":[0,0,0]},\"n\":[\"0p833_1_1_0\",\"0p833_1_1_0\",\"0p833_1_0p167_0\"],\"t\":45,\"s\":[100,99,100],\"e\":[100,0,100]},{\"t\":62.0000025253118}],\"ix\":6}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"ty\":\"rc\",\"d\":1,\"s\":{\"a\":0,\"k\":[80,120],\"ix\":2},\"p\":{\"a\":0,\"k\":[0,0],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":4},\"nm\":\"path-mask-3\",\"mn\":\"ADBE Vector Shape - Rect\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[0,0,0,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"nm\":\"fill-mask-3\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[0,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"トランスフォーム\"}],\"nm\":\"rect-mask-3\",\"np\":2,\"cix\":2,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false}],\"ip\":15.0000006109625,\"op\":195.000007942513,\"st\":15.0000006109625,\"bm\":0},{\"ddd\":0,\"ind\":6,\"ty\":4,\"nm\":\"3\",\"tt\":1,\"sr\":1,\"ks\":{\"o\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0.667],\"y\":[1]},\"o\":{\"x\":[0.333],\"y\":[0]},\"n\":[\"0p667_1_0p333_0\"],\"t\":15,\"s\":[100],\"e\":[60]},{\"i\":{\"x\":[0.667],\"y\":[1]},\"o\":{\"x\":[0.333],\"y\":[0]},\"n\":[\"0p667_1_0p333_0\"],\"t\":30,\"s\":[60],\"e\":[100]},{\"i\":{\"x\":[0.667],\"y\":[1]},\"o\":{\"x\":[0.333],\"y\":[0]},\"n\":[\"0p667_1_0p333_0\"],\"t\":45,\"s\":[100],\"e\":[60]},{\"i\":{\"x\":[0.667],\"y\":[1]},\"o\":{\"x\":[0.333],\"y\":[0]},\"n\":[\"0p667_1_0p333_0\"],\"t\":60,\"s\":[60],\"e\":[100]},{\"i\":{\"x\":[0.667],\"y\":[1]},\"o\":{\"x\":[0.333],\"y\":[0]},\"n\":[\"0p667_1_0p333_0\"],\"t\":75,\"s\":[100],\"e\":[60]},{\"t\":90.0000036657751}],\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":0,\"k\":[118.72,59.485,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[31.53,59.115,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100,100],\"ix\":6}},\"ao\":0,\"shapes\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[0,0],[0,0],[0,0],[0,0]],\"o\":[[0,0],[0,0],[0,0],[0,0]],\"v\":[[30.902,0.377],[62.81,0.25],[32.158,117.852],[0.25,117.979]],\"c\":true},\"ix\":2},\"nm\":\"rect3\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"gf\",\"o\":{\"a\":0,\"k\":100,\"ix\":10},\"r\":1,\"g\":{\"p\":3,\"k\":{\"a\":0,\"k\":[0,0.498,1,0.867,0.5,0.457,0.725,0.933,1,0.416,0.451,1],\"ix\":9}},\"s\":{\"a\":0,\"k\":[0,110],\"ix\":5},\"e\":{\"a\":0,\"k\":[0,10],\"ix\":6},\"t\":1,\"nm\":\"fill3\",\"mn\":\"ADBE Vector Graphic - G-Fill\",\"hd\":false}],\"ip\":0,\"op\":91.000003706506,\"st\":0,\"bm\":0}],\"markers\":[]}"
  },
  {
    "path": "app/src/main/assets/loadingAnimation.json",
    "content": "{\"assets\":[],\"layers\":[{\"ddd\":0,\"ind\":0,\"ty\":4,\"nm\":\"形状图层 5\",\"ks\":{\"o\":{\"k\":[{\"i\":{\"x\":[0.833],\"y\":[0.833]},\"o\":{\"x\":[0.333],\"y\":[0]},\"n\":[\"0p833_0p833_0p333_0\"],\"t\":8,\"s\":[100],\"e\":[30]},{\"i\":{\"x\":[0.833],\"y\":[0.833]},\"o\":{\"x\":[0.333],\"y\":[0]},\"n\":[\"0p833_0p833_0p333_0\"],\"t\":24,\"s\":[30],\"e\":[100]},{\"t\":40}]},\"r\":{\"k\":0},\"p\":{\"k\":[187.875,77.125,0]},\"a\":{\"k\":[-76.375,-2.875,0]},\"s\":{\"k\":[{\"i\":{\"x\":[0.833,0.833,0.833],\"y\":[0.833,0.833,0.833]},\"o\":{\"x\":[0.333,0.333,0.333],\"y\":[0,0,0.333]},\"n\":[\"0p833_0p833_0p333_0\",\"0p833_0p833_0p333_0\",\"0p833_0p833_0p333_0p333\"],\"t\":8,\"s\":[100,100,100],\"e\":[200,200,100]},{\"i\":{\"x\":[0.833,0.833,0.833],\"y\":[0.833,0.833,0.833]},\"o\":{\"x\":[0.333,0.333,0.333],\"y\":[0,0,0.333]},\"n\":[\"0p833_0p833_0p333_0\",\"0p833_0p833_0p333_0\",\"0p833_0p833_0p333_0p333\"],\"t\":24,\"s\":[200,200,100],\"e\":[100,100,100]},{\"t\":40}]}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"d\":1,\"ty\":\"el\",\"s\":{\"k\":[18,18]},\"p\":{\"k\":[0,0]},\"nm\":\"椭圆路径 1\",\"mn\":\"ADBE Vector Shape - Ellipse\"},{\"ty\":\"st\",\"c\":{\"k\":[1,1,1,1]},\"o\":{\"k\":100},\"w\":{\"k\":0},\"lc\":1,\"lj\":1,\"ml\":4,\"nm\":\"描边 1\",\"mn\":\"ADBE Vector Graphic - Stroke\"},{\"ty\":\"fl\",\"c\":{\"k\":[0.87,0.42,0.56,1]},\"o\":{\"k\":100},\"nm\":\"填充 1\",\"mn\":\"ADBE Vector Graphic - Fill\"},{\"ty\":\"tr\",\"p\":{\"k\":[-76.482,-3.482],\"ix\":2},\"a\":{\"k\":[0,0],\"ix\":1},\"s\":{\"k\":[100,100],\"ix\":3},\"r\":{\"k\":0,\"ix\":6},\"o\":{\"k\":100,\"ix\":7},\"sk\":{\"k\":0,\"ix\":4},\"sa\":{\"k\":0,\"ix\":5},\"nm\":\"变换\"}],\"nm\":\"椭圆 1\",\"np\":3,\"mn\":\"ADBE Vector Group\"}],\"ip\":0,\"op\":40,\"st\":0,\"bm\":0,\"sr\":1},{\"ddd\":0,\"ind\":1,\"ty\":4,\"nm\":\"形状图层 4\",\"ks\":{\"o\":{\"k\":[{\"i\":{\"x\":[0.833],\"y\":[0.833]},\"o\":{\"x\":[0.333],\"y\":[0]},\"n\":[\"0p833_0p833_0p333_0\"],\"t\":6,\"s\":[100],\"e\":[30]},{\"i\":{\"x\":[0.833],\"y\":[0.833]},\"o\":{\"x\":[0.333],\"y\":[0]},\"n\":[\"0p833_0p833_0p333_0\"],\"t\":22,\"s\":[30],\"e\":[100]},{\"t\":36}]},\"r\":{\"k\":0},\"p\":{\"k\":[162.125,76.625,0]},\"a\":{\"k\":[-76.375,-2.875,0]},\"s\":{\"k\":[{\"i\":{\"x\":[0.833,0.833,0.833],\"y\":[0.833,0.833,0.833]},\"o\":{\"x\":[0.333,0.333,0.333],\"y\":[0,0,0.333]},\"n\":[\"0p833_0p833_0p333_0\",\"0p833_0p833_0p333_0\",\"0p833_0p833_0p333_0p333\"],\"t\":6,\"s\":[100,100,100],\"e\":[200,200,100]},{\"i\":{\"x\":[0.833,0.833,0.833],\"y\":[0.833,0.833,0.833]},\"o\":{\"x\":[0.333,0.333,0.333],\"y\":[0,0,0.333]},\"n\":[\"0p833_0p833_0p333_0\",\"0p833_0p833_0p333_0\",\"0p833_0p833_0p333_0p333\"],\"t\":22,\"s\":[200,200,100],\"e\":[100,100,100]},{\"t\":36}]}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"d\":1,\"ty\":\"el\",\"s\":{\"k\":[18,18]},\"p\":{\"k\":[0,0]},\"nm\":\"椭圆路径 1\",\"mn\":\"ADBE Vector Shape - Ellipse\"},{\"ty\":\"st\",\"c\":{\"k\":[1,1,1,1]},\"o\":{\"k\":100},\"w\":{\"k\":0},\"lc\":1,\"lj\":1,\"ml\":4,\"nm\":\"描边 1\",\"mn\":\"ADBE Vector Graphic - Stroke\"},{\"ty\":\"fl\",\"c\":{\"k\":[0.81,0.55,0.82,1]},\"o\":{\"k\":100},\"nm\":\"填充 1\",\"mn\":\"ADBE Vector Graphic - Fill\"},{\"ty\":\"tr\",\"p\":{\"k\":[-76.482,-3.482],\"ix\":2},\"a\":{\"k\":[0,0],\"ix\":1},\"s\":{\"k\":[100,100],\"ix\":3},\"r\":{\"k\":0,\"ix\":6},\"o\":{\"k\":100,\"ix\":7},\"sk\":{\"k\":0,\"ix\":4},\"sa\":{\"k\":0,\"ix\":5},\"nm\":\"变换\"}],\"nm\":\"椭圆 1\",\"np\":3,\"mn\":\"ADBE Vector Group\"}],\"ip\":0,\"op\":40,\"st\":0,\"bm\":0,\"sr\":1},{\"ddd\":0,\"ind\":2,\"ty\":4,\"nm\":\"形状图层 3\",\"ks\":{\"o\":{\"k\":[{\"i\":{\"x\":[0.833],\"y\":[0.833]},\"o\":{\"x\":[0.333],\"y\":[0]},\"n\":[\"0p833_0p833_0p333_0\"],\"t\":4,\"s\":[100],\"e\":[30]},{\"i\":{\"x\":[0.833],\"y\":[0.833]},\"o\":{\"x\":[0.333],\"y\":[0]},\"n\":[\"0p833_0p833_0p333_0\"],\"t\":20,\"s\":[30],\"e\":[100]},{\"t\":32}]},\"r\":{\"k\":0},\"p\":{\"k\":[135.625,76.625,0]},\"a\":{\"k\":[-76.375,-2.875,0]},\"s\":{\"k\":[{\"i\":{\"x\":[0.833,0.833,0.833],\"y\":[0.833,0.833,0.833]},\"o\":{\"x\":[0.333,0.333,0.333],\"y\":[0,0,0.333]},\"n\":[\"0p833_0p833_0p333_0\",\"0p833_0p833_0p333_0\",\"0p833_0p833_0p333_0p333\"],\"t\":4,\"s\":[100,100,100],\"e\":[200,200,100]},{\"i\":{\"x\":[0.833,0.833,0.833],\"y\":[0.833,0.833,0.833]},\"o\":{\"x\":[0.333,0.333,0.333],\"y\":[0,0,0.333]},\"n\":[\"0p833_0p833_0p333_0\",\"0p833_0p833_0p333_0\",\"0p833_0p833_0p333_0p333\"],\"t\":20,\"s\":[200,200,100],\"e\":[100,100,100]},{\"t\":32}]}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"d\":1,\"ty\":\"el\",\"s\":{\"k\":[18,18]},\"p\":{\"k\":[0,0]},\"nm\":\"椭圆路径 1\",\"mn\":\"ADBE Vector Shape - Ellipse\"},{\"ty\":\"st\",\"c\":{\"k\":[1,1,1,1]},\"o\":{\"k\":100},\"w\":{\"k\":0},\"lc\":1,\"lj\":1,\"ml\":4,\"nm\":\"描边 1\",\"mn\":\"ADBE Vector Graphic - Stroke\"},{\"ty\":\"fl\",\"c\":{\"k\":[0.47,0.31,0.62,1]},\"o\":{\"k\":100},\"nm\":\"填充 1\",\"mn\":\"ADBE Vector Graphic - Fill\"},{\"ty\":\"tr\",\"p\":{\"k\":[-76.482,-3.482],\"ix\":2},\"a\":{\"k\":[0,0],\"ix\":1},\"s\":{\"k\":[100,100],\"ix\":3},\"r\":{\"k\":0,\"ix\":6},\"o\":{\"k\":100,\"ix\":7},\"sk\":{\"k\":0,\"ix\":4},\"sa\":{\"k\":0,\"ix\":5},\"nm\":\"变换\"}],\"nm\":\"椭圆 1\",\"np\":3,\"mn\":\"ADBE Vector Group\"}],\"ip\":0,\"op\":40,\"st\":0,\"bm\":0,\"sr\":1},{\"ddd\":0,\"ind\":3,\"ty\":4,\"nm\":\"形状图层 2\",\"ks\":{\"o\":{\"k\":[{\"i\":{\"x\":[0.833],\"y\":[0.833]},\"o\":{\"x\":[0.333],\"y\":[0]},\"n\":[\"0p833_0p833_0p333_0\"],\"t\":2,\"s\":[100],\"e\":[30]},{\"i\":{\"x\":[0.833],\"y\":[0.833]},\"o\":{\"x\":[0.333],\"y\":[0]},\"n\":[\"0p833_0p833_0p333_0\"],\"t\":16,\"s\":[30],\"e\":[100]},{\"t\":28}]},\"r\":{\"k\":0},\"p\":{\"k\":[109.375,76.625,0]},\"a\":{\"k\":[-76.625,-3.125,0]},\"s\":{\"k\":[{\"i\":{\"x\":[0.833,0.833,0.833],\"y\":[0.833,0.833,0.833]},\"o\":{\"x\":[0.333,0.333,0.333],\"y\":[0,0,0.333]},\"n\":[\"0p833_0p833_0p333_0\",\"0p833_0p833_0p333_0\",\"0p833_0p833_0p333_0p333\"],\"t\":2,\"s\":[100,100,100],\"e\":[200,200,100]},{\"i\":{\"x\":[0.833,0.833,0.833],\"y\":[0.833,0.833,0.833]},\"o\":{\"x\":[0.333,0.333,0.333],\"y\":[0,0,0.333]},\"n\":[\"0p833_0p833_0p333_0\",\"0p833_0p833_0p333_0\",\"0p833_0p833_0p333_0p333\"],\"t\":16,\"s\":[200,200,100],\"e\":[100,100,100]},{\"t\":28}]}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"d\":1,\"ty\":\"el\",\"s\":{\"k\":[18,18]},\"p\":{\"k\":[0,0]},\"nm\":\"椭圆路径 1\",\"mn\":\"ADBE Vector Shape - Ellipse\"},{\"ty\":\"st\",\"c\":{\"k\":[1,1,1,1]},\"o\":{\"k\":100},\"w\":{\"k\":0},\"lc\":1,\"lj\":1,\"ml\":4,\"nm\":\"描边 1\",\"mn\":\"ADBE Vector Graphic - Stroke\"},{\"ty\":\"fl\",\"c\":{\"k\":[0.54,0.81,0.89,1]},\"o\":{\"k\":100},\"nm\":\"填充 1\",\"mn\":\"ADBE Vector Graphic - Fill\"},{\"ty\":\"tr\",\"p\":{\"k\":[-76.482,-3.482],\"ix\":2},\"a\":{\"k\":[0,0],\"ix\":1},\"s\":{\"k\":[100,100],\"ix\":3},\"r\":{\"k\":0,\"ix\":6},\"o\":{\"k\":100,\"ix\":7},\"sk\":{\"k\":0,\"ix\":4},\"sa\":{\"k\":0,\"ix\":5},\"nm\":\"变换\"}],\"nm\":\"椭圆 1\",\"np\":3,\"mn\":\"ADBE Vector Group\"}],\"ip\":0,\"op\":40,\"st\":0,\"bm\":0,\"sr\":1},{\"ddd\":0,\"ind\":4,\"ty\":4,\"nm\":\"形状图层 1\",\"ks\":{\"o\":{\"k\":[{\"i\":{\"x\":[0.833],\"y\":[0.833]},\"o\":{\"x\":[0.333],\"y\":[0]},\"n\":[\"0p833_0p833_0p333_0\"],\"t\":0,\"s\":[100],\"e\":[30]},{\"i\":{\"x\":[0.833],\"y\":[0.833]},\"o\":{\"x\":[0.333],\"y\":[0]},\"n\":[\"0p833_0p833_0p333_0\"],\"t\":12,\"s\":[30],\"e\":[100]},{\"t\":24}]},\"r\":{\"k\":0},\"p\":{\"k\":[82.625,76.625,0]},\"a\":{\"k\":[-76.625,-3.375,0]},\"s\":{\"k\":[{\"i\":{\"x\":[0.833,0.833,0.833],\"y\":[0.833,0.833,0.833]},\"o\":{\"x\":[0.333,0.333,0.333],\"y\":[0,0,0.333]},\"n\":[\"0p833_0p833_0p333_0\",\"0p833_0p833_0p333_0\",\"0p833_0p833_0p333_0p333\"],\"t\":0,\"s\":[100,100,100],\"e\":[200,200,100]},{\"i\":{\"x\":[0.833,0.833,0.833],\"y\":[0.833,0.833,0.833]},\"o\":{\"x\":[0.333,0.333,0.333],\"y\":[0,0,0.333]},\"n\":[\"0p833_0p833_0p333_0\",\"0p833_0p833_0p333_0\",\"0p833_0p833_0p333_0p333\"],\"t\":12,\"s\":[200,200,100],\"e\":[100,100,100]},{\"t\":24}]}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"d\":1,\"ty\":\"el\",\"s\":{\"k\":[18,18]},\"p\":{\"k\":[0,0]},\"nm\":\"椭圆路径 1\",\"mn\":\"ADBE Vector Shape - Ellipse\"},{\"ty\":\"st\",\"c\":{\"k\":[1,1,1,1]},\"o\":{\"k\":100},\"w\":{\"k\":0},\"lc\":1,\"lj\":1,\"ml\":4,\"nm\":\"描边 1\",\"mn\":\"ADBE Vector Graphic - Stroke\"},{\"ty\":\"fl\",\"c\":{\"k\":[0.34,0.45,0.78,1]},\"o\":{\"k\":100},\"nm\":\"填充 1\",\"mn\":\"ADBE Vector Graphic - Fill\"},{\"ty\":\"tr\",\"p\":{\"k\":[-76.482,-3.482],\"ix\":2},\"a\":{\"k\":[0,0],\"ix\":1},\"s\":{\"k\":[100,100],\"ix\":3},\"r\":{\"k\":0,\"ix\":6},\"o\":{\"k\":100,\"ix\":7},\"sk\":{\"k\":0,\"ix\":4},\"sa\":{\"k\":0,\"ix\":5},\"nm\":\"变换\"}],\"nm\":\"椭圆 1\",\"np\":3,\"mn\":\"ADBE Vector Group\"}],\"ip\":0,\"op\":40,\"st\":0,\"bm\":0,\"sr\":1}],\"v\":\"4.5.4\",\"ddd\":0,\"ip\":0,\"op\":40,\"fr\":24,\"w\":280,\"h\":160}"
  },
  {
    "path": "app/src/main/assets/lockedAnim.json",
    "content": "{\"v\":\"5.3.4\",\"fr\":30,\"ip\":0,\"op\":303,\"w\":1125,\"h\":1125,\"nm\":\"Lottie LOCKED\",\"ddd\":0,\"assets\":[],\"layers\":[{\"ddd\":0,\"ind\":1,\"ty\":4,\"nm\":\"LOCKED Outlines\",\"sr\":1,\"ks\":{\"o\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0],\"y\":[1.094]},\"o\":{\"x\":[0.333],\"y\":[0]},\"n\":[\"0_1p094_0p333_0\"],\"t\":0,\"s\":[0],\"e\":[100]},{\"i\":{\"x\":[0.667],\"y\":[1]},\"o\":{\"x\":[0.9],\"y\":[-0.103]},\"n\":[\"0p667_1_0p9_-0p103\"],\"t\":23.521,\"s\":[100],\"e\":[0]},{\"i\":{\"x\":[0],\"y\":[1.094]},\"o\":{\"x\":[0.333],\"y\":[0]},\"n\":[\"0_1p094_0p333_0\"],\"t\":50.25,\"s\":[0],\"e\":[100]},{\"i\":{\"x\":[0.667],\"y\":[1]},\"o\":{\"x\":[0.9],\"y\":[-0.103]},\"n\":[\"0p667_1_0p9_-0p103\"],\"t\":73.771,\"s\":[100],\"e\":[0]},{\"i\":{\"x\":[0],\"y\":[1.094]},\"o\":{\"x\":[0.333],\"y\":[0]},\"n\":[\"0_1p094_0p333_0\"],\"t\":100.5,\"s\":[0],\"e\":[100]},{\"i\":{\"x\":[0],\"y\":[1.009]},\"o\":{\"x\":[0.9],\"y\":[0.008]},\"n\":[\"0_1p009_0p9_0p008\"],\"t\":124,\"s\":[100],\"e\":[100]},{\"i\":{\"x\":[0.667],\"y\":[1]},\"o\":{\"x\":[0.9],\"y\":[-0.103]},\"n\":[\"0p667_1_0p9_-0p103\"],\"t\":124.021,\"s\":[100],\"e\":[0]},{\"i\":{\"x\":[0],\"y\":[1.094]},\"o\":{\"x\":[0.333],\"y\":[0]},\"n\":[\"0_1p094_0p333_0\"],\"t\":150.729,\"s\":[0],\"e\":[100]},{\"i\":{\"x\":[0.667],\"y\":[1]},\"o\":{\"x\":[0.9],\"y\":[-0.103]},\"n\":[\"0p667_1_0p9_-0p103\"],\"t\":174.25,\"s\":[100],\"e\":[0]},{\"i\":{\"x\":[0],\"y\":[1.094]},\"o\":{\"x\":[0.333],\"y\":[0]},\"n\":[\"0_1p094_0p333_0\"],\"t\":200.979,\"s\":[0],\"e\":[100]},{\"i\":{\"x\":[0],\"y\":[1.2]},\"o\":{\"x\":[0.9],\"y\":[0.193]},\"n\":[\"0_1p2_0p9_0p193\"],\"t\":224.5,\"s\":[100],\"e\":[100]},{\"i\":{\"x\":[0.667],\"y\":[1]},\"o\":{\"x\":[0.9],\"y\":[-0.103]},\"n\":[\"0p667_1_0p9_-0p103\"],\"t\":225,\"s\":[100],\"e\":[0]},{\"i\":{\"x\":[0],\"y\":[1.094]},\"o\":{\"x\":[0.333],\"y\":[0]},\"n\":[\"0_1p094_0p333_0\"],\"t\":251.729,\"s\":[0],\"e\":[100]},{\"i\":{\"x\":[0.667],\"y\":[1]},\"o\":{\"x\":[0.9],\"y\":[-0.103]},\"n\":[\"0p667_1_0p9_-0p103\"],\"t\":275.25,\"s\":[100],\"e\":[0]},{\"t\":301.978515625}],\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":0,\"k\":[562.5,590.5,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100,100],\"ix\":6}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],\"o\":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],\"v\":[[-114.519,0],[-114.519,-10.92],[-154.069,-10.92],[-154.069,-50.47],[-164.919,-50.47],[-164.919,0]],\"c\":true},\"ix\":2},\"nm\":\"L\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"st\",\"c\":{\"a\":0,\"k\":[0,0,0,1],\"ix\":3},\"o\":{\"a\":0,\"k\":80,\"ix\":4},\"w\":{\"a\":0,\"k\":2,\"ix\":5},\"lc\":1,\"lj\":1,\"ml\":4,\"ml2\":{\"a\":0,\"k\":4,\"ix\":8},\"nm\":\"Stroke 1\",\"mn\":\"ADBE Vector Graphic - Stroke\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[0.58431372549,0.976470648074,1,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"nm\":\"Fill 1\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[0,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"L\",\"np\":3,\"cix\":2,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false},{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[2.006,-2.006],[0,-2.893],[0,0],[-2.007,-2.006],[-2.894,0],[0,0],[-2.03,2.03],[0,2.847],[0,0],[2.03,2.03],[2.846,0],[0,0]],\"o\":[[-2.007,2.007],[0,0],[0,2.894],[2.006,2.007],[0,0],[2.846,0],[2.03,-2.03],[0,0],[0,-2.846],[-2.03,-2.03],[0,0],[-2.894,0]],\"v\":[[-107.589,-47.39],[-110.599,-40.04],[-110.599,-10.36],[-107.589,-3.01],[-100.239,0],[-70.559,0],[-63.244,-3.045],[-60.199,-10.36],[-60.199,-40.04],[-63.244,-47.355],[-70.559,-50.4],[-100.239,-50.4]],\"c\":true},\"ix\":2},\"nm\":\"O\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ind\":1,\"ty\":\"sh\",\"ix\":2,\"ks\":{\"a\":0,\"k\":{\"i\":[[0,0],[0,0],[0,0],[0,0]],\"o\":[[0,0],[0,0],[0,0],[0,0]],\"v\":[[-71.189,-39.48],[-71.189,-10.92],[-99.749,-10.92],[-99.749,-39.48]],\"c\":true},\"ix\":2},\"nm\":\"O\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"mm\",\"mm\":1,\"nm\":\"Merge Paths 1\",\"mn\":\"ADBE Vector Filter - Merge\",\"hd\":false},{\"ty\":\"st\",\"c\":{\"a\":0,\"k\":[0,0,0,1],\"ix\":3},\"o\":{\"a\":0,\"k\":80,\"ix\":4},\"w\":{\"a\":0,\"k\":2,\"ix\":5},\"lc\":1,\"lj\":1,\"ml\":4,\"ml2\":{\"a\":0,\"k\":4,\"ix\":8},\"nm\":\"Stroke 1\",\"mn\":\"ADBE Vector Graphic - Stroke\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[0.58431372549,0.976470648074,1,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"nm\":\"Fill 1\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[0,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"O\",\"np\":5,\"cix\":2,\"ix\":2,\"mn\":\"ADBE Vector Group\",\"hd\":false},{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[0,0],[0,0],[2.006,-2.006],[0,-2.893],[0,0],[-2.007,-2.006],[-2.894,0],[0,0],[0,0],[0,0],[0,0],[0,0]],\"o\":[[0,0],[-2.894,0],[-2.007,2.007],[0,0],[0,2.894],[2.006,2.007],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],\"v\":[[-2.24,-50.4],[-42.14,-50.4],[-49.49,-47.39],[-52.5,-40.04],[-52.5,-10.36],[-49.49,-3.01],[-42.14,0],[-2.24,0],[-2.24,-10.92],[-41.65,-10.92],[-41.65,-39.48],[-2.24,-39.48]],\"c\":true},\"ix\":2},\"nm\":\"C\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"st\",\"c\":{\"a\":0,\"k\":[0,0,0,1],\"ix\":3},\"o\":{\"a\":0,\"k\":80,\"ix\":4},\"w\":{\"a\":0,\"k\":2,\"ix\":5},\"lc\":1,\"lj\":1,\"ml\":4,\"ml2\":{\"a\":0,\"k\":4,\"ix\":8},\"nm\":\"Stroke 1\",\"mn\":\"ADBE Vector Graphic - Stroke\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[0.58431372549,0.976470648074,1,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"nm\":\"Fill 1\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[0,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"C\",\"np\":3,\"cix\":2,\"ix\":3,\"mn\":\"ADBE Vector Group\",\"hd\":false},{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],\"o\":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],\"v\":[[32.199,-38.15],[25.899,-30.66],[16.029,-30.66],[16.029,-50.4],[5.109,-50.4],[5.109,0],[16.029,0],[16.029,-19.74],[25.899,-19.74],[42.419,0],[53.759,0],[53.759,-3.78],[35.769,-25.2],[53.759,-46.62],[53.759,-50.4],[42.419,-50.4]],\"c\":true},\"ix\":2},\"nm\":\"K\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"st\",\"c\":{\"a\":0,\"k\":[0,0,0,1],\"ix\":3},\"o\":{\"a\":0,\"k\":80,\"ix\":4},\"w\":{\"a\":0,\"k\":2,\"ix\":5},\"lc\":1,\"lj\":1,\"ml\":4,\"ml2\":{\"a\":0,\"k\":4,\"ix\":8},\"nm\":\"Stroke 1\",\"mn\":\"ADBE Vector Graphic - Stroke\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[0.58431372549,0.976470648074,1,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"nm\":\"Fill 1\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[0,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"K\",\"np\":3,\"cix\":2,\"ix\":4,\"mn\":\"ADBE Vector Group\",\"hd\":false},{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],\"o\":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],\"v\":[[60.969,-50.4],[60.969,0],[107.519,0],[107.519,-10.92],[71.959,-10.92],[71.959,-19.74],[100.589,-19.74],[100.589,-30.66],[71.959,-30.66],[71.959,-39.48],[107.519,-39.48],[107.519,-50.4]],\"c\":true},\"ix\":2},\"nm\":\"E\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"st\",\"c\":{\"a\":0,\"k\":[0,0,0,1],\"ix\":3},\"o\":{\"a\":0,\"k\":80,\"ix\":4},\"w\":{\"a\":0,\"k\":2,\"ix\":5},\"lc\":1,\"lj\":1,\"ml\":4,\"ml2\":{\"a\":0,\"k\":4,\"ix\":8},\"nm\":\"Stroke 1\",\"mn\":\"ADBE Vector Graphic - Stroke\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[0.58431372549,0.976470648074,1,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"nm\":\"Fill 1\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[0,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"E\",\"np\":3,\"cix\":2,\"ix\":5,\"mn\":\"ADBE Vector Group\",\"hd\":false},{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[0,0],[0,0],[-2.03,2.03],[0,2.847],[0,0],[2.03,2.03],[2.893,0],[0,0]],\"o\":[[0,0],[2.893,0],[2.03,-2.03],[0,0],[0,-2.846],[-2.03,-2.03],[0,0],[0,0]],\"v\":[[114.589,0],[154.559,0],[161.944,-3.045],[164.989,-10.36],[164.989,-40.04],[161.944,-47.355],[154.559,-50.4],[114.589,-50.4]],\"c\":true},\"ix\":2},\"nm\":\"D\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ind\":1,\"ty\":\"sh\",\"ix\":2,\"ks\":{\"a\":0,\"k\":{\"i\":[[0,0],[0,0],[0,0],[0,0]],\"o\":[[0,0],[0,0],[0,0],[0,0]],\"v\":[[153.999,-39.48],[153.999,-10.92],[125.439,-10.92],[125.439,-39.48]],\"c\":true},\"ix\":2},\"nm\":\"D\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"mm\",\"mm\":1,\"nm\":\"Merge Paths 1\",\"mn\":\"ADBE Vector Filter - Merge\",\"hd\":false},{\"ty\":\"st\",\"c\":{\"a\":0,\"k\":[0,0,0,1],\"ix\":3},\"o\":{\"a\":0,\"k\":80,\"ix\":4},\"w\":{\"a\":0,\"k\":2,\"ix\":5},\"lc\":1,\"lj\":1,\"ml\":4,\"ml2\":{\"a\":0,\"k\":4,\"ix\":8},\"nm\":\"Stroke 1\",\"mn\":\"ADBE Vector Graphic - Stroke\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[0.58431372549,0.976470648074,1,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"nm\":\"Fill 1\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[0,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"D\",\"np\":5,\"cix\":2,\"ix\":6,\"mn\":\"ADBE Vector Group\",\"hd\":false}],\"ip\":0,\"op\":303,\"st\":0,\"bm\":0},{\"ddd\":0,\"ind\":2,\"ty\":4,\"nm\":\"Shape Layer 5\",\"td\":1,\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":0,\"k\":[562.5,562.5,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100,100],\"ix\":6}},\"ao\":0,\"shapes\":[{\"d\":1,\"ty\":\"el\",\"s\":{\"a\":0,\"k\":[1015,1015],\"ix\":2},\"p\":{\"a\":0,\"k\":[0,0],\"ix\":3},\"nm\":\"Ellipse Path 1\",\"mn\":\"ADBE Vector Shape - Ellipse\",\"hd\":false},{\"ty\":\"gf\",\"o\":{\"a\":0,\"k\":100,\"ix\":10},\"r\":1,\"g\":{\"p\":3,\"k\":{\"a\":0,\"k\":[0,1,1,1,0.466,1,1,1,1,1,1,1,0,1,0.882,0.5,1,0],\"ix\":9}},\"s\":{\"a\":0,\"k\":[0,0],\"ix\":5},\"e\":{\"a\":0,\"k\":[492,0],\"ix\":6},\"t\":2,\"h\":{\"a\":0,\"k\":0,\"ix\":7},\"a\":{\"a\":0,\"k\":0,\"ix\":8},\"nm\":\"Gradient Fill 1\",\"mn\":\"ADBE Vector Graphic - G-Fill\",\"hd\":false}],\"ip\":0,\"op\":303,\"st\":0,\"bm\":0},{\"ddd\":0,\"ind\":3,\"ty\":4,\"nm\":\"comp 3\",\"tt\":1,\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":0,\"k\":[578.5,1349.5,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[-100,197,100],\"ix\":6}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[0,0],[0,0]],\"o\":[[0,0],[0,0]],\"v\":[[-216.5,-846],[-216.5,-34]],\"c\":false},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"st\",\"c\":{\"a\":0,\"k\":[0.125490196078,0.870588295133,0.894117706897,1],\"ix\":3},\"o\":{\"a\":0,\"k\":80,\"ix\":4},\"w\":{\"a\":0,\"k\":14,\"ix\":5},\"lc\":1,\"lj\":1,\"ml\":4,\"ml2\":{\"a\":0,\"k\":4,\"ix\":8},\"d\":[{\"n\":\"d\",\"nm\":\"dash\",\"v\":{\"a\":0,\"k\":23,\"ix\":1}},{\"n\":\"g\",\"nm\":\"gap\",\"v\":{\"a\":0,\"k\":709,\"ix\":2}},{\"n\":\"o\",\"nm\":\"offset\",\"v\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0.833],\"y\":[0.833]},\"o\":{\"x\":[0.167],\"y\":[0.167]},\"n\":[\"0p833_0p833_0p167_0p167\"],\"t\":-53,\"s\":[556],\"e\":[-4793]},{\"t\":630}],\"ix\":7}}],\"nm\":\"Stroke 1\",\"mn\":\"ADBE Vector Graphic - Stroke\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[537,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Group 16\",\"np\":2,\"cix\":2,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false},{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[0,0],[0,0]],\"o\":[[0,0],[0,0]],\"v\":[[-216.5,-846],[-216.5,-34]],\"c\":false},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"st\",\"c\":{\"a\":0,\"k\":[0.125490196078,0.870588295133,0.894117706897,1],\"ix\":3},\"o\":{\"a\":0,\"k\":80,\"ix\":4},\"w\":{\"a\":0,\"k\":19,\"ix\":5},\"lc\":1,\"lj\":1,\"ml\":4,\"ml2\":{\"a\":0,\"k\":4,\"ix\":8},\"d\":[{\"n\":\"d\",\"nm\":\"dash\",\"v\":{\"a\":0,\"k\":19,\"ix\":1}},{\"n\":\"g\",\"nm\":\"gap\",\"v\":{\"a\":0,\"k\":242,\"ix\":2}},{\"n\":\"o\",\"nm\":\"offset\",\"v\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0.833],\"y\":[0.833]},\"o\":{\"x\":[0.167],\"y\":[0.167]},\"n\":[\"0p833_0p833_0p167_0p167\"],\"t\":-53,\"s\":[556],\"e\":[-4793]},{\"t\":664}],\"ix\":7}}],\"nm\":\"Stroke 1\",\"mn\":\"ADBE Vector Graphic - Stroke\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[231,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Group 15\",\"np\":2,\"cix\":2,\"ix\":2,\"mn\":\"ADBE Vector Group\",\"hd\":false},{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[0,0],[0,0]],\"o\":[[0,0],[0,0]],\"v\":[[-216.5,-846],[-216.5,-34]],\"c\":false},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"st\",\"c\":{\"a\":0,\"k\":[0.125490196078,0.870588295133,0.894117706897,1],\"ix\":3},\"o\":{\"a\":0,\"k\":80,\"ix\":4},\"w\":{\"a\":0,\"k\":21,\"ix\":5},\"lc\":1,\"lj\":1,\"ml\":4,\"ml2\":{\"a\":0,\"k\":4,\"ix\":8},\"d\":[{\"n\":\"d\",\"nm\":\"dash\",\"v\":{\"a\":0,\"k\":21,\"ix\":1}},{\"n\":\"g\",\"nm\":\"gap\",\"v\":{\"a\":0,\"k\":662,\"ix\":2}},{\"n\":\"o\",\"nm\":\"offset\",\"v\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0.833],\"y\":[0.833]},\"o\":{\"x\":[0.167],\"y\":[0.167]},\"n\":[\"0p833_0p833_0p167_0p167\"],\"t\":-53,\"s\":[556],\"e\":[-4793]},{\"t\":461}],\"ix\":7}}],\"nm\":\"Stroke 1\",\"mn\":\"ADBE Vector Graphic - Stroke\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[496,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Group 14\",\"np\":2,\"cix\":2,\"ix\":3,\"mn\":\"ADBE Vector Group\",\"hd\":false},{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[0,0],[0,0]],\"o\":[[0,0],[0,0]],\"v\":[[-216.5,-846],[-216.5,-34]],\"c\":false},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"st\",\"c\":{\"a\":0,\"k\":[0.125490196078,0.870588295133,0.894117706897,1],\"ix\":3},\"o\":{\"a\":0,\"k\":80,\"ix\":4},\"w\":{\"a\":0,\"k\":3,\"ix\":5},\"lc\":1,\"lj\":1,\"ml\":4,\"ml2\":{\"a\":0,\"k\":4,\"ix\":8},\"d\":[{\"n\":\"d\",\"nm\":\"dash\",\"v\":{\"a\":0,\"k\":27,\"ix\":1}},{\"n\":\"g\",\"nm\":\"gap\",\"v\":{\"a\":0,\"k\":457,\"ix\":2}},{\"n\":\"o\",\"nm\":\"offset\",\"v\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0.833],\"y\":[0.833]},\"o\":{\"x\":[0.167],\"y\":[0.167]},\"n\":[\"0p833_0p833_0p167_0p167\"],\"t\":-53,\"s\":[556],\"e\":[-4793]},{\"t\":307}],\"ix\":7}}],\"nm\":\"Stroke 1\",\"mn\":\"ADBE Vector Graphic - Stroke\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[-124,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Group 13\",\"np\":2,\"cix\":2,\"ix\":4,\"mn\":\"ADBE Vector Group\",\"hd\":false},{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[0,0],[0,0]],\"o\":[[0,0],[0,0]],\"v\":[[-216.5,-846],[-216.5,-34]],\"c\":false},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"st\",\"c\":{\"a\":0,\"k\":[0.125490196078,0.870588295133,0.894117706897,1],\"ix\":3},\"o\":{\"a\":0,\"k\":80,\"ix\":4},\"w\":{\"a\":0,\"k\":3,\"ix\":5},\"lc\":1,\"lj\":1,\"ml\":4,\"ml2\":{\"a\":0,\"k\":4,\"ix\":8},\"d\":[{\"n\":\"d\",\"nm\":\"dash\",\"v\":{\"a\":0,\"k\":642,\"ix\":1}},{\"n\":\"g\",\"nm\":\"gap\",\"v\":{\"a\":0,\"k\":298,\"ix\":2}},{\"n\":\"o\",\"nm\":\"offset\",\"v\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0.833],\"y\":[0.833]},\"o\":{\"x\":[0.167],\"y\":[0.167]},\"n\":[\"0p833_0p833_0p167_0p167\"],\"t\":-53,\"s\":[556],\"e\":[-4793]},{\"t\":490}],\"ix\":7}}],\"nm\":\"Stroke 1\",\"mn\":\"ADBE Vector Graphic - Stroke\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[663,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Group 12\",\"np\":2,\"cix\":2,\"ix\":5,\"mn\":\"ADBE Vector Group\",\"hd\":false},{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[0,0],[0,0]],\"o\":[[0,0],[0,0]],\"v\":[[-216.5,-846],[-216.5,-34]],\"c\":false},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"st\",\"c\":{\"a\":0,\"k\":[0.125490196078,0.870588295133,0.894117706897,1],\"ix\":3},\"o\":{\"a\":0,\"k\":80,\"ix\":4},\"w\":{\"a\":0,\"k\":23,\"ix\":5},\"lc\":1,\"lj\":1,\"ml\":4,\"ml2\":{\"a\":0,\"k\":4,\"ix\":8},\"d\":[{\"n\":\"d\",\"nm\":\"dash\",\"v\":{\"a\":0,\"k\":27,\"ix\":1}},{\"n\":\"g\",\"nm\":\"gap\",\"v\":{\"a\":0,\"k\":435,\"ix\":2}},{\"n\":\"o\",\"nm\":\"offset\",\"v\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0.833],\"y\":[0.833]},\"o\":{\"x\":[0.167],\"y\":[0.167]},\"n\":[\"0p833_0p833_0p167_0p167\"],\"t\":-53,\"s\":[556],\"e\":[-4793]},{\"t\":418}],\"ix\":7}}],\"nm\":\"Stroke 1\",\"mn\":\"ADBE Vector Graphic - Stroke\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[20,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Group 11\",\"np\":2,\"cix\":2,\"ix\":6,\"mn\":\"ADBE Vector Group\",\"hd\":false},{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[0,0],[0,0]],\"o\":[[0,0],[0,0]],\"v\":[[-216.5,-846],[-216.5,-34]],\"c\":false},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"st\",\"c\":{\"a\":0,\"k\":[0.125490196078,0.870588295133,0.894117706897,1],\"ix\":3},\"o\":{\"a\":0,\"k\":80,\"ix\":4},\"w\":{\"a\":0,\"k\":4,\"ix\":5},\"lc\":1,\"lj\":1,\"ml\":4,\"ml2\":{\"a\":0,\"k\":4,\"ix\":8},\"d\":[{\"n\":\"d\",\"nm\":\"dash\",\"v\":{\"a\":0,\"k\":261,\"ix\":1}},{\"n\":\"g\",\"nm\":\"gap\",\"v\":{\"a\":0,\"k\":948,\"ix\":2}},{\"n\":\"o\",\"nm\":\"offset\",\"v\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0.833],\"y\":[0.833]},\"o\":{\"x\":[0.167],\"y\":[0.167]},\"n\":[\"0p833_0p833_0p167_0p167\"],\"t\":-53,\"s\":[556],\"e\":[-4793]},{\"t\":539}],\"ix\":7}}],\"nm\":\"Stroke 1\",\"mn\":\"ADBE Vector Graphic - Stroke\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[277,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Group 10\",\"np\":2,\"cix\":2,\"ix\":7,\"mn\":\"ADBE Vector Group\",\"hd\":false},{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[0,0],[0,0]],\"o\":[[0,0],[0,0]],\"v\":[[-216.5,-846],[-216.5,-34]],\"c\":false},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"st\",\"c\":{\"a\":0,\"k\":[0.125490196078,0.870588295133,0.894117706897,1],\"ix\":3},\"o\":{\"a\":0,\"k\":80,\"ix\":4},\"w\":{\"a\":0,\"k\":6,\"ix\":5},\"lc\":1,\"lj\":1,\"ml\":4,\"ml2\":{\"a\":0,\"k\":4,\"ix\":8},\"d\":[{\"n\":\"d\",\"nm\":\"dash\",\"v\":{\"a\":0,\"k\":27,\"ix\":1}},{\"n\":\"g\",\"nm\":\"gap\",\"v\":{\"a\":0,\"k\":457,\"ix\":2}},{\"n\":\"o\",\"nm\":\"offset\",\"v\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0.833],\"y\":[0.833]},\"o\":{\"x\":[0.167],\"y\":[0.167]},\"n\":[\"0p833_0p833_0p167_0p167\"],\"t\":-53,\"s\":[556],\"e\":[-4793]},{\"t\":655}],\"ix\":7}}],\"nm\":\"Stroke 1\",\"mn\":\"ADBE Vector Graphic - Stroke\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[-229,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Group 9\",\"np\":2,\"cix\":2,\"ix\":8,\"mn\":\"ADBE Vector Group\",\"hd\":false},{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[0,0],[0,0]],\"o\":[[0,0],[0,0]],\"v\":[[-216.5,-846],[-216.5,-34]],\"c\":false},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"st\",\"c\":{\"a\":0,\"k\":[0.125490196078,0.870588295133,0.894117706897,1],\"ix\":3},\"o\":{\"a\":0,\"k\":80,\"ix\":4},\"w\":{\"a\":0,\"k\":3,\"ix\":5},\"lc\":1,\"lj\":1,\"ml\":4,\"ml2\":{\"a\":0,\"k\":4,\"ix\":8},\"d\":[{\"n\":\"d\",\"nm\":\"dash\",\"v\":{\"a\":0,\"k\":242,\"ix\":1}},{\"n\":\"g\",\"nm\":\"gap\",\"v\":{\"a\":0,\"k\":393,\"ix\":2}},{\"n\":\"o\",\"nm\":\"offset\",\"v\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0.833],\"y\":[0.833]},\"o\":{\"x\":[0.167],\"y\":[0.167]},\"n\":[\"0p833_0p833_0p167_0p167\"],\"t\":-53,\"s\":[556],\"e\":[-4793]},{\"t\":324}],\"ix\":7}}],\"nm\":\"Stroke 1\",\"mn\":\"ADBE Vector Graphic - Stroke\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[98,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Group 8\",\"np\":2,\"cix\":2,\"ix\":9,\"mn\":\"ADBE Vector Group\",\"hd\":false},{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[0,0],[0,0]],\"o\":[[0,0],[0,0]],\"v\":[[-216.5,-846],[-216.5,-34]],\"c\":false},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"st\",\"c\":{\"a\":0,\"k\":[0.125490196078,0.870588295133,0.894117706897,1],\"ix\":3},\"o\":{\"a\":0,\"k\":80,\"ix\":4},\"w\":{\"a\":0,\"k\":39,\"ix\":5},\"lc\":1,\"lj\":1,\"ml\":4,\"ml2\":{\"a\":0,\"k\":4,\"ix\":8},\"d\":[{\"n\":\"d\",\"nm\":\"dash\",\"v\":{\"a\":0,\"k\":80,\"ix\":1}},{\"n\":\"g\",\"nm\":\"gap\",\"v\":{\"a\":0,\"k\":605,\"ix\":2}},{\"n\":\"o\",\"nm\":\"offset\",\"v\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0.833],\"y\":[0.833]},\"o\":{\"x\":[0.167],\"y\":[0.167]},\"n\":[\"0p833_0p833_0p167_0p167\"],\"t\":-53,\"s\":[556],\"e\":[-4793]},{\"t\":469}],\"ix\":7}}],\"nm\":\"Stroke 1\",\"mn\":\"ADBE Vector Graphic - Stroke\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[-60,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Group 7\",\"np\":2,\"cix\":2,\"ix\":10,\"mn\":\"ADBE Vector Group\",\"hd\":false},{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[0,0],[0,0]],\"o\":[[0,0],[0,0]],\"v\":[[-216.5,-846],[-216.5,-34]],\"c\":false},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"st\",\"c\":{\"a\":0,\"k\":[0.125490196078,0.870588295133,0.894117706897,1],\"ix\":3},\"o\":{\"a\":0,\"k\":80,\"ix\":4},\"w\":{\"a\":0,\"k\":14,\"ix\":5},\"lc\":1,\"lj\":1,\"ml\":4,\"ml2\":{\"a\":0,\"k\":4,\"ix\":8},\"d\":[{\"n\":\"d\",\"nm\":\"dash\",\"v\":{\"a\":0,\"k\":74,\"ix\":1}},{\"n\":\"g\",\"nm\":\"gap\",\"v\":{\"a\":0,\"k\":1076,\"ix\":2}},{\"n\":\"o\",\"nm\":\"offset\",\"v\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0.833],\"y\":[0.833]},\"o\":{\"x\":[0.167],\"y\":[0.167]},\"n\":[\"0p833_0p833_0p167_0p167\"],\"t\":-53,\"s\":[556],\"e\":[-4793]},{\"t\":382}],\"ix\":7}}],\"nm\":\"Stroke 1\",\"mn\":\"ADBE Vector Graphic - Stroke\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[261,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Group 6\",\"np\":2,\"cix\":2,\"ix\":11,\"mn\":\"ADBE Vector Group\",\"hd\":false},{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[0,0],[0,0]],\"o\":[[0,0],[0,0]],\"v\":[[-216.5,-846],[-216.5,-34]],\"c\":false},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"st\",\"c\":{\"a\":0,\"k\":[0.125490196078,0.870588295133,0.894117706897,1],\"ix\":3},\"o\":{\"a\":0,\"k\":80,\"ix\":4},\"w\":{\"a\":0,\"k\":16,\"ix\":5},\"lc\":1,\"lj\":1,\"ml\":4,\"ml2\":{\"a\":0,\"k\":4,\"ix\":8},\"d\":[{\"n\":\"d\",\"nm\":\"dash\",\"v\":{\"a\":0,\"k\":27,\"ix\":1}},{\"n\":\"g\",\"nm\":\"gap\",\"v\":{\"a\":0,\"k\":457,\"ix\":2}},{\"n\":\"o\",\"nm\":\"offset\",\"v\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0.833],\"y\":[0.833]},\"o\":{\"x\":[0.167],\"y\":[0.167]},\"n\":[\"0p833_0p833_0p167_0p167\"],\"t\":-53,\"s\":[556],\"e\":[-4793]},{\"t\":547}],\"ix\":7}}],\"nm\":\"Stroke 1\",\"mn\":\"ADBE Vector Graphic - Stroke\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[-161,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Group 5\",\"np\":2,\"cix\":2,\"ix\":12,\"mn\":\"ADBE Vector Group\",\"hd\":false},{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[0,0],[0,0]],\"o\":[[0,0],[0,0]],\"v\":[[-216.5,-846],[-216.5,-34]],\"c\":false},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"st\",\"c\":{\"a\":0,\"k\":[0.125490196078,0.870588295133,0.894117706897,1],\"ix\":3},\"o\":{\"a\":0,\"k\":80,\"ix\":4},\"w\":{\"a\":0,\"k\":3,\"ix\":5},\"lc\":1,\"lj\":1,\"ml\":4,\"ml2\":{\"a\":0,\"k\":4,\"ix\":8},\"d\":[{\"n\":\"d\",\"nm\":\"dash\",\"v\":{\"a\":0,\"k\":642,\"ix\":1}},{\"n\":\"g\",\"nm\":\"gap\",\"v\":{\"a\":0,\"k\":298,\"ix\":2}},{\"n\":\"o\",\"nm\":\"offset\",\"v\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0.833],\"y\":[0.833]},\"o\":{\"x\":[0.167],\"y\":[0.167]},\"n\":[\"0p833_0p833_0p167_0p167\"],\"t\":-53,\"s\":[556],\"e\":[-4793]},{\"t\":655}],\"ix\":7}}],\"nm\":\"Stroke 1\",\"mn\":\"ADBE Vector Graphic - Stroke\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[158,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Group 3\",\"np\":2,\"cix\":2,\"ix\":13,\"mn\":\"ADBE Vector Group\",\"hd\":false},{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[0,0],[0,0]],\"o\":[[0,0],[0,0]],\"v\":[[-216.5,-846],[-216.5,-34]],\"c\":false},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"st\",\"c\":{\"a\":0,\"k\":[0.125490196078,0.870588295133,0.894117706897,1],\"ix\":3},\"o\":{\"a\":0,\"k\":80,\"ix\":4},\"w\":{\"a\":0,\"k\":18,\"ix\":5},\"lc\":1,\"lj\":1,\"ml\":4,\"ml2\":{\"a\":0,\"k\":4,\"ix\":8},\"d\":[{\"n\":\"d\",\"nm\":\"dash\",\"v\":{\"a\":0,\"k\":27,\"ix\":1}},{\"n\":\"g\",\"nm\":\"gap\",\"v\":{\"a\":0,\"k\":435,\"ix\":2}},{\"n\":\"o\",\"nm\":\"offset\",\"v\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0.833],\"y\":[0.833]},\"o\":{\"x\":[0.167],\"y\":[0.167]},\"n\":[\"0p833_0p833_0p167_0p167\"],\"t\":-53,\"s\":[556],\"e\":[-4793]},{\"t\":309}],\"ix\":7}}],\"nm\":\"Stroke 1\",\"mn\":\"ADBE Vector Graphic - Stroke\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[400,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Group 2\",\"np\":2,\"cix\":2,\"ix\":14,\"mn\":\"ADBE Vector Group\",\"hd\":false},{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[0,0],[0,0]],\"o\":[[0,0],[0,0]],\"v\":[[-216.5,-846],[-216.5,-34]],\"c\":false},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"st\",\"c\":{\"a\":0,\"k\":[0.125490196078,0.870588295133,0.894117706897,1],\"ix\":3},\"o\":{\"a\":0,\"k\":80,\"ix\":4},\"w\":{\"a\":0,\"k\":36,\"ix\":5},\"lc\":1,\"lj\":1,\"ml\":4,\"ml2\":{\"a\":0,\"k\":4,\"ix\":8},\"d\":[{\"n\":\"d\",\"nm\":\"dash\",\"v\":{\"a\":0,\"k\":25,\"ix\":1}},{\"n\":\"g\",\"nm\":\"gap\",\"v\":{\"a\":0,\"k\":702,\"ix\":2}},{\"n\":\"o\",\"nm\":\"offset\",\"v\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0.833],\"y\":[0.833]},\"o\":{\"x\":[0.167],\"y\":[0.167]},\"n\":[\"0p833_0p833_0p167_0p167\"],\"t\":-53,\"s\":[556],\"e\":[-4793]},{\"t\":644}],\"ix\":7}}],\"nm\":\"Stroke 1\",\"mn\":\"ADBE Vector Graphic - Stroke\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[609,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Group 4\",\"np\":2,\"cix\":2,\"ix\":15,\"mn\":\"ADBE Vector Group\",\"hd\":false},{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[0,0],[0,0]],\"o\":[[0,0],[0,0]],\"v\":[[-216.5,-846],[-216.5,-34]],\"c\":false},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"st\",\"c\":{\"a\":0,\"k\":[0.125490196078,0.870588295133,0.894117706897,1],\"ix\":3},\"o\":{\"a\":0,\"k\":80,\"ix\":4},\"w\":{\"a\":0,\"k\":6,\"ix\":5},\"lc\":1,\"lj\":1,\"ml\":4,\"ml2\":{\"a\":0,\"k\":4,\"ix\":8},\"d\":[{\"n\":\"d\",\"nm\":\"dash\",\"v\":{\"a\":0,\"k\":27,\"ix\":1}},{\"n\":\"g\",\"nm\":\"gap\",\"v\":{\"a\":0,\"k\":457,\"ix\":2}},{\"n\":\"o\",\"nm\":\"offset\",\"v\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0.833],\"y\":[0.833]},\"o\":{\"x\":[0.167],\"y\":[0.167]},\"n\":[\"0p833_0p833_0p167_0p167\"],\"t\":-53,\"s\":[556],\"e\":[-4793]},{\"t\":732}],\"ix\":7}}],\"nm\":\"Stroke 1\",\"mn\":\"ADBE Vector Graphic - Stroke\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[0,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Group 1\",\"np\":2,\"cix\":2,\"ix\":16,\"mn\":\"ADBE Vector Group\",\"hd\":false}],\"ip\":0,\"op\":303,\"st\":-53,\"bm\":0},{\"ddd\":0,\"ind\":4,\"ty\":4,\"nm\":\"Shape Layer 4\",\"td\":1,\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":0,\"k\":[562.5,562.5,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100,100],\"ix\":6}},\"ao\":0,\"shapes\":[{\"d\":1,\"ty\":\"el\",\"s\":{\"a\":0,\"k\":[1015,1015],\"ix\":2},\"p\":{\"a\":0,\"k\":[0,0],\"ix\":3},\"nm\":\"Ellipse Path 1\",\"mn\":\"ADBE Vector Shape - Ellipse\",\"hd\":false},{\"ty\":\"gf\",\"o\":{\"a\":0,\"k\":100,\"ix\":10},\"r\":1,\"g\":{\"p\":3,\"k\":{\"a\":0,\"k\":[0,1,1,1,0.466,1,1,1,1,1,1,1,0,1,0.882,0.5,1,0],\"ix\":9}},\"s\":{\"a\":0,\"k\":[0,0],\"ix\":5},\"e\":{\"a\":0,\"k\":[492,0],\"ix\":6},\"t\":2,\"h\":{\"a\":0,\"k\":0,\"ix\":7},\"a\":{\"a\":0,\"k\":0,\"ix\":8},\"nm\":\"Gradient Fill 1\",\"mn\":\"ADBE Vector Graphic - G-Fill\",\"hd\":false}],\"ip\":0,\"op\":303,\"st\":0,\"bm\":0},{\"ddd\":0,\"ind\":5,\"ty\":4,\"nm\":\"comp 4\",\"tt\":1,\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":0,\"k\":[562.5,1349.5,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,197,100],\"ix\":6}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[0,0],[0,0]],\"o\":[[0,0],[0,0]],\"v\":[[-216.5,-846],[-216.5,-34]],\"c\":false},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"st\",\"c\":{\"a\":0,\"k\":[0.125490196078,0.870588295133,0.894117706897,1],\"ix\":3},\"o\":{\"a\":0,\"k\":80,\"ix\":4},\"w\":{\"a\":0,\"k\":14,\"ix\":5},\"lc\":1,\"lj\":1,\"ml\":4,\"ml2\":{\"a\":0,\"k\":4,\"ix\":8},\"d\":[{\"n\":\"d\",\"nm\":\"dash\",\"v\":{\"a\":0,\"k\":23,\"ix\":1}},{\"n\":\"g\",\"nm\":\"gap\",\"v\":{\"a\":0,\"k\":709,\"ix\":2}},{\"n\":\"o\",\"nm\":\"offset\",\"v\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0.833],\"y\":[0.833]},\"o\":{\"x\":[0.167],\"y\":[0.167]},\"n\":[\"0p833_0p833_0p167_0p167\"],\"t\":0,\"s\":[556],\"e\":[-4793]},{\"t\":407}],\"ix\":7}}],\"nm\":\"Stroke 1\",\"mn\":\"ADBE Vector Graphic - Stroke\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[537,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Group 16\",\"np\":2,\"cix\":2,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false},{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[0,0],[0,0]],\"o\":[[0,0],[0,0]],\"v\":[[-216.5,-846],[-216.5,-34]],\"c\":false},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"st\",\"c\":{\"a\":0,\"k\":[0.125490196078,0.870588295133,0.894117706897,1],\"ix\":3},\"o\":{\"a\":0,\"k\":80,\"ix\":4},\"w\":{\"a\":0,\"k\":19,\"ix\":5},\"lc\":1,\"lj\":1,\"ml\":4,\"ml2\":{\"a\":0,\"k\":4,\"ix\":8},\"d\":[{\"n\":\"d\",\"nm\":\"dash\",\"v\":{\"a\":0,\"k\":19,\"ix\":1}},{\"n\":\"g\",\"nm\":\"gap\",\"v\":{\"a\":0,\"k\":242,\"ix\":2}},{\"n\":\"o\",\"nm\":\"offset\",\"v\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0.833],\"y\":[0.833]},\"o\":{\"x\":[0.167],\"y\":[0.167]},\"n\":[\"0p833_0p833_0p167_0p167\"],\"t\":0,\"s\":[556],\"e\":[-4793]},{\"t\":441}],\"ix\":7}}],\"nm\":\"Stroke 1\",\"mn\":\"ADBE Vector Graphic - Stroke\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[231,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Group 15\",\"np\":2,\"cix\":2,\"ix\":2,\"mn\":\"ADBE Vector Group\",\"hd\":false},{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[0,0],[0,0]],\"o\":[[0,0],[0,0]],\"v\":[[-216.5,-846],[-216.5,-34]],\"c\":false},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"st\",\"c\":{\"a\":0,\"k\":[0.125490196078,0.870588295133,0.894117706897,1],\"ix\":3},\"o\":{\"a\":0,\"k\":80,\"ix\":4},\"w\":{\"a\":0,\"k\":21,\"ix\":5},\"lc\":1,\"lj\":1,\"ml\":4,\"ml2\":{\"a\":0,\"k\":4,\"ix\":8},\"d\":[{\"n\":\"d\",\"nm\":\"dash\",\"v\":{\"a\":0,\"k\":21,\"ix\":1}},{\"n\":\"g\",\"nm\":\"gap\",\"v\":{\"a\":0,\"k\":662,\"ix\":2}},{\"n\":\"o\",\"nm\":\"offset\",\"v\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0.833],\"y\":[0.833]},\"o\":{\"x\":[0.167],\"y\":[0.167]},\"n\":[\"0p833_0p833_0p167_0p167\"],\"t\":0,\"s\":[556],\"e\":[-4793]},{\"t\":393}],\"ix\":7}}],\"nm\":\"Stroke 1\",\"mn\":\"ADBE Vector Graphic - Stroke\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[496,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Group 14\",\"np\":2,\"cix\":2,\"ix\":3,\"mn\":\"ADBE Vector Group\",\"hd\":false},{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[0,0],[0,0]],\"o\":[[0,0],[0,0]],\"v\":[[-216.5,-846],[-216.5,-34]],\"c\":false},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"st\",\"c\":{\"a\":0,\"k\":[0.125490196078,0.870588295133,0.894117706897,1],\"ix\":3},\"o\":{\"a\":0,\"k\":80,\"ix\":4},\"w\":{\"a\":0,\"k\":3,\"ix\":5},\"lc\":1,\"lj\":1,\"ml\":4,\"ml2\":{\"a\":0,\"k\":4,\"ix\":8},\"d\":[{\"n\":\"d\",\"nm\":\"dash\",\"v\":{\"a\":0,\"k\":27,\"ix\":1}},{\"n\":\"g\",\"nm\":\"gap\",\"v\":{\"a\":0,\"k\":457,\"ix\":2}},{\"n\":\"o\",\"nm\":\"offset\",\"v\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0.833],\"y\":[0.833]},\"o\":{\"x\":[0.167],\"y\":[0.167]},\"n\":[\"0p833_0p833_0p167_0p167\"],\"t\":0,\"s\":[556],\"e\":[-4793]},{\"t\":359}],\"ix\":7}}],\"nm\":\"Stroke 1\",\"mn\":\"ADBE Vector Graphic - Stroke\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[-124,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Group 13\",\"np\":2,\"cix\":2,\"ix\":4,\"mn\":\"ADBE Vector Group\",\"hd\":false},{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[0,0],[0,0]],\"o\":[[0,0],[0,0]],\"v\":[[-216.5,-846],[-216.5,-34]],\"c\":false},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"st\",\"c\":{\"a\":0,\"k\":[0.125490196078,0.870588295133,0.894117706897,1],\"ix\":3},\"o\":{\"a\":0,\"k\":80,\"ix\":4},\"w\":{\"a\":0,\"k\":3,\"ix\":5},\"lc\":1,\"lj\":1,\"ml\":4,\"ml2\":{\"a\":0,\"k\":4,\"ix\":8},\"d\":[{\"n\":\"d\",\"nm\":\"dash\",\"v\":{\"a\":0,\"k\":642,\"ix\":1}},{\"n\":\"g\",\"nm\":\"gap\",\"v\":{\"a\":0,\"k\":298,\"ix\":2}},{\"n\":\"o\",\"nm\":\"offset\",\"v\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0.833],\"y\":[0.833]},\"o\":{\"x\":[0.167],\"y\":[0.167]},\"n\":[\"0p833_0p833_0p167_0p167\"],\"t\":0,\"s\":[556],\"e\":[-4793]},{\"t\":411}],\"ix\":7}}],\"nm\":\"Stroke 1\",\"mn\":\"ADBE Vector Graphic - Stroke\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[663,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Group 12\",\"np\":2,\"cix\":2,\"ix\":5,\"mn\":\"ADBE Vector Group\",\"hd\":false},{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[0,0],[0,0]],\"o\":[[0,0],[0,0]],\"v\":[[-216.5,-846],[-216.5,-34]],\"c\":false},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"st\",\"c\":{\"a\":0,\"k\":[0.125490196078,0.870588295133,0.894117706897,1],\"ix\":3},\"o\":{\"a\":0,\"k\":80,\"ix\":4},\"w\":{\"a\":0,\"k\":23,\"ix\":5},\"lc\":1,\"lj\":1,\"ml\":4,\"ml2\":{\"a\":0,\"k\":4,\"ix\":8},\"d\":[{\"n\":\"d\",\"nm\":\"dash\",\"v\":{\"a\":0,\"k\":27,\"ix\":1}},{\"n\":\"g\",\"nm\":\"gap\",\"v\":{\"a\":0,\"k\":435,\"ix\":2}},{\"n\":\"o\",\"nm\":\"offset\",\"v\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0.833],\"y\":[0.833]},\"o\":{\"x\":[0.167],\"y\":[0.167]},\"n\":[\"0p833_0p833_0p167_0p167\"],\"t\":0,\"s\":[556],\"e\":[-4793]},{\"t\":422}],\"ix\":7}}],\"nm\":\"Stroke 1\",\"mn\":\"ADBE Vector Graphic - Stroke\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[20,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Group 11\",\"np\":2,\"cix\":2,\"ix\":6,\"mn\":\"ADBE Vector Group\",\"hd\":false},{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[0,0],[0,0]],\"o\":[[0,0],[0,0]],\"v\":[[-216.5,-846],[-216.5,-34]],\"c\":false},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"st\",\"c\":{\"a\":0,\"k\":[0.125490196078,0.870588295133,0.894117706897,1],\"ix\":3},\"o\":{\"a\":0,\"k\":80,\"ix\":4},\"w\":{\"a\":0,\"k\":4,\"ix\":5},\"lc\":1,\"lj\":1,\"ml\":4,\"ml2\":{\"a\":0,\"k\":4,\"ix\":8},\"d\":[{\"n\":\"d\",\"nm\":\"dash\",\"v\":{\"a\":0,\"k\":261,\"ix\":1}},{\"n\":\"g\",\"nm\":\"gap\",\"v\":{\"a\":0,\"k\":948,\"ix\":2}},{\"n\":\"o\",\"nm\":\"offset\",\"v\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0.833],\"y\":[0.833]},\"o\":{\"x\":[0.167],\"y\":[0.167]},\"n\":[\"0p833_0p833_0p167_0p167\"],\"t\":0,\"s\":[556],\"e\":[-4793]},{\"t\":474}],\"ix\":7}}],\"nm\":\"Stroke 1\",\"mn\":\"ADBE Vector Graphic - Stroke\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[277,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Group 10\",\"np\":2,\"cix\":2,\"ix\":7,\"mn\":\"ADBE Vector Group\",\"hd\":false},{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[0,0],[0,0]],\"o\":[[0,0],[0,0]],\"v\":[[-216.5,-846],[-216.5,-34]],\"c\":false},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"st\",\"c\":{\"a\":0,\"k\":[0.125490196078,0.870588295133,0.894117706897,1],\"ix\":3},\"o\":{\"a\":0,\"k\":80,\"ix\":4},\"w\":{\"a\":0,\"k\":6,\"ix\":5},\"lc\":1,\"lj\":1,\"ml\":4,\"ml2\":{\"a\":0,\"k\":4,\"ix\":8},\"d\":[{\"n\":\"d\",\"nm\":\"dash\",\"v\":{\"a\":0,\"k\":27,\"ix\":1}},{\"n\":\"g\",\"nm\":\"gap\",\"v\":{\"a\":0,\"k\":457,\"ix\":2}},{\"n\":\"o\",\"nm\":\"offset\",\"v\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0.833],\"y\":[0.833]},\"o\":{\"x\":[0.167],\"y\":[0.167]},\"n\":[\"0p833_0p833_0p167_0p167\"],\"t\":0,\"s\":[556],\"e\":[-4793]},{\"t\":377}],\"ix\":7}}],\"nm\":\"Stroke 1\",\"mn\":\"ADBE Vector Graphic - Stroke\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[-229,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Group 9\",\"np\":2,\"cix\":2,\"ix\":8,\"mn\":\"ADBE Vector Group\",\"hd\":false},{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[0,0],[0,0]],\"o\":[[0,0],[0,0]],\"v\":[[-216.5,-846],[-216.5,-34]],\"c\":false},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"st\",\"c\":{\"a\":0,\"k\":[0.125490196078,0.870588295133,0.894117706897,1],\"ix\":3},\"o\":{\"a\":0,\"k\":80,\"ix\":4},\"w\":{\"a\":0,\"k\":3,\"ix\":5},\"lc\":1,\"lj\":1,\"ml\":4,\"ml2\":{\"a\":0,\"k\":4,\"ix\":8},\"d\":[{\"n\":\"d\",\"nm\":\"dash\",\"v\":{\"a\":0,\"k\":242,\"ix\":1}},{\"n\":\"g\",\"nm\":\"gap\",\"v\":{\"a\":0,\"k\":393,\"ix\":2}},{\"n\":\"o\",\"nm\":\"offset\",\"v\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0.833],\"y\":[0.833]},\"o\":{\"x\":[0.167],\"y\":[0.167]},\"n\":[\"0p833_0p833_0p167_0p167\"],\"t\":0,\"s\":[556],\"e\":[-4793]},{\"t\":381}],\"ix\":7}}],\"nm\":\"Stroke 1\",\"mn\":\"ADBE Vector Graphic - Stroke\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[98,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Group 8\",\"np\":2,\"cix\":2,\"ix\":9,\"mn\":\"ADBE Vector Group\",\"hd\":false},{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[0,0],[0,0]],\"o\":[[0,0],[0,0]],\"v\":[[-216.5,-846],[-216.5,-34]],\"c\":false},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"st\",\"c\":{\"a\":0,\"k\":[0.125490196078,0.870588295133,0.894117706897,1],\"ix\":3},\"o\":{\"a\":0,\"k\":80,\"ix\":4},\"w\":{\"a\":0,\"k\":39,\"ix\":5},\"lc\":1,\"lj\":1,\"ml\":4,\"ml2\":{\"a\":0,\"k\":4,\"ix\":8},\"d\":[{\"n\":\"d\",\"nm\":\"dash\",\"v\":{\"a\":0,\"k\":80,\"ix\":1}},{\"n\":\"g\",\"nm\":\"gap\",\"v\":{\"a\":0,\"k\":605,\"ix\":2}},{\"n\":\"o\",\"nm\":\"offset\",\"v\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0.833],\"y\":[0.833]},\"o\":{\"x\":[0.167],\"y\":[0.167]},\"n\":[\"0p833_0p833_0p167_0p167\"],\"t\":0,\"s\":[556],\"e\":[-4793]},{\"t\":342.583984375}],\"ix\":7}}],\"nm\":\"Stroke 1\",\"mn\":\"ADBE Vector Graphic - Stroke\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[-60,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Group 7\",\"np\":2,\"cix\":2,\"ix\":10,\"mn\":\"ADBE Vector Group\",\"hd\":false},{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[0,0],[0,0]],\"o\":[[0,0],[0,0]],\"v\":[[-216.5,-846],[-216.5,-34]],\"c\":false},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"st\",\"c\":{\"a\":0,\"k\":[0.125490196078,0.870588295133,0.894117706897,1],\"ix\":3},\"o\":{\"a\":0,\"k\":80,\"ix\":4},\"w\":{\"a\":0,\"k\":14,\"ix\":5},\"lc\":1,\"lj\":1,\"ml\":4,\"ml2\":{\"a\":0,\"k\":4,\"ix\":8},\"d\":[{\"n\":\"d\",\"nm\":\"dash\",\"v\":{\"a\":0,\"k\":74,\"ix\":1}},{\"n\":\"g\",\"nm\":\"gap\",\"v\":{\"a\":0,\"k\":1076,\"ix\":2}},{\"n\":\"o\",\"nm\":\"offset\",\"v\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0.833],\"y\":[0.833]},\"o\":{\"x\":[0.167],\"y\":[0.167]},\"n\":[\"0p833_0p833_0p167_0p167\"],\"t\":0,\"s\":[556],\"e\":[-4793]},{\"t\":421}],\"ix\":7}}],\"nm\":\"Stroke 1\",\"mn\":\"ADBE Vector Graphic - Stroke\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[261,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Group 6\",\"np\":2,\"cix\":2,\"ix\":11,\"mn\":\"ADBE Vector Group\",\"hd\":false},{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[0,0],[0,0]],\"o\":[[0,0],[0,0]],\"v\":[[-216.5,-846],[-216.5,-34]],\"c\":false},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"st\",\"c\":{\"a\":0,\"k\":[0.125490196078,0.870588295133,0.894117706897,1],\"ix\":3},\"o\":{\"a\":0,\"k\":80,\"ix\":4},\"w\":{\"a\":0,\"k\":16,\"ix\":5},\"lc\":1,\"lj\":1,\"ml\":4,\"ml2\":{\"a\":0,\"k\":4,\"ix\":8},\"d\":[{\"n\":\"d\",\"nm\":\"dash\",\"v\":{\"a\":0,\"k\":27,\"ix\":1}},{\"n\":\"g\",\"nm\":\"gap\",\"v\":{\"a\":0,\"k\":457,\"ix\":2}},{\"n\":\"o\",\"nm\":\"offset\",\"v\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0.833],\"y\":[0.833]},\"o\":{\"x\":[0.167],\"y\":[0.167]},\"n\":[\"0p833_0p833_0p167_0p167\"],\"t\":0,\"s\":[556],\"e\":[-4793]},{\"t\":375}],\"ix\":7}}],\"nm\":\"Stroke 1\",\"mn\":\"ADBE Vector Graphic - Stroke\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[-161,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Group 5\",\"np\":2,\"cix\":2,\"ix\":12,\"mn\":\"ADBE Vector Group\",\"hd\":false},{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[0,0],[0,0]],\"o\":[[0,0],[0,0]],\"v\":[[-216.5,-846],[-216.5,-34]],\"c\":false},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"st\",\"c\":{\"a\":0,\"k\":[0.125490196078,0.870588295133,0.894117706897,1],\"ix\":3},\"o\":{\"a\":0,\"k\":80,\"ix\":4},\"w\":{\"a\":0,\"k\":18,\"ix\":5},\"lc\":1,\"lj\":1,\"ml\":4,\"ml2\":{\"a\":0,\"k\":4,\"ix\":8},\"d\":[{\"n\":\"d\",\"nm\":\"dash\",\"v\":{\"a\":0,\"k\":27,\"ix\":1}},{\"n\":\"g\",\"nm\":\"gap\",\"v\":{\"a\":0,\"k\":435,\"ix\":2}},{\"n\":\"o\",\"nm\":\"offset\",\"v\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0.833],\"y\":[0.833]},\"o\":{\"x\":[0.167],\"y\":[0.167]},\"n\":[\"0p833_0p833_0p167_0p167\"],\"t\":0,\"s\":[556],\"e\":[-4793]},{\"t\":387}],\"ix\":7}}],\"nm\":\"Stroke 1\",\"mn\":\"ADBE Vector Graphic - Stroke\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[400,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Group 2\",\"np\":2,\"cix\":2,\"ix\":13,\"mn\":\"ADBE Vector Group\",\"hd\":false},{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[0,0],[0,0]],\"o\":[[0,0],[0,0]],\"v\":[[-216.5,-846],[-216.5,-34]],\"c\":false},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"st\",\"c\":{\"a\":0,\"k\":[0.125490196078,0.870588295133,0.894117706897,1],\"ix\":3},\"o\":{\"a\":0,\"k\":80,\"ix\":4},\"w\":{\"a\":0,\"k\":36,\"ix\":5},\"lc\":1,\"lj\":1,\"ml\":4,\"ml2\":{\"a\":0,\"k\":4,\"ix\":8},\"d\":[{\"n\":\"d\",\"nm\":\"dash\",\"v\":{\"a\":0,\"k\":25,\"ix\":1}},{\"n\":\"g\",\"nm\":\"gap\",\"v\":{\"a\":0,\"k\":702,\"ix\":2}},{\"n\":\"o\",\"nm\":\"offset\",\"v\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0.833],\"y\":[0.833]},\"o\":{\"x\":[0.167],\"y\":[0.167]},\"n\":[\"0p833_0p833_0p167_0p167\"],\"t\":0,\"s\":[556],\"e\":[-4793]},{\"t\":421}],\"ix\":7}}],\"nm\":\"Stroke 1\",\"mn\":\"ADBE Vector Graphic - Stroke\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[609,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Group 4\",\"np\":2,\"cix\":2,\"ix\":14,\"mn\":\"ADBE Vector Group\",\"hd\":false},{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[0,0],[0,0]],\"o\":[[0,0],[0,0]],\"v\":[[-216.5,-846],[-216.5,-34]],\"c\":false},\"ix\":2},\"nm\":\"Path 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"st\",\"c\":{\"a\":0,\"k\":[0.125490196078,0.870588295133,0.894117706897,1],\"ix\":3},\"o\":{\"a\":0,\"k\":80,\"ix\":4},\"w\":{\"a\":0,\"k\":6,\"ix\":5},\"lc\":1,\"lj\":1,\"ml\":4,\"ml2\":{\"a\":0,\"k\":4,\"ix\":8},\"d\":[{\"n\":\"d\",\"nm\":\"dash\",\"v\":{\"a\":0,\"k\":27,\"ix\":1}},{\"n\":\"g\",\"nm\":\"gap\",\"v\":{\"a\":0,\"k\":457,\"ix\":2}},{\"n\":\"o\",\"nm\":\"offset\",\"v\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0.833],\"y\":[0.833]},\"o\":{\"x\":[0.167],\"y\":[0.167]},\"n\":[\"0p833_0p833_0p167_0p167\"],\"t\":0,\"s\":[556],\"e\":[-4793]},{\"t\":509}],\"ix\":7}}],\"nm\":\"Stroke 1\",\"mn\":\"ADBE Vector Graphic - Stroke\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[0,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"Transform\"}],\"nm\":\"Group 1\",\"np\":2,\"cix\":2,\"ix\":15,\"mn\":\"ADBE Vector Group\",\"hd\":false}],\"ip\":0,\"op\":303,\"st\":0,\"bm\":0},{\"ddd\":0,\"ind\":6,\"ty\":4,\"nm\":\"Shape Layer 3\",\"sr\":1,\"ks\":{\"o\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0],\"y\":[1.094]},\"o\":{\"x\":[0.333],\"y\":[0]},\"n\":[\"0_1p094_0p333_0\"],\"t\":0,\"s\":[0],\"e\":[100]},{\"i\":{\"x\":[0.667],\"y\":[1]},\"o\":{\"x\":[0.9],\"y\":[-0.103]},\"n\":[\"0p667_1_0p9_-0p103\"],\"t\":23.521,\"s\":[100],\"e\":[0]},{\"i\":{\"x\":[0],\"y\":[1.094]},\"o\":{\"x\":[0.333],\"y\":[0]},\"n\":[\"0_1p094_0p333_0\"],\"t\":50.25,\"s\":[0],\"e\":[100]},{\"i\":{\"x\":[0.667],\"y\":[1]},\"o\":{\"x\":[0.9],\"y\":[-0.103]},\"n\":[\"0p667_1_0p9_-0p103\"],\"t\":73.771,\"s\":[100],\"e\":[0]},{\"i\":{\"x\":[0],\"y\":[1.094]},\"o\":{\"x\":[0.333],\"y\":[0]},\"n\":[\"0_1p094_0p333_0\"],\"t\":100.5,\"s\":[0],\"e\":[100]},{\"i\":{\"x\":[0],\"y\":[1.009]},\"o\":{\"x\":[0.9],\"y\":[0.008]},\"n\":[\"0_1p009_0p9_0p008\"],\"t\":124,\"s\":[100],\"e\":[100]},{\"i\":{\"x\":[0.667],\"y\":[1]},\"o\":{\"x\":[0.9],\"y\":[-0.103]},\"n\":[\"0p667_1_0p9_-0p103\"],\"t\":124.021,\"s\":[100],\"e\":[0]},{\"i\":{\"x\":[0],\"y\":[1.094]},\"o\":{\"x\":[0.333],\"y\":[0]},\"n\":[\"0_1p094_0p333_0\"],\"t\":150.729,\"s\":[0],\"e\":[100]},{\"i\":{\"x\":[0.667],\"y\":[1]},\"o\":{\"x\":[0.9],\"y\":[-0.103]},\"n\":[\"0p667_1_0p9_-0p103\"],\"t\":174.25,\"s\":[100],\"e\":[0]},{\"i\":{\"x\":[0],\"y\":[1.094]},\"o\":{\"x\":[0.333],\"y\":[0]},\"n\":[\"0_1p094_0p333_0\"],\"t\":200.979,\"s\":[0],\"e\":[100]},{\"i\":{\"x\":[0],\"y\":[1.2]},\"o\":{\"x\":[0.9],\"y\":[0.193]},\"n\":[\"0_1p2_0p9_0p193\"],\"t\":224.5,\"s\":[100],\"e\":[100]},{\"i\":{\"x\":[0.667],\"y\":[1]},\"o\":{\"x\":[0.9],\"y\":[-0.103]},\"n\":[\"0p667_1_0p9_-0p103\"],\"t\":225,\"s\":[100],\"e\":[0]},{\"i\":{\"x\":[0],\"y\":[1.094]},\"o\":{\"x\":[0.333],\"y\":[0]},\"n\":[\"0_1p094_0p333_0\"],\"t\":251.729,\"s\":[0],\"e\":[100]},{\"i\":{\"x\":[0.667],\"y\":[1]},\"o\":{\"x\":[0.9],\"y\":[-0.103]},\"n\":[\"0p667_1_0p9_-0p103\"],\"t\":275.25,\"s\":[100],\"e\":[0]},{\"i\":{\"x\":[0],\"y\":[1.094]},\"o\":{\"x\":[0.333],\"y\":[0]},\"n\":[\"0_1p094_0p333_0\"],\"t\":301.979,\"s\":[0],\"e\":[100]},{\"i\":{\"x\":[0],\"y\":[1.009]},\"o\":{\"x\":[0.9],\"y\":[0.008]},\"n\":[\"0_1p009_0p9_0p008\"],\"t\":325.479,\"s\":[100],\"e\":[100]},{\"t\":325.5}],\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":0,\"k\":[568.5,566,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[89.464,43.464,100],\"ix\":6}},\"ao\":0,\"shapes\":[{\"d\":1,\"ty\":\"el\",\"s\":{\"a\":0,\"k\":[678,678],\"ix\":2},\"p\":{\"a\":0,\"k\":[0,0],\"ix\":3},\"nm\":\"Ellipse Path 1\",\"mn\":\"ADBE Vector Shape - Ellipse\",\"hd\":false},{\"ty\":\"gf\",\"o\":{\"a\":0,\"k\":100,\"ix\":10},\"r\":1,\"g\":{\"p\":3,\"k\":{\"a\":0,\"k\":[0,0.584,0.976,1,0.39,0.584,0.976,1,1,0.584,0.976,1,0,0.5,0.321,0.25,1,0],\"ix\":9}},\"s\":{\"a\":0,\"k\":[0,0],\"ix\":5},\"e\":{\"a\":0,\"k\":[303,0],\"ix\":6},\"t\":2,\"h\":{\"a\":0,\"k\":0,\"ix\":7},\"a\":{\"a\":0,\"k\":0,\"ix\":8},\"nm\":\"Gradient Fill 1\",\"mn\":\"ADBE Vector Graphic - G-Fill\",\"hd\":false}],\"ip\":0,\"op\":390,\"st\":0,\"bm\":0}],\"markers\":[]}"
  },
  {
    "path": "app/src/main/assets/version_log/version_log_de_rDE.md",
    "content": "#### 1.0.8\n- Das Absturzproblem beim Öffnen der Schnittstelle zum Entsperren von Fingerabdrücken wurde behoben\n\n#### 1.0.7\n- Dialogfeld \"Verlaufsversion hinzufügen\"\n-Erhöhen Sie die Vibrationsrückmeldung\n-Fix das Problem, dass Dropbox Open Record nicht gespeichert werden kann\n\n#### 1.0.6\n-Fix einige böse Fehler\n-Add [Fingerprint Unlock] (route: //keepassA.com/kpa? Activity = FingerprintActivity)\n\n#### 1.0.4\n-Fix einige böse Fehler\n\n#### 1.0.3\n-Fix einige böse Fehler\n\n#### 1.0.2\n-Fix einige böse Fehler\n\n#### 1.0.1\n-Fix einige böse Fehler"
  },
  {
    "path": "app/src/main/assets/version_log/version_log_en.md",
    "content": "### 2.4.7 (2024/04/30)\n* fix: Fix bug\n\n### 2.4.6 (2024/03/20)\n* new: New password generation ui\n* fix: Fix bug\n\n### 2.4.5 (2024/03/07)\n* fix: Fix bug\n\n### 2.4.2（2024/02/26）\n* new: New details page\n* new: Support Keepass TOTP\n* opt: Night mode color optimization\n* fix: Fixed edit page icon being gray instead of image\n* fix: Fixed display issue on edit entry page\n* fix: Fixed the problem that the copied token is invalid after totp is automatically refreshed\n* fix: Fix experience issues\n\n### 2.4.1 (2023/05/07)\n* new: In-app browser auto-fill\n* new: Fetching log functions\n* opt: Animation effect\n* opt: The flow of auto-fill\n* opt: Auto-fill of web pages\n* fix: The problem of Chinese path synchronization failure in NutCloud\n\n### 2.4.0 (2023/04/30)\n* 【Add】Spanish\n* 【Add】Auto-fill permission pop-up alerts\n* 【Add】Pre-set header mode, fix the nut cloud webdav can not log in the problem\n* 【Fix】Fix the issue of no auto-fill option\n* 【Fix】Fix the issue that some apps can't auto-fill\n* 【Fix】Fix the issue of failed auto-fill save\n\n### 2.3.2 (2022/10/07)\n- 【Add】Nextcloud support\n* 【Fix】Fix some known issues\n* 【Fix】The problem of url not being displayed\n\n### 2.3.1 (2022/06/13)\n- 【Fix】bugs\n\n### 2.3.0 (2022/06/12)\n- 【Add】Collection function\n- 【Add】Auto save db after entering the background\n- 【Add】Ukrainian，thank for [@IhorHordiichuk](https://github.com/IhorHordiichuk)\n- 【Optimize】Open WebDav Process flow\n\n### 2.1.10 (2022/01/05)\n- 【Optimize】TOTP display\n\n![totp_bar](https://raw.githubusercontent.com/AriaLyy/KeepassA/master/img/totpDisplay.png)\n- 【Fix】Failed to create webdav database\n- 【Fix】Failed to set fingerprint\n\n### 2.1.8 (2021/12/31)\nHappy New Year!!\n- 【Add】Network retry，Improve link stability\n- 【Add】Turkish\n- 【Add】Night mode，[Setting->Theme Style](route://keepassA.com/kpa?activity=SettingActivity&type=app&scrollKey=setKeyUiSetting)\n- 【Add】TOTP Bar switch，[Setting->TOTP](route://keepassA.com/kpa?activity=SettingActivity&type=app&scrollKey=setKeyUiSetting)\n\n![totp_bar](https://raw.githubusercontent.com/AriaLyy/KeepassA/master/img/totpBar.png)\n- 【Fix】After the data is created, the quick unlock is opened instead of the home page\n- 【Fix】The problem of fingerprint unlocking\n- 【Fix】Failed to move item\n- 【Optimize】Browser auto-fill\n\n#### 2.1.7（2021/10/18）\n- 【Fix】Dialog layout is out of order\n- 【Fix】Abnormal expiration time display problem\n\n#### 2.1.6 (2021/10/17)\n- 【Fix】Some carsh bug\n- 【Optimize】Logic of expiration time setting\n\n#### 2.1.5 (2021/10/06)\n- 【New】Polish\n- 【Fix】After modifying the data, the database cannot be opened\n\n#### 2.1.4 (2021/9/2)\n- 【New】German translation\n- 【Optimize】Update some multilingual translations\n- 【Fix】The password generation tool has a high probability of selecting data without numbers.\n- 【Fix】The problem of failure to open some KDBV4 DB\n\n#### 2.1.3（2021/7/13）\n- 【New】KDBV4 support\n- 【New】Added reminder for expired entries\n- 【Fix】Group can be moved to itself\n- 【Fix】Other fields of the security keyboard cannot be filled\n- 【Fix】History is not sorted by time\n- 【Fix】Unable to start the quick unlock interface\n- 【Optimize】Safe keyboard multi-entry de-duplication\n\n#### 2.1.2 (2021/5/29)\n- 【New】Allow password to be empty\n- 【New】OneDrive added prompt description\n- 【Fix】The first time to install, there is no waiting animation to load\n- 【Fix】Android 11 does not pop up auto-fill options\n- 【Fix】When creating an entry on the homepage to a group, the number of entries on the homepage does not increase\n- 【Fix】After changing the password, the icon becomes the default icon\n- 【Optimize】Homepage slide to increase fade animation\n- 【Optimize】Animation details\n\n#### 2.1.1（2021/5/6）\n- 【Fix】webview Crash Problem\n\n#### 2.1（20201/5/5）\n- 【New】OneDrive support\n- 【New】Added French translation\n- 【New】Compatible with API30\n- 【Fix】Crash when saving notes\n- 【Fix】When the screen is horizontal, the size of the interface is enlarged\n- 【Fix】Problems caused by multiple Unlock of WebDav\n\n#### 2.0.2 (2021/4/1)\n- 【New】Close loading animation selection[Setting](route://keepassA.com/kpa?activity=SettingActivity) -> UI settings\n- 【New】Does not automatically lock function[Setting](route://keepassA.com/kpa?activity=SettingActivity&type=app) -> Database settings\n- 【New】Russian(40%)，Thank[@KovalevArtem](https://github.com/KovalevArtem)\n- 【New】Norwegian(50%)，Thank[@Allan Nordhøy](https://github.com/comradekingu)\n- 【Optimize】Optimize English translation, Thank[@comradekingu](https://github.com/comradekingu)\n- 【Fix】An animation problem\n- 【Fix】An application setting crash problem\n\n#### 2.0.1（2021/3/27）\n- 【Fix】Webdav login problem\n- 【Fix】Multilingual issues\n\n#### 2.0（2021/3/23）\n- 【New】The recycle bin does not display the add group\\entry button\n- 【New】Whether to hide the status bar，[Setting](route://keepassA.com/kpa?activity=SettingActivity) -> Ui Setting\n- 【New】Key login only\n- 【Optimize】Lots of animations\n- 【Optimize】The default icons are all MD style icons\n- 【Optimize】Icon selection will use the method that pops up at the bottom\n- 【Fix】The problem of invalid timing lock database\n- 【Fix】The problem of missing jump animation\n- 【Fix】Unable to switch historical data\n\n#### 1.8.4 （2021/3/4）\n- Refactor the logic of creating a database\n- Increase the function of creating a database on Webdav\n\n![webdavCreate](https://raw.githubusercontent.com/AriaLyy/KeepassA/master/img/webdavCreate.png)\n- Fix some crashes\n\n#### 1.8.3 (2021/2/25)\n- Fix some crashes\n- Upgrade kotlin version\n- Remove kotlin-android-extensions\n\n#### 1.8.2 (2021/2/17)\n- Fix the crash of the software after the screen is locked\n- Fix the crash of getting steam totp\n- Upgrade kotlin version\n\n#### 1.8.1 (2021/2/14)\n- Fix database loading due to incomplete reading\n- Fix asserts\n- Add Argon2 ID support\n- Add keyfile 2.0 support\n\n#### 1.8 (2021/2/14)\n- Fix a problem that webdav cannot be opened due to cache\n- Optimize the speed of opening the database\n\n#### 1.7 (2021/2/4)\n- Add, When the package name does not match, the keyword will be matched from the url\n- Add, Reference entry hint\n- Add, [When screen lock, automatic lock database function](route://keepassA.com/kpa?activity=SettingActivity&type=app)\n- Add, note add editer\n- Add, User name history record function\n\n![user_drop_down_list](https://raw.githubusercontent.com/AriaLyy/KeepassA/master/img/userDropdownList.png)\n- Add, Search ignores items in the recycle bin\n- Fix, In night mode, auto-fill text cannot be displayed\n- Fix, Some applications cannot be automatically filled\n- Fix, Some crash issues\n\n#### 1.6 (2020/11/28)\n- Auto-fill module, after fixing the associated entry, return to the application, the associated data cannot be displayed\n- Auto-fill module, fix the problem that the input box data is filled incorrectly when multiple input boxes are filled\n- Auto-fill module, add browser filling function\n- Auto-fill module, add other buttons\n- Setup module, After the unlock database, the homepage will display all entries first, [Click Settings](route://keepassA.com/kpa?activity=SettingActivity&type=db)\n- Item details module，Note field automatically increase expansion and contraction functions\n\n![ime](https://raw.githubusercontent.com/AriaLyy/KeepassA/master/img/noteExpand.png)\n- Optimize the homepage flickering problem\n- Thank [@DominicDesbiens](https://github.com/DominicDesbiens) for providing the Canadian French translation\n\n#### 1.5 (2020/11/5)\n- Add group search function\n- Add open source protocol description\n- Increase the function of moving items and groups\n\n![ime](https://raw.githubusercontent.com/AriaLyy/KeepassA/master/img/moveData.png)\n- Fix the problem that the secondary group cannot be added\n- Fix a crash caused by webdav obtaining file information\n- Fix some nasty crashes\n\n#### 1.4.1 (2020/10/29)\n- Upgrade android studio to 4.1\n- Search and add highlight keywords\n- Fix a crash problem of quick unlock\n\n#### 1.4 (2020/10/28)\n- Upgrade kotlin version\n- Add group sorting function\n- Add field reference function\n- Add [safe keyboard](route://keepassA.com/kpa?activity=ime)\n\n![ime](https://raw.githubusercontent.com/AriaLyy/KeepassA/master/img/ime.png)\n- Fix Quickly unlock the interface, the problem that all short passwords cannot be deleted\n- Fix the crash problem caused by webdav login timeout\n- Fix some annoying crash problems\n\n#### 1.3 (2020/9/22)\n- Add the TOTP token setting function, edit the entry, click the add more button to display the function interface\n\n![otp_setting](https://raw.githubusercontent.com/AriaLyy/KeepassA/master/img/otpsetting.png)\n\n#### 1.2 (2020/9/2)\n- Fix some nasty crashes\n\n#### 1.1 (2020/8/21)\n- KeepassA is open source and has been hosted on [github](https://github.com/AriaLyy/KeepassA)\n- Fixed some annoying bugs\n\n#### 1.0.1.2 (2020/7/22)\n- Refactored [Fingerprint Unlock Setting Interface](route://keepassA.com/kpa?activity=FingerprintActivity)\n- Add drop-down synchronization database function\n- Add mobile phone root/emulator detection\n- Add the function of directly deleting entries/groups\n- Fix the problem that http addresses with special characters cannot be recognized\n- Fix the problem that the auto-fill service cannot save data\n\n#### 1.0.1.0 (2020/7/10)\n- Fixed the issue of clicking the notification bar to notify the error when the database is locked\n- TOTP increase countdown effect\n- Fixed the problem that the same entry always prompts when synchronizing the database\n- Fix some nasty crashes\n\n#### 1.0.0.9 (2020/7/11)\n- Fix the problem that after quick lock manually, you cannot enter other pages\n- Fix the problem that the notification bar icon is too small\n- Fixed the crash problem when opening the fingerprint unlock interface\n- Fixed the crash of auto-fill service\n- Optimize the logic of fingerprint unlock function\n\n#### 1.0.0.7 (2020/6/14)\n- Add history version dialog\n- Increase vibration feedback\n- Fix the problem that the dropbox open record cannot be saved\n\n#### 1.0.0.6 (2020/6/7)\n- Fix some nasty bugs\n- add [Fingerprint unlock](route://keepassA.com/kpa?activity=FingerprintActivity)\n\n#### 1.0.0.4 (2020/6/6)\n- Fix some nasty bugs\n\n#### 1.0.0.3 (2020/5/13)\n- Fix some nasty bugs\n\n#### 1.0.0.2 (2020/5/10)\n- Fix some nasty bugs\n\n#### 1.0.0.1 (2020/5/3)\n- Fix some nasty bugs\n"
  },
  {
    "path": "app/src/main/assets/version_log/version_log_ru_rRU.md",
    "content": "#### 1.0.8\n-Исправлена ​​проблема сбоя при открытии интерфейса разблокировки отпечатков пальцев\n\n#### 1.0.7\n-Добавить диалог версии истории\n-Повышение вибрации обратной связи\n-Исправить проблему, что открытая запись Dropbox не может быть сохранена\n\n#### 1.0.6\n-Исправить некоторые неприятные ошибки\n-Добавить [Отпечаток пальца] (маршрут: //keepassA.com/kpa? Activity = FingerprintActivity)\n\n#### 1.0.4\n-Исправить некоторые неприятные ошибки\n\n#### 1.0.3\n-Исправить некоторые неприятные ошибки\n\n#### 1.0.2\n-Исправить некоторые неприятные ошибки\n\n#### 1.0.1\n-Исправить некоторые неприятные ошибки"
  },
  {
    "path": "app/src/main/assets/version_log/version_log_zh_CN.md",
    "content": "### 2.4.7 (2024/04/30)\n* fix: 修复一些已知问题\n\n### 2.4.6 (2024/03/20)\n* new: 新的密码生成界面\n* fix: 修复一些已知问题\n\n### 2.4.5 (2024/03/07)\n* fix: 修复一些已知问题\n\n### 2.4.2（2024/02/26）\n* new: 新的详情页\n* new: 支持Keepass TOTP\n* opt: 夜间模式颜色优化\n* fix: 修复编辑页图标是灰色而不是图像\n* fix: 修复编辑条目页面的显示问题\n* fix: 修复totp自动刷新后，复制的令牌无效的问题\n* fix: 修复体验上的问题\n\n### 2.4.1 （2023/05/07）\n* new: 应用内浏览器自动填充\n* new: 增加取出日志的功能\n* opt: 动画效果\n* opt: 优化自动填充的流程\n* opt: 优化网页的自动填充\n* fix: 修复坚果云中文路径同步失败的问题\n\n### 2.4.0 （2023/04/30）\n* 【增加】西班牙语\n* 【增加】自动填充权限弹窗提示\n* 【增加】预置header模式，修复某些坚果云webdav无法登录的问题\n* 【修复】修复没有自动填充选项的问题\n* 【修复】某些app不能自动填充的问题\n* 【修复】修复自动填充保存失败的问题\n\n### 2.3.1 （2022/10/07）\n* 【增加】隐私说明弹窗\n* 【增加】Nextcloud 支持\n* 【修复】一些已发现的bug\n* 【修复】url不显示的问题\n\n\n### 2.3.0（2022/06/12）\n- 【新增】收藏功能\n- 【新增】后在自动保存数据库\n- 【新增】增加乌克兰语，感谢[@IhorHordiichuk](https://github.com/IhorHordiichuk)\n- 【优化】优化webdav打开流程，现在流程更加人性化\n\n### 2.1.10 (2022/01/05)\n- 【优化】totp显示优化\n\n![totp_bar](https://gitee.com/laoyuyu/blog/raw/master/img/totpDisplay.png)\n- 【修复】创建webdav数据库失败的问题\n- 【修复】设置指纹失败的问题\n\n### 2.1.8 (2021/12/31)\n新年快乐！！\n- 【新增】网络重试，提升OneDrive\\Dropbox\\webdav的网络连接稳定性\n- 【新增】土耳其语\n- 【新增】夜间模式，[应用设置->主题设置](route://keepassA.com/kpa?activity=SettingActivity&type=app&scrollKey=setKeyUiSetting)\n- 【新增】TOTP栏开关[应用设置->界面设置](route://keepassA.com/kpa?activity=SettingActivity&type=app&scrollKey=setKeyUiSetting)\n\n![totp_bar](https://gitee.com/laoyuyu/blog/raw/master/keepassA/totpBar.png)\n- 【修复】创建完成数据后，打开的是快速解锁而不是主页\n- 【修复】指纹解锁的问题\n- 【修复】移动条目失败的问题\n- 【优化】浏览器自动填充\n\n#### 2.1.7（2021/10/18）\n- 【修复】对话框布局错乱问题\n- 【修复】失效时间异常显示问题\n\n#### 2.1.6 (2021/10/17)\n- 【修复】修复一些崩溃问题\n- 【优化】失效时间设置的逻辑\n\n#### 2.1.5 (2021/10/06)\n- 【新增】波兰语\n- 【修复】修改数据后，无法打开数据库\n\n#### 2.1.4 (2021/9/2)\n- 【新增】德语翻译\n- 【优化】更新部分多语言翻译\n- 【修复】密码生成工具选中数据大概率没有数字的问题\n- 【修复】打开某些KDBV4失败的问题\n\n#### 2.1.3（2021/7/13）\n- 【新增】KDBV4支持\n- 【新增】过期的条目增加提示（中横线）\n- 【修复】群组可以移动到自己\n- 【修复】安全键盘其它字段无法填充\n- 【修复】历史记录没有按时间排序\n- 【修复】无法启动快速解锁界面\n- 【优化】安全键盘多条目去重\n\n#### 2.1.2 (2021/5/29)\n- 【新增】允许密码为空\n- 【新增】OneDrive增加提示说明\n- 【修复】第一次安装，没有加载等待动画\n- 【修复】android 11 没有弹出自动填充选择项\n- 【修复】首页创建条目到群组时，首页的条目数量提示没有增加\n- 【修复】修改密码后，图标变为默认图标\n- 【优化】首页滑动增加渐隐动画\n- 【优化】动画细节\n\n#### 2.1.1（2021/5/6）\n- 【修复】webview崩溃问题\n\n#### 2.1（20201/5/5）\n- 【新增】OneDrive支持\n- 【新增】增加法语翻译\n- 【新增】兼容API30\n- 【修复】保存备注出现的崩溃\n- 【修复】横屏时，界面尺寸被放大\n- 【修复】WebDav多次Unlock导致的问题\n\n#### 2.0.2 (2021/4/1)\n- 【新增】关闭加载动画选择项[设置](route://keepassA.com/kpa?activity=SettingActivity) -> 界面设置\n- 【新增】不自动锁定功能[设置](route://keepassA.com/kpa?activity=SettingActivity&type=app) -> 数据库设置\n- 【新增】俄语（40%），感谢[@KovalevArtem](https://github.com/KovalevArtem)\n- 【新增】挪威语（50%），感谢[@Allan Nordhøy](https://github.com/comradekingu)\n- 【优化】优化英文翻译，感谢[@Allan Nordhøy](https://github.com/comradekingu)\n- 【修复】一个动画关闭掉的问题\n- 【修复】点击应设置崩溃的问题\n\n#### 2.0.1（2021/3/27）\n- 【修复】webdav登陆问题\n- 【修复】多语言问题\n\n#### 2.0（2021/3/23）\n- 【新增】回收站不显示添加群组\\条目按钮\n- 【新增】是否隐藏状态栏，[点击设置](route://keepassA.com/kpa?activity=SettingActivity) -> 界面设置\n- 【新增】仅密钥登陆\n- 【优化】大量动画\n- 【优化】默认图标全采用MD风格图标\n- 【优化】图标选择将使用底部弹出的的方式\n- 【修复】定时锁定数据库无效的问题\n- 【修复】跳转动画丢失的问题\n- 【修复】无法切换历史数据\n\n#### 1.8.4（2021/3/4）\n- 重构创建数据库的逻辑\n- 增加在Webdav上创建数据库的功能\n\n![webdavCreate](https://gitee.com/laoyuyu/blog/raw/master/keepassA/webdavCreate.png)\n- 修复一些崩溃问题\n\n\n#### 1.8.3 (2021/2/25)\n- 修复一些崩溃问题\n- 升级kotlin版本\n- 移除kotlin-android-extensions\n\n#### 1.8.2 (2021/2/17)\n- 修复屏幕锁定后，点击通知进入软件出现的崩溃问题\n- 修复获取steam totp 崩溃问题\n- 升级kotlin 版本\n\n#### 1.8.1 (2021/2/14)\n- 修复数据库一直加载的问题\n- 修复一个附件问题\n- 增加 Argon2 ID 支持\n- 增加 keyfile 2.0 支持\n\n#### 1.8 (2021/2/14)\n- 修复一个缓存问题导致的webdav打不开的问题\n- 优化打开数据库的速度\n\n#### 1.7 (2021/2/4)\n- 增加当包名不匹配时，将会从url匹配关键字\n- 增加参考条目提示\n- 增加[屏幕锁定，自动锁定数据库功能](route://keepassA.com/kpa?activity=SettingActivity&type=app)\n- note增加编辑功能\n- 增加用户名历史记录功能\n\n![user_drop_down_list](https://gitee.com/laoyuyu/blog/raw/master/keepassA/userDropdownList.png)\n- 搜索忽略回收站中的条目\n- 修复夜间模式下，自动填充文字无法显示的问题\n- 修复某些应用无法自动填充的问题\n- 修复一些崩溃问题\n\n\n#### 1.6 (2020/11/28)\n- 自动填充模块，修复关联条目后，返回到应用，已关联的数据无法显示的问题\n- 自动填充模块，修复多个输入框时，输入框数据填充错误的问题\n- 自动填充模块，增加浏览器填充功能\n- 自动填充模块，增加其它按钮\n- 设置模块，增加解锁数据库后，主页优先显示所有条目功能，[点击设置](route://keepassA.com/kpa?activity=SettingActivity&type=db)\n- 条目详情模块，note 自动增加展开和收缩功能\n\n![note_expand](https://gitee.com/laoyuyu/blog/raw/master/keepassA/noteExpand.png)\n- 优化主页闪烁问题\n- 感谢[@DominicDesbiens](https://github.com/DominicDesbiens)提供了加拿大法语翻译\n\n#### 1.5 (2020/11/5)\n- 增加群组搜索功能\n- 增加开放源码协议说明\n- 增加移动条目和群组的功能\n\n![ime](https://gitee.com/laoyuyu/blog/raw/master/keepassA/moveData.png)\n- 修复无法增加二级群组的问题\n- 修复一个webdav获取文件信息导致的崩溃问题\n- 修复一些讨厌的崩溃问题\n\n#### 1.4.1 (2020/10/29)\n- 升级android studio 到4.1\n- 搜索增加高亮关键字\n- 修复快速解锁的一个崩溃问题\n\n#### 1.4 (2020/10/28)\n- 升级kotlin版本\n- 增加群组排序功能\n- 增加字段引用功能\n- 增加[安全键盘](route://keepassA.com/kpa?activity=ime)\n\n![ime](https://gitee.com/laoyuyu/blog/raw/master/keepassA/ime.png)\n- 修复快速解锁界面，无法删除所有短密码的问题\n- 修复webdav登陆超时导致的崩溃问题\n- 修复一些讨厌的崩溃问题\n\n#### 1.3 (2020/9/22)\n- 增加TOTP令牌设置功能，编辑条目，点击添加更多按钮便可以显示该功能界面\n\n![otp_setting](https://gitee.com/laoyuyu/blog/raw/master/keepassA/otpsetting.png)\n\n#### 1.2 (2020/9/2)\n- 修复一些讨厌的崩溃问题\n\n#### 1.1 (2020/8/21)\n- KeepassA开源了，已经托管在[github](https://github.com/AriaLyy/KeepassA)上\n- 修复了一些讨厌的bug\n\n#### 1.0.1.2 (2020/8/7)\n- 重构[指纹解锁设置界面](route://keepassA.com/kpa?activity=FingerprintActivity)\n- 增加下拉同步数据库功能\n- 增加手机root/模拟器检测\n- 增加直接删除条目/群组功能\n- 修复无法识别含有特殊字符的http地址的问题\n- 修复自动填充服务，无法保存数据的问题\n\n#### 1.0.1.1 (2020/7/22)\n- 增加[webdav](route://keepassA.com/kpa?activity=WebDavLoginDialog)\n- 修复小米手机增加指纹后，再进行指纹解锁导致的崩溃问题\n\n#### 1.0.1.0 (2020/7/10)\n- 修复数据库已锁定后，点击通知栏通知跳转错误的问题\n- TOTP增加倒计时效果\n- 修复同步数据库时，相同条目总是提示的问题\n- 修复一些讨厌的崩溃问题\n\n#### 1.0.0.9 (2020/7/11)\n- 修复手动进行快速锁定后，无法进入其它页面的问题\n- 修复通知栏图标过小的问题\n- 修复打开指纹解锁界面崩溃的问题\n- 修复自动填充服务崩溃的问题\n- 优化指纹解锁功能的逻辑\n\n#### 1.0.0.7 (2020/6/14)\n- 增加历史版本对话框\n- 增加震动反馈\n- 修复dropbox打开记录无法保存的问题\n\n#### 1.0.0.6 (2020/6/7)\n- 修复一些讨厌bug\n- 增加[指纹解锁](route://keepassA.com/kpa?activity=FingerprintActivity)\n\n#### 1.0.0.4 (2020/6/6)\n- 修复一些讨厌的bug\n\n#### 1.0.0.3 (2020/5/13)\n- 修复一些讨厌的bug\n\n#### 1.0.0.2 (2020/5/10)\n- 修复一些讨厌的bug\n\n#### 1.0.0.1 (2020/5/3)\n- 修复一些讨厌的bug\n"
  },
  {
    "path": "app/src/main/assets/version_log/version_log_zh_TW.md",
    "content": "#### 2.0.1（2021/3/27）\n- [修復]webdav登陸問題\n- [修復]多語言問題\n\n#### 2.0（2021/3/23）\n- [新增]回收站不顯示添加群組\\條目按鈕\n- [新增]是否隱藏狀態欄，[點擊設置](route://keepassA.com/kpa?activity=SettingActivity) -> 界面設置\n- [新增]僅密鑰登陸\n- [優化]大量動畫\n- [優化]默認圖標全採用MD風格圖標\n- [優化]圖標選擇將使用底部彈出的的方式\n- [修復]定時鎖定數據庫無效的問題\n- [修復]跳轉動畫丟失的問題\n- [修復]無法切換歷史數據\n\n#### 1.8.4 （2021/3/4）\n- 重構創建數據庫的邏輯\n- 增加在Webdav上創建數據庫的功能\n\n![webdavCreate](https://raw.githubusercontent.com/AriaLyy/KeepassA/master/img/webdavCreate.png)\n- 修復一些崩潰問題\n\n#### 1.8.3 (2021/2/25)\n- 修復一些崩潰問題\n- 升級kotlin版本\n- 移除kotlin-android-extensions\n\n#### 1.8.2 (2021/2/17)\n- 修復屏幕鎖定後，點擊通知進入軟件出現的崩潰問題\n- 修復獲取steam totp 崩潰問題\n- 升級kotlin 版本\n\n#### 1.8.1 (2021/2/15)\n- 修復數據庫一直加載的問題\n- 修復一個附件問題\n- 增加Argon2 ID 支持\n- 增加keyfile 2.0 支持\n\n#### 1.8 (2021/2/14)\n- 修復一個緩存問題導致的webdav打不開的問題\n- 優化打開數據庫的速度\n\n#### 1.7 (2021/2/4)\n- 增加當包名不匹配時，將會從url匹配關鍵字\n- 增加參考條目提示\n- 增加[屏幕鎖定，自動鎖定數據庫功能](route://keepassA.com/kpa?activity=SettingActivity&type=app)\n- note增加編輯功能\n- 增加用戶名歷史記錄功能\n\n![user_drop_down_list](https://raw.githubusercontent.com/AriaLyy/KeepassA/master/img/userDropdownList.png)\n- 搜索忽略回收站中的條目\n- 修復夜間模式下，自動填充文字無法顯示的問題\n- 修復某些應用無法自動填充的問題\n- 修復一些崩潰問題\n\n#### 1.6 (2020/11/28)\n- 自動填充模塊，修復關聯條目後，返回到應用，已關聯的數據無法顯示的問題\n- 自動填充模塊，修復多個輸入框時，輸入框數據填充錯誤的問題\n- 自動填充模塊，增加瀏覽器填充功能\n- 自動填充模塊，增加其它按鈕\n- 設置模塊，增加解鎖數據庫後，主頁優先顯示所有條目功能，[點擊設置](route://keepassA.com/kpa?activity=SettingActivity&type=db)\n- 條目詳情模塊，note 自動增加展開和收縮功能\n\n![ime](https://raw.githubusercontent.com/AriaLyy/KeepassA/master/img/noteExpand.png)\n- 優化主頁閃爍問題\n- 感謝[@DominicDesbiens](https://github.com/DominicDesbiens)提供了加拿大法語翻譯\n\n#### 1.5 (2020/11/5)\n- 增加群組搜索功能\n- 增加開放源碼協議說明\n- 增加移動條目和群組的功能\n\n![ime](https://raw.githubusercontent.com/AriaLyy/KeepassA/master/img/moveData.png)\n- 修復無法增加二級群組的問題\n- 修復一個webdav獲取文件信息導致的崩潰問題\n- 修復一些討厭的崩潰問題\n\n#### 1.4.1 (2020/10/29)\n- 升級android studio 到4.1\n- 搜索增加高亮關鍵字\n- 修復快速解鎖的一個崩潰問題\n\n#### 1.4 (2020/10/28)\n- 升級kotlin版本\n- 增加群組排序功能\n- 增加字段引用功能\n- 增加[安全鍵盤](route://keepassA.com/kpa?activity=ime)\n\n![ime](https://raw.githubusercontent.com/AriaLyy/KeepassA/master/img/ime.png)\n- 修復快速解鎖界面，無法刪除所有短密碼的問題\n- 修復webdav登陸超時導致的崩潰問題\n- 修復一些討厭的崩潰問題\n\n#### 1.3 (2020/9/22)\n- 增加TOTP令牌設置功能，編輯條目，點擊添加更多按鈕便可以顯示該功能界面\n\n![otp_setting](https://raw.githubusercontent.com/AriaLyy/KeepassA/master/img/otpsetting.png)\n\n#### 1.2 (2020/9/2)\n- 修復一些討厭的崩潰問題\n\n#### 1.1 (2020/8/21)\n- KeepassA開源了，已經託管在[github](https://github.com/AriaLyy/KeepassA)上\n- 修復了一些討厭的bug\n\n#### 1.0.1.2 (2020/7/22)\n- 重構[指紋解鎖設置界面](route://keepassA.com/kpa?activity=FingerprintActivity)\n- 增加下拉同步數據庫功能\n- 增加手機root/模擬器檢測\n- 增加直接刪除條目/群組功能\n- 修復無法識別含有特殊字符的http地址的問題\n- 修復自動填充服務，無法保存數據的問題\n\n#### 1.0.1.0 (2020/7/10)\n- 修復數據庫已鎖定後，點擊通知欄通知跳轉錯誤的問題\n- TOTP增加倒計時效果\n- 修復同步數據庫時，相同條目總是提示的問題\n- 修復一些討厭的崩潰問題\n\n#### 1.0.0.9 (2020/7/11)\n- 修復手動進行快速鎖定後，無法進入其它頁面的問題\n- 修復通知欄圖標過小的問題\n- 修復打開指紋解鎖界面崩潰的問題\n- 修復自動填充服務崩潰的問題\n- 優化指紋解鎖功能的邏輯\n\n#### 1.0.0.7 (2020/6/14)\n- 增加歷史版本對話框\n- 增加震動反饋\n- 修復dropbox打開記錄無法保存的問題\n\n#### 1.0.0.6 (2020/6/7)\n- 修復一些討厭bug\n- 增加[指紋解鎖](route://keepassA.com/kpa?activity=FingerprintActivity)\n\n#### 1.0.0.4 (2020/6/6)\n- 修復一些討厭bug\n\n#### 1.0.0.3 (2020/5/13)\n- 修復一些討厭bug\n\n#### 1.0.0.2 (2020/5/10)\n- 修復一些討厭bug\n\n#### 1.0.0.1 (2020/5/3)\n- 修復一些討厭bug\n"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/base/AnimState.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.base\n\nenum class AnimState {\n  ALL,\n  NOT_ANIM\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/base/BaseActivity.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.base\n\nimport android.annotation.SuppressLint\nimport android.app.Activity\nimport android.app.ActivityOptions\nimport android.content.Context\nimport android.content.Intent\nimport android.os.Bundle\nimport android.os.Looper\nimport android.util.Pair\nimport android.view.View\nimport android.view.WindowManager\nimport androidx.appcompat.widget.Toolbar\nimport androidx.databinding.ViewDataBinding\nimport com.arialyy.frame.core.AbsActivity\nimport com.arialyy.frame.util.ReflectionUtil\nimport com.blankj.utilcode.util.AppUtils\nimport com.blankj.utilcode.util.ScreenUtils\nimport com.gyf.immersionbar.ImmersionBar\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.AnimState.NOT_ANIM\nimport com.lyy.keepassa.util.HitUtil\nimport com.lyy.keepassa.util.KdbUtil.isNull\nimport com.lyy.keepassa.util.KeepassAUtil\nimport com.lyy.keepassa.util.LanguageUtil\nimport me.jessyan.autosize.AutoSizeConfig\nimport timber.log.Timber\nimport java.lang.reflect.Field\n\n/**\n * Created by Lyy on 2016/9/27.\n */\nabstract class BaseActivity<VB : ViewDataBinding> : AbsActivity<VB>() {\n\n  protected lateinit var toolbar: Toolbar\n  private var animState = AnimState.ALL\n\n  companion object {\n    var showStatusBar = false\n  }\n\n  override fun initData(savedInstanceState: Bundle?) {\n    try {\n      toolbar = findViewById(R.id.kpa_toolbar)\n      toolbar.setNavigationOnClickListener { finishAfterTransition() }\n    } catch (e: Exception) {\n      Timber.w(e)\n    }\n  }\n\n  open fun useAnim() = AnimState.ALL\n\n  override fun onPreInit(): Boolean {\n    if (!KeepassAUtil.instance.isHomeActivity(this)\n      && (BaseApp.KDB.isNull() || BaseApp.dbRecord == null)\n    ) {\n      BaseApp.isLocked = true\n      HitUtil.toaskShort(getString(R.string.notify_db_locked))\n      // Cannot be used finishAfterTransition(), because binding invalid\n      finish()\n\n      return false\n    }\n    return true\n  }\n\n  override fun onCreate(savedInstanceState: Bundle?) {\n    AutoSizeConfig.getInstance().screenHeight = ScreenUtils.getScreenHeight()\n    AutoSizeConfig.getInstance().screenWidth = ScreenUtils.getScreenWidth()\n    super.onCreate(savedInstanceState)\n\n    // 进入系统多任务，界面变空白，设置无法截图\n    if (!AppUtils.isAppDebug()) {\n      window.setFlags(\n        WindowManager.LayoutParams.FLAG_SECURE,\n        WindowManager.LayoutParams.FLAG_SECURE\n      )\n    }\n    animState = useAnim()\n    setWindowAnim()\n\n    handleStatusBar()\n  }\n\n  private fun handleStatusBar() {\n    ImmersionBar.with(this)\n      .statusBarColor(R.color.background_color)\n      .autoDarkModeEnable(true)\n      .autoStatusBarDarkModeEnable(true, 0.2f) //自动状态栏字体变色，必须指定状态栏颜色才可以自动变色哦\n      .flymeOSStatusBarFontColor(R.color.text_black_color)\n      .fitsSystemWindows(true)\n//          .hideBar(FLAG_HIDE_STATUS_BAR)\n      .autoNavigationBarDarkModeEnable(true, 0.2f) // 自动导航栏图标变色，必须指定导航栏颜色才可以自动变色哦\n      .navigationBarColor(R.color.background_color)\n      .statusBarDarkFont(\n        true, 0.2f\n      )  //原理：如果当前设备支持状态栏字体变色，会设置状态栏字体为黑色，如果当前设备不支持状态栏字体变色，会使当前状态栏加上透明度，否则不执行透明度\n      .init()\n    return\n  }\n\n  override fun attachBaseContext(newBase: Context?) {\n    super.attachBaseContext(LanguageUtil.setLanguage(newBase!!, BaseApp.currentLang))\n  }\n\n  private fun setWindowAnim() {\n    if (animState == NOT_ANIM) {\n      return\n    }\n\n    // salide 为滑入，其它动画效果参考：https://github.com/lgvalle/Material-Animations\n    // A -> B, B的进入动画\n    // window.enterTransition = TransitionInflater.from(this)\n    //   .inflateTransition(R.transition.slide_enter)\n\n    // A -> B, A的退出动画\n    // window.exitTransition = TransitionInflater.from(this)\n    //   .inflateTransition(R.transition.slide_exit)\n\n    // // A <- B, B的返回动画\n    // window.returnTransition = TransitionInflater.from(this)\n    //   .inflateTransition(R.transition.slide_return)\n    //\n    // // A <- B, A的进入动画\n    // window.reenterTransition = TransitionInflater.from(this)\n    //   .inflateTransition(R.transition.slide_reeter)\n\n    // A -> B, B的enter动画和A的exit动画是否同时执行，false 禁止\n    // window.allowEnterTransitionOverlap = true\n    // A <- B, A的reenter和B的return动画是否同时执行，false 禁止\n    // window.allowReturnTransitionOverlap = true\n\n    // reenterTransition、returnTransition 是方向动画\n//    EnterTransition <-> ReturnTransition\n//    ExitTransition <-> ReenterTransition\n  }\n\n  protected fun showQuickUnlockDialog() {\n    KeepassAUtil.instance.lock()\n    finish()\n  }\n\n  override fun onRestart() {\n    super.onRestart()\n    Timber.d(\"onRestart\")\n    if (!KeepassAUtil.instance.isHomeActivity(this) && (BaseApp.KDB.isNull() || BaseApp.isLocked)) {\n      BaseApp.handler.postDelayed({\n        KeepassAUtil.instance.lock()\n        finish()\n      }, 150)\n      return\n    }\n  }\n\n  var isStartOtherActivity = false\n  override fun startActivity(\n    intent: Intent?,\n    options: Bundle?\n  ) {\n    super.startActivity(intent, options)\n    isStartOtherActivity = true\n    // overridePendingTransition(R.anim.translate_right_in, R.anim.translate_left_out)\n  }\n\n  /**\n   * Android10 Activity的onStop方法可能会导致共享元素动画失效，通过反射注入恢复共享元素动画\n   * @param activity\n   */\n  @SuppressLint(\"PrivateApi\")\n  private fun updateResume(activity: Activity) {\n    if (!isStartOtherActivity) {\n      return\n    }\n    Looper.myQueue()\n      .addIdleHandler {\n        try {\n          Timber.d(\"updateResume\")\n          ActivityOptions.makeSceneTransitionAnimation(this)\n          val stateField: Field = ReflectionUtil.getField(\n            Activity::class.java,\n            \"mActivityTransitionState\"\n          )\n\n          val stateObj = stateField.get(activity)\n          val activityTransitionStateClazz =\n            classLoader.loadClass(\"android.app.ActivityTransitionState\")\n\n          val mPendingExitNamesField: Field = ReflectionUtil.getField(\n            activityTransitionStateClazz,\n            \"mPendingExitNames\"\n          )\n          val b = buildSharedElements()\n          mPendingExitNamesField.set(stateObj, b)\n        } catch (e: java.lang.Exception) {\n          Timber.e(e)\n        }\n        return@addIdleHandler false\n      }\n  }\n\n  /**\n   * @param sharedElements 共享元素属性\n   */\n  open fun buildSharedElements(vararg sharedElements: Pair<View, String>): ArrayList<String> {\n    val names = ArrayList<String>()\n    for (i in sharedElements.indices) {\n      val sharedElement: Pair<View, String> = sharedElements[i]\n      val sharedElementName = sharedElement.second\n        ?: throw IllegalArgumentException(\"Shared element name must not be null\")\n      names.add(sharedElementName)\n      val view = sharedElement.first\n        ?: throw IllegalArgumentException(\"Shared element must not be null\")\n//      views.add(sharedElement.first)\n    }\n    return names\n  }\n\n  override fun onResume() {\n    super.onResume()\n    // 启动定时器\n    KeepassAUtil.instance.startLockTimer(this)\n    // updateResume(this)\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/base/BaseApp.java",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.base;\n\nimport android.content.Context;\nimport android.content.Intent;\nimport android.content.IntentFilter;\nimport android.os.Handler;\nimport android.os.Looper;\nimport androidx.annotation.Nullable;\nimport androidx.appcompat.app.AppCompatDelegate;\nimport androidx.multidex.MultiDexApplication;\nimport androidx.preference.PreferenceManager;\nimport com.alibaba.android.arouter.launcher.ARouter;\nimport com.arialyy.frame.core.AbsFrame;\nimport com.arialyy.frame.router.Routerfit;\nimport com.keepassdroid.Database;\nimport com.lyy.keepassa.R;\nimport com.lyy.keepassa.common.PassType;\nimport com.lyy.keepassa.dao.AppDatabase;\nimport com.lyy.keepassa.entity.DbHistoryRecord;\nimport com.lyy.keepassa.receiver.ScreenLockReceiver;\nimport com.lyy.keepassa.router.ServiceRouter;\nimport com.lyy.keepassa.service.feat.KpaSdkService;\nimport com.lyy.keepassa.util.CommonKVStorage;\nimport com.lyy.keepassa.util.LanguageUtil;\nimport com.lyy.keepassa.view.StorageType;\nimport java.util.Locale;\nimport me.weishu.reflection.Reflection;\n\npublic class BaseApp extends MultiDexApplication {\n\n  public static BaseApp APP;\n  public static Handler handler;\n  public static Database KDB;\n  @Nullable\n  public static DbHistoryRecord dbRecord;\n  public static AppDatabase appDatabase;\n  public static String dbName = \"\";\n  public static String dbFileName = \"\";\n  public static String dbVersion = \"Keepass 4.0\";\n  // SHA 256 加密后的数据库主密码\n  public static String dbPass = \"\";\n  public static String shortPass = \"\";\n  // SHA 256 加密后的密钥路径\n  public static String dbKeyPath = \"\";\n  public static boolean isV4 = true;\n  public static Locale currentLang = Locale.ENGLISH;\n  public static Boolean isLocked = true;\n\n  public static int passType = PassType.INSTANCE.getONLY_PASS();\n\n  public static boolean isAFS() {\n    return dbRecord == null || StorageType.valueOf(dbRecord.getType()) == StorageType.AFS;\n  }\n\n  @Override\n  protected void attachBaseContext(Context base) {\n    currentLang = setLanguage(base);\n    //super.attachBaseContext(LanguageUtil.INSTANCE.setLanguage(base, currentLang));\n    super.attachBaseContext(base);\n    setThemeStyle();\n    Reflection.unseal(base);\n  }\n\n  private void setThemeStyle() {\n    String mode = PreferenceManager.getDefaultSharedPreferences(this)\n        .getString(getString(R.string.set_key_theme_style), \"0\");\n    switch (mode) {\n      case \"0\":\n        AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM);\n        break;\n      case \"1\":\n        AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);\n        break;\n      case \"2\":\n        AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);\n        break;\n    }\n  }\n\n  @Override public void onCreate() {\n    super.onCreate();\n    AbsFrame.init(this);\n    APP = this;\n    handler = new Handler(Looper.getMainLooper());\n    ARouter.init(this); // 尽可能早，推荐在Application中初始化\n    KpaSdkService kpaSdkService = Routerfit.INSTANCE.create(ServiceRouter.class, null).getKpaSdkService();\n    kpaSdkService.preInitSdk(this);\n    initReceiver();\n    if (CommonKVStorage.INSTANCE.getBoolean(Constance.IS_AGREE_PRIVACY_AGREEMENT, false)) {\n      kpaSdkService.initThirdSdk(this);\n    }\n  }\n\n  /**\n   * init receiver\n   */\n  public void initReceiver() {\n    boolean isNeedRegScreenLockReceiver = PreferenceManager.getDefaultSharedPreferences(BaseApp.APP)\n        .getBoolean(getString(R.string.set_key_lock_screen_auto_lock_db), false);\n    if (isNeedRegScreenLockReceiver) {\n      ScreenLockReceiver receiver = new ScreenLockReceiver();\n      IntentFilter inf = new IntentFilter();\n      inf.addAction(Intent.ACTION_SCREEN_OFF);\n      inf.addAction(Intent.ACTION_USER_PRESENT);\n      registerReceiver(receiver, inf);\n    }\n  }\n\n  /**\n   * 设置语言\n   * 优先读取保存的语言，如果配置的语言存在，设置该语言为app的语言\n   * 如果没有已记录的语言，读取系统当前语言\n   * 如果系统语言不在支持列表的[LanguageUtil.SUPPORT_LAN]中，将app语言设置为英文\n   * 如果系统语言在支持列表中，设置该语言为app的语言\n   */\n  private Locale setLanguage(Context context) {\n    Locale lang = LanguageUtil.INSTANCE.getDefLanguage(context);\n    if (lang != null) {\n      currentLang = lang;\n    } else {\n      Locale def = LanguageUtil.INSTANCE.getSysCurrentLan();\n      lang = new Locale(def.getLanguage(), def.getCountry());\n      if (LanguageUtil.SUPPORT_LAN.contains(lang)) {\n        LanguageUtil.INSTANCE.setLanguage(context, lang);\n      } else {\n        LanguageUtil.INSTANCE.setLanguage(context, Locale.ENGLISH);\n      }\n      LanguageUtil.INSTANCE.saveLanguage(context, lang);\n    }\n    return lang;\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/base/BaseBottomSheetDialogFragment.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.base\n\nimport android.content.Context\nimport android.os.Bundle\nimport androidx.databinding.ViewDataBinding\nimport com.arialyy.frame.core.AbsBottomSheetDialogFragment\nimport com.lyy.keepassa.util.LanguageUtil\n\nabstract class BaseBottomSheetDialogFragment<VB : ViewDataBinding> : AbsBottomSheetDialogFragment<VB>() {\n\n  override fun onAttach(context: Context) {\n    super.onAttach(LanguageUtil.setLanguage(context, BaseApp.currentLang))\n  }\n\n  override fun init(savedInstanceState: Bundle?) {\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/base/BaseDialog.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.base\n\nimport android.content.Context\nimport android.os.Build.VERSION\nimport android.view.View\nimport androidx.databinding.ViewDataBinding\nimport androidx.fragment.app.FragmentManager\nimport com.arialyy.frame.base.FrameDialog\nimport com.lyy.keepassa.util.LanguageUtil\nimport timber.log.Timber\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 2021/1/13\n **/\nabstract class BaseDialog<VB : ViewDataBinding> : FrameDialog<VB>() {\n\n  private var onDismissListener: OnDialogDismissListener? = null\n\n  fun setOnDismissListener(dismissListener: OnDialogDismissListener) {\n    this.onDismissListener = dismissListener\n  }\n\n  override fun initData() {\n    super.initData()\n    dialog?.window?.decorView?.setOnSystemUiVisibilityChangeListener { _ ->\n      val uiOptions = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or  //布局位于状态栏下方\n        View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or\n        View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY\n      dialog?.window?.decorView?.systemUiVisibility = uiOptions\n    }\n  }\n\n  override fun onAttach(context: Context) {\n    super.onAttach(LanguageUtil.setLanguage(context, BaseApp.currentLang))\n  }\n\n  override fun show(manager: FragmentManager, tag: String?) {\n    if (manager.isStateSaved) {\n      Timber.d(\"ac 已经保存状态了，不再启动对话框\")\n      return\n    }\n    if (isAdded || manager.findFragmentByTag(tag) != null) {\n      Timber.d(\"fragment 已经被add\")\n      return\n    }\n    try {\n      //在每个add事务前增加一个remove事务，防止连续的add，需要使用的commit 而非其他方法\n      manager.beginTransaction().remove(this).commit()\n      super.show(manager, tag)\n    } catch (e: Exception) {\n      //同一实例使用不同的tag会异常,这里捕获一下\n      Timber.e(e)\n    }\n  }\n\n  override fun dismiss() {\n    if (!isVisible) {\n      Timber.d(\"fragment 还没被加载\")\n      return\n    }\n    if (childFragmentManager.isStateSaved) {\n      Timber.d(\"状态已经保存，不再dismiss\")\n      return\n    }\n    onDismissListener?.onDismiss()\n    super.dismiss()\n  }\n\n  override fun onDestroy() {\n    super.onDestroy()\n    onDismissListener = null\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/base/BaseFragment.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.base\n\nimport android.content.Context\nimport android.content.Intent\nimport android.os.Bundle\nimport android.transition.ChangeBounds\nimport android.transition.Slide\nimport android.transition.TransitionSet\nimport android.view.Gravity\nimport android.view.LayoutInflater\nimport android.view.View\nimport android.view.ViewGroup\nimport androidx.databinding.ViewDataBinding\nimport com.arialyy.frame.core.AbsFragment\nimport com.lyy.keepassa.util.AutoLockDbUtil\nimport com.lyy.keepassa.util.KeepassAUtil\nimport com.lyy.keepassa.util.LanguageUtil\nimport me.jessyan.autosize.AutoSize\n\nabstract class BaseFragment<VB : ViewDataBinding> : AbsFragment<VB>() {\n\n  override fun onAttach(context: Context) {\n    super.onAttach(LanguageUtil.setLanguage(context, BaseApp.currentLang))\n  }\n\n  override fun onCreateView(\n    inflater: LayoutInflater,\n    container: ViewGroup?,\n    savedInstanceState: Bundle?\n  ): View? {\n//    AutoSize.autoConvertDensity(activity, 411f, true);\n    return super.onCreateView(inflater, container, savedInstanceState)\n  }\n\n  override fun onActivityCreated(savedInstanceState: Bundle?) {\n//    setWindowAnim()\n    super.onActivityCreated(savedInstanceState)\n  }\n\n  fun getRootView(): View = mRootView\n\n  private fun setWindowAnim() {\n    // salide 为滑入，其它动画效果参考：https://github.com/lgvalle/Material-Animations\n    // 第一次进入activity的动画\n    val enterSet = TransitionSet()\n    enterSet.addTransition(Slide(Gravity.END))\n        .addTransition(ChangeBounds()) // 右边进入左边\n    enterSet.duration = 400\n    enterSet.excludeTarget(android.R.id.navigationBarBackground, true) // 导航栏不参与动画\n    enterSet.excludeTarget(android.R.id.statusBarBackground, true) // 状态栏不参与动画\n    enterTransition = enterSet\n\n    // 退出当前activity的动画\n    val exitSet = TransitionSet()\n    exitSet.addTransition(Slide(Gravity.START))\n        .addTransition(ChangeBounds()) // 左边到右边\n    exitSet.duration = 400\n    exitSet.excludeTarget(android.R.id.navigationBarBackground, true) // 导航栏不参与动画\n    exitSet.excludeTarget(android.R.id.statusBarBackground, true) // 状态栏不参与动画\n    exitTransition = exitSet\n\n    // 重新进入activity的动画\n    val reEnterSet = TransitionSet()\n    reEnterSet.addTransition(Slide(Gravity.END))\n        .addTransition(ChangeBounds())\n    reEnterSet.duration = 400\n    reEnterSet.excludeTarget(android.R.id.navigationBarBackground, true) // 导航栏不参与动画\n    reEnterSet.excludeTarget(android.R.id.statusBarBackground, true) // 状态栏不参与动画\n    returnTransition = reEnterSet\n\n  }\n\n  override fun onResume() {\n    super.onResume()\n    KeepassAUtil.instance.startLockTimer(this)\n  }\n\n  override fun onDelayLoad() {\n\n  }\n\n  override fun startActivity(\n    intent: Intent?,\n    options: Bundle?\n  ) {\n\n    super.startActivity(intent, options)\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/base/BaseModule.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.base\n\nimport com.arialyy.frame.base.BaseViewModule\n\n/**\n * Created by Lyy on 2016/9/27.\n */\nopen class BaseModule : BaseViewModule()"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/base/BaseService.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.base\n\nimport android.app.Service\nimport android.content.Context\nimport com.lyy.keepassa.util.LanguageUtil\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 2021/5/6\n **/\nabstract class BaseService:Service() {\n\n  override fun attachBaseContext(newBase: Context?) {\n    super.attachBaseContext(LanguageUtil.setLanguage(newBase!!, BaseApp.currentLang))\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/base/Constance.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.base\n\nclass Constance {\n  companion object {\n    const val DEBUG = true\n    const val PRE_FILE_NAME = \"KeepassA\"\n    const val VERSION_CODE = \"VersionCode\"\n    const val PRE_KEY_START_APP_NUM = \"PRE_KEY_START_APP_NUM\"\n    const val START_DONATE_JUDGMENT_VALUE = 60\n    const val KPA_IS_COLLECTION = \"KPA_IS_COLLECTION\"\n    const val IS_AGREE_PRIVACY_AGREEMENT = \"IS_AGREE_PRIVACY_AGREEMENT\"\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/base/DbMigration.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.base\n\nimport androidx.room.migration.Migration\nimport androidx.sqlite.db.SupportSQLiteDatabase\n\nobject DbMigration {\n\n  fun MIGRATION_2_3(): Migration {\n    return object : Migration(2, 3) {\n      override fun migrate(database: SupportSQLiteDatabase) {\n        database.execSQL(\"CREATE TABLE QuickUnLockRecord (uid INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, dbUri TEXT NOT NULL, dbPass TEXT NOT NULL, keyPath TEXT, isUseKey INTEGER NOT NULL, isFullUnlock INTEGER NOT NULL, passIv BLOB NOT NULL)\")\n\n      }\n    }\n  }\n\n  fun MIGRATION_3_4(): Migration {\n    return object : Migration(3, 4) {\n      override fun migrate(database: SupportSQLiteDatabase) {\n        // modify passIv can set null\n        database.execSQL(\"CREATE TABLE QuickUnLockRecord_TEMP (uid INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, dbUri TEXT NOT NULL, dbPass TEXT NOT NULL, keyPath TEXT, isUseKey INTEGER NOT NULL, isUseFingerprint INTEGER NOT NULL, passIv BLOB)\")\n        database.execSQL(\"INSERT INTO QuickUnLockRecord_TEMP(uid, dbUri, dbPass, keyPath, isUseKey, isUseFingerprint, passIv) SELECT uid, dbUri, dbPass, keyPath, isUseKey, isFullUnlock, passIv FROM QuickUnLockRecord\")\n        database.execSQL(\"DROP TABLE QuickUnLockRecord\")\n        database.execSQL(\"ALTER TABLE QuickUnLockRecord_TEMP RENAME TO QuickUnLockRecord\")\n        // renameDbRecord\n        database.execSQL(\"ALTER TABLE DbRecord RENAME TO DbHistoryRecord\")\n      }\n    }\n  }\n\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/base/KeyConstance.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.base\n\nobject KeyConstance {\n  const val KEY_DONT_SHOW_TIP = \"KEY_DONT_SHOW_TIP\"\n  const val KEY_LAST_TIP_START_TIME = \"KEY_LAST_TIP_START_TIME\"\n  const val TOTP = \"TOTP\"\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/base/OnDialogDismissListener.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.base\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 5:56 下午 2022/3/29\n **/\ninterface OnDialogDismissListener {\n  fun onDismiss()\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/base/ViewBindingAdapter.kt",
    "content": "package com.lyy.keepassa.base\n\nimport android.content.Context\nimport android.view.LayoutInflater\nimport android.view.ViewGroup\nimport androidx.recyclerview.widget.RecyclerView\nimport androidx.recyclerview.widget.RecyclerView.ViewHolder\nimport androidx.viewbinding.ViewBinding\nimport java.lang.reflect.ParameterizedType\n\nclass ViewBindingVH<V : ViewBinding>(val b: V) :\n  ViewHolder(b.root)\n\nabstract class AbsViewBindingAdapter<T, V : ViewBinding> :\n  RecyclerView.Adapter<ViewBindingVH<V>>() {\n  var data: MutableList<T> = mutableListOf()\n    internal set\n  lateinit var context: Context\n\n  // 通过反射创建ViewBinding\n  private fun viewBinding(parent: ViewGroup): V {\n    val parameterizedType = this.javaClass.genericSuperclass as ParameterizedType\n    val clazz: Class<V> = parameterizedType.actualTypeArguments[1] as Class<V>\n    val inflateMethod = clazz.getMethod(\n      \"inflate\",\n      LayoutInflater::class.java,\n      ViewGroup::class.java,\n      Boolean::class.java\n    )\n    return inflateMethod.invoke(null, LayoutInflater.from(parent.context), parent, false) as V\n  }\n\n  fun setData(list: MutableList<T>) {\n    data = list\n    notifyDataSetChanged()\n  }\n\n  override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewBindingVH<V> {\n    return ViewBindingVH(viewBinding(parent))\n  }\n\n  override fun onAttachedToRecyclerView(recyclerView: RecyclerView) {\n    super.onAttachedToRecyclerView(recyclerView)\n    context = recyclerView.context\n  }\n\n  override fun getItemCount(): Int {\n    return data.size\n  }\n\n  abstract fun bindData(binding: V, item: T)\n\n  open fun bindData(binding: V, item: T, payloads: MutableList<Any>) {}\n\n  override fun onBindViewHolder(holder: ViewBindingVH<V>, position: Int) {\n    bindData(holder.b, data[position])\n  }\n\n  override fun onBindViewHolder(\n    holder: ViewBindingVH<V>,\n    position: Int,\n    payloads: MutableList<Any>\n  ) {\n    super.onBindViewHolder(holder, position, payloads)\n    bindData(holder.b, data[position], payloads)\n  }\n}\n\ninterface IMultipleItem {\n  fun getType(): Int\n}\n\ninterface OnMultiItemAdapterListener<TYPE : IMultipleItem, ViewHolder : RecyclerView.ViewHolder> {\n  fun onCreate(context: Context, parent: ViewGroup, viewType: Int): ViewHolder\n\n  fun onBind(holder: ViewHolder, position: Int, item: TYPE)\n\n  fun onDetachedFromWindow(holder: ViewHolder) {}\n}\n\nabstract class AbsMultipleViewBindingAdapter :\n  RecyclerView.Adapter<ViewHolder>() {\n  private val typeMap =\n    hashMapOf<Int, OnMultiItemAdapterListener<IMultipleItem, ViewHolder>>()\n  var data: MutableList<IMultipleItem> = mutableListOf()\n    internal set\n\n  fun setData(list: MutableList<IMultipleItem>) {\n    data = list\n    notifyDataSetChanged()\n  }\n\n  fun <T : IMultipleItem, V : ViewHolder> addItemType(\n    type: Int,\n    adapter: OnMultiItemAdapterListener<T, V>\n  ) {\n    typeMap[type] = adapter as OnMultiItemAdapterListener<IMultipleItem, ViewHolder>\n  }\n\n  override fun getItemViewType(position: Int): Int {\n    if (data.isEmpty()) {\n      return -1\n    }\n    return data[position].getType()\n  }\n\n  override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {\n    if (viewType == -1) {\n      throw IllegalArgumentException(\"viewType类型错误\")\n    }\n    return typeMap[viewType]!!.onCreate(parent.context, parent, viewType)\n  }\n\n  override fun onBindViewHolder(holder: ViewHolder, position: Int) {\n    typeMap[getItemViewType(position)]!!.onBind(holder, position, data[position])\n  }\n\n  override fun getItemCount(): Int {\n    return data.size\n  }\n\n  override fun onViewDetachedFromWindow(holder: ViewHolder) {\n    super.onViewDetachedFromWindow(holder)\n    typeMap[getItemViewType(holder.absoluteAdapterPosition)]?.onDetachedFromWindow(holder)\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/common/PassType.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.common\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 2021/3/24\n **/\nobject PassType {\n  val ONLY_PASS = 0\n  val PASS_AND_KEY = 1\n  val ONLY_KEY = 2\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/common/SortType.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.common\n\nenum class SortType {\n  NONE,\n  CHAR_DESC,\n  CHAR_ASC,\n  TIME_DESC,\n  TIME_ASC\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/dao/AppDatabase.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.dao\n\nimport androidx.room.Database\nimport androidx.room.RoomDatabase\nimport com.lyy.keepassa.entity.CloudServiceInfo\nimport com.lyy.keepassa.entity.DbHistoryRecord\nimport com.lyy.keepassa.entity.EntryRecord\nimport com.lyy.keepassa.entity.QuickUnLockRecord\nimport com.lyy.keepassa.entity.SearchRecord\n\n@Database(\n    entities = [DbHistoryRecord::class, EntryRecord::class,\n      SearchRecord::class, CloudServiceInfo::class,\n      QuickUnLockRecord::class\n    ],\n    version = 4\n)\nabstract class AppDatabase : RoomDatabase() {\n  companion object {\n    const val DB_NAME = \"keepassA.db\"\n  }\n\n  abstract fun cloudServiceInfoDao(): CloudServiceInfoDao\n\n  abstract fun dbRecordDao(): DbRecordDao\n\n  abstract fun entryRecordDao(): EntryRecordDao\n\n  abstract fun searchRecordDao(): SearchDao\n\n  abstract fun quickUnlockDao(): QuickUnlockDao\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/dao/CloudServiceInfoDao.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.dao\n\nimport androidx.room.Dao\nimport androidx.room.Insert\nimport androidx.room.Query\nimport androidx.room.Update\nimport com.lyy.keepassa.entity.CloudServiceInfo\n\n@Dao\ninterface CloudServiceInfoDao {\n\n  @Update\n  suspend fun update(serviceInfo: CloudServiceInfo)\n\n  @Query(\"SELECT * FROM CloudServiceInfo WHERE cloudPath=:uri\")\n  suspend fun queryServiceInfo(uri: String): CloudServiceInfo?\n\n  @Insert\n  suspend fun saveServiceInfo(serviceInfo: CloudServiceInfo)\n\n  @Update\n  suspend fun updateServiceInfo(serviceInfo: CloudServiceInfo)\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/dao/DbRecordDao.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.dao\n\nimport androidx.room.Dao\nimport androidx.room.Delete\nimport androidx.room.Insert\nimport androidx.room.Query\nimport androidx.room.Update\nimport com.lyy.keepassa.entity.DbHistoryRecord\n\n@Dao\ninterface DbRecordDao {\n\n  @Query(\"SELECT * FROM DbHistoryRecord ORDER BY time DESC LIMIT 0, 1\")\n  suspend fun getLastRecord(): DbHistoryRecord\n\n  @Query(\"SELECT * FROM DbHistoryRecord ORDER BY time DESC\")\n  suspend fun getAllRecord(): List<DbHistoryRecord>\n\n  @Query(\"SELECT * FROM DbHistoryRecord WHERE localDbUri=:localDbUri\")\n  suspend fun findRecord(localDbUri: String): DbHistoryRecord?\n\n  @Query(\"SELECT * FROM DbHistoryRecord WHERE cloudDiskPath=:cloudPath\")\n  suspend fun findRecordByCloudPath(cloudPath: String): DbHistoryRecord?\n\n  @Insert\n  suspend fun saveRecord(record: DbHistoryRecord)\n\n  @Update\n  suspend fun updateRecord(record: DbHistoryRecord)\n\n  @Delete\n  suspend fun deleteRecord(record: DbHistoryRecord)\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/dao/EntryRecordDao.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.dao\n\nimport androidx.room.Dao\nimport androidx.room.Delete\nimport androidx.room.Insert\nimport androidx.room.Query\nimport androidx.room.Update\nimport com.lyy.keepassa.entity.EntryRecord\n\n@Dao\ninterface EntryRecordDao {\n\n  @Query(\"SELECT COUNT(uid) FROM EntryRecord WHERE dbFileUri = :dbFileUri\")\n  suspend fun hasRecord(dbFileUri: String): Int\n\n  @Query(\"SELECT * FROM EntryRecord WHERE uuid = :uuid AND dbFileUri = :dbFileUri\")\n  suspend fun getRecord(\n    uuid: ByteArray,\n    dbFileUri: String\n  ): EntryRecord?\n\n  /**\n   * 只获取50条历史记录\n   */\n  @Query(\"SELECT * FROM EntryRecord WHERE dbFileUri = :dbFileUri ORDER BY time DESC LIMIT 50\")\n  suspend fun getRecord(dbFileUri: String): List<EntryRecord>\n\n  @Insert\n  suspend fun saveRecord(record: EntryRecord)\n\n  @Update\n  suspend fun updateRecord(record: EntryRecord)\n\n  @Delete\n  suspend fun delReocrd(record: EntryRecord)\n\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/dao/QuickUnlockDao.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.dao\n\nimport androidx.room.Dao\nimport androidx.room.Delete\nimport androidx.room.Insert\nimport androidx.room.Query\nimport androidx.room.Update\nimport com.lyy.keepassa.entity.QuickUnLockRecord\n\n@Dao\ninterface QuickUnlockDao {\n\n  @Query(\"SELECT * FROM QuickUnLockRecord WHERE dbUri=:dbUri\")\n  suspend fun findRecord(dbUri: String): QuickUnLockRecord?\n\n  @Query(\"SELECT * FROM QuickUnLockRecord\")\n  suspend fun getAllRecord(): List<QuickUnLockRecord>?\n\n  @Insert\n  suspend fun saveRecord(record: QuickUnLockRecord)\n\n  @Update\n  suspend fun updateRecord(record: QuickUnLockRecord)\n\n  @Delete\n  suspend fun deleteRecord(record: QuickUnLockRecord)\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/dao/SearchDao.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.dao\n\nimport androidx.room.Dao\nimport androidx.room.Delete\nimport androidx.room.Insert\nimport androidx.room.Query\nimport androidx.room.Update\nimport com.lyy.keepassa.entity.SearchRecord\n\n@Dao\ninterface SearchDao {\n\n  @Insert\n  suspend fun saveRecord(record: SearchRecord)\n\n  @Query(\"SELECT * FROM SearchRecord WHERE title=:title\")\n  suspend fun getRecord(title: String): SearchRecord?\n\n  @Query(\"SELECT * FROM SearchRecord ORDER BY time DESC LIMIT 5\")\n  suspend fun getSearchRecord(): List<SearchRecord>\n\n  @Delete\n  suspend fun delRecord(record: SearchRecord)\n\n  @Update\n  suspend fun updateRecord(record: SearchRecord)\n\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/entity/AutoFillParam.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\npackage com.lyy.keepassa.entity\n\nimport android.os.Parcelable\nimport kotlinx.parcelize.Parcelize\n\n/**\n * 自动填充参数\n */\n@Parcelize\ndata class AutoFillParam(\n  val apkPkgName: String, // other apk packageName\n  val domain: String? = null,\n  val isSave: Boolean = false, // is save mode\n  val saveUserName: String? = null, // User name at the of saving\n  val savePass: String? = null  // Password name at the of saving\n) : Parcelable\n"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/entity/CloudServiceInfo.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.entity\n\nimport androidx.room.ColumnInfo\nimport androidx.room.Entity\nimport androidx.room.PrimaryKey\n\n/**\n * 云端服务器验证信息，所有字段都是加密的\n */\n@Entity\nclass CloudServiceInfo(\n  @PrimaryKey(autoGenerate = true) val uid: Int = 0,\n  @ColumnInfo var userName: String? = null,\n  @ColumnInfo var password: String? = null,\n  // 云端路径\n  @ColumnInfo val cloudPath: String\n)"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/entity/CommonState.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.entity\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 3:45 PM 2024/2/1\n **/\nenum class CommonState {\n  CREATE, DELETE, MODIFY\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/entity/DbHistoryRecord.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.entity\n\nimport android.net.Uri\nimport android.os.Parcel\nimport android.os.Parcelable\nimport android.os.Parcelable.Creator\nimport androidx.room.ColumnInfo\nimport androidx.room.Entity\nimport androidx.room.PrimaryKey\nimport com.lyy.keepassa.util.KeepassAUtil\nimport com.lyy.keepassa.view.StorageType\n\n/**\n * 历史记录实体\n */\n@Entity\ndata class DbHistoryRecord(\n\n  @PrimaryKey(autoGenerate = true) var uid: Int = 0,\n\n  @ColumnInfo var time: Long,\n    // 打开类型\n  /**\n   * [StorageType]\n   */\n  @ColumnInfo(defaultValue = \"AFS\") var type: String,\n    // 本地数据库uri\n  @ColumnInfo var localDbUri: String,\n\n    // 云端路径\n  @ColumnInfo var cloudDiskPath: String? = null,\n\n    //密钥的路径\n  @ColumnInfo var keyUri: String,\n    // 数据库名\n  var dbName: String\n\n//  val uri:ByteArray\n) : Parcelable {\n  constructor(parcel: Parcel) : this(\n      parcel.readInt(),\n      parcel.readLong(),\n      parcel.readString()!!,\n      parcel.readString()!!,\n      parcel.readString(),\n      parcel.readString()!!,\n      parcel.readString()!!\n  ) {\n  }\n\n  fun getDbPathType(): StorageType {\n    return StorageType.valueOf(type)\n  }\n\n  fun getDbUri(): Uri {\n    return KeepassAUtil.instance.convertUri(localDbUri)!!\n  }\n\n  /**\n   * 不能使用 getkeyUri()，否则kotlin 编译会报错\n   */\n  fun getDbKeyUri(): Uri? {\n    return KeepassAUtil.instance.convertUri(keyUri)\n  }\n\n  override fun writeToParcel(\n    parcel: Parcel,\n    flags: Int\n  ) {\n    parcel.writeInt(uid)\n    parcel.writeLong(time)\n    parcel.writeString(type)\n    parcel.writeString(localDbUri)\n    parcel.writeString(cloudDiskPath)\n    parcel.writeString(keyUri)\n    parcel.writeString(dbName)\n  }\n\n  override fun describeContents(): Int {\n    return 0\n  }\n\n  companion object CREATOR : Creator<DbHistoryRecord> {\n    override fun createFromParcel(parcel: Parcel): DbHistoryRecord {\n      return DbHistoryRecord(parcel)\n    }\n\n    override fun newArray(size: Int): Array<DbHistoryRecord?> {\n      return arrayOfNulls(size)\n    }\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/entity/EntryRecord.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.entity\n\nimport androidx.room.ColumnInfo\nimport androidx.room.Entity\nimport androidx.room.PrimaryKey\n\n@Entity\nclass EntryRecord(\n  @PrimaryKey(autoGenerate = true) val uid: Int = 0,\n    // 数据库的本地文件uri\n  var dbFileUri: String,\n  @ColumnInfo var userName: String,\n  @ColumnInfo var title: String,\n  @ColumnInfo val uuid: ByteArray,\n  @ColumnInfo var time: Long\n)"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/entity/IOtpBean.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.entity\n\nimport android.os.Parcelable\nimport androidx.annotation.VisibleForTesting\nimport com.keepassdroid.database.security.ProtectedString\nimport com.lyy.keepassa.util.totp.ComposeKeeTrayTotp\nimport com.lyy.keepassa.util.totp.ComposeKeepass\nimport com.lyy.keepassa.util.totp.ComposeKeepassxc\nimport com.lyy.keepassa.util.totp.SecretHexType\nimport com.lyy.keepassa.util.totp.TokenCalculator\nimport com.lyy.keepassa.util.totp.TokenCalculator.HashAlgorithm\nimport kotlinx.parcelize.Parcelize\n\ninterface IOtpBean\n\n@Parcelize\ndata class OtpBeans(\n  val trayTotp: TrayTotpBean? = null,\n  val keeOtp2: KeeOtp2Bean? = null,\n  val keepassxc: KeepassXcBean? = null,\n  val keeOtp: KeepOtpBean? = null,\n  val googleOtpBean: GoogleOtpBean? = null\n) : Parcelable\n\n/**\n * TrayTotp 的实体\n */\n@Parcelize\ndata class TrayTotpBean(\n  var secret: String,\n  var period: Int,\n  val isSteam: Boolean\n) : Parcelable, IOtpBean\n\n/**\n * KeepOtp实体\n */\n@Parcelize\ndata class KeepOtpBean(\n  val key: ProtectedString\n) : Parcelable, IOtpBean\n\n/**\n * Keepass 实体\n * [ComposeKeepass#toKeepassOtpMap]\n */\n@Parcelize\ndata class KeepassBean(\n  val otpBean: TimeOtp2Bean? = null,\n  val hmac: HmacOtpBean? = null\n) : Parcelable, IOtpBean {\n}\n\n/**\n *  KeeOtp2 的实体\n */\n@Parcelize\ndata class KeeOtp2Bean(\n  val otpBean: TimeOtp2Bean? = null,\n  val hmac: HmacOtpBean? = null\n) : Parcelable, IOtpBean\n\n/**\n * keepassxc的实体\n */\n@Parcelize\ndata class KeepassXcBean(\n  val host: String = \"totp\",\n  val title: String,\n  val userName: String,\n  val isSteam: Boolean,\n  var encoder: String = \"\",\n  var secret: String,\n  val issuer: String,\n  var period: Int,\n  var digits: Int,\n  var algorithm: HashAlgorithm,\n  val counter: String? = \"\"\n) : Parcelable, IOtpBean\n\n@Parcelize\ndata class GoogleOtpBean(\n  val secret: String\n) : Parcelable, IOtpBean\n\nfun GoogleOtpBean.toOtpStringMap(): Map<String, ProtectedString> {\n  return hashMapOf<String, ProtectedString>().apply {\n    put(ComposeKeepassxc.KEY_SEED, ProtectedString(true, secret))\n  }\n}\n\n/**\n * KeeOtp2 插件的otpHmac\n */\n@Parcelize\ndata class HmacOtpBean(\n  val secretType: SecretHexType,\n  /**\n   * HmacOtp-Secret-Hex\n   * HmacOtp-Secret-Base32\n   * HmacOtp-Secret-Base64\n   */\n  val secret: String,\n  /**\n   * HMAC-SHA-1\n   * HMAC-SHA-256\n   * HMAC-SHA-512\n   */\n  val algorithm: HashAlgorithm,\n  val counter: Int,\n  val len: Int = TokenCalculator.TOTP_DEFAULT_DIGITS\n) : Parcelable\n\n/**\n * KeeOtp2 插件的otpbean\n */\n@Parcelize\ndata class TimeOtp2Bean(\n  val secretType: SecretHexType,\n  /**\n   * TimeOtp-Secret-Hex\n   * TimeOtp-Secret-Base32\n   * TimeOtp-Secret-Base64\n   */\n  var secret: String,\n  /**\n   * [6-8]\n   */\n  var digits: Int,\n  /**\n   * HMAC-SHA-1\n   * HMAC-SHA-256\n   * HMAC-SHA-512\n   */\n  var algorithm: HashAlgorithm,\n  /**\n   * 更新时间，默认30s\n   */\n  var period: Int,\n\n  ) : Parcelable{\n\n  }\n\nfun KeepassBean.toOtpStringMap(): Map<String, ProtectedString> {\n  val map = linkedMapOf<String, ProtectedString>()\n  //totp\n  otpBean?.let {\n    map[ComposeKeepass.getSecretType(it.secretType)] = ProtectedString(true, it.secret)\n    map[ComposeKeepass.TimeOtp_Length] = ProtectedString(false, it.digits.toString())\n    map[ComposeKeepass.TimeOtp_Period] = ProtectedString(false, it.period.toString())\n    map[ComposeKeepass.TimeOtp_Algorithm] = ProtectedString(\n      false, when (it.algorithm) {\n        HashAlgorithm.SHA256 -> ComposeKeepass.HMAC_SHA_256\n        HashAlgorithm.SHA512 -> ComposeKeepass.HMAC_SHA_512\n        else -> ComposeKeepass.HMAC_SHA_1\n      }\n    )\n  }\n  // hotp\n  hmac?.let {\n    map[ComposeKeepass.getSecretType(it.secretType)] = ProtectedString(true, it.secret)\n    map[ComposeKeepass.HmacOtp_Counter] = ProtectedString(false, it.counter.toString())\n  }\n\n  return map\n}\n\nfun KeepassXcBean.toOtpStringMap(): Map<String, ProtectedString> {\n  return hashMapOf<String, ProtectedString>().apply {\n    val arithmetic = when (algorithm) {\n      HashAlgorithm.SHA256 -> \"SHA256\"\n      HashAlgorithm.SHA512 -> \"SHA512\"\n      else -> \"SHA1\"\n    }\n    var seedStr =\n      \"otpauth://totp/${title}:${userName}?secret=${secret}&period=${period}&digits=${digits}&issuer=${userName}&algorithm=$arithmetic\"\n\n    if (isSteam) {\n      seedStr += \"&encoder=steam\"\n    }\n    put(ComposeKeepassxc.KEY_SEED, ProtectedString(true, seedStr))\n  }\n}\n\n\nfun TrayTotpBean.toOtpStringMap(): Map<String, ProtectedString> {\n  return hashMapOf<String, ProtectedString>().apply {\n    put(ComposeKeeTrayTotp.KEY_SEED, ProtectedString(true, secret))\n    put(\n      ComposeKeeTrayTotp.KEY_SETTING,\n      ProtectedString(false, \"${period};${if (isSteam) \"S\" else \"6\"}\")\n    )\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/entity/QuickUnLockRecord.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.entity\n\nimport androidx.room.ColumnInfo\nimport androidx.room.Entity\nimport androidx.room.PrimaryKey\n\n/**\n * 快速解锁信息实体\n */\n@Entity\ndata class QuickUnLockRecord(\n  @PrimaryKey(autoGenerate = true)\n  val uid: Int = 0,\n  val dbUri: String,\n  var dbPass: String = \"\",\n  var keyPath: String?,\n  var isUseKey: Boolean = true,\n  var isUseFingerprint: Boolean = false, // 使用指纹解锁\n  @ColumnInfo(name = \"passIv\", typeAffinity = ColumnInfo.BLOB)\n  var passIv: ByteArray? = null\n) {\n  override fun equals(other: Any?): Boolean {\n    if (this === other) return true\n    if (javaClass != other?.javaClass) return false\n\n    other as QuickUnLockRecord\n\n    if (uid != other.uid) return false\n    if (dbUri != other.dbUri) return false\n    if (dbPass != other.dbPass) return false\n    if (keyPath != other.keyPath) return false\n    if (isUseKey != other.isUseKey) return false\n    if (isUseFingerprint != other.isUseFingerprint) return false\n    if (passIv != null) {\n      if (other.passIv == null) return false\n      if (!passIv.contentEquals(other.passIv)) return false\n    } else if (other.passIv != null) return false\n\n    return true\n  }\n\n  override fun hashCode(): Int {\n    var result = uid\n    result = 31 * result + dbUri.hashCode()\n    result = 31 * result + dbPass.hashCode()\n    result = 31 * result + (keyPath?.hashCode() ?: 0)\n    result = 31 * result + isUseKey.hashCode()\n    result = 31 * result + isUseFingerprint.hashCode()\n    result = 31 * result + (passIv?.contentHashCode() ?: 0)\n    return result\n  }\n\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/entity/SearchRecord.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.entity\n\nimport androidx.room.ColumnInfo\nimport androidx.room.Entity\nimport androidx.room.PrimaryKey\n\n@Entity\nclass SearchRecord {\n  @PrimaryKey(autoGenerate = true) var uid: Int = 0\n\n  @ColumnInfo var title: String = \"\"\n  @ColumnInfo var time: Long = 0\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/entity/SimpleItemEntity.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.entity\n\nimport android.view.View\nimport androidx.fragment.app.FragmentActivity\nimport com.keepassdroid.database.PwEntry\nimport com.keepassdroid.database.PwGroup\nimport com.keepassdroid.database.PwGroupV4\nimport com.lyy.keepassa.view.menu.EntryPopMenu\nimport com.lyy.keepassa.view.menu.GroupPopMenu\n\nclass SimpleItemEntity {\n  var title: CharSequence = \"\"\n  var subTitle: CharSequence = \"\"\n  var content: CharSequence = \"\"\n  var icon: Int = 0\n  var id: Int = -1\n  var time: Long = 0\n  lateinit var obj: Any\n  var isSelected: Boolean = false\n\n  var type: Int = 0\n\n  /**\n   * 是否受保护\n   */\n  var isProtected = false\n\n  /**\n   * 是否选中\n   */\n  var isCheck = false\n}\n\nenum class EntryType{\n   TYPE_COLLECTION\n}\n\n/**\n * show pop menu\n */\nfun SimpleItemEntity.showPopMenu(\n  ac: FragmentActivity,\n  v: View,\n  curx: Int,\n  isInRecycleBin: Boolean = false\n) {\n  if (obj is PwGroup) {\n    val pop = GroupPopMenu(\n      ac,\n      v,\n      obj as PwGroupV4,\n      curx,\n      isInRecycleBin\n    )\n    pop.show()\n    return\n  }\n  if (obj is PwEntry) {\n    val pop = EntryPopMenu(\n      ac,\n      v,\n      obj as PwEntry,\n      curx,\n      isInRecycleBin\n    )\n    pop.show()\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/entity/TagBean.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.entity\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 3:45 PM 2023/10/26\n **/\ndata class TagBean(\n  val tag: String,\n  var isSet: Boolean = false\n) : java.io.Serializable"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/entity/TotpType.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.entity\n\nenum class TotpType(val value: String) {\n\n  DEFAULT(\"default\"),\n  STEAM(\"steam\"),\n  CUSTOM(\"custom\");\n\n  companion object {\n    fun from(s: String): TotpType {\n      val tt = values().find { it.value == s }\n      return tt ?: DEFAULT\n    }\n  }\n}\n"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/event/AttrFileEvent.kt",
    "content": "package com.lyy.keepassa.event\n\nimport com.keepassdroid.database.security.ProtectedBinary\nimport com.lyy.keepassa.entity.CommonState\n\ndata class AttrFileEvent(\n  val state: CommonState,\n  val key: String,\n  val file: ProtectedBinary,\n)\n"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/event/AttrStrEvent.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.event\n\nimport com.keepassdroid.database.security.ProtectedString\nimport com.lyy.keepassa.entity.CommonState\n\n/**\n * 创建自定义字段事件\n */\ndata class AttrStrEvent(\n  val state: CommonState,\n  val key: String,\n  val str: ProtectedString,\n  val position: Int = 0\n)"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/event/ChangeDbEvent.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.event\n\nimport android.net.Uri\nimport com.lyy.keepassa.view.StorageType\n\n/**\n * 切换数据库的事件\n */\ndata class ChangeDbEvent(\n  /**\n   * 数据库名\n   */\n  var dbName: String,\n  /**\n   * 本地文件路径\n   */\n  var localFileUri: Uri,\n  /**\n   * 云端文件路径\n   */\n  var cloudPath: String? = null,\n  var uriType: StorageType = StorageType.AFS, // uri类型，afs，google drive,\n  var keyUri: Uri? = null\n)"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/event/CheckEnvEvent.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.event\n\n/**\n * @Author laoyuyu\n * @Description check operating env event\n * @Date 2020/11/25\n **/\nclass CheckEnvEvent {\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/event/CloudFileSelectedEvent.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.event\n\nimport com.lyy.keepassa.view.StorageType\n\ndata class CloudFileSelectedEvent(\n  val isSelectFile: Boolean,\n  val fileFullPath: String,\n  val storageType: StorageType\n)\n"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/event/CollectionEvent.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.event\n\nimport com.keepassdroid.database.PwEntryV4\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 19:58 下午 2022/3/29\n **/\ndata class CollectionEvent(\n  val state: CollectionEventType = CollectionEventType.COLLECTION_STATE_TOTAL,\n  val collectionNum: Int = 0,\n  val pwEntryV4: PwEntryV4? = null\n)\n\nenum class CollectionEventType {\n  COLLECTION_STATE_ADD,\n  COLLECTION_STATE_REMOVE,\n  COLLECTION_STATE_TOTAL\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/event/DbHistoryEvent.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.event\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 2020/12/7\n **/\ndata class DbHistoryEvent(val isEmpty: Boolean)"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/event/DbPathEvent.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.event\n\nimport android.net.Uri\nimport com.lyy.keepassa.view.StorageType\n\n/**\n * 选择数据库事件\n */\ndata class DbPathEvent(\n  /**\n   * 数据库名\n   */\n  var dbName: String,\n  /**\n   * 本地数据库uri\n   */\n  var fileUri: Uri? = null,\n  var storageType: StorageType = StorageType.AFS, // uri类型，afs，google drive\n  /**\n   * 云端路径\n   */\n  var cloudDiskPath: String? = null\n)"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/event/DelAttrFileEvent.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.event\n\n/**\n * 删除附件事件\n */\ndata class DelAttrFileEvent(\n  val key: String\n)"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/event/DelAttrStrEvent.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.event\n\nimport com.keepassdroid.database.security.ProtectedString\n\n/**\n * 删除自定义字段事件\n */\ndata class DelAttrStrEvent(\n  val key: String,\n  val str: ProtectedString\n)"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/event/EditorEvent.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.event\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 2020/12/2\n **/\ndata class EditorEvent(\n  val requestCode: Int,\n  val content: CharSequence?\n)"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/event/FillInfoEvent.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.event\n\n/**\n * 填充时间信息\n * @Author laoyuyu\n * @Description\n * @Date 2020/10/27\n **/\ndata class FillInfoEvent(val infoStr: CharSequence)"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/event/KeyPathEvent.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.event\n\nimport android.net.Uri\n\n/**\n * 获取key的事件\n */\ndata class KeyPathEvent(\n  val keyUri: Uri,\n  val keyName: String\n)"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/event/ModifyDbNameEvent.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.event\n\ndata class ModifyDbNameEvent(val dbName: String)"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/event/ModifyPassEvent.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.event\n\ndata class ModifyPassEvent(val pass: String)"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/event/MoveEvent.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.event\n\nimport com.keepassdroid.database.PwEntryV4\nimport com.keepassdroid.database.PwGroupV4\n\n/**\n * 恢复数据的事件\n */\ndata class MoveEvent(\n  val type: Int = MOVE_TYPE_GROUP, // 1：群组，2：条目\n  val entryV4: PwEntryV4? = null,\n  val pwGroupV4: PwGroupV4? = null\n){\n  companion object{\n    const val MOVE_TYPE_GROUP = 1 // 群组\n    const val MOVE_TYPE_ENTRY = 2 // 条目\n  }\n}\n\n"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/event/MsgDialogEvent.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.event\n\n/**\n * msg dialog 事件\n */\ndata class MsgDialogEvent(\n  /**\n   * @param type 1、确认，2、覆盖、3、取消\n   *\n   */\n  val type: Int = 1,\n  val requestCode: Int = 0   // 请求码\n)"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/event/MultiChoiceEvent.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.event\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 2020/12/25\n **/\nclass MultiChoiceEvent {\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/event/ShowTOTPEvent.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\npackage com.lyy.keepassa.event\n\ndata class ShowTOTPEvent(val show: Boolean = false)\n"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/event/StateChangeEvent.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.event\n\nimport com.keepassdroid.database.PwEntryV4\nimport com.keepassdroid.database.PwGroupV4\nimport com.lyy.keepassa.event.EntryState.UNKNOWN\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 2022/3/30\n **/\ndata class EntryStateChangeEvent(\n  val state: EntryState = UNKNOWN,\n  val pwEntryV4: PwEntryV4? = null,\n  val oldParent: PwGroupV4? = null\n)\n\ndata class GroupStateChangeEvent(\n  val state: EntryState = UNKNOWN,\n  val groupV4: PwGroupV4? = null,\n  val oldParent: PwGroupV4? = null\n)\n\nenum class EntryState {\n  /**\n   * new entry\n   */\n  CREATE,\n  DELETE,\n  MODIFY,\n\n  /**\n   * resume entry from recycle bin or move\n   */\n  MOVE,\n  SAVE,\n  UNKNOWN\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/event/TimeEvent.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.event\n\n/**\n * 时间事件\n */\ndata class TimeEvent(\n  val year: Int,\n  val month: Int,\n  val dayOfMonth: Int,\n  val hour: Int,\n  val minute: Int\n)"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/event/WebDavLoginEvent.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.event\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 2:52 下午 2022/4/21\n **/\ndata class WebDavLoginEvent(\n  val uri: String,\n  val userName: String,\n  val pass: String,\n  val loginSuccess: Boolean = false\n)"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/ondrive/DriveItem.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.ondrive\n\n/**\n * @author laoyuyu\n * @date 2021/2/6\n */\ndata class DriveItem(\n  val id: String,\n  val driveType: String,\n  val name: String\n)"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/ondrive/MsalApi.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.ondrive\n\nimport com.lyy.keepassa.util.cloud.OneDriveUtil.APP_ROOT_DIR\nimport com.lyy.keepassa.util.cloud.OneDriveUtil.TOKEN_KEY\nimport retrofit2.Response\nimport retrofit2.http.DELETE\nimport retrofit2.http.GET\nimport retrofit2.http.Header\nimport retrofit2.http.POST\nimport retrofit2.http.Path\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 2021/2/6\n **/\ninterface MsalApi {\n\n  /**\n   * @param itemPath 云端的路径，如：/foo.txtNet\n   */\n  @POST(\"users/{user-id}/drive/special/$APP_ROOT_DIR:/{item-path}:/createUploadSession\")\n  suspend fun createUploadSession(\n    @Header(TOKEN_KEY) authorization: String,\n    @Path(\"user-id\") userId: String,\n    @Path(\"item-path\") itemPath: String\n  ): MsalUploadSession\n\n  /**\n   * 获取驱动器列表，也就是onedrive 空间信息\n   */\n  @GET(\"users/{userId}/drives\")\n  suspend fun getDriveList(\n    @Header(TOKEN_KEY) authorization: String,\n    @Path(\"userId\") userId: String\n  ): MsalResponse<List<DriveItem>>\n\n  /**\n   * 获取应用的app子文件夹列表\n   */\n  @GET(\"users/{user-id}/drive/items/{item-id}/children\")\n  suspend fun getFolderListById(\n    @Header(TOKEN_KEY) authorization: String,\n    @Path(\"user-id\") userId: String,\n    @Path(\"item-id\") itemId: String\n  ): MsalResponse<List<MsalSourceItem>>\n\n  /**\n   * 获取应用的app文件夹列表\n   */\n  @GET(\"users/{userId}/drive/special/$APP_ROOT_DIR/children\")\n  suspend fun getAppFolderList(\n    @Header(TOKEN_KEY) authorization: String,\n    @Path(\"userId\") userId: String\n  ): MsalResponse<List<MsalSourceItem>>\n\n  /**\n   * 获取单个文件信息\n   * @param itemId 文件id\n   */\n  @GET(\"users/{user-id}/drive/items/{item-id}\")\n  suspend fun getFileInfoById(\n    @Header(TOKEN_KEY) authorization: String,\n    @Path(\"user-id\") userId: String,\n    @Path(\"item-id\") itemId: String\n  ): MsalSourceItem?\n\n  /**\n   * 获取单个文件信息\n   * @param itemPath 文件在云盘的相对路径，如：/xxx.zip\n   */\n  @GET(\"users/{user-id}/drive/special/$APP_ROOT_DIR:/{item-path}\")\n  suspend fun getFileInfoByPath(\n    @Header(TOKEN_KEY) authorization: String,\n    @Path(\"user-id\") userId: String,\n    @Path(\"item-path\") itemPath: String\n  ): MsalSourceItem?\n\n  /**\n   * 删除文件，如果成功，此调用将返回 204 No Content 响应，以指明资源已被删除，没有可返回的内容。\n   */\n  @DELETE(\"users/{userId}/drive/items/{itemId}\")\n  suspend fun deleteFile(\n    @Header(TOKEN_KEY) authorization: String,\n    @Path(\"userId\") userId: String,\n    @Path(\"itemId\") itemId: String\n  ): Response<Void>\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/ondrive/MsalResponse.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.ondrive\n\nimport androidx.annotation.Keep\nimport com.blankj.utilcode.util.ToastUtils\nimport com.google.gson.Gson\nimport com.google.gson.annotations.SerializedName\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.util.cloud.OneDriveUtil\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 2021/2/6\n **/\n@Keep\nclass MsalResponse<T> {\n  @SerializedName(\"value\")\n  val value: T? = null\n    get() {\n      // https://docs.microsoft.com/zh-cn/graph/errors\n      if (error?.code == \"unauthenticated\") {\n        OneDriveUtil.initOneDrive {\n          if (it) {\n            OneDriveUtil.loadAccount()\n            return@initOneDrive\n          }\n          ToastUtils.showLong(R.string.one_drive_init_failure)\n        }\n      }\n      return field\n    }\n\n  @SerializedName(\"error\")\n  val error: MsalErrorInfo? = null\n}\n\n@Keep\ndata class MsalErrorInfo(\n  val code: String, // https://docs.microsoft.com/zh-cn/graph/errors#code-property\n  val message: String\n) {\n  override fun toString(): String {\n    return Gson().toJson(this)\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/ondrive/MsalSourceItem.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.ondrive\n\nimport androidx.annotation.Keep\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 2021/2/7\n * https://docs.microsoft.com/zh-cn/graph/api/resources/driveitem?view=graph-rest-1.0\n **/\n@Keep\ndata class MsalSourceItem(\n  val id: String,\n  val createdDateTime: String,\n  val lastModifiedDateTime: String,\n  val cTag: String,\n  val eTag: String,\n  val webUrl: String?, // 下载地址\n  val name: String,\n  val size: Long,\n  val file: MsalFileInfo?,\n  val folder: MsalFolderInfo?\n) {\n  fun isFolder() = folder != null\n}\n\n@Keep\ndata class MsalFolderInfo(\n  val childCount: Long\n)\n\n@Keep\ndata class MsalFileInfo(\n  val mimeType: String,\n  val hashes: MsalFileHashes\n)\n\n@Keep\ndata class MsalFileHashes(\n  val quickXorHash: String,\n  val sha1Hash: String,\n  val sha256Hash: String\n)"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/ondrive/MsalUploadSession.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.ondrive\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 2021/4/27\n * https://docs.microsoft.com/zh-cn/graph/api/resources/uploadsession?view=graph-rest-1.0\n **/\ndata class MsalUploadSession(\n  val uploadUrl: String,   // 上传路径\n  val expirationDateTime: String, // 以 UTC 表示的上载会话过期的日期和时间。在此过期时间之前必须上载完整的文件文件。\n  val nextExpectedRanges: List<String> // range 0-\n)"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/receiver/ScreenLockReceiver.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.receiver\n\nimport android.content.BroadcastReceiver\nimport android.content.Context\nimport android.content.Intent\nimport androidx.preference.PreferenceManager\nimport com.arialyy.frame.util.ResUtil\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseApp\nimport com.lyy.keepassa.util.KdbUtil.isNull\nimport com.lyy.keepassa.util.KeepassAUtil\n\n/**\n * @Author laoyuyu\n * @Description screen receiver, when the user lock screen, lock the db\n * @Date 2021/2/1\n **/\nclass ScreenLockReceiver : BroadcastReceiver() {\n  override fun onReceive(\n    context: Context?,\n    intent: Intent?\n  ) {\n    // if the user lock screen, lock the db\n    if (intent?.action.equals(Intent.ACTION_SCREEN_OFF) && PreferenceManager.getDefaultSharedPreferences(BaseApp.APP)\n            .getBoolean(context?.getString(R.string.set_key_lock_screen_auto_lock_db), false)) {\n      if (BaseApp.isLocked || BaseApp.KDB.isNull()){\n        return\n      }\n      KeepassAUtil.instance.lock()\n      return\n    }\n  }\n\n}\n"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/router/ActivityRouter.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.router\n\nimport androidx.core.app.ActivityOptionsCompat\nimport com.arialyy.frame.router.RouterArgName\nimport com.arialyy.frame.router.RouterPath\nimport com.blankj.utilcode.util.ActivityUtils\nimport com.keepassdroid.database.PwGroupId\nimport com.lyy.keepassa.entity.AutoFillParam\nimport com.lyy.keepassa.view.create.entry.CreateEntryActivity\nimport com.lyy.keepassa.view.create.entry.CreateEnum\nimport com.lyy.keepassa.view.detail.EntryDetailActivityNew\nimport com.lyy.keepassa.view.detail.GroupDetailActivity\nimport com.lyy.keepassa.view.launcher.LauncherActivity\nimport com.lyy.keepassa.view.setting.SettingActivity\nimport java.util.UUID\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 2021/10/17\n **/\ninterface ActivityRouter {\n\n  @RouterPath(path = \"/search/common\")\n  fun toCommonSearch(\n    @RouterArgName(name = \"apkPkgName\") apkPkgName: String? = null,\n    @RouterArgName(name = \"onlySearch\") onlySearch: Boolean = true\n  )\n\n  @RouterPath(path = \"/collection/ac\")\n  fun toMyCollection(@RouterArgName(name = \"opt\") opt: ActivityOptionsCompat? = null)\n\n  @RouterPath(path = \"/setting/app\")\n  fun toAppSetting(\n    @RouterArgName(name = SettingActivity.KEY_TYPE) type: Int = SettingActivity.TYPE_APP,\n    @RouterArgName(name = \"opt\") opt: ActivityOptionsCompat? = null,\n    @RouterArgName(name = \"scrollKey\") scrollKey: String? = null\n  )\n\n  @RouterPath(path = \"/setting/app\")\n  fun toDbSetting(\n    @RouterArgName(name = SettingActivity.KEY_TYPE) type: Int = SettingActivity.TYPE_DB,\n    @RouterArgName(name = \"opt\") opt: ActivityOptionsCompat? = null\n  )\n\n  @RouterPath(path = \"/launcher/quickLock\")\n  fun toQuickUnlockActivity(\n    @RouterArgName(name = \"flag\", isFlag = true) flags: Int\n  )\n\n  @RouterPath(path = \"/entry/detail\")\n  fun toEntryDetailActivity(\n    @RouterArgName(name = EntryDetailActivityNew.KEY_ENTRY_ID) entryId: UUID,\n    @RouterArgName(name = \"opt\") opt: ActivityOptionsCompat? = null\n  )\n\n  /**\n   * to group detail\n   */\n  @RouterPath(path = \"/group/detail\")\n  fun toGroupDetailActivity(\n    @RouterArgName(name = GroupDetailActivity.KEY_TITLE) groupName: String,\n    @RouterArgName(name = GroupDetailActivity.KEY_GROUP_ID) groupId: PwGroupId,\n    @RouterArgName(name = GroupDetailActivity.KEY_IS_IN_RECYCLE_BIN) isRecycleBin: Boolean = false,\n    @RouterArgName(name = \"opt\") opt: ActivityOptionsCompat? = null\n  )\n\n  /**\n   * create entry\n   */\n  @RouterPath(path = \"/entry/create\")\n  fun toCreateEntryActivity(\n    @RouterArgName(name = CreateEntryActivity.PARENT_GROUP_ID) groupId: PwGroupId?,\n    @RouterArgName(name = \"opt\") opt: ActivityOptionsCompat? = null,\n    @RouterArgName(name = CreateEntryActivity.IS_SHORTCUTS) isFromShortcuts: Boolean = false,\n    @RouterArgName(name = CreateEntryActivity.KEY_TYPE) type: CreateEnum = CreateEnum.CREATE\n  )\n\n  /**\n   * edit entry\n   */\n  @RouterPath(path = \"/entry/create\")\n  fun toEditEntryActivity(\n    @RouterArgName(name = CreateEntryActivity.KEY_ENTRY) uuid: UUID,\n    @RouterArgName(name = \"opt\") opt: ActivityOptionsCompat? = ActivityOptionsCompat.makeSceneTransitionAnimation(ActivityUtils.getTopActivity()),\n    @RouterArgName(name = CreateEntryActivity.KEY_TYPE) type: CreateEnum = CreateEnum.MODIFY\n  )\n\n  @RouterPath(path = \"/entry/create\")\n  fun toEditEntryActivity(\n    @RouterArgName(name = LauncherActivity.KEY_AUTO_FILL_PARAM) params: AutoFillParam\n  )\n\n  @RouterPath(path = \"/main/ac\")\n  fun toMainActivity(\n    @RouterArgName(name = \"isShortcuts\") isShortcuts: Boolean = false,\n    @RouterArgName(name = \"shortcutType\") shortcutType: Int = 1,\n    @RouterArgName(name = \"opt\") opt: ActivityOptionsCompat? = null\n  )\n\n  @RouterPath(path = \"/launcher/createDb\")\n  fun toCreateDbActivity(\n    @RouterArgName(name = \"opt\") opt: ActivityOptionsCompat? = null\n  )\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/router/ContentInterceptor.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.router\n\nimport android.content.Context\nimport android.content.Intent.FLAG_ACTIVITY_NEW_TASK\nimport com.alibaba.android.arouter.facade.Postcard\nimport com.alibaba.android.arouter.facade.annotation.Interceptor\nimport com.alibaba.android.arouter.facade.callback.InterceptorCallback\nimport com.alibaba.android.arouter.facade.template.IInterceptor\nimport com.alibaba.android.arouter.launcher.ARouter\nimport com.arialyy.frame.router.Routerfit\nimport com.lyy.keepassa.base.BaseApp\nimport com.lyy.keepassa.util.KdbUtil.isNull\nimport com.lyy.keepassa.util.KpaUtil\nimport com.lyy.keepassa.util.isCanOpenQuickLock\nimport timber.log.Timber\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 4:14 下午 2021/7/7\n **/\n@Interceptor(priority = 8, name = \"ContentInterceptor\")\nclass ContentInterceptor : IInterceptor {\n\n  companion object {\n    val ROUTE_WHITE_LIST = arrayListOf<String>().apply {\n      add(\"/launcher/activity\")\n      add(\"/launcher/quickLock\")\n      add(\"/launcher/createDb\")\n    }\n  }\n\n  override fun init(context: Context) {\n    // 拦截器的初始化，会在sdk初始化的时候调用该方法，仅会调用一次\n  }\n\n  override fun process(\n    postcard: Postcard,\n    callback: InterceptorCallback\n  ) {\n    Timber.d(\"route path => ${postcard.path}\")\n    if (postcard.path in ROUTE_WHITE_LIST) {\n      callback.onContinue(postcard)\n      return\n    }\n    if (BaseApp.KDB.isNull()) {\n      callback.onInterrupt(Exception(\"kdb is null\"))\n      ARouter.getInstance()\n        .build(\"/launcher/activity\")\n        .navigation()\n      return\n    }\n    if (BaseApp.isLocked && BaseApp.APP.isCanOpenQuickLock()) {\n      callback.onInterrupt(Exception(\"database is locked\"))\n      Routerfit.create(ActivityRouter::class.java).toQuickUnlockActivity(FLAG_ACTIVITY_NEW_TASK)\n      return\n    }\n\n//    Timber.i(\"拦截：${postcard.path}\")\n\n    callback.onContinue(postcard)  // 处理完成，交还控制权\n    // callback.onInterrupt(new RuntimeException(\"我觉得有点异常\"));      // 觉得有问题，中断路由流程\n\n    // 以上两种至少需要调用其中一种，否则不会继续路由\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/router/DeeplinkActivity.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.router\n\nimport android.net.Uri\nimport android.os.Bundle\nimport androidx.appcompat.app.AppCompatActivity\nimport com.arialyy.frame.router.Routerfit\nimport timber.log.Timber\nimport java.net.URLDecoder\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 2021/7/11\n **/\nclass DeeplinkActivity : AppCompatActivity() {\n\n  override fun onCreate(savedInstanceState: Bundle?) {\n    super.onCreate(savedInstanceState)\n    Timber.d(\"uri = ${intent.data}\")\n    val shortcutData = intent.getStringExtra(\"shortcutData\")\n    if (!shortcutData.isNullOrEmpty()) {\n      Timber.d(\"shortcutData = $shortcutData\")\n      val uriString = URLDecoder.decode(shortcutData)\n      val uri = Uri.parse(uriString)\n      handleFormShortcutRoute(uri)\n      finish()\n      return\n    }\n  }\n\n  private fun handleFormShortcutRoute(uri: Uri) {\n    val ac = uri.getQueryParameter(\"ac\")\n    if (ac == \"createEntry\") {\n      Timber.d(\"to create entry\")\n      Routerfit.create(ActivityRouter::class.java).toCreateEntryActivity(\n        groupId = null,\n        isFromShortcuts = true\n      )\n      return\n    }\n    if (ac == \"search\") {\n      val type = uri.getQueryParameter(\"shortcutsType\")\n      Timber.d(\"to search ac\")\n      Routerfit.create(ActivityRouter::class.java).toMainActivity(true, type!!.toInt())\n    }\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/router/DialogRouter.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.router\n\nimport android.graphics.drawable.Drawable\nimport com.arialyy.frame.router.DialogArg\nimport com.arialyy.frame.router.RouterArgName\nimport com.arialyy.frame.router.RouterPath\nimport com.keepassdroid.database.PwEntryV4\nimport com.keepassdroid.database.PwGroupV4\nimport com.keepassdroid.database.security.ProtectedString\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.entity.TagBean\nimport com.lyy.keepassa.view.StorageType\nimport com.lyy.keepassa.view.dialog.CloudFileSelectDialog\nimport com.lyy.keepassa.view.dialog.LoadingDialog\nimport com.lyy.keepassa.view.dialog.OnMsgBtClickListener\nimport com.lyy.keepassa.view.dialog.TimeChangeDialog\nimport com.lyy.keepassa.view.dialog.webdav.WebDavLoginDialogNew\nimport java.util.UUID\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 2021/9/5\n **/\ninterface DialogRouter {\n\n  @RouterPath(path = \"/dialog/chooseTag\")\n  @DialogArg(showDialog = true)\n  fun showChooseTagDialog(\n    @RouterArgName(name = \"entry\") entry: PwEntryV4,\n    @RouterArgName(name = \"newTag\") newTag: TagBean? = null\n  )\n\n  @RouterPath(path = \"/dialog/createTag\")\n  @DialogArg(showDialog = true)\n  fun showCreateTagDialog()\n\n  @RouterPath(path = \"/dialog/createOtp\")\n  @DialogArg(showDialog = true)\n  fun showCreateOtpDialog(\n    @RouterArgName(name = \"entryTitle\") entryTitle: String,\n    @RouterArgName(name = \"entryUserName\") entryUserName: String\n  )\n\n  @RouterPath(path = \"/dialog/otpModify\")\n  @DialogArg(showDialog = true)\n  fun showModifyOtpDialog(\n    @RouterArgName(name = \"uid\") uid: UUID\n  )\n\n  @RouterPath(path = \"/dialog/customStrDialog\")\n  @DialogArg(showDialog = true)\n  fun showCreateCustomDialog(\n    @RouterArgName(name = \"position\") position: Int = 0,\n    @RouterArgName(name = \"key\") key: String? = null,\n    @RouterArgName(name = \"value\") value: ProtectedString? = null\n  )\n\n  @RouterPath(path = \"/dialog/tipsDialog\")\n  @DialogArg(showDialog = true)\n  fun showTipDialog()\n\n  @RouterPath(path = \"/dialog/imgViewer\")\n  @DialogArg(showDialog = true)\n  fun showImgViewerDialog(\n    @RouterArgName(name = \"imgByteArray\") imgByteArray: ByteArray\n  )\n\n  @RouterPath(path = \"/dialog/cloudFileList\")\n  fun getCloudFileListDialog(\n    @RouterArgName(name = \"storageType\") storageType: StorageType,\n    @RouterArgName(name = \"onlyShowDir\") onlyShowDir: Boolean = false\n  ): CloudFileSelectDialog\n\n  @RouterPath(path = \"/dialog/cloudFileList\")\n  @DialogArg(showDialog = true)\n  fun showCloudFileListDialog(\n    @RouterArgName(name = \"storageType\") storageType: StorageType,\n    @RouterArgName(name = \"onlyShowDir\") onlyShowDir: Boolean = false\n  )\n\n  @RouterPath(path = \"/dialog/webdavLogin\")\n  fun getWebDavLoginDialog(): WebDavLoginDialogNew\n\n  @RouterPath(path = \"/dialog/webdavLogin\")\n  @DialogArg(showDialog = true)\n  fun showWebDavLoginDialog(): WebDavLoginDialogNew\n\n  @RouterPath(path = \"/dialog/modifyGroup\")\n  @DialogArg(showDialog = true)\n  fun showModifyGroupDialog(\n    @RouterArgName(name = \"pwGroup\") pwGroup: PwGroupV4\n  )\n\n  @RouterPath(path = \"/dialog/createGroup\")\n  @DialogArg(showDialog = true)\n  fun showCreateGroupDialog(\n    @RouterArgName(name = \"parentGroup\") parentGroup: PwGroupV4\n  )\n\n  @RouterPath(path = \"/dialog/loading\")\n  @DialogArg(showDialog = false)\n  fun getLoadingDialog(): LoadingDialog\n\n  @RouterPath(path = \"/dialog/loading\")\n  @DialogArg(showDialog = true)\n  fun showLoadingDialog()\n\n  /**\n   * show play donate dialog\n   */\n  @RouterPath(path = \"/dialog/playDonate\")\n  @DialogArg(showDialog = true)\n  fun showPlayDonateDialog()\n\n  /**\n   * show display dialog\n   * @param uuid don't use UUID, because is that Serializable\n   */\n  @RouterPath(path = \"/dialog/totpDisplay\")\n  @DialogArg(showDialog = true)\n  fun showTotpDisplayDialog(\n    @RouterArgName(name = \"uuid\") uuid: String\n  )\n\n  /**\n   * 显示消息对话框\n   * @param showCountDownTimer 是否显示倒计时  Pair(true, 5) => 显示倒计时，5s\n   */\n  @RouterPath(path = \"/dialog/msgDialog\")\n  @DialogArg(showDialog = true)\n  fun showMsgDialog(\n    @RouterArgName(name = \"msgTitle\", isObject = true) msgTitle: CharSequence = \"\",\n    @RouterArgName(name = \"msgContent\", isObject = true) msgContent: CharSequence,\n    @RouterArgName(name = \"showCancelBt\") showCancelBt: Boolean = true,\n    @RouterArgName(name = \"showEnterBt\") showEnterBt: Boolean = true,\n    @RouterArgName(name = \"showCoverBt\") showCoverBt: Boolean = false,\n    @RouterArgName(name = \"interceptBackKey\") interceptBackKey: Boolean = false,\n    @RouterArgName(name = \"enterText\", isObject = true) enterText: CharSequence = \"\",\n    @RouterArgName(name = \"cancelText\", isObject = true) cancelText: CharSequence = \"\",\n    @RouterArgName(name = \"coverText\", isObject = true) coverText: CharSequence = \"\",\n    @RouterArgName(name = \"enterBtTextColor\") enterBtTextColor: Int = R.color.text_blue_color,\n    @RouterArgName(name = \"cancelBtTextColor\") cancelBtTextColor: Int = R.color.text_gray_color,\n    @RouterArgName(name = \"coverBtTextColor\") coverBtTextColor: Int = R.color.text_blue_color,\n    @RouterArgName(\n      name = \"btnClickListener\",\n      isObject = true\n    ) btnClickListener: OnMsgBtClickListener? = null,\n    @RouterArgName(name = \"msgTitleEndIcon\", isObject = true) msgTitleEndIcon: Drawable? = null,\n    @RouterArgName(name = \"msgTitleStartIcon\", isObject = true) msgTitleStartIcon: Drawable? = null,\n    @RouterArgName(name = \"showCountDownTimer\") showCountDownTimer: Pair<Boolean, Int> = Pair(\n      false,\n      5\n    )\n  )\n\n  /**\n   * 日期选择对话框\n   */\n  @RouterPath(path = \"/dialog/timeChange\")\n  @DialogArg(showDialog = true)\n  fun showTimeChangeDialog(): TimeChangeDialog\n\n  /**\n   * 日期选择对话框\n   */\n  @RouterPath(path = \"/dialog/timeChange\")\n  fun getTimeChangeDialog(): TimeChangeDialog\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/router/FragmentRouter.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.router\n\nimport androidx.preference.PreferenceFragmentCompat\nimport com.arialyy.frame.router.RouterArgName\nimport com.arialyy.frame.router.RouterPath\nimport com.keepassdroid.database.PwGroup\nimport com.keepassdroid.database.PwGroupId\nimport com.lyy.keepassa.entity.DbHistoryRecord\nimport com.lyy.keepassa.view.dir.DirFragment\nimport com.lyy.keepassa.view.launcher.ChangeDbFragment\nimport com.lyy.keepassa.view.launcher.OpenDbFragment\nimport com.lyy.keepassa.view.main.EntryListFragment\nimport com.lyy.keepassa.view.main.HomeFragment\nimport com.lyy.keepassa.view.setting.AppSettingFragment\nimport com.lyy.keepassa.view.setting.DBSettingFragment\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 3:08 下午 2021/10/27\n **/\ninterface FragmentRouter {\n\n\n  @RouterPath(path = \"/setting/appFm\")\n  fun getAppSettingFragment(\n    @RouterArgName(name = \"scrollKey\") scrollKey: String? = null\n  ): PreferenceFragmentCompat\n\n  @RouterPath(path = \"/setting/DbFm\")\n  fun getDbSettingFragment(): PreferenceFragmentCompat\n\n  @RouterPath(path = \"/group/choose/dir\")\n  fun getDirFragment(\n    @RouterArgName(name = DirFragment.KEY_CUR_GROUP) group: PwGroup,\n    @RouterArgName(name = DirFragment.KEY_IS_MOVE_GROUP) isMoveGroup: Boolean,\n    @RouterArgName(name = DirFragment.KEY_IS_RECYCLE_GROUP_ID) recycleGroupId: PwGroupId?\n  ): DirFragment\n\n  @RouterPath(path = \"/main/fragment/home\")\n  fun toMainHomeFragment(): HomeFragment\n\n  @RouterPath(path = \"/main/fragment/entry\")\n  fun toMainHistoryFragment(@RouterArgName(name = \"type\") type: String = EntryListFragment.TYPE_HISTORY): EntryListFragment\n\n  @RouterPath(path = \"/main/fragment/entry\")\n  fun toMainTOTPFragment(@RouterArgName(name = \"type\") type: String = EntryListFragment.TYPE_TOTP): EntryListFragment\n\n  @RouterPath(path = \"/launcher/opendb\")\n  fun getOpenDbFragment(\n    @RouterArgName(name = \"openDbRecord\") openDbRecord: DbHistoryRecord\n  ): OpenDbFragment\n\n  @RouterPath(path = \"/launcher/changeDb\")\n  fun getChangeDbFragment(): ChangeDbFragment\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/router/ServiceRouter.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.router\n\nimport com.arialyy.frame.router.RouterPath\nimport com.lyy.keepassa.service.feat.KdbHandlerService\nimport com.lyy.keepassa.service.feat.KdbOpenService\nimport com.lyy.keepassa.service.feat.KpaSdkService\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 2:05 下午 2022/3/24\n **/\ninterface ServiceRouter {\n\n  @RouterPath(path = \"/service/kpaSdk\")\n  fun getKpaSdkService(): KpaSdkService\n\n  @RouterPath(path = \"/service/kdbHandler\")\n  fun getDbSaveService(): KdbHandlerService\n\n  @RouterPath(path = \"/service/kdbOpen\")\n  fun getDbOpenService(): KdbOpenService\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/service/DbOpenNotificationService.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.service\n\nimport android.app.Notification\nimport android.app.NotificationChannel\nimport android.app.NotificationManager\nimport android.app.PendingIntent\nimport android.content.Context\nimport android.content.Intent\nimport android.graphics.Color\nimport android.os.Build\nimport android.os.IBinder\nimport com.arialyy.frame.base.FrameApp.context\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseApp\nimport com.lyy.keepassa.base.BaseService\nimport com.lyy.keepassa.util.IconUtil\nimport com.lyy.keepassa.view.launcher.LauncherActivity\nimport com.lyy.keepassa.view.main.MainActivity\nimport com.lyy.keepassa.view.main.QuickUnlockActivity\n\n/**\n * 数据库打开通知\n */\nclass DbOpenNotificationService : BaseService() {\n\n  companion object {\n    const val KEY_NOTIFY_TYPE = \"KEY_NOTIFY_TYPE\"\n\n    // 数据库已解锁\n    const val NOTIFY_TYPE_OPEN_DB = 1\n\n    // 数据库已锁定，启动快速解锁\n    const val NOTIFY_TYPE_QUICK_UNLOCK_DB = 2\n\n    // 数据库已锁定\n    const val NOTIFY_TYPE_DB_LOCKED = 3\n  }\n\n  private val CHANNEL_ID_OPEN_DB = \"CHANNEL_OPEN_DB\"\n  private var CHANNEL_NAME_OPEN_DB: String = \"\"\n\n  // 数据库已解锁的的通知的id\n  private val DB_UNLOCK_ID = 10001\n\n  // 数据库启用快速解锁的通知的id\n  private val DB_START_QUICK_UNLOCK = 10002\n  private lateinit var notificationManager: NotificationManager\n\n  override fun onCreate() {\n    super.onCreate()\n    CHANNEL_NAME_OPEN_DB = getText(R.string.notify_channel_db_open) as String\n    notificationManager =\n      context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager\n    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {\n      // 设置通知归属的渠道\n      // 设置渠道，8.0上必须需要设置渠道，否则通知无法显示，参数分别为：渠道id，渠道名，优先级\n      // 优先级 https://developer.android.com/guide/topics/ui/notifiers/notifications?hl=zh-cn#importance\n      val channel = NotificationChannel(\n          CHANNEL_ID_OPEN_DB,\n          CHANNEL_NAME_OPEN_DB,\n          NotificationManager.IMPORTANCE_LOW\n      )\n      notificationManager.createNotificationChannel(channel)\n    }\n  }\n\n  override fun onBind(intent: Intent?): IBinder? {\n    return null\n  }\n\n  override fun onStartCommand(\n    intent: Intent?,\n    flags: Int,\n    startId: Int\n  ): Int {\n\n//    notificationManager.notify(CHANNEL_OPEN_DB, builder.build());\n    val notify: Notification\n    var notifyId = DB_UNLOCK_ID\n    when (intent?.getIntExtra(KEY_NOTIFY_TYPE, NOTIFY_TYPE_OPEN_DB) ?: DB_START_QUICK_UNLOCK) {\n      NOTIFY_TYPE_QUICK_UNLOCK_DB -> {\n        notifyId = DB_START_QUICK_UNLOCK\n        notify = createQuickUnlockNotify()\n      }\n      NOTIFY_TYPE_DB_LOCKED -> {\n        notify = createDbLockedNotify()\n      }\n      else -> {\n        notify = createDbUnlockNotify()\n      }\n    }\n\n    startForeground(notifyId, notify)\n    return super.onStartCommand(intent, flags, startId)\n  }\n\n  /**\n   * 创建数据库已解锁的通知\n   */\n  private fun createDbUnlockNotify(): Notification {\n    return createDbNotify(getText(R.string.unlocked), createMainPending())\n  }\n\n  /**\n   * 数据库启用快速解锁\n   */\n  private fun createQuickUnlockNotify(): Notification {\n    return createDbNotify(\n        getText(R.string.notify_quick_unlock_start),\n        QuickUnlockActivity.createQuickUnlockPending(this)\n    )\n  }\n\n  /**\n   * 数据库已锁定\n   */\n  private fun createDbLockedNotify(): Notification {\n    return createDbNotify(getText(R.string.notify_db_locked), LauncherActivity.createLauncherPending(this))\n  }\n\n  private fun createDbNotify(\n    contentMsg: CharSequence,\n    pendingIntent: PendingIntent\n  ): Notification {\n\n    val iconId = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {\n      R.drawable.ic_launcher_foreground\n    } else {\n      R.mipmap.ic_launcher\n    }\n    val builder: Notification.Builder = Notification.Builder(this)\n        .setContentTitle(getText(R.string.app_name))\n        .setContentText(\"${BaseApp.dbName}: $contentMsg\")\n        .setLargeIcon(IconUtil.getBitmapFromDrawable(this, iconId, -1))\n        .setSmallIcon(R.drawable.ic_security_24px) // 状态栏图标\n        .setContentIntent(pendingIntent)\n        .setColor(Color.TRANSPARENT) // 大图标右下角的小图标\n\n    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {\n      builder.setChannelId(CHANNEL_ID_OPEN_DB)\n    }\n    return builder.build()\n  }\n\n\n\n  /**\n   * 主页\n   */\n  private fun createMainPending(): PendingIntent {\n    return Intent(this, MainActivity::class.java).let { notificationIntent ->\n      PendingIntent.getActivity(this, 0, notificationIntent, PendingIntent.FLAG_IMMUTABLE)\n    }\n  }\n\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/service/autofill/AutoFillClickReceiver.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.service.autofill\n\nimport android.content.BroadcastReceiver\nimport android.content.Context\nimport android.content.Intent\nimport timber.log.Timber\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 2021/7/10\n **/\nclass AutoFillClickReceiver: BroadcastReceiver() {\n  companion object{\n    const val ACTION_CLICK_OTHER = \"ACTION_CLICK_OTHER\"\n  }\n\n  override fun onReceive(context: Context, intent: Intent) {\n    Timber.d(\"action = ${intent.action}\")\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/service/autofill/AutoFillHelper.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.service.autofill\n\nimport android.annotation.TargetApi\nimport android.app.assist.AssistStructure\nimport android.content.Context\nimport android.content.IntentSender\nimport android.graphics.Bitmap.Config\nimport android.graphics.BitmapFactory\nimport android.os.Build\nimport android.service.autofill.Dataset\nimport android.service.autofill.FillResponse\nimport android.service.autofill.SaveInfo\nimport android.view.View\nimport android.view.autofill.AutofillId\nimport android.view.autofill.AutofillValue\nimport android.widget.RemoteViews\nimport androidx.annotation.DrawableRes\nimport com.keepassdroid.database.PwEntry\nimport com.keepassdroid.database.PwEntryV4\nimport com.keepassdroid.database.PwIconCustom\nimport com.keepassdroid.database.PwIconStandard\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.service.autofill.model.AutoFillFieldMetadataCollection\nimport com.lyy.keepassa.util.IconUtil\nimport com.lyy.keepassa.util.KdbUtil\nimport com.lyy.keepassa.view.launcher.LauncherActivity\nimport com.lyy.keepassa.view.search.AutoFillEntrySearchActivity\nimport com.lyy.keepassa.widget.toPx\nimport timber.log.Timber\n\n/**\n * This is a class containing helper methods for building Autofill Datasets and Responses.\n */\n@TargetApi(Build.VERSION_CODES.O)\nobject AutoFillHelper {\n  val TAG = javaClass.simpleName\n\n  /**\n   * 数据库没打开时的view\n   * @param packageName 当前应用的包名（keepassA的包名)\n   */\n  private fun newRemoteViews(\n    context: Context,\n    packageName: String,\n    remoteViewsText: String,\n    @DrawableRes drawableId: Int\n  ): RemoteViews {\n    val rev = RemoteViews(packageName, R.layout.item_auto_fill)\n    rev.setTextViewText(R.id.text, remoteViewsText)\n    rev.setImageViewResource(R.id.img, drawableId)\n    rev.setViewVisibility(R.id.hint, View.GONE)\n    return rev\n  }\n\n  /**\n   * Not autofill dataset\n   */\n  private fun notAutoFill(\n    context: Context,\n    apkPageName: String\n  ): Dataset {\n    val rev = RemoteViews(context.packageName, R.layout.item_auto_fill)\n    rev.setTextViewText(R.id.text, context.resources.getString(R.string.cur_app_not_autofill))\n    IconUtil.getAppIcon(context, apkPageName)\n      ?.let {\n        rev.setImageViewBitmap(R.id.img, it)\n      }\n//    rev.setOnClickResponse()\n    rev.setViewVisibility(R.id.hint, View.GONE)\n    val db = Dataset.Builder(rev)\n\n    return db.build()\n  }\n\n  /**\n   * use other entry, click the entry, jump to the search activity\n   */\n  private fun otherEntry(\n    context: Context,\n    apkPageName: String,\n    tempFillId: AutofillId,\n    structure: AssistStructure\n  ): Dataset {\n    val rev = RemoteViews(context.packageName, R.layout.item_auto_fill)\n    rev.setTextViewText(R.id.text, context.resources.getString(R.string.other))\n    rev.setViewVisibility(R.id.hint, View.GONE)\n    IconUtil.getBitmapFromDrawable(context, R.drawable.ic_search, 20.toPx())?.let {\n      rev.setImageViewBitmap(R.id.img, it)\n    }\n\n    val sender = AutoFillEntrySearchActivity.createSearchPending(context, apkPageName, structure)\n    rev.setOnClickPendingIntent(\n      R.id.llContent,\n      sender\n    )\n\n//    newSaveResponse(context, metadata, sender)\n\n    val db = Dataset.Builder(rev)\n    db.setValue(tempFillId, AutofillValue.forText(\"\"))\n    return db.build()\n  }\n\n  /**\n   * 创建填充数据，填充用户名，密码\n   * @param dataSetAuth true 验证通过\n   * @param apkPageName 第三方apk包名\n   */\n  private fun newDataSet(\n    context: Context,\n    metadataList: AutoFillFieldMetadataCollection,\n    entry: PwEntry?,\n    dataSetAuth: Boolean,\n    apkPageName: String\n  ): Dataset? {\n    if (entry == null) {\n      return null\n    }\n\n    val dataSetBuilder: Dataset.Builder\n    if (!dataSetAuth) {\n      dataSetBuilder = Dataset.Builder(\n        buildRemoteView(\n          context,\n          entry.title,\n          if (entry is PwEntryV4) entry.customIcon else null,\n          entry.icon,\n          entry.username\n        )\n      )\n      // 设置点击事件，用于数据库没有打开的情况\n      val sender = LauncherActivity.getAuthDbIntentSender(context, apkPageName)\n      dataSetBuilder.setAuthentication(sender)\n    } else {\n      dataSetBuilder = Dataset.Builder(\n        buildRemoteView(\n          context,\n          entry.title,\n          if (entry is PwEntryV4) entry.customIcon else null,\n          entry.icon,\n          entry.username\n        )\n      )\n    }\n    // 填充数据\n    val setValueAtLeastOnce = applyDataInfoToFields(entry, metadataList, dataSetBuilder)\n    if (setValueAtLeastOnce) {\n      return dataSetBuilder.build()\n    }\n    return null\n  }\n\n  /**\n   * 构建用户名view\n   */\n  private fun buildRemoteView(\n    context: Context,\n    title: String,\n    customIcon: PwIconCustom?,\n    icon: PwIconStandard,\n    account: String\n  ): RemoteViews {\n    val rev = RemoteViews(context.packageName, R.layout.item_auto_fill)\n    rev.setTextViewText(R.id.text, title)\n    rev.setViewVisibility(R.id.hint, if (account.isBlank()) View.GONE else View.VISIBLE)\n    rev.setTextViewText(R.id.hint, account)\n    if (customIcon?.imageData != null && customIcon.imageData.isNotEmpty()) {\n      val byte = customIcon.imageData\n      val option = BitmapFactory.Options()\n      option.inPreferredConfig = Config.RGB_565\n      option.inDensity = 480\n      rev.setImageViewBitmap(R.id.img, BitmapFactory.decodeByteArray(byte, 0, byte.size, option))\n    } else {\n      rev.setImageViewResource(R.id.img, IconUtil.getIconById(icon.iconId))\n    }\n    return rev\n  }\n\n  /**\n   * 如果不返回SaveInfo，系统是不会触发保存的\n   */\n  fun newSaveResponse(\n    context: Context,\n    metadataList: AutoFillFieldMetadataCollection,\n    sender: IntentSender\n  ): FillResponse {\n    val responseBuilder = FillResponse.Builder()\n    val presentation = newRemoteViews(\n      context,\n      context.packageName,\n      context.getString(R.string.autofill_sign_in_prompt),\n      R.mipmap.ic_launcher\n    )\n    responseBuilder.setAuthentication(metadataList.autoFillIds.toTypedArray(), sender, presentation)\n    val dataSetBuild = Dataset.Builder()\n    val notUsed = RemoteViews(context.packageName, android.R.layout.simple_list_item_1)\n    val b = applySaveInfoToFields(metadataList, dataSetBuild, notUsed)\n    Timber.d(\"newSaveResponse applyToFields -> $b\")\n    if (b) {\n      responseBuilder.addDataset(dataSetBuild.build())\n    }\n\n    /*\n     * 触发系统保存弹窗的条件：\n     * 1、activity 必须要关闭\n     * 2、editText 中的值必须有更改。如果editText中已经设置了text，这时直接登陆的话，是不会触发弹框的\n     * 3、必须设置DataSet\n     * 4、也可以手动触发，不需要等待activity关闭，但是需要设置setTriggerId\n     */\n    val sbi = SaveInfo.Builder(metadataList.saveType, metadataList.autoFillIds.toTypedArray())\n//        .setOptionalIds(metadataList.autoFillIds.toTypedArray())\n      .setFlags(SaveInfo.FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE)\n      .build()\n\n    responseBuilder.setSaveInfo(sbi)\n\n    return responseBuilder.build()\n  }\n\n  /**\n   * @param dataSetAuth true 验证通过\n   * @param apkPageName 第三方apk包名\n   */\n  fun newResponse(\n    context: Context,\n    dataSetAuth: Boolean,\n    metadata: AutoFillFieldMetadataCollection,\n    entries: MutableList<PwEntry>?,\n    apkPageName: String,\n    structure: AssistStructure\n  ): FillResponse? {\n    val responseBuilder = FillResponse.Builder()\n\n    entries?.forEach { entry ->\n      val dataSet = newDataSet(context, metadata, entry, dataSetAuth, apkPageName)\n      dataSet?.let(responseBuilder::addDataset)\n    }\n//    // user editText add other item\n//    responseBuilder.addDataset(metadata.tempUserFillId?.let {\n//      otherEntry(\n//          context,\n//          apkPageName,\n//          it,\n//          structure\n//      )\n//    })\n//    // pass editText add other item\n//    responseBuilder.addDataset(metadata.tempPassFillId?.let {\n//      otherEntry(\n//          context,\n//          apkPageName,\n//          it,\n//          structure\n//      )\n//    })\n\n    return if (metadata.saveType != 0) {\n      val autoFillIds = metadata.autoFillIds\n      // 设置触发保存的类型\n      responseBuilder.setSaveInfo(\n        SaveInfo.Builder(\n          metadata.saveType,\n          autoFillIds.toTypedArray()\n        )\n          .build()\n      )\n\n//      val rev = RemoteViews(context.packageName, R.layout.item_auto_fill)\n//      rev.setTextViewText(R.id.text, context.resources.getString(R.string.other))\n//\n//      IconUtil.getBitmapFromDrawable(context,  R.drawable.ic_search, 20.toPx())?.let {\n//        rev.setImageViewBitmap(R.id.img, it)\n//      }\n////      rev.setIntent(R.id.llContent, )\n//      rev.setOnClickPendingIntent(R.id.text, PendingIntent.getBroadcast(\n//        context,\n//        1,\n//        Intent(AutoFillClickReceiver.ACTION_CLICK_OTHER),\n//        PendingIntent.FLAG_UPDATE_CURRENT\n//      ))\n//\n//      setTextColor(rev, context)\n//      responseBuilder.setHeader(rev)\n\n      responseBuilder.build()\n    } else {\n      Timber.d(\"These fields are not meant to be saved by autofill.\")\n      null\n    }\n  }\n\n  fun isValidHint(hint: String): Boolean {\n    if (hint.contains(\"user\", true) || hint.contains(\"pass\")) {\n      return true\n    }\n\n    when (hint) {\n      View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DATE,\n      View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DAY,\n      View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_MONTH,\n      View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_YEAR,\n      View.AUTOFILL_HINT_CREDIT_CARD_NUMBER,\n      View.AUTOFILL_HINT_CREDIT_CARD_SECURITY_CODE,\n      View.AUTOFILL_HINT_EMAIL_ADDRESS,\n      View.AUTOFILL_HINT_PHONE,\n      View.AUTOFILL_HINT_NAME,\n      View.AUTOFILL_HINT_PASSWORD,\n      View.AUTOFILL_HINT_POSTAL_ADDRESS,\n      View.AUTOFILL_HINT_POSTAL_CODE,\n      View.AUTOFILL_HINT_USERNAME,\n      ->\n        return true\n      else ->\n        return false\n    }\n  }\n\n  /**\n   * 将数据填充到FillResponse中\n   */\n  private fun applyDataInfoToFields(\n    pwEntry: PwEntry,\n    autoFillFieldMetadataList: AutoFillFieldMetadataCollection,\n    dataSetBuilder: Dataset.Builder\n  ): Boolean {\n    var setValueAtLeastOnce = false\n    for (hint in autoFillFieldMetadataList.allAutoFillHints) {\n      val fillFields = autoFillFieldMetadataList.getFieldsForHint(hint) ?: continue\n      loop@ for (fillField in fillFields) {\n        val fillId = fillField.autoFillId ?: break\n        val fillType = fillField.autoFillType\n        Timber.w(\"applyDataInfoToFields, autoFill type -> $fillType, autoFillId -> $fillId\")\n        when (fillType) {\n          View.AUTOFILL_TYPE_LIST -> {\n            if (fillField.autoFillField.textValue.isNullOrEmpty()) {\n              continue@loop\n            }\n\n            val listValue = fillField.getAutoFillOptionIndex(fillField.autoFillField.textValue!!)\n            if (listValue != 1) {\n              dataSetBuilder.setValue(fillId, AutofillValue.forList(listValue))\n              setValueAtLeastOnce = true\n            }\n          }\n\n          View.AUTOFILL_TYPE_DATE -> {\n            fillField.autoFillField.dateValue ?: continue@loop\n\n            dataSetBuilder.setValue(\n              fillId,\n              AutofillValue.forDate(fillField.autoFillField.dateValue!!)\n            )\n            setValueAtLeastOnce = true\n          }\n\n          View.AUTOFILL_TYPE_TEXT -> {\n            if (fillField.isPassword) {\n              dataSetBuilder.setValue(fillId, AutofillValue.forText(KdbUtil.getPassword(pwEntry)))\n            } else {\n              dataSetBuilder.setValue(fillId, AutofillValue.forText(KdbUtil.getUserName(pwEntry)))\n            }\n            setValueAtLeastOnce = true\n          }\n\n          View.AUTOFILL_TYPE_TOGGLE -> {\n            fillField.autoFillField.toggleValue ?: continue@loop\n\n            dataSetBuilder.setValue(\n              fillId,\n              AutofillValue.forToggle(fillField.autoFillField.toggleValue!!)\n            )\n            setValueAtLeastOnce = true\n          }\n          else -> Timber.w(\"Invalid autoFill type -> $fillType\")\n        }\n      }\n    }\n    return setValueAtLeastOnce\n  }\n\n  /**\n   * 将数据填充到FillResponse中\n   */\n  private fun applySaveInfoToFields(\n    autoFillFieldMetadataList: AutoFillFieldMetadataCollection,\n    dataSetBuilder: Dataset.Builder,\n    rv: RemoteViews\n  ): Boolean {\n    var setValueAtLeastOnce = false\n    for (hint in autoFillFieldMetadataList.allAutoFillHints) {\n      val fillFields = autoFillFieldMetadataList.getFieldsForHint(hint) ?: continue\n      loop@ for (fillField in fillFields) {\n        val fillId = fillField.autoFillId ?: break\n        val fillType = fillField.autoFillType\n        Timber.w(\"applySaveInfoToFields, autoFill type -> $fillType, autoFillId -> $fillId\")\n        when (fillType) {\n          View.AUTOFILL_TYPE_LIST -> {\n            if (fillField.autoFillField.textValue.isNullOrEmpty()) {\n              continue@loop\n            }\n\n            val listValue = fillField.getAutoFillOptionIndex(fillField.autoFillField.textValue!!)\n            if (listValue != 1) {\n              dataSetBuilder.setValue(fillId, AutofillValue.forList(listValue))\n              setValueAtLeastOnce = true\n            }\n          }\n\n          View.AUTOFILL_TYPE_DATE -> {\n            fillField.autoFillField.dateValue ?: continue@loop\n\n            dataSetBuilder.setValue(\n              fillId,\n              AutofillValue.forDate(fillField.autoFillField.dateValue!!)\n            )\n            setValueAtLeastOnce = true\n          }\n\n          View.AUTOFILL_TYPE_TEXT -> {\n            if (fillField.autoFillField.textValue.isNullOrEmpty()) {\n              Timber.w(\"applySaveInfoToFields, textValue is null\")\n              continue@loop\n            }\n            dataSetBuilder.setValue(\n              fillId,\n              AutofillValue.forText(fillField.autoFillField.textValue),\n              rv\n            )\n            setValueAtLeastOnce = true\n          }\n\n          View.AUTOFILL_TYPE_TOGGLE -> {\n            fillField.autoFillField.toggleValue ?: continue@loop\n\n            dataSetBuilder.setValue(\n              fillId,\n              AutofillValue.forToggle(fillField.autoFillField.toggleValue!!)\n            )\n            setValueAtLeastOnce = true\n          }\n          else -> Timber.w(\"Invalid autofill type -> $fillType\")\n        }\n      }\n    }\n    return setValueAtLeastOnce\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/service/autofill/AutoFillService.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.service.autofill\n\nimport KDBAutoFillRepository\nimport android.annotation.TargetApi\nimport android.app.assist.AssistStructure\nimport android.content.Context\nimport android.content.IntentSender\nimport android.os.Build\nimport android.os.Build.VERSION_CODES\nimport android.os.CancellationSignal\nimport android.service.autofill.AutofillService\nimport android.service.autofill.FillCallback\nimport android.service.autofill.FillRequest\nimport android.service.autofill.FillResponse\nimport android.service.autofill.SaveCallback\nimport android.service.autofill.SaveRequest\nimport com.arialyy.frame.util.ResUtil\nimport com.blankj.utilcode.util.ToastUtils\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseApp\nimport com.lyy.keepassa.entity.AutoFillParam\nimport com.lyy.keepassa.service.autofill.model.AutoFillFieldMetadataCollection\nimport com.lyy.keepassa.util.HitUtil\nimport com.lyy.keepassa.util.KLog\nimport com.lyy.keepassa.util.KdbUtil.isNull\nimport com.lyy.keepassa.util.LanguageUtil\nimport com.lyy.keepassa.util.PermissionsUtil\nimport com.lyy.keepassa.util.isCanOpenQuickLock\nimport com.lyy.keepassa.view.create.entry.CreateEntryActivity\nimport com.lyy.keepassa.view.launcher.LauncherActivity\nimport com.lyy.keepassa.view.main.QuickUnlockActivity\nimport com.lyy.keepassa.view.search.AutoFillEntrySearchActivity\nimport timber.log.Timber\n\n/**\n * 自动填充服务\n * 官方demo https://github.com/android/input-samples\n * 官方文档：https://developer.android.com/reference/android/service/autofill/AutofillService\n */\n@TargetApi(VERSION_CODES.O)\nclass AutoFillService : AutofillService() {\n\n  /**\n   * 接收请求\n   */\n  override fun onFillRequest(\n    request: FillRequest,\n    cancellationSignal: CancellationSignal,\n    callback: FillCallback\n  ) {\n    val isManual = request.flags == FillRequest.FLAG_MANUAL_REQUEST\n    val structure = request.fillContexts[request.fillContexts.size - 1].structure\n    val apkPackageName = structure.activityComponent.packageName\n    if (apkPackageName.equals(packageName, ignoreCase = true)) {\n      // 本应用内不进行填充\n      return\n    }\n\n    if (!PermissionsUtil.isCanBackgroundStart()) {\n      ToastUtils.showLong(R.string.hint_open_background_start)\n      return\n    }\n\n    Timber.d(\n      \"onFillRequest(): flags = ${request.flags}, requestId = ${request.id}, clientState = ${\n        KLog.b(\n          request.clientState\n        )\n      }\"\n    )\n    cancellationSignal.setOnCancelListener {\n      Timber.w(\"Cancel autofill not implemented in this sample.\")\n    }\n\n    // Parse AutoFill data in Activity\n    val parser = StructureParser(structure)\n    parser.parseForFill(isManual, apkPackageName)\n    val autoFillFields = parser.autoFillFields\n    val needAuth = BaseApp.KDB == null || BaseApp.isLocked\n\n    if (autoFillFields.autoFillIds.size <= 0) {\n      Timber.i(\"autoFillIds is nulll\")\n      callback.onSuccess(null)\n      return\n    }\n\n\n    // 如果数据库没打开，或者数据库已经锁定，打开登录页面\n    if (needAuth) {\n      val isOpenQuickLock = BaseApp.APP.isCanOpenQuickLock()\n\n      if (BaseApp.KDB == null) {\n        openLoginActivity(callback, autoFillFields, apkPackageName, structure)\n        return\n      }\n\n      if (isOpenQuickLock) {\n        openQuickUnLockActivity(callback, autoFillFields, apkPackageName, structure)\n        return\n      }\n\n      openLoginActivity(callback, autoFillFields, apkPackageName, structure)\n      return\n    }\n    // 获取填充数据\n    val datas = if (parser.domainUrl.isEmpty()) {\n      KDBAutoFillRepository.getAutoFillDataByPackageName(apkPackageName)\n    } else {\n      KDBAutoFillRepository.getAutoFillDataByDomain(parser.domainUrl)\n    }\n\n    Timber.d(\"entrySize = ${datas?.size}\")\n    // 没有匹配的数据，进入搜索界面\n    if (datas == null) {\n      openSearchActivity(callback, autoFillFields, apkPackageName, structure)\n      return\n    }\n    val response =\n      AutoFillHelper.newResponse(this, !needAuth, autoFillFields, datas, apkPackageName, structure)\n    callback.onSuccess(response)\n  }\n\n  /**\n   * 启动搜索界面\n   */\n  private fun openSearchActivity(\n    callback: FillCallback,\n    autofillFields: AutoFillFieldMetadataCollection,\n    apkPackageName: String,\n    structure: AssistStructure\n  ) {\n    callback.onSuccess(\n      getAuthResponse(\n        autofillFields,\n        AutoFillEntrySearchActivity.getSearchIntentSender(this, apkPackageName, structure)\n      )\n    )\n  }\n\n  /**\n   * 启动快速解锁界面\n   */\n  private fun openQuickUnLockActivity(\n    callback: FillCallback,\n    autofillFields: AutoFillFieldMetadataCollection,\n    apkPackageName: String,\n    structure: AssistStructure\n  ) {\n    callback.onSuccess(\n      getAuthResponse(\n        autofillFields,\n        QuickUnlockActivity.getQuickUnlockSenderForResponse(this, apkPackageName, structure)\n      )\n    )\n  }\n\n  /**\n   * 启动登录界面\n   */\n  private fun openLoginActivity(\n    callback: FillCallback,\n    autofillFields: AutoFillFieldMetadataCollection,\n    apkPackageName: String,\n    structure: AssistStructure\n  ) {\n    callback.onSuccess(\n      getAuthResponse(\n        autofillFields,\n        LauncherActivity.getAuthDbIntentSender(this, apkPackageName, structure)\n      )\n    )\n  }\n\n  /**\n   * 启动数据库验证界面或数据为空时的匹配界面\n   */\n  private fun getAuthResponse(\n    metadataList: AutoFillFieldMetadataCollection,\n    sender: IntentSender\n  ): FillResponse {\n    return AutoFillHelper.newSaveResponse(this, metadataList, sender)\n  }\n\n  /**\n   * 保存用户数据\n   */\n  override fun onSaveRequest(\n    request: SaveRequest,\n    callback: SaveCallback\n  ) {\n    if (Build.VERSION.SDK_INT < VERSION_CODES.P) {\n      val str = ResUtil.getString(R.string.fail_unsupported_Systems_O)\n      callback.onFailure(str)\n      ToastUtils.showLong(str)\n      return\n    }\n    val context = request.fillContexts\n    val structure = context[context.size - 1].structure\n    val apkPackageName = structure.activityComponent.packageName\n    val data = request.clientState\n    Timber.d(\"onSaveRequest(): data=${KLog.b(data)}\")\n\n    val parser = StructureParser(structure)\n    parser.parseForFill(true, apkPackageName)\n    val needAuth = BaseApp.KDB.isNull() || BaseApp.isLocked\n\n    // 如果数据库没打开，需要打开登录页面\n    val p = KDBAutoFillRepository.getUserInfo(parser.autoFillFields)\n    Timber.d(\"用户信息：$p\")\n    if (needAuth) {\n      // This api is only at P\n      callback.onSuccess(\n        LauncherActivity.authAndSaveDb(\n          context = this,\n          apkPackageName = apkPackageName,\n          userName = p.first ?: \"\",\n          pass = p.second ?: \"\",\n          if (!BaseApp.KDB.isNull() && BaseApp.APP.isCanOpenQuickLock()) QuickUnlockActivity::class.java else LauncherActivity::class.java\n        )\n      )\n      return\n    }\n    if (BaseApp.KDB == null) {\n      // 用户没有登陆成功，保存失败\n      callback.onFailure(getString(R.string.hint_please_open_database))\n      return\n    }\n\n    // KDBAutoFillRepository.saveDataToKdb(this, apkPackageName, parser.autoFillFields)\n    callback.onSuccess(\n      CreateEntryActivity.authAndSaveDb(\n        this, AutoFillParam(\n          apkPkgName = apkPackageName,\n          saveUserName = p.first ?: \"\",\n          savePass = p.second ?: \"\",\n          isSave = true\n        )\n      )\n    )\n    HitUtil.toaskLong(getString(R.string.save_db_success))\n  }\n\n  override fun onConnected() {\n    Timber.d(\"onConnected\")\n  }\n\n  override fun onDisconnected() {\n    Timber.d(\"onDisconnected\")\n    // W3cHints.curDomainUrl = \"\"\n  }\n\n  override fun attachBaseContext(newBase: Context?) {\n    super.attachBaseContext(LanguageUtil.setLanguage(newBase!!, BaseApp.currentLang))\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/service/autofill/PackageVerifier.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.service.autofill\n\n/*\n * Copyright (C) 2017 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.annotation.SuppressLint\nimport android.content.Context\nimport android.content.pm.PackageManager\nimport android.os.Build\nimport android.os.Build.VERSION_CODES\nimport timber.log.Timber\nimport java.io.ByteArrayInputStream\nimport java.security.MessageDigest\nimport java.security.cert.CertificateFactory\nimport java.security.cert.X509Certificate\nimport java.util.Locale\n\nobject PackageVerifier {\n  val TAG = javaClass.simpleName\n\n  /**\n   * Verifies if a package is valid by matching its certificate with the previously stored\n   * certificate.\n   */\n  fun isValidPackage(\n    context: Context,\n    packageName: String\n  ): Boolean {\n    val hash: String\n    try {\n      hash = getCertificateHash(context, packageName)\n      Timber.d( \"Hash for $packageName: $hash\")\n    } catch (e: Exception) {\n      Timber.w(\"Error getting hash for $packageName: $e\")\n      return false\n    }\n\n    return verifyHash(context, packageName, hash)\n  }\n\n  @SuppressLint(\"PackageManagerGetSignatures\")\n  private fun getCertificateHash(\n    context: Context,\n    packageName: String\n  ): String {\n    val pm = context.packageManager\n    val signatures = if (Build.VERSION.SDK_INT >= VERSION_CODES.P) {\n      pm.getPackageInfo(\n        packageName,\n        PackageManager.GET_SIGNING_CERTIFICATES\n      ).signingInfo.apkContentsSigners\n    } else {\n      pm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES).signatures\n    }\n\n    val cert = signatures[0].toByteArray()\n    ByteArrayInputStream(cert).use { input ->\n      val factory = CertificateFactory.getInstance(\"X509\")\n      val x509 = factory.generateCertificate(input) as X509Certificate\n      val md = MessageDigest.getInstance(\"SHA256\")\n      val publicKey = md.digest(x509.encoded)\n      return toHexFormat(publicKey)\n    }\n  }\n\n  private fun toHexFormat(bytes: ByteArray): String {\n    val builder = StringBuilder(bytes.size * 2)\n    for (i in bytes.indices) {\n      var hex = Integer.toHexString(bytes[i].toInt())\n      val length = hex.length\n      if (length == 1) {\n        hex = \"0$hex\"\n      }\n      if (length > 2) {\n        hex = hex.substring(length - 2, length)\n      }\n      builder.append(hex.toUpperCase(Locale.ROOT))\n      if (i < bytes.size - 1) {\n        builder.append(':')\n      }\n    }\n    return builder.toString()\n  }\n\n  private fun verifyHash(\n    context: Context,\n    packageName: String,\n    hash: String\n  ): Boolean {\n    val prefs = context.applicationContext.getSharedPreferences(\n      \"package-hashes\", Context.MODE_PRIVATE\n    )\n    if (!prefs.contains(packageName)) {\n      Timber.d( \"Creating intial hash for $packageName\")\n      prefs.edit()\n        .putString(packageName, hash)\n        .apply()\n      return true\n    }\n\n    val existingHash = prefs.getString(packageName, null)\n    if (hash != existingHash) {\n      Timber.w(\"hash mismatch for ${packageName}: expected ${existingHash}, got  $hash\")\n      return false\n    }\n    return true\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/service/autofill/StructureParser.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.service.autofill\n\nimport android.annotation.TargetApi\nimport android.app.assist.AssistStructure\nimport android.app.assist.AssistStructure.ViewNode\nimport android.os.Build\nimport android.text.InputType\nimport android.view.View\nimport androidx.autofill.HintConstants\nimport com.lyy.keepassa.service.autofill.model.AutoFillFieldMetadata\nimport com.lyy.keepassa.service.autofill.model.AutoFillFieldMetadataCollection\nimport timber.log.Timber\n\n/**\n * Parser for an AssistStructure object. This is invoked when the Autofill Service receives an\n * AssistStructure from the client Activity, representing its View hierarchy. In this sample, it\n * parses the hierarchy and collects autofill metadata from {@link ViewNode}s along the way.\n */\n@TargetApi(Build.VERSION_CODES.O)\ninternal class StructureParser(private val autofillStructure: AssistStructure) {\n  val autoFillFields = AutoFillFieldMetadataCollection()\n  val useFields = ArrayList<ViewNode>()\n  val passFields = ArrayList<ViewNode>()\n  var domainUrl = \"\"\n  var pkgName = \"\"\n  var isW3c = false\n  var isInnerAppW3c = false\n\n  companion object {\n    // 其它应用editText 可能设置的id名，如：R.id.email\n    val usernameHints = HashSet<String>().also {\n      it.add(\"email\")\n      it.add(\"e-email\")\n      it.add(\"account\")\n      it.add(\"user_name\")\n      it.add(\"mobile\")\n      it.add(HintConstants.AUTOFILL_HINT_EMAIL_ADDRESS)\n      it.add(HintConstants.AUTOFILL_HINT_PHONE)\n      it.add(HintConstants.AUTOFILL_HINT_NAME)\n      it.add(HintConstants.AUTOFILL_HINT_USERNAME)\n      it.add(HintConstants.AUTOFILL_HINT_PERSON_NAME)\n      it.add(HintConstants.AUTOFILL_HINT_PERSON_NAME_GIVEN)\n      it.add(HintConstants.AUTOFILL_HINT_NEW_USERNAME)\n      it.add(HintConstants.AUTOFILL_HINT_POSTAL_ADDRESS)\n      it.add(HintConstants.AUTOFILL_HINT_POSTAL_CODE)\n    }\n\n    val passHints = HashSet<String>().also {\n      it.add(HintConstants.AUTOFILL_HINT_PASSWORD)\n      it.add(HintConstants.AUTOFILL_HINT_NEW_PASSWORD)\n      it.add(\"passwort\")\n    }\n\n    /**\n     * key: class name, value: isEditText\n     */\n    val editTextMap = HashSet<String>()\n\n    /**\n     * key: class name, value: WebView\n     */\n    val webViewMap = HashSet<String>()\n  }\n\n  private fun clear() {\n    autoFillFields.clear()\n    useFields.clear()\n    passFields.clear()\n  }\n\n  /**\n   * 是否是用户手动 用户手机选择了自动填充，也就是editText获取了焦点才开始弹出\n   * @param pkgName 目标应用包名\n   */\n  fun parseForFill(\n    isManual: Boolean,\n    pkgName: String\n  ) {\n    this.pkgName = pkgName\n    parse(isManual)\n  }\n\n  /**\n   * Traverse AssistStructure and add ViewNode metadata to a flat list.\n   */\n  private fun parse(isManual: Boolean) {\n    isW3c = false\n    domainUrl = \"\"\n    Timber.d(\"Parsing structure for ${autofillStructure.activityComponent}\")\n    val nodeSize = autofillStructure.windowNodeCount\n    clear()\n    for (i in 0 until nodeSize) {\n      parseLocked(autofillStructure.getWindowNodeAt(i).rootViewNode)\n    }\n    // 如果密码为空，默认不弹出选择item，这是为了防止遇到editText就弹出item的情况\n    if (passFields.isEmpty() && !isManual && !isW3c) {\n      autoFillFields.clear()\n    }\n  }\n\n  private fun parseLocked(viewNode: ViewNode) {\n    // 处理editText 增加 android:autofillHints 的情况\n    if (!viewNode.autofillHints.isNullOrEmpty()) {\n      if (isW3c) {\n        getW3CInfo(viewNode)\n      } else {\n        getAndroidViewInfo(viewNode)\n      }\n    } else {\n      if (W3cHints.isBrowser(pkgName)) {\n        // Timber.i(\"is browser, start get web info\")\n        checkW3C(viewNode)\n        if (isW3c) {\n          if (domainUrl.isBlank()) {\n            domainUrl = viewNode.webDomain ?: \"\"\n            W3cHints.curDomainUrl = domainUrl\n            Timber.d(\"domainUrl = $domainUrl\")\n          }\n          getW3CInfo(viewNode)\n        }\n      } else {\n        val className = viewNode.className\n        if (classIsEditText(className)) {\n          getAndroidViewInfo(viewNode)\n        } else if (classIsWebView(className)) {\n          innerAppWebView(viewNode)\n          return\n        }\n      }\n    }\n\n    val childrenSize = viewNode.childCount\n    for (i in 0 until childrenSize) {\n      parseLocked(viewNode.getChildAt(i))\n    }\n  }\n\n  /**\n   * 内置浏览器\n   */\n  private fun innerAppWebView(viewNode: ViewNode) {\n    isInnerAppW3c = true\n    if (domainUrl.isBlank()) {\n      domainUrl = viewNode.webDomain ?: \"\"\n      W3cHints.curDomainUrl = domainUrl\n      Timber.d(\"domainUrl = $domainUrl\")\n    }\n    getW3CInfo(viewNode)\n    val childrenSize = viewNode.childCount\n    for (i in 0 until childrenSize) {\n      innerAppWebView(viewNode.getChildAt(i))\n    }\n  }\n\n  private fun checkIsWebView(clazz: Class<*>): Boolean {\n    if (clazz.name.equals(\"android.webkit.WebView\")) {\n      return true\n    }\n    val sup = clazz.superclass ?: return false\n    if (sup.name.equals(\"java.lang.Object\")) {\n      return false\n    }\n    if (sup.name.equals(\"android.webkit.WebView\")) {\n      return true\n    }\n    return checkIsWebView(sup)\n  }\n\n  private fun classIsWebView(className: String?): Boolean {\n    if (className.isNullOrEmpty()) return false\n    if (webViewMap.contains(className)) return true\n    try {\n      if (checkIsWebView(Class.forName(className))) {\n        webViewMap.add(className)\n        return true\n      }\n    } catch (e: ClassNotFoundException) {\n      Timber.e(e)\n    }\n    return false\n  }\n\n  private fun checkIsEditText(clazz: Class<*>): Boolean {\n    if (clazz.name.equals(\"android.widget.EditText\")) {\n      return true\n    }\n    val sup = clazz.superclass ?: return false\n    if (sup.name.equals(\"java.lang.Object\")) {\n      return false\n    }\n    if (sup.name.equals(\"android.widget.EditText\")) {\n      return true\n    }\n    return checkIsEditText(sup)\n  }\n\n  private fun classIsEditText(className: String?): Boolean {\n    if (className.isNullOrEmpty()) return false\n    if (editTextMap.contains(className)) return true\n    try {\n      if (checkIsEditText(Class.forName(className))) {\n        editTextMap.add(className)\n        return true\n      }\n    } catch (e: ClassNotFoundException) {\n      Timber.e(e)\n    }\n\n    return false\n  }\n\n  private fun getAndroidViewInfo(viewNode: ViewNode) {\n    if (isPassword(viewNode)) {\n      addPassField(viewNode)\n      return\n    }\n    if (isUserName(viewNode)) {\n      addUserField(viewNode)\n      return\n    }\n    Timber.d(\n      \"not w3c, unknown idEntry = ${viewNode.idEntry}, isFocused = ${viewNode.isFocused}, autofillId = ${viewNode.autofillId}, fillValue = ${viewNode.autofillValue}, inputType =  ${viewNode.inputType}, htmlInfo = ${viewNode.htmlInfo}, autofillType = ${viewNode.autofillType}, hint = ${viewNode.hint}, isAccessibilityFocused =${viewNode.isAccessibilityFocused},  idPackage = ${viewNode.idPackage}, isActivated = ${viewNode.isActivated}, visibility = ${viewNode.visibility}, isAssistBlocked = ${viewNode.isAssistBlocked}, isOpaque = ${viewNode.isOpaque}\"\n    )\n  }\n\n  private fun getW3CInfo(viewNode: ViewNode) {\n    if (viewNode.htmlInfo == null) {\n      return\n    }\n    if (W3cHints.isW3CUserByHints(viewNode)) {\n      Timber.i(\"addUser by hints\")\n      addUserField(viewNode)\n      return\n    }\n    if (W3cHints.isW3CPassByHints(viewNode)) {\n      Timber.i(\"addPassword by hints\")\n      addPassField(viewNode)\n      return\n    }\n    Timber.d(\n      \"w3c, unknown idEntry = ${viewNode.idEntry}, isFocused = ${viewNode.isFocused}, autofillId = ${viewNode.autofillId}, fillValue = ${viewNode.autofillValue}, inputType =  ${viewNode.inputType}, htmlInfo = ${viewNode.htmlInfo}, autofillType = ${viewNode.autofillType}, hint = ${viewNode.hint}, isAccessibilityFocused =${viewNode.isAccessibilityFocused},  idPackage = ${viewNode.idPackage}, isActivated = ${viewNode.isActivated}, visibility = ${viewNode.visibility}, isAssistBlocked = ${viewNode.isAssistBlocked}, isOpaque = ${viewNode.isOpaque}\"\n    )\n  }\n\n  /**\n   * Check whether the web page\n   */\n  private fun checkW3C(viewNode: ViewNode): Boolean {\n    if (isW3c) {\n      return true\n    }\n    isW3c = viewNode.htmlInfo?.tag == \"input\" || viewNode.className == \"android.webkit.WebView\"\n    return isW3c\n  }\n\n  /**\n   * add pass field\n   */\n  private fun addPassField(viewNode: ViewNode) {\n    if (!isW3c && !isInnerAppW3c && (viewNode.visibility != View.VISIBLE || !viewNode.isFocusable)) {\n      return\n    }\n    autoFillFields.tempPassFillId = viewNode.autofillId\n    Timber.d(\"pass autofillType = ${viewNode.autofillType}, fillId = ${viewNode.autofillId}, fillValue = ${viewNode.autofillValue}, text = ${viewNode.text}, hint = ${viewNode.hint}, visibility = ${viewNode.visibility}, isActivated = ${viewNode.isActivated}\")\n    passFields.add(viewNode)\n    autoFillFields.add(AutoFillFieldMetadata(viewNode, View.AUTOFILL_HINT_PASSWORD))\n  }\n\n  /**\n   * add userName field\n   */\n  private fun addUserField(viewNode: ViewNode) {\n    if (!isW3c && !isInnerAppW3c && (viewNode.visibility != View.VISIBLE || !viewNode.isFocusable)) {\n      return\n    }\n    if (autoFillFields.tempUserFillId == null || viewNode.isFocused) {\n      autoFillFields.tempUserFillId = viewNode.autofillId\n      Timber.d(\"user autofillType = ${viewNode.autofillType}, fillId = ${viewNode.autofillId}, idEntry = ${viewNode.idEntry}, fillValue = ${viewNode.autofillValue} text = ${viewNode.text}, hint = ${viewNode.hint}, visibility = ${viewNode.visibility}, isActivated = ${viewNode.isActivated}\")\n      useFields.add(viewNode)\n      autoFillFields.add(AutoFillFieldMetadata(viewNode, View.AUTOFILL_HINT_USERNAME))\n    }\n  }\n\n  /**\n   * 判断是否是用户名输入框\n   */\n  private fun isUserName(f: ViewNode): Boolean {\n    if (!isPassword(f)\n      || usernameHints.any { f.idEntry != null && f.idEntry!!.contains(it, ignoreCase = true) }\n      || usernameHints.any { f.hint != null && f.hint!!.contains(it, ignoreCase = true) }\n    ) {\n      if ((f.idEntry != null && f.idEntry!!.contains(\"search\", ignoreCase = false))\n        || (f.hint != null && f.hint!!.contains(\"search\", ignoreCase = false))\n      ) {\n        return false\n      }\n      return true\n    }\n    return false\n  }\n\n  /**\n   * 判断是否是密码输入框\n   * @return true 密码输入框\n   */\n  private fun isPassword(f: ViewNode): Boolean {\n    val inputType = f.inputType\n    if (inputType == InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD\n      || inputType == InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_WEB_PASSWORD\n      || inputType == InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_PASSWORD\n      || inputType == InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_VARIATION_PASSWORD\n      || passHints.any { f.idEntry != null && f.idEntry!!.contains(it, ignoreCase = true) }\n    ) {\n      return true\n    }\n    return false\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/service/autofill/W3cHints.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.service.autofill\n\nimport android.annotation.TargetApi\nimport android.app.assist.AssistStructure.ViewNode\nimport android.os.Build\nimport com.arialyy.frame.util.ResUtil\nimport com.blankj.utilcode.util.ActivityUtils\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseApp\nimport timber.log.Timber\nimport java.util.Locale\n\n/**\n * https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/autocomplete\n */\n@TargetApi(Build.VERSION_CODES.O)\nobject W3cHints {\n\n  val CompatBrowsers = setOf(\n    \"org.mozilla.firefox\",\n    \"org.mozilla.firefox_beta\",\n    \"com.microsoft.emmx\",\n    \"com.android.chrome\",\n    \"com.chrome.beta\",\n    \"com.android.browser\",\n    \"com.brave.browser\",\n    \"com.opera.browser\",\n    \"com.opera.browser.beta\",\n    \"com.opera.mini.native\",\n    \"com.chrome.dev\",\n    \"com.chrome.canary\",\n    \"com.google.android.apps.chrome\",\n    \"com.google.android.apps.chrome_dev\",\n    \"com.yandex.browser\",\n    \"com.sec.android.app.sbrowser\",\n    \"com.sec.android.app.sbrowser.beta\",\n    \"org.codeaurora.swe.browser\",\n    \"com.amazon.cloud9\",\n    \"mark.via.gp\",\n    \"mark.via\",\n    \"org.bromite.bromite\",\n    \"org.chromium.chrome\",\n    \"com.kiwibrowser.browser\",\n    \"com.ecosia.android\",\n    \"com.opera.mini.native.beta\",\n    \"org.mozilla.fennec_aurora\",\n    \"org.mozilla.fennec_fdroid\",\n    \"com.qwant.liberty\",\n    \"com.opera.touch\",\n    \"org.mozilla.fenix\",\n    \"org.mozilla.fenix.nightly\",\n    \"org.mozilla.reference.browser\",\n    \"org.mozilla.rocket\",\n    \"org.torproject.torbrowser\",\n    \"com.vivaldi.browser\",\n    \"com.mmbox.xbrowser\",\n    \"info.torapp.uweb\"\n  )\n\n  const val HONORIFIC_PREFIX = \"honorific-prefix\"\n  const val NAME = \"name\"\n  const val GIVEN_NAME = \"given-name\"\n  const val ADDITIONAL_NAME = \"additional-name\"\n  const val FAMILY_NAME = \"family-name\"\n  const val HONORIFIC_SUFFIX = \"honorific-suffix\"\n  const val USERNAME = \"username\"\n  const val PWD = \"pwd\"\n  const val PASSWORD = \"password\"\n  const val NEW_PASSWORD = \"new-password\"\n  const val CURRENT_PASSWORD = \"current-password\"\n  const val ORGANIZATION_TITLE = \"organization-title\"\n  const val ORGANIZATION = \"organization\"\n  const val STREET_ADDRESS = \"street-address\"\n  const val ADDRESS_LINE1 = \"address-line1\"\n  const val ADDRESS_LINE2 = \"address-line2\"\n  const val ADDRESS_LINE3 = \"address-line3\"\n  const val ADDRESS_LEVEL4 = \"address-level4\"\n  const val ADDRESS_LEVEL3 = \"address-level3\"\n  const val ADDRESS_LEVEL2 = \"address-level2\"\n  const val ADDRESS_LEVEL1 = \"address-level1\"\n  const val COUNTRY = \"country\"\n  const val COUNTRY_NAME = \"country-name\"\n  const val POSTAL_CODE = \"postal-code\"\n  const val CC_NAME = \"cc-name\"\n  const val CC_GIVEN_NAME = \"cc-given-name\"\n  const val CC_ADDITIONAL_NAME = \"cc-additional-name\"\n  const val CC_FAMILY_NAME = \"cc-family-name\"\n  const val CC_NUMBER = \"cc-number\"\n  const val CC_EXPIRATION = \"cc-exp\"\n  const val CC_EXPIRATION_MONTH = \"cc-exp-month\"\n  const val CC_EXPIRATION_YEAR = \"cc-exp-year\"\n  const val CC_CSC = \"cc-csc\"\n  const val CC_TYPE = \"cc-type\"\n  const val TRANSACTION_CURRENCY = \"transaction-currency\"\n  const val TRANSACTION_AMOUNT = \"transaction-amount\"\n  const val LANGUAGE = \"language\"\n  const val BDAY = \"bday\"\n  const val BDAY_DAY = \"bday-day\"\n  const val BDAY_MONTH = \"bday-month\"\n  const val BDAY_YEAR = \"bday-year\"\n  const val SEX = \"sex\"\n  const val URL = \"url\"\n  const val PHOTO = \"photo\"\n\n  // Optional W3C prefixes\n  const val PREFIX_SECTION = \"section-\"\n  const val SHIPPING = \"shipping\"\n  const val BILLING = \"billing\"\n\n  // W3C prefixes below...\n  const val PREFIX_HOME = \"home\"\n  const val PREFIX_WORK = \"work\"\n  const val PREFIX_FAX = \"fax\"\n  const val PREFIX_PAGER = \"pager\"\n\n  // ... require those suffix\n  const val TEL = \"tel\"\n  const val TEL_COUNTRY_CODE = \"tel-country-code\"\n  const val TEL_NATIONAL = \"tel-national\"\n  const val TEL_AREA_CODE = \"tel-area-code\"\n  const val TEL_LOCAL = \"tel-local\"\n  const val TEL_LOCAL_PREFIX = \"tel-local-prefix\"\n  const val TEL_LOCAL_SUFFIX = \"tel-local-suffix\"\n  const val TEL_EXTENSION = \"tel_extension\"\n  const val EMAIL = \"email\"\n  const val TEXT = \"text\"\n  const val IMPP = \"impp\"\n\n  // totp\n  const val ONE_TIME_CODE = \"one-time-code\"\n\n  private val PASSWORD_HINT_LIST = arrayListOf(PASSWORD, NEW_PASSWORD, CURRENT_PASSWORD)\n  private val USER_HINT_LIST = arrayListOf(NAME, USERNAME, TEL, GIVEN_NAME, EMAIL, IMPP)\n  private val ATTR_LIST = arrayListOf(\"name\", \"type\")\n\n  private val HINT_USER_LABEL by lazy {\n    if (ActivityUtils.getTopActivity() != null){\n      ActivityUtils.getTopActivity().resources.getStringArray(R.array.auto_fill_hint_label)\n    }else{\n      BaseApp.APP.resources.getStringArray(R.array.auto_fill_hint_label)\n    }\n  }\n\n  private val HINT_PASSWORD_LABEL by lazy {\n    ResUtil.getString(R.string.password)\n  }\n\n  var curDomainUrl = \"\"\n\n  /**\n   * 是否是浏览器\n   */\n  fun isBrowser(pkgName: String?): Boolean {\n    if (pkgName.isNullOrEmpty()) return false\n    return CompatBrowsers.contains(pkgName)\n  }\n\n  /**\n   * 是否是用户名\n   */\n  fun isW3cUserName(p: android.util.Pair<String, String>): Boolean {\n    if (p.second.isNullOrEmpty()) {\n      return false\n    }\n    val temp = p.second.lowercase()\n    return USER_HINT_LIST.contains(temp)\n  }\n\n  /**\n   * 是否是密码\n   */\n  fun isW3cPassWord(p: android.util.Pair<String, String>): Boolean {\n\n    if (p.second.isNullOrEmpty()) {\n      return false\n    }\n    val temp = p.second.lowercase()\n    return PASSWORD_HINT_LIST.contains(temp)\n      // https://developer.mozilla.org/en-US/docs/Web/Security/Securing_your_site/Turning_off_form_autocompletion\n      || (p.first == \"autocomplete\")\n  }\n\n  fun isW3CUserByHints(viewNode: ViewNode): Boolean {\n    if (!viewNode.htmlInfo?.tag.equals(\"input\", true)) {\n      return false\n    }\n    val hints = viewNode.autofillHints\n    hints?.forEach {\n      val temp = it.lowercase()\n      if (USER_HINT_LIST.contains(temp)) {\n        return true\n      }\n\n      if (StructureParser.usernameHints.contains(temp)) {\n        return true\n      }\n\n      if (temp.contains(\"user\", true) || temp.contains(\"account\", true)) {\n        return true\n      }\n    }\n\n    val names = viewNode.htmlInfo?.attributes\n    names?.forEach {\n      if (it.first.isNullOrEmpty() || it.second.isNullOrEmpty()) {\n        Timber.d(\"value is null, first = ${it.first}, second = ${it.second}\")\n        return@forEach\n      }\n      val name = it.first.lowercase()\n      val value = it.second.lowercase()\n      if (ATTR_LIST.contains(name) && USER_HINT_LIST.contains(value)) {\n        return true\n      }\n      if (name == \"label\") {\n        HINT_USER_LABEL.forEach { label ->\n          if (value.contains(label)) {\n            return true\n          }\n        }\n      }\n    }\n    return false\n  }\n\n  fun isW3CPassByHints(viewNode: ViewNode): Boolean {\n    if (!viewNode.htmlInfo?.tag.equals(\"input\", true)) {\n      return false\n    }\n    val hints = viewNode.autofillHints\n    hints?.forEach {\n      val temp = it.lowercase()\n      if (PASSWORD_HINT_LIST.contains(temp)) {\n        return true\n      }\n\n      if (StructureParser.passHints.contains(temp)) {\n        return true\n      }\n\n      if (temp.contains(\"password\", true)) {\n        return true\n      }\n    }\n\n    val names = viewNode.htmlInfo?.attributes\n    names?.forEach {\n      if (it.first.isNullOrEmpty() || it.second.isNullOrEmpty()) {\n        Timber.d(\"value is null, first = ${it.first}, second = ${it.second}\")\n        return@forEach\n      }\n      val name = it.first.lowercase()\n      val value = it.second.lowercase()\n      if (ATTR_LIST.contains(name) && PASSWORD_HINT_LIST.contains(value)) {\n        return true\n      }\n      if (name == \"label\" && value.contains(HINT_PASSWORD_LABEL)) {\n        return true\n      }\n\n    }\n\n    return false\n  }\n\n  fun isW3cSectionPrefix(hint: String): Boolean {\n    return hint.lowercase(Locale.ROOT)\n      .startsWith(PREFIX_SECTION);\n  }\n\n  fun isW3cAddressType(hint: String): Boolean {\n    when (hint.lowercase(Locale.ROOT)) {\n      SHIPPING, BILLING ->\n        return true\n    }\n    return false\n  }\n\n  fun isW3cTypePrefix(hint: String): Boolean {\n    when (hint.lowercase(Locale.ROOT)) {\n      PREFIX_WORK, PREFIX_FAX, PREFIX_HOME, PREFIX_PAGER ->\n        return true\n    }\n    return false\n  }\n\n  fun isW3cTypeHint(hint: String): Boolean {\n    when (hint.lowercase(Locale.ROOT)) {\n      TEL, TEL_COUNTRY_CODE, TEL_NATIONAL, TEL_AREA_CODE, TEL_LOCAL,\n      TEL_LOCAL_PREFIX, TEL_LOCAL_SUFFIX, TEL_EXTENSION, EMAIL, IMPP ->\n        return true;\n    }\n    Timber.w(\"Inid W3C type hint: $hint\")\n    return false;\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/service/autofill/datasource/KDBAutoFillRepository.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\nimport android.content.Context\nimport android.content.pm.PackageManager\nimport android.graphics.Bitmap.CompressFormat.PNG\nimport android.view.View\nimport com.arialyy.frame.config.CommonConstant\nimport com.arialyy.frame.util.ResUtil\nimport com.blankj.utilcode.util.ToastUtils\nimport com.keepassdroid.database.PwDatabaseV4\nimport com.keepassdroid.database.PwEntry\nimport com.keepassdroid.database.PwEntryV3\nimport com.keepassdroid.database.PwEntryV4\nimport com.keepassdroid.database.PwGroupV4\nimport com.keepassdroid.database.PwIconCustom\nimport com.keepassdroid.database.PwIconStandard\nimport com.keepassdroid.database.SearchParametersV4\nimport com.keepassdroid.database.security.ProtectedString\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseApp\nimport com.lyy.keepassa.service.autofill.model.AutoFillFieldMetadataCollection\nimport com.lyy.keepassa.util.IconUtil\nimport com.lyy.keepassa.util.KdbUtil\nimport com.lyy.keepassa.util.KpaUtil\nimport timber.log.Timber\nimport java.io.ByteArrayOutputStream\nimport java.util.UUID\n\n/**\n * Singleton autofill data repository that stores autofill fields to SharedPreferences.\n * Disclaimer: you should not store sensitive fields like user data unencrypted. This is done\n * here only for simplicity and learning purposes.\n */\nobject KDBAutoFillRepository {\n\n  /**\n   * 通过包名获取填充数据\n   */\n  fun getAutoFillDataByPackageName(pkgName: String): MutableList<PwEntry>? {\n    if (BaseApp.KDB?.pm == null) {\n      return null\n    }\n    Timber.d(\"getFillDataByPkgName, pkgName = $pkgName\")\n    val listStorage = ArrayList<PwEntry>()\n    KdbUtil.searchEntriesByPackageName(pkgName, listStorage)\n    if (listStorage.isEmpty()) {\n      val sp = SearchParametersV4()\n      val strs = pkgName.split(\".\")\n      // 如果没有，则从url检索\n      for (s in strs) {\n        if (CommonConstant.domainSuffix.contains(s)) {\n          continue\n        }\n        sp.setupNone()\n        sp.searchInUrls = true\n        sp.searchString = s\n        BaseApp.KDB!!.pm.rootGroup.searchEntries(sp, listStorage)\n      }\n\n      if (listStorage.isEmpty()) {\n        return null\n      }\n    }\n\n    return listStorage.toSet().toMutableList()\n  }\n\n  /**\n   * 通过url获取填充数据\n   */\n  fun getAutoFillDataByDomain(domain: String): ArrayList<PwEntry>? {\n    Timber.d(\"getFillDataByDomain, domain = $domain\")\n    val listStorage = ArrayList<PwEntry>()\n    KdbUtil.searchEntriesByDomain(domain, listStorage)\n    if (listStorage.isEmpty()) {\n      return null\n    }\n    return listStorage\n  }\n\n  /**\n   * 保存数据到数据库\n   */\n  fun saveDataToKdb(\n    context: Context,\n    apkPkgName: String,\n    autofillFields: AutoFillFieldMetadataCollection\n  ) {\n    if (BaseApp.KDB?.pm == null) {\n      Timber.e(\"数据库为空\")\n      return\n    }\n    val listStorage = ArrayList<PwEntry>()\n    KdbUtil.searchEntriesByPackageName(apkPkgName, listStorage)\n    val entry: PwEntry\n    if (listStorage.isEmpty()) {\n      if (BaseApp.isV4) {\n        entry = PwEntryV4(BaseApp.KDB!!.pm.rootGroup as PwGroupV4)\n        val icon = IconUtil.getAppIcon(context, apkPkgName)\n        if (icon != null) {\n          val baos = ByteArrayOutputStream()\n          icon.compress(PNG, 100, baos)\n          val datas: ByteArray = baos.toByteArray()\n          val customIcon = PwIconCustom(UUID.randomUUID(), datas)\n          entry.customIcon = customIcon\n          (BaseApp.KDB!!.pm as PwDatabaseV4).putCustomIcons(customIcon)\n        }\n        entry.strings[\"KP2A_URL_1\"] = ProtectedString(false, \"androidapp://$apkPkgName\")\n      } else {\n        entry = PwEntryV3()\n        entry.setUrl(\"androidapp://$apkPkgName\", BaseApp.KDB!!.pm)\n      }\n      val appName = getAppName(context, apkPkgName)\n      entry.setTitle(appName ?: \"newEntry\", BaseApp.KDB!!.pm)\n      entry.icon = PwIconStandard(0)\n      KpaUtil.kdbHandlerService.createEntry(entry as PwEntryV4)\n    } else {\n      entry = listStorage[0]\n      Timber.w(\"已存在含有【$apkPkgName】的条目，将更新条目\")\n    }\n\n    for (hint in autofillFields.allAutoFillHints) {\n      val fillFields = autofillFields.getFieldsForHint(hint) ?: continue\n      for (fillField in fillFields) {\n        fillField.autoFillField.textValue ?: continue\n        if (fillField.autoFillType == View.AUTOFILL_TYPE_TEXT) {\n          if (fillField.isPassword) {\n            entry.setPassword(fillField.autoFillField.textValue, BaseApp.KDB!!.pm)\n            Timber.d(\"pass = ${fillField.autoFillField.textValue}\")\n          } else {\n            entry.setUsername(fillField.autoFillField.textValue, BaseApp.KDB!!.pm)\n            Timber.d(\"userName = ${fillField.autoFillField.textValue}\")\n          }\n        }\n      }\n    }\n    KpaUtil.kdbHandlerService.saveDbByBackground()\n    ToastUtils.showLong(ResUtil.getString(R.string.save_db_success))\n    Timber.d(\"密码信息保存成功\")\n  }\n\n  /**\n   * 获取用户名和密码\n   * @return first 用户名\n   */\n  fun getUserInfo(autofillFields: AutoFillFieldMetadataCollection): Pair<String?, String?> {\n    var user: String? = null\n    var pass: String? = null\n    for (hint in autofillFields.allAutoFillHints) {\n      val fillFields = autofillFields.getFieldsForHint(hint) ?: continue\n      for (fillField in fillFields) {\n        fillField.autoFillField.textValue ?: continue\n        if (fillField.autoFillType == View.AUTOFILL_TYPE_TEXT) {\n          if (fillField.isPassword) {\n            pass = fillField.autoFillField.textValue\n          }\n          if (!fillField.isPassword) {\n            user = fillField.autoFillField.textValue\n          }\n        }\n      }\n    }\n    return Pair(user, pass)\n  }\n\n  /**\n   * 获取应用程序名称\n   */\n  fun getAppName(\n    context: Context,\n    apkPkgName: String\n  ): String? {\n    try {\n      val packageManager = context.packageManager\n      return packageManager.getApplicationLabel(\n        packageManager.getApplicationInfo(\n          apkPkgName,\n          PackageManager.GET_META_DATA\n        )\n      )\n        .toString()\n    } catch (e: Exception) {\n      Timber.e(e)\n    }\n    return null\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/service/autofill/model/AutoFillFieldMetadata.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.service.autofill.model\n\nimport android.annotation.TargetApi\nimport android.app.assist.AssistStructure.ViewNode;\nimport android.os.Build\nimport android.service.autofill.SaveInfo\nimport android.view.View\nimport android.view.autofill.AutofillId\nimport com.lyy.keepassa.service.autofill.AutoFillHelper\n\n/**\n * A stripped down version of a [ViewNode] that contains only autofill-relevant metadata. It also\n * contains a `saveType` flag that is calculated based on the [ViewNode]'s autofill hints.\n */\n@TargetApi(Build.VERSION_CODES.O)\nclass AutoFillFieldMetadata(viewNode: ViewNode) {\n\n  var saveType = 0\n    private set\n\n  val autoFillHints = HashSet<String>()\n  val autoFillId: AutofillId? = viewNode.autofillId\n  val autoFillType: Int = viewNode.autofillType\n  val autoFillOptions: Array<CharSequence>? = viewNode.autofillOptions\n  val isFocused: Boolean = viewNode.isFocused\n  var isPassword: Boolean = false\n  val autoFillField = FilledAutoFillField(viewNode)\n\n  /**\n   * 处理自定义的情况，也就是控件没有设置android:autofillHints的情况\n   * @param fileType view的类型 [View.AUTOFILL_HINT_PASSWORD]\n   */\n  constructor(\n    view: ViewNode,\n    fileType: String\n  ) : this(view) {\n    autoFillHints.add(fileType)\n    updateSaveTypeFromHints()\n  }\n\n  /**\n   * 处理控件中已经设置了android:autofillHints的情况\n   */\n  init {\n    viewNode.autofillHints?.filter(AutoFillHelper::isValidHint)\n        ?.forEach {\n          autoFillHints.add(it)\n        }\n    updateSaveTypeFromHints()\n  }\n\n  /**\n   * When the [ViewNode] is a list that the user needs to choose a string from (i.e. a spinner),\n   * this is called to return the index of a specific item in the list.\n   */\n  fun getAutoFillOptionIndex(value: CharSequence): Int {\n    return autoFillOptions?.indexOf(value) ?: -1\n  }\n\n  /**\n   * 更新保存类型，和StructureParser.parseLocked 中的需要关联\n   */\n  private fun updateSaveTypeFromHints() {\n    saveType = 0\n    for (hint in autoFillHints) {\n      when (hint) {\n        View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DATE,\n        View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DAY,\n        View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_MONTH,\n        View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_YEAR,\n        View.AUTOFILL_HINT_CREDIT_CARD_NUMBER,\n        View.AUTOFILL_HINT_CREDIT_CARD_SECURITY_CODE -> {\n          saveType = saveType or SaveInfo.SAVE_DATA_TYPE_CREDIT_CARD\n        }\n        View.AUTOFILL_HINT_EMAIL_ADDRESS -> {\n          saveType = saveType or SaveInfo.SAVE_DATA_TYPE_EMAIL_ADDRESS\n        }\n        View.AUTOFILL_HINT_PHONE, View.AUTOFILL_HINT_NAME -> {\n          saveType = saveType or SaveInfo.SAVE_DATA_TYPE_GENERIC\n        }\n        View.AUTOFILL_HINT_PASSWORD -> {\n          isPassword = true\n          saveType = saveType or SaveInfo.SAVE_DATA_TYPE_PASSWORD\n          saveType = saveType and SaveInfo.SAVE_DATA_TYPE_EMAIL_ADDRESS.inv()\n          saveType = saveType and SaveInfo.SAVE_DATA_TYPE_USERNAME.inv()\n        }\n        View.AUTOFILL_HINT_POSTAL_ADDRESS,\n        View.AUTOFILL_HINT_POSTAL_CODE -> {\n          saveType = saveType or SaveInfo.SAVE_DATA_TYPE_ADDRESS\n        }\n        View.AUTOFILL_HINT_USERNAME -> {\n          saveType = saveType or SaveInfo.SAVE_DATA_TYPE_USERNAME\n        }\n      }\n    }\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/service/autofill/model/AutoFillFieldMetadataCollection.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.service.autofill.model\n\nimport android.view.autofill.AutofillId\nimport timber.log.Timber\n\n/**\n * Data structure that stores a collection of `AutofillFieldMetadata`s. Contains all of the client's `View`\n * hierarchy autoFill-relevant metadata.\n */\ndata class AutoFillFieldMetadataCollection @JvmOverloads constructor(\n  val autoFillIds: HashSet<AutofillId> = HashSet(),\n  val allAutoFillHints: HashSet<String> = HashSet(),\n  val focusedAutoFillHints: HashSet<String> = HashSet()\n) {\n\n  private val TAG = javaClass.simpleName\n\n  /**\n   * used for \"other entry\"\n   */\n  var tempUserFillId: AutofillId? = null\n  var tempPassFillId: AutofillId? = null\n\n  /**\n   * key -> autoHintString\n   * value ->\n   */\n  private val autoFillHintsToFieldsMap = HashMap<String, MutableList<AutoFillFieldMetadata>>()\n  var saveType = 0\n    private set\n\n  fun clear() {\n    tempUserFillId = null\n    tempPassFillId = null\n    autoFillIds.clear()\n    allAutoFillHints.clear()\n    focusedAutoFillHints.clear()\n  }\n\n  fun add(autoFillFieldMetadata: AutoFillFieldMetadata) {\n    if (autoFillFieldMetadata.autoFillId == null) {\n      Timber.w(\"autoFillId == null\")\n      return\n    }\n\n    saveType = saveType or autoFillFieldMetadata.saveType\n    autoFillIds.add(autoFillFieldMetadata.autoFillId)\n    val hintsList = autoFillFieldMetadata.autoFillHints\n    allAutoFillHints.addAll(hintsList)\n    if (autoFillFieldMetadata.isFocused) {\n      focusedAutoFillHints.addAll(hintsList)\n    }\n    autoFillFieldMetadata.autoFillHints.forEach {\n      val fields = autoFillHintsToFieldsMap[it] ?: ArrayList()\n      autoFillHintsToFieldsMap[it] = fields\n      fields.add(autoFillFieldMetadata)\n    }\n  }\n\n  fun getFieldsForHint(hint: String): MutableList<AutoFillFieldMetadata>? {\n    return autoFillHintsToFieldsMap[hint]\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/service/autofill/model/FilledAutoFillField.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.service.autofill.model\n\nimport android.annotation.TargetApi\nimport android.app.assist.AssistStructure\nimport android.os.Build\nimport android.view.autofill.AutofillValue\nimport com.google.gson.annotations.Expose\nimport com.lyy.keepassa.service.autofill.AutoFillHelper\n\n/**\n * JSON serializable data class containing the same data as an [AutofillValue].\n */\n@TargetApi(Build.VERSION_CODES.O)\nclass FilledAutoFillField(viewNode: AssistStructure.ViewNode) {\n  @Expose\n  var textValue: String? = null\n\n  @Expose\n  var dateValue: Long? = null\n\n  @Expose\n  var toggleValue: Boolean? = null\n\n  val autoFillHints = viewNode.autofillHints?.filter(AutoFillHelper::isValidHint)?.toTypedArray()\n\n  init {\n    viewNode.autofillValue?.let {\n      when {\n        it.isList -> {\n          val index = it.listValue\n          viewNode.autofillOptions?.let { autofillOptions ->\n            if (autofillOptions.size > index) {\n              textValue = autofillOptions[index].toString()\n            }\n          }\n        }\n        it.isDate -> {\n          dateValue = it.dateValue\n        }\n        it.isText -> {\n          // Using toString of AutofillValue.getTextValue in order to save it to\n          // SharedPreferences.\n          textValue = it.textValue.toString()\n        }\n        else -> {\n        }\n      }\n    }\n  }\n\n  fun isNull(): Boolean {\n    return textValue == null && dateValue == null && toggleValue == null\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/service/feat/IFeature.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.service.feat\n\nimport android.content.Context\n\ninterface IFeature {\n\n  fun init(context: Context)\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/service/feat/KdbHandlerService.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.service.feat\n\nimport android.content.Context\nimport com.alibaba.android.arouter.facade.annotation.Route\nimport com.alibaba.android.arouter.facade.template.IProvider\nimport com.arialyy.frame.router.Routerfit\nimport com.keepassdroid.database.PwDatabase\nimport com.keepassdroid.database.PwDatabaseV4\nimport com.keepassdroid.database.PwEntryV4\nimport com.keepassdroid.database.PwGroup\nimport com.keepassdroid.database.PwGroupV4\nimport com.keepassdroid.database.PwIconCustom\nimport com.keepassdroid.database.PwIconStandard\nimport com.keepassdroid.database.helper.KDBHandlerHelper\nimport com.lyy.keepassa.base.BaseApp\nimport com.lyy.keepassa.event.CollectionEvent\nimport com.lyy.keepassa.event.CollectionEventType\nimport com.lyy.keepassa.event.CollectionEventType.COLLECTION_STATE_ADD\nimport com.lyy.keepassa.event.CollectionEventType.COLLECTION_STATE_REMOVE\nimport com.lyy.keepassa.event.EntryState.CREATE\nimport com.lyy.keepassa.event.EntryState.DELETE\nimport com.lyy.keepassa.event.EntryState.MODIFY\nimport com.lyy.keepassa.event.EntryState.MOVE\nimport com.lyy.keepassa.event.EntryState.SAVE\nimport com.lyy.keepassa.event.EntryStateChangeEvent\nimport com.lyy.keepassa.event.GroupStateChangeEvent\nimport com.lyy.keepassa.router.DialogRouter\nimport com.lyy.keepassa.util.KdbUtil.isNull\nimport com.lyy.keepassa.util.cloud.DbSynUtil\nimport com.lyy.keepassa.util.setCollection\nimport com.lyy.keepassa.view.dialog.LoadingDialog\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.MainScope\nimport kotlinx.coroutines.delay\nimport kotlinx.coroutines.flow.MutableSharedFlow\nimport kotlinx.coroutines.flow.MutableStateFlow\nimport kotlinx.coroutines.launch\nimport kotlinx.coroutines.sync.Mutex\nimport kotlinx.coroutines.sync.withLock\nimport kotlinx.coroutines.withContext\nimport timber.log.Timber\nimport java.util.concurrent.atomic.AtomicInteger\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 2:03 下午 2022/3/24\n **/\n@Route(path = \"/service/kdbHandler\")\nclass KdbHandlerService : IProvider {\n  companion object {\n    const val MIN_TIME = 200L\n  }\n\n  private var scope = MainScope()\n  private var collectionNum = AtomicInteger(0)\n\n  /**\n   * collection state flow\n   */\n  val collectionStateFlow = MutableStateFlow(CollectionEvent())\n\n  val entryStateChangeFlow = MutableSharedFlow<EntryStateChangeEvent>()\n  val groupStateChangeFlow = MutableStateFlow(GroupStateChangeEvent())\n  private val collectionEntries = hashSetOf<PwEntryV4>()\n  private val mutex = Mutex()\n\n  private val loadingDialog: LoadingDialog by lazy {\n    Routerfit.create(DialogRouter::class.java).getLoadingDialog()\n  }\n\n  private val kdbHelper by lazy {\n    KDBHandlerHelper.getInstance(BaseApp.APP)\n  }\n\n  fun clearDb() {\n    BaseApp.KDB?.clear(BaseApp.APP)\n    BaseApp.KDB = null\n    collectionEntries.clear()\n    collectionNum.set(0)\n  }\n\n  fun getCollectionEntries() = collectionEntries\n  fun getCollectionNum() = collectionNum.get()\n\n  internal fun updateCollectionEntries(collectionEntries: Set<PwEntryV4>) {\n    this.collectionEntries.clear()\n    this.collectionEntries.addAll(collectionEntries)\n  }\n\n  internal fun updateCollectionNum(newCollectionNum: Int) {\n    collectionNum.set(newCollectionNum)\n    scope.launch {\n      collectionStateFlow.emit(\n        CollectionEvent(\n          state = CollectionEventType.COLLECTION_STATE_TOTAL,\n          collectionNum = collectionNum.get()\n        )\n      )\n    }\n  }\n\n  /**\n   * @param collection true: add collection, false: cancel collection\n   */\n  fun collection(pwEntryV4: PwEntryV4, collection: Boolean) {\n    pwEntryV4.setCollection(collection)\n    if (collection) {\n      collectionEntries.add(pwEntryV4)\n    } else {\n      collectionEntries.remove(pwEntryV4)\n    }\n    scope.launch {\n      if (collection) {\n        collectionStateFlow.emit(\n          CollectionEvent(\n            COLLECTION_STATE_ADD,\n            collectionNum.incrementAndGet(),\n            pwEntryV4\n          )\n\n        )\n        return@launch\n      }\n      collectionStateFlow.emit(\n        CollectionEvent(\n          COLLECTION_STATE_REMOVE,\n          collectionNum.decrementAndGet(),\n          pwEntryV4\n        )\n      )\n    }\n  }\n\n  private fun showLoading() {\n    Timber.d(\"showLoading, hashCode = ${loadingDialog.hashCode()}\")\n    scope.launch {\n      if (loadingDialog.isVisible) {\n        return@launch\n      }\n      loadingDialog.show()\n    }\n  }\n\n  private fun dismissLoading(delay: Long = 0) {\n    Timber.d(\"dismissLoading, delay = ${delay}\")\n    scope.launch {\n      loadingDialog.dismiss(delay)\n    }\n  }\n\n  /**\n   * delete group\n   */\n  fun deleteGroup(pwGroup: PwGroupV4, callback: () -> Unit) {\n    scope.launch {\n      val oldParent = pwGroup.parent\n      withContext(Dispatchers.IO) {\n        if (BaseApp.KDB!!.pm.canRecycle(pwGroup)) {\n          (BaseApp.KDB!!.pm as PwDatabaseV4).recycle(pwGroup)\n        } else {\n          kdbHelper.deleteGroup(BaseApp.KDB, pwGroup, true)\n        }\n      }\n      callback.invoke()\n      groupStateChangeFlow.emit(GroupStateChangeEvent(DELETE, pwGroup, oldParent))\n    }\n  }\n\n  /**\n   * only send status\n   */\n  fun updateEntryStatus(v4Entry: PwEntryV4) {\n    scope.launch {\n      v4Entry.touch(true, true)\n      entryStateChangeFlow.emit(\n        EntryStateChangeEvent(\n          MODIFY,\n          v4Entry,\n          v4Entry.parent\n        )\n      )\n    }\n  }\n\n  /**\n   * move entry from other group\n   * @param targetParent target parent\n   */\n  fun moveEntry(v4Entry: PwEntryV4, targetParent: PwGroupV4) {\n    scope.launch {\n      val originParent = v4Entry.parent\n      withContext(Dispatchers.IO) {\n        v4Entry.parent.childEntries.remove(v4Entry)\n\n        if (v4Entry.parent == BaseApp.KDB.pm.recycleBin) {\n          (BaseApp.KDB.pm as PwDatabaseV4).undoRecycle(v4Entry, targetParent)\n        } else {\n          (BaseApp.KDB.pm as PwDatabaseV4).moveEntry(v4Entry, targetParent)\n        }\n      }\n      entryStateChangeFlow.emit(\n        EntryStateChangeEvent(\n          MOVE,\n          v4Entry,\n          originParent\n        )\n      )\n    }\n  }\n\n  /**\n   * delete entry\n   */\n  fun deleteEntry(v4Entry: PwEntryV4, callback: () -> Unit) {\n    scope.launch {\n      val parent = v4Entry.parent\n      withContext(Dispatchers.IO) {\n        kdbHelper.deleteEntry(BaseApp.KDB, v4Entry, true)\n      }\n      callback.invoke()\n      entryStateChangeFlow.emit(EntryStateChangeEvent(DELETE, v4Entry, parent))\n    }\n  }\n\n  /**\n   * update group info and send new state\n   */\n  fun modifyGroup(\n    groupName: String,\n    icon: PwIconStandard,\n    customIcon: PwIconCustom?,\n    self: PwGroupV4,\n    callback: () -> Unit\n  ) {\n    scope.launch {\n      withContext(Dispatchers.IO) {\n        self.customIcon = customIcon\n        self.icon = icon\n        self.name = groupName\n        if (kdbHelper.save(BaseApp.KDB)) {\n          BaseApp.KDB.dirty.add(self.parent)\n        }\n      }\n\n      callback.invoke()\n      groupStateChangeFlow.emit(GroupStateChangeEvent(MODIFY, self))\n    }\n  }\n\n  /**\n   * create new Group\n   *\n   * @param icon default icon\n   * @param customIcon custom icon\n   */\n  fun createGroup(\n    groupName: String,\n    icon: PwIconStandard,\n    customIcon: PwIconCustom?,\n    parent: PwGroupV4,\n    callback: (PwGroupV4) -> Unit\n  ) {\n    scope.launch {\n      val tempGroup = withContext(Dispatchers.IO) {\n        val pm: PwDatabase = BaseApp.KDB.pm\n\n        val group = pm.createGroup() as PwGroupV4\n        group.initNewGroup(groupName, pm.newGroupId())\n        group.icon = icon\n        customIcon?.let { group.customIcon = it }\n        pm.addGroupTo(group, parent)\n\n        return@withContext group\n      }\n      callback.invoke(tempGroup)\n      groupStateChangeFlow.emit(GroupStateChangeEvent(CREATE, tempGroup, null))\n    }\n  }\n\n  /**\n   * add new group\n   */\n  fun createGroup(group: PwGroup) {\n    kdbHelper\n      .createGroup(BaseApp.KDB, group.name, group.icon, group.parent)\n  }\n\n  fun addGroup(group: PwGroupV4) {\n    BaseApp.KDB?.pm?.addGroupTo(group, group.parent)\n  }\n\n  /**\n   * add new entry\n   */\n  fun createEntry(entry: PwEntryV4, parent: PwGroup? = null) {\n    BaseApp.KDB!!.pm.addEntryTo(entry, parent ?: entry.parent)\n    scope.launch {\n      entryStateChangeFlow.emit(EntryStateChangeEvent(CREATE, entry))\n    }\n  }\n\n  /**\n   * only add entry\n   */\n  fun addEntryTo(entry: PwEntryV4, parent: PwGroup) {\n    BaseApp.KDB!!.pm.addEntryTo(entry, parent)\n  }\n\n  suspend fun saveOnly(needShowLoading: Boolean = false, callback: (Int) -> Unit) {\n    mutex.withLock {\n      withContext(Dispatchers.IO) {\n        if (needShowLoading) {\n          showLoading()\n        }\n\n        val b = kdbHelper.save(BaseApp.KDB)\n        if (needShowLoading) {\n          dismissLoading()\n        }\n        withContext(Dispatchers.Main) {\n          callback.invoke(if (b) DbSynUtil.STATE_SUCCEED else DbSynUtil.STATE_SAVE_DB_FAIL)\n          entryStateChangeFlow.emit(EntryStateChangeEvent(SAVE))\n        }\n      }\n    }\n  }\n\n  /**\n   * save db by background\n   * @param uploadDb true: upload db to cloud\n   * @param callback run in main thread\n   */\n  fun saveDbByBackground(uploadDb: Boolean = false, callback: (Int) -> Unit = {}) {\n    Timber.d(\"start save db by background\")\n    if (BaseApp.KDB.isNull()) {\n      Timber.d(\"db is null\")\n      return\n    }\n    if (BaseApp.isLocked) {\n      Timber.d(\"db is locked\")\n      return\n    }\n    scope.launch(Dispatchers.IO) {\n      mutex.withLock {\n        BaseApp.KDB?.let { kdb ->\n          val b = kdbHelper.save(kdb)\n          Timber.d(\"保存后的数据库hash：${kdb.hashCode()}，num = ${kdb.pm.entries.size}\")\n          delay(1000)\n          if (uploadDb) {\n            val response = DbSynUtil.uploadSyn(BaseApp.dbRecord!!, false)\n            Timber.i(response.msg)\n\n            withContext(Dispatchers.Main) {\n              callback.invoke(response.code)\n            }\n            entryStateChangeFlow.emit(EntryStateChangeEvent(SAVE))\n            return@withLock\n          }\n          val code = if (b) DbSynUtil.STATE_SUCCEED else DbSynUtil.STATE_SAVE_DB_FAIL\n          withContext(Dispatchers.Main) {\n            callback.invoke(code)\n          }\n          entryStateChangeFlow.emit(EntryStateChangeEvent(SAVE))\n        }\n\n      }\n    }\n  }\n\n  /**\n   * save db\n   * @param uploadDb true: upload db to cloud\n   * @param isCreate true: is create new db, save that db and upload it.\n   * @param needShowLoading do you need to display the load dialog box?\n   * @param callback run in main thread\n   */\n  fun saveDbByForeground(\n    uploadDb: Boolean = true,\n    isCreate: Boolean = false,\n    needShowLoading: Boolean = true,\n    callback: (Int) -> Unit = {}\n  ) {\n    Timber.d(\"saveDbByForeground\")\n    scope.launch(Dispatchers.Main) {\n      mutex.withLock {\n        Timber.d(\"保存前的数据库hash：${BaseApp.KDB.hashCode()}，num = ${BaseApp.KDB!!.pm.entries.size}\")\n        val b = withContext(Dispatchers.IO) {\n          return@withContext kdbHelper.save(BaseApp.KDB)\n        }\n        Timber.d(\"保存后的数据库hash：${BaseApp.KDB.hashCode()}，num = ${BaseApp.KDB!!.pm.entries.size}\")\n        if (uploadDb) {\n          val startTime = System.currentTimeMillis()\n          if (needShowLoading) {\n            showLoading()\n          }\n          val response = withContext(Dispatchers.IO) {\n            return@withContext DbSynUtil.uploadSyn(BaseApp.dbRecord!!, isCreate)\n          }\n          Timber.i(response.msg)\n          val endTime = System.currentTimeMillis()\n          if (needShowLoading) {\n            dismissLoading(if ((endTime - startTime) < MIN_TIME) MIN_TIME else 0L)\n          }\n          callback.invoke(response.code)\n          entryStateChangeFlow.emit(EntryStateChangeEvent(SAVE))\n          return@launch\n        }\n        val code = if (b) DbSynUtil.STATE_SUCCEED else DbSynUtil.STATE_SAVE_DB_FAIL\n        callback.invoke(code)\n        entryStateChangeFlow.emit(EntryStateChangeEvent(SAVE))\n      }\n    }\n  }\n\n  override fun init(context: Context?) {\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/service/feat/KdbOpenService.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.service.feat\n\nimport android.content.Context\nimport android.net.Uri\nimport android.text.TextUtils\nimport androidx.core.net.toFile\nimport com.alibaba.android.arouter.facade.annotation.Route\nimport com.alibaba.android.arouter.facade.template.IProvider\nimport com.arialyy.frame.router.Routerfit\nimport com.keepassdroid.Database\nimport com.keepassdroid.database.PwDatabase\nimport com.keepassdroid.database.PwDatabaseV4\nimport com.keepassdroid.database.PwEntryV4\nimport com.keepassdroid.database.PwGroupV4\nimport com.keepassdroid.database.PwIconStandard\nimport com.keepassdroid.database.helper.CreateDBHelper\nimport com.keepassdroid.database.helper.KDBHandlerHelper\nimport com.keepassdroid.utils.UriUtil\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseApp\nimport com.lyy.keepassa.entity.DbHistoryRecord\nimport com.lyy.keepassa.router.DialogRouter\nimport com.lyy.keepassa.util.HitUtil\nimport com.lyy.keepassa.util.KeepassAUtil\nimport com.lyy.keepassa.util.KpaUtil\nimport com.lyy.keepassa.util.NotificationUtil\nimport com.lyy.keepassa.util.QuickUnLockUtil\nimport com.lyy.keepassa.util.cloud.DbSynUtil\nimport com.lyy.keepassa.util.cloud.OneDriveUtil\nimport com.lyy.keepassa.util.cloud.WebDavUtil\nimport com.lyy.keepassa.util.isCollectioned\nimport com.lyy.keepassa.view.StorageType\nimport com.lyy.keepassa.view.StorageType.AFS\nimport com.lyy.keepassa.view.StorageType.DROPBOX\nimport com.lyy.keepassa.view.StorageType.ONE_DRIVE\nimport com.lyy.keepassa.view.StorageType.UNKNOWN\nimport com.lyy.keepassa.view.StorageType.WEBDAV\nimport com.lyy.keepassa.view.dialog.LoadingDialog\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.MainScope\nimport kotlinx.coroutines.channels.Channel\nimport kotlinx.coroutines.flow.MutableSharedFlow\nimport kotlinx.coroutines.launch\nimport kotlinx.coroutines.withContext\nimport timber.log.Timber\nimport java.io.File\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 11:25 上午 2022/3/28\n **/\n\n@Route(path = \"/service/kdbOpen\")\nclass KdbOpenService : IProvider {\n  private var scope = MainScope()\n  val openDbFlow = MutableSharedFlow<Database?>(0)\n\n  private val loadingDialog: LoadingDialog by lazy {\n    Routerfit.create(DialogRouter::class.java).getLoadingDialog()\n  }\n\n  private fun showLoading() {\n    scope.launch {\n      if (loadingDialog.isVisible) {\n        return@launch\n      }\n      loadingDialog.show()\n    }\n  }\n\n  private fun dismissLoading() {\n    scope.launch {\n      loadingDialog.dismiss()\n    }\n  }\n\n  /**\n   * 创建数据库时创建默认的群组\n   */\n  private fun createDefaultGroup(context: Context) {\n    val icons = arrayListOf(25, 47, 66, 62, 43)\n    val names = context.resources.getStringArray(R.array.create_normal_group)\n    val pm: PwDatabase = BaseApp.KDB.pm\n    for ((index, name) in names.withIndex()) {\n      val group = pm.createGroup() as PwGroupV4\n      group.initNewGroup(name, pm.newGroupId())\n      group.icon = PwIconStandard(icons[index]) // 需要设置默认图标\n\n      // 处理回收站\n      if (icons[index] == 43) {\n        group.enableAutoType = false\n        group.enableSearching = false\n        group.isExpanded = false\n        (BaseApp.KDB.pm as PwDatabaseV4).recycleBinUUID = group.uuid\n      }\n      pm.addGroupTo(group, pm.rootGroup)\n    }\n  }\n\n  /**\n   * create db\n   * @param keyUri the db key\n   * @param cloudPath cloud path eg: https://dev.jianguo.com\n   * @param storageType [StorageType]\n   */\n  fun createDb(\n    dbName: String,\n    localDbUri: Uri?,\n    dbPass: String,\n    keyUri: Uri?,\n    cloudPath: String?,\n    storageType: StorageType = UNKNOWN\n  ) {\n    val context = BaseApp.APP\n    scope.launch(Dispatchers.IO) {\n      try {\n        showLoading()\n        // 创建db\n        val cdb = CreateDBHelper(context, dbName, localDbUri)\n        if (keyUri != null) {\n          cdb.setKeyFile(keyUri)\n          BaseApp.dbKeyPath = QuickUnLockUtil.encryptStr(keyUri.toString())\n        }\n        cdb.setPass(dbPass, null)\n        val db = cdb.build()\n\n        val success = checkCreatedDb(BaseApp.APP, localDbUri!!, dbPass, keyUri)\n        if (!success){\n          scope.launch {\n            openDbFlow.emit(null)\n          }\n          Timber.e(\"create db fail\")\n          return@launch\n        }\n\n        // 保存打开记录\n        BaseApp.KDB = db\n        BaseApp.dbName = db.pm.name\n        BaseApp.dbFileName = dbName\n        BaseApp.dbPass = QuickUnLockUtil.encryptStr(dbPass)\n        KpaUtil.setEmptyPass(dbPass.isEmpty())\n        KeepassAUtil.instance.subShortPass()\n\n        // 创建默认群组\n        createDefaultGroup(context)\n\n        BaseApp.dbVersion = \"Keepass ${if (PwDatabase.isKDBExtension(dbName)) \"3.x\" else \"4.x\"}\"\n        BaseApp.isV4 = !PwDatabase.isKDBExtension(dbName)\n        val record = DbHistoryRecord(\n          time = System.currentTimeMillis(),\n          type = storageType.name,\n          localDbUri = localDbUri.toString(),\n          cloudDiskPath = cloudPath,\n          keyUri = keyUri?.toString() ?: \"\",\n          dbName = dbName\n        )\n        BaseApp.dbRecord = record\n        BaseApp.isLocked = false\n\n        // 保存并上传数据库到云端\n        KpaUtil.kdbHandlerService.saveDbByForeground(\n          uploadDb = true,\n          isCreate = true,\n          needShowLoading = false\n        ) {\n          NotificationUtil.startDbOpenNotify(context)\n          dismissLoading()\n          scope.launch {\n            openDbFlow.emit(BaseApp.KDB)\n          }\n        }\n      } catch (e: Exception) {\n        HitUtil.toaskOpenDbException(e)\n        scope.launch {\n          openDbFlow.emit(null)\n        }\n        Timber.e(e)\n      }\n    }\n  }\n\n  /**\n   * check db\n   */\n  private fun checkCreatedDb(\n    context: Context,\n    dbUri: Uri,\n    dbPass: String,\n    keyUri: Uri?\n  ): Boolean {\n    return KDBHandlerHelper.getInstance(context)\n      .openDb(dbUri, dbPass, keyUri) != null\n  }\n\n  /**\n   * open database\n   * @param needShowLoading Do you need to display the load dialog box?\n   */\n  fun openDb(\n    context: Context,\n    record: DbHistoryRecord,\n    dbPass: String,\n    needShowLoading: Boolean = true\n  ) {\n    Timber.d(\"打开数据库\")\n    if (needShowLoading) {\n      showLoading()\n    }\n    scope.launch {\n      val db: Database? = withContext(Dispatchers.IO) {\n        var temp: Database? = null\n        try {\n          temp = when (record.getDbPathType()) {\n            AFS -> {\n              openDbFile(context, record.getDbUri(), dbPass, record.getDbKeyUri(), record)\n            }\n            DROPBOX -> {\n              openDropboxDb(context, record, dbPass)\n            }\n            WEBDAV -> {\n              openWebDavDb(context, record, dbPass)\n            }\n            ONE_DRIVE -> {\n              openOneDriveDb(context, record, dbPass)\n            }\n            else -> null\n          }\n        } catch (e: Exception) {\n          HitUtil.toaskOpenDbException(e)\n          Timber.e(e)\n        }\n        temp\n      }\n\n      if (db != null) {\n        BaseApp.isLocked = false\n        BaseApp.KDB = db\n        NotificationUtil.startDbOpenNotify(context)\n        withContext(Dispatchers.IO) {\n          var collectionNum = 0\n          val entrySet = hashSetOf<PwEntryV4>()\n          BaseApp.KDB.pm.entries.forEach {\n            if ((it.value as PwEntryV4).isCollectioned()) {\n              entrySet.add(it.value as PwEntryV4)\n              collectionNum++\n            }\n          }\n          KpaUtil.kdbHandlerService.updateCollectionNum(collectionNum)\n          KpaUtil.kdbHandlerService.updateCollectionEntries(entrySet)\n        }\n      }\n\n      if (needShowLoading) {\n        dismissLoading()\n      }\n      openDbFlow.emit(db)\n    }\n  }\n\n  /**\n   * 打开OneDrive数据库\n   */\n  private suspend fun openOneDriveDb(\n    context: Context,\n    record: DbHistoryRecord,\n    dbPass: String\n  ): Database? {\n    val channel = Channel<Database?>()\n    var db: Database? = null\n\n    OneDriveUtil.initOneDrive {\n      if (!it) {\n        scope.launch {\n          channel.send(null)\n        }\n        return@initOneDrive\n      }\n      OneDriveUtil.loginCallback = object : OneDriveUtil.OnLoginCallback {\n        override fun callback(success: Boolean) {\n          scope.launch {\n            if (!success) {\n              channel.send(null)\n              return@launch\n            }\n            val cacheFile = record.getDbUri()\n              .toFile()\n            val cloudFileInfo = OneDriveUtil.getFileInfo(record.cloudDiskPath!!)\n            if (cacheFile.exists()\n              && cloudFileInfo != null\n              && OneDriveUtil.checkContentHash(cloudFileInfo.contentHash, record.getDbUri())\n            ) {\n              Timber.i(\"文件存在，并且hash一致，将使用本地数据库\")\n              db = openDbFile(context, record.getDbUri(), dbPass, record.getDbKeyUri(), record)\n              channel.send(db)\n              return@launch\n            }\n            val cachePath = DbSynUtil.downloadOnly(context, record, Uri.fromFile(cacheFile))\n            db = if (TextUtils.isEmpty(cachePath)) {\n              null\n            } else {\n              openDbFile(context, record.getDbUri(), dbPass, record.getDbKeyUri(), record)\n            }\n            channel.send(db)\n          }\n        }\n      }\n      OneDriveUtil.loadAccount()\n    }\n    repeat(1) {\n      db = channel.receive()\n    }\n\n    return db\n  }\n\n  /**\n   * 打开坚果云数据\n   */\n  private suspend fun openWebDavDb(\n    context: Context,\n    record: DbHistoryRecord,\n    dbPass: String\n  ): Database? {\n    val dao = BaseApp.appDatabase.cloudServiceInfoDao()\n    val serviceInfo = dao.queryServiceInfo(record.cloudDiskPath!!)\n    if (serviceInfo == null) {\n      HitUtil.toaskShort(context.getString(R.string.invalid_auth))\n      return null\n    }\n    WebDavUtil.login(\n      serviceInfo.cloudPath, QuickUnLockUtil.decryption(serviceInfo.userName),\n      QuickUnLockUtil.decryption(serviceInfo.password)\n    )\n\n    val cacheFile = record.getDbUri()\n      .toFile()\n    val cloudFileInfo = DbSynUtil.getFileInfo(record)\n    if (cacheFile.exists()\n      && (cloudFileInfo == null || DbSynUtil.serviceModifyTime == cloudFileInfo.serviceModifyDate)\n    ) {\n      Timber.i(\"文件存在，并且云端文件时间和本地保存的时间一致，不会重新从云端下载数据库\")\n      return openDbFile(context, record.getDbUri(), dbPass, record.getDbKeyUri(), record)\n    }\n    val cachePath = DbSynUtil.downloadOnly(context, record, Uri.fromFile(cacheFile))\n    return if (TextUtils.isEmpty(cachePath)) {\n      null\n    } else {\n      openDbFile(context, record.getDbUri(), dbPass, record.getDbKeyUri(), record)\n    }\n  }\n\n  /**\n   * 打开dropbox的数据库\n   */\n  private suspend fun openDropboxDb(\n    context: Context,\n    record: DbHistoryRecord,\n    dbPass: String\n  ): Database? {\n    val cacheFile = record.getDbUri()\n      .toFile()\n    if (cacheFile.exists()\n      && DbSynUtil.serviceModifyTime == DbSynUtil.getFileServiceModifyTime(record)\n    ) {\n      Timber.i(\"文件存在，并且云端文件时间和本地保存的时间一致，不会重新从云端下载数据库\")\n      return openDbFile(context, record.getDbUri(), dbPass, record.getDbKeyUri(), record)\n    }\n    val cachePath = DbSynUtil.downloadOnly(context, record, Uri.fromFile(cacheFile))\n    return if (TextUtils.isEmpty(cachePath)) {\n      null\n    } else {\n      openDbFile(context, record.getDbUri(), dbPass, record.getDbKeyUri(), record)\n    }\n  }\n\n  /**\n   * 打开数据库文件\n   * @param dbUri 如果是AFS，dbUri表示本地文件的Uri；如果是云端文件，表示的是云端文件的路径\n   */\n  private suspend fun openDbFile(\n    context: Context,\n    dbUri: Uri,\n    dbPass: String,\n    keyUri: Uri?,\n    record: DbHistoryRecord\n  ): Database? {\n    try {\n      val db = KDBHandlerHelper.getInstance(context)\n        .openDb(dbUri, dbPass, keyUri)\n      if (db != null) {\n        val dbName = UriUtil.getFileNameFromUri(context, dbUri)\n        KpaUtil.setEmptyPass(dbPass.isEmpty())\n        BaseApp.dbPass = QuickUnLockUtil.encryptStr(dbPass)\n        KeepassAUtil.instance.subShortPass()\n        if (keyUri != null) {\n          BaseApp.dbKeyPath = QuickUnLockUtil.encryptStr(keyUri.toString())\n        } else {\n          BaseApp.dbKeyPath = null\n        }\n        //              BaseApp.KDB?.clear(context)\n        // 保存打开记录\n        BaseApp.KDB = db\n        BaseApp.dbName = db.pm.name\n        BaseApp.dbFileName = dbName\n\n        BaseApp.dbVersion = \"Keepass ${if (PwDatabase.isKDBExtension(dbName)) \"3.x\" else \"4.x\"}\"\n        BaseApp.isV4 = !PwDatabase.isKDBExtension(dbName)\n        BaseApp.dbRecord = record\n        KeepassAUtil.instance.saveLastOpenDbHistory(record)\n\n        if (!BaseApp.isAFS()) {\n          DbSynUtil.updateServiceModifyTime(record)\n        }\n      }\n      return db\n    } catch (e: Exception) {\n      HitUtil.toaskOpenDbException(e)\n      Timber.e(e)\n    }\n    return null\n  }\n\n  override fun init(context: Context?) {\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/service/feat/KpaSdkService.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.service.feat\n\nimport android.app.Activity\nimport android.app.Application\nimport android.content.Context\nimport androidx.preference.PreferenceManager\nimport com.alibaba.android.arouter.facade.annotation.Route\nimport com.alibaba.android.arouter.facade.template.IProvider\nimport com.alibaba.android.arouter.launcher.ARouter\nimport com.arialyy.frame.util.KeyStoreUtil.Companion.keyStorePass\nimport com.arialyy.frame.util.ResUtil\nimport com.blankj.utilcode.util.AppUtils\nimport com.blankj.utilcode.util.Utils\nimport com.lyy.keepassa.KpaEventBusIndex\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseActivity\nimport com.lyy.keepassa.base.BaseApp\nimport com.lyy.keepassa.util.KeepassAUtil\nimport com.lyy.keepassa.util.KpaUtil\nimport com.lyy.keepassa.util.QuickUnLockUtil\nimport com.lyy.keepassa.view.create.entry.CreateEntryActivity\nimport com.tencent.mmkv.MMKV\nimport com.zzhoujay.richtext.RichText\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.MainScope\nimport kotlinx.coroutines.launch\nimport org.greenrobot.eventbus.EventBus\nimport timber.log.Timber\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 3:24 下午 2022/4/15\n **/\n@Route(path = \"/service/kpaSdk\")\nclass KpaSdkService : IProvider {\n  private val scope = MainScope()\n\n  fun preInitSdk(context: Application) {\n    MMKV.initialize(context)\n    Utils.init(context)\n    RoomFeature.init(context)\n    // 开启kotlin 协程debug\n    if (AppUtils.isAppDebug()) {\n      System.setProperty(\"kotlinx.coroutines.debug\", \"on\")\n      Timber.plant(Timber.DebugTree())\n      ARouter.openLog() // 打印日志\n      ARouter.openDebug() // 开启调试模式(如果在InstantRun模式下运行，必须开启调试模式！线上版本需要关闭,否则有安全风险)\n    }\n    scope.launch(Dispatchers.IO) {\n      // 初始化一下时间\n      KeepassAUtil.instance.isFastClick()\n      keyStorePass = QuickUnLockUtil.getDbPass().toCharArray()\n      val showStatusBar = PreferenceManager.getDefaultSharedPreferences(BaseApp.APP)\n        .getBoolean(ResUtil.getString(R.string.set_key_title_show_state_bar), true)\n      BaseActivity.showStatusBar = showStatusBar\n      EventBus.builder().addIndex(KpaEventBusIndex()).installDefaultEventBus()\n      listenerAppBackground()\n    }\n  }\n\n  fun initThirdSdk(context: Context) {\n    scope.launch(Dispatchers.IO) {\n      RichText.initCacheDir(context)\n      XLogFeature.init(context)\n    }\n  }\n\n  private fun listenerAppBackground() {\n    AppUtils.registerAppStatusChangedListener(object : Utils.OnAppStatusChangedListener {\n      override fun onForeground(activity: Activity?) {\n      }\n\n      override fun onBackground(activity: Activity) {\n        if (activity::class.java.name == CreateEntryActivity::class.java.name){\n          Timber.w(\"in CreateEntryActivity, not save\")\n          return\n        }\n        KpaUtil.kdbHandlerService.saveDbByBackground(true)\n        XLogFeature.flush()\n      }\n    })\n  }\n\n  override fun init(context: Context?) {\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/service/feat/RoomFeature.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.service.feat\n\nimport android.content.Context\nimport androidx.room.Room\nimport com.lyy.keepassa.base.BaseApp\nimport com.lyy.keepassa.base.DbMigration\nimport com.lyy.keepassa.dao.AppDatabase\nimport com.lyy.keepassa.util.QuickUnLockUtil\nimport com.tencent.wcdb.database.SQLiteCipherSpec\nimport com.tencent.wcdb.room.db.WCDBOpenHelperFactory\n\nobject RoomFeature :IFeature{\n  override fun init(context: Context) {\n    // 初始化数据库\n    val cipherSpec = SQLiteCipherSpec() // 指定加密方式，使用默认加密可以省略\n      .setPageSize(4096)\n      .setKDFIteration(64000)\n    val factory = WCDBOpenHelperFactory()\n      .passphrase(QuickUnLockUtil.getDbPass().toByteArray()) // 指定加密DB密钥，非加密DB去掉此行\n      .cipherSpec(cipherSpec) // 指定加密方式，使用默认加密可以省略\n      .writeAheadLoggingEnabled(true) // 打开WAL以及读写并发，可以省略让Room决定是否要打开\n      .asyncCheckpointEnabled(true) // 打开异步Checkpoint优化，不需要可以省略\n    BaseApp.appDatabase = Room.databaseBuilder(\n      context,\n      AppDatabase::class.java, AppDatabase.DB_NAME\n    )\n      .openHelperFactory(factory)\n      .addMigrations(DbMigration.MIGRATION_2_3(), DbMigration.MIGRATION_3_4())\n      .build()\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/service/feat/XLogFeature.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.service.feat\n\nimport android.content.Context\nimport com.blankj.utilcode.util.Utils\nimport com.lyy.keepassa.BuildConfig\nimport com.tencent.mars.xlog.Log\nimport com.tencent.mars.xlog.Xlog\nimport com.tencent.mars.xlog.Xlog.XLogConfig\nimport org.joda.time.DateTime\nimport timber.log.Timber\nimport timber.log.Timber.DebugTree\n\nobject XLogFeature : IFeature {\n\n  init {\n    System.loadLibrary(\"c++_shared\")\n    System.loadLibrary(\"marsxlog\")\n  }\n\n  private val rootDir: String = Utils.getApp().filesDir.absolutePath\n  private val logDir = \"$rootDir/marssample/log\"\n  private val cacheDir = \"$rootDir/marssample/cache\"\n  private val logName = \"kpa\"\n\n  override fun init(context: Context) {\n    val xlog = Xlog()\n    Log.setLogImp(xlog)\n    Log.setConsoleLogOpen(false)\n    Log.appenderOpen(Xlog.LEVEL_VERBOSE, Xlog.AppednerModeAsync, cacheDir, logDir, logName, 0)\n    setTimberPlant()\n  }\n\n  fun flush() {\n    Timber.d(\"写日志到xlog中\")\n    Log.appenderFlush()\n  }\n\n  fun getLogName(): String {\n    val data = DateTime(System.currentTimeMillis())\n    return \"${logName}_${data.toString(\"yyyyMMdd\")}.xlog\"\n  }\n\n  fun getLogPath(): String {\n    flush()\n    return \"${logDir}/${getLogName()}\"\n  }\n\n  private fun setTimberPlant() {\n    Timber.plant(object : DebugTree() {\n      override fun log(priority: Int, tag: String?, message: String, t: Throwable?) {\n        if (BuildConfig.DEBUG) {\n          return\n        }\n        when (priority) {\n          android.util.Log.DEBUG -> {\n            Log.d(tag, message)\n          }\n          android.util.Log.VERBOSE -> {\n            Log.v(tag, message)\n          }\n          android.util.Log.WARN -> {\n            Log.w(tag, message)\n          }\n          android.util.Log.INFO -> {\n            Log.i(tag, message)\n          }\n          android.util.Log.ERROR -> {\n            Log.e(tag, message)\n          }\n          android.util.Log.ASSERT -> {\n            Log.f(tag, message)\n          }\n        }\n      }\n    })\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/service/input/CandidatesAdapter.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.service.input\n\nimport android.content.Context\nimport android.view.View\nimport android.widget.ImageView\nimport android.widget.TextView\nimport com.arialyy.frame.util.adapter.AbsHolder\nimport com.arialyy.frame.util.adapter.AbsRVAdapter\nimport com.keepassdroid.database.PwEntryV4\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.entity.SimpleItemEntity\nimport com.lyy.keepassa.service.input.CandidatesAdapter.Holder\nimport com.lyy.keepassa.util.IconUtil\n\n/**\n * 候选条目适配器\n */\nclass CandidatesAdapter(\n  context: Context,\n  data: List<SimpleItemEntity>\n) : AbsRVAdapter<SimpleItemEntity, Holder>(context, data) {\n\n  override fun getViewHolder(\n    convertView: View,\n    viewType: Int\n  ): Holder {\n    return Holder(convertView)\n  }\n\n  override fun setLayoutId(type: Int): Int {\n    return R.layout.item_ime_entry\n  }\n\n  override fun bindData(\n    holder: Holder,\n    position: Int,\n    item: SimpleItemEntity\n  ) {\n    val pwEntryV4 = item.obj as PwEntryV4\n    IconUtil.setEntryIcon(pwEntryV4, holder.icon)\n    holder.text.text = pwEntryV4.title\n    holder.itemView.isSelected = item.isSelected\n  }\n\n  class Holder(view: View) : AbsHolder(view) {\n    val icon: ImageView = view.findViewById(R.id.icon)\n    val text: TextView = view.findViewById(R.id.text)\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/service/input/EntryOtherInfoAdapter.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.service.input\n\nimport android.content.Context\nimport android.text.method.HideReturnsTransformationMethod\nimport android.text.method.PasswordTransformationMethod\nimport android.view.View\nimport android.widget.ImageView\nimport android.widget.TextView\nimport com.arialyy.frame.util.adapter.AbsHolder\nimport com.arialyy.frame.util.adapter.AbsRVAdapter\nimport com.keepassdroid.database.security.ProtectedString\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.entity.SimpleItemEntity\nimport com.lyy.keepassa.service.input.EntryOtherInfoAdapter.Holder\n\n/**\n * @Author laoyuyu\n * @Description 填充其它信息适配器\n * @Date 2020/10/25\n **/\nclass EntryOtherInfoAdapter(\n  context: Context,\n  data: List<SimpleItemEntity>\n) : AbsRVAdapter<SimpleItemEntity, Holder>(context, data) {\n\n  class Holder(v: View) : AbsHolder(v) {\n    val tvHint: TextView = v.findViewById(R.id.tvHint)\n    val tvContent: TextView = v.findViewById(R.id.tvContent)\n    val ivIcon: ImageView = v.findViewById(R.id.ivIcon)\n  }\n\n  override fun getViewHolder(\n    convertView: View?,\n    viewType: Int\n  ): Holder {\n    return Holder(convertView!!)\n  }\n\n  override fun setLayoutId(type: Int): Int {\n    return R.layout.item_entry_other_info\n  }\n\n  override fun bindData(\n    holder: Holder,\n    position: Int,\n    item: SimpleItemEntity\n  ) {\n    holder.tvHint.text = item.title\n    holder.tvContent.text = item.content\n    if (item.isProtected) {\n      holder.ivIcon.visibility = View.VISIBLE\n      holder.ivIcon.isSelected = !item.isSelected\n      // 显示密码\n      if (item.isSelected) {\n        holder.tvContent.transformationMethod = PasswordTransformationMethod.getInstance()\n      } else {\n        holder.tvContent.transformationMethod = HideReturnsTransformationMethod.getInstance()\n      }\n      holder.ivIcon.setOnClickListener {\n        item.isSelected = !item.isSelected\n        holder.ivIcon.isSelected = item.isSelected\n        notifyItemChanged(position)\n      }\n      return\n    }\n    holder.ivIcon.visibility = View.GONE\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/service/input/EntryOtherInfoDialog.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.service.input\n\nimport android.os.Bundle\nimport androidx.recyclerview.widget.LinearLayoutManager\nimport com.arialyy.frame.util.adapter.RvItemClickSupport\nimport com.keepassdroid.database.PwEntryV4\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseActivity\nimport com.lyy.keepassa.base.BaseApp\nimport com.lyy.keepassa.databinding.DialogOtherInfoBinding\nimport com.lyy.keepassa.entity.SimpleItemEntity\nimport com.lyy.keepassa.event.FillInfoEvent\nimport com.lyy.keepassa.util.HitUtil\nimport org.greenrobot.eventbus.EventBus\nimport java.util.UUID\n\n/**\n * @Author laoyuyu\n * @Description 其它信息\n * @Date 2020/10/25\n **/\nclass EntryOtherInfoDialog : BaseActivity<DialogOtherInfoBinding>() {\n  companion object {\n    val KEY_DATA = \"KEY_DATA\"\n  }\n\n  private var entryUUID: UUID? = null\n  private val moreInfoList = arrayListOf<SimpleItemEntity>()\n  private val moreInfoAdapter by lazy {\n    EntryOtherInfoAdapter(this, moreInfoList)\n  }\n  private lateinit var pwEntry: PwEntryV4\n\n  override fun setLayoutId(): Int {\n    return R.layout.dialog_other_info\n  }\n\n  override fun initData(savedInstanceState: Bundle?) {\n    super.initData(savedInstanceState)\n    entryUUID = intent.getSerializableExtra(KEY_DATA) as UUID?\n    if (entryUUID == null) {\n      HitUtil.toaskShort(getString(R.string.hint_not_nore_info))\n      finish()\n      return\n    }\n    pwEntry = BaseApp.KDB!!.pm.entries[entryUUID] as PwEntryV4\n    binding.rvList.adapter = moreInfoAdapter\n    binding.rvList.setHasFixedSize(true)\n    binding.rvList.layoutManager = LinearLayoutManager(this)\n    RvItemClickSupport.addTo(binding.rvList)\n        .setOnItemClickListener { _, position, _ ->\n          EventBus.getDefault()\n              .post(FillInfoEvent(moreInfoList[position].content))\n          finish()\n        }\n    setEntry(pwEntry)\n  }\n\n  private fun setEntry(pwEntry: PwEntryV4?) {\n    if (pwEntry == null) {\n      HitUtil.toaskShort(getString(R.string.hint_not_nore_info))\n      finish()\n      return\n    }\n    val list = getStrList(pwEntry)\n    if (list.isNullOrEmpty()) {\n      HitUtil.toaskShort(getString(R.string.hint_not_nore_info))\n      finish()\n      return\n    }\n    moreInfoList.clear()\n    moreInfoList.addAll(list)\n    moreInfoAdapter.notifyDataSetChanged()\n  }\n\n  private fun getStrList(pwEntry: PwEntryV4): ArrayList<SimpleItemEntity> {\n    val list = arrayListOf<SimpleItemEntity>()\n    if (!pwEntry.url.isNullOrEmpty()) {\n      val url = SimpleItemEntity()\n      url.title = getString(R.string.url)\n      url.content = pwEntry.url\n      url.isProtected = false\n      list.add(url)\n    }\n\n    if (!pwEntry.notes.isNullOrEmpty()) {\n      val notes = SimpleItemEntity()\n      notes.title = getString(R.string.notice)\n      notes.content = pwEntry.notes\n      notes.isProtected = false\n      list.add(notes)\n    }\n\n    if (pwEntry.strings.isNotEmpty()) {\n      pwEntry.strings.forEach {\n        if (it.value.toString().isEmpty()\n            || it.key.equals(PwEntryV4.STR_TITLE, true)\n            || it.key.equals(PwEntryV4.STR_USERNAME, true)\n            || it.key.equals(PwEntryV4.STR_PASSWORD, true)){\n          return@forEach\n        }\n        val item = SimpleItemEntity()\n        item.title = it.key\n        item.content = it.value.toString()\n        item.isSelected = it.value.isProtected\n        item.isProtected = it.value.isProtected\n        list.add(item)\n      }\n    }\n    return list\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/service/input/InputIMEService.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.service.input\n\nimport android.content.Context\nimport android.content.Intent\nimport android.inputmethodservice.InputMethodService\nimport android.os.Build\nimport android.os.Bundle\nimport android.os.IBinder\nimport android.view.LayoutInflater\nimport android.view.View\nimport android.view.ViewGroup\nimport android.view.autofill.AutofillManager\nimport android.view.inputmethod.EditorInfo\nimport android.view.inputmethod.InlineSuggestionsRequest\nimport android.view.inputmethod.InlineSuggestionsResponse\nimport android.view.inputmethod.InputConnection\nimport android.view.inputmethod.InputMethodManager\nimport android.widget.ImageView\nimport android.widget.TextView\nimport androidx.appcompat.widget.AppCompatImageView\nimport androidx.recyclerview.widget.LinearLayoutManager\nimport androidx.recyclerview.widget.RecyclerView\nimport com.arialyy.frame.router.Routerfit\nimport com.arialyy.frame.util.ResUtil\nimport com.arialyy.frame.util.adapter.RvItemClickSupport\nimport com.keepassdroid.database.PwEntry\nimport com.keepassdroid.database.PwEntryV4\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseApp\nimport com.lyy.keepassa.entity.SimpleItemEntity\nimport com.lyy.keepassa.event.FillInfoEvent\nimport com.lyy.keepassa.router.ActivityRouter\nimport com.lyy.keepassa.router.ServiceRouter\nimport com.lyy.keepassa.service.autofill.W3cHints\nimport com.lyy.keepassa.util.EventBusHelper\nimport com.lyy.keepassa.util.HitUtil\nimport com.lyy.keepassa.util.KdbUtil\nimport com.lyy.keepassa.util.LanguageUtil\nimport com.lyy.keepassa.util.NotificationUtil\nimport com.lyy.keepassa.util.totp.OtpUtil\nimport com.lyy.keepassa.util.isCanOpenQuickLock\nimport com.lyy.keepassa.view.launcher.LauncherActivity\nimport com.lyy.keepassa.view.main.QuickUnlockActivity\nimport com.lyy.keepassa.view.search.CommonSearchActivity\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.MainScope\nimport kotlinx.coroutines.cancel\nimport kotlinx.coroutines.delay\nimport kotlinx.coroutines.flow.collectLatest\nimport kotlinx.coroutines.launch\nimport kotlinx.coroutines.withContext\nimport org.greenrobot.eventbus.Subscribe\nimport org.greenrobot.eventbus.ThreadMode.MAIN\nimport timber.log.Timber\n\n/**\n * 输入法\n * https://developer.android.com/guide/topics/text/creating-input-method?hl=zh-cn\n */\nclass InputIMEService : InputMethodService(), View.OnClickListener {\n\n  private var appPkgName: String? = \"\"\n  private var ic: InputConnection? = null\n  private var curEntry: PwEntry? = null\n  private lateinit var candidatesList: RecyclerView\n  private val candidatesData = arrayListOf<SimpleItemEntity>()\n  private lateinit var candidatesAdapter: CandidatesAdapter\n  private var imeOption = EditorInfo.IME_ACTION_GO\n  private var curImeView: View? = null\n  private var scope = MainScope()\n\n  /**\n   * 当 IME 首次显示时，系统会调用 onCreateInputView() 回调。在此方法的实现中，您可以创建要在 IME 窗口中显示的布局，并将布局返回系统。\n   */\n  override fun onCreateInputView(): View {\n\n    val layout = LayoutInflater.from(this)\n      .inflate(R.layout.layout_kpa_ime, null) as ViewGroup\n    candidatesList = layout.findViewById(R.id.rvContent)\n    for (i in 0..layout.childCount) {\n      val child = layout.getChildAt(i)\n      if (child != null\n        && (child is ImageView || child is TextView)\n        && child.isClickable\n      ) {\n        child.setOnClickListener(this)\n      }\n    }\n    curImeView = layout\n    initCandidatesLayout()\n\n    layout.findViewById<AppCompatImageView>(R.id.ivSearch).setOnClickListener {\n      Routerfit.create(ActivityRouter::class.java).toCommonSearch()\n    }\n    scope = MainScope()\n    scope.launch {\n      CommonSearchActivity.searchFlow.collectLatest {\n        curEntry = it\n        showEntryList(arrayListOf<PwEntry>().apply { add(it) })\n      }\n    }\n\n    return layout\n  }\n\n  private fun initCandidatesLayout() {\n    candidatesAdapter = CandidatesAdapter(this, candidatesData)\n    candidatesList.layoutManager = LinearLayoutManager(this, RecyclerView.HORIZONTAL, false)\n    candidatesList.setHasFixedSize(true)\n    candidatesList.adapter = candidatesAdapter\n    RvItemClickSupport.addTo(candidatesList)\n      .setOnItemClickListener(object : RvItemClickSupport.OnItemClickListener {\n        var lastPosition = 0\n        override fun onItemClicked(\n          recyclerView: RecyclerView?,\n          position: Int,\n          v: View?\n        ) {\n          Timber.d(\"select item, position = $position\")\n          val lastItemEntity = candidatesData[lastPosition]\n          val curItemEntity = candidatesData[position]\n          lastItemEntity.isSelected = false\n          curItemEntity.isSelected = true\n          candidatesAdapter.notifyItemChanged(lastPosition)\n          candidatesAdapter.notifyItemChanged(position)\n          lastPosition = position\n          curEntry = curItemEntity.obj as PwEntry\n        }\n      })\n  }\n\n  /**\n   * 输入法被唤起，开始输入\n   */\n  override fun onStartInputView(\n    info: EditorInfo?,\n    restarting: Boolean\n  ) {\n    super.onStartInputView(info, restarting)\n    EventBusHelper.reg(this)\n    imeOption = info?.imeOptions ?: EditorInfo.IME_ACTION_GO\n    candidatesData.clear()\n    candidatesAdapter.notifyDataSetChanged()\n    candidatesList.visibility = View.GONE\n    curEntry = null\n    ic = currentInputConnection\n    Timber.d(\"pkgName = ${info?.packageName}, inputType = ${info?.inputType}, fieldName = ${info?.fieldName}, fieldId = ${info?.fieldId}\")\n    appPkgName = info?.packageName\n\n    if (W3cHints.isBrowser(appPkgName) && !checkCanOpenAutoFill()) {\n      if (curImeView == null) {\n        HitUtil.toaskLong(ResUtil.getString(R.string.ime_hint_open_auto_fill))\n        return\n      }\n\n      HitUtil.snackLong(\n        curImeView!!,\n        ResUtil.getString(R.string.ime_hint_open_auto_fill),\n        ResUtil.getString(R.string.setting)\n      ) {\n        Routerfit.create(ActivityRouter::class.java, this).toAppSetting(\n          scrollKey = getString(R.string.set_open_auto_fill)\n        )\n      }\n      return\n    }\n\n    showEntryList(searchEntry(appPkgName))\n  }\n\n  private fun checkCanOpenAutoFill(): Boolean {\n    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {\n      Timber.w(\"the sdk version ${Build.VERSION.SDK_INT} less than O\")\n      return false\n    }\n    val am = getSystemService(AutofillManager::class.java)\n    if (!am.isAutofillSupported) {\n      Timber.w(\"it not support autofill\")\n      return false\n    }\n\n    if (!am.hasEnabledAutofillServices()) {\n      Timber.w(\"The auto-fill service is not turned on\")\n      return false\n    }\n\n    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P\n      && (am.autofillServiceComponentName?.packageName?.equals(packageName) == false)\n    ) {\n      Timber.w(\"The auto-fill service is not turned on\")\n      return false\n    }\n    return true\n  }\n\n  override fun onCreateInlineSuggestionsRequest(uiExtras: Bundle): InlineSuggestionsRequest? {\n    Timber.d(\"onCreateInlineSuggestionsRequest\")\n    return super.onCreateInlineSuggestionsRequest(uiExtras)\n  }\n\n  override fun onInlineSuggestionsResponse(response: InlineSuggestionsResponse): Boolean {\n    Timber.d(\"onInlineSuggestionsResponse\")\n    return super.onInlineSuggestionsResponse(response)\n  }\n\n  /**\n   * 填充数据，如果有多个条目，启动对话框，让用户选择特定的条目\n   */\n  override fun onClick(v: View) {\n    when (v.id) {\n      // 锁定\n      R.id.btLock -> {\n        if (BaseApp.KDB == null || BaseApp.isLocked) {\n          return\n        }\n        if (appPkgName == packageName) {\n          LauncherActivity.startLauncherActivity(this, Intent.FLAG_ACTIVITY_NEW_TASK)\n        }\n        BaseApp.isLocked = true\n        NotificationUtil.startDbLocked(this)\n        if (BaseApp.APP.isCanOpenQuickLock()) {\n          return\n        }\n        curEntry = null\n        candidatesData.clear()\n        Routerfit.create(ServiceRouter::class.java).getDbSaveService().clearDb()\n        Timber.d(\"数据库已锁定\")\n        HitUtil.toaskShort(getString(R.string.notify_db_locked))\n        return\n      }\n\n      // 用户名\n      R.id.btAccount -> {\n        if (!dbIsOpen()) {\n          return\n        }\n        showEntryList(searchEntry(appPkgName))\n        curEntry?.let {\n          val userName = KdbUtil.getUserName(it)\n          Timber.d(\"fill user name: $userName\")\n          fillData(userName)\n          return\n        }\n      }\n\n      // 密码\n      R.id.btPass -> {\n        if (!dbIsOpen()) {\n          return\n        }\n        showEntryList(searchEntry(appPkgName))\n        curEntry?.let {\n          val pass = KdbUtil.getPassword(it)\n          Timber.d(\"fill password: $pass\")\n          fillData(pass)\n          return\n        }\n      }\n\n      // 关键软键盘\n      R.id.btClose -> {\n        requestHideSelf(InputMethodManager.HIDE_NOT_ALWAYS)\n      }\n\n      // 选择输入法\n      R.id.btChangeIme -> {\n        val imm = getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager\n        imm.showInputMethodPicker()\n      }\n\n      // totp\n      R.id.btTotp -> {\n        if (!dbIsOpen()) {\n          return\n        }\n        showEntryList(searchEntry(appPkgName))\n        if (curEntry == null) {\n          return\n        }\n        val totp = OtpUtil.getOtpPass(curEntry as PwEntryV4)\n        if (totp.second.isNullOrEmpty()) {\n          HitUtil.toaskShort(getString(R.string.no_totp_token))\n          return\n        } else {\n          fillData(totp.second!!)\n        }\n      }\n\n      // 其它信息\n      R.id.btOtherInfo -> {\n        if (!dbIsOpen()) {\n          return\n        }\n        showEntryList(searchEntry(appPkgName))\n\n        showMoreInfoDialog()\n      }\n\n      // 回退键\n      R.id.btBackspace -> {\n        ic?.deleteSurroundingText(1, 0)\n      }\n\n      // 回车键\n      R.id.btEnter -> {\n        ic?.performEditorAction(imeOption)\n      }\n    }\n  }\n\n  /**\n   * 显示更多信息的对话框，点击item自动填充\n   */\n  private fun showMoreInfoDialog() {\n    startActivity(Intent(this, EntryOtherInfoDialog::class.java).apply {\n      putExtra(EntryOtherInfoDialog.KEY_DATA, curEntry?.uuid)\n      flags = Intent.FLAG_ACTIVITY_NEW_TASK\n    })\n  }\n\n  @Subscribe(threadMode = MAIN)\n  fun onFillOtherInfo(event: FillInfoEvent) {\n    Timber.d(\"getOtherInfo, info = ${event.infoStr}\")\n    MainScope().launch {\n      withContext(Dispatchers.IO) {\n        delay(600)\n      }\n      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {\n        requestShowSelf(InputMethodManager.SHOW_IMPLICIT)\n      } else {\n        try {\n          val inm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager\n          val field = InputMethodService::class.java.getDeclaredField(\"mToken\")\n          field.isAccessible = true\n          inm.showSoftInputFromInputMethod(\n            field.get(this@InputIMEService) as IBinder,\n            InputMethodManager.SHOW_IMPLICIT\n          )\n        } catch (e: Exception) {\n          Timber.e(e)\n        }\n      }\n\n      withContext(Dispatchers.IO) {\n        delay(600)\n      }\n\n      fillData(event.infoStr.toString())\n    }\n  }\n\n  override fun onDestroy() {\n    super.onDestroy()\n    EventBusHelper.unReg(this)\n    scope.cancel()\n  }\n\n  /**\n   * 如果有多个条目，显示条目列表\n   */\n  private fun showEntryList(entries: List<PwEntry>) {\n    if (entries.isNullOrEmpty()) {\n      candidatesList.visibility = View.GONE\n      return\n    }\n    if (entries.size == 1) {\n      candidatesList.visibility = View.GONE\n      curEntry = entries[0]\n      return\n    }\n    candidatesData.clear()\n    candidatesList.visibility = View.VISIBLE\n    entries.forEachIndexed { index, pwEntry ->\n      val item = SimpleItemEntity()\n      item.title = pwEntry.title\n      item.obj = pwEntry\n      if (index == 0) {\n        item.isSelected = true\n        curEntry = pwEntry\n      }\n      candidatesData.add(item)\n    }\n    candidatesAdapter.notifyDataSetChanged()\n  }\n\n  /**\n   * 填充数据\n   */\n  private fun fillData(text: String) {\n    ic?.commitText(text, 0)\n  }\n\n  /**\n   * 判断数据库是否打开，没有打开，启动登陆界面，如果是快速锁定，打开快速解锁界面\n   */\n  private fun dbIsOpen(): Boolean {\n    if (BaseApp.KDB == null || BaseApp.isLocked) {\n      if (BaseApp.KDB == null) {\n        LauncherActivity.startLauncherActivity(this, Intent.FLAG_ACTIVITY_NEW_TASK)\n        return false\n      }\n\n\n      if (BaseApp.APP.isCanOpenQuickLock()) {\n        QuickUnlockActivity.startQuickUnlockActivity(this, Intent.FLAG_ACTIVITY_NEW_TASK)\n      }\n      return false\n    }\n\n    return true\n  }\n\n  /**\n   * 搜索条目\n   */\n  private fun searchEntry(pkgName: String?): List<PwEntry> {\n    if (pkgName.isNullOrEmpty() || BaseApp.KDB == null) {\n      return emptyList()\n    }\n    val listStorage = ArrayList<PwEntry>()\n    if (W3cHints.isBrowser(pkgName)) {\n      Timber.d(\"curDomain = ${W3cHints.curDomainUrl}\")\n      KdbUtil.searchEntriesByDomain(W3cHints.curDomainUrl, listStorage)\n      return listStorage\n    }\n\n    KdbUtil.searchEntriesByPackageName(pkgName, listStorage)\n    return listStorage\n  }\n\n  override fun attachBaseContext(newBase: Context?) {\n    super.attachBaseContext(LanguageUtil.setLanguage(newBase!!, BaseApp.currentLang))\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/service/play/PlayServiceUtil.kt",
    "content": "package com.lyy.keepassa.service.play\n\nimport android.app.Activity\nimport com.android.billingclient.api.AcknowledgePurchaseParams\nimport com.android.billingclient.api.BillingClient\nimport com.android.billingclient.api.BillingClient.BillingResponseCode\nimport com.android.billingclient.api.BillingClient.SkuType\nimport com.android.billingclient.api.BillingClientStateListener\nimport com.android.billingclient.api.BillingFlowParams\nimport com.android.billingclient.api.BillingResult\nimport com.android.billingclient.api.Purchase\nimport com.android.billingclient.api.Purchase.PurchaseState\nimport com.android.billingclient.api.PurchasesUpdatedListener\nimport com.android.billingclient.api.SkuDetails\nimport com.android.billingclient.api.SkuDetailsParams\nimport com.android.billingclient.api.SkuDetailsResult\nimport com.android.billingclient.api.querySkuDetails\nimport com.arialyy.frame.util.ResUtil\nimport com.blankj.utilcode.util.ToastUtils\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseApp\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.withContext\nimport timber.log.Timber\n\n/**\n * https://developer.android.com/google/play/billing/integrate?hl=zh-cn#fetch\n * @Author laoyuyu\n * @Description\n * @Date 1:58 下午 2022/1/20\n **/\nclass PlayServiceUtil {\n  private var isConnected = false\n\n  /**\n   * 购买回调\n   */\n  private val purchasesUpdatedListener = PurchasesUpdatedListener { billingResult, purchases ->\n    // To be implemented in a later section.\n    Timber.d(\"result = ${billingResult}, purchases = $purchases\")\n    if (billingResult.responseCode == BillingResponseCode.OK && purchases != null) {\n      Timber.d(\"purchases success\")\n      for (purchase in purchases) {\n        handlePurchase(purchase)\n      }\n    } else if (billingResult.responseCode == BillingResponseCode.USER_CANCELED) {\n      Timber.d(\"cancel\")\n      // Handle an error caused by a user cancelling the purchase flow.\n    } else {\n      // Handle any other error codes.\n    }\n  }\n\n  private var billingClient = BillingClient.newBuilder(BaseApp.APP)\n    .setListener(purchasesUpdatedListener)\n    .enablePendingPurchases()\n    .build()\n\n  /**\n   * 确认非消耗型商品的购买交易\n   */\n  private fun handlePurchase(purchase: Purchase) {\n    if (purchase.purchaseState == PurchaseState.PURCHASED) {\n      if (!purchase.isAcknowledged) {\n        val acknowledgePurchaseParams = AcknowledgePurchaseParams.newBuilder()\n          .setPurchaseToken(purchase.purchaseToken)\n        billingClient.acknowledgePurchase(acknowledgePurchaseParams.build()) {\n\n          Timber.d(\"code = ${it.responseCode}\")\n        }\n      }\n    }\n  }\n\n  /**\n   * connect to play\n   */\n  fun connectPlay(callback: (Boolean) -> Unit) {\n    // loadingDialog.show()\n    billingClient.startConnection(object : BillingClientStateListener {\n      override fun onBillingSetupFinished(billingResult: BillingResult) {\n        // loadingDialog.dismiss()\n        if (billingResult.responseCode == BillingResponseCode.OK) {\n          // The BillingClient is ready. You can query purchases here.\n          isConnected = true\n          callback.invoke(true)\n          return\n        }\n        ToastUtils.showLong(ResUtil.getString(R.string.error_connect_play))\n        callback.invoke(false)\n      }\n\n      override fun onBillingServiceDisconnected() {\n        // Try to restart the connection on the next request to\n        // Google Play by calling the startConnection() method.\n        isConnected = false\n      }\n    })\n  }\n\n  /**\n   * 普通商品\n   */\n  suspend fun queryInappSkuDetails(): SkuDetailsResult {\n    val skuList = ArrayList<String>()\n    skuList.add(\"01_rqvz3usue52wu77d7dqhywfjc23ki9ae\")\n    skuList.add(\"02_i5phiiuoccqxrwzhu72caqpfjz2ayrsx\")\n    skuList.add(\"03_9i7njrgcrr4rev554hnz777a7uhxxy2w\")\n    val params = SkuDetailsParams.newBuilder()\n    params.setSkusList(skuList).setType(SkuType.INAPP)\n\n    // leverage querySkuDetails Kotlin extension function\n    return withContext(Dispatchers.IO) {\n      val detail = billingClient.querySkuDetails(params.build())\n      val skuList = detail.skuDetailsList\n      val skuResult = detail.billingResult\n      Timber.d(\"get inapp sku success, size = ${skuList?.size}\")\n      return@withContext detail\n    }\n\n    // Process the result.\n  }\n\n  /**\n   * 订阅商品\n   */\n  suspend fun querySubSkuDetails(): SkuDetailsResult {\n    val skuList = ArrayList<String>()\n    skuList.add(\"11_wdvkaaymasi93y7r5hnrcm7unccqv2a7\")\n    skuList.add(\"21_rgwbquffzynsq23ca3239npc7r7fddnm\")\n    skuList.add(\"14_v9bgagya5emfagnbxizhmeoj223wfsuq\")\n    skuList.add(\"13_w2vn9xn7k9zqga29jrxndrtut4kifipo\")\n    val params = SkuDetailsParams.newBuilder()\n    params.setSkusList(skuList).setType(SkuType.SUBS)\n\n    // leverage querySkuDetails Kotlin extension function\n    return withContext(Dispatchers.IO) {\n      val detail = billingClient.querySkuDetails(params.build())\n      val skuList = detail.skuDetailsList\n      val skuResult = detail.billingResult\n      Timber.d(\"get sub sku success, size = ${skuList?.size}\")\n      return@withContext detail\n    }\n  }\n\n  /**\n   * 开始支付流程\n   */\n  suspend fun startPlayFlow(ac: Activity, skuDetail: SkuDetails) {\n    val flowParams = BillingFlowParams.newBuilder()\n      .setSkuDetails(skuDetail)\n      .build()\n    val responseCode = billingClient.launchBillingFlow(ac, flowParams).responseCode\n  }\n\n  fun onDestroy() {\n    billingClient.endConnection()\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/util/AutoLockDbUtil.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.util\n\nimport android.content.Context\nimport androidx.core.content.edit\nimport androidx.preference.PreferenceManager\nimport androidx.work.CoroutineWorker\nimport androidx.work.ExistingWorkPolicy.REPLACE\nimport androidx.work.OneTimeWorkRequest\nimport androidx.work.WorkManager\nimport androidx.work.WorkerParameters\nimport com.arialyy.frame.util.StringUtil\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseApp\nimport com.lyy.keepassa.base.Constance\nimport timber.log.Timber\nimport java.util.concurrent.TimeUnit\n\n/**\n * 自动锁定数据库工具\n */\nclass AutoLockDbUtil private constructor() {\n  private var requestTag = \"LockDbWork\"\n  private val KEY_NAME = \"LockTimer\"\n  private val KEY_LAST_START_TIME = \"LastStartTime\"\n  private val sp = BaseApp.APP.getSharedPreferences(Constance.PRE_FILE_NAME, Context.MODE_PRIVATE)\n  private val TIMER_TAG = \"AutoLockDbTimer\"\n  private val manager by lazy {\n    WorkManager.getInstance(BaseApp.APP)\n  }\n\n  companion object {\n    private val instance: AutoLockDbUtil by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {\n      AutoLockDbUtil()\n    }\n\n    fun get(): AutoLockDbUtil {\n      return instance\n    }\n  }\n\n  /**\n   * 重置定时器\n   */\n  fun resetTimer() {\n    Timber.d( \"resetTimer\")\n    startLockWorker()\n  }\n\n  /**\n   * cancel timer\n   */\n  fun cancelTimer(){\n    manager.cancelAllWorkByTag(TIMER_TAG)\n  }\n\n  /**\n   * 启动定时器\n   */\n  private fun startTimer(workRequest: OneTimeWorkRequest) {\n    val lastStartTime = sp.getLong(KEY_LAST_START_TIME, -1)\n    if (lastStartTime > 0 && System.currentTimeMillis() - lastStartTime <= 3000) {\n      return\n    }\n    Timber.d( \"开始自动锁定\")\n    sp.edit(true) {\n      putLong(KEY_LAST_START_TIME, System.currentTimeMillis())\n    }\n\n    // https://developer.android.com/topic/libraries/architecture/workmanager/how-to/managing-work?hl=zh-cn\n    // 唯一任务\n    manager.enqueueUniqueWork(\n        \"autoLockDb\",\n        REPLACE, // 如果有新任务，则取消以前的任务\n        workRequest\n    )\n  }\n\n  /**\n   * 立即启动定时器\n   */\n  fun startLockWorkerNow() {\n    val wordRequest = OneTimeWorkRequest.Builder(LockWorker::class.java)\n        .addTag(requestTag)\n        .build()\n    startTimer(wordRequest)\n  }\n\n  /**\n   * 启动锁定数据库的工作线程\n   */\n  private fun startLockWorker() {\n    val time = PreferenceManager.getDefaultSharedPreferences(BaseApp.APP)\n        .getString(BaseApp.APP.getString(R.string.set_key_auto_lock_db_time), \"300\")!!\n        .toInt()\n//    val time = 10\n    val wordRequest = OneTimeWorkRequest.Builder(LockWorker::class.java)\n        .addTag(TIMER_TAG)\n        .setInitialDelay(time.toLong(), TimeUnit.SECONDS)\n        .build()\n\n    startTimer(wordRequest)\n  }\n\n  /**\n   * 锁定数据库线程任务\n   * 如果开启了快速解锁，进入快速解锁界面\n   * 如果没有开启快速解锁，直接进入启动页，并清空数据库\n   */\n  class LockWorker(\n    appContext: Context,\n    workerParams: WorkerParameters\n  ) : CoroutineWorker(appContext, workerParams) {\n    override suspend fun doWork(): Result {\n      KeepassAUtil.instance.lock()\n      return Result.success()\n    }\n\n  }\n\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/util/BarUtil.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.util\n\nimport android.app.Activity\nimport android.content.Context\nimport android.content.Context.WINDOW_SERVICE\nimport android.graphics.PixelFormat\nimport android.graphics.Rect\nimport android.os.Build\nimport android.view.Gravity\nimport android.view.View\nimport android.view.ViewTreeObserver\nimport android.view.Window\nimport android.view.WindowManager\nimport com.arialyy.frame.util.ResUtil\nimport com.gyf.immersionbar.ImmersionBar\nimport com.lyy.keepassa.R\nimport timber.log.Timber\nimport java.lang.reflect.Field\nimport java.lang.reflect.Method\n\n/**\n * @author laoyuyu\n * @date 2021/3/22\n */\nobject BarUtil {\n  private val TAG = javaClass.simpleName\n  private const val MIUI = 1\n  private const val FLYME = 2\n  private const val ANDROID_M = 3\n\n  fun showStatusBar(\n    activity: Activity,\n    show: Boolean\n  ) {\n    val window = activity.window\n    var vis: Int = window.decorView.systemUiVisibility\n    if (show) {\n//      setStatusBarLightMode(activity)\n      window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)\n      window.statusBarColor = ResUtil.getColor(R.color.background_color)\n      window.navigationBarColor = ResUtil.getColor(R.color.background_color)\n      window.decorView.systemUiVisibility =\n        (WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS\n            or View.SYSTEM_UI_FLAG_VISIBLE\n            or setMode()\n            )\n\n//      ImmersionBar.with(activity)\n//        .statusBarColor(R.color.background_color)\n//        .autoStatusBarDarkModeEnable(true, 0.2f) //自动状态栏字体变色，必须指定状态栏颜色才可以自动变色哦\n//        .flymeOSStatusBarFontColor(R.color.text_black_color)\n//        .fitsSystemWindows(true)\n////          .hideBar(FLAG_HIDE_STATUS_BAR)\n//        .autoNavigationBarDarkModeEnable(true, 0.2f) // 自动导航栏图标变色，必须指定导航栏颜色才可以自动变色哦\n//        .navigationBarColor(R.color.background_color)\n//        .statusBarDarkFont(\n//          true, 0.2f\n//        )  //原理：如果当前设备支持状态栏字体变色，会设置状态栏字体为黑色，如果当前设备不支持状态栏字体变色，会使当前状态栏加上透明度，否则不执行透明度\n//        .init()\n      return\n    }\n    window.navigationBarColor = ResUtil.getColor(R.color.background_color)\n    vis = vis.or(setMode())\n    vis = vis.or(View.SYSTEM_UI_FLAG_FULLSCREEN)\n    vis = vis.or(View.INVISIBLE)\n    window.decorView.systemUiVisibility = vis\n  }\n\n  private fun setMode(): Int {\n    var mode = 0\n    val isNight = KeepassAUtil.instance.isNightMode()\n    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {\n      mode = mode.or(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR)\n    }\n    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {\n      mode = mode.or(View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR)\n    }\n    return mode\n  }\n\n  /**\n   * 直接读取系统里状态栏高度的值，但是无法判断状态栏是否显示\n   */\n  fun getStatusBarHeight(context: Context):Int {\n    var height = 0\n    //获取status_bar_height资源的ID\n    val resourceId: Int = context.resources.getIdentifier(\"status_bar_height\", \"dimen\", \"android\")\n    if (resourceId > 0) {\n      //根据资源ID获取响应的尺寸值\n      height = context.resources.getDimensionPixelSize(resourceId)\n    }\n    return height\n  }\n\n  /**\n   * 状态栏是否隐藏\n   * @return true 状态栏没有隐藏\n   */\n  fun statusBarIsVisible(window: Window):Boolean{\n    val rectangle = Rect()\n    window.decorView.getWindowVisibleDisplayFrame(rectangle)\n    val statusBarHeight: Int = rectangle.top\n    return statusBarHeight != 0\n  }\n\n  /**\n   * @param callback true 状态栏隐藏; false状态栏显示\n   */\n  private fun statusBarIsVisible(\n    context: Context,\n    callback: (Boolean) -> Unit\n  ) {\n    val wm = context.getSystemService(WINDOW_SERVICE) as WindowManager?\n    val p = WindowManager.LayoutParams()\n    p.type = WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY\n    //放在左上角\n    p.gravity = Gravity.START or Gravity.TOP\n    // 不可触摸，不可获得焦点\n    p.flags = (WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL\n        or WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE)\n\n    p.width = 1\n    p.height = WindowManager.LayoutParams.MATCH_PARENT\n    p.format = PixelFormat.TRANSPARENT\n    val helperWnd = View(context) //View helperWnd;\n\n    val vto: ViewTreeObserver = helperWnd.viewTreeObserver\n    vto.addOnGlobalLayoutListener {\n      val windowParams = IntArray(2)\n      val screenParams = IntArray(2)\n      helperWnd.getLocationInWindow(windowParams)\n      helperWnd.getLocationOnScreen(screenParams)\n      // 如果状态栏隐藏，返回0，如果状态栏显示则返回高度\n      Timber.d( \"getStatusBarHeight = \" + (screenParams[1] - windowParams[1]))\n      val b = screenParams[1] - windowParams[1] == 0\n      callback.invoke(b)\n    }\n    wm!!.addView(helperWnd, p)\n  }\n\n  /**\n   * 状态栏亮色模式，设置状态栏黑色文字、图标，\n   * 适配4.4以上版本MIUIV、Flyme和6.0以上版本其他Android\n   *\n   * @return 1:MIUUI 2:Flyme 3:android6.0\n   */\n  fun setStatusBarLightMode(activity: Activity): Int {\n    var result = 0\n    when {\n      MIUISetStatusBarLightMode(activity, true) -> {\n        result = MIUI\n      }\n      FLYMESetStatusBarLightMode(activity.window, true) -> {\n        result = FLYME\n      }\n      Build.VERSION.SDK_INT >=  Build.VERSION_CODES.M -> {\n        activity.window.decorView.systemUiVisibility =\n          View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR\n        result = ANDROID_M\n      }\n    }\n    return result\n  }\n\n  /**\n   * 已知系统类型时，设置状态栏黑色文字、图标。\n   * 适配4.4以上版本MIUIV、Flyme和6.0以上版本其他Android\n   *\n   * @param type 1:MIUUI 2:Flyme 3:android6.0\n   */\n  fun setStatusBarLightMode(\n    activity: Activity,\n    type: Int\n  ) {\n    when (type) {\n      MIUI -> {\n        MIUISetStatusBarLightMode(activity, true)\n      }\n      FLYME -> {\n        FLYMESetStatusBarLightMode(activity.window, true)\n      }\n      ANDROID_M -> {\n        activity.window.decorView.systemUiVisibility =\n          View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR\n      }\n    }\n  }\n\n  /**\n   * 状态栏暗色模式，清除MIUI、flyme或6.0以上版本状态栏黑色文字、图标\n   */\n  fun setStatusBarDarkMode(\n    activity: Activity,\n    type: Int\n  ) {\n    when (type) {\n      MIUI -> {\n        MIUISetStatusBarLightMode(activity, false)\n      }\n      FLYME -> {\n        FLYMESetStatusBarLightMode(activity.window, false)\n      }\n      ANDROID_M -> {\n        activity.window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE\n      }\n    }\n  }\n\n  /**\n   * 设置状态栏图标为深色和魅族特定的文字风格\n   * 可以用来判断是否为Flyme用户\n   *\n   * @param window 需要设置的窗口\n   * @param dark   是否把状态栏文字及图标颜色设置为深色\n   * @return boolean 成功执行返回true\n   */\n  private fun FLYMESetStatusBarLightMode(\n    window: Window?,\n    dark: Boolean\n  ): Boolean {\n    var result = false\n    if (window != null) {\n      try {\n        val lp: WindowManager.LayoutParams = window.attributes\n        val darkFlag: Field = WindowManager.LayoutParams::class.java\n            .getDeclaredField(\"MEIZU_FLAG_DARK_STATUS_BAR_ICON\")\n        val meizuFlags: Field = WindowManager.LayoutParams::class.java\n            .getDeclaredField(\"meizuFlags\")\n        darkFlag.isAccessible = true\n        meizuFlags.isAccessible = true\n        val bit: Int = darkFlag.getInt(null)\n        var value: Int = meizuFlags.getInt(lp)\n        value = if (dark) {\n          value or bit\n        } else {\n          value and bit.inv()\n        }\n        meizuFlags.setInt(lp, value)\n        window.attributes = lp\n        result = true\n      } catch (e: Exception) {\n      }\n    }\n    return result\n  }\n\n  /**\n   * 需要MIUIV6以上\n   *\n   * @param dark 是否把状态栏文字及图标颜色设置为深色\n   * @return boolean 成功执行返回true\n   */\n  private fun MIUISetStatusBarLightMode(\n    activity: Activity,\n    dark: Boolean\n  ): Boolean {\n    var result = false\n    val window: Window? = activity.window\n    if (window != null) {\n      val clazz: Class<*> = window::class.java\n      try {\n        var darkModeFlag = 0\n        val layoutParams = Class.forName(\"android.view.MiuiWindowManager\\$LayoutParams\")\n        val field: Field = layoutParams.getField(\"EXTRA_FLAG_STATUS_BAR_DARK_MODE\")\n        darkModeFlag = field.getInt(layoutParams)\n        val extraFlagField: Method = clazz.getMethod(\n            \"setExtraFlags\",\n            Int::class.javaPrimitiveType,\n            Int::class.javaPrimitiveType\n        )\n        if (dark) {\n          extraFlagField.invoke(window, darkModeFlag, darkModeFlag) //状态栏透明且黑色字体\n        } else {\n          extraFlagField.invoke(window, 0, darkModeFlag) //清除黑色字体\n        }\n        result = true\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {\n          //开发版 7.7.13 及以后版本采用了系统API，旧方法无效但不会报错，所以两个方式都要加上\n          if (dark) {\n            activity.window.decorView.systemUiVisibility =\n              View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR\n          } else {\n            activity.window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE\n          }\n        }\n      } catch (e: java.lang.Exception) {\n        Timber.e(e)\n      }\n    }\n    return result\n  }\n\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/util/ClipboardUtil.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.util\n\nimport android.content.ClipData\nimport android.content.ClipboardManager\nimport android.content.Context\nimport androidx.preference.PreferenceManager\nimport androidx.work.OneTimeWorkRequest\nimport androidx.work.WorkManager\nimport androidx.work.Worker\nimport androidx.work.WorkerParameters\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseApp\nimport java.util.concurrent.TimeUnit\n\n/**\n * 剪切板工具\n */\nclass ClipboardUtil private constructor() {\n  private val clipManager: ClipboardManager =\n    BaseApp.APP.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager\n\n  companion object {\n    private const val CLIP_LABEL = \"CLIP_LABEL\"\n    private var instance: ClipboardUtil? = null\n      get() {\n        if (field == null) {\n          field = ClipboardUtil()\n        }\n        return field\n      }\n\n    //细心的小伙伴肯定发现了，这里不用getInstance作为为方法名，是因为在伴生对象声明时，内部已有getInstance方法，所以只能取其他名字\n    fun get(): ClipboardUtil {\n\n      return instance!!\n    }\n  }\n\n  /**\n   * 将数据拷贝到剪切板中，并在30s后清空剪切板\n   */\n  public fun copyDataToClip(string: String) {\n    val itemData = ClipData.newPlainText(CLIP_LABEL, string)\n    clipManager.setPrimaryClip(itemData)\n    val time = PreferenceManager.getDefaultSharedPreferences(BaseApp.APP)\n        .getString(\n            BaseApp.APP.getString(\n                R.string.set_key_clean_clip_time\n            ), \"30\"\n        )!!.toLong()\n    val wordRequest = OneTimeWorkRequest.Builder(CleanClipWord::class.java)\n        .setInitialDelay(time, TimeUnit.SECONDS)\n        .build()\n    WorkManager.getInstance(BaseApp.APP)\n        .enqueue(wordRequest)\n  }\n\n  /**\n   * 清空剪切板\n   */\n  fun cleanClipboard() {\n    clipManager.setPrimaryClip(ClipData.newPlainText(null, \"\"))\n  }\n\n  class CleanClipWord(\n    appContext: Context,\n    workerParams: WorkerParameters\n  ) : Worker(appContext, workerParams) {\n    override fun doWork(): Result {\n      get().cleanClipboard()\n      return Result.success()\n    }\n\n  }\n\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/util/CommonKVStorage.kt",
    "content": "package com.lyy.keepassa.util\n\nimport android.os.Parcelable\nimport com.tencent.mmkv.MMKV\n\nobject CommonKVStorage : KVStorage {\n\n  private val kv: MMKV by lazy {\n    MMKV.mmkvWithID(\"com.lyy.kpa\", MMKV.MULTI_PROCESS_MODE)\n  }\n\n  override fun put(key: String, value: String): Boolean = kv.encode(key, value)\n\n  override fun getString(key: String, defaultValue: String): String =\n    kv.decodeString(key, defaultValue) ?: \"\"\n\n  override fun put(key: String, value: Boolean): Boolean = kv.encode(key, value)\n\n  override fun getBoolean(key: String, defaultValue: Boolean): Boolean =\n    kv.decodeBool(key, defaultValue)\n\n  override fun put(key: String, value: Int): Boolean = kv.encode(key, value)\n\n  override fun getInt(key: String, defaultValue: Int): Int = kv.decodeInt(key, defaultValue)\n\n  override fun put(key: String, value: Long): Boolean = kv.encode(key, value)\n\n  override fun getLong(key: String, defaultValue: Long): Long = kv.decodeLong(key, defaultValue)\n\n  override fun put(key: String, value: Float): Boolean = kv.encode(key, value)\n\n  override fun getFloat(key: String, defaultValue: Float): Float =\n    kv.decodeFloat(key, defaultValue)\n\n  override fun put(key: String, value: Double): Boolean = kv.encode(key, value)\n\n  override fun getDouble(key: String, defaultValue: Double): Double =\n    kv.decodeDouble(key, defaultValue)\n\n  override fun put(key: String, value: Set<String>): Boolean = kv.encode(key, value)\n\n  override fun getStringSet(key: String, defaultValue: Set<String>): Set<String> =\n    kv.decodeStringSet(key, defaultValue) ?: mutableSetOf()\n\n  override fun put(key: String, value: Parcelable): Boolean = kv.encode(key, value)\n\n  override fun <T : Parcelable?> get(key: String?, tClass: Class<T>?, defaultValue: T?): T? =\n    kv.decodeParcelable(key, tClass, defaultValue)\n\n  override fun containsKey(key: String): Boolean = kv.containsKey(key)\n\n  override fun remove(key: String) = kv.removeValueForKey(key)\n\n  override fun clean() = kv.clearAll()\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/util/EncryptUtil.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.util\n\nimport com.google.crypto.tink.Aead\nimport com.google.crypto.tink.KeysetHandle\nimport com.google.crypto.tink.aead.AeadKeyTemplates\nimport com.google.crypto.tink.config.TinkConfig\n\n/**\n * 加密工具\n * https://developer.android.com/topic/security/data\n */\nobject EncryptUtil {\n  var AEAD: Aead? = null\n\n  /**\n   *\n   * 加密字符串，并将其保存到配置文件中\n   *\n   * @param str 明文\n   * @param pass 密钥，一般是放在服务器端，或放在keystore\n   * @return 密文\n   */\n  fun encryptStr(\n    str: String,\n    pass: String\n  ): String? {\n    val temp = getAead()?.encrypt(\n        str.toByteArray(Charsets.UTF_8),\n        pass.toByteArray(Charsets.UTF_8)\n    )\n    if (temp != null) {\n      return String(temp, Charsets.UTF_8)\n    }\n    return null\n  }\n\n  /**\n   * 解密字符串\n   *\n   * @param str 密文\n   *  @param pass 密钥，一般是放在服务器端，或放在keystore\n   * @return 明文\n   */\n  fun decryptionStr(\n    str: String,\n    pass: String\n  ): String? {\n    val temp = getAead()?.decrypt(\n        str.toByteArray(Charsets.UTF_8),\n        pass.toByteArray(Charsets.UTF_8)\n    )\n    if (temp != null) {\n      return String(temp, Charsets.UTF_8)\n    }\n    return null\n  }\n\n  /**\n   * @param dbPass keepass数据库密码\n   */\n  fun getAead(): Aead? {\n    if (AEAD == null) {\n      TinkConfig.register()\n      // 生成密钥\n      val keysetHandle = KeysetHandle.generateNew(AeadKeyTemplates.AES256_GCM)\n      AEAD = keysetHandle.getPrimitive(Aead::class.java)\n    }\n    return AEAD\n  }\n\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/util/EventBusHelper.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.util\n\nimport android.app.Activity\nimport android.inputmethodservice.InputMethodService\nimport androidx.fragment.app.Fragment\nimport com.lyy.keepassa.view.menu.IPopMenu\nimport org.greenrobot.eventbus.EventBus\n\nobject EventBusHelper {\n\n  fun reg(inputService: InputMethodService){\n    if (!EventBus.getDefault().isRegistered(inputService)) {\n      EventBus.getDefault().register(inputService)\n    }\n  }\n\n  fun unReg(inputService: InputMethodService) {\n    if (EventBus.getDefault().isRegistered(inputService)) {\n      EventBus.getDefault().unregister(inputService)\n    }\n  }\n\n  fun reg(activity: Activity) {\n    if (!EventBus.getDefault().isRegistered(activity)) {\n      EventBus.getDefault().register(activity)\n    }\n  }\n\n  fun unReg(activity: Activity) {\n    if (EventBus.getDefault().isRegistered(activity)) {\n      EventBus.getDefault().unregister(activity)\n    }\n  }\n\n  fun reg(fragment: Fragment) {\n    if (!EventBus.getDefault().isRegistered(fragment)) {\n      EventBus.getDefault().register(fragment)\n    }\n  }\n\n  fun unReg(fragment: Fragment) {\n    if (EventBus.getDefault().isRegistered(fragment)) {\n      EventBus.getDefault().unregister(fragment)\n    }\n  }\n\n\n  fun reg(popMenu: IPopMenu) {\n    if (!EventBus.getDefault().isRegistered(popMenu)) {\n      EventBus.getDefault().register(popMenu)\n    }\n  }\n\n  fun unReg(popMenu: IPopMenu) {\n    if (EventBus.getDefault().isRegistered(popMenu)) {\n      EventBus.getDefault().unregister(popMenu)\n    }\n  }\n\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/util/Extensions.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.util\n\nimport android.net.Uri\nimport android.os.Bundle\nimport android.os.Parcelable\nimport androidx.fragment.app.Fragment\nimport com.keepassdroid.utils.UriUtil\nimport com.lyy.keepassa.base.BaseApp\nimport com.lyy.keepassa.entity.DbHistoryRecord\nimport com.lyy.keepassa.view.StorageType\nimport com.lyy.keepassa.view.StorageType.AFS\nimport timber.log.Timber\nimport java.io.InputStream\nimport java.io.Serializable\n\nfun Uri.getBytes(): ByteArray? {\n  var ips: InputStream? = null\n  return try {\n    ips = UriUtil.getUriInputStream(BaseApp.APP, this)\n    ips.readBytes()\n  } catch (e: Exception) {\n    Timber.d(e)\n    null\n  } finally {\n    ips?.close()\n  }\n}\n\nfun <T> Fragment.putArgument(\n  key: String,\n  value: T\n) {\n  if (this.arguments == null) {\n    this.arguments = Bundle()\n  }\n\n  when (value) {\n    is Int -> this.requireArguments()\n      .putInt(key, value)\n    is Boolean -> this.requireArguments()\n      .putBoolean(key, value)\n    is String -> this.requireArguments()\n      .putString(key, value)\n    is CharSequence -> this.requireArguments()\n      .putCharSequence(key, value)\n    is Float -> this.requireArguments()\n      .putFloat(key, value)\n    is Long -> this.requireArguments()\n      .putLong(key, value)\n    is Bundle -> this.requireArguments()\n      .putBundle(key, value)\n    is Serializable -> this.requireArguments()\n      .putSerializable(key, value)\n    is Parcelable -> this.requireArguments()\n      .putParcelable(key, value)\n    is Char -> this.requireArguments()\n      .putChar(key, value)\n    is Byte -> this.requireArguments()\n      .putByte(key, value)\n    else -> error(\"不支持的类型, $value\")\n  }\n}\n\nfun <T> Fragment.getArgument(key: String): T? {\n  if (this.arguments == null) {\n    return null\n  }\n  val d = this.arguments?.get(key) ?: return null\n  return d as T\n}\n\nfun DbHistoryRecord.isAFS(): Boolean {\n  return StorageType.valueOf(this.type) === AFS\n}\n\n//fun <U, T> Fragment.getArgument(key: String) = BindLoader<U, T>(key)\n\n//private class IntentDelegate<in U, out T>(private val key: String) : ReadOnlyProperty<U, T> {\n//  override fun getValue(\n//    thisRef: U,\n//    property: KProperty<*>\n//  ): T {\n//    @Suppress(\"UNCHECKED_CAST\")\n//    return when (thisRef) {\n//      is Fragment -> thisRef.arguments?.get(key) as T\n//      else -> (thisRef as Activity).intent?.extras?.get(key) as T\n//    }\n//  }\n//}\n//\n//class BindLoader<in U, out T>(private val key: String) {\n//  operator fun provideDelegate(\n//    thisRef: U,\n//    prop: KProperty<*>\n//  ): ReadOnlyProperty<U, T> {\n//    // 创建委托\n//    return IntentDelegate(key)\n//  }\n//\n//}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/util/FingerprintUtil.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.util\n\nimport android.annotation.TargetApi\nimport android.content.Context\nimport android.os.Build\nimport android.os.Build.VERSION\nimport android.security.keystore.KeyGenParameterSpec\nimport android.security.keystore.KeyProperties\nimport androidx.annotation.Nullable\nimport androidx.biometric.BiometricManager\nimport timber.log.Timber\nimport java.security.KeyPair\nimport java.security.KeyPairGenerator\nimport java.security.KeyStore\nimport java.security.PrivateKey\nimport java.security.PublicKey\nimport java.security.Signature\nimport java.security.spec.ECGenParameterSpec\n\n/**\n * 指纹工具\n *\n */\nobject FingerprintUtil {\n\n  /**\n   * 是否支持生物识别：指纹，面部\n   * @return true 支持，false 硬件不支持，或者用户没有设置指纹\n   */\n  fun hasBiometricPrompt(context: Context): Boolean {\n    if (VERSION.SDK_INT <= Build.VERSION_CODES.M) {\n      return false\n    }\n    val biometricManager = BiometricManager.from(context)\n    var can = false\n    when (biometricManager.canAuthenticate()) {\n      BiometricManager.BIOMETRIC_SUCCESS -> {\n        Timber.d(\"App can authenticate using biometrics.\")\n        can = true\n      }\n\n      BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE ->\n        Timber.e(\"No biometric features available on this device.\")\n      BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE ->\n        Timber.e(\"Biometric features are currently unavailable.\")\n      BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED ->\n        Timber.e(\"The user hasn't associated any biometric credentials with their account.\")\n    }\n    return can\n  }\n\n\n  /**\n   * 生成密钥对\n   */\n  @TargetApi(Build.VERSION_CODES.M)\n  @Throws(Exception::class) fun generateKeyPair(\n    keyName: String,\n    invalidatedByBiometricEnrollment: Boolean\n  ): KeyPair? {\n    val keyPairGenerator: KeyPairGenerator =\n      KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_EC, \"AndroidKeyStore\")\n    val builder: KeyGenParameterSpec.Builder = KeyGenParameterSpec.Builder(\n        keyName,\n        KeyProperties.PURPOSE_SIGN\n    )\n        .setAlgorithmParameterSpec(ECGenParameterSpec(\"secp256r1\"))\n        .setDigests(\n            KeyProperties.DIGEST_SHA256,\n            KeyProperties.DIGEST_SHA384,\n            KeyProperties.DIGEST_SHA512\n        ) // Require the user to authenticate with a biometric to authorize every use of the key\n        .setUserAuthenticationRequired(true)\n\n    // Generated keys will be invalidated if the biometric templates are added more to user device\n    if (Build.VERSION.SDK_INT >= 24) {\n      builder.setInvalidatedByBiometricEnrollment(invalidatedByBiometricEnrollment)\n    }\n    keyPairGenerator.initialize(builder.build())\n    return keyPairGenerator.generateKeyPair()\n  }\n\n  /**\n   * 获取密钥对\n   */\n  @TargetApi(Build.VERSION_CODES.M)\n  @Throws(java.lang.Exception::class)\n  fun getKeyPair(keyName: String): KeyPair? {\n    val keyStore: KeyStore = KeyStore.getInstance(\"AndroidKeyStore\")\n    keyStore.load(null)\n    if (keyStore.containsAlias(keyName)) {\n      // Get public key\n      val publicKey: PublicKey = keyStore.getCertificate(keyName).publicKey\n      // Get private key\n      val privateKey: PrivateKey = keyStore.getKey(keyName, null) as PrivateKey\n      // Return a key pair\n      return KeyPair(publicKey, privateKey)\n    }\n    return null\n  }\n\n  @TargetApi(Build.VERSION_CODES.M)\n  @Nullable @Throws(java.lang.Exception::class)\n  fun initSignature(keyName: String): Signature? {\n    val keyPair = getKeyPair(keyName)\n    if (keyPair != null) {\n      val signature: Signature = Signature.getInstance(\"SHA256withECDSA\")\n      signature.initSign(keyPair.private)\n      return signature\n    }\n    return null\n  }\n\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/util/HitUtil.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.util\n\nimport android.view.View\nimport android.widget.Toast\nimport androidx.annotation.StringRes\nimport com.google.android.material.snackbar.Snackbar\nimport com.keepassdroid.database.exception.ArcFourException\nimport com.keepassdroid.database.exception.InvalidAlgorithmException\nimport com.keepassdroid.database.exception.InvalidDBException\nimport com.keepassdroid.database.exception.InvalidDBSignatureException\nimport com.keepassdroid.database.exception.InvalidDBVersionException\nimport com.keepassdroid.database.exception.InvalidKeyFileException\nimport com.keepassdroid.database.exception.InvalidPasswordException\nimport com.keepassdroid.database.exception.KeyFileEmptyException\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseApp\nimport com.thegrizzlylabs.sardineandroid.impl.SardineException\nimport java.io.FileNotFoundException\nimport java.net.HttpURLConnection\n\n/**\n * 提示工具\n */\nobject HitUtil {\n\n  /**\n   * 打印打开数据库的错误提示\n   */\n  fun toaskOpenDbException(e: Exception) {\n    when (e) {\n      is ArcFourException -> {\n        toaskShort(R.string.error_open_db_arcfour_error)\n      }\n      is InvalidAlgorithmException -> {\n        toaskShort(R.string.error_open_db_algorithm_error)\n      }\n      is InvalidDBSignatureException -> {\n        toaskShort(R.string.error_open_db_signature_error)\n      }\n      is InvalidDBVersionException -> {\n        toaskShort(R.string.error_open_db_version_error)\n      }\n      is InvalidKeyFileException -> {\n        toaskShort(R.string.error_open_db_key_invalid)\n      }\n      is InvalidPasswordException -> {\n        toaskShort(R.string.error_open_db_pass_error)\n      }\n      is KeyFileEmptyException -> {\n        toaskShort(R.string.error_open_db_key_empty)\n      }\n      is InvalidDBException -> {\n        toaskShort(R.string.error_open_db)\n      }\n      is FileNotFoundException -> {\n        toaskShort(R.string.db_file_no_exist)\n      }\n      is SardineException -> {\n        when (e.statusCode) {\n          HttpURLConnection.HTTP_UNAUTHORIZED -> {\n            toaskShort(R.string.invalid_auth)\n          }\n          HttpURLConnection.HTTP_NOT_FOUND -> {\n            toaskShort(R.string.db_file_no_exist)\n          }\n        }\n      }\n    }\n  }\n\n  fun toaskShort(@StringRes strId: Int) {\n    BaseApp.handler.post {\n      Toast.makeText(BaseApp.APP, BaseApp.APP.resources.getString(strId), Toast.LENGTH_SHORT)\n        .show()\n    }\n  }\n\n  fun toaskShort(text: String) {\n    BaseApp.handler.post {\n      Toast.makeText(BaseApp.APP, text, Toast.LENGTH_SHORT)\n        .show()\n    }\n  }\n\n  fun toaskLong(text: String) {\n    BaseApp.handler.post {\n      Toast.makeText(BaseApp.APP, text, Toast.LENGTH_LONG)\n        .show()\n    }\n  }\n\n  fun snackShort(\n    view: View,\n    text: String\n  ) {\n    Snackbar.make(view, text, Snackbar.LENGTH_SHORT)\n      .setAction(\"OK\") {}\n      .show()\n  }\n\n  fun snackLong(\n    view: View,\n    text: String\n  ) {\n    Snackbar.make(view, text, Snackbar.LENGTH_LONG)\n      .setAction(\"OK\") {}\n      .show()\n  }\n\n  fun snackLong(\n    view: View,\n    text: String,\n    actionStr: String,\n    action: View.OnClickListener\n  ) {\n    Snackbar.make(view, text, Snackbar.LENGTH_LONG)\n      .setAction(actionStr, action)\n      .show()\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/util/IconUtil.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.util\n\nimport android.content.Context\nimport android.graphics.Bitmap\nimport android.graphics.Bitmap.Config.ARGB_8888\nimport android.graphics.BitmapFactory\nimport android.graphics.Canvas\nimport android.graphics.Color\nimport android.graphics.drawable.AdaptiveIconDrawable\nimport android.graphics.drawable.BitmapDrawable\nimport android.graphics.drawable.Drawable\nimport android.graphics.drawable.LayerDrawable\nimport android.graphics.drawable.VectorDrawable\nimport android.os.Build\nimport android.widget.ImageView\nimport androidx.annotation.DrawableRes\nimport androidx.palette.graphics.Palette\nimport androidx.vectordrawable.graphics.drawable.VectorDrawableCompat\nimport com.arialyy.frame.util.ResUtil\nimport com.bumptech.glide.Glide\nimport com.keepassdroid.database.PwEntry\nimport com.keepassdroid.database.PwEntryV3\nimport com.keepassdroid.database.PwEntryV4\nimport com.keepassdroid.database.PwGroup\nimport com.keepassdroid.database.PwGroupV3\nimport com.keepassdroid.database.PwGroupV4\nimport com.keepassdroid.database.PwIconCustom\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.R.dimen\nimport com.lyy.keepassa.widget.toPx\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.withContext\nimport timber.log.Timber\n\nobject IconUtil {\n\n  val icons = listOf(\n    R.drawable.cc0_password, R.drawable.cc1_package_network, R.drawable.cc2_messagebox_warning,\n    R.drawable.cc3_server, R.drawable.cc4_klipper, R.drawable.cc5_edu_languages,\n    R.drawable.cc6_kcmdf, R.drawable.cc7_kate, R.drawable.cc8_socket, R.drawable.cc9_identity,\n    R.drawable.cc10_kontact, R.drawable.cc11_camera, R.drawable.cc12_irkick_flash,\n    R.drawable.cc13_kgpg_key3, R.drawable.cc14_laptop_power, R.drawable.cc15_scanner,\n    R.drawable.cc16_mozilla_firebird, R.drawable.cc17_cdrom_unmount, R.drawable.cc18_display,\n    R.drawable.cc19_email_generic, R.drawable.cc20_misc, R.drawable.cc21_korganizer,\n    R.drawable.cc22_ascii, R.drawable.cc23_icons, R.drawable.cc24_connect_established,\n    R.drawable.cc25_folder_mail, R.drawable.cc26_file_save, R.drawable.cc27_nfs_unmount,\n    R.drawable.cc28_quick_time, R.drawable.cc29_kgpg_term, R.drawable.cc30_konsole,\n    R.drawable.cc31_file_print, R.drawable.cc32_fsview, R.drawable.cc33_run,\n    R.drawable.cc34_configure, R.drawable.cc35_krfb, R.drawable.cc36_ark,\n    R.drawable.cc37_kpercentage, R.drawable.cc38_samba_unmount, R.drawable.cc39_history,\n    R.drawable.cc40_mail_find, R.drawable.cc41_vectorgfx, R.drawable.cc42_kcmemory,\n    R.drawable.cc43_edit_trash, R.drawable.cc44_knotes, R.drawable.cc45_cancel,\n    R.drawable.cc46_help, R.drawable.cc47_kpackage, R.drawable.cc48_folder,\n    R.drawable.cc49_folder_blue_open, R.drawable.cc50_folder_tar, R.drawable.cc51_decrypted,\n    R.drawable.cc52_encrypted, R.drawable.cc53_apply, R.drawable.cc54_signature,\n    R.drawable.cc55_thumbnail, R.drawable.cc56_kaddress_book, R.drawable.cc57_view_text,\n    R.drawable.cc58_kgpg, R.drawable.cc59_package_development, R.drawable.cc60_kfm_home,\n    R.drawable.cc61_services, R.drawable.cc62_tux, R.drawable.cc63_feather,\n    R.drawable.cc64_apple, R.drawable.cc65_w, R.drawable.cc66_money,\n    R.drawable.cc67_certificate, R.drawable.cc68_blackberry\n  )\n\n  fun getIconById(kpaIconId: Int): Int {\n    return icons[kpaIconId]\n  }\n\n  /**\n   * 获取图标 bitmap\n   * @param context\n   */\n  fun getAppIcon(\n    context: Context,\n    apkPkgName: String\n  ): Bitmap? {\n    try {\n      val d = context.packageManager.getApplicationIcon(apkPkgName)\n      return getBitmapFromDrawable(context, d)\n    } catch (e: Exception) {\n      Timber.e(e)\n    }\n    return null\n  }\n\n  /**\n   * 将自定义图标转换为drawable，如果自定义图标为空，则需要返回默认图标\n   * @param defIcon 默认图标\n   */\n  fun convertCustomIcon2Drawable(\n    context: Context,\n    customIcon: PwIconCustom,\n    @DrawableRes defIcon: Int = R.drawable.ic_image_blue_24px\n  ): Drawable {\n    if (customIconIsNull(customIcon)) {\n      return context.resources.getDrawable(defIcon)\n    }\n    val bd = BitmapDrawable(\n      context.resources,\n      BitmapFactory.decodeByteArray(\n        customIcon.imageData,\n        0,\n        customIcon.imageData.size\n      )\n    )\n    return zoomDrawable(context, bd)\n  }\n\n  /**\n   * 设置组的icon\n   */\n  fun setGroupIcon(\n    context: Context,\n    group: PwGroup,\n    img: ImageView\n  ) {\n    if (group is PwGroupV3) {\n      Glide.with(context)\n        .load(getIconById(group.icon.iconId))\n        .into(img)\n      return\n    }\n    if (customIconIsNull((group as PwGroupV4).customIcon)) {\n      Glide.with(context)\n        .load(getIconById(group.icon.iconId))\n        .into(img)\n      return\n    }\n    Glide.with(context)\n      .load(group.customIcon.imageData)\n      .into(img)\n  }\n\n  /**\n   * 获取group的drawable\n   */\n  fun getGroupIconDrawable(\n    context: Context,\n    group: PwGroup,\n    zoomIcon: Boolean = false\n  ): Drawable? {\n    if (group is PwGroupV3) {\n      return context.getDrawable(getIconById(group.icon.iconId))\n    } else {\n      val v4Group = group as PwGroupV4\n      return if (!customIconIsNull(group.customIcon)) {\n        val dr = BitmapDrawable(\n          context.resources,\n          BitmapFactory.decodeByteArray(\n            group.customIcon.imageData, 0,\n            group.customIcon.imageData.size\n          )\n        )\n        if (zoomIcon) zoomDrawable(context, dr) else dr\n      } else {\n        context.getDrawable(getIconById(group.icon.iconId))\n      }\n    }\n  }\n\n  /**\n   * 获取自定义图片\n   */\n  fun getCustomBitmap(entry: PwEntryV4): Bitmap {\n    return BitmapFactory.decodeByteArray(\n      entry.customIcon.imageData, 0,\n      entry.customIcon.imageData.size\n    )\n  }\n\n  /**\n   * 获取entry的drawable\n   */\n  fun getEntryIconDrawable(\n    context: Context,\n    entry: PwEntry,\n    zoomIcon: Boolean = false\n  ): Drawable? {\n    if (entry is PwEntryV3) {\n      return context.getDrawable(getIconById(entry.icon.iconId))\n    } else {\n      val v4Entry = entry as PwEntryV4\n      return if (!customIconIsNull(entry.customIcon)) {\n        val dr = BitmapDrawable(\n          context.resources,\n          BitmapFactory.decodeByteArray(\n            entry.customIcon.imageData, 0,\n            entry.customIcon.imageData.size\n          )\n        )\n        if (zoomIcon) zoomDrawable(context, dr) else dr\n      } else {\n        context.getDrawable(getIconById(entry.icon.iconId))\n      }\n    }\n  }\n\n  /**\n   * 设置项目的icon\n   */\n  fun setEntryIcon(\n    entry: PwEntry,\n    icon: ImageView\n  ) {\n    if (entry.icon == null) {\n      return\n    }\n    if (entry is PwEntryV3) {\n      icon.loadImg(getIconById(entry.icon.iconId))\n      return\n    }\n    if (entry is PwEntryV4) {\n      if (!customIconIsNull(entry.customIcon)) {\n        icon.loadImg(entry.customIcon.imageData)\n        return\n      }\n      icon.loadImg(getIconById(entry.icon.iconId))\n    }\n  }\n\n  /**\n   * 检查自定义图标是否为空\n   * @return true 自定义图标为空\n   */\n  private fun customIconIsNull(customIcon: PwIconCustom?): Boolean {\n    return (customIcon?.imageData == null) || customIcon.imageData.isEmpty() || customIcon == PwIconCustom.ZERO\n  }\n\n  /**\n   * 调整drawable大小\n   */\n  fun zoomDrawable(\n    context: Context,\n    drawable: BitmapDrawable\n  ): BitmapDrawable {\n    val iconSize = context.resources.getDimension(R.dimen.icon_size).toInt()\n\n    val newbmp =\n      Bitmap.createScaledBitmap(drawable.bitmap, iconSize, iconSize, true)\n    drawable.bitmap.recycle()\n    val dr = BitmapDrawable(context.resources, newbmp)\n    dr.setBounds(0, 0, iconSize, iconSize)\n    return dr\n  }\n\n  /**\n   * drawable 转bitmap\n   * @param iconSize 默认28dp，如果设置-1，则为图片本身大小\n   */\n  fun getBitmapFromDrawable(\n    context: Context,\n    @DrawableRes drawableId: Int,\n    iconSize: Int = context.resources.getDimension(dimen.icon_size)\n      .toInt()\n  ): Bitmap? {\n    val drawable: Drawable? = context.getDrawable(drawableId)\n    return getBitmapFromDrawable(context, drawable, iconSize)\n  }\n\n  fun getBitmapFromDrawable(\n    context: Context,\n    drawable: Drawable?,\n    iconSize: Int = context.resources.getDimension(dimen.icon_size)\n      .toInt()\n  ): Bitmap? {\n    return if (drawable is BitmapDrawable) {\n      drawable.bitmap\n    } else if (drawable is VectorDrawable || drawable is VectorDrawableCompat || drawable is LayerDrawable) {\n      val bitmap = Bitmap.createBitmap(\n        drawable.intrinsicWidth,\n        drawable.intrinsicHeight,\n        ARGB_8888\n      )\n      val canvas = Canvas(bitmap)\n      if (iconSize == -1) {\n        drawable.setBounds(0, 0, canvas.width, canvas.height)\n      } else {\n        drawable.setBounds(0, 0, iconSize, iconSize)\n      }\n      drawable.draw(canvas)\n      bitmap\n    } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && drawable is AdaptiveIconDrawable) {\n      val drr = arrayOfNulls<Drawable>(2)\n      drr[0] = drawable.background\n      drr[1] = drawable.foreground\n      val layerDrawable = LayerDrawable(drr)\n      return getBitmapFromDrawable(context, layerDrawable, layerDrawable.intrinsicWidth)\n    } else {\n      throw IllegalArgumentException(\"unsupported drawable type\")\n    }\n  }\n\n  /**\n   * get highlight color\n   */\n  fun getIconMdColor(\n    context: Context,\n    icon: Drawable?\n  ): Int {\n    val temp = getBitmapFromDrawable(context, icon, 40.toPx())\n    if (temp == null || temp.isRecycled) {\n      return Color.WHITE\n    }\n    val sw = Palette.from(temp)\n      .maximumColorCount(12)\n      .generate()\n    return when {\n      sw.mutedSwatch != null -> sw.mutedSwatch!!.rgb\n      sw.darkMutedSwatch != null -> sw.darkMutedSwatch!!.rgb\n      sw.lightMutedSwatch != null -> sw.lightMutedSwatch!!.rgb\n      sw.darkVibrantSwatch != null -> sw.darkVibrantSwatch!!.rgb\n      sw.lightVibrantSwatch != null -> sw.lightVibrantSwatch!!.rgb\n      sw.vibrantSwatch != null -> sw.vibrantSwatch!!.rgb\n      else -> ResUtil.getColor(R.color.colorPrimary)\n    }\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/util/ImageExtensions.kt",
    "content": "package com.lyy.keepassa.util\n\nimport android.app.Activity\nimport android.content.Context\nimport android.graphics.Bitmap\nimport android.graphics.drawable.Drawable\nimport android.widget.Button\nimport android.widget.ImageView\nimport androidx.annotation.DrawableRes\nimport androidx.appcompat.widget.AppCompatImageView\nimport com.bumptech.glide.Glide\nimport com.bumptech.glide.request.target.SimpleTarget\nimport com.bumptech.glide.request.transition.Transition\nimport timber.log.Timber\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 10:42 上午 2022/1/27\n **/\n\n/**\n * check bitmap is invalid\n * @return true is effect\n */\nfun Bitmap?.isInvalid():Boolean{\n  if (this == null) return true\n  if (this.isRecycled) return true\n  return false\n}\n\n/**\n * @return true 有效\n */\nprivate fun checkoutContextEffective(context: Context): Boolean {\n  if (context is Activity) {\n    if (context.isFinishing || context.isDestroyed) {\n      Timber.w(\"activity已经被销毁，不加载图片\")\n      return false\n    }\n  }\n  return true\n}\n\nfun Button.loadBackground(imgUrl: String?) {\n  if (checkoutContextEffective(context)) {\n    Glide.with(context).asDrawable().load(imgUrl).into(object : SimpleTarget<Drawable>() {\n      override fun onResourceReady(resource: Drawable, transition: Transition<in Drawable>?) {\n        this@loadBackground.background = resource\n      }\n    })\n  }\n}\n\nfun ImageView.loadImg(@DrawableRes resId: Int) {\n  if (checkoutContextEffective(context)) {\n    Glide.with(context).load(resId).into(this)\n  }\n}\n\nfun ImageView.loadImg(imgUrl: String?) {\n  if (checkoutContextEffective(context)) {\n    Glide.with(context).load(imgUrl).into(this)\n  }\n}\n\nfun ImageView.loadImg(imgBm: Bitmap?) {\n  if (checkoutContextEffective(context) && imgBm != null && !imgBm.isRecycled) {\n    Glide.with(context).load(imgBm).into(this)\n  }\n}\n\nfun ImageView.loadImg(byteArray: ByteArray?) {\n  if (checkoutContextEffective(context) && byteArray != null && byteArray.isNotEmpty()) {\n    Glide.with(context).load(byteArray).into(this)\n  }\n}\n\nfun AppCompatImageView.loadImg(@DrawableRes resId: Int) {\n  if (checkoutContextEffective(context)) {\n    Glide.with(context).load(resId).into(this)\n  }\n}\n\nfun AppCompatImageView.loadImg(imgUrl: String?) {\n  if (checkoutContextEffective(context)) {\n    Glide.with(context).load(imgUrl).into(this)\n  }\n}\n\nfun AppCompatImageView.loadImg(imgBm: Bitmap?) {\n  if (checkoutContextEffective(context) && imgBm != null && !imgBm.isRecycled) {\n    Glide.with(context).load(imgBm).into(this)\n  }\n}\n\nfun AppCompatImageView.loadImg(byteArray: ByteArray?) {\n  if (checkoutContextEffective(context) && byteArray != null && byteArray.isNotEmpty()) {\n    Glide.with(context).load(byteArray).into(this)\n  }\n}\n\nfun AppCompatImageView.loadImg(drawable: Drawable?) {\n  if (checkoutContextEffective(context) && drawable != null) {\n    Glide.with(context).load(drawable).into(this)\n  }\n}\n"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/util/KLog.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.util\n\nimport android.os.Bundle\nimport android.text.TextUtils\nimport android.util.Log\nimport org.json.JSONArray\nimport org.json.JSONException\nimport org.json.JSONObject\nimport java.util.Arrays\nimport kotlin.collections.Map.Entry\n\n/**\n * Created by Aria.Lao on 2017/10/25.\n * Aria日志工具\n */\n@Deprecated(\"please use timber\")\nobject KLog {\n  const val DEBUG = true\n  const val LOG_LEVEL_VERBOSE = 2\n  const val LOG_LEVEL_DEBUG = 3\n  const val LOG_LEVEL_INFO = 4\n  const val LOG_LEVEL_WARN = 5\n  const val LOG_LEVEL_ERROR = 6\n  const val LOG_LEVEL_ASSERT = 7\n  const val LOG_CLOSE = 8\n  const val LOG_DEFAULT = LOG_LEVEL_DEBUG\n  var LOG_LEVEL = LOG_DEFAULT\n  fun v(\n    tag: String,\n    msg: String\n  ): Int {\n    return println(Log.VERBOSE, tag, msg)\n  }\n\n  fun d(\n    tag: String,\n    msg: String\n  ): Int {\n    return println(Log.DEBUG, tag, msg)\n  }\n\n  fun i(\n    tag: String,\n    msg: String\n  ): Int {\n    return println(Log.INFO, tag, msg)\n  }\n\n  fun w(\n    tag: String,\n    msg: String\n  ): Int {\n    return println(Log.WARN, tag, msg)\n  }\n\n  fun e(\n    tag: String,\n    msg: String\n  ): Int {\n    return println(Log.ERROR, tag, msg)\n  }\n\n  fun e(\n    tag: String?,\n    msg: String?,\n    e: Throwable?\n  ) {\n    Log.e(tag, msg, e)\n  }\n\n  /**\n   * 打印MAp，debug级别日志\n   */\n  fun m(\n    tag: String,\n    map: Map<*, *>\n  ) {\n    if (LOG_LEVEL <= Log.DEBUG) {\n      val set: Set<*> = map.entries\n      if (set.size < 1) {\n        d(tag, \"[]\")\n        return\n      }\n      val s = arrayOfNulls<String>(set.size)\n      for ((i, aSet) in set.withIndex()) {\n        val entry = aSet as Entry<*, *>\n        s[i] = \"${entry.key.toString()} = ${entry.value},\"\n      }\n      println(Log.DEBUG, tag, s.contentToString())\n    }\n  }\n\n  /**\n   * 打印JSON，debug级别日志\n   */\n  fun j(\n    tag: String,\n    jsonStr: String\n  ) {\n    if (LOG_LEVEL <= Log.DEBUG) {\n      val message: String = try {\n        when {\n          jsonStr.startsWith(\"{\") -> {\n            val jsonObject = JSONObject(jsonStr)\n            jsonObject.toString(4) //这个是核心方法\n          }\n          jsonStr.startsWith(\"[\") -> {\n            val jsonArray = JSONArray(jsonStr)\n            jsonArray.toString(4)\n          }\n          else -> {\n            jsonStr\n          }\n        }\n      } catch (e: JSONException) {\n        jsonStr\n      }\n      println(Log.DEBUG, tag, \"\\n\\r$message\")\n    }\n  }\n\n  private fun bundleToString(\n    builder: StringBuilder,\n    data: Bundle\n  ) {\n    val keySet = data.keySet()\n    builder.append(\"[Bundle with \")\n        .append(keySet.size)\n        .append(\" keys:\")\n    for (key in keySet) {\n      builder.append(' ')\n          .append(key)\n          .append('=')\n      val value = data.get(key)\n      if (value is Bundle) {\n        bundleToString(builder, value)\n      } else {\n        val string = if (value is Array<*>) Arrays.toString(value) else value\n        builder.append(string)\n      }\n    }\n    builder.append(']')\n  }\n\n  fun b(data: Bundle?): String {\n    if (data == null) {\n      return \"N/A\"\n    }\n    val builder = StringBuilder()\n    bundleToString(builder, data)\n    return builder.toString()\n  }\n\n  /**\n   * 将异常信息转换为字符串\n   */\n  fun getExceptionString(ex: Throwable?): String {\n    if (ex == null) {\n      return \"\"\n    }\n    val err = StringBuilder()\n    err.append(\"ExceptionDetailed:\\n\")\n    err.append(\"====================Exception Info====================\\n\")\n    err.append(ex.toString())\n    err.append(\"\\n\")\n    val stack = ex.stackTrace\n    for (stackTraceElement in stack) {\n      err.append(stackTraceElement.toString())\n          .append(\"\\n\")\n    }\n    val cause = ex.cause\n    if (cause != null) {\n      err.append(\"【Caused by】: \")\n      err.append(cause.toString())\n      err.append(\"\\n\")\n      val stackTrace = cause.stackTrace\n      for (stackTraceElement in stackTrace) {\n        err.append(stackTraceElement.toString())\n            .append(\"\\n\")\n      }\n    }\n    err.append(\"===================================================\")\n    return err.toString()\n  }\n\n  private fun println(\n    level: Int,\n    tag: String,\n    msg: String\n  ): Int {\n    return if (LOG_LEVEL <= level) {\n      Log.println(level, tag, if (TextUtils.isEmpty(msg)) \"null\" else msg)\n    } else {\n      -1\n    }\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/util/KVStorage.kt",
    "content": "package com.lyy.keepassa.util\n\nimport android.os.Parcelable\n\n/**\n * K>V持久化\n */\ninterface KVStorage {\n\n    fun put(key: String, value: String): Boolean\n\n    fun getString(key: String, defaultValue: String = \"\"): String\n\n    fun put(key: String, value: Boolean): Boolean\n\n    fun getBoolean(key: String, defaultValue: Boolean = false): Boolean\n\n    fun put(key: String, value: Int): Boolean\n\n    fun getInt(key: String, defaultValue: Int = 0): Int\n\n    fun put(key: String, value: Long): Boolean\n\n    fun getLong(key: String, defaultValue: Long = 0L): Long\n\n    fun put(key: String, value: Float): Boolean\n\n    fun getFloat(key: String, defaultValue: Float = 0F): Float\n\n    fun put(key: String, value: Double): Boolean\n\n    fun getDouble(key: String, defaultValue: Double = 0.0): Double\n\n    fun put(key: String, value: Set<String>): Boolean\n\n    fun getStringSet(key: String, defaultValue: Set<String> = setOf()): Set<String>\n\n    fun put(key: String, value: Parcelable): Boolean\n\n    fun <T : Parcelable?> get(key: String?, tClass: Class<T>?, defaultValue: T?): T?\n\n    fun containsKey(key: String): Boolean\n\n    fun remove(key: String)\n\n    fun clean()\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/util/KdbUtil.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.util\n\nimport android.widget.TextView\nimport com.arialyy.frame.router.Routerfit\nimport com.arialyy.frame.util.FileUtil\nimport com.arialyy.frame.util.RegularRule\nimport com.arialyy.frame.util.ResUtil\nimport com.blankj.utilcode.util.Utils\nimport com.keepassdroid.Database\nimport com.keepassdroid.database.PwEntry\nimport com.keepassdroid.database.PwEntryV4\nimport com.keepassdroid.database.PwGroup\nimport com.keepassdroid.database.PwGroupIdV3\nimport com.keepassdroid.database.PwGroupIdV4\nimport com.keepassdroid.database.security.ProtectedBinary\nimport com.keepassdroid.database.security.ProtectedString\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseApp\nimport com.lyy.keepassa.router.DialogRouter\nimport com.lyy.keepassa.util.totp.OtpUtil\nimport com.lyy.keepassa.widget.pb.RoundProgressBarWidthNumber\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.MainScope\nimport kotlinx.coroutines.delay\nimport kotlinx.coroutines.launch\nimport kotlinx.coroutines.withContext\nimport timber.log.Timber\nimport java.io.File\nimport java.io.FileOutputStream\nimport java.nio.channels.Channels\nimport java.util.UUID\n\nobject KdbUtil {\n\n  val scope = MainScope()\n  val txtArray = arrayListOf(\"txt\", \"md\")\n  val imgArray = arrayListOf(\"png\", \"jpg\", \"jpeg\", \"webp\")\n  val DATE_FORMAT = \"yyyy/MM/dd HH:mm\"\n\n  /**\n   * 定时自动获取otp密码\n   */\n  fun startAutoGetOtp(entryV4: PwEntryV4, rPb: RoundProgressBarWidthNumber, tvValue: TextView) {\n    val p = OtpUtil.getOtpPass(entryV4)\n    if (p.second.isNullOrEmpty()) {\n      Timber.e(\"无法自动获取otp密码\")\n      return\n    }\n    rPb.setCountdown(true)\n\n    scope.launch(Dispatchers.Main) {\n      Timber.d(p.toString())\n      val time = p.first\n      rPb.max = time\n      tvValue.text = p.second\n      for (i in time downTo 1) {\n        rPb.progress = i\n        withContext(Dispatchers.IO) {\n          delay(1000)\n        }\n      }\n      startAutoGetOtp(entryV4, rPb, tvValue)\n    }\n  }\n\n  fun openFile(fileName: String, file: ProtectedBinary) {\n    txtArray.forEach {\n      if (fileName.endsWith(it, true)) {\n        Routerfit.create(DialogRouter::class.java).showMsgDialog(\n          msgTitle = ResUtil.getString(R.string.txt_viewer),\n          msgContent = String(file.data.readBytes()),\n          showCancelBt = false\n        )\n        return\n      }\n    }\n    imgArray.forEach {\n      if (fileName.endsWith(it, true)) {\n        val bytes = file.data.readBytes()\n        if (bytes.isNotEmpty()) {\n          Routerfit.create(DialogRouter::class.java).showImgViewerDialog(bytes)\n        }\n        return\n      }\n    }\n    openFileBySystem(fileName, file)\n  }\n\n  private fun openFileBySystem(fileName: String, file: ProtectedBinary) {\n    KpaUtil.scope.launch {\n      val context = Utils.getApp()\n      val targetFile = File(context.cacheDir, fileName)\n      withContext(Dispatchers.IO) {\n        val fic = Channels.newChannel(file.data)\n        val foc = FileOutputStream(targetFile).channel\n        foc.transferFrom(fic, 0, Int.MAX_VALUE.toLong())\n        fic.close()\n        foc.close()\n      }\n      FileUtil.openFile(context, targetFile)\n    }\n  }\n\n  fun Database?.isNull(): Boolean {\n    return this == null || this.pm == null\n  }\n\n  suspend fun getAllTags(): Set<String> {\n    val tagList = hashSetOf<String>()\n    withContext(Dispatchers.IO) {\n      BaseApp.KDB.pm.entries.forEach {\n        val entry = it.value as PwEntryV4\n        if (entry.tags.isNullOrEmpty()) {\n          return@forEach\n        }\n        tagList.addAll(getEntryTag(entry))\n      }\n    }\n    return tagList\n  }\n\n  fun getEntryTag(entryV4: PwEntryV4): Set<String> {\n    val tagSet = hashSetOf<String>()\n    entryV4.tags.split(\",\").forEach {\n      tagSet.add(it)\n    }\n    return tagSet\n  }\n\n  /**\n   * 获取用户名，如果是引用其它条目的，解析其引用\n   */\n  @Deprecated(\n    message = \"please use pwEntry.getRealUserName()\",\n    ReplaceWith(\"getRealUserName()\", \"com.lyy.keepassa.util.getRealUserName\")\n  )\n  fun getUserName(entry: PwEntry): String {\n    return entry.getRealUserName()\n  }\n\n  /**\n   * 获取密码，如果是引用其它条目的，解析其引用\n   */\n  @Deprecated(\n    message = \"please use pwEntry.getRealPass()\",\n    ReplaceWith(\"getRealPass()\", \"com.lyy.keepassa.util.getRealPass\")\n  )\n  fun getPassword(entry: PwEntry): String {\n    return entry.getRealPass()\n  }\n\n  /**\n   * 通过搜索条目\n   * @param domain 域名\n   * @param listStorage 搜索结果\n   */\n  fun searchEntriesByDomain(\n    domain: String?,\n    listStorage: MutableList<PwEntry>\n  ) {\n    if (domain.isNullOrEmpty()) {\n      return\n    }\n    val topDomain =\n      Regex(RegularRule.DOMAIN_TOP, RegexOption.IGNORE_CASE).find(domain)?.value.toString()\n    Timber.d(\"topDomain = $topDomain\")\n    for (entry in BaseApp.KDB.pm.entries.values) {\n      val pe4 = entry as PwEntryV4\n      if (pe4.url.contains(topDomain, true)\n        || pe4.strings[\"URL\"]?.toString()?.contains(topDomain) == true\n      ) {\n        listStorage.add(pe4)\n      }\n    }\n  }\n\n  /**\n   * 通过包名搜索条目\n   *\n   * @param pkgName 包名\n   * @param listStorage 搜索结果\n   */\n  fun searchEntriesByPackageName(\n    pkgName: String,\n    listStorage: MutableList<PwEntry>\n  ) {\n    for (entry in BaseApp.KDB.pm.entries.values) {\n      val pe4 = entry as PwEntryV4\n      if (pe4.strings == null || pe4.strings.isEmpty()) {\n        continue\n      }\n      for (ps in pe4.strings.values) {\n        if (ps.toString()\n            .equals(\"androidapp://$pkgName\", ignoreCase = true)\n        ) {\n          listStorage.add(pe4)\n          break\n        }\n      }\n    }\n  }\n\n  fun getGroupEntryNum(pwGroup: PwGroup): Int {\n    return pwGroup.childEntries.size + pwGroup.childGroups.size\n  }\n\n  /**\n   * 获取组中的条目数\n   */\n  fun getGroupAllEntryNum(pwGroup: PwGroup): Int {\n    var num = 0\n    if (pwGroup.childEntries.isEmpty() && pwGroup.childGroups.isEmpty()) {\n      return 0\n    }\n    if (pwGroup.childGroups.isNotEmpty()) {\n      for (group in pwGroup.childGroups) {\n        num += getGroupAllEntryNum(group)\n      }\n      num += pwGroup.childEntries.size\n    } else {\n      num += pwGroup.childEntries.size\n    }\n    return num\n  }\n\n  fun findEntry(uuid: UUID): PwEntry? {\n    val enties = BaseApp.KDB.pm.entries\n    return enties[uuid]\n  }\n\n  /**\n   * 通过id 获取v3的group信息\n   */\n  fun findV3GroupById(groupId: Int): PwGroup? {\n    val groups = BaseApp.KDB.pm.groups\n    return groups[PwGroupIdV3(groupId)]\n  }\n\n  /**\n   * 通过id 获取v4的group信息\n   */\n  fun findV4GroupById(groupId: UUID): PwGroup? {\n    val groups = BaseApp.KDB.pm.groups\n\n    return groups[PwGroupIdV4(groupId)]\n  }\n\n  fun filterCustomStr(map: Map<String, ProtectedString>): Map<String, ProtectedString> {\n    val remap = HashMap<String, ProtectedString>()\n    // var addOTPPass = false\n    for (str in map) {\n      if (str.key.equals(PwEntryV4.STR_NOTES, true)\n        || str.key.equals(PwEntryV4.STR_PASSWORD, true)\n        || str.key.equals(PwEntryV4.STR_TITLE, true)\n        || str.key.equals(PwEntryV4.STR_URL, true)\n        || str.key.equals(PwEntryV4.STR_USERNAME, true)\n      ) {\n        continue\n      }\n\n      remap[str.key] = str.value\n\n      // // 增加TOP密码字段\n      // if (!addOTPPass && (str.key.startsWith(\"TOTP\", ignoreCase = true)\n      //     || str.key.startsWith(\"OTP\", ignoreCase = true))\n      // ) {\n      //   addOTPPass = true\n      //   val totpPass = OtpUtil.getOtpPass(entryV4)\n      //   if (TextUtils.isEmpty(totpPass.second)) {\n      //     continue\n      //   }\n      //   val totpPassStr = ProtectedString(true, totpPass.second)\n      //   totpPassStr.isOtpPass = true\n      //   map[\"TOTP\"] = totpPassStr\n      // }\n    }\n\n    return remap.toList()\n      .sortedBy { it.first }\n      .toMap()\n  }\n\n  /**\n   * 过滤并排序自定义字段和自定义数据\n   */\n  fun filterCustomStr(\n    entryV4: PwEntryV4,\n  ): Map<String, ProtectedString> {\n    return filterCustomStr(entryV4.strings)\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/util/KeepassAUtil.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.util\n\nimport KDBAutoFillRepository\nimport android.annotation.SuppressLint\nimport android.annotation.TargetApi\nimport android.app.Activity\nimport android.app.assist.AssistStructure\nimport android.content.ComponentName\nimport android.content.ContentUris\nimport android.content.Context\nimport android.content.Intent\nimport android.content.pm.PackageManager.NameNotFoundException\nimport android.content.res.Configuration\nimport android.database.Cursor\nimport android.net.Uri\nimport android.os.Build\nimport android.os.Build.VERSION\nimport android.os.Environment\nimport android.provider.DocumentsContract\nimport android.provider.MediaStore.Audio\nimport android.provider.MediaStore.Images.Media\nimport android.provider.MediaStore.Video\nimport android.provider.OpenableColumns\nimport android.text.TextUtils\nimport android.view.View\nimport android.view.autofill.AutofillManager\nimport android.view.inputmethod.InputMethodManager\nimport androidx.annotation.ColorInt\nimport androidx.core.app.ActivityOptionsCompat\nimport androidx.core.content.ContextCompat.getSystemService\nimport androidx.core.graphics.ColorUtils\nimport androidx.documentfile.provider.DocumentFile\nimport androidx.fragment.app.Fragment\nimport androidx.fragment.app.FragmentActivity\nimport androidx.preference.PreferenceManager\nimport com.alibaba.android.arouter.launcher.ARouter\nimport com.arialyy.frame.core.AbsFrame\nimport com.arialyy.frame.router.Routerfit\nimport com.arialyy.frame.util.ResUtil\nimport com.arialyy.frame.util.StringUtil\nimport com.blankj.utilcode.util.AppUtils\nimport com.keepassdroid.database.PwDataInf\nimport com.keepassdroid.database.PwEntry\nimport com.keepassdroid.database.PwGroup\nimport com.keepassdroid.utils.UriUtil\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseApp\nimport com.lyy.keepassa.entity.DbHistoryRecord\nimport com.lyy.keepassa.entity.SimpleItemEntity\nimport com.lyy.keepassa.router.ActivityRouter\nimport com.lyy.keepassa.router.ServiceRouter\nimport com.lyy.keepassa.service.autofill.AutoFillHelper\nimport com.lyy.keepassa.service.autofill.StructureParser\nimport com.lyy.keepassa.view.create.CreateDbActivity\nimport com.lyy.keepassa.view.launcher.LauncherActivity\nimport com.lyy.keepassa.view.launcher.OpenDbHistoryActivity\nimport com.lyy.keepassa.view.main.QuickUnlockActivity\nimport com.lyy.keepassa.view.search.AutoFillEntrySearchActivity\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.delay\nimport kotlinx.coroutines.launch\nimport timber.log.Timber\nimport java.io.BufferedReader\nimport java.io.File\nimport java.io.FileReader\nimport java.io.IOException\nimport java.math.BigDecimal\nimport java.text.SimpleDateFormat\nimport java.util.Date\nimport java.util.GregorianCalendar\n\nclass KeepassAUtil private constructor() {\n  companion object {\n    val instance: KeepassAUtil by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {\n      KeepassAUtil()\n    }\n  }\n\n  private var LAST_CLICK_TIME = System.currentTimeMillis()\n\n  fun getAppVersionCode(context: Context): Int {\n    val manager = context.packageManager\n    var code = 0\n    try {\n      val info = manager.getPackageInfo(context.packageName, 0)\n      code = info.versionCode\n    } catch (e: NameNotFoundException) {\n      Timber.e(e)\n    }\n    return code\n  }\n\n  fun getAppVersionName(context: Context): String {\n    val manager = context.packageManager\n    var name: String? = null\n    try {\n      val info = manager.getPackageInfo(context.packageName, 0)\n      name = info.versionName\n    } catch (e: NameNotFoundException) {\n      Timber.e(e)\n    }\n    return name!!\n  }\n\n  /**\n   * 获取进程号对应的进程名\n   *\n   * @param pid 进程号\n   * @return 进程名\n   */\n  fun getProcessName(pid: Int): String? {\n    var reader: BufferedReader? = null\n    try {\n      reader = BufferedReader(FileReader(\"/proc/$pid/cmdline\"))\n      var processName: String = reader.readLine()\n      if (!TextUtils.isEmpty(processName)) {\n        processName = processName.trim { it <= ' ' }\n      }\n      return processName\n    } catch (throwable: Throwable) {\n      Timber.e(throwable)\n    } finally {\n      try {\n        reader?.close()\n      } catch (exception: IOException) {\n        Timber.e(exception)\n      }\n    }\n    return null\n  }\n\n  /**\n   * is auto lock the database\n   * @return true auto lock the database\n   */\n  fun isAutoLockDb(): Boolean {\n    return PreferenceManager.getDefaultSharedPreferences(BaseApp.APP)\n      .getBoolean(BaseApp.APP.getString(R.string.set_key_auto_lock_database), true)\n  }\n\n  /**\n   * is display loading anim\n   * @return true display loading anim\n   */\n  fun isDisplayLoadingAnim(): Boolean {\n    return PreferenceManager.getDefaultSharedPreferences(BaseApp.APP)\n      .getBoolean(BaseApp.APP.getString(R.string.set_key_loading_anim), true)\n  }\n\n  /**\n   * start lock timer\n   * @param obj fragment or activity\n   */\n  fun startLockTimer(obj: Any?) {\n    KpaUtil.scope.launch(Dispatchers.IO) {\n      // delay 1s, in order to wait for the configuration file to take effect\n      delay(1000)\n      if (!isAutoLockDb()) {\n        return@launch\n      }\n      if (isNeedStartLockActivity(obj)) {\n        if (BaseApp.isLocked) {\n          AutoLockDbUtil.get()\n            .startLockWorkerNow()\n          return@launch\n        }\n\n        if (AppUtils.isAppForeground()) {\n          AutoLockDbUtil.get().resetTimer()\n          return@launch\n        }\n      }\n    }\n  }\n\n  /**\n   * lock the db\n   */\n  fun lock() {\n    Timber.d(\"锁定数据库\")\n    BaseApp.isLocked = true\n    val isOpenQuickLock = BaseApp.APP.isCanOpenQuickLock()\n    // 只有应用在前台才会跳转到锁屏页面\n    if (AppUtils.isAppForeground() && BaseApp.KDB != null) {\n      // 开启快速解锁则跳转到快速解锁页面\n      if (isOpenQuickLock) {\n        NotificationUtil.startQuickUnlockNotify(BaseApp.APP)\n        val cActivity = AbsFrame.getInstance().currentActivity\n//        if (cActivity != null && cActivity is QuickUnlockActivity) {\n//          Timber.w(\"快速解锁已启动，不再启动快速解锁\")\n//          return\n//        }\n\n        Timber.d(\"启动快速解锁\")\n        BaseApp.APP.startActivity(Intent(Intent.ACTION_MAIN).also {\n          it.component =\n            ComponentName(\n              BaseApp.APP.packageName,\n              \"${BaseApp.APP.packageName}.view.main.QuickUnlockActivity\"\n            )\n          it.flags = Intent.FLAG_ACTIVITY_NEW_TASK\n        })\n        return\n      }\n\n      // 没有开启快速解锁，则回到启动页\n      NotificationUtil.startDbLocked(BaseApp.APP)\n      val cActivity = AbsFrame.getInstance().currentActivity\n      if (cActivity != null && cActivity is LauncherActivity) {\n        Timber.w(\"解锁页面已启动，不再启动快速解锁\")\n        return\n      }\n      Timber.d(\"快速解锁没有启动，进入解锁界面\")\n      Routerfit.create(ServiceRouter::class.java).getDbSaveService().clearDb()\n      BaseApp.APP.startActivity(Intent(Intent.ACTION_MAIN).also {\n        it.component =\n          ComponentName(\n            BaseApp.APP.packageName,\n            \"${BaseApp.APP.packageName}.view.launcher.LauncherActivity\"\n          )\n        it.flags = Intent.FLAG_ACTIVITY_NEW_TASK\n      })\n      for (ac in AbsFrame.getInstance().activityStack) {\n        if (isHomeActivity(ac)) {\n          continue\n        }\n        ac.finish()\n      }\n      return\n    }\n\n    // 处理处于后台的情况\n    if (isOpenQuickLock) {\n      NotificationUtil.startQuickUnlockNotify(BaseApp.APP)\n      return\n    }\n    Routerfit.create(ServiceRouter::class.java).getDbSaveService().clearDb()\n    NotificationUtil.startDbLocked(BaseApp.APP)\n  }\n\n  fun isHomeActivity(ac: Activity): Boolean {\n    val clazz = ac.javaClass\n    return (clazz == LauncherActivity::class.java\n      || clazz == CreateDbActivity::class.java\n      || clazz == OpenDbHistoryActivity::class.java\n      || clazz == QuickUnlockActivity::class.java\n      )\n  }\n\n  /**\n   * 判断颜色是不是亮色\n   *\n   * @param color\n   * @return\n   * @from https://stackoverflow.com/questions/24260853/check-if-color-is-dark-or-light-in-android\n   */\n  fun isLightColor(@ColorInt color: Int): Boolean {\n    return ColorUtils.calculateLuminance(color) >= 0.5\n  }\n\n  /**\n   * is night mode\n   * @return true yes, false no\n   */\n  fun isNightMode(): Boolean {\n    return BaseApp.APP.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES\n  }\n\n  /**\n   * 是否需要启动快速解锁\n   * @return true 启动快速解锁\n   */\n  private fun isNeedStartLockActivity(obj: Any?): Boolean {\n    if (obj == null) {\n      return false\n    }\n    if (obj is Fragment && obj.activity == null) {\n      return false\n    }\n\n    val clazz = if (obj is Fragment) {\n      obj.requireActivity().javaClass\n    } else {\n      obj.javaClass\n    }\n\n    return (\n      clazz != LauncherActivity::class.java\n        && clazz != QuickUnlockActivity::class.java\n        && clazz != CreateDbActivity::class.java\n        && clazz != AutoFillEntrySearchActivity::class.java\n        && clazz != OpenDbHistoryActivity::class.java\n      )\n  }\n\n  /**\n   * 重新打开数据库\n   * 如果数据库没有打开或者没有启动快速解锁 跳转到数据库打开页面\n   * 否则跳转到快速启动页\n   */\n  fun reOpenDb(context: Context) {\n    if (!BaseApp.APP.isCanOpenQuickLock()) {\n      ARouter.getInstance()\n        .build(\"/launcher/activity\")\n        .withInt(LauncherActivity.KEY_OPEN_TYPE, LauncherActivity.OPEN_TYPE_OPEN_DB)\n        .navigation()\n      for (ac in AbsFrame.getInstance().activityStack) {\n        if (ac is LauncherActivity) {\n          continue\n        }\n        ac.finish()\n      }\n      return\n    }\n    QuickUnlockActivity.startQuickUnlockActivity(context)\n  }\n\n  /**\n   * 回到启动页\n   * @param turnFragment [LauncherActivity.OPEN_TYPE_CHANGE_DB]选择数据库页面，\n   * [LauncherActivity.OPEN_TYPE_OPEN_DB] 打开数据库\n   */\n  fun turnLauncher(turnFragment: Int = LauncherActivity.OPEN_TYPE_CHANGE_DB) {\n    BaseApp.isLocked = true\n    ARouter.getInstance()\n      .build(\"/launcher/activity\")\n      .withInt(LauncherActivity.KEY_OPEN_TYPE, turnFragment)\n      .navigation()\n    for (ac in AbsFrame.getInstance().activityStack) {\n      if (ac is LauncherActivity) {\n        continue\n      }\n      ac.finish()\n    }\n  }\n\n  /**\n   * 检查http地址是否有效\n   * @return false 无效，true 有效\n   */\n  fun checkUrlIsValid(url: String?): Boolean {\n    if (url.isNullOrBlank()) {\n      return false\n    }\n    val rs = \"^(http|https)://[^\\\\s]*\"\n//      \"^(((file|gopher|news|nntp|telnet|http|ftp|https|ftps|sftp)://)|(www\\\\.))+(([a-zA-Z0-9\\\\._-]+\\\\.[a-zA-Z]{2,6})|([0-9]{1,3}\\\\.[0-9]{1,3}\\\\.[0-9]{1,3}\\\\.[0-9]{1,3}))(/[a-zA-Z0-9\\\\&%_\\\\./-~-]*)?\\$\"\n    val r1 = Regex(rs, RegexOption.IGNORE_CASE)\n    return r1.matches(url)\n  }\n\n  /**\n   * 将pwentry 转换为列表实体\n   */\n  fun convertPwEntry2Item(entry: PwEntry): SimpleItemEntity {\n    val item = SimpleItemEntity()\n    item.title = entry.title\n    item.subTitle = if (entry.isRef()) {\n      val refStr = \"${BaseApp.APP.resources.getString(R.string.ref_entry)}: \"\n      val tempStr = \"${refStr}${KdbUtil.getUserName(entry)}\"\n      StringUtil.highLightStr(tempStr, refStr, ResUtil.getColor(R.color.colorPrimary), true)\n    } else {\n      entry.username\n    }\n    item.obj = entry\n    return item\n  }\n\n  /**\n   * 将pwGroup 转换为列表实体\n   */\n  fun convertPwGroup2Item(pwGroup: PwGroup): SimpleItemEntity {\n    val item = SimpleItemEntity()\n    item.title = pwGroup.name\n    item.subTitle = ResUtil.getString(\n      R.string.hint_group_desc, KdbUtil.getGroupEntryNum(pwGroup)\n        .toString()\n    )\n    item.obj = pwGroup\n    return item\n  }\n\n  /**\n   * 保存上一次打开的数据库记录\n   */\n  fun saveLastOpenDbHistory(record: DbHistoryRecord?) {\n    if (record == null) {\n      return\n    }\n    KpaUtil.scope.launch(Dispatchers.IO) {\n      val dao = BaseApp.appDatabase.dbRecordDao()\n      val his = dao.findRecord(record.localDbUri)\n      if (his == null || his.localDbUri.isEmpty()) {\n        record.uid = 0 // 保证uid能自增且不冲突\n        record.time = System.currentTimeMillis()\n        dao.saveRecord(record)\n        BaseApp.dbRecord = record\n        Timber.d(\"保存数据库打开记录成功\")\n        return@launch\n      }\n\n      his.keyUri = record.keyUri\n      his.cloudDiskPath = record.cloudDiskPath\n      his.type = record.type\n      his.time = record.time\n      dao.updateRecord(his)\n      BaseApp.dbRecord = his\n      Timber.d(\"更新数据库打开记录成功\")\n    }\n  }\n\n  /**\n   *  自动填充的response\n   *  @param intent 自动填充服务床进来的intentc\n   *  @param apkPkgName 第三方包名\n   */\n  @TargetApi(Build.VERSION_CODES.O)\n  fun getFillResponse(\n    context: Context,\n    intent: Intent,\n    apkPkgName: String\n  ): Intent {\n    val autoFillStructure = intent.getParcelableExtra<AssistStructure>(\n      AutofillManager.EXTRA_ASSIST_STRUCTURE\n    )\n    if (autoFillStructure == null) {\n      Timber.e(\"autoFillStructure is null\")\n      return Intent()\n    }\n\n    val parser = StructureParser(autoFillStructure)\n    parser.parseForFill(true, apkPkgName)\n    val autofillFields = parser.autoFillFields\n\n    val datas = KDBAutoFillRepository.getAutoFillDataByPackageName(apkPkgName)\n    val response =\n      AutoFillHelper.newResponse(\n        context,\n        true,\n        autofillFields,\n        datas,\n        apkPkgName,\n        autoFillStructure\n      )\n\n    val data = Intent()\n    data.putExtra(LauncherActivity.KEY_PKG_NAME, apkPkgName)\n    data.putExtra(AutofillManager.EXTRA_AUTHENTICATION_RESULT, response)\n    return data\n  }\n\n  /**\n   *  自动填充的response\n   *  @param intent 自动填充服务床进来的intent\n   *  @param pwEntry u数据条目\n   */\n  @TargetApi(Build.VERSION_CODES.O)\n  fun getFillResponse(\n    context: Context,\n    intent: Intent,\n    pwEntry: PwEntry,\n    apkPkgName: String\n  ): Intent {\n    val autoFillStructure = intent.getParcelableExtra<AssistStructure>(\n      AutofillManager.EXTRA_ASSIST_STRUCTURE\n    )\n    if (autoFillStructure == null) {\n      Timber.e(\"autoFillStructure is null\")\n      return Intent()\n    }\n    val parser = StructureParser(autoFillStructure)\n    parser.parseForFill(true, apkPkgName)\n    val autofillFields = parser.autoFillFields\n\n    val response =\n      AutoFillHelper.newResponse(\n        context,\n        true,\n        autofillFields,\n        arrayListOf(pwEntry),\n        apkPkgName,\n        autoFillStructure\n      )\n\n    val data = Intent()\n    data.putExtra(AutofillManager.EXTRA_AUTHENTICATION_RESULT, response)\n    return data\n  }\n\n  /**\n   * 转换uri\n   */\n  fun convertUri(uriString: String?): Uri? {\n    if (uriString == null) {\n      return null\n    }\n    if (uriString.equals(\"null\", ignoreCase = true)) {\n      return null\n    }\n    val temp = Uri.parse(uriString)\n//    val ub = Uri.Builder()\n//    ub.authority(\"com.android.externalstorage.documents\") // 必须设置，否则8.0上会出现崩溃的问题\n//    val uri = ub.scheme(temp.scheme)\n//        .path(temp.path)\n//        .appendPath(temp.path)\n//        .encodedPath(temp.encodedPath)\n//        .fragment(temp.fragment)\n//        .encodedFragment(temp.encodedFragment)\n//        .query(temp.query)\n//        .encodedQuery(temp.encodedQuery)\n//        .build()\n//    Log.d(TAG, \"new uri = $uri\")\n//    Log.d(TAG, \"temp = $temp\")\n//    Log.d(TAG, \"uriString = $uriString\")\n    return temp\n  }\n\n  fun isFastClick(): Boolean {\n    val isFast = System.currentTimeMillis() - LAST_CLICK_TIME < 400\n    LAST_CLICK_TIME = System.currentTimeMillis()\n    return isFast\n  }\n\n  /**\n   * 截取并保存短密码\n   */\n  fun subShortPass() {\n    val sh = PreferenceManager.getDefaultSharedPreferences(BaseApp.APP)\n    val subType = sh.getString(BaseApp.APP.getString(R.string.set_quick_pass_type), \"1\")!!\n      .toString()\n      .toInt()\n    val passLen = sh.getString(BaseApp.APP.getString(R.string.set_quick_pass_len), \"3\")!!\n      .toString()\n      .toInt()\n    val masterPass = QuickUnLockUtil.decryption(BaseApp.dbPass)\n    var shortPass = \"\"\n    Timber.i(\"截取短密码，长度：$passLen，截取类型：$subType\")\n    when (subType) {\n      // 前面位\n      1 -> {\n        shortPass =\n          if (masterPass.length <= passLen) masterPass else masterPass.substring(0, passLen)\n      }\n      // 末尾\n      2 -> {\n        shortPass =\n          if (masterPass.length <= passLen) masterPass else masterPass.substring(\n            masterPass.length - passLen, masterPass.length\n          )\n      }\n    }\n    Timber.d(\"shortPass = $shortPass\")\n    BaseApp.shortPass = QuickUnLockUtil.encryptStr(shortPass)\n  }\n\n  /**\n   * 格式化时间\n   */\n  @SuppressLint(\"SimpleDateFormat\")\n  fun formatTime(time: Date?): String {\n    val format = SimpleDateFormat(\" yyyy/MM/dd HH:mm\")\n    if (time == null) {\n      return format.format(GregorianCalendar(1970, 1, 1, 0, 0, 0))\n    }\n\n    return format.format(time)\n  }\n\n  @SuppressLint(\"SimpleDateFormat\")\n  fun formatTime(\n    time: Date,\n    format: String\n  ): String {\n    return SimpleDateFormat(format).format(time)\n  }\n\n  /**\n   * 跳转群组详情或项目详情\n   */\n  fun turnEntryDetail(\n    activity: FragmentActivity,\n    entry: PwDataInf,\n    showElement: View? = null\n  ) {\n    if (entry is PwGroup) {\n      Routerfit.create(ActivityRouter::class.java, activity).toGroupDetailActivity(\n        groupName = entry.name,\n        groupId = entry.id,\n        opt = ActivityOptionsCompat.makeSceneTransitionAnimation(activity)\n      )\n      return\n    }\n\n    if (entry is PwEntry) {\n      val opt = if (showElement != null) {\n        val pair = androidx.core.util.Pair(\n          showElement, activity.getString(R.string.transition_entry_icon)\n        )\n        ActivityOptionsCompat.makeSceneTransitionAnimation(activity, pair)\n      } else {\n        ActivityOptionsCompat.makeSceneTransitionAnimation(activity)\n      }\n      Routerfit.create(ActivityRouter::class.java, activity).toEntryDetailActivity(\n        entryId = entry.uuid,\n        opt = opt\n      )\n    }\n  }\n\n  /**\n   * @param requestCode 请求码\n   * @param obj activity 或 fragment\n   * @param type mime\n   *\n   * @see <a href=\"https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Complete_list_of_MIME_types\">mime</a>\n   */\n  fun openSysFileManager(\n    obj: Any,\n    type: String,\n    requestCode: Int\n  ) {\n    try {\n      Intent.ACTION_GET_CONTENT\n      val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {\n        this.type = type\n        addCategory(Intent.CATEGORY_OPENABLE)\n        addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)\n        addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)\n\n      }\n      if (obj is Activity) {\n        obj.startActivityForResult(intent, requestCode)\n      } else if (obj is Fragment) {\n        obj.startActivityForResult(intent, requestCode)\n      }\n    } catch (e: Exception) {\n      Timber.e(\"打开文件失败\")\n      Timber.e(e)\n    }\n  }\n\n  /**\n   * 使用ASF创建文件\n   * @param obj activity 或 fragment\n   * @param mimeType mime\n   * @see <a href=\"https://developer.android.com/guide/topics/providers/document-provider?hl=zh-cn#create\">创建文档</a>\n   */\n  @Deprecated(\"请使用registerForActivityResult(ActivityResultContracts.CreateDocument())\")\n  fun createFile(\n    obj: Any,\n    mimeType: String,\n    fileName: String,\n    requestCode: Int\n  ) {\n    try {\n      val intent = Intent(Intent.ACTION_CREATE_DOCUMENT)\n      intent.addCategory(Intent.CATEGORY_OPENABLE)\n      intent.type = mimeType\n      intent.putExtra(Intent.EXTRA_TITLE, fileName)\n      if (obj is Activity) {\n        obj.startActivityForResult(intent, requestCode)\n      } else if (obj is Fragment) {\n        obj.startActivityForResult(intent, requestCode)\n      }\n    } catch (e: Exception) {\n      Timber.e(e)\n    }\n  }\n\n  /**\n   * 打开或隔壁键盘\n   */\n  fun toggleKeyBord(context: Context) {\n    val imm = getSystemService(context, InputMethodManager::class.java)\n    imm!!.toggleSoftInput(InputMethodManager.HIDE_IMPLICIT_ONLY, 0)\n  }\n\n  /**\n   * 从uri中获取文件长度\n   */\n  fun getFileSizeFormUri(\n    context: Context,\n    uri: Uri\n  ): Long {\n    if (\"content\".equals(uri.scheme, false)) {\n      val df = DocumentFile.fromSingleUri(context, uri)\n\n      if (df != null) {\n        return df.length()\n      }\n\n      if (!UriUtil.checkPermissions(context, uri)) {\n        Timber.e(\"uri没有授权：$uri\")\n        return 0\n      }\n\n      val cursor = context.contentResolver.query(uri, null, null, null, null, null)\n      if (cursor != null && cursor.moveToFirst()) {\n        val size = cursor.getLong(cursor.getColumnIndex(OpenableColumns.SIZE))\n\n        cursor.close()\n        return size\n      }\n    } else if (uri.scheme.equals(\"file\", false)) {\n      return File(uri.path).length()\n    }\n\n    return 0\n  }\n\n  /**\n   * 从uri 中获取文件的路径，没用!!\n   * 该方法的拷贝地址：https://stackoverflow.com/questions/13209494/how-to-get-the-full-file-path-from-uri\n   * 如果只是想使用流，可以使用 contentResolver.openOutputStream(uri) 获取流\n   */\n  @SuppressLint(\"ObsoleteSdkInt\") fun getFilePathFormUri(\n    context: Context,\n    uri: Uri\n  ): String? {\n    var tempUri = uri\n    val needToCheckUri = VERSION.SDK_INT >= 19\n    var selection: String? = null\n    var selectionArgs: Array<String>? = null\n    Timber.d(\"uri = $uri\")\n    if (needToCheckUri && DocumentsContract.isDocumentUri(context.applicationContext, tempUri)) {\n      when {\n        isExternalStorageDocument(tempUri) -> {\n          val docId = DocumentsContract.getDocumentId(tempUri)\n          val split = docId.split(\":\")\n            .toTypedArray()\n          return Environment.getExternalStorageDirectory()\n            .toString() + \"/\" + split[1]\n        }\n\n        isDownloadsDocument(tempUri) -> {\n          val id = DocumentsContract.getDocumentId(tempUri)\n          if (id.startsWith(\"raw\", ignoreCase = true)) {\n            val temp = Uri.parse(id)\n//              val path = temp.path\n//              val s = temp.scheme\n            return temp.path\n          } else if (isNumeric(id)) {\n            tempUri = ContentUris.withAppendedId(\n              Uri.parse(\"content://downloads/public_downloads\"),\n              id.toLong()\n            )\n          } else if (id.startsWith(\"msf\", ignoreCase = true)) {\n            // android 10 的问题，一样有问题！！\n            tempUri = ContentUris.withAppendedId(\n              Uri.parse(\"content://downloads/public_downloads\"), id.split(\":\")[1].toLong()\n            )\n            Timber.d(\"msf Uri = $tempUri\")\n          }\n        }\n\n        isMediaDocument(tempUri) -> {\n          val docId = DocumentsContract.getDocumentId(tempUri)\n          val split = docId.split(\":\")\n            .toTypedArray()\n          val type = split[0]\n          if (\"image\" == type) {\n            tempUri = Media.EXTERNAL_CONTENT_URI\n          } else if (\"video\" == type) {\n            tempUri = Video.Media.EXTERNAL_CONTENT_URI\n          } else if (\"audio\" == type) {\n            tempUri = Audio.Media.EXTERNAL_CONTENT_URI\n          }\n        }\n      }\n    }\n    if (\"content\".equals(tempUri.scheme, ignoreCase = true)) {\n      val projection = arrayOf(Media.DATA)\n      var url = \"\"\n      val cursor: Cursor?\n      try {\n        cursor = context.contentResolver\n          .query(uri, projection, selection, selectionArgs, null)\n        if (cursor == null) {\n          return null\n        }\n        val columnIndex: Int = cursor.getColumnIndexOrThrow(Media.DATA)\n\n        if (cursor.moveToFirst()) {\n          url = cursor.getString(columnIndex)\n        }\n        cursor.close()\n      } catch (e: Exception) {\n      }\n      return url\n    } else if (\"file\".equals(tempUri.scheme, ignoreCase = true)) {\n      return tempUri.path\n    }\n    return null\n  }\n\n  fun isNumeric(str: String?): Boolean {\n    val bigStr: String = try {\n      BigDecimal(str).toString()\n    } catch (e: java.lang.Exception) {\n      return false //异常 说明包含非数字。\n    }\n    return true\n  }\n\n  /**\n   * @param uri The Uri to check.\n   * @return Whether the Uri authority is ExternalStorageProvider.\n   */\n  private fun isExternalStorageDocument(uri: Uri): Boolean {\n    return \"com.android.externalstorage.documents\" == uri.authority\n  }\n\n  /**\n   * @param uri The Uri to check.\n   * @return Whether the Uri authority is DownloadsProvider.\n   */\n  private fun isDownloadsDocument(uri: Uri): Boolean {\n    return \"com.android.providers.downloads.documents\" == uri.authority\n  }\n\n  /**\n   * @param uri The Uri to check.\n   * @return Whether the Uri authority is MediaProvider.\n   */\n  private fun isMediaDocument(uri: Uri): Boolean {\n    return \"com.android.providers.media.documents\" == uri.authority\n  }\n}\n\nfun Uri.getFileInfo(\n  context: Context\n): Pair<String?, Long?> {\n  return Pair(\n    UriUtil.getFileNameFromUri(context, this),\n    KeepassAUtil.instance.getFileSizeFormUri(context, this)\n  )\n}\n\nfun PwEntry.isRef(): Boolean {\n  return (!username.isNullOrEmpty() && username.startsWith(\"{REF:\", ignoreCase = true))\n    || (!password.isNullOrEmpty() && password.startsWith(\"{REF:\", ignoreCase = true))\n}\n\n/**\n * uri 授权\n */\nfun Uri.takePermission() {\n  try {\n    val takeFlags =\n      Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION\n    BaseApp.APP.contentResolver.takePersistableUriPermission(this, takeFlags)\n  } catch (e: Exception) {\n    HitUtil.toaskShort(BaseApp.APP.getString(R.string.error_uri_grant_permission))\n    Timber.e(e)\n  }\n}\n"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/util/KpaExtensions.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.util\n\nimport android.annotation.SuppressLint\nimport android.app.Activity\nimport android.net.Uri\nimport android.text.TextUtils\nimport android.view.MenuInflater\nimport android.view.MenuItem\nimport android.view.MotionEvent\nimport android.view.View\nimport androidx.appcompat.view.menu.MenuPopupHelper\nimport androidx.appcompat.widget.PopupMenu\nimport androidx.preference.PreferenceManager\nimport androidx.recyclerview.widget.RecyclerView\nimport androidx.recyclerview.widget.RecyclerView.OnItemTouchListener\nimport com.arialyy.frame.util.ReflectionUtil\nimport com.arialyy.frame.util.ResUtil\nimport com.arialyy.frame.util.adapter.RvItemClickSupport\nimport com.keepassdroid.database.PwEntry\nimport com.keepassdroid.database.PwEntryV4\nimport com.keepassdroid.database.security.ProtectedString\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseApp\nimport com.lyy.keepassa.base.Constance\nimport com.lyy.keepassa.entity.HmacOtpBean\nimport com.lyy.keepassa.entity.KeepassBean\nimport com.lyy.keepassa.entity.KeepassXcBean\nimport com.lyy.keepassa.entity.TimeOtp2Bean\nimport com.lyy.keepassa.entity.TrayTotpBean\nimport com.lyy.keepassa.util.totp.ComposeKeeTrayTotp\nimport com.lyy.keepassa.util.totp.ComposeKeepass\nimport com.lyy.keepassa.util.totp.ComposeKeepass.HmacOtp_Secret\nimport com.lyy.keepassa.util.totp.ComposeKeepass.TimeOtp_Secret\nimport com.lyy.keepassa.util.totp.ComposeKeepassxc\nimport com.lyy.keepassa.util.totp.ComposeKeepassxc.KEY_STEAM\nimport com.lyy.keepassa.util.totp.OtpUtil\nimport com.lyy.keepassa.util.totp.SecretHexType\nimport com.lyy.keepassa.util.totp.TokenCalculator\nimport com.lyy.keepassa.util.totp.TokenCalculator.HashAlgorithm\nimport timber.log.Timber\n\nval charRegex = Regex(\"[^a-zA-Z0-9]\")\n\nenum class ClickScope {\n  /**\n   * 仅在该View中有效\n   */\n  VIEW,\n\n  /**\n   * 全局的\n   */\n  SYS\n}\n\nprivate var lastClickTime = -1L\n\n/**\n * 时间间隔\n * @param clickScope  时间间隔，[ClickScope.VIEW]，[ClickScope.SYS]\n */\nfun View.doClick(\n  intervalTime: Long = 1000,\n  clickScope: ClickScope = ClickScope.VIEW,\n  body: (View) -> Unit\n) {\n  var curTime = -1L\n  fun viewScopeClick(it: View) {\n    if (curTime == -1L || kotlin.math.abs(System.currentTimeMillis() - curTime) > intervalTime) {\n      curTime = System.currentTimeMillis()\n      body.invoke(it)\n      return\n    }\n    Timber.d(\"间隔太短\")\n  }\n\n  fun sysScopeClick(it: View) {\n    if (kotlin.math.abs(System.currentTimeMillis() - lastClickTime) > intervalTime) {\n      lastClickTime = System.currentTimeMillis()\n      body.invoke(it)\n      return\n    }\n    Timber.d(\"间隔太短\")\n  }\n\n  setOnClickListener {\n    if (intervalTime == 0L) {\n      body.invoke(it)\n      return@setOnClickListener\n    }\n\n    if (clickScope == ClickScope.VIEW) {\n      viewScopeClick(it)\n      return@setOnClickListener\n    }\n\n    if (clickScope == ClickScope.SYS) {\n      sysScopeClick(it)\n      return@setOnClickListener\n    }\n\n    Timber.d(\"间隔太短\")\n  }\n}\n\n@SuppressLint(\"RestrictedApi\")\nfun PopupMenu.init(menuId: Int, onItemCLick: (MenuItem) -> Unit): PopupMenu {\n  val inflater: MenuInflater = menuInflater\n  inflater.inflate(menuId, this.menu)\n  // 以下代码为强制显示icon\n  val mPopup = ReflectionUtil.getField(PopupMenu::class.java, \"mPopup\")\n  mPopup.isAccessible = true\n  val help = mPopup.get(this) as MenuPopupHelper\n  help.setForceShowIcon(true)\n  setOnMenuItemClickListener {\n    onItemCLick.invoke(it)\n    return@setOnMenuItemClickListener true\n  }\n  return this\n}\n\nfun Activity.isDestroy() = isDestroyed || isFinishing\n\n/**\n * @return true has special char\n */\nfun CharSequence.hasSpecialChar(): Boolean {\n  return charRegex.containsMatchIn(this)\n}\n\n/**\n * isOpenQuickLock\n * @return true already open quick lock\n */\nfun BaseApp.isCanOpenQuickLock(): Boolean {\n  return PreferenceManager.getDefaultSharedPreferences(this)\n    .getBoolean(applicationContext.getString(R.string.set_quick_unlock), false)\n    && BaseApp.dbRecord != null\n    && !KpaUtil.isEmptyPass()\n}\n\nfun PwEntryV4.hasNote(): Boolean {\n  for (str in this.strings) {\n    if (str.key.equals(PwEntryV4.STR_NOTES, true) && !TextUtils.isEmpty(str.value.toString())) {\n      return true\n    }\n  }\n  return false\n}\n\nfun Map<String, ProtectedString>.hasTOTP(): Boolean {\n  for (str in this) {\n    if (str.key.equals(PwEntryV4.STR_NOTES, true)\n      || str.key.equals(PwEntryV4.STR_PASSWORD, true)\n      || str.key.equals(PwEntryV4.STR_TITLE, true)\n      || str.key.equals(PwEntryV4.STR_URL, true)\n      || str.key.equals(PwEntryV4.STR_USERNAME, true)\n    ) {\n      continue\n    }\n\n    // 增加TOP密码字段\n    if (str.key.startsWith(\"TOTP\", ignoreCase = true)\n      || str.key.startsWith(\"OTP\", ignoreCase = true)\n      || str.key.startsWith(ComposeKeepass.HmacOtp, ignoreCase = true)\n      || str.key.startsWith(ComposeKeepass.TimeOtp, ignoreCase = true)\n    ) {\n      return true\n    }\n  }\n  return false\n}\n\nfun PwEntryV4.hasTOTP(): Boolean {\n  return strings.hasTOTP()\n}\n\nfun PwEntryV4.isCollectioned(): Boolean {\n  val value = strings[Constance.KPA_IS_COLLECTION]\n  return value != null && value.toString().equals(\"true\", true)\n}\n\nfun PwEntryV4.setCollection(isCollection: Boolean) {\n  this.strings[Constance.KPA_IS_COLLECTION] = ProtectedString(false, isCollection.toString())\n}\n\nfun PwEntry.copyUserName() {\n  val userName = KdbUtil.getUserName(this)\n  ClipboardUtil.get()\n    .copyDataToClip(userName)\n  HitUtil.toaskShort(ResUtil.getString(R.string.hint_copy_user))\n}\n\nfun PwEntry.copyPassword() {\n  val pass = KdbUtil.getPassword(this)\n  ClipboardUtil.get()\n    .copyDataToClip(pass)\n  HitUtil.toaskShort(ResUtil.getString(R.string.hint_copy_pass))\n}\n\nfun PwEntryV4.copyTotp() {\n  val pass = OtpUtil.getOtpPass(this).second\n  if (pass.isNullOrBlank()) {\n    HitUtil.toaskShort(ResUtil.getString(R.string.totp_key_error))\n    return\n  }\n  ClipboardUtil.get()\n    .copyDataToClip(pass)\n  HitUtil.toaskShort(ResUtil.getString(R.string.hint_copy_totp))\n}\n\ninline fun RecyclerView.doOnItemClickListener(\n  crossinline action: (\n    rv: RecyclerView,\n    position: Int,\n    v: View\n  ) -> Unit\n) {\n  RvItemClickSupport.addTo(this)\n    .setOnItemClickListener { rv, position, v ->\n      return@setOnItemClickListener action.invoke(rv, position, v)\n    }\n}\n\ninline fun RecyclerView.doOnItemLongClickListener(\n  crossinline action: (\n    rv: RecyclerView,\n    position: Int,\n    v: View\n  ) -> Boolean\n) {\n  RvItemClickSupport.addTo(this)\n    .setOnItemLongClickListener { rv, position, v ->\n      return@setOnItemLongClickListener action.invoke(rv, position, v)\n    }\n}\n\ninline fun RecyclerView.doOnTouchEvent(\n  crossinline action: (\n    rv: RecyclerView,\n    e: MotionEvent\n  ) -> Unit\n) = addOnItemTouchListener(onTouchEvent = action)\n\ninline fun RecyclerView.doOnInterceptTouchEvent(\n  crossinline action: (rv: RecyclerView, e: MotionEvent) -> Boolean\n) = addOnItemTouchListener(onInterceptTouchEvent = action)\n\ninline fun RecyclerView.doOnRequestDisallowInterceptTouchEvent(\n  crossinline action: (disallowIntercept: Boolean) -> Unit\n) = addOnItemTouchListener(onRequestDisallowInterceptTouchEvent = action)\n\ninline fun RecyclerView.addOnItemTouchListener(\n  crossinline onTouchEvent: (\n    rv: RecyclerView,\n    e: MotionEvent\n  ) -> Unit = { _, _ -> },\n  crossinline onInterceptTouchEvent: (\n    rv: RecyclerView,\n    e: MotionEvent\n  ) -> Boolean = { _, _ -> false },\n  crossinline onRequestDisallowInterceptTouchEvent: (\n    disallowIntercept: Boolean\n  ) -> Unit = { _ -> }\n): OnItemTouchListener {\n  val touchListener = object : OnItemTouchListener {\n    override fun onTouchEvent(\n      rv: RecyclerView,\n      e: MotionEvent\n    ) {\n      onTouchEvent.invoke(rv, e)\n    }\n\n    override fun onInterceptTouchEvent(\n      rv: RecyclerView,\n      e: MotionEvent\n    ): Boolean {\n      return onInterceptTouchEvent.invoke(rv, e)\n    }\n\n    override fun onRequestDisallowInterceptTouchEvent(disallowIntercept: Boolean) {\n      onRequestDisallowInterceptTouchEvent.invoke(disallowIntercept)\n    }\n  }\n\n  addOnItemTouchListener(touchListener)\n  return touchListener\n}\n\nfun PwEntryV4.removeAttrFile(key: String) {\n  binaries.remove(key)\n}\n\nfun PwEntryV4.removeAttrStr(str: String) {\n  strings.remove(str)\n}\n\nfun PwEntryV4.otpIsKeeTrayTotp(): Boolean {\n  return strings[ComposeKeeTrayTotp.KEY_SETTING] != null\n}\n\nfun PwEntryV4.otpIsKeeTraySteam(): Boolean {\n  val otpSettings = strings[ComposeKeeTrayTotp.KEY_SETTING]\n  if (otpSettings != null) {\n    val tempArray = otpSettings.toString()\n      .split(\";\")\n    return tempArray[1] == \"S\"\n  }\n\n  return false\n}\n\nfun PwEntryV4.getKeeTrayBean(): TrayTotpBean {\n  if (!otpIsKeeTrayTotp()) {\n    throw IllegalAccessException(\"not kray otp\")\n  }\n  val totpSetting = strings[ComposeKeeTrayTotp.KEY_SETTING]\n  val array = totpSetting.toString()\n    .split(\";\")\n  return TrayTotpBean(\n    secret = strings[ComposeKeeTrayTotp.KEY_SEED].toString(),\n    period = array[0].toInt(),\n    isSteam = array[1] == \"s\"\n  )\n}\n\nfun PwEntryV4.otpIsKeepassXcSteam(): Boolean {\n  val seed = strings[ComposeKeepassxc.KEY_SEED]?.toString()\n  val uri = Uri.parse(seed)\n  val encoder = uri.getQueryParameter(ComposeKeepassxc.KEY_ENCODER)\n\n  if (encoder != null && encoder.equals(KEY_STEAM, ignoreCase = true)) {\n    return true\n  }\n  return false\n}\n\nfun PwEntryV4.otpKeepassXC(): Boolean {\n  return strings[ComposeKeepassxc.KEY_SEED]?.toString()\n    ?.startsWith(\"otpauth\", ignoreCase = true) == true\n}\n\nfun PwEntryV4.getKeepassXcBean(): KeepassXcBean {\n  if (!otpKeepassXC()) {\n    throw IllegalAccessException(\"not keepassxc otp\")\n  }\n  val seed = strings[ComposeKeepassxc.KEY_SEED]?.toString()\n  val uri = Uri.parse(seed)\n  val algorithm = uri.getQueryParameter(ComposeKeepassxc.KEY_ALGORITHM)\n\n  return KeepassXcBean(\n    host = uri.host ?: \"totp\",\n    title = getRealTitle(),\n    userName = getRealUserName(),\n    isSteam = otpIsKeepassXcSteam(),\n    encoder = uri.getQueryParameter(ComposeKeepassxc.KEY_ENCODER) ?: \"\",\n    secret = uri.getQueryParameter(ComposeKeepassxc.KEY_SECRET) ?: \"\",\n    issuer = uri.getQueryParameter(ComposeKeepassxc.KEY_ISSUER) ?: \"\",\n    period = uri.getQueryParameter(ComposeKeepassxc.KEY_PERIOD)?.toInt()\n      ?: TokenCalculator.TOTP_DEFAULT_PERIOD,\n    digits = uri.getQueryParameter(ComposeKeepassxc.KEY_DIGITS)?.toInt()\n      ?: TokenCalculator.TOTP_DEFAULT_DIGITS,\n    algorithm = when (algorithm) {\n      \"SHA256\" -> HashAlgorithm.SHA256\n      \"SHA512\" -> HashAlgorithm.SHA512\n      else -> HashAlgorithm.SHA1\n    },\n    counter = uri.getQueryParameter(ComposeKeepassxc.KEY_COUNTER) ?: \"\",\n  )\n}\n\nfun PwEntryV4.otpKeepass(): Boolean {\n  for (str in strings) {\n    val key = str.toString()\n    if (key.startsWith(TimeOtp_Secret) || key.startsWith(HmacOtp_Secret)) {\n      return true\n    }\n  }\n  return false\n}\n\nfun PwEntryV4.getKeepassBean(): KeepassBean {\n  var otpBean: TimeOtp2Bean? = null\n  var hmacBean: HmacOtpBean? = null\n\n  fun isHmacOtp(): Boolean {\n    strings.forEach {\n      if (it.toString().startsWith(ComposeKeepass.HmacOtp)) {\n        return true\n      }\n    }\n    return false\n  }\n\n  fun isTotp(): Boolean {\n    strings.forEach {\n      if (it.toString().startsWith(ComposeKeepass.TimeOtp)) {\n        return true\n      }\n    }\n    return false\n  }\n\n  if (isHmacOtp()) {\n    val secretType: SecretHexType\n    val secret = when {\n      strings[ComposeKeepass.HmacOtp_Secret_Base32] != null -> {\n        secretType = SecretHexType.BASE_32\n        strings[ComposeKeepass.HmacOtp_Secret_Base32].toString()\n      }\n\n      strings[ComposeKeepass.HmacOtp_Secret_Base64] != null -> {\n        secretType = SecretHexType.BASE_64\n        strings[ComposeKeepass.HmacOtp_Secret_Base64].toString()\n      }\n\n      strings[ComposeKeepass.HmacOtp_Secret_Hex] != null -> {\n        secretType = SecretHexType.HEX\n        strings[ComposeKeepass.HmacOtp_Secret_Hex].toString()\n      }\n\n      else -> {\n        secretType = SecretHexType.UTF_8\n        strings[HmacOtp_Secret].toString()\n      }\n    }\n\n    hmacBean = HmacOtpBean(\n      secretType = secretType,\n      secret = secret,\n      algorithm = HashAlgorithm.SHA1,\n      counter = strings[ComposeKeepass.HmacOtp_Counter]?.toString()?.toInt()\n        ?: TokenCalculator.HOTP_INITIAL_COUNTER,\n      len = TokenCalculator.TOTP_DEFAULT_DIGITS\n    )\n  }\n\n  if (isTotp()) {\n    val secretType: SecretHexType\n    val secret = when {\n      strings[ComposeKeepass.TimeOtp_Secret_Base32] != null -> {\n        secretType = SecretHexType.BASE_32\n        strings[ComposeKeepass.TimeOtp_Secret_Base32].toString()\n      }\n\n      strings[ComposeKeepass.TimeOtp_Secret_Base64] != null -> {\n        secretType = SecretHexType.BASE_64\n        strings[ComposeKeepass.TimeOtp_Secret_Base64].toString()\n      }\n\n      strings[ComposeKeepass.TimeOtp_Secret_Hex] != null -> {\n        secretType = SecretHexType.HEX\n        strings[ComposeKeepass.TimeOtp_Secret_Hex].toString()\n      }\n\n      else -> {\n        secretType = SecretHexType.UTF_8\n        strings[TimeOtp_Secret].toString()\n      }\n    }\n\n    val algorithm = when (strings[ComposeKeepass.TimeOtp_Algorithm].toString()) {\n      ComposeKeepass.HMAC_SHA_256 -> HashAlgorithm.SHA256\n      ComposeKeepass.HMAC_SHA_512 -> HashAlgorithm.SHA512\n      else -> HashAlgorithm.SHA1\n    }\n\n    otpBean = TimeOtp2Bean(\n      secretType = secretType,\n      secret = secret,\n      digits = strings[ComposeKeepass.TimeOtp_Length]?.toString()?.toInt()\n        ?: TokenCalculator.TOTP_DEFAULT_DIGITS,\n      algorithm = algorithm,\n      period = strings[ComposeKeepass.TimeOtp_Period]?.toString()?.toInt()\n        ?: TokenCalculator.TOTP_DEFAULT_PERIOD\n    )\n  }\n\n  return KeepassBean(\n    otpBean,\n    hmacBean\n  )\n}\n\nfun PwEntryV4.otpIsKeepOtp(): Boolean {\n  val seed = strings[\"otp\"]?.toString()\n  if (seed?.startsWith(\"key\") == true) {\n    return true\n  }\n  return false\n}\n\n/**\n * 判断是否是KeeOtp2插件\n */\nfun PwEntryV4.otpIsKeeOtp2(): Boolean {\n  for (str in strings) {\n    val key = str.toString()\n    if (key.startsWith(\"TimeOtp\") || key.startsWith(\"HmacOtp\")) {\n      return true\n    }\n  }\n  return false\n}\n\nfun PwEntry.getRealTitle(): String {\n  if (BaseApp.KDB?.pm == null) {\n    return \"\"\n  }\n  return if (isRef()) getTitle(true, BaseApp.KDB!!.pm) else title\n}\n\nfun PwEntry.getRealUserName(): String {\n  if (BaseApp.KDB?.pm == null) {\n    return \"\"\n  }\n\n  return if (isRef())\n    getUsername(true, BaseApp.KDB!!.pm)\n  else\n    username\n}\n\nfun PwEntry.getRealPass(): String {\n  if (BaseApp.KDB?.pm == null) {\n    return \"\"\n  }\n\n  return if (isRef())\n    getPassword(true, BaseApp.KDB!!.pm)\n  else\n    password\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/util/KpaListEntryExtensions.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\npackage com.lyy.keepassa.util\n\nimport com.arialyy.frame.util.ResUtil\nimport com.keepassdroid.database.PwEntryV4\nimport com.keepassdroid.database.PwGroupV4\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseApp\nimport com.lyy.keepassa.entity.SimpleItemEntity\nimport com.lyy.keepassa.view.SimpleEntryAdapter\nimport timber.log.Timber\n\n/**\n * Check whether the entry is in the follow group\n */\nfun PwEntryV4.checkGroupIsParent(group: PwGroupV4?): Boolean {\n  if (group == null){\n    return false\n  }\n  return this.parent == group\n}\n\n/**\n * Check whether the entry is in the same group leve\n */\nfun PwEntryV4.checkInGroupLeve(group: PwGroupV4): Boolean {\n  group.childGroups.forEach {\n    if (this.parent == it) {\n      return true\n    }\n  }\n  return false\n}\n\nprivate fun updateEntryModify(itemEntity: SimpleItemEntity){\n  KpaUtil.updateEntryItemInfo(itemEntity)\n}\n\n/**\n * update the status of deleted items\n */\nfun SimpleEntryAdapter.deleteEntry(\n  entryList: MutableList<SimpleItemEntity>,\n  entry: PwEntryV4,\n  oldParentGroup: PwGroupV4,\n  dirGroup: PwGroupV4?\n) {\n\n  if (oldParentGroup == dirGroup || dirGroup == null) {\n    val entryItem =\n      entryList.find { (it.obj is PwEntryV4) && (it.obj as PwEntryV4).uuid == entry.uuid }\n    if (entryItem != null) {\n      val index = entryList.indexOf(entryItem)\n      if (index != -1) {\n        entryList.removeAt(index)\n        notifyItemRemoved(index)\n      }\n    }\n  }\n\n  if (oldParentGroup == BaseApp.KDB.pm.rootGroup) {\n    val recycleBinItem = entryList.find { it.obj == BaseApp.KDB.pm.recycleBin }\n    if (recycleBinItem != null) {\n      val index = entryList.indexOf(recycleBinItem)\n      if (index != -1) {\n        recycleBinItem.subTitle =\n          ResUtil.getString(\n            R.string.hint_group_desc,\n            KdbUtil.getGroupEntryNum(BaseApp.KDB.pm.recycleBin)\n          )\n        notifyItemChanged(index)\n      }\n    }\n  }\n\n  if (dirGroup?.childGroups?.contains(oldParentGroup) == true) {\n    entryList.forEachIndexed { index, it ->\n      if (it.obj == oldParentGroup) {\n        it.subTitle =\n          ResUtil.getString(R.string.hint_group_desc, KdbUtil.getGroupEntryNum(oldParentGroup))\n        notifyItemChanged(index)\n        return\n      }\n    }\n  }\n  Timber.d(\"The entry is not from the home page, title = ${entry.title}\")\n}\n\n/**\n * update the status of modified items\n */\nfun SimpleEntryAdapter.updateModifyEntry(\n  entryList: MutableList<SimpleItemEntity>,\n  pwEntryV4: PwEntryV4,\n  dirGroup: PwGroupV4\n) {\n  if (pwEntryV4.checkGroupIsParent(dirGroup)) {\n    entryList.forEachIndexed { index, item ->\n      if (item.obj !is PwEntryV4){\n        return@forEachIndexed\n      }\n      if ((item.obj as PwEntryV4).uuid == pwEntryV4.uuid) {\n        updateEntryModify(item)\n        notifyItemChanged(index)\n        return\n      }\n    }\n    return\n  }\n  Timber.d(\"The entry is not from the root page, title = ${pwEntryV4.title}\")\n}\n\n/**\n * update root list state\n */\nfun SimpleEntryAdapter.createNewEntry(\n  entryList: MutableList<SimpleItemEntity>,\n  pwEntryV4: PwEntryV4,\n  dirGroup: PwGroupV4\n) {\n  if (pwEntryV4.checkGroupIsParent(dirGroup)) {\n    val index = entryList.size\n    entryList.add(KeepassAUtil.instance.convertPwEntry2Item(pwEntryV4))\n    notifyItemInserted(index)\n    // notifyDataSetChanged()\n    return\n  }\n  if (pwEntryV4.checkInGroupLeve(dirGroup)) {\n    entryList.forEachIndexed { index, simpleItemEntity ->\n      if (simpleItemEntity.obj == pwEntryV4.parent) {\n        simpleItemEntity.subTitle =\n          ResUtil.getString(R.string.hint_group_desc, KdbUtil.getGroupEntryNum(pwEntryV4.parent))\n        notifyItemChanged(index)\n        return\n      }\n    }\n  }\n\n  Timber.d(\"The entry is not from the home page, title = ${pwEntryV4.title}\")\n}\n\n/**\n * move entry from other group\n * @param oldParentGroup Group before the item is moved\n * @param dirGroup The group directory currently displayed\n */\nfun SimpleEntryAdapter.moveEntry(\n  entryList: MutableList<SimpleItemEntity>,\n  entry: PwEntryV4,\n  oldParentGroup: PwGroupV4,\n  dirGroup: PwGroupV4\n) {\n\n  // 检查是否是当前目录下\n  if (entry.parent == dirGroup) {\n    val isInDir =\n      entryList.find { (it.obj is PwEntryV4) && (it.obj as PwEntryV4).uuid == entry.uuid } != null\n\n    if (!isInDir) {\n      val index = entryList.size\n      entryList.add(KeepassAUtil.instance.convertPwEntry2Item(entry))\n      notifyItemInserted(index)\n    }\n  }\n\n  // 检查是否在当前目录的子目录下，不能return 因为有可能是移动到同级目录下\n  if (entry.checkInGroupLeve(dirGroup)) {\n    kotlin.run breaking@{\n      entryList.forEachIndexed { index, simpleItemEntity ->\n        if (simpleItemEntity.obj == entry.parent) {\n          simpleItemEntity.subTitle =\n            ResUtil.getString(R.string.hint_group_desc, KdbUtil.getGroupEntryNum(entry.parent))\n          notifyItemChanged(index)\n          return@breaking\n        }\n      }\n    }\n  }\n\n  // 检查是否是从当前目录下被移走\n  if (oldParentGroup == dirGroup) {\n    val tempIndex = kotlin.run braking@{\n      entryList.forEachIndexed { index, simpleItemEntity ->\n        if (simpleItemEntity.obj !is PwEntryV4) {\n          return@forEachIndexed\n        }\n        if ((simpleItemEntity.obj as PwEntryV4).uuid == entry.uuid) {\n          return@braking index\n        }\n      }\n      return@braking -1\n    }\n\n    if (tempIndex != -1) {\n      entryList.removeAt(tempIndex)\n      notifyItemRemoved(tempIndex)\n    }\n    return\n  }\n\n  // 检查是否是从当前目录的子目录下被移走\n  if (dirGroup.childGroups.contains(oldParentGroup)) {\n    kotlin.run braking@{\n      entryList.forEachIndexed { index, simpleItemEntity ->\n        if (simpleItemEntity.obj !is PwGroupV4) {\n          return@forEachIndexed\n        }\n        if (simpleItemEntity.obj == oldParentGroup) {\n          simpleItemEntity.subTitle =\n            ResUtil.getString(R.string.hint_group_desc, KdbUtil.getGroupEntryNum(oldParentGroup))\n          notifyItemChanged(index)\n          return@braking\n        }\n      }\n    }\n    return\n  }\n\n  Timber.d(\"The entry is not from the home page, title = ${entry.title}\")\n}\n\n"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/util/KpaListGroupExtensions.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.util\n\nimport com.arialyy.frame.util.ResUtil\nimport com.keepassdroid.database.PwGroupV4\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseApp\nimport com.lyy.keepassa.entity.SimpleItemEntity\nimport com.lyy.keepassa.view.SimpleEntryAdapter\nimport timber.log.Timber\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 2:38 下午 2022/4/2\n **/\n\n/**\n * Check whether the entry is in the follow group\n */\nfun PwGroupV4.checkGroupIsParent(group: PwGroupV4?): Boolean {\n  if (group == null) {\n    return false\n  }\n  return this.parent == group\n}\n\nfun SimpleEntryAdapter.createGroup(\n  entryList: MutableList<SimpleItemEntity>,\n  groupV4: PwGroupV4,\n  dirGroup: PwGroupV4?\n) {\n  if (dirGroup == null) {\n    Timber.w(\"parent group is null\")\n    return\n  }\n  if (groupV4.checkGroupIsParent(dirGroup)) {\n    var lastGroupIndex = -1\n    entryList.forEachIndexed { index, item ->\n      if (item.obj is PwGroupV4) {\n        lastGroupIndex = index\n      }\n    }\n\n    if (lastGroupIndex == -1) {\n      val lastIndex = entryList.size\n      entryList.add(KeepassAUtil.instance.convertPwGroup2Item(groupV4))\n      notifyItemInserted(lastIndex)\n      return\n    }\n    entryList.add(lastGroupIndex + 1, KeepassAUtil.instance.convertPwGroup2Item(groupV4))\n    notifyItemInserted(lastGroupIndex + 1)\n\n    // notifyDataSetChanged()\n    return\n  }\n  Timber.d(\"The entry is not from the home page, title = ${groupV4.name}\")\n}\n\nfun SimpleEntryAdapter.updateModifyGroup(\n  entryList: MutableList<SimpleItemEntity>,\n  groupV4: PwGroupV4,\n  curDirGroup: PwGroupV4?\n) {\n  if (curDirGroup == null) {\n    Timber.w(\"parent group is null\")\n    return\n  }\n\n  if (!groupV4.checkGroupIsParent(curDirGroup)) {\n    Timber.d(\"The entry is not from the home page, title = ${groupV4.name}\")\n    return\n  }\n  entryList.forEachIndexed { index, item ->\n    if (item.obj !is PwGroupV4) {\n      return@forEachIndexed\n    }\n\n    if ((item.obj as PwGroupV4).uuid == groupV4.uuid) {\n      KpaUtil.updateGroupItemInfo(item)\n      notifyItemChanged(index)\n      return\n    }\n  }\n}\n\nfun SimpleEntryAdapter.deleteGroup(\n  entryList: MutableList<SimpleItemEntity>,\n  groupV4: PwGroupV4,\n  oldParentGroup: PwGroupV4,\n  curDirGroup: PwGroupV4?\n) {\n  if (curDirGroup == null) {\n    Timber.w(\"parent group is null\")\n    return\n  }\n\n  if (oldParentGroup != curDirGroup) {\n    Timber.d(\"The entry is not from the home page, title = ${groupV4.name}, curDirGroup = ${curDirGroup.name}\")\n    return\n  }\n\n  // remove group form entryList\n  val delIndex = kotlin.run breaking@{\n    entryList.forEachIndexed { index, item ->\n      if (item.obj !is PwGroupV4) {\n        return@forEachIndexed\n      }\n\n      if ((item.obj as PwGroupV4).uuid == groupV4.uuid) {\n        return@breaking index\n      }\n    }\n    return@breaking -1\n  }\n\n  entryList.removeAt(delIndex)\n  notifyItemRemoved(delIndex)\n\n  // update recycling bin\n  if (curDirGroup == BaseApp.KDB.pm.rootGroup) {\n\n    entryList.forEachIndexed { index, item ->\n      if (item.obj == BaseApp.KDB.pm.recycleBin) {\n        item.subTitle = ResUtil.getString(\n          R.string.hint_group_desc, KdbUtil.getGroupEntryNum(BaseApp.KDB.pm.recycleBin)\n        )\n        notifyItemChanged(index)\n        return\n      }\n    }\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/util/KpaUtil.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.util\n\nimport android.content.Intent\nimport android.graphics.Paint\nimport android.net.Uri\nimport android.text.InputType\nimport android.widget.TextView\nimport com.arialyy.frame.router.Routerfit\nimport com.arialyy.frame.util.ResUtil\nimport com.arialyy.frame.util.StringUtil\nimport com.blankj.utilcode.util.ActivityUtils\nimport com.blankj.utilcode.util.ToastUtils\nimport com.keepassdroid.database.PwEntryV4\nimport com.keepassdroid.database.PwGroupV4\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseApp\nimport com.lyy.keepassa.entity.EntryRecord\nimport com.lyy.keepassa.entity.SimpleItemEntity\nimport com.lyy.keepassa.router.ServiceRouter\nimport kotlinx.coroutines.MainScope\nimport kotlinx.coroutines.flow.MutableSharedFlow\nimport timber.log.Timber\nimport java.util.Date\nimport java.util.Locale\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 2022/3/22\n **/\nobject KpaUtil {\n\n  var scope = MainScope()\n  private var isEmptyPass = false\n  val kdbHandlerService by lazy {\n    Routerfit.create(ServiceRouter::class.java).getDbSaveService()\n  }\n\n  val kdbOpenService by lazy {\n    Routerfit.create(ServiceRouter::class.java).getDbOpenService()\n  }\n\n  val openEntryRecordFlow = MutableSharedFlow<EntryRecord>()\n\n  fun isEmptyPass(): Boolean {\n    return isEmptyPass\n  }\n\n  fun setEmptyPass(isEmptyPass: Boolean) {\n    this.isEmptyPass = isEmptyPass\n  }\n\n  fun isChina(): Boolean {\n    return LanguageUtil.getSysCurrentLan().country == Locale.CHINA.country\n  }\n\n  fun updateEntryItemInfo(item: SimpleItemEntity) {\n    val entry = (item.obj as PwEntryV4)\n    item.title = entry.title\n    item.subTitle = if (entry.isRef()) {\n      val refStr = \"${BaseApp.APP.resources.getString(R.string.ref_entry)}: \"\n      val tempStr = \"${refStr}${KdbUtil.getUserName(entry)}\"\n      StringUtil.highLightStr(tempStr, refStr, ResUtil.getColor(R.color.colorPrimary), true)\n    } else {\n      entry.username\n    }\n  }\n\n  fun updateGroupItemInfo(item: SimpleItemEntity) {\n    val pwGroup = (item.obj as PwGroupV4)\n    item.title = pwGroup.name\n    item.subTitle = ResUtil.getString(\n      R.string.hint_group_desc, KdbUtil.getGroupEntryNum(pwGroup)\n        .toString()\n    )\n    item.obj = pwGroup\n  }\n\n  /**\n   * open url with browser\n   */\n  fun openUrlWithBrowser(url: String) {\n    try {\n      ActivityUtils.getTopActivity().startActivity(Intent(Intent.ACTION_VIEW).apply {\n        data = Uri.parse(url)\n      })\n    } catch (e: Exception) {\n      ToastUtils.showLong(\"${ResUtil.getString(R.string.invalid)}${ResUtil.getString(R.string.url)}\")\n      Timber.e(e)\n    }\n  }\n\n  /**\n   * 处理密码的显示\n   */\n  fun handleShowPass(tv: TextView, show: Boolean) {\n    tv.inputType = if (show) {\n      InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD\n    } else {\n      InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_PASSWORD\n    }\n  }\n\n  /**\n   * 处理过期的view，并加上中横线\n   */\n  fun handleExpire(tv: TextView, pwEntryV4: PwEntryV4) {\n    if (pwEntryV4.expires()\n      && pwEntryV4.expiryTime != null\n      && pwEntryV4.expiryTime.before(Date(System.currentTimeMillis()))\n    ) {\n      val paint = tv.paint\n      paint.flags = Paint.STRIKE_THRU_TEXT_FLAG\n      paint.isAntiAlias = true\n    }\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/util/LanguageUtil.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.util\n\nimport android.content.Context\nimport android.content.res.Configuration\nimport android.content.res.Resources\nimport android.os.Build\nimport android.os.LocaleList\nimport android.text.TextUtils\nimport com.lyy.keepassa.base.Constance\nimport java.util.Locale\n\n/**\n * 语言工具\n */\nobject LanguageUtil {\n  private val LOCALE_KEY_LANG = \"LOCALE_KEY_LANG\"\n  private val LOCALE_KEY_COUNTRY = \"LOCALE_KEY_COUNTRY\"\n\n  @JvmField\n  val SUPPORT_LAN =\n    arrayListOf(\n      Locale.ENGLISH,\n      Locale.SIMPLIFIED_CHINESE,\n      Locale.TRADITIONAL_CHINESE,\n      Locale.CANADA_FRENCH\n    )\n\n  /**\n   * 设置app语言\n   * @return context, activity 需要使用attachBaseContext()来更新该context\n   */\n//  @CheckResult(suggest = \"activity需要使用attachBaseContext()来更新该context\")\n  fun setLanguage(\n    context: Context,\n    locale: Locale\n  ): Context {\n    val res: Resources = context.resources\n    val conf = res.configuration\n    conf.setLocale(locale)\n    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {\n      conf.setLocales(LocaleList(locale))\n    }\n    res.updateConfiguration(conf, res.displayMetrics)\n    val cx = context.createConfigurationContext(conf)\n    return cx\n  }\n\n  fun getLanguageConfig(context: Context, locale: Locale): Configuration {\n    val res: Resources = context.resources\n    val conf = res.configuration\n    conf.setLocale(locale)\n    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {\n      conf.setLocales(LocaleList(locale))\n    }\n    return conf\n  }\n\n  /**\n   * 获取当前系统语言\n   * https://mp.weixin.qq.com/s/A27dvFV3glX26Ur0WsdJ9g\n   */\n  fun getSysCurrentLan(): Locale {\n    return if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {\n      Locale.getDefault()\n    } else {\n      LocaleList.getDefault().get(0)\n    }\n  }\n\n  /**\n   * 保存语言到配置文件\n   */\n  fun saveLanguage(\n    context: Context,\n    locale: Locale\n  ) {\n    val pre = context.getSharedPreferences(Constance.PRE_FILE_NAME, Context.MODE_PRIVATE)\n    val editor = pre.edit()\n    editor.putString(LOCALE_KEY_LANG, locale.language)\n    editor.putString(LOCALE_KEY_COUNTRY, locale.country)\n    editor.apply()\n  }\n\n  /**\n   * 读取保存的语言\n   * @return 如果没有，返回null\n   */\n  fun getDefLanguage(context: Context): Locale? {\n    val pre = context.getSharedPreferences(Constance.PRE_FILE_NAME, Context.MODE_PRIVATE)\n    val lang = pre.getString(LOCALE_KEY_LANG, \"\")\n    val country = pre.getString(LOCALE_KEY_COUNTRY, \"\")\n    return if (TextUtils.isEmpty(lang) && TextUtils.isEmpty(country)) {\n      null\n    } else {\n      Locale(lang, country)\n    }\n  }\n\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/util/NotificationUtil.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.util\n\nimport android.content.Context\nimport android.content.Intent\nimport android.os.Build\nimport android.text.TextUtils\nimport com.lyy.keepassa.base.BaseApp\nimport com.lyy.keepassa.service.DbOpenNotificationService\n\n/**\n * 通知工具\n */\nobject NotificationUtil {\n\n  /**\n   * 打开数据库通知\n   */\n  fun startDbOpenNotify(context: Context) {\n    val intent = Intent(context, DbOpenNotificationService::class.java).apply {\n      putExtra(\n          DbOpenNotificationService.KEY_NOTIFY_TYPE,\n          DbOpenNotificationService.NOTIFY_TYPE_OPEN_DB\n      )\n    }\n    startService(context, intent)\n  }\n\n  /**\n   * 数据库已锁定，启动快速解锁\n   */\n  fun startQuickUnlockNotify(context: Context) {\n    val intent = Intent(context, DbOpenNotificationService::class.java).apply {\n      putExtra(\n          DbOpenNotificationService.KEY_NOTIFY_TYPE,\n          DbOpenNotificationService.NOTIFY_TYPE_QUICK_UNLOCK_DB\n      )\n    }\n    startService(context, intent)\n  }\n\n  /**\n   * 数据库已锁定通知\n   */\n  fun startDbLocked(context: Context) {\n    val intent = Intent(context, DbOpenNotificationService::class.java).apply {\n      putExtra(\n          DbOpenNotificationService.KEY_NOTIFY_TYPE,\n          DbOpenNotificationService.NOTIFY_TYPE_DB_LOCKED\n      )\n    }\n    startService(context, intent)\n  }\n\n\n  private fun startService(\n    context: Context,\n    intent: Intent\n  ) {\n    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {\n      /*\n       * Android 在 8.0 限制了后台服务这些，启动后台服务需要设置通知栏，使服务变成前台服务。\n       * 但是在 9.0 上，就会出现Permission Denial: startForeground requires\n       */\n      context.startForegroundService(intent)\n    } else {\n      context.startService(intent)\n    }\n  }\n\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/util/PasswordBuildUtil.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.util\n\nimport timber.log.Timber\nimport kotlin.random.Random\n\n/**\n * 创建密码工具\n */\nclass PasswordBuildUtil private constructor() {\n\n  companion object {\n    @Volatile\n    private var INSTANCE: PasswordBuildUtil? = null\n    private const val LOWER_CHAR = \"lowerChar\"\n    private const val UP_CHAR = \"upChar\"\n    private const val NUM_CHAR = \"numChar\"\n    private const val SYMBOL_CHAR = \"symbolChar\"\n    private const val BRACKET_CHAR = \"bracketChar\"\n    private const val SPACE_CHAR = \"spaceChar\"\n    private const val UNDERLINE = \"underline\"\n    private const val MINUS = \"minus\"\n\n    @Synchronized\n    fun getInstance(): PasswordBuildUtil {\n      if (INSTANCE == null) {\n        synchronized(PasswordBuildUtil::class.java) {\n          INSTANCE = PasswordBuildUtil()\n        }\n      }\n      return INSTANCE!!\n    }\n  }\n\n  private val lowerChar = \"abcdefghijklmnopqrstuvwxyz\"\n  private val upChar = \"ABCDEFGHIJKLMNOPQRSTUVWXYZ\"\n  private val numChar = \"1234567890\"\n\n  // 特殊符号\n  private val symbolChar = \"`~!@#$%^&*+=;:'\\\",.?/|\\\\\" // ~\n\n  // 括号\n  private val bracketChar = \"()<>{}[]\"\n\n  // 空格\n  private val spaceChar = \" \"\n\n  // 下划线\n  private val underline = \"_\"\n\n  // 减号\n  private val minus = \"-\"\n\n  private val charList = arrayListOf<String>()\n\n  // 默认字符串\n  // private var defStr: StringBuffer = StringBuffer()\n\n  /**\n   * 清空字符\n   */\n  public fun clear(): PasswordBuildUtil {\n    charList.clear()\n    return this\n  }\n\n  /**\n   * 使用小写字符\n   */\n  public fun addLowerChar(): PasswordBuildUtil {\n    charList.add(lowerChar)\n    return this\n  }\n\n  /**\n   * 使用大写字符\n   */\n  public fun addUpChar(): PasswordBuildUtil {\n    charList.add(upChar)\n    return this\n  }\n\n  /**\n   * 使用数字\n   */\n  public fun addNumChar(): PasswordBuildUtil {\n    charList.add(numChar)\n    return this\n  }\n\n  /**\n   * 使用特殊字符\n   */\n  public fun addSymbolChar(): PasswordBuildUtil {\n    charList.add(symbolChar)\n    return this\n  }\n\n  /**\n   * 使用括号\n   */\n  public fun addBracketChar(): PasswordBuildUtil {\n    charList.add(bracketChar)\n    return this\n  }\n\n  /**\n   * 使用空格\n   */\n  public fun addSpaceChar(): PasswordBuildUtil {\n    charList.add(spaceChar)\n    return this\n  }\n\n  /**\n   * 使用下划线\n   */\n  public fun addUnderline(): PasswordBuildUtil {\n    charList.add(underline)\n    return this\n  }\n\n  /**\n   * 使用减号\n   */\n  public fun addMinus(): PasswordBuildUtil {\n    charList.add(minus)\n    return this\n  }\n\n  /**\n   * 构建密码，如果密码长度小于选择的[charList]长度，则修改其长度为[charList]的长度\n   * @param len 密码长度\n   * @return 如果[种子][charList]没有设置，或长度小于1 返回\"\"\n   */\n  public fun builder(len: Int): String {\n    if (charList.isEmpty()) {\n      return \"\"\n    }\n\n    if (len < 1) {\n      return \"\"\n    }\n    val realLen = if (len < charList.size) {\n      Timber.w(\"密码长度小于选择的特殊符号种类，将生成和特殊符号种类数量一致长度的密码\")\n      charList.size\n    } else {\n      len\n    }\n\n    val cloneList = arrayListOf<String>()\n    cloneList.addAll(charList)\n\n    val random = Random.Default\n    val sb = StringBuilder()\n    for (i in 1..realLen) {\n\n      // 确保每种类型都有\n      if (cloneList.isNotEmpty()) {\n        val charIndex = random.nextInt(cloneList.size)\n        val char = cloneList[charIndex]\n        sb.append(char[random.nextInt(char.length)])\n        cloneList.remove(char)\n        continue\n      }\n      val charIndex = random.nextInt(charList.size)\n      val char = charList[charIndex]\n      sb.append(char[random.nextInt(char.length)])\n    }\n\n    return sb.toString()\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/util/PermissionPageManagement.java",
    "content": "package com.lyy.keepassa.util;\n\nimport android.content.ComponentName;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.net.Uri;\nimport android.os.Build;\nimport android.provider.Settings;\nimport com.blankj.utilcode.util.RomUtils;\nimport timber.log.Timber;\n\npublic class PermissionPageManagement {\n\n  /**\n   * 此函数可以自己定义\n   */\n  public static void goToSetting(Context activity) {\n    if (RomUtils.isHuawei()) {\n      Huawei(activity);\n      return;\n    }\n\n    if (RomUtils.isMeizu()) {\n      Meizu(activity);\n      return;\n    }\n\n    if (RomUtils.isXiaomi()) {\n      Xiaomi(activity);\n      return;\n    }\n\n    if (RomUtils.isVivo()) {\n      VIVO(activity);\n      return;\n    }\n\n    if (RomUtils.isOppo()) {\n      OPPO(activity);\n      return;\n    }\n    if (RomUtils.isSony()) {\n      Sony(activity);\n      return;\n    }\n    if (RomUtils.isLg()) {\n      LG(activity);\n      return;\n    }\n    ApplicationInfo(activity);\n    Timber.e(\"目前暂不支持此系统\");\n  }\n\n  public static void Huawei(Context activity) {\n    try {\n      Intent intent = new Intent();\n      intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);\n      intent.putExtra(\"packageName\", activity.getApplicationInfo().packageName);\n      ComponentName comp = new ComponentName(\"com.huawei.systemmanager\",\n          \"com.huawei.permissionmanager.ui.MainActivity\");\n      intent.setComponent(comp);\n      activity.startActivity(intent);\n    } catch (Exception e) {\n      Timber.e(e);\n      goIntentSetting(activity);\n    }\n  }\n\n  public static void Meizu(Context activity) {\n    try {\n      Intent intent = new Intent(\"com.meizu.safe.security.SHOW_APPSEC\");\n      intent.addCategory(Intent.CATEGORY_DEFAULT);\n      intent.putExtra(\"packageName\", activity.getPackageName());\n      activity.startActivity(intent);\n    } catch (Exception e) {\n      goIntentSetting(activity);\n      Timber.e(e);\n    }\n  }\n\n  public static void Xiaomi(Context activity) {\n    try {\n      Intent intent = new Intent(\"miui.intent.action.APP_PERM_EDITOR\");\n      intent.putExtra(\"extra_pkgname\", activity.getPackageName());\n\n      //ComponentName componentName = new ComponentName(\"com.miui.securitycenter\",\n      //    \"com.miui.permcenter.permissions.PermissionsEditorActivity\");\n      //intent.setComponent(componentName);\n      intent.addCategory(Intent.CATEGORY_DEFAULT);\n      activity.startActivity(intent);\n    } catch (Exception e) {\n      goIntentSetting(activity);\n      Timber.e(e);\n    }\n  }\n\n  public static void Sony(Context activity) {\n    try {\n      Intent intent = new Intent();\n      intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);\n      intent.putExtra(\"packageName\", activity.getPackageName());\n      ComponentName comp =\n          new ComponentName(\"com.sonymobile.cta\", \"com.sonymobile.cta.SomcCTAMainActivity\");\n      intent.setComponent(comp);\n      activity.startActivity(intent);\n    } catch (Exception e) {\n      goIntentSetting(activity);\n      Timber.e(e);\n    }\n  }\n\n  public static void OPPO(Context activity) {\n    try {\n      Intent intent = new Intent();\n      intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);\n      intent.putExtra(\"packageName\", activity.getPackageName());\n      //        ComponentName comp = new ComponentName(\"com.color.safecenter\", \"com.color.safecenter.permission.PermissionManagerActivity\");\n      ComponentName comp = new ComponentName(\"com.coloros.securitypermission\",\n          \"com.coloros.securitypermission.permission.PermissionAppAllPermissionActivity\");//R11t 7.1.1 os-v3.2\n      intent.setComponent(comp);\n      activity.startActivity(intent);\n    } catch (Exception e) {\n      goIntentSetting(activity);\n      Timber.e(e);\n    }\n  }\n\n  public static void VIVO(Context activity) {\n    Intent localIntent;\n    if (((Build.MODEL.contains(\"Y85\")) && (!Build.MODEL.contains(\"Y85A\"))) || (Build.MODEL.contains(\n        \"vivo Y53L\"))) {\n      localIntent = new Intent();\n      localIntent.setClassName(\"com.vivo.permissionmanager\",\n          \"com.vivo.permissionmanager.activity.PurviewTabActivity\");\n      localIntent.putExtra(\"packagename\", activity.getPackageName());\n      localIntent.putExtra(\"tabId\", \"1\");\n      activity.startActivity(localIntent);\n    } else {\n      localIntent = new Intent();\n      localIntent.setClassName(\"com.vivo.permissionmanager\",\n          \"com.vivo.permissionmanager.activity.SoftPermissionDetailActivity\");\n      localIntent.setAction(\"secure.intent.action.softPermissionDetail\");\n      localIntent.putExtra(\"packagename\", activity.getPackageName());\n      activity.startActivity(localIntent);\n    }\n  }\n\n  public static void LG(Context activity) {\n    try {\n      Intent intent = new Intent(\"android.intent.action.MAIN\");\n      intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);\n      intent.putExtra(\"packageName\", activity.getPackageName());\n      ComponentName comp = new ComponentName(\"com.android.settings\",\n          \"com.android.settings.Settings$AccessLockSummaryActivity\");\n      intent.setComponent(comp);\n      activity.startActivity(intent);\n    } catch (Exception e) {\n      goIntentSetting(activity);\n      Timber.e(e);\n    }\n  }\n\n  /**\n   * 只能打开到自带安全软件\n   */\n  public static void _360(Context activity) {\n    Intent intent = new Intent(\"android.intent.action.MAIN\");\n    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);\n    intent.putExtra(\"packageName\", activity.getPackageName());\n    ComponentName comp = new ComponentName(\"com.qihoo360.mobilesafe\",\n        \"com.qihoo360.mobilesafe.ui.index.AppEnterActivity\");\n    intent.setComponent(comp);\n    activity.startActivity(intent);\n  }\n\n  /**\n   * 应用信息界面\n   */\n  public static void ApplicationInfo(Context activity) {\n    Intent localIntent = new Intent();\n    localIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);\n    localIntent.setAction(\"android.settings.APPLICATION_DETAILS_SETTINGS\");\n    localIntent.setData(Uri.fromParts(\"package\", activity.getPackageName(), null));\n    activity.startActivity(localIntent);\n  }\n\n  /**\n   * 系统设置界面\n   */\n  public static void SystemConfig(Context activity) {\n    Intent intent = new Intent(Settings.ACTION_SETTINGS);\n    activity.startActivity(intent);\n  }\n\n  /**\n   * 默认打开应用详细页\n   */\n  private static void goIntentSetting(Context pActivity) {\n    Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);\n    Uri uri = Uri.fromParts(\"package\", pActivity.getPackageName(), null);\n    intent.setData(uri);\n    try {\n      pActivity.startActivity(intent);\n    } catch (Exception e) {\n      Timber.e(e);\n    }\n  }\n}\n"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/util/PermissionsUtil.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.util;\n\nimport android.app.AppOpsManager\nimport android.content.Context\nimport android.net.Uri\nimport android.os.Build\nimport android.os.Process\nimport android.provider.Settings\nimport android.text.Html\nimport android.view.autofill.AutofillManager\nimport android.widget.Button\nimport com.arialyy.frame.router.Routerfit\nimport com.arialyy.frame.util.ResUtil\nimport com.blankj.utilcode.util.RomUtils\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseApp\nimport com.lyy.keepassa.router.DialogRouter\nimport com.lyy.keepassa.view.dialog.OnMsgBtClickListener\nimport timber.log.Timber\nimport java.lang.reflect.Method\n\nobject PermissionsUtil {\n\n  /**\n   * 是否需要弹出后台启动提示弹窗\n   */\n  fun needShowBackgroundStartDialog(context: Context): Boolean {\n    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {\n      return false\n    }\n    val am = context.getSystemService(AutofillManager::class.java)\n    if (!am.isAutofillSupported) {\n      Timber.i(\"不支持自动填充\")\n      return false\n    }\n    if (am.hasEnabledAutofillServices() && !isCanBackgroundStart()) {\n      Timber.i(\"已经打开了自动填充\")\n      return true\n    }\n    return false\n  }\n\n  /**\n   * 显示弹出框提示用户打开后台启动界面的权限\n   */\n  fun showAutoFillMsgDialog(context: Context, msg: String) {\n//    val IS_HOWED_AUTO_FILL_HINT_DIALOG = \"IS_HOWED_AUTO_FILL_HINT_DIALOG\"\n//    val isShowed =\n//      SharePreUtil.getBoolean(\n//        Constance.PRE_FILE_NAME,\n//        context,\n//        IS_HOWED_AUTO_FILL_HINT_DIALOG\n//      )\n//\n//    if (!isShowed) {\n    Routerfit.create(DialogRouter::class.java).showMsgDialog(\n      msgContent = Html.fromHtml(BaseApp.APP.getString(R.string.hint_background_start, msg)),\n      showCancelBt = true,\n      cancelText = ResUtil.getString(R.string.cancel),\n      enterText = ResUtil.getString(R.string.open_setting),\n      btnClickListener = object : OnMsgBtClickListener {\n        override fun onEnter(v: Button) {\n          PermissionPageManagement.goToSetting(context)\n        }\n\n        override fun onCancel(v: Button) {\n        }\n      }\n    )\n//      SharePreUtil.putBoolean(\n//        Constance.PRE_FILE_NAME,\n//        context,\n//        IS_HOWED_AUTO_FILL_HINT_DIALOG,\n//        true\n//      )\n//    } else {\n//      Timber.i(\"已显示过自动填充对话框，不再重复显示\")\n//    }\n  }\n\n  fun isCanBackgroundStart(): Boolean {\n    if (RomUtils.isXiaomi()) {\n      return miuiCanBackgroundStart()\n    }\n    if (RomUtils.isVivo()) {\n      return vivoBackgroundStartAllowed()\n    }\n    if (RomUtils.isHuawei()) {\n      return hwBackgroundStartAllowed(BaseApp.APP)\n    }\n    return true\n  }\n\n  /**\n   * 检查miui 是否被允许后台启动\n   *\n   * @return true 允许后台启动\n   */\n  fun miuiCanBackgroundStart(): Boolean {\n    val ops = BaseApp.APP.getSystemService(Context.APP_OPS_SERVICE) as AppOpsManager\n    try {\n      val op = 10021\n      val method = ops.javaClass.getMethod(\n        \"checkOpNoThrow\",\n        Int::class.java, Int::class.java, String::class.java\n      )\n      val result = method.invoke(ops, op, Process.myUid(), BaseApp.APP.packageName) as Int\n      return result == AppOpsManager.MODE_ALLOWED\n    } catch (e: Exception) {\n      Timber.e(e, \"not support\")\n    }\n    return false\n  }\n\n  /**\n   * 判断vivo后台弹出界面 1未开启 0开启\n   */\n  fun vivoBackgroundStartAllowed(): Boolean {\n    val packageName = BaseApp.APP.packageName\n    val uri2 =\n      Uri.parse(\"content://com.vivo.permissionmanager.provider.permission/start_bg_activity\")\n    val selection = \"pkgname = ?\"\n    val selectionArgs = arrayOf(packageName)\n    try {\n      val cursor = BaseApp.APP\n        .contentResolver\n        .query(uri2, null, selection, selectionArgs, null);\n      if (cursor != null) {\n        return if (cursor.moveToFirst()) {\n          val index = cursor.getColumnIndex(\"currentstate\")\n          if (index == -1) {\n            false\n          } else {\n            val currentmode = cursor.getInt(index)\n            cursor.close()\n            currentmode == 0\n          }\n        } else {\n          cursor.close()\n          false\n        }\n      }\n    } catch (throwable: Throwable) {\n      Timber.d(throwable)\n    }\n    return false\n  }\n\n  fun hwBackgroundStartAllowed(context: Context): Boolean {\n    try {\n      val c = Class.forName(\"com.huawei.android.app.AppOpsManagerEx\")\n      val ops = context.getSystemService(Context.APP_OPS_SERVICE) as AppOpsManager\n      val m: Method = c.getDeclaredMethod(\n        \"checkHwOpNoThrow\",\n        AppOpsManager::class.java,\n        Int::class.java,\n        Int::class.java,\n        String::class.java\n      )\n      return m.invoke(\n        c.newInstance(), ops, 100000, Process.myUid(), context.packageName\n      ) as Int == AppOpsManager.MODE_ALLOWED\n    } catch (e: Exception) {\n      return false\n    }\n  }\n\n  /**\n   * 检查oppo 是否被允许后台启动\n   *\n   * @return true 允许后台启动\n   */\n  fun oppoBackgroundStartAllowed(): Boolean {\n    return Settings.canDrawOverlays(BaseApp.APP)\n  }\n\n  // fun testCheckHwOp() {\n  //   val c = Class.forName(\"com.huawei.android.app.AppOpsManagerEx\")\n  //   val m: Method = c.getDeclaredMethod(\n  //     \"checkHwOpNoThrow\",\n  //     AppOpsManager::class.java,\n  //     Int::class.javaPrimitiveType,\n  //     Int::class.javaPrimitiveType,\n  //     String::class.java\n  //   )\n  //   val bundle: Bundle = getNoteParamInt()\n  //   val op = bundle.getInt(KEY_OP_CODES)\n  //   val packageName = bundle.getString(KEY_PKG_NAME)\n  //   val uid = bundle.getInt(KEY_UID)\n  //   val checkResult = m.invoke(\n  //     c.newInstance(), arrayOf<Any?>(\n  //       context.getSystemService(\n  //         Context.APP_OPS_SERVICE\n  //       ) as AppOpsManager, op, uid, packageName\n  //     )\n  //   ) as Int\n  //   com.sun.corba.se.impl.activation.ServerMain.printResult(\"check result:$checkResult    op:$op uid:$uid packageName:$packageName\")\n  // }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/util/PlayUtil.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.util\n\nimport android.app.Activity\nimport com.google.android.gms.common.ConnectionResult\nimport com.google.android.gms.common.GoogleApiAvailability\n\nobject PlayUtil {\n\n  fun playServiceExist(context: Activity):Boolean {\n    // 验证是否已在此设备上安装并启用Google Play服务，以及此设备上安装的旧版本是否为此客户端所需的版本\n    val code = GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(context)\n    if (code == ConnectionResult.SUCCESS) {\n      // 支持Google服务\n      return true\n    } else {\n      /**\n       * 依靠 Play 服务 SDK 运行的应用在访问 Google Play 服务功能之前，应始终检查设备是否拥有兼容的 Google Play 服务 APK。\n       * 我们建议您在以下两个位置进行检查：主 Activity 的 onCreate() 方法中，及其 onResume() 方法中。\n       * onCreate() 中的检查可确保该应用在检查成功之前无法使用。\n       * onResume() 中的检查可确保当用户通过一些其他方式返回正在运行的应用（比如通过返回按钮）时，检查仍将继续进行。\n       * 如果设备没有兼容的 Google Play 服务版本，您的应用可以调用以下方法，以便让用户从 Play 商店下载 Google Play 服务。\n       * 它将尝试在此设备上提供Google Play服务。如果Play服务已经可用，则Task可以立即完成返回。\n       */\n      GoogleApiAvailability.getInstance().makeGooglePlayServicesAvailable(context)\n\n      // 或者使用以下代码\n      /**\n       * 通过isUserResolvableError来确定是否可以通过用户操作解决错误\n       */\n      if (GoogleApiAvailability.getInstance().isUserResolvableError(code)) {\n        /**\n         * 返回一个对话框，用于解决提供的errorCode。\n         * @param activity  用于创建对话框的父活动\n         * @param code      通过调用返回的错误代码\n         * @param activity  调用startActivityForResult时给出的requestCode\n         */\n        GoogleApiAvailability.getInstance().getErrorDialog(context, code, 200)?.show()\n      }\n    }\n    return false\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/util/QuickUnLockUtil.java",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.util;\n\n/**\n * 密码工具\n */\npublic class QuickUnLockUtil {\n  /**\n   * 快速解锁保存的信息\n   */\n  private static final String QUICK_UN_LOCK_FILE_NAME = \"91b7b54da7f5154020a528\";\n\n  static {\n    System.loadLibrary(\"keepassA\");\n  }\n\n  /**\n   * 加密字符串\n   *\n   * @param str 明文\n   * @return 密文\n   */\n  //@Deprecated(message = \"不安全\")\n  public native static String encryptStr(String str);\n\n  /**\n   * 解密字符串\n   *\n   * @param str 密文\n   * @return 明文\n   */\n  public native static String decryption(String str);\n\n  /**\n   * 获取数据库密码\n   */\n  public native static String getDbPass();\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/util/RealPathUtil.java",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.util;\n\nimport android.annotation.SuppressLint;\nimport android.content.ContentUris;\nimport android.content.Context;\nimport android.database.Cursor;\nimport android.net.Uri;\nimport android.os.Build;\nimport android.os.Environment;\nimport android.provider.DocumentsContract;\nimport android.provider.MediaStore;\nimport android.provider.OpenableColumns;\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.core.content.FileProvider;\nimport java.io.BufferedOutputStream;\nimport java.io.File;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport timber.log.Timber;\n\npublic class RealPathUtil {\n\n  public static @Nullable Uri compatUriFromFile(@NonNull final Context context,\n      @NonNull final File file) {\n    Uri result = null;\n    if (Build.VERSION.SDK_INT < 21) {\n      result = Uri.fromFile(file);\n    } else {\n      final String packageName = context.getApplicationContext().getPackageName();\n      final String authority = new StringBuilder(packageName).append(\".provider\").toString();\n      try {\n        result = FileProvider.getUriForFile(context, authority, file);\n      } catch (IllegalArgumentException e) {\n        Timber.e(e);\n      }\n    }\n    return result;\n  }\n\n  @SuppressLint(\"NewApi\")\n  public static @Nullable String getRealPathFromURI(@NonNull final Context context,\n      @NonNull final Uri uri) {\n\n    final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;\n\n    // DocumentProvider\n    if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {\n      // ExternalStorageProvider\n      if (isExternalStorageDocument(uri)) {\n        final String docId = DocumentsContract.getDocumentId(uri);\n        final String[] split = docId.split(\":\");\n        final String type = split[0];\n\n        if (\"primary\".equalsIgnoreCase(type)) {\n          return Environment.getExternalStorageDirectory() + \"/\" + split[1];\n        }\n\n        // TODO handle non-primary volumes\n      }\n      // DownloadsProvider\n      else if (isDownloadsDocument(uri)) {\n\n        final String id = DocumentsContract.getDocumentId(uri);\n\n        // Handle raw file urls differently, we can just return the current string minus\n        // the raw: prefix. https://github.com/Yalantis/uCrop/issues/318\n        if (id != null && id.startsWith(\"raw:\")) {\n          return id.substring(4);\n        }\n\n        final Uri contentUri = ContentUris.withAppendedId(\n            Uri.parse(\"content://downloads/public_downloads\"), Long.valueOf(id.split(\":\")[1]));\n        try {\n          return getDataColumn(context, contentUri, null, null);\n        } catch (Exception e) {\n          Timber.e(e);\n        }\n\n        // File couldn't be loaded so we have to create a temporary file and then stream\n        // the contents into that file, some of this code has been implemented from\n        // https://github.com/coltoscosmin/FileUtils/blob/master/FileUtils.java\n\n        String fileName = getFileName(context, uri);\n        File cacheDir = new File(context.getCacheDir(), \"documents\");\n        if (!cacheDir.exists()) cacheDir.mkdirs();\n        File file = new File(cacheDir, fileName);\n        try {\n          file.createNewFile();\n        } catch (Exception e) {\n          Timber.e(e);\n        }\n        String destinationPath = file.getAbsolutePath();\n        Timber.d(\"Destination path: %s\", destinationPath);\n        destinationPath = file.getAbsolutePath();\n        saveFileFromUri(context, uri, destinationPath);\n\n        return destinationPath;\n      }\n      // MediaProvider\n      else if (isMediaDocument(uri)) {\n        final String docId = DocumentsContract.getDocumentId(uri);\n        final String[] split = docId.split(\":\");\n        final String type = split[0];\n\n        Uri contentUri = null;\n        if (\"image\".equals(type)) {\n          contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;\n        } else if (\"video\".equals(type)) {\n          contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;\n        } else if (\"audio\".equals(type)) {\n          contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;\n        }\n\n        final String selection = \"_id=?\";\n        final String[] selectionArgs = new String[] {\n            split[1]\n        };\n\n        return getDataColumn(context, contentUri, selection, selectionArgs);\n      }\n    }\n    // MediaStore (and general)\n    else if (\"content\".equalsIgnoreCase(uri.getScheme())) {\n\n      // Return the remote address\n\t\t\tif (isGooglePhotosUri(uri)) {\n\t\t\t\treturn uri.getLastPathSegment();\n\t\t\t}\n\n\t\t\tif (isFileProviderUri(context, uri)) {\n\t\t\t\treturn getFileProviderPath(context, uri);\n\t\t\t}\n\n      return getDataColumn(context, uri, null, null);\n    }\n    // File\n    else if (\"file\".equalsIgnoreCase(uri.getScheme())) {\n      return uri.getPath();\n    }\n\n    return null;\n  }\n\n  /**\n   * Get the value of the data column for this Uri. This is useful for\n   * MediaStore Uris, and other file-based ContentProviders.\n   *\n   * @param context The context.\n   * @param uri The Uri to query.\n   * @param selection (Optional) Filter used in the query.\n   * @param selectionArgs (Optional) Selection arguments used in the query.\n   * @return The value of the _data column, which is typically a file path.\n   */\n  public static String getDataColumn(Context context, Uri uri, String selection,\n      String[] selectionArgs) {\n\n    Cursor cursor = null;\n    final String column = \"_data\";\n    final String[] projection = {\n        column\n    };\n\n    try {\n      cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,\n          null);\n      if (cursor != null && cursor.moveToFirst()) {\n        final int index = cursor.getColumnIndexOrThrow(column);\n        return cursor.getString(index);\n      }\n    } finally {\n\t\t\tif (cursor != null) {\n\t\t\t\tcursor.close();\n\t\t\t}\n    }\n    return null;\n  }\n\n  /**\n   * @param uri The Uri to check.\n   * @return Whether the Uri authority is ExternalStorageProvider.\n   */\n  public static boolean isExternalStorageDocument(Uri uri) {\n    return \"com.android.externalstorage.documents\".equals(uri.getAuthority());\n  }\n\n  /**\n   * @param uri The Uri to check.\n   * @return Whether the Uri authority is DownloadsProvider.\n   */\n  public static boolean isDownloadsDocument(Uri uri) {\n    return \"com.android.providers.downloads.documents\".equals(uri.getAuthority());\n  }\n\n  /**\n   * @param uri The Uri to check.\n   * @return Whether the Uri authority is MediaProvider.\n   */\n  public static boolean isMediaDocument(Uri uri) {\n    return \"com.android.providers.media.documents\".equals(uri.getAuthority());\n  }\n\n  /**\n   * @param uri The Uri to check.\n   * @return Whether the Uri authority is Google Photos.\n   */\n  public static boolean isGooglePhotosUri(@NonNull final Uri uri) {\n    return \"com.google.android.apps.photos.content\".equals(uri.getAuthority());\n  }\n\n  /**\n   * @param context The Application context\n   * @param uri The Uri is checked by functions\n   * @return Whether the Uri authority is FileProvider\n   */\n  public static boolean isFileProviderUri(@NonNull final Context context,\n      @NonNull final Uri uri) {\n    final String packageName = context.getPackageName();\n    final String authority = new StringBuilder(packageName).append(\".provider\").toString();\n    return authority.equals(uri.getAuthority());\n  }\n\n  /**\n   * @param context The Application context\n   * @param uri The Uri is checked by functions\n   * @return File path or null if file is missing\n   */\n  public static @Nullable String getFileProviderPath(@NonNull final Context context,\n      @NonNull final Uri uri) {\n    final File appDir = context.getExternalFilesDir(Environment.DIRECTORY_PICTURES);\n    final File file = new File(appDir, uri.getLastPathSegment());\n    return file.exists() ? file.toString() : null;\n  }\n\n  private static void saveFileFromUri(Context context, Uri uri, String destinationPath) {\n    InputStream is = null;\n    BufferedOutputStream bos = null;\n    try {\n      is = context.getContentResolver().openInputStream(uri);\n      bos = new BufferedOutputStream(new FileOutputStream(destinationPath, false));\n      byte[] buf = new byte[1024];\n      is.read(buf);\n      do {\n        bos.write(buf);\n      } while (is.read(buf) != -1);\n    } catch (IOException e) {\n      Timber.e(e);\n    } finally {\n      try {\n        if (is != null) is.close();\n        if (bos != null) bos.close();\n      } catch (IOException e) {\n        Timber.e(e);\n      }\n    }\n  }\n\n  public static String getFileName(@NonNull Context context, Uri uri) {\n    String mimeType = context.getContentResolver().getType(uri);\n    String filename = null;\n\n    if (mimeType == null && context != null) {\n      String path = getPath(context, uri);\n      if (path == null) {\n        filename = getName(uri.toString());\n      } else {\n        File file = new File(path);\n        filename = file.getName();\n      }\n    } else {\n      Cursor returnCursor = context.getContentResolver().query(uri, null,\n          null, null, null);\n      if (returnCursor != null) {\n        int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);\n        returnCursor.moveToFirst();\n        filename = returnCursor.getString(nameIndex);\n        returnCursor.close();\n      }\n    }\n\n    return filename;\n  }\n\n  public static String getName(String filename) {\n    if (filename == null) {\n      return null;\n    }\n    int index = filename.lastIndexOf('/');\n    return filename.substring(index + 1);\n  }\n\n  public static String getPath(final Context context, final Uri uri) {\n    String absolutePath = getRealPathFromURI(context, uri);\n    // String absolutePath = getLocalPath(context, uri);\n    return absolutePath != null ? absolutePath : uri.toString();\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/util/VibratorUtil.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.util\n\nimport android.app.Service\nimport android.os.Build\nimport android.os.VibrationEffect\nimport android.os.Vibrator\nimport com.lyy.keepassa.base.BaseApp\n\n/**\n * 震动工具\n */\nobject VibratorUtil {\n  private val vb: Vibrator = BaseApp.APP.getSystemService(Service.VIBRATOR_SERVICE) as Vibrator\n\n  /**\n   * 震动一次\n   */\n  fun vibrator(time: Long) {\n    if (!vb.hasVibrator()) {\n      return\n    }\n    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {\n      vb.vibrate(VibrationEffect.createOneShot(time, VibrationEffect.DEFAULT_AMPLITUDE))\n    } else {\n      vb.vibrate(time)\n    }\n  }\n\n  /**\n   * 指定手机以pattern指定的模式振动\n   * @param pattern pattern为new int[200,400,600,800],就是让他在200,400,600,800这个时间交替启动与关闭振动器!\n   * @param repeat 而第二个则是重复次数,如果是-1的只振动一次,如果是0的话则一直振动\n   */\n  fun vibrate(\n    pattern: LongArray,\n    repeat: Int\n  ) {\n    if (!vb.hasVibrator()) {\n      return\n    }\n    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {\n      vb.vibrate(VibrationEffect.createWaveform(pattern, repeat))\n    } else {\n      vb.vibrate(pattern, repeat)\n    }\n  }\n\n  fun cancel() {\n    vb.cancel()\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/util/cloud/CloudFileInfo.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.util.cloud\n\nimport java.util.Date\n\n/**\n * 云端文件信息\n */\ndata class CloudFileInfo(\n  val fileKey: String,    // webDav/dropbox中为云端路径，onedrive为id\n  val fileName: String,     // 文件名\n  val serviceModifyDate: Date, // 该文件在云端的修改时间\n  val size: Long,         // 文件大小\n  val isDir: Boolean,\n  val contentHash: String? = null,   // hash\n)"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/util/cloud/CloudUtilFactory.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.util.cloud\n\nimport com.lyy.keepassa.view.StorageType\nimport com.lyy.keepassa.view.StorageType.DROPBOX\nimport com.lyy.keepassa.view.StorageType.ONE_DRIVE\nimport com.lyy.keepassa.view.StorageType.WEBDAV\n\n/**\n * 云端文件工具工厂\n */\nobject CloudUtilFactory {\n\n  fun getCloudUtil(storageType: StorageType): ICloudUtil {\n    return when (storageType) {\n      DROPBOX -> DropboxUtil\n      WEBDAV -> WebDavUtil\n      ONE_DRIVE -> OneDriveUtil\n      else ->  throw IllegalArgumentException(\"不识别的工具类型：${storageType}\")\n    }\n  }\n\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/util/cloud/DbSynUtil.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.util.cloud\n\nimport android.content.Context\nimport android.net.Uri\nimport android.text.TextUtils\nimport com.arialyy.frame.util.SharePreUtil\nimport com.arialyy.frame.util.StringUtil\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseApp\nimport com.lyy.keepassa.base.Constance\nimport com.lyy.keepassa.entity.DbHistoryRecord\nimport com.lyy.keepassa.util.HitUtil\nimport com.lyy.keepassa.util.KeepassAUtil\nimport com.lyy.keepassa.util.cloud.interceptor.DbSyncCheckInterceptor\nimport com.lyy.keepassa.util.cloud.interceptor.DbSyncCompareInterceptor\nimport com.lyy.keepassa.util.cloud.interceptor.DbSyncRequest\nimport com.lyy.keepassa.util.cloud.interceptor.DbSyncResponse\nimport com.lyy.keepassa.util.cloud.interceptor.DbSyncUploadInterceptor\nimport com.lyy.keepassa.util.cloud.interceptor.IDbSyncInterceptor\nimport com.lyy.keepassa.view.StorageType\nimport com.lyy.keepassa.view.StorageType.AFS\nimport timber.log.Timber\nimport java.io.File\nimport java.util.Date\n\n/**\n * 数据库同步工具\n */\nobject DbSynUtil : SynStateCode {\n\n  private val TAG = StringUtil.getClassName(this)\n  private const val KEY_SERVICE_MODIFY_TIME = \"KEY_SERVICE_MODIFY_TIME\"\n\n  /**\n   * 打开数据库时，记录的云盘时间，上传完成需要重新更新\n   */\n  var serviceModifyTime: Date = Date(System.currentTimeMillis())\n\n  init {\n    serviceModifyTime =\n      Date(SharePreUtil.getLong(Constance.PRE_FILE_NAME, BaseApp.APP, KEY_SERVICE_MODIFY_TIME))\n  }\n\n  private val interceptors = arrayListOf<IDbSyncInterceptor>().apply {\n    add(DbSyncCheckInterceptor())\n    add(DbSyncCompareInterceptor())\n    add(DbSyncUploadInterceptor())\n  }\n\n  /**\n   * 或去该记录在云端的修改时间\n   */\n  suspend fun getFileServiceModifyTime(record: DbHistoryRecord): Date {\n    return CloudUtilFactory.getCloudUtil(record.getDbPathType())\n      .getFileServiceModifyTime(record.cloudDiskPath!!)\n  }\n\n  suspend fun fileExists(record: DbHistoryRecord): Boolean {\n    return CloudUtilFactory.getCloudUtil(record.getDbPathType()).fileExists(record.cloudDiskPath!!)\n  }\n\n  suspend fun getFileInfo(record: DbHistoryRecord): CloudFileInfo? {\n    return CloudUtilFactory.getCloudUtil(record.getDbPathType()).getFileInfo(record.cloudDiskPath!!)\n  }\n\n  /**\n   * 更新服务器端文件的修改时间\n   */\n  suspend fun updateServiceModifyTime(\n    record: DbHistoryRecord\n  ) {\n    serviceModifyTime = CloudUtilFactory.getCloudUtil(record.getDbPathType())\n      .getFileServiceModifyTime(record.cloudDiskPath!!)\n    SharePreUtil.putLong(\n      Constance.PRE_FILE_NAME,\n      BaseApp.APP, KEY_SERVICE_MODIFY_TIME, serviceModifyTime.time\n    )\n    Timber.d(\n      TAG, \"更新云端文件修改时间：${KeepassAUtil.instance.formatTime(serviceModifyTime)}\"\n    )\n  }\n\n  /**\n   * 从云端下载的文件缓存路径\n   * @param cloudTypeName 云端网盘名\n   */\n  fun getCloudDbTempPath(\n    cloudTypeName: String,\n    dbName: String\n  ): Uri {\n    val file = File(\"${BaseApp.APP.cacheDir.path}/$cloudTypeName/${dbName}\")\n    if (file.parentFile != null && !file.parentFile!!.exists()) {\n      file.parentFile!!.mkdirs()\n    }\n    return Uri.fromFile(file)\n  }\n\n  /**\n   * 上传同步\n   */\n  suspend fun uploadSyn(record: DbHistoryRecord, isCreate: Boolean = false): DbSyncResponse {\n    val storageType = record.getDbPathType()\n    if (storageType == AFS) {\n      return DbSyncResponse(STATE_SUCCEED, \"\")\n    }\n    val util = CloudUtilFactory.getCloudUtil(storageType)\n    if (isCreate) {\n      val ins = arrayListOf<IDbSyncInterceptor>().apply {\n        add(DbSyncUploadInterceptor())\n      }\n      return ins[0].intercept(DbSyncRequest(record, util, ins))\n    }\n    return interceptors[0].intercept(\n      DbSyncRequest(\n        record,\n        util,\n        interceptors\n      )\n    )\n  }\n\n  /**\n   * 只用于下载，如果文件存在，先会删除文件，再执行下载\n   */\n  suspend fun downloadOnly(\n    context: Context,\n    dbRecord: DbHistoryRecord,\n    filePath: Uri\n  ): String? {\n    Timber.i(\"开始下载文件，云端路径：${dbRecord.cloudDiskPath}，文件保存路径：${filePath}\")\n    val util = CloudUtilFactory.getCloudUtil(StorageType.valueOf(dbRecord.type))\n    val path = util.downloadFile(context, dbRecord, filePath)\n    if (!TextUtils.isEmpty(path)) {\n      updateServiceModifyTime(dbRecord)\n    }\n    return path\n  }\n\n  fun toask(\n    msg: String,\n    success: Boolean,\n    des: String\n  ) {\n    BaseApp.handler.post {\n      HitUtil.toaskShort(\n        \"$msg ${\n          if (success) BaseApp.APP.getString(R.string.success) else BaseApp.APP.getString(\n            R.string.fail\n          )\n        } $des\"\n      )\n    }\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/util/cloud/DropboxContentHasher.java",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.util.cloud;\n\nimport java.nio.ByteBuffer;\nimport java.security.DigestException;\nimport java.security.MessageDigest;\nimport java.security.NoSuchAlgorithmException;\n\n/**\n * Computes a hash using the same algorithm that the Dropbox API uses for the\n * the \"content_hash\" metadata field.\n *\n * <p>\n * The {@link #digest()} method returns a raw binary representation of the hash.\n * The \"content_hash\" field in the Dropbox API is a hexadecimal-encoded version\n * of the digest.\n * </p>\n *\n * <p>\n * Example:\n * </p>\n *\n * <pre>\n * MessageDigest hasher = new DropboxContentHasher();\n * byte[] buf = new byte[1024];\n * InputStream in = new FileInputStream(\"some-file\");\n * try {\n *     while (true) {\n *         int n = in.read(buf);\n *         if (n &lt; 0) break;  // EOF\n *         hasher.update(buf, 0, n);\n *     }\n * }\n * finally {\n *     in.close();\n * }\n *\n * byte[] rawHash = hasher.digest();\n * System.out.println(hex(rawHash));\n *     // Assuming 'hex' is a method that converts a byte[] to\n *     // a hexadecimal-encoded String\n * </pre>\n *\n * <p>\n * If you need to hash something as it passes through a stream, you can use the\n * {@link java.security.DigestInputStream} or {@code java.security.DigestOutputStream} helpers.\n * </p>\n *\n * <pre>\n * MessageDigest hasher = new DropboxContentHasher();\n * InputStream in = new FileInputStream(\"some-file\");\n * UploadResponse r;\n * try {\n *     r = someApiClient.upload(new DigestInputStream(in, hasher)));\n * }\n * finally {\n *     in.close();\n * }\n *\n * String locallyComputed = hex(hasher.digest());\n * assert r.contentHash.equals(locallyComputed);\n * </pre>\n */\npublic final class DropboxContentHasher extends MessageDigest implements Cloneable {\n  private MessageDigest overallHasher;\n  private MessageDigest blockHasher;\n  private int blockPos = 0;\n\n  public static final int BLOCK_SIZE = 4 * 1024 * 1024;\n\n  public DropboxContentHasher() {\n    this(newSha256Hasher(), newSha256Hasher(), 0);\n  }\n\n  private DropboxContentHasher(MessageDigest overallHasher, MessageDigest blockHasher,\n      int blockPos) {\n    super(\"Dropbox-Content-Hash\");\n    this.overallHasher = overallHasher;\n    this.blockHasher = blockHasher;\n    this.blockPos = blockPos;\n  }\n\n  @Override\n  protected void engineUpdate(byte input) {\n    finishBlockIfFull();\n\n    blockHasher.update(input);\n    blockPos += 1;\n  }\n\n  @Override\n  protected int engineGetDigestLength() {\n    return overallHasher.getDigestLength();\n  }\n\n  @Override\n  protected void engineUpdate(byte[] input, int offset, int len) {\n    int inputEnd = offset + len;\n    while (offset < inputEnd) {\n      finishBlockIfFull();\n\n      int spaceInBlock = BLOCK_SIZE - this.blockPos;\n      int inputPartEnd = Math.min(inputEnd, offset + spaceInBlock);\n      int inputPartLength = inputPartEnd - offset;\n      blockHasher.update(input, offset, inputPartLength);\n\n      blockPos += inputPartLength;\n      offset += inputPartLength;\n    }\n  }\n\n  @Override\n  protected void engineUpdate(ByteBuffer input) {\n    int inputEnd = input.limit();\n    while (input.position() < inputEnd) {\n      finishBlockIfFull();\n\n      int spaceInBlock = BLOCK_SIZE - this.blockPos;\n      int inputPartEnd = Math.min(inputEnd, input.position() + spaceInBlock);\n      int inputPartLength = inputPartEnd - input.position();\n      input.limit(inputPartEnd);\n      blockHasher.update(input);\n\n      blockPos += inputPartLength;\n      input.position(inputPartEnd);\n    }\n  }\n\n  @Override\n  protected byte[] engineDigest() {\n    finishBlockIfNonEmpty();\n    return overallHasher.digest();\n  }\n\n  @Override\n  protected int engineDigest(byte[] buf, int offset, int len)\n      throws DigestException {\n    finishBlockIfNonEmpty();\n    return overallHasher.digest(buf, offset, len);\n  }\n\n  @Override\n  protected void engineReset() {\n    this.overallHasher.reset();\n    this.blockHasher.reset();\n    this.blockPos = 0;\n  }\n\n  @Override\n  public DropboxContentHasher clone()\n      throws CloneNotSupportedException {\n    DropboxContentHasher clone = (DropboxContentHasher) super.clone();\n    clone.overallHasher = (MessageDigest) clone.overallHasher.clone();\n    clone.blockHasher = (MessageDigest) clone.blockHasher.clone();\n    return clone;\n  }\n\n  private void finishBlock() {\n    overallHasher.update(blockHasher.digest());\n    blockPos = 0;\n  }\n\n  private void finishBlockIfFull() {\n    if (blockPos == BLOCK_SIZE) {\n      finishBlock();\n    }\n  }\n\n  private void finishBlockIfNonEmpty() {\n    if (blockPos > 0) {\n      finishBlock();\n    }\n  }\n\n  static MessageDigest newSha256Hasher() {\n    try {\n      return MessageDigest.getInstance(\"SHA-256\");\n    } catch (NoSuchAlgorithmException ex) {\n      throw new AssertionError(\"Couldn't create SHA-256 hasher\");\n    }\n  }\n\n  static final char[] HEX_DIGITS = new char[] {\n      '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',\n      'a', 'b', 'c', 'd', 'e', 'f'\n  };\n\n  public static String hex(byte[] data) {\n    char[] buf = new char[2 * data.length];\n    int i = 0;\n    for (byte b : data) {\n      buf[i++] = HEX_DIGITS[(b & 0xf0) >>> 4];\n      buf[i++] = HEX_DIGITS[b & 0x0f];\n    }\n    return new String(buf);\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/util/cloud/DropboxUtil.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.util.cloud\n\nimport android.content.Context\nimport android.net.Uri\nimport android.text.TextUtils\nimport com.arialyy.frame.util.SharePreUtil\nimport com.dropbox.core.DbxRequestConfig\nimport com.dropbox.core.android.Auth\nimport com.dropbox.core.http.OkHttp3Requestor\nimport com.dropbox.core.oauth.DbxCredential\nimport com.dropbox.core.v2.DbxClientV2\nimport com.dropbox.core.v2.files.DeletedMetadata\nimport com.dropbox.core.v2.files.FileMetadata\nimport com.keepassdroid.utils.UriUtil\nimport com.lyy.keepassa.base.BaseApp\nimport com.lyy.keepassa.base.Constance\nimport com.lyy.keepassa.entity.DbHistoryRecord\nimport com.lyy.keepassa.util.QuickUnLockUtil\nimport timber.log.Timber\nimport java.util.Date\n\n/**\n * dropbox 工具\n */\nobject DropboxUtil : ICloudUtil {\n  val APP_KEY = \"ib45r6jnfz3oakq\"\n  private val DROPBOX_KEY_TOKEN = \"DROPBOX_KEY_TOKEN\"\n\n  /**\n   * 是否申请短期令牌\n   * {@code true} 使用短期令牌\n   */\n  private val USE_SLT = false\n\n  private var sDbxClient: DbxClientV2? = null\n\n  private var sDbxRequestConfig: DbxRequestConfig? = null\n\n  /**\n   * 配置dropbox请求信息\n   */\n  private fun getRequestConfig(): DbxRequestConfig? {\n    if (sDbxRequestConfig == null) {\n      sDbxRequestConfig = DbxRequestConfig.newBuilder(\"keepassA\")\n        .withHttpRequestor(OkHttp3Requestor(OkHttp3Requestor.defaultOkHttpClient()))\n        .build()\n    }\n    return sDbxRequestConfig\n  }\n\n  /**\n   * 通过token 初始化客户端\n   */\n  @Synchronized\n  private fun init(accessToken: String) {\n    if (sDbxClient == null) {\n      sDbxClient = DbxClientV2(\n        getRequestConfig(), accessToken\n      )\n    }\n  }\n\n  /**\n   * 通过令牌初始化客户端\n   */\n  @Synchronized\n  private fun init(credential: DbxCredential) {\n    val temp = DbxCredential(\n      credential.accessToken, -1L, credential.refreshToken, credential.appKey\n    )\n    if (sDbxClient == null) {\n      sDbxClient = DbxClientV2(\n        getRequestConfig(), temp\n      )\n    }\n  }\n\n  /**\n   * 获取dropbox 客户端\n   * @retun 如果没有授权，返null，需要进入授权页面[Auth.startOAuth2Authentication]\n   */\n  fun getClient(): DbxClientV2? {\n    var token = getLocalToken()\n    if (TextUtils.isEmpty(token)) {\n      token = Auth.getOAuth2Token()\n      if (!TextUtils.isEmpty(token)) {\n        saveToken(token)\n      } else {\n        return null\n      }\n    }\n    if (token != null) {\n      init(token)\n    }\n    return sDbxClient\n  }\n\n  override suspend fun fileExists(fileKey: String): Boolean {\n    return getFileInfo(fileKey) != null\n  }\n\n  /**\n   * https://github.com/dropbox/dropbox-sdk-obj-c/issues/32\n   * dropbox 的根路径是：\"\"\n   */\n  override fun getRootPath(): String {\n    return \"\"\n  }\n\n  override suspend fun getFileList(path: String): List<CloudFileInfo>? {\n    Timber.d(\"开始获取文件列表，path = $path\")\n    val client = getClient() ?: return null\n    val entries = client.files()\n      .listFolder(path).entries\n    if (entries.isEmpty()) {\n      return null\n    }\n\n    val list = ArrayList<CloudFileInfo>()\n    val root = if (TextUtils.isEmpty(path)) \"\" else path\n    for (e in entries) {\n      if (e is FileMetadata) {\n        list.add(\n          CloudFileInfo(\"$root/${e.name}\", e.name, e.serverModified, e.size, false, e.contentHash)\n        )\n      } else {\n        list.add(CloudFileInfo(\"$root/${e.name}\", e.name, Date(), 0, true))\n      }\n    }\n    return list\n  }\n\n  /**\n   * [hash 算法](https://www.dropbox.com/developers/reference/content-hash)\n   *\n   */\n  override suspend fun checkContentHash(\n    cloudFileHash: String?,\n    localFileUri: Uri\n  ): Boolean {\n    if (cloudFileHash == null){\n      return false\n    }\n    val hasher = DropboxContentHasher()\n    val buf = ByteArray(1024)\n    val ips = UriUtil.getUriInputStream(BaseApp.APP, localFileUri)\n    while (true) {\n      val n = ips.read(buf)\n      if (n < 0) break\n      hasher.update(buf, 0, n)\n    }\n    val localHash = DropboxContentHasher.hex(hasher.digest())\n    ips.close()\n    Timber.i(\"本地文件hash: $localHash\")\n    return cloudFileHash.equals(localHash, ignoreCase = true)\n  }\n\n  override suspend fun getFileInfo(fileKey: String): CloudFileInfo? {\n    val client = getClient() ?: return null\n    try {\n      val entries = client.files()\n        .listRevisions(fileKey).entries\n      val entry = entries[0]\n      return CloudFileInfo(\n        fileKey, entry.name, entry.serverModified, entry.size, true, entry.contentHash\n      )\n    } catch (e: Exception) {\n      Timber.e(e)\n    }\n    return null\n  }\n\n  override suspend fun delFile(fileKey: String): Boolean {\n    Timber.d(\"删除云端文件: $fileKey\")\n    val d = getClient()\n      ?.files()\n      ?.deleteV2(fileKey)\n    if (d == null || d.metadata == null || d.metadata is DeletedMetadata) {\n      return false\n    }\n    return true\n  }\n\n  /**\n   * 获取服务器端文件的修改时间\n   */\n  override suspend fun getFileServiceModifyTime(fileKey: String): Date {\n    val client = getClient() ?: return Date(System.currentTimeMillis())\n    val entries = client.files()\n      .listRevisions(fileKey).entries\n    return entries[0].serverModified\n  }\n\n  /**\n   * 上传文件\n   */\n  override suspend fun uploadFile(\n    context: Context,\n    dbRecord: DbHistoryRecord\n  ): Boolean {\n\n    val dbUri = Uri.parse(dbRecord.localDbUri)\n    val cloudDiskPath = dbRecord.cloudDiskPath\n\n    val ips = BaseApp.APP.contentResolver.openInputStream(dbUri)\n    val fd = getClient()\n      ?.files()\n      ?.uploadBuilder(cloudDiskPath)\n      ?.uploadAndFinish(ips)\n    if (fd != null) {\n      DbSynUtil.serviceModifyTime = fd.serverModified\n    }\n    ips?.close()\n    return true\n  }\n\n  override suspend fun downloadFile(\n    context: Context,\n    dbRecord: DbHistoryRecord,\n    filePath: Uri\n  ): String? {\n    val client = getClient() ?: return null\n    val os = context.contentResolver.openOutputStream(filePath)\n    client.files()\n      .download(dbRecord.cloudDiskPath)\n      .download(os)\n    os?.let {\n      it.flush()\n      it.close()\n    }\n    return filePath.toString()\n  }\n\n  /**\n   * 保存token\n   */\n  fun saveToken(token: String) {\n    SharePreUtil.putString(\n      Constance.PRE_FILE_NAME, BaseApp.APP,\n      DROPBOX_KEY_TOKEN,\n      QuickUnLockUtil.encryptStr(token)\n    )\n  }\n\n  /**\n   * 获取本地保存的token\n   * @return 没有保存的token，返回null\n   */\n  private fun getLocalToken(): String? {\n    val token = SharePreUtil.getString(\n      Constance.PRE_FILE_NAME, BaseApp.APP,\n      DROPBOX_KEY_TOKEN\n    )\n    return if (!TextUtils.isEmpty(token)) {\n      QuickUnLockUtil.decryption(token)\n    } else {\n      null\n    }\n  }\n\n  /**\n   * 判断是否登录成功，如果没有授权，请使用[Auth.startOAuth2Authentication]启动授权界面\n   * @return false 没有登录\n   */\n  fun isAuthorized(): Boolean {\n    val token = SharePreUtil.getString(\n      Constance.PRE_FILE_NAME, BaseApp.APP,\n      DROPBOX_KEY_TOKEN\n    )\n    return !TextUtils.isEmpty(token)\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/util/cloud/ICloudUtil.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.util.cloud\n\nimport android.content.Context\nimport android.net.Uri\nimport com.lyy.keepassa.entity.DbHistoryRecord\nimport java.util.Date\n\n/**\n * 云盘工具\n */\ninterface ICloudUtil {\n\n  /**\n   * 文件是否存在\n   * @param fileKey webDav/dropbox中为云端路径，onedrive为id\n   * @return false 文件不存在\n   */\n  suspend fun fileExists(fileKey: String): Boolean\n\n  /**\n   * 获取云盘根路径\n   */\n  fun getRootPath(): String\n\n  /**\n   * 获取文件列表\n   * @return 如果获取不到文件列表，返回null\n   */\n  suspend fun getFileList(dirPath: String): List<CloudFileInfo>?\n\n  /**\n   * 检查云端文件的hash和本地文件的hash是否一致\n   * @param cloudFileHash 云端文件的hash\n   * @param localFileUri 本地文件的Uri\n   * @return true 两端文件一致\n   */\n  suspend fun checkContentHash(\n    cloudFileHash: String?,\n    localFileUri: Uri\n  ): Boolean\n\n  /**\n   * 获取云端文件信息\n   * @param fileKey webDav/dropbox中为云端路径，onedrive为id\n   * @return null 云端文件不存在\n   */\n  suspend fun getFileInfo(fileKey: String): CloudFileInfo?\n\n  /**\n   * 删除文件\n   * @param fileKey webDav/dropbox中为云端路径，onedrive为id\n   * @return true 删除成功\n   */\n  suspend fun delFile(fileKey: String): Boolean\n\n  /**\n   * 云端文件的修改时间\n   * @param fileKey 云端文件路径\n   */\n  suspend fun getFileServiceModifyTime(fileKey: String): Date\n\n  /**\n   * 上传文件，上传完成需要更新[DbSynUtil.serviceModifyTime]\n   * @param dbRecord 文件打开记录\n   * @return true 上传成功\n   */\n  suspend fun uploadFile(\n    context: Context,\n    dbRecord: DbHistoryRecord\n  ): Boolean\n\n  /**\n   * 下载文件\n   * @param filePath 文件保存路径\n   * @return 文件保存路径，null 表示下载失败\n   */\n  suspend fun downloadFile(\n    context: Context,\n    dbRecord: DbHistoryRecord,\n    filePath: Uri\n  ): String?\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/util/cloud/OneDriveUtil.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.util.cloud\n\nimport android.app.Activity\nimport android.content.Context\nimport android.net.Uri\nimport com.arialyy.frame.base.net.NetManager1\nimport com.arialyy.frame.core.AbsFrame\nimport com.arialyy.frame.util.FileUtil\nimport com.blankj.utilcode.util.EncryptUtils\nimport com.blankj.utilcode.util.ToastUtils\nimport com.google.gson.Gson\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseApp\nimport com.lyy.keepassa.entity.DbHistoryRecord\nimport com.lyy.keepassa.ondrive.MsalApi\nimport com.lyy.keepassa.ondrive.MsalSourceItem\nimport com.lyy.keepassa.util.HitUtil\nimport com.lyy.keepassa.util.KLog\nimport com.lyy.keepassa.util.KeepassAUtil\nimport com.lyy.keepassa.util.getBytes\nimport com.microsoft.identity.client.AuthenticationCallback\nimport com.microsoft.identity.client.IAccount\nimport com.microsoft.identity.client.IAuthenticationResult\nimport com.microsoft.identity.client.IPublicClientApplication.ISingleAccountApplicationCreatedListener\nimport com.microsoft.identity.client.ISingleAccountPublicClientApplication\nimport com.microsoft.identity.client.ISingleAccountPublicClientApplication.CurrentAccountCallback\nimport com.microsoft.identity.client.ISingleAccountPublicClientApplication.SignOutCallback\nimport com.microsoft.identity.client.PublicClientApplication\nimport com.microsoft.identity.client.SilentAuthenticationCallback\nimport com.microsoft.identity.client.exception.MsalException\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.withContext\nimport okhttp3.Headers\nimport okhttp3.MediaType.Companion.toMediaType\nimport okhttp3.MultipartBody\nimport okhttp3.OkHttpClient\nimport okhttp3.Request\nimport okhttp3.RequestBody.Companion.asRequestBody\nimport org.joda.time.format.DateTimeFormat\nimport timber.log.Timber\nimport java.io.File\nimport java.io.FileOutputStream\nimport java.net.HttpURLConnection\nimport java.nio.charset.Charset\nimport java.util.Date\n\n/**\n * Onedrive util\n */\nobject OneDriveUtil : ICloudUtil {\n\n  /**\n   * 应用根目录\n   */\n  const val APP_ROOT_DIR = \"approot\"\n\n  /**\n   * token key\n   */\n  const val TOKEN_KEY = \"Authorization\"\n\n  private const val BASE_URL = \"https://graph.microsoft.com/v1.0/\"\n\n  private lateinit var oneDriveApp: ISingleAccountPublicClientApplication\n  private var authInfo: IAuthenticationResult? = null\n  private val netManager by lazy {\n    NetManager1().builderManager(BASE_URL, arrayListOf())\n  }\n  private val dateFormat = DateTimeFormat.forPattern(\"yyyy-MM-dd'T'HH:mm:ss.SSSZ\")\n  var loginCallback: OnLoginCallback? = null\n  private var getTokenFailNum = 0\n  private val MAX_FAIL_NUM = 1\n  private val okClient by lazy {\n    OkHttpClient()\n  }\n\n  fun isInitialized(): Boolean = this::oneDriveApp.isInitialized\n\n  interface OnLoginCallback {\n    fun callback(success: Boolean)\n  }\n\n  private fun getContext(): Context {\n    return BaseApp.APP\n  }\n\n  private fun getCurActivity(): Activity {\n    return AbsFrame.getInstance().currentActivity!!\n  }\n\n  private fun getAuthInfo() = authInfo!!\n\n  private fun getUserId() = authInfo?.account?.id ?: \"\"\n\n  /**\n   * check login\n   * @return true login success\n   */\n  private fun checkLogin(): Boolean {\n    if (authInfo == null || !this::oneDriveApp.isInitialized) {\n      Timber.e(\"登陆失败，sdk没有初始化，或登陆失败\")\n      HitUtil.toaskShort(\"${getContext().getString(R.string.login)}${getContext().getString(R.string.fail)}\")\n      return false\n    }\n    return true\n  }\n\n  /**\n   * 初始化\n   */\n  fun initOneDrive(callback: (Boolean) -> Unit) {\n    PublicClientApplication.createSingleAccountPublicClientApplication(\n      getContext(), R.raw.auth_config_single_account_release,\n      object : ISingleAccountApplicationCreatedListener {\n        override fun onCreated(application: ISingleAccountPublicClientApplication) {\n          /**\n           * This test app assumes that the app is only going to support one account.\n           * This requires \"account_mode\" : \"SINGLE\" in the config json file.\n           */\n          oneDriveApp = application\n          Timber.d(\"初始化成功\")\n          callback.invoke(true)\n        }\n\n        override fun onError(exception: MsalException) {\n          exception.printStackTrace()\n          callback.invoke(false)\n        }\n      })\n  }\n\n  /**\n   * 加载用户，没有登陆过，则需要重新登陆\n   */\n  fun loadAccount() {\n    if (!this::oneDriveApp.isInitialized) {\n      Timber.e(\"还没有初始化sdk\")\n      return\n    }\n\n    oneDriveApp.getCurrentAccountAsync(object : CurrentAccountCallback {\n      override fun onAccountLoaded(activeAccount: IAccount?) {\n        if (activeAccount == null) {\n          Timber.w(\"用户还没有登陆\")\n          login()\n          return\n        }\n        Timber.w(\"已经登陆过，自动登陆，开始获取token\")\n        getTokenByAccountInfo(activeAccount)\n      }\n\n      override fun onAccountChanged(\n        priorAccount: IAccount?,\n        currentAccount: IAccount?\n      ) {\n        Timber.w(\"账号已却换，重新获取token\")\n        if (currentAccount == null) {\n          Timber.e(\"当前账户为空\")\n          return\n        }\n        getTokenByAccountInfo(currentAccount)\n      }\n\n      override fun onError(exception: MsalException) {\n        exception.printStackTrace()\n        ToastUtils.showLong(R.string.one_drive_load_user_failure)\n      }\n    })\n  }\n\n  /**\n   * 根据用户信息获取token\n   */\n  private fun getTokenByAccountInfo(account: IAccount) {\n    oneDriveApp.acquireTokenSilentAsync(getScopes(), account.authority, object :\n      SilentAuthenticationCallback {\n      override fun onSuccess(authenticationResult: IAuthenticationResult?) {\n        Timber.d(\"获取token成功\")\n        authInfo = authenticationResult\n        loginCallback?.callback(true)\n      }\n\n      override fun onError(exception: MsalException) {\n        Timber.d(\"获取token失败，重新启动登陆流程\")\n        exception.printStackTrace()\n        if (getTokenFailNum >= MAX_FAIL_NUM) {\n          HitUtil.toaskShort(R.string.get_token_fail)\n          loginCallback?.callback(false)\n          return\n        }\n        oneDriveApp.signOut(object : SignOutCallback {\n          override fun onSignOut() {\n            Timber.d(\"登出成功，重新开始登陆流程\")\n            login()\n            getTokenFailNum += 1\n          }\n\n          override fun onError(exception: MsalException) {\n            Timber.e(\"登出失败\")\n            exception.printStackTrace()\n            loginCallback?.callback(false)\n          }\n        })\n      }\n    })\n  }\n\n  private fun login() {\n    oneDriveApp.signIn(getCurActivity(), \"\", getScopes(), object : AuthenticationCallback {\n      override fun onSuccess(authenticationResult: IAuthenticationResult?) {\n        authInfo = authenticationResult\n        HitUtil.toaskShort(\"${getContext().getString(R.string.login)}${getContext().getString(R.string.success)}\")\n        Timber.d(\"登陆成功\")\n        loginCallback?.callback(true)\n      }\n\n      override fun onError(exception: MsalException?) {\n        HitUtil.toaskShort(\"${getContext().getString(R.string.login)}${getContext().getString(R.string.fail)}\")\n        Timber.d(\"登陆失败\")\n        exception?.printStackTrace()\n        loginCallback?.callback(false)\n      }\n\n      override fun onCancel() {\n        Timber.d(\"取消登陆\")\n        HitUtil.toaskShort(\"${getContext().getString(R.string.login)}${getContext().getString(R.string.cancel)}\")\n        loginCallback?.callback(false)\n      }\n    })\n  }\n\n  private fun getScopes(): Array<String> {\n    return arrayOf(\"User.Read\", \"Files.ReadWrite.AppFolder\")\n  }\n\n  private fun msalItem2CloudItem(item: MsalSourceItem): CloudFileInfo {\n    return CloudFileInfo(\n      fileKey = item.id,\n      fileName = item.name,\n      serviceModifyDate = dateFormat.parseDateTime(item.lastModifiedDateTime)\n        .toDate(),\n      size = item.size,\n      isDir = item.isFolder(),\n      contentHash = if (item.isFolder()) null else item.file?.hashes?.sha256Hash\n    )\n  }\n\n  override suspend fun fileExists(fileKey: String): Boolean {\n    return getFileInfo(fileKey) != null\n  }\n\n  override fun getRootPath(): String {\n    return \"/\"\n  }\n\n  override suspend fun getFileList(path: String): List<CloudFileInfo>? {\n    if (!checkLogin()) {\n      return null\n    }\n    Timber.d(\"获取文件列表，path = $path\")\n    val response = if (path == \"/\") {\n      netManager.request(MsalApi::class.java)\n        .getAppFolderList(getAuthInfo().accessToken, getUserId())\n    } else {\n      netManager.request(MsalApi::class.java)\n        .getFolderListById(getAuthInfo().accessToken, getUserId(), path)\n    }\n    if (response.value == null) {\n      return null\n    }\n    val fileList = arrayListOf<CloudFileInfo>()\n\n    response.value?.forEach {\n      fileList.add(msalItem2CloudItem(it))\n    }\n\n    return fileList\n  }\n\n  override suspend fun checkContentHash(\n    cloudFileHash: String?,\n    localFileUri: Uri\n  ): Boolean {\n    if (cloudFileHash == null) {\n      return false\n    }\n    Timber.d(\"localFileUri = $localFileUri\")\n    val bytes = localFileUri.getBytes()\n    if (bytes == null) {\n      Timber.e(\"localFileUri get bytes null\")\n      return false\n    }\n    val localHash = EncryptUtils.encryptSHA256ToString(bytes)\n    Timber.d(\"cloudFileHash = $cloudFileHash，localHash = $localHash\")\n    return localHash == cloudFileHash\n  }\n\n  override suspend fun getFileInfo(fileKey: String): CloudFileInfo? {\n    val userId = getUserId()\n    Timber.d(\"getFileInfo, userId = ${userId}, fileKey = $fileKey\")\n\n    try {\n      val response: MsalSourceItem? = if (fileKey.startsWith(\"/\")) {\n        netManager.request(MsalApi::class.java)\n          .getFileInfoByPath(\n            getAuthInfo().accessToken,\n            userId,\n            fileKey.substring(1, fileKey.length)\n          )\n      } else {\n        netManager.request(MsalApi::class.java)\n          .getFileInfoById(getAuthInfo().accessToken, userId, fileKey)\n      }\n      if (response == null) {\n        Timber.e(\"获取文件信息失败\")\n        return null\n      }\n      return msalItem2CloudItem(response)\n    } catch (e: Exception) {\n      Timber.e(e)\n      return null\n    }\n  }\n\n  /**\n   * 如果成功，此调用将返回 204 No Content 响应，以指明资源已被删除，没有可返回的内容。\n   */\n  override suspend fun delFile(fileKey: String): Boolean {\n    Timber.d(\"删除文件，fileKey = $fileKey\")\n    val response = netManager.request(MsalApi::class.java)\n      .deleteFile(getAuthInfo().accessToken, getUserId(), fileKey)\n    return response.code() == HttpURLConnection.HTTP_NO_CONTENT\n  }\n\n  override suspend fun getFileServiceModifyTime(fileKey: String): Date {\n    val fileInfo = getFileInfo(fileKey) ?: return Date(System.currentTimeMillis())\n    return fileInfo.serviceModifyDate\n  }\n\n  override suspend fun uploadFile(\n    context: Context,\n    dbRecord: DbHistoryRecord\n  ): Boolean {\n    val file = File(Uri.parse(dbRecord.localDbUri).path!!)\n    try {\n      // 创建上传session\n      val uploadSession = netManager.request(MsalApi::class.java)\n        .createUploadSession(\n          authorization = getAuthInfo().accessToken,\n          userId = getUserId(),\n          itemPath = file.name\n        )\n\n      Timber.d(\"获取session成功，上传地址：${uploadSession.uploadUrl}\")\n      // 取消上传的临时文件会话\n      cancelUploadSession(uploadSession.uploadUrl)\n\n      // 开始上传文件\n      val desc = file.asRequestBody(\"multipart/form-data\".toMediaType())\n      val body = MultipartBody.Part.createFormData(\"file\", file.name, desc)\n      val fileSize = file.length()\n      val request = Request.Builder()\n        .header(\"Content-Length\", fileSize.toString())\n        .header(\"Content-Range\", \"bytes 0-${fileSize - 1}/${fileSize}\")\n        .url(uploadSession.uploadUrl)\n        .put(body = body.body)\n        .build()\n      val response = okClient.newCall(request)\n        .execute()\n      if (response.code != HttpURLConnection.HTTP_OK && response.code != HttpURLConnection.HTTP_CREATED) {\n        Timber.e(\"上传失败，code = ${response.code}, msg = ${response.message}\")\n        return false\n      }\n      val responseBytes = response.body?.bytes()\n      if (responseBytes == null) {\n        Timber.e(\"body为null\")\n        return false\n      }\n      val responseContent = String(responseBytes, Charset.forName(\"UTF-8\"))\n      Timber.d(\"上传成功，响应内容\")\n      KLog.j(\"TAG\", responseContent)\n      val obj = Gson().fromJson(responseContent, MsalSourceItem::class.java)\n      dbRecord.cloudDiskPath = obj.id\n      KeepassAUtil.instance.saveLastOpenDbHistory(dbRecord)\n\n      return true\n    } catch (e: Exception) {\n      Timber.e(e)\n      return false\n    }\n  }\n\n  /**\n   * 取消上传会话\n   */\n  private fun cancelUploadSession(uploadUrl: String) {\n    try {\n      Timber.d(\"如果有的话，取消临时文件的上传对话\")\n      val request = Request.Builder()\n        .url(uploadUrl)\n        .delete()\n        .build()\n      val response = okClient.newCall(request)\n        .execute()\n      Timber.d(\"code = ${response.code}, body = ${response.body?.string()}\")\n    } catch (e: Exception) {\n      Timber.e(e)\n    }\n  }\n\n  override suspend fun downloadFile(\n    context: Context,\n    dbRecord: DbHistoryRecord,\n    filePath: Uri\n  ): String? {\n    return withContext(Dispatchers.IO) {\n      val hb = Headers.Builder()\n        .add(TOKEN_KEY, getAuthInfo().accessToken)\n        .build()\n      val request: Request = Request.Builder()\n        .url(\"$BASE_URL/users/${getUserId()}/drive/items/${dbRecord.cloudDiskPath}/content\")\n        .headers(hb)\n        .build()\n      val call = netManager.getClient()\n        .newCall(request)\n\n      try {\n        val response = call.execute()\n        if (!response.isSuccessful) {\n          return@withContext null\n        }\n        val byteSystem = response.body?.byteStream() ?: return@withContext null\n        val outF = File(filePath.path)\n        val fr = FileUtil.createFile(outF)\n        if (!fr) {\n          Timber.e(\"创建文件失败，path = $filePath\")\n          return@withContext null\n        }\n        var len = 0\n        val buf = ByteArray(1024)\n        val fos = FileOutputStream(outF)\n        do {\n          len = byteSystem.read(buf)\n          if (len != -1) {\n            fos.write(buf, 0, len)\n          }\n        } while (len != -1)\n        return@withContext filePath.toString()\n      } catch (e: Exception) {\n        Timber.e(e)\n      }\n      return@withContext null\n    }\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/util/cloud/PwDataMap.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.util.cloud\n\nimport com.keepassdroid.database.PwDataInf\n\ndata class PwDataMap(\n    val cloudPwData: PwDataInf,\n    val localPwData: PwDataInf\n  )"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/util/cloud/SynStateCode.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.util.cloud\n\ninterface SynStateCode {\n  val STATE_SUCCEED: Int\n    get() = 0\n  val STATE_FAIL: Int\n    get() = 1\n  val STATE_DEL_FILE_FAIL: Int\n    get() = 2\n\n  val STATE_DOWNLOAD_FILE_FAIL: Int\n    get() = 3\n  val STATE_SAVE_DB_FAIL: Int\n    get() = 4\n  val STATE_CANCEL: Int\n    get() = 100\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/util/cloud/WebDavUtil.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.util.cloud\n\nimport android.content.Context\nimport android.net.Uri\nimport androidx.core.net.toFile\nimport com.arialyy.frame.util.FileUtil\nimport com.lyy.keepassa.entity.DbHistoryRecord\nimport com.lyy.keepassa.util.hasSpecialChar\nimport com.thegrizzlylabs.sardineandroid.impl.OkHttpSardine\nimport timber.log.Timber\nimport java.io.FileOutputStream\nimport java.net.URI\nimport java.net.URLEncoder\nimport java.nio.channels.Channels\nimport java.nio.channels.FileChannel\nimport java.nio.channels.ReadableByteChannel\nimport java.util.Date\n\n/**\n * webdav工具\n */\nobject WebDavUtil : ICloudUtil {\n  val SUPPORTED_WEBDAV_URLS = mutableListOf<String>().apply {\n    add(\"https://dav.jianguoyun.com/\")\n    add(\"https://dav.box.com\")\n    add(\"https://webdav.4shared.com\")\n    add(\"nextcloud\") // https://docs.nextcloud.com/server/24/user_manual/en/files/access_webdav.html\n    // add(\"https://aki.teracloud.jp/dav\")   // 需要在https://teracloud.jp/en/modules/mypage/usage/ 中 勾选 Turn on Apps Connection\n    // add(\"https://my.powerfolder.com/webdav\") // 只能用他家的client登录？电脑qspase可以\n    // add(\"https://dav.dropdav.com\") // 需要注册：https://app.dropdav.com/users/sign_in\n    // add(\"https://webdav.yandex.com\") 需要使用sdk, htts://yandex.com/dev/id/\n    add(\"other\")\n  }\n\n  val REMOVE_PARENT_URLS = mutableListOf<String>().apply {\n    add(\"https://dav.jianguoyun.com\")\n    add(\"https://dav.box.com\")\n    add(\"https://webdav.4shared.com\")\n  }\n\n  var sardine: OkHttpSardine? = null\n  var fileName: String = \"\"\n  private var hostUri: String = \"\"\n  var userName: String = \"\"\n  var password: String = \"\"\n\n  /**\n   * key: 原始路径，value：转换后的路径\n   */\n  private val urlPathMap = hashMapOf<String, String>()\n\n  /**\n   * 是否登录\n   * @return false 未登录\n   */\n  fun isLogin(): Boolean {\n    return sardine != null\n  }\n\n  fun createDir(path: String): Boolean {\n    return try {\n      sardine?.createDirectory(path)\n      true\n    } catch (e: Exception) {\n      Timber.e(e)\n      false\n    }\n  }\n\n  fun setHostUri(hostUri: String) {\n    this.hostUri = if (hostUri.endsWith(\"/\")) hostUri.substring(0, hostUri.length - 1) else hostUri\n  }\n\n  fun getHostUri() = hostUri\n\n  /**\n   * 检查登录\n   * @return true 登录成功，false 登录失败\n   */\n  fun checkLogin(\n    uri: String,\n    userName: String,\n    password: String,\n    isPreemptive: Boolean\n  ): Boolean {\n    Timber.d(\"checkLogin, uri = ${uri}, userName = ${userName}, password = ${password}\")\n    this.userName = userName\n    this.password = password\n    setHostUri(uri)\n    sardine = OkHttpSardine()\n    sardine?.setCredentials(userName, password, isPreemptive)\n    try {\n      val list = sardine?.list(uri)\n      return !list.isNullOrEmpty()\n    } catch (e: Exception) {\n      Timber.e(e)\n    }\n    return false\n  }\n\n  /**\n   * 进行登录\n   * 创建一个小文件上传成成功后并删除\n   */\n  fun login(\n    uri: String,\n    userName: String,\n    password: String\n  ): OkHttpSardine {\n    this.userName = userName\n    this.password = password\n    setHostUri(uri)\n    sardine = OkHttpSardine()\n    sardine?.setCredentials(userName, password, true)\n    return sardine as OkHttpSardine\n  }\n\n  override suspend fun fileExists(fileKey: String): Boolean {\n    sardine ?: return false\n    Timber.d(\"fileExists, fileKey = $fileKey\")\n    try {\n      return sardine!!.exists(fileKey)\n    } catch (e: Exception) {\n      Timber.e(e)\n    }\n    return false\n  }\n\n  override fun getRootPath(): String {\n    return \"/\"\n  }\n\n  /**\n   *@param dirPath 相对路径，如：/dav/\n   */\n  override suspend fun getFileList(dirPath: String): List<CloudFileInfo>? {\n    sardine ?: return null\n    Timber.d(\"getFileList, host = ${hostUri}, fileKey = $dirPath\")\n    val list = ArrayList<CloudFileInfo>()\n    try {\n      val resources = sardine!!.list(convertUrl(\"${hostUri}${dirPath}\"))\n      if (resources == null || resources.isEmpty()) {\n        return null\n      }\n\n      for (file in resources) {\n        list.add(\n          CloudFileInfo(file.path, file.name, file.modified, file.contentLength, file.isDirectory)\n        )\n      }\n      if (hostUri in REMOVE_PARENT_URLS) {\n        // 坚果云移除第一个item\n        list.removeAt(0)\n      }\n    } catch (e: Exception) {\n      Timber.e(e, \"获取文件列表失败\")\n    }\n    return list\n  }\n\n  /**\n   * 不支持比对\n   */\n  override suspend fun checkContentHash(\n    cloudFileHash: String?,\n    localFileUri: Uri\n  ): Boolean {\n    return false\n  }\n\n  override suspend fun getFileInfo(fileKey: String): CloudFileInfo? {\n    Timber.i(\"获取文件信息，cloudPath：$fileKey\")\n    try {\n      sardine ?: return null\n      val resources = sardine!!.list(convertUrl(fileKey))\n      if (resources == null || resources.isEmpty()) {\n        return null\n      }\n      val file = resources[0]\n      return CloudFileInfo(\n        file.path, file.name, file.modified, file.contentLength, file.isDirectory\n      )\n    } catch (e: Exception) {\n      Timber.e(e)\n    }\n    return null\n  }\n\n  override suspend fun delFile(fileKey: String): Boolean {\n    sardine ?: return false\n    try {\n      sardine!!.delete(convertUrl(fileKey))\n    } catch (e: Exception) {\n      Timber.e(e, \"删除文件失败\")\n      return false\n    }\n    return true\n  }\n\n  override suspend fun getFileServiceModifyTime(fileKey: String): Date {\n    sardine ?: return Date()\n    val cloudInfo = getFileInfo(convertUrl(fileKey))\n    cloudInfo ?: return Date()\n    return cloudInfo.serviceModifyDate\n  }\n\n  override suspend fun uploadFile(\n    context: Context,\n    dbRecord: DbHistoryRecord\n  ): Boolean {\n    Timber.d(\"uploadFile, cloudPath = ${dbRecord.cloudDiskPath}, localPath = ${dbRecord.localDbUri}\")\n    sardine ?: return false\n    var localToken: String? = null\n    try {\n      // delFile(dbRecord.cloudDiskPath!!) // 不能删除，否则如果上传失败，文件就丢失了\n      localToken = sardine!!.lock(getConvertedCloudPath(dbRecord), 5)\n      Timber.d(\"localToken = $localToken\")\n\n      sardine?.put(\n        getConvertedCloudPath(dbRecord),\n        Uri.parse(dbRecord.localDbUri).toFile(),\n        \"application/binary\",\n        false,\n        localToken\n      )\n      Timber.d(\"上传完成，重新获取文件信息\")\n      val info = getFileInfo(getConvertedCloudPath(dbRecord))\n      if (info != null) {\n        DbSynUtil.serviceModifyTime = info.serviceModifyDate\n      }\n    } catch (e: Exception) {\n      Timber.e(e, \"上传文件失败\")\n      return false\n    } finally {\n      localToken?.let {\n        sardine?.unlock(getConvertedCloudPath(dbRecord), it)\n      }\n    }\n\n    return true\n  }\n\n  /**\n   * 检查上传路径是否有特殊字符，如果有特殊字符，转换特殊字符\n   */\n  private fun getConvertedCloudPath(dbRecord: DbHistoryRecord): String {\n    val tempUrl = urlPathMap[dbRecord.cloudDiskPath!!]\n    if (tempUrl != null) {\n      return tempUrl\n    }\n\n    val realUrl = if (dbRecord.cloudDiskPath!!.hasSpecialChar()) {\n      Timber.d(\"url中有特殊，需要对特殊字符进行编码处理\")\n      val path = URI.create(dbRecord.cloudDiskPath).path\n      val encodedPath = path.split(\"/\")\n        .joinToString(\"/\") { URLEncoder.encode(it, \"UTF-8\") }\n      \"${dbRecord.cloudDiskPath!!.substringBefore(path)}${encodedPath}\"\n    } else {\n      dbRecord.cloudDiskPath!!\n    }\n    urlPathMap[dbRecord.cloudDiskPath!!] = realUrl\n    return realUrl\n  }\n\n  override suspend fun downloadFile(\n    context: Context,\n    dbRecord: DbHistoryRecord,\n    filePath: Uri\n  ): String? {\n    sardine ?: return null\n    Timber.d(\"start download file, save path: $filePath\")\n    val cloudPath = convertUrl(dbRecord.cloudDiskPath.toString())\n    val fp = filePath.toFile()\n    if (!fp.exists()) {\n      FileUtil.createFile(fp)\n    }\n    sardine?.let {\n      var token = \"\"\n      var fic: ReadableByteChannel? = null\n      var foc: FileChannel? = null\n      try {\n        token = it.lock(cloudPath)\n        val ips = it.get(cloudPath)\n        val fileInfo = getFileInfo(cloudPath)\n        fic = Channels.newChannel(ips)\n        foc = FileOutputStream(fp).channel\n        foc.transferFrom(fic, 0, fileInfo!!.size)\n      } catch (e: Exception) {\n        Timber.e(e, \"下载文件失败\")\n        return null\n      } finally {\n        try {\n          fic?.close()\n          foc?.close()\n        } catch (e: Exception) {\n          Timber.e(e)\n        }\n        it.unlock(cloudPath, token)\n      }\n    }\n\n    return filePath.toString()\n  }\n\n  /**\n   * 将含有中文的url进行格式化\n   */\n  private fun convertUrl(url: String): String {\n    return url\n  }\n\n  private fun getRelativePath() {\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/util/cloud/interceptor/DbMergeDelegate.kt",
    "content": "package com.lyy.keepassa.util.cloud.interceptor\n\nimport android.util.Pair\nimport android.widget.Button\nimport com.arialyy.frame.router.Routerfit\nimport com.arialyy.frame.util.ResUtil\nimport com.keepassdroid.database.PwDataInf\nimport com.keepassdroid.database.PwDatabase\nimport com.keepassdroid.database.PwEntry\nimport com.keepassdroid.database.PwEntryV4\nimport com.keepassdroid.database.PwGroup\nimport com.keepassdroid.database.PwGroupV4\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseApp\nimport com.lyy.keepassa.entity.DbHistoryRecord\nimport com.lyy.keepassa.router.DialogRouter\nimport com.lyy.keepassa.util.KpaUtil\nimport com.lyy.keepassa.util.cloud.DbSynUtil\nimport com.lyy.keepassa.util.cloud.PwDataMap\nimport com.lyy.keepassa.view.dialog.OnMsgBtClickListener\nimport kotlinx.coroutines.ExperimentalCoroutinesApi\nimport kotlinx.coroutines.MainScope\nimport kotlinx.coroutines.channels.Channel\nimport kotlinx.coroutines.launch\nimport timber.log.Timber\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 5:42 下午 2021/12/24\n **/\nobject DbMergeDelegate {\n  private val scope = MainScope()\n\n  const val COVER_LOCAL = 999\n  const val COVER_CLOUD = 998\n\n  /**\n   * 对比云端和本地的数据库，并进行合并\n   * @param isUpload 是否是上传\n   * @return [COVER_CLOUD]、[COVER_LOCAL]、[VS]\n   */\n  @ExperimentalCoroutinesApi\n  suspend fun compareDb(\n    record: DbHistoryRecord,\n    cloudDb: PwDatabase,\n    localDb: PwDatabase,\n    isUpload: Boolean\n  ): Int {\n    val modifyList = ArrayList<Pair<PwDataInf, PwDataInf>>() // 有改动的条目，first 为云端的条目，second 为本地的条目\n    val delList = ArrayList<PwDataInf>() // 云端没有的条目\n    val newList = ArrayList<PwDataInf>() // 本地没有的条目\n    val moveList = ArrayList<PwDataMap>() // 被移动的条目，first 为云端的条目，second 为本地的条目\n\n    for (cloudEntry in cloudDb.entries.values) {\n      val localEntry = localDb.entries[cloudEntry.uuid]\n      when {\n        localEntry == null -> {\n          newList.add(cloudEntry)\n        }\n        cloudEntry.parent.id != localEntry.parent.id -> {\n          moveList.add(PwDataMap(cloudEntry, localEntry))\n        }\n        localDb.entries[cloudEntry.uuid] == null -> {\n          delList.add(cloudEntry)\n        }\n        cloudEntry != localEntry -> {\n          Timber.d(\"修改的条目：${cloudEntry.title}\")\n          modifyList.add(Pair(cloudEntry, localEntry))\n        }\n      }\n    }\n\n    for (cloudGroup in cloudDb.groups.values) {\n      val localGroup = localDb.groups[cloudGroup.id]\n\n      when {\n        localGroup == null -> {\n          newList.add(cloudGroup)\n        }\n        cloudGroup.parent == null -> {\n          Timber.w(\"云端数据库的群组的parent为空，群组名：${cloudGroup.name}\")\n        }\n        cloudGroup.parent.id != localGroup.parent.id -> {\n          moveList.add(PwDataMap(cloudGroup, localGroup))\n        }\n        localDb.groups[cloudGroup.id] == null -> {\n          delList.add(cloudGroup)\n        }\n        cloudGroup != localGroup -> {\n          Timber.d(\"修改的群组：${cloudGroup.name}\")\n          modifyList.add(Pair(cloudGroup, localGroup))\n        }\n      }\n    }\n\n    Timber.i(\n      \"比对数据完成，newListSize = ${newList.size}，moveListSize = ${moveList.size}，delListSize = ${delList.size}，modifyListSize = ${modifyList.size}\"\n    )\n\n    if (newList.size == 0 && moveList.size == 0 && delList.size == 0 && modifyList.size == 0) {\n      Timber.i(\"对比结果：无新增，无删除，无移动，无修改，忽略该次上传，并更新缓存的云端文件修改时间\")\n      DbSynUtil.updateServiceModifyTime(record)\n      return DbSynUtil.STATE_SUCCEED\n    }\n\n    if (newList.size > 0) {\n      // 本地需要新增的条目\n      Timber.i(\"本地需要新增条目\")\n      localAddNewEntry(newList, localDb)\n    }\n\n    if (moveList.size > 0) {\n      // 本地需要移动的条目\n      Timber.i(\"本地需要移动条目\")\n      moveLocalEntry(moveList, localDb)\n    }\n\n    if (modifyList.size <= 0) {\n      KpaUtil.kdbHandlerService.saveDbByBackground()\n      return DbSynUtil.STATE_SUCCEED\n    }\n\n    // 有改动提示用户合并数据\n    Timber.i(\"有改动提示用户合并数据\")\n    var code = DbSynUtil.STATE_FAIL\n    val channel = Channel<Int>()\n    if (isUpload) {\n      showUploadCoverDialog(modifyList, channel)\n    } else {\n      showDownloadCoverDialog(modifyList, channel)\n    }\n\n    val job = scope.launch {\n      code = channel.receive()\n    }\n    //  等待直到子协程执行结束，完美替换wait single\n    job.join()\n    job.cancel()\n    channel.cancel()\n    Timber.d(\"compareDb end point, code = $code\")\n    return code\n  }\n\n  /**\n   * 显示下载文件时冲突的对话框\n   */\n  @ExperimentalCoroutinesApi\n  private fun showDownloadCoverDialog(\n    modifyList: ArrayList<Pair<PwDataInf, PwDataInf>>,\n    channel: Channel<Int>\n  ) {\n    val sb = StringBuilder()\n    for (p in modifyList) {\n      if (p.second is PwEntry) {\n        sb.append((p.second as PwEntry).title)\n      } else {\n        sb.append((p.second as PwGroup).name)\n      }\n      sb.append(\"\\n\")\n    }\n\n    val res = BaseApp.APP.resources\n    Routerfit.create(DialogRouter::class.java).showMsgDialog(\n      msgTitle = ResUtil.getString(R.string.warning),\n      msgContent = res.getString(R.string.file_conflict_msg, sb.toString()),\n      showCoverBt = false,\n      showCancelBt = false,\n      interceptBackKey = true,\n      enterText = ResUtil.getString(R.string.cover_local),\n      btnClickListener = object : OnMsgBtClickListener {\n        override fun onCover(v: Button) {\n        }\n\n        override fun onEnter(v: Button) {\n          scope.launch {\n            // 覆盖本地数据\n            coverModifyEntry(modifyList)\n            channel.send(COVER_LOCAL)\n          }\n        }\n\n        override fun onCancel(v: Button) {\n        }\n      }\n    )\n  }\n\n  /**\n   * 显示上传文件冲突对话框\n   * @param modifyList 有改动的条目，first 为云端的条目，second 为本地的条目\n   */\n  @ExperimentalCoroutinesApi\n  private fun showUploadCoverDialog(\n    modifyList: ArrayList<Pair<PwDataInf, PwDataInf>>,\n    channel: Channel<Int>\n  ) {\n    val sb = StringBuilder()\n    for (p in modifyList) {\n      if (p.second is PwEntry) {\n        sb.append((p.second as PwEntry).title)\n      } else {\n        sb.append((p.second as PwGroup).name)\n      }\n      sb.append(\"\\n\")\n    }\n    val res = BaseApp.APP.resources\n\n    Routerfit.create(DialogRouter::class.java).showMsgDialog(\n      msgTitle = ResUtil.getString(R.string.warning),\n      msgContent = res.getString(R.string.file_conflict_msg, sb.toString()),\n      showCancelBt = false,\n      showCoverBt = true,\n      interceptBackKey = true,\n      enterText = ResUtil.getString(R.string.cover_local),\n      coverText = ResUtil.getString(R.string.cover_cloud),\n      btnClickListener = object : OnMsgBtClickListener {\n        override fun onCover(v: Button) {\n          // 覆盖云端数据\n          scope.launch {\n            channel.send(COVER_CLOUD)\n          }\n        }\n\n        override fun onEnter(v: Button) {\n          scope.launch {\n            // 覆盖本地数据\n            coverModifyEntry(modifyList)\n            channel.send(COVER_LOCAL)\n          }\n        }\n\n        override fun onCancel(v: Button) {\n        }\n      }\n    )\n\n    Timber.d(\"showUploadCoverDialog endPoint\")\n  }\n\n  /**\n   * 覆盖本地数据库有修改冲突的条目和群组\n   * @param modifyList 有改动的条目，first 为云端的条目，second 为本地的条目\n   */\n  private fun coverModifyEntry(modifyList: ArrayList<Pair<PwDataInf, PwDataInf>>): Int {\n    for (p in modifyList) {\n      if (p.first is PwEntry) {\n        (p.second as PwEntry).assign(p.first as PwEntry)\n      } else {\n        (p.second as PwGroup).assign(p.first as PwGroup)\n      }\n    }\n    KpaUtil.kdbHandlerService.saveDbByBackground()\n    return DbSynUtil.STATE_SUCCEED\n  }\n\n  /**\n   * 移动数据\n   * @param needMoveList 本地需要移动的数据，first 为云端的条目，second 为本地的条目\n   */\n  private fun moveLocalEntry(\n    needMoveList: ArrayList<PwDataMap>,\n    localDb: PwDatabase\n  ) {\n    for (p in needMoveList) {\n      if (p.cloudPwData is PwEntry) {\n        localDb.moveEntry(p.localPwData as PwEntry, getParentByCloudPwData(p.cloudPwData, localDb))\n        continue\n      }\n      // 处理群组的移动\n      localDb.moveGroup(p.localPwData as PwGroup, getParentByCloudPwData(p.cloudPwData, localDb))\n    }\n  }\n\n  /**\n   * 本地新增云端有而本地没的条目\n   * @param newList 云端服务器新增加的条目列表\n   */\n  private suspend fun localAddNewEntry(\n    newList: ArrayList<PwDataInf>,\n    localDb: PwDatabase\n  ) {\n    // 需要先增加群组\n    for (pwData in newList) {\n      if (pwData is PwGroup) {\n        val newGroup = pwData.clone()\n        newGroup.childGroups?.clear()\n        newGroup.childEntries?.clear()\n        newGroup.parent = getParentByCloudPwData(pwData, localDb)\n        KpaUtil.kdbHandlerService.addGroup(newGroup as PwGroupV4)\n      }\n    }\n    // 再增加条目\n    for (pwData in newList) {\n      if (pwData is PwEntry) {\n        val newEntry = pwData.clone(true)\n        newEntry.parent = getParentByCloudPwData(pwData, localDb)\n        KpaUtil.kdbHandlerService.createEntry(newEntry as PwEntryV4)\n      }\n    }\n  }\n\n  /**\n   * 通过云端条目获取本地条目\n   */\n  private fun getParentByCloudPwData(\n    cloudPwDataInf: PwDataInf,\n    localDb: PwDatabase\n  ): PwGroup {\n    var parent = localDb.rootGroup\n\n    if (cloudPwDataInf.parent != null) {\n      val temp = localDb.groups[cloudPwDataInf.parent.id]\n      if (temp != null) {\n        parent = temp\n      }\n    }\n    return parent\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/util/cloud/interceptor/DbSyncCheckInterceptor.kt",
    "content": "package com.lyy.keepassa.util.cloud.interceptor\n\nimport com.lyy.keepassa.base.BaseApp\nimport com.lyy.keepassa.util.cloud.DbSynUtil\nimport timber.log.Timber\n\n/**\n * cloud file check interceptor\n * @Author laoyuyu\n * @Description\n * @Date 4:54 下午 2021/12/24\n **/\nclass DbSyncCheckInterceptor : IDbSyncInterceptor {\n\n  override suspend fun intercept(request: DbSyncRequest): DbSyncResponse {\n    if (BaseApp.isAFS()) {\n      return normal(DbSynUtil.STATE_SUCCEED, \"AFS 不需要上传\")\n    }\n    val nextInterceptor = request.nextInterceptor() ?: throw IllegalArgumentException(\"没有上传拦截器\")\n    val util = request.syncUtil\n    val record = request.record\n\n    val cloudFileInfo = util.getFileInfo(record.cloudDiskPath!!)\n    Timber.i(\"获取文件信息成功：${cloudFileInfo.toString()}\")\n    if (cloudFileInfo == null) {\n      Timber.i(\"云端文件不存在，开始上传文件\")\n      val uploadInterceptor = request.interceptors[request.index + 2]\n      return uploadInterceptor.intercept(\n        DbSyncRequest(\n          record = record,\n          syncUtil = util,\n          interceptors = request.interceptors,\n          index = request.index + 1\n        )\n      )\n    }\n\n    // val st = util.getFileServiceModifyTime(record.cloudDiskPath!!)\n    // if (st == DbSynUtil.serviceModifyTime) {\n    //   return normal(DbSynUtil.STATE_SUCCEED, \"云端文件和本地文件的修改时间一致，忽略该上传\")\n    // }\n\n    if (cloudFileInfo.contentHash != null\n      && util.checkContentHash(cloudFileInfo.contentHash, record.getDbUri())\n    ) {\n      return normal(DbSynUtil.STATE_SUCCEED, \"云端文件和本地文件的hash一致，忽略该上传\")\n    }\n    Timber.i(\"云端文件存在，开始同步数据\")\n\n    return nextInterceptor.intercept(\n      DbSyncRequest(\n        record = record,\n        syncUtil = util,\n        interceptors = request.interceptors,\n        index = request.index + 1\n      )\n    )\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/util/cloud/interceptor/DbSyncCompareInterceptor.kt",
    "content": "package com.lyy.keepassa.util.cloud.interceptor\n\nimport android.content.Context\nimport android.net.Uri\nimport android.text.TextUtils\nimport com.arialyy.frame.util.ResUtil\nimport com.arialyy.frame.util.StringUtil\nimport com.keepassdroid.database.PwDatabase\nimport com.keepassdroid.database.helper.KDBHandlerHelper\nimport com.lyy.keepassa.R.string\nimport com.lyy.keepassa.base.BaseApp\nimport com.lyy.keepassa.util.KeepassAUtil\nimport com.lyy.keepassa.util.QuickUnLockUtil\nimport com.lyy.keepassa.util.cloud.DbSynUtil\nimport com.lyy.keepassa.view.StorageType.DROPBOX\nimport com.lyy.keepassa.view.StorageType.WEBDAV\nimport timber.log.Timber\nimport java.io.File\n\n/**\n * db compare\n * @Author laoyuyu\n * @Description\n * @Date 5:10 下午 2021/12/24\n **/\nclass DbSyncCompareInterceptor : IDbSyncInterceptor {\n  private var nextRequest: DbSyncRequest? = null\n\n  override suspend fun intercept(request: DbSyncRequest): DbSyncResponse {\n    val util = request.syncUtil\n    val record = request.record\n\n    val st = util.getFileServiceModifyTime(record.cloudDiskPath!!)\n    if (st == DbSynUtil.serviceModifyTime) {\n      Timber.i(\n        \"云端文件修改时间:${KeepassAUtil.instance.formatTime(st)} 和本地缓存的云端文件时间:${\n          KeepassAUtil.instance.formatTime(\n            DbSynUtil.serviceModifyTime\n          )\n        } 一致，开始覆盖云端文件\"\n      )\n      return coverFile(request)\n    }\n\n    Timber.i(\n      \"云端文件修改时间:${KeepassAUtil.instance.formatTime(st)} 和本地缓存的云端文件时间:${\n        KeepassAUtil.instance.formatTime(\n          DbSynUtil.serviceModifyTime\n        )\n      } 不一致，开始下载云端文件\"\n    )\n\n    // 下载临时文件\n    val filePath = DbSynUtil.getCloudDbTempPath(\n      record.type, \"kpa_${StringUtil.keyToHashKey(record.cloudDiskPath)}.kdbx\"\n    )\n    val path = util.downloadFile(BaseApp.APP, record, filePath)\n    if (path.isNullOrEmpty()) {\n      DbSynUtil.toask(ResUtil.getString(string.sync_db), false, ResUtil.getString(string.net_error))\n      return error(DbSynUtil.STATE_DOWNLOAD_FILE_FAIL, \"下载文件失败，${record.cloudDiskPath}\")\n    }\n\n    val db = File(path)\n    Timber.i(\"云端文件下载成功，开始打开数据库，filePath = ${db.path}，fileSize = ${db.length()}\")\n    val kdb = openDb(BaseApp.APP, dbPath = path)\n    if (kdb == null) {\n      Timber.e(\"打开云端数据库失败，将覆盖云端数据库\")\n      return coverFile(request)\n    }\n\n    if (BaseApp.KDB?.pm == null) {\n      return error(DbSynUtil.STATE_FAIL, \"synUploadFile, local db is null\")\n    }\n    Timber.i(\"打开云端数据库成功，开始比对数据\")\n    val code = DbMergeDelegate.compareDb(record, kdb, BaseApp.KDB!!.pm, true)\n    if (code == DbSynUtil.STATE_FAIL) {\n      return error(code, \"save db fail\")\n    }\n    return coverFile(request)\n  }\n\n  @Synchronized\n  private fun getNextRequest(request: DbSyncRequest): DbSyncRequest {\n    if (nextRequest == null) {\n      nextRequest = DbSyncRequest(\n        record = request.record,\n        syncUtil = request.syncUtil,\n        interceptors = request.interceptors,\n        index = request.index + 1\n      )\n    }\n    return nextRequest!!\n  }\n\n  /**\n   * 覆盖文件，webdav 不需要删除\n   */\n  private suspend fun coverFile(request: DbSyncRequest): DbSyncResponse {\n    val record = request.record\n    val util = request.syncUtil\n    val needDelFile = when (record.getDbPathType()) {\n      DROPBOX -> true\n      WEBDAV -> false\n      else -> false\n    }\n    // 处理需要删除文件的情况\n    if (needDelFile) {\n      if (util.delFile(record.cloudDiskPath!!)) {\n        Timber.i(\"删除云端文件成功：${record.cloudDiskPath}\")\n        return request.nextInterceptor()!!.intercept(getNextRequest(request))\n      }\n      return error(DbSynUtil.STATE_DEL_FILE_FAIL, \"删除云端文件失败：${record.cloudDiskPath}\")\n    }\n\n    return request.nextInterceptor()!!.intercept(getNextRequest(request))\n  }\n\n  /**\n   * 打开数据库\n   */\n  private fun openDb(\n    context: Context,\n    dbPath: String\n  ): PwDatabase? {\n    val uri = Uri.parse(dbPath)\n    Timber.i(\"dbUri = $uri\")\n    val temp = KDBHandlerHelper.getInstance(context)\n      .openDb(\n        uri, QuickUnLockUtil.decryption(BaseApp.dbPass),\n        if (TextUtils.isEmpty(BaseApp.dbKeyPath)) null else Uri.parse(\n          QuickUnLockUtil.decryption(BaseApp.dbKeyPath)\n        )\n      )\n    if (temp?.pm == null) {\n      return null\n    }\n    return temp.pm\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/util/cloud/interceptor/DbSyncRequest.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.util.cloud.interceptor\n\nimport com.lyy.keepassa.entity.DbHistoryRecord\nimport com.lyy.keepassa.util.cloud.ICloudUtil\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 4:41 下午 2021/12/24\n **/\nclass DbSyncRequest constructor(\n  var record: DbHistoryRecord,\n  var syncUtil: ICloudUtil,\n  val interceptors: List<IDbSyncInterceptor>,\n  val index: Int = 0\n) {\n\n  fun nextInterceptor(): IDbSyncInterceptor? {\n    if (index == interceptors.size) {\n      return null\n    }\n    return interceptors[index + 1]\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/util/cloud/interceptor/DbSyncResponse.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.util.cloud.interceptor\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 4:38 下午 2021/12/24\n **/\nclass DbSyncResponse constructor(var code: Int, var msg: String) {\n\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/util/cloud/interceptor/DbSyncUploadInterceptor.kt",
    "content": "package com.lyy.keepassa.util.cloud.interceptor\n\nimport com.lyy.keepassa.base.BaseApp\nimport com.lyy.keepassa.util.cloud.DbSynUtil\nimport timber.log.Timber\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 5:08 下午 2021/12/24\n **/\nclass DbSyncUploadInterceptor : IDbSyncInterceptor {\n  override suspend fun intercept(request: DbSyncRequest): DbSyncResponse {\n    val util = request.syncUtil\n    val record = request.record\n    val b = util.uploadFile(BaseApp.APP, record)\n    val msg = \"上传文件${if (b) \"成功\" else \"失败\"}, fileKey = ${record.cloudDiskPath}\"\n    Timber.d(msg)\n    return DbSyncResponse(if (b) DbSynUtil.STATE_SUCCEED else DbSynUtil.STATE_FAIL, msg)\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/util/cloud/interceptor/IDbSyncInterceptor.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.util.cloud.interceptor\n\nimport timber.log.Timber\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 4:34 下午 2021/12/24\n **/\ninterface IDbSyncInterceptor {\n\n  suspend fun intercept(request: DbSyncRequest): DbSyncResponse\n\n  fun error(code: Int, msg: String): DbSyncResponse {\n    Timber.e(msg)\n    return DbSyncResponse(code, msg)\n  }\n\n  fun normal(code: Int, msg: String): DbSyncResponse {\n    Timber.i(msg)\n    return DbSyncResponse(code, msg)\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/util/totp/Base32String.java",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.util.totp;\n\nimport java.util.HashMap;\nimport java.util.Locale;\nimport java.util.Map;\n\n/**\n * Encodes arbitrary byte arrays as case-insensitive base-32 strings.\n *\n * <p> The implementation is slightly different than in RFC 4648. During encoding, padding is not\n * added, and during decoding the last incomplete chunk is not taken into account. The result is\n * that multiple strings decode to the same byte array, for example, string of sixteen 7s (\"7...7\")\n * and seventeen 7s both decode to the same byte array.\n *\n * <p>TODO: Revisit this encoding and whether this ambiguity needs fixing.\n */\npublic class Base32String {\n  private static final String SEPARATOR = \"-\";\n  private static final char[] DIGITS = \"ABCDEFGHIJKLMNOPQRSTUVWXYZ234567\".toCharArray();\n  private static final int MASK = DIGITS.length - 1;\n  private static final int SHIFT = Integer.numberOfTrailingZeros(DIGITS.length);\n  private static final Map<Character, Integer> CHAR_MAP = new HashMap<>();\n\n  static {\n    for (int i = 0; i < DIGITS.length; i++) {\n      CHAR_MAP.put(DIGITS[i], i);\n    }\n  }\n\n  public static byte[] decode(String encoded) throws DecodingException {\n    // Remove whitespace and separators\n    encoded = encoded.trim().replaceAll(SEPARATOR, \"\").replaceAll(\" \", \"\");\n\n    // Remove padding. Note: the padding is used as hint to determine how many\n    // bits to decode from the last incomplete chunk (which is commented out\n    // below, so this may have been wrong to start with).\n    encoded = encoded.replaceFirst(\"[=]*$\", \"\");\n\n    // Canonicalize to all upper case\n    encoded = encoded.toUpperCase(Locale.US);\n    if (encoded.length() == 0) {\n      return new byte[0];\n    }\n    int encodedLength = encoded.length();\n    int outLength = encodedLength * SHIFT / 8;\n    byte[] result = new byte[outLength];\n    int buffer = 0;\n    int next = 0;\n    int bitsLeft = 0;\n    for (char c : encoded.toCharArray()) {\n      if (!CHAR_MAP.containsKey(c)) {\n        throw new DecodingException(\"Illegal character: \" + c);\n      }\n      buffer <<= SHIFT;\n      buffer |= CHAR_MAP.get(c) & MASK;\n      bitsLeft += SHIFT;\n      if (bitsLeft >= 8) {\n        result[next++] = (byte) (buffer >> (bitsLeft - 8));\n        bitsLeft -= 8;\n      }\n    }\n    // We'll ignore leftover bits for now.\n    //\n    // if (next != outLength || bitsLeft >= SHIFT) {\n    //  throw new DecodingException(\"Bits left: \" + bitsLeft);\n    // }\n    return result;\n  }\n\n  public static String encode(byte[] data) {\n    int dataLength = data.length;\n    if (dataLength == 0) {\n      return \"\";\n    }\n\n    // SHIFT is the number of bits per output character, so the length of the\n    // output is the length of the input multiplied by 8/SHIFT, rounded up.\n    if (dataLength >= (1 << 28)) {\n      // The computation below will fail, so don't do it.\n      throw new IllegalArgumentException();\n    }\n\n    int outputLength = (dataLength * 8 + SHIFT - 1) / SHIFT;\n    StringBuilder result = new StringBuilder(outputLength);\n\n    int buffer = data[0];\n    int next = 1;\n    int bitsLeft = 8;\n    while (bitsLeft > 0 || next < dataLength) {\n      if (bitsLeft < SHIFT) {\n        if (next < dataLength) {\n          buffer <<= 8;\n          buffer |= (data[next++] & 0xff);\n          bitsLeft += 8;\n        } else {\n          int pad = SHIFT - bitsLeft;\n          buffer <<= pad;\n          bitsLeft += pad;\n        }\n      }\n      int index = MASK & (buffer >> (bitsLeft - SHIFT));\n      bitsLeft -= SHIFT;\n      result.append(DIGITS[index]);\n    }\n    return result.toString();\n  }\n\n  /** Exception thrown when decoding fails */\n  public static class DecodingException extends Exception {\n    public DecodingException(String message) {\n      super(message);\n    }\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/util/totp/ComposeKeeOtp.kt",
    "content": "package com.lyy.keepassa.util.totp\n\nimport android.net.Uri\nimport com.blankj.utilcode.util.ConvertUtils\nimport com.blankj.utilcode.util.EncodeUtils\nimport com.keepassdroid.database.PwEntryV4\nimport com.lyy.keepassa.util.totp.TokenCalculator.HashAlgorithm\nimport com.lyy.keepassa.util.totp.TokenCalculator.HashAlgorithm.SHA256\nimport com.lyy.keepassa.util.totp.TokenCalculator.HashAlgorithm.SHA512\n\nobject ComposeKeeOtp : IOtpCompose {\n  override fun getOtpPass(entry: PwEntryV4): Pair<Int, String?> {\n    // keeotp\n    val keepOtp = entry.strings[\"otp\"]\n    if (keepOtp != null) {\n      val uri = Uri.parse(\"otp://laoyuyu.me/?$keepOtp\")\n      val key = uri.getQueryParameter(\"key\")\n      val type = uri.getQueryParameter(\"type\")\n      val len = uri.getQueryParameter(\"size\") ?: TokenCalculator.TOTP_DEFAULT_DIGITS.toString()\n      val hashMode = uri.getQueryParameter(\"otpHashMode\")\n      val encoding = uri.getQueryParameter(\"encoding\")\n      val counter = uri.getQueryParameter(\"counter\") ?: \"0\"\n      val period = uri.getQueryParameter(\"step\") ?: TokenCalculator.TOTP_DEFAULT_PERIOD.toString()\n\n      val algorithm = when (hashMode) {\n        \"Sha256\" -> SHA256\n        \"Sha512\" -> SHA512\n        else -> HashAlgorithm.SHA1\n      }\n      val seedByte = when (encoding) {\n        \"Base64\" -> {\n          EncodeUtils.base64Decode(key)\n        }\n\n        \"UTF8\" -> {\n          key!!.toByteArray(Charsets.UTF_8)\n        }\n\n        \"Hex\" -> {\n          // hex\n          ConvertUtils.hexString2Bytes(key)\n        }\n\n        else -> {\n          // base32\n          Base32String.decode(key)\n        }\n      }\n\n      val token = when (type) {\n        \"Hotp\" -> {\n          TokenCalculator.HOTP(seedByte, counter.toLong(), len.toInt(), algorithm)\n        }\n\n        \"Steam\" -> {\n          TokenCalculator.TOTP_Steam(seedByte, period.toInt(), len.toInt(), algorithm)\n        }\n\n        else -> {\n          // totp\n          TokenCalculator.TOTP_RFC6238(seedByte, period.toInt(), len.toInt(), algorithm)\n        }\n      }\n      return Pair(if (type == \"Hotp\") counter.toInt() else period.toInt(), token)\n    }\n    return Pair(TokenCalculator.TOTP_DEFAULT_PERIOD, null)\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/util/totp/ComposeKeeOtp2.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.util.totp\n\nimport com.blankj.utilcode.util.ConvertUtils\nimport com.blankj.utilcode.util.EncodeUtils\nimport com.keepassdroid.database.PwEntryV4\nimport com.lyy.keepassa.util.totp.ComposeKeepass.HMAC_SHA_256\nimport com.lyy.keepassa.util.totp.ComposeKeepass.HMAC_SHA_512\nimport com.lyy.keepassa.util.totp.ComposeKeepass.HmacOtp_Algorithm\nimport com.lyy.keepassa.util.totp.ComposeKeepass.HmacOtp_Counter\nimport com.lyy.keepassa.util.totp.ComposeKeepass.HmacOtp_Secret\nimport com.lyy.keepassa.util.totp.ComposeKeepass.HmacOtp_Secret_Base32\nimport com.lyy.keepassa.util.totp.ComposeKeepass.HmacOtp_Secret_Base64\nimport com.lyy.keepassa.util.totp.ComposeKeepass.HmacOtp_Secret_Hex\nimport com.lyy.keepassa.util.totp.ComposeKeepass.TimeOtp_Algorithm\nimport com.lyy.keepassa.util.totp.ComposeKeepass.TimeOtp_Length\nimport com.lyy.keepassa.util.totp.ComposeKeepass.TimeOtp_Period\nimport com.lyy.keepassa.util.totp.ComposeKeepass.TimeOtp_Secret\nimport com.lyy.keepassa.util.totp.ComposeKeepass.TimeOtp_Secret_Base32\nimport com.lyy.keepassa.util.totp.ComposeKeepass.TimeOtp_Secret_Base64\nimport com.lyy.keepassa.util.totp.ComposeKeepass.TimeOtp_Secret_Hex\nimport com.lyy.keepassa.util.totp.TokenCalculator.HashAlgorithm\nimport com.lyy.keepassa.util.totp.TokenCalculator.HashAlgorithm.SHA256\nimport com.lyy.keepassa.util.totp.TokenCalculator.HashAlgorithm.SHA512\n\n/**\n * 兼容KeeOtp2插件的totp获取\n * @Author laoyuyu\n * @Description\n * @Date 4:12 PM 2023/9/20\n **/\n@Deprecated(\"有异常，未实现\")\nobject ComposeKeeOtp2 : IOtpCompose {\n\n  override fun getOtpPass(entry: PwEntryV4): Pair<Int, String?> {\n    return getKeeOtp2Totp(entry)\n  }\n\n  /**\n   * 兼容KeeOtp2插件的totp获取\n   */\n  private fun getKeeOtp2Totp(entry: PwEntryV4): Pair<Int, String?> {\n    // 默认的32位\n    val def32 = entry.strings[TimeOtp_Secret_Base32]\n    if (def32.toString().isNotEmpty()) {\n      return Pair(\n        TokenCalculator.TOTP_DEFAULT_PERIOD,\n        getTotpPass(\n          def32.toString(),\n          TokenCalculator.TOTP_DEFAULT_PERIOD,\n          TokenCalculator.TOTP_DEFAULT_DIGITS,\n          false\n        )\n      )\n    }\n\n    // 自定义的totp\n    val secretTotp = entry.strings.keys.find { it.startsWith(TimeOtp_Secret) }\n    if (secretTotp != null) {\n      val lenStr = entry.strings[TimeOtp_Length]\n      val algorithmStr = entry.strings[TimeOtp_Algorithm]\n      val periodStr = entry.strings[TimeOtp_Period]\n      val len = lenStr?.toString()?.toInt() ?: TokenCalculator.TOTP_DEFAULT_PERIOD\n      var algorithm = HashAlgorithm.SHA1\n      if (algorithmStr != null) {\n        algorithm = when (algorithmStr.toString()) {\n          HMAC_SHA_256 -> SHA256\n          HMAC_SHA_512 -> SHA512\n          else -> HashAlgorithm.SHA1\n        }\n      }\n      val period = periodStr?.toString()?.toInt() ?: TokenCalculator.TOTP_DEFAULT_PERIOD\n      val seedByte = when {\n        entry.strings[TimeOtp_Secret_Base64] != null -> {\n          EncodeUtils.base64Decode(secretTotp)\n        }\n\n        entry.strings[TimeOtp_Secret_Base32] != null -> {\n          Base32String.decode(secretTotp)\n        }\n\n        entry.strings[TimeOtp_Secret_Hex] != null -> {\n          // hex\n          ConvertUtils.hexString2Bytes(secretTotp)\n        }\n\n        else -> {\n          // utf-8\n          secretTotp.toByteArray(Charsets.UTF_8)\n        }\n      }\n\n      return Pair(\n        period,\n        TokenCalculator.TOTP_RFC6238(seedByte, period, len, algorithm)\n      )\n    }\n\n\n\n    return Pair(-1, null)\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/util/totp/ComposeKeeTrayTotp.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.util.totp\n\nimport com.arialyy.frame.router.Routerfit\nimport com.keepassdroid.database.PwEntryV4\nimport com.keepassdroid.database.security.ProtectedString\nimport com.lyy.keepassa.entity.TrayTotpBean\nimport com.lyy.keepassa.router.ServiceRouter\nimport com.lyy.keepassa.util.otpIsKeeTraySteam\n\n/**\n * 兼容KeeTrayTOTP插件的totp获取\n */\nobject ComposeKeeTrayTotp : IOtpCompose {\n\n  const val KEY_SETTING = \"TOTP Settings\"\n  const val KEY_SEED = \"TOTP Seed\"\n\n  private val kdbService by lazy {\n    Routerfit.create(ServiceRouter::class.java).getDbSaveService()\n  }\n\n  override fun getOtpPass(entry: PwEntryV4): Pair<Int, String?> {\n    // 修复1.7之前的bug\n    if (isSteamEntry(entry)) {\n      fix1_7bug(entry)\n    }\n\n    return getKeeTrayTotp(entry)\n  }\n\n  /**\n   * 判断是否是steam的条目，1.7之前的版本创建totp时，会将 TOTP Settings 字段设置为 30;S，而S表示的是Steam\n   */\n  private fun isSteamEntry(entry: PwEntryV4): Boolean {\n    return entry.url\n      .contains(\"steampowered\", ignoreCase = true) ||\n      entry.customData.any {\n        it.value.equals(\n          \"androidapp://com.valvesoftware.android.steam.community\",\n          true\n        )\n      }\n  }\n\n  /**\n   * 1.7之前的版本创建totp时，会将 TOTP Settings 字段设置为 30;S，而S表示的是Steam\n   */\n  private fun fix1_7bug(entry: PwEntryV4) {\n    val totpSetting = entry.strings[\"TOTP Settings\"]\n    if (totpSetting != null) {\n      val tempArray = totpSetting.toString()\n        .split(\";\")\n      if (tempArray.isNotEmpty() && tempArray.size == 2 && !tempArray[1].equals(\"S\", true)) {\n        entry.strings[\"TOTP Settings\"] = ProtectedString(false, \"${tempArray[0]};S\")\n        kdbService.saveDbByBackground()\n      }\n    }\n  }\n\n  /**\n   * 兼容KeeTrayTOTP插件的totp获取\n   */\n  private fun getKeeTrayTotp(entry: PwEntryV4): Pair<Int, String?> {\n    val totpSetting = entry.strings[KEY_SETTING]\n    val isSteam = entry.otpIsKeeTraySteam()\n    var period = TokenCalculator.TOTP_DEFAULT_PERIOD\n    var digits = TokenCalculator.TOTP_DEFAULT_DIGITS\n    val s = totpSetting.toString()\n      .split(\";\")\n    if (s.isNotEmpty() && s.size == 2) {\n      period = s[0].toInt()\n      digits = if (isSteam) TokenCalculator.STEAM_DEFAULT_DIGITS else s[1].toInt()\n    }\n    return Pair(\n      period,\n      getTotpPass(entry.strings[KEY_SEED].toString(), period, digits, isSteam)\n    )\n  }\n\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/util/totp/ComposeKeepass.kt",
    "content": "package com.lyy.keepassa.util.totp\n\nimport com.blankj.utilcode.util.ConvertUtils\nimport com.keepassdroid.database.PwEntryV4\nimport com.lyy.keepassa.entity.HmacOtpBean\nimport com.lyy.keepassa.entity.TimeOtp2Bean\nimport com.lyy.keepassa.util.getKeepassBean\n\nobject ComposeKeepass : IOtpCompose {\n  const val HmacOtp = \"HmacOtp\"\n  const val TimeOtp = \"TimeOtp\"\n\n  const val TimeOtp_Secret = \"TimeOtp-Secret\"\n  const val TimeOtp_Length = \"TimeOtp-Length\"\n  const val TimeOtp_Algorithm = \"TimeOtp-Algorithm\"\n  const val TimeOtp_Period = \"TimeOtp-Period\"\n  const val TimeOtp_Secret_Hex = \"TimeOtp-Secret-Hex\"\n  const val TimeOtp_Secret_Base32 = \"TimeOtp-Secret-Base32\"\n  const val TimeOtp_Secret_Base64 = \"TimeOtp-Secret-Base64\"\n  const val HMAC_SHA_1 = \"HMAC-SHA-1\"\n  const val HMAC_SHA_256 = \"HMAC-SHA-256\"\n  const val HMAC_SHA_512 = \"HMAC-SHA-512\"\n\n  //hOtp\n  const val HmacOtp_Counter = \"HmacOtp-Counter\"\n  const val HmacOtp_Secret = \"HmacOtp-Secret\"\n  const val HmacOtp_Algorithm = \"HmacOtp-Algorithm\"\n  const val HmacOtp_Secret_Hex = \"HmacOtp-Secret-Hex\"\n  const val HmacOtp_Secret_Base32 = \"HmacOtp-Secret-Base32\"\n  const val HmacOtp_Secret_Base64 = \"HmacOtp-Secret-Base64\"\n  const val HmacOtp_Length = \"HmacOtp-Length\"\n\n  override fun getOtpPass(entry: PwEntryV4): Pair<Int, String?> {\n    val bean = entry.getKeepassBean()\n    if (bean.hmac != null) return handleHotp(bean.hmac)\n\n    if (bean.otpBean != null) return handleTotp(bean.otpBean)\n    return Pair(-1, null)\n  }\n\n  private fun handleHotp(hmacBean: HmacOtpBean): Pair<Int, String?> {\n    val secret = when (hmacBean.secretType) {\n      SecretHexType.BASE_32 -> {\n        Base32String.decode(hmacBean.secret)\n      }\n\n      SecretHexType.BASE_64 -> {\n        Base32String.decode(hmacBean.secret)\n      }\n\n      SecretHexType.HEX -> {\n        ConvertUtils.hexString2Bytes(hmacBean.secret)\n      }\n\n      else -> {\n        hmacBean.secret.toByteArray(Charsets.UTF_8)\n      }\n    }\n\n    return Pair(\n      TokenCalculator.TOTP_DEFAULT_PERIOD,\n      TokenCalculator.HOTP(secret, hmacBean.counter.toLong(), hmacBean.len, hmacBean.algorithm)\n    )\n  }\n\n  private fun handleTotp(otpBean: TimeOtp2Bean): Pair<Int, String?> {\n    val secret = when (otpBean.secretType) {\n      SecretHexType.BASE_32 -> {\n        Base32String.decode(otpBean.secret)\n      }\n\n      SecretHexType.BASE_64 -> {\n        Base32String.decode(otpBean.secret)\n      }\n\n      SecretHexType.HEX -> {\n        ConvertUtils.hexString2Bytes(otpBean.secret)\n      }\n\n      else -> {\n        otpBean.secret.toByteArray(Charsets.UTF_8)\n      }\n    }\n\n    val token = TokenCalculator.TOTP_RFC6238(\n      secret,\n      otpBean.period,\n      otpBean.digits,\n      otpBean.algorithm\n    )\n    return Pair(otpBean.period, token)\n  }\n\n  fun getSecretType(secretType: SecretHexType): String {\n    return when (secretType) {\n      SecretHexType.UTF_8 -> TimeOtp_Secret\n      SecretHexType.HEX -> TimeOtp_Secret_Hex\n      SecretHexType.BASE_32 -> TimeOtp_Secret_Base32\n      SecretHexType.BASE_64 -> TimeOtp_Secret_Base64\n    }\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/util/totp/ComposeKeepassxc.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.util.totp\n\nimport android.text.TextUtils\nimport com.keepassdroid.database.PwEntryV4\nimport com.keepassdroid.database.security.ProtectedString\nimport com.lyy.keepassa.R.string\nimport com.lyy.keepassa.base.BaseApp\nimport com.lyy.keepassa.entity.GoogleOtpBean\nimport com.lyy.keepassa.entity.KeepassXcBean\nimport com.lyy.keepassa.util.HitUtil\nimport com.lyy.keepassa.util.getKeepassXcBean\nimport com.lyy.keepassa.util.otpIsKeepassXcSteam\nimport com.lyy.keepassa.util.totp.TokenCalculator.HashAlgorithm\nimport timber.log.Timber\nimport java.util.Locale\n\nobject ComposeKeepassxc : IOtpCompose {\n\n  const val KEY_SEED = \"otp\"\n  const val KEY_ENCODER = \"encoder\"\n  const val KEY_STEAM = \"steam\"\n  const val KEY_SECRET = \"secret\"\n  const val KEY_COUNTER = \"counter\"\n  const val KEY_ISSUER = \"issuer\"\n  const val KEY_PERIOD = \"period\"\n  const val KEY_DIGITS = \"digits\"\n  const val KEY_ALGORITHM = \"algorithm\"\n\n  override fun getOtpPass(entry: PwEntryV4): Pair<Int, String?> {\n    return getPass(entry)\n  }\n\n  private fun getPass(entry: PwEntryV4): Pair<Int, String?> {\n    val bean = entry.getKeepassXcBean()\n\n    if (TextUtils.isEmpty(bean.secret)) {\n      return Pair(0, null)\n    }\n\n    try {\n      val b = Base32String.decode(bean.secret)\n      if (entry.otpIsKeepassXcSteam()) {\n        val pass = TokenCalculator.TOTP_Steam(\n          b, TokenCalculator.TOTP_DEFAULT_PERIOD, TokenCalculator.STEAM_DEFAULT_DIGITS,\n          TokenCalculator.DEFAULT_ALGORITHM\n        )\n        return Pair(bean.period, pass)\n      }\n      val arithmetic = when (bean.algorithm) {\n        HashAlgorithm.SHA256 -> \"SHA256\"\n        HashAlgorithm.SHA512 -> \"SHA512\"\n        else -> \"SHA1\"\n      }\n      val pass = when (bean.host) {\n        \"totp\", \"TOTP\" -> {\n          TokenCalculator.TOTP_RFC6238(\n            b,\n            bean.period,\n            bean.digits,\n            HashAlgorithm.valueOf(arithmetic.toUpperCase(Locale.ROOT))\n          )\n        }\n\n        \"hotp\", \"HOTP\" -> {\n          TokenCalculator.HOTP(\n            b,\n            bean.counter?.toLong() ?: TokenCalculator.HOTP_INITIAL_COUNTER.toLong(),\n            bean.digits,\n            HashAlgorithm.valueOf(arithmetic.toUpperCase(Locale.ROOT))\n          )\n        }\n\n        else -> {\n          Timber.e(\"不识别的类型：${bean.host}\")\n          null\n        }\n      }\n      return Pair(bean.period, pass)\n    } catch (e: Exception) {\n      HitUtil.toaskShort(BaseApp.APP.getString(string.totp_key_error))\n      Timber.e(e)\n    }\n    return Pair(bean.period, null)\n  }\n\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/util/totp/IOtpCompose.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.util.totp\n\nimport com.keepassdroid.database.PwEntryV4\nimport com.lyy.keepassa.R.string\nimport com.lyy.keepassa.base.BaseApp\nimport com.lyy.keepassa.util.HitUtil\nimport timber.log.Timber\n\n/**\n * otp\n * @Author laoyuyu\n * @Description\n * @Date 4:02 PM 2023/9/20\n **/\ninterface IOtpCompose {\n\n  fun getTotpPass(\n    seed: String?,\n    period: Int,\n    digits: Int = TokenCalculator.TOTP_DEFAULT_DIGITS,\n    isSteam: Boolean\n  ): String? {\n    // 适配keepass totp插件的密码\n    try {\n      val b = Base32String.decode(seed)\n      return if (isSteam) {\n        TokenCalculator.TOTP_Steam(\n          b, TokenCalculator.TOTP_DEFAULT_PERIOD, TokenCalculator.STEAM_DEFAULT_DIGITS,\n          TokenCalculator.DEFAULT_ALGORITHM\n        )\n      } else {\n        TokenCalculator.TOTP_RFC6238(b, period, digits, TokenCalculator.DEFAULT_ALGORITHM)\n      }\n    } catch (e: Exception) {\n      HitUtil.toaskShort(BaseApp.APP.getString(string.totp_key_error))\n      Timber.e(e)\n    }\n\n    return null\n  }\n\n  /**\n   * 获取totp密码\n   * @return first period， second 密码\n   */\n  fun getOtpPass(entry: PwEntryV4): Pair<Int, String?>\n\n  // fun toOtpStringMap(bean: OtpBean): Map<String, ProtectedString> {\n  //   return hashMapOf()\n  // }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/util/totp/OtpEnum.kt",
    "content": "package com.lyy.keepassa.util.totp\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 5:24 PM 2024/1/11\n **/\nenum class OtpEnum {\n  GOOGLE_OTP,\n  KEEPASSXC,\n  TRAY_TOTP,\n  KEEPASS\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/util/totp/OtpUtil.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.util.totp\n\nimport android.annotation.SuppressLint\nimport com.keepassdroid.database.PwEntryV4\nimport com.lyy.keepassa.util.otpIsKeeOtp2\nimport com.lyy.keepassa.util.otpIsKeeTrayTotp\nimport com.lyy.keepassa.util.otpIsKeepOtp\nimport com.lyy.keepassa.util.otpKeepass\nimport com.lyy.keepassa.util.otpKeepassXC\n\nobject OtpUtil {\n\n  /**\n   * 获取totp密码\n   * @return first period， second 密码\n   */\n  @SuppressLint(\"DefaultLocale\") fun getOtpPass(entry: PwEntryV4): Pair<Int, String?> {\n    val otpCompose = when {\n\n      entry.otpIsKeeTrayTotp() -> {\n        ComposeKeeTrayTotp\n      }\n\n      entry.otpIsKeepOtp() -> {\n        ComposeKeeOtp\n      }\n\n      entry.otpKeepassXC() -> {\n        ComposeKeepassxc\n      }\n\n      entry.otpKeepass() -> {\n        ComposeKeepass\n      }\n\n      entry.otpIsKeeOtp2() -> {\n        ComposeKeeOtp2\n      }\n\n      else -> null\n    }\n    return otpCompose?.getOtpPass(entry) ?: Pair(-1, null)\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/util/totp/SecretHexType.kt",
    "content": "package com.lyy.keepassa.util.totp\n\nenum class SecretHexType {\n  UTF_8, HEX, BASE_32, BASE_64\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/util/totp/TokenCalculator.java",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.util.totp;\n\nimport java.nio.ByteBuffer;\nimport java.security.InvalidKeyException;\nimport java.security.NoSuchAlgorithmException;\nimport java.text.NumberFormat;\nimport java.util.Locale;\nimport javax.crypto.Mac;\nimport javax.crypto.spec.SecretKeySpec;\nimport timber.log.Timber;\n\n/**\n * 地址：https://github.com/andOTP/andOTP.git\n */\npublic class TokenCalculator {\n  public static final int TOTP_DEFAULT_PERIOD = 30;\n  public static final int TOTP_DEFAULT_DIGITS = 6;\n  public static final int HOTP_INITIAL_COUNTER = 1;\n  public static final int STEAM_DEFAULT_DIGITS = 5;\n\n  private static final char[] STEAMCHARS = new char[] {\n      '2', '3', '4', '5', '6', '7', '8', '9', 'B', 'C',\n      'D', 'F', 'G', 'H', 'J', 'K', 'M', 'N', 'P', 'Q',\n      'R', 'T', 'V', 'W', 'X', 'Y'\n  };\n\n  public enum HashAlgorithm {\n    SHA1, SHA256, SHA512\n  }\n\n  public static final HashAlgorithm DEFAULT_ALGORITHM = HashAlgorithm.SHA1;\n\n  private static byte[] generateHash(HashAlgorithm algorithm, byte[] key, byte[] data)\n      throws NoSuchAlgorithmException, InvalidKeyException {\n    String algo = \"Hmac\" + algorithm.toString();\n\n    Mac mac = Mac.getInstance(algo);\n    mac.init(new SecretKeySpec(key, algo));\n\n    return mac.doFinal(data);\n  }\n\n  public static int TOTP_RFC6238(byte[] secret, int period, long time, int digits,\n      HashAlgorithm algorithm) {\n    int fullToken = TOTP(secret, period, time, algorithm);\n    int div = (int) Math.pow(10, digits);\n\n    return fullToken % div;\n  }\n\n  /**\n   * TOTP_RFC6238 协议的TOTP\n   *\n   * @param secret seed\n   * @param period {@link #TOTP_DEFAULT_PERIOD}、\n   * @param digits {@link #TOTP_DEFAULT_DIGITS}、{@link #STEAM_DEFAULT_DIGITS}\n   * @param algorithm {@link HashAlgorithm}\n   */\n  public static String TOTP_RFC6238(byte[] secret, int period, int digits,\n      HashAlgorithm algorithm) {\n    return formatTokenString(\n        TOTP_RFC6238(secret, period, System.currentTimeMillis() / 1000, digits, algorithm), digits);\n  }\n\n  /**\n   * TOTP_RFC6238 协议的TOTP\n   *\n   * @param secret seed\n   * @param period {@link #TOTP_DEFAULT_PERIOD}、\n   * @param digits {@link #TOTP_DEFAULT_DIGITS}、{@link #STEAM_DEFAULT_DIGITS}\n   * @param algorithm {@link HashAlgorithm}\n   */\n  public static String TOTP_Steam(byte[] secret, int period, int digits, HashAlgorithm algorithm) {\n    int fullToken = TOTP(secret, period, System.currentTimeMillis() / 1000, algorithm);\n\n    StringBuilder tokenBuilder = new StringBuilder();\n\n    for (int i = 0; i < digits; i++) {\n      tokenBuilder.append(STEAMCHARS[fullToken % STEAMCHARS.length]);\n      fullToken /= STEAMCHARS.length;\n    }\n\n    return tokenBuilder.toString();\n  }\n\n  /**\n   * TOTP_RFC6238 协议的HOTP\n   *\n   * @param secret seed\n   * @param counter {@link #HOTP_INITIAL_COUNTER}\n   * @param digits {@link #TOTP_DEFAULT_DIGITS}、{@link #STEAM_DEFAULT_DIGITS}\n   * @param algorithm {@link HashAlgorithm}\n   */\n  public static String HOTP(byte[] secret, long counter, int digits, HashAlgorithm algorithm) {\n    int fullToken = HOTP(secret, counter, algorithm);\n    int div = (int) Math.pow(10, digits);\n\n    return formatTokenString(fullToken % div, digits);\n  }\n\n  private static int TOTP(byte[] key, int period, long time, HashAlgorithm algorithm) {\n    return HOTP(key, time / period, algorithm);\n  }\n\n  private static int HOTP(byte[] key, long counter, HashAlgorithm algorithm) {\n    int r = 0;\n\n    try {\n      byte[] data = ByteBuffer.allocate(8).putLong(counter).array();\n      byte[] hash = generateHash(algorithm, key, data);\n\n      int offset = hash[hash.length - 1] & 0xF;\n\n      int binary = (hash[offset] & 0x7F) << 0x18;\n      binary |= (hash[offset + 1] & 0xFF) << 0x10;\n      binary |= (hash[offset + 2] & 0xFF) << 0x08;\n      binary |= (hash[offset + 3] & 0xFF);\n\n      r = binary;\n    } catch (Exception e) {\n      Timber.e(e);\n    }\n\n    return r;\n  }\n\n  private static String formatTokenString(int token, int digits) {\n    NumberFormat numberFormat = NumberFormat.getInstance(Locale.ENGLISH);\n    numberFormat.setMinimumIntegerDigits(digits);\n    numberFormat.setGroupingUsed(false);\n\n    return numberFormat.format(token);\n  }\n\n  public static String formatToken(String s, int chunkSize) {\n    if (chunkSize == 0 || s == null) {\n      return s;\n    }\n\n    StringBuilder ret = new StringBuilder(\"\");\n    int index = s.length();\n    while (index > 0) {\n      ret.insert(0, s.substring(Math.max(index - chunkSize, 0), index));\n      ret.insert(0, \" \");\n      index = index - chunkSize;\n    }\n    return ret.toString().trim();\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/ChoseDirModule.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.view\n\nimport androidx.fragment.app.FragmentActivity\nimport com.keepassdroid.database.PwDatabaseV4\nimport com.keepassdroid.database.PwEntryV4\nimport com.keepassdroid.database.PwGroup\nimport com.keepassdroid.database.PwGroupId\nimport com.keepassdroid.database.PwGroupV4\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseApp\nimport com.lyy.keepassa.base.BaseModule\nimport com.lyy.keepassa.event.MoveEvent\nimport com.lyy.keepassa.util.HitUtil\nimport com.lyy.keepassa.util.KpaUtil\nimport org.greenrobot.eventbus.EventBus\nimport java.util.UUID\n\nclass ChoseDirModule : BaseModule() {\n\n  /**\n   * 恢复群组\n   * @param groupId 需要恢复的群组\n   * @param curGroup 当前群组\n   */\n  fun moveGroup(\n    ac: FragmentActivity,\n    groupId: PwGroupId,\n    curGroup: PwGroup\n  ) {\n    val group = BaseApp.KDB.pm.groups[groupId] as PwGroupV4\n    if (group.parent == BaseApp.KDB.pm.recycleBin) {\n      (BaseApp.KDB.pm as PwDatabaseV4).undoRecycle(group, curGroup)\n    } else {\n      (BaseApp.KDB.pm as PwDatabaseV4).moveGroup(group, curGroup)\n    }\n    KpaUtil.kdbHandlerService.saveDbByBackground()\n    EventBus.getDefault().post(MoveEvent(MoveEvent.MOVE_TYPE_GROUP, null, group))\n    HitUtil.toaskShort(ac.getString(R.string.undo_grouped))\n    ac.finishAfterTransition()\n  }\n\n  /**\n   * 恢复条目\n   */\n  fun moveEntry(\n    ac: FragmentActivity,\n    entryId: UUID,\n    curGroup: PwGroupV4\n  ) {\n    val entry = BaseApp.KDB.pm.entries[entryId] ?: return\n    val entryV4 = entry as PwEntryV4\n    KpaUtil.kdbHandlerService.moveEntry(entryV4, curGroup)\n    HitUtil.toaskShort(ac.getString(R.string.undo_entryed))\n    ac.finishAfterTransition()\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/KpaCaptureManager.java",
    "content": "package com.lyy.keepassa.view;\n\nimport android.Manifest;\nimport android.annotation.TargetApi;\nimport android.app.Activity;\nimport android.app.AlertDialog;\nimport android.content.Intent;\nimport android.content.pm.ActivityInfo;\nimport android.content.pm.PackageManager;\nimport android.content.res.Configuration;\nimport android.graphics.Bitmap;\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.os.Handler;\nimport android.view.Display;\nimport android.view.KeyEvent;\nimport android.view.Surface;\nimport android.view.Window;\nimport android.view.WindowManager;\nimport androidx.core.app.ActivityCompat;\nimport androidx.core.content.ContextCompat;\nimport com.google.zxing.BarcodeFormat;\nimport com.google.zxing.DecodeHintType;\nimport com.google.zxing.MultiFormatReader;\nimport com.google.zxing.ResultMetadataType;\nimport com.google.zxing.ResultPoint;\nimport com.google.zxing.client.android.BeepManager;\nimport com.google.zxing.client.android.DecodeFormatManager;\nimport com.google.zxing.client.android.DecodeHintManager;\nimport com.google.zxing.client.android.InactivityTimer;\nimport com.google.zxing.client.android.Intents;\nimport com.google.zxing.client.android.R;\nimport com.journeyapps.barcodescanner.BarcodeCallback;\nimport com.journeyapps.barcodescanner.BarcodeResult;\nimport com.journeyapps.barcodescanner.BarcodeView;\nimport com.journeyapps.barcodescanner.CameraPreview;\nimport com.journeyapps.barcodescanner.DecoratedBarcodeView;\nimport com.journeyapps.barcodescanner.DefaultDecoderFactory;\nimport com.journeyapps.barcodescanner.camera.CameraSettings;\nimport java.io.File;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport timber.log.Timber;\n\n/**\n * Manages barcode scanning for a CaptureActivity. This class may be used to have a custom Activity\n * (e.g. with a customized look and feel, or a different superclass), but not the barcode scanning\n * process itself.\n *\n * This is intended for an Activity that is dedicated to capturing a single barcode and returning\n * it via setResult(). For other use cases, use DefaultBarcodeScannerView or BarcodeView directly.\n *\n * The following is managed by this class:\n * - Orientation lock\n * - InactivityTimer\n * - BeepManager\n * - Initializing from an Intent (via IntentIntegrator)\n * - Setting the result and finishing the Activity when a barcode is scanned\n * - Displaying camera errors\n */\npublic class KpaCaptureManager {\n  private static int cameraPermissionReqCode = 250;\n\n  private final Activity activity;\n  private int orientationLock = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;\n  private static final String SAVED_ORIENTATION_LOCK = \"SAVED_ORIENTATION_LOCK\";\n  private boolean returnBarcodeImagePath = false;\n\n  private boolean showDialogIfMissingCameraPermission = true;\n  private String missingCameraPermissionDialogMessage = \"\";\n\n  private boolean destroyed = false;\n\n  private final InactivityTimer inactivityTimer;\n  private final BeepManager beepManager;\n\n  private final Handler handler;\n\n  private final BarcodeView barcodeView;\n\n  /**\n   * The instance of @link TorchListener to send events callback.\n   */\n  private DecoratedBarcodeView.TorchListener torchListener;\n\n  private boolean finishWhenClosed = false;\n\n  private final BarcodeCallback callback = new BarcodeCallback() {\n    @Override\n    public void barcodeResult(final BarcodeResult result) {\n      barcodeView.pause();\n      beepManager.playBeepSoundAndVibrate();\n      handler.post(() -> returnResult(result));\n    }\n\n    @Override\n    public void possibleResultPoints(List<ResultPoint> resultPoints) {\n\n    }\n  };\n\n  public KpaCaptureManager(Activity activity, BarcodeView barcodeView) {\n    this.activity = activity;\n    this.barcodeView = barcodeView;\n    CameraPreview.StateListener stateListener = new CameraPreview.StateListener() {\n      @Override\n      public void previewSized() {\n\n      }\n\n      @Override\n      public void previewStarted() {\n\n      }\n\n      @Override\n      public void previewStopped() {\n\n      }\n\n      @Override\n      public void cameraError(Exception error) {\n        displayFrameworkBugMessageAndExit(\n            activity.getString(R.string.zxing_msg_camera_framework_bug)\n        );\n      }\n\n      @Override\n      public void cameraClosed() {\n        if (finishWhenClosed) {\n          Timber.d(\"Camera closed; finishing activity\");\n          finish();\n        }\n      }\n    };\n    barcodeView.addStateListener(stateListener);\n\n    handler = new Handler();\n\n    inactivityTimer = new InactivityTimer(activity, () -> {\n      Timber.d(\"Finishing due to inactivity\");\n      finish();\n    });\n\n    beepManager = new BeepManager(activity);\n  }\n\n  /**\n   * Perform initialization, according to preferences set in the intent.\n   *\n   * @param intent the intent containing the scanning preferences\n   * @param savedInstanceState saved state, containing orientation lock\n   */\n  public void initializeFromIntent(Intent intent, Bundle savedInstanceState) {\n    Window window = activity.getWindow();\n    window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);\n\n    if (savedInstanceState != null) {\n      // If the screen was locked and unlocked again, we may start in a different orientation\n      // (even one not allowed by the manifest). In this case we restore the orientation we were\n      // previously locked to.\n      this.orientationLock = savedInstanceState.getInt(SAVED_ORIENTATION_LOCK,\n          ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);\n    }\n\n    if (intent != null) {\n      // Only lock the orientation if it's not locked to something else yet\n      boolean orientationLocked = intent.getBooleanExtra(Intents.Scan.ORIENTATION_LOCKED, true);\n      if (orientationLocked) {\n        lockOrientation();\n      }\n\n      if (Intents.Scan.ACTION.equals(intent.getAction())) {\n        initializeFromIntent(intent);\n      }\n\n      if (!intent.getBooleanExtra(Intents.Scan.BEEP_ENABLED, true)) {\n        beepManager.setBeepEnabled(false);\n      }\n\n      if (intent.hasExtra(Intents.Scan.SHOW_MISSING_CAMERA_PERMISSION_DIALOG)) {\n        setShowMissingCameraPermissionDialog(\n            intent.getBooleanExtra(Intents.Scan.SHOW_MISSING_CAMERA_PERMISSION_DIALOG, true),\n            intent.getStringExtra(Intents.Scan.MISSING_CAMERA_PERMISSION_DIALOG_MESSAGE)\n        );\n      }\n\n      if (intent.hasExtra(Intents.Scan.TIMEOUT)) {\n        handler.postDelayed(this::returnResultTimeout,\n            intent.getLongExtra(Intents.Scan.TIMEOUT, 0L));\n      }\n\n      if (intent.getBooleanExtra(Intents.Scan.BARCODE_IMAGE_ENABLED, false)) {\n        returnBarcodeImagePath = true;\n      }\n    }\n  }\n\n  public void initializeFromIntent(Intent intent) {\n    // Scan the formats the intent requested, and return the result to the calling activity.\n    Set<BarcodeFormat> decodeFormats = DecodeFormatManager.parseDecodeFormats(intent);\n    Map<DecodeHintType, Object> decodeHints = DecodeHintManager.parseDecodeHints(intent);\n\n    CameraSettings settings = new CameraSettings();\n\n    if (intent.hasExtra(Intents.Scan.CAMERA_ID)) {\n      int cameraId = intent.getIntExtra(Intents.Scan.CAMERA_ID, -1);\n      if (cameraId >= 0) {\n        settings.setRequestedCameraId(cameraId);\n      }\n    }\n\n    if (intent.hasExtra(Intents.Scan.TORCH_ENABLED)) {\n      if (intent.getBooleanExtra(Intents.Scan.TORCH_ENABLED, false)) {\n        this.setTorchOn();\n      }\n    }\n\n    // Check what type of scan. Default: normal scan\n    int scanType = intent.getIntExtra(Intents.Scan.SCAN_TYPE, 0);\n\n    String characterSet = intent.getStringExtra(Intents.Scan.CHARACTER_SET);\n\n    MultiFormatReader reader = new MultiFormatReader();\n    reader.setHints(decodeHints);\n\n    barcodeView.setCameraSettings(settings);\n    barcodeView.setDecoderFactory(\n        new DefaultDecoderFactory(decodeFormats, decodeHints, characterSet, scanType));\n  }\n\n  public void setTorchOn() {\n    barcodeView.setTorch(true);\n\n    if (torchListener != null) {\n      torchListener.onTorchOn();\n    }\n  }\n\n  /**\n   * Lock display to current orientation.\n   */\n  protected void lockOrientation() {\n    // Only get the orientation if it's not locked to one yet.\n    if (this.orientationLock == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) {\n      // Adapted from http://stackoverflow.com/a/14565436\n      Display display = activity.getWindowManager().getDefaultDisplay();\n      int rotation = display.getRotation();\n      int baseOrientation = activity.getResources().getConfiguration().orientation;\n      int orientation = 0;\n      if (baseOrientation == Configuration.ORIENTATION_LANDSCAPE) {\n        if (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_90) {\n          orientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;\n        } else {\n          orientation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;\n        }\n      } else if (baseOrientation == Configuration.ORIENTATION_PORTRAIT) {\n        if (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_270) {\n          orientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;\n        } else {\n          orientation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;\n        }\n      }\n\n      this.orientationLock = orientation;\n    }\n    //noinspection ResourceType\n    activity.setRequestedOrientation(this.orientationLock);\n  }\n\n  /**\n   * Start decoding.\n   */\n  public void decode() {\n    barcodeView.decodeSingle(callback);\n  }\n\n  /**\n   * Call from Activity#onResume().\n   */\n  public void onResume() {\n    if (Build.VERSION.SDK_INT >= 23) {\n      openCameraWithPermission();\n    } else {\n      barcodeView.resume();\n    }\n    inactivityTimer.start();\n  }\n\n  private boolean askedPermission = false;\n\n  @TargetApi(23)\n  private void openCameraWithPermission() {\n    if (ContextCompat.checkSelfPermission(this.activity, Manifest.permission.CAMERA)\n        == PackageManager.PERMISSION_GRANTED) {\n      barcodeView.resume();\n    } else if (!askedPermission) {\n      ActivityCompat.requestPermissions(this.activity,\n          new String[] { Manifest.permission.CAMERA },\n          cameraPermissionReqCode);\n      askedPermission = true;\n    } // else wait for permission result\n  }\n\n  /**\n   * Call from Activity#onRequestPermissionsResult\n   *\n   * @param requestCode The request code passed in {@link androidx.core.app.ActivityCompat#requestPermissions(Activity,\n   * String[], int)}.\n   * @param permissions The requested permissions.\n   * @param grantResults The grant results for the corresponding permissions\n   * which is either {@link android.content.pm.PackageManager#PERMISSION_GRANTED}\n   * or {@link android.content.pm.PackageManager#PERMISSION_DENIED}. Never null.\n   */\n  public void onRequestPermissionsResult(int requestCode, String permissions[],\n      int[] grantResults) {\n    if (requestCode == cameraPermissionReqCode) {\n      if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {\n        // permission was granted\n        barcodeView.resume();\n      } else {\n        setMissingCameraPermissionResult();\n\n        if (showDialogIfMissingCameraPermission) {\n          displayFrameworkBugMessageAndExit(missingCameraPermissionDialogMessage);\n        } else {\n          closeAndFinish();\n        }\n      }\n    }\n  }\n\n  /**\n   * Call from Activity#onPause().\n   */\n  public void onPause() {\n\n    inactivityTimer.cancel();\n    barcodeView.pauseAndWait();\n  }\n\n  /**\n   * Call from Activity#onDestroy().\n   */\n  public void onDestroy() {\n    destroyed = true;\n    inactivityTimer.cancel();\n    handler.removeCallbacksAndMessages(null);\n  }\n\n  /**\n   * Call from Activity#onSaveInstanceState().\n   */\n  public void onSaveInstanceState(Bundle outState) {\n    outState.putInt(SAVED_ORIENTATION_LOCK, this.orientationLock);\n  }\n\n  /**\n   * Create a intent to return as the Activity result.\n   *\n   * @param rawResult the BarcodeResult, must not be null.\n   * @param barcodeImagePath a path to an exported file of the Barcode Image, can be null.\n   * @return the Intent\n   */\n  public static Intent resultIntent(BarcodeResult rawResult, String barcodeImagePath) {\n    Intent intent = new Intent(Intents.Scan.ACTION);\n    intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);\n    intent.putExtra(Intents.Scan.RESULT, rawResult.toString());\n    intent.putExtra(Intents.Scan.RESULT_FORMAT, rawResult.getBarcodeFormat().toString());\n    byte[] rawBytes = rawResult.getRawBytes();\n    if (rawBytes != null && rawBytes.length > 0) {\n      intent.putExtra(Intents.Scan.RESULT_BYTES, rawBytes);\n    }\n    Map<ResultMetadataType, ?> metadata = rawResult.getResultMetadata();\n    if (metadata != null) {\n      if (metadata.containsKey(ResultMetadataType.UPC_EAN_EXTENSION)) {\n        intent.putExtra(Intents.Scan.RESULT_UPC_EAN_EXTENSION,\n            metadata.get(ResultMetadataType.UPC_EAN_EXTENSION).toString());\n      }\n      Number orientation = (Number) metadata.get(ResultMetadataType.ORIENTATION);\n      if (orientation != null) {\n        intent.putExtra(Intents.Scan.RESULT_ORIENTATION, orientation.intValue());\n      }\n      String ecLevel = (String) metadata.get(ResultMetadataType.ERROR_CORRECTION_LEVEL);\n      if (ecLevel != null) {\n        intent.putExtra(Intents.Scan.RESULT_ERROR_CORRECTION_LEVEL, ecLevel);\n      }\n      @SuppressWarnings(\"unchecked\")\n      Iterable<byte[]> byteSegments =\n          (Iterable<byte[]>) metadata.get(ResultMetadataType.BYTE_SEGMENTS);\n      if (byteSegments != null) {\n        int i = 0;\n        for (byte[] byteSegment : byteSegments) {\n          intent.putExtra(Intents.Scan.RESULT_BYTE_SEGMENTS_PREFIX + i, byteSegment);\n          i++;\n        }\n      }\n    }\n    if (barcodeImagePath != null) {\n      intent.putExtra(Intents.Scan.RESULT_BARCODE_IMAGE_PATH, barcodeImagePath);\n    }\n    return intent;\n  }\n\n  /**\n   * Save the barcode image to a temporary file stored in the application's cache, and return its\n   * path.\n   * Only does so if returnBarcodeImagePath is enabled.\n   *\n   * @param rawResult the BarcodeResult, must not be null\n   * @return the path or null\n   */\n  private String getBarcodeImagePath(BarcodeResult rawResult) {\n    String barcodeImagePath = null;\n    if (returnBarcodeImagePath) {\n      Bitmap bmp = rawResult.getBitmap();\n      try {\n        File bitmapFile = File.createTempFile(\"barcodeimage\", \".jpg\", activity.getCacheDir());\n        FileOutputStream outputStream = new FileOutputStream(bitmapFile);\n        bmp.compress(Bitmap.CompressFormat.JPEG, 100, outputStream);\n        outputStream.close();\n        barcodeImagePath = bitmapFile.getAbsolutePath();\n      } catch (IOException e) {\n        Timber.w(\"Unable to create temporary file and store bitmap! %s\", e);\n      }\n    }\n    return barcodeImagePath;\n  }\n\n  private void finish() {\n    activity.finish();\n  }\n\n  protected void closeAndFinish() {\n    if (barcodeView.isCameraClosed()) {\n      finish();\n    } else {\n      finishWhenClosed = true;\n    }\n\n    barcodeView.pause();\n    inactivityTimer.cancel();\n  }\n\n  private void setMissingCameraPermissionResult() {\n    Intent intent = new Intent(Intents.Scan.ACTION);\n    intent.putExtra(Intents.Scan.MISSING_CAMERA_PERMISSION, true);\n    activity.setResult(Activity.RESULT_CANCELED, intent);\n  }\n\n  protected void returnResultTimeout() {\n    Intent intent = new Intent(Intents.Scan.ACTION);\n    intent.putExtra(Intents.Scan.TIMEOUT, true);\n    activity.setResult(Activity.RESULT_CANCELED, intent);\n    closeAndFinish();\n  }\n\n  protected void returnResult(BarcodeResult rawResult) {\n    Intent intent = resultIntent(rawResult, getBarcodeImagePath(rawResult));\n    activity.setResult(Activity.RESULT_OK, intent);\n    closeAndFinish();\n  }\n\n  protected void displayFrameworkBugMessageAndExit(String message) {\n    if (activity.isFinishing() || this.destroyed || finishWhenClosed) {\n      return;\n    }\n\n    if (message.isEmpty()) {\n      message = activity.getString(R.string.zxing_msg_camera_framework_bug);\n    }\n\n    AlertDialog.Builder builder = new AlertDialog.Builder(activity);\n    builder.setTitle(activity.getString(R.string.zxing_app_name));\n    builder.setMessage(message);\n    builder.setPositiveButton(R.string.zxing_button_ok, (dialog, which) -> finish());\n    builder.setOnCancelListener(dialog -> finish());\n    builder.show();\n  }\n\n  public static int getCameraPermissionReqCode() {\n    return cameraPermissionReqCode;\n  }\n\n  public static void setCameraPermissionReqCode(int cameraPermissionReqCode) {\n    KpaCaptureManager.cameraPermissionReqCode = cameraPermissionReqCode;\n  }\n\n  /**\n   * If set to true, shows the default error dialog if camera permission is missing.\n   * <p>\n   * If set to false, instead the capture manager just finishes.\n   * <p>\n   * In both cases, the activity result is set to {@link Intents.Scan#MISSING_CAMERA_PERMISSION}\n   * and cancelled\n   */\n  public void setShowMissingCameraPermissionDialog(boolean visible) {\n    setShowMissingCameraPermissionDialog(visible, \"\");\n  }\n\n  /**\n   * If set to true, shows the specified error dialog message if camera permission is missing.\n   * <p>\n   * If set to false, instead the capture manager just finishes.\n   * <p>\n   * In both cases, the activity result is set to {@link Intents.Scan#MISSING_CAMERA_PERMISSION}\n   * and cancelled\n   */\n  public void setShowMissingCameraPermissionDialog(boolean visible, String message) {\n    showDialogIfMissingCameraPermission = visible;\n    missingCameraPermissionDialogMessage = message != null ? message : \"\";\n  }\n\n  public void setTorchListener(DecoratedBarcodeView.TorchListener listener) {\n    this.torchListener = listener;\n  }\n\n  /**\n   * Turn off the device's flashlight.\n   */\n  public void setTorchOff() {\n    barcodeView.setTorch(false);\n\n    if (torchListener != null) {\n      torchListener.onTorchOff();\n    }\n  }\n\n  public boolean onKeyDown(int keyCode, KeyEvent event) {\n    switch (keyCode) {\n      case KeyEvent.KEYCODE_FOCUS:\n      case KeyEvent.KEYCODE_CAMERA:\n        // Handle these events so they don't launch the Camera app\n        return true;\n      // Use volume up/down to turn on light\n      case KeyEvent.KEYCODE_VOLUME_DOWN:\n        setTorchOff();\n        return true;\n      case KeyEvent.KEYCODE_VOLUME_UP:\n        setTorchOn();\n        return true;\n    }\n    return false;\n  }\n}\n"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/MarkDownEditorActivity.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.view\n\nimport android.app.Activity\nimport android.app.ActivityOptions\nimport android.content.Context\nimport android.content.Intent\nimport android.os.Bundle\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseActivity\nimport com.lyy.keepassa.databinding.ActivityMarkdownEditorBinding\nimport com.lyy.keepassa.event.EditorEvent\nimport com.lyy.keepassa.widget.editor.MarkDownEditor\nimport org.greenrobot.eventbus.EventBus\nimport timber.log.Timber\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 2020/12/2\n **/\nclass MarkDownEditorActivity : BaseActivity<ActivityMarkdownEditorBinding>() {\n\n  private var reqCode: Int = 0\n  private var content: CharSequence? = null\n\n  companion object {\n    private val KEY_REQUESTOIN_CODE = \"KEY_REQUESTOIN_CODE\"\n    private var KEY_CONTENT = \"KEY_CONTENT\"\n\n    fun turnMarkDownEditor(\n      context: Context,\n      requestCode: Int,\n      content: CharSequence?\n    ) {\n      val intent = Intent(context, MarkDownEditorActivity::class.java)\n      intent.putExtra(KEY_REQUESTOIN_CODE, requestCode)\n      intent.putExtra(KEY_CONTENT, content)\n      if (context is Activity) {\n        context.startActivity(\n            intent,\n            ActivityOptions.makeSceneTransitionAnimation(context)\n                .toBundle()\n        )\n        return\n      }\n      context.startActivity(intent)\n    }\n  }\n\n  override fun setLayoutId(): Int {\n    return R.layout.activity_markdown_editor\n  }\n\n  override fun initData(savedInstanceState: Bundle?) {\n    super.initData(savedInstanceState)\n    reqCode = intent.getIntExtra(KEY_REQUESTOIN_CODE, -1)\n    content = intent.getCharSequenceExtra(KEY_CONTENT)\n    if (reqCode == -1) {\n      Timber.e( \"没有设置请求码\")\n      finishAfterTransition()\n      return\n    }\n\n    binding.mdeEditor.setText(content)\n    binding.mdeEditor.setOnSaveListener(object : MarkDownEditor.OnSaveListener {\n      override fun onSave(content: CharSequence?) {\n        EventBus.getDefault()\n            .post(EditorEvent(reqCode, content))\n        finishAfterTransition()\n      }\n    })\n  }\n\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/QrCodeScannerActivity.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.view\n\nimport android.os.Bundle\nimport android.view.KeyEvent\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseActivity\nimport com.lyy.keepassa.databinding.ActivityQrCodeScannerBinding\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 2022/1/11\n **/\ninternal class QrCodeScannerActivity : BaseActivity<ActivityQrCodeScannerBinding>() {\n  private lateinit var capture: KpaCaptureManager\n\n  override fun setLayoutId(): Int {\n    return R.layout.activity_qr_code_scanner\n  }\n\n  override fun initData(savedInstanceState: Bundle?) {\n    super.initData(savedInstanceState)\n    capture = KpaCaptureManager(this, binding.barcodeView)\n    capture.initializeFromIntent(this.intent, savedInstanceState)\n    capture.decode()\n  }\n\n  override fun onResume() {\n    super.onResume()\n    capture.onResume()\n  }\n\n  override fun onPause() {\n    super.onPause()\n    capture.onPause()\n  }\n\n  override fun onDestroy() {\n    super.onDestroy()\n    capture.onDestroy()\n  }\n\n  override fun onSaveInstanceState(outState: Bundle) {\n    super.onSaveInstanceState(outState)\n    capture.onSaveInstanceState(outState)\n  }\n\n  override fun onRequestPermissionsResult(\n    requestCode: Int,\n    permissions: Array<out String>,\n    grantResults: IntArray\n  ) {\n    super.onRequestPermissionsResult(requestCode, permissions, grantResults)\n    capture.onRequestPermissionsResult(requestCode, permissions, grantResults)\n  }\n\n  override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {\n    return capture.onKeyDown(keyCode, event) || super.onKeyDown(keyCode, event)\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/SimpleAdapter.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.view\n\nimport android.content.Context\nimport android.view.View\nimport android.widget.TextView\nimport androidx.appcompat.widget.AppCompatImageView\nimport androidx.preference.PreferenceManager\nimport com.arialyy.frame.util.adapter.AbsHolder\nimport com.arialyy.frame.util.adapter.AbsRVAdapter\nimport com.google.android.material.imageview.ShapeableImageView\nimport com.google.android.material.shape.CornerFamily\nimport com.google.android.material.shape.ShapeAppearanceModel\nimport com.keepassdroid.database.PwGroup\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseApp\nimport com.lyy.keepassa.entity.SimpleItemEntity\nimport com.lyy.keepassa.view.SimpleAdapter.Holder\nimport com.lyy.keepassa.widget.toPx\n\n/**\n * list适配器\n */\nclass SimpleAdapter(\n  context: Context,\n  data: List<SimpleItemEntity>\n) : AbsRVAdapter<SimpleItemEntity, Holder>(context, data) {\n  private val useRoundedCorners by lazy {\n    PreferenceManager.getDefaultSharedPreferences(BaseApp.APP)\n        .getBoolean(BaseApp.APP.getString(R.string.set_key_fillet_bg_icon), true)\n  }\n  private val shapeMode by lazy {\n    ShapeAppearanceModel.Builder()\n        .setAllCorners(\n            CornerFamily.ROUNDED,\n            8.toPx()\n                .toFloat()\n        )\n        .build()\n  }\n\n  override fun getViewHolder(\n    convertView: View?,\n    viewType: Int\n  ): Holder {\n    return Holder(convertView!!)\n  }\n\n  override fun setLayoutId(type: Int): Int {\n    return R.layout.item_path_type\n  }\n\n  override fun bindData(\n    holder: Holder,\n    position: Int,\n    item: SimpleItemEntity\n  ) {\n//    if (useRoundedCorners) {\n//      holder.icon.shapeAppearanceModel = shapeMode\n//    }\n    holder.icon.setImageResource(item.icon)\n\n    holder.title.text = item.title\n    holder.des.text = item.subTitle\n  }\n\n  class Holder(view: View) : AbsHolder(view) {\n    val icon: ShapeableImageView = view.findViewById(R.id.icon)\n    val title: TextView = view.findViewById(R.id.title)\n    val des: TextView = view.findViewById(R.id.des)\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/SimpleEntryAdapter.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.view\n\nimport android.content.Context\nimport android.graphics.Paint\nimport android.view.View\nimport android.widget.TextView\nimport androidx.appcompat.widget.AppCompatImageView\nimport com.arialyy.frame.util.adapter.AbsHolder\nimport com.arialyy.frame.util.adapter.AbsRVAdapter\nimport com.keepassdroid.database.PwEntry\nimport com.keepassdroid.database.PwGroup\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.entity.EntryType\nimport com.lyy.keepassa.entity.SimpleItemEntity\nimport com.lyy.keepassa.util.IconUtil\nimport com.lyy.keepassa.util.loadImg\nimport com.lyy.keepassa.view.SimpleEntryAdapter.Holder\nimport java.util.Date\n\n/**\n * list适配器\n */\nclass SimpleEntryAdapter(\n  context: Context,\n  data: List<SimpleItemEntity>\n) : AbsRVAdapter<SimpleItemEntity, Holder>(context, data) {\n\n  override fun getViewHolder(\n    convertView: View?,\n    viewType: Int\n  ): Holder {\n    return Holder(convertView!!)\n  }\n\n  override fun setLayoutId(type: Int): Int {\n    return R.layout.item_entry\n  }\n\n  override fun bindData(\n    holder: Holder,\n    position: Int,\n    item: SimpleItemEntity\n  ) {    if (item.obj is PwGroup) {\n      IconUtil.setGroupIcon(context, item.obj as PwGroup, holder.icon)\n    } else if (item.obj is PwEntry) {\n      IconUtil.setEntryIcon(item.obj as PwEntry, holder.icon)\n      val paint = holder.title.paint\n      if ((item.obj as PwEntry).expires()\n        && (item.obj as PwEntry).expiryTime != null\n        && (item.obj as PwEntry).expiryTime.before(Date(System.currentTimeMillis()))\n      ) {\n        paint.flags = Paint.STRIKE_THRU_TEXT_FLAG\n        paint.isAntiAlias = true\n      } else {\n        paint.flags = 0\n      }\n    } else if (item.obj == EntryType.TYPE_COLLECTION) {\n      holder.icon.loadImg(item.icon)\n    }\n\n    holder.title.text = item.title\n    holder.des.text = item.subTitle\n    holder.des.visibility = if (item.subTitle.isBlank()) View.GONE else View.VISIBLE\n  }\n\n  class Holder(view: View) : AbsHolder(view) {\n    val icon: AppCompatImageView = view.findViewById(R.id.icon)\n    val title: TextView = view.findViewById(R.id.title)\n    val des: TextView = view.findViewById(R.id.des)\n  }\n}\n\n"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/StorageType.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.view\n\nimport androidx.annotation.DrawableRes\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseApp\n\nenum class StorageType(\n  var type: Int,\n  @DrawableRes var icon: Int,\n  var lable: String\n) {\n  AFS(0, R.drawable.ic_android, BaseApp.APP.getString(R.string.afs)),\n  DROPBOX(1, R.drawable.ic_dropbox, \"Dropbox\"),\n  ONE_DRIVE(2, R.drawable.ic_onedrive, \"OneDrive\"),\n  GOOGLE_DRIVE(3, R.drawable.ic_google_drive, \"GoogleDrive\"),\n  WEBDAV(4, R.drawable.ic_http, \"WebDav\"),\n  FTP(5, R.drawable.ic_ftp, \"Ftp\"),\n  UNKNOWN(-1, R.drawable.ic_android, \"Unknown\")\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/UpgradeLogDialog.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.view\n\nimport android.content.Context\nimport android.content.Intent\nimport android.net.Uri\nimport android.provider.Settings\nimport android.text.TextUtils\nimport androidx.core.app.ActivityOptionsCompat\nimport androidx.core.content.edit\nimport com.arialyy.frame.router.Routerfit\nimport com.arialyy.frame.util.AndroidUtils\nimport com.arialyy.frame.util.ResUtil\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseDialog\nimport com.lyy.keepassa.base.Constance\nimport com.lyy.keepassa.databinding.DialogUpgradeBinding\nimport com.lyy.keepassa.router.ActivityRouter\nimport com.lyy.keepassa.router.DialogRouter\nimport com.lyy.keepassa.util.FingerprintUtil\nimport com.lyy.keepassa.util.KpaUtil\nimport com.lyy.keepassa.util.LanguageUtil\nimport com.lyy.keepassa.view.dialog.DonateDialog\nimport com.lyy.keepassa.view.fingerprint.FingerprintActivity\nimport com.lyy.keepassa.widget.DrawableTextView\nimport com.lyy.keepassa.widget.toPx\nimport com.zzhoujay.richtext.RichText\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.MainScope\nimport kotlinx.coroutines.cancel\nimport kotlinx.coroutines.launch\nimport kotlinx.coroutines.withContext\nimport timber.log.Timber\nimport java.io.InputStream\n\n/**\n * 版本升级对话框\n */\nclass UpgradeLogDialog : BaseDialog<DialogUpgradeBinding>() {\n  private val scope = MainScope()\n\n  override fun setLayoutId(): Int {\n    return R.layout.dialog_upgrade\n  }\n\n  override fun initData() {\n    super.initData()\n    scope.launch {\n      var context = \"\"\n      // val fileName = \"version_log/version_log_${getVersionSuffix()}.md\"\n      val fileName = \"version_log/version_log_${if (KpaUtil.isChina()) \"zh_CN\" else \"en\"}.md\"\n      withContext(Dispatchers.IO) {\n\n//        val ins  = requireContext().assets.open(fileName)\n//        context = String(ins.readBytes())\n//        ins.close()\n\n        var ins: InputStream? = null\n        try {\n          ins = requireContext().assets.open(fileName)\n        } catch (e: Exception) {\n          ins = requireContext().assets.open(\"version_log/version_log_en.md\")\n          Timber.e(e)\n        }\n        ins?.let {\n          context = String(it.readBytes())\n          it.close()\n        }\n      }\n      RichText.fromMarkdown(context)\n        .urlClick { url ->\n          if (handlerUrlClick(url)) {\n            dismiss()\n          }\n          return@urlClick true\n        }\n        .into(binding.tvContent)\n    }\n    binding.btEnter.setOnClickListener {\n      dismiss()\n    }\n    binding.btDonate.setDrawable(\n      DrawableTextView.LEFT,\n      ResUtil.getSvgIcon(R.drawable.ic_favorite_24px, R.color.text_blue_color),\n      16.toPx(),\n      16.toPx()\n    )\n    binding.btDonate.setOnClickListener {\n      DonateDialog().show()\n    }\n  }\n\n  override fun onStart() {\n    super.onStart()\n    dialog?.window?.setLayout(360.toPx(), 600.toPx())\n  }\n\n  override fun dismiss() {\n    super.dismiss()\n    requireContext().getSharedPreferences(Constance.PRE_FILE_NAME, Context.MODE_PRIVATE)\n      .edit {\n        putInt(Constance.VERSION_CODE, AndroidUtils.getVersionCode(requireContext()))\n      }\n  }\n\n  /**\n   * 根据语言获取版本日志后缀名\n   */\n  private fun getVersionSuffix(): String {\n    var defLocal = LanguageUtil.getDefLanguage(requireContext())\n    if (defLocal == null) {\n      defLocal = LanguageUtil.getSysCurrentLan()\n    }\n    return if (TextUtils.isEmpty(defLocal.country)) {\n      defLocal.language\n    } else {\n      \"${defLocal.language}_${defLocal.country}\"\n    }\n  }\n\n  /**\n   * 除了url点击\n   * @return true 已处理\n   */\n  private fun handlerUrlClick(url: String): Boolean {\n    val uri = Uri.parse(url)\n    if (uri.scheme == \"route\") {\n      val activity = uri.getQueryParameter(\"activity\")\n      if (!activity.isNullOrEmpty()) {\n        when (activity) {\n          \"FingerprintActivity\" -> {\n            if (FingerprintUtil.hasBiometricPrompt(requireContext())) {\n              FingerprintActivity.toFingerprintActivity(requireActivity())\n              return true\n            }\n          }\n          \"WebDavLoginDialog\" -> {\n            Routerfit.create(DialogRouter::class.java).showWebDavLoginDialog()\n            return true\n          }\n          \"SettingActivity\" -> {\n            val type = uri.getQueryParameter(\"type\")\n            when (type) {\n              \"db\" -> {\n                Routerfit.create(ActivityRouter::class.java, requireActivity()).toAppSetting(\n                  opt = ActivityOptionsCompat.makeSceneTransitionAnimation(requireActivity())\n                )\n              }\n              \"app\" -> {\n                val scrollKey = uri.getQueryParameter(\"scrollKey\")\n                Routerfit.create(ActivityRouter::class.java, requireActivity()).toAppSetting(\n                  opt = ActivityOptionsCompat.makeSceneTransitionAnimation(requireActivity()),\n                  scrollKey = scrollKey\n                )\n              }\n            }\n            return true\n          }\n          \"ime\" -> {\n            startActivity(Intent(Settings.ACTION_INPUT_METHOD_SETTINGS))\n            return true\n          }\n        }\n      }\n    } else {\n      Timber.d(\"url = $url\")\n      startActivity(Intent(Intent.ACTION_VIEW).apply {\n        data = Uri.parse(url)\n      })\n    }\n    return false\n  }\n\n  override fun onDestroy() {\n    super.onDestroy()\n    scope.cancel()\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/collection/CollectionActivity.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.view.collection\n\nimport android.annotation.SuppressLint\nimport android.os.Bundle\nimport android.view.View\nimport androidx.appcompat.widget.AppCompatImageView\nimport androidx.lifecycle.ViewModelProvider\nimport androidx.lifecycle.lifecycleScope\nimport androidx.recyclerview.widget.LinearLayoutManager\nimport com.alibaba.android.arouter.facade.annotation.Route\nimport com.alibaba.android.arouter.launcher.ARouter\nimport com.arialyy.frame.router.Routerfit\nimport com.arialyy.frame.util.ResUtil\nimport com.keepassdroid.database.PwEntry\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseActivity\nimport com.lyy.keepassa.databinding.ActivityCollectionBinding\nimport com.lyy.keepassa.event.CollectionEventType.COLLECTION_STATE_ADD\nimport com.lyy.keepassa.event.CollectionEventType.COLLECTION_STATE_REMOVE\nimport com.lyy.keepassa.event.CollectionEventType.COLLECTION_STATE_TOTAL\nimport com.lyy.keepassa.router.DialogRouter\nimport com.lyy.keepassa.util.KeepassAUtil\nimport com.lyy.keepassa.util.KpaUtil\nimport com.lyy.keepassa.util.doOnItemClickListener\nimport com.lyy.keepassa.view.SimpleEntryAdapter\nimport kotlinx.coroutines.flow.collectLatest\nimport kotlinx.coroutines.launch\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 19:43 上午 2022/3/29\n **/\n@Route(path = \"/collection/ac\")\ninternal class CollectionActivity : BaseActivity<ActivityCollectionBinding>() {\n  private lateinit var module: CollectionModule\n  private lateinit var adapter: SimpleEntryAdapter\n\n  override fun setLayoutId(): Int {\n    return R.layout.activity_collection\n  }\n\n  override fun initData(savedInstanceState: Bundle?) {\n    super.initData(savedInstanceState)\n    toolbar.title = ResUtil.getString(R.string.my_collection)\n    module = ViewModelProvider(this)[CollectionModule::class.java]\n\n    adapter = SimpleEntryAdapter(this, module.itemDataList)\n    binding.rvList.let {\n      it.layoutManager = LinearLayoutManager(this)\n      it.setHasFixedSize(true)\n      it.adapter = adapter\n    }\n\n    binding.rvList.doOnItemClickListener { _, position, v ->\n      val item = module.itemDataList[position]\n      val icon = v.findViewById<AppCompatImageView>(R.id.icon)\n      KeepassAUtil.instance.turnEntryDetail(this, item.obj as PwEntry, icon)\n    }\n\n    binding.emptyView.setText(ResUtil.getString(R.string.no_collection))\n\n    listenerCollection()\n    listenerGetData()\n    module.getData()\n  }\n\n  @SuppressLint(\"NotifyDataSetChanged\")\n  private fun listenerGetData() {\n    lifecycleScope.launch {\n      module.itemDataFlow.collectLatest {\n        if (it.isNullOrEmpty()) {\n          binding.emptyView.visibility = View.VISIBLE\n          return@collectLatest\n        }\n        binding.emptyView.visibility = View.GONE\n        adapter.notifyDataSetChanged()\n      }\n    }\n  }\n\n  @SuppressLint(\"NotifyDataSetChanged\")\n  private fun listenerCollection() {\n    lifecycleScope.launch {\n      KpaUtil.kdbHandlerService.collectionStateFlow.collectLatest {\n        if (it.collectionNum == 0) {\n          binding.emptyView.visibility = View.VISIBLE\n          return@collectLatest\n        }\n        binding.emptyView.visibility = View.GONE\n        when (it.state) {\n          COLLECTION_STATE_TOTAL -> {\n            adapter.notifyDataSetChanged()\n          }\n          COLLECTION_STATE_ADD -> {\n            module.addNewItem(adapter, it.pwEntryV4)\n          }\n          COLLECTION_STATE_REMOVE -> {\n            module.removeItem(adapter, it.pwEntryV4)\n          }\n        }\n      }\n    }\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/collection/CollectionModule.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.view.collection\n\nimport androidx.lifecycle.viewModelScope\nimport com.keepassdroid.database.PwEntryV4\nimport com.lyy.keepassa.base.BaseModule\nimport com.lyy.keepassa.entity.SimpleItemEntity\nimport com.lyy.keepassa.util.KeepassAUtil\nimport com.lyy.keepassa.util.KpaUtil\nimport com.lyy.keepassa.view.SimpleEntryAdapter\nimport kotlinx.coroutines.flow.MutableStateFlow\nimport kotlinx.coroutines.launch\nimport timber.log.Timber\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 19:48 上午 2022/3/29\n **/\ninternal class CollectionModule : BaseModule() {\n  val itemDataList = arrayListOf<SimpleItemEntity>()\n\n  val itemDataFlow = MutableStateFlow<ArrayList<SimpleItemEntity>?>(null)\n\n  fun removeItem(adapter: SimpleEntryAdapter, newEntry: PwEntryV4?) {\n    if (newEntry == null) {\n      Timber.d(\"entry is null\")\n      return\n    }\n    val newItem = KeepassAUtil.instance.convertPwEntry2Item(newEntry)\n    var removePosition = -1\n    itemDataList.forEachIndexed { index, simpleItemEntity ->\n      if (simpleItemEntity.obj == newItem.obj) {\n        removePosition = index\n        return@forEachIndexed\n      }\n    }\n\n    if (removePosition == -1) {\n      Timber.d(\"the entry is not in the list, title = ${newEntry.title}\")\n      return\n    }\n    itemDataList.removeAt(removePosition)\n    adapter.notifyItemRemoved(removePosition)\n  }\n\n  /**\n   * add new collection\n   */\n  fun addNewItem(adapter: SimpleEntryAdapter, newEntry: PwEntryV4?) {\n    if (newEntry == null) {\n      Timber.d(\"entry is null\")\n      return\n    }\n    val newItem = KeepassAUtil.instance.convertPwEntry2Item(newEntry)\n    val temp = itemDataList.find { it.obj == newItem.obj }\n    if (temp != null) {\n      Timber.d(\"already has the entry, title = ${newEntry.title}\")\n      return\n    }\n    val newPosition = itemDataList.size\n    itemDataList.add(newItem)\n    adapter.notifyItemInserted(newPosition)\n  }\n\n  fun getData() {\n    itemDataList.clear()\n    KpaUtil.kdbHandlerService.getCollectionEntries().forEach {\n      itemDataList.add(KeepassAUtil.instance.convertPwEntry2Item(it))\n    }\n    viewModelScope.launch {\n      itemDataFlow.emit(itemDataList)\n    }\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/create/CreateCustomStrDialog.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.view.create\n\nimport android.view.View\nimport androidx.lifecycle.lifecycleScope\nimport com.alibaba.android.arouter.facade.annotation.Autowired\nimport com.alibaba.android.arouter.facade.annotation.Route\nimport com.alibaba.android.arouter.launcher.ARouter\nimport com.keepassdroid.database.security.ProtectedString\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseDialog\nimport com.lyy.keepassa.databinding.DialogAddAttrStrBinding\nimport com.lyy.keepassa.entity.CommonState\nimport com.lyy.keepassa.event.AttrStrEvent\nimport com.lyy.keepassa.util.HitUtil\nimport kotlinx.coroutines.flow.MutableSharedFlow\nimport kotlinx.coroutines.launch\n\n/**\n * 创建自定义字段的对话框\n */\n@Route(path = \"/dialog/customStrDialog\")\nclass CreateCustomStrDialog : BaseDialog<DialogAddAttrStrBinding>(),\n  View.OnClickListener {\n  companion object {\n    val CustomStrFlow = MutableSharedFlow<AttrStrEvent?>(0)\n  }\n\n  @Autowired(name = \"key\")\n  @JvmField\n  var key: String? = null\n\n  @Autowired(name = \"value\")\n  @JvmField\n  var value: ProtectedString? = null\n\n  @Autowired(name = \"position\")\n  @JvmField\n  var position: Int = 0\n\n  override fun setLayoutId(): Int {\n    return R.layout.dialog_add_attr_str\n  }\n\n  override fun initData() {\n    super.initData()\n    ARouter.getInstance().inject(this)\n    binding.cancel.setOnClickListener(this)\n    binding.enter.setOnClickListener(this)\n    if (key != null) {\n      binding.strKey.setText(key)\n    }\n    if (value != null) {\n      binding.strValue.setText(value.toString())\n      binding.cb.isChecked = value!!.isProtected\n    }\n  }\n\n  override fun onClick(v: View?) {\n    if (v!!.id == R.id.enter) {\n      if (binding.strKey.text.toString().trim().isEmpty()) {\n        HitUtil.toaskShort(getString(R.string.error_attr_str_null))\n        return\n      }\n      lifecycleScope.launch {\n        CustomStrFlow.emit(\n          AttrStrEvent(\n            if (key != null) CommonState.MODIFY else CommonState.CREATE,\n            binding.strKey.text.toString(),\n            ProtectedString(binding.cb.isChecked, binding.strValue.text.toString()),\n            position\n          )\n        )\n      }\n    }\n    dismiss()\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/create/CreateDbActivity.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.view.create\n\nimport android.os.Bundle\nimport android.text.TextUtils\nimport android.view.View\nimport android.view.ViewAnimationUtils\nimport androidx.appcompat.widget.Toolbar\nimport androidx.core.app.ActivityOptionsCompat\nimport androidx.lifecycle.ViewModelProvider\nimport androidx.lifecycle.lifecycleScope\nimport androidx.transition.Transition\nimport androidx.transition.TransitionInflater\nimport com.alibaba.android.arouter.facade.annotation.Route\nimport com.arialyy.frame.router.Routerfit\nimport com.blankj.utilcode.util.ToastUtils\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseActivity\nimport com.lyy.keepassa.base.BaseApp\nimport com.lyy.keepassa.databinding.ActivityCreateDbBinding\nimport com.lyy.keepassa.router.ActivityRouter\nimport com.lyy.keepassa.util.HitUtil\nimport com.lyy.keepassa.util.KeepassAUtil\nimport com.lyy.keepassa.util.KpaUtil\nimport kotlinx.coroutines.flow.collectLatest\nimport kotlinx.coroutines.launch\nimport timber.log.Timber\n\n/**\n * 创建见数据库页面\n */\n@Route(path = \"/launcher/createDb\")\nclass CreateDbActivity : BaseActivity<ActivityCreateDbBinding>(), View.OnClickListener {\n  private var curSetup = 1\n  private lateinit var firstFragment: CreateDbFirstFragment\n  private var secondFragment: CreateDbSecondFragment? = null\n  private lateinit var module: CreateDbModule\n\n  override fun initData(savedInstanceState: Bundle?) {\n    super.initData(savedInstanceState)\n    module = ViewModelProvider(this).get(CreateDbModule::class.java)\n    toolbar.setTitle(R.string.create_db)\n    binding.next.setOnClickListener(this)\n    binding.up.setOnClickListener(this)\n    firstFragment = CreateDbFirstFragment()\n    val transaction = supportFragmentManager.beginTransaction()\n    transaction.replace(R.id.content, firstFragment)\n    transaction.commitNow()\n    listenerOpenDb()\n  }\n\n  private fun listenerOpenDb() {\n    lifecycleScope.launch {\n      KpaUtil.kdbOpenService.openDbFlow.collectLatest {\n        if (it == null) {\n          ToastUtils.showShort(\"${getString(R.string.open_db)}${getString(R.string.fail)}\")\n          return@collectLatest\n        }\n        Timber.d(\"创建数据库成功\")\n        HitUtil.toaskShort(getString(R.string.hint_db_create_success, module.dbName))\n        Routerfit.create(ActivityRouter::class.java, this@CreateDbActivity).toMainActivity(\n          opt = ActivityOptionsCompat.makeSceneTransitionAnimation(this@CreateDbActivity)\n        )\n        KeepassAUtil.instance.saveLastOpenDbHistory(BaseApp.dbRecord)\n        finishAfterTransition()\n      }\n    }\n  }\n\n  /**\n   * 右 -> 左\n   */\n  private fun getRlAnim(): Transition {\n    return TransitionInflater.from(this)\n      .inflateTransition(R.transition.slide_enter)\n  }\n\n  /**\n   * 左 -> 右\n   */\n  private fun getLrAnim(): Transition {\n    return TransitionInflater.from(this)\n      .inflateTransition(R.transition.slide_exit)\n  }\n\n  override fun setLayoutId(): Int {\n    return R.layout.activity_create_db\n  }\n\n  override fun onBackPressed() {\n    if (curSetup == 2) {\n      upFragment()\n    } else {\n      finishAfterTransition()\n    }\n  }\n\n  override fun onClick(v: View?) {\n    if (KeepassAUtil.instance.isFastClick()) {\n      return\n    }\n    when (v!!.id) {\n      R.id.next -> {\n        if (curSetup == 1) {\n//          startNextFragment()\n          firstFragment.startNext()\n        } else { // 完成\n          done()\n        }\n      }\n      R.id.up -> upFragment()\n    }\n  }\n\n  /**\n   * 完成信息输入，并创建数据库\n   */\n  private fun done() {\n    // 密码需要重新获取，将密码设置到module中\n    secondFragment?.getPass()\n    module.createAndOpenDb()\n  }\n\n  /**\n   * 开始设置密码\n   */\n  fun startNextFragment() {\n    if (TextUtils.isEmpty(firstFragment.getDbName())) {\n      firstFragment.handleDbNameNull()\n      return\n    } else if (module.localDbUri == null) {\n      firstFragment.showSaveTypeDialog()\n      return\n    }\n    curSetup = 2\n    binding.next.setText(R.string.done)\n    binding.up.visibility = View.VISIBLE\n\n    if (secondFragment == null) {\n      secondFragment = CreateDbSecondFragment()\n    }\n    /*\n     * 重新设置动画：\n     * fragment1 （进入）左 -> 右；（退出）左 -> 右\n     * fragment2 （进入）右 -> 左；（退出）左 -> 右\n     */\n    firstFragment.exitTransition = getLrAnim()\n    secondFragment!!.enterTransition = getRlAnim()\n\n    val changeBoundsTransition = TransitionInflater.from(this)\n//        .inflateTransition(R.transition.changebounds_with_arcmotion)\n      .inflateTransition(android.R.transition.move)\n    secondFragment!!.sharedElementEnterTransition = changeBoundsTransition\n\n    supportFragmentManager.beginTransaction()\n      .replace(R.id.content, secondFragment!!)\n      .addSharedElement(firstFragment.getShareElement(), getString(R.string.transition_db_name))\n      .commit()\n//    changeBg(true)\n  }\n\n  /**\n   * 返回设置数据库路径\n   */\n  private fun upFragment() {\n    curSetup = 1\n    binding.next.setText(R.string.next)\n    binding.up.visibility = View.GONE\n\n    /*\n     * 重新设置动画：\n     * fragment1 （进入）左 -> 右；（退出）右 -> 左\n     * fragment2 （进入）右 -> 左；（退出）右 -> 左\n     */\n    firstFragment.enterTransition = getLrAnim()\n    firstFragment.exitTransition = getRlAnim()\n    secondFragment!!.exitTransition = getRlAnim()\n\n    val changeBoundsTransition = TransitionInflater.from(this)\n//        .inflateTransition(R.transition.changebounds_with_arcmotion)\n      .inflateTransition(android.R.transition.move)\n    firstFragment.sharedElementEnterTransition = changeBoundsTransition\n    supportFragmentManager.beginTransaction()\n      .replace(R.id.content, firstFragment)\n      .addSharedElement(\n        secondFragment!!.getShareElement(), getString(R.string.transition_db_name)\n      )\n      .commitNow()\n//    changeBg(false)\n  }\n\n  /**\n   * 切换fragment改变背景\n   */\n  private fun changeBg(toSecondFragment: Boolean) {\n    val view = findViewById<Toolbar>(R.id.kpa_toolbar)\n    val finalRadius = view.width.coerceAtLeast(view.height)\n    val anim = ViewAnimationUtils.createCircularReveal(\n      view, if (toSecondFragment) view.right else 0, 0, 0f, finalRadius.toFloat()\n    )\n    view.setBackgroundResource(\n      if (toSecondFragment) R.color.colorPrimary else R.color.white\n    )\n    anim.duration = resources.getInteger(R.integer.anim_duration_long)\n      .toLong()\n//    anim.interpolator = AccelerateInterpolator()\n    view.visibility = View.VISIBLE\n    anim.start()\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/create/CreateDbFirstFragment.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.view.create\n\nimport android.content.Intent\nimport android.text.TextUtils\nimport android.view.View\nimport android.view.inputmethod.EditorInfo\nimport androidx.collection.arrayMapOf\nimport androidx.lifecycle.ViewModelProvider\nimport com.arialyy.frame.router.Routerfit\nimport com.arialyy.frame.util.ResUtil\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseFragment\nimport com.lyy.keepassa.databinding.FragmentCreateDbFirstBinding\nimport com.lyy.keepassa.event.DbPathEvent\nimport com.lyy.keepassa.router.DialogRouter\nimport com.lyy.keepassa.util.HitUtil\nimport com.lyy.keepassa.util.KeepassAUtil\nimport com.lyy.keepassa.util.cloud.DbSynUtil\nimport com.lyy.keepassa.view.StorageType\nimport com.lyy.keepassa.view.StorageType.AFS\nimport com.lyy.keepassa.view.StorageType.DROPBOX\nimport com.lyy.keepassa.view.StorageType.ONE_DRIVE\nimport com.lyy.keepassa.view.StorageType.UNKNOWN\nimport com.lyy.keepassa.view.StorageType.WEBDAV\nimport com.lyy.keepassa.view.create.auth.AuthFlowFactory\nimport com.lyy.keepassa.view.create.auth.IAuthCallback\nimport com.lyy.keepassa.view.create.auth.IAuthFlow\nimport com.lyy.keepassa.view.create.auth.OnNextFinishCallback\nimport com.lyy.keepassa.widget.BubbleTextView\nimport com.lyy.keepassa.widget.BubbleTextView.OnIconClickListener\nimport timber.log.Timber\n\n/**\n * 创建数据库的第一步\n * 1、设置数据库保存类型\n * 2、设置数据库名\n */\nclass CreateDbFirstFragment : BaseFragment<FragmentCreateDbFirstBinding>() {\n\n  private lateinit var module: CreateDbModule\n  private lateinit var pathTypeDialog: PathTypeDialog\n  private var authFlow: IAuthFlow? = null\n  private var isAuthorized: Boolean = false\n  private val flowMap = arrayMapOf<StorageType, IAuthFlow>()\n\n  override fun initData() {\n    module = ViewModelProvider(requireActivity()).get(CreateDbModule::class.java)\n    initView()\n  }\n\n  private fun initView() {\n    setPathTypeInfo()\n    showSaveTypeDialog()\n\n    binding.pathType.setOnIconClickListener(object : OnIconClickListener {\n      override fun onClick(\n        view: BubbleTextView,\n        index: Int\n      ) {\n        if (index == 2) {\n          Routerfit.create(DialogRouter::class.java)\n            .showMsgDialog(\n              msgContent = ResUtil.getString(R.string.help_create_db_path),\n              showCancelBt = false\n            )\n        }\n      }\n    })\n\n    // 设置键盘确定按钮属性\n    binding.dbName.setOnEditorActionListener { _, actionId, _ ->\n      if (!isAdded) {\n        return@setOnEditorActionListener false\n      }\n      // actionId 和android:imeOptions 属性要保持一致\n      if (actionId == EditorInfo.IME_ACTION_DONE && !TextUtils.isEmpty(binding.dbName.text)) {\n        KeepassAUtil.instance.toggleKeyBord(requireContext())\n//        showPathDialog()\n        startNext()\n        true\n      } else {\n        false\n      }\n    }\n  }\n\n  /**\n   * 处理数据库名没有设置的情况\n   */\n  fun handleDbNameNull() {\n    val hint = getString(R.string.error_db_name_null)\n\n    binding.dbNameLayout.error = hint\n    binding.dbName.requestFocus()\n    HitUtil.toaskShort(hint)\n    KeepassAUtil.instance.toggleKeyBord(requireContext())\n  }\n\n  /**\n   * 和其它fragment共享的元素\n   */\n  fun getShareElement(): View {\n    return binding.dbName\n  }\n\n  /**\n   * 获取数据库名\n   */\n  fun getDbName(): String {\n    module.dbName = binding.dbName.text.toString()\n      .trim()\n    return module.dbName\n  }\n\n  fun showSaveTypeDialog() {\n    pathTypeDialog = PathTypeDialog(\n      binding.dbName.text.toString()\n        .trim()\n    )\n    pathTypeDialog.showNow(childFragmentManager, \"PathDialog\")\n    pathTypeDialog.setOnDismissListener {\n      if (module.storageType == UNKNOWN) {\n        requireActivity().finishAfterTransition()\n        return@setOnDismissListener\n      }\n      setPathTypeInfo()\n      authFlow = flowMap[module.storageType]\n      if (authFlow == null) {\n        authFlow = AuthFlowFactory.getAuthFlow(module.storageType)\n        flowMap[module.storageType] = authFlow\n        lifecycle.addObserver(authFlow!!)\n      }\n      authFlow?.let {\n        it.initContent(requireContext(), object : IAuthCallback {\n          override fun callback(success: Boolean) {\n            isAuthorized = success\n            binding.dbName.requestFocus()\n          }\n        })\n        it.startFlow()\n      }\n    }\n  }\n\n  /**\n   * 流程结束\n   */\n  private fun finishFlow(event: DbPathEvent) {\n    if (event.fileUri == null && event.storageType == AFS) {\n      Timber.e(\"uri 获取失败\")\n      return\n    }\n    // 直接启动下一界面\n    val startNextFragment = true\n\n    when (event.storageType) {\n      AFS -> {\n        binding.dbNameLayout.visibility = View.VISIBLE\n        module.localDbUri = event.fileUri!!\n        module.dbName = event.dbName\n      }\n      DROPBOX -> {\n        binding.dbNameLayout.visibility = View.VISIBLE\n        module.localDbUri = DbSynUtil.getCloudDbTempPath(DROPBOX.name, event.dbName)\n        module.cloudPath = event.cloudDiskPath!!\n        module.dbName = event.dbName\n      }\n      WEBDAV -> {\n        binding.dbNameLayout.visibility = View.GONE\n        module.dbName = event.dbName\n        module.localDbUri = DbSynUtil.getCloudDbTempPath(WEBDAV.name, event.dbName)\n        module.cloudPath = event.cloudDiskPath!!\n      }\n      ONE_DRIVE -> {\n        binding.dbNameLayout.visibility = View.VISIBLE\n        module.localDbUri = DbSynUtil.getCloudDbTempPath(ONE_DRIVE.name, event.dbName)\n        module.cloudPath = event.cloudDiskPath!!\n        module.dbName = event.dbName\n      }\n      else -> {\n        throw IllegalArgumentException(\"不支持的类型: ${event.storageType.lable}\")\n      }\n    }\n\n\n    binding.dbName.setText(module.dbName)\n    setPathTypeInfo()\n\n    if (startNextFragment) {\n      (activity as CreateDbActivity).startNextFragment()\n    }\n  }\n\n  /**\n   * 检查是否可以进入下一步\n   */\n  fun startNext(): Boolean {\n    val temp = binding.dbName.text.toString()\n      .trim()\n    if (TextUtils.isEmpty(temp)) {\n      HitUtil.toaskShort(getString(R.string.error_db_name_null))\n      return false\n    }\n\n    authFlow?.doNext(this, temp, object : OnNextFinishCallback {\n      override fun onFinish(event: DbPathEvent) {\n        finishFlow(event)\n      }\n    })\n\n    return true\n  }\n\n  /**\n   * 设置文件路径类型提示\n   */\n  private fun setPathTypeInfo() {\n    binding.pathType.text = module.storageType.lable\n    binding.pathType.setLeftIcon(module.storageType.icon)\n    setDbNameHint(module.storageType)\n  }\n\n  /**\n   * 设置数据名输入提示\n   */\n  private fun setDbNameHint(storageType: StorageType) {\n    binding.dbNameLayout.helperText = getString(R.string.help_create_db)\n    binding.dbNameLayout.hint = getString(R.string.db_name)\n  }\n\n  override fun setLayoutId(): Int {\n    return R.layout.fragment_create_db_first\n  }\n\n  override fun onActivityResult(\n    requestCode: Int,\n    resultCode: Int,\n    data: Intent?\n  ) {\n    super.onActivityResult(requestCode, resultCode, data)\n    authFlow?.onActivityResult(requestCode, resultCode, data)\n  }\n\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/create/CreateDbModule.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.view.create\n\nimport android.content.Context\nimport android.net.Uri\nimport androidx.core.app.ActivityOptionsCompat\nimport androidx.fragment.app.FragmentActivity\nimport androidx.lifecycle.liveData\nimport com.arialyy.frame.router.Routerfit\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseApp\nimport com.lyy.keepassa.base.BaseModule\nimport com.lyy.keepassa.entity.SimpleItemEntity\nimport com.lyy.keepassa.router.ActivityRouter\nimport com.lyy.keepassa.util.HitUtil\nimport com.lyy.keepassa.util.KeepassAUtil\nimport com.lyy.keepassa.util.KpaUtil\nimport com.lyy.keepassa.util.NotificationUtil\nimport com.lyy.keepassa.view.StorageType\nimport com.lyy.keepassa.view.StorageType.UNKNOWN\nimport timber.log.Timber\n\nclass CreateDbModule : BaseModule() {\n\n  /**\n   * 设置的数据库密码\n   */\n  var dbPass: String = \"\"\n\n  /**\n   * 数据库名，包含.kdbx\n   */\n  var dbName: String = \"\"\n\n  /**\n   * 数据库uri\n   */\n  var localDbUri: Uri? = null\n\n  /**\n   * key uri\n   */\n  var keyUri: Uri? = null\n\n  /**\n   * key 的名字\n   */\n  var keyName: String = \"\"\n\n  /**\n   * 数据库类型\n   */\n  var storageType: StorageType = UNKNOWN\n\n  /**\n   * 云盘路径\n   */\n  var cloudPath: String = \"\"\n\n  /**\n   * 创建并打开数据库\n   */\n  fun createAndOpenDb() {\n    KpaUtil.kdbOpenService.createDb(dbName, localDbUri, dbPass, keyUri, cloudPath, storageType)\n  }\n\n  /**\n   * 数据库打开方式\n   */\n  fun getDbOpenTypeData(context: Context) = liveData {\n\n    val titles = context.resources.getStringArray(R.array.cloud_names)\n    val icons = context.resources.obtainTypedArray(R.array.path_type_img)\n    val items = ArrayList<SimpleItemEntity>()\n    for ((index, title) in titles.withIndex()) {\n      val item = SimpleItemEntity()\n      item.title = title\n      item.subTitle = titles[index]\n      item.id = index\n      item.icon = icons.getResourceId(index, 0)\n      items.add(item)\n    }\n    icons.recycle()\n    emit(items)\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/create/CreateDbSecondFragment.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.view.create\n\nimport android.animation.Animator\nimport android.animation.AnimatorListenerAdapter\nimport android.animation.ValueAnimator\nimport android.annotation.SuppressLint\nimport android.text.InputType\nimport android.text.TextUtils\nimport android.view.View\nimport android.view.animation.LinearInterpolator\nimport android.widget.RadioButton\nimport androidx.lifecycle.ViewModelProvider\nimport com.arialyy.frame.router.Routerfit\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseFragment\nimport com.lyy.keepassa.databinding.FragmentCreateDbSecondBinding\nimport com.lyy.keepassa.event.KeyPathEvent\nimport com.lyy.keepassa.router.DialogRouter\nimport com.lyy.keepassa.util.EventBusHelper\nimport com.lyy.keepassa.util.HitUtil\nimport com.lyy.keepassa.util.KeepassAUtil\nimport com.lyy.keepassa.widget.BubbleTextView\nimport org.greenrobot.eventbus.Subscribe\nimport org.greenrobot.eventbus.ThreadMode.MAIN\n\n/**\n * 设置密码、密钥等信息\n */\nclass CreateDbSecondFragment : BaseFragment<FragmentCreateDbSecondBinding>(),\n  BubbleTextView.OnIconClickListener {\n\n  private var keyPassLayoutH: Int = 0\n  private var isShowPass = false\n  private lateinit var module: CreateDbModule\n\n  override fun setLayoutId(): Int {\n    return R.layout.fragment_create_db_second\n  }\n\n  @SuppressLint(\"RestrictedApi\")\n  override fun initData() {\n    EventBusHelper.reg(this)\n    module = ViewModelProvider(requireActivity()).get(CreateDbModule::class.java)\n    binding.dbName.setText(module.dbName)\n    val leftDrawable = resources.getDrawable(module.storageType.icon, requireContext().theme)\n    val iconSize = resources.getDimension(R.dimen.icon_size)\n    leftDrawable.setBounds(0, 0, iconSize.toInt(), iconSize.toInt())\n    binding.dbHint.setCompoundDrawables(leftDrawable, null, null, null)\n    binding.encryptGroup.setOnCheckedChangeListener { group, checkedId ->\n      val rb = group.findViewById<RadioButton>(checkedId)\n      if (rb.tag == \"1\") {\n//          binding.passKeyLayout.visibility = View.GONE\n        hintPassLayout()\n      } else {\n//          binding.passKeyLayout.visibility = View.VISIBLE\n        showPassLayout()\n      }\n    }\n    binding.encryptType.setOnIconClickListener(this)\n    binding.passKey.setOnIconClickListener(this)\n    (binding.encryptGroup.getChildAt(0) as RadioButton).isChecked = true\n    binding.passKeyLayout.post {\n      keyPassLayoutH = binding.passKeyLayout.height\n    }\n    binding.chooseBt.setOnClickListener {\n      val dialog = CreatePassKeyDialog()\n      dialog.show(childFragmentManager, \"passKeyDialog\")\n    }\n    KeepassAUtil.instance.toggleKeyBord(requireContext())\n    binding.password.requestFocus()\n    binding.passwordLayout.endIconDrawable = resources.getDrawable(R.drawable.ic_view_off)\n//    binding.password.imeOptions = EditorInfo.IME_ACTION_NEXT\n\n    binding.passwordLayout.setEndIconOnClickListener {\n      isShowPass = !isShowPass\n      if (isShowPass) {\n        binding.passwordLayout.endIconDrawable = resources.getDrawable(R.drawable.ic_view)\n        binding.enterPasswordLayout.visibility = View.GONE\n        binding.password.inputType = InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD\n        // 重新修改确认按钮\n//        binding.password.imeOptions = EditorInfo.IME_ACTION_NEXT\n      } else {\n        binding.passwordLayout.endIconDrawable = resources.getDrawable(R.drawable.ic_view_off)\n        binding.enterPasswordLayout.visibility = View.VISIBLE\n        binding.password.setRawInputType(InputType.TYPE_TEXT_VARIATION_PASSWORD or InputType.TYPE_CLASS_TEXT)\n\n        // 重新修改确认按钮\n//        binding.password.imeOptions = EditorInfo.IME_ACTION_DONE\n      }\n      // 将光标移动到最后\n      binding.password.setSelection(binding.password.text!!.length)\n      binding.password.requestFocus()\n    }\n  }\n\n  private fun showPassLayout() {\n    val h = resources.getDimension(R.dimen.create_pass_key_h)\n      .toInt()\n    binding.passKeyLayoutWrap.visibility = View.VISIBLE\n    binding.passKeyLayout.layoutParams.height = 0\n    binding.passKeyLayout.visibility = View.VISIBLE\n    val anim = ValueAnimator.ofInt(0, h)\n    anim.addUpdateListener { animation ->\n      binding.passKeyLayout.layoutParams.height = animation.animatedValue as Int\n      binding.passKeyLayout.requestLayout()\n    }\n    anim.interpolator = LinearInterpolator()\n    anim.duration = 400\n    anim.start()\n  }\n\n  private fun hintPassLayout() {\n    val h = resources.getDimension(R.dimen.create_pass_key_h)\n      .toInt()\n    binding.passKeyLayoutWrap.visibility = View.VISIBLE\n    binding.passKeyLayout.layoutParams.height = 0\n    binding.passKeyLayout.visibility = View.VISIBLE\n    module.keyUri = null\n    val anim = ValueAnimator.ofInt(h, 0)\n    anim.addUpdateListener { animation ->\n      binding.passKeyLayout.layoutParams.height = animation.animatedValue as Int\n      binding.passKeyLayout.requestLayout()\n    }\n    anim.interpolator = LinearInterpolator()\n    anim.duration = 400\n    anim.start()\n    anim.addListener(object : AnimatorListenerAdapter() {\n      override fun onAnimationEnd(animation: Animator) {\n        super.onAnimationEnd(animation)\n        binding.passKeyLayout.visibility = View.GONE\n        binding.passKeyLayoutWrap.visibility = View.GONE\n      }\n    })\n  }\n\n  /**\n   * 获取密码，如果两次密码不一致，返回null\n   */\n  fun getPass(): String? {\n    val pass = binding.password.text.toString()\n      .trim()\n    val enterPass = binding.enterPassword.text.toString()\n      .trim()\n    if (TextUtils.isEmpty(pass)) {\n      HitUtil.toaskShort(getString(R.string.error_pass_null))\n      return null\n    }\n    // 如果没有显示密码，需要判断两次输入的密码是否一致\n    if (!isShowPass) {\n      if (TextUtils.isEmpty(enterPass)) {\n        HitUtil.toaskShort(getString(R.string.error_enter_pass_null))\n        binding.enterPassword.requestFocus()\n        KeepassAUtil.instance.toggleKeyBord(requireContext())\n        return null\n      }\n      if (!pass.equals(enterPass, false)) {\n        HitUtil.toaskShort(getString(R.string.error_pass_unfit))\n        return null\n      }\n    }\n\n    module.dbPass = pass\n\n    return module.dbPass\n  }\n\n  /**\n   * 获取key的路径\n   */\n  @Subscribe(threadMode = MAIN)\n  fun onKeyEvent(event: KeyPathEvent) {\n    module.keyUri = event.keyUri\n    module.keyName = event.keyName\n    binding.passKeyName.setText(module.keyName)\n  }\n\n  fun getShareElement(): View {\n    return binding.dbName\n  }\n\n  override fun onClick(\n    view: BubbleTextView,\n    index: Int\n  ) {\n    var msg = \"\"\n    when (view.id) {\n      R.id.encrypt_type -> {\n        msg = getString(R.string.help_pass_type)\n      }\n      R.id.pass_key -> {\n        msg = getString(R.string.help_pass_key)\n      }\n    }\n    Routerfit.create(DialogRouter::class.java).showMsgDialog(msgContent = msg, showCancelBt = false)\n  }\n\n  override fun onDestroy() {\n    super.onDestroy()\n    EventBusHelper.unReg(this)\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/create/CreateGroupDialog.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.view.create\n\nimport android.view.View\nimport androidx.lifecycle.ViewModelProvider\nimport com.alibaba.android.arouter.facade.annotation.Autowired\nimport com.alibaba.android.arouter.facade.annotation.Route\nimport com.alibaba.android.arouter.launcher.ARouter\nimport com.keepassdroid.database.PwGroupV4\nimport com.keepassdroid.database.PwIconCustom\nimport com.keepassdroid.database.PwIconStandard\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseApp\nimport com.lyy.keepassa.base.BaseDialog\nimport com.lyy.keepassa.databinding.DialogAddGroupBinding\nimport com.lyy.keepassa.util.HitUtil\nimport com.lyy.keepassa.util.IconUtil\nimport com.lyy.keepassa.view.create.entry.CreateEntryModule\nimport com.lyy.keepassa.view.icon.IconBottomSheetDialog\nimport com.lyy.keepassa.view.icon.IconItemCallback\n\n/**\n * 创建或编辑群组dialog\n */\n@Route(path = \"/dialog/createGroup\")\nclass CreateGroupDialog : BaseDialog<DialogAddGroupBinding>(), View.OnClickListener {\n\n  private var icon = PwIconStandard(48)\n  private var csIcon: PwIconCustom? = null\n  private lateinit var module: CreateEntryModule\n\n  @Autowired(name = \"parentGroup\")\n  @JvmField\n  var parentGroup: PwGroupV4 = BaseApp.KDB!!.pm.rootGroup as PwGroupV4\n\n  override fun setLayoutId(): Int {\n    return R.layout.dialog_add_group\n  }\n\n  override fun initData() {\n    super.initData()\n    ARouter.getInstance().inject(this)\n    module = ViewModelProvider(this).get(CreateEntryModule::class.java)\n    binding.groupNameLayout.setEndIconOnClickListener {\n      showIconDialog()\n    }\n    binding.enter.setOnClickListener(this)\n    binding.cancel.setOnClickListener(this)\n  }\n\n  private fun showIconDialog() {\n    val iconDialog = IconBottomSheetDialog()\n    iconDialog.setCallback(object : IconItemCallback {\n      override fun onDefaultIcon(defIcon: PwIconStandard) {\n        icon = defIcon\n        binding.groupNameLayout.endIconDrawable =\n          resources.getDrawable(IconUtil.getIconById(icon.iconId), requireContext().theme)\n        csIcon = PwIconCustom.ZERO\n      }\n\n      override fun onCustomIcon(customIcon: PwIconCustom) {\n        csIcon = customIcon\n        binding.groupNameLayout.endIconDrawable =\n          IconUtil.convertCustomIcon2Drawable(requireContext(), csIcon!!)\n      }\n    })\n    iconDialog.show(childFragmentManager, IconBottomSheetDialog::class.java.simpleName)\n  }\n\n  override fun onClick(v: View?) {\n    when (v!!.id) {\n      R.id.enter -> {\n        val title = binding.groupName.text.toString()\n          .trim()\n        if (title.isEmpty()) {\n          HitUtil.toaskShort(getString(R.string.error_group_name_null))\n          return\n        }\n        if (title.length > 16) {\n          HitUtil.toaskShort(requireContext().getString(R.string.title_too_long))\n          return\n        }\n        createGroup()\n      }\n      R.id.cancel -> {\n        dismiss()\n      }\n    }\n  }\n\n  /**\n   * 创建群组\n   */\n  private fun createGroup() {\n    module.createGroup(\n      binding.groupName.text.toString(),\n      parentGroup,\n      icon,\n      csIcon\n    ) {\n      HitUtil.toaskShort(\n        \"${BaseApp.APP.getString(R.string.create_group)}${\n          BaseApp.APP.getString(\n            R.string.success\n          )\n        }\"\n      )\n      dismiss()\n    }\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/create/CreatePassKeyDialog.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.view.create\n\nimport android.app.Activity\nimport android.content.Intent\nimport android.net.Uri\nimport android.os.Bundle\nimport android.view.View\nimport android.widget.Toast\nimport com.arialyy.frame.util.StringUtil\nimport com.google.android.material.bottomsheet.BottomSheetBehavior\nimport com.keepassdroid.utils.UriUtil\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseBottomSheetDialogFragment\nimport com.lyy.keepassa.databinding.DialogPassKeyBinding\nimport com.lyy.keepassa.event.KeyPathEvent\nimport com.lyy.keepassa.util.HitUtil\nimport com.lyy.keepassa.util.KeepassAUtil\nimport com.lyy.keepassa.util.PasswordBuildUtil\nimport com.lyy.keepassa.util.takePermission\nimport org.greenrobot.eventbus.EventBus\nimport timber.log.Timber\nimport java.io.FileOutputStream\nimport java.io.IOException\n\n/**\n * 创建key对话框\n */\nclass CreatePassKeyDialog : BaseBottomSheetDialogFragment<DialogPassKeyBinding>(),\n    View.OnClickListener {\n  private lateinit var behavior: BottomSheetBehavior<*>\n  private val openFileReqCode = 0xB1\n  private val createFileReqCode = 0xB2\n  override fun setLayoutId(): Int {\n    return R.layout.dialog_pass_key\n  }\n\n  override fun init(savedInstanceState: Bundle?) {\n    super.init(savedInstanceState)\n    behavior = BottomSheetBehavior.from(binding.content)\n    binding.close.setOnClickListener(this)\n    binding.item1.setOnClickListener(this)\n    binding.item2.setOnClickListener(this)\n    behavior.state = BottomSheetBehavior.STATE_EXPANDED\n  }\n\n  override fun onClick(v: View?) {\n    when (v!!.id) {\n      R.id.close -> dismiss()\n      R.id.item_1 -> {\n         KeepassAUtil.instance.openSysFileManager(this, \"*/*\", openFileReqCode)\n      }\n      R.id.item_2 -> {\n         KeepassAUtil.instance.createFile(\n            this, \"*/*\", \"${getString(R.string.app_name)}.passkey\", createFileReqCode\n        )\n      }\n    }\n  }\n\n  /**\n   * 将一个随机字符串写入密钥文件中\n   */\n  private fun writeData(uri: Uri?) {\n    val fos = requireContext().contentResolver.openOutputStream(uri!!) as FileOutputStream\n    try {\n      val str = PasswordBuildUtil.getInstance()\n          .addLowerChar()\n          .addNumChar()\n          .addMinus()\n          .addSymbolChar()\n          .builder(128)\n      fos.write(\n          StringUtil.keyToHashKey(str)\n              .toByteArray()\n      )\n      fos.flush()\n    } catch (e: IOException) {\n      Timber.e(e)\n    } finally {\n      fos.close()\n    }\n  }\n\n  override fun onActivityResult(\n    requestCode: Int,\n    resultCode: Int,\n    data: Intent?\n  ) {\n    super.onActivityResult(requestCode, resultCode, data)\n    if (resultCode == Activity.RESULT_OK && data != null && data.data != null) {\n      // 申请长期的uri权限\n      data.data?.takePermission()\n      when (requestCode) {\n        openFileReqCode -> {\n          EventBus.getDefault()\n              .post(\n                  KeyPathEvent(\n                      keyName = UriUtil.getFileNameFromUri(requireContext(), data.data),\n                      keyUri = data.data!!\n                  )\n              )\n        }\n        createFileReqCode -> {\n          writeData(data.data)\n          Toast.makeText(\n              context, getString(\n              R.string.create_pass_key_success,\n              UriUtil.getFileNameFromUri(requireContext(), data.data!!)\n          ), Toast.LENGTH_SHORT\n          )\n              .show()\n          EventBus.getDefault()\n              .post(\n                  KeyPathEvent(\n                      keyName = UriUtil.getFileNameFromUri(requireContext(), data.data),\n                      keyUri = data.data!!\n                  )\n              )\n        }\n        else -> {\n          Timber.e(\"未知请求码：$requestCode\")\n        }\n      }\n      dismiss()\n    } else {\n      HitUtil.toaskShort(\"${getString(R.string.invalid)} ${getString(R.string.key)}\")\n      Timber.e(\"选择密钥文件失败，data为空\")\n    }\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/create/GeneratePassActivity.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.view.create\n\nimport android.app.Activity\nimport android.content.Intent\nimport android.os.Bundle\nimport android.text.TextUtils\nimport android.widget.CompoundButton\nimport android.widget.CompoundButton.OnCheckedChangeListener\nimport androidx.core.widget.doAfterTextChanged\nimport com.google.android.material.slider.Slider\nimport com.google.android.material.slider.Slider.OnSliderTouchListener\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseActivity\nimport com.lyy.keepassa.databinding.ActivityGeneratePassNewBinding\nimport com.lyy.keepassa.util.ClipboardUtil\nimport com.lyy.keepassa.util.HitUtil\nimport com.lyy.keepassa.util.PasswordBuildUtil\nimport com.lyy.keepassa.util.doClick\n\n/**\n * 密码生成器\n */\nclass GeneratePassActivity : BaseActivity<ActivityGeneratePassNewBinding>(),\n  OnCheckedChangeListener {\n\n  private lateinit var generater: PasswordBuildUtil\n  private var passLen = 16\n  private var isUserInputPass = false\n\n  companion object {\n    const val DATA_PASS_WORD = \"DATA_PASS_WORD\"\n  }\n\n  override fun setLayoutId(): Int {\n    return R.layout.activity_generate_pass_new\n  }\n\n  override fun initData(savedInstanceState: Bundle?) {\n    super.initData(savedInstanceState)\n    toolbar.title = getString(R.string.pass_generater)\n    // binding.cancel.setOnClickListener {\n    //   finishAfterTransition()\n    // }\n    binding.edPassLen.setText(\"$passLen\")\n\n    binding.slider.addOnSliderTouchListener(object : OnSliderTouchListener {\n      override fun onStartTrackingTouch(slider: Slider) {\n      }\n\n      override fun onStopTrackingTouch(slider: Slider) {\n        isUserInputPass = false\n        passLen = slider.value.toInt()\n        binding.edPassLen.setText(\"$passLen\")\n      }\n    })\n\n    generater = PasswordBuildUtil.getInstance()\n    binding.scUAZ.setOnCheckedChangeListener(this)\n    binding.scLAZ.setOnCheckedChangeListener(this)\n    binding.scNum.setOnCheckedChangeListener(this)\n    binding.scCh.setOnCheckedChangeListener(this)\n    binding.scBracketChar.setOnCheckedChangeListener(this)\n    binding.scSpace.setOnCheckedChangeListener(this)\n\n    binding.scUAZ.isChecked = true\n    binding.scLAZ.isChecked = true\n    binding.scNum.isChecked = true\n    binding.scCh.isChecked = true\n    generatePass(passLen)\n\n    binding.ivRefresh.doClick {\n      if (checkParamsIsInvalid()) {\n        HitUtil.toaskShort(getString(R.string.error_genera_params))\n        return@doClick\n      }\n      generatePass(passLen)\n    }\n\n    binding.ivCopy.doClick {\n      if (checkParamsIsInvalid()) {\n        HitUtil.toaskShort(getString(R.string.error_genera_params))\n        return@doClick\n      }\n      ClipboardUtil.get()\n        .copyDataToClip(binding.edPass.text.toString())\n    }\n\n\n    binding.edPassLen.doAfterTextChanged { text ->\n      if (!TextUtils.isEmpty(text)) {\n        passLen = text.toString()\n          .toInt()\n        generatePass(passLen)\n      }\n    }\n\n    binding.slider.setLabelFormatter {\n      \"${it.toInt()}\"\n    }\n  }\n\n  override fun finishAfterTransition() {\n    val intent = Intent()\n    intent.putExtra(DATA_PASS_WORD, binding.edPass.text.toString().trim())\n    setResult(Activity.RESULT_OK, intent)\n    super.finishAfterTransition()\n  }\n\n  /**\n   * 检查密码生成条件\n   * @return true: 条件无效\n   */\n  private fun checkParamsIsInvalid(): Boolean {\n    return !binding.scUAZ.isChecked\n      && !binding.scLAZ.isChecked\n      && !binding.scNum.isChecked\n      && !binding.scCh.isChecked\n      && !binding.scBracketChar.isChecked\n      && !binding.scSpace.isChecked\n  }\n\n  /**\n   * 生产密码\n   * @param len 密码长度\n   */\n  private fun generatePass(len: Int): String {\n    if (checkParamsIsInvalid()) {\n      binding.edPass.setText(\"\")\n      return \"\"\n    }\n    generater.clear()\n    if (binding.scUAZ.isChecked) {\n      generater.addUpChar()\n    }\n    if (binding.scLAZ.isChecked) {\n      generater.addLowerChar()\n    }\n    if (binding.scNum.isChecked) {\n      generater.addNumChar()\n    }\n    if (binding.scCh.isChecked) {\n      generater.addMinus()\n      generater.addUnderline()\n      generater.addSymbolChar()\n    }\n    if (binding.scSpace.isChecked) {\n      generater.addSpaceChar()\n    }\n    if (binding.scBracketChar.isChecked) {\n      generater.addBracketChar()\n    }\n\n    val pass = generater.builder(len)\n    binding.edPass.setText(pass)\n    return pass\n  }\n\n  override fun onCheckedChanged(\n    buttonView: CompoundButton?,\n    isChecked: Boolean\n  ) {\n    generatePass(passLen)\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/create/PathTypeDialog.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.view.create\n\nimport android.os.Bundle\nimport android.view.View\nimport androidx.lifecycle.Observer\nimport androidx.lifecycle.ViewModelProvider\nimport androidx.recyclerview.widget.LinearLayoutManager\nimport androidx.recyclerview.widget.RecyclerView\nimport com.arialyy.frame.util.adapter.RvItemClickSupport\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseBottomSheetDialogFragment\nimport com.lyy.keepassa.databinding.DialogPathTypeBinding\nimport com.lyy.keepassa.entity.SimpleItemEntity\nimport com.lyy.keepassa.view.StorageType.AFS\nimport com.lyy.keepassa.view.StorageType.DROPBOX\nimport com.lyy.keepassa.view.StorageType.ONE_DRIVE\nimport com.lyy.keepassa.view.StorageType.WEBDAV\nimport com.lyy.keepassa.view.SimpleAdapter\n\n/**\n * 数据库路径选择\n * @param dbName 如果是webdav，该字段为文件的http url\n */\nclass PathTypeDialog(\n  private val dbName: String\n) : BaseBottomSheetDialogFragment<DialogPathTypeBinding>(), View.OnClickListener {\n\n  private lateinit var module: CreateDbModule\n\n  override fun setLayoutId(): Int {\n    return R.layout.dialog_path_type\n  }\n\n  override fun init(savedInstanceState: Bundle?) {\n    super.init(savedInstanceState)\n    module = ViewModelProvider(requireActivity())\n        .get(CreateDbModule::class.java)\n\n    val data: ArrayList<SimpleItemEntity> = ArrayList()\n    val adapter = SimpleAdapter(requireContext(), data)\n    binding.list.layoutManager = LinearLayoutManager(requireContext(), RecyclerView.VERTICAL, false)\n    binding.list.adapter = adapter\n    binding.content.post {\n      module.getDbOpenTypeData(requireContext())\n          .observe(this, Observer { items ->\n            data.addAll(items)\n            adapter.notifyDataSetChanged()\n          })\n    }\n    mRootView.setOnClickListener(this)\n    binding.close.setOnClickListener(this)\n    RvItemClickSupport.addTo(binding.list)\n        .setOnItemClickListener { _, position, _ ->\n          val item = data[position]\n          when (item.icon) {\n            R.drawable.ic_android -> {//使用系统文件管理器\n              module.storageType = AFS\n            }\n            R.drawable.ic_dropbox -> { // dropbox\n              module.storageType = DROPBOX\n            }\n            R.drawable.ic_http -> { // webDav\n              module.storageType = WEBDAV\n            }\n            R.drawable.ic_onedrive -> { // onedrive\n              module.storageType = ONE_DRIVE\n            }\n          }\n          dismiss()\n        }\n\n  }\n\n  override fun onClick(v: View?) {\n    when (v!!.id) {\n      mRootView.id, R.id.close -> dismiss()\n    }\n  }\n\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/create/auth/AFSAuthFlow.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.view.create.auth\n\nimport android.app.Activity\nimport android.content.Context\nimport android.content.Intent\nimport android.net.Uri\nimport com.keepassdroid.utils.UriUtil\nimport com.lyy.keepassa.event.DbPathEvent\nimport com.lyy.keepassa.util.KeepassAUtil\nimport com.lyy.keepassa.util.takePermission\nimport com.lyy.keepassa.view.StorageType.AFS\nimport com.lyy.keepassa.view.create.CreateDbFirstFragment\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 2021/2/25\n **/\nclass AFSAuthFlow : IAuthFlow {\n  private val PATH_REQUEST_CODE = 0xA1\n  private var context: Context? = null\n  private lateinit var authCallback: IAuthCallback\n  private lateinit var nextCallback: OnNextFinishCallback\n  private var dbUri: Uri? = null\n\n  override fun initContent(\n    context: Context,\n    callback: IAuthCallback\n  ) {\n    this.context = context\n    this.authCallback = callback\n  }\n\n  override fun startFlow() {\n    authCallback.callback(true)\n  }\n\n  override fun onResume() {\n  }\n\n  override fun doNext(\n    fragment: CreateDbFirstFragment,\n    dbName: String,\n    callback: OnNextFinishCallback\n  ) {\n    nextCallback = callback\n    if (dbUri == null) {\n      KeepassAUtil.instance.createFile(\n          fragment, \"*/*\", \"$dbName.kdbx\", PATH_REQUEST_CODE\n      )\n      return\n    }\n    nextCallback.onFinish(\n        DbPathEvent(\n            dbName = UriUtil.getFileNameFromUri(context, dbUri),\n            fileUri = dbUri,\n            storageType = AFS\n        )\n    )\n  }\n\n  override fun onDestroy() {\n  }\n\n  override fun onActivityResult(\n    requestCode: Int,\n    resultCode: Int,\n    data: Intent?\n  ) {\n    dbUri = data?.data\n    if (resultCode == Activity.RESULT_OK\n        && requestCode == PATH_REQUEST_CODE\n        && data != null\n        && data.data != null\n        && context != null\n    ) {\n\n      // 申请长期的uri权限\n      // 防止一个不可思议的空指针，data.data 有可能还是为空\n      data.data?.apply {\n        takePermission()\n        nextCallback.onFinish(\n            DbPathEvent(\n                dbName = UriUtil.getFileNameFromUri(context, this),\n                fileUri = this,\n                storageType = AFS\n            )\n        )\n      }\n    }\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/create/auth/DropboxAuthFlow.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.view.create.auth\n\nimport android.content.Context\nimport android.content.Intent\nimport android.text.Html\nimport android.text.TextUtils\nimport android.widget.Button\nimport androidx.lifecycle.Lifecycle\nimport androidx.lifecycle.OnLifecycleEvent\nimport com.arialyy.frame.router.Routerfit\nimport com.dropbox.core.android.Auth\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.event.DbPathEvent\nimport com.lyy.keepassa.router.DialogRouter\nimport com.lyy.keepassa.util.HitUtil\nimport com.lyy.keepassa.util.cloud.DbSynUtil\nimport com.lyy.keepassa.util.cloud.DropboxUtil\nimport com.lyy.keepassa.view.StorageType.DROPBOX\nimport com.lyy.keepassa.view.create.CreateDbFirstFragment\nimport com.lyy.keepassa.view.dialog.OnMsgBtClickListener\nimport timber.log.Timber\n\n/**\n * @Author laoyuyu\n * @Description default save db to root path (eg: \"/\")\n * @Date 2021/2/25\n **/\nclass DropboxAuthFlow : IAuthFlow {\n  private val TAG = javaClass.simpleName\n  private lateinit var context: Context\n  private var isNeedAuth = false\n  private lateinit var callback: IAuthCallback\n\n  override fun initContent(\n    context: Context,\n    callback: IAuthCallback\n  ) {\n    this.context = context\n    this.callback = callback\n  }\n\n  override fun onResume() {\n    Timber.d(\"onResume\")\n    if (!isNeedAuth || DropboxUtil.isAuthorized()) {\n      return\n    }\n    val token = Auth.getOAuth2Token()\n    if (!TextUtils.isEmpty(token)) {\n      DropboxUtil.saveToken(token)\n      HitUtil.toaskShort(\"dropbox ${context.getString(R.string.auth)}${context.getString(R.string.success)}\")\n      callback.callback(true)\n      return\n    }\n    HitUtil.toaskShort(\"dropbox ${context.getString(R.string.auth)}${context.getString(R.string.fail)}\")\n    callback.callback(false)\n  }\n\n  override fun doNext(\n    fragment: CreateDbFirstFragment,\n    dbName: String,\n    callback: OnNextFinishCallback\n  ) {\n    if (!DropboxUtil.isAuthorized()) {\n      authDropbox()\n      return\n    }\n    val name = \"$dbName.kdbx\"\n    callback.onFinish(\n      DbPathEvent(\n        dbName = name,\n        fileUri = DbSynUtil.getCloudDbTempPath(DROPBOX.name, name),\n        storageType = DROPBOX,\n        cloudDiskPath = \"/$name\"\n      )\n    )\n  }\n\n  override fun startFlow() {\n    isNeedAuth = true\n    if (!DropboxUtil.isAuthorized()) {\n      isNeedAuth = true\n      authDropbox()\n      return\n    }\n  }\n\n  /**\n   * 选择dropbox路径\n   * 只有dropbox为授权才显示该对话框\n   */\n  private fun authDropbox() {\n    Routerfit.create(DialogRouter::class.java)\n      .showMsgDialog(\n        msgContent = Html.fromHtml(context.getString(R.string.dropbox_msg)),\n        showCancelBt = false,\n        btnClickListener = object : OnMsgBtClickListener {\n          override fun onCover(v: Button) {\n          }\n\n          override fun onEnter(v: Button) {\n            Auth.startOAuth2Authentication(context, DropboxUtil.APP_KEY)\n          }\n\n          override fun onCancel(v: Button) {\n          }\n\n        }\n      )\n  }\n\n  @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)\n  override fun onDestroy() {\n    Timber.d(\"onDestroy\")\n  }\n\n  override fun onActivityResult(\n    requestCode: Int,\n    resultCode: Int,\n    data: Intent?\n  ) {\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/create/auth/IAuthFlow.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.view.create.auth\n\nimport android.content.Context\nimport android.content.Intent\nimport androidx.lifecycle.Lifecycle\nimport androidx.lifecycle.LifecycleObserver\nimport androidx.lifecycle.OnLifecycleEvent\nimport com.lyy.keepassa.event.DbPathEvent\nimport com.lyy.keepassa.view.StorageType\nimport com.lyy.keepassa.view.StorageType.AFS\nimport com.lyy.keepassa.view.StorageType.DROPBOX\nimport com.lyy.keepassa.view.StorageType.ONE_DRIVE\nimport com.lyy.keepassa.view.StorageType.WEBDAV\nimport com.lyy.keepassa.view.create.CreateDbFirstFragment\n\n/**\n * @Author laoyuyu\n * @Description cloud file create\n * @Date 2021/2/25\n **/\ninterface IAuthFlow : LifecycleObserver {\n\n  fun initContent(\n    context: Context,\n    callback: IAuthCallback\n  )\n\n  fun startFlow()\n\n  @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)\n  fun onResume()\n\n  /**\n   * 点下一步的处理事件\n   */\n  fun doNext(\n    fragment: CreateDbFirstFragment,\n    dbName: String,\n    callback: OnNextFinishCallback\n  )\n\n  fun onDestroy()\n\n  fun onActivityResult(\n    requestCode: Int,\n    resultCode: Int,\n    data: Intent?\n  )\n}\n\n/**\n * 验证回调\n */\ninterface IAuthCallback {\n  fun callback(success: Boolean)\n}\n\n/**\n * 完成选择云服务的回调\n */\ninterface OnNextFinishCallback {\n  fun onFinish(event: DbPathEvent)\n}\n\nobject AuthFlowFactory {\n\n  fun getAuthFlow(type: StorageType): IAuthFlow? = when (type) {\n    DROPBOX -> DropboxAuthFlow()\n    AFS -> AFSAuthFlow()\n    WEBDAV -> WebDavAuthFlow()\n    ONE_DRIVE -> OneDriveAuthFlow()\n    else -> null\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/create/auth/OneDriveAuthFlow.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.view.create.auth\n\nimport android.content.Context\nimport android.content.Intent\nimport com.arialyy.frame.router.Routerfit\nimport com.lyy.keepassa.event.DbPathEvent\nimport com.lyy.keepassa.router.DialogRouter\nimport com.lyy.keepassa.util.cloud.DbSynUtil\nimport com.lyy.keepassa.util.cloud.OneDriveUtil\nimport com.lyy.keepassa.view.StorageType.ONE_DRIVE\nimport com.lyy.keepassa.view.create.CreateDbFirstFragment\nimport timber.log.Timber\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 2021/4/26\n **/\nclass OneDriveAuthFlow : IAuthFlow {\n  private lateinit var context: Context\n  private lateinit var callback: IAuthCallback\n  private var loginCallback: OneDriveUtil.OnLoginCallback? = null\n  private var isAuthid = false\n  private val loadingDialog by lazy {\n    Routerfit.create(DialogRouter::class.java).getLoadingDialog()\n  }\n\n  override fun initContent(\n    context: Context,\n    callback: IAuthCallback\n  ) {\n    this.context = context\n    this.callback = callback\n  }\n\n  override fun startFlow() {\n    auth()\n  }\n\n  override fun onResume() {\n  }\n\n  override fun doNext(\n    fragment: CreateDbFirstFragment,\n    dbName: String,\n    callback: OnNextFinishCallback\n  ) {\n    if (!isAuthid) {\n      auth()\n      return\n    }\n    val name = \"$dbName.kdbx\"\n    callback.onFinish(\n      DbPathEvent(\n        dbName = name,\n        fileUri = DbSynUtil.getCloudDbTempPath(ONE_DRIVE.name, name),\n        storageType = ONE_DRIVE,\n        cloudDiskPath = \"/$name\"\n      )\n    )\n  }\n\n  private fun auth() {\n    if (isAuthid) {\n      Timber.d(\"已经完成授权\")\n      return\n    }\n    loadingDialog.show()\n    OneDriveUtil.initOneDrive {\n      if (it) {\n        OneDriveUtil.loadAccount()\n        return@initOneDrive\n      }\n      this.callback.callback(false)\n      loadingDialog.dismiss()\n    }\n    OneDriveUtil.loginCallback = object : OneDriveUtil.OnLoginCallback {\n      override fun callback(success: Boolean) {\n        isAuthid = success\n        this@OneDriveAuthFlow.callback.callback(success)\n        loadingDialog.dismiss()\n      }\n    }\n  }\n\n  override fun onDestroy() {\n    loginCallback = null\n  }\n\n  override fun onActivityResult(\n    requestCode: Int,\n    resultCode: Int,\n    data: Intent?\n  ) {\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/create/auth/WebDavAuthFlow.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.view.create.auth\n\nimport android.content.Context\nimport android.content.Intent\nimport android.graphics.Color\nimport android.text.Spannable\nimport android.text.SpannableString\nimport android.text.style.ForegroundColorSpan\nimport android.widget.Button\nimport com.arialyy.frame.router.Routerfit\nimport com.arialyy.frame.util.ResUtil\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseApp\nimport com.lyy.keepassa.entity.CloudServiceInfo\nimport com.lyy.keepassa.event.DbPathEvent\nimport com.lyy.keepassa.router.DialogRouter\nimport com.lyy.keepassa.util.QuickUnLockUtil\nimport com.lyy.keepassa.util.cloud.DbSynUtil\nimport com.lyy.keepassa.util.cloud.WebDavUtil\nimport com.lyy.keepassa.view.StorageType.WEBDAV\nimport com.lyy.keepassa.view.create.CreateDbFirstFragment\nimport com.lyy.keepassa.view.dialog.OnMsgBtClickListener\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.MainScope\nimport kotlinx.coroutines.flow.collectLatest\nimport kotlinx.coroutines.launch\nimport kotlinx.coroutines.withContext\nimport timber.log.Timber\n\n/**\n * @Author laoyuyu\n * @Description webdav auth flow\n * @Date 2021/2/25\n **/\nclass WebDavAuthFlow : IAuthFlow {\n  private var context: Context? = null\n  private lateinit var callback: IAuthCallback\n  private var webDavUri: String? = null\n  private var nextCallback: OnNextFinishCallback? = null\n  private var dbName: String? = null\n  private val scope = MainScope()\n  private var isLogin = false\n  private val loginDialog by lazy {\n    Routerfit.create(DialogRouter::class.java).getWebDavLoginDialog()\n  }\n  private val fileSelectDialog by lazy {\n    Routerfit.create(DialogRouter::class.java).getCloudFileListDialog(WEBDAV, true)\n  }\n\n  override fun initContent(\n    context: Context,\n    callback: IAuthCallback\n  ) {\n    this.context = context\n    this.callback = callback\n  }\n\n  override fun startFlow() {\n    // changeWebDav(false)\n    scope.launch {\n      loginDialog.webDavLoginFlow.collectLatest {\n        isLogin = it.loginSuccess\n        if (it.loginSuccess) {\n          fileSelectDialog.show()\n        }\n      }\n    }\n    loginDialog.show()\n\n    scope.launch {\n      fileSelectDialog.cloudFileSelectFlow.collectLatest {\n        if (it.storageType == WEBDAV) {\n          webDavUri = it.fileFullPath\n          callback.callback(true)\n        }\n      }\n    }\n  }\n\n  override fun onResume() {\n  }\n\n  override fun doNext(\n    fragment: CreateDbFirstFragment,\n    dbName: String,\n    callback: OnNextFinishCallback\n  ) {\n    this.dbName = \"${dbName}.kdbx\"\n    if (!isLogin) {\n      loginDialog.show()\n      return\n    }\n    if (webDavUri == null) {\n      fileSelectDialog.show()\n      return\n    }\n\n    nextCallback = callback\n\n    scope.launch {\n      val fileExist = withContext(Dispatchers.IO) {\n        return@withContext WebDavUtil.fileExists(\"${webDavUri!!}${this@WebDavAuthFlow.dbName}\")\n      }\n      if (fileExist) {\n        val content =\n          ResUtil.getString(\n            R.string.hint_cloud_file_already_exist,\n            this@WebDavAuthFlow.dbName!!\n          )\n        val color = ResUtil.getColor(R.color.red)\n        val ss = SpannableString(content)\n        ss.setSpan(\n          ForegroundColorSpan(color), 0, this@WebDavAuthFlow.dbName!!.length,\n          Spannable.SPAN_INCLUSIVE_EXCLUSIVE\n        )\n        Routerfit.create(DialogRouter::class.java).showMsgDialog(\n          msgTitle = ResUtil.getString(R.string.hint),\n          msgContent = ss,\n          btnClickListener = object : OnMsgBtClickListener{\n            override fun onCover(v: Button) {\n            }\n\n            override fun onEnter(v: Button) {\n              sendFinishEvent()\n            }\n\n            override fun onCancel(v: Button) {\n            }\n          }\n        )\n        return@launch\n      }\n      sendFinishEvent()\n    }\n  }\n\n  private fun sendFinishEvent() {\n    if (dbName == null) {\n      return\n    }\n    scope.launch {\n      saveWebDavServiceInfo(\"${webDavUri!!}${dbName}\", WebDavUtil.userName, WebDavUtil.password)\n      nextCallback?.onFinish(\n        DbPathEvent(\n          dbName = dbName!!,\n          storageType = WEBDAV,\n          fileUri = DbSynUtil.getCloudDbTempPath(WEBDAV.name, dbName!!),\n          cloudDiskPath = \"${webDavUri}${dbName}\"\n        )\n      )\n    }\n  }\n\n  private suspend fun saveWebDavServiceInfo(\n    uri: String,\n    userName: String,\n    pass: String\n  ) {\n    withContext(Dispatchers.IO) {\n      Timber.d(\"开始保存webDav登陆记录，uri = $uri\")\n      val dao = BaseApp.appDatabase.cloudServiceInfoDao()\n      var data = dao.queryServiceInfo(uri)\n      if (data == null) {\n        data = CloudServiceInfo(\n          userName = QuickUnLockUtil.encryptStr(userName),\n          password = QuickUnLockUtil.encryptStr(pass),\n          cloudPath = uri\n        )\n        dao.saveServiceInfo(data)\n      } else {\n        data.userName = QuickUnLockUtil.encryptStr(userName)\n        data.password = QuickUnLockUtil.encryptStr(pass)\n        dao.updateServiceInfo(data)\n      }\n    }\n  }\n\n  override fun onDestroy() {\n    context = null\n  }\n\n  override fun onActivityResult(\n    requestCode: Int,\n    resultCode: Int,\n    data: Intent?\n  ) {\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/create/entry/CardListHelper.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.view.create.entry\n\nimport android.animation.ObjectAnimator\nimport androidx.constraintlayout.widget.ConstraintLayout\nimport androidx.core.animation.doOnEnd\nimport androidx.recyclerview.widget.LinearLayoutManager\nimport com.lyy.keepassa.databinding.LayoutEntryCreateStrCardBinding\nimport com.lyy.keepassa.util.doClick\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 3:27 PM 2023/10/25\n **/\ninternal class CardListHelper(val binding: LayoutEntryCreateStrCardBinding) {\n\n  companion object {\n    private val ARROW_ANIM_DURATION = 100L\n  }\n\n  private var isExpand = false\n\n  fun handleList() {\n    binding.rvList.apply {\n      setHasFixedSize(true)\n      layoutManager = object : LinearLayoutManager(context) {\n        override fun canScrollVertically(): Boolean {\n          return false\n        }\n      }\n      isNestedScrollingEnabled = false\n    }\n    handleArrow()\n  }\n\n  private fun handleArrow() {\n    binding.vClick.doClick {\n      if (!isExpand) {\n        expand()\n        return@doClick\n      }\n      hind()\n    }\n  }\n\n  private fun hind() {\n    val anim = ObjectAnimator.ofFloat(binding.ivArrow, \"rotation\", 180f, 0f)\n    anim.duration = ARROW_ANIM_DURATION\n    anim.doOnEnd {\n      isExpand = false\n      binding.rvList.visibility = ConstraintLayout.GONE\n    }\n    anim.start()\n  }\n\n  /**\n   * 展开列表\n   */\n  private fun expand() {\n    val anim = ObjectAnimator.ofFloat(binding.ivArrow, \"rotation\", 0f, 180f)\n    anim.duration = ARROW_ANIM_DURATION\n    anim.doOnEnd {\n      isExpand = true\n      binding.rvList.visibility = ConstraintLayout.VISIBLE\n    }\n    anim.start()\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/create/entry/CreateEntryActivity.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.view.create.entry\n\nimport android.app.PendingIntent\nimport android.content.Context\nimport android.content.Intent\nimport android.content.IntentSender\nimport android.os.Bundle\nimport android.text.InputType\nimport android.view.View\nimport android.widget.ArrayAdapter\nimport androidx.activity.result.contract.ActivityResultContract\nimport androidx.activity.result.contract.ActivityResultContracts\nimport androidx.core.app.ActivityOptionsCompat\nimport androidx.core.view.isVisible\nimport androidx.lifecycle.ViewModelProvider\nimport androidx.lifecycle.lifecycleScope\nimport com.alibaba.android.arouter.facade.annotation.Autowired\nimport com.alibaba.android.arouter.facade.annotation.Route\nimport com.alibaba.android.arouter.launcher.ARouter\nimport com.arialyy.frame.router.Routerfit\nimport com.arialyy.frame.util.ResUtil\nimport com.keepassdroid.database.PwGroupId\nimport com.keepassdroid.database.PwIconCustom\nimport com.keepassdroid.database.PwIconStandard\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseActivity\nimport com.lyy.keepassa.databinding.ActivityEntryEditNewBinding\nimport com.lyy.keepassa.entity.AutoFillParam\nimport com.lyy.keepassa.entity.CommonState.DELETE\nimport com.lyy.keepassa.entity.GoogleOtpBean\nimport com.lyy.keepassa.entity.KeepassBean\nimport com.lyy.keepassa.entity.KeepassXcBean\nimport com.lyy.keepassa.entity.SimpleItemEntity\nimport com.lyy.keepassa.entity.TagBean\nimport com.lyy.keepassa.entity.TrayTotpBean\nimport com.lyy.keepassa.entity.toOtpStringMap\nimport com.lyy.keepassa.router.DialogRouter\nimport com.lyy.keepassa.util.IconUtil\nimport com.lyy.keepassa.util.KdbUtil\nimport com.lyy.keepassa.util.KeepassAUtil\nimport com.lyy.keepassa.util.doClick\nimport com.lyy.keepassa.util.hasTOTP\nimport com.lyy.keepassa.util.loadImg\nimport com.lyy.keepassa.util.takePermission\nimport com.lyy.keepassa.util.totp.OtpEnum\nimport com.lyy.keepassa.view.create.CreateCustomStrDialog\nimport com.lyy.keepassa.view.create.GeneratePassActivity\nimport com.lyy.keepassa.view.create.entry.CreateEnum.CREATE\nimport com.lyy.keepassa.view.create.entry.CreateEnum.MODIFY\nimport com.lyy.keepassa.view.dialog.AddMoreDialog\nimport com.lyy.keepassa.view.dialog.ChooseTagDialog\nimport com.lyy.keepassa.view.dialog.CreateTagDialog\nimport com.lyy.keepassa.view.dialog.TimeChangeDialog\nimport com.lyy.keepassa.view.dialog.otp.CreateOtpModule\nimport com.lyy.keepassa.view.dir.ChooseGroupActivity\nimport com.lyy.keepassa.view.icon.IconBottomSheetDialog\nimport com.lyy.keepassa.view.icon.IconItemCallback\nimport com.lyy.keepassa.view.launcher.LauncherActivity\nimport kotlinx.coroutines.flow.collectLatest\nimport kotlinx.coroutines.launch\nimport timber.log.Timber\nimport kotlin.math.abs\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 7:24 PM 2023/10/13\n **/\n@Route(path = \"/entry/create\")\nclass CreateEntryActivity : BaseActivity<ActivityEntryEditNewBinding>() {\n  companion object {\n    const val KEY_ENTRY = \"KEY_ENTRY\"\n\n    /**\n     * 类型，1：新建条目，2：利用模版新建条目，3：编辑条目\n     */\n    const val KEY_TYPE = \"KEY_IS_TYPE\"\n    const val IS_SHORTCUTS = \"isShortcuts\"\n    const val PARENT_GROUP_ID = \"PARENT_GROUP_ID\"\n\n    /**\n     * 数据库未解锁，保存数据时打开数据库，并保存\n     */\n    internal fun authAndSaveDb(\n      context: Context,\n      autoFillParam: AutoFillParam,\n    ): IntentSender {\n      val intent = Intent(context, CreateEntryActivity::class.java).also {\n        it.putExtra(LauncherActivity.KEY_AUTO_FILL_PARAM, autoFillParam)\n        it.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK\n      }\n      return PendingIntent.getActivity(\n        context,\n        1,\n        intent,\n        PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_IMMUTABLE\n      )\n        .intentSender\n    }\n  }\n\n  @Autowired(name = IS_SHORTCUTS)\n  @JvmField\n  var isShortcuts: Boolean = false\n\n  @Autowired(name = KEY_TYPE)\n  @JvmField\n  var createEnum: CreateEnum = CREATE\n\n  internal lateinit var module: CreateEntryModule\n  private lateinit var createHandler: ICreateHandler\n  private var isShowPass = false\n\n  private var addMoreDialog: AddMoreDialog? = null\n  private lateinit var addMoreData: ArrayList<SimpleItemEntity>\n\n  private val getFileLauncher =\n    registerForActivityResult(ActivityResultContracts.OpenDocument()) { uri ->\n      uri?.let {\n        it.takePermission()\n        module.addAttrFile(this, it)\n      }\n    }\n\n  /**\n   * 密码创建器\n   */\n  private val passGenerateLauncher =\n    registerForActivityResult(object : ActivityResultContract<String?, String>() {\n      override fun createIntent(context: Context, input: String?): Intent {\n        return Intent(context, GeneratePassActivity::class.java)\n      }\n\n      override fun parseResult(resultCode: Int, intent: Intent?): String {\n        return intent?.getStringExtra(GeneratePassActivity.DATA_PASS_WORD) ?: \"\"\n      }\n    }) {\n      if (it.isEmpty()) {\n        return@registerForActivityResult\n      }\n      binding.edPassword.setText(it)\n      binding.tvConfirm.setText(it)\n    }\n\n  /**\n   * 选择群组\n   */\n  private val chooseGroupLauncher =\n    registerForActivityResult(object : ActivityResultContract<String?, PwGroupId?>() {\n      override fun createIntent(context: Context, input: String?): Intent {\n        return Intent(context, ChooseGroupActivity::class.java).apply {\n          putExtra(ChooseGroupActivity.KEY_TYPE, ChooseGroupActivity.DATA_SELECT_GROUP)\n        }\n      }\n\n      override fun parseResult(resultCode: Int, intent: Intent?): PwGroupId? {\n        return intent?.getSerializableExtra(ChooseGroupActivity.DATA_PARENT) as PwGroupId?\n      }\n    }) {\n      if (it == null) {\n        Timber.d(\"pwGroupId is null\")\n        return@registerForActivityResult\n      }\n      module.updateEntryGroupIdAndSave(this, it)\n    }\n\n  fun launchGroupChoose() {\n    chooseGroupLauncher.launch(null, ActivityOptionsCompat.makeSceneTransitionAnimation(this))\n  }\n\n  override fun initData(savedInstanceState: Bundle?) {\n    super.initData(savedInstanceState)\n    ARouter.getInstance().inject(this)\n    module = ViewModelProvider(this)[CreateEntryModule::class.java]\n\n    createHandler = if (createEnum == MODIFY) {\n      ModifyEntryHandler(this)\n    } else {\n      CreateEntryHandler(this)\n    }\n\n    createHandler.bindData()\n\n    handleTopBarLayout()\n    handlePassLayout()\n    handleIconClick()\n    handlerAddMore()\n    handlerUserLayout()\n    handleTimeLayout()\n    handleTagLayout()\n    handleTotpLayout()\n    handleStr()\n    handleAttrFile()\n  }\n\n  private fun handleAttrFile() {\n    lifecycleScope.launch {\n      CreateEntryModule.attrFlow.collectLatest { bean ->\n\n        if (bean.state != DELETE) {\n          module.fileCacheMap[bean.key] = bean.file\n          binding.cardFile.isVisible = true\n          binding.cardFile.bindData(module.fileCacheMap)\n          return@collectLatest\n        }\n\n        module.fileCacheMap.remove(bean.key)\n\n        binding.cardFile.removeItem(bean.key)\n      }\n    }\n  }\n\n  private fun handleStr() {\n    lifecycleScope.launch {\n      CreateCustomStrDialog.CustomStrFlow.collectLatest { bean ->\n        if (bean == null) {\n          Timber.d(\"attr is null\")\n          return@collectLatest\n        }\n        checkAddMoreBtn()\n        if (bean.state != DELETE) {\n          module.strCacheMap[bean.key] = bean.str\n          binding.cardStr.isVisible = true\n          binding.cardStr.bindDate(module.strCacheMap)\n          return@collectLatest\n        }\n\n        module.strCacheMap.remove(bean.key)\n\n        if (!module.strCacheMap.hasTOTP()) {\n          binding.groupOtp.isVisible = false\n        }\n\n        binding.cardStr.removeItem(bean.key)\n      }\n    }\n  }\n\n  private fun handleTotpLayout() {\n    fun startOtp() {\n      binding.groupOtp.isVisible = true\n      KdbUtil.startAutoGetOtp(module.pwEntry, binding.pbRound, binding.edOtp)\n    }\n\n    lifecycleScope.launch {\n      CreateOtpModule.otpFlow.collectLatest {\n        val map = when (it.first) {\n          OtpEnum.TRAY_TOTP -> (it.second as? TrayTotpBean)?.toOtpStringMap()\n          OtpEnum.KEEPASSXC -> (it.second as? KeepassXcBean)?.toOtpStringMap()\n          OtpEnum.GOOGLE_OTP -> (it.second as? GoogleOtpBean)?.toOtpStringMap()\n          OtpEnum.KEEPASS -> (it.second as? KeepassBean)?.toOtpStringMap()\n        }\n        map?.let { strs ->\n          strs.forEach { kv ->\n            module.strCacheMap[kv.key] = kv.value\n          }\n          startOtp()\n          binding.cardStr.bindDate(module.strCacheMap)\n        }\n        checkAddMoreBtn()\n      }\n    }\n    if (module.strCacheMap.hasTOTP()) {\n      startOtp()\n    }\n    binding.edOtp.doClick {\n      if (module.strCacheMap.hasTOTP()) {\n        Routerfit.create(DialogRouter::class.java).showModifyOtpDialog(module.pwEntry.uuid)\n        return@doClick\n      }\n      Routerfit.create(DialogRouter::class.java)\n        .showCreateOtpDialog(module.pwEntry.title, module.pwEntry.username)\n    }\n  }\n\n  private fun handlerUserLayout() {\n    binding.edUser.threshold = 1 // 设置输入几个字符后开始出现提示 默认是2\n    binding.edUser.setOnFocusChangeListener { _, hasFocus ->\n      if (hasFocus) {\n        binding.edUser.showDropDown()\n      }\n    }\n\n    lifecycleScope.launch {\n      CreateEntryModule.userNameFlow.collectLatest {\n        if (it.isNullOrEmpty()) {\n          return@collectLatest\n        }\n        binding.edUser.setAdapter(\n          ArrayAdapter(\n            this@CreateEntryActivity,\n            R.layout.android_simple_dropdown_item_1line,\n            it\n          )\n        )\n      }\n    }\n\n    lifecycleScope.launch {\n      module.getUserNameCache()\n    }\n  }\n\n  private fun handleTimeLayout() {\n    binding.edLoseTime.doClick {\n      Routerfit.create(DialogRouter::class.java).showTimeChangeDialog()\n    }\n\n    lifecycleScope.launch {\n      TimeChangeDialog.timeFlow.collectLatest { event ->\n        if (event == null) {\n          return@collectLatest\n        }\n        val time = \"${event.year}/${event.month}/${event.dayOfMonth} ${event.hour}:${event.minute}\"\n        binding.edLoseTime.setText(time)\n        binding.tlLoseTime.visibility = View.VISIBLE\n        checkAddMoreBtn()\n      }\n    }\n  }\n\n  private fun handleTagLayout() {\n    binding.edTag.doClick {\n      Routerfit.create(DialogRouter::class.java).showChooseTagDialog(module.pwEntry)\n    }\n\n    lifecycleScope.launch {\n      ChooseTagDialog.chooseTagFlow.collectLatest { tagBeanList ->\n        val tagStrList = arrayListOf<String>()\n        tagBeanList.forEach {\n          tagStrList.add(it.tag)\n        }\n        val tags = tagStrList.joinToString(separator = \",\")\n        binding.edTag.setText(tags)\n        binding.tlTag.visibility = View.VISIBLE\n        checkAddMoreBtn()\n      }\n    }\n\n    lifecycleScope.launch {\n      CreateTagDialog.createTagFlow.collectLatest {\n        Routerfit.create(DialogRouter::class.java)\n          .showChooseTagDialog(module.pwEntry, if (it.isNullOrEmpty()) null else TagBean(it, true))\n      }\n    }\n  }\n\n  private fun handlerAddMore() {\n    binding.btnAddMore.doClick {\n      if (addMoreDialog == null) {\n        addMoreData = module.getMoreItem(this)\n        addMoreDialog = AddMoreDialog(addMoreData)\n        addMoreDialog!!.setOnItemClickListener(object : AddMoreDialog.OnItemClickListener {\n          override fun onItemClick(\n            position: Int,\n            item: SimpleItemEntity,\n            view: View\n          ) {\n            when (item.icon) {\n              R.drawable.ic_attr_str -> { // 自定义字段\n                Routerfit.create(DialogRouter::class.java).showCreateCustomDialog()\n              }\n\n              R.drawable.ic_attr_file -> { // file\n                changeFile()\n              }\n\n              R.drawable.ic_token_grey -> { // totp\n                Routerfit.create(DialogRouter::class.java)\n                  .showCreateOtpDialog(module.pwEntry.title, module.pwEntry.username)\n              }\n\n              R.drawable.ic_notice -> { // notice\n                binding.tlNote.visibility = View.VISIBLE\n                binding.tlNote.requestFocus()\n              }\n\n              R.drawable.ic_net -> { //url\n                binding.tlUrl.visibility = View.VISIBLE\n                binding.tlUrl.requestFocus()\n              }\n\n              R.drawable.ic_tag -> {\n                Routerfit.create(DialogRouter::class.java).showChooseTagDialog(module.pwEntry)\n              }\n\n              R.drawable.ic_lose_time -> {\n                Routerfit.create(DialogRouter::class.java).showTimeChangeDialog()\n              }\n\n            }\n            checkAddMoreBtn()\n            addMoreDialog!!.dismiss()\n          }\n        })\n      }\n      if (binding.tlLoseTime.isVisible) {\n        addMoreData.remove(addMoreData.find { it.icon == R.drawable.ic_lose_time })\n      }\n      if (binding.cardStr.isVisible) {\n        addMoreData.remove(addMoreData.find { it.icon == R.drawable.ic_attr_str })\n      }\n      if (binding.cardFile.isVisible) {\n        addMoreData.remove(addMoreData.find { it.icon == R.drawable.ic_attr_file })\n      }\n      if (module.pwEntry.hasTOTP()) {\n        addMoreData.remove(addMoreData.find { it.icon == R.drawable.ic_token_grey })\n      }\n      if (binding.tlTag.isVisible) {\n        addMoreData.remove(addMoreData.find { it.icon == R.drawable.ic_tag })\n      }\n      if (binding.tlNote.isVisible) {\n        addMoreData.remove(addMoreData.find { it.icon == R.drawable.ic_notice })\n      }\n      if (binding.tlUrl.isVisible) {\n        addMoreData.remove(addMoreData.find { it.icon == R.drawable.ic_net })\n      }\n      addMoreDialog!!.notifyData()\n      addMoreDialog!!.show(supportFragmentManager, \"add_more_dialog\")\n    }\n\n    checkAddMoreBtn()\n  }\n\n  private fun checkAddMoreBtn() {\n    if (binding.tlLoseTime.isVisible\n      && binding.cardStr.isVisible\n      && binding.cardFile.isVisible\n      && module.pwEntry.hasTOTP()\n      && binding.tlTag.isVisible\n      && binding.tlNote.isVisible\n      && binding.tlUrl.isVisible\n    ) {\n      binding.btnAddMore.visibility = View.GONE\n    } else {\n      binding.btnAddMore.visibility = View.VISIBLE\n    }\n  }\n\n  fun changeFile() {\n    getFileLauncher.launch(arrayOf(\"*/*\"))\n  }\n\n  /**\n   * 标题栏\n   */\n  private fun handleTopBarLayout() {\n    binding.topAppBar.title = createHandler.getTitle()\n    toolbar = binding.topAppBar\n    toolbar.setNavigationOnClickListener {\n      finishAfterTransition()\n    }\n    toolbar.inflateMenu(R.menu.menu_entry_edit)\n\n    toolbar.setOnMenuItemClickListener { item ->\n      if (KeepassAUtil.instance.isFastClick()) {\n        return@setOnMenuItemClickListener true\n      }\n      when (item.itemId) {\n        R.id.save -> {\n          createHandler.saveDb(module.pwEntry)\n        }\n\n        R.id.cancel -> {\n          finishAfterTransition()\n        }\n      }\n\n      true\n    }\n    binding.appBarLayout.addOnOffsetChangedListener { _, verticalOffset ->\n      if (verticalOffset == 0) {\n        binding.topAppBar.title = \"\"\n        return@addOnOffsetChangedListener\n      }\n      if (abs(verticalOffset) >= binding.appBarLayout.totalScrollRange) {\n        binding.topAppBar.title = createHandler.getTitle()\n        return@addOnOffsetChangedListener\n      }\n    }\n  }\n\n  private fun handleIconClick() {\n    binding.ivIcon.doClick {\n      val iconDialog = IconBottomSheetDialog()\n      iconDialog.setCallback(object : IconItemCallback {\n        override fun onDefaultIcon(defIcon: PwIconStandard) {\n          module.icon = defIcon\n          binding.ivIcon.loadImg(ResUtil.getDrawable(IconUtil.getIconById(module.icon.iconId)))\n          module.customIcon = PwIconCustom.ZERO\n        }\n\n        override fun onCustomIcon(customIcon: PwIconCustom) {\n          module.customIcon = customIcon\n          binding.ivIcon.loadImg(\n            IconUtil.convertCustomIcon2Drawable(\n              this@CreateEntryActivity,\n              module.customIcon!!\n            )\n          )\n        }\n      })\n      iconDialog.show(supportFragmentManager, IconBottomSheetDialog::class.java.simpleName)\n    }\n  }\n\n  /**\n   * 处理密码\n   */\n  private fun handlePassLayout() {\n    binding.tlPass.endIconDrawable = ResUtil.getDrawable(R.drawable.ic_view_off)\n\n    binding.tlPass.setEndIconOnClickListener {\n      isShowPass = !isShowPass\n      if (isShowPass) {\n        binding.tlPass.endIconDrawable = ResUtil.getDrawable(R.drawable.ic_view)\n        binding.tlConfirm.visibility = View.GONE\n        binding.edPassword.inputType = InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD\n      } else {\n        binding.tlPass.endIconDrawable =\n          ResUtil.getDrawable(R.drawable.ic_view_off)\n        binding.tlConfirm.visibility = View.VISIBLE\n        binding.edPassword.inputType =\n          InputType.TYPE_TEXT_VARIATION_PASSWORD or InputType.TYPE_CLASS_TEXT\n      }\n      // 将光标移动到最后\n      binding.edPassword.setSelection(binding.edPassword.text?.length ?: 0)\n      binding.edPassword.requestFocus()\n    }\n    binding.ivGeneratePw.setOnClickListener {\n      passGenerateLauncher.launch(null, ActivityOptionsCompat.makeSceneTransitionAnimation(this))\n    }\n  }\n\n  override fun setLayoutId(): Int {\n    return R.layout.activity_entry_edit_new\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/create/entry/CreateEntryHandler.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.view.create.entry\n\nimport android.view.View\nimport androidx.core.view.isVisible\nimport com.arialyy.frame.util.ResUtil\nimport com.keepassdroid.database.PwEntryV4\nimport com.keepassdroid.database.PwGroupId\nimport com.keepassdroid.database.PwGroupV4\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseApp\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 7:34 PM 2023/10/13\n **/\ninternal class CreateEntryHandler(val context: CreateEntryActivity) : ICreateHandler {\n\n  override fun bindData() {\n    val groupId =\n      context.intent.getSerializableExtra(CreateEntryActivity.PARENT_GROUP_ID) as? PwGroupId\n    val binding = context.binding\n    val group =\n      (if (groupId != null) BaseApp.KDB.pm.groups[groupId] else BaseApp.KDB.pm.rootGroup) as PwGroupV4\n    val entry = PwEntryV4(group, true, true)\n    context.module.pwEntry = entry\n    context.module.initCache()\n    binding.cardStr.visibility = View.GONE\n    binding.cardFile.visibility = View.GONE\n    binding.tlLoseTime.visibility = View.GONE\n    binding.tlUrl.visibility = View.GONE\n    binding.tlNote.visibility = View.GONE\n    binding.tlTag.visibility = View.GONE\n    binding.groupOtp.isVisible = false\n  }\n\n  override fun getTitle(): String {\n    return ResUtil.getString(R.string.create_entry)\n  }\n\n  override fun saveDb(pwEntryV4: PwEntryV4) {\n    checkAttr(context, pwEntryV4)\n    context.launchGroupChoose()\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/create/entry/CreateEntryModule.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.view.create.entry\n\nimport KDBAutoFillRepository\nimport android.content.Context\nimport android.graphics.Bitmap.CompressFormat.PNG\nimport android.net.Uri\nimport android.text.TextUtils\nimport androidx.lifecycle.lifecycleScope\nimport androidx.lifecycle.viewModelScope\nimport com.arialyy.frame.util.ResUtil\nimport com.keepassdroid.database.PwDatabaseV4\nimport com.keepassdroid.database.PwEntry\nimport com.keepassdroid.database.PwEntryV4\nimport com.keepassdroid.database.PwGroupId\nimport com.keepassdroid.database.PwGroupV4\nimport com.keepassdroid.database.PwIconCustom\nimport com.keepassdroid.database.PwIconStandard\nimport com.keepassdroid.database.security.ProtectedBinary\nimport com.keepassdroid.database.security.ProtectedString\nimport com.keepassdroid.utils.UriUtil\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseApp\nimport com.lyy.keepassa.base.BaseModule\nimport com.lyy.keepassa.entity.AutoFillParam\nimport com.lyy.keepassa.entity.CommonState.CREATE\nimport com.lyy.keepassa.entity.SimpleItemEntity\nimport com.lyy.keepassa.entity.TagBean\nimport com.lyy.keepassa.event.AttrFileEvent\nimport com.lyy.keepassa.util.HitUtil\nimport com.lyy.keepassa.util.IconUtil\nimport com.lyy.keepassa.util.KdbUtil\nimport com.lyy.keepassa.util.KpaUtil\nimport com.lyy.keepassa.util.getFileInfo\nimport com.lyy.keepassa.util.getRealUserName\nimport com.lyy.keepassa.util.hasNote\nimport com.lyy.keepassa.util.hasTOTP\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.flow.MutableSharedFlow\nimport kotlinx.coroutines.flow.MutableStateFlow\nimport kotlinx.coroutines.launch\nimport kotlinx.coroutines.withContext\nimport timber.log.Timber\nimport java.io.ByteArrayOutputStream\nimport java.util.UUID\n\n/**\n * 创建条目、群组的module\n */\nclass CreateEntryModule : BaseModule() {\n  companion object {\n    val attrFlow = MutableSharedFlow<AttrFileEvent>(0)\n    val userNameFlow = MutableStateFlow<List<String>?>(null)\n    private val userNameCache = arrayListOf<String>()\n  }\n\n  /**\n   * 已经选中的标签\n   */\n  var selectedTagBeanCache = mutableListOf<String>()\n  var customIcon: PwIconCustom? = null\n  var icon = PwIconStandard(0)\n  var autoFillParam: AutoFillParam? = null\n  var strCacheMap = hashMapOf<String, ProtectedString>()\n  var fileCacheMap = hashMapOf<String, ProtectedBinary>()\n  lateinit var pwEntry: PwEntryV4\n\n  fun updateEntryGroupIdAndSave(context: CreateEntryActivity, groupId: PwGroupId) {\n\n    viewModelScope.launch {\n      KpaUtil.kdbHandlerService.createEntry(pwEntry)\n      KpaUtil.kdbHandlerService.saveOnly(true) {\n        context.finishAfterTransition()\n      }\n    }\n  }\n\n  fun initCache() {\n    pwEntry.strings.forEach {\n      strCacheMap[it.key] = it.value\n    }\n    pwEntry.binaries.forEach {\n      fileCacheMap[it.key] = it.value\n    }\n    customIcon = pwEntry.customIcon ?: PwIconCustom.ZERO\n    icon = pwEntry.icon\n  }\n\n  fun cacheTag(tagList: List<TagBean>) {\n    selectedTagBeanCache.clear()\n    selectedTagBeanCache.addAll(tagList.filter { it.isSet }.map {\n      it.tag\n    })\n  }\n\n  /**\n   * 添加附件\n   */\n  fun addAttrFile(context: CreateEntryActivity, uri: Uri?) {\n    val rootView = context.rootView\n    if (uri == null) {\n      Timber.e(\"附件uri为空\")\n      HitUtil.snackShort(\n        rootView,\n        \"${ResUtil.getString(R.string.add_attr_file)}${ResUtil.getString(R.string.fail)}\"\n      )\n      return\n    }\n    val fileInfo = uri.getFileInfo(context)\n    if (TextUtils.isEmpty(fileInfo.first) || fileInfo.second == null) {\n      Timber.e(\"获取文件名失败\")\n      HitUtil.snackShort(\n        rootView,\n        \"${ResUtil.getString(R.string.add_attr_file)}${ResUtil.getString(R.string.fail)}\"\n      )\n      return\n    }\n    val fileName = fileInfo.first!!\n    val fileSize = fileInfo.second!!\n    if (fileSize >= 1024 * 1024 * 10) {\n      HitUtil.snackShort(rootView, ResUtil.getString(R.string.error_attr_file_too_large))\n      return\n    }\n    val pbf = ProtectedBinary(\n      false, UriUtil.getUriInputStream(context, uri)\n        .readBytes()\n    )\n    (BaseApp.KDB.pm as PwDatabaseV4).binPool.poolAdd(pbf)\n    context.lifecycleScope.launch {\n      attrFlow.emit(AttrFileEvent(CREATE, fileName, pbf))\n    }\n  }\n\n  /**\n   * Traverse database and get all userName\n   */\n  suspend fun getUserNameCache() {\n    if (userNameCache.isNotEmpty()) {\n      userNameFlow.emit(userNameCache)\n      return\n    }\n\n    val temp = hashSetOf<String>()\n\n    withContext(Dispatchers.IO) {\n      for (map in BaseApp.KDB.pm.entries) {\n        if (map.value.username.isNullOrEmpty()) {\n          continue\n        }\n        temp.add(map.value.getRealUserName())\n      }\n    }\n\n    userNameCache.addAll(temp)\n    userNameFlow.emit(userNameCache)\n  }\n\n  /**\n   * 自动填充进行保存数据时，搜索条目信息，如果条目不存在，新建条目\n   */\n  fun getEntryFromAutoFillSave(\n    context: Context,\n    apkPkgName: String,\n    userName: String?,\n    pass: String?\n  ): PwEntryV4 {\n    val listStorage = ArrayList<PwEntry>()\n    KdbUtil.searchEntriesByPackageName(apkPkgName, listStorage)\n    val entry: PwEntryV4\n    if (listStorage.isEmpty()) {\n      entry = PwEntryV4(BaseApp.KDB.pm.rootGroup as PwGroupV4)\n      val icon = IconUtil.getAppIcon(context, apkPkgName)\n      if (icon != null) {\n        val baos = ByteArrayOutputStream()\n        icon.compress(PNG, 100, baos)\n        val datas: ByteArray = baos.toByteArray()\n        val customIcon = PwIconCustom(UUID.randomUUID(), datas)\n        entry.customIcon = customIcon\n        (BaseApp.KDB.pm as PwDatabaseV4).putCustomIcons(customIcon)\n        entry.strings[\"KP2A_URL_1\"] = ProtectedString(false, \"androidapp://$apkPkgName\")\n      }\n\n      val appName = KDBAutoFillRepository.getAppName(context, apkPkgName)\n      entry.setTitle(appName ?: \"newEntry\", BaseApp.KDB.pm)\n      entry.icon = PwIconStandard(0)\n    } else {\n      entry = listStorage[0] as PwEntryV4\n      Timber.w(\"已存在含有【$apkPkgName】的条目，将更新条目\")\n    }\n    if (!userName.isNullOrEmpty()) {\n      entry.setUsername(userName, BaseApp.KDB.pm)\n    }\n    if (!pass.isNullOrEmpty()) {\n      entry.setPassword(pass, BaseApp.KDB.pm)\n    }\n    return entry\n  }\n\n  /**\n   * 创建群组\n   * @param groupName 群组名\n   * @param parentGroup 父群组\n   * @param icon 标准图标\n   * @param customIcon 自定义图标\n   */\n  fun createGroup(\n    groupName: String,\n    parentGroup: PwGroupV4,\n    icon: PwIconStandard,\n    customIcon: PwIconCustom?,\n    callback: (PwGroupV4) -> Unit\n  ) {\n    KpaUtil.kdbHandlerService.createGroup(groupName, icon, customIcon, parentGroup, callback)\n  }\n\n  /**\n   * 构建的更多选择项目\n   */\n  fun getMoreItem(context: Context): ArrayList<SimpleItemEntity> {\n    val list = ArrayList<SimpleItemEntity>()\n    val titles = context.resources.getStringArray(R.array.v4_add_mor_item)\n    val icons = context.resources.obtainTypedArray(R.array.v4_add_more_icon)\n    val len = titles.size - 1\n    for (i in 0..len) {\n      val item = SimpleItemEntity()\n      item.title = titles[i]\n      item.icon = icons.getResourceId(i, 0)\n      if (item.icon == R.drawable.ic_token_grey && pwEntry.hasTOTP()) {\n        Timber.d(\"Already used totp\")\n        continue\n      }\n      if (item.icon == R.drawable.ic_notice && pwEntry.hasNote()) {\n        Timber.d(\"Already used note\")\n        continue\n      }\n      list.add(item)\n    }\n    icons.recycle()\n    return list\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/create/entry/CreateEnum.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.view.create.entry\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 7:28 PM 2023/10/13\n **/\nenum class CreateEnum {\n  MODIFY,\n  CREATE\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/create/entry/CreateFileCard.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.view.create.entry\n\nimport android.content.Context\nimport android.util.AttributeSet\nimport android.view.Gravity\nimport android.view.LayoutInflater\nimport androidx.appcompat.widget.PopupMenu\nimport androidx.constraintlayout.widget.ConstraintLayout\nimport androidx.core.view.isVisible\nimport com.arialyy.frame.util.ResUtil\nimport com.keepassdroid.database.security.ProtectedBinary\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.AbsViewBindingAdapter\nimport com.lyy.keepassa.databinding.LayoutEntryAttachmentBinding\nimport com.lyy.keepassa.databinding.LayoutEntryCreateStrCardBinding\nimport com.lyy.keepassa.entity.CommonState.DELETE\nimport com.lyy.keepassa.event.AttrFileEvent\nimport com.lyy.keepassa.util.KdbUtil\nimport com.lyy.keepassa.util.KpaUtil\nimport com.lyy.keepassa.util.doOnItemClickListener\nimport com.lyy.keepassa.util.init\nimport kotlinx.coroutines.launch\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 3:26 PM 2023/10/25\n **/\nclass CreateFileCard(context: Context, attributeSet: AttributeSet) :\n  ConstraintLayout(context, attributeSet) {\n  companion object {\n    val ADD_MORE_DATA = Pair(\"addMore\", ProtectedBinary(false, null))\n  }\n\n  private val binding =\n    LayoutEntryCreateStrCardBinding.inflate(LayoutInflater.from(context), this, true)\n\n  private val fileList = mutableListOf<Pair<String, ProtectedBinary>>()\n  private val fileAdapter = FileAdapter()\n  private val helper = CardListHelper(binding)\n\n  init {\n    binding.tvTitle.text = ResUtil.getString(R.string.attachment)\n  }\n\n  fun bindData(fileMap: HashMap<String, ProtectedBinary>) {\n    fileList.clear()\n    fileMap.entries.forEach {\n      fileList.add(Pair(it.key, it.value))\n    }\n    fileList.add(ADD_MORE_DATA)\n    binding.rvList.apply {\n      this.adapter = this@CreateFileCard.fileAdapter\n      this@CreateFileCard.fileAdapter.setData(fileList)\n    }\n    binding.rvList.doOnItemClickListener { _, position, v ->\n      val data = fileList[position]\n      if (data == ADD_MORE_DATA) {\n        (context as CreateEntryActivity).changeFile()\n        return@doOnItemClickListener\n      }\n      PopupMenu(context, v, Gravity.END).init(R.menu.entry_modify_file_summary) {\n        when (it.itemId) {\n          R.id.remove_file -> {\n            KpaUtil.scope.launch {\n              CreateEntryModule.attrFlow.emit(AttrFileEvent(DELETE, data.first, data.second))\n            }\n          }\n\n          R.id.open_file -> {\n            KdbUtil.openFile(data.first, data.second)\n          }\n        }\n      }.show()\n    }\n    helper.handleList()\n  }\n\n  fun removeItem(key: String) {\n    fileList.find { it.first == key }?.let {\n      val pos = fileList.indexOf(it)\n      if (pos >= 0) {\n        fileList.removeAt(pos)\n        fileAdapter.notifyItemRemoved(pos)\n      }\n    }\n    if (fileList.size == 1) {\n      visibility = GONE\n    }\n  }\n\n  private class FileAdapter :\n    AbsViewBindingAdapter<Pair<String, ProtectedBinary>, LayoutEntryAttachmentBinding>() {\n\n    override fun bindData(\n      binding: LayoutEntryAttachmentBinding,\n      item: Pair<String, ProtectedBinary>\n    ) {\n      if (item == ADD_MORE_DATA) {\n        binding.value.isVisible = false\n        binding.addMore.isVisible = true\n        binding.addMore.text = ResUtil.getString(R.string.add_attr_file)\n        return\n      }\n      binding.value.isVisible = true\n      binding.addMore.isVisible = false\n      binding.value.text = item.first\n    }\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/create/entry/CreateStrCard.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\npackage com.lyy.keepassa.view.create.entry\n\nimport android.content.Context\nimport android.os.Build\nimport android.util.AttributeSet\nimport android.view.Gravity\nimport android.view.LayoutInflater\nimport androidx.appcompat.widget.PopupMenu\nimport androidx.constraintlayout.widget.ConstraintLayout\nimport androidx.core.view.isGone\nimport com.arialyy.frame.router.Routerfit\nimport com.keepassdroid.database.security.ProtectedString\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.AbsViewBindingAdapter\nimport com.lyy.keepassa.databinding.LayoutEntryCreateStrCardBinding\nimport com.lyy.keepassa.databinding.LayoutEntryStrBinding\nimport com.lyy.keepassa.entity.CommonState.DELETE\nimport com.lyy.keepassa.event.AttrStrEvent\nimport com.lyy.keepassa.router.DialogRouter\nimport com.lyy.keepassa.util.KdbUtil\nimport com.lyy.keepassa.util.KpaUtil\nimport com.lyy.keepassa.util.doOnItemClickListener\nimport com.lyy.keepassa.util.init\nimport com.lyy.keepassa.view.create.CreateCustomStrDialog\nimport kotlinx.coroutines.launch\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 3:12 PM 2023/10/12\n **/\nclass CreateStrCard(context: Context, attributeSet: AttributeSet) :\n  ConstraintLayout(context, attributeSet) {\n  private val binding =\n    LayoutEntryCreateStrCardBinding.inflate(LayoutInflater.from(context), this, true)\n  private val strList = mutableListOf<Pair<String, ProtectedString>>()\n\n  companion object {\n    val ADD_MORE_DATA = Pair(\"addMore\", ProtectedString(false, \"addMore\"))\n  }\n\n  private val helper = CardListHelper(binding)\n\n  private val adapter = StrAdapter()\n\n  fun bindDate(strMap: Map<String, ProtectedString>) {\n    handleList(strMap)\n  }\n\n  private fun handleList(strMap: Map<String, ProtectedString>) {\n    strList.clear()\n    visibility = VISIBLE\n    KdbUtil.filterCustomStr(strMap).entries.forEach {\n      strList.add(Pair(it.key, it.value))\n    }\n    strList.add(ADD_MORE_DATA)\n    binding.rvList.adapter = adapter\n    adapter.setData(strList)\n\n    binding.rvList.doOnItemClickListener { _, position, v ->\n      val data = strList[position]\n      if (data == ADD_MORE_DATA) {\n        Routerfit.create(DialogRouter::class.java).showCreateCustomDialog()\n        return@doOnItemClickListener\n      }\n      PopupMenu(context, v, Gravity.END).init(R.menu.entry_modify_str_summary) {\n        when (it.itemId) {\n          R.id.remove_str -> {\n            KpaUtil.scope.launch {\n              CreateCustomStrDialog.CustomStrFlow.emit(\n                AttrStrEvent(\n                  DELETE,\n                  data.first,\n                  data.second\n                )\n              )\n            }\n          }\n\n          R.id.modify_text -> {\n            Routerfit.create(DialogRouter::class.java)\n              .showCreateCustomDialog(position, data.first, data.second)\n          }\n        }\n      }.show()\n    }\n    helper.handleList()\n  }\n\n  fun removeItem(key: String) {\n    strList.find { it.first == key }?.let {\n      val pos = strList.indexOf(it)\n      if (pos >= 0) {\n        strList.removeAt(pos)\n        adapter.notifyItemRemoved(pos)\n      }\n    }\n    if (strList.size == 1) {\n      visibility = GONE\n    }\n  }\n\n  private class StrAdapter :\n    AbsViewBindingAdapter<Pair<String, ProtectedString>, LayoutEntryStrBinding>() {\n\n    private fun showContent(binding: LayoutEntryStrBinding, show: Boolean) {\n      binding.title.isGone = !show\n      binding.value.isGone = !show\n      binding.addMore.isGone = show\n    }\n\n    override fun bindData(binding: LayoutEntryStrBinding, item: Pair<String, ProtectedString>) {\n      if (item == ADD_MORE_DATA) {\n        showContent(binding, false)\n        return\n      }\n      showContent(binding, true)\n      binding.title.text = item.first\n\n      val tvValue = binding.value\n      KpaUtil.handleShowPass(tvValue, !item.second.isProtected)\n      if (item.second.toString().isEmpty()) {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {\n          tvValue.typeface = context.resources.getFont(R.font.roboto_thinitalic)\n        }\n        tvValue.text = \"null\"\n        return\n      }\n      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {\n        tvValue.typeface = context.resources.getFont(R.font.roboto_regular)\n      }\n      tvValue.text = item.second.toString()\n    }\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/create/entry/ICreateHandler.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.view.create.entry\n\nimport android.text.Html\nimport androidx.core.view.isVisible\nimport com.arialyy.frame.util.ResUtil\nimport com.blankj.utilcode.util.ToastUtils\nimport com.keepassdroid.database.PwEntryV4\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseApp\nimport com.lyy.keepassa.base.KeyConstance\nimport com.lyy.keepassa.util.KdbUtil\nimport org.joda.time.DateTime\nimport org.joda.time.format.DateTimeFormat\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 7:32 PM 2023/10/13\n **/\ninterface ICreateHandler {\n\n  companion object {\n    private val PROTECTED_KEY = arrayListOf<String>().apply {\n      add(PwEntryV4.STR_TITLE)\n      add(PwEntryV4.STR_USERNAME)\n      add(PwEntryV4.STR_PASSWORD)\n      add(PwEntryV4.STR_URL)\n      add(PwEntryV4.STR_NOTES)\n    }\n  }\n\n  fun bindData()\n\n  fun getTitle(): String\n\n  fun saveDb(pwEntryV4: PwEntryV4)\n\n  fun checkAttr(context: CreateEntryActivity, pwEntryV4: PwEntryV4) {\n    val binding = context.binding\n    val title = binding.title.text.toString()\n    val db = BaseApp.KDB.pm\n    pwEntryV4.setTitle(title.ifEmpty { \"unknown\" }, db)\n    pwEntryV4.setUsername(binding.edUser.text.toString(), db)\n    pwEntryV4.setPassword(binding.edPassword.text.toString(), db)\n    pwEntryV4.setUrl(binding.edUrl.text.toString(), db)\n    pwEntryV4.tags = binding.edTag.text.toString()\n\n    val loseTime = binding.edLoseTime.text.toString()\n    if (loseTime.isNotEmpty() && binding.tlLoseTime.isVisible) {\n      pwEntryV4.expiryTime =\n        DateTime.parse(loseTime, DateTimeFormat.forPattern(KdbUtil.DATE_FORMAT)).toDate()\n    }\n    if (binding.tlNote.isVisible) {\n      pwEntryV4.setNotes(binding.edNote.text.toString(), db)\n    }\n\n    if (binding.cardStr.isVisible) {\n      val temp = arrayListOf<String>()\n      pwEntryV4.strings.forEach {\n        if (it.key in PROTECTED_KEY) {\n          return@forEach\n        }\n        temp.add(it.key)\n      }\n      temp.forEach {\n        pwEntryV4.strings.remove(it)\n      }\n\n      context.module.strCacheMap.filter { it.key != KeyConstance.TOTP && it.key !in PROTECTED_KEY }\n        .forEach {\n          pwEntryV4.strings[it.key] = it.value\n        }\n    }\n\n    if (binding.cardFile.isVisible && checkEntry(pwEntryV4)) {\n      pwEntryV4.binaries.clear()\n      context.module.fileCacheMap.forEach {\n        pwEntryV4.binaries[it.key] = it.value\n      }\n    }\n    pwEntryV4.customIcon = context.module.customIcon\n    pwEntryV4.icon = context.module.icon\n  }\n\n  private fun checkEntry(pwEntry: PwEntryV4): Boolean {\n    pwEntry.binaries.forEach {\n      if (it.value == null || it.value.length() == 0) {\n        ToastUtils.showLong(Html.fromHtml(ResUtil.getString(R.string.error_file, it.key)))\n        return false\n      }\n    }\n    return true\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/create/entry/ModifyEntryHandler.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.view.create.entry\n\nimport android.view.View\nimport androidx.lifecycle.lifecycleScope\nimport com.arialyy.frame.util.ResUtil\nimport com.keepassdroid.database.PwEntryV4\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseApp\nimport com.lyy.keepassa.util.IconUtil\nimport com.lyy.keepassa.util.KdbUtil\nimport com.lyy.keepassa.util.KpaUtil\nimport com.lyy.keepassa.util.getRealPass\nimport com.lyy.keepassa.util.getRealTitle\nimport com.lyy.keepassa.util.getRealUserName\nimport com.lyy.keepassa.util.loadImg\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.launch\nimport kotlinx.coroutines.withContext\nimport org.joda.time.DateTime\nimport java.util.UUID\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 7:34 PM 2023/10/13\n **/\ninternal class ModifyEntryHandler(val context: CreateEntryActivity) : ICreateHandler {\n\n  override fun bindData() {\n    val entryId = context.intent.getSerializableExtra(CreateEntryActivity.KEY_ENTRY) as UUID\n    context.module.pwEntry = BaseApp.KDB!!.pm.entries[entryId] as PwEntryV4\n    context.module.initCache()\n    val entry = context.module.pwEntry\n    val binding = context.binding\n\n    context.lifecycleScope.launch(Dispatchers.IO) {\n      val name = entry.getRealUserName()\n      val pass = entry.getRealPass()\n      val title = entry.getRealTitle()\n      withContext(Dispatchers.Main) {\n        binding.title.setText(title)\n        binding.edUser.setText(name)\n        binding.edPassword.setText(pass)\n        binding.tvConfirm.setText(pass)\n      }\n    }\n\n    if (entry.notes.isNotEmpty()) {\n      binding.tlNote.visibility = View.VISIBLE\n      binding.edNote.setText(entry.notes.toString())\n    }\n    if (entry.url.isNotEmpty()) {\n      binding.tlUrl.visibility = View.VISIBLE\n      binding.edUrl.setText(entry.url)\n    }\n    handleIcon(context, entry)\n    binding.cardStr.apply {\n      visibility = if (entry.strings.isNotEmpty()) View.VISIBLE else View.GONE\n    }\n    binding.cardStr.bindDate(context.module.strCacheMap)\n\n    binding.cardFile.apply {\n      visibility = if (entry.binaries.isNotEmpty()) View.VISIBLE else View.GONE\n\n    }\n    binding.cardFile.bindData(context.module.fileCacheMap)\n    binding.tlTag.apply {\n      visibility = if (entry.tags.isNotEmpty()) View.VISIBLE else View.GONE\n      binding.edTag.setText(entry.tags)\n    }\n\n    if (entry.expiryTime != null) {\n      binding.edLoseTime.setText(DateTime(entry.expiryTime).toString(KdbUtil.DATE_FORMAT))\n      binding.tlLoseTime.visibility = View.VISIBLE\n    }\n  }\n\n  private fun handleIcon(ac: CreateEntryActivity, pwEntry: PwEntryV4) {\n    ac.module.icon = pwEntry.icon\n    ac.binding.ivIcon.loadImg(IconUtil.getEntryIconDrawable(ac, pwEntry, zoomIcon = true))\n  }\n\n  override fun getTitle(): String {\n    return ResUtil.getString(R.string.edit)\n  }\n\n  override fun saveDb(pwEntryV4: PwEntryV4) {\n    checkAttr(context, pwEntryV4)\n    context.lifecycleScope.launch {\n      KpaUtil.kdbHandlerService.saveOnly(true) {\n        context.finishAfterTransition()\n      }\n    }\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/detail/AppIconAdapter.kt",
    "content": "package com.lyy.keepassa.view.detail\n\nimport com.blankj.utilcode.util.AppUtils\nimport com.lyy.keepassa.base.AbsViewBindingAdapter\nimport com.lyy.keepassa.databinding.ItemAppIconBinding\nimport com.lyy.keepassa.util.loadImg\n\nclass AppIconAdapter : AbsViewBindingAdapter<String, ItemAppIconBinding>() {\n  override fun bindData(binding: ItemAppIconBinding, item: String) {\n    binding.ivIcon.apply {\n      // val drawable = ResUtil.getDrawable(R.drawable.ic_app)\n      val drawable = AppUtils.getAppIcon(item)\n      loadImg(drawable)\n    }\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/detail/AppIconLayoutManager.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.view.detail\n\nimport androidx.recyclerview.widget.RecyclerView\nimport androidx.recyclerview.widget.RecyclerView.LayoutManager\nimport androidx.recyclerview.widget.RecyclerView.LayoutParams\n\nclass AppIconLayoutManager(private val offset: Int) : LayoutManager() {\n\n  override fun generateDefaultLayoutParams(): LayoutParams {\n    return LayoutParams(\n      LayoutParams.WRAP_CONTENT,\n      LayoutParams.MATCH_PARENT\n    )\n  }\n\n  override fun onMeasure(\n    recycler: RecyclerView.Recycler,\n    state: RecyclerView.State,\n    widthSpec: Int,\n    heightSpec: Int\n  ) {\n    if (state.itemCount == 0) {\n      super.onMeasure(recycler, state, widthSpec, heightSpec)\n      return\n    }\n    if (state.isPreLayout) return\n\n    //假定每个item的宽高一直，所以用第一个view计算宽高，\n    //这种方式可能不太好\n    val itemView = recycler.getViewForPosition(0)\n    addView(itemView)\n    //这里不能用measureChild方法，具体看内部源码实现，内部getWidth默认为0\n//        measureChildWithMargins(itemView, 0, 0)\n    itemView.measure(widthSpec, heightSpec)\n    val mItemWidth = getDecoratedMeasuredWidth(itemView)\n    val mItemHeight = getDecoratedMeasuredHeight(itemView)\n    //回收这个View\n    detachAndScrapView(itemView, recycler)\n\n    //设置宽高\n    setMeasuredDimension(mItemWidth, mItemHeight)\n\n  }\n\n  override fun onLayoutChildren(recycler: RecyclerView.Recycler, state: RecyclerView.State) {\n\n    //轻量级的将view移除屏幕\n    detachAndScrapAttachedViews(recycler)\n    //开始填充view\n\n    var totalSpace = width - paddingRight\n    var currentPosition = 0\n\n    var left = 0\n    val top = 0\n    var right = 0\n    var bottom = 0\n    //模仿LinearLayoutManager的写法，当可用距离足够和要填充\n    //的itemView的position在合法范围内才填充View\n    while (totalSpace > 0 && currentPosition < state.itemCount) {\n      val view = recycler.getViewForPosition(currentPosition)\n      addView(view)\n      measureChild(view, 0, 0)\n\n      right = left + getDecoratedMeasuredWidth(view)\n      bottom = top + getDecoratedMeasuredHeight(view)\n      layoutDecorated(view, left, top, right, bottom)\n\n      currentPosition++\n      left += getDecoratedMeasuredWidth(view) - offset\n      //关键点\n      totalSpace -= getDecoratedMeasuredWidth(view) - offset\n    }\n    //layout完成后输出相关信息\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/detail/EntryDetailActivityNew.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.view.detail\n\nimport android.graphics.drawable.BitmapDrawable\nimport android.os.Bundle\nimport android.view.View\nimport androidx.activity.result.contract.ActivityResultContracts.CreateDocument\nimport androidx.lifecycle.ViewModelProvider\nimport androidx.lifecycle.lifecycleScope\nimport com.alibaba.android.arouter.facade.annotation.Autowired\nimport com.alibaba.android.arouter.facade.annotation.Route\nimport com.alibaba.android.arouter.launcher.ARouter\nimport com.arialyy.frame.router.Routerfit\nimport com.arialyy.frame.util.ResUtil\nimport com.blankj.utilcode.util.ToastUtils\nimport com.keepassdroid.database.PwEntryV4\nimport com.keepassdroid.database.security.ProtectedBinary\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseActivity\nimport com.lyy.keepassa.base.BaseApp\nimport com.lyy.keepassa.databinding.ActivityEntryDetailNewBinding\nimport com.lyy.keepassa.router.ActivityRouter\nimport com.lyy.keepassa.util.IconUtil\nimport com.lyy.keepassa.util.KeepassAUtil\nimport com.lyy.keepassa.util.KpaUtil\nimport com.lyy.keepassa.util.copyPassword\nimport com.lyy.keepassa.util.copyTotp\nimport com.lyy.keepassa.util.copyUserName\nimport com.lyy.keepassa.util.doClick\nimport com.lyy.keepassa.util.hasTOTP\nimport com.lyy.keepassa.util.isCollectioned\nimport com.lyy.keepassa.util.takePermission\nimport com.lyy.keepassa.view.detail.card.EntryFileCard\nimport com.lyy.keepassa.widget.toPx\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.flow.collectLatest\nimport kotlinx.coroutines.launch\nimport timber.log.Timber\nimport java.nio.ByteBuffer\nimport java.nio.channels.Channels\nimport java.util.Locale\nimport java.util.UUID\nimport kotlin.math.abs\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 5:24 PM 2023/9/26\n **/\n@Route(path = \"/entry/detail\")\nclass EntryDetailActivityNew : BaseActivity<ActivityEntryDetailNewBinding>() {\n  companion object {\n    const val KEY_ENTRY_ID = \"KEY_ENTRY_ID\"\n  }\n\n  private lateinit var module: EntryDetailModule\n  private lateinit var pwEntry: PwEntryV4\n  private var isInRecycleBin = false\n  private var fileSaveCache: ProtectedBinary? = null\n\n  private val saveFile = registerForActivityResult(CreateDocument(\"*/*\")) {\n    if (it == null) {\n      Timber.e(\"uri为空\")\n      return@registerForActivityResult\n    }\n    if (fileSaveCache == null) {\n      Timber.e(\"文件为空\")\n      return@registerForActivityResult\n    }\n    it.takePermission()\n    lifecycleScope.launch(Dispatchers.IO) {\n      contentResolver.openOutputStream(it).use { out ->\n        val iChannel = Channels.newChannel(fileSaveCache!!.data)\n        val oChannel = Channels.newChannel(out)\n        val buffer = ByteBuffer.allocateDirect(16 * 1024)\n        while (iChannel.read(buffer) != -1) {\n          // 切换为读状态\n          buffer.flip()\n          // 保证缓冲区的数据全部写入\n          while (buffer.hasRemaining()) {\n            oChannel.write(buffer)\n          }\n          buffer.clear()\n        }\n        iChannel.close()\n        oChannel.close()\n        ToastUtils.showLong(ResUtil.getString(R.string.file_save_success))\n      }\n    }\n  }\n\n  @Autowired(name = KEY_ENTRY_ID)\n  lateinit var uuid: UUID\n\n  override fun setLayoutId(): Int {\n    return R.layout.activity_entry_detail_new\n  }\n\n  override fun initData(savedInstanceState: Bundle?) {\n    super.initData(savedInstanceState)\n    ARouter.getInstance().inject(this)\n    module = ViewModelProvider(this)[EntryDetailModule::class.java]\n    pwEntry = (BaseApp.KDB!!.pm.entries[uuid] as PwEntryV4?)!!\n    module.initEntry(pwEntry)\n    if (BaseApp.isV4 && pwEntry.parent == BaseApp.KDB!!.pm.recycleBin) {\n      isInRecycleBin = true\n    }\n    setTopBar()\n    listenerSaveFile()\n    module.saveRecord()\n  }\n\n  private fun listenerSaveFile() {\n    lifecycleScope.launch {\n      EntryFileCard.SAVE_FILE_FLOW.collectLatest {\n        saveFile.launch(it.first)\n        fileSaveCache = it.second\n      }\n    }\n  }\n\n  override fun onStart() {\n    super.onStart()\n    bindData()\n  }\n\n  private fun bindData() {\n    setIcon()\n    // 处理过期\n    KpaUtil.handleExpire(binding.tvTitle, pwEntry)\n    binding.tvTitle.text = pwEntry.title\n    binding.topAppBar.title = pwEntry.title\n    binding.cardBaseInfo.bindData(pwEntry)\n    binding.cardNote.bindData(pwEntry)\n    binding.cardStr.bindData(pwEntry)\n    binding.cardAtta.bindData(pwEntry)\n    binding.cardTag.bindData(pwEntry)\n  }\n\n  /**\n   * 标题栏\n   */\n  private fun setTopBar() {\n    toolbar = binding.topAppBar\n    toolbar.setNavigationOnClickListener {\n      finishAfterTransition()\n    }\n    toolbar.inflateMenu(R.menu.menu_entry_detail)\n    toolbar.menu.findItem(R.id.collect)\n      .setIcon(if (!pwEntry.isCollectioned()) R.drawable.ic_star_outline else R.drawable.ic_star)\n\n    toolbar.setOnMenuItemClickListener { item ->\n      if (KeepassAUtil.instance.isFastClick()) {\n        return@setOnMenuItemClickListener true\n      }\n      when (item.itemId) {\n        R.id.edit -> {\n          Routerfit.create(ActivityRouter::class.java, this).toEditEntryActivity(pwEntry.uuid)\n        }\n\n        R.id.collect -> {\n          KpaUtil.kdbHandlerService.collection(pwEntry, !pwEntry.isCollectioned())\n          item.setIcon(if (!pwEntry.isCollectioned()) R.drawable.ic_star_outline else R.drawable.ic_star)\n        }\n      }\n\n      true\n    }\n\n    binding.appBarLayout.addOnOffsetChangedListener { _, verticalOffset ->\n      // Timber.d(\"offset: $verticalOffset， ${binding.appBarLayout.totalScrollRange}\")\n      if (verticalOffset == 0) {\n        binding.topAppBar.title = \"\"\n        return@addOnOffsetChangedListener\n      }\n      if (abs(verticalOffset) >= binding.appBarLayout.totalScrollRange) {\n        binding.topAppBar.title = pwEntry.title\n        return@addOnOffsetChangedListener\n      }\n    }\n    handleMenuBar()\n  }\n\n  private fun handleMenuBar() {\n    binding.btnUserName.doClick {\n      pwEntry.copyUserName()\n    }\n    binding.btnUserPass.doClick {\n      pwEntry.copyPassword()\n    }\n    binding.btnTotp.visibility = if (!pwEntry.hasTOTP()) View.GONE else View.VISIBLE\n    binding.btnTotp.doClick {\n      pwEntry.copyTotp()\n    }\n  }\n\n  /**\n   * 设置图标\n   */\n  private fun setIcon() {\n    val color = if (pwEntry.getCustomIcon()?.imageData?.isNotEmpty() == true) {\n      module.getColor(this, BitmapDrawable(IconUtil.getCustomBitmap(pwEntry)))\n    } else {\n      ResUtil.getColor(R.color.color_444E85DB)\n    }\n\n    binding.tvChar.visibility = View.VISIBLE\n    if (pwEntry.title.isEmpty()){\n      binding.tvChar.text = \"#\"\n    }else{\n      binding.tvChar.text = pwEntry.title.substring(0, 1).uppercase(Locale.getDefault())\n    }\n\n    binding.ivIcon.setBackgroundColor(color)\n  }\n\n  private fun setAppIcon() {\n    val adapter = AppIconAdapter()\n\n    binding.rvAppIcon.apply {\n      this.adapter = adapter\n      setChildDrawingOrderCallback { childCount, i ->\n        if (childCount <= 1) {\n          return@setChildDrawingOrderCallback i\n        }\n        return@setChildDrawingOrderCallback childCount - i - 1\n\n      }\n      layoutManager = AppIconLayoutManager(15.toPx())\n    }\n    adapter.setData(arrayListOf<String>().apply {\n      add(\"tv.danmaku.bili\")\n    })\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/detail/EntryDetailModule.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.view.detail\n\nimport android.animation.Animator\nimport android.animation.AnimatorListenerAdapter\nimport android.content.Context\nimport android.graphics.Color\nimport android.graphics.drawable.ColorDrawable\nimport android.graphics.drawable.Drawable\nimport android.net.Uri\nimport android.view.View\nimport android.view.ViewAnimationUtils\nimport android.widget.ImageView\nimport androidx.fragment.app.FragmentActivity\nimport androidx.lifecycle.viewModelScope\nimport androidx.palette.graphics.Palette\nimport com.arialyy.frame.module.SingleLiveEvent\nimport com.arialyy.frame.util.ResUtil\nimport com.keepassdroid.database.PwEntry\nimport com.keepassdroid.database.PwEntryV4\nimport com.keepassdroid.database.security.ProtectedBinary\nimport com.keepassdroid.database.security.ProtectedString\nimport com.keepassdroid.utils.Types\nimport com.keepassdroid.utils.UriUtil\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.R.color\nimport com.lyy.keepassa.base.BaseApp\nimport com.lyy.keepassa.base.BaseModule\nimport com.lyy.keepassa.entity.EntryRecord\nimport com.lyy.keepassa.util.HitUtil\nimport com.lyy.keepassa.util.IconUtil\nimport com.lyy.keepassa.util.KdbUtil\nimport com.lyy.keepassa.util.KpaUtil\nimport com.lyy.keepassa.util.VibratorUtil\nimport com.lyy.keepassa.widget.toPx\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.launch\nimport kotlinx.coroutines.withContext\nimport timber.log.Timber\n\n/**\n * 条目详情\n */\nclass EntryDetailModule : BaseModule() {\n  private lateinit var pwEntry: PwEntry\n  private val finishAnimEvent = SingleLiveEvent<Boolean>()\n  private val startAnimEvent = SingleLiveEvent<Boolean>()\n\n  fun initEntry(pwEntry: PwEntry) {\n    this.pwEntry = pwEntry\n  }\n\n  /**\n   * 结束动画\n   */\n  fun finishAnim(\n    context: Context,\n    rootView: View,\n    icon: ImageView\n  ): SingleLiveEvent<Boolean> {\n    viewModelScope.launch {\n      val rgb = getColor(context, icon.drawable)\n      val x = icon.x + 20.toPx()\n      val y = icon.y + 60.toPx()\n      val anim = ViewAnimationUtils.createCircularReveal(\n        rootView,\n        x.toInt(),\n        y.toInt(),\n        rootView.height.toFloat(),\n        0f,\n      )\n      anim.duration = 400\n      anim.addListener(object : AnimatorListenerAdapter() {\n        override fun onAnimationStart(animation: Animator) {\n          super.onAnimationStart(animation)\n          rootView.background = ColorDrawable(rgb)\n        }\n\n        override fun onAnimationEnd(animation: Animator) {\n          super.onAnimationEnd(animation)\n          rootView.background = ColorDrawable(ResUtil.getColor(R.color.background_color))\n          finishAnimEvent.postValue(true)\n        }\n      })\n      anim.start()\n    }\n\n    return finishAnimEvent\n  }\n\n  /**\n   * 启动动画\n   */\n  fun startAnim(\n    context: Context,\n    rootView: View,\n    icon: ImageView\n  ): SingleLiveEvent<Boolean> {\n    viewModelScope.launch {\n      val rgb = getColor(context, icon.drawable)\n      val x = icon.x + 20.toPx()\n      val y = icon.y + 60.toPx()\n      val anim = ViewAnimationUtils.createCircularReveal(\n        rootView,\n        x.toInt(),\n        y.toInt(),\n        40.toPx()\n          .toFloat(),\n        rootView.height.toFloat()\n      )\n      anim.duration = 400\n      anim.addListener(object : AnimatorListenerAdapter() {\n        override fun onAnimationStart(animation: Animator) {\n          super.onAnimationStart(animation)\n          rootView.background = ColorDrawable(rgb)\n        }\n\n        override fun onAnimationEnd(animation: Animator) {\n          super.onAnimationEnd(animation)\n          rootView.background = ColorDrawable(ResUtil.getColor(color.background_color))\n          startAnimEvent.postValue(true)\n        }\n      })\n      anim.start()\n    }\n    return startAnimEvent\n  }\n\n  /**\n   * get highlight color\n   */\n  fun getColor(\n    context: Context,\n    icon: Drawable\n  ): Int {\n    return with(Dispatchers.IO) {\n      val temp =\n        IconUtil.getBitmapFromDrawable(context, icon, 40.toPx())\n      if (temp == null || temp.isRecycled) {\n        return@with Color.WHITE\n      }\n      val sw = Palette.from(temp)\n        .maximumColorCount(12)\n        .generate()\n      return@with when {\n        sw.mutedSwatch != null -> sw.mutedSwatch!!.rgb\n        sw.darkMutedSwatch != null -> sw.darkMutedSwatch!!.rgb\n        sw.lightMutedSwatch != null -> sw.lightMutedSwatch!!.rgb\n        sw.darkVibrantSwatch != null -> sw.darkVibrantSwatch!!.rgb\n        sw.lightVibrantSwatch != null -> sw.lightVibrantSwatch!!.rgb\n        sw.vibrantSwatch != null -> sw.vibrantSwatch!!.rgb\n        else -> ResUtil.getColor(R.color.colorPrimary)\n      }\n    }\n  }\n\n  /**\n   * 保存附件到sd卡\n   * @param saveUri 保存路径\n   * @param source 需要保存的文件\n   */\n  fun saveAttachment(\n    context: Context,\n    saveUri: Uri,\n    source: ProtectedBinary\n  ) {\n    viewModelScope.launch(Dispatchers.IO) {\n      try {\n        val byte = source.data.readBytes()\n        val os = context.contentResolver.openOutputStream(saveUri)\n        if (os != null) {\n          os.write(byte, 0, byte.size)\n          os.flush()\n          os.close()\n        }\n        withContext(Dispatchers.Main) {\n          val fileName = UriUtil.getFileNameFromUri(context, saveUri)\n          HitUtil.toaskShort(context.getString(R.string.save_file_success, fileName))\n        }\n      } catch (e: Exception) {\n        Timber.e(e)\n      }\n    }\n  }\n\n  /**\n   * 回收项目\n   * @param pwEntry 需要回收的条目\n   */\n  fun recycleEntry(ac: FragmentActivity, pwEntry: PwEntryV4) {\n    KpaUtil.kdbHandlerService.deleteEntry(pwEntry) {\n      HitUtil.toaskShort(\n        \"${ac.getString(R.string.del_entry)}${ac.getString(R.string.success)}\"\n      )\n      VibratorUtil.vibrator(300)\n      ac.finishAfterTransition()\n    }\n  }\n\n  /**\n   * 保存打开记录\n   */\n  fun saveRecord() {\n    if (BaseApp.dbRecord == null) {\n      return\n    }\n    KpaUtil.scope.launch(Dispatchers.IO) {\n      val dao = BaseApp.appDatabase.entryRecordDao()\n      var record = dao.getRecord(Types.UUIDtoBytes(pwEntry.uuid), BaseApp.dbRecord!!.localDbUri)\n      if (record == null) {\n        record = EntryRecord(\n          userName = pwEntry.username,\n          title = pwEntry.title,\n          uuid = Types.UUIDtoBytes(pwEntry.uuid),\n          time = System.currentTimeMillis(),\n          dbFileUri = BaseApp.dbRecord!!.localDbUri\n        )\n        dao.saveRecord(record)\n      } else {\n        record.title = pwEntry.title\n        record.userName = pwEntry.username\n        record.time = System.currentTimeMillis()\n        dao.updateRecord(record)\n      }\n      KpaUtil.openEntryRecordFlow.emit(record)\n    }\n  }\n\n  /**\n   * 获取项目的属性字段，只有v4版本才有自定义属性字段\n   */\n  fun getV4EntryStr(entryV4: PwEntryV4): Map<String, ProtectedString> {\n    return KdbUtil.filterCustomStr(entryV4)\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/detail/GroupDetailActivity.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.view.detail\n\nimport android.animation.Animator\nimport android.animation.AnimatorListenerAdapter\nimport android.os.Bundle\nimport android.transition.TransitionInflater\nimport android.view.MotionEvent\nimport android.view.View\nimport androidx.appcompat.widget.AppCompatImageView\nimport androidx.core.app.ActivityOptionsCompat\nimport androidx.core.transition.addListener\nimport androidx.lifecycle.ViewModelProvider\nimport androidx.lifecycle.lifecycleScope\nimport androidx.recyclerview.widget.LinearLayoutManager\nimport com.alibaba.android.arouter.facade.annotation.Autowired\nimport com.alibaba.android.arouter.facade.annotation.Route\nimport com.alibaba.android.arouter.launcher.ARouter\nimport com.arialyy.frame.router.Routerfit\nimport com.blankj.utilcode.util.ActivityUtils\nimport com.keepassdroid.database.PwEntry\nimport com.keepassdroid.database.PwGroup\nimport com.keepassdroid.database.PwGroupId\nimport com.keepassdroid.database.PwGroupV4\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.AnimState\nimport com.lyy.keepassa.base.AnimState.NOT_ANIM\nimport com.lyy.keepassa.base.BaseActivity\nimport com.lyy.keepassa.base.BaseApp\nimport com.lyy.keepassa.common.SortType.CHAR_ASC\nimport com.lyy.keepassa.common.SortType.CHAR_DESC\nimport com.lyy.keepassa.common.SortType.NONE\nimport com.lyy.keepassa.common.SortType.TIME_ASC\nimport com.lyy.keepassa.common.SortType.TIME_DESC\nimport com.lyy.keepassa.databinding.ActivityGroupDetailBinding\nimport com.lyy.keepassa.entity.showPopMenu\nimport com.lyy.keepassa.event.EntryState.CREATE\nimport com.lyy.keepassa.event.EntryState.DELETE\nimport com.lyy.keepassa.event.EntryState.MODIFY\nimport com.lyy.keepassa.event.EntryState.MOVE\nimport com.lyy.keepassa.event.EntryState.SAVE\nimport com.lyy.keepassa.event.EntryState.UNKNOWN\nimport com.lyy.keepassa.event.MoveEvent\nimport com.lyy.keepassa.router.ActivityRouter\nimport com.lyy.keepassa.router.DialogRouter\nimport com.lyy.keepassa.util.EventBusHelper\nimport com.lyy.keepassa.util.KeepassAUtil\nimport com.lyy.keepassa.util.KpaUtil\nimport com.lyy.keepassa.util.checkGroupIsParent\nimport com.lyy.keepassa.util.createGroup\nimport com.lyy.keepassa.util.deleteGroup\nimport com.lyy.keepassa.util.doOnInterceptTouchEvent\nimport com.lyy.keepassa.util.doOnItemClickListener\nimport com.lyy.keepassa.util.doOnItemLongClickListener\nimport com.lyy.keepassa.util.updateModifyGroup\nimport com.lyy.keepassa.view.SimpleEntryAdapter\nimport com.lyy.keepassa.widget.MainExpandFloatActionButton\nimport kotlinx.coroutines.flow.collectLatest\nimport kotlinx.coroutines.launch\nimport org.greenrobot.eventbus.Subscribe\nimport org.greenrobot.eventbus.ThreadMode.MAIN\nimport timber.log.Timber\n\n/**\n * 群组详情、回收站详情\n */\n@Route(path = \"/group/detail\")\nclass GroupDetailActivity : BaseActivity<ActivityGroupDetailBinding>() {\n\n  companion object {\n    const val KEY_TITLE = \"KEY_TITLE\"\n    const val KEY_GROUP_ID = \"KEY_V3_GROUP_ID\"\n    const val KEY_IS_IN_RECYCLE_BIN = \"KEY_IS_IN_RECYCLE_BIN\"\n  }\n\n  private lateinit var module: GroupDetailModule\n  private lateinit var adapter: SimpleEntryAdapter\n  private var curx = 0\n\n  @JvmField\n  @Autowired(name = KEY_IS_IN_RECYCLE_BIN)\n  var isRecycleBin = false\n\n  @Autowired(name = KEY_GROUP_ID)\n  lateinit var groupId: PwGroupId\n\n  @Autowired(name = KEY_TITLE)\n  lateinit var groupTitle: String\n\n  override fun setLayoutId(): Int {\n    return R.layout.activity_group_detail\n  }\n\n  override fun onCreate(savedInstanceState: Bundle?) {\n    super.onCreate(savedInstanceState)\n    if (window.enterTransition == null || !KeepassAUtil.instance.isDisplayLoadingAnim()) {\n      loadData()\n      return\n    }\n    window.enterTransition = null\n    window.exitTransition = null\n    binding.laAnim.speed = 2.5f\n    binding.laAnim.playAnimation()\n    binding.laAnim.addAnimatorListener(object : AnimatorListenerAdapter() {\n      override fun onAnimationEnd(animation: Animator) {\n        super.onAnimationEnd(animation)\n        loadData()\n      }\n    })\n  }\n\n  override fun useAnim(): AnimState {\n    return NOT_ANIM\n  }\n\n  override fun initData(savedInstanceState: Bundle?) {\n    super.initData(savedInstanceState)\n    ARouter.getInstance().inject(this)\n    EventBusHelper.reg(this)\n\n    // 检查是否是在回收站中\n    if (!isRecycleBin) {\n      isRecycleBin =\n        BaseApp.isV4 && BaseApp.KDB!!.pm.recycleBin != null && BaseApp.KDB!!.pm.recycleBin.id == groupId\n    }\n    if (isRecycleBin) {\n      binding.fab.visibility = View.GONE\n    }\n\n    binding.ctlCollapsingLayout.title = groupTitle\n    binding.kpaToolbar.title = groupTitle\n    binding.kpaToolbar.setNavigationOnClickListener {\n      finishAfterTransition()\n    }\n  }\n\n  override fun finishAfterTransition() {\n    window.returnTransition = TransitionInflater.from(this)\n      .inflateTransition(R.transition.slide_return)\n    super.finishAfterTransition()\n  }\n\n  private fun loadData() {\n    binding.kpaToolbar.inflateMenu(R.menu.menu_group_detail)\n    module = ViewModelProvider(this)[GroupDetailModule::class.java]\n    initList()\n    initFab()\n    initMenu()\n    listenerGetGroupData()\n    listenerEntryStateChange()\n    listenerGroupStateChange()\n    module.getGroupData(this, groupId)\n  }\n\n  /**\n   * listener the group status change, there are states: create, delete, modify, move\n   */\n  private fun listenerGroupStateChange() {\n    lifecycleScope.launch {\n      KpaUtil.kdbHandlerService.groupStateChangeFlow.collectLatest {\n        if (it.groupV4 == null || module.curGroupV4 == null) {\n          return@collectLatest\n        }\n        when (it.state) {\n          CREATE -> {\n            adapter.createGroup(module.entryData, it.groupV4, module.curGroupV4)\n            if (it.groupV4.checkGroupIsParent(module.curGroupV4)) {\n              showList(true)\n            }\n          }\n          MODIFY -> {\n            adapter.updateModifyGroup(module.entryData, it.groupV4, module.curGroupV4)\n          }\n          MOVE -> {\n            // module.moveEntry(adapter, it.pwEntryV4, it.oldParent!!)\n          }\n          DELETE -> {\n            adapter.deleteGroup(\n              module.entryData,\n              it.groupV4,\n              it.oldParent!!,\n              module.curGroupV4\n            )\n            if (module.entryData.isEmpty()) {\n              showList(false)\n            }\n          }\n          SAVE -> {\n            showList(true)\n            adapter.notifyDataSetChanged()\n          }\n          UNKNOWN -> {\n            Timber.d(\"un known status\")\n          }\n        }\n      }\n    }\n  }\n\n  /**\n   * listener the entry status change, there are three states: create, delete, and modify.\n   */\n  private fun listenerEntryStateChange() {\n    lifecycleScope.launch {\n      KpaUtil.kdbHandlerService.entryStateChangeFlow.collectLatest {\n        it.pwEntryV4?.let { entry ->\n          when (it.state) {\n            CREATE -> {\n              module.createNewEntry(adapter, entry)\n              if (it.pwEntryV4.checkGroupIsParent(module.curGroupV4)) {\n                showList(true)\n              }\n            }\n            MODIFY -> {\n              module.updateModifyEntry(adapter, entry)\n              if (module.entryData.isEmpty()) {\n                showList(false)\n              }\n            }\n            MOVE -> {\n              module.moveEntry(adapter, entry, it.oldParent!!)\n            }\n            DELETE -> {\n              module.deleteEntry(adapter, entry, it.oldParent!!)\n              if (module.entryData.isEmpty()) {\n                showList(false)\n              }\n            }\n            SAVE -> {\n              showList(true)\n              adapter.notifyDataSetChanged()\n            }\n            UNKNOWN -> {\n              Timber.d(\"un known status\")\n            }\n          }\n        }\n      }\n    }\n  }\n\n  private fun listenerGetGroupData() {\n    lifecycleScope.launch {\n      module.getDataFlow.collectLatest { list ->\n        binding.laAnim.cancelAnimation()\n        binding.laAnim.visibility = View.GONE\n        if (list.isNullOrEmpty()) {\n          // 设置appbar为收缩状态\n          binding.appBar.setExpanded(false, false)\n          showList(false)\n          return@collectLatest\n        }\n        showList(true)\n        adapter.notifyDataSetChanged()\n      }\n    }\n  }\n\n  private fun showList(showList: Boolean) {\n    if (showList) {\n      binding.list.visibility = View.VISIBLE\n      getEmptyLayout().visibility = View.GONE\n      return\n    }\n    getEmptyLayout().visibility = View.VISIBLE\n    binding.list.visibility = View.GONE\n  }\n\n  private fun initMenu() {\n    binding.kpaToolbar.setOnMenuItemClickListener {\n      val type = when (it.itemId) {\n        R.id.sort_down_by_char -> {\n          CHAR_DESC\n        }\n        R.id.sort_up_by_char -> {\n          CHAR_ASC\n        }\n        R.id.sort_down_by_time -> {\n          TIME_DESC\n        }\n        R.id.sort_up_by_time -> {\n          TIME_ASC\n        }\n        else -> NONE\n      }\n      if (type != NONE) {\n        module.sortData(adapter, type)\n      }\n      return@setOnMenuItemClickListener true\n    }\n  }\n\n  /**\n   * fab\n   */\n  private fun initFab() {\n    if (isRecycleBin) {\n      binding.fab.visibility = View.GONE\n      return\n    }\n    binding.fab.setOnItemClickListener(object : MainExpandFloatActionButton.OnItemClickListener {\n      override fun onKeyClick() {\n        Routerfit.create(ActivityRouter::class.java, this@GroupDetailActivity)\n          .toCreateEntryActivity(\n            groupId,\n            ActivityOptionsCompat.makeSceneTransitionAnimation(this@GroupDetailActivity)\n          )\n        binding.fab.hintMoreOperate()\n      }\n\n      override fun onGroupClick() {\n        Routerfit.create(DialogRouter::class.java)\n          .showCreateGroupDialog(\n            (BaseApp.KDB!!.pm.groups[groupId] ?: BaseApp.KDB!!.pm.rootGroup) as PwGroupV4\n          )\n        binding.fab.hintMoreOperate()\n      }\n    })\n  }\n\n  private fun initList() {\n    adapter = SimpleEntryAdapter(this, module.entryData)\n    binding.list.setHasFixedSize(true)\n    binding.list.layoutManager = LinearLayoutManager(this)\n    binding.list.adapter = adapter\n    binding.list.doOnItemClickListener { _, position, v ->\n      val item = module.entryData[position]\n      if (item.obj is PwGroup) {\n        val group = item.obj as PwGroup\n        Routerfit.create(ActivityRouter::class.java, this).toGroupDetailActivity(\n          groupName = group.name,\n          groupId = group.id,\n          isRecycleBin = isRecycleBin,\n          opt = ActivityOptionsCompat.makeSceneTransitionAnimation(this)\n        )\n        return@doOnItemClickListener\n      }\n\n      if (item.obj is PwEntry) {\n        val icon = v.findViewById<AppCompatImageView>(R.id.icon)\n        KeepassAUtil.instance.turnEntryDetail(this, item.obj as PwEntry, icon)\n        return@doOnItemClickListener\n      }\n    }\n\n    binding.list.doOnItemLongClickListener { _, position, v ->\n      module.entryData[position].showPopMenu(this, v, curx, isRecycleBin)\n      return@doOnItemLongClickListener true\n    }\n\n    // 获取点击位置\n    binding.list.doOnInterceptTouchEvent { _, e ->\n      if (e.action == MotionEvent.ACTION_DOWN) {\n        curx = e.x.toInt()\n      }\n      return@doOnInterceptTouchEvent false\n    }\n  }\n\n  private fun getEmptyLayout(): View {\n    if (!binding.vsEmpty.isInflated) {\n      binding.vsEmpty.viewStub?.inflate()\n    }\n    return binding.vsEmpty.root\n  }\n\n  /**\n   * 有条目移动或有条目从回收站中撤回\n   */\n  @Subscribe(threadMode = MAIN)\n  fun onMove(event: MoveEvent) {\n    // getData()\n  }\n\n  override fun onDestroy() {\n    super.onDestroy()\n    EventBusHelper.unReg(this)\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/detail/GroupDetailModule.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.view.detail\n\nimport android.content.Context\nimport androidx.lifecycle.viewModelScope\nimport com.arialyy.frame.util.PinyinUtil\nimport com.keepassdroid.database.PwDataInf\nimport com.keepassdroid.database.PwEntryV4\nimport com.keepassdroid.database.PwGroup\nimport com.keepassdroid.database.PwGroupId\nimport com.keepassdroid.database.PwGroupV4\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseApp\nimport com.lyy.keepassa.base.BaseModule\nimport com.lyy.keepassa.common.SortType\nimport com.lyy.keepassa.common.SortType.CHAR_ASC\nimport com.lyy.keepassa.common.SortType.CHAR_DESC\nimport com.lyy.keepassa.common.SortType.NONE\nimport com.lyy.keepassa.common.SortType.TIME_ASC\nimport com.lyy.keepassa.common.SortType.TIME_DESC\nimport com.lyy.keepassa.entity.SimpleItemEntity\nimport com.lyy.keepassa.util.KdbUtil\nimport com.lyy.keepassa.util.createNewEntry\nimport com.lyy.keepassa.util.deleteEntry\nimport com.lyy.keepassa.util.moveEntry\nimport com.lyy.keepassa.util.updateModifyEntry\nimport com.lyy.keepassa.view.SimpleEntryAdapter\nimport kotlinx.coroutines.flow.MutableSharedFlow\nimport kotlinx.coroutines.launch\n\ninternal class GroupDetailModule : BaseModule() {\n\n  val entryData = mutableListOf<SimpleItemEntity>()\n  val getDataFlow = MutableSharedFlow<MutableList<SimpleItemEntity>?>()\n  var curGroupV4: PwGroupV4? = null\n\n  /**\n   * update the status of deleted items\n   */\n  fun deleteEntry(adapter: SimpleEntryAdapter, pwEntryV4: PwEntryV4, oldParent: PwGroupV4) {\n    curGroupV4?.let {\n      adapter.deleteEntry(entryData, pwEntryV4, oldParent, it)\n    }\n  }\n\n  /**\n   * update the status of modified items\n   */\n  fun updateModifyEntry(adapter: SimpleEntryAdapter, pwEntryV4: PwEntryV4) {\n    curGroupV4?.let {\n      adapter.updateModifyEntry(entryData, pwEntryV4, it)\n    }\n  }\n\n  /**\n   * move entry from other group\n   */\n  fun moveEntry(adapter: SimpleEntryAdapter, pwEntryV4: PwEntryV4, oldParent: PwGroupV4) {\n    curGroupV4?.let {\n      adapter.moveEntry(entryData, pwEntryV4, oldParent, it)\n    }\n  }\n\n  /**\n   * update root list state\n   */\n  fun createNewEntry(adapter: SimpleEntryAdapter, pwEntryV4: PwEntryV4) {\n    curGroupV4?.let {\n      adapter.createNewEntry(entryData, pwEntryV4, it)\n    }\n  }\n\n  /**\n   * 获取v3版本的group数据\n   */\n  fun getGroupData(context: Context, groupId: PwGroupId) {\n    entryData.clear()\n    viewModelScope.launch {\n      val group = BaseApp.KDB.pm.groups[groupId]\n      curGroupV4 = group as PwGroupV4?\n      if (group == null) {\n        getDataFlow.emit(null)\n        return@launch\n      }\n      entryData.addAll(convertGroup(context, group))\n      getDataFlow.emit(entryData)\n      return@launch\n    }\n  }\n\n  /**\n   * 排序\n   * @param sortType\n   */\n  fun sortData(adapter: SimpleEntryAdapter, sortType: SortType) {\n    val entryList = arrayListOf<SimpleItemEntity>()\n    val groupList = arrayListOf<SimpleItemEntity>()\n    val tempList = arrayListOf<SimpleItemEntity>()\n\n    for (item in entryData) {\n      if (item.obj is PwGroup) {\n        groupList.add(item)\n        continue\n      }\n      entryList.add(item)\n    }\n    tempList.addAll(sortEntry(sortType, groupList))\n    tempList.addAll(sortEntry(sortType, entryList))\n    entryData.clear()\n    entryData.addAll(tempList)\n    adapter.notifyDataSetChanged()\n  }\n\n  private fun sortEntry(\n    sortType: SortType,\n    data: List<SimpleItemEntity>\n  ): Set<SimpleItemEntity> {\n    val map = hashMapOf<SimpleItemEntity, Char?>()\n    for (item in data) {\n      map[item] = PinyinUtil.getFirstSpellChar(item.title)\n    }\n    return when (sortType) {\n      CHAR_ASC -> {\n        map.toList()\n          .sortedBy { it.second }\n          .toMap().keys\n      }\n      CHAR_DESC -> {\n        map.toList()\n          .sortedByDescending { it.second }\n          .toMap().keys\n      }\n      TIME_ASC -> {\n        map.toList()\n          .sortedBy {\n            (it.first.obj as PwDataInf).creationTime.time\n          }\n          .toMap().keys\n      }\n      TIME_DESC -> {\n        map.toList()\n          .sortedByDescending { (it.first.obj as PwDataInf).creationTime.time }\n          .toMap().keys\n      }\n      NONE -> {\n        emptySet()\n      }\n    }\n  }\n\n  private fun convertGroup(\n    context: Context,\n    group: PwGroup\n  ): ArrayList<SimpleItemEntity> {\n    val data = ArrayList<SimpleItemEntity>()\n    for (cGroup in group.childGroups) {\n      val item = SimpleItemEntity()\n      item.title = cGroup.name\n      item.subTitle =\n        context.getString(\n          R.string.hint_group_desc, KdbUtil.getGroupAllEntryNum(cGroup)\n            .toString()\n        )\n      item.obj = cGroup\n      data.add(item)\n    }\n\n    for (entry in group.childEntries) {\n      val item = SimpleItemEntity()\n      item.title = entry.title\n      item.subTitle = KdbUtil.getUserName(entry)\n      item.obj = entry\n      data.add(item)\n    }\n    return data\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/detail/card/EntryBaseInfoCard.kt",
    "content": "package com.lyy.keepassa.view.detail.card\n\nimport android.content.Context\nimport android.text.Html\nimport android.util.AttributeSet\nimport android.view.LayoutInflater\nimport com.arialyy.frame.util.ResUtil\nimport com.google.android.material.card.MaterialCardView\nimport com.keepassdroid.database.PwEntryV4\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.databinding.LayoutEntryCardBaseInfoBinding\nimport com.lyy.keepassa.util.ClipboardUtil\nimport com.lyy.keepassa.util.HitUtil\nimport com.lyy.keepassa.util.KdbUtil\nimport com.lyy.keepassa.util.KeepassAUtil\nimport com.lyy.keepassa.util.KpaUtil\nimport com.lyy.keepassa.util.doClick\nimport java.util.Date\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 10:19 AM 2023/9/26\n **/\nclass EntryBaseInfoCard(context: Context, attributeSet: AttributeSet) :\n  MaterialCardView(context, attributeSet) {\n  private val binding =\n    LayoutEntryCardBaseInfoBinding.inflate(LayoutInflater.from(context), this, true)\n\n  fun bindData(entry: PwEntryV4) {\n    val userName = KdbUtil.getUserName(entry)\n    binding.tvUserName.text = userName\n    binding.tvUserName.doClick {\n      ClipboardUtil.get()\n        .copyDataToClip(userName)\n      HitUtil.toaskShort(context.getString(R.string.hint_copy_user))\n    }\n    handlePass(entry)\n    if (entry.url.isBlank()) {\n      binding.tvUrl.visibility = GONE\n    } else {\n      binding.tvUrl.visibility = VISIBLE\n      binding.tvUrl.text = entry.url\n      binding.tvUrl.doClick {\n        KpaUtil.openUrlWithBrowser(entry.url)\n      }\n    }\n    handleExpires(entry)\n  }\n\n  /**\n   * 处理过期\n   */\n  private fun handleExpires(entry: PwEntryV4) {\n    if (!entry.expires()) {\n      binding.time1.visibility = GONE\n      return\n    }\n    binding.time1.visibility = VISIBLE\n    if (entry.expiryTime.after(Date())) {\n      binding.time1.text =\n        ResUtil.getString(R.string.expire_time, KeepassAUtil.instance.formatTime(entry.expiryTime))\n      return\n    }\n    binding.time1.text = Html.fromHtml(\n      ResUtil.getString(\n        R.string.expire,\n        KeepassAUtil.instance.formatTime(entry.expiryTime, \"yyyy/MM/dd\")\n      )\n    )\n  }\n\n  private fun handlePass(entry: PwEntryV4) {\n    val pass = KdbUtil.getPassword(entry)\n    binding.tvPass.text = pass\n    binding.ivEye.isSelected = true\n    binding.ivEye.doClick {\n      binding.ivEye.isSelected = !binding.ivEye.isSelected\n      KpaUtil.handleShowPass(binding.tvPass, !binding.ivEye.isSelected)\n    }\n    binding.tvPass.doClick {\n      ClipboardUtil.get()\n        .copyDataToClip(pass)\n      HitUtil.toaskShort(context.getString(R.string.hint_copy_pass))\n    }\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/detail/card/EntryFileCard.kt",
    "content": "package com.lyy.keepassa.view.detail.card\n\nimport android.content.Context\nimport android.util.AttributeSet\nimport android.view.LayoutInflater\nimport androidx.fragment.app.FragmentActivity\nimport androidx.recyclerview.widget.LinearLayoutManager\nimport com.arialyy.frame.util.ResUtil\nimport com.google.android.material.card.MaterialCardView\nimport com.keepassdroid.database.PwEntryV4\nimport com.keepassdroid.database.security.ProtectedBinary\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.AbsViewBindingAdapter\nimport com.lyy.keepassa.databinding.LayoutEntryAttachmentBinding\nimport com.lyy.keepassa.databinding.LayoutEntryCardListBinding\nimport com.lyy.keepassa.util.KpaUtil\nimport com.lyy.keepassa.util.doOnItemClickListener\nimport com.lyy.keepassa.view.menu.EntryDetailFilePopMenu\nimport kotlinx.coroutines.flow.MutableSharedFlow\nimport kotlinx.coroutines.launch\nimport kotlin.collections.MutableMap.MutableEntry\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 11:12 AM 2023/9/26\n **/\nclass EntryFileCard(context: Context, attributeSet: AttributeSet) :\n  MaterialCardView(context, attributeSet) {\n  private val binding = LayoutEntryCardListBinding.inflate(LayoutInflater.from(context), this, true)\n\n  companion object {\n    val SAVE_FILE_FLOW = MutableSharedFlow<Pair<String, ProtectedBinary>>()\n  }\n\n  fun bindData(entry: PwEntryV4) {\n    binding.tvCardTitle.text = ResUtil.getString(R.string.attachment)\n    val data = entry.binaries.entries.toMutableList()\n    if (data.isEmpty()) {\n      visibility = GONE\n      return\n    }\n    visibility = VISIBLE\n    handleList(data)\n  }\n\n  private fun handleList(data: MutableList<MutableEntry<String, ProtectedBinary>>) {\n    val adapter = AttachmentAdapter()\n\n    binding.rvList.apply {\n      this.adapter = adapter\n      setHasFixedSize(true)\n      layoutManager = LinearLayoutManager(context)\n      adapter.setData(data)\n    }\n    binding.rvList.doOnItemClickListener { _, position, v ->\n      val entry = data[position]\n      val pop = EntryDetailFilePopMenu(context as FragmentActivity, v, entry.key, entry.value)\n      pop.setOnDownloadClick(object : EntryDetailFilePopMenu.OnDownloadClick {\n        override fun onDownload(key: String, file: ProtectedBinary) {\n          KpaUtil.scope.launch {\n            SAVE_FILE_FLOW.emit(Pair(key, file))\n          }\n        }\n      })\n      pop.show()\n    }\n  }\n\n  private class AttachmentAdapter :\n    AbsViewBindingAdapter<MutableEntry<String, ProtectedBinary>, LayoutEntryAttachmentBinding>() {\n\n    override fun bindData(\n      binding: LayoutEntryAttachmentBinding,\n      item: MutableEntry<String, ProtectedBinary>\n    ) {\n      binding.value.text = item.key\n    }\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/detail/card/EntryNoteCard.kt",
    "content": "package com.lyy.keepassa.view.detail.card\n\nimport android.content.Context\nimport android.util.AttributeSet\nimport android.view.LayoutInflater\nimport android.widget.TextView\nimport androidx.core.content.res.ResourcesCompat\nimport com.google.android.material.card.MaterialCardView\nimport com.keepassdroid.database.PwEntryV4\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.databinding.LayoutEntryCardNoteBinding\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 11:03 AM 2023/9/26\n **/\nclass EntryNoteCard(context: Context, attributeSet: AttributeSet) :\n  MaterialCardView(context, attributeSet) {\n  private val binding = LayoutEntryCardNoteBinding.inflate(LayoutInflater.from(context), this, true)\n\n  fun bindData(entryV4: PwEntryV4) {\n    visibility = if (entryV4.notes.isBlank()) GONE else VISIBLE\n    binding.expandTv.text = entryV4.notes\n    binding.expandTv.findViewById<TextView>(R.id.expandable_text).typeface =\n      ResourcesCompat.getFont(context, R.font.roboto_thinitalic)\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/detail/card/EntryStrCard.kt",
    "content": "package com.lyy.keepassa.view.detail.card\n\nimport android.annotation.SuppressLint\nimport android.content.Context\nimport android.os.Build\nimport android.text.InputType\nimport android.text.TextUtils\nimport android.util.AttributeSet\nimport android.view.LayoutInflater\nimport android.widget.TextView\nimport androidx.core.view.isVisible\nimport androidx.fragment.app.FragmentActivity\nimport androidx.recyclerview.widget.LinearLayoutManager\nimport com.arialyy.frame.util.ResUtil\nimport com.google.android.material.card.MaterialCardView\nimport com.keepassdroid.database.PwEntryV4\nimport com.keepassdroid.database.security.ProtectedString\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.AbsViewBindingAdapter\nimport com.lyy.keepassa.base.KeyConstance\nimport com.lyy.keepassa.databinding.LayoutEntryCardListBinding\nimport com.lyy.keepassa.databinding.LayoutEntryStrBinding\nimport com.lyy.keepassa.util.KdbUtil\nimport com.lyy.keepassa.util.KpaUtil\nimport com.lyy.keepassa.util.doClick\nimport com.lyy.keepassa.util.doOnItemClickListener\nimport com.lyy.keepassa.util.hasTOTP\nimport com.lyy.keepassa.util.totp.OtpUtil\nimport com.lyy.keepassa.view.menu.EntryDetailStrPopMenu\nimport com.lyy.keepassa.view.menu.EntryDetailStrPopMenu.OnShowPassCallback\nimport timber.log.Timber\nimport kotlin.collections.Map.Entry\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 11:12 AM 2023/9/26\n **/\nclass EntryStrCard(context: Context, attributeSet: AttributeSet) :\n  MaterialCardView(context, attributeSet) {\n  private val binding = LayoutEntryCardListBinding.inflate(LayoutInflater.from(context), this, true)\n\n  fun bindData(entry: PwEntryV4) {\n    binding.tvCardTitle.text = ResUtil.getString(R.string.hint_attr)\n    val data = KdbUtil.filterCustomStr(entry).entries.toMutableList()\n    if (data.isEmpty()) {\n      visibility = GONE\n      return\n    }\n\n    if (entry.hasTOTP()) {\n      val totpPass = OtpUtil.getOtpPass(entry)\n      if (!TextUtils.isEmpty(totpPass.second)) {\n        val totpPassStr = ProtectedString(true, totpPass.second)\n        totpPassStr.isOtpPass = true\n        data.add(object : Entry<String, ProtectedString> {\n          override val key: String\n            get() = KeyConstance.TOTP\n          override val value: ProtectedString\n            get() = totpPassStr\n        })\n      }\n    }\n    data.sortBy { it.key }\n\n    visibility = VISIBLE\n    handleList(entry, data)\n  }\n\n  private fun handleList(entryV4: PwEntryV4, data: MutableList<Entry<String, ProtectedString>>) {\n    val adapter = StrAdapter(entryV4)\n\n    binding.rvList.apply {\n      this.adapter = adapter\n      setHasFixedSize(true)\n      layoutManager = object : LinearLayoutManager(context) {\n        override fun canScrollVertically(): Boolean {\n          return false\n        }\n      }\n      adapter.setData(data)\n      isNestedScrollingEnabled = false\n    }\n    binding.rvList.doOnItemClickListener { _, position, view ->\n      val tvValue = view.findViewById<TextView>(R.id.value)\n      val entry = data[position]\n      if (entry.value.toString().isEmpty()) {\n        Timber.e(\"value is null\")\n        return@doOnItemClickListener\n      }\n      val pop = EntryDetailStrPopMenu(\n        context as FragmentActivity,\n        view,\n        entry.value,\n        tvValue.inputType == InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_PASSWORD\n      )\n      pop.setOnShowPassCallback(object : OnShowPassCallback {\n        override fun showPass(showPass: Boolean) {\n          KpaUtil.handleShowPass(tvValue, showPass)\n        }\n      })\n      pop.show()\n    }\n  }\n\n  private class StrAdapter(val entryV4: PwEntryV4) :\n    AbsViewBindingAdapter<Entry<String, ProtectedString>, LayoutEntryStrBinding>() {\n\n    private fun handleOtp(binding: LayoutEntryStrBinding, tvValue: TextView) {\n      binding.rpbBar.visibility = VISIBLE\n      KdbUtil.startAutoGetOtp(entryV4, binding.rpbBar, tvValue)\n      binding.ivEye.isVisible = true\n      binding.ivEye.isSelected = true\n      binding.ivEye.doClick {\n        binding.ivEye.isSelected = !binding.ivEye.isSelected\n        KpaUtil.handleShowPass(binding.value, !binding.ivEye.isSelected)\n      }\n    }\n\n    @SuppressLint(\"SetTextI18n\")\n    override fun bindData(binding: LayoutEntryStrBinding, item: Entry<String, ProtectedString>) {\n      binding.title.text = item.key\n      val tvValue = binding.value\n      KpaUtil.handleShowPass(tvValue, !item.value.isProtected)\n      if (item.value.isOtpPass) {\n        handleOtp(binding, tvValue)\n        return\n      }\n      binding.ivEye.isVisible = false\n      binding.rpbBar.isVisible = false\n      if (item.value.toString().isEmpty()) {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {\n          tvValue.typeface = context.resources.getFont(R.font.roboto_thinitalic)\n        }\n        tvValue.text = \"null\"\n        return\n      }\n      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {\n        tvValue.typeface = context.resources.getFont(R.font.roboto_regular)\n      }\n      tvValue.text = item.value.toString()\n    }\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/detail/card/EntryTagCard.kt",
    "content": "package com.lyy.keepassa.view.detail.card\n\nimport android.content.Context\nimport android.util.AttributeSet\nimport android.view.LayoutInflater\nimport com.google.android.material.card.MaterialCardView\nimport com.google.android.material.chip.Chip\nimport com.keepassdroid.database.PwEntryV4\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.databinding.LayoutEntryCardTagBinding\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 11:26 AM 2023/9/27\n **/\nclass EntryTagCard(context: Context, attributeSet: AttributeSet) :\n  MaterialCardView(context, attributeSet) {\n  private val layoutInflater = LayoutInflater.from(context)\n  private val binding = LayoutEntryCardTagBinding.inflate(layoutInflater, this, true)\n\n  fun bindData(pwEntryV4: PwEntryV4) {\n    if (pwEntryV4.tags.isBlank()) {\n      visibility = GONE\n      return\n    }\n    visibility = VISIBLE\n    val tagList = pwEntryV4.tags.split(\",\")\n    if (binding.chipGroup.childCount > 0){\n      binding.chipGroup.removeAllViews()\n    }\n    tagList.forEachIndexed { index, s ->\n      buildChip(index, s)\n    }\n  }\n\n  private fun buildChip(index: Int, tag: String) {\n    val chip = layoutInflater.inflate(\n      R.layout.layout_chip_harvest,\n      binding.chipGroup,\n      false\n    ) as Chip\n    chip.id = index\n    chip.stateListAnimator = null\n    chip.text = tag\n    // chip.setOnCheckedChangeListener(this)\n    binding.chipGroup.addView(chip, index)\n  }\n}\n"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/dialog/AddMoreDialog.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.view.dialog\n\nimport android.content.Context\nimport android.os.Bundle\nimport android.view.View\nimport android.widget.TextView\nimport androidx.appcompat.widget.AppCompatImageView\nimport androidx.recyclerview.widget.LinearLayoutManager\nimport com.arialyy.frame.util.adapter.AbsHolder\nimport com.arialyy.frame.util.adapter.AbsRVAdapter\nimport com.arialyy.frame.util.adapter.RvItemClickSupport\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseBottomSheetDialogFragment\nimport com.lyy.keepassa.databinding.DialogAddMoreBinding\nimport com.lyy.keepassa.entity.SimpleItemEntity\nimport com.lyy.keepassa.util.loadImg\nimport com.lyy.keepassa.view.dialog.AddMoreDialog.Adapter.Holder\n\n/**\n * 添加更多\n */\nclass AddMoreDialog(val data: List<SimpleItemEntity>) :\n  BaseBottomSheetDialogFragment<DialogAddMoreBinding>() {\n\n  private lateinit var adapter: Adapter\n  private var listener: OnItemClickListener? = null\n\n  override fun setLayoutId(): Int {\n    return R.layout.dialog_add_more\n  }\n\n  interface OnItemClickListener {\n    fun onItemClick(\n      position: Int,\n      item: SimpleItemEntity,\n      view: View\n    )\n  }\n\n  override fun init(savedInstanceState: Bundle?) {\n    super.init(savedInstanceState)\n    adapter = Adapter(requireContext(), data)\n    binding.list.adapter = adapter\n    binding.list.setHasFixedSize(true)\n    binding.list.layoutManager = LinearLayoutManager(context)\n    RvItemClickSupport.addTo(binding.list)\n      .setOnItemClickListener { _, position, v ->\n        listener?.onItemClick(position, data[position], v)\n      }\n  }\n\n  fun setOnItemClickListener(listener: OnItemClickListener) {\n    this.listener = listener\n  }\n\n  fun notifyData() {\n    if (this::adapter.isInitialized) {\n      adapter.notifyDataSetChanged()\n    }\n  }\n\n  /**\n   * 适配器\n   */\n  private class Adapter(\n    context: Context,\n    data: List<SimpleItemEntity>\n  ) : AbsRVAdapter<SimpleItemEntity, Holder>(context, data) {\n\n    private class Holder(view: View) : AbsHolder(view) {\n      val img: AppCompatImageView = view.findViewById(R.id.img)\n      val text: TextView = view.findViewById(R.id.text)\n    }\n\n    override fun getViewHolder(\n      convertView: View?,\n      viewType: Int\n    ): Holder {\n      return Holder(convertView!!)\n    }\n\n    override fun setLayoutId(type: Int): Int {\n      return R.layout.item_simple\n    }\n\n    override fun bindData(\n      holder: Holder?,\n      position: Int,\n      item: SimpleItemEntity?\n    ) {\n      holder!!.text.text = item!!.title\n      holder.img.loadImg(item.icon)\n    }\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/dialog/ChooseTagDialog.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.view.dialog\n\nimport android.view.View\nimport androidx.core.view.isVisible\nimport androidx.lifecycle.ViewModelProvider\nimport androidx.lifecycle.lifecycleScope\nimport androidx.recyclerview.widget.LinearLayoutManager\nimport com.alibaba.android.arouter.facade.annotation.Autowired\nimport com.alibaba.android.arouter.facade.annotation.Route\nimport com.alibaba.android.arouter.launcher.ARouter\nimport com.arialyy.frame.router.Routerfit\nimport com.arialyy.frame.util.ResUtil\nimport com.keepassdroid.database.PwEntryV4\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.AbsViewBindingAdapter\nimport com.lyy.keepassa.base.BaseDialog\nimport com.lyy.keepassa.databinding.DialogChooseTagBinding\nimport com.lyy.keepassa.databinding.ItemChooseTagBinding\nimport com.lyy.keepassa.entity.TagBean\nimport com.lyy.keepassa.router.DialogRouter\nimport com.lyy.keepassa.util.KdbUtil\nimport com.lyy.keepassa.util.doOnItemClickListener\nimport com.lyy.keepassa.util.loadImg\nimport com.lyy.keepassa.view.create.entry.CreateEntryModule\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.flow.MutableSharedFlow\nimport kotlinx.coroutines.launch\nimport kotlinx.coroutines.withContext\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 10:39 AM 2023/10/26\n **/\n@Route(path = \"/dialog/chooseTag\")\ninternal class ChooseTagDialog : BaseDialog<DialogChooseTagBinding>() {\n  companion object {\n    val chooseTagFlow = MutableSharedFlow<List<TagBean>>(1)\n    val ADD_MORE = TagBean(ResUtil.getString(R.string.create_tag))\n  }\n\n  private lateinit var module: CreateEntryModule\n\n  @Autowired(name = \"entry\")\n  @JvmField\n  var entry: PwEntryV4? = null\n\n  @Autowired(name = \"newTag\")\n  @JvmField\n  var newTag: TagBean? = null\n\n  private val tagList = mutableListOf<TagBean>()\n\n  override fun setLayoutId(): Int {\n    return R.layout.dialog_choose_tag\n  }\n\n  override fun initData() {\n    super.initData()\n    ARouter.getInstance().inject(this)\n    module = ViewModelProvider(requireActivity())[CreateEntryModule::class.java]\n    binding.msgTitle = ResUtil.getString(R.string.add_tag)\n    val tagAdapter = TagAdapter()\n    binding.enableEnterBt = true\n    lifecycleScope.launch {\n      binding.rvList.apply {\n        setHasFixedSize(true)\n        layoutManager = LinearLayoutManager(context)\n        adapter = tagAdapter\n      }\n      withContext(Dispatchers.IO){\n        tagList.addAll(getTagData().toMutableList())\n      }\n      tagAdapter.setData(tagList)\n    }\n    binding.rvList.doOnItemClickListener { _, position, _ ->\n      val tagBean = tagList[position]\n      if (tagBean == ADD_MORE) {\n        module.cacheTag(tagList)\n        dismiss()\n        Routerfit.create(DialogRouter::class.java).showCreateTagDialog()\n        return@doOnItemClickListener\n      }\n      tagBean.isSet = !tagBean.isSet\n      tagAdapter.notifyItemChanged(position, tagBean.isSet)\n    }\n    binding.clicker = object : DialogBtnClicker {\n      override fun onEnter(v: View) {\n        lifecycleScope.launch {\n          chooseTagFlow.emit(tagList.filter { it.isSet })\n          dismiss()\n        }\n      }\n\n      override fun onCancel(v: View) {\n        dismiss()\n      }\n    }\n  }\n\n  private suspend fun getTagData(): List<TagBean> {\n    val tagList = arrayListOf<TagBean>()\n    val allTagList = KdbUtil.getAllTags()\n    entry?.let {\n      val curTagList = KdbUtil.getEntryTag(it)\n      allTagList.forEach { tag ->\n        tagList.add(TagBean(tag, tag in curTagList || tag in module.selectedTagBeanCache))\n      }\n    }\n    newTag?.let {\n      if (!allTagList.contains(it.tag)){\n        tagList.add(it)\n      }\n    }\n    tagList.add(ADD_MORE)\n    return tagList\n  }\n\n  private class TagAdapter : AbsViewBindingAdapter<TagBean, ItemChooseTagBinding>() {\n    override fun bindData(\n      binding: ItemChooseTagBinding,\n      item: TagBean,\n      payloads: MutableList<Any>\n    ) {\n      super.bindData(binding, item, payloads)\n      binding.cb.isChecked = item.isSet\n    }\n\n    override fun bindData(binding: ItemChooseTagBinding, item: TagBean) {\n      binding.cb.isVisible = item != ADD_MORE\n      binding.tvTitle.text = item.tag\n      binding.ivIcon.loadImg(if (item == ADD_MORE) R.drawable.ic_add_24px else R.drawable.ic_baseline_label_24)\n      binding.cb.isChecked = item.isSet\n    }\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/dialog/CloudFileListModule.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.view.dialog\n\nimport androidx.lifecycle.viewModelScope\nimport com.lyy.keepassa.base.BaseApp\nimport com.lyy.keepassa.base.BaseModule\nimport com.lyy.keepassa.entity.CloudServiceInfo\nimport com.lyy.keepassa.util.QuickUnLockUtil\nimport com.lyy.keepassa.util.cloud.CloudFileInfo\nimport com.lyy.keepassa.util.cloud.CloudUtilFactory\nimport com.lyy.keepassa.util.cloud.WebDavUtil\nimport com.lyy.keepassa.view.StorageType\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.flow.MutableSharedFlow\nimport kotlinx.coroutines.launch\nimport kotlinx.coroutines.withContext\nimport timber.log.Timber\nimport java.util.Date\n\n/**\n * 云文件列表module\n */\nclass CloudFileListModule : BaseModule() {\n  private val cache = HashMap<String, List<CloudFileInfo>?>()\n\n  val upEntry = CloudFileInfo(\"\", \"..\", Date(), 0, true)\n  val fileListFlow = MutableSharedFlow<List<CloudFileInfo>?>()\n\n  suspend fun saveWebHistory(uri: String) {\n    withContext(Dispatchers.IO) {\n      // 保存记录\n      val dao = BaseApp.appDatabase.cloudServiceInfoDao()\n      var data = dao.queryServiceInfo(uri)\n      if (data == null) {\n        data = CloudServiceInfo(\n          userName = QuickUnLockUtil.encryptStr(WebDavUtil.userName),\n          password = QuickUnLockUtil.encryptStr(WebDavUtil.password),\n          cloudPath = uri\n        )\n        dao.saveServiceInfo(data)\n        return@withContext\n      }\n      data.userName = QuickUnLockUtil.encryptStr(WebDavUtil.userName)\n      data.password = QuickUnLockUtil.encryptStr(WebDavUtil.password)\n      dao.updateServiceInfo(data)\n    }\n  }\n\n  /**\n   * 获取云盘根路径\n   */\n  fun getCloudRootPath(storageType: StorageType): String {\n    return CloudUtilFactory.getCloudUtil(storageType)\n      .getRootPath()\n  }\n\n  /**\n   * 获取云文件指定路径的文件列表\n   */\n  fun getFileList(\n    storageType: StorageType,\n    path: String,\n    isOnlyGetDir: Boolean\n  ) {\n    viewModelScope.launch {\n      val temp = cache[path]\n      if (temp != null && temp.isNotEmpty()) {\n        fileListFlow.emit(temp)\n        return@launch\n      }\n      val data = withContext(Dispatchers.IO) {\n        try {\n          val util = CloudUtilFactory.getCloudUtil(storageType)\n          val list = util.getFileList(path)\n          val tempList = if (isOnlyGetDir) {\n            list?.filter {\n              it.isDir\n            }\n          } else {\n            list?.sortedBy { !it.isDir }\n          }\n          cache[path] = tempList\n          return@withContext tempList\n        } catch (e: Exception) {\n          Timber.e(e)\n        }\n        null\n      }\n      fileListFlow.emit(data)\n    }\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/dialog/CloudFileSelectDialog.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.view.dialog\n\nimport android.content.Context\nimport android.content.res.AssetManager\nimport android.text.TextUtils\nimport android.view.KeyEvent\nimport android.view.View\nimport android.widget.ImageView\nimport android.widget.RelativeLayout\nimport android.widget.RelativeLayout.CENTER_VERTICAL\nimport android.widget.TextView\nimport androidx.lifecycle.ViewModelProvider\nimport androidx.lifecycle.lifecycleScope\nimport androidx.recyclerview.widget.LinearLayoutManager\nimport com.alibaba.android.arouter.facade.annotation.Autowired\nimport com.alibaba.android.arouter.facade.annotation.Route\nimport com.alibaba.android.arouter.launcher.ARouter\nimport com.arialyy.frame.util.ResUtil\nimport com.arialyy.frame.util.adapter.AbsHolder\nimport com.arialyy.frame.util.adapter.AbsRVAdapter\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseApp\nimport com.lyy.keepassa.base.BaseDialog\nimport com.lyy.keepassa.databinding.DialogCloudFileListBinding\nimport com.lyy.keepassa.event.ChangeDbEvent\nimport com.lyy.keepassa.event.CloudFileSelectedEvent\nimport com.lyy.keepassa.util.KeepassAUtil\nimport com.lyy.keepassa.util.cloud.CloudFileInfo\nimport com.lyy.keepassa.util.cloud.DbSynUtil\nimport com.lyy.keepassa.util.cloud.WebDavUtil\nimport com.lyy.keepassa.util.doOnItemClickListener\nimport com.lyy.keepassa.view.StorageType\nimport com.lyy.keepassa.view.StorageType.UNKNOWN\nimport com.lyy.keepassa.view.StorageType.WEBDAV\nimport com.lyy.keepassa.view.dialog.CloudFileSelectDialog.Adapter.Holder\nimport kotlinx.coroutines.flow.MutableSharedFlow\nimport kotlinx.coroutines.flow.collectLatest\nimport kotlinx.coroutines.launch\nimport org.greenrobot.eventbus.EventBus\nimport timber.log.Timber\nimport java.io.IOException\nimport java.util.Stack\n\n/**\n * 云文件列表\n */\n@Route(path = \"/dialog/cloudFileList\")\nclass CloudFileSelectDialog : BaseDialog<DialogCloudFileListBinding>() {\n\n  private val curDirList = ArrayList<CloudFileInfo>()\n  private lateinit var adapter: Adapter\n  private lateinit var module: CloudFileListModule\n  private val pathStack = Stack<String>()\n  private var lastPath: String = \"\"\n\n  val cloudFileSelectFlow = MutableSharedFlow<CloudFileSelectedEvent>()\n\n  @Autowired(name = \"storageType\")\n  @JvmField\n  var storageType: StorageType = UNKNOWN\n\n  @Autowired(name = \"onlyShowDir\")\n  @JvmField\n  var onlyGetDir = false\n\n  override fun setLayoutId(): Int {\n    return R.layout.dialog_cloud_file_list\n  }\n\n  override fun initData() {\n    super.initData()\n    ARouter.getInstance().inject(this)\n    module = ViewModelProvider(this)[CloudFileListModule::class.java]\n    adapter = Adapter(requireContext(), curDirList)\n    binding.list.adapter = adapter\n    binding.list.setHasFixedSize(true)\n    binding.list.layoutManager = LinearLayoutManager(context)\n    if (onlyGetDir){\n      binding.title.text = ResUtil.getString(R.string.select_save_path)\n    }\n\n    handleItemClick()\n    listenerGetFileList()\n\n    BaseApp.handler.postDelayed({\n      val rootPath = module.getCloudRootPath(storageType)\n      getFileList(rootPath)\n    }, 200)\n    binding.btnSelect.visibility = if (onlyGetDir) View.VISIBLE else View.GONE\n    binding.btnSelect.setOnClickListener {\n      lifecycleScope.launch {\n        val cloudPath =\n          if (storageType == WEBDAV) \"${WebDavUtil.getHostUri()}${lastPath}\" else lastPath\n        cloudFileSelectFlow.emit(CloudFileSelectedEvent(!onlyGetDir, cloudPath, storageType))\n        dismiss()\n      }\n    }\n  }\n\n  private fun listenerGetFileList() {\n    lifecycleScope.launch {\n      module.fileListFlow.collectLatest { list ->\n        hintLoadView()\n        curDirList.clear()\n        curDirList.add(0, module.upEntry)\n        if (!list.isNullOrEmpty()) {\n          curDirList.addAll(list)\n        }\n        adapter.notifyDataSetChanged()\n      }\n    }\n  }\n\n  private fun handleItemClick() {\n    binding.list.doOnItemClickListener { _, position, _ ->\n      if (position == 0) {\n        if (pathStack.isEmpty()) {\n          Timber.d(ResUtil.getString(R.string.error_is_root))\n          return@doOnItemClickListener\n        }\n        lastPath = pathStack.pop()\n        getFileList(lastPath)\n        return@doOnItemClickListener\n      }\n\n      val item = curDirList[position]\n      if (item.isDir) {\n        pathStack.push(lastPath)\n        lastPath = item.fileKey\n        getFileList(item.fileKey)\n        return@doOnItemClickListener\n      }\n      lifecycleScope.launch {\n        val cloudPath =\n          if (storageType == WEBDAV) \"${WebDavUtil.getHostUri()}${item.fileKey}\" else item.fileKey\n        Timber.d(\"couldPath = $cloudPath\")\n\n        if (storageType == WEBDAV) {\n          module.saveWebHistory(cloudPath)\n        }\n        cloudFileSelectFlow.emit(CloudFileSelectedEvent(!onlyGetDir, cloudPath, storageType))\n        // 选择文件\n        EventBus.getDefault()\n          .post(\n            ChangeDbEvent(\n              dbName = item.fileName,\n              localFileUri = DbSynUtil.getCloudDbTempPath(\n                storageType.name,\n                item.fileName\n              ),\n              cloudPath = cloudPath,\n              uriType = storageType\n            )\n          )\n        dismiss()\n      }\n    }\n    dialog!!.setOnKeyListener { _, keyCode, _ ->\n      if (keyCode == KeyEvent.KEYCODE_BACK && pathStack.size > 0) {\n        lastPath = pathStack.pop()\n        getFileList(lastPath)\n        return@setOnKeyListener true\n      }\n      return@setOnKeyListener false\n    }\n    binding.ivClose.setOnClickListener {\n      dismiss()\n    }\n  }\n\n  /**\n   * 获取文件列表\n   */\n  private fun getFileList(path: String) {\n    showLoadView()\n    val realPath = if (TextUtils.isEmpty(path)) module.getCloudRootPath(storageType) else path\n    binding.path.text = realPath\n    module.getFileList(storageType, realPath, onlyGetDir)\n  }\n\n  private fun showLoadView() {\n    binding.list.visibility = View.INVISIBLE\n    binding.anim.visibility = View.VISIBLE\n    binding.path.visibility = View.GONE\n  }\n\n  private fun hintLoadView() {\n    binding.list.visibility = View.VISIBLE\n    // binding.anim.cancelAnimation()\n    binding.anim.visibility = View.GONE\n    binding.path.visibility = View.VISIBLE\n  }\n\n  /**\n   * 列表adapter\n   */\n  private class Adapter(\n    context: Context,\n    fileList: List<CloudFileInfo>\n  ) : AbsRVAdapter<CloudFileInfo, Holder>(context, fileList) {\n\n    private class Holder(view: View) : AbsHolder(view) {\n      val icon: ImageView = view.findViewById(R.id.icon)\n      val title: TextView = view.findViewById(R.id.title)\n      val des: TextView = view.findViewById(R.id.des)\n    }\n\n    override fun getViewHolder(\n      convertView: View,\n      viewType: Int\n    ): Holder {\n      return Holder(convertView)\n    }\n\n    override fun setLayoutId(type: Int): Int {\n      return R.layout.item_cloud_file_list\n    }\n\n    override fun bindData(\n      holder: Holder,\n      position: Int,\n      item: CloudFileInfo\n    ) {\n      holder.title.text = item.fileName\n      if (item.isDir) {\n        holder.des.visibility = View.GONE\n        holder.icon.setImageResource(R.drawable.ic_folder_24px)\n        (holder.title.layoutParams as RelativeLayout.LayoutParams).addRule(CENTER_VERTICAL)\n      } else {\n        (holder.title.layoutParams as RelativeLayout.LayoutParams).removeRule(CENTER_VERTICAL)\n        holder.des.visibility = View.VISIBLE\n        holder.des.text = KeepassAUtil.instance.formatTime(item.serviceModifyDate)\n        holder.icon.setImageResource(R.drawable.ic_file_24px)\n      }\n    }\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/dialog/CreateTagDialog.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.view.dialog\n\nimport android.view.View\nimport android.widget.ArrayAdapter\nimport androidx.core.widget.doAfterTextChanged\nimport androidx.lifecycle.lifecycleScope\nimport com.alibaba.android.arouter.facade.annotation.Route\nimport com.alibaba.android.arouter.launcher.ARouter\nimport com.arialyy.frame.util.ResUtil\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseDialog\nimport com.lyy.keepassa.databinding.DialogCreateTagBinding\nimport com.lyy.keepassa.util.KdbUtil\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.flow.MutableSharedFlow\nimport kotlinx.coroutines.launch\nimport kotlinx.coroutines.withContext\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 7:53 PM 2023/10/26\n **/\n@Route(path = \"/dialog/createTag\")\nclass CreateTagDialog : BaseDialog<DialogCreateTagBinding>() {\n\n  companion object {\n    val createTagFlow = MutableSharedFlow<String?>(0)\n    val tagCacheSet = hashSetOf<String>()\n  }\n\n  override fun setLayoutId(): Int {\n    return R.layout.dialog_create_tag\n  }\n\n  override fun initData() {\n    super.initData()\n    ARouter.getInstance().inject(this)\n    binding.msgTitle = ResUtil.getString(R.string.create_tag)\n\n    binding.clicker = object : DialogBtnClicker {\n      override fun onEnter(v: View) {\n        dismiss()\n        lifecycleScope.launch {\n          val tag = binding.edTag.text.toString().trim()\n          createTagFlow.emit(tag)\n          tagCacheSet.add(tag)\n        }\n      }\n\n      override fun onCancel(v: View) {\n        dismiss()\n        lifecycleScope.launch {\n          createTagFlow.emit(null)\n        }\n      }\n    }\n    binding.edTag.doAfterTextChanged {\n      binding.enableEnterBt = (it?.length ?: 0) > 0\n    }\n    handleTagList()\n  }\n\n  private fun handleTagList() {\n    binding.edTag.threshold = 1 // 设置输入几个字符后开始出现提示 默认是2\n    binding.edTag.setOnFocusChangeListener { _, hasFocus ->\n      if (hasFocus) {\n        binding.edTag.showDropDown()\n      }\n    }\n\n    lifecycleScope.launch {\n      if (tagCacheSet.isEmpty()) {\n        withContext(Dispatchers.IO) {\n          tagCacheSet.addAll(KdbUtil.getAllTags())\n        }\n      }\n\n      binding.edTag.setAdapter(\n        ArrayAdapter(\n          requireContext(),\n          R.layout.android_simple_dropdown_item_1line,\n          tagCacheSet.toArray()\n        )\n      )\n\n    }\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/dialog/DialogBtnClicker.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\npackage com.lyy.keepassa.view.dialog\n\nimport android.view.View\nimport androidx.annotation.Keep\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 10:35 AM 2023/10/26\n **/\n@Keep\ninterface DialogBtnClicker {\n  fun onEnter(v: View){}\n\n  fun onCancel(v: View){}\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/dialog/DonateDialog.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.view.dialog\n\nimport android.content.ActivityNotFoundException\nimport android.content.ComponentName\nimport android.content.Intent\nimport android.net.Uri\nimport android.view.View\nimport com.arialyy.frame.router.Routerfit\nimport com.arialyy.frame.util.ResUtil\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseDialog\nimport com.lyy.keepassa.databinding.DialogDonateBinding\nimport com.lyy.keepassa.router.DialogRouter\nimport com.lyy.keepassa.util.PlayUtil\nimport com.lyy.keepassa.widget.DrawableTextView\nimport com.lyy.keepassa.widget.toPx\nimport com.zzhoujay.richtext.RichText\n\n/**\n * 捐赠对话框\n */\nclass DonateDialog : BaseDialog<DialogDonateBinding>(), View.OnClickListener {\n  override fun setLayoutId(): Int {\n    return R.layout.dialog_donate\n  }\n\n  override fun initData() {\n    super.initData()\n    binding.rlAliPay.setOnClickListener(this)\n    binding.rlPayPal.setOnClickListener(this)\n    binding.rlPayPal.setOnClickListener(this)\n    binding.ivClose.setOnClickListener(this)\n    RichText.fromMarkdown(getString(R.string.donate_desc))\n        .urlClick { url ->\n          startActivity(Intent(Intent.ACTION_VIEW).apply {\n            data = Uri.parse(url)\n          })\n          return@urlClick true\n        }\n        .into(binding.tvDesc)\n\n    binding.title.setDrawable(\n        DrawableTextView.LEFT,\n        ResUtil.getSvgIcon(R.drawable.ic_favorite_24px, R.color.text_blue_color),\n        24.toPx(),\n        24.toPx()\n    )\n    if (PlayUtil.playServiceExist(requireActivity())){\n      binding.rlPlay.visibility = View.VISIBLE\n    }\n  }\n\n  override fun onClick(v: View?) {\n    when (v?.id) {\n      R.id.rlAliPay -> {\n        startAliPay()\n      }\n      R.id.rlPayPal -> {\n        startActivity(Intent(Intent.ACTION_VIEW).apply {\n          data = Uri.parse(\"https://www.paypal.com/paypalme/arialyy\")\n        })\n      }\n      R.id.rlPlay ->{\n        Routerfit.create(DialogRouter::class.java).showPlayDonateDialog()\n      }\n    }\n    dismiss()\n  }\n\n  private fun startAliPay() {\n    val qrCode = \"https://qr.alipay.com/fkx19330ftk0okdlwzdk968\"\n    if (startAliPayIntentUrl(qrCode)) {\n      return\n    }\n    startActivity(Intent(Intent.ACTION_VIEW).apply {\n      data = Uri.parse(qrCode)\n    })\n  }\n\n  /**\n   * 打开 Intent Scheme Url\n   *\n   * @param qrCodeUrl Intent 跳转地址\n   * @return 是否成功调用\n   */\n  private fun startAliPayIntentUrl(\n    qrCodeUrl: String\n  ): Boolean {\n    return try {\n      val cn = ComponentName(\n          \"com.eg.android.AlipayGphone\",\n          \"com.alipay.mobile.quinox.SchemeLauncherActivity\"\n      )\n      val intent = Intent(Intent.ACTION_VIEW).apply {\n        component = cn\n        data =\n          Uri.parse(\"alipayqr://platformapi/startapp?saId=10000007&clientVersion=3.7.0.0718&qrcode=${qrCodeUrl}?_s=web-other&_t=${System.currentTimeMillis()}\")\n      }\n      startActivity(intent)\n      true\n    } catch (e: ActivityNotFoundException) {\n      false\n    }\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/dialog/ImgViewerDialog.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.view.dialog\n\nimport android.graphics.BitmapFactory\nimport androidx.fragment.app.DialogFragment\nimport com.alibaba.android.arouter.facade.annotation.Autowired\nimport com.alibaba.android.arouter.facade.annotation.Route\nimport com.alibaba.android.arouter.launcher.ARouter\nimport com.arialyy.frame.util.ResUtil\nimport com.blankj.utilcode.util.ToastUtils\nimport com.davemorrissey.labs.subscaleview.ImageSource\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseDialog\nimport com.lyy.keepassa.databinding.DialogImgViewerBinding\nimport com.lyy.keepassa.util.isInvalid\n\n/**\n * 图片浏览对话框\n */\n@Route(path = \"/dialog/imgViewer\")\nclass ImgViewerDialog() : BaseDialog<DialogImgViewerBinding>() {\n  init {\n    setStyle(DialogFragment.STYLE_NORMAL, R.style.FullScreenDialog)\n  }\n\n  @Autowired(name = \"imgByteArray\")\n  lateinit var imgByteArray: ByteArray\n\n  override fun setLayoutId(): Int {\n    return R.layout.dialog_img_viewer\n  }\n\n  override fun initData() {\n    super.initData()\n    ARouter.getInstance().inject(this)\n    binding.dialog = this\n    val bm = BitmapFactory.decodeByteArray(imgByteArray, 0, imgByteArray.size)\n    if (bm.isInvalid()){\n      ToastUtils.showLong(ResUtil.getString(R.string.invalid_img))\n      dismiss()\n      return\n    }\n    binding.imageView.setImage(\n      ImageSource.bitmap(bm)\n    )\n  }\n\n  override fun dismiss() {\n    super.dismiss()\n    binding.imageView.recycle()\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/dialog/LoadingDialog.java",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.view.dialog;\n\nimport android.content.res.AssetManager;\nimport android.view.ViewGroup;\nimport androidx.fragment.app.DialogFragment;\nimport com.alibaba.android.arouter.facade.annotation.Route;\nimport com.lyy.keepassa.R;\nimport com.lyy.keepassa.base.BaseApp;\nimport com.lyy.keepassa.base.BaseDialog;\nimport com.lyy.keepassa.databinding.DialogLoadingBinding;\nimport java.io.IOException;\nimport timber.log.Timber;\n\n/**\n * Created by AriaL on 2017/12/15.\n */\n@Route(path = \"/dialog/loading\")\npublic class LoadingDialog extends BaseDialog<DialogLoadingBinding> {\n\n  @Override protected void initData() {\n    super.initData();\n    setStyle(DialogFragment.STYLE_NORMAL, android.R.style.Theme_Black_NoTitleBar_Fullscreen);\n    getDialog().getWindow()\n        .setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);\n\n    setCancelable(false);\n    try {\n      getBinding().anim.setAnimation(\n          requireContext().getAssets().open(\"loadingAnimation.json\", AssetManager.ACCESS_STREAMING),\n          \"LottieCache\");\n    } catch (IOException e) {\n      Timber.e(e);\n    }\n  }\n\n  @Override\n  protected int setLayoutId() {\n    return R.layout.dialog_loading;\n  }\n\n  public void dismiss(long delay) {\n    if (delay == 0) {\n      super.dismiss();\n      return;\n    }\n    BaseApp.handler.postDelayed(this::dismiss, delay);\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/dialog/ModifyGroupDialog.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.view.dialog\n\nimport android.view.View\nimport com.alibaba.android.arouter.facade.annotation.Autowired\nimport com.alibaba.android.arouter.facade.annotation.Route\nimport com.alibaba.android.arouter.launcher.ARouter\nimport com.keepassdroid.database.PwGroupV4\nimport com.keepassdroid.database.PwIconCustom\nimport com.keepassdroid.database.PwIconStandard\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseDialog\nimport com.lyy.keepassa.databinding.DialogAddGroupBinding\nimport com.lyy.keepassa.util.HitUtil\nimport com.lyy.keepassa.util.IconUtil\nimport com.lyy.keepassa.util.KpaUtil\nimport com.lyy.keepassa.view.icon.IconBottomSheetDialog\nimport com.lyy.keepassa.view.icon.IconItemCallback\n\n/**\n * 编辑群组\n */\n@Route(path = \"/dialog/modifyGroup\")\nclass ModifyGroupDialog : BaseDialog<DialogAddGroupBinding>(), View.OnClickListener {\n\n  private var icon: PwIconStandard = PwIconStandard(1)\n  private var csIcon: PwIconCustom? = null\n\n  @Autowired(name = \"pwGroup\")\n  @JvmField\n  var pwGroup: PwGroupV4? = null\n\n  override fun setLayoutId(): Int {\n    return R.layout.dialog_add_group\n  }\n\n  override fun initData() {\n    super.initData()\n    ARouter.getInstance().inject(this)\n    if (pwGroup == null) {\n      dismiss()\n      return\n    }\n    binding.groupNameLayout.setEndIconOnClickListener {\n      showIconDialog()\n    }\n    binding.enter.setOnClickListener(this)\n    binding.cancel.setOnClickListener(this)\n    binding.title.text = getString(R.string.modify_group)\n\n    pwGroup?.let {\n      binding.groupName.setText(it.name)\n      binding.groupNameLayout.endIconDrawable =\n        IconUtil.getGroupIconDrawable(requireContext(), it, true)\n      icon = it.icon\n      csIcon = it.customIcon\n    }\n  }\n\n  private fun showIconDialog() {\n    val iconDialog = IconBottomSheetDialog()\n    iconDialog.setCallback(object : IconItemCallback {\n      override fun onDefaultIcon(defIcon: PwIconStandard) {\n        icon = defIcon\n        binding.groupNameLayout.endIconDrawable =\n          resources.getDrawable(IconUtil.getIconById(icon.iconId), requireContext().theme)\n        csIcon = PwIconCustom.ZERO\n      }\n\n      override fun onCustomIcon(customIcon: PwIconCustom) {\n        csIcon = customIcon\n        binding.groupNameLayout.endIconDrawable =\n          IconUtil.convertCustomIcon2Drawable(requireContext(), csIcon!!)\n      }\n    })\n    iconDialog.show(childFragmentManager, IconBottomSheetDialog::class.java.simpleName)\n  }\n\n  override fun onClick(v: View?) {\n    when (v!!.id) {\n      R.id.enter -> {\n        val newTitle = binding.groupName.text.toString()\n          .trim()\n        if (newTitle.isEmpty()) {\n          HitUtil.toaskShort(getString(R.string.error_group_name_null))\n          return\n        }\n        KpaUtil.kdbHandlerService.modifyGroup(newTitle, icon, csIcon, pwGroup!!) {\n          dismiss()\n        }\n      }\n      R.id.cancel -> {\n        dismiss()\n      }\n    }\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/dialog/ModifyPassDialog.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.view.dialog\n\nimport android.text.InputType\nimport android.view.View\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseDialog\nimport com.lyy.keepassa.databinding.DialogModifyPassBinding\nimport com.lyy.keepassa.event.ModifyPassEvent\nimport com.lyy.keepassa.util.HitUtil\nimport org.greenrobot.eventbus.EventBus\n\n/**\n * 修改密码对话框\n */\nclass ModifyPassDialog : BaseDialog<DialogModifyPassBinding>(), View.OnClickListener {\n  private var isShowPass = false\n\n  override fun setLayoutId(): Int {\n    return R.layout.dialog_modify_pass\n  }\n\n  override fun initData() {\n    super.initData()\n    handlePassLayout()\n    binding.enter.setOnClickListener(this)\n    binding.cancel.setOnClickListener(this)\n  }\n\n  /**\n   * 处理密码\n   */\n  private fun handlePassLayout() {\n    binding.passwordLayout.endIconDrawable = resources.getDrawable(R.drawable.ic_view_off)\n\n    binding.passwordLayout.setEndIconOnClickListener {\n      isShowPass = !isShowPass\n      if (isShowPass) {\n        binding.passwordLayout.endIconDrawable = resources.getDrawable(R.drawable.ic_view)\n        binding.enterPasswordLayout.visibility = View.GONE\n        binding.password.inputType = InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD\n      } else {\n        binding.passwordLayout.endIconDrawable =\n          resources.getDrawable(R.drawable.ic_view_off)\n        binding.enterPasswordLayout.visibility = View.VISIBLE\n        binding.password.inputType =\n          InputType.TYPE_TEXT_VARIATION_PASSWORD or InputType.TYPE_CLASS_TEXT\n      }\n      // 将光标移动到最后\n      binding.password.setSelection(binding.password.text!!.length)\n      binding.password.requestFocus()\n    }\n  }\n\n  override fun onClick(v: View?) {\n    val pass = binding.password.text.toString()\n        .trim()\n    val enterPass = binding.enterPassword.text.toString()\n        .trim()\n\n    if (v!!.id == R.id.enter) {\n      if (pass.length < 6) {\n        HitUtil.toaskShort(getString(R.string.error_db_pass_too_short))\n        return\n      }\n      if (pass.isEmpty()) {\n        HitUtil.toaskShort(getString(R.string.error_pass_null))\n        return\n      }\n\n      if (pass.isNotEmpty() && pass != enterPass) {\n        HitUtil.toaskShort(getString(R.string.error_pass_unfit))\n        return\n      }\n      EventBus.getDefault().post(ModifyPassEvent(pass))\n    }\n    dismiss()\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/dialog/MsgDialog.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.view.dialog\n\nimport android.annotation.SuppressLint\nimport android.graphics.drawable.Drawable\nimport android.view.KeyEvent\nimport android.view.View\nimport android.widget.Button\nimport androidx.lifecycle.lifecycleScope\nimport com.alibaba.android.arouter.facade.annotation.Autowired\nimport com.alibaba.android.arouter.facade.annotation.Route\nimport com.alibaba.android.arouter.launcher.ARouter\nimport com.arialyy.frame.util.ResUtil\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseDialog\nimport com.lyy.keepassa.databinding.DialogMsgBinding\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.delay\nimport kotlinx.coroutines.launch\nimport kotlinx.coroutines.withContext\n\n/**\n * @author laoyuyu\n * @date 2021/9/5\n */\n@Route(path = \"/dialog/msgDialog\")\nclass MsgDialog : BaseDialog<DialogMsgBinding>(), View.OnClickListener {\n\n  @Autowired(name = \"enterBtTextColor\")\n  @JvmField\n  var enterBtTextColor: Int = R.color.text_blue_color\n\n  @Autowired(name = \"cancelBtTextColor\")\n  @JvmField\n  var cancelBtTextColor: Int = R.color.text_gray_color\n\n  @Autowired(name = \"coverBtTextColor\")\n  @JvmField\n  var coverBtTextColor: Int = R.color.text_blue_color\n\n  @Autowired(name = \"msgTitle\")\n  @JvmField\n  var msgTitle: CharSequence = \"\"\n\n  @Autowired(name = \"msgContent\")\n  @JvmField\n  var msgContent: CharSequence = \"\"\n\n  @Autowired(name = \"showCancelBt\")\n  @JvmField\n  var showCancelBt: Boolean = true // 显示取消按钮\n\n  @Autowired(name = \"showEnterBt\")\n  @JvmField\n  var showEnterBt: Boolean = true // 显示确认按钮\n\n  @Autowired(name = \"showCoverBt\")\n  @JvmField\n  var showCoverBt: Boolean = false // 显示覆盖按钮\n\n  @Autowired(name = \"showCountDownTimer\")\n  @JvmField\n  var showCountDownTimer: Pair<Boolean, Int> = Pair(false, 5) // 显示确认按钮倒计时定时器，倒计时5s\n\n  @Autowired(name = \"interceptBackKey\")\n  @JvmField\n  var interceptBackKey: Boolean = false // 是否拦截返回键\n\n  @Autowired(name = \"msgTitleEndIcon\")\n  @JvmField\n  var msgTitleEndIcon: Drawable? = null\n\n  @Autowired(name = \"msgTitleStartIcon\")\n  @JvmField\n  var msgTitleStartIcon: Drawable? = null\n\n  @Autowired(name = \"enterText\")\n  @JvmField\n  var enterText: CharSequence = \"\"\n\n  @Autowired(name = \"coverText\")\n  @JvmField\n  var coverText: CharSequence = \"\"\n\n  @Autowired(name = \"cancelText\")\n  @JvmField\n  var cancelText: CharSequence = \"\"\n\n  @Autowired(name = \"btnClickListener\")\n  @JvmField\n  var btnClickListener: OnMsgBtClickListener? = null\n\n  override fun setLayoutId(): Int {\n    return R.layout.dialog_msg\n  }\n\n  override fun initData() {\n    super.initData()\n    ARouter.getInstance().inject(this)\n\n    msgTitleEndIcon?.let {\n      binding.tvTitle.setEndIcon(it)\n    }\n\n    msgTitleStartIcon?.let {\n      binding.tvTitle.setLeftIcon(it)\n    }\n\n    handleCountDown()\n    if (interceptBackKey) {\n      dialog!!.setOnKeyListener { _, keyCode, _ ->\n        return@setOnKeyListener keyCode == KeyEvent.KEYCODE_BACK\n      }\n    }\n    binding.dialog = this\n  }\n\n  /**\n   * 设置标题左边icon\n   */\n  fun setTitleStartIcon(icon: Drawable): MsgDialog {\n    msgTitleStartIcon = icon\n    return this\n  }\n\n  /**\n   * 设置标题右边icon\n   */\n  fun setTitleEndIcon(icon: Drawable): MsgDialog {\n    msgTitleEndIcon = icon\n    return this\n  }\n\n  /**\n   * 处理倒计时，倒计时结束前，确认按钮，取消按钮，覆盖按钮都不可选择\n   */\n  @SuppressLint(\"SetTextI18n\")\n  private fun handleCountDown() {\n    if (showCountDownTimer.first) {\n      setBtnsEnable(false)\n      lifecycleScope.launch(Dispatchers.Main) {\n        val oldText = binding.enter.text\n        for (i in showCountDownTimer.second downTo 1) {\n          binding.enter.text = \"$oldText (${i} s) \"\n          withContext(Dispatchers.IO) {\n            delay(1000)\n          }\n        }\n        binding.enter.text = oldText\n        setBtnsEnable(true)\n      }\n    }\n  }\n\n  private fun setBtnsEnable(enable: Boolean) {\n    val btns = arrayListOf(binding.cover, binding.enter, binding.cancel)\n    for (btn in btns) {\n      if (btn.visibility == View.GONE) {\n        continue\n      }\n      if (enable) {\n        btn.isEnabled = true\n        btn.setTextColor(ResUtil.getColor(R.color.text_blue_color))\n        btn.background = ResUtil.getDrawable(R.drawable.ripple_white_selector)\n      } else {\n        btn.isEnabled = false\n        btn.setTextColor(ResUtil.getColor(R.color.text_gray_color))\n        btn.setBackgroundColor(ResUtil.getColor(R.color.transparent))\n      }\n    }\n  }\n\n  override fun onClick(v: View?) {\n    when (v!!.id) {\n      R.id.enter -> {\n        btnClickListener?.onEnter(v as Button)\n      }\n      R.id.cancel -> {\n        btnClickListener?.onCancel(v as Button)\n      }\n      R.id.cover -> {\n        btnClickListener?.onCover(v as Button)\n      }\n    }\n    dismiss()\n  }\n\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/dialog/OnMsgBtClickListener.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\npackage com.lyy.keepassa.view.dialog;\n\nimport android.widget.Button\nimport java.io.Serializable\n\n/**\n * @author laoyuyu\n * @date 2021/9/5\n */\ninterface OnMsgBtClickListener {\n\n  fun onCover(v: Button){}\n\n  fun onEnter(v: Button)\n\n  fun onCancel(v: Button)\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/dialog/PlayDonateDialog.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.view.dialog\n\nimport android.view.View\nimport android.view.ViewGroup\nimport androidx.lifecycle.ViewModelProvider\nimport androidx.lifecycle.lifecycleScope\nimport com.alibaba.android.arouter.facade.annotation.Route\nimport com.blankj.utilcode.util.ToastUtils\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseDialog\nimport com.lyy.keepassa.databinding.DialogPlayDonateBinding\nimport kotlinx.coroutines.flow.collectLatest\nimport kotlinx.coroutines.launch\nimport timber.log.Timber\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 2022/2/7\n **/\n@Route(path = \"/dialog/playDonate\")\nclass PlayDonateDialog : BaseDialog<DialogPlayDonateBinding>() {\n  private lateinit var module: PlayDonateModule\n\n  override fun setLayoutId(): Int {\n    return R.layout.dialog_play_donate\n  }\n\n  override fun onStart() {\n    super.onStart()\n    dialog?.window?.setLayout(\n      resources.getDimension(R.dimen.dialog_min_width).toInt(),\n      ViewGroup.LayoutParams.WRAP_CONTENT\n    )\n  }\n\n  override fun initData() {\n    super.initData()\n    module = ViewModelProvider(this)[PlayDonateModule::class.java]\n    binding.dialog = this\n    binding.slider.addOnChangeListener { _, value, _ ->\n      Timber.d(\"value = $value\")\n      module.curIndex = value\n      binding.tvMoney.text = module.convertValue(value)\n    }\n    binding.slider.setLabelFormatter { it ->\n      module.convertValue(it)\n    }\n    lifecycleScope.launch {\n      module.playFlow.collectLatest {\n        if (it == PlayDonateModule.STATE_DEFAULT){\n          return@collectLatest\n        }\n        val resId = when (it) {\n          PlayDonateModule.STATE_CONNECT_PLAY_SERVICE_ERROR -> {\n            R.string.error_connect_play\n          }\n          PlayDonateModule.STATE_DONATE_SUCCESS -> {\n            dismiss()\n            R.string.thank_donate\n          }\n          PlayDonateModule.STATE_DONATE_FAIL -> {\n            R.string.error_donate\n          }\n          else -> {\n            R.string.error_donate\n          }\n        }\n        ToastUtils.showLong(resId)\n      }\n    }\n  }\n\n  fun onClick(v: View) {\n    when (v.id) {\n      R.id.enter -> {\n        module.startFlow(requireActivity(), module.curIndex)\n      }\n      R.id.cancel -> {\n        dismiss()\n      }\n    }\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/dialog/PlayDonateModule.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.view.dialog\n\nimport android.app.Activity\nimport androidx.lifecycle.viewModelScope\nimport com.android.billingclient.api.BillingClient\nimport com.android.billingclient.api.BillingClient.BillingResponseCode\nimport com.android.billingclient.api.BillingClient.SkuType\nimport com.android.billingclient.api.BillingClientStateListener\nimport com.android.billingclient.api.BillingFlowParams\nimport com.android.billingclient.api.BillingResult\nimport com.android.billingclient.api.ConsumeParams\nimport com.android.billingclient.api.Purchase\nimport com.android.billingclient.api.Purchase.PurchaseState\nimport com.android.billingclient.api.PurchasesUpdatedListener\nimport com.android.billingclient.api.SkuDetails\nimport com.android.billingclient.api.SkuDetailsParams\nimport com.android.billingclient.api.SkuDetailsResult\nimport com.android.billingclient.api.consumePurchase\nimport com.android.billingclient.api.querySkuDetails\nimport com.arialyy.frame.util.ResUtil\nimport com.blankj.utilcode.util.ToastUtils\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseApp\nimport com.lyy.keepassa.base.BaseModule\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.flow.MutableStateFlow\nimport kotlinx.coroutines.launch\nimport kotlinx.coroutines.withContext\nimport timber.log.Timber\n\n/**\n * @Author laoyuyu\n * @Description https://developer.android.com/google/play/billing/integrate?hl=zh-cn#fetch\n * @Date 2:32 下午 2022/2/7\n **/\nclass PlayDonateModule : BaseModule() {\n\n  companion object{\n    const val STATE_CONNECT_PLAY_SERVICE_ERROR = -1\n    const val STATE_DONATE_SUCCESS = 0\n    const val STATE_DONATE_FAIL = 1\n    const val STATE_DEFAULT = 999\n  }\n\n  private var isConnected = false\n  private var skuResult: SkuDetailsResult? = null\n  var curIndex = 1f\n  val playFlow = MutableStateFlow(STATE_DEFAULT)\n\n  private val skuList = arrayListOf<String>().apply {\n    add(\"d1_cddobn39u5ugvvvsn2fqvd5ktzfhnxqr\")\n    add(\"d5_wgi5ukrzfj4259m77s2ymn2fkzy5cvgc\")\n    add(\"d10_5h35r3juggi7pwyiy4mqi7zgbehd9kdb\")\n    add(\"d20_wswi2fefwoaswb9u9bmmk35vhd9rhpsy\")\n    add(\"d50_942stx4ouh4dk7suz7j9yrvjpwtwi9np\")\n  }\n\n  private val skuDetailMap = hashMapOf<String, SkuDetails>()\n\n  /**\n   * 购买回调\n   */\n  private val purchasesUpdatedListener = PurchasesUpdatedListener { billingResult, purchases ->\n    // To be implemented in a later section.\n    Timber.d(\"result = ${billingResult}, purchases = $purchases\")\n    if (billingResult.responseCode == BillingResponseCode.OK && purchases != null) {\n      Timber.d(\"purchases success\")\n      for (purchase in purchases) {\n        handlePurchase(purchase)\n      }\n    } else if (billingResult.responseCode == BillingResponseCode.USER_CANCELED) {\n      Timber.d(\"cancel\")\n      // Handle an error caused by a user cancelling the purchase flow.\n    } else {\n      // Handle any other error codes.\n    }\n  }\n\n  private var billingClient = BillingClient.newBuilder(BaseApp.APP)\n    .setListener(purchasesUpdatedListener)\n    .enablePendingPurchases()\n    .build()\n\n  /**\n   * 确认非消耗型商品的购买交易\n   */\n  private fun handlePurchase(purchase: Purchase) {\n    if (purchase.purchaseState == PurchaseState.PURCHASED) {\n      // if (!purchase.isAcknowledged) {\n      //   val acknowledgePurchaseParams = AcknowledgePurchaseParams.newBuilder()\n      //     .setPurchaseToken(purchase.purchaseToken)\n      //   billingClient.acknowledgePurchase(acknowledgePurchaseParams.build()) {\n      //     Timber.d(\"code = ${it.responseCode}\")\n      //   }\n      // }\n\n      val consumeParams =\n        ConsumeParams.newBuilder()\n          .setPurchaseToken(purchase.purchaseToken)\n          .build()\n      viewModelScope.launch {\n        val consumeResult = withContext(Dispatchers.IO) {\n          billingClient.consumePurchase(consumeParams)\n        }\n        playFlow.emit(STATE_DONATE_SUCCESS)\n      }\n    }\n  }\n\n  /**\n   * 1、查询sku\n   * 2、进行支付\n   */\n  fun startFlow(ac: Activity, value: Float) {\n    if (isConnected) {\n      startQuerySdk(ac, value)\n      return\n    }\n    connectPlay {\n      if (!it){\n        viewModelScope.launch {\n          playFlow.emit(STATE_CONNECT_PLAY_SERVICE_ERROR)\n        }\n        return@connectPlay\n      }\n      if (it) {\n        startQuerySdk(ac, value)\n      }\n    }\n  }\n\n  private fun startQuerySdk(ac: Activity, value: Float) {\n    viewModelScope.launch {\n      queryInappSkuDetails()\n      val curSku = skuList[value.toInt() - 1]\n      Timber.d(\"curSku = $curSku\")\n      val skuDetail = skuDetailMap[curSku]\n      if (skuDetail == null) {\n        playFlow.emit(STATE_DONATE_FAIL)\n        return@launch\n      }\n      startPlayFlow(ac, skuDetail)\n    }\n  }\n\n  /**\n   * 开始支付流程\n   */\n  private fun startPlayFlow(ac: Activity, skuDetail: SkuDetails) {\n    val flowParams = BillingFlowParams.newBuilder()\n      .setSkuDetails(skuDetail)\n      .build()\n    val responseCode = billingClient.launchBillingFlow(ac, flowParams).responseCode\n    if (responseCode != BillingResponseCode.OK){\n      viewModelScope.launch {\n        playFlow.emit(STATE_DONATE_FAIL)\n      }\n    }\n    Timber.d(\"responseCode = $responseCode\")\n  }\n\n  /**\n   * connect to play\n   */\n  private fun connectPlay(callback: (Boolean) -> Unit) {\n    billingClient.startConnection(object : BillingClientStateListener {\n      override fun onBillingSetupFinished(billingResult: BillingResult) {\n        if (billingResult.responseCode == BillingResponseCode.OK) {\n          // The BillingClient is ready. You can query purchases here.\n          isConnected = true\n          callback.invoke(true)\n          return\n        }\n        ToastUtils.showLong(ResUtil.getString(R.string.error_connect_play))\n        callback.invoke(false)\n      }\n\n      override fun onBillingServiceDisconnected() {\n        // Try to restart the connection on the next request to\n        // Google Play by calling the startConnection() method.\n        isConnected = false\n      }\n    })\n  }\n\n  /**\n   * 普通商品\n   */\n  suspend fun queryInappSkuDetails(): SkuDetailsResult? {\n    if (skuResult != null) {\n      return skuResult\n    }\n    val params = SkuDetailsParams.newBuilder()\n    params.setSkusList(skuList).setType(SkuType.INAPP)\n\n    // leverage querySkuDetails Kotlin extension function\n    return withContext(Dispatchers.IO) {\n      skuResult = billingClient.querySkuDetails(params.build())\n      val skuList = skuResult?.skuDetailsList\n      skuList?.forEach {\n        skuDetailMap[it.sku] = it\n      }\n      // val skuResult = skuResult.billingResult\n      Timber.d(\"get inapp sku success, size = ${skuList?.size}\")\n      return@withContext skuResult\n    }\n\n    // Process the result.\n  }\n\n  fun convertValue(value: Float): String {\n    return when (value.toInt()) {\n      1 -> {\n        \"$1\"\n      }\n      2 -> {\n        \"$5\"\n      }\n      3 -> {\n        \"$10\"\n      }\n      4 -> {\n        \"$20\"\n      }\n      5 -> {\n        \"$50\"\n      }\n      else -> {\n        \"$1\"\n      }\n    }\n  }\n\n  override fun onCleared() {\n    super.onCleared()\n    billingClient.endConnection()\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/dialog/TimeChangeDialog.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.view.dialog\n\nimport android.os.Build\nimport android.os.Bundle\nimport android.view.LayoutInflater\nimport android.view.View\nimport android.view.ViewGroup\nimport android.widget.DatePicker\nimport android.widget.FrameLayout\nimport android.widget.TimePicker\nimport androidx.fragment.app.Fragment\nimport androidx.lifecycle.lifecycleScope\nimport androidx.viewpager2.adapter.FragmentStateAdapter\nimport com.alibaba.android.arouter.facade.annotation.Route\nimport com.arialyy.frame.util.DpUtils\nimport com.google.android.material.tabs.TabLayoutMediator\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseDialog\nimport com.lyy.keepassa.databinding.DialogTimerBinding\nimport com.lyy.keepassa.event.TimeEvent\nimport kotlinx.coroutines.flow.MutableStateFlow\nimport kotlinx.coroutines.launch\n\n/**\n * 时间选择器\n */\n@Route(path = \"/dialog/timeChange\")\nclass TimeChangeDialog : BaseDialog<DialogTimerBinding>(), View.OnClickListener {\n  private lateinit var vpAdapter: VpAdapter\n  private val fragments = arrayListOf<Fragment>()\n\n  companion object {\n    val timeFlow = MutableStateFlow<TimeEvent?>(null)\n  }\n\n  override fun setLayoutId(): Int {\n    return R.layout.dialog_timer\n  }\n\n  override fun initData() {\n    super.initData()\n    val titles = listOf(getString(R.string.date), getString(R.string.time))\n    fragments.add(DatePickerFragment())\n    fragments.add(TimerPickerFragment())\n    vpAdapter = VpAdapter(fragments, this)\n    binding.vp.adapter = vpAdapter\n    binding.vp.offscreenPageLimit = 2\n    TabLayoutMediator(binding.tabLayout, binding.vp) { tab, position ->\n      tab.text = titles[position]\n    }.attach()\n    binding.cancel.setOnClickListener(this)\n    binding.save.setOnClickListener(this)\n  }\n\n  override fun onClick(v: View?) {\n    when (v!!.id) {\n      R.id.cancel -> dismiss()\n      R.id.save -> {\n        val date = (fragments[0] as DatePickerFragment).datePicker\n        val time = (fragments[1] as TimerPickerFragment).timerPicker\n        val event: TimeEvent = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {\n          TimeEvent(\n            date.year,\n            date.month + 1,\n            date.dayOfMonth,\n            time.currentHour,\n            time.currentMinute\n          )\n        } else {\n          TimeEvent(date.year, date.month + 1, date.dayOfMonth, time.hour, time.minute)\n        }\n        lifecycleScope.launch {\n          timeFlow.emit(event)\n        }\n        dismiss()\n      }\n    }\n  }\n\n  override fun onStart() {\n    super.onStart()\n    dialog?.window?.setLayout(DpUtils.dp2px(280), DpUtils.dp2px(530))\n  }\n\n  private class VpAdapter(\n    private val fragments: List<Fragment>,\n    fm: Fragment\n  ) : FragmentStateAdapter(fm) {\n\n    override fun getItemCount(): Int {\n      return fragments.size\n    }\n\n    override fun createFragment(position: Int): Fragment {\n      return fragments[position]\n    }\n  }\n\n  class DatePickerFragment : Fragment() {\n    val datePicker by lazy {\n      DatePicker(requireContext())\n    }\n\n    override fun onCreateView(\n      inflater: LayoutInflater,\n      container: ViewGroup?,\n      savedInstanceState: Bundle?\n    ): View {\n      datePicker.layoutParams = FrameLayout.LayoutParams(\n        FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT\n      )\n      return datePicker\n    }\n  }\n\n  class TimerPickerFragment : Fragment() {\n    val timerPicker by lazy {\n      TimePicker(requireContext())\n    }\n\n    override fun onCreateView(\n      inflater: LayoutInflater,\n      container: ViewGroup?,\n      savedInstanceState: Bundle?\n    ): View {\n      timerPicker.layoutParams = FrameLayout.LayoutParams(\n        FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT\n      )\n\n      return timerPicker\n    }\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/dialog/TipsDialog.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.view.dialog\n\nimport android.text.SpannableStringBuilder\nimport com.alibaba.android.arouter.facade.annotation.Route\nimport com.arialyy.frame.util.ResUtil\nimport com.blankj.utilcode.util.SpanUtils\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseDialog\nimport com.lyy.keepassa.base.KeyConstance\nimport com.lyy.keepassa.databinding.DialogTipBinding\nimport com.lyy.keepassa.util.CommonKVStorage\nimport com.lyy.keepassa.util.doClick\nimport com.zzhoujay.richtext.RichText\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 4:05 PM 2023/5/10\n **/\n@Route(path = \"/dialog/tipsDialog\")\nclass TipsDialog : BaseDialog<DialogTipBinding>() {\n  override fun setLayoutId(): Int {\n    return R.layout.dialog_tip\n  }\n\n  override fun initData() {\n    super.initData()\n    val vector = ResUtil.getSvgIcon(R.drawable.ic_lightbulb_on, R.color.colorPrimary)\n    binding.msgTitle = ResUtil.getString(R.string.title_tip_of_day)\n    binding.includeLayout.tvTitle.setLeftIcon(vector!!)\n    binding.cancel.doClick {\n      dismiss()\n    }\n\n    binding.cbShow.isChecked = CommonKVStorage.getBoolean(KeyConstance.KEY_DONT_SHOW_TIP, false)\n\n    binding.cbShow.setOnCheckedChangeListener { _, isChecked ->\n      CommonKVStorage.put(KeyConstance.KEY_DONT_SHOW_TIP, isChecked)\n    }\n    bindingContent()\n  }\n\n  private fun bindingContent() {\n    getDarkStr()\n  }\n\n  private fun getDarkStr(): SpannableStringBuilder {\n    return SpanUtils.with(binding.tvContent)\n      .append(\"夜间模式\\n\")\n      .setBold()\n      .setFontSize(16, true)\n      .append(\"应用设置->UI设置->主题风格\\n\")\n      .setFontSize(14, true)\n      .appendImage(R.drawable.tip_1_0)\n      .append(\"选择夜间模式\\n\")\n      .setFontSize(14, true)\n      .appendImage(R.drawable.tip_1_1)\n      .create()\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/dialog/WebDavLoginModule.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.view.dialog\n\nimport android.content.Context\nimport androidx.lifecycle.liveData\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseApp\nimport com.lyy.keepassa.base.BaseModule\nimport com.lyy.keepassa.entity.CloudServiceInfo\nimport com.lyy.keepassa.util.HitUtil\nimport com.lyy.keepassa.util.QuickUnLockUtil\nimport com.lyy.keepassa.util.cloud.WebDavUtil\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.flow.flow\nimport kotlinx.coroutines.withContext\nimport timber.log.Timber\n\nclass WebDavLoginModule : BaseModule() {\n\n  var curWebDavServer: String? = null\n\n  fun isNextcloud() = curWebDavServer == WebDavUtil.SUPPORTED_WEBDAV_URLS[3]\n\n  fun isOtherServer() =\n    curWebDavServer == WebDavUtil.SUPPORTED_WEBDAV_URLS[WebDavUtil.SUPPORTED_WEBDAV_URLS.size - 1]\n\n  fun isJGY() = curWebDavServer == WebDavUtil.SUPPORTED_WEBDAV_URLS[0]\n\n  fun convertHost(hostName: String, port: String, userName: String): String {\n    val hasHttp = hostName.startsWith(\"http\", true)\n    val isHttps = hostName.startsWith(\"https\", true) || port == \"443\"\n    var temp = hostName\n    if (hasHttp) {\n      val index = hostName.indexOf(\"://\")\n      if (index != -1) {\n        temp = hostName.substring(index + 2, hostName.length)\n      }\n    }\n    return \"http${if (isHttps) \"s\" else \"\"}://${temp}${if (port.isEmpty()) \"\" else \":${port}\"}/remote.php/dav/files/${userName}/\"\n  }\n\n  fun checkLogin(\n    uri: String,\n    userName: String,\n    pass: String,\n    isPreemptive:Boolean\n  ) = flow {\n    val b = withContext(Dispatchers.IO) {\n      return@withContext WebDavUtil.checkLogin(uri, userName, pass, isPreemptive)\n    }\n    emit(b)\n  }\n\n  /**\n   * 检查登录状态\n   * 如果是创建数据库，不考虑文件是否存在\n   * 如果是打开云端数据，如果文件不存在，则表示登录失败\n   */\n  fun checkLogin(\n    context: Context,\n    uri: String,\n    userName: String,\n    pass: String\n  ) = liveData {\n    val success = withContext(Dispatchers.IO) {\n      var isSuccess = false\n      try {\n        WebDavUtil.login(uri, userName, pass)\n        val b = WebDavUtil.getFileInfo(uri) != null\n        if (!b) {\n          HitUtil.toaskShort(context.getString(R.string.db_file_no_exist))\n          return@withContext false\n        }\n        // 保存记录\n        val dao = BaseApp.appDatabase.cloudServiceInfoDao()\n        var data = dao.queryServiceInfo(uri)\n        if (data == null) {\n          data = CloudServiceInfo(\n            userName = QuickUnLockUtil.encryptStr(userName),\n            password = QuickUnLockUtil.encryptStr(pass),\n            cloudPath = uri\n          )\n          dao.saveServiceInfo(data)\n        } else {\n          data.userName = QuickUnLockUtil.encryptStr(userName)\n          data.password = QuickUnLockUtil.encryptStr(pass)\n          dao.updateServiceInfo(data)\n        }\n        isSuccess = true\n      } catch (e: Exception) {\n        Timber.e(e)\n      }\n      return@withContext isSuccess\n    }\n    emit(success)\n  }\n\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/dialog/otp/CreateOtpDialog.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.view.dialog.otp\n\nimport android.view.View\nimport android.view.ViewGroup\nimport android.view.ViewStub\nimport android.widget.AdapterView\nimport android.widget.Button\nimport android.widget.RadioButton\nimport android.widget.RadioGroup\nimport android.widget.Spinner\nimport androidx.constraintlayout.widget.Group\nimport androidx.core.widget.doAfterTextChanged\nimport androidx.lifecycle.ViewModelProvider\nimport androidx.lifecycle.lifecycleScope\nimport com.alibaba.android.arouter.facade.annotation.Autowired\nimport com.alibaba.android.arouter.facade.annotation.Route\nimport com.alibaba.android.arouter.launcher.ARouter\nimport com.arialyy.frame.util.ResUtil\nimport com.blankj.utilcode.util.ToastUtils\nimport com.google.android.material.slider.Slider\nimport com.google.android.material.textfield.TextInputEditText\nimport com.journeyapps.barcodescanner.ScanContract\nimport com.journeyapps.barcodescanner.ScanIntentResult\nimport com.journeyapps.barcodescanner.ScanOptions\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseDialog\nimport com.lyy.keepassa.databinding.DialogCreateTotpBinding\nimport com.lyy.keepassa.entity.GoogleOtpBean\nimport com.lyy.keepassa.entity.KeepassXcBean\nimport com.lyy.keepassa.entity.TotpType\nimport com.lyy.keepassa.entity.TotpType.CUSTOM\nimport com.lyy.keepassa.entity.TotpType.DEFAULT\nimport com.lyy.keepassa.entity.TotpType.STEAM\nimport com.lyy.keepassa.entity.TrayTotpBean\nimport com.lyy.keepassa.util.totp.OtpEnum\nimport com.lyy.keepassa.util.totp.OtpEnum.GOOGLE_OTP\nimport com.lyy.keepassa.util.totp.OtpEnum.KEEPASSXC\nimport com.lyy.keepassa.util.totp.TokenCalculator.HashAlgorithm\nimport com.lyy.keepassa.view.QrCodeScannerActivity\nimport com.lyy.keepassa.widget.toPx\nimport kotlinx.coroutines.launch\nimport timber.log.Timber\n\n@Route(path = \"/dialog/createOtp\")\ninternal class CreateOtpDialog : BaseDialog<DialogCreateTotpBinding>(), View.OnClickListener {\n  private var arithmetic = HashAlgorithm.SHA1\n  private var time = 30\n  private var len = 6\n  private var totpType = DEFAULT\n  private var secret = \"\"\n  private lateinit var module: CreateOtpModule\n\n  @Autowired(name = \"entryTitle\")\n  @JvmField\n  var entryTitle: String = \"title\"\n\n  @Autowired(name = \"entryUserName\")\n  @JvmField\n  var entryUserName: String = \"name\"\n\n  private val barcodeLauncher =\n    registerForActivityResult(ScanContract()) { result: ScanIntentResult ->\n      val str = result.contents\n      if (str == null) {\n        ToastUtils.showLong(\"Cancelled\")\n        return@registerForActivityResult\n      }\n      Timber.d(\"contents = ${str}\")\n      if (!str.startsWith(\"otpauth://\")) {\n        ToastUtils.showLong(ResUtil.getString(R.string.error_qr_code_str))\n        return@registerForActivityResult\n      }\n\n      lifecycleScope.launch {\n        CreateOtpModule.otpFlow.emit(\n          Pair(GOOGLE_OTP, GoogleOtpBean(str))\n        )\n      }\n\n      dismiss()\n    }\n\n  override fun setLayoutId(): Int {\n    return R.layout.dialog_create_totp\n  }\n\n  override fun initData() {\n    super.initData()\n    ARouter.getInstance().inject(this)\n    module = ViewModelProvider(requireActivity())[CreateOtpModule::class.java]\n    handleOptionSwitch()\n  }\n\n  private fun handleOptionSwitch() {\n    binding.menuLayout.ivNormal.setOnClickListener(this)\n    binding.menuLayout.ivQrCode.setOnClickListener(this)\n  }\n\n  /**\n   * handle default create otp ui\n   */\n  private fun handleDefaultFlow() {\n    val vs = binding.root.findViewById<ViewStub>(R.id.vsCustom)\n    vs.setOnInflateListener { _, parent ->\n      val clConstom = findViewById<Group>(R.id.group)\n      findViewById<Button>(R.id.enter).setOnClickListener(this)\n      findViewById<Button>(R.id.cancel).setOnClickListener(this)\n\n      findViewById<RadioGroup>(R.id.rgTotp).setOnCheckedChangeListener { _, checkedId ->\n        val rb = findViewById<RadioButton>(checkedId)\n        totpType = TotpType.from(rb.tag as String)\n        when (totpType) {\n          DEFAULT, STEAM -> {\n            parent.layoutParams.height = 228.toPx()\n            clConstom.visibility = View.GONE\n          }\n\n          CUSTOM -> {\n            parent.layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT\n            clConstom.visibility = View.VISIBLE\n          }\n        }\n      }\n\n      findViewById<RadioButton>(R.id.rbDefault).isChecked = true\n\n      findViewById<Spinner>(R.id.sp).onItemSelectedListener =\n        (object : AdapterView.OnItemSelectedListener {\n          override fun onNothingSelected(parent: AdapterView<*>?) {\n          }\n\n          override fun onItemSelected(\n            parent: AdapterView<*>?,\n            view: View?,\n            position: Int,\n            id: Long\n          ) {\n            arithmetic = when (position) {\n              1 -> HashAlgorithm.SHA256\n              2 -> HashAlgorithm.SHA512\n              else -> HashAlgorithm.SHA1\n            }\n          }\n        })\n\n      findViewById<Slider>(R.id.slTime)\n        .addOnSliderTouchListener(object : Slider.OnSliderTouchListener {\n          override fun onStartTrackingTouch(slider: Slider) {\n          }\n\n          override fun onStopTrackingTouch(slider: Slider) {\n            time = slider.value.toInt()\n          }\n        })\n\n      findViewById<Slider>(R.id.slLen)\n        .addOnSliderTouchListener(object : Slider.OnSliderTouchListener {\n          override fun onStartTrackingTouch(slider: Slider) {\n          }\n\n          override fun onStopTrackingTouch(slider: Slider) {\n            len = slider.value.toInt()\n          }\n        })\n\n      findViewById<TextInputEditText>(R.id.str_key).doAfterTextChanged {\n        secret = it?.trim().toString()\n      }\n\n    }\n\n\n    if (vs != null && vs.parent != null && vs.visibility == View.GONE) {\n      vs.inflate()\n    }\n  }\n\n  override fun onClick(v: View?) {\n    when (v?.id) {\n      R.id.enter -> {\n        if (secret.isEmpty()) {\n          ToastUtils.showLong(\"key is null\")\n          return\n        }\n        Timber.d(\"key = $secret arit = $arithmetic, time = $time, len = $len\")\n        createTotpStr()\n        dismiss()\n        return\n      }\n\n      R.id.ivNormal -> {\n        handleDefaultFlow()\n        binding.menuLayout.root.visibility = View.GONE\n        return\n      }\n\n      R.id.ivQrCode -> {\n        val options = ScanOptions()\n        options.captureActivity = QrCodeScannerActivity::class.java\n        options.setDesiredBarcodeFormats(ScanOptions.QR_CODE)\n        options.setPrompt(\"Scan a barcode\")\n        options.setCameraId(0) // Use a specific camera of the device\n        options.setBeepEnabled(false)\n        options.setBarcodeImageEnabled(true)\n        barcodeLauncher.launch(options)\n        return\n      }\n    }\n    dismiss()\n  }\n\n  private fun createTotpStr() {\n    var otpEnum = OtpEnum.TRAY_TOTP\n    val otpBean = when (totpType) {\n      CUSTOM -> {\n        otpEnum = KEEPASSXC\n        KeepassXcBean(\n          title = entryTitle,\n          userName = entryUserName,\n          isSteam = false,\n          secret = secret,\n          issuer = entryTitle,\n          period = time,\n          digits = len,\n          algorithm = arithmetic\n        )\n      }\n\n      DEFAULT -> {\n        otpEnum = OtpEnum.TRAY_TOTP\n        TrayTotpBean(secret = secret, period = time, isSteam = false)\n      }\n\n      STEAM -> {\n        otpEnum = OtpEnum.TRAY_TOTP\n        TrayTotpBean(secret = secret, period = time, isSteam = true)\n      }\n    }\n\n    lifecycleScope.launch {\n      CreateOtpModule.otpFlow.emit(Pair(otpEnum, otpBean))\n    }\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/dialog/otp/CreateOtpModule.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.view.dialog.otp\n\nimport com.lyy.keepassa.base.BaseModule\nimport com.lyy.keepassa.entity.IOtpBean\nimport com.lyy.keepassa.util.totp.OtpEnum\nimport kotlinx.coroutines.flow.MutableSharedFlow\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 3:38 下午 2022/1/19\n **/\nclass CreateOtpModule : BaseModule() {\n  companion object {\n    val otpFlow = MutableSharedFlow<Pair<OtpEnum, IOtpBean?>>(0)\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/dialog/otp/TotpDisplayDialog.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.view.dialog.otp\n\nimport android.view.View\nimport com.alibaba.android.arouter.facade.annotation.Autowired\nimport com.alibaba.android.arouter.facade.annotation.Route\nimport com.alibaba.android.arouter.launcher.ARouter\nimport com.arialyy.frame.util.ResUtil\nimport com.keepassdroid.database.PwEntryV4\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseApp\nimport com.lyy.keepassa.base.BaseDialog\nimport com.lyy.keepassa.databinding.DialogTotpDisplayBinding\nimport com.lyy.keepassa.util.ClipboardUtil\nimport com.lyy.keepassa.util.HitUtil\nimport com.lyy.keepassa.util.totp.OtpUtil\nimport java.util.UUID\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 8:16 下午 2022/1/5\n **/\n@Route(path = \"/dialog/totpDisplay\")\nclass TotpDisplayDialog : BaseDialog<DialogTotpDisplayBinding>() {\n\n  @Autowired(name = \"uuid\")\n  lateinit var uuid: String\n  private var curToTp = \"\"\n\n  override fun initData() {\n    super.initData()\n    ARouter.getInstance().inject(this)\n    binding.dialog = this\n    val entry = BaseApp.KDB.pm.entries[UUID.fromString(uuid)] as PwEntryV4\n    curToTp = OtpUtil.getOtpPass(entry).second ?: \"error\"\n    binding.tvTotp.text = curToTp\n    binding.cvTime.startAnim {\n      curToTp = OtpUtil.getOtpPass(entry).second ?: \"error\"\n      binding.tvTotp.text = curToTp\n    }\n  }\n\n  override fun setLayoutId(): Int {\n    return R.layout.dialog_totp_display\n  }\n\n  fun onClick(v: View) {\n    ClipboardUtil.get().copyDataToClip(curToTp)\n    HitUtil.toaskShort(ResUtil.getString(R.string.hint_copy_totp))\n    dismiss()\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/dialog/otp/modify/IOtpModifyHandler.kt",
    "content": "package com.lyy.keepassa.view.dialog.otp.modify\n\nimport com.lyy.keepassa.util.totp.TokenCalculator\nimport com.lyy.keepassa.util.totp.TokenCalculator.HashAlgorithm\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 5:40 PM 2024/1/25\n **/\ninternal interface IOtpModifyHandler {\n  fun initView(context: ModifyOtpDialog)\n\n  fun save(\n    context: ModifyOtpDialog,\n    secret: String,\n    arithmetic: HashAlgorithm = HashAlgorithm.SHA1,\n    digits: Int = TokenCalculator.TOTP_DEFAULT_DIGITS,\n    period: Int = TokenCalculator.TOTP_DEFAULT_PERIOD,\n    isSteam: Boolean = false\n  ) {\n    // context.lifecycleScope.launch {\n    //   KpaUtil.kdbHandlerService.saveOnly {\n    //     context.dismiss()\n    //   }\n    // }\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/dialog/otp/modify/ModifyOtpDialog.kt",
    "content": "package com.lyy.keepassa.view.dialog.otp.modify\n\nimport android.view.View\nimport android.widget.AdapterView\nimport android.widget.RadioButton\nimport androidx.core.widget.doAfterTextChanged\nimport com.alibaba.android.arouter.facade.annotation.Autowired\nimport com.alibaba.android.arouter.facade.annotation.Route\nimport com.alibaba.android.arouter.launcher.ARouter\nimport com.arialyy.frame.util.ResUtil\nimport com.blankj.utilcode.util.ToastUtils\nimport com.google.android.material.slider.Slider\nimport com.keepassdroid.database.PwEntryV4\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseApp\nimport com.lyy.keepassa.base.BaseDialog\nimport com.lyy.keepassa.databinding.DialogOtpModifyBinding\nimport com.lyy.keepassa.entity.TotpType\nimport com.lyy.keepassa.entity.TotpType.CUSTOM\nimport com.lyy.keepassa.entity.TotpType.DEFAULT\nimport com.lyy.keepassa.entity.TotpType.STEAM\nimport com.lyy.keepassa.util.doClick\nimport com.lyy.keepassa.util.otpIsKeeTraySteam\nimport com.lyy.keepassa.util.otpIsKeeTrayTotp\nimport com.lyy.keepassa.util.otpKeepass\nimport com.lyy.keepassa.util.otpKeepassXC\nimport com.lyy.keepassa.util.totp.TokenCalculator.HashAlgorithm\nimport java.util.UUID\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 2:27 PM 2024/1/11\n **/\n@Route(path = \"/dialog/otpModify\")\nclass ModifyOtpDialog : BaseDialog<DialogOtpModifyBinding>() {\n  private var arithmetic = HashAlgorithm.SHA1\n  private var time = 30\n  private var len = 6\n  private var secret = \"\"\n  private var otpType = DEFAULT\n\n  @Autowired(name = \"uid\")\n  lateinit var uid: UUID\n  lateinit var pwEntryV4: PwEntryV4\n  private var handler: IOtpModifyHandler? = null\n\n  override fun initData() {\n    super.initData()\n    ARouter.getInstance().inject(this)\n    pwEntryV4 = BaseApp.KDB.pm.entries[uid] as PwEntryV4\n    handleLayoutSwitch()\n    handleSp()\n    handleSlTime()\n    handleBtn()\n  }\n\n  override fun setLayoutId(): Int {\n    return R.layout.dialog_otp_modify\n  }\n\n  private fun handleLayoutSwitch() {\n    binding.contentLayout.rgTotp.setOnCheckedChangeListener { _, checkedId ->\n      val rb = findViewById<RadioButton>(checkedId)\n      otpType = TotpType.from(rb.tag as String)\n      when (otpType) {\n        DEFAULT, STEAM -> {\n          binding.contentLayout.group.visibility = View.GONE\n        }\n\n        CUSTOM -> {\n          binding.contentLayout.group.visibility = View.VISIBLE\n        }\n      }\n    }\n    handler = when {\n\n      pwEntryV4.otpIsKeeTraySteam() -> {\n        OtpKeeTraySteamHandler()\n      }\n\n      pwEntryV4.otpIsKeeTrayTotp() -> {\n        OtpKeeTrayHandler()\n      }\n\n      pwEntryV4.otpKeepassXC() -> {\n        OtpKeepassXcHandler()\n      }\n\n      pwEntryV4.otpKeepass() -> {\n        OtpKeepassHandler()\n      }\n\n      else -> {\n        ToastUtils.showLong(ResUtil.getString(R.string.error_totp))\n        null\n      }\n    }\n    handler?.initView(this)\n  }\n\n  private fun handleSp() {\n    binding.contentLayout.sp.onItemSelectedListener =\n      (object : AdapterView.OnItemSelectedListener {\n        override fun onNothingSelected(parent: AdapterView<*>?) {\n        }\n\n        override fun onItemSelected(\n          parent: AdapterView<*>?,\n          view: View?,\n          position: Int,\n          id: Long\n        ) {\n          arithmetic = when (position) {\n            1 -> HashAlgorithm.SHA256\n            2 -> HashAlgorithm.SHA512\n            else -> HashAlgorithm.SHA1\n          }\n        }\n      })\n  }\n\n  private fun handleSlTime() {\n    binding.contentLayout.slTime\n      .addOnSliderTouchListener(object : Slider.OnSliderTouchListener {\n        override fun onStartTrackingTouch(slider: Slider) {\n        }\n\n        override fun onStopTrackingTouch(slider: Slider) {\n          time = slider.value.toInt()\n        }\n      })\n\n    binding.contentLayout.slLen\n      .addOnSliderTouchListener(object : Slider.OnSliderTouchListener {\n        override fun onStartTrackingTouch(slider: Slider) {\n        }\n\n        override fun onStopTrackingTouch(slider: Slider) {\n          len = slider.value.toInt()\n        }\n      })\n\n    binding.contentLayout.strKey.doAfterTextChanged {\n      secret = it?.trim().toString()\n    }\n  }\n\n  private fun handleBtn() {\n    binding.contentLayout.enter.doClick {\n      handler?.save(this, secret, arithmetic, len, time, otpType == STEAM)\n    }\n\n    binding.contentLayout.cancel.doClick {\n      dismiss()\n    }\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/dialog/otp/modify/OtpKeeTrayHandler.kt",
    "content": "package com.lyy.keepassa.view.dialog.otp.modify\n\nimport androidx.core.view.isVisible\nimport androidx.lifecycle.lifecycleScope\nimport com.lyy.keepassa.entity.TrayTotpBean\nimport com.lyy.keepassa.entity.toOtpStringMap\nimport com.lyy.keepassa.util.getKeeTrayBean\nimport com.lyy.keepassa.util.totp.OtpEnum\nimport com.lyy.keepassa.util.totp.TokenCalculator.HashAlgorithm\nimport com.lyy.keepassa.view.dialog.otp.CreateOtpModule\nimport kotlinx.coroutines.launch\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 5:41 PM 2024/1/25\n **/\ninternal class OtpKeeTrayHandler : IOtpModifyHandler {\n  private lateinit var bean: TrayTotpBean\n  override fun initView(context: ModifyOtpDialog) {\n    val binding = context.binding\n    binding.contentLayout.group.isVisible = false\n    binding.contentLayout.rbDefault.isChecked = true\n    binding.contentLayout.rbCustom.isVisible = false\n    binding.contentLayout.rbSteam.isVisible = false\n    bean = context.pwEntryV4.getKeeTrayBean()\n    binding.contentLayout.strKey.setText(bean.secret)\n  }\n\n  override fun save(\n    context: ModifyOtpDialog,\n    secret: String,\n    arithmetic: HashAlgorithm,\n    digits: Int,\n    period: Int,\n    isSteam: Boolean\n  ) {\n    bean.period = period\n    bean.secret = secret\n\n    val beanMap = bean.toOtpStringMap()\n    beanMap.forEach {\n      context.pwEntryV4.strings[it.key] = it.value\n    }\n    context.lifecycleScope.launch {\n      CreateOtpModule.otpFlow.emit(Pair(OtpEnum.TRAY_TOTP, bean))\n      context.dismiss()\n    }\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/dialog/otp/modify/OtpKeeTraySteamHandler.kt",
    "content": "package com.lyy.keepassa.view.dialog.otp.modify\n\nimport androidx.core.view.isVisible\nimport androidx.lifecycle.lifecycleScope\nimport com.lyy.keepassa.entity.TrayTotpBean\nimport com.lyy.keepassa.entity.toOtpStringMap\nimport com.lyy.keepassa.util.getKeeTrayBean\nimport com.lyy.keepassa.util.totp.OtpEnum\nimport com.lyy.keepassa.util.totp.TokenCalculator.HashAlgorithm\nimport com.lyy.keepassa.view.dialog.otp.CreateOtpModule\nimport kotlinx.coroutines.launch\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 5:41 PM 2024/1/25\n **/\ninternal class OtpKeeTraySteamHandler : IOtpModifyHandler {\n  private lateinit var bean: TrayTotpBean\n  override fun initView(context: ModifyOtpDialog) {\n    val binding = context.binding\n    binding.contentLayout.group.isVisible = false\n    binding.contentLayout.rbCustom.isVisible = false\n    bean = context.pwEntryV4.getKeeTrayBean()\n    binding.contentLayout.strKey.setText(bean.secret)\n    binding.contentLayout.rbSteam.isChecked = true\n    binding.contentLayout.rbDefault.isVisible = false\n  }\n\n  override fun save(\n    context: ModifyOtpDialog,\n    secret: String,\n    arithmetic: HashAlgorithm,\n    digits: Int,\n    period: Int,\n    isSteam: Boolean\n  ) {\n    bean.period = period\n    bean.secret = secret\n    val beanMap = bean.toOtpStringMap()\n    beanMap.forEach {\n      context.pwEntryV4.strings[it.key] = it.value\n    }\n\n    context.lifecycleScope.launch {\n      CreateOtpModule.otpFlow.emit(Pair(OtpEnum.TRAY_TOTP, bean))\n      context.dismiss()\n    }\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/dialog/otp/modify/OtpKeepOtherHandler.kt",
    "content": "package com.lyy.keepassa.view.dialog.otp.modify\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 2:51 PM 2024/2/2\n **/\nclass OtpKeepOtherHandler:IOtpModifyHandler {\n  override fun initView(context: ModifyOtpDialog) {\n    val binding = context.binding\n    // binding.contentLayout.strKey.text = context.pwEntryV4.\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/dialog/otp/modify/OtpKeepassHandler.kt",
    "content": "package com.lyy.keepassa.view.dialog.otp.modify\n\nimport androidx.core.view.isVisible\nimport androidx.lifecycle.lifecycleScope\nimport com.arialyy.frame.util.ResUtil\nimport com.blankj.utilcode.util.ToastUtils\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.entity.KeepassBean\nimport com.lyy.keepassa.entity.toOtpStringMap\nimport com.lyy.keepassa.util.getKeepassBean\nimport com.lyy.keepassa.util.totp.OtpEnum\nimport com.lyy.keepassa.util.totp.TokenCalculator.HashAlgorithm\nimport com.lyy.keepassa.view.dialog.otp.CreateOtpModule\nimport kotlinx.coroutines.launch\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 5:41 PM 2024/1/25\n **/\ninternal class OtpKeepassHandler : IOtpModifyHandler {\n  private lateinit var otpBean: KeepassBean\n  override fun initView(context: ModifyOtpDialog) {\n    val binding = context.binding\n    binding.contentLayout.group.isVisible = true\n    binding.contentLayout.rbSteam.isVisible = false\n    binding.contentLayout.rbCustom.isChecked = true\n    binding.contentLayout.rbCustom.isVisible = true\n    val oBean = context.pwEntryV4.getKeepassBean()\n    val bean = oBean.otpBean\n    if (bean == null) {\n      ToastUtils.showLong(ResUtil.getString(R.string.not_souper_otp))\n      return\n    }\n    otpBean = oBean\n    binding.contentLayout.strKey.setText(bean.secret)\n    binding.contentLayout.sp.setSelection(\n      when (bean.algorithm) {\n        HashAlgorithm.SHA1 -> 0\n        HashAlgorithm.SHA256 -> 1\n        HashAlgorithm.SHA512 -> 2\n      }\n    )\n    binding.contentLayout.slTime.value = bean.period.toFloat()\n    binding.contentLayout.slLen.value = bean.digits.toFloat()\n  }\n\n  override fun save(\n    context: ModifyOtpDialog,\n    secret: String,\n    arithmetic: HashAlgorithm,\n    digits: Int,\n    period: Int,\n    isSteam: Boolean\n  ) {\n    otpBean.otpBean?.let {\n      it.secret = secret\n      it.period = period\n      it.algorithm = arithmetic\n      it.digits = digits\n    }\n    otpBean.toOtpStringMap().forEach {\n      context.pwEntryV4.strings[it.key] = it.value\n    }\n\n    context.lifecycleScope.launch {\n      CreateOtpModule.otpFlow.emit(Pair(OtpEnum.KEEPASS, otpBean))\n      context.dismiss()\n    }\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/dialog/otp/modify/OtpKeepassXcHandler.kt",
    "content": "package com.lyy.keepassa.view.dialog.otp.modify\n\nimport androidx.core.view.isVisible\nimport androidx.lifecycle.lifecycleScope\nimport com.lyy.keepassa.entity.KeepassXcBean\nimport com.lyy.keepassa.entity.toOtpStringMap\nimport com.lyy.keepassa.util.getKeepassXcBean\nimport com.lyy.keepassa.util.otpIsKeepassXcSteam\nimport com.lyy.keepassa.util.totp.OtpEnum\nimport com.lyy.keepassa.util.totp.TokenCalculator.HashAlgorithm\nimport com.lyy.keepassa.view.dialog.otp.CreateOtpModule\nimport kotlinx.coroutines.launch\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 5:41 PM 2024/1/25\n **/\ninternal class OtpKeepassXcHandler : IOtpModifyHandler {\n  private lateinit var otpBean: KeepassXcBean\n  override fun initView(context: ModifyOtpDialog) {\n    val binding = context.binding\n    binding.contentLayout.group.isVisible = true\n    binding.contentLayout.rbCustom.isVisible = true\n    val isSteam = context.pwEntryV4.otpIsKeepassXcSteam()\n    if (!isSteam) {\n      binding.contentLayout.rbCustom.isChecked = true\n    } else {\n      binding.contentLayout.rbSteam.isChecked = true\n    }\n    otpBean = context.pwEntryV4.getKeepassXcBean()\n    binding.contentLayout.strKey.setText(otpBean.secret)\n    binding.contentLayout.sp.setSelection(\n      when (otpBean.algorithm) {\n        HashAlgorithm.SHA1 -> 0\n        HashAlgorithm.SHA256 -> 1\n        HashAlgorithm.SHA512 -> 2\n      }\n    )\n    binding.contentLayout.slTime.value = otpBean.period.toFloat()\n    binding.contentLayout.slLen.value = otpBean.digits.toFloat()\n  }\n\n  override fun save(\n    context: ModifyOtpDialog,\n    secret: String,\n    arithmetic: HashAlgorithm,\n    digits: Int,\n    period: Int,\n    isSteam: Boolean\n  ) {\n    otpBean.digits = digits\n    otpBean.secret = secret\n    otpBean.period = period\n    otpBean.algorithm = arithmetic\n    otpBean.encoder = if (isSteam) \"steam\" else \"\"\n\n    otpBean.toOtpStringMap().forEach {\n      context.pwEntryV4.strings[it.key] = it.value\n    }\n\n    context.lifecycleScope.launch {\n      CreateOtpModule.otpFlow.emit(Pair(OtpEnum.KEEPASSXC, otpBean))\n      context.dismiss()\n    }\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/dialog/webdav/DefaultLoginAdapter.kt",
    "content": "package com.lyy.keepassa.view.dialog.webdav\n\nimport com.arialyy.frame.util.ResUtil\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.databinding.DialogWebdavLoginNewBinding\nimport com.lyy.keepassa.util.HitUtil\nimport com.lyy.keepassa.util.KeepassAUtil\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 11:38 上午 2022/7/21\n **/\ninternal open class DefaultLoginAdapter(\n  val binding: DialogWebdavLoginNewBinding,\n  val context: WebDavLoginDialogNew\n) : IWebDavLoginAdapter {\n  override fun updateState() {\n  }\n\n  override fun startLogin(userName: String, password: String) {\n    val uri = binding.uri.text.toString()\n      .trim()\n\n    if (uri.isEmpty() || uri.equals(\"null\", true)) {\n      HitUtil.toaskLong(\n        ResUtil.getString(R.string.hint_please_input, ResUtil.getString(R.string.hint_webdav_url))\n      )\n      return\n    }\n\n    if (!KeepassAUtil.instance.checkUrlIsValid(uri)) {\n      HitUtil.toaskLong(\"${ResUtil.getString(R.string.hint_webdav_url)} ${ResUtil.getString(R.string.invalid)}\")\n      return\n    }\n\n    context.startLoginFlow(uri, userName, password)\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/dialog/webdav/IWebDavLoginAdapter.kt",
    "content": "package com.lyy.keepassa.view.dialog.webdav\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 11:25 上午 2022/7/21\n **/\ninternal interface IWebDavLoginAdapter {\n\n  fun updateState()\n\n  fun startLogin(userName: String, password: String)\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/dialog/webdav/NextcloudLoginAdapter.kt",
    "content": "package com.lyy.keepassa.view.dialog.webdav\n\nimport android.view.View\nimport com.arialyy.frame.util.ResUtil\nimport com.blankj.utilcode.util.KeyboardUtils\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.databinding.DialogWebdavLoginNewBinding\nimport com.lyy.keepassa.util.HitUtil\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 11:25 上午 2022/7/21\n **/\ninternal class NextcloudLoginAdapter(\n  val binding: DialogWebdavLoginNewBinding,\n  val context: WebDavLoginDialogNew\n) : IWebDavLoginAdapter {\n  override fun updateState() {\n    binding.groupHost.visibility = View.VISIBLE\n    KeyboardUtils.showSoftInput(binding.uri)\n  }\n\n  override fun startLogin(userName: String, password: String) {\n    val hostName = binding.host.text.toString().trim()\n    if (hostName.isEmpty()) {\n      HitUtil.toaskLong(ResUtil.getString(R.string.webdav_port_name_null))\n      return\n    }\n\n    val url = context.module.convertHost(hostName, binding.edPort.text.toString().trim(), userName)\n    context.startLoginFlow(url, userName, password)\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/dialog/webdav/OtherLoginAdapter.kt",
    "content": "package com.lyy.keepassa.view.dialog.webdav\n\nimport com.blankj.utilcode.util.KeyboardUtils\nimport com.lyy.keepassa.databinding.DialogWebdavLoginNewBinding\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 11:46 上午 2022/7/21\n **/\ninternal class OtherLoginAdapter(\n  binding: DialogWebdavLoginNewBinding,\n  context: WebDavLoginDialogNew\n) : DefaultLoginAdapter(binding, context) {\n  override fun updateState() {\n    binding.uri.setText(\"\")\n    KeyboardUtils.showSoftInput(binding.uri)\n  }\n}\n"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/dialog/webdav/WebDavLoginDialogNew.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.view.dialog.webdav\n\nimport android.view.View\nimport android.widget.ArrayAdapter\nimport androidx.core.widget.doAfterTextChanged\nimport androidx.lifecycle.ViewModelProvider\nimport androidx.lifecycle.lifecycleScope\nimport com.alibaba.android.arouter.facade.annotation.Route\nimport com.arialyy.frame.router.Routerfit\nimport com.arialyy.frame.util.ResUtil\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseDialog\nimport com.lyy.keepassa.databinding.DialogWebdavLoginNewBinding\nimport com.lyy.keepassa.event.WebDavLoginEvent\nimport com.lyy.keepassa.router.DialogRouter\nimport com.lyy.keepassa.util.HitUtil\nimport com.lyy.keepassa.util.KeepassAUtil\nimport com.lyy.keepassa.util.cloud.WebDavUtil\nimport com.lyy.keepassa.view.dialog.WebDavLoginModule\nimport kotlinx.coroutines.flow.MutableSharedFlow\nimport kotlinx.coroutines.flow.collectLatest\nimport kotlinx.coroutines.launch\n\n/**\n * webdav 登录\n */\n@Route(path = \"/dialog/webdavLogin\")\nclass WebDavLoginDialogNew : BaseDialog<DialogWebdavLoginNewBinding>() {\n\n  lateinit var module: WebDavLoginModule\n  val webDavLoginFlow = MutableSharedFlow<WebDavLoginEvent>()\n  private var loginAdapter: IWebDavLoginAdapter? = null\n\n  private val nextCloudAdapter by lazy {\n    NextcloudLoginAdapter(binding, this)\n  }\n\n  private val defaultAdapter by lazy {\n    DefaultLoginAdapter(binding, this)\n  }\n\n  private val otherAdapter by lazy {\n    OtherLoginAdapter(binding, this)\n  }\n\n  private val loadingDialog by lazy {\n    Routerfit.create(DialogRouter::class.java).getLoadingDialog()\n  }\n\n  override fun setLayoutId(): Int {\n    return R.layout.dialog_webdav_login_new\n  }\n\n  override fun initData() {\n    super.initData()\n    module = ViewModelProvider(this)[WebDavLoginModule::class.java]\n    binding.enter.setOnClickListener {\n      handleEnterClick()\n    }\n    binding.cancel.setOnClickListener { dismiss() }\n    loginAdapter = defaultAdapter\n    handleSelectService()\n  }\n\n  private fun handleSelectService() {\n    val adapter = ArrayAdapter(\n      requireContext(),\n      R.layout.android_simple_dropdown_item_1line,\n      WebDavUtil.SUPPORTED_WEBDAV_URLS\n    )\n\n    binding.uri.setAdapter(adapter)\n    binding.uri.threshold = 10000 // 设置输入几个字符后开始出现提示 默认是2\n    binding.uri.setText(WebDavUtil.SUPPORTED_WEBDAV_URLS[0])\n    binding.uriLayout.setEndIconOnClickListener {\n      binding.uri.showDropDown()\n    }\n    binding.uri.doAfterTextChanged {\n      binding.groupHost.visibility = View.GONE\n      module.curWebDavServer = it?.toString()\n      loginAdapter = when {\n        module.isNextcloud() -> {\n          binding.isPreemptive.isChecked = true\n          binding.passwordLayout.helperText =  null\n          nextCloudAdapter\n        }\n        module.isJGY() -> {\n          binding.isPreemptive.isChecked = false\n          binding.passwordLayout.helperText = ResUtil.getString(R.string.hint_webdav_jgy)\n          defaultAdapter\n        }\n        module.isOtherServer() ->{\n          binding.passwordLayout.helperText =  null\n          otherAdapter\n        }\n        else -> {\n          binding.passwordLayout.helperText =  null\n          defaultAdapter\n        }\n      }\n      loginAdapter?.updateState()\n    }\n  }\n\n  private fun handleEnterClick() {\n    if (KeepassAUtil.instance.isFastClick()) {\n      return\n    }\n    val pass = binding.password.text.toString()\n      .trim()\n\n    val userName = binding.userName.text.toString()\n      .trim()\n    if (pass.isEmpty()) {\n      HitUtil.toaskLong(ResUtil.getString(R.string.hint_please_input, getString(R.string.password)))\n      return\n    }\n    if (userName.isEmpty()) {\n      HitUtil.toaskLong(\n        ResUtil.getString(R.string.hint_please_input, getString(R.string.hint_input_user_name))\n      )\n      return\n    }\n\n    loginAdapter?.startLogin(userName, pass)\n  }\n\n  /**\n   * 登陆流程\n   */\n  fun startLoginFlow(\n    uri: String,\n    userName: String,\n    pass: String\n  ) {\n    loadingDialog.show()\n    lifecycleScope.launch {\n      module.checkLogin(uri, userName, pass, binding.isPreemptive.isChecked).collectLatest {\n        loadingDialog.dismiss()\n        webDavLoginFlow.emit(WebDavLoginEvent(uri, userName, pass, it))\n        if (!it) {\n          HitUtil.toaskLong(\"${getString(R.string.login)} ${getString(R.string.fail)}, url【${uri}】\")\n          return@collectLatest\n        }\n        HitUtil.toaskLong(\"${getString(R.string.login)} ${getString(R.string.success)}\")\n        dismiss()\n      }\n    }\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/dir/ChooseGroupActivity.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.view.dir\n\nimport android.app.Activity\nimport android.os.Bundle\nimport androidx.core.app.ActivityOptionsCompat\nimport androidx.fragment.app.FragmentActivity\nimport androidx.lifecycle.ViewModelProvider\nimport androidx.transition.Transition\nimport androidx.transition.TransitionInflater\nimport com.alibaba.android.arouter.facade.annotation.Autowired\nimport com.alibaba.android.arouter.facade.annotation.Route\nimport com.alibaba.android.arouter.launcher.ARouter\nimport com.arialyy.frame.router.Routerfit\nimport com.keepassdroid.database.PwGroup\nimport com.keepassdroid.database.PwGroupId\nimport com.keepassdroid.database.PwGroupV4\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseActivity\nimport com.lyy.keepassa.base.BaseApp\nimport com.lyy.keepassa.databinding.ActivityGroupDirBinding\nimport com.lyy.keepassa.router.FragmentRouter\nimport com.lyy.keepassa.view.ChoseDirModule\nimport timber.log.Timber\nimport java.util.Stack\nimport java.util.UUID\nimport kotlin.collections.set\n\n/**\n * 选择群组\n */\n@Route(path = \"/group/choose\")\nclass ChooseGroupActivity : BaseActivity<ActivityGroupDirBinding>() {\n\n  companion object {\n    // 需要恢复的群组id\n    private const val KEY_GROUP_ID = \"KEY_GROUP_ID\"\n\n    // 需要恢复的条目id\n    private const val KEY_ENTRY_ID = \"KEY_ENTRY_ID\"\n\n    // 1: 恢复群组，2: 恢复项目，3: 选择群组，4: 移动\n    const val KEY_TYPE = \"KEY_TYPE\"\n\n    // 恢复群组\n    const val DATA_MOVE_GROUP = 1\n\n    // 恢复条目\n    const val DATA_MOVE_ENTRY = 2\n\n    // 选择群组\n    const val DATA_SELECT_GROUP = 3\n\n    // 路径地址\n    const val DATA_PARENT = \"DATA_PARENT\"\n\n    /**\n     * 移动条目\n     * @param entryId 条目id\n     */\n    fun moveEntry(\n      context: FragmentActivity,\n      entryId: UUID\n    ) {\n      ARouter.getInstance()\n        .build(\"/group/choose\")\n        .withInt(KEY_TYPE, DATA_MOVE_ENTRY)\n        .withSerializable(KEY_ENTRY_ID, entryId)\n        .withOptionsCompat(ActivityOptionsCompat.makeSceneTransitionAnimation(context))\n        .navigation(context)\n    }\n\n    /**\n     * 移动群组\n     */\n    fun moveGroup(\n      context: FragmentActivity,\n      groupId: PwGroupId\n    ) {\n      ARouter.getInstance()\n        .build(\"/group/choose\")\n        .withInt(KEY_TYPE, DATA_MOVE_GROUP)\n        .withSerializable(KEY_GROUP_ID, groupId)\n        .withOptionsCompat(ActivityOptionsCompat.makeSceneTransitionAnimation(context))\n        .navigation(context)\n    }\n  }\n\n  private lateinit var curGroup: PwGroupV4\n  private var lastGroupStack: Stack<PwGroup> = Stack()\n  private val fragmentMap: HashMap<PwGroupId, DirFragment> = HashMap()\n  private lateinit var module: ChoseDirModule\n\n  @Autowired(name = KEY_GROUP_ID)\n  @JvmField\n  var recycleGroupId: PwGroupId? = null\n\n  @Autowired(name = KEY_ENTRY_ID)\n  @JvmField\n  var recycleEntryId: UUID? = null\n\n  @Autowired(name = KEY_TYPE)\n  @JvmField\n  var recycleType = DATA_MOVE_GROUP\n\n  override fun setLayoutId(): Int {\n    return R.layout.activity_group_dir\n  }\n\n  override fun initData(savedInstanceState: Bundle?) {\n    super.initData(savedInstanceState)\n    ARouter.getInstance().inject(this)\n    module = ViewModelProvider(this)[ChoseDirModule::class.java]\n    curGroup = BaseApp.KDB.pm.rootGroup as PwGroupV4\n    if (recycleType == DATA_MOVE_GROUP && recycleGroupId == null) {\n      Timber.e(\"需要恢复的群组id为空\")\n      finish()\n      return\n    }\n\n    if ((recycleType == DATA_MOVE_ENTRY) && recycleEntryId == null) {\n      Timber.e(\"需要恢复的项目id为空\")\n      finish()\n      return\n    }\n\n\n    when (recycleType) {\n      DATA_SELECT_GROUP -> {\n        binding.bt.text = getString(R.string.choose_dir)\n      }\n    }\n\n    toolbar.title = curGroup.name\n\n    binding.bt.setOnClickListener {\n      when (recycleType) {\n        // 移动群组\n        DATA_MOVE_GROUP -> {\n          module.moveGroup(this, recycleGroupId!!, curGroup)\n        }\n\n        // 移动条目\n        DATA_MOVE_ENTRY -> {\n          module.moveEntry(this, recycleEntryId!!, curGroup)\n        }\n\n        // 选择群组目录\n        DATA_SELECT_GROUP -> {\n          intent.putExtra(DATA_PARENT, curGroup.id)\n          setResult(Activity.RESULT_OK, intent)\n          finishAfterTransition()\n        }\n      }\n    }\n\n    startNextFragment(curGroup, true)\n  }\n\n  /**\n   * 右 -> 左\n   */\n  private fun getRlAnim(): Transition {\n    return TransitionInflater.from(this)\n      .inflateTransition(R.transition.slide_enter)\n  }\n\n  /**\n   * 左 -> 右\n   */\n  private fun getLrAnim(): Transition {\n    return TransitionInflater.from(this)\n      .inflateTransition(R.transition.slide_exit)\n  }\n\n  override fun onBackPressed() {\n    if (lastGroupStack.empty()) {\n      finishAfterTransition()\n    } else {\n      lastGroupStack.pop()\n      super.onBackPressed()\n    }\n  }\n\n  fun startNextFragment(\n    pwGroup: PwGroupV4,\n    isFirst: Boolean = false\n  ) {\n    if (!isFirst) {\n      lastGroupStack.push(curGroup)\n    }\n    toolbar.title = pwGroup.name\n    var fragment = fragmentMap[pwGroup.id]\n    if (fragment == null) {\n      fragment = Routerfit.create(FragmentRouter::class.java).getDirFragment(\n        pwGroup,\n        recycleType != DATA_SELECT_GROUP,\n        recycleGroupId\n      )\n\n      fragment.enterTransition = getRlAnim()\n      fragment.exitTransition = getLrAnim()\n      fragment.reenterTransition = getLrAnim()\n      fragmentMap[pwGroup.id] = fragment\n    }\n    supportFragmentManager.beginTransaction()\n      .replace(R.id.content, fragment)\n      .addToBackStack(\"dirStack\")\n      .commit()\n    curGroup = pwGroup\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/dir/DirFragment.kt",
    "content": "package com.lyy.keepassa.view.dir\n\nimport androidx.recyclerview.widget.LinearLayoutManager\nimport com.alibaba.android.arouter.facade.annotation.Autowired\nimport com.alibaba.android.arouter.facade.annotation.Route\nimport com.alibaba.android.arouter.launcher.ARouter\nimport com.arialyy.frame.util.adapter.RvItemClickSupport\nimport com.keepassdroid.database.PwGroup\nimport com.keepassdroid.database.PwGroupId\nimport com.keepassdroid.database.PwGroupV4\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseApp\nimport com.lyy.keepassa.base.BaseFragment\nimport com.lyy.keepassa.databinding.FragmentOnlyListBinding\nimport com.lyy.keepassa.entity.SimpleItemEntity\nimport com.lyy.keepassa.util.KdbUtil\nimport com.lyy.keepassa.util.doOnItemClickListener\nimport com.lyy.keepassa.view.SimpleEntryAdapter\n\n@Route(path = \"/group/choose/dir\")\nclass DirFragment : BaseFragment<FragmentOnlyListBinding>() {\n  private lateinit var adapter: SimpleEntryAdapter\n  private val entryData = ArrayList<SimpleItemEntity>()\n\n  @Autowired(name = KEY_CUR_GROUP)\n  lateinit var curGroup: PwGroup\n\n  @Autowired(name = KEY_IS_MOVE_GROUP)\n  @JvmField\n  var isMoverGroup = false\n\n  @Autowired(name = KEY_IS_RECYCLE_GROUP_ID)\n  @JvmField\n  var recycleGroupId: PwGroupId? = null\n\n  companion object {\n    const val KEY_CUR_GROUP = \"KEY_CUR_GROUP\"\n    const val KEY_IS_MOVE_GROUP = \"KEY_IS_MOVE_GROUP\"\n    const val KEY_IS_RECYCLE_GROUP_ID = \"KEY_IS_RECYCLE_GROUP_ID\"\n  }\n\n  fun build(): DirFragment {\n    return this\n  }\n\n  override fun initData() {\n    ARouter.getInstance().inject(this)\n    adapter = SimpleEntryAdapter(requireContext(), entryData)\n    binding.list.setHasFixedSize(true)\n    binding.list.layoutManager = LinearLayoutManager(context)\n    binding.list.adapter = adapter\n\n    entryData.clear()\n    for (group in curGroup.childGroups) {\n      if (group == BaseApp.KDB.pm.recycleBin) {\n        continue\n      }\n      if (isMoverGroup && group.id == recycleGroupId) {\n        continue\n      }\n      val item = SimpleItemEntity()\n      item.title = group.name\n      item.subTitle =\n        requireContext().getString(\n          R.string.hint_group_desc, KdbUtil.getGroupAllEntryNum(group)\n            .toString()\n        )\n      item.obj = group\n      entryData.add(item)\n    }\n    adapter.notifyDataSetChanged()\n\n    binding.list.doOnItemClickListener { _, position, _ ->\n      (activity as ChooseGroupActivity).startNextFragment(entryData[position].obj as PwGroupV4)\n    }\n  }\n\n  override fun setLayoutId(): Int {\n    return R.layout.fragment_only_list\n  }\n\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/fingerprint/FingerprintActivity.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.view.fingerprint\n\nimport android.annotation.TargetApi\nimport android.os.Build\nimport android.os.Bundle\nimport android.view.View\nimport android.view.ViewAnimationUtils\nimport android.view.animation.AccelerateInterpolator\nimport android.widget.Button\nimport androidx.core.app.ActivityOptionsCompat\nimport androidx.fragment.app.FragmentActivity\nimport androidx.lifecycle.Observer\nimport androidx.lifecycle.ViewModelProvider\nimport com.alibaba.android.arouter.facade.annotation.Route\nimport com.alibaba.android.arouter.launcher.ARouter\nimport com.arialyy.frame.router.Routerfit\nimport com.arialyy.frame.util.ResUtil\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseActivity\nimport com.lyy.keepassa.base.BaseApp\nimport com.lyy.keepassa.databinding.ActivityFingerprintBinding\nimport com.lyy.keepassa.router.DialogRouter\nimport com.lyy.keepassa.util.FingerprintUtil\nimport com.lyy.keepassa.view.dialog.OnMsgBtClickListener\n\n/**\n * 指纹签名验证，切换功能后，如果不重新验证指纹，弹出对话框提示验证\n */\n@TargetApi(Build.VERSION_CODES.M)\n@Route(path = \"/setting/fingerprint\")\nclass FingerprintActivity : BaseActivity<ActivityFingerprintBinding>() {\n  private lateinit var module: FingerprintModule\n  private val closedFragment: FingerprintCloseFragment by lazy {\n    FingerprintCloseFragment()\n  }\n  private val fingerprintDescFragment: FingerprintDescFragment by lazy {\n    FingerprintDescFragment()\n  }\n\n  companion object {\n    const val FLAG_CLOSE = 1\n    const val FLAG_FULL_UNLOCK = 2\n    const val FLAG_QUICK_UNLOCK = 3\n\n    fun toFingerprintActivity(ac: FragmentActivity) {\n      ARouter.getInstance()\n        .build(\"/setting/fingerprint\")\n        .withOptionsCompat(\n          ActivityOptionsCompat.makeSceneTransitionAnimation(ac)\n        )\n        .navigation(ac)\n    }\n  }\n\n  override fun setLayoutId(): Int {\n    return R.layout.activity_fingerprint\n  }\n\n  override fun initData(savedInstanceState: Bundle?) {\n    super.initData(savedInstanceState)\n    module = ViewModelProvider(this).get(FingerprintModule::class.java)\n    if (!FingerprintUtil.hasBiometricPrompt(this) || BaseApp.dbRecord == null) {\n      finishAfterTransition()\n      return\n    }\n    toolbar.title = getString(R.string.fingerprint_unlock)\n\n    // 需要放在getQuickUnlockRecord之前\n    binding.swBt.setOnCheckedChangeListener { _, isChecked ->\n      changeBg(isChecked)\n    }\n\n    module.getQuickUnlockRecord(BaseApp.dbRecord!!.localDbUri)\n      .observe(this, Observer { record ->\n        if (record == null) {\n          module.oldFlag = FLAG_CLOSE\n          module.curFlag = FLAG_CLOSE\n          binding.swBt.isChecked = false\n          supportFragmentManager.beginTransaction()\n            .replace(R.id.flContent, closedFragment)\n            .commitAllowingStateLoss()\n          return@Observer\n        }\n        if (!record.isUseFingerprint) {\n          module.oldFlag = FLAG_QUICK_UNLOCK\n          module.curFlag = FLAG_QUICK_UNLOCK\n          binding.swBt.isChecked = true\n          return@Observer\n        }\n        binding.swBt.isChecked = true\n        module.oldFlag = FLAG_FULL_UNLOCK\n        module.curFlag = FLAG_FULL_UNLOCK\n      })\n  }\n\n  override fun finishAfterTransition() {\n    // 当前标志不为关闭，并且当前标志和进入的标志不一致，则需要提示用户验证指纹\n    if (module.curFlag != FLAG_CLOSE && module.curFlag != module.oldFlag) {\n      Routerfit.create(DialogRouter::class.java).showMsgDialog(\n        msgContent = ResUtil.getString(R.string.hint_finger_print_verfiy),\n        btnClickListener = object : OnMsgBtClickListener {\n          override fun onCover(v: Button) {\n          }\n\n          override fun onEnter(v: Button) {\n            fingerprintDescFragment.showBiometricPrompt()\n          }\n\n          override fun onCancel(v: Button) {\n            super@FingerprintActivity.finishAfterTransition()\n          }\n        }\n      )\n    } else {\n      super@FingerprintActivity.finishAfterTransition()\n    }\n  }\n\n  override fun onBackPressed() {\n//    super.onBackPressed()\n    finishAfterTransition()\n  }\n\n  /**\n   * 切换fragment改变背景\n   */\n  private fun changeBg(isChecked: Boolean) {\n    val view = findViewById<View>(R.id.vBg)\n    if (isDestroyed || isFinishing || !view.isAttachedToWindow) {\n      return\n    }\n    if (isChecked) {\n      // 打开\n      binding.closeHint.text = getString(R.string.open1)\n      binding.flSwitch.setBackgroundColor(getColor(R.color.grey600))\n      supportFragmentManager.beginTransaction()\n        .replace(R.id.flContent, fingerprintDescFragment)\n        .commitAllowingStateLoss()\n    } else {\n      // 关闭\n      module.curFlag = FLAG_CLOSE\n      binding.closeHint.text = getString(R.string.disable)\n      binding.flSwitch.setBackgroundColor(getColor(R.color.colorAccent))\n//      HitUtil.toaskShort(\"${getString(R.string.fingerprint_unlock)}${getString(R.string.closed)}\")\n      module.deleteQuickInfo()\n      supportFragmentManager.beginTransaction()\n        .replace(R.id.flContent, closedFragment)\n        .commitAllowingStateLoss()\n    }\n\n    val finalRadius = view.width.coerceAtLeast(view.height)\n    val anim = ViewAnimationUtils.createCircularReveal(\n      view, if (isChecked) view.right else 0, 0, 0f, finalRadius.toFloat()\n    )\n    view.setBackgroundResource(\n      if (isChecked) R.color.colorPrimary else R.color.grey600\n    )\n    anim.duration = resources.getInteger(R.integer.anim_duration_long)\n      .toLong()\n    anim.interpolator = AccelerateInterpolator()\n    anim.start()\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/fingerprint/FingerprintCloseFragment.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.view.fingerprint\n\nimport android.annotation.SuppressLint\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseFragment\nimport com.lyy.keepassa.databinding.FragmentFingerprintCloseBinding\n\n/**\n * 指纹关闭展示页面\n */\nclass FingerprintCloseFragment : BaseFragment<FragmentFingerprintCloseBinding>() {\n  @SuppressLint(\"SetTextI18n\")\n  override fun initData() {\n    binding.tvHint.text = \"${getString(R.string.fingerprint_unlock)}${getString(R.string.closed)}\"\n  }\n\n  override fun setLayoutId(): Int {\n    return R.layout.fragment_fingerprint_close\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/fingerprint/FingerprintDescFragment.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.view.fingerprint\n\nimport android.annotation.SuppressLint\nimport android.os.Build.VERSION_CODES\nimport android.view.View\nimport androidx.annotation.RequiresApi\nimport androidx.arch.core.executor.ArchTaskExecutor\nimport androidx.biometric.BiometricManager.Authenticators.BIOMETRIC_STRONG\nimport androidx.biometric.BiometricPrompt\nimport androidx.biometric.BiometricPrompt.AuthenticationCallback\nimport androidx.biometric.BiometricPrompt.AuthenticationResult\nimport androidx.biometric.BiometricPrompt.CryptoObject\nimport androidx.biometric.BiometricPrompt.ERROR_NEGATIVE_BUTTON\nimport androidx.biometric.BiometricPrompt.PromptInfo\nimport androidx.lifecycle.ViewModelProvider\nimport com.arialyy.frame.util.KeyStoreUtil\nimport com.arialyy.frame.util.ResUtil\nimport com.blankj.utilcode.util.EncryptUtils\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseApp\nimport com.lyy.keepassa.base.BaseFragment\nimport com.lyy.keepassa.common.PassType\nimport com.lyy.keepassa.databinding.FragmentFingerprintDesxBinding\nimport com.lyy.keepassa.entity.QuickUnLockRecord\nimport com.lyy.keepassa.util.HitUtil\nimport com.lyy.keepassa.util.VibratorUtil\nimport timber.log.Timber\nimport java.security.KeyStoreException\nimport javax.crypto.Cipher\n\n/**\n * 指纹解锁描述\n */\n@RequiresApi(VERSION_CODES.M)\nclass FingerprintDescFragment : BaseFragment<FragmentFingerprintDesxBinding>(),\n  View.OnClickListener {\n\n  private val keyStoreUtil = KeyStoreUtil()\n  private var isFullUnlock: Boolean = false\n  private lateinit var module: FingerprintModule\n  private var lastFlag = FingerprintActivity.FLAG_CLOSE\n\n  override fun initData() {\n    module = ViewModelProvider(requireActivity()).get(FingerprintModule::class.java)\n    if (module.curFlag == FingerprintActivity.FLAG_FULL_UNLOCK) {\n      isFullCheck(true)\n    } else if (module.curFlag == FingerprintActivity.FLAG_QUICK_UNLOCK) {\n      isFullCheck(false)\n    }\n    lastFlag = module.curFlag\n\n    binding.rlQuick.setOnClickListener(this)\n    binding.rlFull.setOnClickListener(this)\n  }\n\n  override fun setLayoutId(): Int {\n    return R.layout.fragment_fingerprint_desx\n  }\n\n  override fun onClick(v: View) {\n    if (v.id == R.id.rlQuick) {\n      isFullUnlock = false\n      module.curFlag = FingerprintActivity.FLAG_QUICK_UNLOCK\n      showBiometricPrompt()\n      isFullCheck(isFullUnlock)\n      return\n    }\n\n    if (v.id == R.id.rlFull) {\n      isFullUnlock = true\n      module.curFlag = FingerprintActivity.FLAG_FULL_UNLOCK\n      showBiometricPrompt()\n      isFullCheck(isFullUnlock)\n    }\n  }\n\n  private fun isFullCheck(isFull: Boolean) {\n    binding.scFull.isChecked = isFull\n    binding.scQuick.isChecked = !isFull\n  }\n\n  /**\n   * 恢复状态\n   */\n  private fun goBackCheckStat() {\n    when (lastFlag) {\n      FingerprintActivity.FLAG_QUICK_UNLOCK -> isFullCheck(false)\n      FingerprintActivity.FLAG_FULL_UNLOCK -> isFullCheck(true)\n      else -> {\n        binding.scFull.isChecked = false\n        binding.scQuick.isChecked = false\n      }\n    }\n  }\n\n  fun showBiometricPrompt() {\n    if (BaseApp.passType == PassType.ONLY_KEY) {\n      showOnlyKeyBiometricPrompt()\n      return\n    }\n\n    showNormalBiometricPrompt()\n  }\n\n  /**\n   * 处理数据库有密码的指纹解锁配置\n   * https://developer.android.com/training/sign-in/biometric-auth#kotlin\n   */\n  @SuppressLint(\"RestrictedApi\")\n  fun showNormalBiometricPrompt() {\n    val promptInfo = PromptInfo.Builder()\n      .setTitle(ResUtil.getString(R.string.fingerprint_unlock))\n      .setSubtitle(ResUtil.getString(R.string.verify_finger))\n      .setNegativeButtonText(ResUtil.getString(R.string.cancel))\n\n      // DEVICE_CREDENTIAL pin 码\n      // BIOMETRIC_STRONG 指纹\n      .setAllowedAuthenticators(BIOMETRIC_STRONG)\n//        .setConfirmationRequired(false)\n      .build()\n    val biometricPrompt = BiometricPrompt(this, ArchTaskExecutor.getMainThreadExecutor(),\n      object : AuthenticationCallback() {\n        override fun onAuthenticationError(\n          errorCode: Int,\n          errString: CharSequence\n        ) {\n          goBackCheckStat()\n          if (!isAdded) {\n            return\n          }\n          val str = if (errorCode == ERROR_NEGATIVE_BUTTON) {\n            \"${ResUtil.getString(R.string.verify_finger)}${ResUtil.getString(R.string.cancel)}\"\n          } else {\n            ResUtil.getString(R.string.verify_finger_fail)\n          }\n          HitUtil.snackShort(mRootView, str)\n        }\n\n        override fun onAuthenticationSucceeded(result: AuthenticationResult) {\n          super.onAuthenticationSucceeded(result)\n\n          try {\n            val auth: CryptoObject? = result.cryptoObject\n            if (auth == null || auth.cipher == null) {\n              return\n            }\n            val cipher = auth.cipher!!\n            val useKey = BaseApp.dbKeyPath.isNullOrEmpty()\n\n            Timber.d(\n              \"passLen = ${BaseApp.dbPass.length}, cipherBloack = ${cipher.blockSize}, byteLen = ${\n                BaseApp.dbPass.toByteArray(\n                  Charsets.UTF_8\n                ).size\n              }\"\n            )\n            // val encrypted = cipher.doFinal(BaseApp.dbPass.toByteArray(Charsets.UTF_8))\n            val passPair = keyStoreUtil.encryptData(cipher, BaseApp.dbPass)\n            // val passPair = keyStoreUtil.encryptData(cipher, \"123456\")\n            val quickInfo = QuickUnLockRecord(\n              dbUri = BaseApp.dbRecord!!.localDbUri,\n              dbPass = passPair.first,\n              keyPath = BaseApp.dbKeyPath,\n              isUseKey = useKey,\n              isUseFingerprint = isFullUnlock,\n              passIv = passPair.second\n            )\n            module.saveNormalQuickInfo(quickInfo)\n            HitUtil.toaskShort(\"${ResUtil.getString(R.string.verify_finger)} ${ResUtil.getString(R.string.success)}\")\n            VibratorUtil.vibrator(300)\n\n            module.oldFlag = if (isFullUnlock) {\n              FingerprintActivity.FLAG_FULL_UNLOCK\n            } else {\n              FingerprintActivity.FLAG_QUICK_UNLOCK\n            }\n\n            requireActivity().finishAfterTransition()\n            lastFlag = module.curFlag\n          } catch (e: KeyStoreException) {\n            Timber.e(e)\n            keyStoreUtil.deleteKeyStore()\n            keyStoreUtil.load(null)\n            HitUtil.snackShort(mRootView, ResUtil.getString(R.string.error_keystore))\n          } catch (e: Exception) {\n            Timber.e(e)\n            keyStoreUtil.deleteKeyStore()\n            keyStoreUtil.useUnsafeMode = true\n            HitUtil.snackLong(mRootView, ResUtil.getString(R.string.error_keystore))\n          }\n        }\n\n        override fun onAuthenticationFailed() {\n          super.onAuthenticationFailed()\n          goBackCheckStat()\n          HitUtil.snackShort(mRootView, ResUtil.getString(R.string.verify_finger_fail))\n        }\n      })\n    try {\n      // Displays the \"log in\" prompt.\n      biometricPrompt.authenticate(\n        promptInfo,\n        CryptoObject(keyStoreUtil.getEncryptCipher())\n      )\n    } catch (e: Exception) {\n      keyStoreUtil.deleteKeyStore()\n      Timber.e(e)\n    }\n  }\n\n  /**\n   * 处理数据库仅使用密钥的指纹解锁配置\n   * https://developer.android.com/training/sign-in/biometric-auth#kotlin\n   */\n  @SuppressLint(\"RestrictedApi\")\n  fun showOnlyKeyBiometricPrompt() {\n    val promptInfo = PromptInfo.Builder()\n      .setTitle(ResUtil.getString(R.string.fingerprint_unlock))\n      .setSubtitle(ResUtil.getString(R.string.verify_finger))\n      .setNegativeButtonText(ResUtil.getString(R.string.cancel))\n//        .setConfirmationRequired(false)\n      .build()\n    val biometricPrompt = BiometricPrompt(this, ArchTaskExecutor.getMainThreadExecutor(),\n      object : AuthenticationCallback() {\n        override fun onAuthenticationError(\n          errorCode: Int,\n          errString: CharSequence\n        ) {\n          goBackCheckStat()\n          if (!isAdded) {\n            return\n          }\n          val str = if (errorCode == ERROR_NEGATIVE_BUTTON) {\n            \"${ResUtil.getString(R.string.verify_finger)}${ResUtil.getString(R.string.cancel)}\"\n          } else {\n            ResUtil.getString(R.string.verify_finger_fail)\n          }\n          HitUtil.snackShort(mRootView, str)\n        }\n\n        override fun onAuthenticationSucceeded(result: AuthenticationResult) {\n          super.onAuthenticationSucceeded(result)\n\n          val useKey = BaseApp.dbKeyPath.isNotEmpty()\n          val quickInfo = QuickUnLockRecord(\n            dbUri = BaseApp.dbRecord!!.localDbUri,\n            keyPath = BaseApp.dbKeyPath,\n            isUseKey = useKey,\n            isUseFingerprint = isFullUnlock,\n          )\n          module.saveOnlyKeyQuickInfo(quickInfo)\n\n          HitUtil.toaskShort(\"${ResUtil.getString(R.string.verify_finger)} ${ResUtil.getString(R.string.success)}\")\n          VibratorUtil.vibrator(300)\n\n          module.oldFlag = if (isFullUnlock) {\n            FingerprintActivity.FLAG_FULL_UNLOCK\n          } else {\n            FingerprintActivity.FLAG_QUICK_UNLOCK\n          }\n\n          requireActivity().finishAfterTransition()\n          lastFlag = module.curFlag\n        }\n\n        override fun onAuthenticationFailed() {\n          super.onAuthenticationFailed()\n          goBackCheckStat()\n          HitUtil.snackShort(mRootView, ResUtil.getString(R.string.verify_finger_fail))\n        }\n      })\n    biometricPrompt.authenticate(promptInfo)\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/fingerprint/FingerprintModule.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.view.fingerprint\n\nimport android.annotation.TargetApi\nimport android.os.Build\nimport androidx.lifecycle.liveData\nimport androidx.lifecycle.viewModelScope\nimport com.lyy.keepassa.base.BaseApp\nimport com.lyy.keepassa.base.BaseModule\nimport com.lyy.keepassa.entity.AutoFillParam\nimport com.lyy.keepassa.entity.QuickUnLockRecord\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.launch\nimport kotlinx.coroutines.withContext\n\n@TargetApi(Build.VERSION_CODES.O)\nclass FingerprintModule : BaseModule() {\n\n  // 旧的类型\n  var oldFlag = FingerprintActivity.FLAG_CLOSE\n  var curFlag = FingerprintActivity.FLAG_CLOSE\n  var autoFillParam: AutoFillParam? = null\n\n  fun isAutoFill() = autoFillParam != null\n\n  /**\n   * 获取快速解锁记录\n   */\n  fun getQuickUnlockRecord(dbUri: String) = liveData {\n    var record: QuickUnLockRecord? = null\n    withContext(Dispatchers.IO) {\n      val dao = BaseApp.appDatabase.quickUnlockDao()\n      record = dao.findRecord(dbUri)\n    }\n    emit(record)\n  }\n\n  /**\n   * 移除快速解锁\n   */\n  fun deleteQuickInfo() {\n    if (BaseApp.dbRecord == null) {\n      return\n    }\n    viewModelScope.launch(Dispatchers.IO) {\n      val dao = BaseApp.appDatabase.quickUnlockDao()\n      val record = dao.findRecord(BaseApp.dbRecord!!.localDbUri)\n      if (record != null) {\n        dao.deleteRecord(record)\n      }\n    }\n  }\n\n  /**\n   * 保存有密码的指纹解锁配置\n   */\n  fun saveNormalQuickInfo(info: QuickUnLockRecord) {\n    viewModelScope.launch(Dispatchers.IO) {\n      val dao = BaseApp.appDatabase.quickUnlockDao()\n      val record = dao.findRecord(info.dbUri)\n      if (record != null) {\n        record.dbPass = info.dbPass\n        record.isUseFingerprint = info.isUseFingerprint\n        record.isUseKey = info.isUseKey\n        record.keyPath = info.keyPath\n        record.passIv = info.passIv\n        dao.updateRecord(record)\n      } else {\n        dao.saveRecord(info)\n      }\n    }\n  }\n\n  /**\n   * 保存仅有key的指纹解锁配置\n   */\n  fun saveOnlyKeyQuickInfo(info: QuickUnLockRecord) {\n    viewModelScope.launch(Dispatchers.IO) {\n      val dao = BaseApp.appDatabase.quickUnlockDao()\n      val record = dao.findRecord(info.dbUri)\n      if (record != null) {\n        record.dbPass = \"\"\n        record.isUseFingerprint = info.isUseFingerprint\n        record.isUseKey = info.isUseKey\n        record.keyPath = info.keyPath\n        dao.updateRecord(record)\n      } else {\n        dao.saveRecord(info)\n      }\n    }\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/fingerprint/KeyStoreUtil.kt",
    "content": "// /*\n//  * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n//  *\n//  * This Source Code Form is subject to the terms of the Mozilla Public\n//  * License, v. 2.0. If a copy of the MPL was not distributed with this\n//  * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n//  */\n//\n//\n// package com.lyy.keepassa.view.fingerprint\n//\n// import android.os.Build.VERSION_CODES\n// import android.security.keystore.KeyGenParameterSpec\n// import android.security.keystore.KeyPermanentlyInvalidatedException\n// import android.security.keystore.KeyProperties\n// import android.util.Base64\n// import android.util.Log\n// import androidx.annotation.RequiresApi\n// import java.io.IOException\n// import java.security.InvalidAlgorithmParameterException\n// import java.security.KeyStore\n// import java.security.KeyStore.LoadStoreParameter\n// import java.security.NoSuchAlgorithmException\n// import java.security.NoSuchProviderException\n// import java.security.SecureRandom\n// import java.security.cert.CertificateException\n// import javax.crypto.BadPaddingException\n// import javax.crypto.Cipher\n// import javax.crypto.IllegalBlockSizeException\n// import javax.crypto.KeyGenerator\n// import javax.crypto.SecretKey\n// import javax.crypto.spec.IvParameterSpec\n//\n// /**\n//  * https://github.com/stevenocean/UnpasswdDecrypt/blob/master/app/src/main/java/io/github/stevenocean/unpasswddecrypt/MainActivity.java\n//  */\n// @RequiresApi(VERSION_CODES.M)\n// class KeyStoreUtil {\n//   private val TAG = javaClass.simpleName\n//   private val keyStore: KeyStore = KeyStore.getInstance(\"AndroidKeyStore\")\n//   private val keystoreAlias = \"KeepassA\"\n//\n//   companion object {\n//     var keyStorePass = \"\".toCharArray()\n//   }\n//\n//   init {\n//     load(null)\n//   }\n//\n//   fun load(param: LoadStoreParameter?) {\n//     try {\n//       keyStore.load(param)\n//\n//       if (keyStore.getKey(keystoreAlias, keyStorePass) == null) {\n//         generateKeyStore()\n//       }\n//     } catch (e: Exception) {\n//       e.printStackTrace()\n//       deleteKeyStore()\n//     }\n//   }\n//\n//   fun deleteKeyStore() {\n//     try {\n//       keyStore.deleteEntry(keystoreAlias)\n//     } catch (e: Exception) {\n//       e.printStackTrace()\n//     }\n//   }\n//\n//   /**\n//    * 生成key，并将key存到keystore中\n//    */\n//   @kotlin.jvm.Throws(\n//     NoSuchProviderException::class,\n//     NoSuchAlgorithmException::class,\n//     IOException::class,\n//     CertificateException::class,\n//     InvalidAlgorithmParameterException::class\n//   ) private fun generateKeyStore(): SecretKey {\n//     // AES + CBC + PKCS7\n//     val generator: KeyGenerator =\n//       KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, \"AndroidKeyStore\")\n//     keyStore.load(null)\n//     val builder = KeyGenParameterSpec.Builder(\n//       keystoreAlias,\n//       KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT\n//     )\n//       .setUserAuthenticationRequired(true)\n//       .setBlockModes(KeyProperties.BLOCK_MODE_CBC)\n//       .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)\n//\n//     if (android.os.Build.VERSION.SDK_INT >= VERSION_CODES.N) {\n//       // 默认为true，但是在某些手机上会出现Key user not authenticated\n//       builder.setInvalidatedByBiometricEnrollment(false)\n//     }\n//\n//     generator.init(builder.build())\n//\n//     // Generate (symmetric) key, and store to KeyStore\n//     val sk: SecretKey = generator.generateKey()\n//     Log.d(TAG, String.format(\"Generate key success %s, %s\", sk.algorithm, sk.format))\n//     return sk\n//   }\n//\n//   /**\n//    * 获取加密对象\n//    */\n//   fun getEncryptCipher(): Cipher {\n//     load(null)\n//     val sk = getSk()\n//\n//     val cipher = Cipher.getInstance(\n//       KeyProperties.KEY_ALGORITHM_AES\n//         + \"/\"\n//         + KeyProperties.BLOCK_MODE_CBC\n//         + \"/\"\n//         + KeyProperties.ENCRYPTION_PADDING_PKCS7\n//     )\n//     // https://stackoverflow.com/questions/44886119/key-permanently-invalidated-exception-after-adding-removing-fingerprint\n//     try {\n//       cipher.init(Cipher.ENCRYPT_MODE, sk, SecureRandom())\n//     } catch (e: KeyPermanentlyInvalidatedException) {\n//       keyStore.deleteEntry(keystoreAlias)\n//       return getEncryptCipher()\n//     }\n//\n//     return cipher\n//   }\n//\n//   /**\n//    * 获取解密对象\n//    */\n//   fun getDecryptCipher(iv: ByteArray): Cipher {\n//     keyStore.load(null)\n//\n//     val cipher = Cipher.getInstance(\n//       KeyProperties.KEY_ALGORITHM_AES\n//         + \"/\"\n//         + KeyProperties.BLOCK_MODE_CBC\n//         + \"/\"\n//         + KeyProperties.ENCRYPTION_PADDING_PKCS7\n//     )\n//     try {\n//       cipher.init(Cipher.DECRYPT_MODE, getSk(), IvParameterSpec(iv))\n//     } catch (e: KeyPermanentlyInvalidatedException) {\n//       keyStore.deleteEntry(keystoreAlias)\n//       return getDecryptCipher(iv)\n//     }\n//\n//     return cipher\n//   }\n//\n//   private fun getSk(): SecretKey {\n//     var key = keyStore.getKey(keystoreAlias, keyStorePass)\n//     if (key == null) {\n//       key = generateKeyStore()\n//     }\n//     return key as SecretKey\n//   }\n//\n//   /**\n//    * 加密数据\n//    * @param text 明文\n//    * @return first 秘文，second iv\n//    */\n//   @kotlin.jvm.Throws(\n//     IllegalBlockSizeException::class,\n//     BadPaddingException::class\n//   )\n//   fun encryptData(\n//     cipher: Cipher,\n//     text: String\n//   ): Pair<String, ByteArray> {\n//     val encrypted = cipher.doFinal(text.toByteArray(Charsets.UTF_8))\n//     val iv = cipher.iv\n//     val encryptedWithBase64: String = Base64.encodeToString(encrypted, Base64.URL_SAFE)\n//     return Pair(encryptedWithBase64, iv)\n//   }\n//\n//   /**\n//    * 解密数据\n//    * @param text 密文\n//    * @return 明文\n//    */\n//   fun decryptData(\n//     cipher: Cipher,\n//     text: String\n//   ): String {\n//     val encryptedBytes: ByteArray = Base64.decode(text, Base64.URL_SAFE)\n//     val decryptedBytes = cipher.doFinal(encryptedBytes)\n//     return decryptedBytes.toString(Charsets.UTF_8)\n//   }\n// }"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/icon/IconAdapter.kt",
    "content": "package com.lyy.keepassa.view.icon\n\nimport android.content.Context\nimport android.view.View\nimport android.widget.TextView\nimport androidx.appcompat.widget.AppCompatImageView\nimport com.arialyy.frame.util.adapter.AbsHolder\nimport com.arialyy.frame.util.adapter.AbsRVAdapter\nimport com.bumptech.glide.Glide\nimport com.keepassdroid.database.PwIconCustom\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.entity.SimpleItemEntity\nimport com.lyy.keepassa.util.IconUtil\nimport com.lyy.keepassa.view.icon.IconAdapter.Holder\n\n/**\n * icon 适配器\n */\nclass IconAdapter(\n  context: Context,\n  data: List<SimpleItemEntity>\n) : AbsRVAdapter<SimpleItemEntity, Holder>(context, data) {\n\n  override fun getViewHolder(\n    convertView: View?,\n    viewType: Int\n  ): Holder {\n    return Holder(convertView!!)\n  }\n\n  override fun setLayoutId(type: Int): Int {\n    return R.layout.item_icon\n  }\n\n  override fun bindData(\n    holder: Holder?,\n    position: Int,\n    item: SimpleItemEntity?\n  ) {\n    if (item!!.icon != -1) {\n      Glide.with(context)\n          .load(IconUtil.getIconById(item.icon))\n          .error(IconUtil.getIconById(0))\n          .into(holder!!.img)\n    } else {\n      Glide.with(context)\n          .load((item.obj as PwIconCustom).imageData)\n          .error(R.drawable.ic_image_broken_24px)\n          .into(holder!!.img)\n    }\n    val index = item.id + 1\n    holder.num.text = index.toString()\n\n  }\n\n  class Holder(view: View) : AbsHolder(view) {\n    val img: AppCompatImageView = view.findViewById(R.id.img)\n    val num: TextView = view.findViewById(R.id.num)\n  }\n\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/icon/IconBottomSheetDialog.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.view.icon\n\nimport android.os.Bundle\nimport android.view.View\nimport android.widget.RelativeLayout\nimport androidx.lifecycle.Observer\nimport androidx.lifecycle.ViewModelProvider\nimport androidx.recyclerview.widget.GridLayoutManager\nimport com.airbnb.lottie.LottieAnimationView\nimport com.arialyy.frame.util.adapter.RvItemClickSupport\nimport com.google.android.material.bottomsheet.BottomSheetBehavior\nimport com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_EXPANDED\nimport com.keepassdroid.database.PwDatabaseV4\nimport com.keepassdroid.database.PwIconCustom\nimport com.keepassdroid.database.PwIconStandard\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseApp\nimport com.lyy.keepassa.base.BaseBottomSheetDialogFragment\nimport com.lyy.keepassa.databinding.DialogEntryIconBinding\nimport com.lyy.keepassa.entity.SimpleItemEntity\nimport kotlinx.coroutines.MainScope\nimport kotlinx.coroutines.cancel\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 2021/3/24\n **/\nclass IconBottomSheetDialog : BaseBottomSheetDialogFragment<DialogEntryIconBinding>() {\n\n  companion object {\n    val ICON_TYPE_DEFAULT = 0\n    val ICON_TYPE_CUSTOM = 1\n  }\n\n  private lateinit var behavior: BottomSheetBehavior<RelativeLayout>\n  private lateinit var module: IconModule\n  private var scope = MainScope()\n  private var curType = ICON_TYPE_DEFAULT\n  private var callback: IconItemCallback? = null\n\n  override fun setLayoutId(): Int {\n    return R.layout.dialog_entry_icon\n  }\n\n  override fun init(savedInstanceState: Bundle?) {\n    super.init(savedInstanceState)\n    behavior = BottomSheetBehavior.from(binding.llContent)\n    module = ViewModelProvider(requireActivity())[IconModule::class.java]\n    if (!BaseApp.isV4 || (BaseApp.KDB.pm as PwDatabaseV4).customIcons.isNullOrEmpty()) {\n      binding.rg.visibility = View.GONE\n    }\n    behavior.state = STATE_EXPANDED\n    initWidget()\n  }\n\n  fun setCallback(callback: IconItemCallback) {\n    this.callback = callback\n  }\n\n  private fun initWidget() {\n    val data = arrayListOf<SimpleItemEntity>()\n    val adapter = IconAdapter(requireContext(), data)\n    binding.rvList.adapter = adapter\n    binding.rvList.layoutManager = GridLayoutManager(requireContext(), 6)\n    binding.rvList.hasFixedSize()\n    val observer = Observer<List<SimpleItemEntity>> {\n      data.clear()\n      data.addAll(it)\n      adapter.notifyDataSetChanged()\n      hideLoadingAnim()\n    }\n\n    showLoadingAnim()\n    module.getDefaultIconList()\n      .observe(this, observer)\n\n    binding.rg.setOnCheckedChangeListener { _, checkedId ->\n      showLoadingAnim()\n      if (checkedId == R.id.mrbDefault) {\n        curType = ICON_TYPE_DEFAULT\n        module.getDefaultIconList()\n          .observe(this, observer)\n        return@setOnCheckedChangeListener\n      }\n      curType = ICON_TYPE_CUSTOM\n      module.getCustomIconList()\n        .observe(this, observer)\n    }\n\n    RvItemClickSupport.addTo(binding.rvList)\n      .setOnItemClickListener { _, position, _ ->\n        val item = data[position]\n        if (curType == ICON_TYPE_DEFAULT) {\n          callback?.onDefaultIcon(PwIconStandard(item.icon))\n          dismiss()\n          return@setOnItemClickListener\n        }\n        callback?.onCustomIcon(item.obj as PwIconCustom)\n        dismiss()\n      }\n  }\n\n  override fun onDestroy() {\n    super.onDestroy()\n    scope.cancel()\n  }\n\n  private fun showLoadingAnim() {\n    val animationView = getLadingView()\n    animationView.visibility = View.VISIBLE\n    animationView.playAnimation()\n  }\n\n  private fun hideLoadingAnim() {\n    val animationView = getLadingView()\n    animationView.cancelAnimation()\n    animationView.visibility = View.GONE\n  }\n\n  private fun getLadingView(): LottieAnimationView {\n    if (!binding.vsLoading.isInflated) {\n      binding.vsLoading.viewStub?.inflate()\n    }\n    val anim = binding.vsLoading.root as LottieAnimationView\n    anim.repeatCount = 10\n    return anim\n  }\n}\n\ninterface IconItemCallback {\n  fun onDefaultIcon(defIcon: PwIconStandard)\n  fun onCustomIcon(customIcon: PwIconCustom)\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/icon/IconModule.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.view.icon\n\nimport androidx.lifecycle.liveData\nimport com.keepassdroid.database.PwDatabaseV4\nimport com.lyy.keepassa.base.BaseApp\nimport com.lyy.keepassa.base.BaseModule\nimport com.lyy.keepassa.entity.SimpleItemEntity\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 2021/3/24\n **/\nclass IconModule : BaseModule() {\n\n  /**\n   * get default icon entiry\n   */\n  fun getDefaultIconList() = liveData<List<SimpleItemEntity>>{\n    val list = arrayListOf<SimpleItemEntity>()\n    for (i in 0..68) {\n      val item = SimpleItemEntity()\n      item.id = i\n      item.icon = i\n      list.add(item)\n    }\n    emit(list)\n  }\n\n  /**\n   * only pwV4 has custom icon\n   */\n  fun getCustomIconList() = liveData<List<SimpleItemEntity>> {\n    val list = arrayListOf<SimpleItemEntity>()\n    if (!BaseApp.isV4){\n      emit(list)\n      return@liveData\n    }\n\n    val v4Group = BaseApp.KDB.pm as PwDatabaseV4\n    for ((i, icon) in v4Group.customIcons.withIndex()) {\n      val item = SimpleItemEntity()\n      item.id = i\n      item.icon = -1\n      item.obj = icon\n      list.add(item)\n      emit(list)\n    }\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/launcher/ChangeDbFragment.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.view.launcher\n\nimport android.app.ActivityOptions\nimport android.content.Context\nimport android.content.Intent\nimport android.view.View\nimport android.widget.TextView\nimport androidx.appcompat.widget.AppCompatImageView\nimport androidx.core.app.ActivityOptionsCompat\nimport androidx.lifecycle.Observer\nimport androidx.lifecycle.ViewModelProvider\nimport androidx.recyclerview.widget.GridLayoutManager\nimport androidx.transition.TransitionInflater\nimport com.alibaba.android.arouter.facade.annotation.Route\nimport com.arialyy.frame.router.Routerfit\nimport com.arialyy.frame.util.adapter.AbsHolder\nimport com.arialyy.frame.util.adapter.AbsRVAdapter\nimport com.arialyy.frame.util.adapter.RvItemClickSupport\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseFragment\nimport com.lyy.keepassa.databinding.FragmentChangeDbBinding\nimport com.lyy.keepassa.entity.SimpleItemEntity\nimport com.lyy.keepassa.router.ActivityRouter\nimport com.lyy.keepassa.util.KeepassAUtil\nimport com.lyy.keepassa.view.StorageType\nimport com.lyy.keepassa.view.StorageType.AFS\nimport com.lyy.keepassa.view.StorageType.DROPBOX\nimport com.lyy.keepassa.view.StorageType.ONE_DRIVE\nimport com.lyy.keepassa.view.StorageType.WEBDAV\nimport com.lyy.keepassa.view.launcher.ChangeDbFragment.Adataer.Holder\n\n/**\n * 选择数据库\n */\n@Route(path = \"/launcher/changeDb\")\nclass ChangeDbFragment : BaseFragment<FragmentChangeDbBinding>() {\n  private lateinit var modlue: LauncherModule\n  private var storageType: StorageType = AFS\n  private lateinit var dbDelegate: IOpenDbDelegate\n\n  companion object {\n    val FM_TAG = \"ChangeDbFragment\"\n  }\n\n  override fun initData() {\n    context?.let {\n      enterTransition = TransitionInflater.from(it).inflateTransition(R.transition.slide_enter)\n      exitTransition = TransitionInflater.from(it).inflateTransition(R.transition.slide_exit)\n      returnTransition = TransitionInflater.from(it).inflateTransition(R.transition.slide_return)\n    }\n\n    initList()\n  }\n\n  override fun setLayoutId(): Int {\n    return R.layout.fragment_change_db\n  }\n\n  private fun initList() {\n    modlue = ViewModelProvider(this).get(LauncherModule::class.java)\n    val data: ArrayList<SimpleItemEntity> = ArrayList()\n    val adapter = Adataer(requireContext(), data)\n    binding.list.layoutManager = GridLayoutManager(requireContext(), 4)\n    binding.list.adapter = adapter\n\n    modlue.getDbOpenTypeData(requireContext())\n      ?.observe(this, Observer { simpleItemDaos ->\n        run {\n          data.addAll(simpleItemDaos)\n          adapter.notifyDataSetChanged()\n        }\n      })\n    RvItemClickSupport.addTo(binding.list)\n      .setOnItemClickListener { _, position, _ ->\n        if (KeepassAUtil.instance.isFastClick()\n          || activity == null\n          || position < 0\n          || position >= data.size\n        ) {\n          return@setOnItemClickListener\n        }\n        val typeId = data[position].id\n        if (this::dbDelegate.isInitialized) {\n          lifecycle.removeObserver(dbDelegate)\n        }\n        when (typeId) {\n          // 文件系统选择db\n          AFS.type -> {\n            storageType = AFS\n            dbDelegate = OpenAFSDelegate()\n          }\n          DROPBOX.type -> {\n            storageType = DROPBOX\n            dbDelegate = OpenDropBoxDelegate()\n          }\n          WEBDAV.type -> {\n            storageType = WEBDAV\n            dbDelegate = OpenWebDavDelegate()\n          }\n          ONE_DRIVE.type -> {\n            storageType = ONE_DRIVE\n            dbDelegate = OpenOneDriveDelegate()\n          }\n          LauncherModule.HISTORY_ID -> { // 历史记录\n            startActivity(\n              Intent(context, OpenDbHistoryActivity::class.java),\n              ActivityOptions.makeSceneTransitionAnimation(activity)\n                .toBundle()\n            )\n          }\n        }\n\n        if (typeId != LauncherModule.HISTORY_ID) {\n          dbDelegate.startFlow(this)\n          lifecycle.addObserver(dbDelegate)\n        }\n      }\n    binding.fab.setOnClickListener {\n      activity?.let { ac ->\n        Routerfit.create(ActivityRouter::class.java, ac).toCreateDbActivity(\n          ActivityOptionsCompat.makeSceneTransitionAnimation(ac)\n        )\n      }\n    }\n  }\n\n  override fun onActivityResult(\n    requestCode: Int,\n    resultCode: Int,\n    data: Intent?\n  ) {\n    super.onActivityResult(requestCode, resultCode, data)\n    if (this::dbDelegate.isInitialized) {\n      dbDelegate.onActivityResult(requestCode, resultCode, data)\n    }\n  }\n\n  /**\n   * 适配器\n   */\n  private class Adataer(\n    context: Context,\n    data: List<SimpleItemEntity>\n  ) : AbsRVAdapter<SimpleItemEntity, Holder>(context, data) {\n\n    override fun getViewHolder(\n      convertView: View?,\n      viewType: Int\n    ): Holder {\n      return Holder(convertView!!)\n    }\n\n    override fun setLayoutId(type: Int): Int {\n      return R.layout.item_mian_content\n    }\n\n    override fun bindData(\n      holder: Holder?,\n      position: Int,\n      item: SimpleItemEntity?\n    ) {\n      holder!!.icon.setImageResource(item!!.icon)\n      holder.text.text = item.title\n    }\n\n    private class Holder(itemView: View) : AbsHolder(itemView) {\n      val icon: AppCompatImageView = itemView.findViewById(R.id.img)\n      var text: TextView = itemView.findViewById(R.id.text)\n    }\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/launcher/DelHistoryPopMenu.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.view.launcher\n\nimport android.annotation.SuppressLint\nimport android.content.Context\nimport android.view.Gravity\nimport android.view.MenuInflater\nimport android.view.View\nimport androidx.appcompat.view.menu.MenuPopupHelper\nimport androidx.appcompat.widget.PopupMenu\nimport com.arialyy.frame.util.ReflectionUtil\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.view.menu.IPopMenu\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 2020/12/7\n **/\n@SuppressLint(\"RestrictedApi\")\nclass DelHistoryPopMenu(\n  val context: Context,\n  val view: View,\n  val curX: Int\n) : IPopMenu {\n  private val popup: PopupMenu = PopupMenu(context, view, Gravity.START)\n  private val help: MenuPopupHelper\n\n  init {\n    val inflater: MenuInflater = popup.menuInflater\n    inflater.inflate(R.menu.pop_dele_history_record, popup.menu)\n    // 以下代码为强制显示icon\n    val mPopup = ReflectionUtil.getField(PopupMenu::class.java, \"mPopup\")\n    mPopup.isAccessible = true\n    help = mPopup.get(popup) as MenuPopupHelper\n    help.setForceShowIcon(true)\n  }\n\n  fun getPopMenu():PopupMenu{\n    return popup\n  }\n\n  fun show(){\n    help.show(curX, 0)\n  }\n\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/launcher/IAutoFillFinishDelegate.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.view.launcher\n\nimport android.content.Intent\nimport com.lyy.keepassa.base.BaseActivity\nimport com.lyy.keepassa.entity.AutoFillParam\n\ninternal interface IAutoFillFinishDelegate {\n\n  fun handleAutoFill(autoFillParam: AutoFillParam)\n\n  fun onActivityResult(data: Intent?)\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/launcher/IOpenDbDelegate.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.view.launcher\n\nimport android.content.Intent\nimport androidx.lifecycle.Lifecycle\nimport androidx.lifecycle.LifecycleObserver\nimport androidx.lifecycle.OnLifecycleEvent\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 2021/4/25\n **/\ninterface IOpenDbDelegate : LifecycleObserver {\n\n  fun startFlow(fragment: ChangeDbFragment)\n\n  @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)\n  fun onResume()\n\n  fun onActivityResult(\n    requestCode: Int,\n    resultCode: Int,\n    data: Intent?\n  )\n\n  @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)\n  fun destroy()\n\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/launcher/LauncherActivity.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.view.launcher\n\nimport android.app.Activity\nimport android.app.PendingIntent\nimport android.app.assist.AssistStructure\nimport android.content.Context\nimport android.content.Intent\nimport android.content.IntentSender\nimport android.os.Build\nimport android.os.Bundle\nimport android.view.autofill.AutofillManager\nimport androidx.core.app.ActivityOptionsCompat\nimport androidx.fragment.app.Fragment\nimport androidx.lifecycle.Observer\nimport androidx.lifecycle.ViewModelProvider\nimport com.alibaba.android.arouter.facade.annotation.Autowired\nimport com.alibaba.android.arouter.facade.annotation.Route\nimport com.alibaba.android.arouter.launcher.ARouter\nimport com.arialyy.frame.router.Routerfit\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.R.layout\nimport com.lyy.keepassa.base.AnimState\nimport com.lyy.keepassa.base.AnimState.NOT_ANIM\nimport com.lyy.keepassa.base.BaseActivity\nimport com.lyy.keepassa.base.BaseApp\nimport com.lyy.keepassa.databinding.ActivityLauncherBinding\nimport com.lyy.keepassa.entity.AutoFillParam\nimport com.lyy.keepassa.entity.DbHistoryRecord\nimport com.lyy.keepassa.event.ChangeDbEvent\nimport com.lyy.keepassa.event.DbHistoryEvent\nimport com.lyy.keepassa.router.ActivityRouter\nimport com.lyy.keepassa.router.FragmentRouter\nimport com.lyy.keepassa.util.EventBusHelper\nimport org.greenrobot.eventbus.Subscribe\nimport org.greenrobot.eventbus.ThreadMode.MAIN\nimport timber.log.Timber\n\n@Route(path = \"/launcher/activity\")\nclass LauncherActivity : BaseActivity<ActivityLauncherBinding>() {\n\n  private lateinit var module: LauncherModule\n  private var changeDbFragment: ChangeDbFragment? = null\n  private var openDbFragment: OpenDbFragment? = null\n  private var isChangeDb = false\n\n  /**\n   * 启动类型，只有含有历史打开记录时，该记录才有效\n   */\n  @Autowired(name = KEY_OPEN_TYPE)\n  @JvmField\n  var type = OPEN_TYPE_OPEN_DB\n\n  override fun setLayoutId(): Int {\n    return layout.activity_launcher\n  }\n\n  override fun initData(savedInstanceState: Bundle?) {\n    super.initData(savedInstanceState)\n    ARouter.getInstance().inject(this)\n    EventBusHelper.reg(this)\n    module = ViewModelProvider(this)[LauncherModule::class.java]\n    getAutoFillParam()\n\n    module.showPrivacyAgreement(this)\n    initUI()\n    module.securityCheck(this)\n  }\n\n  override fun onNewIntent(intent: Intent?) {\n    super.onNewIntent(intent)\n    getAutoFillParam()\n  }\n\n  private fun getAutoFillParam() {\n    module.autoFillParam = intent.getParcelableExtra(KEY_AUTO_FILL_PARAM)\n    module.autoFillParam?.let {\n      module.autoFillDelegate = if (it.isSave) {\n        SaveEntityDelegate(this)\n      } else {\n        SearchEntityDelegate(this)\n      }\n    }\n  }\n\n  override fun useAnim(): AnimState {\n    return NOT_ANIM\n  }\n\n  /**\n   * 初始化界面\n   */\n  private fun initUI() {\n    module.getLastOpenDbHistory(this)\n      .observe(this, Observer { t ->\n        val fragment: Fragment\n        val tag: String\n        if (t == null) {\n          val p = buildChangDbFragment()\n          tag = p.first\n          changeDbFragment = p.second\n          fragment = changeDbFragment!!\n        } else {\n          if (type == OPEN_TYPE_CHANGE_DB) {\n            val p = buildChangDbFragment()\n            tag = p.first\n            changeDbFragment = p.second\n            fragment = changeDbFragment!!\n          } else {\n            val p = buildOpenDbFragment(record = t)\n            tag = p.first\n            openDbFragment = p.second\n            fragment = openDbFragment!!\n          }\n        }\n\n        supportFragmentManager.beginTransaction()\n          .replace(R.id.content, fragment, tag)\n          .commitNow()\n      })\n  }\n\n  override fun onStart() {\n    super.onStart()\n\n    // 如果数据库已经打开，直接启用到主页，用于快捷方式添加数据后返回的情况\n    if (BaseApp.KDB != null && !BaseApp.isLocked && !module.isFormAutoFill()) {\n      Timber.d(\"数据库已经打开，进入主页\")\n      Routerfit.create(ActivityRouter::class.java, this).toMainActivity(\n        opt = ActivityOptionsCompat.makeSceneTransitionAnimation(this)\n      )\n    }\n  }\n\n  fun superFinish() {\n    super.finish()\n  }\n\n  /**\n   * 切换数据库\n   */\n  fun changeDb() {\n    if (changeDbFragment == null) {\n      changeDbFragment = ChangeDbFragment()\n    }\n    supportFragmentManager.beginTransaction()\n      .replace(R.id.content, changeDbFragment!!)\n      .commitNow()\n    isChangeDb = true\n  }\n\n  override fun onBackPressed() {\n//    super.onBackPressed()\n    if (isChangeDb) {\n      supportFragmentManager.beginTransaction()\n        .replace(R.id.content, openDbFragment!!)\n        .commitNow()\n      isChangeDb = false\n      return\n    }\n    super.onBackPressed()\n  }\n\n  /**\n   * da history is empty\n   */\n  @Subscribe(threadMode = MAIN)\n  fun onHistoryEmpty(event: DbHistoryEvent) {\n    if (event.isEmpty) {\n      isChangeDb = false\n    }\n  }\n\n  /**\n   * 接收切换数据库的回调\n   */\n  @Subscribe(threadMode = MAIN)\n  fun onDbChange(event: ChangeDbEvent) {\n    val record = DbHistoryRecord(\n      time = System.currentTimeMillis(),\n      type = event.uriType.name,\n      localDbUri = event.localFileUri.toString(),\n      cloudDiskPath = event.cloudPath,\n      keyUri = event.keyUri.toString(),\n      dbName = event.dbName\n    )\n    var tag: String = OpenDbFragment.FM_TAG\n    if (openDbFragment == null) {\n      val p = buildOpenDbFragment(record)\n      tag = p.first\n      openDbFragment = p.second\n    }\n    supportFragmentManager.beginTransaction()\n      .replace(R.id.content, openDbFragment!!, tag)\n      .commitNowAllowingStateLoss()\n    openDbFragment!!.updateData(dbRecord = record)\n  }\n\n  private fun buildChangDbFragment(): Pair<String, ChangeDbFragment> {\n    var fragment = supportFragmentManager.findFragmentByTag(ChangeDbFragment.FM_TAG)\n    if (fragment == null || fragment !is ChangeDbFragment) {\n      fragment = Routerfit.create(FragmentRouter::class.java).getChangeDbFragment()\n    }\n    return Pair(ChangeDbFragment.FM_TAG, fragment)\n  }\n\n  private fun buildOpenDbFragment(record: DbHistoryRecord): Pair<String, OpenDbFragment> {\n    var fragment = supportFragmentManager.findFragmentByTag(OpenDbFragment.FM_TAG)\n    if (fragment == null || fragment !is OpenDbFragment) {\n      fragment = Routerfit.create(FragmentRouter::class.java).getOpenDbFragment(record)\n    }\n    return Pair(OpenDbFragment.FM_TAG, fragment)\n  }\n\n  override fun onDestroy() {\n    super.onDestroy()\n    EventBusHelper.unReg(this)\n  }\n\n  companion object {\n\n    // Unique autofillId for dataset intents.\n    const val KEY_AUTO_FILL_PARAM = \"KEY_AUTO_FILL_PARAM\"\n    const val KEY_OPEN_TYPE = \"KEY_OPEN_TYPE\"\n    const val KEY_PKG_NAME = \"DATA_PKG_NAME\"\n    const val OPEN_TYPE_CHANGE_DB = 1\n    const val OPEN_TYPE_OPEN_DB = 2\n    const val EXTRA_ENTRY_ID = \"EXTRA_ENTRY_ID\"\n\n    internal fun startLauncherActivity(\n      context: Context,\n      flags: Int = -1\n    ) {\n      context.startActivity(Intent(context, LauncherActivity::class.java).apply {\n        if (flags != -1) {\n          this.flags = flags\n        }\n      })\n    }\n\n    /**\n     * 从通知进入登录页\n     */\n    internal fun createLauncherPending(context: Context): PendingIntent {\n      return Intent(context, LauncherActivity::class.java).let { notificationIntent ->\n        PendingIntent.getActivity(context, 0, notificationIntent, PendingIntent.FLAG_IMMUTABLE)\n      }\n    }\n\n    /**\n     * 数据库未解锁\n     * @param apkPackageName 第三方apk包名\n     */\n    internal fun getAuthDbIntentSender(\n      context: Context,\n      apkPackageName: String,\n      structure: AssistStructure? = null\n    ): IntentSender {\n      val intent = Intent(context, LauncherActivity::class.java).also {\n        it.putExtra(KEY_AUTO_FILL_PARAM, AutoFillParam(apkPkgName = apkPackageName))\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {\n          structure?.let { stru ->\n            it.putExtra(AutofillManager.EXTRA_ASSIST_STRUCTURE, stru)\n            it.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK\n          }\n        }\n\n      }\n      return PendingIntent.getActivity(\n        context,\n        1,\n        intent,\n        PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_IMMUTABLE\n      )\n        .intentSender\n    }\n\n    /**\n     * 数据库未解锁，保存数据时打开数据库，并保存\n     */\n    internal fun <T : Activity> authAndSaveDb(\n      context: Context,\n      apkPackageName: String,\n      userName: String,\n      pass: String,\n      clazz: Class<T>\n    ): IntentSender {\n      val intent = Intent(context, clazz).also {\n        it.putExtra(\n          KEY_AUTO_FILL_PARAM, AutoFillParam(\n            apkPkgName = apkPackageName,\n            isSave = true,\n            saveUserName = userName,\n            savePass = pass\n          )\n        )\n        it.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK\n      }\n      return PendingIntent.getActivity(\n        context,\n        1,\n        intent,\n        PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_IMMUTABLE\n      )\n        .intentSender\n    }\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/launcher/LauncherModule.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.view.launcher\n\nimport android.annotation.SuppressLint\nimport android.content.Context\nimport android.net.Uri\nimport android.text.Html\nimport android.widget.Button\nimport androidx.arch.core.executor.ArchTaskExecutor\nimport androidx.biometric.BiometricPrompt\nimport androidx.biometric.BiometricPrompt.AuthenticationCallback\nimport androidx.biometric.BiometricPrompt.AuthenticationResult\nimport androidx.biometric.BiometricPrompt.CryptoObject\nimport androidx.biometric.BiometricPrompt.ERROR_NEGATIVE_BUTTON\nimport androidx.core.content.edit\nimport androidx.lifecycle.LiveData\nimport androidx.lifecycle.MutableLiveData\nimport androidx.lifecycle.liveData\nimport androidx.lifecycle.viewModelScope\nimport androidx.preference.PreferenceManager\nimport androidx.vectordrawable.graphics.drawable.VectorDrawableCompat\nimport com.arialyy.frame.router.Routerfit\nimport com.arialyy.frame.util.KeyStoreUtil\nimport com.arialyy.frame.util.ResUtil\nimport com.blankj.utilcode.util.AppUtils\nimport com.keepassdroid.utils.UriUtil\nimport com.lahm.library.EasyProtectorLib\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseApp\nimport com.lyy.keepassa.base.BaseModule\nimport com.lyy.keepassa.base.Constance\nimport com.lyy.keepassa.common.PassType\nimport com.lyy.keepassa.entity.AutoFillParam\nimport com.lyy.keepassa.entity.DbHistoryRecord\nimport com.lyy.keepassa.entity.QuickUnLockRecord\nimport com.lyy.keepassa.entity.SimpleItemEntity\nimport com.lyy.keepassa.router.DialogRouter\nimport com.lyy.keepassa.router.ServiceRouter\nimport com.lyy.keepassa.util.CommonKVStorage\nimport com.lyy.keepassa.util.FingerprintUtil\nimport com.lyy.keepassa.util.HitUtil\nimport com.lyy.keepassa.util.KpaUtil\nimport com.lyy.keepassa.util.QuickUnLockUtil\nimport com.lyy.keepassa.util.isAFS\nimport com.lyy.keepassa.view.dialog.OnMsgBtClickListener\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.MainScope\nimport kotlinx.coroutines.cancel\nimport kotlinx.coroutines.launch\nimport kotlinx.coroutines.withContext\nimport timber.log.Timber\n\ninternal class LauncherModule : BaseModule() {\n  private val itemData: MutableLiveData<List<SimpleItemEntity>> = MutableLiveData()\n  private val unlockEvent = MutableLiveData<Pair<Boolean, String?>>()\n  private val scope = MainScope()\n  var autoFillDelegate: IAutoFillFinishDelegate? = null\n  var autoFillParam: AutoFillParam? = null\n\n  companion object {\n    val HISTORY_ID = 0xA1\n  }\n\n  init {\n    val pre = BaseApp.APP.getSharedPreferences(Constance.PRE_FILE_NAME, Context.MODE_PRIVATE)\n    val startNum = pre.getInt(Constance.PRE_KEY_START_APP_NUM, 0)\n    pre.edit {\n      putInt(Constance.PRE_KEY_START_APP_NUM, startNum + 1)\n    }\n  }\n\n  fun isFormAutoFill() = autoFillParam != null\n\n  override fun onCleared() {\n    super.onCleared()\n    scope.cancel()\n  }\n\n  private val keyStoreUtil by lazy {\n    KeyStoreUtil()\n  }\n\n  private fun isNeedShowPrPrivacyAgreement(): Boolean {\n    return KpaUtil.isChina()\n      && !CommonKVStorage.getBoolean(Constance.IS_AGREE_PRIVACY_AGREEMENT, false)\n  }\n\n  fun showPrivacyAgreement(context: Context) {\n    if (!isNeedShowPrPrivacyAgreement()) {\n      return\n    }\n    Routerfit.create(DialogRouter::class.java).showMsgDialog(\n      msgTitle = \"隐私协议\",\n      msgContent = Html.fromHtml(\"<big>是否同意本协议</big> <br/> * 本应用没有服务端，不收集任何用户信息，只采用Bugly收集崩溃报告和性能报告 <br/> * 本应用网络同步和备份采用WebDav、FTP协议，DropBox、OneDrive网盘，由用户自己提供同步服务 <br/> * 本应用为开源软件，使用中产生任何问题由用户自己承担\"),\n      enterText = \"同意\",\n      cancelText = \"退出应用\",\n      btnClickListener = object : OnMsgBtClickListener {\n\n        override fun onEnter(v: Button) {\n          CommonKVStorage.put(Constance.IS_AGREE_PRIVACY_AGREEMENT, true)\n          Routerfit.create(ServiceRouter::class.java).getKpaSdkService().initThirdSdk(context)\n        }\n\n        override fun onCancel(v: Button) {\n          AppUtils.exitApp()\n        }\n      }\n    )\n  }\n\n  /**\n   * 处理数据库有密码的指纹解锁配置\n   */\n  @SuppressLint(\"RestrictedApi\")\n  fun showNormalBiometricPrompt(\n    fragment: OpenDbFragment,\n    record: QuickUnLockRecord?,\n    openDbRecord: DbHistoryRecord\n  ) {\n    if (isNeedShowPrPrivacyAgreement()) {\n      return\n    }\n    if (!fragment.isAdded) {\n      return\n    }\n    if (record == null) {\n      Timber.e(\"解锁记录为空\")\n      return\n    }\n    val resource = fragment.requireContext().resources\n    val promptInfo =\n      BiometricPrompt.PromptInfo.Builder()\n        .setTitle(resource.getString(R.string.fingerprint_unlock))\n        .setSubtitle(resource.getString(R.string.verify_finger))\n        .setNegativeButtonText(resource.getString(R.string.cancel))\n        //        .setConfirmationRequired(false)\n        .build()\n\n    val biometricPrompt = BiometricPrompt(fragment,\n      ArchTaskExecutor.getMainThreadExecutor(),\n      object : AuthenticationCallback() {\n        override fun onAuthenticationError(\n          errorCode: Int,\n          errString: CharSequence\n        ) {\n          if (!fragment.isAdded) {\n            Timber.e(\"Fragment没有被加载\")\n            return\n          }\n          val str = if (errorCode == ERROR_NEGATIVE_BUTTON) {\n            \"${resource.getString(R.string.verify_finger)}${resource.getString(R.string.cancel)}\"\n          } else {\n            resource.getString(R.string.verify_finger_fail)\n          }\n          HitUtil.snackShort(fragment.getRootView(), str)\n          unlockEvent.postValue(Pair(false, null))\n        }\n\n        override fun onAuthenticationSucceeded(result: AuthenticationResult) {\n          super.onAuthenticationSucceeded(result)\n          try {\n            val auth: CryptoObject? = result.cryptoObject\n            val cipher = auth!!.cipher!!\n            val pass = QuickUnLockUtil.decryption(\n              keyStoreUtil.decryptData(cipher, record.dbPass)\n            )\n            unlockEvent.postValue(Pair(true, pass))\n          } catch (e: Exception) {\n            Timber.e(e)\n            deleteBiomKey(fragment, openDbRecord)\n          }\n        }\n\n        override fun onAuthenticationFailed() {\n          super.onAuthenticationFailed()\n          if (fragment.isAdded) {\n            HitUtil.snackShort(\n              fragment.getRootView(),\n              resource.getString(R.string.verify_finger_fail)\n            )\n          }\n          unlockEvent.postValue(Pair(false, null))\n        }\n      })\n    try {\n      // Displays the \"log in\" prompt.\n      keyStoreUtil.let {\n        biometricPrompt.authenticate(\n          promptInfo, CryptoObject(it.getDecryptCipher(record.passIv!!))\n        )\n      }\n    } catch (e: Exception) {\n      Timber.e(e)\n      deleteBiomKey(fragment, openDbRecord)\n    }\n  }\n\n  /**\n   * 处理数据库有key的指纹解锁配置\n   */\n  @SuppressLint(\"RestrictedApi\")\n  fun showOnlyKeyBiometricPrompt(\n    fragment: OpenDbFragment,\n    record: QuickUnLockRecord?\n  ) {\n    if (!fragment.isAdded) {\n      return\n    }\n    if (record == null) {\n      Timber.e(\"解锁记录为空\")\n      return\n    }\n    val resource = fragment.requireContext().resources\n    val promptInfo =\n      BiometricPrompt.PromptInfo.Builder()\n        .setTitle(resource.getString(R.string.fingerprint_unlock))\n        .setSubtitle(resource.getString(R.string.verify_finger))\n        .setNegativeButtonText(resource.getString(R.string.cancel))\n        //        .setConfirmationRequired(false)\n        .build()\n\n    val biometricPrompt = BiometricPrompt(fragment,\n      ArchTaskExecutor.getMainThreadExecutor(),\n      object : AuthenticationCallback() {\n        override fun onAuthenticationError(\n          errorCode: Int,\n          errString: CharSequence\n        ) {\n          if (!fragment.isAdded) {\n            Timber.e(\"Fragment没有被加载\")\n            return\n          }\n          val str = if (errorCode == ERROR_NEGATIVE_BUTTON) {\n            \"${resource.getString(R.string.verify_finger)}${resource.getString(R.string.cancel)}\"\n          } else {\n            resource.getString(R.string.verify_finger_fail)\n          }\n          HitUtil.snackShort(fragment.getRootView(), str)\n          unlockEvent.postValue(Pair(false, null))\n        }\n\n        override fun onAuthenticationSucceeded(result: AuthenticationResult) {\n          super.onAuthenticationSucceeded(result)\n          unlockEvent.postValue(Pair(true, null))\n        }\n\n        override fun onAuthenticationFailed() {\n          super.onAuthenticationFailed()\n          if (fragment.isAdded) {\n            HitUtil.snackShort(\n              fragment.getRootView(),\n              resource.getString(R.string.verify_finger_fail)\n            )\n          }\n          unlockEvent.postValue(Pair(false, null))\n        }\n      })\n    biometricPrompt.authenticate(promptInfo)\n  }\n\n  fun checkPassType(\n    dbPass: String,\n    keyPath: String?\n  ) {\n    if (dbPass.isNotBlank() && !keyPath.isNullOrBlank()) {\n      BaseApp.passType = PassType.PASS_AND_KEY\n    } else if (dbPass.isNotBlank() && keyPath.isNullOrBlank()) {\n      BaseApp.passType = PassType.ONLY_PASS\n    } else if (dbPass.isBlank() && !keyPath.isNullOrBlank()) {\n      BaseApp.passType = PassType.ONLY_KEY\n    }\n  }\n\n  private fun deleteBiomKey(\n    fragment: OpenDbFragment,\n    openDbRecord: DbHistoryRecord\n  ) {\n    if (!fragment.isAdded) {\n      Timber.d(\"deleteBiomKey fragment isAdded = false\")\n      return\n    }\n    val resource = fragment.requireContext().resources\n    keyStoreUtil.deleteKeyStore()\n    HitUtil.snackLong(fragment.getRootView(), resource.getString(R.string.hint_fingerprint_modify))\n    fragment.hideFingerprint()\n    deleteFingerprint(openDbRecord.localDbUri)\n  }\n\n  /**\n   * 安全检查\n   */\n  fun securityCheck(context: Context) {\n    val needCheckEnv = PreferenceManager.getDefaultSharedPreferences(context)\n      .getBoolean(context.resources.getString(R.string.set_key_need_root_check), true)\n    if (!needCheckEnv) {\n      return\n    }\n\n    val resources = context.resources\n    if (EasyProtectorLib.checkIsRoot()) {\n      val vector = VectorDrawableCompat.create(resources, R.drawable.ic_eco, context.theme)\n      vector?.setTint(ResUtil.getColor(R.color.red))\n      Routerfit.create(DialogRouter::class.java)\n        .showMsgDialog(\n          msgTitle = ResUtil.getString(R.string.warning),\n          msgContent = resources.getString(R.string.warning_rooted),\n          showCoverBt = false,\n          msgTitleEndIcon = vector\n        )\n    }\n  }\n\n  /**\n   * 获取快速解锁记录\n   */\n  fun getQuickUnlockRecord(\n    openDbRecord: DbHistoryRecord,\n    fragment: OpenDbFragment\n  ): MutableLiveData<Pair<Boolean, String?>> {\n    scope.launch(Dispatchers.IO) {\n      val dao = BaseApp.appDatabase.quickUnlockDao()\n      val record: QuickUnLockRecord? = dao.findRecord(openDbRecord.localDbUri)\n\n      withContext(Dispatchers.Main) {\n        showBiometricPrompt(fragment, record, openDbRecord)\n      }\n    }\n    return unlockEvent\n  }\n\n  private fun showBiometricPrompt(\n    fragment: OpenDbFragment,\n    quickUnlockRecord: QuickUnLockRecord?,\n    openDbRecord: DbHistoryRecord\n  ) {\n    if (quickUnlockRecord != null) {\n      checkPassType(quickUnlockRecord.dbPass, quickUnlockRecord.keyPath)\n    }\n    if (BaseApp.passType == PassType.ONLY_KEY) {\n      showOnlyKeyBiometricPrompt(fragment, quickUnlockRecord)\n      return\n    }\n    showNormalBiometricPrompt(fragment, quickUnlockRecord, openDbRecord)\n  }\n\n  /**\n   * 删除指纹解锁记录\n   */\n  private fun deleteFingerprint(dbUri: String) {\n    if (!FingerprintUtil.hasBiometricPrompt(BaseApp.APP)) {\n      return\n    }\n    viewModelScope.launch(Dispatchers.IO) {\n      val dao = BaseApp.appDatabase.quickUnlockDao()\n      val record = dao.findRecord(dbUri)\n      PreferenceManager.getDefaultSharedPreferences(BaseApp.APP)\n        .edit {\n          putBoolean(BaseApp.APP.getString(R.string.set_quick_unlock), false)\n          commit()\n        }\n      if (record != null) {\n        dao.deleteRecord(record)\n      }\n    }\n  }\n\n  /**\n   * 检查是否需要启动指纹解锁\n   */\n  fun isNeedUseFingerprint(dbUri: String) = liveData {\n    if (!FingerprintUtil.hasBiometricPrompt(BaseApp.APP)) {\n      emit(false)\n      return@liveData\n    }\n\n    val needOpen = withContext(Dispatchers.IO) {\n      val unlockDao = BaseApp.appDatabase.quickUnlockDao()\n      val unLockRecord = unlockDao.findRecord(dbUri)\n      if (unLockRecord == null) {\n        Timber.d(\"unLockRecord is null\")\n        return@withContext false\n      }\n      Timber.d(\"is full unlock = ${unLockRecord.isUseFingerprint}\")\n      val dbDao = BaseApp.appDatabase.dbRecordDao()\n      val dbRecord = dbDao.findRecord(dbUri)\n      if (dbRecord == null) {\n        Timber.d(\"dbRecord is null\")\n        return@withContext false\n      }\n\n      return@withContext unLockRecord.isUseFingerprint\n    }\n    emit(needOpen)\n  }\n\n  /**\n   * 数据库打开方式\n   */\n  fun getDbOpenTypeData(context: Context): LiveData<List<SimpleItemEntity>>? {\n\n    val titles = context.resources.getStringArray(R.array.cloud_names)\n    val icons = context.resources.obtainTypedArray(R.array.cloud_icons)\n    val types = context.resources.getIntArray(R.array.cloud_type_ids)\n\n    val items = ArrayList<SimpleItemEntity>()\n    for ((index, title) in titles.withIndex()) {\n      val item = SimpleItemEntity()\n      item.title = title\n      item.id = types[index]\n      item.icon = icons.getResourceId(index, 0)\n      items.add(item)\n    }\n    icons.recycle()\n\n    // add history item\n    val historyItem = SimpleItemEntity().apply {\n      title = context.resources.getString(R.string.history_record)\n      id = HISTORY_ID\n      icon = R.drawable.ic_history\n    }\n    items.add(historyItem)\n\n    itemData.postValue(items)\n    return itemData\n  }\n\n  /**\n   * 获取上一次打开的数据信息\n   * 对于afs的文件如果uri失效，则删除该uri\n   */\n  fun getLastOpenDbHistory(context: Context) = liveData {\n    val data = withContext(Dispatchers.IO) {\n      val dao = BaseApp.appDatabase.dbRecordDao()\n      val records = dao.getAllRecord()\n      var newRecord: DbHistoryRecord? = null\n      val needRemoveRecords = ArrayList<DbHistoryRecord>()\n      for (record in records) {\n        val localUri = Uri.parse(record.localDbUri)\n        if (!UriUtil.checkPermissions(context, localUri) && record.isAFS()) {\n          needRemoveRecords.add(record)\n          continue\n        }\n        newRecord = record\n        break\n      }\n\n      for (record in needRemoveRecords) {\n        dao.deleteRecord(record)\n      }\n\n      newRecord\n    }\n    emit(data)\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/launcher/OpenAFSDelegate.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.view.launcher\n\nimport android.app.Activity\nimport android.content.Intent\nimport androidx.fragment.app.FragmentActivity\nimport com.keepassdroid.utils.UriUtil\nimport com.lyy.keepassa.event.ChangeDbEvent\nimport com.lyy.keepassa.util.KeepassAUtil\nimport com.lyy.keepassa.util.takePermission\nimport com.lyy.keepassa.view.StorageType.AFS\nimport org.greenrobot.eventbus.EventBus\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 2021/4/25\n **/\nclass OpenAFSDelegate : IOpenDbDelegate {\n  private val REQ_CODE_OPEN_DB_BY_AFS = 0xa1\n  private var activity: FragmentActivity? = null\n\n  override fun onResume() {\n\n  }\n\n  override fun startFlow(fragment: ChangeDbFragment) {\n    this.activity = fragment.requireActivity()\n    KeepassAUtil.instance.openSysFileManager(\n        fragment, \"*/*\", REQ_CODE_OPEN_DB_BY_AFS\n    )\n  }\n\n  override fun onActivityResult(\n    requestCode: Int,\n    resultCode: Int,\n    data: Intent?\n  ) {\n    if (resultCode == Activity.RESULT_OK) {\n      if (requestCode == REQ_CODE_OPEN_DB_BY_AFS && data != null && data.data != null) {\n        // 申请长期的uri权限\n        data.data!!.takePermission()\n        EventBus.getDefault()\n            .post(\n                ChangeDbEvent(\n                    dbName = UriUtil.getFileNameFromUri(activity, data.data),\n                    localFileUri = data.data!!,\n                    uriType = AFS\n                )\n            )\n      }\n    }\n  }\n\n  override fun destroy() {\n    activity = null\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/launcher/OpenDbFragment.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.view.launcher\n\nimport android.animation.Animator\nimport android.animation.Animator.AnimatorListener\nimport android.animation.AnimatorListenerAdapter\nimport android.animation.ObjectAnimator\nimport android.animation.ValueAnimator\nimport android.annotation.SuppressLint\nimport android.annotation.TargetApi\nimport android.app.Activity\nimport android.content.Intent\nimport android.content.res.AssetManager\nimport android.net.Uri\nimport android.os.Build\nimport android.text.Html\nimport android.text.TextUtils\nimport android.view.View\nimport android.view.animation.LinearInterpolator\nimport android.view.inputmethod.EditorInfo\nimport androidx.core.app.ActivityOptionsCompat\nimport androidx.lifecycle.Observer\nimport androidx.lifecycle.ViewModelProvider\nimport androidx.lifecycle.lifecycleScope\nimport androidx.transition.TransitionInflater\nimport com.alibaba.android.arouter.facade.annotation.Autowired\nimport com.alibaba.android.arouter.facade.annotation.Route\nimport com.alibaba.android.arouter.launcher.ARouter\nimport com.arialyy.frame.router.Routerfit\nimport com.arialyy.frame.util.ResUtil\nimport com.blankj.utilcode.util.ToastUtils\nimport com.keepassdroid.utils.UriUtil\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseApp\nimport com.lyy.keepassa.base.BaseFragment\nimport com.lyy.keepassa.databinding.FragmentOpenDbBinding\nimport com.lyy.keepassa.entity.DbHistoryRecord\nimport com.lyy.keepassa.router.ActivityRouter\nimport com.lyy.keepassa.util.HitUtil\nimport com.lyy.keepassa.util.KeepassAUtil\nimport com.lyy.keepassa.util.KpaUtil\nimport com.lyy.keepassa.util.VibratorUtil\nimport com.lyy.keepassa.util.takePermission\nimport com.lyy.keepassa.view.StorageType\nimport kotlinx.coroutines.flow.collectLatest\nimport kotlinx.coroutines.launch\nimport timber.log.Timber\nimport java.io.IOException\n\n/**\n * 打开数据库页面\n */\n@Route(path = \"/launcher/opendb\")\nclass OpenDbFragment : BaseFragment<FragmentOpenDbBinding>(), View.OnClickListener {\n  private lateinit var modlue: LauncherModule\n\n  companion object {\n    val FM_TAG = \"OpenDbFragment\"\n    val REQ_CODE_FILE = 0xa1\n    private val NEED_PROXY_DB = arrayListOf(StorageType.DROPBOX.name, StorageType.ONE_DRIVE.name)\n  }\n\n  @Autowired(name = \"openDbRecord\")\n  lateinit var openDbRecord: DbHistoryRecord\n\n  private var showChangeDbBt: Boolean = true\n\n  /**\n   * 快速解锁读取数据库回调\n   */\n  private val quickUnlockObserver by lazy {\n    Observer<Pair<Boolean, String?>> {\n      if (it.first) {\n        openDb(it.second)\n      }\n    }\n  }\n\n  override fun initData() {\n    ARouter.getInstance().inject(this)\n    binding.fingerprint.visibility = View.GONE\n    context?.let {\n      enterTransition = TransitionInflater.from(it).inflateTransition(R.transition.slide_enter)\n      exitTransition = TransitionInflater.from(it).inflateTransition(R.transition.slide_exit)\n      returnTransition = TransitionInflater.from(it).inflateTransition(R.transition.slide_return)\n    }\n\n    modlue = ViewModelProvider(requireActivity())[LauncherModule::class.java]\n    listenerOpenDb()\n    setDbName(openDbRecord)\n    handleKeyUri(openDbRecord.getDbKeyUri())\n\n    binding.cbKey.setOnCheckedChangeListener { _, isChecked ->\n      if (isChecked) {\n        if (TextUtils.isEmpty(openDbRecord.keyUri)\n          || openDbRecord.keyUri.equals(\"null\", ignoreCase = true)\n        ) {\n          KeepassAUtil.instance.openSysFileManager(this@OpenDbFragment, \"*/*\", REQ_CODE_FILE)\n        }\n        showKeyLayout()\n      } else {\n        openDbRecord.keyUri = \"\"\n        hintKeyLayout()\n      }\n    }\n\n    binding.open.setOnClickListener(this)\n    binding.changeDb.setOnClickListener(this)\n    binding.key.setOnClickListener(this)\n\n    if (!showChangeDbBt) {\n      binding.line.visibility = View.GONE\n      binding.changeDb.visibility = View.GONE\n    }\n\n    binding.password.setOnEditorActionListener { _, actionId, _ ->\n      // actionId 和android:imeOptions 属性要保持一致\n      if (actionId == EditorInfo.IME_ACTION_DONE && !TextUtils.isEmpty(binding.password.text)) {\n        KeepassAUtil.instance.toggleKeyBord(requireContext())\n        openDb(\n          binding.password.text.toString()\n            .trim()\n        )\n        true\n      } else {\n        false\n      }\n    }\n  }\n\n  private fun listenerOpenDb() {\n    lifecycleScope.launch {\n      KpaUtil.kdbOpenService.openDbFlow.collectLatest { db ->\n        if (db == null) {\n          HitUtil.toaskLong(getString(R.string.error_open_db))\n          return@collectLatest\n        }\n        Timber.d(\"打开数据库成功\")\n        modlue.autoFillParam?.let {\n          Timber.d(\"自动填充，不进入首页\")\n          modlue.autoFillDelegate?.handleAutoFill(it)\n          return@collectLatest\n        }\n        Routerfit.create(ActivityRouter::class.java, requireActivity()).toMainActivity(\n          opt = ActivityOptionsCompat.makeSceneTransitionAnimation(requireActivity())\n        )\n      }\n    }\n  }\n\n  override fun onResume() {\n    super.onResume()\n    startHeadAnim()\n    handleFingerprint()\n  }\n\n  /**\n   * 处理指纹\n   */\n  @TargetApi(Build.VERSION_CODES.M) private fun handleFingerprint() {\n    modlue.isNeedUseFingerprint(openDbRecord.localDbUri)\n      .observe(this, Observer { needUse ->\n        if (needUse) {\n          binding.fingerprint.visibility = View.VISIBLE\n          binding.fingerprint.playAnimation()\n          showBiometricPrompt()\n          binding.fingerprint.setOnClickListener {\n            showBiometricPrompt()\n          }\n          return@Observer\n        }\n        binding.fingerprint.visibility = View.GONE\n      })\n  }\n\n  fun hideFingerprint() {\n    binding.fingerprint.visibility = View.GONE\n  }\n\n  /**\n   * 显示验证指纹对话框\n   */\n  @SuppressLint(\"RestrictedApi\")\n  private fun showBiometricPrompt() {\n    modlue.getQuickUnlockRecord(openDbRecord, this)\n      .observe(this, quickUnlockObserver)\n  }\n\n  /**\n   * 启动欢迎标题的动画\n   */\n  private fun startHeadAnim() {\n    try {\n      binding.anim.setAnimation(\n        requireContext().assets.open(\"headAnim.json\", AssetManager.ACCESS_STREAMING),\n        \"LottieCacheWebDav\"\n      )\n      binding.anim.addAnimatorUpdateListener {\n        if (binding.anim.frame == 40) {\n          binding.anim.cancelAnimation()\n          binding.anim.frame = 40\n        }\n      }\n      binding.anim.addAnimatorListener(object : AnimatorListener {\n        override fun onAnimationRepeat(animation: Animator) {\n        }\n\n        override fun onAnimationEnd(animation: Animator) {\n        }\n\n        override fun onAnimationCancel(animation: Animator) {\n          binding.head.visibility = View.VISIBLE\n          ObjectAnimator.ofFloat(binding.head, \"alpha\", 0f, 1f)\n            .setDuration(1000)\n            .start()\n        }\n\n        override fun onAnimationStart(animation: Animator) {\n        }\n      })\n    } catch (e: IOException) {\n      Timber.e(e)\n    }\n  }\n\n  override fun onClick(v: View) {\n    if (KeepassAUtil.instance.isFastClick()) {\n      return\n    }\n    when (v.id) {\n      R.id.open -> {\n        VibratorUtil.vibrator(300)\n        openDb(binding.password.text.toString())\n        // val dialog = Routerfit.create(DialogRouter::class.java).toPlayDonateDialog()\n      }\n      R.id.change_db -> {\n        binding.cbKey.isChecked = false\n        (activity as LauncherActivity).changeDb()\n      }\n      R.id.key -> {\n        KeepassAUtil.instance.openSysFileManager(this@OpenDbFragment, \"*/*\", REQ_CODE_FILE)\n      }\n    }\n  }\n\n  /**\n   * 打开数据库\n   */\n  private fun openDb(pass: String?) {\n    val cache = pass ?: \"\"\n    if (cache.isBlank() && openDbRecord.keyUri.isBlank()) {\n      HitUtil.toaskShort(getString(R.string.error_input_pass_null))\n      return\n    }\n    Timber.d(\"文件类型：${openDbRecord.type}\")\n    if (KpaUtil.isChina() && openDbRecord.type in NEED_PROXY_DB) {\n      ToastUtils.showLong(ResUtil.getString(R.string.please_open_proxy))\n    }\n    modlue.checkPassType(cache, openDbRecord.keyUri)\n    KpaUtil.kdbOpenService.openDb(requireContext(), openDbRecord, cache)\n  }\n\n  /**\n   * 更新数据\n   */\n  fun updateData(dbRecord: DbHistoryRecord) {\n    openDbRecord.dbName = dbRecord.dbName\n    openDbRecord.cloudDiskPath = dbRecord.cloudDiskPath\n    openDbRecord.keyUri = dbRecord.keyUri\n    openDbRecord.localDbUri = dbRecord.localDbUri\n    openDbRecord.time = dbRecord.time\n    openDbRecord.type = dbRecord.type\n    openDbRecord.uid = dbRecord.uid\n    Timber.i(\"更新数据，record = $dbRecord\")\n    setDbName(dbRecord)\n    handleKeyUri(KeepassAUtil.instance.convertUri(dbRecord.keyUri))\n  }\n\n  private fun setDbName(dbRecord: DbHistoryRecord) {\n    binding.db.text = Html.fromHtml(getString(R.string.db1, dbRecord.dbName))\n    binding.db.setLeftIcon(\n      resources.getDrawable(dbRecord.getDbPathType().icon, requireContext().theme)\n    )\n  }\n\n  private fun handleKeyUri(keyUri: Uri?) {\n    val uriStr = keyUri.toString()\n    if (keyUri != null && !TextUtils.isEmpty(uriStr) && uriStr != \"null\") {\n      binding.key.visibility = View.VISIBLE\n      binding.cbKey.isChecked = true\n\n      binding.key.text = getString(R.string.key1, UriUtil.getFileNameFromUri(BaseApp.APP, keyUri))\n      return\n    }\n    binding.key.visibility = View.GONE\n  }\n\n  override fun setLayoutId(): Int {\n    return R.layout.fragment_open_db\n  }\n\n  private fun showKeyLayout() {\n    binding.key.layoutParams.height = 0\n    binding.key.visibility = View.VISIBLE\n    val h = resources.getDimension(R.dimen.input_pass_key_h)\n      .toInt()\n    val anim = ValueAnimator.ofInt(0, h)\n    anim.addUpdateListener { animation ->\n      binding.key.layoutParams.height = animation.animatedValue as Int\n      binding.key.requestLayout()\n    }\n    anim.duration = 400\n    anim.interpolator = LinearInterpolator()\n    anim.start()\n  }\n\n  private fun hintKeyLayout() {\n    binding.key.visibility = View.VISIBLE\n    val h = resources.getDimension(R.dimen.input_pass_key_h).toInt()\n    binding.key.layoutParams.height = h\n    val anim = ValueAnimator.ofInt(h, 0)\n    anim.addUpdateListener { animation ->\n      binding.key.layoutParams.height = animation.animatedValue as Int\n      binding.key.requestLayout()\n    }\n    anim.duration = 400\n    anim.interpolator = LinearInterpolator()\n    anim.start()\n\n    anim.addListener(object : AnimatorListenerAdapter() {\n      override fun onAnimationEnd(animation: Animator) {\n        binding.key.visibility = View.GONE\n      }\n    })\n  }\n\n  override fun onActivityResult(\n    requestCode: Int,\n    resultCode: Int,\n    data: Intent?\n  ) {\n    super.onActivityResult(requestCode, resultCode, data)\n    if (requestCode == REQ_CODE_FILE) {\n      if (resultCode == Activity.RESULT_OK) {\n        if (data == null || data.data == null) {\n          binding.cbKey.isChecked = false\n          return\n        }\n\n        data.data?.takePermission()\n        openDbRecord.keyUri = data.data.toString()\n        binding.key.text = getString(\n          R.string.key1, UriUtil.getFileNameFromUri(requireContext(), data.data)\n        )\n        return\n      }\n\n      if (binding.cbKey.isChecked) {\n        binding.cbKey.isChecked = false\n      }\n    }\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/launcher/OpenDbHistoryActivity.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.view.launcher\n\nimport android.net.Uri\nimport android.os.Bundle\nimport android.text.TextUtils\nimport android.view.MotionEvent\nimport android.view.View\nimport androidx.lifecycle.Observer\nimport androidx.lifecycle.ViewModelProvider\nimport androidx.recyclerview.widget.LinearLayoutManager\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseActivity\nimport com.lyy.keepassa.databinding.ActivityOnlyListBinding\nimport com.lyy.keepassa.entity.DbHistoryRecord\nimport com.lyy.keepassa.entity.SimpleItemEntity\nimport com.lyy.keepassa.event.ChangeDbEvent\nimport com.lyy.keepassa.event.DbHistoryEvent\nimport com.lyy.keepassa.util.doOnInterceptTouchEvent\nimport com.lyy.keepassa.util.doOnItemClickListener\nimport com.lyy.keepassa.util.doOnItemLongClickListener\nimport com.lyy.keepassa.view.SimpleAdapter\nimport com.lyy.keepassa.view.StorageType\nimport org.greenrobot.eventbus.EventBus\n\n/**\n * 数据库打开记录列表\n */\nclass OpenDbHistoryActivity : BaseActivity<ActivityOnlyListBinding>() {\n\n  private val data: ArrayList<SimpleItemEntity> = ArrayList()\n  private lateinit var adapter: SimpleAdapter\n  private lateinit var module: OpenDbHistoryModule\n  private var curx = 0\n\n  override fun setLayoutId(): Int {\n    return R.layout.activity_only_list\n  }\n\n  override fun initData(savedInstanceState: Bundle?) {\n    super.initData(savedInstanceState)\n    module = ViewModelProvider(this)[OpenDbHistoryModule::class.java]\n    toolbar.title = getString(R.string.history_record)\n    adapter = SimpleAdapter(this, data)\n    binding.list.layoutManager = LinearLayoutManager(this)\n    binding.list.setHasFixedSize(true)\n    binding.list.adapter = adapter\n\n    module.getDbOpenRecordList(this)\n      .observe(this, Observer { list ->\n        if (!list.isNullOrEmpty()) {\n          data.addAll(list)\n          adapter.notifyDataSetChanged()\n        }\n        if (data.size > 0) {\n          binding.temp.visibility = View.GONE\n        } else {\n          binding.temp.visibility = View.VISIBLE\n        }\n      })\n\n    binding.list.doOnItemClickListener { _, position, _ ->\n      val record = data[position].obj as DbHistoryRecord\n      finishAfterTransition()\n      EventBus.getDefault()\n        .post(\n          ChangeDbEvent(\n            dbName = record.dbName,\n            localFileUri = Uri.parse(record.localDbUri),\n            cloudPath = record.cloudDiskPath,\n            uriType = StorageType.valueOf(record.type),\n            keyUri = if (TextUtils.isEmpty(record.keyUri)) null else Uri.parse(\n              record.keyUri\n            )\n          )\n        )\n    }\n\n    binding.list.doOnItemLongClickListener { _, position, v ->\n      showDelPopMenu(position, v)\n      return@doOnItemLongClickListener true\n    }\n\n    // 获取点击位置\n    binding.list.doOnInterceptTouchEvent { _, e ->\n      if (e.action == MotionEvent.ACTION_DOWN) {\n        curx = e.x.toInt()\n      }\n      return@doOnInterceptTouchEvent false\n    }\n  }\n\n  private fun showDelPopMenu(\n    position: Int,\n    v: View\n  ) {\n    val popM = DelHistoryPopMenu(this, v, curx)\n    popM.getPopMenu()\n      .setOnMenuItemClickListener {\n        val item = data[position]\n        module.deleteHistoryRecord(item)\n        data.removeAt(position)\n        adapter.notifyItemRemoved(position)\n        if (data.isEmpty()) {\n          binding.temp.visibility = View.VISIBLE\n          EventBus.getDefault().post(DbHistoryEvent(true))\n        }\n        return@setOnMenuItemClickListener true\n      }\n    popM.show()\n  }\n\n  override fun onDestroy() {\n    super.onDestroy()\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/launcher/OpenDbHistoryModule.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.view.launcher\n\nimport android.content.Context\nimport android.net.Uri\nimport android.text.TextUtils\nimport androidx.lifecycle.liveData\nimport com.keepassdroid.utils.UriUtil\nimport com.lyy.keepassa.base.BaseApp\nimport com.lyy.keepassa.base.BaseModule\nimport com.lyy.keepassa.entity.DbHistoryRecord\nimport com.lyy.keepassa.entity.SimpleItemEntity\nimport com.lyy.keepassa.util.KeepassAUtil\nimport com.lyy.keepassa.util.isAFS\nimport com.lyy.keepassa.view.StorageType\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.GlobalScope\nimport kotlinx.coroutines.launch\nimport kotlinx.coroutines.withContext\nimport java.util.Date\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 2020/12/7\n **/\nclass OpenDbHistoryModule : BaseModule() {\n\n  /**\n   * delete history\n   */\n  fun deleteHistoryRecord(item: SimpleItemEntity) {\n    val dao = BaseApp.appDatabase.dbRecordDao()\n    GlobalScope.launch {\n      dao.deleteRecord(item.obj as DbHistoryRecord)\n    }\n  }\n\n  /**\n   * 获取数据库打开列表记录\n   */\n  fun getDbOpenRecordList(context: Context) = liveData {\n    val data = withContext(Dispatchers.IO) {\n      val dao = BaseApp.appDatabase.dbRecordDao()\n      val records = dao.getAllRecord()\n      val needRemoveRecord = ArrayList<DbHistoryRecord>()\n      if (records.isNotEmpty()) {\n        val list = ArrayList<SimpleItemEntity>()\n        for (record in records) {\n          val item = SimpleItemEntity()\n          item.icon = StorageType.valueOf(record.type).icon\n\n          // 检查权限，如果本地uri失效，者删除记录\n          val uri = Uri.parse(record.localDbUri)\n          if (!UriUtil.checkPermissions(context, uri) && record.isAFS()) {\n            needRemoveRecord.add(record)\n            continue\n          }\n          val tx = UriUtil.getFileNameFromUri(context, uri)\n          if (TextUtils.isEmpty(tx)) {\n            continue\n          }\n          item.title = tx\n          item.subTitle =  KeepassAUtil.instance.formatTime(Date(record.time))\n          item.obj = record\n          list.add(item)\n        }\n\n        for (record in needRemoveRecord) {\n          dao.deleteRecord(record)\n        }\n\n        list\n      } else {\n        null\n      }\n    }\n    emit(data)\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/launcher/OpenDropBoxDelegate.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.view.launcher\n\nimport android.content.Intent\nimport android.text.Html\nimport android.text.TextUtils\nimport android.widget.Button\nimport androidx.fragment.app.FragmentActivity\nimport com.arialyy.frame.router.Routerfit\nimport com.dropbox.core.android.Auth\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.router.DialogRouter\nimport com.lyy.keepassa.util.cloud.DropboxUtil\nimport com.lyy.keepassa.view.StorageType.DROPBOX\nimport com.lyy.keepassa.view.dialog.OnMsgBtClickListener\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 2021/4/25\n **/\nclass OpenDropBoxDelegate : IOpenDbDelegate {\n  private var startDropboxAuth = false\n  private var activity: FragmentActivity? = null\n\n  override fun onResume() {\n    if (startDropboxAuth) {\n      val token = Auth.getOAuth2Token()\n      if (!TextUtils.isEmpty(token)) {\n        DropboxUtil.saveToken(token)\n        // 如果授权成功，进入下一步\n        showCloudListDialog()\n      }\n    }\n  }\n\n  override fun startFlow(fragment: ChangeDbFragment) {\n    this.activity = fragment.requireActivity()\n    changeDropbox()\n  }\n\n  private fun delegateRequireActivity(): FragmentActivity = activity!!\n\n  /**\n   * 显示云端文件列表\n   */\n  private fun showCloudListDialog() {\n    Routerfit.create(DialogRouter::class.java).showCloudFileListDialog(DROPBOX)\n  }\n\n  /**\n   * 选择dropbox数据库\n   */\n  private fun changeDropbox() {\n    if (DropboxUtil.isAuthorized()) {\n      showCloudListDialog()\n    } else {\n      startDropboxAuth = true\n      Routerfit.create(DialogRouter::class.java).showMsgDialog(\n        msgContent = Html.fromHtml(delegateRequireActivity().getString(R.string.dropbox_msg)),\n        showCancelBt = true,\n        interceptBackKey = true,\n        btnClickListener = object : OnMsgBtClickListener {\n          override fun onCover(v: Button) {\n          }\n\n          override fun onEnter(v: Button) {\n            Auth.startOAuth2Authentication(delegateRequireActivity(), DropboxUtil.APP_KEY)\n          }\n\n          override fun onCancel(v: Button) {\n            startDropboxAuth = false\n          }\n\n        }\n      )\n    }\n  }\n\n  override fun onActivityResult(\n    requestCode: Int,\n    resultCode: Int,\n    data: Intent?\n  ) {\n  }\n\n  override fun destroy() {\n    activity = null\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/launcher/OpenOneDriveDelegate.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.view.launcher\n\nimport android.content.Intent\nimport android.text.Html\nimport android.widget.Button\nimport androidx.fragment.app.FragmentActivity\nimport com.arialyy.frame.router.Routerfit\nimport com.arialyy.frame.util.ResUtil\nimport com.blankj.utilcode.util.ToastUtils\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.router.DialogRouter\nimport com.lyy.keepassa.util.HitUtil\nimport com.lyy.keepassa.util.KpaUtil\nimport com.lyy.keepassa.util.cloud.OneDriveUtil\nimport com.lyy.keepassa.view.StorageType.ONE_DRIVE\nimport com.lyy.keepassa.view.dialog.OnMsgBtClickListener\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 2021/4/25\n **/\nclass OpenOneDriveDelegate : IOpenDbDelegate {\n  private var activity: FragmentActivity? = null\n\n  override fun onResume() {\n  }\n\n  override fun startFlow(fragment: ChangeDbFragment) {\n    this.activity = fragment.requireActivity()\n    showHitDialog {\n      if (KpaUtil.isChina()) {\n        ToastUtils.showLong(ResUtil.getString(R.string.please_open_proxy))\n      }\n      OneDriveUtil.initOneDrive { success ->\n        if (success) {\n          OneDriveUtil.loadAccount()\n          return@initOneDrive\n        }\n        HitUtil.snackLong(\n          activity!!.window.decorView,\n          activity!!.getString(R.string.one_drive_init_failure)\n        )\n      }\n      OneDriveUtil.loginCallback = object : OneDriveUtil.OnLoginCallback {\n        override fun callback(success: Boolean) {\n          if (success) {\n            showCloudListDialog()\n          }\n        }\n      }\n    }\n  }\n\n  private fun showHitDialog(onClick: () -> Unit) {\n    Routerfit.create(DialogRouter::class.java).showMsgDialog(\n      msgContent = Html.fromHtml(activity!!.resources.getString(R.string.one_drive_hint)),\n      btnClickListener = object : OnMsgBtClickListener {\n        override fun onCover(v: Button) {\n        }\n\n        override fun onEnter(v: Button) {\n          onClick.invoke()\n        }\n\n        override fun onCancel(v: Button) {\n        }\n      }\n    )\n  }\n\n  /**\n   * 显示云端文件列表\n   */\n  private fun showCloudListDialog() {\n    Routerfit.create(DialogRouter::class.java).showCloudFileListDialog(ONE_DRIVE)\n  }\n\n  override fun onActivityResult(\n    requestCode: Int,\n    resultCode: Int,\n    data: Intent?\n  ) {\n  }\n\n  override fun destroy() {\n    OneDriveUtil.loginCallback = null\n    activity = null\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/launcher/OpenWebDavDelegate.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.view.launcher\n\nimport android.content.Intent\nimport com.arialyy.frame.router.Routerfit\nimport com.lyy.keepassa.event.WebDavLoginEvent\nimport com.lyy.keepassa.router.DialogRouter\nimport com.lyy.keepassa.view.StorageType.WEBDAV\nimport kotlinx.coroutines.MainScope\nimport kotlinx.coroutines.cancel\nimport kotlinx.coroutines.flow.collectLatest\nimport kotlinx.coroutines.launch\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 2021/4/25\n **/\nclass OpenWebDavDelegate : IOpenDbDelegate {\n  private val scope = MainScope()\n  private var loginEvent: WebDavLoginEvent? = null\n\n  override fun onResume() {\n  }\n\n  override fun startFlow(fragment: ChangeDbFragment) {\n    val dialog = Routerfit.create(DialogRouter::class.java).getWebDavLoginDialog()\n    dialog.show(fragment.childFragmentManager, \"web_dav_login\")\n    scope.launch {\n      dialog.webDavLoginFlow.collectLatest {\n        if (it.loginSuccess) {\n          loginEvent = it\n          Routerfit.create(DialogRouter::class.java).showCloudFileListDialog(WEBDAV)\n        }\n      }\n    }\n  }\n\n  override fun onActivityResult(\n    requestCode: Int,\n    resultCode: Int,\n    data: Intent?\n  ) {\n  }\n\n  override fun destroy() {\n    scope.cancel()\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/launcher/SaveEntityDelegate.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.view.launcher\n\nimport android.app.Activity\nimport android.content.Context\nimport android.content.Intent\nimport androidx.activity.result.contract.ActivityResultContract\nimport androidx.core.app.ActivityOptionsCompat\nimport com.lyy.keepassa.entity.AutoFillParam\nimport com.lyy.keepassa.view.create.entry.CreateEntryActivity\nimport timber.log.Timber\n\ninternal class SaveEntityDelegate(val activity: LauncherActivity) :\n  IAutoFillFinishDelegate {\n\n  private var autoFillParam: AutoFillParam? = null\n\n  val content = object : ActivityResultContract<AutoFillParam, Intent?>() {\n    override fun createIntent(context: Context, input: AutoFillParam): Intent {\n      val intent = Intent(context, CreateEntryActivity::class.java).apply {\n        putExtra(LauncherActivity.KEY_AUTO_FILL_PARAM, input)\n      }\n      return intent\n    }\n\n    override fun parseResult(resultCode: Int, intent: Intent?): Intent? {\n      if (resultCode == Activity.RESULT_OK) {\n        return intent\n      }\n      return null\n    }\n  }\n\n  private val reg = activity.registerForActivityResult(content) {\n    onActivityResult(it)\n  }\n\n  override fun handleAutoFill(autoFillParam: AutoFillParam) {\n    this.autoFillParam = autoFillParam\n    reg.launch(autoFillParam, ActivityOptionsCompat.makeSceneTransitionAnimation(activity))\n    activity.superFinish()\n  }\n\n  override fun onActivityResult(data: Intent?) {\n    if (autoFillParam == null) {\n      Timber.e(\"autoFillParam is null\")\n      return\n    }\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/launcher/SearchEntityDelegate.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.view.launcher\n\nimport KDBAutoFillRepository\nimport android.app.Activity\nimport android.content.Context\nimport android.content.Intent\nimport androidx.activity.result.contract.ActivityResultContract\nimport androidx.core.app.ActivityOptionsCompat\nimport com.lyy.keepassa.base.BaseApp\nimport com.lyy.keepassa.entity.AutoFillParam\nimport com.lyy.keepassa.util.KeepassAUtil\nimport com.lyy.keepassa.view.search.AutoFillEntrySearchActivity\nimport timber.log.Timber\n\ninternal class SearchEntityDelegate(val activity: LauncherActivity) : IAutoFillFinishDelegate {\n  private var autoFillParam: AutoFillParam? = null\n\n  val content = object : ActivityResultContract<AutoFillParam, Intent?>() {\n    override fun createIntent(context: Context, input: AutoFillParam): Intent {\n      val intent = Intent(context, AutoFillEntrySearchActivity::class.java).apply {\n        putExtra(LauncherActivity.KEY_AUTO_FILL_PARAM, input)\n      }\n      return intent\n    }\n\n    override fun parseResult(resultCode: Int, intent: Intent?): Intent? {\n      if (resultCode == Activity.RESULT_OK) {\n        return intent\n      }\n      return null\n    }\n  }\n\n  private val reg = activity.registerForActivityResult(content) {\n    onActivityResult(it)\n  }\n\n  override fun handleAutoFill(autoFillParam: AutoFillParam) {\n    this.autoFillParam = autoFillParam\n    // 打开搜索界面\n    val datas = KDBAutoFillRepository.getAutoFillDataByPackageName(autoFillParam.apkPkgName)\n    // 如果查找不到数据，跳转到搜索页面\n    if (datas == null || datas.isEmpty()) {\n      reg.launch(autoFillParam, ActivityOptionsCompat.makeSceneTransitionAnimation(activity))\n      return\n    }\n\n    // 将数据回调给service\n    val data =\n      KeepassAUtil.instance.getFillResponse(activity, activity.intent, autoFillParam.apkPkgName)\n    activity.setResult(Activity.RESULT_OK, data)\n    (activity as LauncherActivity).superFinish()\n  }\n\n  override fun onActivityResult(data: Intent?) {\n    if (autoFillParam == null) {\n      Timber.e(\"autoFillParam is null\")\n      return\n    }\n\n    // 搜索页返回的数据\n    if (data != null) {\n      val id = data.getSerializableExtra(AutoFillEntrySearchActivity.EXTRA_ENTRY_ID)\n      activity.setResult(\n        Activity.RESULT_OK,\n        BaseApp.KDB.pm.entries[id]?.let {\n          KeepassAUtil.instance.getFillResponse(\n            activity,\n            activity.intent,\n            it,\n            autoFillParam!!.apkPkgName\n          )\n        }\n      )\n    } else {\n      activity.setResult(\n        Activity.RESULT_OK,\n        KeepassAUtil.instance.getFillResponse(activity, activity.intent, autoFillParam!!.apkPkgName)\n      )\n    }\n    activity.superFinish()\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/main/EntryListFragment.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.view.main\n\nimport android.annotation.SuppressLint\nimport android.content.Context\nimport android.graphics.Color\nimport android.view.MotionEvent\nimport android.view.View\nimport androidx.appcompat.widget.AppCompatImageView\nimport androidx.lifecycle.ViewModelProvider\nimport androidx.lifecycle.lifecycleScope\nimport androidx.recyclerview.widget.LinearLayoutManager\nimport com.alibaba.android.arouter.facade.annotation.Autowired\nimport com.alibaba.android.arouter.facade.annotation.Route\nimport com.alibaba.android.arouter.launcher.ARouter\nimport com.arialyy.frame.util.ResUtil\nimport com.arialyy.frame.util.adapter.RvItemClickSupport\nimport com.keepassdroid.database.PwEntry\nimport com.keepassdroid.utils.Types\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseApp\nimport com.lyy.keepassa.base.BaseFragment\nimport com.lyy.keepassa.databinding.FragmentEntryRecordBinding\nimport com.lyy.keepassa.entity.EntryRecord\nimport com.lyy.keepassa.entity.showPopMenu\nimport com.lyy.keepassa.event.EntryState.DELETE\nimport com.lyy.keepassa.event.EntryState.MODIFY\nimport com.lyy.keepassa.util.EventBusHelper\nimport com.lyy.keepassa.util.KeepassAUtil\nimport com.lyy.keepassa.util.KpaUtil\nimport com.lyy.keepassa.util.doOnInterceptTouchEvent\nimport com.lyy.keepassa.util.updateModifyEntry\nimport com.lyy.keepassa.view.SimpleEntryAdapter\nimport kotlinx.coroutines.flow.collectLatest\nimport kotlinx.coroutines.launch\nimport org.greenrobot.eventbus.Subscribe\nimport org.greenrobot.eventbus.ThreadMode.MAIN\nimport timber.log.Timber\n\n@Route(path = \"/main/fragment/entry\")\nclass EntryListFragment : BaseFragment<FragmentEntryRecordBinding>() {\n\n  companion object {\n    const val TYPE_HISTORY = \"TYPE_HISTORY\"\n    const val TYPE_TOTP = \"TYPE_TOTP\"\n  }\n\n  private lateinit var module: EntryListModule\n  private lateinit var adapter: SimpleEntryAdapter\n  private var curx = 0\n\n  @Autowired(name = \"type\")\n  @JvmField\n  var type = TYPE_HISTORY\n\n  override fun onAttach(context: Context) {\n    super.onAttach(context)\n    module = ViewModelProvider(this)[EntryListModule::class.java]\n  }\n\n  override fun initData() {\n    ARouter.getInstance().inject(this)\n    adapter = SimpleEntryAdapter(requireContext(), module.entryData)\n    binding.list.setHasFixedSize(true)\n    binding.list.layoutManager = LinearLayoutManager(context)\n    binding.list.adapter = adapter\n\n\n    RvItemClickSupport.addTo(binding.list)\n      .setOnItemClickListener { _, position, v ->\n        if (position < 0 || position >= module.entryData.size) {\n          return@setOnItemClickListener\n        }\n        val item = module.entryData[position]\n        val icon = v.findViewById<AppCompatImageView>(R.id.icon)\n        KeepassAUtil.instance.turnEntryDetail(requireActivity(), item.obj as PwEntry, icon)\n      }\n\n    // 长按处理\n    RvItemClickSupport.addTo(binding.list)\n      .setOnItemLongClickListener { _, position, v ->\n        module.entryData[position].showPopMenu(requireActivity(), v, curx)\n        true\n      }\n\n    // 获取点击位置\n    binding.list.doOnInterceptTouchEvent { _, e ->\n      if (e.action == MotionEvent.ACTION_DOWN) {\n        curx = e.x.toInt()\n      }\n      return@doOnInterceptTouchEvent false\n    }\n    if (type == TYPE_TOTP) {\n      binding.temp.setText(ResUtil.getString(R.string.no_totp_token))\n    }\n    initRefresh()\n    listenerGetData()\n    listenerEntryStateChange()\n    listenerUpdateRecord()\n    setData()\n  }\n\n  @SuppressLint(\"NotifyDataSetChanged\")\n  private fun listenerGetData() {\n    lifecycleScope.launch {\n      module.getDataFlow.collectLatest { list ->\n        if (list.isNullOrEmpty()) {\n          binding.temp.visibility = View.VISIBLE\n          binding.swipe.isRefreshing = false\n          return@collectLatest\n        }\n        module.entryData.sortByDescending { it.time }\n        binding.temp.visibility = View.GONE\n        binding.swipe.isRefreshing = false\n        adapter.notifyDataSetChanged()\n      }\n    }\n  }\n\n  /**\n   * listener the entry status change, there are states: create, delete, modify, move\n   */\n  private fun listenerEntryStateChange() {\n    lifecycleScope.launch {\n      KpaUtil.kdbHandlerService.entryStateChangeFlow.collectLatest {\n        if (it.pwEntryV4 == null) {\n          return@collectLatest\n        }\n        if (it.state == MODIFY) {\n          adapter.updateModifyEntry(module.entryData, it.pwEntryV4, it.oldParent!!)\n          return@collectLatest\n        }\n        if (it.state == DELETE) {\n          module.deleteEntry(adapter, it.pwEntryV4, it.oldParent!!, type)\n          return@collectLatest\n        }\n      }\n    }\n  }\n\n  private fun initRefresh() {\n    binding.swipe.setColorSchemeColors(\n      Color.parseColor(\"#4E85DB\"),\n      Color.parseColor(\"#B48CFF\"),\n      Color.parseColor(\"#95DAED\")\n    )\n    binding.swipe.setOnRefreshListener {\n      setData()\n    }\n  }\n\n  private fun setData() {\n    module.getData(type)\n  }\n\n  private fun listenerUpdateRecord(){\n    lifecycleScope.launch {\n      KpaUtil.openEntryRecordFlow.collectLatest {\n        onAddOrUpdateRecord(it)\n      }\n    }\n  }\n\n  private fun onAddOrUpdateRecord(record: EntryRecord) {\n    if (type == TYPE_TOTP) {\n      Timber.d(\"Currently is totp module, ignore this event\")\n      return\n    }\n    if (binding.temp.visibility == View.VISIBLE) {\n      binding.temp.visibility = View.GONE\n    }\n    val newRecordUUid = Types.bytestoUUID(record.uuid)\n    // 查找是该记录是否存在\n    val oldRecord = module.entryData.find { (it.obj as PwEntry).uuid == newRecordUUid }\n\n    val entry = BaseApp.KDB.pm.entries[newRecordUUid]\n    entry?.let { it ->\n      if (oldRecord == null) {\n        val itemData = KeepassAUtil.instance.convertPwEntry2Item(it)\n        itemData.time = record.time\n        module.entryData.add(itemData)\n      } else {\n        val itemData = KeepassAUtil.instance.convertPwEntry2Item(it)\n        oldRecord.title = record.title\n        oldRecord.subTitle = itemData.subTitle\n        oldRecord.time = record.time\n      }\n\n      module.entryData.sortByDescending { entry ->\n        entry.time\n      }\n      adapter.notifyDataSetChanged()\n    }\n  }\n\n\n  override fun setLayoutId(): Int {\n    return R.layout.fragment_entry_record\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/main/EntryListModule.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.view.main\n\nimport androidx.lifecycle.viewModelScope\nimport com.keepassdroid.database.PwEntry\nimport com.keepassdroid.database.PwEntryV4\nimport com.keepassdroid.database.PwGroupV4\nimport com.keepassdroid.utils.Types\nimport com.lyy.keepassa.base.BaseApp\nimport com.lyy.keepassa.base.BaseModule\nimport com.lyy.keepassa.entity.SimpleItemEntity\nimport com.lyy.keepassa.util.KeepassAUtil\nimport com.lyy.keepassa.util.deleteEntry\nimport com.lyy.keepassa.util.hasTOTP\nimport com.lyy.keepassa.view.SimpleEntryAdapter\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.flow.MutableSharedFlow\nimport kotlinx.coroutines.launch\nimport kotlinx.coroutines.withContext\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 4:02 下午 2021/10/25\n **/\ninternal class EntryListModule : BaseModule() {\n\n  val entryData = mutableListOf<SimpleItemEntity>()\n  val getDataFlow = MutableSharedFlow<MutableList<SimpleItemEntity>?>()\n\n  fun getData(type: String) {\n    if (type == EntryListFragment.TYPE_HISTORY) {\n      getEntryHistoryRecord()\n      return\n    }\n    if (type == EntryListFragment.TYPE_TOTP) {\n      getTOTPData()\n      return\n    }\n  }\n\n  fun deleteEntry(adapter: SimpleEntryAdapter, self: PwEntryV4, oldGroup: PwGroupV4, type: String) {\n    if (type == EntryListFragment.TYPE_HISTORY) {\n      val item = entryData.find { it.obj == self }\n      if (item != null) {\n        delHistoryRecord(self)\n      }\n    }\n    adapter.deleteEntry(entryData, self, oldGroup, null)\n  }\n\n  /**\n   * get totp data\n   */\n  private fun getTOTPData() {\n    entryData.clear()\n    viewModelScope.launch {\n      if (BaseApp.KDB == null) {\n        getDataFlow.emit(null)\n        return@launch\n      }\n      val pm = BaseApp.KDB!!.pm\n\n      if (pm == null) {\n        getDataFlow.emit(null)\n        return@launch\n      }\n      for (map in pm.entries) {\n        val entry = map.value\n        if (entry is PwEntryV4 && entry.hasTOTP()) {\n          entryData.add(KeepassAUtil.instance.convertPwEntry2Item(entry))\n        }\n      }\n      getDataFlow.emit(entryData)\n    }\n  }\n\n  /**\n   * 删除历史记录\n   */\n  fun delHistoryRecord(entry: PwEntry) {\n    viewModelScope.launch(Dispatchers.IO) {\n      BaseApp.dbRecord?.let {\n        val dao = BaseApp.appDatabase.entryRecordDao()\n        val record = dao.getRecord(Types.UUIDtoBytes(entry.uuid), it.localDbUri)\n        if (record != null) {\n          dao.delReocrd(record)\n        }\n      }\n    }\n  }\n\n  /**\n   * 获取历史记录\n   */\n  private fun getEntryHistoryRecord() {\n    entryData.clear()\n    viewModelScope.launch {\n      if (BaseApp.dbRecord == null) {\n        getDataFlow.emit(null)\n        return@launch\n      }\n      withContext(Dispatchers.IO) {\n        val dao = BaseApp.appDatabase.entryRecordDao()\n        val records = dao.getRecord(BaseApp.dbRecord!!.localDbUri)\n        if (records.isEmpty()) {\n          return@withContext\n        }\n\n        for (record in records) {\n          val entry = BaseApp.KDB!!.pm.entries[Types.bytestoUUID(record.uuid)] ?: continue\n          val item = KeepassAUtil.instance.convertPwEntry2Item(entry)\n          item.time = record.time\n          entryData.add(item)\n        }\n      }\n      getDataFlow.emit(entryData)\n    }\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/main/HomeFragment.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.view.main\n\nimport android.content.Context\nimport android.graphics.Color\nimport android.graphics.Rect\nimport android.graphics.drawable.ColorDrawable\nimport android.view.MotionEvent\nimport androidx.appcompat.widget.AppCompatImageView\nimport androidx.core.app.ActivityOptionsCompat\nimport androidx.lifecycle.ViewModelProvider\nimport androidx.lifecycle.lifecycleScope\nimport androidx.recyclerview.widget.DividerItemDecoration\nimport androidx.recyclerview.widget.LinearLayoutManager\nimport com.alibaba.android.arouter.facade.annotation.Route\nimport com.arialyy.frame.router.Routerfit\nimport com.arialyy.frame.util.DpUtils\nimport com.arialyy.frame.util.ResUtil\nimport com.keepassdroid.database.PwEntry\nimport com.keepassdroid.database.PwGroup\nimport com.keepassdroid.database.PwGroupV4\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseApp\nimport com.lyy.keepassa.base.BaseFragment\nimport com.lyy.keepassa.databinding.FragmentOnlyListBinding\nimport com.lyy.keepassa.entity.EntryType\nimport com.lyy.keepassa.entity.showPopMenu\nimport com.lyy.keepassa.event.EntryState.CREATE\nimport com.lyy.keepassa.event.EntryState.DELETE\nimport com.lyy.keepassa.event.EntryState.MODIFY\nimport com.lyy.keepassa.event.EntryState.MOVE\nimport com.lyy.keepassa.event.EntryState.SAVE\nimport com.lyy.keepassa.event.EntryState.UNKNOWN\nimport com.lyy.keepassa.event.MoveEvent\nimport com.lyy.keepassa.router.ActivityRouter\nimport com.lyy.keepassa.util.EventBusHelper\nimport com.lyy.keepassa.util.HitUtil\nimport com.lyy.keepassa.util.KeepassAUtil\nimport com.lyy.keepassa.util.KpaUtil\nimport com.lyy.keepassa.util.cloud.DbSynUtil\nimport com.lyy.keepassa.util.createGroup\nimport com.lyy.keepassa.util.deleteGroup\nimport com.lyy.keepassa.util.doOnInterceptTouchEvent\nimport com.lyy.keepassa.util.doOnItemClickListener\nimport com.lyy.keepassa.util.doOnItemLongClickListener\nimport com.lyy.keepassa.util.isAFS\nimport com.lyy.keepassa.util.updateModifyGroup\nimport com.lyy.keepassa.view.SimpleEntryAdapter\nimport kotlinx.coroutines.flow.collectLatest\nimport kotlinx.coroutines.launch\nimport org.greenrobot.eventbus.Subscribe\nimport org.greenrobot.eventbus.ThreadMode.MAIN\nimport timber.log.Timber\n\n@Route(path = \"/main/fragment/home\")\nclass HomeFragment : BaseFragment<FragmentOnlyListBinding>() {\n\n  private lateinit var module: HomeModule\n  private lateinit var adapter: SimpleEntryAdapter\n  private var curx = 0\n  private var isSyncDb = false\n\n  override fun onAttach(context: Context) {\n    super.onAttach(context)\n    module = ViewModelProvider(this)[HomeModule::class.java]\n  }\n\n  override fun initData() {\n    EventBusHelper.reg(this)\n    adapter = SimpleEntryAdapter(requireContext(), module.itemDataList)\n    binding.list.layoutManager = LinearLayoutManager(context)\n    binding.list.adapter = adapter\n    binding.list.doOnItemClickListener { _, position, v ->\n      val item = module.itemDataList[position]\n      if (item.obj == EntryType.TYPE_COLLECTION) {\n        Routerfit.create(ActivityRouter::class.java, requireActivity()).toMyCollection(\n          ActivityOptionsCompat.makeSceneTransitionAnimation(requireActivity())\n        )\n        return@doOnItemClickListener\n      }\n      if (item.obj is PwGroup) {\n        KeepassAUtil.instance.turnEntryDetail(requireActivity(), item.obj as PwGroup)\n      } else if (item.obj is PwEntry) {\n        val icon = v.findViewById<AppCompatImageView>(R.id.icon)\n        KeepassAUtil.instance.turnEntryDetail(requireActivity(), item.obj as PwEntry, icon)\n      }\n    }\n\n    val div = DividerItemDecoration(context, DividerItemDecoration.VERTICAL)\n    val drawable = ColorDrawable(Color.TRANSPARENT)\n    drawable.bounds = Rect(0, 0, 200, DpUtils.dp2px(20))\n    div.setDrawable(drawable)\n    binding.list.addItemDecoration(div)\n\n    // 长按处理\n    binding.list.doOnItemLongClickListener { _, position, v ->\n      module.itemDataList[position].showPopMenu(requireActivity(), v, curx)\n      true\n    }\n\n    // 获取点击位置\n    binding.list.doOnInterceptTouchEvent { _, e ->\n      if (e.action == MotionEvent.ACTION_DOWN) {\n        curx = e.x.toInt()\n      }\n      return@doOnInterceptTouchEvent false\n    }\n    listenerDataGet()\n    initRefresh()\n    module.getRootEntry()\n    listenerCollection()\n    listenerEntryStateChange()\n    listenerGroupStateChange()\n  }\n\n  /**\n   * listener the group status change, there are states: create, delete, modify, move\n   */\n  private fun listenerGroupStateChange() {\n    lifecycleScope.launch {\n      KpaUtil.kdbHandlerService.groupStateChangeFlow.collectLatest {\n        if (it.groupV4 == null) {\n          return@collectLatest\n        }\n        when (it.state) {\n          CREATE -> {\n            adapter.createGroup(\n              module.itemDataList,\n              it.groupV4,\n              BaseApp.KDB.pm.rootGroup as PwGroupV4\n            )\n          }\n          MODIFY -> {\n            adapter.updateModifyGroup(\n              module.itemDataList,\n              it.groupV4,\n              BaseApp.KDB.pm.rootGroup as PwGroupV4\n            )\n          }\n          MOVE -> {\n            // module.moveEntry(adapter, it.pwEntryV4, it.oldParent!!)\n          }\n          DELETE -> {\n            adapter.deleteGroup(\n              module.itemDataList,\n              it.groupV4,\n              it.oldParent!!,\n              BaseApp.KDB.pm.rootGroup as PwGroupV4\n            )\n          }\n          SAVE -> {\n            adapter.notifyDataSetChanged()\n          }\n          UNKNOWN -> {\n            Timber.d(\"un known status\")\n          }\n        }\n      }\n    }\n  }\n\n  /**\n   * listener the entry status change, there are states: create, delete, modify, move\n   */\n  private fun listenerEntryStateChange() {\n    lifecycleScope.launch {\n      KpaUtil.kdbHandlerService.entryStateChangeFlow.collectLatest {\n        if (it.pwEntryV4 == null) {\n          return@collectLatest\n        }\n        Timber.d(\"entry status = ${it.state}\")\n        when (it.state) {\n          CREATE -> {\n            module.createNewEntry(adapter, it.pwEntryV4)\n          }\n          MODIFY -> {\n            module.updateModifyEntry(adapter, it.pwEntryV4)\n          }\n          MOVE -> {\n            module.moveEntry(adapter, it.pwEntryV4, it.oldParent!!)\n          }\n          DELETE -> {\n            module.deleteEntry(adapter, it.pwEntryV4, it.oldParent!!)\n          }\n          SAVE -> {\n            adapter.notifyDataSetChanged()\n          }\n          UNKNOWN -> {\n            Timber.d(\"un known status\")\n          }\n        }\n      }\n    }\n  }\n\n  /**\n   * listener update root data\n   */\n  private fun listenerDataGet() {\n    lifecycleScope.launch {\n      module.itemDataFlow.collectLatest {\n        adapter.notifyDataSetChanged()\n        if (isSyncDb) {\n          finishRefresh(true)\n        }\n      }\n    }\n  }\n\n  /**\n   * listener collection state\n   */\n  private fun listenerCollection() {\n    lifecycleScope.launch {\n      KpaUtil.kdbHandlerService.collectionStateFlow.collectLatest {\n        if (module.itemDataList.isEmpty()) {\n          Timber.d(\"current no any data, please wait get root data.\")\n          return@collectLatest\n        }\n\n        if (module.itemDataList[0] == module.collectionEntry) {\n          module.itemDataList[0].subTitle =\n            ResUtil.getString(R.string.current_collection_num, it.collectionNum.toString())\n          adapter.notifyItemChanged(0)\n          return@collectLatest\n        }\n        module.collectionEntry.subTitle =\n          ResUtil.getString(R.string.current_collection_num, it.collectionNum.toString())\n        module.itemDataList.add(0, module.collectionEntry)\n        adapter.notifyItemInserted(0)\n      }\n    }\n  }\n\n  private fun initRefresh() {\n    binding.swipe.setColorSchemeColors(\n      Color.parseColor(\"#4E85DB\"),\n      Color.parseColor(\"#B48CFF\"),\n      Color.parseColor(\"#95DAED\")\n    )\n    binding.swipe.setOnRefreshListener {\n      if (BaseApp.dbRecord == null) {\n        finishRefresh(false)\n        return@setOnRefreshListener\n      }\n\n      if (BaseApp.dbRecord!!.isAFS()) {\n        isSyncDb = true\n        module.getRootEntry()\n        finishRefresh(true)\n        return@setOnRefreshListener\n      }\n\n      module.syncDb {\n        if (it != DbSynUtil.STATE_SUCCEED) {\n          finishRefresh(false)\n          return@syncDb\n        }\n        isSyncDb = true\n        module.getRootEntry()\n        finishRefresh(true)\n      }\n    }\n  }\n\n  private fun finishRefresh(isSuccess: Boolean) {\n    binding.swipe.isRefreshing = false\n    HitUtil.snackShort(\n      mRootView,\n      \"${getString(R.string.sync_db)} ${\n        if (isSuccess) getString(R.string.success) else getString(\n          R.string.fail\n        )\n      }\"\n    )\n  }\n\n  override fun setLayoutId(): Int {\n    return R.layout.fragment_only_list\n  }\n\n  /**\n   * 回收站中恢复数据\n   */\n  @Subscribe(threadMode = MAIN)\n  fun onMove(event: MoveEvent) {\n    module.getRootEntry()\n  }\n\n  override fun onDestroy() {\n    super.onDestroy()\n    EventBusHelper.unReg(this)\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/main/HomeModule.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.view.main\n\nimport androidx.lifecycle.viewModelScope\nimport com.arialyy.frame.util.ResUtil\nimport com.keepassdroid.database.PwEntryV4\nimport com.keepassdroid.database.PwGroupV4\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseApp\nimport com.lyy.keepassa.base.BaseModule\nimport com.lyy.keepassa.entity.EntryType\nimport com.lyy.keepassa.entity.SimpleItemEntity\nimport com.lyy.keepassa.util.KeepassAUtil\nimport com.lyy.keepassa.util.KpaUtil\nimport com.lyy.keepassa.util.createNewEntry\nimport com.lyy.keepassa.util.deleteEntry\nimport com.lyy.keepassa.util.moveEntry\nimport com.lyy.keepassa.util.updateModifyEntry\nimport com.lyy.keepassa.view.SimpleEntryAdapter\nimport kotlinx.coroutines.flow.MutableSharedFlow\nimport kotlinx.coroutines.launch\nimport timber.log.Timber\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 4:05 下午 2021/10/25\n **/\ninternal class HomeModule : BaseModule() {\n\n  val itemDataList = mutableListOf<SimpleItemEntity>()\n\n  val itemDataFlow = MutableSharedFlow<MutableList<SimpleItemEntity>?>()\n\n  val collectionEntry by lazy {\n    val entity = SimpleItemEntity()\n    entity.title = ResUtil.getString(R.string.my_collection)\n    entity.subTitle = ResUtil.getString(R.string.current_collection_num, \"0\")\n    entity.icon = R.drawable.ic_star\n    entity.obj = EntryType.TYPE_COLLECTION\n    entity\n  }\n\n  /**\n   * update the status of deleted items\n   */\n  fun deleteEntry(adapter: SimpleEntryAdapter, pwEntryV4: PwEntryV4, oldParent: PwGroupV4) {\n    adapter.deleteEntry(itemDataList, pwEntryV4, oldParent, BaseApp.KDB.pm.rootGroup as PwGroupV4)\n  }\n\n  /**\n   * update the status of modified items\n   */\n  fun updateModifyEntry(adapter: SimpleEntryAdapter, pwEntryV4: PwEntryV4) {\n    adapter.updateModifyEntry(itemDataList, pwEntryV4, BaseApp.KDB.pm.rootGroup as PwGroupV4)\n  }\n\n  /**\n   * update root list state\n   */\n  fun createNewEntry(adapter: SimpleEntryAdapter, pwEntryV4: PwEntryV4) {\n    adapter.createNewEntry(itemDataList, pwEntryV4, BaseApp.KDB.pm.rootGroup as PwGroupV4)\n  }\n\n  /**\n   * move entry from other group\n   */\n  fun moveEntry(adapter: SimpleEntryAdapter, pwEntryV4: PwEntryV4, oldParent: PwGroupV4) {\n    adapter.moveEntry(itemDataList, pwEntryV4, oldParent, BaseApp.KDB.pm.rootGroup as PwGroupV4)\n  }\n\n  /**\n   * synchronize database\n   */\n  fun syncDb(callback: (Int) -> Unit) {\n    KpaUtil.kdbHandlerService.saveDbByForeground(callback = callback)\n  }\n\n  /**\n   * get root data\n   */\n  fun getRootEntry() {\n    itemDataList.clear()\n    viewModelScope.launch {\n      if (BaseApp.KDB == null) {\n        itemDataFlow.emit(null)\n        return@launch\n      }\n      val pm = BaseApp.KDB!!.pm\n\n      if (pm == null) {\n        itemDataFlow.emit(null)\n        return@launch\n      }\n\n      collectionEntry.subTitle = ResUtil.getString(\n        R.string.current_collection_num,\n        KpaUtil.kdbHandlerService.getCollectionNum().toString()\n      )\n      itemDataList.add(collectionEntry)\n\n      val rootGroup = pm.rootGroup\n      for (group in rootGroup.childGroups) {\n        itemDataList.add(KeepassAUtil.instance.convertPwGroup2Item(group))\n      }\n      Timber.d(\n        \"getRootEntry， 保存前的数据库hash：${BaseApp.KDB.hashCode()}, num = ${BaseApp.KDB!!.pm.entries.size}\"\n      )\n      for (entry in rootGroup.childEntries) {\n        itemDataList.add(KeepassAUtil.instance.convertPwEntry2Item(entry))\n      }\n\n      itemDataFlow.emit(itemDataList)\n    }\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/main/MainActivity.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.view.main\n\nimport android.animation.Animator\nimport android.animation.AnimatorListenerAdapter\nimport android.animation.ObjectAnimator\nimport android.app.ActivityOptions\nimport android.content.Intent\nimport android.os.Bundle\nimport android.transition.Transition\nimport android.transition.Transition.TransitionListener\nimport android.util.Pair\nimport android.view.View\nimport androidx.appcompat.widget.AppCompatImageView\nimport androidx.fragment.app.Fragment\nimport androidx.fragment.app.FragmentActivity\nimport androidx.lifecycle.ViewModelProvider\nimport androidx.preference.PreferenceManager\nimport androidx.viewpager2.adapter.FragmentStateAdapter\nimport com.alibaba.android.arouter.facade.annotation.Autowired\nimport com.alibaba.android.arouter.facade.annotation.Route\nimport com.alibaba.android.arouter.launcher.ARouter\nimport com.arialyy.frame.core.AbsFrame\nimport com.arialyy.frame.router.Routerfit\nimport com.arialyy.frame.util.ResUtil\nimport com.google.android.material.tabs.TabLayoutMediator\nimport com.keepassdroid.database.PwGroupV4\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.AnimState\nimport com.lyy.keepassa.base.AnimState.NOT_ANIM\nimport com.lyy.keepassa.base.BaseActivity\nimport com.lyy.keepassa.base.BaseApp\nimport com.lyy.keepassa.databinding.ActivityMainBinding\nimport com.lyy.keepassa.event.CheckEnvEvent\nimport com.lyy.keepassa.event.ModifyDbNameEvent\nimport com.lyy.keepassa.event.ShowTOTPEvent\nimport com.lyy.keepassa.router.ActivityRouter\nimport com.lyy.keepassa.router.DialogRouter\nimport com.lyy.keepassa.router.FragmentRouter\nimport com.lyy.keepassa.util.EventBusHelper\nimport com.lyy.keepassa.util.KeepassAUtil\nimport com.lyy.keepassa.view.create.CreateDbActivity\nimport com.lyy.keepassa.view.launcher.LauncherActivity\nimport com.lyy.keepassa.view.search.SearchDialog\nimport com.lyy.keepassa.widget.MainExpandFloatActionButton\nimport org.greenrobot.eventbus.Subscribe\nimport org.greenrobot.eventbus.ThreadMode.MAIN\nimport timber.log.Timber\n\n@Route(path = \"/main/ac\")\nclass MainActivity : BaseActivity<ActivityMainBinding>(), View.OnClickListener {\n\n  private var reenterListener: ReenterListener? = null\n  private lateinit var module: MainModule\n\n  companion object {\n    private const val MIN_SCALE = 0.85f\n    private const val MIN_ALPHA = 0.5f\n\n    // 打开搜索\n    const val OPEN_SEARCH = 1\n  }\n\n  @Autowired(name = \"KEY_IS_SHORTCUTS\")\n  @JvmField\n  var isShortcuts = false\n\n  @Autowired(name = \"shortcutsType\")\n  @JvmField\n  var shortcutType = 1\n\n  private val historyFm by lazy {\n    Routerfit.create(FragmentRouter::class.java).toMainHistoryFragment()\n  }\n\n  private val entryFm by lazy {\n    Routerfit.create(FragmentRouter::class.java).toMainHomeFragment()\n  }\n\n  override fun setLayoutId(): Int {\n    return R.layout.activity_main\n  }\n\n  override fun initData(savedInstanceState: Bundle?) {\n    super.initData(savedInstanceState)\n    ARouter.getInstance().inject(this)\n    EventBusHelper.reg(this)\n    module = ViewModelProvider(this)[MainModule::class.java]\n\n    // 处理快捷方式进入的情况\n    if (isShortcuts) {\n      // 启动搜索页\n      if (shortcutType == OPEN_SEARCH) {\n        showSearchDialog()\n      }\n    }\n    module.setEcoIcon(this, binding.dbName)\n    initData()\n    initVpAnim()\n  }\n\n  private fun initData() {\n    module.showInfoDialog(this)\n    BaseApp.isLocked = false\n    binding.headToolbar.setOnClickListener(this)\n    binding.search.setOnClickListener(this)\n    binding.lock.setOnClickListener(this)\n\n    binding.dbName.text = BaseApp.dbFileName\n    binding.dbVersion.text = BaseApp.dbName\n    val needShowTotp = PreferenceManager.getDefaultSharedPreferences(this)\n      .getBoolean(getString(R.string.set_key_main_show_totp_tab), true)\n    initVP(needShowTotp)\n\n    binding.fab.setOnItemClickListener(object : MainExpandFloatActionButton.OnItemClickListener {\n      override fun onKeyClick() {\n        Routerfit.create(ActivityRouter::class.java).toCreateEntryActivity(null)\n        binding.fab.hintMoreOperate()\n      }\n\n      override fun onGroupClick() {\n        Routerfit.create(DialogRouter::class.java)\n          .showCreateGroupDialog(BaseApp.KDB!!.pm.rootGroup as PwGroupV4)\n        binding.fab.hintMoreOperate()\n      }\n    })\n  }\n\n  private fun initVP(needShowTotp: Boolean) {\n\n    val totpFm = if (needShowTotp) {\n      Routerfit.create(FragmentRouter::class.java).toMainTOTPFragment()\n    } else {\n      null\n    }\n\n    Timber.i(\"initVP\")\n\n    val list = arrayListOf<Fragment>()\n    list.add(historyFm)\n    list.add(entryFm)\n    totpFm?.let {\n      list.add(it)\n    }\n    module.checkHasHistoryRecord()\n      .observe(this) { hasHistory ->\n        binding.vp.adapter = VpAdapter(list, this)\n        TabLayoutMediator(binding.tab, binding.vp) { tab, position ->\n//      tab.text = \"OBJECT ${(position + 1)}\"\n        }.attach()\n\n        // 是否优先显示条目\n        val showEntries = PreferenceManager.getDefaultSharedPreferences(this)\n          .getBoolean(getString(R.string.set_key_main_allow_show_entries), false)\n        if (showEntries) {\n          binding.vp.currentItem = 1\n        } else {\n          binding.vp.currentItem = if (hasHistory) 0 else 1\n        }\n        binding.vp.adapter!!.notifyDataSetChanged()\n\n        binding.tab.getTabAt(0)!!.icon = getDrawable(R.drawable.selector_ic_tab_history)\n        binding.tab.getTabAt(0)!!.text = getString(R.string.history)\n        binding.tab.getTabAt(1)!!.icon = getDrawable(R.drawable.selector_ic_tab_db)\n        binding.tab.getTabAt(1)!!.text = getString(R.string.all)\n\n        val totpTab = binding.tab.getTabAt(2)\n        totpTab?.let {\n          it.icon = getDrawable(R.drawable.selector_ic_tab_token)\n          it.text = ResUtil.getString(R.string.kpa_totp)\n        }\n\n      }\n  }\n\n  private fun initVpAnim() {\n    binding.vp.setPageTransformer { view, position ->\n      view.apply {\n        when {\n          position < -1 -> { // [-Infinity,-1)\n            // This page is way off-screen to the left.\n            alpha = 0f\n          }\n          position <= 1 -> { // [-1,1]\n            // Modify the default slide transition to shrink the page as well\n            val scaleFactor = Math.max(MIN_SCALE, 1 - Math.abs(position))\n            // Fade the page relative to its size.\n            alpha = (MIN_ALPHA +\n              (((scaleFactor - MIN_SCALE) / (1 - MIN_SCALE)) * (1 - MIN_ALPHA)))\n          }\n          else -> { // (1,+Infinity]\n            // This page is way off-screen to the right.\n            alpha = 0f\n          }\n        }\n//        binding.tab.getTabAt(0)?.view?.alpha = alpha\n//        binding.tab.getTabAt(1)?.view?.alpha = alpha\n      }\n    }\n  }\n\n  @Subscribe(threadMode = MAIN)\n  fun onShowTOTP(event: ShowTOTPEvent) {\n    initVP(event.show)\n    Timber.d(\"update totp\")\n  }\n\n  @Subscribe(threadMode = MAIN)\n  fun onCheckEnv(event: CheckEnvEvent) {\n    module.setEcoIcon(this, binding.dbName)\n  }\n\n  @Subscribe(threadMode = MAIN)\n  fun onDnNameModify(event: ModifyDbNameEvent) {\n    binding.dbVersion.text = BaseApp.dbName\n  }\n\n  override fun onClick(v: View?) {\n    if (KeepassAUtil.instance.isFastClick()) {\n      return\n    }\n    when (v!!.id) {\n      R.id.head_toolbar -> startArrowAnim()\n      R.id.search -> {\n        showSearchDialog()\n      }\n      R.id.lock -> {\n        showQuickUnlockDialog()\n      }\n    }\n  }\n\n  /**\n   * 显示搜索对话框\n   */\n  private fun showSearchDialog() {\n    val searchDialog = SearchDialog()\n    searchDialog.show(supportFragmentManager, \"search_dialog\")\n  }\n\n  override fun onBackPressed() {\n    // 返回键不退出而是进入后台\n    moveTaskToBack(true)\n  }\n\n  override fun onPostCreate(savedInstanceState: Bundle?) {\n    super.onPostCreate(savedInstanceState)\n    // 需要关闭 LauncherActivity\\ InputPassActivity \\ CreateActivity 三个界面\n    for (ac in AbsFrame.getInstance().activityStack) {\n      if (ac is LauncherActivity || ac is CreateDbActivity) {\n        ac.rootView.visibility = View.GONE\n        ac.finish()\n        ac.overridePendingTransition(0, 0)\n      }\n    }\n  }\n\n  override fun onPause() {\n    super.onPause()\n    reenterListener?.isToChangeDb = false\n  }\n\n  override fun onDestroy() {\n    super.onDestroy()\n    EventBusHelper.unReg(this)\n  }\n\n  override fun useAnim(): AnimState {\n    return NOT_ANIM\n  }\n\n  /**\n   * 跳转数据库切换\n   */\n  private fun startArrowAnim() {\n    val anim = ObjectAnimator.ofFloat(binding.arrow, \"rotation\", 0f, 180f)\n    anim.duration = MainSettingActivity.arrowAnimDuration\n    anim.addListener(object : AnimatorListenerAdapter() {\n      override fun onAnimationEnd(animation: Animator) {\n        super.onAnimationEnd(animation)\n        // 为了达到更好的效果，先将动画设置为空\n        window.enterTransition = null\n        window.exitTransition = null\n        window.returnTransition = null\n        window.reenterTransition = null\n//        window.sharedElementReenterTransition.excludeTarget(android.R.id.statusBarBackground, true)\n//        window.sharedElementReenterTransition.excludeTarget(\n//            android.R.id.navigationBarBackground, true\n//        )\n\n        val intent = Intent(this@MainActivity, MainSettingActivity::class.java)\n        val appIcon =\n          Pair<View, String>(binding.appIcon, getString(R.string.transition_app_icon))\n        val dbName =\n          Pair<View, String>(binding.dbName, getString(R.string.transition_db_name))\n        val dbVersion =\n          Pair<View, String>(binding.dbVersion, getString(R.string.transition_db_version))\n        val dbLittle =\n          Pair<View, String>(binding.arrow, getString(R.string.transition_db_little))\n        val option =\n          ActivityOptions.makeSceneTransitionAnimation(\n            this@MainActivity, appIcon, dbName, dbLittle, dbVersion\n          )\n\n        startActivity(intent, option.toBundle())\n        if (reenterListener == null) {\n          reenterListener = ReenterListener(binding.arrow)\n          window.sharedElementReenterTransition.addListener(reenterListener)\n        }\n        reenterListener?.isToChangeDb = true\n      }\n    })\n    anim.start()\n  }\n\n  private class ReenterListener(private val arrow: AppCompatImageView) : TransitionListener {\n\n    var isToChangeDb: Boolean = false\n    override fun onTransitionEnd(transition: Transition?) {\n      Timber.d(\"onTransitionEnd\")\n      if (!isToChangeDb) {\n        arrow.rotation = 0f\n      }\n      isToChangeDb = !isToChangeDb\n    }\n\n    override fun onTransitionResume(transition: Transition?) {\n      Timber.d(\"onTransitionResume\")\n    }\n\n    override fun onTransitionPause(transition: Transition?) {\n      Timber.d(\"onTransitionPause\")\n    }\n\n    override fun onTransitionCancel(transition: Transition?) {\n      Timber.d(\"onTransitionCancel\")\n    }\n\n    override fun onTransitionStart(transition: Transition?) {\n      Timber.d(\"onTransitionStart\")\n    }\n  }\n\n  private class VpAdapter(\n    private val fragments: List<Fragment>,\n    fm: FragmentActivity\n  ) : FragmentStateAdapter(fm) {\n\n    override fun getItemCount(): Int {\n      return fragments.size\n    }\n\n    override fun createFragment(position: Int): Fragment {\n      return fragments[position]\n    }\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/main/MainModule.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.view.main\n\nimport android.content.Context\nimport androidx.lifecycle.liveData\nimport androidx.lifecycle.viewModelScope\nimport androidx.preference.PreferenceManager\nimport com.arialyy.frame.router.Routerfit\nimport com.arialyy.frame.util.ResUtil\nimport com.lahm.library.EasyProtectorLib\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseApp\nimport com.lyy.keepassa.base.BaseModule\nimport com.lyy.keepassa.router.DialogRouter\nimport com.lyy.keepassa.view.main.chain.DevBirthdayChain\nimport com.lyy.keepassa.view.main.chain.DialogChain\nimport com.lyy.keepassa.view.main.chain.DonateChain\nimport com.lyy.keepassa.view.main.chain.IMainDialogInterceptor\nimport com.lyy.keepassa.view.main.chain.PermissionsChain\nimport com.lyy.keepassa.view.main.chain.TipChain\nimport com.lyy.keepassa.view.main.chain.VersionLogChain\nimport com.lyy.keepassa.widget.BubbleTextView\nimport com.lyy.keepassa.widget.BubbleTextView.OnIconClickListener\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.delay\nimport kotlinx.coroutines.launch\nimport kotlinx.coroutines.withContext\n\nclass MainModule : BaseModule() {\n\n  override fun onCleared() {\n    super.onCleared()\n  }\n\n  /**\n   * 安全级别对话框\n   * root 红色，提示危险\n   */\n  fun setEcoIcon(\n    cxt: Context,\n    btText: BubbleTextView\n  ) {\n    val needCheckEnv = PreferenceManager.getDefaultSharedPreferences(cxt)\n      .getBoolean(cxt.resources.getString(R.string.set_key_need_root_check), true)\n    if (!needCheckEnv) {\n      btText.clearIcon(BubbleTextView.LOCATION_RIGHT)\n      return\n    }\n\n    var vector = ResUtil.getSvgIcon(R.drawable.ic_eco, R.color.green)\n    var msg = cxt.getString(R.string.hint_security_green)\n    if (EasyProtectorLib.checkIsRoot()) {\n      vector = ResUtil.getSvgIcon(R.drawable.ic_eco, R.color.red)\n      msg = cxt.getString(R.string.hint_security_red)\n    } else if (EasyProtectorLib.checkIsRunningInEmulator(cxt) {\n//          BuglyLog.d(TAG, it)\n      }) {\n      vector = ResUtil.getSvgIcon(R.drawable.ic_eco, R.color.yellow)\n      msg = cxt.getString(R.string.hint_security_yellow)\n    }\n    btText.setEndIcon(vector!!)\n    btText.setOnIconClickListener(object : OnIconClickListener {\n      override fun onClick(\n        view: BubbleTextView,\n        index: Int\n      ) {\n        if (index == 2) {\n          Routerfit.create(DialogRouter::class.java).showMsgDialog(\n            msgContent = msg,\n            showCancelBt = false,\n            msgTitleEndIcon = vector\n          )\n        }\n      }\n    })\n  }\n\n  fun showInfoDialog(activity: MainActivity) {\n\n    viewModelScope.launch {\n      withContext(Dispatchers.IO) {\n        delay(600)\n      }\n      val list = arrayListOf<IMainDialogInterceptor>().apply {\n        add(VersionLogChain())\n        add(DevBirthdayChain())\n        add(DonateChain())\n        add(PermissionsChain())\n        // add(TipChain())\n      }\n      DialogChain(activity, list, 0).proceed(activity)\n\n    }\n  }\n\n  /**\n   * 检查是否有记录\n   */\n  fun checkHasHistoryRecord() = liveData {\n    BaseApp.dbRecord?.let {\n      if (BaseApp.dbRecord == null || it.localDbUri.isEmpty()) {\n        emit(false)\n        return@liveData\n      }\n      val b = withContext(Dispatchers.IO) {\n        val dao = BaseApp.appDatabase.entryRecordDao()\n        dao.hasRecord(it.localDbUri) > 0\n      }\n      emit(b)\n    }\n\n  }\n\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/main/MainSettingActivity.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.view.main\n\nimport android.animation.Animator\nimport android.animation.AnimatorListenerAdapter\nimport android.animation.ObjectAnimator\nimport android.content.Intent\nimport android.net.Uri\nimport android.os.Bundle\nimport android.util.Pair\nimport android.view.View\nimport androidx.core.app.ActivityOptionsCompat\nimport androidx.core.content.FileProvider\nimport androidx.lifecycle.ViewModelProvider\nimport com.arialyy.frame.router.Routerfit\nimport com.arialyy.frame.util.AndroidUtils\nimport com.arialyy.frame.util.ResUtil\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.R.string\nimport com.lyy.keepassa.base.AnimState\nimport com.lyy.keepassa.base.AnimState.NOT_ANIM\nimport com.lyy.keepassa.base.BaseActivity\nimport com.lyy.keepassa.base.BaseApp\nimport com.lyy.keepassa.databinding.ActivityChangeDbBinding\nimport com.lyy.keepassa.event.CheckEnvEvent\nimport com.lyy.keepassa.event.ModifyDbNameEvent\nimport com.lyy.keepassa.router.ActivityRouter\nimport com.lyy.keepassa.service.feat.XLogFeature\nimport com.lyy.keepassa.util.EventBusHelper\nimport com.lyy.keepassa.util.HitUtil\nimport com.lyy.keepassa.util.KeepassAUtil\nimport com.lyy.keepassa.util.KpaUtil\nimport com.lyy.keepassa.util.LanguageUtil\nimport com.lyy.keepassa.view.dialog.DonateDialog\nimport org.greenrobot.eventbus.Subscribe\nimport org.greenrobot.eventbus.ThreadMode.MAIN\nimport java.io.File\nimport java.util.Locale\n\n/**\n * 主页下拉设置\n */\nclass MainSettingActivity : BaseActivity<ActivityChangeDbBinding>(), View.OnClickListener {\n\n  private lateinit var module: MainModule\n\n  companion object {\n    const val arrowAnimDuration = 100L\n  }\n\n  override fun onCreate(savedInstanceState: Bundle?) {\n    super.onCreate(savedInstanceState)\n    window.enterTransition.excludeTarget(android.R.id.statusBarBackground, true)\n    window.enterTransition.excludeTarget(android.R.id.navigationBarBackground, true)\n  }\n\n  override fun useAnim(): AnimState {\n    return NOT_ANIM\n  }\n\n  override fun setLayoutId(): Int {\n    return R.layout.activity_change_db\n  }\n\n  override fun initData(savedInstanceState: Bundle?) {\n    super.initData(savedInstanceState)\n    module = ViewModelProvider(this).get(MainModule::class.java)\n    EventBusHelper.reg(this)\n    binding.kpaToolbar.setOnClickListener(this)\n    binding.close.setOnClickListener(this)\n    binding.arrow.rotation = 180f\n    binding.dbName.text = BaseApp.dbFileName\n    binding.dbVersion.text = BaseApp.dbName\n    binding.changeSetting.setOnClickListener(this)\n    binding.appSetting.setOnClickListener(this)\n    binding.changeDb.setOnClickListener(this)\n    binding.appFeedback.setOnClickListener(this)\n    binding.appFavorite.setOnClickListener(this)\n    binding.tvDonate.setOnClickListener(this)\n    binding.debug.setOnClickListener(this)\n    module.setEcoIcon(this, binding.dbName)\n    if (LanguageUtil.getSysCurrentLan() != Locale.CHINA) {\n      binding.tvTranslate.visibility = View.VISIBLE\n      binding.tvTranslate.setOnClickListener(this)\n    }\n  }\n\n  private fun startArrowAnim() {\n    val anim = ObjectAnimator.ofFloat(binding.arrow, \"rotation\", 180f, 360f)\n    anim.duration = arrowAnimDuration\n    anim.addListener(object : AnimatorListenerAdapter() {\n      override fun onAnimationEnd(animation: Animator) {\n        super.onAnimationEnd(animation)\n        binding.kpaToolbar.setBackgroundColor(ResUtil.getColor(R.color.background_color))\n        finishAfterTransition()\n        binding.arrow.visibility = View.GONE\n      }\n    })\n    anim.start()\n  }\n\n  override fun onBackPressed() {\n//    super.onBackPressed()\n    startArrowAnim()\n  }\n\n  override fun buildSharedElements(vararg sharedElements: Pair<View, String>): ArrayList<String> {\n    val appIcon =\n      Pair<View, String>(binding.appIcon, getString(string.transition_app_icon))\n    val dbName =\n      Pair<View, String>(binding.dbName, getString(string.transition_db_name))\n    val dbVersion =\n      Pair<View, String>(binding.dbVersion, getString(string.transition_db_version))\n    val dbLittle =\n      Pair<View, String>(binding.arrow, getString(string.transition_db_little))\n    return super.buildSharedElements(appIcon, dbName, dbVersion, dbLittle)\n  }\n\n  @Subscribe(threadMode = MAIN)\n  fun onCheckEnv(event: CheckEnvEvent) {\n    module.setEcoIcon(this, binding.dbName)\n  }\n\n  @Subscribe(threadMode = MAIN)\n  fun onDnNameModify(event: ModifyDbNameEvent) {\n    binding.dbVersion.text = BaseApp.dbName\n  }\n\n  override fun onClick(v: View?) {\n    if (KeepassAUtil.instance.isFastClick()) {\n      return\n    }\n    when (v!!.id) {\n      R.id.kpa_toolbar, R.id.close -> {\n        startArrowAnim()\n//        finishAfterTransition()\n      }\n      R.id.change_setting -> {\n        Routerfit.create(ActivityRouter::class.java, this).toDbSetting(\n          opt = ActivityOptionsCompat.makeSceneTransitionAnimation(this)\n        )\n      }\n      R.id.app_setting -> {\n        Routerfit.create(ActivityRouter::class.java, this).toAppSetting(\n          opt = ActivityOptionsCompat.makeSceneTransitionAnimation(this)\n        )\n      }\n      R.id.change_db -> {\n        KeepassAUtil.instance.turnLauncher()\n      }\n      R.id.app_feedback -> {\n//        val emailIntent = Intent(Intent.ACTION_SENDTO).apply {\n//          data = Uri.parse(\"mailto:\") // 确保只有邮件应用能接收\n//          putExtra(Intent.EXTRA_EMAIL, arrayOf(\"dornkpa@gmail.com\"))\n//          putExtra(Intent.EXTRA_SUBJECT, \"Hello, KeepassA\")\n//          putExtra(\n//              Intent.EXTRA_TEXT,\n//              getString(\n//                  R.string.feedback_email_msg, AndroidUtils.getDeviceBrand(),\n//                  AndroidUtils.getDeviceModel(), AndroidUtils.getSystemVersion(),\n//                  AndroidUtils.getMetrics(this@MainSettingActivity),\n//                  AndroidUtils.getVersionName(this@MainSettingActivity)\n//              )\n//          )\n//          addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)\n//        }\n//        if (emailIntent.resolveActivity(packageManager) != null) {\n//          startActivity(emailIntent)\n//        } else {\n//          HitUtil.toaskShort(getString(R.string.send_email_fail))\n//        }\n        startActivity(Intent(Intent.ACTION_VIEW).apply {\n          data = Uri.parse(\"https://github.com/AriaLyy/KeepassA/issues\")\n        })\n      }\n      R.id.app_favorite -> {\n        if (AndroidUtils.hasAnyMarket(this)) {\n          val markIntent = Intent(Intent.ACTION_VIEW).apply {\n            data = Uri.parse(\"market://details?id=com.lyy.keepassa\")\n            addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)\n          }\n          startActivity(markIntent)\n        } else {\n          HitUtil.toaskShort(getString(R.string.mark_not_exit))\n        }\n      }\n      R.id.tvDonate -> {\n        val donateDialog = DonateDialog()\n        donateDialog.show()\n      }\n      R.id.tvTranslate -> {\n        KpaUtil.openUrlWithBrowser(\"https://hosted.weblate.org/projects/keepassa/string/\")\n      }\n      R.id.debug -> {\n        val sendIntent = Intent().apply {\n          action = Intent.ACTION_SEND\n          addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_GRANT_READ_URI_PERMISSION)\n          addCategory(\"android.intent.category.DEFAULT\")\n          val uri = FileProvider.getUriForFile(\n            this@MainSettingActivity,\n            \"${packageName}.provider\",\n            File(XLogFeature.getLogPath())\n          )\n          putExtra(Intent.EXTRA_STREAM, uri)\n          type = \"*/*\"\n        }\n        startActivity(Intent.createChooser(sendIntent, XLogFeature.getLogName()))\n      }\n    }\n  }\n\n  override fun onDestroy() {\n    super.onDestroy()\n    EventBusHelper.unReg(this)\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/main/QuickUnlockActivity.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.view.main\n\nimport KDBAutoFillRepository\nimport android.annotation.SuppressLint\nimport android.app.Activity\nimport android.app.PendingIntent\nimport android.app.PendingIntent.FLAG_IMMUTABLE\nimport android.app.assist.AssistStructure\nimport android.content.Context\nimport android.content.Intent\nimport android.content.IntentSender\nimport android.content.res.AssetManager\nimport android.os.Build\nimport android.os.Build.VERSION\nimport android.os.Build.VERSION_CODES\nimport android.os.Bundle\nimport android.view.View\nimport android.view.autofill.AutofillManager\nimport androidx.activity.result.contract.ActivityResultContract\nimport androidx.arch.core.executor.ArchTaskExecutor\nimport androidx.biometric.BiometricPrompt\nimport androidx.biometric.BiometricPrompt.AuthenticationCallback\nimport androidx.biometric.BiometricPrompt.AuthenticationResult\nimport androidx.biometric.BiometricPrompt.CryptoObject\nimport androidx.biometric.BiometricPrompt.ERROR_NEGATIVE_BUTTON\nimport androidx.core.app.ActivityOptionsCompat\nimport androidx.lifecycle.Observer\nimport androidx.lifecycle.ViewModelProvider\nimport androidx.preference.PreferenceManager\nimport com.alibaba.android.arouter.facade.annotation.Route\nimport com.arialyy.frame.router.Routerfit\nimport com.arialyy.frame.util.KeyStoreUtil\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseActivity\nimport com.lyy.keepassa.base.BaseApp\nimport com.lyy.keepassa.databinding.DialogQuickUnlockBinding\nimport com.lyy.keepassa.entity.AutoFillParam\nimport com.lyy.keepassa.entity.QuickUnLockRecord\nimport com.lyy.keepassa.router.ActivityRouter\nimport com.lyy.keepassa.util.HitUtil\nimport com.lyy.keepassa.util.KeepassAUtil\nimport com.lyy.keepassa.util.KpaUtil\nimport com.lyy.keepassa.util.NotificationUtil\nimport com.lyy.keepassa.util.QuickUnLockUtil\nimport com.lyy.keepassa.util.isCanOpenQuickLock\nimport com.lyy.keepassa.view.fingerprint.FingerprintModule\nimport com.lyy.keepassa.view.launcher.LauncherActivity\nimport com.lyy.keepassa.view.search.AutoFillEntrySearchActivity\nimport com.lyy.keepassa.widget.ShortPasswordView\nimport timber.log.Timber\nimport java.io.IOException\nimport java.util.UUID\n\n/**\n * 快速解锁对话框\n */\n@Route(path = \"/launcher/quickLock\")\nclass QuickUnlockActivity : BaseActivity<DialogQuickUnlockBinding>() {\n  private lateinit var module: FingerprintModule\n  private var fingerRecord: QuickUnLockRecord? = null\n  private lateinit var keyStoreUtil: KeyStoreUtil\n\n  /**\n   * 搜索启动器\n   */\n  private val searchLauncher =\n    registerForActivityResult(object : ActivityResultContract<String, UUID?>() {\n      override fun createIntent(context: Context, input: String): Intent {\n        return AutoFillEntrySearchActivity.createSearchIntent(\n          context,\n          AutoFillParam(apkPkgName = input),\n          if (VERSION.SDK_INT >= VERSION_CODES.O) intent.getParcelableExtra(\n            AutofillManager.EXTRA_ASSIST_STRUCTURE\n          ) as AssistStructure? else null\n        )\n      }\n\n      override fun parseResult(resultCode: Int, data: Intent?): UUID? {\n        if (data == null) {\n          return null\n        }\n        return data.getSerializableExtra(AutoFillEntrySearchActivity.EXTRA_ENTRY_ID) as? UUID\n      }\n    }) { it ->\n      val apkPkgName = module.autoFillParam?.apkPkgName\n      if (apkPkgName.isNullOrEmpty()) {\n        Timber.e(\"apkPkgName is null\")\n        finish()\n        return@registerForActivityResult\n      }\n      // 搜索返回的数据\n      if (it == null) {\n        setResult(\n          Activity.RESULT_OK,\n          KeepassAUtil.instance.getFillResponse(this, intent, apkPkgName)\n        )\n        finish()\n        return@registerForActivityResult\n      }\n      setResult(\n        Activity.RESULT_OK,\n        BaseApp.KDB.pm.entries[it]?.let {\n          KeepassAUtil.instance.getFillResponse(this, intent, it, apkPkgName)\n        }\n      )\n      finish()\n    }\n\n  override fun initData(savedInstanceState: Bundle?) {\n    module = ViewModelProvider(this)[FingerprintModule::class.java]\n    NotificationUtil.startQuickUnlockNotify(this)\n    BaseApp.isLocked = true\n    initUi()\n    module.autoFillParam = intent.getParcelableExtra(LauncherActivity.KEY_AUTO_FILL_PARAM)\n  }\n\n  private fun initUi() {\n    BaseApp.dbRecord?.localDbUri?.let {\n      module.getQuickUnlockRecord(it).observe(this, Observer { record ->\n        if (record != null) {\n          fingerRecord = record\n          binding.fingerprint.visibility = View.VISIBLE\n          binding.fingerprint.playAnimation()\n          keyStoreUtil = KeyStoreUtil()\n          binding.fingerprint.setOnClickListener {\n            showBiometricPrompt()\n          }\n          return@Observer\n        }\n        binding.fingerprint.visibility = View.GONE\n      })\n    }\n\n\n    binding.pass.setInputCompleteListener(object : ShortPasswordView.InputCompleteListener {\n      override fun inputComplete(text: String) {\n        if (QuickUnLockUtil.encryptStr(text) == BaseApp.shortPass) {\n          BaseApp.isLocked = false\n          turnActivity()\n          return\n        }\n        HitUtil.toaskShort(getString(R.string.error_pass))\n      }\n\n      override fun invalidContent() {\n      }\n    })\n\n    val sh = PreferenceManager.getDefaultSharedPreferences(BaseApp.APP)\n    val passLen = sh\n      .getString(getString(R.string.set_quick_pass_len), \"3\")!!\n      .toString()\n      .toInt()\n\n    val value = sh.getString(getString(R.string.set_quick_pass_type), \"1\")!!\n      .toString()\n      .toInt()\n    binding.title.text =\n      resources.getStringArray(R.array.quick_pass_type_entries)[value - 1].format(passLen)\n\n    binding.pass.setPassLen(passLen)\n\n    binding.changeDb.setOnClickListener {\n      KeepassAUtil.instance.turnLauncher(LauncherActivity.OPEN_TYPE_OPEN_DB)\n    }\n\n    startBgAnim()\n  }\n\n  /**\n   * 显示验证指纹对话框\n   */\n  @SuppressLint(\"RestrictedApi\")\n  private fun showBiometricPrompt() {\n    val promptInfo = BiometricPrompt.PromptInfo.Builder()\n      .setTitle(getString(R.string.fingerprint_unlock))\n      .setSubtitle(getString(R.string.verify_finger))\n      .setNegativeButtonText(getString(R.string.cancel))\n//        .setConfirmationRequired(false)\n      .build()\n\n    if (fingerRecord == null) {\n      Timber.e(\"解锁记录为空\")\n      return\n    }\n    val biometricPrompt = BiometricPrompt(this, ArchTaskExecutor.getMainThreadExecutor(),\n      object : AuthenticationCallback() {\n        override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {\n          val str = if (errorCode == ERROR_NEGATIVE_BUTTON) {\n            \"${getString(R.string.verify_finger)}${getString(R.string.cancel)}\"\n          } else {\n            getString(R.string.verify_finger_fail)\n          }\n          HitUtil.snackShort(mRootView, str)\n        }\n\n        override fun onAuthenticationSucceeded(result: AuthenticationResult) {\n          super.onAuthenticationSucceeded(result)\n          turnActivity()\n        }\n\n        override fun onAuthenticationFailed() {\n          super.onAuthenticationFailed()\n          HitUtil.snackShort(mRootView, getString(R.string.verify_finger_fail))\n        }\n      })\n    try {\n      biometricPrompt.authenticate(\n        promptInfo,\n        CryptoObject(keyStoreUtil.getDecryptCipher(fingerRecord!!.passIv!!))\n      )\n    } catch (e: Exception) {\n      Timber.e(e)\n    }\n  }\n\n  private fun startBgAnim() {\n    try {\n      binding.anim.setAnimation(\n        assets.open(\"lockedAnim.json\", AssetManager.ACCESS_STREAMING),\n        \"LottieCacheLock\"\n      )\n    } catch (e: IOException) {\n      Timber.e(e)\n    }\n  }\n\n  override fun onResume() {\n    super.onResume()\n    if (!binding.anim.isAnimating) {\n      binding.anim.resumeAnimation()\n    }\n  }\n\n  override fun onPause() {\n    super.onPause()\n    if (binding.anim.isAnimating) {\n      binding.anim.pauseAnimation()\n    }\n  }\n\n  /**\n   * 解锁成功进入特定页面\n   * 如果MainActivity已启动，直接Finish当前\n   * 如果MainActivity没有启动，启动MainActivity\n   * 如果是自动填充进入，\n   */\n  private fun turnActivity() {\n    BaseApp.isLocked = false\n    NotificationUtil.startDbOpenNotify(this@QuickUnlockActivity)\n    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && BaseApp.KDB != null && module.isAutoFill()) {\n      val apkPkgName = module.autoFillParam?.apkPkgName\n      if (apkPkgName.isNullOrEmpty()) {\n        Timber.e(\"apkpkgName is null\")\n        Routerfit.create(ActivityRouter::class.java, this).toMainActivity(\n          opt = ActivityOptionsCompat.makeSceneTransitionAnimation(this)\n        )\n        return\n      }\n\n      if (module.autoFillParam?.isSave == true) {\n        Routerfit.create(ActivityRouter::class.java).toEditEntryActivity(module.autoFillParam!!)\n        Timber.i(\"save entry\")\n        return\n      }\n\n      val datas = KDBAutoFillRepository.getAutoFillDataByPackageName(apkPkgName)\n      // 如果查找不到数据，跳转到搜索页面\n      if (datas == null || datas.isEmpty()) {\n        searchLauncher.launch(packageName, ActivityOptionsCompat.makeSceneTransitionAnimation(this))\n        return\n      }\n      val data = KeepassAUtil.instance.getFillResponse(this, intent, apkPkgName)\n      setResult(Activity.RESULT_OK, data)\n      finish()\n      return\n    }\n    // 正常跳转\n    Routerfit.create(ActivityRouter::class.java, this).toMainActivity(\n      opt = ActivityOptionsCompat.makeSceneTransitionAnimation(this)\n    )\n  }\n\n  override fun onNewIntent(intent: Intent?) {\n    super.onNewIntent(intent)\n    binding.pass.clean()\n  }\n\n  override fun onBackPressed() {\n//    super.onBackPressed()\n    moveTaskToBack(true)\n  }\n\n  override fun setLayoutId(): Int {\n    return R.layout.dialog_quick_unlock\n  }\n\n  companion object {\n\n    /**\n     * 从通知进入快速解锁页\n     */\n    internal fun createQuickUnlockPending(context: Context): PendingIntent {\n      if (!BaseApp.APP.isCanOpenQuickLock()) {\n        return LauncherActivity.createLauncherPending(context)\n      }\n\n      return Intent(context, QuickUnlockActivity::class.java).let { notificationIntent ->\n        PendingIntent.getActivity(context, 0, notificationIntent, PendingIntent.FLAG_IMMUTABLE)\n      }\n    }\n\n    internal fun startQuickUnlockActivity(context: Context, flags: Int = -1) {\n      if (!BaseApp.APP.isCanOpenQuickLock()) {\n        LauncherActivity.startLauncherActivity(context, flags)\n        return\n      }\n\n      context.startActivity(Intent(context, QuickUnlockActivity::class.java).apply {\n        if (flags != -1) {\n          this.flags = flags\n        }\n      })\n    }\n\n    /**\n     * 从自动填充快速解锁界面\n     */\n    internal fun getQuickUnlockSenderForResponse(\n      context: Context,\n      pkgName: String,\n      structure: AssistStructure\n    ): IntentSender {\n      if (!BaseApp.APP.isCanOpenQuickLock()) {\n        return LauncherActivity.getAuthDbIntentSender(context, apkPackageName = pkgName)\n      }\n      val intent = Intent(context, QuickUnlockActivity::class.java).also {\n        it.putExtra(LauncherActivity.KEY_AUTO_FILL_PARAM, AutoFillParam(apkPkgName = pkgName))\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {\n          it.putExtra(AutofillManager.EXTRA_ASSIST_STRUCTURE, structure)\n        }\n        it.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK\n      }\n      return PendingIntent.getActivity(\n        context,\n        1,\n        intent,\n        PendingIntent.FLAG_CANCEL_CURRENT or FLAG_IMMUTABLE\n      )\n        .intentSender\n    }\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/main/SearchSuggestionProvider.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.view.main\n\nimport android.content.SearchRecentSuggestionsProvider\n\nclass SearchSuggestionProvider : SearchRecentSuggestionsProvider() {\n  init {\n    setupSuggestions(AUTHORITY, MODE)\n  }\n\n  companion object {\n    const val AUTHORITY = \"com.example.MySuggestionProvider\"\n    const val MODE: Int = SearchRecentSuggestionsProvider.DATABASE_MODE_QUERIES\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/main/chain/DevBirthdayChain.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.view.main.chain\n\nimport android.widget.Button\nimport com.arialyy.frame.router.Routerfit\nimport com.arialyy.frame.util.ResUtil\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.router.DialogRouter\nimport com.lyy.keepassa.view.dialog.DonateDialog\nimport com.lyy.keepassa.view.dialog.OnMsgBtClickListener\nimport org.joda.time.DateTime\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 2023/4/22\n **/\nclass DevBirthdayChain : IMainDialogInterceptor {\n  override fun intercept(chain: DialogChain): MainDialogResponse {\n//    val dt = DateTime(2020, 10, 2, 0, 0)\n    val dt = DateTime(System.currentTimeMillis())\n    if (dt.monthOfYear == 10 && dt.dayOfMonth == 2) {\n      Routerfit.create(DialogRouter::class.java).showMsgDialog(\n        msgTitle = ResUtil.getString(R.string.donate),\n        msgContent = ResUtil.getString(R.string.dev_birthday),\n        cancelText = \"NO\",\n        enterText = \"YES\",\n        btnClickListener = object : OnMsgBtClickListener {\n          override fun onCover(v: Button) {\n          }\n\n          override fun onEnter(v: Button) {\n            DonateDialog().show()\n          }\n\n          override fun onCancel(v: Button) {\n          }\n        }\n      )\n      return MainDialogResponse(MainDialogResponse.RESPONSE_OK)\n    }\n    return chain.proceed(chain.activity)\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/main/chain/DialogChain.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.view.main.chain\n\nimport com.lyy.keepassa.view.main.MainActivity\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 2023/4/22\n **/\nclass DialogChain(\n  val activity: MainActivity,\n  private val interceptors: List<IMainDialogInterceptor>,\n  private val index: Int = 0\n) : IMainDialogInterceptor.IChain {\n\n  override fun proceed(context: MainActivity): MainDialogResponse {\n    if (index > interceptors.size - 1) {\n      return MainDialogResponse(MainDialogResponse.RESPONSE_FINISH)\n    }\n    val interceptor = interceptors[index]\n    val next = DialogChain(activity, interceptors, index + 1)\n    return interceptor.intercept(next)\n  }\n\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/main/chain/DonateChain.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.view.main.chain\n\nimport android.content.Context\nimport androidx.core.content.edit\nimport com.lyy.keepassa.base.Constance\nimport com.lyy.keepassa.view.dialog.DonateDialog\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 2023/4/22\n **/\nclass DonateChain : IMainDialogInterceptor {\n  override fun intercept(chain: DialogChain): MainDialogResponse {\n    val ac = chain.activity\n    val pre = ac.getSharedPreferences(Constance.PRE_FILE_NAME, Context.MODE_PRIVATE)\n    val startNum = pre.getInt(Constance.PRE_KEY_START_APP_NUM, 0)\n    if (startNum >= Constance.START_DONATE_JUDGMENT_VALUE) {\n      val donateDialog = DonateDialog()\n      donateDialog.setOnDismissListener {\n        pre.edit {\n          putInt(Constance.PRE_KEY_START_APP_NUM, 0)\n        }\n      }\n      donateDialog.show()\n      return MainDialogResponse(MainDialogResponse.RESPONSE_OK)\n    }\n    return chain.proceed(ac)\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/main/chain/IMainDialogInterceptor.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.view.main.chain\n\nimport com.lyy.keepassa.view.main.MainActivity\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 2023/4/22\n **/\ninterface IMainDialogInterceptor {\n  fun intercept(chain: DialogChain): MainDialogResponse\n\n  public interface IChain {\n    fun proceed(context: MainActivity): MainDialogResponse\n\n  }\n\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/main/chain/MainDialogResponse.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.view.main.chain\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 2023/4/22\n **/\ndata class MainDialogResponse(val code: Int) {\n  companion object {\n    const val RESPONSE_OK = 1\n    const val RESPONSE_BREAK = 2\n    const val RESPONSE_FINISH = 3\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/main/chain/PermissionsChain.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.view.main.chain\n\nimport com.arialyy.frame.util.ResUtil\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.util.PermissionsUtil\nimport timber.log.Timber\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 2023/4/22\n **/\nclass PermissionsChain : IMainDialogInterceptor {\n  override fun intercept(chain: DialogChain): MainDialogResponse {\n    Timber.d(\"PermissionsChain\")\n    val ac = chain.activity\n    if (PermissionsUtil.needShowBackgroundStartDialog(ac)) {\n      PermissionsUtil.showAutoFillMsgDialog(\n        ac,\n        ResUtil.getString(R.string.hint_open_backgroun_start)\n      )\n      return MainDialogResponse(MainDialogResponse.RESPONSE_OK)\n    }\n\n    return chain.proceed(ac)\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/main/chain/TipChain.kt",
    "content": "package com.lyy.keepassa.view.main.chain\n\nimport com.arialyy.frame.router.Routerfit\nimport com.lyy.keepassa.base.KeyConstance\nimport com.lyy.keepassa.router.DialogRouter\nimport com.lyy.keepassa.util.CommonKVStorage\nimport org.joda.time.DateTime\nimport org.joda.time.Days\nimport timber.log.Timber\nimport kotlin.math.abs\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 4:17 PM 2023/5/10\n **/\nclass TipChain : IMainDialogInterceptor {\n  override fun intercept(chain: DialogChain): MainDialogResponse {\n\n    val dontShowTip = CommonKVStorage.getBoolean(KeyConstance.KEY_DONT_SHOW_TIP, false)\n    val lastStartTime = CommonKVStorage.getLong(KeyConstance.KEY_LAST_TIP_START_TIME)\n    val diffDay = abs(Days.daysBetween(DateTime.now(), DateTime(lastStartTime)).days)\n    Timber.d(\"TipChain, dontShowTip = $dontShowTip, lastStartTime = $lastStartTime, diffDay = $diffDay\")\n\n//    if (!dontShowTip && diffDay >= 1) {\n      CommonKVStorage.put(KeyConstance.KEY_LAST_TIP_START_TIME, System.currentTimeMillis())\n      Routerfit.create(DialogRouter::class.java).showTipDialog()\n//    }\n\n    return MainDialogResponse(MainDialogResponse.RESPONSE_OK)\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/main/chain/VersionLogChain.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.view.main.chain\n\nimport android.content.Context\nimport com.arialyy.frame.util.AndroidUtils\nimport com.lyy.keepassa.base.Constance\nimport com.lyy.keepassa.util.isDestroy\nimport com.lyy.keepassa.view.UpgradeLogDialog\nimport timber.log.Timber\n\n/**\n * 显示版本日志对话框，显示逻辑：\n * 配置文件的版本号不存在，或当前版本号大于配置文件的版本号\n * @Author laoyuyu\n * @Description\n * @Date 2023/4/22\n **/\nclass VersionLogChain : IMainDialogInterceptor {\n  override fun intercept(chain: DialogChain): MainDialogResponse {\n    val activity = chain.activity\n    if (activity.isDestroy()) {\n      Timber.i(\"activity 已经销毁\")\n      return MainDialogResponse(MainDialogResponse.RESPONSE_BREAK)\n    }\n    val sharedPreferences =\n      activity.getSharedPreferences(Constance.PRE_FILE_NAME, Context.MODE_PRIVATE)\n    val versionCode = sharedPreferences.getInt(Constance.VERSION_CODE, -1)\n    if (versionCode < 0 || versionCode < AndroidUtils.getVersionCode(activity)) {\n      UpgradeLogDialog().show()\n      return MainDialogResponse(MainDialogResponse.RESPONSE_OK)\n    }\n    return chain.proceed(activity)\n  }\n\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/menu/EntryDetailFilePopMenu.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.view.menu\n\nimport android.annotation.SuppressLint\nimport android.view.Gravity\nimport android.view.MenuInflater\nimport android.view.View\nimport androidx.appcompat.view.menu.MenuPopupHelper\nimport androidx.appcompat.widget.PopupMenu\nimport androidx.fragment.app.FragmentActivity\nimport com.arialyy.frame.router.Routerfit\nimport com.arialyy.frame.util.FileUtil\nimport com.arialyy.frame.util.ReflectionUtil\nimport com.arialyy.frame.util.ResUtil\nimport com.keepassdroid.database.security.ProtectedBinary\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.router.DialogRouter\nimport com.lyy.keepassa.util.KpaUtil\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.MainScope\nimport kotlinx.coroutines.cancel\nimport kotlinx.coroutines.launch\nimport kotlinx.coroutines.withContext\nimport java.io.File\nimport java.io.FileOutputStream\nimport java.nio.channels.Channels\n\n/**\n * 项目详情附件悬浮菜单\n * @param x 偏移量\n */\n@SuppressLint(\"RestrictedApi\")\nclass EntryDetailFilePopMenu(\n  private val context: FragmentActivity,\n  view: View,\n  private val key: String,\n  private val file: ProtectedBinary\n) : IPopMenu {\n  private val popup: PopupMenu = PopupMenu(context, view, Gravity.END)\n  private val help: MenuPopupHelper\n  private var onDownloadClick: OnDownloadClick? = null\n  private val scope = MainScope()\n\n  interface OnDownloadClick {\n    fun onDownload(\n      key: String,\n      file: ProtectedBinary\n    )\n  }\n\n  init {\n    val inflater: MenuInflater = popup.menuInflater\n    inflater.inflate(R.menu.entry_detail_file_summary, popup.menu)\n    // 以下代码为强制显示icon\n    val mPopup = ReflectionUtil.getField(PopupMenu::class.java, \"mPopup\")\n    mPopup.isAccessible = true\n    help = mPopup.get(popup) as MenuPopupHelper\n    help.setForceShowIcon(true)\n    popup.setOnMenuItemClickListener { item ->\n      when (item.itemId) {\n        R.id.download_file -> {\n          onDownloadClick?.onDownload(key, file)\n        }\n        R.id.open_whit_text -> {\n          Routerfit.create(DialogRouter::class.java).showMsgDialog(\n            msgTitle = ResUtil.getString(R.string.txt_viewer),\n            msgContent = String(file.data.readBytes()),\n            showCancelBt = false\n          )\n        }\n        R.id.open_whit_img -> {\n          val bytes = file.data.readBytes()\n          if (bytes.isNotEmpty()) {\n            Routerfit.create(DialogRouter::class.java).showImgViewerDialog(bytes)\n          }\n        }\n        R.id.open_whit_other -> {\n          // 将文件保存到缓存中\n          openFile()\n        }\n      }\n      dismiss()\n\n      true\n    }\n  }\n\n  /**\n   * 将附件保存到缓存中，并打开\n   */\n  private fun openFile() {\n    KpaUtil.scope.launch {\n      val targetFile = File(context.cacheDir, key)\n      withContext(Dispatchers.IO) {\n        val fic = Channels.newChannel(file.data)\n        val foc = FileOutputStream(targetFile).channel\n        foc.transferFrom(fic, 0, Int.MAX_VALUE.toLong())\n        fic.close()\n        foc.close()\n      }\n      FileUtil.openFile(context, targetFile)\n      scope.cancel()\n    }\n  }\n\n  public fun setOnDownloadClick(onDownloadClick: OnDownloadClick) {\n    this.onDownloadClick = onDownloadClick\n  }\n\n  public fun show() {\n    popup.show()\n  }\n\n  public fun dismiss() {\n    popup.dismiss()\n  }\n\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/menu/EntryDetailStrPopMenu.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.view.menu\n\nimport android.annotation.SuppressLint\nimport android.content.Intent\nimport android.net.Uri\nimport android.view.Gravity\nimport android.view.MenuInflater\nimport android.view.View\nimport androidx.appcompat.view.menu.MenuPopupHelper\nimport androidx.appcompat.widget.PopupMenu\nimport androidx.fragment.app.FragmentActivity\nimport com.arialyy.frame.util.ReflectionUtil\nimport com.keepassdroid.database.security.ProtectedString\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.util.ClipboardUtil\nimport com.lyy.keepassa.util.HitUtil\n\n/**\n * 项目详情自定义字段悬浮菜单\n * @param x 偏移量\n */\n@SuppressLint(\"RestrictedApi\")\nclass EntryDetailStrPopMenu(\n  private val context: FragmentActivity,\n  view: View,\n  private val str: ProtectedString,\n  private var showPass: Boolean = true // true：菜单（显示密码），false：菜单(隐藏密码)\n) : IPopMenu {\n  private val popup: PopupMenu = PopupMenu(context, view, Gravity.END)\n  private val help: MenuPopupHelper\n  private var showPassCallback: OnShowPassCallback? = null\n\n  interface OnShowPassCallback {\n    fun showPass(showPass: Boolean)\n  }\n\n  init {\n    val inflater: MenuInflater = popup.menuInflater\n    inflater.inflate(R.menu.entry_detail_text_summary, popup.menu)\n\n    // 是否显示密码\n    popup.menu.findItem(R.id.show_pass).isVisible = str.isProtected\n    // 是否显示打开url\n    popup.menu.findItem(R.id.open_url).isVisible =\n      str.toString()\n        .startsWith(\"http\", ignoreCase = true)\n\n    if (!showPass) {\n      setHidePass()\n    }\n\n    // 以下代码为强制显示icon\n    val mPopup = ReflectionUtil.getField(PopupMenu::class.java, \"mPopup\")\n    mPopup.isAccessible = true\n    help = mPopup.get(popup) as MenuPopupHelper\n    help.setForceShowIcon(true)\n    popup.setOnMenuItemClickListener { item ->\n      when (item.itemId) {\n        R.id.copy_clip -> {\n          ClipboardUtil.get()\n            .copyDataToClip(str.toString())\n          HitUtil.toaskShort(context.getString(R.string.hint_copy_to_clip))\n        }\n\n        R.id.open_url -> {\n          val uri: Uri = Uri.parse(str.toString())\n          val intent = Intent(Intent.ACTION_VIEW, uri)\n          context.startActivity(intent)\n        }\n\n        R.id.show_pass -> {\n          if (showPassCallback != null) {\n            showPassCallback!!.showPass(showPass)\n          }\n        }\n      }\n      popup.dismiss()\n\n      true\n    }\n  }\n\n  /**\n   * 设置隐藏密码\n   */\n  fun setHidePass() {\n    showPass = false\n    val item = popup.menu.findItem(R.id.show_pass)\n    item.icon = context.getDrawable(R.drawable.ic_view_off_black)\n    item.title = context.getString(R.string.hide_pass)\n  }\n\n  fun setOnShowPassCallback(callback: OnShowPassCallback) {\n    this.showPassCallback = callback\n  }\n\n  fun show() {\n    popup.show()\n  }\n\n  fun show(\n    x: Int,\n    y: Int\n  ) {\n    help.show(x, y)\n  }\n\n  fun getPopMenu(): PopupMenu {\n    return popup\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/menu/EntryPopMenu.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.view.menu\n\nimport android.annotation.SuppressLint\nimport android.text.Html\nimport android.text.Spanned\nimport android.view.Gravity\nimport android.view.MenuInflater\nimport android.view.View\nimport android.widget.Button\nimport androidx.appcompat.view.menu.MenuPopupHelper\nimport androidx.appcompat.widget.PopupMenu\nimport androidx.fragment.app.FragmentActivity\nimport androidx.preference.PreferenceManager\nimport com.arialyy.frame.router.Routerfit\nimport com.arialyy.frame.util.ReflectionUtil\nimport com.arialyy.frame.util.ResUtil\nimport com.keepassdroid.database.PwEntry\nimport com.keepassdroid.database.PwEntryV4\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseApp\nimport com.lyy.keepassa.router.DialogRouter\nimport com.lyy.keepassa.util.ClipboardUtil\nimport com.lyy.keepassa.util.HitUtil\nimport com.lyy.keepassa.util.KdbUtil\nimport com.lyy.keepassa.util.KpaUtil\nimport com.lyy.keepassa.util.VibratorUtil\nimport com.lyy.keepassa.util.copyPassword\nimport com.lyy.keepassa.util.copyUserName\nimport com.lyy.keepassa.view.dialog.OnMsgBtClickListener\nimport com.lyy.keepassa.view.dir.ChooseGroupActivity\n\n/**\n * 群组长按菜单\n * @param x 偏移量\n */\n@SuppressLint(\"RestrictedApi\")\nclass EntryPopMenu(\n  private val context: FragmentActivity,\n  view: View,\n  private val entry: PwEntry,\n  private val x: Int,\n  private val isInRecycleBin: Boolean = false\n) : IPopMenu {\n  private val popup: PopupMenu = PopupMenu(context, view, Gravity.START)\n  private val help: MenuPopupHelper\n\n  init {\n    val inflater: MenuInflater = popup.menuInflater\n    inflater.inflate(R.menu.pop_entry_summary, popup.menu)\n\n    popup.menu.findItem(R.id.undo).isVisible = isInRecycleBin\n    var hasOtp = false\n    if (BaseApp.isV4) {\n      for (d in (entry as PwEntryV4).strings) {\n        if (d.key.contains(\"TOTP Seed\") || d.key.contains(\"otp\") || d.key.contains(\"hotp\")) {\n          hasOtp = true\n          break\n        }\n      }\n    }\n    popup.menu.findItem(R.id.copy_totp).isVisible = hasOtp\n\n    // 以下代码为强制显示icon\n    val mPopup = ReflectionUtil.getField(PopupMenu::class.java, \"mPopup\")\n    mPopup.isAccessible = true\n    help = mPopup.get(popup) as MenuPopupHelper\n    help.setForceShowIcon(true)\n    popup.setOnMenuItemClickListener { item ->\n      when (item.itemId) {\n        R.id.del -> {\n          delEntry()\n        }\n        R.id.copy_user -> {\n          entry.copyUserName()\n        }\n        R.id.copy_pw -> {\n          entry.copyPassword()\n        }\n        R.id.copy_totp -> {\n          Routerfit.create(DialogRouter::class.java).showTotpDisplayDialog(entry.uuid.toString())\n        }\n        R.id.undo, R.id.move -> {\n          ChooseGroupActivity.moveEntry(context, entry.uuid)\n        }\n//        R.id.multi_choice -> {\n//          EventBus.getDefault().post(MultiChoiceEvent())\n//        }\n      }\n      dismiss()\n      true\n    }\n  }\n\n  @SuppressLint(\"StringFormatMatches\")\n  private fun delEntry() {\n    // 是否直接删除条目\n    val deleteDirectly = PreferenceManager.getDefaultSharedPreferences(BaseApp.APP)\n      .getBoolean(context.getString(R.string.set_key_delete_no_recycle_bin), false)\n\n    if (deleteDirectly) {\n      handleDelEntry()\n      return\n    }\n\n    val msg: Spanned = if (BaseApp.isV4) {\n      if (isInRecycleBin) {\n        Html.fromHtml(context.getString(R.string.hint_del_entry_no_recycle, entry.title))\n      } else {\n        Html.fromHtml(context.getString(R.string.hint_del_entry, entry.title, entry.title))\n      }\n    } else {\n      Html.fromHtml(context.getString(R.string.hint_del_entry_no_recycle, entry.title))\n    }\n\n    Routerfit.create(DialogRouter::class.java).showMsgDialog(\n      msgTitle = ResUtil.getString(R.string.del_entry),\n      msgContent = msg,\n      btnClickListener = object : OnMsgBtClickListener {\n        override fun onCover(v: Button) {\n        }\n\n        override fun onEnter(v: Button) {\n          handleDelEntry()\n        }\n\n        override fun onCancel(v: Button) {\n        }\n      }\n    )\n  }\n\n  /**\n   * 处理删除条目\n   */\n  private fun handleDelEntry() {\n    KpaUtil.kdbHandlerService.deleteEntry(entry as PwEntryV4) {\n      HitUtil.toaskShort(\n        \"${context.getString(R.string.del_entry)}${context.getString(R.string.success)}\"\n      )\n      VibratorUtil.vibrator(300)\n    }\n  }\n\n  fun dismiss() {\n    popup.dismiss()\n  }\n\n  public fun show() {\n    help.show(x, 0)\n  }\n\n  fun getPopMenu(): PopupMenu {\n    return popup\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/menu/GroupPopMenu.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.view.menu\n\nimport android.annotation.SuppressLint\nimport android.text.Html\nimport android.text.Spanned\nimport android.view.Gravity\nimport android.view.MenuInflater\nimport android.view.View\nimport android.widget.Button\nimport androidx.appcompat.view.menu.MenuPopupHelper\nimport androidx.appcompat.widget.PopupMenu\nimport androidx.fragment.app.FragmentActivity\nimport androidx.preference.PreferenceManager\nimport com.arialyy.frame.router.Routerfit\nimport com.arialyy.frame.util.ReflectionUtil\nimport com.arialyy.frame.util.ResUtil\nimport com.keepassdroid.database.PwGroupV4\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseApp\nimport com.lyy.keepassa.router.DialogRouter\nimport com.lyy.keepassa.util.HitUtil\nimport com.lyy.keepassa.util.KpaUtil\nimport com.lyy.keepassa.util.VibratorUtil\nimport com.lyy.keepassa.view.dialog.OnMsgBtClickListener\nimport com.lyy.keepassa.view.dir.ChooseGroupActivity\n\n/**\n * 群组长按菜单\n * @param x 偏移量\n */\n@SuppressLint(\"RestrictedApi\")\nclass GroupPopMenu(\n  private val context: FragmentActivity,\n  view: View,\n  private val pwGroup: PwGroupV4,\n  private val x: Int,\n  private val isInRecycleBin: Boolean = false\n) : IPopMenu {\n  private val popup: PopupMenu = PopupMenu(context, view, Gravity.START)\n  private val help: MenuPopupHelper\n\n  init {\n    val inflater: MenuInflater = popup.menuInflater\n    inflater.inflate(R.menu.pop_group_summary, popup.menu)\n\n    popup.menu.findItem(R.id.undo)\n      .isVisible = isInRecycleBin\n    // 回收站不允许删除\n    popup.menu.findItem(R.id.del)\n      .isVisible = !(BaseApp.KDB!!.pm.recycleBin != null && BaseApp.KDB!!.pm.recycleBin == pwGroup)\n\n    // 以下代码为强制显示icon\n    val mPopup = ReflectionUtil.getField(PopupMenu::class.java, \"mPopup\")\n    mPopup.isAccessible = true\n    help = mPopup.get(popup) as MenuPopupHelper\n    help.setForceShowIcon(true)\n    popup.setOnMenuItemClickListener { item ->\n      when (item.itemId) {\n        R.id.del -> {\n          delGroup()\n        }\n        R.id.edit -> {\n          Routerfit.create(DialogRouter::class.java).showModifyGroupDialog(pwGroup)\n        }\n        R.id.undo, R.id.move -> {\n          ChooseGroupActivity.moveGroup(context, pwGroup.id)\n        }\n      }\n      popup.dismiss()\n\n      true\n    }\n  }\n\n  /**\n   * 删除群组\n   */\n  @SuppressLint(\"StringFormatMatches\")\n  private fun delGroup() {\n    // 是否直接删除条目\n    val deleteDirectly = PreferenceManager.getDefaultSharedPreferences(BaseApp.APP)\n      .getBoolean(context.getString(R.string.set_key_delete_no_recycle_bin), false)\n\n    if (deleteDirectly) {\n      handleDelGroup()\n      return\n    }\n    val msg: Spanned = if (BaseApp.isV4) {\n      if (isInRecycleBin) {\n        Html.fromHtml(context.getString(R.string.hint_del_group_no_recycle, pwGroup.name))\n      } else {\n        Html.fromHtml(\n          context.getString(R.string.hint_del_group, pwGroup.name, pwGroup.name)\n        )\n      }\n    } else {\n      Html.fromHtml(context.getString(R.string.hint_del_group_no_recycle, pwGroup.name))\n    }\n\n    Routerfit.create(DialogRouter::class.java).showMsgDialog(\n      msgTitle = ResUtil.getString(R.string.del_group),\n      msgContent = msg,\n      btnClickListener = object : OnMsgBtClickListener {\n        override fun onCover(v: Button) {\n        }\n\n        override fun onEnter(v: Button) {\n          handleDelGroup()\n        }\n\n        override fun onCancel(v: Button) {\n        }\n      }\n    )\n  }\n\n  /**\n   * 处理删除群组\n   */\n  private fun handleDelGroup() {\n    KpaUtil.kdbHandlerService.deleteGroup(pwGroup) {\n      HitUtil.toaskShort(\n        \"${context.getString(R.string.del_group)}${context.getString(R.string.success)}\"\n      )\n      VibratorUtil.vibrator(300)\n    }\n  }\n\n  public fun show() {\n    help.show(x, 0)\n  }\n\n  fun getPopMenu(): PopupMenu {\n    return popup\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/menu/IPopMenu.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.view.menu\n\ninterface IPopMenu {\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/search/AutoFillEntrySearchActivity.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.view.search\n\nimport android.annotation.SuppressLint\nimport android.annotation.TargetApi\nimport android.app.Activity\nimport android.app.ActivityOptions\nimport android.app.PendingIntent\nimport android.app.PendingIntent.FLAG_IMMUTABLE\nimport android.app.assist.AssistStructure\nimport android.content.Context\nimport android.content.Intent\nimport android.content.IntentSender\nimport android.os.Build\nimport android.os.Bundle\nimport android.text.Html\nimport android.view.View\nimport android.view.autofill.AutofillManager\nimport android.widget.Button\nimport androidx.appcompat.widget.SearchView.OnQueryTextListener\nimport androidx.lifecycle.ViewModelProvider\nimport androidx.lifecycle.lifecycleScope\nimport androidx.recyclerview.widget.LinearLayoutManager\nimport com.arialyy.frame.router.Routerfit\nimport com.keepassdroid.database.PwEntry\nimport com.keepassdroid.database.PwEntryV4\nimport com.keepassdroid.database.PwGroup\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseActivity\nimport com.lyy.keepassa.databinding.ActivityAutoFillEntrySearchBinding\nimport com.lyy.keepassa.entity.AutoFillParam\nimport com.lyy.keepassa.event.EntryState.CREATE\nimport com.lyy.keepassa.router.DialogRouter\nimport com.lyy.keepassa.service.autofill.W3cHints\nimport com.lyy.keepassa.util.HitUtil\nimport com.lyy.keepassa.util.KeepassAUtil\nimport com.lyy.keepassa.util.KpaUtil\nimport com.lyy.keepassa.util.cloud.DbSynUtil\nimport com.lyy.keepassa.util.doOnItemClickListener\nimport com.lyy.keepassa.view.create.entry.CreateEntryActivity\nimport com.lyy.keepassa.view.create.entry.CreateEnum\nimport com.lyy.keepassa.view.dialog.OnMsgBtClickListener\nimport com.lyy.keepassa.view.launcher.LauncherActivity\nimport kotlinx.coroutines.flow.collectLatest\nimport kotlinx.coroutines.launch\nimport timber.log.Timber\n\n/**\n * 自动填充条目找不到时的选择页面\n */\n@SuppressLint(\"NotifyDataSetChanged\")\nclass AutoFillEntrySearchActivity : BaseActivity<ActivityAutoFillEntrySearchBinding>() {\n\n  private lateinit var module: SearchModule\n  private lateinit var adapter: SearchAdapter\n  private var curEntry: PwEntry? = null\n\n  companion object {\n\n    /**\n     * 条目id\n     */\n    const val EXTRA_ENTRY_ID = \"EXTRA_ENTRY_ID\"\n\n    internal fun createSearchIntent(\n      context: Context,\n      param: AutoFillParam,\n      structure: AssistStructure?\n    ): Intent {\n      val sIntent =\n        Intent(context, AutoFillEntrySearchActivity::class.java).apply {\n          val b = Bundle()\n          b.putParcelable(LauncherActivity.KEY_AUTO_FILL_PARAM, param)\n          if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {\n            b.putParcelable(\n              AutofillManager.EXTRA_ASSIST_STRUCTURE,\n              structure\n            )\n          }\n          putExtras(b)\n        }\n      return sIntent\n    }\n\n    /**\n     * 进入搜索页\n     */\n    internal fun createSearchPending(\n      context: Context,\n      apkPkgName: String,\n      structure: AssistStructure\n    ): PendingIntent {\n      val intent = Intent(context, AutoFillEntrySearchActivity::class.java).also {\n        it.putExtra(LauncherActivity.KEY_AUTO_FILL_PARAM, AutoFillParam(apkPkgName = apkPkgName))\n\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {\n          it.putExtra(AutofillManager.EXTRA_ASSIST_STRUCTURE, structure)\n        }\n        it.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK\n      }\n      return PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE)\n    }\n\n    /**\n     * 没有匹配数据时，启动搜索界面\n     */\n    internal fun getSearchIntentSender(\n      context: Context,\n      apkPackageName: String,\n      structure: AssistStructure\n    ): IntentSender {\n      val intent = Intent(context, AutoFillEntrySearchActivity::class.java).also {\n        it.putExtra(\n          LauncherActivity.KEY_AUTO_FILL_PARAM,\n          AutoFillParam(apkPkgName = apkPackageName)\n        )\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {\n          it.putExtra(AutofillManager.EXTRA_ASSIST_STRUCTURE, structure)\n        }\n        it.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK\n      }\n      return PendingIntent.getActivity(\n        context,\n        1,\n        intent,\n        PendingIntent.FLAG_CANCEL_CURRENT or FLAG_IMMUTABLE\n      )\n        .intentSender\n    }\n  }\n\n  override fun setLayoutId(): Int {\n    return R.layout.activity_auto_fill_entry_search\n  }\n\n  override fun initData(savedInstanceState: Bundle?) {\n    super.initData(savedInstanceState)\n    module = ViewModelProvider(this)[SearchModule::class.java]\n    module.autoFillParam = intent.getParcelableExtra(LauncherActivity.KEY_AUTO_FILL_PARAM)\n    initSearchView()\n    initList()\n    listenerGetSearchData()\n    listenerEntryStateChange()\n  }\n\n  private fun initSearchView() {\n    binding.search.requestFocusFromTouch()\n    binding.search.setIconifiedByDefault(true)\n    binding.search.isIconified = false\n    binding.search.setOnQueryTextListener(object : OnQueryTextListener {\n      /**\n       * 当点击搜索按钮时触发该方法\n       */\n      override fun onQueryTextSubmit(query: String?): Boolean {\n        if (query != null) {\n          searchData(query)\n        }\n        return true\n      }\n\n      /**\n       * 当搜索内容改变时触发该方法\n       */\n      override fun onQueryTextChange(newText: String?): Boolean {\n        if (newText != null) {\n          searchData(newText)\n        }\n        return true\n      }\n    })\n\n    binding.exFab.setOnClickListener {\n      startActivity(\n        Intent(this, CreateEntryActivity::class.java).apply {\n          putExtra(CreateEntryActivity.KEY_TYPE, CreateEnum.CREATE)\n        },\n        ActivityOptions.makeSceneTransitionAnimation(this)\n          .toBundle()\n      )\n    }\n  }\n\n  /**\n   * listener the entry status change, there are three states: create, delete, and modify.\n   */\n  private fun listenerEntryStateChange() {\n    lifecycleScope.launch {\n      KpaUtil.kdbHandlerService.entryStateChangeFlow.collectLatest {\n        if (it.pwEntryV4 == null) {\n          return@collectLatest\n        }\n        when (it.state) {\n          CREATE -> {\n            val lastIndex = module.listData.size\n            module.listData.add(KeepassAUtil.instance.convertPwEntry2Item(it.pwEntryV4))\n            adapter.notifyItemInserted(lastIndex)\n            binding.noEntryLayout.visibility = View.GONE\n            if (module.isFormAutoFill()) {\n              relevanceEntry(it.pwEntryV4)\n            }\n          }\n          else -> {\n            Timber.d(\"ignore other status\")\n          }\n        }\n      }\n    }\n  }\n\n  private fun listenerGetSearchData() {\n    lifecycleScope.launch {\n      module.searchDataFlow.collectLatest { list ->\n        if (list != null) {\n          binding.noEntryLayout.visibility = View.GONE\n          adapter.notifyDataSetChanged()\n          return@collectLatest\n        }\n        adapter.notifyDataSetChanged()\n        binding.noEntryLayout.visibility = View.VISIBLE\n      }\n    }\n  }\n\n  /**\n   * 搜索数据\n   */\n  private fun searchData(query: String) {\n    if (query.isEmpty()) {\n      module.listData.clear()\n      adapter.notifyDataSetChanged()\n      binding.noEntryLayout.visibility = View.VISIBLE\n      return\n    }\n    module.searchEntry(query, module.isFormAutoFill())\n  }\n\n  /**\n   * 初始化列表\n   */\n  private fun initList() {\n    adapter = SearchAdapter(this, module.listData, object : DelListener {\n      override fun onDel(v: View, position: Int) {\n        val item = module.listData[position]\n        module.delHistoryRecord(item.title.toString()) {\n          module.listData.remove(item)\n          adapter.notifyDataSetChanged()\n        }\n      }\n    })\n    binding.list.layoutManager = LinearLayoutManager(this)\n    binding.list.adapter = adapter\n\n    binding.list.doOnItemClickListener { _, position, _ ->\n      if (module.getApkPkgName().isNullOrEmpty()) {\n        return@doOnItemClickListener\n      }\n      val item = module.listData[position]\n      /*\n        if is From autofill and that is group, No operation\n       */\n      if (module.isFormAutoFill() && item.obj is PwGroup) {\n        return@doOnItemClickListener\n      }\n      val entry = item.obj as PwEntry\n      // if is from browser, that entry will be ignore\n      if (W3cHints.isBrowser(module.getApkPkgName()!!)) {\n        callbackAutoFillService(entry)\n        return@doOnItemClickListener\n      }\n\n      val msg =\n        Html.fromHtml(getString(R.string.hint_save_auto_fill, module.getApkPkgName(), entry.title))\n      Routerfit.create(DialogRouter::class.java).showMsgDialog(\n        msgContent = msg,\n        btnClickListener = object : OnMsgBtClickListener {\n          override fun onCover(v: Button) {\n          }\n\n          override fun onEnter(v: Button) {\n            if (entry is PwEntryV4) {\n              // 保存记录\n              relevanceEntry(entry)\n              return\n            }\n            callbackAutoFillService(entry)\n          }\n\n          override fun onCancel(v: Button) {\n            callbackAutoFillService(entry)\n          }\n        }\n      )\n    }\n  }\n\n  /**\n   * 关联记录\n   */\n  private fun relevanceEntry(pwEntry: PwEntryV4) {\n    curEntry = pwEntry\n\n    module.relevanceEntry(pwEntry, module.getApkPkgName()!!) {\n      if (it != DbSynUtil.STATE_SUCCEED) {\n        HitUtil.toaskShort(\"${getString(R.string.relevance_db)}${getString(R.string.fail)}\")\n      } else {\n        HitUtil.toaskShort(\"${getString(R.string.relevance_db)}${getString(R.string.success)}\")\n      }\n      callbackAutoFillService(pwEntry)\n    }\n  }\n\n  /**\n   * 填充数据到自动填充服务\n   */\n  @TargetApi(Build.VERSION_CODES.O)\n  private fun callbackAutoFillService(pwEntry: PwEntry) {\n    val data = Intent().apply {\n      putExtra(EXTRA_ENTRY_ID, pwEntry.uuid)\n    }\n    setResult(Activity.RESULT_OK, data)\n    if (!module.isFormAutoFill()) {\n      finishAfterTransition()\n      return\n    }\n    finish()\n  }\n\n  override fun onBackPressed() {\n    super.onBackPressed()\n    setResult(Activity.RESULT_CANCELED)\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/search/CommonSearchActivity.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.view.search\n\nimport android.annotation.SuppressLint\nimport android.annotation.TargetApi\nimport android.app.Activity\nimport android.app.ActivityOptions\nimport android.content.Intent\nimport android.os.Build\nimport android.os.Bundle\nimport android.text.Html\nimport android.view.View\nimport android.widget.Button\nimport androidx.appcompat.widget.SearchView.OnQueryTextListener\nimport androidx.core.app.ActivityOptionsCompat\nimport androidx.lifecycle.ViewModelProvider\nimport androidx.lifecycle.lifecycleScope\nimport androidx.recyclerview.widget.LinearLayoutManager\nimport com.alibaba.android.arouter.facade.annotation.Autowired\nimport com.alibaba.android.arouter.facade.annotation.Route\nimport com.arialyy.frame.router.Routerfit\nimport com.keepassdroid.database.PwEntry\nimport com.keepassdroid.database.PwEntryV4\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseActivity\nimport com.lyy.keepassa.databinding.ActivityAutoFillEntrySearchBinding\nimport com.lyy.keepassa.event.EntryState.CREATE\nimport com.lyy.keepassa.router.ActivityRouter\nimport com.lyy.keepassa.router.DialogRouter\nimport com.lyy.keepassa.service.autofill.W3cHints\nimport com.lyy.keepassa.util.HitUtil\nimport com.lyy.keepassa.util.KeepassAUtil\nimport com.lyy.keepassa.util.KpaUtil\nimport com.lyy.keepassa.util.cloud.DbSynUtil\nimport com.lyy.keepassa.util.doOnItemClickListener\nimport com.lyy.keepassa.view.dialog.OnMsgBtClickListener\nimport kotlinx.coroutines.flow.MutableSharedFlow\nimport kotlinx.coroutines.flow.collectLatest\nimport kotlinx.coroutines.launch\nimport timber.log.Timber\n\n/**\n * 自动填充条目找不到时的选择页面\n */\n@SuppressLint(\"NotifyDataSetChanged\")\n@Route(path = \"/search/common\")\ninternal class CommonSearchActivity : BaseActivity<ActivityAutoFillEntrySearchBinding>() {\n\n  companion object {\n    val searchFlow = MutableSharedFlow<PwEntry>()\n  }\n\n  private lateinit var module: SearchModule\n  private lateinit var adapter: SearchAdapter\n  private var curEntry: PwEntry? = null\n\n  /**\n   * 第三方应用包名\n   */\n  @Autowired(name = \"apkPkgName\")\n  @JvmField\n  var apkPkgName: String? = null\n\n  /**\n   * only search, not  Association\n   */\n  @Autowired(name = \"onlySearch\")\n  @JvmField\n  var onlySearch: Boolean = false\n\n  override fun setLayoutId(): Int {\n    return R.layout.activity_auto_fill_entry_search\n  }\n\n  override fun initData(savedInstanceState: Bundle?) {\n    super.initData(savedInstanceState)\n    module = ViewModelProvider(this).get(SearchModule::class.java)\n    binding.search.requestFocusFromTouch()\n    binding.search.setIconifiedByDefault(true)\n    binding.search.isIconified = false\n    binding.search.setOnQueryTextListener(object : OnQueryTextListener {\n      /**\n       * 当点击搜索按钮时触发该方法\n       */\n      override fun onQueryTextSubmit(query: String?): Boolean {\n        if (query != null) {\n          searchData(query)\n        }\n\n        return true\n      }\n\n      /**\n       * 当搜索内容改变时触发该方法\n       */\n      override fun onQueryTextChange(newText: String?): Boolean {\n        if (newText != null) {\n          searchData(newText)\n        }\n        return true\n      }\n    })\n\n    binding.exFab.setOnClickListener {\n      val op =  ActivityOptionsCompat.makeSceneTransitionAnimation(this)\n      Routerfit.create(ActivityRouter::class.java).toCreateEntryActivity(null, op)\n    }\n\n    initList()\n    listenerGetSearchData()\n    listenerEntryStateChange()\n  }\n\n  /**\n   * listener the entry status change, there are three states: create, delete, and modify.\n   */\n  private fun listenerEntryStateChange() {\n    lifecycleScope.launch {\n      KpaUtil.kdbHandlerService.entryStateChangeFlow.collectLatest {\n        if (it.pwEntryV4 == null) {\n          return@collectLatest\n        }\n        when (it.state) {\n          CREATE -> {\n            val lastIndex = module.listData.size\n            module.listData.add(KeepassAUtil.instance.convertPwEntry2Item(it.pwEntryV4))\n            adapter.notifyItemInserted(lastIndex)\n            binding.noEntryLayout.visibility = View.GONE\n          }\n          else -> {\n            Timber.d(\"ignore other status\")\n          }\n        }\n      }\n    }\n  }\n\n  private fun listenerGetSearchData() {\n    lifecycleScope.launch {\n      module.searchDataFlow.collectLatest { list ->\n        if (list != null) {\n          binding.noEntryLayout.visibility = View.GONE\n          adapter.notifyDataSetChanged()\n          return@collectLatest\n        }\n        adapter.notifyDataSetChanged()\n        binding.noEntryLayout.visibility = View.VISIBLE\n      }\n    }\n  }\n\n  /**\n   * 搜索数据\n   */\n  private fun searchData(query: String) {\n    if (query.isEmpty()) {\n      module.listData.clear()\n      adapter.notifyDataSetChanged()\n      binding.noEntryLayout.visibility = View.VISIBLE\n      return\n    }\n    module.searchEntry1(query, false)\n  }\n\n  /**\n   * 初始化列表\n   */\n  private fun initList() {\n    adapter = SearchAdapter(this, module.listData, object : DelListener {\n      override fun onDel(v: View, position: Int) {\n        val item = module.listData[position]\n        module.delHistoryRecord(item.title.toString()) {\n          module.listData.remove(item)\n          adapter.notifyDataSetChanged()\n        }\n      }\n    })\n    binding.list.layoutManager = LinearLayoutManager(this)\n    binding.list.adapter = adapter\n\n    binding.list.doOnItemClickListener { _, position, _ ->\n      if (apkPkgName.isNullOrEmpty()) {\n        return@doOnItemClickListener\n      }\n      val item = module.listData[position]\n      val entry = item.obj as PwEntry\n      // if is from browser, that entry will be ignore\n      if (W3cHints.isBrowser(apkPkgName!!)) {\n        callbackAutoFillService(entry)\n        return@doOnItemClickListener\n      }\n\n      val msg = Html.fromHtml(getString(R.string.hint_save_auto_fill, apkPkgName, entry.title))\n      Routerfit.create(DialogRouter::class.java).showMsgDialog(\n        msgContent = msg,\n        btnClickListener = object : OnMsgBtClickListener {\n          override fun onCover(v: Button) {\n          }\n\n          override fun onEnter(v: Button) {\n            if (entry is PwEntryV4 && !onlySearch) {\n              // 保存记录\n              relevanceEntry(entry)\n            } else {\n              callbackAutoFillService(entry)\n            }\n          }\n\n          override fun onCancel(v: Button) {\n            callbackAutoFillService(entry)\n          }\n        }\n      )\n    }\n  }\n\n  /**\n   * 关联记录\n   */\n  private fun relevanceEntry(pwEntry: PwEntryV4) {\n    curEntry = pwEntry\n\n    module.relevanceEntry(pwEntry, apkPkgName!!) {\n      if (it != DbSynUtil.STATE_SUCCEED) {\n        HitUtil.toaskShort(\"${getString(R.string.relevance_db)}${getString(R.string.fail)}\")\n      }\n      HitUtil.toaskShort(\"${getString(R.string.relevance_db)}${getString(R.string.success)}\")\n      callbackAutoFillService(pwEntry)\n    }\n  }\n\n  /**\n   * 填充数据到自动填充服务\n   */\n  @TargetApi(Build.VERSION_CODES.O)\n  private fun callbackAutoFillService(pwEntry: PwEntry) {\n    lifecycleScope.launch {\n      searchFlow.emit(pwEntry)\n    }\n    finish()\n  }\n\n  override fun onBackPressed() {\n    super.onBackPressed()\n    setResult(Activity.RESULT_CANCELED)\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/search/DelListener.kt",
    "content": "package com.lyy.keepassa.view.search\n\nimport android.view.View\nimport com.lyy.keepassa.entity.SimpleItemEntity\n\n/**\n * @Author laoyuyu\n * @Description\n * @Date 11:19 上午 2022/7/11\n **/\ninterface DelListener {\n  fun onDel(v: View, position: Int)\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/search/SearchAdapter.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.view.search\n\nimport android.content.Context\nimport android.text.SpannableStringBuilder\nimport android.view.View\nimport android.widget.TextView\nimport androidx.appcompat.widget.AppCompatImageView\nimport com.arialyy.frame.util.ResUtil\nimport com.arialyy.frame.util.StringUtil\nimport com.arialyy.frame.util.adapter.AbsHolder\nimport com.arialyy.frame.util.adapter.AbsRVAdapter\nimport com.keepassdroid.database.PwEntry\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.entity.SimpleItemEntity\nimport com.lyy.keepassa.util.IconUtil\nimport com.lyy.keepassa.view.search.SearchAdapter.BaseHolder\n\n/**\n * 搜索列表适配器\n */\nclass SearchAdapter(\n  context: Context,\n  val data: MutableList<SimpleItemEntity>,\n  val delListener: DelListener\n) : AbsRVAdapter<SimpleItemEntity, BaseHolder>(context, data) {\n  internal var queryString = \"\"\n\n  companion object {\n    val ITEM_TYPE_RECORD = 1\n    val ITEM_TYPE_ENTRY = 2\n    val ITEM_TYPE_GROUP = 3\n  }\n\n  override fun getViewHolder(\n    convertView: View,\n    viewType: Int\n  ): BaseHolder {\n    return when (viewType) {\n      ITEM_TYPE_RECORD, ITEM_TYPE_GROUP -> RecordHolder(convertView)\n      ITEM_TYPE_ENTRY -> SearchHolder(convertView)\n      else -> RecordHolder(convertView)\n    }\n  }\n\n  override fun setLayoutId(type: Int): Int {\n    return when (type) {\n      ITEM_TYPE_RECORD, ITEM_TYPE_GROUP -> R.layout.item_search_record\n      ITEM_TYPE_ENTRY -> R.layout.item_search_result\n      else -> R.layout.item_search_record\n    }\n  }\n\n  override fun bindData(\n    holder: BaseHolder,\n    position: Int,\n    item: SimpleItemEntity\n  ) {\n\n    when (item.type) {\n      // 历史记录\n      ITEM_TYPE_RECORD -> {\n        holder.text.text = item.title\n        holder as RecordHolder\n        holder.del.setOnClickListener {\n          delListener.onDel(it, position)\n        }\n        holder.del.visibility = View.VISIBLE\n        holder.icon.setImageResource(R.drawable.ic_history)\n      }\n      // 群组\n      ITEM_TYPE_GROUP -> {\n        highLightText(holder.text, item.title.toString())\n        (holder as RecordHolder).del.visibility = View.GONE\n        holder.icon.setImageDrawable(\n          ResUtil.getSvgIcon(\n            R.drawable.ic_folder_24px,\n            R.color.colorPrimary\n          )\n        )\n      }\n\n      // 条目\n      ITEM_TYPE_ENTRY -> {\n        highLightText(holder.text, item.title.toString())\n        highLightText((holder as SearchHolder).des, item.subTitle.toString())\n        IconUtil.setEntryIcon(item.obj as PwEntry, holder.icon)\n      }\n    }\n  }\n\n  private fun highLightText(\n    tv: TextView,\n    str: String\n  ) {\n    if (queryString.isNotEmpty() && str.contains(queryString, ignoreCase = true)) {\n      val temp: SpannableStringBuilder? = StringUtil.highLightStr(\n        str,\n        queryString,\n        ResUtil.getColor(android.R.color.holo_red_light),\n        true\n      )\n\n      if (temp == null) {\n        tv.text = str\n        return\n      }\n      tv.text = temp\n      return\n    }\n    tv.text = str\n  }\n\n  override fun getItemViewType(position: Int): Int {\n    return data[position].type\n  }\n\n  open class BaseHolder(view: View) : AbsHolder(view) {\n    val icon: AppCompatImageView = view.findViewById(R.id.icon)\n    val text: TextView = view.findViewById(R.id.text)\n  }\n\n  private class RecordHolder(view: View) : BaseHolder(view) {\n    val del: AppCompatImageView = view.findViewById(R.id.del)\n  }\n\n  private class SearchHolder(view: View) : BaseHolder(view) {\n    val des: TextView = view.findViewById(R.id.des)\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/search/SearchDialog.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.view.search\n\nimport android.annotation.SuppressLint\nimport android.graphics.drawable.ColorDrawable\nimport android.os.Bundle\nimport android.view.LayoutInflater\nimport android.view.View\nimport android.view.ViewGroup\nimport androidx.appcompat.widget.SearchView.OnQueryTextListener\nimport androidx.fragment.app.DialogFragment\nimport androidx.fragment.app.FragmentActivity\nimport androidx.lifecycle.ViewModelProvider\nimport androidx.lifecycle.lifecycleScope\nimport androidx.recyclerview.widget.LinearLayoutManager\nimport com.arialyy.frame.util.ResUtil\nimport com.keepassdroid.database.PwDataInf\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.R.color\nimport com.lyy.keepassa.base.BaseDialog\nimport com.lyy.keepassa.databinding.DialogSearchBinding\nimport com.lyy.keepassa.entity.SimpleItemEntity\nimport com.lyy.keepassa.util.KeepassAUtil\nimport com.lyy.keepassa.util.doOnItemClickListener\nimport kotlinx.coroutines.flow.collectLatest\nimport kotlinx.coroutines.launch\n\n/**\n * 搜索\n */\nclass SearchDialog : BaseDialog<DialogSearchBinding>() {\n\n  private lateinit var module: SearchModule\n  private lateinit var adapter: SearchAdapter\n  private val listData = mutableListOf<SimpleItemEntity>()\n\n  init {\n    setStyle(DialogFragment.STYLE_NORMAL, R.style.FullScreenDialog)\n  }\n\n  override fun setLayoutId(): Int {\n    return R.layout.dialog_search\n  }\n\n  override fun onCreateView(\n    inflater: LayoutInflater,\n    container: ViewGroup?,\n    savedInstanceState: Bundle?\n  ): View? {\n    dialog!!.window!!.setBackgroundDrawable(ColorDrawable(ResUtil.getColor(color.mask)))\n    return super.onCreateView(inflater, container, savedInstanceState)\n  }\n\n  override fun initData() {\n    super.initData()\n    module = ViewModelProvider(requireActivity()).get(SearchModule::class.java)\n    binding.bg.setOnClickListener {\n      dismiss()\n    }\n    initList()\n    initSearchWidget()\n    listenerGetSearchData()\n    module.getSearchRecord()\n  }\n\n  private fun initSearchWidget() {\n    binding.search.requestFocusFromTouch()\n    binding.search.setIconifiedByDefault(true)\n    binding.search.isIconified = false\n    binding.search.setOnQueryTextListener(object : OnQueryTextListener {\n      /**\n       * 当点击搜索按钮时触发该方法\n       */\n      override fun onQueryTextSubmit(query: String?): Boolean {\n        if (!query.isNullOrBlank()) {\n          adapter.queryString = query\n          searchData(query)\n        }\n\n        return true\n      }\n\n      /**\n       * 当搜索内容改变时触发该方法\n       */\n      override fun onQueryTextChange(newText: String?): Boolean {\n        if (!newText.isNullOrBlank()) {\n          adapter.queryString = newText\n          searchData(newText)\n        }\n        return true\n      }\n    })\n  }\n\n  @SuppressLint(\"NotifyDataSetChanged\")\n  private fun listenerGetSearchData() {\n    lifecycleScope.launch {\n      module.searchDataFlow.collectLatest { list ->\n        listData.clear()\n        if (!list.isNullOrEmpty()) {\n          listData.addAll(list)\n        }\n        adapter.notifyDataSetChanged()\n      }\n    }\n  }\n\n  /**\n   * 搜索数据\n   */\n  private fun searchData(query: String) {\n    if (query.isEmpty()) {\n      module.getSearchRecord()\n      return\n    }\n    module.searchEntry(query)\n  }\n\n  /**\n   * 初始化列表\n   */\n  private fun initList() {\n    adapter = SearchAdapter(requireContext(), listData, object : DelListener {\n      @SuppressLint(\"NotifyDataSetChanged\")\n      override fun onDel(v: View, position: Int) {\n        val item = listData[position]\n        module.delHistoryRecord(item.title.toString()) {\n          listData.remove(item)\n          adapter.notifyDataSetChanged()\n        }\n      }\n    })\n    binding.list.layoutManager = LinearLayoutManager(context)\n    binding.list.adapter = adapter\n    binding.list.doOnItemClickListener { _, position, _ ->\n      val item = listData[position]\n      if (item.type == SearchAdapter.ITEM_TYPE_RECORD) {\n        // 处理历史记录，直接使用该历史搜索数据，输入框设置该历史记录\n        binding.search.setQuery(item.title, true)\n        return@doOnItemClickListener\n      }\n      // 处理搜索结果\n      module.saveSearchRecord(item.title.toString())\n      KeepassAUtil.instance.turnEntryDetail(context as FragmentActivity, item.obj as PwDataInf)\n      dismiss()\n    }\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/search/SearchModule.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.view.search\n\nimport androidx.lifecycle.viewModelScope\nimport com.keepassdroid.database.PwEntry\nimport com.keepassdroid.database.PwEntryV4\nimport com.keepassdroid.database.PwGroup\nimport com.keepassdroid.database.SearchParametersV4\nimport com.keepassdroid.database.security.ProtectedString\nimport com.lyy.keepassa.base.BaseApp\nimport com.lyy.keepassa.base.BaseModule\nimport com.lyy.keepassa.entity.AutoFillParam\nimport com.lyy.keepassa.entity.SearchRecord\nimport com.lyy.keepassa.entity.SimpleItemEntity\nimport com.lyy.keepassa.util.KeepassAUtil\nimport com.lyy.keepassa.util.KpaUtil\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.flow.MutableSharedFlow\nimport kotlinx.coroutines.launch\nimport kotlinx.coroutines.withContext\n\nclass SearchModule : BaseModule() {\n\n  val listData = mutableListOf<SimpleItemEntity>()\n  val searchDataFlow = MutableSharedFlow<MutableList<SimpleItemEntity>?>()\n  var autoFillParam: AutoFillParam? = null\n\n\n  fun isFormAutoFill() = autoFillParam != null\n\n  fun getApkPkgName() = autoFillParam?.apkPkgName\n\n  /**\n   * 将条目和包名进行关联\n   */\n  fun relevanceEntry(\n    pwEntry: PwEntryV4,\n    apkPkgName: String,\n    callback: (Int) -> Unit\n  ) {\n    // 关联数据\n    var nextId = 1\n    for (i in 1 until 100) {\n      if (pwEntry.strings[\"KP2A_URL_$i\"] != null) {\n        continue\n      }\n      nextId = i\n      break\n    }\n    pwEntry.strings[\"KP2A_URL_$nextId\"] = ProtectedString(false, \"androidapp://$apkPkgName\")\n    KpaUtil.kdbHandlerService.saveDbByForeground(callback = callback)\n  }\n\n  /**\n   * @param searchGroup true 仅搜索条目\n   */\n  fun searchEntry1(query: String, searchGroup: Boolean) {\n    listData.clear()\n    val sp = SearchParametersV4()\n    val entryList = ArrayList<PwEntry>()\n    val groupList = ArrayList<PwGroup>()\n    sp.searchString = query\n    BaseApp.KDB!!.pm.rootGroup.searchEntries(sp, entryList)\n\n    if (searchGroup) {\n      searchGroup(query, groupList)\n      // 组合群组信息\n      for (group in groupList) {\n        val item = KeepassAUtil.instance.convertPwGroup2Item(group)\n        item.type = SearchAdapter.ITEM_TYPE_GROUP\n        listData.add(item)\n      }\n    }\n\n    // 组合条目信息\n    for (entry in entryList) {\n      val item = KeepassAUtil.instance.convertPwEntry2Item(entry)\n      item.type = SearchAdapter.ITEM_TYPE_ENTRY\n      listData.add(item)\n    }\n\n    viewModelScope.launch {\n      searchDataFlow.emit(listData)\n    }\n  }\n\n  /**\n   * 搜索项目\n   */\n  fun searchEntry(query: String, isFromAutoFill: Boolean = false) {\n    listData.clear()\n    val sp = SearchParametersV4()\n    val entryList = ArrayList<PwEntry>()\n    val groupList = ArrayList<PwGroup>()\n    sp.searchString = query\n    BaseApp.KDB!!.pm.rootGroup.searchEntries(sp, entryList)\n\n    /*\n      自动填充不搜索群组\n     */\n    if (isFromAutoFill) {\n      searchGroup(query, groupList)\n\n      if (entryList.isEmpty() && groupList.isEmpty()) {\n        viewModelScope.launch {\n          searchDataFlow.emit(null)\n        }\n        return\n      }\n    }\n\n    // 组合群组信息\n    for (group in groupList) {\n      val item = KeepassAUtil.instance.convertPwGroup2Item(group)\n      item.type = SearchAdapter.ITEM_TYPE_GROUP\n      listData.add(item)\n    }\n\n    // 组合条目信息\n    for (entry in entryList) {\n      val item = KeepassAUtil.instance.convertPwEntry2Item(entry)\n      item.type = SearchAdapter.ITEM_TYPE_ENTRY\n      listData.add(item)\n    }\n\n    viewModelScope.launch {\n      searchDataFlow.emit(listData)\n    }\n  }\n\n  /**\n   * 搜索群组\n   */\n  private fun searchGroup(\n    query: String,\n    listStorage: ArrayList<PwGroup>\n  ) {\n    for ((_, group) in BaseApp.KDB!!.pm.groups) {\n      if (group == BaseApp.KDB!!.pm.recycleBin) {\n        continue\n      }\n      if (group.name.contains(query, true)) {\n        listStorage.add(group)\n      }\n    }\n  }\n\n  /**\n   * 获取搜索记录\n   */\n  fun getSearchRecord() {\n    listData.clear()\n    viewModelScope.launch {\n      withContext(Dispatchers.IO) {\n        val dao = BaseApp.appDatabase.searchRecordDao()\n        val temp = dao.getSearchRecord()\n        for (record in temp) {\n          listData.add(convertRecord2Item(record))\n        }\n      }\n      searchDataFlow.emit(listData)\n    }\n  }\n\n  /**\n   * 转换记录为列表实体\n   */\n  private fun convertRecord2Item(record: SearchRecord): SimpleItemEntity {\n    val item = SimpleItemEntity()\n    item.time = record.time\n    item.title = record.title\n    item.type = SearchAdapter.ITEM_TYPE_RECORD\n    return item\n  }\n\n  /**\n   * 保存搜索数据\n   * @param title 搜索的数据\n   */\n  fun saveSearchRecord(title: String) {\n    KpaUtil.scope.launch(Dispatchers.IO) {\n      val dao = BaseApp.appDatabase.searchRecordDao()\n      var record = dao.getRecord(title)\n      if (record != null) {\n        record.time = System.currentTimeMillis()\n        dao.updateRecord(record)\n        return@launch\n      }\n      record = SearchRecord()\n      record.title = title\n      record.time = System.currentTimeMillis()\n      dao.saveRecord(record)\n    }\n  }\n\n  /**\n   * 删除搜索历史\n   */\n  fun delHistoryRecord(title: String, callback: (Boolean) -> Unit) {\n    viewModelScope.launch {\n      val b = withContext(Dispatchers.IO) {\n        val dao = BaseApp.appDatabase.searchRecordDao()\n        val record = dao.getRecord(title)\n        if (record != null) {\n          dao.delRecord(record)\n        }\n        true\n      }\n      callback.invoke(b)\n    }\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/setting/AppSettingFragment.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.view.setting\n\nimport android.app.Activity\nimport android.content.Context\nimport android.content.Intent\nimport android.net.Uri\nimport android.os.Build\nimport android.os.Build.VERSION_CODES\nimport android.os.Bundle\nimport android.provider.Settings\nimport android.view.View\nimport android.view.autofill.AutofillManager\nimport androidx.activity.result.contract.ActivityResultContract\nimport androidx.annotation.RequiresApi\nimport androidx.core.app.ActivityOptionsCompat\nimport androidx.lifecycle.lifecycleScope\nimport androidx.preference.ListPreference\nimport androidx.preference.Preference\nimport androidx.preference.PreferenceFragmentCompat\nimport androidx.preference.PreferenceGroup.PreferencePositionCallback\nimport androidx.preference.PreferenceManager\nimport androidx.preference.SwitchPreference\nimport androidx.recyclerview.widget.RecyclerView\nimport com.alibaba.android.arouter.facade.annotation.Autowired\nimport com.alibaba.android.arouter.facade.annotation.Route\nimport com.alibaba.android.arouter.launcher.ARouter\nimport com.arialyy.frame.core.AbsFrame\nimport com.arialyy.frame.util.ResUtil\nimport com.blankj.utilcode.util.LanguageUtils\nimport com.blankj.utilcode.util.ReflectUtils\nimport com.blankj.utilcode.util.RomUtils\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseApp\nimport com.lyy.keepassa.common.PassType\nimport com.lyy.keepassa.util.FingerprintUtil\nimport com.lyy.keepassa.util.KeepassAUtil\nimport com.lyy.keepassa.util.KpaUtil\nimport com.lyy.keepassa.util.LanguageUtil\nimport com.lyy.keepassa.util.PermissionsUtil\nimport com.lyy.keepassa.view.UpgradeLogDialog\nimport com.lyy.keepassa.view.fingerprint.FingerprintActivity\nimport de.psdev.licensesdialog.LicensesDialog\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.delay\nimport kotlinx.coroutines.launch\nimport kotlinx.coroutines.withContext\nimport timber.log.Timber\nimport java.util.Locale\n\n/**\n * 应用设置\n */\n@Route(path = \"/setting/appFm\")\nclass AppSettingFragment : PreferenceFragmentCompat() {\n  private lateinit var passTypeList: ListPreference\n  private var passLen = 3\n  private lateinit var autoFill: SwitchPreference\n\n  companion object {\n    private val LANGUAGE_MAP = hashMapOf<Int, Locale>().apply {\n      put(1, Locale.ENGLISH)\n      put(2, Locale.SIMPLIFIED_CHINESE)\n      put(3, Locale.TRADITIONAL_CHINESE)\n      put(4, Locale.CANADA_FRENCH)\n      put(5, Locale(\"nb\", \"rNO\"))\n      put(6, Locale(\"ru\", \"rRU\"))\n      put(7, Locale.FRENCH)\n      put(8, Locale.GERMANY)\n      put(9, Locale(\"pl\"))\n      put(10, Locale(\"tr\"))\n      put(11, Locale(\"uk\", \"rUA\"))\n      put(12, Locale(\"es\")) // 西班牙语)\n    }\n  }\n\n  @Autowired(name = \"scrollKey\")\n  @JvmField\n  var scrollKey: String? = null\n\n  private var isHighlighted = false\n\n  @RequiresApi(VERSION_CODES.O)\n  private val autoFillLauncher =\n    registerForActivityResult(object : ActivityResultContract<String, Int>() {\n      override fun createIntent(context: Context, input: String): Intent {\n        return Intent(\n          Settings.ACTION_REQUEST_SET_AUTOFILL_SERVICE,\n          Uri.parse(input)\n        )\n      }\n\n      override fun parseResult(resultCode: Int, intent: Intent?): Int {\n        return resultCode\n      }\n    }) {\n      if (it == Activity.RESULT_OK) {\n        autoFill.isChecked = true\n      } else {\n        autoFill.isChecked = requireContext().getSystemService(AutofillManager::class.java)\n          .hasEnabledAutofillServices()\n      }\n    }\n\n  override fun onCreatePreferences(\n    savedInstanceState: Bundle?,\n    rootKey: String?\n  ) {\n    ARouter.getInstance().inject(this)\n    setPreferencesFromResource(R.xml.app_setting, rootKey)\n    setSubPassType()\n    setAtoFill()\n    setLanguage()\n    setQuickUnLock()\n    setFingerPrint()\n    setVersionLog()\n    setIme()\n    license()\n    screenLock()\n  }\n\n  override fun onViewCreated(view: View, savedInstanceState: Bundle?) {\n    super.onViewCreated(view, savedInstanceState)\n    scrollToKey()\n  }\n\n  /**\n   * turn to scrollKey\n   */\n  private fun scrollToKey() {\n    if (!scrollKey.isNullOrEmpty() && !isHighlighted) {\n      try {\n        val mList = ReflectUtils.reflect(this).field(\"mList\").get<RecyclerView>()\n        val adapter = mList.adapter\n        val position =\n          (adapter as PreferencePositionCallback).getPreferenceAdapterPosition(scrollKey!!)\n        Timber.d(\"postiion = $position, key = $scrollKey\")\n        isHighlighted = true\n        lifecycleScope.launch(Dispatchers.IO) {\n          delay(200)\n          withContext(Dispatchers.Main) {\n            mList.scrollToPosition(position)\n            val v = mList.layoutManager?.findViewByPosition(position)\n            v?.let {\n              itemViewAnim(v)\n            }\n          }\n        }\n      } catch (e: Exception) {\n        Timber.e(e)\n      }\n    }\n  }\n\n  private suspend fun itemViewAnim(view: View) {\n    withContext(Dispatchers.Main) {\n      view.setBackgroundColor(ResUtil.getColor(R.color.color_524E85DB))\n    }\n    withContext(Dispatchers.IO) {\n      delay(2000)\n    }\n    view.setBackgroundColor(ResUtil.getColor(R.color.color_FFFFFF))\n  }\n\n  /**\n   * when the screen lock ,the db will auto lock\n   */\n  private fun screenLock() {\n    findPreference<SwitchPreference>(getString(R.string.set_key_lock_screen_auto_lock_db))?.setOnPreferenceChangeListener { _, _ ->\n      BaseApp.APP.initReceiver()\n      return@setOnPreferenceChangeListener true\n    }\n  }\n\n  /**\n   * 开放源码许可证\n   */\n  private fun license() {\n    findPreference<Preference>(getString(R.string.set_key_license))?.setOnPreferenceClickListener {\n\n      LicensesDialog.Builder(requireContext())\n        .setNotices(R.raw.notices)\n        .setIncludeOwnLicense(true)\n        .build()\n        .show()\n      true\n    }\n  }\n\n  /**\n   * 处理安全键盘\n   */\n  private fun setIme() {\n    findPreference<Preference>(getString(R.string.set_key_open_kpa_ime))?.setOnPreferenceClickListener {\n      startActivity(\n        Intent(Settings.ACTION_INPUT_METHOD_SETTINGS),\n        ActivityOptionsCompat.makeSceneTransitionAnimation(requireActivity()).toBundle()\n      )\n      true\n    }\n  }\n\n  /**\n   * 处理升级日志\n   */\n  private fun setVersionLog() {\n    findPreference<Preference>(getString(R.string.set_key_version_log))?.setOnPreferenceClickListener {\n      UpgradeLogDialog().show()\n      true\n    }\n  }\n\n  /**\n   * 设置截取的短密码类型，和短密码的截取长度\n   */\n  private fun setSubPassType() {\n    // 密码长度\n    val passLenLayout =\n      findPreference<ListPreference>(getString(R.string.set_quick_pass_len))\n    passTypeList = findPreference(getString(R.string.set_quick_pass_type))!!\n\n    if (BaseApp.passType == PassType.ONLY_KEY) {\n      passLenLayout?.isVisible = false\n      passTypeList.isVisible = false\n      return\n    }\n    passLenLayout!!.setOnPreferenceChangeListener { _, newValue ->\n      Timber.i(\"短密码长度：$newValue\")\n      passLen = newValue.toString()\n        .toInt()\n      setPassTypeEntries()\n      true\n    }\n    passLen = passLenLayout.value.toInt()\n\n    // 密码截取类型\n    passTypeList.setOnPreferenceChangeListener { _, newValue ->\n      val subTitle = passTypeList.entries[newValue.toString()\n        .toInt() - 1]\n      Timber.i(\"短密码类型：$subTitle\")\n      passTypeList.summary = subTitle.toString()\n      subShortPass()\n      true\n    }\n    passTypeList.summary = passTypeList.entries[0]\n\n    // 默认截取一次\n    setPassTypeEntries()\n  }\n\n  /**\n   * 截取短密码，需要延时截取，因为Preference的保存是异步的，有可能会比较慢\n   */\n  private fun subShortPass() {\n    KpaUtil.scope.launch {\n      delay(1000)\n      KeepassAUtil.instance.subShortPass()\n    }\n  }\n\n  /**\n   * 处理指纹解锁\n   */\n  private fun setFingerPrint() {\n    val fingerprint =\n      findPreference<Preference>(getString(R.string.set_key_fingerprint_unlock))\n    if (!FingerprintUtil.hasBiometricPrompt(requireContext())) {\n      fingerprint?.isVisible = false\n      return\n    }\n    fingerprint!!.setOnPreferenceClickListener {\n      FingerprintActivity.toFingerprintActivity(requireActivity())\n      return@setOnPreferenceClickListener true\n    }\n  }\n\n  /**\n   * 处理自动填充服务\n   */\n  private fun setAtoFill() {\n    autoFill = findPreference(getString(R.string.set_open_auto_fill))!!\n    // 大于8.0 才能使用自带的填充框架，否则只能使用辅助功能来实现\n    if (Build.VERSION.SDK_INT >= VERSION_CODES.O) {\n      val am = requireContext().getSystemService(AutofillManager::class.java)\n      if (am == null || !am.isAutofillSupported) {\n        autoFill.isVisible = false\n        return\n      }\n\n      // miui 检查后台弹出权限\n      if (am.isAutofillSupported\n        && RomUtils.isXiaomi()\n        && !PermissionsUtil.miuiCanBackgroundStart()\n      ) {\n        PermissionsUtil.showAutoFillMsgDialog(\n          requireContext(),\n          getString(R.string.hint_open_backgroun_start)\n        )\n      }\n\n      // vivo 检查后台弹出权限\n      if (am.isAutofillSupported\n        && RomUtils.isVivo()\n        && !PermissionsUtil.vivoBackgroundStartAllowed()\n      ) {\n        PermissionsUtil.showAutoFillMsgDialog(\n          requireContext(),\n          getString(R.string.hint_open_backgroun_start)\n        )\n      }\n\n\n      autoFill.isChecked = am.hasEnabledAutofillServices()\n      if (!am.isAutofillSupported) {\n        autoFill.isVisible = false\n      }\n\n      autoFill.setOnPreferenceChangeListener { _, newValue ->\n        if (!(newValue as Boolean)) {\n          // 如果已启用，需要包名不同才能重新打开自动填充设置\n          autoFillLauncher.launch(\n            \"package:${requireContext().packageName}1\",\n            ActivityOptionsCompat.makeSceneTransitionAnimation(requireActivity())\n          )\n        } else {\n          autoFillLauncher.launch(\n            \"package:${requireContext().packageName}\",\n            ActivityOptionsCompat.makeSceneTransitionAnimation(requireActivity())\n          )\n        }\n        true\n      }\n    } else {\n      autoFill.isVisible = false\n    }\n  }\n\n  /**\n   * 处理快速解锁，只有密钥的情况不允许使用快速解锁\n   */\n  private fun setQuickUnLock() {\n    val unLock = findPreference<SwitchPreference>(getString(R.string.set_quick_unlock))!!\n    if (BaseApp.passType == PassType.ONLY_KEY) {\n      unLock.isVisible = false\n      return\n    }\n    unLock.setOnPreferenceChangeListener { _, newValue ->\n      Timber.d(\"quick unlock newValue = $newValue\")\n      if (newValue as Boolean) {\n        subShortPass()\n      }\n      return@setOnPreferenceChangeListener true\n    }\n  }\n\n  /**\n   * 设置语言\n   */\n  private fun setLanguage() {\n    val langPre = findPreference<ListPreference>(getString(R.string.set_key_language))\n    val spm = PreferenceManager.getDefaultSharedPreferences(BaseApp.APP)\n    if (spm.getString(getString(R.string.set_key_language), null) == null\n    ) {\n      val sysLan = LanguageUtils.getSystemLanguage()\n      val temp = LANGUAGE_MAP.entries.find { it.value.language == sysLan.language }\n      val index = LANGUAGE_MAP.entries.indexOf(temp)\n      if (index in LANGUAGE_MAP.entries.indices){\n        langPre?.setValueIndex(index)\n      }\n    }\n    langPre?.setOnPreferenceChangeListener { _, newValue ->\n      val lang = LANGUAGE_MAP[newValue.toString()\n        .toInt()] ?: Locale.ENGLISH\n      BaseApp.currentLang = lang\n      LanguageUtil.saveLanguage(requireContext(), lang)\n      for (ac in AbsFrame.getInstance().activityStack) {\n        AbsFrame.getInstance()\n          .removeActivity(ac)\n        ac.recreate()\n      }\n      true\n    }\n  }\n\n  /**\n   * 设置选择项类型条目\n   */\n  private fun setPassTypeEntries() {\n    val entries = requireContext().resources.getStringArray(R.array.quick_pass_type_entries)\n    val newEntries = arrayOfNulls<CharSequence>(entries.size)\n\n    entries.forEachIndexed { index, value ->\n      newEntries[index] = value.toString()\n        .format(passLen.toString())\n    }\n\n    passTypeList.entries = newEntries\n    passTypeList.summary = newEntries[passTypeList.value.toInt() - 1]\n    // 截取短密码\n    subShortPass()\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/setting/DBSettingFragment.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.view.setting\n\nimport android.os.Bundle\nimport androidx.lifecycle.ViewModelProvider\nimport androidx.preference.EditTextPreference\nimport androidx.preference.ListPreference\nimport androidx.preference.Preference\nimport androidx.preference.PreferenceFragmentCompat\nimport androidx.preference.SwitchPreference\nimport com.alibaba.android.arouter.facade.annotation.Route\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseApp\nimport com.lyy.keepassa.event.ModifyDbNameEvent\nimport com.lyy.keepassa.event.ModifyPassEvent\nimport com.lyy.keepassa.util.AutoLockDbUtil\nimport com.lyy.keepassa.util.EventBusHelper\nimport com.lyy.keepassa.util.HitUtil\nimport com.lyy.keepassa.util.KeepassAUtil\nimport com.lyy.keepassa.util.QuickUnLockUtil\nimport com.lyy.keepassa.util.cloud.DbSynUtil\nimport com.lyy.keepassa.view.dialog.ModifyPassDialog\nimport org.greenrobot.eventbus.EventBus\nimport org.greenrobot.eventbus.Subscribe\nimport org.greenrobot.eventbus.ThreadMode.MAIN\nimport timber.log.Timber\n\n/**\n * 数据库设置\n */\n@Route(path = \"/setting/DbFm\")\nclass DBSettingFragment : PreferenceFragmentCompat() {\n  private lateinit var module: SettingModule\n\n  override fun onCreatePreferences(\n    savedInstanceState: Bundle?,\n    rootKey: String?\n  ) {\n    EventBusHelper.reg(this)\n    module = ViewModelProvider(this).get(SettingModule::class.java)\n    setPreferencesFromResource(R.xml.db_setting, rootKey)\n    handleEvent()\n  }\n\n  private fun handleEvent() {\n    // 修改数据库密码\n    val modifyDbPass =\n      findPreference<Preference>(getString(R.string.set_key_modify_db_pass))\n    modifyDbPass!!.onPreferenceClickListener = Preference.OnPreferenceClickListener {\n      val dialog = ModifyPassDialog()\n      dialog.show(childFragmentManager, \"modify_pass\")\n      return@OnPreferenceClickListener true\n    }\n\n    setModifyDbName()\n    setOutDb()\n    setLockDbTime()\n    setAutoLockDb()\n  }\n\n  /**\n   * 是否自动锁定数据库\n   */\n  private fun setAutoLockDb() {\n    val enableAutoLockDb =\n      findPreference<SwitchPreference>(getString(R.string.set_key_auto_lock_database))\n    enableAutoLockDb?.setOnPreferenceChangeListener { _, newValue ->\n      if (newValue as Boolean) {\n        KeepassAUtil.instance.startLockTimer(this)\n        return@setOnPreferenceChangeListener true\n      }\n      AutoLockDbUtil.get().cancelTimer()\n      return@setOnPreferenceChangeListener true\n    }\n  }\n\n  /**\n   * 处理锁屏时间\n   */\n  private fun setLockDbTime() {\n    // handle lock db time\n    val lockDbTime = findPreference<ListPreference>(getString(R.string.set_key_auto_lock_db_time))!!\n    lockDbTime.setOnPreferenceChangeListener { _, _ ->\n      AutoLockDbUtil.get()\n        .resetTimer()\n      return@setOnPreferenceChangeListener true\n    }\n  }\n\n  /**\n   * 处理导出数据库\n   */\n  private fun setOutDb() {\n    val outDb = findPreference<ListPreference>(getString(R.string.set_key_out_db))\n    outDb!!.setOnPreferenceChangeListener { _, newValue ->\n      // todo 导出文件\n      when (newValue as Int) {\n        0 -> {\n          // 导出kdbx数据库\n        }\n        1 -> {\n          // 导出xml格式的文件\n        }\n        2 -> {\n          // 导出csv格式的文件\n        }\n      }\n      Timber.d(\"outDb value = $newValue\")\n      false\n    }\n  }\n\n  /**\n   * 处理数据库名的修改\n   */\n  private fun setModifyDbName() {\n    // 修改数据库名\n    val modifyDbName =\n      findPreference<EditTextPreference>(getString(R.string.set_key_modify_db_name))\n    modifyDbName!!.text = BaseApp.dbName\n    modifyDbName.setOnBindEditTextListener { et ->\n      // 将光标移动到最后\n      et.setSelection(BaseApp.dbName.length)\n    }\n    modifyDbName.setOnPreferenceChangeListener { _, newValue ->\n      if ((newValue as String).isNotEmpty()) {\n        if (newValue == BaseApp.KDB!!.pm.name) {\n          HitUtil.toaskShort(getString(R.string.db_name_no_alter))\n          return@setOnPreferenceChangeListener false\n        }\n        module.modifyDbName(newValue) {\n          if (it == DbSynUtil.STATE_SUCCEED) {\n            BaseApp.dbName = newValue\n            EventBus.getDefault().post(ModifyDbNameEvent(newValue))\n            HitUtil.toaskShort(\"${getString(R.string.db_name_modify)}${getString(R.string.success)}\")\n            return@modifyDbName\n          }\n          HitUtil.toaskShort(\"${getString(R.string.db_name_modify)} ${getString(R.string.fail)}\")\n        }\n      }\n      false\n    }\n  }\n\n  /**\n   * 修改密码\n   */\n  private fun modifyPass(newValue: String) {\n    if (newValue.isNotEmpty()) {\n      if (newValue == QuickUnLockUtil.decryption(BaseApp.dbPass)) {\n        HitUtil.toaskShort(getString(R.string.db_pass_no_alter))\n        return\n      }\n\n      module.modifyPass(requireContext(), newValue) {\n        if (it == DbSynUtil.STATE_SUCCEED) {\n          HitUtil.toaskShort(getString(R.string.hint_db_pass_modify_success))\n        } else {\n          HitUtil.toaskShort(\"${getString(R.string.hint_db_pass_modify)}${getString(R.string.fail)}\")\n        }\n      }\n    }\n  }\n\n  @Subscribe(threadMode = MAIN)\n  fun onModifyPassEvent(event: ModifyPassEvent) {\n    modifyPass(event.pass)\n  }\n\n  override fun onDestroy() {\n    super.onDestroy()\n    EventBusHelper.unReg(this)\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/setting/SettingActivity.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.view.setting\n\nimport android.os.Bundle\nimport androidx.preference.PreferenceFragmentCompat\nimport com.alibaba.android.arouter.facade.annotation.Autowired\nimport com.alibaba.android.arouter.facade.annotation.Route\nimport com.alibaba.android.arouter.launcher.ARouter\nimport com.arialyy.frame.router.Routerfit\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.base.BaseActivity\nimport com.lyy.keepassa.databinding.ActivitySettingBinding\nimport com.lyy.keepassa.router.FragmentRouter\n\n@Route(path = \"/setting/app\")\nclass SettingActivity : BaseActivity<ActivitySettingBinding>() {\n\n  companion object {\n    // 设置类型\n    const val KEY_TYPE = \"KEY_TYPE\"\n\n    // 数据库设置\n    const val TYPE_DB = 0\n\n    // 应用设置\n    const val TYPE_APP = 1\n  }\n\n  @Autowired(name = KEY_TYPE)\n  @JvmField\n  var type = TYPE_APP\n\n  @Autowired(name = \"scrollKey\")\n  @JvmField\n  var scrollKey: String? = null\n\n  private val appFm: PreferenceFragmentCompat by lazy {\n    Routerfit.create(FragmentRouter::class.java).getAppSettingFragment(scrollKey = scrollKey)\n  }\n\n  private val dbFm: PreferenceFragmentCompat by lazy {\n    Routerfit.create(FragmentRouter::class.java).getDbSettingFragment()\n  }\n\n  override fun setLayoutId(): Int {\n    return R.layout.activity_setting\n  }\n\n  override fun initData(savedInstanceState: Bundle?) {\n    super.initData(savedInstanceState)\n    ARouter.getInstance().inject(this)\n    val title: String\n    val fragment: PreferenceFragmentCompat\n    if (type == TYPE_DB) {\n      title = getString(R.string.db_setting)\n      fragment = dbFm\n    } else {\n      title = getString(R.string.app_setting)\n      fragment = appFm\n    }\n    toolbar.title = title\n    supportFragmentManager.beginTransaction()\n      .replace(R.id.content, fragment)\n      .commitAllowingStateLoss()\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/setting/SettingModule.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.view.setting\n\nimport android.content.Context\nimport android.net.Uri\nimport com.keepassdroid.utils.UriUtil\nimport com.lyy.keepassa.base.BaseApp\nimport com.lyy.keepassa.base.BaseModule\nimport com.lyy.keepassa.util.KpaUtil\nimport com.lyy.keepassa.util.QuickUnLockUtil\n\n/**\n * 设置页面的module\n */\nclass SettingModule : BaseModule() {\n\n  /**\n   * 修改数据库密码\n   */\n  fun modifyDbName(newDbName: String, callback: (Int) -> Unit) {\n    BaseApp.KDB.pm.name = newDbName\n    KpaUtil.kdbHandlerService.saveDbByForeground(callback = callback)\n  }\n\n  /**\n   * 修改密码\n   */\n  fun modifyPass(\n    context: Context,\n    newPass: String,\n    callback: (Int) -> Unit\n  ) {\n    if (BaseApp.dbKeyPath == null || BaseApp.dbKeyPath.isEmpty()) {\n      BaseApp.KDB.pm.setMasterKey(newPass, null)\n    } else {\n      val ios = UriUtil.getUriInputStream(\n        context, Uri.parse(QuickUnLockUtil.decryption(BaseApp.dbKeyPath))\n      )\n      BaseApp.KDB.pm.setMasterKey(newPass, ios)\n    }\n    KpaUtil.kdbHandlerService.saveDbByBackground(callback = callback)\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/view/setting/UISettingFragment.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\npackage com.lyy.keepassa.view.setting\n\nimport android.os.Bundle\nimport android.view.animation.Animation\nimport androidx.appcompat.app.AppCompatDelegate\nimport androidx.preference.ListPreference\nimport androidx.preference.Preference\nimport androidx.preference.PreferenceFragmentCompat\nimport androidx.preference.PreferenceManager\nimport androidx.preference.SwitchPreference\nimport com.arialyy.frame.core.AbsFrame\nimport com.arialyy.frame.router.Routerfit\nimport com.arialyy.frame.util.ResUtil\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.R.string\nimport com.lyy.keepassa.base.BaseActivity\nimport com.lyy.keepassa.event.CheckEnvEvent\nimport com.lyy.keepassa.event.ShowTOTPEvent\nimport com.lyy.keepassa.router.DialogRouter\nimport com.lyy.keepassa.util.BarUtil\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.GlobalScope\nimport kotlinx.coroutines.delay\nimport kotlinx.coroutines.launch\nimport org.greenrobot.eventbus.EventBus\n\n/**\n * @Author laoyuyu\n * @Description ui setting\n * @Date 2020/11/25\n **/\nclass UISettingFragment : PreferenceFragmentCompat() {\n  override fun onCreatePreferences(\n    savedInstanceState: Bundle?,\n    rootKey: String?\n  ) {\n    setPreferencesFromResource(R.xml.ui_setting, rootKey)\n    handleEnvCheck()\n    handleShowStatusBar()\n    handleShowMainTotpTab()\n    handleThemStyle()\n    handleTip()\n  }\n\n  override fun onCreateAnimation(\n    transit: Int,\n    enter: Boolean,\n    nextAnim: Int\n  ): Animation? {\n    // clear anim\n    return null\n  }\n\n  private fun handleTip() {\n    val lp = findPreference<Preference>(ResUtil.getString(R.string.set_key_tip_of_day))\n    lp?.setOnPreferenceClickListener {\n      Routerfit.create(DialogRouter::class.java).showTipDialog()\n      return@setOnPreferenceClickListener true\n    }\n  }\n\n  /**\n   * handle theme style\n   */\n  private fun handleThemStyle() {\n    val lp = findPreference<ListPreference>(ResUtil.getString(R.string.set_key_theme_style))\n    val summaryArray =\n      requireContext().resources.getStringArray(R.array.sek_ley_theme_style_entries)\n    val mode = PreferenceManager.getDefaultSharedPreferences(requireContext())\n      .getString(getString(string.set_key_theme_style), \"0\")!!.toInt()\n\n    lp?.summary = summaryArray[mode]\n\n    lp?.setOnPreferenceChangeListener { _, newValue ->\n      val index = newValue.toString().toInt()\n      lp.summary = summaryArray[index]\n      when (index) {\n        0 -> {\n          AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)\n        }\n        1 -> {\n          AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)\n        }\n        2 -> {\n          AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)\n        }\n      }\n\n      return@setOnPreferenceChangeListener true\n    }\n  }\n\n  private fun handleShowMainTotpTab() {\n    val sw = findPreference<SwitchPreference>(getString(R.string.set_key_main_show_totp_tab))\n    sw?.setOnPreferenceChangeListener { _, newValue ->\n      EventBus.getDefault().post(ShowTOTPEvent(newValue as Boolean))\n      return@setOnPreferenceChangeListener true\n    }\n  }\n\n  /**\n   * handle status bar show or hide\n   */\n  private fun handleShowStatusBar() {\n    val sw = findPreference<SwitchPreference>(getString(R.string.set_key_show_state_bar))\n    sw?.isChecked = BarUtil.statusBarIsVisible(requireActivity().window)\n\n    sw?.setOnPreferenceChangeListener { _, newValue ->\n      BaseActivity.showStatusBar = newValue as Boolean\n      for (ac in AbsFrame.getInstance().activityStack) {\n        BarUtil.showStatusBar(ac, BaseActivity.showStatusBar)\n      }\n      return@setOnPreferenceChangeListener true\n    }\n  }\n\n  /**\n   * handle operating env check\n   */\n  private fun handleEnvCheck() {\n    findPreference<SwitchPreference>(getString(R.string.set_key_need_root_check))?.setOnPreferenceChangeListener { _, _ ->\n      GlobalScope.launch(Dispatchers.IO) {\n        delay(200)\n        EventBus.getDefault()\n          .post(CheckEnvEvent())\n      }\n\n      return@setOnPreferenceChangeListener true\n    }\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/widgets/expand/AttrFileItemView.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.widgets.expand\n\nimport android.annotation.SuppressLint\nimport android.content.Context\nimport android.net.Uri\nimport android.view.LayoutInflater\nimport android.widget.RelativeLayout\nimport android.widget.TextView\nimport com.keepassdroid.database.security.ProtectedBinary\nimport com.lyy.keepassa.R\n\n/**\n * 附件item字段\n */\n@SuppressLint(\"ViewConstructor\")\nclass AttrFileItemView(\n  context: Context,\n  var titleStr: String,\n  var file: ProtectedBinary? = null,\n  var fileUri: Uri? = null\n) : RelativeLayout(context) {\n  val valueTx: TextView\n\n  init {\n    LayoutInflater.from(context)\n        .inflate(R.layout.layout_expand_child_file, this, true)\n    valueTx = findViewById(R.id.value)\n    updateData(titleStr, file, fileUri)\n  }\n\n  fun updateData(\n    titleStr: String,\n    valueInfo: ProtectedBinary? = null,\n    fileUri: Uri? = null\n  ) {\n    this.fileUri = fileUri\n    this.titleStr = titleStr\n    this.file = valueInfo\n    valueTx.text = titleStr\n  }\n\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/widgets/expand/AttrStrItemView.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.widgets.expand\n\nimport android.annotation.SuppressLint\nimport android.content.Context\nimport android.text.InputType\nimport android.view.LayoutInflater\nimport android.widget.RelativeLayout\nimport android.widget.TextView\nimport com.keepassdroid.database.security.ProtectedString\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.widget.pb.RoundProgressBarWidthNumber\n\n@SuppressLint(\"ViewConstructor\")\nclass AttrStrItemView(\n  context: Context,\n  var titleStr: String,\n  var valueInfo: ProtectedString\n) : RelativeLayout(context) {\n  val titleTx: TextView\n  val valueTx: TextView\n  val pb: RoundProgressBarWidthNumber\n\n  init {\n    LayoutInflater.from(context).inflate(R.layout.layout_expand_child_str, this, true)\n    titleTx = findViewById(R.id.title)\n    valueTx = findViewById(R.id.value)\n    pb = findViewById(R.id.rpbBar)\n    updateValue(titleStr, valueInfo)\n  }\n\n  fun updateValue(\n    key: String,\n    valueInfo: ProtectedString\n  ) {\n    titleStr = key\n    this.valueInfo = valueInfo\n    titleTx.text = titleStr\n    if (valueInfo.isProtected) {\n      valueTx.inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_WEB_PASSWORD\n    } else {\n      valueTx.inputType =\n        InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD\n    }\n    valueTx.text = valueInfo.toString()\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/widgets/expand/ExpandAttrStrLayout.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.widgets.expand\n\nimport android.animation.LayoutTransition\nimport android.animation.ObjectAnimator\nimport android.annotation.SuppressLint\nimport android.content.Context\nimport android.util.AttributeSet\nimport android.view.LayoutInflater\nimport android.view.View\nimport android.widget.LinearLayout\nimport android.widget.RelativeLayout\nimport android.widget.TextView\nimport androidx.appcompat.widget.AppCompatImageView\nimport androidx.core.view.children\nimport com.keepassdroid.database.PwEntryV4\nimport com.keepassdroid.database.security.ProtectedBinary\nimport com.keepassdroid.database.security.ProtectedString\nimport com.lyy.keepassa.R\nimport com.lyy.keepassa.util.KeepassAUtil\nimport com.lyy.keepassa.util.totp.OtpUtil\nimport com.lyy.keepassa.widget.toPx\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.Job\nimport kotlinx.coroutines.MainScope\nimport kotlinx.coroutines.cancel\nimport kotlinx.coroutines.delay\nimport kotlinx.coroutines.launch\nimport kotlinx.coroutines.withContext\nimport timber.log.Timber\n\n/**\n * 项目详情页，展开自定义字段\n */\nclass ExpandAttrStrLayout(\n  context: Context,\n  attr: AttributeSet\n) : RelativeLayout(context, attr), View.OnClickListener {\n  private val TAG = javaClass.simpleName\n  private var attrIsExpand = false // 属性字段展开状态\n  private val animDuring = 400L\n  private var showAttrAnim: ObjectAnimator? = null\n  private var hintAttrAnim: ObjectAnimator? = null\n  private var attrTitleX = 0F\n  private var strData = LinkedHashMap<String, ProtectedString>()\n  private var fileData = HashMap<String, ProtectedBinary>()\n  private val itemAnimDuring = 500L\n\n  private val attrImg: AppCompatImageView\n  private val attrTt: TextView\n  private val attrArrow: AppCompatImageView\n  private var listener: OnExpandListener? = null\n  private val itemLayout: LinearLayout\n  private var strViewListener: OnAttrViewClickListener? = null\n\n  // otp 密码字段，将会自动更新密码\n  private var otpPassItem: AttrStrItemView? = null\n\n  private var job: Job? = null\n  var entryV4: PwEntryV4? = null\n  private val scope = MainScope()\n\n  /**\n   * 展开事件\n   */\n  interface OnExpandListener {\n    fun onExpand(isExpand: Boolean)\n  }\n\n  /**\n   * 高级属性的view点击事件\n   */\n  interface OnAttrViewClickListener {\n    fun onClickListener(\n      v: View,\n      position: Int\n    )\n  }\n\n  init {\n    LayoutInflater.from(context)\n      .inflate(R.layout.layout_expand_title, this, true)\n    attrImg = findViewById(R.id.attr_img)\n    attrTt = findViewById(R.id.attr_tt)\n    attrArrow = findViewById(R.id.attr_arrow)\n    itemLayout = findViewById(R.id.attrs)\n    val ta = context.obtainStyledAttributes(attr, R.styleable.ExpandTextView)\n    val tStr = ta.getString(R.styleable.ExpandTextView_expand_tt_title)\n    val iDrawable = ta.getDrawable(R.styleable.ExpandTextView_expand_tt_icon)\n    ta.recycle()\n    attrTt.text = tStr\n    if (iDrawable != null) {\n      attrImg.setImageDrawable(iDrawable)\n    }\n    // 需在重新设置一次背景，inflate进去并不是root\n//    rootView.setBackgroundResource(R.drawable.ripple_white_selector)\n\n    findViewById<View>(R.id.head).setOnClickListener {\n      toggle()\n    }\n\n    // 设置visibility 和 gone时播放的动画\n//    initAnim()\n  }\n\n  @SuppressLint(\"ObjectAnimatorBinding\")\n  private fun initAnim() {\n    val mTransitioner = LayoutTransition()\n\n//    val pvhLeft = PropertyValuesHolder.ofInt(\"left\", 0, 0)\n//    val pvhTop = PropertyValuesHolder.ofInt(\"top\", 0, 0)\n//    val pvhRight = PropertyValuesHolder.ofInt(\"right\", 0, 0)\n//    val pvhBottom = PropertyValuesHolder.ofInt(\"bottom\", 0, 0)\n//\n//    val animator = PropertyValuesHolder.ofFloat(\"scaleX\", 1f, 1.5f, 1f)\n//    val changeIn = ObjectAnimator.ofPropertyValuesHolder(\n//            this, pvhLeft, pvhBottom, animator\n//        )\n//        .setDuration(mTransitioner.getDuration(LayoutTransition.CHANGE_APPEARING))\n\n    mTransitioner.setStartDelay(LayoutTransition.APPEARING, 1000)\n    mTransitioner.setStartDelay(LayoutTransition.CHANGE_APPEARING, 1000)\n//    mTransitioner.setAnimator(LayoutTransition.CHANGE_APPEARING, changeIn)\n\n//    val pvhRotation = PropertyValuesHolder.ofFloat(\"scaleX\", 1f, 1.5f, 1f)\n//    val changeOut = ObjectAnimator.ofPropertyValuesHolder(\n//            this, pvhLeft, pvhBottom, pvhRotation\n//        )\n//        .setDuration(mTransitioner.getDuration(LayoutTransition.CHANGE_DISAPPEARING))\n\n//    mTransitioner.setAnimator(LayoutTransition.CHANGE_DISAPPEARING, changeOut)\n\n    mTransitioner.setStartDelay(LayoutTransition.CHANGE_DISAPPEARING, 1000)\n    mTransitioner.disableTransitionType(LayoutTransition.APPEARING)\n    mTransitioner.disableTransitionType(LayoutTransition.DISAPPEARING)\n    mTransitioner.disableTransitionType(LayoutTransition.CHANGE_DISAPPEARING)\n    mTransitioner.disableTransitionType(LayoutTransition.CHANGE_APPEARING)\n    layoutTransition = mTransitioner\n  }\n\n  override fun onDetachedFromWindow() {\n    super.onDetachedFromWindow()\n    job?.cancel()\n    scope.cancel()\n  }\n\n  override fun removeAllViews() {\n    itemLayout.removeAllViews()\n    strData.clear()\n    fileData.clear()\n  }\n\n  private fun toggle() {\n    if (KeepassAUtil.instance.isFastClick()) {\n      return\n    }\n    if (attrIsExpand) {\n      hintAttr()\n    } else {\n      showAttr()\n    }\n  }\n\n  /**\n   * 设置高级属性的view点击事件\n   */\n  fun setOnAttrViewClickListener(listener: OnAttrViewClickListener) {\n    this.strViewListener = listener\n  }\n\n  /**\n   * 设置展开事件监听\n   */\n  fun setOnExpandListener(listener: OnExpandListener) {\n    this.listener = listener\n  }\n\n  /**\n   * 隐藏属性字段\n   */\n  private fun hintAttr() {\n    // 处理icon 和标题\n    attrImg.visibility = View.VISIBLE\n    ObjectAnimator.ofFloat(attrTt, \"translationX\", attrTt.translationX, attrTitleX)\n      .setDuration(200)\n      .start()\n\n    // 处理箭头动画\n    if (hintAttrAnim != null && hintAttrAnim!!.isRunning) {\n      hintAttrAnim!!.cancel()\n    }\n    if (showAttrAnim == null) {\n      showAttrAnim = ObjectAnimator.ofFloat(attrArrow, \"rotation\", -90f, 0f)\n      showAttrAnim!!.duration = animDuring\n    }\n    showAttrAnim!!.start()\n    attrIsExpand = false\n    if (listener != null) {\n      listener!!.onExpand(false)\n    }\n    for (v in itemLayout.children) {\n      v.visibility = View.GONE\n    }\n    itemLayout.visibility = View.GONE\n  }\n\n  /**\n   *  显示属性字段\n   */\n  private fun showAttr() {\n    // 处理icon 和标题\n    val iconX = attrImg.translationX - 16.toPx()\n    attrTitleX = attrTt.translationX\n    attrImg.visibility = View.GONE\n    ObjectAnimator.ofFloat(attrTt, \"translationX\", attrTitleX, iconX)\n      .setDuration(200)\n      .start()\n\n    // 处理箭头动画\n    if (showAttrAnim != null && showAttrAnim!!.isRunning) {\n      showAttrAnim!!.cancel()\n    }\n    if (hintAttrAnim == null) {\n      hintAttrAnim = ObjectAnimator.ofFloat(attrArrow, \"rotation\", 0f, -90f)\n      hintAttrAnim!!.duration = animDuring\n    }\n    hintAttrAnim!!.start()\n    attrIsExpand = true\n    if (listener != null) {\n      listener!!.onExpand(true)\n    }\n\n    for (v in itemLayout.children) {\n      v.visibility = View.VISIBLE\n    }\n    itemLayout.visibility = View.VISIBLE\n  }\n\n  /**\n   * 增加自定义属性\n   */\n  fun setFileValue(map: Map<String, ProtectedBinary>) {\n    fileData.putAll(map)\n    if (fileData.isNotEmpty()) {\n      var i = 0\n      for (d in fileData) {\n        val child = AttrFileItemView(context, d.key, d.value)\n        child.id = i\n        child.setOnClickListener(this)\n        child.isClickable = true\n        itemLayout.addView(child, i)\n        i++\n      }\n    }\n    itemLayout.invalidate()\n  }\n\n  /**\n   * 增加一个自定义属性\n   */\n  fun addFileValue(\n    title: String,\n    value: ProtectedBinary\n  ) {\n    fileData[title] = value\n    val child = AttrFileItemView(\n      context, title, value\n    )\n    child.id = itemLayout.childCount\n    child.setOnClickListener(this)\n    itemLayout.addView(child, child.id)\n    itemLayout.invalidate()\n  }\n\n  /**\n   * 增加自定义属性\n   */\n  fun setAttrValue(map: Map<String, ProtectedString>) {\n    strData.putAll(map)\n    if (strData.isNotEmpty()) {\n      var i = 0\n      for (d in strData) {\n        val child = AttrStrItemView(context, d.key, d.value)\n        child.id = i\n        child.setOnClickListener(this)\n        child.isClickable = true\n        itemLayout.addView(child, i)\n        if (d.value.isOtpPass) {\n          otpPassItem = child\n        }\n        i++\n      }\n    }\n    itemLayout.invalidate()\n    startAutoGetOtp()\n  }\n\n  /**\n   * 定时自动获取otp密码\n   */\n  private fun startAutoGetOtp() {\n    if (otpPassItem == null || entryV4 == null) {\n      Timber.e(\"无法自动获取otp密码\")\n      return\n    }\n    val p = OtpUtil.getOtpPass(entryV4!!)\n    if (p.second.isNullOrEmpty()) {\n      Timber.e(\"无法自动获取otp密码\")\n      return\n    }\n    otpPassItem?.let {\n      it.pb.visibility = View.VISIBLE\n      it.pb.setCountdown(true)\n    }\n\n    scope.launch(Dispatchers.Main) {\n\n      Timber.d(p.toString())\n      val time = p.first\n      otpPassItem?.let {\n        it.pb.max = time\n        it.updateValue(it.titleStr, ProtectedString(false, p.second))\n      }\n      for (i in time downTo 1) {\n        otpPassItem!!.pb.progress = i\n        withContext(Dispatchers.IO) {\n          delay(1000)\n        }\n      }\n      startAutoGetOtp()\n    }\n  }\n\n  /**\n   * 增加一个自定义属性\n   */\n  fun addStrValue(\n    title: String,\n    value: ProtectedString\n  ) {\n    strData[title] = value\n    val child = AttrStrItemView(context, title, value)\n    child.id = itemLayout.childCount + 1\n    child.setOnClickListener(this)\n    itemLayout.addView(child, childCount + 1)\n    itemLayout.invalidate()\n  }\n\n  /**\n   * 删除自定义属性字段\n   */\n  fun removeStrValue(id: Int) {\n    if (id < 0 || id > childCount) {\n      Timber.e(\"id【$id】错误\")\n      return\n    }\n    val child = getChildAt(id) as AttrStrItemView\n    strData.remove(child.titleStr)\n    itemLayout.removeViewAt(id)\n    itemLayout.invalidate()\n  }\n\n  override fun onClick(v: View?) {\n    if (strViewListener != null) {\n      strViewListener!!.onClickListener(v!!, v.id)\n    }\n  }\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/widgets/expand/ExpandFileAttrView.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.widgets.expand\n\nimport android.animation.Animator\nimport android.animation.AnimatorListenerAdapter\nimport android.animation.ValueAnimator\nimport android.content.Context\nimport android.net.Uri\nimport android.util.AttributeSet\nimport android.view.View\nimport android.widget.LinearLayout\nimport com.keepassdroid.database.security.ProtectedBinary\nimport timber.log.Timber\n\n/**\n * 可展开的附件view\n */\nclass ExpandFileAttrView(\n  context: Context,\n  attr: AttributeSet\n) : LinearLayout(context, attr), View.OnClickListener {\n\n  private val data = LinkedHashMap<String, ProtectedBinary?>()\n  private val childMap = LinkedHashMap<String, AttrFileItemView>()\n  private var expandAnim: ValueAnimator? = null\n  private var hintAnim: ValueAnimator? = null\n  private val animDuring = 200L\n  private var strViewListener: OnAttrFileViewClickListener? = null\n\n  /**\n   * 高级属性的view点击事件\n   */\n  public interface OnAttrFileViewClickListener {\n    fun onClickListener(\n      v: AttrFileItemView,\n      key: String,\n      position: Int\n    )\n  }\n\n  init {\n    orientation = VERTICAL\n  }\n\n  override fun removeAllViews() {\n    data.clear()\n    childMap.clear()\n    super.removeAllViews()\n  }\n\n  /**\n   * 设置高级属性的view点击事件\n   */\n  public fun setOnAttrFileViewClickListener(listener: OnAttrFileViewClickListener) {\n    this.strViewListener = listener\n  }\n\n  fun setValue(map: LinkedHashMap<String, ProtectedBinary>) {\n    data.putAll(map)\n    if (data.isNotEmpty()) {\n      var i = 0\n      for (d in data) {\n        val child = AttrFileItemView(context, d.key, d.value)\n        child.setOnClickListener(this)\n        child.isClickable = true\n        addView(child, i)\n        childMap[d.key] = child\n        i++\n      }\n    }\n    invalidate()\n  }\n\n  fun addValue(\n    key: String,\n    value: ProtectedBinary? = null,\n    fileUri: Uri? = null\n  ) {\n    data[key] = value\n    val child = AttrFileItemView(context, key, value, fileUri)\n    child.setOnClickListener(this)\n    addView(child, childCount)\n    childMap[key] = child\n    invalidate()\n  }\n\n  fun removeValue(key: String) {\n    if (key.isEmpty() || !data.keys.contains(key)) {\n      Timber.e(\"key【$key】错误\")\n      return\n    }\n    removeView(childMap[key])\n    childMap.remove(key)\n    invalidate()\n  }\n\n  /**\n   * 更新数据\n   */\n  fun updateKeyValue(\n    v: AttrFileItemView,\n    key: String,\n    value: ProtectedBinary\n  ) {\n    v.updateData(key, value)\n    invalidate()\n  }\n\n  /**\n   * 展开\n   */\n  public fun expand() {\n    if (hintAnim != null && hintAnim!!.isRunning) {\n      hintAnim!!.cancel()\n    }\n    alpha = 0f\n    visibility = View.VISIBLE\n    if (expandAnim == null) {\n      expandAnim = ValueAnimator.ofFloat(0f, 1f)\n      expandAnim!!.addUpdateListener { animation ->\n        alpha = animation.animatedValue as Float\n        requestLayout()\n      }\n    }\n    expandAnim!!.duration = animDuring\n    expandAnim!!.start()\n  }\n\n  /**\n   * 隐藏\n   */\n  public fun hint() {\n    if (expandAnim != null && expandAnim!!.isRunning) {\n      expandAnim!!.cancel()\n    }\n    if (hintAnim == null) {\n      hintAnim = ValueAnimator.ofFloat(1.0f, 0f)\n      hintAnim!!.addUpdateListener { animation ->\n        alpha = animation.animatedValue as Float\n        requestLayout()\n      }\n      hintAnim!!.addListener(object : AnimatorListenerAdapter() {\n        override fun onAnimationEnd(animation: Animator) {\n          super.onAnimationEnd(animation)\n          visibility = View.GONE\n        }\n      })\n    }\n    hintAnim!!.duration = animDuring\n    hintAnim!!.start()\n  }\n\n  override fun onClick(v: View?) {\n    if (strViewListener != null) {\n      strViewListener!!.onClickListener(\n          v!! as AttrFileItemView, (v as AttrFileItemView).titleStr, childMap.values.indexOf(v)\n      )\n    }\n  }\n\n}"
  },
  {
    "path": "app/src/main/java/com/lyy/keepassa/widgets/expand/ExpandStrAttrView.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa.widgets.expand\n\nimport android.animation.Animator\nimport android.animation.AnimatorListenerAdapter\nimport android.animation.ValueAnimator\nimport android.content.Context\nimport android.util.AttributeSet\nimport android.view.View\nimport android.widget.LinearLayout\nimport com.arialyy.frame.util.StringUtil\nimport com.keepassdroid.database.security.ProtectedString\nimport timber.log.Timber\n\n/**\n * 可展开的自定义属性view\n */\nclass ExpandStrAttrView(\n  context: Context,\n  attr: AttributeSet\n) : LinearLayout(context, attr), View.OnClickListener {\n\n  private val TAG = StringUtil.getClassName(this)\n  private val data = LinkedHashMap<String, ProtectedString>()\n  private val childMap = LinkedHashMap<String, AttrStrItemView>()\n  private var expandAnim: ValueAnimator? = null\n  private var hintAnim: ValueAnimator? = null\n  private val animDuring = 200L\n  private var strViewListener: OnAttrStrViewClickListener? = null\n\n  /**\n   * 高级属性的view点击事件\n   */\n  interface OnAttrStrViewClickListener {\n    fun onClickListener(\n      v: AttrStrItemView,\n      key: String,\n      str: ProtectedString,\n      position: Int\n    )\n  }\n\n  init {\n    orientation = VERTICAL\n  }\n\n  override fun removeAllViews() {\n    data.clear()\n    childMap.clear()\n    super.removeAllViews()\n  }\n\n  /**\n   * 设置高级属性的view点击事件\n   */\n  public fun setOnStrViewClickListener(listener: OnAttrStrViewClickListener) {\n    this.strViewListener = listener\n  }\n\n  fun setValue(map: LinkedHashMap<String, ProtectedString>) {\n    data.putAll(map)\n    if (data.isNotEmpty()) {\n      var i = 0\n      for (d in data) {\n        val child = AttrStrItemView(context, d.key, d.value)\n        child.setOnClickListener(this)\n        child.isClickable = true\n        addView(child, i)\n        childMap[d.key] = child\n        i++\n      }\n    }\n    invalidate()\n  }\n\n  fun addValue(\n    key: String,\n    value: ProtectedString\n  ) {\n    data[key] = value\n    val child = AttrStrItemView(context, key, value)\n    child.setOnClickListener(this)\n    addView(child, childCount)\n    childMap[key] = child\n    invalidate()\n  }\n\n  fun removeValue(key: String) {\n    if (key.isEmpty() || !data.keys.contains(key)) {\n      Timber.e(\"key【$key】错误\")\n      return\n    }\n    removeView(childMap[key])\n    childMap.remove(key)\n    invalidate()\n  }\n\n  /**\n   * 更新数据\n   */\n  fun updateKeyValue(\n    v: AttrStrItemView,\n    key: String,\n    value: ProtectedString\n  ) {\n    v.updateValue(key, value)\n    invalidate()\n  }\n\n  /**\n   * 展开\n   */\n  public fun expand() {\n    if (hintAnim != null && hintAnim!!.isRunning) {\n      hintAnim!!.cancel()\n    }\n    alpha = 0f\n    visibility = View.VISIBLE\n    if (expandAnim == null) {\n      expandAnim = ValueAnimator.ofFloat(0f, 1f)\n      expandAnim!!.addUpdateListener { animation ->\n        alpha = animation.animatedValue as Float\n        requestLayout()\n      }\n    }\n    expandAnim!!.duration = animDuring\n    expandAnim!!.start()\n  }\n\n  /**\n   * 隐藏\n   */\n  fun hint() {\n    if (expandAnim != null && expandAnim!!.isRunning) {\n      expandAnim!!.cancel()\n    }\n    if (hintAnim == null) {\n      hintAnim = ValueAnimator.ofFloat(1.0f, 0f)\n      hintAnim!!.addUpdateListener { animation ->\n        alpha = animation.animatedValue as Float\n        requestLayout()\n      }\n      hintAnim!!.addListener(object : AnimatorListenerAdapter() {\n        override fun onAnimationEnd(animation: Animator) {\n          super.onAnimationEnd(animation)\n          visibility = View.GONE\n        }\n      })\n    }\n    hintAnim!!.duration = animDuring\n    hintAnim!!.start()\n  }\n\n  override fun onClick(v: View?) {\n    if (strViewListener != null) {\n      strViewListener!!.onClickListener(\n          v!! as AttrStrItemView, (v as AttrStrItemView).titleStr, v.valueInfo,\n          childMap.values.indexOf(v)\n      )\n    }\n  }\n\n}"
  },
  {
    "path": "app/src/main/jni/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.4.1)\n\nadd_library(\n        keepassA SHARED\n        encrypt_str.cpp\n        aes/aes_cbc.c\n        aes/aes_core.c\n        aes/aes_ecb.c\n        aes/cbc128.c\n)\n\n#find_library(\n#        aesUtil\n#        final_key\n#)"
  },
  {
    "path": "app/src/main/jni/aes/aes.h",
    "content": "\n\n#ifndef HEADER_AES_H\n# define HEADER_AES_H\n\n# include <stddef.h>\n\n# define AES_ENCRYPT     1\n# define AES_DECRYPT     0\n\n# define AES_MAXNR 14\n# define AES_BLOCK_SIZE 16\n\nstruct aes_key_st {\n# ifdef AES_LONG\n    unsigned long rd_key[4 * (AES_MAXNR + 1)];\n# else\n    unsigned int rd_key[4 * (AES_MAXNR + 1)];\n# endif\n    int rounds;\n};\n\ntypedef struct aes_key_st AES_KEY;\n\n\nint AES_set_encrypt_key(const unsigned char *userKey, const int bits, AES_KEY *key);\nint AES_set_decrypt_key(const unsigned char *userKey, const int bits, AES_KEY *key);\n\nvoid AES_encrypt(const unsigned char *in, unsigned char *out, const AES_KEY *key);\nvoid AES_decrypt(const unsigned char *in, unsigned char *out, const AES_KEY *key);\n\nvoid AES_ecb_encrypt(const unsigned char *in, unsigned char *out, const AES_KEY *key, \n\t\t\t\t\tconst int enc);\n\nvoid AES_cbc_encrypt(const unsigned char *in, unsigned char *out,\n                     size_t length, const AES_KEY *key,\n                     unsigned char *ivec, const int enc);\n\n#endif\n\n\n"
  },
  {
    "path": "app/src/main/jni/aes/aes_cbc.c",
    "content": "/*\n * Copyright 2002-2016 The OpenSSL Project Authors. All Rights Reserved.\n *\n * Licensed under the OpenSSL license (the \"License\").  You may not use\n * this file except in compliance with the License.  You can obtain a copy\n * in the file LICENSE in the source distribution or at\n * https://www.openssl.org/source/license.html\n */\n\n#include \"aes.h\"\n#include \"modes.h\"\n\nvoid AES_cbc_encrypt(const unsigned char *in, unsigned char *out,\n                     size_t len, const AES_KEY *key,\n                     unsigned char *ivec, const int enc)\n{\n\n    if (enc)\n        CRYPTO_cbc128_encrypt(in, out, len, key, ivec,\n                              (block128_f) AES_encrypt);\n    else\n        CRYPTO_cbc128_decrypt(in, out, len, key, ivec,\n                              (block128_f) AES_decrypt);\n}\n"
  },
  {
    "path": "app/src/main/jni/aes/aes_core.c",
    "content": "#include <assert.h>\n\n#include \"aes.h\"\n#include \"aes_locl.h\"\n\n\n\n/*-\nTe0[x] = S [x].[02, 01, 01, 03];\nTe1[x] = S [x].[03, 02, 01, 01];\nTe2[x] = S [x].[01, 03, 02, 01];\nTe3[x] = S [x].[01, 01, 03, 02];\n\nTd0[x] = Si[x].[0e, 09, 0d, 0b];\nTd1[x] = Si[x].[0b, 0e, 09, 0d];\nTd2[x] = Si[x].[0d, 0b, 0e, 09];\nTd3[x] = Si[x].[09, 0d, 0b, 0e];\nTd4[x] = Si[x].[01];\n*/\n\nstatic const u32 Te0[256] = {\n    0xc66363a5U, 0xf87c7c84U, 0xee777799U, 0xf67b7b8dU,\n    0xfff2f20dU, 0xd66b6bbdU, 0xde6f6fb1U, 0x91c5c554U,\n    0x60303050U, 0x02010103U, 0xce6767a9U, 0x562b2b7dU,\n    0xe7fefe19U, 0xb5d7d762U, 0x4dababe6U, 0xec76769aU,\n    0x8fcaca45U, 0x1f82829dU, 0x89c9c940U, 0xfa7d7d87U,\n    0xeffafa15U, 0xb25959ebU, 0x8e4747c9U, 0xfbf0f00bU,\n    0x41adadecU, 0xb3d4d467U, 0x5fa2a2fdU, 0x45afafeaU,\n    0x239c9cbfU, 0x53a4a4f7U, 0xe4727296U, 0x9bc0c05bU,\n    0x75b7b7c2U, 0xe1fdfd1cU, 0x3d9393aeU, 0x4c26266aU,\n    0x6c36365aU, 0x7e3f3f41U, 0xf5f7f702U, 0x83cccc4fU,\n    0x6834345cU, 0x51a5a5f4U, 0xd1e5e534U, 0xf9f1f108U,\n    0xe2717193U, 0xabd8d873U, 0x62313153U, 0x2a15153fU,\n    0x0804040cU, 0x95c7c752U, 0x46232365U, 0x9dc3c35eU,\n    0x30181828U, 0x379696a1U, 0x0a05050fU, 0x2f9a9ab5U,\n    0x0e070709U, 0x24121236U, 0x1b80809bU, 0xdfe2e23dU,\n    0xcdebeb26U, 0x4e272769U, 0x7fb2b2cdU, 0xea75759fU,\n    0x1209091bU, 0x1d83839eU, 0x582c2c74U, 0x341a1a2eU,\n    0x361b1b2dU, 0xdc6e6eb2U, 0xb45a5aeeU, 0x5ba0a0fbU,\n    0xa45252f6U, 0x763b3b4dU, 0xb7d6d661U, 0x7db3b3ceU,\n    0x5229297bU, 0xdde3e33eU, 0x5e2f2f71U, 0x13848497U,\n    0xa65353f5U, 0xb9d1d168U, 0x00000000U, 0xc1eded2cU,\n    0x40202060U, 0xe3fcfc1fU, 0x79b1b1c8U, 0xb65b5bedU,\n    0xd46a6abeU, 0x8dcbcb46U, 0x67bebed9U, 0x7239394bU,\n    0x944a4adeU, 0x984c4cd4U, 0xb05858e8U, 0x85cfcf4aU,\n    0xbbd0d06bU, 0xc5efef2aU, 0x4faaaae5U, 0xedfbfb16U,\n    0x864343c5U, 0x9a4d4dd7U, 0x66333355U, 0x11858594U,\n    0x8a4545cfU, 0xe9f9f910U, 0x04020206U, 0xfe7f7f81U,\n    0xa05050f0U, 0x783c3c44U, 0x259f9fbaU, 0x4ba8a8e3U,\n    0xa25151f3U, 0x5da3a3feU, 0x804040c0U, 0x058f8f8aU,\n    0x3f9292adU, 0x219d9dbcU, 0x70383848U, 0xf1f5f504U,\n    0x63bcbcdfU, 0x77b6b6c1U, 0xafdada75U, 0x42212163U,\n    0x20101030U, 0xe5ffff1aU, 0xfdf3f30eU, 0xbfd2d26dU,\n    0x81cdcd4cU, 0x180c0c14U, 0x26131335U, 0xc3ecec2fU,\n    0xbe5f5fe1U, 0x359797a2U, 0x884444ccU, 0x2e171739U,\n    0x93c4c457U, 0x55a7a7f2U, 0xfc7e7e82U, 0x7a3d3d47U,\n    0xc86464acU, 0xba5d5de7U, 0x3219192bU, 0xe6737395U,\n    0xc06060a0U, 0x19818198U, 0x9e4f4fd1U, 0xa3dcdc7fU,\n    0x44222266U, 0x542a2a7eU, 0x3b9090abU, 0x0b888883U,\n    0x8c4646caU, 0xc7eeee29U, 0x6bb8b8d3U, 0x2814143cU,\n    0xa7dede79U, 0xbc5e5ee2U, 0x160b0b1dU, 0xaddbdb76U,\n    0xdbe0e03bU, 0x64323256U, 0x743a3a4eU, 0x140a0a1eU,\n    0x924949dbU, 0x0c06060aU, 0x4824246cU, 0xb85c5ce4U,\n    0x9fc2c25dU, 0xbdd3d36eU, 0x43acacefU, 0xc46262a6U,\n    0x399191a8U, 0x319595a4U, 0xd3e4e437U, 0xf279798bU,\n    0xd5e7e732U, 0x8bc8c843U, 0x6e373759U, 0xda6d6db7U,\n    0x018d8d8cU, 0xb1d5d564U, 0x9c4e4ed2U, 0x49a9a9e0U,\n    0xd86c6cb4U, 0xac5656faU, 0xf3f4f407U, 0xcfeaea25U,\n    0xca6565afU, 0xf47a7a8eU, 0x47aeaee9U, 0x10080818U,\n    0x6fbabad5U, 0xf0787888U, 0x4a25256fU, 0x5c2e2e72U,\n    0x381c1c24U, 0x57a6a6f1U, 0x73b4b4c7U, 0x97c6c651U,\n    0xcbe8e823U, 0xa1dddd7cU, 0xe874749cU, 0x3e1f1f21U,\n    0x964b4bddU, 0x61bdbddcU, 0x0d8b8b86U, 0x0f8a8a85U,\n    0xe0707090U, 0x7c3e3e42U, 0x71b5b5c4U, 0xcc6666aaU,\n    0x904848d8U, 0x06030305U, 0xf7f6f601U, 0x1c0e0e12U,\n    0xc26161a3U, 0x6a35355fU, 0xae5757f9U, 0x69b9b9d0U,\n    0x17868691U, 0x99c1c158U, 0x3a1d1d27U, 0x279e9eb9U,\n    0xd9e1e138U, 0xebf8f813U, 0x2b9898b3U, 0x22111133U,\n    0xd26969bbU, 0xa9d9d970U, 0x078e8e89U, 0x339494a7U,\n    0x2d9b9bb6U, 0x3c1e1e22U, 0x15878792U, 0xc9e9e920U,\n    0x87cece49U, 0xaa5555ffU, 0x50282878U, 0xa5dfdf7aU,\n    0x038c8c8fU, 0x59a1a1f8U, 0x09898980U, 0x1a0d0d17U,\n    0x65bfbfdaU, 0xd7e6e631U, 0x844242c6U, 0xd06868b8U,\n    0x824141c3U, 0x299999b0U, 0x5a2d2d77U, 0x1e0f0f11U,\n    0x7bb0b0cbU, 0xa85454fcU, 0x6dbbbbd6U, 0x2c16163aU,\n};\nstatic const u32 Te1[256] = {\n    0xa5c66363U, 0x84f87c7cU, 0x99ee7777U, 0x8df67b7bU,\n    0x0dfff2f2U, 0xbdd66b6bU, 0xb1de6f6fU, 0x5491c5c5U,\n    0x50603030U, 0x03020101U, 0xa9ce6767U, 0x7d562b2bU,\n    0x19e7fefeU, 0x62b5d7d7U, 0xe64dababU, 0x9aec7676U,\n    0x458fcacaU, 0x9d1f8282U, 0x4089c9c9U, 0x87fa7d7dU,\n    0x15effafaU, 0xebb25959U, 0xc98e4747U, 0x0bfbf0f0U,\n    0xec41adadU, 0x67b3d4d4U, 0xfd5fa2a2U, 0xea45afafU,\n    0xbf239c9cU, 0xf753a4a4U, 0x96e47272U, 0x5b9bc0c0U,\n    0xc275b7b7U, 0x1ce1fdfdU, 0xae3d9393U, 0x6a4c2626U,\n    0x5a6c3636U, 0x417e3f3fU, 0x02f5f7f7U, 0x4f83ccccU,\n    0x5c683434U, 0xf451a5a5U, 0x34d1e5e5U, 0x08f9f1f1U,\n    0x93e27171U, 0x73abd8d8U, 0x53623131U, 0x3f2a1515U,\n    0x0c080404U, 0x5295c7c7U, 0x65462323U, 0x5e9dc3c3U,\n    0x28301818U, 0xa1379696U, 0x0f0a0505U, 0xb52f9a9aU,\n    0x090e0707U, 0x36241212U, 0x9b1b8080U, 0x3ddfe2e2U,\n    0x26cdebebU, 0x694e2727U, 0xcd7fb2b2U, 0x9fea7575U,\n    0x1b120909U, 0x9e1d8383U, 0x74582c2cU, 0x2e341a1aU,\n    0x2d361b1bU, 0xb2dc6e6eU, 0xeeb45a5aU, 0xfb5ba0a0U,\n    0xf6a45252U, 0x4d763b3bU, 0x61b7d6d6U, 0xce7db3b3U,\n    0x7b522929U, 0x3edde3e3U, 0x715e2f2fU, 0x97138484U,\n    0xf5a65353U, 0x68b9d1d1U, 0x00000000U, 0x2cc1ededU,\n    0x60402020U, 0x1fe3fcfcU, 0xc879b1b1U, 0xedb65b5bU,\n    0xbed46a6aU, 0x468dcbcbU, 0xd967bebeU, 0x4b723939U,\n    0xde944a4aU, 0xd4984c4cU, 0xe8b05858U, 0x4a85cfcfU,\n    0x6bbbd0d0U, 0x2ac5efefU, 0xe54faaaaU, 0x16edfbfbU,\n    0xc5864343U, 0xd79a4d4dU, 0x55663333U, 0x94118585U,\n    0xcf8a4545U, 0x10e9f9f9U, 0x06040202U, 0x81fe7f7fU,\n    0xf0a05050U, 0x44783c3cU, 0xba259f9fU, 0xe34ba8a8U,\n    0xf3a25151U, 0xfe5da3a3U, 0xc0804040U, 0x8a058f8fU,\n    0xad3f9292U, 0xbc219d9dU, 0x48703838U, 0x04f1f5f5U,\n    0xdf63bcbcU, 0xc177b6b6U, 0x75afdadaU, 0x63422121U,\n    0x30201010U, 0x1ae5ffffU, 0x0efdf3f3U, 0x6dbfd2d2U,\n    0x4c81cdcdU, 0x14180c0cU, 0x35261313U, 0x2fc3ececU,\n    0xe1be5f5fU, 0xa2359797U, 0xcc884444U, 0x392e1717U,\n    0x5793c4c4U, 0xf255a7a7U, 0x82fc7e7eU, 0x477a3d3dU,\n    0xacc86464U, 0xe7ba5d5dU, 0x2b321919U, 0x95e67373U,\n    0xa0c06060U, 0x98198181U, 0xd19e4f4fU, 0x7fa3dcdcU,\n    0x66442222U, 0x7e542a2aU, 0xab3b9090U, 0x830b8888U,\n    0xca8c4646U, 0x29c7eeeeU, 0xd36bb8b8U, 0x3c281414U,\n    0x79a7dedeU, 0xe2bc5e5eU, 0x1d160b0bU, 0x76addbdbU,\n    0x3bdbe0e0U, 0x56643232U, 0x4e743a3aU, 0x1e140a0aU,\n    0xdb924949U, 0x0a0c0606U, 0x6c482424U, 0xe4b85c5cU,\n    0x5d9fc2c2U, 0x6ebdd3d3U, 0xef43acacU, 0xa6c46262U,\n    0xa8399191U, 0xa4319595U, 0x37d3e4e4U, 0x8bf27979U,\n    0x32d5e7e7U, 0x438bc8c8U, 0x596e3737U, 0xb7da6d6dU,\n    0x8c018d8dU, 0x64b1d5d5U, 0xd29c4e4eU, 0xe049a9a9U,\n    0xb4d86c6cU, 0xfaac5656U, 0x07f3f4f4U, 0x25cfeaeaU,\n    0xafca6565U, 0x8ef47a7aU, 0xe947aeaeU, 0x18100808U,\n    0xd56fbabaU, 0x88f07878U, 0x6f4a2525U, 0x725c2e2eU,\n    0x24381c1cU, 0xf157a6a6U, 0xc773b4b4U, 0x5197c6c6U,\n    0x23cbe8e8U, 0x7ca1ddddU, 0x9ce87474U, 0x213e1f1fU,\n    0xdd964b4bU, 0xdc61bdbdU, 0x860d8b8bU, 0x850f8a8aU,\n    0x90e07070U, 0x427c3e3eU, 0xc471b5b5U, 0xaacc6666U,\n    0xd8904848U, 0x05060303U, 0x01f7f6f6U, 0x121c0e0eU,\n    0xa3c26161U, 0x5f6a3535U, 0xf9ae5757U, 0xd069b9b9U,\n    0x91178686U, 0x5899c1c1U, 0x273a1d1dU, 0xb9279e9eU,\n    0x38d9e1e1U, 0x13ebf8f8U, 0xb32b9898U, 0x33221111U,\n    0xbbd26969U, 0x70a9d9d9U, 0x89078e8eU, 0xa7339494U,\n    0xb62d9b9bU, 0x223c1e1eU, 0x92158787U, 0x20c9e9e9U,\n    0x4987ceceU, 0xffaa5555U, 0x78502828U, 0x7aa5dfdfU,\n    0x8f038c8cU, 0xf859a1a1U, 0x80098989U, 0x171a0d0dU,\n    0xda65bfbfU, 0x31d7e6e6U, 0xc6844242U, 0xb8d06868U,\n    0xc3824141U, 0xb0299999U, 0x775a2d2dU, 0x111e0f0fU,\n    0xcb7bb0b0U, 0xfca85454U, 0xd66dbbbbU, 0x3a2c1616U,\n};\nstatic const u32 Te2[256] = {\n    0x63a5c663U, 0x7c84f87cU, 0x7799ee77U, 0x7b8df67bU,\n    0xf20dfff2U, 0x6bbdd66bU, 0x6fb1de6fU, 0xc55491c5U,\n    0x30506030U, 0x01030201U, 0x67a9ce67U, 0x2b7d562bU,\n    0xfe19e7feU, 0xd762b5d7U, 0xabe64dabU, 0x769aec76U,\n    0xca458fcaU, 0x829d1f82U, 0xc94089c9U, 0x7d87fa7dU,\n    0xfa15effaU, 0x59ebb259U, 0x47c98e47U, 0xf00bfbf0U,\n    0xadec41adU, 0xd467b3d4U, 0xa2fd5fa2U, 0xafea45afU,\n    0x9cbf239cU, 0xa4f753a4U, 0x7296e472U, 0xc05b9bc0U,\n    0xb7c275b7U, 0xfd1ce1fdU, 0x93ae3d93U, 0x266a4c26U,\n    0x365a6c36U, 0x3f417e3fU, 0xf702f5f7U, 0xcc4f83ccU,\n    0x345c6834U, 0xa5f451a5U, 0xe534d1e5U, 0xf108f9f1U,\n    0x7193e271U, 0xd873abd8U, 0x31536231U, 0x153f2a15U,\n    0x040c0804U, 0xc75295c7U, 0x23654623U, 0xc35e9dc3U,\n    0x18283018U, 0x96a13796U, 0x050f0a05U, 0x9ab52f9aU,\n    0x07090e07U, 0x12362412U, 0x809b1b80U, 0xe23ddfe2U,\n    0xeb26cdebU, 0x27694e27U, 0xb2cd7fb2U, 0x759fea75U,\n    0x091b1209U, 0x839e1d83U, 0x2c74582cU, 0x1a2e341aU,\n    0x1b2d361bU, 0x6eb2dc6eU, 0x5aeeb45aU, 0xa0fb5ba0U,\n    0x52f6a452U, 0x3b4d763bU, 0xd661b7d6U, 0xb3ce7db3U,\n    0x297b5229U, 0xe33edde3U, 0x2f715e2fU, 0x84971384U,\n    0x53f5a653U, 0xd168b9d1U, 0x00000000U, 0xed2cc1edU,\n    0x20604020U, 0xfc1fe3fcU, 0xb1c879b1U, 0x5bedb65bU,\n    0x6abed46aU, 0xcb468dcbU, 0xbed967beU, 0x394b7239U,\n    0x4ade944aU, 0x4cd4984cU, 0x58e8b058U, 0xcf4a85cfU,\n    0xd06bbbd0U, 0xef2ac5efU, 0xaae54faaU, 0xfb16edfbU,\n    0x43c58643U, 0x4dd79a4dU, 0x33556633U, 0x85941185U,\n    0x45cf8a45U, 0xf910e9f9U, 0x02060402U, 0x7f81fe7fU,\n    0x50f0a050U, 0x3c44783cU, 0x9fba259fU, 0xa8e34ba8U,\n    0x51f3a251U, 0xa3fe5da3U, 0x40c08040U, 0x8f8a058fU,\n    0x92ad3f92U, 0x9dbc219dU, 0x38487038U, 0xf504f1f5U,\n    0xbcdf63bcU, 0xb6c177b6U, 0xda75afdaU, 0x21634221U,\n    0x10302010U, 0xff1ae5ffU, 0xf30efdf3U, 0xd26dbfd2U,\n    0xcd4c81cdU, 0x0c14180cU, 0x13352613U, 0xec2fc3ecU,\n    0x5fe1be5fU, 0x97a23597U, 0x44cc8844U, 0x17392e17U,\n    0xc45793c4U, 0xa7f255a7U, 0x7e82fc7eU, 0x3d477a3dU,\n    0x64acc864U, 0x5de7ba5dU, 0x192b3219U, 0x7395e673U,\n    0x60a0c060U, 0x81981981U, 0x4fd19e4fU, 0xdc7fa3dcU,\n    0x22664422U, 0x2a7e542aU, 0x90ab3b90U, 0x88830b88U,\n    0x46ca8c46U, 0xee29c7eeU, 0xb8d36bb8U, 0x143c2814U,\n    0xde79a7deU, 0x5ee2bc5eU, 0x0b1d160bU, 0xdb76addbU,\n    0xe03bdbe0U, 0x32566432U, 0x3a4e743aU, 0x0a1e140aU,\n    0x49db9249U, 0x060a0c06U, 0x246c4824U, 0x5ce4b85cU,\n    0xc25d9fc2U, 0xd36ebdd3U, 0xacef43acU, 0x62a6c462U,\n    0x91a83991U, 0x95a43195U, 0xe437d3e4U, 0x798bf279U,\n    0xe732d5e7U, 0xc8438bc8U, 0x37596e37U, 0x6db7da6dU,\n    0x8d8c018dU, 0xd564b1d5U, 0x4ed29c4eU, 0xa9e049a9U,\n    0x6cb4d86cU, 0x56faac56U, 0xf407f3f4U, 0xea25cfeaU,\n    0x65afca65U, 0x7a8ef47aU, 0xaee947aeU, 0x08181008U,\n    0xbad56fbaU, 0x7888f078U, 0x256f4a25U, 0x2e725c2eU,\n    0x1c24381cU, 0xa6f157a6U, 0xb4c773b4U, 0xc65197c6U,\n    0xe823cbe8U, 0xdd7ca1ddU, 0x749ce874U, 0x1f213e1fU,\n    0x4bdd964bU, 0xbddc61bdU, 0x8b860d8bU, 0x8a850f8aU,\n    0x7090e070U, 0x3e427c3eU, 0xb5c471b5U, 0x66aacc66U,\n    0x48d89048U, 0x03050603U, 0xf601f7f6U, 0x0e121c0eU,\n    0x61a3c261U, 0x355f6a35U, 0x57f9ae57U, 0xb9d069b9U,\n    0x86911786U, 0xc15899c1U, 0x1d273a1dU, 0x9eb9279eU,\n    0xe138d9e1U, 0xf813ebf8U, 0x98b32b98U, 0x11332211U,\n    0x69bbd269U, 0xd970a9d9U, 0x8e89078eU, 0x94a73394U,\n    0x9bb62d9bU, 0x1e223c1eU, 0x87921587U, 0xe920c9e9U,\n    0xce4987ceU, 0x55ffaa55U, 0x28785028U, 0xdf7aa5dfU,\n    0x8c8f038cU, 0xa1f859a1U, 0x89800989U, 0x0d171a0dU,\n    0xbfda65bfU, 0xe631d7e6U, 0x42c68442U, 0x68b8d068U,\n    0x41c38241U, 0x99b02999U, 0x2d775a2dU, 0x0f111e0fU,\n    0xb0cb7bb0U, 0x54fca854U, 0xbbd66dbbU, 0x163a2c16U,\n};\nstatic const u32 Te3[256] = {\n    0x6363a5c6U, 0x7c7c84f8U, 0x777799eeU, 0x7b7b8df6U,\n    0xf2f20dffU, 0x6b6bbdd6U, 0x6f6fb1deU, 0xc5c55491U,\n    0x30305060U, 0x01010302U, 0x6767a9ceU, 0x2b2b7d56U,\n    0xfefe19e7U, 0xd7d762b5U, 0xababe64dU, 0x76769aecU,\n    0xcaca458fU, 0x82829d1fU, 0xc9c94089U, 0x7d7d87faU,\n    0xfafa15efU, 0x5959ebb2U, 0x4747c98eU, 0xf0f00bfbU,\n    0xadadec41U, 0xd4d467b3U, 0xa2a2fd5fU, 0xafafea45U,\n    0x9c9cbf23U, 0xa4a4f753U, 0x727296e4U, 0xc0c05b9bU,\n    0xb7b7c275U, 0xfdfd1ce1U, 0x9393ae3dU, 0x26266a4cU,\n    0x36365a6cU, 0x3f3f417eU, 0xf7f702f5U, 0xcccc4f83U,\n    0x34345c68U, 0xa5a5f451U, 0xe5e534d1U, 0xf1f108f9U,\n    0x717193e2U, 0xd8d873abU, 0x31315362U, 0x15153f2aU,\n    0x04040c08U, 0xc7c75295U, 0x23236546U, 0xc3c35e9dU,\n    0x18182830U, 0x9696a137U, 0x05050f0aU, 0x9a9ab52fU,\n    0x0707090eU, 0x12123624U, 0x80809b1bU, 0xe2e23ddfU,\n    0xebeb26cdU, 0x2727694eU, 0xb2b2cd7fU, 0x75759feaU,\n    0x09091b12U, 0x83839e1dU, 0x2c2c7458U, 0x1a1a2e34U,\n    0x1b1b2d36U, 0x6e6eb2dcU, 0x5a5aeeb4U, 0xa0a0fb5bU,\n    0x5252f6a4U, 0x3b3b4d76U, 0xd6d661b7U, 0xb3b3ce7dU,\n    0x29297b52U, 0xe3e33eddU, 0x2f2f715eU, 0x84849713U,\n    0x5353f5a6U, 0xd1d168b9U, 0x00000000U, 0xeded2cc1U,\n    0x20206040U, 0xfcfc1fe3U, 0xb1b1c879U, 0x5b5bedb6U,\n    0x6a6abed4U, 0xcbcb468dU, 0xbebed967U, 0x39394b72U,\n    0x4a4ade94U, 0x4c4cd498U, 0x5858e8b0U, 0xcfcf4a85U,\n    0xd0d06bbbU, 0xefef2ac5U, 0xaaaae54fU, 0xfbfb16edU,\n    0x4343c586U, 0x4d4dd79aU, 0x33335566U, 0x85859411U,\n    0x4545cf8aU, 0xf9f910e9U, 0x02020604U, 0x7f7f81feU,\n    0x5050f0a0U, 0x3c3c4478U, 0x9f9fba25U, 0xa8a8e34bU,\n    0x5151f3a2U, 0xa3a3fe5dU, 0x4040c080U, 0x8f8f8a05U,\n    0x9292ad3fU, 0x9d9dbc21U, 0x38384870U, 0xf5f504f1U,\n    0xbcbcdf63U, 0xb6b6c177U, 0xdada75afU, 0x21216342U,\n    0x10103020U, 0xffff1ae5U, 0xf3f30efdU, 0xd2d26dbfU,\n    0xcdcd4c81U, 0x0c0c1418U, 0x13133526U, 0xecec2fc3U,\n    0x5f5fe1beU, 0x9797a235U, 0x4444cc88U, 0x1717392eU,\n    0xc4c45793U, 0xa7a7f255U, 0x7e7e82fcU, 0x3d3d477aU,\n    0x6464acc8U, 0x5d5de7baU, 0x19192b32U, 0x737395e6U,\n    0x6060a0c0U, 0x81819819U, 0x4f4fd19eU, 0xdcdc7fa3U,\n    0x22226644U, 0x2a2a7e54U, 0x9090ab3bU, 0x8888830bU,\n    0x4646ca8cU, 0xeeee29c7U, 0xb8b8d36bU, 0x14143c28U,\n    0xdede79a7U, 0x5e5ee2bcU, 0x0b0b1d16U, 0xdbdb76adU,\n    0xe0e03bdbU, 0x32325664U, 0x3a3a4e74U, 0x0a0a1e14U,\n    0x4949db92U, 0x06060a0cU, 0x24246c48U, 0x5c5ce4b8U,\n    0xc2c25d9fU, 0xd3d36ebdU, 0xacacef43U, 0x6262a6c4U,\n    0x9191a839U, 0x9595a431U, 0xe4e437d3U, 0x79798bf2U,\n    0xe7e732d5U, 0xc8c8438bU, 0x3737596eU, 0x6d6db7daU,\n    0x8d8d8c01U, 0xd5d564b1U, 0x4e4ed29cU, 0xa9a9e049U,\n    0x6c6cb4d8U, 0x5656faacU, 0xf4f407f3U, 0xeaea25cfU,\n    0x6565afcaU, 0x7a7a8ef4U, 0xaeaee947U, 0x08081810U,\n    0xbabad56fU, 0x787888f0U, 0x25256f4aU, 0x2e2e725cU,\n    0x1c1c2438U, 0xa6a6f157U, 0xb4b4c773U, 0xc6c65197U,\n    0xe8e823cbU, 0xdddd7ca1U, 0x74749ce8U, 0x1f1f213eU,\n    0x4b4bdd96U, 0xbdbddc61U, 0x8b8b860dU, 0x8a8a850fU,\n    0x707090e0U, 0x3e3e427cU, 0xb5b5c471U, 0x6666aaccU,\n    0x4848d890U, 0x03030506U, 0xf6f601f7U, 0x0e0e121cU,\n    0x6161a3c2U, 0x35355f6aU, 0x5757f9aeU, 0xb9b9d069U,\n    0x86869117U, 0xc1c15899U, 0x1d1d273aU, 0x9e9eb927U,\n    0xe1e138d9U, 0xf8f813ebU, 0x9898b32bU, 0x11113322U,\n    0x6969bbd2U, 0xd9d970a9U, 0x8e8e8907U, 0x9494a733U,\n    0x9b9bb62dU, 0x1e1e223cU, 0x87879215U, 0xe9e920c9U,\n    0xcece4987U, 0x5555ffaaU, 0x28287850U, 0xdfdf7aa5U,\n    0x8c8c8f03U, 0xa1a1f859U, 0x89898009U, 0x0d0d171aU,\n    0xbfbfda65U, 0xe6e631d7U, 0x4242c684U, 0x6868b8d0U,\n    0x4141c382U, 0x9999b029U, 0x2d2d775aU, 0x0f0f111eU,\n    0xb0b0cb7bU, 0x5454fca8U, 0xbbbbd66dU, 0x16163a2cU,\n};\n\nstatic const u32 Td0[256] = {\n    0x51f4a750U, 0x7e416553U, 0x1a17a4c3U, 0x3a275e96U,\n    0x3bab6bcbU, 0x1f9d45f1U, 0xacfa58abU, 0x4be30393U,\n    0x2030fa55U, 0xad766df6U, 0x88cc7691U, 0xf5024c25U,\n    0x4fe5d7fcU, 0xc52acbd7U, 0x26354480U, 0xb562a38fU,\n    0xdeb15a49U, 0x25ba1b67U, 0x45ea0e98U, 0x5dfec0e1U,\n    0xc32f7502U, 0x814cf012U, 0x8d4697a3U, 0x6bd3f9c6U,\n    0x038f5fe7U, 0x15929c95U, 0xbf6d7aebU, 0x955259daU,\n    0xd4be832dU, 0x587421d3U, 0x49e06929U, 0x8ec9c844U,\n    0x75c2896aU, 0xf48e7978U, 0x99583e6bU, 0x27b971ddU,\n    0xbee14fb6U, 0xf088ad17U, 0xc920ac66U, 0x7dce3ab4U,\n    0x63df4a18U, 0xe51a3182U, 0x97513360U, 0x62537f45U,\n    0xb16477e0U, 0xbb6bae84U, 0xfe81a01cU, 0xf9082b94U,\n    0x70486858U, 0x8f45fd19U, 0x94de6c87U, 0x527bf8b7U,\n    0xab73d323U, 0x724b02e2U, 0xe31f8f57U, 0x6655ab2aU,\n    0xb2eb2807U, 0x2fb5c203U, 0x86c57b9aU, 0xd33708a5U,\n    0x302887f2U, 0x23bfa5b2U, 0x02036abaU, 0xed16825cU,\n    0x8acf1c2bU, 0xa779b492U, 0xf307f2f0U, 0x4e69e2a1U,\n    0x65daf4cdU, 0x0605bed5U, 0xd134621fU, 0xc4a6fe8aU,\n    0x342e539dU, 0xa2f355a0U, 0x058ae132U, 0xa4f6eb75U,\n    0x0b83ec39U, 0x4060efaaU, 0x5e719f06U, 0xbd6e1051U,\n    0x3e218af9U, 0x96dd063dU, 0xdd3e05aeU, 0x4de6bd46U,\n    0x91548db5U, 0x71c45d05U, 0x0406d46fU, 0x605015ffU,\n    0x1998fb24U, 0xd6bde997U, 0x894043ccU, 0x67d99e77U,\n    0xb0e842bdU, 0x07898b88U, 0xe7195b38U, 0x79c8eedbU,\n    0xa17c0a47U, 0x7c420fe9U, 0xf8841ec9U, 0x00000000U,\n    0x09808683U, 0x322bed48U, 0x1e1170acU, 0x6c5a724eU,\n    0xfd0efffbU, 0x0f853856U, 0x3daed51eU, 0x362d3927U,\n    0x0a0fd964U, 0x685ca621U, 0x9b5b54d1U, 0x24362e3aU,\n    0x0c0a67b1U, 0x9357e70fU, 0xb4ee96d2U, 0x1b9b919eU,\n    0x80c0c54fU, 0x61dc20a2U, 0x5a774b69U, 0x1c121a16U,\n    0xe293ba0aU, 0xc0a02ae5U, 0x3c22e043U, 0x121b171dU,\n    0x0e090d0bU, 0xf28bc7adU, 0x2db6a8b9U, 0x141ea9c8U,\n    0x57f11985U, 0xaf75074cU, 0xee99ddbbU, 0xa37f60fdU,\n    0xf701269fU, 0x5c72f5bcU, 0x44663bc5U, 0x5bfb7e34U,\n    0x8b432976U, 0xcb23c6dcU, 0xb6edfc68U, 0xb8e4f163U,\n    0xd731dccaU, 0x42638510U, 0x13972240U, 0x84c61120U,\n    0x854a247dU, 0xd2bb3df8U, 0xaef93211U, 0xc729a16dU,\n    0x1d9e2f4bU, 0xdcb230f3U, 0x0d8652ecU, 0x77c1e3d0U,\n    0x2bb3166cU, 0xa970b999U, 0x119448faU, 0x47e96422U,\n    0xa8fc8cc4U, 0xa0f03f1aU, 0x567d2cd8U, 0x223390efU,\n    0x87494ec7U, 0xd938d1c1U, 0x8ccaa2feU, 0x98d40b36U,\n    0xa6f581cfU, 0xa57ade28U, 0xdab78e26U, 0x3fadbfa4U,\n    0x2c3a9de4U, 0x5078920dU, 0x6a5fcc9bU, 0x547e4662U,\n    0xf68d13c2U, 0x90d8b8e8U, 0x2e39f75eU, 0x82c3aff5U,\n    0x9f5d80beU, 0x69d0937cU, 0x6fd52da9U, 0xcf2512b3U,\n    0xc8ac993bU, 0x10187da7U, 0xe89c636eU, 0xdb3bbb7bU,\n    0xcd267809U, 0x6e5918f4U, 0xec9ab701U, 0x834f9aa8U,\n    0xe6956e65U, 0xaaffe67eU, 0x21bccf08U, 0xef15e8e6U,\n    0xbae79bd9U, 0x4a6f36ceU, 0xea9f09d4U, 0x29b07cd6U,\n    0x31a4b2afU, 0x2a3f2331U, 0xc6a59430U, 0x35a266c0U,\n    0x744ebc37U, 0xfc82caa6U, 0xe090d0b0U, 0x33a7d815U,\n    0xf104984aU, 0x41ecdaf7U, 0x7fcd500eU, 0x1791f62fU,\n    0x764dd68dU, 0x43efb04dU, 0xccaa4d54U, 0xe49604dfU,\n    0x9ed1b5e3U, 0x4c6a881bU, 0xc12c1fb8U, 0x4665517fU,\n    0x9d5eea04U, 0x018c355dU, 0xfa877473U, 0xfb0b412eU,\n    0xb3671d5aU, 0x92dbd252U, 0xe9105633U, 0x6dd64713U,\n    0x9ad7618cU, 0x37a10c7aU, 0x59f8148eU, 0xeb133c89U,\n    0xcea927eeU, 0xb761c935U, 0xe11ce5edU, 0x7a47b13cU,\n    0x9cd2df59U, 0x55f2733fU, 0x1814ce79U, 0x73c737bfU,\n    0x53f7cdeaU, 0x5ffdaa5bU, 0xdf3d6f14U, 0x7844db86U,\n    0xcaaff381U, 0xb968c43eU, 0x3824342cU, 0xc2a3405fU,\n    0x161dc372U, 0xbce2250cU, 0x283c498bU, 0xff0d9541U,\n    0x39a80171U, 0x080cb3deU, 0xd8b4e49cU, 0x6456c190U,\n    0x7bcb8461U, 0xd532b670U, 0x486c5c74U, 0xd0b85742U,\n};\nstatic const u32 Td1[256] = {\n    0x5051f4a7U, 0x537e4165U, 0xc31a17a4U, 0x963a275eU,\n    0xcb3bab6bU, 0xf11f9d45U, 0xabacfa58U, 0x934be303U,\n    0x552030faU, 0xf6ad766dU, 0x9188cc76U, 0x25f5024cU,\n    0xfc4fe5d7U, 0xd7c52acbU, 0x80263544U, 0x8fb562a3U,\n    0x49deb15aU, 0x6725ba1bU, 0x9845ea0eU, 0xe15dfec0U,\n    0x02c32f75U, 0x12814cf0U, 0xa38d4697U, 0xc66bd3f9U,\n    0xe7038f5fU, 0x9515929cU, 0xebbf6d7aU, 0xda955259U,\n    0x2dd4be83U, 0xd3587421U, 0x2949e069U, 0x448ec9c8U,\n    0x6a75c289U, 0x78f48e79U, 0x6b99583eU, 0xdd27b971U,\n    0xb6bee14fU, 0x17f088adU, 0x66c920acU, 0xb47dce3aU,\n    0x1863df4aU, 0x82e51a31U, 0x60975133U, 0x4562537fU,\n    0xe0b16477U, 0x84bb6baeU, 0x1cfe81a0U, 0x94f9082bU,\n    0x58704868U, 0x198f45fdU, 0x8794de6cU, 0xb7527bf8U,\n    0x23ab73d3U, 0xe2724b02U, 0x57e31f8fU, 0x2a6655abU,\n    0x07b2eb28U, 0x032fb5c2U, 0x9a86c57bU, 0xa5d33708U,\n    0xf2302887U, 0xb223bfa5U, 0xba02036aU, 0x5ced1682U,\n    0x2b8acf1cU, 0x92a779b4U, 0xf0f307f2U, 0xa14e69e2U,\n    0xcd65daf4U, 0xd50605beU, 0x1fd13462U, 0x8ac4a6feU,\n    0x9d342e53U, 0xa0a2f355U, 0x32058ae1U, 0x75a4f6ebU,\n    0x390b83ecU, 0xaa4060efU, 0x065e719fU, 0x51bd6e10U,\n    0xf93e218aU, 0x3d96dd06U, 0xaedd3e05U, 0x464de6bdU,\n    0xb591548dU, 0x0571c45dU, 0x6f0406d4U, 0xff605015U,\n    0x241998fbU, 0x97d6bde9U, 0xcc894043U, 0x7767d99eU,\n    0xbdb0e842U, 0x8807898bU, 0x38e7195bU, 0xdb79c8eeU,\n    0x47a17c0aU, 0xe97c420fU, 0xc9f8841eU, 0x00000000U,\n    0x83098086U, 0x48322bedU, 0xac1e1170U, 0x4e6c5a72U,\n    0xfbfd0effU, 0x560f8538U, 0x1e3daed5U, 0x27362d39U,\n    0x640a0fd9U, 0x21685ca6U, 0xd19b5b54U, 0x3a24362eU,\n    0xb10c0a67U, 0x0f9357e7U, 0xd2b4ee96U, 0x9e1b9b91U,\n    0x4f80c0c5U, 0xa261dc20U, 0x695a774bU, 0x161c121aU,\n    0x0ae293baU, 0xe5c0a02aU, 0x433c22e0U, 0x1d121b17U,\n    0x0b0e090dU, 0xadf28bc7U, 0xb92db6a8U, 0xc8141ea9U,\n    0x8557f119U, 0x4caf7507U, 0xbbee99ddU, 0xfda37f60U,\n    0x9ff70126U, 0xbc5c72f5U, 0xc544663bU, 0x345bfb7eU,\n    0x768b4329U, 0xdccb23c6U, 0x68b6edfcU, 0x63b8e4f1U,\n    0xcad731dcU, 0x10426385U, 0x40139722U, 0x2084c611U,\n    0x7d854a24U, 0xf8d2bb3dU, 0x11aef932U, 0x6dc729a1U,\n    0x4b1d9e2fU, 0xf3dcb230U, 0xec0d8652U, 0xd077c1e3U,\n    0x6c2bb316U, 0x99a970b9U, 0xfa119448U, 0x2247e964U,\n    0xc4a8fc8cU, 0x1aa0f03fU, 0xd8567d2cU, 0xef223390U,\n    0xc787494eU, 0xc1d938d1U, 0xfe8ccaa2U, 0x3698d40bU,\n    0xcfa6f581U, 0x28a57adeU, 0x26dab78eU, 0xa43fadbfU,\n    0xe42c3a9dU, 0x0d507892U, 0x9b6a5fccU, 0x62547e46U,\n    0xc2f68d13U, 0xe890d8b8U, 0x5e2e39f7U, 0xf582c3afU,\n    0xbe9f5d80U, 0x7c69d093U, 0xa96fd52dU, 0xb3cf2512U,\n    0x3bc8ac99U, 0xa710187dU, 0x6ee89c63U, 0x7bdb3bbbU,\n    0x09cd2678U, 0xf46e5918U, 0x01ec9ab7U, 0xa8834f9aU,\n    0x65e6956eU, 0x7eaaffe6U, 0x0821bccfU, 0xe6ef15e8U,\n    0xd9bae79bU, 0xce4a6f36U, 0xd4ea9f09U, 0xd629b07cU,\n    0xaf31a4b2U, 0x312a3f23U, 0x30c6a594U, 0xc035a266U,\n    0x37744ebcU, 0xa6fc82caU, 0xb0e090d0U, 0x1533a7d8U,\n    0x4af10498U, 0xf741ecdaU, 0x0e7fcd50U, 0x2f1791f6U,\n    0x8d764dd6U, 0x4d43efb0U, 0x54ccaa4dU, 0xdfe49604U,\n    0xe39ed1b5U, 0x1b4c6a88U, 0xb8c12c1fU, 0x7f466551U,\n    0x049d5eeaU, 0x5d018c35U, 0x73fa8774U, 0x2efb0b41U,\n    0x5ab3671dU, 0x5292dbd2U, 0x33e91056U, 0x136dd647U,\n    0x8c9ad761U, 0x7a37a10cU, 0x8e59f814U, 0x89eb133cU,\n    0xeecea927U, 0x35b761c9U, 0xede11ce5U, 0x3c7a47b1U,\n    0x599cd2dfU, 0x3f55f273U, 0x791814ceU, 0xbf73c737U,\n    0xea53f7cdU, 0x5b5ffdaaU, 0x14df3d6fU, 0x867844dbU,\n    0x81caaff3U, 0x3eb968c4U, 0x2c382434U, 0x5fc2a340U,\n    0x72161dc3U, 0x0cbce225U, 0x8b283c49U, 0x41ff0d95U,\n    0x7139a801U, 0xde080cb3U, 0x9cd8b4e4U, 0x906456c1U,\n    0x617bcb84U, 0x70d532b6U, 0x74486c5cU, 0x42d0b857U,\n};\nstatic const u32 Td2[256] = {\n    0xa75051f4U, 0x65537e41U, 0xa4c31a17U, 0x5e963a27U,\n    0x6bcb3babU, 0x45f11f9dU, 0x58abacfaU, 0x03934be3U,\n    0xfa552030U, 0x6df6ad76U, 0x769188ccU, 0x4c25f502U,\n    0xd7fc4fe5U, 0xcbd7c52aU, 0x44802635U, 0xa38fb562U,\n    0x5a49deb1U, 0x1b6725baU, 0x0e9845eaU, 0xc0e15dfeU,\n    0x7502c32fU, 0xf012814cU, 0x97a38d46U, 0xf9c66bd3U,\n    0x5fe7038fU, 0x9c951592U, 0x7aebbf6dU, 0x59da9552U,\n    0x832dd4beU, 0x21d35874U, 0x692949e0U, 0xc8448ec9U,\n    0x896a75c2U, 0x7978f48eU, 0x3e6b9958U, 0x71dd27b9U,\n    0x4fb6bee1U, 0xad17f088U, 0xac66c920U, 0x3ab47dceU,\n    0x4a1863dfU, 0x3182e51aU, 0x33609751U, 0x7f456253U,\n    0x77e0b164U, 0xae84bb6bU, 0xa01cfe81U, 0x2b94f908U,\n    0x68587048U, 0xfd198f45U, 0x6c8794deU, 0xf8b7527bU,\n    0xd323ab73U, 0x02e2724bU, 0x8f57e31fU, 0xab2a6655U,\n    0x2807b2ebU, 0xc2032fb5U, 0x7b9a86c5U, 0x08a5d337U,\n    0x87f23028U, 0xa5b223bfU, 0x6aba0203U, 0x825ced16U,\n    0x1c2b8acfU, 0xb492a779U, 0xf2f0f307U, 0xe2a14e69U,\n    0xf4cd65daU, 0xbed50605U, 0x621fd134U, 0xfe8ac4a6U,\n    0x539d342eU, 0x55a0a2f3U, 0xe132058aU, 0xeb75a4f6U,\n    0xec390b83U, 0xefaa4060U, 0x9f065e71U, 0x1051bd6eU,\n    0x8af93e21U, 0x063d96ddU, 0x05aedd3eU, 0xbd464de6U,\n    0x8db59154U, 0x5d0571c4U, 0xd46f0406U, 0x15ff6050U,\n    0xfb241998U, 0xe997d6bdU, 0x43cc8940U, 0x9e7767d9U,\n    0x42bdb0e8U, 0x8b880789U, 0x5b38e719U, 0xeedb79c8U,\n    0x0a47a17cU, 0x0fe97c42U, 0x1ec9f884U, 0x00000000U,\n    0x86830980U, 0xed48322bU, 0x70ac1e11U, 0x724e6c5aU,\n    0xfffbfd0eU, 0x38560f85U, 0xd51e3daeU, 0x3927362dU,\n    0xd9640a0fU, 0xa621685cU, 0x54d19b5bU, 0x2e3a2436U,\n    0x67b10c0aU, 0xe70f9357U, 0x96d2b4eeU, 0x919e1b9bU,\n    0xc54f80c0U, 0x20a261dcU, 0x4b695a77U, 0x1a161c12U,\n    0xba0ae293U, 0x2ae5c0a0U, 0xe0433c22U, 0x171d121bU,\n    0x0d0b0e09U, 0xc7adf28bU, 0xa8b92db6U, 0xa9c8141eU,\n    0x198557f1U, 0x074caf75U, 0xddbbee99U, 0x60fda37fU,\n    0x269ff701U, 0xf5bc5c72U, 0x3bc54466U, 0x7e345bfbU,\n    0x29768b43U, 0xc6dccb23U, 0xfc68b6edU, 0xf163b8e4U,\n    0xdccad731U, 0x85104263U, 0x22401397U, 0x112084c6U,\n    0x247d854aU, 0x3df8d2bbU, 0x3211aef9U, 0xa16dc729U,\n    0x2f4b1d9eU, 0x30f3dcb2U, 0x52ec0d86U, 0xe3d077c1U,\n    0x166c2bb3U, 0xb999a970U, 0x48fa1194U, 0x642247e9U,\n    0x8cc4a8fcU, 0x3f1aa0f0U, 0x2cd8567dU, 0x90ef2233U,\n    0x4ec78749U, 0xd1c1d938U, 0xa2fe8ccaU, 0x0b3698d4U,\n    0x81cfa6f5U, 0xde28a57aU, 0x8e26dab7U, 0xbfa43fadU,\n    0x9de42c3aU, 0x920d5078U, 0xcc9b6a5fU, 0x4662547eU,\n    0x13c2f68dU, 0xb8e890d8U, 0xf75e2e39U, 0xaff582c3U,\n    0x80be9f5dU, 0x937c69d0U, 0x2da96fd5U, 0x12b3cf25U,\n    0x993bc8acU, 0x7da71018U, 0x636ee89cU, 0xbb7bdb3bU,\n    0x7809cd26U, 0x18f46e59U, 0xb701ec9aU, 0x9aa8834fU,\n    0x6e65e695U, 0xe67eaaffU, 0xcf0821bcU, 0xe8e6ef15U,\n    0x9bd9bae7U, 0x36ce4a6fU, 0x09d4ea9fU, 0x7cd629b0U,\n    0xb2af31a4U, 0x23312a3fU, 0x9430c6a5U, 0x66c035a2U,\n    0xbc37744eU, 0xcaa6fc82U, 0xd0b0e090U, 0xd81533a7U,\n    0x984af104U, 0xdaf741ecU, 0x500e7fcdU, 0xf62f1791U,\n    0xd68d764dU, 0xb04d43efU, 0x4d54ccaaU, 0x04dfe496U,\n    0xb5e39ed1U, 0x881b4c6aU, 0x1fb8c12cU, 0x517f4665U,\n    0xea049d5eU, 0x355d018cU, 0x7473fa87U, 0x412efb0bU,\n    0x1d5ab367U, 0xd25292dbU, 0x5633e910U, 0x47136dd6U,\n    0x618c9ad7U, 0x0c7a37a1U, 0x148e59f8U, 0x3c89eb13U,\n    0x27eecea9U, 0xc935b761U, 0xe5ede11cU, 0xb13c7a47U,\n    0xdf599cd2U, 0x733f55f2U, 0xce791814U, 0x37bf73c7U,\n    0xcdea53f7U, 0xaa5b5ffdU, 0x6f14df3dU, 0xdb867844U,\n    0xf381caafU, 0xc43eb968U, 0x342c3824U, 0x405fc2a3U,\n    0xc372161dU, 0x250cbce2U, 0x498b283cU, 0x9541ff0dU,\n    0x017139a8U, 0xb3de080cU, 0xe49cd8b4U, 0xc1906456U,\n    0x84617bcbU, 0xb670d532U, 0x5c74486cU, 0x5742d0b8U,\n};\nstatic const u32 Td3[256] = {\n    0xf4a75051U, 0x4165537eU, 0x17a4c31aU, 0x275e963aU,\n    0xab6bcb3bU, 0x9d45f11fU, 0xfa58abacU, 0xe303934bU,\n    0x30fa5520U, 0x766df6adU, 0xcc769188U, 0x024c25f5U,\n    0xe5d7fc4fU, 0x2acbd7c5U, 0x35448026U, 0x62a38fb5U,\n    0xb15a49deU, 0xba1b6725U, 0xea0e9845U, 0xfec0e15dU,\n    0x2f7502c3U, 0x4cf01281U, 0x4697a38dU, 0xd3f9c66bU,\n    0x8f5fe703U, 0x929c9515U, 0x6d7aebbfU, 0x5259da95U,\n    0xbe832dd4U, 0x7421d358U, 0xe0692949U, 0xc9c8448eU,\n    0xc2896a75U, 0x8e7978f4U, 0x583e6b99U, 0xb971dd27U,\n    0xe14fb6beU, 0x88ad17f0U, 0x20ac66c9U, 0xce3ab47dU,\n    0xdf4a1863U, 0x1a3182e5U, 0x51336097U, 0x537f4562U,\n    0x6477e0b1U, 0x6bae84bbU, 0x81a01cfeU, 0x082b94f9U,\n    0x48685870U, 0x45fd198fU, 0xde6c8794U, 0x7bf8b752U,\n    0x73d323abU, 0x4b02e272U, 0x1f8f57e3U, 0x55ab2a66U,\n    0xeb2807b2U, 0xb5c2032fU, 0xc57b9a86U, 0x3708a5d3U,\n    0x2887f230U, 0xbfa5b223U, 0x036aba02U, 0x16825cedU,\n    0xcf1c2b8aU, 0x79b492a7U, 0x07f2f0f3U, 0x69e2a14eU,\n    0xdaf4cd65U, 0x05bed506U, 0x34621fd1U, 0xa6fe8ac4U,\n    0x2e539d34U, 0xf355a0a2U, 0x8ae13205U, 0xf6eb75a4U,\n    0x83ec390bU, 0x60efaa40U, 0x719f065eU, 0x6e1051bdU,\n    0x218af93eU, 0xdd063d96U, 0x3e05aeddU, 0xe6bd464dU,\n    0x548db591U, 0xc45d0571U, 0x06d46f04U, 0x5015ff60U,\n    0x98fb2419U, 0xbde997d6U, 0x4043cc89U, 0xd99e7767U,\n    0xe842bdb0U, 0x898b8807U, 0x195b38e7U, 0xc8eedb79U,\n    0x7c0a47a1U, 0x420fe97cU, 0x841ec9f8U, 0x00000000U,\n    0x80868309U, 0x2bed4832U, 0x1170ac1eU, 0x5a724e6cU,\n    0x0efffbfdU, 0x8538560fU, 0xaed51e3dU, 0x2d392736U,\n    0x0fd9640aU, 0x5ca62168U, 0x5b54d19bU, 0x362e3a24U,\n    0x0a67b10cU, 0x57e70f93U, 0xee96d2b4U, 0x9b919e1bU,\n    0xc0c54f80U, 0xdc20a261U, 0x774b695aU, 0x121a161cU,\n    0x93ba0ae2U, 0xa02ae5c0U, 0x22e0433cU, 0x1b171d12U,\n    0x090d0b0eU, 0x8bc7adf2U, 0xb6a8b92dU, 0x1ea9c814U,\n    0xf1198557U, 0x75074cafU, 0x99ddbbeeU, 0x7f60fda3U,\n    0x01269ff7U, 0x72f5bc5cU, 0x663bc544U, 0xfb7e345bU,\n    0x4329768bU, 0x23c6dccbU, 0xedfc68b6U, 0xe4f163b8U,\n    0x31dccad7U, 0x63851042U, 0x97224013U, 0xc6112084U,\n    0x4a247d85U, 0xbb3df8d2U, 0xf93211aeU, 0x29a16dc7U,\n    0x9e2f4b1dU, 0xb230f3dcU, 0x8652ec0dU, 0xc1e3d077U,\n    0xb3166c2bU, 0x70b999a9U, 0x9448fa11U, 0xe9642247U,\n    0xfc8cc4a8U, 0xf03f1aa0U, 0x7d2cd856U, 0x3390ef22U,\n    0x494ec787U, 0x38d1c1d9U, 0xcaa2fe8cU, 0xd40b3698U,\n    0xf581cfa6U, 0x7ade28a5U, 0xb78e26daU, 0xadbfa43fU,\n    0x3a9de42cU, 0x78920d50U, 0x5fcc9b6aU, 0x7e466254U,\n    0x8d13c2f6U, 0xd8b8e890U, 0x39f75e2eU, 0xc3aff582U,\n    0x5d80be9fU, 0xd0937c69U, 0xd52da96fU, 0x2512b3cfU,\n    0xac993bc8U, 0x187da710U, 0x9c636ee8U, 0x3bbb7bdbU,\n    0x267809cdU, 0x5918f46eU, 0x9ab701ecU, 0x4f9aa883U,\n    0x956e65e6U, 0xffe67eaaU, 0xbccf0821U, 0x15e8e6efU,\n    0xe79bd9baU, 0x6f36ce4aU, 0x9f09d4eaU, 0xb07cd629U,\n    0xa4b2af31U, 0x3f23312aU, 0xa59430c6U, 0xa266c035U,\n    0x4ebc3774U, 0x82caa6fcU, 0x90d0b0e0U, 0xa7d81533U,\n    0x04984af1U, 0xecdaf741U, 0xcd500e7fU, 0x91f62f17U,\n    0x4dd68d76U, 0xefb04d43U, 0xaa4d54ccU, 0x9604dfe4U,\n    0xd1b5e39eU, 0x6a881b4cU, 0x2c1fb8c1U, 0x65517f46U,\n    0x5eea049dU, 0x8c355d01U, 0x877473faU, 0x0b412efbU,\n    0x671d5ab3U, 0xdbd25292U, 0x105633e9U, 0xd647136dU,\n    0xd7618c9aU, 0xa10c7a37U, 0xf8148e59U, 0x133c89ebU,\n    0xa927eeceU, 0x61c935b7U, 0x1ce5ede1U, 0x47b13c7aU,\n    0xd2df599cU, 0xf2733f55U, 0x14ce7918U, 0xc737bf73U,\n    0xf7cdea53U, 0xfdaa5b5fU, 0x3d6f14dfU, 0x44db8678U,\n    0xaff381caU, 0x68c43eb9U, 0x24342c38U, 0xa3405fc2U,\n    0x1dc37216U, 0xe2250cbcU, 0x3c498b28U, 0x0d9541ffU,\n    0xa8017139U, 0x0cb3de08U, 0xb4e49cd8U, 0x56c19064U,\n    0xcb84617bU, 0x32b670d5U, 0x6c5c7448U, 0xb85742d0U,\n};\nstatic const u8 Td4[256] = {\n    0x52U, 0x09U, 0x6aU, 0xd5U, 0x30U, 0x36U, 0xa5U, 0x38U,\n    0xbfU, 0x40U, 0xa3U, 0x9eU, 0x81U, 0xf3U, 0xd7U, 0xfbU,\n    0x7cU, 0xe3U, 0x39U, 0x82U, 0x9bU, 0x2fU, 0xffU, 0x87U,\n    0x34U, 0x8eU, 0x43U, 0x44U, 0xc4U, 0xdeU, 0xe9U, 0xcbU,\n    0x54U, 0x7bU, 0x94U, 0x32U, 0xa6U, 0xc2U, 0x23U, 0x3dU,\n    0xeeU, 0x4cU, 0x95U, 0x0bU, 0x42U, 0xfaU, 0xc3U, 0x4eU,\n    0x08U, 0x2eU, 0xa1U, 0x66U, 0x28U, 0xd9U, 0x24U, 0xb2U,\n    0x76U, 0x5bU, 0xa2U, 0x49U, 0x6dU, 0x8bU, 0xd1U, 0x25U,\n    0x72U, 0xf8U, 0xf6U, 0x64U, 0x86U, 0x68U, 0x98U, 0x16U,\n    0xd4U, 0xa4U, 0x5cU, 0xccU, 0x5dU, 0x65U, 0xb6U, 0x92U,\n    0x6cU, 0x70U, 0x48U, 0x50U, 0xfdU, 0xedU, 0xb9U, 0xdaU,\n    0x5eU, 0x15U, 0x46U, 0x57U, 0xa7U, 0x8dU, 0x9dU, 0x84U,\n    0x90U, 0xd8U, 0xabU, 0x00U, 0x8cU, 0xbcU, 0xd3U, 0x0aU,\n    0xf7U, 0xe4U, 0x58U, 0x05U, 0xb8U, 0xb3U, 0x45U, 0x06U,\n    0xd0U, 0x2cU, 0x1eU, 0x8fU, 0xcaU, 0x3fU, 0x0fU, 0x02U,\n    0xc1U, 0xafU, 0xbdU, 0x03U, 0x01U, 0x13U, 0x8aU, 0x6bU,\n    0x3aU, 0x91U, 0x11U, 0x41U, 0x4fU, 0x67U, 0xdcU, 0xeaU,\n    0x97U, 0xf2U, 0xcfU, 0xceU, 0xf0U, 0xb4U, 0xe6U, 0x73U,\n    0x96U, 0xacU, 0x74U, 0x22U, 0xe7U, 0xadU, 0x35U, 0x85U,\n    0xe2U, 0xf9U, 0x37U, 0xe8U, 0x1cU, 0x75U, 0xdfU, 0x6eU,\n    0x47U, 0xf1U, 0x1aU, 0x71U, 0x1dU, 0x29U, 0xc5U, 0x89U,\n    0x6fU, 0xb7U, 0x62U, 0x0eU, 0xaaU, 0x18U, 0xbeU, 0x1bU,\n    0xfcU, 0x56U, 0x3eU, 0x4bU, 0xc6U, 0xd2U, 0x79U, 0x20U,\n    0x9aU, 0xdbU, 0xc0U, 0xfeU, 0x78U, 0xcdU, 0x5aU, 0xf4U,\n    0x1fU, 0xddU, 0xa8U, 0x33U, 0x88U, 0x07U, 0xc7U, 0x31U,\n    0xb1U, 0x12U, 0x10U, 0x59U, 0x27U, 0x80U, 0xecU, 0x5fU,\n    0x60U, 0x51U, 0x7fU, 0xa9U, 0x19U, 0xb5U, 0x4aU, 0x0dU,\n    0x2dU, 0xe5U, 0x7aU, 0x9fU, 0x93U, 0xc9U, 0x9cU, 0xefU,\n    0xa0U, 0xe0U, 0x3bU, 0x4dU, 0xaeU, 0x2aU, 0xf5U, 0xb0U,\n    0xc8U, 0xebU, 0xbbU, 0x3cU, 0x83U, 0x53U, 0x99U, 0x61U,\n    0x17U, 0x2bU, 0x04U, 0x7eU, 0xbaU, 0x77U, 0xd6U, 0x26U,\n    0xe1U, 0x69U, 0x14U, 0x63U, 0x55U, 0x21U, 0x0cU, 0x7dU,\n};\nstatic const u32 rcon[] = {\n    0x01000000, 0x02000000, 0x04000000, 0x08000000,\n    0x10000000, 0x20000000, 0x40000000, 0x80000000,\n    0x1B000000, 0x36000000, /* for 128-bit blocks, Rijndael never uses more than 10 rcon values */\n};\n\n/**\n * Expand the cipher key into the encryption key schedule.\n */\nint AES_set_encrypt_key(const unsigned char *userKey, const int bits,\n                        AES_KEY *key)\n{\n\n    u32 *rk;\n    int i = 0;\n    u32 temp;\n\n    if (!userKey || !key)\n        return -1;\n    if (bits != 128 && bits != 192 && bits != 256)\n        return -2;\n\n    rk = key->rd_key;\n\n    if (bits == 128)\n        key->rounds = 10;\n    else if (bits == 192)\n        key->rounds = 12;\n    else\n        key->rounds = 14;\n\n    rk[0] = GETU32(userKey     );\n    rk[1] = GETU32(userKey +  4);\n    rk[2] = GETU32(userKey +  8);\n    rk[3] = GETU32(userKey + 12);\n    if (bits == 128) {\n        while (1) {\n            temp  = rk[3];\n            rk[4] = rk[0] ^\n                (Te2[(temp >> 16) & 0xff] & 0xff000000) ^\n                (Te3[(temp >>  8) & 0xff] & 0x00ff0000) ^\n                (Te0[(temp      ) & 0xff] & 0x0000ff00) ^\n                (Te1[(temp >> 24)       ] & 0x000000ff) ^\n                rcon[i];\n            rk[5] = rk[1] ^ rk[4];\n            rk[6] = rk[2] ^ rk[5];\n            rk[7] = rk[3] ^ rk[6];\n            if (++i == 10) {\n                return 0;\n            }\n            rk += 4;\n        }\n    }\n    rk[4] = GETU32(userKey + 16);\n    rk[5] = GETU32(userKey + 20);\n    if (bits == 192) {\n        while (1) {\n            temp = rk[ 5];\n            rk[ 6] = rk[ 0] ^\n                (Te2[(temp >> 16) & 0xff] & 0xff000000) ^\n                (Te3[(temp >>  8) & 0xff] & 0x00ff0000) ^\n                (Te0[(temp      ) & 0xff] & 0x0000ff00) ^\n                (Te1[(temp >> 24)       ] & 0x000000ff) ^\n                rcon[i];\n            rk[ 7] = rk[ 1] ^ rk[ 6];\n            rk[ 8] = rk[ 2] ^ rk[ 7];\n            rk[ 9] = rk[ 3] ^ rk[ 8];\n            if (++i == 8) {\n                return 0;\n            }\n            rk[10] = rk[ 4] ^ rk[ 9];\n            rk[11] = rk[ 5] ^ rk[10];\n            rk += 6;\n        }\n    }\n    rk[6] = GETU32(userKey + 24);\n    rk[7] = GETU32(userKey + 28);\n    if (bits == 256) {\n        while (1) {\n            temp = rk[ 7];\n            rk[ 8] = rk[ 0] ^\n                (Te2[(temp >> 16) & 0xff] & 0xff000000) ^\n                (Te3[(temp >>  8) & 0xff] & 0x00ff0000) ^\n                (Te0[(temp      ) & 0xff] & 0x0000ff00) ^\n                (Te1[(temp >> 24)       ] & 0x000000ff) ^\n                rcon[i];\n            rk[ 9] = rk[ 1] ^ rk[ 8];\n            rk[10] = rk[ 2] ^ rk[ 9];\n            rk[11] = rk[ 3] ^ rk[10];\n            if (++i == 7) {\n                return 0;\n            }\n            temp = rk[11];\n            rk[12] = rk[ 4] ^\n                (Te2[(temp >> 24)       ] & 0xff000000) ^\n                (Te3[(temp >> 16) & 0xff] & 0x00ff0000) ^\n                (Te0[(temp >>  8) & 0xff] & 0x0000ff00) ^\n                (Te1[(temp      ) & 0xff] & 0x000000ff);\n            rk[13] = rk[ 5] ^ rk[12];\n            rk[14] = rk[ 6] ^ rk[13];\n            rk[15] = rk[ 7] ^ rk[14];\n\n            rk += 8;\n            }\n    }\n    return 0;\n}\n\n/**\n * Expand the cipher key into the decryption key schedule.\n */\nint AES_set_decrypt_key(const unsigned char *userKey, const int bits,\n                        AES_KEY *key)\n{\n\n    u32 *rk;\n    int i, j, status;\n    u32 temp;\n\n    /* first, start with an encryption schedule */\n    status = AES_set_encrypt_key(userKey, bits, key);\n    if (status < 0)\n        return status;\n\n    rk = key->rd_key;\n\n    /* invert the order of the round keys: */\n    for (i = 0, j = 4*(key->rounds); i < j; i += 4, j -= 4) {\n        temp = rk[i    ]; rk[i    ] = rk[j    ]; rk[j    ] = temp;\n        temp = rk[i + 1]; rk[i + 1] = rk[j + 1]; rk[j + 1] = temp;\n        temp = rk[i + 2]; rk[i + 2] = rk[j + 2]; rk[j + 2] = temp;\n        temp = rk[i + 3]; rk[i + 3] = rk[j + 3]; rk[j + 3] = temp;\n    }\n    /* apply the inverse MixColumn transform to all round keys but the first and the last: */\n    for (i = 1; i < (key->rounds); i++) {\n        rk += 4;\n        rk[0] =\n            Td0[Te1[(rk[0] >> 24)       ] & 0xff] ^\n            Td1[Te1[(rk[0] >> 16) & 0xff] & 0xff] ^\n            Td2[Te1[(rk[0] >>  8) & 0xff] & 0xff] ^\n            Td3[Te1[(rk[0]      ) & 0xff] & 0xff];\n        rk[1] =\n            Td0[Te1[(rk[1] >> 24)       ] & 0xff] ^\n            Td1[Te1[(rk[1] >> 16) & 0xff] & 0xff] ^\n            Td2[Te1[(rk[1] >>  8) & 0xff] & 0xff] ^\n            Td3[Te1[(rk[1]      ) & 0xff] & 0xff];\n        rk[2] =\n            Td0[Te1[(rk[2] >> 24)       ] & 0xff] ^\n            Td1[Te1[(rk[2] >> 16) & 0xff] & 0xff] ^\n            Td2[Te1[(rk[2] >>  8) & 0xff] & 0xff] ^\n            Td3[Te1[(rk[2]      ) & 0xff] & 0xff];\n        rk[3] =\n            Td0[Te1[(rk[3] >> 24)       ] & 0xff] ^\n            Td1[Te1[(rk[3] >> 16) & 0xff] & 0xff] ^\n            Td2[Te1[(rk[3] >>  8) & 0xff] & 0xff] ^\n            Td3[Te1[(rk[3]      ) & 0xff] & 0xff];\n    }\n    return 0;\n}\n\n/*\n * Encrypt a single block\n * in and out can overlap\n */\nvoid AES_encrypt(const unsigned char *in, unsigned char *out,\n                 const AES_KEY *key) {\n\n    const u32 *rk;\n    u32 s0, s1, s2, s3, t0, t1, t2, t3;\n#ifndef FULL_UNROLL\n    int r;\n#endif /* ?FULL_UNROLL */\n\n    assert(in && out && key);\n    rk = key->rd_key;\n\n    /*\n     * map byte array block to cipher state\n     * and add initial round key:\n     */\n    s0 = GETU32(in     ) ^ rk[0];\n    s1 = GETU32(in +  4) ^ rk[1];\n    s2 = GETU32(in +  8) ^ rk[2];\n    s3 = GETU32(in + 12) ^ rk[3];\n#ifdef FULL_UNROLL\n    /* round 1: */\n    t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >>  8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[ 4];\n    t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >>  8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[ 5];\n    t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >>  8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[ 6];\n    t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >>  8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[ 7];\n    /* round 2: */\n    s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >>  8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[ 8];\n    s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >>  8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[ 9];\n    s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >>  8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[10];\n    s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >>  8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[11];\n    /* round 3: */\n    t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >>  8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[12];\n    t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >>  8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[13];\n    t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >>  8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[14];\n    t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >>  8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[15];\n    /* round 4: */\n    s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >>  8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[16];\n    s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >>  8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[17];\n    s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >>  8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[18];\n    s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >>  8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[19];\n    /* round 5: */\n    t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >>  8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[20];\n    t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >>  8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[21];\n    t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >>  8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[22];\n    t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >>  8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[23];\n    /* round 6: */\n    s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >>  8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[24];\n    s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >>  8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[25];\n    s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >>  8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[26];\n    s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >>  8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[27];\n    /* round 7: */\n    t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >>  8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[28];\n    t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >>  8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[29];\n    t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >>  8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[30];\n    t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >>  8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[31];\n    /* round 8: */\n    s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >>  8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[32];\n    s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >>  8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[33];\n    s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >>  8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[34];\n    s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >>  8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[35];\n    /* round 9: */\n    t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >>  8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[36];\n    t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >>  8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[37];\n    t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >>  8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[38];\n    t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >>  8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[39];\n    if (key->rounds > 10) {\n        /* round 10: */\n        s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >>  8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[40];\n        s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >>  8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[41];\n        s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >>  8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[42];\n        s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >>  8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[43];\n        /* round 11: */\n        t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >>  8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[44];\n        t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >>  8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[45];\n        t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >>  8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[46];\n        t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >>  8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[47];\n        if (key->rounds > 12) {\n            /* round 12: */\n            s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >>  8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[48];\n            s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >>  8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[49];\n            s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >>  8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[50];\n            s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >>  8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[51];\n            /* round 13: */\n            t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >>  8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[52];\n            t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >>  8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[53];\n            t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >>  8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[54];\n            t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >>  8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[55];\n        }\n    }\n    rk += key->rounds << 2;\n#else  /* !FULL_UNROLL */\n    /*\n     * Nr - 1 full rounds:\n     */\n    r = key->rounds >> 1;\n    for (;;) {\n        t0 =\n            Te0[(s0 >> 24)       ] ^\n            Te1[(s1 >> 16) & 0xff] ^\n            Te2[(s2 >>  8) & 0xff] ^\n            Te3[(s3      ) & 0xff] ^\n            rk[4];\n        t1 =\n            Te0[(s1 >> 24)       ] ^\n            Te1[(s2 >> 16) & 0xff] ^\n            Te2[(s3 >>  8) & 0xff] ^\n            Te3[(s0      ) & 0xff] ^\n            rk[5];\n        t2 =\n            Te0[(s2 >> 24)       ] ^\n            Te1[(s3 >> 16) & 0xff] ^\n            Te2[(s0 >>  8) & 0xff] ^\n            Te3[(s1      ) & 0xff] ^\n            rk[6];\n        t3 =\n            Te0[(s3 >> 24)       ] ^\n            Te1[(s0 >> 16) & 0xff] ^\n            Te2[(s1 >>  8) & 0xff] ^\n            Te3[(s2      ) & 0xff] ^\n            rk[7];\n\n        rk += 8;\n        if (--r == 0) {\n            break;\n        }\n\n        s0 =\n            Te0[(t0 >> 24)       ] ^\n            Te1[(t1 >> 16) & 0xff] ^\n            Te2[(t2 >>  8) & 0xff] ^\n            Te3[(t3      ) & 0xff] ^\n            rk[0];\n        s1 =\n            Te0[(t1 >> 24)       ] ^\n            Te1[(t2 >> 16) & 0xff] ^\n            Te2[(t3 >>  8) & 0xff] ^\n            Te3[(t0      ) & 0xff] ^\n            rk[1];\n        s2 =\n            Te0[(t2 >> 24)       ] ^\n            Te1[(t3 >> 16) & 0xff] ^\n            Te2[(t0 >>  8) & 0xff] ^\n            Te3[(t1      ) & 0xff] ^\n            rk[2];\n        s3 =\n            Te0[(t3 >> 24)       ] ^\n            Te1[(t0 >> 16) & 0xff] ^\n            Te2[(t1 >>  8) & 0xff] ^\n            Te3[(t2      ) & 0xff] ^\n            rk[3];\n    }\n#endif /* ?FULL_UNROLL */\n    /*\n     * apply last round and\n     * map cipher state to byte array block:\n     */\n    s0 =\n        (Te2[(t0 >> 24)       ] & 0xff000000) ^\n        (Te3[(t1 >> 16) & 0xff] & 0x00ff0000) ^\n        (Te0[(t2 >>  8) & 0xff] & 0x0000ff00) ^\n        (Te1[(t3      ) & 0xff] & 0x000000ff) ^\n        rk[0];\n    PUTU32(out     , s0);\n    s1 =\n        (Te2[(t1 >> 24)       ] & 0xff000000) ^\n        (Te3[(t2 >> 16) & 0xff] & 0x00ff0000) ^\n        (Te0[(t3 >>  8) & 0xff] & 0x0000ff00) ^\n        (Te1[(t0      ) & 0xff] & 0x000000ff) ^\n        rk[1];\n    PUTU32(out +  4, s1);\n    s2 =\n        (Te2[(t2 >> 24)       ] & 0xff000000) ^\n        (Te3[(t3 >> 16) & 0xff] & 0x00ff0000) ^\n        (Te0[(t0 >>  8) & 0xff] & 0x0000ff00) ^\n        (Te1[(t1      ) & 0xff] & 0x000000ff) ^\n        rk[2];\n    PUTU32(out +  8, s2);\n    s3 =\n        (Te2[(t3 >> 24)       ] & 0xff000000) ^\n        (Te3[(t0 >> 16) & 0xff] & 0x00ff0000) ^\n        (Te0[(t1 >>  8) & 0xff] & 0x0000ff00) ^\n        (Te1[(t2      ) & 0xff] & 0x000000ff) ^\n        rk[3];\n    PUTU32(out + 12, s3);\n}\n\n/*\n * Decrypt a single block\n * in and out can overlap\n */\nvoid AES_decrypt(const unsigned char *in, unsigned char *out,\n                 const AES_KEY *key)\n{\n\n    const u32 *rk;\n    u32 s0, s1, s2, s3, t0, t1, t2, t3;\n#ifndef FULL_UNROLL\n    int r;\n#endif /* ?FULL_UNROLL */\n\n    assert(in && out && key);\n    rk = key->rd_key;\n\n    /*\n     * map byte array block to cipher state\n     * and add initial round key:\n     */\n    s0 = GETU32(in     ) ^ rk[0];\n    s1 = GETU32(in +  4) ^ rk[1];\n    s2 = GETU32(in +  8) ^ rk[2];\n    s3 = GETU32(in + 12) ^ rk[3];\n#ifdef FULL_UNROLL\n    /* round 1: */\n    t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >>  8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[ 4];\n    t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >>  8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[ 5];\n    t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >>  8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[ 6];\n    t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >>  8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[ 7];\n    /* round 2: */\n    s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >>  8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[ 8];\n    s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >>  8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[ 9];\n    s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >>  8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[10];\n    s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >>  8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[11];\n    /* round 3: */\n    t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >>  8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[12];\n    t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >>  8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[13];\n    t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >>  8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[14];\n    t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >>  8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[15];\n    /* round 4: */\n    s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >>  8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[16];\n    s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >>  8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[17];\n    s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >>  8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[18];\n    s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >>  8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[19];\n    /* round 5: */\n    t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >>  8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[20];\n    t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >>  8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[21];\n    t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >>  8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[22];\n    t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >>  8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[23];\n    /* round 6: */\n    s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >>  8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[24];\n    s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >>  8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[25];\n    s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >>  8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[26];\n    s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >>  8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[27];\n    /* round 7: */\n    t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >>  8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[28];\n    t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >>  8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[29];\n    t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >>  8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[30];\n    t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >>  8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[31];\n    /* round 8: */\n    s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >>  8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[32];\n    s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >>  8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[33];\n    s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >>  8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[34];\n    s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >>  8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[35];\n    /* round 9: */\n    t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >>  8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[36];\n    t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >>  8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[37];\n    t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >>  8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[38];\n    t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >>  8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[39];\n    if (key->rounds > 10) {\n        /* round 10: */\n        s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >>  8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[40];\n        s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >>  8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[41];\n        s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >>  8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[42];\n        s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >>  8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[43];\n        /* round 11: */\n        t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >>  8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[44];\n        t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >>  8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[45];\n        t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >>  8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[46];\n        t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >>  8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[47];\n        if (key->rounds > 12) {\n            /* round 12: */\n            s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >>  8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[48];\n            s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >>  8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[49];\n            s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >>  8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[50];\n            s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >>  8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[51];\n            /* round 13: */\n            t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >>  8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[52];\n            t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >>  8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[53];\n            t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >>  8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[54];\n            t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >>  8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[55];\n        }\n    }\n    rk += key->rounds << 2;\n#else  /* !FULL_UNROLL */\n    /*\n     * Nr - 1 full rounds:\n     */\n    r = key->rounds >> 1;\n    for (;;) {\n        t0 =\n            Td0[(s0 >> 24)       ] ^\n            Td1[(s3 >> 16) & 0xff] ^\n            Td2[(s2 >>  8) & 0xff] ^\n            Td3[(s1      ) & 0xff] ^\n            rk[4];\n        t1 =\n            Td0[(s1 >> 24)       ] ^\n            Td1[(s0 >> 16) & 0xff] ^\n            Td2[(s3 >>  8) & 0xff] ^\n            Td3[(s2      ) & 0xff] ^\n            rk[5];\n        t2 =\n            Td0[(s2 >> 24)       ] ^\n            Td1[(s1 >> 16) & 0xff] ^\n            Td2[(s0 >>  8) & 0xff] ^\n            Td3[(s3      ) & 0xff] ^\n            rk[6];\n        t3 =\n            Td0[(s3 >> 24)       ] ^\n            Td1[(s2 >> 16) & 0xff] ^\n            Td2[(s1 >>  8) & 0xff] ^\n            Td3[(s0      ) & 0xff] ^\n            rk[7];\n\n        rk += 8;\n        if (--r == 0) {\n            break;\n        }\n\n        s0 =\n            Td0[(t0 >> 24)       ] ^\n            Td1[(t3 >> 16) & 0xff] ^\n            Td2[(t2 >>  8) & 0xff] ^\n            Td3[(t1      ) & 0xff] ^\n            rk[0];\n        s1 =\n            Td0[(t1 >> 24)       ] ^\n            Td1[(t0 >> 16) & 0xff] ^\n            Td2[(t3 >>  8) & 0xff] ^\n            Td3[(t2      ) & 0xff] ^\n            rk[1];\n        s2 =\n            Td0[(t2 >> 24)       ] ^\n            Td1[(t1 >> 16) & 0xff] ^\n            Td2[(t0 >>  8) & 0xff] ^\n            Td3[(t3      ) & 0xff] ^\n            rk[2];\n        s3 =\n            Td0[(t3 >> 24)       ] ^\n            Td1[(t2 >> 16) & 0xff] ^\n            Td2[(t1 >>  8) & 0xff] ^\n            Td3[(t0      ) & 0xff] ^\n            rk[3];\n    }\n#endif /* ?FULL_UNROLL */\n    /*\n     * apply last round and\n     * map cipher state to byte array block:\n     */\n    s0 =\n        ((u32)Td4[(t0 >> 24)       ] << 24) ^\n        ((u32)Td4[(t3 >> 16) & 0xff] << 16) ^\n        ((u32)Td4[(t2 >>  8) & 0xff] <<  8) ^\n        ((u32)Td4[(t1      ) & 0xff])       ^\n        rk[0];\n    PUTU32(out     , s0);\n    s1 =\n        ((u32)Td4[(t1 >> 24)       ] << 24) ^\n        ((u32)Td4[(t0 >> 16) & 0xff] << 16) ^\n        ((u32)Td4[(t3 >>  8) & 0xff] <<  8) ^\n        ((u32)Td4[(t2      ) & 0xff])       ^\n        rk[1];\n    PUTU32(out +  4, s1);\n    s2 =\n        ((u32)Td4[(t2 >> 24)       ] << 24) ^\n        ((u32)Td4[(t1 >> 16) & 0xff] << 16) ^\n        ((u32)Td4[(t0 >>  8) & 0xff] <<  8) ^\n        ((u32)Td4[(t3      ) & 0xff])       ^\n        rk[2];\n    PUTU32(out +  8, s2);\n    s3 =\n        ((u32)Td4[(t3 >> 24)       ] << 24) ^\n        ((u32)Td4[(t2 >> 16) & 0xff] << 16) ^\n        ((u32)Td4[(t1 >>  8) & 0xff] <<  8) ^\n        ((u32)Td4[(t0      ) & 0xff])       ^\n        rk[3];\n    PUTU32(out + 12, s3);\n}\n\n\n"
  },
  {
    "path": "app/src/main/jni/aes/aes_ecb.c",
    "content": "/*\n * Copyright 2002-2016 The OpenSSL Project Authors. All Rights Reserved.\n *\n * Licensed under the OpenSSL license (the \"License\").  You may not use\n * this file except in compliance with the License.  You can obtain a copy\n * in the file LICENSE in the source distribution or at\n * https://www.openssl.org/source/license.html\n */\n\n#include <assert.h>\n\n#include \"aes.h\"\n#include \"aes_locl.h\"\n\nvoid AES_ecb_encrypt(const unsigned char *in, unsigned char *out, const AES_KEY *key, const int enc)\n{\n    assert(in && out && key);\n    assert((AES_ENCRYPT == enc) || (AES_DECRYPT == enc));\n\n    if (AES_ENCRYPT == enc)\n        AES_encrypt(in, out, key);\n    else\n        AES_decrypt(in, out, key);\n}\n"
  },
  {
    "path": "app/src/main/jni/aes/aes_locl.h",
    "content": "/*\n * Copyright 2002-2016 The OpenSSL Project Authors. All Rights Reserved.\n *\n * Licensed under the OpenSSL license (the \"License\").  You may not use\n * this file except in compliance with the License.  You can obtain a copy\n * in the file LICENSE in the source distribution or at\n * https://www.openssl.org/source/license.html\n */\n\n#ifndef HEADER_AES_LOCL_H\n# define HEADER_AES_LOCL_H\n\n//# include <e_os2.h>\n# include <stdio.h>\n# include <stdlib.h>\n# include <string.h>\n\n# if defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_AMD64) || defined(_M_X64))\n#  define SWAP(x) (_lrotl(x, 8) & 0x00ff00ff | _lrotr(x, 8) & 0xff00ff00)\n#  define GETU32(p) SWAP(*((u32 *)(p)))\n#  define PUTU32(ct, st) { *((u32 *)(ct)) = SWAP((st)); }\n# else\n#  define GETU32(pt) (((u32)(pt)[0] << 24) ^ ((u32)(pt)[1] << 16) ^ ((u32)(pt)[2] <<  8) ^ ((u32)(pt)[3]))\n#  define PUTU32(ct, st) { (ct)[0] = (u8)((st) >> 24); (ct)[1] = (u8)((st) >> 16); (ct)[2] = (u8)((st) >>  8); (ct)[3] = (u8)(st); }\n# endif\n\n# ifdef AES_LONG\ntypedef unsigned long u32;\n# else\ntypedef unsigned int u32;\n# endif\ntypedef unsigned short u16;\ntypedef unsigned char u8;\n\n# define MAXKC   (256/32)\n# define MAXKB   (256/8)\n# define MAXNR   14\n\n/* This controls loop-unrolling in aes_core.c */\n# undef FULL_UNROLL\n\n#endif                          /* !HEADER_AES_LOCL_H */\n"
  },
  {
    "path": "app/src/main/jni/aes/cbc128.c",
    "content": "/*\n * Copyright 2008-2016 The OpenSSL Project Authors. All Rights Reserved.\n *\n * Licensed under the OpenSSL license (the \"License\").  You may not use\n * this file except in compliance with the License.  You can obtain a copy\n * in the file LICENSE in the source distribution or at\n * https://www.openssl.org/source/license.html\n */\n\n// #include <openssl/crypto.h>\n#include \"modes.h\"\n#include <string.h>\n\n#if !defined(STRICT_ALIGNMENT) && !defined(PEDANTIC)\n# define STRICT_ALIGNMENT 0\n#endif\n\nvoid CRYPTO_cbc128_encrypt(const unsigned char *in, unsigned char *out,\n                           size_t len, const void *key,\n                           unsigned char ivec[16], block128_f block)\n{\n    size_t n;\n    const unsigned char *iv = ivec;\n\n    if (len == 0)\n        return;\n\n#if !defined(OPENSSL_SMALL_FOOTPRINT)\n    if (STRICT_ALIGNMENT &&\n        ((size_t)in | (size_t)out | (size_t)ivec) % sizeof(size_t) != 0) {\n        while (len >= 16) {\n            for (n = 0; n < 16; ++n)\n                out[n] = in[n] ^ iv[n];\n            (*block) (out, out, key);\n            iv = out;\n            len -= 16;\n            in += 16;\n            out += 16;\n        }\n    } else {\n        while (len >= 16) {\n            for (n = 0; n < 16; n += sizeof(size_t))\n                *(size_t *)(out + n) =\n                    *(size_t *)(in + n) ^ *(size_t *)(iv + n);\n            (*block) (out, out, key);\n            iv = out;\n            len -= 16;\n            in += 16;\n            out += 16;\n        }\n    }\n#endif\n    while (len) {\n        for (n = 0; n < 16 && n < len; ++n)\n            out[n] = in[n] ^ iv[n];\n        for (; n < 16; ++n)\n            out[n] = iv[n];\n        (*block) (out, out, key);\n        iv = out;\n        if (len <= 16)\n            break;\n        len -= 16;\n        in += 16;\n        out += 16;\n    }\n    memcpy(ivec, iv, 16);\n}\n\nvoid CRYPTO_cbc128_decrypt(const unsigned char *in, unsigned char *out,\n                           size_t len, const void *key,\n                           unsigned char ivec[16], block128_f block)\n{\n    size_t n;\n    union {\n        size_t t[16 / sizeof(size_t)];\n        unsigned char c[16];\n    } tmp;\n\n    if (len == 0)\n        return;\n\n#if !defined(OPENSSL_SMALL_FOOTPRINT)\n    if (in != out) {\n        const unsigned char *iv = ivec;\n\n        if (STRICT_ALIGNMENT &&\n            ((size_t)in | (size_t)out | (size_t)ivec) % sizeof(size_t) != 0) {\n            while (len >= 16) {\n                (*block) (in, out, key);\n                for (n = 0; n < 16; ++n)\n                    out[n] ^= iv[n];\n                iv = in;\n                len -= 16;\n                in += 16;\n                out += 16;\n            }\n        } else if (16 % sizeof(size_t) == 0) { /* always true */\n            while (len >= 16) {\n                size_t *out_t = (size_t *)out, *iv_t = (size_t *)iv;\n\n                (*block) (in, out, key);\n                for (n = 0; n < 16 / sizeof(size_t); n++)\n                    out_t[n] ^= iv_t[n];\n                iv = in;\n                len -= 16;\n                in += 16;\n                out += 16;\n            }\n        }\n        memcpy(ivec, iv, 16);\n    } else {\n        if (STRICT_ALIGNMENT &&\n            ((size_t)in | (size_t)out | (size_t)ivec) % sizeof(size_t) != 0) {\n            unsigned char c;\n            while (len >= 16) {\n                (*block) (in, tmp.c, key);\n                for (n = 0; n < 16; ++n) {\n                    c = in[n];\n                    out[n] = tmp.c[n] ^ ivec[n];\n                    ivec[n] = c;\n                }\n                len -= 16;\n                in += 16;\n                out += 16;\n            }\n        } else if (16 % sizeof(size_t) == 0) { /* always true */\n            while (len >= 16) {\n                size_t c, *out_t = (size_t *)out, *ivec_t = (size_t *)ivec;\n                const size_t *in_t = (const size_t *)in;\n\n                (*block) (in, tmp.c, key);\n                for (n = 0; n < 16 / sizeof(size_t); n++) {\n                    c = in_t[n];\n                    out_t[n] = tmp.t[n] ^ ivec_t[n];\n                    ivec_t[n] = c;\n                }\n                len -= 16;\n                in += 16;\n                out += 16;\n            }\n        }\n    }\n#endif\n    while (len) {\n        unsigned char c;\n        (*block) (in, tmp.c, key);\n        for (n = 0; n < 16 && n < len; ++n) {\n            c = in[n];\n            out[n] = tmp.c[n] ^ ivec[n];\n            ivec[n] = c;\n        }\n        if (len <= 16) {\n            for (; n < 16; ++n)\n                ivec[n] = in[n];\n            break;\n        }\n        len -= 16;\n        in += 16;\n        out += 16;\n    }\n}\n"
  },
  {
    "path": "app/src/main/jni/aes/main_test.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include \"aes.h\"\n\nint main()\n{\n\tint i;\n\t\n\tconst unsigned char master_key[16] = {\n\t\t'1', '2', '3', '4', '5', '6', '7', '8',\n\t\t'9', '0', 'a', 'b', 'c', 'd', 'e', 'f',\n\t};\n\tconst unsigned char text[16] = {\n\t\t'1', '2', '3', '4', '5', '6', '7', '8',\n\t\t'9', '0', 'a', 'b', 'c', 'd', 'e', 'f',\n\t};\n\n\tconst unsigned char iv[16] = {\n\t\t'9', '0', 'a', 'b', 'c', 'd', 'e', 'f',\n\t\t'1', '2', '3', '4', '5', '6', '7', '8',\t\t\n\t};\n\n\tunsigned char ivc[16];\n\t\n\tunsigned char encrypted[16], decrypted[16];\n\tmemset(encrypted, 0, 16*sizeof(char));\n\tmemset(decrypted, 0, 16*sizeof(char));\n\n\tAES_KEY key;\n\n\tprintf(\"plaintext:\\n\");\n\tfor (i = 0; i < 16; i++) {\n\t\tprintf(\"%02x \", (unsigned int)text[i]);\n\t}\n\tprintf(\"\\n=============================\\n\");\n\t\n\tprintf(\"encrypted:\\n\");\n\n\tAES_set_encrypt_key(master_key, 128, &key); \n\n\t// ecb\n\t// AES_ecb_encrypt(text, encrypted, &key, AES_ENCRYPT);\n\n\t//cbc\n\tmemcpy( ivc, iv, 16*sizeof(char));\n\tAES_cbc_encrypt(text,encrypted,16,&key,ivc,AES_ENCRYPT);\n\tfor (i = 0; i < 16; i++) {\n\t\tprintf(\"%02x \", (unsigned int)encrypted[i]);\n\t}\n\tprintf(\"\\n=============================\\n\");\n\t\n\tprintf(\"decrypted:\\n\");\n\tAES_set_decrypt_key(master_key, 128, &key);\n\t// ecb\n\t// AES_ecb_encrypt(encrypted, decrypted, &key, AES_DECRYPT);\n\n\t//cbc\n\tmemcpy( ivc, iv, 16*sizeof(char));\n\tAES_cbc_encrypt(encrypted,decrypted,16,&key,ivc,AES_DECRYPT);\n\tfor (i = 0; i < 16; i++) {\n\t\tprintf(\"%02x \", (unsigned int)decrypted[i]);\n\t}\n\tprintf(\"\\n\");\n\t\n\treturn 0;\n\n}"
  },
  {
    "path": "app/src/main/jni/aes/modes.h",
    "content": "#ifndef HEADER_MODES_H\n# define HEADER_MODES_H\n\n//# include  \"/Users/aria/dev/android/sdk/ndk/21/toolchains/llvm/prebuilt/darwin-x86_64/lib64/clang/9.0.8/include/stddef.h\"\n\n#include <stddef.h>\n\ntypedef void (*block128_f)(const unsigned char in[16],\n                           unsigned char out[16], const void *key);\n\ntypedef void (*cbc128_f)(const unsigned char *in, unsigned char *out,\n                         size_t len, const void *key,\n                         unsigned char ivec[16], int enc);\n\nvoid CRYPTO_cbc128_encrypt(const unsigned char *in, unsigned char *out,\n                           size_t len, const void *key,\n                           unsigned char ivec[16], block128_f block);\n\nvoid CRYPTO_cbc128_decrypt(const unsigned char *in, unsigned char *out,\n                           size_t len, const void *key,\n                           unsigned char ivec[16], block128_f block);\n\n\n#endif\n"
  },
  {
    "path": "app/src/main/jni/encrypt_str.cpp",
    "content": "#include <jni.h>\n#include <string>\n\nextern \"C\" {\n#include \"aes/aes.h\"\n}\n\n// see https://blog.csdn.net/u010302327/article/details/79726637\n//把字符串转成十六进制字符串\nstd::string char2hex(std::string s) {\n    std::string ret;\n    for (unsigned i = 0; i != s.size(); ++i) {\n        char hex[5];\n        sprintf(hex, \"%.2x\", (unsigned char) s[i]);\n        ret += hex;\n    }\n    return ret;\n}\n\n//把十六进制字符串转成字符串\nstd::string hex2char(std::string s) {\n    std::string ret;\n    int length = (int) s.length();\n    for (int i = 0; i < length; i += 2) {\n        std::string buf = \"0x\" + s.substr(i, 2);\n        unsigned int value;\n        sscanf(buf.c_str(), \"%x\", &value);\n        ret += ((char) value);\n    }\n    return ret;\n}\n\nint hexCharToInt(char c) {\n    if (c >= '0' && c <= '9') return (c - '0');\n    if (c >= 'A' && c <= 'F') return (c - 'A' + 10);\n    if (c >= 'a' && c <= 'f') return (c - 'a' + 10);\n    return 0;\n}\n\n//十六进制字符串转成十六进制数组\nchar *hexstringToBytes(std::string s) {\n    int sz = (int) s.length();\n    char *ret = new char[sz / 2];\n    for (int i = 0; i < sz; i += 2) {\n        ret[i / 2] = (char) ((hexCharToInt(s.at(i)) << 4) | hexCharToInt(s.at(i + 1)));\n    }\n    return ret;\n}\n\n//十六进制数组转成十六进制字符串\nstd::string bytestohexstring(char *bytes, int bytelength) {\n    std::string str;\n    std::string str2(\"0123456789abcdef\");\n    for (int i = 0; i < bytelength; ++i) {\n        int b;\n        b = 0x0f & (bytes[i] >> 4);\n        char s1 = str2.at(b);\n        str.append(1, str2.at(b));\n        b = 0x0f & bytes[i];\n        str.append(1, str2.at(b));\n//        char s2 = str2.at(b);\n    }\n    return str;\n}\n\n//加密\nstd::string EncodeAES(const unsigned char *master_key, std::string data, const unsigned char *iv) {\n    AES_KEY key;\n    AES_set_encrypt_key(master_key, 128, &key);\n\n    unsigned char ivc[AES_BLOCK_SIZE];\n\n    std::string data_bak = data.c_str();\n    unsigned int data_length = (unsigned int) data_bak.length();\n    int padding = 0;\n    if (data_bak.length() % AES_BLOCK_SIZE >= 0) {\n        padding = (int) (AES_BLOCK_SIZE - data_bak.length() % AES_BLOCK_SIZE);\n    }\n    data_length += padding;\n    while (padding > 0) {\n        data_bak += '\\0';\n        padding--;\n    }\n\n    memcpy(ivc, iv, AES_BLOCK_SIZE * sizeof(char));\n    std::string encryhex;\n    for (unsigned int i = 0; i < data_length / AES_BLOCK_SIZE; i++) {\n        std::string str16 = data_bak.substr(i * AES_BLOCK_SIZE, AES_BLOCK_SIZE);\n        unsigned char out[AES_BLOCK_SIZE];\n        memset(out, 0, AES_BLOCK_SIZE);\n        AES_cbc_encrypt((const unsigned char *) str16.c_str(), out, 16, &key, ivc, AES_ENCRYPT);\n        encryhex += bytestohexstring((char *) out, AES_BLOCK_SIZE);\n    }\n    return encryhex;\n\n}\n\n//解密\nstd::string DecodeAES(const unsigned char *master_key, std::string data, const unsigned char *iv) {\n    AES_KEY key;\n    AES_set_decrypt_key(master_key, 128, &key);\n\n    unsigned char ivc[AES_BLOCK_SIZE];\n    memcpy(ivc, iv, AES_BLOCK_SIZE * sizeof(char));\n    std::string ret;\n    for (unsigned int i = 0; i < data.length() / (AES_BLOCK_SIZE * 2); i++) {\n        std::string str16 = data.substr(i * AES_BLOCK_SIZE * 2, AES_BLOCK_SIZE * 2);\n        unsigned char out[AES_BLOCK_SIZE];\n        memset(out, 0, AES_BLOCK_SIZE);\n        char *buf = hexstringToBytes(str16);\n        AES_cbc_encrypt((const unsigned char *) buf, out, AES_BLOCK_SIZE, &key, ivc, AES_DECRYPT);\n        delete (buf);\n        ret += hex2char(bytestohexstring((char *) out, AES_BLOCK_SIZE));\n    }\n    return ret;\n}\n\n\nextern\n\"C\" JNIEXPORT jstring JNICALL\nJava_com_lyy_keepassa_util_QuickUnLockUtil_encryptStr(\n        JNIEnv *env,\n        jclass clazz, jstring str_) {\n    const char *str = env->GetStringUTFChars(str_, 0);\n\n    const auto *master_key = (const unsigned char *) \"KzSn6J0Zk4tIAQLh\";\n    const auto *iv = (const unsigned char *) \"90abcdef12345678\";\n\n    std::string h = EncodeAES(master_key, str, iv);\n\n    env->ReleaseStringUTFChars(str_, str);\n    return env->NewStringUTF(h.c_str());\n}\n\nextern \"C\"\nJNIEXPORT jstring JNICALL\nJava_com_lyy_keepassa_util_QuickUnLockUtil_decryption(JNIEnv *env, jclass thiz, jstring str_) {\n    const char *str = env->GetStringUTFChars(str_, 0);\n\n    const auto *master_key = (const unsigned char *) \"KzSn6J0Zk4tIAQLh\";\n\n    const auto *iv = (const unsigned char *) \"90abcdef12345678\";\n\n    std::string s = DecodeAES(master_key, str, iv);\n    env->ReleaseStringUTFChars(str_, str);\n\n    return env->NewStringUTF(s.c_str());\n}extern \"C\"\n\nJNIEXPORT jstring JNICALL\nJava_com_lyy_keepassa_util_QuickUnLockUtil_getDbPass(JNIEnv *env, jclass clazz) {\n    std::string s = \"stVz7QxFgzA7yMnH\";\n    return env->NewStringUTF(s.c_str());\n}"
  },
  {
    "path": "app/src/main/res/anim/dialog_y_enter.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\n  <translate android:fromYDelta=\"100%\"\n      android:duration=\"100\"/>\n</set>"
  },
  {
    "path": "app/src/main/res/anim/dialog_y_exit.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\n  <translate\n      android:duration=\"100\"\n      android:toYDelta=\"100%\"/>\n</set>"
  },
  {
    "path": "app/src/main/res/anim/translate_bottom_in.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<translate xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:duration=\"500\"\n    android:fromYDelta=\"-100%\"\n    android:toYDelta=\"0\" />"
  },
  {
    "path": "app/src/main/res/anim/translate_left_in.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<translate xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:fromXDelta=\"-100%\"\n    android:toXDelta=\"0\"\n    android:duration=\"500\">\n</translate>\n"
  },
  {
    "path": "app/src/main/res/anim/translate_left_out.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\n  <translate\n      android:duration=\"500\"\n      android:fromXDelta=\"0\"\n      android:toXDelta=\"100%\"/>\n</set>"
  },
  {
    "path": "app/src/main/res/anim/translate_right_in.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\n  <translate\n      android:duration=\"500\"\n      android:fromXDelta=\"100%\"\n      android:toXDelta=\"0\"/>\n</set>"
  },
  {
    "path": "app/src/main/res/anim/translate_right_out.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<translate xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:duration=\"500\"\n    android:fromXDelta=\"0\"\n    android:toXDelta=\"100%\" />"
  },
  {
    "path": "app/src/main/res/color/selector_blue_gray_text_bg.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n  <item android:color=\"@color/colorPrimary\" android:state_pressed=\"false\" />\n  <item android:color=\"@color/text_gray_color\" android:state_pressed=\"true\" />\n  <item android:color=\"@color/text_gray_color\" android:state_enabled=\"false\" />\n</selector>"
  },
  {
    "path": "app/src/main/res/drawable/bg_circle.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:shape=\"oval\"\n    >\n\n    <solid android:color=\"@color/colorPrimary\"/>\n\n    <size\n        android:width=\"100dp\"\n        android:height=\"100dp\"/>\n\n</shape>"
  },
  {
    "path": "app/src/main/res/drawable/bg_ed.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\">\n  <stroke\n      android:width=\"1dp\"\n      android:color=\"@color/line_color\" />\n\n  <corners android:radius=\"4dp\" />\n</shape>"
  },
  {
    "path": "app/src/main/res/drawable/bg_gray_radius_4.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:shape=\"rectangle\">\n\n  <solid android:color=\"@color/color_note_bg\" />\n\n  <corners android:radius=\"4dp\" />\n\n</shape>"
  },
  {
    "path": "app/src/main/res/drawable/bg_ime_entry.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n  <item android:state_selected=\"true\">\n    <shape android:shape=\"rectangle\">\n      <solid android:color=\"@color/white\" />\n      <corners android:radius=\"4dp\" />\n    </shape>\n  </item>\n  <item android:state_selected=\"false\">\n    <shape android:shape=\"rectangle\">\n      <solid android:color=\"@color/transparent\" />\n      <corners android:radius=\"4dp\" />\n    </shape>\n  </item>\n</selector>"
  },
  {
    "path": "app/src/main/res/drawable/bg_ime_key.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n  <item android:state_pressed=\"false\">\n    <shape android:shape=\"rectangle\">\n      <solid android:color=\"@color/color_FFFFFF\" />\n      <corners android:radius=\"6dp\" />\n    </shape>\n  </item>\n\n  <item android:state_pressed=\"true\">\n    <shape android:shape=\"rectangle\">\n      <solid android:color=\"@color/color_22FEFEFE\" />\n      <corners android:radius=\"6dp\" />\n    </shape>\n  </item>\n</selector>"
  },
  {
    "path": "app/src/main/res/drawable/bg_line.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:shape=\"line\">\n\n  <stroke\n      android:width=\"1dp\"\n      android:color=\"@color/line_color\"\n      android:dashWidth=\"4dp\"/>\n\n  <size android:height=\"3dp\"/>\n</shape>"
  },
  {
    "path": "app/src/main/res/drawable/bg_str_attr.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:shape=\"rectangle\">\n\n  <stroke\n      android:color=\"@color/color_icon_grey\"\n      android:width=\"1dp\" />\n\n  <corners android:radius=\"4dp\" />\n\n</shape>"
  },
  {
    "path": "app/src/main/res/drawable/bg_white_radius_2.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:shape=\"rectangle\">\n\n  <solid android:color=\"@color/background_color\" />\n\n  <corners android:radius=\"2dp\" />\n\n</shape>"
  },
  {
    "path": "app/src/main/res/drawable/bg_white_radius_32.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:shape=\"rectangle\">\n\n  <solid android:color=\"@color/white\" />\n\n  <corners android:radius=\"32dp\" />\n\n</shape>"
  },
  {
    "path": "app/src/main/res/drawable/bg_white_radius_4.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:shape=\"rectangle\">\n\n  <solid android:color=\"@color/white\" />\n\n  <corners android:radius=\"4dp\" />\n\n</shape>"
  },
  {
    "path": "app/src/main/res/drawable/bg_white_radius_8.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:shape=\"rectangle\">\n\n  <solid android:color=\"@color/white\" />\n\n  <corners android:radius=\"8dp\" />\n\n</shape>"
  },
  {
    "path": "app/src/main/res/drawable/ic_add.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"128dp\"\n    android:height=\"128dp\"\n    android:viewportWidth=\"1024\"\n    android:viewportHeight=\"1024\">\n  <path\n      android:fillColor=\"#F0F0F0\"\n      android:pathData=\"M488,488V192a16,16 0,0 1,16 -16h16a16,16 0,0 1,16 16v296H832a16,16 0,0 1,16 16v16a16,16 0,0 1,-16 16H536V832a16,16 0,0 1,-16 16h-16a16,16 0,0 1,-16 -16V536H192a16,16 0,0 1,-16 -16v-16a16,16 0,0 1,16 -16h296z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_add_24px.xml",
    "content": "<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:fillColor=\"@color/color_icon_grey\"\n      android:pathData=\"M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_add_blue_24px.xml",
    "content": "<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:fillColor=\"@color/text_blue_color\"\n      android:pathData=\"M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_add_photo_alternate_24px.xml",
    "content": "<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:fillColor=\"@color/color_icon_grey\"\n      android:pathData=\"M19,7v2.99s-1.99,0.01 -2,0L17,7h-3s0.01,-1.99 0,-2h3L17,2h2v3h3v2h-3zM16,11L16,8h-3L13,5L5,5c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2v-8h-3zM5,19l3,-4 2,3 3,-4 4,5L5,19z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_alipay.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"1024\"\n    android:viewportHeight=\"1024\">\n  <path\n      android:fillColor=\"@color/color_icon_grey\"\n      android:pathData=\"M600.96,597.62s22.52,-32.17 45.04,-96.53c24.13,-64.35 27.35,-99.75 27.35,-99.75l-183.4,-1.61v-62.74l222.01,-1.61v-45.04h-222.01v-101.36h-109.4v99.75h-210.75v45.04l207.53,-1.61v67.57h-165.71v35.39h342.67s-3.22,28.96 -16.09,64.35c-14.48,35.39 -27.35,65.96 -27.35,65.96s-160.88,-56.31 -246.15,-56.31c-85.27,0 -188.23,35.39 -197.88,133.53 -9.65,98.14 48.26,152.84 130.31,172.14 80.44,19.31 156.05,0 222.01,-32.17 65.96,-32.17 130.31,-104.57 130.31,-104.57l341.07,165.71s20.91,-32.17 37,-62.74c16.09,-30.57 28.96,-64.35 28.96,-64.35l-355.55,-119.05zM250.24,760.11c-120.66,0 -144.79,-59.53 -144.79,-101.36s25.74,-88.49 127.1,-94.92c101.36,-6.43 239.71,72.4 239.71,72.4s-101.36,123.88 -222.01,123.88z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_anim.xml",
    "content": "<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:fillColor=\"@color/color_icon_grey\"\n      android:pathData=\"M15,2A7,7 0,0 1,22 9C22,11.71 20.46,14.05 18.22,15.22C17.55,16.5 16.5,17.55 15.22,18.22C14.05,20.46 11.71,22 9,22A7,7 0,0 1,2 15C2,12.29 3.54,9.95 5.78,8.78C6.45,7.5 7.5,6.45 8.78,5.78C9.95,3.54 12.29,2 15,2M12,19A7,7 0,0 1,5 12C4.37,12.84 4,13.87 4,15A5,5 0,0 0,9 20C10.13,20 11.16,19.63 12,19M15,16A7,7 0,0 1,8 9H8C7.37,9.84 7,10.87 7,12A5,5 0,0 0,12 17C13.13,17 14.16,16.63 15,16V16M15,4C13.87,4 12.84,4.37 12,5V5A7,7 0,0 1,19 12H19C19.63,11.16 20,10.13 20,9A5,5 0,0 0,15 4M10,9A5,5 0,0 0,15 14C15.6,14 16.17,13.9 16.7,13.7C16.9,13.17 17,12.6 17,12A5,5 0,0 0,12 7C11.4,7 10.83,7.1 10.3,7.3C10.1,7.83 10,8.4 10,9Z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_app.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"1024\"\n    android:viewportHeight=\"1024\">\n  <path\n      android:fillColor=\"@color/color_icon_grey\"\n      android:pathData=\"M374.27,115.54 L211.51,115.54C157.58,115.54 113.78,159.29 113.78,213.22L113.78,376.04c0,53.93 43.8,97.68 97.74,97.68l162.76,0c53.99,0 97.68,-43.75 97.68,-97.68L471.95,213.22C471.95,159.29 428.26,115.54 374.27,115.54zM406.81,376.04c0,17.92 -14.62,32.54 -32.54,32.54L211.51,408.58c-17.92,0 -32.54,-14.62 -32.54,-32.54L178.97,213.22c0,-17.92 14.56,-32.54 32.54,-32.54l162.76,0c17.92,0 32.54,14.56 32.54,32.54L406.81,376.04z\" />\n  <path\n      android:fillColor=\"@color/color_icon_grey\"\n      android:pathData=\"M374.27,551.54 L211.46,551.54C157.58,551.54 113.78,595.29 113.78,649.22l0,162.76c0,53.87 43.75,97.68 97.68,97.68l162.76,0c54.04,0 97.68,-43.8 97.68,-97.68l0,-162.76C471.95,595.29 428.26,551.54 374.27,551.54zM406.81,811.98c0,17.92 -14.62,32.54 -32.54,32.54L211.46,844.52c-17.92,0 -32.54,-14.62 -32.54,-32.54l0,-162.76c0,-17.98 14.56,-32.6 32.54,-32.6l162.76,0c17.92,0 32.54,14.62 32.54,32.6L406.81,811.98 406.81,811.98z\" />\n  <path\n      android:fillColor=\"@color/color_icon_grey\"\n      android:pathData=\"M812.43,551.54l-162.65,0c-53.99,0 -97.74,43.75 -97.74,97.68l0,162.76c0,53.99 43.75,97.74 97.74,97.74l162.65,0c53.99,0 97.68,-43.75 97.68,-97.74l0,-162.76C910.11,595.29 866.42,551.54 812.43,551.54zM845.03,811.98c0,17.92 -14.56,32.54 -32.48,32.54l-162.65,0c-18.03,0 -32.6,-14.62 -32.6,-32.54l0,-162.76c0,-17.98 14.56,-32.6 32.6,-32.6l162.65,0c17.92,0 32.48,14.62 32.48,32.6L845.03,811.98z\" />\n  <path\n      android:fillColor=\"@color/color_icon_grey\"\n      android:pathData=\"M672.65,449.82c16.1,16.04 37.21,24.06 58.2,24.06 21.05,0 42.04,-8.02 58.14,-24.06l96.88,-97c32.14,-32.14 32.14,-84.25 0,-116.34L789.05,139.43c-16.1,-16.04 -37.09,-24.06 -58.14,-24.06 -20.99,0 -42.1,8.02 -58.2,24.06l-96.88,97c-32.09,32.09 -32.09,84.14 0,116.34L672.65,449.82zM621.8,282.45l97,-96.94c4.21,-4.15 8.87,-5.01 12.12,-5.01s7.91,0.85 12.12,5.01l97.11,97c4.1,4.15 5.01,8.87 5.01,12.12 0,3.24 -0.91,7.96 -5.12,12.12l-97,97c-4.21,4.15 -8.87,5.01 -12.12,5.01s-8.02,-0.85 -12.12,-5.01L621.8,306.8C615.14,300.09 615.14,289.17 621.8,282.45z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_arrow_left.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"30dp\"\n    android:height=\"30dp\"\n    android:viewportWidth=\"1024\"\n    android:viewportHeight=\"1024\">\n  <path\n      android:pathData=\"M589.09,790.62L310.46,512l278.62,-278.62 45.25,45.25L400.96,512l233.38,233.38z\"\n      android:fillColor=\"@color/white\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_arrow_left_black.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"30dp\"\n    android:height=\"30dp\"\n    android:viewportWidth=\"1024\"\n    android:viewportHeight=\"1024\">\n  <path\n      android:fillColor=\"@color/text_black_color\"\n      android:pathData=\"M589.09,790.62L310.46,512l278.62,-278.62 45.25,45.25L400.96,512l233.38,233.38z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_attr_file.xml",
    "content": "<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:fillColor=\"@color/color_icon_grey\"\n      android:pathData=\"M2,12.5C2,9.46 4.46,7 7.5,7H18c2.21,0 4,1.79 4,4s-1.79,4 -4,4H9.5C8.12,15 7,13.88 7,12.5S8.12,10 9.5,10H17v2H9.41c-0.55,0 -0.55,1 0,1H18c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2H7.5C5.57,9 4,10.57 4,12.5S5.57,16 7.5,16H17v2H7.5C4.46,18 2,15.54 2,12.5z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_attr_str.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"1024\"\n    android:viewportHeight=\"1024\">\n  <path\n      android:fillColor=\"@color/color_icon_grey\"\n      android:pathData=\"M820.54,141.21L203.48,141.21a62.44,62.44 0,0 0,-62.25 62.25v617.07a62.44,62.44 0,0 0,62.25 62.25h617.06a62.44,62.44 0,0 0,62.25 -62.25L882.79,203.48a62.44,62.44 0,0 0,-62.25 -62.27zM837.02,774.7a62.44,62.44 0,0 1,-62.25 62.25L249.3,836.95a62.44,62.44 0,0 1,-62.25 -62.25v-525.39a62.44,62.44 0,0 1,62.25 -62.32h525.46a62.44,62.44 0,0 1,62.25 62.32z\" />\n  <path\n      android:fillColor=\"@color/color_icon_grey\"\n      android:pathData=\"M264.42,371a22.89,22.89 0,1 0,45.79 0,22.89 22.89,0 1,0 -45.79,0zM412.27,348.11h324.41q22.89,0 22.89,22.89v-0.01q0,22.89 -22.89,22.89H412.27q-22.89,0 -22.89,-22.89v0.01q0,-22.89 22.89,-22.89zM264.42,512.67a22.89,22.89 0,1 0,45.79 0,22.89 22.89,0 1,0 -45.79,0zM412.27,489.77h324.41q22.89,0 22.89,22.89v-0.01q0,22.89 -22.89,22.89H412.27q-22.89,0 -22.89,-22.89v0.01q0,-22.89 22.89,-22.89zM264.42,653a22.89,22.89 0,1 0,45.79 0,22.89 22.89,0 1,0 -45.79,0zM412.27,630.1h324.41q22.89,0 22.89,22.89v-0.01q0,22.89 -22.89,22.89H412.27q-22.89,0 -22.89,-22.89v0.01q0,-22.89 22.89,-22.89z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_auto.xml",
    "content": "<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:fillColor=\"@color/color_icon_grey\"\n      android:pathData=\"M22.78,17.91C22.94,18.16 23,18.42 23,18.7C23,19.08 22.87,19.39 22.57,19.64C22.27,19.89 21.94,20 21.56,20H19.08L12.42,8H11.58L4.92,20H2.39C1.92,20 1.53,19.8 1.22,19.38C0.91,18.96 0.89,18.5 1.17,18L10.78,1.69C11.09,1.22 11.5,1 12,1C12.53,1 12.92,1.22 13.17,1.69L22.78,17.91M4.78,22.31L12,9.38L19.22,22.31L18.5,23L12,20.34L5.44,23L4.78,22.31Z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_auto_fill.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"1024\"\n    android:viewportHeight=\"1024\">\n  <path\n      android:fillColor=\"@color/color_icon_grey\"\n      android:pathData=\"M114.15,485.52L114.15,218.4C114.15,120.97 164.18,113.78 213.08,113.78L610.7,113.78C686.85,113.78 711.11,141.34 711.11,219.59v53.9c0,41.93 -50.4,37.94 -50.4,0L660.71,219.59c0,-39.14 -12.32,-53.9 -50.4,-53.9L213.08,165.69c-40.68,0 -50.4,5.97 -50.4,53.9v533.05c0,29.16 14.93,53.9 50.4,53.9h98.93c29.87,0 32.48,53.9 0,53.9L194.05,860.44c-35.47,0 -65.34,-23.15 -76.17,-57.88A121.17,121.17 0,0 1,113.78 772.21l0.37,-286.72zM651.38,910.22c-141.99,0 -257.31,-111.53 -257.31,-248.89 0,-137.36 115.31,-248.89 257.31,-248.89 142.02,0 258.87,113.04 258.87,250.4C910.22,800.2 794.91,910.22 651.38,910.22zM652.94,462.59c-112.61,0 -205.48,89.83 -205.48,198.74 0,110.42 91.31,198.74 205.48,200.25 114.15,0 207.02,-88.35 207.02,-197.26 -1.56,-111.9 -93.27,-201.73 -207.02,-201.73zM600.66,312.89c40.65,0 35.21,50.52 0,50.52L291.13,363.41c-37.94,0 -36.78,-50.52 0,-50.52h309.53zM343.35,462.59c40.62,0 35.21,50.52 0,50.52h-52.22c-37.94,0 -36.78,-50.52 0,-50.52h52.22zM758.93,586.98a24.46,24.46 0,0 1,1.31 4.64c0.31,1.99 0.65,3.67 0.65,5.66s-0.34,3.98 -1,5.97a20.54,20.54 0,0 1,-3.64 5.63,63.57 63.57,0 0,1 -4.95,4.64l-3.64,3.64 -3.3,3.33 -42.35,-42.47c1.65,-1.65 3.98,-3.64 6.63,-5.97 2.65,-2.3 4.64,-4.3 6.26,-5.63a25.83,25.83 0,0 1,6.63 -3.64,22.87 22.87,0 0,1 6.63,-1c2.3,0 4.3,0.34 6.26,1 1.99,0.65 3.67,1.34 5.32,1.99 3.3,1.65 6.6,4.64 10.58,8.62 3.98,3.98 6.6,8.96 8.59,13.6zM582.29,697.77c1,-1 2.99,-2.99 6.26,-6.66 3.33,-3.64 7.62,-7.62 12.6,-12.6l83.68,-83.91 42.33,42.78 -48.64,49.1 -18.18,18.57c-5.97,5.97 -11.58,11.29 -16.21,16.24 -4.95,4.98 -8.93,8.96 -11.89,12.29a47.93,47.93 0,0 1,-5.63 5.29l-5.29,4.32c-1.99,1.34 -3.98,2.65 -6.26,3.64 -1.99,1 -5.32,2.33 -9.27,3.98 -3.98,1.65 -8.28,2.99 -12.91,4.32a252.19,252.19 0,0 1,-12.57 3.64c-3.98,1 -6.94,1.65 -8.93,1.99 -3.98,0.34 -6.94,0 -8.25,-1.65 -1.34,-1.68 -1.68,-4.32 -1,-8.31 0.31,-1.99 1,-5.29 2.3,-9.27 1,-3.98 2.3,-8.31 3.64,-12.6 1.31,-4.32 2.65,-8.31 3.64,-11.95 1.31,-3.64 2.3,-6.31 2.96,-7.62l2.99,-5.97c1.34,-1.99 2.65,-3.98 4.64,-5.63z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_baseline_arrow_drop_down_24.xml",
    "content": "<vector android:height=\"24dp\" android:tint=\"@color/color_icon_grey\"\n    android:viewportHeight=\"24\" android:viewportWidth=\"24\"\n    android:width=\"24dp\" xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <path android:fillColor=\"@android:color/white\" android:pathData=\"M7,10l5,5 5,-5z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_baseline_arrow_drop_up_24.xml",
    "content": "<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    >\n  <path\n      android:fillColor=\"@color/color_icon_grey\"\n      android:pathData=\"M7,14l5,-5 5,5z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_baseline_bug_report_24.xml",
    "content": "<vector android:height=\"24dp\" android:tint=\"@color/color_icon_grey\"\n    android:viewportHeight=\"24\" android:viewportWidth=\"24\"\n    android:width=\"24dp\" xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <path android:fillColor=\"@android:color/white\" android:pathData=\"M20,8h-2.81c-0.45,-0.78 -1.07,-1.45 -1.82,-1.96L17,4.41 15.59,3l-2.17,2.17C12.96,5.06 12.49,5 12,5c-0.49,0 -0.96,0.06 -1.41,0.17L8.41,3 7,4.41l1.62,1.63C7.88,6.55 7.26,7.22 6.81,8L4,8v2h2.09c-0.05,0.33 -0.09,0.66 -0.09,1v1L4,12v2h2v1c0,0.34 0.04,0.67 0.09,1L4,16v2h2.81c1.04,1.79 2.97,3 5.19,3s4.15,-1.21 5.19,-3L20,18v-2h-2.09c0.05,-0.33 0.09,-0.66 0.09,-1v-1h2v-2h-2v-1c0,-0.34 -0.04,-0.67 -0.09,-1L20,10L20,8zM14,16h-4v-2h4v2zM14,12h-4v-2h4v2z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_baseline_casino_24.xml",
    "content": "<vector android:height=\"24dp\" android:tint=\"@color/color_icon_grey\"\n    android:viewportHeight=\"24\" android:viewportWidth=\"24\"\n    android:width=\"24dp\" xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <path android:fillColor=\"@android:color/white\" android:pathData=\"M19,3L5,3c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2L21,5c0,-1.1 -0.9,-2 -2,-2zM7.5,18c-0.83,0 -1.5,-0.67 -1.5,-1.5S6.67,15 7.5,15s1.5,0.67 1.5,1.5S8.33,18 7.5,18zM7.5,9C6.67,9 6,8.33 6,7.5S6.67,6 7.5,6 9,6.67 9,7.5 8.33,9 7.5,9zM12,13.5c-0.83,0 -1.5,-0.67 -1.5,-1.5s0.67,-1.5 1.5,-1.5 1.5,0.67 1.5,1.5 -0.67,1.5 -1.5,1.5zM16.5,18c-0.83,0 -1.5,-0.67 -1.5,-1.5s0.67,-1.5 1.5,-1.5 1.5,0.67 1.5,1.5 -0.67,1.5 -1.5,1.5zM16.5,9c-0.83,0 -1.5,-0.67 -1.5,-1.5S15.67,6 16.5,6s1.5,0.67 1.5,1.5S17.33,9 16.5,9z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_baseline_confirmation_number_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:height=\"24dp\"\n    android:tint=\"@color/color_icon_grey\"\n    android:viewportHeight=\"24\"\n    android:viewportWidth=\"24\"\n    android:width=\"24dp\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M22,10V6c0,-1.11 -0.9,-2 -2,-2H4C2.9,4 2.01,4.89 2.01,6v4C3.11,10 4,10.9 4,12s-0.89,2 -2,2v4c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2v-4c-1.1,0 -2,-0.9 -2,-2S20.9,10 22,10zM13,17.5h-2v-2h2V17.5zM13,13h-2v-2h2V13zM13,8.5h-2v-2h2V8.5z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_baseline_content_copy_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:height=\"24dp\"\n    android:tint=\"@color/color_icon_grey\"\n    android:viewportHeight=\"24\"\n    android:viewportWidth=\"24\"\n    android:width=\"24dp\">\n  <path\n      android:fillColor=\"@color/color_icon_grey\"\n      android:pathData=\"M16,1L4,1c-1.1,0 -2,0.9 -2,2v14h2L4,3h12L16,1zM19,5L8,5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h11c1.1,0 2,-0.9 2,-2L21,7c0,-1.1 -0.9,-2 -2,-2zM19,21L8,21L8,7h11v14z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_baseline_edit_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:height=\"24dp\"\n    android:tint=\"@color/color_icon_grey\"\n    android:viewportHeight=\"24\"\n    android:viewportWidth=\"24\"\n    android:width=\"24dp\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M3,17.25V21h3.75L17.81,9.94l-3.75,-3.75L3,17.25zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_baseline_event_busy_24.xml",
    "content": "<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:fillColor=\"@color/color_icon_grey\"\n      android:pathData=\"M9.31,17l2.44,-2.44L14.19,17l1.06,-1.06 -2.44,-2.44 2.44,-2.44L14.19,10l-2.44,2.44L9.31,10l-1.06,1.06 2.44,2.44 -2.44,2.44L9.31,17zM19,3h-1L18,1h-2v2L8,3L8,1L6,1v2L5,3c-1.11,0 -1.99,0.9 -1.99,2L3,19c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2L21,5c0,-1.1 -0.9,-2 -2,-2zM19,19L5,19L5,8h14v11z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_baseline_g_translate_24.xml",
    "content": "<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:fillColor=\"@color/color_icon_grey\"\n      android:pathData=\"M21,4L11,4l-1,-3L3,1c-1.1,0 -2,0.9 -2,2v15c0,1.1 0.9,2 2,2h8l1,3h9c1.1,0 2,-0.9 2,-2L23,6c0,-1.1 -0.9,-2 -2,-2zM7,16c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5c1.35,0 2.48,0.5 3.35,1.3L9.03,8.57c-0.38,-0.36 -1.04,-0.78 -2.03,-0.78 -1.74,0 -3.15,1.44 -3.15,3.21S5.26,14.21 7,14.21c2.01,0 2.84,-1.44 2.92,-2.41L7,11.8v-1.71h4.68c0.07,0.31 0.12,0.61 0.12,1.02C11.8,13.97 9.89,16 7,16zM13.17,10.58h3.7c-0.43,1.25 -1.11,2.43 -2.05,3.47 -0.31,-0.35 -0.6,-0.72 -0.86,-1.1l-0.79,-2.37zM21.5,20.5c0,0.55 -0.45,1 -1,1L14,21.5l2,-2.5 -1.04,-3.1 3.1,3.1 0.92,-0.92 -3.3,-3.25 0.02,-0.02c1.13,-1.25 1.93,-2.69 2.4,-4.22L20,10.59v-1.3h-4.53L15.47,8h-1.29v1.29h-1.44L11.46,5.5h9.04c0.55,0 1,0.45 1,1v14z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_baseline_label_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:autoMirrored=\"true\"\n    android:height=\"24dp\"\n    android:tint=\"@color/color_icon_grey\"\n    android:viewportHeight=\"24\"\n    android:viewportWidth=\"24\"\n    android:width=\"24dp\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M17.63,5.84C17.27,5.33 16.67,5 16,5L5,5.01C3.9,5.01 3,5.9 3,7v10c0,1.1 0.9,1.99 2,1.99L16,19c0.67,0 1.27,-0.33 1.63,-0.84L22,12l-4.37,-6.16z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_baseline_language_24.xml",
    "content": "<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:fillColor=\"@color/color_icon_grey\"\n      android:pathData=\"M11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM18.92,8h-2.95c-0.32,-1.25 -0.78,-2.45 -1.38,-3.56 1.84,0.63 3.37,1.91 4.33,3.56zM12,4.04c0.83,1.2 1.48,2.53 1.91,3.96h-3.82c0.43,-1.43 1.08,-2.76 1.91,-3.96zM4.26,14C4.1,13.36 4,12.69 4,12s0.1,-1.36 0.26,-2h3.38c-0.08,0.66 -0.14,1.32 -0.14,2 0,0.68 0.06,1.34 0.14,2L4.26,14zM5.08,16h2.95c0.32,1.25 0.78,2.45 1.38,3.56 -1.84,-0.63 -3.37,-1.9 -4.33,-3.56zM8.03,8L5.08,8c0.96,-1.66 2.49,-2.93 4.33,-3.56C8.81,5.55 8.35,6.75 8.03,8zM12,19.96c-0.83,-1.2 -1.48,-2.53 -1.91,-3.96h3.82c-0.43,1.43 -1.08,2.76 -1.91,3.96zM14.34,14L9.66,14c-0.09,-0.66 -0.16,-1.32 -0.16,-2 0,-0.68 0.07,-1.35 0.16,-2h4.68c0.09,0.65 0.16,1.32 0.16,2 0,0.68 -0.07,1.34 -0.16,2zM14.59,19.56c0.6,-1.11 1.06,-2.31 1.38,-3.56h2.95c-0.96,1.65 -2.49,2.93 -4.33,3.56zM16.36,14c0.08,-0.66 0.14,-1.32 0.14,-2 0,-0.68 -0.06,-1.34 -0.14,-2h3.38c0.16,0.64 0.26,1.31 0.26,2s-0.1,1.36 -0.26,2h-3.38z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_baseline_link_24.xml",
    "content": "<vector android:height=\"24dp\" android:tint=\"@color/color_icon_grey\"\n    android:viewportHeight=\"24\" android:viewportWidth=\"24\"\n    android:width=\"24dp\" xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <path android:fillColor=\"@android:color/white\" android:pathData=\"M3.9,12c0,-1.71 1.39,-3.1 3.1,-3.1h4L11,7L7,7c-2.76,0 -5,2.24 -5,5s2.24,5 5,5h4v-1.9L7,15.1c-1.71,0 -3.1,-1.39 -3.1,-3.1zM8,13h8v-2L8,11v2zM17,7h-4v1.9h4c1.71,0 3.1,1.39 3.1,3.1s-1.39,3.1 -3.1,3.1h-4L13,17h4c2.76,0 5,-2.24 5,-5s-2.24,-5 -5,-5z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_baseline_open_with.xml",
    "content": "<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:fillColor=\"@color/color_icon_grey\"\n      android:pathData=\"M10,9h4L14,6h3l-5,-5 -5,5h3v3zM9,10L6,10L6,7l-5,5 5,5v-3h3v-4zM23,12l-5,-5v3h-3v4h3v3l5,-5zM14,15h-4v3L7,18l5,5 5,-5h-3v-3z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_baseline_photo_camera_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:height=\"24dp\"\n    android:tint=\"@color/color_icon_grey\"\n    android:viewportHeight=\"24\"\n    android:viewportWidth=\"24\"\n    android:width=\"24dp\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M12,12m-3.2,0a3.2,3.2 0,1 1,6.4 0a3.2,3.2 0,1 1,-6.4 0\" />\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M9,2L7.17,4L4,4c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,6c0,-1.1 -0.9,-2 -2,-2h-3.17L15,2L9,2zM12,17c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5 -2.24,5 -5,5z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_change.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"1024\"\n    android:viewportHeight=\"1024\">\n  <path\n      android:fillColor=\"@color/color_icon_grey\"\n      android:pathData=\"M283.65,338.02l22.63,36.1 20.89,28.67 0.36,-0.31c8.55,10.29 21.15,19.92 35.33,19.92 24.22,0 46.64,-19.2 46.64,-45.52a47.36,47.36 0,0 0,-2.71 -15.16c-2.71,-8.24 -7.58,-18.07 -14.18,-23.81l-50.84,-70.71a47.1,47.1 0,0 0,-36.2 -16.95L150.84,250.27c-26.42,0 -47.92,16.84 -47.92,43.88s21.5,43.88 47.87,43.88h132.92zM723.3,360.86v48.64c-0.2,3.07 -0.2,6.14 0,9.22v3.99h0.46a49.72,49.72 0,0 0,13.06 26.32,46.39 46.39,0 0,0 67.17,0L880.64,371.97l26.62,-26.62a49.66,49.66 0,0 0,13.82 -34.82,49.66 49.66,0 0,0 -13.98,-34.82l-103.58,-107.37a46.49,46.49 0,0 0,-67.12 0,50.43 50.43,0 0,0 -13.11,43.93v47.31h-108.24a47.26,47.26 0,0 0,-36.35 17.1L509.95,371.1l-195.79,299.52 -8.04,11.11 -22.53,30.98L150.63,712.7c-26.42,0 -47.92,16.9 -47.92,43.88 0,27.03 21.5,43.88 47.92,43.88h155.44a47.51,47.51 0,0 0,35.64 -17.1l89.6,-122.98 183.81,-283.19 11.98,-16.38h96.15z\" />\n  <path\n      android:fillColor=\"@color/color_icon_grey\"\n      android:pathData=\"M804.04,592.18a46.49,46.49 0,0 0,-67.23 0,49.36 49.36,0 0,0 -13.11,26.01h-0.41v4.4a53.5,53.5 0,0 0,0 8.91v70.25H636.93l-31.69,-42.09 -14.49,-19.25c-8.55,-8.45 -20.99,-17.82 -36.56,-17.82 -20.48,0 -32.97,9.73 -39.22,28.42a48.03,48.03 0,0 0,-2.97 15.26c0,12.95 5.07,24.58 13.16,33.18l-0.41,0.41 54.12,74.34a47.31,47.31 0,0 0,36.2 16.95h108.24v37.89c0,18.94 2.05,39.01 13.52,50.84 9.22,8.04 18.53,12.54 31.18,12.54s26.83,-10.39 35.53,-19.66l103.63,-107.26a49.92,49.92 0,0 0,13.93 -34.82,49.61 49.61,0 0,0 -13.98,-34.92l-103.07,-103.58z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_clipbord.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"1024\"\n    android:viewportHeight=\"1024\">\n  <path\n      android:fillColor=\"@color/color_icon_grey\"\n      android:pathData=\"M907.84,659.46a30.02,30.02 0,0 1,-31.81 31.81h-115.14L760.9,345.92a77.95,77.95 0,0 0,-76.54 -76.54L339.01,269.38L339.01,153.79a30.02,30.02 0,0 1,31.81 -31.81h505.22a30.02,30.02 0,0 1,31.81 31.81v505.66zM677.63,902.21L147.33,902.21a17.02,17.02 0,0 1,-19.26 -19.2L128.06,352.13a17.02,17.02 0,0 1,19.2 -19.2h530.37a17.02,17.02 0,0 1,19.2 19.2v531.2a20.61,20.61 0,0 1,-19.2 18.82zM870.21,64.19L364.54,64.19a94.08,94.08 0,0 0,-95.87 95.87L268.67,275.2L140.61,275.2A77.95,77.95 0,0 0,64 352.19v531.2c0.7,42.05 34.56,75.9 76.61,76.61h530.75a77.95,77.95 0,0 0,76.54 -76.61v-128.06h115.14a98.56,98.56 0,0 0,96.77 -95.87v-505.6a83.78,83.78 0,0 0,-89.6 -89.6zM549.95,659.46L281.22,659.46c-12.99,0 -31.81,12.99 -31.81,25.54a29.12,29.12 0,0 0,25.6 31.81h268.67c12.99,0 31.81,-12.99 31.81,-25.54s-6.27,-25.54 -25.54,-31.81zM549.95,480.26L281.22,480.26c-12.99,0 -31.81,13.06 -31.81,25.6a29.12,29.12 0,0 0,25.6 31.81h268.67c12.99,0 31.81,-12.99 31.81,-25.6 0,-12.48 -6.27,-25.47 -25.54,-31.74z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_close.xml",
    "content": "<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:fillColor=\"@color/color_icon_grey\"\n      android:pathData=\"M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_create_time.xml",
    "content": "<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:fillColor=\"@color/color_icon_grey\"\n      android:pathData=\"M7.88,3.39L6.6,1.86 2,5.71l1.29,1.53 4.59,-3.85zM22,5.72l-4.6,-3.86 -1.29,1.53 4.6,3.86L22,5.72zM12,4c-4.97,0 -9,4.03 -9,9s4.02,9 9,9c4.97,0 9,-4.03 9,-9s-4.03,-9 -9,-9zM12,20c-3.87,0 -7,-3.13 -7,-7s3.13,-7 7,-7 7,3.13 7,7 -3.13,7 -7,7zM13,9h-2v3L8,12v2h3v3h2v-3h3v-2h-3L13,9z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_del.xml",
    "content": "<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:fillColor=\"@color/color_icon_grey\"\n      android:pathData=\"M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6v12zM19,4h-3.5l-1,-1h-5l-1,1H5v2h14V4z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_delete_sweep.xml",
    "content": "<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:fillColor=\"@color/color_icon_grey\"\n      android:pathData=\"M15,16h4v2h-4zM15,8h7v2h-7zM15,12h6v2h-6zM3,18c0,1.1 0.9,2 2,2h6c1.1,0 2,-0.9 2,-2L13,8L3,8v10zM14,5h-3l-1,-1L6,4L5,5L2,5v2h12z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_detail_edit.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"28dp\"\n    android:height=\"28dp\"\n    android:viewportWidth=\"1024\"\n    android:viewportHeight=\"1024\">\n  <path\n      android:fillColor=\"@color/color_icon_grey\"\n      android:pathData=\"M128,896h768v-64L128,832zM256,704v-146.75l224,-224L626.75,480l-224,224L256,704zM608,205.25L754.75,352 672,434.75 525.25,288 608,205.25zM822.62,374.66l0.03,-0.03a32,32 0,0 0,0 -45.25l-0.03,-0.03 -191.97,-191.97 -0.03,-0.03a32,32 0,0 0,-45.25 0l-0.03,0.03L192,530.75L192,768h237.25l393.38,-393.34z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_done.xml",
    "content": "<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:fillColor=\"@color/color_icon_grey\"\n      android:pathData=\"M9,16.2L4.8,12l-1.4,1.4L9,19 21,7l-1.4,-1.4L9,16.2z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_done_all.xml",
    "content": "<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:fillColor=\"@color/color_icon_grey\"\n      android:pathData=\"M18,7l-1.41,-1.41 -6.34,6.34 1.41,1.41L18,7zM22.24,5.59L11.66,16.17 7.48,12l-1.41,1.41L11.66,19l12,-12 -1.42,-1.41zM0.41,13.41L6,19l1.41,-1.41L1.83,12 0.41,13.41z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_download_24px.xml",
    "content": "<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:fillColor=\"@color/color_icon_grey\"\n      android:pathData=\"M16,13h-3V3h-2v10H8l4,4 4,-4zM4,19v2h16v-2H4z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_dropbox.xml",
    "content": "<vector android:height=\"24.380953dp\" android:viewportHeight=\"1024\"\n    android:viewportWidth=\"1092\" android:width=\"26dp\" xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <path android:fillColor=\"#0F82E2\" android:pathData=\"M546.13,592.4l-224.82,189.17L0,570.12l222.19,-179.31L546.13,592.4l323.95,-201.66L1092.27,570.12l-321.31,211.44L546.13,592.4zM321.31,0L0,211.44l222.19,179.31L546.13,189.14 321.31,0zM546.79,633.11L321.31,821.7l-96.48,-63.51v71.18L546.77,1024l321.99,-194.63L868.76,758.22l-96.51,63.51 -225.48,-188.6zM1092.27,211.44L770.96,0 546.13,189.17l323.95,201.61L1092.27,211.44z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_eco.xml",
    "content": "<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:fillColor=\"@color/color_icon_grey\"\n      android:pathData=\"M6.05,8.05c-2.73,2.73 -2.73,7.15 -0.02,9.88c1.47,-3.4 4.09,-6.24 7.36,-7.93c-2.77,2.34 -4.71,5.61 -5.39,9.32c2.6,1.23 5.8,0.78 7.95,-1.37C19.43,14.47 20,4 20,4S9.53,4.57 6.05,8.05z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_fab_dir.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"1024\"\n    android:viewportHeight=\"1024\">\n  <path\n      android:fillColor=\"@color/white\"\n      android:pathData=\"M975,774.77H49V220.37h857.09c38.11,0 69.04,30.93 69.04,69.04v485.37h-0.14zM627.71,220.37c0,-63.93 -51.78,-115.71 -115.71,-115.71H49v115.71h578.71\" />\n  <path\n      android:fillColor=\"@color/colorPrimary\"\n      android:pathData=\"M164.72,620.26V273.11h694.56v347.14h-694.56z\" />\n  <path\n      android:fillColor=\"@color/white\"\n      android:pathData=\"M975,848.1V398.49c0,-31.9 -25.96,-57.86 -57.86,-57.86H106.86c-31.9,0 -57.86,25.96 -57.86,57.86v449.46c0,39.35 31.9,71.39 71.39,71.39h783.35c39.35,0.14 71.25,-31.9 71.25,-71.25z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_favorite_24px.xml",
    "content": "<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:fillColor=\"@color/color_icon_grey\"\n      android:pathData=\"M12,21.35l-1.45,-1.32C5.4,15.36 2,12.28 2,8.5 2,5.42 4.42,3 7.5,3c1.74,0 3.41,0.81 4.5,2.09C13.09,3.81 14.76,3 16.5,3 19.58,3 22,5.42 22,8.5c0,3.78 -3.4,6.86 -8.55,11.54L12,21.35z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_feedback_24px.xml",
    "content": "<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:fillColor=\"@color/color_icon_grey\"\n      android:pathData=\"M20,2L4,2c-1.1,0 -1.99,0.9 -1.99,2L2,22l4,-4h14c1.1,0 2,-0.9 2,-2L22,4c0,-1.1 -0.9,-2 -2,-2zM13,14h-2v-2h2v2zM13,10h-2L11,6h2v4z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_file_24px.xml",
    "content": "<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:fillColor=\"@color/color_icon_grey\"\n      android:pathData=\"M6,2c-1.1,0 -1.99,0.9 -1.99,2L4,20c0,1.1 0.89,2 1.99,2L18,22c1.1,0 2,-0.9 2,-2L20,8l-6,-6L6,2zM13,9L13,3.5L18.5,9L13,9z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_fingerprint.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"1024\"\n    android:viewportHeight=\"1024\">\n  <path\n      android:fillColor=\"@color/text_black_color\"\n      android:pathData=\"M466.73,933.65a14.61,14.61 0,0 1,-11.11 -23.94,622.71 622.71,0 0,0 60.27,-85.84 618.62,618.62 0,0 0,45.18 -95.71,612.8 612.8,0 0,0 37.56,-186.34c0.42,-9.34 0.63,-18.79 0.63,-28.13v-0.21,-0.18a89.54,89.54 0,0 0,-3.92 -26.05,87.04 87.04,0 0,0 -27.83,-41.5 88.43,88.43 0,0 0,-21.53 -13.1,86.86 86.86,0 0,0 -59.93,-2.95 86.71,86.71 0,0 0,-41.38 27.92,88.12 88.12,0 0,0 -18.13,38.01 88.24,88.24 0,0 0,-1.81 17.68L424.72,513.69a445.59,445.59 0,0 1,-6.11 73.4,434.93 434.93,0 0,1 -35.57,113 434.9,434.9 0,0 1,-112.37 146.07,14.49 14.49,0 0,1 -20.48,-1.75 14.61,14.61 0,0 1,1.75 -20.54,410.56 410.56,0 0,0 59.54,-62.34 407.58,407.58 0,0 0,54.15 -93.97,404.27 404.27,0 0,0 30.06,-153.9v-0.18,-0.18a119.02,119.02 0,0 1,5.24 -34.7,117.07 117.07,0 0,1 37.04,-55.36 116.56,116.56 0,0 1,50.6 -24.24,116.4 116.4,0 0,1 139.75,114.3L628.31,513.69a661.02,661.02 0,0 1,-5.84 87.34,647.47 647.47,0 0,1 -25,110.77 641.87,641.87 0,0 1,-102.46 195.25c-5.54,7.23 -11.29,14.43 -17.17,21.38a14.46,14.46 0,0 1,-11.11 5.21zM598.77,926.84a14.61,14.61 0,0 1,-12.2 -22.47,722.67 722.67,0 0,0 68.07,-135.08 723.79,723.79 0,0 0,29.27 -98.33c1.87,-8.43 3.61,-16.99 5.15,-25.48a14.55,14.55 0,1 1,28.58 5.27,747.82 747.82,0 0,1 -106.65,269.43 14.52,14.52 0,0 1,-12.23 6.66zM354.03,904.37a14.61,14.61 0,0 1,-10.15 -25,517.57 517.57,0 0,0 63.97,-76.11 511.4,511.4 0,0 0,66.02 -135.29,510.34 510.34,0 0,0 23.61,-154.29 14.55,14.55 0,1 1,29.03 0,554.01 554.01,0 0,1 -6.38,83.55 542.84,542.84 0,0 1,-27.29 104.99,541.06 541.06,0 0,1 -61.05,117.58 543.5,543.5 0,0 1,-67.64 80.41,14.46 14.46,0 0,1 -10.12,4.16zM749.96,859.2a14.58,14.58 0,0 1,-13.34 -20.3,829.02 829.02,0 0,0 39.66,-116.25 823.12,823.12 0,0 0,25.42 -165.92,840.52 840.52,0 0,0 1.11,-43.07v-0.18,-0.18a298.47,298.47 0,0 0,-5.9 -58.85,291.84 291.84,0 0,0 -29.15,-80.26 292.14,292.14 0,0 0,-142.52 -129.69,289.61 289.61,0 0,0 -113.24,-22.89 14.55,14.55 0,0 1,0 -29.15,324.43 324.43,0 0,1 64.48,6.51 318.46,318.46 0,0 1,182.39 110.17,322.83 322.83,0 0,1 47.89,79.21 320.24,320.24 0,0 1,25.12 124.96L831.88,513.69a877.69,877.69 0,0 1,-7.02 110.23,852.84 852.84,0 0,1 -61.56,226.48 14.52,14.52 0,0 1,-13.34 8.82zM188.69,776.07a14.61,14.61 0,0 1,-9.01 -26.02,303.56 303.56,0 0,0 66.92,-73.67 302.2,302.2 0,0 0,29.79 -60.24,300.82 300.82,0 0,0 17.83,-99.87 14.64,14.64 0,0 1,-0.3 -3.01,222.87 222.87,0 0,1 4.43,-44.09 218.99,218.99 0,0 1,32.8 -78.25,219.74 219.74,0 0,1 58.88,-59.03 14.49,14.49 0,0 1,22.65 12.05,14.58 14.58,0 0,1 -6.38,12.08 190.31,190.31 0,0 0,-68.49 83.43,189.23 189.23,0 0,0 -14.82,71.26 14.76,14.76 0,0 1,0.3 2.98,339 339,0 0,1 -5.03,57.83 329.49,329.49 0,0 1,-37.74 104.93,331.6 331.6,0 0,1 -82.82,96.5 14.43,14.43 0,0 1,-9.01 3.13zM136.28,685.39a14.61,14.61 0,0 1,-9.64 -25.45,195.46 195.46,0 0,0 65.51,-146.25v-0.21,-0.18a327.38,327.38 0,0 1,6.48 -64.69,320.93 320.93,0 0,1 32.11,-88.3 14.52,14.52 0,1 1,25.51 13.88,291.99 291.99,0 0,0 -29.15,80.26 293.74,293.74 0,0 0,-5.9 58.85L221.18,513.69a229.98,229.98 0,0 1,-5.27 48.67,223.29 223.29,0 0,1 -45.93,94.33 225.79,225.79 0,0 1,-24.09 25.03,14.46 14.46,0 0,1 -9.64,3.67zM713.58,582.87a14.55,14.55 0,0 1,-14.49 -15.66,735.41 735.41,0 0,0 1.96,-53.52v-0.21,-0.18a194.08,194.08 0,0 0,-3.86 -38.25,189.62 189.62,0 0,0 -28.4,-67.79 14.55,14.55 0,0 1,24.09 -16.32,219.65 219.65,0 0,1 27.41,57.28 218.5,218.5 0,0 1,9.79 65.05v0.42a770.41,770.41 0,0 1,-2.02 55.66,14.55 14.55,0 0,1 -14.46,13.52zM104.96,530.37a14.88,14.88 0,0 1,-1.87 -0.12h-0.03a14.61,14.61 0,0 1,-12.65 -14.46v-0.09,-0.18L90.35,514.74a7.62,7.62 0,0 0,0 -0.3v-0.6,-0.12L90.35,513.3a431.53,431.53 0,0 1,8.55 -85.29,423.51 423.51,0 0,1 42.32,-116.4 424.66,424.66 0,0 1,72.58 -97.43,14.49 14.49,0 0,1 20.51,0 14.61,14.61 0,0 1,0 20.6,396.56 396.56,0 0,0 -67.58,90.74 394.3,394.3 0,0 0,-39.36 108.33,396.89 396.89,0 0,0 -7.95,77.64 14.76,14.76 0,0 1,0.15 2.2v0.3l-0.03,1.11v0.15l-0.03,0.27v0.15a2.44,2.44 0,0 1,-0.03 0.24v0.24l-0.06,0.81a14.52,14.52 0,0 1,-14.46 13.4zM919.13,527.84a14.55,14.55 0,0 1,-14.55 -14.55,403.09 403.09,0 0,0 -7.95,-79.45 393.94,393.94 0,0 0,-39.36 -108.33,394.93 394.93,0 0,0 -67.58,-90.74 14.61,14.61 0,0 1,0 -20.6,14.49 14.49,0 0,1 20.54,0 425.86,425.86 0,0 1,72.58 97.43,423.36 423.36,0 0,1 42.29,116.4A426.16,426.16 0,0 1,933.65 513.3a14.55,14.55 0,0 1,-14.52 14.55zM655.96,383.46a14.46,14.46 0,0 1,-10.24 -4.28,190.8 190.8,0 0,0 -43.58,-32.65 188.93,188.93 0,0 0,-52.01 -19,189.23 189.23,0 0,0 -57.49,-2.89 189.62,189.62 0,0 0,-36.89 7.53,14.52 14.52,0 0,1 -18.82,-13.91c0,-6.2 3.98,-11.96 10.21,-13.88a217.12,217.12 0,0 1,87.16 -8.73,218.17 218.17,0 0,1 62.64,16.08 217.9,217.9 0,0 1,69.3 46.86,14.55 14.55,0 0,1 -10.24,24.88zM296.06,311.24a14.46,14.46 0,0 1,-14.55 -14.58c0,-3.73 1.42,-7.44 4.25,-10.3a322.47,322.47 0,0 1,73.7 -55.21,319.46 319.46,0 0,1 88.06,-32.2 14.52,14.52 0,0 1,17.44 14.31,14.58 14.58,0 0,1 -11.63,14.25 289.34,289.34 0,0 0,-147 79.45,14.46 14.46,0 0,1 -10.27,4.28zM739.69,189.2a14.46,14.46 0,0 1,-8.13 -2.5,393.28 393.28,0 0,0 -66.65,-36.29c-11.75,-4.97 -23.91,-9.43 -36.08,-13.25a14.58,14.58 0,0 1,8.61 -27.8,419.54 419.54,0 0,1 110.38,53.19 14.61,14.61 0,0 1,-8.13 26.65zM284.31,189.2a14.61,14.61 0,0 1,-8.13 -26.65,422.88 422.88,0 0,1 71.62,-38.97 419.63,419.63 0,0 1,207.33 -31.02,14.55 14.55,0 0,1 -2.95,28.97 398.76,398.76 0,0 0,-119.39 5.96,390.57 390.57,0 0,0 -140.35,59.21 14.43,14.43 0,0 1,-8.13 2.5z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_folder_24px.xml",
    "content": "<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:fillColor=\"@color/color_icon_grey\"\n      android:pathData=\"M10,4H4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V8c0,-1.1 -0.9,-2 -2,-2h-8l-2,-2z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_ftp.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"128dp\"\n    android:height=\"128dp\"\n    android:viewportWidth=\"1024\"\n    android:viewportHeight=\"1024\">\n  <path\n      android:fillColor=\"#FFC107\"\n      android:pathData=\"M400,640c-8.8,0 -16,7.2 -16,16s7.2,16 16,16h224c8.8,0 16,-7.2 16,-16s-7.2,-16 -16,-16H400z\"/>\n  <path\n      android:fillColor=\"#FFC107\"\n      android:pathData=\"M946.7,711.3L832,224c0,-52.8 -43.2,-96 -96,-96L288,128c-52.8,0 -96,43.2 -96,96L77.3,711.3C68.8,728.4 64,747.7 64,768c0,24.2 6.8,46.9 18.6,66.2 0.5,0.9 1.1,1.8 1.6,2.6 1.7,2.6 3.4,5.1 5.3,7.6 7.4,9.9 16.2,18.6 26,26 2.5,1.8 5,3.6 7.6,5.3 0.9,0.6 1.7,1.1 2.6,1.6 19.4,11.8 42,18.6 66.2,18.6h640c35.2,0 67.2,-14.4 90.4,-37.6 0.7,-0.7 1.4,-1.5 2.1,-2.2 2.1,-2.2 4.2,-4.5 6.1,-6.9 1.3,-1.6 2.6,-3.2 3.8,-4.8 1.8,-2.5 3.6,-5 5.3,-7.6 0.6,-0.9 1.1,-1.7 1.6,-2.6 11.8,-19.4 18.6,-42 18.6,-66.2 0.2,-20.3 -4.6,-39.6 -13.1,-56.7zM927.9,771.8c-0.1,1.3 -0.1,2.6 -0.2,3.9 0,0.2 0,0.5 -0.1,0.7 -0.1,1.3 -0.2,2.6 -0.4,3.8 0,0.2 -0.1,0.5 -0.1,0.7 -0.2,1.1 -0.3,2.2 -0.5,3.3 -0.1,0.4 -0.1,0.8 -0.2,1.2 -0.2,1 -0.4,2 -0.6,2.9l-0.3,1.5c-0.2,0.9 -0.5,1.8 -0.7,2.8 -0.1,0.5 -0.3,1 -0.4,1.5 -0.3,0.9 -0.5,1.8 -0.8,2.8 -0.2,0.5 -0.4,1.1 -0.5,1.6 -0.3,0.9 -0.6,1.8 -0.9,2.6 -0.2,0.5 -0.4,1.1 -0.6,1.6 -0.3,0.9 -0.7,1.8 -1.1,2.6l-0.6,1.5c-0.4,0.9 -0.8,1.9 -1.3,2.8 -0.2,0.4 -0.4,0.8 -0.6,1.1 -0.5,1 -1,2 -1.6,3 -0.1,0.2 -0.2,0.4 -0.4,0.7 -0.7,1.2 -1.3,2.3 -2,3.5 -3.9,6.4 -8.6,12.3 -14.1,17.7 -5.6,5.6 -11.9,10.5 -18.5,14.5 -0.2,0.1 -0.5,0.3 -0.7,0.4 -0.4,0.2 -0.8,0.5 -1.1,0.7 -0.5,0.3 -0.9,0.5 -1.4,0.8 -0.2,0.1 -0.3,0.2 -0.5,0.3 -10.2,5.5 -21.4,9.3 -33.4,10.8 -0.7,0.1 -1.5,0.2 -2.2,0.3h-0.2c-0.7,0.1 -1.3,0.1 -2,0.2h-0.4c-0.6,0 -1.3,0.1 -1.9,0.1h-0.5c-0.6,0 -1.2,0.1 -1.8,0.1L189,863.8c-0.6,0 -1.2,0 -1.8,-0.1h-0.5c-0.6,0 -1.3,-0.1 -1.9,-0.1h-0.4c-0.7,-0.1 -1.3,-0.1 -2,-0.2h-0.2c-0.7,-0.1 -1.5,-0.2 -2.2,-0.3 -11.9,-1.5 -23.2,-5.3 -33.4,-10.8 -0.2,-0.1 -0.3,-0.2 -0.5,-0.3 -0.5,-0.3 -0.9,-0.5 -1.4,-0.8 -0.4,-0.2 -0.8,-0.4 -1.1,-0.7 -0.2,-0.1 -0.5,-0.3 -0.7,-0.4 -6.6,-4 -12.9,-8.9 -18.5,-14.5 -5.4,-5.4 -10.1,-11.4 -14.1,-17.7 -0.7,-1.1 -1.4,-2.3 -2,-3.5 -0.1,-0.2 -0.2,-0.4 -0.4,-0.7 -0.6,-1 -1.1,-2 -1.6,-3 -0.2,-0.4 -0.4,-0.8 -0.6,-1.1 -0.4,-0.9 -0.9,-1.8 -1.3,-2.8l-0.6,-1.5c-0.4,-0.9 -0.7,-1.7 -1.1,-2.6 -0.2,-0.5 -0.4,-1.1 -0.6,-1.6 -0.3,-0.9 -0.6,-1.8 -0.9,-2.6 -0.2,-0.5 -0.4,-1.1 -0.5,-1.6 -0.3,-0.9 -0.6,-1.8 -0.8,-2.8 -0.1,-0.5 -0.3,-1 -0.4,-1.5 -0.2,-0.9 -0.5,-1.8 -0.7,-2.8l-0.3,-1.5c-0.2,-1 -0.4,-2 -0.6,-2.9 -0.1,-0.4 -0.1,-0.8 -0.2,-1.2 -0.2,-1.1 -0.4,-2.2 -0.5,-3.3 0,-0.2 -0.1,-0.5 -0.1,-0.7 -0.2,-1.3 -0.3,-2.5 -0.4,-3.8 0,-0.2 0,-0.5 -0.1,-0.7 -0.1,-1.3 -0.2,-2.6 -0.2,-3.9v-0.4l11.5,-49c16.3,-29.9 48.1,-50.4 84.4,-50.4h48c8.8,0 16,-7.2 16,-16s-7.2,-16 -16,-16h-48c-25.8,0 -49.8,7.7 -69.9,20.9l101.1,-429.6 0.9,-3.6L224.4,224c0,-17 6.7,-33 18.8,-45.2S271,160 288,160h448c17,0 33,6.7 45.2,18.8S800,207 800,224v3.7l0.9,3.6L902,660.9c-20.2,-13.2 -44.2,-20.9 -70,-20.9h-48c-8.8,0 -16,7.2 -16,16s7.2,16 16,16h48c36.3,0 68.1,20.4 84.4,50.4l11.5,49v0.4z\"/>\n  <path\n      android:fillColor=\"#FF9800\"\n      android:pathData=\"M832,704L192,704c-35.2,0 -64,28.8 -64,64s28.8,64 64,64h640c35.2,0 64,-28.8 64,-64s-28.8,-64 -64,-64zM512,768c0,12.1 -9.9,22 -22,22L214,790c-12.1,0 -22,-9.9 -22,-22s9.9,-22 22,-22h276c12.1,0 22,9.9 22,22zM800,768c0,17.6 -14.4,32 -32,32s-32,-14.4 -32,-32 14.4,-32 32,-32 32,14.4 32,32zM317.1,431.1h76.3v-32.6h-76.3v-45.8h88.4v-32.6L280.3,320.1v193.1h36.8zM491.6,513.2h36.9L528.5,352.7h54.1v-32.6L437.5,320.1v32.6h54.1zM651.4,440.4h24c16.7,0 29.4,-0.9 38.2,-2.8 6.5,-1.5 12.8,-4.5 19.1,-9.2 6.3,-4.6 11.4,-11 15.5,-19 4.1,-8.1 6.1,-18 6.1,-29.9 0,-15.4 -3.5,-27.9 -10.6,-37.6 -7.1,-9.7 -15.8,-16 -26.3,-18.9 -6.8,-1.9 -21.4,-2.9 -43.8,-2.9h-59.2v193.1h36.9v-72.8zM651.4,352.7h17.8c13.3,0 22.1,0.4 26.5,1.3 6,1.1 10.9,4 14.8,8.6 3.9,4.6 5.9,10.4 5.9,17.4 0,5.7 -1.4,10.7 -4.2,15 -2.8,4.3 -6.6,7.5 -11.5,9.5s-14.6,3 -29.1,3h-20.2v-54.8z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_google_drive.xml",
    "content": "<vector android:height=\"24dp\" android:viewportHeight=\"1024\"\n    android:viewportWidth=\"1024\" android:width=\"24dp\" xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <path android:fillColor=\"#FFC107\" android:pathData=\"M644.1,660.48h263.17l-263.17,-494.59H379.9z\"/>\n    <path android:fillColor=\"#2196F3\" android:pathData=\"M361.47,660.48l-113.15,197.63h535.55l123.39,-197.63z\"/>\n    <path android:fillColor=\"#4CAF50\" android:pathData=\"M379.9,165.89l-263.17,461.31 131.58,230.91L507.39,404.48z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_google_play.xml",
    "content": "<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:fillColor=\"@color/color_icon_grey\"\n      android:pathData=\"M3,20.5V3.5C3,2.91 3.34,2.39 3.84,2.15L13.69,12L3.84,21.85C3.34,21.6 3,21.09 3,20.5M16.81,15.12L6.05,21.34L14.54,12.85L16.81,15.12M20.16,10.81C20.5,11.08 20.75,11.5 20.75,12C20.75,12.5 20.53,12.9 20.18,13.18L17.89,14.5L15.39,12L17.89,9.5L20.16,10.81M6.05,2.66L16.81,8.88L14.54,11.15L6.05,2.66Z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_help_filled.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"128dp\"\n    android:height=\"128dp\"\n    android:viewportWidth=\"1024\"\n    android:viewportHeight=\"1024\">\n    <path\n        android:fillColor=\"@color/grey600\"\n        android:pathData=\"M544,572.74V640h-64v-128h32c52.93,0 96,-43.07 96,-96 0,-52.96 -43.07,-96 -96,-96l-0.29,0.03a96.06,96.06 0,0 0,-95.71 96h-64a160.13,160.13 0,0 1,159.71 -160H512c88.22,0 160,71.74 160,160a160.22,160.22 0,0 1,-128 156.7M511.71,800a48,48 0,1 1,0 -96,48 48,0 0,1 0,96M512,64C264.58,64 64,264.54 64,512c0,247.42 200.58,448 448,448s448,-200.58 448,-448c0,-247.46 -200.58,-448 -448,-448\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_history.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"128dp\"\n    android:height=\"128dp\"\n    android:viewportWidth=\"1024\"\n    android:viewportHeight=\"1024\">\n  <path\n      android:pathData=\"M512,51.2a460.8,460.8 0,1 0,153.6 894.98l-25.6,-72.19a384,384 0,1 1,256 -383.49L972.8,486.4A460.8,460.8 0,0 0,512 51.2z\"\n      android:fillColor=\"#8A8886\"/>\n  <path\n      android:pathData=\"M665.6,563.2h307.2v76.8h-307.2zM665.6,716.8h307.2v76.8h-307.2zM473.6,473.6H307.2v76.8h243.2V256h-76.8v217.6z\"\n      android:fillColor=\"#2196F3\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_history_record.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"1024\"\n    android:viewportHeight=\"1024\">\n  <path\n      android:fillColor=\"@color/color_icon_grey\"\n      android:pathData=\"M416.43,899.41H163.84c-22.19,0 -40.96,-18.77 -40.96,-40.96V165.55c0,-22.19 18.77,-40.96 40.96,-40.96h539.31c22.19,0 40.96,18.77 40.96,40.96v163.84c39.25,1.71 76.8,6.83 112.64,20.48V165.55c0,-85.33 -68.27,-153.6 -153.6,-153.6H163.84c-85.33,0 -153.6,68.27 -153.6,153.6v692.91c0,85.33 68.27,153.6 153.6,153.6h360.11c-42.67,-30.72 -78.51,-68.27 -107.52,-112.64z\" />\n  <path\n      android:fillColor=\"@color/color_icon_grey\"\n      android:pathData=\"M240.64,336.21h382.29c30.72,0 56.32,-25.6 56.32,-56.32 0,-30.72 -25.6,-56.32 -56.32,-56.32L240.64,223.57c-30.72,0 -56.32,25.6 -56.32,56.32 1.71,30.72 25.6,56.32 56.32,56.32zM465.92,443.73L240.64,443.73c-30.72,0 -56.32,25.6 -56.32,56.32s25.6,56.32 56.32,56.32h150.19c6.83,-13.65 11.95,-27.31 20.48,-40.96 15.36,-25.6 34.13,-49.49 54.61,-71.68zM363.52,663.89h-121.17c-30.72,0 -56.32,25.6 -56.32,56.32s25.6,56.32 56.32,56.32h126.29c-5.12,-23.89 -6.83,-47.79 -6.83,-73.39 0,-13.65 0,-27.31 1.71,-39.25zM730.45,416.43c-155.31,0 -283.31,126.29 -283.31,283.31 0,155.31 126.29,283.31 283.31,283.31S1013.76,855.04 1013.76,699.73c0,-157.01 -128,-283.31 -283.31,-283.31zM730.45,890.88c-105.81,0 -191.15,-85.33 -191.15,-191.15s85.33,-191.15 191.15,-191.15S921.6,593.92 921.6,699.73s-85.33,191.15 -191.15,191.15z\" />\n  <path\n      android:fillColor=\"@color/color_icon_grey\"\n      android:pathData=\"M819.2,677.55h-73.39v-73.39c0,-22.19 -18.77,-40.96 -40.96,-40.96 -22.19,0 -40.96,18.77 -40.96,40.96v114.35c0,8.53 1.71,17.07 6.83,22.19 6.83,10.24 20.48,18.77 34.13,18.77H819.2c22.19,0 40.96,-18.77 40.96,-40.96 0,-22.19 -18.77,-40.96 -40.96,-40.96z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_http.xml",
    "content": "<vector android:height=\"23dp\" android:viewportHeight=\"1024\"\n    android:viewportWidth=\"1061\" android:width=\"23.831055dp\" xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <path android:fillColor=\"#80FF3B\" android:pathData=\"M568.35,380.07l16.84,-0.68c73.23,-3.15 128.95,-6.9 167.1,-11.34 2.88,-0.34 6.45,-0.76 10.58,-1.33a9.48,9.48 0,0 1,10.62 8.12l2.28,17.18c4.48,37.89 6.71,77.9 6.71,120.15a1034.45,1034.45 0,0 1,-6.83 120.72,9.48 9.48,0 0,1 -10.62,8.27 1369.48,1369.48 0,0 0,-5.76 -0.76c-53.85,-6.79 -114.38,-10.77 -181.51,-11.98l-9.18,-0.15a9.48,9.48 0,0 1,-9.33 -9.48v-229.22a9.48,9.48 0,0 1,9.1 -9.48zM286.26,633.03a1033.99,1033.99 0,0 1,-6.83 -120.87,1015.45 1015.45,0 0,1 9.03,-137.59 9.48,9.48 0,0 1,10.69 -8.08l11.68,1.59a1594.68,1594.68 0,0 0,182.19 12.21,9.48 9.48,0 0,1 9.33,9.48v229.03a9.48,9.48 0,0 1,-9.33 9.48c-3.15,0 -5.69,0.08 -7.74,0.11 -66.56,1.21 -126.63,5.12 -180.18,11.83l-8.19,1.06a9.48,9.48 0,0 1,-10.62 -8.27zM502.36,10.49v302.68a9.48,9.48 0,0 1,-9.63 9.48h-2.35c-64.4,-1.21 -121.93,-5.16 -172.6,-11.76l-6.9,-0.95a9.48,9.48 0,0 1,-7.93 -11.64l1.25,-5.01c31.17,-122.69 91.78,-217.16 181.89,-284.44a332.22,332.22 0,0 1,5.39 -3.94,6.9 6.9,0 0,1 10.88,5.57zM559.24,9.96a6.71,6.71 0,0 1,10.62 -5.46l7.77,5.69c87.23,65.8 146.58,157.31 178.1,275.45l3.26,12.97a9.48,9.48 0,0 1,-7.93 11.64l-6.94,0.91A1540.44,1540.44 0,0 1,568.88 322.69a9.48,9.48 0,0 1,-9.63 -9.48L559.24,9.96zM307.04,697.35c5.76,-0.76 10.47,-1.33 14.07,-1.74a1695.25,1695.25 0,0 1,171.65 -10.47,9.48 9.48,0 0,1 9.6,9.48v314.06a9.48,9.48 0,0 1,-15.06 7.66,340.95 340.95,0 0,1 -7.02,-5.23c-88.63,-68.27 -147.91,-163.68 -177.87,-287.17a739.62,739.62 0,0 1,-3.41 -15.17,9.48 9.48,0 0,1 8,-11.38zM762.98,708.65c-0.95,4.32 -1.9,8.61 -2.92,12.89 -29.51,123.45 -88.1,218.94 -175.86,287.55a390.25,390.25 0,0 1,-9.86 7.43,9.48 9.48,0 0,1 -15.09,-7.66v-314.25a9.48,9.48 0,0 1,9.63 -9.48l15.17,0.27a1678.95,1678.95 0,0 1,157.58 10.16l13.27,1.67a9.48,9.48 0,0 1,8.08 11.42zM839.36,512.16c0,-43.42 -2.28,-84.84 -6.83,-124.28 -0.64,-5.73 -1.71,-13.65 -3.11,-23.74a9.48,9.48 0,0 1,7.36 -10.54l8.72,-2.01c62.39,-14.71 112.26,-35.12 149.84,-61.44l3.91,-2.69a9.48,9.48 0,0 1,14.03 3.79,529.06 529.06,0 0,1 48.32,221.41 529.47,529.47 0,0 1,-39.56 201.38,9.48 9.48,0 0,1 -14.03,4.25l-2.81,-1.9c-39.14,-26.21 -91.48,-46.46 -157.12,-60.91l-8.87,-1.9a9.48,9.48 0,0 1,-7.51 -10.43c0.53,-4.06 0.99,-8.12 1.44,-12.14 4.17,-37.81 6.22,-77.44 6.22,-118.86zM698.89,9.01a532.09,532.09 0,0 1,276.47 213.63,9.48 9.48,0 0,1 -2.5,12.97l-10.54,7.36c-29.96,21.01 -70.92,37.96 -122.76,50.82l-12.67,2.96a9.48,9.48 0,0 1,-11.34 -7.09l-1.59,-6.71c-27.31,-110.93 -76.46,-202.37 -147.53,-273.78a5.01,5.01 0,0 1,4.82 -8.34c11.64,3.22 20.86,5.92 27.65,8.19zM830.68,709.94c4.06,0.83 8.08,1.74 12.14,2.65 55.14,12.7 98.57,29.73 130.16,50.9l11.34,7.59a9.48,9.48 0,0 1,2.84 12.74,532.2 532.2,0 0,1 -290.62,233.24c-4.55,1.52 -10.24,3.22 -17.07,5.16a7.62,7.62 0,0 1,-7.59 -12.67c70.16,-72.66 118.33,-165.51 144.38,-277.99 0.83,-3.53 1.9,-8.31 3.15,-14.34a9.48,9.48 0,0 1,11.26 -7.28zM57.57,716.39l-3.75,2.54a9.48,9.48 0,0 1,-14.03 -4.25A529.44,529.44 0,0 1,0 512.66a529.06,529.06 0,0 1,48.96 -222.77,9.48 9.48,0 0,1 14.03,-3.75l5.8,4.06c36.41,25.49 84.54,45.47 144.34,60.07 3,0.76 7.02,1.67 12.14,2.84a9.48,9.48 0,0 1,7.28 10.58,1074.23 1074.23,0 0,0 -9.97,148.48 1089.32,1089.32 0,0 0,7.66 131.15,9.48 9.48,0 0,1 -7.47,10.47 648.52,648.52 0,0 0,-7.96 1.67c-65.65,14.49 -118.02,34.74 -157.2,60.95zM397.08,7.64C324.41,80.01 274.43,173.07 247.16,286.32l-0.68,2.88a9.48,9.48 0,0 1,-11.38 7.09c-2.24,-0.53 -4.51,-1.02 -6.75,-1.59 -53.63,-12.89 -95.84,-30.23 -126.56,-51.73L89.5,234.4a9.48,9.48 0,0 1,-2.5 -12.93A532.05,532.05 0,0 1,366.28 7.87c6.45,-2.12 15.28,-4.7 26.43,-7.7a4.48,4.48 0,0 1,4.32 7.47zM77.63,771.65l12.21,-8.15c31.48,-21.09 74.79,-38.08 129.7,-50.82l11.76,-2.54a9.52,9.52 0,0 1,11.23 7.28c1.18,5.61 2.16,10.05 2.92,13.35 25.9,112.33 73.8,205.1 143.66,277.84l0.23,0.27a8,8 0,0 1,-8 13.27,532.01 532.01,0 0,1 -306.59,-237.79 9.48,9.48 0,0 1,2.88 -12.7z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_image_blue_24px.xml",
    "content": "<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:fillColor=\"@color/colorPrimary\"\n      android:pathData=\"M21,19V5c0,-1.1 -0.9,-2 -2,-2H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2zM8.5,13.5l2.5,3.01L14.5,12l4.5,6H5l3.5,-4.5z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_image_broken_24px.xml",
    "content": "<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:fillColor=\"@color/colorPrimary\"\n      android:pathData=\"M21,5v6.59l-3,-3.01 -4,4.01 -4,-4 -4,4 -3,-3.01L3,5c0,-1.1 0.9,-2 2,-2h14c1.1,0 2,0.9 2,2zM18,11.42l3,3.01L21,19c0,1.1 -0.9,2 -2,2L5,21c-1.1,0 -2,-0.9 -2,-2v-6.58l3,2.99 4,-4 4,4 4,-3.99z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_ime_backspace.xml",
    "content": "<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:fillColor=\"@color/color_4E85DB\"\n        android:pathData=\"M22,3L7,3c-0.69,0 -1.23,0.35 -1.59,0.88L0,12l5.41,8.11c0.36,0.53 0.9,0.89 1.59,0.89h15c1.1,0 2,-0.9 2,-2L24,5c0,-1.1 -0.9,-2 -2,-2zM19,15.59L17.59,17 14,13.41 10.41,17 9,15.59 12.59,12 9,8.41 10.41,7 14,10.59 17.59,7 19,8.41 15.41,12 19,15.59z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_ime_close.xml",
    "content": "<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:fillColor=\"@color/color_4E85DB\"\n        android:pathData=\"M7,10l5,5 5,-5z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_ime_enter.xml",
    "content": "<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:fillColor=\"@color/color_4E85DB\"\n        android:pathData=\"M19,7v4H5.83l3.58,-3.59L8,6l-6,6 6,6 1.41,-1.41L5.83,13H21V7z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_ime_keyboard.xml",
    "content": "<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:fillColor=\"@color/color_4E85DB\"\n      android:pathData=\"M20,5L4,5c-1.1,0 -1.99,0.9 -1.99,2L2,17c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,7c0,-1.1 -0.9,-2 -2,-2zM11,8h2v2h-2L11,8zM11,11h2v2h-2v-2zM8,8h2v2L8,10L8,8zM8,11h2v2L8,13v-2zM7,13L5,13v-2h2v2zM7,10L5,10L5,8h2v2zM16,17L8,17v-2h8v2zM16,13h-2v-2h2v2zM16,10h-2L14,8h2v2zM19,13h-2v-2h2v2zM19,10h-2L17,8h2v2z\"/>\n</vector>"
  },
  {
    "path": "app/src/main/res/drawable/ic_ime_lock.xml",
    "content": "<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:fillColor=\"@color/color_4E85DB\"\n      android:pathData=\"M18,8h-1L17,6c0,-2.76 -2.24,-5 -5,-5S7,3.24 7,6v2L6,8c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L20,10c0,-1.1 -0.9,-2 -2,-2zM12,17c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2zM15.1,8L8.9,8L8.9,6c0,-1.71 1.39,-3.1 3.1,-3.1 1.71,0 3.1,1.39 3.1,3.1v2z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_ime_other_info.xml",
    "content": "<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:fillColor=\"@color/color_4E85DB\"\n        android:pathData=\"M4,10.5c-0.83,0 -1.5,0.67 -1.5,1.5s0.67,1.5 1.5,1.5 1.5,-0.67 1.5,-1.5 -0.67,-1.5 -1.5,-1.5zM4,4.5c-0.83,0 -1.5,0.67 -1.5,1.5S3.17,7.5 4,7.5 5.5,6.83 5.5,6 4.83,4.5 4,4.5zM4,16.5c-0.83,0 -1.5,0.68 -1.5,1.5s0.68,1.5 1.5,1.5 1.5,-0.68 1.5,-1.5 -0.67,-1.5 -1.5,-1.5zM7,19h14v-2L7,17v2zM7,13h14v-2L7,11v2zM7,5v2h14L21,5L7,5z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_ime_password.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"1024\"\n    android:viewportHeight=\"1024\">\n  <path\n      android:fillColor=\"@color/color_4E85DB\"\n      android:pathData=\"M537.6,424.96C506.88,327.68 409.6,256 302.08,256 163.84,256 51.2,368.64 51.2,506.88s112.64,250.88 250.88,250.88c107.52,0 204.8,-71.68 235.52,-168.96h184.32v168.96h168.96v-168.96L972.8,588.8L972.8,424.96h-435.2zM302.08,588.8c-46.08,0 -81.92,-35.84 -81.92,-81.92S256,424.96 302.08,424.96s81.92,35.84 81.92,81.92 -35.84,81.92 -81.92,81.92z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_ime_user.xml",
    "content": "<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:fillColor=\"@color/color_4E85DB\"\n      android:pathData=\"M12,12c2.21,0 4,-1.79 4,-4s-1.79,-4 -4,-4 -4,1.79 -4,4 1.79,4 4,4zM12,14c-2.67,0 -8,1.34 -8,4v2h16v-2c0,-2.66 -5.33,-4 -8,-4z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_img_choose.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"1024\"\n    android:viewportHeight=\"1024\">\n    <path\n        android:fillColor=\"@color/colorPrimary\"\n        android:pathData=\"M474.76,474.76V269.96c0,-23.27 18.62,-37.24 37.24,-37.24s37.24,13.96 37.24,37.24v209.45h209.45c18.62,0 37.24,13.96 37.24,37.24s-13.96,37.24 -37.24,37.24h-209.45v209.45c0,18.62 -13.96,37.24 -37.24,37.24s-37.24,-13.96 -37.24,-37.24v-209.45H269.96c-23.27,-4.65 -37.24,-23.27 -37.24,-41.89s13.96,-37.24 37.24,-37.24h204.8z\" />\n    <path\n        android:fillColor=\"@color/colorPrimary\"\n        android:pathData=\"M0,558.55h37.24v172.22H0zM0,316.51h37.24v172.22H0zM311.85,0h172.22v37.24H311.85zM553.89,0h172.22v37.24h-172.22zM926.25,37.24l32.58,9.31 4.65,-27.93C949.53,4.65 930.91,0 907.64,0h-111.71v37.24h130.33zM986.76,791.27v130.33l-4.65,32.58 27.93,4.65c9.31,-13.96 13.96,-32.58 13.96,-55.85V791.27h-37.24zM37.24,926.25c0,-4.65 -4.65,-13.96 -4.65,-18.62v-107.05H0v107.05c0,23.27 4.65,41.89 18.62,60.51l27.93,-4.65 -9.31,-37.24zM37.24,246.69V116.36c0,-9.31 0,-13.96 4.65,-23.27l4.65,-32.58 -27.93,-9.31C9.31,69.82 0,93.09 0,116.36v130.33h37.24zM307.2,986.76h172.22v37.24H307.2zM986.76,307.2h37.24v172.22h-37.24zM1010.04,65.16l-27.93,4.65 4.65,32.58v134.98h37.24V116.36c0,-18.62 -4.65,-37.24 -13.96,-51.2zM986.76,549.24h37.24v172.22h-37.24zM107.05,37.24H242.04V0H116.36c-13.96,0 -32.58,4.65 -46.55,9.31l4.65,27.93h32.58zM963.49,1010.04l-4.65,-27.93 -32.58,4.65H791.27v37.24h116.36c18.62,0 37.24,-4.65 55.85,-13.96zM237.38,1024v-37.24H107.05l-32.58,-4.65 -4.65,27.93c13.96,4.65 32.58,9.31 46.55,9.31h121.02zM549.24,986.76h172.22v37.24h-172.22z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_info_filled.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"1024\"\n    android:viewportHeight=\"1024\">\n  <path\n      android:fillColor=\"@color/red\"\n      android:pathData=\"M512,64C265.6,64 64,265.6 64,512s201.6,448 448,448 448,-201.6 448,-448S758.4,64 512,64zM544,768h-64v-288h64v288zM512,368c-25.6,0 -48,-22.4 -48,-48s22.4,-48 48,-48 48,22.4 48,48 -22.4,48 -48,48z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_keepassa.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"33.942856dp\"\n    android:viewportWidth=\"210\"\n    android:viewportHeight=\"297\">\n  <path\n      android:fillAlpha=\"1\"\n      android:fillColor=\"#4e85db\"\n      android:pathData=\"m21.167,40.368 l-0.36,-0.139C9.259,35.765 7.924,29.932 7.924,25.932L7.924,10.654l1.031,0.035C16.982,10.963 20.4,7.341 20.432,7.306L21.168,6.501 21.903,7.306c0.027,0.029 3.266,3.399 10.619,3.399l0.001,0c0.28,0 0.565,-0.004 0.855,-0.014l1.031,-0.035l0,15.277c0,4 -1.336,9.834 -12.883,14.297zM8.973,11.753l0,14.178c0,2.151 0.946,8.775 12.194,13.242C32.416,34.707 33.36,28.083 33.36,25.932L33.36,11.753C27.131,11.729 22.603,9.512 21.166,8.367 19.729,9.512 15.2,11.727 8.973,11.753Z\" />\n  <path\n      android:fillAlpha=\"1\"\n      android:fillColor=\"#4e85db\"\n      android:pathData=\"m9.777,25.787c0,2.009 0.883,8.196 11.389,12.368 10.507,-4.172 11.389,-10.359 11.389,-12.368L32.556,12.544C26.737,12.522 22.508,10.452 21.166,9.382 19.824,10.451 15.594,12.52 9.777,12.544\" />\n  <path\n      android:fillAlpha=\"1\"\n      android:fillColor=\"#ffffff\"\n      android:fillType=\"evenOdd\"\n      android:pathData=\"M19.851,37.473C16.201,35.883 12.739,33.07 11.243,30.478 9.983,28.296 9.935,27.909 9.935,19.848L9.935,12.572l0.922,0c2.592,0 6.098,-0.862 8.666,-2.132l1.637,-0.809 1.637,0.809c2.568,1.269 6.075,2.132 8.666,2.132l0.922,0l-0.005,7.343c-0.005,7.104 -0.018,7.383 -0.423,8.57 -0.23,0.675 -0.712,1.713 -1.071,2.308 -0.908,1.504 -3.515,3.958 -5.423,5.105 -1.444,0.868 -3.999,2.127 -4.282,2.111 -0.063,-0.004 -0.662,-0.245 -1.331,-0.536z\"\n      android:strokeWidth=\"0.707107\"\n      android:strokeColor=\"#ffffff\" />\n  <path\n      android:fillAlpha=\"1\"\n      android:fillColor=\"#ffffff\"\n      android:fillType=\"evenOdd\"\n      android:pathData=\"M20.462,38.841C19.354,38.378 17.595,37.487 16.607,36.89 12.299,34.283 9.602,30.766 9.09,27.086 9.042,26.743 9.019,24.116 9.019,19.168l0,-7.404l0.618,0c1.379,0 3.808,-0.326 5.398,-0.724 2.24,-0.561 4.528,-1.502 5.758,-2.367l0.364,-0.256 0.467,0.316c0.877,0.593 2.543,1.355 3.998,1.828 2.229,0.724 5.051,1.2 7.131,1.202l0.616,0.001 -0.023,7.524 -0.023,7.524 -0.15,0.719c-0.393,1.88 -1.234,3.612 -2.543,5.234 -0.924,1.145 -2.495,2.54 -3.982,3.537 -1.444,0.968 -3.547,2.066 -5.192,2.712l-0.298,0.117zM22.351,37.696c4.248,-1.876 7.337,-4.399 8.957,-7.316 0.435,-0.783 0.763,-1.601 1.018,-2.542l0.206,-0.761 0.019,-7.293c0.012,-4.643 -0.004,-7.293 -0.044,-7.293 -0.04,0 -0.074,2.647 -0.091,7.193 -0.02,5.224 -0.05,7.326 -0.108,7.679 -0.103,0.62 -0.447,1.609 -0.861,2.474 -0.621,1.297 -1.178,2.018 -2.699,3.494 -1.093,1.061 -2.175,1.929 -3.204,2.571 -0.811,0.505 -3.116,1.698 -3.842,1.988l-0.513,0.205 -0.802,-0.337c-2.831,-1.191 -5.185,-2.744 -7.167,-4.728 -0.985,-0.986 -1.65,-1.855 -2.255,-2.944 -0.503,-0.906 -0.557,-0.911 -0.094,-0.008 1.407,2.74 4.057,5.12 7.755,6.968 1.454,0.726 2.381,1.112 2.601,1.083 0.111,-0.015 0.617,-0.21 1.124,-0.434zM10.371,28.791c-0.423,-1.259 -0.49,-2.665 -0.491,-10.295 -0.001,-3.957 -0.023,-6.003 -0.067,-6.003 -0.092,0 -0.09,13.538 0.002,14.272 0.078,0.624 0.315,1.594 0.527,2.153 0.201,0.532 0.221,0.445 0.029,-0.127zM11.848,12.445c-0.099,-0.015 -0.278,-0.015 -0.397,-0.001 -0.119,0.015 -0.038,0.027 0.181,0.027 0.218,0 0.316,-0.012 0.216,-0.027zM30.898,12.445c-0.099,-0.015 -0.278,-0.015 -0.397,-0.001 -0.119,0.015 -0.038,0.027 0.181,0.027 0.218,0 0.316,-0.012 0.216,-0.027zM13.167,12.321c0.392,-0.058 0.728,-0.121 0.746,-0.14 0.018,-0.019 -0.131,-0.012 -0.331,0.016 -0.2,0.028 -0.632,0.088 -0.959,0.133 -0.327,0.045 -0.499,0.085 -0.382,0.089 0.117,0.004 0.534,-0.04 0.926,-0.099zM29.855,12.355c-0.236,-0.03 -0.723,-0.103 -1.081,-0.162 -0.358,-0.059 -0.664,-0.095 -0.679,-0.079 -0.026,0.026 0.258,0.08 1.264,0.243 0.182,0.029 0.465,0.053 0.628,0.053 0.247,-0.001 0.225,-0.01 -0.132,-0.055zM14.195,12.118c-0.045,-0.018 -0.12,-0.018 -0.165,0 -0.045,0.018 -0.008,0.033 0.083,0.033 0.091,0 0.128,-0.015 0.083,-0.033zM14.525,12.052c-0.045,-0.018 -0.12,-0.018 -0.165,0 -0.045,0.018 -0.008,0.033 0.083,0.033 0.091,0 0.128,-0.015 0.083,-0.033zM27.953,12.052c-0.045,-0.018 -0.12,-0.018 -0.165,0 -0.045,0.018 -0.008,0.033 0.083,0.033 0.091,0 0.128,-0.015 0.083,-0.033zM14.822,11.99c-0.022,-0.022 -0.079,-0.024 -0.127,-0.005 -0.053,0.021 -0.037,0.037 0.04,0.04 0.07,0.003 0.109,-0.013 0.087,-0.035zM27.654,11.99c-0.022,-0.022 -0.079,-0.024 -0.127,-0.005 -0.053,0.021 -0.037,0.037 0.04,0.04 0.07,0.003 0.109,-0.013 0.087,-0.035zM15.152,11.923c-0.022,-0.022 -0.079,-0.024 -0.127,-0.005 -0.053,0.021 -0.037,0.037 0.04,0.04 0.07,0.003 0.109,-0.013 0.087,-0.035zM27.358,11.92c-0.045,-0.018 -0.12,-0.018 -0.165,0 -0.045,0.018 -0.008,0.033 0.083,0.033 0.091,0 0.128,-0.015 0.083,-0.033zM15.417,11.857c-0.022,-0.022 -0.079,-0.024 -0.127,-0.005 -0.053,0.021 -0.037,0.037 0.04,0.04 0.07,0.003 0.109,-0.013 0.087,-0.035zM27.059,11.857c-0.022,-0.022 -0.079,-0.024 -0.127,-0.005 -0.053,0.021 -0.037,0.037 0.04,0.04 0.07,0.003 0.109,-0.013 0.087,-0.035zM22.711,10.305c-0.093,-0.059 -1.022,-0.619 -1.389,-0.837 -0.158,-0.094 -0.193,-0.081 -0.761,0.272 -0.86,0.535 -0.861,0.536 -0.097,0.165l0.697,-0.338 0.792,0.401c0.737,0.373 0.997,0.489 0.759,0.337z\"\n      android:strokeWidth=\"0.25\"\n      android:strokeColor=\"#ffffff\" />\n  <path\n      android:fillAlpha=\"1\"\n      android:fillColor=\"#ffffff\"\n      android:fillType=\"evenOdd\"\n      android:pathData=\"m20.627,38.938c-0.866,-0.37 -2.844,-1.351 -3.573,-1.77 -3.289,-1.895 -5.742,-4.338 -7.01,-6.981 -0.36,-0.751 -0.584,-1.376 -0.839,-2.348l-0.148,-0.562 -0.021,-7.756 -0.021,-7.756 0.515,-0.001c1.011,-0.001 3.166,-0.245 4.451,-0.503 2.485,-0.499 5.052,-1.466 6.652,-2.505l0.534,-0.347 0.484,0.318c2.503,1.648 6.757,2.849 10.672,3.013l1.044,0.044 -0.022,7.613 -0.022,7.613 -0.185,0.761c-1.016,4.17 -4.064,7.51 -9.268,10.154 -0.963,0.489 -2.636,1.233 -2.737,1.217 -0.023,-0.004 -0.249,-0.096 -0.504,-0.205zM21.028,38.056c-0.071,-0.053 -0.15,-0.097 -0.177,-0.097 -0.138,0 -2.187,-0.966 -2.922,-1.378 -2.951,-1.654 -5.471,-3.924 -6.734,-6.066 -0.158,-0.268 -0.297,-0.477 -0.309,-0.465 -0.012,0.012 0.13,0.28 0.316,0.595 1.005,1.701 2.583,3.334 4.534,4.688 1.217,0.845 3.241,1.939 4.725,2.553 0.712,0.295 0.744,0.304 0.566,0.17zM23.281,37.236c5.095,-2.478 7.999,-5.552 9.096,-9.629 0.14,-0.522 0.143,-0.625 0.172,-7.342 0.017,-3.747 0.005,-6.788 -0.025,-6.758 -0.031,0.03 -0.074,3.081 -0.096,6.78 -0.028,4.779 -0.063,6.849 -0.119,7.155 -0.199,1.079 -1.033,2.965 -1.718,3.883 -1.186,1.59 -3.361,3.562 -5.135,4.654 -0.255,0.157 -1.148,0.636 -1.984,1.065 -0.837,0.429 -1.495,0.78 -1.463,0.781 0.032,0.001 0.604,-0.264 1.272,-0.588zM10.113,27.746c-0.18,-1.032 -0.233,-3.031 -0.234,-8.759 -0,-3.853 -0.022,-5.629 -0.069,-5.601 -0.089,0.055 -0.08,12.55 0.01,13.294 0.069,0.575 0.318,1.713 0.364,1.667 0.016,-0.016 -0.017,-0.286 -0.072,-0.602zM32.56,12.704c0.004,-0.086 -0.022,-0.135 -0.06,-0.112 -0.036,0.022 -0.063,0.166 -0.06,0.318 0.006,0.237 0.014,0.253 0.06,0.112 0.029,-0.091 0.056,-0.234 0.06,-0.318zM9.874,12.707c0.003,-0.082 -0.025,-0.149 -0.061,-0.149 -0.036,0 -0.064,0.127 -0.061,0.281 0.004,0.218 0.017,0.251 0.061,0.149 0.031,-0.073 0.059,-0.199 0.061,-0.281zM29.44,12.32c-0.022,-0.022 -0.079,-0.024 -0.127,-0.005 -0.053,0.021 -0.037,0.037 0.04,0.04 0.07,0.003 0.109,-0.013 0.087,-0.035zM21.697,9.713 L21.18,9.382 20.788,9.624c-0.216,0.133 -0.392,0.253 -0.392,0.266 0,0.024 0.258,-0.079 0.603,-0.24 0.153,-0.072 0.225,-0.055 0.628,0.15 0.646,0.328 0.67,0.298 0.07,-0.087z\"\n      android:strokeWidth=\"0.25\"\n      android:strokeColor=\"#ffffff\" />\n  <path\n      android:fillAlpha=\"1\"\n      android:fillColor=\"#ffffff\"\n      android:fillType=\"evenOdd\"\n      android:pathData=\"m20.297,38.796c-3.438,-1.522 -5.759,-3.041 -7.709,-5.046 -1.711,-1.759 -2.707,-3.44 -3.325,-5.614l-0.207,-0.728 -0.02,-7.812 -0.02,-7.812 0.911,-0.047c4.039,-0.208 8.215,-1.371 10.65,-2.964 0.283,-0.185 0.544,-0.336 0.58,-0.336 0.036,0 0.283,0.141 0.547,0.312 0.598,0.388 2.156,1.144 2.992,1.451 2.409,0.886 4.997,1.399 7.756,1.538l0.91,0.046l0,7.507 0,7.507l-0.171,0.761c-0.976,4.354 -4.106,7.786 -9.586,10.515 -0.824,0.41 -2.346,1.077 -2.452,1.075 -0.034,-0.001 -0.418,-0.159 -0.855,-0.353zM20.297,37.76c-0.022,-0.036 -0.076,-0.066 -0.12,-0.065 -0.047,0.001 -0.039,0.027 0.02,0.065 0.127,0.082 0.15,0.082 0.099,0zM19.734,37.496c-0.055,-0.035 -0.129,-0.064 -0.165,-0.064 -0.036,0 -0.021,0.029 0.033,0.064 0.055,0.035 0.129,0.064 0.165,0.064 0.036,0 0.021,-0.029 -0.033,-0.064zM23.475,37.128c0.121,-0.097 0.119,-0.098 -0.037,-0.024 -0.202,0.096 -0.24,0.127 -0.156,0.127 0.035,0 0.122,-0.046 0.193,-0.103zM24.695,36.469c0,-0.018 -0.238,0.087 -0.529,0.233 -0.291,0.146 -0.529,0.281 -0.529,0.299 0,0.018 0.238,-0.087 0.529,-0.233 0.291,-0.146 0.529,-0.281 0.529,-0.299zM17.552,36.413c0,-0.013 -0.257,-0.181 -0.57,-0.374 -1.788,-1.096 -3.867,-2.895 -4.948,-4.282 -0.346,-0.443 -0.507,-0.577 -0.199,-0.165 1.259,1.684 2.821,3.074 4.918,4.376 0.746,0.463 0.799,0.493 0.799,0.445zM25.291,36.137c0,-0.018 -0.119,0.028 -0.265,0.102 -0.146,0.074 -0.265,0.15 -0.265,0.168 0,0.018 0.119,-0.028 0.265,-0.102 0.146,-0.074 0.265,-0.15 0.265,-0.168zM26.926,35.069c0.997,-0.727 1.926,-1.537 2.526,-2.201 0.482,-0.534 1.267,-1.545 1.4,-1.803 0.105,-0.203 0.026,-0.111 -0.438,0.51 -0.761,1.02 -2.735,2.881 -4.015,3.784 -0.282,0.199 -0.513,0.375 -0.513,0.392 0,0.058 0.251,-0.106 1.04,-0.682zM31.433,30.038c0.328,-0.654 0.805,-1.858 0.799,-2.015 -0.002,-0.047 -0.106,0.197 -0.23,0.543 -0.125,0.346 -0.39,0.963 -0.59,1.373 -0.199,0.409 -0.349,0.744 -0.332,0.744 0.016,0 0.175,-0.29 0.353,-0.645zM10.077,27.602c-0.141,-0.874 -0.196,-3.021 -0.203,-7.933 -0.005,-2.989 -0.032,-5.09 -0.065,-4.994 -0.031,0.091 -0.044,2.829 -0.029,6.085 0.027,5.632 0.035,5.952 0.163,6.582 0.074,0.364 0.135,0.674 0.135,0.689 0,0.015 0.014,0.013 0.032,-0.004 0.017,-0.017 0.003,-0.209 -0.032,-0.425zM32.504,23.539c0.063,-4.859 0.059,-8.775 -0.007,-7.408 -0.025,0.528 -0.051,2.864 -0.057,5.192 -0.01,3.792 -0.066,5.969 -0.168,6.449 -0.019,0.091 0.014,0.031 0.074,-0.132 0.094,-0.257 0.115,-0.821 0.158,-4.101zM21.653,9.716c-0.38,-0.293 -0.45,-0.306 -0.767,-0.138 -0.162,0.086 -0.281,0.169 -0.266,0.184 0.015,0.015 0.119,-0.019 0.229,-0.076 0.258,-0.133 0.386,-0.128 0.671,0.028 0.296,0.162 0.341,0.163 0.132,0.002z\"\n      android:strokeWidth=\"0.25\"\n      android:strokeColor=\"#ffffff\" />\n  <path\n      android:fillAlpha=\"1\"\n      android:fillColor=\"#ffffff\"\n      android:fillType=\"evenOdd\"\n      android:pathData=\"m20.826,39.048c-0.109,-0.051 -0.684,-0.315 -1.278,-0.587C16.722,37.166 14.488,35.68 12.76,33.946 10.894,32.072 9.888,30.378 9.212,27.976l-0.194,-0.689L9.019,19.532 9.019,11.778l0.777,-0.045c3.562,-0.205 6.733,-0.948 9.31,-2.18 0.473,-0.226 1.098,-0.56 1.389,-0.742 0.291,-0.182 0.562,-0.349 0.601,-0.373 0.04,-0.023 0.292,0.094 0.56,0.261 2.778,1.729 6.615,2.794 10.927,3.034l0.777,0.043 -0.001,7.584 -0.001,7.584 -0.224,0.895c-0.123,0.492 -0.348,1.207 -0.499,1.587 -1.52,3.838 -5.063,6.972 -10.605,9.382 -0.841,0.366 -0.905,0.378 -1.204,0.238zM16.003,35.425c-1.196,-0.903 -1.802,-1.411 -2.615,-2.196 -0.606,-0.585 -0.83,-0.774 -0.608,-0.513 0.396,0.465 1.072,1.106 1.766,1.674 0.504,0.412 1.562,1.187 1.616,1.183 0.018,-0.001 -0.054,-0.068 -0.159,-0.148zM26.622,35.263c0.082,-0.105 0.08,-0.107 -0.025,-0.025 -0.11,0.086 -0.145,0.141 -0.091,0.141 0.014,0 0.066,-0.052 0.116,-0.116zM27.705,34.46c0.641,-0.508 1.688,-1.531 2.175,-2.124 0.544,-0.662 0.668,-0.845 0.269,-0.397 -0.879,0.988 -1.536,1.616 -3.141,3 -0.294,0.253 -0.294,0.254 -0.031,0.072 0.146,-0.101 0.473,-0.348 0.728,-0.55zM10.004,27.137C9.957,26.773 9.91,24.728 9.881,21.786l-0.046,-4.762 0.009,4.961c0.007,3.921 0.028,5.048 0.1,5.374 0.05,0.227 0.101,0.402 0.114,0.39 0.013,-0.013 -0.011,-0.288 -0.053,-0.611zM32.487,26.251c-0,-0.276 -0.013,-0.324 -0.053,-0.198 -0.029,0.091 -0.052,0.418 -0.05,0.728 0.003,0.537 0.005,0.546 0.053,0.198 0.028,-0.2 0.05,-0.528 0.05,-0.728zM32.484,20.678c-0.01,-0.919 -0.019,-0.167 -0.019,1.67 0,1.837 0.008,2.589 0.019,1.67 0.01,-0.919 0.01,-2.422 0,-3.34zM21.359,9.638c0.252,0.115 0.282,0.042 0.035,-0.086 -0.186,-0.096 -0.223,-0.096 -0.403,-0.003 -0.253,0.131 -0.256,0.176 -0.005,0.081 0.146,-0.055 0.238,-0.053 0.372,0.008z\"\n      android:strokeWidth=\"0.25\"\n      android:strokeColor=\"#ffffff\" />\n  <path\n      android:fillAlpha=\"1\"\n      android:fillColor=\"#ffffff\"\n      android:fillType=\"evenOdd\"\n      android:pathData=\"m20.032,38.7c-2.848,-1.286 -4.92,-2.582 -6.714,-4.199 -2.041,-1.841 -3.363,-3.901 -4.063,-6.333l-0.228,-0.792 -0.014,-7.774c-0.008,-4.276 0,-7.788 0.018,-7.806 0.018,-0.018 0.491,-0.062 1.052,-0.099 2.893,-0.189 5.504,-0.733 7.768,-1.617 0.88,-0.344 2.215,-1.005 2.829,-1.401 0.247,-0.159 0.463,-0.289 0.48,-0.289 0.017,0 0.265,0.147 0.551,0.326 2.16,1.354 5.204,2.35 8.504,2.783 0.787,0.103 2.579,0.258 3.063,0.264 0.066,0.001 0.083,1.568 0.083,7.612L33.36,26.986l-0.204,0.806c-0.45,1.783 -1.139,3.218 -2.252,4.691 -0.51,0.675 -2.013,2.189 -2.77,2.791 -1.243,0.987 -3.274,2.235 -4.862,2.986 -1.052,0.497 -1.992,0.89 -2.13,0.889 -0.066,-0.001 -0.566,-0.202 -1.111,-0.449zM14.94,34.633c-0.145,-0.117 -0.497,-0.421 -0.783,-0.676 -0.81,-0.723 -1.2,-1.032 -0.707,-0.561 0.486,0.464 1.656,1.455 1.715,1.452 0.021,-0.001 -0.08,-0.098 -0.225,-0.215zM28.603,33.66c0.312,-0.285 0.727,-0.707 0.922,-0.938 0.261,-0.308 0.136,-0.207 -0.468,0.377 -0.453,0.438 -1.031,0.985 -1.286,1.215 -0.411,0.371 -0.422,0.387 -0.099,0.141 0.2,-0.153 0.619,-0.511 0.931,-0.796zM9.943,26.28c-0.036,-0.616 -0.076,-1.373 -0.089,-1.683 -0.013,-0.309 -0.012,0.048 0.001,0.794 0.022,1.248 0.077,2.086 0.131,2.032 0.012,-0.012 -0.007,-0.526 -0.043,-1.143zM21.421,9.582c-0.022,-0.036 -0.136,-0.063 -0.252,-0.058l-0.211,0.008 0.198,0.05c0.288,0.073 0.31,0.073 0.265,0z\"\n      android:strokeWidth=\"0.25\"\n      android:strokeColor=\"#ffffff\" />\n  <path\n      android:fillAlpha=\"1\"\n      android:fillColor=\"#ffffff\"\n      android:fillType=\"evenOdd\"\n      android:pathData=\"m20.29,38.828c-0.987,-0.43 -3.159,-1.551 -3.905,-2.014 -2.6,-1.615 -4.65,-3.596 -5.886,-5.688 -0.478,-0.81 -0.909,-1.821 -1.214,-2.848l-0.258,-0.87 -0.043,-7.816 -0.043,-7.816 0.353,-0.036c0.194,-0.02 0.665,-0.054 1.047,-0.076 3.64,-0.21 7.438,-1.281 10.064,-2.84l0.751,-0.446 0.198,0.116c1.463,0.859 1.986,1.12 3.175,1.583 2.17,0.845 4.933,1.43 7.508,1.588 0.346,0.021 0.785,0.055 0.976,0.074l0.347,0.036l0,7.64 0,7.64l-0.164,0.646c-0.655,2.584 -1.811,4.529 -3.877,6.523 -1.188,1.147 -2.485,2.098 -4.19,3.073 -1.364,0.78 -3.679,1.856 -3.991,1.854 -0.063,-0 -0.444,-0.145 -0.848,-0.321zM28.73,33.494c0.308,-0.309 0.544,-0.562 0.526,-0.562 -0.018,0 -0.285,0.253 -0.592,0.562 -0.308,0.309 -0.544,0.562 -0.526,0.562 0.018,0 0.285,-0.253 0.592,-0.562z\"\n      android:strokeWidth=\"0.25\"\n      android:strokeColor=\"#ffffff\" />\n  <path\n      android:fillColor=\"#4E85DB\"\n      android:pathData=\"m21.331,14.371c-2.702,0 -4.912,2.194 -4.912,4.875v1.625h1.637v-1.625c0,-1.803 1.458,-3.25 3.275,-3.25 1.817,0 3.275,1.447 3.275,3.25v1.625h1.637v-1.625c0,-2.681 -2.21,-4.875 -4.912,-4.875zM13.963,21.683v1.625h14.736v-1.625zM13.963,24.12v1.625L20.512,25.745v-1.625zM22.15,24.12v1.625h6.549v-1.625zM13.963,26.557v1.625L20.512,28.182L20.512,26.557ZM22.15,26.557v1.625h6.549L28.699,26.557ZM13.963,28.995v1.625h14.736v-1.625z\"\n      android:strokeWidth=\"0.0191142\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_key.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24.023438dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"1025\"\n    android:viewportHeight=\"1024\">\n  <path\n      android:fillColor=\"@color/white\"\n      android:pathData=\"M704.11,0.02c-176.74,0 -319.99,143.3 -319.99,319.99 0,41.12 8.6,80.03 22.81,116.12l-388.76,388.72c-11.22,11.26 -18.17,21.95 -18.17,39.14l0,96.01c0,34.24 29.74,64 64,64l96.01,0c17.14,0 28,-6.88 39.24,-18.04l45.92,-45.96 74.98,0c35.33,0 64,-28.67 64,-64l0,-64 64,0c35.33,0 64,-28.67 64,-64l0,-75.02 75.82,-75.88c36.13,14.23 75.02,22.88 116.18,22.88 176.68,0 319.99,-143.3 319.99,-319.99s-143.32,-319.99 -319.99,-319.99zM704.11,576.01c-47.37,0 -91.26,-13.76 -129.31,-36.23l-11.02,10.98 -96.95,96.99c-12,12 -18.76,28.26 -18.76,45.24l0,75.02 -64,0c-35.33,0 -64,28.63 -64,64l0,64 -74.93,0c-16.96,0 -33.24,6.76 -45.24,18.76l-45.38,45.38 -90.44,-0.12 -0.1,-91.2 373.28,-372.61c0,0 0,0.04 0.04,0.06l47,-47c-22.47,-38.05 -36.23,-81.92 -36.23,-129.29 0,-141.37 114.64,-255.99 255.99,-255.99s255.99,114.62 255.99,255.99 -114.58,256.04 -255.95,256.04zM890.23,263.65c-35.88,-49.99 -79.54,-93.59 -129.76,-129.72 -8.15,-5.92 -18.72,-7.39 -28.22,-4.05 -44.44,15.65 -74.87,46.04 -90.44,90.5 -1.19,3.32 -1.76,6.78 -1.76,10.2 0,6.37 1.99,12.68 5.82,18.02 36,50.05 79.62,93.69 129.7,129.7 8.19,5.88 18.7,7.39 28.2,4.1 44.5,-15.54 74.93,-46.02 90.56,-90.5 1.19,-3.32 1.76,-6.78 1.76,-10.2 -0.04,-6.37 -2.05,-12.68 -5.86,-18.02zM794.22,352.33c-47.29,-34.02 -88.47,-75.2 -122.24,-121.38 12.39,-35.29 35.55,-58.47 69.81,-71.06 47.33,34.08 88.45,75.16 122.1,121.96 -12.51,35.04 -35.61,58.08 -69.69,70.49z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_key_gray.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24.023438dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"1025\"\n    android:viewportHeight=\"1024\">\n  <path\n      android:fillColor=\"@color/color_icon_grey\"\n      android:pathData=\"M704.11,0.02c-176.74,0 -319.99,143.3 -319.99,319.99 0,41.12 8.6,80.03 22.81,116.12l-388.76,388.72c-11.22,11.26 -18.17,21.95 -18.17,39.14l0,96.01c0,34.24 29.74,64 64,64l96.01,0c17.14,0 28,-6.88 39.24,-18.04l45.92,-45.96 74.98,0c35.33,0 64,-28.67 64,-64l0,-64 64,0c35.33,0 64,-28.67 64,-64l0,-75.02 75.82,-75.88c36.13,14.23 75.02,22.88 116.18,22.88 176.68,0 319.99,-143.3 319.99,-319.99s-143.32,-319.99 -319.99,-319.99zM704.11,576.01c-47.37,0 -91.26,-13.76 -129.31,-36.23l-11.02,10.98 -96.95,96.99c-12,12 -18.76,28.26 -18.76,45.24l0,75.02 -64,0c-35.33,0 -64,28.63 -64,64l0,64 -74.93,0c-16.96,0 -33.24,6.76 -45.24,18.76l-45.38,45.38 -90.44,-0.12 -0.1,-91.2 373.28,-372.61c0,0 0,0.04 0.04,0.06l47,-47c-22.47,-38.05 -36.23,-81.92 -36.23,-129.29 0,-141.37 114.64,-255.99 255.99,-255.99s255.99,114.62 255.99,255.99 -114.58,256.04 -255.95,256.04zM890.23,263.65c-35.88,-49.99 -79.54,-93.59 -129.76,-129.72 -8.15,-5.92 -18.72,-7.39 -28.22,-4.05 -44.44,15.65 -74.87,46.04 -90.44,90.5 -1.19,3.32 -1.76,6.78 -1.76,10.2 0,6.37 1.99,12.68 5.82,18.02 36,50.05 79.62,93.69 129.7,129.7 8.19,5.88 18.7,7.39 28.2,4.1 44.5,-15.54 74.93,-46.02 90.56,-90.5 1.19,-3.32 1.76,-6.78 1.76,-10.2 -0.04,-6.37 -2.05,-12.68 -5.86,-18.02zM794.22,352.33c-47.29,-34.02 -88.47,-75.2 -122.24,-121.38 12.39,-35.29 35.55,-58.47 69.81,-71.06 47.33,34.08 88.45,75.16 122.1,121.96 -12.51,35.04 -35.61,58.08 -69.69,70.49z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_keyboard.xml",
    "content": "<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:fillColor=\"@color/color_icon_grey\"\n      android:pathData=\"M20,5L4,5c-1.1,0 -1.99,0.9 -1.99,2L2,17c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,7c0,-1.1 -0.9,-2 -2,-2zM11,8h2v2h-2L11,8zM11,11h2v2h-2v-2zM8,8h2v2L8,10L8,8zM8,11h2v2L8,13v-2zM7,13L5,13v-2h2v2zM7,10L5,10L5,8h2v2zM16,17L8,17v-2h8v2zM16,13h-2v-2h2v2zM16,10h-2L14,8h2v2zM19,13h-2v-2h2v2zM19,10h-2L17,8h2v2z\"/>\n</vector>"
  },
  {
    "path": "app/src/main/res/drawable/ic_language_24px.xml",
    "content": "<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:fillColor=\"@color/color_icon_grey\"\n      android:pathData=\"M11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM18.92,8h-2.95c-0.32,-1.25 -0.78,-2.45 -1.38,-3.56 1.84,0.63 3.37,1.91 4.33,3.56zM12,4.04c0.83,1.2 1.48,2.53 1.91,3.96h-3.82c0.43,-1.43 1.08,-2.76 1.91,-3.96zM4.26,14C4.1,13.36 4,12.69 4,12s0.1,-1.36 0.26,-2h3.38c-0.08,0.66 -0.14,1.32 -0.14,2 0,0.68 0.06,1.34 0.14,2L4.26,14zM5.08,16h2.95c0.32,1.25 0.78,2.45 1.38,3.56 -1.84,-0.63 -3.37,-1.9 -4.33,-3.56zM8.03,8L5.08,8c0.96,-1.66 2.49,-2.93 4.33,-3.56C8.81,5.55 8.35,6.75 8.03,8zM12,19.96c-0.83,-1.2 -1.48,-2.53 -1.91,-3.96h3.82c-0.43,1.43 -1.08,2.76 -1.91,3.96zM14.34,14L9.66,14c-0.09,-0.66 -0.16,-1.32 -0.16,-2 0,-0.68 0.07,-1.35 0.16,-2h4.68c0.09,0.65 0.16,1.32 0.16,2 0,0.68 -0.07,1.34 -0.16,2zM14.59,19.56c0.6,-1.11 1.06,-2.31 1.38,-3.56h2.95c-0.96,1.65 -2.49,2.93 -4.33,3.56zM16.36,14c0.08,-0.66 0.14,-1.32 0.14,-2 0,-0.68 -0.06,-1.34 -0.14,-2h3.38c0.16,0.64 0.26,1.31 0.26,2s-0.1,1.36 -0.26,2h-3.38z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_launcher_background.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<vector\n    android:height=\"108dp\"\n    android:width=\"108dp\"\n    android:viewportHeight=\"108\"\n    android:viewportWidth=\"108\"\n    xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <path android:fillColor=\"#3DDC84\"\n          android:pathData=\"M0,0h108v108h-108z\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M9,0L9,108\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M19,0L19,108\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M29,0L29,108\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M39,0L39,108\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M49,0L49,108\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M59,0L59,108\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M69,0L69,108\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M79,0L79,108\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M89,0L89,108\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M99,0L99,108\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M0,9L108,9\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M0,19L108,19\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M0,29L108,29\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M0,39L108,39\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M0,49L108,49\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M0,59L108,59\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M0,69L108,69\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M0,79L108,79\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M0,89L108,89\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M0,99L108,99\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M19,29L89,29\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M19,39L89,39\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M19,49L89,49\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M19,59L89,59\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M19,69L89,69\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M19,79L89,79\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M29,19L29,89\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M39,19L39,89\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M49,19L49,89\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M59,19L59,89\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M69,19L69,89\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M79,19L79,89\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_lightbulb_on.xml",
    "content": "<!-- drawable/lightbulb_on.xml -->\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:height=\"24dp\"\n    android:viewportHeight=\"24\"\n    android:viewportWidth=\"24\"\n    android:width=\"24dp\">\n  <path\n      android:fillColor=\"@color/color_icon_grey\"\n      android:pathData=\"M12,6A6,6 0 0,1 18,12C18,14.22 16.79,16.16 15,17.2V19A1,1 0 0,1 14,20H10A1,1 0 0,1 9,19V17.2C7.21,16.16 6,14.22 6,12A6,6 0 0,1 12,6M14,21V22A1,1 0 0,1 13,23H11A1,1 0 0,1 10,22V21H14M20,11H23V13H20V11M1,11H4V13H1V11M13,1V4H11V1H13M4.92,3.5L7.05,5.64L5.63,7.05L3.5,4.93L4.92,3.5M16.95,5.63L19.07,3.5L20.5,4.93L18.37,7.05L16.95,5.63Z\" />\n</vector>"
  },
  {
    "path": "app/src/main/res/drawable/ic_linear_scale.xml",
    "content": "<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:fillColor=\"@color/color_icon_grey\"\n      android:pathData=\"M19.5,9.5c-1.03,0 -1.9,0.62 -2.29,1.5h-2.92C13.9,10.12 13.03,9.5 12,9.5s-1.9,0.62 -2.29,1.5H6.79C6.4,10.12 5.53,9.5 4.5,9.5C3.12,9.5 2,10.62 2,12s1.12,2.5 2.5,2.5c1.03,0 1.9,-0.62 2.29,-1.5h2.92c0.39,0.88 1.26,1.5 2.29,1.5s1.9,-0.62 2.29,-1.5h2.92c0.39,0.88 1.26,1.5 2.29,1.5c1.38,0 2.5,-1.12 2.5,-2.5S20.88,9.5 19.5,9.5z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_lock.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"128dp\"\n    android:height=\"128dp\"\n    android:viewportWidth=\"1024\"\n    android:viewportHeight=\"1024\">\n  <path\n      android:pathData=\"M512,85.33c-117.33,0 -213.33,96 -213.33,213.33v85.33h85.33v-85.33c0,-70.4 57.6,-128 128,-128s128,57.6 128,128v85.33h85.33v-85.33c0,-117.33 -96,-213.33 -213.33,-213.33z\"\n      android:fillColor=\"#424242\"/>\n  <path\n      android:pathData=\"M768,938.67H256c-46.93,0 -85.33,-38.4 -85.33,-85.33V469.33c0,-46.93 38.4,-85.33 85.33,-85.33h512c46.93,0 85.33,38.4 85.33,85.33v384c0,46.93 -38.4,85.33 -85.33,85.33z\"\n      android:fillColor=\"#FB8C00\"/>\n  <path\n      android:pathData=\"M512,661.33m-64,0a64,64 0,1 0,128 0,64 64,0 1,0 -128,0Z\"\n      android:fillColor=\"#C76E00\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_lock_24px.xml",
    "content": "<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:fillColor=\"@color/colorPrimary\"\n      android:pathData=\"M18,8h-1L17,6c0,-2.76 -2.24,-5 -5,-5S7,3.24 7,6v2L6,8c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L20,10c0,-1.1 -0.9,-2 -2,-2zM12,17c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2zM15.1,8L8.9,8L8.9,6c0,-1.71 1.39,-3.1 3.1,-3.1 1.71,0 3.1,1.39 3.1,3.1v2z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_lose_time.xml",
    "content": "<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:fillColor=\"@color/color_icon_grey\"\n      android:pathData=\"M22,5.7l-4.6,-3.9 -1.3,1.5 4.6,3.9L22,5.7zM7.9,3.4L6.6,1.9 2,5.7l1.3,1.5 4.6,-3.8zM12.5,8L11,8v6l4.7,2.9 0.8,-1.2 -4,-2.4L12.5,8zM12,4c-5,0 -9,4 -9,9s4,9 9,9 9,-4 9,-9 -4,-9 -9,-9zM12,20c-3.9,0 -7,-3.1 -7,-7s3.1,-7 7,-7 7,3.1 7,7 -3.1,7 -7,7z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_modify_time.xml",
    "content": "<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:fillColor=\"@color/color_icon_grey\"\n      android:pathData=\"M11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8z\" />\n  <path\n      android:fillColor=\"@color/color_icon_grey\"\n      android:pathData=\"M12.5,7H11v6l5.25,3.15 0.75,-1.23 -4.5,-2.67z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_more_read.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"1024\"\n    android:viewportHeight=\"1024\">\n  <path\n      android:fillColor=\"@color/color_icon_grey\"\n      android:pathData=\"M509.8,758.9c45.5,0.1 82.2,36.8 82.1,82.4 -0.1,45.4 -37,82.3 -82.5,82.2 -45.3,0 -82.2,-37.1 -82.1,-82.6 0.1,-45.4 36.9,-82 82.5,-82zM427.4,181.9c0,-45.5 36.8,-82.5 82.2,-82.5 45.4,-0.1 82.4,36.8 82.4,82.3 0,45.6 -36.6,82.4 -82.2,82.4s-82.4,-36.6 -82.4,-82.2zM592,511.6c0,45.4 -37.3,82.6 -82.4,82.4 -45.5,-0.3 -82.2,-37.1 -82.2,-82.6 0,-45.4 37.1,-82.5 82.4,-82.3 45.4,0.1 82.2,37 82.2,82.5z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_net.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"1024\"\n    android:viewportHeight=\"1024\">\n  <path\n      android:fillColor=\"@color/color_icon_grey\"\n      android:pathData=\"M512,81.53c-124.22,0 -235.98,53.2 -314.63,137.63 -0.37,-0.29 -0.83,-0.53 -1.22,-0.82L186.37,231.27C121.22,306.74 81.53,404.73 81.53,512c0,107.81 40.13,206.23 105.86,281.87l8.76,11.56c0.34,-0.27 0.75,-0.49 1.1,-0.73 78.65,84.53 190.46,137.78 314.76,137.78 237.36,0 430.47,-193.12 430.47,-430.47 0,-237.37 -193.12,-430.47 -430.47,-430.47zM536.38,355.23c58.95,-2.15 115.44,-11.37 166.98,-26.53 14.99,47.69 24.12,101.64 26.18,158.94h-193.17L536.38,355.23zM536.38,306.49L536.38,132.83c61.15,11.94 114.59,68.39 149.82,150.16 -46.13,13.26 -96.77,21.43 -149.82,23.5zM487.62,132.83v173.83c-53.18,-1.63 -103.96,-9.44 -150.41,-22.28 35.23,-82.55 88.91,-139.54 150.41,-151.55zM487.62,355.39v132.22L294.45,487.62c2.07,-56.69 10.97,-110.18 25.7,-157.5 51.8,14.73 108.44,23.58 167.48,25.28zM245.71,487.62h-114.18c4.85,-76.39 32.16,-146.55 75.54,-204.26a470.96,470.96 0,0 0,66.8 31.6c-16.26,52 -26.09,110.35 -28.16,172.67zM245.71,536.39c2.07,62.15 11.85,120.34 28.04,172.25a471.37,471.37 0,0 0,-66.85 31.77c-43.28,-57.64 -70.51,-127.75 -75.37,-204.02h114.18zM294.45,536.39h193.17v131.97c-59,1.7 -115.88,10.02 -167.67,24.84 -14.6,-47.18 -23.45,-100.4 -25.5,-156.82zM487.62,717.12v174.05c-61.63,-12.04 -115.42,-69.29 -150.68,-152.14 46.42,-12.94 97.5,-20.29 150.68,-21.91zM536.38,891.17v-173.88c53.08,2.04 103.94,9.79 150.07,23.16 -35.24,82.09 -88.78,138.75 -150.07,150.72zM536.38,668.53v-132.15h193.17c-2.07,57.05 -11.12,110.84 -26.01,158.41 -51.52,-15.27 -108.19,-24.09 -167.16,-26.27zM778.29,536.38h114.2c-4.93,77.24 -32.77,148.14 -76.99,206.19 -20.29,-11.97 -42.42,-22.53 -65.87,-31.82 16.57,-52.41 26.57,-111.39 28.67,-174.37zM778.29,487.62c-2.1,-63.03 -12.09,-122.02 -28.7,-174.49a462.39,462.39 0,0 0,65.71 -31.94c44.32,58.1 72.26,129.1 77.16,206.43h-114.18zM782.89,243.45c-15.68,8.75 -32.2,16.87 -49.96,23.91 -16.51,-39.15 -37.11,-73.16 -60.66,-101.3 41.35,19.24 78.87,45.37 110.62,77.39zM351.74,166.03c-23.94,28.6 -44.86,63.22 -61.52,103.18 -18.11,-6.95 -34.96,-15.04 -50.95,-23.75C271.41,212.58 309.54,185.66 351.74,166.04zM239.1,778.36a423.65,423.65 0,0 1,51.03 -23.75c16.68,40.04 37.65,74.71 61.61,103.37 -42.28,-19.7 -80.44,-46.64 -112.64,-79.63zM672.28,857.96c23.55,-28.16 44.15,-62.2 60.69,-101.35a421.94,421.94 0,0 1,50.1 23.79c-31.8,32.09 -69.37,58.27 -110.79,77.56z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_new_file.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"1024\"\n    android:viewportHeight=\"1024\">\n  <path\n      android:fillColor=\"#1E59E4\"\n      android:pathData=\"M128,232.32H87.04v696.32h437.12a20.48,20.48 0,0 0,0 -40.32H128zM753.92,617.6a19.84,19.84 0,0 0,19.84 19.84,20.48 20.48,0 0,0 20.48,-20.48V0H319.36v39.68h434.56z\" />\n  <path\n      android:fillColor=\"#E1F0FF\"\n      android:pathData=\"M319.36,232.32v-192,192zM609.28,860.15a30.72,30.72 0,0 1,30.72 -28.16h102.4v-102.4a30.08,30.08 0,0 1,11.52 -23.68V617.6H524.16v270.72h104.32a30.08,30.08 0,0 1,-19.2 -28.16z\" />\n  <path\n      android:fillColor=\"#E1F0FF\"\n      android:pathData=\"M524.16,617.6h229.76v-576H319.36v192h-192v654.72h396.8z\" />\n  <path\n      android:fillColor=\"#1E59E4\"\n      android:pathData=\"M128,228.48h188.16V40.32h3.2V0L272.64,46.72 133.12,185.6 87.04,232.32h40.96v-3.84z\" />\n  <path\n      android:fillColor=\"#1E59E4\"\n      android:pathData=\"M316.16,228.48H128v3.84h191.36v-192h-3.2v188.16z\" />\n  <path\n      android:fillColor=\"#FF5A07\"\n      android:pathData=\"M906.24,831.99h-102.4v-102.4a31.36,31.36 0,0 0,-30.72 -30.72,32 32,0 0,0 -19.2,7.04v184.96h-128a21.76,21.76 0,0 0,11.52 0h102.4v102.4a30.08,30.08 0,0 0,30.72 30.72,30.72 30.72,0 0,0 30.72,-30.72v-102.4h102.4a30.72,30.72 0,0 0,30.72 -30.72,31.36 31.36,0 0,0 -28.16,-28.16z\" />\n  <path\n      android:fillColor=\"#FF5A07\"\n      android:pathData=\"M753.92,704a30.08,30.08 0,0 0,-11.52 23.68V831.99h-102.4a30.72,30.72 0,0 0,-30.72 30.72,30.08 30.08,0 0,0 19.2,28.16h128z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_notice.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"19.458433dp\"\n    android:viewportWidth=\"1263\"\n    android:viewportHeight=\"1024\">\n  <path\n      android:fillColor=\"@color/color_icon_grey\"\n      android:pathData=\"M970.09,0L290.48,0c-48.16,0 -87.2,39.04 -87.2,87.2v849.59c0,48.16 39.04,87.2 87.2,87.2L970.09,1024c48.16,0 87.2,-39.04 87.2,-87.2L1057.29,87.2c0,-48.16 -39.04,-87.2 -87.2,-87.2zM373.95,256h255.36v81.92L373.95,337.92L373.95,256zM886.24,848.59L373.95,848.59v-81.92h512.3v81.92zM886.24,593.26L373.95,593.26L373.95,511.34h512.3v81.92zM886.24,593.26\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_onedrive.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"128dp\"\n    android:height=\"128dp\"\n    android:viewportWidth=\"1024\"\n    android:viewportHeight=\"1024\">\n  <path\n      android:pathData=\"M209.92,749.31c-21.28,13.02 -45.18,19.49 -71.68,19.49 -39.26,-1.25 -71.68,-14.75 -97.57,-40.58C14.78,702.59 1.34,669.89 0,630.85c0.67,-36.77 12.42,-68 35.42,-93.54 23.04,-25.63 52.26,-40.45 87.81,-44.8a145.6,145.6 0,0 1,-1.79 -24.16c1.28,-48.64 17.92,-88.93 49.92,-120.29 32.16,-31.36 72.54,-48 121.25,-49.28 30.69,0 58.21,7.04 82.53,21.76 20.48,-32.67 46.75,-59.52 79.39,-79.33 33.25,-19.84 70.4,-30.08 111.33,-30.75 55.07,1.28 101.76,18.59 141.41,51.84 39.68,33.28 64.67,76.16 74.91,129.28h-12.16c-19.84,0 -37.09,2.56 -52.48,8.29a211.33,211.33 0,0 0,-71.01 -49.89c-26.24,-11.55 -55.04,-16.64 -85.79,-16.64 -28.16,0 -55.04,4.45 -80.64,14.08 -25.6,9.6 -48.64,23.01 -69.12,40.93 -17.92,15.36 -32.64,32.67 -44.8,52.48s-20.48,40.96 -24.96,63.36c-15.36,3.2 -30.08,7.65 -43.55,13.41a150.27,150.27 0,0 0,-55.01 42.88c-14.08,16 -24.99,34.59 -32,55.68a206.66,206.66 0,0 0,-10.91 65.92c0,25.6 3.87,49.31 12.83,71.07l-2.66,-3.87zM928.77,589.44c67.23,16.67 98.91,56.32 94.94,118.66 -3.94,62.43 -40.22,97.57 -109.02,105.44H371.2c-89.76,-11.81 -133.89,-58.24 -132.35,-139.23 1.44,-81.28 47.1,-126.05 136.96,-133.76 11.74,-87.04 56.16,-140.8 133.12,-161.28 77.06,-21.09 142.59,2.59 196.77,71.71 18.59,-15.36 42.08,-21.79 70.4,-19.87 28.51,1.92 52.64,7.71 72.42,18.59 25.6,13.41 46.08,32.64 59.55,56.99a168,168 0,0 1,20.45 81.89l0.26,0.86z\"\n      android:fillColor=\"#094AB2\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_other.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"128dp\"\n    android:height=\"128dp\"\n    android:viewportWidth=\"1024\"\n    android:viewportHeight=\"1024\">\n  <path\n      android:fillColor=\"#E2A09F9F\"\n      android:pathData=\"M128,512m-128,0a128,128 0,1 0,256 0,128 128,0 1,0 -256,0Z\"/>\n  <path\n      android:fillColor=\"#E2A09F9F\"\n      android:pathData=\"M388.34,545.03a128,128 0,1 0,247.32 -66.09,128 128,0 1,0 -247.32,66.09Z\"/>\n  <path\n      android:fillColor=\"#E2A09F9F\"\n      android:pathData=\"M772.33,545.01a128,128 0,1 0,247.32 -66.09,128 128,0 1,0 -247.32,66.09Z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_out_db.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"30dp\"\n    android:height=\"30dp\"\n    android:viewportWidth=\"1024\"\n    android:viewportHeight=\"1024\">\n  <path\n      android:fillColor=\"@color/text_black_color\"\n      android:pathData=\"M489.16,597.86V257.58c0,-12.6 10.24,-22.84 22.84,-22.84 12.6,0 22.84,10.24 22.84,22.84V598.65c0,12.6 -10.24,22.84 -22.84,22.84 -12.6,-0.79 -22.84,-11.03 -22.84,-23.63\" />\n  <path\n      android:fillColor=\"@color/text_black_color\"\n      android:pathData=\"M359.98,377.3L496.25,241.03c8.66,-8.66 22.84,-8.66 32.3,0 8.66,8.66 8.66,23.63 0,32.3L392.27,409.6c-8.66,8.66 -22.84,8.66 -32.3,0 -8.66,-8.66 -8.66,-23.63 0,-32.3\" />\n  <path\n      android:fillColor=\"@color/text_black_color\"\n      android:pathData=\"M527.75,241.03l136.27,136.27c8.66,8.66 8.66,22.84 0,32.3 -8.66,8.66 -23.63,8.66 -32.3,0L496.25,273.33c-8.66,-8.66 -8.66,-22.84 0,-32.3 8.66,-8.66 22.84,-8.66 31.51,0M716.8,779.82H307.2c-37.81,0 -67.74,-30.72 -67.74,-67.74V575.8c0,-12.6 10.24,-22.84 22.84,-22.84 12.6,0 22.84,10.24 22.84,22.84v136.27c0,12.6 10.24,22.84 22.84,22.84H716.8c12.6,0 22.84,-10.24 22.84,-22.84V575.8c0,-12.6 10.24,-22.84 22.84,-22.84 12.6,0 22.84,10.24 22.84,22.84v136.27c-0.79,37.02 -31.51,67.74 -68.53,67.74\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_password.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"1024\"\n    android:viewportHeight=\"1024\">\n  <path\n      android:fillColor=\"@color/color_icon_grey\"\n      android:pathData=\"M537.6,424.96C506.88,327.68 409.6,256 302.08,256 163.84,256 51.2,368.64 51.2,506.88s112.64,250.88 250.88,250.88c107.52,0 204.8,-71.68 235.52,-168.96h184.32v168.96h168.96v-168.96L972.8,588.8L972.8,424.96h-435.2zM302.08,588.8c-46.08,0 -81.92,-35.84 -81.92,-81.92S256,424.96 302.08,424.96s81.92,35.84 81.92,81.92 -35.84,81.92 -81.92,81.92z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_paypal.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"23.405714dp\"\n    android:viewportWidth=\"1050\"\n    android:viewportHeight=\"1024\">\n  <path\n      android:fillColor=\"@color/color_icon_grey\"\n      android:pathData=\"M880.64,130.78c25.7,12.58 48.1,30.5 64.51,52.48 34.53,46.3 44.22,109.38 28.83,187.46 -15.55,79.04 -50.4,145.73 -101.15,194.05 -9.28,9.7 -19.23,18.75 -29.79,27.07 -54.37,42.82 -123.01,65.44 -198.5,65.44l-12.13,0L404.45,657.28 339.87,960l-132.42,0 10.08,-46.3 46.14,0 64.58,-302.72 188.26,0c180.26,0 330.94,-111.07 371.26,-299.94C933.47,97.22 780.16,0 648.35,0L199.07,0 0,913.7l152.03,0L128,1024l263.65,0 64.58,-302.72 188.26,0c180.26,0 330.94,-111.07 371.26,-299.94C1049.47,263.65 974.94,169.41 880.64,130.78zM422.34,186.08l129.12,0c64.58,0 107.62,55.55 88.8,124.96 -16.13,69.44 -83.39,124.96 -150.62,124.96l-123.74,0L422.34,186.08z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_primary_close.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"30dp\"\n    android:height=\"30dp\"\n    android:viewportWidth=\"1024\"\n    android:viewportHeight=\"1024\">\n  <path\n      android:fillColor=\"@color/colorPrimary\"\n      android:pathData=\"M507.17,473.23L716.48,263.94a16,16 0,0 1,22.62 0l11.31,11.31a16,16 0,0 1,0 22.62L541.12,507.17 750.4,716.48a16,16 0,0 1,0 22.62l-11.31,11.31a16,16 0,0 1,-22.62 0L507.17,541.12 297.87,750.4a16,16 0,0 1,-22.62 0l-11.31,-11.31a16,16 0,0 1,0 -22.62l209.3,-209.31 -209.3,-209.3a16,16 0,0 1,0 -22.62l11.31,-11.31a16,16 0,0 1,22.62 0l209.3,209.3z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_qr_code_scanner.xml",
    "content": "<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    >\n  <path\n      android:fillColor=\"@color/color_icon_grey\"\n      android:pathData=\"M9.5,6.5v3h-3v-3H9.5M11,5H5v6h6V5L11,5zM9.5,14.5v3h-3v-3H9.5M11,13H5v6h6V13L11,13zM17.5,6.5v3h-3v-3H17.5M19,5h-6v6h6V5L19,5zM13,13h1.5v1.5H13V13zM14.5,14.5H16V16h-1.5V14.5zM16,13h1.5v1.5H16V13zM13,16h1.5v1.5H13V16zM14.5,17.5H16V19h-1.5V17.5zM16,16h1.5v1.5H16V16zM17.5,14.5H19V16h-1.5V14.5zM17.5,17.5H19V19h-1.5V17.5zM22,7h-2V4h-3V2h5V7zM22,22v-5h-2v3h-3v2H22zM2,22h5v-2H4v-3H2V22zM2,2v5h2V4h3V2H2z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_refresh_black_24dp.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:height=\"24dp\"\n    android:viewportHeight=\"24\"\n    android:viewportWidth=\"24\"\n    android:width=\"24dp\">\n  <path\n      android:fillColor=\"@color/color_icon_grey\"\n      android:pathData=\"M17.65,6.35C16.2,4.9 14.21,4 12,4c-4.42,0 -7.99,3.58 -7.99,8s3.57,8 7.99,8c3.73,0 6.84,-2.55 7.73,-6h-2.08c-0.82,2.33 -3.04,4 -5.65,4 -3.31,0 -6,-2.69 -6,-6s2.69,-6 6,-6c1.66,0 3.14,0.69 4.22,1.78L13,11h7V4l-2.35,2.35z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_save_24px.xml",
    "content": "<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:fillColor=\"@color/color_icon_grey\"\n      android:pathData=\"M17,3L5,3c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2L21,7l-4,-4zM12,19c-1.66,0 -3,-1.34 -3,-3s1.34,-3 3,-3 3,1.34 3,3 -1.34,3 -3,3zM15,9L5,9L5,5h10v4z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_screen_lock_portrait_black_24dp.xml",
    "content": "<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:fillColor=\"@color/color_icon_grey\"\n        android:pathData=\"M10,16h4c0.55,0 1,-0.45 1,-1v-3c0,-0.55 -0.45,-1 -1,-1v-1c0,-1.11 -0.9,-2 -2,-2 -1.11,0 -2,0.9 -2,2v1c-0.55,0 -1,0.45 -1,1v3c0,0.55 0.45,1 1,1zM10.8,10c0,-0.66 0.54,-1.2 1.2,-1.2 0.66,0 1.2,0.54 1.2,1.2v1h-2.4v-1zM17,1L7,1c-1.1,0 -2,0.9 -2,2v18c0,1.1 0.9,2 2,2h10c1.1,0 2,-0.9 2,-2L19,3c0,-1.1 -0.9,-2 -2,-2zM17,19L7,19L7,5h10v14z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_search.xml",
    "content": "<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:fillColor=\"@color/colorPrimary\"\n      android:pathData=\"M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zM9.5,14C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_security_24px.xml",
    "content": "<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:fillColor=\"@color/color_icon_grey\"\n      android:pathData=\"M12,1L3,5v6c0,5.55 3.84,10.74 9,12 5.16,-1.26 9,-6.45 9,-12L21,5l-9,-4zM12,11.99h7c-0.53,4.12 -3.28,7.79 -7,8.94L12,12L5,12L5,6.3l7,-3.11v8.8z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_security_24px_white.xml",
    "content": "<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:fillColor=\"@color/white\"\n      android:pathData=\"M12,1L3,5v6c0,5.55 3.84,10.74 9,12 5.16,-1.26 9,-6.45 9,-12L21,5l-9,-4zM12,11.99h7c-0.53,4.12 -3.28,7.79 -7,8.94L12,12L5,12L5,6.3l7,-3.11v8.8z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_server.xml",
    "content": "<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:fillColor=\"@color/color_icon_grey\"\n      android:pathData=\"M4,1H20A1,1 0,0 1,21 2V6A1,1 0,0 1,20 7H4A1,1 0,0 1,3 6V2A1,1 0,0 1,4 1M4,9H20A1,1 0,0 1,21 10V14A1,1 0,0 1,20 15H4A1,1 0,0 1,3 14V10A1,1 0,0 1,4 9M4,17H20A1,1 0,0 1,21 18V22A1,1 0,0 1,20 23H4A1,1 0,0 1,3 22V18A1,1 0,0 1,4 17M9,5H10V3H9V5M9,13H10V11H9V13M9,21H10V19H9V21M5,3V5H7V3H5M5,11V13H7V11H5M5,19V21H7V19H5Z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_setting.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"1024\"\n    android:viewportHeight=\"1024\">\n  <path\n      android:fillColor=\"@color/color_icon_grey\"\n      android:pathData=\"M881,512c0,-52.4 32.9,-96.8 79,-114.5 -11,-43.2 -28,-83.9 -50.2,-121.3C864.6,296.3 810,288.1 773,251c-37,-37 -45.2,-91.7 -25.1,-136.8C710.4,92 669.7,75 626.5,64c-17.8,46.1 -62.2,79 -114.5,79 -52.4,0 -96.8,-32.9 -114.5,-79 -43.2,11 -83.9,28 -121.3,50.2 20.1,45.2 11.9,99.8 -25.1,136.8 -37,37 -91.7,45.2 -136.8,25.2C92,313.6 75,354.3 64,397.5c46.1,17.8 79,62.2 79,114.5 0,52.4 -32.9,96.8 -79,114.5 11,43.2 28,83.9 50.2,121.3C159.4,727.7 214,735.9 251,773c37,37 45.2,91.7 25.1,136.8C313.6,932 354.3,949 397.5,960c17.8,-46.1 62.2,-79 114.5,-79 52.4,0 96.8,32.9 114.5,79 43.2,-11 83.9,-28 121.3,-50.2 -20.1,-45.2 -11.9,-99.8 25.1,-136.8 37,-37 91.7,-45.2 136.8,-25.2C932,710.4 949,669.7 960,626.5c-46.1,-17.7 -79,-62.1 -79,-114.5zM512,635c-67.9,0 -123,-55.1 -123,-123s55.1,-123 123,-123 123,55.1 123,123 -55.1,123 -123,123z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_setting_lock.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"1024\"\n    android:viewportHeight=\"1024\">\n  <path\n      android:fillColor=\"@color/text_black_color\"\n      android:pathData=\"M785.07,409.6L716.8,409.6L716.8,273.07C716.8,178.8 640.4,102.4 546.13,102.4h-68.26C383.6,102.4 307.2,178.84 307.2,273.07L307.2,409.6h-68.27c-37.53,0 -68.26,30.74 -68.26,68.27v375.47c0,37.53 30.73,68.26 68.26,68.26h546.14c37.53,0 68.26,-30.73 68.26,-68.26L853.33,477.87c0,-37.53 -30.73,-68.27 -68.26,-68.27zM375.47,273.07c0,-56.54 45.86,-102.4 102.4,-102.4h68.26c56.54,0 102.4,45.86 102.4,102.4L648.53,409.6L375.47,409.6L375.47,273.07zM785.07,853.34L238.93,853.34L238.93,477.87h546.14v375.47z\" />\n  <path\n      android:fillColor=\"@color/text_black_color\"\n      android:pathData=\"M580.27,614.4c0,-37.66 -30.57,-68.26 -68.27,-68.26S443.73,576.74 443.73,614.4c0,25.2 13.8,47 34.14,58.8v111.87h68.26V673.2c20.34,-11.8 34.14,-33.6 34.14,-58.8z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_share_24px.xml",
    "content": "<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:fillColor=\"@color/color_icon_grey\"\n      android:pathData=\"M18,16.08c-0.76,0 -1.44,0.3 -1.96,0.77L8.91,12.7c0.05,-0.23 0.09,-0.46 0.09,-0.7s-0.04,-0.47 -0.09,-0.7l7.05,-4.11c0.54,0.5 1.25,0.81 2.04,0.81 1.66,0 3,-1.34 3,-3s-1.34,-3 -3,-3 -3,1.34 -3,3c0,0.24 0.04,0.47 0.09,0.7L8.04,9.81C7.5,9.31 6.79,9 6,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3c0.79,0 1.5,-0.31 2.04,-0.81l7.12,4.16c-0.05,0.21 -0.08,0.43 -0.08,0.65 0,1.61 1.31,2.92 2.92,2.92 1.61,0 2.92,-1.31 2.92,-2.92s-1.31,-2.92 -2.92,-2.92z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_sort_by_char.xml",
    "content": "<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:fillColor=\"@color/color_icon_grey\"\n      android:pathData=\"M14.94,4.66h-4.72l2.36,-2.36zM10.25,19.37h4.66l-2.33,2.33zM6.1,6.27L1.6,17.73h1.84l0.92,-2.45h5.11l0.92,2.45h1.84L7.74,6.27L6.1,6.27zM4.97,13.64l1.94,-5.18 1.94,5.18L4.97,13.64zM15.73,16.14h6.12v1.59h-8.53v-1.29l5.92,-8.56h-5.88v-1.6h8.3v1.26l-5.93,8.6z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_sort_down.xml",
    "content": "<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:fillColor=\"@color/color_icon_grey\"\n      android:pathData=\"M19,15l-1.41,-1.41L13,18.17V2H11v16.17l-4.59,-4.59L5,15l7,7L19,15z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_sort_up.xml",
    "content": "<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:fillColor=\"@color/color_icon_grey\"\n      android:pathData=\"M5,9l1.41,1.41L11,5.83V22H13V5.83l4.59,4.59L19,9l-7,-7L5,9z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_ssh.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"128dp\"\n    android:height=\"128dp\"\n    android:viewportWidth=\"1024\"\n    android:viewportHeight=\"1024\">\n  <path\n      android:pathData=\"M680.96,753.66c-7.17,0 -13.31,-2.56 -16.38,-5.63 -7.17,-10.24 -6.14,-23.55 -0.51,-29.7l121.86,-121.34 -121.86,-117.76c-14.34,-13.82 -10.75,-23.04 -5.63,-30.21 3.07,-4.1 8.7,-6.14 15.36,-6.14 7.68,0 14.85,3.07 19.97,7.68l138.75,137.73 1.02,0.51c1.54,1.02 3.58,4.61 4.1,8.7 0.51,5.63 -1.54,11.26 -5.63,15.36L696.83,747.52c-5.12,5.12 -12.29,6.14 -15.87,6.14zM345.6,750.08c-5.63,0 -11.26,-2.05 -14.85,-6.14l-138.24,-137.22c-4.1,-4.1 -5.63,-8.19 -5.63,-12.8 0,-4.61 2.05,-9.22 5.63,-13.31l138.24,-137.22c3.58,-3.58 9.73,-5.63 15.87,-5.63 6.14,0 12.29,2.05 16.9,5.63 8.19,10.75 6.14,22.53 0,29.18L241.66,593.92 363.52,715.26c3.58,3.58 5.63,9.22 5.63,14.85 0,3.58 -1.02,8.7 -5.12,13.31 -6.14,4.61 -12.8,6.66 -18.43,6.66zM459.26,747.01c-2.56,0 -5.12,-0.51 -7.17,-1.54 -6.14,-2.56 -11.26,-7.68 -13.82,-13.82 -2.56,-5.63 -2.56,-11.26 -0.51,-16.38l101.38,-261.12c3.58,-9.22 13.31,-15.87 23.04,-15.87 2.56,0 5.12,0.51 7.17,1.54 6.14,2.56 11.26,7.68 13.82,13.82 2.56,5.63 2.56,11.26 0.51,16.38l-101.38,261.12c-3.58,9.22 -13.31,15.87 -23.04,15.87z\"\n      android:fillColor=\"#2196F3\"/>\n  <path\n      android:pathData=\"M957.64,45.97h-891.29c-28.54,0 -51.85,23.3 -51.85,52.43v827.21c0,29.13 23.3,52.43 51.85,52.43h891.29c28.54,0 51.85,-23.3 51.85,-52.43L1009.49,98.4c0,-28.54 -23.3,-52.43 -51.85,-52.43zM76.26,99.56h870.9v124.08L76.26,223.64L76.26,99.56zM950.07,919.78L73.93,919.78L73.93,277.82h876.14L950.07,919.78z\"\n      android:fillColor=\"#979CA0\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_star.xml",
    "content": "<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:fillColor=\"@color/kpa_icon_color\"\n      android:pathData=\"M12,17.27L18.18,21L16.54,13.97L22,9.24L14.81,8.62L12,2L9.19,8.62L2,9.24L7.45,13.97L5.82,21L12,17.27Z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_star_outline.xml",
    "content": "<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:fillColor=\"@color/color_icon_grey\"\n      android:pathData=\"M12,15.39L8.24,17.66L9.23,13.38L5.91,10.5L10.29,10.13L12,6.09L13.71,10.13L18.09,10.5L14.77,13.38L15.76,17.66M22,9.24L14.81,8.63L12,2L9.19,8.63L2,9.24L7.45,13.97L5.82,21L12,17.27L18.18,21L16.54,13.97L22,9.24Z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_star_rate.xml",
    "content": "<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:fillColor=\"@color/color_icon_grey\"\n      android:pathData=\"M14.43,10l-2.43,-8l-2.43,8l-7.57,0l6.18,4.41l-2.35,7.59l6.17,-4.69l6.18,4.69l-2.35,-7.59l6.17,-4.41z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_start_selector.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n  <item android:drawable=\"@drawable/ic_star\" android:state_selected=\"true\" />\n  <item android:drawable=\"@drawable/ic_star_outline\" android:state_selected=\"false\" />\n</selector>"
  },
  {
    "path": "app/src/main/res/drawable/ic_state_bar.xml",
    "content": "<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:fillColor=\"@color/color_icon_grey\"\n      android:pathData=\"M20,14H4V10H20\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_swap_horiz.xml",
    "content": "<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:fillColor=\"@color/color_icon_grey\"\n        android:pathData=\"M6.99,11L3,15l3.99,4v-3H14v-2H6.99v-3zM21,9l-3.99,-4v3H10v2h7.01v3L21,9z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_tab_db.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"1024\"\n    android:viewportHeight=\"1024\">\n  <path\n      android:fillColor=\"@color/text_gray_color\"\n      android:pathData=\"M512,942c-89.8,0 -174.6,-10.7 -238.8,-30.1 -33,-10 -59.5,-22 -78.8,-35.8 -33.3,-23.8 -40.3,-50.2 -40.3,-68.2L154.1,216.1c0,-26.1 13.5,-49 40.3,-68.2 19.2,-13.8 45.7,-25.8 78.8,-35.8C337.5,92.7 422.3,82 512,82c89.8,0 174.6,10.7 238.8,30.1 19.2,5.8 36.4,12.4 51.1,19.6l27.8,16.3c33.2,23.8 40.1,50.1 40.1,68.1v591.8c0,18 -7,44.4 -40.3,68.2 -19.2,13.8 -45.7,25.8 -78.8,35.8C686.6,931.3 601.8,942 512,942zM218.6,806.2l0.8,1.1c3.5,4.5 18.9,20.4 70.8,36.1C349.1,861.2 427.9,871 512,871c81.9,0 159.1,-9.4 217.4,-26.3l4.3,-1.3c51.9,-15.7 67.3,-31.6 70.8,-36.1l0.8,-1.1L805.3,693.4l-5.8,2.7c-14.1,6.7 -30.6,12.9 -48.8,18.5 -64.2,19.5 -149,30.2 -238.8,30.2 -89.8,0 -174.6,-10.7 -238.8,-30.1 -18.3,-5.5 -34.7,-11.8 -48.8,-18.5l-5.8,-2.7v112.7zM218.6,608.9l0.8,1.1c3.5,4.5 18.9,20.4 70.8,36.1 58.8,17.8 137.6,27.6 221.7,27.6s162.8,-9.8 221.7,-27.6c51.9,-15.7 67.3,-31.6 70.8,-36.1l0.8,-1.1L805.2,496.1l-5.8,2.7c-14.1,6.7 -30.6,12.9 -48.8,18.5 -64.3,19.4 -149.1,30.1 -238.8,30.1 -89.8,0 -174.6,-10.7 -238.8,-30.1 -18.2,-5.5 -34.7,-11.8 -48.8,-18.5l-5.8,-2.7v112.8zM218.6,411.6l0.8,1.1c3.5,4.5 18.9,20.4 70.8,36.1 58.8,17.8 137.6,27.6 221.7,27.6s162.8,-9.8 221.7,-27.6c51.9,-15.7 67.3,-31.6 70.8,-36.1l0.8,-1.1L805.2,298.9l-5.8,2.7c-14.1,6.7 -30.6,12.9 -48.8,18.5 -64.3,19.4 -149.1,30.1 -238.8,30.1 -89.8,0 -174.6,-10.7 -238.8,-30.1 -18.2,-5.5 -34.7,-11.8 -48.8,-18.5l-5.8,-2.7v112.7zM512,153c-84.1,0 -162.8,9.8 -221.7,27.6 -42.8,13 -60.7,26 -67.6,32.6l-3,2.9 3,2.9c6.9,6.6 24.8,19.6 67.6,32.6 58.8,17.8 137.6,27.6 221.7,27.6s162.8,-9.8 221.7,-27.6c42.8,-13 60.7,-26 67.6,-32.6l3,-2.9 -3,-2.9c-6.9,-6.6 -24.8,-19.6 -67.6,-32.6C674.9,162.8 596.1,153 512,153z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_tab_db_gray.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"1024\"\n    android:viewportHeight=\"1024\">\n  <path\n      android:fillColor=\"@color/color_icon_grey\"\n      android:pathData=\"M512,942c-89.8,0 -174.6,-10.7 -238.8,-30.1 -33,-10 -59.5,-22 -78.8,-35.8 -33.3,-23.8 -40.3,-50.2 -40.3,-68.2L154.1,216.1c0,-26.1 13.5,-49 40.3,-68.2 19.2,-13.8 45.7,-25.8 78.8,-35.8C337.5,92.7 422.3,82 512,82c89.8,0 174.6,10.7 238.8,30.1 19.2,5.8 36.4,12.4 51.1,19.6l27.8,16.3c33.2,23.8 40.1,50.1 40.1,68.1v591.8c0,18 -7,44.4 -40.3,68.2 -19.2,13.8 -45.7,25.8 -78.8,35.8C686.6,931.3 601.8,942 512,942zM218.6,806.2l0.8,1.1c3.5,4.5 18.9,20.4 70.8,36.1C349.1,861.2 427.9,871 512,871c81.9,0 159.1,-9.4 217.4,-26.3l4.3,-1.3c51.9,-15.7 67.3,-31.6 70.8,-36.1l0.8,-1.1L805.3,693.4l-5.8,2.7c-14.1,6.7 -30.6,12.9 -48.8,18.5 -64.2,19.5 -149,30.2 -238.8,30.2 -89.8,0 -174.6,-10.7 -238.8,-30.1 -18.3,-5.5 -34.7,-11.8 -48.8,-18.5l-5.8,-2.7v112.7zM218.6,608.9l0.8,1.1c3.5,4.5 18.9,20.4 70.8,36.1 58.8,17.8 137.6,27.6 221.7,27.6s162.8,-9.8 221.7,-27.6c51.9,-15.7 67.3,-31.6 70.8,-36.1l0.8,-1.1L805.2,496.1l-5.8,2.7c-14.1,6.7 -30.6,12.9 -48.8,18.5 -64.3,19.4 -149.1,30.1 -238.8,30.1 -89.8,0 -174.6,-10.7 -238.8,-30.1 -18.2,-5.5 -34.7,-11.8 -48.8,-18.5l-5.8,-2.7v112.8zM218.6,411.6l0.8,1.1c3.5,4.5 18.9,20.4 70.8,36.1 58.8,17.8 137.6,27.6 221.7,27.6s162.8,-9.8 221.7,-27.6c51.9,-15.7 67.3,-31.6 70.8,-36.1l0.8,-1.1L805.2,298.9l-5.8,2.7c-14.1,6.7 -30.6,12.9 -48.8,18.5 -64.3,19.4 -149.1,30.1 -238.8,30.1 -89.8,0 -174.6,-10.7 -238.8,-30.1 -18.2,-5.5 -34.7,-11.8 -48.8,-18.5l-5.8,-2.7v112.7zM512,153c-84.1,0 -162.8,9.8 -221.7,27.6 -42.8,13 -60.7,26 -67.6,32.6l-3,2.9 3,2.9c6.9,6.6 24.8,19.6 67.6,32.6 58.8,17.8 137.6,27.6 221.7,27.6s162.8,-9.8 221.7,-27.6c42.8,-13 60.7,-26 67.6,-32.6l3,-2.9 -3,-2.9c-6.9,-6.6 -24.8,-19.6 -67.6,-32.6C674.9,162.8 596.1,153 512,153z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_tab_db_selected.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"1024\"\n    android:viewportHeight=\"1024\">\n  <path\n      android:fillColor=\"@color/colorPrimary\"\n      android:pathData=\"M512,942c-89.8,0 -174.6,-10.7 -238.8,-30.1 -33,-10 -59.5,-22 -78.8,-35.8 -33.3,-23.8 -40.3,-50.2 -40.3,-68.2L154.1,216.1c0,-26.1 13.5,-49 40.3,-68.2 19.2,-13.8 45.7,-25.8 78.8,-35.8C337.5,92.7 422.3,82 512,82c89.8,0 174.6,10.7 238.8,30.1 19.2,5.8 36.4,12.4 51.1,19.6l27.8,16.3c33.2,23.8 40.1,50.1 40.1,68.1v591.8c0,18 -7,44.4 -40.3,68.2 -19.2,13.8 -45.7,25.8 -78.8,35.8C686.6,931.3 601.8,942 512,942zM218.6,806.2l0.8,1.1c3.5,4.5 18.9,20.4 70.8,36.1C349.1,861.2 427.9,871 512,871c81.9,0 159.1,-9.4 217.4,-26.3l4.3,-1.3c51.9,-15.7 67.3,-31.6 70.8,-36.1l0.8,-1.1L805.3,693.4l-5.8,2.7c-14.1,6.7 -30.6,12.9 -48.8,18.5 -64.2,19.5 -149,30.2 -238.8,30.2 -89.8,0 -174.6,-10.7 -238.8,-30.1 -18.3,-5.5 -34.7,-11.8 -48.8,-18.5l-5.8,-2.7v112.7zM218.6,608.9l0.8,1.1c3.5,4.5 18.9,20.4 70.8,36.1 58.8,17.8 137.6,27.6 221.7,27.6s162.8,-9.8 221.7,-27.6c51.9,-15.7 67.3,-31.6 70.8,-36.1l0.8,-1.1L805.2,496.1l-5.8,2.7c-14.1,6.7 -30.6,12.9 -48.8,18.5 -64.3,19.4 -149.1,30.1 -238.8,30.1 -89.8,0 -174.6,-10.7 -238.8,-30.1 -18.2,-5.5 -34.7,-11.8 -48.8,-18.5l-5.8,-2.7v112.8zM218.6,411.6l0.8,1.1c3.5,4.5 18.9,20.4 70.8,36.1 58.8,17.8 137.6,27.6 221.7,27.6s162.8,-9.8 221.7,-27.6c51.9,-15.7 67.3,-31.6 70.8,-36.1l0.8,-1.1L805.2,298.9l-5.8,2.7c-14.1,6.7 -30.6,12.9 -48.8,18.5 -64.3,19.4 -149.1,30.1 -238.8,30.1 -89.8,0 -174.6,-10.7 -238.8,-30.1 -18.2,-5.5 -34.7,-11.8 -48.8,-18.5l-5.8,-2.7v112.7zM512,153c-84.1,0 -162.8,9.8 -221.7,27.6 -42.8,13 -60.7,26 -67.6,32.6l-3,2.9 3,2.9c6.9,6.6 24.8,19.6 67.6,32.6 58.8,17.8 137.6,27.6 221.7,27.6s162.8,-9.8 221.7,-27.6c42.8,-13 60.7,-26 67.6,-32.6l3,-2.9 -3,-2.9c-6.9,-6.6 -24.8,-19.6 -67.6,-32.6C674.9,162.8 596.1,153 512,153z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_tab_history.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"128dp\"\n    android:height=\"128dp\"\n    android:viewportWidth=\"1024\"\n    android:viewportHeight=\"1024\">\n  <path\n      android:pathData=\"M512,51.2a460.8,460.8 0,1 0,153.6 894.98l-25.6,-72.19a384,384 0,1 1,256 -383.49L972.8,486.4A460.8,460.8 0,0 0,512 51.2z\"\n      android:fillColor=\"@color/text_gray_color\"/>\n  <path\n      android:pathData=\"M665.6,563.2h307.2v76.8h-307.2zM665.6,716.8h307.2v76.8h-307.2zM473.6,473.6H307.2v76.8h243.2V256h-76.8v217.6z\"\n      android:fillColor=\"@color/text_gray_color\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_tab_history_selected.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"128dp\"\n    android:height=\"128dp\"\n    android:viewportWidth=\"1024\"\n    android:viewportHeight=\"1024\">\n  <path\n      android:pathData=\"M512,51.2a460.8,460.8 0,1 0,153.6 894.98l-25.6,-72.19a384,384 0,1 1,256 -383.49L972.8,486.4A460.8,460.8 0,0 0,512 51.2z\"\n      android:fillColor=\"@color/colorPrimary\"/>\n  <path\n      android:pathData=\"M665.6,563.2h307.2v76.8h-307.2zM665.6,716.8h307.2v76.8h-307.2zM473.6,473.6H307.2v76.8h243.2V256h-76.8v217.6z\"\n      android:fillColor=\"@color/colorPrimary\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_tag.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"1024\"\n    android:viewportHeight=\"1024\">\n  <path\n      android:fillColor=\"@color/color_icon_grey\"\n      android:pathData=\"M896.51,976.96l-1.02,-869.86a64,64 0,0 0,-64 -64h-640a64,64 0,0 0,-64 64l2.53,873.5h0.29l137.18,-137.54 117.25,136.32h2.3l118.46,-136.32L624.8,978.24h2.43l120.26,-134.56 148,133.28h-0.93,1.92zM622.69,499.9l46.88,183.62 -158.05,-98.56 -158.08,98.56 45.54,-179.65 -143.2,-117.22 182.4,-12.96 73.34,-170.56 67.42,170.62 188.32,12.9 -144.58,113.28z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_text_fields_24px.xml",
    "content": "<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:fillColor=\"@color/color_icon_grey\"\n      android:pathData=\"M2.5,4v3h5v12h3V7h5V4H2.5zM21.5,9h-9v3h3v7h3v-7h3V9z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_theme_style.xml",
    "content": "<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:fillColor=\"@color/color_icon_grey\"\n      android:pathData=\"M12,3c-4.97,0 -9,4.03 -9,9s4.03,9 9,9c0.83,0 1.5,-0.67 1.5,-1.5 0,-0.39 -0.15,-0.74 -0.39,-1.01 -0.23,-0.26 -0.38,-0.61 -0.38,-0.99 0,-0.83 0.67,-1.5 1.5,-1.5L16,16c2.76,0 5,-2.24 5,-5 0,-4.42 -4.03,-8 -9,-8zM6.5,12c-0.83,0 -1.5,-0.67 -1.5,-1.5S5.67,9 6.5,9 8,9.67 8,10.5 7.33,12 6.5,12zM9.5,8C8.67,8 8,7.33 8,6.5S8.67,5 9.5,5s1.5,0.67 1.5,1.5S10.33,8 9.5,8zM14.5,8c-0.83,0 -1.5,-0.67 -1.5,-1.5S13.67,5 14.5,5s1.5,0.67 1.5,1.5S15.33,8 14.5,8zM17.5,12c-0.83,0 -1.5,-0.67 -1.5,-1.5S16.67,9 17.5,9s1.5,0.67 1.5,1.5 -0.67,1.5 -1.5,1.5z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_title_24px.xml",
    "content": "<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:fillColor=\"@color/color_icon_grey\"\n      android:pathData=\"M5,4v3h5.5v12h3V7H19V4z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_token_blue.xml",
    "content": "<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=\"M19.97,6.43L12,2L4.03,6.43L9.1,9.24C9.83,8.48 10.86,8 12,8s2.17,0.48 2.9,1.24L19.97,6.43zM10,12c0,-1.1 0.9,-2 2,-2s2,0.9 2,2s-0.9,2 -2,2S10,13.1 10,12zM11,21.44L3,17V8.14l5.13,2.85C8.04,11.31 8,11.65 8,12c0,1.86 1.27,3.43 3,3.87V21.44zM13,21.44v-5.57c1.73,-0.44 3,-2.01 3,-3.87c0,-0.35 -0.04,-0.69 -0.13,-1.01L21,8.14L21,17L13,21.44z\"\n      android:fillColor=\"@color/colorPrimary\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_token_grey.xml",
    "content": "<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=\"M19.97,6.43L12,2L4.03,6.43L9.1,9.24C9.83,8.48 10.86,8 12,8s2.17,0.48 2.9,1.24L19.97,6.43zM10,12c0,-1.1 0.9,-2 2,-2s2,0.9 2,2s-0.9,2 -2,2S10,13.1 10,12zM11,21.44L3,17V8.14l5.13,2.85C8.04,11.31 8,11.65 8,12c0,1.86 1.27,3.43 3,3.87V21.44zM13,21.44v-5.57c1.73,-0.44 3,-2.01 3,-3.87c0,-0.35 -0.04,-0.69 -0.13,-1.01L21,8.14L21,17L13,21.44z\"\n      android:fillColor=\"@color/color_icon_grey\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_token_txt_grey.xml",
    "content": "<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=\"M19.97,6.43L12,2L4.03,6.43L9.1,9.24C9.83,8.48 10.86,8 12,8s2.17,0.48 2.9,1.24L19.97,6.43zM10,12c0,-1.1 0.9,-2 2,-2s2,0.9 2,2s-0.9,2 -2,2S10,13.1 10,12zM11,21.44L3,17V8.14l5.13,2.85C8.04,11.31 8,11.65 8,12c0,1.86 1.27,3.43 3,3.87V21.44zM13,21.44v-5.57c1.73,-0.44 3,-2.01 3,-3.87c0,-0.35 -0.04,-0.69 -0.13,-1.01L21,8.14L21,17L13,21.44z\"\n      android:fillColor=\"@color/text_gray_color\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_totp.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"1024\"\n    android:viewportHeight=\"1024\">\n  <path\n      android:fillColor=\"#95EAFF\"\n      android:pathData=\"M496.64,204.8l-23.04,20.48c-64,51.2 -161.28,92.16 -238.08,102.4l-87.04,12.8c-2.56,71.68 0,189.44 33.28,299.52 25.6,87.04 69.12,168.96 140.8,215.04 0,0 69.12,56.32 186.88,89.6V192l-12.8,12.8z\" />\n  <path\n      android:fillColor=\"#212E3A\"\n      android:pathData=\"M284.16,448v158.72L248.32,606.72v-158.72h-51.2v-30.72h138.24v30.72h-51.2zM504.32,512c0,17.92 -2.56,30.72 -5.12,43.52 -5.12,12.8 -10.24,23.04 -15.36,30.72 -7.68,7.68 -15.36,12.8 -25.6,17.92 -10.24,2.56 -20.48,5.12 -30.72,5.12 -25.6,0 -43.52,-7.68 -56.32,-25.6 -12.8,-15.36 -17.92,-40.96 -17.92,-71.68 0,-17.92 2.56,-30.72 5.12,-43.52 5.12,-12.8 10.24,-23.04 15.36,-30.72 7.68,-7.68 15.36,-12.8 25.6,-17.92 10.24,-2.56 20.48,-5.12 30.72,-5.12 25.6,0 43.52,7.68 56.32,25.6 10.24,15.36 17.92,38.4 17.92,71.68zM465.92,512c0,-23.04 -2.56,-38.4 -7.68,-48.64 -5.12,-10.24 -15.36,-15.36 -28.16,-15.36 -7.68,0 -12.8,2.56 -17.92,5.12 -5.12,2.56 -7.68,7.68 -12.8,12.8 -2.56,5.12 -5.12,12.8 -7.68,20.48s-2.56,17.92 -2.56,28.16c0,23.04 2.56,38.4 10.24,48.64 5.12,10.24 15.36,15.36 28.16,15.36 7.68,0 12.8,-2.56 17.92,-5.12s7.68,-7.68 12.8,-12.8c2.56,-5.12 5.12,-12.8 7.68,-20.48L465.92,512z\" />\n  <path\n      android:fillColor=\"#4D9DFE\"\n      android:pathData=\"M826.88,478.72c0,10.24 -2.56,17.92 -5.12,28.16s-7.68,15.36 -15.36,20.48l-23.04,15.36c-10.24,2.56 -20.48,5.12 -33.28,5.12h-17.92v61.44h-35.84v-186.88h56.32c12.8,0 23.04,2.56 33.28,5.12s17.92,7.68 23.04,12.8c5.12,5.12 10.24,10.24 15.36,17.92 2.56,2.56 2.56,10.24 2.56,20.48zM791.04,481.28c0,-5.12 0,-10.24 -2.56,-12.8 -2.56,-5.12 -5.12,-7.68 -7.68,-10.24 -2.56,-2.56 -7.68,-5.12 -12.8,-7.68 -5.12,-2.56 -10.24,-2.56 -17.92,-2.56h-17.92v69.12h20.48c5.12,0 12.8,0 15.36,-2.56 5.12,-2.56 7.68,-5.12 12.8,-7.68 2.56,-2.56 5.12,-7.68 7.68,-10.24 0,-5.12 2.56,-10.24 2.56,-15.36z\" />\n  <path\n      android:fillColor=\"#212E3A\"\n      android:pathData=\"M506.88,944.64C243.2,857.6 168.96,734.72 153.6,432.64v-158.72V238.08h5.12L217.6,230.4c89.6,-12.8 194.56,-58.88 271.36,-117.76l2.56,-2.56 20.48,-17.92V25.6l-35.84,28.16 -23.04,23.04c-64,51.2 -161.28,92.16 -238.08,102.4L102.4,194.56v79.36c0,56.32 0,110.08 2.56,158.72 17.92,320 99.84,465.92 389.12,563.2l15.36,5.12V947.2l-2.56,-2.56z\" />\n  <path\n      android:fillColor=\"#212E3A\"\n      android:pathData=\"M512,998.4l17.92,-5.12c289.28,-97.28 371.2,-240.64 389.12,-563.2 2.56,-48.64 2.56,-102.4 2.56,-158.72v-76.8L811.52,179.2c-79.36,-10.24 -174.08,-51.2 -238.08,-102.4l-23.04,-20.48L512,25.6v66.56h2.56l20.48,17.92 2.56,2.56c76.8,58.88 181.76,104.96 271.36,117.76l58.88,7.68h5.12v38.4c0,61.44 0,107.52 -2.56,156.16 -15.36,302.08 -87.04,424.96 -350.72,512H512V998.4z\" />\n  <path\n      android:fillColor=\"#4D9DFE\"\n      android:pathData=\"M611.84,448v158.72h-35.84v-158.72h-51.2v-30.72h138.24v30.72h-51.2z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_undo_entry.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"1024\"\n    android:viewportHeight=\"1024\">\n  <path\n      android:fillColor=\"@color/color_icon_grey\"\n      android:pathData=\"M817.35,384c59,0 94.07,-65.45 61.86,-114.91C798.79,145.61 659.76,64 501.71,64a448,448 0,0 0,-288.23 104.56L167,122c-24.34,-24.26 -65.89,-10 -70.3,24.1L66.91,376.61a41.45,41.45 0,0 0,46.27 46.51L344,393.64c34.15,-4.36 48.37,-46.19 24,-70.57l-49,-49a300.63,300.63 0,0 1,182.72 -61.59c106.16,0 199.54,54.83 253.53,137.8a74.11,74.11 0,0 0,62.1 33.72z\" />\n  <path\n      android:fillColor=\"@color/color_icon_grey\"\n      android:pathData=\"M817.51,728.03m-74.89,0a74.89,74.89 0,1 0,149.78 0,74.89 74.89,0 1,0 -149.78,0Z\" />\n  <path\n      android:fillColor=\"@color/color_icon_grey\"\n      android:pathData=\"M882.55,512m-74.89,0a74.89,74.89 0,1 0,149.78 0,74.89 74.89,0 1,0 -149.78,0Z\" />\n  <path\n      android:fillColor=\"@color/color_icon_grey\"\n      android:pathData=\"M638.56,872.98m-74.89,0a74.89,74.89 0,1 0,149.78 0,74.89 74.89,0 1,0 -149.78,0Z\" />\n  <path\n      android:fillColor=\"@color/color_icon_grey\"\n      android:pathData=\"M412.6,885.11m-74.89,0a74.89,74.89 0,1 0,149.78 0,74.89 74.89,0 1,0 -149.78,0Z\" />\n  <path\n      android:fillColor=\"@color/color_icon_grey\"\n      android:pathData=\"M212.63,764.02m-74.89,0a74.89,74.89 0,1 0,149.78 0,74.89 74.89,0 1,0 -149.78,0Z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_up.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"20dp\"\n    android:height=\"20dp\"\n    android:viewportWidth=\"1024\"\n    android:viewportHeight=\"1024\">\n  <path\n      android:fillColor=\"@color/text_black_color\"\n      android:pathData=\"M911.46,463.71 L227.76,463.71l317.67,-317.67c18.73,-18.73 18.73,-49.11 0,-67.84l0,0c-18.73,-18.73 -49.11,-18.73 -67.84,0L78.03,477.77c-0.56,0.56 -1.1,1.13 -1.63,1.72 -0.25,0.28 -0.49,0.57 -0.74,0.85 -0.27,0.31 -0.53,0.61 -0.79,0.92 -0.3,0.37 -0.59,0.74 -0.88,1.11 -0.19,0.24 -0.38,0.48 -0.56,0.72 -0.31,0.41 -0.6,0.83 -0.89,1.25 -0.15,0.22 -0.31,0.43 -0.45,0.65 -0.29,0.43 -0.57,0.87 -0.84,1.31 -0.14,0.22 -0.28,0.44 -0.42,0.67 -0.26,0.43 -0.51,0.87 -0.75,1.31 -0.14,0.25 -0.28,0.49 -0.41,0.74 -0.22,0.41 -0.43,0.83 -0.64,1.25 -0.14,0.29 -0.29,0.58 -0.43,0.87 -0.18,0.38 -0.35,0.77 -0.52,1.16 -0.15,0.34 -0.3,0.68 -0.44,1.02 -0.14,0.34 -0.28,0.69 -0.41,1.04 -0.15,0.39 -0.3,0.79 -0.45,1.19 -0.11,0.3 -0.21,0.61 -0.31,0.92 -0.15,0.45 -0.3,0.89 -0.43,1.34 -0.08,0.27 -0.15,0.54 -0.23,0.81 -0.14,0.49 -0.27,0.97 -0.39,1.46 -0.06,0.25 -0.12,0.51 -0.18,0.76 -0.12,0.51 -0.23,1.01 -0.33,1.52 -0.05,0.26 -0.09,0.52 -0.14,0.78 -0.09,0.5 -0.18,1 -0.26,1.51 -0.04,0.3 -0.08,0.6 -0.11,0.91 -0.06,0.47 -0.12,0.93 -0.17,1.41 -0.04,0.4 -0.06,0.81 -0.09,1.21 -0.03,0.38 -0.06,0.75 -0.08,1.13 -0.04,0.79 -0.06,1.58 -0.06,2.37l0,0 0,0c0,0 0,0 0,0 0,0.79 0.02,1.58 0.06,2.37 0.02,0.38 0.05,0.75 0.08,1.13 0.03,0.4 0.05,0.8 0.09,1.21 0.05,0.47 0.11,0.94 0.17,1.41 0.04,0.3 0.07,0.6 0.11,0.9 0.07,0.51 0.16,1.01 0.26,1.51 0.05,0.26 0.09,0.52 0.14,0.78 0.1,0.51 0.22,1.02 0.33,1.53 0.06,0.25 0.11,0.51 0.17,0.76 0.12,0.49 0.26,0.98 0.39,1.46 0.08,0.27 0.15,0.54 0.23,0.81 0.14,0.45 0.28,0.9 0.43,1.34 0.1,0.3 0.2,0.61 0.31,0.91 0.14,0.4 0.3,0.8 0.45,1.19 0.13,0.35 0.27,0.69 0.41,1.04 0.14,0.34 0.29,0.69 0.45,1.03 0.17,0.39 0.34,0.77 0.52,1.15 0.14,0.29 0.29,0.58 0.43,0.87 0.21,0.42 0.42,0.84 0.64,1.25 0.14,0.25 0.28,0.5 0.42,0.75 0.24,0.44 0.49,0.87 0.75,1.31 0.14,0.23 0.28,0.45 0.42,0.67 0.27,0.44 0.55,0.88 0.84,1.31 0.15,0.22 0.3,0.44 0.46,0.66 0.29,0.42 0.58,0.84 0.89,1.25 0.18,0.25 0.37,0.48 0.56,0.72 0.29,0.37 0.57,0.74 0.87,1.11 0.26,0.32 0.53,0.62 0.8,0.93 0.24,0.28 0.48,0.57 0.73,0.84 0.53,0.59 1.08,1.16 1.64,1.72l399.56,399.56c18.73,18.73 49.11,18.73 67.84,0l0,0c18.73,-18.73 18.73,-49.11 0,-67.84L227.76,559.66l683.7,0c26.49,0 47.97,-21.48 47.97,-47.97l0,0C959.43,485.19 937.95,463.71 911.46,463.71z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_user.xml",
    "content": "<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:fillColor=\"@color/color_icon_grey\"\n      android:pathData=\"M12,12c2.21,0 4,-1.79 4,-4s-1.79,-4 -4,-4 -4,1.79 -4,4 1.79,4 4,4zM12,14c-2.67,0 -8,1.34 -8,4v2h16v-2c0,-2.66 -5.33,-4 -8,-4z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_view.xml",
    "content": "<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:fillColor=\"@color/color_icon_grey\"\n      android:pathData=\"M12,4.5C7,4.5 2.73,7.61 1,12c1.73,4.39 6,7.5 11,7.5s9.27,-3.11 11,-7.5c-1.73,-4.39 -6,-7.5 -11,-7.5zM12,17c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5 -2.24,5 -5,5zM12,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3 3,-1.34 3,-3 -1.34,-3 -3,-3z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_view_black.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"1024\"\n    android:viewportHeight=\"1024\">\n  <path\n      android:fillColor=\"@color/color_icon_grey\"\n      android:pathData=\"M512,608a96,96 0,1 1,0 -192,96 96,0 0,1 0,192m0,-256c-88.22,0 -160,71.78 -160,160s71.78,160 160,160 160,-71.78 160,-160 -71.78,-160 -160,-160\" />\n  <path\n      android:fillColor=\"@color/color_icon_grey\"\n      android:pathData=\"M512,800c-212.06,0 -384,-256 -384,-288s171.94,-288 384,-288 384,256 384,288 -171.94,288 -384,288m0,-640C265.25,160 64,443.01 64,512c0,68.99 201.25,352 448,352s448,-283.01 448,-352c0,-68.99 -201.25,-352 -448,-352\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_view_headline_24px.xml",
    "content": "<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:fillColor=\"@color/text_black_color\"\n      android:pathData=\"M4,15h16v-2L4,13v2zM4,19h16v-2L4,17v2zM4,11h16L20,9L4,9v2zM4,5v2h16L20,5L4,5z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_view_off.xml",
    "content": "<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:fillColor=\"@color/color_icon_grey\"\n      android:pathData=\"M12,7c2.76,0 5,2.24 5,5 0,0.65 -0.13,1.26 -0.36,1.83l2.92,2.92c1.51,-1.26 2.7,-2.89 3.43,-4.75 -1.73,-4.39 -6,-7.5 -11,-7.5 -1.4,0 -2.74,0.25 -3.98,0.7l2.16,2.16C10.74,7.13 11.35,7 12,7zM2,4.27l2.28,2.28 0.46,0.46C3.08,8.3 1.78,10.02 1,12c1.73,4.39 6,7.5 11,7.5 1.55,0 3.03,-0.3 4.38,-0.84l0.42,0.42L19.73,22 21,20.73 3.27,3 2,4.27zM7.53,9.8l1.55,1.55c-0.05,0.21 -0.08,0.43 -0.08,0.65 0,1.66 1.34,3 3,3 0.22,0 0.44,-0.03 0.65,-0.08l1.55,1.55c-0.67,0.33 -1.41,0.53 -2.2,0.53 -2.76,0 -5,-2.24 -5,-5 0,-0.79 0.2,-1.53 0.53,-2.2zM11.84,9.02l3.15,3.15 0.02,-0.16c0,-1.66 -1.34,-3 -3,-3l-0.17,0.01z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_view_off_black.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"1024\"\n    android:viewportHeight=\"1024\">\n  <path\n      android:fillColor=\"@color/text_black_color\"\n      android:pathData=\"M512,800c-66.11,0 -128.32,-24.9 -182.66,-60.1l94.98,-94.98A156.26,156.26 0,0 0,512 672c88.22,0 160,-71.78 160,-160a156.26,156.26 0,0 0,-27.07 -87.68l101.54,-101.54C837.28,398.62 896,493.34 896,512c0,32 -171.94,288 -384,288m96,-288a96,96 0,0 1,-96 96c-14.78,0 -28.64,-3.62 -41.09,-9.66l127.42,-127.42c6.05,12.45 9.66,26.3 9.66,41.09M128,512c0,-32 171.94,-288 384,-288 66.11,0 128.32,24.9 182.66,60.1L277.54,701.22C186.72,625.38 128,530.66 128,512m664.06,-234.82l91.33,-91.33 -45.25,-45.25 -97.63,97.63C673.47,192.7 595.46,160 512,160 265.25,160 64,443.01 64,512c0,39.39 65.73,148.42 167.94,234.82l-91.33,91.33 45.25,45.25 97.63,-97.63C350.53,831.3 428.54,864 512,864c246.75,0 448,-283.01 448,-352 0,-39.39 -65.73,-148.42 -167.94,-234.82\" />\n  <path\n      android:fillColor=\"@color/text_black_color\"\n      android:pathData=\"M512,352c-88.22,0 -160,71.78 -160,160 0,15.33 2.85,29.86 6.88,43.87l58.59,-58.59a95.62,95.62 0,0 1,79.81 -79.81l58.59,-58.59A157.76,157.76 0,0 0,512 352\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_visibility_off.xml",
    "content": "<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:fillColor=\"@color/color_icon_grey\"\n      android:pathData=\"M12,7c2.76,0 5,2.24 5,5 0,0.65 -0.13,1.26 -0.36,1.83l2.92,2.92c1.51,-1.26 2.7,-2.89 3.43,-4.75 -1.73,-4.39 -6,-7.5 -11,-7.5 -1.4,0 -2.74,0.25 -3.98,0.7l2.16,2.16C10.74,7.13 11.35,7 12,7zM2,4.27l2.28,2.28 0.46,0.46C3.08,8.3 1.78,10.02 1,12c1.73,4.39 6,7.5 11,7.5 1.55,0 3.03,-0.3 4.38,-0.84l0.42,0.42L19.73,22 21,20.73 3.27,3 2,4.27zM7.53,9.8l1.55,1.55c-0.05,0.21 -0.08,0.43 -0.08,0.65 0,1.66 1.34,3 3,3 0.22,0 0.44,-0.03 0.65,-0.08l1.55,1.55c-0.67,0.33 -1.41,0.53 -2.2,0.53 -2.76,0 -5,-2.24 -5,-5 0,-0.79 0.2,-1.53 0.53,-2.2zM11.84,9.02l3.15,3.15 0.02,-0.16c0,-1.66 -1.34,-3 -3,-3l-0.17,0.01z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ripple_primary_selector.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n  <!-- 没有焦点时的背景颜色 -->\n  <item android:drawable=\"@color/transparent\" android:state_window_focused=\"false\" />\n  <!-- 非触摸模式下获得焦点并单击时的背景颜色 -->\n  <item android:drawable=\"@color/ripple\" android:state_focused=\"true\" android:state_pressed=\"true\" />\n  <!-- 触摸模式下单击时的背景颜色 -->\n  <item android:drawable=\"@color/ripple\" android:state_focused=\"false\" android:state_pressed=\"true\" />\n  <!-- 选中时的背景颜色 -->\n  <item android:drawable=\"@color/ripple\" android:state_selected=\"true\" />\n  <!-- 获得焦点时的背景  颜色 -->\n  <item android:drawable=\"@color/ripple\" android:state_focused=\"true\" />\n</selector>"
  },
  {
    "path": "app/src/main/res/drawable/ripple_white_selector.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n  <!-- 没有焦点时的背景颜色 -->\n  <item android:drawable=\"@color/transparent\" android:state_window_focused=\"false\" />\n  <!-- 非触摸模式下获得焦点并单击时的背景颜色 -->\n  <item android:drawable=\"@color/ripple\" android:state_focused=\"true\" android:state_pressed=\"true\" />\n  <!-- 触摸模式下单击时的背景颜色 -->\n  <item android:drawable=\"@color/ripple\" android:state_focused=\"false\" android:state_pressed=\"true\" />\n  <!-- 选中时的背景颜色 -->\n  <item android:drawable=\"@color/ripple\" android:state_selected=\"true\" />\n  <!-- 获得焦点时的背景  颜色 -->\n  <item android:drawable=\"@color/ripple\" android:state_focused=\"true\" />\n</selector>"
  },
  {
    "path": "app/src/main/res/drawable/selector_ic_tab_db.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n  <item android:drawable=\"@drawable/ic_tab_db_selected\" android:state_selected=\"true\" />\n  <item android:drawable=\"@drawable/ic_tab_db\" android:state_selected=\"false\" />\n\n</selector>"
  },
  {
    "path": "app/src/main/res/drawable/selector_ic_tab_history.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n  <item android:drawable=\"@drawable/ic_tab_history_selected\" android:state_selected=\"true\" />\n  <item android:drawable=\"@drawable/ic_tab_history\" android:state_selected=\"false\" />\n\n</selector>"
  },
  {
    "path": "app/src/main/res/drawable/selector_ic_tab_token.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n  <item android:drawable=\"@drawable/ic_token_blue\" android:state_selected=\"true\" />\n  <item android:drawable=\"@drawable/ic_token_txt_grey\" android:state_selected=\"false\" />\n\n</selector>"
  },
  {
    "path": "app/src/main/res/drawable/selector_pass_visibility.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n  <item android:drawable=\"@drawable/ic_visibility\" android:state_selected=\"true\" />\n  <item android:drawable=\"@drawable/ic_visibility_off\" android:state_selected=\"false\" />\n</selector>"
  },
  {
    "path": "app/src/main/res/drawable/selector_password.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n  <item android:drawable=\"@drawable/ic_view_off\" android:state_checked=\"false\" android:state_pressed=\"false\" />\n  <item android:drawable=\"@drawable/ic_view\" android:state_checked=\"false\" android:state_pressed=\"true\" />\n  <item android:drawable=\"@drawable/ic_view\" android:state_checked=\"true\" android:state_pressed=\"false\" />\n  <item android:drawable=\"@drawable/ic_view_off\" android:state_checked=\"true\" android:state_pressed=\"true\" />\n</selector>"
  },
  {
    "path": "app/src/main/res/drawable-v21/ripple_primary_selector.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<ripple xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:color=\"@color/ripple\">\n  <!--<item>-->\n    <!--<shape>-->\n      <!--<solid android:color=\"@color/transparent\"/>-->\n      <!--&lt;!&ndash; 设置四个角的角度 &ndash;&gt;-->\n      <!--&lt;!&ndash;<corners android:radius=\"4dp\"/>&ndash;&gt;-->\n    <!--</shape>-->\n  <!--</item>-->\n  <item\n      android:id=\"@android:id/mask\"\n      android:drawable=\"@color/colorPrimary\" />\n</ripple>"
  },
  {
    "path": "app/src/main/res/drawable-v21/ripple_white_selector.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<ripple xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:color=\"@color/ripple\">\n  <!--<item>-->\n    <!--<shape>-->\n      <!--<solid android:color=\"@color/transparent\"/>-->\n      <!--&lt;!&ndash; 设置四个角的角度 &ndash;&gt;-->\n      <!--&lt;!&ndash;<corners android:radius=\"4dp\"/>&ndash;&gt;-->\n    <!--</shape>-->\n  <!--</item>-->\n  <item\n      android:id=\"@android:id/mask\"\n      android:drawable=\"@color/background_color\" />\n</ripple>"
  },
  {
    "path": "app/src/main/res/drawable-v24/ic_launcher_foreground.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:aapt=\"http://schemas.android.com/aapt\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n    android:viewportWidth=\"928.7037\"\n    android:viewportHeight=\"928.7037\">\n  <group android:translateX=\"262.60184\"\n      android:translateY=\"213.60185\">\n    <path\n        android:fillColor=\"#FF000000\"\n        android:pathData=\"M205.8,502.1l-5.4,-1.9C30.7,435.3 10.8,350.9 10.8,292.9V70.9L26,71.3c118.3,4.2 168.5,-48.4 168.9,-49.1l10.9,-11.5l10.9,11.5c0.4,0.4 47.9,49.1 156.1,49.1l0,0c4.3,0 8.2,0 12.5,-0.4l15.2,-0.4v221.6c0,58 -19.5,142.8 -189.6,207.3C211.3,500.2 205.8,502.1 205.8,502.1zM26.4,86.7v205.8c0,31.1 14,127.5 179.4,192c165.4,-64.9 179.4,-160.9 179.4,-192V86.7c-91.9,0 -158.4,-32.3 -179.4,-48.8C184.8,54.4 117.9,86.7 26.4,86.7z\"\n        android:strokeAlpha=\"0.2\"\n        android:fillAlpha=\"0.2\"/>\n    <path\n        android:pathData=\"M201.8,498.1l-5.4,-1.9C26.7,431.3 6.8,346.9 6.8,288.9V66.9L22,67.3c118.3,4.2 168.5,-48.4 168.9,-49.1l10.9,-11.5l10.9,11.5c0.4,0.4 47.9,49.1 156.1,49.1l0,0c4.3,0 8.2,0 12.5,-0.4l15.2,-0.4v221.6c0,58 -19.5,142.8 -189.6,207.3C207.3,496.2 201.8,498.1 201.8,498.1zM22.4,82.7v205.8c0,31.1 14,127.5 179.4,192c165.4,-64.9 179.4,-160.9 179.4,-192V82.7c-91.9,0 -158.4,-32.3 -179.4,-48.8C180.8,50.4 113.9,82.7 22.4,82.7z\">\n      <aapt:attr name=\"android:fillColor\">\n        <gradient \n            android:startY=\"387.1442\"\n            android:startX=\"17.9504\"\n            android:endY=\"18.8757\"\n            android:endX=\"414.1297\"\n            android:type=\"linear\">\n          <item android:offset=\"0.122\" android:color=\"#FF1D7F3B\"/>\n          <item android:offset=\"0.3052\" android:color=\"#FF377E82\"/>\n          <item android:offset=\"0.4369\" android:color=\"#FF477EAF\"/>\n          <item android:offset=\"0.5032\" android:color=\"#FF4D7EC0\"/>\n          <item android:offset=\"0.8572\" android:color=\"#FF4D4398\"/>\n        </gradient>\n      </aapt:attr>\n    </path>\n    <path\n        android:pathData=\"M34.1,286.6c0,29.2 12.8,119 167.4,179.3C356,405.2 368.8,315.4 368.8,286.6V94.2c-85.6,-0.4 -147.9,-30.3 -167.4,-46.1c-19.9,15.4 -82.1,45.7 -167.4,46.1\"\n        android:fillColor=\"#DADCDE\"/>\n    <path\n        android:fillColor=\"#FF000000\"\n        android:pathData=\"M210.7,110c-38.6,0 -70.1,33 -70.1,73.7v24.6H164v-24.6c0,-27.3 20.8,-49.1 46.7,-49.1s46.7,21.9 46.7,49.1v24.6h23.4v-24.6C280.8,143 249.3,110 210.7,110zM105.6,223.7v23h206.3v-23H105.6zM105.6,258.2v23H199v-23H105.6zM218.5,258.2v23h93.4v-23H218.5zM105.6,296.6v23H199v-23H105.6zM218.5,296.6v23h93.4v-23H218.5zM105.6,335v23h206.3v-23H105.6z\"\n        android:strokeAlpha=\"0.2\"\n        android:fillAlpha=\"0.2\"/>\n    <path\n        android:fillColor=\"#FF000000\"\n        android:pathData=\"M210.7,110c-38.6,0 -70.1,33 -70.1,73.7v24.6H164v-24.6c0,-27.3 20.8,-49.1 46.7,-49.1s46.7,21.9 46.7,49.1v24.6h23.4v-24.6C280.8,143 249.3,110 210.7,110zM105.6,223.7v23h206.3v-23H105.6zM105.6,258.2v23H199v-23H105.6zM218.5,258.2v23h93.4v-23H218.5zM105.6,296.6v23H199v-23H105.6zM218.5,296.6v23h93.4v-23H218.5zM105.6,335v23h206.3v-23H105.6z\"\n        android:strokeAlpha=\"0.2\"\n        android:fillAlpha=\"0.2\"/>\n    <path\n        android:pathData=\"M205.7,108c-38.6,0 -70.1,33 -70.1,73.7v24.6H159v-24.6c0,-27.3 20.8,-49.1 46.7,-49.1s46.7,21.9 46.7,49.1v24.6h23.4v-24.6C275.8,141 244.3,108 205.7,108zM100.6,221.7v23h206.3v-23H100.6zM100.6,256.2v23H194v-23H100.6zM213.5,256.2v23h93.4v-23H213.5zM100.6,294.6v23H194v-23H100.6zM213.5,294.6v23h93.4v-23H213.5zM100.6,333v23h206.3v-23H100.6z\">\n      <aapt:attr name=\"android:fillColor\">\n        <gradient \n            android:gradientRadius=\"114.9963\"\n            android:centerX=\"203.7684\"\n            android:centerY=\"232.03732\"\n            android:type=\"radial\">\n          <item android:offset=\"0\" android:color=\"#FF19D3FD\"/>\n          <item android:offset=\"0.9217\" android:color=\"#FF4984C4\"/>\n          <item android:offset=\"1\" android:color=\"#FF4D7EC0\"/>\n        </gradient>\n      </aapt:attr>\n    </path>\n    <path\n        android:pathData=\"M205.7,108c-38.6,0 -70.1,33 -70.1,73.7v24.6H159v-24.6c0,-27.3 20.8,-49.1 46.7,-49.1s46.7,21.9 46.7,49.1v24.6h23.4v-24.6C275.8,141 244.3,108 205.7,108zM100.6,221.7v23h206.3v-23H100.6zM100.6,256.2v23H194v-23H100.6zM213.5,256.2v23h93.4v-23H213.5zM100.6,294.6v23H194v-23H100.6zM213.5,294.6v23h93.4v-23H213.5zM100.6,333v23h206.3v-23H100.6z\">\n      <aapt:attr name=\"android:fillColor\">\n        <gradient \n            android:gradientRadius=\"114.9963\"\n            android:centerX=\"203.7684\"\n            android:centerY=\"232.03732\"\n            android:type=\"radial\">\n          <item android:offset=\"0\" android:color=\"#FF19D3FD\"/>\n          <item android:offset=\"0.9217\" android:color=\"#FF4984C4\"/>\n          <item android:offset=\"1\" android:color=\"#FF4D7EC0\"/>\n        </gradient>\n      </aapt:attr>\n    </path>\n  </group>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/layout/activity_auto_fill_entry_search.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    >\n\n\n  <RelativeLayout\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"match_parent\"\n      android:background=\"@color/background_color\"\n      android:orientation=\"vertical\"\n      >\n\n    <androidx.appcompat.widget.SearchView\n        android:id=\"@+id/search\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_margin=\"16dp\"\n        android:background=\"@drawable/bg_white_radius_2\"\n        android:elevation=\"4dp\"\n        android:imeOptions=\"actionSearch\"\n        app:goIcon=\"@drawable/ic_search\"\n        app:queryHint=\"@string/hint_query\"\n        app:searchIcon=\"@drawable/ic_view_headline_24px\"\n        />\n\n    <androidx.recyclerview.widget.RecyclerView\n        android:id=\"@+id/list\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:layout_below=\"@+id/search\"\n        android:layout_alignStart=\"@+id/search\"\n        android:layout_alignEnd=\"@+id/search\"\n        android:background=\"@drawable/bg_white_radius_2\"\n        />\n\n    <LinearLayout\n        android:id=\"@+id/no_entry_layout\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_centerHorizontal=\"true\"\n        android:layout_centerVertical=\"true\"\n        android:orientation=\"vertical\"\n        >\n\n      <androidx.appcompat.widget.AppCompatImageView\n          android:layout_width=\"50dp\"\n          android:layout_height=\"50dp\"\n          android:layout_gravity=\"center_horizontal\"\n          android:src=\"@drawable/ic_no_record\"\n          />\n\n      <TextView\n          android:layout_width=\"wrap_content\"\n          android:layout_height=\"wrap_content\"\n          android:layout_gravity=\"center_horizontal\"\n          android:layout_marginTop=\"4dp\"\n          android:text=\"@string/no_matching_entry\"\n          android:textColor=\"@color/text_black_color\"\n          android:textSize=\"@dimen/text_size_bigger\"\n          />\n\n    </LinearLayout>\n\n    <com.google.android.material.floatingactionbutton.FloatingActionButton\n        android:id=\"@+id/ex_fab\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_alignParentEnd=\"true\"\n        android:layout_alignParentBottom=\"true\"\n        android:layout_marginEnd=\"16dp\"\n        android:layout_marginBottom=\"16dp\"\n        android:clickable=\"true\"\n        android:src=\"@drawable/ic_add\"\n        app:backgroundTint=\"@color/colorPrimary\"\n        app:elevation=\"2dp\"\n        app:fabCustomSize=\"@dimen/fab_bt_size\"\n        app:layout_behavior=\".widget.FabScrollBehavior\"\n        />\n\n  </RelativeLayout>\n\n</layout>"
  },
  {
    "path": "app/src/main/res/layout/activity_change_db.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n\n\n  <LinearLayout\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"match_parent\"\n      android:background=\"@color/background_color\"\n      android:orientation=\"vertical\">\n\n    <RelativeLayout\n        android:id=\"@+id/kpa_toolbar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"@drawable/ripple_white_selector\"\n        android:clickable=\"true\"\n        android:descendantFocusability=\"blocksDescendants\"\n        android:focusable=\"true\"\n        android:padding=\"16dp\">\n\n      <androidx.appcompat.widget.AppCompatImageView\n          android:id=\"@+id/app_icon\"\n          android:layout_width=\"70dp\"\n          android:layout_height=\"70dp\"\n          android:transitionName=\"@string/transition_app_icon\"\n          app:srcCompat=\"@mipmap/ic_launcher\" />\n\n      <com.lyy.keepassa.widget.BubbleTextView\n          android:id=\"@+id/db_name\"\n          android:layout_width=\"wrap_content\"\n          android:layout_height=\"wrap_content\"\n          android:layout_below=\"@+id/app_icon\"\n          android:layout_marginTop=\"8dp\"\n          android:gravity=\"center_vertical\"\n          android:singleLine=\"true\"\n          android:text=\"dbname\"\n          android:textColor=\"@color/text_black_color\"\n          android:textSize=\"@dimen/text_size_normal\"\n          android:textStyle=\"bold\"\n          android:transitionName=\"@string/transition_db_name\"\n          app:icon_size=\"24dp\"\n          app:right_icon=\"@drawable/ic_eco\" />\n\n      <TextView\n          android:id=\"@+id/db_version\"\n          android:layout_width=\"wrap_content\"\n          android:layout_height=\"wrap_content\"\n          android:layout_below=\"@+id/db_name\"\n          android:layout_alignStart=\"@+id/db_name\"\n          android:text=\"des\"\n          android:textColor=\"@color/text_gray_color\"\n          android:textSize=\"@dimen/text_size_small\"\n          android:transitionName=\"@string/transition_db_version\" />\n\n      <androidx.appcompat.widget.AppCompatImageView\n          android:id=\"@+id/arrow\"\n          android:layout_width=\"wrap_content\"\n          android:layout_height=\"wrap_content\"\n          android:layout_alignTop=\"@+id/db_name\"\n          android:layout_toEndOf=\"@+id/db_name\"\n          android:transitionName=\"@string/transition_db_little\"\n          app:srcCompat=\"@drawable/ic_arrow_down\" />\n\n      <androidx.appcompat.widget.AppCompatImageView\n          android:id=\"@+id/close\"\n          android:layout_width=\"30dp\"\n          android:layout_height=\"30dp\"\n          android:layout_alignParentEnd=\"true\"\n          android:background=\"@drawable/ripple_white_selector\"\n          android:clickable=\"true\"\n          android:focusable=\"true\"\n          app:srcCompat=\"@drawable/ic_primary_close\" />\n\n    </RelativeLayout>\n\n    <View\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"1dp\"\n        android:background=\"@color/line_color\" />\n\n\n    <TextView\n        android:id=\"@+id/change_db\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"@drawable/ripple_white_selector\"\n        android:clickable=\"true\"\n        android:drawableStart=\"@drawable/ic_change\"\n        android:drawablePadding=\"16dp\"\n        android:focusable=\"true\"\n        android:gravity=\"center_vertical\"\n        android:paddingStart=\"32dp\"\n        android:paddingTop=\"16dp\"\n        android:paddingBottom=\"16dp\"\n        android:text=\"@string/change_db\"\n        android:textColor=\"@color/text_black_color\"\n        android:textSize=\"@dimen/text_size_normal\" />\n\n    <TextView\n        android:id=\"@+id/app_setting\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"@drawable/ripple_white_selector\"\n        android:clickable=\"true\"\n        android:drawableStart=\"@drawable/ic_app\"\n        android:drawablePadding=\"16dp\"\n        android:focusable=\"true\"\n        android:gravity=\"center_vertical\"\n        android:paddingStart=\"32dp\"\n        android:paddingTop=\"16dp\"\n        android:paddingBottom=\"16dp\"\n        android:text=\"@string/app_setting\"\n        android:textColor=\"@color/text_black_color\"\n        android:textSize=\"@dimen/text_size_normal\" />\n\n    <TextView\n        android:id=\"@+id/change_setting\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"@drawable/ripple_white_selector\"\n        android:clickable=\"true\"\n        android:drawableStart=\"@drawable/ic_setting\"\n        android:drawablePadding=\"16dp\"\n        android:focusable=\"true\"\n        android:gravity=\"center_vertical\"\n        android:paddingStart=\"32dp\"\n        android:paddingTop=\"16dp\"\n        android:paddingBottom=\"16dp\"\n        android:text=\"@string/db_setting\"\n        android:textColor=\"@color/text_black_color\"\n        android:textSize=\"@dimen/text_size_normal\" />\n\n    <TextView\n        android:id=\"@+id/tvDonate\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"@drawable/ripple_white_selector\"\n        android:clickable=\"true\"\n        android:drawableStart=\"@drawable/ic_favorite_24px\"\n        android:drawablePadding=\"16dp\"\n        android:focusable=\"true\"\n        android:gravity=\"center_vertical\"\n        android:paddingStart=\"32dp\"\n        android:paddingTop=\"16dp\"\n        android:paddingBottom=\"16dp\"\n        android:text=\"@string/donate\"\n        android:textColor=\"@color/text_black_color\"\n        android:textSize=\"@dimen/text_size_normal\" />\n\n    <TextView\n        android:id=\"@+id/tvTranslate\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"@drawable/ripple_white_selector\"\n        android:clickable=\"true\"\n        android:drawableStart=\"@drawable/ic_baseline_g_translate_24\"\n        android:drawablePadding=\"16dp\"\n        android:focusable=\"true\"\n        android:gravity=\"center_vertical\"\n        android:paddingStart=\"32dp\"\n        android:paddingTop=\"16dp\"\n        android:paddingBottom=\"16dp\"\n        android:text=\"@string/translate_language\"\n        android:textColor=\"@color/text_black_color\"\n        android:textSize=\"@dimen/text_size_normal\"\n        android:visibility=\"gone\" />\n\n    <TextView\n        android:id=\"@+id/app_favorite\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"@drawable/ripple_white_selector\"\n        android:clickable=\"true\"\n        android:drawableStart=\"@drawable/ic_star_rate\"\n        android:drawablePadding=\"16dp\"\n        android:focusable=\"true\"\n        android:gravity=\"center_vertical\"\n        android:paddingStart=\"32dp\"\n        android:paddingTop=\"16dp\"\n        android:paddingBottom=\"16dp\"\n        android:text=\"@string/set_key_praise_value\"\n        android:textColor=\"@color/text_black_color\"\n        android:textSize=\"@dimen/text_size_normal\" />\n\n    <TextView\n        android:id=\"@+id/app_feedback\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"@drawable/ripple_white_selector\"\n        android:clickable=\"true\"\n        android:drawableStart=\"@drawable/ic_feedback_24px\"\n        android:drawablePadding=\"16dp\"\n        android:focusable=\"true\"\n        android:gravity=\"center_vertical\"\n        android:paddingStart=\"32dp\"\n        android:paddingTop=\"16dp\"\n        android:paddingBottom=\"16dp\"\n        android:text=\"@string/app_feedback\"\n        android:textColor=\"@color/text_black_color\"\n        android:textSize=\"@dimen/text_size_normal\" />\n\n    <TextView\n        android:id=\"@+id/debug\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"@drawable/ripple_white_selector\"\n        android:clickable=\"true\"\n        android:drawableStart=\"@drawable/ic_baseline_bug_report_24\"\n        android:drawablePadding=\"16dp\"\n        android:focusable=\"true\"\n        android:gravity=\"center_vertical\"\n        android:paddingStart=\"32dp\"\n        android:paddingTop=\"16dp\"\n        android:paddingBottom=\"16dp\"\n        android:text=\"@string/app_debug\"\n        android:textColor=\"@color/text_black_color\"\n        android:textSize=\"@dimen/text_size_normal\" />\n\n\n  </LinearLayout>\n</layout>"
  },
  {
    "path": "app/src/main/res/layout/activity_collection.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n\n  <androidx.constraintlayout.widget.ConstraintLayout\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"match_parent\"\n      android:background=\"@color/background_color\">\n\n    <include\n        layout=\"@layout/layout_action_bar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\" />\n\n    <androidx.recyclerview.widget.RecyclerView\n        android:id=\"@+id/rvList\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@+id/kpa_toolbar\" />\n\n    <com.lyy.keepassa.widget.EmptyDataFillView\n        android:id=\"@+id/emptyView\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        android:visibility=\"gone\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@+id/kpa_toolbar\" />\n\n  </androidx.constraintlayout.widget.ConstraintLayout>\n</layout>\n"
  },
  {
    "path": "app/src/main/res/layout/activity_create_db.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\">\n  <RelativeLayout\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"match_parent\"\n      android:background=\"@color/background_color\"\n      android:orientation=\"vertical\"\n      >\n\n    <include\n        layout=\"@layout/layout_action_bar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        />\n\n    <FrameLayout\n        android:id=\"@+id/content\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:layout_above=\"@+id/next\"\n        android:layout_below=\"@+id/kpa_toolbar\"\n        />\n\n    <TextView\n        android:id=\"@+id/next\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_alignParentRight=\"true\"\n        android:layout_alignParentBottom=\"true\"\n        android:layout_margin=\"16dp\"\n        android:text=\"@string/next\"\n        android:textColor=\"@color/selector_blue_gray_text_bg\"\n        android:textSize=\"@dimen/text_size_big\"\n        />\n\n    <TextView\n        android:id=\"@+id/up\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_alignParentLeft=\"true\"\n        android:layout_alignParentBottom=\"true\"\n        android:layout_margin=\"16dp\"\n        android:clickable=\"true\"\n        android:text=\"@string/up\"\n        android:textColor=\"@color/selector_blue_gray_text_bg\"\n        android:textSize=\"@dimen/text_size_big\"\n        android:visibility=\"gone\"\n        />\n\n  </RelativeLayout>\n</layout>\n\n\n"
  },
  {
    "path": "app/src/main/res/layout/activity_entry_detail_new.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    >\n\n  <LinearLayout\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"match_parent\"\n      android:orientation=\"vertical\"\n      >\n    <com.google.android.material.appbar.MaterialToolbar\n        android:id=\"@+id/topAppBar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"@color/white\"\n        app:navigationIcon=\"@drawable/ic_up\"\n        app:subtitleTextAppearance=\"@style/Toolbar.SubTitleText\"\n        app:theme=\"@style/ToolbarMenuTheme\"\n        app:titleTextAppearance=\"@style/Toolbar.TitleText\"\n        />\n\n\n    <androidx.coordinatorlayout.widget.CoordinatorLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:background=\"@color/background_color\"\n        >\n\n      <com.google.android.material.appbar.AppBarLayout\n          android:id=\"@+id/appBarLayout\"\n          android:layout_width=\"match_parent\"\n          android:layout_height=\"wrap_content\"\n          app:elevation=\"0dp\"\n          >\n\n        <androidx.constraintlayout.widget.ConstraintLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:background=\"@color/white\"\n            app:layout_scrollFlags=\"scroll|enterAlways|snap|snapMargins\"\n            >\n\n          <com.google.android.material.imageview.ShapeableImageView\n              android:id=\"@+id/ivIcon\"\n              android:layout_width=\"120dp\"\n              android:layout_height=\"120dp\"\n              android:layout_marginTop=\"20dp\"\n              android:scaleType=\"centerCrop\"\n              app:layout_constraintEnd_toEndOf=\"parent\"\n              app:layout_constraintStart_toStartOf=\"parent\"\n              app:layout_constraintTop_toTopOf=\"parent\"\n              app:shapeAppearance=\"@style/CircleStyle\"\n              />\n\n          <androidx.recyclerview.widget.RecyclerView\n              android:id=\"@+id/rvAppIcon\"\n              android:layout_width=\"100dp\"\n              android:layout_height=\"36dp\"\n              app:layout_constraintBottom_toBottomOf=\"@+id/ivIcon\"\n              app:layout_constraintEnd_toEndOf=\"@+id/ivIcon\"\n              app:layout_constraintStart_toEndOf=\"@+id/ivIcon\"\n              />\n\n          <androidx.appcompat.widget.AppCompatTextView\n              android:id=\"@+id/tvChar\"\n              android:layout_width=\"wrap_content\"\n              android:layout_height=\"wrap_content\"\n              android:textColor=\"@color/white\"\n              android:textSize=\"60sp\"\n              android:visibility=\"gone\"\n              app:layout_constraintBottom_toBottomOf=\"@+id/ivIcon\"\n              app:layout_constraintEnd_toEndOf=\"@id/ivIcon\"\n              app:layout_constraintStart_toStartOf=\"@id/ivIcon\"\n              app:layout_constraintTop_toTopOf=\"@id/ivIcon\"\n              tools:text=\"大\"\n              />\n\n          <androidx.appcompat.widget.AppCompatTextView\n              android:id=\"@+id/tvTitle\"\n              android:layout_width=\"wrap_content\"\n              android:layout_height=\"wrap_content\"\n              android:layout_marginTop=\"16dp\"\n              android:fontFamily=\"@font/roboto_bold\"\n              android:textColor=\"@color/text_black_color\"\n              android:textSize=\"@dimen/text_size_biggest\"\n              android:textStyle=\"bold\"\n              app:layout_constraintEnd_toEndOf=\"@+id/ivIcon\"\n              app:layout_constraintStart_toStartOf=\"@+id/ivIcon\"\n              app:layout_constraintTop_toBottomOf=\"@+id/ivIcon\"\n              tools:text=\"标题\"\n              />\n        </androidx.constraintlayout.widget.ConstraintLayout>\n\n        <androidx.constraintlayout.widget.ConstraintLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:background=\"@color/white\"\n            app:layout_scrollFlags=\"noScroll\"\n            >\n\n          <com.google.android.material.button.MaterialButton\n              android:id=\"@+id/btnUserName\"\n              android:layout_width=\"wrap_content\"\n              android:layout_height=\"wrap_content\"\n              android:text=\"@string/hint_input_user_name\"\n              app:icon=\"@drawable/ic_user\"\n              style=\"@style/KpaIconButton\"\n              />\n\n          <com.google.android.material.button.MaterialButton\n              android:id=\"@+id/btnUserPass\"\n              android:layout_width=\"wrap_content\"\n              android:layout_height=\"wrap_content\"\n              android:text=\"@string/password\"\n              app:icon=\"@drawable/ic_password\"\n              style=\"@style/KpaIconButton\"\n              />\n\n\n          <com.google.android.material.button.MaterialButton\n              android:id=\"@+id/btnTotp\"\n              android:layout_width=\"wrap_content\"\n              android:layout_height=\"wrap_content\"\n              android:text=\"@string/totp\"\n              app:icon=\"@drawable/ic_token_blue\"\n              style=\"@style/KpaIconButton\"\n              />\n\n\n          <androidx.constraintlayout.helper.widget.Flow\n              android:layout_width=\"match_parent\"\n              android:layout_height=\"wrap_content\"\n              android:layout_marginTop=\"20dp\"\n              app:layout_constraintTop_toTopOf=\"parent\"\n              app:constraint_referenced_ids=\"btnUserName, btnUserPass, btnTotp\"\n              />\n\n\n        </androidx.constraintlayout.widget.ConstraintLayout>\n\n\n      </com.google.android.material.appbar.AppBarLayout>\n\n\n      <androidx.core.widget.NestedScrollView\n          android:layout_width=\"match_parent\"\n          android:layout_height=\"match_parent\"\n          android:layout_marginTop=\"12dp\"\n          android:fillViewport=\"true\"\n          app:layout_behavior=\"@string/appbar_scrolling_view_behavior\"\n          >\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:layout_marginEnd=\"16dp\"\n            android:layout_marginStart=\"16dp\"\n            android:orientation=\"vertical\"\n            >\n\n          <com.lyy.keepassa.view.detail.card.EntryBaseInfoCard\n              android:id=\"@+id/cardBaseInfo\"\n              android:layout_width=\"match_parent\"\n              android:layout_height=\"wrap_content\"\n              app:cardElevation=\"0dp\"\n              style=\"?attr/materialCardViewElevatedStyle\"\n              />\n\n          <com.lyy.keepassa.view.detail.card.EntryNoteCard\n              android:id=\"@+id/cardNote\"\n              android:layout_width=\"match_parent\"\n              android:layout_height=\"wrap_content\"\n              android:layout_marginTop=\"12dp\"\n              app:cardElevation=\"0dp\"\n              style=\"?attr/materialCardViewElevatedStyle\"\n              />\n\n          <com.lyy.keepassa.view.detail.card.EntryTagCard\n              android:id=\"@+id/cardTag\"\n              android:layout_width=\"match_parent\"\n              android:layout_height=\"wrap_content\"\n              android:layout_marginTop=\"12dp\"\n              app:cardElevation=\"0dp\"\n              style=\"?attr/materialCardViewElevatedStyle\"\n              />\n\n          <com.lyy.keepassa.view.detail.card.EntryStrCard\n              android:id=\"@+id/cardStr\"\n              android:layout_width=\"match_parent\"\n              android:layout_height=\"wrap_content\"\n              android:layout_marginTop=\"12dp\"\n              app:cardElevation=\"0dp\"\n              style=\"?attr/materialCardViewElevatedStyle\"\n              />\n\n          <com.lyy.keepassa.view.detail.card.EntryFileCard\n              android:id=\"@+id/cardAtta\"\n              android:layout_width=\"match_parent\"\n              android:layout_height=\"wrap_content\"\n              android:layout_marginTop=\"12dp\"\n              app:cardElevation=\"0dp\"\n              style=\"?attr/materialCardViewElevatedStyle\"\n              />\n\n        </LinearLayout>\n\n\n      </androidx.core.widget.NestedScrollView>\n\n    </androidx.coordinatorlayout.widget.CoordinatorLayout>\n  </LinearLayout>\n</layout>"
  },
  {
    "path": "app/src/main/res/layout/activity_entry_edit_new.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    >\n  <LinearLayout\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"match_parent\"\n      android:background=\"@color/white\"\n      android:orientation=\"vertical\"\n      >\n    <com.google.android.material.appbar.MaterialToolbar\n        android:id=\"@+id/topAppBar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"@color/white\"\n        app:navigationIcon=\"@drawable/ic_up\"\n        app:subtitleTextAppearance=\"@style/Toolbar.SubTitleText\"\n        app:theme=\"@style/ToolbarMenuTheme\"\n        app:titleTextAppearance=\"@style/Toolbar.TitleText\"\n        />\n\n    <androidx.coordinatorlayout.widget.CoordinatorLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:background=\"@color/background_color\"\n        >\n      <com.google.android.material.appbar.AppBarLayout\n          android:id=\"@+id/appBarLayout\"\n          android:layout_width=\"match_parent\"\n          android:layout_height=\"wrap_content\"\n          android:background=\"@color/background_color\"\n          app:elevation=\"0dp\"\n          >\n\n        <androidx.constraintlayout.widget.ConstraintLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            app:layout_scrollFlags=\"scroll|enterAlways|snap|snapMargins\"\n            >\n\n          <androidx.appcompat.widget.AppCompatImageView\n              android:id=\"@+id/ivHeadBg\"\n              android:layout_width=\"match_parent\"\n              android:layout_height=\"180dp\"\n              app:layout_constraintStart_toStartOf=\"parent\"\n              app:layout_constraintTop_toTopOf=\"parent\"\n              />\n\n          <com.google.android.material.imageview.ShapeableImageView\n              android:id=\"@+id/ivIconBg\"\n              android:layout_width=\"120dp\"\n              android:layout_height=\"120dp\"\n              android:background=\"@color/color_444E85DB\"\n              app:layout_constraintBottom_toBottomOf=\"@id/ivHeadBg\"\n              app:layout_constraintEnd_toEndOf=\"@id/ivHeadBg\"\n              app:layout_constraintStart_toStartOf=\"@id/ivHeadBg\"\n              app:layout_constraintTop_toTopOf=\"@id/ivHeadBg\"\n              app:shapeAppearance=\"@style/CircleStyle\"\n              />\n\n          <androidx.appcompat.widget.AppCompatImageView\n              android:id=\"@+id/ivIcon\"\n              android:layout_width=\"60dp\"\n              android:layout_height=\"60dp\"\n              android:src=\"@drawable/ic_baseline_photo_camera_24\"\n              app:layout_constraintBottom_toBottomOf=\"@+id/ivIconBg\"\n              app:layout_constraintEnd_toEndOf=\"@id/ivIconBg\"\n              app:layout_constraintStart_toStartOf=\"@id/ivIconBg\"\n              app:layout_constraintTop_toTopOf=\"@+id/ivIconBg\"\n              />\n\n        </androidx.constraintlayout.widget.ConstraintLayout>\n\n      </com.google.android.material.appbar.AppBarLayout>\n\n      <androidx.core.widget.NestedScrollView\n          android:layout_width=\"match_parent\"\n          android:layout_height=\"match_parent\"\n          android:layout_marginTop=\"12dp\"\n          android:fillViewport=\"true\"\n          app:layout_behavior=\"@string/appbar_scrolling_view_behavior\"\n          >\n\n        <androidx.constraintlayout.widget.ConstraintLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginEnd=\"16dp\"\n            android:layout_marginStart=\"16dp\"\n            >\n\n          <com.google.android.material.textfield.TextInputLayout\n              android:id=\"@+id/tlTitle\"\n              android:layout_width=\"match_parent\"\n              android:layout_height=\"wrap_content\"\n              app:endIconMode=\"clear_text\"\n              app:startIconDrawable=\"@drawable/ic_title_24px\"\n              >\n\n            <com.google.android.material.textfield.TextInputEditText\n                android:id=\"@+id/title\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:hint=\"@string/hint_input_title\"\n                android:imeOptions=\"actionNext\"\n                tools:text=\"标题\"\n                style=\"@style/KpaEditTextStyleNew\"\n                />\n\n          </com.google.android.material.textfield.TextInputLayout>\n\n\n          <com.google.android.material.textfield.TextInputLayout\n              android:id=\"@+id/tlUser\"\n              android:layout_width=\"match_parent\"\n              android:layout_height=\"wrap_content\"\n              app:startIconDrawable=\"@drawable/ic_user\"\n              app:startIconTint=\"@color/color_icon_grey\"\n              style=\"@style/Widget.Material3.TextInputLayout.OutlinedBox.ExposedDropdownMenu\"\n              >\n\n            <com.google.android.material.textfield.MaterialAutoCompleteTextView\n                android:id=\"@+id/edUser\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:hint=\"@string/hint_input_user_name\"\n                android:imeOptions=\"actionNext\"\n                app:backgroundTint=\"?attr/colorPrimary\"\n                tools:text=\"用户名\"\n                style=\"@style/KpaEditTextStyleNew\"\n                />\n\n          </com.google.android.material.textfield.TextInputLayout>\n\n          <androidx.appcompat.widget.AppCompatImageView\n              android:id=\"@+id/ivGeneratePw\"\n              android:layout_width=\"36dp\"\n              android:layout_height=\"36dp\"\n              android:layout_marginTop=\"14dp\"\n              app:layout_constraintEnd_toEndOf=\"parent\"\n              app:layout_constraintTop_toTopOf=\"@+id/tlPass\"\n              app:srcCompat=\"@drawable/ic_baseline_casino_24\"\n              />\n\n          <com.google.android.material.textfield.TextInputLayout\n              android:id=\"@+id/tlPass\"\n              android:layout_width=\"330dp\"\n              android:layout_height=\"wrap_content\"\n              app:layout_constraintStart_toStartOf=\"parent\"\n              app:counterOverflowTextAppearance=\"@style/InputEditTextErrorStyle\"\n              app:counterTextAppearance=\"@style/InputEditTextOverAppearance\"\n              app:errorEnabled=\"true\"\n              app:errorTextAppearance=\"@style/InputEditTextErrorStyle\"\n              app:helperText=\"@string/helper_create_pass\"\n              app:helperTextTextColor=\"@color/text_gray_color\"\n              app:passwordToggleEnabled=\"true\"\n              app:endIconTintMode=\"multiply\"\n              app:endIconTint=\"@color/color_icon_grey\"\n              app:startIconDrawable=\"@drawable/ic_password\"\n              app:startIconTint=\"@color/color_icon_grey\"\n              >\n\n            <com.google.android.material.textfield.TextInputEditText\n                android:id=\"@+id/edPassword\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:hint=\"@string/password\"\n                android:imeOptions=\"actionNext\"\n                android:inputType=\"textPassword\"\n                tools:text=\"1234\"\n                style=\"@style/KpaEditTextStyleNew\"\n                />\n\n          </com.google.android.material.textfield.TextInputLayout>\n\n          <com.google.android.material.textfield.TextInputLayout\n              android:id=\"@+id/tlConfirm\"\n              android:layout_width=\"match_parent\"\n              android:layout_height=\"wrap_content\"\n              app:helperTextTextColor=\"@color/text_gray_color\"\n              app:hintTextAppearance=\"@style/InputEditTextHintStyle\"\n              app:startIconDrawable=\"@drawable/ic_baseline_confirmation_number_24\"\n              app:startIconTint=\"@color/color_icon_grey\"\n              >\n\n            <com.google.android.material.textfield.TextInputEditText\n                android:id=\"@+id/tvConfirm\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:hint=\"@string/hint_enter_password\"\n                android:imeOptions=\"actionDone\"\n                android:inputType=\"textPassword\"\n                tools:text=\"标题\"\n                style=\"@style/KpaEditTextStyleNew\"\n                />\n          </com.google.android.material.textfield.TextInputLayout>\n\n          <com.google.android.material.textfield.TextInputLayout\n              android:id=\"@+id/tlUrl\"\n              android:layout_width=\"match_parent\"\n              android:layout_height=\"wrap_content\"\n              android:visibility=\"gone\"\n              tools:visibility=\"visible\"\n              app:startIconDrawable=\"@drawable/ic_baseline_link_24\"\n              app:startIconTint=\"@color/color_icon_grey\"\n              >\n\n            <com.google.android.material.textfield.TextInputEditText\n                android:id=\"@+id/edUrl\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:hint=\"@string/hint_input_url\"\n                android:imeOptions=\"actionNext\"\n                style=\"@style/KpaEditTextStyleNew\"\n                />\n\n          </com.google.android.material.textfield.TextInputLayout>\n\n          <com.google.android.material.textfield.TextInputLayout\n              android:id=\"@+id/tlNote\"\n              android:layout_width=\"match_parent\"\n              android:layout_height=\"wrap_content\"\n              android:visibility=\"gone\"\n              tools:visibility=\"visible\"\n              app:startIconDrawable=\"@drawable/ic_notice\"\n              app:startIconTint=\"@color/color_icon_grey\"\n              >\n\n            <com.google.android.material.textfield.TextInputEditText\n                android:id=\"@+id/edNote\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:hint=\"@string/notice\"\n                android:imeOptions=\"actionNext\"\n                tools:text=\"Side sheets that are modal on mobile, due to limited screen width, can become standard side sheets on tablet and desktop. The reverse is also true.\"\n                style=\"@style/KpaEditTextMultiLineStyleNew\"\n                />\n\n          </com.google.android.material.textfield.TextInputLayout>\n\n\n          <com.google.android.material.textfield.TextInputLayout\n              android:id=\"@+id/tlOTP\"\n              android:layout_width=\"330dp\"\n              android:layout_height=\"wrap_content\"\n              app:layout_constraintStart_toStartOf=\"parent\"\n              app:counterOverflowTextAppearance=\"@style/InputEditTextErrorStyle\"\n              app:counterTextAppearance=\"@style/InputEditTextOverAppearance\"\n              app:helperTextTextColor=\"@color/text_gray_color\"\n              app:passwordToggleEnabled=\"true\"\n              app:endIconTintMode=\"multiply\"\n              app:endIconTint=\"@color/color_icon_grey\"\n              app:startIconDrawable=\"@drawable/ic_password\"\n              app:startIconTint=\"@color/color_icon_grey\"\n              >\n\n            <com.google.android.material.textfield.TextInputEditText\n                android:id=\"@+id/edOtp\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:cursorVisible=\"false\"\n                android:focusable=\"false\"\n                android:focusableInTouchMode=\"false\"\n                android:hint=\"@string/totp\"\n                android:imeOptions=\"actionNext\"\n                android:inputType=\"textPassword\"\n                tools:text=\"1234\"\n                style=\"@style/KpaEditTextStyleNew\"\n                />\n\n          </com.google.android.material.textfield.TextInputLayout>\n\n          <com.lyy.keepassa.widget.pb.RoundProgressBarWidthNumber\n              android:id=\"@+id/pbRound\"\n              android:layout_width=\"36dp\"\n              android:layout_height=\"36dp\"\n              app:layout_constraintBottom_toBottomOf=\"@+id/tlOTP\"\n              app:layout_constraintEnd_toEndOf=\"parent\"\n              app:layout_constraintTop_toTopOf=\"@+id/tlOTP\"\n              app:radius=\"8dp\"\n              app:progress_text_color=\"@color/text_blue_color\"\n              app:progress_text_size=\"12sp\"\n              app:progress_show_percent=\"false\"\n              app:progress_unreached_bar_height=\"1dp\"\n              />\n\n          <androidx.constraintlayout.widget.Group\n              android:id=\"@+id/groupOtp\"\n              android:layout_width=\"wrap_content\"\n              android:layout_height=\"wrap_content\"\n              android:visibility=\"gone\"\n              tools:visibility=\"visible\"\n              app:constraint_referenced_ids=\"tlOTP, pbRound\"\n              />\n\n          <com.google.android.material.textfield.TextInputLayout\n              android:id=\"@+id/tlTag\"\n              android:layout_width=\"match_parent\"\n              android:layout_height=\"wrap_content\"\n              android:visibility=\"gone\"\n              tools:visibility=\"visible\"\n              app:startIconDrawable=\"@drawable/ic_tag\"\n              app:startIconTint=\"@color/color_icon_grey\"\n              app:endIconDrawable=\"@drawable/ic_baseline_arrow_drop_down_24\"\n              >\n\n            <com.google.android.material.textfield.TextInputEditText\n                android:id=\"@+id/edTag\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:cursorVisible=\"false\"\n                android:focusable=\"false\"\n                android:focusableInTouchMode=\"false\"\n                android:hint=\"@string/tag\"\n                android:imeOptions=\"actionNext\"\n                android:text=\" \"\n                style=\"@style/KpaEditTextStyleNew\"\n                />\n\n          </com.google.android.material.textfield.TextInputLayout>\n\n          <com.google.android.material.textfield.TextInputLayout\n              android:id=\"@+id/tlLoseTime\"\n              android:layout_width=\"match_parent\"\n              android:layout_height=\"wrap_content\"\n              android:visibility=\"gone\"\n              tools:visibility=\"visible\"\n              app:startIconDrawable=\"@drawable/ic_lose_time\"\n              app:startIconTint=\"@color/color_icon_grey\"\n              app:endIconDrawable=\"@drawable/ic_baseline_arrow_drop_down_24\"\n              >\n\n            <com.google.android.material.textfield.TextInputEditText\n                android:id=\"@+id/edLoseTime\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:cursorVisible=\"false\"\n                android:focusable=\"false\"\n                android:focusableInTouchMode=\"false\"\n                android:hint=\"@string/lose_time\"\n                android:imeOptions=\"actionNext\"\n                android:text=\" \"\n                style=\"@style/KpaEditTextStyleNew\"\n                />\n\n          </com.google.android.material.textfield.TextInputLayout>\n\n          <com.lyy.keepassa.view.create.entry.CreateStrCard\n              android:id=\"@+id/cardStr\"\n              android:layout_width=\"match_parent\"\n              android:layout_height=\"wrap_content\"\n              android:visibility=\"gone\"\n              tools:visibility=\"visible\"\n              />\n\n          <com.lyy.keepassa.view.create.entry.CreateFileCard\n              android:id=\"@+id/cardFile\"\n              android:layout_width=\"match_parent\"\n              android:layout_height=\"wrap_content\"\n              android:visibility=\"gone\"\n              tools:visibility=\"visible\"\n              />\n\n          <androidx.constraintlayout.helper.widget.Flow\n              android:layout_width=\"wrap_content\"\n              android:layout_height=\"wrap_content\"\n              android:orientation=\"vertical\"\n              app:layout_constraintStart_toStartOf=\"parent\"\n              app:layout_constraintTop_toTopOf=\"parent\"\n              app:flow_verticalGap=\"12dp\"\n              app:constraint_referenced_ids=\"tlTitle, tlUser, tlPass, tlConfirm, tlUrl, tlNote, tlOTP, tlTag, tlLoseTime, cardStr, cardFile\"\n              />\n\n        </androidx.constraintlayout.widget.ConstraintLayout>\n\n\n      </androidx.core.widget.NestedScrollView>\n\n      <com.google.android.material.floatingactionbutton.FloatingActionButton\n          android:id=\"@+id/btnAddMore\"\n          android:layout_width=\"wrap_content\"\n          android:layout_height=\"wrap_content\"\n          android:layout_gravity=\"end|bottom\"\n          android:layout_margin=\"16dp\"\n          android:contentDescription=\"@string/add_more\"\n          app:backgroundTint=\"@color/colorPrimary\"\n          app:srcCompat=\"@drawable/ic_add_24px\"\n          app:rippleColor=\"@color/colorPrimary\"\n          />\n\n    </androidx.coordinatorlayout.widget.CoordinatorLayout>\n  </LinearLayout>\n</layout>\n"
  },
  {
    "path": "app/src/main/res/layout/activity_fingerprint.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n\n  <RelativeLayout\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"match_parent\"\n      android:background=\"@color/background_color\"\n      android:orientation=\"vertical\">\n\n    <include layout=\"@layout/layout_action_bar\" />\n\n    <FrameLayout\n        android:id=\"@+id/flSwitch\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"60dp\"\n        android:layout_below=\"@+id/kpa_toolbar\">\n\n      <View\n          android:id=\"@+id/vBg\"\n          android:layout_width=\"match_parent\"\n          android:layout_height=\"match_parent\"\n          android:background=\"@color/grey600\" />\n\n      <TextView\n          android:id=\"@+id/closeHint\"\n          android:layout_width=\"wrap_content\"\n          android:layout_height=\"wrap_content\"\n          android:layout_gravity=\"start|center_vertical\"\n          android:layout_marginStart=\"72dp\"\n          android:text=\"@string/disable\"\n          android:textColor=\"@color/white\"\n          android:textSize=\"@dimen/text_size_big\" />\n\n      <androidx.appcompat.widget.SwitchCompat\n          android:id=\"@+id/swBt\"\n          android:layout_width=\"wrap_content\"\n          android:layout_height=\"wrap_content\"\n          android:layout_gravity=\"end|center_vertical\"\n          android:layout_marginEnd=\"16dp\"\n          app:thumbTint=\"@color/white\"\n          app:trackTint=\"#44F1F1F1\" />\n    </FrameLayout>\n\n\n    <FrameLayout\n        android:id=\"@+id/flContent\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:layout_below=\"@+id/flSwitch\" />\n\n\n  </RelativeLayout>\n</layout>"
  },
  {
    "path": "app/src/main/res/layout/activity_generate_pass.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    >\n\n  <RelativeLayout\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"match_parent\"\n      android:background=\"@color/background_color\"\n      >\n\n    <include layout=\"@layout/layout_action_bar\" />\n\n    <com.google.android.material.textfield.TextInputEditText\n        android:id=\"@+id/pass_edit\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_above=\"@+id/hint_len\"\n        android:layout_marginStart=\"16dp\"\n        android:layout_marginEnd=\"16dp\"\n        android:layout_marginBottom=\"24dp\"\n        android:background=\"@color/transparent\"\n        android:gravity=\"center\"\n        android:hint=\"@string/hint_pass_null\"\n        android:textColor=\"@color/text_black_color\"\n        android:textSize=\"@dimen/text_size_biggest\"\n        />\n\n    <TextView\n        android:id=\"@+id/hint_len\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_above=\"@+id/pass_len_layout\"\n        android:layout_marginStart=\"16dp\"\n        android:text=\"@string/len\"\n        android:textColor=\"@color/text_gray_color\"\n        android:textSize=\"@dimen/text_size_normal\"\n        />\n\n    <LinearLayout\n        android:id=\"@+id/pass_len_layout\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_above=\"@+id/case_layout\"\n        android:layout_marginStart=\"16dp\"\n        android:layout_marginTop=\"4dp\"\n        android:layout_marginEnd=\"16dp\"\n        android:orientation=\"horizontal\"\n        >\n\n      <EditText\n          android:id=\"@+id/pass_len\"\n          android:layout_width=\"60dp\"\n          android:layout_height=\"wrap_content\"\n          android:layout_gravity=\"center_vertical\"\n          android:gravity=\"center_horizontal\"\n          android:inputType=\"number\"\n          />\n\n      <com.lyy.keepassa.widget.discreteSeekBar.DiscreteSeekBar\n          android:id=\"@+id/pass_len_progress\"\n          android:layout_width=\"match_parent\"\n          android:layout_height=\"wrap_content\"\n          android:layout_centerVertical=\"true\"\n          android:layout_gravity=\"center_vertical\"\n          app:dsb_max=\"16\"\n          app:dsb_min=\"3\"\n          app:dsb_mirrorForRtl=\"true\"\n          app:dsb_value=\"6\"\n          />\n    </LinearLayout>\n\n    <LinearLayout\n        android:id=\"@+id/case_layout\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_above=\"@+id/cancel\"\n        android:layout_marginStart=\"16dp\"\n        android:layout_marginTop=\"16dp\"\n        android:layout_marginEnd=\"16dp\"\n        android:layout_marginBottom=\"32dp\"\n        android:orientation=\"vertical\"\n        >\n\n      <CheckBox\n          android:id=\"@+id/upper\"\n          android:layout_width=\"wrap_content\"\n          android:layout_height=\"wrap_content\"\n          android:text=\"@string/upper\"\n          android:textColor=\"@color/text_black_color\"\n          android:textSize=\"@dimen/text_size_normal\"\n          />\n\n      <CheckBox\n          android:id=\"@+id/lower\"\n          android:layout_width=\"wrap_content\"\n          android:layout_height=\"wrap_content\"\n          android:layout_marginTop=\"4dp\"\n          android:text=\"@string/lower\"\n          android:textColor=\"@color/text_black_color\"\n          android:textSize=\"@dimen/text_size_normal\"\n          />\n\n      <CheckBox\n          android:id=\"@+id/numer\"\n          android:layout_width=\"wrap_content\"\n          android:layout_height=\"wrap_content\"\n          android:layout_marginTop=\"4dp\"\n          android:text=\"@string/numer\"\n          android:textColor=\"@color/text_black_color\"\n          android:textSize=\"@dimen/text_size_normal\"\n          />\n\n      <CheckBox\n          android:id=\"@+id/minus\"\n          android:layout_width=\"wrap_content\"\n          android:layout_height=\"wrap_content\"\n          android:layout_marginTop=\"4dp\"\n          android:text=\"@string/minus\"\n          android:textColor=\"@color/text_black_color\"\n          android:textSize=\"@dimen/text_size_normal\"\n          />\n\n      <CheckBox\n          android:id=\"@+id/underline\"\n          android:layout_width=\"wrap_content\"\n          android:layout_height=\"wrap_content\"\n          android:layout_marginTop=\"4dp\"\n          android:text=\"@string/underline\"\n          android:textColor=\"@color/text_black_color\"\n          android:textSize=\"@dimen/text_size_normal\"\n          />\n\n      <CheckBox\n          android:id=\"@+id/space\"\n          android:layout_width=\"wrap_content\"\n          android:layout_height=\"wrap_content\"\n          android:layout_marginTop=\"4dp\"\n          android:text=\"@string/space\"\n          android:textColor=\"@color/text_black_color\"\n          android:textSize=\"@dimen/text_size_normal\"\n          />\n\n      <CheckBox\n          android:id=\"@+id/special\"\n          android:layout_width=\"wrap_content\"\n          android:layout_height=\"wrap_content\"\n          android:layout_marginTop=\"4dp\"\n          android:text=\"@string/special\"\n          android:textColor=\"@color/text_black_color\"\n          android:textSize=\"@dimen/text_size_normal\"\n          />\n\n      <CheckBox\n          android:id=\"@+id/bracket\"\n          android:layout_width=\"wrap_content\"\n          android:layout_height=\"wrap_content\"\n          android:layout_marginTop=\"4dp\"\n          android:text=\"@string/bracket\"\n          android:textColor=\"@color/text_black_color\"\n          android:textSize=\"@dimen/text_size_normal\"\n          />\n\n    </LinearLayout>\n\n\n    <TextView\n        android:id=\"@+id/cancel\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_alignParentBottom=\"true\"\n        android:layout_marginStart=\"16dp\"\n        android:layout_marginBottom=\"16dp\"\n        android:background=\"@drawable/ripple_white_selector\"\n        android:drawableLeft=\"@drawable/ic_close\"\n        android:gravity=\"center\"\n        android:text=\"@string/cancel\"\n        android:textColor=\"@color/text_blue_color\"\n        android:textSize=\"@dimen/text_size_normal\"\n        />\n\n\n    <TextView\n        android:id=\"@+id/enter\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_alignParentEnd=\"true\"\n        android:layout_alignParentBottom=\"true\"\n        android:layout_marginEnd=\"16dp\"\n        android:layout_marginBottom=\"16dp\"\n        android:background=\"@drawable/ripple_white_selector\"\n        android:drawableLeft=\"@drawable/ic_done\"\n        android:gravity=\"center\"\n        android:text=\"@string/enter\"\n        android:textColor=\"@color/text_blue_color\"\n        android:textSize=\"@dimen/text_size_normal\"\n        />\n\n  </RelativeLayout>\n</layout>"
  },
  {
    "path": "app/src/main/res/layout/activity_generate_pass_new.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    >\n\n  <LinearLayout\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"match_parent\"\n      android:background=\"@color/background_color\"\n      android:orientation=\"vertical\"\n      >\n\n    <include layout=\"@layout/layout_action_bar\" />\n\n    <androidx.core.widget.NestedScrollView\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:paddingEnd=\"16dp\"\n        android:paddingStart=\"16dp\"\n        >\n\n      <androidx.constraintlayout.widget.ConstraintLayout\n          android:layout_width=\"match_parent\"\n          android:layout_height=\"match_parent\"\n          >\n\n        <com.google.android.material.textfield.TextInputLayout\n            android:id=\"@+id/tlPass\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"12dp\"\n            app:layout_constraintEnd_toStartOf=\"@+id/ivCopy\"\n            app:layout_constraintStart_toStartOf=\"parent\"\n            app:layout_constraintTop_toTopOf=\"parent\"\n            >\n          <com.google.android.material.textfield.TextInputEditText\n              android:id=\"@+id/edPass\"\n              android:layout_width=\"match_parent\"\n              android:layout_height=\"wrap_content\"\n              android:layout_marginEnd=\"12dp\"\n              android:hint=\"@string/hint_pass_null\"\n              android:imeOptions=\"actionNext\"\n              android:textColor=\"@color/text_black_color\"\n              android:textSize=\"@dimen/text_size_normal\"\n              style=\"@style/KpaEditTextStyleNew\"\n              />\n        </com.google.android.material.textfield.TextInputLayout>\n\n        <androidx.appcompat.widget.AppCompatImageView\n            android:id=\"@+id/ivCopy\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginEnd=\"12dp\"\n            android:background=\"@drawable/ripple_white_selector\"\n            app:layout_constraintBottom_toBottomOf=\"@+id/tlPass\"\n            app:layout_constraintEnd_toStartOf=\"@+id/ivRefresh\"\n            app:layout_constraintTop_toTopOf=\"@+id/tlPass\"\n            app:srcCompat=\"@drawable/ic_baseline_content_copy_24\"\n            />\n\n        <androidx.appcompat.widget.AppCompatImageView\n            android:id=\"@+id/ivRefresh\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:background=\"@drawable/ripple_white_selector\"\n            app:layout_constraintBottom_toBottomOf=\"@+id/tlPass\"\n            app:layout_constraintEnd_toEndOf=\"parent\"\n            app:layout_constraintTop_toTopOf=\"@+id/tlPass\"\n            app:srcCompat=\"@drawable/ic_refresh_black_24dp\"\n            />\n\n        <androidx.appcompat.widget.AppCompatTextView\n            android:id=\"@+id/tvOptions\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"16dp\"\n            android:text=\"@string/options\"\n            android:textColor=\"@color/text_black_color\"\n            android:textSize=\"@dimen/text_size_normal\"\n            app:layout_constraintStart_toStartOf=\"parent\"\n            app:layout_constraintTop_toBottomOf=\"@+id/tlPass\"\n            />\n        <com.google.android.material.textfield.TextInputLayout\n            android:id=\"@+id/tlPassLen\"\n            android:layout_width=\"60dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"12dp\"\n            app:layout_constraintStart_toStartOf=\"parent\"\n            app:layout_constraintTop_toBottomOf=\"@+id/tvOptions\"\n            >\n\n          <com.google.android.material.textfield.TextInputEditText\n              android:id=\"@+id/edPassLen\"\n              android:layout_width=\"60dp\"\n              android:layout_height=\"match_parent\"\n              android:layout_gravity=\"center_vertical\"\n              android:gravity=\"center_horizontal\"\n              android:inputType=\"number\"\n              android:text=\"16\"\n              />\n        </com.google.android.material.textfield.TextInputLayout>\n\n\n        <com.google.android.material.slider.Slider\n            android:id=\"@+id/slider\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:value=\"16\"\n            android:valueFrom=\"0\"\n            android:valueTo=\"128\"\n            app:layout_constraintBottom_toBottomOf=\"@+id/tlPassLen\"\n            app:layout_constraintEnd_toEndOf=\"parent\"\n            app:layout_constraintStart_toEndOf=\"@+id/tlPassLen\"\n            app:layout_constraintTop_toBottomOf=\"@+id/tvOptions\"\n            app:layout_constraintTop_toTopOf=\"@+id/tlPassLen\"\n            />\n\n        <androidx.appcompat.widget.AppCompatTextView\n            android:id=\"@+id/tvUAZ\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:text=\"A-Z\"\n            android:textColor=\"@color/text_black_color\"\n            android:textSize=\"@dimen/text_size_biggest\"\n            app:layout_constraintBottom_toBottomOf=\"@+id/scUAZ\"\n            app:layout_constraintStart_toStartOf=\"parent\"\n            app:layout_constraintTop_toTopOf=\"@+id/scUAZ\"\n            />\n\n        <com.google.android.material.materialswitch.MaterialSwitch\n            android:id=\"@+id/scUAZ\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"12dp\"\n            android:checked=\"true\"\n            app:layout_constraintEnd_toEndOf=\"parent\"\n            app:layout_constraintTop_toBottomOf=\"@+id/slider\"\n            />\n\n        <androidx.appcompat.widget.AppCompatTextView\n            android:id=\"@+id/tvLAZ\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:text=\"a-z\"\n            android:textColor=\"@color/text_black_color\"\n            android:textSize=\"@dimen/text_size_biggest\"\n            app:layout_constraintBottom_toBottomOf=\"@+id/scLAZ\"\n            app:layout_constraintStart_toStartOf=\"parent\"\n            app:layout_constraintTop_toTopOf=\"@+id/scLAZ\"\n            />\n\n        <com.google.android.material.materialswitch.MaterialSwitch\n            android:id=\"@+id/scLAZ\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"12dp\"\n            android:checked=\"true\"\n            app:layout_constraintEnd_toEndOf=\"parent\"\n            app:layout_constraintTop_toBottomOf=\"@+id/scUAZ\"\n            />\n\n        <androidx.appcompat.widget.AppCompatTextView\n            android:id=\"@+id/tvNum\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:text=\"0-9\"\n            android:textColor=\"@color/text_black_color\"\n            android:textSize=\"@dimen/text_size_biggest\"\n            app:layout_constraintBottom_toBottomOf=\"@+id/scNum\"\n            app:layout_constraintStart_toStartOf=\"parent\"\n            app:layout_constraintTop_toTopOf=\"@+id/scNum\"\n            />\n\n        <com.google.android.material.materialswitch.MaterialSwitch\n            android:id=\"@+id/scNum\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"12dp\"\n            android:checked=\"true\"\n            app:layout_constraintEnd_toEndOf=\"parent\"\n            app:layout_constraintTop_toBottomOf=\"@+id/scLAZ\"\n            />\n\n        <androidx.appcompat.widget.AppCompatTextView\n            android:id=\"@+id/tvCh\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:text=\"`~!@#$%^*-_+...\"\n            android:textColor=\"@color/text_black_color\"\n            android:textSize=\"@dimen/text_size_biggest\"\n            app:layout_constraintBottom_toBottomOf=\"@+id/scCh\"\n            app:layout_constraintStart_toStartOf=\"parent\"\n            app:layout_constraintTop_toTopOf=\"@+id/scCh\"\n            />\n\n        <com.google.android.material.materialswitch.MaterialSwitch\n            android:id=\"@+id/scCh\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"12dp\"\n            android:checked=\"true\"\n            app:layout_constraintEnd_toEndOf=\"parent\"\n            app:layout_constraintTop_toBottomOf=\"@+id/scNum\"\n            />\n\n        <androidx.appcompat.widget.AppCompatTextView\n            android:id=\"@+id/tvBracketChar\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:text=\"@string/hint_bracket_char\"\n            android:textColor=\"@color/text_black_color\"\n            android:textSize=\"@dimen/text_size_biggest\"\n            app:layout_constraintBottom_toBottomOf=\"@+id/scBracketChar\"\n            app:layout_constraintStart_toStartOf=\"parent\"\n            app:layout_constraintTop_toTopOf=\"@+id/scBracketChar\"\n            />\n\n        <com.google.android.material.materialswitch.MaterialSwitch\n            android:id=\"@+id/scBracketChar\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"12dp\"\n            app:layout_constraintEnd_toEndOf=\"parent\"\n            app:layout_constraintTop_toBottomOf=\"@+id/scCh\"\n            />\n\n        <androidx.appcompat.widget.AppCompatTextView\n            android:id=\"@+id/tvscSpace\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:text=\"@string/space\"\n            android:textColor=\"@color/text_black_color\"\n            android:textSize=\"@dimen/text_size_biggest\"\n            app:layout_constraintBottom_toBottomOf=\"@+id/scSpace\"\n            app:layout_constraintStart_toStartOf=\"parent\"\n            app:layout_constraintTop_toTopOf=\"@+id/scSpace\"\n            />\n\n        <com.google.android.material.materialswitch.MaterialSwitch\n            android:id=\"@+id/scSpace\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"12dp\"\n            app:layout_constraintEnd_toEndOf=\"parent\"\n            app:layout_constraintTop_toBottomOf=\"@+id/scBracketChar\"\n            />\n\n      </androidx.constraintlayout.widget.ConstraintLayout>\n\n    </androidx.core.widget.NestedScrollView>\n\n  </LinearLayout>\n\n</layout>"
  },
  {
    "path": "app/src/main/res/layout/activity_group_detail.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    >\n\n\n  <androidx.coordinatorlayout.widget.CoordinatorLayout\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"match_parent\"\n      android:background=\"@color/background_color\"\n      >\n\n    <com.google.android.material.appbar.AppBarLayout\n        android:id=\"@+id/app_bar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"120dp\"\n        android:background=\"@color/background_color\"\n        android:minHeight=\"@dimen/bar_height\"\n        >\n\n      <com.google.android.material.appbar.CollapsingToolbarLayout\n          android:id=\"@+id/ctlCollapsingLayout\"\n          android:layout_width=\"match_parent\"\n          android:layout_height=\"match_parent\"\n          android:elevation=\"4dp\"\n          app:layout_scrollFlags=\"scroll|exitUntilCollapsed|snap|snapMargins\"\n          app:collapsedTitleGravity=\"left\"\n          app:collapsedTitleTextAppearance=\"@style/CollapsingToolbarLayout.CollapsedTitleText\"\n          app:contentScrim=\"@color/background_color\"\n          app:expandedTitleGravity=\"left|bottom\"\n          app:expandedTitleTextAppearance=\"@style/CollapsingToolbarLayout.ExpandedTitleText\"\n          app:scrimAnimationDuration=\"1200\"\n          app:scrimVisibleHeightTrigger=\"150dp\"\n          app:statusBarScrim=\"@color/background_color\"\n          app:title=\"Group\"\n          app:titleEnabled=\"true\"\n          >\n\n        <androidx.appcompat.widget.Toolbar\n            android:id=\"@+id/kpa_toolbar\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"@dimen/bar_height\"\n            android:background=\"@color/background_color\"\n            app:layout_collapseMode=\"pin\"\n            app:navigationIcon=\"@drawable/ic_up\"\n            app:subtitleTextAppearance=\"@style/Toolbar.SubTitleText\"\n            app:theme=\"@style/ToolbarMenuTheme\"\n            app:titleTextAppearance=\"@style/Toolbar.TitleText\"\n            />\n      </com.google.android.material.appbar.CollapsingToolbarLayout>\n\n\n    </com.google.android.material.appbar.AppBarLayout>\n\n    <androidx.recyclerview.widget.RecyclerView\n        android:id=\"@+id/list\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        app:layout_behavior=\"@string/appbar_scrolling_view_behavior\"\n        />\n\n    <ViewStub\n        android:id=\"@+id/vsEmpty\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:layout_marginTop=\"@dimen/bar_height\"\n        android:layout=\"@layout/layout_empty_fill\"\n        />\n\n    <com.airbnb.lottie.LottieAnimationView\n        android:id=\"@+id/laAnim\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:background=\"@color/background_color\"\n        app:lottie_autoPlay=\"false\"\n        app:lottie_fileName=\"loadingAnimation.json\"\n        app:lottie_loop=\"false\"\n        />\n\n    <!--    <com.lyy.keepassa.widget.EmptyDataFillView-->\n    <!--        android:id=\"@+id/empty_layout\"-->\n    <!--        android:layout_width=\"match_parent\"-->\n    <!--        android:layout_height=\"match_parent\"-->\n    <!--        />-->\n\n    <com.lyy.keepassa.widget.MainExpandFloatActionButton\n        android:id=\"@+id/fab\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"end|bottom\"\n        android:layout_marginBottom=\"16dp\"\n        android:layout_marginEnd=\"16dp\"\n        android:visibility=\"visible\"\n        app:layout_behavior=\"com.lyy.keepassa.widget.MainExpandFloatActionButtonBehavior\"\n        />\n\n\n  </androidx.coordinatorlayout.widget.CoordinatorLayout>\n</layout>"
  },
  {
    "path": "app/src/main/res/layout/activity_group_dir.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    >\n\n  <RelativeLayout\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"wrap_content\"\n      android:background=\"@color/background_color\"\n      >\n\n    <include layout=\"@layout/layout_action_bar\"/>\n\n    <FrameLayout\n        android:id=\"@+id/content\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:layout_above=\"@+id/line\"\n        android:layout_below=\"@+id/kpa_toolbar\"\n        />\n\n    <View\n        android:id=\"@+id/line\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"1dp\"\n        android:layout_above=\"@+id/bt\"\n        android:background=\"@color/line_color\"\n        />\n\n    <Button\n        android:id=\"@+id/bt\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_alignParentBottom=\"true\"\n        android:background=\"@drawable/ripple_white_selector\"\n        android:text=\"@string/undo_to\"\n        android:textColor=\"@color/blue\"\n        android:textSize=\"@dimen/text_size_normal\"\n        />\n\n  </RelativeLayout>\n</layout>"
  },
  {
    "path": "app/src/main/res/layout/activity_icon.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n\n\n  <androidx.constraintlayout.widget.ConstraintLayout\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"match_parent\"\n      android:background=\"@color/background_color\"\n      android:orientation=\"vertical\">\n\n    <include layout=\"@layout/layout_action_bar\" />\n\n    <androidx.recyclerview.widget.RecyclerView\n        android:id=\"@+id/list\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"0dp\"\n        android:layout_marginStart=\"16dp\"\n        android:layout_marginEnd=\"16dp\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@+id/kpa_toolbar\" />\n\n\n  </androidx.constraintlayout.widget.ConstraintLayout>\n</layout>"
  },
  {
    "path": "app/src/main/res/layout/activity_launcher.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n\n  <FrameLayout\n      android:id=\"@+id/content\"\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"match_parent\"\n      android:background=\"@color/background_color\"\n      />\n</layout>"
  },
  {
    "path": "app/src/main/res/layout/activity_main.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    >\n\n\n  <androidx.coordinatorlayout.widget.CoordinatorLayout\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"match_parent\"\n      android:background=\"@color/background_color\"\n      >\n\n    <com.google.android.material.appbar.AppBarLayout\n        android:id=\"@+id/head_bar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"@color/background_color\"\n        android:minHeight=\"48dp\"\n        >\n\n      <RelativeLayout\n          android:id=\"@+id/head_toolbar\"\n          android:layout_width=\"match_parent\"\n          android:layout_height=\"wrap_content\"\n          android:background=\"@drawable/ripple_white_selector\"\n          android:clickable=\"true\"\n          android:descendantFocusability=\"blocksDescendants\"\n          android:focusable=\"true\"\n          android:paddingBottom=\"8dp\"\n          android:paddingLeft=\"16dp\"\n          android:paddingRight=\"16dp\"\n          android:paddingTop=\"8dp\"\n          app:layout_scrollFlags=\"scroll|enterAlways|snap|snapMargins\"\n          >\n\n        <androidx.appcompat.widget.AppCompatImageView\n            android:id=\"@+id/app_icon\"\n            android:layout_width=\"40dp\"\n            android:layout_height=\"40dp\"\n            android:layout_centerVertical=\"true\"\n            android:transitionName=\"@string/transition_app_icon\"\n            app:srcCompat=\"@mipmap/ic_launcher\"\n            />\n\n        <com.lyy.keepassa.widget.BubbleTextView\n            android:id=\"@+id/db_name\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginLeft=\"16dp\"\n            android:layout_toRightOf=\"@+id/app_icon\"\n            android:ellipsize=\"middle\"\n            android:gravity=\"center_vertical\"\n            android:maxLength=\"20\"\n            android:singleLine=\"true\"\n            android:text=\"dbname\"\n            android:textColor=\"@color/text_black_color\"\n            android:textSize=\"@dimen/text_size_normal\"\n            android:textStyle=\"bold\"\n            android:transitionName=\"@string/transition_db_name\"\n            app:icon_size=\"24dp\"\n            app:right_icon=\"@drawable/ic_eco\"\n            />\n\n        <TextView\n            android:id=\"@+id/db_version\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_alignBottom=\"@+id/app_icon\"\n            android:layout_alignLeft=\"@+id/db_name\"\n            android:text=\"des\"\n            android:textColor=\"@color/text_gray_color\"\n            android:textSize=\"@dimen/text_size_small\"\n            android:transitionName=\"@string/transition_db_version\"\n            />\n\n        <androidx.appcompat.widget.AppCompatImageView\n            android:id=\"@+id/arrow\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_toRightOf=\"@+id/db_name\"\n            android:transitionName=\"@string/transition_db_little\"\n            app:srcCompat=\"@drawable/ic_arrow_down\"\n            />\n\n        <androidx.appcompat.widget.AppCompatImageView\n            android:id=\"@+id/lock\"\n            android:layout_width=\"30dp\"\n            android:layout_height=\"30dp\"\n            android:layout_centerVertical=\"true\"\n            android:layout_marginRight=\"8dp\"\n            android:layout_toLeftOf=\"@+id/search\"\n            android:background=\"@drawable/ripple_white_selector\"\n            android:clickable=\"true\"\n            app:srcCompat=\"@drawable/ic_lock_24px\"\n            />\n\n        <androidx.appcompat.widget.AppCompatImageView\n            android:id=\"@+id/search\"\n            android:layout_width=\"30dp\"\n            android:layout_height=\"30dp\"\n            android:layout_alignParentEnd=\"true\"\n            android:layout_centerVertical=\"true\"\n            android:background=\"@drawable/ripple_white_selector\"\n            android:clickable=\"true\"\n            app:srcCompat=\"@drawable/ic_search\"\n            />\n\n      </RelativeLayout>\n\n      <com.google.android.material.tabs.TabLayout\n          android:id=\"@+id/tab\"\n          android:layout_width=\"match_parent\"\n          android:layout_height=\"wrap_content\"\n          app:layout_scrollFlags=\"noScroll\"\n          app:tabInlineLabel=\"true\"\n          style=\"@style/KapTabLayoutStyle\"\n          >\n\n        <com.google.android.material.tabs.TabItem\n            android:id=\"@+id/history\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:icon=\"@drawable/selector_ic_tab_history\"\n            android:text=\"@string/history\"\n            />\n\n        <com.google.android.material.tabs.TabItem\n            android:id=\"@+id/db\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:icon=\"@drawable/selector_ic_tab_db\"\n            android:text=\"@string/all\"\n            />\n\n        <com.google.android.material.tabs.TabItem\n            android:id=\"@+id/totp\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:icon=\"@drawable/selector_ic_tab_token\"\n            android:text=\"TOTP\"\n            />\n      </com.google.android.material.tabs.TabLayout>\n\n      <View\n          android:layout_width=\"match_parent\"\n          android:background=\"@color/color_33666666\"\n          android:layout_height=\"1dp\"/>\n\n\n    </com.google.android.material.appbar.AppBarLayout>\n\n\n    <androidx.viewpager2.widget.ViewPager2\n        android:id=\"@+id/vp\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        app:layout_behavior=\"@string/appbar_scrolling_view_behavior\"\n        />\n\n    <com.lyy.keepassa.widget.MainExpandFloatActionButton\n        android:id=\"@+id/fab\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"end|bottom\"\n        android:layout_marginBottom=\"24dp\"\n        android:layout_marginRight=\"24dp\"\n        app:layout_behavior=\"com.lyy.keepassa.widget.MainExpandFloatActionButtonBehavior\"\n        />\n\n\n    <FrameLayout\n        android:id=\"@+id/container\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        />\n\n\n  </androidx.coordinatorlayout.widget.CoordinatorLayout>\n</layout>"
  },
  {
    "path": "app/src/main/res/layout/activity_markdown_editor.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n  <androidx.constraintlayout.widget.ConstraintLayout xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"match_parent\"\n      android:fitsSystemWindows=\"true\">\n\n    <com.lyy.keepassa.widget.editor.MarkDownEditor\n        android:id=\"@+id/mdeEditor\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\" />\n\n  </androidx.constraintlayout.widget.ConstraintLayout>\n</layout>"
  },
  {
    "path": "app/src/main/res/layout/activity_only_list.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n  <RelativeLayout\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"match_parent\"\n      android:background=\"@color/background_color\"\n      android:orientation=\"vertical\"\n      >\n\n    <include layout=\"@layout/layout_action_bar\" />\n\n\n    <androidx.recyclerview.widget.RecyclerView\n        android:background=\"@color/background_color\"\n        android:id=\"@+id/list\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:layout_below=\"@+id/kpa_toolbar\"\n        android:overScrollMode=\"never\"\n        />\n\n    <com.lyy.keepassa.widget.EmptyDataFillView\n        android:id=\"@+id/temp\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:layout_below=\"@+id/kpa_toolbar\"\n        />\n\n  </RelativeLayout>\n\n</layout>"
  },
  {
    "path": "app/src/main/res/layout/activity_qr_code_scanner.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    >\n\n  <androidx.constraintlayout.widget.ConstraintLayout\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"match_parent\"\n      >\n\n    <com.journeyapps.barcodescanner.BarcodeView\n        android:id=\"@+id/barcodeView\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        />\n\n    <com.journeyapps.barcodescanner.ViewfinderView\n        android:id=\"@+id/vFinderView\"\n        android:layout_width=\"300dp\"\n        android:layout_height=\"300dp\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"@+id/barcodeView\"\n        />\n\n    <View\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        android:background=\"@color/mask\"\n        app:layout_constraintBottom_toTopOf=\"@+id/vFinderView\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        />\n\n    <View\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        android:background=\"@color/mask\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@+id/vFinderView\"\n        />\n\n    <View\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        android:background=\"@color/mask\"\n        app:layout_constraintBottom_toBottomOf=\"@+id/vFinderView\"\n        app:layout_constraintEnd_toStartOf=\"@+id/vFinderView\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"@+id/vFinderView\"\n        />\n\n    <View\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        android:background=\"@color/mask\"\n        app:layout_constraintBottom_toBottomOf=\"@+id/vFinderView\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toEndOf=\"@+id/vFinderView\"\n        app:layout_constraintTop_toTopOf=\"@+id/vFinderView\"\n        />\n\n    <!--    <com.journeyapps.barcodescanner.DecoratedBarcodeView-->\n    <!--        android:id=\"@+id/dbView\"-->\n    <!--        android:layout_width=\"match_parent\"-->\n    <!--        android:layout_height=\"match_parent\"/>-->\n\n  </androidx.constraintlayout.widget.ConstraintLayout>\n</layout>"
  },
  {
    "path": "app/src/main/res/layout/activity_setting.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n  <LinearLayout\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"match_parent\"\n      android:background=\"@color/background_color\"\n      android:orientation=\"vertical\"\n      >\n\n    <include layout=\"@layout/layout_action_bar\" />\n\n    <FrameLayout\n        android:id=\"@+id/content\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        />\n\n  </LinearLayout>\n</layout>"
  },
  {
    "path": "app/src/main/res/layout/android_simple_dropdown_item_1line.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.appcompat.widget.AppCompatTextView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@android:id/text1\"\n    style=\"@style/KpaTitleTextStyle\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"@dimen/bar_height\"\n    android:background=\"@drawable/ripple_white_selector\"\n    android:gravity=\"center_vertical\"\n    android:paddingStart=\"16dp\"\n    android:paddingEnd=\"16dp\"\n    android:singleLine=\"true\"\n    tools:ignore=\"MissingDefaultResource\" />\n"
  },
  {
    "path": "app/src/main/res/layout/dialog_add_attr_str.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n\n  <LinearLayout\n      style=\"@style/KpaDialogLayoutStyle\"\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"wrap_content\"\n      android:minWidth=\"@dimen/dialog_min_width\"\n      android:orientation=\"vertical\">\n\n    <TextView\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"48dp\"\n        android:gravity=\"center\"\n        android:text=\"@string/add_custom_str\"\n        android:textColor=\"@color/text_blue_color\"\n        android:textSize=\"@dimen/text_size_biggest\" />\n\n    <com.google.android.material.textfield.TextInputLayout\n        android:id=\"@+id/str_key_layout\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginStart=\"16dp\"\n        android:layout_marginEnd=\"16dp\"\n        app:startIconDrawable=\"@drawable/ic_title_24px\"\n        app:startIconTint=\"@color/color_icon_grey\">\n\n      <com.google.android.material.textfield.TextInputEditText\n          android:id=\"@+id/str_key\"\n          android:layout_width=\"match_parent\"\n          android:layout_height=\"wrap_content\"\n          android:background=\"@color/transparent\"\n          android:hint=\"@string/hint_input_attr_str_key\"\n          android:imeOptions=\"actionNext\"\n          android:singleLine=\"true\"\n          android:textColor=\"@color/text_black_grey_color\"\n          android:textColorHint=\"@color/text_hint_color\"\n          android:textSize=\"@dimen/text_size_normal\">\n\n      </com.google.android.material.textfield.TextInputEditText>\n\n    </com.google.android.material.textfield.TextInputLayout>\n\n    <com.google.android.material.textfield.TextInputLayout\n        android:id=\"@+id/str_value_layout\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginStart=\"16dp\"\n        android:layout_marginEnd=\"16dp\"\n        app:startIconDrawable=\"@drawable/ic_text_fields_24px\"\n        app:startIconTint=\"@color/color_icon_grey\">\n\n      <com.google.android.material.textfield.TextInputEditText\n          android:id=\"@+id/str_value\"\n          android:layout_width=\"match_parent\"\n          android:layout_height=\"wrap_content\"\n          android:background=\"@color/transparent\"\n          android:hint=\"@string/hint_input_attr_str_value\"\n          android:imeOptions=\"actionNext\"\n          android:singleLine=\"true\"\n          android:textColor=\"@color/text_black_grey_color\"\n          android:textColorHint=\"@color/text_hint_color\"\n          android:textSize=\"@dimen/text_size_normal\">\n\n      </com.google.android.material.textfield.TextInputEditText>\n\n    </com.google.android.material.textfield.TextInputLayout>\n\n    <CheckBox\n        android:id=\"@+id/cb\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginStart=\"25dp\"\n        android:layout_marginTop=\"8dp\"\n        android:layout_marginEnd=\"16dp\"\n        android:text=\"@string/hint_protect_str\"\n        android:textColor=\"@color/text_black_color\"\n        android:textSize=\"@dimen/text_size_normal\" />\n\n    <LinearLayout\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"end\"\n        android:orientation=\"horizontal\">\n\n      <Button\n          android:id=\"@+id/cancel\"\n          android:layout_width=\"wrap_content\"\n          android:layout_height=\"wrap_content\"\n          android:background=\"@drawable/ripple_white_selector\"\n          android:text=\"@string/cancel\"\n          android:textColor=\"@color/text_gray_color\" />\n\n      <Button\n          android:id=\"@+id/enter\"\n          android:layout_width=\"wrap_content\"\n          android:layout_height=\"wrap_content\"\n          android:background=\"@drawable/ripple_white_selector\"\n          android:text=\"@string/enter\"\n          android:textColor=\"@color/text_blue_color\" />\n\n    </LinearLayout>\n\n\n  </LinearLayout>\n</layout>"
  },
  {
    "path": "app/src/main/res/layout/dialog_add_group.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n\n\n  <LinearLayout\n      style=\"@style/KpaDialogLayoutStyle\"\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"wrap_content\"\n      android:minWidth=\"@dimen/dialog_min_width\"\n      android:orientation=\"vertical\">\n\n    <TextView\n        android:id=\"@+id/title\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"48dp\"\n        android:gravity=\"center\"\n        android:text=\"@string/create_group\"\n        android:textColor=\"@color/text_blue_color\"\n        android:textSize=\"@dimen/text_size_biggest\" />\n\n    <View\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"1dp\"\n        android:background=\"@color/line_color\" />\n\n    <com.google.android.material.textfield.TextInputLayout\n        android:id=\"@+id/group_name_layout\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginLeft=\"16dp\"\n        android:layout_marginTop=\"8dp\"\n        android:layout_marginRight=\"16dp\"\n        android:background=\"@color/transparent\"\n        app:counterEnabled=\"true\"\n        app:counterMaxLength=\"16\"\n        app:counterOverflowTextAppearance=\"@style/InputEditTextErrorStyle\"\n        app:counterTextAppearance=\"@style/InputEditTextOverAppearance\"\n        app:endIconCheckable=\"true\"\n        app:endIconDrawable=\"@drawable/ic_img_choose\"\n        app:endIconMode=\"custom\"\n        app:endIconTint=\"@color/color_icon_grey\"\n        app:errorTextAppearance=\"@style/InputEditTextErrorStyle\"\n        app:helperText=\"@string/help_create_group\"\n        app:helperTextTextColor=\"@color/text_gray_color\"\n        app:hintTextAppearance=\"@style/InputEditTextHintStyle\">\n\n      <com.google.android.material.textfield.TextInputEditText\n          android:id=\"@+id/group_name\"\n          android:layout_width=\"match_parent\"\n          android:layout_height=\"48dp\"\n          android:background=\"@color/transparent\"\n          android:hint=\"@string/group_name\"\n          android:imeOptions=\"actionDone\"\n          android:paddingLeft=\"4dp\"\n          android:paddingRight=\"4dp\"\n          android:singleLine=\"true\"\n          android:textColor=\"@color/text_black_grey_color\"\n          android:textColorHint=\"@color/text_hint_color\"\n          android:textSize=\"@dimen/text_size_normal\"\n          android:transitionName=\"@string/transition_db_name\" />\n\n    </com.google.android.material.textfield.TextInputLayout>\n\n    <LinearLayout\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"end\"\n        android:layout_marginTop=\"8dp\"\n        android:orientation=\"horizontal\">\n\n      <Button\n          android:id=\"@+id/cancel\"\n          android:layout_width=\"wrap_content\"\n          android:layout_height=\"wrap_content\"\n          android:background=\"@drawable/ripple_white_selector\"\n          android:text=\"@string/cancel\"\n          android:textColor=\"@color/text_gray_color\" />\n\n      <Button\n          android:id=\"@+id/enter\"\n          android:layout_width=\"wrap_content\"\n          android:layout_height=\"wrap_content\"\n          android:background=\"@drawable/ripple_white_selector\"\n          android:text=\"@string/enter\"\n          android:textColor=\"@color/text_blue_color\" />\n\n    </LinearLayout>\n\n  </LinearLayout>\n</layout>"
  },
  {
    "path": "app/src/main/res/layout/dialog_add_more.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n  <LinearLayout\n      style=\"@style/KpaDialogLayoutStyle\"\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"wrap_content\"\n      android:orientation=\"vertical\">\n\n    <TextView\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"48dp\"\n        android:gravity=\"center\"\n        android:text=\"@string/add_more\"\n        android:textColor=\"@color/text_black_color\"\n        android:textSize=\"@dimen/text_size_bigger\" />\n\n    <androidx.recyclerview.widget.RecyclerView\n        android:id=\"@+id/list\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\" />\n\n  </LinearLayout>\n</layout>"
  },
  {
    "path": "app/src/main/res/layout/dialog_choose_tag.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:bind=\"http://schemas.android.com/apk/res-auto\"\n    >\n  <data>\n    <variable\n        name=\"msgTitle\"\n        type=\"java.lang.CharSequence\"\n        />\n\n    <variable\n        name=\"clicker\"\n        type=\"com.lyy.keepassa.view.dialog.DialogBtnClicker\"\n        />\n    <variable\n        name=\"enableEnterBt\"\n        type=\"Boolean\"\n        />\n\n  </data>\n  <androidx.appcompat.widget.LinearLayoutCompat\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"wrap_content\"\n      android:orientation=\"vertical\"\n      style=\"@style/KpaDialogLayoutStyle\"\n      >\n    <include\n        layout=\"@layout/layout_dialog_title\"\n        android:id=\"@+id/includeLayout\"\n        bind:msgTitle=\"@{msgTitle}\"\n        />\n\n    <androidx.recyclerview.widget.RecyclerView\n        android:id=\"@+id/rvList\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:maxHeight=\"300dp\"\n        android:paddingBottom=\"16dp\"\n        android:paddingTop=\"16dp\"\n        bind:layout_constraintStart_toStartOf=\"parent\"\n        />\n\n    <include\n        layout=\"@layout/layout_dialog_button\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"48dp\"\n        bind:clicker=\"@{clicker}\"\n        bind:enableEnterBt=\"@{enableEnterBt}\"\n        />\n\n  </androidx.appcompat.widget.LinearLayoutCompat>\n</layout>\n"
  },
  {
    "path": "app/src/main/res/layout/dialog_cloud_file_list.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    >\n\n\n  <androidx.constraintlayout.widget.ConstraintLayout\n      android:layout_width=\"@dimen/dialog_min_width\"\n      android:layout_height=\"wrap_content\"\n      android:animateLayoutChanges=\"true\"\n      android:orientation=\"vertical\"\n      style=\"@style/KpaDialogLayoutStyle\"\n      >\n\n    <TextView\n        android:id=\"@+id/title\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"48dp\"\n        android:layout_centerHorizontal=\"true\"\n        android:gravity=\"center\"\n        android:text=\"@string/choose_file\"\n        android:textColor=\"@color/text_blue_color\"\n        android:textSize=\"@dimen/text_size_biggest\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        />\n\n    <ImageView\n        android:id=\"@+id/iv_close\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_centerVertical=\"true\"\n        android:layout_marginEnd=\"16dp\"\n        android:background=\"@drawable/ripple_white_selector\"\n        android:clickable=\"true\"\n        android:src=\"@drawable/ic_close\"\n        app:layout_constraintBottom_toBottomOf=\"@+id/title\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"@+id/title\"\n        />\n\n\n    <View\n        android:id=\"@+id/vLine\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"1dp\"\n        android:background=\"@color/line_color\"\n        app:layout_constraintTop_toBottomOf=\"@+id/title\"\n        />\n\n    <TextView\n        android:id=\"@+id/path\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_below=\"@+id/vLine\"\n        android:layout_marginBottom=\"4dp\"\n        android:layout_marginEnd=\"16dp\"\n        android:layout_marginStart=\"16dp\"\n        android:layout_marginTop=\"4dp\"\n        android:text=\"/\"\n        android:textColor=\"@color/text_gray_color\"\n        android:textSize=\"@dimen/text_size_small\"\n        app:layout_constraintTop_toBottomOf=\"@+id/vLine\"\n        />\n\n    <com.airbnb.lottie.LottieAnimationView\n        android:id=\"@+id/anim\"\n        android:layout_width=\"300dp\"\n        android:layout_height=\"200dp\"\n        android:layout_centerInParent=\"true\"\n        android:layout_gravity=\"center\"\n        app:layout_constraintBottom_toBottomOf=\"@+id/list\"\n        app:layout_constraintEnd_toEndOf=\"@+id/list\"\n        app:layout_constraintStart_toStartOf=\"@+id/list\"\n        app:layout_constraintTop_toTopOf=\"@+id/list\"\n        app:lottie_autoPlay=\"true\"\n        app:lottie_loop=\"true\"\n        app:lottie_fileName=\"loadingAnimation.json\"\n        />\n\n    <androidx.recyclerview.widget.RecyclerView\n        android:id=\"@+id/list\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"350dp\"\n        app:layout_constraintTop_toBottomOf=\"@+id/path\"\n        />\n\n    <androidx.appcompat.widget.AppCompatButton\n        android:id=\"@+id/btnSelect\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"@drawable/ripple_white_selector\"\n        android:text=\"@string/select_cur_path\"\n        android:textColor=\"@color/text_blue_color\"\n        android:textSize=\"@dimen/text_size_normal\"\n        app:layout_constraintTop_toBottomOf=\"@+id/list\"\n        />\n\n  </androidx.constraintlayout.widget.ConstraintLayout>\n</layout>"
  },
  {
    "path": "app/src/main/res/layout/dialog_create_tag.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:bind=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    >\n  <data>\n    <variable\n        name=\"msgTitle\"\n        type=\"java.lang.CharSequence\"\n        />\n\n    <variable\n        name=\"clicker\"\n        type=\"com.lyy.keepassa.view.dialog.DialogBtnClicker\"\n        />\n\n    <variable\n        name=\"enableEnterBt\"\n        type=\"Boolean\"\n        />\n\n  </data>\n  <androidx.appcompat.widget.LinearLayoutCompat\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"wrap_content\"\n      android:orientation=\"vertical\"\n      style=\"@style/KpaDialogLayoutStyle\"\n      >\n    <include\n        layout=\"@layout/layout_dialog_title\"\n        android:id=\"@+id/includeLayout\"\n        bind:msgTitle=\"@{msgTitle}\"\n        />\n\n    <com.google.android.material.textfield.TextInputLayout\n        android:id=\"@+id/tlTag\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:padding=\"16dp\"\n        app:startIconDrawable=\"@drawable/ic_user\"\n        app:startIconTint=\"@color/color_icon_grey\"\n        style=\"@style/Widget.Material3.TextInputLayout.OutlinedBox.ExposedDropdownMenu\"\n        >\n\n      <com.google.android.material.textfield.MaterialAutoCompleteTextView\n          android:id=\"@+id/edTag\"\n          android:layout_width=\"match_parent\"\n          android:layout_height=\"wrap_content\"\n          android:hint=\"@string/tag_name\"\n          android:imeOptions=\"actionNext\"\n          app:backgroundTint=\"?attr/colorPrimary\"\n          tools:text=\"用户名\"\n          style=\"@style/KpaEditTextStyleNew\"\n          />\n\n    </com.google.android.material.textfield.TextInputLayout>\n\n    <include\n        layout=\"@layout/layout_dialog_button\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"48dp\"\n        bind:clicker=\"@{clicker}\"\n        bind:enableEnterBt=\"@{enableEnterBt}\"\n        />\n\n  </androidx.appcompat.widget.LinearLayoutCompat>\n</layout>\n"
  },
  {
    "path": "app/src/main/res/layout/dialog_create_totp.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n  <LinearLayout\n      android:layout_width=\"@dimen/dialog_min_width\"\n      android:layout_height=\"wrap_content\"\n      android:animateLayoutChanges=\"true\"\n      android:minWidth=\"@dimen/dialog_min_width\"\n      android:orientation=\"vertical\"\n      style=\"@style/KpaDialogLayoutStyle\"\n      >\n\n\n    <TextView\n        android:id=\"@+id/tvTitle\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"48dp\"\n        android:gravity=\"center\"\n        android:text=\"@string/create_totp\"\n        android:textColor=\"@color/text_blue_color\"\n        android:textSize=\"@dimen/text_size_biggest\"\n        />\n\n    <include\n        layout=\"@layout/layout_otp_create_menu\"\n        android:id=\"@+id/menuLayout\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"100dp\"\n        />\n\n    <ViewStub\n        android:id=\"@+id/vsCustom\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout=\"@layout/layout_otp_create_default\"\n        />\n\n  </LinearLayout>\n</layout>"
  },
  {
    "path": "app/src/main/res/layout/dialog_donate.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n\n  <androidx.constraintlayout.widget.ConstraintLayout\n      style=\"@style/KpaDialogLayoutStyle\"\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"wrap_content\"\n      android:orientation=\"vertical\">\n\n    <com.lyy.keepassa.widget.DrawableTextView\n        android:id=\"@+id/title\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"48dp\"\n        android:layout_centerHorizontal=\"true\"\n        android:drawablePadding=\"4dp\"\n        android:gravity=\"center_vertical\"\n        android:text=\"@string/donate\"\n        android:textColor=\"@color/text_blue_color\"\n        android:textSize=\"@dimen/text_size_biggest\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\" />\n\n    <ImageView\n        android:id=\"@+id/ivClose\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_alignParentEnd=\"true\"\n        android:layout_centerVertical=\"true\"\n        android:layout_marginEnd=\"16dp\"\n        android:background=\"@drawable/ripple_white_selector\"\n        android:clickable=\"true\"\n        android:src=\"@drawable/ic_close\"\n        app:layout_constraintBottom_toBottomOf=\"@id/title\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"@+id/title\" />\n\n    <View\n        android:id=\"@+id/vLine\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"1dp\"\n        android:background=\"@color/line_color\"\n        app:layout_constraintTop_toBottomOf=\"@+id/title\" />\n\n    <TextView\n        android:id=\"@+id/tvDesc\"\n        style=\"@style/KpaContentTextStyle\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginStart=\"16dp\"\n        android:layout_marginTop=\"16dp\"\n        android:layout_marginEnd=\"16dp\"\n        android:text=\"@string/donate_desc\"\n        android:textColor=\"@color/text_black_color\"\n        android:textSize=\"13sp\"\n        app:layout_constraintTop_toBottomOf=\"@+id/vLine\" />\n\n    <RelativeLayout\n        android:id=\"@+id/rlAliPay\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"60dp\"\n        android:layout_marginTop=\"16dp\"\n        android:background=\"@drawable/ripple_white_selector\"\n        android:clickable=\"true\"\n        android:paddingStart=\"32dp\"\n        android:paddingEnd=\"32dp\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@+id/tvDesc\">\n\n      <ImageView\n          android:id=\"@+id/ivAliPay\"\n          android:layout_width=\"48dp\"\n          android:layout_height=\"48dp\"\n          android:layout_centerVertical=\"true\"\n          android:src=\"@drawable/ic_alipay\" />\n\n      <TextView\n          style=\"@style/KpaTitleTextStyle\"\n          android:layout_width=\"wrap_content\"\n          android:layout_height=\"wrap_content\"\n          android:layout_centerVertical=\"true\"\n          android:layout_marginStart=\"32dp\"\n          android:layout_toEndOf=\"@+id/ivAliPay\"\n          android:text=\"@string/Alipay\"\n          android:textSize=\"20sp\" />\n    </RelativeLayout>\n\n    <RelativeLayout\n        android:visibility=\"gone\"\n        android:id=\"@+id/rlPlay\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"60dp\"\n        android:layout_marginTop=\"16dp\"\n        android:background=\"@drawable/ripple_white_selector\"\n        android:clickable=\"true\"\n        android:paddingStart=\"32dp\"\n        android:paddingEnd=\"32dp\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@+id/rlAliPay\">\n\n      <ImageView\n          android:id=\"@+id/ivPlay\"\n          android:layout_width=\"48dp\"\n          android:layout_height=\"48dp\"\n          android:layout_centerVertical=\"true\"\n          android:src=\"@drawable/ic_google_play\" />\n\n      <TextView\n          style=\"@style/KpaTitleTextStyle\"\n          android:layout_width=\"wrap_content\"\n          android:layout_height=\"wrap_content\"\n          android:layout_centerVertical=\"true\"\n          android:layout_marginStart=\"32dp\"\n          android:layout_toEndOf=\"@+id/ivPlay\"\n          android:text=\"@string/google_play\"\n          android:textSize=\"20sp\" />\n    </RelativeLayout>\n\n    <RelativeLayout\n        android:id=\"@+id/rlPayPal\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"60dp\"\n        android:layout_marginTop=\"16dp\"\n        android:background=\"@drawable/ripple_white_selector\"\n        android:clickable=\"true\"\n        android:paddingStart=\"32dp\"\n        android:paddingEnd=\"32dp\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@+id/rlPlay\">\n\n      <ImageView\n          android:id=\"@+id/ivPayPal\"\n          android:layout_width=\"36dp\"\n          android:layout_height=\"36dp\"\n          android:layout_centerVertical=\"true\"\n          android:layout_marginStart=\"6dp\"\n          android:src=\"@drawable/ic_paypal\" />\n\n      <TextView\n          style=\"@style/KpaTitleTextStyle\"\n          android:layout_width=\"wrap_content\"\n          android:layout_height=\"wrap_content\"\n          android:layout_centerVertical=\"true\"\n          android:layout_marginStart=\"36dp\"\n          android:layout_toEndOf=\"@+id/ivPayPal\"\n          android:text=\"PayPal\"\n          android:textSize=\"20sp\" />\n    </RelativeLayout>\n\n<!--    <RelativeLayout-->\n<!--        android:id=\"@+id/rlBtc\"-->\n<!--        android:layout_width=\"0dp\"-->\n<!--        android:layout_height=\"60dp\"-->\n<!--        android:layout_marginTop=\"16dp\"-->\n<!--        android:background=\"@drawable/ripple_white_selector\"-->\n<!--        android:clickable=\"true\"-->\n<!--        android:paddingStart=\"32dp\"-->\n<!--        android:paddingEnd=\"32dp\"-->\n<!--        app:layout_constraintEnd_toEndOf=\"parent\"-->\n<!--        app:layout_constraintStart_toStartOf=\"parent\"-->\n<!--        app:layout_constraintTop_toBottomOf=\"@+id/rlPayPal\">-->\n\n<!--      <ImageView-->\n<!--          android:id=\"@+id/ivBtc\"-->\n<!--          android:layout_width=\"36dp\"-->\n<!--          android:layout_height=\"36dp\"-->\n<!--          android:layout_centerVertical=\"true\"-->\n<!--          android:layout_marginStart=\"6dp\"-->\n<!--          android:src=\"@drawable/ic_paypal\" />-->\n\n<!--      <TextView-->\n<!--          style=\"@style/KpaTitleTextStyle\"-->\n<!--          android:layout_width=\"wrap_content\"-->\n<!--          android:layout_height=\"wrap_content\"-->\n<!--          android:layout_centerVertical=\"true\"-->\n<!--          android:layout_marginStart=\"36dp\"-->\n<!--          android:layout_toEndOf=\"@+id/ivBtc\"-->\n<!--          android:text=\"PayPal\"-->\n<!--          android:textSize=\"20sp\" />-->\n<!--    </RelativeLayout>-->\n\n\n  </androidx.constraintlayout.widget.ConstraintLayout>\n</layout>\n"
  },
  {
    "path": "app/src/main/res/layout/dialog_entry_icon.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n\n  <androidx.coordinatorlayout.widget.CoordinatorLayout\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"match_parent\">\n\n    <RelativeLayout\n        android:id=\"@+id/llContent\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:background=\"@color/background_color\"\n        android:orientation=\"vertical\"\n        app:layout_behavior=\"com.google.android.material.bottomsheet.BottomSheetBehavior\">\n\n      <FrameLayout\n          android:id=\"@+id/fl\"\n          android:layout_width=\"match_parent\"\n          android:layout_height=\"48dp\">\n\n        <TextView\n            style=\"@style/KpaTitleTextStyle\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_gravity=\"start|center_vertical\"\n            android:layout_marginStart=\"24dp\"\n            android:text=\"ICON\"\n            android:textStyle=\"bold\" />\n\n        <RadioGroup\n            android:layout_marginEnd=\"16dp\"\n            android:id=\"@+id/rg\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"match_parent\"\n            android:layout_gravity=\"end\"\n            android:gravity=\"center_vertical\"\n            android:orientation=\"horizontal\">\n\n          <com.google.android.material.radiobutton.MaterialRadioButton\n              android:id=\"@+id/mrbDefault\"\n              android:layout_width=\"wrap_content\"\n              android:layout_height=\"wrap_content\"\n              android:checked=\"true\"\n              android:text=\"@string/s_default\"\n              android:textSize=\"@dimen/text_size_normal\"\n              app:buttonTint=\"@color/colorPrimary\" />\n\n          <com.google.android.material.radiobutton.MaterialRadioButton\n              android:id=\"@+id/mrbCustom\"\n              android:layout_width=\"wrap_content\"\n              android:layout_height=\"wrap_content\"\n              android:layout_marginStart=\"8dp\"\n              android:text=\"@string/customize\"\n              android:textSize=\"@dimen/text_size_normal\"\n              app:buttonTint=\"@color/colorPrimary\" />\n        </RadioGroup>\n      </FrameLayout>\n\n\n      <androidx.recyclerview.widget.RecyclerView\n          android:id=\"@+id/rvList\"\n          android:layout_width=\"match_parent\"\n          android:layout_height=\"match_parent\"\n          android:layout_below=\"@+id/fl\" />\n\n      <ViewStub\n          android:id=\"@+id/vsLoading\"\n          android:layout_width=\"match_parent\"\n          android:layout_height=\"match_parent\"\n          android:layout_below=\"@+id/fl\"\n          android:layout=\"@layout/layout_loading\" />\n    </RelativeLayout>\n\n  </androidx.coordinatorlayout.widget.CoordinatorLayout>\n</layout>"
  },
  {
    "path": "app/src/main/res/layout/dialog_img_viewer.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n\n  <data>\n\n    <variable\n        name=\"dialog\"\n        type=\"com.lyy.keepassa.view.dialog.ImgViewerDialog\" />\n  </data>\n\n  <androidx.constraintlayout.widget.ConstraintLayout\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"match_parent\"\n      android:background=\"@color/background_color\">\n\n\n    <com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView\n        android:id=\"@+id/imageView\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\" />\n\n    <androidx.appcompat.widget.AppCompatImageView\n        android:layout_width=\"40dp\"\n        android:layout_height=\"40dp\"\n        android:layout_marginTop=\"16dp\"\n        android:layout_marginEnd=\"16dp\"\n        android:background=\"@drawable/ripple_white_selector\"\n        android:clickable=\"true\"\n        android:focusable=\"true\"\n        android:onClick=\"@{()->dialog.dismiss()}\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        app:srcCompat=\"@drawable/ic_close\" />\n\n  </androidx.constraintlayout.widget.ConstraintLayout>\n\n</layout>"
  },
  {
    "path": "app/src/main/res/layout/dialog_loading.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n\n  <FrameLayout 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/mask\"\n      >\n\n    <com.airbnb.lottie.LottieAnimationView\n        android:id=\"@+id/anim\"\n        android:layout_width=\"300dp\"\n        android:layout_height=\"200dp\"\n        android:layout_gravity=\"center\"\n        app:lottie_autoPlay=\"true\"\n        app:lottie_loop=\"true\"\n        />\n  </FrameLayout>\n</layout>\n"
  },
  {
    "path": "app/src/main/res/layout/dialog_modify_pass.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n\n  <LinearLayout\n      style=\"@style/KpaDialogLayoutStyle\"\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"wrap_content\"\n      android:minWidth=\"@dimen/dialog_min_width\"\n      android:orientation=\"vertical\"\n      android:paddingStart=\"24dp\"\n      android:paddingEnd=\"24dp\">\n\n    <TextView\n        android:id=\"@+id/title\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"48dp\"\n        android:gravity=\"center\"\n        android:text=\"@string/modify_db_pass\"\n        android:textColor=\"@color/text_blue_color\"\n        android:textSize=\"@dimen/text_size_biggest\" />\n\n\n    <com.google.android.material.textfield.TextInputLayout\n        android:id=\"@+id/password_layout\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_toStartOf=\"@+id/pass_generate\"\n        app:counterOverflowTextAppearance=\"@style/InputEditTextErrorStyle\"\n        app:counterTextAppearance=\"@style/InputEditTextOverAppearance\"\n        app:endIconTint=\"@color/color_icon_grey\"\n        app:errorEnabled=\"true\"\n        app:errorIconDrawable=\"@drawable/ic_info_filled\"\n        app:errorTextAppearance=\"@style/InputEditTextErrorStyle\"\n        app:helperText=\"@string/helper_create_pass\"\n        app:helperTextTextColor=\"@color/text_gray_color\"\n        app:passwordToggleEnabled=\"true\"\n        app:startIconDrawable=\"@drawable/ic_password\"\n        app:startIconTint=\"@color/color_icon_grey\">\n\n      <com.google.android.material.textfield.TextInputEditText\n          android:id=\"@+id/password\"\n          android:layout_width=\"match_parent\"\n          android:layout_height=\"48dp\"\n          android:background=\"@color/transparent\"\n          android:hint=\"@string/password\"\n          android:imeOptions=\"actionNext\"\n          android:inputType=\"textPassword\"\n          android:singleLine=\"true\"\n          android:textColor=\"@color/text_black_grey_color\"\n          android:textColorHint=\"@color/text_hint_color\"\n          android:textSize=\"@dimen/text_size_normal\">\n\n      </com.google.android.material.textfield.TextInputEditText>\n\n    </com.google.android.material.textfield.TextInputLayout>\n\n    <com.google.android.material.textfield.TextInputLayout\n        android:id=\"@+id/enter_password_layout\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        app:helperTextTextColor=\"@color/text_gray_color\"\n        app:hintTextAppearance=\"@style/InputEditTextHintStyle\"\n        app:startIconDrawable=\"@drawable/ic_password\"\n        app:startIconTint=\"@color/color_icon_grey\">\n\n      <com.google.android.material.textfield.TextInputEditText\n          android:id=\"@+id/enter_password\"\n          android:layout_width=\"match_parent\"\n          android:layout_height=\"48dp\"\n          android:background=\"@color/transparent\"\n          android:hint=\"@string/hint_enter_password\"\n          android:imeOptions=\"actionDone\"\n          android:inputType=\"textPassword\"\n          android:singleLine=\"true\"\n          android:textColor=\"@color/text_black_grey_color\"\n          android:textColorHint=\"@color/text_hint_color\"\n          android:textSize=\"@dimen/text_size_normal\">\n\n      </com.google.android.material.textfield.TextInputEditText>\n\n    </com.google.android.material.textfield.TextInputLayout>\n\n    <LinearLayout\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"end\"\n        android:orientation=\"horizontal\">\n\n      <Button\n          android:id=\"@+id/cancel\"\n          android:layout_width=\"wrap_content\"\n          android:layout_height=\"wrap_content\"\n          android:background=\"@drawable/ripple_white_selector\"\n          android:text=\"@string/cancel\"\n          android:textColor=\"@color/text_gray_color\" />\n\n      <Button\n          android:id=\"@+id/enter\"\n          android:layout_width=\"wrap_content\"\n          android:layout_height=\"wrap_content\"\n          android:background=\"@drawable/ripple_white_selector\"\n          android:text=\"@string/enter\"\n          android:textColor=\"@color/text_blue_color\" />\n\n    </LinearLayout>\n\n  </LinearLayout>\n</layout>"
  },
  {
    "path": "app/src/main/res/layout/dialog_msg.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n\n  <data>\n\n    <variable\n        name=\"dialog\"\n        type=\"com.lyy.keepassa.view.dialog.MsgDialog\" />\n\n    <import type=\"android.text.TextUtils\" />\n\n    <import type=\"android.view.View\" />\n\n  </data>\n\n  <androidx.constraintlayout.widget.ConstraintLayout\n      style=\"@style/KpaDialogLayoutStyle\"\n      android:layout_width=\"@dimen/dialog_min_width\"\n      android:elevation=\"4dp\"\n      android:layout_height=\"wrap_content\">\n\n\n    <com.lyy.keepassa.widget.BubbleTextView\n        android:id=\"@+id/tvTitle\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"48dp\"\n        android:layout_gravity=\"center_horizontal\"\n        android:gravity=\"center_vertical\"\n        android:text=\"@{TextUtils.isEmpty(dialog.msgTitle) ? @string/hint : dialog.msgTitle}\"\n        android:textColor=\"@color/text_blue_color\"\n        android:textSize=\"@dimen/text_size_biggest\"\n        app:icon_size=\"24dp\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\" />\n\n    <View\n        android:id=\"@+id/vLine\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"1dp\"\n        android:background=\"@color/line_color\"\n        app:layout_constraintTop_toBottomOf=\"@+id/tvTitle\" />\n\n    <androidx.core.widget.NestedScrollView\n        android:id=\"@+id/msgScroll\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        app:layout_constraintTop_toBottomOf=\"@+id/vLine\">\n\n      <TextView\n          android:layout_width=\"match_parent\"\n          android:layout_height=\"wrap_content\"\n          android:layout_margin=\"16dp\"\n          android:maxHeight=\"400dp\"\n          android:text=\"@{dialog.msgContent}\"\n          android:textColor=\"@color/text_black_color\"\n          android:textSize=\"@dimen/text_size_normal\" />\n\n    </androidx.core.widget.NestedScrollView>\n\n\n    <androidx.appcompat.widget.AppCompatButton\n        android:id=\"@+id/cancel\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginStart=\"4dp\"\n        android:onClick=\"@{(v) -> dialog.onClick(v)}\"\n        android:background=\"@drawable/ripple_white_selector\"\n        android:text=\"@{TextUtils.isEmpty(dialog.cancelText) ?  @string/cancel : dialog.cancelText}\"\n        android:visibility=\"@{dialog.showCancelBt ? View.VISIBLE : View.GONE}\"\n        android:textColor=\"@{dialog.resources.getColor(dialog.cancelBtTextColor)}\" />\n\n    <androidx.appcompat.widget.AppCompatButton\n        android:id=\"@+id/enter\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginStart=\"4dp\"\n        android:visibility=\"@{dialog.showEnterBt ? View.VISIBLE : View.GONE}\"\n        android:background=\"@drawable/ripple_white_selector\"\n        android:onClick=\"@{(v) -> dialog.onClick(v)}\"\n        android:text=\"@{TextUtils.isEmpty(dialog.enterText) ? @string/enter : dialog.enterText}\"\n        android:textColor=\"@{dialog.resources.getColor(dialog.enterBtTextColor)}\" />\n\n    <androidx.appcompat.widget.AppCompatButton\n        android:id=\"@+id/cover\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginStart=\"4dp\"\n        android:visibility=\"@{dialog.showCoverBt ? View.VISIBLE : View.GONE}\"\n        android:background=\"@drawable/ripple_white_selector\"\n        android:onClick=\"@{(v) -> dialog.onClick(v)}\"\n        android:text=\"@{TextUtils.isEmpty(dialog.coverText) ? @string/cover : dialog.coverText}\"\n        android:textColor=\"@{dialog.resources.getColor(dialog.coverBtTextColor)}\" />\n\n\n    <androidx.constraintlayout.helper.widget.Flow\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        app:constraint_referenced_ids=\"cover,enter,cancel\"\n        app:flow_horizontalStyle=\"packed\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@+id/msgScroll\" />\n\n  </androidx.constraintlayout.widget.ConstraintLayout>\n</layout>"
  },
  {
    "path": "app/src/main/res/layout/dialog_other_info.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n\n  <androidx.constraintlayout.widget.ConstraintLayout\n      style=\"@style/KpaDialogLayoutStyle\"\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"wrap_content\">\n\n    <TextView\n        android:id=\"@+id/title\"\n        style=\"@style/KpaTitleTextStyle\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"48dp\"\n        android:layout_gravity=\"center_horizontal\"\n        android:gravity=\"center_vertical\"\n        android:text=\"@string/ime_entry_other_info\"\n        android:textColor=\"@color/text_blue_color\"\n        android:textSize=\"@dimen/text_size_biggest\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\" />\n\n    <View\n        android:id=\"@+id/line\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"1dp\"\n        android:background=\"@color/line_color\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@+id/title\" />\n\n    <androidx.recyclerview.widget.RecyclerView\n        android:id=\"@+id/rvList\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        app:layout_constraintTop_toBottomOf=\"@+id/line\" />\n\n\n  </androidx.constraintlayout.widget.ConstraintLayout>\n</layout>\n"
  },
  {
    "path": "app/src/main/res/layout/dialog_otp_modify.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\">\n  <androidx.appcompat.widget.LinearLayoutCompat\n      android:layout_width=\"@dimen/dialog_min_width\"\n      android:layout_height=\"match_parent\"\n      android:minWidth=\"@dimen/dialog_min_width\"\n      android:orientation=\"vertical\"\n      style=\"@style/KpaDialogLayoutStyle\"\n      >\n\n    <androidx.appcompat.widget.AppCompatTextView\n        android:id=\"@+id/tvTitle\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"48dp\"\n        android:gravity=\"center\"\n        android:text=\"@string/modify_totp\"\n        android:textColor=\"@color/text_blue_color\"\n        android:textSize=\"@dimen/text_size_biggest\"\n        />\n\n    <include\n        layout=\"@layout/layout_otp_create_default\"\n        android:id=\"@+id/contentLayout\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        />\n\n  </androidx.appcompat.widget.LinearLayoutCompat>\n</layout>\n"
  },
  {
    "path": "app/src/main/res/layout/dialog_pass_key.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    >\n\n  <androidx.coordinatorlayout.widget.CoordinatorLayout\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"match_parent\"\n      >\n\n    <RelativeLayout\n        android:id=\"@+id/content\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"@color/white\"\n        android:orientation=\"vertical\"\n        app:layout_behavior=\"com.google.android.material.bottomsheet.BottomSheetBehavior\"\n        >\n\n      <TextView\n          android:id=\"@+id/title_temp\"\n          android:layout_width=\"match_parent\"\n          android:layout_height=\"48dp\"\n          android:background=\"@color/white\"\n          android:gravity=\"center\"\n          android:text=\"@string/choose_pass_key\"\n          android:textColor=\"@color/text_black_color\"\n          android:textSize=\"@dimen/text_size_biggest\"\n          />\n\n      <androidx.appcompat.widget.AppCompatImageButton\n          android:id=\"@+id/close\"\n          android:layout_width=\"48dp\"\n          android:layout_height=\"48dp\"\n          android:layout_alignParentRight=\"true\"\n          android:background=\"@drawable/ripple_primary_selector\"\n          app:srcCompat=\"@drawable/ic_close\"\n\n          />\n\n      <View\n          android:visibility=\"visible\"\n          android:id=\"@+id/line\"\n          android:layout_width=\"match_parent\"\n          android:layout_height=\"1dp\"\n          android:layout_below=\"@+id/title_temp\"\n          android:background=\"@color/line_color\"\n          />\n\n      <com.lyy.keepassa.widget.SimpleItemView\n          android:id=\"@+id/item_1\"\n          android:layout_width=\"match_parent\"\n          android:layout_height=\"wrap_content\"\n          android:layout_below=\"@+id/title_temp\"\n          android:clickable=\"true\"\n          app:des=\"@string/choose_pass_key_file_des\"\n          app:icon=\"@drawable/ic_android\"\n          app:title=\"@string/choose_file\"\n          />\n\n\n\n      <com.lyy.keepassa.widget.SimpleItemView\n          android:id=\"@+id/item_2\"\n          android:layout_width=\"match_parent\"\n          android:layout_height=\"wrap_content\"\n          android:layout_below=\"@+id/item_1\"\n          android:clickable=\"true\"\n          app:des=\"@string/create_pass_key_file_des\"\n          app:icon=\"@drawable/ic_new_file\"\n          app:title=\"@string/create_file\"\n          />\n\n    </RelativeLayout>\n\n  </androidx.coordinatorlayout.widget.CoordinatorLayout>\n\n</layout>"
  },
  {
    "path": "app/src/main/res/layout/dialog_path_type.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    >\n\n\n  <RelativeLayout\n      android:id=\"@+id/content\"\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"match_parent\"\n      android:background=\"@color/background_color\"\n      android:orientation=\"vertical\"\n      app:expand_h=\"320dp\"\n      >\n    <!--      app:layout_behavior=\"com.google.android.material.bottomsheet.BottomSheetBehavior\"-->\n\n    <RelativeLayout\n        android:id=\"@+id/head\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        >\n\n\n      <TextView\n          android:id=\"@+id/title\"\n          android:layout_width=\"match_parent\"\n          android:layout_height=\"48dp\"\n          android:background=\"@color/white\"\n          android:gravity=\"center\"\n          android:text=\"@string/hint_select_path_type\"\n          android:textColor=\"@color/text_black_color\"\n          android:textSize=\"@dimen/text_size_biggest\"\n          />\n\n      <androidx.appcompat.widget.AppCompatImageButton\n          android:id=\"@+id/close\"\n          android:layout_width=\"48dp\"\n          android:layout_height=\"48dp\"\n          android:layout_alignParentRight=\"true\"\n          android:background=\"@drawable/ripple_primary_selector\"\n          app:srcCompat=\"@drawable/ic_close\"\n\n          />\n\n      <View\n          android:id=\"@+id/line\"\n          android:layout_width=\"match_parent\"\n          android:layout_height=\"1dp\"\n          android:layout_below=\"@+id/title\"\n          android:background=\"@color/line_color\"\n          />\n    </RelativeLayout>\n\n    <androidx.recyclerview.widget.RecyclerView\n        android:id=\"@+id/list\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_below=\"@+id/head\"\n        />\n\n  </RelativeLayout>\n\n\n</layout>\n"
  },
  {
    "path": "app/src/main/res/layout/dialog_play_donate.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    >\n\n  <data>\n    <variable\n        name=\"dialog\"\n        type=\"com.lyy.keepassa.view.dialog.PlayDonateDialog\"\n        />\n  </data>\n\n  <androidx.constraintlayout.widget.ConstraintLayout\n      android:layout_width=\"@dimen/dialog_min_width\"\n      android:layout_height=\"wrap_content\"\n      android:elevation=\"4dp\"\n      style=\"@style/KpaDialogLayoutStyle\"\n      >\n\n    <com.lyy.keepassa.widget.BubbleTextView\n        android:id=\"@+id/tvTitle\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"48dp\"\n        android:layout_gravity=\"center_horizontal\"\n        android:gravity=\"center_vertical\"\n        android:text=\"@string/donate\"\n        android:textColor=\"@color/text_blue_color\"\n        android:textSize=\"@dimen/text_size_biggest\"\n        android:visibility=\"visible\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        app:icon_size=\"24dp\"\n        />\n\n    <View\n        android:id=\"@+id/vLine\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"1dp\"\n        android:background=\"@color/line_color\"\n        app:layout_constraintTop_toBottomOf=\"@+id/tvTitle\"\n        />\n\n\n    <com.google.android.material.slider.Slider\n        android:id=\"@+id/slider\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginEnd=\"46dp\"\n        android:layout_marginStart=\"16dp\"\n        android:layout_marginTop=\"40dp\"\n        android:stepSize=\"1\"\n        android:value=\"2\"\n        android:valueFrom=\"1\"\n        android:valueTo=\"5\"\n        app:layout_constraintEnd_toEndOf=\"@+id/cancel\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@+id/vLine\"\n        />\n\n    <androidx.appcompat.widget.AppCompatTextView\n        android:id=\"@+id/tvMoney\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginEnd=\"20dp\"\n        android:text=\"$5\"\n        android:textColor=\"@color/text_blue_color\"\n        android:textSize=\"@dimen/text_size_normal\"\n        app:layout_constraintBottom_toBottomOf=\"@+id/slider\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"@+id/slider\"\n        />\n\n    <androidx.appcompat.widget.AppCompatButton\n        android:id=\"@+id/cancel\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginStart=\"4dp\"\n        android:background=\"@drawable/ripple_white_selector\"\n        android:onClick=\"@{(v) -> dialog.onClick(v)}\"\n        android:text=\"@string/cancel\"\n        android:textColor=\"@color/text_gray_color\"\n        />\n\n    <androidx.appcompat.widget.AppCompatButton\n        android:id=\"@+id/enter\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginStart=\"4dp\"\n        android:background=\"@drawable/ripple_white_selector\"\n        android:onClick=\"@{(v) -> dialog.onClick(v)}\"\n        android:text=\"@string/enter\"\n        android:textColor=\"@color/text_blue_color\"\n        />\n\n\n    <androidx.constraintlayout.helper.widget.Flow\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginTop=\"16dp\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@+id/slider\"\n        app:constraint_referenced_ids=\"cancel, enter\"\n        app:flow_horizontalStyle=\"packed\"\n        />\n\n  </androidx.constraintlayout.widget.ConstraintLayout>\n</layout>"
  },
  {
    "path": "app/src/main/res/layout/dialog_quick_unlock.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    >\n\n  <RelativeLayout\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"match_parent\"\n      android:background=\"@color/background_color\"\n      >\n\n    <com.airbnb.lottie.LottieAnimationView\n        android:id=\"@+id/anim\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        app:lottie_autoPlay=\"true\"\n        app:lottie_loop=\"true\"\n        />\n\n    <TextView\n        android:id=\"@+id/title\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:layout_above=\"@+id/pass\"\n        android:layout_gravity=\"center\"\n        android:gravity=\"center\"\n        android:textColor=\"@color/text_black_color\"\n        android:textSize=\"24sp\"\n        android:visibility=\"gone\"\n        />\n\n\n    <!--    <com.lyy.keepassa.widget.VerifyCodeView-->\n    <com.lyy.keepassa.widget.ShortPasswordView\n        android:id=\"@+id/pass\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"49dp\"\n        android:layout_centerHorizontal=\"true\"\n        android:layout_centerVertical=\"true\"\n        android:layout_marginEnd=\"40dp\"\n        android:layout_marginStart=\"40dp\"\n        android:layout_marginTop=\"16dp\"\n        />\n\n\n    <com.airbnb.lottie.LottieAnimationView\n        android:id=\"@+id/fingerprint\"\n        android:layout_width=\"200dp\"\n        android:layout_height=\"200dp\"\n        android:layout_alignParentBottom=\"true\"\n        android:layout_centerHorizontal=\"true\"\n        android:visibility=\"gone\"\n        app:lottie_cacheComposition=\"true\"\n        app:lottie_autoPlay=\"true\"\n        app:lottie_loop=\"false\"\n        app:lottie_fileName=\"fingerprint_anim.json\"\n        />\n\n\n    <TextView\n        android:id=\"@+id/change_db\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_alignParentBottom=\"true\"\n        android:layout_alignParentRight=\"true\"\n        android:background=\"@drawable/ripple_white_selector\"\n        android:clickable=\"true\"\n        android:padding=\"16dp\"\n        android:text=\"@string/change_db\"\n        android:textColor=\"@color/text_blue_color\"\n        android:textSize=\"@dimen/text_size_normal\"\n        />\n\n  </RelativeLayout>\n\n</layout>"
  },
  {
    "path": "app/src/main/res/layout/dialog_search.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    >\n\n  <RelativeLayout\n      android:id=\"@+id/bg\"\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"match_parent\"\n      android:background=\"@color/mask\"\n      >\n\n    <androidx.appcompat.widget.SearchView\n        android:id=\"@+id/search\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_margin=\"16dp\"\n        android:background=\"@drawable/bg_white_radius_2\"\n        android:elevation=\"4dp\"\n        android:imeOptions=\"actionSearch\"\n        app:goIcon=\"@drawable/ic_search\"\n        app:queryHint=\"@string/hint_query\"\n        app:searchIcon=\"@drawable/ic_view_headline_24px\"\n        />\n\n    <androidx.recyclerview.widget.RecyclerView\n        android:id=\"@+id/list\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_below=\"@+id/search\"\n        android:layout_alignStart=\"@+id/search\"\n        android:layout_alignEnd=\"@+id/search\"\n        android:background=\"@drawable/bg_white_radius_2\"\n        />\n  </RelativeLayout>\n</layout>"
  },
  {
    "path": "app/src/main/res/layout/dialog_timer.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n\n  <RelativeLayout\n      style=\"@style/KpaDialogLayoutStyle\"\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"match_parent\"\n      android:minWidth=\"@dimen/dialog_min_width\"\n      android:orientation=\"vertical\">\n\n    <com.google.android.material.tabs.TabLayout\n        android:id=\"@+id/tab_Layout\"\n        style=\"@style/KapTabLayoutStyle\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        app:tabInlineLabel=\"true\" />\n\n    <androidx.viewpager2.widget.ViewPager2\n        android:id=\"@+id/vp\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:layout_above=\"@+id/bottom_bar\"\n        android:layout_below=\"@+id/tab_Layout\" />\n\n    <LinearLayout\n        android:id=\"@+id/bottom_bar\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_alignParentEnd=\"true\"\n        android:layout_alignParentBottom=\"true\"\n        android:layout_marginEnd=\"16dp\"\n        android:layout_marginBottom=\"8dp\"\n        android:orientation=\"horizontal\">\n\n      <TextView\n          android:id=\"@+id/cancel\"\n          android:layout_width=\"wrap_content\"\n          android:layout_height=\"wrap_content\"\n          android:layout_marginEnd=\"24dp\"\n          android:background=\"@drawable/ripple_white_selector\"\n          android:clickable=\"true\"\n          android:padding=\"8dp\"\n          android:text=\"@string/cancel\"\n          android:textColor=\"@color/blue\"\n          android:textSize=\"@dimen/text_size_normal\" />\n\n      <TextView\n          android:id=\"@+id/save\"\n          android:layout_width=\"wrap_content\"\n          android:layout_height=\"wrap_content\"\n          android:background=\"@drawable/ripple_white_selector\"\n          android:clickable=\"true\"\n          android:padding=\"8dp\"\n          android:text=\"@string/save\"\n          android:textColor=\"@color/blue\"\n          android:textSize=\"@dimen/text_size_normal\" />\n\n    </LinearLayout>\n\n  </RelativeLayout>\n</layout>"
  },
  {
    "path": "app/src/main/res/layout/dialog_tip.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:bind=\"http://schemas.android.com/tools\"\n    >\n\n  <data>\n    <variable\n        name=\"msgTitle\"\n        type=\"java.lang.CharSequence\" />\n  </data>\n\n  <androidx.constraintlayout.widget.ConstraintLayout\n      android:layout_width=\"@dimen/dialog_min_width\"\n      android:layout_height=\"wrap_content\"\n      style=\"@style/KpaDialogLayoutStyle\"\n      >\n\n    <include\n        android:id=\"@+id/includeLayout\"\n        layout=\"@layout/layout_dialog_title\"\n        bind:msgTitle=\"@{msgTitle}\"\n        />\n\n    <com.lyy.keepassa.widget.ScrollTextView\n        android:id=\"@+id/tvContent\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"200dp\"\n        android:paddingEnd=\"12dp\"\n        android:paddingStart=\"12dp\"\n        android:paddingTop=\"8dp\"\n        android:text=\"ssssss\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        android:layout_marginTop=\"@dimen/toolbar_h\"\n        />\n\n    <androidx.appcompat.widget.AppCompatButton\n        android:id=\"@+id/cancel\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginStart=\"4dp\"\n        android:background=\"@drawable/ripple_white_selector\"\n        android:text=\"@string/close\"\n        android:textColor=\"@color/colorPrimary\"\n        />\n\n    <androidx.appcompat.widget.AppCompatCheckBox\n        android:id=\"@+id/cbShow\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"@string/hint_dont_show_tips\"\n        android:textColor=\"@color/text_black_color\"\n        />\n\n    <androidx.constraintlayout.helper.widget.Flow\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"48dp\"\n        android:layout_marginStart=\"12dp\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@+id/tvContent\"\n        app:flow_horizontalStyle=\"spread_inside\"\n        app:constraint_referenced_ids=\"cbShow, cancel\"\n        />\n\n  </androidx.constraintlayout.widget.ConstraintLayout>\n</layout>"
  },
  {
    "path": "app/src/main/res/layout/dialog_totp_display.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    >\n\n  <data>\n\n    <variable\n        name=\"dialog\"\n        type=\"com.lyy.keepassa.view.dialog.otp.TotpDisplayDialog\"\n        />\n\n  </data>\n\n  <androidx.constraintlayout.widget.ConstraintLayout\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"wrap_content\"\n      android:elevation=\"4dp\"\n      style=\"@style/KpaDialogLayoutStyle\"\n      >\n\n    <androidx.appcompat.widget.AppCompatTextView\n        android:id=\"@+id/tvTitle\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center_horizontal\"\n        android:gravity=\"center_vertical\"\n        android:text=\"@string/kpa_totp\"\n        android:textColor=\"@color/text_blue_color\"\n        android:textSize=\"@dimen/text_size_biggest\"\n        />\n\n    <com.lyy.keepassa.widget.CountdownView\n        android:id=\"@+id/cvTime\"\n        android:layout_width=\"20dp\"\n        android:layout_height=\"20dp\"\n        app:cv_bar_unreached_color=\"@color/text_blue_color\"\n        app:cv_bar_reached_color=\"@color/color_BB757575\"\n        app:cv_bar_time=\"30\"\n        />\n\n    <androidx.constraintlayout.helper.widget.Flow\n        android:id=\"@+id/titleFlow\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"48dp\"\n        app:constraint_referenced_ids=\"tvTitle, cvTime\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        app:flow_verticalAlign=\"baseline\"\n        app:flow_horizontalGap=\"6dp\"\n        />\n\n    <View\n        android:id=\"@+id/vLine\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"1dp\"\n        android:background=\"@color/line_color\"\n        app:layout_constraintTop_toBottomOf=\"@+id/titleFlow\"\n        />\n\n\n    <androidx.appcompat.widget.AppCompatTextView\n        android:id=\"@+id/tvTotp\"\n        android:layout_width=\"wrap_content\"\n        android:textSize=\"50sp\"\n        android:layout_height=\"wrap_content\"\n        android:paddingTop=\"8dp\"\n        android:paddingBottom=\"8dp\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@+id/vLine\"\n        />\n\n    <androidx.appcompat.widget.AppCompatButton\n        android:id=\"@+id/cover\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"@drawable/ripple_white_selector\"\n        android:onClick=\"@{(v) -> dialog.onClick(v)}\"\n        android:text=\"@string/kpa_copy\"\n        android:textColor=\"@color/text_blue_color\"\n        app:layout_constraintTop_toBottomOf=\"@+id/tvTotp\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        />\n\n\n  </androidx.constraintlayout.widget.ConstraintLayout>\n\n</layout>"
  },
  {
    "path": "app/src/main/res/layout/dialog_upgrade.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n\n  <androidx.constraintlayout.widget.ConstraintLayout\n      style=\"@style/KpaDialogLayoutStyle\"\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"match_parent\"\n      android:animateLayoutChanges=\"true\"\n      android:orientation=\"vertical\">\n\n\n    <TextView\n        android:id=\"@+id/tvTitle\"\n        style=\"@style/KpaTitleTextStyle\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:gravity=\"center_horizontal\"\n        android:paddingTop=\"16dp\"\n        android:paddingBottom=\"16dp\"\n        android:text=\"@string/version_log\"\n        android:textStyle=\"bold\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\" />\n\n\n    <View\n        android:id=\"@+id/vLine\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"1dp\"\n        android:layout_below=\"@+id/tvTitle\"\n        android:background=\"@color/line_color\"\n        app:layout_constraintTop_toBottomOf=\"@+id/tvTitle\" />\n\n    <ScrollView\n        android:id=\"@+id/scrollView\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"0dp\"\n        android:layout_above=\"@+id/vLine1\"\n        android:layout_below=\"@+id/vLine\"\n        app:layout_constraintBottom_toTopOf=\"@+id/vLine1\"\n        app:layout_constraintTop_toBottomOf=\"@+id/tvTitle\">\n\n      <TextView\n          android:id=\"@+id/tvContent\"\n          android:layout_width=\"match_parent\"\n          android:layout_height=\"wrap_content\"\n          android:clickable=\"false\"\n          android:padding=\"16dp\" />\n\n    </ScrollView>\n\n    <View\n        android:id=\"@+id/vLine1\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"1dp\"\n        android:layout_above=\"@+id/btEnter\"\n        android:background=\"@color/line_color\"\n        app:layout_constraintBottom_toTopOf=\"@+id/btDonate\" />\n\n    <Button\n        android:id=\"@+id/btEnter\"\n        style=\"@style/KpaTitleTextStyle\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"@drawable/ripple_white_selector\"\n        android:text=\"@string/close\"\n        android:textColor=\"@color/text_blue_color\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintWidth_percent=\"0.5\" />\n\n    <com.lyy.keepassa.widget.DrawableTextView\n        android:id=\"@+id/btDonate\"\n        style=\"@style/KpaTitleTextStyle\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        android:background=\"@drawable/ripple_white_selector\"\n        android:drawablePadding=\"4dp\"\n        android:text=\"@string/donate\"\n        android:textColor=\"@color/text_blue_color\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"@+id/btEnter\"\n        app:layout_constraintWidth_percent=\"0.5\" />\n\n  </androidx.constraintlayout.widget.ConstraintLayout>\n</layout>\n"
  },
  {
    "path": "app/src/main/res/layout/dialog_webdav_login_new.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    >\n\n  <androidx.constraintlayout.widget.ConstraintLayout\n      android:layout_width=\"@dimen/dialog_min_width\"\n      android:layout_height=\"wrap_content\"\n      android:orientation=\"vertical\"\n      style=\"@style/KpaDialogLayoutStyle\"\n      >\n\n    <androidx.appcompat.widget.AppCompatTextView\n        android:id=\"@+id/title\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"48dp\"\n        android:layout_gravity=\"center_horizontal\"\n        android:gravity=\"center\"\n        android:text=\"WebDav\"\n        android:textColor=\"@color/text_blue_color\"\n        android:textSize=\"@dimen/text_size_biggest\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        />\n\n    <View\n        android:id=\"@+id/vLine\"\n        android:layout_width=\"@dimen/dialog_min_width\"\n        android:layout_height=\"1dp\"\n        android:background=\"@color/line_color\"\n        app:layout_constraintTop_toBottomOf=\"@+id/title\"\n        />\n\n\n    <com.google.android.material.textfield.TextInputLayout\n        android:id=\"@+id/uri_layout\"\n        android:layout_width=\"@dimen/dialog_ed_layout_width\"\n        android:layout_height=\"wrap_content\"\n        android:clickable=\"true\"\n        app:helperText=\"@string/helper_webdav_service\"\n        app:helperTextTextColor=\"@color/text_gray_color\"\n        app:startIconDrawable=\"@drawable/ic_http\"\n        app:endIconMode=\"custom\"\n        app:endIconDrawable=\"@drawable/ic_baseline_arrow_drop_down_24\"\n        app:startIconTint=\"@color/color_icon_grey\"\n        >\n\n      <com.google.android.material.textfield.MaterialAutoCompleteTextView\n          android:id=\"@+id/uri\"\n          android:layout_width=\"match_parent\"\n          android:layout_height=\"wrap_content\"\n          android:hint=\"@string/hint_webdav_url\"\n          android:imeOptions=\"actionNext\"\n          android:inputType=\"textUri\"\n          android:singleLine=\"true\"\n          android:textColor=\"@color/text_black_grey_color\"\n          android:textSize=\"@dimen/text_size_normal\"\n          style=\"@style/KpaEditTextStyle\"\n          />\n\n    </com.google.android.material.textfield.TextInputLayout>\n\n    <com.google.android.material.textfield.TextInputLayout\n        android:id=\"@+id/hostLayout\"\n        android:layout_width=\"268dp\"\n        android:layout_height=\"wrap_content\"\n        app:helperText=\"@string/webdav_host_name_hint\"\n        app:helperTextTextColor=\"@color/text_gray_color\"\n        app:startIconDrawable=\"@drawable/ic_server\"\n        app:startIconTint=\"@color/color_icon_grey\"\n        >\n\n      <com.google.android.material.textfield.TextInputEditText\n          android:id=\"@+id/host\"\n          android:layout_width=\"match_parent\"\n          android:layout_height=\"wrap_content\"\n          android:hint=\"@string/hostname\"\n          android:imeOptions=\"actionNext\"\n          android:inputType=\"textUri\"\n          android:singleLine=\"true\"\n          android:textColor=\"@color/text_black_grey_color\"\n          android:textSize=\"@dimen/text_size_normal\"\n          style=\"@style/KpaEditTextStyle\"\n          />\n\n\n    </com.google.android.material.textfield.TextInputLayout>\n\n    <com.google.android.material.textfield.TextInputEditText\n        android:id=\"@+id/edPort\"\n        android:layout_width=\"88dp\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginStart=\"12dp\"\n        android:gravity=\"center\"\n        android:hint=\"@string/port\"\n        android:inputType=\"number\"\n        android:maxLength=\"5\"\n        android:textColor=\"@color/text_black_grey_color\"\n        android:textSize=\"@dimen/text_size_normal\"\n        app:layout_constraintBottom_toBottomOf=\"@+id/hostLayout\"\n        app:layout_constraintStart_toEndOf=\"@+id/hostLayout\"\n        app:layout_constraintTop_toTopOf=\"@+id/hostLayout\"\n        tools:text=\"12345\"\n        />\n\n    <androidx.constraintlayout.widget.Group\n        android:id=\"@+id/groupHost\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:visibility=\"gone\"\n        app:constraint_referenced_ids=\"hostLayout, edPort\"\n        />\n\n\n    <com.google.android.material.textfield.TextInputLayout\n        android:id=\"@+id/user_layout\"\n        android:layout_width=\"@dimen/dialog_ed_layout_width\"\n        android:layout_height=\"wrap_content\"\n        app:startIconDrawable=\"@drawable/ic_user\"\n        app:startIconTint=\"@color/color_icon_grey\"\n        >\n\n      <com.google.android.material.textfield.TextInputEditText\n          android:id=\"@+id/user_name\"\n          android:layout_width=\"match_parent\"\n          android:layout_height=\"wrap_content\"\n          android:background=\"@color/transparent\"\n          android:hint=\"@string/hint_input_user_name\"\n          android:imeOptions=\"actionNext\"\n          android:inputType=\"text\"\n          android:singleLine=\"true\"\n          android:textColor=\"@color/text_black_grey_color\"\n          android:textColorHint=\"@color/text_hint_color\"\n          android:textSize=\"@dimen/text_size_normal\"\n          >\n\n      </com.google.android.material.textfield.TextInputEditText>\n\n    </com.google.android.material.textfield.TextInputLayout>\n\n    <com.google.android.material.textfield.TextInputLayout\n        android:id=\"@+id/password_layout\"\n        android:layout_width=\"@dimen/dialog_ed_layout_width\"\n        android:layout_height=\"wrap_content\"\n        app:passwordToggleEnabled=\"true\"\n        app:startIconDrawable=\"@drawable/ic_password\"\n        app:startIconTint=\"@color/color_icon_grey\"\n        app:helperTextTextColor=\"@color/color_d3ae5b\"\n        app:helperText=\"@string/hint_webdav_jgy\"\n        >\n\n      <com.google.android.material.textfield.TextInputEditText\n          android:id=\"@+id/password\"\n          android:layout_width=\"match_parent\"\n          android:layout_height=\"wrap_content\"\n          android:background=\"@color/transparent\"\n          android:hint=\"@string/password\"\n          android:imeOptions=\"actionNext\"\n          android:inputType=\"textPassword\"\n          android:singleLine=\"true\"\n          android:textColor=\"@color/text_black_grey_color\"\n          android:textColorHint=\"@color/text_hint_color\"\n          android:textSize=\"@dimen/text_size_normal\"\n          >\n\n      </com.google.android.material.textfield.TextInputEditText>\n\n    </com.google.android.material.textfield.TextInputLayout>\n\n    <androidx.constraintlayout.helper.widget.Flow\n        android:id=\"@+id/flowContent\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginTop=\"8dp\"\n        android:orientation=\"vertical\"\n        android:paddingEnd=\"16dp\"\n        android:paddingStart=\"16dp\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@+id/vLine\"\n        app:flow_horizontalAlign=\"start\"\n        app:flow_verticalGap=\"8dp\"\n        app:constraint_referenced_ids=\"uri_layout,hostLayout,user_layout,password_layout\"\n        />\n\n    <androidx.appcompat.widget.AppCompatButton\n        android:id=\"@+id/cancel\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"@drawable/ripple_white_selector\"\n        android:text=\"@string/cancel\"\n        android:textColor=\"@color/text_gray_color\"\n        />\n\n    <androidx.appcompat.widget.AppCompatButton\n        android:id=\"@+id/enter\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"@drawable/ripple_white_selector\"\n        android:text=\"@string/login\"\n        android:textColor=\"@color/text_blue_color\"\n        />\n\n    <androidx.appcompat.widget.AppCompatCheckBox\n        android:id=\"@+id/isPreemptive\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginStart=\"12dp\"\n        android:checked=\"false\"\n        android:text=\"@string/preemptive\"\n        android:textColor=\"@color/text_black_color\"\n        app:layout_constraintBottom_toBottomOf=\"@id/flow\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"@id/flow\"\n        />\n\n    <androidx.constraintlayout.helper.widget.Flow\n        android:id=\"@+id/flow\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginTop=\"16dp\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@+id/flowContent\"\n        app:constraint_referenced_ids=\"cancel, enter\"\n        />\n\n\n  </androidx.constraintlayout.widget.ConstraintLayout>\n\n</layout>"
  },
  {
    "path": "app/src/main/res/layout/fragment_change_db.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    >\n\n\n  <RelativeLayout\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"match_parent\"\n      android:background=\"@color/background_color\"\n      android:orientation=\"vertical\"\n      tools:context=\".view.launcher.LauncherActivity\"\n      >\n\n    <FrameLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:layout_above=\"@+id/content\"\n        >\n\n      <androidx.appcompat.widget.AppCompatImageView\n          android:id=\"@+id/icon\"\n          android:layout_width=\"120dp\"\n          android:layout_height=\"120dp\"\n          android:layout_gravity=\"center\"\n          android:src=\"@mipmap/ic_launcher_round\"\n          />\n    </FrameLayout>\n\n    <LinearLayout\n        android:id=\"@+id/content\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_centerVertical=\"true\"\n        android:orientation=\"vertical\"\n        >\n\n      <TextView\n          android:id=\"@+id/hint\"\n          android:layout_width=\"wrap_content\"\n          android:layout_height=\"wrap_content\"\n          android:layout_marginLeft=\"20dp\"\n          android:layout_marginTop=\"24dp\"\n          android:text=\"@string/open_db\"\n          android:textColor=\"@color/text_black_color\"\n          android:textSize=\"@dimen/text_size_normal\"\n          />\n\n      <androidx.recyclerview.widget.RecyclerView\n          android:id=\"@+id/list\"\n          android:layout_width=\"match_parent\"\n          android:layout_height=\"wrap_content\"\n          android:layout_marginLeft=\"16dp\"\n          android:layout_marginRight=\"16dp\"\n          android:layout_marginTop=\"4dp\"\n          android:overScrollMode=\"never\"\n          />\n    </LinearLayout>\n\n    <com.google.android.material.floatingactionbutton.FloatingActionButton\n        android:id=\"@+id/fab\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_alignParentBottom=\"true\"\n        android:layout_alignParentEnd=\"true\"\n        android:layout_marginBottom=\"20dp\"\n        android:layout_marginEnd=\"20dp\"\n        android:clickable=\"true\"\n        android:contentDescription=\"xxxx\"\n        android:src=\"@drawable/ic_add\"\n        app:backgroundTint=\"@color/colorPrimary\"\n        app:elevation=\"4dp\"\n        app:rippleColor=\"@color/ripple\"\n        />\n\n    <Button\n        android:id=\"@+id/test\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:onClick=\"onClick\"\n        android:text=\"test\"\n        android:visibility=\"gone\"\n        />\n\n\n  </RelativeLayout>\n</layout>"
  },
  {
    "path": "app/src/main/res/layout/fragment_create_db_first.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    >\n\n  <RelativeLayout\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"match_parent\"\n      android:background=\"@color/background_color\"\n      android:padding=\"16dp\"\n      >\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_centerVertical=\"true\"\n        android:orientation=\"vertical\"\n        >\n\n      <com.lyy.keepassa.widget.BubbleTextView\n          android:id=\"@+id/path_type\"\n          android:layout_width=\"wrap_content\"\n          android:layout_height=\"wrap_content\"\n          android:drawablePadding=\"8dp\"\n          android:gravity=\"center_vertical\"\n          android:text=\"@string/afs\"\n          android:textColor=\"@color/text_black_color\"\n          android:textSize=\"@dimen/text_size_normal\"\n          app:left_icon=\"@drawable/ic_android\"\n          app:right_icon=\"@drawable/ic_help_filled\"\n          />\n\n      <com.google.android.material.textfield.TextInputLayout\n          android:id=\"@+id/db_name_layout\"\n          android:layout_width=\"match_parent\"\n          android:layout_height=\"wrap_content\"\n          android:layout_marginTop=\"8dp\"\n          android:background=\"@color/transparent\"\n          app:errorEnabled=\"true\"\n          app:errorIconDrawable=\"@drawable/ic_info_filled\"\n          app:errorTextAppearance=\"@style/InputEditTextErrorStyle\"\n          app:helperText=\"@string/help_create_db\"\n          app:helperTextTextColor=\"@color/text_gray_color\"\n          app:hintTextAppearance=\"@style/InputEditTextHintStyle\"\n          >\n\n        <com.google.android.material.textfield.TextInputEditText\n            android:id=\"@+id/db_name\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"48dp\"\n            android:background=\"@color/transparent\"\n            android:hint=\"@string/db_name\"\n            android:imeOptions=\"actionDone\"\n            android:paddingLeft=\"4dp\"\n            android:paddingRight=\"4dp\"\n            android:singleLine=\"true\"\n            android:textColor=\"@color/text_black_grey_color\"\n            android:textColorHint=\"@color/text_hint_color\"\n            android:textSize=\"@dimen/text_size_normal\"\n            android:transitionName=\"@string/transition_db_name\"\n            />\n\n      </com.google.android.material.textfield.TextInputLayout>\n\n      <com.lyy.keepassa.widget.BubbleTextView\n          android:id=\"@+id/db_uri\"\n          android:layout_width=\"match_parent\"\n          android:layout_height=\"40dp\"\n          android:layout_marginTop=\"16dp\"\n          android:background=\"@drawable/ripple_primary_selector\"\n          android:drawablePadding=\"4dp\"\n          android:gravity=\"left|center_vertical\"\n          android:text=\"@string/hint_select_db_path\"\n          android:textColor=\"@color/text_orange_color\"\n          android:textSize=\"@dimen/text_size_small\"\n          android:visibility=\"gone\"\n          app:right_icon=\"@drawable/ic_help_filled\"\n          tools:ignore=\"RtlHardcoded\"\n          />\n\n    </LinearLayout>\n  </RelativeLayout>\n</layout>"
  },
  {
    "path": "app/src/main/res/layout/fragment_create_db_second.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    >\n\n  <androidx.core.widget.NestedScrollView\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"match_parent\"\n      android:background=\"@color/background_color\"\n      android:orientation=\"vertical\"\n      android:overScrollMode=\"never\"\n      android:padding=\"16dp\"\n      >\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:orientation=\"vertical\"\n        >\n\n      <LinearLayout\n          android:layout_width=\"match_parent\"\n          android:layout_height=\"wrap_content\"\n          android:orientation=\"horizontal\"\n          >\n\n        <TextView\n            android:gravity=\"center_vertical\"\n            android:id=\"@+id/db_hint\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:drawablePadding=\"8dp\"\n            android:text=\"@string/db\"\n            android:textColor=\"@color/text_black_color\"\n            android:textSize=\"@dimen/text_size_big\"\n            />\n\n        <View\n            android:layout_width=\"1dp\"\n            android:layout_height=\"26dp\"\n            android:layout_gravity=\"center_vertical\"\n            android:layout_marginLeft=\"8dp\"\n            android:background=\"@color/line_color\"\n            />\n\n        <com.google.android.material.textfield.TextInputEditText\n            android:id=\"@+id/db_name\"\n            android:gravity=\"center_vertical\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginLeft=\"8dp\"\n            android:background=\"@color/transparent\"\n            android:enabled=\"false\"\n            android:focusable=\"false\"\n            android:hint=\"@string/db_name\"\n            android:imeOptions=\"actionDone\"\n            android:paddingLeft=\"4dp\"\n            android:paddingRight=\"4dp\"\n            android:singleLine=\"true\"\n            android:textColor=\"@color/text_black_color\"\n            android:textColorHint=\"@color/text_hint_color\"\n            android:textSize=\"@dimen/text_size_normal\"\n            android:transitionName=\"@string/transition_db_name\"\n            />\n      </LinearLayout>\n\n      <com.lyy.keepassa.widget.BubbleTextView\n          android:id=\"@+id/encrypt_type\"\n          android:layout_width=\"wrap_content\"\n          android:layout_height=\"wrap_content\"\n          android:layout_marginTop=\"16dp\"\n          android:drawablePadding=\"4dp\"\n          android:gravity=\"center_vertical\"\n          android:text=\"@string/encrypt_type\"\n          android:textColor=\"@color/text_black_color\"\n          android:textSize=\"@dimen/text_size_big\"\n          app:icon_size=\"20dp\"\n          app:right_icon=\"@drawable/ic_help_filled\"\n          />\n\n      <RadioGroup\n          android:id=\"@+id/encrypt_group\"\n          android:layout_width=\"wrap_content\"\n          android:layout_height=\"wrap_content\"\n          android:layout_marginTop=\"8dp\"\n          android:orientation=\"horizontal\"\n          >\n        <RadioButton\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_weight=\"1\"\n            android:tag=\"1\"\n            android:text=\"@string/encrypt_type_1\"\n            android:textColor=\"@color/text_black_color\"\n            android:textSize=\"@dimen/text_size_normal\"\n            />\n\n        <RadioButton\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginLeft=\"16dp\"\n            android:layout_weight=\"1\"\n            android:tag=\"2\"\n            android:text=\"@string/encrypt_type_2\"\n            android:textColor=\"@color/text_black_color\"\n            android:textSize=\"@dimen/text_size_normal\"\n            />\n      </RadioGroup>\n\n      <TextView\n          android:layout_width=\"wrap_content\"\n          android:layout_height=\"wrap_content\"\n          android:layout_marginTop=\"16dp\"\n          android:text=\"@string/password\"\n          android:textColor=\"@color/text_black_color\"\n          android:textSize=\"@dimen/text_size_big\"\n          />\n\n      <com.google.android.material.textfield.TextInputLayout\n          android:id=\"@+id/password_layout\"\n          android:layout_width=\"match_parent\"\n          android:layout_height=\"wrap_content\"\n          android:layout_marginTop=\"8dp\"\n          app:counterEnabled=\"true\"\n          app:counterMaxLength=\"16\"\n          app:counterOverflowTextAppearance=\"@style/InputEditTextErrorStyle\"\n          app:counterTextAppearance=\"@style/InputEditTextOverAppearance\"\n          app:errorEnabled=\"true\"\n          app:errorIconDrawable=\"@drawable/ic_info_filled\"\n          app:errorTextAppearance=\"@style/InputEditTextErrorStyle\"\n          app:helperText=\"@string/helper_create_pass\"\n          app:helperTextTextColor=\"@color/text_gray_color\"\n          app:passwordToggleEnabled=\"true\"\n          >\n\n        <com.google.android.material.textfield.TextInputEditText\n            android:id=\"@+id/password\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:background=\"@color/transparent\"\n            android:hint=\"@string/hint_input_password\"\n            android:imeOptions=\"actionNext\"\n            android:inputType=\"textPassword\"\n            android:singleLine=\"true\"\n            android:textColor=\"@color/text_black_grey_color\"\n            android:textColorHint=\"@color/text_hint_color\"\n            android:textSize=\"@dimen/text_size_normal\"\n            >\n\n        </com.google.android.material.textfield.TextInputEditText>\n\n      </com.google.android.material.textfield.TextInputLayout>\n\n      <com.google.android.material.textfield.TextInputLayout\n          android:id=\"@+id/enter_password_layout\"\n          android:layout_width=\"match_parent\"\n          android:layout_height=\"wrap_content\"\n          android:layout_marginTop=\"8dp\"\n          app:errorEnabled=\"true\"\n          app:errorTextAppearance=\"@style/InputEditTextErrorStyle\"\n          app:helperTextTextColor=\"@color/text_gray_color\"\n          app:hintTextAppearance=\"@style/InputEditTextHintStyle\"\n          app:startIconTint=\"@color/color_icon_grey\"\n          >\n\n        <com.google.android.material.textfield.TextInputEditText\n            android:id=\"@+id/enter_password\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:background=\"@color/transparent\"\n            android:hint=\"@string/hint_enter_password\"\n            android:imeOptions=\"actionDone\"\n            android:inputType=\"textPassword\"\n            android:singleLine=\"true\"\n            android:textColor=\"@color/text_black_grey_color\"\n            android:textColorHint=\"@color/text_hint_color\"\n            android:textSize=\"@dimen/text_size_normal\"\n            >\n\n        </com.google.android.material.textfield.TextInputEditText>\n\n      </com.google.android.material.textfield.TextInputLayout>\n\n      <FrameLayout\n          android:id=\"@+id/pass_key_layout_wrap\"\n          android:layout_width=\"match_parent\"\n          android:layout_height=\"@dimen/create_pass_key_h\"\n          >\n\n        <RelativeLayout\n            android:id=\"@+id/pass_key_layout\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"@dimen/create_pass_key_h\"\n            android:layout_gravity=\"center_vertical\"\n            >\n\n          <com.lyy.keepassa.widget.BubbleTextView\n              android:id=\"@+id/pass_key\"\n              android:layout_width=\"wrap_content\"\n              android:layout_height=\"wrap_content\"\n              android:drawablePadding=\"4dp\"\n              android:gravity=\"center_vertical\"\n              android:text=\"@string/pass_key\"\n              android:textColor=\"@color/text_black_color\"\n              android:textSize=\"@dimen/text_size_big\"\n              app:icon_size=\"20dp\"\n              app:right_icon=\"@drawable/ic_help_filled\"\n              />\n\n          <EditText\n              android:id=\"@+id/pass_key_name\"\n              android:layout_width=\"match_parent\"\n              android:layout_height=\"48dp\"\n              android:layout_below=\"@+id/pass_key\"\n              android:layout_marginTop=\"4dp\"\n              android:layout_marginRight=\"8dp\"\n              android:layout_toLeftOf=\"@+id/choose_bt\"\n              android:background=\"@color/transparent\"\n              android:enabled=\"false\"\n              android:gravity=\"center_vertical\"\n              android:hint=\"@string/hint_set_pass_key\"\n              android:singleLine=\"true\"\n              android:textColor=\"@color/text_black_grey_color\"\n              android:textColorHint=\"@color/text_hint_color\"\n              android:textSize=\"@dimen/text_size_normal\"\n              />\n\n          <Button\n              android:id=\"@+id/choose_bt\"\n              android:layout_width=\"wrap_content\"\n              android:layout_height=\"wrap_content\"\n              android:layout_alignTop=\"@+id/pass_key_name\"\n              android:layout_alignParentRight=\"true\"\n              android:background=\"@drawable/ripple_white_selector\"\n              android:text=\"@string/choose_pass_key\"\n              android:textColor=\"@color/text_orange_color\"\n              android:textSize=\"@dimen/text_size_normal\"\n              />\n\n        </RelativeLayout>\n\n      </FrameLayout>\n    </LinearLayout>\n\n\n  </androidx.core.widget.NestedScrollView>\n\n</layout>"
  },
  {
    "path": "app/src/main/res/layout/fragment_entry_record.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n\n  <FrameLayout\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"match_parent\"\n      android:orientation=\"vertical\">\n\n    <androidx.swiperefreshlayout.widget.SwipeRefreshLayout\n        android:id=\"@+id/swipe\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\">\n\n      <androidx.recyclerview.widget.RecyclerView\n          android:id=\"@+id/list\"\n          android:layout_width=\"match_parent\"\n          android:layout_height=\"match_parent\"\n          android:overScrollMode=\"never\" />\n    </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>\n\n    <com.lyy.keepassa.widget.EmptyDataFillView\n        android:id=\"@+id/temp\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:visibility=\"gone\" />\n\n\n  </FrameLayout>\n\n</layout>"
  },
  {
    "path": "app/src/main/res/layout/fragment_fingerprint_close.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n  <RelativeLayout\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"match_parent\">\n\n    <TextView\n        android:id=\"@+id/tvHint\"\n        style=\"@style/KpaContentTextStyle\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_centerInParent=\"true\"\n        android:textSize=\"@dimen/text_size_biggest\" />\n  </RelativeLayout>\n</layout>\n"
  },
  {
    "path": "app/src/main/res/layout/fragment_fingerprint_desx.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n  <LinearLayout\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"match_parent\"\n      android:orientation=\"vertical\"\n      android:padding=\"16dp\">\n\n    <RelativeLayout\n        android:id=\"@+id/rlQuick\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"@drawable/ripple_white_selector\"\n        android:clickable=\"true\"\n        android:padding=\"16dp\">\n\n      <RadioButton\n          android:id=\"@+id/scQuick\"\n          android:layout_width=\"wrap_content\"\n          android:layout_height=\"wrap_content\"\n          android:layout_alignParentEnd=\"true\"\n          android:layout_centerVertical=\"true\"\n          android:clickable=\"false\"\n          />\n\n      <TextView\n          style=\"@style/KpaContentTextStyle\"\n          android:layout_width=\"wrap_content\"\n          android:layout_height=\"wrap_content\"\n          android:layout_alignParentStart=\"true\"\n          android:layout_centerVertical=\"true\"\n          android:layout_marginTop=\"8dp\"\n          android:layout_marginEnd=\"16dp\"\n          android:layout_toLeftOf=\"@+id/scQuick\"\n          android:text=\"@string/quick_use_fingerprint\"\n          android:textSize=\"@dimen/text_size_bigger\" />\n    </RelativeLayout>\n\n    <View\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"1dp\"\n        android:layout_marginStart=\"16dp\"\n        android:layout_marginEnd=\"16dp\"\n        android:background=\"@color/line_color\" />\n\n    <RelativeLayout\n        android:id=\"@+id/rlFull\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_below=\"@+id/rlQuick\"\n        android:background=\"@drawable/ripple_white_selector\"\n        android:clickable=\"true\"\n        android:padding=\"16dp\">\n\n      <RadioButton\n          android:id=\"@+id/scFull\"\n          android:layout_width=\"wrap_content\"\n          android:layout_height=\"wrap_content\"\n          android:layout_alignParentEnd=\"true\"\n          android:layout_centerVertical=\"true\"\n          android:clickable=\"false\" />\n\n      <TextView\n          style=\"@style/KpaContentTextStyle\"\n          android:layout_width=\"wrap_content\"\n          android:layout_height=\"wrap_content\"\n          android:layout_alignParentStart=\"true\"\n          android:layout_centerVertical=\"true\"\n          android:layout_marginTop=\"8dp\"\n          android:layout_marginEnd=\"16dp\"\n          android:layout_toLeftOf=\"@+id/scFull\"\n          android:text=\"@string/hint_full_fingerprint\"\n          android:textSize=\"@dimen/text_size_bigger\" />\n    </RelativeLayout>\n\n  </LinearLayout>\n</layout>\n"
  },
  {
    "path": "app/src/main/res/layout/fragment_list.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n<!--  <androidx.constraintlayout.widget.ConstraintLayout-->\n<!--      android:layout_width=\"match_parent\"-->\n<!--      android:layout_height=\"match_parent\">-->\n\n    <androidx.recyclerview.widget.RecyclerView\n        android:id=\"@+id/rvList\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\" />\n\n<!--    <include layout=\"@layout/layout_loading\" />-->\n\n<!--  </androidx.constraintlayout.widget.ConstraintLayout>-->\n\n</layout>\n"
  },
  {
    "path": "app/src/main/res/layout/fragment_only_list.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n\n  <FrameLayout\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"match_parent\">\n\n    <androidx.swiperefreshlayout.widget.SwipeRefreshLayout\n        android:id=\"@+id/swipe\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\">\n\n      <androidx.recyclerview.widget.RecyclerView\n          android:id=\"@+id/list\"\n          android:layout_width=\"match_parent\"\n          android:layout_height=\"match_parent\"\n          android:overScrollMode=\"never\" />\n    </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>\n\n<!--    <androidx.recyclerview.widget.RecyclerView-->\n<!--        android:id=\"@+id/list\"-->\n<!--        android:layout_width=\"match_parent\"-->\n<!--        android:layout_height=\"match_parent\"-->\n<!--        android:overScrollMode=\"never\" />-->\n\n    <com.lyy.keepassa.widget.EmptyDataFillView\n        android:id=\"@+id/emptyView\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:visibility=\"gone\" />\n\n  </FrameLayout>\n\n</layout>"
  },
  {
    "path": "app/src/main/res/layout/fragment_open_db.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    >\n\n  <androidx.constraintlayout.widget.ConstraintLayout\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"match_parent\"\n      android:background=\"@color/background_color\"\n      android:fitsSystemWindows=\"true\"\n      >\n\n    <com.airbnb.lottie.LottieAnimationView\n        android:id=\"@+id/anim\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"150dp\"\n        android:layout_centerVertical=\"true\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        app:lottie_autoPlay=\"true\"\n        app:lottie_loop=\"false\"\n        />\n\n    <androidx.appcompat.widget.AppCompatTextView\n        android:id=\"@+id/head\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:gravity=\"center\"\n        android:text=\"@string/welcome\"\n        android:textColor=\"@color/text_black_color\"\n        android:textSize=\"24sp\"\n        android:visibility=\"gone\"\n        app:layout_constraintBottom_toBottomOf=\"@+id/anim\"\n        app:layout_constraintEnd_toEndOf=\"@+id/anim\"\n        app:layout_constraintStart_toStartOf=\"@+id/anim\"\n        app:layout_constraintTop_toTopOf=\"@+id/anim\"\n        />\n\n\n    <com.lyy.keepassa.widget.BubbleTextView\n        android:id=\"@+id/db\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:drawablePadding=\"4dp\"\n        android:ellipsize=\"middle\"\n        android:gravity=\"center_vertical\"\n        android:singleLine=\"true\"\n        android:text=\"@string/db\"\n        android:textColor=\"@color/text_black_color\"\n        android:textSize=\"@dimen/text_size_normal\"\n        app:icon_size=\"24dp\"\n        app:left_icon=\"@drawable/ic_android\"\n        />\n\n    <com.google.android.material.textfield.TextInputLayout\n        android:id=\"@+id/db_name_layout\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"@color/transparent\"\n        app:layout_constraintStart_toStartOf=\"@+id/db\"\n        app:layout_constraintTop_toBottomOf=\"@+id/db\"\n        app:endIconTint=\"@color/color_icon_grey\"\n        app:errorTextAppearance=\"@style/InputEditTextErrorStyle\"\n        app:hintTextAppearance=\"@style/InputEditTextHintStyle\"\n        app:passwordToggleDrawable=\"@drawable/selector_password\"\n        app:passwordToggleEnabled=\"true\"\n        >\n\n      <com.google.android.material.textfield.TextInputEditText\n          android:id=\"@+id/password\"\n          android:layout_width=\"match_parent\"\n          android:layout_height=\"wrap_content\"\n          android:hint=\"@string/password\"\n          android:imeOptions=\"actionDone\"\n          android:inputType=\"textPassword\"\n          android:paddingLeft=\"4dp\"\n          android:paddingRight=\"4dp\"\n          android:transitionName=\"@string/transition_db_name\"\n          style=\"@style/KpaEditTextStyle\"\n          />\n\n    </com.google.android.material.textfield.TextInputLayout>\n\n    <androidx.appcompat.widget.AppCompatCheckBox\n        android:id=\"@+id/cb_key\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_centerVertical=\"true\"\n        android:text=\"@string/need_key\"\n        android:textColor=\"@color/text_black_color\"\n        android:textSize=\"@dimen/text_size_normal\"\n        app:layout_constraintStart_toStartOf=\"@+id/anim\"\n        app:layout_constraintTop_toBottomOf=\"@+id/db_name_layout\"\n        />\n\n    <androidx.appcompat.widget.AppCompatTextView\n        android:id=\"@+id/key\"\n        android:layout_width=\"160dp\"\n        android:layout_height=\"@dimen/input_pass_key_h\"\n        android:layout_gravity=\"center_vertical|end\"\n        android:layout_marginEnd=\"24dp\"\n        android:clickable=\"true\"\n        android:ellipsize=\"end\"\n        android:gravity=\"center_vertical\"\n        android:maxLength=\"40\"\n        android:singleLine=\"true\"\n        android:text=\"@string/key\"\n        android:textColor=\"@color/text_black_color\"\n        android:textSize=\"@dimen/text_size_normal\"\n        app:layout_constraintBottom_toBottomOf=\"@+id/cb_key\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"@+id/cb_key\"\n        />\n\n    <androidx.constraintlayout.helper.widget.Flow\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:orientation=\"vertical\"\n        android:paddingEnd=\"24dp\"\n        android:paddingStart=\"24dp\"\n        app:layout_constraintBottom_toTopOf=\"@+id/fingerprint\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@+id/anim\"\n        app:layout_constraintVertical_bias=\"0.3\"\n        app:flow_horizontalAlign=\"start\"\n        app:flow_verticalGap=\"12dp\"\n        app:constraint_referenced_ids=\"db, db_name_layout, cb_key\"\n        />\n\n    <com.airbnb.lottie.LottieAnimationView\n        android:id=\"@+id/fingerprint\"\n        android:layout_width=\"200dp\"\n        android:layout_height=\"200dp\"\n        android:visibility=\"visible\"\n        app:layout_constraintBottom_toTopOf=\"@+id/line\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:lottie_autoPlay=\"true\"\n        app:lottie_cacheComposition=\"true\"\n        app:lottie_fileName=\"fingerprint_anim.json\"\n        app:lottie_loop=\"false\"\n        />\n\n\n    <View\n        android:id=\"@+id/line\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"1dp\"\n        android:background=\"@color/line_color\"\n        app:layout_constraintBottom_toTopOf=\"@+id/change_db\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        />\n\n\n    <androidx.appcompat.widget.AppCompatButton\n        android:id=\"@+id/change_db\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_alignParentStart=\"true\"\n        android:layout_marginStart=\"16dp\"\n        android:background=\"@drawable/ripple_white_selector\"\n        android:text=\"@string/change_db\"\n        android:textColor=\"@color/text_blue_color\"\n        android:textSize=\"@dimen/text_size_normal\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        />\n\n    <androidx.appcompat.widget.AppCompatButton\n        android:id=\"@+id/open\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_alignParentEnd=\"true\"\n        android:layout_marginEnd=\"16dp\"\n        android:background=\"@drawable/ripple_white_selector\"\n        android:text=\"@string/open\"\n        android:textColor=\"@color/text_blue_color\"\n        android:textSize=\"@dimen/text_size_normal\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"@+id/change_db\"\n        />\n\n\n  </androidx.constraintlayout.widget.ConstraintLayout>\n</layout>"
  },
  {
    "path": "app/src/main/res/layout/item_app_icon.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<com.google.android.material.imageview.ShapeableImageView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/ivIcon\"\n    android:layout_width=\"36dp\"\n    android:layout_height=\"36dp\"\n    android:background=\"@color/white\"\n    android:scaleType=\"fitXY\"\n    tools:src=\"@drawable/ic_del\"\n    app:shapeAppearanceOverlay=\"@style/CircleStyle\"\n    app:strokeWidth=\"2dp\"\n    />\n"
  },
  {
    "path": "app/src/main/res/layout/item_auto_fill.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/llContent\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:background=\"@drawable/ripple_white_selector\"\n    android:orientation=\"horizontal\"\n    android:paddingBottom=\"8dp\"\n    android:paddingEnd=\"16dp\"\n    android:paddingStart=\"16dp\"\n    android:paddingTop=\"8dp\"\n    >\n\n  <ImageView\n      android:id=\"@+id/img\"\n      android:layout_width=\"32dp\"\n      android:layout_height=\"32dp\"\n      android:layout_centerVertical=\"true\"\n      android:src=\"@drawable/ic_android\"\n      />\n\n  <LinearLayout\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"wrap_content\"\n      android:layout_centerVertical=\"true\"\n      android:layout_marginStart=\"16dp\"\n      android:layout_toEndOf=\"@+id/img\"\n      android:orientation=\"vertical\"\n      >\n\n    <TextView\n        android:id=\"@+id/text\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center_vertical\"\n        android:ellipsize=\"end\"\n        android:singleLine=\"true\"\n        android:textColor=\"@color/text_black_color\"\n        android:textSize=\"@dimen/text_size_normal\"\n        />\n\n    <TextView\n        android:id=\"@+id/hint\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center_vertical\"\n        android:ellipsize=\"end\"\n        android:singleLine=\"true\"\n        android:textColor=\"@color/text_gray_color\"\n        android:textSize=\"@dimen/text_size_smaller\"\n        />\n  </LinearLayout>\n\n\n</RelativeLayout>"
  },
  {
    "path": "app/src/main/res/layout/item_choose_tag.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"36dp\"\n    >\n\n  <androidx.appcompat.widget.AppCompatImageView\n      android:id=\"@+id/ivIcon\"\n      android:layout_width=\"@dimen/icon_size\"\n      android:layout_height=\"@dimen/icon_size\"\n      android:layout_marginStart=\"16dp\"\n      app:layout_constraintBottom_toBottomOf=\"parent\"\n      app:layout_constraintStart_toStartOf=\"parent\"\n      app:layout_constraintTop_toTopOf=\"parent\"\n      app:srcCompat=\"@drawable/ic_baseline_label_24\"\n      />\n\n\n  <androidx.appcompat.widget.AppCompatTextView\n      android:id=\"@+id/tvTitle\"\n      android:layout_width=\"0dp\"\n      android:layout_height=\"wrap_content\"\n      android:layout_marginStart=\"12dp\"\n      android:ellipsize=\"end\"\n      android:maxLines=\"1\"\n      app:layout_constraintBottom_toBottomOf=\"@+id/ivIcon\"\n      app:layout_constraintEnd_toStartOf=\"@+id/cb\"\n      app:layout_constraintStart_toEndOf=\"@+id/ivIcon\"\n      app:layout_constraintTop_toTopOf=\"@+id/ivIcon\"\n      tools:text=\"标签，标签，标签，标签标签，标签标签，标签标签，标签标签，标签\"\n      style=\"@style/KpaContentTextStyle\"\n      />\n\n  <androidx.appcompat.widget.AppCompatCheckBox\n      android:id=\"@+id/cb\"\n      android:layout_width=\"wrap_content\"\n      android:layout_height=\"wrap_content\"\n      android:layout_marginEnd=\"16dp\"\n      android:gravity=\"center\"\n      android:clickable=\"false\"\n      android:focusable=\"false\"\n      app:layout_constraintBottom_toBottomOf=\"@+id/ivIcon\"\n      app:layout_constraintEnd_toEndOf=\"parent\"\n      app:layout_constraintTop_toTopOf=\"@+id/ivIcon\"\n      />\n\n\n</androidx.constraintlayout.widget.ConstraintLayout>"
  },
  {
    "path": "app/src/main/res/layout/item_cloud_file_list.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout 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    android:background=\"@drawable/ripple_primary_selector\"\n    android:descendantFocusability=\"blocksDescendants\"\n    android:orientation=\"vertical\"\n    android:paddingBottom=\"8dp\"\n    android:paddingLeft=\"16dp\"\n    android:paddingRight=\"16dp\"\n    android:paddingTop=\"8dp\"\n    >\n\n  <TextView\n      android:id=\"@+id/title\"\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"wrap_content\"\n      android:layout_marginLeft=\"24dp\"\n      android:layout_toRightOf=\"@+id/icon\"\n      android:ellipsize=\"end\"\n      android:singleLine=\"true\"\n      android:text=\"标题\"\n      android:textColor=\"@color/black\"\n      android:textSize=\"@dimen/text_size_normal\"\n      />\n\n  <TextView\n      android:id=\"@+id/des\"\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"wrap_content\"\n      android:layout_alignLeft=\"@+id/title\"\n      android:layout_below=\"@+id/title\"\n      android:layout_marginTop=\"4dp\"\n      android:ellipsize=\"end\"\n      android:singleLine=\"true\"\n      android:text=\"描述描述描述描述描述描述描述\"\n      android:textColor=\"@color/text_gray_color\"\n      android:textSize=\"@dimen/text_size_small\"\n      />\n\n  <androidx.appcompat.widget.AppCompatImageView\n      android:id=\"@+id/icon\"\n      android:layout_width=\"36dp\"\n      android:layout_height=\"36dp\"\n      android:layout_centerVertical=\"true\"\n      android:transitionName=\"@string/transition_entry_icon\"\n      app:srcCompat=\"@drawable/ic_android\"\n      />\n\n\n</RelativeLayout>"
  },
  {
    "path": "app/src/main/res/layout/item_entry.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout 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=\"60dp\"\n    android:background=\"@drawable/ripple_primary_selector\"\n    android:clickable=\"true\"\n    android:descendantFocusability=\"blocksDescendants\"\n    android:focusable=\"true\"\n    >\n\n  <androidx.appcompat.widget.AppCompatImageView\n      android:id=\"@+id/icon\"\n      android:layout_width=\"36dp\"\n      android:layout_height=\"36dp\"\n      android:layout_marginStart=\"16dp\"\n      android:transitionName=\"@string/transition_entry_icon\"\n      app:layout_constraintBottom_toBottomOf=\"parent\"\n      app:layout_constraintStart_toStartOf=\"parent\"\n      app:layout_constraintTop_toTopOf=\"parent\"\n      app:srcCompat=\"@drawable/ic_android\"\n      />\n\n  <androidx.constraintlayout.helper.widget.Flow\n      android:layout_width=\"0dp\"\n      android:layout_height=\"wrap_content\"\n      android:layout_marginEnd=\"24dp\"\n      android:layout_marginStart=\"24dp\"\n      android:orientation=\"vertical\"\n      app:layout_constraintBottom_toBottomOf=\"parent\"\n      app:layout_constraintEnd_toEndOf=\"parent\"\n      app:layout_constraintStart_toEndOf=\"@+id/icon\"\n      app:layout_constraintTop_toTopOf=\"parent\"\n      app:constraint_referenced_ids=\"title, des\"\n      />\n\n  <androidx.appcompat.widget.AppCompatTextView\n      android:id=\"@+id/title\"\n      android:layout_width=\"0dp\"\n      android:layout_height=\"wrap_content\"\n      android:ellipsize=\"end\"\n      android:maxLines=\"1\"\n      android:singleLine=\"true\"\n      android:text=\"标题\"\n      android:textColor=\"@color/text_black_color\"\n      android:textSize=\"@dimen/text_size_normal\"\n      />\n\n\n  <androidx.appcompat.widget.AppCompatTextView\n      android:id=\"@+id/des\"\n      android:layout_width=\"0dp\"\n      android:layout_height=\"wrap_content\"\n      android:text=\"描述描述描述描述描述描述描述\"\n      android:textColor=\"@color/text_gray_color\"\n      android:textSize=\"@dimen/text_size_small\"\n      />\n\n</androidx.constraintlayout.widget.ConstraintLayout>"
  },
  {
    "path": "app/src/main/res/layout/item_entry_other_info.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout 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    android:background=\"@drawable/ripple_white_selector\"\n    android:padding=\"16dp\">\n\n  <TextView\n      android:id=\"@+id/tvHint\"\n      style=\"@style/KpaContentTextStyle\"\n      android:layout_width=\"60dp\"\n      android:layout_height=\"wrap_content\"\n      android:ellipsize=\"end\"\n      android:maxWidth=\"60dp\"\n      android:singleLine=\"true\"\n      android:textColor=\"@color/text_black_color\"\n      app:layout_constraintBottom_toBottomOf=\"parent\"\n      app:layout_constraintStart_toStartOf=\"parent\"\n      app:layout_constraintTop_toTopOf=\"parent\" />\n\n  <View\n      android:id=\"@+id/line\"\n      android:layout_width=\"1dp\"\n      android:layout_height=\"8dp\"\n      android:layout_marginStart=\"8dp\"\n      android:layout_marginEnd=\"8dp\"\n      android:background=\"@color/text_black_grey_color\"\n      app:layout_constraintBottom_toBottomOf=\"parent\"\n      app:layout_constraintStart_toEndOf=\"@+id/tvHint\"\n      app:layout_constraintTop_toTopOf=\"parent\" />\n\n  <TextView\n      android:id=\"@+id/tvContent\"\n      style=\"@style/KpaContentTextStyle\"\n      android:layout_width=\"0dp\"\n      android:layout_height=\"wrap_content\"\n      android:layout_marginStart=\"8dp\"\n      android:layout_marginEnd=\"8dp\"\n      android:ellipsize=\"end\"\n      android:singleLine=\"true\"\n      android:textColor=\"@color/text_black_color\"\n      app:layout_constraintBottom_toBottomOf=\"parent\"\n      app:layout_constraintEnd_toStartOf=\"@+id/ivIcon\"\n      app:layout_constraintStart_toEndOf=\"@+id/line\"\n      app:layout_constraintTop_toTopOf=\"parent\" />\n\n  <ImageView\n      android:id=\"@+id/ivIcon\"\n      android:layout_width=\"wrap_content\"\n      android:layout_height=\"wrap_content\"\n      android:clickable=\"true\"\n      android:src=\"@drawable/selector_pass_visibility\"\n      android:visibility=\"gone\"\n      app:layout_constraintBottom_toBottomOf=\"parent\"\n      app:layout_constraintEnd_toEndOf=\"parent\"\n      app:layout_constraintTop_toTopOf=\"parent\" />\n\n</androidx.constraintlayout.widget.ConstraintLayout>"
  },
  {
    "path": "app/src/main/res/layout/item_icon.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:background=\"@drawable/ripple_white_selector\"\n    android:clickable=\"true\"\n    android:orientation=\"vertical\"\n    android:paddingLeft=\"8dp\"\n    android:paddingTop=\"16dp\"\n    android:paddingRight=\"8dp\"\n    android:paddingBottom=\"16dp\"\n    >\n\n  <androidx.appcompat.widget.AppCompatImageView\n      android:id=\"@+id/img\"\n      android:layout_width=\"30dp\"\n      android:layout_height=\"30dp\"\n      android:layout_centerHorizontal=\"true\"\n      />\n\n  <TextView\n      android:id=\"@+id/num\"\n      android:layout_width=\"wrap_content\"\n      android:layout_height=\"wrap_content\"\n      android:layout_below=\"@+id/img\"\n      android:layout_centerHorizontal=\"true\"\n      android:layout_marginTop=\"4dp\"\n      android:text=\"1\"\n      android:textColor=\"@color/text_black_color\"\n      android:textSize=\"@dimen/text_size_normal\"\n      />\n\n</RelativeLayout>"
  },
  {
    "path": "app/src/main/res/layout/item_ime_entry.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"wrap_content\"\n    android:layout_height=\"wrap_content\"\n    android:background=\"@drawable/bg_ime_entry\"\n    android:padding=\"8dp\">\n\n  <androidx.appcompat.widget.AppCompatImageView\n      android:id=\"@+id/icon\"\n      android:layout_width=\"24dp\"\n      android:layout_height=\"24dp\"\n      android:layout_centerVertical=\"true\"\n      android:transitionName=\"@string/transition_entry_icon\"\n      app:layout_constraintStart_toStartOf=\"parent\"\n      app:layout_constraintTop_toTopOf=\"parent\"\n      app:srcCompat=\"@drawable/ic_android\" />\n\n  <TextView\n      android:id=\"@+id/text\"\n      style=\"@style/KpaContentTextStyle\"\n      android:layout_width=\"wrap_content\"\n      android:layout_height=\"wrap_content\"\n      android:layout_gravity=\"center_vertical\"\n      android:layout_marginLeft=\"8dp\"\n      android:textColor=\"@color/text_black_color\"\n      app:layout_constraintBottom_toBottomOf=\"parent\"\n      app:layout_constraintStart_toEndOf=\"@+id/icon\"\n      app:layout_constraintTop_toTopOf=\"parent\" />\n\n</androidx.constraintlayout.widget.ConstraintLayout>"
  },
  {
    "path": "app/src/main/res/layout/item_mian_content.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.cardview.widget.CardView 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    android:layout_marginLeft=\"6dp\"\n    android:layout_marginTop=\"6dp\"\n    android:layout_marginRight=\"6dp\"\n    android:layout_marginBottom=\"6dp\"\n    android:clickable=\"true\"\n    android:focusable=\"true\"\n    android:foreground=\"@drawable/ripple_white_selector\"\n    app:cardBackgroundColor=\"@color/white\"\n    app:cardCornerRadius=\"4dp\"\n    app:cardElevation=\"4dp\"\n    app:cardMaxElevation=\"8dp\"\n    >\n\n  <RelativeLayout\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"wrap_content\"\n      android:layout_marginTop=\"20dp\"\n      android:layout_marginBottom=\"20dp\"\n      >\n\n    <androidx.appcompat.widget.AppCompatImageView\n        android:id=\"@+id/img\"\n        android:layout_width=\"30dp\"\n        android:layout_height=\"30dp\"\n        android:layout_centerHorizontal=\"true\"\n        app:srcCompat=\"@drawable/ic_onedrive\"\n        />\n\n    <TextView\n        android:id=\"@+id/text\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_below=\"@+id/img\"\n        android:layout_centerHorizontal=\"true\"\n        android:layout_marginTop=\"8dp\"\n        android:text=\"xxx\"\n        android:textColor=\"@color/text_black_color\"\n        android:textSize=\"@dimen/text_size_smallest\"\n        />\n\n\n  </RelativeLayout>\n\n</androidx.cardview.widget.CardView>"
  },
  {
    "path": "app/src/main/res/layout/item_path_type.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout 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    android:background=\"@drawable/ripple_primary_selector\"\n    android:clickable=\"true\"\n    android:descendantFocusability=\"blocksDescendants\"\n    android:focusable=\"true\"\n    android:paddingBottom=\"8dp\"\n    android:paddingLeft=\"16dp\"\n    android:paddingRight=\"16dp\"\n    android:paddingTop=\"8dp\"\n    >\n\n  <com.google.android.material.imageview.ShapeableImageView\n      android:id=\"@+id/icon\"\n      android:layout_width=\"36dp\"\n      android:layout_height=\"36dp\"\n      android:layout_centerVertical=\"true\"\n      app:layout_constraintStart_toStartOf=\"parent\"\n      app:layout_constraintTop_toTopOf=\"parent\"\n      app:srcCompat=\"@drawable/ic_android\"\n      />\n\n  <androidx.constraintlayout.helper.widget.Flow\n      android:layout_width=\"0dp\"\n      android:layout_height=\"0dp\"\n      android:layout_marginStart=\"24dp\"\n      android:orientation=\"vertical\"\n      app:layout_constraintBottom_toBottomOf=\"parent\"\n      app:layout_constraintEnd_toEndOf=\"parent\"\n      app:layout_constraintStart_toEndOf=\"@+id/icon\"\n      app:layout_constraintTop_toTopOf=\"parent\"\n      app:constraint_referenced_ids=\"title, des\"\n      />\n\n  <TextView\n      android:id=\"@+id/title\"\n      android:layout_width=\"0dp\"\n      android:layout_height=\"wrap_content\"\n      android:text=\"标题\"\n      android:textColor=\"@color/black\"\n      android:textSize=\"@dimen/text_size_normal\"\n      />\n\n  <TextView\n      android:id=\"@+id/des\"\n      android:layout_width=\"0dp\"\n      android:layout_height=\"wrap_content\"\n      android:text=\"描述描述描述描述描述描述描述\"\n      android:textColor=\"@color/text_gray_color\"\n      android:textSize=\"@dimen/text_size_small\"\n      />\n\n\n</androidx.constraintlayout.widget.ConstraintLayout>"
  },
  {
    "path": "app/src/main/res/layout/item_search_record.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:background=\"@drawable/ripple_white_selector\"\n    android:descendantFocusability=\"blocksDescendants\"\n    android:paddingStart=\"16dp\"\n    android:paddingTop=\"12dp\"\n    android:paddingEnd=\"16dp\"\n    android:paddingBottom=\"12dp\"\n    >\n\n  <androidx.appcompat.widget.AppCompatImageView\n      android:id=\"@+id/icon\"\n      android:layout_width=\"24dp\"\n      android:layout_height=\"24dp\"\n      android:src=\"@drawable/ic_history\"\n      />\n\n  <TextView\n      android:id=\"@+id/text\"\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"wrap_content\"\n      android:layout_centerVertical=\"true\"\n      android:layout_marginLeft=\"24dp\"\n      android:layout_marginRight=\"16dp\"\n      android:layout_toLeftOf=\"@+id/del\"\n      android:layout_toRightOf=\"@+id/icon\"\n      android:ellipsize=\"end\"\n      android:singleLine=\"true\"\n      android:textColor=\"@color/text_black_color\"\n      android:textSize=\"@dimen/text_size_normal\"\n      />\n\n  <androidx.appcompat.widget.AppCompatImageView\n      android:id=\"@+id/del\"\n      android:layout_width=\"24dp\"\n      android:layout_height=\"24dp\"\n      android:layout_alignParentRight=\"true\"\n      android:clickable=\"true\"\n      android:src=\"@drawable/ic_close\"\n      />\n\n</RelativeLayout>"
  },
  {
    "path": "app/src/main/res/layout/item_search_result.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout 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    android:background=\"@drawable/ripple_white_selector\"\n    android:descendantFocusability=\"blocksDescendants\"\n    android:paddingBottom=\"8dp\"\n    android:paddingEnd=\"16dp\"\n    android:paddingStart=\"16dp\"\n    android:paddingTop=\"8dp\"\n    >\n\n  <androidx.appcompat.widget.AppCompatImageView\n      android:id=\"@+id/icon\"\n      android:layout_width=\"30dp\"\n      android:layout_height=\"30dp\"\n      android:layout_centerVertical=\"true\"\n      android:src=\"@drawable/ic_history\"\n      app:layout_constraintBottom_toBottomOf=\"parent\"\n      app:layout_constraintStart_toStartOf=\"parent\"\n      app:layout_constraintTop_toTopOf=\"parent\"\n      />\n\n  <androidx.appcompat.widget.AppCompatTextView\n      android:id=\"@+id/text\"\n      android:layout_width=\"0dp\"\n      android:layout_height=\"wrap_content\"\n      android:ellipsize=\"end\"\n      android:singleLine=\"true\"\n      android:textColor=\"@color/text_black_color\"\n      android:textSize=\"@dimen/text_size_normal\"\n      />\n\n  <androidx.appcompat.widget.AppCompatTextView\n      android:id=\"@+id/des\"\n      android:layout_width=\"0dp\"\n      android:layout_height=\"wrap_content\"\n      android:textColor=\"@color/text_gray_color\"\n      android:textSize=\"@dimen/text_size_smaller\"\n      />\n\n  <androidx.constraintlayout.helper.widget.Flow\n      android:layout_width=\"0dp\"\n      android:layout_height=\"0dp\"\n      android:layout_marginStart=\"24dp\"\n      android:orientation=\"vertical\"\n      app:layout_constraintBottom_toBottomOf=\"parent\"\n      app:layout_constraintEnd_toEndOf=\"parent\"\n      app:layout_constraintStart_toEndOf=\"@+id/icon\"\n      app:layout_constraintTop_toTopOf=\"parent\"\n      app:flow_verticalGap=\"4dp\"\n      app:constraint_referenced_ids=\"text, des\"\n      />\n\n</androidx.constraintlayout.widget.ConstraintLayout>"
  },
  {
    "path": "app/src/main/res/layout/item_simple.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:background=\"@drawable/ripple_white_selector\"\n    android:orientation=\"horizontal\"\n    android:padding=\"16dp\"\n    >\n\n  <ImageView\n      android:id=\"@+id/img\"\n      android:layout_width=\"32dp\"\n      android:layout_height=\"32dp\"\n      android:src=\"@drawable/ic_android\"\n      />\n\n\n  <TextView\n      android:id=\"@+id/text\"\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"wrap_content\"\n      android:layout_gravity=\"center_vertical\"\n      android:layout_marginLeft=\"24dp\"\n      android:textColor=\"@color/text_black_color\"\n      android:textSize=\"@dimen/text_size_normal\"\n      />\n\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/layout_action_bar.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.appcompat.widget.Toolbar xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:id=\"@+id/kpa_toolbar\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"@dimen/bar_height\"\n    android:background=\"@color/background_color\"\n    android:elevation=\"4dp\"\n    app:navigationIcon=\"@drawable/ic_up\"\n    app:subtitleTextAppearance=\"@style/Toolbar.SubTitleText\"\n    app:theme=\"@style/ToolbarMenuTheme\"\n    app:titleTextAppearance=\"@style/Toolbar.TitleText\"\n    />\n"
  },
  {
    "path": "app/src/main/res/layout/layout_chip_harvest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<com.google.android.material.chip.Chip xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"wrap_content\"\n    android:layout_height=\"wrap_content\"\n    android:background=\"@drawable/ripple_white_selector\"\n    android:textColor=\"@color/text_black_color\"\n    android:textSize=\"14sp\"\n    app:chipIcon=\"@null\"\n    tools:text=\"GGG\"\n    style=\"@style/Widget.MaterialComponents.Chip.Choice\"\n    />"
  },
  {
    "path": "app/src/main/res/layout/layout_dialog_button.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    >\n\n  <data>\n    <import type=\"android.text.TextUtils\" />\n\n    <import type=\"android.view.View\" />\n\n    <variable\n        name=\"enterText\"\n        type=\"java.lang.CharSequence\"\n        />\n    <variable\n        name=\"cancelText\"\n        type=\"java.lang.CharSequence\"\n        />\n    <variable\n        name=\"hintEnterBt\"\n        type=\"Boolean\"\n        />\n\n    <variable\n        name=\"hintCancelBt\"\n        type=\"Boolean\"\n        />\n    <variable\n        name=\"clicker\"\n        type=\"com.lyy.keepassa.view.dialog.DialogBtnClicker\"\n        />\n    <variable\n        name=\"enableEnterBt\"\n        type=\"Boolean\"\n        />\n  </data>\n\n\n  <androidx.constraintlayout.widget.ConstraintLayout\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"48dp\"\n      >\n\n    <androidx.appcompat.widget.AppCompatButton\n        android:id=\"@+id/cancel\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginStart=\"4dp\"\n        android:background=\"@drawable/ripple_white_selector\"\n        android:onClick=\"@{(v) -> clicker.onCancel(v)}\"\n        android:text=\"@{TextUtils.isEmpty(cancelText) ?  @string/cancel : cancelText}\"\n        android:textColor=\"@color/selector_blue_gray_text_bg\"\n        android:visibility=\"@{hintCancelBt ? View.GONE : View.VISIBLE, default=visible}\"\n        />\n\n    <androidx.appcompat.widget.AppCompatButton\n        android:id=\"@+id/enter\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginStart=\"4dp\"\n        android:background=\"@drawable/ripple_white_selector\"\n        android:enabled=\"@{enableEnterBt, default=true}\"\n        android:onClick=\"@{(v) -> clicker.onEnter(v)}\"\n        android:text=\"@{TextUtils.isEmpty(enterText) ? @string/enter : enterText}\"\n        android:textColor=\"@color/selector_blue_gray_text_bg\"\n        android:visibility=\"@{hintEnterBt ? View.GONE : View.VISIBLE, default=visible}\"\n        />\n\n\n    <androidx.constraintlayout.helper.widget.Flow\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        app:constraint_referenced_ids=\"cancel, enter\"\n        app:flow_horizontalStyle=\"packed\"\n        />\n\n  </androidx.constraintlayout.widget.ConstraintLayout>\n</layout>"
  },
  {
    "path": "app/src/main/res/layout/layout_dialog_title.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    >\n\n  <data>\n\n    <variable\n        name=\"msgTitle\"\n        type=\"java.lang.CharSequence\"\n        />\n\n    <import type=\"android.text.TextUtils\" />\n\n    <import type=\"android.view.View\" />\n\n  </data>\n\n  <androidx.constraintlayout.widget.ConstraintLayout\n      android:id=\"@+id/layoutTitle\"\n      android:layout_width=\"wrap_content\"\n      android:layout_height=\"@dimen/toolbar_h\"\n      >\n\n    <com.lyy.keepassa.widget.BubbleTextView\n        android:id=\"@+id/tvTitle\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"47dp\"\n        android:layout_gravity=\"center_horizontal\"\n        android:gravity=\"center_vertical\"\n        android:text=\"@{TextUtils.isEmpty(msgTitle) ? @string/hint : msgTitle}\"\n        android:textColor=\"@color/text_blue_color\"\n        android:textSize=\"@dimen/text_size_biggest\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        app:icon_size=\"24dp\"\n        />\n\n    <View\n        android:id=\"@+id/vLine\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"1dp\"\n        android:background=\"@color/line_color\"\n        app:layout_constraintTop_toBottomOf=\"@+id/tvTitle\"\n        />\n\n  </androidx.constraintlayout.widget.ConstraintLayout>\n</layout>"
  },
  {
    "path": "app/src/main/res/layout/layout_empty_fill.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<com.lyy.keepassa.widget.EmptyDataFillView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/empty_layout\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\" />"
  },
  {
    "path": "app/src/main/res/layout/layout_entry_attachment.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:background=\"@drawable/ripple_white_selector\"\n    android:paddingBottom=\"8dp\"\n    android:paddingTop=\"8dp\"\n    >\n\n  <TextView\n      android:id=\"@+id/value\"\n      android:layout_width=\"wrap_content\"\n      android:layout_height=\"wrap_content\"\n      android:layout_alignParentStart=\"true\"\n      android:textColor=\"@color/text_black_grey_color\"\n      android:textSize=\"@dimen/text_size_smaller\"\n      app:layout_constraintStart_toStartOf=\"parent\"\n      app:layout_constraintTop_toTopOf=\"parent\"\n      tools:text=\"file\"\n      />\n\n  <TextView\n      android:id=\"@+id/add_more\"\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"wrap_content\"\n      android:layout_marginEnd=\"16dp\"\n      android:layout_marginTop=\"8dp\"\n      android:drawablePadding=\"16dp\"\n      android:gravity=\"center_vertical\"\n      android:paddingBottom=\"8dp\"\n      android:paddingTop=\"8dp\"\n      android:text=\"@string/add_attr_str\"\n      android:textColor=\"@color/text_black_color\"\n      android:textSize=\"@dimen/text_size_normal\"\n      android:visibility=\"gone\"\n      app:layout_constraintBottom_toBottomOf=\"parent\"\n      app:layout_constraintTop_toTopOf=\"parent\"\n      tools:ignore=\"RtlSymmetry\"\n      tools:visibility=\"visible\"\n      app:drawableStartCompat=\"@drawable/ic_add_24px\"\n      />\n\n\n</androidx.constraintlayout.widget.ConstraintLayout>"
  },
  {
    "path": "app/src/main/res/layout/layout_entry_card_base_info.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<com.google.android.material.card.MaterialCardView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:checkable=\"false\"\n    android:clickable=\"false\"\n    android:focusable=\"true\"\n    app:cardBackgroundColor=\"@color/color_444E85DB\"\n    app:cardCornerRadius=\"12dp\"\n    app:strokeColor=\"@color/transparent\"\n    app:cardElevation=\"0dp\"\n    style=\"?attr/materialCardViewElevatedStyle\"\n    >\n\n  <androidx.constraintlayout.widget.ConstraintLayout\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"wrap_content\"\n      android:padding=\"16dp\"\n      >\n\n    <androidx.appcompat.widget.AppCompatTextView\n        android:id=\"@+id/tvCardTitle\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"@string/base_info\"\n        android:textColor=\"@color/text_black_color\"\n        android:textSize=\"@dimen/text_size_big\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        />\n\n    <com.lyy.keepassa.widget.BubbleTextView\n        android:id=\"@+id/tvUserName\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"@dimen/entry_detail_item_h\"\n        android:layout_marginTop=\"12dp\"\n        android:ellipsize=\"end\"\n        android:singleLine=\"true\"\n        app:left_icon=\"@drawable/ic_user\"\n        tools:text=\"name\"\n        style=\"@style/KpaEntryDetailTextStyle\"\n        />\n\n    <com.lyy.keepassa.widget.BubbleTextView\n        android:id=\"@+id/tvPass\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"@dimen/entry_detail_item_h\"\n        android:layout_marginEnd=\"12dp\"\n        android:inputType=\"textPassword\"\n        android:longClickable=\"false\"\n        app:layout_constraintEnd_toStartOf=\"@+id/ivEye\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:left_icon=\"@drawable/ic_password\"\n        tools:text=\"name\"\n        style=\"@style/KpaEntryDetailTextStyle\"\n        />\n\n    <androidx.appcompat.widget.AppCompatImageView\n        android:id=\"@+id/ivEye\"\n        android:layout_width=\"24dp\"\n        android:layout_height=\"24dp\"\n        app:layout_constraintBottom_toBottomOf=\"@+id/tvPass\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"@+id/tvPass\"\n        app:srcCompat=\"@drawable/selector_pass_visibility\"\n        />\n\n    <com.lyy.keepassa.widget.BubbleTextView\n        android:id=\"@+id/tvUrl\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"@dimen/entry_detail_item_h\"\n        android:inputType=\"textUri\"\n        android:longClickable=\"false\"\n        app:left_icon=\"@drawable/ic_baseline_link_24\"\n        tools:text=\"net\"\n        style=\"@style/KpaEntryDetailTextStyle\"\n        />\n\n    <com.lyy.keepassa.widget.BubbleTextView\n        android:id=\"@+id/time1\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"@dimen/entry_detail_item_h\"\n        android:layout_marginTop=\"12dp\"\n        android:inputType=\"textUri\"\n        android:longClickable=\"false\"\n        tools:text=\"失效时间\"\n        app:left_icon=\"@drawable/ic_baseline_event_busy_24\"\n        style=\"@style/KpaEntryDetailTextStyle\"\n        />\n\n\n    <androidx.constraintlayout.helper.widget.Flow\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:orientation=\"vertical\"\n        app:layout_constraintTop_toBottomOf=\"@+id/tvCardTitle\"\n        app:constraint_referenced_ids=\"tvUserName, tvPass, tvUrl, time1\"\n        />\n\n  </androidx.constraintlayout.widget.ConstraintLayout>\n\n</com.google.android.material.card.MaterialCardView>"
  },
  {
    "path": "app/src/main/res/layout/layout_entry_card_list.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<com.google.android.material.card.MaterialCardView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    style=\"?attr/materialCardViewFilledStyle\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:checkable=\"false\"\n    android:clickable=\"false\"\n    android:focusable=\"true\"\n    app:cardBackgroundColor=\"@color/color_444E85DB\"\n    app:cardCornerRadius=\"12dp\"\n    app:cardElevation=\"0dp\">\n\n  <androidx.constraintlayout.widget.ConstraintLayout\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"wrap_content\"\n      android:padding=\"16dp\">\n\n    <androidx.appcompat.widget.AppCompatTextView\n        android:id=\"@+id/tvCardTitle\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:textColor=\"@color/text_black_color\"\n        android:textSize=\"@dimen/text_size_big\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        tools:text=\"自定义属性\" />\n\n    <androidx.recyclerview.widget.RecyclerView\n        android:id=\"@+id/rvList\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginTop=\"12dp\"\n        app:layout_constraintTop_toBottomOf=\"@+id/tvCardTitle\" />\n\n  </androidx.constraintlayout.widget.ConstraintLayout>\n\n</com.google.android.material.card.MaterialCardView>"
  },
  {
    "path": "app/src/main/res/layout/layout_entry_card_note.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<com.google.android.material.card.MaterialCardView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:checkable=\"false\"\n    android:clickable=\"false\"\n    android:focusable=\"true\"\n    app:cardBackgroundColor=\"@color/color_444E85DB\"\n    app:cardCornerRadius=\"12dp\"\n    app:cardElevation=\"0dp\"\n    style=\"?attr/materialCardViewFilledStyle\"\n    >\n\n  <androidx.constraintlayout.widget.ConstraintLayout\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"wrap_content\"\n      android:padding=\"16dp\"\n      >\n\n    <androidx.appcompat.widget.AppCompatTextView\n        android:id=\"@+id/tvCardTitle\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"@string/notice\"\n        android:textColor=\"@color/text_black_color\"\n        android:textSize=\"@dimen/text_size_big\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        />\n\n    <com.lyy.keepassa.widget.ExpandableTextView1\n        android:id=\"@+id/expandTv\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginTop=\"12dp\"\n        app:layout_constraintStart_toStartOf=\"@+id/tvCardTitle\"\n        app:layout_constraintTop_toBottomOf=\"@+id/tvCardTitle\"\n        app:maxCollapsedLines=\"3\"\n        />\n\n\n  </androidx.constraintlayout.widget.ConstraintLayout>\n\n</com.google.android.material.card.MaterialCardView>"
  },
  {
    "path": "app/src/main/res/layout/layout_entry_card_tag.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<com.google.android.material.card.MaterialCardView 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    android:checkable=\"false\"\n    android:clickable=\"false\"\n    android:focusable=\"true\"\n    app:cardBackgroundColor=\"@color/color_444E85DB\"\n    app:cardCornerRadius=\"12dp\"\n    app:cardElevation=\"0dp\"\n    style=\"?attr/materialCardViewFilledStyle\"\n    >\n\n  <androidx.constraintlayout.widget.ConstraintLayout\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"wrap_content\"\n      android:padding=\"16dp\"\n      >\n\n    <androidx.appcompat.widget.AppCompatTextView\n        android:id=\"@+id/tvCardTitle\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"@string/tag\"\n        android:textColor=\"@color/text_black_color\"\n        android:textSize=\"@dimen/text_size_big\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        />\n\n\n    <com.google.android.material.chip.ChipGroup\n        android:id=\"@+id/chipGroup\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginTop=\"12dp\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@+id/tvCardTitle\"\n        />\n\n  </androidx.constraintlayout.widget.ConstraintLayout>\n\n</com.google.android.material.card.MaterialCardView>"
  },
  {
    "path": "app/src/main/res/layout/layout_entry_create_str_card.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:background=\"@drawable/bg_str_attr\"\n    >\n\n  <View\n      android:id=\"@+id/vClick\"\n      android:layout_width=\"0dp\"\n      android:layout_height=\"0dp\"\n      android:background=\"@drawable/ripple_white_selector\"\n      app:layout_constraintBottom_toBottomOf=\"@+id/flowTitle\"\n      app:layout_constraintEnd_toEndOf=\"@+id/flowTitle\"\n      app:layout_constraintStart_toStartOf=\"@+id/flowTitle\"\n      app:layout_constraintTop_toTopOf=\"@+id/flowTitle\"\n      />\n\n  <androidx.appcompat.widget.AppCompatImageView\n      android:id=\"@+id/ivIcon\"\n      android:layout_width=\"24dp\"\n      android:layout_height=\"24dp\"\n      android:layout_marginStart=\"12dp\"\n      app:layout_constraintStart_toStartOf=\"parent\"\n      app:srcCompat=\"@drawable/ic_attr_str\"\n      />\n\n  <androidx.appcompat.widget.AppCompatTextView\n      android:id=\"@+id/tvTitle\"\n      android:layout_width=\"wrap_content\"\n      android:layout_height=\"wrap_content\"\n      android:layout_marginStart=\"16dp\"\n      android:text=\"@string/hint_attr\"\n      app:layout_constraintStart_toEndOf=\"@+id/ivIcon\"\n      style=\"@style/KpaTitleTextStyle\"\n      />\n\n  <androidx.appcompat.widget.AppCompatImageView\n      android:id=\"@+id/ivArrow\"\n      android:layout_width=\"24dp\"\n      android:layout_height=\"24dp\"\n      android:layout_marginEnd=\"16dp\"\n      app:layout_constraintEnd_toEndOf=\"parent\"\n      app:srcCompat=\"@drawable/ic_arrow_down\"\n      />\n\n  <androidx.constraintlayout.helper.widget.Flow\n      android:id=\"@+id/flowTitle\"\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"48dp\"\n      app:constraint_referenced_ids=\"ivIcon, tvTitle, ivArrow\"\n      />\n\n  <androidx.recyclerview.widget.RecyclerView\n      android:id=\"@+id/rvList\"\n      android:layout_width=\"0dp\"\n      android:layout_height=\"wrap_content\"\n      android:layout_marginStart=\"16dp\"\n      android:visibility=\"gone\"\n      app:layout_constraintEnd_toEndOf=\"parent\"\n      app:layout_constraintStart_toEndOf=\"@+id/ivIcon\"\n      app:layout_constraintTop_toBottomOf=\"@+id/flowTitle\"\n      tools:visibility=\"visible\"\n      />\n\n</androidx.constraintlayout.widget.ConstraintLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/layout_entry_str.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"54dp\"\n    android:background=\"@drawable/ripple_white_selector\"\n    >\n\n  <TextView\n      android:id=\"@+id/title\"\n      android:layout_width=\"wrap_content\"\n      android:layout_height=\"wrap_content\"\n      android:layout_centerVertical=\"true\"\n      android:layout_marginTop=\"8dp\"\n      android:textColor=\"@color/text_gray_color\"\n      android:textSize=\"@dimen/text_size_smallest\"\n      app:layout_constraintStart_toStartOf=\"parent\"\n      app:layout_constraintTop_toTopOf=\"parent\"\n      tools:text=\"标题\"\n      />\n\n  <com.lyy.keepassa.widget.pb.RoundProgressBarWidthNumber\n      android:id=\"@+id/rpbBar\"\n      android:layout_width=\"wrap_content\"\n      android:layout_height=\"wrap_content\"\n      android:layout_marginStart=\"8dp\"\n      android:progress=\"60\"\n      android:visibility=\"gone\"\n      app:layout_constraintBottom_toBottomOf=\"@+id/title\"\n      app:layout_constraintStart_toEndOf=\"@+id/title\"\n      app:layout_constraintTop_toTopOf=\"@+id/title\"\n      app:radius=\"8dp\"\n      app:progress_text_color=\"@color/text_blue_color\"\n      app:progress_text_size=\"8sp\"\n      app:progress_show_percent=\"false\"\n      app:progress_unreached_bar_height=\"1dp\"\n      />\n\n\n  <TextView\n      android:id=\"@+id/value\"\n      android:layout_width=\"wrap_content\"\n      android:layout_height=\"wrap_content\"\n      android:layout_alignParentStart=\"true\"\n      android:layout_marginTop=\"4dp\"\n      android:textColor=\"@color/text_black_grey_color\"\n      android:textSize=\"@dimen/text_size_smaller\"\n      app:layout_constraintStart_toStartOf=\"@id/title\"\n      app:layout_constraintTop_toBottomOf=\"@+id/title\"\n      tools:text=\"sss\"\n      />\n\n  <TextView\n      android:id=\"@+id/add_more\"\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"wrap_content\"\n      android:layout_marginEnd=\"16dp\"\n      android:layout_marginTop=\"8dp\"\n      android:drawablePadding=\"16dp\"\n      android:gravity=\"center_vertical\"\n      android:paddingBottom=\"8dp\"\n      android:paddingTop=\"8dp\"\n      android:text=\"@string/add_attr_str\"\n      android:textColor=\"@color/text_black_color\"\n      android:textSize=\"@dimen/text_size_normal\"\n      android:visibility=\"gone\"\n      app:layout_constraintBottom_toBottomOf=\"parent\"\n      app:layout_constraintTop_toTopOf=\"parent\"\n      tools:ignore=\"RtlSymmetry\"\n      app:drawableStartCompat=\"@drawable/ic_add_24px\"\n      />\n\n  <androidx.appcompat.widget.AppCompatImageView\n      android:id=\"@+id/ivEye\"\n      android:layout_width=\"24dp\"\n      android:layout_height=\"24dp\"\n      android:visibility=\"gone\"\n      app:layout_constraintBottom_toBottomOf=\"parent\"\n      app:layout_constraintEnd_toEndOf=\"parent\"\n      app:layout_constraintTop_toTopOf=\"parent\"\n      app:srcCompat=\"@drawable/selector_pass_visibility\"\n      tools:visibility=\"visible\"\n      />\n\n</androidx.constraintlayout.widget.ConstraintLayout>"
  },
  {
    "path": "app/src/main/res/layout/layout_expand_attr_child.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout 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    android:background=\"@drawable/ripple_white_selector\"\n    android:paddingTop=\"8dp\"\n    >\n\n  <TextView\n      android:id=\"@+id/title\"\n      android:layout_width=\"wrap_content\"\n      android:layout_height=\"wrap_content\"\n      android:textColor=\"@color/text_black_grey_color\"\n      android:textSize=\"@dimen/text_size_small\"\n      />\n\n  <TextView\n      android:id=\"@+id/value\"\n      android:layout_width=\"wrap_content\"\n      android:layout_height=\"wrap_content\"\n      android:layout_below=\"@+id/title\"\n      android:layout_alignParentStart=\"true\"\n      android:layout_marginTop=\"4dp\"\n      android:layout_toLeftOf=\"@+id/more\"\n      android:text=\"sss\"\n      android:textColor=\"@color/text_black_grey_color\"\n      android:textSize=\"@dimen/text_size_small\"\n      />\n\n  <androidx.appcompat.widget.AppCompatImageView\n      android:id=\"@+id/more\"\n      android:layout_width=\"wrap_content\"\n      android:layout_height=\"wrap_content\"\n      android:layout_alignTop=\"@+id/value\"\n      android:layout_alignBottom=\"@+id/value\"\n      android:layout_alignParentRight=\"true\"\n      app:srcCompat=\"@drawable/ic_more_read\"\n      />\n\n  <View\n      android:id=\"@+id/line\"\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"1dp\"\n      android:layout_below=\"@+id/value\"\n      android:layout_marginTop=\"8dp\"\n      android:background=\"@color/line_color\"\n      />\n\n</RelativeLayout>"
  },
  {
    "path": "app/src/main/res/layout/layout_expand_child_file.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:background=\"@drawable/ripple_white_selector\"\n    android:paddingTop=\"8dp\"\n    android:paddingBottom=\"8dp\"\n    >\n\n  <TextView\n      android:id=\"@+id/value\"\n      android:layout_width=\"wrap_content\"\n      android:layout_height=\"wrap_content\"\n      android:layout_below=\"@+id/title\"\n      android:layout_alignParentStart=\"true\"\n      android:layout_toStartOf=\"@+id/more\"\n      android:textColor=\"@color/text_black_grey_color\"\n      android:textSize=\"@dimen/text_size_smaller\"\n      tools:ignore=\"UnknownIdInLayout\"\n      />\n\n  <androidx.appcompat.widget.AppCompatImageView\n      android:id=\"@+id/more\"\n      android:layout_width=\"16dp\"\n      android:layout_height=\"16dp\"\n      android:layout_alignTop=\"@+id/value\"\n      android:layout_alignBottom=\"@+id/value\"\n      android:layout_alignParentEnd=\"true\"\n      app:srcCompat=\"@drawable/ic_more_read\"\n      />\n\n</RelativeLayout>"
  },
  {
    "path": "app/src/main/res/layout/layout_expand_child_str.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout 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    android:layout_marginBottom=\"12dp\"\n    android:background=\"@drawable/ripple_white_selector\"\n    >\n\n  <RelativeLayout\n      android:id=\"@+id/titleLayout\"\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"wrap_content\"\n      android:layout_toStartOf=\"@+id/more\"\n      >\n\n    <TextView\n        android:id=\"@+id/title\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_centerVertical=\"true\"\n        android:textColor=\"@color/text_gray_color\"\n        android:textSize=\"@dimen/text_size_smallest\"\n        />\n\n    <com.lyy.keepassa.widget.pb.RoundProgressBarWidthNumber\n        android:id=\"@+id/rpbBar\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginStart=\"8dp\"\n        android:layout_toEndOf=\"@+id/title\"\n        android:progress=\"60\"\n        android:visibility=\"gone\"\n        app:radius=\"8dp\"\n        app:progress_text_color=\"@color/text_blue_color\"\n        app:progress_text_size=\"8sp\"\n        app:progress_show_percent=\"false\"\n        app:progress_unreached_bar_height=\"1dp\"\n        />\n\n\n  </RelativeLayout>\n\n\n  <TextView\n      android:id=\"@+id/value\"\n      android:layout_width=\"wrap_content\"\n      android:layout_height=\"wrap_content\"\n      android:layout_alignParentStart=\"true\"\n      android:layout_below=\"@+id/titleLayout\"\n      android:layout_marginTop=\"4dp\"\n      android:layout_toStartOf=\"@+id/more\"\n      android:text=\"sss\"\n      android:textColor=\"@color/text_black_grey_color\"\n      android:textSize=\"@dimen/text_size_smaller\"\n      />\n\n  <androidx.appcompat.widget.AppCompatImageView\n      android:id=\"@+id/more\"\n      android:layout_width=\"16dp\"\n      android:layout_height=\"16dp\"\n      android:layout_alignParentEnd=\"true\"\n      android:layout_centerVertical=\"true\"\n      app:srcCompat=\"@drawable/ic_more_read\"\n      />\n\n</RelativeLayout>"
  },
  {
    "path": "app/src/main/res/layout/layout_expand_title.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout 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    android:clickable=\"false\"\n    android:descendantFocusability=\"blocksDescendants\"\n    android:animateLayoutChanges=\"true\"\n    >\n\n  <RelativeLayout\n      android:id=\"@+id/head\"\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"wrap_content\"\n      android:background=\"@drawable/ripple_white_selector\"\n      android:clickable=\"true\"\n      android:paddingTop=\"8dp\"\n      android:paddingBottom=\"8dp\"\n      >\n\n    <androidx.appcompat.widget.AppCompatImageView\n        android:id=\"@+id/attr_img\"\n        android:layout_width=\"24dp\"\n        android:layout_height=\"24dp\"\n        android:layout_centerVertical=\"true\"\n        app:srcCompat=\"@drawable/ic_attr_str\"\n        />\n\n    <TextView\n        android:id=\"@+id/attr_tt\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_centerVertical=\"true\"\n        android:layout_marginLeft=\"24dp\"\n        android:layout_toRightOf=\"@+id/attr_img\"\n        android:text=\"@string/hint_attr\"\n        android:textColor=\"@color/text_black_color\"\n        />\n\n    <androidx.appcompat.widget.AppCompatImageView\n        android:id=\"@+id/attr_arrow\"\n        android:layout_width=\"24dp\"\n        android:layout_height=\"24dp\"\n        android:layout_alignParentRight=\"true\"\n        android:layout_centerVertical=\"true\"\n        app:srcCompat=\"@drawable/ic_arrow_left_black\"\n        />\n  </RelativeLayout>\n\n  <LinearLayout\n      android:id=\"@+id/attrs\"\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"wrap_content\"\n      android:layout_below=\"@+id/head\"\n      android:layout_marginLeft=\"40dp\"\n      android:orientation=\"vertical\"\n      android:paddingRight=\"8dp\"\n      android:visibility=\"gone\"\n      />\n\n\n</RelativeLayout>"
  },
  {
    "path": "app/src/main/res/layout/layout_kpa_ime.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout 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    android:background=\"@color/color_E7EAED\"\n    android:paddingBottom=\"16dp\"\n    >\n\n\n  <View\n      android:id=\"@+id/line\"\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"1dp\"\n      android:background=\"@color/line_color\"\n      app:layout_constraintTop_toTopOf=\"parent\"\n      />\n\n  <androidx.recyclerview.widget.RecyclerView\n      android:id=\"@+id/rvContent\"\n      android:layout_width=\"0dp\"\n      android:layout_height=\"50dp\"\n      android:layout_marginEnd=\"8dp\"\n      android:layout_marginStart=\"8dp\"\n      android:layout_marginTop=\"16dp\"\n      android:visibility=\"gone\"\n      app:layout_constraintEnd_toEndOf=\"parent\"\n      app:layout_constraintStart_toStartOf=\"parent\"\n      app:layout_constraintTop_toBottomOf=\"@+id/line\"\n      />\n\n  <androidx.appcompat.widget.AppCompatImageView\n      android:id=\"@+id/ivSearch\"\n      android:layout_width=\"wrap_content\"\n      android:layout_height=\"wrap_content\"\n      android:clickable=\"true\"\n      android:focusable=\"true\"\n      android:visibility=\"gone\"\n      app:layout_constraintBottom_toBottomOf=\"@+id/rvContent\"\n      app:layout_constraintEnd_toEndOf=\"parent\"\n      app:layout_constraintTop_toTopOf=\"@+id/rvContent\"\n      app:srcCompat=\"@drawable/ic_search\"\n      />\n\n  <ImageView\n      android:id=\"@+id/btLock\"\n      android:layout_width=\"60dp\"\n      android:layout_height=\"wrap_content\"\n      android:layout_marginTop=\"16dp\"\n      android:src=\"@drawable/ic_ime_lock\"\n      android:text=\"lock\"\n      app:layout_constraintEnd_toStartOf=\"@+id/btAccount\"\n      app:layout_constraintHorizontal_bias=\"0.5\"\n      app:layout_constraintStart_toStartOf=\"parent\"\n      app:layout_constraintTop_toBottomOf=\"@+id/rvContent\"\n      style=\"@style/KpaImeKeyStyle\"\n      />\n\n  <ImageView\n      android:id=\"@+id/btAccount\"\n      android:layout_width=\"0dp\"\n      android:layout_height=\"wrap_content\"\n      android:src=\"@drawable/ic_ime_user\"\n      android:text=\"account\"\n      app:layout_constraintEnd_toStartOf=\"@+id/btPass\"\n      app:layout_constraintHorizontal_bias=\"0.5\"\n      app:layout_constraintStart_toEndOf=\"@+id/btLock\"\n      app:layout_constraintTop_toTopOf=\"@+id/btLock\"\n      style=\"@style/KpaImeKeyStyle\"\n      />\n\n  <TextView\n      android:id=\"@+id/btPass\"\n      android:layout_width=\"0dp\"\n      android:layout_height=\"40dp\"\n      android:gravity=\"center\"\n      android:src=\"@drawable/ic_ime_password\"\n      android:text=\"* * *\"\n      android:textColor=\"@color/color_4E85DB\"\n      app:layout_constraintBottom_toBottomOf=\"@+id/btLock\"\n      app:layout_constraintEnd_toStartOf=\"@+id/btClose\"\n      app:layout_constraintHorizontal_bias=\"0.5\"\n      app:layout_constraintStart_toEndOf=\"@+id/btAccount\"\n      app:layout_constraintTop_toTopOf=\"@+id/btLock\"\n      style=\"@style/KpaImeKeyStyle\"\n      />\n\n  <ImageView\n      android:id=\"@+id/btClose\"\n      android:layout_width=\"60dp\"\n      android:layout_height=\"wrap_content\"\n      android:layout_marginEnd=\"8dp\"\n      android:src=\"@drawable/ic_ime_close\"\n      android:text=\"close\"\n      app:layout_constraintEnd_toEndOf=\"parent\"\n      app:layout_constraintHorizontal_bias=\"0.5\"\n      app:layout_constraintStart_toEndOf=\"@+id/btPass\"\n      app:layout_constraintTop_toTopOf=\"@+id/btLock\"\n      style=\"@style/KpaImeKeyStyle\"\n      />\n\n\n  <ImageView\n      android:id=\"@+id/btChangeIme\"\n      android:layout_width=\"0dp\"\n      android:layout_height=\"wrap_content\"\n      android:layout_marginTop=\"16dp\"\n      android:src=\"@drawable/ic_ime_keyboard\"\n      android:text=\"ime\"\n      app:layout_constraintEnd_toStartOf=\"@+id/btTotp\"\n      app:layout_constraintHorizontal_bias=\"0.5\"\n      app:layout_constraintStart_toStartOf=\"parent\"\n      app:layout_constraintTop_toBottomOf=\"@+id/btLock\"\n      style=\"@style/KpaImeKeyStyle\"\n      />\n\n  <ImageView\n      android:id=\"@+id/btTotp\"\n      android:layout_width=\"0dp\"\n      android:layout_height=\"wrap_content\"\n      android:src=\"@drawable/ic_totp\"\n      android:text=\"totp\"\n      android:visibility=\"visible\"\n      app:layout_constraintEnd_toStartOf=\"@+id/btOtherInfo\"\n      app:layout_constraintHorizontal_bias=\"0.5\"\n      app:layout_constraintStart_toEndOf=\"@+id/btChangeIme\"\n      app:layout_constraintTop_toTopOf=\"@+id/btChangeIme\"\n      style=\"@style/KpaImeKeyStyle\"\n      />\n\n  <ImageView\n      android:id=\"@+id/btOtherInfo\"\n      android:layout_width=\"0dp\"\n      android:layout_height=\"wrap_content\"\n      android:src=\"@drawable/ic_ime_other_info\"\n      android:text=\"other\"\n      app:layout_constraintEnd_toStartOf=\"@+id/btBackspace\"\n      app:layout_constraintHorizontal_bias=\"0.5\"\n      app:layout_constraintStart_toEndOf=\"@+id/btTotp\"\n      app:layout_constraintTop_toTopOf=\"@+id/btChangeIme\"\n      style=\"@style/KpaImeKeyStyle\"\n      />\n\n  <ImageView\n      android:id=\"@+id/btBackspace\"\n      android:layout_width=\"0dp\"\n      android:layout_height=\"wrap_content\"\n      android:src=\"@drawable/ic_ime_backspace\"\n      android:text=\"del\"\n      app:layout_constraintEnd_toStartOf=\"@+id/btEnter\"\n      app:layout_constraintHorizontal_bias=\"0.5\"\n      app:layout_constraintStart_toEndOf=\"@+id/btOtherInfo\"\n      app:layout_constraintTop_toTopOf=\"@+id/btChangeIme\"\n      style=\"@style/KpaImeKeyStyle\"\n      />\n\n  <ImageView\n      android:id=\"@+id/btEnter\"\n      android:layout_width=\"0dp\"\n      android:layout_height=\"wrap_content\"\n      android:layout_marginEnd=\"8dp\"\n      android:src=\"@drawable/ic_ime_enter\"\n      android:text=\"enter\"\n      app:layout_constraintEnd_toEndOf=\"parent\"\n      app:layout_constraintHorizontal_bias=\"0.5\"\n      app:layout_constraintStart_toEndOf=\"@+id/btBackspace\"\n      app:layout_constraintTop_toTopOf=\"@+id/btChangeIme\"\n      style=\"@style/KpaImeKeyStyle\"\n      />\n\n\n</androidx.constraintlayout.widget.ConstraintLayout>"
  },
  {
    "path": "app/src/main/res/layout/layout_loading.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<com.airbnb.lottie.LottieAnimationView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:id=\"@+id/laAnim\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"@color/background_color\"\n    app:layout_constraintBottom_toBottomOf=\"parent\"\n    app:layout_constraintTop_toBottomOf=\"@+id/kpa_toolbar\"\n    app:lottie_autoPlay=\"false\"\n    app:lottie_fileName=\"loadingAnimation.json\"\n    app:lottie_loop=\"false\" />\n\n"
  },
  {
    "path": "app/src/main/res/layout/layout_otp_create_default.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    >\n\n  <com.google.android.material.textfield.TextInputLayout\n      android:id=\"@+id/str_key_layout\"\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"wrap_content\"\n      android:layout_marginEnd=\"16dp\"\n      android:layout_marginStart=\"16dp\"\n      app:layout_constraintStart_toStartOf=\"parent\"\n      app:layout_constraintTop_toTopOf=\"parent\"\n      app:errorIconDrawable=\"@drawable/ic_info_filled\"\n      app:errorTextAppearance=\"@style/InputEditTextErrorStyle\"\n      app:startIconDrawable=\"@drawable/ic_title_24px\"\n      app:startIconTint=\"@color/color_icon_grey\"\n      >\n\n    <com.google.android.material.textfield.TextInputEditText\n        android:id=\"@+id/str_key\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"@color/transparent\"\n        android:hint=\"@string/key\"\n        android:singleLine=\"true\"\n        android:textColor=\"@color/text_black_grey_color\"\n        android:textColorHint=\"@color/text_hint_color\"\n        android:textSize=\"@dimen/text_size_normal\"\n        >\n\n    </com.google.android.material.textfield.TextInputEditText>\n\n  </com.google.android.material.textfield.TextInputLayout>\n\n\n  <RadioGroup\n      android:id=\"@+id/rgTotp\"\n      android:layout_width=\"0dp\"\n      android:layout_height=\"wrap_content\"\n      android:layout_marginTop=\"16dp\"\n      android:orientation=\"vertical\"\n      android:paddingEnd=\"16dp\"\n      android:paddingStart=\"16dp\"\n      app:layout_constraintEnd_toEndOf=\"parent\"\n      app:layout_constraintStart_toStartOf=\"parent\"\n      app:layout_constraintTop_toBottomOf=\"@+id/str_key_layout\"\n      >\n\n    <RadioButton\n        android:id=\"@+id/rbDefault\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:paddingStart=\"8dp\"\n        android:tag=\"default\"\n        android:text=\"@string/totp_defaule\"\n        style=\"@style/KpaRadioButtonStyle\"\n        />\n\n    <RadioButton\n        android:id=\"@+id/rbSteam\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:paddingStart=\"8dp\"\n        android:tag=\"steam\"\n        android:text=\"@string/totp_steam\"\n        style=\"@style/KpaRadioButtonStyle\"\n        />\n\n    <RadioButton\n        android:id=\"@+id/rbCustom\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:paddingStart=\"8dp\"\n        android:tag=\"custom\"\n        android:text=\"@string/totp_custom\"\n        style=\"@style/KpaRadioButtonStyle\"\n        />\n  </RadioGroup>\n\n  <androidx.constraintlayout.widget.Group\n      android:id=\"@+id/group\"\n      android:layout_width=\"wrap_content\"\n      android:layout_height=\"wrap_content\"\n      android:visibility=\"gone\"\n      tools:visibility=\"visible\"\n      app:constraint_referenced_ids=\"tvSettingHint, textView, sp, textView1, slTime, textView2, slLen\"\n      />\n\n  <TextView\n      android:id=\"@+id/tvSettingHint\"\n      android:layout_width=\"wrap_content\"\n      android:layout_height=\"wrap_content\"\n      android:layout_marginStart=\"24dp\"\n      android:layout_marginTop=\"16dp\"\n      android:paddingBottom=\"12sp\"\n      android:text=\"@string/totp_custom_hint\"\n      app:layout_constraintStart_toStartOf=\"parent\"\n      app:layout_constraintTop_toBottomOf=\"@+id/rgTotp\"\n      style=\"@style/KpaTitleTextStyle\"\n      />\n\n  <TextView\n      android:id=\"@+id/textView\"\n      android:layout_width=\"wrap_content\"\n      android:layout_height=\"wrap_content\"\n      android:layout_marginStart=\"24dp\"\n      android:text=\"@string/totp_Arithmetic\"\n      app:layout_constraintBottom_toBottomOf=\"@+id/sp\"\n      app:layout_constraintStart_toStartOf=\"parent\"\n      app:layout_constraintTop_toTopOf=\"@+id/sp\"\n      style=\"@style/KpaContentTextStyle\"\n      />\n\n  <Spinner\n      android:id=\"@+id/sp\"\n      android:layout_width=\"200dp\"\n      android:layout_height=\"40dp\"\n      android:entries=\"@array/totp_arithmetic\"\n      app:layout_constraintStart_toEndOf=\"@+id/textView\"\n      app:layout_constraintTop_toBottomOf=\"@+id/tvSettingHint\"\n      />\n\n  <TextView\n      android:id=\"@+id/textView1\"\n      android:layout_width=\"wrap_content\"\n      android:layout_height=\"wrap_content\"\n      android:text=\"@string/totp_time\"\n      app:layout_constraintBottom_toBottomOf=\"@+id/slTime\"\n      app:layout_constraintEnd_toStartOf=\"@+id/slTime\"\n      app:layout_constraintHorizontal_chainStyle=\"spread\"\n      app:layout_constraintTop_toBottomOf=\"@+id/sp\"\n      style=\"@style/KpaContentTextStyle\"\n      />\n\n  <com.google.android.material.slider.Slider\n      android:id=\"@+id/slTime\"\n      android:layout_width=\"0dp\"\n      android:layout_height=\"wrap_content\"\n      android:stepSize=\"1\"\n      android:value=\"30\"\n      android:valueFrom=\"0\"\n      android:valueTo=\"60\"\n      android:valueType=\"intType\"\n      app:layout_constraintEnd_toEndOf=\"parent\"\n      app:layout_constraintStart_toStartOf=\"@+id/sp\"\n      app:layout_constraintTop_toBottomOf=\"@+id/sp\"\n      />\n\n\n  <TextView\n      android:id=\"@+id/textView2\"\n      android:layout_width=\"wrap_content\"\n      android:layout_height=\"wrap_content\"\n      android:text=\"@string/totp_len\"\n      app:layout_constraintBottom_toBottomOf=\"@+id/slLen\"\n      app:layout_constraintEnd_toStartOf=\"@+id/slLen\"\n      app:layout_constraintTop_toBottomOf=\"@+id/slTime\"\n      style=\"@style/KpaContentTextStyle\"\n      />\n\n  <com.google.android.material.slider.Slider\n      android:id=\"@+id/slLen\"\n      android:layout_width=\"0dp\"\n      android:layout_height=\"wrap_content\"\n      android:stepSize=\"1\"\n      android:value=\"6\"\n      android:valueFrom=\"6\"\n      android:valueTo=\"10\"\n      android:valueType=\"intType\"\n      app:layout_constraintEnd_toEndOf=\"parent\"\n      app:layout_constraintStart_toStartOf=\"@+id/slTime\"\n      app:layout_constraintTop_toBottomOf=\"@+id/slTime\"\n      />\n\n  <Button\n      android:id=\"@+id/cancel\"\n      android:layout_width=\"wrap_content\"\n      android:layout_height=\"wrap_content\"\n      android:background=\"@drawable/ripple_white_selector\"\n      android:text=\"@string/cancel\"\n      android:textColor=\"@color/text_gray_color\"\n      />\n\n  <Button\n      android:id=\"@+id/enter\"\n      android:layout_width=\"wrap_content\"\n      android:layout_height=\"wrap_content\"\n      android:background=\"@drawable/ripple_white_selector\"\n      android:text=\"@string/enter\"\n      android:textColor=\"@color/text_blue_color\"\n      />\n\n  <androidx.constraintlayout.helper.widget.Flow\n      android:layout_width=\"wrap_content\"\n      android:layout_height=\"wrap_content\"\n      android:layout_marginTop=\"8dp\"\n      app:layout_constraintEnd_toEndOf=\"parent\"\n      app:layout_constraintTop_toBottomOf=\"@+id/slLen\"\n      app:constraint_referenced_ids=\"cancel, enter\"\n      />\n\n\n</androidx.constraintlayout.widget.ConstraintLayout>"
  },
  {
    "path": "app/src/main/res/layout/layout_otp_create_menu.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout 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=\"100dp\"\n    >\n\n  <androidx.appcompat.widget.AppCompatImageView\n      android:id=\"@+id/ivNormal\"\n      android:layout_width=\"0dp\"\n      android:layout_height=\"0dp\"\n      android:background=\"@drawable/ripple_white_selector\"\n      android:clickable=\"true\"\n      android:focusable=\"true\"\n      android:paddingBottom=\"20dp\"\n      android:paddingTop=\"20dp\"\n      android:src=\"@drawable/ic_token_grey\"\n      app:layout_constraintBottom_toBottomOf=\"parent\"\n      app:layout_constraintStart_toStartOf=\"parent\"\n      app:layout_constraintTop_toTopOf=\"parent\"\n      app:layout_constraintWidth_percent=\"0.5\"\n      />\n\n  <androidx.appcompat.widget.AppCompatImageView\n      android:id=\"@+id/ivQrCode\"\n      android:layout_width=\"0dp\"\n      android:layout_height=\"0dp\"\n      android:background=\"@drawable/ripple_white_selector\"\n      android:clickable=\"true\"\n      android:focusable=\"true\"\n      android:paddingBottom=\"20dp\"\n      android:paddingTop=\"20dp\"\n      android:src=\"@drawable/ic_qr_code_scanner\"\n      app:layout_constraintBottom_toBottomOf=\"parent\"\n      app:layout_constraintEnd_toEndOf=\"parent\"\n      app:layout_constraintTop_toTopOf=\"parent\"\n      app:layout_constraintWidth_percent=\"0.5\"\n      />\n\n\n</androidx.constraintlayout.widget.ConstraintLayout>"
  },
  {
    "path": "app/src/main/res/menu/entry_detail_file_summary.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<menu xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n\n  <item\n      android:id=\"@+id/download_file\"\n      android:icon=\"@drawable/ic_save_24px\"\n      android:title=\"@string/download_file\"\n      app:showAsAction=\"ifRoom\" />\n\n  <item\n      android:id=\"@+id/open_whit_text\"\n      android:icon=\"@drawable/ic_text_fields_24px\"\n      android:title=\"@string/open_whit_text\"\n      app:showAsAction=\"ifRoom\" />\n\n  <item\n      android:id=\"@+id/open_whit_img\"\n      android:icon=\"@drawable/ic_image_24px\"\n      android:title=\"@string/open_whit_img\"\n      app:showAsAction=\"ifRoom\" />\n\n  <item\n      android:id=\"@+id/open_whit_other\"\n      android:icon=\"@drawable/ic_share_24px\"\n      android:title=\"@string/open_whit_other\"\n      app:showAsAction=\"ifRoom\" />\n</menu>"
  },
  {
    "path": "app/src/main/res/menu/entry_detail_fun_menu.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<menu xmlns:android=\"http://schemas.android.com/apk/res/android\">\n  <item\n      android:id=\"@+id/item_user\"\n      android:enabled=\"true\"\n      android:icon=\"@drawable/ic_add\"\n      android:title=\"@string/copy_user_name\"/>\n  <item\n      android:id=\"@+id/item_pass\"\n      android:enabled=\"true\"\n      android:icon=\"@drawable/ic_add\"\n      android:title=\"@string/copy_password\"/>\n\n  <item\n      android:id=\"@+id/item_totp\"\n      android:enabled=\"true\"\n      android:icon=\"@drawable/ic_add\"\n      android:title=\"@string/copy_totp\"/>\n\n  <item\n      android:id=\"@+id/item_open\"\n      android:enabled=\"true\"\n      android:icon=\"@drawable/ic_add\"\n      android:title=\"@string/transition_entry_icon\"/>\n</menu>"
  },
  {
    "path": "app/src/main/res/menu/entry_detail_text_summary.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<menu xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n\n\n  <item\n      android:id=\"@+id/copy_clip\"\n      android:icon=\"@drawable/ic_clipbord\"\n      android:title=\"@string/copy_to_clip\"\n      app:showAsAction=\"ifRoom\" />\n\n  <item\n      android:id=\"@+id/show_pass\"\n      android:icon=\"@drawable/ic_view_black\"\n      android:title=\"@string/show_pass\"\n      app:showAsAction=\"ifRoom\" />\n\n  <item\n      android:id=\"@+id/open_url\"\n      android:icon=\"@drawable/ic_net\"\n      android:title=\"@string/ope_url\"\n      app:showAsAction=\"ifRoom\" />\n\n</menu>"
  },
  {
    "path": "app/src/main/res/menu/entry_modify_file_summary.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<menu xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n\n\n  <item\n      android:id=\"@+id/remove_file\"\n      android:icon=\"@drawable/ic_del\"\n      android:title=\"@string/del_attr_file\"\n      app:showAsAction=\"ifRoom\" />\n\n  <item\n      android:id=\"@+id/open_file\"\n      android:icon=\"@drawable/ic_share_24px\"\n      android:title=\"@string/open_file\"\n      app:showAsAction=\"ifRoom\" />\n</menu>"
  },
  {
    "path": "app/src/main/res/menu/entry_modify_str_summary.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<menu xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n\n\n  <item\n      android:id=\"@+id/remove_str\"\n      android:icon=\"@drawable/ic_del\"\n      android:title=\"@string/del_attr_str\"\n      app:showAsAction=\"ifRoom\" />\n\n  <item\n      android:id=\"@+id/modify_text\"\n      android:icon=\"@drawable/ic_text_fields_24px\"\n      android:title=\"@string/modify_str_attr\"\n      app:showAsAction=\"ifRoom\" />\n</menu>"
  },
  {
    "path": "app/src/main/res/menu/menu_entry_detail.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<menu xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n\n  <item\n      android:id=\"@+id/history\"\n      android:icon=\"@drawable/ic_history_record\"\n      android:title=\"@string/history\"\n      android:visible=\"false\"\n      app:showAsAction=\"ifRoom\" />\n\n  <item\n      android:id=\"@+id/edit\"\n      android:icon=\"@drawable/ic_baseline_edit_24\"\n      android:title=\"@string/edit\"\n      app:showAsAction=\"ifRoom\" />\n\n  <item\n      android:id=\"@+id/collect\"\n      android:icon=\"@drawable/ic_star_outline\"\n      android:title=\"@string/collection\"\n      app:showAsAction=\"ifRoom\" />\n\n  <item\n      android:id=\"@+id/del\"\n      android:icon=\"@drawable/ic_del\"\n      android:title=\"@string/del_entry\"\n      app:showAsAction=\"withText\" />\n\n</menu>"
  },
  {
    "path": "app/src/main/res/menu/menu_entry_edit.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<menu xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n  <item\n      android:id=\"@+id/save\"\n      android:icon=\"@drawable/ic_save_24px\"\n      android:title=\"@string/save\"\n      app:showAsAction=\"ifRoom\" />\n\n  <item\n      android:id=\"@+id/cancel\"\n      android:icon=\"@drawable/ic_close\"\n      android:title=\"@string/cancel\"\n      app:showAsAction=\"ifRoom\" />\n</menu>"
  },
  {
    "path": "app/src/main/res/menu/menu_group_detail.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<menu xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n\n  <item\n      android:id=\"@+id/sort\"\n      android:icon=\"@drawable/ic_sort_by_char\"\n      android:title=\"@string/sort\"\n      app:showAsAction=\"ifRoom\">\n    <menu>\n\n      <item\n          android:id=\"@+id/sort_down_by_char\"\n          android:title=\"@string/sort_chart_desc\"\n          app:showAsAction=\"withText\" />\n\n      <item\n          android:id=\"@+id/sort_up_by_char\"\n          android:title=\"@string/sort_chart_asc\"\n          app:showAsAction=\"withText\" />\n\n      <item\n          android:id=\"@+id/sort_down_by_time\"\n          android:title=\"@string/sort_time_desc\"\n          app:showAsAction=\"withText\" />\n\n      <item\n          android:id=\"@+id/sort_up_by_time\"\n          android:title=\"@string/sort_time_asc\"\n          app:showAsAction=\"withText\" />\n\n    </menu>\n\n  </item>\n\n\n</menu>"
  },
  {
    "path": "app/src/main/res/menu/pop_create_entry_summary.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<menu xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n\n  <item\n      android:id=\"@+id/edit\"\n      android:icon=\"@drawable/ic_detail_edit\"\n      android:title=\"@string/edit\"\n      app:showAsAction=\"ifRoom\" />\n\n  <item\n      android:id=\"@+id/del\"\n      android:icon=\"@drawable/ic_del\"\n      android:title=\"@string/del_attr_str\"\n      app:showAsAction=\"ifRoom\" />\n\n</menu>"
  },
  {
    "path": "app/src/main/res/menu/pop_dele_history_record.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<menu xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n  <item\n      android:id=\"@+id/del\"\n      android:icon=\"@drawable/ic_del\"\n      android:title=\"@string/del_history\"\n      app:showAsAction=\"ifRoom\" />\n</menu>"
  },
  {
    "path": "app/src/main/res/menu/pop_entry_summary.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<menu xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n\n\n  <item\n      android:id=\"@+id/undo\"\n      android:icon=\"@drawable/ic_undo_entry\"\n      android:title=\"@string/undo_entry\"\n      app:showAsAction=\"ifRoom\" />\n\n  <item\n      android:id=\"@+id/copy_user\"\n      android:icon=\"@drawable/ic_user\"\n      android:title=\"@string/copy_user_name\"\n      app:showAsAction=\"ifRoom\" />\n\n  <item\n      android:id=\"@+id/copy_pw\"\n      android:icon=\"@drawable/ic_password\"\n      android:title=\"@string/copy_password\"\n      app:showAsAction=\"ifRoom\" />\n\n  <item\n      android:id=\"@+id/copy_totp\"\n      android:icon=\"@drawable/ic_token_grey\"\n      android:title=\"@string/kpa_totp\"\n      app:showAsAction=\"ifRoom\" />\n\n\n  <item\n      android:id=\"@+id/del\"\n      android:icon=\"@drawable/ic_del\"\n      android:title=\"@string/del_entry\"\n      app:showAsAction=\"ifRoom\" />\n\n  <item\n      android:id=\"@+id/move\"\n      android:icon=\"@drawable/ic_baseline_open_with\"\n      android:title=\"@string/move\"\n      app:showAsAction=\"ifRoom\" />\n\n<!--  <item-->\n<!--      android:id=\"@+id/multi_choice\"-->\n<!--      android:icon=\"@drawable/ic_baseline_open_with\"-->\n<!--      android:title=\"@string/multi_choice\"-->\n<!--      app:showAsAction=\"ifRoom\" />-->\n\n</menu>"
  },
  {
    "path": "app/src/main/res/menu/pop_group_summary.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<menu xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n\n\n  <item\n      android:id=\"@+id/undo\"\n      android:icon=\"@drawable/ic_undo_entry\"\n      android:title=\"@string/undo_entry\"\n      app:showAsAction=\"ifRoom\" />\n\n  <item\n      android:id=\"@+id/edit\"\n      android:icon=\"@drawable/ic_detail_edit\"\n      android:title=\"@string/edit_group\"\n      app:showAsAction=\"ifRoom\" />\n\n  <item\n      android:id=\"@+id/del\"\n      android:icon=\"@drawable/ic_del\"\n      android:title=\"@string/del_group\"\n      app:showAsAction=\"ifRoom\" />\n\n  <item\n      android:id=\"@+id/move\"\n      android:icon=\"@drawable/ic_baseline_open_with\"\n      android:title=\"@string/move\"\n      app:showAsAction=\"ifRoom\" />\n\n</menu>"
  },
  {
    "path": "app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@color/ic_launcher_background\"/>\n    <foreground android:drawable=\"@drawable/ic_launcher_foreground\"/>\n</adaptive-icon>"
  },
  {
    "path": "app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@color/ic_launcher_background\"/>\n    <foreground android:drawable=\"@drawable/ic_launcher_foreground\"/>\n</adaptive-icon>"
  },
  {
    "path": "app/src/main/res/raw/auth_config_single_account_debug.json",
    "content": "{\n  \"client_id\": \"4be5affb-6284-48ea-9b0e-02e1db25319a\",\n  \"authorization_user_agent\": \"DEFAULT\",\n  \"account_mode\": \"SINGLE\",\n  \"broker_redirect_uri_registered\": false,\n  \"redirect_uri\": \"msauth://com.lyy.keepassa/5qMkIKbIM5Lx7VbajX8mT3rKXpE%3D\",\n  \"environment\": \"Production\",\n  \"power_opt_check_for_network_req_enabled\": true,\n  \"http\": {\n    \"connect_timeout\": 10000,\n    \"read_timeout\": 30000\n  },\n  \"logging\": {\n    \"pii_enabled\": false,\n    \"log_level\": \"WARNING\",\n    \"logcat_enabled\": true\n  },\n  \"authorities\": [\n    {\n      \"type\": \"AAD\",\n      \"audience\": {\n        \"type\": \"PersonalMicrosoftAccount\"\n      }\n    }\n  ],\n  \"browser_safelist\": [\n    {\n      \"browser_package_name\": \"com.android.chrome\",\n      \"browser_signature_hashes\": [\n        \"7fmduHKTdHHrlMvldlEqAIlSfii1tl35bxj1OXN5Ve8c4lU6URVu4xtSHc3BVZxS6WWJnxMDhIfQN0N0K2NDJg==\"\n      ]\n    },\n    {\n      \"browser_package_name\": \"org.mozilla.firefox\",\n      \"browser_signature_hashes\": [\n        \"2gCe6pR_AO_Q2Vu8Iep-4AsiKNnUHQxu0FaDHO_qa178GByKybdT_BuE8_dYk99G5Uvx_gdONXAOO2EaXidpVQ==\"\n      ]\n    },\n    {\n      \"browser_package_name\": \"com.sec.android.app.sbrowser\",\n      \"browser_signature_hashes\": [\n        \"ABi2fbt8vkzj7SJ8aD5jc4xJFTDFntdkMrYXL3itsvqY1QIw-dZozdop5rgKNxjbrQAd5nntAGpgh9w84O1Xgg==\"\n      ]\n    },\n    {\n      \"browser_package_name\": \"com.cloudmosa.puffinFree\",\n      \"browser_signature_hashes\": [\n        \"1WqG8SoK2WvE4NTYgr2550TRhjhxT-7DWxu6C_o6GrOLK6xzG67Hq7GCGDjkAFRCOChlo2XUUglLRAYu3Mn8Ag==\"\n      ]\n    },\n    {\n      \"browser_package_name\": \"com.duckduckgo.mobile.android\",\n      \"browser_signature_hashes\": [\n        \"S5Av4cfEycCvIvKPpKGjyCuAE5gZ8y60-knFfGkAEIZWPr9lU5kA7iOAlSZxaJei08s0ruDvuEzFYlmH-jAi4Q==\"\n      ]\n    },\n    {\n      \"browser_package_name\": \"com.explore.web.browser\",\n      \"browser_signature_hashes\": [\n        \"BzDzBVSAwah8f_A0MYJCPOkt0eb7WcIEw6Udn7VLcizjoU3wxAzVisCm6bW7uTs4WpMfBEJYf0nDgzTYvYHCag==\"\n      ]\n    },\n    {\n      \"browser_package_name\": \"com.ksmobile.cb\",\n      \"browser_signature_hashes\": [\n        \"lFDYx1Rwc7_XUn4KlfQk2klXLufRyuGHLa3a7rNjqQMkMaxZueQfxukVTvA7yKKp3Md3XUeeDSWGIZcRy7nouw==\"\n      ]\n    },\n    {\n      \"browser_package_name\": \"com.microsoft.emmx\",\n      \"browser_signature_hashes\": [\n        \"Ivy-Rk6ztai_IudfbyUrSHugzRqAtHWslFvHT0PTvLMsEKLUIgv7ZZbVxygWy_M5mOPpfjZrd3vOx3t-cA6fVQ==\"\n      ]\n    },\n    {\n      \"browser_package_name\": \"com.opera.browser\",\n      \"browser_signature_hashes\": [\n        \"FIJ3IIeqB7V0qHpRNEpYNkhEGA_eJaf7ntca-Oa_6Feev3UkgnpguTNV31JdAmpEFPGNPo0RHqdlU0k-3jWJWw==\"\n      ]\n    },\n    {\n      \"browser_package_name\": \"com.opera.mini.native\",\n      \"browser_signature_hashes\": [\n        \"TOTyHs086iGIEdxrX_24aAewTZxV7Wbi6niS2ZrpPhLkjuZPAh1c3NQ_U4Lx1KdgyhQE4BiS36MIfP6LbmmUYQ==\"\n      ]\n    },\n    {\n      \"browser_package_name\": \"mobi.mgeek.TunnyBrowser\",\n      \"browser_signature_hashes\": [\n        \"RMVoXuK1sfJZuGZ8onG1yhMc-sKiAV2NiB_GZfdNlN8XJ78XEE2wPM6LnQiyltF25GkHiPN2iKQiGwaO2bkyyQ==\"\n      ]\n    },\n    {\n      \"browser_package_name\": \"org.mozilla.focus\",\n      \"browser_signature_hashes\": [\n        \"L72dT-stFqomSY7sYySrgBJ3VYKbipMZapmUXfTZNqOzN_dekT5wdBACJkpz0C6P0yx5EmZ5IciI93Q0hq0oYA==\"\n      ]\n    },\n    {\n      \"browser_package_name\": \"com.cake.browser\",\n      \"browser_signature_hashes\": [\n        \"442kvSdZT1fEAewzSi8Wre73x4mWmHBhOFtQ-9T9N6ExZzUdsELUmaaS0edsI7ur2nY-bjbWX7IpluFOyvKkOA==\"\n      ]\n    },\n    {\n      \"browser_package_name\": \"com.brave.browser\",\n      \"browser_signature_hashes\": [\n        \"wIwX1v_1TfPxHm5qn-_jdGoH3Pa9VVMR5dtVz0Y0xqPkyM_KlavjWPSgOolrVH05AVO1cHWoLPqMzCH04Pw8LQ==\"\n      ]\n    },\n    {\n      \"browser_package_name\": \"com.kiwibrowser.browser\",\n      \"browser_signature_hashes\": [\n        \"kmPeixKA04JcDuWNBMUPu_6WaODr6a9ofROUIHIGxiiFGvH8Y92MonrDQmsNqEJO2DQkpEQc425WmAYB4NlD3Q==\"\n      ]\n    },\n    {\n      \"browser_package_name\": \"com.mi.globalbrowser.mini\",\n      \"browser_signature_hashes\": [\n        \"6FEWlPfWn-omfES2ZYDj5bZUIR5au_nfyRr-o_1R3fesjfoV1JptBMumtvVIo0q37abcMRWQt9RUSNXpzKpNdA==\"\n      ]\n    },\n    {\n      \"browser_package_name\": \"mark.via.gp\",\n      \"browser_signature_hashes\": [\n        \"oTJf5e5nB1NinkdBpkkmhPnwbVRmDlHZ-s_QhvyuGKM5nq5XtjA439O31wxrkL6ReHyyKfDHFUHpQnoXoj--Ig==\"\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "app/src/main/res/raw/auth_config_single_account_release.json",
    "content": "{\n  \"client_id\": \"4be5affb-6284-48ea-9b0e-02e1db25319a\",\n  \"authorization_user_agent\": \"DEFAULT\",\n  \"account_mode\": \"SINGLE\",\n  \"broker_redirect_uri_registered\": false,\n  \"redirect_uri\": \"msauth://com.lyy.keepassa/xv3czaACtp98KvuSqP3GRLCxjrc%3D\",\n  \"environment\": \"Production\",\n  \"power_opt_check_for_network_req_enabled\": true,\n  \"http\": {\n    \"connect_timeout\": 10000,\n    \"read_timeout\": 30000\n  },\n  \"logging\": {\n    \"pii_enabled\": false,\n    \"log_level\": \"WARNING\",\n    \"logcat_enabled\": true\n  },\n  \"authorities\": [\n    {\n      \"type\": \"AAD\",\n      \"audience\": {\n        \"type\": \"PersonalMicrosoftAccount\"\n      }\n    }\n  ],\n  \"browser_safelist\": [\n    {\n      \"browser_package_name\": \"com.android.chrome\",\n      \"browser_signature_hashes\": [\n        \"7fmduHKTdHHrlMvldlEqAIlSfii1tl35bxj1OXN5Ve8c4lU6URVu4xtSHc3BVZxS6WWJnxMDhIfQN0N0K2NDJg==\"\n      ]\n    },\n    {\n      \"browser_package_name\": \"org.mozilla.firefox\",\n      \"browser_signature_hashes\": [\n        \"2gCe6pR_AO_Q2Vu8Iep-4AsiKNnUHQxu0FaDHO_qa178GByKybdT_BuE8_dYk99G5Uvx_gdONXAOO2EaXidpVQ==\"\n      ]\n    },\n    {\n      \"browser_package_name\": \"com.sec.android.app.sbrowser\",\n      \"browser_signature_hashes\": [\n        \"ABi2fbt8vkzj7SJ8aD5jc4xJFTDFntdkMrYXL3itsvqY1QIw-dZozdop5rgKNxjbrQAd5nntAGpgh9w84O1Xgg==\"\n      ]\n    },\n    {\n      \"browser_package_name\": \"com.cloudmosa.puffinFree\",\n      \"browser_signature_hashes\": [\n        \"1WqG8SoK2WvE4NTYgr2550TRhjhxT-7DWxu6C_o6GrOLK6xzG67Hq7GCGDjkAFRCOChlo2XUUglLRAYu3Mn8Ag==\"\n      ]\n    },\n    {\n      \"browser_package_name\": \"com.duckduckgo.mobile.android\",\n      \"browser_signature_hashes\": [\n        \"S5Av4cfEycCvIvKPpKGjyCuAE5gZ8y60-knFfGkAEIZWPr9lU5kA7iOAlSZxaJei08s0ruDvuEzFYlmH-jAi4Q==\"\n      ]\n    },\n    {\n      \"browser_package_name\": \"com.explore.web.browser\",\n      \"browser_signature_hashes\": [\n        \"BzDzBVSAwah8f_A0MYJCPOkt0eb7WcIEw6Udn7VLcizjoU3wxAzVisCm6bW7uTs4WpMfBEJYf0nDgzTYvYHCag==\"\n      ]\n    },\n    {\n      \"browser_package_name\": \"com.ksmobile.cb\",\n      \"browser_signature_hashes\": [\n        \"lFDYx1Rwc7_XUn4KlfQk2klXLufRyuGHLa3a7rNjqQMkMaxZueQfxukVTvA7yKKp3Md3XUeeDSWGIZcRy7nouw==\"\n      ]\n    },\n    {\n      \"browser_package_name\": \"com.microsoft.emmx\",\n      \"browser_signature_hashes\": [\n        \"Ivy-Rk6ztai_IudfbyUrSHugzRqAtHWslFvHT0PTvLMsEKLUIgv7ZZbVxygWy_M5mOPpfjZrd3vOx3t-cA6fVQ==\"\n      ]\n    },\n    {\n      \"browser_package_name\": \"com.opera.browser\",\n      \"browser_signature_hashes\": [\n        \"FIJ3IIeqB7V0qHpRNEpYNkhEGA_eJaf7ntca-Oa_6Feev3UkgnpguTNV31JdAmpEFPGNPo0RHqdlU0k-3jWJWw==\"\n      ]\n    },\n    {\n      \"browser_package_name\": \"com.opera.mini.native\",\n      \"browser_signature_hashes\": [\n        \"TOTyHs086iGIEdxrX_24aAewTZxV7Wbi6niS2ZrpPhLkjuZPAh1c3NQ_U4Lx1KdgyhQE4BiS36MIfP6LbmmUYQ==\"\n      ]\n    },\n    {\n      \"browser_package_name\": \"mobi.mgeek.TunnyBrowser\",\n      \"browser_signature_hashes\": [\n        \"RMVoXuK1sfJZuGZ8onG1yhMc-sKiAV2NiB_GZfdNlN8XJ78XEE2wPM6LnQiyltF25GkHiPN2iKQiGwaO2bkyyQ==\"\n      ]\n    },\n    {\n      \"browser_package_name\": \"org.mozilla.focus\",\n      \"browser_signature_hashes\": [\n        \"L72dT-stFqomSY7sYySrgBJ3VYKbipMZapmUXfTZNqOzN_dekT5wdBACJkpz0C6P0yx5EmZ5IciI93Q0hq0oYA==\"\n      ]\n    },\n    {\n      \"browser_package_name\": \"com.cake.browser\",\n      \"browser_signature_hashes\": [\n        \"442kvSdZT1fEAewzSi8Wre73x4mWmHBhOFtQ-9T9N6ExZzUdsELUmaaS0edsI7ur2nY-bjbWX7IpluFOyvKkOA==\"\n      ]\n    },\n    {\n      \"browser_package_name\": \"com.brave.browser\",\n      \"browser_signature_hashes\": [\n        \"wIwX1v_1TfPxHm5qn-_jdGoH3Pa9VVMR5dtVz0Y0xqPkyM_KlavjWPSgOolrVH05AVO1cHWoLPqMzCH04Pw8LQ==\"\n      ]\n    },\n    {\n      \"browser_package_name\": \"com.kiwibrowser.browser\",\n      \"browser_signature_hashes\": [\n        \"kmPeixKA04JcDuWNBMUPu_6WaODr6a9ofROUIHIGxiiFGvH8Y92MonrDQmsNqEJO2DQkpEQc425WmAYB4NlD3Q==\"\n      ]\n    },\n    {\n      \"browser_package_name\": \"com.mi.globalbrowser.mini\",\n      \"browser_signature_hashes\": [\n        \"6FEWlPfWn-omfES2ZYDj5bZUIR5au_nfyRr-o_1R3fesjfoV1JptBMumtvVIo0q37abcMRWQt9RUSNXpzKpNdA==\"\n      ]\n    },\n    {\n      \"browser_package_name\": \"mark.via.gp\",\n      \"browser_signature_hashes\": [\n        \"oTJf5e5nB1NinkdBpkkmhPnwbVRmDlHZ-s_QhvyuGKM5nq5XtjA439O31wxrkL6ReHyyKfDHFUHpQnoXoj--Ig==\"\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "app/src/main/res/raw/notices.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright 2013 Philip Schiffer\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<notices>\n\n    <notice>\n        <name>zxing-android-embedded</name>\n        <url>https://github.com/journeyapps/zxing-android-embedded</url>\n        <copyright>Copyright (C) 2012-201 ZXing authors, Journey Mobile</copyright>\n        <license>Apache Software License 2.0</license>\n    </notice>\n\n    <notice>\n        <name>Zxing</name>\n        <url>https://github.com/zxing/zxing</url>\n        <copyright/>\n        <license>Apache Software License 2.0</license>\n    </notice>\n\n    <notice>\n        <name>AndroidUtilCode</name>\n        <url>https://github.com/Blankj/AndroidUtilCode</url>\n        <copyright/>\n        <license>Apache Software License 2.0</license>\n    </notice>\n\n    <notice>\n        <name>timber</name>\n        <url>https://github.com/JakeWharton/timber</url>\n        <copyright/>\n        <license>Apache Software License 2.0</license>\n    </notice>\n\n    <notice>\n        <name>ARouter</name>\n        <url>https://github.com/alibaba/ARouter</url>\n        <copyright/>\n        <license>Apache Software License 2.0</license>\n    </notice>\n\n    <notice>\n        <name>Material design icons</name>\n        <url>https://github.com/google/material-design-icons</url>\n        <copyright/>\n        <license>Apache Software License 2.0</license>\n    </notice>\n\n    <notice>\n        <name>OkHttp</name>\n        <url>https://github.com/square/okhttp</url>\n        <copyright>Copyright 2019 Square, Inc.</copyright>\n        <license>Apache Software License 2.0</license>\n    </notice>\n\n    <notice>\n        <name>Retrofit</name>\n        <url>https://github.com/square/retrofit</url>\n        <copyright>Copyright 2013 Square, Inc.</copyright>\n        <license>Apache Software License 2.0</license>\n    </notice>\n\n    <notice>\n        <name>Gson</name>\n        <url>https://github.com/google/gson</url>\n        <copyright>Copyright 2008 Google Inc.</copyright>\n        <license>Apache Software License 2.0</license>\n    </notice>\n\n    <notice>\n        <name>Spongycastle</name>\n        <url>https://github.com/rtyley/spongycastle</url>\n        <copyright>Copyright (c) 2000-2017 The Legion of the Bouncy Castle Inc. (http://www.bouncycastle.org)</copyright>\n        <license>Apache Software License 2.0</license>\n    </notice>\n\n    <notice>\n        <name>Apache Commons IO</name>\n        <url>http://commons.apache.org/io/</url>\n        <copyright>Copyright 2002-2012 The Apache Software Foundation</copyright>\n        <license>Apache Software License 2.0</license>\n    </notice>\n\n    <notice>\n        <name>Kotlin</name>\n        <url>https://github.com/JetBrains/kotlin</url>\n        <copyright/>\n        <license>Apache Software License 2.0</license>\n    </notice>\n\n    <notice>\n        <name>AndResGuard</name>\n        <url>https://github.com/shwenzhang/AndResGuard</url>\n        <copyright/>\n        <license>Apache Software License 2.0</license>\n    </notice>\n\n    <notice>\n        <name>EasyProtector</name>\n        <url>https://github.com/lamster2018/EasyProtector</url>\n        <copyright/>\n        <license>Apache Software License 2.0</license>\n    </notice>\n\n    <notice>\n        <name>VasDolly</name>\n        <url>https://github.com/Tencent/VasDolly</url>\n        <copyright>Copyright (C) 2018 THL A29 Limited, a Tencent company.</copyright>\n        <license>BSD 3-Clause License</license>\n    </notice>\n\n    <notice>\n        <name>Richtext</name>\n        <url>https://github.com/zzhoujay/RichText</url>\n        <copyright>Copyright (c) 2016 zzhoujay</copyright>\n        <license>MIT License</license>\n    </notice>\n\n    <notice>\n        <name>Wcdb room</name>\n        <url>https://github.com/Tencent/wcdb</url>\n        <copyright>Copyright (C) 2017 THL A29 Limited, a Tencent company.</copyright>\n        <license>BSD 3-Clause License</license>\n    </notice>\n\n    <notice>\n        <name>Wcdb android</name>\n        <url>https://github.com/Tencent/wcdb</url>\n        <copyright>Copyright (C) 2017 THL A29 Limited, a Tencent company.</copyright>\n        <license>BSD 3-Clause License</license>\n    </notice>\n\n    <notice>\n        <name>Sardine android</name>\n        <url>https://github.com/thegrizzlylabs/sardine-android</url>\n        <copyright/>\n        <license>Apache Software License 2.0</license>\n    </notice>\n\n    <notice>\n        <name>Keepassdroid</name>\n        <url>https://github.com/bpellin/keepassdroid</url>\n        <copyright>Copyright 2009 Brian Pellin.</copyright>\n        <license>GNU General Public License 2.0</license>\n    </notice>\n\n    <notice>\n        <name>AOSP</name>\n        <url>https://github.com/bumptech/glide</url>\n        <copyright>Copyright 2014 Google, Inc.</copyright>\n        <license>Apache Software License 2.0</license>\n    </notice>\n\n\n    <notice>\n        <name>Glide</name>\n        <url>https://github.com/bumptech/glide</url>\n        <copyright>Copyright 2014 Google, Inc.</copyright>\n        <license>Apache Software License 2.0</license>\n    </notice>\n\n    <notice>\n        <name>Lottie</name>\n        <url>https://github.com/airbnb/lottie-android</url>\n        <copyright>Copyright 2018 Airbnb, Inc.</copyright>\n        <license>Apache Software License 2.0</license>\n    </notice>\n\n\n    <notice>\n        <name>EventBus</name>\n        <url>https://github.com/greenrobot/EventBus</url>\n        <copyright>Copyright (C) 2012-2020 Markus Junginger, greenrobot (https://greenrobot.org)</copyright>\n        <license>Apache Software License 2.0</license>\n    </notice>\n\n    <notice>\n        <name>Immersionbar</name>\n        <url>https://github.com/gyf-dev/ImmersionBar</url>\n        <copyright/>\n        <license>Apache Software License 2.0</license>\n    </notice>\n\n    <notice>\n        <name>Subsampling scale image view</name>\n        <url>https://github.com/davemorrissey/subsampling-scale-image-view</url>\n        <copyright>Copyright 2018 David Morrissey</copyright>\n        <license>Apache Software License 2.0</license>\n    </notice>\n\n    <notice>\n        <name>LicensesDialog</name>\n        <url>https://github.com/PSDev/LicensesDialog</url>\n        <copyright>Copyright 2013 Philip Schiffer</copyright>\n        <license>Apache Software License 2.0</license>\n    </notice>\n\n    <notice>\n        <name>Joda Time</name>\n        <url>http://joda-time.sourceforge.net/</url>\n        <copyright>Copyright 2001-2005 Stephen Colebourne</copyright>\n        <license>Apache Software License 2.0</license>\n    </notice>\n\n    <notice>\n        <name>AndroidAutoSize</name>\n        <url>https://github.com/JessYanCoding/AndroidAutoSize</url>\n        <copyright>Copyright 2018, jessyan</copyright>\n        <license>Apache Software License 2.0</license>\n    </notice>\n</notices>"
  },
  {
    "path": "app/src/main/res/transition/changebounds_with_arcmotion.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<transitionSet xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:duration=\"400\"\n    android:interpolator=\"@android:interpolator/decelerate_cubic\"\n    >\n  <changeBounds>\n    <!--patternPathMotion android:patternPathData=\"M0 0 L0 100 L100 0\"/-->\n    <arcMotion\n        android:maximumAngle=\"90\"\n        android:minimumHorizontalAngle=\"90\"\n        android:minimumVerticalAngle=\"0\" />\n\n  </changeBounds>\n</transitionSet>\n"
  },
  {
    "path": "app/src/main/res/transition/fade_enter.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<transitionSet xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:duration=\"@integer/anim_duration_long\"\n    android:transitionOrdering=\"sequential\">\n  <fade\n      android:fadingMode=\"fade_in\"\n      android:interpolator=\"@android:interpolator/decelerate_cubic\"\n      />\n  <changeBounds android:interpolator=\"@android:interpolator/bounce\" />\n  <!--  状态栏和导航栏不参与动画 -->\n  <targets>\n    <target android:excludeId=\"@android:id/navigationBarBackground\" />\n    <target android:excludeId=\"@android:id/statusBarBackground\" />\n  </targets>\n</transitionSet>"
  },
  {
    "path": "app/src/main/res/transition/fade_exit.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<transitionSet xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:duration=\"@integer/anim_duration_long\"\n    android:transitionOrdering=\"sequential\">\n  <fade\n      android:fadingMode=\"fade_out\"\n      android:interpolator=\"@android:interpolator/decelerate_cubic\"\n      />\n  <changeBounds android:interpolator=\"@android:interpolator/bounce\" />\n  <!--  状态栏和导航栏不参与动画 -->\n  <targets>\n    <target android:excludeId=\"@android:id/navigationBarBackground\" />\n    <target android:excludeId=\"@android:id/statusBarBackground\" />\n  </targets>\n</transitionSet>"
  },
  {
    "path": "app/src/main/res/transition/slide_enter.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<transitionSet xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:duration=\"200\"\n    android:transitionOrdering=\"together\">\n  <slide\n      android:interpolator=\"@android:interpolator/linear\"\n      android:slideEdge=\"end\" />\n<!--  <changeBounds android:interpolator=\"@android:interpolator/bounce\" />-->\n  <!--  状态栏和导航栏不参与动画 -->\n  <targets>\n    <target android:excludeId=\"@android:id/navigationBarBackground\" />\n    <target android:excludeId=\"@android:id/statusBarBackground\" />\n  </targets>\n</transitionSet>"
  },
  {
    "path": "app/src/main/res/transition/slide_exit.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<transitionSet xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:duration=\"100\"\n    android:transitionOrdering=\"together\">\n  <slide\n      android:interpolator=\"@android:interpolator/linear\"\n      android:slideEdge=\"start\" />\n<!--  <changeBounds android:interpolator=\"@android:interpolator/bounce\" />-->\n  <!--  状态栏和导航栏不参与动画 -->\n  <targets>\n    <target android:excludeId=\"@android:id/navigationBarBackground\" />\n    <target android:excludeId=\"@android:id/statusBarBackground\" />\n  </targets>\n</transitionSet>"
  },
  {
    "path": "app/src/main/res/transition/slide_reeter.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<transitionSet xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:duration=\"@integer/anim_duration_medium\"\n    android:transitionOrdering=\"sequential\">\n  <slide\n      android:interpolator=\"@android:interpolator/linear\"\n      android:slideEdge=\"start\" />\n<!--  <changeBounds android:interpolator=\"@android:interpolator/bounce\" />-->\n  <!--  状态栏和导航栏不参与动画 -->\n  <targets>\n    <target android:excludeId=\"@android:id/navigationBarBackground\" />\n    <target android:excludeId=\"@android:id/statusBarBackground\" />\n  </targets>\n</transitionSet>"
  },
  {
    "path": "app/src/main/res/transition/slide_return.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<transitionSet xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:duration=\"@integer/anim_duration_medium\"\n    android:transitionOrdering=\"sequential\">\n  <slide\n      android:interpolator=\"@android:interpolator/linear\"\n      android:slideEdge=\"end\" />\n<!--  <changeBounds android:interpolator=\"@android:interpolator/bounce\" />-->\n  <!--  状态栏和导航栏不参与动画 -->\n  <targets>\n    <target android:excludeId=\"@android:id/navigationBarBackground\" />\n    <target android:excludeId=\"@android:id/statusBarBackground\" />\n  </targets>\n</transitionSet>"
  },
  {
    "path": "app/src/main/res/values/arrays.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n  <string-array name=\"quick_pass_len_entries\">\n    <item>3</item>\n    <item>4</item>\n    <item>5</item>\n    <item>6</item>\n  </string-array>\n\n  <string-array name=\"quick_pass_len_value\">\n    <item>3</item>\n    <item>4</item>\n    <item>5</item>\n    <item>6</item>\n  </string-array>\n\n  <string-array name=\"quick_pass_type_value\">\n    <item>1</item>\n    <item>2</item>\n  </string-array>\n\n\n  <string-array name=\"clean_clip_value\">\n    <item>30</item>\n    <item>60</item>\n    <item>120</item>\n    <item>300</item>\n  </string-array>\n\n\n  <string-array name=\"quick_lock_value\">\n    <item>120</item>\n    <item>300</item>\n    <item>600</item>\n    <item>1200</item>\n  </string-array>\n\n  <string-array name=\"set_key_theme_values\">\n    <item>0</item>\n    <item>1</item>\n    <item>2</item>\n  </string-array>\n\n\n  <string-array name=\"out_db_entries\">\n    <item>Keepass2 database</item>\n    <item>Unencrypted XML file</item>\n    <item>Unencrypted CSV file</item>\n  </string-array>\n\n  <string-array name=\"out_db_value\">\n    <item>0</item>\n    <item>1</item>\n    <item>2</item>\n  </string-array>\n\n  <integer-array name=\"cloud_icons\">\n    <item>@drawable/ic_android</item>\n    <item>@drawable/ic_dropbox</item>\n    <item>@drawable/ic_onedrive</item>\n    <!--    <item>@drawable/ic_google_drive</item>-->\n    <item>@drawable/ic_http</item>\n    <!--    <item>@drawable/ic_ftp</item>-->\n    <!--    <item>@drawable/ic_history</item>-->\n    <!--    <item>@drawable/ic_other</item>-->\n  </integer-array>\n\n  <!--  see DbOpenType -->\n  <integer-array name=\"cloud_type_ids\">\n    <item>0</item>\n    <item>1</item>\n    <item>2</item>\n    <!--    <item>3</item>-->\n    <item>4</item>\n    <!--    <item>5</item>-->\n    <!--    <item>6</item>-->\n  </integer-array>\n\n  <string-array name=\"cloud_names\">\n    <item>AFS</item>\n    <item>Dropbox</item>\n    <item>OneDrive</item>\n    <!--    <item>GoogleDrive</item>-->\n    <item>WebDav</item>\n    <!--    <item>Ftp</item>-->\n    <!--    <item>SFtp</item>-->\n  </string-array>\n\n\n  <string-array name=\"choose_language_entries\">\n    <item>English</item>\n    <item>简体中文</item>\n    <item>繁体中文</item>\n    <item>Français canadien</item>\n    <item>Norsk bokmål</item>\n    <item>Français</item>\n    <item>Deutsche</item>\n    <item>Język polski</item>\n    <item>Türkçe</item>\n    <item>Україна</item>\n    <item>Español</item>\n  </string-array>\n\n  <string-array name=\"choose_language_value\">\n    <item>1</item>\n    <item>2</item>\n    <item>3</item>\n    <item>4</item>\n    <item>5</item>\n    <item>6</item>\n    <item>7</item>\n    <item>8</item>\n    <item>9</item>\n    <item>10</item>\n    <item>11</item>\n    <item>12</item>\n  </string-array>\n\n\n  <string-array name=\"totp_arithmetic\">\n    <item>SHA-1</item>\n    <item>SHA-256</item>\n    <item>SHA-512</item>\n  </string-array>\n</resources>"
  },
  {
    "path": "app/src/main/res/values/attrs.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n  <declare-styleable name=\"EmptyDataFillView\">\n    <attr name=\"edf_text\" format=\"string\" />\n    <attr name=\"edf_icon\" format=\"reference\" />\n  </declare-styleable>\n\n  <declare-styleable name=\"ShortPasswordView\">\n    <attr name=\"passLen\" format=\"reference\" />\n  </declare-styleable>\n\n  <declare-styleable name=\"BubbleTextView\">\n    <attr name=\"left_icon\" format=\"reference\" />\n    <attr name=\"top_icon\" format=\"reference\" />\n    <attr name=\"right_icon\" format=\"reference\" />\n    <attr name=\"bottom_icon\" format=\"reference\" />\n    <attr name=\"icon_size\" format=\"dimension\">\n      <enum name=\"smaller\" value=\"25\" />\n    </attr>\n  </declare-styleable>\n\n  <declare-styleable name=\"BubbleRadioButton\">\n    <attr name=\"rb_right_icon\" format=\"reference\" />\n    <attr name=\"rb_icon_size\" format=\"dimension\">\n      <enum name=\"smaller\" value=\"25\" />\n    </attr>\n  </declare-styleable>\n\n  <declare-styleable name=\"ExpandAttrStrLayout\">\n    <attr name=\"expand_tt_icon\" format=\"reference\" />\n    <attr name=\"expand_tt_title\" format=\"string|reference\" />\n  </declare-styleable>\n\n  <declare-styleable name=\"RecyclerViewContentBehavior_Layout\">\n    <attr name=\"expand_h\" format=\"dimension\" />\n  </declare-styleable>\n\n  <declare-styleable name=\"SimpleItemView\">\n    <attr name=\"icon\" format=\"reference\" />\n    <attr name=\"title\" format=\"string|reference\" />\n    <attr name=\"des\" format=\"string|reference\" />\n  </declare-styleable>\n\n  <declare-styleable name=\"Theme\">\n    <attr name=\"discreteSeekBarStyle\" format=\"reference\" />\n  </declare-styleable>\n  <declare-styleable name=\"DiscreteSeekBar\">\n    <attr name=\"dsb_min\" format=\"integer|dimension\" />\n    <attr name=\"dsb_max\" format=\"integer|dimension\" />\n    <attr name=\"dsb_value\" format=\"integer|dimension\" />\n    <attr name=\"dsb_mirrorForRtl\" format=\"boolean\" />\n    <attr name=\"dsb_allowTrackClickToDrag\" format=\"boolean\" />\n    <attr name=\"dsb_progressColor\" format=\"color|reference\" />\n    <attr name=\"dsb_trackColor\" format=\"color|reference\" />\n    <attr name=\"dsb_indicatorTextAppearance\" format=\"reference\" />\n    <attr name=\"dsb_indicatorColor\" format=\"color|reference\" />\n    <attr name=\"dsb_indicatorElevation\" format=\"dimension\" />\n    <attr name=\"dsb_indicatorFormatter\" format=\"string|reference\" />\n    <attr name=\"dsb_rippleColor\" format=\"color|reference\" />\n    <attr name=\"dsb_indicatorPopupEnabled\" format=\"boolean\" />\n    <attr name=\"dsb_trackHeight\" format=\"integer|dimension\" />\n    <attr name=\"dsb_scrubberHeight\" format=\"integer|dimension\" />\n    <attr name=\"dsb_thumbSize\" format=\"integer|dimension\" />\n    <attr name=\"dsb_indicatorSeparation\" format=\"integer|dimension\" />\n  </declare-styleable>\n\n\n</resources>"
  },
  {
    "path": "app/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n  <color name=\"background_color\">#FFFFFF</color>\n  <color name=\"text_black_color\">#333333</color>\n\n  <!--  https://blog.csdn.net/qq_31001287/article/details/60867484-->\n  <color name=\"colorPrimary\">#4E85DB</color>  <!--状态栏-->\n  <color name=\"colorPrimaryDark\">#4E85DB</color> <!--action bar-->\n  <color name=\"colorAccent\">#4E85DB</color>   <!--checkbox, progress bar 的颜色-->\n  <color name=\"textColorPrimary\">#4E85DB</color> <!--文字颜色-->\n\n  <color name=\"rippleDefaultColor\">#4f000000</color>\n  <color name=\"ripple\">#18000000</color>\n  <!--  蒙版颜色 -->\n  <color name=\"mask\">#33000000</color>\n  <color name=\"black\">#000</color>\n  <color name=\"transparent\">#00000000</color>\n\n\n  <color name=\"text_hint_color\">#bbbbbb</color>\n  <color name=\"bg_line\">#bbbbbb</color>\n\n  <color name=\"highlight\">#FF4081</color>\n  <color name=\"red\">#e6162d</color>\n  <color name=\"yellow\">#FFFFFF00</color>\n  <color name=\"blue\">#3e96d6</color>\n  <color name=\"green\">#44b549</color>\n\n  <color name=\"item_un_click\">#1f000000</color>\n\n  <color name=\"item_light_background\">#fff</color>\n  <color name=\"white\">#ffffff</color>\n\n  <!--<color name=\"background_color\">#F6F6F8</color>-->\n  <color name=\"background_color_grey_w\">#E5E5E5</color>\n  <color name=\"navigation_bar_color\">#aaFFFFFF</color>\n  <color name=\"line_color\">#eaeaea</color>\n  <color name=\"click_pressed_color\">#22000000</color>\n\n  <color name=\"grey600\">#757575</color>\n  <color name=\"color_BB757575\">#BB757575</color>\n  <color name=\"color_note_bg\">#CBCBCB</color>\n  <color name=\"error_color\">#e6162d</color>\n  <color name=\"bg_first_create_db\">#84BA55</color>\n\n  <color name=\"color_icon_grey\">#333333</color>\n  <color name=\"color_E7EAED\">#E7EAED</color>\n  <color name=\"color_4E85DB\">#4E85DB</color>\n  <color name=\"color_444E85DB\">#444E85DB</color>\n  <color name=\"color_22FEFEFE\">#22fefefe</color>\n  <color name=\"color_FFFFFF\">#FFFFFF</color>\n  <color name=\"color_524E85DB\">#524E85DB</color>\n  <color name=\"color_33666666\">#33666666</color>\n\n\n  <!-- 默迪卡颜色  https://www.bilibili.com/read/cv11988559-->\n  <color name=\"color_d3ae5b\">#d3ae5b</color>\n\n\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/dimens.xml",
    "content": "<resources>\n  <!-- Default screen margins, per the Android Design guidelines. -->\n  <dimen name=\"activity_horizontal_margin\">16dp</dimen>\n  <dimen name=\"activity_vertical_margin\">16dp</dimen>\n  <dimen name=\"fab_margin\">16dp</dimen>\n  <dimen name=\"item_h\">60dp</dimen>\n  <dimen name=\"toolbar_h\">48dp</dimen>\n\n\n\n  <dimen name=\"icon_size\">28dp</dimen>\n  <dimen name=\"input_pass_key_h\">30dp</dimen>\n  <dimen name=\"create_pass_key_h\">90dp</dimen>\n\n  <integer name=\"anim_duration_very_long\">1500</integer>\n  <integer name=\"anim_duration_600\">600</integer>\n  <integer name=\"anim_duration_long\">500</integer>\n  <integer name=\"anim_duration_medium\">200</integer>\n  <dimen name=\"dialog_min_width\">350dp</dimen>\n  <dimen name=\"dialog_ed_layout_width\">340dp</dimen>\n\n\n  <dimen name=\"entry_detail_item_h\">48dp</dimen>\n  <dimen name=\"dle_btn_width\">80dp</dimen>\n\n\n\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/ic_launcher_background.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"ic_launcher_background\">#FFFFFF</color>\n</resources>"
  },
  {
    "path": "app/src/main/res/values/ids.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n  <integer-array name=\"path_type_img\">\n    <item>@drawable/ic_android</item>\n    <item>@drawable/ic_dropbox</item>\n    <item>@drawable/ic_onedrive</item>\n    <item>@drawable/ic_http</item>\n    <item>@drawable/ic_google_drive</item>\n    <item>@drawable/ic_ftp</item>\n    <item>@drawable/ic_ssh</item>\n  </integer-array>\n\n  <integer-array name=\"v3_add_more_icon\">\n    <item>@drawable/ic_notice</item>\n    <item>@drawable/ic_attr_file</item>\n    <item>@drawable/ic_lose_time</item>\n  </integer-array>\n\n  <integer-array name=\"v4_add_more_icon\">\n    <item>@drawable/ic_notice</item>\n    <item>@drawable/ic_attr_str</item>\n    <item>@drawable/ic_attr_file</item>\n    <item>@drawable/ic_token_grey</item>\n    <item>@drawable/ic_tag</item>\n    <item>@drawable/ic_net</item>\n    <item>@drawable/ic_lose_time</item>\n  </integer-array>\n\n</resources>"
  },
  {
    "path": "app/src/main/res/values/pre_key.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n  <!--设置key-->\n  <string name=\"set_key_clean_clip_time\" translatable=\"false\">clean_clip_time</string>\n  <string name=\"set_key_auto_lock_db_time\" translatable=\"false\">auto_lock_db_time</string>\n  <string name=\"set_key_fingerprint_unlock\" translatable=\"false\">fingerprint_unlock</string>\n  <string name=\"set_key_modify_db_name\" translatable=\"false\">modify_db_name</string>\n  <string name=\"set_key_out_db\" translatable=\"false\">out_db</string>\n  <string name=\"set_quick_unlock\" translatable=\"false\">set_qulick_unlock</string>\n  <string name=\"set_quick_pass_len\" translatable=\"false\">set_quick_pass_len</string>\n  <string name=\"set_key_lock_screen_auto_lock_db\" translatable=\"false\">set_key_lock_screen_auto_lock_db</string>\n\n  <string name=\"set_quick_pass_type\" translatable=\"false\">set_quick_pass_type</string>\n  <string name=\"set_key_modify_db_pass\" translatable=\"false\">set_key_modify_db_pass</string>\n  <string name=\"set_open_auto_fill\" translatable=\"false\">set_open_auto_fill</string>\n  <string name=\"set_key_open_kpa_ime\" translatable=\"false\">set_key_open_kpa_ime</string>\n  <string name=\"set_key_praise\" translatable=\"false\">set_key_praise</string>\n  <string name=\"set_key_delete_no_recycle_bin\" translatable=\"false\">set_key_delete_no_recycle_bin</string>\n  <string name=\"set_key_main_allow_show_entries\" translatable=\"false\">set_key_main_allow_show_entries</string>\n  <string name=\"set_key_need_root_check\" translatable=\"false\">set_key_need_root_check</string>\n  <string name=\"set_key_fillet_bg_icon\" translatable=\"false\">set_key_fillet_bg_icon</string>\n  <string name=\"set_key_show_state_bar\" translatable=\"false\">set_key_show_state_bar</string>\n  <string name=\"set_key_loading_anim\" translatable=\"false\">set_key_close_load_anim</string>\n  <string name=\"set_key_main_show_totp_tab\" translatable=\"false\">set_key_main_show_totp_tab1</string>\n  <string name=\"set_key_auto_lock_database\" translatable=\"false\">set_key_auto_lock_database</string>\n\n  <string name=\"set_key_version_log\" translatable=\"false\">set_key_version_log</string>\n  <string name=\"set_key_license\" translatable=\"false\">set_key_license</string>\n  <string name=\"set_key_language\" translatable=\"false\">set_key_language</string>\n  <string name=\"set_key_ui_setting\" translatable=\"false\">setKeyUiSetting</string>\n  <string name=\"set_key_theme_style\" translatable=\"false\">set_key_theme_style</string>\n  <string name=\"set_key_tip_of_day\" translatable=\"false\">set_key_tip_of_day</string>\n\n  <!-- 分享动画元素的信息 -->\n  <string name=\"transition_db_name\" translatable=\"false\">transitionDbName</string>\n  <string name=\"transition_app_icon\" translatable=\"false\">transitionAppIcon</string>\n  <string name=\"transition_entry_icon\" translatable=\"false\">transition_entry_icon</string>\n  <string name=\"transition_db_version\" translatable=\"false\">transitionDbVersion</string>\n  <string name=\"transition_db_little\" translatable=\"false\">transitionDbLittle</string>\n\n<!--  activity 堆栈-->\n  <string name=\"affinity_main\" translatable=\"false\">affinity.main</string>\n</resources>"
  },
  {
    "path": "app/src/main/res/values/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string-array name=\"clean_clip_entries\">\n        <item>30 second</item>\n        <item>1 minute</item>\n        <item>2 minute</item>\n        <item>5 minute</item>\n    </string-array>\n    <string-array name=\"v3_add_mor_item\">\n        <item>Note</item>\n        <item>Attachment</item>\n        <item>Expiration date</item>\n    </string-array>\n\n    <string-array name=\"v4_add_mor_item\">\n        <item>Note</item>\n        <item>Custom field</item>\n        <item>Attachment</item>\n        <item>TOTP</item>\n        <item>Tag</item>\n        <item>URL</item>\n        <item>Expiration date</item>\n    </string-array>\n    <string-array name=\"create_normal_group\">\n        <item>Email box</item>\n        <item>Work</item>\n        <item>Finance</item>\n        <item>Recreation</item>\n        <item>Recycle bin</item>\n    </string-array>\n    <string-array name=\"auto_fill_hint_label\">\n        <item>Email</item>\n        <item>Phone</item>\n        <item>User</item>\n        <item>Account</item>\n    </string-array>\n    <string name=\"app_name\" translatable=\"false\">KeepassA</string>\n    <string name=\"db\">Database</string>\n    <string name=\"db1\"><![CDATA[database\\t|\\t<font color=\"#757575\">%s</font>]]></string>\n    <string name=\"password\">Password</string>\n    <string name=\"key\">Secret key</string>\n    <string name=\"key1\">Secret key: %s</string>\n    <string name=\"open\">Unlock</string>\n    <string name=\"hint_set_password\">Set password</string>\n    <string name=\"hint_input_password\">Enter a password of 6–16 digits</string>\n    <string name=\"hint_enter_password\">Confirm password</string>\n    <string name=\"hint_confirm\">Confirm password</string>\n    <string name=\"help_create_db\">Create v4 KeepPass database by default</string>\n    <string name=\"help_create_group\">Pick a group name of 16 or fewer characters</string>\n    <string name=\"encrypt_type\">Encryption</string>\n    <string name=\"encrypt_type_1\">Password only</string>\n    <string name=\"encrypt_type_2\">Password + key</string>\n    <string name=\"choose_pass_key\">Select key</string>\n    <string name=\"hint_set_pass_key\">Please select a key</string>\n    <string name=\"pass_key\">File key</string>\n    <string name=\"helper_create_pass\">Mixed password is recommended (uppercase and lowercase + numbers + symbols)</string>\n    <string name=\"open_db\">Open database</string>\n    <string name=\"db_name\">Enter database name</string>\n    <string name=\"create_db\">Create database</string>\n    <string name=\"next\">Next step</string>\n    <string name=\"up\">Previous</string>\n    <string name=\"hint_select_db_path\">Click to set the data saving method</string>\n    <string name=\"hint_select_path_type\">Pick where to save your database</string>\n    <string name=\"error_db_name_null\">Please input a database name</string>\n    <string name=\"done\">Done</string>\n    <string name=\"enter\">Enter</string>\n    <string name=\"cancel\">Cancel</string>\n    <string name=\"open_setting\">Setting</string>\n    <string name=\"hint\">Prompt</string>\n    <string name=\"help_create_db_path\">It is recommended that you use cloud paths such as Dropbox and WebDAV. This will auto-sync your database to the cloud, making it easy to use the database on other devices. \\n The database is encrypted using AES-256. Without your password, no one can open your database.</string>\n    <string name=\"help_pass_type\">Use password only: \\n Only your password is needed to open the database；\\n Password + key: \\n You need to enter a password and select a key file to open the database.</string>\n    <string name=\"help_pass_key\">A key file is equivalent to a key to open data, and is usually much more complex and reliable than a password. But it is also more difficult to hide. If you keep the database in the cloud, do not put the key with the database there. \\n You can choose any file as a key to open the database, a photo, or a TXT file. \\n⚠️ Note: If this photo or TXT document is modified, you will not be able to open the database! !! \\n It is highly recommended to use read-only files as keys.</string>\n    <string name=\"choose_file\">Choose a file</string>\n    <string name=\"choose_pass_key_file_des\">Choose any file to use as your key</string>\n    <string name=\"create_file\">Create new file</string>\n    <string name=\"create_pass_key_file_des\">Create a new file to use as your key</string>\n    <string name=\"create_pass_key_success\">Key creation [%s] succeeded</string>\n    <string name=\"error_pass_null\">Please set a database password</string>\n    <string name=\"error_enter_pass_null\">Please enter the confirmation password</string>\n    <string name=\"error_pass_unfit\">Inconsistent passwords twice</string>\n    <string name=\"hint_db_create_success\">The database [%s] was created</string>\n    <string name=\"history\">History</string>\n    <string name=\"history_record\">History record</string>\n    <string name=\"edit\">Edit</string>\n    <string name=\"change_db\">Switch database</string>\n    <string name=\"all\">Items</string>\n    <string name=\"setting\">Setting</string>\n    <string name=\"db_setting\">Database settings</string>\n    <string name=\"app_setting\">App settings</string>\n    <string name=\"welcome\">Welcome</string>\n    <string name=\"help_input_db_pass\">Enter database password</string>\n    <string name=\"need_key\">Need a key?</string>\n    <string name=\"error_uri\">URI error</string>\n    <string name=\"error_input_pass_null\">Please enter the database password</string>\n    <string name=\"error_open_db\">Could not open the database. Is the password incorrect, or is a key needed?</string>\n    <string name=\"error_open_db_key_empty\">Missing key</string>\n    <string name=\"error_open_db_pass_error\">Invalid password</string>\n    <string name=\"error_open_db_key_invalid\">Invalid key</string>\n    <string name=\"error_open_db_version_error\">Invalid Database version</string>\n    <string name=\"error_open_db_signature_error\">Invalid Database signature</string>\n    <string name=\"error_open_db_algorithm_error\">Invalid algorithm</string>\n    <string name=\"error_open_db_arcfour_error\">Invalid stream encryption algorithm</string>\n    <string name=\"hint_group_desc\">Group-%s items</string>\n    <string name=\"error_group_id_null\">group ID has no value</string>\n    <string name=\"error_entry_id_null\">entry ID has no value</string>\n    <string name=\"create_time\">Built in %s</string>\n    <string name=\"expire_time\">Expiring on %s</string>\n    <string name=\"hint_attr\">Advanced properties</string>\n    <string name=\"attachment\">Attachment</string>\n    <string name=\"hint_ex_property\">Extra attributes</string>\n    <string name=\"expire\"><![CDATA[Expired at <font color=\"#FF0000\">%s</font>]]></string>\n    <string name=\"del_group\">Delete group</string>\n    <string name=\"edit_group\">Edit group</string>\n    <string name=\"success\">Success</string>\n    <string name=\"fail\">Fail</string>\n    <string name=\"del_entry\">Delete item</string>\n    <string name=\"del_history\">Delete History record</string>\n    <string name=\"copy_user_name\">Copy username</string>\n    <string name=\"copy_password\">Copy password</string>\n    <string name=\"copy_totp\">Copy TOTP token</string>\n    <string name=\"hint_del_group\"><![CDATA[Really delete the group【<font color=\"#FF0000\">%s</font>】?<br>This moves it to trash, where you can restore it later.]]></string>\n    <string name=\"hint_del_group_no_recycle\"><![CDATA[Really delete the 【<font color=\"#FF0000\">%s</font>】 group?]]></string>\n    <string name=\"hint_del_entry\"><![CDATA[Really delete the 【<font color=\"#FF0000\">%s</font>】 item? <br>This moves it to trash, where you can restore it later.]]></string>\n    <string name=\"hint_del_entry_no_recycle\"><![CDATA[Really delete the 【<font color=\"#FF0000\">%s</font>】entry permanently?]]></string>\n    <string name=\"group_name\">Group name</string>\n    <string name=\"create_group\">Build group</string>\n    <string name=\"error_group_name_null\">The group name is empty</string>\n    <string name=\"create_group_fail\">Could not build group</string>\n    <string name=\"create_group_success\">Group built</string>\n    <string name=\"modify_group\">Edit group</string>\n    <string name=\"error_group_no_modify\">No group changes</string>\n    <string name=\"title_too_long\">The group name is too long</string>\n    <string name=\"choose_icon\">Choose icon</string>\n    <string name=\"undo_entry\">Resumed…</string>\n    <string name=\"undo_to\">Move to this group</string>\n    <string name=\"undo_grouped\">The group was moved to the specified location</string>\n    <string name=\"undo_entryed\">The item was moved to the specified location</string>\n    <string name=\"hint_copy_user\">Username copied to clipboard</string>\n    <string name=\"hint_copy_pass\">Password copied to clipboard</string>\n    <string name=\"hint_copy_totp\">Copied TOTP token to clipboard</string>\n    <string name=\"create_totp\">Generate OTP token</string>\n    <string name=\"safety_set\">Security Settings</string>\n    <string name=\"fingerprint_unlock\">Fingerprint unlock</string>\n    <string name=\"db_handle\">Database operations</string>\n    <string name=\"modify_db_name\">Modify the database name</string>\n    <string name=\"modify_db_pass\">Change database password</string>\n    <string name=\"out_db\">Export database</string>\n    <string name=\"db_name_no_alter\">The database name has not changed</string>\n    <string name=\"db_name_modify\">Database name changed</string>\n    <string name=\"no_record\">No record</string>\n    <string name=\"hint_query\">Username, title, URL, advanced attributes …</string>\n    <string name=\"copy_to_clip\">Copy to clipboard</string>\n    <string name=\"show_pass\">Show password</string>\n    <string name=\"ope_url\">Open link</string>\n    <string name=\"hide_pass\">Hide password</string>\n    <string name=\"hint_copy_to_clip\">Data copied to clipboard</string>\n    <string name=\"verify_finger\">Fingerprint verification </string>\n    <string name=\"verify_finger_fail\">Could not verify fingerprint</string>\n    <string name=\"ban_fingerprint\">No fingerprint unlocking</string>\n    <string name=\"quick_use_fingerprint\">Only use fingerprint to unlock in quick-unlock interface</string>\n    <string name=\"use_full_fingerprint\">Full fingerprint unlock</string>\n    <string name=\"hint_full_fingerprint\">The fingerprint is used to unlock the database in the quick-unlock interface and database-unlock interface, KeePassA encrypts and stores your database master password and key info in the local keystore of the Android system, and uses fingerprint authorization for protection. Only your fingerprint can unlock the database.</string>\n    <string name=\"hint_quick_unlock\">Enter the last %s digits of your database password</string>\n    <string name=\"hint_input_title\">Title</string>\n    <string name=\"hint_input_user_name\">Username</string>\n    <string name=\"hint_input_url\">Link address</string>\n    <string name=\"notice\">Note</string>\n    <string name=\"hint_input_tag\">Tag1, Tag2</string>\n    <string name=\"hint_input_cover_url\">Overwrite link</string>\n    <string name=\"create_entry\">Create entry</string>\n    <string name=\"save\">Save</string>\n    <string name=\"len\">Length</string>\n    <string name=\"upper\">Uppercase character</string>\n    <string name=\"lower\">Lower case character</string>\n    <string name=\"numer\">Numeral</string>\n    <string name=\"minus\">Minus</string>\n    <string name=\"underline\">Underline</string>\n    <string name=\"space\">Space</string>\n    <string name=\"special\">Special</string>\n    <string name=\"bracket\">Bracket</string>\n    <string name=\"pass_generater\">Password generator</string>\n    <string name=\"error_genera_params\">Please select at least one password generation type</string>\n    <string name=\"hint_pass_null\">The password is empty</string>\n    <string name=\"date\">Date</string>\n    <string name=\"time\">Time</string>\n    <string name=\"choose_dir\">Save to current group</string>\n    <string name=\"normal_account\">Normal account</string>\n    <string name=\"add_more\">Add more</string>\n    <string name=\"warning\">Warning</string>\n    <string name=\"create_entry_no_save\">Exit current page without saving entry?</string>\n    <string name=\"add_custom_str\">Create custom fields</string>\n    <string name=\"hint_input_attr_str_key\">Field name</string>\n    <string name=\"hint_input_attr_str_value\">Field value</string>\n    <string name=\"hint_protect_str\">Protected field</string>\n    <string name=\"error_attr_str_null\">The field name is empty</string>\n    <string name=\"del_attr_str\">Delete custom fields</string>\n    <string name=\"del_attr_file\">Delete attachment</string>\n    <string name=\"error_attr_file_too_large\">Reduce memory use by not attaching files larger than 10 MB</string>\n    <string name=\"download_file\">Save attachment to SD card</string>\n    <string name=\"open_whit_text\">Open with internal text</string>\n    <string name=\"open_whit_img\">Open with internal image viewer</string>\n    <string name=\"open_whit_other\">Save to cache and open with system app</string>\n    <string name=\"save_file_success\">The file [%s] was saved</string>\n    <string name=\"txt_viewer\">File viewer</string>\n    <string name=\"save_db_fail\">Could not save database</string>\n    <string name=\"save_db_success\">Database saved</string>\n    <string name=\"error_pass\">Password error</string>\n    <string name=\"db_pass_no_alter\">The new password is the same as the old one</string>\n    <string name=\"hint_db_pass_modify_success\">Use the new database password to open the database the next time</string>\n    <string name=\"hint_db_pass_modify\">Database password changed</string>\n    <string name=\"app_feedback\">Feedback</string>\n    <string name=\"app_debug\">Get log file</string>\n    <string name=\"error_db_pass_too_short\">Make the database password more than 6 characters or more to protect your data</string>\n    <string name=\"autofill_sign_in_prompt\">Auto-fill password with KeePassA</string>\n    <string name=\"hint_open_backgroun_start\">You have set up auto-fill, but KeePassA does not have the appropriate permissions, please authorize the KeePassA backend pop-up screen in the settings page.</string>\n    <string name=\"hint_background_start\"><![CDATA[Go to<font color=\"#FF0000\">【%s】</font>and grant the background pop-up permissions to make better use of the auto-fill service.<br> <font color=\"#FF0000\">KeePassA shows the corresponding interface for opening the database when you use the autofill function</font>]]></string>\n    <string name=\"setting_miui_background_start\">Settings &gt; Application Settings &gt; Application Management &gt; KeePassA &gt; Permission Management</string>\n    <string name=\"setting_vivo_background_start\">Settings &gt; More Settings &gt; Permission Management &gt; KeePassA &gt; Permission Settings &gt; Background Popup</string>\n    <string name=\"no_matching_entry\">No matching item</string>\n    <string name=\"hint_save_auto_fill\"><![CDATA[Whether to associate the package name【<font color=\"#FF0000\">%s</font>】with the entry【<font color=\"#FF0000\">%s</font>】]]></string>\n    <string name=\"relevance_db\">Linked data </string>\n    <string name=\"modify_time\">Modify time</string>\n    <string name=\"lose_time\">Expiration time</string>\n    <string name=\"update_group_fail\">Could not update group info</string>\n    <string name=\"update_group_success\">Group info updated</string>\n    <string name=\"title\">Title</string>\n    <string name=\"feedback\">Feedback ^_^</string>\n    <string name=\"submit\">Submit</string>\n    <string name=\"send_email_fail\">Install an app to send e-mails with first</string>\n    <string name=\"feedback_email_msg\">Hello, I\\'m KeePassA, do you have any questions?&lt;br /&gt; &lt;br /&gt; &lt;br /&gt; _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ &lt;br /&gt; Brand: %s &lt;br /&gt; Model: %s &lt;br /&gt; System version:%s &lt;br /&gt; Resolution: %s &lt;br /&gt; App version: %s </string>\n    <string name=\"mark_not_exit\">Application market does not exist</string>\n    <string name=\"set_open_auto_fill_title\">Auto-fill service</string>\n    <string name=\"set_key_praise_value\">Rate KeePassA</string>\n    <string name=\"other_set\">Other settings</string>\n    <string name=\"open_quick_unlock\">Quick Unlock</string>\n    <string name=\"quick_pass_type_summary\">résumé du type de passe rapide</string>\n    <string name=\"clean_clip_time\">Empty Clipboard</string>\n    <string name=\"clean_clip_time_summary\">After copying password, empty clipboard after %s</string>\n    <string name=\"auto_lock_db_time\">Auto-lock the database</string>\n    <string name=\"auto_lock_db_time_summary\">Auto-lock the database after %s if no operations are made</string>\n    <string name=\"quick_pass_type\">Short password interception location</string>\n    <string name=\"des_quick_unlock\">Unlock your database with a short password</string>\n    <string name=\"error_short_pass_lenght\">Make your short password of 6 or fewer characters</string>\n    <string name=\"error_short_pass_short\">Make your short password of 3 or more characters</string>\n    <string name=\"quick_pass_len_title\">Short password length</string>\n    <string name=\"quick_pass_len_summary\">Short password length: %s</string>\n    <string name=\"set_key_value\">Choose language</string>\n    <string name=\"dropbox_msg\"><![CDATA[Upholding the principle of respecting user privacy, KeepPassA will not ask for permission to all files in your Dropbox, KeePassA <font color = \"# FF0000\"> Only access to the 【Apps/KeePassA】 folder</ font >.<br />Therefore: <br />\n<b>If you already have a KeePass database, grant KeePassA access to the 【Apps/KeePassA】 folder in KeePassA and save the KeePass database to the 【Application/KeePassA】 folder after authorization is complete.</b><br /><br />\nIf you are creating a new KeePass database, the above operations are not be needed. After you create the database, KeePassA auto-creates the corresponding folder and uploads the database to the 【Apps/KeePassA】 directory of Dropbox. ]]></string>\n    <string name=\"cover\">Cover</string>\n    <string name=\"file_conflict_msg\">Conflict between cloud data and local data, Please select the coverage method. The conflicting entries are:\\n %s</string>\n    <string name=\"merge\">Merge</string>\n    <string name=\"sync_db\">Synchronous database</string>\n    <string name=\"net_error\">Network abnormal</string>\n    <string name=\"cover_local\">Local</string>\n    <string name=\"cover_cloud\">Cloud</string>\n    <string name=\"auth\">Auth</string>\n    <string name=\"invalid\">Invalid</string>\n    <string name=\"no_file\">No file</string>\n    <string name=\"login\">Login</string>\n    <string name=\"helper_webdav\">e.g: https://dav.example.org/dav/[your folder]/xxx.kdbx</string>\n    <string name=\"helper_webdav_dir\">e.g: https://dav.example.org/dav/[your folder]/</string>\n    <string name=\"hint_webdav_url\">File URL</string>\n    <string name=\"hint_please_input\">Please input %s</string>\n    <string name=\"db_file_no_exist\">The database file does not exist</string>\n    <string name=\"invalid_auth\">Could not authorize. Please try again.</string>\n    <string name=\"afs\">Android file system</string>\n    <string name=\"hint_kdbx_name\">Wrong suffix name, the suffix name should be \".kdbx\"</string>\n    <string name=\"start_upload_db\">Start uploading the database to your network disk</string>\n    <string name=\"unlocked\">Unlocked</string>\n    <string name=\"notify_channel_db_open\">Database unlock notification</string>\n    <string name=\"notify_quick_unlock_start\">The database locked, quick unlock is on</string>\n    <string name=\"notify_db_locked\">The database is locked</string>\n    <string name=\"search\">Search</string>\n    <string name=\"disable\">Turn off</string>\n    <string name=\"quick_unlock\">Quick unlock</string>\n    <string name=\"db_unlock\">Unlock database</string>\n    <string name=\"hint_quick_use_fingerprint\">Only use fingerprint to unlock on the quick-unlock page</string>\n    <string name=\"version_log\">Upgrade log</string>\n    <string name=\"hint_finger_print_verfiy\">Re-verify your fingerprints to use the new settings</string>\n    <string name=\"closed\">Closed</string>\n    <string name=\"upload\">Upload</string>\n    <string name=\"open1\">open</string>\n    <string name=\"add_attr_file\">Add attachments </string>\n    <string name=\"hint_fingerprint_modify\">Check that the fingerprint has changed, for your password security, please reset the fingerprint unlock function</string>\n    <string name=\"hint_please_open_database\">Please open the database</string>\n    <string name=\"set_delete_setting\">Delete settings</string>\n    <string name=\"set_delete_no_into_recycle_bin\">Delete entries from the database permanently</string>\n    <string name=\"set_delete_no_into_recycle_bin_des\">Move deleted items to trash</string>\n    <string name=\"warning_rooted\">Don\\'t open your database on a rooted phone to keep it safe</string>\n    <string name=\"hint_security_green\">\"The app is in a safe environment \"</string>\n    <string name=\"hint_security_red\">Your device is rooted and the database is in a very dangerous environment</string>\n    <string name=\"hint_security_yellow\">Warning, running in the simulator, this environment will bring some unpredictable risks to the database</string>\n    <string name=\"donate\">Donate</string>\n    <string name=\"donate_desc\">[KeePassA](https://github.com/AriaLyy/KeepassA) is libre software. Donate to support our work, Thank you very much.</string>\n    <string name=\"dev_birthday\">Today is the developer’s birthday🎂. Buy the developer a beer?</string>\n    <string name=\"totp_defaule\">Default RFC 6238 token settings</string>\n    <string name=\"totp_steam\">Steam token settings</string>\n    <string name=\"totp_custom\">Use custom settings</string>\n    <string name=\"totp_custom_hint\">Custom settings </string>\n    <string name=\"totp_Arithmetic\">Arithmetic: </string>\n    <string name=\"totp_time\">Time(s): </string>\n    <string name=\"totp_len\">Length: </string>\n    <string name=\"totp_key_error\">Could not generate a TOTP code, abnormal key</string>\n    <string name=\"sort\">Sort</string>\n    <string name=\"sort_chart_desc\">First letter descending(Z-A)</string>\n    <string name=\"sort_chart_asc\">First letter ascending(A-Z)</string>\n    <string name=\"sort_time_asc\">Time earlist-latest</string>\n    <string name=\"sort_time_desc\">Time latest-earliest</string>\n    <string name=\"ime_label\">KeePassA security keyboard</string>\n    <string name=\"set_open_kpa_ime_title\">Security keyboard</string>\n    <string name=\"no_totp_token\">No TOTP token</string>\n    <string name=\"ime_entry_other_info\">Fill in other fields</string>\n    <string name=\"url\">URL</string>\n    <string name=\"hint_not_nore_info\">No more info</string>\n    <string name=\"error_uri_grant_permission\">URI authorization failed</string>\n    <string name=\"license\">Libre software license</string>\n    <string name=\"move\">Move</string>\n    <string name=\"set_key_sum_main_show_entries\">Show all items after unlocking the database</string>\n    <string name=\"set_key_sum_main_show_history\">Show history after unlocking the database</string>\n    <string name=\"set_key_title_main_allow_show_entries\">Home page</string>\n    <string name=\"expand\">Expand</string>\n    <string name=\"shrink\">Shrink</string>\n    <string name=\"ui_setting\">UI Setting</string>\n    <string name=\"set_key_title_opr_env_check\">Operating environment check</string>\n    <string name=\"set_key_title_opr_env_check_turn_off\">Off</string>\n    <string name=\"set_key_title_opr_env_check_turn_on\">On</string>\n    <string name=\"cur_app_not_autofill\">No auto-filling</string>\n    <string name=\"other\">Other</string>\n    <string name=\"ref_entry\">Reference entry</string>\n    <string name=\"multi_choice\">Multiple choice</string>\n    <string name=\"set_summary_screen_lock_off\">Doesn\\'t lock the database</string>\n    <string name=\"set_summary_screen_lock_on\">Auto-locks the database</string>\n    <string name=\"set_title_screen_lock\">Screen lock</string>\n    <string name=\"close\">Close</string>\n    <string name=\"Alipay\">Alipay</string>\n    <string name=\"error_webdav_end_suffix\">Use the folder path. E.g: \\\"https://example.org/dav/kpa/\\\"</string>\n    <string name=\"error_webdav_end_dav_suffix\">/dav/ path is not allowed. Use a valid path, such as: \\\"https://example.org/dav/kpa/\\\"</string>\n    <string name=\"set_key_title_fillet_bg_icon\">Rounded list icon corners</string>\n    <string name=\"set_key_title_fillet_bg_icon_turn_on\">On</string>\n    <string name=\"set_key_title_fillet_bg_icon_turn_off\">Off</string>\n    <string name=\"set_key_title_show_state_bar\">Status bar</string>\n    <string name=\"set_key_title_show_state_bar_turn_on\">Show</string>\n    <string name=\"set_key_title_show_state_bar_turn_off\">Hide</string>\n    <string name=\"set_key_title_close_load_anim\">Loading animation</string>\n    <string name=\"set_key_title_close_load_anim_turn_on\">On</string>\n    <string name=\"set_key_title_close_load_anim_turn_off\">Off</string>\n    <string name=\"s_default\">Default</string>\n    <string name=\"customize\">Customize</string>\n    <string name=\"set_key_title_auto_lock_database\">Auto-lock the database</string>\n    <string name=\"set_key_title_auto_lock_database_turn_on\">Yes</string>\n    <string name=\"set_key_title_auto_lock_database_turn_off\">No</string>\n    <string name=\"translate_language\">Translate the app to your language</string>\n    <string name=\"get_token_fail\">Failed to obtain token</string>\n    <string name=\"one_drive_init_failure\">OneDrive init failure</string>\n    <string name=\"one_drive_hint\"><![CDATA[In order to protect your privacy, KeepassA will only access the<font color=\"#FF0000\">【application/KeepassA】</font>directory. If you want to read the Keepass database of OneDrive, <font color=\"#FF0000\">please store the database in this directory.</font>]]></string>\n    <string name=\"open_permissions\">Open permissions</string>\n    <string name=\"miui_permissions\">The miui system needs permission to open the background pop-up interface in order to use the auto-fill function.</string>\n    <string name=\"set_key_main_show_totp_tab_title\">Home page shows TOTP category</string>\n    <string name=\"set_key_main_show_totp_tab_on\">Display TOTP category</string>\n    <string name=\"set_key_main_show_totp_tab_off\">Do not display TOTP category</string>\n    <string name=\"set_key_theme_style_title\">Theme style</string>\n    <string name=\"set_key_theme_style_summary\">Following system</string>\n    <string-array name=\"sek_ley_theme_style_entries\">\n        <item>Following system</item>\n        <item>Dark</item>\n        <item>Light</item>\n    </string-array>\n    <string name=\"error_keystore\">Keystore exception, please try again</string>\n    <string name=\"kpa_totp\">Token</string>\n    <string name=\"kpa_copy\">Copy</string>\n    <string-array name=\"quick_pass_type_entries\">\n        <item>Database password in front of %s</item>\n        <item>%s at the end of the database password</item>\n    </string-array>\n    <string-array name=\"quick_lock_entries\">\n        <item>2 minute</item>\n        <item>5 minute</item>\n        <item>10 minute</item>\n        <item>20 minute</item>\n    </string-array>\n    <string name=\"error_qr_code_str\">The QR code is abnormal. Please make sure that the QR code is supported by Google Authenticator</string>\n    <string name=\"error_connect_play\">Could not connect to Google service</string>\n    <string name=\"disposable\">Once</string>\n    <string name=\"monthly\">Monthly</string>\n    <string name=\"year\">Year</string>\n    <string name=\"error_donate\">Donation failed</string>\n    <string name=\"thank_donate\">Thank you for your donation.</string>\n    <string name=\"google_play\">Google Play</string>\n    <string name=\"my_collection\">My collection</string>\n    <string name=\"current_collection_num\">%1$s collections</string>\n    <string name=\"no_collection\">No collection</string>\n    <string name=\"error_open_db_webdav\">Could not open the database, because the WebDAV file does not exist.</string>\n    <string name=\"helper_webdav_service\">Select WebDAV service</string>\n    <string name=\"error_is_root\">Is already the root directory</string>\n    <string name=\"select_cur_path\">Select the folder</string>\n    <string name=\"hint_cloud_file_already_exist\">%1$s already exists. Overwrite this file\\?</string>\n    <string name=\"select_save_path\">Select a save path</string>\n    <string name=\"one_drive_load_user_failure\">Could not load user info.</string>\n    <string name=\"hint_open_background_start\">Please open background eject permission, otherwise can\\'t use auto-fill</string>\n    <string name=\"hostname\">Hostname</string>\n    <string name=\"port\">Port</string>\n    <string name=\"webdav_host_name_hint\">Please type in your hostname</string>\n    <string name=\"webdav_port_hint\">Port</string>\n    <string name=\"webdav_port_name_null\">Hostname cannot be empty</string>\n    <string name=\"privacy_agreement\">Privacy agreement</string>\n    <string name=\"ime_hint_open_auto_fill\">Please turn on the auto-fill service</string>\n    <string name=\"please_open_proxy\">please open proxy</string>\n    <string name=\"invalid_img\">Invalid Image</string>\n    <string name=\"preemptive\">Preemptive</string>\n    <string name=\"fail_unsupported_Systems_O\">Only support P or higher system</string>\n    <string name=\"hint_webdav_jgy\">Please use the password in [Account->Security->Third-party Applications Management].</string>\n    <string name=\"hint_auto_fill_save\">Associate current data to existing entries?</string>\n    <string name=\"hint_dont_show_tips\">Don\\'t show tips</string>\n    <string name=\"title_tip_of_day\">Tip of the day</string>\n    <string name=\"base_info\">Base info</string>\n    <string name=\"tag\">Tag</string>\n    <string name=\"collection\">Collection</string>\n    <string name=\"totp\">TOTP</string>\n    <string name=\"add_attr_str\">Add attribute</string>\n    <string name=\"delete\">Delete</string>\n    <string name=\"create_tag\">New tag</string>\n    <string name=\"tag_name\">Tag name</string>\n    <string name=\"add_tag\">Add tag</string>\n    <string name=\"open_file\">Open File</string>\n    <string name=\"modify_str_attr\">Modify properties</string>\n    <string name=\"error_file\">The file is abnormal, please delete &lt;font color=#FF0000&gt;%s&lt;/font&gt;</string>\n    <string name=\"not_souper_otp\">Unsupported totp</string>\n    <string name=\"modify_totp\">Modify OTP token</string>\n    <string name=\"error_totp\">Error OTP</string>\n    <string name=\"file_save_success\">Attachment saved successfully</string>\n    <string name=\"options\">Options</string>\n    <string name=\"hint_bracket_char\">()&lt;&gt;{}[]</string>\n</resources>"
  },
  {
    "path": "app/src/main/res/values/styles.xml",
    "content": "<resources>\n\n  <!-- Base application theme. -->\n  <style name=\"AppTheme.NoActionBar\" parent=\"BaseTheme\">\n\n  </style>\n\n  <style name=\"BaseTheme\" parent=\"Theme.Material3.DayNight\">\n    <item name=\"windowActionBar\">false</item>\n    <item name=\"windowNoTitle\">true</item>\n    <item name=\"colorPrimary\">@color/colorPrimary</item>\n    <item name=\"colorPrimaryDark\">@color/colorPrimaryDark</item>\n    <item name=\"colorAccent\">@color/colorAccent</item>\n    <item name=\"android:fontFamily\">@font/roboto_regular</item>\n    <item name=\"android:windowAnimationStyle\">@style/windowAnimatorDemo</item>\n    <!--    <item name=\"android:windowIsTranslucent\">true</item>-->\n    <!--    处理状态栏颜色 -->\n    <!--    <item name=\"android:windowTranslucentStatus\">false</item>-->\n    <!--    <item name=\"android:windowTranslucentNavigation\">false</item>-->\n    <item name=\"android:statusBarColor\">@color/colorPrimary</item>\n    <item name=\"android:windowActivityTransitions\">true</item>\n\n  </style>\n\n  <style name=\"windowAnimatorDemo\">\n    <item name=\"android:activityOpenEnterAnimation\">@anim/translate_right_in</item>\n    <item name=\"android:activityOpenExitAnimation\">@anim/translate_left_out</item>\n    <item name=\"android:activityCloseEnterAnimation\">@anim/translate_left_in</item>\n    <item name=\"android:activityCloseExitAnimation\">@anim/translate_right_out</item>\n  </style>\n\n\n\n  <style name=\"DialogActivityStyle\" parent=\"Theme.Material3.DayNight.Dialog\">\n    <!--设置dialog的背景-->\n    <item name=\"android:windowBackground\">@android:color/transparent</item>\n    <!--设置无标题-->\n    <item name=\"android:windowNoTitle\">true</item>\n    <!--是否浮现在activity之上-->\n    <item name=\"android:windowIsFloating\">true</item>\n    <!--背景是否模糊显示-->\n    <item name=\"android:backgroundDimEnabled\">true</item>\n    <!--没有ActionBar -->\n    <item name=\"windowActionBar\">false</item>\n    <!--设置全屏 -->\n    <item name=\"android:windowFullscreen\">true</item>\n    <!--去掉头部标题栏 -->\n    <item name=\"windowNoTitle\">true</item>\n  </style>\n\n  <!-- 设置Toolbar标题字体的颜色大小 -->\n  <!--  app:titleTextAppearance=\"@style/Toolbar.TitleText\"-->\n  <style name=\"Toolbar.TitleText\" parent=\"TextAppearance.Widget.AppCompat.Toolbar.Title\">\n    <item name=\"android:textSize\">@dimen/text_size_biggest</item><!--toolbar标题字体大小-->\n    <item name=\"android:textColor\">@color/text_black_color</item><!--toolbar标题字体颜色-->\n  </style>\n\n  <!-- 设置Toolbar 子标题subtitle标题字体的大小 -->\n  <!--  app:subtitleTextAppearance=\"@style/Toolbar.SubTitleText\"-->\n  <style name=\"Toolbar.SubTitleText\" parent=\"TextAppearance.Widget.AppCompat.Toolbar.Title\">\n    <item name=\"android:textSize\">@dimen/text_size_normal</item><!--子标题字体大小-->\n    <item name=\"android:textColor\">@color/text_black_color</item><!--子标题字体颜色-->\n  </style>\n\n  <!--toolbar mean的字体颜色和大小-->\n  <!--  app:theme=\"@style/ToolbarTheme\"-->\n  <style name=\"ToolbarMenuTheme\" parent=\"Theme.AppCompat.Light.NoActionBar\">\n    <item name=\"actionMenuTextColor\">@color/text_black_color</item> <!--menu字体颜色-->\n    <item name=\"android:textSize\">@dimen/text_size_normal</item> <!--menu字体大小-->\n  </style>\n\n  <style name=\"Toolbar.Def\" parent=\"Theme.AppCompat.Light.NoActionBar\">\n    <item name=\"navigationIcon\">@drawable/ic_up</item>\n    <item name=\"subtitleTextAppearance\">@style/Toolbar.SubTitleText</item>\n    <item name=\"theme\">@style/ToolbarMenuTheme</item>\n    <item name=\"titleTextAppearance\">@style/Toolbar.TitleText</item>\n    <item name=\"android:layout_height\">48dp</item>\n    <item name=\"android:background\">@color/background_color</item>\n  </style>\n\n  <!-- 设置CollapsingToolbarLayout标题 展开的字体的颜色大小 -->\n  <!--  app:expandedTitleTextAppearance=\"@style/Toolbar.TitleText\"-->\n  <style name=\"CollapsingToolbarLayout.ExpandedTitleText\" parent=\"TextAppearance.Widget.AppCompat.Toolbar.Title\">\n    <item name=\"android:textColor\">@color/text_black_color</item><!--toolbar标题字体颜色-->\n    <item name=\"android:textSize\">36sp</item>\n  </style>\n\n  <!-- 设置CollapsingToolbarLayout标题 收缩后的字体的颜色大小 -->\n  <style name=\"CollapsingToolbarLayout.CollapsedTitleText\" parent=\"TextAppearance.Widget.AppCompat.Toolbar.Title\">\n    <item name=\"android:textColor\">@color/text_black_color</item><!--toolbar标题字体颜色-->\n    <item name=\"android:textSize\">@dimen/text_size_biggest</item>\n  </style>\n\n  <style name=\"dialogStyle\" parent=\"@android:style/Animation.Dialog\">\n    <item name=\"android:windowEnterAnimation\">@anim/dialog_y_enter</item>\n    <!-- 进入时的动画 -->\n    <item name=\"android:windowExitAnimation\">@anim/dialog_y_exit</item>\n    <!-- 退出时的动画 -->\n  </style>\n\n  <!-- 对话框样式 -->\n  <style name=\"Theme.Light.Dialog\" parent=\"android:style/Theme.Dialog\">\n    <item name=\"android:windowBackground\">@android:color/transparent</item>\n    <item name=\"android:windowNoTitle\">true</item>\n    <item name=\"android:windowIsFloating\">true</item>\n    <item name=\"android:windowContentOverlay\">@null</item>\n    <item name=\"android:scrollHorizontally\">true</item>\n  </style>\n\n  <!--全屏并且带有状态栏的对话框-->\n  <style name=\"FullScreenDialog\" parent=\"Theme.AppCompat.Light.NoActionBar\">\n    <item name=\"android:windowNoTitle\">true</item>\n    <item name=\"android:windowIsFloating\">false</item>\n    <item name=\"colorPrimary\">@color/colorPrimary</item>\n    <item name=\"colorPrimaryDark\">@color/colorPrimaryDark</item>\n  </style>\n\n  <style name=\"PathTypeDialogAnim\" parent=\"android:Animation\">\n    <item name=\"android:windowEnterAnimation\">@anim/dialog_y_enter</item>\n    <item name=\"android:windowExitAnimation\">@anim/dialog_y_exit</item>\n  </style>\n\n  <!-- InputEditTextLayout 错误样式 -->\n  <style name=\"InputEditTextErrorStyle\" parent=\"TextAppearance.AppCompat\">\n    <item name=\"android:textSize\">@dimen/text_size_small</item>\n    <item name=\"android:textColor\">@color/error_color</item>\n  </style>\n\n  <!-- InputEditTextLayout hint样式 -->\n  <style name=\"InputEditTextHintStyle\" parent=\"TextAppearance.AppCompat\">\n    <item name=\"android:textSize\">@dimen/text_size_small</item>\n    <item name=\"android:textColor\">@color/colorAccent</item>\n  </style>\n\n  <!-- InputEditTextLayout 长度统计样式 -->\n  <style name=\"InputEditTextOverAppearance\" parent=\"TextAppearance.AppCompat\">\n    <item name=\"android:textSize\">@dimen/text_size_small</item>\n    <item name=\"android:textColor\">@color/text_blue_color</item>\n  </style>\n\n  <style name=\"KapTabLayoutStyle\">\n    <item name=\"tabInlineLabel\">true</item>\n    <item name=\"tabIndicatorColor\">@color/colorPrimary</item>\n    <item name=\"tabSelectedTextColor\">@color/colorPrimary</item>\n    <item name=\"tabTextColor\">@color/text_gray_color</item>\n    <item name=\"tabBackground\">@color/background_color</item>\n  </style>\n\n  <style name=\"KpaEntryDetailTextStyle\">\n    <item name=\"android:drawablePadding\">16dp</item>\n    <item name=\"android:paddingStart\">6dp</item>\n    <item name=\"android:gravity\">center_vertical</item>\n    <item name=\"android:paddingTop\">8dp</item>\n    <item name=\"android:paddingBottom\">8dp</item>\n    <item name=\"android:textColor\">@color/text_black_color</item>\n    <item name=\"android:textSize\">@dimen/text_size_normal</item>\n    <item name=\"icon_size\">24dp</item>\n    <item name=\"android:background\">@drawable/ripple_white_selector</item>\n  </style>\n\n  <style name=\"NoAnimDialog\">\n    <item name=\"android:windowAnimationStyle\">@null</item>\n  </style>\n\n  <style name=\"TextInputLayoutStyle\">\n    <item name=\"endIconTint\">@color/color_icon_grey</item>\n  </style>\n\n  <!-- 标题字体 -->\n  <style name=\"KpaTitleTextStyle\">\n    <item name=\"android:textSize\">@dimen/text_size_big</item>\n    <item name=\"android:textColor\">@color/text_black_color</item>\n  </style>\n\n  <!-- 内容字体 -->\n  <style name=\"KpaContentTextStyle\">\n    <item name=\"android:textSize\">@dimen/text_size_normal</item>\n    <item name=\"android:textColor\">@color/text_gray_color</item>\n  </style>\n\n  <!-- radiobt 风格 -->\n  <style name=\"KpaRadioButtonStyle\">\n    <item name=\"android:textSize\">@dimen/text_size_normal</item>\n    <item name=\"android:textColor\">@color/text_black_color</item>\n  </style>\n\n  <!-- 键盘按钮风格 -->\n  <style name=\"KpaImeKeyStyle\">\n    <item name=\"android:layout_marginStart\">8dp</item>\n    <item name=\"android:clickable\">true</item>\n    <item name=\"android:paddingBottom\">8dp</item>\n    <item name=\"android:paddingTop\">8dp</item>\n    <item name=\"android:background\">@drawable/bg_ime_key</item>\n  </style>\n\n  <style name=\"KpaDialogLayoutStyle\">\n    <item name=\"android:background\">@drawable/bg_white_radius_4</item>\n    <item name=\"android:elevation\">4dp</item>\n  </style>\n\n  <!-- 可展开的textView样式  -->\n  <style name=\"KpaExpandAbleTextViewStyle\">\n    <item name=\"android:textColor\">@color/text_black_grey_color</item>\n    <item name=\"android:textSize\">@dimen/text_size_normal</item>\n    <item name=\"android:padding\">6dp</item>\n    <item name=\"android:background\">@drawable/bg_gray_radius_4</item>\n    <item name=\"maxCollapsedLines\">6</item>\n  </style>\n\n  <style name=\"KpaEditTextStyle\">\n    <item name=\"android:background\">@color/transparent</item>\n    <item name=\"android:singleLine\">true</item>\n    <item name=\"android:textColor\">@color/text_black_grey_color</item>\n    <item name=\"android:textColorHint\">@color/text_hint_color</item>\n    <item name=\"android:textSize\">@dimen/text_size_normal</item>\n  </style>\n\n  <style name=\"KpaTextInputLayoutStyle\">\n    <item name=\"android:height\">50dp</item>\n  </style>\n\n  <style name=\"KpaEditTextStyleNew\" parent=\"Widget.Material3.TextInputLayout.OutlinedBox\">\n    <item name=\"android:singleLine\">true</item>\n    <item name=\"android:textColor\">@color/text_black_grey_color</item>\n    <item name=\"android:textColorHint\">@color/text_hint_color</item>\n    <item name=\"android:textSize\">@dimen/text_size_normal</item>\n  </style>\n\n  <style name=\"KpaEditTextMultiLineStyleNew\" parent=\"Widget.Material3.TextInputLayout.OutlinedBox\">\n    <item name=\"android:textColor\">@color/text_black_grey_color</item>\n    <item name=\"android:textColorHint\">@color/text_hint_color</item>\n    <item name=\"android:textSize\">@dimen/text_size_normal</item>\n  </style>\n\n  <!--ShapeableImageView 圆 -->\n  <style name=\"CircleStyle\">\n    <item name=\"cornerFamily\">rounded</item>\n    <item name=\"cornerSize\">50%</item>\n  </style>\n\n\n  <style name=\"KpaIconButton\" parent=\"Widget.Material3.Button.ElevatedButton.Icon\">\n    <item name=\"android:background\">@drawable/ripple_white_selector</item>\n    <item name=\"android:textColor\">@color/text_black_color</item>\n    <item name=\"android:textSize\">@dimen/text_size_normal</item>\n    <item name=\"iconSize\">30dp</item>\n    <item name=\"iconGravity\">top</item>\n    <item name=\"iconTint\">@color/color_icon_grey</item>\n  </style>\n\n</resources>\n\n"
  },
  {
    "path": "app/src/main/res/values-ar/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"db\">قاعدة البيانات</string>\n</resources>"
  },
  {
    "path": "app/src/main/res/values-cs/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"db\">Databáze</string>\n</resources>"
  },
  {
    "path": "app/src/main/res/values-de-rDE/arrays.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n\n</resources>"
  },
  {
    "path": "app/src/main/res/values-de-rDE/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"cancel\">Abbrechen</string>\n    <string name=\"error_db_name_null\">Bitte geben Sie einen Datenbanknamen ein</string>\n    <string name=\"db_name\">Geben Sie den Datenbanknamen ein</string>\n    <string name=\"hint_input_password\">Geben Sie ein 6 bis 16 Zeichen langes Passwort ein</string>\n    <string name=\"encrypt_type_1\">nur Passwort</string>\n    <string name=\"done\">Fertig</string>\n    <string name=\"choose_file\">Datei auswählen</string>\n    <string name=\"up\">Zurück</string>\n    <string name=\"encrypt_type\">Verschlüsselung</string>\n    <string name=\"password\">Passwort</string>\n    <string name=\"hint_set_password\">Passwort setzen</string>\n    <string name=\"create_db\">Datenbank erstellen</string>\n    <string name=\"hint_enter_password\">Passwort bestätigen</string>\n    <string name=\"open_db\">Datenbank öffnen</string>\n    <string name=\"open\">Entsperren</string>\n    <string name=\"db\">Datenbank</string>\n    <string name=\"one_drive_hint\">Zum Schutz ihrer Privatsphäre wir KeepassA nur auf ihren &lt;font color=#FF0000&gt;【application/KeepassA】&lt;/font&gt;Ordner zugreifen. Wenn Sie ihre Keepass Datenbank von OneDrive lesen möchten, &lt;font color=#FF0000&gt;speichern Sie sie bitte in diesem Ordner.&lt;/font&gt;</string>\n    <string name=\"one_drive_init_failure\">OneDrive Initialisierung fehlgeschlagen</string>\n    <string name=\"get_token_fail\">Token nicht erhalten</string>\n    <string name=\"translate_language\">Übersetze die App in deine Sprache</string>\n    <string name=\"set_key_title_auto_lock_database_turn_off\">Nein</string>\n    <string name=\"set_key_title_auto_lock_database_turn_on\">Ja</string>\n    <string name=\"set_key_title_auto_lock_database\">Datenbank automatisch sperren</string>\n    <string name=\"customize\">Anpassen</string>\n    <string name=\"s_default\">Vorgabe</string>\n    <string name=\"set_key_title_close_load_anim_turn_off\">Aus</string>\n    <string name=\"set_key_title_close_load_anim_turn_on\">An</string>\n    <string name=\"set_key_title_close_load_anim\">Ladeanimation</string>\n    <string name=\"set_key_title_show_state_bar_turn_off\">Verstecken</string>\n    <string name=\"set_key_title_show_state_bar_turn_on\">Zeigen</string>\n    <string name=\"set_key_title_show_state_bar\">Statuszeile</string>\n    <string name=\"set_key_title_fillet_bg_icon_turn_off\">Aus</string>\n    <string name=\"set_key_title_fillet_bg_icon_turn_on\">An</string>\n    <string name=\"set_key_title_fillet_bg_icon\">Listen-Icon mit abgerundeten Ecken</string>\n    <string name=\"error_webdav_end_dav_suffix\">/dav/ Pfad ist nicht zulässig. Benutze einen zulässigen Pfad, wie: \\\"https://example.org/dav/kpa/\\\"</string>\n    <string name=\"error_webdav_end_suffix\">Benutze Ordner-Pfad, z.B.: \\\"https://example.org/dav/kpa/\\\"</string>\n    <string name=\"Alipay\">Alipay</string>\n    <string name=\"close\">Schließen</string>\n    <string name=\"set_title_screen_lock\">Bildschirmsperre</string>\n    <string name=\"set_summary_screen_lock_on\">Sperrt die Datenbank automatisch</string>\n    <string name=\"set_summary_screen_lock_off\">Sperrt die Datenbank nicht</string>\n    <string name=\"multi_choice\">Mehrfachauswahl</string>\n    <string name=\"ref_entry\">Referenz-Eintrag</string>\n    <string name=\"other\">Andere</string>\n    <string name=\"cur_app_not_autofill\">Kein Auto-Filling</string>\n    <string name=\"set_key_title_opr_env_check_turn_on\">An</string>\n    <string name=\"set_key_title_opr_env_check_turn_off\">Aus</string>\n    <string name=\"set_key_title_opr_env_check\">Prüfung der Betriebsumgebung</string>\n    <string name=\"ui_setting\">UI-Einstellung</string>\n    <string name=\"shrink\">Einklappen</string>\n    <string name=\"expand\">Erweitern</string>\n    <string name=\"set_key_title_main_allow_show_entries\">Home-Page</string>\n    <string name=\"set_key_sum_main_show_history\">Zeige Historie nach dem Entsperren der Datenbank</string>\n    <string name=\"set_key_sum_main_show_entries\">Zeige alle Einträge nach dem Entsperren der Datenbank</string>\n    <string name=\"move\">Verschieben</string>\n    <string name=\"license\">Freie Software-Lizenz</string>\n    <string name=\"error_uri_grant_permission\">URI Autorisierung fehlgeschlagen</string>\n    <string name=\"hint_not_nore_info\">Keine weiteren Infos</string>\n    <string name=\"url\">URL</string>\n    <string name=\"ime_entry_other_info\">Fülle andere Felder aus</string>\n    <string name=\"no_totp_token\">Kein TOTP-Token</string>\n    <string name=\"set_open_kpa_ime_title\">Sicherheits-Keyboard</string>\n    <string name=\"ime_label\">KeePassA Sicherheits-Keyboard</string>\n    <string name=\"sort_time_desc\">Zeit absteigend</string>\n    <string name=\"sort_time_asc\">Zeit aufsteigend</string>\n    <string name=\"sort_chart_asc\">Aufsteigend (A-Z)</string>\n    <string name=\"sort_chart_desc\">Absteigend (Z-A)</string>\n    <string name=\"sort\">Sortiere</string>\n    <string name=\"totp_len\">Länge:</string>\n    <string name=\"totp_time\">Zeit(sek):</string>\n    <string name=\"totp_Arithmetic\">Algorithmus:</string>\n    <string name=\"donate_desc\">[KeePassA](https://github.com/AriaLyy/KeepassA) ist freie Software. Spende um unsere Arbeit zu fördern, vielen Dank.</string>\n    <string name=\"donate\">Spende</string>\n    <string name=\"hint_security_red\">Ihr Gerät ist gerooted und die Datenbank ist in einer sehr unsicheren Umgebung</string>\n    <string name=\"hint_security_green\">\"Diese App ist in einer sicheren Umgebung \"</string>\n    <string name=\"warning_rooted\">Öffnen Sie ihre Datenbank nicht auf einem gerooteten Telefon, um die Sicherheit aufrecht zu halten</string>\n    <string name=\"set_delete_no_into_recycle_bin_des\">Verschiebe gelöschte Einträge in den Papierkorb</string>\n    <string name=\"set_delete_no_into_recycle_bin\">Einträge aus der Datenbank endgültig löschen</string>\n    <string name=\"set_delete_setting\">Einstellungen zurücksetzen</string>\n    <string name=\"hint_please_open_database\">Bitte öffnen Sie die Datenbank</string>\n    <string name=\"add_attr_file\">Anhang hinzufügen</string>\n    <string name=\"open1\">Offen</string>\n    <string name=\"upload\">Upload</string>\n    <string name=\"closed\">Geschlossen</string>\n    <string name=\"version_log\">Upgrade Log</string>\n    <string name=\"hint_quick_use_fingerprint\">Benutze Fingerprint zum Entsperren auf der Quick-Unlock Seite</string>\n    <string name=\"db_unlock\">Datenbank entsperren</string>\n    <string name=\"quick_unlock\">Quick-Unlock</string>\n    <string name=\"disable\">Ausschalten</string>\n    <string name=\"search\">Suche</string>\n    <string name=\"notify_db_locked\">Die Datenbank ist gesperrt</string>\n    <string name=\"notify_quick_unlock_start\">Die Datenbank ist gesperrt, Quick-Unlock ist aktiv</string>\n    <string name=\"notify_channel_db_open\">Datenbank Entsperr-Benachrichtigung</string>\n    <string name=\"unlocked\">Entsperred</string>\n    <string name=\"start_upload_db\">Beginne Upload der Datenbank in ihren Netzwerkspeicher</string>\n    <string name=\"hint_kdbx_name\">Falsche Dateiendung, die Endung sollte .kdbx sein</string>\n    <string name=\"afs\">Android Dateisystem</string>\n    <string name=\"invalid_auth\">Komnte nicht autorisieren. Bitte versuchen Sie es nochmal.</string>\n    <string name=\"db_file_no_exist\">Die Datenbankdatei existiert nicht</string>\n    <string name=\"hint_please_input\">Bitte eingeben: %s</string>\n    <string name=\"hint_webdav_url\">Datei URL</string>\n    <string name=\"helper_webdav_dir\">z.B.: https://dav.example.org/dav/[ihr Ordner]/</string>\n    <string name=\"helper_webdav\">z.B.: https://dav.example.org/dav/[ihr Ordner]/xxx.kdbx</string>\n    <string name=\"login\">Login</string>\n    <string name=\"no_file\">Keine Datei</string>\n    <string name=\"invalid\">Ungültig</string>\n    <string name=\"auth\">Authentifikation</string>\n    <string name=\"cover_cloud\">Cloud</string>\n    <string name=\"cover_local\">Lokal</string>\n    <string name=\"net_error\">Netzwerk-Problem</string>\n    <string name=\"sync_db\">Synchrone Datenbank</string>\n    <string name=\"merge\">Vereinigen</string>\n    <string name=\"file_conflict_msg\">Es besteht ein Konflikt zwischen den Cloud- und den lokalen Daten, Bitte wählen Sie eine Lösungsmethode.Die betreffenden Einträge sind:\n\\n %s</string>\n    <string name=\"cover\">Verdecke</string>\n    <string name=\"dropbox_msg\">Aus Respekt vor der Privatsphäre der Benutzer wird KeepPassA nicht nach der Berechtigung zum Zugriff auf alle Dateien ihrer Dropbox fragen, KeePassA &lt;font color = # FF0000&gt; Benutzt nur den 【Apps/KeePassA】 Ordner&lt;/ font &gt;.&lt;br /&gt;Deshalb: &lt;br /&gt; &lt;b&gt;wenn Sie bereits eine KeePass Datenbank besitzen, erlauben Sie KeePassA Zugriff auf den 【Apps/KeePassA】 Ordner in KeePassA und speichern Sie die KeePass Datenbank in dem 【Application/KeePassA】 Ordner, nachdem die Autorisierung abgeschlossen ist.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt; Wenn Sie eine neue KeePass Datenbank einrichten, sind die oben genannten Schritte nicht notwendig. Nachdem die Datenbank erstellt wurde, erstellt KeePassA den entsprechenden Ordner automatisch und lädt die Datenbank in den Ordner【Apps/KeePassA】 auf Dropbox hoch.</string>\n    <string name=\"set_key_value\">Sprachauswahl</string>\n    <string name=\"quick_pass_len_summary\">Kurz-Passwort-Länge: %s</string>\n    <string name=\"quick_pass_len_title\">Kurz-Passwort-Länge</string>\n    <string name=\"error_short_pass_short\">Bilde dein Kurz-Passwort mit 3 und mehr Stellen</string>\n    <string name=\"error_short_pass_lenght\">Bilde dein Kurz-Passwort mit bis zu 6 Stellen</string>\n    <string name=\"des_quick_unlock\">Entsprerre die Datenbank mit einem Kurz-Passwort</string>\n    <string name=\"quick_pass_type\">An welcher Stelle soll das Kurz-Passwort stehen</string>\n    <string name=\"auto_lock_db_time_summary\">Sperre die Datenbank nach %s automatisch, wenn keine Aktion erfolgt</string>\n    <string name=\"auto_lock_db_time\">Datenbank automatisch sperren</string>\n    <string name=\"clean_clip_time\">Leere Clipboard</string>\n    <string name=\"open_quick_unlock\">Schnell entsperren</string>\n    <string name=\"other_set\">Andere Einstellungen</string>\n    <string name=\"set_key_praise_value\">Beurteile KeePassA</string>\n    <string name=\"set_open_auto_fill_title\">Auto-fill Dienst</string>\n    <string name=\"submit\">Abschicken</string>\n    <string name=\"feedback\">Feedback ^_^</string>\n    <string name=\"title\">Titel</string>\n    <string name=\"update_group_success\">Gruppeninfo upgedated</string>\n    <string name=\"update_group_fail\">Konnte Gruppeninfo nicht updaten</string>\n    <string name=\"lose_time\">Ablaufdatum</string>\n    <string name=\"modify_time\">Änderungsdatum</string>\n    <string name=\"relevance_db\">Verknüpfte Daten</string>\n    <string name=\"no_matching_entry\">Kein passender Eintrag</string>\n    <string name=\"autofill_sign_in_prompt\">Passwort Auto-fill mit KeePassA</string>\n    <string name=\"error_db_pass_too_short\">Datenbankpasswort mindesten 6 Zeichen um Ihre Datenbanken zu schützen</string>\n    <string name=\"app_feedback\">Feedback</string>\n    <string name=\"hint_db_pass_modify\">Datenbankpassword geändert</string>\n    <string name=\"hint_db_pass_modify_success\">Benutze nächstes Mal das neue Datenbankpasswort zum öffnen der Datenbank</string>\n    <string name=\"db_pass_no_alter\">Das neue Passwort entspricht dem alten</string>\n    <string name=\"error_pass\">Passwort-Fehler</string>\n    <string name=\"save_db_success\">Datenbank gespeichert</string>\n    <string name=\"save_db_fail\">Konnte Datenbank nicht speichern</string>\n    <string name=\"txt_viewer\">Datei-Anzeige</string>\n    <string name=\"save_file_success\">Die Datei [%s] wurde gespeichert</string>\n    <string name=\"open_whit_other\">Speichere im Cache und öffne mit System-App</string>\n    <string name=\"open_whit_img\">Öffne mit interner Bild-Anzeige</string>\n    <string name=\"open_whit_text\">Öffne mit internem Text</string>\n    <string name=\"download_file\">Speichere Anhang auf SD-Karte</string>\n    <string name=\"error_attr_file_too_large\">Reduziere Speicherverbrauch durch maximale Anhanggröße 10 MB</string>\n    <string name=\"del_attr_file\">Lösche Anhang</string>\n    <string name=\"del_attr_str\">Lösche individuelle Felder</string>\n    <string name=\"error_attr_str_null\">Der Feldname ist leer</string>\n    <string name=\"hint_protect_str\">Geschütztes Feld</string>\n    <string name=\"hint_input_attr_str_value\">Feldwert</string>\n    <string name=\"hint_input_attr_str_key\">Feldname</string>\n    <string name=\"add_custom_str\">Erstelle individuelle Felder</string>\n    <string name=\"create_entry_no_save\">Aktuelle Seite ohne zu speichern verlassen\\?</string>\n    <string name=\"warning\">Achtung</string>\n    <string name=\"add_more\">Weitere hinzufügen</string>\n    <string name=\"normal_account\">Normales Konto</string>\n    <string name=\"choose_dir\">Speichere aktuelle Gruppe</string>\n    <string name=\"time\">Zeit</string>\n    <string name=\"date\">Datum</string>\n    <string name=\"hint_pass_null\">Das Passwort ist leer</string>\n    <string name=\"error_genera_params\">Bitte wählen sie einen Passwort-Generator-Typ</string>\n    <string name=\"pass_generater\">Passwort-Generator</string>\n    <string name=\"bracket\">Klammer</string>\n    <string name=\"special\">Sonderzeichen</string>\n    <string name=\"space\">Leerzeichen</string>\n    <string name=\"underline\">Unterstrich</string>\n    <string name=\"minus\">Minus</string>\n    <string name=\"numer\">Ziffer</string>\n    <string name=\"lower\">Klein-Buchstaben</string>\n    <string name=\"upper\">Groß-Buchstaben</string>\n    <string name=\"len\">Länge</string>\n    <string name=\"save\">Speichern</string>\n    <string name=\"create_entry\">Erstelle Eintrag</string>\n    <string name=\"hint_input_cover_url\">Überschreibe Link</string>\n    <string name=\"hint_input_tag\">Tag1, Tag2</string>\n    <string name=\"notice\">Notizen</string>\n    <string name=\"hint_input_url\">Link-Adresse</string>\n    <string name=\"hint_input_user_name\">Benutzername</string>\n    <string name=\"hint_input_title\">Titel</string>\n    <string name=\"hint_quick_unlock\">Geben Sie die letzten %s Zeichen ihres Datenbankpassworts ein</string>\n    <string name=\"use_full_fingerprint\">Voller Fingerprint-Unlock</string>\n    <string name=\"quick_use_fingerprint\">Benutze Fingerprint-Unlock nur im Quick-Unlock-Interface</string>\n    <string name=\"ban_fingerprint\">Kein Fingerprint-Unlocking</string>\n    <string name=\"verify_finger_fail\">Konnte Fingerabdruck nicht prüfen</string>\n    <string name=\"verify_finger\">Fingerabdruck-Prüfung</string>\n    <string name=\"hint_copy_to_clip\">Daten ins Clipboard kopiert</string>\n    <string name=\"hide_pass\">Verstecke Passwort</string>\n    <string name=\"ope_url\">Öffne Link</string>\n    <string name=\"show_pass\">Zeige Passwort</string>\n    <string name=\"copy_to_clip\">Kopiere ins Clipboard</string>\n    <string name=\"hint_query\">Benutzername, Titel, URL, weitere Attribute …</string>\n    <string name=\"no_record\">Kein Eintrag</string>\n    <string name=\"db_name_modify\">Datenbankname geändert</string>\n    <string name=\"db_name_no_alter\">Der Datenbankname wurde nicht geändert</string>\n    <string name=\"out_db\">Exportiere Datenbank</string>\n    <string name=\"modify_db_pass\">Ändere Datenbankpasswort</string>\n    <string name=\"modify_db_name\">Ändere den Datenbanknamen</string>\n    <string name=\"db_handle\">Datenbank-Operationen</string>\n    <string name=\"fingerprint_unlock\">Fingerprint-Unlock</string>\n    <string name=\"safety_set\">Sicherheits-Einstellungen</string>\n    <string name=\"create_totp\">Erstelle OTP-Token</string>\n    <string name=\"hint_copy_totp\">TOTP-Token ins Clipboard kopiert</string>\n    <string name=\"hint_copy_pass\">Passwort ins Clipboard kopiert</string>\n    <string name=\"hint_copy_user\">Benutzername ins Clipboard kopiert</string>\n    <string name=\"undo_entryed\">Der Eintrag wurde an die spezifiziert Position verschoben</string>\n    <string name=\"undo_grouped\">Die Gruppe wurde an die spezifizierte Position verschoben</string>\n    <string name=\"undo_to\">Verschiebe in diese Gruppe</string>\n    <string name=\"undo_entry\">Fortgesetzt…</string>\n    <string name=\"choose_icon\">Wähle Icon</string>\n    <string name=\"title_too_long\">Gruppenname zu lang</string>\n    <string name=\"error_group_no_modify\">Keine Änderung an der Gruppe</string>\n    <string name=\"modify_group\">Ändere Gruppe</string>\n    <string name=\"create_group_success\">Gruppe erstellt</string>\n    <string name=\"create_group_fail\">Konnte Gruppe nicht erstellen</string>\n    <string name=\"error_group_name_null\">Der Gruppenname ist leer</string>\n    <string name=\"create_group\">Erstelle Gruppe</string>\n    <string name=\"group_name\">Gruppenname</string>\n    <string name=\"hint_del_entry_no_recycle\">Eintrag 【&lt;font color=#FF0000&gt;%s&lt;/font&gt;】endgültig löschen\\?</string>\n    <string name=\"hint_del_entry\">Eintrag 【&lt;font color=#FF0000&gt;%s&lt;/font&gt;】 wirklich löschen\\? &lt;br&gt;Kann später aus dem Papierkorb wieder hergestellt werden.</string>\n    <string name=\"hint_del_group_no_recycle\">Gruppe 【&lt;font color=#FF0000&gt;%s&lt;/font&gt;】 wirklich löschen\\?</string>\n    <string name=\"hint_del_group\">Gruppe wirklich löschen【&lt;font color=#FF0000&gt;%s&lt;/font&gt;】\\?&lt;br&gt;Kann später aus dem Papierkorb wieder hergestellt werden.</string>\n    <string name=\"copy_totp\">Kopiere TOTP-Token</string>\n    <string name=\"copy_password\">Kopiere Passwort</string>\n    <string name=\"copy_user_name\">Kopiere Benutzername</string>\n    <string name=\"del_history\">Lösche Historien-Eintrag</string>\n    <string name=\"del_entry\">Lösche Element</string>\n    <string name=\"fail\">Fehler</string>\n    <string name=\"success\">Erfolgreich</string>\n    <string name=\"edit_group\">Ändere Gruppe</string>\n    <string name=\"del_group\">Lösche Gruppe</string>\n    <string name=\"expire\">Ungültig seit &lt;font color=#FF0000&gt;%s&lt;/font&gt;</string>\n    <string name=\"hint_ex_property\">Weitere Attribute</string>\n    <string name=\"attachment\">Anhang</string>\n    <string name=\"hint_attr\">Erweiterte Einstellungen</string>\n    <string name=\"expire_time\">Ungültig ab %s</string>\n    <string name=\"create_time\">Erstellt am %s</string>\n    <string name=\"error_entry_id_null\">Element ID ist leer</string>\n    <string name=\"error_group_id_null\">Gruppen ID ist leer</string>\n    <string name=\"hint_group_desc\">Elemente von %s</string>\n    <string name=\"error_open_db_arcfour_error\">Falscher Strom-Verschlüsselungs-Algorithmus</string>\n    <string name=\"error_open_db_algorithm_error\">Falscher Algorithmus</string>\n    <string name=\"error_open_db_signature_error\">Falsche Datenbank-Signatur</string>\n    <string name=\"error_open_db_version_error\">Falsche Datenbank-Version</string>\n    <string name=\"error_open_db_key_invalid\">Schlüssel falsch</string>\n    <string name=\"error_open_db_pass_error\">Passwort falsch</string>\n    <string name=\"error_open_db_key_empty\">Schlüssel fehlt</string>\n    <string name=\"error_open_db\">Datenbank konnte nicht geöffnet werden. Ist das Passwort korrekt oder wird eine Schlüsseldatei benötigt\\?</string>\n    <string name=\"error_input_pass_null\">Bitte geben Sie das Datenbank-Passwort ein</string>\n    <string name=\"error_uri\">URI Fehler</string>\n    <string name=\"need_key\">Mit Schlüsseldatei\\?</string>\n    <string name=\"help_input_db_pass\">Geben Sie das Datenbank-Passwort ein</string>\n    <string name=\"welcome\">Willkommen</string>\n    <string name=\"app_setting\">Anwendungs-Einstellungen</string>\n    <string name=\"db_setting\">Datenbank-Einstellungen</string>\n    <string name=\"setting\">Einstellung</string>\n    <string name=\"all\">Elemente</string>\n    <string name=\"change_db\">Wechsle Datenbank</string>\n    <string name=\"edit\">Ändern</string>\n    <string name=\"history_record\">Historieneintrag</string>\n    <string name=\"history\">Historie</string>\n    <string name=\"hint_db_create_success\">Die Datenbank [%s] wurde erstellt</string>\n    <string name=\"error_pass_unfit\">Passwörter stimmen nicht überein</string>\n    <string name=\"error_enter_pass_null\">Bitte bestätigen Sie das Passwort</string>\n    <string name=\"error_pass_null\">Bitte setzen Sie ein Datenbankpasswort</string>\n    <string name=\"create_pass_key_success\">Schlüsselerstellung [%s] erfolgt</string>\n    <string name=\"create_pass_key_file_des\">Erstellen Sie eine neue Datei als ihren Schlüssel</string>\n    <string name=\"create_file\">Erstelle neue Datei</string>\n    <string name=\"choose_pass_key_file_des\">Wählen Sie eine Datei als ihren Schlüssel</string>\n    <string name=\"help_pass_type\">Nur Passwort:\n\\n Nur ihr Passwort ist erforderlich um die Datenbank zu öffnen；\n\\n Passwort + Schlüsseldatei:\n\\n Sie müssen das Passwort und einen Schlüssel angeben, um die Datenbank zu öffnen.</string>\n    <string name=\"help_create_db_path\">Es wird empfohlen, einen Cloud wie Dropbox oder WebDAV zu verwenden. Dies wird die Datenbank automatisch in die Cloud synchronisieren, was die Benutzung der Datenbank auf weiteren Geräten erleichtert.\n\\nDie Datenbank wird mit AES-256 verschlüsselt. Ohne Ihr Passwort kann niemand die Datenbank öffnen.</string>\n    <string name=\"hint\">Abfrage</string>\n    <string name=\"enter\">Speichern</string>\n    <string name=\"hint_select_path_type\">Wähle Datenbank-Pfad</string>\n    <string name=\"hint_select_db_path\">Speichermethode wählen</string>\n    <string name=\"next\">Weiter</string>\n    <string name=\"helper_create_pass\">Passwort sollte Groß- und Kleinbuchstaben, Ziffern und Symbole enthalten</string>\n    <string name=\"pass_key\">Schlüsseldatei</string>\n    <string name=\"hint_set_pass_key\">Bitte wähle eine Schlüsseldatei</string>\n    <string name=\"choose_pass_key\">Wähle Schlüsseldatei</string>\n    <string name=\"encrypt_type_2\">Passwort + Schlüssel</string>\n    <string name=\"help_create_group\">Wähle einen Gruppennamen (max. 16 Zeichen)</string>\n    <string name=\"help_create_db\">Erstelle KeePass v4 Datenbank als Voreinstellung</string>\n    <string name=\"db1\">Datenbank\\t|\\t&lt;font color=#757575&gt;%s&lt;/font&gt;</string>\n    <string name=\"key1\">Geheimer Schlüssel: %s</string>\n    <string name=\"key\">Geheimer Schlüssel</string>\n    <string name=\"help_pass_key\">Eine Schlüsseldatei entspricht einem Schlüssel um Daten zu entsperren und ist üblicherweise deutlich komplexer und zuverlässiger als ein Passwort. Aber sie ist auch schwieriger zu verstecken. Wenn die Datenbank in der Cloud gespeichert ist, darf der Schlüssel nicht dort zusammen mit der Datenbank abgelegt werden.\n\\nEs kann eine beliebige Datei als Schlüssel für das Öffnen der Datenbank gewählt werden. Ein Foto oder eine Textdatei.\n\\n⚠️ Achtung: Wenn das Bild oder die Textdatei verändert wurde, kann die Datenbank nicht mehr entsperrt werden!!!\n\\nEs wird daher dringend empfohlen nur Read-Only-Dateien als Schlüssel zu verwenden.</string>\n    <string name=\"hint_full_fingerprint\">Der Fingerabdruck wird verwendet um die Datenbank im Quick-Unlock-Interface und im Datenbank-Unlock-Interface zu entsperren. KeePassA verschlüsselt und speichert das Datenbank-Masterpasswort und die Schlüsselinformation im lokalen Schlüsselspeicher von Android und nutzt die Fingerabdruck-Authorisierung zum Schutz. Nur dein Fingerabdruck kann zum entsperren der Datenbank genutzt werden.</string>\n    <string name=\"hint_background_start\">\"Gehe zu &lt;font color=#FF0000&gt;【%s】&lt;/font&gt;und lasse Hintergrund-Popup-Benachrichtigungen zu um die Autovervollständigung zu verbessern.&lt;br&gt; &lt;font color=#FF0000&gt;KeePassA zeigt das entsprechende Interface um die Datenbank zu öffnen, wenn die Autovervollständigung  verwendet wird&lt;/font&gt;\"</string>\n    <string name=\"setting_miui_background_start\">Einstellungen &gt; Apps &amp; Benachrichtigungen &gt; Alle Apps &gt; KeePassA &gt; Berechtigungen</string>\n    <string name=\"clean_clip_time_summary\">Nach dem Kopieren des Kennworts die Zwischenablage nach %s leeren</string>\n    <string name=\"hint_fingerprint_modify\">Prüfen Sie, ob sich der Fingerabdruck geändert hat, setzen Sie bitte die Funktion zum Entsperren des Fingerabdrucks zurück, um Ihr Passwort zu schützen</string>\n    <string name=\"hint_finger_print_verfiy\">Überprüfen Sie Ihre Fingerabdrücke erneut, um die neuen Einstellungen zu verwenden</string>\n    <string name=\"setting_vivo_background_start\">Einstellungen &gt; Mehr Einstellungen &gt; Berechtigungen Verwalten &gt; KeyPassA &gt; Berechtigungseinstellungen &gt; Hintergrund-Popup</string>\n    <string name=\"send_email_fail\">Installieren Sie zunächst eine App zum Versenden von E-Mails</string>\n    <string name=\"dev_birthday\">Heute hat der Entwickler Geburtstag🎂. Möchten Sie dem Entwickler ein Bier kaufen?</string>\n    <string name=\"feedback_email_msg\">Hallo, Ich bin KeyPassA, haben Sie Fragen\\?<br/> <br/> <br/> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ <br/> Marke: %s <br/> Model: %s <br/> Systemversion:%s <br/> Auflösung: %s <br/> App-Version: %s</string>\n    <string name=\"totp_custom_hint\">Benutzerdefinierte Einstellungen</string>\n    <string name=\"hint_security_yellow\">Achtung, wenn Sie im Simulator arbeiten, birgt diese Umgebung einige unvorhersehbare Risiken für die Datenbank</string>\n    <string name=\"open_setting\">Einstellung</string>\n    <string name=\"open_permissions\">Berechtigungen öffnen</string>\n</resources>"
  },
  {
    "path": "app/src/main/res/values-es/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"db\">Base de datos</string>\n    <string name=\"cancel\">Cancelar</string>\n    <string name=\"db1\">BdD\\t|\\t&lt;font color=#757575&gt;%s&lt;/font&gt;</string>\n    <string name=\"key\">Llave secreta</string>\n    <string name=\"key1\">Llave secreta: %s</string>\n    <string name=\"open\">Desbloquear</string>\n    <string name=\"hint_set_password\">Fijar contraseña</string>\n    <string name=\"hint_enter_password\">Confirmar contraseña</string>\n    <string name=\"encrypt_type\">Cifrado</string>\n    <string name=\"encrypt_type_1\">Solo contraseña</string>\n    <string name=\"encrypt_type_2\">Contraseña + llave</string>\n    <string name=\"choose_pass_key\">Seleccionar llave</string>\n    <string name=\"hint_set_pass_key\">Seleccione una llave</string>\n    <string name=\"pass_key\">Archivo llave</string>\n    <string name=\"helper_create_pass\">La contraseña mezclada es recomendable (minús + MAYÚS + números + símbolos)</string>\n    <string name=\"create_db\">Crear BdD</string>\n    <string name=\"next\">Siguiente</string>\n    <string name=\"up\">Anterior</string>\n    <string name=\"hint_select_db_path\">Pulse para fijar el método de guardado de datos</string>\n    <string name=\"hint_select_path_type\">Tome donde guardar su BdD</string>\n    <string name=\"password\">Contraseña</string>\n    <string name=\"hint_input_password\">Introduzca una contraseña de 6–16 dígitos</string>\n    <string name=\"open_db\">Abrir base de datos</string>\n    <string name=\"db_name\">Ingrese el nombre de la base de datos</string>\n    <string name=\"hint\">Solicitud</string>\n    <string name=\"help_pass_type\">Usar solo una contraseña:\n\\nSolo se necesita su contraseña para abrir la base de datos;\n\\nContraseña + clave:\n\\nDebe ingresar una contraseña y seleccionar un archivo clave para abrir la base de datos.</string>\n    <string name=\"help_create_db\">Crear una base de datos KeePass v4 por defecto</string>\n    <string name=\"error_pass_null\">Fije una contraseña de la base de datos</string>\n    <string name=\"choose_file\">Elija un archivo</string>\n    <string name=\"choose_pass_key_file_des\">Elija cualquier archivo para utilizar como su llave</string>\n    <string name=\"create_file\">Crea un archivo nuevo</string>\n    <string name=\"create_pass_key_file_des\">Crea un archivo nuevo para utilizar como su llave</string>\n    <string name=\"create_pass_key_success\">Creación de llave [%s] correcta</string>\n    <string name=\"error_enter_pass_null\">Introduzca la confirmación de la contraseña</string>\n    <string name=\"history\">Historial</string>\n    <string name=\"history_record\">Registro del historial</string>\n    <string name=\"change_db\">Cambiar de base de datos</string>\n    <string name=\"all\">Elementos</string>\n    <string name=\"setting\">Parámetros</string>\n    <string name=\"db_setting\">Parámetros de BdD</string>\n    <string name=\"help_input_db_pass\">Introduzca la contraseña de la BdD</string>\n    <string name=\"error_open_db_key_empty\">Llave ausente</string>\n    <string name=\"need_key\">¿Necesita una llave\\?</string>\n    <string name=\"error_uri\">URI erróneo</string>\n    <string name=\"error_open_db\">No pudo abrir la BdD. ¿La contraseña es incorrecta, o es una llave requerida\\?</string>\n    <string name=\"error_open_db_pass_error\">Contraseña no válida</string>\n    <string name=\"error_open_db_key_invalid\">Llave no válida</string>\n    <string name=\"error_open_db_version_error\">Versión de BdD no válida</string>\n    <string name=\"error_open_db_signature_error\">Firma de BdD no válida</string>\n    <string name=\"error_open_db_algorithm_error\">Algoritmo no válido</string>\n    <string name=\"error_open_db_arcfour_error\">Algoritmo de cifrado de flujo no válido</string>\n    <string name=\"hint_group_desc\">Ítemes de grupo %s</string>\n    <string name=\"error_group_id_null\">ID de grupo no tiene valor</string>\n    <string name=\"create_time\">Compilado en %s</string>\n    <string name=\"expire_time\">Caducando en %s</string>\n    <string name=\"hint_attr\">Propiedades avanzadas</string>\n    <string name=\"attachment\">Adjunto</string>\n    <string name=\"hint_ex_property\">Atributos adicionales</string>\n    <string name=\"expire\">Caducado el &lt;font color=#FF0000&gt;%s&lt;/font&gt;</string>\n    <string name=\"del_group\">Borrar grupo</string>\n    <string name=\"edit_group\">Editar grupo</string>\n    <string name=\"success\">Correcto</string>\n    <string name=\"fail\">Erróneo</string>\n    <string name=\"del_entry\">Borrar ítem</string>\n    <string name=\"del_history\">Borrar registro histórico</string>\n    <string name=\"error_group_no_modify\">Ningún grupo cambia</string>\n    <string name=\"copy_user_name\">Copiar ID de usuario</string>\n    <string name=\"copy_password\">Copiar contraseña</string>\n    <string name=\"copy_totp\">Copiar ticket TOPC</string>\n    <string name=\"hint_del_group_no_recycle\">¿Borrar realmente el grupo 【&lt;font color=#FF0000&gt;%s&lt;/font&gt;】\\?</string>\n    <string name=\"group_name\">Nombre de grupo</string>\n    <string name=\"create_group_success\">Construir grupo</string>\n    <string name=\"modify_group\">Editar grupo</string>\n    <string name=\"choose_icon\">Elegir icono</string>\n    <string name=\"undo_entry\">Resumido…</string>\n    <string name=\"undo_to\">Mover a este grupo</string>\n    <string name=\"hint_copy_user\">ID usuario copiado al portapapeles</string>\n    <string name=\"hint_copy_pass\">Contraseña copiada al portapapeles</string>\n    <string name=\"safety_set\">Opciones de Seguridad</string>\n    <string name=\"fingerprint_unlock\">Desbloquear huella</string>\n    <string name=\"db_handle\">Operaciones de BdD</string>\n    <string name=\"modify_db_name\">Modifica el nombre de la BdD</string>\n    <string name=\"modify_db_pass\">Cambiar contraseña de BdD</string>\n    <string name=\"out_db\">Exportar BdD</string>\n    <string name=\"db_name_no_alter\">El nombre de la BdD no ha cambiado</string>\n    <string name=\"db_name_modify\">Nombre de BdD modificado</string>\n    <string name=\"no_record\">Sin registro</string>\n    <string name=\"hint_query\">ID usuario, título, URL, atributos avanzados …</string>\n    <string name=\"copy_to_clip\">Copiar al portapapeles</string>\n    <string name=\"show_pass\">Mostrar contraseña</string>\n    <string name=\"ope_url\">Abrir enlace</string>\n    <string name=\"hide_pass\">Ocultar contraseña</string>\n    <string name=\"hint_copy_to_clip\">Datos copiados al portapapeles</string>\n    <string name=\"verify_finger\">Verificación de huella</string>\n    <string name=\"hint_input_url\">Dirección enlace</string>\n    <string name=\"notice\">Anotación</string>\n    <string name=\"hint_input_tag\">Lengüeta1, Lengüeta2</string>\n    <string name=\"hint_input_cover_url\">Sobrescribir enlace</string>\n    <string name=\"create_entry\">Crear apunte</string>\n    <string name=\"save\">Guardar</string>\n    <string name=\"len\">Longitud</string>\n    <string name=\"upper\">Carácter mayúscula</string>\n    <string name=\"lower\">Carácter minúscula</string>\n    <string name=\"numer\">Numérico</string>\n    <string name=\"minus\">Resta</string>\n    <string name=\"underline\">Subrayado</string>\n    <string name=\"space\">Espacio</string>\n    <string name=\"special\">Especial</string>\n    <string name=\"bracket\">Corchete</string>\n    <string name=\"pass_generater\">Generador de contraseña</string>\n    <string name=\"error_genera_params\">Seleccione al menos un tipo de generación de contraseña</string>\n    <string name=\"hint_pass_null\">La contraseña está vacía</string>\n    <string name=\"date\">Fecha</string>\n    <string name=\"time\">Hora</string>\n    <string name=\"choose_dir\">Guardar al grupo actual</string>\n    <string name=\"normal_account\">Cuenta usual</string>\n    <string name=\"add_more\">Añadir más</string>\n    <string name=\"warning\">Aviso</string>\n    <string name=\"add_custom_str\">Crear campos personales</string>\n    <string name=\"hint_input_attr_str_key\">Nombre del campo</string>\n    <string name=\"hint_input_attr_str_value\">Valor del campo</string>\n    <string name=\"hint_protect_str\">Campo protegido</string>\n    <string name=\"del_attr_str\">Borrar campos personales</string>\n    <string name=\"del_attr_file\">Borrar adjunto</string>\n    <string name=\"download_file\">Guardar adjunto a la tarjeta SD</string>\n    <string name=\"open_whit_text\">Abre con texto interno</string>\n    <string name=\"open_whit_img\">Abre con visor de imagen interno</string>\n    <string name=\"open_whit_other\">Guarda a la caché y abre con app del sistema</string>\n    <string name=\"txt_viewer\">Visor de archivo</string>\n    <string name=\"error_pass\">Contraseña errónea</string>\n    <string name=\"hint_db_pass_modify\">Contraseña de BdD modificada</string>\n    <string name=\"app_feedback\">Observaciones</string>\n    <string name=\"hint_background_start\">Vaya a&lt;font color=#FF0000&gt;【%s】&lt;/font&gt;y ceda permisos para la aparición de fondo para empleo mejor del servicio de auto-relleno.&lt;br&gt; &lt;font color=#FF0000&gt;KeePassA muestra el interfaz correspondiente para abrir la BdD cuando emplee la función de autorelleno&lt;/font&gt;</string>\n    <string name=\"setting_vivo_background_start\">Opciones &gt; Más parámetros &gt; Gestión de permisos &gt; KeePassA &gt; Parámetros de permiso &gt; Fondo de aparición</string>\n    <string name=\"no_matching_entry\">Ítem no coincidente</string>\n    <string name=\"submit\">Enviar</string>\n    <string name=\"hint_save_auto_fill\">Cuando asocie el nombre del paquete【&lt;font color=#FF0000&gt;%s&lt;/font&gt;】con el apunte【&lt;font color=#FF0000&gt;%s&lt;/font&gt;】</string>\n    <string name=\"relevance_db\">Datos enlazados</string>\n    <string name=\"modify_time\">Hora de modificación</string>\n    <string name=\"lose_time\">Hora de caducidad</string>\n    <string name=\"update_group_fail\">No pudo actualizar el informe de grupo</string>\n    <string name=\"update_group_success\">Informe de grupo actualizado</string>\n    <string name=\"title\">Título</string>\n    <string name=\"feedback\">Observaciones ^_^</string>\n    <string name=\"mark_not_exit\">No existe mercado de aplicación</string>\n    <string name=\"set_open_auto_fill_title\">Servicio de auto-relleno</string>\n    <string name=\"other_set\">Otras opciones</string>\n    <string name=\"open_quick_unlock\">Desbloqueo rápido</string>\n    <string name=\"clean_clip_time\">Portapapeles vacío</string>\n    <string name=\"clean_clip_time_summary\">Tras copiar contraseña, vaciar portapapeles tras %s</string>\n    <string name=\"auto_lock_db_time\">Auto-bloquear la BdD</string>\n    <string name=\"auto_lock_db_time_summary\">Auto-bloquear la base de datos tras %s si ninguna operación es hecha</string>\n    <string name=\"quick_pass_type\">Ubicación de interceptación de contraseña corta</string>\n    <string name=\"des_quick_unlock\">Desbloquea su BdD con una contraseña breve</string>\n    <string name=\"quick_pass_len_title\">Longitud de contraseña corta</string>\n    <string name=\"error_short_pass_lenght\">Haga su contraseña corta de 6 o menos caracteres</string>\n    <string name=\"error_short_pass_short\">Haga su contraseña corta de 3 o más caracteres</string>\n    <string name=\"quick_pass_len_summary\">Longitud de contraseña corta: %s</string>\n    <string name=\"set_key_value\">Elija idioma</string>\n    <string name=\"cover\">Cobertura</string>\n    <string name=\"file_conflict_msg\">Conflicto entre datos de nube y datos locales. Seleccione el método de la cobertura. Los apuntes en conflicto son:\n\\n %s</string>\n    <string name=\"merge\">Unir</string>\n    <string name=\"sync_db\">Sincronizar BdD</string>\n    <string name=\"cover_local\">Local</string>\n    <string name=\"auth\">Autenticación</string>\n    <string name=\"invalid\">No Válido</string>\n    <string name=\"no_file\">Sin archivo</string>\n    <string name=\"login\">Acceso</string>\n    <string name=\"helper_webdav\">p.ej: https://dav.example.org/dav/[your folder]/xxx.kdbx</string>\n    <string name=\"hint_webdav_url\">URL de archivo</string>\n    <string name=\"db_file_no_exist\">No existe el archivo de la BdD</string>\n    <string name=\"invalid_auth\">No pudo autorizar. Inténtelo de nuevo.</string>\n    <string name=\"afs\">SdA Android</string>\n    <string name=\"unlocked\">Desbloqueado</string>\n    <string name=\"notify_channel_db_open\">Notificación de desbloqueo de BdD</string>\n    <string name=\"notify_quick_unlock_start\">La BdD bloqueada, el desbloqueo rápido está activado</string>\n    <string name=\"search\">Buscar</string>\n    <string name=\"quick_unlock\">Desbloqueo rápido</string>\n    <string name=\"db_unlock\">Desbloquear BdD</string>\n    <string name=\"version_log\">Modernizar boletín</string>\n    <string name=\"hint_finger_print_verfiy\">Re-verifique sus huellas para emplear los parámetros nuevos</string>\n    <string name=\"closed\">Cerrado</string>\n    <string name=\"upload\">Subida</string>\n    <string name=\"open1\">abrir</string>\n    <string name=\"add_attr_file\">Añadir adjuntos</string>\n    <string name=\"hint_fingerprint_modify\">Marque que la huella ha cambiado, para que su seguridad de contraseña, restablezca la función de desbloqueo de huella</string>\n    <string name=\"hint_please_open_database\">Abra la base de datos</string>\n    <string name=\"set_delete_setting\">Borrar opciones</string>\n    <string name=\"set_delete_no_into_recycle_bin_des\">Mueve los elementos borrados a la papelera</string>\n    <string name=\"hint_security_yellow\">Aviso, ejecutando en el simulador, este entorno traerá algunos riesgos impredecibles para la base de datos</string>\n    <string name=\"donate\">Donación</string>\n    <string name=\"totp_defaule\">Opciones del ticket predeterminado de RFC 6238</string>\n    <string name=\"totp_steam\">Ajustes de los tickets de vapor</string>\n    <string name=\"totp_custom\">Emplear ajustes personales</string>\n    <string name=\"totp_custom_hint\">Ajustes personales</string>\n    <string name=\"totp_Arithmetic\">Aritmético:</string>\n    <string name=\"totp_time\">Hora(s):</string>\n    <string name=\"totp_len\">Longitud:</string>\n    <string name=\"sort\">Ordenar</string>\n    <string name=\"sort_time_asc\">Hora temprana-última</string>\n    <string name=\"set_open_kpa_ime_title\">Teclado de seguridad</string>\n    <string name=\"ime_entry_other_info\">Encontrar en otros campos</string>\n    <string name=\"url\">URL</string>\n    <string name=\"move\">Mover</string>\n    <string name=\"set_key_title_main_allow_show_entries\">Página inicial</string>\n    <string name=\"expand\">Expandir</string>\n    <string name=\"shrink\">Encoger</string>\n    <string name=\"set_key_title_opr_env_check\">Comprobante de entorno de operación</string>\n    <string name=\"set_key_title_opr_env_check_turn_off\">Apagar</string>\n    <string name=\"set_key_title_opr_env_check_turn_on\">Encender</string>\n    <string name=\"cur_app_not_autofill\">Sin autocompletado</string>\n    <string name=\"other\">Otro</string>\n    <string name=\"ref_entry\">Apunte referencial</string>\n    <string name=\"multi_choice\">Elección múltiple</string>\n    <string name=\"close\">Cerrar</string>\n    <string name=\"Alipay\">Alipay</string>\n    <string name=\"error_webdav_end_suffix\">Utilice la ruta de carpeta. P.ej.: \\\"https://example.org/dav/kpa/\\\"</string>\n    <string name=\"set_key_title_show_state_bar_turn_on\">Mostrar</string>\n    <string name=\"set_key_title_show_state_bar_turn_off\">Ocultar</string>\n    <string name=\"s_default\">Por defecto</string>\n    <string name=\"customize\">Personalizar</string>\n    <string name=\"open_permissions\">Abrir permisos</string>\n    <string name=\"miui_permissions\">El sistema miui necesita permisos para abrir el interfaz del fondo de aparición con el fin de utilizar la función de auto-relleno.</string>\n    <string name=\"set_key_main_show_totp_tab_title\">La página inicial muestra categoría TOTP</string>\n    <string name=\"set_key_main_show_totp_tab_on\">Representa categoría TOTP</string>\n    <string name=\"set_key_main_show_totp_tab_off\">No representa categoría TOTP</string>\n    <string name=\"my_collection\">Mi colección</string>\n    <string name=\"current_collection_num\">%1$s colecciones</string>\n    <string name=\"get_token_fail\">Error al obtener ticket</string>\n    <string name=\"one_drive_init_failure\">Error de inicio OneDrive</string>\n    <string name=\"hint_db_create_success\">Se ha creado la BdD [%s]</string>\n    <string name=\"error_entry_id_null\">ID de apunte no tiene valor</string>\n    <string name=\"create_group_fail\">No puedo crear grupo</string>\n    <string name=\"title_too_long\">El nombre de grupo es muy largo</string>\n    <string name=\"undo_grouped\">El grupo ha movido a la ubicación especificada</string>\n    <string name=\"hint_copy_totp\">Ticket TOTP copiado al portapapeles</string>\n    <string name=\"create_totp\">Genera ticket OTP</string>\n    <string name=\"verify_finger_fail\">No pudo verificar huella</string>\n    <string name=\"ban_fingerprint\">Ninguna huella desbloqueada</string>\n    <string name=\"quick_use_fingerprint\">Utiliza únicamente huella para desbloquear dentro del interfaz de desbloqueo-rápido</string>\n    <string name=\"create_entry_no_save\">¿Salgo de la página actual sin guardar el apunte\\?</string>\n    <string name=\"error_attr_str_null\">El nombre del campo está vacío</string>\n    <string name=\"save_file_success\">El archivo [%s] fue guardado</string>\n    <string name=\"save_db_fail\">No pudo guardar la BdD</string>\n    <string name=\"save_db_success\">BdD guardada</string>\n    <string name=\"db_pass_no_alter\">La contraseña nueva es la misma que la anterior</string>\n    <string name=\"hint_db_pass_modify_success\">Utilice una contraseña nueva de la BdD para abrir la BdD la siguiente vez</string>\n    <string name=\"autofill_sign_in_prompt\">Auto-relleno de contraseña con KeePassA</string>\n    <string name=\"notify_db_locked\">La BdD está bloqueada</string>\n    <string name=\"disable\">Apagar</string>\n    <string name=\"donate_desc\">[KeePassA](https://github.com/AriaLyy/KeepassA) es software libre. Done para dar mantenimiento a nuestro trabajo. Muchas gracias.</string>\n    <string name=\"totp_key_error\">No pudo generar un código TOTP, llave anormal</string>\n    <string name=\"sort_chart_desc\">Primera letra descendiente (Z-A)</string>\n    <string name=\"sort_chart_asc\">Primera letra ascendente (A-Z)</string>\n    <string name=\"ime_label\">Teclado de seguridad KeePassA</string>\n    <string name=\"no_totp_token\">Sin etiqueta TOTP</string>\n    <string name=\"hint_not_nore_info\">Sin más información</string>\n    <string name=\"license\">Licencia de software libre</string>\n    <string name=\"set_key_sum_main_show_entries\">Muestra todos los elementos tras desbloquear la BdD</string>\n    <string name=\"ui_setting\">Opciones IU</string>\n    <string name=\"set_key_title_close_load_anim\">Cargando animación</string>\n    <string name=\"app_setting\">Parámetros App</string>\n    <string name=\"welcome\">Bienvenido</string>\n    <string name=\"help_create_db_path\">Se recomienda que utilice rutas de nube como Dropbox y WebDAV. Esto auto-sincronizará su BdD a la nube, haciéndolo más fácil de utilizar la base de datos en otros dispositivos.\n\\n La base de datos está cifrada utilizando AES-256. Sin su contraseña, ninguno puede abrir su base de datos.</string>\n    <string name=\"helper_webdav_dir\">p.ej.: https://dav.example.org/dav/[tu carpeta]/</string>\n    <string name=\"warning_rooted\">No abra su base de datos en una teléfono enraizado para conservarlo seguro</string>\n    <string name=\"hint_security_green\">\"La app está dentro de un entorno seguro \"</string>\n    <string name=\"hint_security_red\">Su dispositivo está enraizado y la BdD está en un entorno muy peligroso</string>\n    <string name=\"sort_time_desc\">Hora última-cercana</string>\n    <string name=\"set_summary_screen_lock_off\">No bloquea la BdD</string>\n    <string name=\"set_summary_screen_lock_on\">Auto-bloquea la BdD</string>\n    <string name=\"error_webdav_end_dav_suffix\">ruta /dav/ no está permitida. Utilice una ruta válida, como: \\\"https://example.org/dav/kpa/\\\"</string>\n    <string name=\"set_key_title_fillet_bg_icon\">Listado redondeado de esquinas de icono</string>\n    <string name=\"set_key_title_fillet_bg_icon_turn_on\">Activar</string>\n    <string name=\"set_key_title_fillet_bg_icon_turn_off\">Apagar</string>\n    <string name=\"set_key_title_show_state_bar\">Barra estado</string>\n    <string name=\"set_key_title_close_load_anim_turn_on\">Encender</string>\n    <string name=\"set_key_title_close_load_anim_turn_off\">Apagar</string>\n    <string name=\"set_key_title_auto_lock_database\">Auto-bloquea la base de datos</string>\n    <string name=\"set_key_title_auto_lock_database_turn_on\">Sí</string>\n    <string name=\"translate_language\">Traduzca la app a su idioma</string>\n    <string name=\"help_create_group\">Elija un nombre para el grupo de hasta 16 caracteres</string>\n    <string name=\"done\">Finalizado</string>\n    <string name=\"error_pass_unfit\">Pareja de contraseñas inconsistente</string>\n    <string name=\"error_db_name_null\">Introduzca un nombre de la BdD</string>\n    <string name=\"hint_del_group\">Borra realmente el grupo 【&lt;font color=#FF0000&gt;%s&lt;/font&gt;】\\?&lt;br&gt;Esto lo mueve a la basura, donde puede restaurarlo posteriormente.</string>\n    <string name=\"set_key_title_auto_lock_database_turn_off\">No</string>\n    <string name=\"year\">Anual</string>\n    <string name=\"enter\">Introduzca</string>\n    <string name=\"error_input_pass_null\">Introduzca la contraseña de la BdD</string>\n    <string name=\"edit\">Editar</string>\n    <string name=\"create_group\">Grupo compilador</string>\n    <string name=\"error_group_name_null\">El nombre del grupo está vacío</string>\n    <string name=\"undo_entryed\">El ítem fue trasladado a la ubicación especificada</string>\n    <string name=\"use_full_fingerprint\">Desbloqueo de huella completo</string>\n    <string name=\"send_email_fail\">Instala una app para enviar correo-e con primera</string>\n    <string name=\"hint_quick_unlock\">Introduzca los últimos %s dígitos de su contraseña de la BdD</string>\n    <string name=\"hint_input_title\">Título</string>\n    <string name=\"hint_input_user_name\">ID Usuario</string>\n    <string name=\"error_attr_file_too_large\">Reducir empleo de memoria sin adjuntar archivos más grandes que 10 MB</string>\n    <string name=\"error_db_pass_too_short\">Haga la contraseña de la BdD con más de 6 caracteres para proteger sus datos</string>\n    <string name=\"setting_miui_background_start\">Opciones &gt; Opciones de aplicación &gt; Gestión de aplicación &gt; KeePassA &gt; Gestión de permisos</string>\n    <string name=\"hint_quick_use_fingerprint\">Utilice solamente huellas para desbloquear en la página de desbloqueo rápido</string>\n    <string name=\"net_error\">Red anormal</string>\n    <string name=\"cover_cloud\">Nube</string>\n    <string name=\"hint_please_input\">Entrada %s</string>\n    <string name=\"start_upload_db\">Iniciar subida de la BdD a su disco de red</string>\n    <string name=\"hint_kdbx_name\">Sufijo de nombre erróneo, el sufijo de nombre sería .kbdx</string>\n    <string name=\"set_delete_no_into_recycle_bin\">Borrar apuntes desde la base de datos permanentemente</string>\n    <string name=\"dev_birthday\">Hoy es el cumpleaños del desarrollador🎂. ¿Invitas a una cerveza\\?</string>\n    <string name=\"error_uri_grant_permission\">Autorización URI errónea</string>\n    <string name=\"set_key_sum_main_show_history\">Muestra el historial tras desbloquear la BdD</string>\n    <string name=\"set_title_screen_lock\">Bloquear pantalla</string>\n    <string name=\"error_qr_code_str\">El código QR es erróneo. Asegure que el código QR está admitido por Google Authentication</string>\n    <string name=\"error_connect_play\">No pudo conectar al servicio Google</string>\n    <string name=\"one_drive_load_user_failure\">No se puede cargar la información del usuario.</string>\n    <string name=\"help_pass_key\">Un archivo llave es equivalente a una llave para abrir datos, y usualmente es mucho más complejo y seguro que una contraseña. Pero también es más complicado de ocultar. Si conserva de BdD en la nube, no ponga la llave con la base de datos allí.\n\\n Puede elegir cualquier archivo como una llave para abrir la BdD, una foto, o un archivo *.txt.\n\\n⚠️ Nota: Si esta foto o documento de texto es modificado, no será capaz de abrir la base de datos.\n\\n Es altamente recomendado utilizar archivos de solo lectura como llaves.</string>\n    <string name=\"error_open_db_webdav\">No se puede abrir la base de datos porque el archivo WebDAV no existe.</string>\n    <string name=\"hint_open_background_start\">Abra permiso de expulsión del fondo, en otro caso no puede utilizar auto-relleno</string>\n    <string name=\"no_collection\">Sin colección</string>\n    <string name=\"set_key_theme_style_title\">Estilo de tema</string>\n    <string name=\"set_key_theme_style_summary\">Siguiendo sistema</string>\n    <string name=\"error_keystore\">Excepción de pulsación de tecla, inténtelo de nuevo</string>\n    <string name=\"helper_webdav_service\">Seleccione el servicio WebDAV</string>\n    <string name=\"error_is_root\">Ya está en el directorio raíz</string>\n    <string name=\"select_cur_path\">Seleccione la carpeta</string>\n    <string name=\"hint_cloud_file_already_exist\">%1$s ya existe, ¿quieres sobrescribir el archivo\\?</string>\n    <string name=\"select_save_path\">Seleccione una ruta para guardar</string>\n    <string name=\"disposable\">Una vez</string>\n    <string name=\"monthly\">Mensual</string>\n    <string name=\"error_donate\">Donación errónea</string>\n    <string name=\"thank_donate\">Gracias por su donación.</string>\n    <string name=\"google_play\">Google Play</string>\n    <string name=\"kpa_totp\">Ticket</string>\n    <string name=\"kpa_copy\">Copiar</string>\n    <string name=\"feedback_email_msg\">Hola, soy KeePassA, ¿tienes alguna pregunta\\? <br/> <br/> <br/>_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ <br/>Marca del teléfono móvil: %s <br/>Modelo del teléfono móvil: %s <br/> Versión del sistema: %s <br/> Resolución: %s <br/> Versión de la aplicación: %s</string>\n    <string name=\"dropbox_msg\">Cumpliendo con el principio de respetar la privacidad del usuario, KeePassA no solicitará permiso para acceder a todos sus archivos de Dropbox, KeePassA&lt;font color=#FF0000&gt;solo accederá a la carpeta [Aplicación/KeePassA]&lt;/font&gt;. &lt;br /&gt; Por lo tanto: &lt;br /&gt; &lt;b&gt;Si ya tiene una base de datos de KeePass, primero debe autorizar a KeePassA para acceder a la carpeta [Aplicación/KeePassA] en KeePassA y guardar la base de datos de KeePass en [Aplicación/KeePassA ] carpeta. &lt;/b&gt;&lt;br/&gt;&lt;br /&gt; Si está creando una nueva base de datos de KeepPass, no necesita las operaciones anteriores. Después de crear la base de datos, KeePassA creará automáticamente la carpeta correspondiente y cargará la base de datos en Dropbox [ Aplicación/KeePassA] directorio.</string>\n    <string name=\"hint_del_entry_no_recycle\">¿Realmente desea eliminar de forma permanente la entrada [&lt;font color=#FF0000&gt;%s&lt;/font&gt;]\\?</string>\n    <string name=\"hint_full_fingerprint\">La huella digital se utiliza para desbloquear la base de datos en la interfaz de desbloqueo rápido y la interfaz de desbloqueo de base de datos, KeePassA encripta y almacena su contraseña maestra de base de datos y la información clave en el almacén de claves local del sistema Android, y utiliza la autorización de huellas digitales para la protección. Sólo tu huella digital puede desbloquear la base de datos.</string>\n    <string name=\"set_key_praise_value\">Evalúa KeePassA</string>\n    <string name=\"hint_del_entry\">¿Realmente desea eliminar la entrada [&lt;font color=#FF0000&gt;%s&lt;/font&gt;]\\? &lt;br&gt; Esto moverá este elemento a la papelera, donde podrá restaurarlo más tarde.</string>\n    <string name=\"webdav_port_name_null\">El nombre del equipo no puede estar vacío</string>\n    <string name=\"privacy_agreement\">Acuerdo de privacidad</string>\n    <string name=\"one_drive_hint\">Para proteger su privacidad, KeePassA solo accederá al directorio &lt;font color=#FF0000&gt;【Application/KeepassA】&lt;/font&gt;, si desea leer la base de datos Keepass de OneDrive, &lt;font color=#FF0000&gt; almacene la base de datos en bajo este directorio. &lt;/fuente&gt;</string>\n    <string name=\"please_open_proxy\">por favor, abra el proxy</string>\n    <string name=\"hostname\">Nombre del equipo</string>\n    <string name=\"port\">Puerto</string>\n    <string name=\"webdav_host_name_hint\">Por favor, escriba el nombre del equipo</string>\n    <string name=\"webdav_port_hint\">Puerto</string>\n    <string name=\"ime_hint_open_auto_fill\">Activa el servicio de autocompletar</string>\n    <string name=\"invalid_img\">imagen inválida</string>\n    <string name=\"open_setting\">Ajustes</string>\n    <string name=\"hint_open_backgroun_start\">Ha configurado el autocompletado, pero KeePassA no tiene los permisos adecuados. Autorice la ventana emergente del servicio en segundo plano de KeePassA en la página de configuración.</string>\n    <string name=\"preemptive\">Preventivo</string>\n    <string name=\"fail_unsupported_Systems_O\">Solo admite el sistema P o superior</string>\n    <string name=\"hint_webdav_jgy\">Utiliza la contraseña en [Cuenta-&gt;Seguridad-&gt;Gestión de aplicaciones de terceros].</string>\n    <string name=\"app_debug\">Obtener el archivo de registro</string>\n    <string name=\"hint_auto_fill_save\">¿Asociar los datos actuales a las entradas existentes\\?</string>\n    <string name=\"hint_dont_show_tips\">No mostrar los consejos</string>\n    <string name=\"title_tip_of_day\">Consejo del día</string>\n    <string name=\"file_save_success\">Archivo adjunto guardado correctamente</string>\n    <string name=\"base_info\">Información básica</string>\n    <string name=\"collection\">Colección</string>\n    <string name=\"delete\">Borrar</string>\n    <string name=\"modify_str_attr\">Modificar propiedades</string>\n    <string name=\"hint_confirm\">Confirmar contraseña</string>\n    <string name=\"tag\">Etiqueta</string>\n    <string name=\"totp\">TOTP</string>\n    <string name=\"add_attr_str\">Añadir atributo</string>\n    <string name=\"create_tag\">Etiqueta nueva</string>\n    <string name=\"tag_name\">Nombre de etiqueta</string>\n    <string name=\"add_tag\">Añadir etiqueta</string>\n    <string name=\"open_file\">Abrir archivo</string>\n    <string name=\"error_file\">El archivo es anormal, por favor borre &lt;font color=#FF0000&gt;%s&lt;/font&gt;</string>\n    <string name=\"not_souper_otp\">Totp no compatible</string>\n    <string name=\"modify_totp\">Modificar token OTP</string>\n    <string name=\"error_totp\">Error OTP</string>\n</resources>"
  },
  {
    "path": "app/src/main/res/values-fon/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources></resources>"
  },
  {
    "path": "app/src/main/res/values-fr/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"db\">Base de données</string>\n    <string name=\"time\">Heure</string>\n    <string name=\"date\">Date</string>\n    <string name=\"hint_pass_null\">Le mot de passe est vide</string>\n    <string name=\"error_genera_params\">Veuillez sélectionner au moins un type de génération de mot de passe</string>\n    <string name=\"pass_generater\">Générateur de mot de passe</string>\n    <string name=\"bracket\">Parenthèses</string>\n    <string name=\"special\">Caractères spéciaux</string>\n    <string name=\"space\">Espace</string>\n    <string name=\"underline\">Tiret bas</string>\n    <string name=\"minus\">Signe moins</string>\n    <string name=\"numer\">Chiffres</string>\n    <string name=\"lower\">Minuscules</string>\n    <string name=\"upper\">Majuscules</string>\n    <string name=\"len\">Longueur</string>\n    <string name=\"save\">Enregistrer</string>\n    <string name=\"create_entry\">Créer une entrée</string>\n    <string name=\"hint_input_cover_url\">Écraser le lien</string>\n    <string name=\"hint_input_tag\">Étiquette 1, Étiquette 2</string>\n    <string name=\"notice\">Note</string>\n    <string name=\"hint_input_url\">Lien</string>\n    <string name=\"hint_input_user_name\">Nom d\\'utilisateur</string>\n    <string name=\"hint_input_title\">Nom</string>\n    <string name=\"hint_quick_unlock\">Entrez les %s derniers caractères du mot de passe de votre base de données</string>\n    <string name=\"ban_fingerprint\">Pas de déverrouillage par empreinte digitale</string>\n    <string name=\"verify_finger_fail\">Impossible de vérifier l\\'empreinte digitale</string>\n    <string name=\"verify_finger\">Vérification de l\\'empreinte digitale</string>\n    <string name=\"hint_copy_to_clip\">Données copiées dans le presse-papiers</string>\n    <string name=\"hide_pass\">Masquer le mot de passe</string>\n    <string name=\"ope_url\">Ouvrir le lien</string>\n    <string name=\"show_pass\">Afficher le mot de passe</string>\n    <string name=\"copy_to_clip\">Copier dans le presse-papiers</string>\n    <string name=\"hint_query\">Nom d\\'utilisateur, nom, URL, attributs avancés…</string>\n    <string name=\"no_record\">Aucune entrée</string>\n    <string name=\"db_name_modify\">Nom de la base de donné modifié</string>\n    <string name=\"db_name_no_alter\">Le nom de la base de données n\\'a pas été modifié</string>\n    <string name=\"out_db\">Exporter la base de données</string>\n    <string name=\"modify_db_pass\">Modifier le mot de passe de la base de données</string>\n    <string name=\"modify_db_name\">Modifier le nom de la base de données</string>\n    <string name=\"db_handle\">Opérations sur la base de données</string>\n    <string name=\"fingerprint_unlock\">Déverrouillage par empreinte digitale</string>\n    <string name=\"safety_set\">Paramètres de sécurité</string>\n    <string name=\"create_totp\">Générer un jeton OTP</string>\n    <string name=\"hint_copy_totp\">Jeton TOTP copié dans le presse-papiers</string>\n    <string name=\"hint_copy_pass\">Mot de passe copié dans le presse-papiers</string>\n    <string name=\"hint_copy_user\">Nom d\\'utilisateur copié dans le presse-papiers</string>\n    <string name=\"undo_entryed\">L\\'élément a été déplacé à l\\'emplacement spécifié</string>\n    <string name=\"undo_grouped\">Le groupe a été déplacé à l\\'emplacement spécifié</string>\n    <string name=\"undo_to\">Déplacer vers ce groupe</string>\n    <string name=\"choose_icon\">Sélectionner une icône</string>\n    <string name=\"title_too_long\">Le nom du groupe est trop long</string>\n    <string name=\"error_group_no_modify\">Aucune modification du groupe</string>\n    <string name=\"modify_group\">Modifier le groupe</string>\n    <string name=\"create_group_success\">Groupe créé</string>\n    <string name=\"create_group_fail\">Impossible de créer le groupe</string>\n    <string name=\"error_group_name_null\">Le nom du groupe est vide</string>\n    <string name=\"create_group\">Créer le groupe</string>\n    <string name=\"group_name\">Nom du groupe</string>\n    <string name=\"hint_del_entry\">Voulez-vous vraiment supprimer l\\'élément【&lt;font color=#FF0000&gt;%s&lt;/font&gt;】 \\? &lt;br&gt; Il sera placé dans la corbeille où vous pourrez le restaurer plus tard.</string>\n    <string name=\"hint_del_group_no_recycle\">Voulez-vous vraiment supprimer le groupe【&lt;font color=#FF0000&gt;%s&lt;/font&gt;】 \\?</string>\n    <string name=\"hint_del_group\">Confirmer la suppression du groupe【&lt;font color=#FF0000&gt;%s&lt;/font&gt;】 \\? &lt;br&gt; Il sera placé dans la corbeille où vous pourrez le restaurer plus tard.</string>\n    <string name=\"copy_totp\">Copier le jeton TOTP</string>\n    <string name=\"copy_password\">Copier le mot de passe</string>\n    <string name=\"copy_user_name\">Copier le nom d\\'utilisateur</string>\n    <string name=\"history_record\">Entrée de l\\'historique</string>\n    <string name=\"del_history\">Supprimer l\\'entrée de l\\'historique</string>\n    <string name=\"del_entry\">Supprimer l\\'élément</string>\n    <string name=\"edit_group\">Modifier le groupe</string>\n    <string name=\"del_group\">Supprimer le groupe</string>\n    <string name=\"expire\">Expiré le &lt;font color=#FF0000&gt;%s&lt;/font&gt;</string>\n    <string name=\"hint_ex_property\">Attributs supplémentaires</string>\n    <string name=\"attachment\">Pièce-jointe</string>\n    <string name=\"hint_attr\">Propriétés avancées</string>\n    <string name=\"expire_time\">Expire le %s</string>\n    <string name=\"error_open_db_algorithm_error\">Algorithme invalide</string>\n    <string name=\"error_open_db_signature_error\">Signature de la base de données invalide</string>\n    <string name=\"error_open_db_version_error\">Version de la base de données invalide</string>\n    <string name=\"error_open_db_key_invalid\">Clé invalide</string>\n    <string name=\"error_open_db_pass_error\">Mot de passe invalide</string>\n    <string name=\"error_open_db_key_empty\">Clé manquante</string>\n    <string name=\"error_input_pass_null\">Veuillez entrer le mot de passe de la base de données</string>\n    <string name=\"error_uri\">Erreur de l\\'URI</string>\n    <string name=\"need_key\">Besoin d\\'une clé \\?</string>\n    <string name=\"help_input_db_pass\">Entrez le mot de passe de la base de données</string>\n    <string name=\"welcome\">Bienvenue</string>\n    <string name=\"app_setting\">Paramètres de l\\'application</string>\n    <string name=\"db_setting\">Paramètres de la base de données</string>\n    <string name=\"setting\">Paramètres</string>\n    <string name=\"all\">Éléments</string>\n    <string name=\"change_db\">Changer de base de données</string>\n    <string name=\"edit\">Modifier</string>\n    <string name=\"history\">Historique</string>\n    <string name=\"hint_db_create_success\">La base de données [%s] a été créée</string>\n    <string name=\"error_pass_unfit\">Les mots de passe ne correspondent pas</string>\n    <string name=\"error_enter_pass_null\">Veuillez entrer le mot de passe de confirmation</string>\n    <string name=\"error_pass_null\">Veuillez définir un mot de passe pour la base de données</string>\n    <string name=\"create_file\">Créer un nouveau fichier</string>\n    <string name=\"choose_file\">Sélectionner un fichier</string>\n    <string name=\"hint\">Information</string>\n    <string name=\"cancel\">Annuler</string>\n    <string name=\"enter\">Valider</string>\n    <string name=\"done\">Terminé</string>\n    <string name=\"error_db_name_null\">Veuillez entrer un nom de base de données</string>\n    <string name=\"hint_select_path_type\">Choisissez où sauvegarder la base de données</string>\n    <string name=\"hint_select_db_path\">Cliquez pour définir la méthode de sauvegarde des données</string>\n    <string name=\"up\">Précédent</string>\n    <string name=\"next\">Étape suivante</string>\n    <string name=\"create_db\">Créer une base de données</string>\n    <string name=\"db_name\">Entrez un nom pour la base de données</string>\n    <string name=\"open_db\">Ouvrir la base de données</string>\n    <string name=\"helper_create_pass\">Un mot de passe complexe est recommandé (majuscules et minuscules + chiffres + caractères spéciaux)</string>\n    <string name=\"pass_key\">Fichier de clé</string>\n    <string name=\"hint_set_pass_key\">Veuillez sélectionner une clé</string>\n    <string name=\"choose_pass_key\">Sélectionner une clé</string>\n    <string name=\"encrypt_type_2\">Mot de passe + Clé</string>\n    <string name=\"encrypt_type_1\">Mot de passe seul</string>\n    <string name=\"encrypt_type\">Chiffrement</string>\n    <string name=\"help_create_db\">Créée une base de données KeepPass v4 par défaut</string>\n    <string name=\"hint_enter_password\">Confirmer le mot de passe</string>\n    <string name=\"hint_input_password\">Entrez un mot de passe entre 6 et 16 caractères</string>\n    <string name=\"hint_set_password\">Définir un mot de passe</string>\n    <string name=\"open\">Déverrouiller</string>\n    <string name=\"key1\">Clé secrète : %s</string>\n    <string name=\"key\">Clé secrète</string>\n    <string name=\"password\">Mot de passe</string>\n    <string name=\"sync_db\">Base de données synchronisée</string>\n    <string name=\"cover_cloud\">Dans le nuage</string>\n    <string name=\"net_error\">Problème réseau</string>\n    <string name=\"no_file\">Aucun fichier</string>\n    <string name=\"helper_webdav\">ex. : https://dav.example.org/dav/[votre dossier]/xxx.kdbx</string>\n    <string name=\"afs\">Système de fichier Android</string>\n    <string name=\"hint_kdbx_name\">Suffixe invalide, doit être « .kdbx »</string>\n    <string name=\"start_upload_db\">Envoyer la base de données vers votre disque réseau</string>\n    <string name=\"unlocked\">Déverrouillé</string>\n    <string name=\"db_unlock\">Déverrouiller la base de données</string>\n    <string name=\"quick_unlock\">Déverrouillage rapide</string>\n    <string name=\"version_log\">Mettre à jour le journal</string>\n    <string name=\"hint_finger_print_verfiy\">Veuillez vérifier votre empreinte à nouveau avant d\\'utiliser vos nouveaux paramètres</string>\n    <string name=\"open1\">ouvert</string>\n    <string name=\"add_attr_file\">Ajouter des pièces jointes</string>\n    <string name=\"hint_fingerprint_modify\">Vérifiez que l\\'empreinte digital a changé. Pour que votre mot de passe soit plus sûr, veuillez réinitialiser la fonctionnalité de déverrouillage par empreinte</string>\n    <string name=\"hint_please_open_database\">Veuillez ouvrir la base de données</string>\n    <string name=\"set_delete_no_into_recycle_bin_des\">Déplacer les entrées supprimés dans la corbeille</string>\n    <string name=\"warning_rooted\">Pour plus de sécurité, veuillez ne jamais ouvrir la base de données sur un téléphone rooté</string>\n    <string name=\"sort\">Tri</string>\n    <string name=\"hint_security_yellow\">Attention : Cette application tournant dans un simulateur, certains risques imprévisibles pour la base de données peuvent arriver</string>\n    <string name=\"totp_time\">Essai(s) :</string>\n    <string name=\"totp_len\">Longueur :</string>\n    <string name=\"totp_custom_hint\">Préférences personnalisées</string>\n    <string name=\"totp_custom\">Utiliser les préférences personnalisées</string>\n    <string name=\"totp_defaule\">Jeton au format RFC 6238</string>\n    <string name=\"sort_time_desc\">Date descendante</string>\n    <string name=\"move\">Déplacer</string>\n    <string name=\"hint_not_nore_info\">Pas d\\'autres infos</string>\n    <string name=\"set_key_sum_main_show_entries\">Afficher toutes les entrées après avoir déverrouillé la base de données</string>\n    <string name=\"set_key_title_opr_env_check_turn_off\">Inactif</string>\n    <string name=\"set_key_title_main_allow_show_entries\">Page d\\'accueil</string>\n    <string name=\"set_key_title_opr_env_check\">Vérification de l’environnement opérationnel</string>\n    <string name=\"cur_app_not_autofill\">Aucun remplissage auto</string>\n    <string name=\"multi_choice\">Plusieurs choix</string>\n    <string name=\"ref_entry\">Entrée de référence</string>\n    <string name=\"close\">Fermer</string>\n    <string name=\"Alipay\">Alipay</string>\n    <string name=\"set_title_screen_lock\">Verrouillage de l\\'écran</string>\n    <string name=\"set_summary_screen_lock_on\">Verrouille automatiquement la base de données</string>\n    <string name=\"set_summary_screen_lock_off\">Ne pas verrouiller la base de données</string>\n    <string name=\"set_key_title_show_state_bar\">Barre de status</string>\n    <string name=\"choose_pass_key_file_des\">Choisir n\\'importe quel fichier pour la clé</string>\n    <string name=\"create_pass_key_file_des\">Créer un nouveau fichier à utiliser pour la clé</string>\n    <string name=\"create_pass_key_success\">La création de la clé [%s] a réussi</string>\n    <string name=\"hint_group_desc\">Éléments du groupe « %s »</string>\n    <string name=\"error_group_id_null\">L\\'identifiant du groupe est vide</string>\n    <string name=\"fail\">Échec</string>\n    <string name=\"quick_use_fingerprint\">N\\'utiliser que les empreintes digitales dans l\\'interface de déverrouillage rapide</string>\n    <string name=\"normal_account\">Compte normal</string>\n    <string name=\"choose_dir\">Enregistrer le groupe actuel</string>\n    <string name=\"add_more\">Ajouter plus</string>\n    <string name=\"warning\">Avertissement</string>\n    <string name=\"hint_input_attr_str_key\">Nom du champ</string>\n    <string name=\"del_attr_str\">Supprimer les champs personnalisés</string>\n    <string name=\"download_file\">Enregistrer les pièces jointes dans la carte SD</string>\n    <string name=\"db_pass_no_alter\">Ce nouveau mot de passe est identique à l\\'ancien</string>\n    <string name=\"open_whit_text\">Ouvrir avec l\\'éditeur de texte</string>\n    <string name=\"open_whit_other\">Mettre en cache et ouvrir avec app système</string>\n    <string name=\"txt_viewer\">Aperçu du fichier</string>\n    <string name=\"error_pass\">Erreur du mot de passe</string>\n    <string name=\"app_feedback\">Suggestions</string>\n    <string name=\"relevance_db\">Données liées</string>\n    <string name=\"no_matching_entry\">Aucun correspondance</string>\n    <string name=\"modify_time\">Modifier la date</string>\n    <string name=\"title\">Titre</string>\n    <string name=\"submit\">Soumettre</string>\n    <string name=\"send_email_fail\">Veuillez d\\'abord installer une app pour envoyer des emails</string>\n    <string name=\"update_group_success\">Infos du groupe mis à jour</string>\n    <string name=\"update_group_fail\">Échec de la mise à jour des infos du groupe</string>\n    <string name=\"set_key_praise_value\">Noter KeePassA</string>\n    <string name=\"clean_clip_time_summary\">Vider le presse-papiers %s après que le mot de passe a été copié</string>\n    <string name=\"error_short_pass_lenght\">Veuillez définir le mot de passe court avec 6 caractères ou moins</string>\n    <string name=\"des_quick_unlock\">Verrouiller la base de données avec un mot de passe court</string>\n    <string name=\"quick_pass_len_summary\">Longueur du mot de passe court : %s</string>\n    <string name=\"set_key_main_show_totp_tab_on\">Afficher la catégorie TOTP</string>\n    <string name=\"set_key_main_show_totp_tab_title\">La page d\\'accueil affiche les catégories TOTP</string>\n    <string name=\"set_key_main_show_totp_tab_off\">Ne pas afficher la catégorie TOTP</string>\n    <string name=\"get_token_fail\">Échec d\\'obtention du jeton</string>\n    <string name=\"error_open_db\">Échec de l\\'ouverture de la base de donnée. Le mot de passe est-il correct \\? La clé est-elle manquante \\?</string>\n    <string name=\"error_entry_id_null\">L\\'identifiant de l\\'entrée est vide</string>\n    <string name=\"undo_entry\">Reprise…</string>\n    <string name=\"create_entry_no_save\">Quitter la page actuelle sans sauvegarder cette entrée \\?</string>\n    <string name=\"error_attr_str_null\">Le nom est vide</string>\n    <string name=\"hint_webdav_url\">URL du fichier</string>\n    <string name=\"use_full_fingerprint\">Déverrouillage complet par emprunte digitale</string>\n    <string name=\"disable\">Désactiver</string>\n    <string name=\"helper_webdav_dir\">ex. : https://dav.example.org/dav/[votre dossier]/</string>\n    <string name=\"save_db_fail\">Échec de l\\'enregistrement de la base de données</string>\n    <string name=\"notify_db_locked\">La base de données est verrouillée</string>\n    <string name=\"hint_db_pass_modify_success\">Utiliser ce nouveau mot de passe pour la prochaine ouverture de la base de données</string>\n    <string name=\"setting_miui_background_start\">Paramètres &gt; Paramètres d\\'application &gt; Gestion de l\\'application &gt; KeePassA &gt; Gestion des permissions</string>\n    <string name=\"setting_vivo_background_start\">Paramètres &gt; Plus de paramètres &gt; Gestion des permissions &gt; KeePassA &gt; Paramètres des permissions &gt; Notification en arrière-plan</string>\n    <string name=\"notify_quick_unlock_start\">La base de données est verrouillée, le déverrouillage rapide est activé</string>\n    <string name=\"hint_quick_use_fingerprint\">Ne pouvoir utilisez que votre empreinte digitale pour le déverrouillage rapide</string>\n    <string name=\"donate_desc\">[KeepassA](https://github.com/AriaLyy/KeepassA) est une application libre et open source. Si vous l\\'aimez, vous pouvez supporter notre travail par un don. Merci.</string>\n    <string name=\"sort_chart_asc\">Ordre alphabétique (A-Z)</string>\n    <string name=\"sort_chart_desc\">Ordre anti-alphabétique (Z-A)</string>\n    <string name=\"ime_label\">Clavier sécurisé de KeePassA</string>\n    <string name=\"dev_birthday\">C\\'est l\\'anniversaire du développeur 🎂. Aimerez-vous m\\'offrir un gâteau d\\'anniversaire \\?</string>\n    <string name=\"totp_key_error\">Le code TOTP n\\'a pu être généré, la clé est invalide</string>\n    <string name=\"set_open_kpa_ime_title\">Clavier sécurisé</string>\n    <string name=\"license\">Licence de logiciel libre</string>\n    <string name=\"error_uri_grant_permission\">Échec de l\\'autorisation URI</string>\n    <string name=\"no_totp_token\">Aucun jeton TOTP</string>\n    <string name=\"set_key_sum_main_show_history\">Afficher l\\'historique après avoir déverrouillé la base de données</string>\n    <string name=\"ui_setting\">Préférence de l\\'interface</string>\n    <string name=\"error_webdav_end_suffix\">Utilisez le chemin vers le dossier, par ex. : « https://example.org/dav/kpa/ »</string>\n    <string name=\"error_webdav_end_dav_suffix\">Le chemin « /dav/ » n\\'est pas autorisé. Veuillez utiliser un chemin valide, comme : « https://example.org/dav/kpa/ »</string>\n    <string name=\"set_key_title_fillet_bg_icon\">Icônes avec coin arrondis</string>\n    <string name=\"set_key_title_fillet_bg_icon_turn_on\">Actif</string>\n    <string name=\"set_key_title_fillet_bg_icon_turn_off\">Inactif</string>\n    <string name=\"s_default\">Par défaut</string>\n    <string name=\"customize\">Personnaliser</string>\n    <string name=\"set_key_title_close_load_anim_turn_on\">Actif</string>\n    <string name=\"set_key_title_close_load_anim_turn_off\">Inactif</string>\n    <string name=\"set_key_title_auto_lock_database_turn_off\">Non</string>\n    <string name=\"translate_language\">Traduisez l\\'application dans votre langue</string>\n    <string name=\"hint_security_green\">\"L\\'application est dans un environnement sûr \"</string>\n    <string name=\"hint_security_red\">Votre téléphone a été rooté, la base de données se trouve donc dans un environnement très peu sûr</string>\n    <string name=\"help_create_group\">Choisissez un nom de groupe de 16 caractères max</string>\n    <string name=\"create_time\">Fait à %s</string>\n    <string name=\"help_pass_type\">N\\'utilisez que votre mot de passe :\n\\n Seul votre mot de passe sera nécessaire pour ouvrir la base de données ;\n\\nMot de passe + clé :\n\\n Votre mot de passe sera nécessaire mais il vous sera aussi demandé le fichier de clé pour ouvrir la base de données.</string>\n    <string name=\"success\">Succès</string>\n    <string name=\"hint_protect_str\">Champ protégé</string>\n    <string name=\"help_pass_key\">Un fichier clé est l\\'équivalent d\\'un mot de passe pour accéder à vos données, mais bien souvent, c\\'est une méthode beaucoup plus sûre mais aussi plus complexe. Mais c\\'est aussi bien plus difficile à cacher. Par exemple, si vous stockez votre base de données dans le nuage, veillez ne pas placer votre fichier de clé avec.\n\\n Vous pouvez choisir n\\'importe quel fichier pour servir de clé, comme une photo ou un fichier texte.\n\\n ⚠️ Avertissement : La moindre modification de ce fichier clé vous empêchera de pouvoir l\\'utiliser pour ouvrir la base de données par la suite !!!\n\\n Il est donc fortement recommandé d\\'utiliser un fichier en lecture seule comme clé.</string>\n    <string name=\"hint_del_entry_no_recycle\">Voulez-vous vraiment supprimer l\\'entrée 【&lt;font color=#FF0000&gt;%s&lt;/font&gt;】définitivement \\?</string>\n    <string name=\"hint_input_attr_str_value\">Valeur du champ</string>\n    <string name=\"error_attr_file_too_large\">Réduisez l\\'utilisation de la mémoire en évitant de joindre des fichiers de plus de 10 Mo</string>\n    <string name=\"hint_full_fingerprint\">Votre empreinte digitale est utilisée pour déverrouiller la base de donnée dans les interfaces de déverrouillage rapide et de déverrouillage de la base de données, KeePassA crypte et stocke le mot de passe de votre base de donnée ainsi que les informations de la clé dans le trousseau d\\'accès système d\\'Android, et utiliser l\\'autorisation d\\'empreinte digitale pour plus de protection. Ainsi, seule votre empreinte digitale pourra déverrouiller votre base de données.</string>\n    <string name=\"add_custom_str\">Créer des champs personnalisés</string>\n    <string name=\"del_attr_file\">Supprimer la pièce jointe</string>\n    <string name=\"open_whit_img\">Ouvrir avec le visualiseur d\\'images</string>\n    <string name=\"save_file_success\">Le fichier [%s] a été enregistré</string>\n    <string name=\"feedback\">Suggestions ^_^</string>\n    <string name=\"merge\">Fusionner</string>\n    <string name=\"search\">Chercher</string>\n    <string name=\"open_permissions\">Ouvrir les autorisations</string>\n    <string name=\"quick_pass_len_title\">Longueur des mots de passe courts</string>\n    <string name=\"ime_entry_other_info\">Remplir les autres champs</string>\n    <string name=\"cover\">Couverture</string>\n    <string name=\"login\">Connexion</string>\n    <string name=\"closed\">Fermé</string>\n    <string name=\"upload\">Envoyer</string>\n    <string name=\"donate\">Faire un don</string>\n    <string name=\"url\">URL</string>\n    <string name=\"expand\">Développer</string>\n    <string name=\"shrink\">Réduire</string>\n    <string name=\"other\">Autres</string>\n    <string name=\"set_key_title_show_state_bar_turn_off\">Masquer</string>\n    <string name=\"set_open_auto_fill_title\">Service de remplissage auto</string>\n    <string name=\"clean_clip_time\">Presse-papiers vide</string>\n    <string name=\"set_delete_setting\">Supprimer les préférences</string>\n    <string name=\"set_key_theme_style_summary\">Même que le système</string>\n    <string name=\"cover_local\">Local</string>\n    <string name=\"totp_Arithmetic\">Algorithme :</string>\n    <string name=\"set_key_title_show_state_bar_turn_on\">Afficher</string>\n    <string name=\"lose_time\">Date d\\'expiration</string>\n    <string name=\"other_set\">Autres préférences</string>\n    <string name=\"open_quick_unlock\">Déverrouillage rapide</string>\n    <string name=\"set_key_value\">Choisir la langue</string>\n    <string name=\"invalid\">Invalide</string>\n    <string name=\"set_key_title_opr_env_check_turn_on\">Actif</string>\n    <string name=\"save_db_success\">Base de données enregistrée</string>\n    <string name=\"hint_db_pass_modify\">Mot de passe de la base de données changé</string>\n    <string name=\"set_key_title_auto_lock_database_turn_on\">Oui</string>\n    <string name=\"mark_not_exit\">Boutique d\\'application inexistante</string>\n    <string name=\"hint_please_input\">Veuillez entrer %s</string>\n    <string name=\"db_file_no_exist\">Le fichier de la base de données n\\'existe pas</string>\n    <string name=\"feedback_email_msg\">Bonjour, je suis KeePassA, avez-vous des questions \\?<br/> <br/> <br/> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ <br/> Marque : %s <br/> Modèle : %s <br/> Version du système :%s <br/> Résolution: %s <br/> Version de l\\'application: %s</string>\n    <string name=\"notify_channel_db_open\">Notification de déverrouillage</string>\n    <string name=\"set_key_title_auto_lock_database\">Verrouiller automatiquement la base de données</string>\n    <string name=\"autofill_sign_in_prompt\">Remplissez automatiquement les mots de passe avec KeePassA</string>\n    <string name=\"auth\">Authentification</string>\n    <string name=\"sort_time_asc\">Date ascendante</string>\n    <string name=\"help_create_db_path\">Il est recommandé d\\'utiliser un chemin Cloud tel que Dropbox et Webdav. Cela synchronisera automatiquement votre base de données dans le cloud et facilitera son utilisation sur d\\'autres appareils.\n\\n La base de données est chiffrée en AES256. Sans votre mot de passe, personne ne peut ouvrir votre base de données.</string>\n    <string name=\"db1\">base de données\\t|\\t&lt;font color=#757575&gt;%s&lt;/font&gt;</string>\n    <string name=\"invalid_auth\">L\\'autorisation a échoué. Veuillez réessayer.</string>\n    <string name=\"error_db_pass_too_short\">Veuillez utiliser plus de 6 caractères pour le mot de passe afin de bien protéger vos données</string>\n    <string name=\"error_short_pass_short\">Veuillez définir le mot de passe court avec 3 caractères ou plus</string>\n    <string name=\"file_conflict_msg\">Conflit entre les données dans le nuage et les données locales, veuillez sélectionner la méthode de synchronisation. Les entrées en conflit sont :\n\\n %s</string>\n    <string name=\"set_delete_no_into_recycle_bin\">Supprime définitivement les entrées de la base de données</string>\n    <string name=\"set_key_theme_style_title\">Style du thème</string>\n    <string name=\"auto_lock_db_time\">Verrouiller automatiquement la base de données</string>\n    <string name=\"hint_background_start\">Rendez-vous dans&lt;font color=#FF0000&gt;【%s】&lt;/font&gt;pour activer les permissions de notification en arrière-plan pour un meilleur remplissage en mode automatique.&lt;br&gt; &lt;font color=#FF0000&gt;keepassA va activer seulement l\\'interface correspondante pour ouvrir la base de données quand vous utilisez la fonction de saisie automatique&lt;/font&gt;</string>\n    <string name=\"hint_save_auto_fill\">Lors de l\\'association du nom du paquet【&lt;font color=#FF0000&gt;%s&lt;/font&gt;】avec l\\'entrée【&lt;font color=#FF0000&gt;%s&lt;/font&gt;】</string>\n    <string name=\"auto_lock_db_time_summary\">Verrouiller automatiquement la base de données après %s sans aucune opération</string>\n    <string name=\"dropbox_msg\">Afin de respecter votre vie privée, KeePassA ne demandera pas de permission sur tous les fichiers de votre compte Dropbox, mais &lt;font color = # FF0000&gt; seulement l\\'accès au dossier 【Apps/KeePassA】 &lt;/ font &gt;.&lt;br /&gt; Par conséquent : &lt;br /&gt; &lt;b&gt;Si vous avez déjà une base de données KeePass, vous devez autoriser KeePassA à accéder au dossier 【apps/KeePassA】 dans KeePassA et enregistrer la base de données KeePass dans le dossier 【Application / KeePassA】 lorsque l\\'autorisation est terminée. &lt;/b&gt;&lt;br /&gt;&lt;br /&gt; Si vous créez une nouvelle base de données KeePass, les opérations suivantes ne seront pas nécessaires. Après la création de la base de données, KeePassA créera automatiquement le dossier correspondant et déplacera la base de donnée dans le dossier 【Apps/KeePassA】 de Dropbox.</string>\n    <string name=\"one_drive_hint\">Pour respecter votre vie privée, KeePassA n\\'aura accéder qu\\'au dossier &lt;font color=#FF0000&gt;【application/KeepassA】&lt;/font&gt;. Si vous souhaitez accéder à la base de données depuis OneDrive, &lt;font color=#FF0000&gt;veuillez la stocker dans ce même dossier.&lt;/font&gt;</string>\n    <string name=\"error_keystore\">Erreur du trousseau d\\'accès, veuillez réessayer</string>\n    <string name=\"kpa_totp\">Jeton</string>\n    <string name=\"kpa_copy\">Copier</string>\n    <string name=\"error_connect_play\">Échec de la connexion aux services Google</string>\n    <string name=\"miui_permissions\">Le système « miui » a besoin d\\'autorisations pour ouvrir une fenêtre en arrière-plan pour vous permettre d\\'utiliser le remplissage automatique.</string>\n    <string name=\"one_drive_init_failure\">Échec de OneDrive</string>\n    <string name=\"set_key_title_close_load_anim\">Animation de chargement</string>\n    <string name=\"error_qr_code_str\">Ce QR code n\\'est pas normal. Veuillez vérifier que ce QR code est supporté par Google Authenticator</string>\n    <string name=\"thank_donate\">Merci pour votre don.</string>\n    <string name=\"error_donate\">Échec du don</string>\n    <string name=\"year\">Année</string>\n    <string name=\"monthly\">Tous les mois</string>\n    <string name=\"disposable\">Une seule fois</string>\n    <string name=\"google_play\">Google Play</string>\n    <string name=\"current_collection_num\">%1$s collections</string>\n    <string name=\"my_collection\">Mes collections</string>\n    <string name=\"no_collection\">Aucune collection</string>\n    <string name=\"error_open_db_webdav\">Échec d\\'ouverture de la base de données, le fichier WebDAV n\\'existe pas</string>\n    <string name=\"helper_webdav_service\">Sélectionner un service WebDAV</string>\n    <string name=\"error_is_root\">Est déjà dans le dossier racine</string>\n    <string name=\"select_cur_path\">Sélectionner un dossier</string>\n    <string name=\"select_save_path\">Sélectionner l\\'emplacement d\\'enregistrement</string>\n    <string name=\"one_drive_load_user_failure\">Échec le chargement des infos de l\\'utilisateur</string>\n    <string name=\"totp_steam\">Préférences de jeton de flux</string>\n    <string name=\"error_open_db_arcfour_error\">Algorithme de cryptage de flux invalide</string>\n    <string name=\"quick_pass_type\">Emplacement d\\'interception de mots de passes courts</string>\n    <string name=\"hint_open_background_start\">Veuillez ouvrir la permission de retrait en arrière-plan, sinon vous ne pourrez pas utiliser le remplissage automatique</string>\n    <string name=\"hint_cloud_file_already_exist\">%1$s existe déjà. Voulez-vous écraser ce fichier \\?</string>\n    <string name=\"webdav_port_name_null\">Le nom de l\\'hôte ne peut pas être vide</string>\n    <string name=\"privacy_agreement\">Contrat de confidentialité</string>\n    <string name=\"port\">Port</string>\n    <string name=\"hostname\">Nom d\\'hôte</string>\n    <string name=\"webdav_port_hint\">Port</string>\n    <string name=\"webdav_host_name_hint\">Veuillez entrer votre nom d\\'hôte</string>\n    <string name=\"please_open_proxy\">veuillez ouvrir le proxy</string>\n    <string name=\"ime_hint_open_auto_fill\">Veuillez activer le service de remplissage auto</string>\n    <string name=\"invalid_img\">Image invalide</string>\n</resources>"
  },
  {
    "path": "app/src/main/res/values-fr-rCA/arrays.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n  <string-array name=\"quick_pass_type_entries\">\n    <item>%s au début du mot de passe de BD</item>\n    <item>%s à la fin du mot de passe de BD</item>\n  </string-array>\n\n  <string-array name=\"clean_clip_entries\">\n    <item>30 secondes</item>\n    <item>1 minute</item>\n    <item>2 minutes</item>\n    <item>5 minutes</item>\n  </string-array>\n\n  <string-array name=\"quick_lock_entries\">\n    <item>2 minutes</item>\n    <item>5 minutes</item>\n    <item>10 minutes</item>\n    <item>20 minutes</item>\n  </string-array>\n\n  <string-array name=\"out_db_entries\">\n    <item>Base de données Keepass2</item>\n    <item>Fichier XML non chiffré</item>\n    <item>Fichier CSV non chiffré</item>\n  </string-array>\n\n  <string-array name=\"v3_add_mor_item\">\n    <item>Note</item>\n    <item>Pièce jointe</item>\n    <item>Date d\\'expiration</item>\n  </string-array>\n\n  <string-array name=\"v4_add_mor_item\">\n    <item>Note</item>\n    <item>Champ personnalisé</item>\n    <item>Pièce jointe</item>\n    <item>TOTP</item>\n    <item>Étiquette</item>\n    <item>URL</item>\n    <item>Date d\\'expiration</item>\n  </string-array>\n\n  <string-array name=\"create_normal_group\">\n    <item>Boîte courriel</item>\n    <item>Travail</item>\n    <item>Finances</item>\n    <item>Divertissement</item>\n    <item>Corbeille</item>\n  </string-array>\n\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-fr-rCA/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"db\">Base de données</string>\n    <string name=\"db1\"><![CDATA[database\\t|\\t<font color=\"#757575\">%s</font>]]></string>\n    <string name=\"password\">Mot de passe</string>\n    <string name=\"key\">Clé secrète</string>\n    <string name=\"key1\">Clé secrète: %s</string>\n    <string name=\"open\">Déverrouiller</string>\n    <string name=\"hint_set_password\">Définir un mot de passe</string>\n    <string name=\"hint_input_password\">Entrez un mot de passe de 6–16 chiffres</string>\n    <string name=\"hint_enter_password\">Confirmer le mot de passe</string>\n    <string name=\"help_create_db\">Crée par défaut une version v4 de la base de données KeePass.</string>\n    <string name=\"help_create_group\">Le nom de groupe ne peut excéder 16 caractères</string>\n    <string name=\"encrypt_type\">Chiffrement</string>\n    <string name=\"encrypt_type_1\">Mot de passe seulement</string>\n    <string name=\"encrypt_type_2\">Mot de passe + clé</string>\n    <string name=\"choose_pass_key\">Choisir une clé</string>\n    <string name=\"hint_set_pass_key\">Veuillez sélectionner une clé</string>\n    <string name=\"pass_key\">Fichier de clé</string>\n    <string name=\"helper_create_pass\">Mot de passe mixte recommandé (majuscules et minuscules + chiffres + symboles)</string>\n    <string name=\"open_db\">Ouvrir une base de données</string>\n    <string name=\"db_name\">Nom de la base de données</string>\n    <string name=\"create_db\">Créer une base de données</string>\n    <string name=\"next\">Étape suivante</string>\n    <string name=\"up\">Précédent</string>\n    <string name=\"hint_select_db_path\">Cliquez pour définir la méthode de sauvegarde</string>\n    <string name=\"hint_select_path_type\">Choisir le chemin de sauvegarde de la BD</string>\n    <string name=\"error_db_name_null\">Veuillez entrer le nom de la base de données</string>\n    <string name=\"done\">Terminer</string>\n    <string name=\"enter\">OK</string>\n    <string name=\"cancel\">Annuler</string>\n    <string name=\"hint\">Message</string>\n    <string name=\"help_create_db_path\">Il est recommandé d\\'utiliser un chemin Cloud tel que Dropbox et Webdav. Cela synchronisera automatiquement votre base de données dans le cloud et facilitera son utilisation sur d\\'autres appareils. \\n La base de données est chiffrée en AES256. Sans votre mot de passe, personne ne peut ouvrir votre base de données.</string>\n    <string name=\"help_pass_type\">Utiliser seulement un mot de passe: \\n Seul votre mot de passe est requis pour ouvrir la base de données;\\n Mot de passe + clé: \\n Vous devez entrer un mot de passe et sélectionner un fichier de clé pour ouvrir la base de données.</string>\n    <string name=\"help_pass_key\">Un fichier de clé est l\\'équivalent d\\'une clé pour ouvrir des données et est généralement beaucoup plus complexe et fiable qu\\'un mot de passe. Mais il est aussi plus difficile de le cacher. Si vous conservez la base de données dans le cloud, ne mettez pas la clé avec la base de données, sinon la clé perdra son sens.\n\\n Vous pouvez choisir n\\'importe quel fichier comme clé pour ouvrir la base de données, une photo ou un fichier .txt.\n\\n⚠️ Note: Si cette photo ou ce document .txt est modifié, vous ne pourrez pas ouvrir la base de données !!!\n\\n Il est fortement recommandé d\\'utiliser des fichiers en lecture seule comme clés.</string>\n    <string name=\"choose_file\">Choisir un fichier</string>\n    <string name=\"choose_pass_key_file_des\">Choisissez n\\'importe quel fichier comme clé</string>\n    <string name=\"create_file\">Créer un nouveau fichier</string>\n    <string name=\"create_pass_key_file_des\">Créez un nouveau fichier pour votre clé</string>\n    <string name=\"create_pass_key_success\">Création de la clé [%s] réussie !</string>\n    <string name=\"error_pass_null\">Veuillez définir un mot de passe pour la BD</string>\n    <string name=\"error_enter_pass_null\">Veuillez entrer le mot de passe de confirmation</string>\n    <string name=\"error_pass_unfit\">Les mots de passe ne correspondent pas</string>\n    <string name=\"hint_db_create_success\">Base de données [%s] créée avec succès</string>\n    <string name=\"history\">Historique</string>\n    <string name=\"history_record\">Historique</string>\n    <string name=\"edit\">Éditer</string>\n    <string name=\"change_db\">Changer de base de données</string>\n    <string name=\"all\">Toutes les entrées</string>\n    <string name=\"setting\">Paramètres</string>\n    <string name=\"db_setting\">Paramètres de la base de données</string>\n    <string name=\"app_setting\">Paramètres de l\\'application</string>\n    <string name=\"welcome\">Bienvenue</string>\n    <string name=\"help_input_db_pass\">Entrez le mot de passe de la BD</string>\n    <string name=\"need_key\">Clé requise ?</string>\n    <string name=\"error_uri\">Erreur d\\'Uri</string>\n    <string name=\"error_input_pass_null\">Veuillez entrer le mot de passe de la BD</string>\n    <string name=\"error_open_db\">Échec d\\'ouverture de la base de données, veuillez vérifier si le mot de passe est correct ou si une clé est requise</string>\n    <string name=\"error_open_db_key_empty\">Clé manquante</string>\n    <string name=\"error_open_db_pass_error\">Mot de passe incorrect</string>\n    <string name=\"error_open_db_key_invalid\">Clé non valide</string>\n    <string name=\"error_open_db_version_error\">Version de la base de données non valide</string>\n    <string name=\"error_open_db_signature_error\">Signature de base de données non valide</string>\n    <string name=\"error_open_db_algorithm_error\">Algorithme non valide</string>\n    <string name=\"error_open_db_arcfour_error\">Algorithme de chiffrement de flux non valide</string>\n    <string name=\"hint_group_desc\">Groupe - %s entrées</string>\n    <string name=\"error_group_id_null\">groupId est nul</string>\n    <string name=\"error_entry_id_null\">entryId est nul</string>\n    <string name=\"create_time\">Créée le : %s</string>\n    <string name=\"expire_time\">Expire le : %s</string>\n    <string name=\"hint_attr\">Attributs supplémentaires</string>\n    <string name=\"attachment\">Pièce jointe</string>\n    <string name=\"hint_ex_property\">Attributs supplémentaires</string>\n    <string name=\"expire\">Expiré à &lt;font color=#FF0000&gt;%s&lt;/font&gt;</string>\n    <string name=\"del_group\">Supprimer le groupe</string>\n    <string name=\"edit_group\">Éditer le groupe</string>\n    <string name=\"success\">Succès</string>\n    <string name=\"fail\">Échec</string>\n    <string name=\"del_entry\">Supprimer l\\'entrée</string>\n    <string name=\"del_history\">Supprimer l\\'enregistrement de l\\'historique</string>\n    <string name=\"copy_user_name\">Copier l\\'utilisateur</string>\n    <string name=\"copy_password\">Copier le mot de passe</string>\n    <string name=\"copy_totp\">Copier le jeton TOTP</string>\n    <string name=\"hint_del_group\"><![CDATA[Confirmer la suppression du groupe【<font color=\"#FF0000\">%s</font>】? <br> Cela va déplacer le groupe【<font color=\"#FF0000\">%s</font>】dans la corbeille. Si vous souhaitez annuler la suppression, restaurez le groupe depuis la corbeille.]]></string>\n    <string name=\"hint_del_group_no_recycle\"><![CDATA[Confirmer la suppression du groupe【<font color=\"#FF0000\">%s</font>】? Cela va supprimer définitivement le groupe.]]></string>\n    <string name=\"hint_del_entry\"><![CDATA[Confirmer la suppression de l\\'entrée【<font color=\"#FF0000\">%s</font>】? <br> Cela va déplacer l\\'entrée【<font color=\"#FF0000\">%s</font>】dans la corbeille. Si vous souhaitez annuler la suppression, restaurez l\\'élément depuis la corbeille.]]></string>\n    <string name=\"hint_del_entry_no_recycle\"><![CDATA[Confirmer la suppression de l\\'entrée【<font color=\"#FF0000\">%s</font>】? Cela va la supprimer définitivement.]]></string>\n    <string name=\"group_name\">Nom du groupe</string>\n    <string name=\"create_group\">Créer un groupe</string>\n    <string name=\"error_group_name_null\">Nom du groupe est nul</string>\n    <string name=\"create_group_fail\">Échec de la création du groupe</string>\n    <string name=\"create_group_success\">Création du groupe réussie !</string>\n    <string name=\"modify_group\">Modification du groupe</string>\n    <string name=\"error_group_no_modify\">Aucun changement sur le groupe</string>\n    <string name=\"title_too_long\">Nom du groupe trop long</string>\n    <string name=\"choose_icon\">Choisir une icône</string>\n    <string name=\"undo_entry\">Récupérer..</string>\n    <string name=\"undo_to\">Déplacer vers ce groupe</string>\n    <string name=\"undo_grouped\">Le groupe a été déplacé vers l\\'emplacement spécifié</string>\n    <string name=\"undo_entryed\">L\\'entrée a été déplacée vers l\\'emplacement spécifié</string>\n    <string name=\"hint_copy_user\">Utilisateur copié vers le Presse-papier</string>\n    <string name=\"hint_copy_pass\">Mot de passe copié vers le Presse-papier</string>\n    <string name=\"hint_copy_totp\">Jeton TOTP copié vers le Presse-papier</string>\n    <string name=\"create_totp\">Générer un jeton OTP</string>\n    <string name=\"safety_set\">Paramètres de sécurité</string>\n    <string name=\"fingerprint_unlock\">Déverrouillage avec empreinte</string>\n    <string name=\"db_handle\">Opérations de base de données</string>\n    <string name=\"modify_db_name\">Modifier le nom de la BD</string>\n    <string name=\"modify_db_pass\">Changer le mot de passe de la BD</string>\n    <string name=\"out_db\">Exporter la base de données</string>\n    <string name=\"db_name_no_alter\">Le nom de la BD n\\'a pas été changé</string>\n    <string name=\"db_name_modify\">Nom de base de données changé </string>\n    <string name=\"no_record\">Aucun enregistrement</string>\n    <string name=\"hint_query\">Nom d\\'utilisateur, titre, url, attributs avancés …</string>\n    <string name=\"copy_to_clip\">Copier vers le Presse-papier</string>\n    <string name=\"show_pass\">Afficher le mot de passe</string>\n    <string name=\"ope_url\">Ouvrir le lien</string>\n    <string name=\"hide_pass\">Cacher le mot de passe</string>\n    <string name=\"hint_copy_to_clip\">Donnée copiée vers le Presse-papier</string>\n    <string name=\"verify_finger\">Vérification d\\'empreinte </string>\n    <string name=\"verify_finger_fail\">La vérification de l\\'empreinte a échoué</string>\n    <string name=\"ban_fingerprint\">Désactiver le déverrouillage par empreinte</string>\n    <string name=\"quick_use_fingerprint\">Utiliser l\\'empreinte seulement pour l\\'interface de déverrouillage rapide</string>\n    <string name=\"use_full_fingerprint\">Activer l\\'empreinte pour le déverrouillage complet</string>\n    <string name=\"hint_full_fingerprint\">Activer le déverrouillage complet par empreinte. L\\'empreinte sera utilisée pour déverrouiller la base de données dans l\\'interface de déverrouillage rapide et l\\'interface de déverrouillage de la BD. keepassA va chiffrer et sauvegarder votre mot de passe maître et les infos de la clé dans le fichier keystore du système Android, puis utiliser une autorisation par empreinte pour votre protection. Seule votre empreinte peut déverrouiller la base de données.</string>\n    <string name=\"hint_quick_unlock\">Entrez les %s derniers chiffres du mot de passe de votre base de données</string>\n    <string name=\"hint_input_title\">Titre</string>\n    <string name=\"hint_input_user_name\">Nom d\\'utilisateur</string>\n    <string name=\"hint_input_url\">Adresse Web</string>\n    <string name=\"notice\">Note</string>\n    <string name=\"hint_input_tag\">Étiquette1, Étiquette2</string>\n    <string name=\"hint_input_cover_url\">URL de remplacement</string>\n    <string name=\"create_entry\">Créer une entrée</string>\n    <string name=\"save\">Enregistrer</string>\n    <string name=\"len\">Longueur</string>\n    <string name=\"upper\">Lettres majuscules</string>\n    <string name=\"lower\">Lettres minuscules</string>\n    <string name=\"numer\">Chiffres</string>\n    <string name=\"minus\">Traits d\\'union</string>\n    <string name=\"underline\">Soulignement</string>\n    <string name=\"space\">Espaces</string>\n    <string name=\"special\">Spéciaux</string>\n    <string name=\"bracket\">Crochets et parenthèses</string>\n    <string name=\"pass_generater\">Générateur de mot de passe</string>\n    <string name=\"error_genera_params\">Veuillez sélectionner au moins un type de génération de mot de passe</string>\n    <string name=\"hint_pass_null\">Mot de passe est nul</string>\n    <string name=\"date\">Date</string>\n    <string name=\"time\">Heure</string>\n    <string name=\"choose_dir\">Enregistrer dans le groupe actuel</string>\n    <string name=\"normal_account\">Compte normal</string>\n    <string name=\"add_more\">Ajouter plus</string>\n    <string name=\"warning\">Avertissement</string>\n    <string name=\"create_entry_no_save\">L\\'entrée créée n\\'a pas été sauvegardée. Êtes-vous sûr de vouloir sortir de la page actuelle ?</string>\n    <string name=\"add_custom_str\">Créer un champ personnalisé</string>\n    <string name=\"hint_input_attr_str_key\">Nom du champ</string>\n    <string name=\"hint_input_attr_str_value\">Valeur du champ</string>\n    <string name=\"hint_protect_str\">Champ protégé</string>\n    <string name=\"error_attr_str_null\">Nom de champ vide</string>\n    <string name=\"del_attr_str\">Supprimer le champ personnalisé</string>\n    <string name=\"del_attr_file\">Supprimer la pièce jointe</string>\n    <string name=\"error_attr_file_too_large\">Afin de réduire la consommation de mémoire, veuillez ne pas joindre des fichiers de plus de 10 Mo</string>\n    <string name=\"download_file\">Enregistrer sur une carte SD</string>\n    <string name=\"open_whit_text\">Ouvrir avec éditeur texte intégré</string>\n    <string name=\"open_whit_img\">Ouvrir avec visualiseur d\\'image intégré</string>\n    <string name=\"open_whit_other\">Mettre en cache et ouvrir avec app système</string>\n    <string name=\"save_file_success\">Fichier [%s] enregistré avec succès</string>\n    <string name=\"txt_viewer\">Visualiseur texte</string>\n    <string name=\"save_db_fail\">Échec de sauvegarde de la BD</string>\n    <string name=\"save_db_success\">BD sauvegardée avec succès</string>\n    <string name=\"error_pass\">Erreur de mot de passe</string>\n    <string name=\"db_pass_no_alter\">Le nouveau mot de passe est le même que l\\'ancien</string>\n    <string name=\"hint_db_pass_modify_success\">Le mot de passe de base de données a été changé avec succès et sera effectif à la prochaine ouverture de la BD</string>\n    <string name=\"hint_db_pass_modify\">Le mot de passe a été changé </string>\n    <string name=\"app_feedback\">Donnez votre avis</string>\n    <string name=\"error_db_pass_too_short\">Afin de protéger la sécurité de vos données, le mot de passe de la base de données ne peut avoir moins de 6 chiffres</string>\n    <string name=\"autofill_sign_in_prompt\">Remplissez les mots de passe avec keeppassA</string>\n    <string name=\"hint_background_start\">Rendez-vous dans&lt;font color=#FF0000&gt;【%s】&lt;/font&gt;pour activer les permissions de notification en arrière-plan pour un meilleur remplissage en mode automatique.&lt;br&gt; &lt;font color=#FF0000&gt;keepassA va activer seulement l\\'interface correspondante pour ouvrir la base de données quand vous utilisez la fonction de saisie automatique&lt;/font&gt;</string>\n    <string name=\"setting_miui_background_start\">Paramètres -&gt; Paramètres d\\'application -&gt; Gestion de l\\'application -&gt; keepassA -&gt; Gestion des permissions</string>\n    <string name=\"setting_vivo_background_start\">Paramètres -&gt; Plus de paramètres -&gt; Gestion des permissions -&gt; KeepassA -&gt; Paramètres des permissions -&gt; Notification en arrière-plan</string>\n    <string name=\"no_matching_entry\">Aucune entrée correspondante</string>\n    <string name=\"hint_save_auto_fill\">Lors de l\\'association du nom du paquet【&lt;font color=#FF0000&gt;%s&lt;/font&gt;】avec l\\'entrée【&lt;font color=#FF0000&gt;%s&lt;/font&gt;】</string>\n    <string name=\"relevance_db\">Données liées </string>\n    <string name=\"modify_time\">Date/heure de modification</string>\n    <string name=\"lose_time\">Date/heure d\\'expiration</string>\n    <string name=\"update_group_fail\">Échec de mise à jour des infos du groupe</string>\n    <string name=\"update_group_success\">Infos du groupe mises à jour avec succès</string>\n    <string name=\"title\">Titre</string>\n    <string name=\"feedback\">Donner votre avis ^_^</string>\n    <string name=\"submit\">Soumettre</string>\n    <string name=\"send_email_fail\">Aucune application mail, échec de l\\'envoi du mail</string>\n    <string name=\"feedback_email_msg\">Bonjour, je suis KeepassA, avez-vous des questions ?&lt;br /&gt; &lt;br /&gt; &lt;br /&gt; _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ &lt;br /&gt; Marque de téléphone : %s &lt;br /&gt; Modèle de téléphone : %s &lt;br /&gt; Version du système :%s &lt;br /&gt; Résolution: %s &lt;br /&gt; Version d\\'application: %s </string>\n    <string name=\"mark_not_exit\">Boutique d\\'application inexistante</string>\n    <string name=\"set_open_auto_fill_title\">Activer le remplissage automatique</string>\n    <string name=\"set_key_praise_value\">Évaluer KeepassA</string>\n    <string name=\"other_set\">Autres paramètres</string>\n    <string name=\"open_quick_unlock\">Activer le déverrouillage rapide</string>\n    <string name=\"quick_pass_type_summary\">\"\"</string>\n    <string name=\"clean_clip_time\">Vider le Presse-papier</string>\n    <string name=\"clean_clip_time_summary\">Après avoir copié le mot de passe, vider le Presse-papier après %s</string>\n    <string name=\"auto_lock_db_time\">Verrouiller automatiquement la BD</string>\n    <string name=\"auto_lock_db_time_summary\">Après aucune opération, la base de données est automatiquement verrouillée après %s</string>\n    <string name=\"quick_pass_type\">Emplacement de l\\'interception du MDP court</string>\n    <string name=\"des_quick_unlock\">Déverrouillez votre base de données avec un mot de passe court</string>\n    <string name=\"error_short_pass_lenght\">Veuillez définir le mot de passe court avec 6 caractères ou moins</string>\n    <string name=\"error_short_pass_short\">La longueur du mot de passe court ne peut pas être moins de 3</string>\n    <string name=\"quick_pass_len_title\">Longueur du mot de passe court</string>\n    <string name=\"quick_pass_len_summary\">Longueur du mot de passe court: %s</string>\n    <string name=\"set_key_value\">Choisir une langue</string>\n    <string name=\"dropbox_msg\">Afin de respecter votre vie privée, KeePassA ne demandera pas de permission sur tous les fichiers de votre compte Dropbox, mais &lt;font color = # FF0000&gt; seulement l\\'accès au dossier 【Apps/KeePassA】 &lt;/ font &gt;.&lt;br /&gt; Par conséquent : &lt;br /&gt; &lt;b&gt;Si vous avez déjà une base de données KeePass, vous devez autoriser KeePassA à accéder au dossier 【apps/KeePassA】 dans KeePassA et enregistrer la base de données KeePass dans le dossier 【Application / KeePassA】 lorsque l\\'autorisation est terminée. &lt;/b&gt;&lt;br /&gt;&lt;br /&gt; Si vous créez une nouvelle base de données KeePass, les opérations suivantes ne seront pas nécessaires. Après la création de la base de données, KeePassA créera automatiquement le dossier correspondant et déplacera la base de donnée dans le dossier 【Apps/KeePassA】 de Dropbox.</string>\n    <string name=\"cover\">Contournement</string>\n    <string name=\"file_conflict_msg\">Conflit entre les données cloud et les données locales, sélectionnez la méthode de synchronisation. Les entrées en conflit sont:\\n %s</string>\n    <string name=\"merge\">Fusionner les données</string>\n    <string name=\"sync_db\">Base de données synchronisée</string>\n    <string name=\"net_error\">Anomalie réseau</string>\n    <string name=\"cover_local\">Local</string>\n    <string name=\"cover_cloud\">Cloud</string>\n    <string name=\"auth\">Auth</string>\n    <string name=\"invalid\">Invalide</string>\n    <string name=\"no_file\">Aucun fichier</string>\n    <string name=\"login\">Connexion</string>\n    <string name=\"helper_webdav\">ex: https://dav.xxxx.com/dav/[votre dossier]/xxx.kdbx</string>\n    <string name=\"helper_webdav_dir\">ex: https://dav.xxxx.com/dav/[votre dossier]/</string>\n    <string name=\"hint_webdav_url\">URL du fichier</string>\n    <string name=\"hint_please_input\">Veuillez entrer %s</string>\n    <string name=\"db_file_no_exist\">Le fichier de base de données n\\'existe pas</string>\n    <string name=\"invalid_auth\">L\\'autorisation a échoué, veuillez ré-autoriser</string>\n    <string name=\"afs\">Système de fichiers Android</string>\n    <string name=\"hint_kdbx_name\">Suffixe erroné, le nom de suffixe doit être \".kdbx\"</string>\n    <string name=\"start_upload_db\">Téléchargement de la base de données sur votre disque réseau</string>\n    <string name=\"unlocked\">Déverrouillée</string>\n    <string name=\"notify_channel_db_open\">Notification de déverrouillage</string>\n    <string name=\"notify_quick_unlock_start\">BD verrouillée, déverrouillage rapide activé</string>\n    <string name=\"notify_db_locked\">Base de données verrouillée</string>\n    <string name=\"search\">Recherche</string>\n    <string name=\"disable\">Désactivé</string>\n    <string name=\"quick_unlock\">Déverrouillage rapide</string>\n    <string name=\"db_unlock\">Déverrouiller la BD</string>\n    <string name=\"hint_quick_use_fingerprint\">Utilisez seulement l\\'empreinte pour déverrouiller sur la page de déverrouillage rapide</string>\n    <string name=\"version_log\">Journal de mise à jour</string>\n    <string name=\"hint_finger_print_verfiy\">Veuillez vérifier votre empreinte à nouveau avant d\\'utiliser vos nouveaux paramètres</string>\n    <string name=\"closed\"> désactivé</string>\n    <string name=\"upload\">Téléverser</string>\n    <string name=\"open1\">Activé</string>\n    <string name=\"add_attr_file\">Ajouter des pièces jointes </string>\n    <string name=\"hint_fingerprint_modify\">Vérifiez que l\\'empreinte a changé. Pour la sécurité de votre mot de passe, veuillez réinitialiser la fonction de déverrouillage par empreinte </string>\n    <string name=\"hint_please_open_database\">Veuillez ouvrir la base de données</string>\n    <string name=\"set_delete_setting\">Paramètres de suppression</string>\n    <string name=\"set_delete_no_into_recycle_bin\">Supprime définitivement les entrées de la base de données</string>\n    <string name=\"set_delete_no_into_recycle_bin_des\">Déplace les entrées supprimées vers la corbeille</string>\n    <string name=\"warning_rooted\">Pour la sécurité de vos données, veuillez ne pas ouvrir la base de données sur un téléphone rooté</string>\n    <string name=\"hint_security_green\">\"Super, l\\'application est dans un environnement sans risque \"</string>\n    <string name=\"hint_security_red\">Notez que votre téléphone a été rooté et que la base de données se trouve dans un environnement très dangereux</string>\n    <string name=\"hint_security_yellow\">Attention, fonctionnant dans le simulateur, cet environnement apportera certains risques imprévisibles à la base de données</string>\n    <string name=\"donate\">Faire un don</string>\n    <string name=\"donate_desc\">[KeepassA](https://github.com/AriaLyy/KeepassA) est une application Open Source. Si vous aimez notre application, veuillez considérer une donation pour supporter notre travail. Merci beaucoup !</string>\n    <string name=\"dev_birthday\">Aujourd\\'hui est l\\'anniversaire du développeur 🎂, voudriez-vous que je boive une bouteille de Coca 🎉🎉?</string>\n    <string name=\"totp_defaule\">Paramètres de jeton RFC 6238</string>\n    <string name=\"totp_steam\">Paramètres de jeton Steam</string>\n    <string name=\"totp_custom\">Utiliser des paramètres personnalisés</string>\n    <string name=\"totp_custom_hint\">Paramètres personnalisés </string>\n    <string name=\"totp_Arithmetic\">Arithmétique: </string>\n    <string name=\"totp_time\">Durée(s) : </string>\n    <string name=\"totp_len\">Longueur : </string>\n    <string name=\"totp_key_error\">Échec de la génération du code TOTP, clé anormale</string>\n    <string name=\"sort\">Trier</string>\n    <string name=\"sort_chart_desc\">Première lettre descendante (Z-&gt;A)</string>\n    <string name=\"sort_chart_asc\">Première lettre ascendante (A-&gt;Z)</string>\n    <string name=\"sort_time_asc\">Date ascendante</string>\n    <string name=\"sort_time_desc\">Date descendante</string>\n    <string name=\"ime_label\">Clavier de sécurité KeepassA</string>\n    <string name=\"set_open_kpa_ime_title\">Activer le clavier de sécurité</string>\n    <string name=\"no_totp_token\">L\\'app n\\'a pas défini un jeton TOTP</string>\n    <string name=\"ime_entry_other_info\">Remplir les autres champs</string>\n    <string name=\"url\">URL</string>\n    <string name=\"hint_not_nore_info\">Pas d\\'autres infos</string>\n    <string name=\"error_uri_grant_permission\">Échec d\\'autorisation de l\\'URI</string>\n    <string name=\"license\">Licence Open Source</string>\n    <string name=\"move\">Déplacer</string>\n    <string name=\"set_key_sum_main_show_entries\">Afficher toutes les entrées après déverrouillage de la BD</string>\n    <string name=\"set_key_sum_main_show_history\">Afficher l\\'historique après déverrouillage de la BD</string>\n    <string name=\"set_key_title_main_allow_show_entries\">Paramètres de la page d\\'accueil</string>\n    <string name=\"expand\">développer</string>\n    <string name=\"shrink\">réduire</string>\n    <string name=\"ui_setting\">Paramètres de l\\'interface</string>\n    <string name=\"set_key_title_opr_env_check\">Vérification de l\\'environnement opérationnel</string>\n    <string name=\"set_key_title_opr_env_check_turn_off\">Vérification de l\\'environnement désactivé</string>\n    <string name=\"set_key_title_opr_env_check_turn_on\">Vérification de l\\'environnement activé</string>\n    <string name=\"cur_app_not_autofill\">Pas de remplissage automatique</string>\n    <string name=\"other\">Autre</string>\n    <string name=\"multi_choice\">Choix multiple</string>\n    <string name=\"close\">Fermer</string>\n    <string name=\"open_permissions\">Ouvrir les autorisations</string>\n    <string name=\"set_key_main_show_totp_tab_title\">La page d\\'accueil affiche les catégories TOTP</string>\n    <string name=\"set_key_main_show_totp_tab_on\">Afficher la catégorie TOTP</string>\n    <string name=\"set_key_main_show_totp_tab_off\">Ne pas afficher la catégorie TOTP</string>\n    <string name=\"translate_language\">Traduisez l\\'application dans votre langue</string>\n    <string name=\"error_keystore\">Erreur du trousseau d\\'accès, veuillez réessayer</string>\n    <string name=\"one_drive_hint\">Pour respecter votre vie privée, KeePassA n\\'aura accéder qu\\'au dossier &lt;font color=#FF0000&gt;【application/KeepassA】&lt;/font&gt;. Si vous souhaitez accéder à la base de données depuis OneDrive, &lt;font color=#FF0000&gt;veuillez la stocker dans ce même dossier.&lt;/font&gt;</string>\n    <string name=\"kpa_totp\">Jeton</string>\n    <string name=\"kpa_copy\">Copier</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-ja/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources></resources>"
  },
  {
    "path": "app/src/main/res/values-nb-rNO/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"error_uri\">URI-feil</string>\n    <string name=\"help_input_db_pass\">Skriv inn databasepassord</string>\n    <string name=\"app_setting\">Programinnstillinger</string>\n    <string name=\"db_setting\">Databaseinnstillinger</string>\n    <string name=\"setting\">Innstilling</string>\n    <string name=\"all\">Elementer</string>\n    <string name=\"change_db\">Bytt database</string>\n    <string name=\"history\">Historikk</string>\n    <string name=\"create_file\">Opprett ny fil</string>\n    <string name=\"choose_file\">Velg en fil</string>\n    <string name=\"cancel\">Avbryt</string>\n    <string name=\"done\">Ferdig</string>\n    <string name=\"up\">Forrige</string>\n    <string name=\"next\">Neste steg</string>\n    <string name=\"create_db\">Opprett database</string>\n    <string name=\"db_name\">Skriv inn databasenavn</string>\n    <string name=\"open_db\">Åpne database</string>\n    <string name=\"hint_set_pass_key\">Velg en nøkkel</string>\n    <string name=\"choose_pass_key\">Velg nøkkel</string>\n    <string name=\"encrypt_type_2\">Passord + nøkkel</string>\n    <string name=\"encrypt_type_1\">Kun passord</string>\n    <string name=\"encrypt_type\">Kryptering</string>\n    <string name=\"help_create_group\">Velg et gruppenavn på mindre enn 17 tegn</string>\n    <string name=\"hint_enter_password\">Bekreft passord</string>\n    <string name=\"hint_input_password\">Skriv inn et passord på 6-16 tegn</string>\n    <string name=\"hint_set_password\">Sett passord</string>\n    <string name=\"open\">Lås opp</string>\n    <string name=\"key1\">Hemmelig nøkkel: %s</string>\n    <string name=\"key\">Hemmelig nøkkel</string>\n    <string name=\"password\">Passord</string>\n    <string name=\"db1\">database\\t|\\t&lt;font color=#757575&gt;%s&lt;/font&gt;</string>\n    <string name=\"db\">Database</string>\n    <string name=\"set_delete_no_into_recycle_bin_des\">Flytt slettede elementer til papirkurven</string>\n    <string name=\"set_delete_setting\">Slett innstillinger</string>\n    <string name=\"hint_please_open_database\">Åpne databasen</string>\n    <string name=\"add_attr_file\">Legg til vedlegg</string>\n    <string name=\"open1\">åpen</string>\n    <string name=\"upload\">Last opp</string>\n    <string name=\"closed\">Lukket</string>\n    <string name=\"version_log\">Oppgraderingslogg</string>\n    <string name=\"db_unlock\">Lås opp database</string>\n    <string name=\"quick_unlock\">Hurtigopplåsning</string>\n    <string name=\"disable\">Skru av</string>\n    <string name=\"search\">Søk</string>\n    <string name=\"notify_db_locked\">Databasen er låst</string>\n    <string name=\"unlocked\">Opplåst</string>\n    <string name=\"afs\">Android-filsystem</string>\n    <string name=\"db_file_no_exist\">Databasefilen finnes ikke</string>\n    <string name=\"hint_please_input\">Skriv inn %s</string>\n    <string name=\"hint_webdav_url\">Fil-nettadresse</string>\n    <string name=\"no_file\">Ingen fil</string>\n    <string name=\"invalid\">Ugyldig</string>\n    <string name=\"cover_cloud\">Sky</string>\n    <string name=\"cover_local\">Lokalt</string>\n    <string name=\"sync_db\">Synkron database</string>\n    <string name=\"merge\">Flett</string>\n    <string name=\"set_key_value\">Velg språk</string>\n    <string name=\"auto_lock_db_time_summary\">Auto-lås databasen etter %s hvis ingen handlinger utføres</string>\n    <string name=\"auto_lock_db_time\">Auto-lås databasen</string>\n    <string name=\"clean_clip_time\">Tøm utklippstavle</string>\n    <string name=\"open_quick_unlock\">Hurtigopplåsing</string>\n    <string name=\"other_set\">Andre innstillinger</string>\n    <string name=\"set_key_praise_value\">Vurder KeePassA</string>\n    <string name=\"send_email_fail\">Installer et e-postprogram for å sende e-post først</string>\n    <string name=\"submit\">Send inn</string>\n    <string name=\"feedback\">Tilbakemelding ^_^</string>\n    <string name=\"update_group_success\">Gruppeinfo oppdatert</string>\n    <string name=\"update_group_fail\">Kunne ikke oppdatere gruppeinfo</string>\n    <string name=\"lose_time\">Utløpstid</string>\n    <string name=\"modify_time\">Endre tid</string>\n    <string name=\"autofill_sign_in_prompt\">Auto-fyll passord med KeePassA</string>\n    <string name=\"app_feedback\">Tilbakemelding</string>\n    <string name=\"hint_db_pass_modify\">Databasepassord endret</string>\n    <string name=\"db_pass_no_alter\">Det nye passordet er likt det gamle</string>\n    <string name=\"error_pass\">Passordfeil</string>\n    <string name=\"save_db_success\">Database lagret</string>\n    <string name=\"save_db_fail\">Kunne ikke lagre database</string>\n    <string name=\"txt_viewer\">Filviser</string>\n    <string name=\"save_file_success\">Filen [%s] ble lagret</string>\n    <string name=\"open_whit_other\">Lagre til hurtiglager og åpne med systemprogram</string>\n    <string name=\"open_whit_img\">Åpne med intern bildeviser</string>\n    <string name=\"open_whit_text\">Åpne med intern tekst</string>\n    <string name=\"download_file\">Lagre vedlegg til SD-kort</string>\n    <string name=\"error_attr_file_too_large\">Reduser minnebruk ved å ikke legge til filer større enn 10 MB</string>\n    <string name=\"del_attr_str\">Slett egendefinerte felter</string>\n    <string name=\"del_attr_file\">Slett vedlegg</string>\n    <string name=\"error_attr_str_null\">Feltnavnet er tomt</string>\n    <string name=\"hint_protect_str\">Beskyttet felt</string>\n    <string name=\"hint_input_attr_str_value\">Feltverdi</string>\n    <string name=\"hint_input_attr_str_key\">Feltnavn</string>\n    <string name=\"add_custom_str\">Opprett egendefinerte felter</string>\n    <string name=\"warning\">Advarsel</string>\n    <string name=\"normal_account\">Normal konto</string>\n    <string name=\"choose_dir\">Lagre i nåværende gruppe</string>\n    <string name=\"time\">Tid</string>\n    <string name=\"date\">Dato</string>\n    <string name=\"hint_pass_null\">Passordet er tomt</string>\n    <string name=\"pass_generater\">Passordgenerator</string>\n    <string name=\"special\">Spesialtegn</string>\n    <string name=\"space\">Mellomrom</string>\n    <string name=\"underline\">Understrek</string>\n    <string name=\"minus\">Minus</string>\n    <string name=\"numer\">Tall</string>\n    <string name=\"lower\">Liten bokstav</string>\n    <string name=\"upper\">Stor bokstav</string>\n    <string name=\"len\">Lengde</string>\n    <string name=\"save\">Lagre</string>\n    <string name=\"create_entry\">Opprett oppføring</string>\n    <string name=\"fingerprint_unlock\">Fingeravtrykksopplåsning</string>\n    <string name=\"use_full_fingerprint\">Full fingeravtrykksopplåsning</string>\n    <string name=\"quick_use_fingerprint\">Kun bruk fingeravtrykk for å låse opp i hurtigopplåsningsgrensesnitt</string>\n    <string name=\"ban_fingerprint\">Ingen fingeravtrykksopplåsning</string>\n    <string name=\"verify_finger_fail\">Kunne ikke bekrefte fingeravtrykk</string>\n    <string name=\"verify_finger\">Fingeravtrykksbekreftelse</string>\n    <string name=\"hint_copy_to_clip\">Data kopiert til utklippstavle</string>\n    <string name=\"hide_pass\">Skjul passord</string>\n    <string name=\"ope_url\">Åpne lenke</string>\n    <string name=\"show_pass\">Vis passord</string>\n    <string name=\"copy_to_clip\">Kopier til utklippstavle</string>\n    <string name=\"hint_query\">Brukernavn, tittel, nettadresse, avanserte attributter …</string>\n    <string name=\"no_record\">Ingen oppføring</string>\n    <string name=\"db_name_modify\">Databasenavn endret</string>\n    <string name=\"db_name_no_alter\">Databasenavnet har ikke blitt endret</string>\n    <string name=\"out_db\">Eksporter database</string>\n    <string name=\"modify_db_pass\">Endre databasepassord</string>\n    <string name=\"modify_db_name\">Endre databasenavnet</string>\n    <string name=\"db_handle\">Databaseoperasjoner</string>\n    <string name=\"safety_set\">Sikkerhetsinnstillinger</string>\n    <string name=\"create_totp\">Generer OTP-symbol</string>\n    <string name=\"hint_copy_totp\">Kopierte TOTP-symbol til utklippstavle</string>\n    <string name=\"hint_copy_pass\">Passord kopiert til utklippstavle</string>\n    <string name=\"hint_copy_user\">Brukernavn kopiert til utklippstavle</string>\n    <string name=\"undo_entryed\">Elementet ble flyttet til angitt sted</string>\n    <string name=\"undo_grouped\">Gruppen ble flyttet til angitt sted</string>\n    <string name=\"undo_to\">Flytt til denne gruppen</string>\n    <string name=\"undo_entry\">Gjenopptatt …</string>\n    <string name=\"choose_icon\">Velg ikon</string>\n    <string name=\"title_too_long\">Gruppenavnet er for langt</string>\n    <string name=\"error_group_no_modify\">Ingen gruppeendringer</string>\n    <string name=\"modify_group\">Rediger gruppe</string>\n    <string name=\"create_group_success\">Gruppe bygd</string>\n    <string name=\"create_group_fail\">Kunne ikke bygge gruppe</string>\n    <string name=\"group_name\">Gruppenavn</string>\n    <string name=\"copy_totp\">Kopier TOTP-symbol</string>\n    <string name=\"copy_password\">Kopier passord</string>\n    <string name=\"del_history\">Slett historikkoppføring</string>\n    <string name=\"copy_user_name\">Kopier brukernavn</string>\n    <string name=\"del_entry\">Slett element</string>\n    <string name=\"fail\">Mislykket</string>\n    <string name=\"success\">Vellykket</string>\n    <string name=\"edit_group\">Rediger gruppe</string>\n    <string name=\"del_group\">Slett gruppe</string>\n    <string name=\"hint_ex_property\">Ekstra atributter</string>\n    <string name=\"attachment\">Vedlegg</string>\n    <string name=\"hint_attr\">Avanserte egenskaper</string>\n    <string name=\"expire_time\">Utløper %s</string>\n    <string name=\"create_pass_key_file_des\">Opprett en ny fil å bruke som din nøkkel</string>\n    <string name=\"error_db_name_null\">Skriv inn et databasenavn</string>\n    <string name=\"hint_select_path_type\">Velg hvor du vil lagre databasen din</string>\n    <string name=\"hint_select_db_path\">Klikk for å sette datalagringsmetoden</string>\n    <string name=\"helper_create_pass\">Blandet pass</string>\n    <string name=\"pass_key\">Filnøkkel</string>\n    <string name=\"help_create_db\">Opprett v4-KeePass-database som forvalg</string>\n    <string name=\"feedback_email_msg\">Hei, jeg er KeePassA, har du noen spørsmål\\?<br/> <br/> <br/> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ <br/> Mobiltelefonmerker: %s <br/> Telefonmodell: %s <br/> Systemversjon:%s <br/> Oppløsning: %s <br/> Programversjon: %s</string>\n    <string name=\"error_db_pass_too_short\">Bruk mer enn 6 tegn for databasepassordet for å beskytte din data</string>\n    <string name=\"hint_db_pass_modify_success\">Bruk det nye databasepassordet for å åpne databasen neste gang</string>\n    <string name=\"create_entry_no_save\">Forlat nåværende side uten å lagre oppføringen\\?</string>\n    <string name=\"expire\">Utløpte &lt;font color=#FF0000&gt;%s&lt;/font&gt;</string>\n    <string name=\"error_open_db\">Kunne ikke åpne databasen. Er passordet feil, eller trengs det en nøkkel\\?</string>\n    <string name=\"need_key\">Trenger du en nøkkel\\?</string>\n    <string name=\"error_pass_null\">Sett et databasepassord</string>\n    <string name=\"totp_len\">Lengde:</string>\n    <string name=\"set_key_title_auto_lock_database\">Lås databasen automatisk</string>\n    <string name=\"set_key_title_auto_lock_database_turn_on\">Ja</string>\n    <string name=\"set_key_title_auto_lock_database_turn_off\">Nei</string>\n    <string name=\"ui_setting\">Grensesnittsinnstillinger</string>\n    <string name=\"shrink\">Krymp</string>\n    <string name=\"expand\">Utvid</string>\n    <string name=\"move\">Flytt</string>\n    <string name=\"license\">Friprog-lisens</string>\n    <string name=\"url\">Nettadresse</string>\n    <string name=\"no_totp_token\">Programmet har ikke et TOTP-symbol</string>\n    <string name=\"set_open_kpa_ime_title\">Sikkerhetstastatur</string>\n    <string name=\"ime_label\">KeePassA-sikkerhetstastatur</string>\n    <string name=\"hint_input_user_name\">Brukernavn</string>\n    <string name=\"error_group_name_null\">Gruppenavnet er tomt</string>\n    <string name=\"error_open_db_algorithm_error\">Ugyldig algoritme</string>\n    <string name=\"error_open_db_signature_error\">Ugyldig databasesignatur</string>\n    <string name=\"error_open_db_version_error\">Ugyldig databaseversjon</string>\n    <string name=\"error_open_db_key_invalid\">Ugyldig nøkkel</string>\n    <string name=\"error_open_db_pass_error\">Ugyldig passord</string>\n    <string name=\"error_open_db_key_empty\">Manglende nøkkel</string>\n    <string name=\"error_input_pass_null\">Skriv inn databasepassordet</string>\n    <string name=\"edit\">Rediger</string>\n    <string name=\"error_enter_pass_null\">Skriv inn bekreftelsespassordet</string>\n    <string name=\"set_open_auto_fill_title\">Autoutfyllingstjeneste</string>\n    <string name=\"add_more\">Legg til flere</string>\n    <string name=\"error_genera_params\">Velg minst én passordgenereringstype</string>\n    <string name=\"hint_input_cover_url\">Overskriv lenke</string>\n    <string name=\"open_permissions\">Åpne tilganger</string>\n    <string name=\"miui_permissions\">MIUI-systemet trenger tilgang til å endre bakgrunnsoppsprettsgrensesnittet for å kunne bruke auto-fyllingsfuksjonen.</string>\n    <string name=\"set_key_title_show_state_bar\">Statusfelt</string>\n    <string name=\"customize\">Tilpass</string>\n    <string name=\"enter\">Enter</string>\n    <string name=\"hint\">Merknad</string>\n    <string name=\"help_pass_type\">Kun bruk passord:\n\\n Kun passordet ditt trengs for å åpne databsen ;\n\\n Passord+nøkkel\n\\n Du må skrive inn et passord og velge en nøkkelfil for å åpne databasen.</string>\n    <string name=\"choose_pass_key_file_des\">Velg vilkårlig fil å bruke som nøkkel</string>\n    <string name=\"help_create_db_path\">Det anbefales at du bruker sky-tjenester som Dropbox og WebDAV. Dette synkroniserer databasen din til skyen automatisk, slik at det blir lett å bruke databasen på andre tjenester.\n\\nDetabasen er kryptert med AES-256. Uten passordet ditt kan ingen åpne databasen din.</string>\n    <string name=\"create_pass_key_success\">Opprettet nøkkel [%s]</string>\n    <string name=\"error_pass_unfit\">Passordene samsvarer ikke</string>\n    <string name=\"hint_db_create_success\">Opprettet database [%s]</string>\n    <string name=\"history_record\">Historikkloggføring</string>\n    <string name=\"welcome\">Velkommen</string>\n    <string name=\"hint_group_desc\">Elementer for %s-gruppe</string>\n    <string name=\"error_open_db_arcfour_error\">Ugyldig algoritme for kryptering av strøm</string>\n    <string name=\"error_group_id_null\">gruppe-ID har ingen verdi</string>\n    <string name=\"create_time\">Bygd på %s</string>\n    <string name=\"error_entry_id_null\">oppførings-ID har ingen verdi</string>\n    <string name=\"hint_del_group\">Slett【&lt;font color=#FF0000&gt;%s&lt;/font&gt;】-gruppen\\?&lt;br&gt;Dette flytter den til papirkurven, hvorfra du kan gjenopprette den senere.</string>\n    <string name=\"hint_del_group_no_recycle\">Slett 【&lt;font color=#FF0000&gt;%s&lt;/font&gt;】-gruppen\\?</string>\n    <string name=\"hint_del_entry\">Slett 【&lt;font color=#FF0000&gt;%s&lt;/font&gt;】-elementet\\? &lt;br&gt; Dette flytter den til papirkurven, hvorfra du kan gjenopprette den senere.</string>\n    <string name=\"hint_del_entry_no_recycle\">Slett 【&lt;font color=#FF0000&gt;%s&lt;/font&gt;】-oppføringen for godt\\?</string>\n    <string name=\"create_group\">Bygg gruppe</string>\n    <string name=\"hint_quick_unlock\">Skriv inn de siste %s sifrene av ditt databasepassord</string>\n    <string name=\"hint_input_title\">Tittel</string>\n    <string name=\"hint_input_url\">Lenke-adresse</string>\n    <string name=\"notice\">Notis</string>\n    <string name=\"hint_input_tag\">Etikett1, etikett2</string>\n    <string name=\"bracket\">Parentes</string>\n    <string name=\"no_matching_entry\">Ingen samsvarende elementer</string>\n    <string name=\"relevance_db\">Lenket data</string>\n    <string name=\"hint_save_auto_fill\">Hvorvidt pakkenavnet【&lt;font color=#FF0000&gt;%s&lt;/font&gt;】skal tilknyttes oppføringen【&lt;font color=#FF0000&gt;%s&lt;/font&gt;】</string>\n    <string name=\"mark_not_exit\">Programmarked finnes ikke</string>\n    <string name=\"title\">Tittel</string>\n    <string name=\"clean_clip_time_summary\">Tøm utklippstavle etter %s, når passordet er kopiert</string>\n    <string name=\"quick_pass_len_title\">Kort passordslengde</string>\n    <string name=\"quick_pass_len_summary\">Kort passordslengde: %s</string>\n    <string name=\"des_quick_unlock\">Lås opp databasen din med et kort passord</string>\n    <string name=\"error_short_pass_lenght\">Lag et kort passord på 6 tegn eller mindre</string>\n    <string name=\"quick_pass_type\">Hvor skal det korte passordet vises</string>\n    <string name=\"error_short_pass_short\">Lag et kort passord på 3 tegn eller mindre</string>\n    <string name=\"cover\">Omslag</string>\n    <string name=\"file_conflict_msg\">Konflikt mellom lokal- og data lagret i skyen. Velg løsningsmetode. De forskjellige oppføringene er:\n\\n %s</string>\n    <string name=\"login\">Innlogging</string>\n    <string name=\"net_error\">Unormalt nettverk</string>\n    <string name=\"auth\">Identitetsbekreftelse</string>\n    <string name=\"helper_webdav\">f.eks: https://dav.example.org/dav/[din mappe]/xxx.kdbx</string>\n    <string name=\"helper_webdav_dir\">f.eks: https://dav.example.org/dav/[din mappe]/</string>\n    <string name=\"invalid_auth\">Kunne ikke identitetsbekrefte. Prøv igjen.</string>\n    <string name=\"start_upload_db\">Begynn å laste opp databasen til din nettverksdisk</string>\n    <string name=\"notify_channel_db_open\">Merknad om opplåsing av database</string>\n    <string name=\"hint_kdbx_name\">Feil endelsesnavn. Det skal være .kdbx</string>\n    <string name=\"notify_quick_unlock_start\">Databasen er låst. Hurtiglåsing er påslått.</string>\n    <string name=\"hint_quick_use_fingerprint\">Kun bruk fingeravtrykk til opplåsing på hurtigopplåsingssiden</string>\n    <string name=\"hint_finger_print_verfiy\">Bekreft fingeravtrykkene igjen for å bruke de nye innstillingene</string>\n    <string name=\"set_delete_no_into_recycle_bin\">Slett oppføringer fra databasen for godt.</string>\n    <string name=\"hint_fingerprint_modify\">Sjekk at fingeravtrykket har endret seg. Tilbakestill funksjonen for opplåsing av fingeravtrykk for egen sikkerhet.</string>\n    <string name=\"warning_rooted\">Ikke åpne databasen din på en telefon med rot-tilgang. Da forblir den trygg.</string>\n    <string name=\"hint_security_green\">\"Dette programmet er i et trygt miljø \"</string>\n    <string name=\"hint_security_yellow\">Advarsel. Kjører i simulatoren. Miljøet medfører uforutsigbar risiko for databasen.</string>\n    <string name=\"donate\">Doner</string>\n    <string name=\"dev_birthday\">I dag er det utviklerens geburtsdag🎂. Vil du sende en skål til gudene\\?</string>\n    <string name=\"donate_desc\">[KeePassA](https://github.com/AriaLyy/KeepassA) er fri programvare. Doner for å støtte arbeidet. Takk skal du ha.</string>\n    <string name=\"totp_defaule\">Forvalgte innstillinger for RFC 6238-symbol</string>\n    <string name=\"totp_custom\">Bruk egendefinerte innstillinger</string>\n    <string name=\"totp_custom_hint\">Egendefinerte innstillinger</string>\n    <string name=\"totp_steam\">Innstillinger for Steam-symbol</string>\n    <string name=\"totp_Arithmetic\">Algoritme:</string>\n    <string name=\"totp_time\">Tid(sek):</string>\n    <string name=\"sort\">Sortering</string>\n    <string name=\"sort_time_asc\">Tid nyligst-senest</string>\n    <string name=\"sort_time_desc\">Tid senest-nyligst</string>\n    <string name=\"ime_entry_other_info\">Fyll inn alle feltene</string>\n    <string name=\"hint_not_nore_info\">Ingen videre info</string>\n    <string name=\"error_uri_grant_permission\">URI-bekreftelse mislyktes</string>\n    <string name=\"set_key_title_main_allow_show_entries\">Hjemmeside</string>\n    <string name=\"set_key_title_opr_env_check_turn_off\">Av</string>\n    <string name=\"set_key_title_opr_env_check_turn_on\">På</string>\n    <string name=\"set_key_title_opr_env_check\">Sjekk av kjøringsmiljø</string>\n    <string name=\"other\">Annet</string>\n    <string name=\"ref_entry\">Referanseoppføring</string>\n    <string name=\"multi_choice\">Flervalg</string>\n    <string name=\"cur_app_not_autofill\">Ingen automatisk utfylling</string>\n    <string name=\"set_summary_screen_lock_off\">Låser ikke databasen</string>\n    <string name=\"close\">Lukk</string>\n    <string name=\"Alipay\">Apipay</string>\n    <string name=\"error_webdav_end_suffix\">Bruk mappestien. F.eks: «https://example.org/dav/kpa/»</string>\n    <string name=\"set_key_title_fillet_bg_icon\">Avrundede hjørner for listeikoner</string>\n    <string name=\"error_webdav_end_dav_suffix\">/dav/sti tillates ikke. Bruk en gyldig sti, som f.eks. «https://example.org/dav/kpa/»</string>\n    <string name=\"set_key_main_show_totp_tab_title\">Hjemmesiden viser TOTP-kategori</string>\n    <string name=\"set_key_main_show_totp_tab_on\">Vis TOTP-kategori</string>\n    <string name=\"set_key_main_show_totp_tab_off\">Ikke vis TOTP-kategori</string>\n    <string name=\"setting_vivo_background_start\">Innstillinger → Flere innstillinger → Tilgangshåndtering → KeePassA → Tilgangsinnstillinger → Bakgrunnsoppsprett</string>\n    <string name=\"set_key_title_fillet_bg_icon_turn_on\">På</string>\n    <string name=\"set_key_sum_main_show_history\">Vis historikk etter opplåsing av databasen</string>\n    <string name=\"totp_key_error\">Kunne ikke generere en TOTP-kode. Uvanlig nøkkel.</string>\n    <string name=\"set_key_sum_main_show_entries\">Vis alle elementer etter opplåsing av databasen</string>\n    <string name=\"set_key_title_close_load_anim\">Laster inn animasjon …</string>\n    <string name=\"set_key_title_close_load_anim_turn_on\">På</string>\n    <string name=\"set_key_title_close_load_anim_turn_off\">Av</string>\n    <string name=\"s_default\">Forvalg</string>\n    <string name=\"translate_language\">Bistå oversettelsen</string>\n    <string name=\"set_summary_screen_lock_on\">Låser databasen automatisk</string>\n    <string name=\"set_title_screen_lock\">Skjermlås</string>\n    <string name=\"set_key_title_show_state_bar_turn_on\">Vis</string>\n    <string name=\"set_key_title_show_state_bar_turn_off\">Skjul</string>\n    <string name=\"hint_security_red\">Enheten din har rot-tilgang, og databasen er derfor i et veldig farlig miljø</string>\n    <string name=\"error_keystore\">Nøkkelknippe-unntak. Prøv igjen.</string>\n    <string name=\"get_token_fail\">Klarte ikke å hente symbol</string>\n    <string name=\"set_key_title_fillet_bg_icon_turn_off\">Av</string>\n    <string name=\"set_key_theme_style_title\">Draktstil</string>\n    <string name=\"set_key_theme_style_summary\">System</string>\n    <string name=\"setting_miui_background_start\">Innstillinger → Programinnstilinger → Programhåndtering → KeePassA → Tilgangshåndtering</string>\n    <string name=\"help_pass_key\">En nøkkelfil tilsvarer en nøkkel for åpning av data, og er vanligvis mer kompleks og pålitelig enn et passord. Den er også vanskeligere å skjule. Hvis du har databasen i skyen, må du unngå å legge nøkkelen med databasen der.\n\\n Du kan velge vilkårlig fil til åpning av databasen. Et bilde, eller en TXT-fil for eksempel.\n\\n⚠️ Merk: Hvis dette bildet eller TXT-dokumentet endres, vil du ikke kunne åpne databasen!\n\\n Det anbefales sterkt å kun bruke skrivebeskyttede filer som nøkler.</string>\n    <string name=\"one_drive_init_failure\">Kunne ikke starte OneDrive</string>\n    <string name=\"sort_chart_asc\">Første bokstav stigende (A-Z)</string>\n    <string name=\"sort_chart_desc\">Første bokstav synkende (Å-A)</string>\n    <string name=\"one_drive_hint\">For å beskytte personvernet vil KeePassA kun bruke &lt;font color=#FF0000&gt;【application/KeepassA】&lt;/font&gt;-mappen. Hvis du vil lese KeePass-databasen til OneDrive, &lt;font color=#FF0000&gt;må du lagre databasen i denne mappen.&lt;/font&gt;</string>\n    <string name=\"hint_background_start\">Gå til &lt;font color=#FF0000&gt;【%s】&lt;/font&gt; og innvilg bakgrunnsoppsprettet for å gjøre bedre bruk av auto-utfyllingstjenesten.&lt;br&gt; &lt;font color=#FF0000&gt;KeePassA viser tilhørende grensesnitt for åpning av databasen når du bruker funksjon for automatisk utfylling.&lt;/font&gt;</string>\n    <string name=\"hint_full_fingerprint\">Fingeravtrykket brukes til å låse opp databasen i grensesnittene for hurtigopplåsing og databaseopplåsing. KeePassA krypterer og lagrer hovedpassordet for databasen og nøkkelinfo i det lokale nøkkelknippet til Android. Identitetsbekreftelse med fingeravtrykk brukes for beskyttelse Kun ditt fingeravtrykk kan låse opp databasen.</string>\n    <string name=\"dropbox_msg\">For bedret personvern spør ikke KeePassA om tilgang til alle filene i din Dropbox, kun &lt;font color = # FF0000&gt; tilgang til 【Apps/KeePassA】-mappen&lt;/ font &gt;.&lt;br /&gt;Derfor:&lt;br /&gt; &lt;b&gt;Hvis du allerede har en KeePass-database innvilger du tilgagn til 【Apps/KeePassA】-mappen i 【Application/KeePassA】 -mappen etter at identitetsbekreftelsen er fullført.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt; Hvis du oppretter en ny KeePass-database kan du se bort fra den instruksen. Etter at du har laget databasen lager KeePassA en tilsvarende mappe og laster opp databasen til 【Apps/KeePassA】-mappen i Dropbox.</string>\n    <string name=\"kpa_totp\">Symbol</string>\n    <string name=\"kpa_copy\">Kopier</string>\n    <string name=\"error_qr_code_str\">Denne QR-koden er unormal. Forsikre deg om at den støttes av i OTP-programmer.</string>\n    <string name=\"disposable\">Én gang</string>\n    <string name=\"monthly\">Månedlig</string>\n    <string name=\"thank_donate\">Takk for din donasjon.</string>\n    <string name=\"google_play\">Google Play</string>\n    <string name=\"error_connect_play\">Klarte ikke å koble til Google-tjeneste</string>\n    <string name=\"year\">Årlig</string>\n    <string name=\"error_donate\">Donasjon mislyktes</string>\n    <string name=\"my_collection\">Min samling</string>\n    <string name=\"current_collection_num\">%1$s samlinger</string>\n    <string name=\"one_drive_load_user_failure\">Klarte ikke å laste inn brukerinfo.</string>\n    <string name=\"webdav_port_hint\">Port</string>\n    <string name=\"error_is_root\">Allerede rot-mappen</string>\n    <string name=\"helper_webdav_service\">Velg WebDAV-tjeneste</string>\n    <string name=\"select_cur_path\">Velg mappen</string>\n    <string name=\"hint_cloud_file_already_exist\">%1$s finnes allerede\\? Overskriv\\?</string>\n    <string name=\"no_collection\">Ingen samling</string>\n    <string name=\"hostname\">Vertsnavn</string>\n    <string name=\"error_open_db_webdav\">Klarte ikke å åpne databasen. WebDAV filen finnes ikke.</string>\n    <string name=\"select_save_path\">Velg en lagringsti</string>\n    <string name=\"port\">Port</string>\n    <string name=\"webdav_host_name_hint\">Skriv inn vertsnavnet ditt.</string>\n    <string name=\"hint_open_background_start\">Innvilg tilgang til å løse ut i bakgrunnen for å fylle ut automatisk</string>\n    <string name=\"webdav_port_name_null\">Vertsnavn må fylles ut</string>\n    <string name=\"privacy_agreement\">Personvernssamtykke</string>\n    <string name=\"ime_hint_open_auto_fill\">Skru på autoinnfyllingstjenesten</string>\n    <string name=\"invalid_img\">Ugyldig bilde</string>\n    <string name=\"please_open_proxy\">Åpne mellomtjeneren først</string>\n    <string name=\"open_setting\">Innstilling</string>\n    <string name=\"preemptive\">Forhåndsutfylling</string>\n    <string name=\"hint_open_backgroun_start\">Du har satt opp forhåndsutfylling, men KeePassA mangler tilstrekkelige tilganger til det. Godkjenn oppsprettsvinduet fra KeePassA-bakenden på innstillingssiden.</string>\n</resources>"
  },
  {
    "path": "app/src/main/res/values-night/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n  <color name=\"black\">#FFFFFF</color>\n  <color name=\"text_black_color\">#C9C4CE</color>\n  <color name=\"background_color\">#2E2F2F</color>\n  <color name=\"color_icon_grey\">#928F98</color>\n  <color name=\"white\">#2E2F2F</color>\n  <color name=\"color_FFFFFF\">#2E2F2F</color>\n  <color name=\"color_33666666\">#33F1F1F1</color>\n</resources>"
  },
  {
    "path": "app/src/main/res/values-nl/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"db\">Database</string>\n</resources>"
  },
  {
    "path": "app/src/main/res/values-pl/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"db\">Baza danych</string>\n    <string name=\"key1\">Tajny klucz: %s</string>\n    <string name=\"error_db_name_null\">Proszę wprowadzić nazwę bazy danych</string>\n    <string name=\"hint_select_path_type\">Wybierz gdzie zapisać Twoją bazę danych</string>\n    <string name=\"hint_select_db_path\">Kliknij by ustawić sposób zapisu danych</string>\n    <string name=\"up\">Wstecz</string>\n    <string name=\"next\">Następny krok</string>\n    <string name=\"create_db\">Stwórz bazę danych</string>\n    <string name=\"db_name\">Wpisz nazwę bazy danych</string>\n    <string name=\"open_db\">Otwórz bazę danych</string>\n    <string name=\"pass_key\">Klucz w pliku</string>\n    <string name=\"hint_set_pass_key\">Proszę wybrać klucz</string>\n    <string name=\"choose_pass_key\">Wybierz klucz</string>\n    <string name=\"encrypt_type_2\">Hasło + klucz</string>\n    <string name=\"encrypt_type_1\">Tylko hasło</string>\n    <string name=\"encrypt_type\">Szyfrowanie</string>\n    <string name=\"hint_enter_password\">Potwierdź hasło</string>\n    <string name=\"hint_set_password\">Ustaw hasło</string>\n    <string name=\"open\">Odblokuj</string>\n    <string name=\"key\">Tajny klucz</string>\n    <string name=\"db1\">baza danych\\t|\\t&lt;font color=#757575&gt;%s&lt;/font&gt;</string>\n    <string name=\"no_totp_token\">Ta aplikacja nie posiada tokenu TOTP</string>\n    <string name=\"sort\">Sortuj</string>\n    <string name=\"totp_len\">Długość:</string>\n    <string name=\"totp_custom_hint\">Własne ustawienia</string>\n    <string name=\"totp_custom\">Użyj własnych ustawień</string>\n    <string name=\"donate\">Podaruj</string>\n    <string name=\"set_delete_setting\">Usuń ustawienia</string>\n    <string name=\"hint_please_open_database\">Proszę otwórz bazę danych</string>\n    <string name=\"add_attr_file\">Dodaj załączniki</string>\n    <string name=\"open1\">otwórz</string>\n    <string name=\"db_unlock\">Odblokuj bazę danych</string>\n    <string name=\"quick_unlock\">Szybkie odblokowanie</string>\n    <string name=\"disable\">Wyłącz</string>\n    <string name=\"search\">Wyszukaj</string>\n    <string name=\"notify_db_locked\">Baza danych jest zablokowana</string>\n    <string name=\"notify_quick_unlock_start\">Baza danych zablokowana, szybkie odblokowanie włączone</string>\n    <string name=\"invalid_auth\">Nie można autoryzować. Spróbuj ponownie.</string>\n    <string name=\"db_file_no_exist\">Plik bazy danych nie istnieje</string>\n    <string name=\"hint_please_input\">Wprowadź %s</string>\n    <string name=\"login\">Login</string>\n    <string name=\"sync_db\">Synchroniczna baza danych</string>\n    <string name=\"merge\">Połącz</string>\n    <string name=\"set_key_value\">Wybierz język</string>\n    <string name=\"quick_pass_len_summary\">Długość krótkiego hasła: %s</string>\n    <string name=\"quick_pass_len_title\">Długość krótkiego hasła</string>\n    <string name=\"error_short_pass_short\">Stwórz swoje krótkie hasło z 3 lub więcej znakami</string>\n    <string name=\"error_short_pass_lenght\">Stwórz swoje krótkie hasło z 6 lub mniejszą ilością znaków</string>\n    <string name=\"des_quick_unlock\">Odblokuj bazę danych za pomocą krótkiego hasła</string>\n    <string name=\"auto_lock_db_time\">Automatycznie zablokuj bazę danych</string>\n    <string name=\"clean_clip_time_summary\">Po skopiowaniu hasła, wyczyść schowek po %s</string>\n    <string name=\"clean_clip_time\">Pusty schowek</string>\n    <string name=\"open_quick_unlock\">Szybkie odblokowanie</string>\n    <string name=\"other_set\">Inne ustawienia</string>\n    <string name=\"set_key_praise_value\">Oceń KeePassA</string>\n    <string name=\"feedback_email_msg\">Witaj, jestem KeePassA, czy masz jakieś pytania\\?<br/> <br/> <br/> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ <br/> Marka: %s <br/> Model: %s <br/> Wersja systemu:%s <br/> Rozdzielczość: %s <br/> Wersja aplikacji: %s</string>\n    <string name=\"submit\">Zgłoś</string>\n    <string name=\"title\">Tytuł</string>\n    <string name=\"lose_time\">Data ważności</string>\n    <string name=\"modify_time\">Czas modyfikacji</string>\n    <string name=\"autofill_sign_in_prompt\">Automatycznie uzupełnij hasło z KeePassA</string>\n    <string name=\"hint_db_pass_modify\">Hasło bazy danych zostało zmienione</string>\n    <string name=\"hint_db_pass_modify_success\">Użyj nowego hasła bazy danych aby następnym razem otworzyć bazę danych</string>\n    <string name=\"db_pass_no_alter\">Nowe hasło jest takie same jak stare</string>\n    <string name=\"error_pass\">Błąd hasła</string>\n    <string name=\"save_db_success\">Baza danych została zapisana</string>\n    <string name=\"save_db_fail\">Nie można zapisać bazy danych</string>\n    <string name=\"open_permissions\">Otwórz uprawnienia</string>\n    <string name=\"hint_input_user_name\">Nazwa użytkownika</string>\n    <string name=\"hint_input_title\">Tytuł</string>\n    <string name=\"hint_quick_unlock\">Wprowadź ostatnie %s cyfry hasła Twojej bazy danych</string>\n    <string name=\"ban_fingerprint\">Nie odblokowuj odciskiem palca</string>\n    <string name=\"verify_finger_fail\">Nie udało się zweryfikować odcisku palca</string>\n    <string name=\"verify_finger\">Weryfikacja odcisku palca</string>\n    <string name=\"hint_copy_to_clip\">Dane skopiowane do schowka</string>\n    <string name=\"hide_pass\">Schowaj hasło</string>\n    <string name=\"ope_url\">Otwórz link</string>\n    <string name=\"show_pass\">Pokaż hasło</string>\n    <string name=\"copy_to_clip\">Skopiuj do schowka</string>\n    <string name=\"db_name_modify\">Nazwa bazy danych została zmieniona</string>\n    <string name=\"db_name_no_alter\">Nazwa bazy danych nie została zmieniona</string>\n    <string name=\"out_db\">Eksportuj bazę danych</string>\n    <string name=\"modify_db_pass\">Zmień hasło bazy danych</string>\n    <string name=\"modify_db_name\">Modyfikuj nazwę bazy danych</string>\n    <string name=\"db_handle\">Operacje bazy danych</string>\n    <string name=\"fingerprint_unlock\">Odblokowanie odciskiem palca</string>\n    <string name=\"safety_set\">Ustawienia bezpieczeństwa</string>\n    <string name=\"create_totp\">Wygeneruj token OTP</string>\n    <string name=\"hint_copy_totp\">Token TOTP skopiowany do schowka</string>\n    <string name=\"hint_copy_pass\">Hasło skopiowane do schowka</string>\n    <string name=\"hint_copy_user\">Nazwa użytkownika skopiowana do schowka</string>\n    <string name=\"undo_grouped\">Grupa została przeniesiona do wybranej lokalizacji</string>\n    <string name=\"undo_to\">Przenieś do tej grupy</string>\n    <string name=\"choose_icon\">Wybierz ikonę</string>\n    <string name=\"title_too_long\">Nazwa grupy jest zbyt długa</string>\n    <string name=\"modify_group\">Edytuj grupę</string>\n    <string name=\"create_group_success\">Grupa stworzona</string>\n    <string name=\"create_group_fail\">Nie można stworzyć grupy</string>\n    <string name=\"error_group_name_null\">Nazwa grupy jest pusta</string>\n    <string name=\"create_group\">Zbuduj grupę</string>\n    <string name=\"group_name\">Nazwa grupy</string>\n    <string name=\"copy_password\">Kopiuj hasło</string>\n    <string name=\"copy_user_name\">Kopiuj nazwę użytkownika</string>\n    <string name=\"success\">Sukces</string>\n    <string name=\"edit_group\">Edytuj grupę</string>\n    <string name=\"del_group\">Usuń grupę</string>\n    <string name=\"hint_ex_property\">Dodatkowe atrybuty</string>\n    <string name=\"attachment\">Załącznik</string>\n    <string name=\"hint_attr\">Zaawansowane właściwości</string>\n    <string name=\"expire_time\">Wygasa w %s</string>\n    <string name=\"create_time\">Stworzony w %s</string>\n    <string name=\"error_entry_id_null\">ID wpisu nie ma wartości</string>\n    <string name=\"error_group_id_null\">ID grupy nie ma wartości</string>\n    <string name=\"error_open_db_algorithm_error\">Niepoprawny algorytm</string>\n    <string name=\"error_open_db_signature_error\">Niepoprawna sygnatura bazy danych</string>\n    <string name=\"error_open_db_version_error\">Niepoprawna wersja bazy danych</string>\n    <string name=\"error_open_db_key_invalid\">Niepoprawny klucz</string>\n    <string name=\"error_open_db_pass_error\">Niepoprawne hasło</string>\n    <string name=\"error_open_db_key_empty\">Brakuje klucza</string>\n    <string name=\"error_input_pass_null\">Wprowadź hasło bazy danych</string>\n    <string name=\"error_uri\">Błąd URI</string>\n    <string name=\"need_key\">Potrzebujesz klucza\\?</string>\n    <string name=\"help_input_db_pass\">Wprowadź hasło bazy danych</string>\n    <string name=\"welcome\">Witaj</string>\n    <string name=\"app_setting\">Ustawienia aplikacji</string>\n    <string name=\"db_setting\">Ustawienia bazy danych</string>\n    <string name=\"change_db\">Zmień bazę danych</string>\n    <string name=\"edit\">Edytuj</string>\n    <string name=\"history\">Historia</string>\n    <string name=\"hint_db_create_success\">Baza danych [%s] została stworzona</string>\n    <string name=\"create_pass_key_success\">Stworzenie klucza [%s] powiodło się</string>\n    <string name=\"helper_create_pass\">Rekomendujemy mieszane hasło (wielkie i małe litery + cyfry + symbole)</string>\n    <string name=\"password\">Hasło</string>\n    <string name=\"hint_input_password\">Wpisz hasło o długości 6-16 znaków</string>\n    <string name=\"help_create_db\">Domyślnie utwórz bazę KeepPass v4</string>\n    <string name=\"done\">Gotowe</string>\n    <string name=\"cancel\">Anuluj</string>\n    <string name=\"hint\">Ostrzeżenie</string>\n    <string name=\"enter\">Gotowe</string>\n    <string name=\"help_create_group\">Wybierz nazwę grupy o długości 16 lub więcej znaków</string>\n    <string name=\"help_create_db_path\">Zaleca się korzystanie ze ścieżek do chmur takich jak Dropbox i WebDAV. Spowoduje to automatyczną synchronizację Twojej bazy danych do chmury, co ułatwi korzystanie z bazy danych na innych urządzeniach.\n\\nBaza danych jest zaszyfrowana za pomocą AES-256. Bez Twojego hasła nikt nie może otworzyć Twojej bazy danych.</string>\n    <string name=\"help_pass_type\">Samo hasło:\n\\n Potrzebujesz jedynie swojego hasła aby otworzyć bazę danych;\n\\n Hasło + klucz:\n\\n Musisz najpierw wprowadzić hasło i wybrać plik z kluczem aby otworzyć bazę danych.</string>\n    <string name=\"choose_file\">Wybierz plik</string>\n    <string name=\"create_file\">Stwórz nowy plik</string>\n    <string name=\"create_pass_key_file_des\">Stwórz nowy plik przeznaczony do bycia kluczem</string>\n    <string name=\"choose_pass_key_file_des\">Wybierz dowolny plik przeznaczony do bycia kluczem</string>\n    <string name=\"help_pass_key\">Plik-klucz jest tym samym co zwyczajny klucz, tym samym będąc o wiele bardziej złożoną i stabliną formą uwierzytelniania niż hasło. Jest on również o wiele trudniejszy do ukrycia. Jeżeli twoja baza danych jest w chmurze, nie trzymaj w niej również swojego klucza.\n\\nMożesz wybrać dowolny plik jako klucz, np. zdjęcie czy plik tekstowy.\n\\nUwaga: Jeżeli owy plik zostanie edytowany, nie będziesz w stanie otworzyć nim bazy danych!\n\\nZalecane jest używanie plików do jedynie odczytu jako kluczy (bez możliwości edytowania).</string>\n    <string name=\"error_pass_null\">Proszę utworzyć hasło do bazy danych</string>\n    <string name=\"error_enter_pass_null\">Wprowadź hasło jeszcze raz</string>\n    <string name=\"history_record\">Zapis historii</string>\n    <string name=\"error_pass_unfit\">Wprowadzone hasła nie są takie same</string>\n    <string name=\"error_open_db\">Nie udało się otworzyć bazy danych. Hasło może być niepoprawne lub wymagany jest klucz.</string>\n    <string name=\"setting\">Ustawienie</string>\n    <string name=\"expire\">Wygasł w &lt;font color=#FF0000&gt;%s&lt;/font&gt;</string>\n    <string name=\"hint_query\">Nazwa użytkownika, tytuł, URL, zaawansowane atrybuty …</string>\n    <string name=\"use_full_fingerprint\">Pełne odblokowanie odciskiem palca</string>\n    <string name=\"hint_del_group_no_recycle\">Czy na pewno chcesz usunąć grupę【&lt;font color=#FF0000&gt;%s&lt;/font&gt;】 \\?</string>\n    <string name=\"error_open_db_arcfour_error\">Niepoprawny algorytm szyfrujący</string>\n    <string name=\"hint_group_desc\">Grupuj-%s przedmiotów</string>\n    <string name=\"fail\">Błąd</string>\n    <string name=\"del_entry\">Usuń przedmiot</string>\n    <string name=\"hint_input_url\">Wpisz adres</string>\n    <string name=\"notice\">Notatki</string>\n    <string name=\"app_feedback\">Sugestie</string>\n    <string name=\"feedback\">Sugestie ^_^</string>\n    <string name=\"afs\">System plików Android</string>\n    <string name=\"hint_webdav_url\">URL pliku</string>\n    <string name=\"helper_webdav\">Przykład: https://dav.example.org/dav/[twój folder]/xxx.kdbx</string>\n    <string name=\"unlocked\">Odblokowany</string>\n    <string name=\"version_log\">Dziennik aktualizacji</string>\n    <string name=\"hint_finger_print_verfiy\">Ponownie zweryfikuj swoje odciski palców, aby użyć nowych ustawień</string>\n    <string name=\"upload\">Prześlij</string>\n    <string name=\"hint_fingerprint_modify\">Sprawdź, czy odcisk palca się zmienił, dla bezpieczeństwa hasła zresetuj funkcję odblokowywania odciskiem palca</string>\n    <string name=\"set_delete_no_into_recycle_bin_des\">Przenieś usunięte elementy do kosza</string>\n    <string name=\"set_delete_no_into_recycle_bin\">Usuń wpisy z bazy danych na stałe</string>\n    <string name=\"notify_channel_db_open\">Powiadomienie o odblokowaniu bazy danych</string>\n    <string name=\"closed\">Zamknięty</string>\n    <string name=\"helper_webdav_dir\">Przykład: https://dav.example.org/dav/[twój folder]/</string>\n    <string name=\"invalid_img\">Nieprawidłowy obraz</string>\n    <string name=\"undo_entry\">Wznowiono…</string>\n    <string name=\"copy_totp\">Kopiuj token TOTP</string>\n    <string name=\"quick_use_fingerprint\">Używaj odcisku palca tylko do odblokowania w interfejsie szybkiego odblokowania</string>\n</resources>"
  },
  {
    "path": "app/src/main/res/values-pt/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"db\">Base de dados</string>\n</resources>"
  },
  {
    "path": "app/src/main/res/values-pt-rBR/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"db\">Base de dados</string>\n</resources>"
  },
  {
    "path": "app/src/main/res/values-ru-rRU/arrays.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n\n</resources>"
  },
  {
    "path": "app/src/main/res/values-ru-rRU/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"db\">База данных</string>\n    <string name=\"error_group_no_modify\">Никаких изменений в группе</string>\n    <string name=\"error_open_db_arcfour_error\">Неверный алгоритм шифрования потока</string>\n    <string name=\"save\">Сохранить</string>\n    <string name=\"create_entry\">Создать запись</string>\n    <string name=\"notice\">Примечание</string>\n    <string name=\"hint_input_url\">Адрес ссылки</string>\n    <string name=\"hint_input_user_name\">Имя пользователя</string>\n    <string name=\"hint_input_title\">Название</string>\n    <string name=\"hint_copy_to_clip\">Данные скопированы в буфер обмена</string>\n    <string name=\"hide_pass\">Скрыть пароль</string>\n    <string name=\"ope_url\">Открыть ссылку</string>\n    <string name=\"show_pass\">Показать пароль</string>\n    <string name=\"copy_to_clip\">Копировать в буфер обмена</string>\n    <string name=\"db_name_modify\">Название базы данных изменилось</string>\n    <string name=\"db_name_no_alter\">Имя базы не изменилось</string>\n    <string name=\"out_db\">Экспорт базы данных</string>\n    <string name=\"modify_db_pass\">Изменить пароль базы данных</string>\n    <string name=\"modify_db_name\">Изменить название базы данных</string>\n    <string name=\"db_handle\">Операции с базой данных</string>\n    <string name=\"safety_set\">Настройки безопасности</string>\n    <string name=\"hint_copy_pass\">Пароль скопирован в буфер обмена</string>\n    <string name=\"hint_copy_user\">Имя пользователя скопировано в буфер обмена</string>\n    <string name=\"choose_icon\">Выберите значок</string>\n    <string name=\"title_too_long\">Имя группы слишком большое</string>\n    <string name=\"modify_group\">Редактировать группу</string>\n    <string name=\"group_name\">Название группы</string>\n    <string name=\"copy_password\">Копировать пароль</string>\n    <string name=\"copy_user_name\">Копировать имя пользователя</string>\n    <string name=\"del_history\">Удалить запись истории</string>\n    <string name=\"del_entry\">Удалить элемент</string>\n    <string name=\"fail\">Ошибка</string>\n    <string name=\"success\">Успешно</string>\n    <string name=\"edit_group\">Редактировать группу</string>\n    <string name=\"del_group\">Удалить группу</string>\n    <string name=\"hint_ex_property\">Дополнительные атрибуты</string>\n    <string name=\"attachment\">Вложение</string>\n    <string name=\"hint_attr\">Расширенные свойства</string>\n    <string name=\"error_open_db_algorithm_error\">Неверный алгоритм</string>\n    <string name=\"error_open_db_signature_error\">Неверная подпись базы данных</string>\n    <string name=\"error_open_db_version_error\">Неверная версия базы данных</string>\n    <string name=\"error_open_db_pass_error\">Неверный пароль</string>\n    <string name=\"error_input_pass_null\">Пожалуйста, введите пароль базы данных</string>\n    <string name=\"help_input_db_pass\">Введите пароль базы данных</string>\n    <string name=\"welcome\">Приветствую</string>\n    <string name=\"app_setting\">Настройки приложения</string>\n    <string name=\"db_setting\">Настройки базы данных</string>\n    <string name=\"setting\">Настройки</string>\n    <string name=\"all\">Элементы</string>\n    <string name=\"change_db\">Выбрать базу</string>\n    <string name=\"edit\">Редактировать</string>\n    <string name=\"history_record\">Запись истории</string>\n    <string name=\"history\">История</string>\n    <string name=\"hint_db_create_success\">База данных [%s] создана</string>\n    <string name=\"error_pass_null\">Пожалуйста, установите пароль базы данных</string>\n    <string name=\"create_file\">Создать новый файл</string>\n    <string name=\"choose_file\">Выберите файл</string>\n    <string name=\"cancel\">Отмена</string>\n    <string name=\"done\">Готово</string>\n    <string name=\"error_db_name_null\">Пожалуйста, введите название базы данных</string>\n    <string name=\"hint_select_path_type\">Выберите место хранения базы</string>\n    <string name=\"hint_select_db_path\">Нажмите, чтобы установить метод сохранения данных</string>\n    <string name=\"up\">Предыдущий</string>\n    <string name=\"next\">Следующий шаг</string>\n    <string name=\"create_db\">Создать базу данных</string>\n    <string name=\"db_name\">Введите имя базы данных</string>\n    <string name=\"open_db\">Открыть базу данных</string>\n    <string name=\"encrypt_type_1\">Только пароль</string>\n    <string name=\"encrypt_type\">Шифрование</string>\n    <string name=\"help_create_group\">Не более 16 символов</string>\n    <string name=\"hint_enter_password\">Подтвердить пароль</string>\n    <string name=\"hint_input_password\">Введите пароль из 6-16 цифр</string>\n    <string name=\"hint_set_password\">Задать пароль</string>\n    <string name=\"open\">Открыть</string>\n    <string name=\"password\">Пароль</string>\n    <string name=\"set_title_screen_lock\">Блокировать экран</string>\n    <string name=\"multi_choice\">Множественный выбор</string>\n    <string name=\"ref_entry\">Справочная запись</string>\n    <string name=\"other\">Другое</string>\n    <string name=\"cur_app_not_autofill\">Не заполнять автоматически</string>\n    <string name=\"ui_setting\">Настройка пользовательского интерфейса</string>\n    <string name=\"set_key_sum_main_show_history\">Показывать историю после разблокировки базы данных</string>\n    <string name=\"set_key_sum_main_show_entries\">Показывать все элементы после разблокировки базы данных</string>\n    <string name=\"move\">Переместить</string>\n    <string name=\"url\">URL-адрес</string>\n    <string name=\"hint_protect_str\">Защищенное поле</string>\n    <string name=\"hint_input_attr_str_value\">Значение поля</string>\n    <string name=\"hint_input_attr_str_key\">Название поля</string>\n    <string name=\"warning\">Предупреждение</string>\n    <string name=\"hint_webdav_url\">URL-адрес файла</string>\n    <string name=\"helper_webdav_dir\">например: https://dav.xxxx.com/dav/[ваша папка]/</string>\n    <string name=\"helper_webdav\">например: https://dav.example.org/dav/[your folder]/xxx.kdbx</string>\n    <string name=\"login\">Войти</string>\n    <string name=\"no_file\">Нет файла</string>\n    <string name=\"cover_cloud\">Облако</string>\n    <string name=\"sync_db\">Синхронная база данных</string>\n    <string name=\"merge\">Объединить</string>\n    <string name=\"set_key_value\">Выберите язык</string>\n    <string name=\"quick_pass_len_summary\">Длина короткого пароля: %s</string>\n    <string name=\"quick_pass_len_title\">Длина короткого пароля</string>\n    <string name=\"error_short_pass_short\">Длина короткого пароля не может быть меньше 3 символов</string>\n    <string name=\"error_short_pass_lenght\">Длина короткого пароля должна быть не более 6 символов</string>\n    <string name=\"sort\">Сортировка</string>\n    <string name=\"totp_len\">Длина:</string>\n    <string name=\"totp_Arithmetic\">Арифметика:</string>\n    <string name=\"totp_custom_hint\">Пользовательские настройки</string>\n    <string name=\"totp_custom\">Использовать пользовательские настройки</string>\n    <string name=\"donate\">Пожертвование</string>\n    <string name=\"set_delete_no_into_recycle_bin_des\">Переместить удаленные элементы в корзину</string>\n    <string name=\"set_delete_setting\">Удалить настройки</string>\n    <string name=\"hint_please_open_database\">Пожалуйста, откройте базу</string>\n    <string name=\"add_attr_file\">Добавить вложения</string>\n    <string name=\"open1\">открытие</string>\n    <string name=\"upload\">Выгрузить</string>\n    <string name=\"closed\">Закрыто</string>\n    <string name=\"db_unlock\">Разблокировать базу данных</string>\n    <string name=\"quick_unlock\">Быстрая разблокировка</string>\n    <string name=\"search\">Поиск</string>\n    <string name=\"notify_db_locked\">База данных заблокирована</string>\n    <string name=\"notify_quick_unlock_start\">База данных заблокирована, быстрая разблокировка включена</string>\n    <string name=\"notify_channel_db_open\">Уведомление о разблокировке базы данных</string>\n    <string name=\"unlocked\">Разблокировано</string>\n    <string name=\"afs\">Файловая система Android</string>\n    <string name=\"db_file_no_exist\">Файл базы данных не существует</string>\n    <string name=\"hint_please_input\">Пожалуйста, введите %s</string>\n    <string name=\"s_default\">По умолчанию</string>\n    <string name=\"set_key_title_show_state_bar_turn_off\">Скрыть</string>\n    <string name=\"set_key_title_show_state_bar_turn_on\">Показать</string>\n    <string name=\"set_key_title_fillet_bg_icon\">Закругленные углы для значков списка</string>\n    <string name=\"Alipay\">Alipay</string>\n    <string name=\"close\">Закрыть</string>\n    <string name=\"set_key_title_main_allow_show_entries\">Домашняя страница</string>\n    <string name=\"disable\">Выключить</string>\n    <string name=\"open_quick_unlock\">Быстрая разблокировка</string>\n    <string name=\"other_set\">Другие настройки</string>\n    <string name=\"feedback\">Обратная связь ^_^</string>\n    <string name=\"modify_time\">Изменить время</string>\n    <string name=\"relevance_db\">Связанные данные</string>\n    <string name=\"error_pass\">Ошибка пароля</string>\n    <string name=\"txt_viewer\">Просмотрщик файлов</string>\n    <string name=\"del_attr_file\">Удалить вложение</string>\n    <string name=\"add_more\">Добавить еще</string>\n    <string name=\"normal_account\">Обычный аккаунт</string>\n    <string name=\"pass_generater\">Генератор паролей</string>\n    <string name=\"hint_input_tag\">Тег1, Тег2</string>\n    <string name=\"fingerprint_unlock\">Разблокировка с помощью отпечатка пальца</string>\n    <string name=\"error_open_db_key_invalid\">Неверный ключ</string>\n    <string name=\"pass_key\">Файл ключа</string>\n    <string name=\"choose_pass_key\">Выбрать ключ</string>\n    <string name=\"key\">Секретный ключ</string>\n    <string name=\"set_key_title_auto_lock_database_turn_off\">Нет</string>\n    <string name=\"set_key_title_auto_lock_database_turn_on\">Да</string>\n    <string name=\"set_key_title_close_load_anim_turn_off\">Выкл</string>\n    <string name=\"set_key_title_close_load_anim_turn_on\">Вкл</string>\n    <string name=\"set_key_title_fillet_bg_icon_turn_off\">Выкл</string>\n    <string name=\"set_key_title_fillet_bg_icon_turn_on\">Вкл</string>\n    <string name=\"set_key_title_opr_env_check_turn_on\">Вкл</string>\n    <string name=\"set_key_title_opr_env_check_turn_off\">Выкл</string>\n    <string name=\"app_feedback\">Обратная связь</string>\n    <string name=\"time\">Время</string>\n    <string name=\"date\">Дата</string>\n    <string name=\"minus\">Дефис</string>\n    <string name=\"numer\">Цифры</string>\n    <string name=\"len\">Длина</string>\n    <string name=\"hint\">Подсказка</string>\n    <string name=\"need_key\">Нужен ключ\\?</string>\n    <string name=\"no_record\">Нет записей</string>\n    <string name=\"set_key_praise_value\">Оценить KeePassA</string>\n    <string name=\"encrypt_type_2\">Пароль + ключ</string>\n    <string name=\"hint_set_pass_key\">Выберите ключ</string>\n    <string name=\"create_pass_key_success\">Файл с ключом [%s] создан</string>\n    <string name=\"error_enter_pass_null\">Введите пароль</string>\n    <string name=\"error_pass_unfit\">Дважды выедён неподходящий пароль</string>\n    <string name=\"db1\">база\\t|\\t&lt;font color=#757575&gt;%s&lt;/font&gt;</string>\n    <string name=\"key1\">Ключ: %s</string>\n    <string name=\"enter\">ОК</string>\n    <string name=\"create_pass_key_file_des\">Создать новый файл с ключом</string>\n    <string name=\"error_uri\">Ошибка URI</string>\n    <string name=\"error_open_db_key_empty\">Ключ не выбран</string>\n    <string name=\"create_time\">Создано %s</string>\n    <string name=\"expire_time\">Истекает %s</string>\n    <string name=\"error_group_name_null\">Не задано имя группы</string>\n    <string name=\"create_group_success\">Группа создана</string>\n    <string name=\"hint_query\">Имя пользователя, адрес, другие атрибуты…</string>\n    <string name=\"verify_finger_fail\">Не удалось проверить отпечаток пальца</string>\n    <string name=\"undo_grouped\">Группа была перемещена</string>\n    <string name=\"undo_entryed\">Запись была перемещена</string>\n    <string name=\"hint_quick_unlock\">Введите последние %s символов пароля базы</string>\n    <string name=\"hint_input_cover_url\">Перезаписать ссылку</string>\n    <string name=\"underline\">Подчёркивание</string>\n    <string name=\"space\">Пробел</string>\n    <string name=\"special\">Специальные</string>\n    <string name=\"bracket\">Скобки</string>\n    <string name=\"add_custom_str\">Создать поле</string>\n    <string name=\"use_full_fingerprint\">Полная разблокировка отпечатком пальца</string>\n    <string name=\"del_attr_str\">Удалить поле</string>\n    <string name=\"upper\">Заглавные</string>\n    <string name=\"lower\">Прописные</string>\n    <string name=\"hint_pass_null\">Пароль не задан</string>\n    <string name=\"error_attr_str_null\">Пустое имя поля</string>\n    <string name=\"hint_del_entry\">Удалить запись 【&lt;font color=#FF0000&gt;%s&lt;/font&gt;】\\? &lt;br&gt;При этом она будет перемещена в корзину.</string>\n    <string name=\"hint_del_group\">\"Удалить группу【&lt;font color=#FF0000&gt;%s&lt;/font&gt;】\\?&lt;br&gt;При этом  она будет перемещена в корзину.\"</string>\n    <string name=\"hint_del_group_no_recycle\">Удалить группу 【&lt;font color=#FF0000&gt;%s&lt;/font&gt;】\\?</string>\n    <string name=\"help_create_db\">По умолчанию создавать KeePass версии 4</string>\n    <string name=\"helper_create_pass\">Рекомендуются различные символы (прописные, строчные, цифры и специальные)</string>\n    <string name=\"help_create_db_path\">Рекомендуется использовать облачные сервисы (например Dropbox или WebDAV). Это автоматически синхронизирует базу с облаком, упрощая её использование на разных устройствах.\n\\n\n\\nБаза шифруется с помощью AES-256: не зная пароля никто не сможет её открыть.</string>\n    <string name=\"error_open_db\">Не удалось открыть базу. Возможно неверный пароль или нужен ключ\\?</string>\n    <string name=\"choose_pass_key_file_des\">Выберите файл для ключа</string>\n    <string name=\"help_pass_type\">Только пароль:\n\\nДля открытия базы нужен только пароль;\n\\nПароль + ключ:\n\\nДля открытия базы требуются пароль и файл с ключом.</string>\n    <string name=\"expire\">Истекает &lt;font color=#FF0000&gt;%s&lt;/font&gt;</string>\n    <string name=\"help_pass_key\">Файл-ключ это ключ, необходимый для открытия данных, и, обычно, более сложный и надёжный, чем пароль. При этом его сложнее скрывать. При хранении базы в облаке не храните в том же месте и ключ.\n\\nДля ключа можно взять любой файл: например фото или текстовый.\n\\n⚠️ Внимание: если этот файл будет изменён, то открыть им базу уже не получится.\n\\nДля этого файла рекомендуется установить атрибут \\\"только для чтения \\\".</string>\n    <string name=\"expand\">Развернуть</string>\n    <string name=\"hint_del_entry_no_recycle\">Удалить запись 【&lt;font color=#FF0000&gt;%s&lt;/font&gt;】навсегда\\?</string>\n    <string name=\"create_group_fail\">Не удалось создать группу</string>\n    <string name=\"undo_entry\">Восстановить…</string>\n    <string name=\"undo_to\">Переместить в эту группу</string>\n    <string name=\"verify_finger\">Проверка отпечатка пальца</string>\n    <string name=\"create_group\">Новая группа</string>\n    <string name=\"ban_fingerprint\">Нет разблокировки отпечатком пальца</string>\n    <string name=\"quick_use_fingerprint\">Использовать отпечаток пальца для быстрой разблокировки</string>\n    <string name=\"error_genera_params\">Выберите хотя бы 1 тип</string>\n    <string name=\"copy_totp\">Копировать TOTP токен</string>\n    <string name=\"autofill_sign_in_prompt\">Автозаполнение пароля с помощью KeePassA</string>\n    <string name=\"title\">Название</string>\n    <string name=\"hint_confirm\">Подтвердить пароль</string>\n    <string name=\"set_open_auto_fill_title\">Сервис автозаполнения</string>\n    <string name=\"clean_clip_time\">Очищать буфер обмена</string>\n    <string name=\"clean_clip_time_summary\">После копирования пароля, очищать буфер обмена через %s</string>\n</resources>"
  },
  {
    "path": "app/src/main/res/values-tr/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"hint_enter_password\">Parolayı onayla</string>\n    <string name=\"encrypt_type\">Şifreleme</string>\n    <string name=\"encrypt_type_1\">Yalnızca parola</string>\n    <string name=\"encrypt_type_2\">Parola + anahtar</string>\n    <string name=\"choose_pass_key\">Anahtar seç</string>\n    <string name=\"hint_set_pass_key\">Lütfen bir anahtar seçin</string>\n    <string name=\"db\">Veri tabanı</string>\n    <string name=\"password\">Parola</string>\n    <string name=\"key\">Gizli anahtar</string>\n    <string name=\"key1\">Gizli anahtar: %s</string>\n    <string name=\"open\">Kilidi aç</string>\n    <string name=\"helper_create_pass\">Karışık parola tavsiye edilir (büyük ve küçük harf + sayılar + semboller)</string>\n    <string name=\"open_db\">Veri tabanını aç</string>\n    <string name=\"db_name\">Veri tabanı adını girin</string>\n    <string name=\"create_db\">Veri tabanı oluştur</string>\n    <string name=\"next\">Sonraki adım</string>\n    <string name=\"up\">Önceki</string>\n    <string name=\"hint_set_password\">Parola ayarla</string>\n    <string name=\"help_create_db\">Öntanımlı olarak v4 KeePass veri tabanı oluştur</string>\n    <string name=\"help_create_group\">16 veya daha az karakterden oluşan bir grup adı seçin</string>\n    <string name=\"hint_input_password\">6-16 basamaklı bir parola girin</string>\n    <string name=\"pass_key\">Dosya anahtarı</string>\n    <string name=\"hint_select_db_path\">Veri kaydetme yöntemini ayarlamak için tıklayın</string>\n    <string name=\"hint_select_path_type\">Veri tabanınızı nereye kaydedeceğinizi seçin</string>\n    <string name=\"error_db_name_null\">Lütfen bir veri tabanı adı girin</string>\n    <string name=\"done\">Tamam</string>\n    <string name=\"enter\">Kaydet</string>\n    <string name=\"cancel\">İptal</string>\n    <string name=\"hint\">İleti</string>\n    <string name=\"help_create_db_path\">Dropbox ve WebDAV gibi bulut yollarını kullanmanız tavsiye edilir. Bu veri tabanınızı bulutla otomatik olarak eşzamanlayarak veri tabanını diğer aygıtlarda kullanmayı kolaylaştırır.\n\\n Veri tabanı AES-256 kullanılarak şifrelenir. Parolanız olmadan kimse veri tabanınızı açamaz.</string>\n    <string name=\"help_pass_type\">Yalnızca parola kullan:\n\\n Veri tabanını açmak için yalnızca parolanız gereklidir；\n\\n Parola + anahtar:\n\\n Veri tabanını açmak için bir parola girmeniz ve bir anahtar dosyası seçmeniz gerekir.</string>\n    <string name=\"dropbox_msg\">Kullanıcı gizliliğine saygı ilkesini benimseyen KeePassA, Dropbox\\'ınızdaki tüm dosyalar için izin istemeyecektir, KeePassA &lt;font color u003d # FF0000&gt; yalnızca 【Apps/KeePassA】 klasörüne erişir&lt;/ font &gt;.&lt;br /&gt;Bu nedenle: &lt;br /&gt; &lt;b&gt;Zaten bir KeePass veri tabanınız varsa, KeePassA\\'daki 【Apps/KeePassA】 klasörüne KeePassA erişimi verin ve yetkilendirme tamamlandıktan sonra KeePass veri tabanını 【Application/KeePassA】 klasörüne kaydedin.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt; Yeni bir KeePass veri tabanı oluşturuyorsanız yukarıdaki işlemlere gerek yoktur. Veri tabanını oluşturduktan sonra, KeePassA ilgili klasörü otomatik olarak oluşturur ve veri tabanını Dropbox\\'ın 【Apps/KeePassA】 dizinine yükler.</string>\n    <string name=\"hint_security_red\">Aygıtınız root yapılmış ve veri tabanı çok tehlikeli bir ortamda</string>\n    <string name=\"set_delete_no_into_recycle_bin\">Veri tabanından girdileri kalıcı olarak sil</string>\n    <string name=\"helper_webdav\">örneğin: https://dav.example.org/dav/[klasörünüz]/xxx.kdbx</string>\n    <string name=\"helper_webdav_dir\">örneğin: https://dav.example.org/dav/[klasörünüz]/</string>\n    <string name=\"hint_please_input\">Lütfen %s girin</string>\n    <string name=\"db_file_no_exist\">Veri tabanı dosyası mevcut değil</string>\n    <string name=\"invalid_auth\">Yetkilendirilemedi. Lütfen tekrar deneyin.</string>\n    <string name=\"hint_kdbx_name\">Yanlış son ek adı, son ek adı .kdbx olmalıdır</string>\n    <string name=\"afs\">Android dosya sistemi</string>\n    <string name=\"start_upload_db\">Veri tabanını ağ diskinize yüklemeye başlayın</string>\n    <string name=\"notify_channel_db_open\">Veri tabanı kilit açma bildirimi</string>\n    <string name=\"hint_quick_use_fingerprint\">Hızlı kilit açma sayfasında kilidi açmak için yalnızca parmak izi kullan</string>\n    <string name=\"unlocked\">Kilidi açıldı</string>\n    <string name=\"notify_quick_unlock_start\">Veri tabanı kilitlendi, hızlı kilit açma etkin</string>\n    <string name=\"notify_db_locked\">Veri tabanı kilitlendi</string>\n    <string name=\"search\">Ara</string>\n    <string name=\"open1\">Aç</string>\n    <string name=\"disable\">Devre dışı bırak</string>\n    <string name=\"quick_unlock\">Hızlı kilit açma</string>\n    <string name=\"db_unlock\">Veri tabanının kilidini aç</string>\n    <string name=\"upload\">Karşıya yükle</string>\n    <string name=\"add_attr_file\">Ek ekle</string>\n    <string name=\"set_delete_setting\">Silme ayarları</string>\n    <string name=\"warning_rooted\">Veri tabanınızı güvende tutmak için onu root yapılmış bir telefonda açmayın</string>\n    <string name=\"hint_please_open_database\">Lütfen veri tabanını açın</string>\n    <string name=\"set_delete_no_into_recycle_bin_des\">Silinen ögeleri çöp kutusuna taşı</string>\n    <string name=\"donate_desc\">[KeePassA](https://github.com/AriaLyy/KeepassA) özgür yazılımdır. Çalışmalarımızı desteklemek için bağış yapın, teşekkürler.</string>\n    <string name=\"dev_birthday\">Bugün geliştiricinin doğum günü🎂. Geliştiriciye bir bira ısmarlamak ister misiniz?</string>\n    <string name=\"totp_defaule\">Öntanımlı RFC 6238 belirteç ayarları</string>\n    <string name=\"totp_steam\">Steam belirteci ayarları</string>\n    <string name=\"hint_security_yellow\">Uyarı! Simülatörde çalışmasına göre bu ortam, veri tabanına bazı öngörülemeyen riskler getirecektir</string>\n    <string name=\"donate\">Bağış yap</string>\n    <string name=\"totp_custom\">Özel ayarları kullan</string>\n    <string name=\"totp_custom_hint\">Özel ayarlar</string>\n    <string name=\"totp_key_error\">TOTP kodu oluşturulamadı, normal olmayan anahtar</string>\n    <string name=\"sort_chart_desc\">İlk harf azalan (Z-A)</string>\n    <string name=\"hint_security_green\">\"Uygulama güvenli bir ortamda \"</string>\n    <string name=\"set_open_kpa_ime_title\">Güvenlik klavyesi</string>\n    <string name=\"set_key_title_opr_env_check_turn_on\">Açık</string>\n    <string name=\"totp_Arithmetic\">Aritmetik:</string>\n    <string name=\"no_totp_token\">TOTP belirteci yok</string>\n    <string name=\"ime_label\">KeePassA güvenlik klavyesi</string>\n    <string name=\"license\">Özgür yazılım lisansı</string>\n    <string name=\"set_key_sum_main_show_entries\">Veri tabanının kilidini açtıktan sonra tüm ögeleri göster</string>\n    <string name=\"set_key_sum_main_show_history\">Veri tabanının kilidini açtıktan sonra geçmişi göster</string>\n    <string name=\"set_key_title_main_allow_show_entries\">Ana sayfa</string>\n    <string name=\"set_key_title_opr_env_check\">Çalışma ortamı denetimi</string>\n    <string name=\"cur_app_not_autofill\">Otomatik doldurma yok</string>\n    <string name=\"multi_choice\">Çoktan seçmeli</string>\n    <string name=\"set_summary_screen_lock_off\">Veri tabanını kilitlemez</string>\n    <string name=\"set_key_main_show_totp_tab_on\">TOTP kategorisini görüntüle</string>\n    <string name=\"set_key_main_show_totp_tab_off\">TOTP kategorisini görüntüleme</string>\n    <string name=\"s_default\">Öntanımlı</string>\n    <string name=\"set_key_title_close_load_anim_turn_off\">Kapalı</string>\n    <string name=\"customize\">Özelleştir</string>\n    <string name=\"set_key_title_auto_lock_database\">Veri tabanını otomatik kilitle</string>\n    <string name=\"set_key_title_auto_lock_database_turn_on\">Evet</string>\n    <string name=\"set_key_title_auto_lock_database_turn_off\">Hayır</string>\n    <string name=\"translate_language\">Uygulamayı dilinize çevirin</string>\n    <string name=\"get_token_fail\">Belirteç alınamadı</string>\n    <string name=\"one_drive_init_failure\">OneDrive başlatma hatası</string>\n    <string name=\"set_key_main_show_totp_tab_title\">Ana sayfa TOTP kategorisini gösterir</string>\n    <string name=\"numer\">Rakam</string>\n    <string name=\"space\">Boşluk</string>\n    <string name=\"special\">Özel</string>\n    <string name=\"bracket\">Parantez</string>\n    <string name=\"time\">Saat</string>\n    <string name=\"choose_dir\">Geçerli gruba kaydet</string>\n    <string name=\"create_entry_no_save\">Girdiyi kaydetmeden geçerli sayfadan çıkılsın mı\\?</string>\n    <string name=\"add_custom_str\">Özel alanlar oluştur</string>\n    <string name=\"hint_db_pass_modify_success\">Veri tabanını bir dahaki sefere açmak için yeni veri tabanı parolasını kullanın</string>\n    <string name=\"hint_db_pass_modify\">Veri tabanı parolası değiştirildi</string>\n    <string name=\"app_feedback\">Geri bildirim</string>\n    <string name=\"error_db_pass_too_short\">Verilerinizi korumak için veri tabanı parolasını 6 karakterden uzun yapın</string>\n    <string name=\"autofill_sign_in_prompt\">Parolayı KeePassA ile otomatik doldur</string>\n    <string name=\"hint_webdav_url\">Dosya URL\\'si</string>\n    <string name=\"version_log\">Yükseltme günlüğü</string>\n    <string name=\"hint_finger_print_verfiy\">Yeni ayarları kullanmak için parmak izlerinizi yeniden doğrulayın</string>\n    <string name=\"closed\">Kapalı</string>\n    <string name=\"hint_fingerprint_modify\">Parola güvenliğiniz için parmak izinin değiştiğini gözden geçirin, lütfen parmak izi kilit açma işlevini sıfırlayın</string>\n    <string name=\"set_key_title_close_load_anim\">Yükleme animasyonu</string>\n    <string name=\"set_key_title_close_load_anim_turn_on\">Açık</string>\n    <string name=\"help_pass_key\">Anahtar dosyası, verileri açmak için bir anahtar gibi düşünülebilir ve genellikle bir paroladan çok daha karmaşık ve güvenilirdir. Ama aynı zamanda gizlemek daha zordur. Veri tabanını bulutta tutuyorsanız, anahtarı veri tabanıyla birlikte oraya koymayın.\n\\n Veri tabanını açmak için herhangi bir dosyayı anahtar olarak seçebilirsiniz: bir fotoğraf veya metin dosyası gibi.\n\\n⚠️ Not: Bu fotoğraf veya metin belgesi değiştirilirse veri tabanını açamayacaksınız! !!\n\\n Anahtar olarak salt okunur dosyaları kullanmanız önemle tavsiye edilir.</string>\n    <string name=\"error_open_db_signature_error\">Geçersiz veri tabanı imzası</string>\n    <string name=\"hint_del_group\">【&lt;font color=#FF0000&gt;%s&lt;/font&gt;】 grubu gerçekten silinsin mi\\?&lt;br&gt;Bu, onu daha sonra geri yükleyebileceğiniz çöp kutusuna taşır.</string>\n    <string name=\"hint_del_entry\">【&lt;font color=#FF0000&gt;%s&lt;/font&gt;】 ögesi gerçekten silinsin mi\\? &lt;br&gt;Bu, onu daha sonra geri yükleyebileceğiniz çöp kutusuna taşır.</string>\n    <string name=\"verify_finger\">Parmak izi doğrulama</string>\n    <string name=\"hint_pass_null\">Parola boş</string>\n    <string name=\"need_key\">Bir anahtara mı ihtiyacınız var\\?</string>\n    <string name=\"error_open_db_key_empty\">Anahtar eksik</string>\n    <string name=\"error_open_db_arcfour_error\">Geçersiz akış şifreleme algoritması</string>\n    <string name=\"copy_password\">Parolayı kopyala</string>\n    <string name=\"modify_db_name\">Veri tabanı adını değiştir</string>\n    <string name=\"pass_generater\">Parola oluşturucu</string>\n    <string name=\"error_genera_params\">Lütfen en az bir parola oluşturma türü seçin</string>\n    <string name=\"txt_viewer\">Dosya görüntüleyici</string>\n    <string name=\"error_pass\">Parola hatası</string>\n    <string name=\"error_uri\">URI hatası</string>\n    <string name=\"error_open_db_key_invalid\">Geçersiz anahtar</string>\n    <string name=\"hint_del_group_no_recycle\">【&lt;font color=#FF0000&gt;%s&lt;/font&gt;】 grubu gerçekten silinsin mi\\?</string>\n    <string name=\"create_totp\">OTP belirteci oluştur</string>\n    <string name=\"out_db\">Veri tabanını dışa aktar</string>\n    <string name=\"db_name_no_alter\">Veri tabanı adı değişmedi</string>\n    <string name=\"len\">Uzunluk</string>\n    <string name=\"underline\">Alt çizgi</string>\n    <string name=\"date\">Tarih</string>\n    <string name=\"add_more\">Daha fazla ekle</string>\n    <string name=\"hint_protect_str\">Korumalı alan</string>\n    <string name=\"del_attr_str\">Özel alanları sil</string>\n    <string name=\"open_whit_other\">Önbelleğe kaydet ve sistem uygulamasıyla aç</string>\n    <string name=\"save_db_success\">Veri tabanı kaydedildi</string>\n    <string name=\"hint_full_fingerprint\">Parmak izi, hızlı kilit açma arayüzünde ve veri tabanı kilidi açma arayüzünde veri tabanının kilidini açmak için kullanılır; KeePassA, veri tabanı ana parolanızı ve anahtar bilgilerinizi şifreler ve Android sisteminin yerel anahtar deposunda saklar ve koruma için parmak izi yetkilendirmesini kullanır. Yalnızca parmak iziniz veri tabanının kilidini açabilir.</string>\n    <string name=\"upper\">Büyük harf</string>\n    <string name=\"lower\">Küçük harf</string>\n    <string name=\"minus\">Eksi</string>\n    <string name=\"normal_account\">Normal hesap</string>\n    <string name=\"warning\">Uyarı</string>\n    <string name=\"hint_input_attr_str_key\">Alan adı</string>\n    <string name=\"error_attr_str_null\">Alan adı boş</string>\n    <string name=\"error_attr_file_too_large\">10 MB\\'den büyük dosyalar eklemeyerek bellek kullanımını azaltın</string>\n    <string name=\"download_file\">Eki SD karta kaydet</string>\n    <string name=\"db_pass_no_alter\">Yeni parola eskisiyle aynı</string>\n    <string name=\"hint_input_attr_str_value\">Alan değeri</string>\n    <string name=\"del_attr_file\">Eki sil</string>\n    <string name=\"open_whit_text\">Dahili metin düzenleyiciyle aç</string>\n    <string name=\"save_file_success\">[%s] dosyası kaydedildi</string>\n    <string name=\"open_whit_img\">Dahili resim görüntüleyiciyle aç</string>\n    <string name=\"save_db_fail\">Veri tabanı kaydedilemedi</string>\n    <string name=\"update_group_success\">Grup bilgileri güncellendi</string>\n    <string name=\"title\">Başlık</string>\n    <string name=\"feedback\">Geri bildirim ^_^</string>\n    <string name=\"send_email_fail\">Önce e-posta göndermek için bir uygulama kurun</string>\n    <string name=\"hint_background_start\">&lt;font color=#FF0000&gt;【%s】&lt;/font&gt; seçeneğine gidin ve otomatik doldurma hizmetinden daha iyi yararlanmak için arka planda açılır pencere izinleri verin.&lt;br&gt; &lt;font color=#FF0000&gt;KeePassA, otomatik doldurma işlevini kullandığınızda veri tabanını açmak için ilgili arayüzü gösterir&lt;/font&gt;</string>\n    <string name=\"setting_miui_background_start\">Ayarlar &gt; Uygulama Ayarları &gt; Uygulama Yönetimi &gt; KeePassA &gt; İzin Yönetimi</string>\n    <string name=\"feedback_email_msg\">Merhaba, ben KeePassA, sorunuz var mı\\?<br/> <br/> <br/> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ <br/> Marka: %s <br/> Model: %s <br/> Sistem sürümü:%s <br/> Çözünürlük: %s <br/> Uygulama sürümü: %s</string>\n    <string name=\"db1\">veri tabanı\\t|\\t&lt;font color=#757575&gt;%s&lt;/font&gt;</string>\n    <string name=\"choose_file\">Bir dosya seçin</string>\n    <string name=\"choose_pass_key_file_des\">Anahtarınız olarak kullanmak için herhangi bir dosya seçin</string>\n    <string name=\"create_file\">Yeni dosya oluştur</string>\n    <string name=\"create_pass_key_file_des\">Anahtarınız olarak kullanmak için yeni bir dosya oluşturun</string>\n    <string name=\"create_pass_key_success\">Anahtar oluşturma [%s] başarılı oldu</string>\n    <string name=\"error_pass_null\">Lütfen bir veri tabanı parolası ayarlayın</string>\n    <string name=\"error_enter_pass_null\">Lütfen onay parolasını girin</string>\n    <string name=\"error_pass_unfit\">İki kez tutarsız parola</string>\n    <string name=\"hint_db_create_success\">Veri tabanı [%s] oluşturuldu</string>\n    <string name=\"history\">Geçmiş</string>\n    <string name=\"history_record\">Geçmiş kaydı</string>\n    <string name=\"edit\">Düzenle</string>\n    <string name=\"change_db\">Veri tabanı değiştir</string>\n    <string name=\"all\">Ögeler</string>\n    <string name=\"setting\">Ayarlar</string>\n    <string name=\"db_setting\">Veri tabanı ayarları</string>\n    <string name=\"app_setting\">Uygulama ayarları</string>\n    <string name=\"welcome\">Hoş geldiniz</string>\n    <string name=\"help_input_db_pass\">Veri tabanı parolasını girin</string>\n    <string name=\"error_input_pass_null\">Lütfen veri tabanı parolasını girin</string>\n    <string name=\"error_open_db\">Veri tabanı açılamadı. Parola mı yanlış, yoksa bir anahtar mı gerekli\\?</string>\n    <string name=\"error_open_db_pass_error\">Geçersiz parola</string>\n    <string name=\"error_open_db_version_error\">Geçersiz veri tabanı sürümü</string>\n    <string name=\"error_open_db_algorithm_error\">Geçersiz algoritma</string>\n    <string name=\"hint_group_desc\">Grup-%s ögeleri</string>\n    <string name=\"error_group_id_null\">grup kimliğinin değeri yok</string>\n    <string name=\"error_entry_id_null\">girdi kimliğinin değeri yok</string>\n    <string name=\"create_time\">%s tarihinde oluşturuldu</string>\n    <string name=\"expire_time\">%s tarihinde sona eriyor</string>\n    <string name=\"hint_attr\">Gelişmiş özellikler</string>\n    <string name=\"attachment\">Ek</string>\n    <string name=\"hint_ex_property\">Ek özellikler</string>\n    <string name=\"expire\">&lt;font color=#FF0000&gt;%s&lt;/font&gt; tarihinde süresi doldu</string>\n    <string name=\"del_group\">Grubu sil</string>\n    <string name=\"edit_group\">Grubu düzenle</string>\n    <string name=\"success\">Başarılı</string>\n    <string name=\"fail\">Başarısız</string>\n    <string name=\"del_entry\">Ögeyi sil</string>\n    <string name=\"del_history\">Geçmiş kaydını sil</string>\n    <string name=\"copy_user_name\">Kullanıcı adını kopyala</string>\n    <string name=\"copy_totp\">TOTP belirtecini kopyala</string>\n    <string name=\"save\">Kaydet</string>\n    <string name=\"hint_del_entry_no_recycle\">【&lt;font color=#FF0000&gt;%s&lt;/font&gt;】girdisi gerçekten kalıcı olarak silinsin mi\\?</string>\n    <string name=\"group_name\">Grup adı</string>\n    <string name=\"create_group\">Grup oluştur</string>\n    <string name=\"error_group_name_null\">Grup adı boş</string>\n    <string name=\"create_group_fail\">Grup oluşturulamadı</string>\n    <string name=\"create_group_success\">Grup oluşturuldu</string>\n    <string name=\"modify_group\">Grubu düzenle</string>\n    <string name=\"error_group_no_modify\">Grup değişikliği yok</string>\n    <string name=\"title_too_long\">Grup adı çok uzun</string>\n    <string name=\"choose_icon\">Simge seç</string>\n    <string name=\"undo_entry\">Devam ettirildi…</string>\n    <string name=\"undo_to\">Bu gruba taşı</string>\n    <string name=\"undo_grouped\">Grup belirtilen konuma taşındı</string>\n    <string name=\"undo_entryed\">Öge belirtilen konuma taşındı</string>\n    <string name=\"hint_copy_user\">Kullanıcı adı panoya kopyalandı</string>\n    <string name=\"hint_copy_pass\">Parola panoya kopyalandı</string>\n    <string name=\"hint_copy_totp\">TOTP belirteci panoya kopyalandı</string>\n    <string name=\"safety_set\">Güvenlik Ayarları</string>\n    <string name=\"fingerprint_unlock\">Parmak iziyle kilit açma</string>\n    <string name=\"db_handle\">Veri tabanı işlemleri</string>\n    <string name=\"modify_db_pass\">Veri tabanı parolasını değiştir</string>\n    <string name=\"db_name_modify\">Veri tabanı adı değiştirildi</string>\n    <string name=\"no_record\">Kayıt yok</string>\n    <string name=\"hint_query\">Kullanıcı adı, başlık, URL, gelişmiş özellikler…</string>\n    <string name=\"copy_to_clip\">Panoya kopyala</string>\n    <string name=\"show_pass\">Parolayı göster</string>\n    <string name=\"ope_url\">Bağlantıyı aç</string>\n    <string name=\"hide_pass\">Parolayı gizle</string>\n    <string name=\"hint_copy_to_clip\">Veriler panoya kopyalandı</string>\n    <string name=\"verify_finger_fail\">Parmak izi doğrulanamadı</string>\n    <string name=\"ban_fingerprint\">Parmak iziyle kilit açma yok</string>\n    <string name=\"quick_use_fingerprint\">Hızlı kilit açma arayüzünde kilidi açmak için yalnızca parmak izi kullan</string>\n    <string name=\"use_full_fingerprint\">Tam parmak iziyle kilit açma</string>\n    <string name=\"hint_quick_unlock\">Veri tabanı parolanızın son %s basamağını girin</string>\n    <string name=\"hint_input_title\">Başlık</string>\n    <string name=\"hint_input_user_name\">Kullanıcı adı</string>\n    <string name=\"hint_input_url\">Bağlantı adresi</string>\n    <string name=\"notice\">Not</string>\n    <string name=\"hint_input_tag\">Etiket1, Etiket2</string>\n    <string name=\"hint_input_cover_url\">Bağlantının üzerine yaz</string>\n    <string name=\"create_entry\">Girdi oluştur</string>\n    <string name=\"quick_pass_type\">Kısa parola yakalama konumu</string>\n    <string name=\"des_quick_unlock\">Veri tabanınızın kilidini kısa bir parolayla açın</string>\n    <string name=\"error_short_pass_lenght\">Kısa parolanızı 6 veya daha az karakterden oluşacak şekilde yapın</string>\n    <string name=\"error_short_pass_short\">Kısa parolanızı 3 veya daha fazla karakterden oluşacak şekilde yapın</string>\n    <string name=\"quick_pass_len_title\">Kısa parola uzunluğu</string>\n    <string name=\"quick_pass_len_summary\">Kısa parola uzunluğu: %s</string>\n    <string name=\"set_key_value\">Dil seçin</string>\n    <string name=\"file_conflict_msg\">Bulut verileri ve yerel veriler arasında çakışma var, lütfen çözüm yöntemini seçin. Çakışan girdiler şunlar:\n\\n %s</string>\n    <string name=\"merge\">Birleştir</string>\n    <string name=\"sync_db\">Eşzamanlı veri tabanı</string>\n    <string name=\"net_error\">Ağ normal değil</string>\n    <string name=\"cover_local\">Yerel</string>\n    <string name=\"cover_cloud\">Bulut</string>\n    <string name=\"invalid\">Geçersiz</string>\n    <string name=\"no_file\">Dosya yok</string>\n    <string name=\"login\">Oturum aç</string>\n    <string name=\"cover\">Çözüm</string>\n    <string name=\"auth\">Yetki</string>\n    <string name=\"setting_vivo_background_start\">Ayarlar &gt; Diğer Ayarlar &gt; İzin Yönetimi &gt; KeePassA &gt; İzin Ayarları &gt; Arka Planda Açılır Pencere</string>\n    <string name=\"no_matching_entry\">Eşleşen öge yok</string>\n    <string name=\"hint_save_auto_fill\">【&lt;font color=#FF0000&gt;%s&lt;/font&gt;】paket adının【&lt;font color=#FF0000&gt;%s&lt;/font&gt;】 girdisiyle ilişkilendirilip ilişkilendirilmeyeceği</string>\n    <string name=\"relevance_db\">Bağlantılı veriler</string>\n    <string name=\"modify_time\">Değiştirme zamanı</string>\n    <string name=\"lose_time\">Süresi dolma zamanı</string>\n    <string name=\"update_group_fail\">Grup bilgileri güncellenemedi</string>\n    <string name=\"submit\">Gönder</string>\n    <string name=\"mark_not_exit\">Uygulama mağazası yok</string>\n    <string name=\"set_open_auto_fill_title\">Otomatik doldurma hizmeti</string>\n    <string name=\"set_key_praise_value\">KeePassA\\'yı değerlendirin</string>\n    <string name=\"other_set\">Diğer ayarlar</string>\n    <string name=\"open_quick_unlock\">Hızlı Kilit Açma</string>\n    <string name=\"clean_clip_time\">Panoyu Temizle</string>\n    <string name=\"clean_clip_time_summary\">Parolayı kopyalamanın ardından, %s sonra panoyu temizle</string>\n    <string name=\"auto_lock_db_time\">Veri tabanını otomatik kilitle</string>\n    <string name=\"auto_lock_db_time_summary\">Hiçbir işlem yapılmazsa %s sonra veri tabanını otomatik olarak kilitle</string>\n    <string name=\"totp_time\">Zaman(lar):</string>\n    <string name=\"totp_len\">Uzunluk:</string>\n    <string name=\"sort\">Sırala</string>\n    <string name=\"sort_chart_asc\">İlk harf artan (A-Z)</string>\n    <string name=\"sort_time_asc\">Zaman (ilk - son)</string>\n    <string name=\"sort_time_desc\">Zaman (son - ilk)</string>\n    <string name=\"ime_entry_other_info\">Diğer alanları doldurun</string>\n    <string name=\"url\">URL</string>\n    <string name=\"hint_not_nore_info\">Daha fazla bilgi yok</string>\n    <string name=\"error_uri_grant_permission\">URI yetkilendirmesi başarısız oldu</string>\n    <string name=\"move\">Taşı</string>\n    <string name=\"expand\">Genişlet</string>\n    <string name=\"shrink\">Küçült</string>\n    <string name=\"ui_setting\">Kullanıcı Arayüzü Ayarı</string>\n    <string name=\"set_key_title_opr_env_check_turn_off\">Kapalı</string>\n    <string name=\"other\">Diğer</string>\n    <string name=\"ref_entry\">Referans girdi</string>\n    <string name=\"set_summary_screen_lock_on\">Veri tabanını otomatik kilitler</string>\n    <string name=\"set_title_screen_lock\">Ekran kilidi</string>\n    <string name=\"close\">Kapat</string>\n    <string name=\"Alipay\">Alipay</string>\n    <string name=\"error_webdav_end_suffix\">Klasör yolunu kullanın. Örneğin: \\\"https://example.org/dav/kpa/\\\"</string>\n    <string name=\"error_webdav_end_dav_suffix\">/dav/ yoluna izin verilmiyor. Geçerli bir yol kullanın, örneğin: \\\"https://example.org/dav/kpa/\\\"</string>\n    <string name=\"set_key_title_fillet_bg_icon\">Yuvarlak liste simgesi köşeleri</string>\n    <string name=\"set_key_title_fillet_bg_icon_turn_on\">Açık</string>\n    <string name=\"set_key_title_fillet_bg_icon_turn_off\">Kapalı</string>\n    <string name=\"set_key_title_show_state_bar\">Durum çubuğu</string>\n    <string name=\"set_key_title_show_state_bar_turn_on\">Göster</string>\n    <string name=\"set_key_title_show_state_bar_turn_off\">Gizle</string>\n    <string name=\"one_drive_hint\">Gizliliğinizi korumak için KeePassA yalnızca &lt;font coloru003d#FF0000&gt;【application/KeepassA】&lt;/font&gt;dizinine erişecektir. OneDrive\\'ın Keepass veri tabanını okumak istiyorsanız, &lt;font coloru003d#FF0000&gt;lütfen veri tabanını bu dizinde saklayın.&lt;/font&gt;</string>\n    <string name=\"open_permissions\">İzinleri aç</string>\n    <string name=\"miui_permissions\">Otomatik doldurma işlevini kullanmak için MIUI sisteminin arka plan açılır pencere arayüzünü açma iznine ihtiyacı var.</string>\n    <string name=\"error_keystore\">Anahtar deposu istisnası, lütfen tekrar deneyin</string>\n    <string name=\"set_key_theme_style_title\">Tema tarzı</string>\n    <string name=\"set_key_theme_style_summary\">Sistemi takip et</string>\n    <string name=\"kpa_totp\">Belirteç</string>\n    <string name=\"kpa_copy\">Kopyala</string>\n    <string name=\"error_qr_code_str\">QR kodu normal değil. Lütfen QR kodunun Google Authenticator tarafından desteklendiğinden emin olun</string>\n    <string name=\"google_play\">Google Play</string>\n    <string name=\"disposable\">Bir kez</string>\n    <string name=\"monthly\">Aylık</string>\n    <string name=\"year\">Yıl</string>\n    <string name=\"error_donate\">Bağış başarısız oldu</string>\n    <string name=\"error_connect_play\">Google hizmetine bağlanılamadı</string>\n    <string name=\"thank_donate\">Bağışınız için teşekkür ederiz.</string>\n    <string name=\"current_collection_num\">%1$s koleksiyon</string>\n    <string name=\"my_collection\">Koleksiyonum</string>\n    <string name=\"no_collection\">Koleksiyon yok</string>\n    <string name=\"error_open_db_webdav\">WebDAV dosyası mevcut olmadığı için veri tabanı açılamadı.</string>\n    <string name=\"hint_cloud_file_already_exist\">%1$s zaten var. Bu dosyanın üzerine yazılsın mı\\?</string>\n    <string name=\"select_save_path\">Kaydetme yolu seç</string>\n    <string name=\"helper_webdav_service\">WebDAV hizmeti seç</string>\n    <string name=\"error_is_root\">Zaten kök dizini</string>\n    <string name=\"select_cur_path\">Klasör seç</string>\n    <string name=\"one_drive_load_user_failure\">Kullanıcı bilgileri yüklenemedi.</string>\n    <string name=\"hint_open_background_start\">Lütfen arka plan çıkarma iznini açın, aksi takdirde otomatik doldurmayı kullanamazsınız</string>\n    <string name=\"hostname\">Ana makine adı</string>\n    <string name=\"port\">Bağlantı noktası</string>\n    <string name=\"webdav_host_name_hint\">Lütfen ana makine adınızı yazın</string>\n    <string name=\"webdav_port_hint\">Bağlantı noktası</string>\n    <string name=\"webdav_port_name_null\">Ana makine adı boş olamaz</string>\n    <string name=\"privacy_agreement\">Gizlilik sözleşmesi</string>\n    <string name=\"ime_hint_open_auto_fill\">Lütfen otomatik doldurma hizmetini açın</string>\n    <string name=\"please_open_proxy\">Lütfen vekili açın</string>\n    <string name=\"invalid_img\">Geçersiz Resim</string>\n    <string name=\"hint_open_backgroun_start\">Otomatik doldurmayı ayarladınız, ancak KeePassA uygun izinlere sahip değil, lütfen ayarlar sayfasındaki KeePassA açılır ekranını yetkilendirin.</string>\n    <string name=\"app_debug\">Günlük dosyasını al</string>\n    <string name=\"open_setting\">Ayarlar</string>\n    <string name=\"hint_auto_fill_save\">Geçerli veriler var olan girdilerle ilişkilendirilsin mi?</string>\n    <string name=\"title_tip_of_day\">Günün ipucu</string>\n    <string name=\"hint_dont_show_tips\">İpuçlarını gösterme</string>\n    <string name=\"preemptive\">Önceden</string>\n    <string name=\"fail_unsupported_Systems_O\">Yalnızca P veya daha yüksek sistemi destekler</string>\n    <string name=\"hint_webdav_jgy\">Lütfen [Hesap-&gt;Güvenlik-&gt;Üçüncü Taraf Uygulama Yönetimi] bölümündeki parolayı kullanın.</string>\n    <string name=\"collection\">Koleksiyon</string>\n    <string name=\"add_attr_str\">Özellik ekle</string>\n    <string name=\"delete\">Sil</string>\n    <string name=\"create_tag\">Yeni etiket</string>\n    <string name=\"modify_str_attr\">Özellikleri değiştir</string>\n    <string name=\"error_file\">Dosya normal değil, lütfen silin: &lt;font color=#FF0000&gt;%s&lt;/font&gt;</string>\n    <string name=\"not_souper_otp\">Desteklenmeyen TOTP</string>\n    <string name=\"modify_totp\">OTP belirtecini değiştir</string>\n    <string name=\"error_totp\">Hatalı OTP</string>\n    <string name=\"file_save_success\">Ek başarıyla kaydedildi</string>\n    <string name=\"hint_confirm\">Parolayı onayla</string>\n    <string name=\"base_info\">Temel bilgiler</string>\n    <string name=\"tag\">Etiket</string>\n    <string name=\"totp\">TOTP</string>\n    <string name=\"tag_name\">Etiket adı</string>\n    <string name=\"add_tag\">Etiket ekle</string>\n    <string name=\"open_file\">Dosya Aç</string>\n</resources>"
  },
  {
    "path": "app/src/main/res/values-uk-rUA/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"open\">Розблокувати</string>\n    <string name=\"hint_set_password\">Указати пароль</string>\n    <string name=\"hint_input_password\">Введіть пароль від 6 до 16 цифр</string>\n    <string name=\"hint_enter_password\">Підтвердити пароль</string>\n    <string name=\"help_create_db\">Усталено створюється база даних KeepPass v4</string>\n    <string name=\"help_create_group\">Виберіть назву групи з 16 або менше символів</string>\n    <string name=\"encrypt_type\">Шифрування</string>\n    <string name=\"encrypt_type_1\">Лише пароль</string>\n    <string name=\"encrypt_type_2\">Пароль + ключ</string>\n    <string name=\"pass_key\">Файл ключа</string>\n    <string name=\"helper_create_pass\">Рекомендується змішаний пароль (великі та малі букви + цифри + символи)</string>\n    <string name=\"open_db\">Відкрити базу даних</string>\n    <string name=\"db_name\">Введіть назву бази даних</string>\n    <string name=\"hint_select_path_type\">Виберіть місце збереження бази даних</string>\n    <string name=\"error_db_name_null\">Введіть назву бази даних</string>\n    <string name=\"done\">Готово</string>\n    <string name=\"enter\">Готово</string>\n    <string name=\"cancel\">Скасувати</string>\n    <string name=\"hint\">Підказка</string>\n    <string name=\"db\">База даних</string>\n    <string name=\"db1\">база даних\\t|\\t&lt;font color=#757575&gt;%s&lt;/font&gt;</string>\n    <string name=\"password\">Пароль</string>\n    <string name=\"key\">Таємний ключ</string>\n    <string name=\"key1\">Таємний ключ: %s</string>\n    <string name=\"choose_pass_key\">Вибрати ключ</string>\n    <string name=\"next\">Наступний крок</string>\n    <string name=\"hint_set_pass_key\">Виберіть ключ</string>\n    <string name=\"create_db\">Створення бази даних</string>\n    <string name=\"hint_select_db_path\">Клацніть, щоб установити метод збереження даних</string>\n    <string name=\"up\">Попередній</string>\n    <string name=\"help_create_db_path\">Рекомендується використовувати хмарні сховища, такі як Dropbox і WebDAV. Це автоматично синхронізує вашу базу даних із хмарою, що полегшить користування базою даних на інших пристроях.\n\\n База даних зашифрована за допомогою AES-256. Без вашого пароля ніхто не зможе відкрити вашу базу даних.</string>\n    <string name=\"history_record\">Запис історії</string>\n    <string name=\"edit\">Змінити</string>\n    <string name=\"app_setting\">Налаштування застосунку</string>\n    <string name=\"error_open_db_key_invalid\">Неправильний ключ</string>\n    <string name=\"help_pass_type\">Лише пароль:\n\\n Для відкриття бази даних потрібен тільки ваш пароль;\n\\n Пароль + ключ:\n\\n Для відкриття бази даних потрібно ввести пароль і вибрати файл ключа.</string>\n    <string name=\"help_input_db_pass\">Введіть пароль бази даних</string>\n    <string name=\"need_key\">Потрібен ключ\\?</string>\n    <string name=\"error_open_db_algorithm_error\">Неприпустимий алгоритм</string>\n    <string name=\"copy_user_name\">Копіювати ім\\'я користувача</string>\n    <string name=\"history\">Історія</string>\n    <string name=\"welcome\">Ласкаво просимо</string>\n    <string name=\"error_input_pass_null\">Введіть пароль бази даних</string>\n    <string name=\"error_open_db_version_error\">Неправильна версія бази даних</string>\n    <string name=\"error_open_db_signature_error\">Недійсний підпис бази даних</string>\n    <string name=\"error_open_db_arcfour_error\">Неприпустимий алгоритм шифрування потоку</string>\n    <string name=\"error_entry_id_null\">ID запису не має значення</string>\n    <string name=\"hint_ex_property\">Додаткові атрибути</string>\n    <string name=\"copy_totp\">Копіювати токен TOTP</string>\n    <string name=\"del_history\">Видалити запис історії</string>\n    <string name=\"group_name\">Назва групи</string>\n    <string name=\"error_group_no_modify\">Немає змін у групі</string>\n    <string name=\"hint_copy_user\">Ім\\'я користувача скопійовано до буфера обміну</string>\n    <string name=\"fingerprint_unlock\">Розблокування відбитком пальця</string>\n    <string name=\"ope_url\">Відкрити посилання</string>\n    <string name=\"create_group\">Створення групи</string>\n    <string name=\"error_group_name_null\">Назва групи порожня</string>\n    <string name=\"create_group_fail\">Не вдалося створити групу</string>\n    <string name=\"modify_group\">Редагувати групу</string>\n    <string name=\"title_too_long\">Назва групи задовга</string>\n    <string name=\"choose_icon\">Вибрати піктограму</string>\n    <string name=\"create_group_success\">Група створена</string>\n    <string name=\"undo_entryed\">Елемент переміщено до вказаного розташування</string>\n    <string name=\"undo_to\">Перемістити до цієї групи</string>\n    <string name=\"hint_copy_pass\">Пароль скопійовано до буфера обміну</string>\n    <string name=\"db_handle\">Операції з базою даних</string>\n    <string name=\"db_name_no_alter\">Назва бази даних не змінилася</string>\n    <string name=\"change_db\">Перемкнути базу даних</string>\n    <string name=\"choose_file\">Вибрати файл</string>\n    <string name=\"create_file\">Створити новий файл</string>\n    <string name=\"create_pass_key_file_des\">Створіть новий файл, щоб використовувати його як ключ</string>\n    <string name=\"choose_pass_key_file_des\">Виберіть будь-який файл, щоб використовувати його як ключ</string>\n    <string name=\"create_pass_key_success\">Створення ключа [%s] виконано</string>\n    <string name=\"error_pass_null\">Установіть пароль бази даних</string>\n    <string name=\"error_enter_pass_null\">Введіть підтвердження пароля</string>\n    <string name=\"hint_db_create_success\">Створено базу даних [%s]</string>\n    <string name=\"all\">Елементи</string>\n    <string name=\"setting\">Налаштування</string>\n    <string name=\"db_setting\">Налаштування бази даних</string>\n    <string name=\"error_uri\">Помилка URI</string>\n    <string name=\"error_open_db_key_empty\">Відсутній ключ</string>\n    <string name=\"error_open_db_pass_error\">Неправильний пароль</string>\n    <string name=\"error_open_db\">Не вдалося відкрити базу даних. Неправильний пароль, чи потрібен ключ\\?</string>\n    <string name=\"error_group_id_null\">ID групи не має значення</string>\n    <string name=\"create_time\">Створено %s</string>\n    <string name=\"expire_time\">Чинний до %s</string>\n    <string name=\"hint_attr\">Додаткові властивості</string>\n    <string name=\"attachment\">Вкладення</string>\n    <string name=\"del_group\">Видалити групу</string>\n    <string name=\"edit_group\">Редагувати групу</string>\n    <string name=\"success\">Успішно</string>\n    <string name=\"fail\">Помилка</string>\n    <string name=\"del_entry\">Видалити елемент</string>\n    <string name=\"copy_password\">Копіювати пароль</string>\n    <string name=\"undo_grouped\">Групу переміщено до вказаного розташування</string>\n    <string name=\"safety_set\">Налаштування безпеки</string>\n    <string name=\"modify_db_pass\">Змінити пароль бази даних</string>\n    <string name=\"out_db\">Експортувати базу даних</string>\n    <string name=\"db_name_modify\">Назву бази даних змінено</string>\n    <string name=\"no_record\">Немає записів</string>\n    <string name=\"copy_to_clip\">Копіювати до буфера обміну</string>\n    <string name=\"show_pass\">Показати пароль</string>\n    <string name=\"hide_pass\">Сховати пароль</string>\n    <string name=\"hint_copy_to_clip\">Дані скопійовані до буфера обміну</string>\n    <string name=\"verify_finger\">Перевірка відбитків пальців</string>\n    <string name=\"verify_finger_fail\">Не вдалося перевірити відбиток пальця</string>\n    <string name=\"hint_protect_str\">Захищене поле</string>\n    <string name=\"error_attr_str_null\">Назва поля порожня</string>\n    <string name=\"lose_time\">Термін дії</string>\n    <string name=\"auto_lock_db_time_summary\">Автоблокування бази даних за %s, якщо не виконано жодних операцій</string>\n    <string name=\"hint_input_title\">Назва</string>\n    <string name=\"hint_input_user_name\">Ім’я користувача</string>\n    <string name=\"notice\">Примітка</string>\n    <string name=\"lower\">Символ нижнього регістру</string>\n    <string name=\"choose_dir\">Зберегти в поточній групі</string>\n    <string name=\"hint_input_attr_str_key\">Назва поля</string>\n    <string name=\"open_whit_img\">Відкрити за допомогою внутрішнього переглядача зображень</string>\n    <string name=\"save_db_success\">Базу даних збережено</string>\n    <string name=\"hint_input_url\">Адреса посилання</string>\n    <string name=\"date\">Дата</string>\n    <string name=\"normal_account\">Звичайний обліковий запис</string>\n    <string name=\"add_more\">Додати ще</string>\n    <string name=\"create_entry_no_save\">Вийти з поточної сторінки без збереження запису\\?</string>\n    <string name=\"hint_input_attr_str_value\">Значення поля</string>\n    <string name=\"save_file_success\">Файл [%s] було збережено</string>\n    <string name=\"txt_viewer\">Переглядач файлів</string>\n    <string name=\"error_pass\">Помилка пароля</string>\n    <string name=\"db_pass_no_alter\">Новий пароль такий самий, як і старий</string>\n    <string name=\"update_group_fail\">Не вдалося оновити відомості про групу</string>\n    <string name=\"update_group_success\">Відомості про групу оновлено</string>\n    <string name=\"cover_cloud\">Хмарна</string>\n    <string name=\"afs\">Файлова система Android</string>\n    <string name=\"set_key_theme_style_title\">Стиль теми</string>\n    <string name=\"send_email_fail\">Спочатку встановіть застосунок для надсилання електронних листів</string>\n    <string name=\"open_quick_unlock\">Швидке розблокування</string>\n    <string name=\"auto_lock_db_time\">Автоблокування бази даних</string>\n    <string name=\"sort\">Сортувати</string>\n    <string name=\"kpa_totp\">Токен</string>\n    <string name=\"notify_quick_unlock_start\">База даних заблокована, швидке розблокування увімкнено</string>\n    <string name=\"db_unlock\">Розблокувати базу даних</string>\n    <string name=\"set_delete_no_into_recycle_bin\">Видалити записи з бази даних назавжди</string>\n    <string name=\"ime_label\">Безпечна клавіатура KeePassA</string>\n    <string name=\"no_totp_token\">Немає токена TOTP</string>\n    <string name=\"set_key_sum_main_show_entries\">Показувати всі елементи після розблокування бази даних</string>\n    <string name=\"set_key_title_auto_lock_database_turn_off\">Ні</string>\n    <string name=\"set_key_praise_value\">Оцінити KeePassA</string>\n    <string name=\"sync_db\">Синхронна база даних</string>\n    <string name=\"net_error\">Ненормальна мережа</string>\n    <string name=\"notify_db_locked\">База даних заблокована</string>\n    <string name=\"totp_len\">Довжина:</string>\n    <string name=\"shrink\">Стиснути</string>\n    <string name=\"ui_setting\">Налаштування інтерфейсу користувача</string>\n    <string name=\"set_key_title_opr_env_check\">Перевірка робочого середовища</string>\n    <string name=\"set_key_title_opr_env_check_turn_on\">Увімкнено</string>\n    <string name=\"other\">Інше</string>\n    <string name=\"set_key_title_fillet_bg_icon_turn_off\">Вимкнено</string>\n    <string name=\"url\">URL-адреса</string>\n    <string name=\"ime_entry_other_info\">Заповнити інші поля</string>\n    <string name=\"set_key_title_main_allow_show_entries\">Домашня сторінка</string>\n    <string name=\"expand\">Розгорнути</string>\n    <string name=\"ref_entry\">Довідковий запис</string>\n    <string name=\"multi_choice\">Множинний вибір</string>\n    <string name=\"set_summary_screen_lock_off\">Не блокувати базу даних</string>\n    <string name=\"set_title_screen_lock\">Блокування екрана</string>\n    <string name=\"close\">Закрити</string>\n    <string name=\"Alipay\">Alipay</string>\n    <string name=\"error_webdav_end_suffix\">Використовуйте шлях до теки. Наприклад: «https://example.org/dav/kpa/»</string>\n    <string name=\"set_key_title_fillet_bg_icon\">Заокруглені кути піктограм списку</string>\n    <string name=\"set_key_title_fillet_bg_icon_turn_on\">Увімкнено</string>\n    <string name=\"set_key_main_show_totp_tab_off\">Не показувати категорію TOTP</string>\n    <string name=\"set_key_theme_style_summary\">Системна</string>\n    <string name=\"kpa_copy\">Копіювати</string>\n    <string name=\"hint_cloud_file_already_exist\">%1$s вже існує. Перезаписати цей файл\\?</string>\n    <string name=\"one_drive_load_user_failure\">Не вдалося завантажити відомості про користувача.</string>\n    <string name=\"hint_full_fingerprint\">Відбиток пальця використовується для розблокування бази даних в інтерфейсі швидкого розблокування та інтерфейсі розблокування бази даних, KeePassA шифрує та зберігає головний пароль вашої бази та ключову інформацію в локальному сховищі ключів системи Android, а також використовує авторизацію відбитків пальців для захисту. Тільки ваш відбиток пальця може розблокувати базу даних.</string>\n    <string name=\"hint_background_start\">Перейдіть до&lt;font color=#FF0000&gt;【%s】&lt;/font&gt;і надайте дозволи на показ фонових спливних вікон для поліпшення взаємодії зі службою автозаповнення.&lt;br&gt; &lt;font color=#FF0000&gt;KeePassA показує відповідний інтерфейс для відкриття бази даних за використання функції автозаповнення&lt;/font&gt;</string>\n    <string name=\"quick_pass_len_summary\">Довжина короткого пароля: %s</string>\n    <string name=\"feedback_email_msg\">Привіт, я KeePassA, маєте запитання\\?<br/> <br/> <br/> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ <br/> Марка: %s <br/> Модель: %s <br/> Версія системи: %s <br/> Роздільна здатність: %s <br/> Версія застосунку: %s</string>\n    <string name=\"dropbox_msg\">Дотримуючись принципу поваги до приватності користувачів, KeepPassA не запитуватиме дозволу доступу до всіх файлів у вашій теці Dropbox, KeePassA &lt;font color = # FF0000&gt; потребує доступу лише до теки 【Apps/KeePassA】 &lt;/ font &gt;.&lt;br /&gt;Отже: &lt;br /&gt; &lt;b&gt;Якщо у вас вже є база даних KeePass, надайте KeePassA доступ до теки 【Apps/KeePassA】в KeePassA та збережіть базу даних KeePass у теці 【Application/KeePassA】 після завершення авторизації.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt; Якщо ви створюєте нову базу даних KeePass, наведені вже операції не потрібні. Після створення бази даних, KeePassA автоматично створює відповідну теку та завантажує базу даних до каталогу 【Apps/KeePassA】 Dropbox.</string>\n    <string name=\"file_conflict_msg\">Конфлікт між даними хмари та локальними даними. Виберіть спосіб покриття. Конфліктні записи:\n\\n %s</string>\n    <string name=\"hint_finger_print_verfiy\">Повторно перевірте свої відбитки пальців, щоб використовувати нові налаштування</string>\n    <string name=\"hint_fingerprint_modify\">Перевірте, чи змінився відбиток пальця. Для безпеки пароля скиньте функцію розблокування відбитком пальця</string>\n    <string name=\"warning_rooted\">Не відкривайте базу даних на телефоні з розблокованим root, щоб захистити її</string>\n    <string name=\"hint_security_green\">\"Застосунок розміщено в безпечному середовищі \"</string>\n    <string name=\"hint_security_red\">Ваш пристрій має розблокований root, а база даних розміщена в дуже небезпечному середовищі</string>\n    <string name=\"donate_desc\">[KeePassA](https://github.com/AriaLyy/KeepassA) — це вільне програмне забезпечення. Зробіть внесок, щоб підтримати нашу роботу, будемо щиро вдячні.</string>\n    <string name=\"dev_birthday\">Сьогодні День народження🎂 розробника. Придбаєте йому пивця\\?</string>\n    <string name=\"totp_defaule\">Налаштування усталеного токена RFC 6238</string>\n    <string name=\"totp_steam\">Налаштування Steam-токена</string>\n    <string name=\"totp_time\">Раз(ів):</string>\n    <string name=\"license\">Ліцензія на вільне програмне забезпечення</string>\n    <string name=\"one_drive_hint\">Щоб захистити свою приватність, KeepassA отримає доступ лише до каталогу&lt;font color=#FF0000&gt;【application/KeepassA】&lt;/font&gt;. Якщо ви хочете прочитати базу даних Keepass з OneDrive, &lt;font color=#FF0000&gt;розмістіть базу даних у цьому каталозі.&lt;/font&gt;</string>\n    <string name=\"hint_open_background_start\">Відкрийте дозвіл на витягування у фоновому режимі, інакше не можливо використовувати автозаповнення</string>\n    <string name=\"error_pass_unfit\">Двічі введено невідповідні паролі</string>\n    <string name=\"help_pass_key\">Файл ключа еквівалентний ключу для відкриття даних і зазвичай значно складніший і надійніший, ніж пароль. Але сховати його також важче. Якщо ви зберігаєте базу даних у хмарі, не розміщуйте ключ разом з базою даних.\n\\n Ви можете вибрати будь-який файл ключем для відкриття бази даних: світлину чи файлу TXT.\n\\n⚠️ Примітка: якщо ця світлина або документ TXT будуть змінені, ви не зможете відкрити базу даних! !!\n\\n Радимо використовувати ключами файли лише для читання.</string>\n    <string name=\"expire\">Термін дії закінчився &lt;font color=#FF0000&gt;%s&lt;/font&gt;</string>\n    <string name=\"hint_del_group\">Справді видалити групу &lt;font color=#FF0000&gt;%s&lt;/font&gt;】\\?&lt;br&gt;Це перемістить її в кошик, звідки ви зможете відновити її пізніше.</string>\n    <string name=\"hint_del_group_no_recycle\">Справді видалити групу 【&lt;font color=#FF0000&gt;%s&lt;/font&gt;】\\?</string>\n    <string name=\"hint_del_entry\">Справді видалити елемент 【&lt;font color=#FF0000&gt;%s&lt;/font&gt;】\\? &lt;br&gt;Це перемістить його в кошик, звідки ви зможете відновити його пізніше.</string>\n    <string name=\"hint_del_entry_no_recycle\">Справді видалити запис 【&lt;font color=#FF0000&gt;%s&lt;/font&gt;】назавжди\\?</string>\n    <string name=\"hint_group_desc\">Група-%s елементів</string>\n    <string name=\"undo_entry\">Відновлено…</string>\n    <string name=\"hint_copy_totp\">Токен TOTP скопійовано до буфера обміну</string>\n    <string name=\"create_totp\">Згенерувати OTP-токен</string>\n    <string name=\"modify_db_name\">Змінити назву бази даних</string>\n    <string name=\"hint_query\">Ім\\'я користувача, назва, URL, додаткові атрибути …</string>\n    <string name=\"ban_fingerprint\">Немає розблокування відбитком пальця</string>\n    <string name=\"hint_quick_unlock\">Введіть останні %s цифри пароля бази даних</string>\n    <string name=\"hint_input_tag\">Теґ1, Теґ2</string>\n    <string name=\"hint_input_cover_url\">Перезаписати посилання</string>\n    <string name=\"create_entry\">Створити запис</string>\n    <string name=\"save\">Зберегти</string>\n    <string name=\"len\">Довжина</string>\n    <string name=\"upper\">Символ верхнього регістру</string>\n    <string name=\"numer\">Цифри</string>\n    <string name=\"minus\">Мінус</string>\n    <string name=\"underline\">Підкреслення</string>\n    <string name=\"space\">Пробіл</string>\n    <string name=\"special\">Спеціальний</string>\n    <string name=\"bracket\">Дужки</string>\n    <string name=\"pass_generater\">Генератор паролів</string>\n    <string name=\"error_genera_params\">Виберіть принаймні один тип генерації пароля</string>\n    <string name=\"hint_pass_null\">Пароль порожній</string>\n    <string name=\"time\">Час</string>\n    <string name=\"quick_use_fingerprint\">Використовувати відбиток пальця для швидкого розблокування</string>\n    <string name=\"use_full_fingerprint\">Повне розблокування відбитками пальців</string>\n    <string name=\"warning\">Попередження</string>\n    <string name=\"add_custom_str\">Створити власні поля</string>\n    <string name=\"del_attr_str\">Видалити власні поля</string>\n    <string name=\"del_attr_file\">Видалити вкладення</string>\n    <string name=\"error_attr_file_too_large\">Скоротіть використання пам\\'яті, не додаючи файли розміром понад 10 МБ</string>\n    <string name=\"download_file\">Зберегти вкладення на SD-карту</string>\n    <string name=\"open_whit_text\">Відкрити у внутрішньому текстовому редакторі</string>\n    <string name=\"open_whit_other\">Зберегти в кеші та відкрити за допомогою системного застосунку</string>\n    <string name=\"save_db_fail\">Не вдалося зберегти базу даних</string>\n    <string name=\"hint_db_pass_modify_success\">Використовуйте новий пароль бази даних, щоб відкрити базу даних наступного разу</string>\n    <string name=\"hint_db_pass_modify\">Пароль бази даних змінено</string>\n    <string name=\"app_feedback\">Відгук</string>\n    <string name=\"error_db_pass_too_short\">Щоб захистити свої дані, створіть пароль бази даних довшим ніж 6 символів</string>\n    <string name=\"autofill_sign_in_prompt\">Автозаповнення пароля за допомогою KeePassA</string>\n    <string name=\"no_matching_entry\">Немає відповідного елемента</string>\n    <string name=\"relevance_db\">Пов’язані дані</string>\n    <string name=\"modify_time\">Змінити час</string>\n    <string name=\"title\">Назва</string>\n    <string name=\"feedback\">Відгук ^_^</string>\n    <string name=\"submit\">Надіслати</string>\n    <string name=\"setting_miui_background_start\">Налаштування &gt; Налаштування програм &gt; Керування програмами &gt; KeePassA &gt; Керування дозволами</string>\n    <string name=\"setting_vivo_background_start\">Налаштування &gt; Додаткові налаштування &gt; Керування дозволами &gt; KeePassA &gt; Налаштування дозволами &gt; Фонове спливне вікно</string>\n    <string name=\"hint_save_auto_fill\">Чи пов\\'язувати назву пакунка【&lt;font color=#FF0000&gt;%s&lt;/font&gt;】з записом【&lt;font color=#FF0000&gt;%s&lt;/font&gt;】</string>\n    <string name=\"mark_not_exit\">Крамниці застосунків не існує</string>\n    <string name=\"set_open_auto_fill_title\">Служба автозаповнення</string>\n    <string name=\"other_set\">Інші налаштування</string>\n    <string name=\"clean_clip_time\">Очищення буфера обміну</string>\n    <string name=\"clean_clip_time_summary\">Після копіювання пароля очистити буфер обміну за %s</string>\n    <string name=\"set_key_value\">Вибрати мову</string>\n    <string name=\"cover\">Обкладинка</string>\n    <string name=\"merge\">Об\\'єднати</string>\n    <string name=\"cover_local\">Локальна</string>\n    <string name=\"auth\">Auth</string>\n    <string name=\"invalid\">Недійсний</string>\n    <string name=\"no_file\">Немає файлу</string>\n    <string name=\"login\">Увійти</string>\n    <string name=\"helper_webdav\">наприклад: https://dav.example.org/dav/[ваша тека]/xxx.kdbx</string>\n    <string name=\"helper_webdav_dir\">наприклад: https://dav.example.org/dav/[ваша тека]/</string>\n    <string name=\"hint_webdav_url\">URL-адреса файлу</string>\n    <string name=\"hint_please_input\">Введіть %s</string>\n    <string name=\"db_file_no_exist\">Файл бази даних не існує</string>\n    <string name=\"invalid_auth\">Не вдалося авторизувати. Повторіть спробу.</string>\n    <string name=\"quick_pass_type\">Розташування перехоплення короткого пароля</string>\n    <string name=\"des_quick_unlock\">Розблокування бази даних за допомогою короткого пароля</string>\n    <string name=\"error_short_pass_lenght\">Створіть короткий пароль з 6 або менше символів</string>\n    <string name=\"error_short_pass_short\">Створіть короткий пароль із 3 або більше символів</string>\n    <string name=\"quick_pass_len_title\">Довжина короткого пароля</string>\n    <string name=\"hint_kdbx_name\">Неправильна назва суфікса. Назвою суфікса має бути .kdbx</string>\n    <string name=\"start_upload_db\">Почати передавання бази даних на мережний диск</string>\n    <string name=\"unlocked\">Розблоковано</string>\n    <string name=\"notify_channel_db_open\">Сповіщення про розблокування бази даних</string>\n    <string name=\"search\">Пошук</string>\n    <string name=\"disable\">Вимкнути</string>\n    <string name=\"quick_unlock\">Швидке розблокування</string>\n    <string name=\"version_log\">Журнал оновлення</string>\n    <string name=\"closed\">Закрито</string>\n    <string name=\"upload\">Вивантажити</string>\n    <string name=\"open1\">відкрити</string>\n    <string name=\"add_attr_file\">Додати вкладення</string>\n    <string name=\"hint_please_open_database\">Відкрийте базу даних</string>\n    <string name=\"set_delete_setting\">Видалити налаштування</string>\n    <string name=\"set_delete_no_into_recycle_bin_des\">Перемістити видалені елементи в кошик</string>\n    <string name=\"donate\">Допомогти</string>\n    <string name=\"totp_Arithmetic\">Арифметика:</string>\n    <string name=\"hint_quick_use_fingerprint\">Використовувати лише відбиток пальця для розблокування на сторінці швидкого розблокування</string>\n    <string name=\"hint_security_yellow\">Попередження, запущене в симуляторі, це середовище додасть деякі непередбачувані ризики базі даних</string>\n    <string name=\"totp_custom\">Використовувати власні налаштування</string>\n    <string name=\"totp_custom_hint\">Власні налаштування</string>\n    <string name=\"totp_key_error\">Не вдалося створити код TOTP, неправильний ключ</string>\n    <string name=\"sort_chart_desc\">Перша буква за спаданням (Я-А)</string>\n    <string name=\"sort_chart_asc\">Перша буква за зростанням (А-Я)</string>\n    <string name=\"sort_time_asc\">Давніші-новіші</string>\n    <string name=\"sort_time_desc\">Новіші-давніші</string>\n    <string name=\"set_open_kpa_ime_title\">Безпечна клавіатура</string>\n    <string name=\"error_uri_grant_permission\">Помилка авторизації URI</string>\n    <string name=\"move\">Перемістити</string>\n    <string name=\"hint_not_nore_info\">Немає інших відомостей</string>\n    <string name=\"set_key_sum_main_show_history\">Показувати історію після розблокування бази даних</string>\n    <string name=\"set_key_title_opr_env_check_turn_off\">Вимкнено</string>\n    <string name=\"cur_app_not_autofill\">Без автозаповнення</string>\n    <string name=\"set_key_title_show_state_bar\">Панель стану</string>\n    <string name=\"set_key_title_show_state_bar_turn_on\">Показати</string>\n    <string name=\"set_key_title_show_state_bar_turn_off\">Сховати</string>\n    <string name=\"set_key_title_close_load_anim\">Завантаження анімації</string>\n    <string name=\"set_key_title_close_load_anim_turn_on\">Увімкнено</string>\n    <string name=\"set_key_title_close_load_anim_turn_off\">Вимкнено</string>\n    <string name=\"s_default\">Типово</string>\n    <string name=\"customize\">Налаштувати</string>\n    <string name=\"set_key_title_auto_lock_database\">Автоблокування бази даних</string>\n    <string name=\"set_key_title_auto_lock_database_turn_on\">Так</string>\n    <string name=\"set_summary_screen_lock_on\">Автоблокування бази даних</string>\n    <string name=\"error_webdav_end_dav_suffix\">Шлях /dav/ не дозволений. Використовуйте дійсний шлях, наприклад: «https://example.org/dav/kpa/»</string>\n    <string name=\"translate_language\">Перекласти застосунок своєю мовою</string>\n    <string name=\"get_token_fail\">Не вдалося отримати токен</string>\n    <string name=\"one_drive_init_failure\">Помилка ініціалізації OneDrive</string>\n    <string name=\"open_permissions\">Відкрити дозволи</string>\n    <string name=\"set_key_main_show_totp_tab_title\">Домашня сторінка показує категорію TOTP</string>\n    <string name=\"set_key_main_show_totp_tab_on\">Показати категорію TOTP</string>\n    <string name=\"error_connect_play\">Не вдалося під\\'єднатися до служби Google</string>\n    <string name=\"disposable\">Одноразово</string>\n    <string name=\"year\">Щорічно</string>\n    <string name=\"monthly\">Щомісячно</string>\n    <string name=\"error_donate\">Не вдалося надіслати допомогу</string>\n    <string name=\"thank_donate\">Дякуємо за ваш внесок.</string>\n    <string name=\"google_play\">Google Play</string>\n    <string name=\"my_collection\">Моя колекція</string>\n    <string name=\"current_collection_num\">%1$s колекції</string>\n    <string name=\"no_collection\">Немає колекції</string>\n    <string name=\"error_open_db_webdav\">Не вдалося відкрити базу даних, файл WebDAV не існує.</string>\n    <string name=\"helper_webdav_service\">Вибрати службу WebDAV</string>\n    <string name=\"error_is_root\">Це вже кореневий каталог</string>\n    <string name=\"select_cur_path\">Вибрати теку</string>\n    <string name=\"miui_permissions\">Системі miui потрібен дозвіл на відкриття фонового спливного інтерфейсу, щоб використовувати функцію автозаповнення.</string>\n    <string name=\"error_keystore\">Виняток сховища ключів, повторіть спробу</string>\n    <string name=\"error_qr_code_str\">QR-код неправильний. Переконайтеся, що QR-код підтримується Google Authenticator</string>\n    <string name=\"select_save_path\">Вибрати шлях збереження</string>\n    <string name=\"hostname\">Ім\\'я хосту</string>\n    <string name=\"port\">Порт</string>\n    <string name=\"webdav_port_hint\">Порт</string>\n    <string name=\"webdav_host_name_hint\">Введіть своє ім\\'я хосту</string>\n    <string name=\"webdav_port_name_null\">Ім’я хосту не може бути порожнім</string>\n    <string name=\"privacy_agreement\">Угода про приватність</string>\n    <string name=\"ime_hint_open_auto_fill\">Увімкніть службу автозаповнення</string>\n    <string name=\"please_open_proxy\">відкрийте проксі</string>\n    <string name=\"invalid_img\">Недійсне зображення</string>\n    <string name=\"open_setting\">Налаштування</string>\n    <string name=\"hint_open_backgroun_start\">Ви налаштували автозаповнення, але KeePassA не має відповідних дозволів. Авторизуйте спливне вікно фонової служби KeePassA на сторінці налаштувань.</string>\n    <string name=\"preemptive\">Пріоритетний</string>\n    <string name=\"fail_unsupported_Systems_O\">Підтримка лише системи P або вище</string>\n    <string name=\"hint_webdav_jgy\">Використовуйте пароль в [Обліковий запис-&gt;Безпека-&gt;Керування сторонніми застосунками].</string>\n    <string name=\"app_debug\">Отримати файл журналу</string>\n    <string name=\"hint_auto_fill_save\">Пов\\'язати поточні дані з наявними записами\\?</string>\n    <string name=\"title_tip_of_day\">Порада дня</string>\n    <string name=\"hint_dont_show_tips\">Не показувати поради</string>\n    <string name=\"base_info\">Відомості про базу</string>\n    <string name=\"tag\">Мітка</string>\n    <string name=\"add_attr_str\">Додати атрибут</string>\n    <string name=\"hint_confirm\">Підтвердити пароль</string>\n    <string name=\"collection\">Збірка</string>\n    <string name=\"totp\">TOTP</string>\n    <string name=\"delete\">Видалити</string>\n    <string name=\"create_tag\">Нова мітка</string>\n    <string name=\"error_file\">Файл аномальний, видаліть його &lt;font color=#FF0000&gt;%s&lt;/font&gt;</string>\n    <string name=\"tag_name\">Назва мітки</string>\n    <string name=\"add_tag\">Додати мітку</string>\n    <string name=\"open_file\">Відкрити файл</string>\n    <string name=\"modify_str_attr\">Змінити властивості</string>\n    <string name=\"not_souper_otp\">Непідтримуваний totp</string>\n    <string name=\"modify_totp\">Змінити токен OTP</string>\n    <string name=\"error_totp\">Помилка OTP</string>\n    <string name=\"file_save_success\">Вкладення успішно збережено</string>\n</resources>"
  },
  {
    "path": "app/src/main/res/values-v29/styles.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n  <style name=\"AppTheme.NoActionBar\" parent=\"BaseTheme\">\n    <item name=\"windowActionBar\">false</item>\n    <item name=\"windowNoTitle\">true</item>\n    <item name=\"colorPrimary\">@color/colorPrimary</item>\n    <item name=\"colorPrimaryDark\">@color/colorPrimaryDark</item>\n    <item name=\"colorAccent\">@color/colorAccent</item>\n    <item name=\"android:forceDarkAllowed\">true</item>\n    <!--    <item name=\"android:windowIsTranslucent\">true</item>-->\n    <!--    处理状态栏颜色 -->\n    <!--    <item name=\"android:windowTranslucentStatus\">false</item>-->\n    <!--    <item name=\"android:windowTranslucentNavigation\">false</item>-->\n    <item name=\"android:statusBarColor\">@color/colorPrimary</item>\n    <item name=\"android:windowActivityTransitions\">true</item>\n\n  </style>\n</resources>"
  },
  {
    "path": "app/src/main/res/values-zh-rCN/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string-array name=\"quick_pass_type_entries\">\n        <item>数据库密码前面%s位</item>\n        <item>数据库密码末尾%s位</item>\n    </string-array>\n\n\n    <string-array name=\"clean_clip_entries\">\n        <item>30秒</item>\n        <item>1分钟</item>\n        <item>2分钟</item>\n        <item>5分钟</item>\n    </string-array>\n\n\n    <string-array name=\"quick_lock_entries\">\n        <item>2分钟</item>\n        <item>5分钟</item>\n        <item>10分钟</item>\n        <item>20分钟</item>\n    </string-array>\n\n    <string-array name=\"out_db_entries\">\n        <item>Keepass2数据库</item>\n        <item>不加密的XML文件</item>\n        <item>不加密的CSV文件</item>\n    </string-array>\n\n\n    <string-array name=\"v3_add_mor_item\">\n        <item>备注</item>\n        <item>文件附件</item>\n        <item>失效时间</item>\n    </string-array>\n\n    <string-array name=\"v4_add_mor_item\">\n        <item>备注</item>\n        <item>自定义字段</item>\n        <item>文件附件</item>\n        <item>TOTP</item>\n        <item>标签</item>\n        <item>链接</item>\n        <item>失效时间</item>\n    </string-array>\n\n    <!--  创建数据库时的默认群组 -->\n    <string-array name=\"create_normal_group\">\n        <item>邮箱</item>\n        <item>工作</item>\n        <item>财务</item>\n        <item>娱乐</item>\n        <item>回收站</item>\n    </string-array>\n\n    <string-array name=\"auto_fill_hint_label\">\n        <item>邮箱</item>\n        <item>手机</item>\n        <item>用户</item>\n        <item>账号</item>\n    </string-array>\n\n    <string name=\"db\">数据库</string>\n    <string name=\"db1\"><![CDATA[数据库\\t|\\t<font color=\"#757575\">%s</font>]]></string>\n    <string name=\"password\">密码</string>\n    <string name=\"key\">密钥</string>\n    <string name=\"key1\">密钥：%s</string>\n    <string name=\"open\">解锁</string>\n    <string name=\"hint_set_password\">设置密码</string>\n    <string name=\"hint_input_password\">输入6–16位密码</string>\n    <string name=\"hint_enter_password\">确认密码</string>\n    <string name=\"help_create_db\">默认情况下创建v4 KeepPass数据库</string>\n    <string name=\"help_create_group\">群组名不能超过16个字符</string>\n    <string name=\"encrypt_type\">加密方式</string>\n    <string name=\"encrypt_type_1\">仅密码</string>\n    <string name=\"encrypt_type_2\">密码 + 密钥</string>\n    <string name=\"choose_pass_key\">选择密钥</string>\n    <string name=\"hint_set_pass_key\">请选择密钥</string>\n    <string name=\"pass_key\">文件密钥</string>\n    <string name=\"helper_create_pass\">建议使用混合密码（大小写英文 + 数字 +符号）</string>\n    <string name=\"open_db\">打开数据库</string>\n    <string name=\"db_name\">输入数据库名</string>\n    <string name=\"create_db\">创建数据库</string>\n    <string name=\"next\">下一步</string>\n    <string name=\"up\">上一步</string>\n    <string name=\"hint_select_db_path\">点击设置数据保存方式</string>\n    <string name=\"hint_select_path_type\">选择数据库保存路径</string>\n    <string name=\"error_db_name_null\">请输入数据库名</string>\n    <string name=\"done\">完成</string>\n    <string name=\"enter\">确认</string>\n    <string name=\"cancel\">取消</string>\n    <string name=\"hint\">提示</string>\n    <string name=\"help_create_db_path\">推荐你使用 Dropbox、WebDAV 等云端路径，这样将可以自动同步你的数据库到云端，便于在其它设备上使用数据库。\n\\n数据库采用 AES256 加密。没有你的密码，任何人都无法打开你的数据库。</string>\n    <string name=\"help_pass_type\">仅使用密码：\n\\n打开数据库只需要你的密码；\n\\n密码+密钥：\n\\n你需要输入密码并选择密钥文件才能打开数据库。</string>\n    <string name=\"help_pass_key\">密钥文件相当于打开数据的钥匙，通常比密码复杂和可靠得多。但也更难将其隐藏，如果你将数据库保存在云端，千万不要将密钥和数据库放在一起。\n\\n你可以选择任意文件作为打开数据库的密钥，一张照片，或一个 TXT 文档。\n\\n⚠️注意：如果这张照片或这个 TXT 文档被修改，那么你将无法打开数据库！！\n\\n强烈推荐使用只读文件作为密钥。</string>\n    <string name=\"choose_file\">选择文件</string>\n    <string name=\"choose_pass_key_file_des\">选择任意文件作为你的密钥</string>\n    <string name=\"create_file\">创建新文件</string>\n    <string name=\"create_pass_key_file_des\">创建一个新文件用作你的密钥</string>\n    <string name=\"create_pass_key_success\">创建密钥【%s】成功</string>\n    <string name=\"error_pass_null\">请设置数据库密码</string>\n    <string name=\"error_enter_pass_null\">请输入确认密码</string>\n    <string name=\"error_pass_unfit\">两次密码不一致</string>\n    <string name=\"hint_db_create_success\">创建了数据库 [%s]</string>\n    <string name=\"history\">历史</string>\n    <string name=\"history_record\">历史记录</string>\n    <string name=\"edit\">编辑</string>\n    <string name=\"change_db\">切换数据库</string>\n    <string name=\"all\">条目</string>\n    <string name=\"setting\">设置</string>\n    <string name=\"db_setting\">数据库设置</string>\n    <string name=\"app_setting\">应用程序设置</string>\n    <string name=\"welcome\">欢迎使用</string>\n    <string name=\"help_input_db_pass\">输入数据库密码</string>\n    <string name=\"need_key\">需要密钥？</string>\n    <string name=\"error_uri\">URI 错误</string>\n    <string name=\"error_input_pass_null\">请输入数据库密码</string>\n    <string name=\"error_open_db\">无法打开数据库。密码不正确，还是需要密钥？</string>\n    <string name=\"error_open_db_key_empty\">缺少密钥</string>\n    <string name=\"error_open_db_pass_error\">数据库密码错误</string>\n    <string name=\"error_open_db_key_invalid\">无效的密钥</string>\n    <string name=\"error_open_db_version_error\">数据库版本错误</string>\n    <string name=\"error_open_db_signature_error\">数据库签名错误</string>\n    <string name=\"error_open_db_algorithm_error\">无效的算法</string>\n    <string name=\"error_open_db_arcfour_error\">无效的流加密算法</string>\n    <string name=\"hint_group_desc\">群组-%s个条目</string>\n    <string name=\"error_group_id_null\">group ID 为空</string>\n    <string name=\"error_entry_id_null\">entry ID 为空</string>\n    <string name=\"create_time\">创建于 %s</string>\n    <string name=\"expire_time\">将于 %s 过期</string>\n    <string name=\"hint_attr\">高级属性</string>\n    <string name=\"attachment\">文件附件</string>\n    <string name=\"hint_ex_property\">额外属性</string>\n    <string name=\"expire\">&lt;font color=#FF0000&gt;%s&lt;/font&gt;过期</string>\n    <!--  <string name=\"hint_exit_app\">再按一次退出程序</string>-->\n    <string name=\"del_group\">删除群组</string>\n    <string name=\"edit_group\">编辑群组</string>\n    <string name=\"success\">成功</string>\n    <string name=\"fail\">失败</string>\n    <string name=\"del_entry\">删除条目</string>\n    <string name=\"del_history\">删除历史记录</string>\n    <string name=\"copy_user_name\">复制用户名</string>\n    <string name=\"copy_password\">复制密码</string>\n    <string name=\"copy_totp\">复制TOTP令牌</string>\n    <string name=\"hint_del_group\">确认要删除群组【&lt;font color=#FF0000&gt;%s&lt;/font&gt;】吗？&lt;br&gt; 该操作将会将群组移动到回收站中，你可以在回收站中恢复该群组。</string>\n    <string name=\"hint_del_group_no_recycle\">真要删除群组【&lt;font color=#FF0000&gt;%s&lt;/font&gt;】？</string>\n    <string name=\"hint_del_entry\">真要删除条目【&lt;font color=#FF0000&gt;%s&lt;/font&gt;】？&lt;br&gt; 该操作将会将此条目移动到回收站中，你稍后可从回收站恢复该条目。</string>\n    <string name=\"hint_del_entry_no_recycle\">真要永久删除条目【&lt;font color=#FF0000&gt;%s&lt;/font&gt;】？</string>\n    <string name=\"group_name\">群组名</string>\n    <string name=\"create_group\">创建群组</string>\n    <string name=\"error_group_name_null\">群组名称为空</string>\n    <string name=\"create_group_fail\">无法创建群组</string>\n    <string name=\"create_group_success\">已创建群组</string>\n    <string name=\"modify_group\">编辑群组</string>\n    <string name=\"error_group_no_modify\">群组没有改动</string>\n    <string name=\"title_too_long\">群组名称太长</string>\n    <string name=\"choose_icon\">选择图标</string>\n    <string name=\"undo_entry\">已恢复…</string>\n    <string name=\"undo_to\">移动到该群组</string>\n    <string name=\"undo_grouped\">群组已被移动到指定位置</string>\n    <string name=\"undo_entryed\">条目已移动到指定位置</string>\n    <string name=\"hint_copy_user\">已将用户名拷贝到剪切板</string>\n    <string name=\"hint_copy_pass\">已将密码拷贝到剪切板</string>\n    <string name=\"hint_copy_totp\">已将TOTP令牌拷贝到剪切板</string>\n    <string name=\"create_totp\">生成OTP令牌</string>\n    <string name=\"safety_set\">安全设置</string>\n    <string name=\"fingerprint_unlock\">指纹解锁</string>\n    <string name=\"db_handle\">数据库操作</string>\n    <string name=\"modify_db_name\">修改数据库名</string>\n    <string name=\"modify_db_pass\">修改数据库密码</string>\n    <string name=\"out_db\">导出数据库</string>\n    <string name=\"db_name_no_alter\">数据库名称未更改</string>\n    <string name=\"db_name_modify\">数据库名修改</string>\n    <string name=\"no_record\">没有记录</string>\n    <string name=\"hint_query\">用户名、标题、URL、高级属性…</string>\n    <string name=\"copy_to_clip\">复制到剪切板</string>\n    <string name=\"show_pass\">显示密码</string>\n    <string name=\"ope_url\">打开链接</string>\n    <string name=\"hide_pass\">隐藏密码</string>\n    <string name=\"hint_copy_to_clip\">已将数据复制到剪切板</string>\n    <string name=\"verify_finger\">指纹验证</string>\n    <string name=\"verify_finger_fail\">无法验证指纹</string>\n    <string name=\"ban_fingerprint\">无指纹解锁</string>\n    <string name=\"quick_use_fingerprint\">仅在快速解锁界面使用指纹来解锁</string>\n    <string name=\"use_full_fingerprint\">完整的指纹解锁</string>\n    <string name=\"hint_full_fingerprint\">指纹用于在快速解锁界面和数据库解锁界面中解锁数据库，KeePassA 加密并将数据库的主密码和密钥信息存储在 Android 系统的本地密钥存储库，并使用指纹授权进行保护。只有你的指纹才能解锁数据库。</string>\n    <string name=\"hint_quick_unlock\">输入你数据库密码的后%s位</string>\n    <string name=\"hint_input_title\">标题</string>\n    <string name=\"hint_input_user_name\">用户名</string>\n    <string name=\"hint_input_url\">链接地址</string>\n    <string name=\"notice\">备注</string>\n    <string name=\"hint_input_tag\">标签1，标签2</string>\n    <string name=\"hint_input_cover_url\">覆盖链接</string>\n    <string name=\"create_entry\">创建条目</string>\n    <string name=\"save\">保存</string>\n    <string name=\"len\">长度</string>\n    <string name=\"upper\">大写字母</string>\n    <string name=\"lower\">小写字母</string>\n    <string name=\"numer\">数字</string>\n    <string name=\"minus\">减号</string>\n    <string name=\"underline\">下划线</string>\n    <string name=\"space\">空格</string>\n    <string name=\"special\">特殊字符</string>\n    <string name=\"bracket\">括号</string>\n    <string name=\"pass_generater\">密码生成器</string>\n    <string name=\"error_genera_params\">请至少选择一个密码生成类型</string>\n    <string name=\"hint_pass_null\">密码为空</string>\n    <string name=\"date\">日期</string>\n    <string name=\"time\">时间</string>\n    <string name=\"choose_dir\">保存到当前群组</string>\n    <string name=\"normal_account\">普通账号</string>\n    <string name=\"add_more\">添加更多项</string>\n    <string name=\"warning\">警告</string>\n    <string name=\"create_entry_no_save\">是否退出当前页面而不保存条目？</string>\n    <string name=\"add_custom_str\">创建自定义字段</string>\n    <string name=\"hint_input_attr_str_key\">字段名</string>\n    <string name=\"hint_input_attr_str_value\">字段值</string>\n    <string name=\"hint_protect_str\">受保护的字段</string>\n    <string name=\"error_attr_str_null\">字段名称为空</string>\n    <string name=\"del_attr_str\">删除自定义字段</string>\n    <string name=\"del_attr_file\">删除附件</string>\n    <string name=\"error_attr_file_too_large\">请不要将大于10M的文件作为附件</string>\n    <string name=\"download_file\">保存附件到SD卡</string>\n    <string name=\"open_whit_text\">使用内部文本打开</string>\n    <string name=\"open_whit_img\">使用内部图像查看器打开</string>\n    <string name=\"open_whit_other\">保存到缓存并使用系统应用打开</string>\n    <string name=\"save_file_success\">已保存文件 [%s]</string>\n    <string name=\"txt_viewer\">文件查看器</string>\n    <string name=\"save_db_fail\">无法保存数据库</string>\n    <string name=\"error_pass\">密码错误</string>\n    <string name=\"db_pass_no_alter\">新密码与旧密码相同</string>\n    <string name=\"hint_db_pass_modify_success\">下次使用新的数据库密码打开数据库</string>\n    <string name=\"hint_db_pass_modify\">已修改数据库密码</string>\n    <string name=\"app_feedback\">提意见</string>\n    <string name=\"error_db_pass_too_short\">为了保障你的数据安全，数据库密码长度不能小于6位</string>\n    <string name=\"autofill_sign_in_prompt\">使用 KeePassA 自动填充密码</string>\n    <string name=\"hint_open_backgroun_start\">您已经设置了自动填充，但是KeePassA没有相应的权限，请在设置页授权KeePassA后台弹出界面。</string>\n    <string name=\"hint_background_start\">请转到 &lt;font color=#FF0000&gt;【%s】&lt;/font&gt; 并授予后台弹出窗口权限，以便更好地使用自动填充服务。&lt;br&gt; &lt;font color=#FF0000&gt;当你使用自动填充功能时，KeePassA 会显示相应的打开数据库的界面&lt;/font&gt;</string>\n    <string name=\"setting_miui_background_start\">设置 &gt;应用设置 &gt;应用管理 &gt;KeePassA &gt;权限管理</string>\n    <string name=\"setting_vivo_background_start\">设置 &gt;更多设置 &gt;权限管理-&gt;KeePassA &gt;权限设置 &gt;后台弹出</string>\n    <string name=\"no_matching_entry\">没有匹配的条目</string>\n    <string name=\"hint_save_auto_fill\"><![CDATA[是否将包名【<font color=\"#FF0000\">%s</font>】和条目【<font color=\"#FF0000\">%s</font>】进行关联]]></string>\n    <string name=\"relevance_db\">关联数据</string>\n    <string name=\"modify_time\">修改时间</string>\n    <string name=\"lose_time\">失效时间</string>\n    <string name=\"update_group_fail\">无法更新群组信息</string>\n    <string name=\"update_group_success\">已更新群组信息</string>\n    <string name=\"title\">标题</string>\n    <string name=\"feedback\">意见反馈 ^_^</string>\n    <string name=\"submit\">提交</string>\n    <string name=\"send_email_fail\">先安装一个可以发送电子邮件的应用程序</string>\n    <string name=\"feedback_email_msg\">你好，我是 KeePassA，你有什么问题吗？<br/> <br/> <br/> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ <br/> 手机品牌：%s <br/> 手机型号：%s <br/> 系统版本：%s <br/> 分辨率：%s <br/> 应用版本：%s</string>\n    <string name=\"mark_not_exit\">应用市场不存在</string>\n    <string name=\"set_open_auto_fill_title\">自动填充服务</string>\n    <string name=\"set_key_praise_value\">给 KeePassA 评分</string>\n    <string name=\"other_set\">其它设置</string>\n    <string name=\"open_quick_unlock\">快速解锁</string>\n    <string name=\"quick_pass_type_summary\">\"\"</string>\n    <string name=\"clean_clip_time\">清空剪切板</string>\n    <string name=\"clean_clip_time_summary\">复制密码后，%s后清空剪切板</string>\n    <string name=\"auto_lock_db_time\">自动锁定数据库</string>\n    <string name=\"auto_lock_db_time_summary\">若未进行操作，%s 分钟后自动锁定数据库</string>\n    <string name=\"quick_pass_type\">短密码截取位置</string>\n    <string name=\"des_quick_unlock\">使用短密码解锁你的数据库</string>\n    <string name=\"error_short_pass_lenght\">短密码长度不能大于6位</string>\n    <string name=\"error_short_pass_short\">短密码长度不能小于3位</string>\n    <string name=\"quick_pass_len_title\">短密码长度</string>\n    <string name=\"quick_pass_len_summary\">短密码长度：%s</string>\n    <string name=\"set_key_value\">选择语言</string>\n    <string name=\"dropbox_msg\">秉持尊重用户隐私的原则，KeePassA 不会要求获取访问你所有 Dropbox 文件的权限，KeePassA&lt;font color=#FF0000&gt; 只访问【应用/KeePassA】文件夹&lt;/font&gt;。&lt;br /&gt; 因此： &lt;br /&gt; &lt;b&gt;如果你已经有一个 KeePass 数据库，那么，你需要先授权 KeePassA 访问 KeePassA 中的【应用/KeePassA】文件夹，并在授权完成后将 KeePass 数据库保存到【应用/KeePassA】文件夹。&lt;/b&gt;&lt;br/&gt;&lt;br /&gt; 如果你在建立新的 KeepPass 数据库，则不需要上面的操作，在你创建完成数据库后，KeePassA 会自动创建对应文件夹，并将数据库上传到 Dropbox的【应用/KeePassA】目录下。</string>\n    <string name=\"cover\">覆盖</string>\n    <string name=\"file_conflict_msg\">云端数据和本地数据出现冲突，请选择覆盖方式，冲突的条目有：\\n %s</string>\n    <string name=\"cover_local\">本地</string>\n    <string name=\"cover_cloud\">云端</string>\n    <string name=\"merge\">合并</string>\n    <string name=\"sync_db\">同步数据库</string>\n    <string name=\"net_error\">网络异常</string>\n    <string name=\"auth\">授权</string>\n    <string name=\"invalid\">无效</string>\n    <string name=\"no_file\">没有文件</string>\n    <string name=\"login\">登录</string>\n    <string name=\"helper_webdav\">如：https://dav.example.org/dav/你的文件夹/xxx.kdbx</string>\n    <string name=\"helper_webdav_dir\">如：https://dav.example.org/dav/[你的文件夹]/</string>\n    <string name=\"hint_webdav_url\">文件 URL</string>\n    <string name=\"hint_please_input\">请输入%s</string>\n    <string name=\"db_file_no_exist\">数据库文件不存在</string>\n    <string name=\"invalid_auth\">无法授权。请再试一次。</string>\n    <string name=\"afs\">Android 文件系统</string>\n    <string name=\"hint_kdbx_name\">错误的后缀名，后缀名应该是\".kdbx\"</string>\n    <string name=\"start_upload_db\">开始上传数据库到你的网盘</string>\n    <string name=\"unlocked\">已解锁</string>\n    <string name=\"notify_channel_db_open\">数据库解锁通知</string>\n    <string name=\"notify_quick_unlock_start\">数据库已锁定，快速解锁已启用</string>\n    <string name=\"notify_db_locked\">数据库已锁定</string>\n    <string name=\"search\">搜索</string>\n    <string name=\"disable\">关闭</string>\n    <string name=\"quick_unlock\">快速解锁</string>\n    <string name=\"db_unlock\">解锁数据库</string>\n    <string name=\"hint_quick_use_fingerprint\">仅在快速解锁页面使用指纹解锁</string>\n    <string name=\"version_log\">升级日志</string>\n    <string name=\"hint_finger_print_verfiy\">需要重新验证指纹，才能使用新设置</string>\n    <string name=\"closed\">已关闭</string>\n    <string name=\"open1\">开启</string>\n    <string name=\"upload\">上传</string>\n    <string name=\"add_attr_file\">添加附件</string>\n    <string name=\"hint_fingerprint_modify\">检查到指纹有变化，为了你的密码安全，请重新设置指纹解锁功能</string>\n    <string name=\"hint_please_open_database\">请打开数据库</string>\n    <string name=\"save_db_success\">已保存数据库</string>\n    <string name=\"set_delete_setting\">刪除设置</string>\n    <string name=\"set_delete_no_into_recycle_bin\">从数据库中永久性删除条目</string>\n    <string name=\"set_delete_no_into_recycle_bin_des\">将删除的条目移动到回收站中</string>\n    <string name=\"warning_rooted\">为了你的数据安全，请不要在已经 root 的手机上打开数据库</string>\n    <string name=\"hint_security_green\">\"本应用处于安全环境中 \"</string>\n    <string name=\"hint_security_red\">你的设备已经 root，数据库处于一个非常危险的环境中</string>\n    <string name=\"hint_security_yellow\">警告，应用运行于模拟器中，该环境会给数据库带来一些不可预测的风险</string>\n    <string name=\"donate\">捐赠</string>\n    <string name=\"donate_desc\">[KeePassA](https://github.com/AriaLyy/KeepassA)是一款自由软件。请考虑给我们捐赠，以支持我们的工作，非常感谢你。</string>\n    <string name=\"dev_birthday\">今天是开发者生日🎂，请开发者喝啤酒？</string>\n    <string name=\"totp_defaule\">默认 RFC 6238令牌设置</string>\n    <string name=\"totp_steam\">Steam令牌设置</string>\n    <string name=\"totp_custom\">使用自定义设置</string>\n    <string name=\"totp_custom_hint\">自定义设置</string>\n    <string name=\"totp_Arithmetic\">算法：</string>\n    <string name=\"totp_time\">时间(s)：</string>\n    <string name=\"totp_len\">长度：</string>\n    <string name=\"totp_key_error\">无法生成 TOTP 代码，key 异常</string>\n    <string name=\"sort\">排序</string>\n    <string name=\"sort_chart_desc\">首字母降序 (Z-A)</string>\n    <string name=\"sort_chart_asc\">首字母升序 (A-Z)</string>\n    <string name=\"sort_time_asc\">时间降序</string>\n    <string name=\"sort_time_desc\">时间降序</string>\n    <string name=\"ime_label\">KeePassA 安全键盘</string>\n    <string name=\"set_open_kpa_ime_title\">安全键盘</string>\n    <string name=\"no_totp_token\">没有 TOTP 令牌</string>\n    <string name=\"ime_entry_other_info\">填充其它字段</string>\n    <string name=\"url\">网址</string>\n    <string name=\"hint_not_nore_info\">没有更多信息</string>\n    <string name=\"error_uri_grant_permission\">URI 授权失败</string>\n    <string name=\"license\">自由软件许可证</string>\n    <string name=\"move\">移动</string>\n    <string name=\"set_key_sum_main_show_entries\">解锁数据库后显示所有条目</string>\n    <string name=\"set_key_sum_main_show_history\">解锁数据库后显示历史记录</string>\n    <string name=\"set_key_title_main_allow_show_entries\">主页</string>\n    <string name=\"set_key_title_opr_env_check\">运行环境检查设置</string>\n    <string name=\"set_key_title_opr_env_check_turn_off\">关闭</string>\n    <string name=\"set_key_title_opr_env_check_turn_on\">打开</string>\n    <string name=\"expand\">展开</string>\n    <string name=\"shrink\">收起</string>\n    <string name=\"ui_setting\">用户界面设置</string>\n    <string name=\"cur_app_not_autofill\">不自动填充</string>\n    <string name=\"other\">其它</string>\n    <string name=\"ref_entry\">参考条目</string>\n    <string name=\"multi_choice\">多选</string>\n    <string name=\"set_summary_screen_lock_off\">不锁定数据库</string>\n    <string name=\"set_summary_screen_lock_on\">自动锁定数据库</string>\n    <string name=\"set_title_screen_lock\">屏幕锁定</string>\n    <string name=\"close\">关闭</string>\n    <string name=\"Alipay\">支付宝</string>\n    <string name=\"error_webdav_end_suffix\">使用文件夹路径。如：“https://example.org/dav/kpa/”</string>\n    <string name=\"error_webdav_end_dav_suffix\">不允许使用 /dav/ 路径。使用有效路径，如：“https://example.org/dav/kpa/”</string>\n    <string name=\"set_key_title_fillet_bg_icon\">圆形的列表图标角</string>\n    <string name=\"set_key_title_fillet_bg_icon_turn_on\">开启</string>\n    <string name=\"set_key_title_fillet_bg_icon_turn_off\">关闭</string>\n    <string name=\"set_key_title_show_state_bar\">状态栏</string>\n    <string name=\"set_key_title_show_state_bar_turn_on\">显示</string>\n    <string name=\"set_key_title_show_state_bar_turn_off\">隐藏</string>\n    <string name=\"set_key_title_close_load_anim\">加载动画</string>\n    <string name=\"set_key_title_close_load_anim_turn_on\">打开</string>\n    <string name=\"set_key_title_close_load_anim_turn_off\">关闭</string>\n    <string name=\"s_default\">默认</string>\n    <string name=\"customize\">自定义</string>\n    <string name=\"set_key_title_auto_lock_database\">自动锁定数据库</string>\n    <string name=\"set_key_title_auto_lock_database_turn_on\">是</string>\n    <string name=\"set_key_title_auto_lock_database_turn_off\">关闭</string>\n    <string name=\"get_token_fail\">获取Token失败</string>\n    <string name=\"one_drive_init_failure\">OneDrive初始化失败</string>\n    <string name=\"one_drive_hint\">为了保护你的隐私，KeePassA 仅会访问&lt;font color=#FF0000&gt;【应用/KeepassA】&lt;/font&gt;目录，如果你希望读取OneDrive的 Keepass 数据库，&lt;font color=#FF0000&gt;请将数据库存放到该目录下。&lt;/font&gt;</string>\n    <string name=\"open_permissions\">打开权限</string>\n    <string name=\"miui_permissions\">miui系统需要打开后台弹出界面的权限，才能使用自动填充功能。</string>\n    <string name=\"set_key_main_show_totp_tab_title\">主页显示TOTP分类</string>\n    <string name=\"set_key_main_show_totp_tab_on\">显示TOTP分类</string>\n    <string name=\"set_key_main_show_totp_tab_off\">不显示TOTP分类</string>\n    <string name=\"set_key_theme_style_title\">主题风格</string>\n    <string name=\"set_key_theme_style_summary\">跟随系统</string>\n    <string name=\"open_setting\">打开设置</string>\n    <string-array name=\"sek_ley_theme_style_entries\">\n        <item>跟随系统</item>\n        <item>深色模式</item>\n        <item>浅色模式</item>\n    </string-array>\n    <string name=\"error_keystore\">keystore 异常，请重试</string>\n    <string name=\"kpa_totp\">令牌</string>\n    <string name=\"kpa_copy\">复制</string>\n    <string name=\"translate_language\">将应用程序翻译成你的语言</string>\n    <string name=\"error_qr_code_str\">二维码异常，请确认该二维码是谷歌验证器支持的二维码</string>\n    <string name=\"google_play\">Google Play 商店</string>\n    <string name=\"disposable\">一次性</string>\n    <string name=\"year\">每年</string>\n    <string name=\"error_donate\">捐赠失败</string>\n    <string name=\"thank_donate\">感谢您的捐赠。</string>\n    <string name=\"error_connect_play\">无法连接到 Google 服务</string>\n    <string name=\"monthly\">每月</string>\n    <string name=\"my_collection\">我的收藏</string>\n    <string name=\"current_collection_num\">%1$s 个收藏</string>\n    <string name=\"no_collection\">没有收藏</string>\n    <string name=\"error_open_db_webdav\">无法打开数据库，因该 WebDAV 文件不存在。</string>\n    <string name=\"helper_webdav_service\">选择 WebDAV 服务</string>\n    <string name=\"error_is_root\">已经是根目录</string>\n    <string name=\"select_cur_path\">选择当前文件夹</string>\n    <string name=\"hint_cloud_file_already_exist\">%1$s已存在，是否覆盖该文件？</string>\n    <string name=\"select_save_path\">选择保存路径</string>\n    <string name=\"one_drive_load_user_failure\">无法加载用户信息。</string>\n    <string name=\"hint_open_background_start\">请打开后台弹出权限，否则无法使用自动填充功能</string>\n    <string name=\"hostname\">主机名</string>\n    <string name=\"port\">端口</string>\n    <string name=\"webdav_host_name_hint\">请输入你的主机名</string>\n    <string name=\"webdav_port_hint\">端口</string>\n    <string name=\"webdav_port_name_null\">Hostname 不能为空</string>\n    <string name=\"privacy_agreement\">隐私协议</string>\n    <string name=\"ime_hint_open_auto_fill\">请打开自动填充服务</string>\n    <string name=\"please_open_proxy\">由于众所周知的网络原因，为了能顺畅使用onedrive服务，请使用合适的网络工具</string>\n    <string name=\"invalid_img\">无效的图片</string>\n    <string name=\"preemptive\">预置模式</string>\n    <string name=\"fail_unsupported_Systems_O\">仅支持P以上的系统</string>\n    <string name=\"hint_webdav_jgy\">请使用【账户信息->安全选项->第三方应用】中的密码</string>\n    <string name=\"app_debug\">取出日志文件</string>\n    <string name=\"hint_auto_fill_save\">将当前数据关联到已有条目？</string>\n    <string name=\"hint_dont_show_tips\">不再显示</string>\n    <string name=\"title_tip_of_day\">每日提示</string>\n    <string name=\"base_info\">基础信息</string>\n    <string name=\"tag\">标签</string>\n    <string name=\"collection\">收藏</string>\n    <string name=\"add_attr_str\">增加属性</string>\n    <string name=\"delete\">删除</string>\n    <string name=\"create_tag\">创建标签</string>\n    <string name=\"tag_name\">标签名称</string>\n    <string name=\"add_tag\">添加标签</string>\n    <string name=\"open_file\">打开文件</string>\n    <string name=\"modify_str_attr\">修改属性</string>\n    <string name=\"error_file\">文件异常，请删除：&lt;font color=#FF0000&gt;%s&lt;/font&gt;</string>\n    <string name=\"not_souper_otp\">不支持的TOTP</string>\n    <string name=\"modify_totp\">修改令牌</string>\n    <string name=\"error_totp\">错误的totp</string>\n    <string name=\"file_save_success\">附件保存成功</string>\n    <string name=\"options\">选项</string>\n</resources>"
  },
  {
    "path": "app/src/main/res/values-zh-rTW/arrays.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n\n  <string-array name=\"quick_pass_type_entries\">\n    <item>數據庫密碼前面%s位</item>\n    <item>數據庫密碼末尾%s位</item>\n  </string-array>\n\n\n  <string-array name=\"clean_clip_entries\">\n    <item>30秒</item>\n    <item>1分鐘</item>\n    <item>2分鐘</item>\n    <item>5分鐘</item>\n  </string-array>\n\n\n  <string-array name=\"quick_lock_entries\">\n    <item>2分鐘</item>\n    <item>5分鐘</item>\n    <item>10分鐘</item>\n    <item>20分鐘</item>\n  </string-array>\n\n  <string-array name=\"out_db_entries\">\n    <item>Keepass2數據庫</item>\n    <item>不加密的XML文件</item>\n    <item>不加密的CSV文件</item>\n  </string-array>\n\n  <string-array name=\"v3_add_mor_item\">\n    <item>備註</item>\n    <item>文件附件</item>\n    <item>失效時間</item>\n  </string-array>\n\n  <string-array name=\"v4_add_mor_item\">\n    <item>備註</item>\n    <item>自定義字段</item>\n    <item>文件附件</item>\n    <item>TOTP</item>\n    <item>標簽</item>\n    <item>覆蓋鏈接</item>\n    <item>失效時間</item>\n  </string-array>\n\n  <!--  创建数据库时的默认群组 -->\n  <string-array name=\"create_normal_group\">\n    <item>郵箱</item>\n    <item>工作</item>\n    <item>財務</item>\n    <item>娛樂</item>\n    <item>回收站</item>\n  </string-array>\n</resources>"
  },
  {
    "path": "app/src/main/res/values-zh-rTW/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"db\">數據庫</string>\n    <string name=\"db1\"><![CDATA[數據庫\\t|\\t<font color=\"#757575\">%s</font>]]></string>\n    <string name=\"password\">密碼</string>\n    <string name=\"key\">密鑰</string>\n    <string name=\"key1\">密鑰：%s</string>\n    <string name=\"open\">解鎖</string>\n    <string name=\"hint_set_password\">設置密碼</string>\n    <string name=\"hint_input_password\">輸入6–16位密碼</string>\n    <string name=\"hint_enter_password\">確認密碼</string>\n    <string name=\"help_create_db\">默認創建v4版本的keepass數據庫</string>\n    <string name=\"help_create_group\">群組名不能超過16個字符</string>\n    <string name=\"encrypt_type\">加密方式</string>\n    <string name=\"encrypt_type_1\">僅密碼</string>\n    <string name=\"encrypt_type_2\">密碼 + 密鑰</string>\n    <string name=\"choose_pass_key\">選擇密鑰</string>\n    <string name=\"hint_set_pass_key\">請選擇密鑰</string>\n    <string name=\"pass_key\">文件密鑰</string>\n    <string name=\"helper_create_pass\">建議使用混合密碼（大小寫英文 + 數字 +符號）</string>\n    <string name=\"open_db\">打開數據庫</string>\n    <string name=\"db_name\">輸入數據庫名</string>\n    <string name=\"create_db\">創建數據庫</string>\n    <string name=\"next\">下一步</string>\n    <string name=\"up\">上一步</string>\n    <string name=\"hint_select_db_path\">點擊設置數據庫保存方式</string>\n    <string name=\"hint_select_path_type\">選擇數據庫保存路徑</string>\n    <string name=\"error_db_name_null\">請輸入數據庫名</string>\n    <string name=\"done\">完成</string>\n    <string name=\"enter\">確認</string>\n    <string name=\"cancel\">取消</string>\n    <string name=\"hint\">提示</string>\n    <string name=\"help_create_db_path\">推薦使用dropbox、webdav等雲端路徑，這樣可以自動同步您的數據庫到雲端，便於在其它設備使用數據庫。\\n 數據庫採用AES256的加密方式，若沒有您的密碼，任何人都無法打開您的數據庫。</string>\n    <string name=\"help_pass_type\">僅使用密碼：\\n打開數據庫僅需要您的密碼；\\n密碼+密鑰：\\n您需要輸入密碼並選擇密鑰文件才能打開數據庫。</string>\n    <string name=\"help_pass_key\">密鑰相當於打開數據庫的鑰匙，通常比密碼複雜和可靠的多。但是也更難將其隱藏，如果您將數據庫保存到雲端，千萬不要將密鑰和數據庫放在一起，否則迷藥將變得沒有意義。\\n您可以選擇任意文件作為打開數據庫的密鑰，一張照片，或一個txt文檔。\\n⚠️注意：如果這個照片或txt文檔被修改，那麼您將無法打開數據庫！！\\n強烈推薦使用只讀文件作為密鑰。</string>\n    <string name=\"choose_file\">選擇文件</string>\n    <string name=\"choose_pass_key_file_des\">選擇任意文件作為您的密鑰</string>\n    <string name=\"create_file\">創建新文件</string>\n    <string name=\"create_pass_key_file_des\">創建一個新文件作為您的密鑰</string>\n    <string name=\"create_pass_key_success\">創建密鑰【%s】成功</string>\n    <string name=\"error_pass_null\">請設置數據庫密碼</string>\n    <string name=\"error_enter_pass_null\">請輸入確認密碼</string>\n    <string name=\"error_pass_unfit\">兩次密碼不一致</string>\n    <string name=\"hint_db_create_success\">數據庫【%S】創建成功</string>\n    <string name=\"history\">歷史</string>\n    <string name=\"history_record\">歷史紀錄</string>\n    <string name=\"edit\">編輯</string>\n    <string name=\"change_db\">切換數據庫</string>\n    <string name=\"all\">所有條目</string>\n    <string name=\"setting\">設置</string>\n    <string name=\"db_setting\">數據庫設置</string>\n    <string name=\"app_setting\">應用設置</string>\n    <string name=\"welcome\">歡迎使用\\nKeepassA</string>\n    <string name=\"help_input_db_pass\">輸入數據庫密碼</string>\n    <string name=\"need_key\">需要密鑰？</string>\n    <string name=\"error_uri\">uri 錯誤</string>\n    <string name=\"error_input_pass_null\">請輸入數據庫密碼</string>\n    <string name=\"error_open_db\">數據庫打開失敗，請檢查密碼是否錯誤或是否需要密鑰</string>\n    <string name=\"error_open_db_key_empty\">缺少密鑰</string>\n    <string name=\"error_open_db_pass_error\">數據庫密碼錯誤</string>\n    <string name=\"error_open_db_key_invalid\">無效的密鑰</string>\n    <string name=\"error_open_db_version_error\">數據庫版本錯誤</string>\n    <string name=\"error_open_db_signature_error\">數據庫簽名錯誤</string>\n    <string name=\"error_open_db_algorithm_error\">無效的算法</string>\n    <string name=\"error_open_db_arcfour_error\">無效的流加密算法</string>\n    <string name=\"hint_group_desc\">群組-%s個條目</string>\n    <string name=\"error_group_id_null\">groupId 為空</string>\n    <string name=\"error_entry_id_null\">entryId 為空</string>\n    <string name=\"create_time\">創建於 %s</string>\n    <string name=\"expire_time\">將於 %s 過期</string>\n    <string name=\"hint_attr\">高級書信</string>\n    <string name=\"attachment\">文件附件</string>\n    <string name=\"hint_ex_property\">額外屬性</string>\n    <string name=\"expire\"><![CDATA[已於<font color=\"#FF0000\">%s</font>過期]]></string>\n    <!--  <string name=\"hint_exit_app\">再按一次退出程序</string>-->\n    <string name=\"del_group\">刪除群組</string>\n    <string name=\"edit_group\">編輯群组</string>\n    <string name=\"success\">成功</string>\n    <string name=\"fail\">失敗</string>\n    <string name=\"del_entry\">刪除條目</string>\n    <string name=\"del_history\">刪除歷史記錄</string>\n    <string name=\"copy_user_name\">複製用戶名</string>\n    <string name=\"copy_password\">複製密碼</string>\n    <string name=\"copy_totp\">複製TOTP令牌</string>\n    <string name=\"hint_del_group\"><![CDATA[確認刪除群組【<font color=\"#FF0000\">%s</font>】？<br> 該操作將會將群組【<font color=\"#FF0000\">%s</font>】移動到回收站中，如果您想撤銷刪除操作，請在回收站中恢復該群組。]]></string>\n    <string name=\"hint_del_group_no_recycle\"><![CDATA[確認刪除群組【<font color=\"#FF0000\">%s</font>】？該操作將會從數據庫中刪除該群組。]]></string>\n    <string name=\"hint_del_entry\"><![CDATA[確認刪除條目【<font color=\"#FF0000\">%s</font>】？<br> 該操作將會將條目【<font color=\"#FF0000\">%s</font>】移動到回收站中，如果您想撤銷刪除操作，請在回收站中恢復該群組。]]></string>\n    <string name=\"hint_del_entry_no_recycle\"><![CDATA[確認刪除條目【<font color=\"#FF0000\">%s</font>】？該操作將會從數據庫中刪除該條目。]]></string>\n    <string name=\"group_name\">群組名</string>\n    <string name=\"create_group\">創建群組</string>\n    <string name=\"error_group_name_null\">群組名為空</string>\n    <string name=\"create_group_fail\">創建群組失敗</string>\n    <string name=\"create_group_success\">創建群組成功</string>\n    <string name=\"modify_group\">編輯群組</string>\n    <string name=\"error_group_no_modify\">群組沒有改動</string>\n    <string name=\"title_too_long\">群組名太長</string>\n    <string name=\"choose_icon\">選擇圖表</string>\n    <string name=\"undo_entry\">恢復到..</string>\n    <string name=\"undo_to\">移動到該群組</string>\n    <string name=\"undo_grouped\">群組已移動到指定位置</string>\n    <string name=\"undo_entryed\">條目已移動到指定位置</string>\n    <string name=\"hint_copy_user\">已將用戶名拷貝到剪切板</string>\n    <string name=\"hint_copy_pass\">已將密碼拷貝到剪切板</string>\n    <string name=\"hint_copy_totp\">已將totp令牌拷貝到剪切板</string>\n    <string name=\"create_totp\">生成totp令牌</string>\n    <string name=\"safety_set\">安全設置</string>\n    <string name=\"fingerprint_unlock\">指紋解鎖</string>\n    <string name=\"db_handle\">數據庫操作</string>\n    <string name=\"modify_db_name\">修改數據庫名</string>\n    <string name=\"modify_db_pass\">修改數據庫密碼</string>\n    <string name=\"out_db\">導出數據庫</string>\n    <string name=\"db_name_no_alter\">數據庫名沒有更改</string>\n    <string name=\"db_name_modify\">數據庫名修改</string>\n    <string name=\"no_record\">沒有紀錄</string>\n    <string name=\"hint_query\">用戶名、標題、url、高級屬性..</string>\n    <string name=\"copy_to_clip\">複製到剪切板</string>\n    <string name=\"show_pass\">顯示密碼</string>\n    <string name=\"ope_url\">打開連接</string>\n    <string name=\"hide_pass\">隱藏密碼</string>\n    <string name=\"hint_copy_to_clip\">已將數據複製到剪切板</string>\n    <string name=\"verify_finger\">指紋驗證</string>\n    <string name=\"verify_finger_fail\">指紋驗證失敗</string>\n    <string name=\"ban_fingerprint\">禁用指紋解鎖</string>\n    <string name=\"quick_use_fingerprint\">僅在快速解鎖界面使用指紋解鎖</string>\n    <string name=\"use_full_fingerprint\">啟用完整的指紋解鎖</string>\n    <string name=\"hint_full_fingerprint\">啟用完整的指紋解鎖功能，將在快速解鎖界面和數據庫解鎖界面使用指紋解鎖數據庫，keepassA將會將您的數據庫主密碼和密鑰信心加密存儲到android系統本地的密鑰庫中，並使用指紋授權進行保護。只有您的指紋才能解鎖數據庫。</string>\n    <string name=\"hint_quick_unlock\">輸入您數據庫密碼的後%s位</string>\n    <string name=\"hint_input_title\">標題</string>\n    <string name=\"hint_input_user_name\">用戶名</string>\n    <string name=\"hint_input_url\">鏈接地址</string>\n    <string name=\"notice\">備註</string>\n    <string name=\"hint_input_tag\">標簽1，標簽2</string>\n    <string name=\"hint_input_cover_url\">覆蓋鏈接</string>\n    <string name=\"create_entry\">創建條目</string>\n    <string name=\"save\">保存</string>\n    <string name=\"len\">長度</string>\n    <string name=\"upper\">大寫字母</string>\n    <string name=\"lower\">小寫字母</string>\n    <string name=\"numer\">數字</string>\n    <string name=\"minus\">減號</string>\n    <string name=\"underline\">下劃線</string>\n    <string name=\"space\">空格</string>\n    <string name=\"special\">特殊字符</string>\n    <string name=\"bracket\">括號</string>\n    <string name=\"pass_generater\">密碼生成器</string>\n    <string name=\"error_genera_params\">請至少選擇一個密碼類型</string>\n    <string name=\"hint_pass_null\">密碼為空</string>\n    <string name=\"date\">日期</string>\n    <string name=\"time\">時間</string>\n    <string name=\"choose_dir\">保存到當前群組</string>\n    <string name=\"normal_account\">普通帳號</string>\n    <string name=\"add_more\">添加更多項</string>\n    <string name=\"warning\">警告</string>\n    <string name=\"create_entry_no_save\">您創建的條目尚未保存，確定退出當前界面？</string>\n    <string name=\"add_custom_str\">創建自定義字段</string>\n    <string name=\"hint_input_attr_str_key\">字段名</string>\n    <string name=\"hint_input_attr_str_value\">字段值</string>\n    <string name=\"hint_protect_str\">受保護字段</string>\n    <string name=\"error_attr_str_null\">字段名為空</string>\n    <string name=\"del_attr_str\">刪除自定義字段</string>\n    <string name=\"del_attr_file\">删除附件</string>\n    <string name=\"error_attr_file_too_large\">為了減少內存消耗，請不要將大於10M的文件作為附件</string>\n    <string name=\"download_file\">保存附件到SD卡</string>\n    <string name=\"open_whit_text\">使用內部文本打開</string>\n    <string name=\"open_whit_img\">使用內部圖片查看器打開</string>\n    <string name=\"open_whit_other\">保存到緩存並使用系統應用打開</string>\n    <string name=\"save_file_success\">文件【%s】保存成功</string>\n    <string name=\"txt_viewer\">文件查看器</string>\n    <string name=\"save_db_fail\">保存數據庫失敗</string>\n    <string name=\"error_pass\">密碼錯誤</string>\n    <string name=\"db_pass_no_alter\">新密碼和舊密碼一致</string>\n    <string name=\"hint_db_pass_modify_success\">修改數據庫密碼成功，將於下次打開數據庫時生效</string>\n    <string name=\"hint_db_pass_modify\">修改數據庫密碼</string>\n    <string name=\"app_feedback\">提意見</string>\n    <string name=\"error_db_pass_too_short\">為了保障您的數據安全，數據庫密碼不能小於6位</string>\n    <string name=\"autofill_sign_in_prompt\">使用keepassA填充密碼</string>\n    <string name=\"hint_background_start\"><![CDATA[為了能更好使用自動填充服務，請您到<font color=\"#FF0000\">【%s】</font>中打開後台彈出權限。<br> <font color=\"#FF0000\">keepassA僅會在您使用自動填充功能是啟動相應界面打開數據庫。</font>]]></string>\n    <string name=\"setting_miui_background_start\">設置-&gt;應用設置-&gt;應用管理-&gt;keepassA-&gt;權限管理</string>\n    <string name=\"setting_vivo_background_start\">設置-&gt;更多設置-&gt;應用管理-&gt;KeepassA-&gt;權限管理-&gt;後台彈出</string>\n    <string name=\"no_matching_entry\">沒有匹配的條目</string>\n    <string name=\"hint_save_auto_fill\"><![CDATA[是否將包名【<font color=\"#FF0000\">%s</font>】和條目【<font color=\"#FF0000\">%s</font>】進行關聯]]></string>\n    <string name=\"relevance_db\">關聯數據</string>\n    <string name=\"modify_time\">修改時間</string>\n    <string name=\"lose_time\">失效時間</string>\n    <string name=\"update_group_fail\">更新群組信息失敗</string>\n    <string name=\"update_group_success\">更新群組信息成功</string>\n    <string name=\"title\">標題</string>\n    <string name=\"feedback\">意見反饋 ^_^</string>\n    <string name=\"submit\">提交</string>\n    <string name=\"send_email_fail\">沒有郵件應用，發送郵件失敗</string>\n    <string name=\"feedback_email_msg\">Hello，我是KeepassA，您有什麼問題？&lt;br /&gt; &lt;br /&gt; &lt;br /&gt; _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ &lt;br /&gt; 手機品牌：%s &lt;br /&gt; 手機型號：%s &lt;br /&gt; 系統版本：%s &lt;br /&gt; 分辨率：%s &lt;br /&gt; 應用版本：%s </string>\n    <string name=\"mark_not_exit\">應用市場不存在</string>\n    <string name=\"set_open_auto_fill_title\">打開自動填充服務</string>\n    <string name=\"set_key_praise_value\">給KeepassA評分</string>\n    <string name=\"other_set\">其它設置</string>\n    <string name=\"open_quick_unlock\">開啟快速解鎖</string>\n    <string name=\"quick_pass_type_summary\">\"\"</string>\n    <string name=\"clean_clip_time\">清空剪切板</string>\n    <string name=\"clean_clip_time_summary\">複製密碼後，%s後清空剪切板</string>\n    <string name=\"auto_lock_db_time\">自動鎖定數據庫</string>\n    <string name=\"auto_lock_db_time_summary\">無操作後，%s後自動鎖定數據庫</string>\n    <string name=\"quick_pass_type\">短密碼截取位置</string>\n    <string name=\"des_quick_unlock\">使用短密碼解鎖您的數據庫</string>\n    <string name=\"error_short_pass_lenght\">短密碼長度不能大於6</string>\n    <string name=\"error_short_pass_short\">短密碼長度不能小於3</string>\n    <string name=\"quick_pass_len_title\">短密碼長度</string>\n    <string name=\"quick_pass_len_summary\">短密碼長度：%s</string>\n    <string name=\"set_key_value\">選擇語言</string>\n    <string name=\"dropbox_msg\"><![CDATA[秉持著尊重用戶隱私的原則，keepassA是不會要求去獲取您dropbox所有文件的權限，KeepassA<font color=\"#FF0000\">只有訪問【應用/KeepassA】文件夾的權限</font>。<br />因此： <br />\n<b>如果您已經擁有來一個keepass數據庫，那麼，您需要先在KeepassA中授權KeepassA訪問【應用/KeepassA】文件夾的權限，並在授權後完成後將keepass數據庫保存到【應用/KeepassA】文件夾中。</b ><br/><br />\n如果您是新建keepass數據庫，將不需要上面的操作，在您創建數據庫完成後，keepassA將會自動創建對應文件夾，並將數據庫上傳到dropbox的【應用/KeepassA】目錄下。]]></string>\n    <string name=\"cover\">覆蓋</string>\n    <string name=\"file_conflict_msg\">雲端數據和本地數據出現衝突，请选择覆盖方式，冲突的条目有：\\n %s</string>\n    <string name=\"merge\">合併</string>\n    <string name=\"sync_db\">同步數據庫</string>\n    <string name=\"net_error\">網路異常</string>\n    <string name=\"cover_local\">本地</string>\n    <string name=\"cover_cloud\">雲端</string>\n    <string name=\"auth\">授權</string>\n    <string name=\"invalid\">無效</string>\n    <string name=\"no_file\">沒有文件</string>\n    <string name=\"login\">登錄</string>\n    <string name=\"helper_webdav\">如：https://dav.xxxx.com/dav/你的文件夾/xxx.kdbx</string>\n    <string name=\"helper_webdav_dir\">如：https://dav.xxxx.com/dav/你的文件夾/</string>\n    <string name=\"hint_webdav_url\">文件地址</string>\n    <string name=\"hint_please_input\">請輸入%s</string>\n    <string name=\"db_file_no_exist\">數據庫文件不存在</string>\n    <string name=\"invalid_auth\">授權失敗，請重新獲取授權</string>\n    <string name=\"afs\">Android 文件系統</string>\n    <string name=\"hint_kdbx_name\">錯誤的後綴名，後綴名應該是\".kdbx\"</string>\n    <string name=\"start_upload_db\">開始上傳數據庫到您的網盤</string>\n    <string name=\"unlocked\">已解鎖</string>\n    <string name=\"notify_channel_db_open\">數據庫解鎖通知</string>\n    <string name=\"notify_quick_unlock_start\">數據庫已鎖定，快速解鎖已啟用</string>\n    <string name=\"notify_db_locked\">數據庫已鎖定</string>\n    <string name=\"search\">搜索</string>\n    <string name=\"disable\">關閉</string>\n    <string name=\"quick_unlock\">快速解鎖</string>\n    <string name=\"db_unlock\">解鎖數據庫</string>\n    <string name=\"hint_quick_use_fingerprint\">僅在快速解鎖頁面使用指紋解鎖</string>\n    <string name=\"version_log\">升級日誌</string>\n    <string name=\"hint_finger_print_verfiy\">您需要重新驗證指紋，才能讓設置生效</string>\n    <string name=\"closed\">已關閉</string>\n    <string name=\"open1\">開啟</string>\n    <string name=\"upload\">上傳</string>\n    <string name=\"add_attr_file\">添加附件</string>\n    <string name=\"hint_fingerprint_modify\">檢查到指紋有變化，為了您的密碼安全，請重新設置指紋解鎖功能</string>\n    <string name=\"hint_please_open_database\">請打開數據庫</string>\n    <string name=\"save_db_success\">保存数据库成功</string>\n    <string name=\"set_delete_setting\">刪除設置</string>\n    <string name=\"set_delete_no_into_recycle_bin\">將刪除的條目移動到回收站中</string>\n    <string name=\"set_delete_no_into_recycle_bin_des\">打开后该设置后，回收站中将不再保存被删除的条目和群组</string>\n    <string name=\"warning_rooted\">為了您的數據安全，請不要在已經root的手機上打開數據庫</string>\n    <string name=\"hint_security_green\">非常棒，應用處於無風險的環境中</string>\n    <string name=\"hint_security_red\">注意，您的手機已經被root了，數據庫處於一個非常危險的環境中</string>\n    <string name=\"hint_security_yellow\">警告，運用運行於模擬器中，該環境會給數據庫帶來一些不可預測的風險</string>\n    <string name=\"donate\">捐贈</string>\n    <string name=\"donate_desc\">[KeepassA](https://github.com/AriaLyy/KeepassA)是一款開源軟件，如果您喜歡我們的軟件，請考慮給我們捐贈，以支持我們的工作，非常感謝您。</string>\n    <string name=\"dev_birthday\">今天是開發者生日🎂，請我喝一瓶快樂水🎉🎉？</string>\n    <string name=\"totp_defaule\">默認 RFC 6238令牌設置</string>\n    <string name=\"totp_steam\">Steam令牌設置</string>\n    <string name=\"totp_custom\">使用自定義設置</string>\n    <string name=\"totp_custom_hint\">自定義設置</string>\n    <string name=\"totp_Arithmetic\">算法：</string>\n    <string name=\"totp_time\">時間(s)：</string>\n    <string name=\"totp_len\">長度：</string>\n    <string name=\"totp_key_error\">生成totp代碼失敗，key異常</string>\n    <string name=\"sort\">排序</string>\n    <string name=\"sort_chart_desc\">首字母降序(Z-&gt;A)</string>\n    <string name=\"sort_chart_asc\">首字母升序(A-&gt;Z)</string>\n    <string name=\"sort_time_asc\">按時間升序</string>\n    <string name=\"sort_time_desc\">按時間降序</string>\n    <string name=\"ime_label\">KeepassA 安全鍵盤</string>\n    <string name=\"set_open_kpa_ime_title\">打開安全鍵盤</string>\n    <string name=\"no_totp_token\">該應用沒有設置totp令牌</string>\n    <string name=\"ime_entry_other_info\">填充其它字段</string>\n    <string name=\"url\">網址</string>\n    <string name=\"hint_not_nore_info\">没有更多信息</string>\n    <string name=\"error_uri_grant_permission\">Uri 授權失敗</string>\n    <string name=\"license\">開放源碼許可證</string>\n    <string name=\"move\">移動</string>\n    <string name=\"set_key_sum_main_show_entries\">解鎖數據庫後顯示所有條目</string>\n    <string name=\"set_key_sum_main_show_history\">解鎖數據庫後顯示歷史紀錄</string>\n    <string name=\"set_key_title_main_allow_show_entries\">主頁設置</string>\n    <string name=\"set_key_title_opr_env_check\">運行環境檢查設置</string>\n    <string name=\"set_key_title_opr_env_check_turn_off\">關閉運用環境檢查</string>\n    <string name=\"set_key_title_opr_env_check_turn_on\">打開運行環境檢查</string>\n    <string name=\"expand\">展開</string>\n    <string name=\"shrink\">收起</string>\n    <string name=\"ui_setting\">界面設置</string>\n    <string name=\"cur_app_not_autofill\">不自動填充</string>\n    <string name=\"other\">其它</string>\n    <string name=\"ref_entry\">參考條目</string>\n    <string name=\"multi_choice\">多選</string>\n    <string name=\"set_summary_screen_lock_off\">屏幕鎖定不鎖定數據庫</string>\n    <string name=\"set_summary_screen_lock_on\">屏幕鎖定自動鎖定數據庫</string>\n    <string name=\"set_title_screen_lock\">屏幕鎖定設置</string>\n    <string name=\"close\">關閉</string>\n    <string name=\"Alipay\">支付寶</string>\n    <string name=\"error_webdav_end_suffix\">錯誤的路徑，應該使用文件夾路徑。有效路徑，如：\"https://webdav.com/dav.kpa/\"</string>\n    <string name=\"error_webdav_end_dav_suffix\">錯誤的路徑，不允許使用/dav/路徑。有效路徑，如：\"https://webdav.com/dav/kpa/\"</string>\n    <string name=\"set_key_title_fillet_bg_icon\">列表圖標添加圓角</string>\n    <string name=\"set_key_title_fillet_bg_icon_turn_on\">圖標添加圓角</string>\n    <string name=\"set_key_title_fillet_bg_icon_turn_off\">圖標不添加圓角</string>\n    <string name=\"set_key_title_show_state_bar\">是否顯示狀態欄</string>\n    <string name=\"set_key_title_show_state_bar_turn_on\">顯示狀態欄</string>\n    <string name=\"set_key_title_show_state_bar_turn_off\">隱藏狀態欄</string>\n    <string name=\"set_key_title_close_load_anim\">加載動畫</string>\n    <string name=\"set_key_title_close_load_anim_turn_on\">打開加載動畫</string>\n    <string name=\"set_key_title_close_load_anim_turn_off\">關閉加載動畫</string>\n    <string name=\"s_default\">默認</string>\n    <string name=\"customize\">自定義</string>\n    <string name=\"set_key_title_auto_lock_database\">自動鎖定數據庫</string>\n    <string name=\"set_key_title_auto_lock_database_turn_on\">自動鎖定數據庫</string>\n    <string name=\"set_key_title_auto_lock_database_turn_off\">關閉</string>\n    <string name=\"thank_donate\">感謝您的捐贈。</string>\n</resources>"
  },
  {
    "path": "app/src/main/res/xml/app_setting.xml",
    "content": "<PreferenceScreen xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n\n  <PreferenceCategory app:title=\"@string/safety_set\">\n\n    <SwitchPreference\n        app:defaultValue=\"false\"\n        app:icon=\"@drawable/ic_security_24px\"\n        app:key=\"@string/set_quick_unlock\"\n        app:summary=\"@string/des_quick_unlock\"\n        app:title=\"@string/open_quick_unlock\" />\n\n    <ListPreference\n        android:defaultValue=\"1\"\n        android:icon=\"@drawable/ic_password\"\n        android:summary=\"@string/quick_pass_type_summary\"\n        app:dependency=\"@string/set_quick_unlock\"\n        app:entries=\"@array/quick_pass_type_entries\"\n        app:entryValues=\"@array/quick_pass_type_value\"\n        app:key=\"@string/set_quick_pass_type\"\n        app:title=\"@string/quick_pass_type\" />\n\n    <ListPreference\n        android:defaultValue=\"3\"\n        android:icon=\"@drawable/ic_linear_scale\"\n        android:summary=\"@string/quick_pass_len_summary\"\n        android:title=\"@string/quick_pass_len_title\"\n        app:dependency=\"@string/set_quick_unlock\"\n        app:entries=\"@array/quick_pass_len_entries\"\n        app:entryValues=\"@array/quick_pass_len_value\"\n        app:key=\"@string/set_quick_pass_len\" />\n\n    <SwitchPreference\n        android:defaultValue=\"false\"\n        android:title=\"@string/set_open_auto_fill_title\"\n        app:icon=\"@drawable/ic_auto_fill\"\n        app:key=\"@string/set_open_auto_fill\" />\n\n    <SwitchPreference\n        android:defaultValue=\"false\"\n        android:icon=\"@drawable/ic_screen_lock_portrait_black_24dp\"\n        app:key=\"@string/set_key_lock_screen_auto_lock_db\"\n        app:summaryOff=\"@string/set_summary_screen_lock_off\"\n        app:summaryOn=\"@string/set_summary_screen_lock_on\"\n        app:title=\"@string/set_title_screen_lock\" />\n\n    <Preference\n        android:defaultValue=\"false\"\n        android:title=\"@string/set_open_kpa_ime_title\"\n        app:icon=\"@drawable/ic_keyboard\"\n        app:key=\"@string/set_key_open_kpa_ime\" />\n\n    <Preference\n        app:icon=\"@drawable/ic_fingerprint\"\n        app:isPreferenceVisible=\"true\"\n        app:key=\"@string/set_key_fingerprint_unlock\"\n        app:title=\"@string/fingerprint_unlock\">\n\n      <intent\n          android:targetClass=\"com.lyy.keepassa.view.fingerprint.FingerprintActivity\"\n          android:targetPackage=\"com.lyy.keepassa\" />\n\n    </Preference>\n\n\n  </PreferenceCategory>\n\n  <PreferenceCategory app:title=\"@string/other_set\">\n\n    <Preference\n        android:title=\"@string/ui_setting\"\n        app:key=\"@string/set_key_ui_setting\"\n        app:fragment=\"com.lyy.keepassa.view.setting.UISettingFragment\"\n        app:icon=\"@drawable/ic_app\" />\n\n    <ListPreference\n        android:icon=\"@drawable/ic_language_24px\"\n        android:title=\"@string/set_key_value\"\n        app:entries=\"@array/choose_language_entries\"\n        app:entryValues=\"@array/choose_language_value\"\n        app:key=\"@string/set_key_language\" />\n\n    <Preference\n        app:icon=\"@drawable/ic_version\"\n        app:isPreferenceVisible=\"true\"\n        app:key=\"@string/set_key_version_log\"\n        app:title=\"@string/version_log\" />\n\n    <Preference\n        app:icon=\"@drawable/ic_notice\"\n        app:isPreferenceVisible=\"true\"\n        app:key=\"@string/set_key_license\"\n        app:title=\"@string/license\" />\n\n  </PreferenceCategory>\n\n\n</PreferenceScreen>"
  },
  {
    "path": "app/src/main/res/xml/aria_fileprovider_paths.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<paths xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <root-path\n        name=\"root\"\n        path=\"\" />\n    <files-path\n        name=\"files\"\n        path=\".\" />\n\n    <cache-path\n        name=\"cache\"\n        path=\".\" />\n\n    <external-path\n        name=\"external\"\n        path=\".\" />\n\n    <external-files-path\n        name=\"external_file_path\"\n        path=\".\" />\n    <external-cache-path\n        name=\"external_cache_path\"\n        path=\".\" />\n\n    <files-path path=\"marssample/log/\" name=\"xlog\" />\n\n</paths>"
  },
  {
    "path": "app/src/main/res/xml/auto_fill_service_configuration.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<autofill-service xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:settingsActivity=\"com.lyy.keepassa.view.setting.SettingActivity\">\n  <!--  settingsActivity 为在系统设置跳转到应用内的页面-->\n  <compatibility-package android:name=\"com.android.browser\" />\n  <compatibility-package android:name=\"com.android.chrome\" />\n  <compatibility-package android:name=\"com.chrome.beta\" />\n  <compatibility-package android:name=\"com.chrome.dev\" />\n  <compatibility-package android:name=\"com.chrome.canary\" />\n  <compatibility-package android:name=\"com.microsoft.emmx\" />\n  <compatibility-package android:name=\"com.opera.browser\" />\n  <compatibility-package android:name=\"com.opera.browser.beta\" />\n  <compatibility-package android:name=\"com.opera.mini.native\" />\n  <compatibility-package android:name=\"com.opera.mini.native.beta\" />\n  <compatibility-package android:name=\"com.sec.android.app.sbrowser\" />\n  <compatibility-package android:name=\"com.sec.android.app.sbrowser.beta\" />\n  <compatibility-package android:name=\"org.mozilla.fennec_aurora\" />\n  <compatibility-package android:name=\"org.mozilla.fennec_fdroid\" />\n  <compatibility-package android:name=\"org.mozilla.firefox\" />\n  <compatibility-package android:name=\"org.mozilla.firefox_beta\" />\n  <compatibility-package android:name=\"org.mozilla.fenix\" />\n  <compatibility-package android:name=\"org.mozilla.fenix.nightly\" />\n  <compatibility-package android:name=\"org.mozilla.reference.browser\" />\n  <compatibility-package android:name=\"org.mozilla.rocket\" />\n  <compatibility-package android:name=\"com.brave.browser\" />\n  <compatibility-package android:name=\"com.google.android.apps.chrome\" />\n  <compatibility-package android:name=\"com.google.android.apps.chrome_dev\" />\n  <compatibility-package android:name=\"com.yandex.browser\" />\n  <compatibility-package android:name=\"org.codeaurora.swe.browser\" />\n  <compatibility-package android:name=\"com.amazon.cloud9\" />\n  <compatibility-package android:name=\"mark.via.gp\" />\n  <compatibility-package android:name=\"org.bromite.bromite\" />\n  <compatibility-package android:name=\"org.chromium.chrome\" />\n  <compatibility-package android:name=\"com.kiwibrowser.browser\" />\n  <compatibility-package android:name=\"com.ecosia.android\" />\n  <compatibility-package android:name=\"com.vivaldi.browser\" />\n  <compatibility-package android:name=\"mark.via\" />\n  <compatibility-package android:name=\"com.mmbox.xbrowser\" />\n  <compatibility-package android:name=\"info.torapp.uweb\" />\n\n</autofill-service>\n"
  },
  {
    "path": "app/src/main/res/xml/db_setting.xml",
    "content": "<PreferenceScreen xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n\n  <PreferenceCategory app:title=\"@string/safety_set\">\n\n    <ListPreference\n        app:defaultValue=\"120\"\n        app:entries=\"@array/clean_clip_entries\"\n        app:entryValues=\"@array/clean_clip_value\"\n        app:icon=\"@drawable/ic_clipbord\"\n        app:key=\"@string/set_key_clean_clip_time\"\n        app:summary=\"@string/clean_clip_time_summary\"\n        app:title=\"@string/clean_clip_time\" />\n\n    <ListPreference\n        app:dependency=\"@string/set_key_auto_lock_database\"\n        app:defaultValue=\"300\"\n        app:entries=\"@array/quick_lock_entries\"\n        app:entryValues=\"@array/quick_lock_value\"\n        app:icon=\"@drawable/ic_auto\"\n        app:key=\"@string/set_key_auto_lock_db_time\"\n        app:summary=\"@string/auto_lock_db_time_summary\"\n        app:title=\"@string/auto_lock_db_time\" />\n\n    <SwitchPreference\n        app:defaultValue=\"true\"\n        app:summaryOff=\"@string/set_key_title_auto_lock_database_turn_off\"\n        app:summaryOn=\"@string/set_key_title_auto_lock_database_turn_on\"\n        app:title=\"@string/set_key_title_auto_lock_database\"\n        app:icon=\"@drawable/ic_setting_lock\"\n        app:key=\"@string/set_key_auto_lock_database\" />\n\n  </PreferenceCategory>\n\n  <PreferenceCategory app:title=\"@string/db_handle\">\n\n    <SwitchPreference\n        app:defaultValue=\"false\"\n        app:summaryOff=\"@string/set_delete_no_into_recycle_bin_des\"\n        app:summaryOn=\"@string/set_delete_no_into_recycle_bin\"\n        app:title=\"@string/set_delete_setting\"\n        app:icon=\"@drawable/ic_delete_sweep\"\n        app:key=\"@string/set_key_delete_no_recycle_bin\" />\n\n    <Preference\n        app:icon=\"@drawable/ic_key_gray\"\n        app:key=\"@string/set_key_modify_db_pass\"\n        app:title=\"@string/modify_db_pass\" />\n\n    <EditTextPreference\n        app:icon=\"@drawable/ic_tab_db_gray\"\n        app:key=\"@string/set_key_modify_db_name\"\n        app:title=\"@string/modify_db_name\" />\n\n    <ListPreference\n        app:defaultValue=\"120\"\n        app:entries=\"@array/out_db_entries\"\n        app:entryValues=\"@array/out_db_value\"\n        app:icon=\"@drawable/ic_out_db\"\n        app:isPreferenceVisible=\"false\"\n        app:key=\"@string/set_key_out_db\"\n        app:title=\"@string/out_db\" />\n\n  </PreferenceCategory>\n\n</PreferenceScreen>"
  },
  {
    "path": "app/src/main/res/xml/input_method.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<input-method xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:icon=\"@mipmap/ic_launcher\"\n    android:settingsActivity=\"com.lyy.keepassa.view.launcher.LauncherActivity\"\n    android:supportsSwitchingToNextInputMethod=\"true\">\n\n  <subtype android:imeSubtypeMode=\"keyboard\" />\n</input-method>\n"
  },
  {
    "path": "app/src/main/res/xml/keyboard_pass_entry.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Keyboard xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n\n</Keyboard>"
  },
  {
    "path": "app/src/main/res/xml/network_security_config.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<network-security-config>\n  <base-config cleartextTrafficPermitted=\"true\">\n    <trust-anchors>\n      <certificates src=\"system\" />\n      <certificates src=\"user\" />\n    </trust-anchors>\n  </base-config>\n\n  <domain-config cleartextTrafficPermitted=\"true\">\n    <domain includeSubdomains=\"true\">android.bugly.qq.com</domain>\n  </domain-config>\n\n</network-security-config>"
  },
  {
    "path": "app/src/main/res/xml/ui_setting.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<PreferenceScreen xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n\n  <SwitchPreference\n      android:defaultValue=\"false\"\n      android:summaryOff=\"@string/set_key_sum_main_show_history\"\n      android:summaryOn=\"@string/set_key_sum_main_show_entries\"\n      android:title=\"@string/set_key_title_main_allow_show_entries\"\n      app:icon=\"@drawable/ic_swap_horiz\"\n      app:key=\"@string/set_key_main_allow_show_entries\" />\n\n  <SwitchPreference\n      android:defaultValue=\"true\"\n      android:summaryOff=\"@string/set_key_title_opr_env_check_turn_off\"\n      android:summaryOn=\"@string/set_key_title_opr_env_check_turn_on\"\n      android:title=\"@string/set_key_title_opr_env_check\"\n      app:icon=\"@drawable/ic_eco\"\n      app:key=\"@string/set_key_need_root_check\" />\n\n  <SwitchPreference\n      android:defaultValue=\"true\"\n      android:summaryOff=\"@string/set_key_title_show_state_bar_turn_off\"\n      android:summaryOn=\"@string/set_key_title_show_state_bar_turn_on\"\n      android:title=\"@string/set_key_title_show_state_bar\"\n      app:isPreferenceVisible=\"false\"\n      app:icon=\"@drawable/ic_state_bar\"\n      app:key=\"@string/set_key_show_state_bar\" />\n\n\n  <SwitchPreference\n      android:defaultValue=\"true\"\n      android:summaryOff=\"@string/set_key_title_close_load_anim_turn_off\"\n      android:summaryOn=\"@string/set_key_title_close_load_anim_turn_on\"\n      android:title=\"@string/set_key_title_close_load_anim\"\n      app:icon=\"@drawable/ic_anim\"\n      app:key=\"@string/set_key_loading_anim\" />\n\n\n  <SwitchPreference\n      android:defaultValue=\"true\"\n      android:summaryOff=\"@string/set_key_main_show_totp_tab_off\"\n      android:summaryOn=\"@string/set_key_main_show_totp_tab_on\"\n      android:title=\"@string/set_key_main_show_totp_tab_title\"\n      app:icon=\"@drawable/ic_token_grey\"\n      app:key=\"@string/set_key_main_show_totp_tab\" />\n\n\n  <ListPreference\n      android:defaultValue=\"0\"\n      android:icon=\"@drawable/ic_theme_style\"\n      android:summary=\"@string/set_key_theme_style_summary\"\n      app:entries=\"@array/sek_ley_theme_style_entries\"\n      app:entryValues=\"@array/set_key_theme_values\"\n      app:key=\"@string/set_key_theme_style\"\n      app:title=\"@string/set_key_theme_style_title\" />\n\n  <Preference\n      android:icon=\"@drawable/ic_lightbulb_on\"\n      app:key=\"@string/set_key_tip_of_day\"\n      app:title=\"@string/title_tip_of_day\" />\n\n\n  <!--  <SwitchPreference-->\n  <!--      android:defaultValue=\"true\"-->\n  <!--      android:summaryOff=\"@string/set_key_title_fillet_bg_icon_turn_off\"-->\n  <!--      android:summaryOn=\"@string/set_key_title_fillet_bg_icon_turn_on\"-->\n  <!--      android:title=\"@string/set_key_title_fillet_bg_icon\"-->\n  <!--      app:icon=\"@drawable/ic_eco\"-->\n  <!--      app:key=\"@string/set_key_fillet_bg_icon\" />-->\n\n</PreferenceScreen>"
  },
  {
    "path": "app/src/main/res/xml-v25/shortcuts.xml",
    "content": "<shortcuts xmlns:android=\"http://schemas.android.com/apk/res/android\">\n  <shortcut\n      android:enabled=\"true\"\n      android:icon=\"@drawable/ic_add_blue_24px\"\n      android:shortcutDisabledMessage=\"@string/create_entry\"\n      android:shortcutId=\"add\"\n      android:shortcutLongLabel=\"@string/create_entry\"\n      android:shortcutShortLabel=\"@string/create_entry\">\n    <intent\n        android:action=\"android.intent.action.VIEW\"\n        android:targetClass=\"com.lyy.keepassa.router.RouterActivity\"\n        android:targetPackage=\"com.lyy.keepassa\" >\n\n      <extra\n          android:name=\"shortcutData\"\n          android:value=\"kpa-router%3A%2F%2Fcom.lyy.keepassa%2Fshortcut%3Fac%3DcreateEntry\"\n          />\n\n    </intent>\n    <!-- If your shortcut is associated with multiple intents, include them\n         here. The last intent in the list determines what the user sees when\n         they launch this shortcut. -->\n    <categories android:name=\"android.shortcut.conversation\" />\n  </shortcut>\n  <!-- Specify more shortcuts here. -->\n\n  <shortcut\n      android:enabled=\"true\"\n      android:icon=\"@drawable/ic_search\"\n      android:shortcutDisabledMessage=\"@string/search\"\n      android:shortcutId=\"search\"\n      android:shortcutLongLabel=\"@string/search\"\n      android:shortcutShortLabel=\"@string/search\">\n    <intent\n        android:action=\"android.intent.action.VIEW\"\n        android:targetClass=\"com.lyy.keepassa.router.RouterActivity\"\n        android:targetPackage=\"com.lyy.keepassa\">\n\n      <extra\n          android:name=\"shortcutData\"\n          android:value=\"kpa-router%3A%2F%2Fcom.lyy.keepassa%2Fshortcut%3Fac%3Dsearch%26shortcutsType%3D1\"\n          />\n\n    </intent>\n    <!-- If your shortcut is associated with multiple intents, include them\n         here. The last intent in the list determines what the user sees when\n         they launch this shortcut. -->\n    <categories android:name=\"android.shortcut.conversation\" />\n  </shortcut>\n\n\n</shortcuts>"
  },
  {
    "path": "app/src/main/res/xml-v28/auto_fill_service_configuration.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<autofill-service xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:settingsActivity=\"com.lyy.keepassa.view.setting.SettingActivity\">\n  <!--  settingsActivity 为在系统设置跳转到应用内的页面-->\n  <compatibility-package\n      android:name=\"com.android.browser\"\n      android:maxLongVersionCode=\"10000000000\" />\n  <compatibility-package\n      android:name=\"com.android.chrome\"\n      android:maxLongVersionCode=\"10000000000\" />\n  <compatibility-package\n      android:name=\"com.chrome.beta\"\n      android:maxLongVersionCode=\"10000000000\" />\n  <compatibility-package\n      android:name=\"com.chrome.dev\"\n      android:maxLongVersionCode=\"10000000000\" />\n  <compatibility-package\n      android:name=\"com.chrome.canary\"\n      android:maxLongVersionCode=\"10000000000\" />\n  <compatibility-package\n      android:name=\"com.microsoft.emmx\"\n      android:maxLongVersionCode=\"10000000000\" />\n  <compatibility-package\n      android:name=\"com.opera.browser\"\n      android:maxLongVersionCode=\"10000000000\" />\n  <compatibility-package\n      android:name=\"com.opera.browser.beta\"\n      android:maxLongVersionCode=\"10000000000\" />\n  <compatibility-package\n      android:name=\"com.opera.mini.native\"\n      android:maxLongVersionCode=\"10000000000\" />\n  <compatibility-package\n      android:name=\"com.opera.mini.native.beta\"\n      android:maxLongVersionCode=\"10000000000\" />\n  <compatibility-package\n      android:name=\"com.sec.android.app.sbrowser\"\n      android:maxLongVersionCode=\"10000000000\" />\n  <compatibility-package\n      android:name=\"com.sec.android.app.sbrowser.beta\"\n      android:maxLongVersionCode=\"10000000000\" />\n  <compatibility-package\n      android:name=\"org.mozilla.fennec_aurora\"\n      android:maxLongVersionCode=\"10000000000\" />\n  <compatibility-package\n      android:name=\"org.mozilla.fennec_fdroid\"\n      android:maxLongVersionCode=\"10000000000\" />\n  <compatibility-package\n      android:name=\"org.mozilla.firefox\"\n      android:maxLongVersionCode=\"10000000000\" />\n  <compatibility-package\n      android:name=\"org.mozilla.firefox_beta\"\n      android:maxLongVersionCode=\"10000000000\" />\n  <compatibility-package\n      android:name=\"org.mozilla.fenix\"\n      android:maxLongVersionCode=\"10000000000\" />\n  <compatibility-package\n      android:name=\"org.mozilla.fenix.nightly\"\n      android:maxLongVersionCode=\"10000000000\" />\n  <compatibility-package\n      android:name=\"org.mozilla.reference.browser\"\n      android:maxLongVersionCode=\"10000000000\" />\n  <compatibility-package\n      android:name=\"org.mozilla.rocket\"\n      android:maxLongVersionCode=\"10000000000\" />\n  <compatibility-package\n      android:name=\"com.brave.browser\"\n      android:maxLongVersionCode=\"10000000000\" />\n  <compatibility-package\n      android:name=\"com.google.android.apps.chrome\"\n      android:maxLongVersionCode=\"10000000000\" />\n  <compatibility-package\n      android:name=\"com.google.android.apps.chrome_dev\"\n      android:maxLongVersionCode=\"10000000000\" />\n  <compatibility-package\n      android:name=\"com.yandex.browser\"\n      android:maxLongVersionCode=\"10000000000\" />\n  <compatibility-package\n      android:name=\"org.codeaurora.swe.browser\"\n      android:maxLongVersionCode=\"10000000000\" />\n  <compatibility-package\n      android:name=\"com.amazon.cloud9\"\n      android:maxLongVersionCode=\"10000000000\" />\n  <compatibility-package\n      android:name=\"mark.via.gp\"\n      android:maxLongVersionCode=\"10000000000\" />\n  <compatibility-package\n      android:name=\"org.bromite.bromite\"\n      android:maxLongVersionCode=\"10000000000\" />\n  <compatibility-package\n      android:name=\"org.chromium.chrome\"\n      android:maxLongVersionCode=\"10000000000\" />\n  <compatibility-package\n      android:name=\"com.kiwibrowser.browser\"\n      android:maxLongVersionCode=\"10000000000\" />\n  <compatibility-package\n      android:name=\"com.ecosia.android\"\n      android:maxLongVersionCode=\"10000000000\" />\n  <compatibility-package\n      android:name=\"com.vivaldi.browser\"\n      android:maxLongVersionCode=\"10000000000\" />\n  <compatibility-package\n      android:name=\"mark.via\"\n      android:maxLongVersionCode=\"10000000000\" />\n  <compatibility-package\n      android:name=\"com.mmbox.xbrowser\"\n      android:maxLongVersionCode=\"10000000000\" />\n  <compatibility-package\n      android:name=\"info.torapp.uweb\"\n      android:maxLongVersionCode=\"10000000000\" />\n\n</autofill-service>\n"
  },
  {
    "path": "app/src/test/java/com/lyy/keepassa/LanguageTest.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa\n\nimport com.lyy.keepassa.util.LanguageUtil\nimport org.junit.Test\n\nclass LanguageTest {\n\n  @Test\n  fun getSysCurrentLanguage(){\n    print(LanguageUtil.getSysCurrentLan())\n  }\n\n  @Test\n  fun checkUrl(){\n    val rs = \"^(((file|gopher|news|nntp|telnet|http|ftp|https|ftps|sftp)://)|(www\\\\.))+(([a-zA-Z0-9\\\\._-]+\\\\.[a-zA-Z]{2,6})|([0-9]{1,3}\\\\.[0-9]{1,3}\\\\.[0-9]{1,3}\\\\.[0-9]{1,3}))(/[a-zA-Z0-9\\\\&%_\\\\./-~-]*)?\\$\"\n    val r1 = Regex(rs)\n    print(r1.matches(\"http://baiducom\"))\n  }\n}"
  },
  {
    "path": "app/src/test/java/com/lyy/keepassa/LockTest.java",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa;\n\nimport java.util.concurrent.TimeUnit;\nimport org.junit.Test;\n\n/**\n * 线程锁测试\n */\npublic class LockTest {\n\n  @Test\n  public static void main(String[] args) {\n    Object co = new Object();\n    System.out.println(co);\n\n    for (int i = 0; i < 5; i++) {\n      MyThread t = new MyThread(\"Thread\" + i, co);\n      t.start();\n    }\n\n    try {\n      TimeUnit.SECONDS.sleep(2);\n      System.out.println(\"-----Main Thread notify-----\");\n      synchronized (co) {\n        co.notify();\n      }\n\n      TimeUnit.SECONDS.sleep(2);\n      System.out.println(\"Main Thread is end.\");\n    } catch (InterruptedException e) {\n      e.printStackTrace();\n    }\n  }\n\n  static class MyThread extends Thread {\n    private String name;\n    private Object co;\n\n    public MyThread(String name, Object o) {\n      this.name = name;\n      this.co = o;\n    }\n\n    @Override\n    public void run() {\n      System.out.println(name + \" is waiting.\");\n      try {\n        synchronized (co) {\n          co.wait();\n        }\n        System.out.println(name + \" has been notified.\");\n      } catch (InterruptedException e) {\n        e.printStackTrace();\n      }\n    }\n  }\n}"
  },
  {
    "path": "app/src/test/java/com/lyy/keepassa/PasswordBuildTest.kt",
    "content": "/*\n * Copyright (C) 2020 AriaLyy(https://github.com/AriaLyy/KeepassA)\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, you can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n\npackage com.lyy.keepassa\n\nimport com.lyy.keepassa.util.PasswordBuildUtil\nimport org.junit.Test\n\nclass PasswordBuildTest {\n  private val TAG = \"PasswordBuildTest\"\n\n  @Test\n  fun sss(){\n    println(\"https://store.steampowered.com/\".contains(\"steampowered\"))\n  }\n\n  @Test\n  fun buildPassWord() {\n    val sTime = System.currentTimeMillis()\n    val pass = PasswordBuildUtil.getInstance()\n    val len = 16\n    pass\n        .addNumChar()\n        .addLowerChar()\n//        .addUnderline()\n//        .addSpaceChar()\n//        .addSymbolChar()\n//        .addMinus()\n        .addUpChar()\n//        .addbracketChar()\n    println(\" 密码：${pass.builder(len)}\\n 密码长度：$len\\n 耗时：${System.currentTimeMillis() - sTime}\")\n  }\n\n}"
  },
  {
    "path": "app/打包命令.txt",
    "content": "./gradlew assembleRelease  --> ./gradlew resguardRelease\n或\n./gradlew assemble渠道Release  --> ./gradlew resguard渠道Release"
  },
  {
    "path": "build.gradle",
    "content": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\n\nbuildscript {\n  ext.kotlin_version = '1.9.0'\n  dependencies {\n//    classpath files(libs.class.superclass.protectionDomain.codeSource.location)\n//    classpath libs.ali.arouter.register\n//    classpath libs.ali.third.arouter.register\n    classpath 'com.alibaba:arouter-register-asm7:1.0.2'\n    // 资源文件混淆\n    classpath libs.tencent.andresguard\n    // 腾讯多渠道打包工具：VasDolly\n    classpath libs.tencent.vasdolly.plugin\n    classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.9'\n  }\n}\n\nplugins {\n  id 'com.android.application' version '7.2.2' apply false\n  id 'com.android.library' version '7.2.2' apply false\n  id 'org.jetbrains.kotlin.android' version \"$kotlin_version\" apply false\n  id 'org.jetbrains.kotlin.jvm' version \"$kotlin_version\" apply false\n  id 'com.google.gms.google-services' version '4.3.10' apply false\n}\n\n\ntask clean(type: Delete) {\n  delete rootProject.buildDir\n}\n"
  },
  {
    "path": "gradle/testConfig.gradle",
    "content": "android {\n  packagingOptions {\n    exclude(\"META-INF/LICENSE.md\")\n    exclude(\"META-INF/LICENSE-notice.md\")\n  }\n}"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "content": "#Tue Dec 03 19:49:59 CST 2019\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-7.4-all.zip\n"
  },
  {
    "path": "gradle.properties",
    "content": "# Project-wide Gradle settings.\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will override*\n# any settings specified in this file.\n# For more details on how to configure your build environment visit\n# http://www.gradle.org/docs/current/userguide/build_environment.html\n# Specifies the JVM arguments used for the daemon process.\n# The setting is particularly useful for tweaking memory settings.\norg.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8\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# AndroidX package structure to make it clearer which packages are bundled with the\n# Android operating system, and which are packaged with your app's APK\n# https://developer.android.com/topic/libraries/support-library/androidx-rn\nandroid.useAndroidX=true\n# Automatically convert third-party libraries to use AndroidX\nandroid.enableJetifier=true\n# Kotlin code style for this project: \"official\" or \"obsolete\":\n#kotlin.code.style=official\n#systemProp.https.proxyPort=1087\n#systemProp.http.proxyHost=127.0.0.1\n#systemProp.https.proxyHost=127.0.0.1\n#systemProp.http.proxyPort=1087\nandroid.enableD8=true\n"
  },
  {
    "path": "gradlew",
    "content": "#!/usr/bin/env sh\n\n##############################################################################\n##\n##  Gradle start up script for UN*X\n##\n##############################################################################\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\nAPP_NAME=\"Gradle\"\nAPP_BASE_NAME=`basename \"$0\"`\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\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\nnonstop=false\ncase \"`uname`\" in\n  CYGWIN* )\n    cygwin=true\n    ;;\n  Darwin* )\n    darwin=true\n    ;;\n  MINGW* )\n    msys=true\n    ;;\n  NONSTOP* )\n    nonstop=true\n    ;;\nesac\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\" -a \"$nonstop\" = \"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# Escape application args\nsave () {\n    for i do printf %s\\\\n \"$i\" | sed \"s/'/'\\\\\\\\''/g;1s/^/'/;\\$s/\\$/' \\\\\\\\/\" ; done\n    echo \" \"\n}\nAPP_ARGS=$(save \"$@\")\n\n# Collect all arguments for the java command, following the shell quoting and substitution rules\neval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS \"\\\"-Dorg.gradle.appname=$APP_BASE_NAME\\\"\" -classpath \"\\\"$CLASSPATH\\\"\" org.gradle.wrapper.GradleWrapperMain \"$APP_ARGS\"\n\n# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong\nif [ \"$(uname)\" = \"Darwin\" ] && [ \"$HOME\" = \"$PWD\" ]; then\n  cd \"$(dirname \"$0\")\"\nfi\n\nexec \"$JAVACMD\" \"$@\"\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\nset DIRNAME=%~dp0\nif \"%DIRNAME%\" == \"\" set DIRNAME=.\nset APP_BASE_NAME=%~n0\nset APP_HOME=%DIRNAME%\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\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 Windows variants\n\nif not \"%OS%\" == \"Windows_NT\" goto win9xME_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=%*\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": "libs.versions.toml",
    "content": "[versions]\ngroovy = \"3.0.10\"\ncheckstyle = \"8.37\"\nminSdk = \"23\"\ncompilesdk = \"34\"\ntargetsdk = \"33\"\nndk = \"21.3.6528147\"\nbuildtools = \"33.0.2\"\n\njetpack-appcompat = \"1.6.1\"\njetpack-material3 = \"1.0.1\"\njetpack-material2 = \"1.11.0\"\njetpack-constraintlayout = \"2.1.4\"\njetpack-compose = \"2.4.3\"\njetpack-multidex = \"2.0.1\"\njetpack-aterial2 = \"1.6.1\"\njetpack-room = \"2.6.1\"\njetpack-lifecycle = \"2.6.2\"\njetpack-viewpager2 = \"1.0.0\"\njetpack-preference-ktx = \"1.2.1\"\njetpack-workmanager = \"2.8.1\"\njetpack-cardview = \"1.0.0\"\njetpack-recyclerview = \"1.3.2\"\njetpack-autofill = \"1.1.0\"\njetpack-swiperefreshlayout = \"1.3.0-alpha01\"\njetpack-biometric = \"1.1.0\"\njetpack-documentfile = \"1.0.1\"\n\n\ngoogle-gson = \"2.9.0\"\ngoogle-tink-android = \"1.5.0\"\ngoogle-zxing = \"3.4.1\"\ngoogle-billing-ktx = \"6.1.0\"\ngoogle-play-services-base = \"18.2.0\"\n\nsquareup-retrofit = \"2.9.0\"\nsquareup-okhttp3 = \"4.10.0\"\n\napache-commons = \"1.3.2\"\n\nthird-jodatime = \"2.10.10\"\nthird-richtext = \"3.0.8\"\nthird-licensesdialog = \"2.1.0\"\nthird-immersionbar = \"3.0.0\"\nthird-subsampling-scale-image-view = \"3.10.0\"\nthird-eventbus = \"3.3.1\"\nthird-lottie = \"4.2.2\"\nthird-glide = \"4.12.0\"\nthird-dropbox = \"3.1.5\"\nthird-webdav = \"0.8\"\nthird-easy-protector-release = \"1.1.2\"\nthird-auto-size = \"1.2.1\"\nthird-freereflection = \"3.1.0\"\nthird-zxing-android-embedded = \"4.3.0\"\nthird-blankj = \"1.31.1\"\nthird-timber = \"5.0.1\"\nthird-spongycastle = \"1.58.0.0\"\n\n\nkotlin-coroutines = \"1.6.4\"\nkotlin-android-core = \"1.12.0\"\nkotlin = \"1.9.0\"\n\ntest-junit = '4.13.2'\ntest-runner = '1.5.2'\ntest-junut-ext = '1.1.5'\ntest-espresso = '3.5.1'\ntest-rules = '1.5.0'\ntest-mockk = '1.13.9'\n\ntencent-wcdb = \"1.0.8\"\ntencent-vasdolly = \"3.0.3\"\ntencent-bugly-crashreport = \"4.1.9\"\ntencent-bugly-nativecrashreport = \"3.9.2\"\ntencent-mmkv = \"1.2.15\"\ntencent-andresguard-gradle-plugin = \"1.2.21\"\n\nali-arouter-compiler = \"1.5.2\"\nali-arouter-register = \"1.0.2\"\nali-third-arouter = \"1.0.1\"\n\nmicrosoft-msal = \"2.+\"\n\n\n\n[libraries]\nthird-richtext = { module = \"com.zzhoujay.richtext:richtext\", version.ref = \"third-richtext\" } # 富文本\nthird-licensesdialog = { module = \"de.psdev.licensesdialog:licensesdialog\", version.ref = \"third-licensesdialog\" } # 开源许可对话框\nthird-immersionbar = { module = \"com.gyf.immersionbar:immersionbar\", version.ref = \"third-immersionbar\" } # 状态栏\nthird-subsampling = { module = \"com.davemorrissey.labs:subsampling-scale-image-view\", version.ref = \"third-subsampling-scale-image-view\" } # 大图浏览\nthird-eventbus = { module = \"org.greenrobot:eventbus\", version.ref = \"third-eventbus\" } # event\nthird-eventbus-compiler = { module = \"org.greenrobot:eventbus-annotation-processor\", version.ref = \"third-eventbus\" } # event\nthird-lottie = { module = \"com.airbnb.android:lottie\", version.ref = \"third-lottie\" } # lottie\nthird-glide = { module = \"com.github.bumptech.glide:glide\", version.ref = \"third-glide\" } # glide\nthird-glide-compiler = { module = \"com.github.bumptech.glide:compiler\", version.ref = \"third-glide\" } # glide\nthird-dropbox = { module = \"com.dropbox.core:dropbox-core-sdk\", version.ref = \"third-dropbox\" } # dropbox\nthird-webdav = { module = \"com.thegrizzlylabs.sardine-android:sardine-android\", version.ref = \"third-webdav\" }\nthird-protector = { module = \"com.lahm.library:easy-protector-release\", version.ref = \"third-easy-protector-release\" } # XP/调试/多开/模拟器/root 判断\nthird-autosize = { module = \"me.jessyan:autosize\", version.ref = \"third-auto-size\" }\nthird-freereflection = { module = \"com.github.tiann:FreeReflection\", version.ref = \"third-freereflection\" }\nthird-zxing-embedded = { module = \"com.journeyapps:zxing-android-embedded\", version.ref = \"third-zxing-android-embedded\" }\nthird-jodatime = { module = \"joda-time:joda-time\", version.ref = \"third-jodatime\" }\nthird-utilcodex = { module = \"com.blankj:utilcodex\", version.ref = \"third-blankj\" }\nthird-timber = { module = \"com.jakewharton.timber:timber\", version.ref = \"third-timber\" }\nthird-BaseRecyclerViewAdapterHelper = {module = \"io.github.cymchad:BaseRecyclerViewAdapterHelper\", version = \"3.0.14\"}\n\ndidi-drouter = { module = \"io.github.didi:drouter-plugin-proxy\", version = \"1.0.2\"} # drouter\ndidi-drouter-api = { module = \"io.github.didi:drouter-api\", version = \"2.3.0\"}\n\nspongycastle-core = { module = \"com.madgag.spongycastle:core\", version.ref = \"third-spongycastle\" } # 加密算法\nspongycastle-prov = { module = \"com.madgag.spongycastle:prov\", version.ref = \"third-spongycastle\" } # 加密算法\n\nmicrosoft-msal = { module = \"com.microsoft.identity.client:msal\", version.ref = \"microsoft-msal\" }\napache-commons-io = { module = \"org.apache.commons:commons-io\", version.ref = \"apache-commons\" }\n\nali-arouter-compiler = { module = \"com.alibaba:arouter-compiler\", version.ref = \"ali-arouter-compiler\" }\nali-arouter-register = { module = \"com.alibaba:arouter-register\", version.ref = \"ali-arouter-register\" }\nali-arouter-api = { module = \"com.alibaba:arouter-api\", version.ref = \"ali-arouter-compiler\" }\nali-third-arouter-compiler = { module = \"com.github.jadepeakpoet.ARouter:arouter-compiler\", version.ref = \"ali-third-arouter\" }\nali-third-arouter-register = { module = \"com.github.jadepeakpoet.ARouter:arouter-register\", version.ref = \"ali-third-arouter\" }\nali-third-arouter-api = { module = \"com.github.jadepeakpoet.ARouter:arouter-api\", version.ref = \"ali-third-arouter\" }\n\ntencent-wcdb = { module = \"com.tencent.wcdb:wcdb-android\", version.ref = \"tencent-wcdb\" }\ntencent-wcdb-room = { module = \"com.tencent.wcdb:room\", version.ref = \"tencent-wcdb\" }\ntencent-vasdolly = { module = \"com.tencent.vasdolly:helper\", version.ref = \"tencent-vasdolly\" }\ntencent-vasdolly-plugin = { module = \"com.tencent.vasdolly:plugin\", version.ref = \"tencent-vasdolly\" }\ntencent-bugly-crashreport = { module = \"com.tencent.bugly:crashreport\", version.ref = \"tencent-bugly-crashreport\" }\ntencent-bugly-nativecrashreport = { module = \"com.tencent.bugly:nativecrashreport\", version.ref = \"tencent-bugly-nativecrashreport\" }\ntencent-mmkv = { module = \"com.tencent:mmkv\", version.ref = \"tencent-mmkv\" }\ntencent-andresguard = { module = \"com.tencent.mm:AndResGuard-gradle-plugin\", version.ref = \"tencent-andresguard-gradle-plugin\" } # 资源混淆\n\ngoogle-tink = { module = \"com.google.crypto.tink:tink-android\", version.ref = \"google-tink-android\" } # 加密工具\ngoogle-gson = { module = \"com.google.code.gson:gson\", version.ref = \"google-gson\" }\ngoogle-zxing = { module = \"com.google.zxing:core\", version.ref = \"google-zxing\" }\ngoogle-gms-billing = { module = \"com.android.billingclient:billing-ktx\", version.ref = \"google-billing-ktx\" }\ngoogle-gms-base = { module = \"com.google.android.gms:play-services-base\", version.ref = \"google-play-services-base\" }\n\njetpack-workmanager = { module = \"androidx.work:work-runtime-ktx\", version.ref = \"jetpack-workmanager\" }\njetpack-appcompat = { module = \"androidx.appcompat:appcompat\", version.ref = \"jetpack-appcompat\" }\njetpack-preference-ktx = { module = \"androidx.preference:preference-ktx\", version.ref = \"jetpack-preference-ktx\" }\njetpack-viewpager2 = { module = \"androidx.viewpager2:viewpager2\", version.ref = \"jetpack-viewpager2\" }\njetpack-lifecycle-viewmodel-ktx = { module = \"androidx.lifecycle:lifecycle-viewmodel-ktx\", version.ref = \"jetpack-lifecycle\" }\njetpack-lifecycle-livedata-ktx = { module = \"androidx.lifecycle:lifecycle-livedata-ktx\", version.ref = \"jetpack-lifecycle\" }\njetpack-lifecycle-common-java8 = { module = \"androidx.lifecycle:lifecycle-common-java8\", version.ref = \"jetpack-lifecycle\" }\njetpack-cardview = { module = \"androidx.cardview:cardview\", version.ref = \"jetpack-cardview\" }\njetpack-recyclerview = { module = \"androidx.recyclerview:recyclerview\", version.ref = \"jetpack-recyclerview\" }\njetpack-compose-ui-tooling = { module = \"androidx.compose.ui:ui-tooling\", version.ref = \"jetpack-compose\" }\njetpack-compose-ui-tooling-preview = { module = \"androidx.compose.ui:ui-tooling-preview\", version.ref = \"jetpack-compose\" }\njetpack-compose-ui = { module = \"androidx.compose.ui:ui\", version.ref = \"jetpack-compose\" }\njetpack-compose-runtime = { module = \"androidx.compose.runtime:runtime\", version.ref = \"jetpack-compose\" }\njetpack-compose-material3 = { module = \"androidx.compose.material3:material3\", version.ref = \"jetpack-compose\" }\njetpack-room-runtime = { module = \"androidx.room:room-runtime\", version.ref = \"jetpack-room\" }\njetpack-room-ktx = { module = \"androidx.room:room-ktx\", version.ref = \"jetpack-room\" }\njetpack-room-compiler = { module = \"androidx.room:room-compiler\", version.ref = \"jetpack-room\" }\njetpack-autofill = { module = \"androidx.autofill:autofill\", version.ref = \"jetpack-autofill\" }\njetpack-swiperefreshlayout = { module = \"androidx.autofill:autofill\", version.ref = \"jetpack-swiperefreshlayout\" } # 下拉刷新控件\njetpack-constraintlayout = { module = \"androidx.constraintlayout:constraintlayout\", version.ref = \"jetpack-constraintlayout\" } # 约束布局\njetpack-biometric = { module = \"androidx.biometric:biometric\", version.ref = \"jetpack-biometric\" } # 生物识别\njetpack-palette-ktx = { module = \"androidx.palette:palette-ktx\", version = \"1.0.0\" } # 取色板  https://mvnrepository.com/artifact/androidx.palette/palette\njetpack-material2 = { module = \"com.google.android.material:material\", version.ref = \"jetpack-material2\" }\njetpack-multidex = { module = \"androidx.multidex:multidex\", version.ref = \"jetpack-multidex\" }\njetpack-documentfile = { module = \"androidx.documentfile:documentfile\", version.ref = \"jetpack-documentfile\" }\n\nkotlin-stdlib-jdk8 = { module = \"org.jetbrains.kotlin:kotlin-stdlib-jdk8\", version.ref = \"kotlin\" }\nkotlin-reflect = { module = \"org.jetbrains.kotlin:kotlin-reflect\", version.ref = \"kotlin\" } #kotlin 反射依赖\nkotlin-core-ktx = { module = \"androidx.core:core-ktx\", version.ref = \"kotlin-android-core\" } # Androidx 组件，包括了协程\nkotlin-coroutines-android = { module = \"org.jetbrains.kotlinx:kotlinx-coroutines-android\", version.ref = \"kotlin-coroutines\" } # 协程支持\n\ntest-junit = { module = \"junit:junit\", version.ref = \"test-junit\" }\ntest-android-runner = { module = \"androidx.test:runner\", version.ref = \"test-runner\" }\ntest-android-junit = { module = \"androidx.test.ext:junit\", version.ref = \"test-junut-ext\" }\ntest-android-rules = { module = \"androidx.test:rules\", version.ref = \"test-rules\" }\ntest-espresso = { module = \"androidx.test.espresso:espresso-core\", version.ref = \"test-espresso\" }\ntest-mockk = { module = \"io.mockk:mockk\", version.ref = \"test-mockk\" }\ntest-mockk-android = { module = \"io.mockk:mockk-android\", version.ref = \"test-mockk\" }\n\n\nsquareup-retrofit = { module = \"com.squareup.retrofit2:retrofit\", version.ref = \"squareup-retrofit\" }\nsquareup-retrofit-gson = { module = \"com.squareup.retrofit2:converter-gson\", version.ref = \"squareup-retrofit\" }\nsquareup-okhttp3 = { module = \"com.squareup.okhttp3:okhttp\", version.ref = \"squareup-okhttp3\" }\nsquareup-okhttp3-logginginterceptor = { module = \"com.squareup.okhttp3:logging-interceptor\", version.ref = \"squareup-okhttp3\" }\n\ngroovy-core = { module = \"org.codehaus.groovy:groovy\", version.ref = \"groovy\" }\ngroovy-json = { module = \"org.codehaus.groovy:groovy-json\", version.ref = \"groovy\" }\ngroovy-nio = { module = \"org.codehaus.groovy:groovy-nio\", version.ref = \"groovy\" }\ncommons-lang3 = { group = \"org.apache.commons\", name = \"commons-lang3\", version = { strictly = \"[3.8, 4.0[\", prefer = \"3.9\" } }\n\n[bundles]\ngroovy = [\"groovy-core\", \"groovy-json\", \"groovy-nio\"]\n\njetpack-compose = [\"jetpack-compose-ui-tooling\", \"jetpack-compose-ui-tooling-preview\", \"jetpack-compose-ui\", \"jetpack-compose-runtime\", \"jetpack-compose-material3\"]\njetpack-ui = [\"jetpack-appcompat\", \"jetpack-material2\", \"jetpack-preference-ktx\", \"jetpack-viewpager2\", \"jetpack-cardview\", \"jetpack-recyclerview\", \"jetpack-constraintlayout\", \"jetpack-swiperefreshlayout\"]\njetpack-lifecycle = [\"jetpack-lifecycle-viewmodel-ktx\", \"jetpack-lifecycle-livedata-ktx\", \"jetpack-lifecycle-common-java8\"]\nandroid-test = [\"test-android-runner\", \"test-android-junit\", \"test-android-rules\", \"test-espresso\", \"test-mockk-android\"]\nandroid-kotlin = [\"kotlin-stdlib-jdk8\", \"kotlin-reflect\", \"kotlin-core-ktx\", \"kotlin-coroutines-android\"]\n\nthird-eventbus = [\"third-eventbus\", \"third-eventbus-compiler\"]\nthird-spongycastle = [\"spongycastle-core\", \"spongycastle-prov\"]\n\ntencent-wcdb = [\"tencent-wcdb\", \"tencent-wcdb-room\"]\ntencent-bugly = [\"tencent-bugly-crashreport\", \"tencent-bugly-nativecrashreport\"]\n\nsquareup-netlibs = [\"squareup-retrofit\", \"squareup-retrofit-gson\", \"squareup-okhttp3\", \"squareup-okhttp3-logginginterceptor\"]\n\n\n\n\n"
  },
  {
    "path": "localMaven/com/alibaba/arouter-register-asm7/1.0.2/arouter-register-asm7-1.0.2-javadoc.jar.md5",
    "content": "0c49d2b2bcbdc5b6e14f1ad54f2f7d42"
  },
  {
    "path": "localMaven/com/alibaba/arouter-register-asm7/1.0.2/arouter-register-asm7-1.0.2-javadoc.jar.sha1",
    "content": "ffa836a313142568c4c7929516f12fdfffb8fb5c"
  },
  {
    "path": "localMaven/com/alibaba/arouter-register-asm7/1.0.2/arouter-register-asm7-1.0.2-sources.jar.md5",
    "content": "90ed59c3829a0bbe9e9025e44d97c11f"
  },
  {
    "path": "localMaven/com/alibaba/arouter-register-asm7/1.0.2/arouter-register-asm7-1.0.2-sources.jar.sha1",
    "content": "ffa51b72bee753ce30a380f384d848d436da83b1"
  },
  {
    "path": "localMaven/com/alibaba/arouter-register-asm7/1.0.2/arouter-register-asm7-1.0.2.jar.md5",
    "content": "59889bf2fa075d3f0beafdeb8fe3870d"
  },
  {
    "path": "localMaven/com/alibaba/arouter-register-asm7/1.0.2/arouter-register-asm7-1.0.2.jar.sha1",
    "content": "239710f928e971deb36f8ffd6c5d92104487a175"
  },
  {
    "path": "localMaven/com/alibaba/arouter-register-asm7/1.0.2/arouter-register-asm7-1.0.2.pom",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\" xmlns=\"http://maven.apache.org/POM/4.0.0\"\n    xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n  <modelVersion>4.0.0</modelVersion>\n  <groupId>com.alibaba</groupId>\n  <artifactId>arouter-register-asm7</artifactId>\n  <version>1.0.2</version>\n  <name>ARouter Register</name>\n  <description>Gradle plugin used for arouter route map register</description>\n  <url>https://github.com/Alibaba/ARouter/</url>\n  <licenses>\n    <license>\n      <name>The Apache Software License, Version 2.0</name>\n      <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>\n      <distribution>repo</distribution>\n    </license>\n  </licenses>\n  <developers>\n    <developer>\n      <id>zhi1ong</id>\n      <name>ZhiLong Liu</name>\n    </developer>\n  </developers>\n  <scm>\n    <connection>scm:git:git://github.com/Alibaba/ARouter.git</connection>\n    <developerConnection>scm:git:ssh://git@github.com/Alibaba/ARouter.git</developerConnection>\n    <url>https://github.com/Alibaba/ARouter/</url>\n  </scm>\n  <dependencies>\n    <dependency>\n      <groupId>com.android.tools.build</groupId>\n      <artifactId>gradle</artifactId>\n      <version>3.2.0</version>\n      <scope>runtime</scope>\n    </dependency>\n  </dependencies>\n</project>\n"
  },
  {
    "path": "localMaven/com/alibaba/arouter-register-asm7/1.0.2/arouter-register-asm7-1.0.2.pom.md5",
    "content": "b704ef0b5df49c2c728a3c013a77b499"
  },
  {
    "path": "localMaven/com/alibaba/arouter-register-asm7/1.0.2/arouter-register-asm7-1.0.2.pom.sha1",
    "content": "b89e1917824fe76195138d9e2b084bc6ee9367b2"
  },
  {
    "path": "localMaven/com/alibaba/arouter-register-asm7/maven-metadata.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<metadata>\n  <groupId>com.alibaba</groupId>\n  <artifactId>arouter-register-asm7</artifactId>\n  <versioning>\n    <release>1.0.2</release>\n    <versions>\n      <version>1.0.2</version>\n    </versions>\n    <lastUpdated>20221014082010</lastUpdated>\n  </versioning>\n</metadata>\n"
  },
  {
    "path": "localMaven/com/alibaba/arouter-register-asm7/maven-metadata.xml.md5",
    "content": "e936cba0254ba9491de0821a039ed75a"
  },
  {
    "path": "localMaven/com/alibaba/arouter-register-asm7/maven-metadata.xml.sha1",
    "content": "6f82c7eaa9fcc83cbe6f3e59b044145371e1f27d"
  },
  {
    "path": "settings.gradle",
    "content": "\npluginManagement {\n  repositories {\n    gradlePluginPortal()\n    google()\n    mavenCentral()\n    maven { url 'https://jitpack.io' }\n    mavenLocal()\n    maven { url 'https://maven.aliyun.com/nexus/content/groups/public/' }\n    maven {\n      url = \"${rootProject.getPath()}/../localMaven\"\n    }\n  }\n}\n\nenableFeaturePreview('VERSION_CATALOGS')\ndependencyResolutionManagement {\n  //  repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)\n  repositories {\n    google()\n    mavenCentral()\n    mavenLocal()\n    maven { url 'https://maven.aliyun.com/nexus/content/groups/public/' }\n    maven { url 'https://jitpack.io' }\n    maven {\n      url 'https://pkgs.dev.azure.com/MicrosoftDeviceSDK/DuoSDK-Public/_packaging/Duo-SDK-Feed/maven/v1'\n    }\n  }\n\n  versionCatalogs {\n    libs {\n//      from(\"com.lyy.kpa.version:catalog:0.0.1\")\n      from(files('libs.versions.toml'))\n      // 我们也可以重写覆盖catalog中的groovy版本\n//      version(\"groovy\", \"3.0.6\")\n    }\n  }\n\n}\n\ninclude ':app'\ninclude('VersionManager')\nrootProject.name='KeepassA-Lite'\n"
  }
]