Skip to content
This repository was archived by the owner on Jul 24, 2020. It is now read-only.
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
341 changes: 40 additions & 301 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
***This document is under construction***

# Android Coding Guidelines Android
# Android Coding Guidelines

## 1. Project structure

Expand All @@ -10,327 +10,66 @@ New projects should follow the Android Gradle project structure that is defined

It's recommended to structure in the following way:

### App
mainpackage
* homescreen // 1.1.1. Activity level
* category // 1.1.2 Feature of the activity
* di // 1.1.3 Dependency Injection
* presenter // 1.1.4 Presentation layer
* repository // 1.1.5 Repository layer
* navigator // 1.1.6 Navigator layer
* search
* di
* presenter
* repository
* navigator

* network
* picasso
* dashboard
* matchfeed
* model
* injection
* modules
* component
* qualifiers
* ui
* dashboard
* notifications
* matchfeed
* persistence
* dashboard
* matchfeed
* analytics
* ads
* utils

#### 1.1.1 Network
#### 1.1.1 Activity level

Contains all the network related files (Http services, deserializers, image loading...). Try to group network files for different parts of the app in different subpackages.
Activity providing a context for multiple features

#### 1.1.2 Model
#### 1.1.2 Feature level

Contains all the model related files.
Feature level encapsulates all packages and classes required for delivering specific feature.

#### 1.1.3 Injection

