Add compression

This commit is contained in:
Isaac Shoebottom 2022-12-06 20:28:56 -04:00
parent 9be11ad95b
commit 494981c7ab
4 changed files with 49 additions and 462 deletions

View File

@ -1,11 +0,0 @@
package com.example.myapplication
import java.io.FileDescriptor
class Compressor {
fun compressFile(fileDescriptor: FileDescriptor) {
}
}

View File

@ -1,11 +1,13 @@
package com.example.myapplication package com.example.myapplication
import android.Manifest import android.Manifest
import android.content.ContentResolver import android.annotation.SuppressLint
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.provider.MediaStore import android.provider.MediaStore
import android.util.Log import android.util.Log
import android.view.Menu import android.view.Menu
@ -14,20 +16,17 @@ import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat import androidx.core.app.ActivityCompat
import androidx.core.net.toUri
import androidx.navigation.findNavController import androidx.navigation.findNavController
import androidx.navigation.ui.AppBarConfiguration import androidx.navigation.ui.AppBarConfiguration
import androidx.navigation.ui.setupActionBarWithNavController import androidx.navigation.ui.setupActionBarWithNavController
import androidx.navigation.ui.setupWithNavController import androidx.navigation.ui.setupWithNavController
import com.arthenica.ffmpegkit.FFmpegKit import com.arthenica.ffmpegkit.FFmpegKit
import com.arthenica.ffmpegkit.FFmpegKitConfig import com.arthenica.ffmpegkit.FFmpegKitConfig
import com.arthenica.ffmpegkit.FFmpegSession
import com.example.myapplication.databinding.ActivityMainBinding import com.example.myapplication.databinding.ActivityMainBinding
import com.example.myapplication.ui.compressing.CompressingAdapter import com.example.myapplication.ui.compressing.CompressingAdapter
import com.example.myapplication.ui.compressing.CompressingItem import com.example.myapplication.ui.compressing.CompressingItem
import com.google.android.material.bottomnavigation.BottomNavigationView import com.google.android.material.bottomnavigation.BottomNavigationView
import java.io.File import java.io.File
import java.io.FileInputStream
import java.util.* import java.util.*
class MainActivity : AppCompatActivity() { class MainActivity : AppCompatActivity() {
@ -101,6 +100,7 @@ class MainActivity : AppCompatActivity() {
} }
//grabs output from pressing files, used for grabbing URI //grabs output from pressing files, used for grabbing URI
@SuppressLint("NotifyDataSetChanged") // Needed because of custom adapter
private var resultLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> private var resultLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode == RESULT_OK) { if (result.resultCode == RESULT_OK) {
@ -109,27 +109,6 @@ class MainActivity : AppCompatActivity() {
val inUri = FFmpegKitConfig.getSafParameterForRead(this, data) val inUri = FFmpegKitConfig.getSafParameterForRead(this, data)
val outputFile = File(this.filesDir, "output.mp4")
if(outputFile.createNewFile()) {
Log.i("Tag", "File created")
} else {
Log.i("Tag", "File not created")
}
val outUri = FFmpegKitConfig.getSafParameter(this, outputFile.toUri(), "rw")
val command = "-i $inUri -c:v mpeg4 $outUri -y"
val session = FFmpegKit.execute(command)
Log.i("Tag", Arrays.deepToString(session.arguments))
Log.i("Tag", session.output)
val cursor = contentResolver.query(data!!, null, null, null, null) val cursor = contentResolver.query(data!!, null, null, null, null)
cursor?.moveToFirst() cursor?.moveToFirst()
@ -137,7 +116,33 @@ class MainActivity : AppCompatActivity() {
val fileDate = Date(System.currentTimeMillis()) val fileDate = Date(System.currentTimeMillis())
val fileName = cursor?.getString(cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DISPLAY_NAME)) val fileName = cursor?.getString(cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DISPLAY_NAME))
compressingItems.add(CompressingItem(fileName!!, 0.0, fileDate)) val item = CompressingItem(fileName!!, 0.0, fileDate)
val outputFile = File(this.getExternalFilesDir(null), "converted_$fileName")
compressingItems.add(item)
val handler = Handler(Looper.getMainLooper())
val command = "-i $inUri -c:v mpeg4 ${outputFile.absolutePath} -y"
val session = FFmpegKit.executeAsync(command) {
compressingItems.remove(item)
handler.post {
Toast.makeText(this, "Finished converting $fileName", Toast.LENGTH_SHORT).show()
adapter.notifyDataSetChanged()
}
}
Log.i("Tag", Arrays.deepToString(session.arguments))
Log.i("Tag", session.output)
adapter.notifyDataSetChanged() adapter.notifyDataSetChanged()
} }
@ -145,6 +150,7 @@ class MainActivity : AppCompatActivity() {
companion object { companion object {
val compressingItems: MutableList<CompressingItem> = mutableListOf() val compressingItems: MutableList<CompressingItem> = mutableListOf()
val adapter = CompressingAdapter(compressingItems) val adapter = CompressingAdapter(compressingItems)
} }
} }

