├── .github └── ISSUE_TEMPLATE │ └── bug_report.md ├── .gitignore ├── LICENSE ├── PRIVACY.md ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── caydey │ │ └── ffshare │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── ic_launcher-playstore.png │ ├── java │ │ └── com │ │ │ └── caydey │ │ │ └── ffshare │ │ │ ├── App.kt │ │ │ ├── CacheCleanUpReceiver.kt │ │ │ ├── HandleMediaActivity.kt │ │ │ ├── LogItemDialog.kt │ │ │ ├── LogItemsAdapter.kt │ │ │ ├── LogsActivity.kt │ │ │ ├── MainActivity.kt │ │ │ ├── PreferencesActivity.kt │ │ │ ├── PreferencesFragment.kt │ │ │ ├── SettingsVersionUpdater.kt │ │ │ ├── extensions │ │ │ ├── Compat.kt │ │ │ └── Context.kt │ │ │ └── utils │ │ │ ├── ExifTools.kt │ │ │ ├── FFmpegParamMaker.kt │ │ │ ├── MediaCompressor.kt │ │ │ ├── Settings.kt │ │ │ ├── Utils.kt │ │ │ └── logs │ │ │ ├── Log.kt │ │ │ └── LogsDbHelper.kt │ └── res │ │ ├── drawable │ │ ├── ic_launcher_background.xml │ │ └── ic_launcher_foreground.xml │ │ ├── layout │ │ ├── activity_handle_media.xml │ │ ├── activity_logs.xml │ │ ├── activity_main.xml │ │ ├── activity_preferences.xml │ │ ├── log_item_dialog.xml │ │ └── log_item_view.xml │ │ ├── menu │ │ ├── menu_logs.xml │ │ └── menu_main.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── navigation │ │ └── nav_graph.xml │ │ ├── values-es │ │ └── strings.xml │ │ ├── values-fr │ │ ├── arrays.xml │ │ └── strings.xml │ │ ├── values-gl │ │ └── strings.xml │ │ ├── values-land │ │ └── dimens.xml │ │ ├── values-night │ │ └── themes.xml │ │ ├── values-tr │ │ └── strings.xml │ │ ├── values-w1240dp │ │ └── dimens.xml │ │ ├── values-w600dp │ │ └── dimens.xml │ │ ├── values-zh-rCN │ │ ├── arrays.xml │ │ └── strings.xml │ │ ├── values-zh-rTW │ │ └── strings.xml │ │ ├── values │ │ ├── arrays.xml │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── themes.xml │ │ └── xml │ │ ├── filepaths.xml │ │ └── preferences.xml │ └── test │ └── java │ └── com │ └── caydey │ └── ffshare │ └── ExampleUnitTest.kt ├── artwork ├── icon_background.svg └── icon_foreground.svg ├── build.gradle ├── f-droid └── com.caydey.ffshare.yml ├── fastlane └── metadata │ └── android │ ├── en-US │ ├── changelogs │ │ ├── 10.txt │ │ ├── 11.txt │ │ ├── 12.txt │ │ ├── 13.txt │ │ ├── 14.txt │ │ ├── 15.txt │ │ ├── 16.txt │ │ ├── 18.txt │ │ ├── 19.txt │ │ ├── 2.txt │ │ ├── 20.txt │ │ ├── 21.txt │ │ ├── 22.txt │ │ ├── 3.txt │ │ ├── 4.txt │ │ ├── 5.txt │ │ ├── 6.txt │ │ ├── 7.txt │ │ ├── 8.txt │ │ └── 9.txt │ ├── full_description.txt │ ├── images │ │ ├── icon.png │ │ └── phoneScreenshots │ │ │ ├── 1.png │ │ │ ├── 2.png │ │ │ ├── 3.png │ │ │ └── 4.png │ ├── short_description.txt │ ├── title.txt │ └── video.txt │ ├── fr-FR │ ├── full_description.txt │ ├── short_description.txt │ └── title.txt │ └── tr-TR │ ├── full_description.txt │ ├── images │ ├── icon.png │ └── phoneScreenshots │ │ ├── 1.png │ │ ├── 2.png │ │ ├── 3.png │ │ └── 4.png │ ├── short_description.txt │ ├── title.txt │ └── video.txt ├── generate_test_files.sh ├── github_build_release.sh ├── google_play └── feature_graphic.png ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Report a bug encountered with app 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | 12 | **Settings applied** 13 | Does this happen on a clean install of the app or only when specific settings are changed 14 | 15 | **App Logs** 16 | Enable logs and provide a copy if it 17 | 18 | **File used** 19 | Uploading the file to help replicate the bug is very helpful however if it is not possible, run the following command on the file `ffprobe -v quiet -print_format json -show_format -show_streams FILE` and include its output 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea 5 | 6 | .DS_Store 7 | /app/full/release 8 | /build 9 | /captures 10 | .externalNativeBuild 11 | .cxx 12 | local.properties 13 | 14 | *.apk 15 | /repo 16 | /stats 17 | /app/release 18 | 19 | /github_releases 20 | /test_files -------------------------------------------------------------------------------- /PRIVACY.md: -------------------------------------------------------------------------------- 1 | # FFShare Privacy Policy 2 | This is an open source Android application developed by Caydey. The source code is available on GitHub under the GNU General Public License v3.0 3 | 4 | # Data collected 5 | This app does not collect any personal information 6 | 7 | # Contact 8 | If you have any questions regarding this privacy policy, you can contact the developer at ffshare-caydey@protonmail.com 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FFShare 2 | 3 | [Get it on F-Droid](https://f-droid.org/app/com.caydey.ffshare) 6 | [Get it on Obtainium](https://apps.obtainium.imranr.dev/redirect?r=obtainium://app/%7B%22id%22%3A%22com.caydey.ffshare%22%2C%22url%22%3A%22https%3A%2F%2Fgithub.com%2Fcaydey%2Fffshare%22%2C%22author%22%3A%22caydey%22%2C%22name%22%3A%22FFShare%22%2C%22preferredApkIndex%22%3A1%2C%22additionalSettings%22%3A%22%7B%5C%22includePrereleases%5C%22%3Afalse%2C%5C%22fallbackToOlderReleases%5C%22%3Atrue%2C%5C%22filterReleaseTitlesByRegEx%5C%22%3A%5C%22%5C%22%2C%5C%22filterReleaseNotesByRegEx%5C%22%3A%5C%22%5C%22%2C%5C%22verifyLatestTag%5C%22%3Afalse%2C%5C%22dontSortReleasesList%5C%22%3Afalse%2C%5C%22useLatestAssetDateAsReleaseDate%5C%22%3Afalse%2C%5C%22releaseTitleAsVersion%5C%22%3Afalse%2C%5C%22trackOnly%5C%22%3Afalse%2C%5C%22versionExtractionRegEx%5C%22%3A%5C%22%5C%22%2C%5C%22matchGroupToUse%5C%22%3A%5C%22%5C%22%2C%5C%22versionDetection%5C%22%3Atrue%2C%5C%22releaseDateAsVersion%5C%22%3Afalse%2C%5C%22useVersionCodeAsOSVersion%5C%22%3Afalse%2C%5C%22apkFilterRegEx%5C%22%3A%5C%22%5C%22%2C%5C%22invertAPKFilter%5C%22%3Afalse%2C%5C%22autoApkFilterByArch%5C%22%3Atrue%2C%5C%22appName%5C%22%3A%5C%22%5C%22%2C%5C%22shizukuPretendToBeGooglePlay%5C%22%3Afalse%2C%5C%22allowInsecure%5C%22%3Afalse%2C%5C%22exemptFromBackgroundUpdates%5C%22%3Afalse%2C%5C%22skipUpdateNotifications%5C%22%3Afalse%2C%5C%22about%5C%22%3A%5C%22%5C%22%2C%5C%22refreshBeforeDownload%5C%22%3Afalse%7D%22%2C%22overrideSource%22%3A%22GitHub%22%7D) 9 | 10 | [![GitHub release (latest by date)](https://img.shields.io/github/v/release/caydey/FFShare)](https://github.com/caydey/ffshare/releases/latest) 11 | [![GitHub all releases](https://img.shields.io/github/downloads/caydey/ffshare/total)](https://github.com/caydey/ffshare/releases/latest) 12 | [![GitHub license](https://img.shields.io/github/license/caydey/ffshare)](https://github.com/caydey/ffshare/blob/master/LICENSE) 13 | 14 | An android app to compress image, video and audio files through ffmpeg before sharing them 15 | 16 | ## Images 17 | 18 |

