feat(android,interface): Implement video thumbnail generation

This commit is contained in:
2023-08-24 20:06:31 +02:00
parent fb71ac330a
commit fe6d0a60c1
12 changed files with 183 additions and 2 deletions

View File

@@ -10,7 +10,7 @@
<application>
<provider
android:name="me.polynom.moxplatform_android.MoxplatformFileProvider"
android:authorities="me.polynom.moxplatform_android.fileprovider"
android:authorities="me.polynom.moxplatform_android.fileprovider2"
android:exported="false"
android:grantUriPermissions="true">
<meta-data

View File

@@ -1097,6 +1097,9 @@ public class Api {
void decryptFile(@NonNull String sourcePath, @NonNull String destPath, @NonNull byte[] key, @NonNull byte[] iv, @NonNull CipherAlgorithm algorithm, @NonNull String hashSpec, @NonNull Result<CryptographyResult> result);
void hashFile(@NonNull String sourcePath, @NonNull String hashSpec, @NonNull Result<byte[]> result);
/** Media APIs */
@NonNull
Boolean generateVideoThumbnail(@NonNull String src, @NonNull String dest, @NonNull Long maxWidth);
/** Stubs */
void eventStub(@NonNull NotificationEvent event);
@@ -1466,6 +1469,32 @@ public class Api {
channel.setMessageHandler(null);
}
}
{
BasicMessageChannel<Object> channel =
new BasicMessageChannel<>(
binaryMessenger, "dev.flutter.pigeon.moxplatform_platform_interface.MoxplatformApi.generateVideoThumbnail", getCodec());
if (api != null) {
channel.setMessageHandler(
(message, reply) -> {
ArrayList<Object> wrapped = new ArrayList<Object>();
ArrayList<Object> args = (ArrayList<Object>) message;
String srcArg = (String) args.get(0);
String destArg = (String) args.get(1);
Number maxWidthArg = (Number) args.get(2);
try {
Boolean output = api.generateVideoThumbnail(srcArg, destArg, (maxWidthArg == null) ? null : maxWidthArg.longValue());
wrapped.add(0, output);
}
catch (Throwable exception) {
ArrayList<Object> wrappedError = wrapError(exception);
wrapped = wrappedError;
}
reply.reply(wrapped);
});
} else {
channel.setMessageHandler(null);
}
}
{
BasicMessageChannel<Object> channel =
new BasicMessageChannel<>(

View File

@@ -25,7 +25,7 @@ const val NOTIFICATION_EXTRA_ID_KEY = "notification_id"
const val NOTIFICATION_MESSAGE_EXTRA_MIME = "mime"
const val NOTIFICATION_MESSAGE_EXTRA_PATH = "path"
const val MOXPLATFORM_FILEPROVIDER_ID = "me.polynom.moxplatform_android.fileprovider"
const val MOXPLATFORM_FILEPROVIDER_ID = "me.polynom.moxplatform_android.fileprovider2"
// Shared preferences keys
const val SHARED_PREFERENCES_KEY = "me.polynom.moxplatform_android"

View File

@@ -3,9 +3,11 @@ package me.polynom.moxplatform_android;
import static android.provider.Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS;
import static androidx.core.content.ContextCompat.getSystemService;
import static androidx.core.content.ContextCompat.startActivity;
import static me.polynom.moxplatform_android.ConstantsKt.MOXPLATFORM_FILEPROVIDER_ID;
import static me.polynom.moxplatform_android.ConstantsKt.SHARED_PREFERENCES_KEY;
import static me.polynom.moxplatform_android.CryptoKt.*;
import static me.polynom.moxplatform_android.RecordSentMessageKt.*;
import static me.polynom.moxplatform_android.ThumbnailsKt.generateVideoThumbnailImplementation;
import me.polynom.moxplatform_android.Api.*;
@@ -13,20 +15,31 @@ import android.app.ActivityManager;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.media.MediaMetadataRetriever;
import android.media.ThumbnailUtils;
import android.net.Uri;
import android.os.Build;
import android.os.PowerManager;
import android.provider.MediaStore;
import android.util.Log;
import android.util.Size;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.app.NotificationManagerCompat;
import androidx.core.content.ContextCompat;
import androidx.core.content.FileProvider;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.List;
@@ -313,6 +326,12 @@ public class MoxplatformAndroidPlugin extends BroadcastReceiver implements Flutt
CryptoKt.hashFile(sourcePath, hashSpec, result);
}
@NonNull
@Override
public Boolean generateVideoThumbnail(@NonNull String src, @NonNull String dest, @NonNull Long maxWidth) {
return generateVideoThumbnailImplementation(src, dest, maxWidth);
}
@Override
public void eventStub(@NonNull NotificationEvent event) {
// Stub to trick pigeon into

View File

@@ -0,0 +1,43 @@
package me.polynom.moxplatform_android
import android.graphics.Bitmap
import android.media.MediaMetadataRetriever
import android.util.Log
import java.io.FileOutputStream
/*
* Generate a video thumbnail using the first frame of the video at @src. Afterwards, scale it
* down such that its width is equal to @maxWidth (while keeping the aspect ratio) and write it to
* @dest.
*
* If everything went well, returns true. If we're unable to generate the thumbnail, returns false.
* */
fun generateVideoThumbnailImplementation(src: String, dest: String, maxWidth: Long): Boolean {
try {
val mmr = MediaMetadataRetriever().apply {
setDataSource(src)
}
val unscaledThumbnail = mmr.getFrameAtTime(0) ?: return false
// Scale down the thumbnail while keeping the aspect ratio
val scalingFactor = maxWidth.toDouble() / unscaledThumbnail.width;
Log.d(TAG, "Scaling to $maxWidth from ${unscaledThumbnail.width} with scalingFactor $scalingFactor");
val thumbnail = Bitmap.createScaledBitmap(
unscaledThumbnail,
(unscaledThumbnail.width * scalingFactor).toInt(),
(unscaledThumbnail.height * scalingFactor).toInt(),
false,
)
// Write it to the destination file
val fos = FileOutputStream(dest)
thumbnail.compress(Bitmap.CompressFormat.JPEG, 75, fos)
fos.flush()
fos.close()
return true;
} catch (ex: Exception) {
Log.e(TAG, "Failed to create thumbnail for $src: ${ex.message}")
ex.printStackTrace()
return false;
}
}

View File

@@ -1,3 +1,4 @@
import 'package:moxplatform/moxplatform.dart';
import 'package:moxplatform_platform_interface/moxplatform_platform_interface.dart';
class AndroidPlatformImplementation extends PlatformImplementation {
@@ -20,4 +21,10 @@ class AndroidPlatformImplementation extends PlatformImplementation {
Future<void> openBatteryOptimisationSettings() {
return MoxplatformInterface.api.openBatteryOptimisationSettings();
}
@override
Future<bool> generateVideoThumbnail(
String src, String dest, int width) async {
return MoxplatformInterface.api.generateVideoThumbnail(src, dest, width);
}
}