diff --git a/app/build.gradle b/app/build.gradle index 5bafd3f15..3a7547d58 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -10,6 +10,7 @@ dependencies { implementation 'com.github.nicolas-raoul:Quadtree:ac16ea8035bf07' implementation 'fr.avianey.com.viewpagerindicator:library:2.4.1.1@aar' implementation 'in.yuvi:http.fluent:1.3' + implementation 'com.github.chrisbanes:PhotoView:2.0.0' implementation 'com.android.volley:volley:1.0.0' implementation 'ch.acra:acra:4.9.2' implementation 'org.mediawiki:api:1.3' diff --git a/app/src/main/java/fr/free/nrw/commons/upload/MultipleUploadListFragment.java b/app/src/main/java/fr/free/nrw/commons/upload/MultipleUploadListFragment.java index 5b39b92f7..34fe1d0cb 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/MultipleUploadListFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/MultipleUploadListFragment.java @@ -11,7 +11,6 @@ import android.text.Editable; import android.text.TextUtils; import android.text.TextWatcher; import android.util.DisplayMetrics; -import android.util.Log; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; diff --git a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java index aca17601c..aea703f5e 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java @@ -2,33 +2,50 @@ package fr.free.nrw.commons.upload; import android.Manifest; import android.app.Activity; +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; import android.content.ContentResolver; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.PackageManager; +import android.graphics.Bitmap; +import android.graphics.BitmapRegionDecoder; +import android.graphics.Point; +import android.graphics.Rect; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.Environment; import android.os.ParcelFileDescriptor; +import android.provider.MediaStore; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.annotation.RequiresApi; +import android.support.design.widget.FloatingActionButton; import android.support.design.widget.Snackbar; import android.support.graphics.drawable.VectorDrawableCompat; import android.support.v4.app.ActivityCompat; import android.support.v4.app.FragmentManager; import android.support.v4.content.ContextCompat; +import android.support.v4.graphics.BitmapCompat; import android.support.v7.app.AlertDialog; +import android.util.Log; import android.view.MenuItem; import android.view.View; +import android.view.WindowManager; +import android.view.animation.DecelerateInterpolator; +import android.view.inputmethod.InputMethodManager; import android.widget.TextView; import android.widget.Toast; import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder; import com.facebook.drawee.view.SimpleDraweeView; +import com.github.chrisbanes.photoview.PhotoView; + import java.io.File; import java.io.FileNotFoundException; @@ -63,8 +80,11 @@ import fr.free.nrw.commons.utils.ImageUtils; import fr.free.nrw.commons.mwapi.MediaWikiApi; import timber.log.Timber; + + import static fr.free.nrw.commons.upload.ExistingFileAsync.Result.DUPLICATE_PROCEED; import static fr.free.nrw.commons.upload.ExistingFileAsync.Result.NO_DUPLICATE; +import static java.lang.Long.min; /** * Activity for the title/desc screen after image is selected. Also starts processing image @@ -120,6 +140,12 @@ public class ShareActivity private boolean haveCheckedForOtherImages = false; private boolean isNearbyUpload = false; + private Animator CurrentAnimator; + private long ShortAnimationDuration; + private FloatingActionButton zoomInButton; + private FloatingActionButton zoomOutButton; + + /** * Called when user taps the submit button. */ @@ -257,6 +283,18 @@ public class ShareActivity if (mediaUri != null) { backgroundImageView.setImageURI(mediaUri); } + zoomInButton = (FloatingActionButton) findViewById(R.id.media_upload_zoom_in); + try { + zoomInButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + zoomImageFromThumb(backgroundImageView, mediaUri); + } + }); + } catch (Exception e){ + Log.i("exception", e.toString()); + } + zoomOutButton = (FloatingActionButton) findViewById(R.id.media_upload_zoom_out); if (savedInstanceState != null) { contribution = savedInstanceState.getParcelable("contribution"); @@ -690,4 +728,189 @@ public class ShareActivity } } } + + private void zoomImageFromThumb(final View thumbView, Uri imageuri ) { + // If there's an animation in progress, cancel it + // immediately and proceed with this one. + if (CurrentAnimator != null) { + CurrentAnimator.cancel(); + } + hideKeyboard(ShareActivity.this); + InputStream input = null; + Bitmap scaled = null; + try { + input = this.getContentResolver().openInputStream(imageuri); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } + BitmapRegionDecoder decoder = null; + try { + decoder = BitmapRegionDecoder.newInstance(input, false); + } catch (IOException e) { + e.printStackTrace(); + } + Bitmap bitmap = decoder.decodeRegion(new Rect(10, 10, 50, 50), null); + try { + //Compress the Image + System.gc(); + Runtime rt = Runtime.getRuntime(); + long maxMemory = rt.freeMemory(); + bitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), imageuri); + int bitmapByteCount= BitmapCompat.getAllocationByteCount(bitmap); + long height = bitmap.getHeight(); + long width = bitmap.getWidth(); + long calHeight = (long) ((height * maxMemory)/(bitmapByteCount * 1.1)); + long calWidth = (long) ((width * maxMemory)/(bitmapByteCount * 1.1)); + scaled = Bitmap.createScaledBitmap(bitmap,(int) Math.min(width,calWidth), (int) Math.min(height,calHeight), true); + } catch (IOException e) { + } catch (NullPointerException e){ + scaled = bitmap; + } + // Load the high-resolution "zoomed-in" image. + PhotoView expandedImageView = (PhotoView) findViewById( + R.id.expanded_image); + expandedImageView.setImageBitmap(scaled); + + + + // Calculate the starting and ending bounds for the zoomed-in image. + // This step involves lots of math. Yay, math. + final Rect startBounds = new Rect(); + final Rect finalBounds = new Rect(); + final Point globalOffset = new Point(); + + // The start bounds are the global visible rectangle of the thumbnail, + // and the final bounds are the global visible rectangle of the container + // view. Also set the container view's offset as the origin for the + // bounds, since that's the origin for the positioning animation + // properties (X, Y). + thumbView.getGlobalVisibleRect(startBounds); + findViewById(R.id.container) + .getGlobalVisibleRect(finalBounds, globalOffset); + startBounds.offset(-globalOffset.x, -globalOffset.y); + finalBounds.offset(-globalOffset.x, -globalOffset.y); + + // Adjust the start bounds to be the same aspect ratio as the final + // bounds using the "center crop" technique. This prevents undesirable + // stretching during the animation. Also calculate the start scaling + // factor (the end scaling factor is always 1.0). + float startScale; + if ((float) finalBounds.width() / finalBounds.height() + > (float) startBounds.width() / startBounds.height()) { + // Extend start bounds horizontally + startScale = (float) startBounds.height() / finalBounds.height(); + float startWidth = startScale * finalBounds.width(); + float deltaWidth = (startWidth - startBounds.width()) / 2; + startBounds.left -= deltaWidth; + startBounds.right += deltaWidth; + } else { + // Extend start bounds vertically + startScale = (float) startBounds.width() / finalBounds.width(); + float startHeight = startScale * finalBounds.height(); + float deltaHeight = (startHeight - startBounds.height()) / 2; + startBounds.top -= deltaHeight; + startBounds.bottom += deltaHeight; + } + + // Hide the thumbnail and show the zoomed-in view. When the animation + // begins, it will position the zoomed-in view in the place of the + // thumbnail. + thumbView.setAlpha(0f); + expandedImageView.setVisibility(View.VISIBLE); + zoomOutButton.setVisibility(View.VISIBLE); + zoomInButton.setVisibility(View.GONE); + + // Set the pivot point for SCALE_X and SCALE_Y transformations + // to the top-left corner of the zoomed-in view (the default + // is the center of the view). + expandedImageView.setPivotX(0f); + expandedImageView.setPivotY(0f); + + // Construct and run the parallel animation of the four translation and + // scale properties (X, Y, SCALE_X, and SCALE_Y). + AnimatorSet set = new AnimatorSet(); + set + .play(ObjectAnimator.ofFloat(expandedImageView, View.X, + startBounds.left, finalBounds.left)) + .with(ObjectAnimator.ofFloat(expandedImageView, View.Y, + startBounds.top, finalBounds.top)) + .with(ObjectAnimator.ofFloat(expandedImageView, View.SCALE_X, + startScale, 1f)) + .with(ObjectAnimator.ofFloat(expandedImageView, + View.SCALE_Y, startScale, 1f)); + set.setDuration(ShortAnimationDuration); + set.setInterpolator(new DecelerateInterpolator()); + set.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + CurrentAnimator = null; + } + + @Override + public void onAnimationCancel(Animator animation) { + CurrentAnimator = null; + } + }); + set.start(); + CurrentAnimator = set; + + // Upon clicking the zoomed-in image, it should zoom back down + // to the original bounds and show the thumbnail instead of + // the expanded image. + final float startScaleFinal = startScale; + zoomOutButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + if (CurrentAnimator != null) { + CurrentAnimator.cancel(); + } + zoomOutButton.setVisibility(View.GONE); + zoomInButton.setVisibility(View.VISIBLE); + + // Animate the four positioning/sizing properties in parallel, + // back to their original values. + AnimatorSet set = new AnimatorSet(); + set.play(ObjectAnimator + .ofFloat(expandedImageView, View.X, startBounds.left)) + .with(ObjectAnimator + .ofFloat(expandedImageView, + View.Y,startBounds.top)) + .with(ObjectAnimator + .ofFloat(expandedImageView, + View.SCALE_X, startScaleFinal)) + .with(ObjectAnimator + .ofFloat(expandedImageView, + View.SCALE_Y, startScaleFinal)); + set.setDuration(ShortAnimationDuration); + set.setInterpolator(new DecelerateInterpolator()); + set.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + thumbView.setAlpha(1f); + expandedImageView.setVisibility(View.GONE); + CurrentAnimator = null; + } + + @Override + public void onAnimationCancel(Animator animation) { + thumbView.setAlpha(1f); + expandedImageView.setVisibility(View.GONE); + CurrentAnimator = null; + } + }); + set.start(); + CurrentAnimator = set; + + } + + }); + } + public static void hideKeyboard(Activity activity) { + View view = activity.findViewById(R.id.titleEdit | R.id.descEdit); + if (view != null) { + InputMethodManager imm = (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE); + imm.hideSoftInputFromWindow(view.getWindowToken(), 0); + } + } + } diff --git a/app/src/main/java/fr/free/nrw/commons/upload/SingleUploadFragment.java b/app/src/main/java/fr/free/nrw/commons/upload/SingleUploadFragment.java index a88d03c03..d0e38dafe 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/SingleUploadFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/SingleUploadFragment.java @@ -11,6 +11,7 @@ import android.net.Uri; import android.os.Bundle; import android.preference.PreferenceManager; import android.support.annotation.NonNull; +import android.support.design.widget.FloatingActionButton; import android.support.v4.view.ViewCompat; import android.support.v7.app.AlertDialog; import android.text.Editable; @@ -61,6 +62,7 @@ public class SingleUploadFragment extends CommonsDaggerSupportFragment { @BindView(R.id.share_license_summary) TextView licenseSummaryView; @BindView(R.id.licenseSpinner) Spinner licenseSpinner; + @Inject @Named("default_preferences") SharedPreferences prefs; @Inject @Named("direct_nearby_upload_prefs") SharedPreferences directPrefs; @@ -346,6 +348,7 @@ public class SingleUploadFragment extends CommonsDaggerSupportFragment { } } + private void showInfoAlert (int titleStringID, int messageStringID){ new AlertDialog.Builder(getContext()) .setTitle(titleStringID) diff --git a/app/src/main/res/drawable-hdpi/ic_zoom_in_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_zoom_in_white_24dp.png new file mode 100644 index 000000000..637329408 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_zoom_in_white_24dp.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_zoom_out_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_zoom_out_white_24dp.png new file mode 100644 index 000000000..7772ceea7 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_zoom_out_white_24dp.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_zoom_in_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_zoom_in_white_24dp.png new file mode 100644 index 000000000..36a659dd2 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_zoom_in_white_24dp.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_zoom_out_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_zoom_out_white_24dp.png new file mode 100644 index 000000000..e69da0ba0 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_zoom_out_white_24dp.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_zoom_in_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_zoom_in_white_24dp.png new file mode 100644 index 000000000..ce88ebdd7 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_zoom_in_white_24dp.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_zoom_out_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_zoom_out_white_24dp.png new file mode 100644 index 000000000..8c6ccea7b Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_zoom_out_white_24dp.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_zoom_in_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_zoom_in_white_24dp.png new file mode 100644 index 000000000..30b847067 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_zoom_in_white_24dp.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_zoom_out_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_zoom_out_white_24dp.png new file mode 100644 index 000000000..68aaec6d8 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_zoom_out_white_24dp.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_zoom_in_white_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_zoom_in_white_24dp.png new file mode 100644 index 000000000..b636f6e3c Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_zoom_in_white_24dp.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_zoom_out_white_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_zoom_out_white_24dp.png new file mode 100644 index 000000000..d85e242b9 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_zoom_out_white_24dp.png differ diff --git a/app/src/main/res/drawable/ic_zoom_in_white_24dp.png b/app/src/main/res/drawable/ic_zoom_in_white_24dp.png new file mode 100644 index 000000000..637329408 Binary files /dev/null and b/app/src/main/res/drawable/ic_zoom_in_white_24dp.png differ diff --git a/app/src/main/res/drawable/ic_zoom_out_white_24dp.png b/app/src/main/res/drawable/ic_zoom_out_white_24dp.png new file mode 100644 index 000000000..7772ceea7 Binary files /dev/null and b/app/src/main/res/drawable/ic_zoom_out_white_24dp.png differ diff --git a/app/src/main/res/layout/activity_share.xml b/app/src/main/res/layout/activity_share.xml index e915517b5..ca8097495 100644 --- a/app/src/main/res/layout/activity_share.xml +++ b/app/src/main/res/layout/activity_share.xml @@ -19,7 +19,8 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:layout_below="@id/toolbar" - android:background="?attr/mainBackground"> + android:background="?attr/mainBackground" + android:id="@+id/container"> + + + + + + + + diff --git a/app/src/main/res/layout/layout_upload_item.xml b/app/src/main/res/layout/layout_upload_item.xml index fb552560a..155664d91 100644 --- a/app/src/main/res/layout/layout_upload_item.xml +++ b/app/src/main/res/layout/layout_upload_item.xml @@ -34,6 +34,7 @@ android:textColor="#FFFFFFFF" style="?android:textAppearanceSmall" /> + \ No newline at end of file