Created by alexstyl on 05/02/15.
- */ -public class PayPal { - - public static final String URL_DONATIONS = "https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=M7V8YHDL2NVUC"; -} diff --git a/mobile/src/main/java/com/alexstyl/specialdates/about/AboutActivity.java b/mobile/src/main/java/com/alexstyl/specialdates/about/AboutActivity.java index 2d79bdd4..f1a9fc45 100644 --- a/mobile/src/main/java/com/alexstyl/specialdates/about/AboutActivity.java +++ b/mobile/src/main/java/com/alexstyl/specialdates/about/AboutActivity.java @@ -11,6 +11,7 @@ import com.alexstyl.specialdates.Navigator; import com.alexstyl.specialdates.R; +import com.alexstyl.specialdates.analytics.AnalyticsProvider; import com.alexstyl.specialdates.theming.AttributeExtractor; import com.alexstyl.specialdates.ui.CheatsSheat; import com.alexstyl.specialdates.ui.activity.MainActivity; @@ -41,7 +42,7 @@ protected void onCreate(Bundle savedInstanceState) { SimpleChromeCustomTabs.initialize(this); - navigator = new Navigator(this); + navigator = new Navigator(this, AnalyticsProvider.getAnalytics(this)); MementoToolbar toolbar = Views.findById(this, R.id.memento_toolbar); setSupportActionBar(toolbar); diff --git a/mobile/src/main/java/com/alexstyl/specialdates/addevent/AddBirthdayActivity.java b/mobile/src/main/java/com/alexstyl/specialdates/addevent/AddBirthdayActivity.java index cc5df3b4..d9b9b8b5 100644 --- a/mobile/src/main/java/com/alexstyl/specialdates/addevent/AddBirthdayActivity.java +++ b/mobile/src/main/java/com/alexstyl/specialdates/addevent/AddBirthdayActivity.java @@ -9,10 +9,9 @@ import com.alexstyl.specialdates.addevent.ui.ContactHeroView; import com.alexstyl.specialdates.addevent.ui.ContactsAutoCompleteView; import com.alexstyl.specialdates.analytics.Action; -import com.alexstyl.specialdates.analytics.Analytics; import com.alexstyl.specialdates.analytics.ActionWithParameters; -import com.alexstyl.specialdates.analytics.Firebase; -import com.alexstyl.specialdates.analytics.Screen; +import com.alexstyl.specialdates.analytics.Analytics; +import com.alexstyl.specialdates.analytics.AnalyticsProvider; import com.alexstyl.specialdates.contact.Birthday; import com.alexstyl.specialdates.contact.Contact; import com.alexstyl.specialdates.theming.MementoTheme; @@ -35,9 +34,8 @@ public class AddBirthdayActivity extends ThemedActivity { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - analytics = Firebase.get(this); - analytics.trackScreen(Screen.ADD_BIRTHDAY); - MementoTheme theme = Themer.get().getCurrentTheme(); + analytics = AnalyticsProvider.getAnalytics(this); + MementoTheme theme = Themer.get(this).getCurrentTheme(); setContentView(R.layout.activity_add_birthday, theme); contactHeroView = Views.findById(this, R.id.addbirthday_hero); diff --git a/mobile/src/main/java/com/alexstyl/specialdates/addevent/BirthdayQuery.java b/mobile/src/main/java/com/alexstyl/specialdates/addevent/BirthdayQuery.java index dabfff15..4ec4f5a0 100644 --- a/mobile/src/main/java/com/alexstyl/specialdates/addevent/BirthdayQuery.java +++ b/mobile/src/main/java/com/alexstyl/specialdates/addevent/BirthdayQuery.java @@ -15,7 +15,6 @@ import com.alexstyl.specialdates.date.DateParseException; import com.alexstyl.specialdates.date.DayDate; import com.alexstyl.specialdates.util.DateParser; -import com.alexstyl.specialdates.util.Utils; import com.novoda.notils.exception.DeveloperError; public class BirthdayQuery { @@ -127,8 +126,7 @@ public static Cursor query(ContentResolver cr) { } private static final Uri CONTENT_URI = ContactsContract.Data.CONTENT_URI; - private static final String COL_DISPLAY_NAME = Utils.hasHoneycomb() ? ContactsContract.Contacts.DISPLAY_NAME_PRIMARY - : ContactsContract.Contacts.DISPLAY_NAME; + private static final String COL_DISPLAY_NAME = ContactsContract.Contacts.DISPLAY_NAME_PRIMARY; public static final String WHERE = "(" + ContactsContract.Data.MIMETYPE + " = ? AND " + diff --git a/mobile/src/main/java/com/alexstyl/specialdates/addevent/ui/BirthdayDatePicker.java b/mobile/src/main/java/com/alexstyl/specialdates/addevent/ui/BirthdayDatePicker.java index 7c5ba697..7df892dd 100644 --- a/mobile/src/main/java/com/alexstyl/specialdates/addevent/ui/BirthdayDatePicker.java +++ b/mobile/src/main/java/com/alexstyl/specialdates/addevent/ui/BirthdayDatePicker.java @@ -8,6 +8,7 @@ import android.view.View; import android.widget.CheckedTextView; import android.widget.LinearLayout; +import android.widget.NumberPicker; import com.alexstyl.specialdates.R; import com.alexstyl.specialdates.contact.Birthday; @@ -18,19 +19,17 @@ import java.util.Locale; -import net.simonvt.numberpicker.NumberPicker; - public class BirthdayDatePicker extends LinearLayout { - private MonthLabels labels; + private final MonthLabels labels; - private NumberPicker dayPicker; - private NumberPicker monthPicker; - private NumberPicker yearPicker; + private final NumberPicker dayPicker; + private final NumberPicker monthPicker; + private final NumberPicker yearPicker; - private CheckedTextView includesYearCheckbox; + private final CheckedTextView includesYearCheckbox; - private DayDate today; + private final DayDate today; public BirthdayDatePicker(Context context, AttributeSet attrs) { super(context, attrs); diff --git a/mobile/src/main/java/com/alexstyl/specialdates/analytics/Action.java b/mobile/src/main/java/com/alexstyl/specialdates/analytics/Action.java index 9b192da8..2966b8a8 100644 --- a/mobile/src/main/java/com/alexstyl/specialdates/analytics/Action.java +++ b/mobile/src/main/java/com/alexstyl/specialdates/analytics/Action.java @@ -1,12 +1,12 @@ package com.alexstyl.specialdates.analytics; public enum Action { - ADD_BIRTHDAY("add birthday"), - DAILY_REMINDER("enable daily reminder"), + ADD_BIRTHDAY("add_bday"), + DAILY_REMINDER("reminder"), DONATION("donate"), - INTERACT_CONTACT("interact contact"), - SELECT_THEME("select theme"), - GO_TO_TODAY("go to today"); + INTERACT_CONTACT("contact"), + SELECT_THEME("theme"), + GO_TO_TODAY("gotoday"); private final String name; diff --git a/mobile/src/main/java/com/alexstyl/specialdates/analytics/ActionWithParameters.java b/mobile/src/main/java/com/alexstyl/specialdates/analytics/ActionWithParameters.java index 4b455c5b..85e791f1 100644 --- a/mobile/src/main/java/com/alexstyl/specialdates/analytics/ActionWithParameters.java +++ b/mobile/src/main/java/com/alexstyl/specialdates/analytics/ActionWithParameters.java @@ -30,4 +30,12 @@ public String getValue() { return value; } + @Override + public String toString() { + return "ActionWithParameters{" + + "actionName=" + actionName + + ", label='" + label + '\'' + + ", value='" + value + '\'' + + '}'; + } } diff --git a/mobile/src/main/java/com/alexstyl/specialdates/analytics/Analytics.java b/mobile/src/main/java/com/alexstyl/specialdates/analytics/Analytics.java index 2da83786..97713c86 100644 --- a/mobile/src/main/java/com/alexstyl/specialdates/analytics/Analytics.java +++ b/mobile/src/main/java/com/alexstyl/specialdates/analytics/Analytics.java @@ -1,7 +1,7 @@ package com.alexstyl.specialdates.analytics; public interface Analytics { - void trackAction(Action goToToday); + void trackAction(Action action); void trackAction(ActionWithParameters event); diff --git a/mobile/src/main/java/com/alexstyl/specialdates/analytics/AnalyticsProvider.java b/mobile/src/main/java/com/alexstyl/specialdates/analytics/AnalyticsProvider.java new file mode 100644 index 00000000..e9e0aa66 --- /dev/null +++ b/mobile/src/main/java/com/alexstyl/specialdates/analytics/AnalyticsProvider.java @@ -0,0 +1,25 @@ +package com.alexstyl.specialdates.analytics; + +import android.content.Context; + +import com.alexstyl.specialdates.BuildConfig; +import com.mixpanel.android.mpmetrics.MixpanelAPI; + +public class AnalyticsProvider { + + private static Analytics ANALYTICS; + + public static Analytics getAnalytics(Context context) { + if (ANALYTICS == null) { + ANALYTICS = createMixpanelAnalytics(context); + } + return ANALYTICS; + } + + private static Analytics createMixpanelAnalytics(Context context) { + String projectToken = BuildConfig.MIXPANEL_TOKEN; + MixpanelAPI mixpanel = MixpanelAPI.getInstance(context, projectToken); + return new MixPanel(mixpanel); + } + +} diff --git a/mobile/src/main/java/com/alexstyl/specialdates/analytics/Firebase.java b/mobile/src/main/java/com/alexstyl/specialdates/analytics/Firebase.java deleted file mode 100644 index e002cce4..00000000 --- a/mobile/src/main/java/com/alexstyl/specialdates/analytics/Firebase.java +++ /dev/null @@ -1,53 +0,0 @@ -package com.alexstyl.specialdates.analytics; - -import android.content.Context; -import android.os.Bundle; - -import com.google.firebase.analytics.FirebaseAnalytics; -import com.novoda.notils.logger.simple.Log; - -import java.util.Locale; - -public class Firebase implements Analytics { - - private static final Bundle NO_DATA = null; - - private final FirebaseAnalytics firebaseAnalytics; - - private static Firebase INSTANCE; - - public static Firebase get(Context context) { - if (INSTANCE == null) { - INSTANCE = new Firebase(FirebaseAnalytics.getInstance(context)); - } - return INSTANCE; - } - - private Firebase(FirebaseAnalytics firebaseAnalytics) { - this.firebaseAnalytics = firebaseAnalytics; - } - - @Override - public void trackAction(Action goToToday) { - String actionName = goToToday.getName(); - firebaseAnalytics.logEvent(actionName, NO_DATA); - Log.d("Tracking event:" + actionName); - } - - @Override - public void trackAction(ActionWithParameters action) { - String formattedAction = format(action); - firebaseAnalytics.logEvent(formattedAction, NO_DATA); - Log.d("Tracking event:" + formattedAction); - } - - @Override - public void trackScreen(Screen screen) { - firebaseAnalytics.logEvent("screen_view:" + screen.screenName(), NO_DATA); - Log.d("Tracking screen_view:" + screen); - } - - private String format(ActionWithParameters action) { - return String.format(Locale.US, "%s:%s:%s", action.getName(), action.getLabel(), action.getValue()); - } -} diff --git a/mobile/src/main/java/com/alexstyl/specialdates/analytics/MixPanel.java b/mobile/src/main/java/com/alexstyl/specialdates/analytics/MixPanel.java new file mode 100644 index 00000000..793207f2 --- /dev/null +++ b/mobile/src/main/java/com/alexstyl/specialdates/analytics/MixPanel.java @@ -0,0 +1,42 @@ +package com.alexstyl.specialdates.analytics; + +import com.mixpanel.android.mpmetrics.MixpanelAPI; + +import org.json.JSONException; +import org.json.JSONObject; + +public class MixPanel implements Analytics { + + private final MixpanelAPI mixpanel; + + MixPanel(MixpanelAPI mixpanel) { + this.mixpanel = mixpanel; + } + + @Override + public void trackAction(Action action) { + mixpanel.track("Action: " + action.getName()); + } + + @Override + public void trackAction(ActionWithParameters event) { + JSONObject properties = createJSONfor(event); + mixpanel.track("Action: " + event.getName(), properties); + + } + + @Override + public void trackScreen(Screen screen) { + mixpanel.track("ScreenView: " + screen.screenName()); + } + + private static JSONObject createJSONfor(ActionWithParameters event) { + JSONObject properties = new JSONObject(); + try { + properties.put(event.getLabel(), event.getValue()); + } catch (JSONException e) { + e.printStackTrace(); + } + return properties; + } +} diff --git a/mobile/src/main/java/com/alexstyl/specialdates/analytics/Screen.java b/mobile/src/main/java/com/alexstyl/specialdates/analytics/Screen.java index 7b1cbf34..0139bcaa 100644 --- a/mobile/src/main/java/com/alexstyl/specialdates/analytics/Screen.java +++ b/mobile/src/main/java/com/alexstyl/specialdates/analytics/Screen.java @@ -1,13 +1,14 @@ package com.alexstyl.specialdates.analytics; public enum Screen { - HOME("home"), + HOME("upcoming"), ADD_BIRTHDAY("add birthday"), SEARCH("search"), SETTINGS("settings"), DATE_DETAILS("date details"), DONATE("donate"), - ABOUT("about"); + ABOUT("about"), + CONTACT_PERMISSION_REQUESTED("contact permission"); private final String screenName; diff --git a/mobile/src/main/java/com/alexstyl/specialdates/contact/ContactsQuery.java b/mobile/src/main/java/com/alexstyl/specialdates/contact/ContactsQuery.java index fcee9c80..74136715 100644 --- a/mobile/src/main/java/com/alexstyl/specialdates/contact/ContactsQuery.java +++ b/mobile/src/main/java/com/alexstyl/specialdates/contact/ContactsQuery.java @@ -7,20 +7,16 @@ import android.os.Build; import android.provider.ContactsContract; -import com.alexstyl.specialdates.util.Utils; - @TargetApi(Build.VERSION_CODES.HONEYCOMB) class ContactsQuery { public final static Uri CONTENT_URI = ContactsContract.Data.CONTENT_URI; - public static String COL_DISPLAY_NAME = Utils.hasHoneycomb() ? ContactsContract.Contacts.DISPLAY_NAME_PRIMARY //3 - : ContactsContract.Contacts.DISPLAY_NAME; + public static String COL_DISPLAY_NAME = ContactsContract.Contacts.DISPLAY_NAME_PRIMARY; public static String COL_LOOKUP = ContactsContract.Contacts.LOOKUP_KEY; @SuppressLint("InlinedApi") - public final static String SORT_ORDER = Utils.hasHoneycomb() ? - ContactsContract.Contacts.DISPLAY_NAME_PRIMARY : ContactsContract.Contacts.DISPLAY_NAME; + public final static String SORT_ORDER = ContactsContract.Contacts.DISPLAY_NAME_PRIMARY; @SuppressLint("InlinedApi") public static final String[] PROJECTION = { diff --git a/mobile/src/main/java/com/alexstyl/specialdates/dailyreminder/DailyReminderDebugPreferences.java b/mobile/src/main/java/com/alexstyl/specialdates/dailyreminder/DailyReminderDebugPreferences.java new file mode 100644 index 00000000..5864e64b --- /dev/null +++ b/mobile/src/main/java/com/alexstyl/specialdates/dailyreminder/DailyReminderDebugPreferences.java @@ -0,0 +1,43 @@ +package com.alexstyl.specialdates.dailyreminder; + +import android.content.Context; +import android.support.v4.util.Pair; + +import com.alexstyl.specialdates.EasyPreferences; +import com.alexstyl.specialdates.R; +import com.alexstyl.specialdates.date.DayDate; + +public final class DailyReminderDebugPreferences { + + private final EasyPreferences preferences; + + public static DailyReminderDebugPreferences newInstance(Context context) { + return new DailyReminderDebugPreferences(EasyPreferences.createForPrivatePreferences(context, R.string.pref_dailyreminder_debug)); + } + + private DailyReminderDebugPreferences(EasyPreferences preferences) { + this.preferences = preferences; + } + + public DayDate getSelectedDate() { + int dayOfMonth = preferences.getInt(R.string.key_debug_daily_reminder_date_fake_day, 1); + int month = preferences.getInt(R.string.key_debug_daily_reminder_date_fake_month, DayDate.JANUARY); + int year = preferences.getInt(R.string.key_debug_daily_reminder_date_fake_year, 2016); + return DayDate.newInstance(dayOfMonth, month, year); + } + + public boolean isFakeDateEnabled() { + return preferences.getBoolean(R.string.key_debug_daily_reminder_date_enable, false); + } + + public void setSelectedDate(int dayOfMonth, int month, int year) { + PairCreated by alexstyl on 20/04/15.
- */ -public class DeviceSearchFragment extends MementoFragment implements NameSuggestionsAdapter.OnNameSelectedListener { - - private static final String KEY_QUERY = "alexstyl:key_query"; - - private static final int ID_CONTACTS = 31; - private static final int ID_NAMEDAYS = 32; - - private static final int INITAL_COUNT = 5; - - private int searchCounter = INITAL_COUNT; - - private EditText searchField; - - private ImageButton clearButton; - private RecyclerView resultView; - - private RecyclerView namesSuggestionsView; - private SearchResultAdapter adapter; - - private NameSuggestionsAdapter namesAdapter; - - private boolean displayNamedays; - - private String searchQuery; - - @Override - public void onStart() { - super.onStart(); - searchField.requestFocus(); - } - - @Override - public void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - outState.putString(KEY_QUERY, searchQuery); - } - - private LoaderManager.LoaderCallbacksCreated by alexstyl on 20/04/15.
+ */ public class SearchActivity extends ThemedActivity { + private static final String KEY_QUERY = "alexstyl:key_query"; + private static final int ID_CONTACTS = 31; + private static final int ID_NAMEDAYS = 32; + private static final int INITAL_COUNT = 5; + + private int searchCounter = INITAL_COUNT; + private SearchBar searchbar; + private RecyclerView resultView; + private RecyclerView namesSuggestionsView; + private SearchResultAdapter adapter; + private NameSuggestionsAdapter namesAdapter; + private String searchQuery; + + private ViewFader fader = new ViewFader(); + private ViewGroup content; + private NamedayPreferences namedayPreferences; + private ContactPermissionRequest permissions; + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_search); + Analytics analytics = AnalyticsProvider.getAnalytics(this); + analytics.trackScreen(Screen.SEARCH); + searchbar = Views.findById(this, R.id.search_searchbar); + setSupportActionBar(searchbar); + content = Views.findById(this, R.id.search_content); + resultView = Views.findById(this, android.R.id.list); + resultView.setHasFixedSize(false); + namesSuggestionsView = Views.findById(this, R.id.nameday_suggestions); + PermissionChecker checker = new PermissionChecker(this); + Navigator navigator = new Navigator(this, analytics); + permissions = new ContactPermissionRequest(navigator, checker, permissionCallbacks); + + if (savedInstanceState != null) { + searchQuery = savedInstanceState.getString(KEY_QUERY); + } + + setupSearchField(); + + ImageLoader imageLoader = ImageLoader.createSquareThumbnailLoader(getResources()); + int year = DayDate.today().getYear(); + NamedayLocale locale = NamedayPreferences.newInstance(this).getSelectedLanguage(); + NamedayCalendar namedayCalendar = NamedayCalendarProvider.newInstance(this).loadNamedayCalendarForLocale(locale, year); + + adapter = new SearchResultAdapter(imageLoader, namedayCalendar); + adapter.setSearchResultClickListener(listener); + + resultView.setHasFixedSize(true); + RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(context()); + int spacingInPixels = getResources().getDimensionPixelSize(R.dimen.card_spacing_between); + resultView.addItemDecoration(new SpacesItemDecoration(spacingInPixels, 3)); + resultView.setLayoutManager(mLayoutManager); + resultView.setAdapter(adapter); + + searchbar.setOnBackKeyPressedListener(onBackKeyPressedListener); + + namedayPreferences = NamedayPreferences.newInstance(this); + setupSearchbarHint(namedayPreferences); + + if (namedayPreferences.isEnabled()) { + // we are loading namedays as well + GridLayoutManager namedayManager = new GridLayoutManager(context(), 1, RecyclerView.HORIZONTAL, false); + namesAdapter = NameSuggestionsAdapter.newInstance(context()); + namesAdapter.setOnNameSelectedListener(onNameSelectedListener); + namesSuggestionsView.setHasFixedSize(true); + namesSuggestionsView.setLayoutManager(namedayManager); + namesSuggestionsView.setAdapter(namesAdapter); + + searchbar.setInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS); + searchbar.setOnFocusChangeListener(new ToggleVisibilityOnFocus(namesSuggestionsView)); + } else { + namesSuggestionsView.setVisibility(GONE); + } + + if (savedInstanceState == null) { + fader.hideContentOf(searchbar); + ViewTreeObserver viewTreeObserver = searchbar.getViewTreeObserver(); + viewTreeObserver.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { + @Override + public void onGlobalLayout() { + searchbar.getViewTreeObserver().removeOnGlobalLayoutListener(this); + animateShowSearch(); + } + + private void animateShowSearch() { + TransitionManager.beginDelayedTransition(searchbar, FadeInTransition.createTransition()); + fader.showContent(searchbar); + } + }); + } + + if (!permissions.permissionIsPresent()) { + Log.d("requesting permission"); + permissions.requestForPermission(); + } + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + permissions.onActivityResult(requestCode, resultCode, data); + } + + private void setupSearchbarHint(NamedayPreferences preferences) { + SearchHintCreator searchHintCreator = new SearchHintCreator(getResources(), preferences); + searchbar.setHint(searchHintCreator.createHint()); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.menu_search, menu); + return true; + } + + @Override + public boolean onPrepareOptionsMenu(Menu menu) { + super.onPrepareOptionsMenu(menu); + MenuItem clearMenuItem = menu.findItem(R.id.action_clear); + clearMenuItem.setVisible(searchbar.hasText()); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case android.R.id.home: + finish(); + return true; + case R.id.action_clear: + onClearPressed(); + return true; + } + return super.onOptionsItemSelected(item); + } + + @Override + public void finish() { + if (supportsTransitions()) { + AndroidUtils.requestHideKeyboard(this, searchbar); + exitTransitionWithAction(new Runnable() { + @Override + public void run() { + SearchActivity.super.finish(); + overridePendingTransition(0, 0); + } + }); + } else { + super.finish(); + } + } + + private void exitTransitionWithAction(final Runnable endingAction) { + Transition transition = FadeOutTransition.withAction(new SimpleTransitionListener() { + @Override + public void onTransitionEnd(Transition transition) { + endingAction.run(); + } + }); + + TransitionManager.beginDelayedTransition(searchbar, transition); + fader.hideContentOf(searchbar); + + TransitionManager.beginDelayedTransition(content, new Fade(Fade.OUT)); } + private void onClearPressed() { + searchbar.clearText(); + AndroidUtils.toggleKeyboard(this); + } + + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + outState.putString(KEY_QUERY, searchQuery); + } + + private void startSearching() { + getSupportLoaderManager().restartLoader(ID_CONTACTS, null, contactSearchCallbacks); + getSupportLoaderManager().restartLoader(ID_NAMEDAYS, null, namedayLoaderCallbacks); + } + + private void clearResults() { + adapter.clearResults(); + if (namedayPreferences.isEnabled()) { + namesAdapter.clearNames(); + } + } + + private void resetSearchCounter() { + searchCounter = INITAL_COUNT; + } + + private void updateNameSuggestions(String text) { + if (namedayPreferences.isEnabled()) { + namesAdapter.getFilter().filter(text); + } + } + + private void setupSearchField() { + searchbar.addTextWatcher(DelayedTextWatcher.newInstance(textUpdatedTextUpdatedCallback)); + } + + private void onNameSet(String name) { + // setting the text to the EditText will trigger the search for the name + AndroidUtils.requestHideKeyboard(this, searchbar); + searchbar.setText(name); + searchbar.clearFocus(); + if (namedayPreferences.isEnabled()) { + namesAdapter.clearNames(); + } + } + + private final NameSuggestionsAdapter.OnNameSelectedListener onNameSelectedListener = new NameSuggestionsAdapter.OnNameSelectedListener() { + @Override + public void onNameSelected(View view, String name) { + onNameSet(name); + } + }; + + private final SearchResultAdapter.SearchResultClickListener listener = new SearchResultAdapter.SearchResultClickListener() { + + @Override + public void onContactClicked(View v, Contact contact) { + contact.displayQuickInfo(context(), v); + } + + @Override + public void onNamedayClicked(View v, int month, int day) { + DayDate dayDate = DayDate.today(); + DateDetailsActivity.startActivity(context(), month, day, dayDate.getYear()); + } + + }; + + private final LoaderManager.LoaderCallbacksNOTE: The DailyReminder service will display
*/ public class DailyReminderIntentService extends IntentService { private static final int REQUEST_CODE = 0; - private PeopleEventsProvider provider; private NamedayPreferences namedayPreferences; private NamedayCalendarProvider namedayCalendarProvider; private BankHolidaysPreferences bankHolidaysPreferences; + private PermissionChecker checker; public DailyReminderIntentService() { super("DailyReminder"); @@ -51,18 +54,26 @@ public static void startService(Context context) { } @Override - protected void onHandleIntent(Intent intent) { - rescheduleAlarm(this); - provider = PeopleEventsProvider.newInstance(this); + public void onCreate() { + super.onCreate(); notifier = Notifier.newInstance(this); namedayPreferences = NamedayPreferences.newInstance(this); namedayCalendarProvider = NamedayCalendarProvider.newInstance(this); bankHolidaysPreferences = BankHolidaysPreferences.newInstance(this); + checker = new PermissionChecker(this); + } - DayDate today = DayDate.today(); - ContactEvents celebrationDate = provider.getCelebrationDateFor(today); - if (containsAnyContactEvents(celebrationDate)) { - notifier.forDailyReminder(celebrationDate); + @Override + protected void onHandleIntent(Intent intent) { + rescheduleAlarm(this); + PeopleEventsProvider provider = PeopleEventsProvider.newInstance(this); + DayDate today = getDayDateToDisplay(); + + if (hasContactPermission()) { + ContactEvents celebrationDate = provider.getCelebrationDateFor(today); + if (containsAnyContactEvents(celebrationDate)) { + notifier.forDailyReminder(celebrationDate); + } } if (namedaysAreEnabledForAllCases()) { notifyForNamedaysFor(today); @@ -70,7 +81,22 @@ protected void onHandleIntent(Intent intent) { if (bankholidaysAreEnabled()) { notifyForBankholidaysFor(today); } + } + + private boolean hasContactPermission() { + return checker.hasPermission(Manifest.permission.READ_CONTACTS); + } + private DayDate getDayDateToDisplay() { + if (BuildConfig.DEBUG) { + DailyReminderDebugPreferences preferences = DailyReminderDebugPreferences.newInstance(this); + if (preferences.isFakeDateEnabled()) { + DayDate selectedDate = preferences.getSelectedDate(); + Log.d("Using DEBUG date to display: " + selectedDate); + return selectedDate; + } + } + return DayDate.today(); } private boolean containsAnyContactEvents(ContactEvents celebrationDate) { diff --git a/mobile/src/main/java/com/alexstyl/specialdates/settings/DailyReminderFragment.java b/mobile/src/main/java/com/alexstyl/specialdates/settings/DailyReminderFragment.java index e0d3cee5..d294e667 100644 --- a/mobile/src/main/java/com/alexstyl/specialdates/settings/DailyReminderFragment.java +++ b/mobile/src/main/java/com/alexstyl/specialdates/settings/DailyReminderFragment.java @@ -1,14 +1,20 @@ package com.alexstyl.specialdates.settings; +import android.Manifest; +import android.annotation.TargetApi; +import android.app.Activity; import android.content.Context; import android.content.Intent; +import android.content.pm.PackageManager; import android.media.Ringtone; import android.media.RingtoneManager; import android.net.Uri; +import android.os.Build; import android.os.Bundle; import android.preference.CheckBoxPreference; import android.preference.Preference; import android.preference.Preference.OnPreferenceChangeListener; +import android.support.v4.app.ActivityCompat; import android.text.TextUtils; import android.text.format.DateFormat; import android.view.Menu; @@ -16,33 +22,34 @@ import com.alexstyl.specialdates.R; import com.alexstyl.specialdates.analytics.Action; -import com.alexstyl.specialdates.analytics.Analytics; import com.alexstyl.specialdates.analytics.ActionWithParameters; -import com.alexstyl.specialdates.analytics.Firebase; +import com.alexstyl.specialdates.analytics.Analytics; +import com.alexstyl.specialdates.analytics.AnalyticsProvider; import com.alexstyl.specialdates.service.DailyReminderIntentService; +import com.alexstyl.specialdates.ui.base.MementoPreferenceFragment; import com.alexstyl.specialdates.ui.widget.TimePreference; import com.alexstyl.specialdates.util.Utils; import java.util.Calendar; -public class DailyReminderFragment extends MyPreferenceFragment { +import static android.Manifest.permission.READ_EXTERNAL_STORAGE; + +public class DailyReminderFragment extends MementoPreferenceFragment { + private static final int EXTERNAL_STORAGE_REQUEST_CODE = 15; private CheckBoxPreference enablePreference; - private Preference ringtonePreference; - private Preference vibratePreference; + private RingtonePreference ringtonePreference; private TimePreference timePreference; private Analytics analytics; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - analytics = Firebase.get(getActivity()); + analytics = AnalyticsProvider.getAnalytics(getActivity()); setHasOptionsMenu(true); addPreferencesFromResource(R.xml.preference_dailyreminder); - // the switch is controlled by the activity - - enablePreference = (CheckBoxPreference) findPreference(getString(R.string.key_daily_reminder)); + enablePreference = findPreference(R.string.key_daily_reminder); enablePreference.setOnPreferenceChangeListener(new OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(Preference preference, Object newValue) { @@ -61,7 +68,24 @@ public boolean onPreferenceChange(Preference preference, Object newValue) { }); - ringtonePreference = findPreference(getString(R.string.key_daily_reminder_ringtone)); + ringtonePreference = findPreference(R.string.key_daily_reminder_ringtone); + ringtonePreference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() { + @TargetApi(Build.VERSION_CODES.M) + @Override + public boolean onPreferenceClick(Preference preference) { + if (isExternalStoragePermissionPressent()) { + // the permission exists. Let the system handle the event + return false; + } else { + requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, EXTERNAL_STORAGE_REQUEST_CODE); + return true; + } + } + + public boolean isExternalStoragePermissionPressent() { + return ActivityCompat.checkSelfPermission(getActivity(), READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED; + } + }); ringtonePreference.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { @Override @@ -71,7 +95,7 @@ public boolean onPreferenceChange(Preference preference, Object newValue) { } }); - timePreference = (TimePreference) findPreference(getString(R.string.key_daily_reminder_time)); + timePreference = findPreference(R.string.key_daily_reminder_time); timePreference.setOnPreferenceChangeListener(new OnPreferenceChangeListener() { @Override @@ -84,14 +108,16 @@ public boolean onPreferenceChange(Preference preference, Object newValue) { } }); - vibratePreference = findPreference(getString(R.string.key_daily_reminder_vibrate_enabled)); + hideVibratorSettingIfNotPresent(); + } + private void hideVibratorSettingIfNotPresent() { + Preference vibratePreference = findPreference(getString(R.string.key_daily_reminder_vibrate_enabled)); if (!Utils.hasVibrator(getActivity())) { // hide the vibrator preference if the device doesn't support // vibration getPreferenceScreen().removePreference(vibratePreference); } - } @Override @@ -152,6 +178,14 @@ public static CharSequence getHour(Context context, Calendar cal) { } + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + if (requestCode == EXTERNAL_STORAGE_REQUEST_CODE && resultCode == Activity.RESULT_OK) { + ringtonePreference.onClick(); + } + } + private void updateRingtoneSummary(String uri) { String name = null; if (!TextUtils.isEmpty(uri)) { diff --git a/mobile/src/main/java/com/alexstyl/specialdates/settings/MainPreferenceActivity.java b/mobile/src/main/java/com/alexstyl/specialdates/settings/MainPreferenceActivity.java index e673df7f..3f97282d 100644 --- a/mobile/src/main/java/com/alexstyl/specialdates/settings/MainPreferenceActivity.java +++ b/mobile/src/main/java/com/alexstyl/specialdates/settings/MainPreferenceActivity.java @@ -25,7 +25,7 @@ public class MainPreferenceActivity extends MementoPreferenceActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - Themer.get().initialiseActivity(this); + Themer.get(this).initialiseActivity(this); setContentView(R.layout.activity_settings); MementoToolbar toolbar = Views.findById(this, R.id.memento_toolbar); diff --git a/mobile/src/main/java/com/alexstyl/specialdates/settings/MainPreferenceFragment.java b/mobile/src/main/java/com/alexstyl/specialdates/settings/MainPreferenceFragment.java index 97c27a30..6400036d 100644 --- a/mobile/src/main/java/com/alexstyl/specialdates/settings/MainPreferenceFragment.java +++ b/mobile/src/main/java/com/alexstyl/specialdates/settings/MainPreferenceFragment.java @@ -9,7 +9,8 @@ import com.alexstyl.specialdates.R; import com.alexstyl.specialdates.analytics.Action; import com.alexstyl.specialdates.analytics.ActionWithParameters; -import com.alexstyl.specialdates.analytics.Firebase; +import com.alexstyl.specialdates.analytics.Analytics; +import com.alexstyl.specialdates.analytics.AnalyticsProvider; import com.alexstyl.specialdates.events.namedays.NamedayLocale; import com.alexstyl.specialdates.events.namedays.NamedayPreferences; import com.alexstyl.specialdates.theming.MementoTheme; @@ -24,12 +25,13 @@ final public class MainPreferenceFragment extends MementoPreferenceFragment { private ListPreference namedayLanguageListPreferences; private NamedayPreferences namedaysPreferences; - private ThemingPreferences themingPreferences = new ThemingPreferences(); + private ThemingPreferences themingPreferences; + private Preference appThemePreference; private MainPreferenceActivity activity; - private Firebase analytics; + private Analytics analytics; @Override public void onAttach(Activity activity) { @@ -41,7 +43,8 @@ public void onAttach(Activity activity) { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); addPreferencesFromResource(R.xml.preference_main); - analytics = Firebase.get(getActivity()); + analytics = AnalyticsProvider.getAnalytics(getActivity()); + themingPreferences = ThemingPreferences.newInstance(getActivity()); Preference bankholidaysLanguage = findPreference(R.string.key_bankholidays_language); bankholidaysLanguage.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() { @Override diff --git a/mobile/src/main/java/com/alexstyl/specialdates/settings/RingtonePreference.java b/mobile/src/main/java/com/alexstyl/specialdates/settings/RingtonePreference.java new file mode 100644 index 00000000..296ecc08 --- /dev/null +++ b/mobile/src/main/java/com/alexstyl/specialdates/settings/RingtonePreference.java @@ -0,0 +1,25 @@ +package com.alexstyl.specialdates.settings; + +import android.content.Context; +import android.util.AttributeSet; + +public class RingtonePreference extends android.preference.RingtonePreference { + private OnPreferenceClickListener onPreferenceClickListener; + + public RingtonePreference(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + public void setOnPreferenceClickListener(OnPreferenceClickListener onPreferenceClickListener) { + // don't call super. We'll handle the clicks ourselves + this.onPreferenceClickListener = onPreferenceClickListener; + } + + @Override + protected void onClick() { + if (!onPreferenceClickListener.onPreferenceClick(this)) { + super.onClick(); + } + } +} diff --git a/mobile/src/main/java/com/alexstyl/specialdates/support/RateDialog.java b/mobile/src/main/java/com/alexstyl/specialdates/support/RateDialog.java index 74752226..fcfab539 100644 --- a/mobile/src/main/java/com/alexstyl/specialdates/support/RateDialog.java +++ b/mobile/src/main/java/com/alexstyl/specialdates/support/RateDialog.java @@ -10,6 +10,7 @@ import com.alexstyl.specialdates.Navigator; import com.alexstyl.specialdates.R; +import com.alexstyl.specialdates.analytics.AnalyticsProvider; import com.alexstyl.specialdates.ui.base.MementoActivity; import com.novoda.notils.caster.Views; @@ -17,17 +18,18 @@ public class RateDialog extends MementoActivity { private final String smiley = " " + Emoticon.SMILEY.asText(); private AskForSupport askForSupport; + private Navigator navigator; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_rate_dialog); askForSupport = new AskForSupport(context()); - + navigator = new Navigator(this, AnalyticsProvider.getAnalytics(this)); Views.findById(this, R.id.support_rate_button).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - new Navigator(v.getContext()).toPlayStore(); + navigator.toPlayStore(); Toast.makeText(context(), R.string.support_thanks_for_rating, Toast.LENGTH_LONG).show(); askForSupport.onRateEnd(); finish(); diff --git a/mobile/src/main/java/com/alexstyl/specialdates/support/SupportDonateDialog.java b/mobile/src/main/java/com/alexstyl/specialdates/support/SupportDonateDialog.java index 7426cdb6..5347bc75 100644 --- a/mobile/src/main/java/com/alexstyl/specialdates/support/SupportDonateDialog.java +++ b/mobile/src/main/java/com/alexstyl/specialdates/support/SupportDonateDialog.java @@ -15,7 +15,7 @@ import com.alexstyl.specialdates.analytics.Action; import com.alexstyl.specialdates.analytics.Analytics; import com.alexstyl.specialdates.analytics.ActionWithParameters; -import com.alexstyl.specialdates.analytics.Firebase; +import com.alexstyl.specialdates.analytics.AnalyticsProvider; import com.alexstyl.specialdates.billing.util.IabHelper; import com.alexstyl.specialdates.billing.util.IabResult; import com.alexstyl.specialdates.billing.util.Inventory; @@ -80,7 +80,7 @@ public void onQueryInventoryFinished(IabResult result, public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setTitle(R.string.donate); - analytics = Firebase.get(this); + analytics = AnalyticsProvider.getAnalytics(this); mTokens = new HashMap<>(); if (BuildConfig.DEBUG) { mTokens.put("1", ITEM_TEST); @@ -216,8 +216,4 @@ public void onDestroy() { } } - public static void displayDialog(Context context) { - Intent intent = new Intent(context, SupportDonateDialog.class); - context.startActivity(intent); - } } diff --git a/mobile/src/main/java/com/alexstyl/specialdates/support/SupportTranslateDialog.java b/mobile/src/main/java/com/alexstyl/specialdates/support/SupportTranslateDialog.java index eac9b76a..ebb00911 100644 --- a/mobile/src/main/java/com/alexstyl/specialdates/support/SupportTranslateDialog.java +++ b/mobile/src/main/java/com/alexstyl/specialdates/support/SupportTranslateDialog.java @@ -1,8 +1,6 @@ package com.alexstyl.specialdates.support; -import android.annotation.TargetApi; import android.content.Context; -import android.os.Build; import android.os.Bundle; import android.view.View; import android.widget.Toast; @@ -10,7 +8,6 @@ import com.alexstyl.specialdates.R; import com.alexstyl.specialdates.theming.Themer; import com.alexstyl.specialdates.ui.base.ThemedActivity; -import com.alexstyl.specialdates.util.Utils; import com.novoda.notils.caster.Views; public class SupportTranslateDialog extends ThemedActivity { @@ -19,7 +16,7 @@ public class SupportTranslateDialog extends ThemedActivity { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setContentView(R.layout.dialog_translate, Themer.get().getCurrentTheme()); + setContentView(R.layout.dialog_translate, Themer.get(this).getCurrentTheme()); Views.findById(this, android.R.id.content).setBackgroundColor(getResources().getColor(android.R.color.transparent)); Views.findById(this, R.id.button_ok).setOnClickListener(new View.OnClickListener() { @@ -38,18 +35,10 @@ public void onClick(View v) { } - @TargetApi(Build.VERSION_CODES.HONEYCOMB) public boolean copyLinkToClipboard() { - if (Utils.hasHoneycomb()) { - android.content.ClipboardManager clipboard = (android.content.ClipboardManager) - getSystemService(Context.CLIPBOARD_SERVICE); - android.content.ClipData clip = android.content.ClipData - .newPlainText(getResources().getString(R.string.app_name), getString(R.string.link_translate_short)); - clipboard.setPrimaryClip(clip); - } else { - android.text.ClipboardManager clipboard = (android.text.ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE); - clipboard.setText(getString(R.string.link_translate_short)); - } + android.content.ClipboardManager clipboard = (android.content.ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE); + android.content.ClipData clip = android.content.ClipData.newPlainText(getResources().getString(R.string.app_name), getString(R.string.link_translate_short)); + clipboard.setPrimaryClip(clip); return true; } diff --git a/mobile/src/main/java/com/alexstyl/specialdates/theming/MementoTheme.java b/mobile/src/main/java/com/alexstyl/specialdates/theming/MementoTheme.java index b2276ee6..461fb936 100644 --- a/mobile/src/main/java/com/alexstyl/specialdates/theming/MementoTheme.java +++ b/mobile/src/main/java/com/alexstyl/specialdates/theming/MementoTheme.java @@ -15,8 +15,14 @@ public enum MementoTheme { EGGPLANT_GREEN(withThemeName(R.string.theme_Eggplant), R.style.Theme_Memento_Eggplant), MONOCHROME(withThemeName(R.string.theme_Monochrome), R.style.Theme_Memento_Monochrome), SYSTEMO(withThemeName(R.string.theme_Systemo), R.style.Theme_Memento_Systemo), - AMBER(withThemeName(R.string.theme_Amber), R.style.Theme_Memento_Amber), - ; + AMBER(withThemeName(R.string.theme_Amber), R.style.Theme_Memento_Amber); + private final String themeName; + @StyleRes + private final int styleResId; + + private static String withThemeName(@StringRes int themNameResId) { + return MementoApplication.getContext().getString(themNameResId); + } public static MementoTheme fromName(@NonNull String themeName) { for (MementoTheme mementoTheme : values()) { @@ -28,14 +34,6 @@ public static MementoTheme fromName(@NonNull String themeName) { throw new DeveloperError("No theme exists with the name [%s]", themeName); } - private static String withThemeName(@StringRes int themNameResId) { - return MementoApplication.getAppContext().getString(themNameResId); - } - - private final String themeName; - @StyleRes - private final int styleResId; - MementoTheme(@NonNull String themeName, @StyleRes int styleResId) { this.themeName = themeName; this.styleResId = styleResId; diff --git a/mobile/src/main/java/com/alexstyl/specialdates/theming/Themer.java b/mobile/src/main/java/com/alexstyl/specialdates/theming/Themer.java index 1c135be0..7531406e 100644 --- a/mobile/src/main/java/com/alexstyl/specialdates/theming/Themer.java +++ b/mobile/src/main/java/com/alexstyl/specialdates/theming/Themer.java @@ -14,9 +14,10 @@ public class Themer { private static Themer INSTANCE; - public static Themer get() { + public static Themer get(Context context) { if (INSTANCE == null) { - INSTANCE = new Themer(new ThemingPreferences(), new AttributeExtractor()); + ThemingPreferences preferences = ThemingPreferences.newInstance(context); + INSTANCE = new Themer(preferences, new AttributeExtractor()); } return INSTANCE; } diff --git a/mobile/src/main/java/com/alexstyl/specialdates/theming/ThemingPreferences.java b/mobile/src/main/java/com/alexstyl/specialdates/theming/ThemingPreferences.java index fc55ace3..47f1ce9d 100644 --- a/mobile/src/main/java/com/alexstyl/specialdates/theming/ThemingPreferences.java +++ b/mobile/src/main/java/com/alexstyl/specialdates/theming/ThemingPreferences.java @@ -1,16 +1,21 @@ package com.alexstyl.specialdates.theming; +import android.content.Context; + import com.alexstyl.specialdates.EasyPreferences; -import com.alexstyl.specialdates.MementoApplication; import com.alexstyl.specialdates.R; -public class ThemingPreferences { +public final class ThemingPreferences { private static final String DEFAULT_THEME = MementoTheme.CHERRY_RED.getThemeName(); private final EasyPreferences preferences; - public ThemingPreferences() { - this.preferences = EasyPreferences.createForDefaultPreferences(MementoApplication.getAppContext()); + public static ThemingPreferences newInstance(Context context) { + return new ThemingPreferences(EasyPreferences.createForDefaultPreferences(context)); + } + + private ThemingPreferences(EasyPreferences defaultPreferences) { + this.preferences = defaultPreferences; } public MementoTheme getSelectedTheme() { diff --git a/mobile/src/main/java/com/alexstyl/specialdates/theming/ViewTinter.java b/mobile/src/main/java/com/alexstyl/specialdates/theming/ViewTinter.java new file mode 100644 index 00000000..5bab4e53 --- /dev/null +++ b/mobile/src/main/java/com/alexstyl/specialdates/theming/ViewTinter.java @@ -0,0 +1,19 @@ +package com.alexstyl.specialdates.theming; + +import android.content.Context; +import android.graphics.PorterDuff; +import android.graphics.drawable.Drawable; + +public class ViewTinter { + private final AttributeExtractor extractor; + + public ViewTinter(AttributeExtractor extractor) { + this.extractor = extractor; + } + + public Drawable createAccentTintedDrawable(Drawable mutate, Context context) { + int accentColor = extractor.extractAccentColorFrom(context); + mutate.setColorFilter(accentColor, PorterDuff.Mode.SRC_IN); + return mutate; + } +} diff --git a/mobile/src/main/java/com/alexstyl/specialdates/transition/FadeInTransition.java b/mobile/src/main/java/com/alexstyl/specialdates/transition/FadeInTransition.java new file mode 100644 index 00000000..eb363f12 --- /dev/null +++ b/mobile/src/main/java/com/alexstyl/specialdates/transition/FadeInTransition.java @@ -0,0 +1,19 @@ +package com.alexstyl.specialdates.transition; + +import android.support.transition.AutoTransition; +import android.support.transition.Transition; + +public class FadeInTransition extends AutoTransition { + + private static final int FADE_IN_DURATION = 200; + + private FadeInTransition() { + // force callers to call the factory method to instantiate this class + } + + public static Transition createTransition() { + AutoTransition transition = new AutoTransition(); + transition.setDuration(FADE_IN_DURATION); + return transition; + } +} diff --git a/mobile/src/main/java/com/alexstyl/specialdates/transition/FadeOutTransition.java b/mobile/src/main/java/com/alexstyl/specialdates/transition/FadeOutTransition.java new file mode 100644 index 00000000..04632563 --- /dev/null +++ b/mobile/src/main/java/com/alexstyl/specialdates/transition/FadeOutTransition.java @@ -0,0 +1,25 @@ +package com.alexstyl.specialdates.transition; + +import android.support.transition.AutoTransition; +import android.support.transition.Transition; + +public class FadeOutTransition extends AutoTransition { + + private FadeOutTransition() { + // force callers to call the factory method to instantiate this class + } + + private static final int FADE_OUT_DURATION = 250; + + /** + * Creates a AutoTransition that calls the {@linkplain Transition.TransitionListener#onTransitionEnd(Transition)} + * of the passing Listener when complete + */ + public static Transition withAction(TransitionListener finishingAction) { + AutoTransition transition = new AutoTransition(); + transition.setDuration(FADE_OUT_DURATION); + transition.addListener(finishingAction); + return transition; + } + +} diff --git a/mobile/src/main/java/com/alexstyl/specialdates/transition/SimpleTransitionListener.java b/mobile/src/main/java/com/alexstyl/specialdates/transition/SimpleTransitionListener.java new file mode 100644 index 00000000..570412cf --- /dev/null +++ b/mobile/src/main/java/com/alexstyl/specialdates/transition/SimpleTransitionListener.java @@ -0,0 +1,32 @@ +package com.alexstyl.specialdates.transition; + +import android.support.transition.Transition; + +import static android.support.transition.Transition.TransitionListener; + +public class SimpleTransitionListener implements TransitionListener { + @Override + public void onTransitionStart(Transition transition) { + // do nothing + } + + @Override + public void onTransitionPause(Transition transition) { + // do nothing + } + + @Override + public void onTransitionResume(Transition transition) { + // do nothing + } + + @Override + public void onTransitionEnd(Transition transition) { + // do nothing + } + + @Override + public void onTransitionCancel(Transition transition) { + // do nothing + } +} diff --git a/mobile/src/main/java/com/alexstyl/specialdates/ui/ThemeMonitor.java b/mobile/src/main/java/com/alexstyl/specialdates/ui/ThemeMonitor.java new file mode 100644 index 00000000..0876953d --- /dev/null +++ b/mobile/src/main/java/com/alexstyl/specialdates/ui/ThemeMonitor.java @@ -0,0 +1,25 @@ +package com.alexstyl.specialdates.ui; + +import com.alexstyl.specialdates.theming.MementoTheme; +import com.alexstyl.specialdates.theming.ThemingPreferences; + +public final class ThemeMonitor { + + private final MementoTheme theme; + private final ThemingPreferences preferences; + + public static ThemeMonitor startMonitoring(ThemingPreferences preferences) { + MementoTheme currentTheme = preferences.getSelectedTheme(); + return new ThemeMonitor(currentTheme, preferences); + } + + private ThemeMonitor(MementoTheme theme, ThemingPreferences preferences) { + this.theme = theme; + this.preferences = preferences; + } + + public boolean hasThemeChanged() { + MementoTheme newTheme = preferences.getSelectedTheme(); + return newTheme != theme; + } +} diff --git a/mobile/src/main/java/com/alexstyl/specialdates/ui/ThemeReapplier.java b/mobile/src/main/java/com/alexstyl/specialdates/ui/ThemeReapplier.java deleted file mode 100644 index 4d1106ae..00000000 --- a/mobile/src/main/java/com/alexstyl/specialdates/ui/ThemeReapplier.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.alexstyl.specialdates.ui; - -import android.content.Context; - -import com.alexstyl.specialdates.theming.MementoTheme; -import com.alexstyl.specialdates.theming.ThemingPreferences; - -public class ThemeReapplier { - - private final Context context; - - private final MementoTheme theme; - private final ThemingPreferences themingPreferences; - - public ThemeReapplier(Context context) { - this.context = context.getApplicationContext(); - this.themingPreferences = new ThemingPreferences(); - this.theme = themingPreferences.getSelectedTheme(); - } - - public boolean hasThemeChanged() { - MementoTheme newTheme = themingPreferences.getSelectedTheme(); - return newTheme != theme; - } -} diff --git a/mobile/src/main/java/com/alexstyl/specialdates/ui/ViewFader.java b/mobile/src/main/java/com/alexstyl/specialdates/ui/ViewFader.java new file mode 100644 index 00000000..1f7a78c8 --- /dev/null +++ b/mobile/src/main/java/com/alexstyl/specialdates/ui/ViewFader.java @@ -0,0 +1,22 @@ +package com.alexstyl.specialdates.ui; + +import android.view.ViewGroup; + +import static android.view.View.GONE; +import static android.view.View.VISIBLE; + +public final class ViewFader { + + public void hideContentOf(ViewGroup viewGroup) { + for (int i = 0; i < viewGroup.getChildCount(); i++) { + viewGroup.getChildAt(i).setVisibility(GONE); + } + } + + public void showContent(ViewGroup viewGroup) { + for (int i = 0; i < viewGroup.getChildCount(); i++) { + viewGroup.getChildAt(i).setVisibility(VISIBLE); + } + } + +} diff --git a/mobile/src/main/java/com/alexstyl/specialdates/ui/activity/MainActivity.java b/mobile/src/main/java/com/alexstyl/specialdates/ui/activity/MainActivity.java index 3d65b75e..d0bedcea 100644 --- a/mobile/src/main/java/com/alexstyl/specialdates/ui/activity/MainActivity.java +++ b/mobile/src/main/java/com/alexstyl/specialdates/ui/activity/MainActivity.java @@ -1,28 +1,33 @@ package com.alexstyl.specialdates.ui.activity; -import android.content.Intent; import android.os.Bundle; import android.support.design.widget.FloatingActionButton; -import android.support.v7.widget.Toolbar; import android.view.Menu; import android.view.MenuItem; import android.view.View; +import android.view.ViewGroup; +import com.alexstyl.specialdates.Navigator; import com.alexstyl.specialdates.R; -import com.alexstyl.specialdates.about.AboutActivity; -import com.alexstyl.specialdates.addevent.AddBirthdayActivity; import com.alexstyl.specialdates.analytics.Analytics; -import com.alexstyl.specialdates.analytics.Firebase; +import com.alexstyl.specialdates.analytics.AnalyticsProvider; import com.alexstyl.specialdates.analytics.Screen; -import com.alexstyl.specialdates.settings.MainPreferenceActivity; +import com.alexstyl.specialdates.events.namedays.NamedayPreferences; +import com.alexstyl.specialdates.search.SearchHintCreator; import com.alexstyl.specialdates.support.AskForSupport; import com.alexstyl.specialdates.support.SupportDonateDialog; -import com.alexstyl.specialdates.ui.ThemeReapplier; +import com.alexstyl.specialdates.theming.ThemingPreferences; +import com.alexstyl.specialdates.ui.ThemeMonitor; +import com.alexstyl.specialdates.ui.ViewFader; import com.alexstyl.specialdates.ui.base.ThemedActivity; -import com.alexstyl.specialdates.upcoming.UpcomingEventsFragment; +import com.alexstyl.specialdates.upcoming.ExposedSearchToolbar; +import com.alexstyl.specialdates.upcoming.SearchTransitioner; import com.alexstyl.specialdates.util.Notifier; import com.alexstyl.specialdates.widgetprovider.TodayWidgetProvider; import com.novoda.notils.caster.Views; +import com.novoda.notils.meta.AndroidUtils; + +import static android.view.View.OnClickListener; /* * The activity was first launched with MainActivity being in package.ui.activity @@ -31,49 +36,49 @@ public class MainActivity extends ThemedActivity { private Notifier notifier; - private UpcomingEventsFragment upcomingEventsFragment; private AskForSupport askForSupport; - private ThemeReapplier reapplier; - private Analytics analytics; + private ThemeMonitor themeMonitor; + + private Navigator navigator; + + private SearchTransitioner searchTransitioner; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); - reapplier = new ThemeReapplier(this); - analytics = Firebase.get(this); + themeMonitor = ThemeMonitor.startMonitoring(ThemingPreferences.newInstance(this)); + Analytics analytics = AnalyticsProvider.getAnalytics(this); analytics.trackScreen(Screen.HOME); - Toolbar toolbar = Views.findById(this, R.id.memento_toolbar); + navigator = new Navigator(this, analytics); + + ExposedSearchToolbar toolbar = Views.findById(this, R.id.memento_toolbar); + toolbar.setOnClickListener(onToolbarClickListener); setSupportActionBar(toolbar); + ViewGroup activityContent = Views.findById(this, R.id.main_content); + searchTransitioner = new SearchTransitioner(this, navigator, activityContent, toolbar, new ViewFader()); + notifier = Notifier.newInstance(this); - FloatingActionButton floatingActionButton = Views.findById(this, R.id.fab_add); - floatingActionButton.setOnClickListener(startAddBirthdayOnClick); + FloatingActionButton addBirthdayFAB = Views.findById(this, R.id.main_birthday_add_fab); + addBirthdayFAB.setOnClickListener(startAddBirthdayOnClick); askForSupport = new AskForSupport(this); - upcomingEventsFragment = (UpcomingEventsFragment) getSupportFragmentManager().findFragmentById(R.id.upcoming); + SearchHintCreator hintCreator = new SearchHintCreator(getResources(), NamedayPreferences.newInstance(this)); + setTitle(hintCreator.createHint()); } @Override protected void onResume() { super.onResume(); - if (reapplier.hasThemeChanged()) { + if (themeMonitor.hasThemeChanged()) { reapplyTheme(); } else if (askForSupport.shouldAskForRating()) { askForSupport.askForRatingFromUser(this); } - } - - @Override - protected void onPostResume() { - super.onPostResume(); - } - - private void startAddBirthdayActivity() { - Intent intent = new Intent(this, AddBirthdayActivity.class); - startActivity(intent); + searchTransitioner.onActivityResumed(); } @Override @@ -89,37 +94,22 @@ public boolean onCreateOptionsMenu(Menu menu) { public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.action_settings: - openSettings(); + navigator.toSettings(); break; case R.id.action_about: - openAboutScreen(); + navigator.toAbout(); break; case R.id.action_donate: - openDonateDialog(); + navigator.toDonateDialog(); break; } return super.onOptionsItemSelected(item); } - private void openDonateDialog() { - analytics.trackScreen(Screen.DONATE); - SupportDonateDialog.displayDialog(this); - } - - private void openAboutScreen() { - analytics.trackScreen(Screen.ABOUT); - startActivity(new Intent(this, AboutActivity.class)); - - } - - private void openSettings() { - analytics.trackScreen(Screen.SETTINGS); - startActivity(new Intent(this, MainPreferenceActivity.class)); - } - @Override public boolean onSearchRequested() { - return upcomingEventsFragment.onSearchRequested(); + searchTransitioner.transitionToSearch(); + return true; } @Override @@ -136,10 +126,19 @@ protected void onDestroy() { } } - private final View.OnClickListener startAddBirthdayOnClick = new View.OnClickListener() { + private final OnClickListener onToolbarClickListener = new OnClickListener() { + @Override + public void onClick(View v) { + AndroidUtils.toggleKeyboard(v.getContext()); + onSearchRequested(); + } + + }; + + private final OnClickListener startAddBirthdayOnClick = new OnClickListener() { @Override public void onClick(View v) { - startAddBirthdayActivity(); + navigator.toAddBirthday(); } }; } diff --git a/mobile/src/main/java/com/alexstyl/specialdates/ui/base/MementoActivity.java b/mobile/src/main/java/com/alexstyl/specialdates/ui/base/MementoActivity.java index ec2c5c07..dace1112 100644 --- a/mobile/src/main/java/com/alexstyl/specialdates/ui/base/MementoActivity.java +++ b/mobile/src/main/java/com/alexstyl/specialdates/ui/base/MementoActivity.java @@ -3,12 +3,16 @@ import android.content.Context; import android.content.Intent; import android.os.Bundle; +import android.support.v4.app.Fragment; import android.support.v7.app.ActionBar; import android.support.v7.app.AppCompatActivity; import android.view.MenuItem; +import com.alexstyl.specialdates.util.Utils; import com.novoda.notils.exception.DeveloperError; +import java.util.List; + public class MementoActivity extends AppCompatActivity { /** @@ -40,6 +44,17 @@ public boolean onOptionsItemSelected(MenuItem item) { return super.onOptionsItemSelected(item); } + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + List