From 0603378c75008c247734d83baf74c076af1d6b0b Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Mon, 18 Sep 2017 17:56:25 +0200 Subject: [PATCH] show permanent notification while transcoding video --- .../AttachFileToConversationRunnable.java | 134 ++++++++++++++++++ .../services/NotificationService.java | 12 ++ .../services/XmppConnectionService.java | 123 +++------------- .../utils/SerialSingleThreadExecutor.java | 6 +- .../ic_hourglass_empty_white_24dp.png | Bin 0 -> 159 bytes .../ic_hourglass_empty_white_24dp.png | Bin 0 -> 135 bytes .../ic_hourglass_empty_white_24dp.png | Bin 0 -> 174 bytes .../ic_hourglass_empty_white_24dp.png | Bin 0 -> 255 bytes .../ic_hourglass_empty_white_24dp.png | Bin 0 -> 273 bytes src/main/res/values/strings.xml | 2 +- 10 files changed, 170 insertions(+), 107 deletions(-) create mode 100644 src/main/java/eu/siacs/conversations/services/AttachFileToConversationRunnable.java create mode 100644 src/main/res/drawable-hdpi/ic_hourglass_empty_white_24dp.png create mode 100644 src/main/res/drawable-mdpi/ic_hourglass_empty_white_24dp.png create mode 100644 src/main/res/drawable-xhdpi/ic_hourglass_empty_white_24dp.png create mode 100644 src/main/res/drawable-xxhdpi/ic_hourglass_empty_white_24dp.png create mode 100644 src/main/res/drawable-xxxhdpi/ic_hourglass_empty_white_24dp.png diff --git a/src/main/java/eu/siacs/conversations/services/AttachFileToConversationRunnable.java b/src/main/java/eu/siacs/conversations/services/AttachFileToConversationRunnable.java new file mode 100644 index 000000000..aae41e453 --- /dev/null +++ b/src/main/java/eu/siacs/conversations/services/AttachFileToConversationRunnable.java @@ -0,0 +1,134 @@ +package eu.siacs.conversations.services; + +import android.net.Uri; +import android.os.Build; +import android.os.ParcelFileDescriptor; +import android.util.Log; + +import net.ypresto.androidtranscoder.MediaTranscoder; +import net.ypresto.androidtranscoder.format.MediaFormatStrategy; +import net.ypresto.androidtranscoder.format.MediaFormatStrategyPresets; + +import java.io.FileDescriptor; +import java.io.FileNotFoundException; +import java.util.concurrent.atomic.AtomicInteger; + +import eu.siacs.conversations.Config; +import eu.siacs.conversations.R; +import eu.siacs.conversations.crypto.PgpEngine; +import eu.siacs.conversations.entities.DownloadableFile; +import eu.siacs.conversations.entities.Message; +import eu.siacs.conversations.persistance.FileBackend; +import eu.siacs.conversations.ui.UiCallback; +import eu.siacs.conversations.utils.MimeUtils; + +public class AttachFileToConversationRunnable implements Runnable, MediaTranscoder.Listener { + + private final XmppConnectionService mXmppConnectionService; + private final Message message; + private final Uri uri; + private final UiCallback callback; + private final boolean isVideoMessage; + private int currentProgress = -1; + + public AttachFileToConversationRunnable(XmppConnectionService xmppConnectionService, Uri uri, Message message, UiCallback callback) { + this.uri = uri; + this.mXmppConnectionService = xmppConnectionService; + this.message = message; + this.callback = callback; + final String mimeType = MimeUtils.guessMimeTypeFromUri(mXmppConnectionService, uri); + this.isVideoMessage = (mimeType != null && mimeType.startsWith("video/") && Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2); + } + + + private void processAsFile() { + final String path = mXmppConnectionService.getFileBackend().getOriginalPath(uri); + if (path != null) { + message.setRelativeFilePath(path); + mXmppConnectionService.getFileBackend().updateFileParams(message); + if (message.getEncryption() == Message.ENCRYPTION_DECRYPTED) { + mXmppConnectionService.getPgpEngine().encrypt(message, callback); + } else { + callback.success(message); + } + } else { + try { + mXmppConnectionService.getFileBackend().copyFileToPrivateStorage(message, uri); + mXmppConnectionService.getFileBackend().updateFileParams(message); + if (message.getEncryption() == Message.ENCRYPTION_DECRYPTED) { + final PgpEngine pgpEngine = mXmppConnectionService.getPgpEngine(); + if (pgpEngine != null) { + pgpEngine.encrypt(message, callback); + } else if (callback != null) { + callback.error(R.string.unable_to_connect_to_keychain, null); + } + } else { + callback.success(message); + } + } catch (FileBackend.FileCopyException e) { + callback.error(e.getResId(), message); + } + } + } + + private void processAsVideo() throws FileNotFoundException { + Log.d(Config.LOGTAG,"processing file as video"); + mXmppConnectionService.startForcingForegroundNotification(); + message.setRelativeFilePath(message.getUuid() + ".mp4"); + final DownloadableFile file = mXmppConnectionService.getFileBackend().getFile(message); + final int runtime = mXmppConnectionService.getFileBackend().getMediaRuntime(uri); + MediaFormatStrategy formatStrategy = runtime >= 8000 ? MediaFormatStrategyPresets.createExportPreset960x540Strategy() : MediaFormatStrategyPresets.createAndroid720pStrategy(); + Log.d(Config.LOGTAG,"runtime "+runtime); + file.getParentFile().mkdirs(); + ParcelFileDescriptor parcelFileDescriptor = mXmppConnectionService.getContentResolver().openFileDescriptor(uri, "r"); + FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor(); + MediaTranscoder.getInstance().transcodeVideo(fileDescriptor, file.getAbsolutePath(), formatStrategy, this); + } + + @Override + public void onTranscodeProgress(double progress) { + final int p = (int) Math.round(progress * 100); + if (p > currentProgress) { + currentProgress = p; + mXmppConnectionService.getNotificationService().updateFileAddingNotification(p,message); + } + } + + @Override + public void onTranscodeCompleted() { + mXmppConnectionService.stopForcingForegroundNotification(); + mXmppConnectionService.getFileBackend().updateFileParams(message); + if (message.getEncryption() == Message.ENCRYPTION_DECRYPTED) { + mXmppConnectionService.getPgpEngine().encrypt(message, callback); + } else { + callback.success(message); + } + } + + @Override + public void onTranscodeCanceled() { + mXmppConnectionService.stopForcingForegroundNotification(); + processAsFile(); + } + + @Override + public void onTranscodeFailed(Exception e) { + mXmppConnectionService.stopForcingForegroundNotification(); + Log.d(Config.LOGTAG,"video transcoding failed "+e.getMessage()); + processAsFile(); + } + + @Override + public void run() { + if (isVideoMessage) { + try { + processAsVideo(); + } catch (Throwable e) { + processAsFile(); + } + } else { + processAsFile(); + } + } + +} diff --git a/src/main/java/eu/siacs/conversations/services/NotificationService.java b/src/main/java/eu/siacs/conversations/services/NotificationService.java index 2a234d209..eaa07d07d 100644 --- a/src/main/java/eu/siacs/conversations/services/NotificationService.java +++ b/src/main/java/eu/siacs/conversations/services/NotificationService.java @@ -739,4 +739,16 @@ public class NotificationService { PendingIntent.FLAG_UPDATE_CURRENT)); notificationManager.notify(ERROR_NOTIFICATION_ID, mBuilder.build()); } + + public Notification updateFileAddingNotification(int current, Message message) { + final NotificationManagerCompat notificationManager = NotificationManagerCompat.from(mXmppConnectionService); + NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(mXmppConnectionService); + mBuilder.setContentTitle(mXmppConnectionService.getString(R.string.transcoding_video)); + mBuilder.setProgress(100, current, false); + mBuilder.setSmallIcon(R.drawable.ic_hourglass_empty_white_24dp); + mBuilder.setContentIntent(createContentIntent(message.getConversation())); + Notification notification = mBuilder.build(); + notificationManager.notify(FOREGROUND_NOTIFICATION_ID, notification); + return notification; + } } diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java index b02e6659d..8e722ec27 100644 --- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java @@ -189,6 +189,7 @@ public class XmppConnectionService extends Service { private NotificationService mNotificationService = new NotificationService(this); private ShortcutService mShortcutService = new ShortcutService(this); private AtomicBoolean mInitialAddressbookSyncCompleted = new AtomicBoolean(false); + private AtomicBoolean mForceForegroundService = new AtomicBoolean(false); private OnMessagePacketReceived mMessageParser = new MessageParser(this); private OnPresencePacketReceived mPresenceParser = new PresenceParser(this); private IqParser mIqParser = new IqParser(this); @@ -398,6 +399,16 @@ public class XmppConnectionService extends Service { } } + public void startForcingForegroundNotification() { + mForceForegroundService.set(true); + toggleForegroundService(); + } + + public void stopForcingForegroundNotification() { + mForceForegroundService.set(false); + toggleForegroundService(); + } + private OpenPgpServiceConnection pgpServiceConnection; private PgpEngine mPgpEngine = null; private WakeLock wakeLock; @@ -483,106 +494,8 @@ public class XmppConnectionService extends Service { } message.setCounterpart(conversation.getNextCounterpart()); message.setType(Message.TYPE_FILE); - mFileAddingExecutor.execute(new Runnable() { - - private void processAsFile() { - final String path = getFileBackend().getOriginalPath(uri); - if (path != null) { - message.setRelativeFilePath(path); - getFileBackend().updateFileParams(message); - if (message.getEncryption() == Message.ENCRYPTION_DECRYPTED) { - getPgpEngine().encrypt(message, callback); - } else { - callback.success(message); - } - } else { - try { - getFileBackend().copyFileToPrivateStorage(message, uri); - getFileBackend().updateFileParams(message); - if (message.getEncryption() == Message.ENCRYPTION_DECRYPTED) { - final PgpEngine pgpEngine = getPgpEngine(); - if (pgpEngine != null) { - pgpEngine.encrypt(message, callback); - } else if (callback != null) { - callback.error(R.string.unable_to_connect_to_keychain, null); - } - } else { - callback.success(message); - } - } catch (FileBackend.FileCopyException e) { - callback.error(e.getResId(), message); - } - } - } - - private void processAsVideo() throws FileNotFoundException { - Log.d(Config.LOGTAG,"processing file as video"); - message.setRelativeFilePath(message.getUuid() + ".mp4"); - final DownloadableFile file = getFileBackend().getFile(message); - final int runtime = getFileBackend().getMediaRuntime(uri); - MediaFormatStrategy formatStrategy = runtime >= 8000 ? MediaFormatStrategyPresets.createExportPreset960x540Strategy() : MediaFormatStrategyPresets.createAndroid720pStrategy(); - Log.d(Config.LOGTAG,"runtime "+runtime); - file.getParentFile().mkdirs(); - ParcelFileDescriptor parcelFileDescriptor = getContentResolver().openFileDescriptor(uri, "r"); - FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor(); - final ArrayList progressTracker = new ArrayList<>(); - final UiInformableCallback informableCallback; - if (callback instanceof UiInformableCallback) { - informableCallback = (UiInformableCallback) callback; - } else { - informableCallback = null; - } - MediaTranscoder.Listener listener = new MediaTranscoder.Listener() { - @Override - public void onTranscodeProgress(double progress) { - int p = ((int) Math.round(progress * 100) / 20) * 20; - if (!progressTracker.contains(p) && p != 100 && p != 0) { - progressTracker.add(p); - if (informableCallback != null) { - informableCallback.inform(getString(R.string.transcoding_video_progress, String.valueOf(p))); - } - } - } - - @Override - public void onTranscodeCompleted() { - getFileBackend().updateFileParams(message); - if (message.getEncryption() == Message.ENCRYPTION_DECRYPTED) { - getPgpEngine().encrypt(message, callback); - } else { - callback.success(message); - } - } - - @Override - public void onTranscodeCanceled() { - processAsFile(); - } - - @Override - public void onTranscodeFailed(Exception e) { - Log.d(Config.LOGTAG,"video transcoding failed "+e.getMessage()); - processAsFile(); - } - }; - MediaTranscoder.getInstance().transcodeVideo(fileDescriptor, file.getAbsolutePath(), formatStrategy, listener); - } - - @Override - public void run() { - final String mimeType = MimeUtils.guessMimeTypeFromUri(XmppConnectionService.this, uri); - if (mimeType != null && mimeType.startsWith("video/") && Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { - try { - processAsVideo(); - } catch (Throwable e) { - processAsFile(); - } - } else { - processAsFile(); - } - - } - }); + AttachFileToConversationRunnable runnable = new AttachFileToConversationRunnable(this,uri,message,callback); + mFileAddingExecutor.execute(runnable); } public void attachImageToConversation(final Conversation conversation, final Uri uri, final UiCallback callback) { @@ -1095,10 +1008,12 @@ public class XmppConnectionService extends Service { } public void toggleForegroundService() { - if (keepForegroundService() && hasEnabledAccounts()) { + if (mForceForegroundService.get() || (keepForegroundService() && hasEnabledAccounts())) { startForeground(NotificationService.FOREGROUND_NOTIFICATION_ID, this.mNotificationService.createForegroundNotification()); + Log.d(Config.LOGTAG,"started foreground service"); } else { stopForeground(true); + Log.d(Config.LOGTAG,"stopped foreground service"); } } @@ -1109,10 +1024,10 @@ public class XmppConnectionService extends Service { @Override public void onTaskRemoved(final Intent rootIntent) { super.onTaskRemoved(rootIntent); - if (!keepForegroundService()) { - this.logoutAndSave(false); - } else { + if (keepForegroundService() || mForceForegroundService.get()) { Log.d(Config.LOGTAG,"ignoring onTaskRemoved because foreground service is activated"); + } else { + this.logoutAndSave(false); } } diff --git a/src/main/java/eu/siacs/conversations/utils/SerialSingleThreadExecutor.java b/src/main/java/eu/siacs/conversations/utils/SerialSingleThreadExecutor.java index 55489ee30..79859af25 100644 --- a/src/main/java/eu/siacs/conversations/utils/SerialSingleThreadExecutor.java +++ b/src/main/java/eu/siacs/conversations/utils/SerialSingleThreadExecutor.java @@ -7,11 +7,13 @@ import java.util.Queue; import java.util.concurrent.Executor; import java.util.concurrent.Executors; +import eu.siacs.conversations.services.AttachFileToConversationRunnable; + public class SerialSingleThreadExecutor implements Executor { final Executor executor = Executors.newSingleThreadExecutor(); - protected final Queue tasks = new ArrayDeque(); - Runnable active; + protected final ArrayDeque tasks = new ArrayDeque<>(); + private Runnable active; public SerialSingleThreadExecutor() { this(false); diff --git a/src/main/res/drawable-hdpi/ic_hourglass_empty_white_24dp.png b/src/main/res/drawable-hdpi/ic_hourglass_empty_white_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..5b8b6841da0770afe9a3c8b692d7feca6f08901f GIT binary patch literal 159 zcmeAS@N?(olHy`uVBq!ia0vp^Dj>|k0wldT1B8K8nx~6nh{y4_SFO1i6nI=5`==*Y z=Il<_U3OsyN8L7Fo)w-xFP9ojSiCUetFt0Uakd$ADub7Qq6ovMjQrhKOdba-E?a)c z$6MEsck8he3KuWBOxLZd3v@o^=3>63-Z?LRt9jkc?Ms**p0(hgd~H($&_)JNS3j3^ HP6ASbai3e-^RA!4Redwf=}!Y6WQiS1#A^qC=7&QA9S{3q-hKhL+3 Y72P#S@Ko$epaU2@UHx3vIVCg!0C2cKX#fBK literal 0 HcmV?d00001 diff --git a/src/main/res/drawable-xxhdpi/ic_hourglass_empty_white_24dp.png b/src/main/res/drawable-xxhdpi/ic_hourglass_empty_white_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..8f67bc62b6e96d50071c12d9ada9cdd5628ac6f7 GIT binary patch literal 255 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY0wn)GsXhawj(NH`hGg7(d)=F_L4n8h;&zc~ zT)P6xgPtozU8rMwf6m*FNm28Fg!hC$B91JAOBgH93AqRWSzC%$Oj&s{iQnprie}M` zhHs*mrYc(tc)YTZ{+HXAa*mPV=9VRWmlG!UJvnx9VvHq&!rRU(g<5Z8^Sw;?q`t>)mx^ONie-u~ w*&LrSB|q@Y9m5J1 z#uVgiyP9tG`QzNRae3?po8@9ytD6H0{v0UY$93)}8^itT)&hZ*M+E*X&=csHAkg!m zNl>_9V$#75&I2AR6IxuDl@gehRHOtHFE(&=@>v&@DmX1-6cl276fWt%eRll M)78&qol`;+0E~idHvj+t literal 0 HcmV?d00001 diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index 0be4878c0..54af30ff9 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -700,7 +700,7 @@ Automatically delete messages from this device that are older than the configured time frame. Encrypting message Not fetching messages due to local retention period. - Compressing video (%s%% completed) + Compressing video Corresponding conversations closed. Contact blocked. Notifications from strangers