Split up contributions related code into separate classes

A controller introduced to deal with common code for picking images
from the gallery or camera. We should probably start using similar
controller methods elsewhere.

Splitting this particular one up because I'll be using it elsewhere

GitHub: https://github.com/wikimedia/apps-android-commons/pull/39
Change-Id: Idc393414be921d9a0fd54fcb3e2dcd676d8cc08b
This commit is contained in:
YuviPanda 2013-07-25 20:07:36 +00:00
parent 27f4fe1481
commit 5f3132718e
4 changed files with 237 additions and 180 deletions

View file

@ -0,0 +1,96 @@
package org.wikimedia.commons.contributions;
import android.app.*;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.util.Log;
import com.actionbarsherlock.app.SherlockFragment;
import org.wikimedia.commons.upload.ShareActivity;
import org.wikimedia.commons.upload.UploadService;
import java.io.File;
import java.io.IOException;
import java.util.Date;
public class ContributionController {
private SherlockFragment fragment;
private Activity activity;
private final static int SELECT_FROM_GALLERY = 1;
private final static int SELECT_FROM_CAMERA = 2;
public ContributionController(SherlockFragment fragment) {
this.fragment = fragment;
this.activity = fragment.getActivity();
}
// See http://stackoverflow.com/a/5054673/17865 for why this is done
private Uri lastGeneratedCaptureURI;
private Uri reGenerateImageCaptureURI() {
String storageState = Environment.getExternalStorageState();
if(storageState.equals(Environment.MEDIA_MOUNTED)) {
String path = Environment.getExternalStorageDirectory().getAbsolutePath() + "/Commons/images/" + new Date().getTime() + ".jpg";
File _photoFile = new File(path);
try {
if(_photoFile.exists() == false) {
_photoFile.getParentFile().mkdirs();
_photoFile.createNewFile();
}
} catch (IOException e) {
Log.e("Commons", "Could not create file: " + path, e);
}
return Uri.fromFile(_photoFile);
} else {
throw new RuntimeException("No external storage found!");
}
}
public void startCameraCapture() {
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
lastGeneratedCaptureURI = reGenerateImageCaptureURI();
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, lastGeneratedCaptureURI);
fragment.startActivityForResult(takePictureIntent, SELECT_FROM_CAMERA);
}
public void startGalleryPick() {
Intent pickImageIntent = new Intent(Intent.ACTION_GET_CONTENT);
pickImageIntent.setType("image/*");
fragment.startActivityForResult(pickImageIntent, SELECT_FROM_GALLERY);
}
public void handleImagePicked(int requestCode, Intent data) {
Intent shareIntent = new Intent(activity, ShareActivity.class);
shareIntent.setAction(Intent.ACTION_SEND);
switch(requestCode) {
case SELECT_FROM_GALLERY:
shareIntent.setType(activity.getContentResolver().getType(data.getData()));
shareIntent.putExtra(Intent.EXTRA_STREAM, data.getData());
shareIntent.putExtra(UploadService.EXTRA_SOURCE, Contribution.SOURCE_GALLERY);
break;
case SELECT_FROM_CAMERA:
shareIntent.setType("image/jpeg"); //FIXME: Find out appropriate mime type
shareIntent.putExtra(Intent.EXTRA_STREAM, lastGeneratedCaptureURI);
shareIntent.putExtra(UploadService.EXTRA_SOURCE, Contribution.SOURCE_CAMERA);
break;
}
activity.startActivity(shareIntent);
}
public void saveState(Bundle outState) {
outState.putParcelable("lastGeneratedCaptureURI", lastGeneratedCaptureURI);
}
public void loadState(Bundle savedInstanceState) {
if(savedInstanceState != null) {
lastGeneratedCaptureURI = (Uri) savedInstanceState.getParcelable("lastGeneratedCaptureURI");
}
}
}

View file

