From 48c14866b3accaed0ebc075a1d0fa333f5e4c62f Mon Sep 17 00:00:00 2001 From: Manjunath Chandrashekar Date: Wed, 8 Mar 2017 15:58:05 +0530 Subject: [PATCH] Initial commit --- .gitignore | 14 + .travis.yml | 60 +++ LICENSE | 201 ++++++++ README.md | 24 + build.gradle | 149 ++++++ ci.sh | 3 + circle.yml | 31 ++ coachmarks/.gitignore | 1 + coachmarks/build.gradle | 63 +++ coachmarks/proguard-rules.pro | 17 + .../myntra/coachmarks/ApplicationTest.java | 13 + coachmarks/src/main/AndroidManifest.xml | 3 + .../com/myntra/coachmarks/PopUpCoachMark.java | 357 ++++++++++++++ .../coachmarks/builder/CoachMarkBuilder.java | 137 ++++++ .../builder/CoachMarkLayoutMargin.java | 46 ++ .../builder/CoachMarkPixelInfo.java | 92 ++++ .../builder/ImageLayoutInformation.java | 34 ++ .../coachmarks/builder/InfoForViewToMask.java | 37 ++ .../coachmarks/common/AnimationType.java | 18 + .../common/CoachMarkAlignPosition.java | 19 + .../common/CoachMarkLayoutOrientation.java | 14 + .../common/CoachMarkTextGravity.java | 17 + .../common/DialogDismissButtonPosition.java | 14 + .../coachmarks/common/PopUpPosition.java | 20 + .../DefaultDimensionResourceProvider.java | 23 + .../providers/DefaultScreenInfoProvider.java | 30 ++ .../DefaultStringResourceProvider.java | 28 ++ .../providers/DefaultTypeFaceProvider.java | 29 ++ .../IDimensionResourceProvider.java | 7 + .../interfaces/IScreenInfoProvider.java | 7 + .../interfaces/IStringResourceProvider.java | 12 + .../interfaces/ITypeFaceProvider.java | 7 + .../coachmarks/ui/common/BaseViews.java | 26 + .../IPopUpCoachMarkPresentation.java | 52 ++ .../ui/presenter/PopUpCoachMarkPresenter.java | 450 ++++++++++++++++++ .../coachmarks/ui/utils/TransitionUtils.java | 52 ++ .../drawable/background_shadow_drawable.xml | 19 + ...ch_mark_vertical_dashed_line_separator.xml | 16 + .../drawable/coachmark_drawable_no_image.xml | 5 + .../drawable/coachmark_pop_up_drawable.xml | 19 + .../src/main/res/drawable/triangle_up.xml | 17 + .../src/main/res/layout/pop_up_coach_mark.xml | 121 +++++ coachmarks/src/main/res/values-v21/styles.xml | 15 + coachmarks/src/main/res/values/attrs.xml | 4 + coachmarks/src/main/res/values/color.xml | 16 + coachmarks/src/main/res/values/dimens.xml | 40 ++ coachmarks/src/main/res/values/integers.xml | 5 + coachmarks/src/main/res/values/strings.xml | 6 + coachmarks/src/main/res/values/styles.xml | 20 + .../myntra/coachmarks/CoachMarkUnitTest.java | 12 + .../PopUpCoachMarkPresenterTest.java | 35 ++ config/checkstyle/checkstyle.xml | 40 ++ config/checkstyle/suppressions.xml | 14 + config/findbugs/findbugs.xml | 20 + config/lint/lint.xml | 154 ++++++ config/pmd/pmd.xml | 30 ++ contributors.txt | 3 + dependencies.gradle | 206 ++++++++ gradle.properties | 18 + gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 53636 bytes gradle/wrapper/gradle-wrapper.properties | 6 + gradlew | 160 +++++++ gradlew.bat | 90 ++++ sample/.gitignore | 14 + sample/build.gradle | 34 ++ sample/proguard-rules.pro | 17 + .../android/sample/ApplicationTest.java | 10 + sample/src/main/AndroidManifest.xml | 20 + sample/src/main/assets/Whitney-Book-Bas.otf | Bin 0 -> 97756 bytes .../src/main/assets/Whitney-Semibold-Bas.otf | Bin 0 -> 99444 bytes .../myntra/sample/CoachMarksDemoActivity.java | 61 +++ .../res/drawable/similar_item_drawable.xml | 11 + .../res/layout/activity_coachmarks_demo.xml | 25 + .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 3418 bytes .../src/main/res/mipmap-hdpi/ic_wishlist.png | Bin 0 -> 322 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 2206 bytes .../src/main/res/mipmap-mdpi/ic_wishlist.png | Bin 0 -> 244 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 4842 bytes .../src/main/res/mipmap-xhdpi/ic_wishlist.png | Bin 0 -> 372 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 7718 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 10486 bytes sample/src/main/res/values-w820dp/dimens.xml | 6 + sample/src/main/res/values/colors.xml | 8 + sample/src/main/res/values/dimens.xml | 7 + sample/src/main/res/values/strings.xml | 4 + sample/src/main/res/values/styles.xml | 11 + .../android/sample/CoachMarkUnitTest.java | 12 + settings.gradle | 1 + 88 files changed, 3439 insertions(+) create mode 100644 .gitignore create mode 100644 .travis.yml create mode 100644 LICENSE create mode 100644 README.md create mode 100644 build.gradle create mode 100644 ci.sh create mode 100644 circle.yml create mode 100644 coachmarks/.gitignore create mode 100644 coachmarks/build.gradle create mode 100644 coachmarks/proguard-rules.pro create mode 100644 coachmarks/src/androidTest/java/com/myntra/coachmarks/ApplicationTest.java create mode 100644 coachmarks/src/main/AndroidManifest.xml create mode 100644 coachmarks/src/main/java/com/myntra/coachmarks/PopUpCoachMark.java create mode 100644 coachmarks/src/main/java/com/myntra/coachmarks/builder/CoachMarkBuilder.java create mode 100644 coachmarks/src/main/java/com/myntra/coachmarks/builder/CoachMarkLayoutMargin.java create mode 100644 coachmarks/src/main/java/com/myntra/coachmarks/builder/CoachMarkPixelInfo.java create mode 100644 coachmarks/src/main/java/com/myntra/coachmarks/builder/ImageLayoutInformation.java create mode 100644 coachmarks/src/main/java/com/myntra/coachmarks/builder/InfoForViewToMask.java create mode 100644 coachmarks/src/main/java/com/myntra/coachmarks/common/AnimationType.java create mode 100644 coachmarks/src/main/java/com/myntra/coachmarks/common/CoachMarkAlignPosition.java create mode 100644 coachmarks/src/main/java/com/myntra/coachmarks/common/CoachMarkLayoutOrientation.java create mode 100644 coachmarks/src/main/java/com/myntra/coachmarks/common/CoachMarkTextGravity.java create mode 100644 coachmarks/src/main/java/com/myntra/coachmarks/common/DialogDismissButtonPosition.java create mode 100644 coachmarks/src/main/java/com/myntra/coachmarks/common/PopUpPosition.java create mode 100644 coachmarks/src/main/java/com/myntra/coachmarks/providers/DefaultDimensionResourceProvider.java create mode 100644 coachmarks/src/main/java/com/myntra/coachmarks/providers/DefaultScreenInfoProvider.java create mode 100644 coachmarks/src/main/java/com/myntra/coachmarks/providers/DefaultStringResourceProvider.java create mode 100644 coachmarks/src/main/java/com/myntra/coachmarks/providers/DefaultTypeFaceProvider.java create mode 100644 coachmarks/src/main/java/com/myntra/coachmarks/providers/interfaces/IDimensionResourceProvider.java create mode 100644 coachmarks/src/main/java/com/myntra/coachmarks/providers/interfaces/IScreenInfoProvider.java create mode 100644 coachmarks/src/main/java/com/myntra/coachmarks/providers/interfaces/IStringResourceProvider.java create mode 100644 coachmarks/src/main/java/com/myntra/coachmarks/providers/interfaces/ITypeFaceProvider.java create mode 100644 coachmarks/src/main/java/com/myntra/coachmarks/ui/common/BaseViews.java create mode 100644 coachmarks/src/main/java/com/myntra/coachmarks/ui/presentation/IPopUpCoachMarkPresentation.java create mode 100644 coachmarks/src/main/java/com/myntra/coachmarks/ui/presenter/PopUpCoachMarkPresenter.java create mode 100644 coachmarks/src/main/java/com/myntra/coachmarks/ui/utils/TransitionUtils.java create mode 100644 coachmarks/src/main/res/drawable/background_shadow_drawable.xml create mode 100644 coachmarks/src/main/res/drawable/coach_mark_vertical_dashed_line_separator.xml create mode 100644 coachmarks/src/main/res/drawable/coachmark_drawable_no_image.xml create mode 100644 coachmarks/src/main/res/drawable/coachmark_pop_up_drawable.xml create mode 100644 coachmarks/src/main/res/drawable/triangle_up.xml create mode 100644 coachmarks/src/main/res/layout/pop_up_coach_mark.xml create mode 100644 coachmarks/src/main/res/values-v21/styles.xml create mode 100644 coachmarks/src/main/res/values/attrs.xml create mode 100644 coachmarks/src/main/res/values/color.xml create mode 100644 coachmarks/src/main/res/values/dimens.xml create mode 100644 coachmarks/src/main/res/values/integers.xml create mode 100644 coachmarks/src/main/res/values/strings.xml create mode 100644 coachmarks/src/main/res/values/styles.xml create mode 100644 coachmarks/src/test/java/com/myntra/coachmarks/CoachMarkUnitTest.java create mode 100644 coachmarks/src/test/java/com/myntra/coachmarks/ui/presenter/PopUpCoachMarkPresenterTest.java create mode 100644 config/checkstyle/checkstyle.xml create mode 100755 config/checkstyle/suppressions.xml create mode 100755 config/findbugs/findbugs.xml create mode 100755 config/lint/lint.xml create mode 100755 config/pmd/pmd.xml create mode 100644 contributors.txt create mode 100644 dependencies.gradle create mode 100644 gradle.properties create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100755 gradlew create mode 100644 gradlew.bat create mode 100644 sample/.gitignore create mode 100644 sample/build.gradle create mode 100644 sample/proguard-rules.pro create mode 100644 sample/src/androidTest/java/com/myntra/android/sample/ApplicationTest.java create mode 100644 sample/src/main/AndroidManifest.xml create mode 100644 sample/src/main/assets/Whitney-Book-Bas.otf create mode 100644 sample/src/main/assets/Whitney-Semibold-Bas.otf create mode 100644 sample/src/main/java/com/myntra/sample/CoachMarksDemoActivity.java create mode 100644 sample/src/main/res/drawable/similar_item_drawable.xml create mode 100644 sample/src/main/res/layout/activity_coachmarks_demo.xml create mode 100644 sample/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100755 sample/src/main/res/mipmap-hdpi/ic_wishlist.png create mode 100644 sample/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100755 sample/src/main/res/mipmap-mdpi/ic_wishlist.png create mode 100644 sample/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100755 sample/src/main/res/mipmap-xhdpi/ic_wishlist.png create mode 100644 sample/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 sample/src/main/res/values-w820dp/dimens.xml create mode 100644 sample/src/main/res/values/colors.xml create mode 100644 sample/src/main/res/values/dimens.xml create mode 100644 sample/src/main/res/values/strings.xml create mode 100644 sample/src/main/res/values/styles.xml create mode 100644 sample/src/test/java/com/myntra/android/sample/CoachMarkUnitTest.java create mode 100644 settings.gradle diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..905ab1c --- /dev/null +++ b/.gitignore @@ -0,0 +1,14 @@ +*.iml +.gradle +/local.properties +/.idea/workspace.xml +/.idea/libraries +/.idea/vcs.xml +/.idea/misc.xml +.DS_Store +/build +/captures +.idea/vcs.xml +app/libs/ +/.idea + diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..f770fc9 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,60 @@ +# +# Build configuration for TRAVIS CI +# + +# Disabling sudo moves build to the Container Based Infrastructure on Travis CI +sudo: false + +language: android +jdk: oraclejdk8 + +env: + global: + - MALLOC_ARENA_MAX=2 + - ADB_INSTALL_TIMEOUT=10 + - ANDROID_API_LEVEL=25 + - ANDROID_BUILD_TOOLS_VERSION=25.0.2 + - ANDROID_ABI=armeabi-v7a + - ANDROID_TAG=google_apis + +android: + components: + - platform-tools + - tools + - tools + - android-$ANDROID_API_LEVEL + - build-tools-$ANDROID_BUILD_TOOLS_VERSION + # For Google Maps API v1 + - addon-google_apis-google-$ANDROID_API_LEVEL + # Google Play Services + - extra-google-google_play_services + # Support library + - extra-android-support + # Latest artifacts in local repository + - extra-google-m2repository + - extra-android-m2repository + # Specify at least one system image + - sys-img-armeabi-v7a-android-$ANDROID_API_LEVEL + - sys-img-armeabi-v7a-addon-google_apis-google-$ANDROID_API_LEVEL + + licenses: + - 'android-sdk-preview-license-.+' + - 'android-sdk-license-.+' + - 'google-gdk-license-.+' + - '.+' + +before_install: + - pip install --user codecov + +script: + - sh ci.sh + +after_success: + - codecov + +notifications: + email: true + slack: myntra:3GgFvhHOLMEe3WIVzuoFUW8Y + +cache: false +sudo: required \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..8dada3e --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md new file mode 100644 index 0000000..df329a8 --- /dev/null +++ b/README.md @@ -0,0 +1,24 @@ +# CoachMarks (Work In Progress) + +[![Platform](https://img.shields.io/badge/platform-android-green.svg)](http://developer.android.com/index.html) +![SDK](https://img.shields.io/badge/SDK-16%2B-green.svg) +![Release](https://img.shields.io/badge/release-0.1-green.svg) +[![Build Status](https://travis-ci.org/myntra/CoachMarks.svg?branch=master)](https://travis-ci.org/myntra/CoachMarks) +[![CircleCI](https://circleci.com/gh/myntra/CoachMarks.svg?style=svg)](https://circleci.com/gh/myntra/CoachMarks) + +License +------- + + Copyright Manjunath Chandrashekar & Mudit Pant 2017 + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..b71d312 --- /dev/null +++ b/build.gradle @@ -0,0 +1,149 @@ +import com.android.build.gradle.internal.tasks.AndroidTestTask + +// Top-level build file where you can add configuration options common to all sub-projects/modules. +apply from: 'dependencies.gradle' + +buildscript { + // Gradle will not find vars defined in an external file when referring to them + // in the buildscript block, unless you link it from the buildscript block, too. + apply from: 'dependencies.gradle' + + repositories { + jcenter() + + maven { url "https://jitpack.io" } + } + + dependencies { + classpath gradlePlugins.aptPlugin + classpath gradlePlugins.androidToolsPlugin + classpath gradlePlugins.butterKnifePlugin + // NOTE: Do not place your application dependencies here; they belong + // in the individual module build.gradle files + } +} + +allprojects { + repositories { + jcenter() + + maven { url "https://jitpack.io" } + } + + // Workaround to prevent Gradle from stealing focus from other apps during tests run/etc. + // https://gist.github.com/artem-zinnatullin/4c250e04636e25797165 + tasks.withType(JavaForkOptions) { + jvmArgs '-Djava.awt.headless=true' + } +} + +task clean(type: Delete) { + delete rootProject.buildDir +} + +ext.preDexLibs = !project.hasProperty('disablePreDex') + +subprojects { + + project.plugins.whenPluginAdded { plugin -> + if ('com.android.build.gradle.AppPlugin'.equals(plugin.class.name) || + 'com.android.build.gradle.LibraryPlugin'.equals(plugin.class.name)) { + // enable or disable pre-dexing + project.android.dexOptions.preDexLibraries = rootProject.ext.preDexLibs + } + } + + plugins.apply('pmd') + pmd { + toolVersion = '5.4.1' + } + task pmd(type: Pmd) { + ignoreFailures = true + ruleSetFiles = project.files(rootProject.file("config/pmd/pmd.xml")) + ruleSets = [] + + source = fileTree('src/main/java') + source 'src' + include '**/*.java' + exclude '**/gen/**' + + reports { + xml.enabled = false + html.enabled = true + xml { + destination "$project.buildDir/reports/pmd/pmd.xml" + } + html { + destination "$project.buildDir/reports/pmd/pmd.html" + } + } + } + + plugins.apply('findbugs') + task findbugs(type: FindBugs) { + ignoreFailures = true + effort = "max" + reportLevel = "high" + excludeFilter = rootProject.file('config/findbugs/findbugs.xml') + classes = files("${project.rootDir}/app/build/intermediates/classes") + + source = fileTree('src/main/java') + source 'src' + include '**/*.java' + exclude '**/gen/**' + + reports { + xml.enabled = false + html.enabled = true + + xml { + destination "$project.buildDir/reports/findbugs/findbugs.xml" + withMessages = true + } + html { + destination "$project.buildDir/reports/findbugs/findbugs.html" + } + } + classpath = files() + } + + plugins.apply('checkstyle') + checkstyle { + toolVersion = '6.7' + } + task checkstyle(type: Checkstyle) { + configFile rootProject.file('config/checkstyle/checkstyle.xml') + configProperties.checkstyleSuppressionsPath = file("${project.rootDir}/code_quality/checkstyle/suppressions.xml").absolutePath + + ignoreFailures true + showViolations true + + source 'src' + include '**/*.java' + exclude '**/gen/**' + classpath = files() + } + + afterEvaluate { + tasks.findByName('pmd').dependsOn('assemble') + tasks.findByName('findbugs').dependsOn('assemble') + + def checkTask = tasks.findByName('check') + + checkTask.dependsOn('pmd') + //checkTask.dependsOn('findbugs') + checkTask.dependsOn('checkstyle') + checkTask.dependsOn('lint') + + // Log instrumentation tests results. + tasks.withType(AndroidTestTask) { task -> + task.doFirst { + logging.level = LogLevel.INFO + } + task.doLast { + logging.level = LogLevel.LIFECYCLE + } + } + } +} + diff --git a/ci.sh b/ci.sh new file mode 100644 index 0000000..a64222b --- /dev/null +++ b/ci.sh @@ -0,0 +1,3 @@ +#!/bin/bash +# Please run it from root project directory +./gradlew clean build -PdisablePreDex -PwithDexcount \ No newline at end of file diff --git a/circle.yml b/circle.yml new file mode 100644 index 0000000..9daf338 --- /dev/null +++ b/circle.yml @@ -0,0 +1,31 @@ +# +# Build configuration for Circle CI +# +general: + artifacts: + - /home/ubuntu/com.myntra.coachmarks/app/build/outputs/apk/ + +machine: + environment: + ANDROID_HOME: /usr/local/android-sdk-linux + GRADLE_OPTS: '-Dorg.gradle.jvmargs="-Xmx2048m -XX:+HeapDumpOnOutOfMemoryError"' + java: + version: openjdk8 + +dependencies: + pre: + - echo y | android update sdk --no-ui --all --filter "tools,android-25,build-tools-25.0.2,platform-tools,extra-android-m2repository,extra-google-m2repository,extra-google-google_play_services" + +#-PdisablePreDex is a must else gradle just dies due to memory limit +test: + override: + - (./gradlew assemble -PdisablePreDex): + timeout: 1000 + +#Deploy when tests pass +deployment: + master: + branch: master + commands: + - (./gradlew clean build -PdisablePreDex -PwithDexcount --info): + timeout: 720 \ No newline at end of file diff --git a/coachmarks/.gitignore b/coachmarks/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/coachmarks/.gitignore @@ -0,0 +1 @@ +/build diff --git a/coachmarks/build.gradle b/coachmarks/build.gradle new file mode 100644 index 0000000..8d520e7 --- /dev/null +++ b/coachmarks/build.gradle @@ -0,0 +1,63 @@ +apply plugin: 'com.android.library' +apply plugin: 'com.jakewharton.butterknife' + +android { + compileSdkVersion versions.compileSdk + buildToolsVersion versions.buildTools + + defaultConfig { + minSdkVersion versions.minSdk + targetSdkVersion versions.targetSdk + versionCode 1 + versionName "0.0.1" + vectorDrawables.useSupportLibrary = true + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + + lintOptions { + warningsAsErrors false + abortOnError false + + xmlReport false + htmlReport true + lintConfig file("${project.rootDir}/../code_quality/lint/lint.xml") + htmlOutput file("$project.buildDir/reports/lint/lint-result.html") + xmlOutput file("$project.buildDir/reports/lint/lint-result.xml") + } + +} + +dependencies { + compile fileTree(dir: 'libs', include: ['*.jar']) + + //SUPPORT LIB + compile libraries.supportAppCompat + + //Utils + compile libraries.zetaUtils + + //PROGRAMMING + provided libraries.autoValue + annotationProcessor libraries.autoValueParcel + compile libraries.butterKnife + annotationProcessor libraries.butterKnifeCompiler + + //ANNOTATION + compile libraries.jsr305 + compile libraries.javaxAnnotationApi + + compile(libraries.bundler) { + // exclude this because bundler refers to an old version of the support lib + exclude group: 'com.google.android' + } + + //TESTING + testCompile libraries.junit + +} diff --git a/coachmarks/proguard-rules.pro b/coachmarks/proguard-rules.pro new file mode 100644 index 0000000..353bea2 --- /dev/null +++ b/coachmarks/proguard-rules.pro @@ -0,0 +1,17 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in /Users/z087205/Library/Android/sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} diff --git a/coachmarks/src/androidTest/java/com/myntra/coachmarks/ApplicationTest.java b/coachmarks/src/androidTest/java/com/myntra/coachmarks/ApplicationTest.java new file mode 100644 index 0000000..3e27c5b --- /dev/null +++ b/coachmarks/src/androidTest/java/com/myntra/coachmarks/ApplicationTest.java @@ -0,0 +1,13 @@ +package com.myntra.coachmarks; + +import android.app.Application; +import android.test.ApplicationTestCase; + +/** + * Testing Fundamentals + */ +public class ApplicationTest extends ApplicationTestCase { + public ApplicationTest() { + super(Application.class); + } +} \ No newline at end of file diff --git a/coachmarks/src/main/AndroidManifest.xml b/coachmarks/src/main/AndroidManifest.xml new file mode 100644 index 0000000..d897f98 --- /dev/null +++ b/coachmarks/src/main/AndroidManifest.xml @@ -0,0 +1,3 @@ + + + diff --git a/coachmarks/src/main/java/com/myntra/coachmarks/PopUpCoachMark.java b/coachmarks/src/main/java/com/myntra/coachmarks/PopUpCoachMark.java new file mode 100644 index 0000000..077dce2 --- /dev/null +++ b/coachmarks/src/main/java/com/myntra/coachmarks/PopUpCoachMark.java @@ -0,0 +1,357 @@ +package com.myntra.coachmarks; + + +import android.app.Dialog; +import android.content.Context; +import android.graphics.Rect; +import android.graphics.Typeface; +import android.graphics.drawable.ColorDrawable; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.app.DialogFragment; +import android.support.v4.content.ContextCompat; +import android.support.v7.widget.AppCompatTextView; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.Window; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.RelativeLayout; +import android.widget.Toast; + +import com.f2prateek.bundler.FragmentBundlerCompat; +import com.myntra.coachmarks.builder.CoachMarkBuilder; +import com.myntra.coachmarks.common.CoachMarkAlignPosition; +import com.myntra.coachmarks.common.CoachMarkLayoutOrientation; +import com.myntra.coachmarks.common.CoachMarkTextGravity; +import com.myntra.coachmarks.providers.DefaultDimensionResourceProvider; +import com.myntra.coachmarks.providers.DefaultScreenInfoProvider; +import com.myntra.coachmarks.providers.DefaultStringResourceProvider; +import com.myntra.coachmarks.providers.DefaultTypeFaceProvider; +import com.myntra.coachmarks.providers.interfaces.IDimensionResourceProvider; +import com.myntra.coachmarks.providers.interfaces.IScreenInfoProvider; +import com.myntra.coachmarks.providers.interfaces.IStringResourceProvider; +import com.myntra.coachmarks.providers.interfaces.ITypeFaceProvider; +import com.myntra.coachmarks.ui.common.BaseViews; +import com.myntra.coachmarks.ui.presentation.IPopUpCoachMarkPresentation; +import com.myntra.coachmarks.ui.presenter.PopUpCoachMarkPresenter; +import com.myntra.coachmarks.ui.utils.TransitionUtils; + +import butterknife.BindView; +import zeta.android.utils.view.ViewUtils; + +public class PopUpCoachMark extends DialogFragment implements IPopUpCoachMarkPresentation, View.OnClickListener { + private static final String TAG = PopUpCoachMark.class.getSimpleName(); + + private static final int NO_MARGIN = 0; + private static final String ARG_POP_UP_COACH_MARK_BUILDER_PARCEL = "ARG_POP_UP_COACH_MARK_BUILDER_PARCEL"; + + private Views mViews; + private PopUpCoachMarkPresenter mPopUpCoachMarkPresenter; + + static class Views extends BaseViews { + + @BindView(R2.id.rl_coachmark_parent) + RelativeLayout rlCoachMarkParent; + + @BindView(R2.id.tv_coachmark_text) + AppCompatTextView tvPopUpCoachMarkText; + + @BindView(R2.id.tv_ok_button) + AppCompatTextView tvPopUpDismissButton; + + @BindView(R2.id.v_separator) + ImageView vSeparator; + + @BindView(R2.id.iv_coachmark_image) + ImageView ivCoachMarkImage; + + @BindView(R2.id.rl_shim_out_view_parent) + FrameLayout rlShimOutViewParent; + + @BindView(R2.id.rl_coachmark_text_part) + RelativeLayout rlCoachMarkTextPart; + + @BindView(R2.id.ll_coach_mark_pop_up_parent) + LinearLayout llPopUpCoachMarkParent; + + @BindView(R2.id.right_bottom_notch) + View vRightBottomNotch; + + @BindView(R2.id.left_top_notch) + View vLeftTopNotch; + + @BindView(R2.id.v_notch_base_white_left) + View vNotchBaseWhiteLeft; + + @BindView(R2.id.v_notch_base_white_top) + View vNotchBaseWhiteTop; + + @BindView(R2.id.test) + LinearLayout test; + + Views(View root) { + super(root); + } + } + + public static PopUpCoachMark newInstance(CoachMarkBuilder builder) { + return FragmentBundlerCompat.create(new PopUpCoachMark()) + .put(ARG_POP_UP_COACH_MARK_BUILDER_PARCEL, builder) + .build(); + } + + @NonNull + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + Dialog dialog = super.onCreateDialog(savedInstanceState); + Window window = dialog.getWindow(); + if (window == null) { + Log.wtf(TAG, "getWindow() should not be null ever"); + return dialog; + } + window.getAttributes().windowAnimations = R.style.coach_mark_dialog_animation; + window.setBackgroundDrawable(new ColorDrawable(ContextCompat.getColor(getContext(), + R.color.coach_mark_transparent_color))); + dialog.requestWindowFeature(Window.FEATURE_NO_TITLE); + return dialog; + } + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setStyle(STYLE_NO_FRAME, R.style.AppTheme); + + final Context context = getContext(); + final IStringResourceProvider stringResourceProvider = new DefaultStringResourceProvider(context); + final IDimensionResourceProvider dimensionResourceProvider = new DefaultDimensionResourceProvider(context); + final ITypeFaceProvider typeFaceProvider = new DefaultTypeFaceProvider(context); + final IScreenInfoProvider screenInfoProvider = new DefaultScreenInfoProvider(context); + + mPopUpCoachMarkPresenter = new PopUpCoachMarkPresenter(stringResourceProvider, dimensionResourceProvider, + typeFaceProvider, screenInfoProvider); + //coach mark bundle is injected in onCreate as its available from bundle only + //If we decide to migrate to DI pattern this will be useful + mPopUpCoachMarkPresenter.onCreate(getCoachMarkBuilderFromBundle()); + } + + @Nullable + @Override + public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + super.onCreateView(inflater, container, savedInstanceState); + final View view = inflater.inflate(R.layout.pop_up_coach_mark, container, false); + mViews = new Views(view); + mPopUpCoachMarkPresenter.onCreateView(this); + registerClickListener(); + return view; + } + + @Override + public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + mPopUpCoachMarkPresenter.onViewCreated(); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + unRegisterClickListener(); + mViews = null; + } + + @Override + public void onDestroy() { + super.onDestroy(); + mPopUpCoachMarkPresenter.onDestroy(); + mPopUpCoachMarkPresenter = null; + } + + //region presentation methods + @Override + public void createViewToBeMaskedOut(int startX, int startY, int height, int width) { + View view = new View(getContext()); + FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(width, height); + layoutParams.setMargins(startX, startY, NO_MARGIN, NO_MARGIN); + view.setLayoutParams(layoutParams); + view.setBackgroundColor(ContextCompat.getColor(getContext(), R.color.coach_mark_black_translucent)); + mViews.rlShimOutViewParent.addView(view); + } + + @Override + public void dismissWithError(String message) { + Toast.makeText(getContext(), message, Toast.LENGTH_LONG).show(); + mViews.rlCoachMarkParent.setVisibility(View.INVISIBLE); + } + + @Override + public void onDismiss() { + dismiss(); + } + + @Override + public void setImageInformation(double centerX, double centerY, int imageWidth, int imageHeight, int backGroundTint, int imageDrawableRes) { + RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(imageWidth, imageHeight); + layoutParams.setMargins((int) (centerX - (imageWidth / 2)), (int) (centerY - (imageHeight / 2)), NO_MARGIN, NO_MARGIN); + mViews.ivCoachMarkImage.setLayoutParams(layoutParams); + mViews.ivCoachMarkImage.setBackgroundColor(ContextCompat.getColor(getContext(), R.color.coach_mark_transparent_color)); + mViews.ivCoachMarkImage.setImageDrawable(ContextCompat.getDrawable(getContext(), imageDrawableRes)); + mViews.ivCoachMarkImage.setColorFilter(ContextCompat.getColor(getContext(), backGroundTint)); + } + + @Override + public void setPopUpViewPositionWithRespectToImage(@CoachMarkAlignPosition int alignPosition) { + RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) mViews.llPopUpCoachMarkParent.getLayoutParams(); + layoutParams.addRule(alignPosition, mViews.ivCoachMarkImage.getId()); + mViews.llPopUpCoachMarkParent.setLayoutParams(layoutParams); + } + + @Override + public void setPopUpViewTopLeft(Rect margin, @CoachMarkLayoutOrientation int orientation) { + RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) mViews.llPopUpCoachMarkParent.getLayoutParams(); + layoutParams.setMargins(margin.left, margin.top, margin.right, margin.bottom); + mViews.llPopUpCoachMarkParent.setLayoutParams(layoutParams); + mViews.llPopUpCoachMarkParent.setOrientation(orientation); + mViews.vRightBottomNotch.setVisibility(View.VISIBLE); + mViews.vLeftTopNotch.setVisibility(View.GONE); + } + + @Override + public void setPopUpViewBottomRight(Rect margin, @CoachMarkLayoutOrientation int orientation) { + RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) mViews.llPopUpCoachMarkParent.getLayoutParams(); + layoutParams.setMargins(margin.left, margin.top, margin.right, margin.bottom); + mViews.llPopUpCoachMarkParent.setLayoutParams(layoutParams); + mViews.llPopUpCoachMarkParent.setOrientation(orientation); + mViews.vRightBottomNotch.setVisibility(View.GONE); + mViews.vLeftTopNotch.setVisibility(View.VISIBLE); + } + + @Override + public void setDismissButtonPositionLeft() { + RelativeLayout.LayoutParams okButtonLayoutParams = (RelativeLayout.LayoutParams) mViews.tvPopUpDismissButton.getLayoutParams(); + RelativeLayout.LayoutParams separatorLayoutParams = (RelativeLayout.LayoutParams) mViews.vSeparator.getLayoutParams(); + RelativeLayout.LayoutParams coachMarkTextParams = (RelativeLayout.LayoutParams) mViews.test.getLayoutParams(); + okButtonLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT, mViews.rlCoachMarkTextPart.getId()); + mViews.tvPopUpDismissButton.setLayoutParams(okButtonLayoutParams); + separatorLayoutParams.addRule(RelativeLayout.RIGHT_OF, mViews.tvPopUpDismissButton.getId()); + mViews.vSeparator.setLayoutParams(separatorLayoutParams); + coachMarkTextParams.addRule(RelativeLayout.RIGHT_OF, mViews.vSeparator.getId()); + mViews.test.setLayoutParams(coachMarkTextParams); + } + + @Override + public void setDismissButtonPositionRight() { + RelativeLayout.LayoutParams okButtonLayoutParams = (RelativeLayout.LayoutParams) mViews.tvPopUpDismissButton.getLayoutParams(); + RelativeLayout.LayoutParams separatorLayoutParams = (RelativeLayout.LayoutParams) mViews.vSeparator.getLayoutParams(); + RelativeLayout.LayoutParams coachMarkTextParams = (RelativeLayout.LayoutParams) mViews.test.getLayoutParams(); + coachMarkTextParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT, mViews.rlCoachMarkTextPart.getId()); + mViews.test.setLayoutParams(coachMarkTextParams); + separatorLayoutParams.addRule(RelativeLayout.RIGHT_OF, mViews.test.getId()); + mViews.vSeparator.setLayoutParams(separatorLayoutParams); + okButtonLayoutParams.addRule(RelativeLayout.RIGHT_OF, mViews.vSeparator.getId()); + mViews.tvPopUpDismissButton.setLayoutParams(okButtonLayoutParams); + } + + @Override + public void startScaleAnimationOnImage() { + mViews.ivCoachMarkImage.startAnimation(TransitionUtils.createScaleAnimation()); + } + + + @Override + public void startThrobAnimationOnImage() { + mViews.ivCoachMarkImage.startAnimation(TransitionUtils.createThrobAnimation()); + } + + @Override + public void startAlphaAnimationOnImage() { + mViews.ivCoachMarkImage.startAnimation(TransitionUtils.createAlphaAnimation()); + } + + @Override + public void setNotchPositionIfPopUpTopLeft(Rect margin, float rotation) { + mViews.vRightBottomNotch.setRotation(rotation); + LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) mViews.vRightBottomNotch.getLayoutParams(); + layoutParams.setMargins(margin.left, margin.top, margin.right, margin.bottom); + mViews.vRightBottomNotch.setLayoutParams(layoutParams); + } + + @Override + public void setNotchPositionIfPopUpBottomRight(Rect margin, float rotation) { + mViews.vLeftTopNotch.setRotation(rotation); + LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) mViews.vLeftTopNotch.getLayoutParams(); + layoutParams.setMargins(margin.left, margin.top, margin.right, margin.bottom); + mViews.vLeftTopNotch.setLayoutParams(layoutParams); + } + + @Override + public void setTypeFaceForDismissButton(Typeface typeface) { + mViews.tvPopUpDismissButton.setTypeface(typeface); + } + + @Override + public void setTypeFaceForPopUpText(Typeface typeface) { + mViews.tvPopUpCoachMarkText.setTypeface(typeface); + } + + @Override + public void setUpGravityForCoachMarkText(@CoachMarkTextGravity int gravity) { + mViews.tvPopUpCoachMarkText.setGravity(gravity); + } + + @Override + public void uiAdjustmentForNotchIfPopUpRight(Rect margin) { + ViewUtils.setToVisible(mViews.vNotchBaseWhiteLeft); + RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) mViews.vNotchBaseWhiteLeft.getLayoutParams(); + layoutParams.setMargins(margin.left, margin.top, margin.right, margin.bottom); + mViews.vNotchBaseWhiteLeft.setLayoutParams(layoutParams); + } + + @Override + public void uiAdjustmentForNotchIfPopUpBottom(Rect margin) { + ViewUtils.setToVisible(mViews.vNotchBaseWhiteTop); + RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) mViews.vNotchBaseWhiteTop.getLayoutParams(); + layoutParams.setMargins(margin.left, margin.top, margin.right, margin.bottom); + mViews.vNotchBaseWhiteTop.setLayoutParams(layoutParams); + } + + @Override + public void setCoachMarkMessage(String message) { + mViews.tvPopUpCoachMarkText.setText(message); + } + + //endregion + + //region click listeners + @Override + public void onClick(View v) { + if (v.getId() == R.id.tv_ok_button) { + if (mPopUpCoachMarkPresenter != null) { + mPopUpCoachMarkPresenter.onOkButtonClicked(); + } + } else if (v.getId() == R.id.rl_shim_out_view_parent) { + mPopUpCoachMarkPresenter.onShimClicked(); + } + } + //endregion + + //region helper methods + private CoachMarkBuilder getCoachMarkBuilderFromBundle() { + return getArguments().getParcelable(ARG_POP_UP_COACH_MARK_BUILDER_PARCEL); + } + + private void registerClickListener() { + mViews.tvPopUpDismissButton.setOnClickListener(this); + mViews.rlShimOutViewParent.setOnClickListener(this); + } + + private void unRegisterClickListener() { + mViews.tvPopUpDismissButton.setOnClickListener(null); + mViews.rlShimOutViewParent.setOnClickListener(null); + } + //endregion + +} diff --git a/coachmarks/src/main/java/com/myntra/coachmarks/builder/CoachMarkBuilder.java b/coachmarks/src/main/java/com/myntra/coachmarks/builder/CoachMarkBuilder.java new file mode 100644 index 0000000..fb21551 --- /dev/null +++ b/coachmarks/src/main/java/com/myntra/coachmarks/builder/CoachMarkBuilder.java @@ -0,0 +1,137 @@ +package com.myntra.coachmarks.builder; + +import android.graphics.Point; +import android.os.Parcelable; +import android.support.annotation.ColorRes; +import android.support.annotation.DimenRes; +import android.support.annotation.DrawableRes; +import android.support.annotation.Nullable; +import android.support.annotation.StringRes; + +import com.google.auto.value.AutoValue; +import com.myntra.coachmarks.R; +import com.myntra.coachmarks.common.AnimationType; +import com.myntra.coachmarks.common.CoachMarkTextGravity; +import com.myntra.coachmarks.common.DialogDismissButtonPosition; +import com.myntra.coachmarks.common.PopUpPosition; + +import java.util.ArrayList; + +@AutoValue +public abstract class CoachMarkBuilder implements Parcelable { + + public static Builder create(Point coachMarkViewAnchorTop, + Point coachMarkViewAnchorBottom, + @StringRes int coachMarkMessage) { + double DEFAULT_NOTCH_POSITION = 0.0; + return new AutoValue_CoachMarkBuilder.Builder() + .setAnchorTop(coachMarkViewAnchorTop) + .setAnchorBottom(coachMarkViewAnchorBottom) + .setCoachMarkMessage(coachMarkMessage) + .setCoachMarkTextGravity(CoachMarkTextGravity.LEFT) + .setActionBarHeight(R.dimen.coach_mark_zero_dp) + .setFooterHeight(R.dimen.coach_mark_zero_dp) + .setInfoForViewToMaskList(new ArrayList(0)) + .setAnimationTypeOnImage(AnimationType.THROB_ANIMATION) + .setUserDesiredPopUpPositionWithRespectToView(PopUpPosition.RIGHT) + .setBackGroundTintForImage(R.color.coach_mark_transparent_color) + .setImageDrawableRes(R.drawable.coachmark_drawable_no_image) + .setNotchPosition(DEFAULT_NOTCH_POSITION) + .setCoachMarkLayoutMargin(CoachMarkLayoutMargin.create().build()) + .setImageLayoutInformation(ImageLayoutInformation.create(R.dimen.coach_mark_zero_dp, R.dimen.coach_mark_zero_dp).build()) + .setFontStyleForDismissButton(null) + .setFontStyleForCoachMarkText(null); + } + + @SuppressWarnings("unused") + public Builder newBuilder() { + return new AutoValue_CoachMarkBuilder.Builder(this); + } + + public abstract Point getAnchorTop(); + + public abstract Point getAnchorBottom(); + + @StringRes + public abstract int getCoachMarkMessage(); + + @DimenRes + public abstract int getActionBarHeight(); + + @DimenRes + public abstract int getFooterHeight(); + + @Nullable + public abstract ArrayList getInfoForViewToMaskList(); + + @AnimationType + public abstract int getAnimationTypeOnImage(); + + @CoachMarkTextGravity + public abstract int getCoachMarkTextGravity(); + + @Nullable + public abstract String getFontStyleForDismissButton(); + + @Nullable + public abstract String getFontStyleForCoachMarkText(); + + @DialogDismissButtonPosition + public abstract int getPopUpCoachMarkDismissButtonPosition(); + + public abstract double getNotchPosition(); + + @PopUpPosition + public abstract int getUserDesiredPopUpPositionWithRespectToView(); + + @ColorRes + public abstract int getBackGroundTintForImage(); + + @DrawableRes + public abstract int getImageDrawableRes(); + + public abstract CoachMarkLayoutMargin getCoachMarkLayoutMargin(); + + public abstract ImageLayoutInformation getImageLayoutInformation(); + + @AutoValue.Builder + public static abstract class Builder { + + public abstract Builder setAnchorTop(Point anchorTop); + + public abstract Builder setAnchorBottom(Point anchorBottom); + + public abstract Builder setCoachMarkMessage(@StringRes int coachMarkMessage); + + public abstract Builder setCoachMarkTextGravity(@CoachMarkTextGravity int coachMarkTextGravity); + + public abstract Builder setFontStyleForDismissButton(@Nullable String fontStyleForDismissButton); + + public abstract Builder setFontStyleForCoachMarkText(@Nullable String fontStyleForCoachMarkText); + + public abstract Builder setActionBarHeight(@DimenRes int actionBarHeight); + + public abstract Builder setFooterHeight(@DimenRes int footerHeight); + + public abstract Builder setInfoForViewToMaskList(@Nullable ArrayList infoForViewToMaskList); + + public abstract Builder setAnimationTypeOnImage(@AnimationType int animationTypeOnImage); + + public abstract Builder setPopUpCoachMarkDismissButtonPosition(@DialogDismissButtonPosition int popUpCoachMarkDismissButtonPosition); + + public abstract Builder setNotchPosition(double notchPosition); + + public abstract Builder setUserDesiredPopUpPositionWithRespectToView(@PopUpPosition int userDesiredPopUpPositionWithRespectToView); + + public abstract Builder setBackGroundTintForImage(@ColorRes int backGroundTintForImage); + + public abstract Builder setImageDrawableRes(@DrawableRes int imageDrawableRes); + + public abstract Builder setCoachMarkLayoutMargin(CoachMarkLayoutMargin coachMarkLayoutMargin); + + public abstract Builder setImageLayoutInformation(ImageLayoutInformation imageLayoutInformation); + + public abstract CoachMarkBuilder build(); + + } +} diff --git a/coachmarks/src/main/java/com/myntra/coachmarks/builder/CoachMarkLayoutMargin.java b/coachmarks/src/main/java/com/myntra/coachmarks/builder/CoachMarkLayoutMargin.java new file mode 100644 index 0000000..9ffb23d --- /dev/null +++ b/coachmarks/src/main/java/com/myntra/coachmarks/builder/CoachMarkLayoutMargin.java @@ -0,0 +1,46 @@ +package com.myntra.coachmarks.builder; + +import android.os.Parcelable; +import android.support.annotation.DimenRes; + +import com.google.auto.value.AutoValue; +import com.myntra.coachmarks.R; + +@AutoValue +public abstract class CoachMarkLayoutMargin implements Parcelable { + + public static CoachMarkLayoutMargin.Builder create() { + return new AutoValue_CoachMarkLayoutMargin.Builder() + .setLeftMarginForCoachMark(R.dimen.coach_mark_zero_dp) + .setRightMarginForCoachMark(R.dimen.coach_mark_zero_dp) + .setTopMarginForCoachMark(R.dimen.coach_mark_zero_dp) + .setBottomMarginForCoachMark(R.dimen.coach_mark_zero_dp); + } + + @DimenRes + public abstract int getLeftMarginForCoachMark(); + + @DimenRes + public abstract int getRightMarginForCoachMark(); + + @DimenRes + public abstract int getTopMarginForCoachMark(); + + @DimenRes + public abstract int getBottomMarginForCoachMark(); + + @AutoValue.Builder + public static abstract class Builder { + + public abstract Builder setLeftMarginForCoachMark(@DimenRes int leftMarginForView); + + public abstract Builder setRightMarginForCoachMark(@DimenRes int rightMarginForView); + + public abstract Builder setTopMarginForCoachMark(@DimenRes int topMarginForView); + + public abstract Builder setBottomMarginForCoachMark(@DimenRes int bottomMarginForView); + + public abstract CoachMarkLayoutMargin build(); + + } +} diff --git a/coachmarks/src/main/java/com/myntra/coachmarks/builder/CoachMarkPixelInfo.java b/coachmarks/src/main/java/com/myntra/coachmarks/builder/CoachMarkPixelInfo.java new file mode 100644 index 0000000..136920a --- /dev/null +++ b/coachmarks/src/main/java/com/myntra/coachmarks/builder/CoachMarkPixelInfo.java @@ -0,0 +1,92 @@ +package com.myntra.coachmarks.builder; + +import android.graphics.Rect; +import android.os.Parcelable; + +import com.google.auto.value.AutoValue; + +@AutoValue +public abstract class CoachMarkPixelInfo implements Parcelable { + + public static CoachMarkPixelInfo.Builder create() { + return new AutoValue_CoachMarkPixelInfo.Builder() + .setImageWidthInPixels(0) + .setImageHeightInPixels(0) + .setMarginRectInPixels(new Rect(0, 0, 0, 0)) + .setPopUpWidthInPixelsWithOffset(0) + .setPopUpHeightInPixelsWithOffset(0) + .setPopUpWidthInPixels(0) + .setPopUpHeightInPixels(0) + .setScreenWidthInPixels(0) + .setScreenHeightInPixels(0) + .setNotchDimenInPixels(0) + .setActionBarHeightPixels(0) + .setFooterHeightPixels(0) + .setMarginOffsetForNotchInPixels(0) + .setWidthHeightOffsetForCoachMarkPopUp(0); + } + + public abstract int getImageWidthInPixels(); + + public abstract int getImageHeightInPixels(); + + public abstract Rect getMarginRectInPixels(); + + public abstract int getPopUpWidthInPixelsWithOffset(); + + public abstract int getPopUpHeightInPixelsWithOffset(); + + public abstract int getPopUpWidthInPixels(); + + public abstract int getPopUpHeightInPixels(); + + public abstract int getScreenWidthInPixels(); + + public abstract int getScreenHeightInPixels(); + + public abstract int getNotchDimenInPixels(); + + public abstract int getActionBarHeightPixels(); + + public abstract int getFooterHeightPixels(); + + public abstract int getMarginOffsetForNotchInPixels(); + + public abstract int getWidthHeightOffsetForCoachMarkPopUp(); + + + @AutoValue.Builder + public static abstract class Builder { + + public abstract Builder setImageWidthInPixels(int imageWidthInPixels); + + public abstract Builder setImageHeightInPixels(int imageHeightInPixels); + + public abstract Builder setMarginRectInPixels(Rect coachMarkMarginRectInPixels); + + public abstract Builder setPopUpWidthInPixelsWithOffset(int coachMarkPopUpWidthInPixelsWithOffset); + + public abstract Builder setPopUpHeightInPixelsWithOffset(int coachMarkPopUpHeightInPixelsWithOffset); + + public abstract Builder setPopUpWidthInPixels(int coachMarkPopUpWidthInPixels); + + public abstract Builder setPopUpHeightInPixels(int coachMarkPopUpHeightInPixels); + + public abstract Builder setScreenWidthInPixels(int screenWidthInPixels); + + public abstract Builder setScreenHeightInPixels(int screenHeightInPixels); + + public abstract Builder setNotchDimenInPixels(int notchDimenInPixels); + + public abstract Builder setActionBarHeightPixels(int actionBarHeightPixels); + + public abstract Builder setFooterHeightPixels(int footerHeightPixels); + + public abstract Builder setMarginOffsetForNotchInPixels(int marginOffsetForNotchInPixels); + + public abstract Builder setWidthHeightOffsetForCoachMarkPopUp(int widthHeightOffsetForCoachMarkPopUp); + + public abstract CoachMarkPixelInfo build(); + + } +} diff --git a/coachmarks/src/main/java/com/myntra/coachmarks/builder/ImageLayoutInformation.java b/coachmarks/src/main/java/com/myntra/coachmarks/builder/ImageLayoutInformation.java new file mode 100644 index 0000000..b37fd94 --- /dev/null +++ b/coachmarks/src/main/java/com/myntra/coachmarks/builder/ImageLayoutInformation.java @@ -0,0 +1,34 @@ +package com.myntra.coachmarks.builder; + +import android.os.Parcelable; +import android.support.annotation.DimenRes; + +import com.google.auto.value.AutoValue; + +@AutoValue +public abstract class ImageLayoutInformation implements Parcelable { + + public static Builder create(@DimenRes int imageWidth, @DimenRes int imageHeight) { + return new AutoValue_ImageLayoutInformation.Builder() + .setImageWidth(imageWidth) + .setImageHeight(imageHeight); + + } + + @DimenRes + public abstract int getImageHeight(); + + @DimenRes + public abstract int getImageWidth(); + + @AutoValue.Builder + public static abstract class Builder { + + public abstract Builder setImageHeight(@DimenRes int imageHeight); + + public abstract Builder setImageWidth(@DimenRes int imageWidth); + + public abstract ImageLayoutInformation build(); + + } +} diff --git a/coachmarks/src/main/java/com/myntra/coachmarks/builder/InfoForViewToMask.java b/coachmarks/src/main/java/com/myntra/coachmarks/builder/InfoForViewToMask.java new file mode 100644 index 0000000..b871f97 --- /dev/null +++ b/coachmarks/src/main/java/com/myntra/coachmarks/builder/InfoForViewToMask.java @@ -0,0 +1,37 @@ +package com.myntra.coachmarks.builder; + +import android.graphics.Point; +import android.os.Parcelable; + +import com.google.auto.value.AutoValue; + +@AutoValue +public abstract class InfoForViewToMask implements Parcelable { + + public static Builder create(Point viewToMaskStartPosition, int viewToMaskHeight, int viewToMaskWidth) { + return new AutoValue_InfoForViewToMask.Builder() + .setViewToMaskStartPosition(viewToMaskStartPosition) + .setViewToMaskHeight(viewToMaskHeight) + .setViewToMaskWidth(viewToMaskWidth); + } + + public abstract Point getViewToMaskStartPosition(); + + public abstract int getViewToMaskHeight(); + + public abstract int getViewToMaskWidth(); + + @AutoValue.Builder + public static abstract class Builder { + + public abstract Builder setViewToMaskStartPosition(Point viewToMaskStartPosition); + + public abstract Builder setViewToMaskHeight(int viewToMaskHeight); + + public abstract Builder setViewToMaskWidth(int viewToMaskWidth); + + public abstract InfoForViewToMask build(); + + } + +} diff --git a/coachmarks/src/main/java/com/myntra/coachmarks/common/AnimationType.java b/coachmarks/src/main/java/com/myntra/coachmarks/common/AnimationType.java new file mode 100644 index 0000000..3146f49 --- /dev/null +++ b/coachmarks/src/main/java/com/myntra/coachmarks/common/AnimationType.java @@ -0,0 +1,18 @@ +package com.myntra.coachmarks.common; + +import android.support.annotation.IntDef; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@IntDef({AnimationType.THROB_ANIMATION, + AnimationType.SCALE_ANIMATION, + AnimationType.ALPHA_ANIMATION, + AnimationType.ANIMATION_NONE}) +@Retention(RetentionPolicy.SOURCE) +public @interface AnimationType { + int THROB_ANIMATION = 0; + int SCALE_ANIMATION = 1; + int ALPHA_ANIMATION = 2; + int ANIMATION_NONE = 3; +} \ No newline at end of file diff --git a/coachmarks/src/main/java/com/myntra/coachmarks/common/CoachMarkAlignPosition.java b/coachmarks/src/main/java/com/myntra/coachmarks/common/CoachMarkAlignPosition.java new file mode 100644 index 0000000..7e8f711 --- /dev/null +++ b/coachmarks/src/main/java/com/myntra/coachmarks/common/CoachMarkAlignPosition.java @@ -0,0 +1,19 @@ +package com.myntra.coachmarks.common; + +import android.support.annotation.IntDef; +import android.widget.RelativeLayout; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@IntDef({CoachMarkAlignPosition.ALIGN_LEFT, + CoachMarkAlignPosition.ALIGN_TOP, + CoachMarkAlignPosition.ALIGN_RIGHT, + CoachMarkAlignPosition.ALIGN_BOTTOM}) +@Retention(RetentionPolicy.SOURCE) +public @interface CoachMarkAlignPosition { + int ALIGN_LEFT = RelativeLayout.ALIGN_LEFT; + int ALIGN_TOP = RelativeLayout.ALIGN_TOP; + int ALIGN_RIGHT = RelativeLayout.ALIGN_RIGHT; + int ALIGN_BOTTOM = RelativeLayout.ALIGN_BOTTOM; +} \ No newline at end of file diff --git a/coachmarks/src/main/java/com/myntra/coachmarks/common/CoachMarkLayoutOrientation.java b/coachmarks/src/main/java/com/myntra/coachmarks/common/CoachMarkLayoutOrientation.java new file mode 100644 index 0000000..2caae34 --- /dev/null +++ b/coachmarks/src/main/java/com/myntra/coachmarks/common/CoachMarkLayoutOrientation.java @@ -0,0 +1,14 @@ +package com.myntra.coachmarks.common; + +import android.support.annotation.IntDef; +import android.widget.LinearLayout; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@IntDef({CoachMarkLayoutOrientation.HORIZONTAL, CoachMarkLayoutOrientation.VERTICAL}) +@Retention(RetentionPolicy.SOURCE) +public @interface CoachMarkLayoutOrientation { + int HORIZONTAL = LinearLayout.HORIZONTAL; + int VERTICAL = LinearLayout.VERTICAL; +} \ No newline at end of file diff --git a/coachmarks/src/main/java/com/myntra/coachmarks/common/CoachMarkTextGravity.java b/coachmarks/src/main/java/com/myntra/coachmarks/common/CoachMarkTextGravity.java new file mode 100644 index 0000000..13e641c --- /dev/null +++ b/coachmarks/src/main/java/com/myntra/coachmarks/common/CoachMarkTextGravity.java @@ -0,0 +1,17 @@ +package com.myntra.coachmarks.common; + +import android.support.annotation.IntDef; +import android.view.Gravity; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@IntDef({CoachMarkTextGravity.LEFT, + CoachMarkTextGravity.CENTER, + CoachMarkTextGravity.RIGHT}) +@Retention(RetentionPolicy.SOURCE) +public @interface CoachMarkTextGravity { + int LEFT = Gravity.LEFT; + int CENTER = Gravity.CENTER; + int RIGHT = Gravity.RIGHT; +} \ No newline at end of file diff --git a/coachmarks/src/main/java/com/myntra/coachmarks/common/DialogDismissButtonPosition.java b/coachmarks/src/main/java/com/myntra/coachmarks/common/DialogDismissButtonPosition.java new file mode 100644 index 0000000..3ed6818 --- /dev/null +++ b/coachmarks/src/main/java/com/myntra/coachmarks/common/DialogDismissButtonPosition.java @@ -0,0 +1,14 @@ +package com.myntra.coachmarks.common; + +import android.support.annotation.IntDef; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@IntDef({DialogDismissButtonPosition.LEFT, + DialogDismissButtonPosition.RIGHT}) +@Retention(RetentionPolicy.SOURCE) +public @interface DialogDismissButtonPosition { + int LEFT = 0; + int RIGHT = 1; +} \ No newline at end of file diff --git a/coachmarks/src/main/java/com/myntra/coachmarks/common/PopUpPosition.java b/coachmarks/src/main/java/com/myntra/coachmarks/common/PopUpPosition.java new file mode 100644 index 0000000..42b72af --- /dev/null +++ b/coachmarks/src/main/java/com/myntra/coachmarks/common/PopUpPosition.java @@ -0,0 +1,20 @@ +package com.myntra.coachmarks.common; + +import android.support.annotation.IntDef; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@IntDef({PopUpPosition.LEFT, + PopUpPosition.RIGHT, + PopUpPosition.BOTTOM, + PopUpPosition.TOP, + PopUpPosition.NONE}) +@Retention(RetentionPolicy.SOURCE) +public @interface PopUpPosition { + int LEFT = 0; + int RIGHT = 1; + int TOP = 2; + int BOTTOM = 3; + int NONE = 4; +} \ No newline at end of file diff --git a/coachmarks/src/main/java/com/myntra/coachmarks/providers/DefaultDimensionResourceProvider.java b/coachmarks/src/main/java/com/myntra/coachmarks/providers/DefaultDimensionResourceProvider.java new file mode 100644 index 0000000..7b7f91d --- /dev/null +++ b/coachmarks/src/main/java/com/myntra/coachmarks/providers/DefaultDimensionResourceProvider.java @@ -0,0 +1,23 @@ +package com.myntra.coachmarks.providers; + +import android.content.Context; +import android.support.annotation.DimenRes; + +import com.myntra.coachmarks.providers.interfaces.IDimensionResourceProvider; + +import javax.annotation.ParametersAreNonnullByDefault; + +@ParametersAreNonnullByDefault +public class DefaultDimensionResourceProvider implements IDimensionResourceProvider { + + private Context mContext; + + public DefaultDimensionResourceProvider(final Context context) { + mContext = context; + } + + @Override + public int getDimensionInPixel(@DimenRes final int dimenRes) { + return mContext.getResources().getDimensionPixelSize(dimenRes); + } +} diff --git a/coachmarks/src/main/java/com/myntra/coachmarks/providers/DefaultScreenInfoProvider.java b/coachmarks/src/main/java/com/myntra/coachmarks/providers/DefaultScreenInfoProvider.java new file mode 100644 index 0000000..681b84e --- /dev/null +++ b/coachmarks/src/main/java/com/myntra/coachmarks/providers/DefaultScreenInfoProvider.java @@ -0,0 +1,30 @@ +package com.myntra.coachmarks.providers; + +import android.content.Context; +import android.util.DisplayMetrics; + +import com.myntra.coachmarks.providers.interfaces.IScreenInfoProvider; + +import javax.annotation.ParametersAreNonnullByDefault; + +@ParametersAreNonnullByDefault +public class DefaultScreenInfoProvider implements IScreenInfoProvider { + + private Context mContext; + + public DefaultScreenInfoProvider(final Context context) { + mContext = context; + } + + @Override + public int getScreenHeightInPixels() { + DisplayMetrics displayMetrics = mContext.getResources().getDisplayMetrics(); + return displayMetrics.heightPixels; + } + + @Override + public int getScreenWidthInPixels() { + DisplayMetrics displayMetrics = mContext.getResources().getDisplayMetrics(); + return displayMetrics.widthPixels; + } +} diff --git a/coachmarks/src/main/java/com/myntra/coachmarks/providers/DefaultStringResourceProvider.java b/coachmarks/src/main/java/com/myntra/coachmarks/providers/DefaultStringResourceProvider.java new file mode 100644 index 0000000..9c2b199 --- /dev/null +++ b/coachmarks/src/main/java/com/myntra/coachmarks/providers/DefaultStringResourceProvider.java @@ -0,0 +1,28 @@ +package com.myntra.coachmarks.providers; + +import android.content.Context; +import android.support.annotation.StringRes; + +import com.myntra.coachmarks.providers.interfaces.IStringResourceProvider; + +import javax.annotation.ParametersAreNonnullByDefault; + +@ParametersAreNonnullByDefault +public class DefaultStringResourceProvider implements IStringResourceProvider { + + private Context mContext; + + public DefaultStringResourceProvider(final Context context) { + mContext = context; + } + + @Override + public String getStringFromResource(@StringRes final int stringRes) { + return mContext.getString(stringRes); + } + + @Override + public String getStringFromResource(@StringRes final int stringResId, final Object... formatArgs) { + return mContext.getString(stringResId, formatArgs); + } +} diff --git a/coachmarks/src/main/java/com/myntra/coachmarks/providers/DefaultTypeFaceProvider.java b/coachmarks/src/main/java/com/myntra/coachmarks/providers/DefaultTypeFaceProvider.java new file mode 100644 index 0000000..b6199e7 --- /dev/null +++ b/coachmarks/src/main/java/com/myntra/coachmarks/providers/DefaultTypeFaceProvider.java @@ -0,0 +1,29 @@ +package com.myntra.coachmarks.providers; + +import android.content.Context; +import android.graphics.Typeface; +import android.util.Log; + +import com.myntra.coachmarks.providers.interfaces.ITypeFaceProvider; + +import javax.annotation.ParametersAreNonnullByDefault; + +@ParametersAreNonnullByDefault +public class DefaultTypeFaceProvider implements ITypeFaceProvider { + + private Context mContext; + + public DefaultTypeFaceProvider(final Context context) { + mContext = context; + } + + @Override + public Typeface getTypeFaceFromAssets(final String fontFilePath) { + try { + return Typeface.createFromAsset(mContext.getAssets(), fontFilePath); + } catch (RuntimeException e) { + Log.e(DefaultTypeFaceProvider.class.getName(), e.getMessage()); + } + return null; + } +} diff --git a/coachmarks/src/main/java/com/myntra/coachmarks/providers/interfaces/IDimensionResourceProvider.java b/coachmarks/src/main/java/com/myntra/coachmarks/providers/interfaces/IDimensionResourceProvider.java new file mode 100644 index 0000000..ab952ca --- /dev/null +++ b/coachmarks/src/main/java/com/myntra/coachmarks/providers/interfaces/IDimensionResourceProvider.java @@ -0,0 +1,7 @@ +package com.myntra.coachmarks.providers.interfaces; + +import android.support.annotation.DimenRes; + +public interface IDimensionResourceProvider { + int getDimensionInPixel(@DimenRes final int dimenRes); +} diff --git a/coachmarks/src/main/java/com/myntra/coachmarks/providers/interfaces/IScreenInfoProvider.java b/coachmarks/src/main/java/com/myntra/coachmarks/providers/interfaces/IScreenInfoProvider.java new file mode 100644 index 0000000..d8bb2f7 --- /dev/null +++ b/coachmarks/src/main/java/com/myntra/coachmarks/providers/interfaces/IScreenInfoProvider.java @@ -0,0 +1,7 @@ +package com.myntra.coachmarks.providers.interfaces; + +public interface IScreenInfoProvider { + int getScreenHeightInPixels(); + + int getScreenWidthInPixels(); +} diff --git a/coachmarks/src/main/java/com/myntra/coachmarks/providers/interfaces/IStringResourceProvider.java b/coachmarks/src/main/java/com/myntra/coachmarks/providers/interfaces/IStringResourceProvider.java new file mode 100644 index 0000000..ad62317 --- /dev/null +++ b/coachmarks/src/main/java/com/myntra/coachmarks/providers/interfaces/IStringResourceProvider.java @@ -0,0 +1,12 @@ +package com.myntra.coachmarks.providers.interfaces; + +import android.support.annotation.StringRes; + +public interface IStringResourceProvider { + + String getStringFromResource(@StringRes final int stringRes); + + String getStringFromResource(@StringRes final int stringResId, + final Object... formatArgs); + +} \ No newline at end of file diff --git a/coachmarks/src/main/java/com/myntra/coachmarks/providers/interfaces/ITypeFaceProvider.java b/coachmarks/src/main/java/com/myntra/coachmarks/providers/interfaces/ITypeFaceProvider.java new file mode 100644 index 0000000..d01fbad --- /dev/null +++ b/coachmarks/src/main/java/com/myntra/coachmarks/providers/interfaces/ITypeFaceProvider.java @@ -0,0 +1,7 @@ +package com.myntra.coachmarks.providers.interfaces; + +import android.graphics.Typeface; + +public interface ITypeFaceProvider { + Typeface getTypeFaceFromAssets(final String fontFilePath); +} diff --git a/coachmarks/src/main/java/com/myntra/coachmarks/ui/common/BaseViews.java b/coachmarks/src/main/java/com/myntra/coachmarks/ui/common/BaseViews.java new file mode 100644 index 0000000..a64f229 --- /dev/null +++ b/coachmarks/src/main/java/com/myntra/coachmarks/ui/common/BaseViews.java @@ -0,0 +1,26 @@ +package com.myntra.coachmarks.ui.common; + +import android.support.annotation.CallSuper; +import android.view.View; + +import butterknife.ButterKnife; + +public abstract class BaseViews { + + private View mRoot; + + protected BaseViews(View root) { + mRoot = root; + ButterKnife.bind(this, root); + } + + public View getRootView() { + return mRoot; + } + + @CallSuper + public void clear() { + mRoot = null; + } + +} diff --git a/coachmarks/src/main/java/com/myntra/coachmarks/ui/presentation/IPopUpCoachMarkPresentation.java b/coachmarks/src/main/java/com/myntra/coachmarks/ui/presentation/IPopUpCoachMarkPresentation.java new file mode 100644 index 0000000..4b7bb45 --- /dev/null +++ b/coachmarks/src/main/java/com/myntra/coachmarks/ui/presentation/IPopUpCoachMarkPresentation.java @@ -0,0 +1,52 @@ +package com.myntra.coachmarks.ui.presentation; + +import android.graphics.Rect; +import android.graphics.Typeface; + +import com.myntra.coachmarks.common.CoachMarkAlignPosition; +import com.myntra.coachmarks.common.CoachMarkLayoutOrientation; +import com.myntra.coachmarks.common.CoachMarkTextGravity; + +public interface IPopUpCoachMarkPresentation { + void createViewToBeMaskedOut(int startX, int startY, int height, int width); + + void setImageInformation(double centerX, double centerY, int imageWidth, + int imageHeight, int backGroundTint, int imageDrawableRes); + + void dismissWithError(String message); + + void onDismiss(); + + void startThrobAnimationOnImage(); + + void setDismissButtonPositionLeft(); + + void setDismissButtonPositionRight(); + + void startScaleAnimationOnImage(); + + void startAlphaAnimationOnImage(); + + void setTypeFaceForDismissButton(Typeface typeface); + + void setTypeFaceForPopUpText(Typeface typeface); + + void setUpGravityForCoachMarkText(@CoachMarkTextGravity int gravity); + + void setPopUpViewTopLeft(Rect margin, @CoachMarkLayoutOrientation int orientation); + + void setPopUpViewBottomRight(Rect margin, @CoachMarkLayoutOrientation int orientation); + + void setNotchPositionIfPopUpTopLeft(Rect margin, float rotation); + + void setNotchPositionIfPopUpBottomRight(Rect margin, float rotation); + + void uiAdjustmentForNotchIfPopUpRight(Rect margin); + + void uiAdjustmentForNotchIfPopUpBottom(Rect margin); + + void setCoachMarkMessage(String message); + + void setPopUpViewPositionWithRespectToImage(@CoachMarkAlignPosition int alignPosition); + +} diff --git a/coachmarks/src/main/java/com/myntra/coachmarks/ui/presenter/PopUpCoachMarkPresenter.java b/coachmarks/src/main/java/com/myntra/coachmarks/ui/presenter/PopUpCoachMarkPresenter.java new file mode 100644 index 0000000..18208f2 --- /dev/null +++ b/coachmarks/src/main/java/com/myntra/coachmarks/ui/presenter/PopUpCoachMarkPresenter.java @@ -0,0 +1,450 @@ +package com.myntra.coachmarks.ui.presenter; + +import android.graphics.Point; +import android.graphics.Rect; +import android.graphics.Typeface; +import android.support.annotation.ColorRes; +import android.support.annotation.DrawableRes; +import android.support.annotation.Nullable; +import android.support.annotation.StringRes; + +import com.myntra.coachmarks.R; +import com.myntra.coachmarks.builder.CoachMarkBuilder; +import com.myntra.coachmarks.builder.CoachMarkLayoutMargin; +import com.myntra.coachmarks.builder.CoachMarkPixelInfo; +import com.myntra.coachmarks.builder.InfoForViewToMask; +import com.myntra.coachmarks.common.AnimationType; +import com.myntra.coachmarks.common.CoachMarkAlignPosition; +import com.myntra.coachmarks.common.CoachMarkLayoutOrientation; +import com.myntra.coachmarks.common.CoachMarkTextGravity; +import com.myntra.coachmarks.common.DialogDismissButtonPosition; +import com.myntra.coachmarks.common.PopUpPosition; +import com.myntra.coachmarks.providers.interfaces.IDimensionResourceProvider; +import com.myntra.coachmarks.providers.interfaces.IScreenInfoProvider; +import com.myntra.coachmarks.providers.interfaces.IStringResourceProvider; +import com.myntra.coachmarks.providers.interfaces.ITypeFaceProvider; +import com.myntra.coachmarks.ui.presentation.IPopUpCoachMarkPresentation; + +import java.util.List; + +import javax.annotation.ParametersAreNonnullByDefault; + +import zeta.android.utils.lang.StringUtils; + +@ParametersAreNonnullByDefault +public class PopUpCoachMarkPresenter { + + private static final double MAX_NOTCH_RANGE = .85; + private static final double MIN_NOTCH_RANGE = 0.0; + + private static final int NO_MARGIN = 0; + + private static final float ROTATION_0 = 0; + private static final float ROTATION_90 = 90; + private static final float ROTATION_180 = 180; + private static final float ROTATION_270 = 270; + + private static final int MULTIPLICATION_FACTOR_FOR_NOTCH_UI_ADJUSTMENT = 3; + + private CoachMarkBuilder mCoachMarkBuilder; + private IPopUpCoachMarkPresentation mPopUpCoachMarkPresentation; + + private ITypeFaceProvider mTypeFaceProvider; + private IScreenInfoProvider mScreenInfoProvider; + private IStringResourceProvider mStringResourceProvider; + private IDimensionResourceProvider mDimensionResourceProvider; + + public PopUpCoachMarkPresenter(final IStringResourceProvider stringResourceProvider, + final IDimensionResourceProvider dimensionResourceProvider, + final ITypeFaceProvider typeFaceProvider, + final IScreenInfoProvider screenInfoProvider) { + mStringResourceProvider = stringResourceProvider; + mDimensionResourceProvider = dimensionResourceProvider; + mTypeFaceProvider = typeFaceProvider; + mScreenInfoProvider = screenInfoProvider; + } + + public void onCreate(CoachMarkBuilder coachMarkBuilder) { + mCoachMarkBuilder = coachMarkBuilder; + } + + public void onCreateView(IPopUpCoachMarkPresentation popUpCoachMarkPresentation) { + mPopUpCoachMarkPresentation = popUpCoachMarkPresentation; + } + + public void onViewCreated() { + displayCoachMark(); + } + + public void onDestroy() { + mPopUpCoachMarkPresentation = null; + mScreenInfoProvider = null; + mDimensionResourceProvider = null; + mStringResourceProvider = null; + mTypeFaceProvider = null; + } + + public void onOkButtonClicked() { + if (mPopUpCoachMarkPresentation != null) { + mPopUpCoachMarkPresentation.onDismiss(); + } + } + + public void onShimClicked() { + if (mPopUpCoachMarkPresentation != null) { + mPopUpCoachMarkPresentation.onDismiss(); + } + } + + //region helper methods + private void displayCoachMark() { + CoachMarkPixelInfo coachMarkDimenInPixel = createCoachMarkPixelInfo(); + @PopUpPosition + int popUpPosition = findCoachMarkTextPopUpDisplayPosition(mCoachMarkBuilder.getAnchorTop(), + mCoachMarkBuilder.getAnchorBottom(), + mCoachMarkBuilder.getUserDesiredPopUpPositionWithRespectToView(), coachMarkDimenInPixel); + + if (popUpPosition == PopUpPosition.NONE) { + mPopUpCoachMarkPresentation.dismissWithError( + mStringResourceProvider.getStringFromResource(R.string.coachmark_no_position_found)); + return; + } + + setTypeFaceForDismissButton(mCoachMarkBuilder.getFontStyleForDismissButton()); + setTypeFaceForCoachMarkText(mCoachMarkBuilder.getFontStyleForCoachMarkText()); + setGravityForCoachMarkText(mCoachMarkBuilder.getCoachMarkTextGravity()); + setMessageForCoachMarkText(mCoachMarkBuilder.getCoachMarkMessage()); + + setNotchDisplayEdge(popUpPosition, mCoachMarkBuilder.getAnchorTop().y, mCoachMarkBuilder.getAnchorBottom().y, mCoachMarkBuilder.getAnchorTop().x, coachMarkDimenInPixel); + detectAndCreateShimOutViews(mCoachMarkBuilder.getInfoForViewToMaskList()); + setImageParamsAndPosition(mCoachMarkBuilder.getAnchorTop(), mCoachMarkBuilder.getAnchorBottom(), coachMarkDimenInPixel.getImageWidthInPixels(), coachMarkDimenInPixel.getImageHeightInPixels(), mCoachMarkBuilder.getBackGroundTintForImage(), mCoachMarkBuilder.getImageDrawableRes()); + createAnimationOnImage(mCoachMarkBuilder.getAnimationTypeOnImage()); + showCoachMark(mCoachMarkBuilder.getPopUpCoachMarkDismissButtonPosition(), popUpPosition); + } + + private void setMessageForCoachMarkText(@StringRes int messageForCoachMarkTextRes) { + mPopUpCoachMarkPresentation.setCoachMarkMessage(mStringResourceProvider.getStringFromResource(messageForCoachMarkTextRes)); + } + + private void setTypeFaceForDismissButton(@Nullable String fontFileForDismissButton) { + if (StringUtils.isNotNullOrEmpty(fontFileForDismissButton)) { + Typeface typeface = mTypeFaceProvider.getTypeFaceFromAssets(fontFileForDismissButton); + if (typeface != null) + mPopUpCoachMarkPresentation.setTypeFaceForDismissButton(typeface); + } + } + + private void setTypeFaceForCoachMarkText(@Nullable String fontFileForPopUpText) { + if (StringUtils.isNotNullOrEmpty(fontFileForPopUpText)) { + Typeface typeface = mTypeFaceProvider.getTypeFaceFromAssets(fontFileForPopUpText); + if (typeface != null) + mPopUpCoachMarkPresentation.setTypeFaceForPopUpText(typeface); + } + } + + private void setGravityForCoachMarkText(int textAlignmentForPopUpText) { + switch (textAlignmentForPopUpText) { + case CoachMarkTextGravity.CENTER: + mPopUpCoachMarkPresentation.setUpGravityForCoachMarkText(CoachMarkTextGravity.CENTER); + break; + case CoachMarkTextGravity.LEFT: + mPopUpCoachMarkPresentation.setUpGravityForCoachMarkText(CoachMarkTextGravity.LEFT); + break; + case CoachMarkTextGravity.RIGHT: + mPopUpCoachMarkPresentation.setUpGravityForCoachMarkText(CoachMarkTextGravity.RIGHT); + break; + } + } + + private void setNotchDisplayEdge(int position, int anchorTopY, int anchorBottomY, int anchorTopX, CoachMarkPixelInfo coachMarkDimenInPixel) { + int centerY = (anchorTopY + anchorBottomY) / 2; + int actualTopMargin; + int actualLeftMargin; + Rect coachMarkMarginRect; + int notchPosition; + Rect notchMarginRect; + Rect notchUiAdjustmentMarginRect; + switch (position) { + case PopUpPosition.LEFT: + actualTopMargin = getActualTopMargin(centerY, coachMarkDimenInPixel); + coachMarkMarginRect = new Rect(coachMarkDimenInPixel.getMarginRectInPixels().left, actualTopMargin - coachMarkDimenInPixel.getMarginRectInPixels().bottom, coachMarkDimenInPixel.getMarginRectInPixels().right + coachMarkDimenInPixel.getImageWidthInPixels(), NO_MARGIN); + mPopUpCoachMarkPresentation.setPopUpViewTopLeft(coachMarkMarginRect, CoachMarkLayoutOrientation.HORIZONTAL); + notchPosition = getMarginTopForNotch(mCoachMarkBuilder.getNotchPosition(), coachMarkDimenInPixel.getPopUpHeightInPixels(), coachMarkDimenInPixel.getNotchDimenInPixels()); + notchMarginRect = new Rect(-coachMarkDimenInPixel.getMarginOffsetForNotchInPixels(), notchPosition, NO_MARGIN, NO_MARGIN); + mPopUpCoachMarkPresentation.setNotchPositionIfPopUpTopLeft(notchMarginRect, ROTATION_90); + break; + case PopUpPosition.TOP: + actualLeftMargin = getActualLeftMargin(anchorTopX, coachMarkDimenInPixel); + coachMarkMarginRect = new Rect(actualLeftMargin - coachMarkDimenInPixel.getMarginRectInPixels().right, coachMarkDimenInPixel.getMarginRectInPixels().top, NO_MARGIN, coachMarkDimenInPixel.getMarginRectInPixels().bottom + coachMarkDimenInPixel.getImageHeightInPixels()); + mPopUpCoachMarkPresentation.setPopUpViewTopLeft(coachMarkMarginRect, CoachMarkLayoutOrientation.VERTICAL); + notchPosition = getMarginLeftForNotch(mCoachMarkBuilder.getNotchPosition(), coachMarkDimenInPixel.getPopUpWidthInPixels(), coachMarkDimenInPixel.getNotchDimenInPixels()); + notchMarginRect = new Rect(notchPosition + coachMarkDimenInPixel.getMarginOffsetForNotchInPixels(), -coachMarkDimenInPixel.getMarginOffsetForNotchInPixels(), NO_MARGIN, NO_MARGIN); + mPopUpCoachMarkPresentation.setNotchPositionIfPopUpTopLeft(notchMarginRect, ROTATION_180); + break; + case PopUpPosition.RIGHT: + actualTopMargin = getActualTopMargin(centerY, coachMarkDimenInPixel); + coachMarkMarginRect = new Rect(coachMarkDimenInPixel.getMarginRectInPixels().left + coachMarkDimenInPixel.getImageWidthInPixels(), actualTopMargin - coachMarkDimenInPixel.getMarginRectInPixels().bottom, coachMarkDimenInPixel.getMarginRectInPixels().right, NO_MARGIN); + mPopUpCoachMarkPresentation.setPopUpViewBottomRight(coachMarkMarginRect, CoachMarkLayoutOrientation.HORIZONTAL); + notchPosition = getMarginTopForNotch(mCoachMarkBuilder.getNotchPosition(), coachMarkDimenInPixel.getPopUpHeightInPixels(), coachMarkDimenInPixel.getNotchDimenInPixels()); + notchMarginRect = new Rect(NO_MARGIN, notchPosition - (2 * coachMarkDimenInPixel.getMarginOffsetForNotchInPixels()), NO_MARGIN, NO_MARGIN); + mPopUpCoachMarkPresentation.setNotchPositionIfPopUpBottomRight(notchMarginRect, ROTATION_270); + notchUiAdjustmentMarginRect = new Rect(NO_MARGIN, notchPosition - coachMarkDimenInPixel.getMarginOffsetForNotchInPixels(), NO_MARGIN, NO_MARGIN); + mPopUpCoachMarkPresentation.uiAdjustmentForNotchIfPopUpRight(notchUiAdjustmentMarginRect); + break; + case PopUpPosition.BOTTOM: + actualLeftMargin = getActualLeftMargin(anchorTopX, coachMarkDimenInPixel); + coachMarkMarginRect = new Rect(actualLeftMargin - coachMarkDimenInPixel.getMarginRectInPixels().right, coachMarkDimenInPixel.getMarginRectInPixels().top + coachMarkDimenInPixel.getImageHeightInPixels(), NO_MARGIN, coachMarkDimenInPixel.getMarginRectInPixels().bottom); + mPopUpCoachMarkPresentation.setPopUpViewBottomRight(coachMarkMarginRect, CoachMarkLayoutOrientation.VERTICAL); + notchPosition = getMarginLeftForNotch(mCoachMarkBuilder.getNotchPosition(), coachMarkDimenInPixel.getPopUpWidthInPixels(), coachMarkDimenInPixel.getNotchDimenInPixels()); + notchMarginRect = new Rect(notchPosition + coachMarkDimenInPixel.getMarginOffsetForNotchInPixels(), NO_MARGIN, NO_MARGIN, NO_MARGIN); + mPopUpCoachMarkPresentation.setNotchPositionIfPopUpBottomRight(notchMarginRect, ROTATION_0); + notchUiAdjustmentMarginRect = new Rect(notchPosition + MULTIPLICATION_FACTOR_FOR_NOTCH_UI_ADJUSTMENT * coachMarkDimenInPixel.getMarginOffsetForNotchInPixels(), NO_MARGIN, NO_MARGIN, NO_MARGIN); + mPopUpCoachMarkPresentation.uiAdjustmentForNotchIfPopUpBottom(notchUiAdjustmentMarginRect); + break; + } + } + + private int getMarginLeftForNotch(double notchPosition, int popUpWidth, int notchDimen) { + if (notchPosition > MAX_NOTCH_RANGE) + notchPosition = MAX_NOTCH_RANGE; + if (notchPosition <= MIN_NOTCH_RANGE) + notchPosition = MIN_NOTCH_RANGE; + int marginLeft = (int) (popUpWidth * notchPosition); + if (marginLeft > popUpWidth - notchDimen) + return (popUpWidth - notchDimen); + return marginLeft; + } + + private int getMarginTopForNotch(double notchPosition, int popUpHeight, int notchDimen) { + if (notchPosition > MAX_NOTCH_RANGE) + notchPosition = MAX_NOTCH_RANGE; + if (notchPosition <= MIN_NOTCH_RANGE) + notchPosition = MIN_NOTCH_RANGE; + int marginTop = (int) (popUpHeight * notchPosition); + if (marginTop > popUpHeight - notchDimen) + return (popUpHeight - notchDimen); + return marginTop; + } + + private void detectAndCreateShimOutViews(@Nullable List infoForViewToMaskList) { + if (infoForViewToMaskList == null) { + return; + } + for (InfoForViewToMask infoForViewToMask : infoForViewToMaskList) { + mPopUpCoachMarkPresentation.createViewToBeMaskedOut( + infoForViewToMask.getViewToMaskStartPosition().x, + infoForViewToMask.getViewToMaskStartPosition().y, + infoForViewToMask.getViewToMaskHeight(), + infoForViewToMask.getViewToMaskWidth()); + } + } + + private int findCoachMarkTextPopUpDisplayPosition(Point anchorTop, Point anchorBottom, + @PopUpPosition int defaultPopUpPosition, + CoachMarkPixelInfo coachMarkDimenInPixel) { + int centerX = (anchorTop.x + anchorBottom.x) / 2; + int centerY = (anchorTop.y + anchorBottom.y) / 2; + Point centerViewPoint = new Point(centerX, centerY); + @PopUpPosition + int popUpPosition = getDisplayPosition(centerViewPoint, + defaultPopUpPosition, coachMarkDimenInPixel); + return popUpPosition; + } + + private void createAnimationOnImage(@AnimationType int animationType) { + switch (animationType) { + case AnimationType.THROB_ANIMATION: + mPopUpCoachMarkPresentation.startThrobAnimationOnImage(); + break; + case AnimationType.ALPHA_ANIMATION: + mPopUpCoachMarkPresentation.startAlphaAnimationOnImage(); + break; + case AnimationType.SCALE_ANIMATION: + mPopUpCoachMarkPresentation.startScaleAnimationOnImage(); + break; + case AnimationType.ANIMATION_NONE: + break; + } + } + + private void setImageParamsAndPosition(Point anchorTop, Point anchorBottom, + int imageWidth, int imageHeight, + @ColorRes int backGroundTintForImage, + @DrawableRes int imageDrawableRes) { + double centerX = (anchorTop.x + anchorBottom.x) / 2; + double centerY = (anchorTop.y + anchorBottom.y) / 2; + mPopUpCoachMarkPresentation.setImageInformation(centerX, centerY, imageWidth, + imageHeight, backGroundTintForImage, imageDrawableRes); + } + + private void showCoachMark(@DialogDismissButtonPosition int dismissButtonPosition, + @PopUpPosition int popUpPosition) { + + switch (popUpPosition) { + case PopUpPosition.LEFT: + mPopUpCoachMarkPresentation.setPopUpViewPositionWithRespectToImage(CoachMarkAlignPosition.ALIGN_RIGHT); + break; + case PopUpPosition.TOP: + mPopUpCoachMarkPresentation.setPopUpViewPositionWithRespectToImage(CoachMarkAlignPosition.ALIGN_BOTTOM); + break; + case PopUpPosition.RIGHT: + mPopUpCoachMarkPresentation.setPopUpViewPositionWithRespectToImage(CoachMarkAlignPosition.ALIGN_LEFT); + break; + case PopUpPosition.BOTTOM: + mPopUpCoachMarkPresentation.setPopUpViewPositionWithRespectToImage(CoachMarkAlignPosition.ALIGN_TOP); + break; + case PopUpPosition.NONE: + //TODO:: Handle this case + break; + } + + switch (dismissButtonPosition) { + case DialogDismissButtonPosition.LEFT: + mPopUpCoachMarkPresentation.setDismissButtonPositionLeft(); + break; + case DialogDismissButtonPosition.RIGHT: + mPopUpCoachMarkPresentation.setDismissButtonPositionRight(); + break; + } + } + + @PopUpPosition + private int getDisplayPosition(Point viewCenterPoint, + @PopUpPosition int defaultPopUpPosition, + CoachMarkPixelInfo coachMarkDimenInPixel) { + @PopUpPosition + int correctPosition = 0; + switch (defaultPopUpPosition) { + case PopUpPosition.LEFT: + if (checkIfLeftPossible(viewCenterPoint, coachMarkDimenInPixel)) { + correctPosition = defaultPopUpPosition; + } else { + correctPosition = getCorrectPositionOfCoachMarkIfDefaultFails(viewCenterPoint, coachMarkDimenInPixel); + } + break; + case PopUpPosition.RIGHT: + if (checkIfRightPossible(viewCenterPoint, coachMarkDimenInPixel)) { + correctPosition = defaultPopUpPosition; + } else { + correctPosition = getCorrectPositionOfCoachMarkIfDefaultFails(viewCenterPoint, coachMarkDimenInPixel); + } + break; + + case PopUpPosition.BOTTOM: + if (checkIfBottomPossible(viewCenterPoint, coachMarkDimenInPixel)) { + correctPosition = defaultPopUpPosition; + } else { + correctPosition = getCorrectPositionOfCoachMarkIfDefaultFails(viewCenterPoint, coachMarkDimenInPixel); + } + break; + + case PopUpPosition.TOP: + if (checkIfTopPossible(viewCenterPoint, coachMarkDimenInPixel)) { + correctPosition = defaultPopUpPosition; + } else { + correctPosition = getCorrectPositionOfCoachMarkIfDefaultFails(viewCenterPoint, coachMarkDimenInPixel); + } + break; + case PopUpPosition.NONE: + //TODO:: Handle this + break; + } + return correctPosition; + } + + private boolean checkIfLeftPossible(Point viewCenterPoint, CoachMarkPixelInfo coachMarkDimenInPixel) { + int centerX = viewCenterPoint.x; + return (coachMarkDimenInPixel.getPopUpWidthInPixelsWithOffset() + coachMarkDimenInPixel.getMarginRectInPixels().right + coachMarkDimenInPixel.getMarginRectInPixels().left) < centerX && ((coachMarkDimenInPixel.getPopUpHeightInPixelsWithOffset() + coachMarkDimenInPixel.getMarginRectInPixels().top + coachMarkDimenInPixel.getActionBarHeightPixels() + coachMarkDimenInPixel.getFooterHeightPixels() + coachMarkDimenInPixel.getMarginRectInPixels().bottom) <= coachMarkDimenInPixel.getScreenHeightInPixels()); + } + + private boolean checkIfRightPossible(Point viewCenterPoint, CoachMarkPixelInfo coachMarkDimenInPixel) { + int centerX = viewCenterPoint.x; + return (coachMarkDimenInPixel.getPopUpWidthInPixelsWithOffset() + coachMarkDimenInPixel.getMarginRectInPixels().right + coachMarkDimenInPixel.getMarginRectInPixels().left) <= (coachMarkDimenInPixel.getScreenWidthInPixels() - centerX) && ((coachMarkDimenInPixel.getPopUpHeightInPixelsWithOffset() + coachMarkDimenInPixel.getMarginRectInPixels().top + coachMarkDimenInPixel.getActionBarHeightPixels() + coachMarkDimenInPixel.getFooterHeightPixels() + coachMarkDimenInPixel.getMarginRectInPixels().bottom) <= coachMarkDimenInPixel.getScreenHeightInPixels()); + } + + private boolean checkIfTopPossible(Point viewCenterPoint, CoachMarkPixelInfo coachMarkDimenInPixel) { + int centerY = viewCenterPoint.y; + return (coachMarkDimenInPixel.getPopUpHeightInPixelsWithOffset() + coachMarkDimenInPixel.getMarginRectInPixels().top + coachMarkDimenInPixel.getMarginRectInPixels().bottom + coachMarkDimenInPixel.getActionBarHeightPixels()) <= centerY && ((coachMarkDimenInPixel.getWidthHeightOffsetForCoachMarkPopUp() + coachMarkDimenInPixel.getMarginRectInPixels().left + coachMarkDimenInPixel.getMarginRectInPixels().right) <= coachMarkDimenInPixel.getScreenWidthInPixels()); + } + + private boolean checkIfBottomPossible(Point viewCenterPoint, CoachMarkPixelInfo coachMarkDimenInPixel) { + int centerY = viewCenterPoint.y; + return (coachMarkDimenInPixel.getPopUpHeightInPixelsWithOffset() + coachMarkDimenInPixel.getMarginRectInPixels().top + coachMarkDimenInPixel.getMarginRectInPixels().bottom + coachMarkDimenInPixel.getFooterHeightPixels()) <= coachMarkDimenInPixel.getScreenHeightInPixels() - centerY && ((coachMarkDimenInPixel.getPopUpWidthInPixelsWithOffset() + coachMarkDimenInPixel.getMarginRectInPixels().left + coachMarkDimenInPixel.getMarginRectInPixels().right) <= coachMarkDimenInPixel.getScreenWidthInPixels()); + } + + @PopUpPosition + private int getCorrectPositionOfCoachMarkIfDefaultFails(Point viewCenterPoint, CoachMarkPixelInfo coachMarkDimenInPixel) { + + @PopUpPosition int correctPopUpPosition = PopUpPosition.NONE; + + if (checkIfRightPossible(viewCenterPoint, coachMarkDimenInPixel)) + correctPopUpPosition = PopUpPosition.RIGHT; + else if (checkIfBottomPossible(viewCenterPoint, coachMarkDimenInPixel)) + correctPopUpPosition = PopUpPosition.BOTTOM; + else if (checkIfLeftPossible(viewCenterPoint, coachMarkDimenInPixel)) + correctPopUpPosition = PopUpPosition.LEFT; + else if (checkIfTopPossible(viewCenterPoint, coachMarkDimenInPixel)) + correctPopUpPosition = PopUpPosition.TOP; + + return correctPopUpPosition; + } + + private CoachMarkPixelInfo createCoachMarkPixelInfo() { + final CoachMarkLayoutMargin coachMarkLayoutMargin = mCoachMarkBuilder.getCoachMarkLayoutMargin(); + int leftMargin = mDimensionResourceProvider.getDimensionInPixel(coachMarkLayoutMargin.getLeftMarginForCoachMark()); + int rightMargin = mDimensionResourceProvider.getDimensionInPixel(coachMarkLayoutMargin.getRightMarginForCoachMark()); + int topMargin = mDimensionResourceProvider.getDimensionInPixel(coachMarkLayoutMargin.getTopMarginForCoachMark()); + int bottomMargin = mDimensionResourceProvider.getDimensionInPixel(coachMarkLayoutMargin.getBottomMarginForCoachMark()); + + int actionBarHeightPixels = mDimensionResourceProvider.getDimensionInPixel(mCoachMarkBuilder.getActionBarHeight()); + int footerHeightPixels = mDimensionResourceProvider.getDimensionInPixel(mCoachMarkBuilder.getFooterHeight()); + + int imageHeightInPixels = mDimensionResourceProvider.getDimensionInPixel(mCoachMarkBuilder.getImageLayoutInformation().getImageHeight()); + int imageWidthInPixels = mDimensionResourceProvider.getDimensionInPixel(mCoachMarkBuilder.getImageLayoutInformation().getImageWidth()); + + int coachMarkPopUpHeightInPixels = mDimensionResourceProvider.getDimensionInPixel(R.dimen.coach_mark_pop_up_height); + int coachMarkPopUpWidthInPixels = mDimensionResourceProvider.getDimensionInPixel(R.dimen.coach_mark_pop_up_width); + + int notchDimenInPixels = mDimensionResourceProvider.getDimensionInPixel(R.dimen.coach_mark_notch_width_height); + int marginOffsetForNotchInPixels = mDimensionResourceProvider.getDimensionInPixel(R.dimen.coach_mark_notch_margin_offset); + + int widthHeightOffsetForCoachMarkPopUp = mDimensionResourceProvider.getDimensionInPixel(R.dimen.coach_mark_pop_up_width_height_offset); + int coachMarkPopUpWidthInPixelsWithOffset = coachMarkPopUpWidthInPixels + widthHeightOffsetForCoachMarkPopUp + imageWidthInPixels; + int coachMarkPopUpHeightInPixelsWithOffset = coachMarkPopUpHeightInPixels + widthHeightOffsetForCoachMarkPopUp + imageHeightInPixels; + + int screenHeightInPixels = mScreenInfoProvider.getScreenHeightInPixels(); + int screenWidthInPixels = mScreenInfoProvider.getScreenWidthInPixels(); + + return CoachMarkPixelInfo.create() + .setMarginRectInPixels(new Rect(leftMargin, topMargin, rightMargin, bottomMargin)) + .setPopUpHeightInPixels(coachMarkPopUpHeightInPixels) + .setPopUpHeightInPixelsWithOffset(coachMarkPopUpHeightInPixelsWithOffset) + .setPopUpWidthInPixels(coachMarkPopUpWidthInPixels) + .setPopUpWidthInPixelsWithOffset(coachMarkPopUpWidthInPixelsWithOffset) + .setWidthHeightOffsetForCoachMarkPopUp(widthHeightOffsetForCoachMarkPopUp) + .setActionBarHeightPixels(actionBarHeightPixels) + .setFooterHeightPixels(footerHeightPixels) + .setNotchDimenInPixels(notchDimenInPixels) + .setMarginOffsetForNotchInPixels(marginOffsetForNotchInPixels) + .setImageHeightInPixels(imageHeightInPixels) + .setImageWidthInPixels(imageWidthInPixels) + .setScreenHeightInPixels(screenHeightInPixels) + .setScreenWidthInPixels(screenWidthInPixels) + .build(); + } + + private int getActualTopMargin(int centerTopY, CoachMarkPixelInfo coachMarkDimenInPixel) { + if (centerTopY + coachMarkDimenInPixel.getPopUpHeightInPixels() + coachMarkDimenInPixel.getWidthHeightOffsetForCoachMarkPopUp() + coachMarkDimenInPixel.getMarginRectInPixels().top + coachMarkDimenInPixel.getMarginRectInPixels().bottom > coachMarkDimenInPixel.getScreenHeightInPixels()) { + return coachMarkDimenInPixel.getScreenHeightInPixels() - coachMarkDimenInPixel.getPopUpHeightInPixels() + coachMarkDimenInPixel.getWidthHeightOffsetForCoachMarkPopUp() + coachMarkDimenInPixel.getMarginRectInPixels().top + coachMarkDimenInPixel.getMarginRectInPixels().bottom; + } + return centerTopY + coachMarkDimenInPixel.getMarginRectInPixels().top; + } + + private int getActualLeftMargin(int anchorTopX, CoachMarkPixelInfo coachMarkDimenInPixel) { + if (anchorTopX + coachMarkDimenInPixel.getPopUpWidthInPixels() + coachMarkDimenInPixel.getWidthHeightOffsetForCoachMarkPopUp() + coachMarkDimenInPixel.getMarginRectInPixels().right + coachMarkDimenInPixel.getMarginRectInPixels().left > coachMarkDimenInPixel.getScreenWidthInPixels()) { + return coachMarkDimenInPixel.getScreenWidthInPixels() - coachMarkDimenInPixel.getPopUpWidthInPixels() + coachMarkDimenInPixel.getWidthHeightOffsetForCoachMarkPopUp() + coachMarkDimenInPixel.getMarginRectInPixels().right + coachMarkDimenInPixel.getMarginRectInPixels().left; + } + return anchorTopX + coachMarkDimenInPixel.getMarginRectInPixels().left; + } + //endregion +} diff --git a/coachmarks/src/main/java/com/myntra/coachmarks/ui/utils/TransitionUtils.java b/coachmarks/src/main/java/com/myntra/coachmarks/ui/utils/TransitionUtils.java new file mode 100644 index 0000000..2c5eba4 --- /dev/null +++ b/coachmarks/src/main/java/com/myntra/coachmarks/ui/utils/TransitionUtils.java @@ -0,0 +1,52 @@ +package com.myntra.coachmarks.ui.utils; + +import android.animation.ValueAnimator; +import android.view.animation.AccelerateDecelerateInterpolator; +import android.view.animation.AlphaAnimation; +import android.view.animation.Animation; +import android.view.animation.AnimationSet; +import android.view.animation.ScaleAnimation; + +public class TransitionUtils { + + private static final int ALPHA_ANIMATION_DURATION = 2000; + private static final double ALPHA_ANIMATION_FROM = 1; + private static final double ALPHA_ANIMATION_TO = 0; + private static final double SCALE_ANIMATION_FROM_X = 1; + private static final double SCALE_ANIMATION_TO_X = 1.5; + private static final double SCALE_ANIMATION_FROM_Y = 1; + private static final double SCALE_ANIMATION_TO_Y = 1.5; + private static final double SCALE_ANIMATION_PIVOT_X = .5; + private static final double SCALE_ANIMATION_PIVOT_Y = .5; + private static final int SCALE_ANIMATION_DURATION = 2000; + + public static AnimationSet createThrobAnimation() { + AnimationSet animationSet = new AnimationSet(false); + animationSet.addAnimation(createAlphaAnimation()); + animationSet.addAnimation(createScaleAnimation()); + return animationSet; + } + + public static Animation createAlphaAnimation() { + ScaleAnimation scaleAnimation = new ScaleAnimation((float) SCALE_ANIMATION_FROM_X, + (float) SCALE_ANIMATION_TO_X, (float) SCALE_ANIMATION_FROM_Y, + (float) SCALE_ANIMATION_TO_Y, Animation.RELATIVE_TO_SELF, + (float) SCALE_ANIMATION_PIVOT_X, Animation.RELATIVE_TO_SELF, + (float) SCALE_ANIMATION_PIVOT_Y); + scaleAnimation.setDuration(SCALE_ANIMATION_DURATION); + scaleAnimation.setInterpolator(new AccelerateDecelerateInterpolator()); + scaleAnimation.setRepeatCount(ValueAnimator.INFINITE); + scaleAnimation.setRepeatMode(ValueAnimator.RESTART); + return scaleAnimation; + } + + public static Animation createScaleAnimation() { + AlphaAnimation alphaAnimation = new AlphaAnimation((float) ALPHA_ANIMATION_FROM, + (float) ALPHA_ANIMATION_TO); + alphaAnimation.setDuration(ALPHA_ANIMATION_DURATION); + alphaAnimation.setInterpolator(new AccelerateDecelerateInterpolator()); + alphaAnimation.setRepeatCount(ValueAnimator.INFINITE); + alphaAnimation.setRepeatMode(ValueAnimator.RESTART); + return alphaAnimation; + } +} diff --git a/coachmarks/src/main/res/drawable/background_shadow_drawable.xml b/coachmarks/src/main/res/drawable/background_shadow_drawable.xml new file mode 100644 index 0000000..c7909f9 --- /dev/null +++ b/coachmarks/src/main/res/drawable/background_shadow_drawable.xml @@ -0,0 +1,19 @@ + + + + + + + \ No newline at end of file diff --git a/coachmarks/src/main/res/drawable/coach_mark_vertical_dashed_line_separator.xml b/coachmarks/src/main/res/drawable/coach_mark_vertical_dashed_line_separator.xml new file mode 100644 index 0000000..7a4eb24 --- /dev/null +++ b/coachmarks/src/main/res/drawable/coach_mark_vertical_dashed_line_separator.xml @@ -0,0 +1,16 @@ + + + + + + + + \ No newline at end of file diff --git a/coachmarks/src/main/res/drawable/coachmark_drawable_no_image.xml b/coachmarks/src/main/res/drawable/coachmark_drawable_no_image.xml new file mode 100644 index 0000000..471369b --- /dev/null +++ b/coachmarks/src/main/res/drawable/coachmark_drawable_no_image.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/coachmarks/src/main/res/drawable/coachmark_pop_up_drawable.xml b/coachmarks/src/main/res/drawable/coachmark_pop_up_drawable.xml new file mode 100644 index 0000000..0d76940 --- /dev/null +++ b/coachmarks/src/main/res/drawable/coachmark_pop_up_drawable.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + diff --git a/coachmarks/src/main/res/drawable/triangle_up.xml b/coachmarks/src/main/res/drawable/triangle_up.xml new file mode 100644 index 0000000..1bb7dac --- /dev/null +++ b/coachmarks/src/main/res/drawable/triangle_up.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/coachmarks/src/main/res/layout/pop_up_coach_mark.xml b/coachmarks/src/main/res/layout/pop_up_coach_mark.xml new file mode 100644 index 0000000..c956f04 --- /dev/null +++ b/coachmarks/src/main/res/layout/pop_up_coach_mark.xml @@ -0,0 +1,121 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/coachmarks/src/main/res/values-v21/styles.xml b/coachmarks/src/main/res/values-v21/styles.xml new file mode 100644 index 0000000..f462ea2 --- /dev/null +++ b/coachmarks/src/main/res/values-v21/styles.xml @@ -0,0 +1,15 @@ + + + + + + \ No newline at end of file diff --git a/coachmarks/src/main/res/values/attrs.xml b/coachmarks/src/main/res/values/attrs.xml new file mode 100644 index 0000000..0d2c4cc --- /dev/null +++ b/coachmarks/src/main/res/values/attrs.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/coachmarks/src/main/res/values/color.xml b/coachmarks/src/main/res/values/color.xml new file mode 100644 index 0000000..4fbc8e4 --- /dev/null +++ b/coachmarks/src/main/res/values/color.xml @@ -0,0 +1,16 @@ + + + + #00000000 + #3E4152 + #526cd0 + #D4D5D9 + #FFFFFF + #70000000 + #50000000 + #bfc0c6 + #1B202A + #0BC6A0 + #FFFFFF + + \ No newline at end of file diff --git a/coachmarks/src/main/res/values/dimens.xml b/coachmarks/src/main/res/values/dimens.xml new file mode 100644 index 0000000..e8a3deb --- /dev/null +++ b/coachmarks/src/main/res/values/dimens.xml @@ -0,0 +1,40 @@ + + + + 10dp + 0dp + 10dp + 3dp + 2dp + + 210dp + 50dp + 50dp + 50dp + 50dp + 12dp + 48dp + 12dp + 145dp + 50dp + 2dp + 2dp + 2dp + 13dp + 8dp + 146dp + 8dp + 6dp + + 14sp + 6dp + 8dp + 16dp + + 20dp + 16dp + 17dp + 1dp + + + \ No newline at end of file diff --git a/coachmarks/src/main/res/values/integers.xml b/coachmarks/src/main/res/values/integers.xml new file mode 100644 index 0000000..71c892d --- /dev/null +++ b/coachmarks/src/main/res/values/integers.xml @@ -0,0 +1,5 @@ + + + 2 + 3 + \ No newline at end of file diff --git a/coachmarks/src/main/res/values/strings.xml b/coachmarks/src/main/res/values/strings.xml new file mode 100644 index 0000000..5c8ad8e --- /dev/null +++ b/coachmarks/src/main/res/values/strings.xml @@ -0,0 +1,6 @@ + + + OK + Coachmark cannot be displayed + + diff --git a/coachmarks/src/main/res/values/styles.xml b/coachmarks/src/main/res/values/styles.xml new file mode 100644 index 0000000..0b4eea0 --- /dev/null +++ b/coachmarks/src/main/res/values/styles.xml @@ -0,0 +1,20 @@ + + + + + + + + + diff --git a/sample/src/test/java/com/myntra/android/sample/CoachMarkUnitTest.java b/sample/src/test/java/com/myntra/android/sample/CoachMarkUnitTest.java new file mode 100644 index 0000000..53aaf01 --- /dev/null +++ b/sample/src/test/java/com/myntra/android/sample/CoachMarkUnitTest.java @@ -0,0 +1,12 @@ +package com.myntra.android.sample; + +import org.junit.Test; + +import static org.junit.Assert.*; + +public class CoachMarkUnitTest { + @Test + public void addition_isCorrect() throws Exception { + assertEquals(4, 2 + 2); + } +} \ No newline at end of file diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..1ba1c11 --- /dev/null +++ b/settings.gradle @@ -0,0 +1 @@ +include ':sample', ':coachmarks'