Project Name | Stars | Downloads | Repos Using This | Packages Using This | Most Recent Commit | Total Releases | Latest Release | Open Issues | License | Language |
---|---|---|---|---|---|---|---|---|---|---|
Food Ordering App Like Swiggy Uber Eats Mvvm And Room Database | 33 | 4 years ago | 2 | Java | ||||||
Food ordering app using MVVM architecture patterns, Architecture Lifecycle components and Room database. | ||||||||||
Food Diary | 8 | 6 years ago | mit | Swift | ||||||
Uses iOS 11 and Apple's CoreML to add nutrition data to your food diary based on pictures. CoreML is used for the image recognition (Inceptionv3). Alamofire (with CocoaPods) is used for REST requests against the Nutritionix-API for nutrition data. | ||||||||||
Food Delivery | 7 | 5 years ago | Vue | |||||||
A food-delivery app based on Vue 2.0 | ||||||||||
Sofra | 3 | 2 years ago | 1 | mit | Java | |||||
Sofra is an application With a double interface as a customer or restaurant you can as a restaurant offer food for sale and add offers, And as a customer you can buy food from any restaurant available | ||||||||||
Food Recipe App | 1 | 4 years ago | Java | |||||||
REST API with MVVM and Retrofit2 | ||||||||||
Ambrosia | 1 | 3 years ago | Kotlin | |||||||
Android food calculator app | ||||||||||
Pro Medin | 1 | 3 years ago | C# | |||||||
WPF app that help to coumadin patient save them meals, and learn about the food effects on the medicin. Built at MVVM pattern. |
Food ordering app using MVVM architecture patterns, Architecture Lifecycle components, Retrofit2 and Room database.
In this demo, I have covered Mediator Live data, Mutable live data, Observable, Observers, Retrofit and Room using same POJO.
I have used same models for Room and Retrofit2 library as both are configured using annotaions.
@Entity
public class FoodDetails {
@PrimaryKey //Room annotation
@SerializedName("item_name") //Retrofit annotation
@Expose
@NonNull
private String name;
@SerializedName("item_price")
@Expose
private Double price;
@SerializedName("average_rating")
@Expose
private Double rating;
@SerializedName("image_url")
@Expose
private String imageUrl;
@SerializedName("item_quantity")
@Expose
private Integer quantity = 0;
// For Retrofit
public FoodDetails(@NonNull String name, Double price, Double rating, String imageUrl,Integer quantity) {
this.name = name;
this.price = price;
this.rating = rating;
this.imageUrl = imageUrl;
this.quantity = quantity;
}
// Getters and Setters for Room
@NonNull
public String getName() {
return name;
}
public void setName(@NonNull String name) {
this.name = name;
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
public Double getRating() {
return rating;
}
public void setRating(Double rating) {
this.rating = rating;
}
public String getImageUrl() {
return imageUrl;
}
public void setImageUrl(String imageUrl) {
this.imageUrl = imageUrl;
}
public Integer getQuantity() {
return quantity;
}
public void setQuantity(Integer quantity) {
this.quantity = quantity;
}
}
Sorting of food items is done based on Pricing and rating. I have used Mediator Live data to observe same types of Live Data from different db queries. We are going to expose Mediator Live Data to UI which will observe data changes from other two Live Data.
...\
public class FoodViewModel extends AndroidViewModel {
MediatorLiveData<List<FoodDetails>> foodDetailsMediatorLiveData = new MediatorLiveData<>();
private LiveData<List<FoodDetails>> foodDetailsLiveDataSortPrice;
private LiveData<List<FoodDetails>> foodDetailsLiveDataSortRating;
private AppDatabase db;
private static String DEFAULT_SORT = ACTION_SORT_BY_PRICE;
public FoodViewModel(@NonNull Application application) {
super(application);
init();
}
private void init() {
db = AppDatabase.getDatabase(getApplication().getApplicationContext());
subscribeToFoodChanges();
}
private void subscribeToFoodChanges() {
if(DEFAULT_SORT.equals(ACTION_SORT_BY_PRICE)){
foodDetailsLiveDataSortPrice = db.foodDetailsDao().getFoodsByPrice();
foodDetailsMediatorLiveData.addSource(foodDetailsLiveDataSortPrice, new Observer<List<FoodDetails>>() {
@Override
public void onChanged(@Nullable List<FoodDetails> foodDetails) {
foodDetailsMediatorLiveData.setValue(foodDetails);
}
});
}else if(DEFAULT_SORT.equals(ACTION_SORT_BY_RATING)){
foodDetailsLiveDataSortRating = db.foodDetailsDao().getFoodsByRating();
foodDetailsMediatorLiveData.addSource(foodDetailsLiveDataSortRating, new Observer<List<FoodDetails>>() {
@Override
public void onChanged(@Nullable List<FoodDetails> foodDetails) {
foodDetailsMediatorLiveData.setValue(foodDetails);
}
});
}
}
...\
Manually we are triggering observers by using foodDetailsMediatorLiveData.setValue(foodDetails);
@Dao
public interface FoodDetailsDao {
...\
@Query("SELECT * FROM fooddetails ORDER BY price ASC")
LiveData<List<FoodDetails>> getFoodsByPrice();
@Query("SELECT * FROM fooddetails ORDER BY rating DESC")
LiveData<List<FoodDetails>> getFoodsByRating();
...\
}
We have to create observers for live data. Only then if any data is changed, observers will be notified with updated data by ViewModel.
...\
@Override
protected void onCreate(Bundle savedInstanceState) {
.../
// Define viewmodel
FoodViewModel foodViewModel = ViewModelProviders.of(this).get(FoodViewModel.class);
// Define Observer with actions to be done after update
Observer<List<FoodDetails>> foodMenuObserver = new Observer<List<FoodDetails>>() {
@Override
public void onChanged(@Nullable List<FoodDetails> foodDetails) {
if(foodDetails!=null){
foodListAdapter.setData(foodDetails);
foodListAdapter.notifyDataSetChanged();
runLayoutAnimation(foodList);
}else{
Log.e("Food details","null");
}
}
};
// set observer to watch for data changes
foodViewModel.getFoodDetailsMutableLiveData().observe(this, foodMenuObserver);
.../
}
...\
We have used Retrofit to get data from Rest API and updating db using Room in Intent Service
public interface YummyAPIServices {
@GET("/data.json")
Call<List<FoodDetails>> getFoodData();
}
public class FoodRepository {
private static FoodRepository instance;
private static final String TAG = "FoodRepository";
private YummyAPIServices yummyAPIServices = APIClient.getClient().create(YummyAPIServices.class);
public MutableLiveData<Boolean> getFoodMenu(final Context context){
final MutableLiveData<Boolean> isFoodCallOngoing = new MutableLiveData<>();
isFoodCallOngoing.setValue(true);
yummyAPIServices.getFoodData().enqueue(new Callback<List<FoodDetails>>() {
@Override
public void onResponse(Call<List<FoodDetails>> call, Response<List<FoodDetails>> response) {
if(response.isSuccessful()) {
new SaveFoodMenu(AppDatabase.getDatabase(context), response.body()).execute();
isFoodCallOngoing.setValue(false); // on success we are updating empty mutable live data with new food menus
}else{
Log.e(TAG,"response not successful");
}
}
@Override
public void onFailure(Call<List<FoodDetails>> call, Throwable t) {
Log.e(TAG,t.toString());
}
});
return isFoodCallOngoing; // returns empty mutable live data
}
public static FoodRepository getInstance() {
if(instance == null){
synchronized (FoodRepository.class){
if(instance == null){
instance = new FoodRepository();
}
}
}
return instance;
}
}
We are using singleton to get Retrofit client.
public class APIClient {
private static final String BASE_URL = "YOUR_SERVER_BASE_URL";
private static Retrofit retrofit = null;
public static Retrofit getClient() {
if (retrofit==null) {
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit;
}
}
This is the overall project structure of this project.