├── .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 [](https://travis-ci.org/stepstone-tech/android-material-app-rating) [](https://android-arsenal.com/details/1/6143)[](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 |
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 | *