├── .github └── workflows │ └── android-master.yml ├── .gitignore ├── .idea ├── .gitignore ├── .name ├── compiler.xml ├── gradle.xml ├── jarRepositories.xml └── misc.xml ├── README.md ├── app ├── .editorconfig ├── .gitignore ├── build.gradle ├── code-quality.gradle ├── detekt-rule.yml ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── dev │ │ └── happysingh │ │ └── template │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── dev │ │ │ └── happysingh │ │ │ └── template │ │ │ ├── MainApplication.kt │ │ │ └── ui │ │ │ └── MainActivity.kt │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ └── ic_launcher_background.xml │ │ ├── layout │ │ └── activity_main.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.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-night │ │ └── themes.xml │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── themes.xml │ └── test │ └── java │ └── dev │ └── happysingh │ └── template │ └── ExampleUnitTest.kt ├── build.gradle ├── buildSrc ├── .gitignore ├── build.gradle └── src │ └── main │ └── java │ ├── ConfigFields.kt │ ├── Dependencies.kt │ ├── MavenUrls.kt │ └── Version.kt ├── core ├── .gitignore ├── build.gradle ├── consumer-rules.pro ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── dev │ │ └── happysingh │ │ └── core │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── dev │ │ │ └── happysingh │ │ │ └── core │ │ │ ├── base │ │ │ ├── BaseActivity.kt │ │ │ └── BaseFragment.kt │ │ │ └── ext │ │ │ ├── ActivityExt.kt │ │ │ ├── AndroidApiExt.kt │ │ │ ├── BitmapExt.kt │ │ │ ├── CommonViewExt.kt │ │ │ ├── CoreExt.kt │ │ │ ├── FileExt.kt │ │ │ ├── FragmentExt.kt │ │ │ ├── SpecificViewExt.kt │ │ │ ├── StringExt.kt │ │ │ └── WebViewExt.kt │ └── res │ │ └── values │ │ ├── colors.xml │ │ └── strings.xml │ └── test │ └── java │ └── dev │ └── happysingh │ └── core │ └── ExampleUnitTest.kt ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.github/workflows/android-master.yml: -------------------------------------------------------------------------------- 1 | name: Android CI 2 | 3 | on: pull_request 4 | 5 | jobs: 6 | # Running linting using detekt,ktlint 7 | lint: 8 | name: Running lints 9 | runs-on: ubuntu-18.04 10 | 11 | steps: 12 | - uses: actions/checkout@v1 13 | 14 | - name: Set up JDK 1.8 15 | uses: actions/setup-java@v1 16 | with: 17 | java-version: 1.8 18 | 19 | - name: Running Ktlint 20 | run: bash ./gradlew ktlint --stacktrace 21 | 22 | - name: Running detekt 23 | run: bash ./gradlew detekt --stacktrace 24 | 25 | # Running Unit Test Cases 26 | unitTest: 27 | needs: lint 28 | name: Running Unit Tests 29 | runs-on: ubuntu-18.04 30 | 31 | steps: 32 | - uses: actions/checkout@v1 33 | - name: Set up JDK 1.8 34 | uses: actions/setup-java@v1 35 | with: 36 | java-version: 1.8 37 | - name: Run Unit tests 38 | run: bash ./gradlew test --stacktrace -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | /local.properties 3 | /.idea/caches 4 | /.idea/libraries 5 | /.idea/modules.xml 6 | /.idea/workspace.xml 7 | /.idea/navEditor.xml 8 | /.idea/assetWizardSettings.xml 9 | .DS_Store 10 | /build 11 | /captures 12 | .externalNativeBuild 13 | .cxx 14 | local.properties 15 | 16 | 17 | 18 | # Created by https://www.toptal.com/developers/gitignore/api/androidstudio 19 | # Edit at https://www.toptal.com/developers/gitignore?templates=androidstudio 20 | 21 | ### AndroidStudio ### 22 | # Covers files to be ignored for android development using Android Studio. 23 | 24 | # Built application files 25 | *.apk 26 | *.ap_ 27 | *.aab 28 | 29 | # Files for the ART/Dalvik VM 30 | *.dex 31 | 32 | # Java class files 33 | *.class 34 | 35 | # Generated files 36 | bin/ 37 | gen/ 38 | out/ 39 | 40 | # Gradle files 41 | .gradle 42 | .gradle/ 43 | build/ 44 | 45 | # Signing files 46 | .signing/ 47 | 48 | # Proguard folder generated by Eclipse 49 | proguard/ 50 | 51 | # Log Files 52 | *.log 53 | 54 | # Android Studio 55 | /*/build/ 56 | /*/local.properties 57 | /*/out 58 | /*/*/build 59 | /*/*/production 60 | captures/ 61 | .navigation/ 62 | *.ipr 63 | *~ 64 | *.swp 65 | 66 | # Keystore files 67 | *.jks 68 | *.keystore 69 | 70 | # Google Services (e.g. APIs or Firebase) 71 | # google-services.json 72 | 73 | # Android Patch 74 | gen-external-apklibs 75 | 76 | 77 | # NDK 78 | obj/ 79 | 80 | # IntelliJ IDEA 81 | *.iws 82 | /out/ 83 | 84 | # User-specific configurations 85 | .idea/caches/ 86 | .idea/libraries/ 87 | .idea/shelf/ 88 | .idea/workspace.xml 89 | .idea/tasks.xml 90 | .idea/.name 91 | .idea/compiler.xml 92 | .idea/copyright/profiles_settings.xml 93 | .idea/encodings.xml 94 | .idea/misc.xml 95 | .idea/modules.xml 96 | .idea/scopes/scope_settings.xml 97 | .idea/dictionaries 98 | .idea/vcs.xml 99 | .idea/jsLibraryMappings.xml 100 | .idea/datasources.xml 101 | .idea/dataSources.ids 102 | .idea/sqlDataSources.xml 103 | .idea/dynamic.xml 104 | .idea/uiDesigner.xml 105 | .idea/assetWizardSettings.xml 106 | .idea/gradle.xml 107 | .idea/jarRepositories.xml 108 | .idea/navEditor.xml 109 | 110 | # OS-specific files 111 | .DS_Store? 112 | ._* 113 | .Spotlight-V100 114 | .Trashes 115 | ehthumbs.db 116 | Thumbs.db 117 | 118 | # Legacy Eclipse project files 119 | .classpath 120 | .project 121 | .cproject 122 | .settings/ 123 | 124 | # Mobile Tools for Java (J2ME) 125 | .mtj.tmp/ 126 | 127 | # Package Files # 128 | *.war 129 | *.ear 130 | 131 | # virtual machine crash logs (Reference: http://www.java.com/en/download/help/error_hotspot.xml) 132 | hs_err_pid* 133 | 134 | ## Plugin-specific files: 135 | 136 | # mpeltonen/sbt-idea plugin 137 | .idea_modules/ 138 | 139 | # JIRA plugin 140 | atlassian-ide-plugin.xml 141 | 142 | # Mongo Explorer plugin 143 | .idea/mongoSettings.xml 144 | 145 | # Crashlytics plugin (for Android Studio and IntelliJ) 146 | com_crashlytics_export_strings.xml 147 | crashlytics.properties 148 | crashlytics-build.properties 149 | fabric.properties 150 | 151 | ### AndroidStudio Patch ### 152 | 153 | !/gradle/wrapper/gradle-wrapper.jar 154 | 155 | # End of https://www.toptal.com/developers/gitignore/api/androidstudio -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | Android-Github-Repo-Template -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 20 | 21 | -------------------------------------------------------------------------------- /.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | 24 | 25 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Android Project Github Template 2 | 3 | [![platform](https://img.shields.io/badge/platform-Android-yellow.svg)](https://www.android.com) 4 | [![GitHub license](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE) 5 | ![Github Followers](https://img.shields.io/github/followers/happysingh23828?label=Follow&style=social) 6 | ![GitHub stars](https://img.shields.io/github/stars/happysingh23828/android-github-repo-template?style=social) 7 | ![GitHub forks](https://img.shields.io/github/forks/happysingh23828/android-github-repo-template?style=social) 8 | ![GitHub watchers](https://img.shields.io/github/watchers/happysingh23828/android-github-repo-template?style=social) 9 | ![Twitter Follow](https://img.shields.io/twitter/follow/happysingh23828?label=Follow&style=social) 10 | 11 | > A template for creating new repositories for the new Android Project. 12 | 13 | ## What is diffrent here from creating a new project from Android Studio? 14 | 15 | This repository is meant to serve as a general template for how to set up new repository for Android. In general, setting up a new repository takes 30-60 minutes; use this repository as a way of setting up in 5 minutes, and use the following checklist to ensure that you've set up the repository correctly. 16 | It has additional modules and files to save your time. 17 | 18 | ### Features 19 | - BuildSrc module (for handling dependecies at one place). 20 | - Core module (includes Kotlin Extensions, Base classess etc.). 21 | - Static tool analysis with Report generation (Ktlint, KtlintAutoFormat Detekt, Custom Detekt Lint Rules). 22 | - Android CI (using Github's Action workflow) 23 | - .gitignore for Android Studio Projects. 24 | - and many more to come.... 25 | 26 | ## Checklist 27 | These instructions are basic; The important part is making sure that you follow the checklist for error free setup. 28 | 29 | ### Create Repository from Github 30 | - [ ] Click on **use this template** on this repository. 31 | - [ ] Enter your Github repo name. 32 | - [ ] Uncheck **include all branches** option. 33 | - [ ] Click on **create repo from template** button. 34 | now your repo has been created. 35 | 36 | ### Clone Repo in Android Studio and set up. 37 | - [ ] Clone your newly created github repo into Android Studio. 38 | - [ ] Go to **settings.gradle** and change **rootProject.name** as per your project name. 39 | - [ ] Go to App's **build.gradle** and change **applicationId** as per your project package. 40 | - [ ] Go to App's module **strings.xml** and change **app_name** to your app name. 41 | - [ ] Delete Readme.MD file. 42 | - [ ] [Optional] Go to **themes.xml** and refactor Base Application them's name **Theme.AndroidRepoGithubTemplate** as per your requirements. 43 | that's it. 44 | 45 | ### Additional Notes 46 | - CI is only triggred on pull request. If you need to change action go to [android-master.yml](https://github.com/happysingh23828/android-github-repo-template/blob/master/.github/workflows/android-master.yml) 47 | - You can edit rules for **Detekt** in this [detekt-rule.yml](https://github.com/happysingh23828/android-github-repo-template/blob/master/app/detekt-rule.yml). 48 | 49 | ## - Code Quality Checks 50 | This projects uses detekt and ktlint static code analyser in CI/CD pipeline to maintain code quality. they also genrated reports. 51 | 52 | #### Run detekt locally 53 | Use `./gradlew detekt` 54 | 55 | Reports location for each module-
56 | `-/app/build/reports/detekt/report.html`
57 | `-/core/build/reports/detekt/report.html`
58 | 59 | #### Run ktlint locally 60 | Use `./gradlew ktlint` 61 | 62 | Reports location for each module-
63 | `-/app/build/reports/ktlint/ktlint-checkstyle-report.xml`
64 | `-/core/build/reports/ktlint/ktlint-checkstyle-report.xml`
65 | 66 | #### Run ktlint formatter locally 67 | Use `./gradlew ktlintFormat` 68 | 69 | 70 | ## If this project helps you in anyway, show your love :heart: by putting a :star: on this project :v: 71 | 72 | 73 | ## - Contributing 74 | 75 | Please fork this repository and contribute back using 76 | [pull requests](https://github.com/happysingh23828/android-github-repo-template/pulls). 77 | 78 | Any contributions, large or small, major features, bug fixes, are welcomed and appreciated 79 | but will be thoroughly reviewed . 80 | 81 | ### - Contact - Let's become friend 82 | - [Blog](https://happysingh.dev/) 83 | - [Youtube Channel](https://www.youtube.com/channel/UCILhpbLSFkGzsiCYAeR30DA) 84 | - [Github](https://github.com/happysingh23828) 85 | - [Linkedin](https://www.linkedin.com/in/happpysingh23828/) 86 | 87 | ## - License 88 | 89 | ``` 90 | MIT License 91 | 92 | Copyright (c) 2020 Happy Singh 93 | 94 | Permission is hereby granted, free of charge, to any person obtaining a copy 95 | of this software and associated documentation files (the "Software"), to deal 96 | in the Software without restriction, including without limitation the rights 97 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 98 | copies of the Software, and to permit persons to whom the Software is 99 | furnished to do so, subject to the following conditions: 100 | 101 | The above copyright notice and this permission notice shall be included in all 102 | copies or substantial portions of the Software. 103 | 104 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 105 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 106 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 107 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 108 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 109 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 110 | SOFTWARE.``` 111 | -------------------------------------------------------------------------------- /app/.editorconfig: -------------------------------------------------------------------------------- 1 | [*.{kt,kts}] 2 | # Disabling rules that were added in the latest versions of ktlint 3 | # tracking here: https://github.com/mozilla-mobile/fenix/issues/4861 4 | disabled_rules=import-ordering -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | import dependencies.Dependencies 2 | import dependencies.Version 3 | 4 | apply plugin: Dependencies.Plugins.ANDROID_APPLICATION 5 | apply plugin: Dependencies.Plugins.KOTLIN_ANDROID 6 | apply plugin: Dependencies.Plugins.KOTLIN_ANDROID_EXTENSIONS 7 | apply plugin: Dependencies.Plugins.KOTLIN_KAPT 8 | 9 | android { 10 | compileSdkVersion Version.compileSdk 11 | buildToolsVersion Version.buildTools 12 | 13 | defaultConfig { 14 | applicationId "com.your.packagename" 15 | minSdkVersion Version.minSdk 16 | targetSdkVersion Version.targetSdk 17 | versionCode Version.versionCode 18 | versionName Version.versionName 19 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 20 | } 21 | 22 | buildTypes { 23 | release { 24 | minifyEnabled true 25 | shrinkResources true 26 | debuggable false 27 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 28 | } 29 | 30 | debug { 31 | debuggable true 32 | } 33 | } 34 | 35 | kotlinOptions { 36 | jvmTarget = Version.jvmTarget 37 | } 38 | 39 | androidExtensions { 40 | experimental = true 41 | } 42 | 43 | dataBinding { 44 | enabled = true 45 | } 46 | 47 | testOptions { 48 | unitTests.includeAndroidResources = true 49 | unitTests.returnDefaultValues = true 50 | } 51 | 52 | 53 | // comment out below to create customize apk name with build details. 54 | /*applicationVariants.all { variant -> 55 | variant.outputs.all { output -> 56 | def versionName = variant.versionName 57 | def versionCode = variant.versionCode 58 | def flavorName = variant.flavorName 59 | def variantName = variant.name 60 | //customize your app name by using variables 61 | outputFileName = "YourAppName-$variantName-$versionCode-${versionName}.apk" 62 | } 63 | }*/ 64 | 65 | } 66 | 67 | dependencies { 68 | implementation fileTree(dir: "libs", include: ["*.jar"]) 69 | implementation Dependencies.Kotlin.kotlin_stdlib_jdk7 70 | 71 | // Modules 72 | implementation project(Dependencies.Module.core) 73 | 74 | //Android X 75 | implementation Dependencies.AndroidX.appcompat 76 | implementation Dependencies.AndroidX.materialDesign 77 | implementation Dependencies.AndroidX.constraintlayout 78 | implementation Dependencies.AndroidX.vectordrawable 79 | implementation Dependencies.AndroidX.core 80 | 81 | //Testing 82 | testImplementation Dependencies.Test.test_junit 83 | androidTestImplementation Dependencies.Test.android_test_espresso_core 84 | 85 | //LifeCycle 86 | implementation Dependencies.Lifecycle.lifeCycleExtension 87 | } -------------------------------------------------------------------------------- /app/code-quality.gradle: -------------------------------------------------------------------------------- 1 | import dependencies.Dependencies 2 | 3 | //detekt implementation 4 | configurations { 5 | detekt 6 | } 7 | 8 | dependencies { 9 | detekt Dependencies.Lint.detekt 10 | } 11 | 12 | def output = new File(project.buildDir, "reports/detekt/") 13 | 14 | task detekt(type: JavaExec, group: "verification", description: "Runs detekt.") { 15 | def configFile = project.rootProject.file("app/detekt-rule.yml") 16 | inputs.files(project.fileTree(dir: "src", include: "**/*.kt"), configFile) 17 | outputs.dir(output.toString()) 18 | main = "io.gitlab.arturbosch.detekt.cli.Main" 19 | classpath = project.configurations.detekt 20 | args = ["--config", configFile, "--input", project.file("."), "--report", "plain:$output/plain.txt,xml:$output/checkstyle.xml,html:$output/report.html"] 21 | } 22 | 23 | // ktlint. 24 | configurations { 25 | ktlint 26 | } 27 | 28 | dependencies { 29 | ktlint Dependencies.Lint.ktLint 30 | } 31 | 32 | def outputDir = "${project.buildDir}/reports/ktlint/" 33 | 34 | task ktlint(type: JavaExec, group: "verification", description: "Runs ktlint.") { 35 | inputs.files(fileTree(dir: "src", include: "**/*.kt"), 36 | fileTree(dir: ".", include: "**/.editorconfig")) 37 | outputs.dir(outputDir) 38 | main = "com.pinterest.ktlint.Main" 39 | classpath = configurations.ktlint 40 | args = ["--reporter=plain", 41 | "--reporter=checkstyle,output=${outputDir}ktlint-checkstyle-report.xml", 42 | "src/**/*.kt"] 43 | } 44 | 45 | // Automatic lint fix with ktlintFormat 46 | task ktlintFormat(type: JavaExec, group: "formatting") { 47 | inputs.files( 48 | fileTree(dir: "src", include: "**/*.kt"), 49 | fileTree(dir: ".", include: "**/.editorconfig") 50 | ) 51 | outputs.upToDateWhen { true } 52 | description = "Runs ktlint and autoformats your code." 53 | main = "com.pinterest.ktlint.Main" 54 | classpath = configurations.ktlint 55 | args = ["-F", "src/**/*.kt"] 56 | } -------------------------------------------------------------------------------- /app/detekt-rule.yml: -------------------------------------------------------------------------------- 1 | build: 2 | maxIssues: 1 3 | weights: 4 | # complexity: 2 5 | # LongParameterList: 1 6 | # style: 1 7 | # comments: 1 8 | 9 | processors: 10 | active: true 11 | exclude: 12 | # - 'DetektProgressListener' 13 | # - 'FunctionCountProcessor' 14 | # - 'PropertyCountProcessor' 15 | # - 'ClassCountProcessor' 16 | # - 'PackageCountProcessor' 17 | # - 'KtFileCountProcessor' 18 | 19 | console-reports: 20 | active: true 21 | exclude: 22 | # - 'ProjectStatisticsReport' 23 | # - 'ComplexityReport' 24 | # - 'NotificationReport' 25 | # - 'FindingsReport' 26 | - 'FileBasedFindingsReport' 27 | # - 'BuildFailureReport' 28 | 29 | comments: 30 | active: true 31 | excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" 32 | CommentOverPrivateFunction: 33 | active: false 34 | CommentOverPrivateProperty: 35 | active: false 36 | EndOfSentenceFormat: 37 | active: false 38 | endOfSentenceFormat: ([.?!][ \t\n\r\f<])|([.?!:]$) 39 | UndocumentedPublicClass: 40 | active: false 41 | searchInNestedClass: true 42 | searchInInnerClass: true 43 | searchInInnerObject: true 44 | searchInInnerInterface: true 45 | UndocumentedPublicFunction: 46 | active: false 47 | UndocumentedPublicProperty: 48 | active: false 49 | 50 | complexity: 51 | active: true 52 | ComplexCondition: 53 | active: true 54 | threshold: 4 55 | ComplexInterface: 56 | active: false 57 | threshold: 10 58 | includeStaticDeclarations: false 59 | ComplexMethod: 60 | active: true 61 | threshold: 10 62 | ignoreSingleWhenExpression: false 63 | ignoreSimpleWhenEntries: false 64 | LabeledExpression: 65 | active: false 66 | ignoredLabels: "" 67 | LargeClass: 68 | active: true 69 | threshold: 600 70 | LongMethod: 71 | active: true 72 | threshold: 60 73 | LongParameterList: 74 | active: true 75 | functionThreshold: 6 76 | constructorThreshold: 6 77 | ignoreDefaultParameters: true 78 | MethodOverloading: 79 | active: false 80 | threshold: 6 81 | NestedBlockDepth: 82 | active: true 83 | threshold: 4 84 | StringLiteralDuplication: 85 | active: false 86 | excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" 87 | threshold: 3 88 | ignoreAnnotation: true 89 | excludeStringsWithLessThan5Characters: true 90 | ignoreStringsRegex: '$^' 91 | TooManyFunctions: 92 | active: true 93 | excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" 94 | thresholdInFiles: 13 95 | thresholdInClasses: 13 96 | thresholdInInterfaces: 13 97 | thresholdInObjects: 13 98 | thresholdInEnums: 13 99 | ignoreDeprecated: false 100 | ignorePrivate: false 101 | ignoreOverridden: false 102 | 103 | empty-blocks: 104 | active: true 105 | EmptyCatchBlock: 106 | active: true 107 | allowedExceptionNameRegex: "^(_|(ignore|expected).*)" 108 | EmptyClassBlock: 109 | active: true 110 | EmptyDefaultConstructor: 111 | active: true 112 | EmptyDoWhileBlock: 113 | active: true 114 | EmptyElseBlock: 115 | active: true 116 | EmptyFinallyBlock: 117 | active: true 118 | EmptyForBlock: 119 | active: true 120 | EmptyFunctionBlock: 121 | active: true 122 | ignoreOverridden: false 123 | EmptyIfBlock: 124 | active: true 125 | EmptyInitBlock: 126 | active: true 127 | EmptyKtFile: 128 | active: true 129 | EmptySecondaryConstructor: 130 | active: true 131 | EmptyWhenBlock: 132 | active: true 133 | EmptyWhileBlock: 134 | active: true 135 | 136 | exceptions: 137 | active: true 138 | ExceptionRaisedInUnexpectedLocation: 139 | active: false 140 | methodNames: 'toString,hashCode,equals,finalize' 141 | InstanceOfCheckForException: 142 | active: false 143 | excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" 144 | NotImplementedDeclaration: 145 | active: false 146 | PrintStackTrace: 147 | active: false 148 | RethrowCaughtException: 149 | active: false 150 | ReturnFromFinally: 151 | active: false 152 | ignoreLabeled: false 153 | SwallowedException: 154 | active: false 155 | ignoredExceptionTypes: 'InterruptedException,NumberFormatException,ParseException,MalformedURLException' 156 | allowedExceptionNameRegex: "^(_|(ignore|expected).*)" 157 | ThrowingExceptionFromFinally: 158 | active: false 159 | ThrowingExceptionInMain: 160 | active: false 161 | ThrowingExceptionsWithoutMessageOrCause: 162 | active: false 163 | exceptions: 'IllegalArgumentException,IllegalStateException,IOException' 164 | ThrowingNewInstanceOfSameException: 165 | active: false 166 | TooGenericExceptionCaught: 167 | active: true 168 | excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" 169 | exceptionNames: 170 | - ArrayIndexOutOfBoundsException 171 | - Error 172 | - Exception 173 | - IllegalMonitorStateException 174 | - NullPointerException 175 | - IndexOutOfBoundsException 176 | - RuntimeException 177 | - Throwable 178 | allowedExceptionNameRegex: "^(_|(ignore|expected).*)" 179 | TooGenericExceptionThrown: 180 | active: true 181 | exceptionNames: 182 | - Error 183 | - Exception 184 | - Throwable 185 | - RuntimeException 186 | 187 | formatting: 188 | active: true 189 | android: false 190 | autoCorrect: true 191 | AnnotationOnSeparateLine: 192 | active: false 193 | autoCorrect: true 194 | ChainWrapping: 195 | active: true 196 | autoCorrect: true 197 | CommentSpacing: 198 | active: true 199 | autoCorrect: true 200 | Filename: 201 | active: true 202 | FinalNewline: 203 | active: true 204 | autoCorrect: true 205 | ImportOrdering: 206 | active: false 207 | autoCorrect: true 208 | Indentation: 209 | active: false 210 | autoCorrect: true 211 | indentSize: 4 212 | continuationIndentSize: 4 213 | MaximumLineLength: 214 | active: true 215 | maxLineLength: 120 216 | ModifierOrdering: 217 | active: true 218 | autoCorrect: true 219 | MultiLineIfElse: 220 | active: true 221 | autoCorrect: true 222 | NoBlankLineBeforeRbrace: 223 | active: true 224 | autoCorrect: true 225 | NoConsecutiveBlankLines: 226 | active: true 227 | autoCorrect: true 228 | NoEmptyClassBody: 229 | active: true 230 | autoCorrect: true 231 | NoLineBreakAfterElse: 232 | active: true 233 | autoCorrect: true 234 | NoLineBreakBeforeAssignment: 235 | active: true 236 | autoCorrect: true 237 | NoMultipleSpaces: 238 | active: true 239 | autoCorrect: true 240 | NoSemicolons: 241 | active: true 242 | autoCorrect: true 243 | NoTrailingSpaces: 244 | active: true 245 | autoCorrect: true 246 | NoUnitReturn: 247 | active: true 248 | autoCorrect: true 249 | NoUnusedImports: 250 | active: true 251 | autoCorrect: true 252 | NoWildcardImports: 253 | active: true 254 | PackageName: 255 | active: true 256 | autoCorrect: true 257 | ParameterListWrapping: 258 | active: true 259 | autoCorrect: true 260 | indentSize: 4 261 | SpacingAroundColon: 262 | active: true 263 | autoCorrect: true 264 | SpacingAroundComma: 265 | active: true 266 | autoCorrect: true 267 | SpacingAroundCurly: 268 | active: true 269 | autoCorrect: true 270 | SpacingAroundDot: 271 | active: true 272 | autoCorrect: true 273 | SpacingAroundKeyword: 274 | active: true 275 | autoCorrect: true 276 | SpacingAroundOperators: 277 | active: true 278 | autoCorrect: true 279 | SpacingAroundParens: 280 | active: true 281 | autoCorrect: true 282 | SpacingAroundRangeOperator: 283 | active: true 284 | autoCorrect: true 285 | StringTemplate: 286 | active: true 287 | autoCorrect: true 288 | 289 | naming: 290 | active: true 291 | ClassNaming: 292 | active: true 293 | excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" 294 | classPattern: '[A-Z$][a-zA-Z0-9$]*' 295 | ConstructorParameterNaming: 296 | active: true 297 | excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" 298 | parameterPattern: '[a-z][A-Za-z0-9]*' 299 | privateParameterPattern: '[a-z][A-Za-z0-9]*' 300 | excludeClassPattern: '$^' 301 | EnumNaming: 302 | active: true 303 | excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" 304 | enumEntryPattern: '^[A-Z][_a-zA-Z0-9]*' 305 | ForbiddenClassName: 306 | active: false 307 | excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" 308 | forbiddenName: '' 309 | FunctionMaxLength: 310 | active: false 311 | excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" 312 | maximumFunctionNameLength: 30 313 | FunctionMinLength: 314 | active: false 315 | excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" 316 | minimumFunctionNameLength: 3 317 | FunctionNaming: 318 | active: true 319 | excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" 320 | functionPattern: '^([a-z$][a-zA-Z$0-9]*)|(`.*`)$' 321 | excludeClassPattern: '$^' 322 | ignoreOverridden: true 323 | FunctionParameterNaming: 324 | active: true 325 | excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" 326 | parameterPattern: '[a-z][A-Za-z0-9]*' 327 | excludeClassPattern: '$^' 328 | ignoreOverridden: true 329 | InvalidPackageDeclaration: 330 | active: false 331 | rootPackage: '' 332 | MatchingDeclarationName: 333 | active: true 334 | MemberNameEqualsClassName: 335 | active: false 336 | ignoreOverridden: true 337 | ObjectPropertyNaming: 338 | active: true 339 | excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" 340 | constantPattern: '[A-Za-z][_A-Za-z0-9]*' 341 | propertyPattern: '[A-Za-z][_A-Za-z0-9]*' 342 | privatePropertyPattern: '(_)?[A-Za-z][_A-Za-z0-9]*' 343 | PackageNaming: 344 | active: true 345 | excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" 346 | packagePattern: '^[a-z]+(\.[a-z][A-Za-z0-9]*)*$' 347 | TopLevelPropertyNaming: 348 | active: true 349 | excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" 350 | constantPattern: '[A-Z][_A-Z0-9]*' 351 | propertyPattern: '[A-Za-z][_A-Za-z0-9]*' 352 | privatePropertyPattern: '_?[A-Za-z][_A-Za-z0-9]*' 353 | VariableMaxLength: 354 | active: false 355 | excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" 356 | maximumVariableNameLength: 64 357 | VariableMinLength: 358 | active: false 359 | excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" 360 | minimumVariableNameLength: 1 361 | VariableNaming: 362 | active: true 363 | excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" 364 | variablePattern: '[a-z][A-Za-z0-9]*' 365 | privateVariablePattern: '(_)?[a-z][A-Za-z0-9]*' 366 | excludeClassPattern: '$^' 367 | ignoreOverridden: true 368 | 369 | performance: 370 | active: true 371 | ArrayPrimitive: 372 | active: false 373 | ForEachOnRange: 374 | active: true 375 | excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" 376 | SpreadOperator: 377 | active: true 378 | excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" 379 | UnnecessaryTemporaryInstantiation: 380 | active: true 381 | 382 | potential-bugs: 383 | active: true 384 | Deprecation: 385 | active: false 386 | DuplicateCaseInWhenExpression: 387 | active: true 388 | EqualsAlwaysReturnsTrueOrFalse: 389 | active: false 390 | EqualsWithHashCodeExist: 391 | active: true 392 | ExplicitGarbageCollectionCall: 393 | active: true 394 | HasPlatformType: 395 | active: false 396 | InvalidRange: 397 | active: false 398 | IteratorHasNextCallsNextMethod: 399 | active: false 400 | IteratorNotThrowingNoSuchElementException: 401 | active: false 402 | LateinitUsage: 403 | active: false 404 | excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" 405 | excludeAnnotatedProperties: "" 406 | ignoreOnClassesPattern: "" 407 | MissingWhenCase: 408 | active: false 409 | RedundantElseInWhen: 410 | active: false 411 | UnconditionalJumpStatementInLoop: 412 | active: false 413 | UnreachableCode: 414 | active: true 415 | UnsafeCallOnNullableType: 416 | active: false 417 | UnsafeCast: 418 | active: false 419 | UselessPostfixExpression: 420 | active: false 421 | WrongEqualsTypeParameter: 422 | active: false 423 | 424 | style: 425 | active: true 426 | CollapsibleIfStatements: 427 | active: false 428 | DataClassContainsFunctions: 429 | active: false 430 | conversionFunctionPrefix: 'to' 431 | DataClassShouldBeImmutable: 432 | active: false 433 | EqualsNullCall: 434 | active: false 435 | EqualsOnSignatureLine: 436 | active: false 437 | ExplicitItLambdaParameter: 438 | active: false 439 | ExpressionBodySyntax: 440 | active: false 441 | includeLineWrapping: false 442 | ForbiddenComment: 443 | active: true 444 | values: 'TODO:,FIXME:,STOPSHIP:' 445 | allowedPatterns: "" 446 | ForbiddenImport: 447 | active: false 448 | imports: '' 449 | forbiddenPatterns: "" 450 | ForbiddenVoid: 451 | active: false 452 | ignoreOverridden: false 453 | ignoreUsageInGenerics: false 454 | FunctionOnlyReturningConstant: 455 | active: false 456 | ignoreOverridableFunction: true 457 | excludedFunctions: 'describeContents' 458 | excludeAnnotatedFunction: "dagger.Provides" 459 | LibraryCodeMustSpecifyReturnType: 460 | active: false 461 | LoopWithTooManyJumpStatements: 462 | active: false 463 | maxJumpCount: 1 464 | MagicNumber: 465 | active: true 466 | excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" 467 | ignoreNumbers: '-1,0,1,2' 468 | ignoreHashCodeFunction: true 469 | ignorePropertyDeclaration: false 470 | ignoreConstantDeclaration: true 471 | ignoreCompanionObjectPropertyDeclaration: true 472 | ignoreAnnotation: false 473 | ignoreNamedArgument: true 474 | ignoreEnums: false 475 | ignoreRanges: false 476 | MandatoryBracesIfStatements: 477 | active: false 478 | MaxLineLength: 479 | active: true 480 | maxLineLength: 120 481 | excludePackageStatements: true 482 | excludeImportStatements: true 483 | excludeCommentStatements: false 484 | MayBeConst: 485 | active: false 486 | ModifierOrder: 487 | active: true 488 | NestedClassesVisibility: 489 | active: false 490 | NewLineAtEndOfFile: 491 | active: true 492 | NoTabs: 493 | active: false 494 | OptionalAbstractKeyword: 495 | active: true 496 | OptionalUnit: 497 | active: false 498 | OptionalWhenBraces: 499 | active: false 500 | PreferToOverPairSyntax: 501 | active: false 502 | ProtectedMemberInFinalClass: 503 | active: false 504 | RedundantExplicitType: 505 | active: false 506 | RedundantVisibilityModifierRule: 507 | active: false 508 | ReturnCount: 509 | active: true 510 | max: 2 511 | excludedFunctions: "equals" 512 | excludeLabeled: false 513 | excludeReturnFromLambda: true 514 | excludeGuardClauses: false 515 | SafeCast: 516 | active: true 517 | SerialVersionUIDInSerializableClass: 518 | active: false 519 | SpacingBetweenPackageAndImports: 520 | active: false 521 | ThrowsCount: 522 | active: true 523 | max: 2 524 | TrailingWhitespace: 525 | active: false 526 | UnderscoresInNumericLiterals: 527 | active: false 528 | acceptableDecimalLength: 5 529 | UnnecessaryAbstractClass: 530 | active: false 531 | excludeAnnotatedClasses: "dagger.Module" 532 | UnnecessaryApply: 533 | active: false 534 | UnnecessaryInheritance: 535 | active: false 536 | UnnecessaryLet: 537 | active: false 538 | UnnecessaryParentheses: 539 | active: false 540 | UntilInsteadOfRangeTo: 541 | active: false 542 | UnusedImports: 543 | active: false 544 | UnusedPrivateClass: 545 | active: false 546 | UnusedPrivateMember: 547 | active: false 548 | allowedNames: "(_|ignored|expected|serialVersionUID)" 549 | UseArrayLiteralsInAnnotations: 550 | active: false 551 | UseCheckOrError: 552 | active: false 553 | UseDataClass: 554 | active: false 555 | excludeAnnotatedClasses: "" 556 | allowVars: false 557 | UseIfInsteadOfWhen: 558 | active: false 559 | UseRequire: 560 | active: false 561 | UselessCallOnNotNull: 562 | active: false 563 | UtilityClassWithPublicConstructor: 564 | active: false 565 | VarCouldBeVal: 566 | active: false 567 | WildcardImport: 568 | active: true 569 | excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" 570 | excludeImports: 'java.util.*,kotlinx.android.synthetic.*' 571 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /app/src/androidTest/java/dev/happysingh/template/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package dev.happysingh.template 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | import org.junit.Assert.assertEquals 6 | 7 | import org.junit.Test 8 | import org.junit.runner.RunWith 9 | 10 | /** 11 | * Instrumented test, which will execute on an Android device. 12 | * 13 | * See [testing documentation](http://d.android.com/tools/testing). 14 | */ 15 | @RunWith(AndroidJUnit4::class) 16 | class ExampleInstrumentedTest { 17 | @Test 18 | fun useAppContext() { 19 | // Context of the app under test. 20 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 21 | assertEquals("dev.happysingh.template", appContext.packageName) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /app/src/main/java/dev/happysingh/template/MainApplication.kt: -------------------------------------------------------------------------------- 1 | package dev.happysingh.template 2 | 3 | import android.app.Application 4 | 5 | class MainApplication : Application() 6 | -------------------------------------------------------------------------------- /app/src/main/java/dev/happysingh/template/ui/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package dev.happysingh.template.ui 2 | 3 | import androidx.appcompat.app.AppCompatActivity 4 | import android.os.Bundle 5 | import dev.happysingh.template.R 6 | 7 | class MainActivity : AppCompatActivity() { 8 | override fun onCreate(savedInstanceState: Bundle?) { 9 | super.onCreate(savedInstanceState) 10 | setContentView(R.layout.activity_main) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/happysingh23828/android-github-repo-template/0c79546786520c4ef05b1b22334f81797ebc3d4b/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/happysingh23828/android-github-repo-template/0c79546786520c4ef05b1b22334f81797ebc3d4b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/happysingh23828/android-github-repo-template/0c79546786520c4ef05b1b22334f81797ebc3d4b/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/happysingh23828/android-github-repo-template/0c79546786520c4ef05b1b22334f81797ebc3d4b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/happysingh23828/android-github-repo-template/0c79546786520c4ef05b1b22334f81797ebc3d4b/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/happysingh23828/android-github-repo-template/0c79546786520c4ef05b1b22334f81797ebc3d4b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/happysingh23828/android-github-repo-template/0c79546786520c4ef05b1b22334f81797ebc3d4b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/happysingh23828/android-github-repo-template/0c79546786520c4ef05b1b22334f81797ebc3d4b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/happysingh23828/android-github-repo-template/0c79546786520c4ef05b1b22334f81797ebc3d4b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/happysingh23828/android-github-repo-template/0c79546786520c4ef05b1b22334f81797ebc3d4b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/values-night/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFBB86FC 4 | #FF6200EE 5 | #FF3700B3 6 | #FF03DAC5 7 | #FF018786 8 | #FF000000 9 | #FFFFFFFF 10 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Android-Repo-Github-Template 3 | -------------------------------------------------------------------------------- /app/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | -------------------------------------------------------------------------------- /app/src/test/java/dev/happysingh/template/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package dev.happysingh.template 2 | 3 | import org.junit.Assert.assertEquals 4 | import org.junit.Test 5 | 6 | /** 7 | * Example local unit test, which will execute on the development machine (host). 8 | * 9 | * See [testing documentation](http://d.android.com/tools/testing). 10 | */ 11 | class ExampleUnitTest { 12 | @Test 13 | fun addition_isCorrect() { 14 | assertEquals(4, 2 + 2) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | import dependencies.Dependencies 2 | import dependencies.MavenUrls 3 | 4 | buildscript { 5 | repositories { 6 | google() 7 | jcenter() 8 | maven { url MavenUrls.JITPACK_IO} 9 | } 10 | dependencies { 11 | classpath Dependencies.ClassPaths.gradleClasspath 12 | classpath Dependencies.ClassPaths.kotlinGradlePluginClasspath 13 | } 14 | } 15 | 16 | allprojects { 17 | repositories { 18 | google() 19 | jcenter() 20 | maven { url MavenUrls.JITPACK_IO} 21 | } 22 | } 23 | 24 | task clean(type: Delete) { 25 | delete rootProject.buildDir 26 | } 27 | 28 | subprojects { subProject -> 29 | //this allows to run detekt or ktlint for a specific module 30 | // example usage: ./gradlew :data:ktlintFormat 31 | apply from: "$project.rootDir/app/code-quality.gradle" 32 | } -------------------------------------------------------------------------------- /buildSrc/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /buildSrc/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'kotlin' 2 | 3 | buildscript { 4 | ext.kotlin_version = '1.3.72' 5 | repositories { 6 | mavenCentral() 7 | } 8 | dependencies { 9 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 10 | } 11 | } 12 | repositories { 13 | mavenCentral() 14 | } 15 | dependencies { 16 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" 17 | } 18 | compileKotlin { 19 | kotlinOptions { 20 | jvmTarget = "1.8" 21 | } 22 | } 23 | compileTestKotlin { 24 | kotlinOptions { 25 | jvmTarget = "1.8" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /buildSrc/src/main/java/ConfigFields.kt: -------------------------------------------------------------------------------- 1 | package dependencies 2 | 3 | object ConfigFields { 4 | // add your config fields KEYS here for ex : 5 | // const val BASE_URL = "BASE_URL" 6 | } -------------------------------------------------------------------------------- /buildSrc/src/main/java/Dependencies.kt: -------------------------------------------------------------------------------- 1 | package dependencies 2 | 3 | object Dependencies { 4 | 5 | // add your modules variables here 6 | object Module { 7 | const val core = ":core" 8 | } 9 | 10 | 11 | object ClassPaths { 12 | const val gradleClasspath = "com.android.tools.build:gradle:${Version.gradleVersion}" 13 | const val googleService = "com.google.gms:google-services:${Version.googleServices}" 14 | const val firebaseCrashlytics = "com.google.firebase:firebase-crashlytics-gradle:${Version.firebaseCrashlyticsGradle}" 15 | const val navigationSafeArgs = "androidx.navigation:navigation-safe-args-gradle-plugin:${Version.navigation}" 16 | const val kotlinGradlePluginClasspath = 17 | "org.jetbrains.kotlin:kotlin-gradle-plugin:${Version.kotlinVersion}" 18 | const val dokkaPluginClasspath = 19 | "org.jetbrains.dokka:dokka-gradle-plugin:${Version.dokkaVeresion}" 20 | const val jacoco = "com.vanniktech:gradle-android-junit-jacoco-plugin:${Version.jacoco}" 21 | } 22 | 23 | object Plugins { 24 | const val ANDROID_APPLICATION = "com.android.application" 25 | const val ANDROID_LIBRARY = "com.android.library" 26 | const val GOOGLE_SERVICES = "com.google.gms.google-services" 27 | const val FIREBASE_CRASHLYTICS = "com.google.firebase.crashlytics" 28 | const val KOTLIN_ANDROID = "kotlin-android" 29 | const val KOTLIN_KAPT = "kotlin-kapt" 30 | const val KOTLIN_ANDROID_EXTENSIONS = "kotlin-android-extensions" 31 | const val SAFE_ARGS = "androidx.navigation.safeargs.kotlin" 32 | const val DETEKT = "io.gitlab.arturbosch.detekt" 33 | const val JACOCO = "com.vanniktech.android.junit.jacoco" 34 | } 35 | 36 | object Lint { 37 | const val detekt = "io.gitlab.arturbosch.detekt:detekt-cli:${Version.detektVersion}" 38 | const val ktLint = "com.pinterest:ktlint:${Version.ktLint}" 39 | } 40 | 41 | object Firebase { 42 | const val core = "com.google.firebase:firebase-core:${Version.firebaseCore}" 43 | const val analytics = "com.google.firebase:firebase-analytics:${Version.firebaseAnalytics}" 44 | const val crashlytics = "com.google.firebase:firebase-crashlytics:${Version.firebaseCrashlytics}" 45 | const val messaging = "com.google.firebase:firebase-messaging:${Version.message}" 46 | const val play_services = "com.google.android.gms:play-services-auth:${Version.play_services}" 47 | const val play_plus = "com.google.android.gms:play-services-plus:${Version.play_plus}" 48 | const val location = "com.google.android.gms:play-services-location:${Version.location}" 49 | const val remoteConfig = "com.google.firebase:firebase-config:${Version.firebaseRemoteConfig}" 50 | const val playCore = "com.google.android.play:core:${Version.playCore}" 51 | 52 | } 53 | 54 | object Lifecycle { 55 | const val extension = "android.arch.lifecycle:extensions:${Version.lifecycle}" 56 | const val annotation_compliler = "android.arch.lifecycle:compiler:${Version.lifecycle}" 57 | 58 | // ViewModel and LiveData 59 | const val lifeCycleExtension = 60 | "androidx.lifecycle:lifecycle-extensions:${Version.lifecycleVersion}" 61 | } 62 | 63 | object Glide { 64 | const val glide = "com.github.bumptech.glide:glide:${Version.glide}" 65 | const val annotationProcessor = "androidx.annotation:annotation:1.0.0" 66 | const val annotationCompiler = "com.github.bumptech.glide:compiler:${Version.glide}" 67 | } 68 | 69 | object Gson { 70 | const val gson = "com.google.code.gson:gson:${Version.gson}" 71 | } 72 | 73 | object Kotlin { 74 | const val kotlin_stdlib_jdk7 = "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${Version.kotlin}" 75 | } 76 | 77 | object OkHttp3 { 78 | const val loggingInterceptor = 79 | "com.squareup.okhttp3:logging-interceptor:${Version.retrofit_log}" 80 | const val okHttp3 = "com.squareup.okhttp3:okhttp:3.12.1" 81 | } 82 | 83 | object Retrofit2 { 84 | const val adapterRxjava2 = "com.squareup.retrofit2:adapter-rxjava2:${Version.retrofit}" 85 | const val converterGson = "com.squareup.retrofit2:converter-gson:${Version.retrofit}" 86 | const val retrofit = "com.squareup.retrofit2:retrofit:${Version.retrofit}" 87 | } 88 | 89 | object AndroidX { 90 | const val fragment = "androidx.fragment:fragment:${Version.androidx}" 91 | const val annotation = "androidx.annotation:annotation:${Version.androidx}" 92 | const val core = "androidx.core:core:${Version.androidx}" 93 | const val coreKtx = "androidx.core:core-ktx:${Version.ktx}" 94 | const val constraintlayout = 95 | "androidx.constraintlayout:constraintlayout:${Version.androidx_112}" 96 | const val materialDesign = "com.google.android.material:material:${Version.materialDesign}" 97 | const val support_recyclerview_v7 = 98 | "androidx.recyclerview:recyclerview:${Version.recyclerView}" 99 | const val appcompat = "androidx.appcompat:appcompat:${Version.androidx_100beta01}" 100 | const val vectordrawable = 101 | "androidx.vectordrawable:vectordrawable:${Version.androidx_100beta01}" 102 | const val legacySupport = "androidx.legacy:legacy-support-v4:${Version.legacySupport}" 103 | const val navigation = "androidx.navigation:navigation-fragment-ktx:${Version.navigation}" 104 | const val navigationUI = "androidx.navigation:navigation-ui-ktx:${Version.navigation}" 105 | } 106 | 107 | object Room { 108 | const val runtime = "androidx.room:room-runtime:${Version.room}" 109 | const val rxjava2 = "androidx.room:room-rxjava2:${Version.room}" 110 | const val annotationProcessor = "androidx.room:room-compiler:${Version.room}" 111 | } 112 | 113 | object RxJava { 114 | const val rxAndroid = "io.reactivex.rxjava2:rxandroid:${Version.rxAndroid}" 115 | const val rxjava2 = "io.reactivex.rxjava2:rxjava:${Version.rx}" 116 | const val rxrelay2 = "com.jakewharton.rxrelay2:rxrelay:${Version.rxRelay}" 117 | const val rxBinding = "com.jakewharton.rxbinding3:rxbinding:${Version.rxBinding}" 118 | } 119 | 120 | object Dagger { 121 | const val dagger2 = "com.google.dagger:dagger:${Version.dagger2}" 122 | const val daggerAndroid = "com.google.dagger:dagger-android:${Version.dagger2}" 123 | const val daggerAndroidSupport = 124 | "com.google.dagger:dagger-android-support:${Version.dagger2}" 125 | const val processor = "com.google.dagger:dagger-android-processor:${Version.dagger2}" 126 | const val compiler = "com.google.dagger:dagger-compiler:${Version.dagger2}" 127 | } 128 | 129 | object Test { 130 | const val test_junit = "androidx.test.ext:junit:${Version.androidXJunit}" 131 | const val android_test_espresso_core = 132 | "androidx.test.espresso:espresso-core:${Version.espresso}" 133 | const val android_test_room = "android.arch.persistence.room:testing:${Version.room}" 134 | const val testing_core_testing = "android.arch.core:core-testing:${Version.lifecycle}" 135 | const val android_test_rules = "androidx.test:rules:${Version.rules}" 136 | const val android_test_runner = "androidx.test:runner:${Version.runner}" 137 | const val mockito = "org.mockito:mockito-core:${Version.mockito}" 138 | const val mockitoInLine = "org.mockito:mockito-inline:${Version.mockitoInline}" 139 | const val mockWebServer = "com.squareup.okhttp3:mockwebserver:${Version.mockWebServer}" 140 | const val assert_j = "org.assertj:assertj-core:${Version.assertJVersion}" 141 | const val roboElectric = "org.robolectric:robolectric:${Version.roboElectric}" 142 | } 143 | 144 | object Support { 145 | const val supportV4 = "com.android.support:support-v4:${Version.supportLib}" 146 | } 147 | 148 | object ThirdPartiesLib { 149 | const val timber = "com.jakewharton.timber:timber:${Version.timber}" 150 | const val jodaTime = "joda-time:joda-time:${Version.jodaTime}" 151 | const val picasso = "com.squareup.picasso:picasso:${Version.picasso}" 152 | } 153 | 154 | const val javax = "javax.inject:javax.inject:${Version.javaxInject}" 155 | const val javaxjsr250 = "javax.annotation:jsr250-api:${Version.javaxAnnotation}" 156 | const val parceler = "org.parceler:parceler-api:${Version.parcelerVersion}" 157 | const val parcelerProcessor = "org.parceler:parceler-api:${Version.parcelerVersion}" 158 | } 159 | -------------------------------------------------------------------------------- /buildSrc/src/main/java/MavenUrls.kt: -------------------------------------------------------------------------------- 1 | package dependencies 2 | 3 | object MavenUrls { 4 | const val FABRIC_PUBLIC = "https://maven.fabric.io/public" 5 | const val JITPACK_IO = "https://jitpack.io" 6 | const val COMMONS_WARE = "https://s3.amazonaws.com/repo.commonsware.com" 7 | const val PLUGIN_GRADLE_M2 = "https://plugins.gradle.org/m2/" 8 | } 9 | -------------------------------------------------------------------------------- /buildSrc/src/main/java/Version.kt: -------------------------------------------------------------------------------- 1 | package dependencies 2 | 3 | object Version { 4 | 5 | // android configuration 6 | const val buildTools = "28.0.3" 7 | const val compileSdk = 29 8 | const val minSdk = 21 9 | const val targetSdk = 29 10 | const val versionCode = 3 11 | const val versionName = "1.0.0" 12 | 13 | // Libraries 14 | const val supportLib = "28.0.0" 15 | const val recyclerView = "1.0.0" 16 | const val androidx = "1.0.0" 17 | const val androidx_112 = "1.1.3" 18 | const val androidx_100beta01 = "1.0.0-beta01" 19 | const val assertJVersion = "3.2.0" 20 | const val circleImageView = "3.1.0" 21 | const val componentManager = "2.0.1" 22 | const val firebaseBOM = "25.12.0" 23 | const val firebaseCore = "17.4.4" 24 | const val firebaseAnalytics = "18.0.0" 25 | const val firebasePref = "19.0.8" 26 | const val firebaseCrashlytics = "17.2.1" 27 | const val firebaseCrashlyticsGradle = "2.2.1" 28 | const val firebaseRemoteConfig = "19.1.4" 29 | const val materialDesign = "1.2.1" 30 | const val mockito = "1.10.19" 31 | const val mockitoInline = "2.24.5" 32 | const val mockWebServer = "4.9.0" 33 | const val mpAndroidChart = "3.1.0" 34 | const val navigation = "2.3.0" 35 | const val dagger2 = "2.21" 36 | const val lifecycleVersion = "2.0.0" 37 | const val javaxInject = "1" 38 | const val javaxAnnotation = "1.0" 39 | const val jacoco = "0.16.0" 40 | const val jodaTime = "2.10.6" 41 | const val jvmTarget = "1.8" 42 | const val parcelerVersion = "1.1.12" 43 | const val permissionDispatcher = "4.8.0" 44 | const val permissionAnnotation = "4.8.0" 45 | const val picasso = "2.71828" 46 | const val gradleVersion = "3.5.0" 47 | const val googleServices = "4.3.3" 48 | const val ktx = "1.1.0" 49 | const val ktLint = "0.35.0" 50 | const val kotlinVersion = "1.3.50" 51 | const val legacySupport = "1.0.0" 52 | const val dokkaVeresion = "0.9.18" 53 | const val espresso = "3.1.0" 54 | const val glide = "4.9.0" 55 | const val gson = "2.8.5" 56 | const val androidXJunit = "1.1.2" 57 | const val kotlin = "1.3.31" 58 | const val lifecycle = "1.1.1" 59 | const val playCore = "1.7.0" 60 | const val reactiveNetwork = "3.0.3" 61 | const val retrofit = "2.5.0" 62 | const val retrofit_log = "3.10.0" 63 | const val roboElectric = "3.4.2" 64 | const val room = "2.2.5" 65 | const val rules = "1.1.0" 66 | const val runner = "1.1.0" 67 | const val rx = "2.1.0" 68 | const val rxAndroid = "2.0.1" 69 | const val rxRelay = "2.0.0" 70 | const val rxBinding = "3.0.0" 71 | const val stetho = "1.5.1" 72 | const val timber = "4.7.1" 73 | const val message = "21.0.0" 74 | const val play_services = "18.1.0" 75 | const val play_plus = "17.0.0" 76 | const val location = "17.1.0" 77 | const val expandableLayout = "2.9.2" 78 | const val progressWheel = "1.1.5" 79 | const val imageSlider = "1.4.0" 80 | 81 | // plugins versions 82 | const val detektVersion = "1.9.1" 83 | } 84 | -------------------------------------------------------------------------------- /core/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /core/build.gradle: -------------------------------------------------------------------------------- 1 | import dependencies.Dependencies 2 | import dependencies.Version 3 | 4 | apply plugin: Dependencies.Plugins.ANDROID_LIBRARY 5 | apply plugin: Dependencies.Plugins.KOTLIN_ANDROID 6 | apply plugin: Dependencies.Plugins.KOTLIN_ANDROID_EXTENSIONS 7 | apply plugin: Dependencies.Plugins.KOTLIN_KAPT 8 | 9 | android { 10 | compileSdkVersion Version.compileSdk 11 | buildToolsVersion Version.buildTools 12 | 13 | defaultConfig { 14 | minSdkVersion Version.minSdk 15 | targetSdkVersion Version.targetSdk 16 | versionCode Version.versionCode 17 | versionName Version.versionName 18 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 19 | consumerProguardFiles "consumer-rules.pro" 20 | } 21 | 22 | kotlinOptions { 23 | jvmTarget = Version.jvmTarget 24 | } 25 | 26 | androidExtensions { 27 | experimental = true 28 | } 29 | 30 | dataBinding { 31 | enabled = true 32 | } 33 | 34 | buildTypes { 35 | release { 36 | minifyEnabled false 37 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 38 | } 39 | 40 | debug { 41 | debuggable true 42 | } 43 | } 44 | 45 | } 46 | 47 | dependencies { 48 | implementation fileTree(dir: "libs", include: ["*.jar"]) 49 | implementation Dependencies.Kotlin.kotlin_stdlib_jdk7 50 | 51 | //Android X 52 | implementation Dependencies.AndroidX.appcompat 53 | implementation Dependencies.AndroidX.materialDesign 54 | implementation Dependencies.AndroidX.constraintlayout 55 | implementation Dependencies.AndroidX.vectordrawable 56 | implementation Dependencies.AndroidX.core 57 | 58 | //Testing 59 | testImplementation Dependencies.Test.test_junit 60 | androidTestImplementation Dependencies.Test.android_test_espresso_core 61 | 62 | //LifeCycle 63 | implementation Dependencies.Lifecycle.lifeCycleExtension 64 | } -------------------------------------------------------------------------------- /core/consumer-rules.pro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/happysingh23828/android-github-repo-template/0c79546786520c4ef05b1b22334f81797ebc3d4b/core/consumer-rules.pro -------------------------------------------------------------------------------- /core/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /core/src/androidTest/java/dev/happysingh/core/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package dev.happysingh.core 2 | 3 | import androidx.test.ext.junit.runners.AndroidJUnit4 4 | import androidx.test.platform.app.InstrumentationRegistry 5 | import org.junit.Assert.assertEquals 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | /** 10 | * Instrumented test, which will execute on an Android device. 11 | * 12 | * See [testing documentation](http://d.android.com/tools/testing). 13 | */ 14 | @RunWith(AndroidJUnit4::class) 15 | class ExampleInstrumentedTest { 16 | @Test 17 | fun useAppContext() { 18 | // Context of the app under test. 19 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 20 | assertEquals("dev.happysingh.core.test", appContext.packageName) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /core/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | -------------------------------------------------------------------------------- /core/src/main/java/dev/happysingh/core/base/BaseActivity.kt: -------------------------------------------------------------------------------- 1 | package dev.happysingh.core.base 2 | 3 | import androidx.appcompat.app.AppCompatActivity 4 | 5 | abstract class BaseActivity : AppCompatActivity() 6 | -------------------------------------------------------------------------------- /core/src/main/java/dev/happysingh/core/base/BaseFragment.kt: -------------------------------------------------------------------------------- 1 | package dev.happysingh.core.base 2 | 3 | import androidx.fragment.app.Fragment 4 | 5 | abstract class BaseFragment : Fragment() 6 | -------------------------------------------------------------------------------- /core/src/main/java/dev/happysingh/core/ext/ActivityExt.kt: -------------------------------------------------------------------------------- 1 | package dev.happysingh.core.ext 2 | 3 | import android.app.Activity 4 | import android.view.WindowManager 5 | import android.view.inputmethod.InputMethodManager 6 | import android.widget.EditText 7 | import android.widget.Toast 8 | import androidx.annotation.StyleRes 9 | import androidx.appcompat.app.AppCompatActivity 10 | import androidx.fragment.app.Fragment 11 | import com.google.android.material.dialog.MaterialAlertDialogBuilder 12 | import com.google.android.material.snackbar.Snackbar 13 | import dev.happysingh.core.R 14 | 15 | fun AppCompatActivity.disableScreenCapture(buildType: String) { 16 | this.window.setFlags( 17 | WindowManager.LayoutParams.FLAG_SECURE, 18 | WindowManager.LayoutParams.FLAG_SECURE 19 | ) 20 | } 21 | 22 | fun Activity.hideInputMethod() = 23 | run { 24 | val inputMethodManager: InputMethodManager = 25 | getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager 26 | window.peekDecorView() 27 | ?.let { 28 | inputMethodManager.hideSoftInputFromWindow( 29 | window.peekDecorView().windowToken, 30 | 0 31 | ) 32 | } 33 | } 34 | 35 | fun Activity.showInputMethod(v: EditText) = 36 | run { 37 | val inputMethodManager: InputMethodManager = 38 | getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager 39 | v.requestFocus() 40 | inputMethodManager.showSoftInput(v, InputMethodManager.SHOW_FORCED) 41 | } 42 | 43 | /** 44 | * This showToast fun can be called from Activity 45 | */ 46 | fun AppCompatActivity.showToast(message: String?) { 47 | Toast.makeText(this, message, Toast.LENGTH_SHORT).show() 48 | } 49 | 50 | /** 51 | * This showSnackBar fun can be called from Activity 52 | */ 53 | fun AppCompatActivity.showSnackBar(message: String) { 54 | val mParentView = this.window.decorView.rootView 55 | if (mParentView != null) { 56 | Snackbar.make(mParentView, message, Snackbar.LENGTH_LONG).show() 57 | } 58 | } 59 | 60 | fun AppCompatActivity.openSelectionDialog( 61 | title: String, 62 | arrayOfString: Array, 63 | onPositionSelect: (Int) -> Unit, 64 | @StyleRes style: Int = 0 65 | ) { 66 | 67 | MaterialAlertDialogBuilder(this, style) 68 | .setTitle(title) 69 | .setItems(arrayOfString) { dialog, which -> 70 | onPositionSelect.invoke(which) 71 | dialog.dismiss() 72 | }.show() 73 | } 74 | 75 | fun AppCompatActivity.openConfirmDialog( 76 | msg: String, 77 | onYesClick: () -> Unit, 78 | onNoClick: () -> Unit 79 | ) { 80 | MaterialAlertDialogBuilder(this) 81 | .setMessage(msg) 82 | .setPositiveButton(getString(R.string.yes)) { dialog, _ -> 83 | onYesClick.invoke() 84 | dialog.dismiss() 85 | } 86 | .setNegativeButton(getString(R.string.no)) { dialog, _ -> 87 | onNoClick.invoke() 88 | dialog.dismiss() 89 | }.show() 90 | } 91 | 92 | fun AppCompatActivity.getScreenOrientation(): ScreenOrientation { 93 | return when (resources.configuration.orientation) { 94 | ScreenOrientation.PORTRAIT.value -> ScreenOrientation.PORTRAIT 95 | ScreenOrientation.LANDSCAPE.value -> ScreenOrientation.LANDSCAPE 96 | else -> ScreenOrientation.PORTRAIT 97 | } 98 | } 99 | 100 | fun AppCompatActivity.addFragment(layoutId: Int, fragment: Fragment) { 101 | this.supportFragmentManager.beginTransaction().add(layoutId, fragment).commit() 102 | } 103 | 104 | fun AppCompatActivity.replaceFragment(layoutId: Int, fragment: Fragment) { 105 | this.supportFragmentManager.beginTransaction().replace(layoutId, fragment).commit() 106 | } 107 | 108 | fun AppCompatActivity.addFragmentWithBackStack(layoutId: Int, fragment: Fragment, tag: String) { 109 | this.supportFragmentManager.beginTransaction().add(layoutId, fragment) 110 | .addToBackStack(tag).commit() 111 | } 112 | -------------------------------------------------------------------------------- /core/src/main/java/dev/happysingh/core/ext/AndroidApiExt.kt: -------------------------------------------------------------------------------- 1 | package dev.happysingh.core.ext 2 | 3 | import android.os.Build 4 | 5 | inline fun aboveApi(api: Int, included: Boolean = false, block: () -> Unit) { 6 | if (Build.VERSION.SDK_INT > if (included) api - 1 else api) { 7 | block() 8 | } 9 | } 10 | 11 | inline fun belowApi(api: Int, included: Boolean = false, block: () -> Unit) { 12 | if (Build.VERSION.SDK_INT < if (included) api + 1 else api) { 13 | block() 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /core/src/main/java/dev/happysingh/core/ext/BitmapExt.kt: -------------------------------------------------------------------------------- 1 | package dev.happysingh.core.ext 2 | 3 | import android.graphics.Bitmap 4 | import android.graphics.Matrix 5 | import android.util.Base64 6 | import java.io.ByteArrayOutputStream 7 | import java.io.File 8 | import java.io.FileOutputStream 9 | 10 | private const val BITMAP_QUALITY = 100 11 | fun Bitmap.toBase64(compressFormat: Bitmap.CompressFormat = Bitmap.CompressFormat.PNG): String { 12 | val result: String 13 | val baos = ByteArrayOutputStream() 14 | compress(compressFormat, BITMAP_QUALITY, baos) 15 | baos.flush() 16 | baos.close() 17 | val bitmapBytes = baos.toByteArray() 18 | result = Base64.encodeToString(bitmapBytes, Base64.DEFAULT) 19 | baos.flush() 20 | baos.close() 21 | return result 22 | } 23 | 24 | fun Bitmap.resize(w: Number, h: Number): Bitmap { 25 | val width = width 26 | val height = height 27 | val scaleWidth = w.toFloat() / width 28 | val scaleHeight = h.toFloat() / height 29 | val matrix = Matrix() 30 | matrix.postScale(scaleWidth, scaleHeight) 31 | if (width > 0 && height > 0) { 32 | return Bitmap.createBitmap(this, 0, 0, width, height, matrix, true) 33 | } 34 | return this 35 | } 36 | 37 | fun Bitmap.saveFile(path: String, compressFormat: Bitmap.CompressFormat = Bitmap.CompressFormat.PNG) { 38 | val f = File(path) 39 | if (!f.exists()) { 40 | f.createNewFile() 41 | } 42 | val stream = FileOutputStream(f) 43 | compress(compressFormat, BITMAP_QUALITY, stream) 44 | stream.flush() 45 | stream.close() 46 | } 47 | 48 | fun Bitmap.toByteArray(compressFormat: Bitmap.CompressFormat = Bitmap.CompressFormat.PNG): ByteArray { 49 | val stream = ByteArrayOutputStream() 50 | compress(compressFormat, BITMAP_QUALITY, stream) 51 | return stream.toByteArray() 52 | } 53 | -------------------------------------------------------------------------------- /core/src/main/java/dev/happysingh/core/ext/CommonViewExt.kt: -------------------------------------------------------------------------------- 1 | package dev.happysingh.core.ext 2 | 3 | import android.view.View 4 | import android.view.ViewGroup 5 | import com.google.android.material.snackbar.Snackbar 6 | 7 | operator fun ViewGroup.get(index: Int): View { 8 | return getChildAt(index) 9 | } 10 | 11 | fun View.visible() { 12 | if (visibility != View.VISIBLE) { 13 | visibility = View.VISIBLE 14 | } 15 | } 16 | 17 | inline fun View.visibleIf(block: () -> Boolean) { 18 | if (visibility != View.VISIBLE && block()) { 19 | visibility = View.VISIBLE 20 | } 21 | } 22 | 23 | fun View.invisible() { 24 | if (visibility != View.INVISIBLE) { 25 | visibility = View.INVISIBLE 26 | } 27 | } 28 | 29 | inline fun View.invisiableIf(block: () -> Boolean) { 30 | if (visibility != View.INVISIBLE && block()) { 31 | visibility = View.INVISIBLE 32 | } 33 | } 34 | 35 | fun View.gone() { 36 | if (visibility != View.GONE) { 37 | visibility = View.GONE 38 | } 39 | } 40 | 41 | inline fun View.goneIf(block: () -> Boolean) { 42 | if (visibility != View.GONE && block()) { 43 | visibility = View.GONE 44 | } 45 | } 46 | 47 | fun View.isVisible() = visibility == View.VISIBLE 48 | 49 | fun View.isGone() = visibility == View.GONE 50 | 51 | fun View.isInvisible() = visibility == View.INVISIBLE 52 | 53 | /** 54 | * This showSnackBar fun can be called from any view 55 | */ 56 | fun View.showSnackBar(message: String) { 57 | Snackbar.make(this, message, Snackbar.LENGTH_LONG).show() 58 | } 59 | -------------------------------------------------------------------------------- /core/src/main/java/dev/happysingh/core/ext/CoreExt.kt: -------------------------------------------------------------------------------- 1 | package dev.happysingh.core.ext 2 | 3 | import android.content.Context 4 | import android.content.Intent 5 | import android.content.res.Configuration 6 | import android.graphics.Paint 7 | import android.net.Uri 8 | import android.provider.Settings 9 | import android.view.View 10 | import android.view.ViewGroup 11 | import android.widget.Toast 12 | import androidx.annotation.ColorRes 13 | import androidx.appcompat.widget.AppCompatTextView 14 | import androidx.core.content.ContextCompat 15 | import androidx.core.graphics.ColorUtils 16 | import com.google.android.material.dialog.MaterialAlertDialogBuilder 17 | import kotlin.math.roundToInt 18 | 19 | fun isAllValuesNull(vararg items: Int?): Boolean { 20 | for (item in items) { 21 | if (item == null) { 22 | return true 23 | } 24 | } 25 | return false 26 | } 27 | 28 | fun isAllValuesNull(vararg items: String?): Boolean { 29 | for (item in items) { 30 | if (item == null) { 31 | return true 32 | } 33 | } 34 | return false 35 | } 36 | 37 | fun isAllValuesNull(vararg items: Any?): Boolean { 38 | for (item in items) { 39 | if (item == null) { 40 | return true 41 | } 42 | } 43 | return false 44 | } 45 | 46 | fun Float.roundToOneDecimalPlace(): Float { 47 | return String.format("%.2f", this).toFloat() 48 | } 49 | 50 | fun Context.shareIntent(msg: String) { 51 | val shareIntent = Intent() 52 | shareIntent.action = Intent.ACTION_SEND 53 | shareIntent.type = "text/plain" 54 | shareIntent.putExtra(Intent.EXTRA_TEXT, msg) 55 | startActivity(Intent.createChooser(shareIntent, "Share to")) 56 | } 57 | 58 | fun Context.openSettings() { 59 | val intent = Intent( 60 | Settings.ACTION_APPLICATION_DETAILS_SETTINGS, 61 | Uri.fromParts("package", packageName, null) 62 | ) 63 | intent.addCategory(Intent.CATEGORY_DEFAULT) 64 | intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) 65 | startActivity(intent) 66 | } 67 | 68 | inline var AppCompatTextView.strike: Boolean 69 | set(visible) { 70 | paintFlags = if (visible) paintFlags or Paint.STRIKE_THRU_TEXT_FLAG 71 | else paintFlags and Paint.STRIKE_THRU_TEXT_FLAG.inv() 72 | } 73 | get() = paintFlags and Paint.STRIKE_THRU_TEXT_FLAG == Paint.STRIKE_THRU_TEXT_FLAG 74 | 75 | fun Context.openCustomizedConfirmDialog( 76 | msg: String, 77 | positiveButtonText: String, 78 | negativeButtonText: String, 79 | onYesClick: () -> Unit, 80 | onNoClick: () -> Unit 81 | ) { 82 | MaterialAlertDialogBuilder(this) 83 | .setMessage(msg) 84 | .setPositiveButton(positiveButtonText) { dialog, _ -> 85 | onYesClick.invoke() 86 | dialog.dismiss() 87 | } 88 | .setNegativeButton(negativeButtonText) { dialog, _ -> 89 | onNoClick.invoke() 90 | dialog.dismiss() 91 | }.show() 92 | } 93 | 94 | // send alpha under from 0.0f to 1.0f. 95 | private const val RGB_MAX_VALUE = 255 96 | fun Context.getAlphaColor(@ColorRes color: Int, alpha: Float): Int { 97 | return ColorUtils.setAlphaComponent( 98 | ContextCompat.getColor(this, color), 99 | RGB_MAX_VALUE.times(alpha).roundToInt() 100 | ) 101 | } 102 | 103 | /** 104 | * This showToast fun can be called from context object 105 | */ 106 | fun Context.showToast(message: String?) { 107 | Toast.makeText(this, message, Toast.LENGTH_SHORT).show() 108 | } 109 | 110 | enum class ScreenOrientation(val value: Int) { 111 | PORTRAIT(Configuration.ORIENTATION_PORTRAIT), 112 | LANDSCAPE(Configuration.ORIENTATION_LANDSCAPE) 113 | } 114 | 115 | inline val ViewGroup.children: List 116 | get() = (0 until childCount).map { getChildAt(it) } 117 | -------------------------------------------------------------------------------- /core/src/main/java/dev/happysingh/core/ext/FileExt.kt: -------------------------------------------------------------------------------- 1 | package dev.happysingh.core.ext 2 | 3 | import java.io.ByteArrayOutputStream 4 | import java.io.File 5 | import java.io.FileInputStream 6 | import java.io.FileOutputStream 7 | import java.io.IOException 8 | import java.nio.channels.FileChannel 9 | 10 | fun File.copy(dest: File) { 11 | var fi: FileInputStream? = null 12 | var fo: FileOutputStream? = null 13 | var ic: FileChannel? = null 14 | var oc: FileChannel? = null 15 | try { 16 | if (!dest.exists()) { 17 | dest.createNewFile() 18 | } 19 | fi = FileInputStream(this) 20 | fo = FileOutputStream(dest) 21 | ic = fi.channel 22 | oc = fo.channel 23 | ic.transferTo(0, ic.size(), oc) 24 | } catch (e: IOException) { 25 | e.printStackTrace() 26 | } finally { 27 | fi?.close() 28 | fo?.close() 29 | ic?.close() 30 | oc?.close() 31 | } 32 | } 33 | 34 | fun File.move(dest: File) { 35 | copy(dest) 36 | delete() 37 | } 38 | 39 | fun File.copyDirectory(dest: File) { 40 | if (!dest.exists()) { 41 | dest.mkdirs() 42 | } 43 | val files = listFiles() 44 | files?.forEach { 45 | if (it.isFile) { 46 | it.copy(File("${dest.absolutePath}/${it.name}")) 47 | } 48 | if (it.isDirectory) { 49 | val dirSrc = File("$absolutePath/${it.name}") 50 | val dirDest = File("${dest.absolutePath}/${it.name}") 51 | dirSrc.copyDirectory(dirDest) 52 | } 53 | } 54 | } 55 | 56 | fun File.moveDirectory(dest: File) { 57 | copyDirectory(dest) 58 | deleteAll() 59 | } 60 | 61 | fun File.deleteAll() { 62 | if (isFile && exists()) { 63 | delete() 64 | return 65 | } 66 | if (isDirectory) { 67 | val files = listFiles() 68 | if (files == null || files.isEmpty()) { 69 | delete() 70 | return 71 | } 72 | files.forEach { it.deleteAll() } 73 | delete() 74 | } 75 | } 76 | 77 | private const val ONE_MB = 1024 78 | fun File.toByteArray(): ByteArray { 79 | val bos = ByteArrayOutputStream(this.length().toInt()) 80 | val input = FileInputStream(this) 81 | val size = ONE_MB 82 | val buffer = ByteArray(size) 83 | var len = input.read(buffer, 0, size) 84 | while (len != -1) { 85 | bos.write(buffer, 0, len) 86 | len = input.read(buffer, 0, size) 87 | } 88 | input.close() 89 | bos.close() 90 | return bos.toByteArray() 91 | } 92 | -------------------------------------------------------------------------------- /core/src/main/java/dev/happysingh/core/ext/FragmentExt.kt: -------------------------------------------------------------------------------- 1 | package dev.happysingh.core.ext 2 | 3 | import android.widget.Toast 4 | import androidx.annotation.StyleRes 5 | import androidx.fragment.app.Fragment 6 | import com.google.android.material.dialog.MaterialAlertDialogBuilder 7 | import com.google.android.material.snackbar.Snackbar 8 | 9 | /** 10 | * This showToast fun can be called from fragment 11 | */ 12 | fun Fragment.showToast(message: String?) { 13 | Toast.makeText(this.activity, message, Toast.LENGTH_SHORT).show() 14 | } 15 | 16 | /** 17 | * This showSnackBar fun can be called from fragment 18 | */ 19 | fun Fragment.showSnackBar(message: String) { 20 | val mParentView = requireActivity().window.decorView.rootView 21 | if (mParentView != null) { 22 | Snackbar.make(mParentView, message, Snackbar.LENGTH_LONG).show() 23 | } 24 | } 25 | 26 | fun Fragment.openSelectionDialog( 27 | title: String, 28 | arrayOfString: Array, 29 | onPositionSelect: (Int) -> Unit, 30 | @StyleRes style: Int = 0 31 | ) { 32 | 33 | MaterialAlertDialogBuilder(requireContext(), style) 34 | .setTitle(title) 35 | .setItems(arrayOfString) { dialog, which -> 36 | onPositionSelect.invoke(which) 37 | dialog.dismiss() 38 | }.show() 39 | } 40 | 41 | fun Fragment.getScreenOrientation(): ScreenOrientation { 42 | return when (resources.configuration.orientation) { 43 | ScreenOrientation.PORTRAIT.value -> ScreenOrientation.PORTRAIT 44 | ScreenOrientation.LANDSCAPE.value -> ScreenOrientation.LANDSCAPE 45 | else -> ScreenOrientation.PORTRAIT 46 | } 47 | } 48 | 49 | fun Fragment.addFragment(layoutId: Int, fragment: Fragment) { 50 | this.requireFragmentManager().beginTransaction().add(layoutId, fragment).commit() 51 | } 52 | 53 | fun Fragment.addChildFragment(layoutId: Int, fragment: Fragment) { 54 | this.childFragmentManager.beginTransaction().add(layoutId, fragment).commit() 55 | } 56 | 57 | fun Fragment.addFragmentBackStack(layoutId: Int, fragment: Fragment, tag: String) { 58 | this.fragmentManager?.beginTransaction()?.add(layoutId, fragment)?.addToBackStack(tag) 59 | ?.commit() 60 | } 61 | 62 | fun Fragment.replaceFragment(layoutId: Int, fragment: Fragment) { 63 | this.requireFragmentManager().beginTransaction().replace(layoutId, fragment).commit() 64 | } 65 | 66 | fun Fragment.replaceChildFragment(layoutId: Int, fragment: Fragment) { 67 | if (isAdded) { 68 | this.childFragmentManager.beginTransaction().replace(layoutId, fragment) 69 | .commitAllowingStateLoss() 70 | } 71 | } 72 | 73 | fun Fragment.replaceFragmentBackStack(layoutId: Int, fragment: Fragment, tag: String) { 74 | this.requireFragmentManager().beginTransaction().replace(layoutId, fragment).addToBackStack(tag) 75 | .commit() 76 | } 77 | -------------------------------------------------------------------------------- /core/src/main/java/dev/happysingh/core/ext/SpecificViewExt.kt: -------------------------------------------------------------------------------- 1 | package dev.happysingh.core.ext 2 | 3 | import android.graphics.Paint 4 | import android.text.method.ReplacementTransformationMethod 5 | import android.widget.RadioButton 6 | import android.widget.RadioGroup 7 | import android.widget.ScrollView 8 | import androidx.appcompat.widget.AppCompatButton 9 | import androidx.appcompat.widget.AppCompatEditText 10 | import androidx.appcompat.widget.AppCompatTextView 11 | 12 | fun AppCompatTextView.underLine() { 13 | paint.flags = paint.flags or Paint.UNDERLINE_TEXT_FLAG 14 | paint.isAntiAlias = true 15 | } 16 | 17 | fun AppCompatTextView.deleteLine() { 18 | paint.flags = paint.flags or Paint.STRIKE_THRU_TEXT_FLAG 19 | paint.isAntiAlias = true 20 | } 21 | 22 | fun AppCompatTextView.bold(isBold: Boolean = true) { 23 | paint.isFakeBoldText = isBold 24 | paint.isAntiAlias = true 25 | } 26 | 27 | var AppCompatEditText.value: String 28 | get() = text.toString() 29 | set(value) = setText(value) 30 | 31 | fun AppCompatEditText.uppercase() { 32 | transformationMethod = object : ReplacementTransformationMethod() { 33 | private val lower = charArrayOf('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 34 | 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z') 35 | private val upper = charArrayOf('A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 36 | 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z') 37 | 38 | override fun getOriginal() = lower 39 | 40 | override fun getReplacement() = upper 41 | } 42 | } 43 | 44 | fun AppCompatEditText.lowercase() { 45 | transformationMethod = object : ReplacementTransformationMethod() { 46 | private val lower = charArrayOf('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 47 | 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z') 48 | private val upper = charArrayOf('A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 49 | 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z') 50 | 51 | override fun getOriginal() = upper 52 | 53 | override fun getReplacement() = lower 54 | } 55 | } 56 | 57 | var RadioGroup.checkedIndex: Int 58 | get() = (0 until childCount).firstOrNull { (getChildAt(it) as RadioButton).isChecked } ?: -1 59 | set(value) { 60 | if (value !in 0 until childCount) { 61 | children.map { it as RadioButton }.filter { it.isChecked } 62 | .forEach { it.isChecked = false } 63 | } else { 64 | (getChildAt(value) as RadioButton).isChecked = true 65 | } 66 | } 67 | 68 | private const val HALF_ALPHA = 0.5f 69 | private const val NO_ALPHA = 1f 70 | fun AppCompatButton.disableAlpha() { 71 | this.isClickable = false 72 | this.isEnabled = false 73 | this.alpha = HALF_ALPHA 74 | } 75 | 76 | fun AppCompatButton.enableWithDefaultAlpha() { 77 | this.isClickable = true 78 | this.isEnabled = true 79 | this.alpha = NO_ALPHA 80 | } 81 | 82 | fun AppCompatTextView.disableAlpha() { 83 | this.isClickable = false 84 | this.isEnabled = false 85 | this.alpha = HALF_ALPHA 86 | } 87 | 88 | fun AppCompatTextView.enableWithDefaultAlpha() { 89 | this.isClickable = true 90 | this.isEnabled = true 91 | this.alpha = NO_ALPHA 92 | } 93 | 94 | fun AppCompatEditText.disableAlpha() { 95 | alpha = HALF_ALPHA 96 | isFocusable = false 97 | isFocusableInTouchMode = false 98 | } 99 | 100 | fun AppCompatEditText.enableWithDefaultAlpha() { 101 | alpha = NO_ALPHA 102 | isFocusable = true 103 | isFocusableInTouchMode = true 104 | } 105 | 106 | fun ScrollView.scrollToBottom() { 107 | val lastChild = getChildAt(childCount - 1) 108 | val bottom = lastChild.bottom + paddingBottom 109 | val delta = bottom - (scrollY + height) 110 | smoothScrollBy(0, delta) 111 | } 112 | -------------------------------------------------------------------------------- /core/src/main/java/dev/happysingh/core/ext/StringExt.kt: -------------------------------------------------------------------------------- 1 | package dev.happysingh.core.ext 2 | 3 | import android.os.Build 4 | import android.text.Editable 5 | import android.text.Html 6 | import android.text.Spanned 7 | import android.text.TextUtils 8 | import java.util.regex.Pattern 9 | 10 | fun String?.isValid(): Boolean { 11 | return this != null && this.isNotEmpty() && this.isNotBlank() 12 | } 13 | 14 | fun String?.isNotValid(): Boolean { 15 | return !this.isValid() 16 | } 17 | 18 | fun String.space(string: String?): String { 19 | return this.plus(" ${string ?: ""}") 20 | } 21 | 22 | fun String.newLineWithBullet(): String { 23 | return "".plus("\n \n").plus("\u25cf ").plus(this) 24 | } 25 | 26 | fun String.toBoldHtml(): String { 27 | return "".plus(this).plus("") 28 | } 29 | 30 | fun String.toSpannedHtml(): Spanned { 31 | return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { 32 | Html.fromHtml(this, Html.FROM_HTML_MODE_COMPACT) 33 | } else { 34 | Html.fromHtml(this) 35 | } 36 | } 37 | 38 | fun String.toFormattedInt(): String { 39 | return if (this.contains(".00")) { 40 | this.toDouble().toInt().toString() 41 | } else 42 | this.toDouble().toString() 43 | } 44 | 45 | private val emailRegex = Pattern.compile( 46 | "[a-zA-Z0-9\\+\\.\\_\\%\\-\\+]{1,256}" + 47 | "\\@" + 48 | "[a-zA-Z0-9][a-zA-Z0-9\\-]{0,64}" + 49 | "(" + 50 | "\\." + 51 | "[a-zA-Z0-9][a-zA-Z0-9\\-]{0,25}" + 52 | ")+" 53 | ) 54 | 55 | fun String.isEmail(): Boolean { 56 | return this.isValid() && emailRegex.matcher(this.trimEnd().trimStart()).matches() 57 | } 58 | 59 | private const val MIN_LENGTH_PHONE = 7 60 | private const val MAX_LENGTH_PHONE = 13 61 | fun String.isPhoneNumber(): Boolean { 62 | val minLength = MIN_LENGTH_PHONE 63 | val maxLength = MAX_LENGTH_PHONE 64 | return this.isValid() && IntRange(minLength, maxLength).contains(this.trimEnd().trimStart().length) 65 | } 66 | 67 | fun String.toEditable(): Editable = Editable.Factory.getInstance().newEditable(this) 68 | 69 | fun String.toHtmlString(): Spanned { 70 | return Html.fromHtml(this) 71 | } 72 | 73 | fun String?.fromHtmlToPlainText(): String? { 74 | 75 | return if (this != null) { 76 | val text = this.replace("\n", "
") 77 | val spanned = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { 78 | Html.fromHtml(text, Html.FROM_HTML_MODE_COMPACT) 79 | } else { 80 | Html.fromHtml(text) 81 | } 82 | 83 | val chars = CharArray(spanned.length) 84 | TextUtils.getChars(spanned, 0, spanned.length, chars, 0) 85 | return String(chars) 86 | } else { 87 | null 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /core/src/main/java/dev/happysingh/core/ext/WebViewExt.kt: -------------------------------------------------------------------------------- 1 | package dev.happysingh.core.ext 2 | 3 | import android.webkit.WebChromeClient 4 | import android.webkit.WebResourceError 5 | import android.webkit.WebResourceRequest 6 | import android.webkit.WebView 7 | import android.webkit.WebViewClient 8 | 9 | /** 10 | * returns lambda if [WebView] is successfully loaded. 11 | */ 12 | private const val MAX_PROGRESS = 100 13 | fun WebView.doOnPageLoad(onWebPageLoad: () -> Unit) { 14 | webChromeClient = object : WebChromeClient() { 15 | 16 | override fun onProgressChanged(view: WebView?, newProgress: Int) { 17 | super.onProgressChanged(view, newProgress) 18 | if (progress == MAX_PROGRESS) { 19 | onWebPageLoad.invoke() 20 | } 21 | } 22 | } 23 | } 24 | 25 | /** 26 | * returns lambda if [WebView] is gets an error while loading loaded. 27 | */ 28 | fun WebView.doOnPageLoadError(onError: () -> Unit) { 29 | webViewClient = object : WebViewClient() { 30 | 31 | override fun onReceivedError( 32 | view: WebView?, 33 | request: WebResourceRequest?, 34 | error: WebResourceError? 35 | ) { 36 | super.onReceivedError(view, request, error) 37 | onError.invoke() 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /core/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | #FFEBEE 9 | #FFCDD2 10 | #EF9A9A 11 | #E57373 12 | #EF5350 13 | #F44336 14 | #E53935 15 | #D32F2F 16 | #C62828 17 | #B71C1C 18 | #FF8A80 19 | #FF5252 20 | #FF1744 21 | #D50000 22 | 23 | 24 | #FCE4EC 25 | #F8BBD0 26 | #F48FB1 27 | #F06292 28 | #EC407A 29 | #E91E63 30 | #D81B60 31 | #C2185B 32 | #AD1457 33 | #880E4F 34 | #FF80AB 35 | #FF4081 36 | #F50057 37 | #C51162 38 | 39 | 40 | #F3E5F5 41 | #E1BEE7 42 | #CE93D8 43 | #BA68C8 44 | #AB47BC 45 | #9C27B0 46 | #8E24AA 47 | #7B1FA2 48 | #6A1B9A 49 | #4A148C 50 | #EA80FC 51 | #E040FB 52 | #D500F9 53 | #AA00FF 54 | 55 | 56 | #EDE7F6 57 | #D1C4E9 58 | #B39DDB 59 | #9575CD 60 | #7E57C2 61 | #673AB7 62 | #5E35B1 63 | #512DA8 64 | #4527A0 65 | #311B92 66 | #B388FF 67 | #7C4DFF 68 | #651FFF 69 | #6200EA 70 | 71 | 72 | #E8EAF6 73 | #C5CAE9 74 | #9FA8DA 75 | #7986CB 76 | #5C6BC0 77 | #3F51B5 78 | #3949AB 79 | #303F9F 80 | #283593 81 | #1A237E 82 | #8C9EFF 83 | #536DFE 84 | #3D5AFE 85 | #304FFE 86 | 87 | 88 | #E3F2FD 89 | #BBDEFB 90 | #90CAF9 91 | #64B5F6 92 | #42A5F5 93 | #2196F3 94 | #1E88E5 95 | #1976D2 96 | #1565C0 97 | #0D47A1 98 | #82B1FF 99 | #448AFF 100 | #2979FF 101 | #2962FF 102 | 103 | 104 | #E1F5FE 105 | #B3E5FC 106 | #81D4fA 107 | #4fC3F7 108 | #29B6FC 109 | #03A9F4 110 | #039BE5 111 | #0288D1 112 | #0277BD 113 | #01579B 114 | #80D8FF 115 | #40C4FF 116 | #00B0FF 117 | #0091EA 118 | 119 | 120 | #E0F7FA 121 | #B2EBF2 122 | #80DEEA 123 | #4DD0E1 124 | #26C6DA 125 | #00BCD4 126 | #00ACC1 127 | #0097A7 128 | #00838F 129 | #006064 130 | #84FFFF 131 | #18FFFF 132 | #00E5FF 133 | #00B8D4 134 | 135 | 136 | #E0F2F1 137 | #B2DFDB 138 | #80CBC4 139 | #4DB6AC 140 | #26A69A 141 | #009688 142 | #00897B 143 | #00796B 144 | #00695C 145 | #004D40 146 | #A7FFEB 147 | #64FFDA 148 | #1DE9B6 149 | #00BFA5 150 | 151 | 152 | #E8F5E9 153 | #C8E6C9 154 | #A5D6A7 155 | #81C784 156 | #66BB6A 157 | #4CAF50 158 | #43A047 159 | #388E3C 160 | #2E7D32 161 | #1B5E20 162 | #B9F6CA 163 | #69F0AE 164 | #00E676 165 | #00C853 166 | 167 | 168 | #F1F8E9 169 | #DCEDC8 170 | #C5E1A5 171 | #AED581 172 | #9CCC65 173 | #8BC34A 174 | #7CB342 175 | #689F38 176 | #558B2F 177 | #33691E 178 | #CCFF90 179 | #B2FF59 180 | #76FF03 181 | #64DD17 182 | 183 | 184 | #F9FBE7 185 | #F0F4C3 186 | #E6EE9C 187 | #DCE775 188 | #D4E157 189 | #CDDC39 190 | #C0CA33 191 | #A4B42B 192 | #9E9D24 193 | #827717 194 | #F4FF81 195 | #EEFF41 196 | #C6FF00 197 | #AEEA00 198 | 199 | 200 | #FFFDE7 201 | #FFF9C4 202 | #FFF590 203 | #FFF176 204 | #FFEE58 205 | #FFEB3B 206 | #FDD835 207 | #FBC02D 208 | #F9A825 209 | #F57F17 210 | #FFFF82 211 | #FFFF00 212 | #FFEA00 213 | #FFD600 214 | 215 | 216 | #FFF8E1 217 | #FFECB3 218 | #FFE082 219 | #FFD54F 220 | #FFCA28 221 | #FFC107 222 | #FFB300 223 | #FFA000 224 | #FF8F00 225 | #FF6F00 226 | #FFE57F 227 | #FFD740 228 | #FFC400 229 | #FFAB00 230 | 231 | 232 | #FFF3E0 233 | #FFE0B2 234 | #FFCC80 235 | #FFB74D 236 | #FFA726 237 | #FF9800 238 | #FB8C00 239 | #F57C00 240 | #EF6C00 241 | #E65100 242 | #FFD180 243 | #FFAB40 244 | #FF9100 245 | #FF6D00 246 | 247 | 248 | #FBE9A7 249 | #FFCCBC 250 | #FFAB91 251 | #FF8A65 252 | #FF7043 253 | #FF5722 254 | #F4511E 255 | #E64A19 256 | #D84315 257 | #BF360C 258 | #FF9E80 259 | #FF6E40 260 | #FF3D00 261 | #DD2600 262 | 263 | 264 | #EFEBE9 265 | #D7CCC8 266 | #BCAAA4 267 | #A1887F 268 | #8D6E63 269 | #795548 270 | #6D4C41 271 | #5D4037 272 | #4E342E 273 | #3E2723 274 | 275 | 276 | #FAFAFA 277 | #F5F5F5 278 | #EEEEEE 279 | #E0E0E0 280 | #BDBDBD 281 | #9E9E9E 282 | #757575 283 | #616161 284 | #424242 285 | #212121 286 | 287 | 288 | #ECEFF1 289 | #CFD8DC 290 | #B0BBC5 291 | #90A4AE 292 | #78909C 293 | #607D8B 294 | #546E7A 295 | #455A64 296 | #37474F 297 | #263238 298 | 299 | 300 | #ffffff 301 | #121212 302 | #000000 303 | #00FFFFFF 304 | #BCC0DC 305 | 306 | 307 | -------------------------------------------------------------------------------- /core/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Yes 4 | No 5 | -------------------------------------------------------------------------------- /core/src/test/java/dev/happysingh/core/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package dev.happysingh.core 2 | 3 | import org.junit.Assert.assertEquals 4 | import org.junit.Test 5 | 6 | /** 7 | * Example local unit test, which will execute on the development machine (host). 8 | * 9 | * See [testing documentation](http://d.android.com/tools/testing). 10 | */ 11 | class ExampleUnitTest { 12 | @Test 13 | fun addition_isCorrect() { 14 | assertEquals(4, 2 + 2) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app"s APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Automatically convert third-party libraries to use AndroidX 19 | android.enableJetifier=true 20 | # Kotlin code style for this project: "official" or "obsolete": 21 | kotlin.code.style=official -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/happysingh23828/android-github-repo-template/0c79546786520c4ef05b1b22334f81797ebc3d4b/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sun Feb 07 11:51:42 IST 2021 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-6.5-bin.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':core' 2 | include ':app' 3 | rootProject.name = "Android-Github-Repo-Template" --------------------------------------------------------------------------------