Skip to content

Commit

Permalink
Merge pull request #18 from Automattic/app/foundation-add-mp4composer
Browse files Browse the repository at this point in the history
Introduce mp4composer lib as a module
  • Loading branch information
aforcier authored Aug 12, 2019
2 parents 0708d3c + e5ced1c commit d367fe8
Show file tree
Hide file tree
Showing 58 changed files with 5,414 additions and 5 deletions.
1 change: 1 addition & 0 deletions .idea/modules.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,18 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.automattic.portkey">

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO"/>

<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />


<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package com.automattic.portkey.compose

import android.Manifest
import android.annotation.SuppressLint
import android.app.ProgressDialog
import android.net.Uri
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
Expand All @@ -10,21 +13,29 @@ import android.widget.Toast
import androidx.core.content.ContextCompat
import com.automattic.photoeditor.OnPhotoEditorListener
import com.automattic.photoeditor.PhotoEditor
import com.automattic.photoeditor.SaveSettings
import com.automattic.photoeditor.state.BackgroundSurfaceManager
import com.automattic.photoeditor.util.FileUtils.Companion.getLoopFrameFile
import com.automattic.photoeditor.util.PermissionUtils
import com.automattic.photoeditor.util.PermissionUtils.OnRequestPermissionGrantedCheck
import com.automattic.photoeditor.views.ViewType
import com.automattic.portkey.R
import com.automattic.portkey.R.color
import com.automattic.portkey.R.id
import com.automattic.portkey.R.layout
import com.automattic.portkey.R.string
import com.google.android.material.snackbar.Snackbar
import kotlinx.android.synthetic.main.activity_composer.*

import kotlinx.android.synthetic.main.activity_main.toolbar
import kotlinx.android.synthetic.main.content_composer.*
import java.io.File
import java.io.IOException

class ComposeLoopFrameActivity : AppCompatActivity() {
private lateinit var photoEditor: PhotoEditor
private lateinit var backgroundSurfaceManager: BackgroundSurfaceManager
private var progressDialog: ProgressDialog? = null

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Expand Down Expand Up @@ -85,6 +96,23 @@ class ComposeLoopFrameActivity : AppCompatActivity() {
super.onSaveInstanceState(outState)
}

override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
PermissionUtils.onRequestPermissionsResult(object : OnRequestPermissionGrantedCheck {
override fun isPermissionGranted(isGranted: Boolean, permission: String) {
if (isGranted) {
if (permission == Manifest.permission.WRITE_EXTERNAL_STORAGE) {
// TODO we should check for a request Code to make sure
// the permission was requested in order to save a loop frame rather than something else
// (i.e. as opposed to saving the originally captured video)
saveLoopFrame()
} else if (permission == Manifest.permission.RECORD_AUDIO) {
backgroundSurfaceManager.switchCameraPreviewOn()
}
}
}
}, requestCode, permissions, grantResults)
}

