package de.vanmar.android.yarrn.projects; import android.app.Activity; import android.net.Uri; import android.support.v4.app.Fragment; import android.text.Editable; import android.text.SpannableString; import android.text.style.UnderlineSpan; import android.view.Menu; import android.view.MenuInflater; import android.view.View; import android.widget.AdapterView; import android.widget.ImageButton; import android.widget.ProgressBar; import android.widget.Spinner; import android.widget.TextView; import android.widget.Toast; import com.androidquery.util.AQUtility; import com.google.gson.JsonObject; import com.meetme.android.horizontallistview.HorizontalListView; import com.octo.android.robospice.SpiceManager; import com.octo.android.robospice.persistence.exception.SpiceException; import com.octo.android.robospice.request.listener.RequestListener; import org.androidannotations.annotations.AfterViews; import org.androidannotations.annotations.Click; import org.androidannotations.annotations.EFragment; import org.androidannotations.annotations.FragmentArg; import org.androidannotations.annotations.OptionsItem; import org.androidannotations.annotations.OptionsMenu; import org.androidannotations.annotations.UiThread; import org.androidannotations.annotations.ViewById; import org.androidannotations.annotations.sharedpreferences.Pref; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; import de.vanmar.android.yarrn.R; import de.vanmar.android.yarrn.YarrnPrefs_; import de.vanmar.android.yarrn.YarrnSpiceService; import de.vanmar.android.yarrn.components.AddEditFavoriteDialog; import de.vanmar.android.yarrn.components.ImageDialog; import de.vanmar.android.yarrn.components.SimpleImageArrayAdapter; import de.vanmar.android.yarrn.components.ViewEditText; import de.vanmar.android.yarrn.ravelry.IRavelryActivity; import de.vanmar.android.yarrn.ravelry.RavelryResultListener; import de.vanmar.android.yarrn.ravelry.dts.BookmarkShort; import de.vanmar.android.yarrn.ravelry.dts.Photo; import de.vanmar.android.yarrn.ravelry.dts.PhotoResult; import de.vanmar.android.yarrn.ravelry.dts.Project; import de.vanmar.android.yarrn.ravelry.dts.ProjectResult; import de.vanmar.android.yarrn.requests.AbstractRavelryGetRequest; import de.vanmar.android.yarrn.requests.AddFavoriteRequest; import de.vanmar.android.yarrn.requests.GetProjectRequest; import de.vanmar.android.yarrn.requests.ReorderProjectPhotosRequest; import de.vanmar.android.yarrn.requests.UpdateProjectRequest; @EFragment(R.layout.fragment_project_detail) @OptionsMenu(R.menu.project_fragment_menu) public class ProjectFragment extends Fragment { public static final String ARG_PROJECT_ID = "projectId"; public static final String ARG_USERNAME = "username"; protected SpiceManager spiceManager; private AdapterView.OnItemSelectedListener progressListener; private ViewEditText.OnSaveListener notesListener; private AdapterView.OnItemSelectedListener statusListener; private AdapterView.OnItemSelectedListener ratingListener; private View.OnClickListener progressBarListener; private List<Photo> photos; private List<Photo> photosOriginal; private ImageDialog dialog; public interface ProjectFragmentListener extends IRavelryActivity { void takePhoto(); void pickImage(); void onPatternSelected(int patternId); } @ViewById(R.id.name) TextView name; @ViewById(R.id.pattern_name) TextView patternName; @ViewById(R.id.status) Spinner status; @ViewById(R.id.gallery_edit_done) ImageButton galleryEditDone; @ViewById(R.id.gallery_edit_cancel) ImageButton galleryEditCancel; @ViewById(R.id.gallery) HorizontalListView gallery; @ViewById(R.id.progressBar) ProgressBar progressBar; @ViewById(R.id.progressSpinner) Spinner progressSpinner; @ViewById(R.id.started) public TextView started; @ViewById(R.id.completed) public TextView completed; @ViewById(R.id.notes) public ViewEditText notes; @ViewById(R.id.rating) public Spinner rating; @FragmentArg(ARG_PROJECT_ID) int projectId; @FragmentArg(ARG_USERNAME) String username; private ProjectFragmentListener listener; private PhotoAdapter adapter; private boolean isEditable = false; private boolean isOwn = false; private boolean isLoaded = false; @Pref YarrnPrefs_ prefs; @Override public void onDestroy() { super.onDestroy(); if (dialog != null && dialog.isShowing()) { dialog.dismiss(); dialog = null; } } @AfterViews public void afterViews() { if (spiceManager == null) { spiceManager = new SpiceManager(YarrnSpiceService.class); } notesListener = new ViewEditText.OnSaveListener() { @Override public void onSave(ViewEditText view, Editable text) { JsonObject updateData = new JsonObject(); updateData.addProperty("notes", text.toString()); executeUpdate(updateData); } }; setupStatusSpinner(); setupRatingSpinner(); progressBarListener = new View.OnClickListener() { @Override public void onClick(View v) { progressSpinner.performClick(); } }; adapter = new PhotoAdapter(getActivity()); gallery.setAdapter(adapter); gallery.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { dialog = new ImageDialog(getActivity(), adapter.getItem(position).mediumUrl); dialog.show(); } }); gallery.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() { @Override public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) { setGalleryEditable(true); return true; } }); adapter.setPhotoAdapterListener(new PhotoAdapter.PhotoAdapterListener() { private void movePhotoFromTo(int fromPosition, int toPosition) { Photo photo = photos.remove(fromPosition); photos.add(toPosition, photo); adapter.setItems(photos); } @Override public void onMoveLeft(int position) { movePhotoFromTo(position, position - 1); } @Override public void onMoveAllLeft(int position) { movePhotoFromTo(position, 0); } @Override public void onMoveRight(int position) { movePhotoFromTo(position, position + 1); } @Override public void onMoveAllRight(int position) { movePhotoFromTo(position, photos.size() - 1); } }); progressListener = new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { int newProgress = position * 5; displayProgress(newProgress); JsonObject updateData = new JsonObject(); updateData.addProperty("progress", newProgress); executeUpdate(updateData); } @Override public void onNothingSelected(AdapterView<?> parent) { // do nothing } }; } private void setupStatusSpinner() { statusListener = new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { int newStatus = position + 1; JsonObject updateData = new JsonObject(); updateData.addProperty("project_status_id", newStatus); executeUpdate(updateData); } @Override public void onNothingSelected(AdapterView<?> parent) { // do nothing } }; } private void setupRatingSpinner() { ratingListener = new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { int newRating = position; JsonObject updateData = new JsonObject(); updateData.addProperty("rating", newRating); executeUpdate(updateData); } @Override public void onNothingSelected(AdapterView<?> parent) { // do nothing } }; SimpleImageArrayAdapter ratingAdapter = new SimpleImageArrayAdapter(getActivity(), new Integer[]{R.drawable.smiley1, R.drawable.smiley2, R.drawable.smiley3, R.drawable.smiley4, R.drawable.smiley5}); rating.setAdapter(ratingAdapter); } private void setGalleryEditable(boolean shouldBeEditable) { boolean editable = isEditable && shouldBeEditable; adapter.setEditable(editable); galleryEditDone.setVisibility(editable ? View.VISIBLE : View.GONE); galleryEditCancel.setVisibility(editable ? View.VISIBLE : View.GONE); } private void executeUpdate(JsonObject updateData) { spiceManager.execute(new UpdateProjectRequest(prefs, getActivity().getApplication(), projectId, updateData), new ProjectListener(listener)); spiceManager.removeDataFromCache(ProjectResult.class, new GetProjectRequest(getActivity().getApplication(), prefs, projectId, prefs.username().get()).getCacheKey()); } private void savePhotoOrder() { spiceManager.execute(new ReorderProjectPhotosRequest(prefs, getActivity().getApplication(), projectId, photos), new RavelryResultListener<PhotoResult>(listener) { @Override public void onRequestSuccess(PhotoResult photoResult) { photos = photoResult.photos; adapter.setItems(photos); } }); spiceManager.removeDataFromCache(ProjectResult.class, new GetProjectRequest(getActivity().getApplication(), prefs, projectId, prefs.username().get()).getCacheKey()); } @Override public void onAttach(final Activity activity) { super.onAttach(activity); if (activity instanceof ProjectFragmentListener) { listener = (ProjectFragmentListener) activity; } else { throw new ClassCastException(activity.toString() + " must implement ProjectFragmentListener"); } } @Override public void onDetach() { super.onDetach(); listener = null; } public void onProjectSelected(final int projectId, final String username) { clearProject(); if (projectId != 0) { GetProjectRequest request = new GetProjectRequest(this.getActivity().getApplication(), prefs, projectId, username); spiceManager.execute(request, request.getCacheKey(), AbstractRavelryGetRequest.CACHE_DURATION, new ProjectListener(listener)); isLoaded = false; } } @Override public void onStart() { super.onStart(); spiceManager.start(this.getActivity()); onProjectSelected(projectId, username); } @Override public void onResume() { super.onResume(); } @Override public void onStop() { if (spiceManager.isStarted()) { spiceManager.shouldStop(); } super.onStop(); } @UiThread protected void clearProject() { View view = getView(); if (view != null) { view.setVisibility(View.GONE); } } protected void displayProject(final ProjectResult projectResult) { final Project project = projectResult.project; getActivity().setTitle(project.name); name.setText(project.name); rating.setOnItemSelectedListener(null); rating.setSelection(project.rating, false); setPatternName(project); status.setOnItemSelectedListener(null); status.setSelection(project.statusId - 1, false); started.setText(getCompletedDateText(project.started, project.startedDaySet)); completed.setText(getCompletedDateText(project.completed, project.completedDaySet)); notes.setBodyText(project.notes); adapter.clear(); photos = project.photos; photosOriginal = new ArrayList<Photo>(project.photos); adapter.setItems(photos); setGalleryEditable(false); displayProgress(project.progress); progressSpinner.setOnItemSelectedListener(null); getView().setVisibility(View.VISIBLE); if (project.user != null && prefs.username().get().equals(project.user.username)) { setEditable(); isOwn = true; } else { setNonEditable(); isOwn = false; } isLoaded = true; } private void setPatternName(final Project project) { String name = project.patternName == null ? "" : project.patternName; SpannableString patternNameText = new SpannableString(name); if (project.patternId != null) { patternNameText.setSpan(new UnderlineSpan(), 0, name.length(), 0); patternName.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { listener.onPatternSelected(project.patternId); } }); } else { patternName.setOnClickListener(null); } patternName.setText(patternNameText); } private void setEditable() { progressSpinner.setOnItemSelectedListener(progressListener); progressBar.setOnClickListener(progressBarListener); notes.setOnSaveListener(notesListener); status.setOnItemSelectedListener(statusListener); rating.setOnItemSelectedListener(ratingListener); isEditable = true; setFieldsEditable(); } private void setNonEditable() { progressSpinner.setOnItemSelectedListener(null); progressSpinner.setOnItemSelectedListener(null); progressBar.setOnClickListener(null); notes.setOnSaveListener(null); status.setOnItemSelectedListener(null); rating.setOnItemSelectedListener(null); isEditable = false; setFieldsEditable(); } private void setFieldsEditable() { getActivity().supportInvalidateOptionsMenu(); setSpinnerEditable(status, isEditable); setSpinnerEditable(rating, isEditable); setSpinnerEditable(progressSpinner, isEditable); notes.setEditable(isEditable); } private void setSpinnerEditable(Spinner spinner, boolean editable) { spinner.setEnabled(editable); spinner.setClickable(editable); spinner.setFocusable(editable); } private String getCompletedDateText(Date date, boolean withDay) { if (date == null) { return getActivity().getString(R.string.date_unknown); } DateFormat dateFormat; if (withDay) { dateFormat = SimpleDateFormat.getDateInstance(); } else { dateFormat = new SimpleDateFormat("MMMM yyyy"); } return dateFormat.format(date); } private void displayProgress(int progress) { progressBar.setProgress(progress); progressSpinner.setOnItemSelectedListener(null); progressSpinner.setSelection(progress / 5, false); progressSpinner.setOnItemSelectedListener(progressListener); } public void uploadPhotoToProject(final Uri photoUri) { Toast.makeText(getActivity(), getActivity().getString(R.string.upload_started), Toast.LENGTH_LONG).show(); spiceManager.execute(new PhotoUploadRequest(getActivity().getApplication(), prefs, photoUri, projectId), new RequestListener<String>() { @Override public void onRequestFailure(SpiceException spiceException) { AQUtility.report(spiceException); } @Override public void onRequestSuccess(String s) { onPhotoUploadSuccess(); } }); } @UiThread public void onPhotoUploadSuccess() { Toast.makeText(getActivity(), getActivity().getString(R.string.upload_successful), Toast.LENGTH_LONG).show(); spiceManager.removeDataFromCache(ProjectResult.class, new GetProjectRequest(getActivity().getApplication(), prefs, projectId, prefs.username().get()).getCacheKey()); } @Click(R.id.gallery_edit_done) public void onGalleryEditDoneClicked() { savePhotoOrder(); setGalleryEditable(false); } @Click(R.id.gallery_edit_cancel) public void onGalleryEditCancelClicked() { photos = new ArrayList<Photo>(photosOriginal); adapter.setItems(photos); setGalleryEditable(false); } @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { super.onCreateOptionsMenu(menu, inflater); menu.findItem(R.id.menu_add_photo).setVisible(isEditable); menu.findItem(R.id.menu_take_photo).setVisible(isEditable); menu.findItem(R.id.menu_reorder_photos).setVisible(isEditable); menu.findItem(R.id.menu_add_as_favorite).setVisible(isLoaded && !isOwn); } @OptionsItem(R.id.menu_add_photo) public void onAddPhotoClicked() { listener.pickImage(); } @OptionsItem(R.id.menu_take_photo) public void onTakePhotoClicked() { listener.takePhoto(); } @OptionsItem(R.id.menu_refresh) public void menuRefresh() { onProjectSelected(projectId, username); } @OptionsItem(R.id.menu_add_as_favorite) public void menuAddAsFavorite() { new AddEditFavoriteDialog(getActivity(), new AddEditFavoriteDialog.AddEditFavoriteDialogListener() { @Override public void onSave(String comment, String tags) { JsonObject updateData = new JsonObject(); updateData.addProperty("type", "project"); updateData.addProperty("favorited_id", projectId); updateData.addProperty("comment", comment); updateData.addProperty("tag_names", tags); spiceManager.execute(new AddFavoriteRequest(prefs, getActivity().getApplication(), updateData), new RequestListener<BookmarkShort>() { @Override public void onRequestFailure(SpiceException spiceException) { AQUtility.report(spiceException); } @Override public void onRequestSuccess(BookmarkShort bookmarkShort) { Toast.makeText(getActivity(), R.string.add_favorite_success, Toast.LENGTH_LONG).show(); } }); } }, prefs).show(); } @OptionsItem(R.id.menu_reorder_photos) public void menuReorderPhotos() { setGalleryEditable(true); } class ProjectListener extends RavelryResultListener<ProjectResult> { protected ProjectListener(IRavelryActivity activity) { super(activity); } @Override public void onRequestSuccess(ProjectResult result) { if (listener != null) { displayProject(result); } } } }