diff --git a/build.gradle b/build.gradle index 4e5a91b..58a6efc 100644 --- a/build.gradle +++ b/build.gradle @@ -1,18 +1,28 @@ buildscript { repositories { + maven { + url 'https://maven.google.com' + } google() jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.0.0-beta2' + classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.2.10' + classpath 'com.android.tools.build:gradle:3.0.1' classpath 'com.novoda:bintray-release:0.5.0' } } allprojects { repositories { + maven { + url 'https://maven.google.com' + } google() jcenter() } } +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/config/config.gradle b/config/config.gradle new file mode 100644 index 0000000..bfaa23e --- /dev/null +++ b/config/config.gradle @@ -0,0 +1,21 @@ +ext.configuration = [ + applicationId: "com.chyrta.boilerplate", + versionMajor: 1, + versionMinor: 0, + versionPatch: 0, + versionClassifier: "", + minimumSdk: 14, + compileSdk: 27, + targetSdk: 27, + buildTools: "27.0.3" +] + +def configuration = ext.configuration; + +ext.buildVersionCode = { + return configuration.minimumSdk * 10000000 + configuration.versionMajor * 10000 + configuration.versionMinor * 100 + configuration.versionPatch +} + +ext.buildVersionName = { + return configuration.versionMajor + "." + configuration.versionMinor + "." + configuration.versionPatch; +} diff --git a/config/library.gradle b/config/library.gradle new file mode 100644 index 0000000..d60704e --- /dev/null +++ b/config/library.gradle @@ -0,0 +1,47 @@ +ext { + versions = [ + kotlin: "1.2.10", + butterKnife: "8.0.1", + supportLibrary: "26.0.1", + constraintLayout: "1.0.2", + pageIndicator: "0.2.0" + ] +} + + +ext { + versions = [ + androidBuildToolsVersion: "27.0.3", + androidMinSdkVersion : 15, + androidTargetSdkVersion : 27, + androidCompileSdkVersion: 27, + kotlinVersion : "1.2.10", + butterKnifeVersion : "8.8.1", + supportLibraryVersion : "27.0.3", + constraintLayoutVersion : "1.0.2", + pageIndicatorViewVersion: "0.2.0", + ] + + // Libraries + supportDeps = [ + appCompatV7 : "com.android.support:appcompat-v7:$versions.supportLibraryVersion", + design : "com.android.support:design:$versions.supportLibraryVersion", + constraintLayout: "com.android.support.constraint:constraint-layout:$versions.constraintLayoutVersion" + ] + + injection = [ + butterKnife: "com.jakewharton:butterknife:$versions.butterKnifeVersion", + ] + + processors = [ + butterKnifeCompiler: "com.jakewharton:butterknife-compiler:$versions.butterKnifeVersion" + ] + + kotlin = "org.jetbrains.kotlin:kotlin-stdlib-jre8:$versions.kotlinVersion" + pageIndicator = "com.romandanylyk:pageindicatorview:$versions.pageIndicatorViewVersion" + + supportDepsLibraries = supportDeps.values() + injectionLibraries = injection.values() + annotationProcessors = processors.values() + +} diff --git a/dependencies.gradle b/dependencies.gradle new file mode 100644 index 0000000..8904c38 --- /dev/null +++ b/dependencies.gradle @@ -0,0 +1,36 @@ +ext { + versions = [ + androidBuildToolsVersion: "26.0.1", + androidMinSdkVersion : 15, + androidTargetSdkVersion : 26, + androidCompileSdkVersion: 26, + kotlinVersion : "1.1.3-2", + butterKnifeVersion : "8.0.1", + supportLibraryVersion : "26.0.1", + constraintLayoutVersion : "1.0.2", + pageIndicatorViewVersion: "0.2.0", + ] + + // Libraries + supportDeps = [ + appCompatV7 : "com.android.support:appcompat-v7:$versions.supportLibraryVersion", + design : "com.android.support:design:$versions.supportLibraryVersion", + constraintLayout: "com.android.support.constraint:constraint-layout:$versions.constraintLayoutVersion" + ] + + injection = [ + butterKnife: "com.jakewharton:butterknife:$versions.butterKnifeVersion", + ] + + processors = [ + butterKnifeCompiler: "com.jakewharton:butterknife-compiler:$versions.butterKnifeVersion" + ] + + kotlin = "org.jetbrains.kotlin:kotlin-stdlib-jre8:$versions.kotlinVersion" + pageIndicator = "com.romandanylyk:pageindicatorview:$versions.pageIndicatorViewVersion" + + supportDepsLibraries = supportDeps.values() + injectionLibraries = injection.values() + annotationProcessors = processors.values() + +} diff --git a/documentation/Android Onboarder 1.0 Migration Guide.md b/documentation/Android Onboarder 1.0 Migration Guide.md new file mode 100644 index 0000000..42b1b34 --- /dev/null +++ b/documentation/Android Onboarder 1.0 Migration Guide.md @@ -0,0 +1,109 @@ +# Android Onboarder 1.0 Migration Guide + +Android Onboarder 1.0 a is first and latest major release of Android Onboarder which helps you to create beautiful onboarding experience for the users of your app. Recently, Google has released their own [guidelines]("material.io/guidelines/growth-communications/onboarding.html") of how onboarding should works in Android apps which support Material Design pattern. + +## New Requirements +Android Onboarder works starting from API 16 and targeted to Android Oreo API 26. + +## Breaking API changes + +### Kotlin +First release of the library was rewritten to Kotlin to reduce size of the codebase and provides support of first-class supported of the modern language by Google. + +### Creating of onboarding pages +One of the weak points of the library as its many copycats around Github is creating onboarging simply with constructor and provided parameters which strongly restricted of what do we want to see in the result. Android Onboarder offers you to use classic Builder patterns while creating onboarding pages. + +Previously: +```java +OnboarderPage onboarderPage1 = new OnboarderPage("Planet Earth", "Our lovely pale blue dot", R.drawable.planet1); +OnboarderPage onboarderPage2 = new OnboarderPage("Venus", "The love goddess", R.drawable.planet2); +OnboarderPage onboarderPage3 = new OnboarderPage("Mars", "Say hi to Curiosity!", R.drawable.planet3); + +onboarderPage1.setBackgroundColor(R.color.onboarder_bg_1); +onboarderPage2.setBackgroundColor(R.color.onboarder_bg_2); +onboarderPage3.setBackgroundColor(R.color.onboarder_bg_3); + +List pages = new ArrayList<>(); + +pages.add(onboarderPage1); +pages.add(onboarderPage2); +pages.add(onboarderPage3); + +for (OnboarderPage page : pages) { + page.setTitleColor(R.color.primary_text); + page.setDescriptionColor(R.color.secondary_text); + page.setMultilineDescriptionCentered(true); +} + +setSkipButtonTitle("Skip"); +setFinishButtonTitle("Finish"); + +setOnboardPagesReady(pages); +``` + +Now: +```java +addPage(OnboarderPage.newCreator() + .title("Planet Earth") + .description("Our lovely pale blue dot") + .backgroundColor(R.color.black_transparent) + .imageResourceId(R.drawable.planet1) + .create()); +addPage(OnboarderPage.newCreator() + .title("Venus") + .description("The love goddess") + .backgroundColor(R.color.black_transparent) + .imageResourceId(R.drawable.planet2) + .create()); +addPage(OnboarderPage.newCreator() + .title("Mars") + .description("Say hi to Curiosity") + .backgroundColor(R.color.black_transparent) + .imageResourceId(R.drawable.planet3) + .create()); +addGetStartedButton(GetStartedButton.newCreator() + .text("Get started") + .textColorResourceId(R.color.colorAccent) + .create()); +startOnboarding(); +``` + +### Introducing Get Started Button + + +### Reducing customization +Regarding Google's in direction of describing, as a creator of the library I decided to reduce the respecting to support key features as the aim of the library is to help developers reduce quantity of hours on implementing onboarding stuff. + +Due to that changes, skip and finish buttons were removed. + +#### Removed Skip and Finish buttons +These buttons were removed, use Getting Started. +```java +setSkipButtonTitle("Skip"); +setFinishButtonTitle("Finish") +``` + + +Previously: +```java +@Override +public void onSkipButtonPressed() { + super.onSkipButtonPressed(); + Toast.makeText(this, "Skip button was pressed!", Toast.LENGTH_SHORT).show(); +} + +@Override +public void onFinishButtonPressed() { + Toast.makeText(this, "Finish button was pressed", Toast.LENGTH_SHORT).show(); +} +``` + +Now: +```java +@Override +public void onGetStartedButtonPressed() { + Toast.makeText(this, "Skip button was pressed!", Toast.LENGTH_SHORT).show(); +} +``` + +Presenting new 'Get Started', you have take care of all the stuff which should be happened after in diff --git a/gradle.properties b/gradle.properties index 0e2f2ca..1d3591c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -15,6 +15,4 @@ # When configured, Gradle will run in incubating parallel mode. # This option should only be used with decoupled projects. More details, visit # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects -# org.gradle.parallel=true - -support_library_version = 26.0.1 +# org.gradle.parallel=true \ No newline at end of file diff --git a/onboarder/build.gradle b/onboarder/build.gradle index ccb5b30..d3dc8c5 100644 --- a/onboarder/build.gradle +++ b/onboarder/build.gradle @@ -1,37 +1,40 @@ apply plugin: 'com.android.library' apply plugin: 'com.novoda.bintray-release' - -repositories { - jcenter() -} +apply plugin: 'kotlin-android' +apply plugin: 'kotlin-kapt' android { compileSdkVersion 26 - buildToolsVersion "26.0.1" + buildToolsVersion '26.0.2' defaultConfig { - minSdkVersion 14 + minSdkVersion 15 targetSdkVersion 26 versionCode 1 versionName "1.0" } + buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } + lintOptions { abortOnError false } } -dependencies { - testCompile 'junit:junit:4.12' +apply from: "../dependencies.gradle" - compile "com.android.support:appcompat-v7:$support_library_version" - compile "com.android.support:design:$support_library_version" +dependencies { + implementation kotlin + implementation supportDepsLibraries + implementation injectionLibraries + implementation pageIndicator + annotationProcessor annotationProcessors } publish { @@ -106,4 +109,4 @@ subprojects { } } } -} \ No newline at end of file +} diff --git a/onboarder/src/main/java/com/chyrta/onboarder/GetStartedButton.kt b/onboarder/src/main/java/com/chyrta/onboarder/GetStartedButton.kt new file mode 100644 index 0000000..6311491 --- /dev/null +++ b/onboarder/src/main/java/com/chyrta/onboarder/GetStartedButton.kt @@ -0,0 +1,45 @@ +package com.chyrta.onboarder + +import android.support.annotation.ColorRes +import android.support.annotation.StringRes + +class GetStartedButton { + + var title: String? = null + var titleResourceId: Int? = null + private set + @StringRes get + var textColorResourceId: Int? = null + private set + @ColorRes get + + inner class Creator internal constructor() { + + fun text(title: String): Creator { + this@GetStartedButton.title = title + return this + } + + fun textResourceId(@StringRes titleResourceId: Int): Creator { + this@GetStartedButton.titleResourceId = titleResourceId + return this + } + + fun textColor(@ColorRes textColorResourceId: Int): Creator { + this@GetStartedButton.textColorResourceId = textColorResourceId + return this + } + + fun create(): GetStartedButton { + return this@GetStartedButton + } + + } + + companion object { + fun newCreator(): Creator { + return GetStartedButton().Creator() + } + } + +} diff --git a/onboarder/src/main/java/com/chyrta/onboarder/OnboarderActivity.java b/onboarder/src/main/java/com/chyrta/onboarder/OnboarderActivity.java deleted file mode 100644 index cb1758f..0000000 --- a/onboarder/src/main/java/com/chyrta/onboarder/OnboarderActivity.java +++ /dev/null @@ -1,209 +0,0 @@ -package com.chyrta.onboarder; - -import android.animation.ArgbEvaluator; -import android.graphics.Color; -import android.os.Build; -import android.os.Bundle; -import android.support.annotation.ColorInt; -import android.support.annotation.StringRes; -import android.support.design.widget.FloatingActionButton; -import android.support.v4.content.ContextCompat; -import android.support.v4.view.ViewPager; -import android.support.v7.app.AppCompatActivity; -import android.util.TypedValue; -import android.view.View; -import android.widget.Button; -import android.widget.FrameLayout; -import android.widget.ImageButton; - -import com.chyrta.onboarder.utils.ColorsArrayBuilder; -import com.chyrta.onboarder.views.CircleIndicatorView; - -import java.util.List; - -public abstract class OnboarderActivity extends AppCompatActivity implements View.OnClickListener, ViewPager.OnPageChangeListener { - - private Integer[] colors; - private CircleIndicatorView circleIndicatorView; - private ViewPager vpOnboarderPager; - private OnboarderAdapter onboarderAdapter; - private ImageButton ibNext; - private Button btnSkip, btnFinish; - private FrameLayout buttonsLayout; - private FloatingActionButton fab; - private View divider; - private ArgbEvaluator evaluator; - - private boolean shouldDarkenButtonsLayout = false; - private boolean shouldUseFloatingActionButton = false; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_onboarder); - setStatusBackgroundColor(); - hideActionBar(); - circleIndicatorView = (CircleIndicatorView) findViewById(R.id.circle_indicator_view); - ibNext = (ImageButton) findViewById(R.id.ib_next); - btnSkip = (Button) findViewById(R.id.btn_skip); - btnFinish = (Button) findViewById(R.id.btn_finish); - buttonsLayout = (FrameLayout) findViewById(R.id.buttons_layout); - fab = (FloatingActionButton) findViewById(R.id.fab); - divider = findViewById(R.id.divider); - vpOnboarderPager = (ViewPager) findViewById(R.id.vp_onboarder_pager); - vpOnboarderPager.addOnPageChangeListener(this); - ibNext.setOnClickListener(this); - btnSkip.setOnClickListener(this); - btnFinish.setOnClickListener(this); - fab.setOnClickListener(this); - evaluator = new ArgbEvaluator(); - } - - public void setOnboardPagesReady(List pages) { - onboarderAdapter = new OnboarderAdapter(pages, getSupportFragmentManager()); - vpOnboarderPager.setAdapter(onboarderAdapter); - colors = ColorsArrayBuilder.getPageBackgroundColors(this, pages); - circleIndicatorView.setPageIndicators(pages.size()); - } - - public void setInactiveIndicatorColor(int color) { - this.circleIndicatorView.setInactiveIndicatorColor(color); - } - - public void setActiveIndicatorColor(int color) { - this.circleIndicatorView.setActiveIndicatorColor(color); - } - - public void shouldDarkenButtonsLayout(boolean shouldDarkenButtonsLayout) { - this.shouldDarkenButtonsLayout = shouldDarkenButtonsLayout; - } - - public void setDividerColor(@ColorInt int color) { - if (!this.shouldDarkenButtonsLayout) - this.divider.setBackgroundColor(color); - } - - public void setDividerHeight(int heightInDp) { - if (!this.shouldDarkenButtonsLayout) - this.divider.getLayoutParams().height = - (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, heightInDp, - getResources().getDisplayMetrics()); - } - - public void setDividerVisibility(int dividerVisibility) { - this.divider.setVisibility(dividerVisibility); - } - - public void setSkipButtonTitle(CharSequence title) { - this.btnSkip.setText(title); - } - - public void setSkipButtonHidden() { - this.btnSkip.setVisibility(View.GONE); - } - - public void setSkipButtonTitle(@StringRes int titleResId) { - this.btnSkip.setText(titleResId); - } - - public void setFinishButtonTitle(CharSequence title) { - this.btnFinish.setText(title); - } - - public void setFinishButtonTitle(@StringRes int titleResId) { - this.btnFinish.setText(titleResId); - } - - public void shouldUseFloatingActionButton(boolean shouldUseFloatingActionButton) { - - this.shouldUseFloatingActionButton = shouldUseFloatingActionButton; - - if (shouldUseFloatingActionButton) { - this.fab.setVisibility(View.VISIBLE); - this.setDividerVisibility(View.GONE); - this.shouldDarkenButtonsLayout(false); - this.btnFinish.setVisibility(View.GONE); - this.btnSkip.setVisibility(View.GONE); - this.ibNext.setVisibility(View.GONE); - this.ibNext.setFocusable(false); - this.buttonsLayout.getLayoutParams().height = - (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 96, - getResources().getDisplayMetrics()); - } - - } - - private int darkenColor(@ColorInt int color) { - float[] hsv = new float[3]; - Color.colorToHSV(color, hsv); - hsv[2] *= 0.9f; - return Color.HSVToColor(hsv); - } - - public void setStatusBackgroundColor() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - getWindow().getDecorView().setSystemUiVisibility( - View.SYSTEM_UI_FLAG_LAYOUT_STABLE - | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN); - getWindow().setStatusBarColor(ContextCompat.getColor(this, R.color.black_transparent)); - } - } - - @Override - public void onClick(View v) { - int i = v.getId(); - boolean isInLastPage = vpOnboarderPager.getCurrentItem() == onboarderAdapter.getCount() - 1; - if (i == R.id.ib_next || i == R.id.fab && !isInLastPage) { - vpOnboarderPager.setCurrentItem(vpOnboarderPager.getCurrentItem() + 1); - } else if (i == R.id.btn_skip) { - onSkipButtonPressed(); - } else if (i == R.id.btn_finish || i == R.id.fab && isInLastPage) { - onFinishButtonPressed(); - } - } - - @Override - public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { - if(position < (onboarderAdapter.getCount() - 1) && position < (colors.length - 1)) { - vpOnboarderPager.setBackgroundColor((Integer) evaluator.evaluate(positionOffset, colors[position], colors[position + 1])); - if (shouldDarkenButtonsLayout) { - buttonsLayout.setBackgroundColor(darkenColor((Integer) evaluator.evaluate(positionOffset, colors[position], colors[position + 1]))); - divider.setVisibility(View.GONE); - } - } else { - vpOnboarderPager.setBackgroundColor(colors[colors.length - 1]); - if(shouldDarkenButtonsLayout) { - buttonsLayout.setBackgroundColor(darkenColor(colors[colors.length - 1])); - divider.setVisibility(View.GONE); - } - } - } - - @Override - public void onPageSelected(int position) { - int lastPagePosition = onboarderAdapter.getCount() - 1; - circleIndicatorView.setCurrentPage(position); - ibNext.setVisibility(position == lastPagePosition && !this.shouldUseFloatingActionButton ? View.GONE : View.VISIBLE); - btnFinish.setVisibility(position == lastPagePosition && !this.shouldUseFloatingActionButton ? View.VISIBLE : View.GONE); - if (this.shouldUseFloatingActionButton) - this.fab.setImageResource(position == lastPagePosition ? R.drawable.ic_done_white_24dp : R.drawable.ic_arrow_forward_white_24dp); - } - - @Override - public void onPageScrollStateChanged(int state) { - - } - - private void hideActionBar() { - if (getSupportActionBar() != null) { - getSupportActionBar().hide(); - } - } - - protected void onSkipButtonPressed() { - vpOnboarderPager.setCurrentItem(onboarderAdapter.getCount()); - } - - abstract public void onFinishButtonPressed(); - -} diff --git a/onboarder/src/main/java/com/chyrta/onboarder/OnboarderActivity.kt b/onboarder/src/main/java/com/chyrta/onboarder/OnboarderActivity.kt new file mode 100644 index 0000000..aa1bd89 --- /dev/null +++ b/onboarder/src/main/java/com/chyrta/onboarder/OnboarderActivity.kt @@ -0,0 +1,89 @@ +package com.chyrta.onboarder + +import android.animation.ArgbEvaluator +import android.os.Build +import android.os.Bundle +import android.support.v4.content.ContextCompat +import android.support.v4.view.ViewPager +import android.support.v7.app.AppCompatActivity +import android.view.View +import android.widget.Button +import com.chyrta.onboarder.utils.ColorsArrayBuilder +import com.rd.PageIndicatorView + +abstract class OnboarderActivity : AppCompatActivity(), View.OnClickListener, ViewPager.OnPageChangeListener { + + private var colors: Array? = null + private var pageIndicatorView: PageIndicatorView? = null + private var vpOnboarderPager: ViewPager? = null + private var onboarderAdapter: OnboarderAdapter? = null + private var evaluator: ArgbEvaluator? = null + private var btnGetStarted: Button? = null + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_onboarder) + setStatusBackgroundColor() + hideActionBar() + onboarderAdapter = OnboarderAdapter(supportFragmentManager) + btnGetStarted = findViewById