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'