diff --git a/.gitignore b/.gitignore index ccf2efe..01afe3d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,12 @@ +<<<<<<< HEAD +.gradle +/local.properties +/.idea/workspace.xml +/.idea/libraries +.DS_Store +/build +/captures +======= # Built application files *.apk *.ap_ @@ -25,3 +34,4 @@ proguard/ # Log Files *.log +>>>>>>> ac8e0a393d1ea8a8d5f7401afa3b0f8ca0829d02 diff --git a/.idea/.name b/.idea/.name new file mode 100644 index 0000000..d388799 --- /dev/null +++ b/.idea/.name @@ -0,0 +1 @@ +robinhood_hackathon \ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..9a8b7e5 --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,22 @@ + + + + + \ No newline at end of file diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml new file mode 100644 index 0000000..e7bedf3 --- /dev/null +++ b/.idea/copyright/profiles_settings.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml new file mode 100644 index 0000000..1bbc21d --- /dev/null +++ b/.idea/gradle.xml @@ -0,0 +1,18 @@ + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..5689cf8 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + 1.7 + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..eabd445 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..6564d52 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Fe_NYC.iml b/Fe_NYC.iml new file mode 100644 index 0000000..754edab --- /dev/null +++ b/Fe_NYC.iml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + \ 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/app.iml b/app/app.iml new file mode 100644 index 0000000..dd304bc --- /dev/null +++ b/app/app.iml @@ -0,0 +1,151 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 0000000..4fa965d --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,36 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 22 + buildToolsVersion "22.0.1" + + defaultConfig { + applicationId "abassawo.c4q.nyc.fe_nyc" + minSdkVersion 15 + targetSdkVersion 22 + versionCode 1 + versionName "1.0" + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } +} + +dependencies { + compile fileTree(dir: 'libs', include: ['*.jar']) + compile 'com.android.support:recyclerview-v7:22.2.0' + compile 'com.android.support:appcompat-v7:22.2.1' + compile 'com.android.support:design:22.2.0' + compile 'com.jakewharton:butterknife:7.0.1' + compile 'com.android.support:support-v4:22.2.1' + compile 'com.google.android.gms:play-services:7.5.0' + compile 'com.bignerdranch.android:expandablerecyclerview:1.0.3' + compile 'org.achartengine:achartengine:1.2.0' + compile 'com.squareup.okhttp:okhttp-urlconnection:2.0.0' + compile 'com.squareup.okhttp:okhttp:2.0.0' + compile files('libs/json-simple-1.1.1 (1).jar') + +} diff --git a/app/libs/json-simple-1.1.1 (1).jar b/app/libs/json-simple-1.1.1 (1).jar new file mode 100644 index 0000000..66347a6 Binary files /dev/null and b/app/libs/json-simple-1.1.1 (1).jar differ diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..dcb04d2 --- /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 /Users/c4q-Abass/Library/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/abassawo/c4q/nyc/fe_nyc/ApplicationTest.java b/app/src/androidTest/java/abassawo/c4q/nyc/fe_nyc/ApplicationTest.java new file mode 100644 index 0000000..7b0c3e1 --- /dev/null +++ b/app/src/androidTest/java/abassawo/c4q/nyc/fe_nyc/ApplicationTest.java @@ -0,0 +1,13 @@ +package abassawo.c4q.nyc.fe_nyc; + +import android.app.Application; +import android.test.ApplicationTestCase; + +/** + * Testing Fundamentals + */ +public class ApplicationTest extends ApplicationTestCase { + public ApplicationTest() { + super(Application.class); + } +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..64d764a --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,55 @@ + + + android:theme="@style/Theme.DesignDemo" > + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/assets/chart.html b/app/src/main/assets/chart.html new file mode 100644 index 0000000..0fe4a60 --- /dev/null +++ b/app/src/main/assets/chart.html @@ -0,0 +1,45 @@ + + + + + + + + + +
+ + \ No newline at end of file diff --git a/app/src/main/java/abassawo/c4q/nyc/fe_nyc/Account.java b/app/src/main/java/abassawo/c4q/nyc/fe_nyc/Account.java new file mode 100644 index 0000000..5403f6c --- /dev/null +++ b/app/src/main/java/abassawo/c4q/nyc/fe_nyc/Account.java @@ -0,0 +1,35 @@ +package abassawo.c4q.nyc.fe_nyc; + +/** + * Created by c4q-Abass on 8/1/15. + */ +public class Account { + + public static double getSpendable() { + return spendable; + } + + private static double savingGoal; + private static double spendable; + private static double currentSavings; + private static double income; + private static double expenses; + private static double foodPct, rentPct, miscPct, transPct; + private String name; + + public static void main(String[] args) { + //fixme + savingGoal = .20 * income; + rentPct = .3 * income; + foodPct = .09 * income; + miscPct = .10 * income; + transPct = .20 * income; + expenses = rentPct + foodPct + miscPct + transPct; + spendable = income - (expenses + savingGoal); //full. for daily divide this by 30. + } + + public Account(double income){ + this.income = income; + + } +} diff --git a/app/src/main/java/abassawo/c4q/nyc/fe_nyc/AlertDialogFragment.java b/app/src/main/java/abassawo/c4q/nyc/fe_nyc/AlertDialogFragment.java new file mode 100644 index 0000000..12f82ce --- /dev/null +++ b/app/src/main/java/abassawo/c4q/nyc/fe_nyc/AlertDialogFragment.java @@ -0,0 +1,28 @@ +package abassawo.c4q.nyc.fe_nyc; + +import android.app.AlertDialog; +import android.app.Dialog; +import android.app.DialogFragment; +import android.content.Context; +import android.os.Bundle; + +/** + * Created by c4q-tashasmith on 8/2/15. + */ +public class AlertDialogFragment extends DialogFragment { + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + Context context = getActivity(); + AlertDialog.Builder builder = new AlertDialog.Builder(context) + .setTitle(context.getString(R.string.error_title)) + .setMessage(context.getString(R.string.error_message)) + .setPositiveButton(context.getString(R.string.error_ok_button_text), null); //close dialog after click + + AlertDialog dialog = builder.create(); + return dialog; + } + + } + + diff --git a/app/src/main/java/abassawo/c4q/nyc/fe_nyc/BudgetViewFragment.java b/app/src/main/java/abassawo/c4q/nyc/fe_nyc/BudgetViewFragment.java new file mode 100644 index 0000000..a7c52bb --- /dev/null +++ b/app/src/main/java/abassawo/c4q/nyc/fe_nyc/BudgetViewFragment.java @@ -0,0 +1,76 @@ + +package abassawo.c4q.nyc.fe_nyc; + +import android.annotation.SuppressLint; +import android.app.Activity; +import android.graphics.Color; +import android.graphics.drawable.AnimationDrawable; +import android.net.Uri; +import android.os.Bundle; + +import android.support.annotation.Nullable; +import android.support.v4.app.Fragment; + +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.animation.AccelerateDecelerateInterpolator; +import android.webkit.JavascriptInterface; +import android.webkit.WebSettings; +import android.webkit.WebView; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.Toast; + +import org.achartengine.ChartFactory; +import org.achartengine.GraphicalView; +import org.achartengine.model.CategorySeries; +import org.achartengine.model.SeriesSelection; +import org.achartengine.renderer.DefaultRenderer; +import org.achartengine.renderer.SimpleSeriesRenderer; + +public class BudgetViewFragment extends Fragment { + public WebView webView; + public ImageView goalImage; + + + @SuppressLint("SetJavaScriptEnabled") + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + final View myInflatedView = inflater.inflate(R.layout.fragment_budget_view, container, false); + + goalImage = (ImageView)myInflatedView.findViewById(R.id.goal); + goalImage.setBackgroundResource(R.drawable.goal_animation); + AnimationDrawable frameAnimation = (AnimationDrawable) goalImage.getBackground(); + frameAnimation.start(); + + webView = (WebView) myInflatedView.findViewById(R.id.web); + webView.addJavascriptInterface(new WebAppInterface(), "Android"); + + webView.getSettings().setJavaScriptEnabled(true); + webView.loadUrl("file:///android_asset/chart.html"); + + return myInflatedView; + } + + + public class WebAppInterface { + + @JavascriptInterface + public int getNum1() { + return 320; + } + + @JavascriptInterface + public int getNum2() { + return 1285; + } + + @JavascriptInterface + public int getNum3() { + return 115; + } + + } +} + diff --git a/app/src/main/java/abassawo/c4q/nyc/fe_nyc/ExpenseFragment.java b/app/src/main/java/abassawo/c4q/nyc/fe_nyc/ExpenseFragment.java new file mode 100644 index 0000000..2c99dc8 --- /dev/null +++ b/app/src/main/java/abassawo/c4q/nyc/fe_nyc/ExpenseFragment.java @@ -0,0 +1,80 @@ + +package abassawo.c4q.nyc.fe_nyc; + +import android.app.Activity; +import android.net.Uri; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + + +/** + * A simple {@link Fragment} subclass. + * Activities that contain this fragment must implement the + * {@link ExpenseFragment.OnFragmentInteractionListener} interface + * to handle interaction events. + * Use the {@link ExpenseFragment#newInstance} factory method to + * create an instance of this fragment. + */ +public class ExpenseFragment extends Fragment { + // TODO: Rename parameter arguments, choose names that match + // the fragment initialization parameters, e.g. ARG_ITEM_NUMBER + private static final String ARG_PARAM1 = "param1"; + private static final String ARG_PARAM2 = "param2"; + + + // TODO: Rename and change types of parameters + private String mParam1; + private String mParam2; + + /** + * Use this factory method to create a new instance of + * this fragment using the provided parameters. + * + * @param param1 Parameter 1. + * @param param2 Parameter 2. + * @return A new instance of fragment ExpenseFragment. + */ + // TODO: Rename and change types and number of parameters + public static ExpenseFragment newInstance(String param1, String param2) { + ExpenseFragment fragment = new ExpenseFragment(); + Bundle args = new Bundle(); + args.putString(ARG_PARAM1, param1); + args.putString(ARG_PARAM2, param2); + fragment.setArguments(args); + return fragment; + } + + + public ExpenseFragment() { + // Required empty public constructor + } + + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + if (getArguments() != null) { + mParam1 = getArguments().getString(ARG_PARAM1); + mParam2 = getArguments().getString(ARG_PARAM2); + } + } + + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + // Inflate the layout for this fragment + final View myInflatedView = inflater.inflate(R.layout.fragment_expense, container, false); + + return myInflatedView; + } + + + + + + +} diff --git a/app/src/main/java/abassawo/c4q/nyc/fe_nyc/LandingPageActivity.java b/app/src/main/java/abassawo/c4q/nyc/fe_nyc/LandingPageActivity.java new file mode 100644 index 0000000..ea568f5 --- /dev/null +++ b/app/src/main/java/abassawo/c4q/nyc/fe_nyc/LandingPageActivity.java @@ -0,0 +1,22 @@ +package abassawo.c4q.nyc.fe_nyc; + + +import android.app.Activity; +import android.os.Bundle; +import android.support.v4.app.FragmentManager; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +/** + * Created by c4q-Abass on 8/1/15. + */ +public class LandingPageActivity extends Activity { + + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_landing_page); + } +} diff --git a/app/src/main/java/abassawo/c4q/nyc/fe_nyc/LanguageFragment.java b/app/src/main/java/abassawo/c4q/nyc/fe_nyc/LanguageFragment.java new file mode 100644 index 0000000..e68c29e --- /dev/null +++ b/app/src/main/java/abassawo/c4q/nyc/fe_nyc/LanguageFragment.java @@ -0,0 +1,82 @@ +package abassawo.c4q.nyc.fe_nyc; + + +import android.support.v4.app.Fragment; + + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.annotation.TargetApi; + + +import android.app.LoaderManager.LoaderCallbacks; +import android.content.ContentResolver; +import android.content.CursorLoader; +import android.content.Loader; +import android.database.Cursor; +import android.net.Uri; +import android.os.AsyncTask; + +import android.os.Bundle; + +import android.support.v4.app.FragmentManager; +import android.text.TextUtils; + +import android.os.Bundle; +import android.support.v4.app.Fragment; + +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + + +import android.support.annotation.Nullable; + +import android.support.v4.app.Fragment; +import android.text.TextUtils; +import android.view.KeyEvent; +import android.view.LayoutInflater; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.ViewGroup; +import android.view.inputmethod.EditorInfo; +import android.widget.ArrayAdapter; +import android.widget.AutoCompleteTextView; +import android.widget.Button; +import android.widget.EditText; +import android.widget.Toast; + +import com.google.android.gms.common.SignInButton; + +import java.util.List; + +import butterknife.Bind; +import butterknife.ButterKnife; + + +/** + * A login screen that offers login via email/password and via Google+ sign in. + *

+ * ************ IMPORTANT SETUP NOTES: ************ + * In order for Google+ sign in to work with your app, you must first go to: + * https://developers.google.com/+/mobile/android/getting-started#step_1_enable_the_google_api + * and follow the steps in "Step 1" to create an OAuth 2.0 client for your package. + */ + + +public class LanguageFragment extends Fragment { + + + public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + + final View myInflatedView = inflater.inflate(R.layout.fragment_language, container, false); + ButterKnife.bind(this, myInflatedView); + + + + return myInflatedView; + } + + + +} \ No newline at end of file diff --git a/app/src/main/java/abassawo/c4q/nyc/fe_nyc/LoginFragment.java b/app/src/main/java/abassawo/c4q/nyc/fe_nyc/LoginFragment.java new file mode 100644 index 0000000..cccb51c --- /dev/null +++ b/app/src/main/java/abassawo/c4q/nyc/fe_nyc/LoginFragment.java @@ -0,0 +1,139 @@ +package abassawo.c4q.nyc.fe_nyc; + +import android.support.v4.app.Fragment; + + +import android.os.AsyncTask; + +import android.os.Bundle; + +import android.support.v4.app.FragmentManager; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import android.widget.Button; + +import java.util.List; + +import butterknife.Bind; +import butterknife.ButterKnife; + + +/** + * A login screen that offers login via email/password and via Google+ sign in. + *

+ * ************ IMPORTANT SETUP NOTES: ************ + * In order for Google+ sign in to work with your app, you must first go to: + * https://developers.google.com/+/mobile/android/getting-started#step_1_enable_the_google_api + * and follow the steps in "Step 1" to create an OAuth 2.0 client for your package. + */ + + +public class LoginFragment extends Fragment { + + @Bind(R.id.email_sign_in_button) + Button signInBtn; + /** + * A dummy authentication store containing known user names and passwords. + * TODO: remove after connecting to a real authentication system. + */ + + public LoginFragment(){ + + } + + private static final String[] DUMMY_CREDENTIALS = new String[]{ + "foo@example.com:hello", "bar@example.com:world" + }; + /** + * Keep track of the login task to ensure we can cancel it if requested. + */ + private UserLoginTask mAuthTask = null; + + + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.fragment_homescreen, container, false); + ButterKnife.bind(this, view); + signInBtn.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + FragmentManager fragmentManager = getActivity().getSupportFragmentManager(); + fragmentManager.beginTransaction() + .replace(R.id.main_container, new ExpenseFragment()) + .addToBackStack(null) //allows user to press back button and return to previous fragment + .commit(); + + } + }); + return view; + } + + + + private boolean isEmailValid(String email) { + //TODO: Replace this with your own logic + return email.contains("@"); + } + + private boolean isPasswordValid(String password) { + //TODO: Replace this with your own logic + return password.length() > 4; + } + + /** + * Shows the progress UI and hides the login form. + */ + + + + private void addEmailsToAutoComplete(List emailAddressCollection) { + //Create adapter to tell the AutoCompleteTextView what to show in its dropdown list. + // ArrayAdapter adapter = +// new ArrayAdapter(LoginActivity.this, +// android.R.layout.simple_dropdown_item_1line, emailAddressCollection); + + // mEmailView.setAdapter(adapter); + } + + /** + * Represents an asynchronous login/registration task used to authenticate + * the user. + */ + public class UserLoginTask extends AsyncTask { + + private final String mEmail; + private final String mPassword; + + UserLoginTask(String email, String password) { + mEmail = email; + mPassword = password; + } + + @Override + protected Boolean doInBackground(Void... params) { + // TODO: attempt authentication against a network service. + + try { + // Simulate network access. + Thread.sleep(2000); + } catch (InterruptedException e) { + return false; + } + + for (String credential : DUMMY_CREDENTIALS) { + String[] pieces = credential.split(":"); + if (pieces[0].equals(mEmail)) { + // Account exists, return true if the password matches. + return pieces[1].equals(mPassword); + } + } + + // TODO: register the new account here. + return true; + } + + } +} diff --git a/app/src/main/java/abassawo/c4q/nyc/fe_nyc/MainActivity.java b/app/src/main/java/abassawo/c4q/nyc/fe_nyc/MainActivity.java new file mode 100644 index 0000000..c8bf26a --- /dev/null +++ b/app/src/main/java/abassawo/c4q/nyc/fe_nyc/MainActivity.java @@ -0,0 +1,156 @@ +package abassawo.c4q.nyc.fe_nyc; + +import android.support.v7.app.ActionBarDrawerToggle; +import android.support.v7.app.AppCompatActivity; +import android.content.Intent; +import android.support.design.widget.NavigationView; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentManager; +import android.support.v4.widget.DrawerLayout; +import android.support.v7.app.ActionBar; +import android.os.Bundle; +import android.view.Menu; +import android.view.MenuItem; +import android.app.Activity; +import android.view.View; +import android.widget.EditText; +import android.widget.Toast; + +import butterknife.Bind; +import butterknife.ButterKnife; + + + +public class MainActivity extends AppCompatActivity { + @Bind(R.id.drawer_layout) DrawerLayout mDrawerLayout; + private CharSequence mTitle; + NavigationView navigationView; + private ActionBarDrawerToggle mDrawerToggle; + + + private FragmentManager fragmentManager; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + ButterKnife.bind(this); + mTitle = getTitle(); + mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout, R.string.app_name, R.string.app_name) { + @Override + public void onDrawerOpened(View drawerView) { + super.onDrawerOpened(drawerView); + + invalidateOptionsMenu(); + } + }; + + + navigationView = (NavigationView) findViewById(R.id.nav_view); + if (navigationView != null) { + setupDrawerContent(navigationView); + } + + final ActionBar actionBar = getSupportActionBar(); + actionBar.isHideOnContentScrollEnabled(); + actionBar.setHomeAsUpIndicator(R.drawable.ic_menu); + actionBar.setTitle(getString(R.string.app_name)); + actionBar.setLogo(R.drawable.fe_nyc_logo); + actionBar.setIcon(R.drawable.fe_nyc_logo); + actionBar.setDisplayHomeAsUpEnabled(true); + + + + fragmentManager = getSupportFragmentManager(); + +// + FragmentManager fragmentManager = getSupportFragmentManager(); + + fragmentManager.beginTransaction() + .add(R.id.main_container, new LanguageFragment()) + .addToBackStack(null) //allows user to press back button and return to previous fragment + .commit(); + + } + + + + + public void submitClick(View v){ + fragmentManager.beginTransaction() + .add(R.id.main_container, new BudgetViewFragment()) + .addToBackStack(null) //allows user to press back button and return to previous fragment + .commit(); + + } + + + private void setupDrawerContent(NavigationView navigationView) { + fragmentManager = getSupportFragmentManager(); + navigationView.setNavigationItemSelectedListener( + new NavigationView.OnNavigationItemSelectedListener() { + @Override + public boolean onNavigationItemSelected(MenuItem menuItem) { + menuItem.setChecked(true); + if (menuItem.getItemId() == R.id.nav_expense) { + fragmentManager.beginTransaction() + .replace(R.id.main_container, new BudgetViewFragment())//allows user to press back button and return to previous fragment + .commit(); + } else if (menuItem.getItemId() == R.id.nav_wallet) { + fragmentManager.beginTransaction() + .replace(R.id.main_container, new WalletFragment()) + //allows user to press back button and return to previous fragment + .commit(); + } else if (menuItem.getItemId() == R.id.nav_resources) { + Intent resourceIntent = new Intent(getApplicationContext(), ResourceActivity.class); + //resourceIntent.putExtra("zip", zip); + startActivity(resourceIntent); + } else if ((menuItem.getItemId() == R.id.nav_settings)) { + Intent intent = new Intent(getApplicationContext(), SettingsActivity.class); + startActivity(intent); + } else if (menuItem.getItemId() == R.id.nav_connect) { + Intent intent = new Intent(MainActivity.this, VenmoWebViewActivity.class); + startActivityForResult(intent, 1); + } + mDrawerLayout.closeDrawers(); + return true; + } + }); + } + + + + public void missionIntent(View v){ + Intent mission = new Intent(MainActivity.this, MissionActivity.class); + startActivity(mission); + } + + + public boolean onCreateOptionsMenu(Menu menu) { + // Inflate the menu; this adds items to the action bar if it is present. + getMenuInflater().inflate(R.menu.menu_main, menu); + return super.onCreateOptionsMenu(menu); + + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + // Handle action bar item clicks here. The action bar will + // automatically handle clicks on the Home/Up button, so long + // as you specify a parent activity in AndroidManifest.xml. + int id = item.getItemId(); + + if (mDrawerToggle.onOptionsItemSelected(item)) { + return true; + } + if (id == R.id.action_log_out) { + return true; + } + + return super.onOptionsItemSelected(item); + } + +} + + + diff --git a/app/src/main/java/abassawo/c4q/nyc/fe_nyc/MissionActivity.java b/app/src/main/java/abassawo/c4q/nyc/fe_nyc/MissionActivity.java new file mode 100644 index 0000000..427834b --- /dev/null +++ b/app/src/main/java/abassawo/c4q/nyc/fe_nyc/MissionActivity.java @@ -0,0 +1,38 @@ +package abassawo.c4q.nyc.fe_nyc; + +import android.support.v7.app.ActionBarActivity; +import android.os.Bundle; +import android.view.Menu; +import android.view.MenuItem; + + +public class MissionActivity extends ActionBarActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.fragment_mission); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + // Inflate the menu; this adds items to the action bar if it is present. + getMenuInflater().inflate(R.menu.menu_mission, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + // Handle action bar item clicks here. The action bar will + // automatically handle clicks on the Home/Up button, so long + // as you specify a parent activity in AndroidManifest.xml. + int id = item.getItemId(); + + //noinspection SimplifiableIfStatement + if (id == R.id.action_settings) { + return true; + } + + return super.onOptionsItemSelected(item); + } +} diff --git a/app/src/main/java/abassawo/c4q/nyc/fe_nyc/Program.java b/app/src/main/java/abassawo/c4q/nyc/fe_nyc/Program.java new file mode 100644 index 0000000..a6275ac --- /dev/null +++ b/app/src/main/java/abassawo/c4q/nyc/fe_nyc/Program.java @@ -0,0 +1,77 @@ +package abassawo.c4q.nyc.fe_nyc; + +import android.view.View; +import android.widget.ImageView; +import android.widget.TextView; + +import com.bignerdranch.expandablerecyclerview.Model.ParentObject; +import com.bignerdranch.expandablerecyclerview.ViewHolder.ParentViewHolder; + +import java.util.List; + +/** + * Created by c4q-tashasmith on 8/2/15. + */ +//parent class +public class Program implements ParentObject { + private List mChildObjectList; + private String providerName; + private String providerDescription; + private String website; + private String mParentText; + + + public String getProviderDescription() { + return providerDescription; + } + + public void setProviderDescription(String providerDescription) { + this.providerDescription = providerDescription; + } + + public String getProviderName() { + return providerName; + } + + public void setProviderName(String providerName) { + this.providerName = providerName; + } + + public String getWebsite() { + return website; + } + + public void setWebsite(String website) { + this.website = website; + } + + @Override + public String toString() { + return providerName ; + } + + public String getParentText() { + return mParentText; + } + public void setParentText(String parentText) { + mParentText = parentText; + } + + public String getBody(){ + return " Description: " + providerDescription + '\'' + + " Website: " + website + '\''; + } + + @Override + public List getChildObjectList() { + return mChildObjectList; + } + + @Override + public void setChildObjectList(List list) { + mChildObjectList = list; + } + + + +} diff --git a/app/src/main/java/abassawo/c4q/nyc/fe_nyc/ResAdapter.java b/app/src/main/java/abassawo/c4q/nyc/fe_nyc/ResAdapter.java new file mode 100644 index 0000000..dd1c686 --- /dev/null +++ b/app/src/main/java/abassawo/c4q/nyc/fe_nyc/ResAdapter.java @@ -0,0 +1,59 @@ +package abassawo.c4q.nyc.fe_nyc; + +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import java.util.List; + +/** + * Created by c4q-Abass on 8/3/15. + */ +public class ResAdapter extends RecyclerView.Adapter { + + private List progList; + + @Override + public int getItemViewType(int position) { + return super.getItemViewType(position); + } + + public ResAdapter(List progList) { + this.progList = progList; + } + + + @Override + public int getItemCount() { + return progList.size(); + } + + @Override + public void onBindViewHolder(ProgramViewHolder cardTitleViewHolder, int i) { + Program ci = progList.get(i); + cardTitleViewHolder.vName.setText(ci.getProviderName()); + } + + @Override + public ProgramViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) { + View itemView = LayoutInflater. + from(viewGroup.getContext()). + inflate(R.layout.item_resource_list, viewGroup, false); + + return new ProgramViewHolder(itemView); + } + + public static class ProgramViewHolder extends RecyclerView.ViewHolder { + + protected TextView vName; + + + public ProgramViewHolder(View v) { + super(v); + vName = (TextView) v.findViewById(R.id.program_title); + + } + } +} \ No newline at end of file diff --git a/app/src/main/java/abassawo/c4q/nyc/fe_nyc/ResourceActivity.java b/app/src/main/java/abassawo/c4q/nyc/fe_nyc/ResourceActivity.java new file mode 100644 index 0000000..4891477 --- /dev/null +++ b/app/src/main/java/abassawo/c4q/nyc/fe_nyc/ResourceActivity.java @@ -0,0 +1,200 @@ +package abassawo.c4q.nyc.fe_nyc; + +import android.content.Context; +import android.content.Intent; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; +import android.net.Uri; +import android.support.annotation.Nullable; +import android.support.design.widget.Snackbar; +import android.support.v4.widget.DrawerLayout; +import android.support.v7.app.ActionBar; +import android.support.v7.app.ActionBarActivity; +import android.os.Bundle; +import android.support.v7.app.ActionBarDrawerToggle; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.util.AttributeSet; +import android.util.Log; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.webkit.WebSettings; +import android.webkit.WebView; +import android.webkit.WebViewClient; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.ListView; +import android.widget.RelativeLayout; +import android.widget.Toast; + +import com.bignerdranch.expandablerecyclerview.Model.ParentObject; +import com.squareup.okhttp.Call; +import com.squareup.okhttp.Callback; +import com.squareup.okhttp.OkHttpClient; +import com.squareup.okhttp.Request; +import com.squareup.okhttp.Response; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import butterknife.Bind; +import butterknife.ButterKnife; + + +public class ResourceActivity extends ActionBarActivity { + + public static final String TAG = MainActivity.class.getSimpleName(); + private static final String API_KEY = "fb8edd11a14dc07088e183b288c2503c"; + private ActionBarDrawerToggle mDrawerToggle; + private String zipParam; + +// @Bind(R.id.resourceListRV) RecyclerView resourceRV; +@Bind(R.id.drawer_layout) DrawerLayout mDrawerLayout; + @Bind(R.id.resource_List) + ListView mResourceList; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_resource); + ButterKnife.bind(this); + final ActionBar actionBar = getSupportActionBar(); + actionBar.isHideOnContentScrollEnabled(); + //actionBar.setHomeAsUpIndicator(R.drawable.ic_menu); + actionBar.setTitle(getString(R.string.app_name)); + actionBar.setLogo(R.drawable.fe_nyc_logo); + actionBar.setIcon(R.drawable.fe_nyc_logo); + actionBar.setDisplayHomeAsUpEnabled(true); + + zipParam = "10003"; + + + + String foodservicesUrl = "https://searchbertha-hrd.appspot.com/_ah/api/search/v1/zipcodes/" + zipParam + "/programs?api_key=" + API_KEY + "&serviceTag=food%20pantry"; + getABServices(foodservicesUrl); + + } + + + + public void getABServices(String service) { + if (isNetworkAvailable()) { + OkHttpClient client = new OkHttpClient(); + Request request = new Request.Builder() + .url(service) + .build(); + + Call call = client.newCall(request); + call.enqueue(new Callback() { + @Override + public void onFailure(Request request, IOException e) { + runOnUiThread(new Runnable() { // A new runnable to run on UI thread + @Override + public void run() { + //TODO: add refresh button method here + } + }); + alertUserAboutError(); + } + + @Override + public void onResponse(Response response) throws IOException { + try { + final String jsonData = response.body().string(); + Log.v(TAG, jsonData); + if (response.isSuccessful()) { + runOnUiThread(new Runnable() { + @Override + public void run() { + try { + getCurrentPrograms(jsonData); + } catch (JSONException e) { + e.printStackTrace(); + } + } + }); + } else { + alertUserAboutError(); + } + } catch (IOException e) { + Log.e(TAG, "Exception caught: ", e); + } + } + }); + } else { + Toast.makeText(this, getString(R.string.network_unavailable_message), Toast.LENGTH_LONG).show(); + } + } + + private void updateDisplay(ArrayList programList) { + ResourceListAdapter simpleAdapter = new ResourceListAdapter(getApplicationContext(), R.layout.item_resource_list, programList); + mResourceList.setAdapter(simpleAdapter); + + mResourceList.setOnItemClickListener(new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView adapterView, View view, int position, long l) { + final Program program2 = (Program) (mResourceList.getItemAtPosition(position)); + + Snackbar snackbar = Snackbar.make(view,program2.getProviderDescription(), Snackbar.LENGTH_LONG) + .setAction("->", new View.OnClickListener() { + @Override + public void onClick(View v) { + Intent goToWebIntent= new Intent(Intent.ACTION_VIEW); + goToWebIntent.setData(Uri.parse(program2.getWebsite())); + startActivity(goToWebIntent); + } + }); + + snackbar.show(); + + + } + }); + + } + + private void getCurrentPrograms(String jsonData) throws JSONException { + ArrayList programList = new ArrayList<>(); + JSONObject service = new JSONObject(jsonData); + JSONArray programsArray = service.getJSONArray("programs"); + Log.v(TAG, String.valueOf(programsArray)); + for (int n = 0; n < programsArray.length(); n++) { + JSONObject progObject = programsArray.getJSONObject(n); + Program program = new Program(); + program.setProviderName(progObject.getString("provider_name")); + program.setProviderDescription(progObject.getString("description")); + program.setWebsite(progObject.getString("website_url")); + programList.add(program); + Log.v(TAG, program.getProviderName()); + Log.v(TAG, program.getProviderDescription()); + Log.v(TAG, program.getWebsite()); + } + + updateDisplay(programList); + } + + + + private boolean isNetworkAvailable() { + ConnectivityManager manager = (ConnectivityManager) + getSystemService(Context.CONNECTIVITY_SERVICE); + NetworkInfo networkInfo = manager.getActiveNetworkInfo(); + boolean isAvailable = false; + if (networkInfo != null && networkInfo.isConnected()) { + isAvailable = true; + } + return isAvailable; + } + + private void alertUserAboutError() { + AlertDialogFragment dialog = new AlertDialogFragment(); + dialog.show(getFragmentManager(), "error_dialog"); + } + +} \ No newline at end of file diff --git a/app/src/main/java/abassawo/c4q/nyc/fe_nyc/ResourceListAdapter.java b/app/src/main/java/abassawo/c4q/nyc/fe_nyc/ResourceListAdapter.java new file mode 100644 index 0000000..7e6adf6 --- /dev/null +++ b/app/src/main/java/abassawo/c4q/nyc/fe_nyc/ResourceListAdapter.java @@ -0,0 +1,58 @@ +package abassawo.c4q.nyc.fe_nyc; + +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.TextView; +import java.util.ArrayList; +import java.util.List; + +/** + * Created by c4q-tashasmith on 8/2/15. + */ +public class ResourceListAdapter extends ArrayAdapter { + private ArrayList programs; + + public ResourceListAdapter(Context context, int resource, ArrayList programs) { + super(context, resource, programs); + this.programs = programs; + + } + + @Override + public Program getItem(int position) { + return programs.get(position); + } + + @Override + public int getPosition(Program item) { + return programs.indexOf(item); + } + + @Override + public int getCount() { + return programs.size(); + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + Program program = getItem(position); + + if (convertView == null) { + convertView = LayoutInflater.from(getContext()).inflate(R.layout.item_resource_list, parent, false); + } + + TextView providerDisplay = (TextView) convertView.findViewById(R.id.providerDisplay); + TextView descriptionDisplay = (TextView) convertView.findViewById(R.id.descriptionDisplay); + TextView webDisplay = (TextView) convertView.findViewById(R.id.webDisplay); + + providerDisplay.setText(program.getProviderName()); + descriptionDisplay.setText(program.getProviderDescription()); + webDisplay.setText(program.getWebsite()); + + return convertView; + + } +} diff --git a/app/src/main/java/abassawo/c4q/nyc/fe_nyc/SettingsActivity.java b/app/src/main/java/abassawo/c4q/nyc/fe_nyc/SettingsActivity.java new file mode 100644 index 0000000..cc5759a --- /dev/null +++ b/app/src/main/java/abassawo/c4q/nyc/fe_nyc/SettingsActivity.java @@ -0,0 +1,259 @@ +package abassawo.c4q.nyc.fe_nyc; + +import android.annotation.TargetApi; +import android.content.Context; +import android.content.res.Configuration; +import android.media.Ringtone; +import android.media.RingtoneManager; +import android.net.Uri; +import android.os.Build; +import android.os.Bundle; +import android.preference.ListPreference; +import android.preference.Preference; +import android.preference.PreferenceActivity; +import android.preference.PreferenceCategory; +import android.preference.PreferenceFragment; +import android.preference.PreferenceManager; +import android.preference.RingtonePreference; +import android.text.TextUtils; + + +import java.util.List; + +/** + * A {@link PreferenceActivity} that presents a set of application settings. On + * handset devices, settings are presented as a single list. On tablets, + * settings are split by category, with category headers shown to the left of + * the list of settings. + *

+ * See + * Android Design: Settings for design guidelines and the Settings + * API Guide for more information on developing a Settings UI. + */ +public class SettingsActivity extends PreferenceActivity { + /** + * Determines whether to always show the simplified settings UI, where + * settings are presented in a single list. When false, settings are shown + * as a master/detail two-pane view on tablets. When true, a single pane is + * shown on tablets. + */ + private static final boolean ALWAYS_SIMPLE_PREFS = false; + + + @Override + protected void onPostCreate(Bundle savedInstanceState) { + super.onPostCreate(savedInstanceState); + + setupSimplePreferencesScreen(); + } + + /** + * Shows the simplified settings UI if the device configuration if the + * device configuration dictates that a simplified, single-pane UI should be + * shown. + */ + private void setupSimplePreferencesScreen() { + if (!isSimplePreferences(this)) { + return; + } + + // In the simplified UI, fragments are not used at all and we instead + // use the older PreferenceActivity APIs. + + // Add 'general' preferences. + addPreferencesFromResource(R.xml.pref_general); + + // Add 'notifications' preferences, and a corresponding header. + PreferenceCategory fakeHeader = new PreferenceCategory(this); + fakeHeader.setTitle(R.string.pref_header_notifications); + getPreferenceScreen().addPreference(fakeHeader); + addPreferencesFromResource(R.xml.pref_notification); + + // Add 'data and sync' preferences, and a corresponding header. + fakeHeader = new PreferenceCategory(this); + fakeHeader.setTitle(R.string.pref_header_data_sync); + getPreferenceScreen().addPreference(fakeHeader); + addPreferencesFromResource(R.xml.pref_data_sync); + + // Bind the summaries of EditText/List/Dialog/Ringtone preferences to + // their values. When their values change, their summaries are updated + // to reflect the new value, per the Android Design guidelines. + bindPreferenceSummaryToValue(findPreference("example_text")); + //bindPreferenceSummaryToValue(findPreference("example_list")); +// bindPreferenceSummaryToValue(findPreference("notifications_new_message_ringtone")); +// bindPreferenceSummaryToValue(findPreference("sync_frequency")); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean onIsMultiPane() { + return isXLargeTablet(this) && !isSimplePreferences(this); + } + + /** + * Helper method to determine if the device has an extra-large screen. For + * example, 10" tablets are extra-large. + */ + private static boolean isXLargeTablet(Context context) { + return (context.getResources().getConfiguration().screenLayout + & Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_XLARGE; + } + + /** + * Determines whether the simplified settings UI should be shown. This is + * true if this is forced via {@link #ALWAYS_SIMPLE_PREFS}, or the device + * doesn't have newer APIs like {@link PreferenceFragment}, or the device + * doesn't have an extra-large screen. In these cases, a single-pane + * "simplified" settings UI should be shown. + */ + private static boolean isSimplePreferences(Context context) { + return ALWAYS_SIMPLE_PREFS + || Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB + || !isXLargeTablet(context); + } + + /** + * {@inheritDoc} + */ + @Override + @TargetApi(Build.VERSION_CODES.HONEYCOMB) + public void onBuildHeaders(List

target) { + if (!isSimplePreferences(this)) { + loadHeadersFromResource(R.xml.pref_headers, target); + } + } + + /** + * A preference value change listener that updates the preference's summary + * to reflect its new value. + */ + private static Preference.OnPreferenceChangeListener sBindPreferenceSummaryToValueListener = new Preference.OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(Preference preference, Object value) { + String stringValue = value.toString(); + + if (preference instanceof ListPreference) { + // For list preferences, look up the correct display value in + // the preference's 'entries' list. + ListPreference listPreference = (ListPreference) preference; + int index = listPreference.findIndexOfValue(stringValue); + + // Set the summary to reflect the new value. + preference.setSummary( + index >= 0 + ? listPreference.getEntries()[index] + : null); + + } else if (preference instanceof RingtonePreference) { + // For ringtone preferences, look up the correct display value + // using RingtoneManager. + if (TextUtils.isEmpty(stringValue)) { + // Empty values correspond to 'silent' (no ringtone). + preference.setSummary(R.string.pref_ringtone_silent); + + } else { + Ringtone ringtone = RingtoneManager.getRingtone( + preference.getContext(), Uri.parse(stringValue)); + + if (ringtone == null) { + // Clear the summary if there was a lookup error. + preference.setSummary(null); + } else { + // Set the summary to reflect the new ringtone display + // name. + String name = ringtone.getTitle(preference.getContext()); + preference.setSummary(name); + } + } + + } else { + // For all other preferences, set the summary to the value's + // simple string representation. + preference.setSummary(stringValue); + } + return true; + } + }; + + /** + * Binds a preference's summary to its value. More specifically, when the + * preference's value is changed, its summary (line of text below the + * preference title) is updated to reflect the value. The summary is also + * immediately updated upon calling this method. The exact display format is + * dependent on the type of preference. + * + * @see #sBindPreferenceSummaryToValueListener + */ + private static void bindPreferenceSummaryToValue(Preference preference) { + // Set the listener to watch for value changes. + preference.setOnPreferenceChangeListener(sBindPreferenceSummaryToValueListener); + + // Trigger the listener immediately with the preference's + // current value. + sBindPreferenceSummaryToValueListener.onPreferenceChange(preference, + PreferenceManager + .getDefaultSharedPreferences(preference.getContext()) + .getString(preference.getKey(), "")); + } + + /** + * This fragment shows general preferences only. It is used when the + * activity is showing a two-pane settings UI. + */ + @TargetApi(Build.VERSION_CODES.HONEYCOMB) + public static class GeneralPreferenceFragment extends PreferenceFragment { + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + addPreferencesFromResource(R.xml.pref_general); + + // Bind the summaries of EditText/List/Dialog/Ringtone preferences + // to their values. When their values change, their summaries are + // updated to reflect the new value, per the Android Design + // guidelines. + bindPreferenceSummaryToValue(findPreference("example_text")); + //bindPreferenceSummaryToValue(findPreference("example_list")); + } + } + + /** + * This fragment shows notification preferences only. It is used when the + * activity is showing a two-pane settings UI. + */ + @TargetApi(Build.VERSION_CODES.HONEYCOMB) + public static class NotificationPreferenceFragment extends PreferenceFragment { + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + addPreferencesFromResource(R.xml.pref_notification); + + // Bind the summaries of EditText/List/Dialog/Ringtone preferences + // to their values. When their values change, their summaries are + // updated to reflect the new value, per the Android Design + // guidelines. + //bindPreferenceSummaryToValue(findPreference("notifications_new_message_ringtone")); + } + } + + /** + * This fragment shows data and sync preferences only. It is used when the + * activity is showing a two-pane settings UI. + */ + @TargetApi(Build.VERSION_CODES.HONEYCOMB) + public static class DataSyncPreferenceFragment extends PreferenceFragment { + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + addPreferencesFromResource(R.xml.pref_data_sync); + + // Bind the summaries of EditText/List/Dialog/Ringtone preferences + // to their values. When their values change, their summaries are + // updated to reflect the new value, per the Android Design + // guidelines. + bindPreferenceSummaryToValue(findPreference("sync_frequency")); + } + } +} diff --git a/app/src/main/java/abassawo/c4q/nyc/fe_nyc/VenmoLibrary.java b/app/src/main/java/abassawo/c4q/nyc/fe_nyc/VenmoLibrary.java new file mode 100755 index 0000000..d30ff32 --- /dev/null +++ b/app/src/main/java/abassawo/c4q/nyc/fe_nyc/VenmoLibrary.java @@ -0,0 +1,202 @@ +package abassawo.c4q.nyc.fe_nyc; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.net.Uri; +import android.util.Base64; +import android.util.Log; + +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; +import org.json.simple.JSONValue; + +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.util.List; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; + +/*VenmoLibrary class and webview were contributed by the Venmo Team. + + +*/ + +public class VenmoLibrary { + private static final String VENMO_PACKAGE = "com.venmo"; + private static final String VENMO_ACTIVITY = "com.venmo.ComposeActivity"; + + public VenmoLibrary() {} + + /** + * Takes the recipients, amount, and note, and returns an Intent object + */ + public static Intent openVenmoPayment(String myAppId, String myAppName, String recipients, + String amount, String note, String txn) { + String venmo_uri = "venmosdk://paycharge?txn=" + txn; + + if (!recipients.equals("")) { + try { + venmo_uri += "&recipients=" + URLEncoder.encode(recipients, "UTF-8"); + } catch (UnsupportedEncodingException e) { + Log.e("venmo_library", "cannot encode recipients"); + } + } + if (!amount.equals("")) { + try { + venmo_uri += "&amount=" + URLEncoder.encode(amount, "UTF-8"); + } catch (UnsupportedEncodingException e) { + Log.e("venmo_library", "cannot encode amount"); + } + } + if (!note.equals("")) { + try { + venmo_uri += "¬e=" + URLEncoder.encode(note, "UTF-8"); + } catch (UnsupportedEncodingException e) { + Log.e("venmo_library", "cannot encode note"); + } + } + + try { + venmo_uri += "&app_id=" + URLEncoder.encode(myAppId, "UTF-8"); + } catch (UnsupportedEncodingException e) { + Log.e("venmo_library", "cannot encode app ID"); + } + + try { + venmo_uri += "&app_name=" + URLEncoder.encode(myAppName, "UTF-8"); + } catch (UnsupportedEncodingException e) { + Log.e("venmo_library", "cannot encode app Name"); + } + + try { + venmo_uri += "&app_local_id=" + URLEncoder.encode("abcd", "UTF-8"); + } catch (UnsupportedEncodingException e) { + Log.e("venmo_library", "cannot encode app local id"); + } + + venmo_uri += "&using_new_sdk=true"; + + venmo_uri = venmo_uri.replaceAll("\\+", "%20"); // use %20 encoding instead of + + + return new Intent(Intent.ACTION_VIEW, Uri.parse(venmo_uri)); + } + + /** + * Called once control has been given back to your app - it takes the signed_payload, decodes + * it, and gives you the response object which gives you details about the transaction - + * whether it was successful, the note, the amount,etc. + */ + public VenmoResponse validateVenmoPaymentResponse(String signed_payload, String app_secret) { + String encoded_signature; + String payload; + if (signed_payload == null) { + return new VenmoResponse(null, null, null, "0"); + } + try { + String[] encodedsig_payload_array = signed_payload.split("\\."); + encoded_signature = encodedsig_payload_array[0]; + payload = encodedsig_payload_array[1]; + } catch (ArrayIndexOutOfBoundsException e) { + return new VenmoResponse(null, null, null, "0"); + } + + String decoded_signature = base64_url_decode(encoded_signature); + + String data; + + // check signature + String expected_sig = hash_hmac(payload, app_secret, "HmacSHA256"); + + VenmoResponse myVenmoResponse; + + if (decoded_signature.equals(expected_sig)) { + //json decode data + data = base64_url_decode(payload); + try { + JSONArray response = (JSONArray) JSONValue.parse(data); + + JSONObject obj = (JSONObject) response.get(0); + + String payment_id = obj.get("payment_id").toString(); + String note = obj.get("note").toString(); + String amount = obj.get("amount").toString(); + String success = obj.get("success").toString(); + + myVenmoResponse = new VenmoResponse(payment_id, note, amount, success); + + } catch (Exception e) { + myVenmoResponse = new VenmoResponse(null, null, null, "0"); + } + } else { + //Signature does NOT match + myVenmoResponse = new VenmoResponse(null, null, null, "0"); + } + + return myVenmoResponse; + + } + + /** + * @return a boolean indicating whether or not the Venmo app is installed on a client's device. + */ + public static boolean isVenmoInstalled(Context context) { + PackageManager packageManager = context.getPackageManager(); + List activities = packageManager.queryIntentActivities(new Intent() + .setComponent(new ComponentName(VENMO_PACKAGE, VENMO_ACTIVITY)), 0); + + return activities.size() == 1 && + VENMO_PACKAGE.equals(activities.get(0).activityInfo.packageName); + } + + private static String hash_hmac(String payload, String app_secret, String algorithm) { + try { + Mac mac = Mac.getInstance(algorithm); + SecretKeySpec secret = new SecretKeySpec(app_secret.getBytes(), algorithm); + mac.init(secret); + byte[] digest = mac.doFinal(payload.getBytes()); + return new String(digest); + } catch (Exception e) { + return ""; + } + } + + private static String base64_url_decode(String payload) { + String payload_modified = payload.replace('-', '+').replace('_', '/').trim(); + return new String(Base64.decode(payload_modified, Base64.DEFAULT)); + } + + /** + * This is the object returned to you after a transaction has gone through. + * It tells you whether it was successful, the amount, te note, and the payment id. + */ + public class VenmoResponse { + private String payment_id, note, amount, success; + + public VenmoResponse(String payment_id, String note, String amount, String success) { + this.payment_id = payment_id; + this.note = note; + this.amount = amount; + this.success = success; + } + + public String getPaymentId() { + return payment_id; + } + + public String getNote() { + return note; + } + + public String getAmount() { + return amount; + } + + public String getSuccess() { + return success; + } + } +} diff --git a/app/src/main/java/abassawo/c4q/nyc/fe_nyc/VenmoWebViewActivity.java b/app/src/main/java/abassawo/c4q/nyc/fe_nyc/VenmoWebViewActivity.java new file mode 100755 index 0000000..c0fce23 --- /dev/null +++ b/app/src/main/java/abassawo/c4q/nyc/fe_nyc/VenmoWebViewActivity.java @@ -0,0 +1,95 @@ +package abassawo.c4q.nyc.fe_nyc; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.view.View; +import android.view.Window; +import android.webkit.WebSettings; +import android.webkit.WebView; +import android.webkit.WebViewClient; +import android.widget.RelativeLayout; + + +public class VenmoWebViewActivity extends Activity { + + Context mContext; + private WebView mVenmoWebView; + String mUrl; + + @Override + public void onCreate(Bundle icicle) { + super.onCreate(icicle); + setContentView(R.layout.venmo_webview); + + mUrl = "https://venmo.com/signup"; + + mContext = this; + + mVenmoWebView = (WebView)getLastNonConfigurationInstance(); + if(mVenmoWebView != null) { //If screen was rotated, don't reload the whole webview + + WebView myWebView = (WebView)findViewById(R.id.venmo_wv); + RelativeLayout parent = (RelativeLayout)myWebView.getParent(); + parent.removeView(myWebView); + + RelativeLayout parent2 = (RelativeLayout)mVenmoWebView.getParent(); + parent2.removeView(mVenmoWebView); + + parent.addView(mVenmoWebView); + } + else { //load the webview + mVenmoWebView = (WebView)findViewById(R.id.venmo_wv); + mVenmoWebView.addJavascriptInterface(new VenmoJavaScriptInterface(this), "VenmoAndroidSDK"); + mVenmoWebView.setScrollBarStyle(View.SCROLLBARS_INSIDE_OVERLAY); + mVenmoWebView.getSettings().setJavaScriptEnabled(true); + mVenmoWebView.getSettings().setUserAgentString("venmo-android-2.0"); + mVenmoWebView.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE); + mVenmoWebView.setWebViewClient(new WebViewClient()); + mVenmoWebView.loadUrl(mUrl); + } + + } + + + /*called right before screen rotates. We store the webview object so it can be recovered when the activity re-starts*/ + @Override + public Object onRetainNonConfigurationInstance() { + return mVenmoWebView; + } + + //This class handles what happens when the user has successfully paid OR if there's an error, and it's time to + //yield control back to your previous activity (the one that opened up this Venmo payment webview). + public class VenmoJavaScriptInterface + { + Context mContext; + Activity mActivity; + + /** Instantiate the interface and set the context */ + VenmoJavaScriptInterface(Context c) { + mContext = c; + mActivity = (Activity)c; + } + + public void paymentSuccessful(String signed_request) { + Intent i = new Intent(); + i.putExtra("signedrequest", signed_request); + mActivity.setResult(mActivity.RESULT_OK, i); + mActivity.finish(); + } + + public void error(String error_message) { + Intent i = new Intent(); + i.putExtra("error_message", error_message); + mActivity.setResult(mActivity.RESULT_OK, i); + mActivity.finish(); + } + + public void cancel() { + Intent i = new Intent(); + mActivity.setResult(mActivity.RESULT_CANCELED); + mActivity.finish(); + } + } +} diff --git a/app/src/main/java/abassawo/c4q/nyc/fe_nyc/WalletFragment.java b/app/src/main/java/abassawo/c4q/nyc/fe_nyc/WalletFragment.java new file mode 100644 index 0000000..6e76867 --- /dev/null +++ b/app/src/main/java/abassawo/c4q/nyc/fe_nyc/WalletFragment.java @@ -0,0 +1,115 @@ +package abassawo.c4q.nyc.fe_nyc; + +import android.content.Intent; +import android.os.Bundle; + +import android.support.annotation.Nullable; +import android.support.v4.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.EditText; +import android.widget.ImageView; +import android.widget.TextView; + + +public class WalletFragment extends Fragment { + String strCash; + boolean addCash; + double cashInWallet = 300, transactionCash, savings, monthlyWallet = 300; + Button add, minus, calculate; + ImageView tree; + TextView cashDisplay, warning; + EditText enterAmount; + + public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + + final View myInflatedView = inflater.inflate(R.layout.fragment_wallet, container, false); + + add = (Button) myInflatedView.findViewById(R.id.add_button); + minus = (Button) myInflatedView.findViewById(R.id.minus_button); + calculate = (Button) myInflatedView.findViewById(R.id.calculate); + tree = (ImageView) myInflatedView.findViewById(R.id.tree); + cashDisplay = (TextView) myInflatedView.findViewById(R.id.wallet_money); + warning = (TextView) myInflatedView.findViewById(R.id.warning); + enterAmount = (EditText) myInflatedView.findViewById(R.id.enter_amount); + + //TODO get cash amount from shared preferences + + cashDisplay.setText(String.valueOf(cashInWallet)); + + minus.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + enterAmount.setVisibility(View.VISIBLE); + calculate.setVisibility(View.VISIBLE); + addCash = false; + } + }); + + add.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + enterAmount.setVisibility(View.VISIBLE); + calculate.setVisibility(View.VISIBLE); + addCash = true; + } + }); + + warning.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + Intent intent = new Intent(getActivity(), ResourceActivity.class); + startActivity(intent); + } + }); + + calculate.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + transactionCash = Double.parseDouble(enterAmount.getText().toString()); + + if (addCash) { + cashInWallet = cashInWallet + transactionCash; + } else { + cashInWallet = cashInWallet - transactionCash; + if (cashInWallet < 0) { + cashInWallet = 0.0; + } + } + cashDisplay.setText(String.valueOf(cashInWallet)); + enterAmount.setText(""); + enterAmount.setVisibility(View.GONE); + calculate.setVisibility(View.GONE); + + double cashPercentage = (cashInWallet / monthlyWallet) * 100; + + if (cashPercentage > 80) { + tree.setBackgroundResource(R.drawable.tree_full); + warning.setVisibility(View.GONE); + } else if (cashPercentage > 60) { + tree.setBackgroundResource(R.drawable.tree_60); + warning.setVisibility(View.GONE); + } else if (cashPercentage > 40) { + tree.setBackgroundResource(R.drawable.tree_40); + warning.setVisibility(View.GONE); + } else if (cashPercentage > 20) { + tree.setBackgroundResource(R.drawable.tree_20); + warning.setVisibility(View.GONE); + } else { + tree.setBackgroundResource(R.drawable.tree_empty); + warning.setVisibility(View.VISIBLE); + + } + } + }); + + return myInflatedView; + } + + +} + + + diff --git a/app/src/main/res/drawable/a_new_car_1.png b/app/src/main/res/drawable/a_new_car_1.png new file mode 100644 index 0000000..f5005b1 Binary files /dev/null and b/app/src/main/res/drawable/a_new_car_1.png differ diff --git a/app/src/main/res/drawable/a_new_car_2.png b/app/src/main/res/drawable/a_new_car_2.png new file mode 100644 index 0000000..02464d6 Binary files /dev/null and b/app/src/main/res/drawable/a_new_car_2.png differ diff --git a/app/src/main/res/drawable/a_new_car_3.png b/app/src/main/res/drawable/a_new_car_3.png new file mode 100644 index 0000000..f6eb3ab Binary files /dev/null and b/app/src/main/res/drawable/a_new_car_3.png differ diff --git a/app/src/main/res/drawable/a_new_car_4.png b/app/src/main/res/drawable/a_new_car_4.png new file mode 100644 index 0000000..ce21855 Binary files /dev/null and b/app/src/main/res/drawable/a_new_car_4.png differ diff --git a/app/src/main/res/drawable/arrow.jpg b/app/src/main/res/drawable/arrow.jpg new file mode 100644 index 0000000..65a75a6 Binary files /dev/null and b/app/src/main/res/drawable/arrow.jpg differ diff --git a/app/src/main/res/drawable/button.xml b/app/src/main/res/drawable/button.xml new file mode 100644 index 0000000..bfcef6d --- /dev/null +++ b/app/src/main/res/drawable/button.xml @@ -0,0 +1,18 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/button_normal.jpg b/app/src/main/res/drawable/button_normal.jpg new file mode 100644 index 0000000..364ca23 Binary files /dev/null and b/app/src/main/res/drawable/button_normal.jpg differ diff --git a/app/src/main/res/drawable/button_pressed.jpg b/app/src/main/res/drawable/button_pressed.jpg new file mode 100644 index 0000000..c5e4cf5 Binary files /dev/null and b/app/src/main/res/drawable/button_pressed.jpg differ diff --git a/app/src/main/res/drawable/custom_button.xml b/app/src/main/res/drawable/custom_button.xml new file mode 100644 index 0000000..b5d20ca --- /dev/null +++ b/app/src/main/res/drawable/custom_button.xml @@ -0,0 +1,8 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/custom_button2.xml b/app/src/main/res/drawable/custom_button2.xml new file mode 100644 index 0000000..04ae41f --- /dev/null +++ b/app/src/main/res/drawable/custom_button2.xml @@ -0,0 +1,8 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/down_arrow_selected.png b/app/src/main/res/drawable/down_arrow_selected.png new file mode 100644 index 0000000..5d90d3e Binary files /dev/null and b/app/src/main/res/drawable/down_arrow_selected.png differ diff --git a/app/src/main/res/drawable/fe_nyc_logo.png b/app/src/main/res/drawable/fe_nyc_logo.png new file mode 100644 index 0000000..13bab65 Binary files /dev/null and b/app/src/main/res/drawable/fe_nyc_logo.png differ diff --git a/app/src/main/res/drawable/goal_animation.xml b/app/src/main/res/drawable/goal_animation.xml new file mode 100644 index 0000000..3bbf4a1 --- /dev/null +++ b/app/src/main/res/drawable/goal_animation.xml @@ -0,0 +1,10 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_menu.png b/app/src/main/res/drawable/ic_menu.png new file mode 100644 index 0000000..c62db8c Binary files /dev/null and b/app/src/main/res/drawable/ic_menu.png differ diff --git a/app/src/main/res/drawable/list_border.xml b/app/src/main/res/drawable/list_border.xml new file mode 100644 index 0000000..3348bb9 --- /dev/null +++ b/app/src/main/res/drawable/list_border.xml @@ -0,0 +1,19 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/moneytrees.png b/app/src/main/res/drawable/moneytrees.png new file mode 100644 index 0000000..4392e55 Binary files /dev/null and b/app/src/main/res/drawable/moneytrees.png differ diff --git a/app/src/main/res/drawable/progress_bar.png b/app/src/main/res/drawable/progress_bar.png new file mode 100644 index 0000000..abff577 Binary files /dev/null and b/app/src/main/res/drawable/progress_bar.png differ diff --git a/app/src/main/res/drawable/tree_20.jpg b/app/src/main/res/drawable/tree_20.jpg new file mode 100644 index 0000000..06fa7b9 Binary files /dev/null and b/app/src/main/res/drawable/tree_20.jpg differ diff --git a/app/src/main/res/drawable/tree_40.jpg b/app/src/main/res/drawable/tree_40.jpg new file mode 100644 index 0000000..bf78cf0 Binary files /dev/null and b/app/src/main/res/drawable/tree_40.jpg differ diff --git a/app/src/main/res/drawable/tree_60.jpg b/app/src/main/res/drawable/tree_60.jpg new file mode 100644 index 0000000..86e8a5a Binary files /dev/null and b/app/src/main/res/drawable/tree_60.jpg differ diff --git a/app/src/main/res/drawable/tree_80.jpg b/app/src/main/res/drawable/tree_80.jpg new file mode 100644 index 0000000..c67f576 Binary files /dev/null and b/app/src/main/res/drawable/tree_80.jpg differ diff --git a/app/src/main/res/drawable/tree_empty.jpg b/app/src/main/res/drawable/tree_empty.jpg new file mode 100644 index 0000000..cc3ba76 Binary files /dev/null and b/app/src/main/res/drawable/tree_empty.jpg differ diff --git a/app/src/main/res/drawable/tree_full.jpg b/app/src/main/res/drawable/tree_full.jpg new file mode 100644 index 0000000..f4389c2 Binary files /dev/null and b/app/src/main/res/drawable/tree_full.jpg differ diff --git a/app/src/main/res/layout/activity_landing_page.xml b/app/src/main/res/layout/activity_landing_page.xml new file mode 100644 index 0000000..dccd9fd --- /dev/null +++ b/app/src/main/res/layout/activity_landing_page.xml @@ -0,0 +1,16 @@ + + + +