Skip to content

Latest commit

 

History

History
122 lines (88 loc) · 6.63 KB

CODE-GUIDELINES.md

File metadata and controls

122 lines (88 loc) · 6.63 KB

Code style guidelines

Java style guidelines

Follow the Android style rules and the Google Java style guide.

XML style guidelines

Follow these naming conventions in Android XML files:

  • Attributes (attr): shouldBeCamelCased

  • String, dimension and color names: should_be_snake_cased

  • Themes and Styles: ShouldBePascalCased and should also be qualified in a similar manner to Java package names like <Type>.<Package>.<Name>.... For instance:

    Theme.Collect.Light
    TextAppearance.Collect.H1.Purple
    Widget.Collect.Button
    Widget.Collect.Button.BigRed

UI components style guidelines

Ensure that the added UI components are compatible with both light and dark themes. Follow the below points to get the color for coloring the UI components like text and icons instead of directly using color values (eg. #000000 or R.color.color_name).

UI Component Java Xml (layouts, drawables, vectors):
text color themeUtils.getPrimaryTextColor() ?primaryTextColor
accent color themeUtils.getAccentColor() ?colorAccent
icon color themeUtils.getIconColor() ?iconColor

Custom view guidelines

When creating or refactoring views, keep in mind our vision of an "ideal" view:

  • Views should generally be as dumb and as stateless as possible
  • Views shouldn't interact with other layers of your application (repositories, network etc). They should be able to take in and render data (via setters) and emit "meaningful" events via listeners.
  • A view should have a single public Listener sub interface and setListener method. Methods on the interface should correspond to "meaningful" events. If the view is a button that could just be onClick but if it's a volume slider this might be something like onVolumeChanged.
  • Views would ideally have just one setter for data but more complex views (often that have many subviews) that take in data at different times may have more - fewer setters is better as it's less changing state in the view.
  • Views that render more than one kind of data (and that have more than one setter) might benefit from a render method that encapsulates all the logic around displaying the state of a view.

Strings

Always use string resources instead of literal strings. This ensures wording consistency across the project and also enables full translation of the app. Only make changes to the base res/values/strings.xml English file and not to the other language files. The translated files are generated from Transifex where translations can be submitted by the community. Names of software packages or other untranslatable strings should be placed in res/values/untranslated.xml.

Strings that represent very rare failure cases or that are meant more for ODK developers to use for troubleshooting rather than directly for users may be written as literal strings. This reduces the burden on translators and makes it easier for developers to troubleshoot edge cases without having to look up translations.

Dependency injection

As much as possible to facilitate simpler, more modular and more testable components you should follow the Dependency Inversion principle in Collect Code. An example tutorial on this concept can be found here.

Because many Android components (Activity and Fragment for instance) don't allow us control over their constructors Collect uses Dagger to 'inject' dependencies. The configuration for Dagger can be found in AppDepdendencyComponent. For any normal objects it is probably best to avoid Dagger and use normal Java constructors.

While it's important to read the Dagger documentation we've provided some basic instructions on how to use Dagger within Collect below.

Providing dependencies

To declare a new dependency that objects can inject add a @Provider method to the AppDepedencyModule:

@Provider
public MyDependency providesMyDependency() {
    return MyDependency();
}

You can also have Dagger return the same instance every time (i.e. a Singleton) by annotating the method with @Singleton as well.

Injecting dependencies into Activity/Fragment objects

To inject a dependency into the Activity you first need to make Dagger aware it's injecting into that Activity by adding an inject to the AppDependencyComponent (if it's not already there):

void inject(MyActivity activity);

Then define a field with the @Inject annotation in your Activity:

@Inject
MyDependency dependency;

To have Dagger inject the dependency you need to hook the injection into the Activity's onCreate (as this is the first part of the lifecycle we have access to):

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    DaggerUtils.getComponent(this).inject(this);
}

For Fragment objects you should hook into the onAttach lifecycle method instead:

@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);
    DaggerUtils.getComponent(activity).inject(this);
}

Swapping out dependencies in tests

To swap out depdendencies in a Robolectric test you can override the module the Application object uses to inject objects using provided helpers:

@Before
public void setup() {
    MyDependency mocked = mock(MyDependency.class);
    RobolectricHelpers.overrideAppDependencyModule(new AppDependencyModule() {
      @Override
      public MyDependency providesMyDependency() {
        return mocked;
      }
    });
}

Code from external sources

ODK Collect is released under the Apache 2.0 license. Please make sure that any code you include is an OSI-approved permissive license. Please note that if no license is specified for a piece of code or if it has an incompatible license such as GPL, using it puts the project at legal risk.

Sites with compatible licenses (including StackOverflow) will sometimes provide exactly the code snippet needed to solve a problem. You are encouraged to use such snippets in ODK Collect as long as you attribute them by including a direct link to the source. In addition to complying with the content license, this provides useful context for anyone reading the code.