19 | 20 | 21 | 22 | 23 |

24 | 25 | ## Used Libraries 26 | 27 | - [FFmpegKit](https://github.com/arthenica/ffmpeg-kit) 28 | - [Timber](https://github.com/JakeWharton/timber) 29 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' 3 | id 'org.jetbrains.kotlin.android' 4 | } 5 | 6 | android { 7 | compileSdk 34 8 | 9 | defaultConfig { 10 | applicationId "com.caydey.ffshare" 11 | minSdk 26 12 | targetSdk 34 13 | versionCode 22 14 | versionName "1.3.3" 15 | 16 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 17 | } 18 | signingConfigs { 19 | release { 20 | // environment variables from ~/.gradle/gradle.properties 21 | storeFile file(FFSHARE_RELEASE_STORE_FILE) 22 | storePassword FFSHARE_RELEASE_STORE_PASSWORD 23 | keyAlias FFSHARE_RELEASE_KEY_ALIAS 24 | keyPassword FFSHARE_RELEASE_KEY_PASSWORD 25 | } 26 | } 27 | 28 | splits { 29 | // Configures multiple APKs based on ABI. 30 | abi { 31 | // Enables building multiple APKs per ABI. 32 | enable true 33 | 34 | // Resets the list of ABIs for Gradle to create APKs for to none. 35 | reset() 36 | 37 | //noinspection ChromeOsAbiSupport 38 | include "armeabi-v7a", "arm64-v8a" 39 | 40 | // Create apk with merged ABI libraries 41 | universalApk true 42 | } 43 | } 44 | 45 | flavorDimensions += 'version' 46 | productFlavors { 47 | full { 48 | dimension 'version' 49 | } 50 | video { 51 | dimension 'version' 52 | } 53 | } 54 | 55 | 56 | buildTypes { 57 | release { 58 | ndk { 59 | //noinspection ChromeOsAbiSupport 60 | abiFilters "armeabi-v7a", "arm64-v8a" 61 | } 62 | minifyEnabled true 63 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 64 | signingConfig signingConfigs.release 65 | } 66 | debug { 67 | ndk { 68 | abiFilters "armeabi-v7a", "arm64-v8a", "x86", "x86_64" 69 | } 70 | minifyEnabled false 71 | } 72 | 73 | } 74 | 75 | // applicationVariants.all { variant -> 76 | // def version = variant.name 77 | // def name = variant.buildType 78 | // def flavor = variant.flavorName 79 | // def apkName = "${name}_${flavor}_${version}.apk" 80 | // variant.outputs.all { output -> { 81 | // outputFileName = apkName 82 | // }} 83 | // } 84 | compileOptions { 85 | sourceCompatibility JavaVersion.VERSION_1_8 86 | targetCompatibility JavaVersion.VERSION_1_8 87 | } 88 | kotlinOptions { 89 | jvmTarget = '1.8' 90 | } 91 | buildFeatures { 92 | viewBinding true 93 | buildConfig true 94 | } 95 | namespace 'com.caydey.ffshare' 96 | } 97 | dependencies { 98 | implementation 'androidx.core:core-ktx:1.8.0' 99 | implementation 'androidx.appcompat:appcompat:1.5.0' 100 | implementation 'com.google.android.material:material:1.6.1' 101 | implementation 'androidx.constraintlayout:constraintlayout:2.1.4' 102 | implementation 'androidx.navigation:navigation-fragment-ktx:2.5.1' 103 | implementation 'androidx.navigation:navigation-ui-ktx:2.5.1' 104 | 105 | implementation 'androidx.multidex:multidex:2.0.1' 106 | implementation 'androidx.exifinterface:exifinterface:1.3.3' 107 | implementation 'androidx.preference:preference-ktx:1.2.0' 108 | 109 | // import ffmpeg-kit-full if building with audioSupport 110 | // else import ffmpeg-kit-min 111 | fullImplementation 'com.arthenica:ffmpeg-kit-full-gpl:5.1' 112 | videoImplementation 'com.arthenica:ffmpeg-kit-min-gpl:5.1' 113 | 114 | implementation 'com.jakewharton.timber:timber:5.0.1' 115 | 116 | testImplementation 'junit:junit:4.13.2' 117 | androidTestImplementation 'androidx.test.ext:junit:1.1.3' 118 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' 119 | } -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /app/src/androidTest/java/com/caydey/ffshare/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.caydey.ffshare 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("com.caydey.sharemediacompressed", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 16 | 21 | 24 | 25 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 38 | 41 | 42 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 67 | 70 | 71 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /app/src/main/ic_launcher-playstore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caydey/ffshare/a436ad5d5667e0276ff2588b96424287b1d7f872/app/src/main/ic_launcher-playstore.png -------------------------------------------------------------------------------- /app/src/main/java/com/caydey/ffshare/App.kt: -------------------------------------------------------------------------------- 1 | package com.caydey.ffshare 2 | 3 | import androidx.multidex.MultiDexApplication 4 | import timber.log.Timber 5 | 6 | class App: MultiDexApplication() { 7 | companion object { 8 | var versionName = "" 9 | } 10 | private val settingsVersionUpdater = SettingsVersionUpdater(this) 11 | override fun onCreate() { 12 | super.onCreate() 13 | 14 | // save version name as static variable for use with Log class and MainActivity classes 15 | @Suppress("DEPRECATION") 16 | versionName = packageManager.getPackageInfo(applicationContext.packageName, 0).versionName 17 | 18 | // check if there has been a version change and if it requires the settings to be changed 19 | settingsVersionUpdater.check() 20 | 21 | if (BuildConfig.DEBUG) { 22 | Timber.plant(Timber.DebugTree()) 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /app/src/main/java/com/caydey/ffshare/CacheCleanUpReceiver.kt: -------------------------------------------------------------------------------- 1 | package com.caydey.ffshare 2 | 3 | import android.app.AlarmManager 4 | import android.app.PendingIntent 5 | import android.content.BroadcastReceiver 6 | import android.content.Context 7 | import android.content.Intent 8 | import timber.log.Timber 9 | import java.io.File 10 | 11 | class CacheCleanUpReceiver: BroadcastReceiver() { 12 | override fun onReceive(context: Context, intent: Intent) { 13 | /* 14 | Cache structure 15 | /media/ 16 | UUID/custom-name.mp4 17 | UUID/custom-name.mp4 18 | UUID/UUID.jpg 19 | UUID/UUID.jpg 20 | */ 21 | 22 | Timber.d("Cleaning up cache") 23 | 24 | val mediaDir = File(context.cacheDir, "media") 25 | val cacheFiles = mediaDir.listFiles() 26 | 27 | cacheFiles?: return 28 | 29 | 30 | for (cacheFile in cacheFiles) { 31 | // media file older that 1 hour 32 | if (System.currentTimeMillis() - cacheFile.lastModified() > HOUR) { 33 | Timber.d("Deleting '%s'", cacheFile) 34 | 35 | cacheFile.deleteRecursively() 36 | } 37 | } 38 | 39 | // re-scan directory checking if it is empty 40 | if (mediaDir.listFiles()?.isEmpty() == true) { 41 | Timber.d("Cache folder empty, canceling cleanup alarm") 42 | // cancel alarm scheduler as there is no more cache files to cleanup 43 | val sender = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_CANCEL_CURRENT) 44 | val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager? 45 | alarmManager?.cancel(sender) 46 | } 47 | } 48 | companion object { 49 | private const val HOUR = 60 * 60 * 1_000 50 | } 51 | } -------------------------------------------------------------------------------- /app/src/main/java/com/caydey/ffshare/HandleMediaActivity.kt: -------------------------------------------------------------------------------- 1 | package com.caydey.ffshare 2 | 3 | import android.app.AlarmManager 4 | import android.app.PendingIntent 5 | import android.content.Context 6 | import android.content.Intent 7 | import android.net.Uri 8 | import android.os.Bundle 9 | import android.os.SystemClock 10 | import android.view.WindowManager 11 | import android.widget.Toast 12 | import androidx.appcompat.app.AppCompatActivity 13 | import com.caydey.ffshare.extensions.parcelable 14 | import com.caydey.ffshare.extensions.parcelableArrayList 15 | import com.caydey.ffshare.utils.MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE 16 | import com.caydey.ffshare.utils.MediaCompressor 17 | import com.caydey.ffshare.utils.Utils 18 | import timber.log.Timber 19 | 20 | 21 | class HandleMediaActivity : AppCompatActivity() { 22 | // by lazy means load when variable is used, lazy-loading helps performance 23 | // also without it there is a null error for applicationContext 24 | private val mediaCompressor: MediaCompressor by lazy { MediaCompressor(applicationContext) } 25 | private val utils: Utils by lazy { Utils(applicationContext) } 26 | 27 | override fun onCreate(savedInstanceState: Bundle?) { 28 | super.onCreate(savedInstanceState) 29 | setContentView(R.layout.activity_handle_media) 30 | 31 | window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) 32 | 33 | if (utils.isReadPermissionGranted) { 34 | onMediaReceive() 35 | } else { 36 | Timber.d("Requesting read permissions") 37 | utils.requestReadPermissions(this) 38 | } 39 | } 40 | 41 | override fun finish() { 42 | mediaCompressor.cancelAllOperations() 43 | scheduleCacheCleanup() 44 | super.finish() 45 | } 46 | 47 | override fun onStop() { 48 | mediaCompressor.cancelAllOperations() 49 | super.onStop() 50 | } 51 | 52 | override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { 53 | super.onRequestPermissionsResult(requestCode, permissions, grantResults) 54 | // first time running app user is requested to allow app to read external storage, 55 | // after clicking "allow" the app will continue handling media it was shared 56 | if (requestCode == MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE) { 57 | Timber.d("Read permissions granted, continuing...") 58 | onMediaReceive() 59 | } 60 | } 61 | 62 | private fun onMediaReceive() { 63 | // "intent" variable is the shared item 64 | val receivedMedia = 65 | when (intent.action) { 66 | Intent.ACTION_SEND -> arrayListOf(intent.parcelable(Intent.EXTRA_STREAM)!!) 67 | Intent.ACTION_SEND_MULTIPLE -> intent.parcelableArrayList(Intent.EXTRA_STREAM)!! 68 | else -> ArrayList() 69 | } 70 | 71 | // unable to get file from intent 72 | if (receivedMedia.isEmpty()) { 73 | Toast.makeText(this, getString(R.string.error_no_uri_intent), Toast.LENGTH_LONG).show() 74 | Timber.d("No files found in shared intent") 75 | finish() 76 | } else { 77 | // callback 78 | mediaCompressor.compressFiles(this, receivedMedia) { compressedMedia -> 79 | if (compressedMedia.isNotEmpty()) { 80 | shareMedia(compressedMedia) 81 | } 82 | finish() 83 | } 84 | } 85 | } 86 | 87 | private fun shareMedia(mediaUris: ArrayList) { 88 | val shareIntent = Intent() 89 | 90 | // temp permissions for other app to view file 91 | shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) 92 | 93 | // add compressed media files 94 | if (mediaUris.size == 1) { 95 | Timber.d("Creating share intent for single item") 96 | shareIntent.action = Intent.ACTION_SEND 97 | shareIntent.putExtra(Intent.EXTRA_STREAM, mediaUris[0]) 98 | } else { 99 | Timber.d("Creating share intent for multiple items") 100 | shareIntent.action = Intent.ACTION_SEND_MULTIPLE 101 | shareIntent.putExtra(Intent.EXTRA_STREAM, mediaUris) 102 | } 103 | 104 | // set mime for each file 105 | mediaUris.forEach { mediaUri -> 106 | shareIntent.setDataAndType(mediaUri, contentResolver.getType(mediaUri)) 107 | } 108 | 109 | val chooser = Intent.createChooser(shareIntent, "media") 110 | startActivity(chooser) 111 | } 112 | 113 | private fun scheduleCacheCleanup() { 114 | Timber.d("Scheduling cleanup alarm") 115 | val alarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager 116 | val intent = Intent(applicationContext, CacheCleanUpReceiver::class.java) 117 | val pendingIntent = PendingIntent.getBroadcast(applicationContext, 0, intent, PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_CANCEL_CURRENT) 118 | 119 | // every 12 hours clear cache 120 | alarmManager.setInexactRepeating( 121 | AlarmManager.ELAPSED_REALTIME_WAKEUP, 122 | SystemClock.elapsedRealtime(), 123 | AlarmManager.INTERVAL_HALF_DAY, 124 | pendingIntent 125 | ) 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /app/src/main/java/com/caydey/ffshare/LogItemDialog.kt: -------------------------------------------------------------------------------- 1 | package com.caydey.ffshare 2 | 3 | import android.app.Dialog 4 | import android.content.ClipData 5 | import android.content.ClipboardManager 6 | import android.content.Context 7 | import android.content.Context.CLIPBOARD_SERVICE 8 | import android.os.Bundle 9 | import android.text.method.ScrollingMovementMethod 10 | import android.widget.Button 11 | import android.widget.TextView 12 | import android.widget.Toast 13 | import com.caydey.ffshare.utils.logs.Log 14 | 15 | class LogItemDialog(context: Context, private val log: Log) : Dialog(context) { 16 | override fun onCreate(savedInstanceState: Bundle?) { 17 | super.onCreate(savedInstanceState) 18 | setContentView(R.layout.log_item_dialog) 19 | 20 | val txtFfmpegOutput = findViewById(R.id.logFfmpegOutput) 21 | val txtFfmpegTitle = findViewById(R.id.logFfmpegCommand) 22 | 23 | // scrollbars 24 | txtFfmpegOutput.movementMethod = ScrollingMovementMethod() 25 | txtFfmpegOutput.setHorizontallyScrolling(true) 26 | 27 | // content 28 | txtFfmpegTitle.text = log.command 29 | txtFfmpegOutput.text = log.ffmpeg_output 30 | 31 | findViewById