diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml index fa2ec0f3b399..1f602a461044 100644 --- a/src/main/AndroidManifest.xml +++ b/src/main/AndroidManifest.xml @@ -112,6 +112,7 @@ + diff --git a/src/main/java/com/owncloud/android/db/PreferenceManager.java b/src/main/java/com/owncloud/android/db/PreferenceManager.java index 5d42b7d1ff1b..76a4ee1615bb 100644 --- a/src/main/java/com/owncloud/android/db/PreferenceManager.java +++ b/src/main/java/com/owncloud/android/db/PreferenceManager.java @@ -67,6 +67,7 @@ public final class PreferenceManager { private static final String PREF__FOLDER_SORT_ORDER = "folder_sort_order"; private static final String PREF__FOLDER_LAYOUT = "folder_layout"; public static final String PREF__LOCK_TIMESTAMP = "lock_timestamp"; + private static final String PREF__SHOW_MEDIA_SCAN_NOTIFICATIONS = "show_media_scan_notifications"; private PreferenceManager() { } @@ -550,6 +551,14 @@ public static void setShowDetailedTimestamp(Context context, boolean showDetaile saveBooleanPreference(context, AUTO_PREF__SHOW_DETAILED_TIMESTAMP, showDetailedTimestamp); } + public static boolean isShowMediaScanNotifications(Context context) { + return getDefaultSharedPreferences(context).getBoolean(PREF__SHOW_MEDIA_SCAN_NOTIFICATIONS, true); + } + + public static void setShowMediaScanNotifications(Context context, boolean value) { + saveBooleanPreference(context, PREF__SHOW_MEDIA_SCAN_NOTIFICATIONS, value); + } + private static void saveBooleanPreference(Context context, String key, boolean value) { SharedPreferences.Editor appPreferences = getDefaultSharedPreferences(context.getApplicationContext()).edit(); appPreferences.putBoolean(key, value).apply(); diff --git a/src/main/java/com/owncloud/android/jobs/MediaFoldersDetectionJob.java b/src/main/java/com/owncloud/android/jobs/MediaFoldersDetectionJob.java index ae0add5fdc72..5adc5a166f60 100644 --- a/src/main/java/com/owncloud/android/jobs/MediaFoldersDetectionJob.java +++ b/src/main/java/com/owncloud/android/jobs/MediaFoldersDetectionJob.java @@ -23,8 +23,10 @@ import android.accounts.Account; +import android.app.Activity; import android.app.NotificationManager; import android.app.PendingIntent; +import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; @@ -41,6 +43,8 @@ import com.owncloud.android.datamodel.MediaFoldersModel; import com.owncloud.android.datamodel.MediaProvider; import com.owncloud.android.datamodel.SyncedFolderProvider; +import com.owncloud.android.db.PreferenceManager; +import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.ui.activity.ManageAccountsActivity; import com.owncloud.android.ui.activity.SyncedFoldersActivity; import com.owncloud.android.ui.notifications.NotificationUtils; @@ -48,10 +52,13 @@ import java.util.ArrayList; import java.util.List; +import java.util.Random; import androidx.annotation.NonNull; import androidx.core.app.NotificationCompat; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +@SuppressFBWarnings(value = "PREDICTABLE_RANDOM", justification = "Only used for notification id.") public class MediaFoldersDetectionJob extends Job { public static final String TAG = "MediaFoldersDetectionJob"; @@ -60,6 +67,11 @@ public class MediaFoldersDetectionJob extends Job { private static final String ACCOUNT_NAME_GLOBAL = "global"; private static final String KEY_MEDIA_FOLDERS = "media_folders"; + public static final String NOTIFICATION_ID = "NOTIFICATION_ID"; + + private static final String DISABLE_DETECTION_CLICK = "DISABLE_DETECTION_CLICK"; + + private Random randomId = new Random(); @NonNull @Override @@ -78,11 +90,11 @@ protected Result onRunJob(@NonNull Params params) { List imageMediaFolderPaths = new ArrayList<>(); List videoMediaFolderPaths = new ArrayList<>(); - for (MediaFolder imageMediaFolder: imageMediaFolders) { + for (MediaFolder imageMediaFolder : imageMediaFolders) { imageMediaFolderPaths.add(imageMediaFolder.absolutePath); } - for (MediaFolder videoMediaFolder: videoMediaFolders) { + for (MediaFolder videoMediaFolder : videoMediaFolders) { imageMediaFolderPaths.add(videoMediaFolder.absolutePath); } @@ -92,38 +104,41 @@ protected Result onRunJob(@NonNull Params params) { // Store updated values arbitraryDataProvider.storeOrUpdateKeyValue(ACCOUNT_NAME_GLOBAL, KEY_MEDIA_FOLDERS, gson.toJson(new - MediaFoldersModel(imageMediaFolderPaths, videoMediaFolderPaths))); + MediaFoldersModel(imageMediaFolderPaths, videoMediaFolderPaths))); - imageMediaFolderPaths.removeAll(mediaFoldersModel.getImageMediaFolders()); - videoMediaFolderPaths.removeAll(mediaFoldersModel.getVideoMediaFolders()); - if (!imageMediaFolderPaths.isEmpty() || !videoMediaFolderPaths.isEmpty()) { - Account[] accounts = AccountUtils.getAccounts(getContext()); - List accountList = new ArrayList<>(); - for (Account account : accounts) { - if (!arbitraryDataProvider.getBooleanValue(account, ManageAccountsActivity.PENDING_FOR_REMOVAL)) { - accountList.add(account); + if (PreferenceManager.isShowMediaScanNotifications(context)) { + imageMediaFolderPaths.removeAll(mediaFoldersModel.getImageMediaFolders()); + videoMediaFolderPaths.removeAll(mediaFoldersModel.getVideoMediaFolders()); + + if (!imageMediaFolderPaths.isEmpty() || !videoMediaFolderPaths.isEmpty()) { + Account[] accounts = AccountUtils.getAccounts(getContext()); + List accountList = new ArrayList<>(); + for (Account account : accounts) { + if (!arbitraryDataProvider.getBooleanValue(account, ManageAccountsActivity.PENDING_FOR_REMOVAL)) { + accountList.add(account); + } } - } - for (Account account : accountList) { - for (String imageMediaFolder : imageMediaFolderPaths) { - if (syncedFolderProvider.findByLocalPathAndAccount(imageMediaFolder, account) == null) { - sendNotification(String.format(context.getString(R.string.new_media_folder_detected), + for (Account account : accountList) { + for (String imageMediaFolder : imageMediaFolderPaths) { + if (syncedFolderProvider.findByLocalPathAndAccount(imageMediaFolder, account) == null) { + sendNotification(String.format(context.getString(R.string.new_media_folder_detected), context.getString(R.string.new_media_folder_photos)), - imageMediaFolder.substring(imageMediaFolder.lastIndexOf('/') + 1 - ), - account, imageMediaFolder, 1); + imageMediaFolder.substring(imageMediaFolder.lastIndexOf('/') + 1 + ), + account, imageMediaFolder, 1); + } } - } - for (String videoMediaFolder : videoMediaFolderPaths) { - if (syncedFolderProvider.findByLocalPathAndAccount(videoMediaFolder, account) == null) { - sendNotification(String.format(context.getString(R.string.new_media_folder_detected), + for (String videoMediaFolder : videoMediaFolderPaths) { + if (syncedFolderProvider.findByLocalPathAndAccount(videoMediaFolder, account) == null) { + sendNotification(String.format(context.getString(R.string.new_media_folder_detected), context.getString(R.string.new_media_folder_videos)), - videoMediaFolder.substring(videoMediaFolder.lastIndexOf('/') + 1 - ), - account, videoMediaFolder, 2); + videoMediaFolder.substring(videoMediaFolder.lastIndexOf('/') + 1 + ), + account, videoMediaFolder, 2); + } } } } @@ -139,8 +154,11 @@ protected Result onRunJob(@NonNull Params params) { } private void sendNotification(String contentTitle, String subtitle, Account account, String path, int type) { + int notificationId = randomId.nextInt(); + Context context = getContext(); Intent intent = new Intent(getContext(), SyncedFoldersActivity.class); + intent.putExtra(NOTIFICATION_ID, notificationId); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); intent.putExtra(NotificationJob.KEY_NOTIFICATION_ACCOUNT, account.name); intent.putExtra(KEY_MEDIA_FOLDER_PATH, path); @@ -149,22 +167,76 @@ private void sendNotification(String contentTitle, String subtitle, Account acco PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_ONE_SHOT); NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder( - context, NotificationUtils.NOTIFICATION_CHANNEL_GENERAL) - .setSmallIcon(R.drawable.notification_icon) - .setLargeIcon(BitmapFactory.decodeResource(context.getResources(), R.drawable.notification_icon)) - .setColor(ThemeUtils.primaryColor(getContext())) - .setSubText(account.name) - .setContentTitle(contentTitle) - .setContentText(subtitle) - .setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)) - .setAutoCancel(true) - .setContentIntent(pendingIntent); + context, NotificationUtils.NOTIFICATION_CHANNEL_GENERAL) + .setSmallIcon(R.drawable.notification_icon) + .setLargeIcon(BitmapFactory.decodeResource(context.getResources(), R.drawable.notification_icon)) + .setColor(ThemeUtils.primaryColor(getContext())) + .setSubText(account.name) + .setContentTitle(contentTitle) + .setContentText(subtitle) + .setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)) + .setAutoCancel(true) + .setContentIntent(pendingIntent); + + Intent disableDetection = new Intent(context, NotificationReceiver.class); + disableDetection.putExtra(NOTIFICATION_ID, notificationId); + disableDetection.setAction(DISABLE_DETECTION_CLICK); + + PendingIntent disableIntent = PendingIntent.getBroadcast( + context, + notificationId, + disableDetection, + PendingIntent.FLAG_CANCEL_CURRENT + ); + notificationBuilder.addAction( + new NotificationCompat.Action( + R.drawable.ic_close, + context.getString(R.string.disable_new_media_folder_detection_notifications), + disableIntent + ) + ); + + PendingIntent configureIntent = PendingIntent.getActivity( + context, + notificationId, + intent, + PendingIntent.FLAG_CANCEL_CURRENT + ); + notificationBuilder.addAction( + new NotificationCompat.Action( + R.drawable.ic_settings, + context.getString(R.string.configure_new_media_folder_detection_notifications), + configureIntent + ) + ); NotificationManager notificationManager = (NotificationManager) - context.getSystemService(Context.NOTIFICATION_SERVICE); + context.getSystemService(Context.NOTIFICATION_SERVICE); if (notificationManager != null) { - notificationManager.notify(0, notificationBuilder.build()); + notificationManager.notify(notificationId, notificationBuilder.build()); + } + } + + + public static class NotificationReceiver extends BroadcastReceiver { + + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + int notificationId = intent.getIntExtra(NOTIFICATION_ID, 0); + + if (DISABLE_DETECTION_CLICK.equals(action)) { + Log_OC.d(this, "Disable media scan notifications"); + PreferenceManager.setShowMediaScanNotifications(context, false); + cancel(context, notificationId); + } + } + + private void cancel(Context context, int notificationId) { + NotificationManager notificationManager = + (NotificationManager) context.getSystemService(Activity.NOTIFICATION_SERVICE); + notificationManager.cancel(notificationId); } } } diff --git a/src/main/java/com/owncloud/android/ui/activity/Preferences.java b/src/main/java/com/owncloud/android/ui/activity/Preferences.java index b28a94c4201f..4cbec4bd2f4f 100644 --- a/src/main/java/com/owncloud/android/ui/activity/Preferences.java +++ b/src/main/java/com/owncloud/android/ui/activity/Preferences.java @@ -96,6 +96,7 @@ public class Preferences extends PreferenceActivity public static final String LOCK_DEVICE_CREDENTIALS = "device_credentials"; public final static String PREFERENCE_USE_FINGERPRINT = "use_fingerprint"; + public static final String PREFERENCE_SHOW_MEDIA_SCAN_NOTIFICATIONS = "show_media_scan_notifications"; private static final int ACTION_REQUEST_PASSCODE = 5; private static final int ACTION_CONFIRM_PASSCODE = 6; @@ -506,16 +507,30 @@ private void setupDetailsCategory(int accentColor, PreferenceScreen preferenceSc boolean fDeviceCredentialsEnabled = getResources().getBoolean(R.bool.device_credentials_enabled); boolean fShowHiddenFilesEnabled = getResources().getBoolean(R.bool.show_hidden_files_enabled); boolean fSyncedFolderLightEnabled = getResources().getBoolean(R.bool.syncedFolder_light); + boolean fShowMediaScanNotifications = com.owncloud.android.db.PreferenceManager + .isShowMediaScanNotifications(this); setupLockPreference(preferenceCategoryDetails, fPassCodeEnabled, fDeviceCredentialsEnabled); setupHiddenFilesPreference(preferenceCategoryDetails, fShowHiddenFilesEnabled); - if (!fPassCodeEnabled && !fDeviceCredentialsEnabled && !fShowHiddenFilesEnabled && fSyncedFolderLightEnabled) { + setupShowMediaScanNotifications(preferenceCategoryDetails, fShowMediaScanNotifications); + + if (!fPassCodeEnabled && !fDeviceCredentialsEnabled && !fShowHiddenFilesEnabled && fSyncedFolderLightEnabled + && fShowMediaScanNotifications) { preferenceScreen.removePreference(preferenceCategoryDetails); } } + private void setupShowMediaScanNotifications(PreferenceCategory preferenceCategoryDetails, + boolean fShowMediaScanNotifications) { + SwitchPreference mShowMediaScanNotifications = (SwitchPreference) findPreference(PREFERENCE_SHOW_MEDIA_SCAN_NOTIFICATIONS); + + if (fShowMediaScanNotifications) { + preferenceCategoryDetails.removePreference(mShowMediaScanNotifications); + } + } + private void setupHiddenFilesPreference(PreferenceCategory preferenceCategoryDetails, boolean fShowHiddenFilesEnabled) { mShowHiddenFiles = (SwitchPreference) findPreference("show_hidden_files"); diff --git a/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.java b/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.java index f6bc5ce4b72f..b612f933b1e3 100644 --- a/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.java @@ -23,6 +23,8 @@ import android.accounts.Account; import android.annotation.SuppressLint; +import android.app.Activity; +import android.app.NotificationManager; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; @@ -131,6 +133,12 @@ protected void onCreate(Bundle savedInstanceState) { path = getIntent().getStringExtra(MediaFoldersDetectionJob.KEY_MEDIA_FOLDER_PATH); type = getIntent().getIntExtra(MediaFoldersDetectionJob.KEY_MEDIA_FOLDER_TYPE, -1); + + // Cancel notification + int notificationId = getIntent().getIntExtra(MediaFoldersDetectionJob.NOTIFICATION_ID, 0); + NotificationManager notificationManager = + (NotificationManager) getSystemService(Activity.NOTIFICATION_SERVICE); + notificationManager.cancel(notificationId); } // setup toolbar diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index 2c9bf150e312..6817d882d23d 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -45,6 +45,8 @@ Device credentials enabled No device credentials have been set up. Show hidden files + Show media scan notifications + Notify about newly found media folders Delete history Sync calendar & contacts Set up DAVdroid (v1.3.0+) for current account @@ -788,6 +790,8 @@ General notifications Show notifications for new media folders and similar New %1$s media folder detected. + Configure + Disable photo video The server has reached end of life, please upgrade! diff --git a/src/main/res/xml/preferences.xml b/src/main/res/xml/preferences.xml index b88f7740b051..4354a36440c0 100644 --- a/src/main/res/xml/preferences.xml +++ b/src/main/res/xml/preferences.xml @@ -47,6 +47,10 @@ +