package com.automattic.simplenote;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.preference.PreferenceManager;
import android.support.design.widget.NavigationView;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.content.ContextCompat;
import android.support.v4.view.GravityCompat;
import android.support.v4.view.MenuItemCompat;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBar;
import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.SearchView;
import android.support.v7.widget.Toolbar;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.TextUtils;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.WindowManager;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.ProgressBar;
import com.automattic.simplenote.analytics.AnalyticsTracker;
import com.automattic.simplenote.models.Note;
import com.automattic.simplenote.models.Tag;
import com.automattic.simplenote.utils.AniUtils;
import com.automattic.simplenote.utils.DisplayUtils;
import com.automattic.simplenote.utils.DrawableUtils;
import com.automattic.simplenote.utils.HtmlCompat;
import com.automattic.simplenote.utils.PrefUtils;
import com.automattic.simplenote.utils.StrUtils;
import com.automattic.simplenote.utils.TagsAdapter;
import com.automattic.simplenote.utils.ThemeUtils;
import com.automattic.simplenote.utils.UndoBarController;
import com.automattic.simplenote.widgets.TypefaceSpan;
import com.simperium.Simperium;
import com.simperium.android.LoginActivity;
import com.simperium.client.Bucket;
import com.simperium.client.BucketObjectMissingException;
import com.simperium.client.BucketObjectNameInvalid;
import com.simperium.client.Query;
import com.simperium.client.User;
import org.wordpress.passcodelock.AppLockManager;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
public class NotesActivity extends AppCompatActivity implements
NoteListFragment.Callbacks, User.StatusChangeListener, Simperium.OnUserCreatedListener, UndoBarController.UndoListener,
Bucket.Listener<Note> {
public static String TAG_NOTE_LIST = "noteList";
public static String TAG_NOTE_EDITOR = "noteEditor";
protected Bucket<Note> mNotesBucket;
protected Bucket<Tag> mTagsBucket;
private int TRASH_SELECTED_ID = 1;
private boolean mIsShowingMarkdown;
private boolean mShouldSelectNewNote;
private String mTabletSearchQuery;
private UndoBarController mUndoBarController;
private View mFragmentsContainer;
private SearchView mSearchView;
private MenuItem mSearchMenuItem;
private NoteListFragment mNoteListFragment;
private NoteEditorFragment mNoteEditorFragment;
private Note mCurrentNote;
private MenuItem mEmptyTrashMenuItem;
// Menu drawer
private DrawerLayout mDrawerLayout;
private ListView mDrawerList;
private NavigationView mNavigationView;
private ActionBarDrawerToggle mDrawerToggle;
private TagsAdapter mTagsAdapter;
private TagsAdapter.TagMenuItem mSelectedTag;
// Tags bucket listener
private Bucket.Listener<Tag> mTagsMenuUpdater = new Bucket.Listener<Tag>() {
void updateNavigationDrawer() {
runOnUiThread(new Runnable() {
public void run() {
updateNavigationDrawerItems();
}
});
}
@Override
public void onSaveObject(Bucket<Tag> bucket, Tag tag) {
updateNavigationDrawer();
}
@Override
public void onDeleteObject(Bucket<Tag> bucket, Tag tag) {
updateNavigationDrawer();
}
@Override
public void onNetworkChange(Bucket<Tag> bucket, Bucket.ChangeType type, String key) {
updateNavigationDrawer();
}
@Override
public void onBeforeUpdateObject(Bucket<Tag> bucket, Tag object) {
// noop
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
// On lollipop, configure the translucent status bar
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
getWindow().setStatusBarColor(ContextCompat.getColor(this, R.color.transparent));
}
ThemeUtils.setTheme(this);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_notes);
mFragmentsContainer = findViewById(R.id.note_fragment_container);
Simplenote currentApp = (Simplenote) getApplication();
if (mNotesBucket == null) {
mNotesBucket = currentApp.getNotesBucket();
}
if (mTagsBucket == null) {
mTagsBucket = currentApp.getTagsBucket();
}
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
configureNavigationDrawer(toolbar);
if (savedInstanceState == null) {
mNoteListFragment = new NoteListFragment();
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.add(R.id.note_fragment_container, mNoteListFragment, TAG_NOTE_LIST);
fragmentTransaction.commit();
} else {
mNoteListFragment = (NoteListFragment) getSupportFragmentManager().findFragmentByTag(TAG_NOTE_LIST);
}
if (DisplayUtils.isLargeScreen(this)) {
if (getSupportFragmentManager().findFragmentByTag(TAG_NOTE_EDITOR) != null) {
mNoteEditorFragment = (NoteEditorFragment) getSupportFragmentManager().findFragmentByTag(TAG_NOTE_EDITOR);
} else if (DisplayUtils.isLandscape(this)) {
addEditorFragment();
}
}
// enable ActionBar app icon to behave as action to toggle nav drawer
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.setDisplayHomeAsUpEnabled(true);
actionBar.setHomeButtonEnabled(true);
// Add loading indicator to show when indexing
ProgressBar progressBar = (ProgressBar) getLayoutInflater().inflate(R.layout.progressbar_toolbar, null);
actionBar.setDisplayShowCustomEnabled(true);
actionBar.setCustomView(progressBar);
setToolbarProgressVisibility(false);
}
mUndoBarController = new UndoBarController(this);
// Creates 'Welcome' note
checkForFirstLaunch();
checkForSharedContent();
currentApp.getSimperium().setOnUserCreatedListener(this);
currentApp.getSimperium().setUserStatusChangeListener(this);
}
@Override
protected void onResume() {
super.onResume();
// Ensure user has valid authorization
if (userAuthenticationIsInvalid()) {
startLoginActivity(true);
}
mNotesBucket.start();
mTagsBucket.start();
mNotesBucket.addOnNetworkChangeListener(this);
mNotesBucket.addOnSaveObjectListener(this);
mNotesBucket.addOnDeleteObjectListener(this);
mTagsBucket.addListener(mTagsMenuUpdater);
updateNavigationDrawerItems();
// if the user is not authenticated and the tag doesn't exist revert to default drawer selection
if (userIsUnauthorized()) {
if (-1 == mTagsAdapter.getPosition(mSelectedTag)) {
mSelectedTag = null;
mDrawerList.setSelection(TagsAdapter.DEFAULT_ITEM_POSITION);
}
}
setSelectedTagActive();
if (mCurrentNote != null && mShouldSelectNewNote) {
onNoteSelected(mCurrentNote.getSimperiumKey(), 0, true, null, mCurrentNote.isMarkdownEnabled());
mShouldSelectNewNote = false;
}
if (mIsShowingMarkdown) {
setMarkdownShowing(false);
}
}
@Override
protected void onPause() {
super.onPause();
mTagsBucket.removeListener(mTagsMenuUpdater);
mNotesBucket.removeOnNetworkChangeListener(this);
mNotesBucket.removeOnSaveObjectListener(this);
mNotesBucket.removeOnDeleteObjectListener(this);
}
@Override
public void setTitle(CharSequence title) {
if (title == null) {
title = "";
}
setTitleWithCustomFont(title);
}
@Override
public void onBackPressed() {
if (mDrawerLayout != null && mDrawerLayout.isDrawerOpen(GravityCompat.START)) {
mDrawerLayout.closeDrawer(GravityCompat.START);
} else {
super.onBackPressed();
}
}
private void setTitleWithCustomFont(CharSequence title) {
if (getSupportActionBar() == null) return;
// LG devices running 4.1 can't handle a custom font in the action bar title
if ((!TextUtils.isEmpty(Build.BRAND) && Build.BRAND.toLowerCase().contains("lge"))
&& Build.VERSION.SDK_INT <= Build.VERSION_CODES.JELLY_BEAN) {
getSupportActionBar().setTitle(title);
return;
}
SpannableString s = new SpannableString(title);
s.setSpan(new TypefaceSpan(this), 0, s.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
getSupportActionBar().setTitle(s);
}
private void configureNavigationDrawer(Toolbar toolbar) {
mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START);
mNavigationView = (NavigationView) findViewById(R.id.navigation_view);
mDrawerList = (ListView) findViewById(R.id.drawer_list);
View settingsButton = findViewById(R.id.nav_settings);
settingsButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent i = new Intent(NotesActivity.this, PreferencesActivity.class);
startActivityForResult(i, Simplenote.INTENT_PREFERENCES);
}
});
mNavigationView.getLayoutParams().width = ThemeUtils.getOptimalDrawerWidth(this);
mTagsAdapter = new TagsAdapter(this, mNotesBucket, mDrawerList.getHeaderViewsCount());
mDrawerList.setAdapter(mTagsAdapter);
// Set the list's click listener
mDrawerList.setOnItemClickListener(new DrawerItemClickListener());
if (mSelectedTag == null)
mSelectedTag = mTagsAdapter.getDefaultItem();
mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout, toolbar, R.string.open_drawer,
R.string.close_drawer) {
public void onDrawerClosed(View view) {
supportInvalidateOptionsMenu();
}
public void onDrawerOpened(View drawerView) {
// noop
}
@Override
public void onDrawerSlide(View drawerView, float slideOffset) {
super.onDrawerSlide(drawerView, 0f);
}
};
mDrawerLayout.addDrawerListener(mDrawerToggle);
}
private void checkForFirstLaunch() {
if (PrefUtils.getBoolPref(this, PrefUtils.PREF_FIRST_LAUNCH, true)) {
// Create the welcome note
try {
Note welcomeNote = mNotesBucket.newObject("welcome-android");
welcomeNote.setCreationDate(Calendar.getInstance());
welcomeNote.setModificationDate(welcomeNote.getCreationDate());
welcomeNote.setContent(getString(R.string.welcome_note));
welcomeNote.getTitle();
welcomeNote.save();
} catch (BucketObjectNameInvalid e) {
// this won't happen because welcome-android is a valid name
}
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
SharedPreferences.Editor editor = preferences.edit();
editor.putBoolean(PrefUtils.PREF_FIRST_LAUNCH, false);
editor.putBoolean(PrefUtils.PREF_ACCOUNT_REQUIRED, true);
editor.apply();
}
}
private void checkForSharedContent() {
if (getIntent().hasExtra(Intent.EXTRA_TEXT)) {
// Check share action
Intent intent = getIntent();
String subject = intent.getStringExtra(Intent.EXTRA_SUBJECT);
String text = intent.getStringExtra(Intent.EXTRA_TEXT);
// Don't add the 'Note to self' subject or open the note if this was shared from a voice search
String intentAction = StrUtils.notNullStr(intent.getAction());
boolean isVoiceShare = intentAction.equals("com.google.android.gm.action.AUTO_SEND");
if (!TextUtils.isEmpty(text)) {
if (!TextUtils.isEmpty(subject) && !isVoiceShare) {
text = subject + "\n\n" + text;
}
Note note = mNotesBucket.newObject();
note.setCreationDate(Calendar.getInstance());
note.setModificationDate(note.getCreationDate());
note.setContent(text);
note.save();
setCurrentNote(note);
mShouldSelectNewNote = true;
AnalyticsTracker.track(
AnalyticsTracker.Stat.LIST_NOTE_CREATED,
AnalyticsTracker.CATEGORY_NOTE,
"external_share"
);
if (!DisplayUtils.isLargeScreenLandscape(this)) {
// Disable the lock screen when sharing content and opening NoteEditorActivity
// Lock screen activities are enabled again in NoteEditorActivity.onPause()
if (AppLockManager.getInstance().isAppLockFeatureEnabled()) {
AppLockManager.getInstance().getAppLock().setExemptActivities(
new String[]{"com.automattic.simplenote.NotesActivity",
"com.automattic.simplenote.NoteEditorActivity"});
AppLockManager.getInstance().getAppLock().setOneTimeTimeout(0);
}
}
}
}
}
private void updateNavigationDrawerItems() {
Bucket.ObjectCursor<Tag> tagCursor = Tag.allWithCount(mTagsBucket).execute();
mTagsAdapter.changeCursor(tagCursor);
}
private void setSelectedTagActive() {
if (mSelectedTag == null)
mSelectedTag = mTagsAdapter.getDefaultItem();
setTitle(mSelectedTag.name);
mDrawerList.setItemChecked(mTagsAdapter.getPosition(mSelectedTag) + mDrawerList.getHeaderViewsCount(), true);
}
public TagsAdapter.TagMenuItem getSelectedTag() {
if (mSelectedTag == null) {
mSelectedTag = mTagsAdapter.getDefaultItem();
}
return mSelectedTag;
}
// Enable or disable the trash action bar button depending on if there are deleted notes or not
public void updateTrashMenuItem() {
if (mEmptyTrashMenuItem == null || mNotesBucket == null)
return;
// Disable the trash icon if there are no notes trashed.
Query<Note> query = Note.allDeleted(mNotesBucket);
if (query.count() == 0) {
mEmptyTrashMenuItem.getIcon().setAlpha(127);
mEmptyTrashMenuItem.setEnabled(false);
} else {
mEmptyTrashMenuItem.getIcon().setAlpha(255);
mEmptyTrashMenuItem.setEnabled(true);
}
}
private void addEditorFragment() {
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
mNoteEditorFragment = new NoteEditorFragment();
ft.add(R.id.note_fragment_container, mNoteEditorFragment, TAG_NOTE_EDITOR);
ft.commitAllowingStateLoss();
fm.executePendingTransactions();
}
private boolean userAccountRequired() {
return PrefUtils.getBoolPref(this, PrefUtils.PREF_ACCOUNT_REQUIRED, false);
}
/**
* Checks for a previously valid user that is now not authenticated
* Also checks if user account is required (added in version 1.5.6)
*
* @return true if user has invalid authorization
*/
private boolean userAuthenticationIsInvalid() {
Simplenote currentApp = (Simplenote) getApplication();
Simperium simperium = currentApp.getSimperium();
User user = simperium.getUser();
boolean isNotAuthorized = user.getStatus().equals(User.Status.NOT_AUTHORIZED);
return (user.hasAccessToken() && isNotAuthorized) ||
(userAccountRequired() && isNotAuthorized);
}
private boolean userIsUnauthorized() {
Simplenote currentApp = (Simplenote) getApplication();
return currentApp.getSimperium().getUser().getStatus() == User.Status.NOT_AUTHORIZED;
}
public void setCurrentNote(Note note) {
mCurrentNote = note;
}
public NoteListFragment getNoteListFragment() {
return mNoteListFragment;
}
@SuppressWarnings("ResourceType")
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.notes_list, menu);
// restore the search query if on a landscape tablet
String searchQuery = null;
if (DisplayUtils.isLargeScreenLandscape(this) && mSearchView != null)
searchQuery = mSearchView.getQuery().toString();
mSearchMenuItem = menu.findItem(R.id.menu_search);
mSearchView = (SearchView) mSearchMenuItem.getActionView();
if (!TextUtils.isEmpty(searchQuery)) {
mSearchView.setQuery(searchQuery, false);
mSearchMenuItem.expandActionView();
} else {
// Workaround for setting the search placeholder text color
String hintHexColor = (ThemeUtils.isLightTheme(this) ?
getString(R.color.simplenote_light_grey) :
getString(R.color.simplenote_text_preview)).replace("ff", "");
mSearchView.setQueryHint(HtmlCompat.fromHtml(String.format("<font color=\"%s\">%s</font>",
hintHexColor,
getString(R.string.search))));
}
mSearchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override
public boolean onQueryTextChange(String newText) {
if (mSearchMenuItem.isActionViewExpanded()) {
getNoteListFragment().searchNotes(newText);
}
return true;
}
@Override
public boolean onQueryTextSubmit(String queryText) {
getNoteListFragment().searchNotes(queryText);
return true;
}
});
MenuItemCompat.setOnActionExpandListener(mSearchMenuItem, new MenuItemCompat.OnActionExpandListener() {
@Override
public boolean onMenuItemActionExpand(MenuItem menuItem) {
checkEmptyListText(true);
if (mNoteListFragment != null) {
mNoteListFragment.setFloatingActionButtonVisible(false);
}
AnalyticsTracker.track(
AnalyticsTracker.Stat.LIST_NOTES_SEARCHED,
AnalyticsTracker.CATEGORY_NOTE,
"action_bar_search_tap"
);
return true;
}
@Override
public boolean onMenuItemActionCollapse(MenuItem menuItem) {
// Show all notes again
if (mNoteListFragment != null) {
mNoteListFragment.setFloatingActionButtonVisible(true);
}
mTabletSearchQuery = "";
mSearchView.setQuery("", false);
checkEmptyListText(false);
getNoteListFragment().clearSearch();
return true;
}
});
mSearchMenuItem.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
if (!mSearchMenuItem.isActionViewExpanded())
showDetailPlaceholder();
return false;
}
});
MenuItem trashItem = menu.findItem(R.id.menu_delete).setTitle(R.string.undelete);
if (mCurrentNote != null && mCurrentNote.isDeleted())
trashItem.setTitle(R.string.undelete);
else
trashItem.setTitle(R.string.delete);
if (DisplayUtils.isLargeScreenLandscape(this)) {
// Restore the search query on landscape tablets
if (!TextUtils.isEmpty(mTabletSearchQuery)) {
mSearchMenuItem.expandActionView();
mSearchView.setQuery(mTabletSearchQuery, false);
mSearchView.clearFocus();
}
if (mCurrentNote != null) {
menu.findItem(R.id.menu_share).setVisible(true);
menu.findItem(R.id.menu_view_info).setVisible(true);
menu.findItem(R.id.menu_history).setVisible(true);
menu.findItem(R.id.menu_markdown_preview).setVisible(mCurrentNote.isMarkdownEnabled());
trashItem.setVisible(true);
} else {
menu.findItem(R.id.menu_share).setVisible(false);
menu.findItem(R.id.menu_view_info).setVisible(false);
menu.findItem(R.id.menu_history).setVisible(false);
menu.findItem(R.id.menu_markdown_preview).setVisible(false);
trashItem.setVisible(false);
}
menu.findItem(R.id.menu_empty_trash).setVisible(false);
} else {
menu.findItem(R.id.menu_search).setVisible(true);
menu.findItem(R.id.menu_share).setVisible(false);
menu.findItem(R.id.menu_view_info).setVisible(false);
menu.findItem(R.id.menu_history).setVisible(false);
menu.findItem(R.id.menu_markdown_preview).setVisible(false);
trashItem.setVisible(false);
menu.findItem(R.id.menu_empty_trash).setVisible(false);
}
// Are we looking at the trash? Adjust menu accordingly.
if (mDrawerList.getCheckedItemPosition() == TRASH_SELECTED_ID) {
mEmptyTrashMenuItem = menu.findItem(R.id.menu_empty_trash);
mEmptyTrashMenuItem.setVisible(true);
updateTrashMenuItem();
menu.findItem(R.id.menu_search).setVisible(false);
menu.findItem(R.id.menu_share).setVisible(false);
menu.findItem(R.id.menu_history).setVisible(false);
}
DrawableUtils.tintMenuWithAttribute(this, menu, R.attr.actionBarTextColor);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (mDrawerToggle.onOptionsItemSelected(item)) {
return true;
}
switch (item.getItemId()) {
case R.id.menu_markdown_preview:
if (mIsShowingMarkdown) {
item.setIcon(R.drawable.ic_markdown_outline_24dp);
item.setTitle(getString(R.string.markdown_show));
setMarkdownShowing(false);
} else {
item.setIcon(R.drawable.ic_markdown_solid_24dp);
item.setTitle(getString(R.string.markdown_hide));
setMarkdownShowing(true);
}
DrawableUtils.tintMenuItemWithAttribute(this, item, R.attr.actionBarTextColor);
return true;
case R.id.menu_delete:
if (mNoteEditorFragment != null) {
if (mCurrentNote != null) {
mCurrentNote.setDeleted(!mCurrentNote.isDeleted());
mCurrentNote.setModificationDate(Calendar.getInstance());
mCurrentNote.save();
if (mCurrentNote.isDeleted()) {
List<String> deletedNoteIds = new ArrayList<>();
deletedNoteIds.add(mCurrentNote.getSimperiumKey());
mUndoBarController.setDeletedNoteIds(deletedNoteIds);
mUndoBarController.showUndoBar(getUndoView(), getString(R.string.note_deleted));
AnalyticsTracker.track(
AnalyticsTracker.Stat.LIST_NOTE_DELETED,
AnalyticsTracker.CATEGORY_NOTE,
"overflow_menu"
);
} else {
AnalyticsTracker.track(
AnalyticsTracker.Stat.EDITOR_NOTE_RESTORED,
AnalyticsTracker.CATEGORY_NOTE,
"overflow_menu"
);
}
showDetailPlaceholder();
}
NoteListFragment fragment = getNoteListFragment();
if (fragment != null) {
fragment.getPrefs();
fragment.refreshList();
}
}
return true;
case R.id.menu_empty_trash:
AlertDialog.Builder alert = new AlertDialog.Builder(this);
alert.setTitle(R.string.empty_trash);
alert.setMessage(R.string.confirm_empty_trash);
alert.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
new emptyTrashTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
AnalyticsTracker.track(
AnalyticsTracker.Stat.LIST_TRASH_EMPTIED,
AnalyticsTracker.CATEGORY_NOTE,
"overflow_menu"
);
}
});
alert.setNegativeButton(R.string.no, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
// Do nothing, just closing the dialog
}
});
alert.show();
return true;
case android.R.id.home:
invalidateOptionsMenu();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
MenuItem markdownItem = menu.findItem(R.id.menu_markdown_preview);
if (mIsShowingMarkdown) {
markdownItem.setIcon(R.drawable.ic_markdown_solid_24dp);
markdownItem.setTitle(getString(R.string.markdown_hide));
} else {
markdownItem.setIcon(R.drawable.ic_markdown_outline_24dp);
markdownItem.setTitle(getString(R.string.markdown_show));
}
DrawableUtils.tintMenuItemWithAttribute(this, markdownItem, R.attr.actionBarTextColor);
return super.onPrepareOptionsMenu(menu);
}
public void setMarkdownShowing(boolean isMarkdownShowing) {
mIsShowingMarkdown = isMarkdownShowing;
if (mNoteEditorFragment != null) {
if (isMarkdownShowing) {
mNoteEditorFragment.showMarkdown();
} else {
mNoteEditorFragment.hideMarkdown();
}
}
invalidateOptionsMenu();
}
/**
* Callback method from {@link NoteListFragment.Callbacks} indicating that
* the item with the given ID was selected. Used for tablets only.
*/
@Override
public void onNoteSelected(String noteID, int position, boolean isNew, String matchOffsets, boolean isMarkdownEnabled) {
if (!DisplayUtils.isLargeScreenLandscape(this)) {
// Launch the editor activity
Bundle arguments = new Bundle();
arguments.putString(NoteEditorFragment.ARG_ITEM_ID, noteID);
arguments.putBoolean(NoteEditorFragment.ARG_NEW_NOTE, isNew);
arguments.putBoolean(NoteEditorFragment.ARG_MARKDOWN_ENABLED, isMarkdownEnabled);
if (matchOffsets != null)
arguments.putString(NoteEditorFragment.ARG_MATCH_OFFSETS, matchOffsets);
Intent editNoteIntent = new Intent(this, NoteEditorActivity.class);
editNoteIntent.putExtras(arguments);
startActivityForResult(editNoteIntent, Simplenote.INTENT_EDIT_NOTE);
} else {
mNoteEditorFragment.setIsNewNote(isNew);
mNoteEditorFragment.setNote(noteID, matchOffsets);
getNoteListFragment().setNoteSelected(noteID);
if (mSearchView != null && mSearchView.getQuery() != null) {
mTabletSearchQuery = mSearchView.getQuery().toString();
}
mNoteEditorFragment.clearMarkdown();
if (!isMarkdownEnabled && mIsShowingMarkdown) {
setMarkdownShowing(false);
}
invalidateOptionsMenu();
}
AnalyticsTracker.track(
AnalyticsTracker.Stat.LIST_NOTE_OPENED,
AnalyticsTracker.CATEGORY_NOTE,
"note_list_row_tap"
);
}
@Override
public void onUserCreated(User user) {
// New account created
AnalyticsTracker.track(
AnalyticsTracker.Stat.USER_ACCOUNT_CREATED,
AnalyticsTracker.CATEGORY_USER,
"account_created_from_login_activity"
);
}
public void onUserStatusChange(User.Status status) {
switch (status) {
// successfully used access token to connect to simperium bucket
case AUTHORIZED:
runOnUiThread(new Runnable() {
@Override
public void run() {
if (!mNotesBucket.hasChangeVersion()) {
setToolbarProgressVisibility(true);
}
}
});
break;
// NOT_AUTHORIZED means we attempted to connect but the token was not valid
case NOT_AUTHORIZED:
runOnUiThread(new Runnable() {
@Override
public void run() {
startLoginActivity(true);
}
});
break;
// Default starting state of User, don't do anything we allow use of app while not signed in so don't do anything
case UNKNOWN:
break;
}
}
private void setToolbarProgressVisibility(boolean isVisible) {
if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayShowCustomEnabled(isVisible);
}
}
public void startLoginActivity(boolean signInFirst) {
Intent loginIntent = new Intent(this, LoginActivity.class);
if (signInFirst)
loginIntent.putExtra(LoginActivity.EXTRA_SIGN_IN_FIRST, true);
startActivityForResult(loginIntent, Simperium.SIGNUP_SIGNIN_REQUEST);
}
@Override
public void recreate() {
Handler handler = new Handler();
handler.post(new Runnable() {
@Override
public void run() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
NotesActivity.this.finish();
NotesActivity.this.startActivity(NotesActivity.this.getIntent());
} else {
NotesActivity.super.recreate();
}
}
});
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case Simplenote.INTENT_PREFERENCES:
if (ThemeUtils.themeWasChanged(data)) {
// Restart this activity to apply the new theme
recreate();
break;
}
// nbradbury - refresh note list when user returns from preferences (in case they changed anything)
invalidateOptionsMenu();
NoteListFragment fragment = getNoteListFragment();
if (fragment != null) {
fragment.getPrefs();
fragment.refreshList();
}
break;
case Simplenote.INTENT_EDIT_NOTE:
if (resultCode == RESULT_OK && data != null && data.hasExtra(Simplenote.DELETED_NOTE_ID)) {
String noteId = data.getStringExtra(Simplenote.DELETED_NOTE_ID);
if (noteId != null) {
List<String> deletedNoteIds = new ArrayList<>();
deletedNoteIds.add(noteId);
mUndoBarController.setDeletedNoteIds(deletedNoteIds);
mUndoBarController.showUndoBar(getUndoView(), getString(R.string.note_deleted));
}
}
break;
case Simperium.SIGNUP_SIGNIN_REQUEST:
invalidateOptionsMenu();
Simplenote app = (Simplenote) getApplication();
AnalyticsTracker.refreshMetadata(app.getSimperium().getUser().getEmail());
AnalyticsTracker.track(
AnalyticsTracker.Stat.USER_SIGNED_IN,
AnalyticsTracker.CATEGORY_USER,
"signed_in_from_login_activity"
);
if (resultCode == Activity.RESULT_CANCELED && userAuthenticationIsInvalid()) {
finish();
}
break;
}
}
@Override
public void onUndo() {
if (mUndoBarController == null) return;
List<String> deletedNoteIds = mUndoBarController.getDeletedNoteIds();
if (deletedNoteIds != null) {
for (int i = 0; i < deletedNoteIds.size(); i++) {
Note deletedNote;
try {
deletedNote = mNotesBucket.get(deletedNoteIds.get(i));
} catch (BucketObjectMissingException e) {
return;
}
if (deletedNote != null) {
deletedNote.setDeleted(false);
deletedNote.setModificationDate(Calendar.getInstance());
deletedNote.save();
NoteListFragment fragment = getNoteListFragment();
if (fragment != null) {
fragment.getPrefs();
fragment.refreshList();
}
}
}
}
}
@Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
// Sync the toggle state after onRestoreInstanceState has occurred.
mDrawerToggle.syncState();
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
mDrawerToggle.onConfigurationChanged(newConfig);
if (DisplayUtils.isLargeScreen(this)) {
mIsShowingMarkdown = false;
if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
// Add the editor fragment
addEditorFragment();
if (mNoteListFragment != null) {
mNoteListFragment.setActivateOnItemClick(true);
mNoteListFragment.setDividerVisible(true);
}
// Select the current note on a tablet
if (mCurrentNote != null)
onNoteSelected(mCurrentNote.getSimperiumKey(), 0, false, null, mCurrentNote.isMarkdownEnabled());
else {
mNoteEditorFragment.setPlaceholderVisible(true);
mNoteListFragment.getListView().clearChoices();
}
invalidateOptionsMenu();
} else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT && mNoteEditorFragment != null) {
// Remove the editor fragment when rotating back to portrait
mCurrentNote = null;
if (mNoteListFragment != null) {
mNoteListFragment.setActivateOnItemClick(false);
mNoteListFragment.setDividerVisible(false);
mNoteListFragment.setActivatedPosition(ListView.INVALID_POSITION);
mNoteListFragment.refreshList();
}
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.remove(mNoteEditorFragment);
mNoteEditorFragment = null;
ft.commitAllowingStateLoss();
fm.executePendingTransactions();
invalidateOptionsMenu();
}
}
}
public void checkEmptyListText(boolean isSearch) {
if (isSearch) {
getNoteListFragment().setEmptyListMessage("<strong>" + getString(R.string.no_notes_found) + "</strong>");
getNoteListFragment().setEmptyListViewClickable(false);
} else if (mDrawerList.getCheckedItemPosition() == TRASH_SELECTED_ID) {
getNoteListFragment().setEmptyListMessage("<strong>" + getString(R.string.trash_is_empty) + "</strong>");
AnalyticsTracker.track(
AnalyticsTracker.Stat.LIST_TRASH_VIEWED,
AnalyticsTracker.CATEGORY_NOTE,
"trash_filter_selected"
);
getNoteListFragment().setEmptyListViewClickable(false);
} else {
getNoteListFragment().setEmptyListMessage("<strong>" + getString(R.string.no_notes_here) + "</strong><br />" + String.format(getString(R.string.why_not_create_one), "<u>", "</u>"));
getNoteListFragment().setEmptyListViewClickable(true);
}
}
public void showDetailPlaceholder() {
if (DisplayUtils.isLargeScreenLandscape(this) && mNoteEditorFragment != null) {
mCurrentNote = null;
mNoteEditorFragment.setPlaceholderVisible(true);
mNoteEditorFragment.clearMarkdown();
mNoteEditorFragment.hideMarkdown();
mIsShowingMarkdown = false;
}
}
public void stopListeningToNotesBucket() {
mNotesBucket.removeOnNetworkChangeListener(this);
mNotesBucket.removeOnSaveObjectListener(this);
mNotesBucket.removeOnDeleteObjectListener(this);
}
// Returns the appropriate view to show the undo bar within
private View getUndoView() {
View undoView = mFragmentsContainer;
if (!DisplayUtils.isLargeScreenLandscape(this) &&
getNoteListFragment() != null &&
getNoteListFragment().getRootView() != null) {
undoView = getNoteListFragment().getRootView();
}
return undoView;
}
public void showUndoBarWithNoteIds(List<String> noteIds) {
if (mUndoBarController != null) {
mUndoBarController.setDeletedNoteIds(noteIds);
mUndoBarController.showUndoBar(
getUndoView(),
getResources().getQuantityString(R.plurals.trashed_notes, noteIds.size(), noteIds.size())
);
}
}
/* Simperium Bucket Listeners */
// received a change from the network, refresh the list
@Override
public void onNetworkChange(Bucket<Note> bucket, final Bucket.ChangeType type, String key) {
runOnUiThread(new Runnable() {
@Override
public void run() {
if (type == Bucket.ChangeType.INDEX) {
setToolbarProgressVisibility(false);
}
mNoteListFragment.refreshList();
}
});
}
@Override
public void onSaveObject(Bucket<Note> bucket, Note object) {
runOnUiThread(new Runnable() {
@Override
public void run() {
mNoteListFragment.refreshList();
}
});
}
@Override
public void onDeleteObject(Bucket<Note> bucket, Note object) {
runOnUiThread(new Runnable() {
@Override
public void run() {
mNoteListFragment.refreshList();
}
});
}
@Override
public void onBeforeUpdateObject(Bucket<Note> bucket, Note note) {
// noop, NoteEditorFragment will handle this
}
/* The click listener for ListView in the navigation drawer */
private class DrawerItemClickListener implements ListView.OnItemClickListener {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
// Adjust for header view
position -= mDrawerList.getHeaderViewsCount();
mSelectedTag = mTagsAdapter.getItem(position);
checkEmptyListText(false);
// Update checked item in navigation drawer and close it
setSelectedTagActive();
mDrawerLayout.closeDrawer(mNavigationView);
updateNavigationDrawerItems();
// Disable long press on notes if we're viewing the trash
if (mDrawerList.getCheckedItemPosition() == TRASH_SELECTED_ID) {
getNoteListFragment().getListView().setLongClickable(false);
} else {
getNoteListFragment().getListView().setLongClickable(true);
}
getNoteListFragment().refreshListFromNavSelect();
if (position > 1) {
AnalyticsTracker.track(
AnalyticsTracker.Stat.LIST_TAG_VIEWED,
AnalyticsTracker.CATEGORY_TAG,
"selected_tag_in_navigation_drawer"
);
}
}
}
private class emptyTrashTask extends AsyncTask<Void, Void, Void> {
@Override
protected Void doInBackground(Void... voids) {
if (mNotesBucket == null) return null;
Query<Note> query = Note.allDeleted(mNotesBucket);
Bucket.ObjectCursor c = query.execute();
while (c.moveToNext()) {
c.getObject().delete();
}
return null;
}
@Override
protected void onPostExecute(Void nada) {
showDetailPlaceholder();
}
}
}