diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..11db06a
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,51 @@
+# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
+# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
+local.properties
+.DS_Store
+/Database/content.db
+/Database/build
+
+# User-specific stuff:
+.idea/workspace.xml
+.idea/tasks.xml
+
+# Sensitive or high-churn files:
+.idea/dataSources/
+.idea/dataSources.ids
+.idea/dataSources.xml
+.idea/dataSources.local.xml
+.idea/sqlDataSources.xml
+.idea/dynamic.xml
+.idea/uiDesigner.xml
+.idea
+
+# Gradle:
+.idea/gradle.xml
+.idea/libraries
+.gradle
+/build/
+
+
+# Mongo Explorer plugin:
+.idea/mongoSettings.xml
+
+## File-based project format:
+*.iws
+*.iml
+
+## Plugin-specific files:
+
+# IntelliJ
+/out/
+
+# mpeltonen/sbt-idea plugin
+.idea_modules/
+
+# JIRA plugin
+atlassian-ide-plugin.xml
+
+# Crashlytics plugin (for Android Studio and IntelliJ)
+com_crashlytics_export_strings.xml
+crashlytics.properties
+crashlytics-build.properties
+fabric.properties
diff --git a/README.md b/README.md
index 2a4754e..7dea91d 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,4 @@
# FoodManager
Android project
+
+Ссылка на сервер: https://github.com/shavkunov/FoodManagerServer
\ No newline at end of file
diff --git a/app/.gitignore b/app/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/app/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/app/build.gradle b/app/build.gradle
new file mode 100644
index 0000000..d87dac6
--- /dev/null
+++ b/app/build.gradle
@@ -0,0 +1,38 @@
+apply plugin: 'com.android.application'
+
+sourceCompatibility = 1.7
+
+android {
+ compileSdkVersion 25
+ buildToolsVersion "25.0.0"
+ defaultConfig {
+ applicationId "ru.spbau.mit.foodmanager"
+ minSdkVersion 23
+ targetSdkVersion 25
+ versionCode 1
+ versionName "1.0"
+ testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+ lintOptions {
+ abortOnError false
+ }
+
+ sourceSets { main { java.srcDirs = ['src/main/java', 'src/main/java/mod'] } }
+}
+
+dependencies {
+ compile fileTree(dir: 'libs', include: ['*.jar'])
+ androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
+ exclude group: 'com.android.support', module: 'support-annotations'
+ })
+ compile 'com.android.support:appcompat-v7:25.0.0'
+ compile group: 'mysql', name: 'mysql-connector-java', version: '5.1.13'
+ compile group: 'com.cloudinary', name: 'cloudinary-android', version: '1.5.0'
+ testCompile 'junit:junit:4.12'
+}
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
new file mode 100644
index 0000000..55cf66c
--- /dev/null
+++ b/app/proguard-rules.pro
@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /home/ArgentumWalker/Android/Sdk/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
diff --git a/app/src/androidTest/java/ru/spbau/mit/foodmanager/ExampleInstrumentedTest.java b/app/src/androidTest/java/ru/spbau/mit/foodmanager/ExampleInstrumentedTest.java
new file mode 100644
index 0000000..bb39557
--- /dev/null
+++ b/app/src/androidTest/java/ru/spbau/mit/foodmanager/ExampleInstrumentedTest.java
@@ -0,0 +1,26 @@
+package ru.spbau.mit.foodmanager;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.*;
+
+/**
+ * Instrumentation test, which will execute on an Android device.
+ *
+ * @see Testing documentation
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+ @Test
+ public void useAppContext() throws Exception {
+ // Context of the app under test.
+ Context appContext = InstrumentationRegistry.getTargetContext();
+
+ assertEquals("ru.spbau.mit.foodmanager", appContext.getPackageName());
+ }
+}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..81625e6
--- /dev/null
+++ b/app/src/main/AndroidManifest.xml
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/ru/spbau/mit/foodmanager/BootCompleteReceiver.java b/app/src/main/java/ru/spbau/mit/foodmanager/BootCompleteReceiver.java
new file mode 100644
index 0000000..6c6cc24
--- /dev/null
+++ b/app/src/main/java/ru/spbau/mit/foodmanager/BootCompleteReceiver.java
@@ -0,0 +1,20 @@
+package ru.spbau.mit.foodmanager;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+
+/**
+ * Узнает о запуске Андроида и запускает процесс нотификаций
+ */
+
+public class BootCompleteReceiver extends BroadcastReceiver {
+ public BootCompleteReceiver() {
+
+ }
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ context.startService(new Intent(context, NotificationService.class));
+ }
+}
diff --git a/app/src/main/java/ru/spbau/mit/foodmanager/Category.java b/app/src/main/java/ru/spbau/mit/foodmanager/Category.java
new file mode 100644
index 0000000..7f953cc
--- /dev/null
+++ b/app/src/main/java/ru/spbau/mit/foodmanager/Category.java
@@ -0,0 +1,34 @@
+package ru.spbau.mit.foodmanager;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+
+import java.util.ArrayList;
+
+public class Category {
+ private String description;
+ private int ID;
+ private Bitmap image;
+
+ public Category(int ID, String description, Bitmap image) {
+ this.ID = ID;
+ this.description = description;
+ this.image = image;
+ }
+
+ public int getID() {
+ return ID;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public ArrayList getRecipes(Context context) {
+ return CookBookStorage.getInstance(context).getRecipesOfCategory(this.ID);
+ }
+
+ public Bitmap getImage() {
+ return image;
+ }
+}
diff --git a/app/src/main/java/ru/spbau/mit/foodmanager/ChooseMealtimePresetActivity.java b/app/src/main/java/ru/spbau/mit/foodmanager/ChooseMealtimePresetActivity.java
new file mode 100644
index 0000000..02b6c27
--- /dev/null
+++ b/app/src/main/java/ru/spbau/mit/foodmanager/ChooseMealtimePresetActivity.java
@@ -0,0 +1,124 @@
+package ru.spbau.mit.foodmanager;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.ImageButton;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+public class ChooseMealtimePresetActivity extends AppCompatActivity {
+ private static final int REQUEST_ADD = 0;
+ private static final int REQUEST_EDIT = 1;
+ private MenuSettings menuSettings;
+ private ArrayList presets;
+ private HashMap presetView;
+ private LayoutInflater inflater;
+ private LinearLayout presetList;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.choose_mealtime_preset);
+ menuSettings = MenuSettings.getInstance(this);
+ presets = DaySettings.getMealtimePresets(this);
+ inflater = (LayoutInflater) this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ presetList = (LinearLayout) findViewById(R.id.choose_mealtime_preset_list);
+ presetView = new HashMap<>();
+ fillPresets();
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ DaySettings.savePresets(this);
+ }
+
+ private void fillPresets() {
+ final LinearLayout v = presetList;
+ for (final DaySettings.MealtimeSettings settings : presets) {
+ v.addView(generatePresetView(v, settings));
+ }
+ ImageButton addBtn = (ImageButton) findViewById(R.id.choose_mealtime_preset_add);
+ addBtn.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ Intent intent = new Intent(ChooseMealtimePresetActivity.this,
+ MealtimeSettingsActivity.class);
+ startActivityForResult(intent, REQUEST_ADD);
+ }
+ });
+ }
+
+ public void onActivityResult(int requestCode, int errorCode, Intent resultContainer) {
+ switch (requestCode) {
+ case REQUEST_ADD :
+ if (errorCode == RESULT_OK) {
+ DaySettings.MealtimeSettings result =
+ (DaySettings.MealtimeSettings) resultContainer.getSerializableExtra("Result");
+ presets.add(result);
+ presetList.addView(generatePresetView(presetList, result));
+ }
+ break;
+ case REQUEST_EDIT :
+ if (errorCode == RESULT_OK) {
+ DaySettings.MealtimeSettings result =
+ (DaySettings.MealtimeSettings) resultContainer.getSerializableExtra("Result");
+ Integer position = resultContainer.getIntExtra("Position", -1);
+ presets.get(position).clone(result);
+ TextView mealtimeName = (TextView) presetView.get(presets.get(position))
+ .findViewById(R.id.menu_settings_day_mealtime_name);
+ mealtimeName.setText(result.getName());
+ }
+ break;
+ }
+ }
+
+ private View generatePresetView(final LinearLayout v, final DaySettings.MealtimeSettings settings) {
+ final View preset = inflater.inflate(R.layout.menu_settings_day_mealtime, null);
+ TextView mealtimeName = (TextView) preset.findViewById(R.id.menu_settings_day_mealtime_name);
+ ImageButton editBtn = (ImageButton) preset.findViewById(R.id.menu_settings_day_mealtime_edit);
+ ImageButton delBtn = (ImageButton) preset.findViewById(R.id.menu_settings_day_mealtime_delete);
+ final Integer position = presets.indexOf(settings);
+
+ mealtimeName.setText(settings.getName());
+ mealtimeName.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ returnResult(settings);
+ }
+ });
+ editBtn.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ Intent intent = new Intent(ChooseMealtimePresetActivity.this,
+ MealtimeSettingsActivity.class);
+ intent.putExtra("Settings", settings);
+ intent.putExtra("Position", position);
+ startActivityForResult(intent, REQUEST_EDIT);
+ }
+ });
+ delBtn.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ presets.remove(settings);
+ v.removeView(preset);
+ }
+ });
+ presetView.put(settings, preset);
+ return preset;
+ }
+
+ private void returnResult(DaySettings.MealtimeSettings result) {
+ Intent intent = getIntent();
+ intent.putExtra("Result", result);
+ setResult(RESULT_OK, intent);
+ finish();
+ }
+}
diff --git a/app/src/main/java/ru/spbau/mit/foodmanager/Commands.java b/app/src/main/java/ru/spbau/mit/foodmanager/Commands.java
new file mode 100644
index 0000000..2f8baca
--- /dev/null
+++ b/app/src/main/java/ru/spbau/mit/foodmanager/Commands.java
@@ -0,0 +1,27 @@
+package ru.spbau.mit.foodmanager;
+
+public class Commands {
+ public static final String getRecipeCommand = "/getRecipe";
+ public static final String getRecipeCategoriesCommand = "/getRecipeCategories";
+ public static final String getRecipeIngredientsCommand = "/getRecipeIngredients";
+ public static final String getRecipeStepsCommand = "/getRecipeSteps";
+ public static final String getRecipesByFilterCommand = "/getRecipesByFilter";
+ public static final String getUserSettingsCommand = "/getUserSettings";
+ public static final String getUserLikeCommand = "/getUserLike";
+ public static final String getRecipeLikesCommand = "/getRecipeLikes";
+ public static final String getFavoritesCommand = "/getFavorites";
+ public static final String getRecipesOfCategoryCommand = "/getRecipesOfCategory";
+ public static final String getCategoryByIDCommand = "/getCategoryByID";
+ public static final String getCategoriesListCommand = "/getCategoriesList";
+ public static final String setUserLikeCommand = "/setLike";
+ public static final String setUserNotLikeCommand = "/setNotLike";
+ public static final String addToFavoritesCommand = "/addToFavorites";
+ public static final String removeFromFavoritesCommand = "/removeFromFavorites";
+ public static final String saveUserSettingsCommand = "/saveUserSettings";
+ public static final String getRandomDishCommand = "/getRandomDishOfCategory";
+ public static final String isUserOwnRecipeCommand = "/ownRecipe";
+ public static final String insertRecipeCommand = "/insertRecipe";
+ public static final String deleteRecipeCommand = "/deleteRecipe";
+ public static final String changeRecipeCommand = "/changeRecipe";
+}
+
diff --git a/app/src/main/java/ru/spbau/mit/foodmanager/CookBookActivity.java b/app/src/main/java/ru/spbau/mit/foodmanager/CookBookActivity.java
new file mode 100755
index 0000000..0452a07
--- /dev/null
+++ b/app/src/main/java/ru/spbau/mit/foodmanager/CookBookActivity.java
@@ -0,0 +1,177 @@
+package ru.spbau.mit.foodmanager;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.LinearLayout;
+import android.widget.ListView;
+import android.widget.Spinner;
+
+import java.util.ArrayList;
+
+public class CookBookActivity extends AppCompatActivity {
+ private Intent task;
+ public static final int GROUPINGBY_TYPE_OF_DISH = 0;
+ public static final int GROUPINGBY_NATIONAL_KITCHEN = 1;
+ public static final int TARGET_NO = 0;
+ public static final int TARGET_CATEGORY = 1;
+ public static final int TARGET_RECIPE = 2;
+ private Integer target;
+ private static final String[] CATEGORY_TYPES =
+ {"Тип блюда",
+ "Национальные кухни"};
+ private final ArrayList categories = new ArrayList<>();
+ private CookBookStorage cookbook;
+ private GifImageView loaderAnimation;
+ private LinearLayout informationLayout;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.cook_book);
+
+ //Init loaderAnimation
+ loaderAnimation = (GifImageView) findViewById(R.id.loader_animation_view);
+ loaderAnimation.setGifImageResource(LoaderAnimationSelector.getRandomLoaderResource());
+ loaderAnimation.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
+ informationLayout = (LinearLayout) findViewById(R.id.information_layout);
+ //Init Task
+ task = getIntent();
+ target = task.getIntExtra("Target", TARGET_NO);
+ //Init Categories
+ cookbook = CookBookStorage.getInstance(this);
+ //Group by init
+ ArrayAdapter adapter = new ArrayAdapter(this,
+ android.R.layout.simple_spinner_dropdown_item, CATEGORY_TYPES);
+ adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ Spinner groupingBy = (Spinner) findViewById(R.id.cook_book_group_by);
+ groupingBy.setAdapter(adapter);
+ groupingBy.setSelection(0);
+ groupingBy.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+ @Override
+ public void onItemSelected(AdapterView> adapterView, View view, int i, long l) {
+ ContentLoader contentLoader;
+ Thread loader;
+ switch (i) {
+ case 0:
+ contentLoader = new ContentLoader(GROUPINGBY_TYPE_OF_DISH);
+ loader = new Thread(contentLoader);
+ loader.start();
+ break;
+ case 1:
+ contentLoader = new ContentLoader(GROUPINGBY_NATIONAL_KITCHEN);
+ loader = new Thread(contentLoader);
+ loader.start();
+ break;
+ }
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView> adapterView) {
+ //categories.clear();
+ }
+ });
+ }
+
+ public void onSearchClick(View v) {
+ Intent intent = new Intent(this, SearchRecipeActivity.class);
+ startActivityForResult(intent, target);
+ }
+
+ public void onActivityResult(int requestCode, int errorCode, Intent resultContainer) {
+ if (errorCode == RESULT_OK) {
+ if (target == TARGET_RECIPE) {
+ setResult(RESULT_OK, resultContainer);
+ finish();
+ } else {
+ int recipeID = resultContainer.getIntExtra("Result", 0);
+ Intent intent = new Intent(this, RecipeViewActivity.class);
+ intent.putExtra("Recipe", recipeID);
+ startActivity(intent);
+ }
+ }
+ }
+
+ private void showCategories() {
+ ListView listView = (ListView) findViewById(R.id.cook_book_list);
+ //TODO: сделать собственные адаптеры для CookBook и Category
+ ArrayList names = new ArrayList<>();
+ for (Category c : categories) {
+ names.add(c.getDescription());
+ }
+ ArrayAdapter adapter = new ArrayAdapter<>(this,
+ android.R.layout.simple_list_item_1, names);
+ listView.setAdapter(adapter);
+ listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView> adapterView, View view, int i, long l) {
+ Intent intent;
+ switch (target) {
+ case TARGET_NO:
+ intent = new Intent(CookBookActivity.this, CookBookCategoryActivity.class);
+ intent.putExtra("Category", categories.get(i).getID());
+ startActivity(intent);
+ break;
+ case TARGET_RECIPE:
+ intent = new Intent(CookBookActivity.this, CookBookCategoryActivity.class);
+ intent.putExtras(task);
+ intent.putExtra("Category", categories.get(i).getID());
+ startActivityForResult(intent, TARGET_RECIPE);
+ break;
+ case TARGET_CATEGORY:
+ intent = task;
+ intent.putExtra("Result", categories.get(i).getID());
+ setResult(RESULT_OK, intent);
+ finish();
+ break;
+ }
+ }
+ });
+ }
+
+ public class ContentLoader implements Runnable {
+ private int groupingBy;
+ public ContentLoader(int groupingBy) {
+ this.groupingBy = groupingBy;
+ }
+
+ @Override
+ public void run() {
+ CookBookActivity.this.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ loaderAnimation.setVisibility(View.VISIBLE);
+ informationLayout.setVisibility(View.INVISIBLE);
+ }
+ });
+ categories.clear();
+ while(categories.isEmpty()) {
+ categories.clear();
+ try {
+ switch (groupingBy) {
+ case GROUPINGBY_TYPE_OF_DISH:
+ categories.addAll(cookbook.getRecipesTypeOfDish());
+ break;
+ case GROUPINGBY_NATIONAL_KITCHEN:
+ categories.addAll(cookbook.getRecipesNationalKitchen());
+ break;
+ }
+ } catch (Throwable e) {
+ //Repeat
+ }
+ }
+ CookBookActivity.this.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ showCategories();
+ loaderAnimation.setVisibility(View.INVISIBLE);
+ informationLayout.setVisibility(View.VISIBLE);
+ loaderAnimation.setGifImageResource(LoaderAnimationSelector.getRandomLoaderResource());
+ }
+ });
+ }
+ }
+}
diff --git a/app/src/main/java/ru/spbau/mit/foodmanager/CookBookCategoryActivity.java b/app/src/main/java/ru/spbau/mit/foodmanager/CookBookCategoryActivity.java
new file mode 100755
index 0000000..800a1fe
--- /dev/null
+++ b/app/src/main/java/ru/spbau/mit/foodmanager/CookBookCategoryActivity.java
@@ -0,0 +1,138 @@
+package ru.spbau.mit.foodmanager;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+import android.util.Log;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+
+import java.util.ArrayList;
+
+public class CookBookCategoryActivity extends AppCompatActivity {
+ public static final int TARGET_FAVOURITES = 10;
+ private Category category;
+ private Intent task;
+ private Integer target;
+ private ArrayList recipes;
+ private GifImageView loaderAnimation;
+ private ListView informationLayout;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.cook_book_category);
+ //Init loaderAnimation
+ loaderAnimation = (GifImageView) findViewById(R.id.loader_animation_view);
+ loaderAnimation.setGifImageResource(LoaderAnimationSelector.getRandomLoaderResource());
+ loaderAnimation.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
+ informationLayout = (ListView) findViewById(R.id.cook_book_category_list);
+ //Task init
+ task = getIntent();
+ target = task.getIntExtra("Target",CookBookActivity.TARGET_NO);
+ ContentLoader contentLoader = new ContentLoader(task.getIntExtra("Category", -1));
+ Thread loader = new Thread(contentLoader);
+ loader.start();
+ //category = CookBookStorage.getInstance().getCategoryByID(task.getIntExtra("Category", -1));
+ }
+
+ public void onActivityResult(int requestCode, int errorCode, Intent resultContainer) {
+ if (errorCode == RESULT_OK) {
+ if (target == CookBookActivity.TARGET_RECIPE) {
+ setResult(RESULT_OK, resultContainer);
+ finish();
+ } else {
+ int recipeID = resultContainer.getIntExtra("Result", 0);
+ Intent intent = new Intent(this, RecipeViewActivity.class);
+ intent.putExtra("Recipe", recipeID);
+ startActivity(intent);
+ }
+ }
+ }
+
+ public void onSearchClick(View v) {
+ Intent intent = new Intent(this, SearchRecipeActivity.class);
+ startActivityForResult(intent, target);
+ }
+
+ private void showRecipies() {
+ Log.d("ShowRecipies", "Show");
+ ListView listView = (ListView) findViewById(R.id.cook_book_category_list);
+ ArrayList names = new ArrayList<>();
+ if (recipes != null) {
+ for (Recipe r : recipes) {
+ names.add(r.getName());
+ }
+ }
+ Log.d("names", ((Integer)names.size()).toString());
+ ArrayAdapter adapter = new ArrayAdapter<>(this,
+ android.R.layout.simple_list_item_1, names);
+ listView.setAdapter(adapter);
+ listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView> adapterView, View view, int i, long l) {
+ Intent intent;
+ switch (target) {
+ case CookBookActivity.TARGET_NO :
+ intent = new Intent(CookBookCategoryActivity.this, RecipeViewActivity.class);
+ intent.putExtra("Recipe", recipes.get(i).getID());
+ startActivity(intent);
+ break;
+ case TARGET_FAVOURITES :
+ intent = new Intent(CookBookCategoryActivity.this, RecipeViewActivity.class);
+ intent.putExtra("Recipe", recipes.get(i).getID());
+ startActivity(intent);
+ break;
+ case CookBookActivity.TARGET_RECIPE :
+ intent = task;
+ intent.putExtra("Result", recipes.get(i).getID());
+ setResult(RESULT_OK, intent);
+ finish();
+ break;
+ }
+ }
+ });
+ }
+
+ public class ContentLoader implements Runnable {
+ private int categoryID;
+ public ContentLoader(int id) {
+ this.categoryID = id;
+ }
+
+ @Override
+ public void run() {
+ CookBookCategoryActivity.this.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ loaderAnimation.setVisibility(View.VISIBLE);
+ informationLayout.setVisibility(View.INVISIBLE);
+ }
+ });
+ recipes = null;
+ while (recipes == null) {
+ try {
+ if (target != TARGET_FAVOURITES) {
+ recipes = CookBookStorage.getInstance(CookBookCategoryActivity.this).getRecipesOfCategory(categoryID);
+ } else {
+ recipes = CookBookStorage.getInstance(CookBookCategoryActivity.this).getFavorites();
+ }
+ }
+ catch (Throwable e) {
+ //Repeat
+ }
+ }
+ CookBookCategoryActivity.this.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ loaderAnimation.setVisibility(View.INVISIBLE);
+ informationLayout.setVisibility(View.VISIBLE);
+ showRecipies();
+ loaderAnimation.setGifImageResource(LoaderAnimationSelector.getRandomLoaderResource());
+ }
+ });
+ }
+ }
+}
diff --git a/app/src/main/java/ru/spbau/mit/foodmanager/CookBookStorage.java b/app/src/main/java/ru/spbau/mit/foodmanager/CookBookStorage.java
new file mode 100644
index 0000000..f0fd5d0
--- /dev/null
+++ b/app/src/main/java/ru/spbau/mit/foodmanager/CookBookStorage.java
@@ -0,0 +1,875 @@
+package ru.spbau.mit.foodmanager;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.util.Log;
+
+import com.cloudinary.Cloudinary;
+import com.cloudinary.utils.ObjectUtils;
+
+import org.json.JSONObject;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.Map;
+
+import static ru.spbau.mit.foodmanager.Commands.addToFavoritesCommand;
+import static ru.spbau.mit.foodmanager.Commands.changeRecipeCommand;
+import static ru.spbau.mit.foodmanager.Commands.deleteRecipeCommand;
+import static ru.spbau.mit.foodmanager.Commands.getCategoriesListCommand;
+import static ru.spbau.mit.foodmanager.Commands.getCategoryByIDCommand;
+import static ru.spbau.mit.foodmanager.Commands.getFavoritesCommand;
+import static ru.spbau.mit.foodmanager.Commands.getRandomDishCommand;
+import static ru.spbau.mit.foodmanager.Commands.getRecipeCategoriesCommand;
+import static ru.spbau.mit.foodmanager.Commands.getRecipeCommand;
+import static ru.spbau.mit.foodmanager.Commands.getRecipeIngredientsCommand;
+import static ru.spbau.mit.foodmanager.Commands.getRecipeLikesCommand;
+import static ru.spbau.mit.foodmanager.Commands.getRecipeStepsCommand;
+import static ru.spbau.mit.foodmanager.Commands.getRecipesByFilterCommand;
+import static ru.spbau.mit.foodmanager.Commands.getRecipesOfCategoryCommand;
+import static ru.spbau.mit.foodmanager.Commands.getUserLikeCommand;
+import static ru.spbau.mit.foodmanager.Commands.getUserSettingsCommand;
+import static ru.spbau.mit.foodmanager.Commands.insertRecipeCommand;
+import static ru.spbau.mit.foodmanager.Commands.isUserOwnRecipeCommand;
+import static ru.spbau.mit.foodmanager.Commands.removeFromFavoritesCommand;
+import static ru.spbau.mit.foodmanager.Commands.saveUserSettingsCommand;
+import static ru.spbau.mit.foodmanager.Commands.setUserLikeCommand;
+import static ru.spbau.mit.foodmanager.Commands.setUserNotLikeCommand;
+
+/**
+ * Хранилище всех рецептов. Singleton. В этом классе слишком много методов, которые можно отнести
+ * к другим классам. Будет изменено после рефакторинга.
+ */
+public class CookBookStorage {
+ private static final String CLOUD_SERVER_IP = "138.68.91.54";
+ private static final String LOCAL_SERVER_IP = "192.168.211.199";
+ private static final int port = 48800; // free random port;
+ private static final int HTTP_CONNECT_TIMEOUT_MS = 5000;
+ private static final int HTTP_READ_TIMEOUT_MS = 5000;
+ private static final int MAX_ATTEMPTS = 3;
+
+ /**
+ * Инстанс класса CookBookStorage.
+ */
+ private static CookBookStorage instance;
+
+ /**
+ * Вспомогательный тег для отладки.
+ */
+ private final String LOG_TAG = "CookBookStorageTAG";
+
+ /**
+ * Логин для хостинга картинок cloudinary.
+ */
+ private final String CLOUDINARY_URL = "cloudinary://285162791646134:yGqzM1FdReQ8uPa1taEUZihoNgI@dxc952wrd";
+
+ /**
+ * ID пользователя.
+ */
+ private String userID;
+
+ private CookBookStorage(Context context) {
+ userID = Installation.getUserID(context);
+ }
+
+ /**
+ * Изменение информации о рецепте. Можно было изменить рецепт, удалив его и добавив новый,
+ * но использование этого метода эффективнее в количестве SQL запросов.
+ * @param recipe информация этого рецепта будет помещена в БД.
+ */
+ public void changeRecipe(RecipeToChange recipe) throws Exception {
+ for (int attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {
+ HttpURLConnection connection = storeRecipeInformation(recipe, changeRecipeCommand);
+ if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
+ continue;
+ }
+ break;
+ }
+ }
+
+ /**
+ * Добавление рецепта в базу данных на сервере. Если одна из операций вставок провалилась,
+ * то рецепт не будет встален полностью.
+ * @param recipe добавление рецепта в базу данных на сервере.
+ * @return ID рецепта.
+ */
+ public int insertRecipe(RecipeToChange recipe) throws Exception {
+ int recipeID = -1;
+ for (int attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {
+ HttpURLConnection connection = storeRecipeInformation(recipe, insertRecipeCommand);
+ if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
+ continue;
+ }
+
+ ObjectInputStream input = new ObjectInputStream(connection.getInputStream());
+ recipeID = input.readInt();
+ input.close();
+
+ break;
+ }
+
+ return recipeID;
+ }
+
+ private HttpURLConnection storeRecipeInformation(RecipeToChange recipe, String command) throws Exception {
+ final HttpURLConnection connection =
+ openHttpURLConnectionForServerCommand(command);
+
+ ObjectOutputStream output = new ObjectOutputStream(connection.getOutputStream());
+ output.writeObject(recipe.getName());
+ output.writeObject(recipe.getDescription());
+ output.writeObject(recipe.getCategoryIDs());
+ output.writeObject(userID);
+ output.writeObject(recipe.getIngredients());
+
+ ArrayList descriptions = new ArrayList<>();
+ ArrayList links = new ArrayList<>();
+ if (recipe.getSteps() != null) {
+ for (Step step : recipe.getSteps()) {
+ descriptions.add(step.getDescription());
+ links.add(uploadImage(step.getImage()));
+ }
+ }
+
+ output.writeObject(descriptions);
+ output.writeObject(links);
+
+ if (command.equals(deleteRecipeCommand) || command.equals(changeRecipeCommand)) {
+ output.writeInt(recipe.getID());
+ }
+ output.flush();
+ output.close();
+
+ return connection;
+ }
+
+ /**
+ * Загрузка изображения.
+ * @param bitmap изображение.
+ * @return онлайн ссылка на изображение в сервисе cloudinary
+ */
+ private String uploadImage(Bitmap bitmap) throws Exception {
+ InputStream imageIn = convertImage(bitmap);
+ Cloudinary cloudinary = new Cloudinary(CLOUDINARY_URL);
+ Map result = cloudinary.uploader().upload(imageIn, ObjectUtils.emptyMap());
+ JSONObject jsonObject = new JSONObject(result);
+
+ return jsonObject.getString("url");
+ }
+
+ private ByteArrayInputStream convertImage(Bitmap bitmap) {
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ bitmap.compress(Bitmap.CompressFormat.PNG, 0 /*ignored for PNG*/, bos);
+ byte[] bitmapData = bos.toByteArray();
+ return new ByteArrayInputStream(bitmapData);
+ }
+
+ /**
+ * Добавить рецепт в избранное, предполагается, что его там нет.
+ * @param recipe рецепт, который хотим добавить.
+ */
+ public void addToFavorites(Recipe recipe) {
+ for (int attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {
+ try {
+ HttpURLConnection connection = openHttpURLConnectionForServerCommand(
+ addToFavoritesCommand);
+ ObjectOutputStream output = new ObjectOutputStream(connection.getOutputStream());
+ output.writeInt(recipe.getID());
+ output.writeObject(userID);
+ output.flush();
+ output.close();
+
+ if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
+ continue;
+ }
+ break;
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ /**
+ * Удаление рецепта из БД.
+ */
+ public void deleteRecipe(RecipeToChange recipe) {
+ try {
+ for (int attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {
+ HttpURLConnection connection = storeRecipeInformation(recipe, deleteRecipeCommand);
+
+ if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
+ continue;
+ }
+
+ break;
+ }
+ } catch (Exception e) {
+ Log.d(LOG_TAG, "Unable to delete recipe");
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Удаление рецепта из избранного.
+ * @param recipeID ID рецепта.
+ */
+ public void removeFromFavorites(int recipeID) {
+ for (int attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {
+ try {
+ HttpURLConnection connection = openHttpURLConnectionForServerCommand(
+ removeFromFavoritesCommand);
+ ObjectOutputStream output = new ObjectOutputStream(connection.getOutputStream());
+ output.writeInt(recipeID);
+ output.flush();
+ output.close();
+
+ if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
+ continue;
+ }
+ break;
+ } catch (Exception e) {
+ Log.d(LOG_TAG, "Не удалось убрать рецепт из избранного");
+ e.printStackTrace();
+ }
+ }
+ }
+
+ /**
+ * Получение рецепта по его уникальному идентификатору.
+ */
+ public Recipe getRecipe(int ID) {
+ Recipe recipe = null;
+
+ try {
+ for (int attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {
+ final HttpURLConnection connection =
+ openHttpURLConnectionForServerCommand(getRecipeCommand);
+
+ ObjectOutputStream stream = new ObjectOutputStream(connection.getOutputStream());
+ stream.writeInt(ID);
+ stream.flush();
+ stream.close();
+
+ if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
+ continue;
+ }
+
+ ObjectInputStream input = new ObjectInputStream(connection.getInputStream());
+ recipe = (Recipe) input.readObject();
+ input.close();
+ break;
+ }
+ } catch (Exception e) {
+ Log.d(LOG_TAG, "Unable to get recipe");
+ e.printStackTrace();
+ }
+
+ return recipe;
+ }
+
+ /**
+ * Получение категорий, к которым принадлежит рецепт.
+ * @param ID ID рецепта.
+ */
+ public ArrayList getRecipeCategories(int ID) {
+ ArrayList ids = null;
+ try {
+ for (int attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {
+ final HttpURLConnection connection = openHttpURLConnectionForServerCommand(
+ getRecipeCategoriesCommand);
+
+ ObjectOutputStream output = new ObjectOutputStream(connection.getOutputStream());
+ output.writeInt(ID);
+ output.flush();
+ output.close();
+
+ if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
+ continue;
+ }
+
+ ObjectInputStream input = new ObjectInputStream(connection.getInputStream());
+ ids = (ArrayList) input.readObject();
+ break;
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ Log.d(LOG_TAG, "Unable to get categories of recipe");
+ }
+
+ return ids;
+ }
+
+ /**
+ * Получение ингредиентов из двух таблиц: Ingredient_to_recipe и Ingredient
+ * @param ID ID рецепта.
+ */
+ public ArrayList getRecipeIngredients(int ID) {
+ ArrayList recipeIngredients = new ArrayList<>();
+
+ try {
+ for (int attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {
+ final HttpURLConnection connection = openHttpURLConnectionForServerCommand(
+ getRecipeIngredientsCommand);
+
+ ObjectOutputStream output = new ObjectOutputStream(connection.getOutputStream());
+ output.writeInt(ID);
+ output.flush();
+ output.close();
+
+ if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
+ continue;
+ }
+
+ ObjectInputStream input = new ObjectInputStream(connection.getInputStream());
+ recipeIngredients = (ArrayList) input.readObject();
+
+ break;
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ return recipeIngredients;
+ }
+
+ /**
+ * Получение инструкции для готовки из двух таблиц -- Step и Image.
+ * Картинки не загружаются.
+ * @param ID ID рецепта.
+ */
+ public ArrayList getRecipeSteps(int ID) {
+ ArrayList recipeSteps = new ArrayList<>();
+
+ for (int attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {
+ try {
+ HttpURLConnection connection = openHttpURLConnectionForServerCommand(
+ getRecipeStepsCommand);
+ ObjectOutputStream output = new ObjectOutputStream(connection.getOutputStream());
+ output.writeInt(ID);
+ output.flush();
+ output.close();
+
+ if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
+ continue;
+ }
+
+ ObjectInputStream input = new ObjectInputStream(connection.getInputStream());
+ ArrayList> stepsData = (ArrayList>)
+ input.readObject();
+
+ for (ArrayList data : stepsData) {
+ String stepDescription = data.get(0);
+ String imageURL = data.get(1);
+ recipeSteps.add(new Step(stepDescription, imageURL));
+ }
+
+ break;
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ return recipeSteps;
+ }
+
+ /**
+ * Получение списка рецептов по фильтру, т.е. по префиксу.
+ */
+ public ArrayList getRecipesByFilter(String filter) {
+ filter = filter.toLowerCase();
+ if (filter.length() > 0) {
+ filter = filter.substring(0, 1).toUpperCase() + filter.substring(1);
+ }
+
+ ArrayList res = new ArrayList<>();
+ for (int attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {
+ try {
+ HttpURLConnection connection = openHttpURLConnectionForServerCommand(
+ getRecipesByFilterCommand);
+
+ ObjectOutputStream output = new ObjectOutputStream(connection.getOutputStream());
+ output.writeObject(filter);
+ output.flush();
+ output.close();
+
+ if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
+ continue;
+ }
+
+ ObjectInputStream input = new ObjectInputStream(connection.getInputStream());
+ res = (ArrayList) input.readObject();
+
+ } catch (Exception e) {
+ Log.d(LOG_TAG, "Unable to filter recipes");
+ e.printStackTrace();
+ }
+ }
+
+ return res;
+ }
+
+ /**
+ * Получение пользовательских настроек в БД.
+ * @return settings настройки хранятся как сериализованный instance MenuSettings в этой строчке.
+ */
+ public String getUserSettings() {
+ String userSettings = "";
+ for (int attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {
+ try {
+ HttpURLConnection connection = openHttpURLConnectionForServerCommand(
+ getUserSettingsCommand);
+
+ ObjectOutputStream output = new ObjectOutputStream(connection.getOutputStream());
+ output.writeObject(userID);
+ output.flush();
+ output.close();
+
+ if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
+ continue;
+ }
+
+ ObjectInputStream input = new ObjectInputStream(connection.getInputStream());
+ userSettings = (String) input.readObject();
+ input.close();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ return userSettings;
+ }
+
+ /**
+ * Получение инстанса класса CookBookStorage.
+ * @param context контекст приложения.
+ * @return инстанс класса.
+ */
+ public static CookBookStorage getInstance(Context context) {
+ if (instance == null) {
+ instance = new CookBookStorage(context);
+ }
+
+ return instance;
+ }
+
+ /**
+ * Возвращает количество лайков рецепта.
+ * @param recipe вернется количество лайков этого рецепта.
+ * @return null если не удалось соединится с сервером, иначе количество лайков.
+ */
+ public Integer getRecipeLikes(Recipe recipe) {
+ for (int attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {
+ try {
+ HttpURLConnection connection = openHttpURLConnectionForServerCommand(
+ getRecipeLikesCommand);
+ ObjectOutputStream output = new ObjectOutputStream(connection.getOutputStream());
+ output.writeInt(recipe.getID());
+ output.flush();
+ output.close();
+
+ if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
+ continue;
+ }
+
+ ObjectInputStream input = new ObjectInputStream(connection.getInputStream());
+ int likes = input.readInt();
+ input.close();
+ return likes;
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Если пользователь поставил лайк то вернется True, иначе False. Null если не получилось
+ * соединиться с сервером.
+ * @param recipe вернется лайк пользователя этого рецепта.
+ * @return если пользователь поставил лайк то вернется True, иначе False. Null если не получилось
+ * соединиться с сервером.
+ */
+ public Boolean getUserLike(Recipe recipe) {
+ for (int attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {
+ try {
+ HttpURLConnection connection = openHttpURLConnectionForServerCommand(getUserLikeCommand);
+ ObjectOutputStream output = new ObjectOutputStream(connection.getOutputStream());
+ output.writeInt(recipe.getID());
+ output.writeObject(userID);
+ output.flush();
+ output.close();
+
+ if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
+ continue;
+ }
+
+ ObjectInputStream input = new ObjectInputStream(connection.getInputStream());
+ int like = input.readInt();
+ input.close();
+ return like == 1;
+ } catch (Exception e) {
+ Log.d(LOG_TAG, "Не удалось получить лайк");
+ e.printStackTrace();
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Получение избранных рецептов.
+ * @return пустой список если в избранном пусто.
+ */
+ public ArrayList getFavorites() {
+ ArrayList favorites = new ArrayList<>();
+ for (int attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {
+ try {
+ HttpURLConnection connection = openHttpURLConnectionForServerCommand(
+ getFavoritesCommand);
+ ObjectOutputStream output = new ObjectOutputStream(connection.getOutputStream());
+ output.writeObject(userID);
+ output.flush();
+ output.close();
+
+ if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
+ continue;
+ }
+
+ ObjectInputStream input = new ObjectInputStream(connection.getInputStream());
+ favorites = (ArrayList) input.readObject();
+ input.close();
+ break;
+ } catch (Exception e) {
+ Log.d(LOG_TAG, "Не удалось получить избранное");
+ e.printStackTrace();
+ }
+ }
+
+ return favorites;
+ }
+
+ /**
+ * Получение всех рецептов категории.
+ * @param ID идентификатор категории.
+ * @return рецепты категории.
+ */
+ public ArrayList getRecipesOfCategory(int ID) {
+ ArrayList recipes = new ArrayList<>();
+ for (int attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {
+ try {
+ HttpURLConnection connection = openHttpURLConnectionForServerCommand(
+ getRecipesOfCategoryCommand);
+ ObjectOutputStream output = new ObjectOutputStream(connection.getOutputStream());
+ output.writeInt(ID);
+ output.flush();
+ output.close();
+
+ if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
+ continue;
+ }
+
+ ObjectInputStream input = new ObjectInputStream(connection.getInputStream());
+ recipes = (ArrayList) input.readObject();
+ input.close();
+ break;
+ } catch (Exception e) {
+ Log.d(LOG_TAG, "Не удалось получить рецепты категории");
+ e.printStackTrace();
+ }
+ }
+
+ return recipes;
+ }
+
+ /**
+ * Получение категории по ID.
+ * @param ID идентификатор категории, которую хотим получить.
+ * @return инстанс класса Category
+ */
+ public Category getCategoryByID(int ID) {
+ Category category = null;
+
+ for (int attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {
+ try {
+ HttpURLConnection connection = openHttpURLConnectionForServerCommand(
+ getCategoryByIDCommand);
+ ObjectOutputStream output = new ObjectOutputStream(connection.getOutputStream());
+ output.writeInt(ID);
+ output.flush();
+ output.close();
+
+ if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
+ continue;
+ }
+
+ ObjectInputStream input = new ObjectInputStream(connection.getInputStream());
+ String categoryName = (String) input.readObject();
+ category = new Category(ID, categoryName, null);
+ input.close();
+ break;
+ } catch (Exception e) {
+ Log.d(LOG_TAG, "Не удалось получить категорию");
+ e.printStackTrace();
+ }
+ }
+
+ return category;
+ }
+
+ /**
+ * Получение списка категорий.
+ * @param categoryType тип категорий, который хотим получить.
+ * @return Linked List категорий, полученных из запроса.
+ */
+ private LinkedList getCategoryByType(String categoryType) {
+ LinkedList categories = new LinkedList<>();
+
+ for (int attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {
+ try {
+ HttpURLConnection connection = openHttpURLConnectionForServerCommand(
+ getCategoriesListCommand);
+ ObjectOutputStream output = new ObjectOutputStream(connection.getOutputStream());
+ output.writeObject(categoryType);
+ output.flush();
+ output.close();
+
+ if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
+ continue;
+ }
+
+ ObjectInputStream input = new ObjectInputStream(connection.getInputStream());
+ ArrayList ids = (ArrayList) input.readObject();
+ ArrayList names = (ArrayList) input.readObject();
+ for (int i = 0; i < ids.size(); i++) {
+ categories.add(new Category(ids.get(i), names.get(i), null));
+ }
+
+ input.close();
+ break;
+ } catch (Exception e) {
+ Log.d(LOG_TAG, "Не удалось получить список категорий");
+ e.printStackTrace();
+ }
+ }
+
+ return categories;
+ }
+
+ /**
+ * Получение списка категорий по типу блюда.
+ * @return список категорий по типу блюда.
+ */
+ public LinkedList getRecipesTypeOfDish() {
+ String categoryType = "category_dish";
+
+ return getCategoryByType(categoryType);
+ }
+
+ /**
+ * Получение списка категорий по национальной кухне блюда.
+ * @return список категорий по национальной кухне блюда.
+ */
+ public LinkedList getRecipesNationalKitchen() {
+ String categoryType = "national_kitchen";
+
+ return getCategoryByType(categoryType);
+ }
+
+ //----------------------------------rest-------------------------------
+
+ /**
+ * Сохранение пользовательских настроек в БД.
+ * @param settings настройки хранятся как сериализованный instance MenuSettings в этой строчке.
+ */
+ public void saveUserSettings(String settings) {
+ for (int attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {
+ try {
+ HttpURLConnection connection = openHttpURLConnectionForServerCommand(
+ saveUserSettingsCommand);
+ ObjectOutputStream output = new ObjectOutputStream(connection.getOutputStream());
+ output.writeObject(userID);
+ output.writeObject(settings);
+ output.flush();
+ output.close();
+
+ if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
+ continue;
+ }
+
+ break;
+ } catch (Exception e) {
+ Log.d(LOG_TAG, "Не удалось сохранить настройки");
+ e.printStackTrace();
+ }
+ }
+ }
+
+ /**
+ * Загрузка картинки шага.
+ * @param step загрузка прямо в поле объекта.
+ */
+ public void downloadStepImage(Step step) {
+ Log.d(LOG_TAG, step.getImageLink());
+ try {
+ Bitmap image = BitmapFactory.decodeStream((InputStream)new URL(step.getImageLink())
+ .getContent());
+
+ step.setImage(image);
+ } catch (IOException e) {
+ Log.d(LOG_TAG, "Unable to load step image\ndescription: " + step.getDescription());
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Метод ставит лайк пользователя.
+ * @param recipeID ID рецепта.
+ * @return true если операция прошла успешно, иначе false.
+ */
+ public boolean setLike(int recipeID) {
+ for (int attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {
+ try {
+ HttpURLConnection connection = openHttpURLConnectionForServerCommand(setUserLikeCommand);
+ ObjectOutputStream output = new ObjectOutputStream(connection.getOutputStream());
+ output.writeInt(recipeID);
+ output.writeObject(userID);
+ output.flush();
+ output.close();
+
+ if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
+ continue;
+ }
+
+ return true;
+ } catch (Exception e) {
+ Log.d(LOG_TAG, "Не удалось поставить лайк");
+ e.printStackTrace();
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Метод убирает лайк пользователя.
+ * @param recipeID ID рецепта.
+ * @return true, если операция прошла успешно, false иначе.
+ */
+ public boolean setNotLike(int recipeID) {
+ for (int attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {
+ try {
+ HttpURLConnection connection = openHttpURLConnectionForServerCommand(
+ setUserNotLikeCommand);
+ ObjectOutputStream output = new ObjectOutputStream(connection.getOutputStream());
+ output.writeInt(recipeID);
+ output.writeObject(userID);
+ output.flush();
+ output.close();
+
+ if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
+ continue;
+ }
+
+ return true;
+ } catch (Exception e) {
+ Log.d(LOG_TAG, "Не удалось убрать лайк");
+ e.printStackTrace();
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Выбор случайного блюда категории.
+ */
+ public Recipe chooseRandomDishFromCategory(int categoryID) {
+ Recipe recipe = null;
+
+ for (int attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {
+ try {
+ HttpURLConnection connection = openHttpURLConnectionForServerCommand(
+ getRandomDishCommand);
+ ObjectOutputStream output = new ObjectOutputStream(connection.getOutputStream());
+ output.writeInt(categoryID);
+ output.flush();
+ output.close();
+
+ if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
+ continue;
+ }
+
+ ObjectInputStream input = new ObjectInputStream(connection.getInputStream());
+ recipe = (Recipe) input.readObject();
+ input.close();
+
+ break;
+ } catch (Exception e) {
+ Log.d(LOG_TAG, "Не удалось получить случайный рецепт категории");
+ e.printStackTrace();
+ }
+ }
+
+ return recipe;
+ }
+
+ /**
+ * Является ли текущий пользователь владельцем рецепта.
+ * @param recipe рецепт, который хотим проверить
+ * @return true если recipe принадлежить пользователь и false иначе.
+ */
+ public boolean isUserOwnRecipe(Recipe recipe) {
+ boolean answer = false;
+
+ for (int attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {
+ try {
+ HttpURLConnection connection = openHttpURLConnectionForServerCommand(
+ isUserOwnRecipeCommand);
+ ObjectOutputStream output = new ObjectOutputStream(connection.getOutputStream());
+ output.writeInt(recipe.getID());
+ output.writeObject(userID);
+ output.flush();
+ output.close();
+
+ if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
+ continue;
+ }
+
+ ObjectInputStream input = new ObjectInputStream(connection.getInputStream());
+ answer = input.readBoolean();
+ input.close();
+
+ break;
+ } catch (Exception e) {
+ Log.d(LOG_TAG, "Не удалось проверить принадлежность рецепта");
+ e.printStackTrace();
+ }
+ }
+
+ return answer;
+ }
+
+ private static HttpURLConnection openHttpURLConnectionForServerCommand(String command) throws IOException {
+ final String urlString = "http://" + CLOUD_SERVER_IP + ':' + port + command;
+
+ final URL url = new URL(urlString);
+
+ final HttpURLConnection connection = (HttpURLConnection) url.openConnection();
+ connection.setRequestMethod("POST");
+ connection.setDoOutput(true);
+ connection.setConnectTimeout(HTTP_CONNECT_TIMEOUT_MS);
+ connection.setReadTimeout(HTTP_READ_TIMEOUT_MS);
+
+ return connection;
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/ru/spbau/mit/foodmanager/Day.java b/app/src/main/java/ru/spbau/mit/foodmanager/Day.java
new file mode 100644
index 0000000..7c170c1
--- /dev/null
+++ b/app/src/main/java/ru/spbau/mit/foodmanager/Day.java
@@ -0,0 +1,3 @@
+package ru.spbau.mit.foodmanager;
+
+enum Day {Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday}
diff --git a/app/src/main/java/ru/spbau/mit/foodmanager/DayMenu.java b/app/src/main/java/ru/spbau/mit/foodmanager/DayMenu.java
new file mode 100644
index 0000000..9cde0ed
--- /dev/null
+++ b/app/src/main/java/ru/spbau/mit/foodmanager/DayMenu.java
@@ -0,0 +1,39 @@
+package ru.spbau.mit.foodmanager;
+
+import android.content.Intent;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+
+/**
+ * Описывет меню на день.
+ */
+
+public class DayMenu implements Serializable{
+ private ArrayList mealtimes;
+
+ /**
+ * Просто сохраняет список Mealtime
+ */
+ public DayMenu(ArrayList dishes) {
+ mealtimes = dishes;
+ }
+
+ /**
+ * Возвращает сохраненный список Mealtime
+ */
+ public ArrayList getMealtimes() {
+ return mealtimes;
+ }
+
+ /**
+ * Возвращает список всех рецептов из Mealtime
+ */
+ public ArrayList getDishes() {
+ ArrayList result = new ArrayList<>();
+ for (Mealtime m : mealtimes) {
+ result.addAll(m.getRecipeIDs());
+ }
+ return result;
+ }
+}
diff --git a/app/src/main/java/ru/spbau/mit/foodmanager/DaySettings.java b/app/src/main/java/ru/spbau/mit/foodmanager/DaySettings.java
new file mode 100644
index 0000000..efdf142
--- /dev/null
+++ b/app/src/main/java/ru/spbau/mit/foodmanager/DaySettings.java
@@ -0,0 +1,118 @@
+package ru.spbau.mit.foodmanager;
+
+import android.content.Context;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.util.ArrayList;
+
+/**
+ * Настройки меню определенного дня
+ */
+
+public class DaySettings implements Serializable {
+ private ArrayList mealtimeSettings = new ArrayList<>();
+ static private ArrayList presets;
+ private static final String presetsFilename = "Presets";
+
+ public static void savePresets(Context context) {
+ try {
+ FileOutputStream output = context.openFileOutput(
+ presetsFilename, Context.MODE_PRIVATE);
+
+ ObjectOutputStream outputStream = new ObjectOutputStream(output);
+ outputStream.writeObject(presets);
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ public static void loadPresets(Context context) {
+ File settings = new File(context.getFilesDir(), presetsFilename);
+ if (settings.exists()) {
+ try {
+ FileInputStream input = context.openFileInput(presetsFilename);
+ ObjectInputStream inputStream = new ObjectInputStream(input);
+ presets = (ArrayList) inputStream.readObject();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ static public ArrayList getMealtimePresets(Context context) {
+ if (presets == null) {
+ loadPresets(context);
+ }
+ if (presets == null) {
+ presets = new ArrayList<>();
+ {
+ ArrayList categories = new ArrayList<>();
+ categories.add(10);
+ presets.add(new MealtimeSettings("Завтрак", categories));
+ }
+ {
+ ArrayList categories = new ArrayList<>();
+ categories.add(9);
+ //categories.add(1);
+ //categories.add(0);
+ presets.add(new MealtimeSettings("Обед", categories));
+ }
+ {
+ ArrayList categories = new ArrayList<>();
+ categories.add(8);
+ presets.add(new MealtimeSettings("Ужин", categories));
+ }
+ savePresets(context);
+ }
+ return presets;
+ }
+
+ public DaySettings(ArrayList settings) {
+ mealtimeSettings = new ArrayList<>();
+ for (MealtimeSettings s : settings) {
+ mealtimeSettings.add(new MealtimeSettings(s.getName(), s.dishesCategories()));
+ }
+ }
+
+ public ArrayList getMealtimeSettings() {
+ return mealtimeSettings;
+ }
+
+ public ArrayList getCategoryIDs() {
+ ArrayList result = new ArrayList<>();
+ for (MealtimeSettings settigs : mealtimeSettings) {
+ result.addAll(settigs.dishesCategories());
+ }
+ return result;
+ }
+
+ public static class MealtimeSettings implements Serializable {
+ private String name;
+ //TODO: Использовать объединение ID категорий
+ private ArrayList categories;
+
+ public MealtimeSettings(String name, ArrayList settings) {
+ this.name = name;
+ this.categories = new ArrayList<>(settings);
+ }
+
+ public void clone(MealtimeSettings settings) {
+ this.name = settings.name;
+ this.categories = settings.categories;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public ArrayList dishesCategories() {
+ return categories;
+ }
+ }
+}
diff --git a/app/src/main/java/ru/spbau/mit/foodmanager/EditRecipeActivity.java b/app/src/main/java/ru/spbau/mit/foodmanager/EditRecipeActivity.java
new file mode 100644
index 0000000..09225b7
--- /dev/null
+++ b/app/src/main/java/ru/spbau/mit/foodmanager/EditRecipeActivity.java
@@ -0,0 +1,325 @@
+package ru.spbau.mit.foodmanager;
+
+import android.Manifest;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.graphics.Bitmap;
+import android.net.Uri;
+import android.provider.MediaStore;
+import android.support.v7.app.AppCompatActivity;
+import android.os.Bundle;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.EditText;
+import android.widget.ImageButton;
+import android.widget.LinearLayout;
+import android.widget.Spinner;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.util.ArrayList;
+
+//TODO Add, Delete and Edit tags
+public class EditRecipeActivity extends AppCompatActivity {
+ private final static int REQUEST_PICK_IMAGE = 0;
+ private final static int REQUEST_EDIT_STEPS = 1;
+ private final static int REQUEST_PICK_CATEGORY = 2;
+ private int recipeID;
+ private Object saveSynchronizer = new Object();
+ private ArrayList uriSteps;
+ private ArrayList ingredients;
+ private ArrayList tags;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.edit_recipe);
+ recipeID = getIntent().getIntExtra("RecipeID", 0);
+ ingredients = new ArrayList<>();
+ uriSteps = new ArrayList<>();
+ tags = new ArrayList<>();
+ if (recipeID != 0) {
+ EditText name = (EditText) findViewById(R.id.edit_recipe_header_name);
+ EditText description = (EditText) findViewById(R.id.edit_recipe_body_description);
+ CookBookStorage cookbook = CookBookStorage.getInstance(this);
+ Recipe recipe = cookbook.getRecipe(recipeID);
+ ingredients = cookbook.getRecipeIngredients(recipeID);
+ for (Step s : cookbook.getRecipeSteps(recipeID)) {
+ uriSteps.add(new UriStep(s));
+ }
+ while (tags == null) {
+ tags = cookbook.getRecipeCategories(recipeID);
+ }
+ name.setText(recipe.getName());
+ description.setText(recipe.getDescription());
+ showRecipeData();
+ }
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int errorCode, Intent resultContainer) {
+ if (errorCode == RESULT_OK) {
+ switch (requestCode) {
+ /*case REQUEST_PICK_IMAGE:
+ Uri newImage = resultContainer.getData();
+ ImageView recipeImage = (ImageView) findViewById(R.id.edit_recipe_header_photo);
+ try {
+ recipeImage.setImageBitmap(MediaStore.Images.Media.getBitmap(this.getContentResolver(), newImage));
+ }
+ catch (IOException e) {
+ //Cant upload image;
+ }
+ break;*/
+ case REQUEST_EDIT_STEPS:
+ uriSteps = (ArrayList) resultContainer.getSerializableExtra("UriSteps");
+ break;
+ case REQUEST_PICK_CATEGORY:
+ final Integer category = resultContainer.getIntExtra("Result", 0);
+ tags.add(category);
+ addTagToView(category);
+ break;
+ }
+ }
+ }
+
+ public void onSaveClick(View v) {
+ EditText name = (EditText) findViewById(R.id.edit_recipe_header_name);
+ EditText description = (EditText) findViewById(R.id.edit_recipe_body_description);
+ final RecipeToChange result = new RecipeToChange(recipeID,
+ description.getText().toString(), name.getText().toString());
+ result.setCategoryIDs(tags);
+ result.setIngredients(ingredients);
+ //INISteps
+ final ArrayList steps = new ArrayList<>();
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ synchronized (saveSynchronizer) {
+ for (UriStep uriStep : uriSteps) {
+ String descr = uriStep.getDescription();
+ Bitmap image;
+ try {
+ if (checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE)
+ != PackageManager.PERMISSION_GRANTED) {
+ requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
+ MainActivity.PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE);
+ }
+ if (checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE)
+ != PackageManager.PERMISSION_GRANTED) {
+ return;
+ }
+ image = MediaStore.Images.Media.getBitmap(
+ EditRecipeActivity.this.getContentResolver(), uriStep.getImageUri());
+ } catch (IOException e) {
+ Step s = new Step(
+ uriStep.getDescription(),
+ uriStep.getImageUri().toString());
+ CookBookStorage.getInstance(EditRecipeActivity.this).downloadStepImage(s);
+ image = s.getImage();
+ }
+ steps.add(new Step(descr, image));
+ }
+ result.setSteps(steps);
+ try {
+ if (recipeID == 0) {
+ recipeID = CookBookStorage.getInstance(EditRecipeActivity.this).insertRecipe(result);
+ } else {
+ CookBookStorage.getInstance(EditRecipeActivity.this).changeRecipe(result);
+ }
+
+ EditRecipeActivity.this.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ Toast.makeText(EditRecipeActivity.this, "Saved", Toast.LENGTH_SHORT).show();
+ }
+ });
+ } catch (final Exception e) {
+ EditRecipeActivity.this.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ Toast.makeText(EditRecipeActivity.this,
+ "Unable to save recipe\n" + "Cause:\n" + e.toString(),
+ Toast.LENGTH_LONG).show();
+ }
+ });
+ }
+ }
+ }
+ }).start();
+ }
+
+ public void onStepsClick(View v) {
+ Intent task = new Intent(this, EditStepActivity.class);
+ task.putExtra("UriSteps", uriSteps);
+ try {
+ ObjectOutputStream test = new ObjectOutputStream(new ByteArrayOutputStream());
+ test.writeObject(uriSteps);
+ Log.d("STEP CLICK", test.toString());
+ }
+ catch (Exception e) {
+ Log.d("STEP BOOM", e.toString());
+ }
+ startActivityForResult(task, REQUEST_EDIT_STEPS);
+ }
+
+ //Finished
+ public void onAddIngredientClick(View v) {
+ final Ingredient newIngredient = new Ingredient("", Measure.gr, 0.0);
+ ingredients.add(newIngredient);
+ addIngredientToView(newIngredient);
+ }
+
+ //We have no separated pic of recipe yet
+ /*public void onRecipeImageClick(View v) {
+ Intent getIntent = new Intent(Intent.ACTION_GET_CONTENT);
+ getIntent.setType("image/*");
+ Intent pickIntent = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
+ pickIntent.setType("image/*");
+ Intent chooserIntent = Intent.createChooser(getIntent, "Select Image");
+ chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Intent[] {pickIntent});
+ startActivityForResult(chooserIntent, REQUEST_PICK_IMAGE);
+ }*/
+
+ public void onAddTagClick(View view) {
+ Intent task = new Intent(this, CookBookActivity.class);
+ task.putExtra("Target", CookBookActivity.TARGET_CATEGORY);
+ startActivityForResult(task, REQUEST_PICK_CATEGORY);
+ }
+
+ public static class UriStep implements Serializable {
+ private String description;
+ private String imageUri;
+
+ public UriStep() {}
+ public UriStep(Step s) {
+ description = s.getDescription();
+ imageUri = s.getImageLink();
+ }
+ public Uri getImageUri() {
+ if (imageUri != null) {
+ return Uri.parse(imageUri);
+ } else {
+ return null;
+ }
+ }
+ public void setImageUri(Uri uri) {
+ if (uri != null) {
+ imageUri = uri.toString();
+ } else {
+ imageUri = null;
+ }
+ }
+
+ public String getDescription() {
+ return description;
+ }
+ public void setDescription(String s) {
+ description = s;
+ }
+ }
+
+ public void showRecipeData() {
+ for (Integer i : tags) {
+ addTagToView(i);
+ }
+ for (Ingredient i : ingredients) {
+ addIngredientToView(i);
+ }
+ }
+
+ private void addTagToView(final Integer category) {
+ final LinearLayout tagsLayout = (LinearLayout) findViewById(R.id.edit_recipe_header_tags);
+ final View newTag = ((LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE))
+ .inflate(R.layout.edit_recipe_tag, null);
+ TextView newTagName = (TextView) newTag.findViewById(R.id.edit_recipe_tag_name);
+ ImageButton newTagDelete = (ImageButton) newTag.findViewById(R.id.edit_recipe_delete_tag);
+ Category newCategory = null;
+ while (newCategory == null) {
+ newCategory = CookBookStorage.getInstance(this).getCategoryByID(category);
+ }
+ newTagName.setText(newCategory.getDescription());
+ newTagDelete.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ tagsLayout.removeView(newTag);
+ tags.remove(category);
+ }
+ });
+ tagsLayout.addView(newTag);
+ }
+
+ private void addIngredientToView(final Ingredient newIngredient) {
+ final LinearLayout ingredientsLayout = (LinearLayout) findViewById(R.id.edit_recipe_body_ingredients);
+ LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ //Create ingredient view
+ final View view = inflater.inflate(R.layout.edit_recipe_ingredient, null);
+ EditText ingredientNameView = (EditText) view.findViewById(R.id.edit_recipe_ingredient_name);
+ EditText ingredientCountView = (EditText) view.findViewById(R.id.edit_recipe_ingredient_count);
+ Spinner ingredientMeasureView = (Spinner) view.findViewById(R.id.edit_recipe_ingredient_measure);
+ ImageButton delete = (ImageButton) view.findViewById(R.id.edit_recipe_ingredient_delete);
+ //Change name
+ ingredientNameView.setText(newIngredient.getName());
+ ingredientNameView.addTextChangedListener(new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
+ @Override
+ public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
+ newIngredient.setName(charSequence.toString());
+ }
+ @Override
+ public void afterTextChanged(Editable editable) {}
+ });
+ //Change count
+ ingredientCountView.setText(new Double(newIngredient.getQuantity()).toString());
+ ingredientCountView.addTextChangedListener(new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
+ @Override
+ public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
+ if (charSequence.length() != 0) {
+ newIngredient.setQuantity(Double.parseDouble(charSequence.toString()));
+ }
+ }
+ @Override
+ public void afterTextChanged(Editable editable) {}
+ });
+ //Change measurement
+ ArrayList measurementNames = new ArrayList<>();
+ for (Measure m : Measure.values()) {
+ measurementNames.add(Ingredient.getMeasureName(m));
+ }
+ ArrayAdapter adapter = new ArrayAdapter<>(this,
+ android.R.layout.simple_spinner_dropdown_item, measurementNames);
+ adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ ingredientMeasureView.setAdapter(adapter);
+ ingredientMeasureView.setSelection(newIngredient.getMeasure().ordinal());
+ ingredientMeasureView.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+ @Override
+ public void onItemSelected(AdapterView> adapterView, View view, int i, long l) {
+ newIngredient.setMeasure(Measure.values()[i]);
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView> adapterView) {}
+ });
+ //remove ingredient
+ delete.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ ingredientsLayout.removeView(view);
+ ingredients.remove(newIngredient);
+ }
+ });
+ ingredientsLayout.addView(view);
+ }
+}
diff --git a/app/src/main/java/ru/spbau/mit/foodmanager/EditStepActivity.java b/app/src/main/java/ru/spbau/mit/foodmanager/EditStepActivity.java
new file mode 100644
index 0000000..f18d86c
--- /dev/null
+++ b/app/src/main/java/ru/spbau/mit/foodmanager/EditStepActivity.java
@@ -0,0 +1,169 @@
+package ru.spbau.mit.foodmanager;
+
+import android.Manifest;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.graphics.BitmapFactory;
+import android.net.Uri;
+import android.provider.MediaStore;
+import android.support.v7.app.AppCompatActivity;
+import android.os.Bundle;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.view.View;
+import android.widget.EditText;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+public class EditStepActivity extends AppCompatActivity {
+ private static final int REQUEST_PICK_IMAGE = 0;
+ private ArrayList uriSteps;
+ private Integer stepPosition;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.edit_step);
+
+ Intent task = getIntent();
+ uriSteps = (ArrayList) task.getSerializableExtra("UriSteps");
+ if (uriSteps == null) {
+ uriSteps = new ArrayList<>();
+ }
+ stepPosition = 0;
+ showStep(stepPosition);
+ //Initialize edit text
+ EditText stepDescription = (EditText) findViewById(R.id.edit_step_step_text);
+ stepDescription.addTextChangedListener(new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
+ @Override
+ public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
+ if (uriSteps.size() > stepPosition) {
+ uriSteps.get(stepPosition).setDescription(charSequence.toString());
+ }
+ }
+ @Override
+ public void afterTextChanged(Editable editable) {}
+ });
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int errorCode, Intent resultContainer) {
+ if (errorCode == RESULT_OK) {
+ switch (requestCode) {
+ case REQUEST_PICK_IMAGE:
+ try {
+ Uri imageUri = resultContainer.getData();
+ ImageView image = (ImageView) findViewById(R.id.edit_step_step_image);
+ image.setImageBitmap(MediaStore.Images.Media.getBitmap(this.getContentResolver(), imageUri));
+ uriSteps.get(stepPosition).setImageUri(imageUri);
+ }
+ catch (IOException e) {
+ //Failed load image
+ }
+ break;
+ }
+ }
+ }
+
+ public void onAddClick(View v) {
+ EditRecipeActivity.UriStep newStep = new EditRecipeActivity.UriStep();
+ uriSteps.add(stepPosition, newStep);
+ showStep(stepPosition);
+ }
+
+ public void onDeleteClick(View v) {
+ if (stepPosition > 0) {
+ uriSteps.remove((int)stepPosition);
+ stepPosition--;
+ }
+ showStep(stepPosition);
+ }
+
+ public void onImageClick(View v) {
+ if (checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE)
+ != PackageManager.PERMISSION_GRANTED) {
+ requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
+ MainActivity.PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE);
+ }
+ if (checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE)
+ != PackageManager.PERMISSION_GRANTED) {
+ return;
+ }
+ Intent getIntent = new Intent(Intent.ACTION_GET_CONTENT);
+ getIntent.setType("image/*");
+ Intent pickIntent = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
+ pickIntent.setType("image/*");
+ Intent chooserIntent = Intent.createChooser(getIntent, "Select Image");
+ chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Intent[] {pickIntent});
+ startActivityForResult(chooserIntent, REQUEST_PICK_IMAGE);
+ }
+
+ public void onSaveClick(View v) {
+ Boolean checkingOK = true;
+ for (EditRecipeActivity.UriStep step : uriSteps) {
+ if (step.getDescription().equals("") || step.getImageUri() == null) {
+ checkingOK = false;
+ //TODO: Show alarm
+ }
+ }
+ if (checkingOK) {
+ Intent result = new Intent();
+ result.putExtra("UriSteps", uriSteps);
+ setResult(RESULT_OK, result);
+ //TODO: Show result ok
+ finish();
+ }
+ }
+
+ public void onPrevBtnClick(View view) {
+ if (stepPosition > 0) {
+ stepPosition--;
+ showStep(stepPosition);
+ }
+ }
+
+ public void onNextBtnClick(View view) {
+ if (stepPosition < uriSteps.size() - 1) {
+ stepPosition++;
+ showStep(stepPosition);
+ }
+ }
+
+ private void showStep(int id) {
+ //TODO If there are no step, hide edit field and image field
+ if (uriSteps.size() > 0) {
+ EditText description = (EditText) findViewById(R.id.edit_step_step_text);
+ if (uriSteps.get(id).getDescription() != null) {
+ description.setText(uriSteps.get(id).getDescription());
+ } else {
+ description.setText("");
+ }
+ ImageView image = (ImageView) findViewById(R.id.edit_step_step_image);
+ if (uriSteps.get(id).getImageUri() != null) {
+ try {
+ image.setImageBitmap(
+ MediaStore.Images.Media.getBitmap(this.getContentResolver(), uriSteps.get(id).getImageUri()));
+ } catch (IOException e) {
+ Step s = new Step(
+ uriSteps.get(id).getDescription(),
+ uriSteps.get(id).getImageUri().toString());
+ CookBookStorage.getInstance(this).downloadStepImage(s);
+ image.setImageBitmap(s.getImage());
+ }
+ } else {
+ //TODO upload image
+ image.setImageBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.add_image));
+ }
+ TextView counter = (TextView) findViewById(R.id.edit_step_position);
+ counter.setText(((Integer) (id + 1)).toString());
+ TextView count = (TextView) findViewById(R.id.edit_step_step_count);
+ count.setText(((Integer) uriSteps.size()).toString());
+
+ }
+ }
+}
diff --git a/app/src/main/java/ru/spbau/mit/foodmanager/EntityType.java b/app/src/main/java/ru/spbau/mit/foodmanager/EntityType.java
new file mode 100644
index 0000000..6592ac2
--- /dev/null
+++ b/app/src/main/java/ru/spbau/mit/foodmanager/EntityType.java
@@ -0,0 +1,3 @@
+package ru.spbau.mit.foodmanager;
+
+public enum EntityType {step, recipe, category}
\ No newline at end of file
diff --git a/app/src/main/java/ru/spbau/mit/foodmanager/GifImageView.java b/app/src/main/java/ru/spbau/mit/foodmanager/GifImageView.java
new file mode 100644
index 0000000..74e289e
--- /dev/null
+++ b/app/src/main/java/ru/spbau/mit/foodmanager/GifImageView.java
@@ -0,0 +1,79 @@
+package ru.spbau.mit.foodmanager;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Movie;
+import android.os.SystemClock;
+import android.util.AttributeSet;
+import android.view.View;
+
+import java.io.InputStream;
+
+/**
+ * Copypaste from Internet. I just need a view, which plays animated gif
+ */
+
+public class GifImageView extends View {
+
+ private InputStream mInputStream;
+ private Movie mMovie;
+ private int mWidth, mHeight;
+ private long mStart;
+ private Context mContext;
+ public GifImageView(Context context) {
+ super(context);
+ this.mContext = context;
+ }
+
+ public GifImageView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public GifImageView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ this.mContext = context;
+ if (attrs.getAttributeName(1).equals("background")) {
+ int id = Integer.parseInt(attrs.getAttributeValue(1).substring(1));
+ setGifImageResource(id);
+ }
+ }
+
+
+ private void init() {
+ setFocusable(true);
+ mMovie = Movie.decodeStream(mInputStream);
+ mWidth = mMovie.width();
+ mHeight = mMovie.height();
+ mStart = SystemClock.uptimeMillis();
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ setMeasuredDimension(mWidth, mHeight);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ canvas.drawColor(Color.TRANSPARENT);
+ super.onDraw(canvas);
+ long now = SystemClock.uptimeMillis();
+ if (mMovie != null) {
+ int duration = mMovie.duration();
+ if (duration == 0) {
+ duration = 1;
+ }
+ int relTime;
+ relTime = (int) ((now - mStart) % duration);
+ mMovie.setTime(relTime);
+ mMovie.draw(canvas, 0, 0);
+ this.invalidate();
+ }
+ }
+
+ public void setGifImageResource(int id) {
+ mInputStream = mContext.getResources().openRawResource(id);
+ init();
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/ru/spbau/mit/foodmanager/Ingredient.java b/app/src/main/java/ru/spbau/mit/foodmanager/Ingredient.java
new file mode 100644
index 0000000..4e16288
--- /dev/null
+++ b/app/src/main/java/ru/spbau/mit/foodmanager/Ingredient.java
@@ -0,0 +1,73 @@
+package ru.spbau.mit.foodmanager;
+
+import java.io.Serializable;
+
+public class Ingredient implements Serializable {
+ private String name;
+ private Measure type;
+ private double quantity;
+
+ public Ingredient(String name, Measure type, double amount) {
+ this.name = name;
+ this.type = type;
+ this.quantity = amount;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getTypeName() {
+ return getMeasureName(type);
+ }
+
+
+ public static String getMeasureName(Measure m) {
+ switch (m) {
+ case gr:
+ return "гр.";
+
+ case ml:
+ return "мл.";
+
+ case apiece:
+ return "шт.";
+
+ case teaspoon:
+ return "ч.л.";
+
+ case tablespoon:
+ return "ст.л.";
+
+ case pinch:
+ return "щепотка";
+
+ case byTaste:
+ return "по вкусу";
+
+ case cloves:
+ return "зуб.";
+ }
+ return null;
+ }
+
+ public Measure getMeasure() {
+ return type;
+ }
+
+ public double getQuantity() {
+ return quantity;
+ }
+
+ public void setName(String s) {
+ name = s;
+ }
+
+ public void setQuantity(double q) {
+ quantity = q;
+ }
+
+ public void setMeasure(Measure m) {
+ type = m;
+ }
+}
diff --git a/app/src/main/java/ru/spbau/mit/foodmanager/Installation.java b/app/src/main/java/ru/spbau/mit/foodmanager/Installation.java
new file mode 100644
index 0000000..88af9d6
--- /dev/null
+++ b/app/src/main/java/ru/spbau/mit/foodmanager/Installation.java
@@ -0,0 +1,47 @@
+package ru.spbau.mit.foodmanager;
+
+import android.content.Context;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.util.UUID;
+
+public class Installation {
+ private static String sID = null;
+ private static final String INSTALLATION = "INSTALLATION";
+
+ /**
+ * Получение пользовательского ID. Подробнее по ссылке.
+ * https://android-developers.googleblog.com/2011/03/identifying-app-installations.html
+ */
+ public synchronized static String getUserID(Context context) {
+ if (sID == null) {
+ File installation = new File(context.getFilesDir(), INSTALLATION);
+ try {
+ if (!installation.exists())
+ writeInstallationFile(installation);
+ sID = readInstallationFile(installation);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ return sID;
+ }
+
+ private static String readInstallationFile(File installation) throws IOException {
+ RandomAccessFile f = new RandomAccessFile(installation, "r");
+ byte[] bytes = new byte[(int) f.length()];
+ f.readFully(bytes);
+ f.close();
+ return new String(bytes);
+ }
+
+ private static void writeInstallationFile(File installation) throws IOException {
+ FileOutputStream out = new FileOutputStream(installation);
+ String id = UUID.randomUUID().toString();
+ out.write(id.getBytes());
+ out.close();
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/ru/spbau/mit/foodmanager/MainActivity.java b/app/src/main/java/ru/spbau/mit/foodmanager/MainActivity.java
new file mode 100644
index 0000000..f9d07de
--- /dev/null
+++ b/app/src/main/java/ru/spbau/mit/foodmanager/MainActivity.java
@@ -0,0 +1,57 @@
+package ru.spbau.mit.foodmanager;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.StrictMode;
+import android.support.v7.app.AppCompatActivity;
+import android.view.View;
+
+public class MainActivity extends AppCompatActivity {
+ public final static int PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE = 1;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.main);
+ StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
+ StrictMode.setThreadPolicy(policy);
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ while (CookBookStorage.getInstance(MainActivity.this) == null);
+ startService(new Intent(MainActivity.this, NotificationService.class));
+ }
+ }).start();
+ }
+
+ public void onOpenCookBookClick(View view) {
+ Intent intent = new Intent(this, CookBookActivity.class);
+ startActivity(intent);
+ }
+
+ public void onWeekMenuClick(View view) {
+ Intent intent = new Intent(this, MenuViewActivity.class);
+ startActivity(intent);
+ }
+
+ public void onShoppingListClick(View view) {
+ Intent intent = new Intent(this, ShoppingListActivity.class);
+ startActivity(intent);
+ }
+
+ public void onSettingsClick(View view) {
+ Intent intent = new Intent(this, SettingsActivity.class);
+ startActivity(intent);
+ }
+
+ public void onFavouritesClick(View view) {
+ Intent intent = new Intent(this, CookBookCategoryActivity.class);
+ intent.putExtra("Target", CookBookCategoryActivity.TARGET_FAVOURITES);
+ startActivity(intent);
+ }
+
+ public void onAddRecipeClick(View view) {
+ Intent intent = new Intent(this, EditRecipeActivity.class);
+ startActivity(intent);
+ }
+}
diff --git a/app/src/main/java/ru/spbau/mit/foodmanager/Mealtime.java b/app/src/main/java/ru/spbau/mit/foodmanager/Mealtime.java
new file mode 100644
index 0000000..1f34956
--- /dev/null
+++ b/app/src/main/java/ru/spbau/mit/foodmanager/Mealtime.java
@@ -0,0 +1,35 @@
+package ru.spbau.mit.foodmanager;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+
+/**
+ * Описывает один прием пищи
+ */
+public class Mealtime implements Serializable {
+ private ArrayList recipeIDs;
+ private String name;
+ //Нужно ли время?
+
+ /**
+ * Задает название и блюда приема пищи
+ */
+ public Mealtime(String name, ArrayList recipes) {
+ this.name = name;
+ this.recipeIDs = recipes;
+ }
+
+ /**
+ * Возвращает имя приема пищи
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Возвращает список ID блюд приема пищи
+ */
+ public ArrayList getRecipeIDs() {
+ return recipeIDs;
+ }
+}
diff --git a/app/src/main/java/ru/spbau/mit/foodmanager/MealtimeSettingsActivity.java b/app/src/main/java/ru/spbau/mit/foodmanager/MealtimeSettingsActivity.java
new file mode 100644
index 0000000..7f58253
--- /dev/null
+++ b/app/src/main/java/ru/spbau/mit/foodmanager/MealtimeSettingsActivity.java
@@ -0,0 +1,122 @@
+package ru.spbau.mit.foodmanager;
+
+import android.content.Context;
+import android.content.Intent;
+import android.support.v7.app.AppCompatActivity;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.EditText;
+import android.widget.ImageButton;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+public class MealtimeSettingsActivity extends AppCompatActivity {
+ private static final int REQUEST_ADD = 0;
+ private static final int REQUEST_EDIT = 1;
+ private CookBookStorage cookbook;
+ private ArrayList categories = new ArrayList<>();
+ private HashMap views = new HashMap<>();
+ private LayoutInflater inflater;
+ private LinearLayout dishes;
+ private Intent task;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.mealtime_settings);
+
+ cookbook = CookBookStorage.getInstance(this);
+ inflater = (LayoutInflater) this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ dishes = (LinearLayout) findViewById(R.id.mealtime_settings_dishes);
+ task = getIntent();
+ DaySettings.MealtimeSettings settings = (DaySettings.MealtimeSettings)
+ task.getSerializableExtra("Settings");
+
+ if (settings != null) {
+ EditText name = (EditText) findViewById(R.id.mealtime_settings_name);
+ categories.addAll(settings.dishesCategories());
+
+ name.setText(settings.getName());
+ for (Integer categoryID : categories) {
+ dishes.addView(generateDishView(dishes, categoryID));
+ }
+ }
+
+ ImageButton addBtn = (ImageButton) findViewById(R.id.mealtime_settings_add);
+ addBtn.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ Intent intent = new Intent(MealtimeSettingsActivity.this, CookBookActivity.class);
+ intent.putExtra("Target", CookBookActivity.TARGET_CATEGORY);
+ startActivityForResult(intent, REQUEST_ADD);
+ }
+ });
+ }
+
+ public void onActivityResult(int requestCode, int errorCode, Intent resultContainer) {
+ switch (requestCode) {
+ case REQUEST_ADD :
+ if (errorCode == RESULT_OK) {
+ Integer result = resultContainer.getIntExtra("Result", -1);
+ categories.add(result);
+ dishes.addView(generateDishView(dishes, result));
+ }
+ break;
+ case REQUEST_EDIT :
+ if (errorCode == RESULT_OK) {
+ Integer result = resultContainer.getIntExtra("Result", -1);
+ Integer position = resultContainer.getIntExtra("Position", -1);
+ categories.set(position, result);
+ TextView categoryName = (TextView) views.get(position).findViewById(R.id.mealtime_settings_dish_name);
+ categoryName.setText(cookbook.getCategoryByID(result).getDescription());
+ }
+ break;
+ }
+ }
+
+ private View generateDishView(final LinearLayout parent, final Integer id) {
+ final View dish = inflater.inflate(R.layout.mealtime_settings_dish, null);
+ TextView dishName = (TextView) dish.findViewById(R.id.mealtime_settings_dish_name);
+ ImageButton editBtn = (ImageButton) dish.findViewById(R.id.mealtime_settings_dish_edit);
+ ImageButton delBtn = (ImageButton) dish.findViewById(R.id.mealtime_settings_dish_delete);
+ Category category = cookbook.getCategoryByID(id);
+ final Integer position = categories.indexOf(id); //TODO fix possible problems
+
+ dishName.setText(category.getDescription());
+ editBtn.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ Intent intent = new Intent(MealtimeSettingsActivity.this, CookBookActivity.class);
+ intent.putExtra("Target", CookBookActivity.TARGET_CATEGORY);
+ intent.putExtra("Position", position);
+ startActivityForResult(intent, REQUEST_EDIT);
+ }
+ });
+ delBtn.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ categories.remove(id);
+ parent.removeView(dish);
+ }
+ });
+ views.put(position, dish);
+ return dish;
+ }
+
+ public void onSaveClick(View v) {
+ EditText name = (EditText) findViewById(R.id.mealtime_settings_name);
+ returnResult(new DaySettings.MealtimeSettings(
+ name.getText().toString(),
+ categories));
+ }
+ private void returnResult(DaySettings.MealtimeSettings result) {
+ Intent resultIntent = task;
+ resultIntent.putExtra("Result", result);
+ setResult(RESULT_OK, resultIntent);
+ finish();
+ }
+}
diff --git a/app/src/main/java/ru/spbau/mit/foodmanager/Measure.java b/app/src/main/java/ru/spbau/mit/foodmanager/Measure.java
new file mode 100644
index 0000000..f276262
--- /dev/null
+++ b/app/src/main/java/ru/spbau/mit/foodmanager/Measure.java
@@ -0,0 +1,3 @@
+package ru.spbau.mit.foodmanager;
+
+public enum Measure {gr, ml, apiece, teaspoon, tablespoon, pinch, byTaste, cloves}
diff --git a/app/src/main/java/ru/spbau/mit/foodmanager/MenuSettings.java b/app/src/main/java/ru/spbau/mit/foodmanager/MenuSettings.java
new file mode 100644
index 0000000..7d5cb31
--- /dev/null
+++ b/app/src/main/java/ru/spbau/mit/foodmanager/MenuSettings.java
@@ -0,0 +1,99 @@
+package ru.spbau.mit.foodmanager;
+
+
+import android.content.Context;
+import android.util.Log;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+public class MenuSettings implements Serializable {
+ private HashMap settingsByDay;
+ private HashMap isCookingDay;
+ private static MenuSettings instance;
+
+ private MenuSettings(Context context) {
+ settingsByDay = new HashMap<>();
+ isCookingDay = new HashMap<>();
+ for (Day d : Day.values()) {
+ isCookingDay.put(d, true);
+ //DEBUG ONLY
+ ArrayList mealtieSettings = new ArrayList<>();
+ mealtieSettings.add(DaySettings.getMealtimePresets(context).get(0));
+ mealtieSettings.add(DaySettings.getMealtimePresets(context).get(1));
+ mealtieSettings.add(DaySettings.getMealtimePresets(context).get(2));
+ DaySettings settings = new DaySettings(mealtieSettings);
+ settingsByDay.put(d, settings);
+ //DEBUG ONLY
+ }
+ }
+
+ /**
+ * Сохраняет instance MenuSettings в БД.
+ * @return возвращает сериализованный instance настроек.
+ */
+ public static String saveMenuSettings(Context context) {
+ try {
+ ByteArrayOutputStream byteOutputStream = new ByteArrayOutputStream();
+ ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteOutputStream);
+ objectOutputStream.writeObject(instance);
+ objectOutputStream.flush();
+
+ String serializedInstance = objectOutputStream.toString();
+ CookBookStorage.getInstance(context).saveUserSettings(serializedInstance);
+ return serializedInstance;
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ return null;
+ }
+
+ /**
+ * Загрузка поля instance из БД.
+ */
+ public static void loadMenuSettings(Context context) {
+ String serializedInstance = CookBookStorage.getInstance(context).getUserSettings();
+ if (serializedInstance == null) {
+ instance = null;
+ return;
+ }
+ byte bytes[] = serializedInstance.getBytes();
+ ByteArrayInputStream byteInputStream = new ByteArrayInputStream(bytes);
+ try {
+ ObjectInputStream objectInputStream = new ObjectInputStream(byteInputStream);
+ instance = (MenuSettings) objectInputStream.readObject();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ public static MenuSettings getInstance(Context context) {
+ Log.d("MenuSettings", "getInstance");
+ loadMenuSettings(context);
+
+ if (instance == null) {
+ instance = new MenuSettings(context);
+ saveMenuSettings(context);
+ }
+
+ return instance;
+ }
+
+ public DaySettings getDaySettings(Day day) {
+ return settingsByDay.get(day);
+ }
+
+ public Boolean isCookingDay(Day day) {
+ return isCookingDay.get(day);
+ }
+
+ public void setCookingDay(Day day, Boolean isCookingDay, Context c) {
+ this.isCookingDay.put(day, isCookingDay);
+ }
+}
diff --git a/app/src/main/java/ru/spbau/mit/foodmanager/MenuSettingsActivity.java b/app/src/main/java/ru/spbau/mit/foodmanager/MenuSettingsActivity.java
new file mode 100644
index 0000000..db5a786
--- /dev/null
+++ b/app/src/main/java/ru/spbau/mit/foodmanager/MenuSettingsActivity.java
@@ -0,0 +1,152 @@
+package ru.spbau.mit.foodmanager;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.ImageButton;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+public class MenuSettingsActivity extends AppCompatActivity {
+ private static final int REQUEST_ADD = 0;
+ private static final int REQUEST_EDIT = 1;
+ private MenuSettings menuSettings;
+ private HashMap daySettingViews;
+ private HashMap> mealtimeSettings;
+ private HashMap> mealtimeSettingsViews;
+ private LayoutInflater inflater;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.menu_settings);
+ menuSettings = MenuSettings.getInstance(this);
+ LinearLayout daysSettings = (LinearLayout) findViewById(R.id.menu_settings_days);
+ inflater = (LayoutInflater) this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ daySettingViews = new HashMap<>();
+ for (Day d : Day.values()) {
+ View v = inflater.inflate(R.layout.menu_settings_day, null);
+ ((TextView)v.findViewById(R.id.menu_settings_day_name))
+ .setText(MenuStorage.getDayNames()[d.ordinal()]);
+ daySettingViews.put(d, v);
+ daysSettings.addView(v);
+ }
+ fillDays();
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ MenuSettings.saveMenuSettings(this);
+ }
+
+ public void onActivityResult(int requestCode, int errorCode, Intent resultContainer) {
+ if (errorCode == RESULT_OK) {
+ DaySettings.MealtimeSettings result;
+ Day d;
+ switch (requestCode) {
+ case REQUEST_ADD :
+ d = (Day) resultContainer.getSerializableExtra("Day");
+ result = (DaySettings.MealtimeSettings)
+ resultContainer.getSerializableExtra("Result");
+ LinearLayout mealtimes = (LinearLayout)
+ daySettingViews.get(d).findViewById(R.id.menu_settings_day_mealtimes);
+ DaySettings.MealtimeSettings settings = new DaySettings.MealtimeSettings(
+ result.getName(), result.dishesCategories());
+ mealtimeSettings.get(d).add(settings);
+ mealtimeSettingsViews.get(d).put(settings,
+ generateMealtimeView(d, mealtimes, settings));
+ break;
+ case REQUEST_EDIT :
+ d = (Day) resultContainer.getSerializableExtra("Day");
+ result = (DaySettings.MealtimeSettings)
+ resultContainer.getSerializableExtra("Result");
+ Integer position = resultContainer.getIntExtra("Position", -1);
+ TextView mealtimeName = (TextView)
+ mealtimeSettingsViews.get(d).get(mealtimeSettings.get(d).get(position))
+ .findViewById(R.id.menu_settings_day_mealtime_name);
+ mealtimeName.setText(result.getName());
+ mealtimeSettings.get(d).get(position).clone(result);
+ break;
+ }
+ }
+ }
+
+ private void fillDays() {
+ mealtimeSettingsViews = new HashMap<>();
+ mealtimeSettings = new HashMap<>();
+ for (final Day d : Day.values()) {
+ View v = daySettingViews.get(d);
+ CheckBox isCookingDay = (CheckBox) v.findViewById(R.id.menu_settings_day_is_cookingday);
+ ImageButton addMealtime = (ImageButton) v.findViewById(R.id.menu_settings_day_mealtime_add);
+ LinearLayout mealtimes = (LinearLayout) v.findViewById(R.id.menu_settings_day_mealtimes);
+
+ mealtimeSettingsViews.put(d, new HashMap());
+ isCookingDay.setChecked(menuSettings.isCookingDay(d));
+ mealtimeSettings.put(d, menuSettings.getDaySettings(d).getMealtimeSettings());
+ for (DaySettings.MealtimeSettings settings : mealtimeSettings.get(d)) {
+ mealtimeSettingsViews.get(d).put(settings,
+ generateMealtimeView(d, mealtimes, settings));
+ }
+
+ isCookingDay.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
+ menuSettings.setCookingDay(d, b, MenuSettingsActivity.this);
+ }
+ });
+
+ addMealtime.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ Intent intent = new Intent(MenuSettingsActivity.this,
+ ChooseMealtimePresetActivity.class);
+ intent.putExtra("Day", d);
+ startActivityForResult(intent, REQUEST_ADD);
+ }
+ });
+ }
+ }
+
+ private View generateMealtimeView (final Day d, final LinearLayout mealtimes,
+ final DaySettings.MealtimeSettings settings) {
+ final View mealtimeView = inflater.inflate(R.layout.menu_settings_day_mealtime, null);
+ ImageButton editMealtime = (ImageButton)
+ mealtimeView.findViewById(R.id.menu_settings_day_mealtime_edit);
+ ImageButton deleteMealtime = (ImageButton)
+ mealtimeView.findViewById(R.id.menu_settings_day_mealtime_delete);
+ TextView mealtimeName = (TextView)
+ mealtimeView.findViewById(R.id.menu_settings_day_mealtime_name);
+ final Integer position = mealtimeSettings.get(d).indexOf(settings);
+
+ mealtimeName.setText(settings.getName());
+ deleteMealtime.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ menuSettings.getDaySettings(d).getMealtimeSettings().remove(settings);
+ mealtimes.removeView(mealtimeView);
+ }
+ });
+ editMealtime.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ Intent intent = new Intent(MenuSettingsActivity.this,
+ ChooseMealtimePresetActivity.class);
+ intent.putExtra("Day", d);
+ intent.putExtra("Position", position);
+ startActivityForResult(intent, REQUEST_EDIT);
+ }
+ });
+
+ mealtimes.addView(mealtimeView);
+ return mealtimeView;
+ }
+}
diff --git a/app/src/main/java/ru/spbau/mit/foodmanager/MenuStorage.java b/app/src/main/java/ru/spbau/mit/foodmanager/MenuStorage.java
new file mode 100644
index 0000000..b8e2792
--- /dev/null
+++ b/app/src/main/java/ru/spbau/mit/foodmanager/MenuStorage.java
@@ -0,0 +1,157 @@
+package ru.spbau.mit.foodmanager;
+
+import android.content.Context;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+/**
+ * Хранит меню для каждого дня недели
+ */
+public class MenuStorage implements Serializable {
+ private HashMap dayMenus;
+ private static MenuStorage instance;
+ private static final String menuStorageFilename = "MenuStorage";
+ static final private String[] DAY_NAMES = {
+ "Понедельник",
+ "Вторник",
+ "Среда",
+ "Четверг",
+ "Пятница",
+ "Суббота",
+ "Воскресенье"};
+
+ private MenuStorage() {
+ dayMenus = new HashMap<>();
+ }
+
+ static public String[] getDayNames() {
+ return DAY_NAMES;
+ }
+
+ static public MenuStorage getInstance(Context context) {
+ if (instance == null) {
+ loadMenuStorage(context);
+ }
+ if (instance == null) {
+ instance = new MenuStorage();
+ saveMenuStorage(context);
+ }
+ return instance;
+ }
+
+ /**
+ * Возвращает HashMap, где ключ - день, значение - DayMenu
+ */
+ public HashMap getMenu() {
+ return dayMenus;
+ }
+
+ /**
+ * Записывает новое меню на определенный день
+ */
+ public void setNewDayMenu(Day day, DayMenu dayMenu) {
+ if (day != null) {
+ dayMenus.put(day,dayMenu);
+ }
+ }
+
+ public static void saveMenuStorage(Context context) {
+ try {
+ FileOutputStream output = context.openFileOutput(
+ menuStorageFilename, Context.MODE_PRIVATE);
+
+ ObjectOutputStream outputStream = new ObjectOutputStream(output);
+ outputStream.writeObject(instance);
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ public static void loadMenuStorage(Context context) {
+ File settings = new File(context.getFilesDir(), menuStorageFilename);
+ if (settings.exists()) {
+ try {
+ FileInputStream input = context.openFileInput(menuStorageFilename);
+ ObjectInputStream inputStream = new ObjectInputStream(input);
+ instance = (MenuStorage) inputStream.readObject();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ /**
+ * Генерирует меню на один день, учитывая настройки и имеющиеся рецепты
+ * @param settings настройки меню на этот день
+ * @param recipes Map из CategoryID в RecipeID. Если для какой-то категории есть рецепт,
+ * то будет использован именно он
+ */
+ public DayMenu generateDayMenu(DaySettings settings, HashMap recipes, Context context) {
+ ArrayList dishesForDay = new ArrayList<>();
+ for (DaySettings.MealtimeSettings mealtimeSettings : settings.getMealtimeSettings()) {
+ ArrayList mealtimeRecipes = new ArrayList<>();
+ for (Integer categoryID : mealtimeSettings.dishesCategories()) {
+ if (recipes.get(categoryID) == null) {
+ CookBookStorage cookbook = CookBookStorage.getInstance(context);
+ recipes.put(categoryID, cookbook.chooseRandomDishFromCategory(categoryID).getID());
+ }
+ mealtimeRecipes.add(recipes.get(categoryID));
+ }
+ dishesForDay.add(new Mealtime(mealtimeSettings.getName(), mealtimeRecipes));
+ }
+ return new DayMenu(dishesForDay);
+ }
+
+ /**
+ * Генерирует меню на неделю
+ */
+ public void generateWeekMenu(Context context) {
+ MenuSettings settings = MenuSettings.getInstance(context);
+ Day firstCookingDay = Day.Monday;
+ for (Day day : Day.values()) {
+ if (settings.isCookingDay(day)) {
+ firstCookingDay = day;
+ break;
+ }
+ }
+ if (!settings.isCookingDay(firstCookingDay)) {
+ //TODO обработать случай, когда нет дней готовки.
+ }
+ //Получаем для каждого дня готовки список дней, на которые готовим.
+ HashMap> cookForDays = new HashMap<>();
+ Day currentCookingDay = firstCookingDay;
+ for (Integer day = firstCookingDay.ordinal(); day < Day.values().length; day++) {
+ if (settings.isCookingDay(Day.values()[day])) {
+ currentCookingDay = Day.values()[day];
+ cookForDays.put(currentCookingDay, new ArrayList());
+ }
+ cookForDays.get(currentCookingDay).add(Day.values()[day]);
+ }
+ for (Integer day = 0; day < firstCookingDay.ordinal(); day++) {
+ cookForDays.get(currentCookingDay).add(Day.values()[day]);
+ }
+ //Получаем рецепты для каждого дня готовки
+ for (Day cookingDay : cookForDays.keySet()) {
+ //TODO Использовать множество категорий
+ //Создаем меню на каждый день
+ HashMap categoryRecipes = new HashMap<>();
+ for (Day day : cookForDays.get(cookingDay)) {
+ DaySettings daySettings = settings.getDaySettings(day);
+ dayMenus.put(day, null);
+ if (daySettings != null) {
+ DayMenu dayMenu = generateDayMenu(daySettings, categoryRecipes, context);
+ dayMenus.put(day, dayMenu);
+ }
+ }
+ }
+ saveMenuStorage(context);
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/ru/spbau/mit/foodmanager/MenuViewActivity.java b/app/src/main/java/ru/spbau/mit/foodmanager/MenuViewActivity.java
new file mode 100644
index 0000000..31da776
--- /dev/null
+++ b/app/src/main/java/ru/spbau/mit/foodmanager/MenuViewActivity.java
@@ -0,0 +1,215 @@
+package ru.spbau.mit.foodmanager;
+
+import android.content.Context;
+import android.content.Intent;
+import android.support.v7.app.AppCompatActivity;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.ImageButton;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import java.util.HashMap;
+
+public class MenuViewActivity extends AppCompatActivity {
+ private final static int REQUEST_ADD = 0;
+ private final static int REQUEST_EDIT = 1;
+ private HashMap allDayMenu;
+ private HashMap recipes;
+ private CookBookStorage cookbook;
+ private MenuStorage menu;
+ private LayoutInflater inflater;
+ private GifImageView loaderAnimation;
+ private LinearLayout informationLayout;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.menu_view);
+ //Init loaderAnimation
+ loaderAnimation = (GifImageView) findViewById(R.id.loader_animation_view);
+ loaderAnimation.setGifImageResource(LoaderAnimationSelector.getRandomLoaderResource());
+ loaderAnimation.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
+ informationLayout = (LinearLayout) findViewById(R.id.information_layout);
+
+ inflater = (LayoutInflater) this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ cookbook = CookBookStorage.getInstance(this);
+ menu = MenuStorage.getInstance(this);
+ recipes = new HashMap<>();
+ //Init Task
+ ContentLoader contentLoader = new ContentLoader(false);
+ Thread loader = new Thread(contentLoader);
+ loader.start();
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ MenuStorage.saveMenuStorage(this);
+ }
+
+ public void onGenerateBtnClick(View v) {
+ //Init Task
+ ContentLoader contentLoader = new ContentLoader(true);
+ Thread loader = new Thread(contentLoader);
+ loader.start();
+ }
+
+ public void onActivityResult(int requestCode, int errorCode, Intent resultContainer) {
+ if (errorCode == RESULT_OK) {
+ Day day = (Day) resultContainer.getSerializableExtra("Day");
+ Integer mealtimePos = resultContainer.getIntExtra("MealtimePosition", -1);
+ Integer result = resultContainer.getIntExtra("Result", -1);
+ switch (requestCode) {
+ case REQUEST_ADD :
+ allDayMenu.get(day).getMealtimes().get(mealtimePos).getRecipeIDs().add(result);
+ break;
+ case REQUEST_EDIT :
+ Integer position = resultContainer.getIntExtra("Position", -1);
+ allDayMenu.get(day).getMealtimes().get(mealtimePos).getRecipeIDs().set(position, result);
+ break;
+ }
+ ContentLoader contentLoader = new ContentLoader(false);
+ Thread loader = new Thread(contentLoader);
+ loader.start();
+ }
+ }
+
+ private void showRecipes() {
+ LinearLayout daysListView = (LinearLayout) findViewById(R.id.menu_view_days);
+ daysListView.removeAllViews();
+ for (Day d : Day.values()) {
+ generateDayMenuView(d, daysListView);
+ }
+ }
+
+ private void generateDayMenuView(Day day, LinearLayout parent) {
+ View view = inflater.inflate(R.layout.menu_view_list_element, null);
+ DayMenu menu = allDayMenu.get(day);
+ TextView dayName = (TextView) view.findViewById(R.id.menu_day_name);
+ LinearLayout dayMealtimes = (LinearLayout) view.findViewById(R.id.menu_day_mealtimes);
+
+ if (menu != null) {
+ dayName.setText(MenuStorage.getDayNames()[day.ordinal()]);
+ for (Integer i = 0; i < menu.getMealtimes().size(); i++) {
+ generateMealtimeView(menu, i, dayMealtimes, day);
+ }
+ parent.addView(view);
+ }
+ }
+
+ private void generateMealtimeView(DayMenu menu, final Integer position, LinearLayout parent,
+ final Day day) {
+ LinearLayout mealtimeElement = (LinearLayout) inflater.inflate(
+ R.layout.menu_view_day_menu_list_element, null);
+ TextView mealtimeName = (TextView) mealtimeElement.findViewById(
+ R.id.menu_day_mealtime_name);
+ LinearLayout recipeList = (LinearLayout) mealtimeElement.findViewById(
+ R.id.menu_day_mealtime_dishes);
+ Mealtime mealtime = menu.getMealtimes().get(position);
+ ImageButton addDish = (ImageButton) mealtimeElement.findViewById(R.id.menu_day_mealtime_dish_add);
+
+
+ mealtimeName.setText(mealtime.getName());
+ for (Integer i = 0; i < mealtime.getRecipeIDs().size(); i++) {
+ generateRecipeView(mealtime, i, recipeList, day, position);
+ }
+ addDish.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ Intent task = new Intent(MenuViewActivity.this, CookBookActivity.class);
+ task.putExtra("Target", CookBookActivity.TARGET_RECIPE);
+ task.putExtra("Day", day);
+ task.putExtra("MealtimePosition", position);
+ startActivityForResult(task, REQUEST_ADD);
+ }
+ });
+ parent.addView(mealtimeElement);
+ }
+
+ private void generateRecipeView(final Mealtime mealtime, final Integer position, LinearLayout parent,
+ final Day day, final Integer mealtimePos) {
+ View recipeView = inflater.inflate(R.layout.menu_view_day_menu_meltime_list_element, null);
+ TextView recipeName = (TextView) recipeView.findViewById(R.id.menu_day_mealtime_dish_name);
+ final Integer recipeID = mealtime.getRecipeIDs().get(position); //WTF
+ ImageButton editDish = (ImageButton) recipeView.findViewById(R.id.menu_day_mealtime_dish_edit);
+ ImageButton deleteDish = (ImageButton) recipeView.findViewById(R.id.menu_day_mealtime_dish_delete);
+ Log.d("LOLOLOLOL", recipeID.toString() + " " + recipes.containsKey(recipeID));
+ recipeName.setText(recipes.get(recipeID).getName());
+ recipeName.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ Intent intent = new Intent(MenuViewActivity.this, RecipeViewActivity.class);
+ intent.putExtra("Recipe", recipeID);
+ startActivity(intent);
+ }
+ });
+ deleteDish.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ allDayMenu.get(day).getMealtimes().get(mealtimePos).getRecipeIDs().remove((int)position);
+ Log.d("Try to remove", "Try tot remove");
+ showRecipes(); //We need to do this because positions in other buttons now broken
+ }
+ });
+ editDish.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ Intent task = new Intent(MenuViewActivity.this, CookBookActivity.class);
+ task.putExtra("Target", CookBookActivity.TARGET_RECIPE);
+ task.putExtra("Day", day);
+ task.putExtra("MealtimePosition", mealtimePos);
+ task.putExtra("Position", position);
+ startActivityForResult(task, REQUEST_EDIT);
+ }
+ });
+ parent.addView(recipeView);
+ }
+
+ public class ContentLoader implements Runnable {
+ private boolean generateNewMenu;
+
+ public ContentLoader(boolean generate) {
+ generateNewMenu = generate;
+ }
+
+ @Override
+ public void run() {
+ MenuViewActivity.this.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ loaderAnimation.setVisibility(View.VISIBLE);
+ informationLayout.setVisibility(View.INVISIBLE);
+ }
+ });
+ if (generateNewMenu) {
+ menu.generateWeekMenu(MenuViewActivity.this);
+ }
+ allDayMenu = menu.getMenu();
+ for (DayMenu dm : allDayMenu.values()) {
+ if (dm != null) {
+ for (Mealtime m : dm.getMealtimes()) {
+ for (Integer id : m.getRecipeIDs()) {
+ Log.d("ADDT", id.toString());
+ while (recipes.get(id) == null) {
+ recipes.put(id, cookbook.getRecipe(id));
+ }
+ }
+ }
+ }
+ }
+
+ MenuViewActivity.this.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ showRecipes();
+ loaderAnimation.setVisibility(View.INVISIBLE);
+ informationLayout.setVisibility(View.VISIBLE);
+ loaderAnimation.setGifImageResource(LoaderAnimationSelector.getRandomLoaderResource());
+ }
+ });
+ }
+ }
+}
diff --git a/app/src/main/java/ru/spbau/mit/foodmanager/NotificationService.java b/app/src/main/java/ru/spbau/mit/foodmanager/NotificationService.java
new file mode 100644
index 0000000..1aef35c
--- /dev/null
+++ b/app/src/main/java/ru/spbau/mit/foodmanager/NotificationService.java
@@ -0,0 +1,125 @@
+package ru.spbau.mit.foodmanager;
+
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.content.Intent;
+import android.icu.util.Calendar;
+import android.os.IBinder;
+import android.support.v4.app.NotificationCompat;
+import android.util.Log;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Сервис нотифицирования пользователя
+ */
+public class NotificationService extends Service {
+ private static final int COOK_NOTIFICATION_ID = 0;
+ private static final String COOK_NOTIFICATION_TITLE = "Время начинать готовить";
+ private static final String COOK_NOTIFICATION_TEXT = "Сейчас самое время приготовить что-нибудь вкусное!";
+ private NotificationManager notificationManager;
+ private static Day lastCookDayNotify;
+ private static Boolean notifyedToday;
+ private static Boolean serviceStopped;
+ private Calendar calendar = Calendar.getInstance();
+ private MenuSettings menuSettings;
+ private NotificationSettings notificationSettings;
+ private Thread serviceActionThread;
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ serviceStopped = false;
+ notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
+ notificationSettings = NotificationSettings.getInstance(this);
+ menuSettings = MenuSettings.getInstance(this);
+ serviceActionThread = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ while (!serviceStopped) {
+ try {
+ if (calendarDayToDay(calendar.get(Calendar.DAY_OF_WEEK)) != lastCookDayNotify) {
+ notifyedToday = false;
+ }
+ if (notificationSettings.getShowCookNotifications() && !notifyedToday) {
+ sendCookNotification();
+ }
+ TimeUnit.SECONDS.sleep(30);
+ } catch (Exception e) {
+ //Finish thread
+ }
+ }
+ }
+ });
+ serviceActionThread.start();
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ if (serviceActionThread.isInterrupted()) {
+ serviceActionThread.start();
+ }
+ return START_STICKY;
+ }
+
+ /**
+ * Проверяет, нужно ли послать оповещение о времени готовить или нет
+ */
+ private void sendCookNotification() {
+ Log.d("NOTIFICATIONS", "Checkpoint0");
+ long timeOfDay = System.currentTimeMillis() % calendar.get(Calendar.MILLISECONDS_IN_DAY);
+ long notificationTimeBegin = notificationSettings.getTimeOfDayBeginCookNotifications();
+ long notificationTimeEnd = notificationSettings.getTimeOfDayEndCookNotifications();
+ Day day = calendarDayToDay(calendar.get(Calendar.DAY_OF_WEEK));
+ Log.d("NOTIFICATIONS", "Checkpoint1");
+ if (timeOfDay > notificationTimeBegin &&
+ timeOfDay < notificationTimeEnd &&
+ menuSettings.isCookingDay(day)) {
+ Intent intent = new Intent(this, MainActivity.class);
+ PendingIntent pendingIntent = PendingIntent.getActivity(this, COOK_NOTIFICATION_ID, intent, 0);
+ NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this);
+ notificationBuilder.setSmallIcon(R.mipmap.ic_launcher);
+ notificationBuilder.setContentTitle(COOK_NOTIFICATION_TITLE);
+ notificationBuilder.setContentText(COOK_NOTIFICATION_TEXT);
+ notificationBuilder.setContentIntent(pendingIntent);
+ notificationManager.notify(COOK_NOTIFICATION_ID, notificationBuilder.build());
+ lastCookDayNotify = day; //TODO делать нормальную проверку, т.к. если только один день готовки, то все плохо.
+ notifyedToday = true;
+ }
+ Log.d("NOTIFICATIONS", "Checkpoint2");
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ serviceStopped = true;
+ serviceActionThread.interrupt();
+ }
+
+ private Day calendarDayToDay (int calendarDay) {
+ switch (calendarDay) {
+ case Calendar.MONDAY :
+ return Day.Monday;
+ case Calendar.TUESDAY:
+ return Day.Tuesday;
+ case Calendar.WEDNESDAY :
+ return Day.Wednesday;
+ case Calendar.THURSDAY :
+ return Day.Thursday;
+ case Calendar.FRIDAY :
+ return Day.Friday;
+ case Calendar.SATURDAY :
+ return Day.Saturday;
+ case Calendar.SUNDAY :
+ return Day.Sunday;
+ }
+ return null;
+ }
+
+}
diff --git a/app/src/main/java/ru/spbau/mit/foodmanager/NotificationSettings.java b/app/src/main/java/ru/spbau/mit/foodmanager/NotificationSettings.java
new file mode 100644
index 0000000..1b33773
--- /dev/null
+++ b/app/src/main/java/ru/spbau/mit/foodmanager/NotificationSettings.java
@@ -0,0 +1,121 @@
+package ru.spbau.mit.foodmanager;
+
+import android.content.Context;
+import android.icu.util.Calendar;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.util.concurrent.TimeUnit;
+/**
+ * Настройки сервера нотификации
+ */
+
+public class NotificationSettings implements Serializable {
+ private Boolean showCookNotifications;
+ private long timeOfDayBeginCookNotifications;
+ private long timeOfDayEndCookNotifications;
+ private static NotificationSettings instance;
+ private static final String notificationSettingsFilename = "NotificationSettings";
+
+ static public NotificationSettings getInstance(Context context) {
+ loadNotificationSettings(context);
+
+ if (instance == null) {
+ instance = new NotificationSettings();
+ saveNotificationSettings(context);
+ }
+
+ return instance;
+ }
+
+ public static void saveNotificationSettings(Context context) {
+ try {
+ FileOutputStream output = context.openFileOutput(
+ notificationSettingsFilename, Context.MODE_PRIVATE);
+
+ ObjectOutputStream outputStream = new ObjectOutputStream(output);
+ outputStream.writeObject(instance);
+
+ } catch (Throwable e) {
+ e.printStackTrace();
+ }
+ }
+
+ public static void loadNotificationSettings(Context context) {
+ File settings = new File(context.getFilesDir(), notificationSettingsFilename);
+ if (settings.exists()) {
+ try {
+ FileInputStream input = context.openFileInput(notificationSettingsFilename);
+ ObjectInputStream inputStream = new ObjectInputStream(input);
+ instance = (NotificationSettings) inputStream.readObject();
+ } catch (Throwable e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ private NotificationSettings() {
+ showCookNotifications = false;
+ timeOfDayBeginCookNotifications = 0;
+ timeOfDayEndCookNotifications = Calendar.getInstance().get(Calendar.MILLISECONDS_IN_DAY);
+ }
+
+ public void setShowCookNotifications(Boolean b, Context c) {
+ showCookNotifications = b;
+ saveNotificationSettings(c);
+ }
+
+ public void setTimeOfDayBeginCookNotifications(long timeOfDay, Context c) {
+ timeOfDayBeginCookNotifications = timeOfDay % Calendar.MILLISECONDS_IN_DAY;
+ saveNotificationSettings(c);
+ }
+
+ public void setTimeOfDayBeginCookNotifications(long hour, long minute, Context c) {
+ hour = hour % 24;
+ minute = minute % 60;
+ timeOfDayBeginCookNotifications = TimeUnit.HOURS.toMillis(hour) + TimeUnit.MINUTES.toMillis(minute);
+ saveNotificationSettings(c);
+ }
+
+ public void setTimeOfDayEndCookNotifications(long timeOfDay, Context c) {
+ timeOfDayEndCookNotifications = timeOfDay % Calendar.MILLISECONDS_IN_DAY;
+ saveNotificationSettings(c);
+ }
+ public void setTimeOfDayEndCookNotifications(long hour, long minute, Context c) {
+ hour = hour % 24;
+ minute = minute % 60;
+ timeOfDayEndCookNotifications = TimeUnit.HOURS.toMillis(hour) + TimeUnit.MINUTES.toMillis(minute);
+ saveNotificationSettings(c);
+ }
+ public Boolean getShowCookNotifications() {
+ return showCookNotifications;
+ }
+
+ public long getTimeOfDayBeginCookNotifications() {
+ return timeOfDayBeginCookNotifications;
+ }
+
+ public long getTimeOfDayEndCookNotifications() {
+ return timeOfDayEndCookNotifications;
+ }
+
+ public long getHourOfDayBeginCookNotifications() {
+ return TimeUnit.MILLISECONDS.toHours(timeOfDayBeginCookNotifications);
+ }
+
+ public long getHourOfDayEndCookNotifications() {
+ return TimeUnit.MILLISECONDS.toHours(timeOfDayEndCookNotifications);
+ }
+
+ public long getMinuteOfDayBeginCookNotifications() {
+ return TimeUnit.MILLISECONDS.toMinutes(timeOfDayBeginCookNotifications) % 60;
+ }
+
+ public long getMinuteOfDayEndCookNotifications() {
+ return TimeUnit.MILLISECONDS.toMinutes(timeOfDayEndCookNotifications) % 60;
+ }
+}
diff --git a/app/src/main/java/ru/spbau/mit/foodmanager/NotificationSettingsActivity.java b/app/src/main/java/ru/spbau/mit/foodmanager/NotificationSettingsActivity.java
new file mode 100644
index 0000000..ad8d03d
--- /dev/null
+++ b/app/src/main/java/ru/spbau/mit/foodmanager/NotificationSettingsActivity.java
@@ -0,0 +1,95 @@
+package ru.spbau.mit.foodmanager;
+
+import android.app.TimePickerDialog;
+import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+import android.view.View;
+import android.widget.CompoundButton;
+import android.widget.Switch;
+import android.widget.TextView;
+import android.widget.TimePicker;
+
+public class NotificationSettingsActivity extends AppCompatActivity {
+ private NotificationSettings settings;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.notification_settings);
+
+ settings = NotificationSettings.getInstance(this);
+ Switch switchCookNotify = (Switch) findViewById(R.id.notification_settings_toggle_cook_notify);
+ showTimes();
+ switchCookNotify.setChecked(settings.getShowCookNotifications());
+ switchCookNotify.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
+ settings.setShowCookNotifications(b, NotificationSettingsActivity.this);
+ }
+ });
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ NotificationSettings.saveNotificationSettings(this);
+ }
+
+ private void showTimes() {
+ TextView cookNotifyFromHours = (TextView) findViewById(R.id.notification_settings_time_cook_notify_from_hours);
+ TextView cookNotifyFromMinutes = (TextView) findViewById(R.id.notification_settings_time_cook_notify_from_minutes);
+ TextView cookNotifyToHours = (TextView) findViewById(R.id.notification_settings_time_cook_notify_to_hours);
+ TextView cookNotifyToMinutes = (TextView) findViewById(R.id.notification_settings_time_cook_notify_to_minutes);
+ String s;
+ s = ((Long)settings.getHourOfDayBeginCookNotifications()).toString();
+ if (s.length() < 2) {
+ s = "0".concat(s);
+ }
+ cookNotifyFromHours.setText(s);
+ s = ((Long)settings.getHourOfDayEndCookNotifications()).toString();
+ if (s.length() < 2) {
+ s = "0".concat(s);
+ }
+ cookNotifyToHours.setText(s);
+ s = ((Long)settings.getMinuteOfDayBeginCookNotifications()).toString();
+ if (s.length() < 2) {
+ s = "0".concat(s);
+ }
+ cookNotifyFromMinutes.setText(s);
+ s = ((Long)settings.getMinuteOfDayEndCookNotifications()).toString();
+ if (s.length() < 2) {
+ s = "0".concat(s);
+ }
+ cookNotifyToMinutes.setText(s);
+ }
+
+ public void onCookNotifyTimeFromEditClick(View v) {
+ //final TextView cookNotifyFromHours = (TextView) findViewById(R.id.notification_settings_time_cook_notify_from_hours);
+ //final TextView cookNotifyFromMinutes = (TextView) findViewById(R.id.notification_settings_time_cook_notify_from_minutes);
+ TimePickerDialog dialog = new TimePickerDialog(this, new TimePickerDialog.OnTimeSetListener() {
+ @Override
+ public void onTimeSet(TimePicker timePicker, int hour, int minute) {
+ settings.setTimeOfDayBeginCookNotifications(hour, minute, NotificationSettingsActivity.this);
+ showTimes();
+ }
+ },
+ (int)settings.getHourOfDayBeginCookNotifications(),
+ (int)settings.getMinuteOfDayBeginCookNotifications(), true);
+ dialog.show();
+ }
+
+ public void onCookNotifyTimeToEditClick(View v) {
+ //final TextView cookNotifyToHours = (TextView) findViewById(R.id.notification_settings_time_cook_notify_to_hours);
+ //final TextView cookNotifyToMinutes = (TextView) findViewById(R.id.notification_settings_time_cook_notify_to_minutes);
+ TimePickerDialog dialog = new TimePickerDialog(this, new TimePickerDialog.OnTimeSetListener() {
+ @Override
+ public void onTimeSet(TimePicker timePicker, int hour, int minute) {
+ settings.setTimeOfDayEndCookNotifications(hour, minute, NotificationSettingsActivity.this);
+ showTimes();
+ }
+ },
+ (int)settings.getHourOfDayEndCookNotifications(),
+ (int)settings.getMinuteOfDayEndCookNotifications(), true);
+ dialog.show();
+ }
+}
diff --git a/app/src/main/java/ru/spbau/mit/foodmanager/Recipe.java b/app/src/main/java/ru/spbau/mit/foodmanager/Recipe.java
new file mode 100644
index 0000000..7943dba
--- /dev/null
+++ b/app/src/main/java/ru/spbau/mit/foodmanager/Recipe.java
@@ -0,0 +1,44 @@
+package ru.spbau.mit.foodmanager;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+
+public class Recipe implements Serializable {
+ /** Название рецепта */
+ private String name;
+
+ /** Описание рецепта */
+ private String description;
+
+ /** ID рецепта */
+ private int ID;
+
+ /** Содержит идентификаторы категорий, к которым принадлежит блюдо */
+ private ArrayList categoryID;
+
+ public Recipe(int ID, String description, String name) {
+ this.ID = ID;
+ this.description = description;
+ this.name = name;
+ }
+
+ public int getID() {
+ return ID;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setCategoryID(ArrayList categoryID) {
+ this.categoryID = categoryID;
+ }
+
+ public ArrayList getCategoryID() {
+ return categoryID;
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/ru/spbau/mit/foodmanager/RecipeToChange.java b/app/src/main/java/ru/spbau/mit/foodmanager/RecipeToChange.java
new file mode 100644
index 0000000..ca85c2c
--- /dev/null
+++ b/app/src/main/java/ru/spbau/mit/foodmanager/RecipeToChange.java
@@ -0,0 +1,79 @@
+package ru.spbau.mit.foodmanager;
+
+import java.util.ArrayList;
+
+/**
+ * Класс, созданный специально для добавления и удаления рецепта в БД.
+ */
+public class RecipeToChange {
+ /** Название рецепта */
+ private String name;
+
+ /** Описание рецепта */
+ private String description;
+
+ /** ID добавляемого рецепта */
+ private int ID;
+
+ /** Содержит идентификаторы категорий, к которым принадлежит блюдо */
+ private ArrayList categoryIDs;
+
+ /** Пошаговая инструкция готовки блюда */
+ private ArrayList steps;
+
+ /** Ингредиенты необходимые для приготовления блюда */
+ private ArrayList ingredients;
+
+ public RecipeToChange(int ID, String description, String name) {
+ categoryIDs = null;
+ steps = null;
+ ingredients = null;
+ this.ID = ID;
+ this.description = description;
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) { this.name = name; }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) { this.description = description; }
+
+ public int getID() {
+ return ID;
+ }
+
+ public void setID(int ID) {
+ this.ID = ID;
+ }
+
+ public void setIngredients(ArrayList ingredients) {
+ this.ingredients = ingredients;
+ }
+
+ public ArrayList getIngredients() {
+ return ingredients;
+ }
+
+ public void setCategoryIDs(ArrayList categoryIDs) {
+ this.categoryIDs = categoryIDs;
+ }
+
+ public ArrayList getCategoryIDs() {
+ return categoryIDs;
+ }
+
+ public void setSteps(ArrayList steps) {
+ this.steps = steps;
+ }
+
+ public ArrayList getSteps() {
+ return steps;
+ }
+}
diff --git a/app/src/main/java/ru/spbau/mit/foodmanager/RecipeViewActivity.java b/app/src/main/java/ru/spbau/mit/foodmanager/RecipeViewActivity.java
new file mode 100644
index 0000000..719e501
--- /dev/null
+++ b/app/src/main/java/ru/spbau/mit/foodmanager/RecipeViewActivity.java
@@ -0,0 +1,276 @@
+package ru.spbau.mit.foodmanager;
+
+import android.content.Intent;
+import android.graphics.BitmapFactory;
+import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+import android.util.TypedValue;
+import android.view.View;
+import android.widget.Button;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+public class RecipeViewActivity extends AppCompatActivity {
+ private static final String INGRIDIENT_LIST_DIVIDER = " - ";
+ private static final String INGRIDIENT_LIST_COUNT_DIVIDER = " ";
+ private static final int IMAGES_WIDTH = 300; //dp
+ private static final int IMAGES_HEIGHT = 200; //dp
+ private static final int CATEGORY_NAME_PADDING = 12; //dp
+ private Recipe recipe;
+ private ArrayList steps;
+ private ArrayList categories;
+ private ArrayList ingredients;
+ private HashMap categoryDescriptions;
+ private GifImageView loaderAnimation;
+ private LinearLayout informationLayout;
+ private ImageButton likeBtn;
+ private ImageButton favoriteBtn;
+ private ImageButton deleteBtn;
+ private ImageButton editBtn;
+ private TextView likeCounter;
+ private int likeCount;
+ private boolean liked;
+ private boolean inFavourites;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.recipe_view);
+
+ //Init loaderAnimation
+ loaderAnimation = (GifImageView) findViewById(R.id.loader_animation_view);
+ loaderAnimation.setGifImageResource(LoaderAnimationSelector.getRandomLoaderResource());
+ loaderAnimation.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
+ informationLayout = (LinearLayout) findViewById(R.id.information_layout);
+ //Init like & selected btn
+ likeBtn = (ImageButton) findViewById(R.id.recipe_like);
+ likeCounter = (TextView) findViewById(R.id.recipe_like_count);
+ favoriteBtn = (ImageButton) findViewById(R.id.recipe_add_to_favorites);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ Intent intent = getIntent();
+ ContentLoader contentLoader = new ContentLoader(intent.getIntExtra("Recipe", -1));
+ Thread loader = new Thread(contentLoader);
+ loader.start();
+ }
+
+ public void onCookClick(View v) {
+ Intent intent = new Intent(this, StepViewActivity.class);
+ intent.putExtra("Recipe", recipe.getID());
+ startActivity(intent);
+ }
+
+ public void onEditClick(View v) {
+ Intent intent = new Intent(this, EditRecipeActivity.class);
+ intent.putExtra("RecipeID", recipe.getID());
+ startActivity(intent);
+ }
+
+ public void onDeleteClick(View v) {
+ boolean complete = false;
+ while(!complete) {
+ try {
+ CookBookStorage.getInstance(this).deleteRecipe(new RecipeToChange(recipe.getID(), "", ""));
+ complete = true;
+ }
+ catch (Exception e) {
+ //Repeat
+ }
+ }
+ finish();
+ }
+
+ public void onLikeClick(View v) {
+ if (liked) {
+ likeBtn.setImageBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.like_off));
+ CookBookStorage.getInstance(this).setNotLike(recipe.getID());
+ } else {
+ likeBtn.setImageBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.like_on));
+ CookBookStorage.getInstance(this).setLike(recipe.getID());
+ }
+ liked = !liked;
+ likeCounter.setText(String.valueOf(CookBookStorage.getInstance(this).getRecipeLikes(recipe)));
+ }
+
+ public void onFavoriteClick(View v) {
+ if (inFavourites) {
+ favoriteBtn.setImageBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.favourite_off));
+ CookBookStorage.getInstance(this).removeFromFavorites(recipe.getID());
+ } else {
+ favoriteBtn.setImageBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.favourite_on));
+ CookBookStorage.getInstance(this).addToFavorites(recipe);
+ }
+ inFavourites = !inFavourites;
+ }
+ private void showRecipe() {
+ ////Header
+ TextView nameView = (TextView)findViewById(R.id.recipe_header_name);
+ LinearLayout categoryList = (LinearLayout)findViewById(R.id.recipe_header_tags);
+ //Categories
+ categoryList.removeAllViews();
+ for (final Integer id : categories) {
+ LinearLayout categoryNameLayout = new LinearLayout(this);
+ categoryNameLayout.setPadding(dpToPix(CATEGORY_NAME_PADDING), 0, 0, 0);
+ TextView categoryNameView = new TextView(this);
+ //TextView categoryNameView = new TextView(this);
+ categoryNameView.setBackgroundColor(0x00_00_00_00);
+ categoryNameView.setTextAppearance(this, R.style.RecipeView_CategoryShowStyle); //I can't found another method
+ categoryNameView.setText(categoryDescriptions.get(id));
+ categoryNameView.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ Intent intent = new Intent(RecipeViewActivity.this, CookBookCategoryActivity.class);
+ intent.putExtra("Category", id);
+ startActivity(intent);
+ }
+ });
+ categoryNameLayout.addView(categoryNameView);
+ categoryList.addView(categoryNameLayout);
+ }
+
+ //Body
+ TextView descriptionView = (TextView)findViewById(R.id.recipe_body_description);
+ TextView ingridientsView = (TextView)findViewById(R.id.recipe_body_ingredients);
+
+ nameView.setText(recipe.getName());
+ descriptionView.setText(recipe.getDescription());
+ StringBuilder ingredientList = new StringBuilder();
+ for (Ingredient i : ingredients) {
+ if (i.getQuantity() >= 0.0) {
+ ingredientList = ingredientList.append(INGRIDIENT_LIST_DIVIDER + i.getName() +
+ INGRIDIENT_LIST_COUNT_DIVIDER + i.getQuantity() + " " +
+ i.getTypeName() + "\n");
+ } else {
+ ingredientList = ingredientList.append(INGRIDIENT_LIST_DIVIDER + i.getName() +
+ INGRIDIENT_LIST_COUNT_DIVIDER +
+ i.getTypeName() + "\n");
+ }
+ }
+ ingridientsView.setText(ingredientList.toString());
+ }
+
+ private int dpToPix(int value) {
+ return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, value, getResources().getDisplayMetrics());
+ }
+
+ public class ContentLoader implements Runnable {
+ private int recipeID;
+ public ContentLoader(int id) {
+ this.recipeID = id;
+ }
+
+ @Override
+ public void run() {
+ RecipeViewActivity.this.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ loaderAnimation.setVisibility(View.VISIBLE);
+ informationLayout.setVisibility(View.INVISIBLE);
+ }
+ });
+ final CookBookStorage cookbook = CookBookStorage.getInstance(RecipeViewActivity.this);
+ boolean loadingComplete = false;
+ while (!loadingComplete) {
+ try {
+ recipe = cookbook.getRecipe(recipeID);
+ steps = cookbook.getRecipeSteps(recipe.getID());
+ categories = cookbook.getRecipeCategories(recipe.getID());
+ liked = cookbook.getUserLike(recipe);
+ inFavourites = false;
+ for (Recipe r : cookbook.getFavorites()) {
+ if (r.getID() == recipe.getID()) {
+ inFavourites = true;
+ }
+ }
+ ingredients = cookbook.getRecipeIngredients(recipeID);
+ likeCount = cookbook.getRecipeLikes(recipe);
+ categoryDescriptions = new HashMap<>();
+ for (Integer id : categories) {
+ categoryDescriptions.put(id, cookbook.getCategoryByID(id).getDescription());
+ }
+ loadingComplete = true;
+ }
+ catch (Throwable e) {
+ //Repeat
+ }
+ }
+ final boolean userPermission = cookbook.isUserOwnRecipe(recipe);
+ RecipeViewActivity.this.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ showRecipe();
+ //Check user permissions
+ deleteBtn = (ImageButton) findViewById(R.id.recipe_delete);
+ editBtn = (ImageButton) findViewById(R.id.recipe_edit);
+ if (userPermission) {
+ deleteBtn.setVisibility(View.INVISIBLE);
+ editBtn.setVisibility(View.INVISIBLE);
+ }
+ loaderAnimation.setVisibility(View.INVISIBLE);
+ informationLayout.setVisibility(View.VISIBLE);
+ loaderAnimation.setGifImageResource(LoaderAnimationSelector.getRandomLoaderResource());
+ if (!liked) {
+ likeBtn.setImageBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.like_off));
+ } else {
+ likeBtn.setImageBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.like_on));
+ }
+ if (!inFavourites) {
+ favoriteBtn.setImageBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.favourite_off));
+ } else {
+ favoriteBtn.setImageBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.favourite_on));
+ }
+ likeCounter.setText(String.valueOf(likeCount));
+ }
+ });
+
+ //Image loader
+ final int widthInPx = dpToPix(IMAGES_WIDTH);
+ final int heightInPx = dpToPix(IMAGES_HEIGHT);
+ final LinearLayout recipeImages = (LinearLayout)findViewById(R.id.recipe_body_images);
+ RecipeViewActivity.this.runOnUiThread(
+ new Runnable() {
+ @Override
+ public void run() {
+ recipeImages.removeAllViews();
+ }
+ }
+ );
+ for (final Step s : steps) {
+ cookbook.downloadStepImage(s);
+ RecipeViewActivity.this.runOnUiThread(
+ new Runnable() {
+ @Override
+ public void run() {
+ ImageView photo = new ImageView(RecipeViewActivity.this);
+ photo.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
+ photo.setLayoutParams(new LinearLayout.LayoutParams(
+ Math.min(widthInPx, s.getImage().getWidth()),
+ Math.min(heightInPx, s.getImage().getHeight())));
+ photo.setImageBitmap(s.getImage());
+ recipeImages.addView(photo);
+ }
+ }
+ );
+ }
+ RecipeViewActivity.this.runOnUiThread(
+ new Runnable() {
+ @Override
+ public void run() {
+ if (steps.size() > 0) {
+ ImageView photoView = (ImageView) findViewById(R.id.recipe_header_photo);
+ photoView.setImageBitmap(steps.get(steps.size() - 1).getImage());
+ }
+ }
+ }
+ );
+ }
+ }
+}
diff --git a/app/src/main/java/ru/spbau/mit/foodmanager/SearchRecipeActivity.java b/app/src/main/java/ru/spbau/mit/foodmanager/SearchRecipeActivity.java
new file mode 100644
index 0000000..96fb53b
--- /dev/null
+++ b/app/src/main/java/ru/spbau/mit/foodmanager/SearchRecipeActivity.java
@@ -0,0 +1,109 @@
+package ru.spbau.mit.foodmanager;
+
+import android.content.Intent;
+import android.support.v7.app.AppCompatActivity;
+import android.os.Bundle;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.util.Log;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.EditText;
+import android.widget.ListView;
+
+import java.util.ArrayList;
+
+public class SearchRecipeActivity extends AppCompatActivity {
+
+ private ArrayList recipes;
+ private Intent task;
+ private GifImageView loaderAnimation;
+ private ListView recipesList;
+ private Thread searchingThread;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.search_recipe);
+ final EditText searchText = (EditText)findViewById(R.id.search_text);
+
+ //Init loaderAnimation
+ loaderAnimation = (GifImageView) findViewById(R.id.loader_animation_view);
+ loaderAnimation.setGifImageResource(LoaderAnimationSelector.getRandomLoaderResource());
+ loaderAnimation.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
+ loaderAnimation.setVisibility(View.INVISIBLE);
+ recipesList = (ListView) findViewById(R.id.search_recipes_list);
+ task = getIntent();
+
+
+ searchText.addTextChangedListener(new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
+
+ @Override
+ public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
+
+ @Override
+ public void afterTextChanged(Editable editable) {
+ if (searchingThread != null) {
+ searchingThread.interrupt();
+ }
+ searchingThread = new Thread(new ContentLoader(editable.toString()));
+ searchingThread.start();
+ }
+ });
+ }
+
+ private void showRecipes() {
+ Log.d("ShowRecipies", "Show");
+ ArrayList names = new ArrayList<>();
+ if (recipes != null) {
+ for (Recipe r : recipes) {
+ names.add(r.getName());
+ }
+ }
+ Log.d("names", ((Integer)names.size()).toString());
+ ArrayAdapter adapter = new ArrayAdapter<>(this,
+ R.layout.search_recipe_element, names);
+ recipesList.setAdapter(adapter);
+ recipesList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView> adapterView, View view, int i, long l) {
+ task.putExtra("Result", recipes.get(i).getID());
+ setResult(RESULT_OK, task);
+ finish();
+ }
+ });
+ }
+
+ public class ContentLoader implements Runnable {
+ private String searchFilter;
+ public ContentLoader(String searchFilter) {
+ this.searchFilter = searchFilter;
+ }
+
+ @Override
+ public void run() {
+ SearchRecipeActivity.this.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ //loaderAnimation.setVisibility(View.VISIBLE);
+ }
+ });
+
+ recipes = null;
+ while (recipes == null) {
+ recipes = CookBookStorage.getInstance(SearchRecipeActivity.this).getRecipesByFilter(searchFilter);
+ }
+ SearchRecipeActivity.this.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ //loaderAnimation.setVisibility(View.INVISIBLE);
+ showRecipes();
+ loaderAnimation.setGifImageResource(LoaderAnimationSelector.getRandomLoaderResource());
+ }
+ });
+ }
+ }
+}
diff --git a/app/src/main/java/ru/spbau/mit/foodmanager/SettingsActivity.java b/app/src/main/java/ru/spbau/mit/foodmanager/SettingsActivity.java
new file mode 100644
index 0000000..ee64beb
--- /dev/null
+++ b/app/src/main/java/ru/spbau/mit/foodmanager/SettingsActivity.java
@@ -0,0 +1,25 @@
+package ru.spbau.mit.foodmanager;
+
+import android.content.Intent;
+import android.support.v7.app.AppCompatActivity;
+import android.os.Bundle;
+import android.view.View;
+
+public class SettingsActivity extends AppCompatActivity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.settings);
+ }
+
+ public void onMenuSettingsClick(View v) {
+ Intent intent = new Intent(this, MenuSettingsActivity.class);
+ startActivity(intent);
+ }
+
+ public void onNotificationSettingsClick(View v) {
+ Intent intent = new Intent(this, NotificationSettingsActivity.class);
+ startActivity(intent);
+ }
+}
diff --git a/app/src/main/java/ru/spbau/mit/foodmanager/ShoppingListActivity.java b/app/src/main/java/ru/spbau/mit/foodmanager/ShoppingListActivity.java
new file mode 100644
index 0000000..79ebcc0
--- /dev/null
+++ b/app/src/main/java/ru/spbau/mit/foodmanager/ShoppingListActivity.java
@@ -0,0 +1,213 @@
+package ru.spbau.mit.foodmanager;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AbsListView;
+import android.widget.BaseAdapter;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.LinearLayout;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+
+public class ShoppingListActivity extends AppCompatActivity {
+ private static final String shoppingListFilename = "ShoppingList";
+ private CookBookStorage cookbook;
+ private ArrayList recipes;
+ private ArrayList ingredients;
+ private GifImageView loaderAnimation;
+ private LinearLayout informationLayout;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.shopping_list);
+ //Init loaderAnimation
+ loaderAnimation = (GifImageView) findViewById(R.id.loader_animation_view);
+ loaderAnimation.setGifImageResource(LoaderAnimationSelector.getRandomLoaderResource());
+ loaderAnimation.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
+ informationLayout = (LinearLayout) findViewById(R.id.information_layout);
+ //Init cookbook
+ cookbook = CookBookStorage.getInstance(this);
+ //Init Task
+ ContentLoader contentLoader = new ContentLoader();
+ Thread loader = new Thread(contentLoader);
+ loader.start();
+ }
+
+ public void saveMenuStorage() {
+ try {
+ FileOutputStream output = this.openFileOutput(
+ shoppingListFilename, Context.MODE_PRIVATE);
+
+ ObjectOutputStream outputStream = new ObjectOutputStream(output);
+ outputStream.writeObject(ingredients);
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ public void loadMenuStorage() {
+ File settings = new File(this.getFilesDir(), shoppingListFilename);
+ if (settings.exists()) {
+ try {
+ FileInputStream input = this.openFileInput(shoppingListFilename);
+ ObjectInputStream inputStream = new ObjectInputStream(input);
+ ingredients = (ArrayList) inputStream.readObject();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ private void showIngredients() {
+ ListView listView = (ListView) findViewById(R.id.shopping_list_view);
+ listView.setChoiceMode(AbsListView.CHOICE_MODE_MULTIPLE);
+ IngredientAdapter adapter = new IngredientAdapter(ingredients);
+ listView.setAdapter(adapter);
+ }
+
+ public class IngredientAdapter extends BaseAdapter {
+ private ArrayList ingredients;
+ private HashMap isChecked;
+ private LayoutInflater inflater;
+
+ public IngredientAdapter(ArrayList ing) {
+ ingredients = ing;
+ inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ isChecked = new HashMap<>();
+ }
+
+ @Override
+ public int getCount() {
+ return ingredients.size();
+ }
+
+ @Override
+ public Object getItem(int i) {
+ return ingredients.get(i);
+ }
+
+ @Override
+ public long getItemId(int i) {
+ return i;
+ }
+
+ @Override
+ public View getView(int i, View view, ViewGroup viewGroup) {
+ if (view == null) {
+ view = inflater.inflate(R.layout.shopping_list_element, null);
+ }
+ TextView name = (TextView) view.findViewById(R.id.shopping_list_element_text);
+ TextView count = (TextView) view.findViewById(R.id.shopping_list_element_count);
+ CheckBox checkBox = (CheckBox) view.findViewById(R.id.shopping_list_element_checkbox);
+ name.setText(ingredients.get(i).getName());
+ String countStr = "";
+ if (ingredients.get(i).getQuantity() > 0) {
+ countStr = ((Double)ingredients.get(i).getQuantity()).toString() + " ";
+ }
+ countStr = countStr + ingredients.get(i).getTypeName();
+ count.setText(countStr);
+ if (isChecked.get(i) == null) {
+ isChecked.put(i, false);
+ }
+ checkBox.setTag(i);
+ checkBox.setChecked(isChecked.get(i));
+ checkBox.setOnCheckedChangeListener(onCheckedChangeListener);
+ return view;
+ }
+
+ private CompoundButton.OnCheckedChangeListener onCheckedChangeListener =
+ new CompoundButton.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
+ isChecked.put((Integer)compoundButton.getTag(), b);
+ }
+ };
+ }
+
+ public class ContentLoader implements Runnable {
+
+ public ContentLoader() {
+ }
+
+ @Override
+ public void run() {
+ ShoppingListActivity.this.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ loaderAnimation.setVisibility(View.VISIBLE);
+ informationLayout.setVisibility(View.INVISIBLE);
+ }
+ });
+ MenuStorage menu = MenuStorage.getInstance(ShoppingListActivity.this);
+ HashMap menuRecipes = menu.getMenu();
+ recipes = new ArrayList<>();
+ for (DayMenu rs : menuRecipes.values()) {
+ for (Integer id : rs.getDishes()) {
+ recipes.add(cookbook.getRecipe(id));
+ //TODO: do in background
+ }
+ }
+ loadMenuStorage();
+ while (ingredients == null) {
+ try {
+ ArrayList allIngredients = new ArrayList<>();
+ for (Recipe r : recipes) {
+ allIngredients.addAll(cookbook.getRecipeIngredients(r.getID()));
+ }
+ ingredients = new ArrayList<>();
+ for (Ingredient i : allIngredients) {
+ Boolean newIngredient = true;
+ for (Ingredient j : ingredients) {
+ if (i.getName().equals(j.getName()) && i.getMeasure().equals(j.getMeasure())) {
+ newIngredient = false;
+ j.setQuantity(j.getQuantity() + i.getQuantity());
+ break;
+ }
+ }
+ if (newIngredient) {
+ ingredients.add(i);
+ }
+ }
+
+ Collections.sort(ingredients, new Comparator() {
+ @Override
+ public int compare(Ingredient i1, Ingredient i2) {
+ return i1.getName().compareTo(i2.getName());
+ }
+ });
+ }
+ catch (Throwable e) {
+ //Repeat
+ }
+ saveMenuStorage();
+ }
+
+ ShoppingListActivity.this.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ showIngredients();
+ loaderAnimation.setVisibility(View.INVISIBLE);
+ informationLayout.setVisibility(View.VISIBLE);
+ loaderAnimation.setGifImageResource(LoaderAnimationSelector.getRandomLoaderResource());
+ }
+ });
+ }
+ }
+}
diff --git a/app/src/main/java/ru/spbau/mit/foodmanager/Step.java b/app/src/main/java/ru/spbau/mit/foodmanager/Step.java
new file mode 100644
index 0000000..adb9a78
--- /dev/null
+++ b/app/src/main/java/ru/spbau/mit/foodmanager/Step.java
@@ -0,0 +1,36 @@
+package ru.spbau.mit.foodmanager;
+
+import android.graphics.Bitmap;
+
+public class Step {
+ /**
+ * Шаг инструкции.
+ */
+ private String description;
+ private String imageLink;
+ private Bitmap image;
+
+ public Step(String description, String imageLink) {
+ this.description = description;
+ this.imageLink = imageLink;
+ this.image = null;
+ }
+
+ public Step(String description, Bitmap image) {
+ this.description = description;
+ this.image = image;
+ this.imageLink = null;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public Bitmap getImage() {
+ return image;
+ }
+
+ public void setImage(Bitmap image) { this.image = image; }
+
+ public String getImageLink() { return imageLink; }
+}
\ No newline at end of file
diff --git a/app/src/main/java/ru/spbau/mit/foodmanager/StepViewActivity.java b/app/src/main/java/ru/spbau/mit/foodmanager/StepViewActivity.java
new file mode 100644
index 0000000..95ec1d6
--- /dev/null
+++ b/app/src/main/java/ru/spbau/mit/foodmanager/StepViewActivity.java
@@ -0,0 +1,124 @@
+package ru.spbau.mit.foodmanager;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+
+public class StepViewActivity extends AppCompatActivity {
+ private Recipe recipe;
+ private ArrayList steps;
+ private Integer stepPosition = 0;
+ private GifImageView loaderAnimation;
+ private LinearLayout informationLayout;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.step_view);
+ //Init loaderAnimation
+ loaderAnimation = (GifImageView) findViewById(R.id.loader_animation_view);
+ loaderAnimation.setGifImageResource(LoaderAnimationSelector.getRandomLoaderResource());
+ loaderAnimation.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
+ informationLayout = (LinearLayout) findViewById(R.id.information_layout);
+ //Init Task
+ Intent i = getIntent();
+ ContentLoader contentLoader = new ContentLoader(i.getIntExtra("Recipe", -1));
+ Thread loader = new Thread(contentLoader);
+ loader.start();
+
+ }
+
+ private void showRecipe() {
+ TextView recipeName = (TextView)findViewById(R.id.step_view_recipe_name);
+ recipeName.setText(recipe.getName());
+ TextView stepCount = (TextView)findViewById(R.id.step_view_step_count);
+ stepCount.setText(((Integer)steps.size()).toString());
+ showStep(stepPosition);
+ }
+
+ private void showStep(int id) {
+ if (steps.size() > id) {
+ TextView description = (TextView) findViewById(R.id.step_view_step_text);
+ description.setText(steps.get(id).getDescription());
+ ImageView image = (ImageView) findViewById(R.id.step_view_step_image);
+ if (steps.get(id).getImage() != null) {
+ image.setImageBitmap(steps.get(id).getImage());
+ }
+ TextView counter = (TextView) findViewById(R.id.step_view_position);
+ counter.setText(((Integer) (id + 1)).toString());
+ }
+ }
+
+ public void onNextBtnClick(View v) {
+ if (stepPosition < steps.size() - 1) {
+ stepPosition++;
+ showStep(stepPosition);
+ }
+ }
+
+ public void onPrevBtnClick(View v) {
+ if (stepPosition > 0) {
+ stepPosition--;
+ showStep(stepPosition);
+ }
+ }
+ public class ContentLoader implements Runnable {
+ private int recipeID;
+ public ContentLoader(int id) {
+ this.recipeID = id;
+ }
+
+ @Override
+ public void run() {
+ StepViewActivity.this.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ loaderAnimation.setVisibility(View.VISIBLE);
+ informationLayout.setVisibility(View.INVISIBLE);
+ }
+ });
+ steps = null;
+ CookBookStorage cookbook = CookBookStorage.getInstance(StepViewActivity.this);
+ while (steps == null) {
+ try {
+ recipe = cookbook.getRecipe(recipeID);
+ steps = cookbook.getRecipeSteps(recipe.getID());
+ }
+ catch (Throwable e) {
+ //Repeat
+ }
+ }
+ StepViewActivity.this.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ showRecipe();
+ loaderAnimation.setVisibility(View.INVISIBLE);
+ informationLayout.setVisibility(View.VISIBLE);
+ loaderAnimation.setGifImageResource(LoaderAnimationSelector.getRandomLoaderResource());
+ }
+ });
+ int i = 0;
+ for (final Step s : steps) {
+ cookbook.downloadStepImage(s);
+ if (i == stepPosition) {
+ StepViewActivity.this.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ ImageView image = (ImageView)findViewById(R.id.step_view_step_image);
+ if (s.getImage() != null) {
+ image.setImageBitmap(s.getImage());
+ }
+ }
+ });
+ }
+ i++;
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/ru/spbau/mit/foodmanager/loaderAnimationSelector.java b/app/src/main/java/ru/spbau/mit/foodmanager/loaderAnimationSelector.java
new file mode 100644
index 0000000..ed69bf4
--- /dev/null
+++ b/app/src/main/java/ru/spbau/mit/foodmanager/loaderAnimationSelector.java
@@ -0,0 +1,32 @@
+package ru.spbau.mit.foodmanager;
+
+import java.util.ArrayList;
+import java.util.Random;
+
+/**
+ * Generates number of resource for loader animations
+ */
+
+public class LoaderAnimationSelector {
+ private static ArrayList loaderResources;
+ private static Random random = new Random();
+
+ static {
+ loaderResources = new ArrayList<>();
+ loaderResources.add(R.drawable.loader);
+ //loaderResources.add(R.drawable.loading_animation);
+ //loaderResources.add(R.drawable.loading_animation2);
+ //loaderResources.add(R.drawable.loading_animation3);
+ //loaderResources.add(R.drawable.loading_animation4);
+ //loaderResources.add(R.drawable.loading_animation7);
+ //loaderResources.add(R.drawable.loading_animation6);
+ }
+
+ /**
+ * return random loader animation's resource ID
+ */
+ public static int getRandomLoaderResource() {
+ int resID = loaderResources.get(Math.abs(random.nextInt()) % loaderResources.size());
+ return resID;
+ }
+}
diff --git a/app/src/main/res/drawable/add.png b/app/src/main/res/drawable/add.png
new file mode 100644
index 0000000..896ec32
Binary files /dev/null and b/app/src/main/res/drawable/add.png differ
diff --git a/app/src/main/res/drawable/add_image.png b/app/src/main/res/drawable/add_image.png
new file mode 100644
index 0000000..65dd52c
Binary files /dev/null and b/app/src/main/res/drawable/add_image.png differ
diff --git a/app/src/main/res/drawable/border.xml b/app/src/main/res/drawable/border.xml
new file mode 100644
index 0000000..dc6234f
--- /dev/null
+++ b/app/src/main/res/drawable/border.xml
@@ -0,0 +1,7 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/delete.png b/app/src/main/res/drawable/delete.png
new file mode 100644
index 0000000..d2c53ae
Binary files /dev/null and b/app/src/main/res/drawable/delete.png differ
diff --git a/app/src/main/res/drawable/edit.png b/app/src/main/res/drawable/edit.png
new file mode 100644
index 0000000..bef9aba
Binary files /dev/null and b/app/src/main/res/drawable/edit.png differ
diff --git a/app/src/main/res/drawable/favourite_off.png b/app/src/main/res/drawable/favourite_off.png
new file mode 100644
index 0000000..a4cda25
Binary files /dev/null and b/app/src/main/res/drawable/favourite_off.png differ
diff --git a/app/src/main/res/drawable/favourite_on.png b/app/src/main/res/drawable/favourite_on.png
new file mode 100644
index 0000000..1ec07d3
Binary files /dev/null and b/app/src/main/res/drawable/favourite_on.png differ
diff --git a/app/src/main/res/drawable/like_off.png b/app/src/main/res/drawable/like_off.png
new file mode 100644
index 0000000..479e2d3
Binary files /dev/null and b/app/src/main/res/drawable/like_off.png differ
diff --git a/app/src/main/res/drawable/like_on.png b/app/src/main/res/drawable/like_on.png
new file mode 100644
index 0000000..b9ce1d1
Binary files /dev/null and b/app/src/main/res/drawable/like_on.png differ
diff --git a/app/src/main/res/drawable/loader.gif b/app/src/main/res/drawable/loader.gif
new file mode 100644
index 0000000..88bf0b4
Binary files /dev/null and b/app/src/main/res/drawable/loader.gif differ
diff --git a/app/src/main/res/drawable/loading_animation.gif b/app/src/main/res/drawable/loading_animation.gif
new file mode 100644
index 0000000..56a47ab
Binary files /dev/null and b/app/src/main/res/drawable/loading_animation.gif differ
diff --git a/app/src/main/res/drawable/loading_animation2.gif b/app/src/main/res/drawable/loading_animation2.gif
new file mode 100644
index 0000000..125b7d8
Binary files /dev/null and b/app/src/main/res/drawable/loading_animation2.gif differ
diff --git a/app/src/main/res/drawable/loading_animation3.gif b/app/src/main/res/drawable/loading_animation3.gif
new file mode 100644
index 0000000..417f7df
Binary files /dev/null and b/app/src/main/res/drawable/loading_animation3.gif differ
diff --git a/app/src/main/res/drawable/loading_animation4.gif b/app/src/main/res/drawable/loading_animation4.gif
new file mode 100644
index 0000000..fc1a553
Binary files /dev/null and b/app/src/main/res/drawable/loading_animation4.gif differ
diff --git a/app/src/main/res/drawable/loading_animation5.gif b/app/src/main/res/drawable/loading_animation5.gif
new file mode 100644
index 0000000..9f67b70
Binary files /dev/null and b/app/src/main/res/drawable/loading_animation5.gif differ
diff --git a/app/src/main/res/drawable/loading_animation6.gif b/app/src/main/res/drawable/loading_animation6.gif
new file mode 100644
index 0000000..33e3f5a
Binary files /dev/null and b/app/src/main/res/drawable/loading_animation6.gif differ
diff --git a/app/src/main/res/drawable/loading_animation7.gif b/app/src/main/res/drawable/loading_animation7.gif
new file mode 100644
index 0000000..f27d0de
Binary files /dev/null and b/app/src/main/res/drawable/loading_animation7.gif differ
diff --git a/app/src/main/res/drawable/logo.png b/app/src/main/res/drawable/logo.png
new file mode 100644
index 0000000..fafef43
Binary files /dev/null and b/app/src/main/res/drawable/logo.png differ
diff --git a/app/src/main/res/drawable/next.png b/app/src/main/res/drawable/next.png
new file mode 100644
index 0000000..e48bc00
Binary files /dev/null and b/app/src/main/res/drawable/next.png differ
diff --git a/app/src/main/res/drawable/previous.png b/app/src/main/res/drawable/previous.png
new file mode 100644
index 0000000..7bbaf0c
Binary files /dev/null and b/app/src/main/res/drawable/previous.png differ
diff --git a/app/src/main/res/drawable/search.png b/app/src/main/res/drawable/search.png
new file mode 100644
index 0000000..ec7dfbc
Binary files /dev/null and b/app/src/main/res/drawable/search.png differ
diff --git a/app/src/main/res/layout/choose_mealtime_preset.xml b/app/src/main/res/layout/choose_mealtime_preset.xml
new file mode 100644
index 0000000..c7f2ec2
--- /dev/null
+++ b/app/src/main/res/layout/choose_mealtime_preset.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/cook_book.xml b/app/src/main/res/layout/cook_book.xml
new file mode 100644
index 0000000..c68a54c
--- /dev/null
+++ b/app/src/main/res/layout/cook_book.xml
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/cook_book_category.xml b/app/src/main/res/layout/cook_book_category.xml
new file mode 100644
index 0000000..d1ecadb
--- /dev/null
+++ b/app/src/main/res/layout/cook_book_category.xml
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/edit_recipe.xml b/app/src/main/res/layout/edit_recipe.xml
new file mode 100644
index 0000000..b5843d5
--- /dev/null
+++ b/app/src/main/res/layout/edit_recipe.xml
@@ -0,0 +1,166 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/edit_recipe_ingredient.xml b/app/src/main/res/layout/edit_recipe_ingredient.xml
new file mode 100644
index 0000000..9e3dcf6
--- /dev/null
+++ b/app/src/main/res/layout/edit_recipe_ingredient.xml
@@ -0,0 +1,70 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/edit_recipe_tag.xml b/app/src/main/res/layout/edit_recipe_tag.xml
new file mode 100644
index 0000000..b981f5a
--- /dev/null
+++ b/app/src/main/res/layout/edit_recipe_tag.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/edit_step.xml b/app/src/main/res/layout/edit_step.xml
new file mode 100644
index 0000000..8a306f6
--- /dev/null
+++ b/app/src/main/res/layout/edit_step.xml
@@ -0,0 +1,134 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/main.xml b/app/src/main/res/layout/main.xml
new file mode 100644
index 0000000..0904b2d
--- /dev/null
+++ b/app/src/main/res/layout/main.xml
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/mealtime_settings.xml b/app/src/main/res/layout/mealtime_settings.xml
new file mode 100644
index 0000000..8a7292e
--- /dev/null
+++ b/app/src/main/res/layout/mealtime_settings.xml
@@ -0,0 +1,74 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/mealtime_settings_dish.xml b/app/src/main/res/layout/mealtime_settings_dish.xml
new file mode 100644
index 0000000..4cb8edd
--- /dev/null
+++ b/app/src/main/res/layout/mealtime_settings_dish.xml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/menu_settings.xml b/app/src/main/res/layout/menu_settings.xml
new file mode 100644
index 0000000..bc40305
--- /dev/null
+++ b/app/src/main/res/layout/menu_settings.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/menu_settings_day.xml b/app/src/main/res/layout/menu_settings_day.xml
new file mode 100644
index 0000000..ab7714b
--- /dev/null
+++ b/app/src/main/res/layout/menu_settings_day.xml
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/menu_settings_day_mealtime.xml b/app/src/main/res/layout/menu_settings_day_mealtime.xml
new file mode 100644
index 0000000..ca3082c
--- /dev/null
+++ b/app/src/main/res/layout/menu_settings_day_mealtime.xml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/menu_view.xml b/app/src/main/res/layout/menu_view.xml
new file mode 100644
index 0000000..633a83a
--- /dev/null
+++ b/app/src/main/res/layout/menu_view.xml
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/menu_view_day_menu_list_element.xml b/app/src/main/res/layout/menu_view_day_menu_list_element.xml
new file mode 100644
index 0000000..57edc74
--- /dev/null
+++ b/app/src/main/res/layout/menu_view_day_menu_list_element.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/menu_view_day_menu_meltime_list_element.xml b/app/src/main/res/layout/menu_view_day_menu_meltime_list_element.xml
new file mode 100644
index 0000000..442d17b
--- /dev/null
+++ b/app/src/main/res/layout/menu_view_day_menu_meltime_list_element.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/menu_view_list_element.xml b/app/src/main/res/layout/menu_view_list_element.xml
new file mode 100644
index 0000000..2a3e196
--- /dev/null
+++ b/app/src/main/res/layout/menu_view_list_element.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/notification_settings.xml b/app/src/main/res/layout/notification_settings.xml
new file mode 100644
index 0000000..a8960ce
--- /dev/null
+++ b/app/src/main/res/layout/notification_settings.xml
@@ -0,0 +1,137 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/recipe_view.xml b/app/src/main/res/layout/recipe_view.xml
new file mode 100644
index 0000000..9b68173
--- /dev/null
+++ b/app/src/main/res/layout/recipe_view.xml
@@ -0,0 +1,190 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/search_recipe.xml b/app/src/main/res/layout/search_recipe.xml
new file mode 100644
index 0000000..d992bba
--- /dev/null
+++ b/app/src/main/res/layout/search_recipe.xml
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/search_recipe_element.xml b/app/src/main/res/layout/search_recipe_element.xml
new file mode 100644
index 0000000..01c5dcf
--- /dev/null
+++ b/app/src/main/res/layout/search_recipe_element.xml
@@ -0,0 +1,11 @@
+
+
diff --git a/app/src/main/res/layout/settings.xml b/app/src/main/res/layout/settings.xml
new file mode 100644
index 0000000..442ff8c
--- /dev/null
+++ b/app/src/main/res/layout/settings.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/shopping_list.xml b/app/src/main/res/layout/shopping_list.xml
new file mode 100644
index 0000000..8bcf7e4
--- /dev/null
+++ b/app/src/main/res/layout/shopping_list.xml
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/shopping_list_element.xml b/app/src/main/res/layout/shopping_list_element.xml
new file mode 100644
index 0000000..c506dd3
--- /dev/null
+++ b/app/src/main/res/layout/shopping_list_element.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/step_view.xml b/app/src/main/res/layout/step_view.xml
new file mode 100644
index 0000000..f95bcea
--- /dev/null
+++ b/app/src/main/res/layout/step_view.xml
@@ -0,0 +1,131 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..53cd723
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/values-w820dp/dimens.xml b/app/src/main/res/values-w820dp/dimens.xml
new file mode 100644
index 0000000..63fc816
--- /dev/null
+++ b/app/src/main/res/values-w820dp/dimens.xml
@@ -0,0 +1,6 @@
+
+
+ 64dp
+
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..1f5cf97
--- /dev/null
+++ b/app/src/main/res/values/colors.xml
@@ -0,0 +1,9 @@
+
+
+ #FF3F51B5
+ #FF303F9F
+ #FF303F9F
+ #FFFFFFFF
+ #00000000
+ #88000000
+
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..725db4b
--- /dev/null
+++ b/app/src/main/res/values/dimens.xml
@@ -0,0 +1,9 @@
+
+
+ 16dp
+ 16dp
+ 32dp
+ 15sp
+ 35dp
+ 70dp
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..bc185a2
--- /dev/null
+++ b/app/src/main/res/values/strings.xml
@@ -0,0 +1,28 @@
+
+ FoodManager
+ Книга Рецептов
+ Меню на неделю
+ Список покупок
+ Настройки
+ Описание:
+ Вам нужны следующие продукты:
+ А также:
+ Краткое описание шагов:
+ Приготовить
+ Like
+ В избранное
+ :
+ Добавить
+ Сгенерировать
+ Назад
+ Описание рецепта
+ Вперед
+ Шаг\
+ \ из\
+ Сгруппировать по:
+ Настройки генератора меню
+ Настройки оповещений
+ День готовки
+ Название:
+ Настройки шаблона приема пищи
+
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..3d3e1cd
--- /dev/null
+++ b/app/src/main/res/values/styles.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/test/java/ru/spbau/mit/foodmanager/ExampleUnitTest.java b/app/src/test/java/ru/spbau/mit/foodmanager/ExampleUnitTest.java
new file mode 100644
index 0000000..9accdd0
--- /dev/null
+++ b/app/src/test/java/ru/spbau/mit/foodmanager/ExampleUnitTest.java
@@ -0,0 +1,17 @@
+package ru.spbau.mit.foodmanager;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see Testing documentation
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() throws Exception {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 0000000..74b2ab0
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,23 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+ repositories {
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:2.2.3'
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+allprojects {
+ repositories {
+ jcenter()
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
diff --git a/captures/ru.spbau.mit.foodmanager_2017.02.16_19.32.trace b/captures/ru.spbau.mit.foodmanager_2017.02.16_19.32.trace
new file mode 100644
index 0000000..6c09791
--- /dev/null
+++ b/captures/ru.spbau.mit.foodmanager_2017.02.16_19.32.trace
@@ -0,0 +1,3184 @@
+*version
+3
+data-file-overflow=false
+clock=dual
+elapsed-time-usec=1150939
+num-method-calls=18596
+clock-call-overhead-nsec=19903
+vm=art
+pid=2497
+*threads
+2497 main
+2500 Signal Catcher
+2503 JDWP
+2507 HeapTaskDaemon
+2505 FinalizerDaemon
+2504 ReferenceQueueDaemon
+2506 FinalizerWatchdogDaemon
+2508 Binder_1
+2509 Binder_2
+2559 Thread-92
+2751 RenderThread
+2760 hwuiTask2
+2759 hwuiTask1
+2768 Binder_3
+2923 Binder_4
+2924 Thread-99
+*methods
+0x78 java.lang.ref.Reference get ()Ljava/lang/Object; Reference.java
+0x2e0 java.lang.Throwable fillInStackTrace ()Ljava/lang/Throwable; Throwable.java
+0x2d0 java.lang.Exception (Ljava/lang/String;)V Exception.java
+0x360 java.util.AbstractCollection ()V AbstractCollection.java
+0x35c java.util.AbstractList ()V AbstractList.java
+0x10 java.lang.ClassLoader findLoadedClass (Ljava/lang/String;)Ljava/lang/Class; ClassLoader.java
+0x8 java.lang.ClassLoader loadClass (Ljava/lang/String;)Ljava/lang/Class; ClassLoader.java
+0xc java.lang.ClassLoader loadClass (Ljava/lang/String;Z)Ljava/lang/Class; ClassLoader.java
+0x350 dalvik.system.BaseDexClassLoader findClass (Ljava/lang/String;)Ljava/lang/Class; BaseDexClassLoader.java
+0x1cc java.lang.ThreadLocal get ()Ljava/lang/Object; ThreadLocal.java
+0x1d4 java.lang.ThreadLocal values (Ljava/lang/Thread;)Ljava/lang/ThreadLocal$Values; ThreadLocal.java
+0x374 dalvik.system.DexFile loadClassBinaryName (Ljava/lang/String;Ljava/lang/ClassLoader;Ljava/util/List;)Ljava/lang/Class; DexFile.java
+0x370 dalvik.system.DexPathList$Element -get0 (Ldalvik/system/DexPathList$Element;)Ldalvik/system/DexFile; DexPathList.java
+0x36c dalvik.system.DexPathList findClass (Ljava/lang/String;Ljava/util/List;)Ljava/lang/Class; DexPathList.java
+0x130 java.lang.AbstractStringBuilder ()V AbstractStringBuilder.java
+0x154 java.lang.AbstractStringBuilder enlargeBuffer (I)V AbstractStringBuilder.java
+0x138 java.lang.AbstractStringBuilder append0 (Ljava/lang/String;)V AbstractStringBuilder.java
+0x160 java.lang.AbstractStringBuilder toString ()Ljava/lang/String; AbstractStringBuilder.java
+0x14 java.lang.BootClassLoader getInstance ()Ljava/lang/BootClassLoader; ClassLoader.java
+0x2ac java.lang.BootClassLoader findClass (Ljava/lang/String;)Ljava/lang/Class; ClassLoader.java
+0x1c java.lang.BootClassLoader loadClass (Ljava/lang/String;Z)Ljava/lang/Class; ClassLoader.java
+0xb4 java.lang.Number ()V Number.java
+0x2c8 java.lang.ReflectiveOperationException (Ljava/lang/String;)V ReflectiveOperationException.java
+0x2c0 java.lang.ClassNotFoundException (Ljava/lang/String;Ljava/lang/Throwable;)V ClassNotFoundException.java
+0xb0 java.lang.Integer (I)V Integer.java
+0xa8 java.lang.Integer valueOf (I)Ljava/lang/Integer; Integer.java
+0xdc java.lang.Integer equals (Ljava/lang/Object;)Z Integer.java
+0xc4 java.lang.Integer hashCode ()I Integer.java
+0x148 java.lang.IntegralToString appendLong (Ljava/lang/AbstractStringBuilder;J)V IntegralToString.java
+0x150 java.lang.IntegralToString convertInt (Ljava/lang/AbstractStringBuilder;I)Ljava/lang/String; IntegralToString.java
+0x14c java.lang.IntegralToString convertLong (Ljava/lang/AbstractStringBuilder;J)Ljava/lang/String; IntegralToString.java
+0x12c java.lang.StringBuilder ()V StringBuilder.java
+0x144 java.lang.StringBuilder append (J)Ljava/lang/StringBuilder; StringBuilder.java
+0x134 java.lang.StringBuilder append (Ljava/lang/String;)Ljava/lang/StringBuilder; StringBuilder.java
+0x15c java.lang.StringBuilder toString ()Ljava/lang/String; StringBuilder.java
+0x1dc java.lang.ThreadLocal$Values -get0 (Ljava/lang/ThreadLocal$Values;)I ThreadLocal.java
+0x1d8 java.lang.ThreadLocal$Values -get1 (Ljava/lang/ThreadLocal$Values;)[Ljava/lang/Object; ThreadLocal.java
+0x118 java.nio.Buffer (IIJ)V Buffer.java
+0x114 java.nio.ByteBuffer (IJ)V ByteBuffer.java
+0xfc java.nio.ByteBuffer wrap ([BII)Ljava/nio/ByteBuffer; ByteBuffer.java
+0x11c java.nio.ByteBuffer order (Ljava/nio/ByteOrder;)Ljava/nio/ByteBuffer; ByteBuffer.java
+0x110 java.nio.ByteArrayBuffer (I[BIZ)V ByteArrayBuffer.java
+0x10c java.nio.ByteArrayBuffer ([B)V ByteArrayBuffer.java
+0x120 java.nio.ByteArrayBuffer get ()B ByteArrayBuffer.java
+0x354 java.util.ArrayList ()V ArrayList.java
+0x1e8 java.util.ArrayList clear ()V ArrayList.java
+0x1e4 java.util.ArrayList size ()I ArrayList.java
+0x100 java.util.Arrays checkOffsetAndCount (III)V Arrays.java
+0x2d8 java.util.Collections emptyList ()Ljava/util/List; Collections.java
+0xd0 java.util.Collections secondaryHash (I)I Collections.java
+0xc0 java.util.Collections secondaryHash (Ljava/lang/Object;)I Collections.java
+0xbc java.util.HashMap get (Ljava/lang/Object;)Ljava/lang/Object; HashMap.java
+0xe0 org.apache.harmony.dalvik.ddmc.Chunk (I[BII)V Chunk.java
+0xf8 org.apache.harmony.dalvik.ddmc.ChunkHandler wrapChunk (Lorg/apache/harmony/dalvik/ddmc/Chunk;)Ljava/nio/ByteBuffer; ChunkHandler.java
+0x308 android.view.ContextThemeWrapper getResources ()Landroid/content/res/Resources; ContextThemeWrapper.java
+0x30c android.content.res.Resources getDisplayMetrics ()Landroid/util/DisplayMetrics; Resources.java
+0x364 android.graphics.Bitmap eraseColor (I)V Bitmap.java
+0x324 android.graphics.Bitmap getHeight ()I Bitmap.java
+0x320 android.graphics.Bitmap getWidth ()I Bitmap.java
+0x330 android.graphics.Bitmap isMutable ()Z Bitmap.java
+0x33c android.graphics.Bitmap isPremultiplied ()Z Bitmap.java
+0x338 android.graphics.Bitmap isRecycled ()Z Bitmap.java
+0x290 android.graphics.Canvas clipRect (IIII)Z Canvas.java
+0x48c android.graphics.Canvas drawBitmap (Landroid/graphics/Bitmap;FFLandroid/graphics/Paint;)V Canvas.java
+0x390 android.graphics.Canvas drawColor (I)V Canvas.java
+0x3a8 android.graphics.Canvas getNativeCanvasWrapper ()J Canvas.java
+0x32c android.graphics.Canvas isHardwareAccelerated ()Z Canvas.java
+0x2dc android.graphics.Canvas quickReject (FFFFLandroid/graphics/Canvas$EdgeType;)Z Canvas.java
+0x47c android.graphics.Canvas restoreToCount (I)V Canvas.java
+0x288 android.graphics.Canvas save ()I Canvas.java
+0x328 android.graphics.Canvas setBitmap (Landroid/graphics/Bitmap;)V Canvas.java
+0x278 android.graphics.Canvas translate (FF)V Canvas.java
+0x488 android.graphics.Rect contains (Landroid/graphics/Rect;)Z Rect.java
+0x1c4 android.graphics.Rect height ()I Rect.java
+0x3e8 android.graphics.Rect intersect (IIII)Z Rect.java
+0x214 android.graphics.Rect isEmpty ()Z Rect.java
+0x210 android.graphics.Rect offset (II)V Rect.java
+0x3c0 android.graphics.Rect set (IIII)V Rect.java
+0x21c android.graphics.Rect setEmpty ()V Rect.java
+0x3f8 android.graphics.Rect union (IIII)V Rect.java
+0x1c0 android.graphics.Rect width ()I Rect.java
+0xcc android.os.Handler enqueueMessage (Landroid/os/MessageQueue;Landroid/os/Message;J)Z Handler.java
+0x5c android.os.Handler handleCallback (Landroid/os/Message;)V Handler.java
+0x60 android.os.Handler dispatchMessage (Landroid/os/Message;)V Handler.java
+0x1a0 android.os.Handler getLooper ()Landroid/os/Looper; Handler.java
+0xc8 android.os.Handler sendMessageAtTime (Landroid/os/Message;J)Z Handler.java
+0x428 android.os.Looper myLooper ()Landroid/os/Looper; Looper.java
+0x1a4 android.os.Looper getQueue ()Landroid/os/MessageQueue; Looper.java
+0xa4 android.os.Message obtain ()Landroid/os/Message; Message.java
+0xa0 android.os.Message obtain (Landroid/os/Handler;Ljava/lang/Runnable;)Landroid/os/Message; Message.java
+0x128 android.os.Message isAsynchronous ()Z Message.java
+0xd8 android.os.Message isInUse ()Z Message.java
+0x104 android.os.Message markInUse ()V Message.java
+0x6c android.os.Message recycleUnchecked ()V Message.java
+0xac android.os.Message setAsynchronous (Z)V Message.java
+0xd4 android.os.MessageQueue enqueueMessage (Landroid/os/Message;J)Z MessageQueue.java
+0x70 android.os.MessageQueue next ()Landroid/os/Message; MessageQueue.java
+0x400 android.os.MessageQueue postSyncBarrier ()I MessageQueue.java
+0x1a8 android.os.MessageQueue removeSyncBarrier (I)V MessageQueue.java
+0x4bc android.util.Pools$SimplePool isInPool (Ljava/lang/Object;)Z Pools.java
+0x254 android.util.Pools$SimplePool acquire ()Ljava/lang/Object; Pools.java
+0x4b8 android.util.Pools$SimplePool release (Ljava/lang/Object;)Z Pools.java
+0x250 android.util.Pools$SynchronizedPool acquire ()Ljava/lang/Object; Pools.java
+0x4b4 android.util.Pools$SynchronizedPool release (Ljava/lang/Object;)Z Pools.java
+0x310 android.util.SparseArray get (I)Ljava/lang/Object; SparseArray.java
+0x314 android.util.SparseArray get (ILjava/lang/Object;)Ljava/lang/Object; SparseArray.java
+0x198 android.view.FrameInfo markAnimationsStart ()V FrameInfo.java
+0x220 android.view.FrameInfo markDrawStart ()V FrameInfo.java
+0x178 android.view.FrameInfo markInputHandlingStart ()V FrameInfo.java
+0x19c android.view.FrameInfo markPerformTraversalsStart ()V FrameInfo.java
+0x170 android.view.FrameInfo setVsync (JJ)V FrameInfo.java
+0x218 android.view.HardwareRenderer isEnabled ()Z HardwareRenderer.java
+0x18c android.view.InputEventReceiver consumeBatchedInputEvents (J)Z InputEventReceiver.java
+0x498 android.view.RenderNode end (Landroid/view/DisplayListCanvas;)V RenderNode.java
+0x458 android.view.RenderNode getElevation ()F RenderNode.java
+0x3d4 android.view.RenderNode getMatrix (Landroid/graphics/Matrix;)V RenderNode.java
+0x4ec android.view.RenderNode getNativeDisplayList ()J RenderNode.java
+0x464 android.view.RenderNode getTranslationZ ()F RenderNode.java
+0x2c4 android.view.RenderNode hasIdentityMatrix ()Z RenderNode.java
+0x234 android.view.RenderNode isValid ()Z RenderNode.java
+0x4e0 android.view.RenderNode setAlpha (F)Z RenderNode.java
+0x4d4 android.view.RenderNode setClipToBounds (Z)Z RenderNode.java
+0x4c8 android.view.RenderNode setHasOverlappingRendering (Z)Z RenderNode.java
+0x248 android.view.RenderNode start (II)Landroid/view/DisplayListCanvas; RenderNode.java
+0x200 android.view.Surface isValid ()Z Surface.java
+0x20 android.view.ThreadedRenderer draw (Landroid/view/View;Landroid/view/View$AttachInfo;Landroid/view/HardwareRenderer$HardwareDrawCallbacks;)V ThreadedRenderer.java
+0x440 android.view.ThreadedRenderer notifyFramePending ()V ThreadedRenderer.java
+0x3d0 android.view.View$TransformationInfo -get1 (Landroid/view/View$TransformationInfo;)Landroid/graphics/Matrix; View.java
+0x2f0 android.view.View buildDrawingCacheImpl (Z)V View.java
+0x388 android.view.View drawBackground (Landroid/graphics/Canvas;)V View.java
+0x4dc android.view.View getFinalAlpha ()F View.java
+0x474 android.view.View onDrawScrollIndicators (Landroid/graphics/Canvas;)V View.java
+0x3bc android.view.View skipInvalidate ()Z View.java
+0x2ec android.view.View buildDrawingCache (Z)V View.java
+0x230 android.view.View canHaveDisplayList ()Z View.java
+0x274 android.view.View computeScroll ()V View.java
+0x46c android.view.View dispatchDraw (Landroid/graphics/Canvas;)V View.java
+0x384 android.view.View draw (Landroid/graphics/Canvas;)V View.java
+0x2b0 android.view.View draw (Landroid/graphics/Canvas;Landroid/view/ViewGroup;J)Z View.java
+0x3cc android.view.View ensureTransformationInfo ()V View.java
+0x240 android.view.View getAnimation ()Landroid/view/animation/Animation; View.java
+0x484 android.view.View getDrawingCache (Z)Landroid/graphics/Bitmap; View.java
+0x298 android.view.View getDrawingTime ()J View.java
+0x454 android.view.View getElevation ()F View.java
+0x270 android.view.View getHardwareLayer ()Landroid/view/HardwareLayer; View.java
+0x244 android.view.View getLayerType ()I View.java
+0x3c8 android.view.View getMatrix ()Landroid/graphics/Matrix; View.java
+0x460 android.view.View getTranslationZ ()F View.java
+0x1b4 android.view.View getVisibility ()I View.java
+0x450 android.view.View getZ ()F View.java
+0x2bc android.view.View hasIdentityMatrix ()Z View.java
+0x4c4 android.view.View hasOverlappingRendering ()Z View.java
+0x3b0 android.view.View invalidate ()V View.java
+0x3b4 android.view.View invalidate (Z)V View.java
+0x3b8 android.view.View invalidateInternal (IIIIZZ)V View.java
+0x44c android.view.View isHardwareAccelerated ()Z View.java
+0x2f4 android.view.View isOpaque ()Z View.java
+0x398 android.view.View onDraw (Landroid/graphics/Canvas;)V View.java
+0x470 android.view.View onDrawForeground (Landroid/graphics/Canvas;)V View.java
+0x478 android.view.View onDrawScrollBars (Landroid/graphics/Canvas;)V View.java
+0x4c0 android.view.View setDisplayListProperties (Landroid/view/RenderNode;)V View.java
+0x22c android.view.View updateDisplayListIfDirty ()Landroid/view/RenderNode; View.java
+0x2f8 android.view.ViewConfiguration get (Landroid/content/Context;)Landroid/view/ViewConfiguration; ViewConfiguration.java
+0x31c android.view.ViewConfiguration getScaledMaximumDrawingCacheSize ()I ViewConfiguration.java
+0x4f8 android.view.ViewGroup debugDraw ()Z ViewGroup.java
+0x23c android.view.ViewGroup recreateChildDisplayList (Landroid/view/View;)V ViewGroup.java
+0x280 android.view.ViewGroup dispatchDraw (Landroid/graphics/Canvas;)V ViewGroup.java
+0x238 android.view.ViewGroup dispatchGetDisplayList ()V ViewGroup.java
+0x2a8 android.view.ViewGroup drawChild (Landroid/graphics/Canvas;Landroid/view/View;J)Z ViewGroup.java
+0x4d0 android.view.ViewGroup getClipChildren ()Z ViewGroup.java
+0x3c4 android.view.ViewGroup invalidateChild (Landroid/view/View;Landroid/graphics/Rect;)V ViewGroup.java
+0x3e4 android.view.ViewGroup invalidateChildInParent ([ILandroid/graphics/Rect;)Landroid/view/ViewParent; ViewGroup.java
+0x2a4 android.view.ViewGroup isChildrenDrawingOrderEnabled ()Z ViewGroup.java
+0x180 android.view.ViewRootImpl$ConsumeBatchedInputRunnable run ()V ViewRootImpl.java
+0x1e0 android.view.ViewRootImpl$RunQueue executeActions (Landroid/os/Handler;)V ViewRootImpl.java
+0x3c android.view.ViewRootImpl$TraversalRunnable run ()V ViewRootImpl.java
+0x1ec android.view.ViewRootImpl collectViewAttributes ()Z ViewRootImpl.java
+0x24 android.view.ViewRootImpl draw (Z)V ViewRootImpl.java
+0x1c8 android.view.ViewRootImpl getRunQueue ()Landroid/view/ViewRootImpl$RunQueue; ViewRootImpl.java
+0x3f4 android.view.ViewRootImpl invalidateRectOnScreen (Landroid/graphics/Rect;)V ViewRootImpl.java
+0x1f4 android.view.ViewRootImpl isInLocalFocusMode ()Z ViewRootImpl.java
+0x30 android.view.ViewRootImpl performDraw ()V ViewRootImpl.java
+0x34 android.view.ViewRootImpl performTraversals ()V ViewRootImpl.java
+0x3f0 android.view.ViewRootImpl checkThread ()V ViewRootImpl.java
+0x188 android.view.ViewRootImpl doConsumeBatchedInput (J)V ViewRootImpl.java
+0x194 android.view.ViewRootImpl doProcessInputEvents ()V ViewRootImpl.java
+0x38 android.view.ViewRootImpl doTraversal ()V ViewRootImpl.java
+0x1b0 android.view.ViewRootImpl getHostVisibility ()I ViewRootImpl.java
+0x3ec android.view.ViewRootImpl invalidateChildInParent ([ILandroid/graphics/Rect;)Landroid/view/ViewParent; ViewRootImpl.java
+0x43c android.view.ViewRootImpl notifyRendererOfFramePending ()V ViewRootImpl.java
+0x448 android.view.ViewRootImpl pokeDrawLockIfNeeded ()V ViewRootImpl.java
+0x438 android.view.ViewRootImpl scheduleConsumeBatchedInput ()V ViewRootImpl.java
+0x3fc android.view.ViewRootImpl scheduleTraversals ()V ViewRootImpl.java
+0x208 android.view.ViewRootImpl scrollToRectOrFocus (Landroid/graphics/Rect;Z)Z ViewRootImpl.java
+0x20c android.view.ViewTreeObserver dispatchOnDraw ()V ViewTreeObserver.java
+0x1fc android.view.ViewTreeObserver dispatchOnPreDraw ()Z ViewTreeObserver.java
+0x1f0 android.view.ViewTreeObserver hasComputeInternalInsetsListeners ()Z ViewTreeObserver.java
+0x1f8 android.view.WindowManager$LayoutParams mayUseInputMethod (I)Z WindowManager.java
+0xf4 android.ddm.DdmHandleHeap handleHPIF (Lorg/apache/harmony/dalvik/ddmc/Chunk;)Lorg/apache/harmony/dalvik/ddmc/Chunk; DdmHandleHeap.java
+0xf0 android.ddm.DdmHandleHeap handleChunk (Lorg/apache/harmony/dalvik/ddmc/Chunk;)Lorg/apache/harmony/dalvik/ddmc/Chunk; DdmHandleHeap.java
+0xe4 android.ddm.DdmHandleProfiling handleMPRQ (Lorg/apache/harmony/dalvik/ddmc/Chunk;)Lorg/apache/harmony/dalvik/ddmc/Chunk; DdmHandleProfiling.java
+0x4fc android.ddm.DdmHandleProfiling handleMPSEOrSPSE (Lorg/apache/harmony/dalvik/ddmc/Chunk;Ljava/lang/String;)Lorg/apache/harmony/dalvik/ddmc/Chunk; DdmHandleProfiling.java
+0x90 android.ddm.DdmHandleProfiling handleMPSS (Lorg/apache/harmony/dalvik/ddmc/Chunk;)Lorg/apache/harmony/dalvik/ddmc/Chunk; DdmHandleProfiling.java
+0x94 android.ddm.DdmHandleProfiling handleChunk (Lorg/apache/harmony/dalvik/ddmc/Chunk;)Lorg/apache/harmony/dalvik/ddmc/Chunk; DdmHandleProfiling.java
+0x340 android.graphics.Bitmap$BitmapFinalizer -get0 (Landroid/graphics/Bitmap$BitmapFinalizer;)J Bitmap.java
+0x318 android.util.ContainerHelpers binarySearch ([III)I ContainerHelpers.java
+0x414 android.view.Choreographer$CallbackQueue addCallbackLocked (JLjava/lang/Object;Ljava/lang/Object;)V Choreographer.java
+0x50 android.view.Choreographer$CallbackQueue extractDueCallbacksLocked (J)Landroid/view/Choreographer$CallbackRecord; Choreographer.java
+0x40 android.view.Choreographer$CallbackRecord run (J)V Choreographer.java
+0x430 android.view.DisplayEventReceiver scheduleVsync ()V DisplayEventReceiver.java
+0x88 android.view.Choreographer$FrameDisplayEventReceiver onVsync (JII)V Choreographer.java
+0x58 android.view.Choreographer$FrameDisplayEventReceiver run ()V Choreographer.java
+0xb8 java.lang.Object ()V Object.java
+0x140 java.lang.String getCharsNoCheck (II[CI)V String.java
+0x13c java.lang.String length ()I String.java
+0x7c java.lang.ref.Reference getReferent ()Ljava/lang/Object; Reference.java
+0x2d4 java.lang.Throwable (Ljava/lang/String;)V Throwable.java
+0x2e8 java.lang.Throwable nativeFillInStackTrace ()Ljava/lang/Object; Throwable.java
+0x378 dalvik.system.DexFile defineClass (Ljava/lang/String;Ljava/lang/ClassLoader;Ljava/lang/Object;Ljava/util/List;)Ljava/lang/Class; DexFile.java
+0x380 dalvik.system.DexFile defineClassNative (Ljava/lang/String;Ljava/lang/ClassLoader;Ljava/lang/Object;)Ljava/lang/Class; DexFile.java
+0xec dalvik.system.VMDebug getMethodTracingMode ()I VMDebug.java
+0x84 dalvik.system.VMDebug startMethodTracingDdms (IIZI)V VMDebug.java
+0x68 dalvik.system.VMDebug startMethodTracingDdmsImpl (IIZI)V VMDebug.java
+0x504 dalvik.system.VMDebug stopMethodTracing ()V VMDebug.java
+0x164 java.lang.StringFactory newStringFromChars (II[C)Ljava/lang/String; StringFactory.java
+0x158 java.lang.System arraycopy ([CI[CII)V System.java
+0x4c java.lang.System nanoTime ()J System.java
+0x1d0 java.lang.Thread currentThread ()Ljava/lang/Thread; Thread.java
+0x18 java.lang.VMClassLoader findLoadedClass (Ljava/lang/ClassLoader;Ljava/lang/String;)Ljava/lang/Class; VMClassLoader.java
+0x4 libcore.io.Posix recvfromBytes (Ljava/io/FileDescriptor;Ljava/lang/Object;IIILjava/net/InetSocketAddress;)I Posix.java
+0x98 org.apache.harmony.dalvik.ddmc.DdmServer dispatch (I[BII)Lorg/apache/harmony/dalvik/ddmc/Chunk; DdmServer.java
+0x124 org.apache.harmony.dalvik.ddmc.DdmVmInternal heapInfoNotify (I)Z DdmVmInternal.java
+0x2b8 java.lang.Class classForName (Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class; Class.java
+0x64 android.os.Binder clearCallingIdentity ()J Binder.java
+0x1bc android.content.res.CompatibilityInfo supportsScreen ()Z CompatibilityInfo.java
+0x368 android.graphics.Bitmap checkRecycled (Ljava/lang/String;)V Bitmap.java
+0x37c android.graphics.Bitmap nativeErase (JI)V Bitmap.java
+0x344 android.graphics.Bitmap nativeIsPremultiplied (J)Z Bitmap.java
+0x294 android.graphics.Canvas native_clipRect (JFFFFI)Z Canvas.java
+0x494 android.graphics.Canvas native_drawBitmap (JLandroid/graphics/Bitmap;FFJIII)V Canvas.java
+0x394 android.graphics.Canvas native_drawColor (JII)V Canvas.java
+0x2e4 android.graphics.Canvas native_quickReject (JFFFF)Z Canvas.java
+0x480 android.graphics.Canvas native_restoreToCount (JIZ)V Canvas.java
+0x28c android.graphics.Canvas native_save (JI)I Canvas.java
+0x358 android.graphics.Canvas native_setBitmap (JLandroid/graphics/Bitmap;)V Canvas.java
+0x27c android.graphics.Canvas native_translate (JFF)V Canvas.java
+0x334 android.graphics.Canvas throwIfCannotDraw (Landroid/graphics/Bitmap;)V Canvas.java
+0x3e0 android.graphics.Matrix native_isIdentity (J)Z Matrix.java
+0x3dc android.graphics.Matrix isIdentity ()Z Matrix.java
+0x490 android.graphics.Paint getNativeInstance ()J Paint.java
+0xe8 android.os.Debug getMethodTracingMode ()I Debug.java
+0x8c android.os.Debug startMethodTracingDdms (IIZI)V Debug.java
+0x500 android.os.Debug stopMethodTracing ()V Debug.java
+0x74 android.os.MessageQueue nativePollOnce (JI)V MessageQueue.java
+0x1ac android.os.MessageQueue nativeWake (J)V MessageQueue.java
+0x404 android.os.MessageQueue postSyncBarrier (J)I MessageQueue.java
+0x108 android.os.SystemClock uptimeMillis ()J SystemClock.java
+0x2c android.os.Trace isTagEnabled (J)Z Trace.java
+0x174 android.os.Trace traceBegin (JLjava/lang/String;)V Trace.java
+0x28 android.os.Trace traceEnd (J)V Trace.java
+0x168 android.util.Log i (Ljava/lang/String;Ljava/lang/String;)I Log.java
+0x16c android.util.Log println_native (IILjava/lang/String;Ljava/lang/String;)I Log.java
+0x17c android.view.Choreographer -get0 ()Ljava/lang/Object; Choreographer.java
+0x9c android.view.Choreographer -get1 (Landroid/view/Choreographer;)Landroid/view/Choreographer$FrameHandler; Choreographer.java
+0x418 android.view.Choreographer -wrap0 (Landroid/view/Choreographer;JLjava/lang/Object;Ljava/lang/Object;)Landroid/view/Choreographer$CallbackRecord; Choreographer.java
+0x424 android.view.Choreographer isRunningOnLooperThreadLocked ()Z Choreographer.java
+0x41c android.view.Choreographer obtainCallbackLocked (JLjava/lang/Object;Ljava/lang/Object;)Landroid/view/Choreographer$CallbackRecord; Choreographer.java
+0x410 android.view.Choreographer postCallbackDelayedInternal (ILjava/lang/Object;Ljava/lang/Object;J)V Choreographer.java
+0x44 android.view.Choreographer recycleCallbackLocked (Landroid/view/Choreographer$CallbackRecord;)V Choreographer.java
+0x420 android.view.Choreographer scheduleFrameLocked (J)V Choreographer.java
+0x42c android.view.Choreographer scheduleVsyncLocked ()V Choreographer.java
+0x48 android.view.Choreographer doCallbacks (IJ)V Choreographer.java
+0x54 android.view.Choreographer doFrame (JI)V Choreographer.java
+0x184 android.view.Choreographer getFrameTimeNanos ()J Choreographer.java
+0x408 android.view.Choreographer postCallback (ILjava/lang/Runnable;Ljava/lang/Object;)V Choreographer.java
+0x40c android.view.Choreographer postCallbackDelayed (ILjava/lang/Runnable;Ljava/lang/Object;J)V Choreographer.java
+0x1b8 android.view.DisplayAdjustments getCompatibilityInfo ()Landroid/content/res/CompatibilityInfo; DisplayAdjustments.java
+0x4f0 android.view.DisplayListCanvas nDrawRenderNode (JJ)V DisplayListCanvas.java
+0x4a0 android.view.DisplayListCanvas nFinish (J)V DisplayListCanvas.java
+0x4a8 android.view.DisplayListCanvas nFinishRecording (J)J DisplayListCanvas.java
+0x2a0 android.view.DisplayListCanvas nInsertReorderBarrier (JZ)V DisplayListCanvas.java
+0x264 android.view.DisplayListCanvas nPrepare (J)V DisplayListCanvas.java
+0x26c android.view.DisplayListCanvas nSetHighContrastText (JZ)V DisplayListCanvas.java
+0x25c android.view.DisplayListCanvas nSetViewport (JII)V DisplayListCanvas.java
+0x24c android.view.DisplayListCanvas obtain (Landroid/view/RenderNode;)Landroid/view/DisplayListCanvas; DisplayListCanvas.java
+0x4e8 android.view.DisplayListCanvas drawRenderNode (Landroid/view/RenderNode;)V DisplayListCanvas.java
+0x4a4 android.view.DisplayListCanvas finishRecording ()J DisplayListCanvas.java
+0x4f4 android.view.DisplayListCanvas insertInorderBarrier ()V DisplayListCanvas.java
+0x29c android.view.DisplayListCanvas insertReorderBarrier ()V DisplayListCanvas.java
+0x2b4 android.view.DisplayListCanvas isHardwareAccelerated ()Z DisplayListCanvas.java
+0x284 android.view.DisplayListCanvas isRecordingFor (Ljava/lang/Object;)Z DisplayListCanvas.java
+0x49c android.view.DisplayListCanvas onPostDraw ()V DisplayListCanvas.java
+0x260 android.view.DisplayListCanvas onPreDraw (Landroid/graphics/Rect;)V DisplayListCanvas.java
+0x4b0 android.view.DisplayListCanvas recycle ()V DisplayListCanvas.java
+0x268 android.view.DisplayListCanvas setHighContrastText (Z)V DisplayListCanvas.java
+0x258 android.view.DisplayListCanvas setViewport (II)V DisplayListCanvas.java
+0x190 android.view.InputEventReceiver nativeConsumeBatchedInputEvents (JJ)Z InputEventReceiver.java
+0x45c android.view.RenderNode nGetElevation (J)F RenderNode.java
+0x3d8 android.view.RenderNode nGetTransformMatrix (JJ)V RenderNode.java
+0x468 android.view.RenderNode nGetTranslationZ (J)F RenderNode.java
+0x2cc android.view.RenderNode nHasIdentityMatrix (J)Z RenderNode.java
+0x4e4 android.view.RenderNode nSetAlpha (JF)Z RenderNode.java
+0x4d8 android.view.RenderNode nSetClipToBounds (JZ)Z RenderNode.java
+0x4ac android.view.RenderNode nSetDisplayListData (JJ)V RenderNode.java
+0x4cc android.view.RenderNode nSetHasOverlappingRendering (JZ)Z RenderNode.java
+0x204 android.view.Surface nativeIsValid (J)Z Surface.java
+0x444 android.view.ThreadedRenderer nNotifyFramePending (J)V ThreadedRenderer.java
+0x0 android.view.ThreadedRenderer nSyncAndDrawFrame (J[JI)I ThreadedRenderer.java
+0x224 android.view.ThreadedRenderer updateRootDisplayList (Landroid/view/View;Landroid/view/HardwareRenderer$HardwareDrawCallbacks;)V ThreadedRenderer.java
+0x228 android.view.ThreadedRenderer updateViewTreeDisplayList (Landroid/view/View;)V ThreadedRenderer.java
+0x3ac android.graphics.Movie nDraw (JFFJ)V Movie.java
+0x3a4 android.graphics.Movie draw (Landroid/graphics/Canvas;FF)V Movie.java
+0x39c android.graphics.Movie duration ()I Movie.java
+0x3a0 android.graphics.Movie setTime (I)Z Movie.java
+0x80 android.view.DisplayEventReceiver dispatchVsync (JII)V DisplayEventReceiver.java
+0x434 android.view.DisplayEventReceiver nativeScheduleVsync (J)V DisplayEventReceiver.java
+0x38c ru.spbau.mit.foodmanager.GifImageView onDraw (Landroid/graphics/Canvas;)V GifImageView.java
+0x348 com.android.tools.fd.runtime.IncrementalClassLoader findClass (Ljava/lang/String;)Ljava/lang/Class; IncrementalClassLoader.java
+0x34c com.android.tools.fd.runtime.IncrementalClassLoader$DelegateClassLoader findClass (Ljava/lang/String;)Ljava/lang/Class; IncrementalClassLoader.java
+0x2fc android.support.v7.app.AppCompatActivity getResources ()Landroid/content/res/Resources; AppCompatActivity.java
+0x304 android.support.v7.app.AppCompatDelegate isCompatVectorFromResourcesEnabled ()Z AppCompatDelegate.java
+0x300 android.support.v7.widget.VectorEnabledTintResources shouldBeUsed ()Z VectorEnabledTintResources.java
+*end
+SLOW & U
+ l
+ l (
+ l E I
+ l V a
+ l w |
+ l
+ l
+ l ^ e
+ l u z
+ l
+ l
+ l
+ l
+ l
+ l
#
+ l F K
+ l V [
+ l d h
+ l r w
+ l
+ l
+ l !
+ l 3 9
+ l H N
+ l [ `
+ l m t
+ l
+ l
+ l " /
+ l G P
+ l ` f
+ l
s y
+ l
+ l
+ l
+ l g r
+ l
+ l
+ l
+ l
+ l
+ l
+
+ l
+
+ l %
+ *
+ l 2
+ 7
+ l g
+ r
+ l
+
+ l
+
+ l
+ l + 2
+ l B I
+ l V ]
+ l i p
+ l }
+ l ,
+ l
+ l
+ l
+ l
+ l
+ l
+ l
'
+ l Q
X
+ l c
h
+ l p
u
+ l
+ l
+ l
+ l
+ l
+ l
+ l
+ l
+ l
+ l 8 B
+ l
+ l
+ l
+ l
+ l
+ l
+ l !
+ l $ +!
+ l 8 @!
+ l L T!
+ l ` h!
+ l s {!
+ l !
+ l !
+ l !
+ l !
+ l "
+ l "
+ l " ("
+ l 0 U"
+ l "
+ l "
+ l "
+ l V#
+ l
} #
+ l
#
+ l
#
+ l #
+ l ^ g$
+ l r x$
+ l $
+ l $
+ l $
+ l '%
+ l \ g%
+ l v %
+ l %
+ l %
+ l %
+ l %
+ l K T&
+ l &
+ l &
+ l &
+ l '
+ l
+ '
+ l 2 :'
+ l _ u'
+ l '
+ l '
+ l
(
+ l &(
+ l . Q(
+ l (
+ l 8 D)
+ l )
+ l )
+ l )
+ l
)
+ l
)
+ l
*
+ l #*
+ l [ c*
+ l m t*
+ l { *
+ l *
+ l *
+ l +
+ l R \+
+ l g n+
+ l v }+
+ l +
+ l +
+ l +
+ l +
+ l ,
+ l $,
+ l , 3,
+ l 9 @,
+ l F M,
+ l U \,
+ l z ,
+ l ,
+ l ,
+ l ,
+ l ,
+ l ,
+ l -
+ l D L-
+ l r z-
+ l -
+ l -
+ l
-
+ l
-
+ l
-
+ l -
+ ! .
+ % .
+ ( ..
+ , D.
+ - _.
+ ) m.
+ 1 + ~.
+ 5 ; .
+ 9 J .
+ = Y .
+ A g .
+ D y .
+ E +/
+ ( @/
+ , N/
+ - \/
+ ) j/
+ I $ w/
+ H 4 /
+ L O /
+ M o /
+ P /
+ Q /
+ I /
+ ( /
+ ,
0
+ - 0
+ ) (0
+ U 50
+ Y C0
+ ] Q0
+ a a0
+ d % b2
+ e K" 2
+ l ! 2
+ i 2
+ l ! 3
+ l " !3
+ l t" 2
+ l *" 13
+ m Q# 3
+ p m# 3
+ t # 3
+ l g" 4
+ x $ \4
+ l " ]4
+ | +$ r4
+ } D$ 4
+ y [$ 4
+ u$ 4
+
4
+ $ 4
+ 4
+ L $ 4
+ l
# 4
+ , 4
+ ; 4
+ l ^# 4
+ M $ 5
+ I 5
+ l p#
5
+ $ 5
+ l ~# 5
+ $ !5
+ l # )5
+ $ 05
+ l # 85
+ $ ?5
+ N5
+ % e5
+ l # 5
+ {5
+ M% 5
+ d% 5
+ 5
+ u% 5
+ l 2$ 5
+ % 5
+ 5
+ l P$ 5
+ % 6
+ K 6
+ l s$ 6
+ % #6
+ d '7
+ m8
+ 8
+ 8
+ 8
+ 9 8
+ l $ 9
+ l $ 9
+ S /9
+ l $ t9
+ 9
+ &