├── .gitignore ├── LICENSE.md ├── androidDemo ├── build.gradle.kts └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── org │ └── ferredoxin │ └── ferredoxinui │ └── android │ ├── UiTable.kt │ ├── activity │ └── MainActivity.kt │ └── fragment │ ├── DemoAbout.kt │ ├── DemoFragment.kt │ └── DemoViewPagerFragment.kt ├── build.gradle.kts ├── common ├── build.gradle.kts └── src │ ├── androidMain │ ├── AndroidManifest.xml │ ├── kotlin │ │ └── org │ │ │ └── ferredoxin │ │ │ └── ferredoxinui │ │ │ └── common │ │ │ ├── activity │ │ │ └── MaterialSettingActivity.kt │ │ │ ├── base │ │ │ ├── Preference.kt │ │ │ ├── ResourceProvider.kt │ │ │ ├── UiDSLHelper.kt │ │ │ ├── UiFactory.kt │ │ │ └── UiInterface.kt │ │ │ ├── fragment │ │ │ └── MaterialSettingFragment.kt │ │ │ └── platform.kt │ └── res │ │ ├── anim │ │ ├── slide_left_in.xml │ │ ├── slide_left_out.xml │ │ ├── slide_right_in.xml │ │ └── slide_right_out.xml │ │ └── layout │ │ ├── activity_material3_setting.xml │ │ └── activity_material_setting.xml │ ├── commonMain │ └── kotlin │ │ └── org │ │ └── ferredoxin │ │ └── ferredoxinui │ │ └── common │ │ ├── App.kt │ │ ├── base │ │ ├── Preference.kt │ │ ├── ResourceProvider.kt │ │ ├── TitleAble.kt │ │ ├── UiDSLHelper.kt │ │ ├── UiFactory.kt │ │ ├── UiInterface.kt │ │ └── UiScreenLoader.kt │ │ └── platform.kt │ └── desktopMain │ └── kotlin │ └── org │ └── ferredoxin │ └── ferredoxinui │ └── common │ └── platform.kt ├── desktop ├── build.gradle.kts └── src │ └── jvmMain │ └── kotlin │ └── Main.kt ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── qnotified_style ├── .gitignore ├── build.gradle.kts ├── consumer-rules.pro ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── org │ │ └── ferredoxin │ │ └── ferredoxinui │ │ └── qnotified_style │ │ ├── activity │ │ └── MaiTungTMStyleActivity.kt │ │ ├── base │ │ └── QNotifiedStyleUiDSLHelper.kt │ │ ├── fragment │ │ ├── MaiTungTMSettingFragment.kt │ │ └── ViewPagerFragment.kt │ │ └── item │ │ ├── About.kt │ │ ├── Category.kt │ │ ├── ClickableItem.kt │ │ ├── ClickableSwitchItem.kt │ │ ├── Subtitle.kt │ │ ├── Switch.kt │ │ └── SwitchItem.kt │ └── res │ ├── anim │ ├── slide_left_in_no_alpha.xml │ ├── slide_left_out_no_alpha.xml │ ├── slide_right_in_no_alpha.xml │ └── slide_right_out_no_alpha.xml │ ├── drawable │ ├── ic_arrow_back_black_24dp.xml │ ├── ic_chevron_right_24px.xml │ ├── ic_search_24px.xml │ ├── ic_toggle_disable_24px.xml │ ├── ic_toggle_disable_on.xml │ ├── ic_toggle_off_24px.xml │ ├── ic_toggle_on_24px.xml │ ├── icon_empty.webp │ ├── item_back_ripple.xml │ ├── item_background_ripple.xml │ ├── item_category_background.xml │ ├── item_line.xml │ ├── switch_off_to_on.xml │ ├── switch_on_to_off.xml │ └── tabs_indicator.xml │ ├── layout │ ├── activity_maitung_tm_style.xml │ ├── fragment_empty.xml │ ├── fragment_settings.xml │ ├── fragment_view_pager.xml │ ├── item_about.xml │ ├── item_category.xml │ ├── item_clickable.xml │ ├── item_clickable_switch.xml │ ├── item_subtitle.xml │ └── item_switch.xml │ ├── mipmap │ └── ic_launcher_demo.webp │ ├── values-night │ ├── colors.xml │ └── themes.xml │ └── values │ ├── attrs.xml │ ├── colors.xml │ └── themes.xml └── settings.gradle.kts /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | local.properties 16 | 17 | .idea 18 | 19 | .project 20 | build/ 21 | 22 | .settings 23 | .classpath -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # 通用许可协议 2 | 3 | ## 版权所有 © 2022 James Clef 4 | 5 | 版本 2 2022-04-09 6 | 7 | 本协议证原始发布地址: [https://github.com/qwq233/license](https://github.com/qwq233/license) / [https://qwq2333.top/license-v2](https://qwq2333.top/license-v2) 8 | 9 | 10 | 允许在遵守 CC BY-NC-SA 4.0 协议的同时,复制和分发此协议文档的逐字记录副本,且允许对其进行更改,但必须保留其版权信息与原作者。如果您提出申请特殊权限,协议作者可口头或书面授予任何人任何的使用本协议的权利。 11 | 12 | ## 正文 13 | 14 | 请务必仔细阅读和理解通用许可协议书中规定的所有权利和限制。在使用前,您需要仔细阅读并决定接受或不接受本协议的条款。除非或直至您接受本协议的条款,否则本作品及其相关副本、相关程序代码或相关资源不得在您的任何终端上下载、安装或使用。 15 | 16 | 您一旦下载、使用本作品及其相关副本、相关程序代码或相关资源,即表示您同意接受本协议各项条款的约束。如您不同意本协议中的条款,您则应当立即删除本作品、附属资源及其相关源代码。 17 | 18 | 本作品权利只许可使用,而不出售。 19 | 20 | 1. 定义 21 | 22 | 1. “本协议”指通用许可协议第二版。 23 | 24 | 2. “您”指依据本通用许可协议行使其所获得授予之权利的个人或机构。 “您的” 有相应的含义。 25 | 26 | 3. “作者”或“本作品作者”指通过本协议进行授权的个人或组织和/或根据《与贸易有关的知识产权协定》所规定的对本作品拥有著作权的个人或组织。 27 | 28 | 4. “协议作者”指上文提及的协议版权方,即 James Clef 。 29 | 30 | 5. “本作品”或“作品”指根据本协议的任何受版权保护的作品,如源代码。 31 | 32 | 6. “本作品发布网址”指本作品作者初次或后续发布的所指定的唯一或多个的网址。 33 | 34 | 7. “修改”作品是指以需要版权许可的方式复制或改编该作品的全部或部分内容,而不是制作一个完全的副本。由此产生的作品也被称为本作品的"修改版"或“基于”本作品的作品。 35 | 36 | 8. “修改作品作者”指任何依据本通用许可协议对在修改作品中所贡献的部分所享有的著作权的个人或机构。 37 | 38 | 9. “传播”作品指对该作品进行任何未经许可的行为,这将使您直接或次要根据适用的版权法承担侵权责任,但在计算机上执行该作品或修改其私人副本除外。传播包括复制、分发(进行或不进行修改)、向公众公开、以及在某些国家/地区进行其他活动。 39 | 40 | 10. “非商业使用”指该使用的主要意图或者指向并非获取商业优势或金钱报酬。为本协议之目的,以数字文件共享或类似方式,用本作品交换其他受到著作权与类似权利保护的作品是非商业性使用,只要该交换不直接或潜在涉及金钱报酬的支付。 41 | 42 | 11. “商业使用”指该使用的主要意图或者指向为获取商业优势或金钱报酬。以数字文件共享或类似方式,用本作品交换其他受到著作权与类似权利保护的作品是商业性使用,只要该交换直接或潜在涉及金钱报酬的支付。 43 | 44 | 12. “源代码”指生成、安装和(对于可执行作品)运行目标代码以及修改作品所需的所有源代码,包括控制这些活动的脚本。但是,它不包括作品的系统库,也不包括在执行这些活动时未经修改但不属于作品的通用工具或普遍可用的免费程序。 45 | 46 | 13. “目标代码”指通过本作品源代码或修改作品源代码生成的计算机可识别的机器语言或近似与机器语言的代码。“编译作品”有相同含义。 47 | 48 | 49 | 2. 本协议无意削减、限制、或约束您基于以下法律规定对本作品的合法使用:合理使用,权利穷竭原则,及著作权法或其他相关法律对著作权人专有权利的限制。 50 | 51 | 3. 授权范围 52 | 53 | 根据本协议的条款和条件,作者在此授予您全球性、免版税、非独占并且在本作品的著作权存续期间内均有效的许可,就本作品行使以下权利: 54 | 55 | 1. 在一台个人所有终端上安装、使用、显示、运行本作品的一份副本。 56 | 57 | 2. 为了防止副本损坏而制作备份复制品。这些备份复制品不得通过任何方式提供给他人使用,并在您丧失该合法副本的所有权时,负责将备份复制品销毁。 58 | 59 | 3. 为了把本作品用于实际的终端应用环境或者改进其功能、性能而进行必要的修改。 60 | 61 | 4. 对本作品进行反向工程、反向编译或反汇编;或进行其他获得本作品源代码的访问或行为。 62 | 63 | 5. 发行、公开传播本作品及其修改作品。 64 | 65 | 6. 根据本协议的条款,作者授予您在全球范围内,免费的、不可再许可、非独占、不可撤销的许可,以对本作品行使以下“协议所授予的权利”: 66 | 67 | 1. 复制和分享本作品的全部或部分,仅限于非商业性使用。 68 | 69 | 2. 以非商业使用为目的制作、复制和分享修改作品。 70 | 71 | 以上权利可在任何现有的或者以后出现的并为可适用的法律认可的媒体和形式上行使。上述权利包括为在其他媒体和形式上行使权利而必须进行技术性修改的权利。作者在此保留所有未明示授予的权利。 72 | 73 | 4. 限制 74 | 75 | 1. 您在发行或公开传播本作品时,必须遵守本协议。在您发行或公开传播的本作品的每一份复制件中,您必须附上一份本协议协议的复制件。您不得就本作品提出或增加任何条款,从而限制本协议协议或者限制获得本作品的第三方行使本协议协议所赋予的权利。您不得对本作品进行再许可。您必须在您发行或公开传播的每份作品复制件中完整保留所有与本协议协议及免责条款相关的声明。 在发行或公开传播本作品时,您不得对本作品施加任何技术措施,从而限制从您处获得本作品的第三方行使本协议协议授予的权利。 76 | 77 | 2. 您必须以下述许可条款发行或公开传播修改作品: 78 | 79 | 1. 本协议或后续版本 80 | 81 | 2. 您不得就修改作品提出或增加任何条款,从而限制“可适用的协议”的规定,或者限制获得修改作品的第三方行使“可适用的协议”所赋予的权利。在发行或公开传播包含本作品的修改作品时,您必须在本作品的每一份复制件中完整地保留所有与“可适用的协议”及免责条款相关的声明。在发行或公开传播修改作品时,您不得对修改作品施加任何技术措施,从而限制从您处获得修改作品的第三方行使“可适用的协议”所赋予的权利。本项(第 4 款第 2 项)规定同样适用于收录在汇编作品中的修改作品,但并不要求汇编作品中除基于本作品而创作的修改作品之外的其他作品受“可适用的协议”的约束。 82 | 83 | 3. 以源代码的形式传播本作品或编译作品时,您必须满足以下所有条件: 84 | 85 | 1. 修改作品必须有醒目的声明,说明您修改了它,并给出相关的日期。 86 | 87 | 2. 若修改作品使用了本作品所包含全部或部分源代码和/或其他部分的本作品,需提供完整的修改作品,如其全部源代码、可用于生成修改作品的编译作品的脚本、修改作品使用到的资源。 88 | 89 | 4. 以编译作品的形式传播本作品或修改作品时,以下列方式之一传递源代码: 90 | 91 | 1. 在实体产品(包括实体销售媒介)中传递编译作品,或体现在实体产品(包括实体销售媒介)中,同时将相应的源代码固定在通常用于软件交换的实体媒介上。 92 | 93 | 2. 点对点传输。 94 | 95 | 3. 可免费访问的网络服务器。 96 | 97 | 3. 您不得进行商业使用,这里的商业使用包括第 1 款第 10 项所提到的内容、以盈利为目的的提供本作品和/或修改作品的帮助和/或指南、以盈利为目的的使用或授予他人本作品的所提供给您的权利/许可。 98 | 99 | 4. 在发行或公开传播本作品、任何修改作品时,您必须完整保留所有关于本作品的著作权声明,并以适于所使用的媒介或方法的形式提供下述信息: 100 | 101 | 1. 作者的姓名或者其他能够体现作者身份的标志物。 102 | 103 | 2. 标明本作品发布网址 104 | 105 | 3. 依第 4 条第 2 项之要求,注明修改作品中使用的本作品的作者的姓名或者其他能够体现作者身份的标志物和作品名称。为避免疑义,本条有关标示作者姓名和作品名称之规定,仅适用于前述署名的用途;除非您事先另行取得作者的书面同意,否则您不得以明示或者默示的方式主张或暗示,您本人或您对作品的使用与作者有关联或者已获得上述人士的赞助或者支持。 106 | 107 | 5. 您在复制、发行或者公开表演本作品,或者复制、发行或者公开表演作为任何修改作品一部分的本作品时,不得歪曲、损害或者以其他方式损害本作品,导致原始作者的名誉或者荣誉受损。 108 | 109 | 6. 您不得利用本作品的全部或部分申请商业用途的商标和/或专利。 110 | 111 | 7. 作者拥有对本协议的修改权,当您使用本作品、源代码及其附属资源的修改协议后的作品,需遵守最新协议。 112 | 113 | 5. 免责声明: 114 | 115 | 1. 您在下载并使用本作品时均被视为已经仔细阅读本协议并完全同意。凡以任何方式使用本作品,或直接、间接使用本作品,均被视为自愿接受相关声明和用户服务协议的约束。 116 | 117 | 2. 除非本协议的当事人相互以书面的方式做出相反约定,且在相关法律所允许的最大范围内,否则作者按其现状提供本作品,对本作品不作任何明示或者默示、依照法律或者其他规定的陈述或担保,包括但是不限于任何有关可否商业性使用、是否符合特定的目的、不具有潜在的或者其他缺陷、准确性或者不存在不论能否被发现的错误的担保。有些司法管辖区不允许排除前述默示保证,因此这些排除性规定并不一定适用于您。 118 | 119 | 3. 用户明确并同意其使用本作品所存在的风险及法律风险将完全由其本人承担;因其使用作品而产生的一切后果也由其本人承担,本作品作者对此不承担任何责任。 120 | 121 | 6. 除非书面同意,否则在任何情况下,任何作者与协议作者,或经其修改和/或传送上述程序的任何其他方均不对您承担赔偿责任,包括任何一般的,特殊的,因本作品而使您对其他法律实体造成的一切损害。本作品及作者已提前告知您此类损害的可能性。 122 | 123 | 7. 您在传播、使用本作品及其修改作品时,应自行保证您的一切行为与本作品的全部功能符合一切对您有管辖权的法律法规的要求,由您传播、使用本作品产生的法律风险及其造成的相应后果,将由您自行承担,本作品及其作者不承担任何责任。 124 | 125 | 8. 本协议最终解释权归本作品作者与协议作者所有。 126 | 127 | 128 | 6. 许可终止: 129 | 130 | 1. 在您违反本协议协议任何条款时,本协议及其所授予的权利将自动终止。然而,根据本协议从您处获取修改作品的自然人、法人或者其他组织,如果他们仍完全遵守相关条款,则对他们的许可不会随之终止。即使本协议被终止,第 1 款、第 2 款、第 5 款、第 6 款仍然有效。 131 | 132 | 2. 在上述条款及条件的前提下,此处授予的许可在法定著作权保护期限内有效。即便如此,作者保留依其他许可条款发行本作品及在任何时候停止发行本作品的权利;但是,作者的上述权利不能被用于撤销本协议或任何其他在本协议条款下授予的或必须授予的许可,除本款第 1 项指明的终止外,本协议将保持其完全效力。 133 | -------------------------------------------------------------------------------- /androidDemo/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("com.android.application") 3 | kotlin("android") 4 | id("org.jetbrains.compose") 5 | } 6 | 7 | group = "org.ferredoxin.ferredoxinui" 8 | version = "1.0" 9 | 10 | val java_version = JavaVersion.VERSION_11 11 | 12 | dependencies { 13 | implementation(project(":common")) 14 | implementation(project(":qnotified_style")) 15 | } 16 | 17 | android { 18 | compileSdk = 31 19 | defaultConfig { 20 | applicationId = "org.ferredoxin.ferredoxinui.android" 21 | minSdk = 21 22 | targetSdk = 31 23 | versionCode = 1 24 | versionName = "0.1" 25 | } 26 | buildTypes { 27 | getByName("release") { 28 | isMinifyEnabled = false 29 | } 30 | } 31 | buildFeatures { 32 | viewBinding = true 33 | } 34 | lint { 35 | isCheckDependencies = true 36 | } 37 | compileOptions { 38 | sourceCompatibility(java_version) 39 | targetCompatibility(java_version) 40 | } 41 | kotlinOptions { 42 | jvmTarget = java_version.majorVersion 43 | } 44 | namespace = "org.ferredoxin.ferredoxinui.android" 45 | } -------------------------------------------------------------------------------- /androidDemo/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /androidDemo/src/main/java/org/ferredoxin/ferredoxinui/android/UiTable.kt: -------------------------------------------------------------------------------- 1 | package org.ferredoxin.ferredoxinui.android 2 | 3 | import org.ferredoxin.ferredoxinui.android.fragment.DemoAbout 4 | import org.ferredoxin.ferredoxinui.android.fragment.DemoFragment 5 | import org.ferredoxin.ferredoxinui.android.fragment.DemoViewPagerFragment 6 | import org.ferredoxin.ferredoxinui.common.base.* 7 | 8 | val UiTable = uiScreen { 9 | name = "QNotified" 10 | contains = linkedMapOf(uiCategory { 11 | name = "模块设置" 12 | contains = linkedMapOf(uiClickableItem { 13 | title = "净化功能" 14 | onClickListener = ClickToNewPages(DemoViewPagerFragment) 15 | }, uiClickableItem { 16 | title = "增强功能" 17 | onClickListener = ClickToNewSetting(DemoViewPagerFragment[0].second) 18 | }, 19 | uiClickableItem { 20 | title = "辅助功能" 21 | onClickListener = ClickToNewSetting(DemoFragment) 22 | }, 23 | uiClickableItem { 24 | title = "其他功能" 25 | onClickListener = ClickToNewSetting(uiScreen { 26 | name = "其他功能" 27 | }.second) 28 | } 29 | ) 30 | }, 31 | uiCategory { 32 | name = "其他设置" 33 | contains = linkedMapOf( 34 | uiClickableItem { 35 | title = "参数设定" 36 | }, 37 | uiClickableItem { 38 | title = "故障排查" 39 | } 40 | ) 41 | }, 42 | uiCategory { 43 | name = "更多" 44 | contains = linkedMapOf( 45 | uiClickableItem { 46 | title = "鸽子画饼" 47 | }, 48 | uiClickableItem { 49 | title = "关于" 50 | onClickListener = ClickToNewSetting(DemoAbout) 51 | } 52 | ) 53 | } 54 | ) 55 | } 56 | -------------------------------------------------------------------------------- /androidDemo/src/main/java/org/ferredoxin/ferredoxinui/android/activity/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package org.ferredoxin.ferredoxinui.android.activity 2 | 3 | import androidx.preference.PreferenceFragmentCompat 4 | import org.ferredoxin.ferredoxinui.android.UiTable 5 | import org.ferredoxin.ferredoxinui.common.activity.MaterialSettingActivity 6 | import org.ferredoxin.ferredoxinui.common.base.TitleAble 7 | import org.ferredoxin.ferredoxinui.common.fragment.MaterialSettingFragment 8 | 9 | class MainActivity : MaterialSettingActivity() where T : PreferenceFragmentCompat, T : TitleAble { 10 | 11 | override val theme = MaterialTheme.You 12 | override val fragment: T = MaterialSettingFragment().setUiScreen(UiTable.second) as T 13 | 14 | } 15 | -------------------------------------------------------------------------------- /androidDemo/src/main/java/org/ferredoxin/ferredoxinui/android/fragment/DemoAbout.kt: -------------------------------------------------------------------------------- 1 | package org.ferredoxin.ferredoxinui.android.fragment 2 | 3 | import org.ferredoxin.ferredoxinui.common.base.* 4 | import org.ferredoxin.ferredoxinui.qnotified_style.R 5 | 6 | val DemoAbout: UiScreen = uiScreen { 7 | name = "关于" 8 | contains = linkedMapOf(uiAboutItem { 9 | title = "Ferredoxin UI——QNotified Style demo" 10 | icon = { 11 | it.getDrawable(R.mipmap.ic_launcher_demo)!! 12 | } 13 | }, uiCategory { 14 | noTitle = true 15 | contains = linkedMapOf(uiClickableItem { 16 | title = "用户协议" 17 | }, uiClickableItem { 18 | title = "隐私条款" 19 | }, uiClickableItem { 20 | title = "模块版本" 21 | clickAble = false 22 | subSummary = "0.8.10.260.badce21" 23 | }, uiClickableItem { 24 | title = "QQ版本" 25 | clickAble = false 26 | subSummary = "8.5.5(1630)" 27 | }, uiClickableItem { 28 | title = "检查更新" 29 | }) 30 | }) 31 | }.second -------------------------------------------------------------------------------- /androidDemo/src/main/java/org/ferredoxin/ferredoxinui/android/fragment/DemoFragment.kt: -------------------------------------------------------------------------------- 1 | package org.ferredoxin.ferredoxinui.android.fragment 2 | 3 | import android.widget.TextView 4 | import org.ferredoxin.ferredoxinui.common.base.* 5 | import org.ferredoxin.ferredoxinui.qnotified_style.base.uiDialogPreference 6 | import org.ferredoxin.ferredoxinui.qnotified_style.base.uiEditTextPreference 7 | 8 | val DemoFragment: UiScreen = uiScreen { 9 | name = "功能示例" 10 | contains = linkedMapOf( 11 | uiCategory { 12 | noTitle = true 13 | contains = linkedMapOf(uiDialogPreference { 14 | title = "对话框测试" 15 | materialAlertDialogBuilder = { 16 | setTitle("对话框测试") 17 | setView(TextView(context).apply { 18 | text = "测试文本" 19 | }) 20 | setPositiveButton("确定", null) 21 | } 22 | }, uiEditTextPreference { 23 | title = "文本测试" 24 | inputLayout = { 25 | hint = "一个提示" 26 | helperText = "帮助文本" 27 | } 28 | }, uiClickableSwitchItem { 29 | title = "开关带二级界面" 30 | summary = "辅助文字" 31 | onClickListener = ClickToNewSetting(DemoViewPagerFragment[1].second) 32 | }, uiClickableSwitchItem { 33 | title = "禁用开关带二级界面" 34 | value.value = true 35 | valid = false 36 | } 37 | ) 38 | } 39 | ) 40 | }.second 41 | -------------------------------------------------------------------------------- /androidDemo/src/main/java/org/ferredoxin/ferredoxinui/android/fragment/DemoViewPagerFragment.kt: -------------------------------------------------------------------------------- 1 | package org.ferredoxin.ferredoxinui.android.fragment 2 | 3 | import org.ferredoxin.ferredoxinui.common.base.* 4 | 5 | val DemoViewPagerFragment: ViewMap = listOf( 6 | 7 | uiScreen { 8 | name = "主页" 9 | contains = linkedMapOf( 10 | uiCategory { 11 | name = "示例" 12 | contains = linkedMapOf( 13 | uiClickableItem { 14 | title = "不可用" 15 | summary = "暂不开放" 16 | valid = false 17 | }, 18 | uiClickableItem { 19 | title = "不可用 - 打开二级界面" 20 | summary = "暂不开放" 21 | valid = false 22 | } 23 | ) 24 | }, 25 | uiCategory { 26 | name = "示例2" 27 | contains = linkedMapOf( 28 | uiSwitchItem { 29 | title = "打开开关" 30 | value.value = true 31 | }, 32 | uiSwitchItem { 33 | title = "关闭开关" 34 | value.value = false 35 | } 36 | ) 37 | }, 38 | uiCategory { 39 | name = "示例3" 40 | contains = linkedMapOf( 41 | uiSwitchItem { 42 | title = "不可用" 43 | summary = "暂不开放" 44 | valid = false 45 | }, 46 | uiClickableItem { 47 | title = "不可用 - 打开二级界面" 48 | summary = "暂不开放" 49 | valid = false 50 | } 51 | ) 52 | }, 53 | uiCategory { 54 | name = "示例4" 55 | contains = linkedMapOf( 56 | uiSwitchItem { 57 | title = "不可用" 58 | summary = "暂不开放" 59 | valid = false 60 | value.value = true 61 | }, uiClickableItem { 62 | title = "不可用 - 打开二级界面" 63 | summary = "暂不开放" 64 | }) 65 | }, uiCategory { 66 | name = "示例5" 67 | contains = linkedMapOf(uiClickableItem { 68 | title = "不可用" 69 | summary = "暂不开放" 70 | valid = false 71 | }, uiClickableItem { 72 | title = "不可用 - 打开二级界面" 73 | summary = "暂不开放" 74 | valid = false 75 | }) 76 | }, uiCategory { 77 | name = "示例6" 78 | contains = linkedMapOf(uiSwitchItem { 79 | title = "打开开关" 80 | value.value = true 81 | }, uiSwitchItem { 82 | title = "关闭开关" 83 | value.value = false 84 | }) 85 | }, uiCategory { 86 | name = "示例7" 87 | contains = linkedMapOf(uiSwitchItem { 88 | title = "不可用" 89 | summary = "暂不开放" 90 | valid = false 91 | }, uiClickableItem { 92 | title = "不可用 - 打开二级界面" 93 | summary = "暂不开放" 94 | valid = false 95 | }) 96 | }, uiCategory { 97 | name = "示例8" 98 | contains = linkedMapOf(uiSwitchItem { 99 | title = "不可用" 100 | summary = "暂不开放" 101 | valid = false 102 | value.value = true 103 | }, uiClickableItem { 104 | title = "不可用 - 打开二级界面" 105 | summary = "暂不开放" 106 | }) 107 | }) 108 | }, 109 | uiScreen { 110 | name = "侧滑" 111 | contains = linkedMapOf( 112 | uiCategory { 113 | name = "示例" 114 | contains = linkedMapOf( 115 | uiClickableItem { 116 | title = "不可用" 117 | summary = "暂不开放" 118 | }, 119 | uiClickableItem { 120 | title = "不可用 - 打开二级界面" 121 | summary = "暂不开放" 122 | } 123 | ) 124 | } 125 | ) 126 | }, 127 | uiScreen { 128 | name = "聊天" 129 | contains = linkedMapOf( 130 | uiCategory { 131 | name = "示例" 132 | contains = linkedMapOf( 133 | uiClickableItem { 134 | title = "不可用" 135 | summary = "暂不开放" 136 | }, 137 | uiClickableItem { 138 | title = "不可用 - 打开二级界面" 139 | summary = "暂不开放" 140 | }) 141 | }, uiCategory { 142 | name = "示例2" 143 | contains = linkedMapOf(uiChangeableItem { 144 | title = "打开开关" 145 | value.value = "虽然这不是一个开关" 146 | }) 147 | }, uiCategory { 148 | name = "示例3" 149 | contains = linkedMapOf(uiClickableItem { 150 | title = "不可用" 151 | summary = "暂不开放" 152 | }, uiClickableItem { 153 | title = "不可用 - 打开二级界面" 154 | summary = "暂不开放" 155 | }) 156 | }, uiCategory { 157 | name = "示例4" 158 | contains = linkedMapOf(uiChangeableItem { 159 | title = "打开开关" 160 | value.value = "虽然这不是一个开关" 161 | }) 162 | }) 163 | }, 164 | uiScreen { 165 | name = "群聊" 166 | contains = linkedMapOf( 167 | uiCategory { 168 | name = "示例" 169 | contains = linkedMapOf( 170 | uiClickableItem { 171 | title = "不可用" 172 | summary = "暂不开放" 173 | }, 174 | uiClickableItem { 175 | title = "不可用 - 打开二级界面" 176 | summary = "暂不开放" 177 | } 178 | ) 179 | }, 180 | uiCategory { 181 | name = "示例2" 182 | contains = linkedMapOf( 183 | uiChangeableItem { 184 | title = "打开开关" 185 | value.value = "虽然这不是一个开关" 186 | } 187 | ) 188 | } 189 | ) 190 | }, 191 | uiScreen { 192 | name = "扩展" 193 | contains = linkedMapOf( 194 | uiCategory { 195 | name = "示例" 196 | contains = linkedMapOf( 197 | uiClickableItem { 198 | title = "不可用" 199 | summary = "暂不开放" 200 | }, 201 | uiClickableItem { 202 | title = "不可用 - 打开二级界面" 203 | summary = "暂不开放" 204 | } 205 | ) 206 | }, 207 | uiCategory { 208 | name = "示例2" 209 | contains = linkedMapOf( 210 | uiChangeableItem { 211 | title = "打开开关" 212 | value.value = "虽然这不是一个开关" 213 | } 214 | ) 215 | } 216 | ) 217 | }, 218 | ) 219 | -------------------------------------------------------------------------------- /build.gradle.kts: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | maven("https://maven.aliyun.com/nexus/content/groups/public/") 4 | gradlePluginPortal() 5 | mavenCentral() 6 | google() 7 | maven("https://jitpack.io") 8 | maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") 9 | } 10 | dependencies { 11 | val kotlin_version = "1.5.31" 12 | classpath("org.jetbrains.compose:compose-gradle-plugin:1.0.0") 13 | classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version") 14 | classpath("com.android.tools.build:gradle:7.0.4") 15 | } 16 | } 17 | 18 | allprojects { 19 | afterEvaluate { 20 | // Remove log pollution until Android support in KMP improves. 21 | project.extensions.findByType()?.let { kmpExt -> 22 | kmpExt.sourceSets.removeAll { arrayOf("androidAndroidTestRelease").contains(it.name) } 23 | } 24 | } 25 | } 26 | 27 | group = "org.ferredoxin.ferredoxinui" 28 | version = "1.0" -------------------------------------------------------------------------------- /common/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.jetbrains.compose.compose 2 | 3 | plugins { 4 | id("org.jetbrains.compose") 5 | kotlin("multiplatform") 6 | id("com.android.library") 7 | } 8 | 9 | group = "org.ferredoxin.ferredoxinui" 10 | version = "1.0" 11 | 12 | val java_version = JavaVersion.VERSION_11 13 | 14 | kotlin { 15 | android { 16 | compilations.all { 17 | kotlinOptions.jvmTarget = java_version.majorVersion 18 | } 19 | } 20 | jvm("desktop") { 21 | compilations.all { 22 | kotlinOptions.jvmTarget = java_version.majorVersion 23 | } 24 | } 25 | sourceSets { 26 | val commonMain by getting { 27 | dependencies { 28 | api(compose.runtime) 29 | api(compose.foundation) 30 | api(compose.material) 31 | api(compose.preview) 32 | } 33 | } 34 | val androidMain by getting { 35 | dependencies { 36 | api("androidx.activity:activity-compose:1.4.0") 37 | api("androidx.appcompat:appcompat:1.4.0") 38 | api("androidx.core:core-ktx:1.7.0") 39 | api("com.google.android.material:material:1.6.0-alpha01") 40 | 41 | val lifecycleVersion = "2.4.0-alpha03" 42 | api("androidx.lifecycle:lifecycle-runtime-ktx:$lifecycleVersion") 43 | api("androidx.lifecycle:lifecycle-common-java8:$lifecycleVersion") 44 | 45 | val preference_version = "1.1.1" 46 | api("androidx.preference:preference:$preference_version") 47 | api("androidx.preference:preference-ktx:$preference_version") 48 | } 49 | } 50 | val desktopMain by getting { 51 | dependencies { 52 | api(compose.desktop.common) 53 | } 54 | } 55 | } 56 | } 57 | 58 | android { 59 | compileSdk = 31 60 | sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml") 61 | defaultConfig { 62 | minSdk = 21 63 | targetSdk = 31 64 | } 65 | buildFeatures { 66 | viewBinding = true 67 | } 68 | compileOptions { 69 | sourceCompatibility(java_version) 70 | targetCompatibility(java_version) 71 | } 72 | namespace = "org.ferredoxin.ferredoxinui.common" 73 | 74 | } 75 | 76 | java { 77 | sourceCompatibility = java_version 78 | targetCompatibility = java_version 79 | } -------------------------------------------------------------------------------- /common/src/androidMain/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /common/src/androidMain/kotlin/org/ferredoxin/ferredoxinui/common/activity/MaterialSettingActivity.kt: -------------------------------------------------------------------------------- 1 | package org.ferredoxin.ferredoxinui.common.activity 2 | 3 | import android.os.Bundle 4 | import androidx.annotation.LayoutRes 5 | import androidx.annotation.StyleRes 6 | import androidx.appcompat.app.AppCompatActivity 7 | import androidx.appcompat.widget.Toolbar 8 | import androidx.fragment.app.Fragment 9 | import androidx.preference.PreferenceFragmentCompat 10 | import org.ferredoxin.ferredoxinui.common.R 11 | import org.ferredoxin.ferredoxinui.common.base.TitleAble 12 | import org.ferredoxin.ferredoxinui.common.base.UiDescription 13 | import org.ferredoxin.ferredoxinui.common.base.ViewMap 14 | import org.ferredoxin.ferredoxinui.common.base.uiScreen 15 | import org.ferredoxin.ferredoxinui.common.fragment.MaterialSettingFragment 16 | import java.io.* 17 | 18 | abstract class MaterialSettingActivity : AppCompatActivity() where T : PreferenceFragmentCompat, T : TitleAble { 19 | 20 | abstract val theme: MaterialTheme 21 | abstract val fragment: T 22 | 23 | private lateinit var toolbar: Toolbar 24 | 25 | override fun onCreate(savedInstanceState: Bundle?) { 26 | @StyleRes val themeInt: Int = when (theme) { 27 | MaterialTheme.Classic -> com.google.android.material.R.style.Theme_MaterialComponents_DayNight_NoActionBar 28 | MaterialTheme.You -> com.google.android.material.R.style.Theme_Material3_DayNight_NoActionBar 29 | } 30 | setTheme(themeInt) 31 | super.onCreate(savedInstanceState) 32 | @LayoutRes val layoutInt: Int = when (theme) { 33 | MaterialTheme.Classic -> R.layout.activity_material_setting 34 | MaterialTheme.You -> R.layout.activity_material3_setting 35 | } 36 | setContentView(layoutInt) 37 | toolbar = findViewById(R.id.toolbar) 38 | changeFragment(fragment) 39 | } 40 | 41 | fun changeFragment(fragment: T) where T : Fragment, T : TitleAble { 42 | supportFragmentManager.beginTransaction() 43 | .setCustomAnimations(R.anim.slide_right_in, R.anim.slide_left_out, R.anim.slide_left_in, R.anim.slide_right_out) 44 | .replace(R.id.content_frame, fragment).addToBackStack(fragment.title).commit() 45 | toolbar.title = fragment.title 46 | } 47 | 48 | fun changeFragment(viewMap: ViewMap, string: String) { 49 | val uiScreen = uiScreen { 50 | title = string 51 | contains = mutableMapOf().apply { 52 | for (pair in viewMap) { 53 | this[pair.first] = pair.second 54 | } 55 | } 56 | }.second 57 | val rootFragment = MaterialSettingFragment().setUiScreen(uiScreen) 58 | supportFragmentManager.beginTransaction() 59 | .setCustomAnimations(R.anim.slide_right_in, R.anim.slide_left_out, R.anim.slide_left_in, R.anim.slide_right_out) 60 | .replace(R.id.content_frame, rootFragment).addToBackStack(uiScreen.name).commit() 61 | toolbar.title = uiScreen.name 62 | } 63 | 64 | override fun onBackPressed() { 65 | if (supportFragmentManager.backStackEntryCount <= 1) { 66 | finish() 67 | } else { 68 | toolbar.title = supportFragmentManager.getBackStackEntryAt(0).name 69 | supportFragmentManager.popBackStack() 70 | } 71 | } 72 | 73 | override fun recreate() { 74 | finish() 75 | startActivity(intent) 76 | } 77 | 78 | enum class MaterialTheme { 79 | Classic, You 80 | } 81 | 82 | } -------------------------------------------------------------------------------- /common/src/androidMain/kotlin/org/ferredoxin/ferredoxinui/common/base/Preference.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * QNotified - An Xposed module for QQ/TIM 3 | * Copyright (C) 2019-2021 dmca@ioctl.cc 4 | * https://github.com/ferredoxin/QNotified 5 | * 6 | * This software is non-free but opensource software: you can redistribute it 7 | * and/or modify it under the terms of the GNU Affero General Public License 8 | * as published by the Free Software Foundation; either 9 | * version 3 of the License, or any later version and our eula as published 10 | * by ferredoxin. 11 | * 12 | * This software is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | * Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * and eula along with this software. If not, see 19 | * 20 | * . 21 | */ 22 | 23 | package org.ferredoxin.ferredoxinui.common.base 24 | 25 | import android.app.Activity 26 | 27 | actual sealed interface UiPreference : UiDescription { 28 | actual val title: String 29 | actual val summary: String? 30 | actual var valid: Boolean 31 | actual val clickAble: Boolean 32 | actual val subSummary: String? 33 | val onClickListener: (Activity) -> Boolean 34 | actual val titleProvider: ResourceProvider 35 | actual val summaryProvider: ResourceProvider 36 | } 37 | -------------------------------------------------------------------------------- /common/src/androidMain/kotlin/org/ferredoxin/ferredoxinui/common/base/ResourceProvider.kt: -------------------------------------------------------------------------------- 1 | package org.ferredoxin.ferredoxinui.common.base 2 | 3 | import android.content.Context 4 | 5 | actual interface ResourceProvider { 6 | fun getValue(context: Context): T 7 | } 8 | 9 | actual class DirectResourceProvider actual constructor(val value: T) : ResourceProvider { 10 | override inline fun getValue(context: Context) = value 11 | } 12 | 13 | actual class FunctionalResourceProvider(val function: (Context) -> T) : ResourceProvider { 14 | override inline fun getValue(context: Context): T { 15 | return function.invoke(context) 16 | } 17 | } -------------------------------------------------------------------------------- /common/src/androidMain/kotlin/org/ferredoxin/ferredoxinui/common/base/UiDSLHelper.kt: -------------------------------------------------------------------------------- 1 | @file:JvmName("UiDSLHelperKtAndroid") 2 | 3 | package org.ferredoxin.ferredoxinui.common.base 4 | 5 | import android.app.Activity 6 | import android.content.Intent 7 | import androidx.lifecycle.Lifecycle 8 | import androidx.lifecycle.LifecycleOwner 9 | import androidx.lifecycle.lifecycleScope 10 | import androidx.lifecycle.repeatOnLifecycle 11 | import kotlinx.coroutines.flow.StateFlow 12 | import kotlinx.coroutines.flow.collect 13 | import kotlinx.coroutines.launch 14 | 15 | class ClickToActivity(val activity: Class) : (Activity) -> Boolean { 16 | override fun invoke(p1: Activity): Boolean { 17 | p1.startActivity(Intent(p1, activity)) 18 | return true 19 | } 20 | } 21 | 22 | sealed class ClickListenerAgent : (Activity) -> Boolean { 23 | override fun invoke(p1: Activity): Boolean { 24 | throw IllegalAccessException("Invalid operation") 25 | } 26 | } 27 | 28 | class ClickToNewSetting(val uiScreen: UiScreen) : ClickListenerAgent() 29 | 30 | class ClickToNewPages(val viewMap: ViewMap) : ClickListenerAgent() 31 | 32 | fun LifecycleOwner.observeStateFlow(stateFlow: StateFlow, action: suspend (T) -> Unit) { 33 | lifecycleScope.launch { 34 | repeatOnLifecycle(Lifecycle.State.STARTED) { 35 | stateFlow.collect(action) 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /common/src/androidMain/kotlin/org/ferredoxin/ferredoxinui/common/base/UiFactory.kt: -------------------------------------------------------------------------------- 1 | package org.ferredoxin.ferredoxinui.common.base 2 | 3 | import android.app.Activity 4 | import android.content.Context 5 | import android.graphics.drawable.Drawable 6 | import com.google.android.material.dialog.MaterialAlertDialogBuilder 7 | import com.google.android.material.textfield.TextInputEditText 8 | import com.google.android.material.textfield.TextInputLayout 9 | import kotlinx.coroutines.flow.MutableStateFlow 10 | 11 | actual open class UiClickableItemFactory : UiPreference { 12 | actual override var title: String = "" 13 | actual override var summary: String? = null 14 | actual override var valid: Boolean = true 15 | actual override var clickAble: Boolean = true 16 | actual override var subSummary: String? = null 17 | override var onClickListener: (Activity) -> Boolean = { true } 18 | private lateinit var titleProviderCache: ResourceProvider 19 | override var titleProvider: ResourceProvider 20 | get() { 21 | if (this::titleProviderCache.isInitialized) { 22 | return titleProviderCache 23 | } else { 24 | return DirectResourceProvider(title) 25 | } 26 | } 27 | set(value) { 28 | titleProviderCache = value 29 | } 30 | private lateinit var summaryProviderCache: ResourceProvider 31 | override var summaryProvider: ResourceProvider 32 | get() { 33 | if (this::summaryProviderCache.isInitialized) { 34 | return summaryProviderCache 35 | } else { 36 | return DirectResourceProvider(summary) 37 | } 38 | } 39 | set(value) { 40 | summaryProviderCache = value 41 | } 42 | } 43 | 44 | actual open class UiChangeableItemFactory : UiChangeablePreference, UiClickableItemFactory() { 45 | actual override val value: MutableStateFlow = MutableStateFlow(null) 46 | } 47 | 48 | actual open class UiSwitchItemFactory : UiSwitchPreference, UiChangeableItemFactory() { 49 | override var onClickListener: (Activity) -> Boolean = { true } 50 | } 51 | 52 | interface ContextWrapper { 53 | val contextWrapper: (Context) -> Context 54 | } 55 | 56 | class MaterialAlertDialogPreferenceFactory : UiChangeablePreference, ContextWrapper { 57 | override lateinit var title: String 58 | override var summary: String? = null 59 | override var valid: Boolean = true 60 | override var clickAble: Boolean = true 61 | override var subSummary: String? = null 62 | override var onClickListener: (Activity) -> Boolean = { true } 63 | override val value: MutableStateFlow = MutableStateFlow(null) 64 | override var contextWrapper: (Context) -> Context = { it } 65 | var materialAlertDialogBuilder: MaterialAlertDialogBuilder.() -> Unit = {} 66 | private lateinit var titleProviderCache: ResourceProvider 67 | override var titleProvider: ResourceProvider 68 | get() { 69 | if (this::titleProviderCache.isInitialized) { 70 | return titleProviderCache 71 | } else { 72 | return DirectResourceProvider(title) 73 | } 74 | } 75 | set(value) { 76 | titleProviderCache = value 77 | } 78 | private lateinit var summaryProviderCache: ResourceProvider 79 | override var summaryProvider: ResourceProvider 80 | get() { 81 | if (this::summaryProviderCache.isInitialized) { 82 | return summaryProviderCache 83 | } else { 84 | return DirectResourceProvider(summary) 85 | } 86 | } 87 | set(value) { 88 | summaryProviderCache = value 89 | } 90 | } 91 | 92 | class EditPreferenceFactory : UiChangeablePreference, ContextWrapper { 93 | override val value: MutableStateFlow = MutableStateFlow(null) 94 | override lateinit var title: String 95 | override var summary: String? = null 96 | override var valid: Boolean = true 97 | override var clickAble: Boolean = true 98 | override var subSummary: String? = null 99 | var theme = com.google.android.material.R.style.Widget_MaterialComponents_TextInputLayout_FilledBox 100 | override lateinit var onClickListener: (Activity) -> Boolean 101 | var inputLayout: TextInputLayout.() -> Unit = {} 102 | override var contextWrapper: (Context) -> Context = { it } 103 | var textInputEditText: TextInputEditText.() -> Unit = {} 104 | private lateinit var titleProviderCache: ResourceProvider 105 | override var titleProvider: ResourceProvider 106 | get() { 107 | if (this::titleProviderCache.isInitialized) { 108 | return titleProviderCache 109 | } else { 110 | return DirectResourceProvider(title) 111 | } 112 | } 113 | set(value) { 114 | titleProviderCache = value 115 | } 116 | private lateinit var summaryProviderCache: ResourceProvider 117 | override var summaryProvider: ResourceProvider 118 | get() { 119 | if (this::summaryProviderCache.isInitialized) { 120 | return summaryProviderCache 121 | } else { 122 | return DirectResourceProvider(summary) 123 | } 124 | } 125 | set(value) { 126 | summaryProviderCache = value 127 | } 128 | } 129 | 130 | actual class UiAboutItemFactory : UiAboutItem { 131 | override lateinit var title: String 132 | override lateinit var icon: (Context) -> Drawable 133 | private lateinit var titleProviderCache: ResourceProvider 134 | override var titleProvider: ResourceProvider 135 | get() { 136 | if (this::titleProviderCache.isInitialized) { 137 | return titleProviderCache 138 | } else { 139 | return DirectResourceProvider(title) 140 | } 141 | } 142 | set(value) { 143 | titleProviderCache = value 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /common/src/androidMain/kotlin/org/ferredoxin/ferredoxinui/common/base/UiInterface.kt: -------------------------------------------------------------------------------- 1 | package org.ferredoxin.ferredoxinui.common.base 2 | 3 | import android.content.Context 4 | import android.graphics.drawable.Drawable 5 | 6 | actual interface UiAboutItem : UiDescription, TitleAble { 7 | val icon: (Context) -> Drawable 8 | } 9 | -------------------------------------------------------------------------------- /common/src/androidMain/kotlin/org/ferredoxin/ferredoxinui/common/fragment/MaterialSettingFragment.kt: -------------------------------------------------------------------------------- 1 | package org.ferredoxin.ferredoxinui.common.fragment 2 | 3 | import android.os.Bundle 4 | import androidx.fragment.app.FragmentActivity 5 | import androidx.preference.* 6 | import org.ferredoxin.ferredoxinui.common.activity.MaterialSettingActivity 7 | import org.ferredoxin.ferredoxinui.common.base.* 8 | 9 | class MaterialSettingFragment : PreferenceFragmentCompat(), TitleAble { 10 | 11 | private lateinit var uiScreen: UiScreen 12 | override lateinit var title: String 13 | private lateinit var titleProviderCache: ResourceProvider 14 | override var titleProvider: ResourceProvider 15 | get() { 16 | if (this::titleProviderCache.isInitialized) { 17 | return titleProviderCache 18 | } else { 19 | return DirectResourceProvider(title) 20 | } 21 | } 22 | set(value) { 23 | titleProviderCache = value 24 | } 25 | 26 | override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { 27 | preferenceManager.preferenceDataStore = null 28 | preferenceScreen = preferenceManager.createPreferenceScreen(requireContext()) 29 | addViewInUiGroup(uiScreen, preferenceScreen) 30 | } 31 | 32 | private fun addViewInUiGroup(uiGroup: UiGroup, preferenceGroup: PreferenceGroup) { 33 | for (uiDescription in uiGroup.contains.values) { 34 | when { 35 | uiDescription is UiCategory -> { 36 | val preferenceCategory = PreferenceCategory(requireContext()).apply { 37 | title = uiDescription.nameProvider.getValue(requireContext()) 38 | } 39 | preferenceGroup.addPreference(preferenceCategory) 40 | if (uiDescription.contains.isNotEmpty()) { 41 | addViewInUiGroup(uiDescription, preferenceCategory) 42 | } 43 | } 44 | uiDescription is UiScreen -> { 45 | preferenceGroup.addPreference(Preference(requireContext()).apply { 46 | title = uiDescription.nameProvider.getValue(requireContext()) 47 | summary = uiDescription.summaryProvider.getValue(requireContext()) 48 | onPreferenceClickListener = getOnClickListener(ClickToNewSetting(uiDescription), uiDescription.name) 49 | }) 50 | } 51 | uiDescription is UiPreference -> { 52 | when (uiDescription) { 53 | is UiClickableSwitchPreference -> { 54 | preferenceGroup.addPreference(SwitchPreferenceCompat(requireContext()).apply { 55 | title = uiDescription.titleProvider.getValue(requireContext()) 56 | summary = uiDescription.summaryProvider.getValue(requireContext()) 57 | setOnPreferenceChangeListener { _: Preference, any: Any -> 58 | val bool = any as? Boolean 59 | if (bool != null) { 60 | uiDescription.value.value = bool 61 | } 62 | true 63 | } 64 | isEnabled = uiDescription.valid 65 | observeStateFlow(uiDescription.value) { 66 | isChecked = it ?: isChecked 67 | } 68 | onPreferenceClickListener = getOnClickListener(uiDescription.onClickListener, uiDescription.title) 69 | }) 70 | } 71 | is UiSwitchPreference -> { 72 | preferenceGroup.addPreference(SwitchPreferenceCompat(requireContext()).apply { 73 | title = uiDescription.titleProvider.getValue(requireContext()) 74 | summary = uiDescription.summaryProvider.getValue(requireContext()) 75 | setOnPreferenceChangeListener { _: Preference, any: Any -> 76 | val bool = any as? Boolean 77 | if (bool != null) { 78 | uiDescription.value.value = bool 79 | } 80 | true 81 | } 82 | isEnabled = uiDescription.valid 83 | observeStateFlow(uiDescription.value) { 84 | isChecked = it ?: isChecked 85 | } 86 | }) 87 | } 88 | is UiChangeablePreference<*> -> { 89 | preferenceGroup.addPreference(Preference(requireContext()).apply { 90 | title = uiDescription.titleProvider.getValue(requireContext()) 91 | summary = uiDescription.summaryProvider.getValue(requireContext()) 92 | isEnabled = uiDescription.valid 93 | observeStateFlow(uiDescription.value) { 94 | summary = it?.toString() 95 | } 96 | onPreferenceClickListener = getOnClickListener(uiDescription.onClickListener, uiDescription.title) 97 | }) 98 | } 99 | is UiPreference -> { 100 | preferenceGroup.addPreference(Preference(requireContext()).apply { 101 | title = uiDescription.titleProvider.getValue(requireContext()) 102 | summary = uiDescription.summaryProvider.getValue(requireContext()) 103 | isEnabled = uiDescription.valid 104 | onPreferenceClickListener = getOnClickListener(uiDescription.onClickListener, uiDescription.title) 105 | }) 106 | } 107 | } 108 | } 109 | } 110 | } 111 | } 112 | 113 | fun setUiScreen(uiScreen: UiScreen): MaterialSettingFragment { 114 | this.uiScreen = uiScreen 115 | this.title = uiScreen.name 116 | return this 117 | } 118 | 119 | private fun getOnClickListener(listener: (FragmentActivity) -> Boolean, title: String): Preference.OnPreferenceClickListener { 120 | return when (listener) { 121 | is ClickToNewSetting -> { 122 | Preference.OnPreferenceClickListener { 123 | (requireActivity() as MaterialSettingActivity<*>).changeFragment(MaterialSettingFragment().setUiScreen(listener.uiScreen)) 124 | true 125 | } 126 | } 127 | is ClickToNewPages -> { 128 | Preference.OnPreferenceClickListener { 129 | (requireActivity() as MaterialSettingActivity<*>).changeFragment(listener.viewMap, title) 130 | true 131 | } 132 | } 133 | else -> { 134 | Preference.OnPreferenceClickListener { 135 | listener.invoke(requireActivity()) 136 | } 137 | } 138 | } 139 | } 140 | 141 | } -------------------------------------------------------------------------------- /common/src/androidMain/kotlin/org/ferredoxin/ferredoxinui/common/platform.kt: -------------------------------------------------------------------------------- 1 | package org.ferredoxin.ferredoxinui.common 2 | 3 | actual fun getPlatformName(): String { 4 | return "Android" 5 | } -------------------------------------------------------------------------------- /common/src/androidMain/res/anim/slide_left_in.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 11 | -------------------------------------------------------------------------------- /common/src/androidMain/res/anim/slide_left_out.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 11 | -------------------------------------------------------------------------------- /common/src/androidMain/res/anim/slide_right_in.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 11 | -------------------------------------------------------------------------------- /common/src/androidMain/res/anim/slide_right_out.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 11 | -------------------------------------------------------------------------------- /common/src/androidMain/res/layout/activity_material3_setting.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 12 | 13 | 18 | 19 | 25 | 26 | 27 | 28 | 29 | 30 | 34 | 35 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /common/src/androidMain/res/layout/activity_material_setting.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 11 | 12 | 16 | 17 | 18 | 19 | 23 | 24 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /common/src/commonMain/kotlin/org/ferredoxin/ferredoxinui/common/App.kt: -------------------------------------------------------------------------------- 1 | package org.ferredoxin.ferredoxinui.common 2 | 3 | import androidx.compose.material.Button 4 | import androidx.compose.material.MaterialTheme 5 | import androidx.compose.material.Text 6 | import androidx.compose.runtime.* 7 | 8 | @Composable 9 | fun App() { 10 | var text by remember { mutableStateOf("Hello, World!") } 11 | 12 | MaterialTheme { 13 | Button(onClick = { 14 | text = "Hello, ${getPlatformName()}" 15 | }) { 16 | Text(text) 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /common/src/commonMain/kotlin/org/ferredoxin/ferredoxinui/common/base/Preference.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * QNotified - An Xposed module for QQ/TIM 3 | * Copyright (C) 2019-2021 dmca@ioctl.cc 4 | * https://github.com/ferredoxin/QNotified 5 | * 6 | * This software is non-free but opensource software: you can redistribute it 7 | * and/or modify it under the terms of the GNU Affero General Public License 8 | * as published by the Free Software Foundation; either 9 | * version 3 of the License, or any later version and our eula as published 10 | * by ferredoxin. 11 | * 12 | * This software is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | * Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * and eula along with this software. If not, see 19 | * 20 | * . 21 | */ 22 | 23 | package org.ferredoxin.ferredoxinui.common.base 24 | 25 | import kotlinx.coroutines.flow.MutableStateFlow 26 | 27 | expect sealed interface UiPreference : UiDescription { 28 | val title: String 29 | val titleProvider: ResourceProvider 30 | val summary: String? 31 | val summaryProvider: ResourceProvider 32 | val clickAble: Boolean 33 | val subSummary: String? 34 | var valid: Boolean 35 | } 36 | 37 | interface UiChangeablePreference : UiPreference { 38 | val value: MutableStateFlow 39 | } 40 | 41 | interface UiSwitchPreference : UiChangeablePreference 42 | 43 | interface UiClickableSwitchPreference : UiSwitchPreference 44 | -------------------------------------------------------------------------------- /common/src/commonMain/kotlin/org/ferredoxin/ferredoxinui/common/base/ResourceProvider.kt: -------------------------------------------------------------------------------- 1 | package org.ferredoxin.ferredoxinui.common.base 2 | 3 | expect interface ResourceProvider 4 | 5 | expect class DirectResourceProvider constructor(value: T) : ResourceProvider 6 | 7 | expect class FunctionalResourceProvider : ResourceProvider -------------------------------------------------------------------------------- /common/src/commonMain/kotlin/org/ferredoxin/ferredoxinui/common/base/TitleAble.kt: -------------------------------------------------------------------------------- 1 | package org.ferredoxin.ferredoxinui.common.base 2 | 3 | interface TitleAble { 4 | var title: String 5 | var titleProvider: ResourceProvider 6 | } -------------------------------------------------------------------------------- /common/src/commonMain/kotlin/org/ferredoxin/ferredoxinui/common/base/UiDSLHelper.kt: -------------------------------------------------------------------------------- 1 | package org.ferredoxin.ferredoxinui.common.base 2 | 3 | typealias ViewMap = List> 4 | 5 | fun uiCategory(init: UiCategoryFactory.() -> Unit): Pair { 6 | val uiCategory = UiCategoryFactory() 7 | uiCategory.init() 8 | if (uiCategory.noTitle && uiCategory.name.isBlank()) { 9 | uiCategory.name = uiCategory.toString() 10 | } 11 | return Pair(uiCategory.name, uiCategory) 12 | } 13 | 14 | fun uiScreen(init: UiScreenFactory.() -> Unit): Pair { 15 | val uiScreenFactory = UiScreenFactory() 16 | uiScreenFactory.init() 17 | return Pair(uiScreenFactory.name, uiScreenFactory) 18 | } 19 | 20 | fun uiClickableItem(init: UiClickableItemFactory.() -> Unit): Pair { 21 | val uiClickableItemFactory = UiClickableItemFactory() 22 | uiClickableItemFactory.init() 23 | return Pair(uiClickableItemFactory.title, uiClickableItemFactory) 24 | } 25 | 26 | fun uiChangeableItem(init: UiChangeableItemFactory.() -> Unit): Pair> { 27 | val uiChangeableItemFactory = UiChangeableItemFactory() 28 | uiChangeableItemFactory.init() 29 | return Pair(uiChangeableItemFactory.title, uiChangeableItemFactory) 30 | } 31 | 32 | fun uiSwitchItem(init: UiSwitchItemFactory.() -> Unit): Pair { 33 | val uiSwitchItemFactory = UiSwitchItemFactory() 34 | uiSwitchItemFactory.init() 35 | return Pair(uiSwitchItemFactory.title, uiSwitchItemFactory) 36 | } 37 | 38 | fun uiClickableSwitchItem(init: UiClickableSwitchFactory.() -> Unit): Pair { 39 | val uiClickableSwitchFactory = UiClickableSwitchFactory() 40 | uiClickableSwitchFactory.init() 41 | return Pair(uiClickableSwitchFactory.title, uiClickableSwitchFactory) 42 | } 43 | 44 | fun uiAboutItem(init: UiAboutItemFactory.() -> Unit): Pair { 45 | val uiAboutItemFactory = UiAboutItemFactory() 46 | uiAboutItemFactory.init() 47 | return Pair(uiAboutItemFactory.title, uiAboutItemFactory) 48 | } -------------------------------------------------------------------------------- /common/src/commonMain/kotlin/org/ferredoxin/ferredoxinui/common/base/UiFactory.kt: -------------------------------------------------------------------------------- 1 | package org.ferredoxin.ferredoxinui.common.base 2 | 3 | import kotlinx.coroutines.flow.MutableStateFlow 4 | 5 | class UiCategoryFactory : UiCategory { 6 | override var name: String = "" 7 | private lateinit var nameProviderCache: ResourceProvider 8 | override var nameProvider: ResourceProvider 9 | get() { 10 | if (this::nameProviderCache.isInitialized) { 11 | return nameProviderCache 12 | } else { 13 | return DirectResourceProvider(name) 14 | } 15 | } 16 | set(value) { 17 | nameProviderCache = value 18 | } 19 | override var contains: UiMap = linkedMapOf() 20 | override var noTitle: Boolean = false 21 | } 22 | 23 | class UiScreenFactory : UiScreen { 24 | override var name: String = "" 25 | private lateinit var nameProviderCache: ResourceProvider 26 | override var nameProvider: ResourceProvider 27 | get() { 28 | if (this::nameProviderCache.isInitialized) { 29 | return nameProviderCache 30 | } else { 31 | return DirectResourceProvider(name) 32 | } 33 | } 34 | set(value) { 35 | nameProviderCache = value 36 | } 37 | var summary: String? = null 38 | private lateinit var summaryProviderCache: ResourceProvider 39 | override var summaryProvider: ResourceProvider 40 | get() { 41 | if (this::summaryProviderCache.isInitialized) { 42 | return summaryProviderCache 43 | } else { 44 | return DirectResourceProvider(name) 45 | } 46 | } 47 | set(value) { 48 | summaryProviderCache = value 49 | } 50 | override var contains: UiMap = linkedMapOf() 51 | override var onClickHook: (UiDescription) -> Unit = {} 52 | override var onValueChangeHook: (UiDescription) -> Unit = {} 53 | } 54 | 55 | expect open class UiClickableItemFactory() : UiPreference { 56 | override var title: String 57 | override var summary: String? 58 | override var valid: Boolean 59 | override var clickAble: Boolean 60 | override var subSummary: String? 61 | } 62 | 63 | expect open class UiChangeableItemFactory() : UiChangeablePreference, UiClickableItemFactory { 64 | override val value: MutableStateFlow 65 | } 66 | 67 | expect open class UiSwitchItemFactory() : UiSwitchPreference, UiChangeableItemFactory 68 | 69 | class UiClickableSwitchFactory : UiSwitchItemFactory(), UiClickableSwitchPreference 70 | 71 | expect class UiAboutItemFactory() : UiAboutItem -------------------------------------------------------------------------------- /common/src/commonMain/kotlin/org/ferredoxin/ferredoxinui/common/base/UiInterface.kt: -------------------------------------------------------------------------------- 1 | package org.ferredoxin.ferredoxinui.common.base 2 | 3 | typealias UiMap = MutableMap 4 | 5 | interface UiDescription 6 | 7 | sealed interface UiGroup : UiDescription { 8 | val name: String 9 | val nameProvider: ResourceProvider 10 | val contains: UiMap 11 | } 12 | 13 | interface UiScreen : UiGroup { 14 | val summaryProvider: ResourceProvider 15 | get() = DirectResourceProvider(null) 16 | val onClickHook: (UiDescription) -> Unit 17 | val onValueChangeHook: (UiDescription) -> Unit 18 | } 19 | 20 | interface UiCategory : UiGroup { 21 | val noTitle: Boolean 22 | get() = false 23 | } 24 | 25 | interface UiItem : UiDescription { 26 | val preference: UiDescription 27 | val preferenceLocate: Array? 28 | get() = null 29 | } 30 | 31 | interface UiSwitchItem : UiItem { 32 | override val preference: UiSwitchPreference 33 | } 34 | 35 | expect interface UiAboutItem : UiDescription, TitleAble 36 | -------------------------------------------------------------------------------- /common/src/commonMain/kotlin/org/ferredoxin/ferredoxinui/common/base/UiScreenLoader.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * QNotified - An Xposed module for QQ/TIM 3 | * Copyright (C) 2019-2021 dmca@ioctl.cc 4 | * https://github.com/ferredoxin/QNotified 5 | * 6 | * This software is non-free but opensource software: you can redistribute it 7 | * and/or modify it under the terms of the GNU Affero General Public License 8 | * as published by the Free Software Foundation; either 9 | * version 3 of the License, or any later version and our eula as published 10 | * by ferredoxin. 11 | * 12 | * This software is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | * Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * and eula along with this software. If not, see 19 | * 20 | * . 21 | */ 22 | 23 | package org.ferredoxin.ferredoxinui.common.base 24 | 25 | fun loadUiInList(map: UiMap, list: List) { 26 | for (uiItem in list) { 27 | addUiItemToUiGroup(uiItem.preference, map, uiItem.preferenceLocate, 0) 28 | } 29 | } 30 | 31 | private fun addUiItemToUiGroup(uiDescription: UiDescription, container: UiMap, array: Array?, point: Int) { 32 | if (array == null || array.isEmpty()) { 33 | if (uiDescription is UiPreference) { 34 | val contains = container[uiDescription.title] 35 | if (contains is UiGroup) { 36 | contains.contains[uiDescription.title] = uiDescription 37 | } 38 | } 39 | } else { 40 | if (container.containsKey(array[point])) { 41 | if (point == array.size - 1) { 42 | val ui = container[array[point]] 43 | if (ui != null && ui is UiGroup) { 44 | if (uiDescription is UiPreference) { 45 | ui.contains[uiDescription.title] = uiDescription 46 | } else if (uiDescription is UiGroup) { 47 | ui.contains[uiDescription.name] = uiDescription 48 | } 49 | } 50 | } else { 51 | val ui = container[array[point]] 52 | if (ui is UiGroup) { 53 | addUiItemToUiGroup(uiDescription, ui.contains, array, point + 1) 54 | } 55 | } 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /common/src/commonMain/kotlin/org/ferredoxin/ferredoxinui/common/platform.kt: -------------------------------------------------------------------------------- 1 | package org.ferredoxin.ferredoxinui.common 2 | 3 | expect fun getPlatformName(): String -------------------------------------------------------------------------------- /common/src/desktopMain/kotlin/org/ferredoxin/ferredoxinui/common/platform.kt: -------------------------------------------------------------------------------- 1 | package org.ferredoxin.ferredoxinui.common 2 | 3 | actual fun getPlatformName(): String { 4 | return "Desktop" 5 | } -------------------------------------------------------------------------------- /desktop/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.jetbrains.compose.compose 2 | import org.jetbrains.compose.desktop.application.dsl.TargetFormat 3 | 4 | plugins { 5 | kotlin("multiplatform") 6 | id("org.jetbrains.compose") 7 | } 8 | 9 | group = "org.ferredoxin.ferredoxinui" 10 | version = "1.0" 11 | 12 | val java_version = JavaVersion.VERSION_11 13 | 14 | kotlin { 15 | jvm { 16 | compilations.all { 17 | kotlinOptions.jvmTarget = java_version.majorVersion 18 | } 19 | } 20 | sourceSets { 21 | val jvmMain by getting { 22 | dependencies { 23 | implementation(project(":common")) 24 | implementation(compose.desktop.currentOs) 25 | } 26 | } 27 | } 28 | } 29 | 30 | compose.desktop { 31 | application { 32 | mainClass = "MainKt" 33 | nativeDistributions { 34 | targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.AppImage) 35 | packageName = "Ferredoxin UI Demo" 36 | packageVersion = "1.0.0" 37 | } 38 | } 39 | } 40 | 41 | java { 42 | sourceCompatibility = java_version 43 | targetCompatibility = java_version 44 | } -------------------------------------------------------------------------------- /desktop/src/jvmMain/kotlin/Main.kt: -------------------------------------------------------------------------------- 1 | import androidx.compose.ui.window.Window 2 | import androidx.compose.ui.window.application 3 | import org.ferredoxin.ferredoxinui.common.App 4 | 5 | fun main() = application { 6 | Window(onCloseRequest = ::exitApplication) { 7 | App() 8 | } 9 | } -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | kotlin.code.style=official 2 | android.useAndroidX=true 3 | kotlin.mpp.enableGranularSourceSetsMetadata=true 4 | kotlin.native.enableDependencyPropagation=false 5 | kotlin.mpp.stability.nowarn=true 6 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ferredoxin/FerredoxinUI/ecea5e161d79268ee0b7d872889627892ecc089f/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Sep 01 09:11:40 CST 2021 2 | distributionBase=GRADLE_USER_HOME 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-all.zip 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /qnotified_style/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /qnotified_style/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("com.android.library") 3 | id("org.jetbrains.kotlin.android") 4 | } 5 | 6 | val java_version = JavaVersion.VERSION_11 7 | 8 | android { 9 | compileSdk = 31 10 | 11 | defaultConfig { 12 | minSdk = 21 13 | targetSdk = 31 14 | consumerProguardFiles("consumer-rules.pro") 15 | } 16 | 17 | buildTypes { 18 | release { 19 | isMinifyEnabled = false 20 | proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") 21 | } 22 | } 23 | compileOptions { 24 | sourceCompatibility = java_version 25 | targetCompatibility = java_version 26 | } 27 | kotlinOptions { 28 | jvmTarget = java_version.majorVersion 29 | } 30 | buildFeatures { 31 | viewBinding = true 32 | } 33 | } 34 | 35 | dependencies { 36 | implementation("androidx.core:core-ktx:1.7.0") 37 | implementation("androidx.appcompat:appcompat:1.4.0") 38 | implementation("com.google.android.material:material:1.6.0-alpha01") 39 | implementation(project(":common")) 40 | } -------------------------------------------------------------------------------- /qnotified_style/consumer-rules.pro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ferredoxin/FerredoxinUI/ecea5e161d79268ee0b7d872889627892ecc089f/qnotified_style/consumer-rules.pro -------------------------------------------------------------------------------- /qnotified_style/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /qnotified_style/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /qnotified_style/src/main/java/org/ferredoxin/ferredoxinui/qnotified_style/activity/MaiTungTMStyleActivity.kt: -------------------------------------------------------------------------------- 1 | package org.ferredoxin.ferredoxinui.qnotified_style.activity 2 | 3 | import android.animation.ObjectAnimator 4 | import android.graphics.Typeface 5 | import android.os.Bundle 6 | import android.util.TypedValue 7 | import android.view.animation.AnimationUtils 8 | import android.widget.TextView 9 | import androidx.appcompat.app.AppCompatActivity 10 | import androidx.core.content.ContextCompat 11 | import androidx.fragment.app.Fragment 12 | import org.ferredoxin.ferredoxinui.common.base.TitleAble 13 | import org.ferredoxin.ferredoxinui.qnotified_style.R 14 | import org.ferredoxin.ferredoxinui.qnotified_style.databinding.ActivityMaitungTmStyleBinding 15 | 16 | abstract class MaiTungTMStyleActivity : AppCompatActivity() where T : Fragment, T : TitleAble { 17 | 18 | private val translation: Float by lazy { 19 | TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 40F, resources.displayMetrics) 20 | } 21 | 22 | open var showNavigationIcon: Boolean = true 23 | set(value) { 24 | if (value == field) return 25 | if (value) { 26 | ObjectAnimator.ofFloat(binding.constraintLayout3, "translationX", 0F).apply { 27 | duration = 400 28 | start() 29 | } 30 | } else { 31 | ObjectAnimator.ofFloat(binding.constraintLayout3, "translationX", -translation) 32 | .apply { 33 | duration = 400 34 | start() 35 | } 36 | } 37 | field = value 38 | } 39 | 40 | abstract val fragment: T 41 | 42 | private lateinit var binding: ActivityMaitungTmStyleBinding 43 | 44 | override fun onCreate(savedInstanceState: Bundle?) { 45 | super.onCreate(savedInstanceState) 46 | binding = ActivityMaitungTmStyleBinding.inflate(layoutInflater) 47 | binding.imageView3.setOnClickListener { 48 | onBackPressed() 49 | } 50 | binding.title.setFactory { 51 | val textView = TextView(this) 52 | textView.setTextColor(ContextCompat.getColor(this, R.color.FirstTextColor)) 53 | textView.textSize = 20F 54 | textView.typeface = Typeface.defaultFromStyle(Typeface.BOLD) 55 | textView 56 | } 57 | setContentView(binding.root) 58 | addFragment(fragment) 59 | showNavigationIcon = false 60 | } 61 | 62 | var title: String? = null 63 | set(value) { 64 | binding.title.setText(value) 65 | field = value 66 | } 67 | 68 | fun addFragment(fragment: T) where T : Fragment, T : TitleAble { 69 | supportFragmentManager.beginTransaction() 70 | .setCustomAnimations( 71 | R.anim.slide_left_in, 72 | R.anim.slide_right_out, 73 | R.anim.slide_right_in, 74 | R.anim.slide_left_out 75 | ) 76 | .replace(R.id.container, fragment) 77 | .addToBackStack(fragment.title).commit() 78 | showNavigationIcon = true 79 | binding.title.inAnimation = 80 | AnimationUtils.loadAnimation(this, R.anim.slide_left_in_no_alpha) 81 | binding.title.outAnimation = 82 | AnimationUtils.loadAnimation(this, R.anim.slide_right_out_no_alpha) 83 | this.title = fragment.title 84 | } 85 | 86 | override fun onBackPressed() { 87 | if (supportFragmentManager.backStackEntryCount <= 1) { 88 | finish() 89 | } else { 90 | showNavigationIcon = supportFragmentManager.backStackEntryCount != 2 91 | this.title = supportFragmentManager.getBackStackEntryAt(0).name 92 | supportFragmentManager.popBackStack() 93 | binding.title.inAnimation = 94 | AnimationUtils.loadAnimation(this, R.anim.slide_right_in_no_alpha) 95 | binding.title.outAnimation = 96 | AnimationUtils.loadAnimation(this, R.anim.slide_left_out_no_alpha) 97 | } 98 | } 99 | 100 | override fun recreate() { 101 | finish() 102 | startActivity(intent) 103 | } 104 | 105 | } -------------------------------------------------------------------------------- /qnotified_style/src/main/java/org/ferredoxin/ferredoxinui/qnotified_style/base/QNotifiedStyleUiDSLHelper.kt: -------------------------------------------------------------------------------- 1 | package org.ferredoxin.ferredoxinui.qnotified_style.base 2 | 3 | import android.content.DialogInterface 4 | import android.view.ViewGroup 5 | import android.widget.LinearLayout 6 | import com.google.android.material.dialog.MaterialAlertDialogBuilder 7 | import com.google.android.material.textfield.TextInputEditText 8 | import com.google.android.material.textfield.TextInputLayout 9 | import org.ferredoxin.ferredoxinui.common.base.EditPreferenceFactory 10 | import org.ferredoxin.ferredoxinui.common.base.MaterialAlertDialogPreferenceFactory 11 | import org.ferredoxin.ferredoxinui.common.base.UiChangeablePreference 12 | import org.ferredoxin.ferredoxinui.qnotified_style.R 13 | 14 | fun uiDialogPreference(init: MaterialAlertDialogPreferenceFactory.() -> Unit): Pair { 15 | val builder = MaterialAlertDialogPreferenceFactory() 16 | builder.init() 17 | builder.onClickListener = { 18 | val context = builder.contextWrapper(it) 19 | val builder2 = MaterialAlertDialogBuilder(context, R.style.MaterialDialog) 20 | builder2.setTitle(builder.title) 21 | builder.materialAlertDialogBuilder.invoke(builder2) 22 | builder2.create().show() 23 | true 24 | } 25 | return builder.title to builder 26 | } 27 | 28 | fun uiEditTextPreference(init: EditPreferenceFactory.() -> Unit): Pair> { 29 | val builder = EditPreferenceFactory() 30 | builder.init() 31 | builder.onClickListener = { 32 | val context = builder.contextWrapper(it) 33 | val builder2 = MaterialAlertDialogBuilder(context, R.style.MaterialDialog) 34 | val inputLayout = TextInputLayout(context, null, builder.theme) 35 | inputLayout.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) 36 | builder.inputLayout.invoke(inputLayout) 37 | val textInputEditText = TextInputEditText(context, null) 38 | inputLayout.addView(textInputEditText, LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)) 39 | builder.textInputEditText.invoke(textInputEditText) 40 | builder2.setTitle(builder.title) 41 | builder2.setView(inputLayout) 42 | builder2.setNegativeButton("取消", null) 43 | builder2.setPositiveButton("确定") { dialog: DialogInterface, _: Int -> 44 | builder.value.value = textInputEditText.text.toString() 45 | dialog.dismiss() 46 | } 47 | builder2.create().show() 48 | true 49 | } 50 | return builder.title to builder 51 | } 52 | -------------------------------------------------------------------------------- /qnotified_style/src/main/java/org/ferredoxin/ferredoxinui/qnotified_style/fragment/MaiTungTMSettingFragment.kt: -------------------------------------------------------------------------------- 1 | package org.ferredoxin.ferredoxinui.qnotified_style.fragment 2 | 3 | import android.os.Bundle 4 | import android.view.LayoutInflater 5 | import android.view.View 6 | import android.view.ViewGroup 7 | import androidx.fragment.app.Fragment 8 | import androidx.fragment.app.FragmentActivity 9 | import androidx.viewbinding.ViewBinding 10 | import org.ferredoxin.ferredoxinui.common.base.* 11 | import org.ferredoxin.ferredoxinui.qnotified_style.activity.MaiTungTMStyleActivity 12 | import org.ferredoxin.ferredoxinui.qnotified_style.databinding.FragmentEmptyBinding 13 | import org.ferredoxin.ferredoxinui.qnotified_style.databinding.FragmentSettingsBinding 14 | import org.ferredoxin.ferredoxinui.qnotified_style.item.* 15 | import java.lang.ref.WeakReference 16 | import kotlin.concurrent.thread 17 | 18 | class MaiTungTMSettingFragment : Fragment(), TitleAble { 19 | 20 | private lateinit var uiScreen: UiScreen 21 | override lateinit var title: String 22 | private lateinit var titleProviderCache: ResourceProvider 23 | override var titleProvider: ResourceProvider 24 | get() { 25 | if (this::titleProviderCache.isInitialized) { 26 | return titleProviderCache 27 | } else { 28 | return DirectResourceProvider(title) 29 | } 30 | } 31 | set(value) { 32 | titleProviderCache = value 33 | } 34 | private lateinit var binding: ViewBinding 35 | private var viewCache: WeakReference? = null 36 | 37 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { 38 | 39 | val view = viewCache?.get() 40 | if (view != null) { 41 | val parent = view.parent as ViewGroup? 42 | parent?.removeView(view) 43 | return view 44 | } 45 | 46 | if (!::uiScreen.isInitialized) { 47 | requireActivity().recreate() 48 | return null 49 | } 50 | 51 | if (uiScreen.contains.isEmpty()) { 52 | binding = FragmentEmptyBinding.inflate(layoutInflater, container, false) 53 | } else { 54 | binding = FragmentSettingsBinding.inflate(layoutInflater, container, false) 55 | thread { 56 | requireActivity().runOnUiThread { 57 | addViewInUiGroup(uiScreen, (binding as FragmentSettingsBinding).linearContainer) 58 | } 59 | } 60 | } 61 | 62 | viewCache = WeakReference(binding.root) 63 | 64 | return binding.root 65 | } 66 | 67 | fun setUiScreen(uiScreen: UiScreen): MaiTungTMSettingFragment { 68 | this.uiScreen = uiScreen 69 | this.title = uiScreen.name 70 | return this 71 | } 72 | 73 | private fun addViewInUiGroup(uiGroup: UiGroup, viewGroup: ViewGroup) { 74 | //Log.d(this::class.java.simpleName, "Adding: " + uiGroup.name + " = " + uiGroup.contains.toString()) 75 | for (uiDescription in uiGroup.contains.values) { 76 | //Log.d(this::class.java.simpleName, "Adding: $uiDescription") 77 | when { 78 | uiDescription is UiAboutItem -> { 79 | val about = About(requireContext()) 80 | viewGroup.addView(about) 81 | about.setTitle(uiDescription.titleProvider.getValue(requireContext())) 82 | about.setIcon(uiDescription.icon.invoke(requireContext())) 83 | } 84 | uiDescription is UiCategory -> { 85 | if (!uiDescription.noTitle) { 86 | viewGroup.addView(Subtitle(requireContext()).apply { 87 | setTitle(uiDescription.nameProvider.getValue(requireContext())) 88 | }) 89 | } 90 | if (uiDescription.contains.isNotEmpty()) { 91 | val category = Category(requireContext()) 92 | viewGroup.addView(category) 93 | addViewInUiGroup(uiDescription, category.linearLayout) 94 | } 95 | } 96 | uiDescription is UiPreference -> { 97 | when (uiDescription) { 98 | is UiClickableSwitchPreference -> { 99 | viewGroup.addView(ClickableSwitchItem(requireContext()).apply { 100 | title = uiDescription.titleProvider.getValue(requireContext()) 101 | summary = uiDescription.summaryProvider.getValue(requireContext()) 102 | onValueChangedListener = { 103 | uiDescription.value.value = it 104 | } 105 | enable = uiDescription.valid 106 | observeStateFlow(uiDescription.value) { 107 | checked = it ?: checked 108 | } 109 | setOnClickListener(getOnClickListener(uiDescription.onClickListener, uiDescription.title)) 110 | }) 111 | } 112 | is UiSwitchPreference -> { 113 | viewGroup.addView(SwitchItem(requireContext()).apply { 114 | title = uiDescription.titleProvider.getValue(requireContext()) 115 | summary = uiDescription.summaryProvider.getValue(requireContext()) 116 | onValueChangedListener = { 117 | uiDescription.value.value = it 118 | } 119 | enable = uiDescription.valid 120 | observeStateFlow(uiDescription.value) { 121 | checked = it ?: checked 122 | } 123 | }) 124 | } 125 | is UiChangeablePreference<*> -> { 126 | viewGroup.addView(ClickableItem(requireContext()).apply { 127 | title = uiDescription.titleProvider.getValue(requireContext()) 128 | summary = uiDescription.summaryProvider.getValue(requireContext()) 129 | enable = uiDescription.valid 130 | setOnClickListener(getOnClickListener(uiDescription.onClickListener, uiDescription.title)) 131 | observeStateFlow(uiDescription.value) { 132 | value = it?.toString() 133 | } 134 | }) 135 | } 136 | is UiPreference -> { 137 | viewGroup.addView(ClickableItem(requireContext()).apply { 138 | title = uiDescription.titleProvider.getValue(requireContext()) 139 | summary = uiDescription.summaryProvider.getValue(requireContext()) 140 | clickAble = uiDescription.clickAble 141 | subSummary = uiDescription.subSummary 142 | setOnClickListener(getOnClickListener(uiDescription.onClickListener, uiDescription.title)) 143 | enable = uiDescription.valid 144 | }) 145 | } 146 | } 147 | } 148 | } 149 | } 150 | } 151 | 152 | private fun getOnClickListener( 153 | listener: (FragmentActivity) -> Boolean, 154 | title: String 155 | ): View.OnClickListener { 156 | return when (listener) { 157 | is ClickToNewSetting -> { 158 | View.OnClickListener { 159 | (requireActivity() as MaiTungTMStyleActivity<*>).addFragment( 160 | MaiTungTMSettingFragment().setUiScreen( 161 | listener.uiScreen 162 | ) 163 | ) 164 | } 165 | } 166 | is ClickToNewPages -> { 167 | View.OnClickListener { 168 | (requireActivity() as MaiTungTMStyleActivity<*>).addFragment( 169 | ViewPagerFragment().setViewMap( 170 | listener.viewMap 171 | ).setTitle(title) 172 | ) 173 | } 174 | 175 | } 176 | else -> { 177 | View.OnClickListener { 178 | listener.invoke(requireActivity()) 179 | } 180 | } 181 | } 182 | } 183 | 184 | } -------------------------------------------------------------------------------- /qnotified_style/src/main/java/org/ferredoxin/ferredoxinui/qnotified_style/fragment/ViewPagerFragment.kt: -------------------------------------------------------------------------------- 1 | package org.ferredoxin.ferredoxinui.qnotified_style.fragment 2 | 3 | import android.graphics.Typeface 4 | import android.os.Bundle 5 | import android.view.Gravity 6 | import android.view.LayoutInflater 7 | import android.view.View 8 | import android.view.ViewGroup 9 | import android.widget.LinearLayout 10 | import android.widget.TextView 11 | import androidx.core.content.ContextCompat 12 | import androidx.fragment.app.Fragment 13 | import androidx.viewpager2.adapter.FragmentStateAdapter 14 | import com.google.android.material.tabs.TabLayout 15 | import com.google.android.material.tabs.TabLayoutMediator 16 | import org.ferredoxin.ferredoxinui.common.base.DirectResourceProvider 17 | import org.ferredoxin.ferredoxinui.common.base.ResourceProvider 18 | import org.ferredoxin.ferredoxinui.common.base.TitleAble 19 | import org.ferredoxin.ferredoxinui.common.base.ViewMap 20 | import org.ferredoxin.ferredoxinui.qnotified_style.R 21 | import org.ferredoxin.ferredoxinui.qnotified_style.databinding.FragmentViewPagerBinding 22 | 23 | class ViewPagerFragment : Fragment(), TitleAble { 24 | 25 | private lateinit var binding: FragmentViewPagerBinding 26 | private lateinit var viewMap: ViewMap 27 | override lateinit var title: String 28 | 29 | private lateinit var titleProviderCache: ResourceProvider 30 | override var titleProvider: ResourceProvider 31 | get() { 32 | if (this::titleProviderCache.isInitialized) { 33 | return titleProviderCache 34 | } else { 35 | return DirectResourceProvider(title) 36 | } 37 | } 38 | set(value) { 39 | titleProviderCache = value 40 | } 41 | 42 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { 43 | if (!::viewMap.isInitialized) { 44 | requireActivity().recreate() 45 | return null 46 | } 47 | binding = FragmentViewPagerBinding.inflate(layoutInflater, container, false) 48 | val adapter = Adapter() 49 | binding.viewPager2.adapter = adapter 50 | TabLayoutMediator(binding.tabLayout, binding.viewPager2) { tab, position -> 51 | tab.text = viewMap[position].first 52 | }.attach() 53 | binding.tabLayout.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener { 54 | override fun onTabSelected(tab: TabLayout.Tab?) { 55 | val textView = getTextViewForTab() 56 | textView.text = tab?.text 57 | tab?.customView = textView 58 | } 59 | 60 | override fun onTabUnselected(tab: TabLayout.Tab?) { 61 | tab?.customView = null 62 | } 63 | 64 | override fun onTabReselected(tab: TabLayout.Tab?) {} 65 | 66 | }) 67 | val firstTab = binding.tabLayout.getTabAt(0) 68 | val textView = getTextViewForTab() 69 | textView.text = firstTab?.text 70 | firstTab?.customView = textView 71 | return binding.root 72 | } 73 | 74 | private fun getTextViewForTab(): TextView { 75 | val textView = TextView(requireContext()) 76 | textView.textSize = 18F 77 | textView.typeface = Typeface.defaultFromStyle(Typeface.BOLD) 78 | textView.setTextColor(ContextCompat.getColor(requireContext(), R.color.ThemeColor)) 79 | val params: LinearLayout.LayoutParams = LinearLayout.LayoutParams( 80 | ViewGroup.LayoutParams.WRAP_CONTENT, 81 | ViewGroup.LayoutParams.WRAP_CONTENT 82 | ) 83 | params.gravity = Gravity.CENTER 84 | textView.layoutParams = params 85 | return textView 86 | } 87 | 88 | fun setTitle(title: String): ViewPagerFragment { 89 | this.title = title 90 | return this 91 | } 92 | 93 | fun setViewMap(viewMap: ViewMap): ViewPagerFragment { 94 | this.viewMap = viewMap 95 | return this 96 | } 97 | 98 | inner class Adapter : FragmentStateAdapter(this@ViewPagerFragment) { 99 | override fun getItemCount(): Int = viewMap.count() 100 | 101 | override fun createFragment(position: Int): Fragment { 102 | return MaiTungTMSettingFragment().setUiScreen(viewMap[position].second) 103 | } 104 | 105 | } 106 | 107 | } -------------------------------------------------------------------------------- /qnotified_style/src/main/java/org/ferredoxin/ferredoxinui/qnotified_style/item/About.kt: -------------------------------------------------------------------------------- 1 | package org.ferredoxin.ferredoxinui.qnotified_style.item 2 | 3 | import android.content.Context 4 | import android.graphics.drawable.Drawable 5 | import android.util.AttributeSet 6 | import android.view.LayoutInflater 7 | import android.widget.FrameLayout 8 | import org.ferredoxin.ferredoxinui.qnotified_style.databinding.ItemAboutBinding 9 | 10 | class About @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : 11 | FrameLayout(context, attrs, defStyleAttr) { 12 | 13 | private val binding: ItemAboutBinding = ItemAboutBinding.inflate(LayoutInflater.from(context), this, true) 14 | 15 | fun setTitle(title: String) { 16 | binding.textView4.text = title 17 | } 18 | 19 | fun setIcon(drawable: Drawable) { 20 | binding.imageView5.setImageDrawable(drawable) 21 | } 22 | 23 | } -------------------------------------------------------------------------------- /qnotified_style/src/main/java/org/ferredoxin/ferredoxinui/qnotified_style/item/Category.kt: -------------------------------------------------------------------------------- 1 | package org.ferredoxin.ferredoxinui.qnotified_style.item 2 | 3 | import android.content.Context 4 | import android.util.AttributeSet 5 | import android.view.LayoutInflater 6 | import android.view.View 7 | import android.widget.FrameLayout 8 | import org.ferredoxin.ferredoxinui.qnotified_style.databinding.ItemCategoryBinding 9 | 10 | class Category @JvmOverloads constructor( 11 | context: Context, 12 | attrs: AttributeSet? = null, 13 | defStyleAttr: Int = 0 14 | ) : FrameLayout(context, attrs, defStyleAttr) { 15 | 16 | private val binding: ItemCategoryBinding = 17 | ItemCategoryBinding.inflate(LayoutInflater.from(context), this, true) 18 | 19 | fun addItem(view: View) { 20 | binding.linearLayout.addView(view) 21 | } 22 | 23 | val linearLayout = binding.linearLayout 24 | 25 | } -------------------------------------------------------------------------------- /qnotified_style/src/main/java/org/ferredoxin/ferredoxinui/qnotified_style/item/ClickableItem.kt: -------------------------------------------------------------------------------- 1 | package org.ferredoxin.ferredoxinui.qnotified_style.item 2 | 3 | import android.content.Context 4 | import android.content.res.ColorStateList 5 | import android.util.AttributeSet 6 | import android.view.LayoutInflater 7 | import android.view.View 8 | import android.widget.FrameLayout 9 | import androidx.core.content.ContextCompat 10 | import androidx.core.widget.ImageViewCompat 11 | import org.ferredoxin.ferredoxinui.qnotified_style.R 12 | import org.ferredoxin.ferredoxinui.qnotified_style.databinding.ItemClickableBinding 13 | 14 | class ClickableItem @JvmOverloads constructor( 15 | context: Context, 16 | attrs: AttributeSet? = null, 17 | defStyleAttr: Int = 0 18 | ) : FrameLayout(context, attrs, defStyleAttr) { 19 | 20 | private val binding: ItemClickableBinding = 21 | ItemClickableBinding.inflate(LayoutInflater.from(context), this, true) 22 | 23 | init { 24 | isClickable = true 25 | } 26 | 27 | var title: String 28 | get() = binding.textView2.text.toString() 29 | set(value) { 30 | binding.textView2.text = value 31 | } 32 | 33 | var summary: String? 34 | get() = binding.summary.text.toString() 35 | set(value) { 36 | binding.summary.text = value 37 | if (value.isNullOrBlank()) { 38 | binding.summary.visibility = View.GONE 39 | } else { 40 | binding.summary.visibility = View.VISIBLE 41 | } 42 | } 43 | 44 | var clickAble: Boolean = true 45 | set(value) { 46 | if (value) { 47 | binding.imageView.visibility = View.VISIBLE 48 | } else { 49 | binding.imageView.visibility = View.GONE 50 | setOnClickListener(null) 51 | } 52 | field = value 53 | } 54 | 55 | var subSummary: String? = null 56 | get() = binding.value.text.toString() 57 | set(value) { 58 | binding.value.text = value 59 | field = value 60 | } 61 | 62 | var value: String? 63 | get() = binding.value.text.toString() 64 | set(value) { 65 | binding.value.text = value 66 | } 67 | 68 | var enable: Boolean = true 69 | set(value) { 70 | if (value) { 71 | isClickable = true 72 | binding.textView2.setTextColor( 73 | ContextCompat.getColor( 74 | context, 75 | R.color.SecondTextColor 76 | ) 77 | ) 78 | binding.summary.setTextColor( 79 | ContextCompat.getColor( 80 | context, 81 | R.color.SecondTextColor 82 | ) 83 | ) 84 | ImageViewCompat.setImageTintList( 85 | binding.imageView, 86 | ColorStateList.valueOf( 87 | ContextCompat.getColor( 88 | context, 89 | R.color.ItemRightIconColor 90 | ) 91 | ) 92 | ) 93 | } else { 94 | isClickable = false 95 | binding.textView2.setTextColor(ContextCompat.getColor(context,R.color.DisableTextColor)) 96 | binding.summary.setTextColor( 97 | ContextCompat.getColor( 98 | context, 99 | R.color.DisableTextColor 100 | ) 101 | ) 102 | ImageViewCompat.setImageTintList( 103 | binding.imageView, 104 | ColorStateList.valueOf( 105 | ContextCompat.getColor( 106 | context, 107 | R.color.DisableTextColor 108 | ) 109 | ) 110 | ) 111 | } 112 | field = value 113 | } 114 | 115 | } -------------------------------------------------------------------------------- /qnotified_style/src/main/java/org/ferredoxin/ferredoxinui/qnotified_style/item/ClickableSwitchItem.kt: -------------------------------------------------------------------------------- 1 | package org.ferredoxin.ferredoxinui.qnotified_style.item 2 | 3 | import android.content.Context 4 | import android.util.AttributeSet 5 | import android.view.LayoutInflater 6 | import android.view.View 7 | import android.widget.FrameLayout 8 | import androidx.core.content.ContextCompat 9 | import org.ferredoxin.ferredoxinui.qnotified_style.R 10 | import org.ferredoxin.ferredoxinui.qnotified_style.databinding.ItemClickableSwitchBinding 11 | 12 | class ClickableSwitchItem @JvmOverloads constructor( 13 | context: Context, 14 | attrs: AttributeSet? = null, 15 | defStyleAttr: Int = 0 16 | ) : FrameLayout(context, attrs, defStyleAttr) { 17 | 18 | private val binding: ItemClickableSwitchBinding = ItemClickableSwitchBinding.inflate( 19 | LayoutInflater.from(context), this, true 20 | ) 21 | 22 | var checked: Boolean 23 | get() { 24 | return binding.switch1.checked 25 | } 26 | set(value) { 27 | binding.switch1.checked = value 28 | } 29 | 30 | var title: String 31 | get() = binding.textView2.text.toString() 32 | set(value) { 33 | binding.textView2.text = value 34 | } 35 | 36 | var summary: String? 37 | get() = binding.summary.text.toString() 38 | set(value) { 39 | binding.summary.text = value 40 | if (value.isNullOrBlank()) { 41 | binding.summary.visibility = View.GONE 42 | } else { 43 | binding.summary.visibility = View.VISIBLE 44 | } 45 | } 46 | 47 | var enable: Boolean = binding.switch1.isEnabled 48 | set(value) { 49 | binding.constraintLayout.isClickable = value 50 | binding.constraintLayout.isFocusable = value 51 | binding.constraintLayout.isEnabled = value 52 | if (value) { 53 | binding.textView2.setTextColor( 54 | ContextCompat.getColor( 55 | context, 56 | R.color.SecondTextColor 57 | ) 58 | ) 59 | binding.summary.setTextColor( 60 | ContextCompat.getColor( 61 | context, 62 | R.color.SecondTextColor 63 | ) 64 | ) 65 | } else { 66 | binding.textView2.setTextColor( 67 | ContextCompat.getColor( 68 | context, 69 | R.color.DisableTextColor 70 | ) 71 | ) 72 | binding.summary.setTextColor( 73 | ContextCompat.getColor( 74 | context, 75 | R.color.DisableTextColor 76 | ) 77 | ) 78 | } 79 | binding.switch1.isEnabled = value 80 | field = value 81 | } 82 | 83 | override fun setOnClickListener(l: OnClickListener?) { 84 | binding.constraintLayout.setOnClickListener(l) 85 | } 86 | 87 | var onValueChangedListener: ((Boolean) -> Unit)? 88 | get() { 89 | return binding.switch1.onValueChangedListener 90 | } 91 | set(value) { 92 | binding.switch1.onValueChangedListener = value 93 | } 94 | 95 | } -------------------------------------------------------------------------------- /qnotified_style/src/main/java/org/ferredoxin/ferredoxinui/qnotified_style/item/Subtitle.kt: -------------------------------------------------------------------------------- 1 | package org.ferredoxin.ferredoxinui.qnotified_style.item 2 | 3 | import android.content.Context 4 | import android.util.AttributeSet 5 | import android.view.LayoutInflater 6 | import android.widget.FrameLayout 7 | import org.ferredoxin.ferredoxinui.qnotified_style.databinding.ItemSubtitleBinding 8 | 9 | class Subtitle @JvmOverloads constructor( 10 | context: Context, 11 | attrs: AttributeSet? = null, 12 | defStyleAttr: Int = 0 13 | ) : FrameLayout(context, attrs, defStyleAttr) { 14 | 15 | private val binding: ItemSubtitleBinding = 16 | ItemSubtitleBinding.inflate(LayoutInflater.from(context), this, true) 17 | 18 | fun setTitle(string: String) { 19 | binding.textView.text = string 20 | } 21 | 22 | } -------------------------------------------------------------------------------- /qnotified_style/src/main/java/org/ferredoxin/ferredoxinui/qnotified_style/item/Switch.kt: -------------------------------------------------------------------------------- 1 | package org.ferredoxin.ferredoxinui.qnotified_style.item 2 | 3 | import android.content.Context 4 | import android.content.res.TypedArray 5 | import android.graphics.drawable.AnimatedVectorDrawable 6 | import android.graphics.drawable.Drawable 7 | import android.util.AttributeSet 8 | import android.view.ViewGroup 9 | import android.widget.LinearLayout 10 | import androidx.appcompat.widget.AppCompatImageView 11 | import androidx.vectordrawable.graphics.drawable.Animatable2Compat 12 | import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat 13 | import org.ferredoxin.ferredoxinui.qnotified_style.R 14 | 15 | class Switch @JvmOverloads constructor( 16 | context: Context, 17 | attrs: AttributeSet? = null, 18 | defStyle: Int = 0 19 | ) : AppCompatImageView(context, attrs, defStyle) { 20 | 21 | var checked: Boolean = false 22 | set(value) { 23 | if (field == value) return 24 | field = value 25 | update(true) 26 | onValueChangedListener?.invoke(value) 27 | } 28 | 29 | init { 30 | val typedArray: TypedArray = context.obtainStyledAttributes(attrs, R.styleable.Switch) 31 | val on = typedArray.getBoolean(R.styleable.Switch_checked, false) 32 | this.checked = on 33 | val enable = typedArray.getBoolean(R.styleable.Switch_enabled, true) 34 | this.isEnabled = enable 35 | typedArray.recycle() 36 | val params: ViewGroup.LayoutParams = LinearLayout.LayoutParams( 37 | ViewGroup.LayoutParams.WRAP_CONTENT, 38 | ViewGroup.LayoutParams.WRAP_CONTENT 39 | ) 40 | this.layoutParams = params 41 | setOnClickListener { 42 | checked = !checked 43 | } 44 | } 45 | 46 | override fun setEnabled(enabled: Boolean) { 47 | super.setEnabled(enabled) 48 | update(false) 49 | } 50 | 51 | @Synchronized 52 | private fun update(animate: Boolean) { 53 | if (!isEnabled) { 54 | if (checked) { 55 | setImageResource(R.drawable.ic_toggle_disable_on) 56 | } else { 57 | setImageResource(R.drawable.ic_toggle_disable_24px) 58 | } 59 | } else { 60 | if (animate) { 61 | showAnimate(checked) 62 | } else { 63 | if (checked) { 64 | setImageResource(R.drawable.ic_toggle_on_24px) 65 | } else { 66 | setImageResource(R.drawable.ic_toggle_off_24px) 67 | } 68 | } 69 | } 70 | } 71 | 72 | private fun showAnimate(on: Boolean) { 73 | isClickable = false 74 | val animateRes = if (on) R.drawable.switch_off_to_on else R.drawable.switch_on_to_off 75 | setImageResource(animateRes) 76 | AnimatedVectorDrawableCompat.clearAnimationCallbacks(drawable) 77 | AnimatedVectorDrawableCompat.registerAnimationCallback( 78 | drawable, 79 | object : Animatable2Compat.AnimationCallback() { 80 | override fun onAnimationEnd(drawable: Drawable?) { 81 | AnimatedVectorDrawableCompat.clearAnimationCallbacks(drawable) 82 | val drawableRes = 83 | if (on) R.drawable.ic_toggle_on_24px else R.drawable.ic_toggle_off_24px 84 | setImageResource(drawableRes) 85 | isClickable = true 86 | } 87 | }) 88 | (drawable as? AnimatedVectorDrawable)?.start() 89 | } 90 | 91 | var onValueChangedListener: ((Boolean) -> Unit)? = null 92 | 93 | } -------------------------------------------------------------------------------- /qnotified_style/src/main/java/org/ferredoxin/ferredoxinui/qnotified_style/item/SwitchItem.kt: -------------------------------------------------------------------------------- 1 | package org.ferredoxin.ferredoxinui.qnotified_style.item 2 | 3 | import android.content.Context 4 | import android.util.AttributeSet 5 | import android.view.LayoutInflater 6 | import android.view.View 7 | import android.widget.FrameLayout 8 | import androidx.core.content.ContextCompat 9 | import org.ferredoxin.ferredoxinui.qnotified_style.R 10 | import org.ferredoxin.ferredoxinui.qnotified_style.databinding.ItemSwitchBinding 11 | 12 | class SwitchItem @JvmOverloads constructor( 13 | context: Context, 14 | attrs: AttributeSet? = null, 15 | defStyleAttr: Int = 0 16 | ) : FrameLayout(context, attrs, defStyleAttr) { 17 | 18 | private val binding: ItemSwitchBinding = 19 | ItemSwitchBinding.inflate(LayoutInflater.from(context), this, true) 20 | 21 | var checked: Boolean 22 | get() { 23 | return binding.switch1.checked 24 | } 25 | set(value) { 26 | binding.switch1.checked = value 27 | } 28 | 29 | init { 30 | isClickable = true 31 | setOnClickListener { 32 | checked = !checked 33 | } 34 | } 35 | 36 | var title: String 37 | get() = binding.textView2.text.toString() 38 | set(value) { 39 | binding.textView2.text = value 40 | } 41 | 42 | var summary: String? 43 | get() = binding.summary.text.toString() 44 | set(value) { 45 | binding.summary.text = value 46 | if (value.isNullOrBlank()) { 47 | binding.summary.visibility = View.GONE 48 | } else { 49 | binding.summary.visibility = View.VISIBLE 50 | } 51 | } 52 | 53 | var enable: Boolean = binding.switch1.isEnabled 54 | set(value) { 55 | if (value) { 56 | isClickable = true 57 | binding.textView2.setTextColor( 58 | ContextCompat.getColor( 59 | context, 60 | R.color.SecondTextColor 61 | ) 62 | ) 63 | binding.summary.setTextColor( 64 | ContextCompat.getColor( 65 | context, 66 | R.color.SecondTextColor 67 | ) 68 | ) 69 | } else { 70 | isClickable = false 71 | binding.textView2.setTextColor( 72 | ContextCompat.getColor( 73 | context, 74 | R.color.DisableTextColor 75 | ) 76 | ) 77 | binding.summary.setTextColor( 78 | ContextCompat.getColor( 79 | context, 80 | R.color.DisableTextColor 81 | ) 82 | ) 83 | } 84 | binding.switch1.isEnabled = value 85 | field = value 86 | } 87 | 88 | var onValueChangedListener: ((Boolean) -> Unit)? 89 | get() { 90 | return binding.switch1.onValueChangedListener 91 | } 92 | set(value) { 93 | binding.switch1.onValueChangedListener = value 94 | } 95 | 96 | } -------------------------------------------------------------------------------- /qnotified_style/src/main/res/anim/slide_left_in_no_alpha.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /qnotified_style/src/main/res/anim/slide_left_out_no_alpha.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /qnotified_style/src/main/res/anim/slide_right_in_no_alpha.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /qnotified_style/src/main/res/anim/slide_right_out_no_alpha.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /qnotified_style/src/main/res/drawable/ic_arrow_back_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /qnotified_style/src/main/res/drawable/ic_chevron_right_24px.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /qnotified_style/src/main/res/drawable/ic_search_24px.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /qnotified_style/src/main/res/drawable/ic_toggle_disable_24px.xml: -------------------------------------------------------------------------------- 1 | 6 | 13 | 16 | 19 | 20 | -------------------------------------------------------------------------------- /qnotified_style/src/main/res/drawable/ic_toggle_disable_on.xml: -------------------------------------------------------------------------------- 1 | 6 | 13 | 16 | 17 | -------------------------------------------------------------------------------- /qnotified_style/src/main/res/drawable/ic_toggle_off_24px.xml: -------------------------------------------------------------------------------- 1 | 6 | 13 | 17 | 18 | -------------------------------------------------------------------------------- /qnotified_style/src/main/res/drawable/ic_toggle_on_24px.xml: -------------------------------------------------------------------------------- 1 | 6 | 13 | 17 | 18 | -------------------------------------------------------------------------------- /qnotified_style/src/main/res/drawable/icon_empty.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ferredoxin/FerredoxinUI/ecea5e161d79268ee0b7d872889627892ecc089f/qnotified_style/src/main/res/drawable/icon_empty.webp -------------------------------------------------------------------------------- /qnotified_style/src/main/res/drawable/item_back_ripple.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /qnotified_style/src/main/res/drawable/item_background_ripple.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /qnotified_style/src/main/res/drawable/item_category_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /qnotified_style/src/main/res/drawable/item_line.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /qnotified_style/src/main/res/drawable/switch_off_to_on.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 15 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /qnotified_style/src/main/res/drawable/switch_on_to_off.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 15 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /qnotified_style/src/main/res/drawable/tabs_indicator.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /qnotified_style/src/main/res/layout/activity_maitung_tm_style.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 15 | 16 | 25 | 26 | 38 | 39 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 64 | 65 | -------------------------------------------------------------------------------- /qnotified_style/src/main/res/layout/fragment_empty.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 15 | 16 | 25 | 26 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /qnotified_style/src/main/res/layout/fragment_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 12 | 13 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /qnotified_style/src/main/res/layout/fragment_view_pager.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 18 | 19 | 27 | 28 | -------------------------------------------------------------------------------- /qnotified_style/src/main/res/layout/item_about.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 17 | 18 | 31 | 32 | -------------------------------------------------------------------------------- /qnotified_style/src/main/res/layout/item_category.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 18 | 19 | 30 | 31 | -------------------------------------------------------------------------------- /qnotified_style/src/main/res/layout/item_clickable.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 20 | 21 | 30 | 31 | 40 | 41 | 51 | 52 | 58 | 59 | 66 | 67 | 75 | 76 | -------------------------------------------------------------------------------- /qnotified_style/src/main/res/layout/item_clickable_switch.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 19 | 20 | 31 | 32 | 42 | 43 | 49 | 50 | 57 | 58 | 59 | 60 | 67 | 68 | 78 | 79 | 80 | 88 | 89 | -------------------------------------------------------------------------------- /qnotified_style/src/main/res/layout/item_subtitle.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 20 | -------------------------------------------------------------------------------- /qnotified_style/src/main/res/layout/item_switch.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 21 | 22 | 32 | 33 | 39 | 40 | 47 | 48 | 56 | 57 | -------------------------------------------------------------------------------- /qnotified_style/src/main/res/mipmap/ic_launcher_demo.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ferredoxin/FerredoxinUI/ecea5e161d79268ee0b7d872889627892ecc089f/qnotified_style/src/main/res/mipmap/ic_launcher_demo.webp -------------------------------------------------------------------------------- /qnotified_style/src/main/res/values-night/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFFFFFFF 4 | #DEFFFFFF 5 | #99FFFFFF 6 | #FFFFFFFF 7 | #61FFFFFF 8 | #FF202124 9 | #FFFFFFFF 10 | #FFD93025 11 | #FF303134 12 | #61FFFFFF 13 | #61FFFFFF 14 | #FF82A8E7 15 | #FF3C4043 16 | #FF4F5355 17 | #FF4F5355 18 | #FF82A8E7 19 | #FF5F6368 20 | #FF404144 21 | #6182A8E7 22 | #00000000 23 | #9982A8E7 24 | -------------------------------------------------------------------------------- /qnotified_style/src/main/res/values-night/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 20 | 21 | 28 | 29 | 33 | 34 | 39 | -------------------------------------------------------------------------------- /qnotified_style/src/main/res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /qnotified_style/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FF202124 4 | #FF3C4043 5 | #FF5F6368 6 | #FFFFFFFF 7 | #613C4043 8 | #FFFFFFFF 9 | #FFFFFFFF 10 | #FFD93025 11 | #FFFAFAFA 12 | #FF5F6368 13 | #FF5F6368 14 | #FF4285F4 15 | #143C4043 16 | #FFD6DDE7 17 | #FFD6DDE7 18 | #FF4285F4 19 | #FFC0CBD9 20 | #FFDDDDDD 21 | #614285F4 22 | #00000000 23 | #994285F4 24 | 25 | #1A73E8 26 | #0049b5 27 | #1A73E8 28 | 29 | -------------------------------------------------------------------------------- /qnotified_style/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 20 | 21 | 28 | 29 | 33 | 34 | 39 | 40 | 45 | 46 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | dependencyResolutionManagement { 2 | repositoriesMode.set(RepositoriesMode.PREFER_SETTINGS) 3 | repositories { 4 | maven("https://maven.aliyun.com/nexus/content/groups/public/") 5 | gradlePluginPortal() 6 | google() 7 | mavenCentral() 8 | maven("https://jitpack.io") 9 | maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") 10 | } 11 | } 12 | 13 | rootProject.name = "FerredoxinUI" 14 | 15 | 16 | include(":androidDemo") 17 | include(":desktop") 18 | include(":common") 19 | include(":qnotified_style") 20 | --------------------------------------------------------------------------------