Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,15 @@ android {

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
signingConfigs {
release {
// environment variables from ~/.gradle/gradle.properties
storeFile file(FFSHARE_RELEASE_STORE_FILE)
storePassword FFSHARE_RELEASE_STORE_PASSWORD
keyAlias FFSHARE_RELEASE_KEY_ALIAS
keyPassword FFSHARE_RELEASE_KEY_PASSWORD
}
}
// signingConfigs {
// release {
// // environment variables from ~/.gradle/gradle.properties
// storeFile file(FFSHARE_RELEASE_STORE_FILE)
// storePassword FFSHARE_RELEASE_STORE_PASSWORD
// keyAlias FFSHARE_RELEASE_KEY_ALIAS
// keyPassword FFSHARE_RELEASE_KEY_PASSWORD
// }
// }

splits {
// Configures multiple APKs based on ABI.
Expand Down Expand Up @@ -61,7 +61,7 @@ android {
}
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.release
// signingConfig signingConfigs.release
}
debug {
ndk {
Expand Down
11 changes: 10 additions & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
xmlns:tools="http://schemas.android.com/tools">

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

<!-- TODO: For when the app is upgraded to support Android 13 -->
<!-- <uses-permission android:name="android.permission.POST_NOTIFICATIONS"/> -->

<application
android:name=".App"
Expand Down Expand Up @@ -47,18 +51,21 @@
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />

<data android:mimeType="image/*" />
<data android:mimeType="video/*" />
<data android:mimeType="audio/*" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND_MULTIPLE" />
<category android:name="android.intent.category.DEFAULT" />

<data android:mimeType="image/*" />
<data android:mimeType="video/*" />
<data android:mimeType="audio/*" />
</intent-filter>
</activity>

<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.caydey.ffshare.fileprovider"
Expand All @@ -68,10 +75,12 @@
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/filepaths" />
</provider>

<receiver
android:name=".CacheCleanUpReceiver"
android:enabled="true"
android:exported="false" />
</application>

<service android:name=".services.HandleMediaService" />
</application>
</manifest>
21 changes: 20 additions & 1 deletion app/src/main/java/com/caydey/ffshare/App.kt
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
package com.caydey.ffshare

import android.app.NotificationChannel
import android.app.NotificationManager
import android.content.Context
import androidx.multidex.MultiDexApplication
import com.caydey.ffshare.services.MEDIA_NOTIFICATION_CHANNEL
import timber.log.Timber

class App: MultiDexApplication() {
class App : MultiDexApplication() {
companion object {
var versionName = ""
}

private val settingsVersionUpdater = SettingsVersionUpdater(this)
override fun onCreate() {
super.onCreate()
Expand All @@ -21,5 +26,19 @@ class App: MultiDexApplication() {
if (BuildConfig.DEBUG) {
Timber.plant(Timber.DebugTree())
}

// Initialize notification channel
initializeNotificationChannel()
}

private fun initializeNotificationChannel() {
val channel = NotificationChannel(
MEDIA_NOTIFICATION_CHANNEL,
getString(R.string.media_service_notification_title),
NotificationManager.IMPORTANCE_HIGH
)

(getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager)
.createNotificationChannel(channel)
}
}
113 changes: 73 additions & 40 deletions app/src/main/java/com/caydey/ffshare/HandleMediaActivity.kt
Original file line number Diff line number Diff line change
@@ -1,26 +1,61 @@
package com.caydey.ffshare

import android.Manifest
import android.app.AlarmManager
import android.app.PendingIntent
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.net.Uri
import android.os.Bundle
import android.os.Handler
import android.os.IBinder
import android.os.Looper
import android.os.SystemClock
import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.caydey.ffshare.services.HandleMediaService
import com.caydey.ffshare.services.HandleMediaService.ProgressCallback
import com.caydey.ffshare.utils.MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE
import com.caydey.ffshare.utils.MediaCompressor
import com.caydey.ffshare.utils.Utils
import timber.log.Timber


class HandleMediaActivity : AppCompatActivity() {
class HandleMediaActivity : AppCompatActivity(), ProgressCallback {
// by lazy means load when variable is used, lazy-loading helps performance
// also without it there is a null error for applicationContext
private val mediaCompressor: MediaCompressor by lazy { MediaCompressor(applicationContext) }
private val utils: Utils by lazy { Utils(applicationContext) }

private lateinit var handleMediaService: HandleMediaService
private var isBoundToMediaService = false


private val mediaServiceConnection = object : ServiceConnection {
override fun onServiceConnected(className: ComponentName, service: IBinder) {

val binder = service as HandleMediaService.HandleMediaBinder
handleMediaService = binder.getService()
handleMediaService.setProgressHandlerCallback(this@HandleMediaActivity)
isBoundToMediaService = true
}

override fun onServiceDisconnected(arg0: ComponentName) {
isBoundToMediaService = false
}
}

override fun onProgressUpdate(progress: Float) {
// TODO: Retrieve all other information and display them
Handler(Looper.getMainLooper()).post {
val txtProcessedPercent: TextView = findViewById(R.id.txtProcessedPercent)
txtProcessedPercent.text = getString(R.string.format_percentage, progress)
}
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_handle_media)
Expand All @@ -29,22 +64,38 @@ class HandleMediaActivity : AppCompatActivity() {
onMediaReceive()
} else {
Timber.d("Requesting read permissions")
utils.requestReadPermissions(this)
utils.requestPermissions(
this,
arrayOf(
Manifest.permission.READ_EXTERNAL_STORAGE,
// TODO: Add this when the app is upgraded to support Android 13
// Manifest.permission.POST_NOTIFICATIONS,
)
)
}
}

override fun finish() {
mediaCompressor.cancelAllOperations()
scheduleCacheCleanup()
super.finish()
}

override fun onStop() {

override fun onDestroy() {
mediaCompressor.cancelAllOperations()
super.onStop()
if (isBoundToMediaService) {
handleMediaService.setProgressHandlerCallback(null)
unbindService(mediaServiceConnection)
isBoundToMediaService = false
}
super.onDestroy()
}

override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
// first time running app user is requested to allow app to read external storage,
// after clicking "allow" the app will continue handling media it was shared
Expand All @@ -61,53 +112,35 @@ class HandleMediaActivity : AppCompatActivity() {
Intent.ACTION_SEND_MULTIPLE -> intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM)!!
else -> ArrayList<Uri>()
}
// unable to get file from intent

// Start foreground service to start conversion
if (receivedMedia.isEmpty()) {
Toast.makeText(this, getString(R.string.error_no_uri_intent), Toast.LENGTH_LONG).show()
Timber.d("No files found in shared intent")
finish()
} else {
// callback
mediaCompressor.compressFiles(this, receivedMedia) { compressedMedia ->
if (compressedMedia.isNotEmpty()) {
shareMedia(compressedMedia)
Intent(this, HandleMediaService::class.java)
.apply {
action = HandleMediaService.ServiceActions.START.toString()
putParcelableArrayListExtra(Intent.EXTRA_STREAM, receivedMedia)
}
.also {
startService(it)
bindService(it, mediaServiceConnection, Context.BIND_AUTO_CREATE)
}
finish()
}
}
}

private fun shareMedia(mediaUris: ArrayList<Uri>) {
val shareIntent = Intent()

// temp permissions for other app to view file
shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)

// add compressed media files
if (mediaUris.size == 1) {
Timber.d("Creating share intent for single item")
shareIntent.action = Intent.ACTION_SEND
shareIntent.putExtra(Intent.EXTRA_STREAM, mediaUris[0])
} else {
Timber.d("Creating share intent for multiple items")
shareIntent.action = Intent.ACTION_SEND_MULTIPLE
shareIntent.putExtra(Intent.EXTRA_STREAM, mediaUris)
}

// set mime for each file
mediaUris.forEach { mediaUri ->
shareIntent.setDataAndType(mediaUri, contentResolver.getType(mediaUri))
}

val chooser = Intent.createChooser(shareIntent, "media")
startActivity(chooser)
}

private fun scheduleCacheCleanup() {
Timber.d("Scheduling cleanup alarm")
val alarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager
val intent = Intent(applicationContext, CacheCleanUpReceiver::class.java)
val pendingIntent = PendingIntent.getBroadcast(applicationContext, 0, intent, PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_CANCEL_CURRENT)
val pendingIntent = PendingIntent.getBroadcast(
applicationContext,
0,
intent,
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_CANCEL_CURRENT
)

// every 12 hours clear cache
alarmManager.setInexactRepeating(
Expand Down
Loading