@ -0,0 +1,25 @@
package org.wikimedia.commons.contributions;
import android.view.View;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
import org.wikimedia.commons.R;
class ContributionViewHolder {
final ImageView imageView;
final TextView titleView;
final TextView stateView;
final TextView seqNumView;
final ProgressBar progressView;
String url;
ContributionViewHolder(View parent) {
imageView = (ImageView)parent.findViewById(R.id.contributionImage);
titleView = (TextView)parent.findViewById(R.id.contributionTitle);
stateView = (TextView)parent.findViewById(R.id.contributionState);
seqNumView = (TextView)parent.findViewById(R.id.contributionSequenceNumber);
progressView = (ProgressBar)parent.findViewById(R.id.contributionProgress);
}
}

View file

@ -0,0 +1,106 @@
package org.wikimedia.commons.contributions;
import android.content.Context;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.support.v4.widget.CursorAdapter;
import android.text.TextUtils;
import android.view.View;
import android.view.ViewGroup;
import com.actionbarsherlock.app.SherlockFragment;
import com.nostra13.universalimageloader.core.DisplayImageOptions;
import com.nostra13.universalimageloader.core.assist.SimpleImageLoadingListener;
import org.wikimedia.commons.CommonsApplication;
import org.wikimedia.commons.MediaWikiImageView;
import org.wikimedia.commons.R;
import org.wikimedia.commons.Utils;
class ContributionsListAdapter extends CursorAdapter {
private DisplayImageOptions contributionDisplayOptions = Utils.getGenericDisplayOptions().build();;
private SherlockFragment fragment;
public ContributionsListAdapter(SherlockFragment fragment, Cursor c, int flags) {
super(fragment.getActivity(), c, flags);
this.fragment = fragment;
}
@Override
public View newView(Context context, Cursor cursor, ViewGroup viewGroup) {
View parent = fragment.getActivity().getLayoutInflater().inflate(R.layout.layout_contribution, viewGroup, false);
parent.setTag(new ContributionViewHolder(parent));
return parent;
}
@Override
public void bindView(View view, Context context, Cursor cursor) {
final ContributionViewHolder views = (ContributionViewHolder)view.getTag();
Contribution contribution = Contribution.fromCursor(cursor);
String actualUrl = TextUtils.isEmpty(contribution.getImageUrl()) ? contribution.getLocalUri().toString() : contribution.getThumbnailUrl(320);
if(views.url == null || !views.url.equals(actualUrl)) {
if(actualUrl.startsWith("http")) {
MediaWikiImageView mwImageView = (MediaWikiImageView)views.imageView;
mwImageView.setMedia(contribution, ((CommonsApplication) fragment.getActivity().getApplicationContext()).getImageLoader());
// FIXME: For transparent images
} else {
com.nostra13.universalimageloader.core.ImageLoader.getInstance().displayImage(actualUrl, views.imageView, contributionDisplayOptions, new SimpleImageLoadingListener() {
@Override
public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
if(loadedImage.hasAlpha()) {
views.imageView.setBackgroundResource(android.R.color.white);
}
views.seqNumView.setVisibility(View.GONE);
}
});
}
views.url = actualUrl;
}
BitmapDrawable actualImageDrawable = (BitmapDrawable)views.imageView.getDrawable();
if(actualImageDrawable != null && actualImageDrawable.getBitmap() != null && actualImageDrawable.getBitmap().hasAlpha()) {
views.imageView.setBackgroundResource(android.R.color.white);
} else {
views.imageView.setBackgroundDrawable(null);
}
views.titleView.setText(contribution.getDisplayTitle());
views.seqNumView.setText(String.valueOf(cursor.getPosition() + 1));
views.seqNumView.setVisibility(View.VISIBLE);
switch(contribution.getState()) {
case Contribution.STATE_COMPLETED:
views.stateView.setVisibility(View.GONE);
views.progressView.setVisibility(View.GONE);
views.stateView.setText("");
break;
case Contribution.STATE_QUEUED:
views.stateView.setVisibility(View.VISIBLE);
views.progressView.setVisibility(View.GONE);
views.stateView.setText(R.string.contribution_state_queued);
break;
case Contribution.STATE_IN_PROGRESS:
views.stateView.setVisibility(View.GONE);
views.progressView.setVisibility(View.VISIBLE);
long total = contribution.getDataLength();
long transferred = contribution.getTransferred();
if(transferred == 0 || transferred >= total) {
views.progressView.setIndeterminate(true);
} else {
views.progressView.setProgress((int)(((double)transferred / (double)total) * 100));
}
break;
case Contribution.STATE_FAILED:
views.stateView.setVisibility(View.VISIBLE);
views.stateView.setText(R.string.contribution_state_failed);
views.progressView.setVisibility(View.GONE);
break;
}
}
}

