--------------------------------------------------------------------------------
/sample/src/main/java/com/fueled/flowr/sample/core/AbstractActivity.java:
--------------------------------------------------------------------------------
1 | package com.fueled.flowr.sample.core;
2 |
3 | import android.support.v4.content.ContextCompat;
4 |
5 | import com.fueled.flowr.AbstractFlowrActivity;
6 | import com.fueled.flowr.Flowr;
7 | import com.fueled.flowr.sample.R;
8 |
9 | public abstract class AbstractActivity extends AbstractFlowrActivity {
10 |
11 | @Override
12 | protected int getDefaultNavigationBarColor() {
13 | return ContextCompat.getColor(this, R.color.navigationBarColor);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/flowr-mock-classes/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'java'
2 |
3 | dependencies {
4 | compile fileTree(dir: 'libs', include: ['*.jar'])
5 | }
6 |
7 | task sourcesJar(type: Jar, dependsOn: classes) {
8 | classifier = 'sources'
9 | from sourceSets.main.allSource
10 | }
11 |
12 | task javadocJar(type: Jar, dependsOn: javadoc) {
13 | classifier = 'javadoc'
14 | from javadoc.destinationDir
15 | }
16 |
17 | artifacts {
18 | archives sourcesJar
19 | archives javadocJar
20 | }
21 | sourceCompatibility = "1.7"
22 | targetCompatibility = "1.7"
23 |
--------------------------------------------------------------------------------
/sample/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 | #000000
7 |
8 | #FFFFFF
9 |
10 | #673AB7
11 | #2196F3
12 | #009688
13 | #F44336
14 |
15 |
16 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/fragment_first.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
14 |
15 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/fragment_stack2.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
14 |
15 |
--------------------------------------------------------------------------------
/sample/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Built application files
2 | *.apk
3 | *.ap_
4 |
5 | # Files for the ART/Dalvik VM
6 | *.dex
7 |
8 | # Java class files
9 | *.class
10 |
11 | # Generated files
12 | bin/
13 | gen/
14 | out/
15 |
16 | # Gradle files
17 | .gradle/
18 | build/
19 |
20 | # Local configuration file (sdk path, etc)
21 | local.properties
22 |
23 | # Proguard folder generated by Eclipse
24 | proguard/
25 |
26 | # Log Files
27 | *.log
28 |
29 | # Android Studio Navigation editor temp files
30 | .navigation/
31 |
32 | # Android Studio captures folder
33 | captures/
34 |
35 | # Intellij
36 | *.iml
37 | .idea/*
38 | !.idea/codeStyleSettings.xml
39 |
40 | # Keystore files
41 | *.jks
42 |
--------------------------------------------------------------------------------
/flowr/src/main/java/com/fueled/flowr/ToolbarHandler.java:
--------------------------------------------------------------------------------
1 | package com.fueled.flowr;
2 |
3 | import android.graphics.drawable.Drawable;
4 | import android.view.View;
5 |
6 | /**
7 | * Created by hussein@fueled.com on 31/05/2016.
8 | * Copyright (c) 2016 Fueled. All rights reserved.
9 | */
10 | public interface ToolbarHandler {
11 |
12 | void setNavigationIcon(NavigationIconType navigationIconType);
13 |
14 | void setCustomNavigationIcon(Drawable navigationIcon);
15 |
16 | void setToolbarVisible(boolean visible);
17 |
18 | void setToolbarTitle(String title);
19 |
20 | void setToolbarNavigationButtonListener(View.OnClickListener onClickListener);
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/flowr/src/main/java/com/fueled/flowr/internal/FlowrDeepLinkHandler.java:
--------------------------------------------------------------------------------
1 | package com.fueled.flowr.internal;
2 |
3 | import android.content.Intent;
4 | import android.support.annotation.NonNull;
5 | import android.support.annotation.Nullable;
6 |
7 | /**
8 | * Created by julienFueled on 5/11/17.
9 | */
10 |
11 | public interface FlowrDeepLinkHandler {
12 |
13 | /**
14 | * Retrieve deep link info from an intent.
15 | *
16 | * @param intent the intent to retrieve the info from.
17 | * @return the deep link info if found for the specified intent, else null.
18 | */
19 | @Nullable
20 | FlowrDeepLinkInfo getDeepLinkInfoForIntent(@NonNull Intent intent);
21 | }
22 |
--------------------------------------------------------------------------------
/flowr/src/main/java/com/fueled/flowr/internal/FlowrDeepLinkInfo.java:
--------------------------------------------------------------------------------
1 | package com.fueled.flowr.internal;
2 |
3 | import android.os.Bundle;
4 | import android.support.v4.app.Fragment;
5 |
6 | import com.fueled.flowr.FlowrFragment;
7 |
8 | /**
9 | * Class that contains all the necessary info for Deep Links.
10 | * Created by julienFueled on 5/11/17.
11 | */
12 |
13 | public class FlowrDeepLinkInfo {
14 | public final Bundle data;
15 | public final Class extends T> fragment;
16 |
17 | public FlowrDeepLinkInfo(Bundle data, Class extends T> fragment) {
18 | this.data = data;
19 | this.fragment = fragment;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/extra/checkstyle/checkstyle_android_library.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'checkstyle'
2 |
3 | checkstyle {
4 | configFile rootProject.file('extra/checkstyle/checkstyle.xml')
5 | showViolations true
6 | }
7 |
8 | android.libraryVariants.all { variant ->
9 | def name = variant.buildType.name
10 |
11 | def checkstyle = project.tasks.create "checkstyle${name.capitalize()}", Checkstyle
12 | checkstyle.dependsOn variant.javaCompile
13 | checkstyle.source variant.javaCompile.source
14 | checkstyle.classpath = project.fileTree(variant.javaCompile.destinationDir)
15 | checkstyle.exclude('**/BuildConfig.java')
16 | checkstyle.exclude('**/R.java')
17 | project.tasks.getByName("check").dependsOn checkstyle
18 | }
--------------------------------------------------------------------------------
/sample/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 16dp
5 | 16dp
6 | 10dp
7 | 16dp
8 |
9 |
10 | 14sp
11 | 16sp
12 | 18sp
13 |
14 |
15 | 168dp
16 |
17 |
18 | 50dp
19 | 56dp
20 |
21 |
22 |
--------------------------------------------------------------------------------
/sample-library/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 | apply plugin: 'checkstyle'
3 | apply from: "../extra/checkstyle/checkstyle_android_library.gradle"
4 |
5 | android {
6 | compileSdkVersion compileSdk
7 | buildToolsVersion buildTools
8 |
9 | defaultConfig {
10 | minSdkVersion minSdk
11 | targetSdkVersion targetSdk
12 | }
13 |
14 | buildTypes {
15 | release {
16 | minifyEnabled false
17 | proguardFiles getDefaultProguardFile('proguard-android.txt')
18 | }
19 | }
20 | }
21 |
22 | dependencies {
23 | compile fileTree(dir: 'libs', include: ['*.jar'])
24 |
25 | compile project(':flowr')
26 | annotationProcessor project(':flowr-compiler')
27 |
28 | compile libraries.appCompat
29 | }
30 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | org.gradle.jvmargs=-Xmx1536m
13 |
14 | # When configured, Gradle will run in incubating parallel mode.
15 | # This option should only be used with decoupled projects. More details, visit
16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
17 | # org.gradle.parallel=true
18 |
--------------------------------------------------------------------------------
/flowr-annotations/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'java'
2 | apply plugin: 'com.github.dcendents.android-maven'
3 | apply plugin: 'checkstyle'
4 | apply from: "../extra/checkstyle/checkstyle_java_library.gradle"
5 |
6 | group = libraryGroup
7 | version = libraryVersion
8 |
9 | dependencies {
10 | compile fileTree(dir: 'libs', include: ['*.jar'])
11 | }
12 |
13 | task sourcesJar(type: Jar, dependsOn: classes) {
14 | classifier = 'sources'
15 | from sourceSets.main.allSource
16 | }
17 |
18 | task javadocJar(type: Jar, dependsOn: javadoc) {
19 | classifier = 'javadoc'
20 | from javadoc.destinationDir
21 | }
22 |
23 | artifacts {
24 | archives sourcesJar
25 | archives javadocJar
26 | }
27 | sourceCompatibility = JavaVersion.VERSION_1_7
28 | targetCompatibility = JavaVersion.VERSION_1_7
29 |
--------------------------------------------------------------------------------
/extra/checkstyle/checkstyle.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'checkstyle'
2 |
3 | checkstyle {
4 | configFile rootProject.file('extra/checkstyle/checkstyle.xml')
5 | showViolations true
6 | }
7 |
8 | android.applicationVariants.all { variant ->
9 | def name = variant.buildType.name
10 |
11 | def checkstyle = project.tasks.create "checkstyle${name.capitalize()}", Checkstyle
12 | checkstyle.dependsOn variant.javaCompile
13 | checkstyle.source variant.javaCompile.source
14 | checkstyle.classpath = project.fileTree(variant.javaCompile.destinationDir)
15 | checkstyle.exclude('**/BuildConfig.java')
16 | checkstyle.exclude('**/R.java')
17 | checkstyle.exclude('**/Manifest.java')
18 | checkstyle.exclude('**/DataBindingInfo.java')
19 | project.tasks.getByName("check").dependsOn checkstyle
20 | }
--------------------------------------------------------------------------------
/sample/src/main/res/layout/layout_drawer_header.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
16 |
--------------------------------------------------------------------------------
/flowr-annotations/src/main/java/com/fueled/flowr/annotations/DeepLinkHandler.java:
--------------------------------------------------------------------------------
1 | package com.fueled.flowr.annotations;
2 |
3 | import java.lang.annotation.ElementType;
4 | import java.lang.annotation.Retention;
5 | import java.lang.annotation.RetentionPolicy;
6 | import java.lang.annotation.Target;
7 |
8 | /**
9 | * Created by hussein@fueled.com on 05/06/2017.
10 | * Copyright (c) 2017 Fueled. All rights reserved.
11 | */
12 | @Retention(RetentionPolicy.SOURCE)
13 | @Target(ElementType.TYPE)
14 | public @interface DeepLinkHandler {
15 |
16 | /**
17 | * The name of the generated deep link handler class, by default this will be the name
18 | * of the class annotated with "Impl" appended at the end.
19 | *
20 | * @return the name to be used for the generated class.
21 | */
22 | String value() default "";
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/sample/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in F:\android-sdk\sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
19 | # Uncomment this to preserve the line number information for
20 | # debugging stack traces.
21 | #-keepattributes SourceFile,LineNumberTable
22 |
23 | # If you keep the line number information, uncomment this to
24 | # hide the original source file name.
25 | #-renamesourcefileattribute SourceFile
26 |
--------------------------------------------------------------------------------
/sample-library/src/main/java/com/fueled/flowr/sample/library/LinkFragment.java:
--------------------------------------------------------------------------------
1 | package com.fueled.flowr.sample.library;
2 |
3 | import android.os.Bundle;
4 | import android.support.annotation.Nullable;
5 | import android.view.LayoutInflater;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 |
9 | import com.fueled.flowr.AbstractFlowrFragment;
10 | import com.fueled.flowr.annotations.DeepLink;
11 |
12 | /**
13 | * Created by hussein@fueled.com on 05/06/2017.
14 | * Copyright (c) 2017 Fueled. All rights reserved.
15 | */
16 | @DeepLink("/hello")
17 | public class LinkFragment extends AbstractFlowrFragment {
18 |
19 | @Nullable
20 | @Override
21 | public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
22 | @Nullable Bundle savedInstanceState) {
23 | return inflater.inflate(R.layout.fragment_link, container, false);
24 | }
25 |
26 | @Override
27 | public String getTitle() {
28 | return "Sample Library Link Fragment";
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/flowr-annotations/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/julienFueled/Library/Android/sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
19 | # Uncomment this to preserve the line number information for
20 | # debugging stack traces.
21 | #-keepattributes SourceFile,LineNumberTable
22 |
23 | # If you keep the line number information, uncomment this to
24 | # hide the original source file name.
25 | #-renamesourcefileattribute SourceFile
26 |
--------------------------------------------------------------------------------
/sample/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | FlowR Sample
3 |
4 |
5 | Flowr User
6 | Home
7 | Categories
8 |
9 |
10 | Open View Fragment
11 | Open Link Fragment
12 | Open First Fragment
13 |
14 |
15 | Categories Fragment!
16 | Open Second Fragment
17 | Return to Home Fragment
18 |
19 |
20 | Pick Navigation Bar Color
21 | Toolbar Visible
22 | Drawer Enabled
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/sample/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 | apply plugin: 'checkstyle'
3 | apply from: "../extra/checkstyle/checkstyle.gradle"
4 |
5 | android {
6 | compileSdkVersion compileSdk
7 | buildToolsVersion buildTools
8 |
9 | defaultConfig {
10 | applicationId appId
11 | minSdkVersion minSdk
12 | targetSdkVersion targetSdk
13 | versionCode 1
14 | versionName "1.0"
15 |
16 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
17 |
18 | }
19 | buildTypes {
20 | release {
21 | minifyEnabled false
22 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
23 | }
24 | }
25 |
26 | dataBinding {
27 | enabled = true
28 | }
29 | }
30 |
31 | dependencies {
32 | compile fileTree(dir: 'libs', include: ['*.jar'])
33 |
34 | compile project(':flowr')
35 | annotationProcessor project(':flowr-compiler')
36 |
37 | compile project(':sample-library')
38 |
39 | compile libraries.appCompat
40 | compile libraries.designSupport
41 |
42 | compile libraries.rxJava
43 | compile libraries.rxAndroid
44 |
45 | testCompile testLibraries.junit
46 | }
47 |
--------------------------------------------------------------------------------
/sample/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/fueled/flowr/sample/FirstFragment.java:
--------------------------------------------------------------------------------
1 | package com.fueled.flowr.sample;
2 |
3 | import android.view.View;
4 |
5 | import com.fueled.flowr.NavigationIconType;
6 | import com.fueled.flowr.annotations.DeepLink;
7 | import com.fueled.flowr.sample.core.AbstractFragment;
8 |
9 | /**
10 | * Created by hussein@fueled.com on 18/05/2017.
11 | * Copyright (c) 2017 Fueled. All rights reserved.
12 | */
13 |
14 | @DeepLink(value = "/first")
15 | public class FirstFragment extends AbstractFragment implements View.OnClickListener {
16 |
17 | @Override
18 | public int getLayoutId() {
19 | return R.layout.fragment_first;
20 | }
21 |
22 | @Override
23 | protected void setupView(View view) {
24 | view.findViewById(R.id.add_stack_button).setOnClickListener(this);
25 | }
26 |
27 | @Override
28 | public String getTitle() {
29 | return "First Fragment";
30 | }
31 |
32 | @Override
33 | public NavigationIconType getNavigationIconType() {
34 | return NavigationIconType.HAMBURGER;
35 | }
36 |
37 | @Override
38 | public void onClick(View view) {
39 | getFlowr().open("/second")
40 | .displayFragmentForResults(HomeFragment.targetFragmentId, HomeFragment.RC_STACK);
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/fragment_home.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
11 |
12 |
17 |
18 |
24 |
25 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/flowr-compiler/build.gradle:
--------------------------------------------------------------------------------
1 | import org.gradle.internal.jvm.Jvm
2 |
3 | apply plugin: 'java'
4 | apply plugin: 'com.github.dcendents.android-maven'
5 | apply plugin: 'checkstyle'
6 | apply from: "../extra/checkstyle/checkstyle_java_library.gradle"
7 |
8 | group = libraryGroup
9 | version = libraryVersion
10 |
11 | dependencies {
12 | compile fileTree(dir: 'libs', include: ['*.jar'])
13 | compile project(path: ':flowr-annotations')
14 |
15 | compile libraries.javaPoet
16 | compile libraries.autoCommon
17 | compile libraries.autoService
18 |
19 | testCompile project(':flowr-mock-classes')
20 | testCompile testLibraries.junit
21 | testCompile testLibraries.hamcrest
22 | testCompile testLibraries.junit
23 | testCompile testLibraries.mockito
24 | testCompile testLibraries.compileTesting
25 | testCompile testLibraries.googleTruth
26 | testCompile files(Jvm.current().getToolsJar())
27 | }
28 |
29 | task sourcesJar(type: Jar, dependsOn: classes) {
30 | classifier = 'sources'
31 | from sourceSets.main.allSource
32 | }
33 |
34 | task javadocJar(type: Jar, dependsOn: javadoc) {
35 | classifier = 'javadoc'
36 | from javadoc.destinationDir
37 | }
38 |
39 | artifacts {
40 | archives sourcesJar
41 | archives javadocJar
42 | }
43 |
44 | sourceCompatibility = JavaVersion.VERSION_1_7
45 | targetCompatibility = JavaVersion.VERSION_1_7
46 |
47 |
--------------------------------------------------------------------------------
/flowr/src/main/java/com/fueled/flowr/FlowrScreen.java:
--------------------------------------------------------------------------------
1 | package com.fueled.flowr;
2 |
3 | import android.support.annotation.ColorInt;
4 | import android.support.annotation.Nullable;
5 | import android.support.v4.app.Fragment;
6 | import android.support.v4.app.FragmentManager;
7 |
8 | /**
9 | * Created by hussein@fueled.com on 31/05/2016.
10 | * Copyright (c) 2016 Fueled. All rights reserved.
11 | */
12 |
13 | public interface FlowrScreen {
14 |
15 | /**
16 | * Return the FragmentManager for interacting with fragments associated
17 | * with this screen.
18 | */
19 | FragmentManager getScreenFragmentManager();
20 |
21 | /**
22 | * Invoke the {@code onBackPressed} method for this screen.
23 | */
24 | void invokeOnBackPressed();
25 |
26 | /**
27 | * Indicates that the fragment currently being displayed has changed.
28 | *
29 | * @param currentFragment the fragment currently displayed or {@code null} if no fragment
30 | * is being displayed.
31 | */
32 | void onCurrentFragmentChanged(@Nullable Fragment currentFragment);
33 |
34 | /**
35 | * Specify the screen orientation to be used for this screen.
36 | *
37 | * @param orientation the screen orientation to be used.
38 | */
39 | void setScreenOrientation(int orientation);
40 |
41 | /**
42 | * Specify the navigation bar color to be used for this screen.
43 | *
44 | * @param color the navigation bar color to be used.
45 | */
46 | void setNavigationBarColor(@ColorInt int color);
47 | }
48 |
--------------------------------------------------------------------------------
/flowr/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 | apply plugin: 'checkstyle'
3 | apply from: "../extra/checkstyle/checkstyle_android_library.gradle"
4 | apply plugin: 'com.github.dcendents.android-maven'
5 |
6 | group = libraryGroup
7 | version = libraryVersion
8 |
9 | android {
10 | compileSdkVersion compileSdk
11 | buildToolsVersion buildTools
12 |
13 | defaultConfig {
14 | minSdkVersion minSdk
15 | targetSdkVersion targetSdk
16 | }
17 |
18 | buildTypes {
19 | release {
20 | minifyEnabled false
21 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
22 | }
23 | }
24 | }
25 |
26 | dependencies {
27 | compile fileTree(dir: 'libs', include: ['*.jar'])
28 |
29 | compile libraries.appCompat
30 |
31 | compile project(':flowr-annotations')
32 | // Test
33 | testCompile testLibraries.hamcrest
34 | testCompile testLibraries.junit
35 | testCompile testLibraries.mockito
36 | }
37 |
38 | // build a jar with source files
39 | task sourcesJar(type: Jar) {
40 | from android.sourceSets.main.java.srcDirs
41 | classifier = 'sources'
42 | }
43 |
44 | task javadoc(type: Javadoc) {
45 | failOnError false
46 | source = android.sourceSets.main.java.sourceFiles
47 | classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
48 | classpath += configurations.compile
49 | }
50 |
51 | // build a jar with javadoc
52 | task javadocJar(type: Jar, dependsOn: javadoc) {
53 | classifier = 'javadoc'
54 | from javadoc.destinationDir
55 | }
56 |
57 | artifacts {
58 | archives sourcesJar
59 | archives javadocJar
60 | }
61 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/fueled/flowr/sample/SecondFragment.java:
--------------------------------------------------------------------------------
1 | package com.fueled.flowr.sample;
2 |
3 | import android.app.Activity;
4 | import android.os.Bundle;
5 | import android.support.annotation.NonNull;
6 | import android.view.View;
7 |
8 | import com.fueled.flowr.Flowr;
9 | import com.fueled.flowr.NavigationIconType;
10 | import com.fueled.flowr.annotations.DeepLink;
11 | import com.fueled.flowr.sample.core.AbstractFragment;
12 |
13 | /**
14 | * Created by hussein@fueled.com on 18/05/2017.
15 | * Copyright (c) 2017 Fueled. All rights reserved.
16 | */
17 |
18 | @DeepLink(value = "/second")
19 | public class SecondFragment extends AbstractFragment implements View.OnClickListener {
20 |
21 | public static final String RESULT_FROM_SECOND = "RESULT_FROM_SECOND";
22 |
23 | @Override
24 | public int getLayoutId() {
25 | return R.layout.fragment_stack2;
26 | }
27 |
28 | @Override
29 | protected void setupView(View view) {
30 | view.findViewById(R.id.return_home_button).setOnClickListener(this);
31 | }
32 |
33 | @Override
34 | public String getTitle() {
35 | return "Second Fragment";
36 | }
37 |
38 | @Override
39 | public NavigationIconType getNavigationIconType() {
40 | return NavigationIconType.HAMBURGER;
41 | }
42 |
43 | @Override
44 | public void onClick(View view) {
45 | getFlowr().closeUptoWithResults(
46 | Flowr.getResultsResponse(getArguments(), Activity.RESULT_OK, getBundle()),
47 | HomeFragment.backStackIdentifier);
48 | }
49 |
50 | @NonNull
51 | private Bundle getBundle() {
52 | Bundle args = new Bundle();
53 | args.putString(RESULT_FROM_SECOND, "Wow! Whata Stack!");
54 | return args;
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/fueled/flowr/sample/core/FragmentResultPublisherImpl.java:
--------------------------------------------------------------------------------
1 | package com.fueled.flowr.sample.core;
2 |
3 | import com.fueled.flowr.FragmentsResultPublisher;
4 | import com.fueled.flowr.ResultResponse;
5 |
6 | import io.reactivex.android.schedulers.AndroidSchedulers;
7 | import io.reactivex.disposables.Disposable;
8 | import io.reactivex.functions.Consumer;
9 | import io.reactivex.functions.Predicate;
10 | import io.reactivex.subjects.PublishSubject;
11 |
12 | /**
13 | * Created by hussein@fueled.com on 13/02/2017.
14 | * Copyright (c) 2017 Fueled. All rights reserved.
15 | */
16 | public class FragmentResultPublisherImpl implements FragmentsResultPublisher {
17 |
18 | private static FragmentResultPublisherImpl instance;
19 |
20 | private PublishSubject publishSubject;
21 |
22 | private FragmentResultPublisherImpl() {
23 | publishSubject = PublishSubject.create();
24 | }
25 |
26 | @Override
27 | public void publishResult(ResultResponse resultResponse) {
28 | publishSubject.onNext(resultResponse);
29 | }
30 |
31 | public Disposable observeResultsForFragment(final String fragmentId, Consumer consumer) {
32 | return publishSubject
33 | .filter(new Predicate() {
34 | @Override
35 | public boolean test(ResultResponse resultResponse) throws Exception {
36 | return resultResponse.fragmentId.equals(fragmentId);
37 | }
38 | })
39 | .observeOn(AndroidSchedulers.mainThread())
40 | .subscribe(consumer, new Consumer() {
41 | @Override
42 | public void accept(Throwable throwable) throws Exception {
43 | throwable.printStackTrace();
44 | }
45 | });
46 | }
47 |
48 | public static FragmentResultPublisherImpl getInstance() {
49 | if (instance == null) {
50 | instance = new FragmentResultPublisherImpl();
51 | }
52 |
53 | return instance;
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
11 |
12 |
15 |
16 |
20 |
21 |
27 |
28 |
29 |
30 |
35 |
36 |
37 |
38 |
39 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/extra/gradle/libraries.gradle:
--------------------------------------------------------------------------------
1 | ext {
2 |
3 | //app
4 | compileSdk = 27
5 | buildTools = "27.0.3"
6 | minSdk = 16
7 | targetSdk = 27
8 | appId = "com.fueled.flowr.sample"
9 |
10 | //library
11 | libraryGroup = 'com.github.fueled'
12 | libraryVersion = '1.5.2'
13 |
14 | //android libraries
15 | supportVersion = '27.1.1'
16 |
17 | // Rx
18 | rxJavaVersion = '2.0.4'
19 | rxAndroidVersion = '2.0.1'
20 |
21 | // dagger
22 | daggerVersion = '2.8'
23 | jsr250Version = "1.0"
24 |
25 | //test libraries
26 | junitVersion = '4.12'
27 | tesetRunnerVersion = '0.5'
28 | hamcrestVersion = '1.4-atlassian-1'
29 | espressoVersion = '2.2.2'
30 | mockitoVersion = '1.10.19'
31 | compileTestingVersion = '0.10'
32 | googleTruthVersion = '0.30'
33 |
34 | javaPoetVersion = '1.8.0'
35 | autoCommon = '0.8'
36 | autoService = '1.0-rc3'
37 |
38 |
39 | libraries = [
40 | appCompat : "com.android.support:appcompat-v7:${supportVersion}",
41 | designSupport: "com.android.support:design:${supportVersion}",
42 | vectorSupport: "com.android.support:support-vector-drawable:${supportVersion}",
43 |
44 | rxJava : "io.reactivex.rxjava2:rxjava:${rxJavaVersion}",
45 | rxAndroid : "io.reactivex.rxjava2:rxandroid:${rxAndroidVersion}",
46 | javaPoet : "com.squareup:javapoet:${javaPoetVersion}",
47 |
48 | autoCommon : "com.google.auto:auto-common:${autoCommon}",
49 | autoService : "com.google.auto.service:auto-service:${autoService}"
50 | ]
51 |
52 | testLibraries = [
53 | junit : "junit:junit:${junitVersion}",
54 | supportAnnotations: "com.android.support:support-annotations:${supportVersion}",
55 | testRunner : "com.android.support.test:runner:${tesetRunnerVersion}",
56 | testRule : "com.android.support.test:rules:${tesetRunnerVersion}",
57 | hamcrest : "org.hamcrest:hamcrest-library:${hamcrestVersion}",
58 | espresso : "com.android.support.test.espresso:espresso-core:${espressoVersion}",
59 | mockito : "org.mockito:mockito-core:${mockitoVersion}",
60 | compileTesting : "com.google.testing.compile:compile-testing:${compileTestingVersion}",
61 | googleTruth : "com.google.truth:truth:${googleTruthVersion}"
62 | ]
63 |
64 | }
65 |
--------------------------------------------------------------------------------
/flowr-compiler/src/test/java/com/fueled/flowr/compilers/DeepLinkAnnotationCompilerTest.java:
--------------------------------------------------------------------------------
1 | package com.fueled.flowr.compilers;
2 |
3 | import com.google.testing.compile.JavaFileObjects;
4 |
5 | import org.junit.Test;
6 |
7 | import java.util.Arrays;
8 |
9 | import javax.tools.JavaFileObject;
10 |
11 | import static com.google.common.truth.Truth.assertAbout;
12 | import static com.google.testing.compile.JavaSourcesSubjectFactory.javaSources;
13 |
14 | /**
15 | * Created by julienFueled on 5/25/17.
16 | */
17 | public class DeepLinkAnnotationCompilerTest {
18 |
19 | private static final String TEST_PACKAGE = "com.fueled.flowr.sample";
20 |
21 | private static final JavaFileObject TEST_DEEP_LINK_FRAGMENT = JavaFileObjects
22 | .forSourceString("HomeFragment", "package " + TEST_PACKAGE + ";\n" +
23 | "import com.fueled.flowr.AbstractFlowrFragment;\n" +
24 | "import com.fueled.flowr.annotations.DeepLink;\n" +
25 | "@DeepLink(value = {\"/test\"})\n" +
26 | "public class HomeFragment extends AbstractFlowrFragment {\n" +
27 | "}");
28 |
29 | private static final JavaFileObject TEST_DEEP_LINK_HANDLER = JavaFileObjects
30 | .forSourceString("TestDeepLinkHandler", "package " + TEST_PACKAGE + ";\n" +
31 | "import com.fueled.flowr.annotations.DeepLinkHandler;\n" +
32 | "@DeepLinkHandler\n" +
33 | "public interface TestDeepLinkHandler {\n" +
34 | "}");
35 |
36 | private static final JavaFileObject TEST_DEEP_GENERATED_HANDLER = JavaFileObjects
37 | .forSourceString("TestDeepLinkHandlerImpl", "package com.fueled.flowr.sample;\n" +
38 | "import com.fueled.flowr.internal.AbstractFlowrDeepLinkHandler;\n" +
39 | "public final class TestDeepLinkHandlerImpl extends AbstractFlowrDeepLinkHandler {\n" +
40 | " public TestDeepLinkHandlerImpl() {\n" +
41 | " addFragment(\"/test\", HomeFragment.class);\n" +
42 | " }\n" +
43 | "}");
44 |
45 |
46 | @Test
47 | public void process() throws Exception {
48 | assertAbout(javaSources())
49 | .that(Arrays.asList(TEST_DEEP_LINK_FRAGMENT, TEST_DEEP_LINK_HANDLER))
50 | .processedWith(new DeepLinkAnnotationCompiler())
51 | .compilesWithoutError()
52 | .and()
53 | .generatesSources(TEST_DEEP_GENERATED_HANDLER);
54 | }
55 |
56 | }
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/fueled/flowr/sample/core/AbstractFragment.java:
--------------------------------------------------------------------------------
1 | package com.fueled.flowr.sample.core;
2 |
3 | import android.os.Bundle;
4 | import android.support.annotation.LayoutRes;
5 | import android.view.LayoutInflater;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 |
9 | import com.fueled.flowr.AbstractFlowrFragment;
10 | import com.fueled.flowr.ResultResponse;
11 | import com.fueled.flowr.Flowr;
12 | import com.fueled.flowr.sample.R;
13 |
14 | import io.reactivex.disposables.Disposable;
15 | import io.reactivex.functions.Consumer;
16 |
17 | /**
18 | * Created by hussein@fueled.com on 13/02/2017.
19 | * Copyright (c) 2017 Fueled. All rights reserved.
20 | */
21 | public abstract class AbstractFragment extends AbstractFlowrFragment {
22 |
23 | private Disposable fragmentResultSubscription;
24 |
25 | private String title;
26 |
27 | public abstract @LayoutRes int getLayoutId();
28 |
29 | protected abstract void setupView(View view);
30 |
31 | @Override
32 | public View onCreateView(LayoutInflater inflater, ViewGroup container,
33 | Bundle savedInstanceState) {
34 | View view = inflater.inflate(getLayoutId(), container, false);
35 | view.setClickable(true);
36 | setupView(view);
37 | return view;
38 | }
39 |
40 | @Override
41 | @SuppressWarnings("unchecked")
42 | public void onActivityCreated(Bundle savedInstanceState) {
43 | super.onActivityCreated(savedInstanceState);
44 | listenToResults();
45 |
46 | title = getString(R.string.app_name);
47 | getFlowr().syncScreenState();
48 | }
49 |
50 | protected Flowr getFlowr() {
51 | if (getActivity() != null && getActivity() instanceof AbstractActivity) {
52 | return ((AbstractActivity) getActivity()).getFlowr();
53 | }
54 |
55 | return null;
56 | }
57 |
58 | private void listenToResults() {
59 | removeFragmentResultSubscription();
60 | fragmentResultSubscription = FragmentResultPublisherImpl.getInstance()
61 | .observeResultsForFragment(getFragmentId(), new Consumer() {
62 | @Override
63 | public void accept(ResultResponse resultResponse) throws Exception {
64 | onFragmentResults(resultResponse.requestCode, resultResponse.resultCode,
65 | resultResponse.data);
66 | }
67 | });
68 | }
69 |
70 | private void removeFragmentResultSubscription() {
71 | if (fragmentResultSubscription != null && !fragmentResultSubscription.isDisposed()) {
72 | fragmentResultSubscription.dispose();
73 | }
74 | }
75 |
76 | @Override
77 | public String getTitle() {
78 | return title;
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/flowr/src/main/java/com/fueled/flowr/AbstractFlowrActivity.java:
--------------------------------------------------------------------------------
1 | package com.fueled.flowr;
2 |
3 | import android.content.pm.ActivityInfo;
4 | import android.graphics.Color;
5 | import android.os.Build;
6 | import android.os.Bundle;
7 | import android.support.annotation.ColorInt;
8 | import android.support.annotation.Nullable;
9 | import android.support.v4.app.Fragment;
10 | import android.support.v4.app.FragmentManager;
11 | import android.support.v7.app.AppCompatActivity;
12 |
13 | /**
14 | * Created by hussein@fueled.com on 13/02/2017.
15 | * Copyright (c) 2017 Fueled. All rights reserved.
16 | */
17 | public abstract class AbstractFlowrActivity extends AppCompatActivity
18 | implements FlowrScreen {
19 |
20 | public abstract T getFlowr();
21 |
22 | @Override
23 | public void onPostCreate(@Nullable Bundle savedInstanceState) {
24 | super.onPostCreate(savedInstanceState);
25 |
26 | if (getFlowr() != null) {
27 | getFlowr().syncScreenState();
28 | }
29 | }
30 |
31 | @Override
32 | protected void onDestroy() {
33 | super.onDestroy();
34 |
35 | if (getFlowr() != null) {
36 | getFlowr().onDestroy();
37 | }
38 | }
39 |
40 | @Override
41 | public FragmentManager getScreenFragmentManager() {
42 | return getSupportFragmentManager();
43 | }
44 |
45 | @Override
46 | public void invokeOnBackPressed() {
47 | onBackPressed();
48 | }
49 |
50 | @Override
51 | public void onBackPressed() {
52 | if (getFlowr() == null || !getFlowr().onBackPressed()) {
53 | super.onBackPressed();
54 | }
55 | }
56 |
57 | protected int getDefaultOrientation() {
58 | return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
59 | }
60 |
61 | protected int getDefaultNavigationBarColor() {
62 | return Color.BLACK;
63 | }
64 |
65 | @Override
66 | public void onCurrentFragmentChanged(@Nullable Fragment currentFragment) {
67 | // Do Nothing. No Default implementation is required.
68 | }
69 |
70 | @SuppressWarnings("WrongConstant")
71 | @Override
72 | public void setScreenOrientation(int orientation) {
73 | // if the orientation is unspecified we set it to activity default
74 | if (orientation == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) {
75 | setRequestedOrientation(getDefaultOrientation());
76 | } else {
77 | setRequestedOrientation(orientation);
78 | }
79 | }
80 |
81 | @Override
82 | public void setNavigationBarColor(@ColorInt int color) {
83 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
84 | if (color == -1) {
85 | getWindow().setNavigationBarColor(getDefaultNavigationBarColor());
86 | } else {
87 | getWindow().setNavigationBarColor(color);
88 | }
89 | }
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/fueled/flowr/sample/HomeFragment.java:
--------------------------------------------------------------------------------
1 | package com.fueled.flowr.sample;
2 |
3 | import android.databinding.DataBindingUtil;
4 | import android.os.Bundle;
5 | import android.view.View;
6 | import android.widget.Toast;
7 |
8 | import com.fueled.flowr.NavigationIconType;
9 | import com.fueled.flowr.sample.core.AbstractFragment;
10 | import com.fueled.flowr.sample.databinding.FragmentHomeBinding;
11 |
12 | /**
13 | * Created by hussein@fueled.com on 13/02/2017.
14 | * Copyright (c) 2017 Fueled. All rights reserved.
15 | */
16 | public class HomeFragment extends AbstractFragment implements View.OnClickListener {
17 |
18 | public static final int RC_STACK = 101;
19 | public static int backStackIdentifier;
20 | public static String targetFragmentId;
21 |
22 | private FragmentHomeBinding binding;
23 |
24 | @Override
25 | public int getLayoutId() {
26 | return R.layout.fragment_home;
27 | }
28 |
29 | @Override
30 | protected void setupView(View view) {
31 | binding = DataBindingUtil.bind(view);
32 | binding.homeOpenViewButton.setOnClickListener(this);
33 | binding.homeOpenLinkButton.setOnClickListener(this);
34 | binding.homeOpenFirstButton.setOnClickListener(this);
35 | }
36 |
37 | @Override
38 | public boolean onNavigationIconClick() {
39 | return true;
40 | }
41 |
42 | @Override
43 | public NavigationIconType getNavigationIconType() {
44 | return NavigationIconType.HAMBURGER;
45 | }
46 |
47 | @Override
48 | public void onClick(View view) {
49 | if (view.getId() == R.id.home_open_view_button) {
50 | displayViewFragment();
51 | } else if (view.getId() == R.id.home_open_link_button) {
52 | displayLinkFragment();
53 | } else if (view.getId() == R.id.home_open_first_button) {
54 | displayFirstFragment();
55 | } else {
56 |
57 | }
58 | }
59 |
60 | private void displayViewFragment() {
61 | getFlowr().open("/m/From%20HomeFragment")
62 | .setCustomTransactionAnimation(android.R.anim.fade_in, android.R.anim.fade_out,
63 | android.R.anim.slide_in_left, android.R.anim.slide_out_right)
64 | .displayFragment();
65 | }
66 |
67 | private void displayLinkFragment() {
68 | getFlowr().open("/hello")
69 | .displayFragment();
70 | }
71 |
72 | private void displayFirstFragment() {
73 | targetFragmentId = getFragmentId();
74 | backStackIdentifier = getFlowr().open("/first")
75 | .displayFragmentForResults(targetFragmentId, RC_STACK);
76 | }
77 |
78 | @Override
79 | protected void onFragmentResults(int requestCode, int resultCode, Bundle data) {
80 | super.onFragmentResults(requestCode, resultCode, data);
81 | if (requestCode == RC_STACK && data.containsKey(SecondFragment.RESULT_FROM_SECOND)) {
82 | String resultFromStack = data.getString(SecondFragment.RESULT_FROM_SECOND);
83 | Toast.makeText(getContext(), resultFromStack, Toast.LENGTH_SHORT).show();
84 | }
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
6 |
7 | ## Our Standards
8 |
9 | Examples of behavior that contributes to creating a positive environment include:
10 |
11 | * Using welcoming and inclusive language
12 | * Being respectful of differing viewpoints and experiences
13 | * Gracefully accepting constructive criticism
14 | * Focusing on what is best for the community
15 | * Showing empathy towards other community members
16 |
17 | Examples of unacceptable behavior by participants include:
18 |
19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances
20 | * Trolling, insulting/derogatory comments, and personal or political attacks
21 | * Public or private harassment
22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission
23 | * Other conduct which could reasonably be considered inappropriate in a professional setting
24 |
25 | ## Our Responsibilities
26 |
27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
28 |
29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
30 |
31 | ## Scope
32 |
33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
34 |
35 | ## Enforcement
36 |
37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at @julien-fueled. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
38 |
39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
40 |
41 | ## Attribution
42 |
43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
44 |
45 | [homepage]: http://contributor-covenant.org
46 | [version]: http://contributor-covenant.org/version/1/4/
47 |
--------------------------------------------------------------------------------
/flowr/src/main/java/com/fueled/flowr/FlowrFragment.java:
--------------------------------------------------------------------------------
1 | package com.fueled.flowr;
2 |
3 | import android.graphics.drawable.Drawable;
4 | import android.support.annotation.ColorInt;
5 |
6 | /**
7 | * Created by hussein@fueled.com on 09/02/2017.
8 | * Copyright (c) 2017 Fueled. All rights reserved.
9 | */
10 | public interface FlowrFragment {
11 |
12 | /**
13 | * Called when the back button has been pressed. Override this method
14 | * if the fragment needs to handle back button presses
15 | *
16 | * @return true if the event has been handled by the fragment
17 | */
18 | boolean onBackPressed();
19 |
20 | /**
21 | * Override this method if this fragment should handle navigation icon clicks
22 | *
23 | * @return true if the click was handled by the fragment
24 | */
25 | boolean onNavigationIconClick();
26 |
27 | /**
28 | * Returns the type of the navigation icon that should be show in the current toolbar
29 | *
30 | * Note: when the returned navigation icon type is {@link NavigationIconType.CUSTOM}
31 | * you must also override {@link #getNavigationIcon()}
32 | *
33 | * @return the navigation icon that should be shown
34 | */
35 | NavigationIconType getNavigationIconType();
36 |
37 | /**
38 | * Returns the drawable to be used for the navigation icon when the navigation icon type
39 | * is {@link NavigationIconType.CUSTOM}
40 | *
41 | * @return the drawable for the navigation icon
42 | */
43 | Drawable getNavigationIcon();
44 |
45 | /**
46 | * Specify whether the toolbar should be visible or not for this fragment,
47 | * this option is only available if the container screen has a toolbar and the router
48 | * was provided with a {@link ToolbarHandler} instance.
49 | *
50 | * @return true if the toolbar should be visible for this fragment
51 | */
52 | boolean isToolbarVisible();
53 |
54 | /**
55 | * Specify whether the drawer should be enabled or not for this fragment,
56 | * this option is only available if the container screen has a drawer and the router
57 | * was provided with a {@link DrawerHandler} instance.
58 | *
59 | * @return true if the drawer should be enabled for this fragment
60 | */
61 | boolean isDrawerEnabled();
62 |
63 | /**
64 | * Returns the screen orientation to be used while this fragment is visible,
65 | * return {@link android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED} to use the activity
66 | * default orientation
67 | *
68 | * @return the screen orientation to be used for this fragment
69 | */
70 | int getScreenOrientation();
71 |
72 | /**
73 | * Returns the color to be used for the navigation bar while this fragment is visible, return
74 | * -1 to use the activity default color
75 | *
76 | * @return the color to be used for the navigation bar
77 | */
78 | @ColorInt int getNavigationBarColor();
79 |
80 | /**
81 | * Returns the title to be used for this fragment, return null to use the activity default title.
82 | *
83 | * @return the title to be used for this fragment
84 | */
85 | String getTitle();
86 |
87 | /**
88 | * Called when the fragment is added or returned to the top of the back stack.
89 | */
90 | void onShown();
91 |
92 | /**
93 | * Called when the fragment has been removed from the top of the back stack.
94 | */
95 | void onHidden();
96 | }
97 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/fueled/flowr/sample/ViewFragment.java:
--------------------------------------------------------------------------------
1 | package com.fueled.flowr.sample;
2 |
3 | import android.databinding.DataBindingUtil;
4 | import android.os.Bundle;
5 | import android.support.annotation.ColorRes;
6 | import android.support.v4.content.ContextCompat;
7 | import android.view.View;
8 | import android.widget.CompoundButton;
9 |
10 | import com.fueled.flowr.annotations.DeepLink;
11 | import com.fueled.flowr.sample.core.AbstractFragment;
12 | import com.fueled.flowr.sample.databinding.FragmentViewBinding;
13 |
14 | /**
15 | * Created by hussein@fueled.com on 14/02/2017.
16 | * Copyright (c) 2017 Fueled. All rights reserved.
17 | */
18 | @DeepLink(value = {"/m/{message}", "/test2"})
19 | public class ViewFragment extends AbstractFragment implements View.OnClickListener,
20 | CompoundButton.OnCheckedChangeListener {
21 |
22 | private FragmentViewBinding binding;
23 |
24 | private int navBarColor = -1;
25 | private boolean isToolbarVisible = true;
26 | private boolean isDrawerEnabled = true;
27 |
28 | @Override
29 | public int getLayoutId() {
30 | return R.layout.fragment_view;
31 | }
32 |
33 | @Override
34 | protected void setupView(View view) {
35 | binding = DataBindingUtil.bind(view);
36 |
37 | binding.setClickListener(this);
38 | binding.drawerSwitch.setOnCheckedChangeListener(this);
39 | binding.toolbarSwitch.setOnCheckedChangeListener(this);
40 | Bundle data = getArguments();
41 | if (data != null && data.containsKey("message")) {
42 | binding.setMessage(data.getString("message"));
43 | } else {
44 | binding.setMessage("View Fragment");
45 | }
46 | }
47 |
48 | @Override
49 | public String getTitle() {
50 | return "View Fragment";
51 | }
52 |
53 | @Override
54 | public int getNavigationBarColor() {
55 | return navBarColor;
56 | }
57 |
58 | @Override
59 | public boolean isToolbarVisible() {
60 | return isToolbarVisible;
61 | }
62 |
63 | @Override
64 | public boolean isDrawerEnabled() {
65 | return isDrawerEnabled;
66 | }
67 |
68 | @Override
69 | public void onClick(View view) {
70 | switch (view.getId()) {
71 | case R.id.nav_color_deep_purple:
72 | setNavColor(R.color.deep_purple);
73 | break;
74 | case R.id.nav_color_blue:
75 | setNavColor(R.color.blue);
76 | break;
77 | case R.id.nav_color_teal:
78 | setNavColor(R.color.teal);
79 | break;
80 | case R.id.nav_color_red:
81 | setNavColor(R.color.red);
82 | break;
83 | default:
84 | break;
85 | }
86 |
87 | getFlowr().syncScreenState();
88 | }
89 |
90 | private void setNavColor(@ColorRes int colorResId) {
91 | navBarColor = ContextCompat.getColor(getContext(), colorResId);
92 | }
93 |
94 | @Override
95 | public void onCheckedChanged(CompoundButton compoundButton, boolean checked) {
96 | switch (compoundButton.getId()) {
97 | case R.id.toolbar_switch:
98 | isToolbarVisible = checked;
99 | break;
100 | case R.id.drawer_switch:
101 | isDrawerEnabled = checked;
102 | break;
103 | default:
104 | break;
105 | }
106 |
107 | getFlowr().syncScreenState();
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/flowr/src/test/java/com/fueled/flowr/internal/AbstractFlowrDeepLinkHandlerTest.java:
--------------------------------------------------------------------------------
1 | package com.fueled.flowr.internal;
2 |
3 | import android.content.Intent;
4 | import android.net.Uri;
5 | import android.os.Bundle;
6 |
7 | import com.fueled.flowr.AbstractFlowrFragment;
8 | import com.fueled.flowr.Flowr;
9 |
10 | import org.junit.Before;
11 | import org.junit.Test;
12 | import org.junit.runner.RunWith;
13 | import org.mockito.Mock;
14 | import org.mockito.runners.MockitoJUnitRunner;
15 |
16 | import static org.junit.Assert.assertEquals;
17 | import static org.junit.Assert.assertNotNull;
18 | import static org.junit.Assert.assertNull;
19 | import static org.mockito.Mockito.spy;
20 | import static org.mockito.Mockito.verify;
21 | import static org.mockito.Mockito.verifyNoMoreInteractions;
22 | import static org.mockito.Mockito.verifyZeroInteractions;
23 | import static org.mockito.Mockito.when;
24 |
25 | /**
26 | * Created by hussein@fueled.com on 05/06/2017.
27 | * Copyright (c) 2017 Fueled. All rights reserved.
28 | */
29 | @RunWith(MockitoJUnitRunner.class)
30 | public class AbstractFlowrDeepLinkHandlerTest {
31 |
32 | @Mock Intent intent;
33 | @Mock Uri uri;
34 | @Mock Bundle bundle;
35 |
36 | private AbstractFlowrDeepLinkHandler deepLinkHandler;
37 |
38 | @Before
39 | public void setup() {
40 | deepLinkHandler = spy(new AbstractFlowrDeepLinkHandler());
41 |
42 | when(intent.getData()).thenReturn(uri);
43 | when(deepLinkHandler.getNewBundle()).thenReturn(bundle);
44 | }
45 |
46 | @Test
47 | public void testMapLinkWithNoArguments() {
48 | when(uri.getPath()).thenReturn("/hello");
49 | when(uri.toString()).thenReturn("http://fueled.com/hello");
50 |
51 | deepLinkHandler.addFragment("/hi", DemoFragment.class);
52 | deepLinkHandler.addFragment("/hello", SampleFragment.class);
53 |
54 | FlowrDeepLinkInfo info = deepLinkHandler.getDeepLinkInfoForIntent(intent);
55 |
56 | verify(bundle).putString(Flowr.DEEP_LINK_URL, "http://fueled.com/hello");
57 | verifyNoMoreInteractions(bundle);
58 |
59 | assertNotNull(info);
60 | assertEquals(SampleFragment.class, info.fragment);
61 | assertNotNull(info.data);
62 | }
63 |
64 | @Test
65 | public void testMapLinkWithPathVariable() {
66 | when(uri.getPath()).thenReturn("/m/123");
67 | when(uri.toString()).thenReturn("http://fueled.com/m/123");
68 |
69 | deepLinkHandler.addFragment("/m/{id}", DemoFragment.class);
70 | deepLinkHandler.addFragment("/hello", SampleFragment.class);
71 |
72 | FlowrDeepLinkInfo info = deepLinkHandler.getDeepLinkInfoForIntent(intent);
73 |
74 | verify(bundle).putString(Flowr.DEEP_LINK_URL, "http://fueled.com/m/123");
75 | verify(bundle).putString("id", "123");
76 | verifyNoMoreInteractions(bundle);
77 |
78 | assertNotNull(info);
79 | assertEquals(DemoFragment.class, info.fragment);
80 | assertNotNull(info.data);
81 | }
82 |
83 | @Test
84 | public void testMapLinkNoMappingFound() {
85 | when(uri.getPath()).thenReturn("/m/123");
86 | when(uri.toString()).thenReturn("http://fueled.com/m/123");
87 |
88 | deepLinkHandler.addFragment("/hi", DemoFragment.class);
89 | deepLinkHandler.addFragment("/hello", SampleFragment.class);
90 |
91 | FlowrDeepLinkInfo info = deepLinkHandler.getDeepLinkInfoForIntent(intent);
92 |
93 | verifyZeroInteractions(bundle);
94 |
95 | assertNull(info);
96 | }
97 |
98 | public static class SampleFragment extends AbstractFlowrFragment {
99 |
100 | }
101 |
102 | public static class DemoFragment extends AbstractFlowrFragment {
103 |
104 | }
105 |
106 | }
107 |
--------------------------------------------------------------------------------
/flowr/src/main/java/com/fueled/flowr/AbstractFlowrFragment.java:
--------------------------------------------------------------------------------
1 | package com.fueled.flowr;
2 |
3 | import android.content.pm.ActivityInfo;
4 | import android.graphics.drawable.Drawable;
5 | import android.os.Bundle;
6 | import android.support.v4.app.Fragment;
7 |
8 | import java.util.UUID;
9 |
10 | /**
11 | * Created by hussein@fueled.com on 13/02/2017.
12 | * Copyright (c) 2017 Fueled. All rights reserved.
13 | */
14 | public abstract class AbstractFlowrFragment extends Fragment implements FlowrFragment {
15 |
16 | private static final String KEY_FRAGMENT_ID = "key_fragment_id";
17 |
18 | private String fragmentId;
19 | private boolean shown;
20 |
21 | @Override
22 | @SuppressWarnings("unchecked")
23 | public void onActivityCreated(Bundle savedInstanceState) {
24 | super.onActivityCreated(savedInstanceState);
25 |
26 | if (savedInstanceState != null) {
27 | fragmentId = savedInstanceState.getString(KEY_FRAGMENT_ID, null);
28 | }
29 |
30 | if (fragmentId == null) {
31 | fragmentId = UUID.randomUUID().toString();
32 | }
33 | }
34 |
35 | @Override
36 | public void onSaveInstanceState(Bundle outState) {
37 | super.onSaveInstanceState(outState);
38 |
39 | if (fragmentId != null) {
40 | outState.putString(KEY_FRAGMENT_ID, fragmentId);
41 | }
42 | }
43 |
44 | protected void onFragmentResults(int requestCode, int resultCode, Bundle data) {
45 | // Do Nothing. No Default implementation is required.
46 | }
47 |
48 | /**
49 | * Returns the unique id for this fragment instance.
50 | *
51 | * @return a unique id for this fragment instance.
52 | */
53 | public String getFragmentId() {
54 | return fragmentId;
55 | }
56 |
57 |
58 | /**
59 | * @inheritDoc
60 | */
61 | @Override
62 | public boolean onBackPressed() {
63 | return false;
64 | }
65 |
66 | /**
67 | * @inheritDoc
68 | */
69 | @Override
70 | public boolean onNavigationIconClick() {
71 | return false;
72 | }
73 |
74 | /**
75 | * @inheritDoc
76 | */
77 | @Override
78 | public NavigationIconType getNavigationIconType() {
79 | return NavigationIconType.BACK;
80 | }
81 |
82 | /**
83 | * @inheritDoc
84 | */
85 | @Override
86 | public Drawable getNavigationIcon() {
87 | return null;
88 | }
89 |
90 | /**
91 | * @inheritDoc
92 | */
93 | @Override
94 | public boolean isToolbarVisible() {
95 | return true;
96 | }
97 |
98 | /**
99 | * @inheritDoc
100 | */
101 | @Override
102 | public boolean isDrawerEnabled() {
103 | return true;
104 | }
105 |
106 | /**
107 | * @inheritDoc
108 | */
109 | @Override
110 | public int getScreenOrientation() {
111 | return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
112 | }
113 |
114 | /**
115 | * @inheritDoc
116 | */
117 | @Override
118 | public int getNavigationBarColor() {
119 | return -1;
120 | }
121 |
122 | /**
123 | * @inheritDoc
124 | */
125 | @Override
126 | public String getTitle() {
127 | return null;
128 | }
129 |
130 | /**
131 | * @inheritDoc
132 | */
133 | @Override
134 | public void onShown() {
135 | this.shown = true;
136 | }
137 |
138 | /**
139 | * @inheritDoc
140 | */
141 | @Override
142 | public void onHidden() {
143 | this.shown = false;
144 | }
145 |
146 | /**
147 | * Returns whether this fragment is currently at the top of the back stack.
148 | *
149 | * @return true if this fragment is at the top of the back stack.
150 | */
151 | protected boolean isShown() {
152 | return shown;
153 | }
154 | }
155 |
--------------------------------------------------------------------------------
/flowr/src/main/java/com/fueled/flowr/internal/TransactionData.java:
--------------------------------------------------------------------------------
1 | package com.fueled.flowr.internal;
2 |
3 | import android.content.Intent;
4 | import android.os.Bundle;
5 | import android.support.v4.app.Fragment;
6 | import android.support.v4.app.FragmentTransaction;
7 |
8 | import com.fueled.flowr.FlowrFragment;
9 |
10 | /**
11 | * Created by hussein@fueled.com on 16/02/2017.
12 | * Copyright (c) 2017 Fueled. All rights reserved.
13 | */
14 | public final class TransactionData {
15 |
16 | private Class extends T> fragmentClass;
17 | private Bundle args;
18 | private boolean skipBackStack = false;
19 | private boolean clearBackStack = false;
20 | private boolean replaceCurrentFragment = false;
21 | private int enterAnim;
22 | private int exitAnim;
23 | private int popEnterAnim;
24 | private int popExitAnim;
25 | private Intent deepLinkIntent;
26 |
27 | public TransactionData(Class extends T> fragmentClass) {
28 | this(fragmentClass, FragmentTransaction.TRANSIT_NONE, FragmentTransaction.TRANSIT_NONE);
29 | }
30 |
31 | public TransactionData(Class extends T> fragmentClass, int enterAnim, int exitAnim) {
32 | this(fragmentClass, enterAnim, exitAnim, FragmentTransaction.TRANSIT_NONE,
33 | FragmentTransaction.TRANSIT_NONE);
34 | }
35 |
36 | public TransactionData(Class extends T> fragmentClass, int enterAnim, int exitAnim,
37 | int popEnterAnim, int popExitAnim) {
38 | this.fragmentClass = fragmentClass;
39 | this.enterAnim = enterAnim;
40 | this.exitAnim = exitAnim;
41 | this.popEnterAnim = popEnterAnim;
42 | this.popExitAnim = popExitAnim;
43 | }
44 |
45 | public Intent getDeepLinkIntent() {
46 | return deepLinkIntent;
47 | }
48 |
49 | public void setDeepLinkIntent(Intent deepLinkIntent) {
50 | this.deepLinkIntent = deepLinkIntent;
51 | }
52 |
53 | public int getPopEnterAnim() {
54 | return popEnterAnim;
55 | }
56 |
57 | public void setPopEnterAnim(int popEnterAnim) {
58 | this.popEnterAnim = popEnterAnim;
59 | }
60 |
61 | public int getPopExitAnim() {
62 | return popExitAnim;
63 | }
64 |
65 | public void setPopExitAnim(int popExitAnim) {
66 | this.popExitAnim = popExitAnim;
67 | }
68 |
69 | public Class extends T> getFragmentClass() {
70 | return fragmentClass;
71 | }
72 |
73 | public void setFragmentClass(Class extends T> fragmentClass) {
74 | this.fragmentClass = fragmentClass;
75 | }
76 |
77 | public Bundle getArgs() {
78 | return args;
79 | }
80 |
81 | public void setArgs(Bundle args) {
82 | this.args = args;
83 | }
84 |
85 | public boolean isSkipBackStack() {
86 | return skipBackStack;
87 | }
88 |
89 | public void setSkipBackStack(boolean skipBackStack) {
90 | this.skipBackStack = skipBackStack;
91 | }
92 |
93 | public boolean isClearBackStack() {
94 | return clearBackStack;
95 | }
96 |
97 | public void setClearBackStack(boolean clearBackStack) {
98 | this.clearBackStack = clearBackStack;
99 | }
100 |
101 | public boolean isReplaceCurrentFragment() {
102 | return replaceCurrentFragment;
103 | }
104 |
105 | public void setReplaceCurrentFragment(boolean replaceCurrentFragment) {
106 | this.replaceCurrentFragment = replaceCurrentFragment;
107 | }
108 |
109 | public int getEnterAnim() {
110 | return enterAnim;
111 | }
112 |
113 | public void setEnterAnim(int enterAnim) {
114 | this.enterAnim = enterAnim;
115 | }
116 |
117 | public int getExitAnim() {
118 | return exitAnim;
119 | }
120 |
121 | public void setExitAnim(int exitAnim) {
122 | this.exitAnim = exitAnim;
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/flowr/src/main/java/com/fueled/flowr/internal/AbstractFlowrDeepLinkHandler.java:
--------------------------------------------------------------------------------
1 | package com.fueled.flowr.internal;
2 |
3 | import android.content.Intent;
4 | import android.net.Uri;
5 | import android.os.Bundle;
6 | import android.support.annotation.NonNull;
7 | import android.support.annotation.Nullable;
8 | import android.support.annotation.VisibleForTesting;
9 | import android.support.v4.app.Fragment;
10 |
11 | import com.fueled.flowr.Flowr;
12 | import com.fueled.flowr.FlowrFragment;
13 |
14 | import java.util.ArrayList;
15 | import java.util.HashMap;
16 | import java.util.Iterator;
17 | import java.util.List;
18 | import java.util.Map;
19 | import java.util.regex.Matcher;
20 | import java.util.regex.Pattern;
21 |
22 | /**
23 | * Created by husseinaladeen@gmail.com on 04/06/2017.
24 | * Copyright (c) 2017 Hussein Ala. All rights reserved.
25 | */
26 | public class AbstractFlowrDeepLinkHandler
27 | implements FlowrDeepLinkHandler {
28 |
29 | private static final String NAMED_PARAM_REGEX = "\\{([a-zA-Z][a-zA-Z0-9]*)\\}";
30 | private static final Pattern NAMED_PARAM_PATTERN = Pattern.compile(NAMED_PARAM_REGEX);
31 |
32 | private final Map> linkFragmentMap;
33 |
34 | public AbstractFlowrDeepLinkHandler() {
35 | linkFragmentMap = new HashMap<>();
36 | }
37 |
38 | /**
39 | * Add a new link between a specific URL pattern and a specific fragment class.
40 | *
41 | * @param url the URL pattern to link the fragment class to.
42 | * @param fragment the fragment class to be linked to the URL pattern.
43 | */
44 | protected void addFragment(String url, Class extends T> fragment) {
45 | linkFragmentMap.put(url, fragment);
46 | }
47 |
48 | /**
49 | * Generates a bundle of the url named path variables and parameters.
50 | *
51 | * @param uri the url to generate the bundle from.
52 | * @param pattern the pattern to extract named path variables with.
53 | * @return a bundle containing all path variables and parameters as strings.
54 | */
55 | @Nullable
56 | private Bundle bundleUriInfo(Uri uri, String pattern) {
57 | String regex = getRegexPattern(pattern);
58 | String path = uri.getPath();
59 | Matcher m = Pattern.compile(regex).matcher(path);
60 |
61 | if (m.matches()) {
62 | Bundle data = getNewBundle();
63 | data.putString(Flowr.DEEP_LINK_URL, uri.toString());
64 | Iterator params = getNamedGroupCandidates(pattern).iterator();
65 |
66 | //start at 1 because 0 is the searched string
67 | int i = 1;
68 | while (params.hasNext()) {
69 | String variableName = params.next();
70 | data.putString(variableName, m.group(i));
71 | i++;
72 | }
73 |
74 | return data;
75 | }
76 |
77 | return null;
78 | }
79 |
80 | private String getRegexPattern(String pattern) {
81 | return pattern.replaceAll("\\{(.+?)\\}", "(\\.+?)");
82 | }
83 |
84 | private List getNamedGroupCandidates(String regex) {
85 | List namedGroups = new ArrayList<>();
86 |
87 | Matcher m = NAMED_PARAM_PATTERN.matcher(regex);
88 |
89 | while (m.find()) {
90 | String variableName = m.group(1);
91 | namedGroups.add(variableName);
92 | }
93 |
94 | return namedGroups;
95 | }
96 |
97 | @VisibleForTesting
98 | protected Bundle getNewBundle() {
99 | return new Bundle();
100 | }
101 |
102 | /**
103 | * @inheritDoc
104 | */
105 | @Nullable
106 | public FlowrDeepLinkInfo getDeepLinkInfoForIntent(@NonNull Intent intent) {
107 | Uri uri = intent.getData();
108 |
109 | if (uri != null) {
110 | for (String uriPattern : linkFragmentMap.keySet()) {
111 | Bundle data = bundleUriInfo(uri, uriPattern);
112 | if (data != null) {
113 | return new FlowrDeepLinkInfo<>(data, linkFragmentMap.get(uriPattern));
114 | }
115 | }
116 | }
117 |
118 | return null;
119 | }
120 |
121 | }
122 |
--------------------------------------------------------------------------------
/extra/gradle/jacoco.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'jacoco'
2 |
3 | jacoco {
4 | toolVersion = "0.7.5.201505241946"
5 | }
6 |
7 | ext {
8 | limits = [
9 | 'instruction': 0,
10 | 'branch' : 0,
11 | 'line' : 0,
12 | 'complexity' : 0,
13 | 'method' : 0,
14 | 'class' : 0 //TODO - 100%
15 | ]
16 | }
17 |
18 |
19 | project.afterEvaluate {
20 | // Grab all build types and product flavors
21 | def buildTypes = android.buildTypes.collect { type -> type.name }
22 | def productFlavors = android.productFlavors.collect { flavor -> flavor.name }
23 |
24 | // When no product flavors defined, use empty
25 | if (!productFlavors) productFlavors.add('')
26 |
27 | productFlavors.each { productFlavorName ->
28 | buildTypes.each { buildTypeName ->
29 | def sourceName, sourcePath
30 | if (!productFlavorName) {
31 | sourceName = sourcePath = "${buildTypeName}"
32 | } else {
33 | sourceName = "${productFlavorName}${buildTypeName.capitalize()}"
34 | sourcePath = "${productFlavorName}/${buildTypeName}"
35 | }
36 | def testTaskName = "test${sourceName.capitalize()}UnitTest"
37 |
38 | // Create coverage task of form 'testFlavorTypeCoverage' depending on 'testFlavorTypeUnitTest'
39 | task "${testTaskName}Coverage"(type: JacocoReport, dependsOn: "$testTaskName") {
40 | group = "Reporting"
41 | description = "Generate Jacoco coverage reports on the ${sourceName.capitalize()} build."
42 |
43 | classDirectories = fileTree(
44 | dir: "${project.buildDir}/intermediates/classes/${sourcePath}",
45 | excludes: ['**/R.class',
46 | '**/R$*.class',
47 | '**/*$ViewInjector*.*',
48 | '**/*$ViewBinder*.*',
49 | '**/BuildConfig.*',
50 | '**/Manifest*.*',
51 | '**/*Factory*',
52 | '**/*_MembersInjector*',
53 | '**/*Module*',
54 | '**/*Component*',
55 | '**android**',
56 | '**/BR.class']
57 | )
58 |
59 | def coverageSourceDirs = [
60 | "src/main/java",
61 | "src/$productFlavorName/java",
62 | "src/$buildTypeName/java"
63 | ]
64 | additionalSourceDirs = files(coverageSourceDirs)
65 | sourceDirectories = files(coverageSourceDirs)
66 | executionData = files("${project.buildDir}/jacoco/${testTaskName}.exec")
67 |
68 | reports {
69 | xml.enabled = true
70 | html.enabled = true
71 | }
72 |
73 | doLast {
74 | jacocoTestReport("${testTaskName}Coverage")
75 | }
76 | }
77 | }
78 | }
79 | }
80 |
81 | def jacocoTestReport(testTaskName) {
82 | def report = file("${jacoco.reportsDir}/${testTaskName}/${testTaskName}.xml")
83 | logger.lifecycle("Checking coverage results: ${report}")
84 |
85 | def parser = new XmlParser()
86 | parser.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
87 | parser.setFeature("http://apache.org/xml/features/disallow-doctype-decl", false)
88 | def results = parser.parse(report)
89 |
90 | def percentage = {
91 | def covered = it.'@covered' as Double
92 | def missed = it.'@missed' as Double
93 | ((covered / (covered + missed)) * 100).round(2)
94 | }
95 |
96 | def counters = results.counter
97 | def metrics = [:]
98 | metrics << [
99 | 'instruction': percentage(counters.find { it.'@type'.equals('INSTRUCTION') }),
100 | 'branch' : percentage(counters.find { it.'@type'.equals('BRANCH') }),
101 | 'line' : percentage(counters.find { it.'@type'.equals('LINE') }),
102 | 'complexity' : percentage(counters.find { it.'@type'.equals('COMPLEXITY') }),
103 | 'method' : percentage(counters.find { it.'@type'.equals('METHOD') }),
104 | 'class' : percentage(counters.find { it.'@type'.equals('CLASS') })
105 | ]
106 |
107 |
108 | def failures = []
109 | metrics.each {
110 | def limit = limits[it.key]
111 | if (it.value < limit) {
112 | failures.add("- ${it.key} coverage rate is: ${it.value}%, minimum is ${limit}%")
113 | }
114 | }
115 |
116 | if (failures) {
117 | logger.quiet("------------------ Code Coverage Failed -----------------------")
118 | failures.each {
119 | logger.quiet(it)
120 | }
121 | logger.quiet("---------------------------------------------------------------")
122 | throw new GradleException("Code coverage failed")
123 | } else {
124 | logger.quiet("Passed Code Coverage Checks")
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/flowr-compiler/src/main/java/com/fueled/flowr/compilers/DeepLinkAnnotationCompiler.java:
--------------------------------------------------------------------------------
1 | package com.fueled.flowr.compilers;
2 |
3 | import com.fueled.flowr.annotations.DeepLink;
4 | import com.fueled.flowr.annotations.DeepLinkHandler;
5 | import com.google.auto.service.AutoService;
6 | import com.squareup.javapoet.ClassName;
7 | import com.squareup.javapoet.JavaFile;
8 | import com.squareup.javapoet.MethodSpec;
9 | import com.squareup.javapoet.TypeSpec;
10 |
11 | import java.io.IOException;
12 | import java.util.Set;
13 |
14 | import javax.annotation.processing.AbstractProcessor;
15 | import javax.annotation.processing.Processor;
16 | import javax.annotation.processing.RoundEnvironment;
17 | import javax.annotation.processing.SupportedAnnotationTypes;
18 | import javax.annotation.processing.SupportedSourceVersion;
19 | import javax.lang.model.SourceVersion;
20 | import javax.lang.model.element.Element;
21 | import javax.lang.model.element.Modifier;
22 | import javax.lang.model.element.TypeElement;
23 |
24 |
25 | /**
26 | * Annotation processor that will generate the FlowrDeepLinkHandlerImpl based on the information
27 | * provided by {@link DeepLink} and {@link DeepLinkHandler}.
28 | */
29 | @SupportedAnnotationTypes({"com.fueled.flowr.annotations.DeepLink",
30 | "com.fueled.flowr.annotations.DeepLinkHandler"})
31 | @SupportedSourceVersion(SourceVersion.RELEASE_7)
32 | @AutoService(Processor.class)
33 | public class DeepLinkAnnotationCompiler extends AbstractProcessor {
34 |
35 | private static final String deeplinkFormat = "addFragment($S, $T.class)";
36 | private static final String HANDLER_FILE_NAME_POST_FIX = "Impl";
37 | private static final String FLOWR_INTERNAL_PACKAGE_NAME = "com.fueled.flowr.internal";
38 | private static final String ABSTRACT_HANDLER_CLASS_NAME = "AbstractFlowrDeepLinkHandler";
39 |
40 | /**
41 | * Main method that will build the deep link handler implementation class.
42 | *
43 | * @param annotations the list of Annotations.
44 | * @param roundEnv The environment object.
45 | */
46 | @Override
47 | public boolean process(Set extends TypeElement> annotations, RoundEnvironment roundEnv) {
48 | System.out.println("[FlowR]: generating deep Link Handler...");
49 |
50 | MethodSpec.Builder constructorBuilder = generateConstructor();
51 |
52 | // Browse all the annotations and create the FlowrDeepLinkHandler constructor
53 | for (Element element : roundEnv.getElementsAnnotatedWith(DeepLink.class)) {
54 | String[] value = element.getAnnotation(DeepLink.class).value();
55 |
56 | for (String url : value) {
57 | constructorBuilder.addStatement(deeplinkFormat, url, ClassName.get(element.asType()));
58 | }
59 | }
60 |
61 | for (Element element : roundEnv.getElementsAnnotatedWith(DeepLinkHandler.class)) {
62 | String packageName = processingEnv.getElementUtils().getPackageOf(element)
63 | .getQualifiedName().toString();
64 |
65 | String className = element.getAnnotation(DeepLinkHandler.class).value();
66 |
67 | if (className.isEmpty()) {
68 | className = element.getSimpleName().toString() + HANDLER_FILE_NAME_POST_FIX;
69 | }
70 |
71 | generateDeepLinkHandler(packageName, className, constructorBuilder);
72 | }
73 |
74 | return true;
75 | }
76 |
77 | /**
78 | * Create the JavaPoet Type builder for an AbstractFlowrDeepLinkHandler implementation class.
79 | *
80 | * @param className the name to be used for the implementation class.
81 | * @return The builder for the implementation class.
82 | */
83 | private static TypeSpec.Builder getClassObject(String className) {
84 | return TypeSpec.classBuilder(className)
85 | .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
86 | .superclass(ClassName.get(FLOWR_INTERNAL_PACKAGE_NAME, ABSTRACT_HANDLER_CLASS_NAME));
87 | }
88 |
89 | /**
90 | * Create the constructor for deep link handler implementation class.
91 | *
92 | * @return The Constructor JavaPoet object.
93 | */
94 | private static MethodSpec.Builder generateConstructor() {
95 | return MethodSpec.constructorBuilder()
96 | .addModifiers(Modifier.PUBLIC);
97 | }
98 |
99 | /**
100 | * Generate a new deep link handler implementation class with the specified package name,
101 | * class name and the constructor body.
102 | *
103 | * @param packageName the name of the package to use for the generated class.
104 | * @param className the name to be used for the implementation class.
105 | * @param constructorBuilder the constructor body builder for the class.
106 | */
107 | private void generateDeepLinkHandler(String packageName, String className,
108 | MethodSpec.Builder constructorBuilder) {
109 | TypeSpec classObject = getClassObject(className)
110 | .addMethod(constructorBuilder.build())
111 | .build();
112 |
113 | JavaFile javaFile = JavaFile.builder(packageName, classObject)
114 | .build();
115 |
116 | try {
117 | javaFile.writeTo(processingEnv.getFiler());
118 | } catch (IOException e) {
119 | e.printStackTrace();
120 | }
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/extra/checkstyle/checkstyle.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/fragment_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
11 |
12 |
15 |
16 |
17 |
18 |
23 |
24 |
31 |
32 |
39 |
40 |
46 |
47 |
54 |
55 |
62 |
63 |
71 |
72 |
80 |
81 |
82 |
83 |
87 |
88 |
95 |
96 |
103 |
104 |
109 |
110 |
111 |
112 |
116 |
117 |
124 |
125 |
132 |
133 |
138 |
139 |
140 |
141 |
145 |
146 |
147 |
148 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/fueled/flowr/sample/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.fueled.flowr.sample;
2 |
3 | import android.databinding.DataBindingUtil;
4 | import android.graphics.drawable.Drawable;
5 | import android.os.Bundle;
6 | import android.support.annotation.NonNull;
7 | import android.support.design.widget.NavigationView;
8 | import android.support.v4.widget.DrawerLayout;
9 | import android.view.Gravity;
10 | import android.view.MenuItem;
11 | import android.view.View;
12 |
13 | import com.fueled.flowr.DrawerHandler;
14 | import com.fueled.flowr.Flowr;
15 | import com.fueled.flowr.NavigationIconType;
16 | import com.fueled.flowr.ToolbarHandler;
17 | import com.fueled.flowr.annotations.DeepLinkHandler;
18 | import com.fueled.flowr.sample.core.AbstractActivity;
19 | import com.fueled.flowr.sample.core.AbstractFragment;
20 | import com.fueled.flowr.sample.core.FragmentResultPublisherImpl;
21 | import com.fueled.flowr.sample.databinding.ActivityMainBinding;
22 | import com.fueled.flowr.sample.library.LibraryDeepLinkHandlerImpl;
23 |
24 | @DeepLinkHandler("MainDeepLinkHandlerImpl")
25 | public class MainActivity extends AbstractActivity implements ToolbarHandler, DrawerHandler {
26 |
27 | private Flowr flowr;
28 | private ActivityMainBinding binding;
29 |
30 | private View.OnClickListener toolbarNavigationClickListener;
31 |
32 | @Override
33 | protected void onCreate(Bundle savedInstanceState) {
34 | super.onCreate(savedInstanceState);
35 |
36 | binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
37 | setSupportActionBar(binding.toolbar);
38 |
39 | if (getFlowr().getCurrentFragment() == null) {
40 | getFlowr()
41 | .open(getIntent(), HomeFragment.class)
42 | .skipBackStack(true)
43 | .displayFragment();
44 | }
45 |
46 | setToolbarNavigationClickListener();
47 | setNavigationItemSelectedListener();
48 | }
49 |
50 | public Flowr getFlowr() {
51 | if (flowr == null) {
52 | flowr = new Flowr(R.id.main_container, this, this, this,
53 | FragmentResultPublisherImpl.getInstance());
54 |
55 | flowr.setDeepLinkHandlers(new MainDeepLinkHandlerImpl(),
56 | new LibraryDeepLinkHandlerImpl());
57 | }
58 |
59 | return flowr;
60 | }
61 |
62 | private void setToolbarNavigationClickListener() {
63 | binding.toolbar.setNavigationOnClickListener(new View.OnClickListener() {
64 | @Override
65 | public void onClick(View view) {
66 | if (getFlowr() != null && getFlowr().isHomeFragment()) {
67 | openDrawer();
68 | } else if (toolbarNavigationClickListener != null) {
69 | toolbarNavigationClickListener.onClick(view);
70 | }
71 | }
72 | });
73 | }
74 |
75 | private void setNavigationItemSelectedListener() {
76 | binding.navigation.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
77 | @Override
78 | public boolean onNavigationItemSelected(@NonNull MenuItem item) {
79 | switch (item.getItemId()) {
80 | case R.id.navigation_menu_home:
81 | displayNavigationScreen(HomeFragment.class);
82 | break;
83 | case R.id.navigation_menu_categories:
84 | displayNavigationScreen(FirstFragment.class);
85 | break;
86 | default:
87 | break;
88 | }
89 |
90 | return true;
91 | }
92 | });
93 | }
94 |
95 | private void displayNavigationScreen(Class extends AbstractFragment> screen) {
96 | closeDrawer();
97 |
98 | if (!screen.equals(getFlowr().getCurrentFragment().getClass())) {
99 | getFlowr().open(screen)
100 | .clearBackStack(true)
101 | .skipBackStack(true)
102 | .replaceCurrentFragment(true)
103 | .displayFragment();
104 | }
105 | }
106 |
107 | @Override
108 | public void setNavigationIcon(NavigationIconType navigationIconType) {
109 | switch (navigationIconType) {
110 | case BACK:
111 | binding.toolbar.setNavigationIcon(R.drawable.ic_arrow_back);
112 | break;
113 | case HAMBURGER:
114 | binding.toolbar.setNavigationIcon(R.drawable.ic_menu);
115 | break;
116 | default:
117 | binding.toolbar.setNavigationIcon(null);
118 | break;
119 | }
120 | }
121 |
122 | @Override
123 | public void onBackPressed() {
124 | if (binding.drawerLayout.isDrawerOpen(Gravity.START)) {
125 | closeDrawer();
126 | } else {
127 | super.onBackPressed();
128 | }
129 | }
130 |
131 | @Override
132 | public void setCustomNavigationIcon(Drawable navigationIcon) {
133 | binding.toolbar.setNavigationIcon(navigationIcon);
134 | }
135 |
136 | @Override
137 | public void setToolbarVisible(boolean visible) {
138 | binding.toolbar.setVisibility(visible ? View.VISIBLE : View.GONE);
139 | }
140 |
141 | @Override
142 | public void setToolbarTitle(String title) {
143 | binding.toolbar.setTitle(title);
144 | }
145 |
146 | @Override
147 | public void setToolbarNavigationButtonListener(View.OnClickListener onClickListener) {
148 | toolbarNavigationClickListener = onClickListener;
149 | }
150 |
151 | @Override
152 | public void openDrawer() {
153 | binding.drawerLayout.openDrawer(Gravity.START);
154 | }
155 |
156 | @Override
157 | public void closeDrawer() {
158 | binding.drawerLayout.closeDrawer(Gravity.START);
159 | }
160 |
161 | @Override
162 | public void setDrawerEnabled(boolean enabled) {
163 | binding.drawerLayout.setDrawerLockMode(enabled ? DrawerLayout.LOCK_MODE_UNLOCKED :
164 | DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
165 | }
166 | }
167 |
--------------------------------------------------------------------------------
/.idea/codeStyleSettings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
229 |
230 |
231 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "{}"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright 2016 Fueled
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # FlowR [](https://jitpack.io/#Fueled/flowr)
2 |
3 | FlowR is a wrapper class around the Fragment Manager. It's mainly used to navigate between different fragments easily while providing a wide range of functionality. The following are the functionalities provided by the Flowr:
4 |
5 | * Easily navigate between different fragments.
6 | * Ability to open fragments for result.
7 | * Keeping the state of the activity correctly synced depending on the custom values specified by the fragment currently visible such as the screen orientation and navigation bar color.
8 |
9 | ## Install
10 | Add it in your root build.gradle at the end of repositories:
11 |
12 | ```groovy
13 | allprojects {
14 | repositories {
15 | ...
16 | maven { url "https://jitpack.io" }
17 | }
18 | }
19 | ```
20 | Then add the dependency to the application module:
21 |
22 | ```groovy
23 | dependencies {
24 | annotationProcessor 'com.github.fueled.flowr:flowr-compiler:X.X.X'
25 | compile 'com.github.fueled.flowr:flowr:X.X.X'
26 | }
27 | ```
28 |
29 | ## Getting Started
30 |
31 | Each new activity will have its own Flowr instance, the Flowr lifecycle should be bound to the activity, meaning it should be destroyed and created with the activity. For the activity to work with the Flowr we need to make sure that it implements the interface `FlowrScreen`.
32 |
33 | While creating a new Flowr instance these are the required parameters:
34 |
35 | * **`containerId`**: this is the id for the layout that the Flowr will use to display the fragments inside.
36 | * **`FlowrScreen`**: in most cases this will be your activity with the interface `FlowrScreen` implemented, this will provide the Flowr with access to the activity `FragmentManager` and the ability to set values such as the screen orientation and navigation bar color depending on the fragment currently being displayed.
37 | * **`FragmentsResultPublisher`**: this is used to publish results from fragments that where opened for results.
38 |
39 | And some of the optional values are:
40 |
41 | * **`ToolbarHandler`**: this is only needed if a single `Toolbar` is going to be used for every fragment, in most cases this will be your activity with the interface `ToolbarHandler` implemented. The `ToolbarHandler` provide extra functionality to the fragments displayed to define the toolbar navigation icon, and to toggle the toolbar visibility.
42 | * **`tagPrefix`**: a custom prefix for the tags to be used for fragments that will be added to the backstack, the default tag used is `#id-`.
43 | * **`DrawerHandler`**: this is only needed if the activity contains a side drawer, again in most cases this will be your activity with the `DrawerHandler` interface implemented. The `DrawerHandler` provides the ability to enable/disable the drawer and open/close the drawer.
44 |
45 |
46 | Creating a new Flowr instance for an activity that does not have a toolbar and a drawer:
47 | ```java
48 | Flowr Flowr = new Flowr(R.id.main_container, flowrScreen, resultPublisher);
49 | ```
50 |
51 | Creating a new Flowr instance for an activity that does have a toolbar and a drawer:
52 | ```java
53 | Flowr Flowr = new Flowr(R.id.main_container, flowrScreen, toolbarHandler, drawerHandler, resultPublisher);
54 | ```
55 |
56 |
57 | ## Displaying a Fragment
58 |
59 | When displaying a new fragment there are multiple parameters that can be specified. Displaying a Fragment is done through a builder pattern which allows us to easily only specify the parameters that we require. these parameters are listed and explained below:
60 |
61 | * **`fragmentClass`**: The class of the fragment to be displayed, this is the only parameter that would always be required when displaying a new fragment.
62 | * **`data`**: a `Bundle` containing the arguments the fragment might need. The default value for this is null.
63 | * **`skipBackStack`**: specify whether this fragment will be added to the `FragmentManager` back stack or not. The default value for this is `false`.
64 | * **`clearBackStack`**: specify whether the `FragmentManager` backstack should be cleared before displaying this fragment. The default value used for this is `false`.
65 | * **`replaceCurrentFragment`**: specify whether this fragment should replace the fragment currently displayed inside the container or just be added over it. The default value used for this is `false`.
66 | * **`enterAnim`** and **`exitAnim`**: animation resource ID for the enter and exit fragment animation. The default values used here are `R.anim.fragment_enter_anim` and `R.anim.fragment_exit_anim`.
67 |
68 | finally after we have specified all the parameters we need we can simply call `displayFragment()` to display the fragment.
69 |
70 | ```java
71 | Flowr.open(DemoFragment.class)
72 | .setData(data)
73 | .skipBackStack(false)
74 | .clearBackStack(true)
75 | .replaceCurrentFragment(true)
76 | .setCustomTransactionAnimation(android.R.anim.fade_in, android.R.anim.fade_out, android.R.anim.slide_in_left, android.R.anim.slide_out_right)
77 | .displayFragment();
78 | ```
79 |
80 | ## Displaying a Fragment for Results
81 |
82 | When displaying a fragment for results, we will have access to all the parameters described in the previous section with the only difference being that `displayFragmentForResults` should be called rather than `displayFragment` at the end with the following parameters:
83 |
84 | * **`fragmentId`**: a unique ID that each new instance of `AbstractFragment` is assigned at the start and it can simply be accessed by calling `getFragmentId()` from inside your fragment. This ID is used to deliver the results to the correct fragment instance at the end.
85 | * **`requestCode`**: this code will be returned in `onFragmentResults()` when the fragment is closed, and it can be used to identify the request from which the results were returned.
86 |
87 | ```java
88 | Flowr.open(RequestFragment.class)
89 | .displayFragmentForResults(getFragmentId(), REQUEST_CODE);
90 | ```
91 |
92 | To handle the results all you need to do is simply just override the `onFragmentResults()` method in your fragment:
93 |
94 | ```java
95 | @Override
96 | protected void onFragmentResults(int requestCode, int resultCode, Bundle data) {
97 | super.onFragmentResults(requestCode, resultCode, data);
98 |
99 | if (requestCode == REQUEST_CODE) {
100 | if (resultCode == Activity.RESULT_OK) {
101 | demoTextView.setText("Result OK");
102 | } else {
103 | demoTextView.setText("Result CANCELED");
104 | }
105 | }
106 | }
107 | ```
108 |
109 | Finally when closing a fragment that was opened for results you will need to call the Flowr method `closeWithResults()`:
110 |
111 | ```java
112 | Flowr.closeWithResults(getResultsResponse(resultCode, resultData));
113 | ```
114 |
115 | ## State Sync and Fragment Dependent Customization
116 |
117 | The Flowr provides the ability to set custom attributes for values such as the screen orientation and navigation bar color for each fragment. These values are kept in sync automatically with the parent activity when navigating between different fragments through the Flowr `syncScreenState()` method. The `syncScreenState()` method is only required to be invoked manually when a change to one of these values has been made after the fragment has been already displayed.
118 |
119 | The following custom values are what is currently supported by the Flowr:
120 |
121 | ### Screen Orientation
122 |
123 | Each fragment can specify its own preferred screen orientation by simply overriding the method `getScreenOrientation()` from `AbstractFragment` and returning one of the following supported values listed [here](https://developer.android.com/reference/android/content/pm/ActivityInfo.html#screenOrientation).
124 |
125 | ```java
126 | public class DemoFragment extends AbstractFragment implements DemoScene {
127 |
128 | ....
129 |
130 | @Override
131 | public int getScreenOrientation() {
132 | return ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
133 | }
134 | }
135 | ```
136 |
137 | ### Navigation Bar Color
138 |
139 | Fragments can also specify a preferred color to be used for the navigation bar, this can be done by simply overriding `getNavigationBarColor()` and returning the integer value for the color to be used.
140 |
141 | ```java
142 | public class DemoFragment extends AbstractFragment implements DemoScene {
143 |
144 | ....
145 |
146 | @Override
147 | public int getNavigationBarColor() {
148 | return navigationBarColor;
149 | }
150 | }
151 | ```
152 |
153 | ### Toolbar Options
154 |
155 | These values are only used if the parent activity has a toolbar and the Flowr was provided a `ToolbarHandler` instance.
156 |
157 | 1. **Toolbar Visibility:** The boolean value specified here by overriding the `isToolbarVisible()` method in your fragment, will be used to determine if the fragment should be visible or not. By default the toolbar is visible.
158 | ```java
159 | public class DemoFragment extends AbstractFragment implements DemoScene {
160 |
161 | ....
162 |
163 | @Override
164 | public boolean isToolbarVisible() {
165 | return false;
166 | }
167 | }
168 | ```
169 |
170 | 2. **Navigation Icon Type:** This value is used to determine the type of the navigation icon to be used by the toolbar. This is done simply by overriding the `getNavigationIconType()` method in your fragment, and returning one of the following values `HIDDEN`, `HAMBURGER`, `BACK`, or `CUSTOM`.
171 | ```java
172 | public class DemoFragment extends AbstractFragment implements DemoScene {
173 |
174 | ....
175 |
176 | @Override
177 | public NavigationIconType getNavigationIconType() {
178 | return NavigationIconType.HAMBURGER;
179 | }
180 | }
181 | ```
182 |
183 | 3. **Navigation Icon Drawable:** This value is only used if the navigation icon type returned previously was `CUSTOM`. The drawable returned here will be used as the toolbar navigation icon, which can be simply done by just overriding the `getNavigationIcon()` in you fragment.
184 | ```java
185 | public class DemoFragment extends AbstractFragment implements DemoScene {
186 |
187 | ....
188 |
189 | @Override
190 | public Drawable getNavigationIcon() {
191 | return navigationIcon;
192 | }
193 | }
194 | ```
195 |
196 | ### Drawer Options
197 |
198 | The Flowr also provides the ability to specify whether the drawer should be enabled or not for the current fragment, this option is also only available if the fragment has a drawer and the Flowr was provided with a `DrawerHandler` instance. To specify this you will have to override the `isDrawerEnabled()` method in your fragment and return either `true` or `false`.
199 |
200 | ```java
201 | public class DemoFragment extends AbstractFragment implements DemoScene {
202 |
203 | ....
204 |
205 | @Override
206 | public boolean isDrawerEnabled() {
207 | return super.isDrawerEnabled();
208 | }
209 | }
210 | ```
211 |
212 | ## Deep Link
213 | FlowR support deep links.
214 | First, setup the deep link as explained in the [documentation](https://developer.android.com/training/app-indexing/deep-linking.html#adding-filters).
215 | Then annote your fragment with a `@DeepLink` annotation. That value will be the relative path...
216 |
217 | ```java
218 | @DeepLink("/path1/")
219 | public class TestFragment extends Fragment implement FlowrFragment
220 | ```
221 | ... or two.
222 |
223 | ```java
224 | @DeepLink({"/path1/","path2"})
225 | public class TestFragment extends Fragment implement FlowrFragment
226 | ```
227 | You can also insert variable:
228 |
229 | ```java
230 | @DeepLink("/{id}/details")
231 | public class TestFragment extends Fragment implement FlowrFragment
232 | .
233 | .
234 | .
235 | String url = getArguments().getString(Flowr.DEEP_LINK_URL,"");
236 | String id = getArguments().getString("id","");
237 | ```
238 |
239 | ### Deep Linking Setup:
240 |
241 | To generate your deep link handler you will need to annotate at least one class with the `@DeepLinkHandler` annotation. The name of the class annotated with the `@DeepLinkHandler` annotation would then be used as the name of the generated handler class with "Impl" appended at the end.
242 |
243 | ```java
244 | /** This will generate a MainDeepLinkHandlerImpl class */
245 | @DeepLinkHandler
246 | public class MainDeepLinkHandler {
247 | }
248 | ```
249 |
250 | However it is also possible to specify a custom name for the generated class by passing the desired class name as a string argument to the `@DeepLinkHandler` annotation.
251 |
252 | ```java
253 | /** This will generate a MyDeepLinkHandler class */
254 | @DeepLinkHandler("MyDeepLinkHandler")
255 | public class MainActivity extends AbstractActivity {
256 | }
257 | ```
258 |
259 | If you have fragments across multiple modules, you will need to add the `@DeepLinkHandler` annotation to at least one class in each module.
260 |
261 | ```java
262 | /** This will generate a LibraryDeepLinkHandlerImpl class */
263 | @DeepLinkHandler
264 | public class LibraryDeepLinkHandler {
265 | }
266 | ```
267 |
268 | Provide the list of generated deep link handlers to your flowr instance.
269 |
270 | ```java
271 | public class MainActivity extends AbstractActivity {
272 |
273 | private Flowr flowr;
274 |
275 | public void getFlowr() {
276 | if (flowr == null) {
277 | flowr = new Flowr(...);
278 | flowr.setDeepLinkHandlers(new MainDeepLinkHandlerImpl(), new LibraryDeepLinkHandlerImpl());
279 | }
280 |
281 | return flowr;
282 | }
283 | }
284 | ```
285 |
286 | Finally to trigger the deep linking handling, simply call `open(Intent, Fragment))` from your `Activity#onCreate(Bundle)` method.
287 |
288 | ```java
289 | public class MainActivity extends AbstractActivity {
290 |
291 | @Override
292 | protected void onCreate(Bundle savedInstanceState) {
293 | ...
294 | getFlowr()
295 | .open(getIntent(), HomeFragment.class)
296 | .skipBackStack(true)
297 | .displayFragment();
298 | }
299 | }
300 | ```
301 |
302 | Additionally you can access a Fragment via the link attached to it:
303 |
304 | ```java
305 | getFlowr()
306 | .open("/path1/")
307 | .skipBackStack(true)
308 | .displayFragment();
309 | ```
310 | variables work too:
311 |
312 | ```java
313 | getFlowr()
314 | .open("/1234/details")
315 | .skipBackStack(true)
316 | .displayFragment();
317 | ```
318 |
319 | # License
320 |
321 | Copyright 2016 Fueled
322 |
323 | Licensed under the Apache License, Version 2.0 (the "License");
324 | you may not use this file except in compliance with the License.
325 | You may obtain a copy of the License at
326 |
327 | http://www.apache.org/licenses/LICENSE-2.0
328 |
329 | Unless required by applicable law or agreed to in writing, software
330 | distributed under the License is distributed on an "AS IS" BASIS,
331 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
332 | See the License for the specific language governing permissions and
333 | limitations under the License.
334 |
--------------------------------------------------------------------------------
/flowr/src/main/java/com/fueled/flowr/Flowr.java:
--------------------------------------------------------------------------------
1 | package com.fueled.flowr;
2 |
3 | import android.content.Intent;
4 | import android.content.pm.ActivityInfo;
5 | import android.net.Uri;
6 | import android.os.Bundle;
7 | import android.support.annotation.AnimRes;
8 | import android.support.annotation.IdRes;
9 | import android.support.annotation.NonNull;
10 | import android.support.annotation.Nullable;
11 | import android.support.v4.app.Fragment;
12 | import android.support.v4.app.FragmentManager;
13 | import android.support.v4.app.FragmentTransaction;
14 | import android.text.TextUtils;
15 | import android.util.Log;
16 | import android.view.View;
17 |
18 | import com.fueled.flowr.internal.FlowrDeepLinkHandler;
19 | import com.fueled.flowr.internal.FlowrDeepLinkInfo;
20 | import com.fueled.flowr.internal.TransactionData;
21 |
22 | import java.util.ArrayList;
23 | import java.util.Collections;
24 | import java.util.List;
25 |
26 | /**
27 | * Created by hussein@fueled.com on 31/05/2016.
28 | * Copyright (c) 2016 Fueled. All rights reserved.
29 | */
30 | @SuppressWarnings({"WeakerAccess", "UnusedDeclaration"}) // Public API.
31 | public class Flowr implements FragmentManager.OnBackStackChangedListener,
32 | View.OnClickListener {
33 |
34 | /**
35 | * To be used as Bundle key for deep links.
36 | */
37 | public final static String DEEP_LINK_URL = "DEEP_LINK_URL";
38 |
39 | private final static String KEY_REQUEST_BUNDLE = "KEY_REQUEST_BUNDLE";
40 | private final static String KEY_FRAGMENT_ID = "KEY_FRAGMENT_ID";
41 | private final static String KEY_REQUEST_CODE = "KEY_REQUEST_CODE";
42 |
43 | private final static String TAG = Flowr.class.getSimpleName();
44 |
45 | private final FragmentsResultPublisher resultPublisher;
46 | private final int mainContainerId;
47 |
48 | @Nullable private FlowrScreen screen;
49 | @Nullable private ToolbarHandler toolbarHandler;
50 | @Nullable private DrawerHandler drawerHandler;
51 |
52 | @Nullable private Fragment currentFragment;
53 |
54 | private boolean overrideBack;
55 | private String tagPrefix;
56 |
57 | private List deepLinkHandlers;
58 |
59 | /**
60 | * Constructor to use when creating a new router for an activity
61 | * that has no toolbar.
62 | *
63 | * @param mainContainerId the id of the container where the fragments should be displayed
64 | * @param screen the fragment's parent screen
65 | */
66 | public Flowr(@IdRes int mainContainerId, @Nullable FlowrScreen screen,
67 | FragmentsResultPublisher resultPublisher) {
68 | this(mainContainerId, screen, null, null, resultPublisher);
69 | }
70 |
71 | /**
72 | * Constructor to use when creating a new router for an activity
73 | * that has no toolbar.
74 | *
75 | * @param mainContainerId the id of the container where the fragments should be displayed
76 | * @param screen the fragment's parent screen
77 | * @param tagPrefix a custom prefix for the tags to be used for fragments that will be added to
78 | * the backstack.
79 | * @param resultPublisher the result publish to be used to publish results from fragments
80 | * that where opened for results.
81 | */
82 | public Flowr(@IdRes int mainContainerId, @Nullable FlowrScreen screen, @NonNull String tagPrefix,
83 | FragmentsResultPublisher resultPublisher) {
84 | this(mainContainerId, screen, null, null, tagPrefix, resultPublisher);
85 | }
86 |
87 | /**
88 | * Constructor to use when creating a new router for an activity
89 | * that has toolbar and a drawer.
90 | *
91 | * @param mainContainerId the id of the container where the fragments should be displayed
92 | * @param screen the fragment's parent screen
93 | * @param toolbarHandler the {@link ToolbarHandler} to be used to sync toolbar state
94 | * @param drawerHandler the {@link DrawerHandler} to be used to sync drawer state
95 | * @param resultPublisher the result publish to be used to publish results from fragments
96 | * that where opened for results.
97 | */
98 | public Flowr(@IdRes int mainContainerId, @Nullable FlowrScreen screen,
99 | @Nullable ToolbarHandler toolbarHandler, @Nullable DrawerHandler drawerHandler,
100 | FragmentsResultPublisher resultPublisher) {
101 | this(mainContainerId, screen, toolbarHandler, drawerHandler, "#id-", resultPublisher);
102 | }
103 |
104 | /**
105 | * Constructor to use when creating a new router for an activity
106 | * that has toolbar and a drawer.
107 | *
108 | * @param mainContainerId the id of the container where the fragments should be displayed
109 | * @param screen the fragment's parent screen
110 | * @param toolbarHandler the {@link ToolbarHandler} to be used to sync toolbar state
111 | * @param drawerHandler the {@link DrawerHandler} to be used to sync drawer state
112 | * @param tagPrefix a custom prefix for the tags to be used for fragments that will be added to
113 | * the backstack.
114 | * @param resultPublisher the result publish to be used to publish results from fragments
115 | * that where opened for results.
116 | */
117 | public Flowr(@IdRes int mainContainerId, @Nullable FlowrScreen screen,
118 | @Nullable ToolbarHandler toolbarHandler, @Nullable DrawerHandler drawerHandler,
119 | @NonNull String tagPrefix, FragmentsResultPublisher resultPublisher) {
120 | this.resultPublisher = resultPublisher;
121 | this.mainContainerId = mainContainerId;
122 | this.tagPrefix = tagPrefix;
123 | this.overrideBack = false;
124 |
125 | setRouterScreen(screen);
126 | setToolbarHandler(toolbarHandler);
127 | setDrawerHandler(drawerHandler);
128 |
129 | deepLinkHandlers = new ArrayList<>();
130 |
131 | syncScreenState();
132 | }
133 |
134 | /**
135 | * Build and return a new ResultResponse instant using the arguments passed in.
136 | *
137 | * @param arguments Used to retrieve the ID and request code for the fragment
138 | * requesting the results.
139 | * @param resultCode The results code to be returned.
140 | * @param data Used to return extra data that might be required.
141 | * @return a new {@link ResultResponse} instance
142 | */
143 | public static ResultResponse getResultsResponse(Bundle arguments, int resultCode, Bundle data) {
144 | if (arguments == null || !arguments.containsKey(KEY_REQUEST_BUNDLE)) {
145 | return null;
146 | }
147 |
148 | ResultResponse resultResponse = new ResultResponse();
149 | resultResponse.resultCode = resultCode;
150 | resultResponse.data = data;
151 |
152 | Bundle requestBundle = arguments.getBundle(KEY_REQUEST_BUNDLE);
153 |
154 | if (requestBundle != null) {
155 | resultResponse.fragmentId = requestBundle.getString(KEY_FRAGMENT_ID);
156 | resultResponse.requestCode = requestBundle.getInt(KEY_REQUEST_CODE);
157 | }
158 |
159 | return resultResponse;
160 | }
161 |
162 |
163 | /**
164 | * Returns the {@link FlowrScreen} used for this router.
165 | *
166 | * @return the router screen for this router
167 | */
168 | @Nullable
169 | protected FlowrScreen getRouterScreen() {
170 | return screen;
171 | }
172 |
173 | /**
174 | * Sets the {@link FlowrScreen} to be used for this router.
175 | *
176 | * @param flowrScreen the router screen to be used
177 | */
178 | public void setRouterScreen(@Nullable FlowrScreen flowrScreen) {
179 | removeCurrentRouterScreen();
180 | if (flowrScreen != null) {
181 | this.screen = flowrScreen;
182 |
183 | if (flowrScreen.getScreenFragmentManager() != null) {
184 | screen.getScreenFragmentManager().addOnBackStackChangedListener(this);
185 | setCurrentFragment(retrieveCurrentFragment());
186 | }
187 | }
188 | }
189 |
190 | private void removeCurrentRouterScreen() {
191 | if (screen != null) {
192 | screen.getScreenFragmentManager().removeOnBackStackChangedListener(this);
193 | screen = null;
194 | currentFragment = null;
195 | }
196 | }
197 |
198 | /**
199 | * Sets the {@link ToolbarHandler} to be used to sync toolbar state.
200 | *
201 | * @param toolbarHandler the toolbar handler to be used.
202 | */
203 | public void setToolbarHandler(@Nullable ToolbarHandler toolbarHandler) {
204 | removeCurrentToolbarHandler();
205 |
206 | if (toolbarHandler != null) {
207 | this.toolbarHandler = toolbarHandler;
208 | toolbarHandler.setToolbarNavigationButtonListener(this);
209 | }
210 | }
211 |
212 | private void removeCurrentToolbarHandler() {
213 | if (toolbarHandler != null) {
214 | toolbarHandler.setToolbarNavigationButtonListener(null);
215 | toolbarHandler = null;
216 | }
217 | }
218 |
219 | /**
220 | * Sets the {@link DrawerHandler} to be used to sync drawer state.
221 | *
222 | * @param drawerHandler the drawer handler to be used.
223 | */
224 | public void setDrawerHandler(@Nullable DrawerHandler drawerHandler) {
225 | this.drawerHandler = drawerHandler;
226 | }
227 |
228 | /**
229 | * Specify a collection of {@link FlowrDeepLinkHandler} to be used when routing deep link
230 | * intents replacing all previously set handlers.
231 | *
232 | * @param handlers the collection of handlers to be used.
233 | */
234 | public void setDeepLinkHandlers(FlowrDeepLinkHandler... handlers) {
235 | this.deepLinkHandlers.clear();
236 |
237 | if (handlers != null) {
238 | Collections.addAll(deepLinkHandlers, handlers);
239 | }
240 | }
241 |
242 | /**
243 | * Returns the prefix used for the backstack fragments tag
244 | *
245 | * @return the prefix used for the backstack fragments tag
246 | */
247 | @NonNull
248 | protected final String getTagPrefix() {
249 | return tagPrefix;
250 | }
251 |
252 | /**
253 | *
254 | * @param data TransactionData used to configure fragment transaction
255 | * @param type Fragment & FlowrFragment
256 | * @return id Identifier of the committed transaction.
257 | */
258 | protected int displayFragment(TransactionData data) {
259 | int identifier = -1;
260 | try {
261 | if (screen == null) {
262 | return identifier;
263 | }
264 |
265 | injectDeepLinkInfo(data);
266 |
267 | if (data.isClearBackStack()) {
268 | clearBackStack();
269 | }
270 |
271 | currentFragment = retrieveCurrentFragment();
272 |
273 | Fragment fragment = data.getFragmentClass().newInstance();
274 | fragment.setArguments(data.getArgs());
275 |
276 | FragmentTransaction transaction = screen.getScreenFragmentManager().beginTransaction();
277 |
278 | if (!data.isSkipBackStack()) {
279 | String id = tagPrefix + screen.getScreenFragmentManager().getBackStackEntryCount();
280 | transaction.addToBackStack(id);
281 | }
282 |
283 | setCustomAnimations(transaction, data.getEnterAnim(), data.getExitAnim(), data.getPopEnterAnim(), data.getPopExitAnim());
284 |
285 | if (data.isReplaceCurrentFragment()) {
286 | transaction.replace(mainContainerId, fragment);
287 | } else {
288 | transaction.add(mainContainerId, fragment);
289 | }
290 |
291 | identifier = transaction.commit();
292 |
293 | if (data.isSkipBackStack()) {
294 | setCurrentFragment(fragment);
295 | }
296 | } catch (Exception e) {
297 | Log.e(TAG, "Error while displaying fragment.", e);
298 | }
299 | return identifier;
300 | }
301 |
302 | /**
303 | * Parse the intent set by {@link TransactionData#deepLinkIntent} and if this intent contains
304 | * Deep Link info, update the {@link #currentFragment} and the Transaction data.
305 | *
306 | * @param data The Transaction data to extend if Deep link info are found in
307 | * the {@link TransactionData#deepLinkIntent}.
308 | * @param The generic type for a valid Fragment.
309 | */
310 | @SuppressWarnings("unchecked")
311 | private void injectDeepLinkInfo(TransactionData data) {
312 | Intent deepLinkIntent = data.getDeepLinkIntent();
313 | if (deepLinkIntent != null) {
314 | for (FlowrDeepLinkHandler handler : deepLinkHandlers) {
315 | FlowrDeepLinkInfo info = handler.getDeepLinkInfoForIntent(deepLinkIntent);
316 |
317 | if (info != null) {
318 | data.setFragmentClass(info.fragment);
319 | Bundle dataArgs = data.getArgs();
320 | if (dataArgs != null) {
321 | data.getArgs().putAll(info.data);
322 | } else {
323 | data.setArgs(info.data);
324 | }
325 |
326 | break;
327 | }
328 | }
329 | }
330 | }
331 |
332 | /**
333 | * Set a Custom Animation to a Fragment transaction
334 | *
335 | * @param transaction The transaction that will
336 | * @param enterAnim The animation resource to be used when the next fragment enters.
337 | * @param exitAnim The animation resource to be used when the current fragment exits.
338 | * @param popEnterAnim The animation resource to be used when the previous fragment enters on back pressed.
339 | * @param popExitAnim The animation resource to be used when the current fragment exits on back pressed..
340 | */
341 | private void setCustomAnimations(FragmentTransaction transaction, @AnimRes int enterAnim,
342 | @AnimRes int exitAnim, @AnimRes int popEnterAnim, @AnimRes int popExitAnim) {
343 | transaction.setCustomAnimations(
344 | enterAnim,
345 | exitAnim,
346 | popEnterAnim,
347 | popExitAnim
348 | );
349 | }
350 |
351 | @Nullable
352 | private Fragment retrieveCurrentFragment() {
353 | Fragment fragment = null;
354 |
355 | if (screen != null) {
356 | fragment = screen.getScreenFragmentManager()
357 | .findFragmentById(mainContainerId);
358 | }
359 |
360 | return fragment;
361 | }
362 |
363 | @Override
364 | public void onBackStackChanged() {
365 | setCurrentFragment(retrieveCurrentFragment());
366 | }
367 |
368 | private void updateVisibilityState(Fragment fragment, boolean shown) {
369 | if (fragment instanceof FlowrFragment) {
370 | if (shown) {
371 | ((FlowrFragment) fragment).onShown();
372 | } else {
373 | ((FlowrFragment) fragment).onHidden();
374 | }
375 | }
376 | }
377 |
378 | /**
379 | * Closes the current activity if the fragments back stack is empty,
380 | * otherwise pop the top fragment from the stack.
381 | */
382 | public void close() {
383 | overrideBack = true;
384 |
385 | if (screen != null) {
386 | screen.invokeOnBackPressed();
387 | }
388 | }
389 |
390 | /**
391 | * Closes the current activity if the fragments back stack is empty,
392 | * otherwise pop the top n fragments from the stack.
393 | *
394 | * @param n the number of fragments to remove from the back stack
395 | */
396 | public void close(int n) {
397 | if (screen == null) {
398 | return;
399 | }
400 |
401 | int count = screen.getScreenFragmentManager().getBackStackEntryCount();
402 | if (count > 1) {
403 | String id = tagPrefix + (screen.getScreenFragmentManager().getBackStackEntryCount() - n);
404 | screen.getScreenFragmentManager()
405 | .popBackStackImmediate(id, FragmentManager.POP_BACK_STACK_INCLUSIVE);
406 | } else {
407 | close();
408 | }
409 | }
410 |
411 | /**
412 | * Closes the current activity if the fragments back stack is empty,
413 | * otherwise pop upto fragments with id Identifier from the stack.
414 | *
415 | * @param id Identifier of the committed transaction.
416 | */
417 | public void closeUpto(int id) {
418 | if (screen == null) {
419 | return;
420 | }
421 |
422 | int count = screen.getScreenFragmentManager().getBackStackEntryCount();
423 | if (count > 1) {
424 | screen.getScreenFragmentManager()
425 | .popBackStackImmediate(id, FragmentManager.POP_BACK_STACK_INCLUSIVE);
426 | } else {
427 | close();
428 | }
429 | }
430 |
431 | /**
432 | * Closes the current activity if the fragments back stack is empty,
433 | * otherwise pop the top fragment from the stack and publish the results response.
434 | *
435 | * @param resultResponse the results response to be published
436 | */
437 | public void closeWithResults(ResultResponse resultResponse) {
438 | closeWithResults(resultResponse, 1);
439 | }
440 |
441 | /**
442 | * Closes the current activity if the fragments back stack is empty,
443 | * otherwise pop the top n fragments from the stack and publish the results response.
444 | *
445 | * @param resultResponse the results response to be published
446 | * @param n the number of fragments to remove from the back stack
447 | */
448 | public void closeWithResults(ResultResponse resultResponse, int n) {
449 | close(n);
450 |
451 | if (resultResponse != null) {
452 | resultPublisher.publishResult(resultResponse);
453 | }
454 | }
455 |
456 | /**
457 | * Closes the current activity if the fragments back stack is empty,
458 | * otherwise pop upto fragments with id Identifier from the stack and publish the results response.
459 | *
460 | * @param resultResponse the results response to be published
461 | * @param id Identifier of the committed transaction.
462 | */
463 | public void closeUptoWithResults(ResultResponse resultResponse, int id) {
464 | closeUpto(id);
465 |
466 | if (resultResponse != null) {
467 | resultPublisher.publishResult(resultResponse);
468 | }
469 | }
470 |
471 | /**
472 | * Clears the fragments back stack.
473 | */
474 | public void clearBackStack() {
475 | if (screen != null) {
476 | screen.getScreenFragmentManager()
477 | .popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
478 | currentFragment = null;
479 | }
480 | }
481 |
482 | /**
483 | * Notify the current fragment of the back press event
484 | * and see if the fragment will handle it.
485 | *
486 | * @return true if the event was handled by the fragment
487 | */
488 | public boolean onBackPressed() {
489 | if (!overrideBack && currentFragment instanceof FlowrFragment &&
490 | ((FlowrFragment) currentFragment).onBackPressed()) {
491 | return true;
492 | }
493 |
494 | overrideBack = false;
495 | return false;
496 | }
497 |
498 | public void onNavigationIconClicked() {
499 | if (!(currentFragment instanceof FlowrFragment &&
500 | ((FlowrFragment) currentFragment).onNavigationIconClick())) {
501 | close();
502 | }
503 | }
504 |
505 | /**
506 | * Checks if the current fragment is the home fragment.
507 | *
508 | * @return true if the current fragment is the home fragment
509 | */
510 | public boolean isHomeFragment() {
511 | return screen == null || screen.getScreenFragmentManager().getBackStackEntryCount() == 0;
512 | }
513 |
514 | /**
515 | * Returns the fragment currently being displayed for this screen,
516 | *
517 | * @return the fragment currently being displayed
518 | */
519 | @Nullable
520 | public Fragment getCurrentFragment() {
521 | return currentFragment;
522 | }
523 |
524 | private void setCurrentFragment(@Nullable Fragment newFragment) {
525 | if (currentFragment != newFragment) {
526 | updateVisibilityState(currentFragment, false);
527 | currentFragment = newFragment;
528 | updateVisibilityState(currentFragment, true);
529 | syncScreenState();
530 | }
531 | }
532 |
533 | /**
534 | * Called by the {@link android.app.Activity#onPostCreate(Bundle)} to update
535 | * the state of the container screen.
536 | */
537 | public void syncScreenState() {
538 | int screenOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
539 | int navigationBarColor = -1;
540 |
541 | if (currentFragment instanceof FlowrFragment) {
542 | screenOrientation = ((FlowrFragment) currentFragment).getScreenOrientation();
543 | navigationBarColor = ((FlowrFragment) currentFragment).getNavigationBarColor();
544 | }
545 |
546 | if (screen != null) {
547 | screen.onCurrentFragmentChanged(getCurrentFragment());
548 | screen.setScreenOrientation(screenOrientation);
549 | screen.setNavigationBarColor(navigationBarColor);
550 | }
551 |
552 | syncToolbarState();
553 | syncDrawerState();
554 | }
555 |
556 | private void syncToolbarState() {
557 | if (toolbarHandler == null) {
558 | return;
559 | }
560 |
561 | NavigationIconType iconType = NavigationIconType.HIDDEN;
562 | String title = null;
563 |
564 | if (currentFragment instanceof FlowrFragment) {
565 | iconType = ((FlowrFragment) currentFragment).getNavigationIconType();
566 | title = ((FlowrFragment) currentFragment).getTitle();
567 | }
568 |
569 | if (iconType == NavigationIconType.CUSTOM) {
570 | toolbarHandler.setCustomNavigationIcon(((FlowrFragment) currentFragment).getNavigationIcon());
571 | } else {
572 | toolbarHandler.setNavigationIcon(iconType);
573 | }
574 |
575 | toolbarHandler.setToolbarVisible(!(currentFragment instanceof FlowrFragment) ||
576 | ((FlowrFragment) currentFragment).isToolbarVisible());
577 |
578 | toolbarHandler.setToolbarTitle(title != null ? title : "");
579 | }
580 |
581 | private void syncDrawerState() {
582 | if (drawerHandler == null) {
583 | return;
584 | }
585 |
586 | drawerHandler.setDrawerEnabled(!(currentFragment instanceof FlowrFragment) ||
587 | ((FlowrFragment) currentFragment).isDrawerEnabled());
588 | }
589 |
590 | /**
591 | * Creates a new {@link Builder} instance to be used to display a fragment
592 | *
593 | * @param fragmentClass the class for the fragment to be displayed
594 | * @return a new {@link Builder} instance
595 | */
596 | public Builder open(Class extends T> fragmentClass) {
597 | return new Builder<>(fragmentClass);
598 | }
599 |
600 | /**
601 | * Creates a new {@link Builder} instance to be used to display a fragment
602 | *
603 | * @param deepLinkIntent An Intent to parse and open the appropriate Fragment.
604 | * @return a new {@link Builder} instance
605 | */
606 | public Builder open(Intent deepLinkIntent) {
607 | return new Builder<>(deepLinkIntent);
608 | }
609 |
610 | /**
611 | * Creates a new {@link Builder} instance to be used to display a fragment.
612 | *
613 | * @param intent An intent that contains a possible Deep link.
614 | * @param fragmentClass The fragment to use if the intent doesn't contain a Deep Link.
615 | * @param The proper Fragment type.
616 | * @return a new {@link Builder} instance
617 | */
618 | public Builder open(Intent intent, Class extends T> fragmentClass) {
619 | return new Builder<>(intent, fragmentClass);
620 | }
621 |
622 | /**
623 | * Creates a new {@link Builder} instance to be used to display a fragment
624 | *
625 | * @param path the path of a Fragment annotated with {@link com.fueled.flowr.annotations.DeepLink}
626 | * @return a new {@link Builder} instance
627 | */
628 | public Builder open(String path) {
629 | Uri uri = Uri.parse(path);
630 | Intent intent = new Intent();
631 | intent.setData(uri);
632 | return open(intent);
633 | }
634 |
635 |
636 | /**
637 | * The default enter animation to be used for fragment transactions
638 | *
639 | * @return the default fragment enter animation
640 | */
641 | protected int getDefaultEnterAnimation() {
642 | return FragmentTransaction.TRANSIT_NONE;
643 | }
644 |
645 | /**
646 | * The default exit animation to be used for fragment transactions
647 | *
648 | * @return the default fragment exit animation
649 | */
650 | protected int getDefaultExitAnimation() {
651 | return FragmentTransaction.TRANSIT_NONE;
652 | }
653 |
654 | /**
655 | * The default pop enter animation to be used for fragment transactions
656 | *
657 | * @return the default fragment pop enter animation
658 | */
659 | protected int getDefaultPopEnterAnimation() {
660 | return FragmentTransaction.TRANSIT_NONE;
661 | }
662 |
663 | /**
664 | * The default pop exit animation to be used for fragment transactions
665 | *
666 | * @return the default fragment pop exit animation
667 | */
668 | protected int getDefaultPopExitAnimation() {
669 | return FragmentTransaction.TRANSIT_NONE;
670 | }
671 |
672 | @Override
673 | public void onClick(View view) {
674 | onNavigationIconClicked();
675 | }
676 |
677 | public void onDestroy() {
678 | setRouterScreen(null);
679 | setToolbarHandler(null);
680 | setDrawerHandler(null);
681 | }
682 |
683 | /**
684 | * This builder class is used to show a new fragment inside the current activity
685 | */
686 | public class Builder {
687 |
688 | private TransactionData data;
689 |
690 | public Builder(Class extends T> fragmentClass) {
691 | data = new TransactionData<>(fragmentClass, getDefaultEnterAnimation(),
692 | getDefaultExitAnimation(), getDefaultPopEnterAnimation(),
693 | getDefaultPopExitAnimation());
694 | }
695 |
696 | public Builder(Intent intent) {
697 | data = new TransactionData<>(null, getDefaultEnterAnimation(),
698 | getDefaultExitAnimation(), getDefaultPopEnterAnimation(),
699 | getDefaultPopExitAnimation());
700 | data.setDeepLinkIntent(intent);
701 | }
702 |
703 | public Builder(Intent intent, Class extends T> fragmentClass) {
704 | data = new TransactionData<>(fragmentClass, getDefaultEnterAnimation(),
705 | getDefaultExitAnimation(), getDefaultPopEnterAnimation(),
706 | getDefaultPopExitAnimation());
707 | data.setDeepLinkIntent(intent);
708 | }
709 |
710 | /**
711 | * Sets the construction arguments for fragment to be displayed.
712 | *
713 | * @param args the construction arguments for this fragment.
714 | */
715 | public Builder setData(Bundle args) {
716 | data.setArgs(args);
717 | return this;
718 | }
719 |
720 | /**
721 | * Specifies if this fragment should not be added to the back stack.
722 | */
723 | public Builder skipBackStack(boolean skipBackStack) {
724 | data.setSkipBackStack(skipBackStack);
725 | return this;
726 | }
727 |
728 | /**
729 | * Specifies if the fragment manager back stack should be cleared.
730 | */
731 | public Builder clearBackStack(boolean clearBackStack) {
732 | data.setClearBackStack(clearBackStack);
733 | return this;
734 | }
735 |
736 | /**
737 | * Specifies if this fragment should replace the current fragment.
738 | */
739 | public Builder replaceCurrentFragment(boolean replaceCurrentFragment) {
740 | data.setReplaceCurrentFragment(replaceCurrentFragment);
741 | return this;
742 | }
743 |
744 | /**
745 | * Specifies the animations to be used for this transaction.
746 | *
747 | * @param enterAnim the fragment enter animation.
748 | * @param exitAnim the fragment exit animation.
749 | */
750 | public Builder setCustomTransactionAnimation(@AnimRes int enterAnim, @AnimRes int exitAnim) {
751 | return setCustomTransactionAnimation(enterAnim, FragmentTransaction.TRANSIT_NONE,
752 | FragmentTransaction.TRANSIT_NONE, exitAnim);
753 | }
754 |
755 |
756 | /**
757 | * Set a Custom Animation to a Fragment transaction.
758 | *
759 | * @param enterAnim The animation resource to be used when the next fragment enters.
760 | * @param exitAnim The animation resource to be used when the current fragment exits.
761 | * @param popEnterAnim The animation resource to be used when the previous fragment enters on back pressed.
762 | * @param popExitAnim The animation resource to be used when the current fragment exits on back pressed..
763 | */
764 | public Builder setCustomTransactionAnimation(@AnimRes int enterAnim, @AnimRes int exitAnim,
765 | @AnimRes int popEnterAnim, @AnimRes int popExitAnim) {
766 | data.setEnterAnim(enterAnim);
767 | data.setExitAnim(exitAnim);
768 | data.setPopEnterAnim(popEnterAnim);
769 | data.setPopExitAnim(popExitAnim);
770 | return this;
771 |
772 | }
773 |
774 | /**
775 | * Don't use any animations for this transaction
776 | */
777 | public Builder noTransactionAnimation() {
778 | return setCustomTransactionAnimation(FragmentTransaction.TRANSIT_NONE,
779 | FragmentTransaction.TRANSIT_NONE);
780 | }
781 |
782 | /**
783 | * Displays the fragment using this builder configurations.
784 | *
785 | * @return id Identifier of the committed transaction.
786 | */
787 | public int displayFragment() {
788 | return Flowr.this.displayFragment(data);
789 | }
790 |
791 | /**
792 | * Displays the fragment for results using this builder configurations.
793 | *
794 | * @param fragmentId a unique ID that the fragment requesting the results can be identified by,
795 | * it will be later used to deliver the results to the correct fragment instance.
796 | * @param requestCode this code will be returned in {@link ResultResponse} when the fragment is closed,
797 | * and it can be used to identify the request from which the results were returned.
798 | *
799 | * @return id Identifier of the committed transaction.
800 | */
801 | public int displayFragmentForResults(String fragmentId, int requestCode) {
802 | if (!TextUtils.isEmpty(fragmentId)) {
803 | if (data.getArgs() == null) {
804 | data.setArgs(new Bundle());
805 | }
806 |
807 | data.getArgs().putBundle(KEY_REQUEST_BUNDLE,
808 | getResultRequestBundle(fragmentId, requestCode));
809 | }
810 |
811 | return Flowr.this.displayFragment(data);
812 | }
813 |
814 | private Bundle getResultRequestBundle(String fragmentId, int requestCode) {
815 | Bundle request = new Bundle();
816 | request.putString(KEY_FRAGMENT_ID, fragmentId);
817 | request.putInt(KEY_REQUEST_CODE, requestCode);
818 | return request;
819 | }
820 | }
821 |
822 | }
--------------------------------------------------------------------------------