├── .editorconfig ├── .github ├── ISSUE_TEMPLATE │ ├── Bug_report.md │ └── Feature_request.md └── pull_request_template.md ├── .gitignore ├── .travis.yml ├── LICENSE.md ├── README.md ├── bintrayconfig.gradle ├── build.gradle ├── dependencies.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── images ├── 1.jpg ├── 2.jpg ├── 3.jpg ├── 4.jpg ├── 5.jpg └── 6.jpg ├── library ├── .gitignore ├── build.gradle └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── afollestad │ │ └── aesthetic │ │ ├── Aesthetic.kt │ │ ├── AestheticActivity.kt │ │ ├── Modes.kt │ │ ├── States.kt │ │ ├── internal │ │ ├── AestheticExt.kt │ │ ├── AttrWizard.kt │ │ ├── InflationDelegate.kt │ │ ├── InflationInterceptor.kt │ │ └── PrefNames.kt │ │ ├── utils │ │ ├── ActivityExt.kt │ │ ├── AttrObservableExt.kt │ │ ├── ClassExt.kt │ │ ├── CollectionExt.kt │ │ ├── ColorExt.kt │ │ ├── ContextExt.kt │ │ ├── EdgeGlowUtil.kt │ │ ├── PackageManagerExt.kt │ │ ├── PrefsExt.kt │ │ ├── ResourcesExt.kt │ │ ├── RxExt.kt │ │ ├── StringExt.kt │ │ ├── TintHelper.kt │ │ └── ViewExt.kt │ │ └── views │ │ ├── AestheticActionMenuItemView.kt │ │ ├── AestheticBorderlessButton.kt │ │ ├── AestheticBottomAppBar.kt │ │ ├── AestheticBottomNavigationView.kt │ │ ├── AestheticButton.kt │ │ ├── AestheticCardView.kt │ │ ├── AestheticCheckBox.kt │ │ ├── AestheticCheckedTextView.kt │ │ ├── AestheticCoordinatorLayout.kt │ │ ├── AestheticDialogButton.kt │ │ ├── AestheticDrawerLayout.kt │ │ ├── AestheticEditText.kt │ │ ├── AestheticFab.kt │ │ ├── AestheticListView.kt │ │ ├── AestheticNavigationView.kt │ │ ├── AestheticNestedScrollView.kt │ │ ├── AestheticProgressBar.kt │ │ ├── AestheticRadioButton.kt │ │ ├── AestheticRatingBar.kt │ │ ├── AestheticRecyclerView.kt │ │ ├── AestheticScrollView.kt │ │ ├── AestheticSeekBar.kt │ │ ├── AestheticSnackBarContentLayout.kt │ │ ├── AestheticSpinner.kt │ │ ├── AestheticSwipeRefreshLayout.kt │ │ ├── AestheticSwitch.kt │ │ ├── AestheticSwitchCompat.kt │ │ ├── AestheticTabLayout.kt │ │ ├── AestheticTextInputEditText.kt │ │ ├── AestheticTextInputLayout.kt │ │ ├── AestheticToolbar.kt │ │ └── AestheticViewPager.kt │ ├── res-public │ └── values │ │ └── public.xml │ └── res │ └── values │ ├── colors.xml │ └── ids.xml ├── sample-appcompat ├── .gitignore ├── build.gradle └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── afollestad │ │ └── aestheticsample │ │ └── appcompat │ │ ├── AppCompatDemoActivity.kt │ │ ├── DrawerActivity.kt │ │ ├── MainAdapter.kt │ │ ├── MainFragment.kt │ │ ├── MainPagerAdapter.kt │ │ ├── RecyclerViewActivity.kt │ │ ├── SecondaryFragment.kt │ │ ├── SettingsActivity.kt │ │ └── SwipeRefreshActivity.kt │ └── res │ ├── drawable │ ├── ic_announcement.xml │ ├── ic_archive.xml │ ├── ic_arrow_back.xml │ ├── ic_attach_money.xml │ ├── ic_backup.xml │ ├── ic_book.xml │ ├── ic_check.xml │ ├── ic_close.xml │ ├── ic_info.xml │ ├── ic_search.xml │ └── ic_watch.xml │ ├── layout │ ├── activity_demo_appcompat.xml │ ├── activity_drawer.xml │ ├── activity_recyclerview.xml │ ├── activity_settings.xml │ ├── activity_swipe_refresh.xml │ ├── drawer_header.xml │ ├── fragment_main.xml │ ├── fragment_secondary.xml │ ├── list_item_rv.xml │ ├── list_item_spinner.xml │ └── list_item_spinner_dropdown.xml │ ├── menu │ ├── bottomtabs.xml │ ├── coordinatorlayout.xml │ ├── drawer.xml │ └── main.xml │ ├── mipmap-hdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-mdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xxhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xxxhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── values │ ├── arrays.xml │ ├── attrs.xml │ ├── colors.xml │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml │ └── xml │ └── preferences.xml ├── sample-materialcomponents ├── .gitignore ├── build.gradle └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── afollestad │ │ └── aestheticsample │ │ └── materialcomponents │ │ └── MaterialComponentsDemoActivity.kt │ └── res │ ├── drawable │ ├── ic_3d_rotation_24px.xml │ ├── ic_accelerator_24px.xml │ ├── ic_add_24px.xml │ ├── ic_dashboard_24px.xml │ ├── ic_drawer_menu_24px.xml │ └── ic_search_24px.xml │ ├── layout │ ├── activity_demo_materialcomponents.xml │ └── main_content.xml │ ├── menu │ └── demo_primary.xml │ ├── mipmap-hdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-mdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xxhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xxxhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ └── values │ ├── attrs.xml │ ├── colors.xml │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml ├── sample.apk ├── settings.gradle ├── spotless.gradle ├── spotless.license.kt └── versionsPlugin.gradle /.editorconfig: -------------------------------------------------------------------------------- 1 | [*.kt] 2 | indent_size = 2 3 | continuation_indent_size=4 -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/Bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Something is crashing or not working as intended 4 | labels: bug 5 | 6 | --- 7 | 8 | *Please consider making a Pull Request if you are capable of doing so.* 9 | 10 | **Library Version:** 11 | 12 | 1.x.x 13 | 14 | **Affected Device(s):** 15 | 16 | Google Pixel 3 XL with Android 9.0 17 | 18 | **Describe the Bug:** 19 | 20 | A clear description of what is the bug is. 21 | 22 | **To Reproduce:** 23 | 1. 24 | 2. 25 | 3. 26 | 27 | **Expected Behavior:** 28 | 29 | A clear description of what you expected to happen. 30 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/Feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | labels: enhancement 5 | 6 | --- 7 | 8 | *Please consider making a Pull Request if you are capable of doing so.* 9 | 10 | **What module does this apply to?** 11 | 12 | Core? Input? Files? Color? 13 | 14 | **Description what you'd like to happen:** 15 | 16 | A clear description if the feature or behavior you'd like implemented. 17 | 18 | **Describe alternatives you've considered:** 19 | 20 | A clear description of any alternative solutions you've considered. 21 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ### Guidelines 2 | 3 | 1. You must run the `spotlessApply` task before commiting, either through Android Studio or with `./gradlew spotlessApply`. 4 | 2. A PR should be focused and contained. If you are changing multiple unrelated things, they should be in separate PRs. 5 | 3. A PR should fix a bug or solve a problem - something that only you would use is not necessarily something that should be published. 6 | 4. Give your PR a detailed title and description - look over your code one last time before actually creating the PR. Give it a self-review. 7 | 8 | **If you do not follow the guidelines, your PR will be rejected.** 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/osx,macos,android,windows,androidstudio 3 | 4 | ### Android ### 5 | # Built application files 6 | *.ap_ 7 | 8 | # Files for the ART/Dalvik VM 9 | *.dex 10 | 11 | # Java class files 12 | *.class 13 | 14 | # Generated files 15 | bin/ 16 | gen/ 17 | out/ 18 | 19 | # Gradle files 20 | .gradle/ 21 | build/ 22 | 23 | # Local configuration file (sdk path, etc) 24 | local.properties 25 | 26 | # Proguard folder generated by Eclipse 27 | proguard/ 28 | 29 | # Log Files 30 | *.log 31 | 32 | # Android Studio Navigation editor temp files 33 | .navigation/ 34 | 35 | # Android Studio captures folder 36 | captures/ 37 | 38 | # Intellij 39 | *.iml 40 | .idea/workspace.xml 41 | .idea/tasks.xml 42 | .idea/gradle.xml 43 | .idea/dictionaries 44 | .idea/libraries 45 | 46 | # Keystore files 47 | *.jks 48 | 49 | # External native build folder generated in Android Studio 2.2 and later 50 | .externalNativeBuild 51 | 52 | # Google Services (e.g. APIs or Firebase) 53 | google-services.json 54 | 55 | # Freeline 56 | freeline.py 57 | freeline/ 58 | freeline_project_description.json 59 | 60 | ### Android Patch ### 61 | gen-external-apklibs 62 | 63 | ### AndroidStudio ### 64 | # Covers files to be ignored for android development using Android Studio. 65 | 66 | # Built application files 67 | 68 | # Files for the ART/Dalvik VM 69 | 70 | # Java class files 71 | 72 | # Generated files 73 | 74 | # Gradle files 75 | .gradle 76 | 77 | # Signing files 78 | .signing/ 79 | 80 | # Local configuration file (sdk path, etc) 81 | 82 | # Proguard folder generated by Eclipse 83 | 84 | # Log Files 85 | 86 | # Android Studio 87 | /*/build/ 88 | /*/local.properties 89 | /*/out 90 | /*/*/build 91 | /*/*/production 92 | *.ipr 93 | *~ 94 | *.swp 95 | 96 | # Android Patch 97 | 98 | # External native build folder generated in Android Studio 2.2 and later 99 | 100 | # NDK 101 | obj/ 102 | 103 | # IntelliJ IDEA 104 | *.iws 105 | /out/ 106 | 107 | # User-specific configurations 108 | .idea/libraries/ 109 | .idea/.name 110 | .idea/compiler.xml 111 | .idea/copyright/profiles_settings.xml 112 | .idea/encodings.xml 113 | .idea/misc.xml 114 | .idea/modules.xml 115 | .idea/scopes/scope_settings.xml 116 | .idea/vcs.xml 117 | .idea/jsLibraryMappings.xml 118 | .idea/datasources.xml 119 | .idea/dataSources.ids 120 | .idea/sqlDataSources.xml 121 | .idea/dynamic.xml 122 | .idea/uiDesigner.xml 123 | 124 | # Keystore files 125 | 126 | # OS-specific files 127 | .DS_Store 128 | .DS_Store? 129 | ._* 130 | .Spotlight-V100 131 | .Trashes 132 | ehthumbs.db 133 | Thumbs.db 134 | 135 | # Legacy Eclipse project files 136 | .classpath 137 | .project 138 | 139 | # Mobile Tools for Java (J2ME) 140 | .mtj.tmp/ 141 | 142 | # Package Files # 143 | *.jar 144 | *.war 145 | *.ear 146 | 147 | # virtual machine crash logs (Reference: http://www.java.com/en/download/help/error_hotspot.xml) 148 | hs_err_pid* 149 | 150 | ## Plugin-specific files: 151 | 152 | # mpeltonen/sbt-idea plugin 153 | .idea_modules/ 154 | 155 | # JIRA plugin 156 | atlassian-ide-plugin.xml 157 | 158 | # Mongo Explorer plugin 159 | .idea/mongoSettings.xml 160 | 161 | # Crashlytics plugin (for Android Studio and IntelliJ) 162 | com_crashlytics_export_strings.xml 163 | crashlytics.properties 164 | crashlytics-build.properties 165 | fabric.properties 166 | 167 | ### AndroidStudio Patch ### 168 | # Google Services plugin 169 | 170 | !/gradle/wrapper/gradle-wrapper.jar 171 | 172 | ### macOS ### 173 | *.DS_Store 174 | .AppleDouble 175 | .LSOverride 176 | 177 | # Icon must end with two \r 178 | Icon 179 | 180 | # Thumbnails 181 | 182 | # Files that might appear in the root of a volume 183 | .DocumentRevisions-V100 184 | .fseventsd 185 | .TemporaryItems 186 | .VolumeIcon.icns 187 | .com.apple.timemachine.donotpresent 188 | 189 | # Directories potentially created on remote AFP share 190 | .AppleDB 191 | .AppleDesktop 192 | Network Trash Folder 193 | Temporary Items 194 | .apdisk 195 | 196 | ### OSX ### 197 | 198 | # Icon must end with two \r 199 | 200 | # Thumbnails 201 | 202 | # Files that might appear in the root of a volume 203 | 204 | # Directories potentially created on remote AFP share 205 | 206 | ### Windows ### 207 | # Windows thumbnail cache files 208 | ehthumbs_vista.db 209 | 210 | # Folder config file 211 | Desktop.ini 212 | 213 | # Recycle Bin used on file shares 214 | $RECYCLE.BIN/ 215 | 216 | # Windows Installer files 217 | *.cab 218 | *.msi 219 | *.msm 220 | *.msp 221 | 222 | # Windows shortcuts 223 | *.lnk 224 | 225 | # End of https://www.gitignore.io/api/osx,macos,android,windows,androidstudio -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: android 2 | dist: trusty 3 | android: 4 | components: 5 | - tools 6 | - platform-tools 7 | - build-tools-29.0.0 8 | - android-29 9 | - extra-android-support 10 | - extra-android-m2repository 11 | - extra-google-m2repository 12 | 13 | licenses: 14 | - '.+' 15 | -------------------------------------------------------------------------------- /bintrayconfig.gradle: -------------------------------------------------------------------------------- 1 | if (!project.rootProject.file('local.properties').exists()) { 2 | println "Not applying bintrayconfig.gradle" 3 | return 4 | } 5 | apply plugin: 'com.novoda.bintray-release' 6 | 7 | def getBintrayUserAndKey() { 8 | Properties properties = new Properties() 9 | properties.load(project.rootProject.file('local.properties').newDataInputStream()) 10 | return [ 11 | properties.getProperty("bintray.user"), 12 | properties.getProperty("bintray.apikey") 13 | ] 14 | } 15 | 16 | if (versions == null || versions.publishVersion == null) { 17 | throw new IllegalStateException("Unable to reference publishVersion") 18 | } 19 | 20 | task checkBintrayConfig { 21 | doLast { 22 | def (user, key) = getBintrayUserAndKey() 23 | if (user == null || user.isEmpty() || 24 | key == null || key.isEmpty()) { 25 | throw new IllegalStateException("Must specify Bintray user/API key in your local.properties.") 26 | } 27 | } 28 | } 29 | 30 | afterEvaluate { 31 | bintrayUpload.dependsOn checkBintrayConfig 32 | } 33 | 34 | def (user, key) = getBintrayUserAndKey() 35 | publish { 36 | bintrayUser = user 37 | bintrayKey = key 38 | userOrg = 'drummer-aidan' 39 | groupId = 'com.afollestad' 40 | artifactId = 'aesthetic' 41 | publishVersion = versions.publishVersion 42 | desc = 43 | '[BETA] A fast and easy to use plug-and-play dynamic theme engine. Powered by Rx, for Android apps.' 44 | website = 'https://github.com/afollestad/aesthetic' 45 | dryRun = false 46 | override = true 47 | } -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | apply from: './dependencies.gradle' 2 | apply from: './versionsPlugin.gradle' 3 | 4 | buildscript { 5 | apply from: './dependencies.gradle' 6 | 7 | repositories { 8 | google() 9 | jcenter() 10 | } 11 | 12 | dependencies { 13 | classpath 'com.android.tools.build:gradle:' + versions.gradlePlugin 14 | classpath "com.diffplug.spotless:spotless-plugin-gradle:" + versions.spotlessPlugin 15 | classpath 'com.novoda:bintray-release:' + versions.bintrayRelease 16 | classpath 'com.github.ben-manes:gradle-versions-plugin:' + versions.versionsPlugin 17 | classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:' + versions.kotlin 18 | } 19 | } 20 | 21 | allprojects { 22 | repositories { 23 | google() 24 | jcenter() 25 | } 26 | 27 | tasks.withType(Javadoc).all { 28 | enabled = false 29 | } 30 | } -------------------------------------------------------------------------------- /dependencies.gradle: -------------------------------------------------------------------------------- 1 | ext.versions = [ 2 | minSdk : 16, 3 | compileSdk : 29, 4 | buildTools : '29.0.0', 5 | publishVersion : '1.0.0-beta05', 6 | publishVersionCode: 33, 7 | 8 | gradlePlugin : '3.4.1', 9 | kotlin : '1.3.31', 10 | spotlessPlugin : '3.22.0', 11 | versionsPlugin : '0.20.0', 12 | bintrayRelease : '0.9.1', 13 | 14 | androidxCore : '1.0.2', 15 | androidx : '1.0.0', 16 | 17 | rxJava : '2.2.6', 18 | rxAndroid : '2.1.1', 19 | rxkPrefs : '1.2.5', 20 | leakCanary : '1.6.1' 21 | ] 22 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | org.gradle.jvmargs=-Xmx1536m 13 | org.gradle.configureondemand=false 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true 19 | 20 | android.useAndroidX=true 21 | android.enableJetifier=true 22 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afollestad/aesthetic/896215c2905cb1b18467c86e97e2353aacbb19ea/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Tue Jul 17 12:42:56 PDT 2018 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-bin.zip 7 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /images/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afollestad/aesthetic/896215c2905cb1b18467c86e97e2353aacbb19ea/images/1.jpg -------------------------------------------------------------------------------- /images/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afollestad/aesthetic/896215c2905cb1b18467c86e97e2353aacbb19ea/images/2.jpg -------------------------------------------------------------------------------- /images/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afollestad/aesthetic/896215c2905cb1b18467c86e97e2353aacbb19ea/images/3.jpg -------------------------------------------------------------------------------- /images/4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afollestad/aesthetic/896215c2905cb1b18467c86e97e2353aacbb19ea/images/4.jpg -------------------------------------------------------------------------------- /images/5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afollestad/aesthetic/896215c2905cb1b18467c86e97e2353aacbb19ea/images/5.jpg -------------------------------------------------------------------------------- /images/6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afollestad/aesthetic/896215c2905cb1b18467c86e97e2353aacbb19ea/images/6.jpg -------------------------------------------------------------------------------- /library/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /library/build.gradle: -------------------------------------------------------------------------------- 1 | apply from: '../dependencies.gradle' 2 | apply plugin: 'com.android.library' 3 | apply plugin: 'kotlin-android' 4 | apply plugin: 'kotlin-kapt' 5 | 6 | apply from: '../bintrayconfig.gradle' 7 | 8 | android { 9 | compileSdkVersion versions.compileSdk 10 | buildToolsVersion versions.buildTools 11 | 12 | defaultConfig { 13 | minSdkVersion versions.minSdk 14 | targetSdkVersion versions.compileSdk 15 | versionCode versions.publishVersionCode 16 | versionName versions.publishVersion 17 | } 18 | 19 | sourceSets { 20 | main.res.srcDirs = [ 21 | 'src/main/res', 22 | 'src/main/res-public' 23 | ] 24 | } 25 | 26 | compileOptions { 27 | kotlinOptions.freeCompilerArgs += ['-module-name', "com.afollestad.aesthetic"] 28 | } 29 | 30 | packagingOptions { 31 | pickFirst 'META-INF/proguard/androidx-annotations.pro' 32 | } 33 | } 34 | 35 | repositories { 36 | google() 37 | jcenter() 38 | } 39 | 40 | dependencies { 41 | api 'androidx.appcompat:appcompat:' + versions.androidxCore 42 | 43 | implementation 'androidx.cardview:cardview:' + versions.androidx 44 | implementation 'androidx.recyclerview:recyclerview:' + versions.androidx 45 | implementation 'com.google.android.material:material:' + versions.androidx 46 | 47 | implementation 'io.reactivex.rxjava2:rxjava:' + versions.rxJava 48 | implementation 'io.reactivex.rxjava2:rxandroid:' + versions.rxAndroid 49 | implementation 'com.afollestad:rxkprefs:' + versions.rxkPrefs 50 | 51 | implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8:' + versions.kotlin 52 | } 53 | 54 | apply from: '../spotless.gradle' 55 | -------------------------------------------------------------------------------- /library/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /library/src/main/java/com/afollestad/aesthetic/AestheticActivity.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Designed and developed by Aidan Follestad (@afollestad) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.afollestad.aesthetic 17 | 18 | import android.os.Bundle 19 | import androidx.appcompat.app.AppCompatActivity 20 | import com.afollestad.aesthetic.internal.InflationDelegate 21 | 22 | /** @author Aidan Follestad (afollestad) */ 23 | open class AestheticActivity : AppCompatActivity() { 24 | 25 | open fun getInflationDelegate(): InflationDelegate? = null 26 | 27 | override fun onCreate(savedInstanceState: Bundle?) { 28 | Aesthetic.attach(this, getInflationDelegate()) 29 | super.onCreate(savedInstanceState) 30 | } 31 | 32 | override fun onResume() { 33 | super.onResume() 34 | Aesthetic.resume(this) 35 | } 36 | 37 | override fun onPause() { 38 | Aesthetic.pause(this) 39 | super.onPause() 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /library/src/main/java/com/afollestad/aesthetic/Modes.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Designed and developed by Aidan Follestad (@afollestad) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | @file:Suppress("unused") 17 | 18 | package com.afollestad.aesthetic 19 | 20 | import io.reactivex.Observable 21 | 22 | @Deprecated(message = "Use ColorMode instead.", replaceWith = ReplaceWith("ColorMode")) 23 | typealias TabLayoutBgMode = ColorMode 24 | 25 | @Deprecated(message = "Use ColorMode instead.", replaceWith = ReplaceWith("ColorMode")) 26 | typealias TabLayoutIndicatorMode = ColorMode 27 | 28 | /** @author Aidan Follestad (afollestad)*/ 29 | enum class ColorMode(val value: Int) { 30 | PRIMARY(0), 31 | ACCENT(1); 32 | 33 | companion object { 34 | internal fun fromInt(value: Int): ColorMode { 35 | return when (value) { 36 | 0 -> PRIMARY 37 | else -> ACCENT 38 | } 39 | } 40 | } 41 | } 42 | 43 | internal fun Observable.mapToColorMode(): Observable { 44 | return map { ColorMode.fromInt(it) } 45 | } 46 | 47 | /** @author Aidan Follestad (afollestad) */ 48 | enum class NavigationViewMode(val value: Int) { 49 | SELECTED_PRIMARY(0), 50 | SELECTED_ACCENT(1), 51 | NONE(2); 52 | 53 | companion object { 54 | internal fun fromInt(value: Int): NavigationViewMode { 55 | return when (value) { 56 | 0 -> SELECTED_PRIMARY 57 | 1 -> SELECTED_ACCENT 58 | else -> NONE 59 | } 60 | } 61 | } 62 | } 63 | 64 | internal fun Observable.mapToNavigationViewMode(): Observable { 65 | return map { NavigationViewMode.fromInt(it) } 66 | } 67 | 68 | /** @author Aidan Follestad (afollestad) */ 69 | enum class AutoSwitchMode(val value: Int) { 70 | OFF(0), 71 | ON(1), 72 | AUTO(2); 73 | 74 | companion object { 75 | internal fun fromInt(value: Int): AutoSwitchMode { 76 | return when (value) { 77 | 0 -> OFF 78 | 1 -> ON 79 | else -> AUTO 80 | } 81 | } 82 | } 83 | } 84 | 85 | internal fun Observable.mapToAutoSwitchMode(): Observable { 86 | return map { AutoSwitchMode.fromInt(it) } 87 | } 88 | 89 | /** @author Aidan Follestad (afollestad) */ 90 | enum class BottomNavBgMode(val value: Int) { 91 | BLACK_WHITE_AUTO(0), 92 | PRIMARY(1), 93 | PRIMARY_DARK(2), 94 | ACCENT(3), 95 | NONE(4); 96 | 97 | companion object { 98 | internal fun fromInt(value: Int): BottomNavBgMode { 99 | return when (value) { 100 | 0 -> BLACK_WHITE_AUTO 101 | 1 -> PRIMARY 102 | 2 -> PRIMARY_DARK 103 | 3 -> ACCENT 104 | else -> NONE 105 | } 106 | } 107 | } 108 | } 109 | 110 | internal fun Observable.mapToBottomNavBgMode(): Observable { 111 | return map { BottomNavBgMode.fromInt(it) } 112 | } 113 | 114 | /** @author Aidan Follestad (afollestad) */ 115 | enum class BottomNavIconTextMode(val value: Int) { 116 | SELECTED_PRIMARY(0), 117 | SELECTED_ACCENT(1), 118 | BLACK_WHITE_AUTO(2), 119 | NONE(3); 120 | 121 | companion object { 122 | internal fun fromInt(value: Int): BottomNavIconTextMode { 123 | return when (value) { 124 | 0 -> SELECTED_PRIMARY 125 | 1 -> SELECTED_ACCENT 126 | 2 -> BLACK_WHITE_AUTO 127 | else -> NONE 128 | } 129 | } 130 | } 131 | } 132 | 133 | internal fun Observable.mapToBottomNavIconTextMode(): Observable { 134 | return map { BottomNavIconTextMode.fromInt(it) } 135 | } 136 | -------------------------------------------------------------------------------- /library/src/main/java/com/afollestad/aesthetic/States.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Designed and developed by Aidan Follestad (@afollestad) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.afollestad.aesthetic 17 | 18 | import android.R.attr 19 | import android.content.res.ColorStateList 20 | import androidx.annotation.ColorInt 21 | 22 | /** @author Aidan Follestad (afollestad) */ 23 | internal data class ActiveInactiveColors( 24 | @field:ColorInt val activeColor: Int, 25 | @field:ColorInt val inactiveColor: Int 26 | ) { 27 | fun toEnabledSl(): ColorStateList { 28 | return ColorStateList( 29 | arrayOf( 30 | intArrayOf(attr.state_enabled), intArrayOf(-attr.state_enabled) 31 | ), 32 | intArrayOf(activeColor, inactiveColor) 33 | ) 34 | } 35 | } 36 | 37 | /** @author Aidan Follestad (afollestad) */ 38 | internal data class ColorIsDarkState( 39 | @field:ColorInt val color: Int, 40 | val isDark: Boolean 41 | ) 42 | -------------------------------------------------------------------------------- /library/src/main/java/com/afollestad/aesthetic/internal/AttrWizard.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Designed and developed by Aidan Follestad (@afollestad) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.afollestad.aesthetic.internal 17 | 18 | import android.content.Context 19 | import android.util.AttributeSet 20 | import androidx.annotation.AttrRes 21 | import com.afollestad.aesthetic.utils.isNumber 22 | import com.afollestad.aesthetic.utils.safeResourceName 23 | 24 | /** @author Aidan Follestad (afollestad) */ 25 | internal class AttrWizard( 26 | private val context: Context, 27 | private val attrs: AttributeSet? 28 | ) { 29 | 30 | fun getRawValue(@AttrRes attrId: Int): String { 31 | if (attrs == null || attrId == 0) { 32 | return "" 33 | } 34 | 35 | val res = context.resources 36 | val attrName = res.safeResourceName(attrId) 37 | val attrIndex = attrs.indexOfAttr(context) { it == attrName } 38 | if (attrIndex == -1) { 39 | return "" 40 | } 41 | 42 | val attrValue = attrs.getAttributeValue(attrIndex) 43 | return when { 44 | attrValue.startsWith('@') || attrValue.startsWith('?') -> { 45 | val rawId = attrValue.substring(1) 46 | var resName = if (rawId.isNumber()) { 47 | val id = rawId.toInt() 48 | if (id == 0) { 49 | return "" 50 | } 51 | res.safeResourceName(id) 52 | } else { 53 | rawId 54 | } 55 | if (!resName.startsWith("android")) { 56 | resName = resName.substring(resName.indexOf(':') + 1) 57 | } 58 | "${attrValue[0]}$resName" 59 | } 60 | else -> attrValue 61 | } 62 | } 63 | } 64 | 65 | private fun AttributeSet.indexOfAttr( 66 | context: Context, 67 | matcher: (String) -> (Boolean) 68 | ): Int { 69 | for (i in 0 until attributeCount) { 70 | val nameResource = getAttributeNameResource(i) 71 | val literalName = context.resources.safeResourceName(nameResource) 72 | if (matcher(literalName)) { 73 | return i 74 | } 75 | } 76 | return -1 77 | } 78 | -------------------------------------------------------------------------------- /library/src/main/java/com/afollestad/aesthetic/internal/InflationDelegate.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Designed and developed by Aidan Follestad (@afollestad) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.afollestad.aesthetic.internal 17 | 18 | import android.content.Context 19 | import android.util.AttributeSet 20 | import android.view.View 21 | import androidx.annotation.IdRes 22 | 23 | /** @author Aidan Follestad (afollestad) */ 24 | interface InflationDelegate { 25 | 26 | fun createView( 27 | context: Context, 28 | attrs: AttributeSet?, 29 | name: String, 30 | @IdRes viewId: Int 31 | ): View? 32 | } 33 | -------------------------------------------------------------------------------- /library/src/main/java/com/afollestad/aesthetic/internal/PrefNames.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Designed and developed by Aidan Follestad (@afollestad) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.afollestad.aesthetic.internal 17 | 18 | // Base 19 | const val PREFS_NAME = "[aesthetic-prefs]" 20 | const val KEY_FIRST_TIME = "first_time" 21 | const val KEY_ACTIVITY_THEME = "activity_theme_default" 22 | const val KEY_IS_DARK = "is_dark" 23 | const val KEY_ATTRIBUTE = "ate_attribute.%s" 24 | 25 | // Legacy Attributes 26 | @Deprecated("Legacy attribute") 27 | const val KEY_PRIMARY_COLOR = "primary_color" 28 | @Deprecated("Legacy attribute") 29 | const val KEY_PRIMARY_DARK_COLOR = "primary_dark_color" 30 | @Deprecated("Legacy attribute") 31 | const val KEY_ACCENT_COLOR = "accent_color" 32 | @Deprecated("Legacy attribute") 33 | const val KEY_PRIMARY_TEXT_COLOR = "primary_text" 34 | @Deprecated("Legacy attribute") 35 | const val KEY_SECONDARY_TEXT_COLOR = "secondary_text" 36 | @Deprecated("Legacy attribute") 37 | const val KEY_PRIMARY_TEXT_INVERSE_COLOR = "primary_text_inverse" 38 | @Deprecated("Legacy attribute") 39 | const val KEY_SECONDARY_TEXT_INVERSE_COLOR = "secondary_text_inverse" 40 | @Deprecated("Legacy attribute") 41 | const val KEY_WINDOW_BG_COLOR = "window_bg_color" 42 | @Deprecated("Legacy attribute") 43 | const val KEY_ICON_TITLE_ACTIVE_COLOR = "icon_title_active_color" 44 | @Deprecated("Legacy attribute") 45 | const val KEY_ICON_TITLE_INACTIVE_COLOR = "icon_title_inactive_color" 46 | 47 | // Window/System 48 | const val KEY_STATUS_BAR_COLOR = "status_bar_color_default" 49 | const val KEY_NAV_BAR_COLOR = "nav_bar_color_default" 50 | const val KEY_LIGHT_STATUS_MODE = "light_status_mode" 51 | const val KEY_LIGHT_NAV_MODE = "light_navigation_bar_mode" 52 | 53 | // Custom Views 54 | const val KEY_TOOLBAR_ICON_COLOR = "toolbar_icon_color" 55 | const val KEY_TOOLBAR_TITLE_COLOR = "toolbar_title_color" 56 | const val KEY_TOOLBAR_SUBTITLE_COLOR = "toolbar_subtitle_color" 57 | const val KEY_TAB_LAYOUT_BG_MODE = "tab_layout_bg_mode" 58 | const val KEY_TAB_LAYOUT_INDICATOR_MODE = "tab_layout_indicator_mode" 59 | const val KEY_NAV_VIEW_MODE = "nav_view_mode" 60 | const val KEY_BOTTOM_NAV_BG_MODE = "bottom_nav_bg_mode" 61 | const val KEY_BOTTOM_NAV_ICONTEXT_MODE = "bottom_nav_icontext_mode" 62 | const val KEY_CARD_VIEW_BG_COLOR = "card_view_bg_color" 63 | const val KEY_SNACKBAR_TEXT = "snackbar_text_color" 64 | const val KEY_SNACKBAR_ACTION_TEXT = "snackbar_action_text_color" 65 | const val KEY_SNACKBAR_BG_COLOR = "snackbar_bg_color" 66 | const val KEY_SWIPEREFRESH_COLORS = "swiperefreshlayout_colors" 67 | -------------------------------------------------------------------------------- /library/src/main/java/com/afollestad/aesthetic/utils/ActivityExt.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Designed and developed by Aidan Follestad (@afollestad) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.afollestad.aesthetic.utils 17 | 18 | import android.annotation.TargetApi 19 | import android.app.Activity 20 | import android.app.ActivityManager 21 | import android.graphics.Bitmap 22 | import android.graphics.drawable.BitmapDrawable 23 | import android.os.Build 24 | import android.os.Build.VERSION.SDK_INT 25 | import android.os.Build.VERSION_CODES.LOLLIPOP 26 | import android.os.Build.VERSION_CODES.M 27 | import android.os.Build.VERSION_CODES.O 28 | import android.view.LayoutInflater 29 | import android.view.View 30 | import android.view.ViewGroup 31 | import androidx.annotation.ColorInt 32 | import androidx.appcompat.app.AppCompatActivity 33 | import androidx.core.view.LayoutInflaterCompat.setFactory2 34 | import com.afollestad.aesthetic.internal.InflationDelegate 35 | import com.afollestad.aesthetic.internal.InflationInterceptor 36 | 37 | internal fun AppCompatActivity.setInflaterFactory( 38 | li: LayoutInflater, 39 | aestheticDelegate: InflationDelegate? 40 | ) = setFactory2(li, InflationInterceptor(this, aestheticDelegate, delegate)) 41 | 42 | internal fun Activity.setStatusBarColorCompat(@ColorInt color: Int) { 43 | if (SDK_INT >= LOLLIPOP) { 44 | window.statusBarColor = color 45 | } 46 | } 47 | 48 | internal fun Activity.getRootView(): ViewGroup? { 49 | val content = findViewById(android.R.id.content) 50 | if (content != null && content.getChildAt(0) is ViewGroup) { 51 | return content.getChildAt(0) as ViewGroup 52 | } 53 | return null 54 | } 55 | 56 | internal fun Activity?.setNavBarColorCompat(@ColorInt color: Int) { 57 | if (SDK_INT >= LOLLIPOP) { 58 | this?.window?.navigationBarColor = color 59 | } 60 | } 61 | 62 | internal fun Activity?.setLightStatusBarCompat(lightMode: Boolean) { 63 | val view = this?.window?.decorView ?: return 64 | if (SDK_INT >= M) { 65 | var flags = view.systemUiVisibility 66 | flags = if (lightMode) { 67 | flags or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR 68 | } else { 69 | flags and View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.inv() 70 | } 71 | view.systemUiVisibility = flags 72 | } 73 | } 74 | 75 | internal fun Activity?.setLightNavBarCompat(lightMode: Boolean) { 76 | val view = this?.window?.decorView ?: return 77 | if (SDK_INT >= O) { 78 | var flags = view.systemUiVisibility 79 | flags = if (lightMode) { 80 | flags or View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR 81 | } else { 82 | flags and View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR.inv() 83 | } 84 | view.systemUiVisibility = flags 85 | } 86 | } 87 | 88 | @TargetApi(Build.VERSION_CODES.LOLLIPOP) 89 | internal fun Activity?.setTaskDescriptionColor(@ColorInt requestedColor: Int) { 90 | if (this == null || SDK_INT <= LOLLIPOP) return 91 | var color = requestedColor 92 | 93 | // Task description requires fully opaque color 94 | color = color.stripAlpha() 95 | // Default is app's launcher icon 96 | val icon: Bitmap? = if (Build.VERSION.SDK_INT >= 26) { 97 | packageManager.getAppIcon(packageName) 98 | } else { 99 | (applicationInfo.loadIcon(packageManager) as BitmapDrawable) 100 | .bitmap 101 | } 102 | if (icon != null) { 103 | // Sets color of entry in the system recents page 104 | @Suppress("DEPRECATION") 105 | val td = ActivityManager.TaskDescription(title as String, icon, color) 106 | setTaskDescription(td) 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /library/src/main/java/com/afollestad/aesthetic/utils/AttrObservableExt.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Designed and developed by Aidan Follestad (@afollestad) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.afollestad.aesthetic.utils 17 | 18 | import androidx.annotation.CheckResult 19 | import com.afollestad.aesthetic.Aesthetic 20 | import io.reactivex.Observable 21 | import io.reactivex.Observable.empty 22 | 23 | @CheckResult internal fun Aesthetic.observableForAttrName( 24 | name: String, 25 | fallback: Observable? = null 26 | ): Observable? { 27 | if (name.isNotEmpty() && !name.startsWith('?')) { 28 | // Don't override the hardcoded or resource value that is set. 29 | return empty() 30 | } 31 | return when (name) { 32 | "" -> fallback 33 | 34 | "?attr/colorPrimary", "?android:attr/colorPrimary" -> colorPrimary() 35 | "?attr/colorPrimaryDark", "?android:attr/colorPrimaryDark" -> colorPrimaryDark() 36 | "?attr/colorAccent", "?android:attr/colorAccent" -> colorAccent() 37 | 38 | "?android:attr/statusBarColor" -> colorStatusBar() 39 | "?android:attr/navigationBarColor" -> colorNavigationBar() 40 | "?android:attr/windowBackground" -> colorWindowBackground() 41 | 42 | "?android:attr/textColorPrimary" -> textColorPrimary() 43 | "?android:attr/textColorPrimaryInverse" -> textColorPrimaryInverse() 44 | "?android:attr/textColorSecondary" -> textColorSecondary() 45 | "?android:attr/textColorSecondaryInverse" -> textColorSecondaryInverse() 46 | 47 | else -> fallback ?: attribute(name.substring(1)) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /library/src/main/java/com/afollestad/aesthetic/utils/ClassExt.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Designed and developed by Aidan Follestad (@afollestad) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.afollestad.aesthetic.utils 17 | 18 | import java.lang.reflect.Field 19 | import kotlin.reflect.KClass 20 | 21 | internal fun KClass.findField(vararg nameOptions: String): Field = with(java) { 22 | for (name in nameOptions) { 23 | try { 24 | val field = getDeclaredField(name) 25 | field.isAccessible = true 26 | return field 27 | } catch (_: NoSuchFieldException) { 28 | } 29 | } 30 | throw IllegalArgumentException( 31 | "Unable to find any of fields ${nameOptions.toList()} in ${this.name}" 32 | ) 33 | } 34 | -------------------------------------------------------------------------------- /library/src/main/java/com/afollestad/aesthetic/utils/CollectionExt.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Designed and developed by Aidan Follestad (@afollestad) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.afollestad.aesthetic.utils 17 | 18 | import androidx.collection.ArrayMap 19 | import io.reactivex.Observable 20 | 21 | internal fun String.splitToInts(delimiter: String = ","): IntArray { 22 | return split(delimiter).map { it.toInt() } 23 | .toIntArray() 24 | } 25 | 26 | internal fun Observable.mapToIntArray(delimiter: String = ","): Observable { 27 | return map { it.splitToInts(delimiter) } 28 | } 29 | 30 | internal fun mutableArrayMap(initialCapacity: Int = 0): MutableMap { 31 | return ArrayMap(initialCapacity) 32 | } 33 | -------------------------------------------------------------------------------- /library/src/main/java/com/afollestad/aesthetic/utils/ColorExt.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Designed and developed by Aidan Follestad (@afollestad) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.afollestad.aesthetic.utils 17 | 18 | import android.graphics.Color 19 | import androidx.annotation.ColorInt 20 | import androidx.annotation.FloatRange 21 | 22 | @ColorInt internal fun Int.blendWith( 23 | @ColorInt otherColor: Int, 24 | ratio: Float 25 | ): Int { 26 | val inverseRatio = 1f - ratio 27 | val a = Color.alpha(this) * inverseRatio + Color.alpha(otherColor) * ratio 28 | val r = Color.red(this) * inverseRatio + Color.red(otherColor) * ratio 29 | val g = Color.green(this) * inverseRatio + Color.green(otherColor) * ratio 30 | val b = Color.blue(this) * inverseRatio + Color.blue(otherColor) * ratio 31 | return Color.argb(a.toInt(), r.toInt(), g.toInt(), b.toInt()) 32 | } 33 | 34 | @ColorInt internal fun Int.stripAlpha(): Int { 35 | return Color.rgb( 36 | Color.red(this), 37 | Color.green(this), 38 | Color.blue(this) 39 | ) 40 | } 41 | 42 | @ColorInt internal fun Int.adjustAlpha(factor: Float): Int { 43 | val alpha = Math.round(Color.alpha(this) * factor) 44 | val red = Color.red(this) 45 | val green = Color.green(this) 46 | val blue = Color.blue(this) 47 | return Color.argb(alpha, red, green, blue) 48 | } 49 | 50 | @ColorInt 51 | internal fun Int.shiftColor( 52 | @FloatRange(from = 0.0, to = 2.0) by: Float 53 | ): Int { 54 | if (by == 1f) return this 55 | val hsv = FloatArray(3) 56 | Color.colorToHSV(this, hsv) 57 | hsv[2] *= by // value component 58 | return Color.HSVToColor(hsv) 59 | } 60 | 61 | @ColorInt 62 | internal fun Int.darkenColor(): Int { 63 | return shiftColor(0.9f) 64 | } 65 | 66 | internal fun Int.isColorLight(): Boolean { 67 | if (this == Color.BLACK) { 68 | return false 69 | } else if (this == Color.WHITE || this == Color.TRANSPARENT) { 70 | return true 71 | } 72 | val darkness = 73 | 1 - (0.299 * Color.red(this) + 74 | 0.587 * Color.green(this) + 75 | 0.114 * Color.blue(this)) / 255 76 | return darkness < 0.4 77 | } 78 | -------------------------------------------------------------------------------- /library/src/main/java/com/afollestad/aesthetic/utils/ContextExt.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Designed and developed by Aidan Follestad (@afollestad) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.afollestad.aesthetic.utils 17 | 18 | import android.annotation.SuppressLint 19 | import android.content.Context 20 | import android.graphics.drawable.Drawable 21 | import android.os.Build.VERSION.SDK_INT 22 | import android.os.Build.VERSION_CODES.P 23 | import android.util.AttributeSet 24 | import android.view.LayoutInflater 25 | import androidx.annotation.AttrRes 26 | import androidx.annotation.ColorInt 27 | import androidx.annotation.ColorRes 28 | import androidx.annotation.DrawableRes 29 | import androidx.annotation.IdRes 30 | import androidx.core.content.ContextCompat 31 | import java.lang.reflect.Array 32 | import java.lang.reflect.Field 33 | 34 | @ColorInt internal fun Context.color(@ColorRes color: Int): Int { 35 | return ContextCompat.getColor(this, color) 36 | } 37 | 38 | internal fun Context.drawable(@DrawableRes drawable: Int): Drawable? { 39 | return ContextCompat.getDrawable(this, drawable) 40 | } 41 | 42 | @ColorInt internal fun Context.colorAttr(@AttrRes attr: Int, @ColorInt fallback: Int = 0): Int { 43 | val a = theme.obtainStyledAttributes(intArrayOf(attr)) 44 | return try { 45 | a.getColor(0, fallback) 46 | } catch (ignored: Throwable) { 47 | fallback 48 | } finally { 49 | a.recycle() 50 | } 51 | } 52 | 53 | @SuppressLint("Recycle") 54 | @IdRes 55 | internal fun Context.resId( 56 | attrs: AttributeSet? = null, 57 | @AttrRes attrId: Int, 58 | fallback: Int = 0 59 | ): Int { 60 | val typedArray = if (attrs != null) { 61 | obtainStyledAttributes(attrs, intArrayOf(attrId)) 62 | } else { 63 | theme.obtainStyledAttributes(intArrayOf(attrId)) 64 | } 65 | try { 66 | return typedArray.getResourceId(0, fallback) 67 | } finally { 68 | typedArray.recycle() 69 | } 70 | } 71 | 72 | private var mConstructorArgsField: Field? = null 73 | 74 | /** 75 | * Gets around an issue existing before API 16, or some weird 8.1 devices. 76 | * 77 | * See https://github.com/afollestad/aesthetic/issues/101 or 78 | * https://github.com/afollestad/aesthetic/issues/113 79 | */ 80 | internal fun Context.fixedLayoutInflater(): LayoutInflater { 81 | val inflater = LayoutInflater.from(this) 82 | if (SDK_INT >= P) { 83 | // Don't apply fix to the latest Android versions. 84 | return inflater 85 | } 86 | if (mConstructorArgsField == null) { 87 | //mConstructorArgs 88 | mConstructorArgsField = LayoutInflater::class.findField("mConstructorArgs") 89 | } 90 | val constructorArgs = mConstructorArgsField!!.get(inflater) 91 | if (Array.get(constructorArgs, 0) == null) { 92 | Array.set(constructorArgs, 0, this) 93 | mConstructorArgsField!!.set(inflater, constructorArgs) 94 | } 95 | return inflater 96 | } 97 | -------------------------------------------------------------------------------- /library/src/main/java/com/afollestad/aesthetic/utils/PackageManagerExt.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Designed and developed by Aidan Follestad (@afollestad) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.afollestad.aesthetic.utils 17 | 18 | import android.annotation.TargetApi 19 | import android.content.pm.PackageManager 20 | import android.graphics.Bitmap 21 | import android.graphics.Canvas 22 | import android.graphics.drawable.AdaptiveIconDrawable 23 | import android.graphics.drawable.BitmapDrawable 24 | import android.graphics.drawable.LayerDrawable 25 | import android.os.Build.VERSION.SDK_INT 26 | import android.os.Build.VERSION_CODES.O 27 | 28 | @TargetApi(O) 29 | internal fun PackageManager.getAppIcon(packageName: String): Bitmap? { 30 | try { 31 | val drawable = getApplicationIcon(packageName) 32 | if (drawable is BitmapDrawable) { 33 | return drawable.bitmap 34 | } else if (SDK_INT >= O && drawable is AdaptiveIconDrawable) { 35 | val drr = arrayOf(drawable.background, drawable.foreground) 36 | val layerDrawable = LayerDrawable(drr) 37 | 38 | val width = layerDrawable.intrinsicWidth 39 | val height = layerDrawable.intrinsicHeight 40 | 41 | val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888) 42 | val canvas = Canvas(bitmap) 43 | 44 | layerDrawable.setBounds(0, 0, canvas.width, canvas.height) 45 | layerDrawable.draw(canvas) 46 | 47 | return bitmap 48 | } 49 | } catch (e: PackageManager.NameNotFoundException) { 50 | e.printStackTrace() 51 | } 52 | 53 | return null 54 | } 55 | -------------------------------------------------------------------------------- /library/src/main/java/com/afollestad/aesthetic/utils/PrefsExt.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Designed and developed by Aidan Follestad (@afollestad) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.afollestad.aesthetic.utils 17 | 18 | import android.content.SharedPreferences 19 | 20 | typealias PrefsEditor = SharedPreferences.Editor 21 | 22 | internal fun PrefsEditor.save(exec: PrefsEditor.() -> Unit) { 23 | this.exec() 24 | this.apply() 25 | } 26 | 27 | internal fun SharedPreferences.edit(exec: PrefsEditor.() -> Unit) { 28 | val editor = this.edit() 29 | editor.exec() 30 | editor.apply() 31 | } 32 | 33 | internal fun SharedPreferences.clear(vararg keys: String) { 34 | edit { 35 | for (key in keys) { 36 | remove(key) 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /library/src/main/java/com/afollestad/aesthetic/utils/ResourcesExt.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Designed and developed by Aidan Follestad (@afollestad) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.afollestad.aesthetic.utils 17 | 18 | import android.content.res.Resources 19 | import android.util.Log 20 | import androidx.annotation.CheckResult 21 | import com.afollestad.aesthetic.BuildConfig 22 | 23 | @CheckResult 24 | internal fun Resources.safeResourceName(resId: Int): String { 25 | if (resId == 0) { 26 | return "" 27 | } 28 | return try { 29 | getResourceName(resId) 30 | } catch (_: Resources.NotFoundException) { 31 | if (BuildConfig.DEBUG) Log.w("AttrWizard", "Unable to get resource name for $resId") 32 | "" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /library/src/main/java/com/afollestad/aesthetic/utils/RxExt.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Designed and developed by Aidan Follestad (@afollestad) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.afollestad.aesthetic.utils 17 | 18 | import android.view.View 19 | import android.widget.ImageView 20 | import android.widget.TextView 21 | import androidx.annotation.CheckResult 22 | import androidx.cardview.widget.CardView 23 | import com.afollestad.aesthetic.blowUp 24 | import io.reactivex.Observable 25 | import io.reactivex.Observable.combineLatest 26 | import io.reactivex.ObservableSource 27 | import io.reactivex.android.schedulers.AndroidSchedulers 28 | import io.reactivex.disposables.CompositeDisposable 29 | import io.reactivex.disposables.Disposable 30 | import io.reactivex.disposables.Disposables.empty 31 | import io.reactivex.exceptions.Exceptions 32 | import io.reactivex.functions.BiFunction 33 | import io.reactivex.functions.Consumer 34 | import io.reactivex.functions.Function3 35 | 36 | typealias KotlinSubscriber = (T) -> Unit 37 | 38 | internal typealias RxMapper = (T) -> ObservableSource 39 | 40 | internal fun Observable.distinctToMainThread(): Observable { 41 | return observeOn(AndroidSchedulers.mainThread()).distinctUntilChanged() 42 | } 43 | 44 | internal operator fun CompositeDisposable?.plusAssign(disposable: Disposable) { 45 | this?.add(disposable) 46 | } 47 | 48 | internal fun onErrorLogAndRethrow(): Consumer { 49 | return Consumer { throwable -> 50 | throwable.printStackTrace() 51 | throw Exceptions.propagate(throwable) 52 | } 53 | } 54 | 55 | internal fun Observable.one(): Observable { 56 | return take(1) 57 | } 58 | 59 | internal fun Observable.toMainThread(): Observable { 60 | return observeOn(AndroidSchedulers.mainThread()) 61 | } 62 | 63 | internal inline fun Observable.subscribeTo( 64 | crossinline subscriber: KotlinSubscriber 65 | ): Disposable { 66 | return this.subscribe( 67 | Consumer { subscriber(it) }, 68 | onErrorLogAndRethrow() 69 | ) 70 | } 71 | 72 | internal inline fun combine( 73 | source1: Observable, 74 | source2: Observable, 75 | crossinline combineFunction: (T1, T2) -> R 76 | ) = combineLatest(source1, source2, BiFunction { t1, t2 -> combineFunction(t1, t2) })!! 77 | 78 | internal fun combine( 79 | source1: Observable, 80 | source2: Observable 81 | ) = combineLatest(source1, source2, BiFunction> { t1, t2 -> t1 to t2 })!! 82 | 83 | inline fun combine( 84 | source1: Observable, 85 | source2: Observable, 86 | source3: Observable, 87 | crossinline combineFunction: (T1, T2, T3) -> R 88 | ) = combineLatest(source1, source2, source3, 89 | Function3 { t1: T1, t2: T2, t3: T3 -> combineFunction(t1, t2, t3) })!! 90 | 91 | fun Observable.subscribeBackgroundColor(view: View): Disposable { 92 | return subscribeTo { 93 | when (view) { 94 | is CardView -> view.setCardBackgroundColor(it) 95 | else -> view.setBackgroundColor(it) 96 | } 97 | } 98 | } 99 | 100 | fun Observable.subscribeTextColor(view: View): Disposable { 101 | if (view !is TextView) return empty() 102 | return subscribeTo(view::setTextColor) 103 | } 104 | 105 | fun Observable.subscribeHintTextColor(view: View): Disposable { 106 | if (view !is TextView) return empty() 107 | return subscribeTo(view::setHintTextColor) 108 | } 109 | 110 | fun Observable.subscribeImageViewTint(view: View): Disposable { 111 | if (view !is ImageView) return empty() 112 | return subscribeTo(view::setColorFilter) 113 | } 114 | 115 | /** 116 | * We use this to we don't get lint warnings when using flatMap. Since Observable.flatMap is 117 | * a Java function and does not have nullability annotations, it can "possibly be null" 118 | * (it really cannot be). 119 | */ 120 | @CheckResult 121 | internal fun Observable.kFlatMap(mapper: RxMapper) = flatMap(mapper) ?: blowUp() 122 | -------------------------------------------------------------------------------- /library/src/main/java/com/afollestad/aesthetic/utils/StringExt.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Designed and developed by Aidan Follestad (@afollestad) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.afollestad.aesthetic.utils 17 | 18 | import androidx.annotation.CheckResult 19 | 20 | @CheckResult 21 | internal fun String.isNumber() = all { it.isDigit() } 22 | -------------------------------------------------------------------------------- /library/src/main/java/com/afollestad/aesthetic/views/AestheticActionMenuItemView.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Designed and developed by Aidan Follestad (@afollestad) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.afollestad.aesthetic.views 17 | 18 | import android.annotation.SuppressLint 19 | import android.content.Context 20 | import android.content.res.ColorStateList 21 | import android.graphics.drawable.Drawable 22 | import android.util.AttributeSet 23 | import androidx.appcompat.view.menu.ActionMenuItemView 24 | import com.afollestad.aesthetic.Aesthetic.Companion.get 25 | import com.afollestad.aesthetic.utils.adjustAlpha 26 | import com.afollestad.aesthetic.utils.one 27 | import com.afollestad.aesthetic.utils.subscribeTo 28 | import com.afollestad.aesthetic.utils.tint 29 | import com.afollestad.aesthetic.utils.toMainThread 30 | import com.afollestad.aesthetic.utils.unsubscribeOnDetach 31 | 32 | /** @author Aidan Follestad (afollestad) */ 33 | @SuppressLint("RestrictedApi") 34 | internal class AestheticActionMenuItemView( 35 | context: Context, 36 | attrs: AttributeSet? = null 37 | ) : ActionMenuItemView(context, attrs) { 38 | 39 | companion object { 40 | const val UNFOCUSED_ALPHA = 0.5f 41 | } 42 | 43 | private var icon: Drawable? = null 44 | 45 | private fun invalidateColors(color: Int) { 46 | val sl = ColorStateList( 47 | arrayOf( 48 | intArrayOf(-android.R.attr.state_selected), 49 | intArrayOf(android.R.attr.state_selected) 50 | ), 51 | intArrayOf( 52 | color.adjustAlpha(UNFOCUSED_ALPHA), 53 | color 54 | ) 55 | ) 56 | if (icon != null) { 57 | setIcon(icon!!, sl) 58 | } 59 | setTextColor(color) 60 | } 61 | 62 | override fun setIcon(icon: Drawable) { 63 | super.setIcon(icon) 64 | // We need to retrieve the color again here. 65 | // For some reason, without this, a transparent color is used and the icon disappears 66 | // when the overflow menu opens. 67 | get().toolbarIconColor() 68 | .one() 69 | .toMainThread() 70 | .subscribeTo(::invalidateColors) 71 | .unsubscribeOnDetach(this) 72 | } 73 | 74 | @Suppress("MemberVisibilityCanBePrivate") 75 | fun setIcon( 76 | icon: Drawable, 77 | colors: ColorStateList 78 | ) { 79 | this.icon = icon 80 | super.setIcon(icon.tint(colors)) 81 | } 82 | 83 | override fun onAttachedToWindow() { 84 | super.onAttachedToWindow() 85 | get().toolbarIconColor() 86 | .one() 87 | .toMainThread() 88 | .subscribeTo(::invalidateColors) 89 | .unsubscribeOnDetach(this) 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /library/src/main/java/com/afollestad/aesthetic/views/AestheticBorderlessButton.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Designed and developed by Aidan Follestad (@afollestad) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.afollestad.aesthetic.views 17 | 18 | import android.content.Context 19 | import android.content.res.ColorStateList 20 | import android.util.AttributeSet 21 | import androidx.appcompat.widget.AppCompatButton 22 | import com.afollestad.aesthetic.Aesthetic.Companion.get 23 | import com.afollestad.aesthetic.utils.adjustAlpha 24 | import com.afollestad.aesthetic.utils.distinctToMainThread 25 | import com.afollestad.aesthetic.utils.subscribeTo 26 | import com.afollestad.aesthetic.utils.unsubscribeOnDetach 27 | 28 | /** @author Aidan Follestad (afollestad) */ 29 | class AestheticBorderlessButton( 30 | context: Context, 31 | attrs: AttributeSet? = null 32 | ) : AppCompatButton(context, attrs) { 33 | 34 | private fun invalidateColors(accentColor: Int) { 35 | val textColorSl = ColorStateList( 36 | arrayOf( 37 | intArrayOf(android.R.attr.state_enabled), 38 | intArrayOf(-android.R.attr.state_enabled) 39 | ), 40 | intArrayOf( 41 | accentColor, 42 | accentColor.adjustAlpha(0.56f) 43 | ) 44 | ) 45 | setTextColor(textColorSl) 46 | 47 | // Hack around button color not updating 48 | isEnabled = !isEnabled 49 | isEnabled = !isEnabled 50 | } 51 | 52 | override fun onAttachedToWindow() { 53 | super.onAttachedToWindow() 54 | get().colorAccent() 55 | .distinctToMainThread() 56 | .subscribeTo(::invalidateColors) 57 | .unsubscribeOnDetach(this) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /library/src/main/java/com/afollestad/aesthetic/views/AestheticBottomAppBar.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Designed and developed by Aidan Follestad (@afollestad) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.afollestad.aesthetic.views 17 | 18 | import android.content.Context 19 | import android.content.res.ColorStateList 20 | import android.graphics.drawable.Drawable 21 | import android.util.AttributeSet 22 | import androidx.annotation.ColorInt 23 | import com.afollestad.aesthetic.Aesthetic.Companion.get 24 | import com.afollestad.aesthetic.R 25 | import com.afollestad.aesthetic.internal.AttrWizard 26 | import com.afollestad.aesthetic.utils.darkenColor 27 | import com.afollestad.aesthetic.utils.observableForAttrName 28 | import com.afollestad.aesthetic.utils.setOverflowButtonColor 29 | import com.afollestad.aesthetic.utils.subscribeTo 30 | import com.afollestad.aesthetic.utils.tint 31 | import com.afollestad.aesthetic.utils.tintMenu 32 | import com.afollestad.aesthetic.utils.unsubscribeOnDetach 33 | import com.google.android.material.bottomappbar.BottomAppBar 34 | 35 | /** @author Aidan Follestad (afollestad) */ 36 | class AestheticBottomAppBar( 37 | context: Context, 38 | attrs: AttributeSet? = null 39 | ) : BottomAppBar(context, attrs) { 40 | 41 | private var menuIconColor: Int? = null 42 | 43 | private val wizard = AttrWizard(context, attrs) 44 | private val backgroundColorValue = wizard.getRawValue(android.R.attr.background) 45 | private val titleTextColorValue = wizard.getRawValue(R.attr.titleTextColor) 46 | private val subtitleTextColorValue = wizard.getRawValue(R.attr.subtitleTextColor) 47 | 48 | override fun setNavigationIcon(icon: Drawable?) { 49 | if (menuIconColor == null) { 50 | super.setNavigationIcon(icon) 51 | return 52 | } 53 | super.setNavigationIcon(icon.tint(menuIconColor!!)) 54 | } 55 | 56 | fun setNavigationIcon(icon: Drawable?, @ColorInt color: Int) { 57 | if (menuIconColor == null) { 58 | super.setNavigationIcon(icon) 59 | return 60 | } 61 | super.setNavigationIcon(icon.tint(color)) 62 | } 63 | 64 | override fun onAttachedToWindow() { 65 | super.onAttachedToWindow() 66 | 67 | get().observableForAttrName( 68 | backgroundColorValue, 69 | get().colorPrimary() 70 | ) 71 | ?.distinctUntilChanged() 72 | ?.subscribeTo { backgroundTint = ColorStateList.valueOf(it) } 73 | ?.unsubscribeOnDetach(this) 74 | 75 | get().toolbarIconColor() 76 | .distinctUntilChanged() 77 | .subscribeTo(::invalidateColors) 78 | .unsubscribeOnDetach(this) 79 | 80 | get().observableForAttrName( 81 | titleTextColorValue, 82 | get().toolbarTitleColor() 83 | ) 84 | ?.distinctUntilChanged() 85 | ?.subscribeTo(::setTitleTextColor) 86 | ?.unsubscribeOnDetach(this) 87 | 88 | get().observableForAttrName( 89 | subtitleTextColorValue, 90 | get().toolbarSubtitleColor() 91 | ) 92 | ?.distinctUntilChanged() 93 | ?.subscribeTo(::setSubtitleTextColor) 94 | ?.unsubscribeOnDetach(this) 95 | } 96 | 97 | private fun invalidateColors(color: Int) { 98 | this.menuIconColor = color 99 | setOverflowButtonColor(color) 100 | tintMenu(menu, color, color.darkenColor()) 101 | if (navigationIcon != null) { 102 | this.navigationIcon = navigationIcon 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /library/src/main/java/com/afollestad/aesthetic/views/AestheticButton.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Designed and developed by Aidan Follestad (@afollestad) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.afollestad.aesthetic.views 17 | 18 | import android.content.Context 19 | import android.content.res.ColorStateList 20 | import android.graphics.Color.BLACK 21 | import android.graphics.Color.WHITE 22 | import android.util.AttributeSet 23 | import androidx.appcompat.widget.AppCompatButton 24 | import com.afollestad.aesthetic.Aesthetic.Companion.get 25 | import com.afollestad.aesthetic.ColorIsDarkState 26 | import com.afollestad.aesthetic.internal.AttrWizard 27 | import com.afollestad.aesthetic.utils.combine 28 | import com.afollestad.aesthetic.utils.distinctToMainThread 29 | import com.afollestad.aesthetic.utils.isColorLight 30 | import com.afollestad.aesthetic.utils.observableForAttrName 31 | import com.afollestad.aesthetic.utils.setTintAuto 32 | import com.afollestad.aesthetic.utils.subscribeTo 33 | import com.afollestad.aesthetic.utils.unsubscribeOnDetach 34 | 35 | /** @author Aidan Follestad (afollestad) */ 36 | class AestheticButton( 37 | context: Context, 38 | attrs: AttributeSet? = null 39 | ) : AppCompatButton(context, attrs) { 40 | 41 | private val wizard = AttrWizard(context, attrs) 42 | private val backgroundColorValue = wizard.getRawValue(android.R.attr.background) 43 | 44 | private fun invalidateColors(state: ColorIsDarkState) { 45 | setTintAuto(state.color, true, state.isDark) 46 | val textColorSl = ColorStateList( 47 | arrayOf( 48 | intArrayOf(android.R.attr.state_enabled), 49 | intArrayOf(-android.R.attr.state_enabled) 50 | ), 51 | intArrayOf( 52 | if (state.color.isColorLight()) BLACK else WHITE, 53 | if (state.isDark) WHITE else BLACK 54 | ) 55 | ) 56 | setTextColor(textColorSl) 57 | 58 | // Hack around button color not updating 59 | isEnabled = !isEnabled 60 | isEnabled = !isEnabled 61 | } 62 | 63 | override fun onAttachedToWindow() { 64 | super.onAttachedToWindow() 65 | combine( 66 | get().observableForAttrName(backgroundColorValue, get().colorAccent())!!, 67 | get().isDark 68 | ) { color, isDark -> ColorIsDarkState(color, isDark) } 69 | .distinctToMainThread() 70 | .subscribeTo(::invalidateColors) 71 | .unsubscribeOnDetach(this) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /library/src/main/java/com/afollestad/aesthetic/views/AestheticCardView.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Designed and developed by Aidan Follestad (@afollestad) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.afollestad.aesthetic.views 17 | 18 | import android.annotation.SuppressLint 19 | import android.content.Context 20 | import android.util.AttributeSet 21 | import androidx.cardview.widget.CardView 22 | import com.afollestad.aesthetic.Aesthetic.Companion.get 23 | import com.afollestad.aesthetic.R 24 | import com.afollestad.aesthetic.internal.AttrWizard 25 | import com.afollestad.aesthetic.utils.distinctToMainThread 26 | import com.afollestad.aesthetic.utils.observableForAttrName 27 | import com.afollestad.aesthetic.utils.subscribeBackgroundColor 28 | import com.afollestad.aesthetic.utils.unsubscribeOnDetach 29 | 30 | /** @author Aidan Follestad (afollestad) */ 31 | @SuppressLint("PrivateResource") 32 | class AestheticCardView( 33 | context: Context, 34 | attrs: AttributeSet? = null 35 | ) : CardView(context, attrs) { 36 | 37 | private val wizard = AttrWizard(context, attrs) 38 | private val backgroundColorValue = wizard.getRawValue(R.attr.cardBackgroundColor) 39 | 40 | override fun onAttachedToWindow() { 41 | super.onAttachedToWindow() 42 | 43 | get().observableForAttrName( 44 | backgroundColorValue, 45 | get().colorCardViewBackground() 46 | )!! 47 | .distinctToMainThread() 48 | .subscribeBackgroundColor(this) 49 | .unsubscribeOnDetach(this) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /library/src/main/java/com/afollestad/aesthetic/views/AestheticCheckBox.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Designed and developed by Aidan Follestad (@afollestad) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.afollestad.aesthetic.views 17 | 18 | import android.content.Context 19 | import android.util.AttributeSet 20 | import androidx.appcompat.widget.AppCompatCheckBox 21 | import com.afollestad.aesthetic.Aesthetic.Companion.get 22 | import com.afollestad.aesthetic.ColorIsDarkState 23 | import com.afollestad.aesthetic.internal.AttrWizard 24 | import com.afollestad.aesthetic.utils.combine 25 | import com.afollestad.aesthetic.utils.distinctToMainThread 26 | import com.afollestad.aesthetic.utils.observableForAttrName 27 | import com.afollestad.aesthetic.utils.setTint 28 | import com.afollestad.aesthetic.utils.subscribeTextColor 29 | import com.afollestad.aesthetic.utils.subscribeTo 30 | import com.afollestad.aesthetic.utils.unsubscribeOnDetach 31 | 32 | /** @author Aidan Follestad (afollestad) */ 33 | class AestheticCheckBox( 34 | context: Context, 35 | attrs: AttributeSet? = null 36 | ) : AppCompatCheckBox(context, attrs) { 37 | 38 | private val wizard = AttrWizard(context, attrs) 39 | private val backgroundColorValue = wizard.getRawValue(android.R.attr.background) 40 | 41 | private fun invalidateColors(state: ColorIsDarkState) = setTint(state.color, state.isDark) 42 | 43 | override fun onAttachedToWindow() { 44 | super.onAttachedToWindow() 45 | 46 | combine( 47 | get().observableForAttrName( 48 | backgroundColorValue, 49 | get().colorAccent() 50 | )!!, 51 | get().isDark 52 | ) { color, isDark -> ColorIsDarkState(color, isDark) } 53 | .distinctToMainThread() 54 | .subscribeTo(::invalidateColors) 55 | .unsubscribeOnDetach(this) 56 | 57 | get().textColorPrimary() 58 | .distinctToMainThread() 59 | .subscribeTextColor(this) 60 | .unsubscribeOnDetach(this) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /library/src/main/java/com/afollestad/aesthetic/views/AestheticCheckedTextView.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Designed and developed by Aidan Follestad (@afollestad) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.afollestad.aesthetic.views 17 | 18 | import android.content.Context 19 | import android.util.AttributeSet 20 | import androidx.appcompat.widget.AppCompatCheckedTextView 21 | import com.afollestad.aesthetic.Aesthetic.Companion.get 22 | import com.afollestad.aesthetic.ColorIsDarkState 23 | import com.afollestad.aesthetic.internal.AttrWizard 24 | import com.afollestad.aesthetic.utils.combine 25 | import com.afollestad.aesthetic.utils.distinctToMainThread 26 | import com.afollestad.aesthetic.utils.observableForAttrName 27 | import com.afollestad.aesthetic.utils.setTint 28 | import com.afollestad.aesthetic.utils.subscribeTextColor 29 | import com.afollestad.aesthetic.utils.subscribeTo 30 | import com.afollestad.aesthetic.utils.unsubscribeOnDetach 31 | 32 | /** @author Aidan Follestad (afollestad) */ 33 | class AestheticCheckedTextView( 34 | context: Context, 35 | attrs: AttributeSet? = null 36 | ) : AppCompatCheckedTextView(context, attrs) { 37 | 38 | private val wizard = AttrWizard(context, attrs) 39 | private val backgroundColorValue = wizard.getRawValue(android.R.attr.background) 40 | 41 | private fun invalidateColors(state: ColorIsDarkState) = setTint(state.color, state.isDark) 42 | 43 | override fun onAttachedToWindow() { 44 | super.onAttachedToWindow() 45 | 46 | combine( 47 | get().observableForAttrName( 48 | backgroundColorValue, 49 | get().colorAccent() 50 | )!!, 51 | get().isDark 52 | ) { color, isDark -> ColorIsDarkState(color, isDark) } 53 | .distinctToMainThread() 54 | .subscribeTo(::invalidateColors) 55 | .unsubscribeOnDetach(this) 56 | 57 | get().textColorPrimary() 58 | .distinctToMainThread() 59 | .subscribeTextColor(this) 60 | .unsubscribeOnDetach(this) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /library/src/main/java/com/afollestad/aesthetic/views/AestheticDialogButton.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Designed and developed by Aidan Follestad (@afollestad) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.afollestad.aesthetic.views 17 | 18 | import android.content.Context 19 | import android.util.AttributeSet 20 | import androidx.appcompat.widget.AppCompatButton 21 | import com.afollestad.aesthetic.Aesthetic.Companion.get 22 | import com.afollestad.aesthetic.utils.distinctToMainThread 23 | import com.afollestad.aesthetic.utils.subscribeTextColor 24 | import com.afollestad.aesthetic.utils.unsubscribeOnDetach 25 | 26 | /** @author Aidan Follestad (afollestad) */ 27 | internal class AestheticDialogButton( 28 | context: Context, 29 | attrs: AttributeSet? = null 30 | ) : AppCompatButton(context, attrs) { 31 | 32 | override fun onAttachedToWindow() { 33 | super.onAttachedToWindow() 34 | get().colorAccent() 35 | .distinctToMainThread() 36 | .subscribeTextColor(this) 37 | .unsubscribeOnDetach(this) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /library/src/main/java/com/afollestad/aesthetic/views/AestheticDrawerLayout.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Designed and developed by Aidan Follestad (@afollestad) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.afollestad.aesthetic.views 17 | 18 | import android.content.Context 19 | import android.util.AttributeSet 20 | import androidx.appcompat.app.ActionBarDrawerToggle 21 | import androidx.appcompat.graphics.drawable.DrawerArrowDrawable 22 | import androidx.drawerlayout.widget.DrawerLayout 23 | import com.afollestad.aesthetic.Aesthetic.Companion.get 24 | import com.afollestad.aesthetic.utils.distinctToMainThread 25 | import com.afollestad.aesthetic.utils.subscribeTo 26 | import com.afollestad.aesthetic.utils.unsubscribeOnDetach 27 | 28 | /** @author Aidan Follestad (afollestad) */ 29 | class AestheticDrawerLayout( 30 | context: Context, 31 | attrs: AttributeSet? = null 32 | ) : DrawerLayout(context, attrs) { 33 | 34 | private var lastColor: Int? = null 35 | private var arrowDrawable: DrawerArrowDrawable? = null 36 | 37 | private fun invalidateColor(color: Int?) { 38 | if (color == null) { 39 | return 40 | } 41 | this.lastColor = color 42 | this.arrowDrawable?.color = color 43 | } 44 | 45 | override fun onAttachedToWindow() { 46 | super.onAttachedToWindow() 47 | get().toolbarIconColor() 48 | .distinctToMainThread() 49 | .subscribeTo(::invalidateColor) 50 | .unsubscribeOnDetach(this) 51 | } 52 | 53 | override fun addDrawerListener(listener: DrawerLayout.DrawerListener) { 54 | super.addDrawerListener(listener) 55 | if (listener is ActionBarDrawerToggle) { 56 | this.arrowDrawable = listener.drawerArrowDrawable 57 | } 58 | invalidateColor(lastColor) 59 | } 60 | 61 | @Suppress("OverridingDeprecatedMember", "DEPRECATION") 62 | override fun setDrawerListener(listener: DrawerLayout.DrawerListener) { 63 | super.setDrawerListener(listener) 64 | if (listener is ActionBarDrawerToggle) { 65 | this.arrowDrawable = listener.drawerArrowDrawable 66 | } 67 | invalidateColor(lastColor) 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /library/src/main/java/com/afollestad/aesthetic/views/AestheticEditText.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Designed and developed by Aidan Follestad (@afollestad) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.afollestad.aesthetic.views 17 | 18 | import android.annotation.SuppressLint 19 | import android.content.Context 20 | import android.util.AttributeSet 21 | import androidx.appcompat.widget.AppCompatEditText 22 | import com.afollestad.aesthetic.Aesthetic.Companion.get 23 | import com.afollestad.aesthetic.ColorIsDarkState 24 | import com.afollestad.aesthetic.R 25 | import com.afollestad.aesthetic.internal.AttrWizard 26 | import com.afollestad.aesthetic.utils.combine 27 | import com.afollestad.aesthetic.utils.distinctToMainThread 28 | import com.afollestad.aesthetic.utils.observableForAttrName 29 | import com.afollestad.aesthetic.utils.setTintAuto 30 | import com.afollestad.aesthetic.utils.subscribeHintTextColor 31 | import com.afollestad.aesthetic.utils.subscribeTextColor 32 | import com.afollestad.aesthetic.utils.subscribeTo 33 | import com.afollestad.aesthetic.utils.unsubscribeOnDetach 34 | 35 | /** @author Aidan Follestad (afollestad) */ 36 | @SuppressLint("ResourceType") 37 | class AestheticEditText( 38 | context: Context, 39 | attrs: AttributeSet? = null 40 | ) : AppCompatEditText(context, attrs) { 41 | 42 | private val wizard = AttrWizard(context, attrs) 43 | private val tintColorValue = wizard.getRawValue(R.attr.tint) 44 | private val textColorValue = wizard.getRawValue(android.R.attr.textColor) 45 | private val textColorHintValue = wizard.getRawValue(android.R.attr.textColorHint) 46 | 47 | private fun invalidateColors(state: ColorIsDarkState) = 48 | setTintAuto(state.color, true, state.isDark) 49 | 50 | override fun onAttachedToWindow() { 51 | super.onAttachedToWindow() 52 | 53 | combine( 54 | get().observableForAttrName( 55 | tintColorValue, 56 | get().colorAccent() 57 | )!!, 58 | get().isDark 59 | ) { color, isDark -> ColorIsDarkState(color, isDark) } 60 | .distinctToMainThread() 61 | .subscribeTo(::invalidateColors) 62 | .unsubscribeOnDetach(this) 63 | 64 | get().observableForAttrName( 65 | textColorValue, 66 | get().textColorPrimary() 67 | )!! 68 | .distinctToMainThread() 69 | .subscribeTextColor(this) 70 | .unsubscribeOnDetach(this) 71 | 72 | get().observableForAttrName( 73 | textColorHintValue, 74 | get().textColorSecondary() 75 | )!! 76 | .distinctToMainThread() 77 | .subscribeHintTextColor(this) 78 | .unsubscribeOnDetach(this) 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /library/src/main/java/com/afollestad/aesthetic/views/AestheticFab.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Designed and developed by Aidan Follestad (@afollestad) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.afollestad.aesthetic.views 17 | 18 | import android.content.Context 19 | import android.graphics.Color.BLACK 20 | import android.graphics.Color.WHITE 21 | import android.graphics.drawable.Drawable 22 | import android.util.AttributeSet 23 | import com.afollestad.aesthetic.Aesthetic.Companion.get 24 | import com.afollestad.aesthetic.ColorIsDarkState 25 | import com.afollestad.aesthetic.internal.AttrWizard 26 | import com.afollestad.aesthetic.utils.combine 27 | import com.afollestad.aesthetic.utils.distinctToMainThread 28 | import com.afollestad.aesthetic.utils.isColorLight 29 | import com.afollestad.aesthetic.utils.observableForAttrName 30 | import com.afollestad.aesthetic.utils.setTintAuto 31 | import com.afollestad.aesthetic.utils.subscribeTo 32 | import com.afollestad.aesthetic.utils.tint 33 | import com.afollestad.aesthetic.utils.unsubscribeOnDetach 34 | import com.google.android.material.floatingactionbutton.FloatingActionButton 35 | 36 | /** @author Aidan Follestad (afollestad) */ 37 | class AestheticFab( 38 | context: Context, 39 | attrs: AttributeSet? = null 40 | ) : FloatingActionButton(context, attrs) { 41 | 42 | private val wizard = AttrWizard(context, attrs) 43 | private val backgroundColorValue = wizard.getRawValue(android.R.attr.background) 44 | private var iconColor: Int = 0 45 | 46 | private fun invalidateColors(state: ColorIsDarkState) { 47 | setTintAuto(state.color, true, state.isDark) 48 | iconColor = if (state.color.isColorLight()) BLACK else WHITE 49 | setImageDrawable(drawable) 50 | } 51 | 52 | override fun setImageDrawable(drawable: Drawable?) = 53 | super.setImageDrawable(drawable?.tint(iconColor)) 54 | 55 | override fun onAttachedToWindow() { 56 | super.onAttachedToWindow() 57 | 58 | combine( 59 | get().observableForAttrName( 60 | backgroundColorValue, 61 | get().colorAccent() 62 | )!!, 63 | get().isDark 64 | ) { color, isDark -> ColorIsDarkState(color, isDark) } 65 | .distinctToMainThread() 66 | .subscribeTo(::invalidateColors) 67 | .unsubscribeOnDetach(this) 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /library/src/main/java/com/afollestad/aesthetic/views/AestheticListView.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Designed and developed by Aidan Follestad (@afollestad) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.afollestad.aesthetic.views 17 | 18 | import android.content.Context 19 | import android.util.AttributeSet 20 | import android.widget.ListView 21 | import com.afollestad.aesthetic.Aesthetic.Companion.get 22 | import com.afollestad.aesthetic.utils.EdgeGlowUtil.setEdgeGlowColor 23 | import com.afollestad.aesthetic.utils.distinctToMainThread 24 | import com.afollestad.aesthetic.utils.subscribeTo 25 | import com.afollestad.aesthetic.utils.unsubscribeOnDetach 26 | 27 | /** @author Aidan Follestad (afollestad) */ 28 | class AestheticListView( 29 | context: Context, 30 | attrs: AttributeSet? = null 31 | ) : ListView(context, attrs) { 32 | 33 | private fun invalidateColors(color: Int) = 34 | setEdgeGlowColor(this, color) 35 | 36 | override fun onAttachedToWindow() { 37 | super.onAttachedToWindow() 38 | get().colorAccent() 39 | .distinctToMainThread() 40 | .subscribeTo(::invalidateColors) 41 | .unsubscribeOnDetach(this) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /library/src/main/java/com/afollestad/aesthetic/views/AestheticNavigationView.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Designed and developed by Aidan Follestad (@afollestad) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.afollestad.aesthetic.views 17 | 18 | import android.annotation.SuppressLint 19 | import android.content.Context 20 | import android.content.res.ColorStateList 21 | import android.graphics.Color 22 | import android.graphics.drawable.ColorDrawable 23 | import android.graphics.drawable.StateListDrawable 24 | import android.util.AttributeSet 25 | import com.afollestad.aesthetic.Aesthetic.Companion.get 26 | import com.afollestad.aesthetic.ColorIsDarkState 27 | import com.afollestad.aesthetic.NavigationViewMode.NONE 28 | import com.afollestad.aesthetic.NavigationViewMode.SELECTED_ACCENT 29 | import com.afollestad.aesthetic.NavigationViewMode.SELECTED_PRIMARY 30 | import com.afollestad.aesthetic.R 31 | import com.afollestad.aesthetic.utils.adjustAlpha 32 | import com.afollestad.aesthetic.utils.color 33 | import com.afollestad.aesthetic.utils.combine 34 | import com.afollestad.aesthetic.utils.distinctToMainThread 35 | import com.afollestad.aesthetic.utils.subscribeTo 36 | import com.afollestad.aesthetic.utils.unsubscribeOnDetach 37 | import com.google.android.material.navigation.NavigationView 38 | import io.reactivex.Observable.empty 39 | import io.reactivex.disposables.Disposable 40 | 41 | /** @author Aidan Follestad (afollestad) */ 42 | @SuppressLint("RestrictedApi") 43 | class AestheticNavigationView( 44 | context: Context, 45 | attrs: AttributeSet? = null 46 | ) : NavigationView(context, attrs) { 47 | 48 | private var colorSubscription: Disposable? = null 49 | 50 | private fun invalidateColors(state: ColorIsDarkState) { 51 | val selectedColor = state.color 52 | val isDark = state.isDark 53 | val baseColor = if (isDark) Color.WHITE else Color.BLACK 54 | val unselectedIconColor = baseColor.adjustAlpha(.54f) 55 | val unselectedTextColor = baseColor.adjustAlpha(.87f) 56 | 57 | val selectedItemBgColor = context.color( 58 | if (isDark) R.color.ate_navigation_drawer_selected_dark 59 | else R.color.ate_navigation_drawer_selected_light 60 | ) 61 | 62 | val iconSl = ColorStateList( 63 | arrayOf( 64 | intArrayOf(-android.R.attr.state_checked), 65 | intArrayOf(android.R.attr.state_checked) 66 | ), 67 | intArrayOf(unselectedIconColor, selectedColor) 68 | ) 69 | val textSl = ColorStateList( 70 | arrayOf( 71 | intArrayOf(-android.R.attr.state_checked), 72 | intArrayOf(android.R.attr.state_checked) 73 | ), 74 | intArrayOf(unselectedTextColor, selectedColor) 75 | ) 76 | 77 | itemTextColor = textSl 78 | itemIconTintList = iconSl 79 | 80 | val bgDrawable = StateListDrawable() 81 | bgDrawable.addState( 82 | intArrayOf(android.R.attr.state_checked), 83 | ColorDrawable(selectedItemBgColor) 84 | ) 85 | itemBackground = bgDrawable 86 | } 87 | 88 | override fun onAttachedToWindow() { 89 | super.onAttachedToWindow() 90 | get().navigationViewMode() 91 | .distinctToMainThread() 92 | .flatMap { 93 | when (it) { 94 | SELECTED_PRIMARY -> combine( 95 | get().colorPrimary(), 96 | get().isDark 97 | ) { color, isDark -> ColorIsDarkState(color, isDark) } 98 | 99 | SELECTED_ACCENT -> combine( 100 | get().colorAccent(), 101 | get().isDark 102 | ) { color, isDark -> ColorIsDarkState(color, isDark) } 103 | 104 | NONE -> empty() 105 | } 106 | } 107 | .distinctUntilChanged() 108 | .subscribeTo(::invalidateColors) 109 | .unsubscribeOnDetach(this) 110 | } 111 | 112 | override fun onDetachedFromWindow() { 113 | colorSubscription?.dispose() 114 | super.onDetachedFromWindow() 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /library/src/main/java/com/afollestad/aesthetic/views/AestheticNestedScrollView.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Designed and developed by Aidan Follestad (@afollestad) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.afollestad.aesthetic.views 17 | 18 | import android.content.Context 19 | import android.util.AttributeSet 20 | import androidx.core.widget.NestedScrollView 21 | import com.afollestad.aesthetic.Aesthetic.Companion.get 22 | import com.afollestad.aesthetic.utils.EdgeGlowUtil.setEdgeGlowColor 23 | import com.afollestad.aesthetic.utils.distinctToMainThread 24 | import com.afollestad.aesthetic.utils.subscribeTo 25 | import com.afollestad.aesthetic.utils.unsubscribeOnDetach 26 | 27 | /** @author Aidan Follestad (afollestad) */ 28 | class AestheticNestedScrollView( 29 | context: Context, 30 | attrs: AttributeSet? = null 31 | ) : NestedScrollView(context, attrs) { 32 | 33 | private fun invalidateColors(color: Int) = 34 | setEdgeGlowColor(this, color) 35 | 36 | override fun onAttachedToWindow() { 37 | super.onAttachedToWindow() 38 | get().colorAccent() 39 | .distinctToMainThread() 40 | .subscribeTo(::invalidateColors) 41 | .unsubscribeOnDetach(this) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /library/src/main/java/com/afollestad/aesthetic/views/AestheticProgressBar.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Designed and developed by Aidan Follestad (@afollestad) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.afollestad.aesthetic.views 17 | 18 | import android.content.Context 19 | import android.util.AttributeSet 20 | import android.widget.ProgressBar 21 | import com.afollestad.aesthetic.Aesthetic.Companion.get 22 | import com.afollestad.aesthetic.utils.distinctToMainThread 23 | import com.afollestad.aesthetic.utils.setTint 24 | import com.afollestad.aesthetic.utils.subscribeTo 25 | import com.afollestad.aesthetic.utils.unsubscribeOnDetach 26 | 27 | /** @author Aidan Follestad (afollestad) */ 28 | class AestheticProgressBar( 29 | context: Context?, 30 | attrs: AttributeSet? = null 31 | ) : ProgressBar(context, attrs) { 32 | 33 | private fun invalidateColors(color: Int) = setTint(color) 34 | 35 | override fun onAttachedToWindow() { 36 | super.onAttachedToWindow() 37 | get().colorAccent() 38 | .distinctToMainThread() 39 | .subscribeTo(::invalidateColors) 40 | .unsubscribeOnDetach(this) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /library/src/main/java/com/afollestad/aesthetic/views/AestheticRadioButton.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Designed and developed by Aidan Follestad (@afollestad) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.afollestad.aesthetic.views 17 | 18 | import android.content.Context 19 | import android.util.AttributeSet 20 | import androidx.appcompat.widget.AppCompatRadioButton 21 | import com.afollestad.aesthetic.Aesthetic.Companion.get 22 | import com.afollestad.aesthetic.ColorIsDarkState 23 | import com.afollestad.aesthetic.internal.AttrWizard 24 | import com.afollestad.aesthetic.utils.combine 25 | import com.afollestad.aesthetic.utils.distinctToMainThread 26 | import com.afollestad.aesthetic.utils.observableForAttrName 27 | import com.afollestad.aesthetic.utils.setTint 28 | import com.afollestad.aesthetic.utils.subscribeTextColor 29 | import com.afollestad.aesthetic.utils.subscribeTo 30 | import com.afollestad.aesthetic.utils.unsubscribeOnDetach 31 | 32 | /** @author Aidan Follestad (afollestad) */ 33 | class AestheticRadioButton( 34 | context: Context, 35 | attrs: AttributeSet? = null 36 | ) : AppCompatRadioButton(context, attrs) { 37 | 38 | private val wizard = AttrWizard(context, attrs) 39 | private val backgroundColorValue = wizard.getRawValue(android.R.attr.background) 40 | 41 | private fun invalidateColors(state: ColorIsDarkState) = setTint(state.color, state.isDark) 42 | 43 | override fun onAttachedToWindow() { 44 | super.onAttachedToWindow() 45 | 46 | combine( 47 | get().observableForAttrName( 48 | backgroundColorValue, 49 | get().colorAccent() 50 | )!!, 51 | get().isDark 52 | ) { color, isDark -> ColorIsDarkState(color, isDark) } 53 | .distinctToMainThread() 54 | .subscribeTo(::invalidateColors) 55 | .unsubscribeOnDetach(this) 56 | 57 | get().textColorPrimary() 58 | .distinctToMainThread() 59 | .subscribeTextColor(this) 60 | .unsubscribeOnDetach(this) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /library/src/main/java/com/afollestad/aesthetic/views/AestheticRatingBar.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Designed and developed by Aidan Follestad (@afollestad) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.afollestad.aesthetic.views 17 | 18 | import android.content.Context 19 | import android.util.AttributeSet 20 | import androidx.appcompat.widget.AppCompatRatingBar 21 | import com.afollestad.aesthetic.Aesthetic.Companion.get 22 | import com.afollestad.aesthetic.ColorIsDarkState 23 | import com.afollestad.aesthetic.internal.AttrWizard 24 | import com.afollestad.aesthetic.utils.combine 25 | import com.afollestad.aesthetic.utils.distinctToMainThread 26 | import com.afollestad.aesthetic.utils.observableForAttrName 27 | import com.afollestad.aesthetic.utils.setTint 28 | import com.afollestad.aesthetic.utils.subscribeTo 29 | import com.afollestad.aesthetic.utils.unsubscribeOnDetach 30 | 31 | /** @author Aidan Follestad (afollestad) */ 32 | class AestheticRatingBar( 33 | context: Context, 34 | attrs: AttributeSet? = null 35 | ) : AppCompatRatingBar(context, attrs) { 36 | 37 | private val wizard = AttrWizard(context, attrs) 38 | private val backgroundColorValue = wizard.getRawValue(android.R.attr.background) 39 | 40 | private fun invalidateColors(state: ColorIsDarkState) = setTint(state.color, state.isDark) 41 | 42 | override fun onAttachedToWindow() { 43 | super.onAttachedToWindow() 44 | 45 | combine( 46 | get().observableForAttrName( 47 | backgroundColorValue, 48 | get().colorAccent() 49 | )!!, 50 | get().isDark 51 | ) { color, isDark -> ColorIsDarkState(color, isDark) } 52 | .distinctToMainThread() 53 | .subscribeTo(::invalidateColors) 54 | .unsubscribeOnDetach(this) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /library/src/main/java/com/afollestad/aesthetic/views/AestheticRecyclerView.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Designed and developed by Aidan Follestad (@afollestad) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.afollestad.aesthetic.views 17 | 18 | import android.content.Context 19 | import android.util.AttributeSet 20 | import androidx.recyclerview.widget.RecyclerView 21 | import com.afollestad.aesthetic.Aesthetic.Companion.get 22 | import com.afollestad.aesthetic.utils.EdgeGlowUtil.setEdgeGlowColor 23 | import com.afollestad.aesthetic.utils.distinctToMainThread 24 | import com.afollestad.aesthetic.utils.subscribeTo 25 | import com.afollestad.aesthetic.utils.unsubscribeOnDetach 26 | 27 | /** @author Aidan Follestad (afollestad) */ 28 | class AestheticRecyclerView( 29 | context: Context, 30 | attrs: AttributeSet? = null 31 | ) : RecyclerView(context, attrs) { 32 | 33 | private fun invalidateColors(color: Int) = 34 | setEdgeGlowColor(this, color, null) 35 | 36 | override fun onAttachedToWindow() { 37 | super.onAttachedToWindow() 38 | get().colorAccent() 39 | .distinctToMainThread() 40 | .subscribeTo(::invalidateColors) 41 | .unsubscribeOnDetach(this) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /library/src/main/java/com/afollestad/aesthetic/views/AestheticScrollView.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Designed and developed by Aidan Follestad (@afollestad) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.afollestad.aesthetic.views 17 | 18 | import android.content.Context 19 | import android.util.AttributeSet 20 | import android.widget.ScrollView 21 | import com.afollestad.aesthetic.Aesthetic.Companion.get 22 | import com.afollestad.aesthetic.utils.EdgeGlowUtil.setEdgeGlowColor 23 | import com.afollestad.aesthetic.utils.distinctToMainThread 24 | import com.afollestad.aesthetic.utils.subscribeTo 25 | import com.afollestad.aesthetic.utils.unsubscribeOnDetach 26 | 27 | /** @author Aidan Follestad (afollestad) */ 28 | class AestheticScrollView( 29 | context: Context?, 30 | attrs: AttributeSet? = null 31 | ) : ScrollView(context, attrs) { 32 | 33 | private fun invalidateColors(color: Int) = 34 | setEdgeGlowColor(this, color) 35 | 36 | override fun onAttachedToWindow() { 37 | super.onAttachedToWindow() 38 | get().colorAccent() 39 | .distinctToMainThread() 40 | .subscribeTo(::invalidateColors) 41 | .unsubscribeOnDetach(this) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /library/src/main/java/com/afollestad/aesthetic/views/AestheticSeekBar.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Designed and developed by Aidan Follestad (@afollestad) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.afollestad.aesthetic.views 17 | 18 | import android.content.Context 19 | import android.util.AttributeSet 20 | import androidx.appcompat.widget.AppCompatSeekBar 21 | import com.afollestad.aesthetic.Aesthetic.Companion.get 22 | import com.afollestad.aesthetic.ColorIsDarkState 23 | import com.afollestad.aesthetic.internal.AttrWizard 24 | import com.afollestad.aesthetic.utils.combine 25 | import com.afollestad.aesthetic.utils.distinctToMainThread 26 | import com.afollestad.aesthetic.utils.observableForAttrName 27 | import com.afollestad.aesthetic.utils.setTint 28 | import com.afollestad.aesthetic.utils.subscribeTo 29 | import com.afollestad.aesthetic.utils.unsubscribeOnDetach 30 | 31 | /** @author Aidan Follestad (afollestad) */ 32 | class AestheticSeekBar( 33 | context: Context, 34 | attrs: AttributeSet? = null 35 | ) : AppCompatSeekBar(context, attrs) { 36 | 37 | private val wizard = AttrWizard(context, attrs) 38 | private val backgroundColorValue = wizard.getRawValue(android.R.attr.background) 39 | 40 | private fun invalidateColors(state: ColorIsDarkState) = setTint(state.color, state.isDark) 41 | 42 | override fun onAttachedToWindow() { 43 | super.onAttachedToWindow() 44 | 45 | combine( 46 | get().observableForAttrName( 47 | backgroundColorValue, 48 | get().colorAccent() 49 | )!!, 50 | get().isDark 51 | ) { color, isDark -> ColorIsDarkState(color, isDark) } 52 | .distinctToMainThread() 53 | .subscribeTo(::invalidateColors) 54 | .unsubscribeOnDetach(this) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /library/src/main/java/com/afollestad/aesthetic/views/AestheticSnackBarContentLayout.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Designed and developed by Aidan Follestad (@afollestad) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.afollestad.aesthetic.views 17 | 18 | import android.annotation.SuppressLint 19 | import android.content.Context 20 | import android.util.AttributeSet 21 | import com.afollestad.aesthetic.Aesthetic.Companion.get 22 | import com.afollestad.aesthetic.utils.distinctToMainThread 23 | import com.afollestad.aesthetic.utils.subscribeTextColor 24 | import com.afollestad.aesthetic.utils.subscribeTo 25 | import com.afollestad.aesthetic.utils.tint 26 | import com.afollestad.aesthetic.utils.unsubscribeOnDetach 27 | import com.google.android.material.snackbar.Snackbar 28 | import com.google.android.material.snackbar.SnackbarContentLayout 29 | 30 | /** @author Aidan Follestad (afollestad) */ 31 | @SuppressLint("RestrictedApi") 32 | internal class AestheticSnackBarContentLayout( 33 | context: Context, 34 | attrs: AttributeSet? = null 35 | ) : SnackbarContentLayout(context, attrs) { 36 | 37 | override fun onAttachedToWindow() { 38 | super.onAttachedToWindow() 39 | 40 | get().snackbarBackgroundColor() 41 | .distinctToMainThread() 42 | .subscribeTo(this::invalidateBgColors) 43 | .unsubscribeOnDetach(this) 44 | 45 | get().snackbarTextColor() 46 | .distinctToMainThread() 47 | .subscribeTextColor(messageView) 48 | .unsubscribeOnDetach(this) 49 | 50 | get().snackbarActionTextColor() 51 | .distinctToMainThread() 52 | .subscribeTextColor(actionView) 53 | .unsubscribeOnDetach(this) 54 | } 55 | 56 | private fun invalidateBgColors(color: Int) { 57 | setBackgroundColor(color) 58 | val parent = this.parent 59 | if (parent is Snackbar.SnackbarLayout) { 60 | val background = parent.background 61 | if (background != null) { 62 | parent.background = background.tint(color) 63 | } else { 64 | parent.setBackgroundColor(color) 65 | } 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /library/src/main/java/com/afollestad/aesthetic/views/AestheticSpinner.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Designed and developed by Aidan Follestad (@afollestad) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.afollestad.aesthetic.views 17 | 18 | import android.content.Context 19 | import android.util.AttributeSet 20 | import androidx.appcompat.widget.AppCompatSpinner 21 | import com.afollestad.aesthetic.Aesthetic.Companion.get 22 | import com.afollestad.aesthetic.ColorIsDarkState 23 | import com.afollestad.aesthetic.internal.AttrWizard 24 | import com.afollestad.aesthetic.utils.combine 25 | import com.afollestad.aesthetic.utils.distinctToMainThread 26 | import com.afollestad.aesthetic.utils.observableForAttrName 27 | import com.afollestad.aesthetic.utils.setTintAuto 28 | import com.afollestad.aesthetic.utils.subscribeTo 29 | import com.afollestad.aesthetic.utils.unsubscribeOnDetach 30 | 31 | /** @author Aidan Follestad (afollestad) */ 32 | class AestheticSpinner( 33 | context: Context, 34 | attrs: AttributeSet? = null 35 | ) : AppCompatSpinner(context, attrs) { 36 | 37 | private val wizard = AttrWizard(context, attrs) 38 | private val backgroundColorValue = wizard.getRawValue(android.R.attr.background) 39 | 40 | private fun invalidateColors(state: ColorIsDarkState) = 41 | setTintAuto(state.color, true, state.isDark) 42 | 43 | override fun onAttachedToWindow() { 44 | super.onAttachedToWindow() 45 | 46 | combine( 47 | get().observableForAttrName( 48 | backgroundColorValue, 49 | get().colorAccent() 50 | )!!, 51 | get().isDark 52 | ) { color, isDark -> ColorIsDarkState(color, isDark) } 53 | .distinctToMainThread() 54 | .subscribeTo(::invalidateColors) 55 | .unsubscribeOnDetach(this) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /library/src/main/java/com/afollestad/aesthetic/views/AestheticSwipeRefreshLayout.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Designed and developed by Aidan Follestad (@afollestad) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.afollestad.aesthetic.views 17 | 18 | import android.annotation.SuppressLint 19 | import android.content.Context 20 | import android.util.AttributeSet 21 | import androidx.swiperefreshlayout.widget.SwipeRefreshLayout 22 | import com.afollestad.aesthetic.Aesthetic.Companion.get 23 | import com.afollestad.aesthetic.utils.distinctToMainThread 24 | import com.afollestad.aesthetic.utils.subscribeTo 25 | import com.afollestad.aesthetic.utils.unsubscribeOnDetach 26 | 27 | /** @author Aidan Follestad (afollestad) */ 28 | @SuppressLint("PrivateResource") 29 | class AestheticSwipeRefreshLayout( 30 | context: Context, 31 | attrs: AttributeSet? = null 32 | ) : SwipeRefreshLayout(context, attrs) { 33 | 34 | override fun onAttachedToWindow() { 35 | super.onAttachedToWindow() 36 | get().swipeRefreshLayoutColors() 37 | .distinctToMainThread() 38 | .subscribeTo(::setColorSchemeColors) 39 | .unsubscribeOnDetach(this) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /library/src/main/java/com/afollestad/aesthetic/views/AestheticSwitch.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Designed and developed by Aidan Follestad (@afollestad) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.afollestad.aesthetic.views 17 | 18 | import android.content.Context 19 | import android.util.AttributeSet 20 | import android.widget.Switch 21 | import com.afollestad.aesthetic.Aesthetic.Companion.get 22 | import com.afollestad.aesthetic.ColorIsDarkState 23 | import com.afollestad.aesthetic.internal.AttrWizard 24 | import com.afollestad.aesthetic.utils.combine 25 | import com.afollestad.aesthetic.utils.distinctToMainThread 26 | import com.afollestad.aesthetic.utils.observableForAttrName 27 | import com.afollestad.aesthetic.utils.setTint 28 | import com.afollestad.aesthetic.utils.subscribeTo 29 | import com.afollestad.aesthetic.utils.unsubscribeOnDetach 30 | 31 | /** @author Aidan Follestad (afollestad) */ 32 | class AestheticSwitch( 33 | context: Context, 34 | attrs: AttributeSet? = null 35 | ) : Switch(context, attrs) { 36 | 37 | private val wizard = AttrWizard(context, attrs) 38 | private val backgroundColorValue = wizard.getRawValue(android.R.attr.background) 39 | 40 | private fun invalidateColors(state: ColorIsDarkState) = setTint(state.color, state.isDark) 41 | 42 | override fun onAttachedToWindow() { 43 | super.onAttachedToWindow() 44 | 45 | combine( 46 | get().observableForAttrName( 47 | backgroundColorValue, 48 | get().colorAccent() 49 | )!!, 50 | get().isDark 51 | ) { color, isDark -> ColorIsDarkState(color, isDark) } 52 | .distinctToMainThread() 53 | .subscribeTo(::invalidateColors) 54 | .unsubscribeOnDetach(this) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /library/src/main/java/com/afollestad/aesthetic/views/AestheticSwitchCompat.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Designed and developed by Aidan Follestad (@afollestad) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.afollestad.aesthetic.views 17 | 18 | import android.content.Context 19 | import android.util.AttributeSet 20 | import androidx.appcompat.widget.SwitchCompat 21 | import com.afollestad.aesthetic.Aesthetic.Companion.get 22 | import com.afollestad.aesthetic.ColorIsDarkState 23 | import com.afollestad.aesthetic.internal.AttrWizard 24 | import com.afollestad.aesthetic.utils.combine 25 | import com.afollestad.aesthetic.utils.distinctToMainThread 26 | import com.afollestad.aesthetic.utils.observableForAttrName 27 | import com.afollestad.aesthetic.utils.setTint 28 | import com.afollestad.aesthetic.utils.subscribeTo 29 | import com.afollestad.aesthetic.utils.unsubscribeOnDetach 30 | 31 | /** @author Aidan Follestad (afollestad) */ 32 | class AestheticSwitchCompat( 33 | context: Context, 34 | attrs: AttributeSet? = null 35 | ) : SwitchCompat(context, attrs) { 36 | 37 | private val wizard = AttrWizard(context, attrs) 38 | private val backgroundColorValue = wizard.getRawValue(android.R.attr.background) 39 | 40 | private fun invalidateColors(state: ColorIsDarkState) = setTint(state.color, state.isDark) 41 | 42 | override fun onAttachedToWindow() { 43 | super.onAttachedToWindow() 44 | 45 | combine( 46 | get().observableForAttrName( 47 | backgroundColorValue, 48 | get().colorAccent() 49 | )!!, 50 | get().isDark 51 | ) { color, isDark -> ColorIsDarkState(color, isDark) } 52 | .distinctToMainThread() 53 | .subscribeTo(::invalidateColors) 54 | .unsubscribeOnDetach(this) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /library/src/main/java/com/afollestad/aesthetic/views/AestheticTabLayout.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Designed and developed by Aidan Follestad (@afollestad) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.afollestad.aesthetic.views 17 | 18 | import android.annotation.SuppressLint 19 | import android.content.Context 20 | import android.content.res.ColorStateList 21 | import android.util.AttributeSet 22 | import androidx.annotation.ColorInt 23 | import com.afollestad.aesthetic.Aesthetic.Companion.get 24 | import com.afollestad.aesthetic.ColorMode 25 | import com.afollestad.aesthetic.utils.adjustAlpha 26 | import com.afollestad.aesthetic.utils.distinctToMainThread 27 | import com.afollestad.aesthetic.utils.one 28 | import com.afollestad.aesthetic.utils.subscribeBackgroundColor 29 | import com.afollestad.aesthetic.utils.subscribeTo 30 | import com.afollestad.aesthetic.utils.tint 31 | import com.afollestad.aesthetic.utils.unsubscribeOnDetach 32 | import com.google.android.material.tabs.TabLayout 33 | 34 | /** @author Aidan Follestad (afollestad) */ 35 | class AestheticTabLayout( 36 | context: Context, 37 | attrs: AttributeSet? = null 38 | ) : TabLayout(context, attrs) { 39 | 40 | companion object { 41 | const val UNFOCUSED_ALPHA = 0.5f 42 | } 43 | 44 | @SuppressLint("CheckResult") 45 | override fun setBackgroundColor(@ColorInt color: Int) { 46 | super.setBackgroundColor(color) 47 | get().toolbarIconColor() 48 | .one() 49 | .subscribeTo(::setIconsColor) 50 | .unsubscribeOnDetach(this) 51 | get().toolbarTitleColor() 52 | .one() 53 | .subscribeTo { setTabTextColors(it.adjustAlpha(UNFOCUSED_ALPHA), it) } 54 | .unsubscribeOnDetach(this) 55 | } 56 | 57 | override fun onAttachedToWindow() { 58 | super.onAttachedToWindow() 59 | 60 | get().toolbarIconColor() 61 | .subscribeTo(::setIconsColor) 62 | .unsubscribeOnDetach(this) 63 | 64 | get().toolbarTitleColor() 65 | .subscribeTo { setTabTextColors(it.adjustAlpha(UNFOCUSED_ALPHA), it) } 66 | .unsubscribeOnDetach(this) 67 | 68 | get().tabLayoutBackgroundMode() 69 | .distinctToMainThread() 70 | .flatMap { 71 | when (it) { 72 | ColorMode.PRIMARY -> get().colorPrimary() 73 | ColorMode.ACCENT -> get().colorAccent() 74 | } 75 | } 76 | .distinctToMainThread() 77 | .subscribeBackgroundColor(this@AestheticTabLayout) 78 | .unsubscribeOnDetach(this) 79 | 80 | get().tabLayoutIndicatorMode() 81 | .distinctToMainThread() 82 | .flatMap { 83 | when (it) { 84 | ColorMode.PRIMARY -> get().colorPrimary() 85 | ColorMode.ACCENT -> get().colorAccent() 86 | } 87 | } 88 | .distinctToMainThread() 89 | .subscribeTo(::setSelectedTabIndicatorColor) 90 | .unsubscribeOnDetach(this) 91 | } 92 | 93 | private fun setIconsColor(color: Int) { 94 | val sl = ColorStateList( 95 | arrayOf( 96 | intArrayOf(-android.R.attr.state_selected), 97 | intArrayOf(android.R.attr.state_selected) 98 | ), 99 | intArrayOf( 100 | color.adjustAlpha(UNFOCUSED_ALPHA), 101 | color 102 | ) 103 | ) 104 | for (i in 0 until tabCount) { 105 | val tab = getTabAt(i) 106 | if (tab != null && tab.icon != null) { 107 | tab.icon = tab.icon.tint(sl) 108 | } 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /library/src/main/java/com/afollestad/aesthetic/views/AestheticTextInputEditText.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Designed and developed by Aidan Follestad (@afollestad) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.afollestad.aesthetic.views 17 | 18 | import android.content.Context 19 | import android.util.AttributeSet 20 | import com.afollestad.aesthetic.Aesthetic.Companion.get 21 | import com.afollestad.aesthetic.ColorIsDarkState 22 | import com.afollestad.aesthetic.internal.AttrWizard 23 | import com.afollestad.aesthetic.utils.combine 24 | import com.afollestad.aesthetic.utils.distinctToMainThread 25 | import com.afollestad.aesthetic.utils.observableForAttrName 26 | import com.afollestad.aesthetic.utils.setTintAuto 27 | import com.afollestad.aesthetic.utils.subscribeHintTextColor 28 | import com.afollestad.aesthetic.utils.subscribeTextColor 29 | import com.afollestad.aesthetic.utils.subscribeTo 30 | import com.afollestad.aesthetic.utils.unsubscribeOnDetach 31 | import com.google.android.material.textfield.TextInputEditText 32 | 33 | /** @author Aidan Follestad (afollestad) */ 34 | class AestheticTextInputEditText( 35 | context: Context, 36 | attrs: AttributeSet? = null 37 | ) : TextInputEditText(context, attrs) { 38 | 39 | private var lastState: ColorIsDarkState? = null 40 | private val wizard = AttrWizard(context, attrs) 41 | private val backgroundColorValue = wizard.getRawValue(android.R.attr.background) 42 | 43 | private fun invalidateColors(state: ColorIsDarkState) { 44 | this.lastState = state 45 | setTintAuto(state.color, true, state.isDark) 46 | } 47 | 48 | override fun onAttachedToWindow() { 49 | super.onAttachedToWindow() 50 | 51 | get().textColorPrimary() 52 | .distinctToMainThread() 53 | .subscribeTextColor(this) 54 | .unsubscribeOnDetach(this) 55 | 56 | get().textColorSecondary() 57 | .distinctToMainThread() 58 | .subscribeHintTextColor(this) 59 | .unsubscribeOnDetach(this) 60 | 61 | combine( 62 | get().observableForAttrName( 63 | backgroundColorValue, 64 | get().colorAccent() 65 | )!!, 66 | get().isDark 67 | ) { color, isDark -> ColorIsDarkState(color, isDark) } 68 | .distinctToMainThread() 69 | .subscribeTo(::invalidateColors) 70 | .unsubscribeOnDetach(this) 71 | } 72 | 73 | override fun refreshDrawableState() { 74 | super.refreshDrawableState() 75 | if (lastState != null) { 76 | post { invalidateColors(lastState!!) } 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /library/src/main/java/com/afollestad/aesthetic/views/AestheticTextInputLayout.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Designed and developed by Aidan Follestad (@afollestad) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.afollestad.aesthetic.views 17 | 18 | import android.content.Context 19 | import android.util.AttributeSet 20 | import com.afollestad.aesthetic.Aesthetic.Companion.get 21 | import com.afollestad.aesthetic.internal.AttrWizard 22 | import com.afollestad.aesthetic.utils.adjustAlpha 23 | import com.afollestad.aesthetic.utils.distinctToMainThread 24 | import com.afollestad.aesthetic.utils.observableForAttrName 25 | import com.afollestad.aesthetic.utils.setAccentColor 26 | import com.afollestad.aesthetic.utils.setHintColor 27 | import com.afollestad.aesthetic.utils.subscribeTo 28 | import com.afollestad.aesthetic.utils.unsubscribeOnDetach 29 | import com.google.android.material.textfield.TextInputLayout 30 | 31 | /** @author Aidan Follestad (afollestad) */ 32 | class AestheticTextInputLayout( 33 | context: Context, 34 | attrs: AttributeSet? = null 35 | ) : TextInputLayout(context, attrs) { 36 | 37 | private val wizard = AttrWizard(context, attrs) 38 | private val backgroundColorValue = wizard.getRawValue(android.R.attr.background) 39 | 40 | private fun invalidateColors(color: Int) = setAccentColor(color) 41 | 42 | override fun onAttachedToWindow() { 43 | super.onAttachedToWindow() 44 | 45 | get().textColorSecondary() 46 | .distinctToMainThread() 47 | .subscribeTo { setHintColor(it.adjustAlpha(0.7f)) } 48 | .unsubscribeOnDetach(this) 49 | 50 | get().observableForAttrName( 51 | backgroundColorValue, 52 | get().colorAccent() 53 | )!! 54 | .distinctToMainThread() 55 | .subscribeTo(::invalidateColors) 56 | .unsubscribeOnDetach(this) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /library/src/main/java/com/afollestad/aesthetic/views/AestheticToolbar.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Designed and developed by Aidan Follestad (@afollestad) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.afollestad.aesthetic.views 17 | 18 | import android.content.Context 19 | import android.graphics.drawable.Drawable 20 | import android.util.AttributeSet 21 | import androidx.annotation.ColorInt 22 | import androidx.appcompat.widget.Toolbar 23 | import com.afollestad.aesthetic.Aesthetic.Companion.get 24 | import com.afollestad.aesthetic.R 25 | import com.afollestad.aesthetic.internal.AttrWizard 26 | import com.afollestad.aesthetic.utils.darkenColor 27 | import com.afollestad.aesthetic.utils.observableForAttrName 28 | import com.afollestad.aesthetic.utils.setOverflowButtonColor 29 | import com.afollestad.aesthetic.utils.subscribeTo 30 | import com.afollestad.aesthetic.utils.tint 31 | import com.afollestad.aesthetic.utils.tintMenu 32 | import com.afollestad.aesthetic.utils.unsubscribeOnDetach 33 | import io.reactivex.subjects.PublishSubject 34 | 35 | /** @author Aidan Follestad (afollestad) */ 36 | class AestheticToolbar( 37 | context: Context, 38 | attrs: AttributeSet? = null 39 | ) : Toolbar(context, attrs) { 40 | 41 | private var onColorUpdated = PublishSubject.create() 42 | private var menuIconColor: Int? = null 43 | 44 | private val wizard = AttrWizard(context, attrs) 45 | private val backgroundColorValue = wizard.getRawValue(android.R.attr.background) 46 | private val titleTextColorValue = wizard.getRawValue(R.attr.titleTextColor) 47 | private val subtitleTextColorValue = wizard.getRawValue(R.attr.subtitleTextColor) 48 | 49 | fun colorUpdated() = onColorUpdated 50 | 51 | override fun setNavigationIcon(icon: Drawable?) { 52 | if (menuIconColor == null) { 53 | super.setNavigationIcon(icon) 54 | return 55 | } 56 | super.setNavigationIcon(icon.tint(menuIconColor!!)) 57 | } 58 | 59 | fun setNavigationIcon(icon: Drawable?, @ColorInt color: Int) { 60 | if (menuIconColor == null) { 61 | super.setNavigationIcon(icon) 62 | return 63 | } 64 | super.setNavigationIcon(icon.tint(color)) 65 | } 66 | 67 | override fun onAttachedToWindow() { 68 | super.onAttachedToWindow() 69 | 70 | get().observableForAttrName( 71 | backgroundColorValue, 72 | get().colorPrimary() 73 | )!! 74 | .distinctUntilChanged() 75 | .doOnNext { onColorUpdated.onNext(it) } 76 | .subscribeTo(::setBackgroundColor) 77 | .unsubscribeOnDetach(this) 78 | 79 | get().toolbarIconColor() 80 | .distinctUntilChanged() 81 | .subscribeTo(::invalidateColors) 82 | .unsubscribeOnDetach(this) 83 | 84 | get().observableForAttrName( 85 | titleTextColorValue, 86 | get().toolbarTitleColor() 87 | ) 88 | ?.distinctUntilChanged() 89 | ?.subscribeTo(::setTitleTextColor) 90 | ?.unsubscribeOnDetach(this) 91 | 92 | get().observableForAttrName( 93 | subtitleTextColorValue, 94 | get().toolbarSubtitleColor() 95 | ) 96 | ?.distinctUntilChanged() 97 | ?.subscribeTo(::setSubtitleTextColor) 98 | ?.unsubscribeOnDetach(this) 99 | } 100 | 101 | private fun invalidateColors(color: Int) { 102 | this.menuIconColor = color 103 | setOverflowButtonColor(color) 104 | tintMenu(menu, color, color.darkenColor()) 105 | if (navigationIcon != null) { 106 | this.navigationIcon = navigationIcon 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /library/src/main/java/com/afollestad/aesthetic/views/AestheticViewPager.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Designed and developed by Aidan Follestad (@afollestad) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.afollestad.aesthetic.views 17 | 18 | import android.content.Context 19 | import android.util.AttributeSet 20 | import androidx.viewpager.widget.ViewPager 21 | import com.afollestad.aesthetic.Aesthetic.Companion.get 22 | import com.afollestad.aesthetic.utils.EdgeGlowUtil.setEdgeGlowColor 23 | import com.afollestad.aesthetic.utils.distinctToMainThread 24 | import com.afollestad.aesthetic.utils.subscribeTo 25 | import com.afollestad.aesthetic.utils.unsubscribeOnDetach 26 | 27 | /** @author Aidan Follestad (afollestad) */ 28 | class AestheticViewPager( 29 | context: Context, 30 | attrs: AttributeSet? = null 31 | ) : ViewPager(context, attrs) { 32 | 33 | private fun invalidateColors(color: Int) = 34 | setEdgeGlowColor(this, color) 35 | 36 | override fun onAttachedToWindow() { 37 | super.onAttachedToWindow() 38 | get().colorAccent() 39 | .distinctToMainThread() 40 | .subscribeTo(::invalidateColors) 41 | .unsubscribeOnDetach(this) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /library/src/main/res-public/values/public.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /library/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | #1F000000 8 | #1F000000 9 | 10 | #43000000 11 | #43000000 12 | #43000000 13 | 14 | #61000000 15 | 16 | #8A000000 17 | 18 | #61000000 19 | #8A000000 20 | 21 | #DE000000 22 | 23 | #FFFAFAFA 24 | 25 | #FFBDBDBD 26 | 27 | #E8E8E8 28 | #F5F5F5 29 | #FFFFFF 30 | 31 | 32 | 33 | #1AFFFFFF 34 | 35 | #1F000000 36 | 37 | #4DFFFFFF 38 | #4DFFFFFF 39 | #4DFFFFFF 40 | #4DFFFFFF 41 | 42 | #ffffffff 43 | 44 | #80FFFFFF 45 | #B3FFFFFF 46 | 47 | #FFFFFFFF 48 | 49 | #FFBDBDBD 50 | 51 | #FF424242 52 | 53 | #202020 54 | #424242 55 | #424242 56 | 57 | -------------------------------------------------------------------------------- /library/src/main/res/values/ids.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /sample-appcompat/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /sample-appcompat/build.gradle: -------------------------------------------------------------------------------- 1 | apply from: '../dependencies.gradle' 2 | apply plugin: 'com.android.application' 3 | apply plugin: 'kotlin-android' 4 | apply plugin: 'kotlin-kapt' 5 | apply plugin: 'kotlin-android-extensions' 6 | 7 | android { 8 | compileSdkVersion versions.compileSdk 9 | buildToolsVersion versions.buildTools 10 | 11 | defaultConfig { 12 | applicationId "com.afollestad.aestheticsample.appcompat" 13 | minSdkVersion versions.minSdk 14 | targetSdkVersion versions.compileSdk 15 | versionCode versions.publishVersionCode 16 | versionName versions.publishVersion 17 | vectorDrawables.useSupportLibrary = true 18 | } 19 | 20 | compileOptions { 21 | kotlinOptions.freeCompilerArgs += ['-module-name', "com.afollestad.aestheticsample.appcompat"] 22 | } 23 | 24 | packagingOptions { 25 | pickFirst 'META-INF/proguard/androidx-annotations.pro' 26 | } 27 | } 28 | 29 | repositories { 30 | google() 31 | jcenter() 32 | } 33 | 34 | dependencies { 35 | implementation project(':library') 36 | 37 | implementation 'androidx.preference:preference:' + versions.androidx 38 | implementation 'com.google.android.material:material:' + versions.androidx 39 | 40 | implementation 'io.reactivex.rxjava2:rxjava:' + versions.rxJava 41 | implementation 'io.reactivex.rxjava2:rxandroid:' + versions.rxAndroid 42 | 43 | implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk7:' + versions.kotlin 44 | } 45 | 46 | apply from: '../spotless.gradle' -------------------------------------------------------------------------------- /sample-appcompat/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 15 | 16 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 30 | 33 | 36 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /sample-appcompat/src/main/java/com/afollestad/aestheticsample/appcompat/AppCompatDemoActivity.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Designed and developed by Aidan Follestad (@afollestad) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.afollestad.aestheticsample.appcompat 17 | 18 | import android.content.Intent 19 | import android.graphics.Color 20 | import android.os.Bundle 21 | import androidx.appcompat.widget.SearchView 22 | import com.afollestad.aesthetic.Aesthetic 23 | import com.afollestad.aesthetic.AestheticActivity 24 | import com.afollestad.aesthetic.BottomNavBgMode 25 | import com.afollestad.aesthetic.BottomNavIconTextMode 26 | import com.afollestad.aesthetic.NavigationViewMode 27 | import kotlinx.android.synthetic.main.activity_demo_appcompat.pager 28 | import kotlinx.android.synthetic.main.activity_demo_appcompat.tabs 29 | import kotlinx.android.synthetic.main.activity_demo_appcompat.toolbar 30 | 31 | /** @author Aidan Follestad (afollestad) */ 32 | class AppCompatDemoActivity : AestheticActivity() { 33 | 34 | override fun onCreate(savedInstanceState: Bundle?) { 35 | super.onCreate(savedInstanceState) 36 | setContentView(R.layout.activity_demo_appcompat) 37 | 38 | toolbar.inflateMenu(R.menu.main) 39 | toolbar.setOnMenuItemClickListener { 40 | if (it.itemId == R.id.settings) { 41 | startActivity(Intent(this@AppCompatDemoActivity, SettingsActivity::class.java)) 42 | } 43 | true 44 | } 45 | 46 | val searchItem = toolbar.menu.findItem(R.id.search) 47 | val searchView = searchItem.actionView as SearchView 48 | searchView.queryHint = getString(R.string.search_view_example) 49 | 50 | // If we haven't set any defaults, do that now 51 | if (Aesthetic.isFirstTime) { 52 | Aesthetic.config { 53 | activityTheme(R.style.AppCompatDemoTheme) 54 | textColorPrimary(res = R.color.text_color_primary) 55 | textColorSecondary(res = R.color.text_color_secondary) 56 | colorPrimary(res = R.color.md_white) 57 | colorAccent(res = R.color.md_blue) 58 | colorStatusBarAuto() 59 | colorNavigationBarAuto() 60 | textColorPrimary(Color.BLACK) 61 | navigationViewMode(NavigationViewMode.SELECTED_ACCENT) 62 | bottomNavigationBackgroundMode(BottomNavBgMode.PRIMARY) 63 | bottomNavigationIconTextMode(BottomNavIconTextMode.SELECTED_ACCENT) 64 | swipeRefreshLayoutColorsRes( 65 | R.color.md_blue, 66 | R.color.md_blue_grey, 67 | R.color.md_green 68 | ) 69 | attribute(R.attr.my_custom_attr, res = R.color.md_red) 70 | } 71 | } 72 | 73 | pager.adapter = 74 | MainPagerAdapter(this, supportFragmentManager) 75 | tabs.setupWithViewPager(pager) 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /sample-appcompat/src/main/java/com/afollestad/aestheticsample/appcompat/DrawerActivity.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Designed and developed by Aidan Follestad (@afollestad) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.afollestad.aestheticsample.appcompat 17 | 18 | import android.content.res.Configuration 19 | import android.os.Bundle 20 | import android.view.MenuItem 21 | import androidx.appcompat.app.ActionBarDrawerToggle 22 | import com.afollestad.aesthetic.AestheticActivity 23 | import kotlinx.android.synthetic.main.activity_drawer.drawer_layout 24 | import kotlinx.android.synthetic.main.activity_drawer.navigation_view 25 | import kotlinx.android.synthetic.main.activity_drawer.toolbar 26 | 27 | /** @author Aidan Follestad (afollestad) */ 28 | class DrawerActivity : AestheticActivity() { 29 | 30 | private lateinit var drawerToggle: ActionBarDrawerToggle 31 | 32 | override fun onCreate(savedInstanceState: Bundle?) { 33 | super.onCreate(savedInstanceState) 34 | setContentView(R.layout.activity_drawer) 35 | setSupportActionBar(toolbar) 36 | 37 | drawerToggle = ActionBarDrawerToggle( 38 | this, drawer_layout, toolbar, R.string.open_drawer, R.string.close_drawer 39 | ) 40 | drawer_layout.addDrawerListener(drawerToggle) 41 | 42 | supportActionBar!!.apply { 43 | setDisplayHomeAsUpEnabled(true) 44 | setHomeButtonEnabled(true) 45 | } 46 | 47 | navigation_view.post { navigation_view.setCheckedItem(R.id.item_three) } 48 | navigation_view.setNavigationItemSelectedListener { 49 | navigation_view.setCheckedItem(it.itemId) 50 | false 51 | } 52 | } 53 | 54 | override fun onPostCreate(savedInstanceState: Bundle?) { 55 | super.onPostCreate(savedInstanceState) 56 | // Sync the toggle state after onRestoreInstanceState has occurred. 57 | drawerToggle.syncState() 58 | } 59 | 60 | override fun onConfigurationChanged(newConfig: Configuration) { 61 | super.onConfigurationChanged(newConfig) 62 | drawerToggle.onConfigurationChanged(newConfig) 63 | } 64 | 65 | override fun onOptionsItemSelected(item: MenuItem): Boolean { 66 | return drawerToggle.onOptionsItemSelected(item) || super.onOptionsItemSelected(item) 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /sample-appcompat/src/main/java/com/afollestad/aestheticsample/appcompat/MainAdapter.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Designed and developed by Aidan Follestad (@afollestad) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.afollestad.aestheticsample.appcompat 17 | 18 | import android.annotation.SuppressLint 19 | import android.view.LayoutInflater 20 | import android.view.View 21 | import android.view.ViewGroup 22 | import androidx.recyclerview.widget.RecyclerView 23 | import com.afollestad.aestheticsample.appcompat.MainAdapter.ViewHolder 24 | import kotlinx.android.synthetic.main.list_item_rv.view.subtitle 25 | import kotlinx.android.synthetic.main.list_item_rv.view.title 26 | 27 | /** @author Aidan Follestad (afollestad) */ 28 | internal class MainAdapter : RecyclerView.Adapter() { 29 | 30 | override fun onCreateViewHolder( 31 | parent: ViewGroup, 32 | viewType: Int 33 | ): ViewHolder { 34 | val view = LayoutInflater.from(parent.context) 35 | .inflate(R.layout.list_item_rv, parent, false) 36 | return ViewHolder(view) 37 | } 38 | 39 | @SuppressLint("SetTextI18n") 40 | override fun onBindViewHolder( 41 | holder: ViewHolder, 42 | position: Int 43 | ) { 44 | holder.itemView.title.text = "Item #$position" 45 | holder.itemView.subtitle.setText(R.string.hello_world) 46 | } 47 | 48 | override fun getItemCount(): Int { 49 | return 20 50 | } 51 | 52 | internal class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) 53 | } 54 | -------------------------------------------------------------------------------- /sample-appcompat/src/main/java/com/afollestad/aestheticsample/appcompat/MainPagerAdapter.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Designed and developed by Aidan Follestad (@afollestad) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.afollestad.aestheticsample.appcompat 17 | 18 | import android.content.Context 19 | import androidx.fragment.app.Fragment 20 | import androidx.fragment.app.FragmentManager 21 | import androidx.fragment.app.FragmentStatePagerAdapter 22 | 23 | /** @author Aidan Follestad (afollestad) */ 24 | internal class MainPagerAdapter( 25 | private val context: Context, 26 | fm: FragmentManager 27 | ) : FragmentStatePagerAdapter(fm) { 28 | 29 | override fun getItem(position: Int): Fragment { 30 | return if (position == 0) MainFragment() else SecondaryFragment() 31 | } 32 | 33 | override fun getCount(): Int { 34 | return 2 35 | } 36 | 37 | override fun getPageTitle(position: Int): CharSequence? { 38 | return context.getString(if (position == 0) R.string.main else R.string.other) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /sample-appcompat/src/main/java/com/afollestad/aestheticsample/appcompat/RecyclerViewActivity.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Designed and developed by Aidan Follestad (@afollestad) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.afollestad.aestheticsample.appcompat 17 | 18 | import android.os.Bundle 19 | import androidx.recyclerview.widget.LinearLayoutManager 20 | import com.afollestad.aesthetic.AestheticActivity 21 | import kotlinx.android.synthetic.main.activity_recyclerview.recycler_view 22 | import kotlinx.android.synthetic.main.activity_recyclerview.toolbar 23 | 24 | /** @author Aidan Follestad (afollestad) */ 25 | class RecyclerViewActivity : AestheticActivity() { 26 | 27 | override fun onCreate(savedInstanceState: Bundle?) { 28 | super.onCreate(savedInstanceState) 29 | setContentView(R.layout.activity_recyclerview) 30 | 31 | toolbar.setNavigationOnClickListener { finish() } 32 | recycler_view.layoutManager = LinearLayoutManager(this) 33 | recycler_view.adapter = MainAdapter() 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /sample-appcompat/src/main/java/com/afollestad/aestheticsample/appcompat/SecondaryFragment.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Designed and developed by Aidan Follestad (@afollestad) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.afollestad.aestheticsample.appcompat 17 | 18 | import android.content.Intent 19 | import android.os.Bundle 20 | import android.view.LayoutInflater 21 | import android.view.View 22 | import android.view.ViewGroup 23 | import androidx.fragment.app.Fragment 24 | import kotlinx.android.synthetic.main.fragment_secondary.view.drawer_layout 25 | import kotlinx.android.synthetic.main.fragment_secondary.view.recycler_view 26 | import kotlinx.android.synthetic.main.fragment_secondary.view.swipe_refresh 27 | 28 | /** @author Aidan Follestad (afollestad) */ 29 | class SecondaryFragment : Fragment() { 30 | 31 | override fun onCreateView( 32 | inflater: LayoutInflater, 33 | container: ViewGroup?, 34 | savedInstanceState: Bundle? 35 | ): View? { 36 | return inflater.inflate(R.layout.fragment_secondary, container, false) 37 | } 38 | 39 | override fun onViewCreated( 40 | view: View, 41 | savedInstanceState: Bundle? 42 | ) { 43 | super.onViewCreated(view, savedInstanceState) 44 | 45 | view.drawer_layout.setOnClickListener { 46 | startActivity( 47 | Intent(activity, DrawerActivity::class.java) 48 | ) 49 | } 50 | view.recycler_view.setOnClickListener { 51 | startActivity( 52 | Intent(activity, RecyclerViewActivity::class.java) 53 | ) 54 | } 55 | view.swipe_refresh.setOnClickListener { 56 | startActivity( 57 | Intent(activity, SwipeRefreshActivity::class.java) 58 | ) 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /sample-appcompat/src/main/java/com/afollestad/aestheticsample/appcompat/SettingsActivity.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Designed and developed by Aidan Follestad (@afollestad) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.afollestad.aestheticsample.appcompat 17 | 18 | import android.os.Bundle 19 | import androidx.preference.PreferenceFragmentCompat 20 | import com.afollestad.aesthetic.AestheticActivity 21 | import kotlinx.android.synthetic.main.activity_settings.toolbar 22 | 23 | class SettingsFragment : PreferenceFragmentCompat() { 24 | 25 | override fun onCreatePreferences( 26 | savedInstanceState: Bundle?, 27 | rootKey: String? 28 | ) = setPreferencesFromResource(R.xml.preferences, rootKey) 29 | } 30 | 31 | class SettingsActivity : AestheticActivity() { 32 | 33 | override fun onCreate(savedInstanceState: Bundle?) { 34 | super.onCreate(savedInstanceState) 35 | setContentView(R.layout.activity_settings) 36 | 37 | toolbar.setOnClickListener { finish() } 38 | 39 | if (savedInstanceState == null) { 40 | supportFragmentManager 41 | .beginTransaction() 42 | .add(R.id.container, SettingsFragment()) 43 | .commit() 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /sample-appcompat/src/main/java/com/afollestad/aestheticsample/appcompat/SwipeRefreshActivity.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Designed and developed by Aidan Follestad (@afollestad) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.afollestad.aestheticsample.appcompat 17 | 18 | import android.os.Bundle 19 | import com.afollestad.aesthetic.AestheticActivity 20 | import io.reactivex.Observable.just 21 | import io.reactivex.disposables.Disposable 22 | import kotlinx.android.synthetic.main.activity_swipe_refresh.swipe_refresh_layout 23 | import kotlinx.android.synthetic.main.activity_swipe_refresh.toolbar 24 | import java.util.concurrent.TimeUnit.SECONDS 25 | 26 | /** @author Aidan Follestad (afollestad) */ 27 | class SwipeRefreshActivity : AestheticActivity() { 28 | 29 | private var delayed: Disposable? = null 30 | 31 | override fun onCreate(savedInstanceState: Bundle?) { 32 | super.onCreate(savedInstanceState) 33 | setContentView(R.layout.activity_swipe_refresh) 34 | 35 | toolbar.setNavigationOnClickListener { finish() } 36 | swipe_refresh_layout.setOnRefreshListener { 37 | delayed = just(true) 38 | .delay(2, SECONDS) 39 | .subscribe { swipe_refresh_layout.isRefreshing = false } 40 | } 41 | } 42 | 43 | override fun onPause() { 44 | delayed?.dispose() 45 | super.onPause() 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /sample-appcompat/src/main/res/drawable/ic_announcement.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /sample-appcompat/src/main/res/drawable/ic_archive.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /sample-appcompat/src/main/res/drawable/ic_arrow_back.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /sample-appcompat/src/main/res/drawable/ic_attach_money.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /sample-appcompat/src/main/res/drawable/ic_backup.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /sample-appcompat/src/main/res/drawable/ic_book.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /sample-appcompat/src/main/res/drawable/ic_check.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /sample-appcompat/src/main/res/drawable/ic_close.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /sample-appcompat/src/main/res/drawable/ic_info.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /sample-appcompat/src/main/res/drawable/ic_search.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /sample-appcompat/src/main/res/drawable/ic_watch.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /sample-appcompat/src/main/res/layout/activity_demo_appcompat.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 20 | 21 | 30 | 31 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /sample-appcompat/src/main/res/layout/activity_drawer.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 19 | 20 | 29 | 30 | 39 | 40 | 41 | 42 | 43 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /sample-appcompat/src/main/res/layout/activity_recyclerview.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 21 | 22 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /sample-appcompat/src/main/res/layout/activity_settings.xml: -------------------------------------------------------------------------------- 1 | 9 | 10 | 20 | 21 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /sample-appcompat/src/main/res/layout/activity_swipe_refresh.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 21 | 22 | 27 | 28 | 32 | 33 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /sample-appcompat/src/main/res/layout/drawer_header.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | -------------------------------------------------------------------------------- /sample-appcompat/src/main/res/layout/fragment_secondary.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 |