View File

@ -1,413 +0,0 @@
package com.example.myapplication.utils;
import android.annotation.SuppressLint;
import android.content.ContentUris;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.provider.DocumentsContract;
import android.provider.MediaStore;
import android.provider.OpenableColumns;
import android.text.TextUtils;
import android.util.Log;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.UUID;
public class FileUtils {
public static String FALLBACK_COPY_FOLDER = "upload_part";
private static String TAG = "FileUtils";
private static Uri contentUri = null;
Context context;
public FileUtils(Context context) {
this.context = context;
}
@SuppressLint("NewApi")
public String getPath(final Uri uri) {
// check here to KITKAT or new version
final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
String selection = null;
String[] selectionArgs = null;
// DocumentProvider
if (isKitKat) {
// ExternalStorageProvider
if (isExternalStorageDocument(uri)) {
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];
String fullPath = getPathFromExtSD(split);
if (fullPath == null || !fileExists(fullPath)) {
Log.d(TAG, "Copy files as a fallback");
fullPath = copyFileToInternalStorage(uri, FALLBACK_COPY_FOLDER);
}
if (fullPath != "") {
return fullPath;
} else {
return null;
}
}
// DownloadsProvider
if (isDownloadsDocument(uri)) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
final String id;
Cursor cursor = null;
try {
cursor = context.getContentResolver().query(uri, new String[] {
MediaStore.MediaColumns.DISPLAY_NAME
}, null, null, null);
if (cursor != null && cursor.moveToFirst()) {
String fileName = cursor.getString(0);
String path = Environment.getExternalStorageDirectory().toString() + "/Download/" + fileName;
if (!TextUtils.isEmpty(path)) {
return path;
}
}
} finally {
if (cursor != null)
cursor.close();
}
id = DocumentsContract.getDocumentId(uri);
if (!TextUtils.isEmpty(id)) {
if (id.startsWith("raw:")) {
return id.replaceFirst("raw:", "");
}
String[] contentUriPrefixesToTry = new String[] {
"content://downloads/public_downloads",
"content://downloads/my_downloads"
};
for (String contentUriPrefix: contentUriPrefixesToTry) {
try {
final Uri contentUri = ContentUris.withAppendedId(Uri.parse(contentUriPrefix), Long.valueOf(id));
return getDataColumn(context, contentUri, null, null);
} catch (NumberFormatException e) {
//In Android 8 and Android P the id is not a number
return uri.getPath().replaceFirst("^/document/raw:", "").replaceFirst("^raw:", "");
}
}
}
} else {
final String id = DocumentsContract.getDocumentId(uri);
if (id.startsWith("raw:")) {
return id.replaceFirst("raw:", "");
}
try {
contentUri = ContentUris.withAppendedId(
Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
} catch (NumberFormatException e) {
e.printStackTrace();
}
if (contentUri != null)
return getDataColumn(context, contentUri, null, null);
}
}
// MediaProvider
if (isMediaDocument(uri)) {
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];
Log.d(TAG, "MEDIA DOCUMENT TYPE: " + type);
Uri contentUri = null;
if ("image".equals(type)) {
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
} else if ("video".equals(type)) {
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
} else if ("audio".equals(type)) {
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
} else if ("document".equals(type)) {
contentUri = MediaStore.Files.getContentUri(MediaStore.getVolumeName(uri));
}
selection = "_id=?";
selectionArgs = new String[] {
split[1]
};
return getDataColumn(context, contentUri, selection, selectionArgs);
}
if (isGoogleDriveUri(uri)) {
return getDriveFilePath(uri);
}
if (isWhatsAppFile(uri)) {
return getFilePathForWhatsApp(uri);
}
if ("content".equalsIgnoreCase(uri.getScheme())) {
if (isGooglePhotosUri(uri)) {
return uri.getLastPathSegment();
}
if (isGoogleDriveUri(uri)) {
return getDriveFilePath(uri);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
// return getFilePathFromURI(context,uri);
return copyFileToInternalStorage(uri, FALLBACK_COPY_FOLDER);
// return getRealPathFromURI(context,uri);
} else {
return getDataColumn(context, uri, null, null);
}
}
if ("file".equalsIgnoreCase(uri.getScheme())) {
return uri.getPath();
}
} else {
if (isWhatsAppFile(uri)) {
return getFilePathForWhatsApp(uri);
}
if ("content".equalsIgnoreCase(uri.getScheme())) {
String[] projection = {
MediaStore.Images.Media.DATA
};
Cursor cursor = null;
try {
cursor = context.getContentResolver()
.query(uri, projection, selection, selectionArgs, null);
int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
if (cursor.moveToFirst()) {
return cursor.getString(column_index);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
return copyFileToInternalStorage(uri, FALLBACK_COPY_FOLDER);
}
private static boolean fileExists(String filePath) {
File file = new File(filePath);
return file.exists();
}
private static String getPathFromExtSD(String[] pathData) {
final String type = pathData[0];
final String relativePath = File.separator + pathData[1];
String fullPath = "";
Log.d(TAG, "MEDIA EXTSD TYPE: " + type);
Log.d(TAG, "Relative path: " + relativePath);
// on my Sony devices (4.4.4 & 5.1.1), `type` is a dynamic string
// something like "71F8-2C0A", some kind of unique id per storage
// don't know any API that can get the root path of that storage based on its id.
//
// so no "primary" type, but let the check here for other devices
if ("primary".equalsIgnoreCase(type)) {
fullPath = Environment.getExternalStorageDirectory() + relativePath;
if (fileExists(fullPath)) {
return fullPath;
}
}
if ("home".equalsIgnoreCase(type)) {
fullPath = "/storage/emulated/0/Documents" + relativePath;
if (fileExists(fullPath)) {
return fullPath;
}
}
// Environment.isExternalStorageRemovable() is `true` for external and internal storage
// so we cannot relay on it.
//
// instead, for each possible path, check if file exists
// we'll start with secondary storage as this could be our (physically) removable sd card
fullPath = System.getenv("SECONDARY_STORAGE") + relativePath;
if (fileExists(fullPath)) {
return fullPath;
}
fullPath = System.getenv("EXTERNAL_STORAGE") + relativePath;
if (fileExists(fullPath)) {
return fullPath;
}
return null;
}
private String getDriveFilePath(Uri uri) {
Uri returnUri = uri;
Cursor returnCursor = context.getContentResolver().query(returnUri, null, null, null, null);
/*
* Get the column indexes of the data in the Cursor,
* * move to the first row in the Cursor, get the data,
* * and display it.
* */
int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
int sizeIndex = returnCursor.getColumnIndex(OpenableColumns.SIZE);
returnCursor.moveToFirst();
String name = (returnCursor.getString(nameIndex));
String size = (Long.toString(returnCursor.getLong(sizeIndex)));
File file = new File(context.getCacheDir(), name);
try {
InputStream inputStream = context.getContentResolver().openInputStream(uri);
FileOutputStream outputStream = new FileOutputStream(file);
int read = 0;
int maxBufferSize = 1 * 1024 * 1024;
int bytesAvailable = inputStream.available();
//int bufferSize = 1024;
int bufferSize = Math.min(bytesAvailable, maxBufferSize);
final byte[] buffers = new byte[bufferSize];
while ((read = inputStream.read(buffers)) != -1) {
outputStream.write(buffers, 0, read);
}
Log.e(TAG, "Size " + file.length());
inputStream.close();
outputStream.close();
Log.e(TAG, "Path " + file.getPath());
Log.e(TAG, "Size " + file.length());
} catch (Exception e) {
Log.e(TAG, e.getMessage());
}
return file.getPath();
}
/***
* Used for Android Q+
* @param uri
* @param newDirName if you want to create a directory, you can set this variable
* @return
*/
private String copyFileToInternalStorage(Uri uri, String newDirName) {
Uri returnUri = uri;
Cursor returnCursor = context.getContentResolver().query(returnUri, new String[] {
OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE
}, null, null, null);
/*
* Get the column indexes of the data in the Cursor,
* * move to the first row in the Cursor, get the data,
* * and display it.
* */
int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
int sizeIndex = returnCursor.getColumnIndex(OpenableColumns.SIZE);
returnCursor.moveToFirst();
String name = (returnCursor.getString(nameIndex));
String size = (Long.toString(returnCursor.getLong(sizeIndex)));
File output;
if (!newDirName.equals("")) {
String random_collision_avoidance = UUID.randomUUID().toString();
File dir = new File(context.getFilesDir() + File.separator + newDirName + File.separator + random_collision_avoidance);
if (!dir.exists()) {
dir.mkdirs();
}
output = new File(context.getFilesDir() + File.separator + newDirName + File.separator + random_collision_avoidance + File.separator + name);
} else {
output = new File(context.getFilesDir() + File.separator + name);
}
try {
InputStream inputStream = context.getContentResolver().openInputStream(uri);
FileOutputStream outputStream = new FileOutputStream(output);
int read = 0;
int bufferSize = 1024;
final byte[] buffers = new byte[bufferSize];
while ((read = inputStream.read(buffers)) != -1) {
outputStream.write(buffers, 0, read);
}
inputStream.close();
outputStream.close();
} catch (Exception e) {
Log.e(TAG, e.getMessage());
}
return output.getPath();
}
private String getFilePathForWhatsApp(Uri uri) {
return copyFileToInternalStorage(uri, "whatsapp");
}
private String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) {
Cursor cursor = null;
final String column = "_data";
final String[] projection = {
column
};
try {
cursor = context.getContentResolver().query(uri, projection,
selection, selectionArgs, null);
if (cursor != null && cursor.moveToFirst()) {
final int index = cursor.getColumnIndexOrThrow(column);
return cursor.getString(index);
}
} finally {
if (cursor != null)
cursor.close();
}
return null;
}
private static boolean isExternalStorageDocument(Uri uri) {
return "com.android.externalstorage.documents".equals(uri.getAuthority());
}
private static boolean isDownloadsDocument(Uri uri) {
return "com.android.providers.downloads.documents".equals(uri.getAuthority());
}
private boolean isMediaDocument(Uri uri) {
return "com.android.providers.media.documents".equals(uri.getAuthority());
}
private boolean isGooglePhotosUri(Uri uri) {
return "com.google.android.apps.photos.content".equals(uri.getAuthority());
}
public boolean isWhatsAppFile(Uri uri) {
return "com.whatsapp.provider.media".equals(uri.getAuthority());
}
private boolean isGoogleDriveUri(Uri uri) {
return "com.google.android.apps.docs.storage".equals(uri.getAuthority()) || "com.google.android.apps.docs.storage.legacy".equals(uri.getAuthority());
}
}

View File

@ -1,13 +1,18 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" xmlns:tools="http://schemas.android.com/tools"
android:layout_height="match_parent" android:layout_width="match_parent"
tools:context=".ui.settings.SettingsFragment"> android:layout_height="match_parent"
tools:context=".ui.settings.SettingsFragment">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/compressing_recycler_view" <androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent" android:id="@+id/compressing_recycler_view"
android:layout_height="match_parent" /> android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>