├── .github ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE.md └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── app-rating ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── stepstone │ │ └── apprating │ │ ├── AppRatingDialog.kt │ │ ├── AppRatingDialogFragment.kt │ │ ├── AppRatingDialogView.kt │ │ ├── C.kt │ │ ├── StringValue.kt │ │ ├── common │ │ └── Preconditions.kt │ │ ├── extensions │ │ └── extensions.kt │ │ ├── listener │ │ ├── OnRatingBarChangedListener.kt │ │ └── RatingDialogListener.kt │ │ └── ratingbar │ │ ├── CustomRatingBar.kt │ │ └── StarButton.kt │ └── res │ ├── drawable │ ├── edit_text_background.xml │ ├── ic_star_empty_48dp.xml │ └── ic_star_full_48dp.xml │ ├── layout │ ├── component_app_rate_dialog.xml │ ├── component_custom_rating_bar.xml │ └── star_button_layout.xml │ └── values │ ├── attrs.xml │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml ├── bintrayv1.gradle ├── build.gradle ├── gifs ├── firstDialog.gif └── secondDialog.gif ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── graphics ├── logo.png ├── sample.png └── stepstone-logo.png ├── installv1.gradle ├── quality └── detekt.yml ├── samples ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── stepstone │ │ │ └── apprating │ │ │ └── sample │ │ │ ├── SamplesActivity.kt │ │ │ ├── SamplesFragment.kt │ │ │ └── SamplesFragmentActivity.kt │ └── res │ │ ├── anim │ │ ├── slide_down.xml │ │ ├── slide_left.xml │ │ ├── slide_right.xml │ │ └── slide_up.xml │ │ ├── layout │ │ ├── activity_fragment_samples.xml │ │ ├── activity_samples.xml │ │ └── fragment_samples.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ ├── values-w820dp │ │ └── dimens.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ ├── styles.xml │ │ └── themes.xml │ └── second │ └── res │ └── values │ └── colors.xml └── settings.gradle /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | All contributions are welcome and encouraged! 2 | 3 | Before creating an issue please check in the issue tracker whether 4 | this issue wasn't already reported. 5 | 6 | Pull requests should be merged to the ```develop``` branch (Rebase & merge preferred). 7 | When adding new changes, please squash the commits together. 8 | 9 | Once ```develop``` is stable and we're ready to release the next version 10 | it will be merged to the ```master``` branch. Next, a new version will be 11 | uploaded to Bintray and a new release will be created on GitHub. 12 | Library releases must be made from the ```master``` branch. 13 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Make sure these boxes are checked before submitting your issue - thank you! 2 | 3 | - [ ] Check in the issue tracker whether 4 | this issue wasn't already reported. 5 | - [ ] If it's a bug report check that clear **steps to reproduce**, 6 | **stacktrace**, **expected behaviour** and other needed details are provided. 7 | - [ ] Feature requests contain a **description** of the feature 8 | you're requesting as well as a brief **scenario** explaining 9 | under what circumstances this might be useful. -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Make sure these boxes are checked before submitting your PR - thank you! 2 | 3 | - [ ] Pull request is being merged to the `develop` branch. 4 | - [ ] There's a reference to an *Issue* which this solves. 5 | - [ ] For new features a sample has been added to the sample app 6 | illustrating this feature's behaviour. 7 | - [ ] Commits are squashed to a single commit. -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea 5 | .DS_Store 6 | /build 7 | /captures 8 | .externalNativeBuild 9 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: android 2 | jdk: oraclejdk8 3 | sudo: false 4 | 5 | env: 6 | global: 7 | - ANDROID_API_LEVEL=28 8 | - ANDROID_BUILD_TOOLS_VERSION=28.0.3 9 | 10 | android: 11 | components: 12 | # use the latest revision of Android SDK Tools 13 | - platform-tools 14 | - tools 15 | 16 | # The BuildTools version used by your project 17 | - build-tools-${ANDROID_BUILD_TOOLS_VERSION} 18 | 19 | # Android platform 20 | - android-${ANDROID_API_LEVEL} 21 | 22 | # Support library 23 | - extra-android-support 24 | 25 | # Latest artifacts in local repository 26 | - extra-google-m2repository 27 | - extra-android-m2repository 28 | 29 | before_script: 30 | - printf 'bintray.user=dummy_user\nbintray.apikey=dummy_api_key' > local.properties 31 | 32 | script: 33 | - ./gradlew detektCheck 34 | - ./gradlew assembleDebug 35 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) 5 | and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). 6 | 7 | ## [2.3.1] 8 | ### Fixed 9 | - crash #56 which occur when set default rating to 0 10 | 11 | [2.3.1]: https://github.com/stepstone-tech/android-material-app-rating/compare/v2.3.0...v2.3.1 12 | 13 | ## [2.3.0] 14 | ### Added 15 | - "setCancelable", "setCancelOnTouchOutside" methods, which allow to control dialog cancelling 16 | - "setTargetFragment" method to handle listener in fragment 17 | - "setCommentInputEnabled" method to enabled/disable comment box 18 | - new api samples 19 | 20 | ### Fixed 21 | - some minor issues 22 | 23 | [2.3.0]: https://github.com/stepstone-tech/android-material-app-rating/compare/v2.2.0...v2.3.0 24 | 25 | ## [2.2.0] 26 | ### Added 27 | - "setDefaultComment" method, which allow user to use dialog also for editing comments 28 | 29 | [2.2.0]: https://github.com/stepstone-tech/android-material-app-rating/compare/v2.1.1...v2.2.0 30 | 31 | 32 | 33 | ## [2.1.1] 34 | ### Fixed 35 | - crash on devices below Lollipop 36 | 37 | [2.1.1]: https://github.com/stepstone-tech/android-material-app-rating/compare/v2.1.0...v2.1.1 38 | 39 | 40 | 41 | ## [2.1.0] 42 | ### Added 43 | - support for custom styles for title, description, note texts, comments 44 | - custom star color 45 | - "Later" button 46 | 47 | ### Changed 48 | - **Breaking change:** Added "onNeutralButtonClicked" method to listener 49 | 50 | [2.1.0]: https://github.com/stepstone-tech/android-material-app-rating/compare/v2.0.0...v2.1.0 51 | 52 | 53 | 54 | ## [2.0.0] 55 | ### Added 56 | - support for custom hint text and color in comment box 57 | 58 | ### Changed 59 | - **Breaking change:** Replaced "positive/nagative button" clicked listener by a global listener which needs to be implemented by the host activity 60 | 61 | [2.0.0]: https://github.com/stepstone-tech/android-material-app-rating/compare/v1.2.0...v2.0.0 62 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the description of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 | 5 | # Android Material App Rating [![Build Status](https://travis-ci.org/stepstone-tech/android-material-app-rating.svg?branch=master)](https://travis-ci.org/stepstone-tech/android-material-app-rating) [![Android Arsenal](https://img.shields.io/badge/Android%20Arsenal-Android%20Material%20App%20Rating-brightgreen.svg?style=flat)](https://android-arsenal.com/details/1/6143)[![Awesome Android](https://img.shields.io/badge/Awesome%20Android-Material%20App%20Rating-brightgreen.svg?style=flat)](https://android.libhunt.com/project/android-material-app-rating) 6 | 7 | This library allows to use customized Rating Dialog inside applications. 8 | 9 | 10 | 11 | ## Download 12 | ```groovy 13 | compile 'com.stepstone.apprating:app-rating:2.3.1' 14 | ``` 15 | 16 | ## Supported features 17 | - custom styles for dialog's background, stars, title, description, hint 18 | - custom rating scope (number of stars) 19 | - note descriptions below rating bar, which describes each note 20 | - defining custom dialog's title, description and hint 21 | - defining text for positive, negative and neutral button 22 | - enter/exit window animation 23 | 24 | ### Using different app themes 25 |      26 | 27 | 28 | ## Getting started 29 | 30 | ### Browse samples 31 | 32 | There are few samples of rating dialog usage. 33 | Please refer to `SamplesActivity` in `samples` module. There are also two gradle flavors for 34 | sample app which can switched to see dialog in different themes, styles. 35 | 36 | ### Prepare styling for dialog 37 | 38 | Styling app rating dialog works in the same way like styling standard android's AlertDialog. 39 | Just need to define own style for your dialog. 40 | ```xml 41 | 42 | 43 | 44 | 45 | 51 | 52 | 56 | 57 | 61 | 62 | 63 | ``` 64 | 65 | And assign this style to the theme. 66 | ```xml 67 | 68 | 69 | 70 | 85 | 86 | 87 | ``` 88 | 89 | ### Setup and create dialog 90 | 91 | This code need to be invoked inside FragmentActivity. 92 | Activity MUST implement RatingDialogListener ! Otherwise an exception will be thrown. 93 | 94 | ```java 95 | private void showDialog() { 96 | new AppRatingDialog.Builder() 97 | .setPositiveButtonText("Submit") 98 | .setNegativeButtonText("Cancel") 99 | .setNeutralButtonText("Later") 100 | .setNoteDescriptions(Arrays.asList("Very Bad", "Not good", "Quite ok", "Very Good", "Excellent !!!")) 101 | .setDefaultRating(2) 102 | .setTitle("Rate this application") 103 | .setDescription("Please select some stars and give your feedback") 104 | .setCommentInputEnabled(true) 105 | .setDefaultComment("This app is pretty cool !") 106 | .setStarColor(R.color.starColor) 107 | .setNoteDescriptionTextColor(R.color.noteDescriptionTextColor) 108 | .setTitleTextColor(R.color.titleTextColor) 109 | .setDescriptionTextColor(R.color.contentTextColor) 110 | .setHint("Please write your comment here ...") 111 | .setHintTextColor(R.color.hintTextColor) 112 | .setCommentTextColor(R.color.commentTextColor) 113 | .setCommentBackgroundColor(R.color.colorPrimaryDark) 114 | .setWindowAnimation(R.style.MyDialogFadeAnimation) 115 | .setCancelable(false) 116 | .setCanceledOnTouchOutside(false) 117 | .create(MainActivity.this) 118 | .setTargetFragment(this, TAG) // only if listener is implemented by fragment 119 | .show(); 120 | } 121 | ``` 122 | 123 | ### Getting results 124 | 125 | Rating and comments can be fetched by listener implemented by activity. 126 | 127 | ```java 128 | class MyActivity implements RatingDialogListener { 129 | 130 | @Override 131 | public void onPositiveButtonClicked(int rate, String comment) { 132 | // interpret results, send it to analytics etc... 133 | } 134 | 135 | @Override 136 | public void onNegativeButtonClicked() { 137 | 138 | } 139 | 140 | @Override 141 | public void onNeutralButtonClicked() { 142 | 143 | } 144 | } 145 | ``` 146 | 147 | ## Changelog 148 | See [changelog](CHANGELOG.md) 149 | 150 | ## License 151 | Copyright 2017 StepStone Services 152 | 153 | Licensed under the Apache License, Version 2.0 (the "License"); 154 | you may not use this file except in compliance with the License. 155 | You may obtain a copy of the License at 156 | 157 |     [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0) 158 | 159 | Unless required by applicable law or agreed to in writing, software 160 | distributed under the License is distributed on an "AS IS" BASIS, 161 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 162 | See the License for the specific language governing permissions and 163 | limitations under the License. 164 | 165 | ## Maintained by 166 | Stepstone 167 | -------------------------------------------------------------------------------- /app-rating/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app-rating/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'kotlin-android' 3 | apply plugin: 'kotlin-android-extensions' 4 | apply plugin: 'maven' 5 | apply plugin: "io.gitlab.arturbosch.detekt" 6 | 7 | ext { 8 | bintrayRepo = 'maven' 9 | bintrayName = POM_ARTIFACT_ID 10 | 11 | publishedGroupId = POM_GROUP_ID 12 | libraryName = 'Android Material App Rating' 13 | artifact = POM_ARTIFACT_ID 14 | 15 | libraryDescription = 'This library allows to use customized Rating Dialog inside applications.' 16 | 17 | siteUrl = 'https://github.com/stepstone-tech/android-material-app-rating' 18 | gitUrl = 'https://github.com/stepstone-tech/android-material-app-rating.git' 19 | 20 | libraryVersion = POM_VERSION 21 | 22 | developerId = 'pglebocki' 23 | developerName = 'Piotr Głębocki' 24 | developerEmail = 'Piotr.Glebocki@stepstone.com' 25 | 26 | licenseName = 'The Apache Software License, Version 2.0' 27 | licenseUrl = 'http://www.apache.org/licenses/LICENSE-2.0.txt' 28 | allLicenses = ["Apache-2.0"] 29 | } 30 | 31 | android { 32 | compileSdkVersion project.androidCompileSdkVersion 33 | buildToolsVersion project.androidBuildToolsVersion 34 | 35 | defaultConfig { 36 | minSdkVersion project.androidMinSdkVersion 37 | targetSdkVersion project.androidTargetSdkVersion 38 | versionCode 1 39 | versionName "2.3.1" 40 | vectorDrawables.useSupportLibrary = true 41 | } 42 | buildTypes { 43 | release { 44 | minifyEnabled false 45 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 46 | } 47 | } 48 | buildToolsVersion project.androidBuildToolsVersion 49 | } 50 | 51 | dependencies { 52 | implementation fileTree(dir: 'libs', include: ['*.jar']) 53 | 54 | implementation("androidx.appcompat:appcompat:$androidXLibraryVersion") 55 | implementation("androidx.annotation:annotation:$androidXLibraryVersion") 56 | implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version") 57 | 58 | detekt "io.gitlab.arturbosch.detekt:detekt-formatting:$detektVersion" 59 | } 60 | 61 | gradle.taskGraph.beforeTask { Task task -> 62 | if (task instanceof Javadoc) { 63 | def releaseVariant = android.libraryVariants.find { it.buildType.name == 'release' } 64 | Javadoc javadocTask = (Javadoc) task 65 | javadocTask.source = android.sourceSets.main.java.srcDirs 66 | ext.androidJar = "${android.sdkDirectory}/platforms/${android.compileSdkVersion}/android.jar" 67 | javadocTask.classpath = files(releaseVariant.javaCompile.classpath.files) + files(ext.androidJar) 68 | } 69 | } 70 | 71 | tasks.withType(Javadoc).all { 72 | excludes = ['**/*.kt'] 73 | } 74 | 75 | apply from: '../installv1.gradle' 76 | apply from: '../bintrayv1.gradle' 77 | repositories { 78 | mavenCentral() 79 | google() 80 | } 81 | 82 | detekt { 83 | profile("") { 84 | input = "$project.projectDir" 85 | output = "$projectDir/build/reports" 86 | filters = ".*/resources/.*,.*/build/.*" 87 | config = "$project.parent.projectDir/quality/detekt.yml" 88 | baseline = "$project.parent.projectDir/quality/baseline.xml" 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /app-rating/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/pglebocki/Library/Android/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app-rating/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app-rating/src/main/java/com/stepstone/apprating/AppRatingDialog.kt: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 StepStone Services 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package com.stepstone.apprating 18 | 19 | import android.text.TextUtils 20 | import androidx.annotation.ColorRes 21 | import androidx.annotation.StringRes 22 | import androidx.annotation.StyleRes 23 | import androidx.fragment.app.Fragment 24 | import androidx.fragment.app.FragmentActivity 25 | import com.stepstone.apprating.AppRatingDialog.Builder 26 | import com.stepstone.apprating.common.Preconditions 27 | import java.io.Serializable 28 | 29 | /** 30 | * This class represents dialog object produced by [Builder]. 31 | * Dialog can be fully configurable. 32 | * 33 | * @see Builder 34 | */ 35 | class AppRatingDialog private constructor( 36 | private val fragmentActivity: FragmentActivity, 37 | private val data: Builder.Data 38 | ) { 39 | 40 | private var fragment: Fragment? = null 41 | private var requestCode: Int = 0 42 | 43 | /** 44 | * Optional target for this fragment. This may be used, for example, 45 | * if this fragment is being started by another, 46 | * and when done wants to give a result back to the first 47 | * 48 | * @param fragment The fragment that is the target of this one. 49 | * @param requestCode Optional request code, for convenience if you 50 | * are going to call back with {@link #onActivityResult(int, int, Intent)} 51 | */ 52 | fun setTargetFragment(fragment: Fragment, requestCode: Int): AppRatingDialog { 53 | this.fragment = fragment 54 | this.requestCode = requestCode 55 | return this 56 | } 57 | 58 | /** 59 | * This method shows rating dialog. 60 | */ 61 | fun show() { 62 | AppRatingDialogFragment.newInstance(data).apply { 63 | fragment?.let { 64 | setTargetFragment(it, requestCode) 65 | } 66 | show(fragmentActivity.supportFragmentManager, "") 67 | } 68 | } 69 | 70 | /** 71 | * This class allows to setup rating dialog 72 | * using builder pattern. 73 | */ 74 | class Builder : Serializable { 75 | 76 | data class Data( 77 | var numberOfStars: Int = MAX_RATING, 78 | var defaultRating: Int = DEFAULT_RATING, 79 | val positiveButtonText: StringValue = StringValue(), 80 | val negativeButtonText: StringValue = StringValue(), 81 | val neutralButtonText: StringValue = StringValue(), 82 | val title: StringValue = StringValue(), 83 | val description: StringValue = StringValue(), 84 | val defaultComment: StringValue = StringValue(), 85 | val hint: StringValue = StringValue(), 86 | var commentInputEnabled: Boolean = true, 87 | var starColorResId: Int = 0, 88 | var noteDescriptionTextColor: Int = 0, 89 | var titleTextColorResId: Int = 0, 90 | var descriptionTextColorResId: Int = 0, 91 | var hintTextColorResId: Int = 0, 92 | var commentTextColorResId: Int = 0, 93 | var commentBackgroundColorResId: Int = 0, 94 | var windowAnimationResId: Int = 0, 95 | var noteDescriptions: ArrayList? = null, 96 | var cancelable: Boolean? = null, 97 | var canceledOnTouchOutside: Boolean? = null 98 | ) : Serializable 99 | 100 | val data = Data() 101 | 102 | /** 103 | * This method creates [AppRatingDialog] object. It should be called 104 | * after setup. 105 | * 106 | * @param activity an activity where dialog belongs to 107 | * @return instance of dialog 108 | */ 109 | fun create(activity: FragmentActivity): AppRatingDialog { 110 | Preconditions.checkNotNull(activity, "FragmentActivity cannot be null") 111 | return AppRatingDialog(activity, data) 112 | } 113 | 114 | /** 115 | * This method sets maximum number of start which will be visible in the dialog. 116 | * Default value is 6. If want to add some note descriptions below rating bar 117 | * then need to use [.setNoteDescriptions] method instead of this one. 118 | * 119 | * @param maxRating maximum number of stars 120 | * @return Builder for chaining 121 | */ 122 | fun setNumberOfStars(maxRating: Int): Builder { 123 | Preconditions.checkArgument( 124 | maxRating in 1..MAX_RATING, 125 | "max rating value should be between 1 and $MAX_RATING" 126 | ) 127 | data.numberOfStars = maxRating 128 | return this 129 | } 130 | 131 | /** 132 | * This method sets note description for appropriate rating numbers. 133 | * If note descriptions were set by this then [.setNumberOfStars] 134 | * method will be ignored. 135 | * 136 | * @param noteDescriptions list of note descriptions 137 | * @return Builder for chaining 138 | */ 139 | fun setNoteDescriptions(noteDescriptions: List): Builder { 140 | Preconditions.checkNotNull(noteDescriptions, "list cannot be null") 141 | Preconditions.checkArgument(!noteDescriptions.isEmpty(), "list cannot be empty") 142 | Preconditions.checkArgument( 143 | noteDescriptions.size <= MAX_RATING, 144 | "size of the list can be maximally $MAX_RATING" 145 | ) 146 | data.noteDescriptions = ArrayList(noteDescriptions) 147 | return this 148 | } 149 | 150 | /** 151 | * This method sets number of stars which are selected by default 152 | * when dialog is opened. 153 | * 154 | * @param defaultRating number of stars which should be selected 155 | * @return Builder for chaining 156 | */ 157 | fun setDefaultRating(defaultRating: Int): Builder { 158 | Preconditions.checkArgument( 159 | defaultRating >= 0 && defaultRating <= data.numberOfStars, 160 | "default rating value should be between 0 and " + data.numberOfStars 161 | ) 162 | data.defaultRating = defaultRating 163 | return this 164 | } 165 | 166 | /** 167 | * This method sets dialog title. 168 | * The title is optional. 169 | * 170 | * @param title dialog's title text 171 | * @return Builder for chaining 172 | * @see#setTitle(int) 173 | */ 174 | fun setTitle(title: String): Builder { 175 | Preconditions.checkArgument(!TextUtils.isEmpty(title), "title cannot be empty") 176 | data.title.text = title 177 | return this 178 | } 179 | 180 | /** 181 | * This method sets dialog title. 182 | * The title is optional. 183 | * 184 | * @param resId resource id of dialog's title 185 | * @return Builder for chaining 186 | * @see#setTitle(String) 187 | */ 188 | fun setTitle(@StringRes resId: Int): Builder { 189 | data.title.textResId = resId 190 | return this 191 | } 192 | 193 | /** 194 | * This method sets dialog description description text, which is visible below title. 195 | * The description is optional. 196 | * 197 | * @param content dialog's description text 198 | * @return Builder for chaining 199 | * @see#setDescription(int) 200 | */ 201 | fun setDescription(content: String): Builder { 202 | Preconditions.checkArgument(!TextUtils.isEmpty(content), "description cannot be empty") 203 | data.description.text = content 204 | return this 205 | } 206 | 207 | /** 208 | * This method sets dialog description description text, which is visible below title. 209 | * The description description is optional. 210 | * 211 | * @param resId resource id of dialog's description text 212 | * @return Builder for chaining 213 | * @see#setDescription(String) 214 | */ 215 | fun setDescription(@StringRes resId: Int): Builder { 216 | data.description.textResId = resId 217 | return this 218 | } 219 | 220 | /** 221 | * This method sets comment edit text to be enabled/disabled. 222 | * By default it is always enabled. 223 | * 224 | * @param enabled if set to false then comment input will be not visible 225 | * @return Builder for chaining 226 | */ 227 | fun setCommentInputEnabled(enabled: Boolean): Builder { 228 | data.commentInputEnabled = enabled 229 | return this 230 | } 231 | 232 | /** 233 | * This method sets dialog default comment text. 234 | * The comment is optional. 235 | * 236 | * @param comment dialog's comment text 237 | * @return Builder for chaining 238 | * @see#setDefaultComment(int) 239 | */ 240 | fun setDefaultComment(comment: String): Builder { 241 | Preconditions.checkArgument(!TextUtils.isEmpty(comment), "comment cannot be empty") 242 | data.defaultComment.text = comment 243 | return this 244 | } 245 | 246 | /** 247 | * This method sets dialog default comment text. 248 | * The comment is optional. 249 | * 250 | * @param resId resource id of dialog's comment 251 | * @return Builder for chaining 252 | * @see#setDefaultComment(String) 253 | */ 254 | fun setDefaultComment(@StringRes resId: Int): Builder { 255 | data.defaultComment.textResId = resId 256 | return this 257 | } 258 | 259 | /** 260 | * This method sets hint text. 261 | * The hint is optional. 262 | * 263 | * @param hint a hint text 264 | * @return Builder for chaining 265 | * @see#setHint(int) 266 | */ 267 | fun setHint(hint: String): Builder { 268 | Preconditions.checkArgument(!TextUtils.isEmpty(hint), "hint cannot be empty") 269 | data.hint.text = hint 270 | return this 271 | } 272 | 273 | /** 274 | * This method sets hint text. 275 | * The hint is optional. 276 | * 277 | * @param resId resource id of hint text 278 | * @return Builder for chaining 279 | * @see#setHint(String) 280 | */ 281 | fun setHint(@StringRes resId: Int): Builder { 282 | data.hint.textResId = resId 283 | return this 284 | } 285 | 286 | /** 287 | * This method sets text of dialog's positive button. 288 | * 289 | * @param positiveButtonText text for positive button 290 | * @return Builder for chaining 291 | * @see#setPositiveButtonText(int) 292 | */ 293 | fun setPositiveButtonText(positiveButtonText: String): Builder { 294 | Preconditions.checkArgument( 295 | !TextUtils.isEmpty(positiveButtonText), 296 | "text cannot be empty" 297 | ) 298 | data.positiveButtonText.text = positiveButtonText 299 | return this 300 | } 301 | 302 | /** 303 | * This method sets text of dialog's positive button. 304 | * 305 | * @param resId resource id of positive button text 306 | * @return Builder for chaining 307 | * @see#setPositiveButtonText(String) 308 | */ 309 | fun setPositiveButtonText(@StringRes resId: Int): Builder { 310 | data.positiveButtonText.textResId = resId 311 | return this 312 | } 313 | 314 | /** 315 | * This method sets text of dialog's negative button. 316 | * 317 | * @param negativeButtonText text for negative button 318 | * @return Builder for chaining 319 | * @see#setNegativeButtonText(int) 320 | */ 321 | fun setNegativeButtonText(negativeButtonText: String): Builder { 322 | Preconditions.checkArgument( 323 | !TextUtils.isEmpty(negativeButtonText), 324 | "text cannot be empty" 325 | ) 326 | data.negativeButtonText.text = negativeButtonText 327 | return this 328 | } 329 | 330 | /** 331 | * This method sets text of dialog's neutral button. 332 | * 333 | * @param neutralButtonText text for neutral button 334 | * @return Builder for chaining 335 | * @see#setNeutralButtonText(int) 336 | */ 337 | fun setNeutralButtonText(neutralButtonText: String): Builder { 338 | Preconditions.checkArgument( 339 | !TextUtils.isEmpty(neutralButtonText), 340 | "text cannot be empty" 341 | ) 342 | data.neutralButtonText.text = neutralButtonText 343 | return this 344 | } 345 | 346 | /** 347 | * This method sets text of dialog's negative button. 348 | * 349 | * @param resId resource id of negative button text 350 | * @return Builder for chaining 351 | * @see#setNegativeButtonText(String) 352 | */ 353 | fun setNegativeButtonText(@StringRes resId: Int): Builder { 354 | data.negativeButtonText.textResId = resId 355 | return this 356 | } 357 | 358 | /** 359 | * This method sets text of dialog's neutral button. 360 | * 361 | * @param resId resource id of neutral button text 362 | * @return Builder for chaining 363 | * @see#setNeutralButtonText(String) 364 | */ 365 | fun setNeutralButtonText(@StringRes resId: Int): Builder { 366 | data.neutralButtonText.textResId = resId 367 | return this 368 | } 369 | 370 | /** 371 | * This method sets stars's color resource. 372 | * If not set then it uses accent color 373 | * defined in theme. 374 | * 375 | * @param colorResId color resource id for stars 376 | * @return Builder for chaining 377 | */ 378 | fun setStarColor(@ColorRes colorResId: Int): Builder { 379 | data.starColorResId = colorResId 380 | return this 381 | } 382 | 383 | /** 384 | * This method sets note description's color resource. 385 | * If not set then it uses accent color 386 | * defined in theme. 387 | * 388 | * @param colorResId color resource id for note descriptions 389 | * @return Builder for chaining 390 | */ 391 | fun setNoteDescriptionTextColor(@ColorRes colorResId: Int): Builder { 392 | data.noteDescriptionTextColor = colorResId 393 | return this 394 | } 395 | 396 | /** 397 | * This method sets title's text color resource. 398 | * If not set then it uses default primary text color 399 | * defined in theme. 400 | * 401 | * @param colorResId color resource id for title label 402 | * @return Builder for chaining 403 | */ 404 | fun setTitleTextColor(@ColorRes colorResId: Int): Builder { 405 | data.titleTextColorResId = colorResId 406 | return this 407 | } 408 | 409 | /** 410 | * This method sets description's text color resource. 411 | * If not set then it uses default primary text color 412 | * defined in theme. 413 | * 414 | * @param colorResId color resource id for description label 415 | * @return Builder for chaining 416 | */ 417 | fun setDescriptionTextColor(@ColorRes colorResId: Int): Builder { 418 | data.descriptionTextColorResId = colorResId 419 | return this 420 | } 421 | 422 | /** 423 | * This method sets hint's text color resource. 424 | * If not set then it uses default hint text color 425 | * defined in theme. 426 | * 427 | * @param colorResId color resource id for hint 428 | * @return Builder for chaining 429 | */ 430 | fun setHintTextColor(@ColorRes colorResId: Int): Builder { 431 | data.hintTextColorResId = colorResId 432 | return this 433 | } 434 | 435 | /** 436 | * This method sets comment's color resource. 437 | * If not set then it uses default primary text color 438 | * defined in theme. 439 | * 440 | * @param colorResId color resource id for comment edit text 441 | * @return Builder for chaining 442 | */ 443 | fun setCommentTextColor(@ColorRes colorResId: Int): Builder { 444 | data.commentTextColorResId = colorResId 445 | return this 446 | } 447 | 448 | /** 449 | * This method sets comments edit text's background color resource. 450 | * If not set then it uses default white color will be used. 451 | * 452 | * @param colorResId color resource id for edit text background 453 | * @return Builder for chaining 454 | */ 455 | fun setCommentBackgroundColor(@ColorRes colorResId: Int): Builder { 456 | data.commentBackgroundColorResId = colorResId 457 | return this 458 | } 459 | 460 | /** 461 | * This method sets window's animation resource. 462 | * 463 | * @param animationResId resource if of animation 464 | * @return Builder for chaining 465 | */ 466 | fun setWindowAnimation(@StyleRes animationResId: Int): Builder { 467 | data.windowAnimationResId = animationResId 468 | return this 469 | } 470 | 471 | /** 472 | * Sets whether this dialog is cancelable with the BACK key. 473 | * 474 | * @param cancelable if false when dialog cannot be canceled 475 | * @return Builder for chaining 476 | */ 477 | fun setCancelable(cancelable: Boolean): Builder { 478 | data.cancelable = cancelable 479 | return this 480 | } 481 | 482 | /** 483 | * Sets whether this dialog is canceled when touched outside the window's bounds. 484 | * If setting to true, the dialog is set to be cancelable if not already set. 485 | * 486 | * @param cancel whether the dialog should be canceled when touched outside the window. 487 | * @return Builder for chaining 488 | */ 489 | fun setCanceledOnTouchOutside(cancel: Boolean): Builder { 490 | data.canceledOnTouchOutside = cancel 491 | return this 492 | } 493 | } 494 | } 495 | -------------------------------------------------------------------------------- /app-rating/src/main/java/com/stepstone/apprating/AppRatingDialogFragment.kt: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 StepStone Services 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package com.stepstone.apprating 18 | 19 | import android.app.Dialog 20 | import android.content.Context 21 | import android.os.Bundle 22 | import androidx.fragment.app.DialogFragment 23 | import androidx.appcompat.app.AlertDialog 24 | import android.text.TextUtils 25 | import com.stepstone.apprating.extensions.applyIfNotZero 26 | import com.stepstone.apprating.listener.RatingDialogListener 27 | 28 | /** 29 | * This class represents rating dialog created by [com.stepstone.apprating.AppRatingDialog.Builder]. 30 | 31 | * @see AppRatingDialog 32 | */ 33 | class AppRatingDialogFragment : DialogFragment() { 34 | 35 | private var listener: RatingDialogListener? = null 36 | get() { 37 | if (host is RatingDialogListener) { 38 | return host as RatingDialogListener 39 | } 40 | return targetFragment as RatingDialogListener? 41 | } 42 | 43 | private lateinit var data: AppRatingDialog.Builder.Data 44 | private lateinit var alertDialog: AlertDialog 45 | private lateinit var dialogView: AppRatingDialogView 46 | 47 | private val title by lazy { data.title.resolveText(resources) } 48 | private val description by lazy { data.description.resolveText(resources) } 49 | private val defaultComment by lazy { data.defaultComment.resolveText(resources) } 50 | private val hint by lazy { data.hint.resolveText(resources) } 51 | private val positiveButtonText by lazy { data.positiveButtonText.resolveText(resources) } 52 | private val neutralButtonText by lazy { data.neutralButtonText.resolveText(resources) } 53 | private val negativeButtonText by lazy { data.negativeButtonText.resolveText(resources) } 54 | 55 | override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { 56 | return setupAlertDialog(activity!!) 57 | } 58 | 59 | override fun onSaveInstanceState(outState: Bundle) { 60 | outState.putFloat(CURRENT_RATE_NUMBER, dialogView.rateNumber) 61 | super.onSaveInstanceState(outState) 62 | } 63 | 64 | override fun onActivityCreated(savedInstanceState: Bundle?) { 65 | super.onActivityCreated(savedInstanceState) 66 | val rateNumber: Float? = savedInstanceState?.getFloat(CURRENT_RATE_NUMBER) 67 | if (rateNumber != null) { 68 | dialogView.setDefaultRating(rateNumber.toInt()) 69 | } 70 | } 71 | 72 | private fun setupAlertDialog(context: Context): AlertDialog { 73 | dialogView = AppRatingDialogView(context) 74 | val builder = AlertDialog.Builder(activity!!) 75 | data = arguments?.getSerializable(DIALOG_DATA) as AppRatingDialog.Builder.Data 76 | 77 | setupPositiveButton(dialogView, builder) 78 | setupNegativeButton(builder) 79 | setupNeutralButton(builder) 80 | setupTitleAndContentMessages(dialogView) 81 | setupHint(dialogView) 82 | setupColors(dialogView) 83 | setupInputBox() 84 | setupRatingBar() 85 | 86 | builder.setView(dialogView) 87 | alertDialog = builder.create() 88 | setupAnimation() 89 | setupCancelable() 90 | return alertDialog 91 | } 92 | 93 | private fun setupRatingBar() { 94 | dialogView.setNumberOfStars(data.numberOfStars) 95 | 96 | val isEmpty = data.noteDescriptions?.isEmpty() ?: true 97 | if (!isEmpty) { 98 | dialogView.setNoteDescriptions(data.noteDescriptions!!) 99 | } 100 | 101 | dialogView.setDefaultRating(data.defaultRating) 102 | } 103 | 104 | private fun setupInputBox() { 105 | dialogView.setCommentInputEnabled(data.commentInputEnabled) 106 | } 107 | 108 | private fun setupCancelable() { 109 | data.cancelable?.let { isCancelable = it } 110 | data.canceledOnTouchOutside?.let { alertDialog.setCanceledOnTouchOutside(it) } 111 | } 112 | 113 | private fun setupAnimation() { 114 | if (data.windowAnimationResId != 0) { 115 | alertDialog.window.attributes.windowAnimations = data.windowAnimationResId 116 | } 117 | } 118 | 119 | private fun setupColors(dialogView: AppRatingDialogView) { 120 | data.titleTextColorResId.applyIfNotZero { 121 | dialogView.setTitleTextColor(this) 122 | } 123 | data.descriptionTextColorResId.applyIfNotZero { 124 | dialogView.setDescriptionTextColor(this) 125 | } 126 | data.commentTextColorResId.applyIfNotZero { 127 | dialogView.setEditTextColor(this) 128 | } 129 | data.commentBackgroundColorResId.applyIfNotZero { 130 | dialogView.setEditBackgroundColor(this) 131 | } 132 | data.hintTextColorResId.applyIfNotZero { 133 | dialogView.setHintColor(this) 134 | } 135 | data.starColorResId.applyIfNotZero { 136 | dialogView.setStarColor(this) 137 | } 138 | data.noteDescriptionTextColor.applyIfNotZero { 139 | dialogView.setNoteDescriptionTextColor(this) 140 | } 141 | } 142 | 143 | private fun setupTitleAndContentMessages(dialogView: AppRatingDialogView) { 144 | if (!title.isNullOrEmpty()) { 145 | dialogView.setTitleText(title!!) 146 | } 147 | if (!description.isNullOrEmpty()) { 148 | dialogView.setDescriptionText(description!!) 149 | } 150 | if (!defaultComment.isNullOrEmpty()) { 151 | dialogView.setDefaultComment(defaultComment!!) 152 | } 153 | } 154 | 155 | private fun setupHint(dialogView: AppRatingDialogView) { 156 | if (!TextUtils.isEmpty(hint)) { 157 | dialogView.setHint(hint!!) 158 | } 159 | } 160 | 161 | private fun setupNegativeButton(builder: AlertDialog.Builder) { 162 | if (!TextUtils.isEmpty(negativeButtonText)) { 163 | builder.setNegativeButton(negativeButtonText) { _, _ -> 164 | listener?.onNegativeButtonClicked() 165 | } 166 | } 167 | } 168 | 169 | private fun setupPositiveButton(dialogView: AppRatingDialogView, builder: AlertDialog.Builder) { 170 | if (!TextUtils.isEmpty(positiveButtonText)) { 171 | builder.setPositiveButton(positiveButtonText) { _, _ -> 172 | val rateNumber = dialogView.rateNumber.toInt() 173 | val comment = dialogView.comment 174 | listener?.onPositiveButtonClicked(rateNumber, comment) 175 | } 176 | } 177 | } 178 | 179 | private fun setupNeutralButton(builder: AlertDialog.Builder) { 180 | if (!TextUtils.isEmpty(neutralButtonText)) { 181 | builder.setNeutralButton(neutralButtonText) { _, _ -> 182 | listener?.onNeutralButtonClicked() 183 | } 184 | } 185 | } 186 | 187 | companion object { 188 | 189 | fun newInstance(data: AppRatingDialog.Builder.Data): AppRatingDialogFragment { 190 | val fragment = AppRatingDialogFragment() 191 | val bundle = Bundle() 192 | bundle.putSerializable(DIALOG_DATA, data) 193 | fragment.arguments = bundle 194 | return fragment 195 | } 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /app-rating/src/main/java/com/stepstone/apprating/AppRatingDialogView.kt: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 StepStone Services 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package com.stepstone.apprating 18 | 19 | import android.annotation.SuppressLint 20 | import android.content.Context 21 | import android.content.res.Resources 22 | import android.graphics.PorterDuff 23 | import android.graphics.PorterDuffColorFilter 24 | import android.util.TypedValue 25 | import android.view.LayoutInflater 26 | import android.view.View 27 | import android.widget.LinearLayout 28 | import androidx.annotation.ColorRes 29 | import androidx.core.content.ContextCompat 30 | import androidx.core.content.res.ResourcesCompat 31 | import androidx.core.widget.TextViewCompat 32 | import com.stepstone.apprating.listener.OnRatingBarChangedListener 33 | import kotlinx.android.synthetic.main.component_app_rate_dialog.view.* 34 | 35 | /** 36 | * This class represents custom dialog view which contains 37 | * rating bar, edit box and labels. 38 | */ 39 | class AppRatingDialogView(context: Context) : LinearLayout(context), OnRatingBarChangedListener { 40 | 41 | private var noteDescriptions: List? = null 42 | 43 | init { 44 | setup(context) 45 | } 46 | 47 | /** 48 | * This method returns current rating. 49 | * 50 | * @return number of current selected stars 51 | */ 52 | val rateNumber: Float 53 | get() = ratingBar.rating 54 | 55 | /** 56 | * This method returns rating comment. 57 | * 58 | * @return comment text from edit box 59 | */ 60 | val comment: String 61 | get() = commentEditText.text.toString() 62 | 63 | /** 64 | * This method sets maximum numbers of start which are visible. 65 | * 66 | * @param numberOfStars maximum number of stars 67 | */ 68 | fun setNumberOfStars(numberOfStars: Int) { 69 | ratingBar.setNumStars(numberOfStars) 70 | } 71 | 72 | /** 73 | * This method sets color for stars. 74 | */ 75 | fun setStarColor(@ColorRes colorResId: Int) { 76 | ratingBar.setStarColor(colorResId) 77 | } 78 | 79 | /** 80 | * This method sets color for note descriptions. 81 | */ 82 | fun setNoteDescriptionTextColor(@ColorRes colorResId: Int) { 83 | noteDescriptionText.setTextColor(getColorFromRes(colorResId)) 84 | } 85 | 86 | /** 87 | * This method sets note descriptions for each rating value. 88 | * 89 | * @param noteDescriptions list of note descriptions 90 | */ 91 | fun setNoteDescriptions(noteDescriptions: List) { 92 | val numberOfStars = noteDescriptions.size 93 | setNumberOfStars(numberOfStars) 94 | this.noteDescriptions = noteDescriptions 95 | } 96 | 97 | /** 98 | * This method sets default number of stars. 99 | * 100 | * @param defaultRating number of stars 101 | */ 102 | fun setDefaultRating(defaultRating: Int) { 103 | ratingBar.setRating(defaultRating) 104 | } 105 | 106 | /** 107 | * This method sets dialog's title. 108 | * 109 | * @param title dialog's title text 110 | */ 111 | fun setTitleText(title: String) { 112 | titleText.text = title 113 | titleText.visibility = View.VISIBLE 114 | } 115 | 116 | /** 117 | * This method sets dialog's description text. 118 | * 119 | * @param content dialog's description text 120 | */ 121 | fun setDescriptionText(content: String) { 122 | descriptionText.text = content 123 | descriptionText.visibility = View.VISIBLE 124 | } 125 | 126 | /** 127 | * This method enable/disable comment edit text. 128 | * 129 | * @param enabled if set to false then comment input will be not visible 130 | */ 131 | fun setCommentInputEnabled(enabled: Boolean) { 132 | commentEditText.visibility = if (enabled) View.VISIBLE else View.GONE 133 | } 134 | 135 | /** 136 | * This method sets dialog's default comment text. 137 | * 138 | * @param comment dialog's comment text 139 | */ 140 | fun setDefaultComment(comment: String) { 141 | commentEditText.setText(comment) 142 | } 143 | 144 | /** 145 | * This method sets color of dialog's title. 146 | * 147 | * @param color resource id of title label color 148 | */ 149 | fun setTitleTextColor(@ColorRes color: Int) { 150 | titleText.setTextColor(getColorFromRes(color)) 151 | } 152 | 153 | /** 154 | * This method sets color of dialog's description. 155 | * 156 | * @param color resource id of description label color 157 | */ 158 | fun setDescriptionTextColor(@ColorRes color: Int) { 159 | descriptionText.setTextColor(getColorFromRes(color)) 160 | } 161 | 162 | /** 163 | * This method sets color of dialog's comment. 164 | * 165 | * @param color resource id of comment text color 166 | */ 167 | fun setEditTextColor(@ColorRes color: Int) { 168 | commentEditText.setTextColor(getColorFromRes(color)) 169 | } 170 | 171 | /** 172 | * This method sets color of dialog's edit text. 173 | * 174 | * @param color resource id of edit text 175 | */ 176 | fun setEditBackgroundColor(@ColorRes color: Int) { 177 | val drawable = commentEditText.background 178 | drawable.colorFilter = PorterDuffColorFilter(ContextCompat.getColor(context, color), PorterDuff.Mode.SRC_IN) 179 | } 180 | 181 | /** 182 | * This method sets hint for comment edit text. 183 | * 184 | * @param hint a hint to be displayed 185 | */ 186 | fun setHint(hint: String) { 187 | commentEditText.hint = hint 188 | } 189 | 190 | /** 191 | * This method sets color of dialog's hint. 192 | * 193 | * @param color resource id of hint text color 194 | */ 195 | fun setHintColor(@ColorRes color: Int) { 196 | commentEditText.setHintTextColor(getColorFromRes(color)) 197 | } 198 | 199 | override fun onRatingChanged(rating: Int) { 200 | updateNoteDescriptionText(rating - 1) 201 | } 202 | 203 | private fun updateNoteDescriptionText(rating: Int) { 204 | if (noteDescriptions == null || (noteDescriptions?.isEmpty() ?: true)) { 205 | noteDescriptionText.visibility = View.GONE 206 | return 207 | } 208 | 209 | if (rating >= 0) { 210 | val text = noteDescriptions!![rating] 211 | noteDescriptionText.text = text 212 | noteDescriptionText.visibility = View.VISIBLE 213 | } 214 | } 215 | 216 | @SuppressLint("ResourceType") 217 | private fun setup(context: Context) { 218 | LayoutInflater.from(context).inflate(R.layout.component_app_rate_dialog, this, true) 219 | ratingBar.setIsIndicator(false) 220 | ratingBar.setOnRatingBarChangeListener(this) 221 | 222 | TextViewCompat.setTextAppearance( 223 | titleText, 224 | fetchAttributeValue(R.attr.appRatingDialogTitleStyle) 225 | ) 226 | TextViewCompat.setTextAppearance( 227 | descriptionText, 228 | fetchAttributeValue(R.attr.appRatingDialogDescriptionStyle) 229 | ) 230 | TextViewCompat.setTextAppearance( 231 | noteDescriptionText, 232 | fetchAttributeValue(R.attr.appRatingDialogNoteDescriptionStyle) 233 | ) 234 | TextViewCompat.setTextAppearance( 235 | commentEditText, 236 | fetchAttributeValue(R.attr.appRatingDialogCommentStyle) 237 | ) 238 | } 239 | 240 | private val theme: Resources.Theme 241 | get() = context.theme 242 | 243 | private fun getColorFromRes(@ColorRes colorResId: Int): Int { 244 | return ResourcesCompat.getColor(context.resources, colorResId, theme) 245 | } 246 | 247 | private fun fetchAttributeValue(attr: Int): Int { 248 | val outValue = TypedValue() 249 | context.theme.resolveAttribute(attr, outValue, true) 250 | return outValue.data 251 | } 252 | } 253 | -------------------------------------------------------------------------------- /app-rating/src/main/java/com/stepstone/apprating/C.kt: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 StepStone Services 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package com.stepstone.apprating 18 | 19 | const val MAX_RATING = 6 20 | const val DEFAULT_RATING = 4 21 | 22 | const val DIALOG_DATA = "data" 23 | const val CURRENT_RATE_NUMBER = "currentRateNumber" 24 | 25 | const val ALPHA_VISIBLE = 1.0f 26 | const val ALPHA_INVISIBLE = 0.0f 27 | 28 | const val CHECK_STAR_DURATION: Long = 200 29 | -------------------------------------------------------------------------------- /app-rating/src/main/java/com/stepstone/apprating/StringValue.kt: -------------------------------------------------------------------------------- 1 | package com.stepstone.apprating 2 | 3 | import android.content.res.Resources 4 | import androidx.annotation.StringRes 5 | import android.text.TextUtils 6 | import java.io.Serializable 7 | 8 | /** 9 | * This class in a container for string value which can 10 | * be represented by java String class or resources id. 11 | * It provides text based on last given value. 12 | */ 13 | class StringValue : Serializable { 14 | 15 | var text: String? = null 16 | set(value) { 17 | field = value 18 | value?.let { 19 | textResId = 0 20 | } 21 | } 22 | 23 | @StringRes 24 | var textResId: Int = 0 25 | set(value) { 26 | field = value 27 | if (value != 0) { 28 | text = null 29 | } 30 | } 31 | 32 | fun resolveText(resources: Resources): String? { 33 | return if (TextUtils.isEmpty(text)) { 34 | if (textResId == 0) { 35 | return null 36 | } 37 | resources.getString(textResId) 38 | } else text 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app-rating/src/main/java/com/stepstone/apprating/common/Preconditions.kt: -------------------------------------------------------------------------------- 1 | package com.stepstone.apprating.common 2 | 3 | /* 4 | * Copyright (C) 2007 Google Inc. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | import java.util.* 20 | 21 | /** 22 | * Simple static methods to be called at the start of your own methods to verify 23 | * correct arguments and state. This allows constructs such as 24 | *
 25 |  * if (count <= 0) {
 26 |  * throw new IllegalArgumentException("must be positive: " + count);
 27 |  * }
