From ccf606742fa95fb2b471d4c5809f5700d0e180a5 Mon Sep 17 00:00:00 2001 From: Julian Rosser Date: Fri, 2 Sep 2016 18:05:28 +0100 Subject: [PATCH 01/83] Prevent index out of bounds crash. Added error message to all languages --- .../birthdays/viewholder/BirthdayViewHolder.java | 7 ++++++- app/src/main/res/values-de/strings.xml | 1 + app/src/main/res/values-es/strings.xml | 1 + app/src/main/res/values-fr/strings.xml | 1 + app/src/main/res/values-it/strings.xml | 1 + app/src/main/res/values-pt/strings.xml | 1 + app/src/main/res/values-ro/strings.xml | 1 + app/src/main/res/values-ru/strings.xml | 1 + app/src/main/res/values/strings.xml | 1 + 9 files changed, 14 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/website/julianrosser/birthdays/viewholder/BirthdayViewHolder.java b/app/src/main/java/website/julianrosser/birthdays/viewholder/BirthdayViewHolder.java index 1356892..a5c77e8 100644 --- a/app/src/main/java/website/julianrosser/birthdays/viewholder/BirthdayViewHolder.java +++ b/app/src/main/java/website/julianrosser/birthdays/viewholder/BirthdayViewHolder.java @@ -2,6 +2,7 @@ import android.graphics.Typeface; import android.graphics.drawable.Drawable; +import android.support.design.widget.Snackbar; import android.support.v7.widget.RecyclerView; import android.view.View; import android.widget.ImageView; @@ -114,7 +115,11 @@ public void onClick(View v) { int currentPosition = RecyclerListFragment.recyclerView.getChildAdapterPosition(itemView); // Open ItemOption menu for selected birthday - BirthdayListActivity.getContext().showItemOptionsFragment(currentPosition); + if (currentPosition != RecyclerView.NO_POSITION) { + BirthdayListActivity.getContext().showItemOptionsFragment(currentPosition); + } else { + Snackbar.make(container, R.string.error_try_again, Snackbar.LENGTH_SHORT).show(); + } } } } \ No newline at end of file diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 3baa021..8d0ec56 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -96,6 +96,7 @@ Bitte fügen Sie Geburtstagsinformationen zu Ihren Kontakten für sie hier zu zeigen. hinzugefügt Datenschutz-Bestimmungen + Fehler, versuchen Sie es erneut… \ No newline at end of file diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 44e34bd..675e597 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -96,6 +96,7 @@ Por favor, añadir información de cumpleaños a sus contactos para que se muestran aquí . agregó Política de privacidad + Error, por favor, inténtelo de nuevo… \ No newline at end of file diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index f5d1b8e..b828567 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -96,6 +96,7 @@ l\'autorisation nécessaire pour charger les contacts ajouté Politique de confidentialité + Erreur, s'il vous plaît essayer à nouveau… \ No newline at end of file diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 0fdad83..6c7b30e 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -96,6 +96,7 @@ Si prega di aggiungere le informazioni di compleanno ai tuoi contatti per loro di mostrare qui. aggiunto politica sulla riservatezza + Errore, riprova… \ No newline at end of file diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index e85b835..76a7eeb 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -96,6 +96,7 @@ já foi adicionada adicionou Política de Privacidade + Erro, por favor, tente novamente… \ No newline at end of file diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml index 9f91eb6..45b00ed 100644 --- a/app/src/main/res/values-ro/strings.xml +++ b/app/src/main/res/values-ro/strings.xml @@ -96,6 +96,7 @@ Vă rugăm să adăugați informații ziua de naștere la persoanele de contact pentru ei pentru a arăta aici. a adăugat Politica de confidentialitate + Eroare, vă rugăm să încercați din nou… \ No newline at end of file diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 190ec55..743f538 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -97,6 +97,7 @@ уже была добавлена добавленный политика конфиденциальности + Ошибка, попробуйте еще раз… \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 0979c1a..92a0147 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -153,5 +153,6 @@ N/A added Privacy Policy + Error, please try again … From deed6af102e66990744fe82e01b8174ee511e269 Mon Sep 17 00:00:00 2001 From: Julian Rosser Date: Mon, 26 Sep 2016 11:52:05 +0100 Subject: [PATCH 02/83] Fixed incorrect privacy policy heading string. Added apostrophe to french error string --- app/src/main/res/layout/content_privacy_policy.xml | 2 +- app/src/main/res/values-fr/strings.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/res/layout/content_privacy_policy.xml b/app/src/main/res/layout/content_privacy_policy.xml index 01d42da..1a5b41a 100644 --- a/app/src/main/res/layout/content_privacy_policy.xml +++ b/app/src/main/res/layout/content_privacy_policy.xml @@ -113,7 +113,7 @@ android:layout_height="wrap_content" android:layout_marginBottom="@dimen/privacy_margin_large" android:layout_marginTop="8dp" - android:text="@string/privacy_policy_summary" + android:text="@string/privacy_policy_full" android:textColor="@color/textColorPrimary" android:textSize="@dimen/privacy_text_size_title" android:textStyle="bold" /> diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index b828567..03aade9 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -96,7 +96,7 @@ l\'autorisation nécessaire pour charger les contacts ajouté Politique de confidentialité - Erreur, s'il vous plaît essayer à nouveau… + Erreur, s\'il vous plaît essayer à nouveau… \ No newline at end of file From dc018d65a5db34b7fddfbf2e9afb3c67a2ba9adf Mon Sep 17 00:00:00 2001 From: Julian Rosser Date: Wed, 5 Oct 2016 18:34:28 +0100 Subject: [PATCH 03/83] Update gradle version --- build.gradle | 2 +- gradle/wrapper/gradle-wrapper.properties | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index 2fb545f..dcc8ba7 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:2.1.2' + classpath 'com.android.tools.build:gradle:2.2.0' classpath 'com.google.gms:google-services:3.0.0' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 516ee4b..f47dd9b 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Mon May 16 23:33:39 BST 2016 +#Wed Oct 05 18:31:51 BST 2016 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip From 5fcea38ecf9f1f53ae5b7bca9c0bce03ab78da8d Mon Sep 17 00:00:00 2001 From: Julian Rosser Date: Wed, 5 Oct 2016 20:20:29 +0100 Subject: [PATCH 04/83] Implemented DrawerLayout --- .../activities/BirthdayListActivity.java | 86 ++++++++++++++++++- app/src/main/res/layout/activity_main.xml | 65 +++++++++----- app/src/main/res/layout/drawer_list_item.xml | 8 ++ 3 files changed, 134 insertions(+), 25 deletions(-) create mode 100644 app/src/main/res/layout/drawer_list_item.xml diff --git a/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java b/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java index e70b493..bc61d2b 100644 --- a/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java +++ b/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java @@ -16,10 +16,15 @@ import android.support.design.widget.Snackbar; import android.support.v4.app.ActivityCompat; import android.support.v4.content.ContextCompat; +import android.support.v4.widget.DrawerLayout; +import android.support.v7.app.ActionBarDrawerToggle; import android.support.v7.widget.Toolbar; import android.view.Menu; import android.view.MenuItem; import android.view.View; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.ListView; import com.google.android.gms.analytics.GoogleAnalytics; import com.google.android.gms.analytics.HitBuilders; @@ -74,7 +79,10 @@ public class BirthdayListActivity extends BaseActivity implements AddEditFragmen private String mUrl; private String mTitle; private String mDescription; - private String mSchemaType; + + private ListView mDrawerList; + private DrawerLayout mDrawerLayout; + private ActionBarDrawerToggle mDrawerToggle; /** * For easy access to BirthdayListActivity context from multiple Classes @@ -173,6 +181,36 @@ protected void onCreate(Bundle savedInstanceState) { Toolbar mToolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(mToolbar); + mDrawerList = (ListView) findViewById(R.id.left_drawer); + // Set the adapter for the list view + mDrawerList.setAdapter(new ArrayAdapter(this, + R.layout.drawer_list_item, new String[] {"oNE", "tWO", "tHrEe"})); + // Set the list's click listener + mDrawerList.setOnItemClickListener(new DrawerItemClickListener()); + + mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); + mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout, + mToolbar, R.string.birthday, R.string.button_negative) { + + /** Called when a drawer has settled in a completely closed state. */ + public void onDrawerClosed(View view) { + super.onDrawerClosed(view); + invalidateOptionsMenu(); // creates call to onPrepareOptionsMenu() + } + + /** Called when a drawer has settled in a completely open state. */ + public void onDrawerOpened(View drawerView) { + super.onDrawerOpened(drawerView); + invalidateOptionsMenu(); // creates call to onPrepareOptionsMenu() + } + }; + + // Set the drawer toggle as the DrawerListener + mDrawerLayout.setDrawerListener(mDrawerToggle); + + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + getSupportActionBar().setHomeButtonEnabled(true); + floatingActionButton = (FloatingActionButton) findViewById(R.id.floatingActionButton); floatingActionButton.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { @@ -216,7 +254,6 @@ public void onClick(View v) { mUrl = "http://julianrosser.website"; mTitle = "Birthday Reminders"; mDescription = "Simple birthday reminders for loved-ones"; - mSchemaType = "http://schema.org/Article"; if (getIntent().getExtras() != null && getIntent().getExtras().getInt(Constants.INTENT_FROM_KEY, 10) == Constants.INTENT_FROM_NOTIFICATION) { mTracker.send(new HitBuilders.EventBuilder() @@ -226,6 +263,8 @@ public void onClick(View v) { } + + // Get sample of theme choice SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); Random r = new Random(); @@ -615,6 +654,49 @@ public void setTheme() { } } + // NavDrawer + private class DrawerItemClickListener implements ListView.OnItemClickListener { + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + selectItem(position); + } + } + + /** Swaps fragments in the main content view */ + private void selectItem(int position) { + // Create a new fragment and specify the planet to show based on position +// Fragment fragment = new PlanetFragment(); +// Bundle args = new Bundle(); +// args.putInt(PlanetFragment.ARG_PLANET_NUMBER, position); +// fragment.setArguments(args); +// +// // Insert the fragment by replacing any existing fragment +// FragmentManager fragmentManager = getFragmentManager(); +// fragmentManager.beginTransaction() +// .replace(R.id.content_frame, fragment) +// .commit(); +// +// // Highlight the selected item, update the title, and close the drawer +// mDrawerList.setItemChecked(position, true); +// setTitle(mPlanetTitles[position]); +// mDrawerLayout.closeDrawer(mDrawerList); + } + + @Override + public void setTitle(CharSequence title) { + mTitle = (String) title; + getActionBar().setTitle(mTitle); + } + + /* Called whenever we call invalidateOptionsMenu() */ + @Override + public boolean onPrepareOptionsMenu(Menu menu) { + // If the nav drawer is open, hide action items related to the content view + boolean drawerOpen = mDrawerLayout.isDrawerOpen(mDrawerList); +// menu.findItem(R.id.action_websearch).setVisible(!drawerOpen); + return super.onPrepareOptionsMenu(menu); + } + /** * Interface Methods * - onItemEdit: Launch AddEditFragment to edit current birthday diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 401e860..389086e 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -1,32 +1,51 @@ - - + android:layout_height="match_parent"> - + + + + + + + + + - - + android:choiceMode="singleChoice" + android:divider="@android:color/transparent" + android:dividerHeight="0dp" /> - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout/drawer_list_item.xml b/app/src/main/res/layout/drawer_list_item.xml new file mode 100644 index 0000000..59c2cec --- /dev/null +++ b/app/src/main/res/layout/drawer_list_item.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file From 9041e9e869656ad817cb871fd6b93bb4adefd99d Mon Sep 17 00:00:00 2001 From: Julian Rosser Date: Sat, 8 Oct 2016 19:33:55 +0100 Subject: [PATCH 05/83] Added nav drawer list items. --- .../birthdays/activities/BirthdayListActivity.java | 9 +++++++-- app/src/main/res/layout/activity_main.xml | 3 +-- app/src/main/res/values/arrays.xml | 7 +++++++ 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java b/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java index bc61d2b..3171bbb 100644 --- a/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java +++ b/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java @@ -43,6 +43,7 @@ import java.io.OutputStreamWriter; import java.io.Writer; import java.util.ArrayList; +import java.util.Arrays; import java.util.Date; import java.util.Random; @@ -182,9 +183,13 @@ protected void onCreate(Bundle savedInstanceState) { setSupportActionBar(mToolbar); mDrawerList = (ListView) findViewById(R.id.left_drawer); + + + String[] menuItems = getResources().getStringArray(R.array.menu_nav_drawer); + // Set the adapter for the list view - mDrawerList.setAdapter(new ArrayAdapter(this, - R.layout.drawer_list_item, new String[] {"oNE", "tWO", "tHrEe"})); + mDrawerList.setAdapter(new ArrayAdapter<>(this, + R.layout.drawer_list_item, menuItems)); // Set the list's click listener mDrawerList.setOnItemClickListener(new DrawerItemClickListener()); diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 389086e..931419a 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -42,8 +42,7 @@ android:layout_width="240dp" android:layout_height="match_parent" android:layout_gravity="start" - android:background="@color/blue_accent_400" - android:layout_marginTop="?attr/actionBarSize" + android:background="?attr/theme_colour" android:choiceMode="singleChoice" android:divider="@android:color/transparent" android:dividerHeight="0dp" /> diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml index 3ae607c..0da4984 100644 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -72,6 +72,13 @@ 1 + + @string/import_contacts + @string/action_settings + @string/action_help + @string/title_privacy_policy + + Alan Turing From f1343d2b72a3b8be47d87d3fbd01925554d2b58a Mon Sep 17 00:00:00 2001 From: Julian Rosser Date: Tue, 11 Oct 2016 22:16:10 +0100 Subject: [PATCH 06/83] Implemented NavigationView to improve NavDrawer --- .../activities/BirthdayListActivity.java | 125 +++++++++--------- .../res/drawable-hdpi/ic_menu_white_24dp.png | Bin 0 -> 92 bytes .../res/drawable-mdpi/ic_menu_white_24dp.png | Bin 0 -> 83 bytes .../res/drawable-xhdpi/ic_menu_white_24dp.png | Bin 0 -> 93 bytes .../drawable-xxhdpi/ic_menu_white_24dp.png | Bin 0 -> 95 bytes .../drawable-xxxhdpi/ic_menu_white_24dp.png | Bin 0 -> 99 bytes app/src/main/res/layout/activity_main.xml | 13 +- app/src/main/res/layout/drawer_list_item.xml | 6 +- app/src/main/res/layout/layout_nav_header.xml | 60 +++++++++ app/src/main/res/menu/menu_nav_drawer.xml | 26 ++++ 10 files changed, 159 insertions(+), 71 deletions(-) create mode 100644 app/src/main/res/drawable-hdpi/ic_menu_white_24dp.png create mode 100644 app/src/main/res/drawable-mdpi/ic_menu_white_24dp.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_menu_white_24dp.png create mode 100644 app/src/main/res/drawable-xxhdpi/ic_menu_white_24dp.png create mode 100644 app/src/main/res/drawable-xxxhdpi/ic_menu_white_24dp.png create mode 100644 app/src/main/res/layout/layout_nav_header.xml create mode 100644 app/src/main/res/menu/menu_nav_drawer.xml diff --git a/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java b/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java index 3171bbb..7eaf1d5 100644 --- a/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java +++ b/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java @@ -13,6 +13,7 @@ import android.os.Handler; import android.preference.PreferenceManager; import android.support.design.widget.FloatingActionButton; +import android.support.design.widget.NavigationView; import android.support.design.widget.Snackbar; import android.support.v4.app.ActivityCompat; import android.support.v4.content.ContextCompat; @@ -22,9 +23,7 @@ import android.view.Menu; import android.view.MenuItem; import android.view.View; -import android.widget.AdapterView; -import android.widget.ArrayAdapter; -import android.widget.ListView; +import android.widget.RelativeLayout; import com.google.android.gms.analytics.GoogleAnalytics; import com.google.android.gms.analytics.HitBuilders; @@ -43,7 +42,6 @@ import java.io.OutputStreamWriter; import java.io.Writer; import java.util.ArrayList; -import java.util.Arrays; import java.util.Date; import java.util.Random; @@ -59,7 +57,7 @@ import website.julianrosser.birthdays.services.SetAlarmsService; @SuppressWarnings("deprecation") -public class BirthdayListActivity extends BaseActivity implements AddEditFragment.NoticeDialogListener, ItemOptionsFragment.ItemOptionsListener { +public class BirthdayListActivity extends BaseActivity implements AddEditFragment.NoticeDialogListener, ItemOptionsFragment.ItemOptionsListener, View.OnClickListener { ; public static ArrayList birthdaysList = new ArrayList<>(); public static Tracker mTracker; @@ -81,9 +79,10 @@ public class BirthdayListActivity extends BaseActivity implements AddEditFragmen private String mTitle; private String mDescription; - private ListView mDrawerList; + // private ListView mDrawerList; private DrawerLayout mDrawerLayout; private ActionBarDrawerToggle mDrawerToggle; + private NavigationView navigationView; /** * For easy access to BirthdayListActivity context from multiple Classes @@ -171,6 +170,16 @@ public static void dataChangedUiThread() { RecyclerListFragment.showEmptyMessageIfRequired(); } + public static boolean isContactAlreadyAdded(Birthday contact) { + boolean onList = false; + for (Birthday b : birthdaysList) { + if (b.getName().equals(contact.getName())) { + onList = true; + } + } + return onList; + } + @Override protected void onCreate(Bundle savedInstanceState) { @@ -182,16 +191,48 @@ protected void onCreate(Bundle savedInstanceState) { Toolbar mToolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(mToolbar); - mDrawerList = (ListView) findViewById(R.id.left_drawer); - + navigationView = (NavigationView) findViewById(R.id.nav_view); + //Setting Navigation View Item Selected FirebaseAuthListener to handle the item click of the navigation menu + navigationView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() { + + // This method will trigger on item Click of navigation menu + @Override + public boolean onNavigationItemSelected(MenuItem menuItem) { + + //Checking if the item is in checked state or not, if not make it in checked state + if (menuItem.isChecked()) menuItem.setChecked(false); + else menuItem.setChecked(true); + + //Closing drawer on item click + mDrawerLayout.closeDrawers(); + + //Check to see which item was being clicked and perform appropriate action + switch (menuItem.getItemId()) { + + //Replacing the main content with ContentFragment Which is our Inbox View; + case R.id.menu_birthdays: +// Toast.makeText(getApplicationContext(), "What\'s On Selected", Toast.LENGTH_SHORT).show(); + return true; + case R.id.menu_help: + startActivity(new Intent(getApplicationContext(), HelpActivity.class)); + return true; + case R.id.menu_import_contacts: + startActivity(new Intent(getApplicationContext(), ImportContactsActivity.class)); + return true; + case R.id.menu_settings: + startActivity(new Intent(getApplicationContext(), SettingsActivity.class)); + return true; + default: + return true; + } + } + }); - String[] menuItems = getResources().getStringArray(R.array.menu_nav_drawer); + // Nav header info + View headerView = navigationView.inflateHeaderView(R.layout.layout_nav_header); - // Set the adapter for the list view - mDrawerList.setAdapter(new ArrayAdapter<>(this, - R.layout.drawer_list_item, menuItems)); - // Set the list's click listener - mDrawerList.setOnItemClickListener(new DrawerItemClickListener()); + RelativeLayout userDetailLayout = (RelativeLayout) headerView.findViewById(R.id.layoutNavUserInfo); + userDetailLayout.setOnClickListener(this); mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout, @@ -214,6 +255,7 @@ public void onDrawerOpened(View drawerView) { mDrawerLayout.setDrawerListener(mDrawerToggle); getSupportActionBar().setDisplayHomeAsUpEnabled(true); + getSupportActionBar().setHomeAsUpIndicator(R.drawable.ic_menu_white_24dp); getSupportActionBar().setHomeButtonEnabled(true); floatingActionButton = (FloatingActionButton) findViewById(R.id.floatingActionButton); @@ -267,9 +309,6 @@ public void onClick(View v) { .build()); } - - - // Get sample of theme choice SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); Random r = new Random(); @@ -659,32 +698,15 @@ public void setTheme() { } } - // NavDrawer - private class DrawerItemClickListener implements ListView.OnItemClickListener { - @Override - public void onItemClick(AdapterView parent, View view, int position, long id) { - selectItem(position); - } - } + @Override + public void onClick(View v) { + int id = v.getId(); - /** Swaps fragments in the main content view */ - private void selectItem(int position) { - // Create a new fragment and specify the planet to show based on position -// Fragment fragment = new PlanetFragment(); -// Bundle args = new Bundle(); -// args.putInt(PlanetFragment.ARG_PLANET_NUMBER, position); -// fragment.setArguments(args); -// -// // Insert the fragment by replacing any existing fragment -// FragmentManager fragmentManager = getFragmentManager(); -// fragmentManager.beginTransaction() -// .replace(R.id.content_frame, fragment) -// .commit(); -// -// // Highlight the selected item, update the title, and close the drawer -// mDrawerList.setItemChecked(position, true); -// setTitle(mPlanetTitles[position]); -// mDrawerLayout.closeDrawer(mDrawerList); + switch (id) { + case R.id.layoutNavUserInfo: + Snackbar.make(v, "Change user display", Snackbar.LENGTH_SHORT).show(); + break; + } } @Override @@ -693,15 +715,6 @@ public void setTitle(CharSequence title) { getActionBar().setTitle(mTitle); } - /* Called whenever we call invalidateOptionsMenu() */ - @Override - public boolean onPrepareOptionsMenu(Menu menu) { - // If the nav drawer is open, hide action items related to the content view - boolean drawerOpen = mDrawerLayout.isDrawerOpen(mDrawerList); -// menu.findItem(R.id.action_websearch).setVisible(!drawerOpen); - return super.onPrepareOptionsMenu(menu); - } - /** * Interface Methods * - onItemEdit: Launch AddEditFragment to edit current birthday @@ -796,16 +809,6 @@ public void checkContactPermissionAndLaunchImportActivity() { } } - public static boolean isContactAlreadyAdded(Birthday contact) { - boolean onList = false; - for (Birthday b : birthdaysList) { - if (b.getName().equals(contact.getName())) { - onList = true; - } - } - return onList; - } - @Override public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) { diff --git a/app/src/main/res/drawable-hdpi/ic_menu_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_menu_white_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..238cfd66b4898d94602df3b9939f2937b5f94b34 GIT binary patch literal 92 zcmeAS@N?(olHy`uVBq!ia0vp^Dj>`VBp6OsFEs^HOeH~n!3+##lh0ZJd7_>!jv*C{ n$qJ$${{R0kKXb{>s!t3I&*G(X-_5)J6Qs@4)z4*}Q$iB}y>uFl literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-mdpi/ic_menu_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_menu_white_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..d3cec05646ba9db5b07edf68e5ac42ebf0d2660a GIT binary patch literal 83 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM0wlfaz7_*1DNh&25R22v2@&Hr5(PMP#n fRb9Efn1O>KFzdSJ6Ga<8pehDWS3j3^P6o^sE literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xhdpi/ic_menu_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_menu_white_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..193185fd8f2b59ce59bafcd6eebf4772a9acf12e GIT binary patch literal 93 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZA`BpB)|k7xlYrjj7PUT$m`Z~Df*BafCZDwc@?<<+978G? ulNE$}nEw3__|P~_=F`CpiJrED8Vn3(8rL-!S-MJqbbGq`xvX - - + app:itemTextColor="#fff" + app:menu="@menu/menu_nav_drawer" /> \ No newline at end of file diff --git a/app/src/main/res/layout/drawer_list_item.xml b/app/src/main/res/layout/drawer_list_item.xml index 59c2cec..1934bfa 100644 --- a/app/src/main/res/layout/drawer_list_item.xml +++ b/app/src/main/res/layout/drawer_list_item.xml @@ -2,7 +2,9 @@ + android:padding="8dp" + android:layout_marginLeft="32dp" + android:textColor="#fff" + android:textSize="13sp"> \ No newline at end of file diff --git a/app/src/main/res/layout/layout_nav_header.xml b/app/src/main/res/layout/layout_nav_header.xml new file mode 100644 index 0000000..28acb70 --- /dev/null +++ b/app/src/main/res/layout/layout_nav_header.xml @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/menu_nav_drawer.xml b/app/src/main/res/menu/menu_nav_drawer.xml new file mode 100644 index 0000000..42004a8 --- /dev/null +++ b/app/src/main/res/menu/menu_nav_drawer.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + From 43646f7522f3fbbba2a4e6cc27fc87645e0bed7a Mon Sep 17 00:00:00 2001 From: Julian Rosser Date: Wed, 12 Oct 2016 21:38:22 +0100 Subject: [PATCH 07/83] Add permission check before importing contacts. --- .../birthdays/activities/BirthdayListActivity.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java b/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java index 7eaf1d5..07f741a 100644 --- a/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java +++ b/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java @@ -211,13 +211,12 @@ public boolean onNavigationItemSelected(MenuItem menuItem) { //Replacing the main content with ContentFragment Which is our Inbox View; case R.id.menu_birthdays: -// Toast.makeText(getApplicationContext(), "What\'s On Selected", Toast.LENGTH_SHORT).show(); return true; case R.id.menu_help: startActivity(new Intent(getApplicationContext(), HelpActivity.class)); return true; case R.id.menu_import_contacts: - startActivity(new Intent(getApplicationContext(), ImportContactsActivity.class)); + checkContactPermissionAndLaunchImportActivity(); return true; case R.id.menu_settings: startActivity(new Intent(getApplicationContext(), SettingsActivity.class)); @@ -796,7 +795,6 @@ public void checkContactPermissionAndLaunchImportActivity() { != PackageManager.PERMISSION_GRANTED) { // No explanation needed, we can request the permission. - ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_CONTACTS}, Constants.CONTACT_PERMISSION_CODE); From e47e5d87947a665fee72862da7339d31b271bfaa Mon Sep 17 00:00:00 2001 From: Julian Rosser Date: Thu, 13 Oct 2016 09:00:19 +0100 Subject: [PATCH 08/83] Added Google Authentication --- app/build.gradle | 16 +- .../activities/BirthdayListActivity.java | 138 +++++++++++++++++- app/src/main/res/layout/layout_nav_header.xml | 13 +- build.gradle | 2 +- 4 files changed, 156 insertions(+), 13 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 3f80554..d8b1432 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,5 +1,4 @@ apply plugin: 'com.android.application' -apply plugin: 'com.google.gms.google-services' android { compileSdkVersion 23 @@ -35,10 +34,13 @@ dependencies { compile 'com.android.support:support-v4:23.1.1' compile 'org.apache.commons:commons-lang3:3.0' compile 'com.android.support:design:23.1.1' - compile 'com.google.android.gms:play-services-appindexing:9.0.0' - compile 'com.google.android.gms:play-services-analytics:9.0.0' + compile 'com.google.android.gms:play-services-appindexing:9.6.1' + compile 'com.google.android.gms:play-services-analytics:9.6.1' compile 'org.greenrobot:eventbus:3.0.0' - compile 'com.google.firebase:firebase-core:9.0.0' - compile 'com.google.firebase:firebase-crash:9.0.0' - -} \ No newline at end of file + compile 'com.google.firebase:firebase-core:9.6.1' + compile 'com.google.firebase:firebase-crash:9.6.1' + compile 'com.google.firebase:firebase-auth:9.6.1' + compile 'com.google.android.gms:play-services-auth:9.6.1' + compile 'com.google.android.gms:play-services-auth:9.6.1' +} +apply plugin: 'com.google.gms.google-services' \ No newline at end of file diff --git a/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java b/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java index 07f741a..0548ec2 100644 --- a/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java +++ b/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java @@ -12,6 +12,7 @@ import android.os.Bundle; import android.os.Handler; import android.preference.PreferenceManager; +import android.support.annotation.NonNull; import android.support.design.widget.FloatingActionButton; import android.support.design.widget.NavigationView; import android.support.design.widget.Snackbar; @@ -20,10 +21,12 @@ import android.support.v4.widget.DrawerLayout; import android.support.v7.app.ActionBarDrawerToggle; import android.support.v7.widget.Toolbar; +import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.RelativeLayout; +import android.widget.Toast; import com.google.android.gms.analytics.GoogleAnalytics; import com.google.android.gms.analytics.HitBuilders; @@ -31,7 +34,20 @@ import com.google.android.gms.appindexing.Action; import com.google.android.gms.appindexing.AppIndex; import com.google.android.gms.appindexing.Thing; +import com.google.android.gms.auth.api.Auth; +import com.google.android.gms.auth.api.signin.GoogleSignInAccount; +import com.google.android.gms.auth.api.signin.GoogleSignInOptions; +import com.google.android.gms.auth.api.signin.GoogleSignInResult; +import com.google.android.gms.common.ConnectionResult; +import com.google.android.gms.common.SignInButton; import com.google.android.gms.common.api.GoogleApiClient; +import com.google.android.gms.tasks.OnCompleteListener; +import com.google.android.gms.tasks.Task; +import com.google.firebase.auth.AuthCredential; +import com.google.firebase.auth.AuthResult; +import com.google.firebase.auth.FirebaseAuth; +import com.google.firebase.auth.FirebaseUser; +import com.google.firebase.auth.GoogleAuthProvider; import org.apache.commons.lang3.text.WordUtils; import org.json.JSONArray; @@ -57,7 +73,8 @@ import website.julianrosser.birthdays.services.SetAlarmsService; @SuppressWarnings("deprecation") -public class BirthdayListActivity extends BaseActivity implements AddEditFragment.NoticeDialogListener, ItemOptionsFragment.ItemOptionsListener, View.OnClickListener { +public class BirthdayListActivity extends BaseActivity implements AddEditFragment.NoticeDialogListener, ItemOptionsFragment.ItemOptionsListener, View.OnClickListener, GoogleApiClient.OnConnectionFailedListener { + private static final int RC_SIGN_IN = 6006; ; public static ArrayList birthdaysList = new ArrayList<>(); public static Tracker mTracker; @@ -84,6 +101,11 @@ public class BirthdayListActivity extends BaseActivity implements AddEditFragmen private ActionBarDrawerToggle mDrawerToggle; private NavigationView navigationView; + // Sign In + private GoogleApiClient mGoogleApiClient; + private FirebaseAuth mAuth; + private FirebaseAuth.AuthStateListener mAuthListener; + /** * For easy access to BirthdayListActivity context from multiple Classes */ @@ -233,6 +255,8 @@ public boolean onNavigationItemSelected(MenuItem menuItem) { RelativeLayout userDetailLayout = (RelativeLayout) headerView.findViewById(R.id.layoutNavUserInfo); userDetailLayout.setOnClickListener(this); + setUpGoogleSignInButton(headerView); + mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout, mToolbar, R.string.birthday, R.string.button_negative) { @@ -320,6 +344,105 @@ public void onClick(View v) { } } + /** + * Google Authentication methods + */ + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + + // Result returned from launching the Intent from GoogleSignInApi.getSignInIntent(...); + if (requestCode == RC_SIGN_IN) { + GoogleSignInResult result = Auth.GoogleSignInApi.getSignInResultFromIntent(data); + handleSignInResult(result); + } + } + + private void handleSignInResult(GoogleSignInResult result) { + Log.d("SIGN IN", "handleSignInResult:" + result.isSuccess()); + if (result.isSuccess()) { + // Signed in successfully, show authenticated UI. + GoogleSignInAccount account = result.getSignInAccount(); + firebaseAuthWithGoogle(account); +// .setText(getString(R.string.signed_in_fmt, acct.getDisplayName())); +// updateUI(true); + } else { + // Signed out, show unauthenticated UI. +// updateUI(false); + } + } + + public void firebaseAuthWithGoogle(GoogleSignInAccount account) { + Log.d("Auth", "firebaseAuthWithGoogle: " + account.getId()); + + AuthCredential credential = GoogleAuthProvider.getCredential(account.getIdToken(), null); + mAuth.signInWithCredential(credential) + .addOnCompleteListener(this, new OnCompleteListener() { + @Override + public void onComplete(@NonNull Task task) { + Log.d("Auth", "signInWithCredential:onComplete:" + task.isSuccessful()); + + // If sign in fails, display a message to the user. If sign in succeeds + // the auth state listener will be notified and logic to handle the + // signed in user can be handled in the listener. + if (!task.isSuccessful()) { + Log.w("Auth", "signInWithCredential", task.getException()); + Toast.makeText(BirthdayListActivity.this, "Authentication failed.", + Toast.LENGTH_SHORT).show(); + } + // ... + } + }); + } + + private void setUpGoogleSignInButton(View headerView) { + GoogleSignInOptions gso = setUpGoogleSignInOptions(); + SignInButton signInButton = (SignInButton) headerView.findViewById(R.id.sign_in_button); + signInButton.setSize(SignInButton.SIZE_WIDE); + signInButton.setScopes(gso.getScopeArray()); + signInButton.setOnClickListener(this); + + mAuth = FirebaseAuth.getInstance(); + mAuthListener = new FirebaseAuth.AuthStateListener() { + @Override + public void onAuthStateChanged(@NonNull FirebaseAuth firebaseAuth) { + FirebaseUser user = firebaseAuth.getCurrentUser(); + if (user != null) { + // User is signed in + Log.d("Auth", "onAuthStateChanged:signed_in:" + user.getUid()); + Snackbar.make(floatingActionButton, "SignedIN: " + user.getDisplayName() + " | " + user.getEmail(), Snackbar.LENGTH_SHORT).show(); + } else { + // User is signed out + Log.d("Auth", "onAuthStateChanged:signed_out"); + } + // ... + } + }; + } + + private GoogleSignInOptions setUpGoogleSignInOptions() { + // Configure sign-in to request the user's ID, email address, and basic + // profile. ID and basic profile are included in DEFAULT_SIGN_IN. + GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN) + .requestIdToken("1072707269724-3v8qbbu86kmfs252eu44amna8cpqqj9c.apps.googleusercontent.com") + .requestEmail() + .build(); + + // Build a GoogleApiClient with access to the Google Sign-In API and the + // options specified by gso. + mGoogleApiClient = new GoogleApiClient.Builder(this) + .enableAutoManage(this /* FragmentActivity */, this /* OnConnectionFailedListener */) + .addApi(Auth.GOOGLE_SIGN_IN_API, gso) + .build(); + return gso; + } + + @Override + public void onConnectionFailed(@NonNull ConnectionResult connectionResult) { + Snackbar.make(floatingActionButton, "Sign in failed", Snackbar.LENGTH_SHORT).show(); + } + public Action getAction() { Thing object = new Thing.Builder() .setName(mTitle) @@ -337,6 +460,7 @@ public Action getAction() { public void onStart() { super.onStart(); mClient.connect(); + mAuth.addAuthStateListener(mAuthListener); AppIndex.AppIndexApi.start(mClient, getAction()); } @@ -389,7 +513,9 @@ protected void onStop() { .setValue(birthdaysList.size()) .build()); } - + if (mAuthListener != null) { + mAuth.removeAuthStateListener(mAuthListener); + } AppIndex.AppIndexApi.end(mClient, getAction()); mClient.disconnect(); super.onStop(); @@ -705,9 +831,17 @@ public void onClick(View v) { case R.id.layoutNavUserInfo: Snackbar.make(v, "Change user display", Snackbar.LENGTH_SHORT).show(); break; + case R.id.sign_in_button: + signIn(); + break; } } + private void signIn() { + Intent signInIntent = Auth.GoogleSignInApi.getSignInIntent(mGoogleApiClient); + startActivityForResult(signInIntent, RC_SIGN_IN); + } + @Override public void setTitle(CharSequence title) { mTitle = (String) title; diff --git a/app/src/main/res/layout/layout_nav_header.xml b/app/src/main/res/layout/layout_nav_header.xml index 28acb70..c961cda 100644 --- a/app/src/main/res/layout/layout_nav_header.xml +++ b/app/src/main/res/layout/layout_nav_header.xml @@ -29,7 +29,7 @@ + android:textStyle="bold" + android:visibility="invisible" /> + android:textStyle="bold" + android:visibility="invisible" /> + + diff --git a/build.gradle b/build.gradle index dcc8ba7..10d1dbd 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:2.2.0' + classpath 'com.android.tools.build:gradle:2.2.1' classpath 'com.google.gms:google-services:3.0.0' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files From e93c35875740b67396c66133aeae26b1028dcc2f Mon Sep 17 00:00:00 2001 From: Julian Rosser Date: Thu, 13 Oct 2016 21:45:39 +0100 Subject: [PATCH 09/83] Finished Google Auth implementation. Updating header view with username, email and display image. --- app/build.gradle | 1 + .../julianrosser/birthdays/Constants.java | 1 + .../activities/BirthdayListActivity.java | 107 +++++++++++++----- .../birthdays/views/CircleTransform.java | 45 ++++++++ app/src/main/res/layout/layout_nav_header.xml | 59 +++++----- app/src/main/res/menu/menu_main.xml | 23 +--- 6 files changed, 162 insertions(+), 74 deletions(-) create mode 100644 app/src/main/java/website/julianrosser/birthdays/views/CircleTransform.java diff --git a/app/build.gradle b/app/build.gradle index d8b1432..4b535a9 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -42,5 +42,6 @@ dependencies { compile 'com.google.firebase:firebase-auth:9.6.1' compile 'com.google.android.gms:play-services-auth:9.6.1' compile 'com.google.android.gms:play-services-auth:9.6.1' + compile 'com.squareup.picasso:picasso:2.5.2' } apply plugin: 'com.google.gms.google-services' \ No newline at end of file diff --git a/app/src/main/java/website/julianrosser/birthdays/Constants.java b/app/src/main/java/website/julianrosser/birthdays/Constants.java index 94d5a45..51f2401 100644 --- a/app/src/main/java/website/julianrosser/birthdays/Constants.java +++ b/app/src/main/java/website/julianrosser/birthdays/Constants.java @@ -11,4 +11,5 @@ public class Constants { public static int INTENT_FROM_NOTIFICATION = 30; public static int CONTACT_PERMISSION_CODE = 3; public static String INTENT_FROM_KEY = "intent_from_key"; + public static String GOOGLE_SIGN_IN_KEY = "1072707269724-3v8qbbu86kmfs252eu44amna8cpqqj9c.apps.googleusercontent.com"; } diff --git a/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java b/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java index 0548ec2..877f6a4 100644 --- a/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java +++ b/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java @@ -25,7 +25,9 @@ import android.view.Menu; import android.view.MenuItem; import android.view.View; -import android.widget.RelativeLayout; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; import android.widget.Toast; import com.google.android.gms.analytics.GoogleAnalytics; @@ -41,6 +43,8 @@ import com.google.android.gms.common.ConnectionResult; import com.google.android.gms.common.SignInButton; import com.google.android.gms.common.api.GoogleApiClient; +import com.google.android.gms.common.api.ResultCallback; +import com.google.android.gms.common.api.Status; import com.google.android.gms.tasks.OnCompleteListener; import com.google.android.gms.tasks.Task; import com.google.firebase.auth.AuthCredential; @@ -48,6 +52,7 @@ import com.google.firebase.auth.FirebaseAuth; import com.google.firebase.auth.FirebaseUser; import com.google.firebase.auth.GoogleAuthProvider; +import com.squareup.picasso.Picasso; import org.apache.commons.lang3.text.WordUtils; import org.json.JSONArray; @@ -71,6 +76,7 @@ import website.julianrosser.birthdays.model.tasks.LoadBirthdaysTask; import website.julianrosser.birthdays.recievers.NotificationBuilderReceiver; import website.julianrosser.birthdays.services.SetAlarmsService; +import website.julianrosser.birthdays.views.CircleTransform; @SuppressWarnings("deprecation") public class BirthdayListActivity extends BaseActivity implements AddEditFragment.NoticeDialogListener, ItemOptionsFragment.ItemOptionsListener, View.OnClickListener, GoogleApiClient.OnConnectionFailedListener { @@ -82,21 +88,23 @@ public class BirthdayListActivity extends BaseActivity implements AddEditFragmen static BirthdayListActivity mContext; static Context mAppContext; private static FloatingActionButton floatingActionButton; + // Keys for orientation change reference final String ADD_EDIT_INSTANCE_KEY = "fragment_add_edit"; final String ITEM_OPTIONS_INSTANCE_KEY = "fragment_item_options"; final String RECYCLER_LIST_INSTANCE_KEY = "fragment_recycler_list"; + // Fragment references AddEditFragment addEditFragment; ItemOptionsFragment itemOptionsFragment; LoadBirthdaysTask loadBirthdaysTask; + // App indexing private GoogleApiClient mClient; private String mUrl; private String mTitle; private String mDescription; - // private ListView mDrawerList; private DrawerLayout mDrawerLayout; private ActionBarDrawerToggle mDrawerToggle; private NavigationView navigationView; @@ -106,6 +114,12 @@ public class BirthdayListActivity extends BaseActivity implements AddEditFragmen private FirebaseAuth mAuth; private FirebaseAuth.AuthStateListener mAuthListener; + private LinearLayout userDetailLayout; + private SignInButton signInButton; + private TextView textNavHeaderUserName; + private TextView textNavHeaderEmail; + private ImageView imageNavHeaderProfile; + /** * For easy access to BirthdayListActivity context from multiple Classes */ @@ -252,9 +266,13 @@ public boolean onNavigationItemSelected(MenuItem menuItem) { // Nav header info View headerView = navigationView.inflateHeaderView(R.layout.layout_nav_header); - RelativeLayout userDetailLayout = (RelativeLayout) headerView.findViewById(R.id.layoutNavUserInfo); + userDetailLayout = (LinearLayout) headerView.findViewById(R.id.layoutNavHeaderUserInfo); userDetailLayout.setOnClickListener(this); + textNavHeaderUserName = (TextView) headerView.findViewById(R.id.navHeaderUserName); + textNavHeaderEmail = (TextView) headerView.findViewById(R.id.navHeaderUserEmail); + imageNavHeaderProfile = (ImageView) headerView.findViewById(R.id.profile_image); + setUpGoogleSignInButton(headerView); mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); @@ -365,11 +383,8 @@ private void handleSignInResult(GoogleSignInResult result) { // Signed in successfully, show authenticated UI. GoogleSignInAccount account = result.getSignInAccount(); firebaseAuthWithGoogle(account); -// .setText(getString(R.string.signed_in_fmt, acct.getDisplayName())); -// updateUI(true); } else { - // Signed out, show unauthenticated UI. -// updateUI(false); + setNavHeaderUserState(NavHeaderState.LOGGED_OUT); } } @@ -391,14 +406,13 @@ public void onComplete(@NonNull Task task) { Toast.makeText(BirthdayListActivity.this, "Authentication failed.", Toast.LENGTH_SHORT).show(); } - // ... } }); } private void setUpGoogleSignInButton(View headerView) { GoogleSignInOptions gso = setUpGoogleSignInOptions(); - SignInButton signInButton = (SignInButton) headerView.findViewById(R.id.sign_in_button); + signInButton = (SignInButton) headerView.findViewById(R.id.sign_in_button); signInButton.setSize(SignInButton.SIZE_WIDE); signInButton.setScopes(gso.getScopeArray()); signInButton.setOnClickListener(this); @@ -410,22 +424,31 @@ public void onAuthStateChanged(@NonNull FirebaseAuth firebaseAuth) { FirebaseUser user = firebaseAuth.getCurrentUser(); if (user != null) { // User is signed in + handleUserAuthenticated(user); Log.d("Auth", "onAuthStateChanged:signed_in:" + user.getUid()); - Snackbar.make(floatingActionButton, "SignedIN: " + user.getDisplayName() + " | " + user.getEmail(), Snackbar.LENGTH_SHORT).show(); } else { // User is signed out + setNavHeaderUserState(NavHeaderState.LOGGED_OUT); + Snackbar.make(floatingActionButton, "Signed OUT", Snackbar.LENGTH_SHORT).show(); Log.d("Auth", "onAuthStateChanged:signed_out"); } - // ... } }; } + private void handleUserAuthenticated(FirebaseUser user) { + textNavHeaderUserName.setText(user.getDisplayName()); + textNavHeaderEmail.setText(user.getEmail()); + Picasso.with(getApplicationContext()).load(user.getPhotoUrl()).transform(new CircleTransform()).into(imageNavHeaderProfile); + setNavHeaderUserState(NavHeaderState.SIGNED_IN); + Snackbar.make(floatingActionButton, user.getDisplayName() + "signed IN | " + user.getEmail(), Snackbar.LENGTH_SHORT).show(); + } + private GoogleSignInOptions setUpGoogleSignInOptions() { // Configure sign-in to request the user's ID, email address, and basic // profile. ID and basic profile are included in DEFAULT_SIGN_IN. GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN) - .requestIdToken("1072707269724-3v8qbbu86kmfs252eu44amna8cpqqj9c.apps.googleusercontent.com") + .requestIdToken(Constants.GOOGLE_SIGN_IN_KEY) .requestEmail() .build(); @@ -438,6 +461,26 @@ private GoogleSignInOptions setUpGoogleSignInOptions() { return gso; } + enum NavHeaderState { + LOGGED_OUT, + SIGNED_IN + } + + public void setNavHeaderUserState(NavHeaderState state) { + switch (state) { + case LOGGED_OUT: + userDetailLayout.setVisibility(View.GONE); + signInButton.setVisibility(View.VISIBLE); + imageNavHeaderProfile.setVisibility(View.GONE); + break; + case SIGNED_IN: + userDetailLayout.setVisibility(View.VISIBLE); + signInButton.setVisibility(View.GONE); + imageNavHeaderProfile.setVisibility(View.VISIBLE); + break; + } + } + @Override public void onConnectionFailed(@NonNull ConnectionResult connectionResult) { Snackbar.make(floatingActionButton, "Sign in failed", Snackbar.LENGTH_SHORT).show(); @@ -776,26 +819,34 @@ public boolean onOptionsItemSelected(MenuItem item) { // as you specify a parent activity in AndroidManifest.xml. int id = item.getItemId(); - //noinspection SimplifiableIfStatement - if (id == R.id.action_import_contacts) { - checkContactPermissionAndLaunchImportActivity(); - - } else if (id == R.id.action_settings) { - Intent i = new Intent(this, SettingsActivity.class); - startActivity(i); - - } else if (id == R.id.action_help) { - Intent intentHelp = new Intent(this, HelpActivity.class); - startActivity(intentHelp); - return true; - } else if (id == R.id.action_privacy_policy) { - Intent intentPrivacy = new Intent(this, PrivacyPolicyActivity.class); - startActivity(intentPrivacy); + if (id == R.id.action_sign_out) { + signOutGoogle(); return true; } return super.onOptionsItemSelected(item); } + private void signOutGoogle() { + Auth.GoogleSignInApi.signOut(mGoogleApiClient).setResultCallback( + new ResultCallback() { + @Override + public void onResult(Status status) { + setNavHeaderUserState(NavHeaderState.LOGGED_OUT); + Snackbar.make(floatingActionButton, "signOutGoogle: " + status.getStatusMessage(), Snackbar.LENGTH_SHORT).show(); + } + }); + } + + private void revokeAccessGoogle() { + Auth.GoogleSignInApi.revokeAccess(mGoogleApiClient).setResultCallback( + new ResultCallback() { + @Override + public void onResult(Status status) { + Snackbar.make(floatingActionButton, "revokeAccessGoogle: " + status.getStatusMessage(), Snackbar.LENGTH_SHORT).show(); + } + }); + } + // Call this method from Adapter so reference can be kept here in BirthdayListActivity public void launchLoadBirthdaysTask() { loadBirthdaysTask = new LoadBirthdaysTask(); @@ -828,7 +879,7 @@ public void onClick(View v) { int id = v.getId(); switch (id) { - case R.id.layoutNavUserInfo: + case R.id.layoutNavHeaderUserInfo: Snackbar.make(v, "Change user display", Snackbar.LENGTH_SHORT).show(); break; case R.id.sign_in_button: diff --git a/app/src/main/java/website/julianrosser/birthdays/views/CircleTransform.java b/app/src/main/java/website/julianrosser/birthdays/views/CircleTransform.java new file mode 100644 index 0000000..13f5eaa --- /dev/null +++ b/app/src/main/java/website/julianrosser/birthdays/views/CircleTransform.java @@ -0,0 +1,45 @@ +package website.julianrosser.birthdays.views; + +import android.graphics.Bitmap; +import android.graphics.BitmapShader; +import android.graphics.Canvas; +import android.graphics.Paint; + +import com.squareup.picasso.Transformation; + +public class CircleTransform implements Transformation { + + @Override + public Bitmap transform(Bitmap source) { + int size = Math.min(source.getWidth(), source.getHeight()); + + int x = (source.getWidth() - size) / 2; + int y = (source.getHeight() - size) / 2; + + Bitmap squaredBitmap = Bitmap.createBitmap(source, x, y, size, size); + if (squaredBitmap != source) { + source.recycle(); + } + + Bitmap bitmap = Bitmap.createBitmap(size, size, source.getConfig()); + + Canvas canvas = new Canvas(bitmap); + Paint paint = new Paint(); + BitmapShader shader = new BitmapShader(squaredBitmap, + BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP); + paint.setShader(shader); + paint.setAntiAlias(true); + + float r = size / 2f; + canvas.drawCircle(r, r, r, paint); + + squaredBitmap.recycle(); + return bitmap; + } + + + @Override + public String key() { + return "rounded(" + getClass().hashCode() + ")"; + } +} diff --git a/app/src/main/res/layout/layout_nav_header.xml b/app/src/main/res/layout/layout_nav_header.xml index c961cda..fa2f6d9 100644 --- a/app/src/main/res/layout/layout_nav_header.xml +++ b/app/src/main/res/layout/layout_nav_header.xml @@ -2,7 +2,7 @@ + android:layout_height="wrap_content"> + android:layout_marginTop="8dp"> - + android:background="?attr/selectableItemBackground" + android:orientation="vertical" + android:padding="16dp" + android:visibility="gone"> - + + + + + + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_centerHorizontal="true" + android:layout_margin="16dp" + android:layout_marginTop="8dp" /> diff --git a/app/src/main/res/menu/menu_main.xml b/app/src/main/res/menu/menu_main.xml index c65ca45..4f4a163 100644 --- a/app/src/main/res/menu/menu_main.xml +++ b/app/src/main/res/menu/menu_main.xml @@ -1,28 +1,11 @@ - - - - - - + android:title="Sign out" /> + From fceca0482d18c17de70eb527ea31a9cc51255430 Mon Sep 17 00:00:00 2001 From: Julian Rosser Date: Thu, 13 Oct 2016 23:09:02 +0100 Subject: [PATCH 10/83] Removed majority of static fields and methods. Needs further testing. --- app/build.gradle | 1 + app/src/main/AndroidManifest.xml | 1 + .../activities/BirthdayListActivity.java | 106 +++++++++--------- .../activities/SettingsActivity.java | 11 +- .../adapter/BirthdayViewAdapter.java | 5 +- .../DialogFragments/ItemOptionsFragment.java | 4 +- .../fragments/ImportContactFragment.java | 2 +- .../birthdays/model/Birthday.java | 27 +++-- .../events/BirthdayAlarmToggleEvent.java | 14 +++ .../model/events/BirthdayItemClickEvent.java | 14 +++ .../model/events/BirthdaysLoadedEvent.java | 19 ++++ .../model/tasks/LoadBirthdaysTask.java | 19 ++-- .../viewholder/BirthdayViewHolder.java | 11 +- .../viewholder/ContactViewHolder.java | 7 +- 14 files changed, 149 insertions(+), 92 deletions(-) create mode 100644 app/src/main/java/website/julianrosser/birthdays/model/events/BirthdayAlarmToggleEvent.java create mode 100644 app/src/main/java/website/julianrosser/birthdays/model/events/BirthdayItemClickEvent.java create mode 100644 app/src/main/java/website/julianrosser/birthdays/model/events/BirthdaysLoadedEvent.java diff --git a/app/build.gradle b/app/build.gradle index 4b535a9..2342f9d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -43,5 +43,6 @@ dependencies { compile 'com.google.android.gms:play-services-auth:9.6.1' compile 'com.google.android.gms:play-services-auth:9.6.1' compile 'com.squareup.picasso:picasso:2.5.2' + compile 'org.greenrobot:eventbus:3.0.0' } apply plugin: 'com.google.gms.google-services' \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index b034860..d038155 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -20,6 +20,7 @@ diff --git a/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java b/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java index 877f6a4..2e87749 100644 --- a/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java +++ b/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java @@ -55,6 +55,8 @@ import com.squareup.picasso.Picasso; import org.apache.commons.lang3.text.WordUtils; +import org.greenrobot.eventbus.EventBus; +import org.greenrobot.eventbus.Subscribe; import org.json.JSONArray; import org.json.JSONException; @@ -73,6 +75,9 @@ import website.julianrosser.birthdays.fragments.DialogFragments.ItemOptionsFragment; import website.julianrosser.birthdays.fragments.RecyclerListFragment; import website.julianrosser.birthdays.model.Birthday; +import website.julianrosser.birthdays.model.events.BirthdayAlarmToggleEvent; +import website.julianrosser.birthdays.model.events.BirthdayItemClickEvent; +import website.julianrosser.birthdays.model.events.BirthdaysLoadedEvent; import website.julianrosser.birthdays.model.tasks.LoadBirthdaysTask; import website.julianrosser.birthdays.recievers.NotificationBuilderReceiver; import website.julianrosser.birthdays.services.SetAlarmsService; @@ -85,9 +90,9 @@ public class BirthdayListActivity extends BaseActivity implements AddEditFragmen public static ArrayList birthdaysList = new ArrayList<>(); public static Tracker mTracker; static RecyclerListFragment recyclerListFragment; - static BirthdayListActivity mContext; - static Context mAppContext; - private static FloatingActionButton floatingActionButton; + BirthdayListActivity mContext; + Context mAppContext; + private FloatingActionButton floatingActionButton; // Keys for orientation change reference final String ADD_EDIT_INSTANCE_KEY = "fragment_add_edit"; @@ -123,36 +128,36 @@ public class BirthdayListActivity extends BaseActivity implements AddEditFragmen /** * For easy access to BirthdayListActivity context from multiple Classes */ - public static BirthdayListActivity getContext() { - return mContext; - } - - public static Context getAppContext() { - return mAppContext; - } +// public static BirthdayListActivity getContext() { +// return mContext; +// } +// +// public static Context getAppContext() { +// return mAppContext; +// } // This builds an identical PendingIntent to the alarm and cancels when - private static void cancelAlarm(Birthday deletedBirthday) { + private void cancelAlarm(Birthday deletedBirthday) { // CreateIntent to start the AlarmNotificationReceiver - Intent mNotificationReceiverIntent = new Intent(BirthdayListActivity.getAppContext(), + Intent mNotificationReceiverIntent = new Intent(BirthdayListActivity.this, NotificationBuilderReceiver.class); // Create pending Intent using Intent we just built PendingIntent mNotificationReceiverPendingIntent = PendingIntent - .getBroadcast(getAppContext(), deletedBirthday.getName().hashCode(), + .getBroadcast(getApplicationContext(), deletedBirthday.getName().hashCode(), mNotificationReceiverIntent, PendingIntent.FLAG_UPDATE_CURRENT); // Finish by passing PendingIntent and delay time to AlarmManager - AlarmManager mAlarmManager = (AlarmManager) getAppContext().getSystemService(ALARM_SERVICE); + AlarmManager mAlarmManager = (AlarmManager) getApplicationContext().getSystemService(ALARM_SERVICE); mAlarmManager.cancel(mNotificationReceiverPendingIntent); } /** * Save Birthdays to JSON file, then Update alarms by starting Service **/ - public static void saveBirthdays() + public void saveBirthdays() throws JSONException, IOException { if (birthdaysList != null) { @@ -181,18 +186,18 @@ public static void saveBirthdays() } // Launch service to update alarms when data changed - Intent serviceIntent = new Intent(BirthdayListActivity.getAppContext(), SetAlarmsService.class); - BirthdayListActivity.getAppContext().startService(serviceIntent); + Intent serviceIntent = new Intent(BirthdayListActivity.this, SetAlarmsService.class); + startService(serviceIntent); } } // Force UI thread to ensure mAdapter updates RecyclerView list - public static void dataChangedUiThread() { + public void dataChangedUiThread() { // Reorder ArrayList to sort by desired method - SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(BirthdayListActivity.getAppContext()); + SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); // Get users sort preference - if (Integer.valueOf(sharedPref.getString(getAppContext().getString(R.string.pref_sort_by_key), "0")) == 1) { + if (Integer.valueOf(sharedPref.getString(getApplicationContext().getString(R.string.pref_sort_by_key), "0")) == 1) { BirthdayViewAdapter.sortBirthdaysByName(); } else { BirthdayViewAdapter.sortBirthdaysByDate(); @@ -335,6 +340,8 @@ public void onClick(View v) { // This is to help the fragment keep its state on rotation recyclerListFragment.setRetainInstance(true); + EventBus.getDefault().register(this); + // Obtain the shared Tracker instance. mTracker = getDefaultTracker(); @@ -561,6 +568,7 @@ protected void onStop() { } AppIndex.AppIndexApi.end(mClient, getAction()); mClient.disconnect(); + EventBus.getDefault().unregister(this); super.onStop(); } @@ -647,16 +655,16 @@ public void onDialogPositiveClick(AddEditFragment dialog, String name, int day, birthday = birthdaysList.get(position); birthday.edit(name, dateOfBirth, true, includeYear); - mContext.runOnUiThread(new Runnable() { + runOnUiThread(new Runnable() { @Override public void run() { /** Logic for edit animation. Depending first on sorting preference, check whether the sorting will change. * if so, used adapter moved animation, else just refresh information */ - SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(BirthdayListActivity.getAppContext()); + SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); // Get users sorting preference - if (Integer.valueOf(sharedPref.getString(getAppContext().getString(R.string.pref_sort_by_key), "0")) != 1) { + if (Integer.valueOf(sharedPref.getString(getApplicationContext().getString(R.string.pref_sort_by_key), "0")) != 1) { // PREF SORT: DATE if (BirthdayViewAdapter.willChangeDateOrder(birthday)) { @@ -664,12 +672,10 @@ public void run() { // Order will change, sort, then notify adapter of move BirthdayViewAdapter.sortBirthdaysByDate(); RecyclerListFragment.mAdapter.notifyItemMoved(position, birthdaysList.indexOf(birthday)); - } else { // No order change, so just notify item changed RecyclerListFragment.mAdapter.notifyItemChanged(birthdaysList.indexOf(birthday)); } - } else { // PREF SORT: NAME. If order changes, sort the notify adapter @@ -684,7 +690,6 @@ public void run() { RecyclerListFragment.mAdapter.notifyItemChanged(birthdaysList.indexOf(birthday)); } } - // Delay update until after animation has finished Runnable r = new Runnable() { public void run() { @@ -692,11 +697,7 @@ public void run() { } }; new Handler().postDelayed(r, 500); - - } - - }); } else { @@ -705,13 +706,13 @@ public void run() { birthdaysList.add(birthday); // Notify adapter - mContext.runOnUiThread(new Runnable() { + runOnUiThread(new Runnable() { public void run() { - SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(BirthdayListActivity.getAppContext()); + SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); // Get users sort preference - if (Integer.valueOf(sharedPref.getString(getAppContext().getString(R.string.pref_sort_by_key), "0")) == 1) { + if (Integer.valueOf(sharedPref.getString(getApplicationContext().getString(R.string.pref_sort_by_key), "0")) == 1) { BirthdayViewAdapter.sortBirthdaysByName(); } else { BirthdayViewAdapter.sortBirthdaysByDate(); @@ -725,18 +726,6 @@ public void run() { .setCategory("Action") .setAction("New Birthday") .build()); - -// mTracker.send(new HitBuilders.EventBuilder() -// .setCategory("Data") -// .setAction("Name") -// .setLabel(name) -// .build()); - -// mTracker.send(new HitBuilders.EventBuilder() -// .setCategory("Data") -// .setAction("Date") -// .setLabel(dateOfBirth.getDate() + " / " + dateOfBirth.getMonth()) -// .build()); } try { @@ -771,7 +760,7 @@ public void run() { } catch (Exception e) { e.printStackTrace(); } - Snackbar.make(floatingActionButton, BirthdayListActivity.getAppContext().getString(R.string.deleted) + " " + Snackbar.make(floatingActionButton, getApplicationContext().getString(R.string.deleted) + " " + birthdayToDelete.getName(), Snackbar.LENGTH_LONG).setAction(R.string.undo, new View.OnClickListener() { @Override @@ -781,10 +770,10 @@ public void onClick(View v) { mContext.runOnUiThread(new Runnable() { public void run() { - SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(BirthdayListActivity.getAppContext()); + SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); // Get users sort preference - if (Integer.valueOf(sharedPref.getString(getAppContext().getString(R.string.pref_sort_by_key), "0")) == 1) { + if (Integer.valueOf(sharedPref.getString(getApplicationContext().getString(R.string.pref_sort_by_key), "0")) == 1) { BirthdayViewAdapter.sortBirthdaysByName(); } else { BirthdayViewAdapter.sortBirthdaysByDate(); @@ -888,6 +877,21 @@ public void onClick(View v) { } } + @Subscribe + public void onMessageEvent(BirthdaysLoadedEvent event) { + birthdaysList = event.getBirthdays(); + } + + @Subscribe + public void onMessageEvent(BirthdayAlarmToggleEvent event) { + alarmToggled(event.getCurrentPosition()); + } + + @Subscribe + public void onMessageEvent(BirthdayItemClickEvent event) { + showItemOptionsFragment(event.getCurrentPosition()); + } + private void signIn() { Intent signInIntent = Auth.GoogleSignInApi.getSignInIntent(mGoogleApiClient); startActivityForResult(signInIntent, RC_SIGN_IN); @@ -953,10 +957,10 @@ public void alarmToggled(int position) { // Notify user of change. If birthday is today, let user know alarm is set for next year if (birthday.getDaysBetween() == 0 && birthday.getRemind()) { - Snackbar.make(floatingActionButton, floatingActionButton.getContext().getString(R.string.reminder_for) + birthday.getName() + " " + - birthday.getReminderString() + floatingActionButton.getContext().getString(R.string.for_next_year), Snackbar.LENGTH_LONG).show(); + Snackbar.make(floatingActionButton, getApplicationContext().getString(R.string.reminder_for) + birthday.getName() + " " + + birthday.getReminderString() + getApplicationContext().getString(R.string.for_next_year), Snackbar.LENGTH_LONG).show(); } else { - Snackbar.make(floatingActionButton, BirthdayListActivity.getAppContext().getString(R.string.reminder_for) + birthday.getName() + " " + + Snackbar.make(floatingActionButton, getApplicationContext().getString(R.string.reminder_for) + birthday.getName() + " " + birthday.getReminderString(), Snackbar.LENGTH_LONG).show(); } diff --git a/app/src/main/java/website/julianrosser/birthdays/activities/SettingsActivity.java b/app/src/main/java/website/julianrosser/birthdays/activities/SettingsActivity.java index d1bd0c6..cfa4288 100644 --- a/app/src/main/java/website/julianrosser/birthdays/activities/SettingsActivity.java +++ b/app/src/main/java/website/julianrosser/birthdays/activities/SettingsActivity.java @@ -19,6 +19,7 @@ import com.google.android.gms.analytics.HitBuilders; import com.google.android.gms.analytics.Tracker; +import website.julianrosser.birthdays.BirthdayReminder; import website.julianrosser.birthdays.R; import website.julianrosser.birthdays.recievers.NotificationBuilderReceiver; import website.julianrosser.birthdays.services.SetAlarmsService; @@ -26,7 +27,7 @@ public class SettingsActivity extends BaseActivity { static Context mContext; - private static Tracker mTracker; + private Tracker mTracker; @Override public void onCreate(Bundle savedInstanceState) { @@ -81,7 +82,7 @@ protected void onPause() { */ private static void launchTestNotification() { - Context context = BirthdayListActivity.getAppContext(); + Context context = BirthdayReminder.getInstance(); int PENDING_INTENT_ID = 0; int MY_NOTIFICATION_ID = 100; @@ -233,9 +234,7 @@ public boolean onPreferenceChange(Preference preference, Object value) { // If theme preference is changed, immediately recreate the activity. getActivity().recreate(); // Also remove BirthdayListActivity, so it can be recreated to apply theme - if (BirthdayListActivity.mContext != null) { - BirthdayListActivity.mContext.finish(); - } + } return true; @@ -249,7 +248,7 @@ public boolean onPreferenceClick(Preference preference) { launchTestNotification(); } - mTracker.send(new HitBuilders.EventBuilder() + ((SettingsActivity)getActivity()).mTracker.send(new HitBuilders.EventBuilder() .setCategory("Preference") .setAction("Pref click") .setLabel(preference.getKey()) diff --git a/app/src/main/java/website/julianrosser/birthdays/adapter/BirthdayViewAdapter.java b/app/src/main/java/website/julianrosser/birthdays/adapter/BirthdayViewAdapter.java index 4b09674..ee84a3c 100644 --- a/app/src/main/java/website/julianrosser/birthdays/adapter/BirthdayViewAdapter.java +++ b/app/src/main/java/website/julianrosser/birthdays/adapter/BirthdayViewAdapter.java @@ -12,6 +12,7 @@ import website.julianrosser.birthdays.activities.BirthdayListActivity; import website.julianrosser.birthdays.model.Birthday; import website.julianrosser.birthdays.R; +import website.julianrosser.birthdays.model.tasks.LoadBirthdaysTask; import website.julianrosser.birthdays.viewholder.BirthdayViewHolder; public class BirthdayViewAdapter extends RecyclerView.Adapter { @@ -23,7 +24,8 @@ public BirthdayViewAdapter(ArrayList birthdayData) { // BirthdayListActivity.birthdaysList = new ArrayList<>(); } else if (birthdayData.size() == 0) { // After Adapter is constructed, start the process of loading data - BirthdayListActivity.getContext().launchLoadBirthdaysTask(); + LoadBirthdaysTask loadBirthdaysTask = new LoadBirthdaysTask(); + loadBirthdaysTask.execute(); } } @@ -124,5 +126,4 @@ public int getItemCount() { return BirthdayListActivity.birthdaysList.size(); } - } \ No newline at end of file diff --git a/app/src/main/java/website/julianrosser/birthdays/fragments/DialogFragments/ItemOptionsFragment.java b/app/src/main/java/website/julianrosser/birthdays/fragments/DialogFragments/ItemOptionsFragment.java index dae4b58..7c2e77f 100644 --- a/app/src/main/java/website/julianrosser/birthdays/fragments/DialogFragments/ItemOptionsFragment.java +++ b/app/src/main/java/website/julianrosser/birthdays/fragments/DialogFragments/ItemOptionsFragment.java @@ -134,7 +134,7 @@ public Dialog onCreateDialog(Bundle savedInstanceState) { ListView listView = (ListView) inflater.inflate(R.layout.item_edit_fragment, null); // Create adapter using custom class - OptionListAdapter adapter = new OptionListAdapter(BirthdayListActivity.getContext(), + OptionListAdapter adapter = new OptionListAdapter(getActivity(), getResources().getStringArray(R.array.item_menu_array)); listView.setAdapter(adapter); @@ -175,7 +175,7 @@ public class OptionListAdapter extends BaseAdapter { private LayoutInflater inflater = null; - public OptionListAdapter(BirthdayListActivity birthdayListActivity, String[] stringArray) { + public OptionListAdapter(Context birthdayListActivity, String[] stringArray) { result = stringArray; context = birthdayListActivity; diff --git a/app/src/main/java/website/julianrosser/birthdays/fragments/ImportContactFragment.java b/app/src/main/java/website/julianrosser/birthdays/fragments/ImportContactFragment.java index f3e8e77..0c01edb 100644 --- a/app/src/main/java/website/julianrosser/birthdays/fragments/ImportContactFragment.java +++ b/app/src/main/java/website/julianrosser/birthdays/fragments/ImportContactFragment.java @@ -95,7 +95,7 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, } // Set layout properties - LinearLayoutManager llm = new LinearLayoutManager(BirthdayListActivity.getAppContext()); + LinearLayoutManager llm = new LinearLayoutManager(getActivity()); llm.setOrientation(LinearLayoutManager.VERTICAL); recyclerView.setLayoutManager(llm); diff --git a/app/src/main/java/website/julianrosser/birthdays/model/Birthday.java b/app/src/main/java/website/julianrosser/birthdays/model/Birthday.java index de6ca33..b0aef7b 100644 --- a/app/src/main/java/website/julianrosser/birthdays/model/Birthday.java +++ b/app/src/main/java/website/julianrosser/birthdays/model/Birthday.java @@ -15,6 +15,7 @@ import java.util.Date; import java.util.Locale; +import website.julianrosser.birthdays.BirthdayReminder; import website.julianrosser.birthdays.R; import website.julianrosser.birthdays.Utils; import website.julianrosser.birthdays.activities.BirthdayListActivity; @@ -32,7 +33,7 @@ public class Birthday { private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat( "dd.MM.yyyy", Locale.getDefault()); - public static final int DAY_IN_MILLIS = 86400000; + private static final int DAY_IN_MILLIS = 86400000; // References to data private String name; @@ -135,17 +136,17 @@ public boolean getRemind() { public String getReminderString() { if (remind) { - return BirthdayListActivity.getAppContext().getString(R.string.reminder_set); + return BirthdayReminder.getInstance().getResources().getString(R.string.reminder_set); } else { - return BirthdayListActivity.getAppContext().getString(R.string.reminder_canceled); + return BirthdayReminder.getInstance().getResources().getString(R.string.reminder_canceled); } } public Drawable getRemindAlarmDrawable() { if (remind) { - return BirthdayListActivity.getAppContext().getResources().getDrawable(R.drawable.ic_alarm_on_white_24dp); + return BirthdayReminder.getInstance().getResources().getDrawable(R.drawable.ic_alarm_on_white_24dp); } else { - return BirthdayListActivity.getAppContext().getResources().getDrawable(R.drawable.ic_alarm_off_white_24dp); + return BirthdayReminder.getInstance().getResources().getDrawable(R.drawable.ic_alarm_off_white_24dp); } } @@ -166,24 +167,26 @@ public String getBirthDay() { public String getFormattedDaysRemainingString() { int i = getDaysBetween(); + Context context = BirthdayReminder.getInstance(); + if (i == 0) { - return WordUtils.capitalize(BirthdayListActivity.getAppContext().getString(R.string.date_today) + "!"); + return WordUtils.capitalize(context.getString(R.string.date_today) + "!"); } else if (i == 1) { - return WordUtils.capitalize(BirthdayListActivity.getAppContext().getString(R.string.date_tomorrow) + "!"); + return WordUtils.capitalize(context.getString(R.string.date_tomorrow) + "!"); } else if (i == -1) { - return BirthdayListActivity.getAppContext().getString(R.string.date_yesterday); + return context.getString(R.string.date_yesterday); } else if (i > 1 && i <= 6) { Date newDate = new Date(); newDate.setTime(getDate().getTime() - DAY_IN_MILLIS); return (String) DateFormat.format("EEEE", newDate); } else if (i == 7) { - return WordUtils.capitalize(BirthdayListActivity.getAppContext().getString(R.string.date_week)); + return WordUtils.capitalize(context.getString(R.string.date_week)); } else if (i < 9) { - return " " + String.valueOf(i) + " " + BirthdayListActivity.getAppContext().getString(R.string.date_days); + return " " + String.valueOf(i) + " " + context.getString(R.string.date_days); } else if (i > 99) { - return " " + String.valueOf(i) + " " + BirthdayListActivity.getAppContext().getString(R.string.date_days); + return " " + String.valueOf(i) + " " + context.getString(R.string.date_days); } else { - return "" + i + " " + BirthdayListActivity.getAppContext().getString(R.string.date_days); + return "" + i + " " + context.getString(R.string.date_days); } } diff --git a/app/src/main/java/website/julianrosser/birthdays/model/events/BirthdayAlarmToggleEvent.java b/app/src/main/java/website/julianrosser/birthdays/model/events/BirthdayAlarmToggleEvent.java new file mode 100644 index 0000000..7d3d3ee --- /dev/null +++ b/app/src/main/java/website/julianrosser/birthdays/model/events/BirthdayAlarmToggleEvent.java @@ -0,0 +1,14 @@ +package website.julianrosser.birthdays.model.events; + +public class BirthdayAlarmToggleEvent { + + private final int currentPosition; + + public BirthdayAlarmToggleEvent(int currentPosition) { + this.currentPosition = currentPosition; + } + + public int getCurrentPosition() { + return currentPosition; + } +} diff --git a/app/src/main/java/website/julianrosser/birthdays/model/events/BirthdayItemClickEvent.java b/app/src/main/java/website/julianrosser/birthdays/model/events/BirthdayItemClickEvent.java new file mode 100644 index 0000000..d843fb1 --- /dev/null +++ b/app/src/main/java/website/julianrosser/birthdays/model/events/BirthdayItemClickEvent.java @@ -0,0 +1,14 @@ +package website.julianrosser.birthdays.model.events; + +public class BirthdayItemClickEvent { + + private final int currentPosition; + + public BirthdayItemClickEvent(int currentPosition) { + this.currentPosition = currentPosition; + } + + public int getCurrentPosition() { + return currentPosition; + } +} diff --git a/app/src/main/java/website/julianrosser/birthdays/model/events/BirthdaysLoadedEvent.java b/app/src/main/java/website/julianrosser/birthdays/model/events/BirthdaysLoadedEvent.java new file mode 100644 index 0000000..543256f --- /dev/null +++ b/app/src/main/java/website/julianrosser/birthdays/model/events/BirthdaysLoadedEvent.java @@ -0,0 +1,19 @@ +package website.julianrosser.birthdays.model.events; + +import java.util.ArrayList; + +import website.julianrosser.birthdays.model.Birthday; + +public class BirthdaysLoadedEvent { + + private ArrayList birthdays; + + public BirthdaysLoadedEvent(ArrayList birthday) { + this.birthdays = birthday; + } + + public ArrayList getBirthdays() { + return birthdays; + } + +} diff --git a/app/src/main/java/website/julianrosser/birthdays/model/tasks/LoadBirthdaysTask.java b/app/src/main/java/website/julianrosser/birthdays/model/tasks/LoadBirthdaysTask.java index 87c2bda..be11e7c 100644 --- a/app/src/main/java/website/julianrosser/birthdays/model/tasks/LoadBirthdaysTask.java +++ b/app/src/main/java/website/julianrosser/birthdays/model/tasks/LoadBirthdaysTask.java @@ -2,6 +2,7 @@ import android.os.AsyncTask; +import org.greenrobot.eventbus.EventBus; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONTokener; @@ -13,16 +14,18 @@ import java.io.InputStreamReader; import java.util.ArrayList; +import website.julianrosser.birthdays.BirthdayReminder; import website.julianrosser.birthdays.Constants; import website.julianrosser.birthdays.activities.BirthdayListActivity; import website.julianrosser.birthdays.model.Birthday; +import website.julianrosser.birthdays.model.events.BirthdaysLoadedEvent; +import website.julianrosser.birthdays.model.events.ContactsLoadedEvent; public class LoadBirthdaysTask extends AsyncTask> { - ArrayList loadedBirthdays; - @Override protected ArrayList doInBackground(Void... params) { + ArrayList loadedBirthdays; try { loadedBirthdays = loadBirthdays(); } catch (Exception e) { @@ -35,22 +38,22 @@ protected ArrayList doInBackground(Void... params) { protected void onPostExecute(ArrayList loadedBirthdays) { super.onPostExecute(loadedBirthdays); + ArrayList birthdaysList = new ArrayList<>(); + for (Birthday b : loadedBirthdays) { - BirthdayListActivity.birthdaysList.add(b); + birthdaysList.add(b); } - BirthdayListActivity.dataChangedUiThread(); + EventBus.getDefault().post(new BirthdaysLoadedEvent(birthdaysList)); } - - // THis is done in background by - public ArrayList loadBirthdays() throws IOException, + private ArrayList loadBirthdays() throws IOException, JSONException { ArrayList loadedBirthdays = new ArrayList<>(); BufferedReader reader = null; try { // Open and read the file into a StringBuilder - InputStream in = BirthdayListActivity.getAppContext().openFileInput(Constants.FILENAME); + InputStream in = BirthdayReminder.getInstance().openFileInput(Constants.FILENAME); reader = new BufferedReader(new InputStreamReader(in)); StringBuilder jsonString = new StringBuilder(); diff --git a/app/src/main/java/website/julianrosser/birthdays/viewholder/BirthdayViewHolder.java b/app/src/main/java/website/julianrosser/birthdays/viewholder/BirthdayViewHolder.java index a5c77e8..6a5e5fe 100644 --- a/app/src/main/java/website/julianrosser/birthdays/viewholder/BirthdayViewHolder.java +++ b/app/src/main/java/website/julianrosser/birthdays/viewholder/BirthdayViewHolder.java @@ -1,6 +1,5 @@ package website.julianrosser.birthdays.viewholder; -import android.graphics.Typeface; import android.graphics.drawable.Drawable; import android.support.design.widget.Snackbar; import android.support.v7.widget.RecyclerView; @@ -10,10 +9,14 @@ import com.google.android.gms.analytics.HitBuilders; +import org.greenrobot.eventbus.EventBus; + import website.julianrosser.birthdays.R; import website.julianrosser.birthdays.activities.BirthdayListActivity; import website.julianrosser.birthdays.fragments.RecyclerListFragment; import website.julianrosser.birthdays.model.Birthday; +import website.julianrosser.birthdays.model.events.BirthdayAlarmToggleEvent; +import website.julianrosser.birthdays.model.events.BirthdayItemClickEvent; /** * ViewHolder class to hold view references to be used in recyclerview. @@ -28,7 +31,6 @@ public class BirthdayViewHolder extends RecyclerView.ViewHolder implements View. private TextView textDaysRemaining; private ImageView imageAlarm; private View container; - private Typeface typeLight; public BirthdayViewHolder(View itemView) { super(itemView); @@ -41,7 +43,6 @@ public BirthdayViewHolder(View itemView) { textDateDay = (TextView) itemView.findViewById(R.id.dateDay); textDateMonth = (TextView) itemView.findViewById(R.id.dateMonth); imageAlarm = (ImageView) itemView.findViewById(R.id.alarmImage); - typeLight = Typeface.createFromAsset(BirthdayListActivity.getAppContext().getResources().getAssets(), "Roboto-Light.ttf"); } public void setTag(Birthday birthday) { @@ -108,7 +109,7 @@ public void onClick(View v) { .build()); // Callback to BirthdayListActivity. - BirthdayListActivity.getContext().alarmToggled(currentPosition); + EventBus.getDefault().post(new BirthdayAlarmToggleEvent(currentPosition)); } else { // Get actual position, accounting for deletion @@ -116,7 +117,7 @@ public void onClick(View v) { // Open ItemOption menu for selected birthday if (currentPosition != RecyclerView.NO_POSITION) { - BirthdayListActivity.getContext().showItemOptionsFragment(currentPosition); + EventBus.getDefault().post(new BirthdayItemClickEvent(currentPosition)); } else { Snackbar.make(container, R.string.error_try_again, Snackbar.LENGTH_SHORT).show(); } diff --git a/app/src/main/java/website/julianrosser/birthdays/viewholder/ContactViewHolder.java b/app/src/main/java/website/julianrosser/birthdays/viewholder/ContactViewHolder.java index 7226d4d..311edc6 100644 --- a/app/src/main/java/website/julianrosser/birthdays/viewholder/ContactViewHolder.java +++ b/app/src/main/java/website/julianrosser/birthdays/viewholder/ContactViewHolder.java @@ -1,6 +1,5 @@ package website.julianrosser.birthdays.viewholder; -import android.graphics.Typeface; import android.support.design.widget.Snackbar; import android.support.v7.widget.RecyclerView; import android.text.format.DateFormat; @@ -28,7 +27,6 @@ public class ContactViewHolder extends RecyclerView.ViewHolder implements View.O private TextView textName; private ImageView imageAdd; private View container; - private Typeface typeLight; public ContactViewHolder(View itemView) { super(itemView); @@ -40,7 +38,6 @@ public ContactViewHolder(View itemView) { textDateMonth = (TextView) itemView.findViewById(R.id.dateMonth); imageAdd = (ImageView) itemView.findViewById(R.id.addImage); imageAdd.setOnClickListener(this); - typeLight = Typeface.createFromAsset(BirthdayListActivity.getAppContext().getResources().getAssets(), "Roboto-Light.ttf"); } @Override @@ -79,11 +76,11 @@ public void setDate(String birthday) { textDateMonth.setText("" + getBirthMonth(birthdate)); } - public String getBirthMonth(Date date) { + private String getBirthMonth(Date date) { return (String) DateFormat.format("MMM", date); } - public String getBirthDay(Date date) { + private String getBirthDay(Date date) { return "" + date.getDate() + Utils.getDateSuffix(date.getDate()); } From 55b276eb5753809fa9c9a1a33c8013ea7b502273 Mon Sep 17 00:00:00 2001 From: Julian Rosser Date: Thu, 13 Oct 2016 23:22:13 +0100 Subject: [PATCH 11/83] Removed unused static tracker --- .../birthdays/activities/BirthdayListActivity.java | 5 ++--- .../birthdays/viewholder/BirthdayViewHolder.java | 5 ----- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java b/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java index 2e87749..3ab3c9e 100644 --- a/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java +++ b/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java @@ -86,10 +86,9 @@ @SuppressWarnings("deprecation") public class BirthdayListActivity extends BaseActivity implements AddEditFragment.NoticeDialogListener, ItemOptionsFragment.ItemOptionsListener, View.OnClickListener, GoogleApiClient.OnConnectionFailedListener { private static final int RC_SIGN_IN = 6006; - ; public static ArrayList birthdaysList = new ArrayList<>(); - public static Tracker mTracker; - static RecyclerListFragment recyclerListFragment; + public Tracker mTracker; + RecyclerListFragment recyclerListFragment; BirthdayListActivity mContext; Context mAppContext; private FloatingActionButton floatingActionButton; diff --git a/app/src/main/java/website/julianrosser/birthdays/viewholder/BirthdayViewHolder.java b/app/src/main/java/website/julianrosser/birthdays/viewholder/BirthdayViewHolder.java index 6a5e5fe..125b718 100644 --- a/app/src/main/java/website/julianrosser/birthdays/viewholder/BirthdayViewHolder.java +++ b/app/src/main/java/website/julianrosser/birthdays/viewholder/BirthdayViewHolder.java @@ -103,11 +103,6 @@ public void onClick(View v) { // Get correct position, as deleted views may have altered pos int int currentPosition = RecyclerListFragment.recyclerView.getChildAdapterPosition(itemView); - BirthdayListActivity.mTracker.send(new HitBuilders.EventBuilder() - .setCategory("Action") - .setAction("Toggle Alarm ICON") - .build()); - // Callback to BirthdayListActivity. EventBus.getDefault().post(new BirthdayAlarmToggleEvent(currentPosition)); From 6e7f63ca11b1e41383db3ebf3de938009c9c50f1 Mon Sep 17 00:00:00 2001 From: Julian Rosser Date: Tue, 18 Oct 2016 19:46:07 +0100 Subject: [PATCH 12/83] Add Thai translations --- app/src/main/res/values-th/strings.xml | 104 +++++++++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 app/src/main/res/values-th/strings.xml diff --git a/app/src/main/res/values-th/strings.xml b/app/src/main/res/values-th/strings.xml new file mode 100644 index 0000000..42f11ae --- /dev/null +++ b/app/src/main/res/values-th/strings.xml @@ -0,0 +1,104 @@ + + + + วันคล้ายวันเกิด + เพิ่มวันเกิดใหม่ + แก้ไขวันเกิด + การตั้งค่า + เตือนวันเกิด + ช่วยด้วย + + + ช่วยเหลือและความคิดเห็น + การตั้งค่า + + แก้ไข + ลบ + การแจ้งเตือนแบบก้านยาว + + + ชื่อ… + ประหยัด + ยกเลิก + + + แตะไอคอนเค้กวันเกิดที่จะเริ่มต้นการเพิ่มวันเกิด! + + วันเกิด + คือ + ในวันนี้ + พรุ่งนี้ + สัปดาห์หน้า + ใน 2 สัปดาห์ + นี้ + วัน + ชุด + ยกเลิก + ตั้งค่าการเตือน + + + ใช่ + ไม่ + วันที่ + ชื่อ + ในวันที่ + 1 วันก่อน + 2 วันก่อน + 3 วันก่อน + 4 วันก่อน + 5 วันก่อน + 6 วันก่อน + หนึ่งสัปดาห์ก่อน + 2 สัปดาห์ก่อน + + เตือนฉัน + วันแห่งการเตือนความจำ + เวลาของการแจ้งเตือน + แสดงการแจ้งเตือน? + อนุญาตให้สั่นสะเทือน? + อนุญาตให้เสียง? + แสดงการแจ้งเตือนตัวอย่างการใช้การตั้งค่าปัจจุบัน + แสดงตัวอย่างการแจ้งเตือน + เรียงวันเกิด + เรียงโดยวันเกิด + + เพิ่มวันเกิด + เพื่อเพิ่มการแจ้งเตือนวันเกิดใหม่แตะไอคอนเค้กวันเกิด จากนั้นใส่ชื่อและวันที่ + แก้ไขวันเกิด + แตะชื่อวันเกิดที่คุณต้องการจะเปลี่ยน จากนั้นคลิกที่ปุ่มแก้ไข + ลบวันเกิด + แตะวันเกิดที่คุณต้องการจะเปลี่ยนจากรายการ แล้วแตะลบ + เปิดการแจ้งเตือนออก + แตะที่ไอคอนการเตือนภัยเพื่อสลับการแจ้งเตือนปลุก + เปิดการแจ้งเตือนทั้งหมดออก + เปิดเมนูการตั้งค่าและแตะ \'อนุญาตการแจ้งเตือนการแจ้งเตือน\' option.For ความช่วยเหลือมากขึ้นเพื่อส่งข้อเสนอแนะหรือให้คำแนะนำคุณลักษณะใหม่โปรดส่งอีเมลฉันที่ julianrosser91@gmail.com หรือแตะที่ปุ่มด้านล่าง! + + + เมื่อวาน + ชุดรูปแบบสี + เลือกสี + สีน้ำเงิน + สีชมพู + สีเขียว + ตั้งค่าการแสดง + เลือกแอปอีเมลที่คุณต้องการ + ส่งอีเมลล์ + ได้รับความช่วยเหลือให้ข้อเสนอแนะหรือให้คำแนะนำคุณลักษณะใหม่ + "สำหรับการแจ้งเตือน " + " สำหรับปีถัดไป" + รวมถึงปี + ที่ถูกลบ + แก้ + + + นำเข้าที่ติดต่อ + ไม่มีรายชื่อที่พบ + กรุณาเพิ่มข้อมูลวันเกิดให้กับผู้ติดต่อของคุณสำหรับพวกเขาที่จะแสดงที่นี่ + ได้รับอนุญาตที่จำเป็นในการโหลดรายชื่อผู้ติดต่อ + ได้รับการเพิ่มแล้ว + N/A + ที่เพิ่ม + นโยบายความเป็นส่วนตัว + ข้อผิดพลาดโปรดลองอีกครั้ง … + + From c0cf29f24d409af5d1671afd02a6d14d1b945931 Mon Sep 17 00:00:00 2001 From: Julian Rosser Date: Tue, 18 Oct 2016 22:13:38 +0100 Subject: [PATCH 13/83] Save birthdays do database test. Strings for NavDrawer. --- app/build.gradle | 1 + .../birthdays/BirthdayReminder.java | 19 ++++++++++++ .../activities/BirthdayListActivity.java | 31 ++++++++++++++++--- .../adapter/BirthdayViewAdapter.java | 2 +- .../birthdays/adapter/ContactAdapter.java | 2 +- .../viewholder/BirthdayViewHolder.java | 5 +-- .../viewholder/ContactViewHolder.java | 2 +- app/src/main/res/menu/menu_main.xml | 8 +++-- app/src/main/res/menu/menu_nav_drawer.xml | 8 ++--- app/src/main/res/values/strings.xml | 5 +++ 10 files changed, 65 insertions(+), 18 deletions(-) rename app/src/main/java/website/julianrosser/birthdays/{ => views}/viewholder/BirthdayViewHolder.java (95%) rename app/src/main/java/website/julianrosser/birthdays/{ => views}/viewholder/ContactViewHolder.java (98%) diff --git a/app/build.gradle b/app/build.gradle index 2342f9d..8f902dc 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -40,6 +40,7 @@ dependencies { compile 'com.google.firebase:firebase-core:9.6.1' compile 'com.google.firebase:firebase-crash:9.6.1' compile 'com.google.firebase:firebase-auth:9.6.1' + compile 'com.google.firebase:firebase-database:9.6.1' compile 'com.google.android.gms:play-services-auth:9.6.1' compile 'com.google.android.gms:play-services-auth:9.6.1' compile 'com.squareup.picasso:picasso:2.5.2' diff --git a/app/src/main/java/website/julianrosser/birthdays/BirthdayReminder.java b/app/src/main/java/website/julianrosser/birthdays/BirthdayReminder.java index 7f48c1d..ebfaeef 100644 --- a/app/src/main/java/website/julianrosser/birthdays/BirthdayReminder.java +++ b/app/src/main/java/website/julianrosser/birthdays/BirthdayReminder.java @@ -2,10 +2,16 @@ import android.app.Application; +import com.google.firebase.FirebaseApp; +import com.google.firebase.database.DatabaseReference; +import com.google.firebase.database.FirebaseDatabase; + public class BirthdayReminder extends Application { // Application reference private static BirthdayReminder sInstance; + private FirebaseDatabase mFirebaseDatabase; + private DatabaseReference mDatabase; public static BirthdayReminder getInstance() { return sInstance; @@ -15,6 +21,19 @@ public static BirthdayReminder getInstance() { public void onCreate() { super.onCreate(); sInstance = this; + FirebaseApp.initializeApp(this); + + mFirebaseDatabase = FirebaseDatabase.getInstance(); + mDatabase = mFirebaseDatabase.getReference("message"); } + public DatabaseReference getDatabaseReference() { + if (mDatabase == null) { + if (mFirebaseDatabase == null) { + mFirebaseDatabase = FirebaseDatabase.getInstance(); + } + mDatabase = mFirebaseDatabase.getReference("message"); + } + return mDatabase; + } } diff --git a/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java b/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java index 3ab3c9e..ce02070 100644 --- a/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java +++ b/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java @@ -52,6 +52,8 @@ import com.google.firebase.auth.FirebaseAuth; import com.google.firebase.auth.FirebaseUser; import com.google.firebase.auth.GoogleAuthProvider; +import com.google.firebase.database.DatabaseReference; +import com.google.firebase.database.FirebaseDatabase; import com.squareup.picasso.Picasso; import org.apache.commons.lang3.text.WordUtils; @@ -66,8 +68,10 @@ import java.io.Writer; import java.util.ArrayList; import java.util.Date; +import java.util.List; import java.util.Random; +import website.julianrosser.birthdays.BirthdayReminder; import website.julianrosser.birthdays.Constants; import website.julianrosser.birthdays.R; import website.julianrosser.birthdays.adapter.BirthdayViewAdapter; @@ -339,8 +343,6 @@ public void onClick(View v) { // This is to help the fragment keep its state on rotation recyclerListFragment.setRetainInstance(true); - EventBus.getDefault().register(this); - // Obtain the shared Tracker instance. mTracker = getDefaultTracker(); @@ -391,6 +393,7 @@ private void handleSignInResult(GoogleSignInResult result) { firebaseAuthWithGoogle(account); } else { setNavHeaderUserState(NavHeaderState.LOGGED_OUT); + Snackbar.make(floatingActionButton, "Error while logging in", Snackbar.LENGTH_SHORT).show(); // todo - translate } } @@ -544,6 +547,14 @@ protected void onResume() { // Tracker mTracker.setScreenName("BirthdayListActivity"); mTracker.send(new HitBuilders.ScreenViewBuilder().build()); + + EventBus.getDefault().register(this); + } + + @Override + protected void onPause() { + super.onPause(); + EventBus.getDefault().unregister(this); } @Override @@ -567,7 +578,6 @@ protected void onStop() { } AppIndex.AppIndexApi.end(mClient, getAction()); mClient.disconnect(); - EventBus.getDefault().unregister(this); super.onStop(); } @@ -810,10 +820,20 @@ public boolean onOptionsItemSelected(MenuItem item) { if (id == R.id.action_sign_out) { signOutGoogle(); return true; + } else if (id == R.id.action_firebase) { + performFirebaseAction(); + return true; } return super.onOptionsItemSelected(item); } + private void performFirebaseAction() { + DatabaseReference dbr = BirthdayReminder.getInstance().getDatabaseReference(); + for (int i = 0; i < birthdaysList.size(); i++) { + dbr.child("" + i).setValue(birthdaysList.get(i).getName()); + } + } + private void signOutGoogle() { Auth.GoogleSignInApi.signOut(mGoogleApiClient).setResultCallback( new ResultCallback() { @@ -879,15 +899,16 @@ public void onClick(View v) { @Subscribe public void onMessageEvent(BirthdaysLoadedEvent event) { birthdaysList = event.getBirthdays(); + RecyclerListFragment.showEmptyMessageIfRequired(); } @Subscribe - public void onMessageEvent(BirthdayAlarmToggleEvent event) { + public void onAlarmToggle(BirthdayAlarmToggleEvent event) { alarmToggled(event.getCurrentPosition()); } @Subscribe - public void onMessageEvent(BirthdayItemClickEvent event) { + public void onBirthdayClicked(BirthdayItemClickEvent event) { showItemOptionsFragment(event.getCurrentPosition()); } diff --git a/app/src/main/java/website/julianrosser/birthdays/adapter/BirthdayViewAdapter.java b/app/src/main/java/website/julianrosser/birthdays/adapter/BirthdayViewAdapter.java index ee84a3c..2661510 100644 --- a/app/src/main/java/website/julianrosser/birthdays/adapter/BirthdayViewAdapter.java +++ b/app/src/main/java/website/julianrosser/birthdays/adapter/BirthdayViewAdapter.java @@ -13,7 +13,7 @@ import website.julianrosser.birthdays.model.Birthday; import website.julianrosser.birthdays.R; import website.julianrosser.birthdays.model.tasks.LoadBirthdaysTask; -import website.julianrosser.birthdays.viewholder.BirthdayViewHolder; +import website.julianrosser.birthdays.views.viewholder.BirthdayViewHolder; public class BirthdayViewAdapter extends RecyclerView.Adapter { diff --git a/app/src/main/java/website/julianrosser/birthdays/adapter/ContactAdapter.java b/app/src/main/java/website/julianrosser/birthdays/adapter/ContactAdapter.java index 38276a0..6f8d708 100644 --- a/app/src/main/java/website/julianrosser/birthdays/adapter/ContactAdapter.java +++ b/app/src/main/java/website/julianrosser/birthdays/adapter/ContactAdapter.java @@ -8,7 +8,7 @@ import java.util.ArrayList; import website.julianrosser.birthdays.model.Contact; -import website.julianrosser.birthdays.viewholder.ContactViewHolder; +import website.julianrosser.birthdays.views.viewholder.ContactViewHolder; import website.julianrosser.birthdays.R; public class ContactAdapter diff --git a/app/src/main/java/website/julianrosser/birthdays/viewholder/BirthdayViewHolder.java b/app/src/main/java/website/julianrosser/birthdays/views/viewholder/BirthdayViewHolder.java similarity index 95% rename from app/src/main/java/website/julianrosser/birthdays/viewholder/BirthdayViewHolder.java rename to app/src/main/java/website/julianrosser/birthdays/views/viewholder/BirthdayViewHolder.java index 125b718..07bee87 100644 --- a/app/src/main/java/website/julianrosser/birthdays/viewholder/BirthdayViewHolder.java +++ b/app/src/main/java/website/julianrosser/birthdays/views/viewholder/BirthdayViewHolder.java @@ -1,4 +1,4 @@ -package website.julianrosser.birthdays.viewholder; +package website.julianrosser.birthdays.views.viewholder; import android.graphics.drawable.Drawable; import android.support.design.widget.Snackbar; @@ -7,12 +7,9 @@ import android.widget.ImageView; import android.widget.TextView; -import com.google.android.gms.analytics.HitBuilders; - import org.greenrobot.eventbus.EventBus; import website.julianrosser.birthdays.R; -import website.julianrosser.birthdays.activities.BirthdayListActivity; import website.julianrosser.birthdays.fragments.RecyclerListFragment; import website.julianrosser.birthdays.model.Birthday; import website.julianrosser.birthdays.model.events.BirthdayAlarmToggleEvent; diff --git a/app/src/main/java/website/julianrosser/birthdays/viewholder/ContactViewHolder.java b/app/src/main/java/website/julianrosser/birthdays/views/viewholder/ContactViewHolder.java similarity index 98% rename from app/src/main/java/website/julianrosser/birthdays/viewholder/ContactViewHolder.java rename to app/src/main/java/website/julianrosser/birthdays/views/viewholder/ContactViewHolder.java index 311edc6..784163f 100644 --- a/app/src/main/java/website/julianrosser/birthdays/viewholder/ContactViewHolder.java +++ b/app/src/main/java/website/julianrosser/birthdays/views/viewholder/ContactViewHolder.java @@ -1,4 +1,4 @@ -package website.julianrosser.birthdays.viewholder; +package website.julianrosser.birthdays.views.viewholder; import android.support.design.widget.Snackbar; import android.support.v7.widget.RecyclerView; diff --git a/app/src/main/res/menu/menu_main.xml b/app/src/main/res/menu/menu_main.xml index 4f4a163..ffaa238 100644 --- a/app/src/main/res/menu/menu_main.xml +++ b/app/src/main/res/menu/menu_main.xml @@ -5,7 +5,11 @@ - + android:title="@string/sign_out" /> + + diff --git a/app/src/main/res/menu/menu_nav_drawer.xml b/app/src/main/res/menu/menu_nav_drawer.xml index 42004a8..48be83b 100644 --- a/app/src/main/res/menu/menu_nav_drawer.xml +++ b/app/src/main/res/menu/menu_nav_drawer.xml @@ -7,20 +7,20 @@ android:id="@+id/menu_birthdays" android:checked="true" android:icon="@drawable/ic_cake_white_24dp" - android:title="Birthdays" /> + android:title="@string/app_name" /> + android:title="@string/import_contacts" /> + android:title="@string/action_settings" /> + android:title="@string/action_help" /> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 92a0147..45a519e 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -155,4 +155,9 @@ Privacy Policy Error, please try again … + + + Sign out + + From dd5b74d39d363e04f4917a5ce742a3977828ab97 Mon Sep 17 00:00:00 2001 From: Julian Rosser Date: Wed, 19 Oct 2016 08:45:07 +0100 Subject: [PATCH 14/83] Fixed bug where contact with no birthdate wouldn't import correctly. --- .../website/julianrosser/birthdays/Utils.java | 3 ++ .../fragments/ImportContactFragment.java | 36 +++++++++---------- .../julianrosser/birthdays/model/Contact.java | 10 +++--- .../views/viewholder/ContactViewHolder.java | 8 +++-- 4 files changed, 32 insertions(+), 25 deletions(-) diff --git a/app/src/main/java/website/julianrosser/birthdays/Utils.java b/app/src/main/java/website/julianrosser/birthdays/Utils.java index 119739d..c335218 100644 --- a/app/src/main/java/website/julianrosser/birthdays/Utils.java +++ b/app/src/main/java/website/julianrosser/birthdays/Utils.java @@ -13,6 +13,9 @@ public static Date stringToDate(String birthdayString) { Date date = new Date(); SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd"); try { + if (birthdayString.startsWith("-")) { + birthdayString = birthdayString.replaceFirst("-", "1990"); + } date = format.parse(birthdayString); System.out.println(date); } catch (java.text.ParseException e) { diff --git a/app/src/main/java/website/julianrosser/birthdays/fragments/ImportContactFragment.java b/app/src/main/java/website/julianrosser/birthdays/fragments/ImportContactFragment.java index 0c01edb..7c934d2 100644 --- a/app/src/main/java/website/julianrosser/birthdays/fragments/ImportContactFragment.java +++ b/app/src/main/java/website/julianrosser/birthdays/fragments/ImportContactFragment.java @@ -10,7 +10,6 @@ import android.support.annotation.Nullable; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; -import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -23,11 +22,10 @@ import java.util.Collections; import java.util.Comparator; -import website.julianrosser.birthdays.activities.BirthdayListActivity; -import website.julianrosser.birthdays.model.Birthday; -import website.julianrosser.birthdays.model.Contact; -import website.julianrosser.birthdays.adapter.ContactAdapter; import website.julianrosser.birthdays.R; +import website.julianrosser.birthdays.Utils; +import website.julianrosser.birthdays.adapter.ContactAdapter; +import website.julianrosser.birthdays.model.Contact; import website.julianrosser.birthdays.model.events.ContactsLoadedEvent; /** @@ -39,10 +37,10 @@ public class ImportContactFragment extends android.support.v4.app.Fragment { public static ContactAdapter mAdapter; // Reference to recyclerView - public static RecyclerView recyclerView; + public RecyclerView recyclerView; // Reference to view which shows when list empty. - static View emptyView; + public View emptyView; public ArrayList contacts; private ProgressBar progressBar; @@ -131,14 +129,14 @@ private ArrayList loadContacts() { cur.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME)); ContentResolver bd = getActivity().getContentResolver(); Cursor bdc = bd.query(android.provider.ContactsContract.Data.CONTENT_URI, - new String[] { ContactsContract.CommonDataKinds.Event.DATA }, - android.provider.ContactsContract.Data.CONTACT_ID+" = "+id+" AND "+ ContactsContract.Contacts.Data.MIMETYPE+" = '"+ ContactsContract.CommonDataKinds.Event.CONTENT_ITEM_TYPE+"' AND "+ ContactsContract.CommonDataKinds.Event.TYPE+" = "+ ContactsContract.CommonDataKinds.Event.TYPE_BIRTHDAY, null, android.provider.ContactsContract.Data.DISPLAY_NAME); + new String[]{ContactsContract.CommonDataKinds.Event.DATA}, + android.provider.ContactsContract.Data.CONTACT_ID + " = " + id + " AND " + ContactsContract.Contacts.Data.MIMETYPE + " = '" + ContactsContract.CommonDataKinds.Event.CONTENT_ITEM_TYPE + "' AND " + ContactsContract.CommonDataKinds.Event.TYPE + " = " + ContactsContract.CommonDataKinds.Event.TYPE_BIRTHDAY, null, android.provider.ContactsContract.Data.DISPLAY_NAME); if (bdc != null && bdc.getCount() > 0) { while (bdc.moveToNext()) { String birthday = bdc.getString(0); - Log.i(getClass().getSimpleName(), "Name: " + name + " // Birth: " + birthday); +// Log.i(getClass().getSimpleName(), "Name: " + name + " // Birth: " + birthday); // now "id" is the user's unique ID, "name" is his full name and "birthday" is the date and time of his birth - Contact con = new Contact(name, birthday); + Contact con = new Contact(name, Utils.stringToDate(birthday)); contactsList.add(con); } } @@ -160,15 +158,22 @@ private void setContacts(ArrayList contacts) { } } - /** Detect whether contacts were found, and display empty message if necessary. */ + /** + * Detect whether contacts were found, and display empty message if necessary. + */ public void showEmptyMessageIfRequired() { - if (contacts.size() == 0){ + if (contacts.size() == 0) { emptyView.setVisibility(View.VISIBLE); } else { emptyView.setVisibility(View.INVISIBLE); } } + @Subscribe + public void onMessageEvent(ContactsLoadedEvent event) { + setContacts(event.getContacts()); + } + private class LoadContactsTask extends AsyncTask> { @Override @@ -187,9 +192,4 @@ protected void onPostExecute(ArrayList result) { EventBus.getDefault().post(new ContactsLoadedEvent(result)); } } - - @Subscribe - public void onMessageEvent(ContactsLoadedEvent event) { - setContacts(event.getContacts()); - } } \ No newline at end of file diff --git a/app/src/main/java/website/julianrosser/birthdays/model/Contact.java b/app/src/main/java/website/julianrosser/birthdays/model/Contact.java index 7717294..c0be6be 100644 --- a/app/src/main/java/website/julianrosser/birthdays/model/Contact.java +++ b/app/src/main/java/website/julianrosser/birthdays/model/Contact.java @@ -1,11 +1,13 @@ package website.julianrosser.birthdays.model; +import java.util.Date; + public class Contact { - String name; - String birthday; + private String name; + private Date birthday; - public Contact(String name, String birthday) { + public Contact(String name, Date birthday) { this.name = name; this.birthday = birthday; } @@ -14,7 +16,7 @@ public String getName() { return name; } - public String getBirthday() { + public Date getBirthday() { return birthday; } } diff --git a/app/src/main/java/website/julianrosser/birthdays/views/viewholder/ContactViewHolder.java b/app/src/main/java/website/julianrosser/birthdays/views/viewholder/ContactViewHolder.java index 784163f..e21ea59 100644 --- a/app/src/main/java/website/julianrosser/birthdays/views/viewholder/ContactViewHolder.java +++ b/app/src/main/java/website/julianrosser/birthdays/views/viewholder/ContactViewHolder.java @@ -44,12 +44,15 @@ public ContactViewHolder(View itemView) { public void onClick(View v) { Contact contact = (Contact) v.getTag(); - Date birthdate = Utils.stringToDate(contact.getBirthday()); + Date birthdate = contact.getBirthday(); if ((birthdate.getYear() + 1900) < 1902) { birthdate.setYear(1990); } else { birthdate.setYear(birthdate.getYear() + 1900); } + birthdate.setMonth(birthdate.getMonth()); + birthdate.setDate(birthdate.getDate()); + Birthday birthday = new Birthday(contact.getName(), birthdate, true, false); if (BirthdayListActivity.isContactAlreadyAdded(birthday)) { @@ -70,8 +73,7 @@ public void setTag(Contact contact) { imageAdd.setTag(contact); } - public void setDate(String birthday) { - Date birthdate = Utils.stringToDate(birthday); + public void setDate(Date birthdate) { textDateDay.setText("" + getBirthDay(birthdate)); textDateMonth.setText("" + getBirthMonth(birthdate)); } From dd2a5b1af21429e1bc7f45e1eb1ac67511250fa3 Mon Sep 17 00:00:00 2001 From: Julian Rosser Date: Wed, 18 Jan 2017 23:37:50 +0000 Subject: [PATCH 15/83] Update Firebase SDK, add dependencies. --- app/build.gradle | 28 ++++++++++++++-------------- build.gradle | 2 +- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 8f902dc..5ce710a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -29,21 +29,21 @@ android { } dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) - compile 'com.android.support:appcompat-v7:23.1.1' - compile 'com.android.support:recyclerview-v7:23.1.1' - compile 'com.android.support:support-v4:23.1.1' - compile 'org.apache.commons:commons-lang3:3.0' - compile 'com.android.support:design:23.1.1' - compile 'com.google.android.gms:play-services-appindexing:9.6.1' - compile 'com.google.android.gms:play-services-analytics:9.6.1' + compile 'com.google.android.gms:play-services-auth:10.0.1' compile 'org.greenrobot:eventbus:3.0.0' - compile 'com.google.firebase:firebase-core:9.6.1' - compile 'com.google.firebase:firebase-crash:9.6.1' - compile 'com.google.firebase:firebase-auth:9.6.1' - compile 'com.google.firebase:firebase-database:9.6.1' - compile 'com.google.android.gms:play-services-auth:9.6.1' - compile 'com.google.android.gms:play-services-auth:9.6.1' - compile 'com.squareup.picasso:picasso:2.5.2' + compile 'com.android.support:appcompat-v7:23.4.0' + compile 'com.android.support:recyclerview-v7:23.4.0' + compile 'com.android.support:support-v4:23.4.0' + compile 'org.apache.commons:commons-lang3:3.3.2' + compile 'com.android.support:design:23.4.0' + compile 'com.google.firebase:firebase-appindexing:10.0.1' + compile 'com.google.android.gms:play-services-analytics:10.0.1' compile 'org.greenrobot:eventbus:3.0.0' + compile 'com.google.firebase:firebase-core:10.0.1' + compile 'com.google.firebase:firebase-crash:10.0.1' + compile 'com.google.firebase:firebase-auth:10.0.1' + compile 'com.google.firebase:firebase-database:10.0.1' + compile 'com.google.android.gms:play-services-auth:10.0.1' + compile 'com.squareup.picasso:picasso:2.5.2' } apply plugin: 'com.google.gms.google-services' \ No newline at end of file diff --git a/build.gradle b/build.gradle index 10d1dbd..7dd9d2d 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:2.2.1' + classpath 'com.android.tools.build:gradle:2.2.2' classpath 'com.google.gms:google-services:3.0.0' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files From 7f32832a89f7ad04a476bcd0ca92fe5e8e2cbe6a Mon Sep 17 00:00:00 2001 From: Julian Rosser Date: Wed, 18 Jan 2017 23:39:05 +0000 Subject: [PATCH 16/83] Created classes associated with Firebase --- .../julianrosser/birthdays/Constants.java | 6 ++ .../birthdays/database/FirebaseHelper.java | 98 +++++++++++++++++++ .../birthdays/model/FirebaseBirthday.java | 40 ++++++++ 3 files changed, 144 insertions(+) create mode 100644 app/src/main/java/website/julianrosser/birthdays/database/FirebaseHelper.java create mode 100644 app/src/main/java/website/julianrosser/birthdays/model/FirebaseBirthday.java diff --git a/app/src/main/java/website/julianrosser/birthdays/Constants.java b/app/src/main/java/website/julianrosser/birthdays/Constants.java index 51f2401..dc88045 100644 --- a/app/src/main/java/website/julianrosser/birthdays/Constants.java +++ b/app/src/main/java/website/julianrosser/birthdays/Constants.java @@ -12,4 +12,10 @@ public class Constants { public static int CONTACT_PERMISSION_CODE = 3; public static String INTENT_FROM_KEY = "intent_from_key"; public static String GOOGLE_SIGN_IN_KEY = "1072707269724-3v8qbbu86kmfs252eu44amna8cpqqj9c.apps.googleusercontent.com"; + + /** + * Firebase constants + */ + public static String TABLE_BIRTHDAYS = "birthdays"; + } diff --git a/app/src/main/java/website/julianrosser/birthdays/database/FirebaseHelper.java b/app/src/main/java/website/julianrosser/birthdays/database/FirebaseHelper.java new file mode 100644 index 0000000..9cb748b --- /dev/null +++ b/app/src/main/java/website/julianrosser/birthdays/database/FirebaseHelper.java @@ -0,0 +1,98 @@ +package website.julianrosser.birthdays.database; + +import com.google.firebase.auth.FirebaseUser; +import com.google.firebase.database.DataSnapshot; +import com.google.firebase.database.DatabaseError; +import com.google.firebase.database.DatabaseReference; +import com.google.firebase.database.ServerValue; +import com.google.firebase.database.ValueEventListener; + +import org.greenrobot.eventbus.EventBus; + +import java.util.ArrayList; + +import website.julianrosser.birthdays.BirthdayReminder; +import website.julianrosser.birthdays.Constants; +import website.julianrosser.birthdays.Utils; +import website.julianrosser.birthdays.activities.BirthdayListActivity; +import website.julianrosser.birthdays.model.Birthday; +import website.julianrosser.birthdays.model.FirebaseBirthday; +import website.julianrosser.birthdays.model.events.BirthdaysLoadedEvent; + +public class FirebaseHelper { + + public static void loadBirthdays() { + + String userID = BirthdayReminder.getInstance().getCurrentUser().getUid(); + + // load birthdays from FB + final DatabaseReference ref = BirthdayReminder.getInstance().getDatabaseReference().child(userID).child(Constants.TABLE_BIRTHDAYS); // todo - refacteringignignigng + ref.addListenerForSingleValueEvent(new ValueEventListener() { + @Override + public void onDataChange(DataSnapshot dataSnapshot) { + ArrayList firebaseBirthdays = new ArrayList<>(); + + for (DataSnapshot birthdaySnap : dataSnapshot.getChildren()) { + FirebaseBirthday firebaseBirthday = birthdaySnap.getValue(FirebaseBirthday.class); + firebaseBirthdays.add(firebaseBirthday); + } + + ArrayList birthdays = new ArrayList<>(); + for (FirebaseBirthday fb : firebaseBirthdays) { + birthdays.add(Birthday.fromFB(fb)); + } + + EventBus.getDefault().post(new BirthdaysLoadedEvent(birthdays)); + + ref.removeEventListener(this); + } + + @Override + public void onCancelled(DatabaseError databaseError) { + ref.removeEventListener(this); + } + }); + } + + public enum FirebaseUpdate { + CREATE, + UPDATE, + DELETE, + } + + public static void saveBirthdayChange(Birthday birthday, FirebaseUpdate state) { + + FirebaseUser user = BirthdayReminder.getInstance().getCurrentUser(); + if (user == null || Utils.isStringEmpty(user.getUid())) { + return; + } + DatabaseReference databaseReference = BirthdayReminder.getInstance().getDatabaseReference(); + databaseReference = databaseReference.child(user.getUid()).child(Constants.TABLE_BIRTHDAYS) + .child(String.valueOf(birthday.getUID())); + + FirebaseBirthday firebaseBirthday = FirebaseBirthday.convertToFirebaseBirthday(birthday); + + switch (state) { + case CREATE: + databaseReference.setValue(firebaseBirthday); + break; + case UPDATE: + databaseReference.setValue(firebaseBirthday); + break; + case DELETE: + databaseReference.setValue(null); + break; + } + setLastUpdatedTime(); + } + + private static void setLastUpdatedTime() { + FirebaseUser user = BirthdayReminder.getInstance().getCurrentUser(); + if (user != null) { + DatabaseReference dbr = BirthdayReminder.getInstance().getDatabaseReference().child(user.getUid()); + dbr.child("lastUpdated").setValue(ServerValue.TIMESTAMP); + dbr.child("email").setValue(user.getEmail()); + } + } + +} diff --git a/app/src/main/java/website/julianrosser/birthdays/model/FirebaseBirthday.java b/app/src/main/java/website/julianrosser/birthdays/model/FirebaseBirthday.java new file mode 100644 index 0000000..7d3d233 --- /dev/null +++ b/app/src/main/java/website/julianrosser/birthdays/model/FirebaseBirthday.java @@ -0,0 +1,40 @@ +package website.julianrosser.birthdays.model; + +import java.util.Date; + +@SuppressWarnings("deprecation") +public class FirebaseBirthday { + + public String name; + public int dateDay; + public int dateMonth; + public int dateYear; + public boolean remind; + public boolean showYear; + public String uID; + + // Required empty constructor + public FirebaseBirthday() { + + } + + /** + * Constructor for creating new birthday. + */ + private FirebaseBirthday(String name, Date dateOfBirthday, int year, boolean notifyUserOfBirthday, boolean includeYear, String uID) { + + this.name = name; + this.remind = notifyUserOfBirthday; + this.showYear = includeYear; + dateDay = dateOfBirthday.getDate(); + dateMonth = dateOfBirthday.getMonth(); + dateYear = year; + this.uID = uID; + } + + public static FirebaseBirthday convertToFirebaseBirthday(Birthday birthday) { + return new FirebaseBirthday(birthday.getName(), birthday.getDate(), birthday.getYear(), + birthday.getRemind(), birthday.shouldIncludeYear(), birthday.getUID()); + } + +} \ No newline at end of file From b7f7de48044c91099cec6a7041651919fa40b587 Mon Sep 17 00:00:00 2001 From: Julian Rosser Date: Wed, 18 Jan 2017 23:41:58 +0000 Subject: [PATCH 17/83] Refactored ImportContacts section. Removed static references, refactored LoadContactsTask, Saving to Firebase from Adapter rather than using multiple callbacks. --- .../activities/ImportContactsActivity.java | 37 ++++++- .../birthdays/adapter/ContactAdapter.java | 24 +++-- .../fragments/ImportContactFragment.java | 97 ++++--------------- .../julianrosser/birthdays/model/Contact.java | 12 ++- .../model/tasks/LoadContactsTask.java | 84 ++++++++++++++++ .../views/viewholder/ContactViewHolder.java | 64 ++++++------ 6 files changed, 189 insertions(+), 129 deletions(-) create mode 100644 app/src/main/java/website/julianrosser/birthdays/model/tasks/LoadContactsTask.java diff --git a/app/src/main/java/website/julianrosser/birthdays/activities/ImportContactsActivity.java b/app/src/main/java/website/julianrosser/birthdays/activities/ImportContactsActivity.java index 5fdc248..104d410 100644 --- a/app/src/main/java/website/julianrosser/birthdays/activities/ImportContactsActivity.java +++ b/app/src/main/java/website/julianrosser/birthdays/activities/ImportContactsActivity.java @@ -4,14 +4,16 @@ import android.os.Bundle; import android.preference.PreferenceManager; import android.support.v7.widget.Toolbar; +import android.view.MenuItem; + +import java.util.ArrayList; import website.julianrosser.birthdays.R; import website.julianrosser.birthdays.fragments.ImportContactFragment; public class ImportContactsActivity extends BaseActivity { - private ImportContactFragment recyclerListFragment; - private Toolbar toolbar; + public static String BIRTHDAYS_ARRAY_KEY = "BIRTHDAYS_ARRAY"; @Override protected void onCreate(Bundle savedInstanceState) { @@ -19,16 +21,18 @@ protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_import_contacts); - toolbar = (Toolbar) findViewById(R.id.toolbar); + Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); - // Show home button on toolbar + // Show backToBirthdays button on toolbar if (getSupportActionBar() != null) { getSupportActionBar().setDisplayHomeAsUpEnabled(true); } + ArrayList birthdayNames = getIntent().getExtras().getStringArrayList(BIRTHDAYS_ARRAY_KEY); // Create new RecyclerListFragment - recyclerListFragment = ImportContactFragment.newInstance(); + ImportContactFragment recyclerListFragment = ImportContactFragment.newInstance(); + recyclerListFragment.setBirthdayNames(birthdayNames); getSupportFragmentManager().beginTransaction() .add(R.id.container, recyclerListFragment) .commit(); @@ -49,4 +53,27 @@ public void setTheme() { setTheme(R.style.PinkTheme); } } + + @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 (id == android.R.id.home) { + backToBirthdays(); + return true; + } + return super.onOptionsItemSelected(item); + } + + @Override + public void onBackPressed() { + super.onBackPressed(); + backToBirthdays(); + } + + private void backToBirthdays() { + finish(); + } } diff --git a/app/src/main/java/website/julianrosser/birthdays/adapter/ContactAdapter.java b/app/src/main/java/website/julianrosser/birthdays/adapter/ContactAdapter.java index 6f8d708..c5d52f1 100644 --- a/app/src/main/java/website/julianrosser/birthdays/adapter/ContactAdapter.java +++ b/app/src/main/java/website/julianrosser/birthdays/adapter/ContactAdapter.java @@ -7,18 +7,20 @@ import java.util.ArrayList; +import website.julianrosser.birthdays.R; +import website.julianrosser.birthdays.database.FirebaseHelper; +import website.julianrosser.birthdays.model.Birthday; import website.julianrosser.birthdays.model.Contact; import website.julianrosser.birthdays.views.viewholder.ContactViewHolder; -import website.julianrosser.birthdays.R; public class ContactAdapter extends RecyclerView.Adapter - { + implements ContactViewHolder.ContactCallback { - ArrayList allContacts; + private ArrayList allContacts; public ContactAdapter() { - allContacts = new ArrayList<>(); + this.allContacts = new ArrayList<>(); } @Override @@ -26,8 +28,7 @@ public ContactViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) { View itemView = LayoutInflater. from(viewGroup.getContext()). inflate(R.layout.contact_list_view, viewGroup, false); - - return new ContactViewHolder(itemView); + return new ContactViewHolder(itemView, this); } @Override @@ -36,12 +37,11 @@ public void onBindViewHolder(final ContactViewHolder viewHolder, final int posit if (allContacts.size() <= position) { return; } - Contact contact = allContacts.get(position); viewHolder.setTag(contact); viewHolder.setName(contact.getName()); viewHolder.setDate(contact.getBirthday()); - viewHolder.setImageIcon(contact.getName()); + viewHolder.setImageIcon(contact.isAlreadyAdded()); } @Override @@ -59,4 +59,12 @@ public void setData(ArrayList contactsList) { allContacts = contactsList; notifyDataSetChanged(); } + + // Callback from ViewHolder + @Override + public void addContact(Contact contact) { + // todo - if year is available, INCLUDE! (Last Parameter!!!! see email from user) + Birthday contactBirthday = new Birthday(contact.getName(), contact.getBirthday(), true, false); + FirebaseHelper.saveBirthdayChange(contactBirthday, FirebaseHelper.FirebaseUpdate.CREATE); + } } \ No newline at end of file diff --git a/app/src/main/java/website/julianrosser/birthdays/fragments/ImportContactFragment.java b/app/src/main/java/website/julianrosser/birthdays/fragments/ImportContactFragment.java index 7c934d2..82e37f4 100644 --- a/app/src/main/java/website/julianrosser/birthdays/fragments/ImportContactFragment.java +++ b/app/src/main/java/website/julianrosser/birthdays/fragments/ImportContactFragment.java @@ -1,12 +1,8 @@ package website.julianrosser.birthdays.fragments; -import android.content.ContentResolver; -import android.database.Cursor; -import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; -import android.provider.ContactsContract; import android.support.annotation.Nullable; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; @@ -19,38 +15,33 @@ import org.greenrobot.eventbus.Subscribe; import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; import website.julianrosser.birthdays.R; -import website.julianrosser.birthdays.Utils; import website.julianrosser.birthdays.adapter.ContactAdapter; import website.julianrosser.birthdays.model.Contact; import website.julianrosser.birthdays.model.events.ContactsLoadedEvent; +import website.julianrosser.birthdays.model.tasks.LoadContactsTask; /** * Main view. Fragment which */ public class ImportContactFragment extends android.support.v4.app.Fragment { - // Reference to mAdapter - public static ContactAdapter mAdapter; - - // Reference to recyclerView - public RecyclerView recyclerView; + // Views + private ContactAdapter mAdapter; + private View emptyView; + private ProgressBar progressBar; - // Reference to view which shows when list empty. - public View emptyView; + // Data + private ArrayList contacts; + private ArrayList birthdayNames; - public ArrayList contacts; - private ProgressBar progressBar; + // Processes private LoadContactsTask loadContactsTask; - // Required empty constructor public ImportContactFragment() { } - /* Use newInstance in case in the future we want to add construction parameters or initialisation here */ public static ImportContactFragment newInstance() { return new ImportContactFragment(); @@ -60,7 +51,7 @@ public static ImportContactFragment newInstance() { public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); EventBus.getDefault().register(this); - loadContactsTask = new LoadContactsTask(); + loadContactsTask = new LoadContactsTask(getActivity(), birthdayNames); loadContactsTask.execute(); } @@ -79,31 +70,26 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, // Inflate view View view = inflater.inflate(R.layout.fragment_import_contacts, container, false); - // Initialise important reference to the main view: RecyclerView - recyclerView = (RecyclerView) view.findViewById(R.id.recyclerView); - - // Reference empty TextView + // Initialise view references + RecyclerView recyclerView = (RecyclerView) view.findViewById(R.id.recyclerView); emptyView = view.findViewById(R.id.empty_view); - progressBar = (ProgressBar) view.findViewById(R.id.progressBar); - // hide drop shadow if running lollipop or higher + // hide drop shadow if running lollipop or higher // todo - refactor to base if (Build.VERSION.SDK_INT >= 21) { view.findViewById(R.id.drop_shadow).setVisibility(View.GONE); } - // Set layout properties + // Setup Contacts RecyclerView LinearLayoutManager llm = new LinearLayoutManager(getActivity()); llm.setOrientation(LinearLayoutManager.VERTICAL); recyclerView.setLayoutManager(llm); - recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) { super.onScrollStateChanged(recyclerView, newState); } }); - // Can use this to optimize performance as RecyclerView will NOT change size. recyclerView.setHasFixedSize(true); @@ -116,39 +102,6 @@ public void onScrollStateChanged(RecyclerView recyclerView, int newState) { return view; } - private ArrayList loadContacts() { - ArrayList contactsList = new ArrayList<>(); - ContentResolver cr = getActivity().getContentResolver(); - Cursor cur = cr.query(ContactsContract.Contacts.CONTENT_URI, - null, null, null, null); - if (cur != null && cur.getCount() > 0) { - while (cur.moveToNext()) { - String id = cur.getString( - cur.getColumnIndex(ContactsContract.Contacts._ID)); - String name = cur.getString( - cur.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME)); - ContentResolver bd = getActivity().getContentResolver(); - Cursor bdc = bd.query(android.provider.ContactsContract.Data.CONTENT_URI, - new String[]{ContactsContract.CommonDataKinds.Event.DATA}, - android.provider.ContactsContract.Data.CONTACT_ID + " = " + id + " AND " + ContactsContract.Contacts.Data.MIMETYPE + " = '" + ContactsContract.CommonDataKinds.Event.CONTENT_ITEM_TYPE + "' AND " + ContactsContract.CommonDataKinds.Event.TYPE + " = " + ContactsContract.CommonDataKinds.Event.TYPE_BIRTHDAY, null, android.provider.ContactsContract.Data.DISPLAY_NAME); - if (bdc != null && bdc.getCount() > 0) { - while (bdc.moveToNext()) { - String birthday = bdc.getString(0); -// Log.i(getClass().getSimpleName(), "Name: " + name + " // Birth: " + birthday); - // now "id" is the user's unique ID, "name" is his full name and "birthday" is the date and time of his birth - Contact con = new Contact(name, Utils.stringToDate(birthday)); - contactsList.add(con); - } - } - if (bdc != null) { - bdc.close(); - } - } - cur.close(); - } - return contactsList; - } - private void setContacts(ArrayList contacts) { this.contacts = contacts; if (mAdapter != null) { @@ -165,7 +118,7 @@ public void showEmptyMessageIfRequired() { if (contacts.size() == 0) { emptyView.setVisibility(View.VISIBLE); } else { - emptyView.setVisibility(View.INVISIBLE); + emptyView.setVisibility(View.GONE); } } @@ -174,22 +127,8 @@ public void onMessageEvent(ContactsLoadedEvent event) { setContacts(event.getContacts()); } - private class LoadContactsTask extends AsyncTask> { - - @Override - protected ArrayList doInBackground(Void... params) { - contacts = loadContacts(); - Collections.sort(contacts, new Comparator() { - @Override - public int compare(Contact b1, Contact b2) { - return b1.getName().compareTo(b2.getName()); - } - }); - return contacts; - } - - protected void onPostExecute(ArrayList result) { - EventBus.getDefault().post(new ContactsLoadedEvent(result)); - } + // todo - why does this require it's own method? use constructor?!! + public void setBirthdayNames(ArrayList birthdayNames) { + this.birthdayNames = birthdayNames; } } \ No newline at end of file diff --git a/app/src/main/java/website/julianrosser/birthdays/model/Contact.java b/app/src/main/java/website/julianrosser/birthdays/model/Contact.java index c0be6be..fe7da1b 100644 --- a/app/src/main/java/website/julianrosser/birthdays/model/Contact.java +++ b/app/src/main/java/website/julianrosser/birthdays/model/Contact.java @@ -6,10 +6,12 @@ public class Contact { private String name; private Date birthday; + private boolean alreadyAdded; - public Contact(String name, Date birthday) { + public Contact(String name, Date birthday, boolean alreadyAdded) { this.name = name; this.birthday = birthday; + this.alreadyAdded = alreadyAdded; } public String getName() { @@ -19,4 +21,12 @@ public String getName() { public Date getBirthday() { return birthday; } + + public boolean isAlreadyAdded() { + return alreadyAdded; + } + + public void setAlreadyAdded(boolean alreadyAdded) { + this.alreadyAdded = alreadyAdded; + } } diff --git a/app/src/main/java/website/julianrosser/birthdays/model/tasks/LoadContactsTask.java b/app/src/main/java/website/julianrosser/birthdays/model/tasks/LoadContactsTask.java new file mode 100644 index 0000000..42ab0a8 --- /dev/null +++ b/app/src/main/java/website/julianrosser/birthdays/model/tasks/LoadContactsTask.java @@ -0,0 +1,84 @@ +package website.julianrosser.birthdays.model.tasks; + +import android.content.ContentResolver; +import android.content.Context; +import android.database.Cursor; +import android.os.AsyncTask; +import android.provider.ContactsContract; + +import org.greenrobot.eventbus.EventBus; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; + +import website.julianrosser.birthdays.Utils; +import website.julianrosser.birthdays.model.Contact; +import website.julianrosser.birthdays.model.events.ContactsLoadedEvent; + +public class LoadContactsTask extends AsyncTask> { + + private final Context mContext; + private final ArrayList birthdayNames; + + public LoadContactsTask(Context context, ArrayList birthdayNames) { + mContext = context; + this.birthdayNames = birthdayNames; + } + + @Override + protected ArrayList doInBackground(Void... params) { + ArrayList contacts = loadContacts(); + Collections.sort(contacts, new Comparator() { + @Override + public int compare(Contact b1, Contact b2) { + return b1.getName().compareTo(b2.getName()); + } + }); + return contacts; + } + + private ArrayList loadContacts() { + ArrayList contactsList = new ArrayList<>(); + ContentResolver cr = mContext.getContentResolver(); + Cursor cur = cr.query(ContactsContract.Contacts.CONTENT_URI, + null, null, null, null); + if (cur != null && cur.getCount() > 0) { + while (cur.moveToNext()) { + String id = cur.getString( + cur.getColumnIndex(ContactsContract.Contacts._ID)); + String name = cur.getString( + cur.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME)); + ContentResolver bd = mContext.getContentResolver(); + Cursor bdc = bd.query(android.provider.ContactsContract.Data.CONTENT_URI, + new String[]{ContactsContract.CommonDataKinds.Event.DATA}, + android.provider.ContactsContract.Data.CONTACT_ID + " = " + id + " AND " + ContactsContract.Contacts.Data.MIMETYPE + " = '" + ContactsContract.CommonDataKinds.Event.CONTENT_ITEM_TYPE + "' AND " + ContactsContract.CommonDataKinds.Event.TYPE + " = " + ContactsContract.CommonDataKinds.Event.TYPE_BIRTHDAY, null, android.provider.ContactsContract.Data.DISPLAY_NAME); + if (bdc != null && bdc.getCount() > 0) { + while (bdc.moveToNext()) { + String birthday = bdc.getString(0); + Contact con = new Contact(name, Utils.stringToDate(birthday), isContactAlreadyAdded(name)); + contactsList.add(con); + } + } + if (bdc != null) { + bdc.close(); + } + } + cur.close(); + } + return contactsList; + } + + private boolean isContactAlreadyAdded(String name) { + for (String birthday : birthdayNames) { + if (birthday.equals(name)) { + return true; + } + } + return false; + } + + protected void onPostExecute(ArrayList result) { + EventBus.getDefault().post(new ContactsLoadedEvent(result)); + } +} \ No newline at end of file diff --git a/app/src/main/java/website/julianrosser/birthdays/views/viewholder/ContactViewHolder.java b/app/src/main/java/website/julianrosser/birthdays/views/viewholder/ContactViewHolder.java index e21ea59..c8e3082 100644 --- a/app/src/main/java/website/julianrosser/birthdays/views/viewholder/ContactViewHolder.java +++ b/app/src/main/java/website/julianrosser/birthdays/views/viewholder/ContactViewHolder.java @@ -7,13 +7,10 @@ import android.widget.ImageView; import android.widget.TextView; -import java.util.ArrayList; import java.util.Date; import website.julianrosser.birthdays.R; import website.julianrosser.birthdays.Utils; -import website.julianrosser.birthdays.activities.BirthdayListActivity; -import website.julianrosser.birthdays.model.Birthday; import website.julianrosser.birthdays.model.Contact; /** @@ -27,8 +24,13 @@ public class ContactViewHolder extends RecyclerView.ViewHolder implements View.O private TextView textName; private ImageView imageAdd; private View container; + private ContactCallback contactCallback; - public ContactViewHolder(View itemView) { + public interface ContactCallback { + void addContact(Contact contact); + } + + public ContactViewHolder(View itemView, ContactCallback contactCallback) { super(itemView); // Set up references @@ -38,30 +40,7 @@ public ContactViewHolder(View itemView) { textDateMonth = (TextView) itemView.findViewById(R.id.dateMonth); imageAdd = (ImageView) itemView.findViewById(R.id.addImage); imageAdd.setOnClickListener(this); - } - - @Override - public void onClick(View v) { - Contact contact = (Contact) v.getTag(); - - Date birthdate = contact.getBirthday(); - if ((birthdate.getYear() + 1900) < 1902) { - birthdate.setYear(1990); - } else { - birthdate.setYear(birthdate.getYear() + 1900); - } - birthdate.setMonth(birthdate.getMonth()); - birthdate.setDate(birthdate.getDate()); - - Birthday birthday = new Birthday(contact.getName(), birthdate, true, false); - - if (BirthdayListActivity.isContactAlreadyAdded(birthday)) { - Snackbar.make(container, contact.getName() + " " + container.getContext().getString(R.string.contact_already_added), Snackbar.LENGTH_SHORT).show(); - } else { - BirthdayListActivity.birthdaysList.add(birthday); - Snackbar.make(container, contact.getName() + " " + container.getContext().getString(R.string.added), Snackbar.LENGTH_SHORT).show(); - setImageIcon(birthday.getName()); - } + this.contactCallback = contactCallback; } public void setName(String name) { @@ -86,15 +65,28 @@ private String getBirthDay(Date date) { return "" + date.getDate() + Utils.getDateSuffix(date.getDate()); } - public void setImageIcon(String name) { - ArrayList birthdaysList = BirthdayListActivity.birthdaysList; + public void setImageIcon(boolean alreadyAdded) { + if (alreadyAdded) { + imageAdd.setImageDrawable(imageAdd.getContext().getResources().getDrawable(R.drawable.ic_done_white_24dp)); + } else { + imageAdd.setImageDrawable(imageAdd.getContext().getResources().getDrawable(R.drawable.ic_add_circle_outline_white_24dp)); + } + } - for (Birthday birthday : birthdaysList) { - if (birthday.getName().equals(name)) { - imageAdd.setImageDrawable(imageAdd.getContext().getResources().getDrawable(R.drawable.ic_done_white_24dp)); - return; - } + @Override + public void onClick(View v) { + Contact contact = (Contact) v.getTag(); + int id = v.getId(); + if (id == R.id.addImage) { + if (contact.isAlreadyAdded()) { + Snackbar.make(v, contact.getName() + " " + v.getContext().getString(R.string.contact_already_added), Snackbar.LENGTH_SHORT).show(); + } else { + Snackbar.make(v, contact.getName() + " " + v.getContext().getString(R.string.added), Snackbar.LENGTH_SHORT).show(); + setImageIcon(true); + contact.setAlreadyAdded(true); + + contactCallback.addContact(contact); + } } - imageAdd.setImageDrawable(imageAdd.getContext().getResources().getDrawable(R.drawable.ic_add_circle_outline_white_24dp)); } } \ No newline at end of file From 52255ef2700471bcf9ab58a44b8e582fc6c0e00e Mon Sep 17 00:00:00 2001 From: Julian Rosser Date: Wed, 18 Jan 2017 23:52:27 +0000 Subject: [PATCH 18/83] Refactored Settings class to SettingsFragment. --- .../activities/SettingsActivity.java | 164 +---------------- .../birthdays/fragments/SettingsFragment.java | 166 ++++++++++++++++++ 2 files changed, 168 insertions(+), 162 deletions(-) create mode 100644 app/src/main/java/website/julianrosser/birthdays/fragments/SettingsFragment.java diff --git a/app/src/main/java/website/julianrosser/birthdays/activities/SettingsActivity.java b/app/src/main/java/website/julianrosser/birthdays/activities/SettingsActivity.java index cfa4288..bf4d39e 100644 --- a/app/src/main/java/website/julianrosser/birthdays/activities/SettingsActivity.java +++ b/app/src/main/java/website/julianrosser/birthdays/activities/SettingsActivity.java @@ -1,16 +1,9 @@ package website.julianrosser.birthdays.activities; -import android.app.Notification; -import android.app.NotificationManager; -import android.app.PendingIntent; -import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.os.Build; import android.os.Bundle; -import android.preference.ListPreference; -import android.preference.Preference; -import android.preference.PreferenceFragment; import android.preference.PreferenceManager; import android.support.v7.widget.Toolbar; import android.view.View; @@ -19,15 +12,13 @@ import com.google.android.gms.analytics.HitBuilders; import com.google.android.gms.analytics.Tracker; -import website.julianrosser.birthdays.BirthdayReminder; import website.julianrosser.birthdays.R; -import website.julianrosser.birthdays.recievers.NotificationBuilderReceiver; +import website.julianrosser.birthdays.fragments.SettingsFragment; import website.julianrosser.birthdays.services.SetAlarmsService; public class SettingsActivity extends BaseActivity { - static Context mContext; - private Tracker mTracker; + public Tracker mTracker; @Override public void onCreate(Bundle savedInstanceState) { @@ -51,8 +42,6 @@ public void onCreate(Bundle savedInstanceState) { .replace(R.id.content, new SettingsFragment()) .commit(); - mContext = getApplicationContext(); - // Obtain the shared Tracker instance. mTracker = getDefaultTracker(); } @@ -77,58 +66,6 @@ protected void onPause() { } } - /** - * This method launches a test notification if the user wants to see an example of the reminder - */ - private static void launchTestNotification() { - - Context context = BirthdayReminder.getInstance(); - - int PENDING_INTENT_ID = 0; - int MY_NOTIFICATION_ID = 100; - - // Intent which opens App when notification is clicked - Intent mNotificationIntent = new Intent(); //(context, BirthdayListActivity.class); - - // Use Intent and other information to build PendingIntent - PendingIntent mContentIntent = PendingIntent.getActivity(context, PENDING_INTENT_ID, // test noti, diff number - mNotificationIntent, PendingIntent.FLAG_UPDATE_CURRENT); - - String messageText = "Julian's " + mContext.getString(R.string.birthday) + " " + mContext.getResources().getString(R.string.date_is) - + " " + mContext.getResources().getString(R.string.date_tomorrow); - - // Build notification - Notification.Builder notificationBuilder = new Notification.Builder( - context).setTicker(messageText) - .setSmallIcon(R.drawable.ic_cake_white_24dp) - .setAutoCancel(true) - .setContentTitle(context.getString(R.string.notification_title)) - .setContentText(messageText) - .setContentIntent(mContentIntent); - - if (NotificationBuilderReceiver.getVibrationAllowedPref(context)) { - notificationBuilder.setVibrate(NotificationBuilderReceiver.mVibratePattern); - } - if (NotificationBuilderReceiver.getSoundAllowedPref(context)) { - notificationBuilder.setSound(NotificationBuilderReceiver.notificationSound); - } - - // Get NotificationManager - NotificationManager mNotificationManager = (NotificationManager) context - .getSystemService(Context.NOTIFICATION_SERVICE); - - // Pass built notification to NotificationManager, depending on API level. - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { // api - // 16+ - mNotificationManager.notify(MY_NOTIFICATION_ID, - notificationBuilder.build()); - } else { - //noinspection deprecation - mNotificationManager.notify(MY_NOTIFICATION_ID, - notificationBuilder.getNotification()); - } - } - // Sets theme based on users preference public void setTheme() { SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); @@ -160,101 +97,4 @@ protected void onStop() { startService(serviceIntent); } - /** - * todo - REFACTOR - * Use separate fragment so we can keep the ActionBar - */ - public static class SettingsFragment extends PreferenceFragment implements Preference.OnPreferenceChangeListener, Preference.OnPreferenceClickListener { - - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - addPreferencesFromResource(R.xml.preferences); - - bindPreferenceSummaryToValue(findPreference(getString(R.string.pref_days_before_key))); - bindPreferenceSummaryToValue(findPreference(getString(R.string.pref_time_before_key))); - bindPreferenceSummaryToValue(findPreference(getString(R.string.pref_sort_by_key))); - - Preference testNotiPref = findPreference(getString(R.string.pref_test_notification_key)); - testNotiPref.setOnPreferenceClickListener(this); - - Preference themePref = findPreference(getString(R.string.pref_theme_key)); - themePref.setOnPreferenceClickListener(this); - themePref.setOnPreferenceChangeListener(this); - setThemeSummary(themePref); - - Preference sortByPref = findPreference(getString(R.string.pref_sort_by_key)); - sortByPref.setOnPreferenceChangeListener(this); - } - - private void setThemeSummary(Preference pref) { - ListPreference listPref = (ListPreference) pref; - pref.setSummary(listPref.getEntry()); - } - - /** - * Attaches a listener so the summary is always updated with the preference value. - * Also fires the listener once, to initialize the summary (so it shows up before the value - * is changed.) - */ - private void bindPreferenceSummaryToValue(Preference preference) { - // Set the listener to watch for value changes. - preference.setOnPreferenceChangeListener(this); - - // Trigger the listener immediately with the preference's - // current value. - onPreferenceChange(preference, - PreferenceManager - .getDefaultSharedPreferences(preference.getContext()) - .getString(preference.getKey(), "")); - } - - // Callback method for updating preference summary - @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 (since they have separate labels/values). - ListPreference listPreference = (ListPreference) preference; - int prefIndex = listPreference.findIndexOfValue(stringValue); - if (prefIndex >= 0) { - preference.setSummary(listPreference.getEntries()[prefIndex]); - } - - } else { - // For other preferences, set the summary to the value's simple string representation. - preference.setSummary(stringValue); - } - - if (preference.getKey().equals(getString(R.string.pref_theme_key))) { - // If theme preference is changed, immediately recreate the activity. - getActivity().recreate(); - // Also remove BirthdayListActivity, so it can be recreated to apply theme - - } - - return true; - } - - @Override - public boolean onPreferenceClick(Preference preference) { - - // If user clicks 'Test Reminder', launch test notification - if (preference.getKey().equals(getString(R.string.pref_test_notification_key))) { - launchTestNotification(); - } - - ((SettingsActivity)getActivity()).mTracker.send(new HitBuilders.EventBuilder() - .setCategory("Preference") - .setAction("Pref click") - .setLabel(preference.getKey()) - .build()); - - return false; - } - } } diff --git a/app/src/main/java/website/julianrosser/birthdays/fragments/SettingsFragment.java b/app/src/main/java/website/julianrosser/birthdays/fragments/SettingsFragment.java new file mode 100644 index 0000000..ab6a240 --- /dev/null +++ b/app/src/main/java/website/julianrosser/birthdays/fragments/SettingsFragment.java @@ -0,0 +1,166 @@ +package website.julianrosser.birthdays.fragments; + + +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.os.Build; +import android.os.Bundle; +import android.preference.ListPreference; +import android.preference.Preference; +import android.preference.PreferenceFragment; +import android.preference.PreferenceManager; + +import com.google.android.gms.analytics.HitBuilders; + +import website.julianrosser.birthdays.BirthdayReminder; +import website.julianrosser.birthdays.R; +import website.julianrosser.birthdays.activities.SettingsActivity; +import website.julianrosser.birthdays.recievers.NotificationBuilderReceiver; + +public class SettingsFragment extends PreferenceFragment implements Preference.OnPreferenceChangeListener, Preference.OnPreferenceClickListener { + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + addPreferencesFromResource(R.xml.preferences); + + bindPreferenceSummaryToValue(findPreference(getString(R.string.pref_days_before_key))); + bindPreferenceSummaryToValue(findPreference(getString(R.string.pref_time_before_key))); + bindPreferenceSummaryToValue(findPreference(getString(R.string.pref_sort_by_key))); + + Preference testNotiPref = findPreference(getString(R.string.pref_test_notification_key)); + testNotiPref.setOnPreferenceClickListener(this); + + Preference themePref = findPreference(getString(R.string.pref_theme_key)); + themePref.setOnPreferenceClickListener(this); + themePref.setOnPreferenceChangeListener(this); + setThemeSummary(themePref); + + Preference sortByPref = findPreference(getString(R.string.pref_sort_by_key)); + sortByPref.setOnPreferenceChangeListener(this); + } + + private void setThemeSummary(Preference pref) { + ListPreference listPref = (ListPreference) pref; + pref.setSummary(listPref.getEntry()); + } + + /** + * Attaches a listener so the summary is always updated with the preference value. + * Also fires the listener once, to initialize the summary (so it shows up before the value + * is changed.) + */ + private void bindPreferenceSummaryToValue(Preference preference) { + // Set the listener to watch for value changes. + preference.setOnPreferenceChangeListener(this); + + // Trigger the listener immediately with the preference's + // current value. + onPreferenceChange(preference, + PreferenceManager + .getDefaultSharedPreferences(preference.getContext()) + .getString(preference.getKey(), "")); + } + + // Callback method for updating preference summary + @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 (since they have separate labels/values). + ListPreference listPreference = (ListPreference) preference; + int prefIndex = listPreference.findIndexOfValue(stringValue); + if (prefIndex >= 0) { + preference.setSummary(listPreference.getEntries()[prefIndex]); + } + + } else { + // For other preferences, set the summary to the value's simple string representation. + preference.setSummary(stringValue); + } + + if (preference.getKey().equals(getString(R.string.pref_theme_key))) { + // If theme preference is changed, immediately recreate the activity. + getActivity().recreate(); + // Also remove BirthdayListActivity, so it can be recreated to apply theme + + } + + return true; + } + + @Override + public boolean onPreferenceClick(Preference preference) { + + // If user clicks 'Test Reminder', launch test notification + if (preference.getKey().equals(getString(R.string.pref_test_notification_key))) { + launchTestNotification(); + } + + ((SettingsActivity)getActivity()).mTracker.send(new HitBuilders.EventBuilder() + .setCategory("Preference") + .setAction("Pref click") + .setLabel(preference.getKey()) + .build()); + + return false; + } + + /** + * This method launches a test notification if the user wants to see an example of the reminder + */ + private void launchTestNotification() { + + Context context = BirthdayReminder.getInstance(); + + int PENDING_INTENT_ID = 0; + int MY_NOTIFICATION_ID = 100; + + // Intent which opens App when notification is clicked + Intent mNotificationIntent = new Intent(); //(context, BirthdayListActivity.class); + + // Use Intent and other information to build PendingIntent + PendingIntent mContentIntent = PendingIntent.getActivity(context, PENDING_INTENT_ID, // test noti, diff number + mNotificationIntent, PendingIntent.FLAG_UPDATE_CURRENT); + + String messageText = "Julian's " + getActivity().getString(R.string.birthday) + " " + getActivity().getResources().getString(R.string.date_is) + + " " + getActivity().getResources().getString(R.string.date_tomorrow); + + // Build notification + Notification.Builder notificationBuilder = new Notification.Builder( + context).setTicker(messageText) + .setSmallIcon(R.drawable.ic_cake_white_24dp) + .setAutoCancel(true) + .setContentTitle(context.getString(R.string.notification_title)) + .setContentText(messageText) + .setContentIntent(mContentIntent); + + if (NotificationBuilderReceiver.getVibrationAllowedPref(context)) { + notificationBuilder.setVibrate(NotificationBuilderReceiver.mVibratePattern); + } + if (NotificationBuilderReceiver.getSoundAllowedPref(context)) { + notificationBuilder.setSound(NotificationBuilderReceiver.notificationSound); + } + + // Get NotificationManager + NotificationManager mNotificationManager = (NotificationManager) context + .getSystemService(Context.NOTIFICATION_SERVICE); + + // Pass built notification to NotificationManager, depending on API level. + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { // api + // 16+ + mNotificationManager.notify(MY_NOTIFICATION_ID, + notificationBuilder.build()); + } else { + //noinspection deprecation + mNotificationManager.notify(MY_NOTIFICATION_ID, + notificationBuilder.getNotification()); + } + } +} \ No newline at end of file From be658c2052685d7e2a640ba205d825818bb7c7bd Mon Sep 17 00:00:00 2001 From: Jules Date: Sun, 22 Jan 2017 14:02:40 +0000 Subject: [PATCH 19/83] Ensure new contacts are added with correct birth year. (Remember not to use Java.Date! in the future) --- .../website/julianrosser/birthdays/model/FirebaseBirthday.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/website/julianrosser/birthdays/model/FirebaseBirthday.java b/app/src/main/java/website/julianrosser/birthdays/model/FirebaseBirthday.java index 7d3d233..a0d741c 100644 --- a/app/src/main/java/website/julianrosser/birthdays/model/FirebaseBirthday.java +++ b/app/src/main/java/website/julianrosser/birthdays/model/FirebaseBirthday.java @@ -33,7 +33,7 @@ private FirebaseBirthday(String name, Date dateOfBirthday, int year, boolean not } public static FirebaseBirthday convertToFirebaseBirthday(Birthday birthday) { - return new FirebaseBirthday(birthday.getName(), birthday.getDate(), birthday.getYear(), + return new FirebaseBirthday(birthday.getName(), birthday.getDate(), birthday.getYear() + 1900, birthday.getRemind(), birthday.shouldIncludeYear(), birthday.getUID()); } From 3da621788a84756d7a5b27ec606c5cad022b21b7 Mon Sep 17 00:00:00 2001 From: Julian Rosser Date: Mon, 30 Jan 2017 23:20:52 +0000 Subject: [PATCH 20/83] Birthdays loaded from Firebase Database in Fragment. --- .../adapter/BirthdayViewAdapter.java | 53 ++++---- .../birthdays/database/FirebaseHelper.java | 51 ++++---- .../fragments/RecyclerListFragment.java | 116 +++++++++++++++--- 3 files changed, 144 insertions(+), 76 deletions(-) diff --git a/app/src/main/java/website/julianrosser/birthdays/adapter/BirthdayViewAdapter.java b/app/src/main/java/website/julianrosser/birthdays/adapter/BirthdayViewAdapter.java index 2661510..4bc576f 100644 --- a/app/src/main/java/website/julianrosser/birthdays/adapter/BirthdayViewAdapter.java +++ b/app/src/main/java/website/julianrosser/birthdays/adapter/BirthdayViewAdapter.java @@ -9,24 +9,17 @@ import java.util.Collections; import java.util.Comparator; -import website.julianrosser.birthdays.activities.BirthdayListActivity; -import website.julianrosser.birthdays.model.Birthday; import website.julianrosser.birthdays.R; -import website.julianrosser.birthdays.model.tasks.LoadBirthdaysTask; +import website.julianrosser.birthdays.model.Birthday; import website.julianrosser.birthdays.views.viewholder.BirthdayViewHolder; public class BirthdayViewAdapter extends RecyclerView.Adapter { + private ArrayList birthdays; + // Constructor - public BirthdayViewAdapter(ArrayList birthdayData) { // - - if (birthdayData == null) { - BirthdayListActivity.birthdaysList = new ArrayList<>(); - } else if (birthdayData.size() == 0) { - // After Adapter is constructed, start the process of loading data - LoadBirthdaysTask loadBirthdaysTask = new LoadBirthdaysTask(); - loadBirthdaysTask.execute(); - } + public BirthdayViewAdapter() { + birthdays = new ArrayList<>(); } @Override @@ -41,7 +34,7 @@ public BirthdayViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) @Override public void onBindViewHolder(final BirthdayViewHolder viewHolder, final int position) { // Get reference to birthday - final Birthday birthday = BirthdayListActivity.birthdaysList.get(position); + final Birthday birthday = birthdays.get(position); viewHolder.setTag(birthday); viewHolder.showView(); // Pass data to the TextViews @@ -50,8 +43,6 @@ public void onBindViewHolder(final BirthdayViewHolder viewHolder, final int posi viewHolder.setBirthday(birthday.getBirthDay(), birthday.getBirthMonth()); viewHolder.displayAgeIfNeeded(birthday.shouldIncludeYear(), birthday.getYear(), birthday.getAge()); viewHolder.setImageIcon(birthday.getRemindAlarmDrawable()); - viewHolder.setImageClickListener(); - } @Override @@ -60,12 +51,16 @@ public void onViewRecycled(BirthdayViewHolder holder) { super.onViewRecycled(holder); } + public void setData(ArrayList birthdays) { + this.birthdays = birthdays; + notifyDataSetChanged(); + } + // use this method to find out whether edit will change order of birthdays - public static boolean willChangeDateOrder(Birthday b) { - ArrayList originalOrder = BirthdayListActivity.birthdaysList; + public boolean willChangeDateOrder(Birthday b) { + ArrayList originalOrder = birthdays; int originalPos = originalOrder.indexOf(b); - //Sorting Collections.sort(originalOrder, new Comparator() { @Override @@ -73,16 +68,14 @@ public int compare(Birthday b1, Birthday b2) { return b1.getDate().compareTo(b2.getDate()); } }); - return originalPos != originalOrder.indexOf(b); } // use this method to find out whether edit will change order of birthdays - public static boolean willChangeNameOrder(Birthday b) { - ArrayList originalOrder = BirthdayListActivity.birthdaysList; + public boolean willChangeNameOrder(Birthday b) { + ArrayList originalOrder = birthdays; int originalPos = originalOrder.indexOf(b); - //Sorting Collections.sort(originalOrder, new Comparator() { @Override @@ -90,20 +83,16 @@ public int compare(Birthday b1, Birthday b2) { return b1.getName().compareTo(b2.getName()); } }); - return originalPos != originalOrder.indexOf(b); } // Sort Birthday array by closest date - public static void sortBirthdaysByDate() { - - - for (Birthday b : BirthdayListActivity.birthdaysList) { + public void sortBirthdaysByDate() { + for (Birthday b : birthdays) { b.setYearOfDate(Birthday.getYearOfNextBirthday(b.getDate())); } - //Sorting - Collections.sort(BirthdayListActivity.birthdaysList, new Comparator() { + Collections.sort(birthdays, new Comparator() { @Override public int compare(Birthday b1, Birthday b2) { return b1.getDate().compareTo(b2.getDate()); @@ -112,8 +101,8 @@ public int compare(Birthday b1, Birthday b2) { } // Sort Birthday array by first name - public static void sortBirthdaysByName() { - Collections.sort(BirthdayListActivity.birthdaysList, new Comparator() { + public void sortBirthdaysByName() { + Collections.sort(birthdays, new Comparator() { @Override public int compare(Birthday b1, Birthday b2) { return b1.getName().compareTo(b2.getName()); @@ -123,7 +112,7 @@ public int compare(Birthday b1, Birthday b2) { @Override public int getItemCount() { - return BirthdayListActivity.birthdaysList.size(); + return birthdays.size(); } } \ No newline at end of file diff --git a/app/src/main/java/website/julianrosser/birthdays/database/FirebaseHelper.java b/app/src/main/java/website/julianrosser/birthdays/database/FirebaseHelper.java index 9cb748b..4ba60d6 100644 --- a/app/src/main/java/website/julianrosser/birthdays/database/FirebaseHelper.java +++ b/app/src/main/java/website/julianrosser/birthdays/database/FirebaseHelper.java @@ -1,5 +1,7 @@ package website.julianrosser.birthdays.database; +import android.util.Log; + import com.google.firebase.auth.FirebaseUser; import com.google.firebase.database.DataSnapshot; import com.google.firebase.database.DatabaseError; @@ -14,52 +16,45 @@ import website.julianrosser.birthdays.BirthdayReminder; import website.julianrosser.birthdays.Constants; import website.julianrosser.birthdays.Utils; -import website.julianrosser.birthdays.activities.BirthdayListActivity; import website.julianrosser.birthdays.model.Birthday; import website.julianrosser.birthdays.model.FirebaseBirthday; import website.julianrosser.birthdays.model.events.BirthdaysLoadedEvent; public class FirebaseHelper { - public static void loadBirthdays() { - - String userID = BirthdayReminder.getInstance().getCurrentUser().getUid(); - + public static void loadBirthdaysOnce() { + FirebaseUser user = BirthdayReminder.getInstance().getCurrentUser(); + if (null == user) { + Log.i(FirebaseHelper.class.getSimpleName(), "User not loaded yet"); + return; + } // load birthdays from FB - final DatabaseReference ref = BirthdayReminder.getInstance().getDatabaseReference().child(userID).child(Constants.TABLE_BIRTHDAYS); // todo - refacteringignignigng - ref.addListenerForSingleValueEvent(new ValueEventListener() { + final DatabaseReference databaseReference = BirthdayReminder.getInstance().getDatabaseReference().child(user.getUid()).child(Constants.TABLE_BIRTHDAYS); + if (databaseReference == null) { + Log.i(FirebaseHelper.class.getSimpleName(), "Database not loaded yet"); + return; + } + databaseReference.addListenerForSingleValueEvent(new ValueEventListener() { @Override public void onDataChange(DataSnapshot dataSnapshot) { - ArrayList firebaseBirthdays = new ArrayList<>(); - + ArrayList birthdays = new ArrayList<>(); for (DataSnapshot birthdaySnap : dataSnapshot.getChildren()) { FirebaseBirthday firebaseBirthday = birthdaySnap.getValue(FirebaseBirthday.class); - firebaseBirthdays.add(firebaseBirthday); + Birthday birthday = Birthday.fromFB(firebaseBirthday); + birthdays.add(birthday); } - - ArrayList birthdays = new ArrayList<>(); - for (FirebaseBirthday fb : firebaseBirthdays) { - birthdays.add(Birthday.fromFB(fb)); - } - EventBus.getDefault().post(new BirthdaysLoadedEvent(birthdays)); - - ref.removeEventListener(this); + databaseReference.removeEventListener(this); } @Override public void onCancelled(DatabaseError databaseError) { - ref.removeEventListener(this); + Log.i(FirebaseHelper.class.getSimpleName(), databaseError.getMessage()); + databaseReference.removeEventListener(this); } }); } - public enum FirebaseUpdate { - CREATE, - UPDATE, - DELETE, - } - public static void saveBirthdayChange(Birthday birthday, FirebaseUpdate state) { FirebaseUser user = BirthdayReminder.getInstance().getCurrentUser(); @@ -95,4 +90,10 @@ private static void setLastUpdatedTime() { } } + public enum FirebaseUpdate { + CREATE, + UPDATE, + DELETE, + } + } diff --git a/app/src/main/java/website/julianrosser/birthdays/fragments/RecyclerListFragment.java b/app/src/main/java/website/julianrosser/birthdays/fragments/RecyclerListFragment.java index b776f52..0335a1e 100644 --- a/app/src/main/java/website/julianrosser/birthdays/fragments/RecyclerListFragment.java +++ b/app/src/main/java/website/julianrosser/birthdays/fragments/RecyclerListFragment.java @@ -3,15 +3,34 @@ import android.os.Build; import android.os.Bundle; +import android.support.design.widget.Snackbar; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; +import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.Toast; -import website.julianrosser.birthdays.activities.BirthdayListActivity; -import website.julianrosser.birthdays.adapter.BirthdayViewAdapter; +import com.google.firebase.auth.FirebaseUser; +import com.google.firebase.database.DataSnapshot; +import com.google.firebase.database.DatabaseError; +import com.google.firebase.database.DatabaseReference; +import com.google.firebase.database.ValueEventListener; + +import org.greenrobot.eventbus.EventBus; +import org.greenrobot.eventbus.Subscribe; + +import java.util.ArrayList; + +import website.julianrosser.birthdays.BirthdayReminder; +import website.julianrosser.birthdays.Constants; import website.julianrosser.birthdays.R; +import website.julianrosser.birthdays.adapter.BirthdayViewAdapter; +import website.julianrosser.birthdays.database.FirebaseHelper; +import website.julianrosser.birthdays.model.Birthday; +import website.julianrosser.birthdays.model.FirebaseBirthday; +import website.julianrosser.birthdays.model.events.BirthdaysLoadedEvent; /** * Main view. Fragment which holds the RecyclerView. @@ -19,25 +38,23 @@ public class RecyclerListFragment extends android.support.v4.app.Fragment { // Reference to mAdapter - public static BirthdayViewAdapter mAdapter; - - // Reference to recyclerView - public static RecyclerView recyclerView; + private BirthdayViewAdapter mAdapter; // Reference to view which shows when list empty. - static View emptyView; + private View emptyView; + private ValueEventListener loadBirthdaysEventListener; + private DatabaseReference databaseReference; // Required empty constructor public RecyclerListFragment() { - } + } /* Use newInstance in case in the future we want to add construction parameters or initialisation here */ public static RecyclerListFragment newInstance() { return new RecyclerListFragment(); } - @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { @@ -45,15 +62,11 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, View view = inflater.inflate(R.layout.fragment_main, container, false); // Initialise important reference to the main view: RecyclerView - recyclerView = (RecyclerView) view.findViewById(R.id.recyclerView); + RecyclerView recyclerView = (RecyclerView) view.findViewById(R.id.recyclerView); // Reference empty TextView emptyView = view.findViewById(R.id.empty_view); - /* Detect whether the 'no birthdays found' message should be displayed instead of Rec.view. - Using empty and recycler references. */ - showEmptyMessageIfRequired(); - // hide drop shadow if running lollipop or higher if (Build.VERSION.SDK_INT >= 21) { view.findViewById(R.id.drop_shadow).setVisibility(View.GONE); @@ -73,19 +86,84 @@ public void onScrollStateChanged(RecyclerView recyclerView, int newState) { // Can use this to optimize performance as RecyclerView will NOT change size. recyclerView.setHasFixedSize(true); - - mAdapter = new BirthdayViewAdapter(BirthdayListActivity.birthdaysList); - + mAdapter = new BirthdayViewAdapter(); recyclerView.setAdapter(mAdapter); + setUpBirthdayListener(); return view; } + @Override + public void onStart() { + super.onStart(); + EventBus.getDefault().register(this); + } + @Override + public void onStop() { + super.onStop(); + EventBus.getDefault().unregister(this); + databaseReference.removeEventListener(loadBirthdaysEventListener); + loadBirthdaysEventListener = null; + } + + @Override + public void onResume() { + super.onResume(); + loadBirthdays(); + } + + public void loadBirthdays() { + setUpBirthdayListener(); + } + + private void setUpBirthdayListener() { + FirebaseUser user = BirthdayReminder.getInstance().getCurrentUser(); + if (null == user) { + Log.i(FirebaseHelper.class.getSimpleName(), "User not loaded yet"); + return; + } + // load birthdays from FB + databaseReference = BirthdayReminder.getInstance().getDatabaseReference().child(user.getUid()).child(Constants.TABLE_BIRTHDAYS); + if (databaseReference == null) { + Log.i(FirebaseHelper.class.getSimpleName(), "Database not loaded yet"); + return; + } + if (loadBirthdaysEventListener == null) { + loadBirthdaysEventListener = new ValueEventListener() { + @Override + public void onDataChange(DataSnapshot dataSnapshot) { + + ArrayList birthdays = new ArrayList<>(); + for (DataSnapshot birthdaySnap : dataSnapshot.getChildren()) { + FirebaseBirthday firebaseBirthday = birthdaySnap.getValue(FirebaseBirthday.class); + Birthday birthday = Birthday.fromFB(firebaseBirthday); + birthdays.add(birthday); + } + EventBus.getDefault().post(new BirthdaysLoadedEvent(birthdays)); + } + + @Override + public void onCancelled(DatabaseError databaseError) { + Toast.makeText(BirthdayReminder.getInstance(), databaseError.getMessage(), Toast.LENGTH_SHORT).show(); + } + }; + } + databaseReference.addValueEventListener(loadBirthdaysEventListener); + } + + @Subscribe + public void onBirthdaysLoaded(BirthdaysLoadedEvent event) { + mAdapter.setData(event.getBirthdays()); + showEmptyMessageIfRequired(event.getBirthdays()); + Snackbar.make(emptyView, "Birthdays Loaded!", Snackbar.LENGTH_SHORT).show(); + Log.i(getClass().getSimpleName(), "setting birthday data!"); + + } // Show or hide the 'no birthdays found' message depending on size of birthday Array - public static void showEmptyMessageIfRequired() { - if (BirthdayListActivity.birthdaysList.isEmpty()){ + private void showEmptyMessageIfRequired(ArrayList birthdays) { + if (birthdays.isEmpty()) { emptyView.setVisibility(View.VISIBLE); } else { emptyView.setVisibility(View.INVISIBLE); From 1fa247c8a8e20b36531d062440332ac7883e390f Mon Sep 17 00:00:00 2001 From: Julian Rosser Date: Mon, 30 Jan 2017 23:23:43 +0000 Subject: [PATCH 21/83] Small formatting changes. --- app/src/main/java/website/julianrosser/birthdays/Utils.java | 4 ++++ .../julianrosser/birthdays/model/tasks/LoadBirthdaysTask.java | 2 -- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/website/julianrosser/birthdays/Utils.java b/app/src/main/java/website/julianrosser/birthdays/Utils.java index c335218..11aff7b 100644 --- a/app/src/main/java/website/julianrosser/birthdays/Utils.java +++ b/app/src/main/java/website/julianrosser/birthdays/Utils.java @@ -51,4 +51,8 @@ public static int getHighlightColor(Context context) { return R.color.blue_accent_400; } } + + public static boolean isStringEmpty(String s) { + return s == null || s.equals(""); + } } diff --git a/app/src/main/java/website/julianrosser/birthdays/model/tasks/LoadBirthdaysTask.java b/app/src/main/java/website/julianrosser/birthdays/model/tasks/LoadBirthdaysTask.java index be11e7c..326880c 100644 --- a/app/src/main/java/website/julianrosser/birthdays/model/tasks/LoadBirthdaysTask.java +++ b/app/src/main/java/website/julianrosser/birthdays/model/tasks/LoadBirthdaysTask.java @@ -16,10 +16,8 @@ import website.julianrosser.birthdays.BirthdayReminder; import website.julianrosser.birthdays.Constants; -import website.julianrosser.birthdays.activities.BirthdayListActivity; import website.julianrosser.birthdays.model.Birthday; import website.julianrosser.birthdays.model.events.BirthdaysLoadedEvent; -import website.julianrosser.birthdays.model.events.ContactsLoadedEvent; public class LoadBirthdaysTask extends AsyncTask> { From 3853cd715fe128e0ec66db7ab4f1efa36f5f9192 Mon Sep 17 00:00:00 2001 From: Julian Rosser Date: Mon, 30 Jan 2017 23:24:57 +0000 Subject: [PATCH 22/83] Keep reference to FirebaseDatabase and FirebaseUser in Application class. --- .../birthdays/BirthdayReminder.java | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/website/julianrosser/birthdays/BirthdayReminder.java b/app/src/main/java/website/julianrosser/birthdays/BirthdayReminder.java index ebfaeef..4d92882 100644 --- a/app/src/main/java/website/julianrosser/birthdays/BirthdayReminder.java +++ b/app/src/main/java/website/julianrosser/birthdays/BirthdayReminder.java @@ -3,15 +3,16 @@ import android.app.Application; import com.google.firebase.FirebaseApp; +import com.google.firebase.auth.FirebaseUser; import com.google.firebase.database.DatabaseReference; import com.google.firebase.database.FirebaseDatabase; public class BirthdayReminder extends Application { - // Application reference private static BirthdayReminder sInstance; private FirebaseDatabase mFirebaseDatabase; private DatabaseReference mDatabase; + private FirebaseUser currentUser; public static BirthdayReminder getInstance() { return sInstance; @@ -21,10 +22,14 @@ public static BirthdayReminder getInstance() { public void onCreate() { super.onCreate(); sInstance = this; - FirebaseApp.initializeApp(this); + initializeFirebase(); + } + private void initializeFirebase() { + FirebaseApp.initializeApp(this); mFirebaseDatabase = FirebaseDatabase.getInstance(); - mDatabase = mFirebaseDatabase.getReference("message"); + mFirebaseDatabase.setPersistenceEnabled(true); + mDatabase = mFirebaseDatabase.getReference(); } public DatabaseReference getDatabaseReference() { @@ -32,8 +37,17 @@ public DatabaseReference getDatabaseReference() { if (mFirebaseDatabase == null) { mFirebaseDatabase = FirebaseDatabase.getInstance(); } - mDatabase = mFirebaseDatabase.getReference("message"); + mDatabase = mFirebaseDatabase.getReference(); } return mDatabase; } + + public void setUser(FirebaseUser currentUser) { + this.currentUser = currentUser; + } + + public FirebaseUser getCurrentUser() { + return currentUser; + } + } From f88304331c3163fe47815191f6d840186646a4b2 Mon Sep 17 00:00:00 2001 From: Julian Rosser Date: Tue, 31 Jan 2017 23:25:22 +0000 Subject: [PATCH 23/83] Automatically show imported contacts year if available. Fix issue where an imported contact with no year would show age of 1000's of years too old. --- app/src/main/java/website/julianrosser/birthdays/Utils.java | 1 + .../julianrosser/birthdays/adapter/ContactAdapter.java | 4 ++-- .../julianrosser/birthdays/model/FirebaseBirthday.java | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/website/julianrosser/birthdays/Utils.java b/app/src/main/java/website/julianrosser/birthdays/Utils.java index 11aff7b..dbfb441 100644 --- a/app/src/main/java/website/julianrosser/birthdays/Utils.java +++ b/app/src/main/java/website/julianrosser/birthdays/Utils.java @@ -17,6 +17,7 @@ public static Date stringToDate(String birthdayString) { birthdayString = birthdayString.replaceFirst("-", "1990"); } date = format.parse(birthdayString); + date.setYear(date.getYear() + 1900); System.out.println(date); } catch (java.text.ParseException e) { e.printStackTrace(); diff --git a/app/src/main/java/website/julianrosser/birthdays/adapter/ContactAdapter.java b/app/src/main/java/website/julianrosser/birthdays/adapter/ContactAdapter.java index c5d52f1..c613a6f 100644 --- a/app/src/main/java/website/julianrosser/birthdays/adapter/ContactAdapter.java +++ b/app/src/main/java/website/julianrosser/birthdays/adapter/ContactAdapter.java @@ -63,8 +63,8 @@ public void setData(ArrayList contactsList) { // Callback from ViewHolder @Override public void addContact(Contact contact) { - // todo - if year is available, INCLUDE! (Last Parameter!!!! see email from user) - Birthday contactBirthday = new Birthday(contact.getName(), contact.getBirthday(), true, false); + boolean isYearNot1990 = ! (contact.getBirthday().getYear() == 1990); + Birthday contactBirthday = new Birthday(contact.getName(), contact.getBirthday(), true, isYearNot1990); FirebaseHelper.saveBirthdayChange(contactBirthday, FirebaseHelper.FirebaseUpdate.CREATE); } } \ No newline at end of file diff --git a/app/src/main/java/website/julianrosser/birthdays/model/FirebaseBirthday.java b/app/src/main/java/website/julianrosser/birthdays/model/FirebaseBirthday.java index a0d741c..7d3d233 100644 --- a/app/src/main/java/website/julianrosser/birthdays/model/FirebaseBirthday.java +++ b/app/src/main/java/website/julianrosser/birthdays/model/FirebaseBirthday.java @@ -33,7 +33,7 @@ private FirebaseBirthday(String name, Date dateOfBirthday, int year, boolean not } public static FirebaseBirthday convertToFirebaseBirthday(Birthday birthday) { - return new FirebaseBirthday(birthday.getName(), birthday.getDate(), birthday.getYear() + 1900, + return new FirebaseBirthday(birthday.getName(), birthday.getDate(), birthday.getYear(), birthday.getRemind(), birthday.shouldIncludeYear(), birthday.getUID()); } From fa6a32ebd7b6bed7dcc22102bc77892b3eb28df1 Mon Sep 17 00:00:00 2001 From: Julian Rosser Date: Wed, 1 Feb 2017 23:31:37 +0000 Subject: [PATCH 24/83] Fix Add, Edit, Delete & Alarm toggle. --- .../DialogFragments/AddEditFragment.java | 41 +++++++++------- .../DialogFragments/ItemOptionsFragment.java | 48 ++++++++----------- .../birthdays/model/FirebaseBirthday.java | 2 +- .../views/viewholder/BirthdayViewHolder.java | 25 +++------- 4 files changed, 51 insertions(+), 65 deletions(-) diff --git a/app/src/main/java/website/julianrosser/birthdays/fragments/DialogFragments/AddEditFragment.java b/app/src/main/java/website/julianrosser/birthdays/fragments/DialogFragments/AddEditFragment.java index 803131e..67c3e5a 100644 --- a/app/src/main/java/website/julianrosser/birthdays/fragments/DialogFragments/AddEditFragment.java +++ b/app/src/main/java/website/julianrosser/birthdays/fragments/DialogFragments/AddEditFragment.java @@ -11,7 +11,6 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.app.DialogFragment; -import android.support.v4.app.Fragment; import android.support.v4.content.ContextCompat; import android.support.v7.app.AlertDialog; import android.support.v7.view.ContextThemeWrapper; @@ -28,12 +27,13 @@ import android.widget.Toast; import java.util.Calendar; +import java.util.Date; import website.julianrosser.birthdays.R; +import website.julianrosser.birthdays.Utils; +import website.julianrosser.birthdays.database.FirebaseHelper; +import website.julianrosser.birthdays.model.Birthday; -/** - * A simple {@link Fragment} subclass. - */ public class AddEditFragment extends DialogFragment { // Keys for passing birthday information to Dialog @@ -42,24 +42,24 @@ public class AddEditFragment extends DialogFragment { public final static String MONTH_KEY = "key_month"; public final static String SHOW_YEAR_KEY = "key_show_year"; public final static String YEAR_KEY = "key_year"; - public final static String POS_KEY = "key_pos"; + public final static String UID_KEY = "key_uid"; public final static String NAME_KEY = "key_position"; // To check if we are in new birthday mode or editing birthday mode. - int ADD_OR_EDIT_MODE; + private int ADD_OR_EDIT_MODE; public final static int MODE_ADD = 0; public final static int MODE_EDIT = 1; - final int DIALOG_WIDTH_SIZE = 280; + private final int DIALOG_WIDTH_SIZE = 280; // Reference to passed bundle when in edit mode - Bundle bundle; + private Bundle bundle; // Needed as we inflate in onCreate, but access in onStart. Due to Overriding dialog button, onStart is needed. - View view; + private View view; // Use this instance of the interface to deliver action events - NoticeDialogListener mListener; + private NoticeDialogListener mListener; private CheckBox checkYearToggle; public AddEditFragment() { @@ -238,10 +238,8 @@ public void onStart() { ((AlertDialog) getDialog()).getButton(AlertDialog.BUTTON_POSITIVE).setTextColor(ContextCompat.getColor(getActivity().getApplicationContext(), R.color.blue_accent_700)); ((AlertDialog) getDialog()).getButton(AlertDialog.BUTTON_NEGATIVE).setTextColor(ContextCompat.getColor(getActivity().getApplicationContext(), R.color.blue_accent_700)); } - // Null check if (dialog != null) { - // This is it! The done button listener which we override onStart to use. final Button positiveButton = dialog.getButton(Dialog.BUTTON_POSITIVE); positiveButton.setOnClickListener(new View.OnClickListener() { @@ -257,11 +255,22 @@ public void onClick(View v) { int year = datePicker.getYear(); boolean includeYear = checkYearToggle.isChecked(); - // Send the positive button event back to BirthdayListActivity - mListener.onDialogPositiveClick(AddEditFragment.this, editText.getText().toString(), - dateOfMonth, month, year, includeYear, - ADD_OR_EDIT_MODE, bundle.getInt(POS_KEY)); + // Build date object which will be used by new Birthday + Date dateOfBirth = new Date(); + dateOfBirth.setYear(year); + dateOfBirth.setMonth(month); + dateOfBirth.setDate(dateOfMonth); + // Send the positive button event back to BirthdayListActivity + Birthday birthday = new Birthday(editText.getText().toString(), dateOfBirth, true, includeYear); + + if (ADD_OR_EDIT_MODE == MODE_EDIT) { + String key = bundle.getString(UID_KEY); + if (! Utils.isStringEmpty(key)) birthday.setUID(key); + FirebaseHelper.saveBirthdayChange(birthday, FirebaseHelper.FirebaseUpdate.UPDATE); + } else if (ADD_OR_EDIT_MODE == MODE_ADD) { + FirebaseHelper.saveBirthdayChange(birthday, FirebaseHelper.FirebaseUpdate.CREATE); + } // Finally close the dialog, and breath a sign of relief dialog.dismiss(); diff --git a/app/src/main/java/website/julianrosser/birthdays/fragments/DialogFragments/ItemOptionsFragment.java b/app/src/main/java/website/julianrosser/birthdays/fragments/DialogFragments/ItemOptionsFragment.java index 7c2e77f..64c723f 100644 --- a/app/src/main/java/website/julianrosser/birthdays/fragments/DialogFragments/ItemOptionsFragment.java +++ b/app/src/main/java/website/julianrosser/birthdays/fragments/DialogFragments/ItemOptionsFragment.java @@ -24,53 +24,43 @@ import android.widget.ListView; import android.widget.TextView; -import website.julianrosser.birthdays.activities.BirthdayListActivity; import website.julianrosser.birthdays.R; +import website.julianrosser.birthdays.model.Birthday; public class ItemOptionsFragment extends DialogFragment { + private static Birthday sBirthday; final int DIALOG_WIDTH_SIZE = 220; // Use this instance of the interface to deliver action events static ItemOptionsListener mListener; - // Reference to selected Birthday's position in Array - static int birthdayListPosition; - - // String reference which we use for title - static String titleName; - public ItemOptionsFragment() { // Required empty public constructor } // New instance is preferable as we have option to initialize here instead of using passed in params. - public static ItemOptionsFragment newInstance(int position) { + public static ItemOptionsFragment newInstance(Birthday birthday) { ItemOptionsFragment itemOptionsFragment = new ItemOptionsFragment(); - // We need reference to selected birthday for passing back to BirthdayListActivity - birthdayListPosition = position; - // Get selected birthday's title - titleName = BirthdayListActivity.birthdaysList.get(birthdayListPosition).getName(); + sBirthday = birthday; return itemOptionsFragment; } - /* BirthdayListActivity implements this interface in order to receive event callbacks. Passes the DialogFragment in case the host needs to query it. */ public interface ItemOptionsListener { - void onItemEdit(ItemOptionsFragment dialog, int position); + void onItemEdit(ItemOptionsFragment dialog, Birthday birthday); - void onItemDelete(ItemOptionsFragment dialog, int position); + void onItemDelete(ItemOptionsFragment dialog, Birthday birthday); - void onItemToggleAlarm(ItemOptionsFragment dialog, int position); + void onItemToggleAlarm(ItemOptionsFragment dialog, Birthday birthday); } - // We override the Fragment.onAttach() method to instantiate NoticeDialogListener and read bundle data @Override public void onAttach(Activity activity) { @@ -117,7 +107,7 @@ public void onDestroyView() { } // Helper method for getting exact pixel size for device from density independent pixels - public int getPixelsFromDP(int px) { + public int getPixelsFromDP(int px) { // todo - refactor Resources r = getResources(); return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, px, r.getDisplayMetrics()); } @@ -144,13 +134,13 @@ public Dialog onCreateDialog(Bundle savedInstanceState) { public void onItemClick(AdapterView parent, View view, int position, long id) { switch (position) { case 0: - mListener.onItemEdit(ItemOptionsFragment.this, birthdayListPosition); + mListener.onItemEdit(ItemOptionsFragment.this, sBirthday); break; case 1: - mListener.onItemDelete(ItemOptionsFragment.this, birthdayListPosition); + mListener.onItemDelete(ItemOptionsFragment.this, sBirthday); break; case 2: - mListener.onItemToggleAlarm(ItemOptionsFragment.this, birthdayListPosition); + mListener.onItemToggleAlarm(ItemOptionsFragment.this, sBirthday); break; } } @@ -160,7 +150,7 @@ public void onItemClick(AdapterView parent, View view, int position, long id) builder.setView(listView); // Set title to name of selected birthday - builder.setTitle(titleName); + builder.setTitle(sBirthday.getName()); return builder.create(); } @@ -198,27 +188,27 @@ public long getItemId(int position) { return result[position].hashCode(); } - public class Holder { + public class ItemOptionHolder { TextView textView; ImageView imageIcon; } @Override public View getView(int position, View convertView, ViewGroup parent) { - Holder holder = new Holder(); + ItemOptionHolder itemOptionHolder = new ItemOptionHolder(); View rowView = inflater.inflate(R.layout.item_edit_fragment_list, null); - holder.textView = (TextView) rowView.findViewById(R.id.option_list_textview); - holder.textView.setText(result[position]); + itemOptionHolder.textView = (TextView) rowView.findViewById(R.id.option_list_textview); + itemOptionHolder.textView.setText(result[position]); // Get references - holder.imageIcon = (ImageView) rowView.findViewById(R.id.imageView); + itemOptionHolder.imageIcon = (ImageView) rowView.findViewById(R.id.imageView); if (position == 2) { - holder.imageIcon.setImageDrawable(BirthdayListActivity.birthdaysList.get(birthdayListPosition).getRemindAlarmDrawable()); + itemOptionHolder.imageIcon.setImageDrawable(sBirthday.getRemindAlarmDrawable()); } else { - holder.imageIcon.setImageDrawable(ContextCompat.getDrawable(getActivity().getApplicationContext(), imageIcons[position])); + itemOptionHolder.imageIcon.setImageDrawable(ContextCompat.getDrawable(getActivity().getApplicationContext(), imageIcons[position])); } return rowView; diff --git a/app/src/main/java/website/julianrosser/birthdays/model/FirebaseBirthday.java b/app/src/main/java/website/julianrosser/birthdays/model/FirebaseBirthday.java index 7d3d233..3aea8fc 100644 --- a/app/src/main/java/website/julianrosser/birthdays/model/FirebaseBirthday.java +++ b/app/src/main/java/website/julianrosser/birthdays/model/FirebaseBirthday.java @@ -21,7 +21,7 @@ public FirebaseBirthday() { /** * Constructor for creating new birthday. */ - private FirebaseBirthday(String name, Date dateOfBirthday, int year, boolean notifyUserOfBirthday, boolean includeYear, String uID) { + public FirebaseBirthday(String name, Date dateOfBirthday, int year, boolean notifyUserOfBirthday, boolean includeYear, String uID) { this.name = name; this.remind = notifyUserOfBirthday; diff --git a/app/src/main/java/website/julianrosser/birthdays/views/viewholder/BirthdayViewHolder.java b/app/src/main/java/website/julianrosser/birthdays/views/viewholder/BirthdayViewHolder.java index 07bee87..4bd8767 100644 --- a/app/src/main/java/website/julianrosser/birthdays/views/viewholder/BirthdayViewHolder.java +++ b/app/src/main/java/website/julianrosser/birthdays/views/viewholder/BirthdayViewHolder.java @@ -1,7 +1,6 @@ package website.julianrosser.birthdays.views.viewholder; import android.graphics.drawable.Drawable; -import android.support.design.widget.Snackbar; import android.support.v7.widget.RecyclerView; import android.view.View; import android.widget.ImageView; @@ -10,9 +9,8 @@ import org.greenrobot.eventbus.EventBus; import website.julianrosser.birthdays.R; -import website.julianrosser.birthdays.fragments.RecyclerListFragment; +import website.julianrosser.birthdays.database.FirebaseHelper; import website.julianrosser.birthdays.model.Birthday; -import website.julianrosser.birthdays.model.events.BirthdayAlarmToggleEvent; import website.julianrosser.birthdays.model.events.BirthdayItemClickEvent; /** @@ -40,6 +38,7 @@ public BirthdayViewHolder(View itemView) { textDateDay = (TextView) itemView.findViewById(R.id.dateDay); textDateMonth = (TextView) itemView.findViewById(R.id.dateMonth); imageAlarm = (ImageView) itemView.findViewById(R.id.alarmImage); + setImageClickListener(); } public void setTag(Birthday birthday) { @@ -94,25 +93,13 @@ public void onClick(View v) { Birthday birthday = (Birthday) v.getTag(); int id = v.getId(); + if (id == R.id.alarmImage) { birthday.toggleReminder(); - - // Get correct position, as deleted views may have altered pos int - int currentPosition = RecyclerListFragment.recyclerView.getChildAdapterPosition(itemView); - - // Callback to BirthdayListActivity. - EventBus.getDefault().post(new BirthdayAlarmToggleEvent(currentPosition)); - + FirebaseHelper.saveBirthdayChange(birthday, FirebaseHelper.FirebaseUpdate.UPDATE); } else { - // Get actual position, accounting for deletion - int currentPosition = RecyclerListFragment.recyclerView.getChildAdapterPosition(itemView); - - // Open ItemOption menu for selected birthday - if (currentPosition != RecyclerView.NO_POSITION) { - EventBus.getDefault().post(new BirthdayItemClickEvent(currentPosition)); - } else { - Snackbar.make(container, R.string.error_try_again, Snackbar.LENGTH_SHORT).show(); - } + // Callback + EventBus.getDefault().post(new BirthdayItemClickEvent(birthday)); } } } \ No newline at end of file From 3fd113f1ea65b157569d46bd2a3904b829959501 Mon Sep 17 00:00:00 2001 From: Julian Rosser Date: Wed, 1 Feb 2017 23:32:36 +0000 Subject: [PATCH 25/83] Add Unique ID to birthday. --- .../birthdays/model/Birthday.java | 28 ++++++++++++++++--- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/website/julianrosser/birthdays/model/Birthday.java b/app/src/main/java/website/julianrosser/birthdays/model/Birthday.java index b0aef7b..b31e981 100644 --- a/app/src/main/java/website/julianrosser/birthdays/model/Birthday.java +++ b/app/src/main/java/website/julianrosser/birthdays/model/Birthday.java @@ -14,11 +14,11 @@ import java.util.Calendar; import java.util.Date; import java.util.Locale; +import java.util.UUID; import website.julianrosser.birthdays.BirthdayReminder; import website.julianrosser.birthdays.R; import website.julianrosser.birthdays.Utils; -import website.julianrosser.birthdays.activities.BirthdayListActivity; @SuppressWarnings("deprecation") public class Birthday { @@ -36,30 +36,32 @@ public class Birthday { private static final int DAY_IN_MILLIS = 86400000; // References to data - private String name; + public String name; private Date date; private boolean remind; private int yearOfBirth; private boolean showYear; + private String uID; /** * Constructor for creating new birthday. + * */ public Birthday(String name, Date dateOfBirthday, boolean notifyUserOfBirthday, boolean includeYear) { - this.name = name; this.remind = notifyUserOfBirthday; this.date = dateOfBirthday; this.yearOfBirth = dateOfBirthday.getYear(); this.showYear = includeYear; + this.uID = UUID.randomUUID().toString(); } /** * For updating Birthday information without creating new */ public void edit(String editName, Date editDate, boolean editRemind, boolean includeYear) { - this.name = editName; + this.remind = editRemind; this.date = editDate; this.yearOfBirth = editDate.getYear(); this.showYear = includeYear; @@ -130,6 +132,14 @@ public void setYearOfDate(int year) { this.date.setYear(year); } + public String getUID() { + return uID; + } + + public void setUID(String uID) { + this.uID = uID; + } + public boolean getRemind() { return remind; } @@ -331,4 +341,14 @@ public String getAge() { return String.valueOf(age); } } + + public static Birthday fromFB(FirebaseBirthday fb) { // todo - refactor to helper + Date date = new Date(); + date.setYear(fb.dateYear); + date.setMonth(fb.dateMonth); + date.setDate(fb.dateDay); + Birthday birthday = new Birthday(fb.name, date, fb.remind, fb.showYear); + birthday.setUID(fb.uID); + return birthday; + } } \ No newline at end of file From 84b2e08a1edae515b705bde066395294cd001bbf Mon Sep 17 00:00:00 2001 From: Julian Rosser Date: Wed, 1 Feb 2017 23:34:46 +0000 Subject: [PATCH 26/83] Remove AlarmToggleEvent & update BirthdayItemClickEvent. --- .../model/events/BirthdayAlarmToggleEvent.java | 14 -------------- .../model/events/BirthdayItemClickEvent.java | 12 +++++++----- 2 files changed, 7 insertions(+), 19 deletions(-) delete mode 100644 app/src/main/java/website/julianrosser/birthdays/model/events/BirthdayAlarmToggleEvent.java diff --git a/app/src/main/java/website/julianrosser/birthdays/model/events/BirthdayAlarmToggleEvent.java b/app/src/main/java/website/julianrosser/birthdays/model/events/BirthdayAlarmToggleEvent.java deleted file mode 100644 index 7d3d3ee..0000000 --- a/app/src/main/java/website/julianrosser/birthdays/model/events/BirthdayAlarmToggleEvent.java +++ /dev/null @@ -1,14 +0,0 @@ -package website.julianrosser.birthdays.model.events; - -public class BirthdayAlarmToggleEvent { - - private final int currentPosition; - - public BirthdayAlarmToggleEvent(int currentPosition) { - this.currentPosition = currentPosition; - } - - public int getCurrentPosition() { - return currentPosition; - } -} diff --git a/app/src/main/java/website/julianrosser/birthdays/model/events/BirthdayItemClickEvent.java b/app/src/main/java/website/julianrosser/birthdays/model/events/BirthdayItemClickEvent.java index d843fb1..4f61b7c 100644 --- a/app/src/main/java/website/julianrosser/birthdays/model/events/BirthdayItemClickEvent.java +++ b/app/src/main/java/website/julianrosser/birthdays/model/events/BirthdayItemClickEvent.java @@ -1,14 +1,16 @@ package website.julianrosser.birthdays.model.events; +import website.julianrosser.birthdays.model.Birthday; + public class BirthdayItemClickEvent { - private final int currentPosition; + private final Birthday birthday; - public BirthdayItemClickEvent(int currentPosition) { - this.currentPosition = currentPosition; + public BirthdayItemClickEvent(Birthday birthday) { + this.birthday = birthday; } - public int getCurrentPosition() { - return currentPosition; + public Birthday getBirthday() { + return birthday; } } From 0b2cb46c483ee63a38cb8feb687feaa319188152 Mon Sep 17 00:00:00 2001 From: Julian Rosser Date: Thu, 2 Feb 2017 00:03:35 +0000 Subject: [PATCH 27/83] Refactor PixelsToDp method to Utils. Refactor default YOB. --- .../website/julianrosser/birthdays/Constants.java | 1 + .../java/website/julianrosser/birthdays/Utils.java | 8 ++++++++ .../fragments/DialogFragments/AddEditFragment.java | 14 ++++---------- .../DialogFragments/ItemOptionsFragment.java | 12 +----------- .../julianrosser/birthdays/model/Birthday.java | 3 ++- 5 files changed, 16 insertions(+), 22 deletions(-) diff --git a/app/src/main/java/website/julianrosser/birthdays/Constants.java b/app/src/main/java/website/julianrosser/birthdays/Constants.java index dc88045..7c6e4d4 100644 --- a/app/src/main/java/website/julianrosser/birthdays/Constants.java +++ b/app/src/main/java/website/julianrosser/birthdays/Constants.java @@ -7,6 +7,7 @@ public class Constants { */ public static final String FILENAME = "birthdays.json"; + public static int DEFAULT_YEAR_OF_BIRTH = 1990; public static int INTENT_FROM_NOTIFICATION = 30; public static int CONTACT_PERMISSION_CODE = 3; diff --git a/app/src/main/java/website/julianrosser/birthdays/Utils.java b/app/src/main/java/website/julianrosser/birthdays/Utils.java index dbfb441..0ae78b0 100644 --- a/app/src/main/java/website/julianrosser/birthdays/Utils.java +++ b/app/src/main/java/website/julianrosser/birthdays/Utils.java @@ -2,7 +2,9 @@ import android.content.Context; import android.content.SharedPreferences; +import android.content.res.Resources; import android.preference.PreferenceManager; +import android.util.TypedValue; import java.text.SimpleDateFormat; import java.util.Date; @@ -56,4 +58,10 @@ public static int getHighlightColor(Context context) { public static boolean isStringEmpty(String s) { return s == null || s.equals(""); } + + // Helper method for getting exact pixel size for device from density independent pixels + public static int getPixelsFromDP(Context context, int px) { + Resources r = context.getResources(); + return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, px, r.getDisplayMetrics()); + } } diff --git a/app/src/main/java/website/julianrosser/birthdays/fragments/DialogFragments/AddEditFragment.java b/app/src/main/java/website/julianrosser/birthdays/fragments/DialogFragments/AddEditFragment.java index 67c3e5a..8c5a020 100644 --- a/app/src/main/java/website/julianrosser/birthdays/fragments/DialogFragments/AddEditFragment.java +++ b/app/src/main/java/website/julianrosser/birthdays/fragments/DialogFragments/AddEditFragment.java @@ -14,7 +14,6 @@ import android.support.v4.content.ContextCompat; import android.support.v7.app.AlertDialog; import android.support.v7.view.ContextThemeWrapper; -import android.util.TypedValue; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -29,6 +28,7 @@ import java.util.Calendar; import java.util.Date; +import website.julianrosser.birthdays.Constants; import website.julianrosser.birthdays.R; import website.julianrosser.birthdays.Utils; import website.julianrosser.birthdays.database.FirebaseHelper; @@ -50,7 +50,7 @@ public class AddEditFragment extends DialogFragment { public final static int MODE_ADD = 0; public final static int MODE_EDIT = 1; - private final int DIALOG_WIDTH_SIZE = 280; + private final int DIALOG_WIDTH_SIZE = 220; // Reference to passed bundle when in edit mode private Bundle bundle; @@ -160,7 +160,7 @@ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { private void setUpDatePicker(final DatePicker datePicker) { Calendar today = Calendar.getInstance(); setYearFieldVisibility(false, datePicker); - datePicker.init(2000, today.get(Calendar.MONTH), today.get(Calendar.DATE), new DatePicker.OnDateChangedListener() { + datePicker.init(Constants.DEFAULT_YEAR_OF_BIRTH, today.get(Calendar.MONTH), today.get(Calendar.DATE), new DatePicker.OnDateChangedListener() { @Override public void onDateChanged(DatePicker view, int year, int monthOfYear, int dayOfMonth) { InputMethodManager imm = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE); @@ -182,7 +182,7 @@ private void setYearFieldVisibility(boolean isChecked, DatePicker datePicker) { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - getDialog().getWindow().setLayout(getPixelsFromDP(DIALOG_WIDTH_SIZE), ViewGroup.LayoutParams.WRAP_CONTENT); +// getDialog().getWindow().setLayout(Utils.getPixelsFromDP(getActivity(), DIALOG_WIDTH_SIZE), ViewGroup.LayoutParams.WRAP_CONTENT); setRetainInstance(true); return super.onCreateView(inflater, container, savedInstanceState); @@ -196,12 +196,6 @@ public void onDestroyView() { super.onDestroyView(); } - // Helper method for getting exact pixel size for device from density independent pixels - public int getPixelsFromDP(int px) { - Resources r = getResources(); - return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, px, r.getDisplayMetrics()); - } - /** * Use onStart to set Button clickListener, which enables us to override the 'Done' button. Then we can * stop dialog from being dismissed and the new birthday from being created until the name String is to our liking. diff --git a/app/src/main/java/website/julianrosser/birthdays/fragments/DialogFragments/ItemOptionsFragment.java b/app/src/main/java/website/julianrosser/birthdays/fragments/DialogFragments/ItemOptionsFragment.java index 64c723f..aa8518d 100644 --- a/app/src/main/java/website/julianrosser/birthdays/fragments/DialogFragments/ItemOptionsFragment.java +++ b/app/src/main/java/website/julianrosser/birthdays/fragments/DialogFragments/ItemOptionsFragment.java @@ -5,7 +5,6 @@ import android.app.Dialog; import android.content.Context; import android.content.SharedPreferences; -import android.content.res.Resources; import android.os.Bundle; import android.preference.PreferenceManager; import android.support.annotation.NonNull; @@ -14,7 +13,6 @@ import android.support.v4.content.ContextCompat; import android.support.v7.app.AlertDialog; import android.support.v7.view.ContextThemeWrapper; -import android.util.TypedValue; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -92,8 +90,7 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa getDialog().getWindow().setBackgroundDrawable(ContextCompat.getDrawable(getActivity().getApplicationContext(), R.drawable.dialog_background_green)); } - getDialog().getWindow().setLayout(getPixelsFromDP(DIALOG_WIDTH_SIZE), ViewGroup.LayoutParams.WRAP_CONTENT); - // To ensure data is kept through orientation change +// getDialog().getWindow().setLayout(Utils.getPixelsFromDP(getActivity(), DIALOG_WIDTH_SIZE), ViewGroup.LayoutParams.WRAP_CONTENT); setRetainInstance(true); return super.onCreateView(inflater, container, savedInstanceState); } @@ -106,18 +103,11 @@ public void onDestroyView() { super.onDestroyView(); } - // Helper method for getting exact pixel size for device from density independent pixels - public int getPixelsFromDP(int px) { // todo - refactor - Resources r = getResources(); - return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, px, r.getDisplayMetrics()); - } - @NonNull @Override public Dialog onCreateDialog(Bundle savedInstanceState) { AlertDialog.Builder builder = new AlertDialog.Builder( new ContextThemeWrapper(getActivity(), R.style.DialogFragmentTheme)); - LayoutInflater inflater = getActivity().getLayoutInflater(); // Inflate the brilliantly designed layout, passing null as the parent view diff --git a/app/src/main/java/website/julianrosser/birthdays/model/Birthday.java b/app/src/main/java/website/julianrosser/birthdays/model/Birthday.java index b31e981..a14dd64 100644 --- a/app/src/main/java/website/julianrosser/birthdays/model/Birthday.java +++ b/app/src/main/java/website/julianrosser/birthdays/model/Birthday.java @@ -17,6 +17,7 @@ import java.util.UUID; import website.julianrosser.birthdays.BirthdayReminder; +import website.julianrosser.birthdays.Constants; import website.julianrosser.birthdays.R; import website.julianrosser.birthdays.Utils; @@ -90,7 +91,7 @@ public Birthday(JSONObject json) throws JSONException { if (json.has(JSON_YEAR)) { yearOfBirth = json.getInt(JSON_YEAR); } else { - yearOfBirth = 1990; + yearOfBirth = Constants.DEFAULT_YEAR_OF_BIRTH; } // Should use age? showYear = json.has(JSON_SHOW_YEAR) && json.getBoolean(JSON_SHOW_YEAR); From c35d48603ae02a2ee2c572bdcedd5107e49dd22d Mon Sep 17 00:00:00 2001 From: Julian Rosser Date: Tue, 7 Feb 2017 22:51:33 +0000 Subject: [PATCH 28/83] Recreate BirthdayListActivity when theme has changed. Using better implementation where we check theme after returning from SettingsActivity --- .../julianrosser/birthdays/activities/SettingsActivity.java | 6 +----- .../julianrosser/birthdays/fragments/SettingsFragment.java | 2 -- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/app/src/main/java/website/julianrosser/birthdays/activities/SettingsActivity.java b/app/src/main/java/website/julianrosser/birthdays/activities/SettingsActivity.java index bf4d39e..862323a 100644 --- a/app/src/main/java/website/julianrosser/birthdays/activities/SettingsActivity.java +++ b/app/src/main/java/website/julianrosser/birthdays/activities/SettingsActivity.java @@ -54,15 +54,13 @@ protected void onResume() { mTracker.send(new HitBuilders.ScreenViewBuilder().build()); } - // @Override protected void onPause() { super.onPause(); // Detect if Activity is closing, and recreate BirthdayListActivity to apply new theme if (this.isFinishing()) { - Intent i = new Intent(getApplicationContext(), BirthdayListActivity.class); - startActivity(i); + setResult(BirthdayListActivity.RC_SETTINGS); } } @@ -72,10 +70,8 @@ public void setTheme() { if (prefs.getString(getResources().getString(R.string.pref_theme_key), "0").equals("0")) { setTheme(R.style.PreferenceThemeBlue); - } else if (prefs.getString(getResources().getString(R.string.pref_theme_key), "0").equals("1")) { setTheme(R.style.PreferenceThemePink); - } else { setTheme(R.style.PreferenceThemeGreen); } diff --git a/app/src/main/java/website/julianrosser/birthdays/fragments/SettingsFragment.java b/app/src/main/java/website/julianrosser/birthdays/fragments/SettingsFragment.java index ab6a240..d5eb0e2 100644 --- a/app/src/main/java/website/julianrosser/birthdays/fragments/SettingsFragment.java +++ b/app/src/main/java/website/julianrosser/birthdays/fragments/SettingsFragment.java @@ -88,8 +88,6 @@ public boolean onPreferenceChange(Preference preference, Object value) { if (preference.getKey().equals(getString(R.string.pref_theme_key))) { // If theme preference is changed, immediately recreate the activity. getActivity().recreate(); - // Also remove BirthdayListActivity, so it can be recreated to apply theme - } return true; From 4eaab2d065be9d7c9d001dedfc38e63503e1087c Mon Sep 17 00:00:00 2001 From: Julian Rosser Date: Sat, 11 Feb 2017 16:26:28 +0000 Subject: [PATCH 29/83] If year is available when importing birthday, display year. --- .../java/website/julianrosser/birthdays/Utils.java | 4 ++++ .../birthdays/adapter/ContactAdapter.java | 3 +-- .../julianrosser/birthdays/model/Contact.java | 12 ++++++++++-- .../birthdays/model/tasks/LoadContactsTask.java | 3 +-- 4 files changed, 16 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/website/julianrosser/birthdays/Utils.java b/app/src/main/java/website/julianrosser/birthdays/Utils.java index 0ae78b0..a6eb5dc 100644 --- a/app/src/main/java/website/julianrosser/birthdays/Utils.java +++ b/app/src/main/java/website/julianrosser/birthdays/Utils.java @@ -27,6 +27,10 @@ public static Date stringToDate(String birthdayString) { return date; } + public static boolean hasYearOfBirth(String birthdayString) { + return ! birthdayString.startsWith("-"); + } + public static String getDateSuffix(int date) { if (date == 11 || date == 12 || date == 13) { return "th"; diff --git a/app/src/main/java/website/julianrosser/birthdays/adapter/ContactAdapter.java b/app/src/main/java/website/julianrosser/birthdays/adapter/ContactAdapter.java index c613a6f..443a59e 100644 --- a/app/src/main/java/website/julianrosser/birthdays/adapter/ContactAdapter.java +++ b/app/src/main/java/website/julianrosser/birthdays/adapter/ContactAdapter.java @@ -63,8 +63,7 @@ public void setData(ArrayList contactsList) { // Callback from ViewHolder @Override public void addContact(Contact contact) { - boolean isYearNot1990 = ! (contact.getBirthday().getYear() == 1990); - Birthday contactBirthday = new Birthday(contact.getName(), contact.getBirthday(), true, isYearNot1990); + Birthday contactBirthday = new Birthday(contact.getName(), contact.getBirthday(), true, contact.hasYear()); FirebaseHelper.saveBirthdayChange(contactBirthday, FirebaseHelper.FirebaseUpdate.CREATE); } } \ No newline at end of file diff --git a/app/src/main/java/website/julianrosser/birthdays/model/Contact.java b/app/src/main/java/website/julianrosser/birthdays/model/Contact.java index fe7da1b..1031962 100644 --- a/app/src/main/java/website/julianrosser/birthdays/model/Contact.java +++ b/app/src/main/java/website/julianrosser/birthdays/model/Contact.java @@ -2,16 +2,20 @@ import java.util.Date; +import website.julianrosser.birthdays.Utils; + public class Contact { private String name; private Date birthday; private boolean alreadyAdded; + private boolean hasYear; - public Contact(String name, Date birthday, boolean alreadyAdded) { + public Contact(String name, String birthdayString, boolean alreadyAdded) { this.name = name; - this.birthday = birthday; + this.birthday = Utils.stringToDate(birthdayString); this.alreadyAdded = alreadyAdded; + this.hasYear = Utils.hasYearOfBirth(birthdayString); } public String getName() { @@ -29,4 +33,8 @@ public boolean isAlreadyAdded() { public void setAlreadyAdded(boolean alreadyAdded) { this.alreadyAdded = alreadyAdded; } + + public boolean hasYear() { + return hasYear; + } } diff --git a/app/src/main/java/website/julianrosser/birthdays/model/tasks/LoadContactsTask.java b/app/src/main/java/website/julianrosser/birthdays/model/tasks/LoadContactsTask.java index 42ab0a8..be107dc 100644 --- a/app/src/main/java/website/julianrosser/birthdays/model/tasks/LoadContactsTask.java +++ b/app/src/main/java/website/julianrosser/birthdays/model/tasks/LoadContactsTask.java @@ -12,7 +12,6 @@ import java.util.Collections; import java.util.Comparator; -import website.julianrosser.birthdays.Utils; import website.julianrosser.birthdays.model.Contact; import website.julianrosser.birthdays.model.events.ContactsLoadedEvent; @@ -56,7 +55,7 @@ private ArrayList loadContacts() { if (bdc != null && bdc.getCount() > 0) { while (bdc.moveToNext()) { String birthday = bdc.getString(0); - Contact con = new Contact(name, Utils.stringToDate(birthday), isContactAlreadyAdded(name)); + Contact con = new Contact(name, birthday, isContactAlreadyAdded(name)); contactsList.add(con); } } From e031f55989c599348d62330bf39581d40d32ca53 Mon Sep 17 00:00:00 2001 From: Julian Rosser Date: Sun, 12 Feb 2017 16:18:33 +0000 Subject: [PATCH 30/83] Show snacker feedback when user toggles alarm. Create SnackBar Helper class. --- .../birthdays/views/SnackBarHelper.java | 22 +++++++++++++++++++ .../views/viewholder/BirthdayViewHolder.java | 8 ++++--- 2 files changed, 27 insertions(+), 3 deletions(-) create mode 100644 app/src/main/java/website/julianrosser/birthdays/views/SnackBarHelper.java diff --git a/app/src/main/java/website/julianrosser/birthdays/views/SnackBarHelper.java b/app/src/main/java/website/julianrosser/birthdays/views/SnackBarHelper.java new file mode 100644 index 0000000..e7b23d1 --- /dev/null +++ b/app/src/main/java/website/julianrosser/birthdays/views/SnackBarHelper.java @@ -0,0 +1,22 @@ +package website.julianrosser.birthdays.views; + +import android.support.design.widget.Snackbar; +import android.view.View; + +import website.julianrosser.birthdays.R; +import website.julianrosser.birthdays.model.Birthday; + +public class SnackBarHelper { + + public static void alarmToggle(View view, Birthday birthday) { + // Notify user of change. If birthday is today, let user know alarm is set for next year + if (birthday.getDaysBetween() == 0 && birthday.getRemind()) { + Snackbar.make(view, view.getContext().getString(R.string.reminder_for) + birthday.getName() + " " + + birthday.getReminderString() + view.getContext().getString(R.string.for_next_year), Snackbar.LENGTH_LONG).show(); + } else { + Snackbar.make(view, view.getContext().getString(R.string.reminder_for) + birthday.getName() + " " + + birthday.getReminderString(), Snackbar.LENGTH_LONG).show(); + } + } + +} diff --git a/app/src/main/java/website/julianrosser/birthdays/views/viewholder/BirthdayViewHolder.java b/app/src/main/java/website/julianrosser/birthdays/views/viewholder/BirthdayViewHolder.java index 4bd8767..67be49d 100644 --- a/app/src/main/java/website/julianrosser/birthdays/views/viewholder/BirthdayViewHolder.java +++ b/app/src/main/java/website/julianrosser/birthdays/views/viewholder/BirthdayViewHolder.java @@ -12,6 +12,7 @@ import website.julianrosser.birthdays.database.FirebaseHelper; import website.julianrosser.birthdays.model.Birthday; import website.julianrosser.birthdays.model.events.BirthdayItemClickEvent; +import website.julianrosser.birthdays.views.SnackBarHelper; /** * ViewHolder class to hold view references to be used in recyclerview. @@ -89,14 +90,15 @@ public void setImageClickListener() { } @Override - public void onClick(View v) { - Birthday birthday = (Birthday) v.getTag(); + public void onClick(View view) { + Birthday birthday = (Birthday) view.getTag(); - int id = v.getId(); + int id = view.getId(); if (id == R.id.alarmImage) { birthday.toggleReminder(); FirebaseHelper.saveBirthdayChange(birthday, FirebaseHelper.FirebaseUpdate.UPDATE); + SnackBarHelper.alarmToggle(view, birthday); } else { // Callback EventBus.getDefault().post(new BirthdayItemClickEvent(birthday)); From e110d97eb0214daf2c3ca3626e086edf990e1904 Mon Sep 17 00:00:00 2001 From: Julian Rosser Date: Sun, 12 Feb 2017 17:17:04 +0000 Subject: [PATCH 31/83] Improve management of setting notification alarms. Update on CRUD. Load birthdays within service. --- .../julianrosser/birthdays/AlarmsHelper.java | 17 ++++ .../julianrosser/birthdays/Constants.java | 2 + .../activities/SettingsActivity.java | 6 +- .../birthdays/database/FirebaseHelper.java | 18 ++-- .../recievers/BootBroadcastReceiver.java | 6 +- .../birthdays/services/SetAlarmsService.java | 91 +++++++++++-------- 6 files changed, 88 insertions(+), 52 deletions(-) create mode 100644 app/src/main/java/website/julianrosser/birthdays/AlarmsHelper.java diff --git a/app/src/main/java/website/julianrosser/birthdays/AlarmsHelper.java b/app/src/main/java/website/julianrosser/birthdays/AlarmsHelper.java new file mode 100644 index 0000000..ec61667 --- /dev/null +++ b/app/src/main/java/website/julianrosser/birthdays/AlarmsHelper.java @@ -0,0 +1,17 @@ +package website.julianrosser.birthdays; + +import android.content.Context; +import android.content.Intent; +import android.widget.Toast; + +import website.julianrosser.birthdays.services.SetAlarmsService; + +public class AlarmsHelper { + + public static void setAllNotificationAlarms(Context context) { + Intent serviceIntent = new Intent(context, SetAlarmsService.class); + context.startService(serviceIntent); + Toast.makeText(context, "SETTING ALARMS", Toast.LENGTH_SHORT).show(); // tod - remove + } + +} \ No newline at end of file diff --git a/app/src/main/java/website/julianrosser/birthdays/Constants.java b/app/src/main/java/website/julianrosser/birthdays/Constants.java index 7c6e4d4..643f443 100644 --- a/app/src/main/java/website/julianrosser/birthdays/Constants.java +++ b/app/src/main/java/website/julianrosser/birthdays/Constants.java @@ -8,6 +8,8 @@ public class Constants { public static final String FILENAME = "birthdays.json"; public static int DEFAULT_YEAR_OF_BIRTH = 1990; + public static long DAY_IN_MILLIS = 86400000L; // / 86,400,000 milliseconds in a day + public static long HOUR_IN_MILLIS = 3600000L; // Amount of milliseconds in an hour public static int INTENT_FROM_NOTIFICATION = 30; public static int CONTACT_PERMISSION_CODE = 3; diff --git a/app/src/main/java/website/julianrosser/birthdays/activities/SettingsActivity.java b/app/src/main/java/website/julianrosser/birthdays/activities/SettingsActivity.java index 862323a..967d1fe 100644 --- a/app/src/main/java/website/julianrosser/birthdays/activities/SettingsActivity.java +++ b/app/src/main/java/website/julianrosser/birthdays/activities/SettingsActivity.java @@ -1,6 +1,5 @@ package website.julianrosser.birthdays.activities; -import android.content.Intent; import android.content.SharedPreferences; import android.os.Build; import android.os.Bundle; @@ -12,9 +11,9 @@ import com.google.android.gms.analytics.HitBuilders; import com.google.android.gms.analytics.Tracker; +import website.julianrosser.birthdays.AlarmsHelper; import website.julianrosser.birthdays.R; import website.julianrosser.birthdays.fragments.SettingsFragment; -import website.julianrosser.birthdays.services.SetAlarmsService; public class SettingsActivity extends BaseActivity { @@ -89,8 +88,7 @@ synchronized public Tracker getDefaultTracker() { @Override protected void onStop() { super.onStop(); - Intent serviceIntent = new Intent(this, SetAlarmsService.class); - startService(serviceIntent); + AlarmsHelper.setAllNotificationAlarms(this); } } diff --git a/app/src/main/java/website/julianrosser/birthdays/database/FirebaseHelper.java b/app/src/main/java/website/julianrosser/birthdays/database/FirebaseHelper.java index 4ba60d6..e99bd48 100644 --- a/app/src/main/java/website/julianrosser/birthdays/database/FirebaseHelper.java +++ b/app/src/main/java/website/julianrosser/birthdays/database/FirebaseHelper.java @@ -9,20 +9,23 @@ import com.google.firebase.database.ServerValue; import com.google.firebase.database.ValueEventListener; -import org.greenrobot.eventbus.EventBus; - import java.util.ArrayList; +import website.julianrosser.birthdays.AlarmsHelper; import website.julianrosser.birthdays.BirthdayReminder; import website.julianrosser.birthdays.Constants; import website.julianrosser.birthdays.Utils; import website.julianrosser.birthdays.model.Birthday; import website.julianrosser.birthdays.model.FirebaseBirthday; -import website.julianrosser.birthdays.model.events.BirthdaysLoadedEvent; public class FirebaseHelper { - public static void loadBirthdaysOnce() { + public interface BirthdaysLoadedListener { + void onBirthdaysReturned(ArrayList birthdays); + void onCancelled(String errorMessage); + } + + public static void loadFirebaseBirthdays(final BirthdaysLoadedListener birthdaysLoadedListener) { FirebaseUser user = BirthdayReminder.getInstance().getCurrentUser(); if (null == user) { Log.i(FirebaseHelper.class.getSimpleName(), "User not loaded yet"); @@ -31,7 +34,7 @@ public static void loadBirthdaysOnce() { // load birthdays from FB final DatabaseReference databaseReference = BirthdayReminder.getInstance().getDatabaseReference().child(user.getUid()).child(Constants.TABLE_BIRTHDAYS); if (databaseReference == null) { - Log.i(FirebaseHelper.class.getSimpleName(), "Database not loaded yet"); + Log.i(FirebaseHelper.class.getSimpleName(), "Database not initialised yet"); return; } databaseReference.addListenerForSingleValueEvent(new ValueEventListener() { @@ -43,13 +46,13 @@ public void onDataChange(DataSnapshot dataSnapshot) { Birthday birthday = Birthday.fromFB(firebaseBirthday); birthdays.add(birthday); } - EventBus.getDefault().post(new BirthdaysLoadedEvent(birthdays)); + birthdaysLoadedListener.onBirthdaysReturned(birthdays); databaseReference.removeEventListener(this); } @Override public void onCancelled(DatabaseError databaseError) { - Log.i(FirebaseHelper.class.getSimpleName(), databaseError.getMessage()); + birthdaysLoadedListener.onCancelled(databaseError.getMessage()); databaseReference.removeEventListener(this); } }); @@ -79,6 +82,7 @@ public static void saveBirthdayChange(Birthday birthday, FirebaseUpdate state) { break; } setLastUpdatedTime(); + AlarmsHelper.setAllNotificationAlarms(BirthdayReminder.getInstance()); } private static void setLastUpdatedTime() { diff --git a/app/src/main/java/website/julianrosser/birthdays/recievers/BootBroadcastReceiver.java b/app/src/main/java/website/julianrosser/birthdays/recievers/BootBroadcastReceiver.java index 4ba2e9c..11491a7 100644 --- a/app/src/main/java/website/julianrosser/birthdays/recievers/BootBroadcastReceiver.java +++ b/app/src/main/java/website/julianrosser/birthdays/recievers/BootBroadcastReceiver.java @@ -4,7 +4,7 @@ import android.content.Context; import android.content.Intent; -import website.julianrosser.birthdays.services.SetAlarmsService; +import website.julianrosser.birthdays.AlarmsHelper; /** * Listen for device restarts, so we can reset all the canceled alarms. @@ -16,9 +16,7 @@ public void onReceive(Context context, Intent i) { // Launch service to set alarms if ACTION_BOOT_COMPLETE is received if (Intent.ACTION_BOOT_COMPLETED.equals(i.getAction())) { - - Intent serviceIntent = new Intent(context, SetAlarmsService.class); - context.startService(serviceIntent); + AlarmsHelper.setAllNotificationAlarms(context); } } } \ No newline at end of file diff --git a/app/src/main/java/website/julianrosser/birthdays/services/SetAlarmsService.java b/app/src/main/java/website/julianrosser/birthdays/services/SetAlarmsService.java index 792b0ce..45e5bd0 100644 --- a/app/src/main/java/website/julianrosser/birthdays/services/SetAlarmsService.java +++ b/app/src/main/java/website/julianrosser/birthdays/services/SetAlarmsService.java @@ -1,14 +1,15 @@ package website.julianrosser.birthdays.services; import android.app.AlarmManager; +import android.app.Application; import android.app.PendingIntent; import android.app.Service; -import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.os.IBinder; import android.preference.PreferenceManager; import android.support.annotation.Nullable; +import android.util.Log; import android.widget.Toast; import org.json.JSONArray; @@ -23,28 +24,20 @@ import java.util.ArrayList; import java.util.Date; +import website.julianrosser.birthdays.BirthdayReminder; import website.julianrosser.birthdays.Constants; -import website.julianrosser.birthdays.recievers.NotificationBuilderReceiver; import website.julianrosser.birthdays.R; -import website.julianrosser.birthdays.activities.BirthdayListActivity; +import website.julianrosser.birthdays.database.FirebaseHelper; import website.julianrosser.birthdays.model.Birthday; +import website.julianrosser.birthdays.recievers.NotificationBuilderReceiver; /** * This service sets notifications alarms for each birthday. */ public class SetAlarmsService extends Service { - ArrayList mBirthdayList = new ArrayList<>(); - - long dayInMillis = 86400000l; // / 86,400,000 milliseconds in a day - long hourInMillis = 3600000l; // Amount of milliseconds in an hour - - long fullDaysBetweenInMillis, millisExtraAlarmHour, millisRemainingInDay, - dayOfReminderMillis, alarmDelayInMillis; - private AlarmManager mAlarmManager; - - static Context mContext; + private Application mContext; @Nullable @Override @@ -56,24 +49,30 @@ public IBinder onBind(Intent intent) { public void onCreate() { super.onCreate(); - mContext = getApplicationContext(); - - try { - mBirthdayList = loadBirthdays(); - } catch (IOException e) { - e.printStackTrace(); - Toast.makeText(getApplicationContext(), "Birthdays - IO Error", - Toast.LENGTH_LONG).show(); - } catch (JSONException e) { - e.printStackTrace(); - Toast.makeText(getApplicationContext(), "Birthdays - JSON Error", - Toast.LENGTH_LONG).show(); + mContext = BirthdayReminder.getInstance(); + + if (true) { // todo 3.0 - switch JSON / FB + loadBirthdaysFromFirebase(); + } else { + try { + loadBirthdaysFromJSON(); + } catch (IOException e) { + e.printStackTrace(); + Toast.makeText(getApplicationContext(), "Birthdays - IO Error", + Toast.LENGTH_LONG).show(); + } catch (JSONException e) { + e.printStackTrace(); + Toast.makeText(getApplicationContext(), "Birthdays - JSON Error", + Toast.LENGTH_LONG).show(); + } } + } + private void onBirthdaysLoaded(ArrayList birthdays) { // If user wants notifications, set alarms if (getNotificationAllowedPref()) { - for (int i = 0; i < mBirthdayList.size(); i++) { - Birthday b = mBirthdayList.get(i); + for (int i = 0; i < birthdays.size(); i++) { + Birthday b = birthdays.get(i); // If reminder not toggled off, set alarm if (b.getRemind()) { @@ -86,8 +85,25 @@ public void onCreate() { stopSelf(); } + private void loadBirthdaysFromFirebase() { + FirebaseHelper.loadFirebaseBirthdays(new FirebaseHelper.BirthdaysLoadedListener() { + @Override + public void onBirthdaysReturned(ArrayList birthdays) { + onBirthdaysLoaded(birthdays); + } + + @Override + public void onCancelled(String errorMessage) { + Toast.makeText(mContext, errorMessage, Toast.LENGTH_SHORT).show(); + Log.i(getClass().getSimpleName(), errorMessage); + mContext = null; + stopSelf(); + } + }); + } + // Function which loads Birthdays from JSON - public static ArrayList loadBirthdays() throws IOException, + private void loadBirthdaysFromJSON() throws IOException, JSONException { ArrayList birthdays = new ArrayList<>(); BufferedReader reader = null; @@ -112,10 +128,9 @@ public static ArrayList loadBirthdays() throws IOException, } catch (FileNotFoundException e) { // Ignore this one; it happens when starting fresh } finally { - if (reader != null) - reader.close(); + if (reader != null) reader.close(); } - return birthdays; + onBirthdaysLoaded(birthdays); } @SuppressWarnings("deprecation") @@ -125,22 +140,22 @@ private void setAlarm(Birthday birthday) { int remHour = 23 - currentTimeDate.getHours(); // extra hour int remMinute = 60 - currentTimeDate.getMinutes(); - millisRemainingInDay = (remHour * hourInMillis) + long millisRemainingInDay = (remHour * Constants.HOUR_IN_MILLIS) + (remMinute * 60 * 1000); // Get days between in milliseconds - fullDaysBetweenInMillis = ((birthday.getDaysBetween() - 1) * dayInMillis); + long fullDaysBetweenInMillis = ((birthday.getDaysBetween() - 1) * Constants.DAY_IN_MILLIS); // Alarm time in milliseconds int hourOfAlarm = getTimeOfReminderPref(); - millisExtraAlarmHour = hourOfAlarm * hourInMillis; // Set alarm to 12th hour of day + long millisExtraAlarmHour = hourOfAlarm * Constants.HOUR_IN_MILLIS; // For each extra day before notification, add the amount of millis in day - dayOfReminderMillis = dayInMillis * getDaysBeforeReminderPref(); + long dayOfReminderMillis = Constants.DAY_IN_MILLIS * getDaysBeforeReminderPref(); // //////// millisTotalAlarmDelay - alarmDelayInMillis = fullDaysBetweenInMillis + millisExtraAlarmHour - + millisRemainingInDay - dayOfReminderMillis; // + days + long alarmDelayInMillis = fullDaysBetweenInMillis + millisExtraAlarmHour + + millisRemainingInDay - dayOfReminderMillis; // Get alarm manager if needed if (null == mAlarmManager) { @@ -174,6 +189,8 @@ private void setAlarm(Birthday birthday) { mAlarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + alarmDelayInMillis, mNotificationReceiverPendingIntent); + + Log.i(getClass().getSimpleName(), "Set alarm for " + birthday.getName()); } } From ce54ec5d3f6acc8e90d65c12d7cc4e6ca69673df Mon Sep 17 00:00:00 2001 From: Julian Rosser Date: Sun, 12 Feb 2017 17:21:38 +0000 Subject: [PATCH 32/83] Show empty layout by default. Remove logs. --- .../birthdays/fragments/RecyclerListFragment.java | 4 ---- app/src/main/res/layout/fragment_main.xml | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/app/src/main/java/website/julianrosser/birthdays/fragments/RecyclerListFragment.java b/app/src/main/java/website/julianrosser/birthdays/fragments/RecyclerListFragment.java index 0335a1e..115eca1 100644 --- a/app/src/main/java/website/julianrosser/birthdays/fragments/RecyclerListFragment.java +++ b/app/src/main/java/website/julianrosser/birthdays/fragments/RecyclerListFragment.java @@ -3,7 +3,6 @@ import android.os.Build; import android.os.Bundle; -import android.support.design.widget.Snackbar; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.util.Log; @@ -156,9 +155,6 @@ public void onCancelled(DatabaseError databaseError) { public void onBirthdaysLoaded(BirthdaysLoadedEvent event) { mAdapter.setData(event.getBirthdays()); showEmptyMessageIfRequired(event.getBirthdays()); - Snackbar.make(emptyView, "Birthdays Loaded!", Snackbar.LENGTH_SHORT).show(); - Log.i(getClass().getSimpleName(), "setting birthday data!"); - } // Show or hide the 'no birthdays found' message depending on size of birthday Array diff --git a/app/src/main/res/layout/fragment_main.xml b/app/src/main/res/layout/fragment_main.xml index cc84f83..1ba8ef6 100644 --- a/app/src/main/res/layout/fragment_main.xml +++ b/app/src/main/res/layout/fragment_main.xml @@ -17,7 +17,7 @@ android:layout_marginTop="16dp" android:orientation="vertical" android:padding="16dp" - android:visibility="gone"> + android:visibility="visible"> Date: Sun, 12 Feb 2017 18:01:29 +0000 Subject: [PATCH 33/83] Cancel notification when deleting or changing name. Remove old Listener reference --- .../julianrosser/birthdays/AlarmsHelper.java | 22 +++++++++++++++++++ .../DialogFragments/AddEditFragment.java | 19 ++-------------- 2 files changed, 24 insertions(+), 17 deletions(-) diff --git a/app/src/main/java/website/julianrosser/birthdays/AlarmsHelper.java b/app/src/main/java/website/julianrosser/birthdays/AlarmsHelper.java index ec61667..52c3eb2 100644 --- a/app/src/main/java/website/julianrosser/birthdays/AlarmsHelper.java +++ b/app/src/main/java/website/julianrosser/birthdays/AlarmsHelper.java @@ -1,9 +1,12 @@ package website.julianrosser.birthdays; +import android.app.AlarmManager; +import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.widget.Toast; +import website.julianrosser.birthdays.recievers.NotificationBuilderReceiver; import website.julianrosser.birthdays.services.SetAlarmsService; public class AlarmsHelper { @@ -14,4 +17,23 @@ public static void setAllNotificationAlarms(Context context) { Toast.makeText(context, "SETTING ALARMS", Toast.LENGTH_SHORT).show(); // tod - remove } + // This builds an identical PendingIntent to the alarm and cancels when + public static void cancelAlarm(Context context, int id) { + + // CreateIntent to start the AlarmNotificationReceiver + Intent mNotificationReceiverIntent = new Intent(context, + NotificationBuilderReceiver.class); + + // Create pending Intent using Intent we just built + PendingIntent mNotificationReceiverPendingIntent = PendingIntent + .getBroadcast(context.getApplicationContext(), + id, + mNotificationReceiverIntent, + PendingIntent.FLAG_UPDATE_CURRENT); + + // Cancel alarm + AlarmManager mAlarmManager = (AlarmManager) context.getApplicationContext().getSystemService(Context.ALARM_SERVICE); + mAlarmManager.cancel(mNotificationReceiverPendingIntent); + } + } \ No newline at end of file diff --git a/app/src/main/java/website/julianrosser/birthdays/fragments/DialogFragments/AddEditFragment.java b/app/src/main/java/website/julianrosser/birthdays/fragments/DialogFragments/AddEditFragment.java index 8c5a020..c1d9427 100644 --- a/app/src/main/java/website/julianrosser/birthdays/fragments/DialogFragments/AddEditFragment.java +++ b/app/src/main/java/website/julianrosser/birthdays/fragments/DialogFragments/AddEditFragment.java @@ -28,6 +28,7 @@ import java.util.Calendar; import java.util.Date; +import website.julianrosser.birthdays.AlarmsHelper; import website.julianrosser.birthdays.Constants; import website.julianrosser.birthdays.R; import website.julianrosser.birthdays.Utils; @@ -59,7 +60,6 @@ public class AddEditFragment extends DialogFragment { private View view; // Use this instance of the interface to deliver action events - private NoticeDialogListener mListener; private CheckBox checkYearToggle; public AddEditFragment() { @@ -71,25 +71,9 @@ public static AddEditFragment newInstance() { return new AddEditFragment(); } - /* BirthdayListActivity implements this interface in order to receive event callbacks. Passes the - DialogFragment in case the host needs to query it. */ - public interface NoticeDialogListener { - void onDialogPositiveClick(AddEditFragment dialog, String name, int date, int month, int year, boolean includeYear, int AddEditMode, int position); - } - - // We override the Fragment.onAttach() method to instantiate NoticeDialogListener and read bundle data @Override public void onAttach(Activity activity) { super.onAttach(activity); - // Verify that the host activity implements the callback interface - try { - // Instantiate the NoticeDialogListener so we can send events to the host - mListener = (NoticeDialogListener) activity; - } catch (ClassCastException e) { - // The activity doesn't implement the interface, throw exception - throw new ClassCastException(activity.toString() - + " must implement NoticeDialogListener"); - } // Detect which state mode Dialog should be in bundle = this.getArguments(); @@ -261,6 +245,7 @@ public void onClick(View v) { if (ADD_OR_EDIT_MODE == MODE_EDIT) { String key = bundle.getString(UID_KEY); if (! Utils.isStringEmpty(key)) birthday.setUID(key); + AlarmsHelper.cancelAlarm(getActivity(), birthday.hashCode()); FirebaseHelper.saveBirthdayChange(birthday, FirebaseHelper.FirebaseUpdate.UPDATE); } else if (ADD_OR_EDIT_MODE == MODE_ADD) { FirebaseHelper.saveBirthdayChange(birthday, FirebaseHelper.FirebaseUpdate.CREATE); From 217f7a994b429fa8f54a08eb5031aac954ab116a Mon Sep 17 00:00:00 2001 From: Julian Rosser Date: Sun, 12 Feb 2017 18:04:54 +0000 Subject: [PATCH 34/83] Improve BirthdayListActivity; Remove static variables, move data storage elsewhere, move add / edit & saving / loading logic to helper classes. Remove old and irrelevant methods and references. --- .../activities/BirthdayListActivity.java | 596 ++++-------------- .../DialogFragments/ItemOptionsFragment.java | 4 +- .../birthdays/model/Birthday.java | 13 +- 3 files changed, 130 insertions(+), 483 deletions(-) diff --git a/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java b/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java index ce02070..f1314b8 100644 --- a/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java +++ b/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java @@ -1,16 +1,11 @@ package website.julianrosser.birthdays.activities; import android.Manifest; -import android.app.AlarmManager; -import android.app.PendingIntent; -import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.net.Uri; -import android.os.AsyncTask; import android.os.Bundle; -import android.os.Handler; import android.preference.PreferenceManager; import android.support.annotation.NonNull; import android.support.design.widget.FloatingActionButton; @@ -52,60 +47,46 @@ import com.google.firebase.auth.FirebaseAuth; import com.google.firebase.auth.FirebaseUser; import com.google.firebase.auth.GoogleAuthProvider; -import com.google.firebase.database.DatabaseReference; -import com.google.firebase.database.FirebaseDatabase; import com.squareup.picasso.Picasso; -import org.apache.commons.lang3.text.WordUtils; import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.Subscribe; -import org.json.JSONArray; -import org.json.JSONException; -import java.io.IOException; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.io.Writer; import java.util.ArrayList; -import java.util.Date; -import java.util.List; -import java.util.Random; +import website.julianrosser.birthdays.AlarmsHelper; import website.julianrosser.birthdays.BirthdayReminder; import website.julianrosser.birthdays.Constants; import website.julianrosser.birthdays.R; -import website.julianrosser.birthdays.adapter.BirthdayViewAdapter; +import website.julianrosser.birthdays.database.FirebaseHelper; import website.julianrosser.birthdays.fragments.DialogFragments.AddEditFragment; import website.julianrosser.birthdays.fragments.DialogFragments.ItemOptionsFragment; import website.julianrosser.birthdays.fragments.RecyclerListFragment; import website.julianrosser.birthdays.model.Birthday; -import website.julianrosser.birthdays.model.events.BirthdayAlarmToggleEvent; import website.julianrosser.birthdays.model.events.BirthdayItemClickEvent; import website.julianrosser.birthdays.model.events.BirthdaysLoadedEvent; -import website.julianrosser.birthdays.model.tasks.LoadBirthdaysTask; -import website.julianrosser.birthdays.recievers.NotificationBuilderReceiver; -import website.julianrosser.birthdays.services.SetAlarmsService; import website.julianrosser.birthdays.views.CircleTransform; +import website.julianrosser.birthdays.views.SnackBarHelper; @SuppressWarnings("deprecation") -public class BirthdayListActivity extends BaseActivity implements AddEditFragment.NoticeDialogListener, ItemOptionsFragment.ItemOptionsListener, View.OnClickListener, GoogleApiClient.OnConnectionFailedListener { - private static final int RC_SIGN_IN = 6006; - public static ArrayList birthdaysList = new ArrayList<>(); - public Tracker mTracker; - RecyclerListFragment recyclerListFragment; - BirthdayListActivity mContext; - Context mAppContext; - private FloatingActionButton floatingActionButton; +public class BirthdayListActivity extends BaseActivity implements ItemOptionsFragment.ItemOptionsListener, View.OnClickListener, GoogleApiClient.OnConnectionFailedListener { + + private static final int RC_SIGN_IN = 6006; /// todo - refactor + public static final int RC_SETTINGS = 5311; + + private ArrayList birthdaysList = new ArrayList<>(); // Keys for orientation change reference final String ADD_EDIT_INSTANCE_KEY = "fragment_add_edit"; final String ITEM_OPTIONS_INSTANCE_KEY = "fragment_item_options"; final String RECYCLER_LIST_INSTANCE_KEY = "fragment_recycler_list"; + public Tracker mTracker; + private RecyclerListFragment recyclerListFragment; // Fragment references - AddEditFragment addEditFragment; - ItemOptionsFragment itemOptionsFragment; - LoadBirthdaysTask loadBirthdaysTask; + private AddEditFragment addEditFragment; + private ItemOptionsFragment itemOptionsFragment; + private FloatingActionButton floatingActionButton; // App indexing private GoogleApiClient mClient; @@ -128,105 +109,31 @@ public class BirthdayListActivity extends BaseActivity implements AddEditFragmen private TextView textNavHeaderEmail; private ImageView imageNavHeaderProfile; - /** - * For easy access to BirthdayListActivity context from multiple Classes - */ -// public static BirthdayListActivity getContext() { -// return mContext; -// } -// -// public static Context getAppContext() { -// return mAppContext; -// } - - // This builds an identical PendingIntent to the alarm and cancels when - private void cancelAlarm(Birthday deletedBirthday) { - - // CreateIntent to start the AlarmNotificationReceiver - Intent mNotificationReceiverIntent = new Intent(BirthdayListActivity.this, - NotificationBuilderReceiver.class); - - // Create pending Intent using Intent we just built - PendingIntent mNotificationReceiverPendingIntent = PendingIntent - .getBroadcast(getApplicationContext(), deletedBirthday.getName().hashCode(), - mNotificationReceiverIntent, - PendingIntent.FLAG_UPDATE_CURRENT); - - // Finish by passing PendingIntent and delay time to AlarmManager - AlarmManager mAlarmManager = (AlarmManager) getApplicationContext().getSystemService(ALARM_SERVICE); - mAlarmManager.cancel(mNotificationReceiverPendingIntent); - } - - /** - * Save Birthdays to JSON file, then Update alarms by starting Service - **/ - public void saveBirthdays() - throws JSONException, IOException { - - if (birthdaysList != null) { - - try { - // Build an array in JSON - JSONArray array = new JSONArray(); - for (Birthday b : birthdaysList) - array.put(b.toJSON()); - - // Write the file to disk - Writer writer = null; - try { - OutputStream out = mAppContext.openFileOutput(Constants.FILENAME, - Context.MODE_PRIVATE); - writer = new OutputStreamWriter(out); - writer.write(array.toString()); - - } finally { - if (writer != null) - writer.close(); - } - - } catch (JSONException | IOException e) { - e.printStackTrace(); - } - - // Launch service to update alarms when data changed - Intent serviceIntent = new Intent(BirthdayListActivity.this, SetAlarmsService.class); - startService(serviceIntent); - } - } - - // Force UI thread to ensure mAdapter updates RecyclerView list - public void dataChangedUiThread() { - // Reorder ArrayList to sort by desired method - SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); - - // Get users sort preference - if (Integer.valueOf(sharedPref.getString(getApplicationContext().getString(R.string.pref_sort_by_key), "0")) == 1) { - BirthdayViewAdapter.sortBirthdaysByName(); - } else { - BirthdayViewAdapter.sortBirthdaysByDate(); - } - - if (floatingActionButton != null && floatingActionButton.getVisibility() == View.INVISIBLE) { - floatingActionButton.show(); - } + private String themePref = ""; - RecyclerListFragment.mAdapter.notifyDataSetChanged(); - RecyclerListFragment.showEmptyMessageIfRequired(); - } +// // Force UI thread to ensure mAdapter updates RecyclerView list +// public void dataChangedUiThread() { +// +// // Reorder ArrayList to sort by desired method +// SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); +// +// // Get users sort preference // todo - necexssary? +// if (Integer.valueOf(sharedPref.getString(getApplicationContext().getString(R.string.pref_sort_by_key), "0")) == 1) { +// recyclerListFragment.mAdapter.sortBirthdaysByName(); +// } else { +// recyclerListFragment.mAdapter.sortBirthdaysByDate(); +// } +// +// if (floatingActionButton != null && floatingActionButton.getVisibility() == View.INVISIBLE) { +// floatingActionButton.show(); +// } +// +// recyclerListFragment.mAdapter.notifyDataSetChanged(); - public static boolean isContactAlreadyAdded(Birthday contact) { - boolean onList = false; - for (Birthday b : birthdaysList) { - if (b.getName().equals(contact.getName())) { - onList = true; - } - } - return onList; - } +// } @Override protected void onCreate(Bundle savedInstanceState) { - setTheme(); super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); @@ -243,17 +150,12 @@ protected void onCreate(Bundle savedInstanceState) { @Override public boolean onNavigationItemSelected(MenuItem menuItem) { - //Checking if the item is in checked state or not, if not make it in checked state - if (menuItem.isChecked()) menuItem.setChecked(false); - else menuItem.setChecked(true); - //Closing drawer on item click mDrawerLayout.closeDrawers(); //Check to see which item was being clicked and perform appropriate action switch (menuItem.getItemId()) { - //Replacing the main content with ContentFragment Which is our Inbox View; case R.id.menu_birthdays: return true; case R.id.menu_help: @@ -263,13 +165,14 @@ public boolean onNavigationItemSelected(MenuItem menuItem) { checkContactPermissionAndLaunchImportActivity(); return true; case R.id.menu_settings: - startActivity(new Intent(getApplicationContext(), SettingsActivity.class)); + startActivityForResult(new Intent(getApplicationContext(), SettingsActivity.class), RC_SETTINGS); return true; default: return true; } } }); + navigationView.setCheckedItem(R.id.menu_birthdays); // Nav header info View headerView = navigationView.inflateHeaderView(R.layout.layout_nav_header); @@ -287,13 +190,17 @@ public boolean onNavigationItemSelected(MenuItem menuItem) { mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout, mToolbar, R.string.birthday, R.string.button_negative) { - /** Called when a drawer has settled in a completely closed state. */ + /** + * Called when a drawer has settled in a completely closed state. + */ public void onDrawerClosed(View view) { super.onDrawerClosed(view); invalidateOptionsMenu(); // creates call to onPrepareOptionsMenu() } - /** Called when a drawer has settled in a completely open state. */ + /** + * Called when a drawer has settled in a completely open state. + */ public void onDrawerOpened(View drawerView) { super.onDrawerOpened(drawerView); invalidateOptionsMenu(); // creates call to onPrepareOptionsMenu() @@ -311,14 +218,10 @@ public void onDrawerOpened(View drawerView) { floatingActionButton.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { // Open New Birthday Fragment - showAddEditBirthdayFragment(AddEditFragment.MODE_ADD, 0); + showEditBirthdayFragment(); } }); - // Initialize context reference - mContext = this; - mAppContext = getApplicationContext(); - // Find RecyclerListFragment reference if (savedInstanceState != null) { //Restore the fragment's instance @@ -349,7 +252,7 @@ public void onClick(View v) { mClient = new GoogleApiClient.Builder(this).addApi(AppIndex.API).build(); mUrl = "http://julianrosser.website"; mTitle = "Birthday Reminders"; - mDescription = "Simple birthday reminders for loved-ones"; + mDescription = "Simple birthday reminder notifications"; if (getIntent().getExtras() != null && getIntent().getExtras().getInt(Constants.INTENT_FROM_KEY, 10) == Constants.INTENT_FROM_NOTIFICATION) { mTracker.send(new HitBuilders.EventBuilder() @@ -357,23 +260,8 @@ public void onClick(View v) { .setAction("Notification Touch") .build()); } - - // Get sample of theme choice - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); - Random r = new Random(); - if (r.nextInt(10) == 5) { - mTracker.send(new HitBuilders.EventBuilder() - .setCategory("Samples") - .setAction("Theme") - .setValue(Long.valueOf(prefs.getString(getResources().getString(R.string.pref_theme_key), "0"))) - .build()); - } } - /** - * Google Authentication methods - */ - @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); @@ -382,6 +270,14 @@ public void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == RC_SIGN_IN) { GoogleSignInResult result = Auth.GoogleSignInApi.getSignInResultFromIntent(data); handleSignInResult(result); + + } else if (requestCode == RC_SETTINGS) { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); + String newTheme = prefs.getString(getString(R.string.pref_theme_key), "0"); + if (! newTheme.equals(themePref)) { + recreate(); + themePref = newTheme; + } } } @@ -430,7 +326,7 @@ private void setUpGoogleSignInButton(View headerView) { mAuthListener = new FirebaseAuth.AuthStateListener() { @Override public void onAuthStateChanged(@NonNull FirebaseAuth firebaseAuth) { - FirebaseUser user = firebaseAuth.getCurrentUser(); + FirebaseUser user = firebaseAuth.getCurrentUser(); // todo - move to HERE if (user != null) { // User is signed in handleUserAuthenticated(user); @@ -450,7 +346,9 @@ private void handleUserAuthenticated(FirebaseUser user) { textNavHeaderEmail.setText(user.getEmail()); Picasso.with(getApplicationContext()).load(user.getPhotoUrl()).transform(new CircleTransform()).into(imageNavHeaderProfile); setNavHeaderUserState(NavHeaderState.SIGNED_IN); - Snackbar.make(floatingActionButton, user.getDisplayName() + "signed IN | " + user.getEmail(), Snackbar.LENGTH_SHORT).show(); + Snackbar.make(floatingActionButton, user.getDisplayName() + " signed in", Snackbar.LENGTH_SHORT).show(); + BirthdayReminder.getInstance().setUser(user); + recyclerListFragment.loadBirthdays(); } private GoogleSignInOptions setUpGoogleSignInOptions() { @@ -470,11 +368,6 @@ private GoogleSignInOptions setUpGoogleSignInOptions() { return gso; } - enum NavHeaderState { - LOGGED_OUT, - SIGNED_IN - } - public void setNavHeaderUserState(NavHeaderState state) { switch (state) { case LOGGED_OUT: @@ -492,7 +385,7 @@ public void setNavHeaderUserState(NavHeaderState state) { @Override public void onConnectionFailed(@NonNull ConnectionResult connectionResult) { - Snackbar.make(floatingActionButton, "Sign in failed", Snackbar.LENGTH_SHORT).show(); + Snackbar.make(navigationView, "Sign in failed", Snackbar.LENGTH_SHORT).show(); } public Action getAction() { @@ -516,34 +409,15 @@ public void onStart() { AppIndex.AppIndexApi.start(mClient, getAction()); } - @Override - protected void onDestroy() { - super.onDestroy(); - - // Remove context to prevent memory leaks - mContext = null; - - // Cancel the task if it's running - if (isTaskRunning()) { - loadBirthdaysTask.cancel(true); - } - - loadBirthdaysTask = null; - } - @Override protected void onResume() { super.onResume(); + recyclerListFragment.loadBirthdays(); - if (mContext == null) { - mContext = this; - } - if (mAppContext == null) { - mAppContext = getApplicationContext(); + if (navigationView != null) { + navigationView.setCheckedItem(R.id.menu_birthdays); } - dataChangedUiThread(); - // Tracker mTracker.setScreenName("BirthdayListActivity"); mTracker.send(new HitBuilders.ScreenViewBuilder().build()); @@ -559,20 +433,6 @@ protected void onPause() { @Override protected void onStop() { - try { - saveBirthdays(); - } catch (Exception e) { - e.printStackTrace(); - } - - Random r = new Random(); - if (r.nextInt(10) == 5) { - mTracker.send(new HitBuilders.EventBuilder() - .setCategory("Data") - .setAction("Birthdays count") - .setValue(birthdaysList.size()) - .build()); - } if (mAuthListener != null) { mAuth.removeAuthStateListener(mAuthListener); } @@ -607,200 +467,48 @@ protected void onSaveInstanceState(Bundle outState) { } } - public void showAddEditBirthdayFragment(int mode, int birthdayListPosition) { + public void showEditBirthdayFragment() { // Create an instance of the dialog fragment and show it addEditFragment = AddEditFragment.newInstance(); // Create bundle for storing mode information Bundle bundle = new Bundle(); // Pass mode parameter onto Fragment - bundle.putInt(AddEditFragment.MODE_KEY, mode); - - // If we are editing an old birthday, pass its information to fragment - if (mode == AddEditFragment.MODE_EDIT) { - // Reference to birthday we're editing - Birthday editBirthday = birthdaysList.get(birthdayListPosition); - // Pass birthday's data to Fragment - bundle.putInt(AddEditFragment.DATE_KEY, editBirthday.getDate().getDate()); - bundle.putInt(AddEditFragment.MONTH_KEY, editBirthday.getDate().getMonth()); - bundle.putInt(AddEditFragment.YEAR_KEY, editBirthday.getYear()); - bundle.putInt(AddEditFragment.POS_KEY, birthdayListPosition); - bundle.putString(AddEditFragment.NAME_KEY, editBirthday.getName()); - bundle.putBoolean(AddEditFragment.SHOW_YEAR_KEY, editBirthday.shouldIncludeYear()); - } - - // Pass bundle to Dialog, get FragmentManager and show + bundle.putInt(AddEditFragment.MODE_KEY, AddEditFragment.MODE_ADD); addEditFragment.setArguments(bundle); + // Pass bundle to Dialog, get FragmentManager and show addEditFragment.show(getSupportFragmentManager(), "AddEditBirthdayFragment"); } - // This method creates and shows a new ItemOptionsFragment, this replaces ContextMenu - public void showItemOptionsFragment(int position) { + public void showAddBirthdayFragment(Birthday birthday) { // Create an instance of the dialog fragment and show it - itemOptionsFragment = ItemOptionsFragment.newInstance(position); - itemOptionsFragment.setRetainInstance(true); - itemOptionsFragment.show(getSupportFragmentManager(), "AddEditBirthdayFragment"); - } - - // Callback from AddEditFragment, create new Birthday object and add to array - @Override - public void onDialogPositiveClick(AddEditFragment dialog, String name, int day, int month, int year, boolean includeYear, int addEditMode, final int position) { - - // Build date object which will be used by new Birthday - Date dateOfBirth = new Date(); - dateOfBirth.setYear(year); - dateOfBirth.setMonth(month); - dateOfBirth.setDate(day); - - - // Format name by capitalizing name - name = WordUtils.capitalize(name); - - final Birthday birthday; - - // Decide whether to create new or edit old birthday - if (addEditMode == AddEditFragment.MODE_EDIT) { - // Edit text - birthday = birthdaysList.get(position); - birthday.edit(name, dateOfBirth, true, includeYear); - - runOnUiThread(new Runnable() { - @Override - public void run() { - - /** Logic for edit animation. Depending first on sorting preference, check whether the sorting will change. - * if so, used adapter moved animation, else just refresh information */ - SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); - - // Get users sorting preference - if (Integer.valueOf(sharedPref.getString(getApplicationContext().getString(R.string.pref_sort_by_key), "0")) != 1) { - - // PREF SORT: DATE - if (BirthdayViewAdapter.willChangeDateOrder(birthday)) { - - // Order will change, sort, then notify adapter of move - BirthdayViewAdapter.sortBirthdaysByDate(); - RecyclerListFragment.mAdapter.notifyItemMoved(position, birthdaysList.indexOf(birthday)); - } else { - // No order change, so just notify item changed - RecyclerListFragment.mAdapter.notifyItemChanged(birthdaysList.indexOf(birthday)); - } - } else { - - // PREF SORT: NAME. If order changes, sort the notify adapter - if (BirthdayViewAdapter.willChangeNameOrder(birthday)) { - - // Order will change, sort, then notify adapter of move - BirthdayViewAdapter.sortBirthdaysByName(); - RecyclerListFragment.mAdapter.notifyItemMoved(position, birthdaysList.indexOf(birthday)); - - } else { - // order not changes, so forget sot, and just notify item changed - RecyclerListFragment.mAdapter.notifyItemChanged(birthdaysList.indexOf(birthday)); - } - } - // Delay update until after animation has finished - Runnable r = new Runnable() { - public void run() { - RecyclerListFragment.mAdapter.notifyDataSetChanged(); - } - }; - new Handler().postDelayed(r, 500); - } - }); - - } else { - // Create birthday, add to array and notify adapter - birthday = new Birthday(name, dateOfBirth, true, includeYear); - birthdaysList.add(birthday); - - // Notify adapter - runOnUiThread(new Runnable() { - public void run() { - - SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); + addEditFragment = AddEditFragment.newInstance(); - // Get users sort preference - if (Integer.valueOf(sharedPref.getString(getApplicationContext().getString(R.string.pref_sort_by_key), "0")) == 1) { - BirthdayViewAdapter.sortBirthdaysByName(); - } else { - BirthdayViewAdapter.sortBirthdaysByDate(); - } - RecyclerListFragment.mAdapter.notifyItemInserted(birthdaysList.indexOf(birthday)); - RecyclerListFragment.showEmptyMessageIfRequired(); - } - }); + // Create bundle for storing mode information + Bundle bundle = new Bundle(); - mTracker.send(new HitBuilders.EventBuilder() - .setCategory("Action") - .setAction("New Birthday") - .build()); - } + // Pass mode parameter onto Fragment + bundle.putInt(AddEditFragment.MODE_KEY, AddEditFragment.MODE_EDIT); + // Reference to birthday we're editing + // Pass birthday's data to Fragment + bundle.putInt(AddEditFragment.DATE_KEY, birthday.getDate().getDate()); + bundle.putInt(AddEditFragment.MONTH_KEY, birthday.getDate().getMonth()); + bundle.putInt(AddEditFragment.YEAR_KEY, birthday.getYear()); + bundle.putString(AddEditFragment.NAME_KEY, birthday.getName()); + bundle.putString(AddEditFragment.UID_KEY, birthday.getUID()); + bundle.putBoolean(AddEditFragment.SHOW_YEAR_KEY, birthday.shouldIncludeYear()); - try { - saveBirthdays(); - } catch (Exception e) { - e.printStackTrace(); - } + // Pass bundle to Dialog, get FragmentManager and show + addEditFragment.setArguments(bundle); + addEditFragment.show(getSupportFragmentManager(), "AddEditBirthdayFragment"); } - // We only use this method to delete data from Birthday array and pass a reference to the cancel alarm method. - public void deleteFromArray(final int position) { - - // Cancel the notification PendingIntent - cancelAlarm(birthdaysList.get(position)); - - // Notify adapter - mContext.runOnUiThread(new Runnable() { - public void run() { - - final Birthday birthdayToDelete = birthdaysList.get(position); - - birthdaysList.remove(position); - - RecyclerListFragment.mAdapter.notifyItemRemoved(position); - RecyclerListFragment.showEmptyMessageIfRequired(); - - if (floatingActionButton != null && floatingActionButton.getVisibility() == View.INVISIBLE) { - floatingActionButton.show(); - } - try { - saveBirthdays(); - } catch (Exception e) { - e.printStackTrace(); - } - Snackbar.make(floatingActionButton, getApplicationContext().getString(R.string.deleted) + " " - + birthdayToDelete.getName(), Snackbar.LENGTH_LONG).setAction(R.string.undo, - new View.OnClickListener() { - @Override - public void onClick(View v) { - birthdaysList.add(birthdayToDelete); - // Notify adapter - mContext.runOnUiThread(new Runnable() { - public void run() { - - SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); - - // Get users sort preference - if (Integer.valueOf(sharedPref.getString(getApplicationContext().getString(R.string.pref_sort_by_key), "0")) == 1) { - BirthdayViewAdapter.sortBirthdaysByName(); - } else { - BirthdayViewAdapter.sortBirthdaysByDate(); - } - - RecyclerListFragment.mAdapter.notifyItemInserted(birthdaysList.indexOf(birthdayToDelete)); - RecyclerListFragment.showEmptyMessageIfRequired(); - } - }); - try { - saveBirthdays(); - } catch (Exception e) { - e.printStackTrace(); - } - } - }).show(); - } - }); + // This method creates and shows a new ItemOptionsFragment, this replaces ContextMenu + public void showItemOptionsFragment(Birthday birthday) { + // Create an instance of the dialog fragment and show it + itemOptionsFragment = ItemOptionsFragment.newInstance(birthday); + itemOptionsFragment.setRetainInstance(true); + itemOptionsFragment.show(getSupportFragmentManager(), "ItemOptionsBirthdayFragment"); } @Override @@ -820,20 +528,10 @@ public boolean onOptionsItemSelected(MenuItem item) { if (id == R.id.action_sign_out) { signOutGoogle(); return true; - } else if (id == R.id.action_firebase) { - performFirebaseAction(); - return true; } return super.onOptionsItemSelected(item); } - private void performFirebaseAction() { - DatabaseReference dbr = BirthdayReminder.getInstance().getDatabaseReference(); - for (int i = 0; i < birthdaysList.size(); i++) { - dbr.child("" + i).setValue(birthdaysList.get(i).getName()); - } - } - private void signOutGoogle() { Auth.GoogleSignInApi.signOut(mGoogleApiClient).setResultCallback( new ResultCallback() { @@ -845,6 +543,7 @@ public void onResult(Status status) { }); } + // todo - unlink Google private void revokeAccessGoogle() { Auth.GoogleSignInApi.revokeAccess(mGoogleApiClient).setResultCallback( new ResultCallback() { @@ -855,31 +554,24 @@ public void onResult(Status status) { }); } - // Call this method from Adapter so reference can be kept here in BirthdayListActivity - public void launchLoadBirthdaysTask() { - loadBirthdaysTask = new LoadBirthdaysTask(); - loadBirthdaysTask.execute(); - } - - // Check if Async task is currently running, to prevent errors when exiting - private boolean isTaskRunning() { - return (loadBirthdaysTask != null) && (loadBirthdaysTask.getStatus() == AsyncTask.Status.RUNNING); - } - // Set theme based on users preference - // todo - refactor public void setTheme() { SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); - + String pref; if (prefs.getString(getResources().getString(R.string.pref_theme_key), "0").equals("0")) { setTheme(R.style.BlueTheme); + pref = "0"; } else if (prefs.getString(getResources().getString(R.string.pref_theme_key), "0").equals("1")) { setTheme(R.style.PinkTheme); + pref = "1"; } else if (prefs.getString(getResources().getString(R.string.pref_theme_key), "0").equals("2")) { setTheme(R.style.GreenTheme); + pref = "2"; } else { setTheme(R.style.PinkTheme); + pref = "1"; } + themePref = pref; } @Override @@ -896,22 +588,6 @@ public void onClick(View v) { } } - @Subscribe - public void onMessageEvent(BirthdaysLoadedEvent event) { - birthdaysList = event.getBirthdays(); - RecyclerListFragment.showEmptyMessageIfRequired(); - } - - @Subscribe - public void onAlarmToggle(BirthdayAlarmToggleEvent event) { - alarmToggled(event.getCurrentPosition()); - } - - @Subscribe - public void onBirthdayClicked(BirthdayItemClickEvent event) { - showItemOptionsFragment(event.getCurrentPosition()); - } - private void signIn() { Intent signInIntent = Auth.GoogleSignInApi.getSignInIntent(mGoogleApiClient); startActivityForResult(signInIntent, RC_SIGN_IN); @@ -929,9 +605,10 @@ public void setTitle(CharSequence title) { * - onItemDelete: Delete selected birthday from array */ @Override - public void onItemEdit(ItemOptionsFragment dialog, int position) { + public void onItemEdit(ItemOptionsFragment dialog, Birthday birthday) { itemOptionsFragment.dismiss(); - showAddEditBirthdayFragment(AddEditFragment.MODE_EDIT, position); + + showAddBirthdayFragment(birthday); mTracker.send(new HitBuilders.EventBuilder() .setCategory("Action") @@ -940,61 +617,18 @@ public void onItemEdit(ItemOptionsFragment dialog, int position) { } @Override - public void onItemDelete(ItemOptionsFragment dialog, int position) { + public void onItemDelete(ItemOptionsFragment dialog, Birthday birthday) { itemOptionsFragment.dismiss(); - deleteFromArray(position); - - mTracker.send(new HitBuilders.EventBuilder() - .setCategory("Action") - .setAction("Delete") - .build()); + FirebaseHelper.saveBirthdayChange(birthday, FirebaseHelper.FirebaseUpdate.DELETE); + AlarmsHelper.cancelAlarm(this, birthday.hashCode()); } @Override - public void onItemToggleAlarm(ItemOptionsFragment dialog, int position) { + public void onItemToggleAlarm(ItemOptionsFragment dialog, Birthday birthday) { + birthday.toggleReminder(); + FirebaseHelper.saveBirthdayChange(birthday, FirebaseHelper.FirebaseUpdate.UPDATE); + SnackBarHelper.alarmToggle(navigationView, birthday); itemOptionsFragment.dismiss(); - // Change birthdays remind bool - birthdaysList.get(position).toggleReminder(); - alarmToggled(position); - - mTracker.send(new HitBuilders.EventBuilder() - .setCategory("Action") - .setAction("Toggle Alarm OPTION") - .build()); - } - - // This is in a separate method so it can be called from different classes - public void alarmToggled(int position) { - - // Use position parameter to get Birthday reference - Birthday birthday = birthdaysList.get(position); - - // Cancel the previously set alarm, without re-calling service - cancelAlarm(birthday); - - // Notify adapter of change, so that UI is updated - dataChangedUiThread(); - - // Notify user of change. If birthday is today, let user know alarm is set for next year - if (birthday.getDaysBetween() == 0 && birthday.getRemind()) { - Snackbar.make(floatingActionButton, getApplicationContext().getString(R.string.reminder_for) + birthday.getName() + " " + - birthday.getReminderString() + getApplicationContext().getString(R.string.for_next_year), Snackbar.LENGTH_LONG).show(); - } else { - Snackbar.make(floatingActionButton, getApplicationContext().getString(R.string.reminder_for) + birthday.getName() + " " + - birthday.getReminderString(), Snackbar.LENGTH_LONG).show(); - } - - // Attempt to save updated Birthday data - try { - saveBirthdays(); - } catch (Exception e) { - e.printStackTrace(); - } - - mTracker.send(new HitBuilders.EventBuilder() - .setCategory("Action") - .setAction("Toggle Alarm") - .build()); } public void checkContactPermissionAndLaunchImportActivity() { @@ -1035,6 +669,30 @@ public void onRequestPermissionsResult(int requestCode, public void launchImportContactActivity() { Intent intent = new Intent(this, ImportContactsActivity.class); + Bundle bundle = new Bundle(); + + ArrayList birthdayNames = new ArrayList<>(); + for (Birthday b : birthdaysList) { + birthdayNames.add(b.getName()); + } + bundle.putStringArrayList(ImportContactsActivity.BIRTHDAYS_ARRAY_KEY, birthdayNames); + intent.putExtras(bundle); startActivity(intent); } + + @Subscribe + public void onBirthdayClicked(BirthdayItemClickEvent event) { + showItemOptionsFragment(event.getBirthday()); + } + + // Birthdays array required to prevent importing duplicate contacts //todo - reduce to string names + @Subscribe + public void onBirthdaysLoaded(BirthdaysLoadedEvent event) { + birthdaysList = event.getBirthdays(); + } + + enum NavHeaderState { + LOGGED_OUT, + SIGNED_IN + } } \ No newline at end of file diff --git a/app/src/main/java/website/julianrosser/birthdays/fragments/DialogFragments/ItemOptionsFragment.java b/app/src/main/java/website/julianrosser/birthdays/fragments/DialogFragments/ItemOptionsFragment.java index aa8518d..f9b2235 100644 --- a/app/src/main/java/website/julianrosser/birthdays/fragments/DialogFragments/ItemOptionsFragment.java +++ b/app/src/main/java/website/julianrosser/birthdays/fragments/DialogFragments/ItemOptionsFragment.java @@ -59,13 +59,13 @@ public interface ItemOptionsListener { void onItemToggleAlarm(ItemOptionsFragment dialog, Birthday birthday); } - // We override the Fragment.onAttach() method to instantiate NoticeDialogListener and read bundle data + // We override the Fragment.onAttach() method to instantiate ItemOptionListener and read bundle data @Override public void onAttach(Activity activity) { super.onAttach(activity); // Verify that the host activity implements the callback interface try { - // Instantiate the NoticeDialogListener so we can send events to the host + // Instantiate the ItemOptionListener so we can send events to the host mListener = (ItemOptionsListener) activity; } catch (ClassCastException e) { // The activity doesn't implement the interface, throw exception diff --git a/app/src/main/java/website/julianrosser/birthdays/model/Birthday.java b/app/src/main/java/website/julianrosser/birthdays/model/Birthday.java index a14dd64..d8526bd 100644 --- a/app/src/main/java/website/julianrosser/birthdays/model/Birthday.java +++ b/app/src/main/java/website/julianrosser/birthdays/model/Birthday.java @@ -49,7 +49,7 @@ public class Birthday { * */ public Birthday(String name, Date dateOfBirthday, boolean notifyUserOfBirthday, boolean includeYear) { - this.name = name; + this.name = WordUtils.capitalize(name);; this.remind = notifyUserOfBirthday; this.date = dateOfBirthday; this.yearOfBirth = dateOfBirthday.getYear(); @@ -57,17 +57,6 @@ public Birthday(String name, Date dateOfBirthday, boolean notifyUserOfBirthday, this.uID = UUID.randomUUID().toString(); } - /** - * For updating Birthday information without creating new - */ - public void edit(String editName, Date editDate, boolean editRemind, boolean includeYear) { - this.name = editName; - this.remind = editRemind; - this.date = editDate; - this.yearOfBirth = editDate.getYear(); - this.showYear = includeYear; - } - /** * Constructor for parsing JSON data to Birthday objects. For each Birthday variable, check JSON data and update if found. */ From dc60663d381655603fd0f3d6f21f61fbf4694234 Mon Sep 17 00:00:00 2001 From: Julian Rosser Date: Sun, 12 Feb 2017 18:12:00 +0000 Subject: [PATCH 35/83] Show SnackBar when deleting birthday. --- .../birthdays/activities/BirthdayListActivity.java | 1 + .../website/julianrosser/birthdays/views/SnackBarHelper.java | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java b/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java index f1314b8..3ec1063 100644 --- a/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java +++ b/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java @@ -621,6 +621,7 @@ public void onItemDelete(ItemOptionsFragment dialog, Birthday birthday) { itemOptionsFragment.dismiss(); FirebaseHelper.saveBirthdayChange(birthday, FirebaseHelper.FirebaseUpdate.DELETE); AlarmsHelper.cancelAlarm(this, birthday.hashCode()); + SnackBarHelper.birthdayDeleted(navigationView, birthday); } @Override diff --git a/app/src/main/java/website/julianrosser/birthdays/views/SnackBarHelper.java b/app/src/main/java/website/julianrosser/birthdays/views/SnackBarHelper.java index e7b23d1..fe66cec 100644 --- a/app/src/main/java/website/julianrosser/birthdays/views/SnackBarHelper.java +++ b/app/src/main/java/website/julianrosser/birthdays/views/SnackBarHelper.java @@ -19,4 +19,9 @@ public static void alarmToggle(View view, Birthday birthday) { } } + public static void birthdayDeleted(View view, Birthday birthday) { + Snackbar.make(view, birthday.getName() + " " + view.getContext().getString(R.string.deleted) + , Snackbar.LENGTH_LONG).show(); + } + } From e3908dd6f03539a01e6e3b5643401bd9a714937d Mon Sep 17 00:00:00 2001 From: Julian Rosser Date: Sun, 12 Feb 2017 18:16:27 +0000 Subject: [PATCH 36/83] Add PrivacyPolicy to NavDrawer --- .../birthdays/activities/BirthdayListActivity.java | 2 ++ app/src/main/res/menu/menu_nav_drawer.xml | 3 +++ 2 files changed, 5 insertions(+) diff --git a/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java b/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java index 3ec1063..fd7b62f 100644 --- a/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java +++ b/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java @@ -167,6 +167,8 @@ public boolean onNavigationItemSelected(MenuItem menuItem) { case R.id.menu_settings: startActivityForResult(new Intent(getApplicationContext(), SettingsActivity.class), RC_SETTINGS); return true; + case R.id.menu_privacy: + startActivity(new Intent(getApplicationContext(), PrivacyPolicyActivity.class)); default: return true; } diff --git a/app/src/main/res/menu/menu_nav_drawer.xml b/app/src/main/res/menu/menu_nav_drawer.xml index 48be83b..fb8f221 100644 --- a/app/src/main/res/menu/menu_nav_drawer.xml +++ b/app/src/main/res/menu/menu_nav_drawer.xml @@ -22,5 +22,8 @@ + From 8da89568b751f2d0037c4ecd29e26fc80ac400f7 Mon Sep 17 00:00:00 2001 From: Julian Rosser Date: Sun, 12 Feb 2017 18:58:03 +0000 Subject: [PATCH 37/83] Sort birthdays using users preference. --- .../activities/BirthdayListActivity.java | 22 ---------- .../adapter/BirthdayViewAdapter.java | 44 +++++++------------ .../fragments/RecyclerListFragment.java | 1 - 3 files changed, 15 insertions(+), 52 deletions(-) diff --git a/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java b/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java index fd7b62f..c2689c6 100644 --- a/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java +++ b/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java @@ -111,27 +111,6 @@ public class BirthdayListActivity extends BaseActivity implements ItemOptionsFra private String themePref = ""; -// // Force UI thread to ensure mAdapter updates RecyclerView list -// public void dataChangedUiThread() { -// -// // Reorder ArrayList to sort by desired method -// SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); -// -// // Get users sort preference // todo - necexssary? -// if (Integer.valueOf(sharedPref.getString(getApplicationContext().getString(R.string.pref_sort_by_key), "0")) == 1) { -// recyclerListFragment.mAdapter.sortBirthdaysByName(); -// } else { -// recyclerListFragment.mAdapter.sortBirthdaysByDate(); -// } -// -// if (floatingActionButton != null && floatingActionButton.getVisibility() == View.INVISIBLE) { -// floatingActionButton.show(); -// } -// -// recyclerListFragment.mAdapter.notifyDataSetChanged(); - -// } - @Override protected void onCreate(Bundle savedInstanceState) { setTheme(); @@ -414,7 +393,6 @@ public void onStart() { @Override protected void onResume() { super.onResume(); - recyclerListFragment.loadBirthdays(); if (navigationView != null) { navigationView.setCheckedItem(R.id.menu_birthdays); diff --git a/app/src/main/java/website/julianrosser/birthdays/adapter/BirthdayViewAdapter.java b/app/src/main/java/website/julianrosser/birthdays/adapter/BirthdayViewAdapter.java index 4bc576f..408bd1c 100644 --- a/app/src/main/java/website/julianrosser/birthdays/adapter/BirthdayViewAdapter.java +++ b/app/src/main/java/website/julianrosser/birthdays/adapter/BirthdayViewAdapter.java @@ -1,5 +1,8 @@ package website.julianrosser.birthdays.adapter; +import android.content.Context; +import android.content.SharedPreferences; +import android.preference.PreferenceManager; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; @@ -9,6 +12,7 @@ import java.util.Collections; import java.util.Comparator; +import website.julianrosser.birthdays.BirthdayReminder; import website.julianrosser.birthdays.R; import website.julianrosser.birthdays.model.Birthday; import website.julianrosser.birthdays.views.viewholder.BirthdayViewHolder; @@ -53,41 +57,23 @@ public void onViewRecycled(BirthdayViewHolder holder) { public void setData(ArrayList birthdays) { this.birthdays = birthdays; - notifyDataSetChanged(); - } - // use this method to find out whether edit will change order of birthdays - public boolean willChangeDateOrder(Birthday b) { - ArrayList originalOrder = birthdays; + Context context = BirthdayReminder.getInstance(); - int originalPos = originalOrder.indexOf(b); - //Sorting - Collections.sort(originalOrder, new Comparator() { - @Override - public int compare(Birthday b1, Birthday b2) { - return b1.getDate().compareTo(b2.getDate()); - } - }); - return originalPos != originalOrder.indexOf(b); - } + SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(context); - // use this method to find out whether edit will change order of birthdays - public boolean willChangeNameOrder(Birthday b) { - ArrayList originalOrder = birthdays; + // Get users sort preference + if (Integer.valueOf(sharedPref.getString(context.getString(R.string.pref_sort_by_key), "0")) == 1) { + sortBirthdaysByName(); + } else { + sortBirthdaysByDate(); + } - int originalPos = originalOrder.indexOf(b); - //Sorting - Collections.sort(originalOrder, new Comparator() { - @Override - public int compare(Birthday b1, Birthday b2) { - return b1.getName().compareTo(b2.getName()); - } - }); - return originalPos != originalOrder.indexOf(b); + notifyDataSetChanged(); } // Sort Birthday array by closest date - public void sortBirthdaysByDate() { + private void sortBirthdaysByDate() { for (Birthday b : birthdays) { b.setYearOfDate(Birthday.getYearOfNextBirthday(b.getDate())); } @@ -101,7 +87,7 @@ public int compare(Birthday b1, Birthday b2) { } // Sort Birthday array by first name - public void sortBirthdaysByName() { + private void sortBirthdaysByName() { Collections.sort(birthdays, new Comparator() { @Override public int compare(Birthday b1, Birthday b2) { diff --git a/app/src/main/java/website/julianrosser/birthdays/fragments/RecyclerListFragment.java b/app/src/main/java/website/julianrosser/birthdays/fragments/RecyclerListFragment.java index 115eca1..3aeffcb 100644 --- a/app/src/main/java/website/julianrosser/birthdays/fragments/RecyclerListFragment.java +++ b/app/src/main/java/website/julianrosser/birthdays/fragments/RecyclerListFragment.java @@ -87,7 +87,6 @@ public void onScrollStateChanged(RecyclerView recyclerView, int newState) { recyclerView.setHasFixedSize(true); mAdapter = new BirthdayViewAdapter(); recyclerView.setAdapter(mAdapter); - setUpBirthdayListener(); return view; } From c032159583deceac449614c833d34f17ded945ac Mon Sep 17 00:00:00 2001 From: Julian Rosser Date: Mon, 13 Feb 2017 20:49:41 +0000 Subject: [PATCH 38/83] Pass NavigationView to floatingActionBar to ensure animation works correctly. Remove old Toast. --- .../java/website/julianrosser/birthdays/AlarmsHelper.java | 2 -- .../birthdays/activities/BirthdayListActivity.java | 4 ++-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/website/julianrosser/birthdays/AlarmsHelper.java b/app/src/main/java/website/julianrosser/birthdays/AlarmsHelper.java index 52c3eb2..ecdf078 100644 --- a/app/src/main/java/website/julianrosser/birthdays/AlarmsHelper.java +++ b/app/src/main/java/website/julianrosser/birthdays/AlarmsHelper.java @@ -4,7 +4,6 @@ import android.app.PendingIntent; import android.content.Context; import android.content.Intent; -import android.widget.Toast; import website.julianrosser.birthdays.recievers.NotificationBuilderReceiver; import website.julianrosser.birthdays.services.SetAlarmsService; @@ -14,7 +13,6 @@ public class AlarmsHelper { public static void setAllNotificationAlarms(Context context) { Intent serviceIntent = new Intent(context, SetAlarmsService.class); context.startService(serviceIntent); - Toast.makeText(context, "SETTING ALARMS", Toast.LENGTH_SHORT).show(); // tod - remove } // This builds an identical PendingIntent to the alarm and cancels when diff --git a/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java b/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java index c2689c6..6ced700 100644 --- a/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java +++ b/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java @@ -601,14 +601,14 @@ public void onItemDelete(ItemOptionsFragment dialog, Birthday birthday) { itemOptionsFragment.dismiss(); FirebaseHelper.saveBirthdayChange(birthday, FirebaseHelper.FirebaseUpdate.DELETE); AlarmsHelper.cancelAlarm(this, birthday.hashCode()); - SnackBarHelper.birthdayDeleted(navigationView, birthday); + SnackBarHelper.birthdayDeleted(floatingActionButton, birthday); } @Override public void onItemToggleAlarm(ItemOptionsFragment dialog, Birthday birthday) { birthday.toggleReminder(); FirebaseHelper.saveBirthdayChange(birthday, FirebaseHelper.FirebaseUpdate.UPDATE); - SnackBarHelper.alarmToggle(navigationView, birthday); + SnackBarHelper.alarmToggle(floatingActionButton, birthday); itemOptionsFragment.dismiss(); } From 4dee0307145900ed4aa8046364f48a6ecb48f602 Mon Sep 17 00:00:00 2001 From: Julian Rosser Date: Mon, 13 Feb 2017 21:25:33 +0000 Subject: [PATCH 39/83] =?UTF-8?q?Unimportant=20change=20(=C2=AC=5F=C2=AC)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle | 3 +++ .../birthdays/activities/BirthdayListActivity.java | 3 ++- gradle.properties | 4 +++- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 5ce710a..a61ca74 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -18,6 +18,9 @@ android { proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' lintOptions { disable "MissingTranslation" } } + buildTypes.each { + it.buildConfigField 'String', 'GOOGLE_SIGN_IN_KEY', GOOGLE_SIGN_IN_KEY + } } packagingOptions { exclude 'META-INF/DEPENDENCIES' diff --git a/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java b/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java index 6ced700..ec17901 100644 --- a/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java +++ b/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java @@ -56,6 +56,7 @@ import website.julianrosser.birthdays.AlarmsHelper; import website.julianrosser.birthdays.BirthdayReminder; +import website.julianrosser.birthdays.BuildConfig; import website.julianrosser.birthdays.Constants; import website.julianrosser.birthdays.R; import website.julianrosser.birthdays.database.FirebaseHelper; @@ -336,7 +337,7 @@ private GoogleSignInOptions setUpGoogleSignInOptions() { // Configure sign-in to request the user's ID, email address, and basic // profile. ID and basic profile are included in DEFAULT_SIGN_IN. GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN) - .requestIdToken(Constants.GOOGLE_SIGN_IN_KEY) + .requestIdToken(BuildConfig.GOOGLE_SIGN_IN_KEY) .requestEmail() .build(); diff --git a/gradle.properties b/gradle.properties index 1d3591c..3ff7b78 100644 --- a/gradle.properties +++ b/gradle.properties @@ -15,4 +15,6 @@ # When configured, Gradle will run in incubating parallel mode. # This option should only be used with decoupled projects. More details, visit # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects -# org.gradle.parallel=true \ No newline at end of file +# org.gradle.parallel=true + +GOOGLE_SIGN_IN_KEY = "1072707269724-3v8qbbu86kmfs252eu44amna8cpqqj9c.apps.googleusercontent.com"; \ No newline at end of file From afcd29146faa596a024d44f83dacdda1255b16db Mon Sep 17 00:00:00 2001 From: Julian Rosser Date: Tue, 21 Mar 2017 22:28:08 +0000 Subject: [PATCH 40/83] Legacy JSON data storage working alongside Firebase data. Moved Birthdays Array to BirthdayViewAdapter class. --- .../julianrosser/birthdays/Preferences.java | 23 ++ .../activities/BirthdayListActivity.java | 15 +- .../adapter/BirthdayViewAdapter.java | 3 + .../birthdays/adapter/ContactAdapter.java | 4 +- .../birthdays/database/DatabaseHelper.java | 218 ++++++++++++++++++ .../birthdays/database/FirebaseHelper.java | 103 --------- .../DialogFragments/AddEditFragment.java | 7 +- .../fragments/ImportContactFragment.java | 2 +- .../fragments/RecyclerListFragment.java | 75 +++++- .../birthdays/model/Birthday.java | 8 + .../birthdays/services/SetAlarmsService.java | 28 +-- .../views/viewholder/BirthdayViewHolder.java | 4 +- 12 files changed, 350 insertions(+), 140 deletions(-) create mode 100644 app/src/main/java/website/julianrosser/birthdays/Preferences.java create mode 100644 app/src/main/java/website/julianrosser/birthdays/database/DatabaseHelper.java delete mode 100644 app/src/main/java/website/julianrosser/birthdays/database/FirebaseHelper.java diff --git a/app/src/main/java/website/julianrosser/birthdays/Preferences.java b/app/src/main/java/website/julianrosser/birthdays/Preferences.java new file mode 100644 index 0000000..5036a6d --- /dev/null +++ b/app/src/main/java/website/julianrosser/birthdays/Preferences.java @@ -0,0 +1,23 @@ +package website.julianrosser.birthdays; + +import android.content.Context; +import android.content.SharedPreferences; + +public class Preferences { + + private static final String SHARED_PREFERENCE_KEY = "BirthdayReminderPreferences"; + private static final String KEY_USING_FIREBASE = "USING_FIREBASE"; + + public static synchronized void setIsUsingFirebase(Context context, boolean usingFirebase) { + SharedPreferences prefs = context.getSharedPreferences(SHARED_PREFERENCE_KEY, 0); + SharedPreferences.Editor editor = prefs.edit(); + editor.putBoolean(KEY_USING_FIREBASE, usingFirebase); + editor.commit(); + } + + public static synchronized boolean isUsingFirebase(Context context) { + SharedPreferences prefs = context.getSharedPreferences(SHARED_PREFERENCE_KEY, 0); + return prefs.getBoolean(KEY_USING_FIREBASE, false); + } + +} diff --git a/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java b/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java index ec17901..8ae8a58 100644 --- a/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java +++ b/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java @@ -58,8 +58,9 @@ import website.julianrosser.birthdays.BirthdayReminder; import website.julianrosser.birthdays.BuildConfig; import website.julianrosser.birthdays.Constants; +import website.julianrosser.birthdays.Preferences; import website.julianrosser.birthdays.R; -import website.julianrosser.birthdays.database.FirebaseHelper; +import website.julianrosser.birthdays.database.DatabaseHelper; import website.julianrosser.birthdays.fragments.DialogFragments.AddEditFragment; import website.julianrosser.birthdays.fragments.DialogFragments.ItemOptionsFragment; import website.julianrosser.birthdays.fragments.RecyclerListFragment; @@ -509,6 +510,10 @@ public boolean onOptionsItemSelected(MenuItem item) { if (id == R.id.action_sign_out) { signOutGoogle(); return true; + } else if (id == R.id.action_firebase) { + boolean bool = Preferences.isUsingFirebase(this); + Preferences.setIsUsingFirebase(this, ! bool); + return true; } return super.onOptionsItemSelected(item); } @@ -600,7 +605,7 @@ public void onItemEdit(ItemOptionsFragment dialog, Birthday birthday) { @Override public void onItemDelete(ItemOptionsFragment dialog, Birthday birthday) { itemOptionsFragment.dismiss(); - FirebaseHelper.saveBirthdayChange(birthday, FirebaseHelper.FirebaseUpdate.DELETE); + DatabaseHelper.saveBirthdayChange(birthday, DatabaseHelper.Update.DELETE); AlarmsHelper.cancelAlarm(this, birthday.hashCode()); SnackBarHelper.birthdayDeleted(floatingActionButton, birthday); } @@ -608,7 +613,7 @@ public void onItemDelete(ItemOptionsFragment dialog, Birthday birthday) { @Override public void onItemToggleAlarm(ItemOptionsFragment dialog, Birthday birthday) { birthday.toggleReminder(); - FirebaseHelper.saveBirthdayChange(birthday, FirebaseHelper.FirebaseUpdate.UPDATE); + DatabaseHelper.saveBirthdayChange(birthday, DatabaseHelper.Update.UPDATE); SnackBarHelper.alarmToggle(floatingActionButton, birthday); itemOptionsFragment.dismiss(); } @@ -653,8 +658,10 @@ public void launchImportContactActivity() { Intent intent = new Intent(this, ImportContactsActivity.class); Bundle bundle = new Bundle(); + ArrayList bdays = recyclerListFragment.getAdapter().getData(); + ArrayList birthdayNames = new ArrayList<>(); - for (Birthday b : birthdaysList) { + for (Birthday b : bdays) { birthdayNames.add(b.getName()); } bundle.putStringArrayList(ImportContactsActivity.BIRTHDAYS_ARRAY_KEY, birthdayNames); diff --git a/app/src/main/java/website/julianrosser/birthdays/adapter/BirthdayViewAdapter.java b/app/src/main/java/website/julianrosser/birthdays/adapter/BirthdayViewAdapter.java index 408bd1c..34c1f3f 100644 --- a/app/src/main/java/website/julianrosser/birthdays/adapter/BirthdayViewAdapter.java +++ b/app/src/main/java/website/julianrosser/birthdays/adapter/BirthdayViewAdapter.java @@ -101,4 +101,7 @@ public int getItemCount() { return birthdays.size(); } + public ArrayList getData() { + return birthdays; + } } \ No newline at end of file diff --git a/app/src/main/java/website/julianrosser/birthdays/adapter/ContactAdapter.java b/app/src/main/java/website/julianrosser/birthdays/adapter/ContactAdapter.java index 443a59e..1dd31e3 100644 --- a/app/src/main/java/website/julianrosser/birthdays/adapter/ContactAdapter.java +++ b/app/src/main/java/website/julianrosser/birthdays/adapter/ContactAdapter.java @@ -8,7 +8,7 @@ import java.util.ArrayList; import website.julianrosser.birthdays.R; -import website.julianrosser.birthdays.database.FirebaseHelper; +import website.julianrosser.birthdays.database.DatabaseHelper; import website.julianrosser.birthdays.model.Birthday; import website.julianrosser.birthdays.model.Contact; import website.julianrosser.birthdays.views.viewholder.ContactViewHolder; @@ -64,6 +64,6 @@ public void setData(ArrayList contactsList) { @Override public void addContact(Contact contact) { Birthday contactBirthday = new Birthday(contact.getName(), contact.getBirthday(), true, contact.hasYear()); - FirebaseHelper.saveBirthdayChange(contactBirthday, FirebaseHelper.FirebaseUpdate.CREATE); + DatabaseHelper.saveBirthdayChange(contactBirthday, DatabaseHelper.Update.CREATE); } } \ No newline at end of file diff --git a/app/src/main/java/website/julianrosser/birthdays/database/DatabaseHelper.java b/app/src/main/java/website/julianrosser/birthdays/database/DatabaseHelper.java new file mode 100644 index 0000000..01cc000 --- /dev/null +++ b/app/src/main/java/website/julianrosser/birthdays/database/DatabaseHelper.java @@ -0,0 +1,218 @@ +package website.julianrosser.birthdays.database; + +import android.content.Context; +import android.util.Log; +import android.widget.Toast; + +import com.google.firebase.auth.FirebaseUser; +import com.google.firebase.database.DataSnapshot; +import com.google.firebase.database.DatabaseError; +import com.google.firebase.database.DatabaseReference; +import com.google.firebase.database.ServerValue; +import com.google.firebase.database.ValueEventListener; + +import org.greenrobot.eventbus.EventBus; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONTokener; + +import java.io.BufferedReader; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.Writer; +import java.util.ArrayList; + +import website.julianrosser.birthdays.AlarmsHelper; +import website.julianrosser.birthdays.BirthdayReminder; +import website.julianrosser.birthdays.Constants; +import website.julianrosser.birthdays.Preferences; +import website.julianrosser.birthdays.Utils; +import website.julianrosser.birthdays.model.Birthday; +import website.julianrosser.birthdays.model.FirebaseBirthday; +import website.julianrosser.birthdays.model.events.BirthdaysLoadedEvent; + +public class DatabaseHelper { + + public interface BirthdaysLoadedListener { + void onBirthdaysReturned(ArrayList birthdays); + void onCancelled(String errorMessage); + } + + public static void loadFirebaseBirthdays(final BirthdaysLoadedListener birthdaysLoadedListener) { + FirebaseUser user = BirthdayReminder.getInstance().getCurrentUser(); + if (null == user) { + Log.i(DatabaseHelper.class.getSimpleName(), "User not loaded yet"); + return; + } + // load birthdays from FB + final DatabaseReference databaseReference = BirthdayReminder.getInstance().getDatabaseReference().child(user.getUid()).child(Constants.TABLE_BIRTHDAYS); + if (databaseReference == null) { + Log.i(DatabaseHelper.class.getSimpleName(), "Database not initialised yet"); + return; + } + databaseReference.addListenerForSingleValueEvent(new ValueEventListener() { + @Override + public void onDataChange(DataSnapshot dataSnapshot) { + ArrayList birthdays = new ArrayList<>(); + for (DataSnapshot birthdaySnap : dataSnapshot.getChildren()) { + FirebaseBirthday firebaseBirthday = birthdaySnap.getValue(FirebaseBirthday.class); + Birthday birthday = Birthday.fromFB(firebaseBirthday); + birthdays.add(birthday); + } + birthdaysLoadedListener.onBirthdaysReturned(birthdays); + databaseReference.removeEventListener(this); + } + + @Override + public void onCancelled(DatabaseError databaseError) { + birthdaysLoadedListener.onCancelled(databaseError.getMessage()); + databaseReference.removeEventListener(this); + } + }); + } + + public static void saveBirthdayChange(Birthday birthday, Update state) { + if (Preferences.isUsingFirebase(BirthdayReminder.getInstance())) { + saveFirebaseChange(birthday, state); + } else { + try { + saveJSONChange(birthday, state); + } catch (IOException e) { + e.printStackTrace(); + Toast.makeText(BirthdayReminder.getInstance(), "ERROR saving data, try signing in!", Toast.LENGTH_SHORT).show(); + } + } + } + + private static void saveFirebaseChange(Birthday birthday, Update state) { + FirebaseUser user = BirthdayReminder.getInstance().getCurrentUser(); + if (user == null || Utils.isStringEmpty(user.getUid())) { + return; + } + DatabaseReference databaseReference = BirthdayReminder.getInstance().getDatabaseReference(); + databaseReference = databaseReference.child(user.getUid()).child(Constants.TABLE_BIRTHDAYS) + .child(String.valueOf(birthday.getUID())); + + FirebaseBirthday firebaseBirthday = FirebaseBirthday.convertToFirebaseBirthday(birthday); + + switch (state) { + case CREATE: + databaseReference.setValue(firebaseBirthday); + break; + case UPDATE: + databaseReference.setValue(firebaseBirthday); + break; + case DELETE: + databaseReference.setValue(null); + break; + } + setLastUpdatedTime(); + AlarmsHelper.setAllNotificationAlarms(BirthdayReminder.getInstance()); + } + + private static void setLastUpdatedTime() { + FirebaseUser user = BirthdayReminder.getInstance().getCurrentUser(); + if (user != null) { + DatabaseReference dbr = BirthdayReminder.getInstance().getDatabaseReference().child(user.getUid()); + dbr.child("lastUpdated").setValue(ServerValue.TIMESTAMP); + dbr.child("email").setValue(user.getEmail()); // todo refactor + } + } + + private static void saveJSONChange(Birthday birthday, Update state) throws IOException { + ArrayList birthdays = new ArrayList<>(); + Context context = BirthdayReminder.getInstance(); + + // Load birthdays + BufferedReader reader = null; + try { + // Open and read the file into a StringBuilder + InputStream in = context.openFileInput(Constants.FILENAME); + reader = new BufferedReader(new InputStreamReader(in)); + StringBuilder jsonString = new StringBuilder(); + String line; + while ((line = reader.readLine()) != null) { + // Line breaks are omitted and irrelevant + jsonString.append(line); + } + // Parse the JSON using JSONTokener + JSONArray array = (JSONArray) new JSONTokener(jsonString.toString()) + .nextValue(); + + // Build the array of birthdays from JSONObjects + for (int i = 0; i < array.length(); i++) { + birthdays.add(new Birthday(array.getJSONObject(i))); + } + } catch (FileNotFoundException e) { + // Ignore this one; it happens when starting fresh + } catch (JSONException e) { + e.printStackTrace(); + } finally { + if (reader != null) reader.close(); + } + + // Perform action + switch (state) { + case CREATE: + birthdays.add(birthday); + break; + case UPDATE: + for (Birthday b : birthdays) { + if (b.getUID().equals(birthday.getUID())) { + birthdays.remove(b); + birthdays.add(birthday); + break; + } + } + break; + case DELETE: + for (int i = 0; i < 5; i++) { + if (birthdays.get(i).getUID().equals(birthday.getUID())) { + birthdays.remove(i); + break; + } + } + break; + } + + // Save JSON data + try { + // Build an array in JSON + JSONArray array = new JSONArray(); + for (Birthday b : birthdays) + array.put(b.toJSON()); + + // Write the file to disk + Writer writer = null; + try { + OutputStream out = context.openFileOutput(Constants.FILENAME, + Context.MODE_PRIVATE); + writer = new OutputStreamWriter(out); + writer.write(array.toString()); + + } finally { + if (writer != null) + writer.close(); + } + + } catch (JSONException | IOException e) { + e.printStackTrace(); + } + + EventBus.getDefault().post(new BirthdaysLoadedEvent(birthdays)); + + // Set alarms + AlarmsHelper.setAllNotificationAlarms(BirthdayReminder.getInstance()); + } + + public enum Update { + CREATE, + UPDATE, + DELETE, + } + +} diff --git a/app/src/main/java/website/julianrosser/birthdays/database/FirebaseHelper.java b/app/src/main/java/website/julianrosser/birthdays/database/FirebaseHelper.java deleted file mode 100644 index e99bd48..0000000 --- a/app/src/main/java/website/julianrosser/birthdays/database/FirebaseHelper.java +++ /dev/null @@ -1,103 +0,0 @@ -package website.julianrosser.birthdays.database; - -import android.util.Log; - -import com.google.firebase.auth.FirebaseUser; -import com.google.firebase.database.DataSnapshot; -import com.google.firebase.database.DatabaseError; -import com.google.firebase.database.DatabaseReference; -import com.google.firebase.database.ServerValue; -import com.google.firebase.database.ValueEventListener; - -import java.util.ArrayList; - -import website.julianrosser.birthdays.AlarmsHelper; -import website.julianrosser.birthdays.BirthdayReminder; -import website.julianrosser.birthdays.Constants; -import website.julianrosser.birthdays.Utils; -import website.julianrosser.birthdays.model.Birthday; -import website.julianrosser.birthdays.model.FirebaseBirthday; - -public class FirebaseHelper { - - public interface BirthdaysLoadedListener { - void onBirthdaysReturned(ArrayList birthdays); - void onCancelled(String errorMessage); - } - - public static void loadFirebaseBirthdays(final BirthdaysLoadedListener birthdaysLoadedListener) { - FirebaseUser user = BirthdayReminder.getInstance().getCurrentUser(); - if (null == user) { - Log.i(FirebaseHelper.class.getSimpleName(), "User not loaded yet"); - return; - } - // load birthdays from FB - final DatabaseReference databaseReference = BirthdayReminder.getInstance().getDatabaseReference().child(user.getUid()).child(Constants.TABLE_BIRTHDAYS); - if (databaseReference == null) { - Log.i(FirebaseHelper.class.getSimpleName(), "Database not initialised yet"); - return; - } - databaseReference.addListenerForSingleValueEvent(new ValueEventListener() { - @Override - public void onDataChange(DataSnapshot dataSnapshot) { - ArrayList birthdays = new ArrayList<>(); - for (DataSnapshot birthdaySnap : dataSnapshot.getChildren()) { - FirebaseBirthday firebaseBirthday = birthdaySnap.getValue(FirebaseBirthday.class); - Birthday birthday = Birthday.fromFB(firebaseBirthday); - birthdays.add(birthday); - } - birthdaysLoadedListener.onBirthdaysReturned(birthdays); - databaseReference.removeEventListener(this); - } - - @Override - public void onCancelled(DatabaseError databaseError) { - birthdaysLoadedListener.onCancelled(databaseError.getMessage()); - databaseReference.removeEventListener(this); - } - }); - } - - public static void saveBirthdayChange(Birthday birthday, FirebaseUpdate state) { - - FirebaseUser user = BirthdayReminder.getInstance().getCurrentUser(); - if (user == null || Utils.isStringEmpty(user.getUid())) { - return; - } - DatabaseReference databaseReference = BirthdayReminder.getInstance().getDatabaseReference(); - databaseReference = databaseReference.child(user.getUid()).child(Constants.TABLE_BIRTHDAYS) - .child(String.valueOf(birthday.getUID())); - - FirebaseBirthday firebaseBirthday = FirebaseBirthday.convertToFirebaseBirthday(birthday); - - switch (state) { - case CREATE: - databaseReference.setValue(firebaseBirthday); - break; - case UPDATE: - databaseReference.setValue(firebaseBirthday); - break; - case DELETE: - databaseReference.setValue(null); - break; - } - setLastUpdatedTime(); - AlarmsHelper.setAllNotificationAlarms(BirthdayReminder.getInstance()); - } - - private static void setLastUpdatedTime() { - FirebaseUser user = BirthdayReminder.getInstance().getCurrentUser(); - if (user != null) { - DatabaseReference dbr = BirthdayReminder.getInstance().getDatabaseReference().child(user.getUid()); - dbr.child("lastUpdated").setValue(ServerValue.TIMESTAMP); - dbr.child("email").setValue(user.getEmail()); - } - } - - public enum FirebaseUpdate { - CREATE, - UPDATE, - DELETE, - } - -} diff --git a/app/src/main/java/website/julianrosser/birthdays/fragments/DialogFragments/AddEditFragment.java b/app/src/main/java/website/julianrosser/birthdays/fragments/DialogFragments/AddEditFragment.java index c1d9427..292c432 100644 --- a/app/src/main/java/website/julianrosser/birthdays/fragments/DialogFragments/AddEditFragment.java +++ b/app/src/main/java/website/julianrosser/birthdays/fragments/DialogFragments/AddEditFragment.java @@ -32,7 +32,7 @@ import website.julianrosser.birthdays.Constants; import website.julianrosser.birthdays.R; import website.julianrosser.birthdays.Utils; -import website.julianrosser.birthdays.database.FirebaseHelper; +import website.julianrosser.birthdays.database.DatabaseHelper; import website.julianrosser.birthdays.model.Birthday; public class AddEditFragment extends DialogFragment { @@ -246,9 +246,10 @@ public void onClick(View v) { String key = bundle.getString(UID_KEY); if (! Utils.isStringEmpty(key)) birthday.setUID(key); AlarmsHelper.cancelAlarm(getActivity(), birthday.hashCode()); - FirebaseHelper.saveBirthdayChange(birthday, FirebaseHelper.FirebaseUpdate.UPDATE); + DatabaseHelper.saveBirthdayChange(birthday, DatabaseHelper.Update.UPDATE); } else if (ADD_OR_EDIT_MODE == MODE_ADD) { - FirebaseHelper.saveBirthdayChange(birthday, FirebaseHelper.FirebaseUpdate.CREATE); + DatabaseHelper.saveBirthdayChange(birthday, DatabaseHelper.Update.CREATE); + } // Finally close the dialog, and breath a sign of relief dialog.dismiss(); diff --git a/app/src/main/java/website/julianrosser/birthdays/fragments/ImportContactFragment.java b/app/src/main/java/website/julianrosser/birthdays/fragments/ImportContactFragment.java index 82e37f4..30e0012 100644 --- a/app/src/main/java/website/julianrosser/birthdays/fragments/ImportContactFragment.java +++ b/app/src/main/java/website/julianrosser/birthdays/fragments/ImportContactFragment.java @@ -23,7 +23,7 @@ import website.julianrosser.birthdays.model.tasks.LoadContactsTask; /** - * Main view. Fragment which + * Fragment which displays contacts and enables users to import */ public class ImportContactFragment extends android.support.v4.app.Fragment { diff --git a/app/src/main/java/website/julianrosser/birthdays/fragments/RecyclerListFragment.java b/app/src/main/java/website/julianrosser/birthdays/fragments/RecyclerListFragment.java index 3aeffcb..3beb43f 100644 --- a/app/src/main/java/website/julianrosser/birthdays/fragments/RecyclerListFragment.java +++ b/app/src/main/java/website/julianrosser/birthdays/fragments/RecyclerListFragment.java @@ -19,14 +19,23 @@ import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.Subscribe; - +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONTokener; + +import java.io.BufferedReader; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; import java.util.ArrayList; import website.julianrosser.birthdays.BirthdayReminder; import website.julianrosser.birthdays.Constants; +import website.julianrosser.birthdays.Preferences; import website.julianrosser.birthdays.R; import website.julianrosser.birthdays.adapter.BirthdayViewAdapter; -import website.julianrosser.birthdays.database.FirebaseHelper; +import website.julianrosser.birthdays.database.DatabaseHelper; import website.julianrosser.birthdays.model.Birthday; import website.julianrosser.birthdays.model.FirebaseBirthday; import website.julianrosser.birthdays.model.events.BirthdaysLoadedEvent; @@ -101,30 +110,42 @@ public void onStart() { public void onStop() { super.onStop(); EventBus.getDefault().unregister(this); - databaseReference.removeEventListener(loadBirthdaysEventListener); - loadBirthdaysEventListener = null; + if (loadBirthdaysEventListener != null) { + databaseReference.removeEventListener(loadBirthdaysEventListener); + loadBirthdaysEventListener = null; + } } @Override public void onResume() { super.onResume(); - loadBirthdays(); + if (Preferences.isUsingFirebase(getActivity())) { + loadBirthdays(); + } } public void loadBirthdays() { - setUpBirthdayListener(); + if (Preferences.isUsingFirebase(getActivity())) { + setUpBirthdayListener(); + } else { + try { + loadJSONData(); + } catch (IOException e) { + e.printStackTrace(); + } + } } private void setUpBirthdayListener() { FirebaseUser user = BirthdayReminder.getInstance().getCurrentUser(); if (null == user) { - Log.i(FirebaseHelper.class.getSimpleName(), "User not loaded yet"); + Log.i(DatabaseHelper.class.getSimpleName(), "User not loaded yet"); return; } // load birthdays from FB databaseReference = BirthdayReminder.getInstance().getDatabaseReference().child(user.getUid()).child(Constants.TABLE_BIRTHDAYS); if (databaseReference == null) { - Log.i(FirebaseHelper.class.getSimpleName(), "Database not loaded yet"); + Log.i(DatabaseHelper.class.getSimpleName(), "Database not loaded yet"); return; } if (loadBirthdaysEventListener == null) { @@ -150,6 +171,40 @@ public void onCancelled(DatabaseError databaseError) { databaseReference.addValueEventListener(loadBirthdaysEventListener); } + private void loadJSONData() throws IOException { // todo - refactor + ArrayList birthdays = new ArrayList<>(); + // Load birthdays + BufferedReader reader = null; + try { + // Open and read the file into a StringBuilder + InputStream in = getContext().openFileInput(Constants.FILENAME); + reader = new BufferedReader(new InputStreamReader(in)); + StringBuilder jsonString = new StringBuilder(); + String line; + while ((line = reader.readLine()) != null) { + // Line breaks are omitted and irrelevant + jsonString.append(line); + } + // Parse the JSON using JSONTokener + JSONArray array = (JSONArray) new JSONTokener(jsonString.toString()) + .nextValue(); + + // Build the array of birthdays from JSONObjects + for (int i = 0; i < array.length(); i++) { + birthdays.add(new Birthday(array.getJSONObject(i))); + } + } catch (FileNotFoundException e) { + // Ignore this one; it happens when starting fresh + } catch (JSONException e) { + e.printStackTrace(); + } finally { + if (reader != null) reader.close(); + } + mAdapter.setData(birthdays); + showEmptyMessageIfRequired(birthdays); + + } + @Subscribe public void onBirthdaysLoaded(BirthdaysLoadedEvent event) { mAdapter.setData(event.getBirthdays()); @@ -164,4 +219,8 @@ private void showEmptyMessageIfRequired(ArrayList birthdays) { emptyView.setVisibility(View.INVISIBLE); } } + + public BirthdayViewAdapter getAdapter() { + return mAdapter; + } } diff --git a/app/src/main/java/website/julianrosser/birthdays/model/Birthday.java b/app/src/main/java/website/julianrosser/birthdays/model/Birthday.java index d8526bd..11f7e85 100644 --- a/app/src/main/java/website/julianrosser/birthdays/model/Birthday.java +++ b/app/src/main/java/website/julianrosser/birthdays/model/Birthday.java @@ -29,6 +29,7 @@ public class Birthday { private static final String JSON_DATE = "date"; private static final String JSON_YEAR = "year"; private static final String JSON_REMIND = "remind"; + private static final String JSON_UID = "uid"; private static final String JSON_SHOW_YEAR = "show_year"; private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat( @@ -82,6 +83,12 @@ public Birthday(JSONObject json) throws JSONException { } else { yearOfBirth = Constants.DEFAULT_YEAR_OF_BIRTH; } + + // UID + if (json.has(JSON_UID)) { + uID = json.getString(JSON_UID); + } + // Should use age? showYear = json.has(JSON_SHOW_YEAR) && json.getBoolean(JSON_SHOW_YEAR); } @@ -95,6 +102,7 @@ public JSONObject toJSON() throws JSONException { json.put(JSON_DATE, this.getDate().getTime()); json.put(JSON_YEAR, this.getYear()); json.put(JSON_REMIND, this.getRemind()); + json.put(JSON_UID, this.getUID()); json.put(JSON_SHOW_YEAR, this.shouldIncludeYear()); return json; } diff --git a/app/src/main/java/website/julianrosser/birthdays/services/SetAlarmsService.java b/app/src/main/java/website/julianrosser/birthdays/services/SetAlarmsService.java index 45e5bd0..9f2c53e 100644 --- a/app/src/main/java/website/julianrosser/birthdays/services/SetAlarmsService.java +++ b/app/src/main/java/website/julianrosser/birthdays/services/SetAlarmsService.java @@ -1,7 +1,6 @@ package website.julianrosser.birthdays.services; import android.app.AlarmManager; -import android.app.Application; import android.app.PendingIntent; import android.app.Service; import android.content.Intent; @@ -24,10 +23,10 @@ import java.util.ArrayList; import java.util.Date; -import website.julianrosser.birthdays.BirthdayReminder; import website.julianrosser.birthdays.Constants; +import website.julianrosser.birthdays.Preferences; import website.julianrosser.birthdays.R; -import website.julianrosser.birthdays.database.FirebaseHelper; +import website.julianrosser.birthdays.database.DatabaseHelper; import website.julianrosser.birthdays.model.Birthday; import website.julianrosser.birthdays.recievers.NotificationBuilderReceiver; @@ -37,7 +36,6 @@ public class SetAlarmsService extends Service { private AlarmManager mAlarmManager; - private Application mContext; @Nullable @Override @@ -49,9 +47,7 @@ public IBinder onBind(Intent intent) { public void onCreate() { super.onCreate(); - mContext = BirthdayReminder.getInstance(); - - if (true) { // todo 3.0 - switch JSON / FB + if (Preferences.isUsingFirebase(this)) { loadBirthdaysFromFirebase(); } else { try { @@ -81,12 +77,11 @@ private void onBirthdaysLoaded(ArrayList birthdays) { } } // Service has to control its own life cycles, so call stopSelf here - mContext = null; stopSelf(); } private void loadBirthdaysFromFirebase() { - FirebaseHelper.loadFirebaseBirthdays(new FirebaseHelper.BirthdaysLoadedListener() { + DatabaseHelper.loadFirebaseBirthdays(new DatabaseHelper.BirthdaysLoadedListener() { @Override public void onBirthdaysReturned(ArrayList birthdays) { onBirthdaysLoaded(birthdays); @@ -94,9 +89,8 @@ public void onBirthdaysReturned(ArrayList birthdays) { @Override public void onCancelled(String errorMessage) { - Toast.makeText(mContext, errorMessage, Toast.LENGTH_SHORT).show(); + Toast.makeText(getApplicationContext(), errorMessage, Toast.LENGTH_SHORT).show(); Log.i(getClass().getSimpleName(), errorMessage); - mContext = null; stopSelf(); } }); @@ -109,7 +103,7 @@ private void loadBirthdaysFromJSON() throws IOException, BufferedReader reader = null; try { // Open and read the file into a StringBuilder - InputStream in = mContext.openFileInput(Constants.FILENAME); + InputStream in = openFileInput(Constants.FILENAME); reader = new BufferedReader(new InputStreamReader(in)); StringBuilder jsonString = new StringBuilder(); String line; @@ -169,19 +163,19 @@ private void setAlarm(Birthday birthday) { int id = birthday.getName().hashCode(); // CreateIntent to start the AlarmNotificationReceiver - Intent mNotificationReceiverIntent = new Intent(mContext, + Intent mNotificationReceiverIntent = new Intent(this, NotificationBuilderReceiver.class); // Build message String - String messageString = "" + birthday.getName() + "'s " + mContext.getResources().getString(R.string.birthday) - + " " + mContext.getResources().getString(R.string.date_is) + " " + - Birthday.getFormattedStringDay(birthday, mContext); + String messageString = "" + birthday.getName() + "'s " + getResources().getString(R.string.birthday) + + " " + getResources().getString(R.string.date_is) + " " + + Birthday.getFormattedStringDay(birthday, this); mNotificationReceiverIntent.putExtra(NotificationBuilderReceiver.STRING_MESSAGE_KEY, messageString); // Create pending Intent using Intent we just built PendingIntent mNotificationReceiverPendingIntent = PendingIntent - .getBroadcast(mContext, id, + .getBroadcast(this, id, mNotificationReceiverIntent, PendingIntent.FLAG_UPDATE_CURRENT); diff --git a/app/src/main/java/website/julianrosser/birthdays/views/viewholder/BirthdayViewHolder.java b/app/src/main/java/website/julianrosser/birthdays/views/viewholder/BirthdayViewHolder.java index 67be49d..2672e8c 100644 --- a/app/src/main/java/website/julianrosser/birthdays/views/viewholder/BirthdayViewHolder.java +++ b/app/src/main/java/website/julianrosser/birthdays/views/viewholder/BirthdayViewHolder.java @@ -9,7 +9,7 @@ import org.greenrobot.eventbus.EventBus; import website.julianrosser.birthdays.R; -import website.julianrosser.birthdays.database.FirebaseHelper; +import website.julianrosser.birthdays.database.DatabaseHelper; import website.julianrosser.birthdays.model.Birthday; import website.julianrosser.birthdays.model.events.BirthdayItemClickEvent; import website.julianrosser.birthdays.views.SnackBarHelper; @@ -97,7 +97,7 @@ public void onClick(View view) { if (id == R.id.alarmImage) { birthday.toggleReminder(); - FirebaseHelper.saveBirthdayChange(birthday, FirebaseHelper.FirebaseUpdate.UPDATE); + DatabaseHelper.saveBirthdayChange(birthday, DatabaseHelper.Update.UPDATE); SnackBarHelper.alarmToggle(view, birthday); } else { // Callback From 39b007b3c548e020a2b80e39fa121b1dc3645fd6 Mon Sep 17 00:00:00 2001 From: Jules Date: Mon, 29 Jan 2018 19:32:45 +0000 Subject: [PATCH 41/83] add google-services.json file to gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 15f6078..d7f5992 100644 --- a/.gitignore +++ b/.gitignore @@ -42,3 +42,5 @@ Thumbs.db .gradle build/ *.iml + +app/google-services\.json From f7fbaa3f66f4f5921e6631b5ebfb87a53412ec92 Mon Sep 17 00:00:00 2001 From: Jules Date: Mon, 29 Jan 2018 19:33:38 +0000 Subject: [PATCH 42/83] make switching debug DB switching clearer --- .../birthdays/activities/BirthdayListActivity.java | 3 +++ app/src/main/res/menu/menu_main.xml | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java b/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java index 8ae8a58..0fd254a 100644 --- a/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java +++ b/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java @@ -17,6 +17,7 @@ import android.support.v7.app.ActionBarDrawerToggle; import android.support.v7.widget.Toolbar; import android.util.Log; +import android.view.Gravity; import android.view.Menu; import android.view.MenuItem; import android.view.View; @@ -513,6 +514,7 @@ public boolean onOptionsItemSelected(MenuItem item) { } else if (id == R.id.action_firebase) { boolean bool = Preferences.isUsingFirebase(this); Preferences.setIsUsingFirebase(this, ! bool); + Toast.makeText(this, "DB mode: " +( !bool ? "Firebase" : "JSON"), Toast.LENGTH_SHORT).show(); return true; } return super.onOptionsItemSelected(item); @@ -575,6 +577,7 @@ public void onClick(View v) { } private void signIn() { + mDrawerLayout.closeDrawer(Gravity.START); Intent signInIntent = Auth.GoogleSignInApi.getSignInIntent(mGoogleApiClient); startActivityForResult(signInIntent, RC_SIGN_IN); } diff --git a/app/src/main/res/menu/menu_main.xml b/app/src/main/res/menu/menu_main.xml index ffaa238..9881eb2 100644 --- a/app/src/main/res/menu/menu_main.xml +++ b/app/src/main/res/menu/menu_main.xml @@ -10,6 +10,6 @@ + android:title="SWITCH DB MODE" /> From a3a3f683972c0a33e05e1f44c88588d8d5c754ef Mon Sep 17 00:00:00 2001 From: Jules Date: Mon, 29 Jan 2018 19:34:00 +0000 Subject: [PATCH 43/83] update gradle plugin --- BirthdayReminder.iml | 4 ++-- build.gradle | 2 +- gradle/wrapper/gradle-wrapper.properties | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/BirthdayReminder.iml b/BirthdayReminder.iml index 7786044..b6d34a2 100644 --- a/BirthdayReminder.iml +++ b/BirthdayReminder.iml @@ -1,5 +1,5 @@ - + @@ -13,7 +13,7 @@ - + \ No newline at end of file diff --git a/build.gradle b/build.gradle index 7dd9d2d..8760f10 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:2.2.2' + classpath 'com.android.tools.build:gradle:3.0.1' classpath 'com.google.gms:google-services:3.0.0' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index f47dd9b..5dcc71b 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Wed Oct 05 18:31:51 BST 2016 +#Mon Jan 29 19:02:31 GMT 2018 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip From 4e55ea74b2a0be0813a7755758348e91c9733b2f Mon Sep 17 00:00:00 2001 From: Jules Date: Mon, 29 Jan 2018 20:56:18 +0000 Subject: [PATCH 44/83] full logout, show loading indicator --- .../activities/BirthdayListActivity.java | 23 ++++++------------- .../fragments/RecyclerListFragment.java | 8 +++++++ app/src/main/res/layout/fragment_main.xml | 7 ++++++ 3 files changed, 22 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java b/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java index 0fd254a..658b686 100644 --- a/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java +++ b/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java @@ -258,7 +258,7 @@ public void onActivityResult(int requestCode, int resultCode, Intent data) { } else if (requestCode == RC_SETTINGS) { SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); String newTheme = prefs.getString(getString(R.string.pref_theme_key), "0"); - if (! newTheme.equals(themePref)) { + if (!newTheme.equals(themePref)) { recreate(); themePref = newTheme; } @@ -295,6 +295,7 @@ public void onComplete(@NonNull Task task) { Toast.makeText(BirthdayListActivity.this, "Authentication failed.", Toast.LENGTH_SHORT).show(); } + recyclerListFragment.loadBirthdays(); } }); } @@ -513,31 +514,21 @@ public boolean onOptionsItemSelected(MenuItem item) { return true; } else if (id == R.id.action_firebase) { boolean bool = Preferences.isUsingFirebase(this); - Preferences.setIsUsingFirebase(this, ! bool); - Toast.makeText(this, "DB mode: " +( !bool ? "Firebase" : "JSON"), Toast.LENGTH_SHORT).show(); + Preferences.setIsUsingFirebase(this, !bool); + Snackbar.make(floatingActionButton, "DB mode: " + (!bool ? "Firebase" : "JSON"), Snackbar.LENGTH_SHORT).show(); + recyclerListFragment.getAdapter().clearBirthdays(); return true; } return super.onOptionsItemSelected(item); } private void signOutGoogle() { - Auth.GoogleSignInApi.signOut(mGoogleApiClient).setResultCallback( - new ResultCallback() { - @Override - public void onResult(Status status) { - setNavHeaderUserState(NavHeaderState.LOGGED_OUT); - Snackbar.make(floatingActionButton, "signOutGoogle: " + status.getStatusMessage(), Snackbar.LENGTH_SHORT).show(); - } - }); - } - - // todo - unlink Google - private void revokeAccessGoogle() { + FirebaseAuth.getInstance().signOut(); Auth.GoogleSignInApi.revokeAccess(mGoogleApiClient).setResultCallback( new ResultCallback() { @Override public void onResult(Status status) { - Snackbar.make(floatingActionButton, "revokeAccessGoogle: " + status.getStatusMessage(), Snackbar.LENGTH_SHORT).show(); + finish(); } }); } diff --git a/app/src/main/java/website/julianrosser/birthdays/fragments/RecyclerListFragment.java b/app/src/main/java/website/julianrosser/birthdays/fragments/RecyclerListFragment.java index 3beb43f..023f012 100644 --- a/app/src/main/java/website/julianrosser/birthdays/fragments/RecyclerListFragment.java +++ b/app/src/main/java/website/julianrosser/birthdays/fragments/RecyclerListFragment.java @@ -9,6 +9,7 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.ProgressBar; import android.widget.Toast; import com.google.firebase.auth.FirebaseUser; @@ -50,6 +51,7 @@ public class RecyclerListFragment extends android.support.v4.app.Fragment { // Reference to view which shows when list empty. private View emptyView; + private ProgressBar progressBar; private ValueEventListener loadBirthdaysEventListener; private DatabaseReference databaseReference; @@ -74,6 +76,7 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, // Reference empty TextView emptyView = view.findViewById(R.id.empty_view); + progressBar = (ProgressBar) view.findViewById(R.id.progressBar); // hide drop shadow if running lollipop or higher if (Build.VERSION.SDK_INT >= 21) { @@ -125,6 +128,7 @@ public void onResume() { } public void loadBirthdays() { + progressBar.setVisibility(View.VISIBLE); if (Preferences.isUsingFirebase(getActivity())) { setUpBirthdayListener(); } else { @@ -194,13 +198,16 @@ private void loadJSONData() throws IOException { // todo - refactor birthdays.add(new Birthday(array.getJSONObject(i))); } } catch (FileNotFoundException e) { + progressBar.setVisibility(View.GONE); // Ignore this one; it happens when starting fresh } catch (JSONException e) { + progressBar.setVisibility(View.GONE); e.printStackTrace(); } finally { if (reader != null) reader.close(); } mAdapter.setData(birthdays); + progressBar.setVisibility(View.GONE); showEmptyMessageIfRequired(birthdays); } @@ -208,6 +215,7 @@ private void loadJSONData() throws IOException { // todo - refactor @Subscribe public void onBirthdaysLoaded(BirthdaysLoadedEvent event) { mAdapter.setData(event.getBirthdays()); + progressBar.setVisibility(View.GONE); showEmptyMessageIfRequired(event.getBirthdays()); } diff --git a/app/src/main/res/layout/fragment_main.xml b/app/src/main/res/layout/fragment_main.xml index 1ba8ef6..cad33da 100644 --- a/app/src/main/res/layout/fragment_main.xml +++ b/app/src/main/res/layout/fragment_main.xml @@ -35,4 +35,11 @@ android:layout_height="6dp" android:background="@drawable/toolbar_dropshadow" /> + + \ No newline at end of file From 3f0f50f83a5e2ea39c9b2af88f322b1168451d9d Mon Sep 17 00:00:00 2001 From: Jules Date: Tue, 30 Jan 2018 20:07:29 +0000 Subject: [PATCH 45/83] clear birthdays on logout --- .../activities/BirthdayListActivity.java | 19 ++++++++----------- .../adapter/BirthdayViewAdapter.java | 6 +++++- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java b/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java index 658b686..c63bae9 100644 --- a/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java +++ b/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java @@ -67,7 +67,6 @@ import website.julianrosser.birthdays.fragments.RecyclerListFragment; import website.julianrosser.birthdays.model.Birthday; import website.julianrosser.birthdays.model.events.BirthdayItemClickEvent; -import website.julianrosser.birthdays.model.events.BirthdaysLoadedEvent; import website.julianrosser.birthdays.views.CircleTransform; import website.julianrosser.birthdays.views.SnackBarHelper; @@ -77,8 +76,6 @@ public class BirthdayListActivity extends BaseActivity implements ItemOptionsFra private static final int RC_SIGN_IN = 6006; /// todo - refactor public static final int RC_SETTINGS = 5311; - private ArrayList birthdaysList = new ArrayList<>(); - // Keys for orientation change reference final String ADD_EDIT_INSTANCE_KEY = "fragment_add_edit"; final String ITEM_OPTIONS_INSTANCE_KEY = "fragment_item_options"; @@ -516,7 +513,7 @@ public boolean onOptionsItemSelected(MenuItem item) { boolean bool = Preferences.isUsingFirebase(this); Preferences.setIsUsingFirebase(this, !bool); Snackbar.make(floatingActionButton, "DB mode: " + (!bool ? "Firebase" : "JSON"), Snackbar.LENGTH_SHORT).show(); - recyclerListFragment.getAdapter().clearBirthdays(); + clearBirthdays(); return true; } return super.onOptionsItemSelected(item); @@ -528,11 +525,17 @@ private void signOutGoogle() { new ResultCallback() { @Override public void onResult(Status status) { - finish(); + setNavHeaderUserState(NavHeaderState.LOGGED_OUT); + clearBirthdays(); } }); } + public void clearBirthdays() { + recyclerListFragment.getAdapter().clearBirthdays(); + recyclerListFragment.showEmptyMessageIfRequired(new ArrayList()); + } + // Set theme based on users preference public void setTheme() { SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); @@ -668,12 +671,6 @@ public void onBirthdayClicked(BirthdayItemClickEvent event) { showItemOptionsFragment(event.getBirthday()); } - // Birthdays array required to prevent importing duplicate contacts //todo - reduce to string names - @Subscribe - public void onBirthdaysLoaded(BirthdaysLoadedEvent event) { - birthdaysList = event.getBirthdays(); - } - enum NavHeaderState { LOGGED_OUT, SIGNED_IN diff --git a/app/src/main/java/website/julianrosser/birthdays/adapter/BirthdayViewAdapter.java b/app/src/main/java/website/julianrosser/birthdays/adapter/BirthdayViewAdapter.java index 34c1f3f..0147ce3 100644 --- a/app/src/main/java/website/julianrosser/birthdays/adapter/BirthdayViewAdapter.java +++ b/app/src/main/java/website/julianrosser/birthdays/adapter/BirthdayViewAdapter.java @@ -55,6 +55,11 @@ public void onViewRecycled(BirthdayViewHolder holder) { super.onViewRecycled(holder); } + public void clearBirthdays() { + birthdays.clear(); + notifyDataSetChanged(); + } + public void setData(ArrayList birthdays) { this.birthdays = birthdays; @@ -68,7 +73,6 @@ public void setData(ArrayList birthdays) { } else { sortBirthdaysByDate(); } - notifyDataSetChanged(); } From 5f9ce11ffeb5654668854836782e41cafc3a01bb Mon Sep 17 00:00:00 2001 From: Jules Date: Tue, 30 Jan 2018 20:08:13 +0000 Subject: [PATCH 46/83] remove unecessary static object in DialogFragment --- .../fragments/DialogFragments/ItemOptionsFragment.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/website/julianrosser/birthdays/fragments/DialogFragments/ItemOptionsFragment.java b/app/src/main/java/website/julianrosser/birthdays/fragments/DialogFragments/ItemOptionsFragment.java index f9b2235..662b4de 100644 --- a/app/src/main/java/website/julianrosser/birthdays/fragments/DialogFragments/ItemOptionsFragment.java +++ b/app/src/main/java/website/julianrosser/birthdays/fragments/DialogFragments/ItemOptionsFragment.java @@ -27,7 +27,7 @@ public class ItemOptionsFragment extends DialogFragment { - private static Birthday sBirthday; + protected Birthday sBirthday; final int DIALOG_WIDTH_SIZE = 220; @@ -38,13 +38,15 @@ public ItemOptionsFragment() { // Required empty public constructor } + private void setBirthday(Birthday birthday) { + this.sBirthday = birthday; + } + // New instance is preferable as we have option to initialize here instead of using passed in params. public static ItemOptionsFragment newInstance(Birthday birthday) { ItemOptionsFragment itemOptionsFragment = new ItemOptionsFragment(); - - // Get selected birthday's title - sBirthday = birthday; + itemOptionsFragment.setBirthday(birthday); return itemOptionsFragment; } From 43f4856cf5dbd27195698fdf1e85c5b13fff9dc7 Mon Sep 17 00:00:00 2001 From: Jules Date: Tue, 30 Jan 2018 20:08:40 +0000 Subject: [PATCH 47/83] improve empty state layout --- app/src/main/res/layout/fragment_main.xml | 17 +++++++++++++---- app/src/main/res/values/strings.xml | 8 +++++--- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/app/src/main/res/layout/fragment_main.xml b/app/src/main/res/layout/fragment_main.xml index cad33da..8d4bc04 100644 --- a/app/src/main/res/layout/fragment_main.xml +++ b/app/src/main/res/layout/fragment_main.xml @@ -14,11 +14,20 @@ android:id="@+id/empty_view" android:layout_width="match_parent" android:layout_height="match_parent" - android:layout_marginTop="16dp" + android:layout_marginTop="32dp" android:orientation="vertical" android:padding="16dp" android:visibility="visible"> + + + android:layout_height="wrap_content" + android:layout_centerInParent="true" + android:visibility="gone" /> \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 45a519e..ae1718e 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -21,9 +21,6 @@ Save Cancel - - Tap the Birthday cake icon to start adding birthdays! - birthday is today @@ -158,6 +155,11 @@ Sign out + Sign In + + + No Birthdays + Tap the Birthday cake icon to add your first birthday, or sign in to your Google account to retrieve them! From 0c3e9610736028c8b068188c50940dd215518f51 Mon Sep 17 00:00:00 2001 From: Jules Date: Tue, 30 Jan 2018 20:26:37 +0000 Subject: [PATCH 48/83] remove useless variable --- .../fragments/DialogFragments/ItemOptionsFragment.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/src/main/java/website/julianrosser/birthdays/fragments/DialogFragments/ItemOptionsFragment.java b/app/src/main/java/website/julianrosser/birthdays/fragments/DialogFragments/ItemOptionsFragment.java index 662b4de..228a8c5 100644 --- a/app/src/main/java/website/julianrosser/birthdays/fragments/DialogFragments/ItemOptionsFragment.java +++ b/app/src/main/java/website/julianrosser/birthdays/fragments/DialogFragments/ItemOptionsFragment.java @@ -29,8 +29,6 @@ public class ItemOptionsFragment extends DialogFragment { protected Birthday sBirthday; - final int DIALOG_WIDTH_SIZE = 220; - // Use this instance of the interface to deliver action events static ItemOptionsListener mListener; From 02ece7fa8958c9a011ac2fcbfc09be6fa1dd924f Mon Sep 17 00:00:00 2001 From: Jules Date: Tue, 30 Jan 2018 20:27:38 +0000 Subject: [PATCH 49/83] use tools to display default data in layout preview --- app/src/main/res/layout/item_edit_fragment_list.xml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/layout/item_edit_fragment_list.xml b/app/src/main/res/layout/item_edit_fragment_list.xml index 3b7a8d7..6a8c6f9 100644 --- a/app/src/main/res/layout/item_edit_fragment_list.xml +++ b/app/src/main/res/layout/item_edit_fragment_list.xml @@ -2,7 +2,8 @@ + android:layout_height="wrap_content" + xmlns:tools="http://schemas.android.com/tools"> From fd66dcf53408e0f10644a144789169e2d264f46b Mon Sep 17 00:00:00 2001 From: Jules Date: Tue, 30 Jan 2018 20:28:14 +0000 Subject: [PATCH 50/83] ensure all error states are handled --- .../birthdays/fragments/RecyclerListFragment.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/website/julianrosser/birthdays/fragments/RecyclerListFragment.java b/app/src/main/java/website/julianrosser/birthdays/fragments/RecyclerListFragment.java index 023f012..a1f5692 100644 --- a/app/src/main/java/website/julianrosser/birthdays/fragments/RecyclerListFragment.java +++ b/app/src/main/java/website/julianrosser/birthdays/fragments/RecyclerListFragment.java @@ -144,11 +144,13 @@ private void setUpBirthdayListener() { FirebaseUser user = BirthdayReminder.getInstance().getCurrentUser(); if (null == user) { Log.i(DatabaseHelper.class.getSimpleName(), "User not loaded yet"); + handleError(); return; } // load birthdays from FB databaseReference = BirthdayReminder.getInstance().getDatabaseReference().child(user.getUid()).child(Constants.TABLE_BIRTHDAYS); if (databaseReference == null) { + handleError(); Log.i(DatabaseHelper.class.getSimpleName(), "Database not loaded yet"); return; } @@ -169,6 +171,7 @@ public void onDataChange(DataSnapshot dataSnapshot) { @Override public void onCancelled(DatabaseError databaseError) { Toast.makeText(BirthdayReminder.getInstance(), databaseError.getMessage(), Toast.LENGTH_SHORT).show(); + handleError(); } }; } @@ -219,8 +222,13 @@ public void onBirthdaysLoaded(BirthdaysLoadedEvent event) { showEmptyMessageIfRequired(event.getBirthdays()); } + public void handleError() { + progressBar.setVisibility(View.GONE); + emptyView.setVisibility(View.VISIBLE); + } + // Show or hide the 'no birthdays found' message depending on size of birthday Array - private void showEmptyMessageIfRequired(ArrayList birthdays) { + public void showEmptyMessageIfRequired(ArrayList birthdays) { if (birthdays.isEmpty()) { emptyView.setVisibility(View.VISIBLE); } else { From 0a188efabc376f03b1e260b477245a46f966b6e9 Mon Sep 17 00:00:00 2001 From: Jules Date: Tue, 30 Jan 2018 20:28:30 +0000 Subject: [PATCH 51/83] fix issue where JSON birthdays were not loaded --- .../birthdays/fragments/RecyclerListFragment.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/src/main/java/website/julianrosser/birthdays/fragments/RecyclerListFragment.java b/app/src/main/java/website/julianrosser/birthdays/fragments/RecyclerListFragment.java index a1f5692..debe6bf 100644 --- a/app/src/main/java/website/julianrosser/birthdays/fragments/RecyclerListFragment.java +++ b/app/src/main/java/website/julianrosser/birthdays/fragments/RecyclerListFragment.java @@ -122,9 +122,7 @@ public void onStop() { @Override public void onResume() { super.onResume(); - if (Preferences.isUsingFirebase(getActivity())) { - loadBirthdays(); - } + loadBirthdays(); } public void loadBirthdays() { From 2e68b00556a14f3fc2975a329f0c932fd3c87aa6 Mon Sep 17 00:00:00 2001 From: Jules Date: Tue, 30 Jan 2018 20:33:33 +0000 Subject: [PATCH 52/83] fix issue where user wasn't fully logged out --- .../julianrosser/birthdays/activities/BirthdayListActivity.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java b/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java index c63bae9..9c36fb2 100644 --- a/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java +++ b/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java @@ -527,6 +527,8 @@ private void signOutGoogle() { public void onResult(Status status) { setNavHeaderUserState(NavHeaderState.LOGGED_OUT); clearBirthdays(); + BirthdayReminder.getInstance().setUser(null); + } }); } From b690ce0d51250bf8c646aad7cb6e73653164e832 Mon Sep 17 00:00:00 2001 From: Jules Date: Wed, 31 Jan 2018 20:44:32 +0000 Subject: [PATCH 53/83] show Log out confirmation dialog --- .../activities/BirthdayListActivity.java | 33 +++++++------ .../DialogFragments/SignOutDialog.java | 46 +++++++++++++++++++ app/src/main/res/values/strings.xml | 3 ++ 3 files changed, 69 insertions(+), 13 deletions(-) create mode 100644 app/src/main/java/website/julianrosser/birthdays/fragments/DialogFragments/SignOutDialog.java diff --git a/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java b/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java index 9c36fb2..848495e 100644 --- a/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java +++ b/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java @@ -64,6 +64,7 @@ import website.julianrosser.birthdays.database.DatabaseHelper; import website.julianrosser.birthdays.fragments.DialogFragments.AddEditFragment; import website.julianrosser.birthdays.fragments.DialogFragments.ItemOptionsFragment; +import website.julianrosser.birthdays.fragments.DialogFragments.SignOutDialog; import website.julianrosser.birthdays.fragments.RecyclerListFragment; import website.julianrosser.birthdays.model.Birthday; import website.julianrosser.birthdays.model.events.BirthdayItemClickEvent; @@ -268,6 +269,7 @@ private void handleSignInResult(GoogleSignInResult result) { // Signed in successfully, show authenticated UI. GoogleSignInAccount account = result.getSignInAccount(); firebaseAuthWithGoogle(account); + AlarmsHelper.setAllNotificationAlarms(this.getApplicationContext()); } else { setNavHeaderUserState(NavHeaderState.LOGGED_OUT); Snackbar.make(floatingActionButton, "Error while logging in", Snackbar.LENGTH_SHORT).show(); // todo - translate @@ -316,7 +318,6 @@ public void onAuthStateChanged(@NonNull FirebaseAuth firebaseAuth) { } else { // User is signed out setNavHeaderUserState(NavHeaderState.LOGGED_OUT); - Snackbar.make(floatingActionButton, "Signed OUT", Snackbar.LENGTH_SHORT).show(); Log.d("Auth", "onAuthStateChanged:signed_out"); } } @@ -520,17 +521,23 @@ public boolean onOptionsItemSelected(MenuItem item) { } private void signOutGoogle() { - FirebaseAuth.getInstance().signOut(); - Auth.GoogleSignInApi.revokeAccess(mGoogleApiClient).setResultCallback( - new ResultCallback() { - @Override - public void onResult(Status status) { - setNavHeaderUserState(NavHeaderState.LOGGED_OUT); - clearBirthdays(); - BirthdayReminder.getInstance().setUser(null); - - } - }); + SignOutDialog.newInstance(new SignOutDialog.SignOutCallback() { + @Override + public void onClicked() { + FirebaseAuth.getInstance().signOut(); + Auth.GoogleSignInApi.revokeAccess(mGoogleApiClient).setResultCallback( + new ResultCallback() { + @Override + public void onResult(Status status) { + setNavHeaderUserState(NavHeaderState.LOGGED_OUT); + AlarmsHelper.cancelAllAlarms(BirthdayListActivity.this.getApplicationContext(), recyclerListFragment.getAdapter().getBirthdays()); + clearBirthdays(); + BirthdayReminder.getInstance().setUser(null); + Snackbar.make(floatingActionButton, R.string.message_signed_out, Snackbar.LENGTH_SHORT).show(); + } + }); + } + }).show(getSupportFragmentManager(), SignOutDialog.class.getSimpleName()); } public void clearBirthdays() { @@ -605,7 +612,7 @@ public void onItemEdit(ItemOptionsFragment dialog, Birthday birthday) { public void onItemDelete(ItemOptionsFragment dialog, Birthday birthday) { itemOptionsFragment.dismiss(); DatabaseHelper.saveBirthdayChange(birthday, DatabaseHelper.Update.DELETE); - AlarmsHelper.cancelAlarm(this, birthday.hashCode()); + AlarmsHelper.cancelAlarm(this, birthday.getName().hashCode()); SnackBarHelper.birthdayDeleted(floatingActionButton, birthday); } diff --git a/app/src/main/java/website/julianrosser/birthdays/fragments/DialogFragments/SignOutDialog.java b/app/src/main/java/website/julianrosser/birthdays/fragments/DialogFragments/SignOutDialog.java new file mode 100644 index 0000000..10fa509 --- /dev/null +++ b/app/src/main/java/website/julianrosser/birthdays/fragments/DialogFragments/SignOutDialog.java @@ -0,0 +1,46 @@ +package website.julianrosser.birthdays.fragments.DialogFragments; + +import android.app.Dialog; +import android.content.DialogInterface; +import android.os.Bundle; +import android.support.v4.app.DialogFragment; +import android.support.v7.app.AlertDialog; + +import website.julianrosser.birthdays.R; + +public class SignOutDialog extends DialogFragment { + + private SignOutCallback signOutCallback; + + public interface SignOutCallback { + void onClicked(); + } + + public static SignOutDialog newInstance(SignOutCallback signOutCallback) { + SignOutDialog dialog = new SignOutDialog(); + dialog.setCallback(signOutCallback); + return dialog; + } + + private void setCallback(SignOutCallback signOutCallback) { + this.signOutCallback = signOutCallback; + } + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + // Use the Builder class for convenient dialog construction + AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); + builder.setMessage(getString(R.string.dialog_sign_out_message)) + .setTitle(getString(R.string.dialog_sign_out_title)) + .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + signOutCallback.onClicked(); + } + }) + .setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { } + }); + // Create the AlertDialog object and return it + return builder.create(); + } +} \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ae1718e..0962a57 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -160,6 +160,9 @@ No Birthdays Tap the Birthday cake icon to add your first birthday, or sign in to your Google account to retrieve them! + Sign Out + Are you sure you want to sign out? You will only see your birthdays after signing back in. + Signed Out From 4eda675ec949c6d949650a0fde76f271bc57861b Mon Sep 17 00:00:00 2001 From: Jules Date: Wed, 31 Jan 2018 20:45:00 +0000 Subject: [PATCH 54/83] fix issue where deleted birthdays wouldn't be canceled --- .../birthdays/fragments/DialogFragments/AddEditFragment.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/website/julianrosser/birthdays/fragments/DialogFragments/AddEditFragment.java b/app/src/main/java/website/julianrosser/birthdays/fragments/DialogFragments/AddEditFragment.java index 292c432..a013868 100644 --- a/app/src/main/java/website/julianrosser/birthdays/fragments/DialogFragments/AddEditFragment.java +++ b/app/src/main/java/website/julianrosser/birthdays/fragments/DialogFragments/AddEditFragment.java @@ -245,7 +245,7 @@ public void onClick(View v) { if (ADD_OR_EDIT_MODE == MODE_EDIT) { String key = bundle.getString(UID_KEY); if (! Utils.isStringEmpty(key)) birthday.setUID(key); - AlarmsHelper.cancelAlarm(getActivity(), birthday.hashCode()); + AlarmsHelper.cancelAlarm(getActivity(), birthday.getName().hashCode()); DatabaseHelper.saveBirthdayChange(birthday, DatabaseHelper.Update.UPDATE); } else if (ADD_OR_EDIT_MODE == MODE_ADD) { DatabaseHelper.saveBirthdayChange(birthday, DatabaseHelper.Update.CREATE); From 6d9f5ddd7deab9b9c961d456a0dd98e0cd0379e1 Mon Sep 17 00:00:00 2001 From: Jules Date: Wed, 31 Jan 2018 20:45:19 +0000 Subject: [PATCH 55/83] refactor method name --- .../julianrosser/birthdays/database/DatabaseHelper.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/website/julianrosser/birthdays/database/DatabaseHelper.java b/app/src/main/java/website/julianrosser/birthdays/database/DatabaseHelper.java index 01cc000..bad2ffd 100644 --- a/app/src/main/java/website/julianrosser/birthdays/database/DatabaseHelper.java +++ b/app/src/main/java/website/julianrosser/birthdays/database/DatabaseHelper.java @@ -77,7 +77,7 @@ public void onCancelled(DatabaseError databaseError) { public static void saveBirthdayChange(Birthday birthday, Update state) { if (Preferences.isUsingFirebase(BirthdayReminder.getInstance())) { - saveFirebaseChange(birthday, state); + saveFirebaseChangeAndSetAlarms(birthday, state); } else { try { saveJSONChange(birthday, state); @@ -88,7 +88,7 @@ public static void saveBirthdayChange(Birthday birthday, Update state) { } } - private static void saveFirebaseChange(Birthday birthday, Update state) { + private static void saveFirebaseChangeAndSetAlarms(Birthday birthday, Update state) { FirebaseUser user = BirthdayReminder.getInstance().getCurrentUser(); if (user == null || Utils.isStringEmpty(user.getUid())) { return; From 6465a2400d0360a4bcfbfc9f3cc4dbb6b81a9be4 Mon Sep 17 00:00:00 2001 From: Jules Date: Wed, 31 Jan 2018 20:46:09 +0000 Subject: [PATCH 56/83] cancel all birthdays, allow birthday access from adapter --- .../julianrosser/birthdays/AlarmsHelper.java | 15 +++++++++++++-- .../birthdays/adapter/BirthdayViewAdapter.java | 4 ++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/website/julianrosser/birthdays/AlarmsHelper.java b/app/src/main/java/website/julianrosser/birthdays/AlarmsHelper.java index ecdf078..489cb3e 100644 --- a/app/src/main/java/website/julianrosser/birthdays/AlarmsHelper.java +++ b/app/src/main/java/website/julianrosser/birthdays/AlarmsHelper.java @@ -5,6 +5,9 @@ import android.content.Context; import android.content.Intent; +import java.util.ArrayList; + +import website.julianrosser.birthdays.model.Birthday; import website.julianrosser.birthdays.recievers.NotificationBuilderReceiver; import website.julianrosser.birthdays.services.SetAlarmsService; @@ -15,6 +18,12 @@ public static void setAllNotificationAlarms(Context context) { context.startService(serviceIntent); } + public static void cancelAllAlarms(Context context, ArrayList birthdays) { + for (Birthday b: birthdays) { + cancelAlarm(context, b.getName().hashCode()); + } + } + // This builds an identical PendingIntent to the alarm and cancels when public static void cancelAlarm(Context context, int id) { @@ -22,7 +31,7 @@ public static void cancelAlarm(Context context, int id) { Intent mNotificationReceiverIntent = new Intent(context, NotificationBuilderReceiver.class); - // Create pending Intent using Intent we just built + // Create pending Intent exactly as it was set previously PendingIntent mNotificationReceiverPendingIntent = PendingIntent .getBroadcast(context.getApplicationContext(), id, @@ -31,7 +40,9 @@ public static void cancelAlarm(Context context, int id) { // Cancel alarm AlarmManager mAlarmManager = (AlarmManager) context.getApplicationContext().getSystemService(Context.ALARM_SERVICE); - mAlarmManager.cancel(mNotificationReceiverPendingIntent); + if (mAlarmManager != null) { + mAlarmManager.cancel(mNotificationReceiverPendingIntent); + } } } \ No newline at end of file diff --git a/app/src/main/java/website/julianrosser/birthdays/adapter/BirthdayViewAdapter.java b/app/src/main/java/website/julianrosser/birthdays/adapter/BirthdayViewAdapter.java index 0147ce3..a60b310 100644 --- a/app/src/main/java/website/julianrosser/birthdays/adapter/BirthdayViewAdapter.java +++ b/app/src/main/java/website/julianrosser/birthdays/adapter/BirthdayViewAdapter.java @@ -60,6 +60,10 @@ public void clearBirthdays() { notifyDataSetChanged(); } + public ArrayList getBirthdays() { + return birthdays; + } + public void setData(ArrayList birthdays) { this.birthdays = birthdays; From 3a42f1b7ee1382e67ee816eae4aa1c83cf8f563d Mon Sep 17 00:00:00 2001 From: Jules Date: Wed, 31 Jan 2018 20:46:18 +0000 Subject: [PATCH 57/83] formatting fix --- .../java/website/julianrosser/birthdays/model/Birthday.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/website/julianrosser/birthdays/model/Birthday.java b/app/src/main/java/website/julianrosser/birthdays/model/Birthday.java index 11f7e85..0b0e0ef 100644 --- a/app/src/main/java/website/julianrosser/birthdays/model/Birthday.java +++ b/app/src/main/java/website/julianrosser/birthdays/model/Birthday.java @@ -50,7 +50,7 @@ public class Birthday { * */ public Birthday(String name, Date dateOfBirthday, boolean notifyUserOfBirthday, boolean includeYear) { - this.name = WordUtils.capitalize(name);; + this.name = WordUtils.capitalize(name); this.remind = notifyUserOfBirthday; this.date = dateOfBirthday; this.yearOfBirth = dateOfBirthday.getYear(); From c93e18644f6107f640077b3bd645b0f923e17f79 Mon Sep 17 00:00:00 2001 From: Jules Date: Mon, 5 Feb 2018 23:47:48 +0000 Subject: [PATCH 58/83] remove unused layout --- app/src/main/res/layout/drawer_list_item.xml | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 app/src/main/res/layout/drawer_list_item.xml diff --git a/app/src/main/res/layout/drawer_list_item.xml b/app/src/main/res/layout/drawer_list_item.xml deleted file mode 100644 index 1934bfa..0000000 --- a/app/src/main/res/layout/drawer_list_item.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - \ No newline at end of file From 86fae16e1ae19e14f00d43e1365889f28050c822 Mon Sep 17 00:00:00 2001 From: Jules Date: Mon, 5 Feb 2018 23:48:14 +0000 Subject: [PATCH 59/83] update log out message --- app/src/main/res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 0962a57..543ed4d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -161,7 +161,7 @@ No Birthdays Tap the Birthday cake icon to add your first birthday, or sign in to your Google account to retrieve them! Sign Out - Are you sure you want to sign out? You will only see your birthdays after signing back in. + Are you sure you want to sign out? You will loose these birthday reminders until you sign back in. Signed Out From 4eb7801f8b40130a6d24fa7416621366aad9a278 Mon Sep 17 00:00:00 2001 From: Jules Date: Mon, 5 Feb 2018 23:48:42 +0000 Subject: [PATCH 60/83] add Log out option to NavDrawer, remove from overflow menu --- .../birthdays/activities/BirthdayListActivity.java | 10 +++++----- app/src/main/res/menu/menu_main.xml | 7 +------ app/src/main/res/menu/menu_nav_drawer.xml | 3 +++ app/src/main/res/values/strings.xml | 3 +++ 4 files changed, 12 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java b/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java index 848495e..ab0230a 100644 --- a/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java +++ b/app/src/main/java/website/julianrosser/birthdays/activities/BirthdayListActivity.java @@ -148,7 +148,10 @@ public boolean onNavigationItemSelected(MenuItem menuItem) { startActivityForResult(new Intent(getApplicationContext(), SettingsActivity.class), RC_SETTINGS); return true; case R.id.menu_privacy: - startActivity(new Intent(getApplicationContext(), PrivacyPolicyActivity.class)); + startActivityForResult(new Intent(getApplicationContext(), SettingsActivity.class), RC_SETTINGS); + return true; + case R.id.menu_logout: + signOutGoogle(); default: return true; } @@ -507,10 +510,7 @@ public boolean onOptionsItemSelected(MenuItem item) { // as you specify a parent activity in AndroidManifest.xml. int id = item.getItemId(); - if (id == R.id.action_sign_out) { - signOutGoogle(); - return true; - } else if (id == R.id.action_firebase) { + if (id == R.id.action_firebase) { boolean bool = Preferences.isUsingFirebase(this); Preferences.setIsUsingFirebase(this, !bool); Snackbar.make(floatingActionButton, "DB mode: " + (!bool ? "Firebase" : "JSON"), Snackbar.LENGTH_SHORT).show(); diff --git a/app/src/main/res/menu/menu_main.xml b/app/src/main/res/menu/menu_main.xml index 9881eb2..fdf78e1 100644 --- a/app/src/main/res/menu/menu_main.xml +++ b/app/src/main/res/menu/menu_main.xml @@ -1,12 +1,7 @@ - - - + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 543ed4d..430b1d2 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -164,5 +164,8 @@ Are you sure you want to sign out? You will loose these birthday reminders until you sign back in. Signed Out + Log Out + + From 150920df530738b9dedb0e481d38774505897f00 Mon Sep 17 00:00:00 2001 From: Jules Date: Wed, 7 Feb 2018 20:03:56 +0000 Subject: [PATCH 61/83] add kotlin dependencies --- app/build.gradle | 2 ++ build.gradle | 3 +++ 2 files changed, 5 insertions(+) diff --git a/app/build.gradle b/app/build.gradle index a61ca74..d60e0e4 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,4 +1,5 @@ apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' android { compileSdkVersion 23 @@ -48,5 +49,6 @@ dependencies { compile 'com.google.firebase:firebase-database:10.0.1' compile 'com.google.android.gms:play-services-auth:10.0.1' compile 'com.squareup.picasso:picasso:2.5.2' + compile "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" } apply plugin: 'com.google.gms.google-services' \ No newline at end of file diff --git a/build.gradle b/build.gradle index 8760f10..20ee844 100644 --- a/build.gradle +++ b/build.gradle @@ -1,12 +1,14 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { + ext.kotlin_version = '1.2.21' repositories { jcenter() } dependencies { classpath 'com.android.tools.build:gradle:3.0.1' classpath 'com.google.gms:google-services:3.0.0' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } @@ -14,5 +16,6 @@ buildscript { allprojects { repositories { jcenter() + mavenCentral() } } From 1ac0ead3e474e57b8a780f3f155596c6076c2917 Mon Sep 17 00:00:00 2001 From: Jules Date: Wed, 7 Feb 2018 20:34:44 +0000 Subject: [PATCH 62/83] create Welcome screen --- app/src/main/AndroidManifest.xml | 9 +++- .../birthdays/activities/WelcomeActivity.kt | 26 +++++++++++ app/src/main/res/layout/activity_welcome.xml | 43 +++++++++++++++++++ 3 files changed, 77 insertions(+), 1 deletion(-) create mode 100644 app/src/main/java/website/julianrosser/birthdays/activities/WelcomeActivity.kt create mode 100644 app/src/main/res/layout/activity_welcome.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index d038155..0f4ec92 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -18,9 +18,9 @@ @@ -94,6 +94,13 @@ android:name="android.support.PARENT_ACTIVITY" android:value=".activities.BirthdayListActivity" /> + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/website/julianrosser/birthdays/activities/WelcomeActivity.kt b/app/src/main/java/website/julianrosser/birthdays/activities/WelcomeActivity.kt new file mode 100644 index 0000000..5f550cf --- /dev/null +++ b/app/src/main/java/website/julianrosser/birthdays/activities/WelcomeActivity.kt @@ -0,0 +1,26 @@ +package website.julianrosser.birthdays.activities + +import android.os.Bundle +import android.preference.PreferenceManager +import android.support.v7.app.AppCompatActivity +import website.julianrosser.birthdays.R + +class WelcomeActivity : AppCompatActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + setTheme() + setContentView(R.layout.activity_welcome) + } + + private fun setTheme() { + val prefs = PreferenceManager.getDefaultSharedPreferences(applicationContext) + setTheme(when (prefs.getString(resources.getString(R.string.pref_theme_key), "")) { + "0" -> R.style.BlueTheme + "1" -> R.style.PinkTheme + "2" -> R.style.GreenTheme + else -> R.style.PinkTheme + }) + } +} \ No newline at end of file diff --git a/app/src/main/res/layout/activity_welcome.xml b/app/src/main/res/layout/activity_welcome.xml new file mode 100644 index 0000000..3230036 --- /dev/null +++ b/app/src/main/res/layout/activity_welcome.xml @@ -0,0 +1,43 @@ + + + + + + + +