All Projects → ShaishavGandhi → navigator

ShaishavGandhi / navigator

Licence: Apache-2.0 license
Annotation processor that eliminates navigation and Bundle boilerplate

Programming Languages

java
68154 projects - #9 most used programming language
kotlin
9241 projects

Projects that are alternatives of or similar to navigator

Flownav
Annotation processor that provides better navigation on android multi-modules projects 🛳.
Stars: ✭ 122 (+838.46%)
Mutual labels:  fragments, navigation, annotation-processor
Scene
Android Single Activity Applications framework without Fragment.
Stars: ✭ 1,793 (+13692.31%)
Mutual labels:  fragments, navigation, activity
Androidpreferenceactivity
Provides an alternative implementation of Android's PreferenceActivity
Stars: ✭ 63 (+384.62%)
Mutual labels:  navigation, activity
Dart
Extras binding and intent builders for Android apps.
Stars: ✭ 1,203 (+9153.85%)
Mutual labels:  navigation, annotation-processor
Simple Stack
[ACTIVE] Simple Stack, a backstack library / navigation framework for simpler navigation and state management (for fragments, views, or whatevers).
Stars: ✭ 1,012 (+7684.62%)
Mutual labels:  fragments, navigation
Navigator
Android Multi-module navigator, trying to find a way to navigate into a modularized android project
Stars: ✭ 131 (+907.69%)
Mutual labels:  navigation, activity
kaptain
👨‍✈️ multi-module navigation on Android has never been so easier!
Stars: ✭ 24 (+84.62%)
Mutual labels:  navigation, activity
Alligator
Alligator is a modern Android navigation library that will help to organize your navigation code in clean and testable way.
Stars: ✭ 287 (+2107.69%)
Mutual labels:  fragments, navigation
Bundler
🎁 Android Intent & Bundle extensions that insert and retrieve values elegantly.
Stars: ✭ 195 (+1400%)
Mutual labels:  bundle, activity
Tieguanyin
Activity Builder.
Stars: ✭ 113 (+769.23%)
Mutual labels:  fragments, activity
Flowr
FlowR is a wrapper class around the Fragment Manager.
Stars: ✭ 123 (+846.15%)
Mutual labels:  fragments, navigation
Fragnav
An Android library for managing multiple stacks of fragments
Stars: ✭ 1,379 (+10507.69%)
Mutual labels:  fragments, navigation
Cicerone
🚦 Cicerone is a lightweight library that makes the navigation in an Android app easy.
Stars: ✭ 2,345 (+17938.46%)
Mutual labels:  fragments, navigation
Base
🍁 Base是针对于Android开发封装好一些常用的基类,主要包括通用的Adapter、Activity、Fragment、Dialog等、和一些常用的Util类,只为更简单。
Stars: ✭ 249 (+1815.38%)
Mutual labels:  fragments, activity
Pursuit-Core-Android
Pursuit Core Android
Stars: ✭ 45 (+246.15%)
Mutual labels:  fragments
My Android Garage
A quick reference guide for Android development.
Stars: ✭ 66 (+407.69%)
Mutual labels:  activity
Easy-Fragment-Argument
This library will help you to pass and receive fragment arguments in easier way
Stars: ✭ 17 (+30.77%)
Mutual labels:  fragments
grunt-angular-combine
Grunt task for combining AngularJS partials into a single HTML file.
Stars: ✭ 16 (+23.08%)
Mutual labels:  fragments
socketio
No description or website provided.
Stars: ✭ 23 (+76.92%)
Mutual labels:  bundle
awsBundle
Symfony AWS Bundle (supports Symfony 2, 3 and 4)
Stars: ✭ 18 (+38.46%)
Mutual labels:  bundle

Navigator

CircleCI branch

Utility library that generates activity navigation boilerplate for you, along with all it's bindings.

Download

Maven Central

dependencies {
  implementation 'com.shaishavgandhi.navigator:navigator:x.y.z'
  annotationProcessor 'com.shaishavgandhi.navigator:navigator-compiler:x.y.z'
  
  // Or if using Kotlin
  kapt 'com.shaishavgandhi.navigator:navigator-compiler:x.y.z'
}

Snapshots of the development version are available in Sonatype's snapshots repository.

Use Case

Navigating to another activity requires a lot of boilerplate code in both activites.

Source activity:

public final class MainActivity extends Activity {
  
  protected void openDetailActivity() {
    Intent intent = new Intent(context, DetailActivity.class);
    intent.putParcelableExtra("user", user);
    intent.putString("source", source);
    intent.putString("title", title);
    intent.putString("subtitle", subtitle);
    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK || Intent.FLAG_ACTIVITY_CLEAR_TASK);
    startActivity(intent);
  }

}

The destination activity is even more complicated:

public final class DetailActivity extends Activity {
  
  String title;
  String source;
  String subtitle;
 
  @Override protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    parseIntent();
  }
  
  private void parseIntent() {
    if (getIntent() != null && getIntent().getExtras() != null) {
      Bundle bundle = getIntent().getExtras();
      if (bundle.containsKey("title")) {
        title = bundle.getString("title");
      }
      
      // So on for every attribute
    
    }
  }
}

There are lots of things that can go wrong with this.

  1. There is no type safety.
  2. There is no implicit contract between any two activities which state what is required and what is optional.
  3. Everything is nullable and that's not good.

Usage

Navigator provides a simple builder API, that provides an implicit contract between the source and destination activites, as well as removes the binding boilerplate for you.

You only need to annotate your fields with @Extra in your destination activity and Navigator will generate a builder API for you to start that activity.

By default, Navigator will treat all fields with @Extra as necessary and required for the destination activity to start. Fields that are not required by the activity but might be expected from some places can be annotated with @Nullable from android support-annotations.

Fields annotated with @Extra must be public or package-private. If the fields are private, then you must provide a setter for them that will be used by Navigator to bind the data.

Using the same example:

public final class DetailActivity extends Activity {
  
  @Extra String title; // Annotate with @Extra to tell Navigator that this is required when opening activity
  @Extra @Nullable String source; // @Nullable tells Navigator that this is an optional extra
  @Extra String subtitle;
 
  @Override protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    DetailActivityBinder.bind(this); // Automatically bind extras
  }
}
public final class MainActivity extends Activity {
  
  protected void openDetailActivity() {
    DetailActivityBuilder.builder(title, subtitle) // Required extras by ActivityC go in static factory method
      .setSource(source) // optional extras
      .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK || Intent.FLAG_ACTIVITY_CLEAR_TASK)
      .start(this);
  }

}

The sample example would work in Kotlin as well.

Fragments

The same examples mentioned above work for fragments as well. However, Navigator is not interested in being a navigation library for fragments. A whole different library can be written about that.

Navigator does support binding of arguments passed to the fragment as well as constructing the arguments required for a fragment in an API that is very similar to Activities.

Get arguments

Bundle arguments = DetailFragmentBuilder.builder(userList)
                .setPoints(points)
                .getBundle();

MyFragment fragment = new MyFragment();
fragment.setArguments(arguments);

Bind arguments

class DetailFragment extends Fragment {

    @Extra User user;
    @Extra Point points;

    @Override public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        DetailFragmentBinder.bind(this);
    }

}

Alternative To SafeArgs

If you've used the Jetpack Navigation library from Google, you might have used SafeArgs to safely pass data between two fragments. Navigator seems to have a better approach than SafeArgs since you can bind all your Fragment variables in one go instead of actually getting them individually like in SafeArgs. You can also use handy Kotlin extensions that make the API better. More on that in the next section.

Kotlin

Navigator has first class support for Kotlin and it's language features. If you use kapt as your annotation processor, Navigator will generate handy Kotlin extensions for you which simplify the API.

Bind arguments

class DetailActivity : Activity() {
  
  @Extra lateinit var title: String 
  @Extra var source: String? = null // null type indicates that it is optional
  @Extra lateinit var subtitle: String
 
  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    bind() // Simply call bind extension on DetailActivity
  }
}

Using kapt will also simplify your API when using it in a Java class.

public class DetailActivity extends Activity {
    
    @Extra String message;
    
    @Override protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        DetailActivityNavigator.bind(this); // Generated kotlin extension with a nicer API
    }
    
}

Creating Builder

You can also use a handy Kotlin extension to get rid of the static factory builders. If you're preparing Bundle for DetailFragment, you can do:

class MainFragment: Fragment() {
  
  fun showDetail(post: Post, authors: List<Author>?) {
    val bundle = detailFragmentBuilder(post)
                   .setAuthors(authors)
                   
    val fragment = DetailFragment()
    fragment.setArguments(bundle)
    replaceFragment(fragment)
  }

}

Advanced Usage

Navigator exposes most ways to start an activity.

Start Activity For Result

DetailActivityBuilder.builder(users, source)
               .setPoints(points)
               .startForResult(activity, requestCode)

Start Activity With Transition Bundle

DetailActivityBuilder.builder(users, source)
               .setPoints(points)
               .startWithExtras(activity, transitionBundle)

Supply your own keys

It is easy to transition to Navigator in a large codebase. For example, you can mark a variable as @Extra in an existing class, which already has logic to parse out the Bundle. But you cannot possibly change the key to that particular variable in every place from which it's called. With Navigator, you can easily specify your own custom key which will be used to execute the binding of all the extras. Example:

public final class MyActivity extends Activity {
   
   @Extra(key = FragmentExtras.CUSTOM_KEY) // Custom key that other classes use when invoking MyActivity
   String extra;
   
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       MyActivityNavigator.bind(this);
   }
}

Bundle

In cases where you just want to use the type-safety and implicit contract of Navigator, you can easily use the builder to get the bundle created by Navigator

Bundle bundle = DetailActivityBuilder.builder(users, source)
               .setPoints(points)
               .getBundle();

Add Ons

If you're using Kotlin, BundleX is a useful add-on to Navigator. BundleX generates extensions on the Bundle using the same @Extra annotation.

class MyActivity: AppcompatActivity {
  
  @Extra lateinit var message: String
  
  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
        
    val bundle = intent.extras
    message = bundle.getMessage(defaultValue = "hello world") // Generated extension
  }
  
  fun startActivity(message: String) {
    val bundle = Bundle()
    bundle.putMessage(message) // Use generated setter 
    // Start activity with bundle
  }
    
}

Simply add to your build.gradle

dependencies {
  kapt 'com.shaishavgandhi:bundlex-compiler:x.y.z'
}

Thanks

License

Copyright 2018 Shaishav Gandhi.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].