override fun onCreateOptionsMenu(menu: Menu): Boolean {
// Inflate the menu; this adds items to the action bar if it is present.
menuInflater.inflate(R.menu.menu_main, menu)
Expand Down Expand Up @@ -133,7 +161,7 @@ class ComposeLoopFrameActivity : AppCompatActivity() {
}

id.action_save -> {
Toast.makeText(this, "Not implemented", Toast.LENGTH_SHORT).show()
saveLoopFrame()
true
}
else -> super.onOptionsItemSelected(item)
Expand Down Expand Up @@ -186,4 +214,167 @@ class ComposeLoopFrameActivity : AppCompatActivity() {
txtCurrentTool.setText(string.main_test_static_background)
backgroundSurfaceManager.switchStaticImageBackgroundModeOn()
}

// this one saves one composed unit: ether an Image or a Video
private fun saveLoopFrame() {
// check wether we have an Image or a Video, and call its save functionality accordingly
if (backgroundSurfaceManager.cameraVisible() || backgroundSurfaceManager.videoPlayerVisible()) {
saveVideo(backgroundSurfaceManager.getCurrentVideoFile().toString())
} else {
// check whether there are any GIF stickers - if there are, we need to produce a video instead
if (photoEditor.anyStickersAdded()) {
saveVideoWithStaticBackground()
} else {
saveImage()
}
}
}

@SuppressLint("MissingPermission")
private fun saveImage() {
if (PermissionUtils.checkAndRequestPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
showLoading("Saving...")
val file = getLoopFrameFile(false)
try {
file.createNewFile()

val saveSettings = SaveSettings.Builder()
.setClearViewsEnabled(true)
.setTransparencyEnabled(true)
.build()

photoEditor.saveAsFile(file.absolutePath, saveSettings, object : PhotoEditor.OnSaveListener {
override fun onSuccess(imagePath: String) {
hideLoading()
showSnackbar("Image Saved Successfully")
photoEditorView.source.setImageURI(Uri.fromFile(File(imagePath)))
}

override fun onFailure(exception: Exception) {
hideLoading()
showSnackbar("Failed to save Image")
}
})
} catch (e: IOException) {
e.printStackTrace()
hideLoading()
e.message?.takeIf { it.isNotEmpty() }?.let { showSnackbar(it) }
}
}
}

@SuppressLint("MissingPermission")
private fun saveVideo(inputFile: String) {
if (PermissionUtils.checkPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
showLoading("Saving...")
try {
val file = getLoopFrameFile(true)
file.createNewFile()

val saveSettings = SaveSettings.Builder()
.setClearViewsEnabled(true)
.setTransparencyEnabled(true)
.build()

photoEditor.saveVideoAsFile(
inputFile,
file.absolutePath,
saveSettings,
object : PhotoEditor.OnSaveWithCancelListener {
override fun onCancel(noAddedViews: Boolean) {
hideLoading()
showSnackbar("No views added - original video saved")
}

override fun onSuccess(imagePath: String) {
hideLoading()
showSnackbar("Video Saved Successfully")
}

override fun onFailure(exception: Exception) {
hideLoading()
showSnackbar("Failed to save Video")
}
})
} catch (e: IOException) {
e.printStackTrace()
hideLoading()
e.message?.takeIf { it.isNotEmpty() }?.let { showSnackbar(it) }
}
} else {
showSnackbar("Please allow WRITE TO STORAGE permissions")
}
}

@SuppressLint("MissingPermission")
private fun saveVideoWithStaticBackground() {
if (PermissionUtils.checkPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
showLoading("Saving...")
try {
val file = getLoopFrameFile(true, "tmp")
file.createNewFile()

val saveSettings = SaveSettings.Builder()
.setClearViewsEnabled(true)
.setTransparencyEnabled(true)
.build()

photoEditor.saveVideoFromStaticBackgroundAsFile(
file.absolutePath,
saveSettings,
object : PhotoEditor.OnSaveWithCancelListener {
override fun onCancel(noAddedViews: Boolean) {
// TODO not implemented
}

override fun onSuccess(imagePath: String) {
// now save the video with emoji, but using the previously saved video as input
hideLoading()
saveVideo(imagePath)
// TODO: delete the temporal video produced originally
}

override fun onFailure(exception: Exception) {
hideLoading()
showSnackbar("Failed to save Video")
}
})
} catch (e: IOException) {
e.printStackTrace()
hideLoading()
e.message?.takeIf { it.isNotEmpty() }?.let { showSnackbar(it) }
}
} else {
showSnackbar("Please allow WRITE TO STORAGE permissions")
}
}

protected fun showLoading(message: String) {
runOnUiThread {
progressDialog = ProgressDialog(this)
progressDialog!!.setMessage(message)
progressDialog!!.setProgressStyle(ProgressDialog.STYLE_SPINNER)
progressDialog!!.setCancelable(false)
progressDialog!!.show()
}
}

protected fun hideLoading() {
runOnUiThread {
if (progressDialog != null) {
progressDialog!!.dismiss()
}
}
}

protected fun showSnackbar(message: String) {
runOnUiThread {
val view = findViewById<View>(android.R.id.content)
if (view != null) {
Snackbar.make(view, message, Snackbar.LENGTH_SHORT).show()
} else {
Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
}
}
}
}
1 change: 1 addition & 0 deletions mp4compose/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
21 changes: 21 additions & 0 deletions mp4compose/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2018 Masayuki Suda

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
34 changes: 34 additions & 0 deletions mp4compose/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-android'

android {
compileSdkVersion 29

defaultConfig {
minSdkVersion 21
targetSdkVersion 29
versionCode 1
versionName "0.2.0"
}

buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
lintOptions {
abortOnError false
}

}

dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'com.github.bumptech.glide:glide:4.9.0'
}
repositories {
mavenCentral()
}
21 changes: 21 additions & 0 deletions mp4compose/proguard-rules.pro
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable

# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
2 changes: 2 additions & 0 deletions mp4compose/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.daasuu.mp4compose" />
Loading

0 comments on commit d367fe8

Please sign in to comment.