├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ ├── assets │ │ ├── ingredient_analysis.json │ │ ├── more_items_search_result.json │ │ ├── recipe.json │ │ ├── recipe_analysis.json │ │ ├── search_result.json │ │ └── search_result_empty.json │ └── java │ │ └── com │ │ └── mlsdev │ │ └── recipefinder │ │ ├── AssetUtils.java │ │ ├── MainActivityTest.java │ │ ├── MockApp.java │ │ ├── TestRunner.java │ │ ├── di │ │ ├── MockApplicationComponent.java │ │ ├── MockApplicationInjector.java │ │ └── module │ │ │ ├── MockApiModule.java │ │ │ └── MockDataSourceModule.java │ │ └── view │ │ ├── analysenutrition │ │ ├── ingredient │ │ │ └── IngredientAnalysisFragmentTest.java │ │ └── recipe │ │ │ └── RecipeAnalysisFragmentTest.java │ │ └── searchrecipes │ │ └── SearchRecipesFragmentTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── assets │ │ ├── 1.png │ │ ├── 10.png │ │ ├── 11.png │ │ ├── 2.png │ │ ├── 3.png │ │ ├── 4.png │ │ ├── 5.png │ │ ├── 6.png │ │ ├── 7.png │ │ ├── 8.png │ │ └── 9.png │ ├── ic_launcher-web.png │ ├── java │ │ └── com │ │ │ └── mlsdev │ │ │ └── recipefinder │ │ │ ├── RecipeApplication.java │ │ │ ├── data │ │ │ ├── entity │ │ │ │ ├── nutrition │ │ │ │ │ ├── Nutrient.java │ │ │ │ │ ├── NutritionAnalysisResult.java │ │ │ │ │ ├── RecipeAnalysisParams.java │ │ │ │ │ └── TotalNutrients.java │ │ │ │ └── recipe │ │ │ │ │ ├── Error.java │ │ │ │ │ ├── Hit.java │ │ │ │ │ ├── Ingredient.java │ │ │ │ │ ├── Params.java │ │ │ │ │ ├── Recipe.java │ │ │ │ │ ├── SearchResult.java │ │ │ │ │ ├── TotalDaily.java │ │ │ │ │ └── stringwrapper │ │ │ │ │ ├── DietLabel.java │ │ │ │ │ ├── HealthLabel.java │ │ │ │ │ └── Label.java │ │ │ └── source │ │ │ │ ├── BaseDataSource.java │ │ │ │ ├── BaseObserver.java │ │ │ │ ├── DataSource.java │ │ │ │ ├── local │ │ │ │ ├── LocalDataSource.java │ │ │ │ └── roomdb │ │ │ │ │ ├── AppDatabase.java │ │ │ │ │ ├── converter │ │ │ │ │ └── Converter.java │ │ │ │ │ └── dao │ │ │ │ │ ├── IngredientDao.java │ │ │ │ │ ├── NutrientDao.java │ │ │ │ │ ├── RecipeDao.java │ │ │ │ │ └── TotalNutrientsDao.java │ │ │ │ ├── remote │ │ │ │ ├── NutritionAnalysisService.java │ │ │ │ ├── ParameterKeys.java │ │ │ │ ├── PathConstants.java │ │ │ │ ├── RemoteDataSource.java │ │ │ │ └── SearchRecipesService.java │ │ │ │ └── repository │ │ │ │ └── DataRepository.java │ │ │ ├── di │ │ │ ├── ApplicationInjector.java │ │ │ ├── Injectable.java │ │ │ ├── component │ │ │ │ └── ApplicationComponent.java │ │ │ └── module │ │ │ │ ├── ApiModule.java │ │ │ │ ├── DataSourceModule.java │ │ │ │ ├── DatabaseModule.java │ │ │ │ ├── FragmentBuilderModule.java │ │ │ │ ├── MainActivityModule.java │ │ │ │ ├── RecipeAnalysisActivityModule.java │ │ │ │ ├── UtilsModule.java │ │ │ │ ├── ViewModelKey.java │ │ │ │ └── ViewModelModule.java │ │ │ └── view │ │ │ ├── ActionListener.java │ │ │ ├── BaseActivity.java │ │ │ ├── BottomNavigationItemSelectedListener.java │ │ │ ├── Extras.java │ │ │ ├── MainActivity.java │ │ │ ├── NavigationController.java │ │ │ ├── OnKeyboardStateChangedListener.java │ │ │ ├── analysenutrition │ │ │ ├── AnalyseNutritionFragment.java │ │ │ ├── adapter │ │ │ │ ├── BaseViewHolder.java │ │ │ │ └── ProgressViewHolder.java │ │ │ ├── ingredient │ │ │ │ ├── IngredientAnalysisFragment.java │ │ │ │ └── IngredientAnalysisViewModel.java │ │ │ └── recipe │ │ │ │ ├── AddIngredientDialogFragment.java │ │ │ │ ├── IngredientsAdapter.java │ │ │ │ ├── OnAddIngredientClickListener.java │ │ │ │ ├── RecipeAnalysisDetailsActivity.java │ │ │ │ ├── RecipeAnalysisDetailsViewModel.java │ │ │ │ ├── RecipeAnalysisFragment.java │ │ │ │ └── RecipeAnalysisViewModel.java │ │ │ ├── bindingutils │ │ │ └── DataBinder.java │ │ │ ├── enums │ │ │ └── TabItemType.java │ │ │ ├── favoriterecipes │ │ │ ├── FavoriteRecipesFragment.java │ │ │ └── FavoritesViewModel.java │ │ │ ├── fragment │ │ │ ├── BaseFragment.java │ │ │ └── RecipeListFragment.java │ │ │ ├── fragments │ │ │ └── TabFragment.java │ │ │ ├── listener │ │ │ ├── OnDataLoadedListener.java │ │ │ └── OnIngredientAnalyzedListener.java │ │ │ ├── message │ │ │ ├── Message.java │ │ │ ├── ProgressDialogMessage.java │ │ │ └── SnackbarMessage.java │ │ │ ├── recipedetails │ │ │ ├── RecipeDetailsFragment.java │ │ │ └── RecipeViewModel.java │ │ │ ├── searchrecipes │ │ │ ├── FilterDialogFragment.java │ │ │ ├── RecipeListAdapter.java │ │ │ ├── RecipeListItemViewModel.java │ │ │ ├── SearchRecipeFragment.java │ │ │ └── SearchViewModel.java │ │ │ ├── utils │ │ │ ├── DiagramUtils.java │ │ │ ├── ParamsHelper.java │ │ │ └── UtilsUI.java │ │ │ └── viewmodel │ │ │ ├── BaseViewModel.java │ │ │ └── ViewModelFactory.java │ └── res │ │ ├── anim │ │ ├── fade_in.xml │ │ └── fade_out.xml │ │ ├── color │ │ └── tab_item_text_color.xml │ │ ├── drawable-v21 │ │ ├── recipe_list_item_foreground.xml │ │ ├── tab_item_background.xml │ │ └── toolbar_item_selector.xml │ │ ├── drawable-xxhdpi │ │ ├── btn_cancel.png │ │ ├── ic_favorite_checked.png │ │ ├── ic_favorite_normal.png │ │ ├── ic_navigation_analyse.png │ │ ├── ic_navigation_favorites.png │ │ ├── ic_navigation_search.png │ │ └── ic_nothing_found.png │ │ ├── drawable-xxxhdpi │ │ ├── ic_close.png │ │ ├── ic_empty_view.png │ │ ├── ic_filter.png │ │ └── ic_search.png │ │ ├── drawable │ │ ├── primary_color_shape.xml │ │ ├── primary_dark_color_shape.xml │ │ ├── progress_bar_background.xml │ │ ├── progress_drawable.xml │ │ ├── recipe_item_title_bg.xml │ │ ├── recipe_list_item_foreground.xml │ │ ├── tab_item_background.xml │ │ ├── toolbar_item_selector.xml │ │ └── top_shadow.xml │ │ ├── layout-land │ │ ├── fragment_ingredient_analysis.xml │ │ └── fragment_recipe_analysis.xml │ │ ├── layout │ │ ├── activity_main.xml │ │ ├── activity_recipe_analysis_details.xml │ │ ├── add_ingredient_button.xml │ │ ├── dialog_fragment_add_ingredient.xml │ │ ├── dialog_fragment_search_filter.xml │ │ ├── fragment_analyse_nutrition.xml │ │ ├── fragment_favorite_recipes.xml │ │ ├── fragment_ingredient_analysis.xml │ │ ├── fragment_recipe_analysis.xml │ │ ├── fragment_recipe_details.xml │ │ ├── fragment_search_recipes.xml │ │ ├── ingredient_list_item.xml │ │ ├── progress_view.xml │ │ └── recipe_list_item.xml │ │ ├── menu │ │ ├── navigation.xml │ │ └── options_menu.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ ├── transition │ │ └── change_image_transform.xml │ │ ├── values-w820dp │ │ └── dimens.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── mlsdev │ └── recipefinder │ ├── NutrientTest.java │ └── ParamsHelperTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/* 5 | .DS_Store 6 | /build 7 | /captures 8 | .externalNativeBuild 9 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: android 2 | jdk: oraclejdk8 3 | android: 4 | components: 5 | - tools 6 | - platform-tools 7 | - build-tools-25.0.3 8 | - android-25 9 | - extra-google-m2repository 10 | - extra-android-m2repository 11 | licenses: 12 | - android-sdk-preview-license-.+ 13 | - android-sdk-license-.+ 14 | - google-gdk-license-.+ 15 | script: 16 | - ./gradlew build 17 | before_install: 18 | - chmod +x gradlew -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Sergey Glebov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Travis-ci](https://api.travis-ci.org/MLSDev/RecipeFinderJavaVersion.svg) 2 | 3 | # RecipeFinder 4 | 5 | This is a sample app with **MVVM**, **RxJava** and **Data Binding** 6 | 7 | ## [Kotlin version](https://github.com/MLSDev/RecipeFinderKotlinVersion) 8 | 9 | ## Authors 10 | * [Sergey Petrosyuk](mailto:petrosyuk@mlsdev.com), MLSDev 11 | 12 | 13 | 14 | ## Used libraries 15 | * [MockWebServer](https://github.com/square/okhttp/tree/master/mockwebserver): mock a server responses 16 | * [OkHttp](https://github.com/square/okhttp) 17 | * [Retrofit](http://square.github.io/retrofit/): for the network requests 18 | * [RxJava 2](https://github.com/ReactiveX/RxJava) 19 | * [RxAndroid 2](https://github.com/ReactiveX/RxAndroid) 20 | * [Glide](https://github.com/bumptech/glide): for image loading 21 | * [MPAndroidChart](https://github.com/PhilJay/MPAndroidChart): charts and diagrams 22 | * [ORMLite](http://ormlite.com/): local data storage 23 | 24 | ## Authors 25 | * [Sergey Petrosyuk](mailto:petrosyuk@mlsdev.com), MLSDev 26 | 27 | ## About MLSDev 28 | 29 | [MLSDev.com][mlsdev] 30 | 31 | RecipeFinder is maintained by MLSDev, Inc. We specialize in providing all-in-one solution in mobile and web development. Our team follows Lean principles and works according to agile methodologies to deliver the best results reducing the budget for development and its timeline. 32 | 33 | Find out more [here][mlsdev] and don't hesitate to [contact us][contact]! 34 | 35 | [mlsdev]: http://mlsdev.com 36 | [contact]: http://mlsdev.com/contact_us 37 | [github-frederikos]: https://github.com/SerhiyPetrosyuk 38 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/serhii/Library/Android/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/androidTest/assets/search_result_empty.json: -------------------------------------------------------------------------------- 1 | { 2 | "q": "chicken", 3 | "from": 0, 4 | "to": 10, 5 | "params": { 6 | "sane": [], 7 | "to": [ 8 | "10" 9 | ], 10 | "q": [ 11 | "chicken" 12 | ], 13 | "app_id": [ 14 | "75347af5" 15 | ], 16 | "app_key": [ 17 | "c12154e41f974c51a56fad5a256c27f2" 18 | ], 19 | "from": [ 20 | "0" 21 | ] 22 | }, 23 | "more": true, 24 | "count": 1000, 25 | "hits": [] 26 | } -------------------------------------------------------------------------------- /app/src/androidTest/java/com/mlsdev/recipefinder/AssetUtils.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder; 2 | 3 | import android.content.Context; 4 | 5 | import com.google.gson.Gson; 6 | import com.mlsdev.recipefinder.data.entity.nutrition.NutritionAnalysisResult; 7 | import com.mlsdev.recipefinder.data.entity.recipe.Hit; 8 | import com.mlsdev.recipefinder.data.entity.recipe.Recipe; 9 | import com.mlsdev.recipefinder.data.entity.recipe.SearchResult; 10 | 11 | import java.io.IOException; 12 | import java.io.InputStream; 13 | import java.util.ArrayList; 14 | import java.util.List; 15 | 16 | public class AssetUtils { 17 | 18 | public static SearchResult getSearchResult(Context context) { 19 | return new Gson().fromJson(getSearchResultJsonData(context), SearchResult.class); 20 | } 21 | 22 | public static List getRecipeList(Context context) { 23 | return getResipesFromSearchResult(getSearchResult(context)); 24 | } 25 | 26 | public static String getSearchResultJsonData(Context context) { 27 | return getJsonStringFromAssets(context, "search_result.json"); 28 | } 29 | 30 | public static String getEmptySearchResultJsonData(Context context) { 31 | return getJsonStringFromAssets(context, "search_result_empty.json"); 32 | } 33 | 34 | public static String getMoreRecipesJsonData(Context context) { 35 | return getJsonStringFromAssets(context, "more_items_search_result.json"); 36 | } 37 | 38 | public static List getMoreRecipeList(Context context) { 39 | return getResipesFromSearchResult(new Gson().fromJson(getMoreRecipesJsonData(context), SearchResult.class)); 40 | } 41 | 42 | public static Recipe getRecipeEntity(Context context) { 43 | return new Gson().fromJson(getJsonStringFromAssets(context, "recipe.json"), Recipe.class); 44 | } 45 | 46 | public static String getIngredientAnalysisJsonData(Context context) { 47 | return getJsonStringFromAssets(context, "ingredient_analysis.json"); 48 | } 49 | 50 | public static NutritionAnalysisResult getNutritionAnalysisResult(Context context) { 51 | return new Gson().fromJson(getIngredientAnalysisJsonData(context), NutritionAnalysisResult.class); 52 | } 53 | 54 | public static String getRecipeAnalysisJsonData(Context context) { 55 | return getJsonStringFromAssets(context, "recipe_analysis.json"); 56 | } 57 | 58 | public static NutritionAnalysisResult getRecipeAnalysisResult(Context context) { 59 | return new Gson().fromJson(getRecipeAnalysisJsonData(context), NutritionAnalysisResult.class); 60 | } 61 | 62 | private static List getResipesFromSearchResult(SearchResult searchResult) { 63 | List recipes = new ArrayList<>(); 64 | for (Hit hit : searchResult.getHits()) 65 | recipes.add(hit.getRecipe()); 66 | return recipes; 67 | } 68 | 69 | private static String getJsonStringFromAssets(Context context, String fileName) { 70 | String json = null; 71 | try { 72 | InputStream inputStream = context.getAssets().open(fileName); 73 | int size = inputStream.available(); 74 | byte[] buffer = new byte[size]; 75 | inputStream.read(buffer); 76 | inputStream.close(); 77 | json = new String(buffer, "UTF-8"); 78 | } catch (IOException e) { 79 | e.printStackTrace(); 80 | } 81 | 82 | return json; 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/mlsdev/recipefinder/MainActivityTest.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder; 2 | 3 | import android.support.annotation.IdRes; 4 | import android.support.test.espresso.Espresso; 5 | import android.support.test.espresso.action.ViewActions; 6 | import android.support.test.espresso.assertion.ViewAssertions; 7 | import android.support.test.espresso.matcher.ViewMatchers; 8 | import android.support.test.rule.ActivityTestRule; 9 | 10 | import com.mlsdev.recipefinder.view.MainActivity; 11 | 12 | import org.junit.Rule; 13 | import org.junit.Test; 14 | 15 | public class MainActivityTest { 16 | @Rule 17 | public ActivityTestRule testRule = new ActivityTestRule<>(MainActivity.class); 18 | 19 | @Test 20 | public void testSelectNavigationTab() { 21 | // Select the Analyse Nutrition tab 22 | performClickUponTab(R.id.action_analyse_nutrition); 23 | assertTabOpened(R.id.ll_analyse_nutrition); 24 | 25 | // Select the Favorite Recipes tab 26 | performClickUponTab(R.id.action_favorites); 27 | assertTabOpened(R.id.rl_favorite_recipes); 28 | 29 | // Select the Search Recipes tab 30 | performClickUponTab(R.id.action_search_recipe); 31 | assertTabOpened(R.id.cl_search_recipes); 32 | } 33 | 34 | private void performClickUponTab(@IdRes int tabId) { 35 | Espresso.onView(ViewMatchers.withId(tabId)).perform(ViewActions.click()); 36 | } 37 | 38 | private void assertTabOpened(@IdRes int layoutId) { 39 | Espresso.onView(ViewMatchers.withId(layoutId)) 40 | .check(ViewAssertions.matches(ViewMatchers.isDisplayed())); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/mlsdev/recipefinder/MockApp.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder; 2 | 3 | import com.mlsdev.recipefinder.di.MockApplicationComponent; 4 | import com.mlsdev.recipefinder.di.MockApplicationInjector; 5 | 6 | public class MockApp extends RecipeApplication { 7 | private MockApplicationComponent component; 8 | 9 | @Override 10 | public void onCreate() { 11 | super.onCreate(); 12 | instance = this; 13 | component = MockApplicationInjector.init(this); 14 | } 15 | 16 | public MockApplicationComponent getComponent() { 17 | return component; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/mlsdev/recipefinder/TestRunner.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder; 2 | 3 | import android.app.Application; 4 | import android.content.Context; 5 | import android.os.Bundle; 6 | import android.os.IBinder; 7 | import android.support.test.runner.AndroidJUnitRunner; 8 | 9 | import java.lang.reflect.Method; 10 | 11 | import static android.content.pm.PackageManager.PERMISSION_GRANTED; 12 | 13 | public class TestRunner extends AndroidJUnitRunner { 14 | 15 | @Override 16 | public Application newApplication(ClassLoader cl, String className, Context context) 17 | throws InstantiationException, IllegalAccessException, ClassNotFoundException { 18 | return super.newApplication(cl, MockApp.class.getName(), context); 19 | } 20 | 21 | /* 22 | *Fix android emulator issues on CIs 23 | */ 24 | @Override 25 | public void onStart() { 26 | runOnMainSync(new Runnable() { 27 | @Override 28 | public void run() { 29 | Context app = TestRunner.this.getTargetContext().getApplicationContext(); 30 | TestRunner.this.disableAnimations(app); 31 | } 32 | }); 33 | 34 | super.onStart(); 35 | } 36 | 37 | 38 | @Override 39 | public void finish(int resultCode, Bundle results) { 40 | super.finish(resultCode, results); 41 | enableAnimations(getContext()); 42 | } 43 | 44 | 45 | void disableAnimations(Context context) { 46 | int permStatus = context.checkCallingOrSelfPermission(android.Manifest.permission.SET_ANIMATION_SCALE); 47 | if (permStatus == PERMISSION_GRANTED) { 48 | setSystemAnimationsScale(0.0f); 49 | } 50 | } 51 | 52 | void enableAnimations(Context context) { 53 | int permStatus = context.checkCallingOrSelfPermission(android.Manifest.permission.SET_ANIMATION_SCALE); 54 | if (permStatus == PERMISSION_GRANTED) { 55 | setSystemAnimationsScale(1.0f); 56 | } 57 | } 58 | 59 | private void setSystemAnimationsScale(float animationScale) { 60 | try { 61 | Class windowManagerStubClazz = Class.forName("android.view.IWindowManager$Stub"); 62 | Method asInterface = windowManagerStubClazz.getDeclaredMethod("asInterface", IBinder.class); 63 | Class serviceManagerClazz = Class.forName("android.os.ServiceManager"); 64 | Method getService = serviceManagerClazz.getDeclaredMethod("getService", String.class); 65 | Class windowManagerClazz = Class.forName("android.view.IWindowManager"); 66 | Method setAnimationScales = windowManagerClazz.getDeclaredMethod("setAnimationScales", float[].class); 67 | Method getAnimationScales = windowManagerClazz.getDeclaredMethod("getAnimationScales"); 68 | 69 | IBinder windowManagerBinder = (IBinder) getService.invoke(null, "window"); 70 | Object windowManagerObj = asInterface.invoke(null, windowManagerBinder); 71 | float[] currentScales = (float[]) getAnimationScales.invoke(windowManagerObj); 72 | for (int i = 0; i < currentScales.length; i++) { 73 | currentScales[i] = animationScale; 74 | } 75 | setAnimationScales.invoke(windowManagerObj, new Object[]{currentScales}); 76 | } catch (Exception e) { 77 | } 78 | } 79 | 80 | 81 | } 82 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/mlsdev/recipefinder/di/MockApplicationComponent.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder.di; 2 | 3 | import android.app.Application; 4 | 5 | import com.mlsdev.recipefinder.di.module.MockApiModule; 6 | import com.mlsdev.recipefinder.MockApp; 7 | import com.mlsdev.recipefinder.di.module.MockDataSourceModule; 8 | import com.mlsdev.recipefinder.di.module.DatabaseModule; 9 | import com.mlsdev.recipefinder.di.module.MainActivityModule; 10 | import com.mlsdev.recipefinder.di.module.RecipeAnalysisActivityModule; 11 | import com.mlsdev.recipefinder.di.module.UtilsModule; 12 | import com.mlsdev.recipefinder.di.module.ViewModelModule; 13 | import com.mlsdev.recipefinder.view.analysenutrition.ingredient.IngredientAnalysisFragmentTest; 14 | import com.mlsdev.recipefinder.view.analysenutrition.recipe.RecipeAnalysisFragmentTest; 15 | import com.mlsdev.recipefinder.view.searchrecipes.SearchRecipesFragmentTest; 16 | 17 | import javax.inject.Singleton; 18 | 19 | import dagger.BindsInstance; 20 | import dagger.Component; 21 | import dagger.android.AndroidInjectionModule; 22 | 23 | @Singleton 24 | @Component(modules = { 25 | AndroidInjectionModule.class, 26 | UtilsModule.class, 27 | MockDataSourceModule.class, 28 | DatabaseModule.class, 29 | MockApiModule.class, 30 | MainActivityModule.class, 31 | RecipeAnalysisActivityModule.class, 32 | ViewModelModule.class}) 33 | public interface MockApplicationComponent { 34 | 35 | @Component.Builder 36 | interface Builder { 37 | @BindsInstance 38 | Builder application(Application application); 39 | 40 | MockApplicationComponent build(); 41 | } 42 | 43 | void inject(MockApp application); 44 | void inject(SearchRecipesFragmentTest fragmentTest); 45 | void inject(IngredientAnalysisFragmentTest fragmentTest); 46 | void inject(RecipeAnalysisFragmentTest fragmentTest); 47 | 48 | } 49 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/mlsdev/recipefinder/di/MockApplicationInjector.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder.di; 2 | 3 | import android.app.Activity; 4 | import android.app.Application; 5 | import android.os.Bundle; 6 | import android.support.v4.app.Fragment; 7 | import android.support.v4.app.FragmentActivity; 8 | import android.support.v4.app.FragmentManager; 9 | 10 | import com.mlsdev.recipefinder.MockApp; 11 | 12 | import dagger.android.AndroidInjection; 13 | import dagger.android.support.AndroidSupportInjection; 14 | import dagger.android.support.HasSupportFragmentInjector; 15 | 16 | public class MockApplicationInjector { 17 | 18 | private MockApplicationInjector() { 19 | } 20 | 21 | public static MockApplicationComponent init(MockApp application) { 22 | MockApplicationComponent component = DaggerMockApplicationComponent.builder() 23 | .application(application) 24 | .build(); 25 | 26 | component.inject(application); 27 | 28 | application.registerActivityLifecycleCallbacks(new Application.ActivityLifecycleCallbacks() { 29 | @Override 30 | public void onActivityCreated(Activity activity, Bundle savedInstanceState) { 31 | handleActivity(activity); 32 | } 33 | 34 | @Override 35 | public void onActivityStarted(Activity activity) { 36 | 37 | } 38 | 39 | @Override 40 | public void onActivityResumed(Activity activity) { 41 | 42 | } 43 | 44 | @Override 45 | public void onActivityPaused(Activity activity) { 46 | 47 | } 48 | 49 | @Override 50 | public void onActivityStopped(Activity activity) { 51 | 52 | } 53 | 54 | @Override 55 | public void onActivitySaveInstanceState(Activity activity, Bundle outState) { 56 | 57 | } 58 | 59 | @Override 60 | public void onActivityDestroyed(Activity activity) { 61 | 62 | } 63 | }); 64 | 65 | return component; 66 | } 67 | 68 | private static void handleActivity(Activity activity) { 69 | 70 | if (activity instanceof HasSupportFragmentInjector) { 71 | AndroidInjection.inject(activity); 72 | } 73 | 74 | if (activity instanceof FragmentActivity) { 75 | ((FragmentActivity) activity).getSupportFragmentManager() 76 | .registerFragmentLifecycleCallbacks(new FragmentManager.FragmentLifecycleCallbacks() { 77 | @Override 78 | public void onFragmentCreated(FragmentManager fm, Fragment f, Bundle savedInstanceState) { 79 | if (f instanceof Injectable) 80 | AndroidSupportInjection.inject(f); 81 | } 82 | }, true); 83 | } 84 | 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/mlsdev/recipefinder/di/module/MockApiModule.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder.di.module; 2 | 3 | import com.mlsdev.recipefinder.data.source.remote.NutritionAnalysisService; 4 | import com.mlsdev.recipefinder.data.source.remote.SearchRecipesService; 5 | import com.mlsdev.recipefinder.di.module.ApiModule; 6 | 7 | import org.mockito.Mockito; 8 | 9 | import javax.inject.Singleton; 10 | 11 | import dagger.Module; 12 | import dagger.Provides; 13 | 14 | @Module 15 | public class MockApiModule extends ApiModule { 16 | 17 | @Provides 18 | @Singleton 19 | SearchRecipesService provideSearchRecipesService() { 20 | return Mockito.mock(SearchRecipesService.class); 21 | } 22 | 23 | @Provides 24 | @Singleton 25 | NutritionAnalysisService provideNutritionAnalysisService() { 26 | return Mockito.mock(NutritionAnalysisService.class); 27 | } 28 | 29 | 30 | } 31 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/mlsdev/recipefinder/di/module/MockDataSourceModule.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder.di.module; 2 | 3 | import com.mlsdev.recipefinder.data.source.DataSource; 4 | import com.mlsdev.recipefinder.data.source.local.LocalDataSource; 5 | import com.mlsdev.recipefinder.data.source.local.roomdb.AppDatabase; 6 | import com.mlsdev.recipefinder.data.source.remote.RemoteDataSource; 7 | import com.mlsdev.recipefinder.data.source.repository.DataRepository; 8 | 9 | import org.mockito.Mockito; 10 | 11 | import javax.inject.Named; 12 | import javax.inject.Singleton; 13 | 14 | import dagger.Module; 15 | import dagger.Provides; 16 | 17 | @Module 18 | public class MockDataSourceModule { 19 | public static final String LOCAL_DATA_SOURCE = "local_data_source"; 20 | public static final String REMOTE_DATA_SOURCE = "remote_data_source"; 21 | 22 | @Provides 23 | @Singleton 24 | DataRepository provideDataRepository(@Named(LOCAL_DATA_SOURCE) DataSource local, 25 | @Named(REMOTE_DATA_SOURCE) DataSource remote) { 26 | return Mockito.mock(DataRepository.class); 27 | } 28 | 29 | @Provides 30 | @Singleton 31 | @Named(LOCAL_DATA_SOURCE) 32 | DataSource provideLocalDataSource(AppDatabase database) { 33 | return Mockito.mock(LocalDataSource.class); 34 | } 35 | 36 | @Provides 37 | @Singleton 38 | @Named(REMOTE_DATA_SOURCE) 39 | DataSource provideRemoteDataSource() { 40 | return Mockito.mock(RemoteDataSource.class); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 17 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /app/src/main/assets/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MLSDev/RecipeFinderJavaVersion/7e87c0c029186c66fb6f23259f9a06b63a4c21ca/app/src/main/assets/1.png -------------------------------------------------------------------------------- /app/src/main/assets/10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MLSDev/RecipeFinderJavaVersion/7e87c0c029186c66fb6f23259f9a06b63a4c21ca/app/src/main/assets/10.png -------------------------------------------------------------------------------- /app/src/main/assets/11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MLSDev/RecipeFinderJavaVersion/7e87c0c029186c66fb6f23259f9a06b63a4c21ca/app/src/main/assets/11.png -------------------------------------------------------------------------------- /app/src/main/assets/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MLSDev/RecipeFinderJavaVersion/7e87c0c029186c66fb6f23259f9a06b63a4c21ca/app/src/main/assets/2.png -------------------------------------------------------------------------------- /app/src/main/assets/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MLSDev/RecipeFinderJavaVersion/7e87c0c029186c66fb6f23259f9a06b63a4c21ca/app/src/main/assets/3.png -------------------------------------------------------------------------------- /app/src/main/assets/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MLSDev/RecipeFinderJavaVersion/7e87c0c029186c66fb6f23259f9a06b63a4c21ca/app/src/main/assets/4.png -------------------------------------------------------------------------------- /app/src/main/assets/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MLSDev/RecipeFinderJavaVersion/7e87c0c029186c66fb6f23259f9a06b63a4c21ca/app/src/main/assets/5.png -------------------------------------------------------------------------------- /app/src/main/assets/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MLSDev/RecipeFinderJavaVersion/7e87c0c029186c66fb6f23259f9a06b63a4c21ca/app/src/main/assets/6.png -------------------------------------------------------------------------------- /app/src/main/assets/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MLSDev/RecipeFinderJavaVersion/7e87c0c029186c66fb6f23259f9a06b63a4c21ca/app/src/main/assets/7.png -------------------------------------------------------------------------------- /app/src/main/assets/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MLSDev/RecipeFinderJavaVersion/7e87c0c029186c66fb6f23259f9a06b63a4c21ca/app/src/main/assets/8.png -------------------------------------------------------------------------------- /app/src/main/assets/9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MLSDev/RecipeFinderJavaVersion/7e87c0c029186c66fb6f23259f9a06b63a4c21ca/app/src/main/assets/9.png -------------------------------------------------------------------------------- /app/src/main/ic_launcher-web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MLSDev/RecipeFinderJavaVersion/7e87c0c029186c66fb6f23259f9a06b63a4c21ca/app/src/main/ic_launcher-web.png -------------------------------------------------------------------------------- /app/src/main/java/com/mlsdev/recipefinder/RecipeApplication.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder; 2 | 3 | import android.app.Activity; 4 | import android.app.Application; 5 | 6 | import com.mlsdev.recipefinder.di.ApplicationInjector; 7 | 8 | import javax.inject.Inject; 9 | 10 | import dagger.android.DispatchingAndroidInjector; 11 | import dagger.android.HasActivityInjector; 12 | 13 | public class RecipeApplication extends Application implements HasActivityInjector { 14 | protected static RecipeApplication instance; 15 | 16 | public static RecipeApplication getInstance() { 17 | return instance; 18 | } 19 | 20 | @Inject 21 | DispatchingAndroidInjector dispatchingAndroidInjector; 22 | 23 | @Override 24 | public void onCreate() { 25 | super.onCreate(); 26 | instance = this; 27 | ApplicationInjector.init(this); 28 | } 29 | 30 | @Override 31 | public DispatchingAndroidInjector activityInjector() { 32 | return dispatchingAndroidInjector; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /app/src/main/java/com/mlsdev/recipefinder/data/entity/nutrition/Nutrient.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder.data.entity.nutrition; 2 | 3 | import android.arch.persistence.room.Entity; 4 | import android.arch.persistence.room.Ignore; 5 | import android.arch.persistence.room.PrimaryKey; 6 | import android.os.Parcel; 7 | import android.os.Parcelable; 8 | 9 | import com.mlsdev.recipefinder.view.utils.UtilsUI; 10 | 11 | @Entity(tableName = "nutrients") 12 | public class Nutrient implements Parcelable{ 13 | @PrimaryKey(autoGenerate = true) 14 | private long id; 15 | private String label; 16 | private double quantity; 17 | private String unit; 18 | 19 | public Nutrient() { 20 | } 21 | 22 | @Ignore 23 | public Nutrient(String label, double quantity, String unit) { 24 | this.label = label; 25 | this.quantity = quantity; 26 | this.unit = unit; 27 | } 28 | 29 | // region Getters 30 | public long getId() { 31 | return id; 32 | } 33 | 34 | public String getLabel() { 35 | return label; 36 | } 37 | 38 | public double getQuantity() { 39 | return quantity; 40 | } 41 | 42 | public String getUnit() { 43 | return unit; 44 | } 45 | //endregion 46 | 47 | // region Setters 48 | public void setId(long id) { 49 | this.id = id; 50 | } 51 | 52 | public void setLabel(String label) { 53 | this.label = label; 54 | } 55 | 56 | public void setQuantity(double quantity) { 57 | this.quantity = quantity; 58 | } 59 | 60 | public void setUnit(String unit) { 61 | this.unit = unit; 62 | } 63 | // endregion 64 | 65 | public String getFormattedFullText() { 66 | return label + " " + UtilsUI.formatDecimalToString(quantity) + " " + unit; 67 | } 68 | 69 | @Override 70 | public int describeContents() { 71 | return 0; 72 | } 73 | 74 | @Override 75 | public void writeToParcel(Parcel dest, int flags) { 76 | dest.writeLong(this.id); 77 | dest.writeString(this.label); 78 | dest.writeDouble(this.quantity); 79 | dest.writeString(this.unit); 80 | } 81 | 82 | protected Nutrient(Parcel in) { 83 | this.id = in.readLong(); 84 | this.label = in.readString(); 85 | this.quantity = in.readDouble(); 86 | this.unit = in.readString(); 87 | } 88 | 89 | public static final Creator CREATOR = new Creator() { 90 | @Override 91 | public Nutrient createFromParcel(Parcel source) { 92 | return new Nutrient(source); 93 | } 94 | 95 | @Override 96 | public Nutrient[] newArray(int size) { 97 | return new Nutrient[size]; 98 | } 99 | }; 100 | } 101 | -------------------------------------------------------------------------------- /app/src/main/java/com/mlsdev/recipefinder/data/entity/nutrition/NutritionAnalysisResult.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder.data.entity.nutrition; 2 | 3 | import android.os.Parcel; 4 | import android.os.Parcelable; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | public class NutritionAnalysisResult implements Parcelable { 10 | private String uri; 11 | private int calories; 12 | private double totalWeight; 13 | private double yield; 14 | private List dietLabels = new ArrayList<>(); 15 | private List healthLabels = new ArrayList<>(); 16 | private List cautions = new ArrayList<>(); 17 | private TotalNutrients totalNutrients; 18 | 19 | public String getUri() { 20 | return uri; 21 | } 22 | 23 | public int getCalories() { 24 | return calories; 25 | } 26 | 27 | public TotalNutrients getTotalNutrients() { 28 | return totalNutrients; 29 | } 30 | 31 | public double getYield() { 32 | return yield; 33 | } 34 | 35 | public NutritionAnalysisResult() { 36 | } 37 | 38 | @Override 39 | public int describeContents() { 40 | return 0; 41 | } 42 | 43 | @Override 44 | public void writeToParcel(Parcel dest, int flags) { 45 | dest.writeString(this.uri); 46 | dest.writeInt(this.calories); 47 | dest.writeDouble(this.totalWeight); 48 | dest.writeDouble(this.yield); 49 | dest.writeStringList(this.dietLabels); 50 | dest.writeStringList(this.healthLabels); 51 | dest.writeStringList(this.cautions); 52 | dest.writeParcelable(this.totalNutrients, flags); 53 | } 54 | 55 | protected NutritionAnalysisResult(Parcel in) { 56 | this.uri = in.readString(); 57 | this.calories = in.readInt(); 58 | this.totalWeight = in.readDouble(); 59 | this.yield = in.readDouble(); 60 | this.dietLabels = in.createStringArrayList(); 61 | this.healthLabels = in.createStringArrayList(); 62 | this.cautions = in.createStringArrayList(); 63 | this.totalNutrients = in.readParcelable(TotalNutrients.class.getClassLoader()); 64 | } 65 | 66 | public static final Creator CREATOR = new Creator() { 67 | @Override 68 | public NutritionAnalysisResult createFromParcel(Parcel source) { 69 | return new NutritionAnalysisResult(source); 70 | } 71 | 72 | @Override 73 | public NutritionAnalysisResult[] newArray(int size) { 74 | return new NutritionAnalysisResult[size]; 75 | } 76 | }; 77 | } 78 | -------------------------------------------------------------------------------- /app/src/main/java/com/mlsdev/recipefinder/data/entity/nutrition/RecipeAnalysisParams.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder.data.entity.nutrition; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | public class RecipeAnalysisParams { 7 | private String title; 8 | private String prep; 9 | private String yield; 10 | private List ingr = new ArrayList<>(); 11 | 12 | public void setTitle(String title) { 13 | this.title = title; 14 | } 15 | 16 | public void setPrep(String prep) { 17 | this.prep = prep; 18 | } 19 | 20 | public void setYield(String yield) { 21 | this.yield = yield; 22 | } 23 | 24 | public void setIngr(List ingr) { 25 | this.ingr = ingr; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/src/main/java/com/mlsdev/recipefinder/data/entity/recipe/Error.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder.data.entity.recipe; 2 | 3 | import com.google.gson.annotations.Expose; 4 | import com.google.gson.annotations.SerializedName; 5 | 6 | public class Error { 7 | @SerializedName("error") 8 | @Expose 9 | private String error; 10 | 11 | public String getError() { 12 | return error; 13 | } 14 | 15 | public void setError(String error) { 16 | this.error = error; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /app/src/main/java/com/mlsdev/recipefinder/data/entity/recipe/Hit.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder.data.entity.recipe; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | 5 | public class Hit { 6 | @SerializedName("recipe") 7 | private Recipe recipe; 8 | @SerializedName("bookmarked") 9 | private boolean bookmarked; 10 | @SerializedName("bought") 11 | private boolean bought; 12 | 13 | public void setRecipe(Recipe recipe) { 14 | this.recipe = recipe; 15 | } 16 | 17 | public void setBookmarked(boolean bookmarked) { 18 | this.bookmarked = bookmarked; 19 | } 20 | 21 | public void setBought(boolean bought) { 22 | this.bought = bought; 23 | } 24 | 25 | public Recipe getRecipe() { 26 | return recipe; 27 | } 28 | 29 | public boolean isBookmarked() { 30 | return bookmarked; 31 | } 32 | 33 | public boolean isBought() { 34 | return bought; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /app/src/main/java/com/mlsdev/recipefinder/data/entity/recipe/Ingredient.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder.data.entity.recipe; 2 | 3 | 4 | import android.arch.persistence.room.ColumnInfo; 5 | import android.arch.persistence.room.Entity; 6 | import android.arch.persistence.room.PrimaryKey; 7 | 8 | import java.io.Serializable; 9 | 10 | @Entity(tableName = "ingredients") 11 | public class Ingredient implements Serializable { 12 | @PrimaryKey(autoGenerate = true) 13 | private long id; 14 | 15 | @ColumnInfo(name = "recipe_uri", index = true) 16 | private String recipeUri; 17 | 18 | private String text; 19 | private double weight; 20 | 21 | public Ingredient() { 22 | } 23 | 24 | public void setRecipeUri(String recipeUri) { 25 | this.recipeUri = recipeUri; 26 | } 27 | 28 | public void setId(long id) { 29 | this.id = id; 30 | } 31 | 32 | public void setText(String text) { 33 | this.text = text; 34 | } 35 | 36 | public void setWeight(double weight) { 37 | this.weight = weight; 38 | } 39 | 40 | public String getRecipeUri() { 41 | return recipeUri; 42 | } 43 | 44 | public long getId() { 45 | return id; 46 | } 47 | 48 | public String getText() { 49 | return text; 50 | } 51 | 52 | public double getWeight() { 53 | return weight; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /app/src/main/java/com/mlsdev/recipefinder/data/entity/recipe/Params.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder.data.entity.recipe; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | public class Params { 9 | @SerializedName("sane") 10 | private List sane = new ArrayList<>(); 11 | @SerializedName("to") 12 | private List to = new ArrayList<>(); 13 | @SerializedName("from") 14 | private List from = new ArrayList<>(); 15 | @SerializedName("q") 16 | private List queries = new ArrayList<>(); 17 | @SerializedName("calories") 18 | private List calories = new ArrayList<>(); 19 | @SerializedName("health") 20 | private List health = new ArrayList<>(); 21 | 22 | public List getSane() { 23 | return sane; 24 | } 25 | 26 | public List getTo() { 27 | return to; 28 | } 29 | 30 | public List getFrom() { 31 | return from; 32 | } 33 | 34 | public List getQueries() { 35 | return queries; 36 | } 37 | 38 | public List getCalories() { 39 | return calories; 40 | } 41 | 42 | public List getHealth() { 43 | return health; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /app/src/main/java/com/mlsdev/recipefinder/data/entity/recipe/Recipe.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder.data.entity.recipe; 2 | 3 | import android.arch.persistence.room.ColumnInfo; 4 | import android.arch.persistence.room.Entity; 5 | import android.arch.persistence.room.ForeignKey; 6 | import android.arch.persistence.room.Ignore; 7 | import android.arch.persistence.room.Index; 8 | import android.arch.persistence.room.PrimaryKey; 9 | import android.arch.persistence.room.TypeConverters; 10 | 11 | import com.google.gson.annotations.SerializedName; 12 | import com.mlsdev.recipefinder.data.entity.nutrition.TotalNutrients; 13 | import com.mlsdev.recipefinder.data.source.local.roomdb.converter.Converter; 14 | 15 | import java.io.Serializable; 16 | import java.util.ArrayList; 17 | import java.util.List; 18 | 19 | @Entity(tableName = "recipes", 20 | foreignKeys = { 21 | @ForeignKey( 22 | entity = TotalNutrients.class, 23 | parentColumns = "id", 24 | childColumns = "total_nutrients_id")}, 25 | indices = {@Index("total_nutrients_id")} 26 | ) 27 | @TypeConverters(Converter.class) 28 | public class Recipe implements Serializable { 29 | @PrimaryKey 30 | private String uri; 31 | private String label; 32 | private String image; 33 | private String source; 34 | private double yield; 35 | private double calories; 36 | 37 | @SerializedName("totalWeight") 38 | private double totalWeight; 39 | 40 | @Ignore 41 | @SerializedName("totalNutrients") 42 | private TotalNutrients totalNutrients; 43 | 44 | @ColumnInfo(name = "total_nutrients_id") 45 | private long totalNutrientsId; 46 | 47 | @ColumnInfo(name = "diet_labels") 48 | @SerializedName("dietLabels") 49 | private List dietLabels = new ArrayList<>(); 50 | 51 | @ColumnInfo(name = "health_labels") 52 | @SerializedName("healthLabels") 53 | private List healthLabels = new ArrayList<>(); 54 | 55 | @Ignore 56 | @SerializedName("ingredients") 57 | private List ingredients = new ArrayList<>(); 58 | 59 | public Recipe() { 60 | } 61 | 62 | @Ignore 63 | public Recipe(long totalNutrientsId) { 64 | this.totalNutrientsId = totalNutrientsId; 65 | } 66 | 67 | // region Getters 68 | 69 | public String getUri() { 70 | return uri; 71 | } 72 | 73 | public String getLabel() { 74 | return label; 75 | } 76 | 77 | public String getImage() { 78 | return image; 79 | } 80 | 81 | public String getSource() { 82 | return source; 83 | } 84 | 85 | public double getYield() { 86 | return yield; 87 | } 88 | 89 | public double getCalories() { 90 | return calories; 91 | } 92 | 93 | public double getTotalWeight() { 94 | return totalWeight; 95 | } 96 | 97 | public TotalNutrients getTotalNutrients() { 98 | return totalNutrients; 99 | } 100 | 101 | public long getTotalNutrientsId() { 102 | return totalNutrientsId; 103 | } 104 | 105 | public List getDietLabels() { 106 | return dietLabels; 107 | } 108 | 109 | public List getHealthLabels() { 110 | return healthLabels; 111 | } 112 | 113 | public List getIngredients() { 114 | return ingredients; 115 | } 116 | // endregion 117 | 118 | // region Setters 119 | 120 | public void setUri(String uri) { 121 | this.uri = uri; 122 | } 123 | 124 | public void setLabel(String label) { 125 | this.label = label; 126 | } 127 | 128 | public void setImage(String image) { 129 | this.image = image; 130 | } 131 | 132 | public void setSource(String source) { 133 | this.source = source; 134 | } 135 | 136 | public void setYield(double yield) { 137 | this.yield = yield; 138 | } 139 | 140 | public void setCalories(double calories) { 141 | this.calories = calories; 142 | } 143 | 144 | public void setTotalWeight(double totalWeight) { 145 | this.totalWeight = totalWeight; 146 | } 147 | 148 | public void setTotalNutrients(TotalNutrients totalNutrients) { 149 | this.totalNutrients = totalNutrients; 150 | } 151 | 152 | public void setTotalNutrientsId(long totalNutrientsId) { 153 | this.totalNutrientsId = totalNutrientsId; 154 | } 155 | 156 | public void setDietLabels(List dietLabels) { 157 | this.dietLabels = dietLabels; 158 | } 159 | 160 | public void setHealthLabels(List healthLabels) { 161 | this.healthLabels = healthLabels; 162 | } 163 | 164 | public void setIngredients(List ingredients) { 165 | this.ingredients = ingredients; 166 | } 167 | 168 | // endregion 169 | 170 | } 171 | -------------------------------------------------------------------------------- /app/src/main/java/com/mlsdev/recipefinder/data/entity/recipe/SearchResult.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder.data.entity.recipe; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | public class SearchResult { 9 | @SerializedName("q") 10 | private String query; 11 | @SerializedName("from") 12 | private int from; 13 | @SerializedName("to") 14 | private int to; 15 | @SerializedName("params") 16 | private Params params; 17 | @SerializedName("more") 18 | private boolean more; 19 | @SerializedName("count") 20 | private int count; 21 | @SerializedName("hits") 22 | private List hits = new ArrayList<>(); 23 | 24 | public String getQuery() { 25 | return query; 26 | } 27 | 28 | public int getFrom() { 29 | return from; 30 | } 31 | 32 | public int getTo() { 33 | return to; 34 | } 35 | 36 | public Params getParams() { 37 | return params; 38 | } 39 | 40 | public boolean isMore() { 41 | return more; 42 | } 43 | 44 | public int getCount() { 45 | return count; 46 | } 47 | 48 | public List getHits() { 49 | return hits; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /app/src/main/java/com/mlsdev/recipefinder/data/entity/recipe/TotalDaily.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder.data.entity.recipe; 2 | 3 | import java.io.Serializable; 4 | 5 | public class TotalDaily implements Serializable { 6 | 7 | public TotalDaily() { 8 | } 9 | 10 | } 11 | -------------------------------------------------------------------------------- /app/src/main/java/com/mlsdev/recipefinder/data/entity/recipe/stringwrapper/DietLabel.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder.data.entity.recipe.stringwrapper; 2 | 3 | import com.mlsdev.recipefinder.data.entity.recipe.Recipe; 4 | 5 | public class DietLabel extends Label { 6 | public DietLabel() { 7 | } 8 | 9 | public DietLabel(String value, Recipe recipe) { 10 | super(value, recipe); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /app/src/main/java/com/mlsdev/recipefinder/data/entity/recipe/stringwrapper/HealthLabel.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder.data.entity.recipe.stringwrapper; 2 | 3 | import com.mlsdev.recipefinder.data.entity.recipe.Recipe; 4 | 5 | public class HealthLabel extends Label { 6 | public HealthLabel(String value, Recipe recipe) { 7 | super(value, recipe); 8 | } 9 | 10 | public HealthLabel() { 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /app/src/main/java/com/mlsdev/recipefinder/data/entity/recipe/stringwrapper/Label.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder.data.entity.recipe.stringwrapper; 2 | 3 | import com.mlsdev.recipefinder.data.entity.recipe.Recipe; 4 | 5 | import java.io.Serializable; 6 | 7 | public abstract class Label implements Serializable { 8 | protected Recipe recipe; 9 | protected long id; 10 | protected String value; 11 | 12 | public Label() { 13 | } 14 | 15 | public Label(String value, Recipe recipe) { 16 | this.recipe = recipe; 17 | this.value = value; 18 | } 19 | 20 | public String getValue() { 21 | return value; 22 | } 23 | 24 | public void setValue(String value) { 25 | this.value = value; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/src/main/java/com/mlsdev/recipefinder/data/source/BaseDataSource.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder.data.source; 2 | 3 | import com.mlsdev.recipefinder.data.entity.nutrition.NutritionAnalysisResult; 4 | import com.mlsdev.recipefinder.data.entity.nutrition.RecipeAnalysisParams; 5 | import com.mlsdev.recipefinder.data.entity.recipe.Recipe; 6 | import com.mlsdev.recipefinder.data.entity.recipe.SearchResult; 7 | 8 | import java.util.List; 9 | import java.util.Map; 10 | 11 | import io.reactivex.Completable; 12 | import io.reactivex.Flowable; 13 | import io.reactivex.Single; 14 | 15 | public abstract class BaseDataSource implements DataSource { 16 | @Override 17 | public Single searchRecipes(Map params) { 18 | return null; 19 | } 20 | 21 | @Override 22 | public Single getIngredientData(Map params) { 23 | return null; 24 | } 25 | 26 | @Override 27 | public Flowable> getFavorites() { 28 | return null; 29 | } 30 | 31 | @Override 32 | public Completable addToFavorites(Recipe favoriteRecipe) { 33 | return null; 34 | } 35 | 36 | @Override 37 | public Completable removeFromFavorites(Recipe removedRecipe) { 38 | return null; 39 | } 40 | 41 | @Override 42 | public Single isInFavorites(Recipe recipe) { 43 | return null; 44 | } 45 | 46 | @Override 47 | public Single getRecipeAnalysingResult(RecipeAnalysisParams params) { 48 | return null; 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /app/src/main/java/com/mlsdev/recipefinder/data/source/BaseObserver.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder.data.source; 2 | 3 | import android.util.Log; 4 | 5 | import com.mlsdev.recipefinder.view.MainActivity; 6 | 7 | import io.reactivex.SingleObserver; 8 | import io.reactivex.annotations.NonNull; 9 | import io.reactivex.disposables.Disposable; 10 | 11 | public abstract class BaseObserver implements SingleObserver { 12 | public static final int SERVER_ERROR = 500; 13 | 14 | @Override 15 | public void onSubscribe(@NonNull Disposable d) { 16 | 17 | } 18 | 19 | @Override 20 | public void onSuccess(@NonNull T t) { 21 | 22 | } 23 | 24 | @Override 25 | public void onError(Throwable e) { 26 | Log.e(MainActivity.LOG_TAG, e.getMessage()); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /app/src/main/java/com/mlsdev/recipefinder/data/source/DataSource.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder.data.source; 2 | 3 | import com.mlsdev.recipefinder.data.entity.nutrition.NutritionAnalysisResult; 4 | import com.mlsdev.recipefinder.data.entity.nutrition.RecipeAnalysisParams; 5 | import com.mlsdev.recipefinder.data.entity.recipe.Recipe; 6 | import com.mlsdev.recipefinder.data.entity.recipe.SearchResult; 7 | 8 | import java.util.List; 9 | import java.util.Map; 10 | 11 | import io.reactivex.Completable; 12 | import io.reactivex.Flowable; 13 | import io.reactivex.Single; 14 | 15 | public interface DataSource { 16 | Single searchRecipes(Map params); 17 | 18 | Single getIngredientData(Map params); 19 | 20 | Flowable> getFavorites(); 21 | 22 | Completable addToFavorites(Recipe favoriteRecipe); 23 | 24 | Completable removeFromFavorites(Recipe removedRecipe); 25 | 26 | Single isInFavorites(Recipe recipe); 27 | 28 | Single getRecipeAnalysingResult(RecipeAnalysisParams params); 29 | } 30 | -------------------------------------------------------------------------------- /app/src/main/java/com/mlsdev/recipefinder/data/source/local/roomdb/AppDatabase.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder.data.source.local.roomdb; 2 | 3 | import android.arch.persistence.room.Database; 4 | import android.arch.persistence.room.Room; 5 | import android.arch.persistence.room.RoomDatabase; 6 | import android.content.Context; 7 | 8 | import com.mlsdev.recipefinder.data.entity.nutrition.Nutrient; 9 | import com.mlsdev.recipefinder.data.entity.nutrition.TotalNutrients; 10 | import com.mlsdev.recipefinder.data.entity.recipe.Ingredient; 11 | import com.mlsdev.recipefinder.data.entity.recipe.Recipe; 12 | import com.mlsdev.recipefinder.data.source.local.roomdb.dao.IngredientDao; 13 | import com.mlsdev.recipefinder.data.source.local.roomdb.dao.NutrientDao; 14 | import com.mlsdev.recipefinder.data.source.local.roomdb.dao.RecipeDao; 15 | import com.mlsdev.recipefinder.data.source.local.roomdb.dao.TotalNutrientsDao; 16 | 17 | @Database( 18 | entities = { 19 | Recipe.class, 20 | TotalNutrients.class, 21 | Nutrient.class, 22 | Ingredient.class}, 23 | version = 1, 24 | exportSchema = false) 25 | public abstract class AppDatabase extends RoomDatabase { 26 | private static AppDatabase INSTANCE; 27 | 28 | public abstract RecipeDao recipeDao(); 29 | 30 | public abstract NutrientDao nutrientDao(); 31 | 32 | public abstract TotalNutrientsDao totalNutrientsDao(); 33 | 34 | public abstract IngredientDao ingredientDao(); 35 | 36 | public static AppDatabase getDb() { 37 | return INSTANCE; 38 | } 39 | 40 | public static AppDatabase init(Context context) { 41 | if (INSTANCE == null) { 42 | INSTANCE = 43 | Room.databaseBuilder(context.getApplicationContext(), AppDatabase.class, "recipes.db") 44 | .build(); 45 | } 46 | 47 | return INSTANCE; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /app/src/main/java/com/mlsdev/recipefinder/data/source/local/roomdb/converter/Converter.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder.data.source.local.roomdb.converter; 2 | 3 | import android.arch.persistence.room.TypeConverter; 4 | import android.arch.persistence.room.util.StringUtil; 5 | 6 | import java.util.Arrays; 7 | import java.util.Collections; 8 | import java.util.List; 9 | 10 | public class Converter { 11 | 12 | @TypeConverter 13 | public static String stringListToString(List stringList) { 14 | if (stringList == null) return null; 15 | 16 | String listAsString = stringList.toString().replaceAll(" ", ""); 17 | return listAsString.substring(1, listAsString.length() - 1); 18 | } 19 | 20 | @TypeConverter 21 | public static List stringToStringList(String string) { 22 | 23 | if (string == null) return null; 24 | 25 | return Arrays.asList(string.split(",")); 26 | } 27 | 28 | @TypeConverter 29 | public static String integersListToString(List longs) { 30 | 31 | if (longs == null) return null; 32 | 33 | return StringUtil.joinIntoString(longs); 34 | } 35 | 36 | @TypeConverter 37 | public static List stringToIntegerList(String string) { 38 | 39 | if (string == null) return Collections.emptyList(); 40 | 41 | return StringUtil.splitToIntList(string); 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /app/src/main/java/com/mlsdev/recipefinder/data/source/local/roomdb/dao/IngredientDao.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder.data.source.local.roomdb.dao; 2 | 3 | import android.arch.persistence.room.Dao; 4 | import android.arch.persistence.room.Insert; 5 | import android.arch.persistence.room.OnConflictStrategy; 6 | import android.arch.persistence.room.Query; 7 | 8 | import com.mlsdev.recipefinder.data.entity.recipe.Ingredient; 9 | 10 | import java.util.List; 11 | 12 | @Dao 13 | public interface IngredientDao { 14 | @Insert(onConflict = OnConflictStrategy.REPLACE) 15 | void insert(List ingredients); 16 | 17 | @Insert(onConflict = OnConflictStrategy.REPLACE) 18 | void insert(Ingredient ingredient); 19 | 20 | @Insert(onConflict = OnConflictStrategy.IGNORE) 21 | void createIfNotExist(Ingredient ingredient); 22 | 23 | @Query("select * from ingredients where recipe_uri = :uri") 24 | List loadByRecipeUri(String uri); 25 | 26 | @Query("delete from ingredients where recipe_uri = :uri") 27 | void deleteByRecipeUri(String uri); 28 | } 29 | -------------------------------------------------------------------------------- /app/src/main/java/com/mlsdev/recipefinder/data/source/local/roomdb/dao/NutrientDao.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder.data.source.local.roomdb.dao; 2 | 3 | import android.arch.persistence.room.Dao; 4 | import android.arch.persistence.room.Delete; 5 | import android.arch.persistence.room.Insert; 6 | import android.arch.persistence.room.OnConflictStrategy; 7 | import android.arch.persistence.room.Query; 8 | 9 | import com.mlsdev.recipefinder.data.entity.nutrition.Nutrient; 10 | 11 | import java.util.List; 12 | 13 | @Dao 14 | public interface NutrientDao { 15 | 16 | @Insert(onConflict = OnConflictStrategy.REPLACE) 17 | void insert(List nutrients); 18 | 19 | @Insert(onConflict = OnConflictStrategy.IGNORE) 20 | long createIfNotExist(Nutrient nutrient); 21 | 22 | @Query("select * from nutrients where id = :id") 23 | Nutrient loadById(long id); 24 | 25 | @Query("select * from nutrients where id in (:ids)") 26 | List loadByIds(List ids); 27 | 28 | @Query("delete from nutrients where id = :nutrientId") 29 | void deleteById(long nutrientId); 30 | 31 | @Delete 32 | void delete(Nutrient Nutrient); 33 | 34 | @Delete 35 | void delete(List nutrients); 36 | 37 | @Query("delete from nutrients where id in (:ids)") 38 | void deleteByIds(List ids); 39 | 40 | } 41 | -------------------------------------------------------------------------------- /app/src/main/java/com/mlsdev/recipefinder/data/source/local/roomdb/dao/RecipeDao.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder.data.source.local.roomdb.dao; 2 | 3 | import android.arch.persistence.room.Dao; 4 | import android.arch.persistence.room.Delete; 5 | import android.arch.persistence.room.Insert; 6 | import android.arch.persistence.room.OnConflictStrategy; 7 | import android.arch.persistence.room.Query; 8 | 9 | import com.mlsdev.recipefinder.data.entity.recipe.Recipe; 10 | 11 | import java.util.List; 12 | 13 | import io.reactivex.Flowable; 14 | 15 | @Dao 16 | public interface RecipeDao { 17 | 18 | @Insert(onConflict = OnConflictStrategy.REPLACE) 19 | long insert(Recipe recipe); 20 | 21 | @Query("select uri from recipes where uri = :recipeUri") 22 | Flowable loadByUri(String recipeUri); 23 | 24 | @Query("select * from recipes") 25 | Flowable> loadAll(); 26 | 27 | @Delete 28 | int delete(Recipe recipe); 29 | 30 | @Delete 31 | void delete(List recipes); 32 | 33 | @Query("delete from recipes where uri = :uri") 34 | void deleteByIds(String uri); 35 | 36 | } 37 | -------------------------------------------------------------------------------- /app/src/main/java/com/mlsdev/recipefinder/data/source/local/roomdb/dao/TotalNutrientsDao.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder.data.source.local.roomdb.dao; 2 | 3 | import android.arch.persistence.room.Dao; 4 | import android.arch.persistence.room.Delete; 5 | import android.arch.persistence.room.Insert; 6 | import android.arch.persistence.room.OnConflictStrategy; 7 | import android.arch.persistence.room.Query; 8 | 9 | import com.mlsdev.recipefinder.data.entity.nutrition.TotalNutrients; 10 | 11 | @Dao 12 | public interface TotalNutrientsDao { 13 | 14 | @Insert(onConflict = OnConflictStrategy.REPLACE) 15 | long createIfNotExist(TotalNutrients totalNutrients); 16 | 17 | @Query("select * from total_nutrients where id = :totalNutrientsId") 18 | TotalNutrients loadById(long totalNutrientsId); 19 | 20 | @Query("delete from total_nutrients where id = :totalNutrientsId") 21 | void deleteById(long totalNutrientsId); 22 | 23 | @Delete 24 | void delete(TotalNutrients totalNutrients); 25 | } 26 | -------------------------------------------------------------------------------- /app/src/main/java/com/mlsdev/recipefinder/data/source/remote/NutritionAnalysisService.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder.data.source.remote; 2 | 3 | import com.mlsdev.recipefinder.data.entity.nutrition.NutritionAnalysisResult; 4 | import com.mlsdev.recipefinder.data.entity.nutrition.RecipeAnalysisParams; 5 | 6 | import java.util.Map; 7 | 8 | import io.reactivex.Single; 9 | import retrofit2.http.Body; 10 | import retrofit2.http.GET; 11 | import retrofit2.http.POST; 12 | import retrofit2.http.QueryMap; 13 | 14 | public interface NutritionAnalysisService { 15 | 16 | @GET(PathConstants.NUTRITION_DATA) 17 | Single analyzeIngredient(@QueryMap Map params); 18 | 19 | @POST(PathConstants.NUTRITION_DETAILS) 20 | Single analyzeRecipe(@Body RecipeAnalysisParams params, 21 | @QueryMap Map credentials); 22 | 23 | } 24 | -------------------------------------------------------------------------------- /app/src/main/java/com/mlsdev/recipefinder/data/source/remote/ParameterKeys.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder.data.source.remote; 2 | 3 | public class ParameterKeys { 4 | public static final String APP_ID = "app_id"; 5 | public static final String APP_KEY = "app_key"; 6 | public static final String QUERY = "q"; 7 | public static final String FROM = "from"; 8 | public static final String TO = "to"; 9 | public static final String HEALTH = "health"; 10 | public static final String DIET = "diet"; 11 | public static final String INGREDIENT = "ingr"; 12 | } 13 | -------------------------------------------------------------------------------- /app/src/main/java/com/mlsdev/recipefinder/data/source/remote/PathConstants.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder.data.source.remote; 2 | 3 | public class PathConstants { 4 | public static final String BASE_URL = "https://api.edamam.com/"; 5 | public static final String SEARCH = "search"; 6 | public static final String NUTRITION_DATA = "api/nutrition-data"; 7 | public static final String NUTRITION_DETAILS = "api/nutrition-details"; 8 | } 9 | -------------------------------------------------------------------------------- /app/src/main/java/com/mlsdev/recipefinder/data/source/remote/RemoteDataSource.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder.data.source.remote; 2 | 3 | import android.support.v4.util.ArrayMap; 4 | 5 | import com.mlsdev.recipefinder.BuildConfig; 6 | import com.mlsdev.recipefinder.data.entity.nutrition.NutritionAnalysisResult; 7 | import com.mlsdev.recipefinder.data.entity.nutrition.RecipeAnalysisParams; 8 | import com.mlsdev.recipefinder.data.entity.recipe.SearchResult; 9 | import com.mlsdev.recipefinder.data.source.BaseDataSource; 10 | import com.mlsdev.recipefinder.data.source.DataSource; 11 | 12 | import java.util.Map; 13 | 14 | import io.reactivex.Single; 15 | import okhttp3.OkHttpClient; 16 | import okhttp3.logging.HttpLoggingInterceptor; 17 | import retrofit2.Retrofit; 18 | import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory; 19 | import retrofit2.converter.gson.GsonConverterFactory; 20 | 21 | public class RemoteDataSource extends BaseDataSource implements DataSource { 22 | private static String baseUrl = PathConstants.BASE_URL; 23 | private SearchRecipesService searchRecipesService; 24 | private NutritionAnalysisService nutritionAnalysisService; 25 | private static RemoteDataSource instance; 26 | 27 | public RemoteDataSource() { 28 | initApiServices(); 29 | } 30 | 31 | public static RemoteDataSource getInstance() { 32 | if (instance == null) 33 | instance = new RemoteDataSource(); 34 | 35 | return instance; 36 | } 37 | 38 | public static void setBaseUrl(String url) { 39 | baseUrl = url; 40 | instance = null; 41 | } 42 | 43 | private void initApiServices() { 44 | HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor(); 45 | interceptor.setLevel(BuildConfig.DEBUG ? HttpLoggingInterceptor.Level.BODY : HttpLoggingInterceptor.Level.NONE); 46 | 47 | OkHttpClient client = new OkHttpClient.Builder() 48 | .addInterceptor(interceptor) 49 | .build(); 50 | 51 | Retrofit retrofit = new Retrofit.Builder() 52 | .client(client) 53 | .baseUrl(baseUrl) 54 | .addConverterFactory(GsonConverterFactory.create()) 55 | .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) 56 | .build(); 57 | 58 | searchRecipesService = retrofit.create(SearchRecipesService.class); 59 | nutritionAnalysisService = retrofit.create(NutritionAnalysisService.class); 60 | } 61 | 62 | @Override 63 | public Single searchRecipes(Map params) { 64 | setCredentials(params, true); 65 | return searchRecipesService.searchRecipes(params); 66 | } 67 | 68 | @Override 69 | public Single getIngredientData(Map params) { 70 | setCredentials(params, false); 71 | return nutritionAnalysisService.analyzeIngredient(params); 72 | } 73 | 74 | @Override 75 | public Single getRecipeAnalysingResult(RecipeAnalysisParams params) { 76 | Map credentials = new ArrayMap<>(); 77 | setCredentials(credentials, false); 78 | return nutritionAnalysisService.analyzeRecipe(params, credentials); 79 | } 80 | 81 | private void setCredentials(Map params, boolean search) { 82 | params.put(ParameterKeys.APP_ID, search ? BuildConfig.SEARCH_APP_ID : BuildConfig.ANALYSE_APP_ID); 83 | params.put(ParameterKeys.APP_KEY, search ? BuildConfig.SEARCH_APP_KEY : BuildConfig.ANALYSE_APP_KEY); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /app/src/main/java/com/mlsdev/recipefinder/data/source/remote/SearchRecipesService.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder.data.source.remote; 2 | 3 | import com.mlsdev.recipefinder.data.entity.recipe.SearchResult; 4 | 5 | import java.util.Map; 6 | 7 | import io.reactivex.Single; 8 | import retrofit2.http.GET; 9 | import retrofit2.http.QueryMap; 10 | 11 | public interface SearchRecipesService { 12 | 13 | @GET(PathConstants.SEARCH) 14 | Single searchRecipes(@QueryMap Map params); 15 | 16 | } 17 | -------------------------------------------------------------------------------- /app/src/main/java/com/mlsdev/recipefinder/data/source/repository/DataRepository.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder.data.source.repository; 2 | 3 | import android.support.annotation.NonNull; 4 | 5 | import com.mlsdev.recipefinder.data.entity.nutrition.NutritionAnalysisResult; 6 | import com.mlsdev.recipefinder.data.entity.nutrition.RecipeAnalysisParams; 7 | import com.mlsdev.recipefinder.data.entity.recipe.Hit; 8 | import com.mlsdev.recipefinder.data.entity.recipe.Recipe; 9 | import com.mlsdev.recipefinder.data.entity.recipe.SearchResult; 10 | import com.mlsdev.recipefinder.data.source.DataSource; 11 | import com.mlsdev.recipefinder.data.source.remote.ParameterKeys; 12 | 13 | import java.util.ArrayList; 14 | import java.util.List; 15 | import java.util.Map; 16 | 17 | import io.reactivex.Completable; 18 | import io.reactivex.Flowable; 19 | import io.reactivex.Single; 20 | import io.reactivex.SingleSource; 21 | import io.reactivex.functions.Consumer; 22 | import io.reactivex.functions.Function; 23 | 24 | public class DataRepository { 25 | private final int offset = 10; 26 | private int from = 0; 27 | private int to = offset; 28 | private boolean more = true; 29 | 30 | private DataSource localDataSource; 31 | private DataSource remoteDataSource; 32 | 33 | private List cachedRecipes; 34 | private boolean cacheIsDirty = false; 35 | 36 | public DataRepository(DataSource local, DataSource remote) { 37 | localDataSource = local; 38 | remoteDataSource = remote; 39 | cachedRecipes = new ArrayList<>(); 40 | } 41 | 42 | public void setCacheIsDirty() { 43 | cacheIsDirty = true; 44 | } 45 | 46 | public Single> searchRecipes(Map params) { 47 | if (!cacheIsDirty) { 48 | return Single.just(cachedRecipes); 49 | } 50 | 51 | more = true; 52 | cachedRecipes.clear(); 53 | 54 | params.put(ParameterKeys.FROM, String.valueOf(0)); 55 | params.put(ParameterKeys.TO, String.valueOf(offset)); 56 | 57 | return getRecipes(params); 58 | } 59 | 60 | public Single> loadMore(Map params) { 61 | 62 | if (!more) 63 | return Single.amb(new ArrayList>>()); 64 | 65 | params.put(ParameterKeys.FROM, String.valueOf(from)); 66 | params.put(ParameterKeys.TO, String.valueOf(to)); 67 | 68 | return getRecipes(params); 69 | } 70 | 71 | @NonNull 72 | private Single> getRecipes(Map params) { 73 | return remoteDataSource.searchRecipes(params) 74 | .map(new Function>() { 75 | @Override 76 | public List apply(@io.reactivex.annotations.NonNull SearchResult searchResult) throws Exception { 77 | from = searchResult.getTo(); 78 | to = from + offset; 79 | more = searchResult.isMore(); 80 | 81 | List recipes = new ArrayList<>(); 82 | for (Hit hit : searchResult.getHits()) 83 | recipes.add(hit.getRecipe()); 84 | 85 | cachedRecipes.addAll(recipes); 86 | 87 | return recipes; 88 | } 89 | }) 90 | .doOnSuccess(new Consumer>() { 91 | @Override 92 | public void accept(@io.reactivex.annotations.NonNull List recipes) throws Exception { 93 | cacheIsDirty = false; 94 | } 95 | }); 96 | } 97 | 98 | public Flowable> getFavoriteRecipes() { 99 | return localDataSource.getFavorites(); 100 | } 101 | 102 | public Completable addToFavorites(Recipe favoriteRecipe) { 103 | return localDataSource.addToFavorites(favoriteRecipe); 104 | } 105 | 106 | public Completable removeFromFavorites(Recipe removedRecipe) { 107 | return localDataSource.removeFromFavorites(removedRecipe); 108 | } 109 | 110 | public Single isInFavorites(Recipe recipe) { 111 | return localDataSource.isInFavorites(recipe); 112 | } 113 | 114 | public Single getIngredientData(final Map params) { 115 | return remoteDataSource.getIngredientData(params); 116 | } 117 | 118 | public Single getRecipeAnalysisData(final RecipeAnalysisParams params) { 119 | return remoteDataSource.getRecipeAnalysingResult(params); 120 | } 121 | 122 | } 123 | -------------------------------------------------------------------------------- /app/src/main/java/com/mlsdev/recipefinder/di/ApplicationInjector.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder.di; 2 | 3 | import android.app.Activity; 4 | import android.app.Application; 5 | import android.os.Bundle; 6 | import android.support.v4.app.Fragment; 7 | import android.support.v4.app.FragmentActivity; 8 | import android.support.v4.app.FragmentManager; 9 | 10 | import com.mlsdev.recipefinder.RecipeApplication; 11 | import com.mlsdev.recipefinder.di.component.DaggerApplicationComponent; 12 | 13 | import dagger.android.AndroidInjection; 14 | import dagger.android.support.AndroidSupportInjection; 15 | import dagger.android.support.HasSupportFragmentInjector; 16 | 17 | public class ApplicationInjector { 18 | 19 | private ApplicationInjector() { 20 | } 21 | 22 | public static void init(RecipeApplication application) { 23 | DaggerApplicationComponent.builder() 24 | .application(application) 25 | .build() 26 | .inject(application); 27 | 28 | application.registerActivityLifecycleCallbacks(new Application.ActivityLifecycleCallbacks() { 29 | @Override 30 | public void onActivityCreated(Activity activity, Bundle savedInstanceState) { 31 | handleActivity(activity); 32 | } 33 | 34 | @Override 35 | public void onActivityStarted(Activity activity) { 36 | 37 | } 38 | 39 | @Override 40 | public void onActivityResumed(Activity activity) { 41 | 42 | } 43 | 44 | @Override 45 | public void onActivityPaused(Activity activity) { 46 | 47 | } 48 | 49 | @Override 50 | public void onActivityStopped(Activity activity) { 51 | 52 | } 53 | 54 | @Override 55 | public void onActivitySaveInstanceState(Activity activity, Bundle outState) { 56 | 57 | } 58 | 59 | @Override 60 | public void onActivityDestroyed(Activity activity) { 61 | 62 | } 63 | }); 64 | } 65 | 66 | private static void handleActivity(Activity activity) { 67 | 68 | if (activity instanceof HasSupportFragmentInjector) { 69 | AndroidInjection.inject(activity); 70 | } 71 | 72 | if (activity instanceof FragmentActivity) { 73 | ((FragmentActivity) activity).getSupportFragmentManager() 74 | .registerFragmentLifecycleCallbacks(new FragmentManager.FragmentLifecycleCallbacks() { 75 | @Override 76 | public void onFragmentCreated(FragmentManager fm, Fragment f, Bundle savedInstanceState) { 77 | if (f instanceof Injectable) 78 | AndroidSupportInjection.inject(f); 79 | } 80 | }, true); 81 | } 82 | 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /app/src/main/java/com/mlsdev/recipefinder/di/Injectable.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder.di; 2 | 3 | public interface Injectable { 4 | } 5 | -------------------------------------------------------------------------------- /app/src/main/java/com/mlsdev/recipefinder/di/component/ApplicationComponent.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder.di.component; 2 | 3 | import android.app.Application; 4 | 5 | import com.mlsdev.recipefinder.RecipeApplication; 6 | import com.mlsdev.recipefinder.di.module.ApiModule; 7 | import com.mlsdev.recipefinder.di.module.DataSourceModule; 8 | import com.mlsdev.recipefinder.di.module.DatabaseModule; 9 | import com.mlsdev.recipefinder.di.module.MainActivityModule; 10 | import com.mlsdev.recipefinder.di.module.RecipeAnalysisActivityModule; 11 | import com.mlsdev.recipefinder.di.module.UtilsModule; 12 | import com.mlsdev.recipefinder.di.module.ViewModelModule; 13 | 14 | import javax.inject.Singleton; 15 | 16 | import dagger.BindsInstance; 17 | import dagger.Component; 18 | import dagger.android.AndroidInjectionModule; 19 | 20 | @Singleton 21 | @Component(modules = { 22 | AndroidInjectionModule.class, 23 | UtilsModule.class, 24 | DataSourceModule.class, 25 | DatabaseModule.class, 26 | ApiModule.class, 27 | MainActivityModule.class, 28 | RecipeAnalysisActivityModule.class, 29 | ViewModelModule.class}) 30 | public interface ApplicationComponent { 31 | 32 | @Component.Builder 33 | interface Builder { 34 | @BindsInstance 35 | Builder application(Application application); 36 | 37 | ApplicationComponent build(); 38 | } 39 | 40 | void inject(RecipeApplication application); 41 | 42 | } 43 | -------------------------------------------------------------------------------- /app/src/main/java/com/mlsdev/recipefinder/di/module/ApiModule.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder.di.module; 2 | 3 | import com.google.gson.Gson; 4 | import com.mlsdev.recipefinder.BuildConfig; 5 | import com.mlsdev.recipefinder.data.source.remote.NutritionAnalysisService; 6 | import com.mlsdev.recipefinder.data.source.remote.PathConstants; 7 | import com.mlsdev.recipefinder.data.source.remote.SearchRecipesService; 8 | 9 | import javax.inject.Named; 10 | import javax.inject.Singleton; 11 | 12 | import dagger.Module; 13 | import dagger.Provides; 14 | import okhttp3.Interceptor; 15 | import okhttp3.OkHttpClient; 16 | import okhttp3.logging.HttpLoggingInterceptor; 17 | import retrofit2.Retrofit; 18 | import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory; 19 | import retrofit2.converter.gson.GsonConverterFactory; 20 | 21 | @Module 22 | public class ApiModule { 23 | public static final String HTTP_LOGGING = "http_logging_interceptor"; 24 | public String baseUrl = PathConstants.BASE_URL; 25 | 26 | @Provides 27 | @Singleton 28 | Gson provideGson() { 29 | return new Gson(); 30 | } 31 | 32 | @Provides 33 | @Named(HTTP_LOGGING) 34 | Interceptor provideHttpLoggingInterceptor() { 35 | HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor(); 36 | interceptor.setLevel(BuildConfig.DEBUG ? HttpLoggingInterceptor.Level.BODY : HttpLoggingInterceptor.Level.NONE); 37 | return interceptor; 38 | } 39 | 40 | @Provides 41 | @Singleton 42 | OkHttpClient provideOkHttpClient(@Named(HTTP_LOGGING) Interceptor httpLoggingInterceptor) { 43 | return new OkHttpClient.Builder() 44 | .addInterceptor(httpLoggingInterceptor) 45 | .build(); 46 | } 47 | 48 | @Provides 49 | @Singleton 50 | Retrofit provideRetrofit(OkHttpClient okHttpClient) { 51 | return new Retrofit.Builder() 52 | .client(okHttpClient) 53 | .baseUrl(baseUrl) 54 | .addConverterFactory(GsonConverterFactory.create()) 55 | .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) 56 | .build(); 57 | } 58 | 59 | @Provides 60 | @Singleton 61 | SearchRecipesService provideSearchRecipesService(Retrofit retrofit) { 62 | return retrofit.create(SearchRecipesService.class); 63 | } 64 | 65 | @Provides 66 | @Singleton 67 | NutritionAnalysisService provideNutritionAnalysisService(Retrofit retrofit) { 68 | return retrofit.create(NutritionAnalysisService.class); 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /app/src/main/java/com/mlsdev/recipefinder/di/module/DataSourceModule.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder.di.module; 2 | 3 | import com.mlsdev.recipefinder.data.source.DataSource; 4 | import com.mlsdev.recipefinder.data.source.local.LocalDataSource; 5 | import com.mlsdev.recipefinder.data.source.local.roomdb.AppDatabase; 6 | import com.mlsdev.recipefinder.data.source.remote.RemoteDataSource; 7 | import com.mlsdev.recipefinder.data.source.repository.DataRepository; 8 | 9 | import javax.inject.Named; 10 | import javax.inject.Singleton; 11 | 12 | import dagger.Module; 13 | import dagger.Provides; 14 | 15 | @Module 16 | public class DataSourceModule { 17 | public static final String LOCAL_DATA_SOURCE = "local_data_source"; 18 | public static final String REMOTE_DATA_SOURCE = "remote_data_source"; 19 | 20 | @Provides 21 | @Singleton 22 | DataRepository provideDataRepository(@Named(LOCAL_DATA_SOURCE) DataSource local, 23 | @Named(REMOTE_DATA_SOURCE) DataSource remote) { 24 | return new DataRepository(local, remote); 25 | } 26 | 27 | @Provides 28 | @Singleton 29 | @Named(LOCAL_DATA_SOURCE) 30 | DataSource provideLocalDataSource(AppDatabase database) { 31 | return new LocalDataSource(database); 32 | } 33 | 34 | @Provides 35 | @Singleton 36 | @Named(REMOTE_DATA_SOURCE) 37 | DataSource provideRemoteDataSource() { 38 | return new RemoteDataSource(); 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /app/src/main/java/com/mlsdev/recipefinder/di/module/DatabaseModule.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder.di.module; 2 | 3 | import android.app.Application; 4 | import android.arch.persistence.room.Room; 5 | 6 | import com.mlsdev.recipefinder.data.source.local.roomdb.AppDatabase; 7 | 8 | import javax.inject.Singleton; 9 | 10 | import dagger.Module; 11 | import dagger.Provides; 12 | 13 | @Module 14 | public class DatabaseModule { 15 | 16 | @Provides 17 | @Singleton 18 | AppDatabase provideDatabase(Application application) { 19 | return Room.databaseBuilder(application.getApplicationContext(), AppDatabase.class, "recipes.db") 20 | .build(); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /app/src/main/java/com/mlsdev/recipefinder/di/module/FragmentBuilderModule.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder.di.module; 2 | 3 | import com.mlsdev.recipefinder.view.analysenutrition.ingredient.IngredientAnalysisFragment; 4 | import com.mlsdev.recipefinder.view.analysenutrition.recipe.RecipeAnalysisFragment; 5 | import com.mlsdev.recipefinder.view.favoriterecipes.FavoriteRecipesFragment; 6 | import com.mlsdev.recipefinder.view.recipedetails.RecipeDetailsFragment; 7 | import com.mlsdev.recipefinder.view.searchrecipes.SearchRecipeFragment; 8 | 9 | import dagger.Module; 10 | import dagger.android.ContributesAndroidInjector; 11 | 12 | @Module 13 | public abstract class FragmentBuilderModule { 14 | 15 | @ContributesAndroidInjector 16 | abstract SearchRecipeFragment contributeSearchRecipeFragment(); 17 | 18 | @ContributesAndroidInjector 19 | abstract FavoriteRecipesFragment contributeFavoriteRecipesFragment(); 20 | 21 | @ContributesAndroidInjector 22 | abstract IngredientAnalysisFragment contributeIngredientAnalysisFragment(); 23 | 24 | @ContributesAndroidInjector 25 | abstract RecipeAnalysisFragment contributeRecipeAnalysisFragment(); 26 | 27 | @ContributesAndroidInjector 28 | abstract RecipeDetailsFragment contribureRecipeDetailsFragment(); 29 | 30 | } 31 | -------------------------------------------------------------------------------- /app/src/main/java/com/mlsdev/recipefinder/di/module/MainActivityModule.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder.di.module; 2 | 3 | import com.mlsdev.recipefinder.view.MainActivity; 4 | 5 | import dagger.Module; 6 | import dagger.android.ContributesAndroidInjector; 7 | 8 | @Module 9 | public abstract class MainActivityModule { 10 | 11 | @ContributesAndroidInjector(modules = {FragmentBuilderModule.class}) 12 | abstract MainActivity contributeMainActivityInjector(); 13 | } 14 | -------------------------------------------------------------------------------- /app/src/main/java/com/mlsdev/recipefinder/di/module/RecipeAnalysisActivityModule.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder.di.module; 2 | 3 | import com.mlsdev.recipefinder.view.analysenutrition.recipe.RecipeAnalysisDetailsActivity; 4 | 5 | import dagger.Module; 6 | import dagger.android.ContributesAndroidInjector; 7 | 8 | @Module 9 | public abstract class RecipeAnalysisActivityModule { 10 | @ContributesAndroidInjector(modules = {FragmentBuilderModule.class}) 11 | abstract RecipeAnalysisDetailsActivity contributeActivity(); 12 | } 13 | -------------------------------------------------------------------------------- /app/src/main/java/com/mlsdev/recipefinder/di/module/UtilsModule.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder.di.module; 2 | 3 | import com.mlsdev.recipefinder.view.utils.DiagramUtils; 4 | import com.mlsdev.recipefinder.view.utils.ParamsHelper; 5 | import com.mlsdev.recipefinder.view.utils.UtilsUI; 6 | 7 | import javax.inject.Singleton; 8 | 9 | import dagger.Module; 10 | import dagger.Provides; 11 | 12 | @Module 13 | public class UtilsModule { 14 | 15 | @Provides 16 | @Singleton 17 | DiagramUtils provideDiagramUtils(UtilsUI utilsUI) { 18 | return new DiagramUtils(utilsUI); 19 | } 20 | 21 | @Provides 22 | @Singleton 23 | UtilsUI provideUtilsUI() { 24 | return new UtilsUI(); 25 | } 26 | 27 | @Provides 28 | @Singleton 29 | ParamsHelper provideParamsHelper() { 30 | return new ParamsHelper(); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /app/src/main/java/com/mlsdev/recipefinder/di/module/ViewModelKey.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder.di.module; 2 | 3 | import android.arch.lifecycle.ViewModel; 4 | 5 | import java.lang.annotation.Documented; 6 | import java.lang.annotation.ElementType; 7 | import java.lang.annotation.Retention; 8 | import java.lang.annotation.RetentionPolicy; 9 | import java.lang.annotation.Target; 10 | 11 | import dagger.MapKey; 12 | 13 | @Documented 14 | @Target(ElementType.METHOD) 15 | @Retention(RetentionPolicy.RUNTIME) 16 | @MapKey 17 | @interface ViewModelKey { 18 | Class value(); 19 | } 20 | -------------------------------------------------------------------------------- /app/src/main/java/com/mlsdev/recipefinder/di/module/ViewModelModule.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder.di.module; 2 | 3 | import android.arch.lifecycle.ViewModel; 4 | import android.arch.lifecycle.ViewModelProvider; 5 | 6 | import com.mlsdev.recipefinder.view.analysenutrition.ingredient.IngredientAnalysisViewModel; 7 | import com.mlsdev.recipefinder.view.analysenutrition.recipe.RecipeAnalysisDetailsViewModel; 8 | import com.mlsdev.recipefinder.view.analysenutrition.recipe.RecipeAnalysisViewModel; 9 | import com.mlsdev.recipefinder.view.favoriterecipes.FavoritesViewModel; 10 | import com.mlsdev.recipefinder.view.recipedetails.RecipeViewModel; 11 | import com.mlsdev.recipefinder.view.searchrecipes.SearchViewModel; 12 | import com.mlsdev.recipefinder.view.viewmodel.ViewModelFactory; 13 | 14 | import dagger.Binds; 15 | import dagger.Module; 16 | import dagger.multibindings.IntoMap; 17 | 18 | @Module 19 | public abstract class ViewModelModule { 20 | 21 | @Binds 22 | @IntoMap 23 | @ViewModelKey(SearchViewModel.class) 24 | abstract ViewModel bindSearchViewModel(SearchViewModel searchViewModel); 25 | 26 | @Binds 27 | @IntoMap 28 | @ViewModelKey(FavoritesViewModel.class) 29 | abstract ViewModel bindFavoritesViewModel(FavoritesViewModel favoritesViewModel); 30 | 31 | @Binds 32 | @IntoMap 33 | @ViewModelKey(IngredientAnalysisViewModel.class) 34 | abstract ViewModel bindIngredientAnalysisViewModel(IngredientAnalysisViewModel ingredientAnalysisViewModel); 35 | 36 | @Binds 37 | @IntoMap 38 | @ViewModelKey(RecipeAnalysisViewModel.class) 39 | abstract ViewModel bindRecipeAnalysisViewModel(RecipeAnalysisViewModel recipeAnalysisViewModel); 40 | 41 | @Binds 42 | @IntoMap 43 | @ViewModelKey(RecipeViewModel.class) 44 | abstract ViewModel bindRecipeViewModel(RecipeViewModel recipeViewModel); 45 | 46 | @Binds 47 | @IntoMap 48 | @ViewModelKey(RecipeAnalysisDetailsViewModel.class) 49 | abstract ViewModel bindRecipeAnalysisDetailsViewModel(RecipeAnalysisDetailsViewModel viewModel); 50 | 51 | @Binds 52 | abstract ViewModelProvider.Factory bindViewModelFactory(ViewModelFactory factory); 53 | 54 | } 55 | -------------------------------------------------------------------------------- /app/src/main/java/com/mlsdev/recipefinder/view/ActionListener.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder.view; 2 | 3 | import android.support.annotation.StringRes; 4 | import android.view.View; 5 | 6 | public interface ActionListener { 7 | 8 | void onStartFilter(); 9 | 10 | void showProgressDialog(boolean show, String message); 11 | 12 | void showSnackbar(@StringRes int message); 13 | 14 | void showSnackbar(String message); 15 | 16 | void showSnackbar(String message, String action, View.OnClickListener listener); 17 | 18 | void showSnackbar(@StringRes int message, @StringRes int action, View.OnClickListener listener); 19 | 20 | } 21 | -------------------------------------------------------------------------------- /app/src/main/java/com/mlsdev/recipefinder/view/BaseActivity.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder.view; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.os.Bundle; 7 | import android.support.annotation.Nullable; 8 | import android.support.annotation.StringRes; 9 | import android.support.v7.app.AppCompatActivity; 10 | import android.view.MenuItem; 11 | import android.view.View; 12 | import android.view.inputmethod.InputMethodManager; 13 | 14 | import com.mlsdev.recipefinder.view.message.ProgressDialogMessage; 15 | import com.mlsdev.recipefinder.view.message.SnackbarMessage; 16 | 17 | public class BaseActivity extends AppCompatActivity implements NavigationController, ActionListener { 18 | private ProgressDialogMessage progressDialogMessage; 19 | private SnackbarMessage snackbarMessage; 20 | 21 | @Override 22 | public void onCreate(@Nullable Bundle savedInstanceState) { 23 | super.onCreate(savedInstanceState); 24 | progressDialogMessage = new ProgressDialogMessage(this); 25 | snackbarMessage = new SnackbarMessage(this); 26 | } 27 | 28 | @Override 29 | public boolean onOptionsItemSelected(MenuItem item) { 30 | if (item.getItemId() == android.R.id.home) 31 | onBackPressed(); 32 | return super.onOptionsItemSelected(item); 33 | } 34 | 35 | public void hideSoftKeyboard() { 36 | View view = getCurrentFocus(); 37 | 38 | if (view != null) { 39 | InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); 40 | inputMethodManager.hideSoftInputFromInputMethod(view.getWindowToken(), 0); 41 | } 42 | } 43 | 44 | public void showSoftKeyboard() { 45 | InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); 46 | imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0); 47 | } 48 | 49 | @Override 50 | public void launchActivity(Intent intent) { 51 | startActivity(intent); 52 | } 53 | 54 | @Override 55 | public void launchActivityForResult(Intent intent, int requestCode) { 56 | startActivityForResult(intent, requestCode); 57 | } 58 | 59 | @Override 60 | public void finishCurrentActivity() { 61 | finish(); 62 | } 63 | 64 | @Override 65 | public void finishWithResult(Intent data) { 66 | setActivityResult(data); 67 | finish(); 68 | } 69 | 70 | @Override 71 | public void setActivityResult(Intent data) { 72 | setResult(Activity.RESULT_OK, data); 73 | } 74 | 75 | @Override 76 | public void onStartFilter() { 77 | 78 | } 79 | 80 | @Override 81 | public void showProgressDialog(boolean show, String message) { 82 | progressDialogMessage.showProgressDialog(show, message); 83 | } 84 | 85 | @Override 86 | public void showSnackbar(@StringRes int message) { 87 | snackbarMessage.showSnackbar(message); 88 | } 89 | 90 | @Override 91 | public void showSnackbar(String message) { 92 | snackbarMessage.showSnackbar(message); 93 | } 94 | 95 | @Override 96 | public void showSnackbar(String message, String action, View.OnClickListener listener) { 97 | snackbarMessage.showSnackbar(message, action, listener); 98 | } 99 | 100 | @Override 101 | public void showSnackbar(@StringRes int message, @StringRes int action, View.OnClickListener listener) { 102 | snackbarMessage.showSnackbar(message, action, listener); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /app/src/main/java/com/mlsdev/recipefinder/view/BottomNavigationItemSelectedListener.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder.view; 2 | 3 | import android.arch.lifecycle.Lifecycle; 4 | import android.arch.lifecycle.LifecycleObserver; 5 | import android.arch.lifecycle.OnLifecycleEvent; 6 | import android.support.annotation.NonNull; 7 | import android.support.v4.app.FragmentManager; 8 | import android.support.v4.app.FragmentTransaction; 9 | import android.view.MenuItem; 10 | 11 | import com.mlsdev.recipefinder.R; 12 | import com.mlsdev.recipefinder.view.fragments.TabFragment; 13 | 14 | import javax.inject.Inject; 15 | 16 | import static android.support.design.widget.BottomNavigationView.OnNavigationItemSelectedListener; 17 | import static com.mlsdev.recipefinder.view.enums.TabItemType.ANALYSE; 18 | import static com.mlsdev.recipefinder.view.enums.TabItemType.FAVORITES; 19 | import static com.mlsdev.recipefinder.view.enums.TabItemType.SEARCH; 20 | 21 | public class BottomNavigationItemSelectedListener implements OnNavigationItemSelectedListener, 22 | LifecycleObserver { 23 | private FragmentManager fragmentManager; 24 | private TabFragment analyseNutritionFragment; 25 | private TabFragment searchRecipesFragment; 26 | private TabFragment favoriteRecipesFragment; 27 | private TabFragment currentFragment; 28 | private int checkedItemId = -1; 29 | private MenuItem currentMenuItem; 30 | 31 | @Inject 32 | public BottomNavigationItemSelectedListener() { 33 | analyseNutritionFragment = TabFragment.getNewInstance(ANALYSE); 34 | searchRecipesFragment = TabFragment.getNewInstance(SEARCH); 35 | favoriteRecipesFragment = TabFragment.getNewInstance(FAVORITES); 36 | } 37 | 38 | public void setFragmentManager(FragmentManager fragmentManager) { 39 | this.fragmentManager = fragmentManager; 40 | } 41 | 42 | public void setCurrentMenuItem(MenuItem currentMenuItem) { 43 | if (this.currentMenuItem == null) 44 | this.currentMenuItem = currentMenuItem; 45 | } 46 | 47 | @OnLifecycleEvent(Lifecycle.Event.ON_START) 48 | void start() { 49 | if (currentMenuItem != null) 50 | onNavigationItemSelected(currentMenuItem); 51 | } 52 | 53 | @Override 54 | public boolean onNavigationItemSelected(@NonNull MenuItem item) { 55 | currentMenuItem = item; 56 | 57 | if (checkedItemId == item.getItemId()) { 58 | currentFragment.scrollToTop(); 59 | return true; 60 | } 61 | 62 | checkedItemId = item.getItemId(); 63 | 64 | switch (item.getItemId()) { 65 | case R.id.action_analyse_nutrition: 66 | replaceFragment(analyseNutritionFragment); 67 | return true; 68 | case R.id.action_search_recipe: 69 | replaceFragment(searchRecipesFragment); 70 | return true; 71 | case R.id.action_favorites: 72 | replaceFragment(favoriteRecipesFragment); 73 | return true; 74 | default: 75 | return false; 76 | 77 | } 78 | 79 | } 80 | 81 | private void replaceFragment(TabFragment fragment) { 82 | currentFragment = fragment; 83 | 84 | clearBackStack(); 85 | 86 | fragmentManager.beginTransaction() 87 | .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE) 88 | .replace(R.id.fl_content, fragment) 89 | .commit(); 90 | } 91 | 92 | private void clearBackStack() { 93 | if (fragmentManager.getBackStackEntryCount() > 0) { 94 | fragmentManager.popBackStackImmediate(); 95 | clearBackStack(); 96 | } 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /app/src/main/java/com/mlsdev/recipefinder/view/Extras.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder.view; 2 | 3 | public class Extras { 4 | public static final String ALERT_DIALOG_TITLE = "alert_dialog_title"; 5 | public static final String ALERT_DIALOG_MESSAGE = "alert_dialog_message"; 6 | public static final String IMAGE_DATA = "image_data"; 7 | public static final String DATA = "data"; 8 | public static final String IMAGE_TRANSITION_NAME = "image_transition_name"; 9 | } 10 | -------------------------------------------------------------------------------- /app/src/main/java/com/mlsdev/recipefinder/view/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder.view; 2 | 3 | import android.arch.lifecycle.LifecycleRegistry; 4 | import android.arch.lifecycle.LifecycleRegistryOwner; 5 | import android.content.BroadcastReceiver; 6 | import android.content.Context; 7 | import android.content.Intent; 8 | import android.content.IntentFilter; 9 | import android.databinding.DataBindingUtil; 10 | import android.os.Bundle; 11 | import android.support.v4.app.Fragment; 12 | import android.support.v4.content.LocalBroadcastManager; 13 | import android.support.v7.app.AlertDialog; 14 | 15 | import com.mlsdev.recipefinder.R; 16 | import com.mlsdev.recipefinder.databinding.ActivityMainBinding; 17 | 18 | import javax.inject.Inject; 19 | 20 | import dagger.android.AndroidInjection; 21 | import dagger.android.DispatchingAndroidInjector; 22 | import dagger.android.support.HasSupportFragmentInjector; 23 | 24 | public class MainActivity extends BaseActivity implements LifecycleRegistryOwner, 25 | HasSupportFragmentInjector { 26 | public static final String LOG_TAG = "RECIPE_FINDER"; 27 | private ActivityMainBinding binding; 28 | private AppBroadcastReceiver broadcastReceiver; 29 | private LifecycleRegistry lifecycleRegistry = new LifecycleRegistry(this); 30 | 31 | @Inject 32 | DispatchingAndroidInjector supportFragmentInjector; 33 | 34 | @Inject 35 | BottomNavigationItemSelectedListener bottomNavigationItemSelectedListener; 36 | 37 | @Override 38 | public void onCreate(Bundle savedInstanceState) { 39 | AndroidInjection.inject(this); 40 | super.onCreate(savedInstanceState); 41 | binding = DataBindingUtil.setContentView(this, R.layout.activity_main); 42 | broadcastReceiver = new AppBroadcastReceiver(); 43 | initNavigation(); 44 | } 45 | 46 | @Override 47 | protected void onStart() { 48 | super.onStart(); 49 | LocalBroadcastManager.getInstance(this) 50 | .registerReceiver(broadcastReceiver, new IntentFilter(AppBroadcastReceiver.SHOW_ERROR_ACTION)); 51 | } 52 | 53 | @Override 54 | protected void onStop() { 55 | super.onStop(); 56 | LocalBroadcastManager.getInstance(this) 57 | .unregisterReceiver(broadcastReceiver); 58 | } 59 | 60 | private void initNavigation() { 61 | getLifecycle().addObserver(bottomNavigationItemSelectedListener); 62 | bottomNavigationItemSelectedListener.setCurrentMenuItem(binding.bnvNavigationView.getMenu().getItem(0)); 63 | bottomNavigationItemSelectedListener.setFragmentManager(getSupportFragmentManager()); 64 | binding.bnvNavigationView.setOnNavigationItemSelectedListener(bottomNavigationItemSelectedListener); 65 | } 66 | 67 | @Override 68 | public LifecycleRegistry getLifecycle() { 69 | return lifecycleRegistry; 70 | } 71 | 72 | @Override 73 | public DispatchingAndroidInjector supportFragmentInjector() { 74 | return supportFragmentInjector; 75 | } 76 | 77 | public class AppBroadcastReceiver extends BroadcastReceiver { 78 | public static final String SHOW_ERROR_ACTION = "show_error"; 79 | 80 | @Override 81 | public void onReceive(Context context, Intent intent) { 82 | if (intent.getAction().equals(SHOW_ERROR_ACTION)) { 83 | AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this, R.style.AlertDialogAppCompat); 84 | 85 | if (intent.hasExtra(Extras.ALERT_DIALOG_TITLE)) 86 | builder.setTitle(intent.getStringExtra(Extras.ALERT_DIALOG_TITLE)); 87 | 88 | if (intent.hasExtra(Extras.ALERT_DIALOG_MESSAGE)) 89 | builder.setMessage(intent.getStringExtra(Extras.ALERT_DIALOG_MESSAGE)); 90 | 91 | builder.setPositiveButton(android.R.string.ok, null); 92 | builder.create().show(); 93 | } 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /app/src/main/java/com/mlsdev/recipefinder/view/NavigationController.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder.view; 2 | 3 | import android.content.Intent; 4 | 5 | public interface NavigationController { 6 | 7 | void launchActivity(Intent intent); 8 | 9 | void launchActivityForResult(Intent intent, int requestCode); 10 | 11 | void finishCurrentActivity(); 12 | 13 | void finishWithResult(Intent data); 14 | 15 | void setActivityResult(Intent data); 16 | 17 | } 18 | -------------------------------------------------------------------------------- /app/src/main/java/com/mlsdev/recipefinder/view/OnKeyboardStateChangedListener.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder.view; 2 | 3 | public interface OnKeyboardStateChangedListener { 4 | 5 | void showKeyboard(); 6 | 7 | void hideKeyboard(); 8 | 9 | } 10 | -------------------------------------------------------------------------------- /app/src/main/java/com/mlsdev/recipefinder/view/analysenutrition/AnalyseNutritionFragment.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder.view.analysenutrition; 2 | 3 | import android.databinding.DataBindingUtil; 4 | import android.os.Bundle; 5 | import android.support.annotation.Nullable; 6 | import android.support.v4.app.Fragment; 7 | import android.support.v4.app.FragmentManager; 8 | import android.support.v4.app.FragmentPagerAdapter; 9 | import android.view.LayoutInflater; 10 | import android.view.View; 11 | import android.view.ViewGroup; 12 | 13 | import com.mlsdev.recipefinder.R; 14 | import com.mlsdev.recipefinder.databinding.FragmentAnalyseNutritionBinding; 15 | import com.mlsdev.recipefinder.view.analysenutrition.ingredient.IngredientAnalysisFragment; 16 | import com.mlsdev.recipefinder.view.analysenutrition.recipe.RecipeAnalysisFragment; 17 | import com.mlsdev.recipefinder.view.fragments.TabFragment; 18 | 19 | public class AnalyseNutritionFragment extends TabFragment { 20 | public static final int INGREDIENT_ANALYSIS_FRAGMENT = 0; 21 | public static final int RECIPE_ANALYSIS_FRAGMENT = 1; 22 | public static final int PAGES_NUMBER = 2; 23 | private FragmentAnalyseNutritionBinding binding; 24 | private ViewPagerAdapter pagerAdapter; 25 | 26 | @Nullable 27 | @Override 28 | public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, 29 | @Nullable Bundle savedInstanceState) { 30 | 31 | binding = DataBindingUtil.inflate(inflater, R.layout.fragment_analyse_nutrition, container, false); 32 | initViewPager(); 33 | return binding.getRoot(); 34 | } 35 | 36 | private void initViewPager() { 37 | pagerAdapter = new ViewPagerAdapter(getChildFragmentManager()); 38 | binding.vpAnalysisPages.setAdapter(pagerAdapter); 39 | binding.vpAnalysisPages.setOffscreenPageLimit(PAGES_NUMBER); 40 | binding.tlAnalysisTabs.setupWithViewPager(binding.vpAnalysisPages); 41 | } 42 | 43 | public class ViewPagerAdapter extends FragmentPagerAdapter { 44 | Fragment nutritionAnalysisFragment; 45 | Fragment recipeAnalysisFragment; 46 | 47 | public ViewPagerAdapter(FragmentManager fm) { 48 | super(fm); 49 | nutritionAnalysisFragment = new IngredientAnalysisFragment(); 50 | recipeAnalysisFragment = new RecipeAnalysisFragment(); 51 | } 52 | 53 | @Override 54 | public Fragment getItem(int position) { 55 | return position == RECIPE_ANALYSIS_FRAGMENT 56 | ? recipeAnalysisFragment 57 | : nutritionAnalysisFragment; 58 | } 59 | 60 | @Override 61 | public int getCount() { 62 | return PAGES_NUMBER; 63 | } 64 | 65 | @Override 66 | public CharSequence getPageTitle(int position) { 67 | return getString(position == RECIPE_ANALYSIS_FRAGMENT 68 | ? R.string.tab_recipe_analysis 69 | : R.string.tab_nutrition_analysis); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /app/src/main/java/com/mlsdev/recipefinder/view/analysenutrition/adapter/BaseViewHolder.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder.view.analysenutrition.adapter; 2 | 3 | import android.support.v7.widget.RecyclerView; 4 | import android.view.View; 5 | 6 | 7 | public abstract class BaseViewHolder extends RecyclerView.ViewHolder { 8 | public BaseViewHolder(View itemView) { 9 | super(itemView); 10 | } 11 | 12 | public abstract void bindViewModel(); 13 | } 14 | -------------------------------------------------------------------------------- /app/src/main/java/com/mlsdev/recipefinder/view/analysenutrition/adapter/ProgressViewHolder.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder.view.analysenutrition.adapter; 2 | 3 | import com.mlsdev.recipefinder.databinding.ProgressViewBinding; 4 | 5 | public class ProgressViewHolder extends BaseViewHolder { 6 | 7 | public ProgressViewHolder(ProgressViewBinding binding) { 8 | super(binding.getRoot()); 9 | } 10 | 11 | @Override 12 | public void bindViewModel() { 13 | 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /app/src/main/java/com/mlsdev/recipefinder/view/analysenutrition/ingredient/IngredientAnalysisFragment.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder.view.analysenutrition.ingredient; 2 | 3 | import android.arch.lifecycle.ViewModelProvider; 4 | import android.arch.lifecycle.ViewModelProviders; 5 | import android.databinding.DataBindingUtil; 6 | import android.os.Bundle; 7 | import android.support.annotation.Nullable; 8 | import android.view.LayoutInflater; 9 | import android.view.View; 10 | import android.view.ViewGroup; 11 | 12 | import com.github.mikephil.charting.data.PieData; 13 | import com.mlsdev.recipefinder.R; 14 | import com.mlsdev.recipefinder.databinding.FragmentIngredientAnalysisBinding; 15 | import com.mlsdev.recipefinder.di.Injectable; 16 | import com.mlsdev.recipefinder.view.MainActivity; 17 | import com.mlsdev.recipefinder.view.OnKeyboardStateChangedListener; 18 | import com.mlsdev.recipefinder.view.fragment.BaseFragment; 19 | import com.mlsdev.recipefinder.view.listener.OnIngredientAnalyzedListener; 20 | import com.mlsdev.recipefinder.view.utils.DiagramUtils; 21 | 22 | import javax.inject.Inject; 23 | 24 | public class IngredientAnalysisFragment extends BaseFragment implements OnIngredientAnalyzedListener, 25 | OnKeyboardStateChangedListener, Injectable { 26 | private FragmentIngredientAnalysisBinding binding; 27 | private IngredientAnalysisViewModel viewModel; 28 | 29 | @Inject 30 | ViewModelProvider.Factory viewModelFactory; 31 | 32 | @Inject 33 | DiagramUtils diagramUtils; 34 | 35 | @Override 36 | public void onCreate(@Nullable Bundle savedInstanceState) { 37 | super.onCreate(savedInstanceState); 38 | } 39 | 40 | @Override 41 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 42 | binding = DataBindingUtil.inflate(inflater, R.layout.fragment_ingredient_analysis, container, false); 43 | 44 | if (viewModel == null) 45 | viewModel = ViewModelProviders.of(this, viewModelFactory).get(IngredientAnalysisViewModel.class); 46 | 47 | getLifecycle().addObserver(viewModel); 48 | viewModel.setKeyboardListener(this); 49 | viewModel.setOnIngredientAnalyzedListener(this); 50 | viewModel.setActionListener(this); 51 | 52 | binding.setViewModel(viewModel); 53 | diagramUtils.preparePieChart(binding.pieChart); 54 | return binding.getRoot(); 55 | } 56 | 57 | @Override 58 | public void onStop() { 59 | super.onStop(); 60 | binding.etIngredientInput.clearFocus(); 61 | } 62 | 63 | @Override 64 | public void onIngredientAnalyzed(PieData diagramData) { 65 | diagramUtils.setData(binding.pieChart, diagramData); 66 | } 67 | 68 | @Override 69 | public void showKeyboard() { 70 | ((MainActivity) getActivity()).showSoftKeyboard(); 71 | } 72 | 73 | @Override 74 | public void hideKeyboard() { 75 | ((MainActivity) getActivity()).hideSoftKeyboard(); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /app/src/main/java/com/mlsdev/recipefinder/view/analysenutrition/recipe/AddIngredientDialogFragment.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder.view.analysenutrition.recipe; 2 | 3 | import android.app.Activity; 4 | import android.app.Dialog; 5 | import android.content.DialogInterface; 6 | import android.content.Intent; 7 | import android.databinding.DataBindingUtil; 8 | import android.os.Bundle; 9 | import android.support.v4.app.DialogFragment; 10 | import android.support.v7.app.AlertDialog; 11 | import android.view.LayoutInflater; 12 | 13 | import com.mlsdev.recipefinder.R; 14 | import com.mlsdev.recipefinder.databinding.DialogFragmentAddIngredientBinding; 15 | import com.mlsdev.recipefinder.view.BaseActivity; 16 | 17 | public class AddIngredientDialogFragment extends DialogFragment { 18 | public static final String INGREDIENT_TITLE_KEY = "ingredient_label_key"; 19 | private DialogFragmentAddIngredientBinding binding; 20 | 21 | @Override 22 | public Dialog onCreateDialog(Bundle savedInstanceState) { 23 | binding = DataBindingUtil 24 | .inflate(LayoutInflater.from(getActivity()), R.layout.dialog_fragment_add_ingredient, null, false); 25 | 26 | ((BaseActivity) getActivity()).showSoftKeyboard(); 27 | 28 | AlertDialog.Builder builder = new AlertDialog.Builder(getActivity(), R.style.AlertDialogAppCompat); 29 | builder.setTitle("Add Ingredient") 30 | .setView(binding.getRoot()) 31 | .setNegativeButton(android.R.string.cancel, null) 32 | .setPositiveButton(R.string.btn_add, new DialogInterface.OnClickListener() { 33 | @Override 34 | public void onClick(DialogInterface dialogInterface, int i) { 35 | Intent data = new Intent(); 36 | data.putExtra(INGREDIENT_TITLE_KEY, binding.etIngredientInput.getText().toString()); 37 | 38 | if (getTargetFragment() != null) { 39 | getTargetFragment().onActivityResult( 40 | RecipeAnalysisViewModel.ADD_INGREDIENT_REQUEST_CODE, 41 | Activity.RESULT_OK, 42 | data); 43 | } 44 | 45 | dismiss(); 46 | } 47 | }); 48 | 49 | return builder.create(); 50 | } 51 | 52 | @Override 53 | public void onDismiss(DialogInterface dialog) { 54 | if (getActivity() != null) 55 | ((BaseActivity) getActivity()).hideSoftKeyboard(); 56 | super.onDismiss(dialog); 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /app/src/main/java/com/mlsdev/recipefinder/view/analysenutrition/recipe/OnAddIngredientClickListener.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder.view.analysenutrition.recipe; 2 | 3 | public interface OnAddIngredientClickListener { 4 | void onAddIngredientButtonClick(); 5 | } 6 | -------------------------------------------------------------------------------- /app/src/main/java/com/mlsdev/recipefinder/view/analysenutrition/recipe/RecipeAnalysisDetailsActivity.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder.view.analysenutrition.recipe; 2 | 3 | import android.arch.lifecycle.LifecycleRegistry; 4 | import android.arch.lifecycle.LifecycleRegistryOwner; 5 | import android.arch.lifecycle.ViewModelProvider; 6 | import android.arch.lifecycle.ViewModelProviders; 7 | import android.databinding.DataBindingUtil; 8 | import android.os.Bundle; 9 | import android.support.v4.app.Fragment; 10 | import android.view.MenuItem; 11 | 12 | import com.github.mikephil.charting.data.PieData; 13 | import com.mlsdev.recipefinder.R; 14 | import com.mlsdev.recipefinder.databinding.ActivityRecipeAnalysisDetailsBinding; 15 | import com.mlsdev.recipefinder.view.BaseActivity; 16 | import com.mlsdev.recipefinder.view.listener.OnDataLoadedListener; 17 | import com.mlsdev.recipefinder.view.utils.DiagramUtils; 18 | 19 | import javax.inject.Inject; 20 | 21 | import dagger.android.DispatchingAndroidInjector; 22 | import dagger.android.support.HasSupportFragmentInjector; 23 | 24 | public class RecipeAnalysisDetailsActivity extends BaseActivity implements HasSupportFragmentInjector, LifecycleRegistryOwner, OnDataLoadedListener { 25 | public static final String RECIPE_ANALYSING_RESULT_KEY = "recipe_analysing_result"; 26 | private ActivityRecipeAnalysisDetailsBinding binding; 27 | private LifecycleRegistry lifecycleRegistry = new LifecycleRegistry(this); 28 | private RecipeAnalysisDetailsViewModel viewModel; 29 | 30 | @Inject 31 | ViewModelProvider.Factory viewModelFactory; 32 | 33 | @Inject 34 | DiagramUtils diagramUtils; 35 | 36 | @Inject 37 | DispatchingAndroidInjector supportFragmentInjector; 38 | 39 | @Override 40 | public void onCreate(Bundle savedInstanceState) { 41 | super.onCreate(savedInstanceState); 42 | binding = DataBindingUtil.setContentView(this, R.layout.activity_recipe_analysis_details); 43 | 44 | if (viewModel == null) { 45 | viewModel = ViewModelProviders.of(this, viewModelFactory).get(RecipeAnalysisDetailsViewModel.class); 46 | viewModel.setData(getIntent().getExtras()); 47 | } 48 | 49 | getLifecycle().addObserver(viewModel); 50 | viewModel.setOnDataLoadedListener(this); 51 | binding.setViewModel(viewModel); 52 | diagramUtils.preparePieChart(binding.pieChart); 53 | 54 | setSupportActionBar(binding.toolbar); 55 | 56 | if (getSupportActionBar() != null) 57 | getSupportActionBar().setDisplayHomeAsUpEnabled(true); 58 | } 59 | 60 | @Override 61 | public boolean onOptionsItemSelected(MenuItem item) { 62 | if (item.getItemId() == android.R.id.home) 63 | onBackPressed(); 64 | return super.onOptionsItemSelected(item); 65 | } 66 | 67 | @Override 68 | public DispatchingAndroidInjector supportFragmentInjector() { 69 | return supportFragmentInjector; 70 | } 71 | 72 | @Override 73 | public LifecycleRegistry getLifecycle() { 74 | return lifecycleRegistry; 75 | } 76 | 77 | @Override 78 | public void onDataLoaded(PieData pieData) { 79 | diagramUtils.setData(binding.pieChart, pieData); 80 | } 81 | 82 | @Override 83 | public void onMoreDataLoaded(PieData moreRecipes) { 84 | } 85 | 86 | } 87 | -------------------------------------------------------------------------------- /app/src/main/java/com/mlsdev/recipefinder/view/analysenutrition/recipe/RecipeAnalysisDetailsViewModel.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder.view.analysenutrition.recipe; 2 | 3 | import android.arch.lifecycle.Lifecycle; 4 | import android.arch.lifecycle.LifecycleObserver; 5 | import android.arch.lifecycle.OnLifecycleEvent; 6 | import android.databinding.ObservableField; 7 | import android.databinding.ObservableInt; 8 | import android.os.Bundle; 9 | import android.util.Log; 10 | import android.view.View; 11 | 12 | import com.github.mikephil.charting.data.PieData; 13 | import com.github.mikephil.charting.data.PieDataSet; 14 | import com.github.mikephil.charting.data.PieEntry; 15 | import com.mlsdev.recipefinder.R; 16 | import com.mlsdev.recipefinder.data.entity.nutrition.NutritionAnalysisResult; 17 | import com.mlsdev.recipefinder.view.listener.OnDataLoadedListener; 18 | import com.mlsdev.recipefinder.view.utils.DiagramUtils; 19 | import com.mlsdev.recipefinder.view.viewmodel.BaseViewModel; 20 | 21 | import java.util.List; 22 | 23 | import javax.inject.Inject; 24 | 25 | public class RecipeAnalysisDetailsViewModel extends BaseViewModel implements LifecycleObserver { 26 | 27 | public final ObservableField calories = new ObservableField<>(); 28 | public final ObservableField yield = new ObservableField<>(); 29 | public final ObservableInt chartVisibility = new ObservableInt(View.GONE); 30 | private DiagramUtils diagramUtils; 31 | private PieData pieData; 32 | private OnDataLoadedListener onDataLoadedListener; 33 | 34 | @Inject 35 | public RecipeAnalysisDetailsViewModel(DiagramUtils diagramUtils) { 36 | this.diagramUtils = diagramUtils; 37 | } 38 | 39 | public void setOnDataLoadedListener(OnDataLoadedListener onDataLoadedListener) { 40 | this.onDataLoadedListener = onDataLoadedListener; 41 | } 42 | 43 | @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) 44 | void start() { 45 | if (onDataLoadedListener != null) 46 | onDataLoadedListener.onDataLoaded(pieData); 47 | } 48 | 49 | @OnLifecycleEvent(Lifecycle.Event.ON_STOP) 50 | void stop() { 51 | Log.d("RF", "lifecycle stop"); 52 | } 53 | 54 | public void setData(Bundle recipeAnalysingData) { 55 | NutritionAnalysisResult nutritionAnalysisResult = recipeAnalysingData 56 | .getParcelable(RecipeAnalysisDetailsActivity.RECIPE_ANALYSING_RESULT_KEY); 57 | 58 | showResults(nutritionAnalysisResult); 59 | } 60 | 61 | private void showResults(NutritionAnalysisResult nutritionAnalysisResult) { 62 | if (nutritionAnalysisResult == null) 63 | return; 64 | 65 | calories.set(context.getString(R.string.calories, nutritionAnalysisResult.getCalories())); 66 | yield.set(context.getString(R.string.yields, String.valueOf(nutritionAnalysisResult.getYield()))); 67 | 68 | List pieEntries = diagramUtils.preparePieEntries(nutritionAnalysisResult.getTotalNutrients()); 69 | chartVisibility.set(pieEntries.isEmpty() ? View.GONE : View.VISIBLE); 70 | 71 | if (pieEntries.isEmpty()) 72 | return; 73 | 74 | PieDataSet pieDataSet = diagramUtils.createPieDataSet(pieEntries, "Nutrients", null); 75 | pieData = diagramUtils.createPieData(pieDataSet); 76 | 77 | if (onDataLoadedListener != null) 78 | onDataLoadedListener.onDataLoaded(pieData); 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /app/src/main/java/com/mlsdev/recipefinder/view/analysenutrition/recipe/RecipeAnalysisFragment.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder.view.analysenutrition.recipe; 2 | 3 | import android.app.Activity; 4 | import android.arch.lifecycle.ViewModelProvider; 5 | import android.arch.lifecycle.ViewModelProviders; 6 | import android.content.Intent; 7 | import android.databinding.DataBindingUtil; 8 | import android.os.Bundle; 9 | import android.support.annotation.Nullable; 10 | import android.support.v7.widget.LinearLayoutManager; 11 | import android.view.LayoutInflater; 12 | import android.view.View; 13 | import android.view.ViewGroup; 14 | 15 | import com.mlsdev.recipefinder.R; 16 | import com.mlsdev.recipefinder.databinding.FragmentRecipeAnalysisBinding; 17 | import com.mlsdev.recipefinder.di.Injectable; 18 | import com.mlsdev.recipefinder.view.fragment.BaseFragment; 19 | import com.mlsdev.recipefinder.view.listener.OnDataLoadedListener; 20 | 21 | import java.util.List; 22 | 23 | import javax.inject.Inject; 24 | 25 | public class RecipeAnalysisFragment extends BaseFragment implements OnAddIngredientClickListener, 26 | OnDataLoadedListener>, Injectable { 27 | private FragmentRecipeAnalysisBinding binding; 28 | private RecipeAnalysisViewModel viewModel; 29 | private IngredientsAdapter adapter; 30 | 31 | @Inject 32 | ViewModelProvider.Factory viewModelFactory; 33 | 34 | @Override 35 | public void onCreate(@Nullable Bundle savedInstanceState) { 36 | super.onCreate(savedInstanceState); 37 | } 38 | 39 | @Override 40 | public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { 41 | binding = DataBindingUtil.inflate(inflater, R.layout.fragment_recipe_analysis, container, false); 42 | 43 | if (viewModel == null) 44 | viewModel = ViewModelProviders.of(this, viewModelFactory).get(RecipeAnalysisViewModel.class); 45 | 46 | viewModel.setAddIngredientListener(this); 47 | viewModel.setDataLoadedListener(this); 48 | viewModel.setActionListener(this); 49 | 50 | getLifecycle().addObserver(viewModel); 51 | 52 | binding.setViewModel(viewModel); 53 | initRecyclerView(); 54 | return binding.getRoot(); 55 | } 56 | 57 | private void initRecyclerView() { 58 | adapter = new IngredientsAdapter(this); 59 | binding.rvIngredients.setHasFixedSize(true); 60 | binding.rvIngredients.setLayoutManager(new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false)); 61 | binding.rvIngredients.setAdapter(adapter); 62 | } 63 | 64 | @Override 65 | public void onActivityResult(int requestCode, int resultCode, Intent data) { 66 | 67 | if (requestCode == RecipeAnalysisViewModel.ADD_INGREDIENT_REQUEST_CODE 68 | && resultCode == Activity.RESULT_OK && data != null) { 69 | adapter.addItem(data); 70 | binding.rvIngredients.smoothScrollToPosition(adapter.getItemCount()); 71 | viewModel.setIngredients(adapter.getIngredientList()); 72 | } 73 | 74 | } 75 | 76 | @Override 77 | public void onAddIngredientButtonClick() { 78 | AddIngredientDialogFragment dialogFragment = new AddIngredientDialogFragment(); 79 | dialogFragment.setTargetFragment(this, RecipeAnalysisViewModel.ADD_INGREDIENT_REQUEST_CODE); 80 | dialogFragment.show(getFragmentManager(), "add_ingredient"); 81 | } 82 | 83 | @Override 84 | public void onDataLoaded(List ingredients) { 85 | adapter.setData(ingredients); 86 | } 87 | 88 | @Override 89 | public void onMoreDataLoaded(List moreData) { 90 | 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /app/src/main/java/com/mlsdev/recipefinder/view/analysenutrition/recipe/RecipeAnalysisViewModel.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder.view.analysenutrition.recipe; 2 | 3 | import android.arch.lifecycle.Lifecycle; 4 | import android.arch.lifecycle.LifecycleObserver; 5 | import android.arch.lifecycle.OnLifecycleEvent; 6 | import android.content.Intent; 7 | import android.databinding.ObservableField; 8 | import android.util.Log; 9 | import android.view.View; 10 | 11 | import com.mlsdev.recipefinder.R; 12 | import com.mlsdev.recipefinder.data.entity.nutrition.NutritionAnalysisResult; 13 | import com.mlsdev.recipefinder.data.entity.nutrition.RecipeAnalysisParams; 14 | import com.mlsdev.recipefinder.data.source.BaseObserver; 15 | import com.mlsdev.recipefinder.data.source.repository.DataRepository; 16 | import com.mlsdev.recipefinder.view.MainActivity; 17 | import com.mlsdev.recipefinder.view.listener.OnDataLoadedListener; 18 | import com.mlsdev.recipefinder.view.viewmodel.BaseViewModel; 19 | 20 | import java.util.ArrayList; 21 | import java.util.List; 22 | 23 | import javax.inject.Inject; 24 | 25 | import io.reactivex.android.schedulers.AndroidSchedulers; 26 | import io.reactivex.disposables.Disposable; 27 | import io.reactivex.schedulers.Schedulers; 28 | 29 | public class RecipeAnalysisViewModel extends BaseViewModel implements LifecycleObserver { 30 | public static final int ADD_INGREDIENT_REQUEST_CODE = 0; 31 | public final ObservableField title = new ObservableField<>(); 32 | public final ObservableField preparation = new ObservableField<>(); 33 | public final ObservableField yield = new ObservableField<>(); 34 | private List ingredients = new ArrayList<>(); 35 | private OnAddIngredientClickListener addIngredientListener; 36 | private OnDataLoadedListener> dataLoadedListener; 37 | 38 | @Inject 39 | public RecipeAnalysisViewModel(DataRepository repository) { 40 | this.repository = repository; 41 | } 42 | 43 | public void setDataLoadedListener(OnDataLoadedListener> dataLoadedListener) { 44 | this.dataLoadedListener = dataLoadedListener; 45 | } 46 | 47 | public void setAddIngredientListener(OnAddIngredientClickListener addIngredientListener) { 48 | this.addIngredientListener = addIngredientListener; 49 | } 50 | 51 | @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) 52 | void start() { 53 | dataLoadedListener.onDataLoaded(ingredients); 54 | } 55 | 56 | @OnLifecycleEvent(Lifecycle.Event.ON_STOP) 57 | void stop() { 58 | Log.d("RF", "lifecycle stop"); 59 | } 60 | 61 | public void onAnalyzeButtonClick(View view) { 62 | if (ingredients.isEmpty()) { 63 | View.OnClickListener listener = new View.OnClickListener() { 64 | @Override 65 | public void onClick(View v) { 66 | addIngredientListener.onAddIngredientButtonClick(); 67 | } 68 | }; 69 | 70 | actionListener.showSnackbar(R.string.no_ingredients_error_message, R.string.btn_add, listener); 71 | return; 72 | } 73 | 74 | 75 | actionListener.showProgressDialog(true, "Analysing..."); 76 | 77 | RecipeAnalysisParams recipeAnalysisParams = new RecipeAnalysisParams(); 78 | recipeAnalysisParams.setTitle(title.get()); 79 | recipeAnalysisParams.setYield(yield.get()); 80 | recipeAnalysisParams.setPrep(preparation.get()); 81 | recipeAnalysisParams.setIngr(ingredients); 82 | 83 | subscriptions.clear(); 84 | 85 | repository.getRecipeAnalysisData(recipeAnalysisParams) 86 | .observeOn(AndroidSchedulers.mainThread()) 87 | .subscribeOn(Schedulers.io()) 88 | .subscribe(new BaseObserver() { 89 | @Override 90 | public void onSuccess(@io.reactivex.annotations.NonNull NutritionAnalysisResult nutritionAnalysisResult) { 91 | actionListener.showProgressDialog(false, null); 92 | Log.d(MainActivity.LOG_TAG, "onNext()"); 93 | Intent intent = new Intent(context, RecipeAnalysisDetailsActivity.class); 94 | intent.putExtra(RecipeAnalysisDetailsActivity.RECIPE_ANALYSING_RESULT_KEY, nutritionAnalysisResult); 95 | context.startActivity(intent); 96 | } 97 | 98 | @Override 99 | public void onSubscribe(@io.reactivex.annotations.NonNull Disposable d) { 100 | subscriptions.add(d); 101 | } 102 | 103 | @Override 104 | public void onError(Throwable e) { 105 | super.onError(e); 106 | showError(e); 107 | } 108 | }); 109 | 110 | } 111 | 112 | public void setIngredients(List ingredients) { 113 | this.ingredients = ingredients; 114 | } 115 | 116 | } 117 | -------------------------------------------------------------------------------- /app/src/main/java/com/mlsdev/recipefinder/view/bindingutils/DataBinder.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder.view.bindingutils; 2 | 3 | import android.animation.ObjectAnimator; 4 | import android.databinding.BindingAdapter; 5 | import android.support.design.widget.TextInputEditText; 6 | import android.support.v4.widget.ContentLoadingProgressBar; 7 | import android.widget.ImageView; 8 | 9 | import com.bumptech.glide.Glide; 10 | import com.mlsdev.recipefinder.R; 11 | 12 | public final class DataBinder { 13 | 14 | public DataBinder() { 15 | } 16 | 17 | @BindingAdapter("imageUrl") 18 | public static void setImageUrl(ImageView imageView, String imageUrl) { 19 | if (imageUrl.isEmpty()) 20 | return; 21 | 22 | Glide.with(imageView.getContext()) 23 | .load(imageUrl) 24 | .override(600, 400) 25 | .error(R.mipmap.ic_launcher) 26 | .into(imageView); 27 | } 28 | 29 | @BindingAdapter("progressValue") 30 | public static void setProgressValue(ContentLoadingProgressBar progressBar, int progressValue) { 31 | ObjectAnimator objectAnimator = ObjectAnimator.ofInt(progressBar, "progress", 0, progressValue); 32 | objectAnimator.setDuration(350); 33 | objectAnimator.setStartDelay(250); 34 | objectAnimator.start(); 35 | } 36 | 37 | @BindingAdapter("focused") 38 | public static void setFocused(TextInputEditText editText, boolean requestFocus) { 39 | if (requestFocus) 40 | editText.requestFocus(); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /app/src/main/java/com/mlsdev/recipefinder/view/enums/TabItemType.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder.view.enums; 2 | 3 | public enum TabItemType { 4 | ANALYSE, SEARCH, FAVORITES 5 | } 6 | -------------------------------------------------------------------------------- /app/src/main/java/com/mlsdev/recipefinder/view/favoriterecipes/FavoriteRecipesFragment.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder.view.favoriterecipes; 2 | 3 | import android.arch.lifecycle.ViewModelProvider; 4 | import android.arch.lifecycle.ViewModelProviders; 5 | import android.databinding.DataBindingUtil; 6 | import android.os.Bundle; 7 | import android.support.annotation.Nullable; 8 | import android.view.LayoutInflater; 9 | import android.view.View; 10 | import android.view.ViewGroup; 11 | 12 | import com.mlsdev.recipefinder.R; 13 | import com.mlsdev.recipefinder.databinding.FragmentFavoriteRecipesBinding; 14 | import com.mlsdev.recipefinder.di.Injectable; 15 | import com.mlsdev.recipefinder.view.fragment.RecipeListFragment; 16 | 17 | import javax.inject.Inject; 18 | 19 | import static com.mlsdev.recipefinder.view.searchrecipes.RecipeListAdapter.OnLastItemShownListener; 20 | 21 | public class FavoriteRecipesFragment extends RecipeListFragment implements OnLastItemShownListener, 22 | Injectable { 23 | private FragmentFavoriteRecipesBinding binding; 24 | private FavoritesViewModel viewModel; 25 | 26 | @Inject 27 | ViewModelProvider.Factory viewModelFactory; 28 | 29 | @Override 30 | public void onCreate(@Nullable Bundle savedInstanceState) { 31 | super.onCreate(savedInstanceState); 32 | } 33 | 34 | @Override 35 | public View onCreateView(LayoutInflater inflater, 36 | @Nullable ViewGroup container, 37 | @Nullable Bundle savedInstanceState) { 38 | 39 | binding = DataBindingUtil.inflate(inflater, R.layout.fragment_favorite_recipes, container, false); 40 | initRecyclerView(binding.rvRecipeList); 41 | 42 | if (viewModel == null) 43 | viewModel = ViewModelProviders.of(this, viewModelFactory).get(FavoritesViewModel.class); 44 | 45 | getLifecycle().addObserver(viewModel); 46 | viewModel.setOnDataLoadedListener(this); 47 | binding.setViewModel(viewModel); 48 | 49 | return binding.getRoot(); 50 | } 51 | 52 | @Override 53 | public void scrollToTop() { 54 | binding.rvRecipeList.smoothScrollToPosition(0); 55 | } 56 | 57 | @Override 58 | public void onDestroy() { 59 | super.onDestroy(); 60 | viewModel.onDestroy(); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /app/src/main/java/com/mlsdev/recipefinder/view/favoriterecipes/FavoritesViewModel.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder.view.favoriterecipes; 2 | 3 | import android.arch.lifecycle.Lifecycle; 4 | import android.arch.lifecycle.LifecycleObserver; 5 | import android.arch.lifecycle.OnLifecycleEvent; 6 | import android.databinding.ObservableInt; 7 | import android.view.View; 8 | 9 | import com.mlsdev.recipefinder.data.entity.recipe.Recipe; 10 | import com.mlsdev.recipefinder.data.source.repository.DataRepository; 11 | import com.mlsdev.recipefinder.view.listener.OnDataLoadedListener; 12 | import com.mlsdev.recipefinder.view.viewmodel.BaseViewModel; 13 | 14 | import java.util.List; 15 | 16 | import javax.inject.Inject; 17 | 18 | import io.reactivex.android.schedulers.AndroidSchedulers; 19 | import io.reactivex.annotations.NonNull; 20 | import io.reactivex.disposables.Disposable; 21 | import io.reactivex.functions.Consumer; 22 | import io.reactivex.schedulers.Schedulers; 23 | 24 | public class FavoritesViewModel extends BaseViewModel implements LifecycleObserver { 25 | private OnDataLoadedListener> onDataLoadedListener; 26 | public final ObservableInt emptyViewVisibility; 27 | 28 | @Inject 29 | public FavoritesViewModel(DataRepository repository) { 30 | this.repository = repository; 31 | emptyViewVisibility = new ObservableInt(View.VISIBLE); 32 | } 33 | 34 | public void setOnDataLoadedListener(OnDataLoadedListener> onDataLoadedListener) { 35 | this.onDataLoadedListener = onDataLoadedListener; 36 | } 37 | 38 | @Override 39 | public void onStop() { 40 | super.onStop(); 41 | subscriptions.clear(); 42 | } 43 | 44 | @OnLifecycleEvent(Lifecycle.Event.ON_START) 45 | public void start() { 46 | getFavoriteRecipes(); 47 | } 48 | 49 | @OnLifecycleEvent(Lifecycle.Event.ON_STOP) 50 | public void stop() { 51 | } 52 | 53 | public void getFavoriteRecipes() { 54 | Disposable disposable = repository.getFavoriteRecipes() 55 | .observeOn(AndroidSchedulers.mainThread()) 56 | .subscribeOn(Schedulers.io()) 57 | .subscribe(new Consumer>() { 58 | @Override 59 | public void accept(@NonNull List recipes) throws Exception { 60 | emptyViewVisibility.set(recipes.isEmpty() ? View.VISIBLE : View.INVISIBLE); 61 | onDataLoadedListener.onDataLoaded(recipes); 62 | } 63 | }); 64 | 65 | subscriptions.add(disposable); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /app/src/main/java/com/mlsdev/recipefinder/view/fragment/BaseFragment.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder.view.fragment; 2 | 3 | import android.arch.lifecycle.LifecycleFragment; 4 | import android.support.annotation.StringRes; 5 | import android.view.View; 6 | 7 | import com.mlsdev.recipefinder.view.ActionListener; 8 | import com.mlsdev.recipefinder.view.BaseActivity; 9 | 10 | public class BaseFragment extends LifecycleFragment implements ActionListener { 11 | 12 | @Override 13 | public void onStartFilter() { 14 | 15 | } 16 | 17 | @Override 18 | public void showProgressDialog(boolean show, String message) { 19 | ((BaseActivity) getActivity()).showProgressDialog(show, message); 20 | } 21 | 22 | @Override 23 | public void showSnackbar(@StringRes int message) { 24 | ((BaseActivity) getActivity()).showSnackbar(message); 25 | } 26 | 27 | @Override 28 | public void showSnackbar(String message) { 29 | ((BaseActivity) getActivity()).showSnackbar(message); 30 | } 31 | 32 | @Override 33 | public void showSnackbar(String message, String action, View.OnClickListener listener) { 34 | ((BaseActivity) getActivity()).showSnackbar(message, action, listener); 35 | } 36 | 37 | @Override 38 | public void showSnackbar(@StringRes int message, @StringRes int action, View.OnClickListener listener) { 39 | ((BaseActivity) getActivity()).showSnackbar(message, action, listener); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /app/src/main/java/com/mlsdev/recipefinder/view/fragment/RecipeListFragment.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder.view.fragment; 2 | 3 | import android.graphics.Bitmap; 4 | import android.graphics.drawable.BitmapDrawable; 5 | import android.os.Build; 6 | import android.os.Bundle; 7 | import android.support.v4.app.Fragment; 8 | import android.support.v4.view.ViewCompat; 9 | import android.support.v4.widget.SwipeRefreshLayout; 10 | import android.support.v7.widget.GridLayoutManager; 11 | import android.support.v7.widget.RecyclerView; 12 | import android.transition.Fade; 13 | import android.transition.TransitionInflater; 14 | 15 | import com.bumptech.glide.load.resource.bitmap.GlideBitmapDrawable; 16 | import com.mlsdev.recipefinder.R; 17 | import com.mlsdev.recipefinder.data.entity.recipe.Recipe; 18 | import com.mlsdev.recipefinder.databinding.RecipeListItemBinding; 19 | import com.mlsdev.recipefinder.view.Extras; 20 | import com.mlsdev.recipefinder.view.fragments.TabFragment; 21 | import com.mlsdev.recipefinder.view.listener.OnDataLoadedListener; 22 | import com.mlsdev.recipefinder.view.recipedetails.RecipeDetailsFragment; 23 | import com.mlsdev.recipefinder.view.searchrecipes.RecipeListAdapter; 24 | import com.mlsdev.recipefinder.view.utils.UtilsUI; 25 | 26 | import java.util.List; 27 | 28 | import javax.inject.Inject; 29 | 30 | public class RecipeListFragment extends TabFragment implements RecipeListAdapter.OnItemClickListener, 31 | RecipeListAdapter.OnLastItemShownListener, OnDataLoadedListener> { 32 | protected RecipeListAdapter recipeListAdapter; 33 | protected RecyclerView recipeRecyclerView; 34 | protected SwipeRefreshLayout swipeRefreshLayout; 35 | 36 | @Inject 37 | UtilsUI utilsUI; 38 | 39 | @Override 40 | public void onItemClicked(Recipe recipe, RecipeListItemBinding itemBinding) { 41 | Bundle recipeData = new Bundle(); 42 | Fragment fragment = new RecipeDetailsFragment(); 43 | 44 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 45 | setSharedElementReturnTransition(TransitionInflater.from(getActivity()) 46 | .inflateTransition(R.transition.change_image_transform)); 47 | setExitTransition(new Fade()); 48 | 49 | fragment.setSharedElementEnterTransition(TransitionInflater.from(getActivity()) 50 | .inflateTransition(R.transition.change_image_transform)); 51 | fragment.setEnterTransition(new Fade()); 52 | } 53 | 54 | recipeData.putSerializable(Extras.DATA, recipe); 55 | 56 | if (itemBinding.ivRecipeImage.getDrawable() != null) { 57 | Bitmap bitmapImage = itemBinding.ivRecipeImage.getDrawable() instanceof GlideBitmapDrawable ? 58 | ((GlideBitmapDrawable) itemBinding.ivRecipeImage.getDrawable()).getBitmap() : 59 | ((BitmapDrawable) itemBinding.ivRecipeImage.getDrawable()).getBitmap(); 60 | recipeData.putParcelable(Extras.IMAGE_DATA, bitmapImage); 61 | } 62 | 63 | recipeData.putString(Extras.IMAGE_TRANSITION_NAME, ViewCompat.getTransitionName(itemBinding.ivRecipeImage)); 64 | 65 | fragment.setArguments(recipeData); 66 | 67 | getFragmentManager() 68 | .beginTransaction() 69 | .addToBackStack("RecipeDetails") 70 | .replace(R.id.fl_content, fragment) 71 | .addSharedElement(itemBinding.ivRecipeImage, ViewCompat.getTransitionName(itemBinding.ivRecipeImage)) 72 | .commit(); 73 | } 74 | 75 | @Override 76 | public void onLastItemShown() { 77 | 78 | } 79 | 80 | protected void initRecyclerView(RecyclerView recyclerView) { 81 | int columns = getActivity().getResources().getConfiguration().orientation; 82 | 83 | if (recipeListAdapter == null) 84 | recipeListAdapter = new RecipeListAdapter(this, this); 85 | 86 | recipeRecyclerView = recyclerView; 87 | recipeRecyclerView.setLayoutManager(new GridLayoutManager(getActivity(), columns, GridLayoutManager.VERTICAL, false)); 88 | recipeRecyclerView.setHasFixedSize(true); 89 | recipeRecyclerView.setAdapter(recipeListAdapter); 90 | } 91 | 92 | protected void initSwipeRefreshLayout(SwipeRefreshLayout refreshLayout, SwipeRefreshLayout.OnRefreshListener listener) { 93 | swipeRefreshLayout = refreshLayout; 94 | swipeRefreshLayout.setOnRefreshListener(listener); 95 | swipeRefreshLayout.setColorSchemeColors( 96 | utilsUI.getColor(R.color.colorPrimaryDark), 97 | utilsUI.getColor(R.color.colorPrimary), 98 | utilsUI.getColor(R.color.colorAccent) 99 | ); 100 | } 101 | 102 | @Override 103 | public void onDataLoaded(List recipes) { 104 | recipeListAdapter.setData(recipes); 105 | stopRefreshing(); 106 | } 107 | 108 | @Override 109 | public void onMoreDataLoaded(List moreRecipes) { 110 | recipeListAdapter.setMoreData(moreRecipes); 111 | stopRefreshing(); 112 | } 113 | 114 | protected void stopRefreshing() { 115 | if (swipeRefreshLayout != null) 116 | swipeRefreshLayout.setRefreshing(false); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /app/src/main/java/com/mlsdev/recipefinder/view/fragments/TabFragment.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder.view.fragments; 2 | 3 | import android.support.annotation.Nullable; 4 | 5 | import com.mlsdev.recipefinder.view.analysenutrition.AnalyseNutritionFragment; 6 | import com.mlsdev.recipefinder.view.enums.TabItemType; 7 | import com.mlsdev.recipefinder.view.favoriterecipes.FavoriteRecipesFragment; 8 | import com.mlsdev.recipefinder.view.fragment.BaseFragment; 9 | import com.mlsdev.recipefinder.view.searchrecipes.SearchRecipeFragment; 10 | 11 | public abstract class TabFragment extends BaseFragment { 12 | 13 | @Nullable 14 | public static TabFragment getNewInstance(TabItemType tabItemType) { 15 | 16 | switch (tabItemType) { 17 | case ANALYSE: 18 | return new AnalyseNutritionFragment(); 19 | case SEARCH: 20 | return new SearchRecipeFragment(); 21 | case FAVORITES: 22 | return new FavoriteRecipesFragment(); 23 | default: 24 | return null; 25 | } 26 | 27 | } 28 | 29 | /** 30 | * Scrolls the root view of the fragment to top by clicking on a current tab in the navigation view. 31 | */ 32 | public void scrollToTop() { 33 | 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /app/src/main/java/com/mlsdev/recipefinder/view/listener/OnDataLoadedListener.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder.view.listener; 2 | 3 | public interface OnDataLoadedListener { 4 | void onDataLoaded(T recipes); 5 | 6 | void onMoreDataLoaded(T moreRecipes); 7 | } 8 | -------------------------------------------------------------------------------- /app/src/main/java/com/mlsdev/recipefinder/view/listener/OnIngredientAnalyzedListener.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder.view.listener; 2 | 3 | import com.github.mikephil.charting.data.PieData; 4 | 5 | public interface OnIngredientAnalyzedListener { 6 | void onIngredientAnalyzed(PieData diagramData); 7 | } 8 | -------------------------------------------------------------------------------- /app/src/main/java/com/mlsdev/recipefinder/view/message/Message.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder.view.message; 2 | 3 | import com.mlsdev.recipefinder.view.BaseActivity; 4 | 5 | public class Message { 6 | 7 | protected BaseActivity activity; 8 | 9 | public Message(BaseActivity activity) { 10 | this.activity = activity; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /app/src/main/java/com/mlsdev/recipefinder/view/message/ProgressDialogMessage.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder.view.message; 2 | 3 | import android.app.ProgressDialog; 4 | import android.support.annotation.Nullable; 5 | 6 | import com.mlsdev.recipefinder.R; 7 | import com.mlsdev.recipefinder.view.BaseActivity; 8 | 9 | public class ProgressDialogMessage extends Message { 10 | private ProgressDialog progressDialog; 11 | 12 | public ProgressDialogMessage(BaseActivity activity) { 13 | super(activity); 14 | } 15 | 16 | public void showProgressDialog(boolean isShow, @Nullable String message) { 17 | if (progressDialog == null) { 18 | progressDialog = new ProgressDialog(activity, R.style.AlertDialogAppCompat); 19 | progressDialog.setIndeterminate(true); 20 | progressDialog.setMessage(message); 21 | } 22 | 23 | if (isShow) 24 | progressDialog.show(); 25 | else 26 | progressDialog.dismiss(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /app/src/main/java/com/mlsdev/recipefinder/view/message/SnackbarMessage.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder.view.message; 2 | 3 | import android.support.annotation.StringRes; 4 | import android.support.design.widget.Snackbar; 5 | import android.view.View; 6 | 7 | import com.mlsdev.recipefinder.R; 8 | import com.mlsdev.recipefinder.view.BaseActivity; 9 | 10 | public class SnackbarMessage extends Message { 11 | 12 | public SnackbarMessage(BaseActivity activity) { 13 | super(activity); 14 | } 15 | 16 | public void showSnackbar(@StringRes int message) { 17 | if (activity != null) 18 | showSnackbar(activity.getString(message)); 19 | } 20 | 21 | public void showSnackbar(String message) { 22 | showSnackbar(message, null, null); 23 | } 24 | 25 | public void showSnackbar(@StringRes int message, @StringRes int action, View.OnClickListener listener) { 26 | if (activity != null) 27 | showSnackbar(activity.getString(message), activity.getString(action), listener); 28 | } 29 | 30 | public void showSnackbar(String message, String action, View.OnClickListener listener) { 31 | if (activity != null) { 32 | String tag = activity.getString(R.string.content_tag); 33 | View view = activity.getWindow().getDecorView().findViewWithTag(tag); 34 | if (view != null) { 35 | Snackbar snackbar = Snackbar.make(view, message, Snackbar.LENGTH_LONG); 36 | 37 | if (action != null && listener != null) 38 | snackbar.setAction(action, listener); 39 | 40 | snackbar.show(); 41 | } 42 | 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /app/src/main/java/com/mlsdev/recipefinder/view/recipedetails/RecipeDetailsFragment.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder.view.recipedetails; 2 | 3 | import android.arch.lifecycle.ViewModelProvider; 4 | import android.arch.lifecycle.ViewModelProviders; 5 | import android.databinding.DataBindingUtil; 6 | import android.graphics.Bitmap; 7 | import android.os.Bundle; 8 | import android.support.annotation.Nullable; 9 | import android.support.v4.view.ViewCompat; 10 | import android.view.LayoutInflater; 11 | import android.view.View; 12 | import android.view.ViewGroup; 13 | 14 | import com.mlsdev.recipefinder.R; 15 | import com.mlsdev.recipefinder.databinding.FragmentRecipeDetailsBinding; 16 | import com.mlsdev.recipefinder.di.Injectable; 17 | import com.mlsdev.recipefinder.view.Extras; 18 | import com.mlsdev.recipefinder.view.MainActivity; 19 | import com.mlsdev.recipefinder.view.fragment.BaseFragment; 20 | 21 | import javax.inject.Inject; 22 | 23 | public class RecipeDetailsFragment extends BaseFragment implements Injectable { 24 | private FragmentRecipeDetailsBinding binding; 25 | private RecipeViewModel viewModel; 26 | 27 | @Inject 28 | ViewModelProvider.Factory viewModelFactory; 29 | 30 | @Override 31 | public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { 32 | viewModel = ViewModelProviders.of(this, viewModelFactory).get(RecipeViewModel.class); 33 | viewModel.setRecipeData(getArguments()); 34 | viewModel.setActionListener(this); 35 | binding = DataBindingUtil.inflate(inflater, R.layout.fragment_recipe_details, container, false); 36 | binding.setViewModel(viewModel); 37 | 38 | ((MainActivity) getActivity()).setSupportActionBar(binding.toolbar); 39 | if (((MainActivity) getActivity()).getSupportActionBar() != null) { 40 | ((MainActivity) getActivity()).getSupportActionBar().setDisplayHomeAsUpEnabled(true); 41 | } 42 | 43 | String imageTransitionName = getArguments().getString(Extras.IMAGE_TRANSITION_NAME); 44 | 45 | if (imageTransitionName != null) 46 | ViewCompat.setTransitionName(binding.ivRecipeImage, imageTransitionName); 47 | 48 | Bitmap bitmap = getArguments().getParcelable(Extras.IMAGE_DATA); 49 | if (bitmap != null) 50 | binding.ivRecipeImage.setImageBitmap(bitmap); 51 | 52 | return binding.getRoot(); 53 | } 54 | 55 | @Override 56 | public void onStart() { 57 | super.onStart(); 58 | viewModel.onStart(); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /app/src/main/java/com/mlsdev/recipefinder/view/searchrecipes/FilterDialogFragment.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder.view.searchrecipes; 2 | 3 | import android.app.Activity; 4 | import android.app.Dialog; 5 | import android.content.DialogInterface; 6 | import android.content.Intent; 7 | import android.databinding.DataBindingUtil; 8 | import android.os.Bundle; 9 | import android.support.annotation.NonNull; 10 | import android.support.v4.app.DialogFragment; 11 | import android.support.v7.app.AlertDialog; 12 | import android.view.LayoutInflater; 13 | 14 | import com.mlsdev.recipefinder.R; 15 | import com.mlsdev.recipefinder.databinding.DialogFragmentSearchFilterBinding; 16 | 17 | public class FilterDialogFragment extends DialogFragment { 18 | private DialogFragmentSearchFilterBinding binding; 19 | public static final String HEALTH_LABEL_KEY = "health_label_key"; 20 | public static final String DIET_LABEL_KEY = "diet_label_key"; 21 | private int healthSelectedIndex; 22 | private int dietSelectedIndex; 23 | 24 | @Override 25 | public void onStart() { 26 | super.onStart(); 27 | binding.spHealthLabels.setSelection(healthSelectedIndex); 28 | binding.spDietLabels.setSelection(dietSelectedIndex); 29 | } 30 | 31 | @NonNull 32 | @Override 33 | public Dialog onCreateDialog(Bundle savedInstanceState) { 34 | LayoutInflater inflater = getActivity().getLayoutInflater(); 35 | binding = DataBindingUtil.inflate(inflater, R.layout.dialog_fragment_search_filter, null, false); 36 | 37 | AlertDialog.Builder builder = new AlertDialog.Builder(getActivity(), R.style.AlertDialogAppCompat); 38 | builder.setView(binding.getRoot()) 39 | .setTitle(R.string.dialog_title_filter) 40 | .setPositiveButton(R.string.btn_apply_filter, new DialogInterface.OnClickListener() { 41 | @Override 42 | public void onClick(DialogInterface dialogInterface, int i) { 43 | Intent data = new Intent(); 44 | healthSelectedIndex = binding.spHealthLabels.getSelectedItemPosition(); 45 | dietSelectedIndex = binding.spDietLabels.getSelectedItemPosition(); 46 | data.putExtra(HEALTH_LABEL_KEY, (String) binding.spHealthLabels.getSelectedItem()); 47 | data.putExtra(DIET_LABEL_KEY, (String) binding.spDietLabels.getSelectedItem()); 48 | 49 | if (getTargetFragment() != null) { 50 | getTargetFragment().onActivityResult(SearchRecipeFragment.FILTER_REQUEST_CODE, 51 | Activity.RESULT_OK, data); 52 | } 53 | 54 | dismiss(); 55 | } 56 | }); 57 | 58 | 59 | return builder.create(); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /app/src/main/java/com/mlsdev/recipefinder/view/searchrecipes/RecipeListItemViewModel.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder.view.searchrecipes; 2 | 3 | import android.databinding.ObservableField; 4 | 5 | import com.mlsdev.recipefinder.data.entity.recipe.Recipe; 6 | 7 | public class RecipeListItemViewModel { 8 | private Recipe recipe; 9 | public final ObservableField recipeTitle; 10 | public final ObservableField recipeImageUrl; 11 | 12 | public RecipeListItemViewModel(Recipe recipe) { 13 | this.recipe = recipe; 14 | recipeTitle = new ObservableField<>(recipe.getLabel()); 15 | recipeImageUrl = new ObservableField<>(recipe.getImage()); 16 | } 17 | 18 | public void setRecipe(Recipe recipe) { 19 | this.recipe = recipe; 20 | recipeTitle.set(recipe.getLabel()); 21 | recipeImageUrl.set(recipe.getImage()); 22 | } 23 | 24 | public Recipe getRecipe() { 25 | return recipe; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/src/main/java/com/mlsdev/recipefinder/view/utils/DiagramUtils.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder.view.utils; 2 | 3 | import android.support.annotation.Nullable; 4 | 5 | import com.github.mikephil.charting.animation.Easing; 6 | import com.github.mikephil.charting.charts.PieChart; 7 | import com.github.mikephil.charting.components.Legend; 8 | import com.github.mikephil.charting.data.PieData; 9 | import com.github.mikephil.charting.data.PieDataSet; 10 | import com.github.mikephil.charting.data.PieEntry; 11 | import com.github.mikephil.charting.formatter.PercentFormatter; 12 | import com.mlsdev.recipefinder.R; 13 | import com.mlsdev.recipefinder.data.entity.nutrition.TotalNutrients; 14 | 15 | import java.util.ArrayList; 16 | import java.util.List; 17 | 18 | public class DiagramUtils { 19 | private UtilsUI utilsUI; 20 | 21 | public DiagramUtils(UtilsUI utilsUI) { 22 | this.utilsUI = utilsUI; 23 | } 24 | 25 | public ArrayList preparePieEntries(TotalNutrients nutrients) { 26 | ArrayList entries = new ArrayList<>(3); 27 | if (nutrients.getProtein() != null) 28 | entries.add(new PieEntry((float) nutrients.getProtein().getQuantity(), nutrients.getProtein().getLabel())); 29 | if (nutrients.getFat() != null) 30 | entries.add(new PieEntry((float) nutrients.getFat().getQuantity(), nutrients.getFat().getLabel())); 31 | if (nutrients.getCarbs() != null) 32 | entries.add(new PieEntry((float) nutrients.getCarbs().getQuantity(), nutrients.getCarbs().getLabel())); 33 | 34 | return entries; 35 | } 36 | 37 | public PieChart preparePieChart(PieChart pieChart) { 38 | pieChart.setUsePercentValues(true); 39 | pieChart.getDescription().setEnabled(false); 40 | pieChart.setExtraOffsets(5, 10, 5, 5); 41 | pieChart.setDragDecelerationFrictionCoef(0.96f); 42 | 43 | pieChart.setDrawHoleEnabled(true); 44 | pieChart.setHoleColor(android.R.color.transparent); 45 | 46 | pieChart.setTransparentCircleColor(utilsUI.getColor(android.R.color.white)); 47 | pieChart.setTransparentCircleAlpha(50); 48 | 49 | pieChart.setHoleRadius(40f); 50 | pieChart.setTransparentCircleRadius(45f); 51 | 52 | pieChart.setDrawCenterText(true); 53 | pieChart.setCenterText("Balance"); 54 | 55 | pieChart.setRotationAngle(0); 56 | // enable rotation of the chart by touch 57 | pieChart.setRotationEnabled(false); 58 | pieChart.setHighlightPerTapEnabled(true); 59 | pieChart.setDrawEntryLabels(true); 60 | pieChart.setEntryLabelColor(utilsUI.getColor(android.R.color.white)); 61 | pieChart.setEntryLabelTextSize(15f); 62 | 63 | Legend l = pieChart.getLegend(); 64 | l.setVerticalAlignment(Legend.LegendVerticalAlignment.BOTTOM); 65 | l.setHorizontalAlignment(Legend.LegendHorizontalAlignment.CENTER); 66 | l.setOrientation(Legend.LegendOrientation.HORIZONTAL); 67 | l.setDrawInside(false); 68 | 69 | return pieChart; 70 | } 71 | 72 | public PieDataSet createPieDataSet(List pieEntryList, 73 | @Nullable String label, 74 | @Nullable List colors) { 75 | PieDataSet pieDataSet = new PieDataSet(pieEntryList, label); 76 | pieDataSet.setSliceSpace(1.5f); 77 | pieDataSet.setSelectionShift(2f); 78 | 79 | pieDataSet.setDrawValues(true); 80 | 81 | if (colors == null) { 82 | colors = new ArrayList<>(3); 83 | colors.add(utilsUI.getColor(R.color.colorPrimaryDark)); 84 | colors.add(utilsUI.getColor(R.color.colorPrimary)); 85 | colors.add(utilsUI.getColor(R.color.colorAccent)); 86 | } 87 | 88 | pieDataSet.setColors(colors); 89 | return pieDataSet; 90 | } 91 | 92 | public PieData createPieData(PieDataSet pieDataSet) { 93 | PieData pieData = new PieData(pieDataSet); 94 | pieData.setValueFormatter(new PercentFormatter()); 95 | pieData.setValueTextSize(15f); 96 | pieData.setValueTextColor(utilsUI.getColor(android.R.color.white)); 97 | return pieData; 98 | } 99 | 100 | public void setData(PieChart pieChart, PieData pieData) { 101 | pieChart.setData(pieData); 102 | pieChart.highlightValue(null); 103 | pieChart.invalidate(); 104 | pieChart.animateY(1400, Easing.EasingOption.EaseInOutQuad); 105 | } 106 | 107 | } 108 | -------------------------------------------------------------------------------- /app/src/main/java/com/mlsdev/recipefinder/view/utils/ParamsHelper.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder.view.utils; 2 | 3 | public class ParamsHelper { 4 | 5 | public static String formatLabel(String label) { 6 | if (label == null) 7 | throw new IllegalArgumentException("'label' can't be 'null'"); 8 | 9 | String formattedLabel = label.toLowerCase(); 10 | formattedLabel = formattedLabel.replaceAll(" ", "-"); 11 | return formattedLabel; 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /app/src/main/java/com/mlsdev/recipefinder/view/utils/UtilsUI.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder.view.utils; 2 | 3 | import android.content.Context; 4 | import android.support.annotation.ColorInt; 5 | import android.support.annotation.ColorRes; 6 | 7 | import com.mlsdev.recipefinder.RecipeApplication; 8 | 9 | import java.text.DecimalFormat; 10 | 11 | public class UtilsUI { 12 | private Context context; 13 | 14 | public UtilsUI() { 15 | this.context = RecipeApplication.getInstance(); 16 | } 17 | 18 | @ColorInt 19 | public int getColor(@ColorRes int colorResId) { 20 | @ColorInt int color = 0; 21 | if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) { 22 | color = context.getResources().getColor(colorResId, context.getTheme()); 23 | } else { 24 | color = context.getResources().getColor(colorResId); 25 | } 26 | return color; 27 | } 28 | 29 | public static String formatDecimalToString(double value) { 30 | return new DecimalFormat("#0.00").format(value); 31 | } 32 | 33 | public static int getPersents(double fullValue, double partValue) { 34 | return (int) ((partValue / fullValue) * 100); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /app/src/main/java/com/mlsdev/recipefinder/view/viewmodel/BaseViewModel.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder.view.viewmodel; 2 | 3 | import android.arch.lifecycle.ViewModel; 4 | import android.content.Context; 5 | import android.support.annotation.NonNull; 6 | 7 | import com.mlsdev.recipefinder.R; 8 | import com.mlsdev.recipefinder.RecipeApplication; 9 | import com.mlsdev.recipefinder.data.source.BaseObserver; 10 | import com.mlsdev.recipefinder.data.source.repository.DataRepository; 11 | import com.mlsdev.recipefinder.view.ActionListener; 12 | 13 | import java.io.IOException; 14 | 15 | import io.reactivex.disposables.CompositeDisposable; 16 | import retrofit2.HttpException; 17 | 18 | public class BaseViewModel extends ViewModel { 19 | protected Context context = RecipeApplication.getInstance(); 20 | protected DataRepository repository; 21 | protected CompositeDisposable subscriptions; 22 | protected ActionListener actionListener; 23 | 24 | public BaseViewModel() { 25 | subscriptions = new CompositeDisposable(); 26 | } 27 | 28 | public void onDestroy() { 29 | subscriptions.clear(); 30 | } 31 | 32 | public void onStop() { 33 | 34 | } 35 | 36 | public void onStart() { 37 | 38 | } 39 | 40 | public void setActionListener(@NonNull ActionListener actionListener) { 41 | this.actionListener = actionListener; 42 | } 43 | 44 | protected void showError(Throwable throwable) { 45 | actionListener.showProgressDialog(false, null); 46 | String errorMessage = context.getString(R.string.error_message_common); 47 | 48 | if (throwable instanceof HttpException) { 49 | HttpException httpException = (HttpException) throwable; 50 | 51 | if (httpException.code() >= BaseObserver.SERVER_ERROR) 52 | errorMessage = context.getString(R.string.error_message_technical); 53 | } else if (throwable instanceof IOException) { 54 | errorMessage = context.getString(R.string.error_message_connection); 55 | } 56 | 57 | actionListener.showSnackbar(errorMessage); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /app/src/main/java/com/mlsdev/recipefinder/view/viewmodel/ViewModelFactory.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder.view.viewmodel; 2 | 3 | import android.arch.lifecycle.ViewModel; 4 | import android.arch.lifecycle.ViewModelProvider; 5 | 6 | import java.util.Map; 7 | 8 | import javax.inject.Inject; 9 | import javax.inject.Provider; 10 | import javax.inject.Singleton; 11 | 12 | @Singleton 13 | public class ViewModelFactory implements ViewModelProvider.Factory { 14 | private Map, Provider> creators; 15 | 16 | @Inject 17 | public ViewModelFactory(Map, Provider> creators) { 18 | this.creators = creators; 19 | } 20 | 21 | @Override 22 | public T create(Class modelClass) { 23 | Provider creator = creators.get(modelClass); 24 | 25 | if (creator == null) { 26 | for (Map.Entry, Provider> entry : creators.entrySet()) { 27 | if (modelClass.isAssignableFrom(entry.getKey())) { 28 | creator = entry.getValue(); 29 | break; 30 | } 31 | } 32 | } 33 | 34 | if (creator == null) 35 | throw new IllegalArgumentException("unknown model class " + modelClass); 36 | 37 | try { 38 | return (T) creator.get(); 39 | } catch (Exception e) { 40 | throw new RuntimeException(); 41 | } 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /app/src/main/res/anim/fade_in.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/anim/fade_out.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/color/tab_item_text_color.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v21/recipe_list_item_foreground.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v21/tab_item_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v21/toolbar_item_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/btn_cancel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MLSDev/RecipeFinderJavaVersion/7e87c0c029186c66fb6f23259f9a06b63a4c21ca/app/src/main/res/drawable-xxhdpi/btn_cancel.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_favorite_checked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MLSDev/RecipeFinderJavaVersion/7e87c0c029186c66fb6f23259f9a06b63a4c21ca/app/src/main/res/drawable-xxhdpi/ic_favorite_checked.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_favorite_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MLSDev/RecipeFinderJavaVersion/7e87c0c029186c66fb6f23259f9a06b63a4c21ca/app/src/main/res/drawable-xxhdpi/ic_favorite_normal.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_navigation_analyse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MLSDev/RecipeFinderJavaVersion/7e87c0c029186c66fb6f23259f9a06b63a4c21ca/app/src/main/res/drawable-xxhdpi/ic_navigation_analyse.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_navigation_favorites.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MLSDev/RecipeFinderJavaVersion/7e87c0c029186c66fb6f23259f9a06b63a4c21ca/app/src/main/res/drawable-xxhdpi/ic_navigation_favorites.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_navigation_search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MLSDev/RecipeFinderJavaVersion/7e87c0c029186c66fb6f23259f9a06b63a4c21ca/app/src/main/res/drawable-xxhdpi/ic_navigation_search.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_nothing_found.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MLSDev/RecipeFinderJavaVersion/7e87c0c029186c66fb6f23259f9a06b63a4c21ca/app/src/main/res/drawable-xxhdpi/ic_nothing_found.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MLSDev/RecipeFinderJavaVersion/7e87c0c029186c66fb6f23259f9a06b63a4c21ca/app/src/main/res/drawable-xxxhdpi/ic_close.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_empty_view.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MLSDev/RecipeFinderJavaVersion/7e87c0c029186c66fb6f23259f9a06b63a4c21ca/app/src/main/res/drawable-xxxhdpi/ic_empty_view.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_filter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MLSDev/RecipeFinderJavaVersion/7e87c0c029186c66fb6f23259f9a06b63a4c21ca/app/src/main/res/drawable-xxxhdpi/ic_filter.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MLSDev/RecipeFinderJavaVersion/7e87c0c029186c66fb6f23259f9a06b63a4c21ca/app/src/main/res/drawable-xxxhdpi/ic_search.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/primary_color_shape.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/primary_dark_color_shape.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/progress_bar_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/progress_drawable.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | 10 | 11 | 12 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/recipe_item_title_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/recipe_list_item_foreground.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/tab_item_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/toolbar_item_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/top_shadow.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 15 | 16 | 21 | 22 | 27 | 28 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_recipe_analysis_details.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 11 | 16 | 17 | 22 | 23 | 31 | 32 | 33 | 34 | 38 | 39 | 45 | 46 | 51 | 52 | 59 | 60 | 64 | 65 | 70 | 71 | 75 | 76 | 77 | 78 | 79 | 85 | 86 | 93 | 94 | 99 | 100 | 101 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /app/src/main/res/layout/add_ingredient_button.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 11 | 20 | 21 | -------------------------------------------------------------------------------- /app/src/main/res/layout/dialog_fragment_add_ingredient.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 13 | 14 | 19 | 20 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /app/src/main/res/layout/dialog_fragment_search_filter.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 14 | 15 | 21 | 22 | 28 | 29 | 36 | 37 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_analyse_nutrition.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 14 | 15 | 19 | 20 | 30 | 31 | 32 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_favorite_recipes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 11 | 15 | 16 | 20 | 21 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_recipe_analysis.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 11 | 15 | 16 | 20 | 21 | 27 | 28 | 29 | 34 | 35 | 41 | 42 | 43 | 48 | 49 | 55 | 56 | 57 | 66 | 67 | 72 | 73 | 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_search_recipes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 11 | 12 | 13 | 19 | 20 | 24 | 25 | 32 | 33 | 34 | 35 | 40 | 41 | 46 | 47 | 51 | 52 | 53 | 54 | 55 | 62 | 63 | 70 | 71 | 76 | 77 | 81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /app/src/main/res/layout/ingredient_list_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 11 | 17 | 18 | 25 | 26 | 38 | 39 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /app/src/main/res/layout/progress_view.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/layout/recipe_list_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 10 | 11 | 12 | 20 | 21 | 27 | 28 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /app/src/main/res/menu/navigation.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 10 | 11 | 17 | 18 | 24 | -------------------------------------------------------------------------------- /app/src/main/res/menu/options_menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 10 | 11 | 17 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MLSDev/RecipeFinderJavaVersion/7e87c0c029186c66fb6f23259f9a06b63a4c21ca/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MLSDev/RecipeFinderJavaVersion/7e87c0c029186c66fb6f23259f9a06b63a4c21ca/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MLSDev/RecipeFinderJavaVersion/7e87c0c029186c66fb6f23259f9a06b63a4c21ca/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MLSDev/RecipeFinderJavaVersion/7e87c0c029186c66fb6f23259f9a06b63a4c21ca/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MLSDev/RecipeFinderJavaVersion/7e87c0c029186c66fb6f23259f9a06b63a4c21ca/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/transition/change_image_transform.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 64dp 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #C5CAE9 5 | #303F9F 6 | #FF4081 7 | #55ffffff 8 | #BDBDBD 9 | #46ffffff 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 56dp 6 | 56dp 7 | 6dp 8 | 8dp 9 | 8dp 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | RecipeFinder 3 | 75347af5 4 | c12154e41f974c51a56fad5a256c27f2 5 | d07b5508 6 | b45686d25ac88da90a36f286122ee3de 7 | 8 | 9 | Analyze 10 | Search 11 | Favorites 12 | 13 | Recipe analysing details 14 | 15 | Nutrition 16 | Recipe 17 | 18 | Search… 19 | Ingredient for analysis 20 | Health labels 21 | Diet labels 22 | Ingredients 23 | Find your recipe! 24 | Nothing. Try again. 25 | 26 | apply 27 | Filter Nutrition 28 | Diet label 29 | Health label 30 | You have no favorites yet 31 | Analyze 32 | add 33 | add ingredient 34 | No Ingredients 35 | Add at least one ingredient 36 | 37 | Calories: %1$d 38 | Total weight: %1$s 39 | Yields: %1$s 40 | 41 | sharedImage%1$s 42 | %1$s; Weight: %2$s 43 | %1$d) 44 | Balance 45 | protein 46 | carbs 47 | fat 48 | Technical error 49 | Server error. Try again later. 50 | Something went wrong. 51 | Seems to be a problem with your internet connection. 52 | Error 53 | Fill in the ingredient field 54 | content 55 | Fill in 56 | 57 | 58 | Balanced 59 | High-Fiber 60 | High-Protein 61 | Low-Carb 62 | Low-Fat 63 | Low-Sodium 64 | 65 | 66 | 67 | Alcohol-free 68 | Vegan 69 | Vegetarian 70 | paleo 71 | Peanut-free 72 | Tree-nut-free 73 | 74 | 75 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | 13 | 17 | 18 | 23 | 24 | 29 | 30 | 37 | 38 | 44 | 45 | 51 | 52 | 55 | 56 | 63 | 64 | 65 | 71 | 72 | 79 | 80 | 81 | 85 | 86 | 96 | 97 | 98 | 99 | 103 | 104 | 105 | -------------------------------------------------------------------------------- /app/src/test/java/com/mlsdev/recipefinder/NutrientTest.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder; 2 | 3 | import com.mlsdev.recipefinder.data.entity.nutrition.Nutrient; 4 | 5 | import org.junit.Assert; 6 | import org.junit.Before; 7 | import org.junit.Test; 8 | 9 | public class NutrientTest { 10 | private double quantity = 100.081; 11 | private String label = "Protein"; 12 | private String unit = "g"; 13 | private String expectedResult; 14 | private Nutrient nutrient; 15 | 16 | @Before 17 | public void setUp() { 18 | expectedResult = label + " 100.08 " + unit; 19 | nutrient = new Nutrient(label, quantity, unit); 20 | } 21 | 22 | @Test 23 | public void testGetFormattedFullText() { 24 | String actualResult = nutrient.getFormattedFullText(); 25 | Assert.assertEquals(expectedResult, actualResult); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /app/src/test/java/com/mlsdev/recipefinder/ParamsHelperTest.java: -------------------------------------------------------------------------------- 1 | package com.mlsdev.recipefinder; 2 | 3 | import com.mlsdev.recipefinder.view.utils.ParamsHelper; 4 | 5 | import org.junit.Assert; 6 | import org.junit.Test; 7 | 8 | public class ParamsHelperTest { 9 | private final String expectedFormatLabelResult = "alcohol-free"; 10 | private final String testableFormatLabelValue = "Alcohol Free"; 11 | 12 | @Test 13 | public void testFilterLabelFormatting() { 14 | String actualResult = ParamsHelper.formatLabel(testableFormatLabelValue); 15 | Assert.assertEquals(expectedFormatLabelResult, actualResult); 16 | } 17 | 18 | @Test(expected = IllegalArgumentException.class) 19 | public void testFilterLabelFormatting_WithNullAsArgument() { 20 | ParamsHelper.formatLabel(null); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | mavenCentral() 7 | } 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:2.3.3' 10 | classpath 'com.stanfy.spoon:spoon-gradle-plugin:1.2.2' 11 | 12 | // NOTE: Do not place your application dependencies here; they belong 13 | // in the individual module build.gradle files 14 | } 15 | } 16 | 17 | allprojects { 18 | repositories { 19 | jcenter() 20 | maven { url "https://jitpack.io" } 21 | maven { url 'https://maven.google.com' } 22 | } 23 | } 24 | 25 | task clean(type: Delete) { 26 | delete rootProject.buildDir 27 | } 28 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | org.gradle.jvmargs=-Xmx1536m 13 | 14 | # When configured, Gradle will run in incubating parallel mode. 15 | # This option should only be used with decoupled projects. More details, visit 16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 17 | # org.gradle.parallel=true 18 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MLSDev/RecipeFinderJavaVersion/7e87c0c029186c66fb6f23259f9a06b63a4c21ca/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Mar 03 09:28:50 EET 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip 7 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | --------------------------------------------------------------------------------