We use depency injection usually using [Dagger 2](http://google.github.io/dagger/). This package will contain all the dependency injection related files (modules, components, qualifiers, scopes...).

#### 1.1.4 Ui
Injection package will contain all components, modules, qualifiers and scopes used by this feature. If this feature requires more elaborate injection it is worth to split the package further by role

Contains all ui related files (Views, adapters...). Try to group ui files for different parts of the app into different subpackages.

#### 1.1.5 Persistence
* components
* modules
* scopes
* qualifiers

Contains all the persistence related files (Shared preferences, sqlite, files storage...). Try to group persistence files for different parts of the app into different subpackages.
#### 1.1.4 Presenter

#### 1.1.6 Analytics
Presentation layer will contain interface and implementation(s) of view logic

Contains all the analytics related files
#### 1.1.5 Repository

#### 1.1.7 Ads
Repository package will encapsulate all interfaces and logic around requesting data from external and internal sources. It may contain packages e.g.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Personal opinion but I'm not a fan of having the network, model, parsers and persistence layer together

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Network should definitely separated from repository.


Contains all the adverts related files
* parsers
* models
* handlers

#### 1.1.8 Utils
#### 1.1.6 Navigator

Contains all the utility related files, usually they are files containing only static methods that helps to do some specific task that it's repeated all over the app and doesn't really belong to any other part of the app (TimeUtils, FontUtils, ViewUtils...)
Navigator interface defines all navigational operations that can be performed on Fragments/Activity inside this Activity. e.g.

public interface HomeSearchNavigator {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've seen "Navigator" objects implemented at TAB and in my personal opinion they end up always being a mess and implemented by the activities, which specially it we are following the MVC pattern this logic is part of the controller. The controller could delegate this logic to a "navigator" but generally if the app is not really complex it's not even worth it, plus I recommend the intent to be created as a static method in the activity you want to launch, that way is really easy to know entry points to that activity/fragment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I might be more straightforward to do it the way Juan was saying. Subject to complexity.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also second the static intent route

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that activity gets messy, depending on size and structure of app (activity/fragments).

void onFixedSearch(String search);
void onRawSearch(String search);
void onOpenSettings();
void onExit();
}

Implementation will handle creation of intents and dispatching them.


## 2. Coding Style

At TAB we follow [Android Code Style](https://source.android.com/source/code-style.html#follow-field-naming-conventions) guidelines


On top of rules listed there we have few extra rules and exceptions:

### 2.1 Name conventions

- **Do NOT use m or s prefix in fields inside POJO (models, events)**

*good:*

``` java
public class League {
private String id;
private String name;

public String getId() {
return id;
}

public void setId(String id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
}
```

*bad:*

``` java
public class League {
private String mId;
private String mName;

public String getId() {
return mId;
}

public void setId(String id) {
mId = id;
}

public String getName() {
return mName;
}

public void setName(String name) {
mName = name;
}
}
```

- **Use full words**

*good:*

mInitalDownloadTimestamp

*bad:*

mInitDlTimestamp

- Try to place most important part of the name in the first or last part of the name

*good:*

mInitialDownloadTimestamp

*not so good:*

mDownloadInitialTimestamp

(important part is that this is a timestamp and it is initial one)

- **Be explicit in choosing names**

*good:*

calculateDownloadDuration()

*not so good:*

duration()

### 2.2 Constants

- Always user string resources for user facing strings.
- Define all Intent extra keys, FragmentArgument keys, FragmentManager tags, "magic" values, etc as constants

*good:*

``` java
public class MyFragment extends Fragment{
private static final String ARG_FIRST = "arg1";
private static final String ARG_SECOND = "arg2";

public static MyFragment newInstance(String arg1, int arg2){
Bundle args = new Bundle();
args.put(ARGS_FIRST, arg1);
args.put(ARGS_SECOND, arg2);
.....
}
}
```

*bad:*

``` java
public class MyFragment extends Fragment{
public static MyFragment newInstance(String arg1, int arg2){
Bundle args = new Bundle();
args.put("arg1" , arg1);
args.put("arg2" , arg2);
.....
}
}
```

- Define constants into the file where they're mostly used or makes sense logically.
- Don't create specific files with loads of constants that it's not clear where they belong to
- Don't use enum when they can be replaced by constants. Use constants and make use of [Enumerated Annotations](https://developer.android.com/tools/debugging/annotations.html#enum-annotations) when possible

*good:*

``` java
public static final int ONE = 0;
public static final int TWO = 1;
publci static final int THREE = 2;
```

*bad:*

``` java
public enum Values{
ONE(0), TWO(1), THREE(2)
}
```

### 2.3 Use Standard Brace Style

- Avoid one line braceless conditions

*good:*

``` java
if (condtion) {
body();
}
```

*bad:*

```
if (condition)
body()
```

*or:*

``` java
if (condition) body();
```

### 2.4 Switch statements

- Braces should be on same line as case
- One space between the switch keyword and the open parenthesis, one space between the close parenthesis and the opening brace.
- Don't use "magic" words or numbers for each case, use always constants

*good:*

``` java
private static final int CASE_1 = 0;
private static final int CASE_2 = 1;
....

switch (expression) {
case CASE_1:
// code
break;
case CASE_2:
// code
break;
default:
// default code
}
```
*bad:*

``` java
switch (expression) {
case 0:
// code
break;
case 0:
// code
break;
default:
// default code
}
```

- Don't use the default case if it's not required

*good:*

``` java
switch (expresion) {
case CASE_1:
// code
break;
case CASE_2:
// code
break;
}
```

*bad:*

``` java
switch (expresion) {
case CASE_1:
// code
break;
case CASE_2:
// code
break;
default:
}
```

### 2.5 Use Standard Java Annotations

- Even a single annotation should be above the annotated field/method/class

*good:*

@Override
public void onCrate(Bundle savedInstanceState) { ... }

*bad:*

@Override public void onCreate(Bundle savedInstanceState) { ... }

(we want to keep method signature lines short as possible to use more explicit method names)


### 2.6 Log Sparingly

Implement or use Logger which allows filtering log calls per subsystem. It's recommended to use [Timber](https://github.com/JakeWharton/timber)

*good:*
We are migrating from AOSP (Android Open Source Project) code guidelines to Google Java Style which can be found here

TabLog.e(TabLog.UI,"Cannot determine image size");
[Google Java Style](https://google.github.io/styleguide/javaguide.html)

*not so good:*

Log.e("App", "Cannot determine image size");