28 | 29 | * to be replaced with the more compact 30 | *
 31 |  * checkArgument(count > 0, "must be positive: %s", count);
32 | 33 | * Note that the sense of the expression is inverted; with `Preconditions` 34 | * you declare what you expect to be *true*, just as you do with an 35 | * [ 36 | * `assert`](http://java.sun.com/j2se/1.5.0/docs/guide/language/assert.html) or a JUnit `assertTrue` call. 37 | 38 | * 39 | * **Warning:** only the `"%s"` specifier is recognized as a 40 | * placeholder in these messages, not the full range of [ ][String.format] specifiers. 41 | 42 | * 43 | * Take care not to confuse precondition checking with other similar types 44 | * of checks! Precondition exceptions -- including those provided here, but also 45 | * [IndexOutOfBoundsException], [NoSuchElementException], [ ] and others -- are used to signal that the 46 | * *calling method* has made an error. This tells the caller that it should 47 | * not have invoked the method when it did, with the arguments it did, or 48 | * perhaps ever. Postcondition or other invariant failures should not throw 49 | * these types of exceptions. 50 | 51 | * @author Kevin Bourrillion 52 | */ 53 | object Preconditions { 54 | 55 | /** 56 | * Ensures the truth of an expression involving one or more parameters to the 57 | * calling method. 58 | 59 | * @param expression a boolean expression 60 | * * 61 | * @throws IllegalArgumentException if `expression` is false 62 | */ 63 | fun checkArgument(expression: Boolean) { 64 | if (!expression) { 65 | throw IllegalArgumentException() 66 | } 67 | } 68 | 69 | /** 70 | * Ensures the truth of an expression involving one or more parameters to the 71 | * calling method. 72 | 73 | * @param expression a boolean expression 74 | * * 75 | * @param errorMessage the exception message to use if the check fails; will 76 | * * be converted to a string using [String.valueOf] 77 | * * 78 | * @throws IllegalArgumentException if `expression` is false 79 | */ 80 | fun checkArgument(expression: Boolean, errorMessage: Any) { 81 | if (!expression) { 82 | throw IllegalArgumentException(errorMessage.toString()) 83 | } 84 | } 85 | 86 | /** 87 | * Ensures the truth of an expression involving one or more parameters to the 88 | * calling method. 89 | 90 | * @param expression a boolean expression 91 | * * 92 | * @param errorMessageTemplate a template for the exception message should the 93 | * * check fail. The message is formed by replacing each `%s` 94 | * * placeholder in the template with an argument. These are matched by 95 | * * position - the first `%s` gets `errorMessageArgs[0]`, etc. 96 | * * Unmatched arguments will be appended to the formatted message in square 97 | * * braces. Unmatched placeholders will be left as-is. 98 | * * 99 | * @param errorMessageArgs the arguments to be substituted into the message 100 | * * template. Arguments are converted to strings using 101 | * * [String.valueOf]. 102 | * * 103 | * @throws IllegalArgumentException if `expression` is false 104 | * * 105 | * @throws NullPointerException if the check fails and either `errorMessageTemplate` or `errorMessageArgs` is null (don't let 106 | * * this happen) 107 | */ 108 | fun checkArgument(expression: Boolean, 109 | errorMessageTemplate: String, vararg errorMessageArgs: Any) { 110 | if (!expression) { 111 | throw IllegalArgumentException( 112 | format(errorMessageTemplate, *errorMessageArgs)) 113 | } 114 | } 115 | 116 | /** 117 | * Ensures the truth of an expression involving the state of the calling 118 | * instance, but not involving any parameters to the calling method. 119 | 120 | * @param expression a boolean expression 121 | * * 122 | * @throws IllegalStateException if `expression` is false 123 | */ 124 | fun checkState(expression: Boolean) { 125 | if (!expression) { 126 | throw IllegalStateException() 127 | } 128 | } 129 | 130 | /** 131 | * Ensures the truth of an expression involving the state of the calling 132 | * instance, but not involving any parameters to the calling method. 133 | 134 | * @param expression a boolean expression 135 | * * 136 | * @param errorMessage the exception message to use if the check fails; will 137 | * * be converted to a string using [String.valueOf] 138 | * * 139 | * @throws IllegalStateException if `expression` is false 140 | */ 141 | fun checkState(expression: Boolean, errorMessage: Any) { 142 | if (!expression) { 143 | throw IllegalStateException(errorMessage.toString()) 144 | } 145 | } 146 | 147 | /** 148 | * Ensures the truth of an expression involving the state of the calling 149 | * instance, but not involving any parameters to the calling method. 150 | 151 | * @param expression a boolean expression 152 | * * 153 | * @param errorMessageTemplate a template for the exception message should the 154 | * * check fail. The message is formed by replacing each `%s` 155 | * * placeholder in the template with an argument. These are matched by 156 | * * position - the first `%s` gets `errorMessageArgs[0]`, etc. 157 | * * Unmatched arguments will be appended to the formatted message in square 158 | * * braces. Unmatched placeholders will be left as-is. 159 | * * 160 | * @param errorMessageArgs the arguments to be substituted into the message 161 | * * template. Arguments are converted to strings using 162 | * * [String.valueOf]. 163 | * * 164 | * @throws IllegalStateException if `expression` is false 165 | * * 166 | * @throws NullPointerException if the check fails and either `errorMessageTemplate` or `errorMessageArgs` is null (don't let 167 | * * this happen) 168 | */ 169 | fun checkState(expression: Boolean, 170 | errorMessageTemplate: String, vararg errorMessageArgs: Any) { 171 | if (!expression) { 172 | throw IllegalStateException( 173 | format(errorMessageTemplate, *errorMessageArgs)) 174 | } 175 | } 176 | 177 | /** 178 | * Ensures that an object reference passed as a parameter to the calling 179 | * method is not null. 180 | 181 | * @param reference an object reference 182 | * * 183 | * @return the non-null reference that was validated 184 | * * 185 | * @throws NullPointerException if `reference` is null 186 | */ 187 | fun checkNotNull(reference: T?): T { 188 | if (reference == null) { 189 | throw NullPointerException() 190 | } 191 | return reference 192 | } 193 | 194 | /** 195 | * Ensures that an object reference passed as a parameter to the calling 196 | * method is not null. 197 | 198 | * @param reference an object reference 199 | * * 200 | * @param errorMessage the exception message to use if the check fails; will 201 | * * be converted to a string using [String.valueOf] 202 | * * 203 | * @return the non-null reference that was validated 204 | * * 205 | * @throws NullPointerException if `reference` is null 206 | */ 207 | fun checkNotNull(reference: T?, errorMessage: Any): T { 208 | if (reference == null) { 209 | throw NullPointerException(errorMessage.toString()) 210 | } 211 | return reference 212 | } 213 | 214 | /** 215 | * Ensures that an object reference passed as a parameter to the calling 216 | * method is not null. 217 | 218 | * @param reference an object reference 219 | * * 220 | * @param errorMessageTemplate a template for the exception message should the 221 | * * check fail. The message is formed by replacing each `%s` 222 | * * placeholder in the template with an argument. These are matched by 223 | * * position - the first `%s` gets `errorMessageArgs[0]`, etc. 224 | * * Unmatched arguments will be appended to the formatted message in square 225 | * * braces. Unmatched placeholders will be left as-is. 226 | * * 227 | * @param errorMessageArgs the arguments to be substituted into the message 228 | * * template. Arguments are converted to strings using 229 | * * [String.valueOf]. 230 | * * 231 | * @return the non-null reference that was validated 232 | * * 233 | * @throws NullPointerException if `reference` is null 234 | */ 235 | fun checkNotNull(reference: T?, errorMessageTemplate: String, 236 | vararg errorMessageArgs: Any): T { 237 | if (reference == null) { 238 | // If either of these parameters is null, the right thing happens anyway 239 | throw NullPointerException( 240 | format(errorMessageTemplate, *errorMessageArgs)) 241 | } 242 | return reference 243 | } 244 | 245 | /** 246 | * Ensures that `index` specifies a valid *element* in an array, 247 | * list or string of size `size`. An element index may range from zero, 248 | * inclusive, to `size`, exclusive. 249 | 250 | * @param index a user-supplied index identifying an element of an array, list 251 | * * or string 252 | * * 253 | * @param size the size of that array, list or string 254 | * * 255 | * @param desc the text to use to describe this index in an error message 256 | * * 257 | * @return the value of `index` 258 | * * 259 | * @throws IndexOutOfBoundsException if `index` is negative or is not 260 | * * less than `size` 261 | * * 262 | * @throws IllegalArgumentException if `size` is negative 263 | */ 264 | @JvmOverloads 265 | fun checkElementIndex(index: Int, size: Int, desc: String = "index"): Int { 266 | // Carefully optimized for execution by hotspot (explanatory comment above) 267 | if (index < 0 || index >= size) { 268 | throw IndexOutOfBoundsException(badElementIndex(index, size, desc)) 269 | } 270 | return index 271 | } 272 | 273 | private fun badElementIndex(index: Int, size: Int, desc: String): String { 274 | if (index < 0) { 275 | return format("%s (%s) must not be negative", desc, index) 276 | } else if (size < 0) { 277 | throw IllegalArgumentException("negative size: " + size) 278 | } else { // index >= size 279 | return format("%s (%s) must be less than size (%s)", desc, index, size) 280 | } 281 | } 282 | 283 | /** 284 | * Ensures that `index` specifies a valid *position* in an array, 285 | * list or string of size `size`. A position index may range from zero 286 | * to `size`, inclusive. 287 | 288 | * @param index a user-supplied index identifying a position in an array, list 289 | * * or string 290 | * * 291 | * @param size the size of that array, list or string 292 | * * 293 | * @param desc the text to use to describe this index in an error message 294 | * * 295 | * @return the value of `index` 296 | * * 297 | * @throws IndexOutOfBoundsException if `index` is negative or is 298 | * * greater than `size` 299 | * * 300 | * @throws IllegalArgumentException if `size` is negative 301 | */ 302 | @JvmOverloads 303 | fun checkPositionIndex(index: Int, size: Int, desc: String = "index"): Int { 304 | // Carefully optimized for execution by hotspot (explanatory comment above) 305 | if (index < 0 || index > size) { 306 | throw IndexOutOfBoundsException(badPositionIndex(index, size, desc)) 307 | } 308 | return index 309 | } 310 | 311 | private fun badPositionIndex(index: Int, size: Int, desc: String): String { 312 | if (index < 0) { 313 | return format("%s (%s) must not be negative", desc, index) 314 | } else if (size < 0) { 315 | throw IllegalArgumentException("negative size: " + size) 316 | } else { // index > size 317 | return format("%s (%s) must not be greater than size (%s)", 318 | desc, index, size) 319 | } 320 | } 321 | 322 | /** 323 | * Ensures that `start` and `end` specify a valid *positions* 324 | * in an array, list or string of size `size`, and are in order. A 325 | * position index may range from zero to `size`, inclusive. 326 | 327 | * @param start a user-supplied index identifying a starting position in an 328 | * * array, list or string 329 | * * 330 | * @param end a user-supplied index identifying a ending position in an array, 331 | * * list or string 332 | * * 333 | * @param size the size of that array, list or string 334 | * * 335 | * @throws IndexOutOfBoundsException if either index is negative or is 336 | * * greater than `size`, or if `end` is less than `start` 337 | * * 338 | * @throws IllegalArgumentException if `size` is negative 339 | */ 340 | fun checkPositionIndexes(start: Int, end: Int, size: Int) { 341 | // Carefully optimized for execution by hotspot (explanatory comment above) 342 | if (start < 0 || end < start || end > size) { 343 | throw IndexOutOfBoundsException(badPositionIndexes(start, end, size)) 344 | } 345 | } 346 | 347 | private fun badPositionIndexes(start: Int, end: Int, size: Int): String { 348 | if (start < 0 || start > size) { 349 | return badPositionIndex(start, size, "start index") 350 | } 351 | if (end < 0 || end > size) { 352 | return badPositionIndex(end, size, "end index") 353 | } 354 | // end < start 355 | return format("end index (%s) must not be less than start index (%s)", 356 | end, start) 357 | } 358 | 359 | /** 360 | * Substitutes each `%s` in `template` with an argument. These 361 | * are matched by position - the first `%s` gets `args[0]`, etc. 362 | * If there are more arguments than placeholders, the unmatched arguments will 363 | * be appended to the end of the formatted message in square braces. 364 | 365 | * @param template a non-null string containing 0 or more `%s` 366 | * * placeholders. 367 | * * 368 | * @param args the arguments to be substituted into the message 369 | * * template. Arguments are converted to strings using 370 | * * [String.valueOf]. Arguments can be null. 371 | */ 372 | internal fun format(template: String, vararg args: Any): String { 373 | // start substituting the arguments into the '%s' placeholders 374 | val builder = StringBuilder( 375 | template.length + 16 * args.size) 376 | var templateStart = 0 377 | var i = 0 378 | while (i < args.size) { 379 | val placeholderStart = template.indexOf("%s", templateStart) 380 | if (placeholderStart == -1) { 381 | break 382 | } 383 | builder.append(template.substring(templateStart, placeholderStart)) 384 | builder.append(args[i++]) 385 | templateStart = placeholderStart + 2 386 | } 387 | builder.append(template.substring(templateStart)) 388 | 389 | // if we run out of placeholders, append the extra args in square braces 390 | if (i < args.size) { 391 | builder.append(" [") 392 | builder.append(args[i++]) 393 | while (i < args.size) { 394 | builder.append(", ") 395 | builder.append(args[i++]) 396 | } 397 | builder.append("]") 398 | } 399 | 400 | return builder.toString() 401 | } 402 | } 403 | /** 404 | * Ensures that `index` specifies a valid *element* in an array, 405 | * list or string of size `size`. An element index may range from zero, 406 | * inclusive, to `size`, exclusive. 407 | 408 | * @param index a user-supplied index identifying an element of an array, list 409 | * * or string 410 | * * 411 | * @param size the size of that array, list or string 412 | * * 413 | * @return the value of `index` 414 | * * 415 | * @throws IndexOutOfBoundsException if `index` is negative or is not 416 | * * less than `size` 417 | * * 418 | * @throws IllegalArgumentException if `size` is negative 419 | */ 420 | /** 421 | * Ensures that `index` specifies a valid *position* in an array, 422 | * list or string of size `size`. A position index may range from zero 423 | * to `size`, inclusive. 424 | 425 | * @param index a user-supplied index identifying a position in an array, list 426 | * * or string 427 | * * 428 | * @param size the size of that array, list or string 429 | * * 430 | * @return the value of `index` 431 | * * 432 | * @throws IndexOutOfBoundsException if `index` is negative or is 433 | * * greater than `size` 434 | * * 435 | * @throws IllegalArgumentException if `size` is negative 436 | */ 437 | -------------------------------------------------------------------------------- /app-rating/src/main/java/com/stepstone/apprating/extensions/extensions.kt: -------------------------------------------------------------------------------- 1 | package com.stepstone.apprating.extensions 2 | 3 | inline fun Int.applyIfNotZero(body: Int.() -> Any) { 4 | if (this != 0) { 5 | body() 6 | } 7 | } -------------------------------------------------------------------------------- /app-rating/src/main/java/com/stepstone/apprating/listener/OnRatingBarChangedListener.kt: -------------------------------------------------------------------------------- 1 | package com.stepstone.apprating.listener 2 | 3 | /** 4 | * @author Piotr Głębocki (Piotr.Glebocki@stepstone.com) on 08/05/2017. 5 | */ 6 | interface OnRatingBarChangedListener { 7 | 8 | fun onRatingChanged(rating: Int) 9 | } 10 | -------------------------------------------------------------------------------- /app-rating/src/main/java/com/stepstone/apprating/listener/RatingDialogListener.kt: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 StepStone Services 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package com.stepstone.apprating.listener 18 | 19 | /** 20 | * This listener notify when user clicked on dialog's positive button. 21 | * 22 | * @see com.stepstone.apprating.AppRatingDialog 23 | */ 24 | interface RatingDialogListener { 25 | 26 | fun onPositiveButtonClicked(rate: Int, comment: String) 27 | 28 | fun onNegativeButtonClicked() 29 | 30 | fun onNeutralButtonClicked() 31 | } 32 | -------------------------------------------------------------------------------- /app-rating/src/main/java/com/stepstone/apprating/ratingbar/CustomRatingBar.kt: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 StepStone Services 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package com.stepstone.apprating.ratingbar 18 | 19 | import android.content.Context 20 | import androidx.annotation.ColorRes 21 | import androidx.core.content.res.ResourcesCompat 22 | import android.util.AttributeSet 23 | import android.util.TypedValue 24 | import android.view.LayoutInflater 25 | import android.view.View 26 | import android.view.ViewGroup.LayoutParams.WRAP_CONTENT 27 | import android.widget.LinearLayout 28 | import com.stepstone.apprating.R 29 | import com.stepstone.apprating.common.Preconditions 30 | import com.stepstone.apprating.listener.OnRatingBarChangedListener 31 | import kotlinx.android.synthetic.main.component_custom_rating_bar.view.* 32 | 33 | /** 34 | * This class is a custom rating bar. It handles displaying of 35 | * stars, tinting etc. 36 | */ 37 | class CustomRatingBar(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs) { 38 | 39 | private val starList = ArrayList() 40 | 41 | private var numStars: Int = 0 42 | 43 | private var starColorResId: Int = 0 44 | 45 | var rating: Float = 0.0f 46 | private set 47 | 48 | private var isIndicator: Boolean = false 49 | 50 | private var onRatingBarChangedListener: OnRatingBarChangedListener? = null 51 | 52 | init { 53 | LayoutInflater.from(context).inflate(R.layout.component_custom_rating_bar, this) 54 | } 55 | 56 | private fun addStars(numberOfAll: Int, numberOfChecked: Int) { 57 | Preconditions.checkArgument(numberOfChecked <= numberOfAll, "wrong argument") 58 | 59 | starList.clear() 60 | ratingBarContainer.removeAllViews() 61 | 62 | for (index in 0 until numberOfAll) { 63 | addStar() 64 | .setCheckedWithoutAnimation(index < numberOfChecked) 65 | .setColor(getStarColor(context)) 66 | .setOnClickListener(OnStarClickedHandler(index + 1)) 67 | } 68 | } 69 | 70 | private fun addStar(): StarButton { 71 | val starButton = StarButton(context) 72 | starButton.layoutParams = LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT) 73 | starList.add(starButton) 74 | ratingBarContainer.addView(starButton) 75 | return starButton 76 | } 77 | 78 | fun setStarColor(@ColorRes colorResId: Int) { 79 | this.starColorResId = colorResId 80 | } 81 | 82 | fun setNumStars(numStars: Int) { 83 | this.numStars = numStars 84 | 85 | addStars(numStars, 0) 86 | } 87 | 88 | fun setRating(rating: Int, withAnimation: Boolean = false) { 89 | this.rating = rating.toFloat() 90 | 91 | if (rating <= starList.size) { 92 | for (index in starList.indices) { 93 | if (withAnimation) { 94 | starList[index].setChecked(index < rating) 95 | } else { 96 | starList[index].setCheckedWithoutAnimation(index < rating) 97 | } 98 | } 99 | } 100 | 101 | onRatingBarChangedListener!!.onRatingChanged(rating) 102 | } 103 | 104 | fun setIsIndicator(isIndicator: Boolean) { 105 | this.isIndicator = isIndicator 106 | } 107 | 108 | fun setOnRatingBarChangeListener(onRatingBarChangedListener: OnRatingBarChangedListener) { 109 | this.onRatingBarChangedListener = onRatingBarChangedListener 110 | } 111 | 112 | private fun getStarColor(context: Context): Int { 113 | if (starColorResId != 0) { 114 | return ResourcesCompat.getColor(context.resources, starColorResId, context.theme) 115 | } 116 | return getThemeAccentColor(context) 117 | } 118 | 119 | private fun getThemeAccentColor(context: Context): Int { 120 | val colorAttr: Int = context.resources.getIdentifier("colorAccent", "attr", context.packageName) 121 | val outValue = TypedValue() 122 | context.theme.resolveAttribute(colorAttr, outValue, true) 123 | return outValue.data 124 | } 125 | 126 | private inner class OnStarClickedHandler(private val number: Int) : View.OnClickListener { 127 | 128 | override fun onClick(v: View) { 129 | setRating(number, true) 130 | } 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /app-rating/src/main/java/com/stepstone/apprating/ratingbar/StarButton.kt: -------------------------------------------------------------------------------- 1 | package com.stepstone.apprating.ratingbar 2 | 3 | import android.content.Context 4 | import androidx.annotation.ColorInt 5 | import android.util.AttributeSet 6 | import android.view.LayoutInflater 7 | import android.widget.FrameLayout 8 | import com.stepstone.apprating.ALPHA_INVISIBLE 9 | import com.stepstone.apprating.ALPHA_VISIBLE 10 | import com.stepstone.apprating.CHECK_STAR_DURATION 11 | import com.stepstone.apprating.R 12 | import kotlinx.android.synthetic.main.star_button_layout.view.* 13 | 14 | /** 15 | * @author Piotr Głębocki (Piotr.Glebocki@stepstone.com) on 12/04/2017. 16 | */ 17 | class StarButton : FrameLayout { 18 | 19 | constructor(context: Context) : super(context) { 20 | initialize() 21 | } 22 | 23 | constructor(context: Context, attrs: AttributeSet) : super(context, attrs) 24 | 25 | private fun initialize() { 26 | val inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater 27 | inflater.inflate(R.layout.star_button_layout, this, true) 28 | } 29 | 30 | fun setChecked(checked: Boolean): StarButton { 31 | fullStarImage 32 | .animate() 33 | .alpha(if (checked) ALPHA_VISIBLE else ALPHA_INVISIBLE) 34 | .setDuration(CHECK_STAR_DURATION) 35 | .start() 36 | 37 | return this 38 | } 39 | 40 | fun setCheckedWithoutAnimation(checked: Boolean): StarButton { 41 | fullStarImage.alpha = if (checked) ALPHA_VISIBLE else ALPHA_INVISIBLE 42 | return this 43 | } 44 | 45 | fun setColor(@ColorInt color: Int): StarButton { 46 | emptyStarImage.setColorFilter(color) 47 | fullStarImage.setColorFilter(color) 48 | return this 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /app-rating/src/main/res/drawable/edit_text_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app-rating/src/main/res/drawable/ic_star_empty_48dp.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | -------------------------------------------------------------------------------- /app-rating/src/main/res/drawable/ic_star_full_48dp.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | -------------------------------------------------------------------------------- /app-rating/src/main/res/layout/component_app_rate_dialog.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 22 | 23 | 33 | 34 | 38 | 39 | 48 | 49 | 61 | 62 | -------------------------------------------------------------------------------- /app-rating/src/main/res/layout/component_custom_rating_bar.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app-rating/src/main/res/layout/star_button_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 10 | 11 | 12 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /app-rating/src/main/res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /app-rating/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 96dp 5 | 64dp 6 | 48dp 7 | 32dp 8 | 24dp 9 | 16dp 10 | 12dp 11 | 8dp 12 | 6dp 13 | 4dp 14 | 2dp 15 | 0dp 16 | 17 | 12sp 18 | 14sp 19 | 16sp 20 | 18sp 21 | 20sp 22 | 22sp 23 | 32sp 24 | 36sp 25 | 26 | -------------------------------------------------------------------------------- /app-rating/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | AppRating 3 | 4 | -------------------------------------------------------------------------------- /app-rating/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 | 13 | 17 | 18 | 19 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /bintrayv1.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.jfrog.bintray' 2 | 3 | version = libraryVersion 4 | 5 | if (project.hasProperty("android")) { // Android libraries 6 | task sourcesJar(type: Jar) { 7 | classifier = 'sources' 8 | from android.sourceSets.main.java.srcDirs 9 | } 10 | 11 | task javadoc(type: Javadoc) { 12 | source = android.sourceSets.main.java.srcDirs 13 | classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) 14 | } 15 | } else { // Java libraries 16 | task sourcesJar(type: Jar, dependsOn: classes) { 17 | classifier = 'sources' 18 | from sourceSets.main.allSource 19 | } 20 | } 21 | 22 | task javadocJar(type: Jar, dependsOn: javadoc) { 23 | classifier = 'javadoc' 24 | from javadoc.destinationDir 25 | } 26 | 27 | artifacts { 28 | archives javadocJar 29 | archives sourcesJar 30 | } 31 | 32 | /* 33 | * 34 | * Copyright 2017 StepStone Services 35 | * 36 | * Licensed under the Apache License, Version 2.0 (the "License"); 37 | * you may not use this file except in compliance with the License. 38 | * You may obtain a copy of the License at 39 | * 40 | * http://www.apache.org/licenses/LICENSE-2.0 41 | * 42 | * Unless required by applicable law or agreed to in writing, software 43 | * distributed under the License is distributed on an "AS IS" BASIS, 44 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 45 | * See the License for the specific language governing permissions and 46 | * limitations under the License. 47 | * 48 | */ 49 | 50 | // Bintray 51 | Properties properties = new Properties() 52 | properties.load(project.rootProject.file('local.properties').newDataInputStream()) 53 | 54 | bintray { 55 | user = properties.getProperty("bintray.user") 56 | key = properties.getProperty("bintray.apikey") 57 | 58 | configurations = ['archives'] 59 | pkg { 60 | repo = bintrayRepo 61 | name = bintrayName 62 | desc = libraryDescription 63 | websiteUrl = siteUrl 64 | vcsUrl = gitUrl 65 | licenses = allLicenses 66 | publish = true 67 | publicDownloadNumbers = true 68 | version { 69 | desc = libraryDescription 70 | } 71 | } 72 | } -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | ext.kotlin_version = '1.3.20' 5 | ext.bintrayVersion = '1.7.3' 6 | ext.mavenGradlePluginVersion = '2.0' 7 | ext.detektVersion = "1.0.0.RC8" 8 | 9 | repositories { 10 | jcenter() 11 | google() 12 | maven { 13 | url "https://plugins.gradle.org/m2/" 14 | } 15 | } 16 | dependencies { 17 | classpath 'com.android.tools.build:gradle:3.3.0' 18 | classpath "com.jfrog.bintray.gradle:gradle-bintray-plugin:$bintrayVersion" 19 | classpath "com.github.dcendents:android-maven-gradle-plugin:$mavenGradlePluginVersion" 20 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 21 | classpath "gradle.plugin.io.gitlab.arturbosch.detekt:detekt-gradle-plugin:$detektVersion" 22 | } 23 | } 24 | 25 | allprojects { 26 | repositories { 27 | jcenter() 28 | google() 29 | } 30 | } 31 | 32 | task clean(type: Delete) { 33 | delete rootProject.buildDir 34 | } 35 | 36 | configure(allprojects) { 37 | /* Android config and dependency versions */ 38 | ext { 39 | androidMinSdkVersion = 14 40 | androidTargetSdkVersion = 28 41 | androidCompileSdkVersion = 28 42 | androidBuildToolsVersion = "28.0.3" 43 | androidXLibraryVersion = "1.0.0-beta01" 44 | } 45 | } 46 | 47 | -------------------------------------------------------------------------------- /gifs/firstDialog.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stepstone-tech/android-material-app-rating/8f707b7da541878c6147c55bf03862de01af3892/gifs/firstDialog.gif -------------------------------------------------------------------------------- /gifs/secondDialog.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stepstone-tech/android-material-app-rating/8f707b7da541878c6147c55bf03862de01af3892/gifs/secondDialog.gif -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | org.gradle.jvmargs=-Xmx1536m 13 | 14 | # When configured, Gradle will run in incubating parallel mode. 15 | # This option should only be used with decoupled projects. More details, visit 16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 17 | # org.gradle.parallel=true 18 | 19 | POM_GROUP_ID=com.stepstone.apprating 20 | POM_ARTIFACT_ID=app-rating 21 | POM_VERSION=2.3.1 22 | android.useAndroidX=true 23 | android.enableJetifier=true 24 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stepstone-tech/android-material-app-rating/8f707b7da541878c6147c55bf03862de01af3892/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Tue Jan 29 11:06:46 CET 2019 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-4.10.1-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /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 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 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 Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /graphics/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stepstone-tech/android-material-app-rating/8f707b7da541878c6147c55bf03862de01af3892/graphics/logo.png -------------------------------------------------------------------------------- /graphics/sample.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stepstone-tech/android-material-app-rating/8f707b7da541878c6147c55bf03862de01af3892/graphics/sample.png -------------------------------------------------------------------------------- /graphics/stepstone-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stepstone-tech/android-material-app-rating/8f707b7da541878c6147c55bf03862de01af3892/graphics/stepstone-logo.png -------------------------------------------------------------------------------- /installv1.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.github.dcendents.android-maven' 2 | 3 | group = publishedGroupId // Maven Group ID for the artifact 4 | 5 | install { 6 | repositories.mavenInstaller { 7 | // This generates POM.xml with proper parameters 8 | pom { 9 | project { 10 | packaging 'aar' 11 | groupId publishedGroupId 12 | artifactId artifact 13 | 14 | // Add your description here 15 | name libraryName 16 | description libraryDescription 17 | url siteUrl 18 | 19 | // Set your license 20 | licenses { 21 | license { 22 | name licenseName 23 | url licenseUrl 24 | } 25 | } 26 | developers { 27 | developer { 28 | id developerId 29 | name developerName 30 | email developerEmail 31 | } 32 | } 33 | scm { 34 | connection gitUrl 35 | developerConnection gitUrl 36 | url siteUrl 37 | } 38 | } 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /quality/detekt.yml: -------------------------------------------------------------------------------- 1 | failFast: false 2 | 3 | test-pattern: 4 | active: true 5 | patterns: # Test file regexes 6 | - '.*/test/.*' 7 | - '.*Test.kt' 8 | - '.*Spec.kt' 9 | - '.*Preconditions.kt' 10 | exclude-rule-sets: 11 | - 'comments' 12 | exclude-rules: 13 | - 'NamingRules' 14 | - 'WildcardImport' 15 | - 'MagicNumber' 16 | - 'MaxLineLength' 17 | - 'LateinitUsage' 18 | - 'StringLiteralDuplication' 19 | - 'SpreadOperator' 20 | - 'TooManyFunctions' 21 | - 'ForEachOnRange' 22 | - 'ReturnCount' 23 | - 'ParameterListWrapping' 24 | - 'NoMultipleSpaces' 25 | - 'NoWildcardImports' 26 | 27 | build: 28 | maxIssues: 10 29 | weights: 30 | # complexity: 2 31 | # LongParameterList: 1 32 | # style: 1 33 | # comments: 1 34 | 35 | processors: 36 | active: true 37 | exclude: 38 | # - 'FunctionCountProcessor' 39 | # - 'PropertyCountProcessor' 40 | # - 'ClassCountProcessor' 41 | # - 'PackageCountProcessor' 42 | # - 'KtFileCountProcessor' 43 | 44 | console-reports: 45 | active: true 46 | exclude: 47 | # - 'ProjectStatisticsReport' 48 | # - 'ComplexityReport' 49 | # - 'NotificationReport' 50 | # - 'FindingsReport' 51 | # - 'BuildFailureReport' 52 | 53 | output-reports: 54 | active: true 55 | exclude: 56 | # - 'HtmlOutputReport' 57 | # - 'PlainOutputReport' 58 | # - 'XmlOutputReport' 59 | 60 | comments: 61 | active: true 62 | CommentOverPrivateFunction: 63 | active: false 64 | CommentOverPrivateProperty: 65 | active: false 66 | EndOfSentenceFormat: 67 | active: false 68 | endOfSentenceFormat: ([.?!][ \t\n\r\f<])|([.?!]$) 69 | UndocumentedPublicClass: 70 | active: false 71 | searchInNestedClass: true 72 | searchInInnerClass: true 73 | searchInInnerObject: true 74 | searchInInnerInterface: true 75 | UndocumentedPublicFunction: 76 | active: false 77 | 78 | complexity: 79 | active: true 80 | ComplexCondition: 81 | active: true 82 | threshold: 4 83 | ComplexInterface: 84 | active: false 85 | threshold: 10 86 | includeStaticDeclarations: false 87 | ComplexMethod: 88 | active: true 89 | threshold: 10 90 | ignoreSingleWhenExpression: false 91 | LabeledExpression: 92 | active: false 93 | LargeClass: 94 | active: true 95 | threshold: 300 96 | LongMethod: 97 | active: true 98 | threshold: 20 99 | LongParameterList: 100 | active: true 101 | threshold: 6 102 | ignoreDefaultParameters: false 103 | MethodOverloading: 104 | active: false 105 | threshold: 6 106 | NestedBlockDepth: 107 | active: true 108 | threshold: 4 109 | StringLiteralDuplication: 110 | active: false 111 | threshold: 3 112 | ignoreAnnotation: true 113 | excludeStringsWithLessThan5Characters: true 114 | ignoreStringsRegex: '$^' 115 | TooManyFunctions: 116 | active: true 117 | thresholdInFiles: 30 118 | thresholdInClasses: 30 119 | thresholdInInterfaces: 30 120 | thresholdInObjects: 11 121 | thresholdInEnums: 11 122 | ignoreDeprecated: false 123 | ignorePrivate: false 124 | 125 | empty-blocks: 126 | active: true 127 | EmptyCatchBlock: 128 | active: true 129 | allowedExceptionNameRegex: "^(_|(ignore|expected).*)" 130 | EmptyClassBlock: 131 | active: true 132 | EmptyDefaultConstructor: 133 | active: true 134 | EmptyDoWhileBlock: 135 | active: true 136 | EmptyElseBlock: 137 | active: true 138 | EmptyFinallyBlock: 139 | active: true 140 | EmptyForBlock: 141 | active: true 142 | EmptyFunctionBlock: 143 | active: true 144 | ignoreOverriddenFunctions: false 145 | EmptyIfBlock: 146 | active: true 147 | EmptyInitBlock: 148 | active: true 149 | EmptyKtFile: 150 | active: true 151 | EmptySecondaryConstructor: 152 | active: true 153 | EmptyWhenBlock: 154 | active: true 155 | EmptyWhileBlock: 156 | active: true 157 | 158 | exceptions: 159 | active: true 160 | ExceptionRaisedInUnexpectedLocation: 161 | active: false 162 | methodNames: 'toString,hashCode,equals,finalize' 163 | InstanceOfCheckForException: 164 | active: false 165 | NotImplementedDeclaration: 166 | active: false 167 | PrintStackTrace: 168 | active: false 169 | RethrowCaughtException: 170 | active: false 171 | ReturnFromFinally: 172 | active: false 173 | SwallowedException: 174 | active: false 175 | ThrowingExceptionFromFinally: 176 | active: false 177 | ThrowingExceptionInMain: 178 | active: false 179 | ThrowingExceptionsWithoutMessageOrCause: 180 | active: false 181 | exceptions: 'IllegalArgumentException,IllegalStateException,IOException' 182 | ThrowingNewInstanceOfSameException: 183 | active: false 184 | TooGenericExceptionCaught: 185 | active: true 186 | exceptionNames: 187 | - ArrayIndexOutOfBoundsException 188 | - Error 189 | - Exception 190 | - IllegalMonitorStateException 191 | - NullPointerException 192 | - IndexOutOfBoundsException 193 | - RuntimeException 194 | - Throwable 195 | TooGenericExceptionThrown: 196 | active: true 197 | exceptionNames: 198 | - Error 199 | - Exception 200 | - Throwable 201 | - RuntimeException 202 | 203 | formatting: 204 | active: true 205 | ChainWrapping: 206 | active: true 207 | CommentSpacing: 208 | active: true 209 | Filename: 210 | active: true 211 | FinalNewline: 212 | active: true 213 | ImportOrdering: 214 | active: true 215 | Indentation: 216 | active: true 217 | indentSize: 4 218 | continuationIndentSize: 4 219 | MaximumLineLength: 220 | active: true 221 | maxLineLength: 130 222 | ModifierOrdering: 223 | active: true 224 | NoBlankLineBeforeRbrace: 225 | active: true 226 | NoConsecutiveBlankLines: 227 | active: true 228 | NoEmptyClassBody: 229 | active: true 230 | NoItParamInMultilineLambda: 231 | active: true 232 | NoLineBreakAfterElse: 233 | active: true 234 | NoLineBreakBeforeAssignment: 235 | active: true 236 | NoMultipleSpaces: 237 | active: true 238 | NoSemicolons: 239 | active: true 240 | NoTrailingSpaces: 241 | active: true 242 | NoUnitReturn: 243 | active: true 244 | NoUnusedImports: 245 | active: true 246 | NoWildcardImports: 247 | active: true 248 | ParameterListWrapping: 249 | active: true 250 | indentSize: 4 251 | SpacingAroundColon: 252 | active: true 253 | SpacingAroundComma: 254 | active: true 255 | SpacingAroundCurly: 256 | active: true 257 | SpacingAroundKeyword: 258 | active: true 259 | SpacingAroundOperators: 260 | active: true 261 | SpacingAroundRangeOperator: 262 | active: true 263 | StringTemplate: 264 | active: true 265 | 266 | naming: 267 | active: true 268 | ClassNaming: 269 | active: true 270 | classPattern: '[A-Z$][a-zA-Z0-9$]*' 271 | EnumNaming: 272 | active: true 273 | enumEntryPattern: '^[A-Z][_a-zA-Z0-9]*' 274 | ForbiddenClassName: 275 | active: false 276 | forbiddenName: '' 277 | FunctionMaxLength: 278 | active: false 279 | maximumFunctionNameLength: 30 280 | FunctionMinLength: 281 | active: false 282 | minimumFunctionNameLength: 3 283 | FunctionNaming: 284 | active: true 285 | functionPattern: '^([a-z$][a-zA-Z$0-9]*)|(`.*`)$' 286 | excludeClassPattern: '$^' 287 | MatchingDeclarationName: 288 | active: true 289 | MemberNameEqualsClassName: 290 | active: false 291 | ignoreOverriddenFunction: true 292 | ObjectPropertyNaming: 293 | active: true 294 | propertyPattern: '[A-Za-z][_A-Za-z0-9]*' 295 | constantPattern: '[A-Za-z][_A-Za-z0-9]*' 296 | PackageNaming: 297 | active: true 298 | packagePattern: '^[a-z]+(\.[a-z][a-z0-9]*)*$' 299 | TopLevelPropertyNaming: 300 | active: true 301 | constantPattern: '[A-Z][_A-Z0-9]*' 302 | propertyPattern: '[A-Za-z][_A-Za-z0-9]*' 303 | privatePropertyPattern: '(_)?[A-Za-z][A-Za-z0-9]*' 304 | VariableMaxLength: 305 | active: false 306 | maximumVariableNameLength: 64 307 | VariableMinLength: 308 | active: false 309 | minimumVariableNameLength: 1 310 | VariableNaming: 311 | active: true 312 | variablePattern: '[a-z][A-Za-z0-9]*' 313 | privateVariablePattern: '(_)?[a-z][A-Za-z0-9]*' 314 | excludeClassPattern: '$^' 315 | 316 | performance: 317 | active: true 318 | ForEachOnRange: 319 | active: true 320 | SpreadOperator: 321 | active: true 322 | UnnecessaryTemporaryInstantiation: 323 | active: true 324 | 325 | potential-bugs: 326 | active: true 327 | DuplicateCaseInWhenExpression: 328 | active: true 329 | EqualsAlwaysReturnsTrueOrFalse: 330 | active: false 331 | EqualsWithHashCodeExist: 332 | active: true 333 | ExplicitGarbageCollectionCall: 334 | active: true 335 | InvalidRange: 336 | active: false 337 | IteratorHasNextCallsNextMethod: 338 | active: false 339 | IteratorNotThrowingNoSuchElementException: 340 | active: false 341 | LateinitUsage: 342 | active: false 343 | excludeAnnotatedProperties: "" 344 | ignoreOnClassesPattern: "" 345 | UnconditionalJumpStatementInLoop: 346 | active: false 347 | UnreachableCode: 348 | active: true 349 | UnsafeCallOnNullableType: 350 | active: false 351 | UnsafeCast: 352 | active: false 353 | UselessPostfixExpression: 354 | active: false 355 | WrongEqualsTypeParameter: 356 | active: false 357 | 358 | style: 359 | active: true 360 | CollapsibleIfStatements: 361 | active: false 362 | DataClassContainsFunctions: 363 | active: false 364 | conversionFunctionPrefix: 'to' 365 | EqualsNullCall: 366 | active: false 367 | ExpressionBodySyntax: 368 | active: false 369 | includeLineWrapping: false 370 | ForbiddenComment: 371 | active: true 372 | values: 'TODO:,FIXME:,STOPSHIP:' 373 | ForbiddenImport: 374 | active: false 375 | imports: '' 376 | FunctionOnlyReturningConstant: 377 | active: false 378 | ignoreOverridableFunction: true 379 | excludedFunctions: 'describeContents' 380 | LoopWithTooManyJumpStatements: 381 | active: false 382 | maxJumpCount: 1 383 | MagicNumber: 384 | active: true 385 | ignoreNumbers: '-1,0,1,2' 386 | ignoreHashCodeFunction: false 387 | ignorePropertyDeclaration: false 388 | ignoreConstantDeclaration: true 389 | ignoreCompanionObjectPropertyDeclaration: true 390 | ignoreAnnotation: false 391 | ignoreNamedArgument: true 392 | ignoreEnums: false 393 | MandatoryBracesIfStatements: 394 | active: false 395 | MaxLineLength: 396 | active: true 397 | maxLineLength: 120 398 | excludePackageStatements: false 399 | excludeImportStatements: false 400 | excludeCommentStatements: false 401 | MayBeConst: 402 | active: false 403 | ModifierOrder: 404 | active: true 405 | NestedClassesVisibility: 406 | active: false 407 | NewLineAtEndOfFile: 408 | active: false 409 | NoTabs: 410 | active: false 411 | OptionalAbstractKeyword: 412 | active: true 413 | OptionalUnit: 414 | active: false 415 | OptionalWhenBraces: 416 | active: false 417 | PreferToOverPairSyntax: 418 | active: false 419 | ProtectedMemberInFinalClass: 420 | active: false 421 | RedundantVisibilityModifierRule: 422 | active: false 423 | ReturnCount: 424 | active: true 425 | max: 2 426 | excludedFunctions: "equals" 427 | SafeCast: 428 | active: true 429 | SerialVersionUIDInSerializableClass: 430 | active: false 431 | SpacingBetweenPackageAndImports: 432 | active: false 433 | ThrowsCount: 434 | active: true 435 | max: 2 436 | TrailingWhitespace: 437 | active: false 438 | UnnecessaryAbstractClass: 439 | active: false 440 | UnnecessaryInheritance: 441 | active: false 442 | UnnecessaryParentheses: 443 | active: false 444 | UntilInsteadOfRangeTo: 445 | active: false 446 | UnusedImports: 447 | active: false 448 | UnusedPrivateMember: 449 | active: false 450 | allowedNames: "(_|ignored|expected)" 451 | UseDataClass: 452 | active: false 453 | excludeAnnotatedClasses: "" 454 | UtilityClassWithPublicConstructor: 455 | active: false 456 | WildcardImport: 457 | active: false 458 | excludeImports: 'java.util.*,kotlinx.android.synthetic.*' -------------------------------------------------------------------------------- /samples/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /samples/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'kotlin-android' 3 | apply plugin: 'kotlin-android-extensions' 4 | 5 | android { 6 | compileSdkVersion project.androidCompileSdkVersion 7 | buildToolsVersion ("$androidBuildToolsVersion") 8 | 9 | defaultConfig { 10 | minSdkVersion project.androidMinSdkVersion 11 | targetSdkVersion project.androidTargetSdkVersion 12 | applicationId "com.stepstone.apprating.sample" 13 | versionCode 1 14 | versionName "1.0" 15 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 16 | } 17 | buildTypes { 18 | release { 19 | minifyEnabled false 20 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 21 | } 22 | } 23 | 24 | flavorDimensions "sample" 25 | 26 | productFlavors { 27 | first { 28 | applicationId = "com.stepstone.apprating.sample.first" 29 | buildConfigField "String", "GROUP", "\"first\"" 30 | } 31 | 32 | second { 33 | applicationId = "com.stepstone.apprating.sample.second" 34 | buildConfigField "String", "GROUP", "\"second\"" 35 | } 36 | } 37 | buildToolsVersion project.androidBuildToolsVersion 38 | } 39 | 40 | dependencies { 41 | implementation project(':app-rating') 42 | implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version") 43 | implementation ("androidx.appcompat:appcompat:$androidXLibraryVersion") 44 | testImplementation 'junit:junit:4.12' 45 | } 46 | -------------------------------------------------------------------------------- /samples/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/pglebocki/Library/Android/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /samples/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /samples/src/main/java/com/stepstone/apprating/sample/SamplesActivity.kt: -------------------------------------------------------------------------------- 1 | package com.stepstone.apprating.sample 2 | 3 | import android.os.Bundle 4 | import android.widget.Toast 5 | import androidx.fragment.app.FragmentActivity 6 | import com.stepstone.apprating.AppRatingDialog 7 | import com.stepstone.apprating.listener.RatingDialogListener 8 | import kotlinx.android.synthetic.main.activity_samples.* 9 | 10 | class SamplesActivity : FragmentActivity(), RatingDialogListener { 11 | 12 | override fun onCreate(savedInstanceState: Bundle?) { 13 | super.onCreate(savedInstanceState) 14 | setContentView(R.layout.activity_samples) 15 | 16 | showDialogButton_1.setOnClickListener { showRatingDialog_example1() } 17 | showDialogButton_2.setOnClickListener { showRatingDialog_example2() } 18 | showDialogButton_3.setOnClickListener { showRatingDialog_example3() } 19 | showDialogButton_4.setOnClickListener { showRatingDialog_example4() } 20 | } 21 | 22 | private fun showRatingDialog_example1() { 23 | AppRatingDialog.Builder() 24 | .setPositiveButtonText("Submit") 25 | .setNegativeButtonText("Cancel") 26 | .setNeutralButtonText("Later") 27 | .setNoteDescriptions(listOf("Very Bad", "Not good", "Quite ok", "Very Good", "Excellent !!!")) 28 | .setDefaultRating(2) 29 | .setTitle("Rate this application") 30 | .setDescription("Please select some stars and give your feedback") 31 | .setStarColor(R.color.starColor) 32 | .setNoteDescriptionTextColor(R.color.noteDescriptionTextColor) 33 | .setTitleTextColor(R.color.titleTextColor) 34 | .setDescriptionTextColor(R.color.descriptionTextColor) 35 | .setCommentTextColor(R.color.commentTextColor) 36 | .setCommentBackgroundColor(R.color.colorPrimaryDark) 37 | .setWindowAnimation(R.style.MyDialogSlideHorizontalAnimation) 38 | .setHint("Please write your comment here ...") 39 | .setHintTextColor(R.color.hintTextColor) 40 | .setCancelable(false) 41 | .setCanceledOnTouchOutside(false) 42 | .create(this@SamplesActivity) 43 | .show() 44 | } 45 | 46 | private fun showRatingDialog_example2() { 47 | AppRatingDialog.Builder() 48 | .setPositiveButtonText("Send feedback") 49 | .setNumberOfStars(6) 50 | .setDefaultRating(0) 51 | .setTitle("Rate this application") 52 | .setTitleTextColor(R.color.titleTextColor) 53 | .setCommentTextColor(R.color.commentTextColor) 54 | .setCommentBackgroundColor(R.color.colorPrimaryDark) 55 | .setWindowAnimation(R.style.MyDialogFadeAnimation) 56 | .setHint("Please write your comment here ...") 57 | .setHintTextColor(R.color.hintTextColor) 58 | .create(this@SamplesActivity) 59 | .show() 60 | } 61 | 62 | private fun showRatingDialog_example3() { 63 | AppRatingDialog.Builder() 64 | .setPositiveButtonText(R.string.send_review) 65 | .setNegativeButtonText(R.string.cancel) 66 | .setNeutralButtonText(R.string.later) 67 | .setNumberOfStars(5) 68 | .setDefaultRating(3) 69 | .setTitle(R.string.title) 70 | .setTitleTextColor(R.color.titleTextColor) 71 | .setDescription(R.string.description) 72 | .setDefaultComment(R.string.defaultComment) 73 | .setDescriptionTextColor(R.color.descriptionTextColor) 74 | .setCommentTextColor(R.color.commentTextColor) 75 | .setCommentBackgroundColor(R.color.colorPrimaryDark) 76 | .setWindowAnimation(R.style.MyDialogSlideVerticalAnimation) 77 | .create(this@SamplesActivity) 78 | .show() 79 | } 80 | 81 | private fun showRatingDialog_example4() { 82 | AppRatingDialog.Builder() 83 | .setPositiveButtonText("Send feedback") 84 | .setNumberOfStars(6) 85 | .setDefaultRating(4) 86 | .setTitle("Rate this application") 87 | .setTitleTextColor(R.color.titleTextColor) 88 | .setCommentInputEnabled(false) 89 | .setWindowAnimation(R.style.MyDialogFadeAnimation) 90 | .setHint("Please write your comment here ...") 91 | .setHintTextColor(R.color.hintTextColor) 92 | .create(this@SamplesActivity) 93 | .show() 94 | } 95 | 96 | override fun onPositiveButtonClicked(rate: Int, comment: String) { 97 | Toast.makeText(this@SamplesActivity, "Rate : $rate\nComment : $comment", Toast.LENGTH_LONG).show() 98 | } 99 | 100 | override fun onNegativeButtonClicked() { 101 | Toast.makeText(this@SamplesActivity, "Negative button clicked", Toast.LENGTH_LONG).show() 102 | } 103 | 104 | override fun onNeutralButtonClicked() { 105 | Toast.makeText(this@SamplesActivity, "Neutral button clicked", Toast.LENGTH_LONG).show() 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /samples/src/main/java/com/stepstone/apprating/sample/SamplesFragment.kt: -------------------------------------------------------------------------------- 1 | package com.stepstone.apprating.sample 2 | 3 | import android.os.Bundle 4 | import androidx.fragment.app.Fragment 5 | import android.view.LayoutInflater 6 | import android.view.View 7 | import android.view.ViewGroup 8 | import android.widget.Toast 9 | import com.stepstone.apprating.AppRatingDialog 10 | import com.stepstone.apprating.listener.RatingDialogListener 11 | import kotlinx.android.synthetic.main.fragment_samples.* 12 | 13 | class SamplesFragment : Fragment(), RatingDialogListener { 14 | 15 | override fun onCreateView( 16 | inflater: LayoutInflater, container: ViewGroup?, 17 | savedInstanceState: Bundle? 18 | ): View? { 19 | return inflater.inflate(R.layout.fragment_samples, container, false) 20 | } 21 | 22 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 23 | super.onViewCreated(view, savedInstanceState) 24 | showDialogButton_1.setOnClickListener { showRatingDialog() } 25 | } 26 | 27 | private fun showRatingDialog() { 28 | AppRatingDialog.Builder() 29 | .setPositiveButtonText("Submit") 30 | .setNegativeButtonText("Cancel") 31 | .setNeutralButtonText("Later") 32 | .setDefaultRating(2) 33 | .setTitle("Rate this application") 34 | .setDescription("Please select some stars and give your feedback") 35 | .setStarColor(R.color.starColor) 36 | .setNoteDescriptionTextColor(R.color.noteDescriptionTextColor) 37 | .setTitleTextColor(R.color.titleTextColor) 38 | .setDescriptionTextColor(R.color.descriptionTextColor) 39 | .setCommentTextColor(R.color.commentTextColor) 40 | .setCommentBackgroundColor(R.color.colorPrimaryDark) 41 | .setWindowAnimation(R.style.MyDialogSlideHorizontalAnimation) 42 | .setHint("Please write your comment here ...") 43 | .setHintTextColor(R.color.hintTextColor) 44 | .setCancelable(false) 45 | .setCanceledOnTouchOutside(false) 46 | .create(activity!!) 47 | .setTargetFragment(this, 0) 48 | .show() 49 | } 50 | 51 | override fun onPositiveButtonClicked(rate: Int, comment: String) { 52 | Toast.makeText(activity, "Rate : $rate\nComment : $comment", Toast.LENGTH_LONG).show() 53 | } 54 | 55 | override fun onNegativeButtonClicked() { 56 | Toast.makeText(activity, "Negative button clicked", Toast.LENGTH_LONG).show() 57 | } 58 | 59 | override fun onNeutralButtonClicked() { 60 | Toast.makeText(activity, "Neutral button clicked", Toast.LENGTH_LONG).show() 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /samples/src/main/java/com/stepstone/apprating/sample/SamplesFragmentActivity.kt: -------------------------------------------------------------------------------- 1 | package com.stepstone.apprating.sample 2 | 3 | import android.os.Bundle 4 | import androidx.fragment.app.FragmentActivity 5 | 6 | class SamplesFragmentActivity : FragmentActivity() { 7 | 8 | override fun onCreate(savedInstanceState: Bundle?) { 9 | super.onCreate(savedInstanceState) 10 | setContentView(R.layout.activity_fragment_samples) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /samples/src/main/res/anim/slide_down.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /samples/src/main/res/anim/slide_left.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /samples/src/main/res/anim/slide_right.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /samples/src/main/res/anim/slide_up.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /samples/src/main/res/layout/activity_fragment_samples.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /samples/src/main/res/layout/activity_samples.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 |