{
}
Timber.d("Before execution!");
- 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()));
-
+ curProgressNotification = getNotificationBuilder(
+ contribution,
+ CommonsApplication.NOTIFICATION_CHANNEL_ID_ALL);
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 78c1ca155..2e4592e40 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,12 +1,16 @@
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 {
@@ -92,4 +96,31 @@ 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 79dad33e5..dce29402c 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,6 +8,7 @@ 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;
@@ -29,11 +30,6 @@ 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,
@@ -41,13 +37,6 @@ 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
@@ -62,39 +51,15 @@ public class ImageUtils {
int loadImageWidth = bitmapRegionDecoder.getWidth();
int checkImageTopPosition = 0;
- int checkImageBottomPosition = loadImageHeight / 10;
int checkImageLeftPosition = 0;
- int checkImageRightPosition = loadImageWidth / 10;
- int totalDividedRectangles = 0;
- int numberOfDarkRectangles = 0;
+ Timber.v("left: " + checkImageLeftPosition + " right: " + loadImageWidth + " top: " + checkImageTopPosition + " bottom: " + loadImageHeight);
- while ((checkImageRightPosition <= loadImageWidth) && (checkImageLeftPosition < checkImageRightPosition)) {
- while ((checkImageBottomPosition <= loadImageHeight) && (checkImageTopPosition < checkImageBottomPosition)) {
- Timber.v("left: " + checkImageLeftPosition + " right: " + checkImageRightPosition + " top: " + checkImageTopPosition + " bottom: " + checkImageBottomPosition);
+ Rect rect = new Rect(checkImageLeftPosition,checkImageTopPosition, loadImageWidth, loadImageHeight);
- Rect rect = new Rect(checkImageLeftPosition,checkImageTopPosition,checkImageRightPosition,checkImageBottomPosition);
- totalDividedRectangles++;
+ Bitmap processBitmap = bitmapRegionDecoder.decodeRegion(rect,null);
- 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) {
+ if (checkIfImageIsDark(processBitmap)) {
return Result.IMAGE_DARK;
}
@@ -104,14 +69,12 @@ 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". 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.
- *
+ * 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.
* @param bitmap The bitmap that needs to be checked.
* @return true if bitmap is dark or null, false if bitmap is bright
*/
@@ -126,28 +89,45 @@ 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);
- boolean isImageDark = false;
- int darkPixelsCount = 0;
+ int numberOfBrightPixels = 0;
+ int numberOfMediumBrightnessPixels = 0;
+ double brightPixelThreshold = 0.025*allPixelsCount;
+ double mediumBrightPixelThreshold = 0.3*allPixelsCount;
for (int pixel : bitmapPixels) {
int r = Color.red(pixel);
int g = Color.green(pixel);
int b = Color.blue(pixel);
- 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;
+ int secondMax = r>g ? r:g;
+ double max = (secondMax>b ? secondMax:b)/255.0;
+
+ int secondMin = rmediumBrightnessLuminance){
+ numberOfMediumBrightnessPixels++;
}
}
- }
+ else {
+ numberOfBrightPixels++;
+ }
- return isImageDark;
+ if (numberOfBrightPixels>=brightPixelThreshold || numberOfMediumBrightnessPixels>=mediumBrightPixelThreshold){
+ return false;
+ }
+
+ }
+ return true;
}
/**
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
new file mode 100644
index 000000000..ecdc01511
--- /dev/null
+++ b/app/src/main/java/fr/free/nrw/commons/utils/PermissionUtils.java
@@ -0,0 +1,23 @@
+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 82bad3f09..c7c0cfb81 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,18 +3,27 @@ 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;
@@ -27,10 +36,7 @@ import fr.free.nrw.commons.R;
*/
public class PicOfDayAppWidget extends AppWidgetProvider {
- static void updateAppWidget(Context context, AppWidgetManager appWidgetManager,
- int appWidgetId) {
-
- // Construct the RemoteViews object
+ static void updateAppWidget(Context context, AppWidgetManager appWidgetManager, int appWidgetId) {
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.pic_of_day_app_widget);
String urlString = BuildConfig.WIKIMEDIA_API_POTD;
@@ -45,19 +51,37 @@ public class PicOfDayAppWidget extends AppWidgetProvider {
Elements elements = document.select("img");
String imageUrl = elements.get(0).attr("src");
if (imageUrl != null && imageUrl.length() > 0) {
- Picasso.get().load(imageUrl).into(views, R.id.appwidget_image, new int[]{appWidgetId});
+
+ 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());
}
}
-
}
@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 deb295438..ed1c8a578 100644
--- a/app/src/main/res/layout-land/activity_login.xml
+++ b/app/src/main/res/layout-land/activity_login.xml
@@ -4,6 +4,10 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
+
-
+
@@ -230,5 +233,15 @@
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 819260920..1fe7acf64 100644
--- a/app/src/main/res/layout-xlarge/activity_login.xml
+++ b/app/src/main/res/layout-xlarge/activity_login.xml
@@ -4,6 +4,11 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
+
+
-
+
@@ -230,5 +234,14 @@
tools:ignore="UnusedAttribute" />
+
+
diff --git a/app/src/main/res/layout/activity_login.xml b/app/src/main/res/layout/activity_login.xml
index 5d1345bd3..0470cf720 100644
--- a/app/src/main/res/layout/activity_login.xml
+++ b/app/src/main/res/layout/activity_login.xml
@@ -207,6 +207,16 @@
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 a984eff90..51b0c711c 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_marginLeft="@dimen/activity_margin_horizontal"
+ android:layout_centerHorizontal="true"
android:paddingBottom="@dimen/small_gap"/>
+
+
+ android:text="@string/nominate_deletion"/>
diff --git a/app/src/main/res/layout/fragment_single_upload.xml b/app/src/main/res/layout/fragment_single_upload.xml
index 00cc1d194..196760bb0 100644
--- a/app/src/main/res/layout/fragment_single_upload.xml
+++ b/app/src/main/res/layout/fragment_single_upload.xml
@@ -12,6 +12,7 @@
android:paddingRight="@dimen/standard_gap"
android:paddingStart="@dimen/standard_gap"
android:paddingTop="@dimen/small_gap"
+ android:nestedScrollingEnabled="false"
android:theme="@style/DarkAppTheme">
-
-
-
-
-
-
-
-
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+
+ style="@style/TextAppearance.AppCompat.Body1"
+ android:text="@string/add_description"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+
+