{
}
Timber.d("Before execution!");
- curProgressNotification = getNotificationBuilder(
- contribution,
- CommonsApplication.NOTIFICATION_CHANNEL_ID_ALL);
+ curProgressNotification = new NotificationCompat.Builder(this).setAutoCancel(true)
+ .setSmallIcon(R.drawable.ic_launcher)
+ .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher))
+ .setAutoCancel(true)
+ .setContentTitle(getString(R.string.upload_progress_notification_title_start, contribution.getDisplayTitle()))
+ .setContentText(getResources().getQuantityString(R.plurals.uploads_pending_notification_indicator, toUpload, toUpload))
+ .setOngoing(true)
+ .setProgress(100, 0, true)
+ .setContentIntent(PendingIntent.getActivity(this, 0, new Intent(this, ContributionsActivity.class), 0))
+ .setTicker(getString(R.string.upload_progress_notification_title_in_progress, contribution.getDisplayTitle()));
+
this.startForeground(NOTIFICATION_UPLOAD_IN_PROGRESS, curProgressNotification.build());
String filename = null;
diff --git a/app/src/main/java/fr/free/nrw/commons/utils/DialogUtil.java b/app/src/main/java/fr/free/nrw/commons/utils/DialogUtil.java
index 2e4592e40..78c1ca155 100644
--- a/app/src/main/java/fr/free/nrw/commons/utils/DialogUtil.java
+++ b/app/src/main/java/fr/free/nrw/commons/utils/DialogUtil.java
@@ -1,16 +1,12 @@
package fr.free.nrw.commons.utils;
import android.app.Activity;
-import android.app.AlertDialog;
-import android.app.AlertDialog.Builder;
import android.app.Dialog;
-import android.content.Context;
import android.os.Build;
import android.support.annotation.Nullable;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.FragmentActivity;
-import fr.free.nrw.commons.R;
import timber.log.Timber;
public class DialogUtil {
@@ -96,31 +92,4 @@ public class DialogUtil {
Timber.e(e, "Could not show dialog.");
}
}
-
- public static AlertDialog getAlertDialogWithPositiveAndNegativeCallbacks(
- Context context, String title, String message, int iconResourceId, Callback callback) {
-
- AlertDialog alertDialog = new Builder(context)
- .setTitle(title)
- .setMessage(message)
- .setPositiveButton(context.getString(R.string.ok), (dialog, which) -> {
- callback.onPositiveButtonClicked();
- dialog.dismiss();
- })
- .setNegativeButton(context.getString(R.string.cancel), (dialog, which) -> {
- callback.onNegativeButtonClicked();
- dialog.dismiss();
- })
- .setIcon(iconResourceId).create();
-
- return alertDialog;
-
- }
-
- public interface Callback {
-
- void onPositiveButtonClicked();
-
- void onNegativeButtonClicked();
- }
}
diff --git a/app/src/main/java/fr/free/nrw/commons/utils/ImageUtils.java b/app/src/main/java/fr/free/nrw/commons/utils/ImageUtils.java
index dce29402c..79dad33e5 100644
--- a/app/src/main/java/fr/free/nrw/commons/utils/ImageUtils.java
+++ b/app/src/main/java/fr/free/nrw/commons/utils/ImageUtils.java
@@ -8,7 +8,6 @@ import android.graphics.Color;
import android.graphics.Rect;
import android.net.Uri;
import android.support.annotation.Nullable;
-import android.util.Log;
import com.facebook.common.executors.CallerThreadExecutor;
import com.facebook.common.references.CloseableReference;
@@ -30,6 +29,11 @@ import timber.log.Timber;
*/
public class ImageUtils {
+ //atleast 50% of the image in question should be considered dark for the entire image to be dark
+ private static final double MINIMUM_DARKNESS_FACTOR = 0.50;
+ //atleast 50% of the image in question should be considered blurry for the entire image to be blurry
+ private static final double MINIMUM_BLURRYNESS_FACTOR = 0.50;
+ private static final int LAPLACIAN_VARIANCE_THRESHOLD = 70;
public enum Result {
IMAGE_DARK,
@@ -37,6 +41,13 @@ public class ImageUtils {
}
/**
+ * BitmapRegionDecoder allows us to process a large bitmap by breaking it down into smaller rectangles. The rectangles
+ * are obtained by setting an initial width, height and start position of the rectangle as a factor of the width and
+ * height of the original bitmap and then manipulating the width, height and position to loop over the entire original
+ * bitmap. Each individual rectangle is independently processed to check if its too dark. Based on
+ * the factor of "bright enough" individual rectangles amongst the total rectangles into which the image
+ * was divided, we will declare the image as wanted/unwanted
+ *
* @param bitmapRegionDecoder BitmapRegionDecoder for the image we wish to process
* @return Result.IMAGE_OK if image is neither dark nor blurry or if the input bitmapRegionDecoder provided is null
* Result.IMAGE_DARK if image is too dark
@@ -51,15 +62,39 @@ public class ImageUtils {
int loadImageWidth = bitmapRegionDecoder.getWidth();
int checkImageTopPosition = 0;
+ int checkImageBottomPosition = loadImageHeight / 10;
int checkImageLeftPosition = 0;
+ int checkImageRightPosition = loadImageWidth / 10;
- Timber.v("left: " + checkImageLeftPosition + " right: " + loadImageWidth + " top: " + checkImageTopPosition + " bottom: " + loadImageHeight);
+ int totalDividedRectangles = 0;
+ int numberOfDarkRectangles = 0;
- Rect rect = new Rect(checkImageLeftPosition,checkImageTopPosition, loadImageWidth, loadImageHeight);
+ while ((checkImageRightPosition <= loadImageWidth) && (checkImageLeftPosition < checkImageRightPosition)) {
+ while ((checkImageBottomPosition <= loadImageHeight) && (checkImageTopPosition < checkImageBottomPosition)) {
+ Timber.v("left: " + checkImageLeftPosition + " right: " + checkImageRightPosition + " top: " + checkImageTopPosition + " bottom: " + checkImageBottomPosition);
- Bitmap processBitmap = bitmapRegionDecoder.decodeRegion(rect,null);
+ Rect rect = new Rect(checkImageLeftPosition,checkImageTopPosition,checkImageRightPosition,checkImageBottomPosition);
+ totalDividedRectangles++;
- if (checkIfImageIsDark(processBitmap)) {
+ Bitmap processBitmap = bitmapRegionDecoder.decodeRegion(rect,null);
+
+ if (checkIfImageIsDark(processBitmap)) {
+ numberOfDarkRectangles++;
+ }
+
+ checkImageTopPosition = checkImageBottomPosition;
+ checkImageBottomPosition += (checkImageBottomPosition < (loadImageHeight - checkImageBottomPosition)) ? checkImageBottomPosition : (loadImageHeight - checkImageBottomPosition);
+ }
+
+ checkImageTopPosition = 0; //reset to start
+ checkImageBottomPosition = loadImageHeight / 10; //reset to start
+ checkImageLeftPosition = checkImageRightPosition;
+ checkImageRightPosition += (checkImageRightPosition < (loadImageWidth - checkImageRightPosition)) ? checkImageRightPosition : (loadImageWidth - checkImageRightPosition);
+ }
+
+ Timber.d("dark rectangles count = " + numberOfDarkRectangles + ", total rectangles count = " + totalDividedRectangles);
+
+ if (numberOfDarkRectangles > totalDividedRectangles * MINIMUM_DARKNESS_FACTOR) {
return Result.IMAGE_DARK;
}
@@ -69,12 +104,14 @@ public class ImageUtils {
/**
* Pulls the pixels into an array and then runs through it while checking the brightness of each pixel.
* The calculation of brightness of each pixel is done by extracting the RGB constituents of the pixel
- * and then applying the formula to calculate its "Luminance".
- * Pixels with luminance greater than 40% are considered to be bright pixels while the ones with luminance
- * greater than 26% but less than 40% are considered to be pixels with medium brightness. The rest are
- * dark pixels.
- * If the number of bright pixels is more than 2.5% or the number of pixels with medium brightness is
- * more than 30% of the total number of pixels then the image is considered to be OK else dark.
+ * and then applying the formula to calculate its "Luminance". If this brightness value is less than
+ * 50 then the pixel is considered to be dark. Based on the MINIMUM_DARKNESS_FACTOR if enough pixels
+ * are dark then the entire bitmap is considered to be dark.
+ *
+ * For more information on this brightness/darkness calculation technique refer the accepted answer
+ * on this -> https://stackoverflow.com/questions/35914461/how-to-detect-dark-photos-in-android/35914745
+ * SO question and follow the trail.
+ *
* @param bitmap The bitmap that needs to be checked.
* @return true if bitmap is dark or null, false if bitmap is bright
*/
@@ -89,45 +126,28 @@ public class ImageUtils {
int allPixelsCount = bitmapWidth * bitmapHeight;
int[] bitmapPixels = new int[allPixelsCount];
- Log.e("total", Integer.toString(allPixelsCount));
bitmap.getPixels(bitmapPixels,0,bitmapWidth,0,0,bitmapWidth,bitmapHeight);
- int numberOfBrightPixels = 0;
- int numberOfMediumBrightnessPixels = 0;
- double brightPixelThreshold = 0.025*allPixelsCount;
- double mediumBrightPixelThreshold = 0.3*allPixelsCount;
+ boolean isImageDark = false;
+ int darkPixelsCount = 0;
for (int pixel : bitmapPixels) {
int r = Color.red(pixel);
int g = Color.green(pixel);
int b = Color.blue(pixel);
- int secondMax = r>g ? r:g;
- double max = (secondMax>b ? secondMax:b)/255.0;
-
- int secondMin = rmediumBrightnessLuminance){
- numberOfMediumBrightnessPixels++;
+ int brightness = (int) (0.2126 * r + 0.7152 * g + 0.0722 * b);
+ if (brightness < 50) {
+ //pixel is dark
+ darkPixelsCount++;
+ if (darkPixelsCount > allPixelsCount * MINIMUM_DARKNESS_FACTOR) {
+ isImageDark = true;
+ break;
}
}
- else {
- numberOfBrightPixels++;
- }
-
- if (numberOfBrightPixels>=brightPixelThreshold || numberOfMediumBrightnessPixels>=mediumBrightPixelThreshold){
- return false;
- }
-
}
- return true;
+
+ return isImageDark;
}
/**
diff --git a/app/src/main/java/fr/free/nrw/commons/utils/PermissionUtils.java b/app/src/main/java/fr/free/nrw/commons/utils/PermissionUtils.java
deleted file mode 100644
index ecdc01511..000000000
--- a/app/src/main/java/fr/free/nrw/commons/utils/PermissionUtils.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package fr.free.nrw.commons.utils;
-
-import android.app.Activity;
-import android.content.Intent;
-import android.net.Uri;
-import android.provider.Settings;
-import fr.free.nrw.commons.CommonsApplication;
-
-public class PermissionUtils {
-
- /**
- * This method can be used by any activity which requires a permission which has been blocked(marked never ask again by the user)
- It open the app settings from where the user can manually give us the required permission.
- * @param activity
- */
- public static void askUserToManuallyEnablePermissionFromSettings(
- Activity activity) {
- Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
- Uri uri = Uri.fromParts("package", activity.getPackageName(), null);
- intent.setData(uri);
- activity.startActivityForResult(intent,CommonsApplication.OPEN_APPLICATION_DETAIL_SETTINGS);
- }
-}
diff --git a/app/src/main/java/fr/free/nrw/commons/widget/PicOfDayAppWidget.java b/app/src/main/java/fr/free/nrw/commons/widget/PicOfDayAppWidget.java
index c7c0cfb81..82bad3f09 100644
--- a/app/src/main/java/fr/free/nrw/commons/widget/PicOfDayAppWidget.java
+++ b/app/src/main/java/fr/free/nrw/commons/widget/PicOfDayAppWidget.java
@@ -3,27 +3,18 @@ package fr.free.nrw.commons.widget;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.net.Uri;
-import android.support.annotation.Nullable;
import android.widget.RemoteViews;
-import com.facebook.common.executors.CallerThreadExecutor;
-import com.facebook.common.references.CloseableReference;
-import com.facebook.datasource.DataSource;
-import com.facebook.drawee.backends.pipeline.Fresco;
-import com.facebook.imagepipeline.core.ImagePipeline;
-import com.facebook.imagepipeline.datasource.BaseBitmapDataSubscriber;
-import com.facebook.imagepipeline.image.CloseableImage;
-import com.facebook.imagepipeline.request.ImageRequest;
-import com.facebook.imagepipeline.request.ImageRequestBuilder;
import com.prof.rssparser.Article;
import com.prof.rssparser.Parser;
+import com.squareup.picasso.Picasso;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
+import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import java.util.ArrayList;
@@ -36,7 +27,10 @@ import fr.free.nrw.commons.R;
*/
public class PicOfDayAppWidget extends AppWidgetProvider {
- static void updateAppWidget(Context context, AppWidgetManager appWidgetManager, int appWidgetId) {
+ static void updateAppWidget(Context context, AppWidgetManager appWidgetManager,
+ int appWidgetId) {
+
+ // Construct the RemoteViews object
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.pic_of_day_app_widget);
String urlString = BuildConfig.WIKIMEDIA_API_POTD;
@@ -51,37 +45,19 @@ public class PicOfDayAppWidget extends AppWidgetProvider {
Elements elements = document.select("img");
String imageUrl = elements.get(0).attr("src");
if (imageUrl != null && imageUrl.length() > 0) {
-
- ImageRequest request = ImageRequestBuilder.newBuilderWithSource(Uri.parse(imageUrl)).build();
- ImagePipeline imagePipeline = Fresco.getImagePipeline();
- DataSource> dataSource
- = imagePipeline.fetchDecodedImage(request, context);
- dataSource.subscribe(new BaseBitmapDataSubscriber() {
- @Override
- protected void onNewResultImpl(@Nullable Bitmap tempBitmap) {
- Bitmap bitmap = null;
- if (tempBitmap != null) {
- bitmap = Bitmap.createBitmap(tempBitmap.getWidth(), tempBitmap.getHeight(), Bitmap.Config.ARGB_8888);
- Canvas canvas = new Canvas(bitmap);
- canvas.drawBitmap(tempBitmap, 0f, 0f, new Paint());
- }
- views.setImageViewBitmap(R.id.appwidget_image, bitmap);
- appWidgetManager.updateAppWidget(appWidgetId, views);
- }
-
- @Override
- protected void onFailureImpl(DataSource> dataSource) {
- // Ignore failure for now.
- }
- }, CallerThreadExecutor.getInstance());
+ Picasso.get().load(imageUrl).into(views, R.id.appwidget_image, new int[]{appWidgetId});
}
}
+
}
@Override
public void onError() {
}
});
+
+ // Instruct the widget manager to update the widget
+ appWidgetManager.updateAppWidget(appWidgetId, views);
}
@Override
diff --git a/app/src/main/res/layout-land/activity_login.xml b/app/src/main/res/layout-land/activity_login.xml
index ed1c8a578..deb295438 100644
--- a/app/src/main/res/layout-land/activity_login.xml
+++ b/app/src/main/res/layout-land/activity_login.xml
@@ -4,10 +4,6 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
-
-
+
@@ -233,15 +230,5 @@
tools:ignore="UnusedAttribute" />
-
-
diff --git a/app/src/main/res/layout-xlarge/activity_login.xml b/app/src/main/res/layout-xlarge/activity_login.xml
index 1fe7acf64..819260920 100644
--- a/app/src/main/res/layout-xlarge/activity_login.xml
+++ b/app/src/main/res/layout-xlarge/activity_login.xml
@@ -4,11 +4,6 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
-
-
-
+
@@ -234,14 +230,5 @@
tools:ignore="UnusedAttribute" />
-
-
diff --git a/app/src/main/res/layout/activity_login.xml b/app/src/main/res/layout/activity_login.xml
index 0470cf720..5d1345bd3 100644
--- a/app/src/main/res/layout/activity_login.xml
+++ b/app/src/main/res/layout/activity_login.xml
@@ -207,16 +207,6 @@
android:layout_marginBottom="@dimen/standard_gap"
android:text="@string/forgot_password" />
-
-
diff --git a/app/src/main/res/layout/drawer_header.xml b/app/src/main/res/layout/drawer_header.xml
index 51b0c711c..a984eff90 100644
--- a/app/src/main/res/layout/drawer_header.xml
+++ b/app/src/main/res/layout/drawer_header.xml
@@ -25,7 +25,7 @@
android:textColor="@color/item_white_background"
android:textSize="@dimen/subheading_text_size"
android:layout_below="@+id/pictureOfTheDay"
- android:layout_centerHorizontal="true"
+ android:layout_marginLeft="@dimen/activity_margin_horizontal"
android:paddingBottom="@dimen/small_gap"/>
-
-
+ android:text="@string/nominate_deletion"
+ android:visibility="gone"/>
diff --git a/app/src/main/res/layout/fragment_single_upload.xml b/app/src/main/res/layout/fragment_single_upload.xml
index 196760bb0..00cc1d194 100644
--- a/app/src/main/res/layout/fragment_single_upload.xml
+++ b/app/src/main/res/layout/fragment_single_upload.xml
@@ -12,7 +12,6 @@
android:paddingRight="@dimen/standard_gap"
android:paddingStart="@dimen/standard_gap"
android:paddingTop="@dimen/small_gap"
- android:nestedScrollingEnabled="false"
android:theme="@style/DarkAppTheme">
-
-
-
-
-
-
-
+ android:layout_height="wrap_content">
+
+
+
+
+
+
+
+
+