View file

@ -5,14 +5,7 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.database.Cursor; import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.support.v4.widget.CursorAdapter;
import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
@ -25,135 +18,21 @@ import com.actionbarsherlock.view.MenuItem;
import com.nostra13.universalimageloader.core.DisplayImageOptions; import com.nostra13.universalimageloader.core.DisplayImageOptions;
import java.io.*;
import java.util.*;
import com.nostra13.universalimageloader.core.assist.SimpleImageLoadingListener;
import org.wikimedia.commons.*; import org.wikimedia.commons.*;
import org.wikimedia.commons.R; import org.wikimedia.commons.R;
import org.wikimedia.commons.upload.ShareActivity;
import org.wikimedia.commons.upload.UploadService;
public class ContributionsListFragment extends SherlockFragment { public class ContributionsListFragment extends SherlockFragment {
private final static int SELECT_FROM_GALLERY = 1;
private final static int SELECT_FROM_CAMERA = 2;
private GridView contributionsList; private GridView contributionsList;
private TextView waitingMessage; private TextView waitingMessage;
private TextView emptyMessage; private TextView emptyMessage;
private ContributionsListAdapter contributionsAdapter; private ContributionsListAdapter contributionsAdapter;
private DisplayImageOptions contributionDisplayOptions;
private Cursor allContributions; private Cursor allContributions;
private static class ContributionViewHolder { private ContributionController controller;
final ImageView imageView;
final TextView titleView;
final TextView stateView;
final TextView seqNumView;
final ProgressBar progressView;
String url;
ContributionViewHolder(View parent) {
imageView = (ImageView)parent.findViewById(R.id.contributionImage);
titleView = (TextView)parent.findViewById(R.id.contributionTitle);
stateView = (TextView)parent.findViewById(R.id.contributionState);
seqNumView = (TextView)parent.findViewById(R.id.contributionSequenceNumber);
progressView = (ProgressBar)parent.findViewById(R.id.contributionProgress);
}
}
private class ContributionsListAdapter extends CursorAdapter {
public ContributionsListAdapter(Context context, Cursor c, int flags) {
super(context, c, flags);
}
@Override
public View newView(Context context, Cursor cursor, ViewGroup viewGroup) {
View parent = getActivity().getLayoutInflater().inflate(R.layout.layout_contribution, viewGroup, false);
parent.setTag(new ContributionViewHolder(parent));
return parent;
}
@Override
public void bindView(View view, Context context, Cursor cursor) {
// hack: hide the 'first sync' message once we've loaded a cell
clearSyncMessage();
final ContributionViewHolder views = (ContributionViewHolder)view.getTag();
Contribution contribution = Contribution.fromCursor(cursor);
String actualUrl = TextUtils.isEmpty(contribution.getImageUrl()) ? contribution.getLocalUri().toString() : contribution.getThumbnailUrl(320);
if(views.url == null || !views.url.equals(actualUrl)) {
if(actualUrl.startsWith("http")) {
MediaWikiImageView mwImageView = (MediaWikiImageView)views.imageView;
mwImageView.setMedia(contribution, ((CommonsApplication) getActivity().getApplicationContext()).getImageLoader());
// FIXME: For transparent images
} else {
com.nostra13.universalimageloader.core.ImageLoader.getInstance().displayImage(actualUrl, views.imageView, contributionDisplayOptions, new SimpleImageLoadingListener() {
@Override
public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
if(loadedImage.hasAlpha()) {
views.imageView.setBackgroundResource(android.R.color.white);
}
views.seqNumView.setVisibility(View.GONE);
}
});
}
views.url = actualUrl;
}
BitmapDrawable actualImageDrawable = (BitmapDrawable)views.imageView.getDrawable();
if(actualImageDrawable != null && actualImageDrawable.getBitmap() != null && actualImageDrawable.getBitmap().hasAlpha()) {
views.imageView.setBackgroundResource(android.R.color.white);
} else {
views.imageView.setBackgroundDrawable(null);
}
views.titleView.setText(contribution.getDisplayTitle());
views.seqNumView.setText(String.valueOf(cursor.getPosition() + 1));
views.seqNumView.setVisibility(View.VISIBLE);
switch(contribution.getState()) {
case Contribution.STATE_COMPLETED:
views.stateView.setVisibility(View.GONE);
views.progressView.setVisibility(View.GONE);
views.stateView.setText("");
break;
case Contribution.STATE_QUEUED:
views.stateView.setVisibility(View.VISIBLE);
views.progressView.setVisibility(View.GONE);
views.stateView.setText(R.string.contribution_state_queued);
break;
case Contribution.STATE_IN_PROGRESS:
views.stateView.setVisibility(View.GONE);
views.progressView.setVisibility(View.VISIBLE);
long total = contribution.getDataLength();
long transferred = contribution.getTransferred();
if(transferred == 0 || transferred >= total) {
views.progressView.setIndeterminate(true);
} else {
views.progressView.setProgress((int)(((double)transferred / (double)total) * 100));
}
break;
case Contribution.STATE_FAILED:
views.stateView.setVisibility(View.VISIBLE);
views.stateView.setText(R.string.contribution_state_failed);
views.progressView.setVisibility(View.GONE);
break;
}
}
}
@Override @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
@ -162,7 +41,7 @@ public class ContributionsListFragment extends SherlockFragment {
public void setCursor(Cursor cursor) { public void setCursor(Cursor cursor) {
if(allContributions == null) { if(allContributions == null) {
contributionsAdapter = new ContributionsListAdapter(this.getActivity(), cursor, 0); contributionsAdapter = new ContributionsListAdapter(this, cursor, 0);
contributionsList.setAdapter(contributionsAdapter); contributionsList.setAdapter(contributionsAdapter);
} }
allContributions = cursor; allContributions = cursor;
@ -172,77 +51,27 @@ public class ContributionsListFragment extends SherlockFragment {
@Override @Override
public void onSaveInstanceState(Bundle outState) { public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState); super.onSaveInstanceState(outState);
controller.saveState(outState);
outState.putInt("grid-position", contributionsList.getFirstVisiblePosition()); outState.putInt("grid-position", contributionsList.getFirstVisiblePosition());
outState.putParcelable("lastGeneratedCaptureURI", lastGeneratedCaptureURI);
} }
@Override @Override
public void onActivityResult(int requestCode, int resultCode, Intent data) { public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data); super.onActivityResult(requestCode, resultCode, data);
switch(requestCode) { if(resultCode == Activity.RESULT_OK) {
case SELECT_FROM_GALLERY: controller.handleImagePicked(requestCode, data);
if(resultCode == Activity.RESULT_OK) {
Intent shareIntent = new Intent(getActivity(), ShareActivity.class);
shareIntent.setAction(Intent.ACTION_SEND);
shareIntent.setType(getActivity().getContentResolver().getType(data.getData()));
shareIntent.putExtra(Intent.EXTRA_STREAM, data.getData());
shareIntent.putExtra(UploadService.EXTRA_SOURCE, Contribution.SOURCE_GALLERY);
startActivity(shareIntent);
}
break;
case SELECT_FROM_CAMERA:
if(resultCode == Activity.RESULT_OK) {
Intent shareIntent = new Intent(getActivity(), ShareActivity.class);
shareIntent.setAction(Intent.ACTION_SEND);
Log.d("Commons", "Uri is " + lastGeneratedCaptureURI);
shareIntent.setType("image/jpeg"); //FIXME: Find out appropriate mime type
shareIntent.putExtra(Intent.EXTRA_STREAM, lastGeneratedCaptureURI);
shareIntent.putExtra(UploadService.EXTRA_SOURCE, Contribution.SOURCE_CAMERA);
startActivity(shareIntent);
}
break;
} }
} }
// See http://stackoverflow.com/a/5054673/17865 for why this is done
private Uri lastGeneratedCaptureURI;
private void reGenerateImageCaptureURI() {
String storageState = Environment.getExternalStorageState();
if(storageState.equals(Environment.MEDIA_MOUNTED)) {
String path = Environment.getExternalStorageDirectory().getAbsolutePath() + "/Commons/images/" + new Date().getTime() + ".jpg";
File _photoFile = new File(path);
try {
if(_photoFile.exists() == false) {
_photoFile.getParentFile().mkdirs();
_photoFile.createNewFile();
}
} catch (IOException e) {
Log.e("Commons", "Could not create file: " + path, e);
}
lastGeneratedCaptureURI = Uri.fromFile(_photoFile);
} else {
throw new RuntimeException("No external storage found!");
}
}
@Override @Override
public boolean onOptionsItemSelected(MenuItem item) { public boolean onOptionsItemSelected(MenuItem item) {
switch(item.getItemId()) { switch(item.getItemId()) {
case R.id.menu_from_gallery: case R.id.menu_from_gallery:
Intent pickImageIntent = new Intent(Intent.ACTION_GET_CONTENT); controller.startGalleryPick();
pickImageIntent.setType("image/*");
startActivityForResult(pickImageIntent, SELECT_FROM_GALLERY);
return true; return true;
case R.id.menu_from_camera: case R.id.menu_from_camera:
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); controller.startCameraCapture();
reGenerateImageCaptureURI();
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, lastGeneratedCaptureURI);
startActivityForResult(takePictureIntent, SELECT_FROM_CAMERA);
return true; return true;
case R.id.menu_settings: case R.id.menu_settings:
Intent settingsIntent = new Intent(getActivity(), SettingsActivity.class); Intent settingsIntent = new Intent(getActivity(), SettingsActivity.class);
@ -286,15 +115,16 @@ public class ContributionsListFragment extends SherlockFragment {
public void onActivityCreated(Bundle savedInstanceState) { public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState); super.onActivityCreated(savedInstanceState);
controller = new ContributionController(this);
controller.loadState(savedInstanceState);
contributionsList = (GridView)getView().findViewById(R.id.contributionsList); contributionsList = (GridView)getView().findViewById(R.id.contributionsList);
waitingMessage = (TextView)getView().findViewById(R.id.waitingMessage); waitingMessage = (TextView)getView().findViewById(R.id.waitingMessage);
emptyMessage = (TextView)getView().findViewById(R.id.waitingMessage); emptyMessage = (TextView)getView().findViewById(R.id.waitingMessage);
contributionDisplayOptions = Utils.getGenericDisplayOptions().build();
contributionsList.setOnItemClickListener((AdapterView.OnItemClickListener)getActivity()); contributionsList.setOnItemClickListener((AdapterView.OnItemClickListener)getActivity());
if(savedInstanceState != null) { if(savedInstanceState != null) {
Log.d("Commons", "Scrolling to " + savedInstanceState.getInt("grid-position")); Log.d("Commons", "Scrolling to " + savedInstanceState.getInt("grid-position"));
lastGeneratedCaptureURI = (Uri) savedInstanceState.getParcelable("lastGeneratedCaptureURI");
contributionsList.setSelection(savedInstanceState.getInt("grid-position")); contributionsList.setSelection(savedInstanceState.getInt("grid-position"));
} }