nextcloud-android/src/main/java/com/owncloud/android/jobs/MediaFoldersDetectionJob.java

270 lines
12 KiB
Java

/*
* Nextcloud Android client application
*
* @author Mario Danic
* @author Andy Scherzinger
* @author Chris Narkiewicz
* Copyright (C) 2018 Mario Danic
* Copyright (C) 2018 Andy Scherzinger
* Copyright (C) 2019 Chris Narkiewicz <hello@ezaquarii.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
* License as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.owncloud.android.jobs;
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;
import android.graphics.BitmapFactory;
import android.media.RingtoneManager;
import android.text.TextUtils;
import com.evernote.android.job.Job;
import com.google.gson.Gson;
import com.nextcloud.client.account.UserAccountManager;
import com.nextcloud.client.core.Clock;
import com.nextcloud.client.preferences.AppPreferences;
import com.nextcloud.client.preferences.AppPreferencesImpl;
import com.owncloud.android.R;
import com.owncloud.android.datamodel.ArbitraryDataProvider;
import com.owncloud.android.datamodel.MediaFolder;
import com.owncloud.android.datamodel.MediaFoldersModel;
import com.owncloud.android.datamodel.MediaProvider;
import com.owncloud.android.datamodel.SyncedFolderProvider;
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;
import com.owncloud.android.utils.ThemeUtils;
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";
public static final String KEY_MEDIA_FOLDER_PATH = "KEY_MEDIA_FOLDER_PATH";
public static final String KEY_MEDIA_FOLDER_TYPE = "KEY_MEDIA_FOLDER_TYPE";
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 final UserAccountManager userAccountManager;
private final Clock clock;
private final Random randomId = new Random();
MediaFoldersDetectionJob(UserAccountManager accountManager, Clock clock) {
this.userAccountManager = accountManager;
this.clock = clock;
}
@NonNull
@Override
protected Result onRunJob(@NonNull Params params) {
Context context = getContext();
ContentResolver contentResolver = context.getContentResolver();
ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProvider(contentResolver);
SyncedFolderProvider syncedFolderProvider = new SyncedFolderProvider(contentResolver,
AppPreferencesImpl.fromContext(context),
clock);
Gson gson = new Gson();
String arbitraryDataString;
MediaFoldersModel mediaFoldersModel;
List<MediaFolder> imageMediaFolders = MediaProvider.getImageFolders(contentResolver, 1, null, true);
List<MediaFolder> videoMediaFolders = MediaProvider.getVideoFolders(contentResolver, 1, null, true);
List<String> imageMediaFolderPaths = new ArrayList<>();
List<String> videoMediaFolderPaths = new ArrayList<>();
for (MediaFolder imageMediaFolder : imageMediaFolders) {
imageMediaFolderPaths.add(imageMediaFolder.absolutePath);
}
for (MediaFolder videoMediaFolder : videoMediaFolders) {
imageMediaFolderPaths.add(videoMediaFolder.absolutePath);
}
arbitraryDataString = arbitraryDataProvider.getValue(ACCOUNT_NAME_GLOBAL, KEY_MEDIA_FOLDERS);
if (!TextUtils.isEmpty(arbitraryDataString)) {
mediaFoldersModel = gson.fromJson(arbitraryDataString, MediaFoldersModel.class);
// merge new detected paths with already notified ones
for (String existingImageFolderPath : mediaFoldersModel.getImageMediaFolders()) {
if (!imageMediaFolderPaths.contains(existingImageFolderPath)) {
imageMediaFolderPaths.add(existingImageFolderPath);
}
}
for (String existingVideoFolderPath : mediaFoldersModel.getVideoMediaFolders()) {
if (!videoMediaFolderPaths.contains(existingVideoFolderPath)) {
videoMediaFolderPaths.add(existingVideoFolderPath);
}
}
// Store updated values
arbitraryDataProvider.storeOrUpdateKeyValue(ACCOUNT_NAME_GLOBAL, KEY_MEDIA_FOLDERS, gson.toJson(new
MediaFoldersModel(imageMediaFolderPaths, videoMediaFolderPaths)));
final AppPreferences preferences = AppPreferencesImpl.fromContext(getContext());
if (preferences.isShowMediaScanNotifications()) {
imageMediaFolderPaths.removeAll(mediaFoldersModel.getImageMediaFolders());
videoMediaFolderPaths.removeAll(mediaFoldersModel.getVideoMediaFolders());
if (!imageMediaFolderPaths.isEmpty() || !videoMediaFolderPaths.isEmpty()) {
Account[] accounts = userAccountManager.getAccounts();
List<Account> 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),
context.getString(R.string.new_media_folder_photos)),
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),
context.getString(R.string.new_media_folder_videos)),
videoMediaFolder.substring(videoMediaFolder.lastIndexOf('/') + 1
),
account, videoMediaFolder, 2);
}
}
}
}
}
} else {
mediaFoldersModel = new MediaFoldersModel(imageMediaFolderPaths, videoMediaFolderPaths);
arbitraryDataProvider.storeOrUpdateKeyValue(ACCOUNT_NAME_GLOBAL, KEY_MEDIA_FOLDERS,
gson.toJson(mediaFoldersModel));
}
return Result.SUCCESS;
}
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);
intent.putExtra(KEY_MEDIA_FOLDER_TYPE, type);
intent.putExtra(SyncedFoldersActivity.EXTRA_SHOW_SIDEBAR, true);
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);
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);
if (notificationManager != null) {
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);
final AppPreferences preferences = AppPreferencesImpl.fromContext(context);
if (DISABLE_DETECTION_CLICK.equals(action)) {
Log_OC.d(this, "Disable media scan notifications");
preferences.setShowMediaScanNotifications(false);
cancel(context, notificationId);
}
}
private void cancel(Context context, int notificationId) {
NotificationManager notificationManager =
(NotificationManager) context.getSystemService(Activity.NOTIFICATION_SERVICE);
notificationManager.cancel(notificationId);
}
}
}