Name | Student Id | Github |
---|---|---|
Goh Ying Ming, Bryce | 1005016 | @brycegoh |
Sean Chen Zhi En | 1005122 | @seancze |
Umang Sanjeev Gupta | 1005393 | @Usgupta |
Tee Zhi Zhang | 1005136 | @ZhiZhangT |
Lim Shu Hui Pamela | 1005647 | @pamz23 |
Sim Shang Hong | 1005500 | @shanghongsim |
For submission purposes, API keys has been included in the repo. API keys will be disabled after grading to avoid incurring any charges.
For in-app authentication, you can either create an account or use an existing one which is as follows:
username: [email protected]
password: umangg
HawkerGo aims to be a single, centralised resource for users to view and review hawker stalls and hawkers centres in Singapore. We organise information on hawker centre stalls and present them in an easy to access manner (e.g. opening hours, stall number etc). We also help people locate suitable food stalls in each hawker centre and populate our app with real and updated information from their own experiences. Ultimately, we also wish to build a vibrant community of hawker lovers on our platform, and keep the hawker culture in Singapore alive!
Please stick to the folder structure where each folder will contain code responsible for different aspects of the app.
Folders:
app/src/main/java/com/example/hawkergo
|
└─── activities # All activties goes here
│ └─── baseActivities # Activities to extend from these classes.
│ # Contains common logic Eg. screens that requires checking of authenticated user
│
│ └─── helpers # Helper classes or classes with abstracted logic used in activies.
│
└─── adapters # All customer adapters goes here
│
└─── fragments # All fragments goes here
│
└─── models # All data models goes here
│
└─── services # All Database interactions related code goes here
│ └─── interfaces # Contains interfaces used in Services class
│ └─── utils # General utility/helper classes
│
└─── utils # App-wide helper classes goes here
│
└─── res # App-wide resources goes here
│ └─── color # Colors based on widget states
│ └─── layout # Layouts used in actitivies/Fragments
│ └─── menu # Menu and Action bar / toolbar layouts
│ └─── values # Globally defined colors, styles and themes
│
- If you have a new collection, define them in
utils/FirebaseConstants
public class FirebaseConstants
{
public static class CollectionIds {
public static String HAWKER_CENTRES = "hawkerCentres";
public static String HAWKER_STALLS = "hawkerStalls";
public static String TAGS = "tags";
public static String REVIEWS = "reviews";
/**
* DEFINE NEW COLLECTION NAME HERE
*
*/
}
// ...
}
- Using an interface in
services/interface/
named_____Queryable
, list down what CRUD methods you require.
public interface HawkerCentreQueryable {
static void addHawkerCentre(HawkerCentre hawkerCentre, DbEventHandler<String> eventHandler {};
static void deleteHawkerCentre(String hawkerCentreID,DbEventHandler<String> callBack){};
static void getHawkerCentreByID(String hawkerCentreID, DbEventHandler<HawkerCentre> eventHandler){};
static void addStallIntoHawkerCentre(String hawkerCentreID, HawkerStall newHawkerStall, DbEventHandler<String> eventHandler){};
static void getAllHawkerCentres(DbEventHandler<List<HawkerCentre>> eventHandler){};
}
- Define your Services class as shown below.
Things to take note:
- Implement your previously defined Queryable interface.
- Define the Collection name and reference
- Document what your method is suppose to do
- Always accept a
DbEventHandler
as a param. This is used by the activities to define callback functions DbEventHandler
expects a Generic type which is what you expect the DB to return- Always call the
.onSuccess
and pass in the deserialised data using firebase's.toObject
or.toObjects
for a list of data
public class HawkerStallsService implements HawkerStallQueryable {
private static final String collectionId = FirebaseConstants.CollectionIds.HAWKER_STALLS;
private static final CollectionReference collectionRef = FirebaseConstants.getCollectionReference(collectionId);
/**
* Get a hawker stall by its ID
* @param hawkerStallID ID of the hawker stall document
* @param eventHandler Callback to handle on success or failure events
*/
public static void getHawkerStallByID(String hawkerStallID, DbEventHandler<HawkerStall> eventHandler){
DocumentReference docRef = collectionRef.document(hawkerStallID);
docRef.get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
@Override
public void onComplete(@NonNull Task<DocumentSnapshot> task) {
if (task.isSuccessful()) {
DocumentSnapshot document = task.getResult();
if (document != null && document.exists()) {
HawkerStall hawkerStall = document.toObject(HawkerStall.class);
eventHandler.onSuccess(hawkerStall);
} else {
eventHandler.onSuccess(null);
}
} else {
eventHandler.onFailure(task.getException());
}
}
});
};
// ...
}
-
Always use the globally defined colors and styles from
res/values/color.xml
andres/values/styles.xml
respectively. Do add new styles into those folders as well. -
Our team follows a convention where procedures that happen
onCreate
are abstracted out into meaningful methods. This allows team members to easily understand what the code is for.For example:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_add_hawker_stall);
newCategories = new ArrayList<>();
this.initViews(); // find all views by id
this.handleIntent(); // get extra data from intent
this.inflateOpeningDaysChips(); // inflate dynamically generated views
this.getAllTagsAndInflateChips(); // inflate dynamically generated views
this.initDynamicEditTextManager(); // init abstracted logic
this.attachButtonEventListeners(); // set button listeners
this.addFragmentBundleListener(); // listen for fragment bundle
}
- Any DB queries/writes are done via a Service and require you to define a
DbEventHandler
. For example:
TagsService.getAllTags(new DbEventHandler<Tags>() {
@Override
public void onSuccess(Tags o) {
String[] categoriesArray = o.getCategoriesArray();
new FilterDialogFragment(categoriesArray).show(fm, FilterDialogFragment.TAG);
}
@Override
public void onFailure(Exception e) {
Toast.makeText(HawkerStallActivity.this,
"Unable to retrieve filter tags. Please try again.",
Toast.LENGTH_SHORT).show();
}
});
- Any DB interaction that happens on a button click or any user input, requires a debouncer. We have defined it in Utils so just use it like this:
final Debouncer debouncer = new Debouncer();
debouncer.debounce(
view,
new Runnable() {
@Override
public void run() {
// put what you want to write to DB here
}
}
);
- Please also define
onActivityResult
if your activity consumes anything from Intents. Please also define your Intent data keys, result codes, etc in/utils/constants
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == Constants.RequestCodes.HAWKER_STALL_LISTING_TO_ADD_STALL_FORM &&
resultCode == Constants.ResultCodes.TO_HAWKER_STALL_LISTING) {
hawkerCentreName = data.getStringExtra(Constants.IntentExtraDataKeys.HAWKER_CENTRE_NAME);
hawkerCentreId = data.getStringExtra(Constants.IntentExtraDataKeys.HAWKER_CENTRE_ID);
}
}
- For any images that require downloading,please use
DownloadImageTask
- If your activity requires the Authentication with a toolbar, add
extends AuthenticatedActivity
. If you just require the toolbar, useextends ToolbarActivity
.
- Always extend
BaseDbFields
as it contains document id - Always make the attributes private and selectively include setters to data that are mutable
- If you have business logic, it can be placed in models as well