package com.dropbox.examples.notes;
import java.util.List;
import android.annotation.TargetApi;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.app.ListFragment;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.Loader;
import android.text.TextUtils;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MenuItem.OnMenuItemClickListener;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.EditorInfo;
import android.widget.AdapterView;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.Toast;
import com.dropbox.sync.android.DbxAccountManager;
import com.dropbox.sync.android.DbxException;
import com.dropbox.sync.android.DbxFileInfo;
import com.dropbox.sync.android.DbxFileSystem;
import com.dropbox.sync.android.DbxPath;
import com.dropbox.sync.android.util.FolderLoader;
public class NoteListFragment extends ListFragment implements LoaderCallbacks<List<DbxFileInfo>> {
@SuppressWarnings("unused")
private static final String TAG = NoteListFragment.class.getName();
private static final String STATE_ACTIVATED_POSITION = "activated_position";
private static final int MENU_RENAME = 1;
private static final int MENU_DELETE = 2;
private Callbacks mCallbacks = sDummyCallbacks;
private int mActivatedPosition = ListView.INVALID_POSITION;
private View mEmptyText;
private View mLinkButton;
private View mLoadingSpinner;
private DbxAccountManager mAccountManager;
public interface Callbacks {
public void onItemSelected(DbxPath path);
}
private static Callbacks sDummyCallbacks = new Callbacks() {
@Override
public void onItemSelected(DbxPath path) {
}
};
public NoteListFragment() {}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public void onStart() {
super.onStart();
doLoad();
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == 0) {
if (resultCode == Activity.RESULT_OK) {
// We are now linked.
showLinkedView();
}
} else {
super.onActivityResult(requestCode, resultCode, data);
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_note_list, container, false);
mEmptyText = view.findViewById(R.id.empty_text);
mLinkButton = view.findViewById(R.id.link_button);
mLoadingSpinner = view.findViewById(R.id.list_loading);
mLinkButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mAccountManager.startLink(NoteListFragment.this, 0);
}
});
return view;
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
if (savedInstanceState != null && savedInstanceState
.containsKey(STATE_ACTIVATED_POSITION)) {
setActivatedPosition(savedInstanceState.getInt(STATE_ACTIVATED_POSITION));
}
getListView().setEmptyView(view.findViewById(android.R.id.empty));
if (!mAccountManager.hasLinkedAccount()) {
showUnlinkedView();
} else {
showLinkedView();
}
}
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
DbxFileInfo fileInfo = (DbxFileInfo)getListAdapter().getItem(info.position);
menu.setHeaderTitle(Util.stripExtension("txt", fileInfo.path.getName()));
menu.add(Menu.NONE, MENU_RENAME, Menu.NONE, R.string.menu_rename);
menu.add(Menu.NONE, MENU_DELETE, Menu.NONE, R.string.menu_delete);
}
@Override
public boolean onContextItemSelected(MenuItem item) {
AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo)item.getMenuInfo();
final DbxFileInfo fileInfo = (DbxFileInfo)getListAdapter().getItem(info.position);
int itemId = item.getItemId();
if (itemId == MENU_RENAME) {
final EditText filenameInput = new EditText(getActivity());
filenameInput.setText(Util.stripExtension("txt", fileInfo.path.getName()));
filenameInput.setSelectAllOnFocus(true);
new AlertDialog.Builder(getActivity())
.setTitle(R.string.rename_note_dialog_title)
.setView(filenameInput)
.setPositiveButton(R.string.rename_note_confirm, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int whichButton) {
String filename = filenameInput.getText().toString();
if (TextUtils.isEmpty(filename)) {
filename = filenameInput.getHint().toString();
}
if (!filename.endsWith(".txt")) {
filename += ".txt";
}
DbxPath p;
try {
p = new DbxPath("/" + filename);
} catch (DbxPath.InvalidPathException e) {
// TODO: build a custom dialog that won't even allow invalid filenames
Toast.makeText(getActivity(), R.string.error_invalid_filename, Toast.LENGTH_LONG).show();
return;
}
try {
DbxFileSystem.forAccount(mAccountManager.getLinkedAccount()).move(fileInfo.path, p);
} catch (DbxException.Exists e) {
Toast.makeText(getActivity(), R.string.error_file_already_exists, Toast.LENGTH_LONG).show();
} catch (DbxException e) {
e.printStackTrace();
}
}
}).setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int whichButton) {
// Do nothing.
}
}).show();
} else if (itemId == MENU_DELETE) {
try {
DbxFileSystem.forAccount(mAccountManager.getLinkedAccount()).delete(fileInfo.path);
} catch (DbxException e) {
e.printStackTrace();
}
}
return true;
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
if (!(activity instanceof Callbacks)) {
throw new IllegalStateException("Activity must implement fragment's callbacks.");
}
mAccountManager = NotesAppConfig.getAccountManager(activity);
mCallbacks = (Callbacks) activity;
setHasOptionsMenu(true);
}
@Override
public void onDetach() {
super.onDetach();
mCallbacks = sDummyCallbacks;
}
@Override
public void onListItemClick(ListView listView, View view, int position, long id) {
super.onListItemClick(listView, view, position, id);
mCallbacks.onItemSelected(((DbxFileInfo)getListAdapter().getItem(position)).path);
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (mActivatedPosition != ListView.INVALID_POSITION) {
outState.putInt(STATE_ACTIVATED_POSITION, mActivatedPosition);
}
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
registerForContextMenu(getListView());
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
if (mAccountManager.hasLinkedAccount()) {
// New note
MenuItem newNote = menu.add(R.string.new_note_option);
newNote.setOnMenuItemClickListener(new OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
final EditText filenameInput = new EditText(getActivity());
filenameInput.setHint(R.string.new_note_name_hint);
filenameInput.setInputType(EditorInfo.TYPE_CLASS_TEXT | EditorInfo.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
new AlertDialog.Builder(getActivity())
.setTitle(R.string.new_note_dialog_title)
.setView(filenameInput)
.setPositiveButton(R.string.new_note_confirm, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int whichButton) {
String filename = filenameInput.getText().toString();
if (TextUtils.isEmpty(filename)) {
filename = filenameInput.getHint().toString();
}
if (!filename.endsWith(".txt")) {
filename += ".txt";
}
DbxPath p;
try {
p = new DbxPath("/" + filename);
} catch (DbxPath.InvalidPathException e) {
// TODO: build a custom dialog that won't even allow invalid filenames
Toast.makeText(getActivity(), "invalid filename", Toast.LENGTH_LONG).show();
return;
}
mCallbacks.onItemSelected(p);
}
}).setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int whichButton) {
// Do nothing.
}
}).show();
return true;
}
});
// Unlink
MenuItem unlink = menu.add(R.string.unlink_from_dropbox);
unlink.setOnMenuItemClickListener(new OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
mAccountManager.unlink();
setListAdapter(null);
showUnlinkedView();
return true;
}
});
}
}
@Override
public Loader<List<DbxFileInfo>> onCreateLoader(int id, Bundle args) {
return new FolderLoader(getActivity(), mAccountManager, DbxPath.ROOT);
}
@Override
public void onLoadFinished(Loader<List<DbxFileInfo>> loader, List<DbxFileInfo> data) {
mLoadingSpinner.setVisibility(View.GONE);
mEmptyText.setVisibility(View.VISIBLE);
// TODO: would be nice to maintain the list's scroll position here when new data is delivered
setListAdapter(new FolderAdapter(getActivity(), data));
registerForContextMenu(getListView());
}
@Override
public void onLoaderReset(Loader<List<DbxFileInfo>> loader) {
// Do nothing.
}
public void setActivateOnItemClick(boolean activateOnItemClick) {
getListView().setChoiceMode(activateOnItemClick
? ListView.CHOICE_MODE_SINGLE
: ListView.CHOICE_MODE_NONE);
}
public void setActivatedPosition(int position) {
if (position == ListView.INVALID_POSITION) {
getListView().setItemChecked(mActivatedPosition, false);
} else {
getListView().setItemChecked(position, true);
}
mActivatedPosition = position;
}
private void doLoad() {
if (mAccountManager.hasLinkedAccount()) {
mEmptyText.setVisibility(View.GONE);
mLoadingSpinner.setVisibility(View.VISIBLE);
getLoaderManager().initLoader(0, null, this);
}
}
@TargetApi(11)
private void showUnlinkedView() {
getListView().setVisibility(View.GONE);
mEmptyText.setVisibility(View.GONE);
mLoadingSpinner.setVisibility(View.GONE);
mLinkButton.setVisibility(View.VISIBLE);
if (Build.VERSION.SDK_INT >= 11) {
getActivity().invalidateOptionsMenu();
}
getView().postInvalidate();
}
@TargetApi(11)
private void showLinkedView() {
getListView().setVisibility(View.VISIBLE);
mEmptyText.setVisibility(View.GONE);
mLoadingSpinner.setVisibility(View.VISIBLE);
mLinkButton.setVisibility(View.GONE);
if (Build.VERSION.SDK_INT >= 11) {
getActivity().invalidateOptionsMenu();
}
getView().postInvalidate();
doLoad();
}
}