Skip to content

Commit

Permalink
KeepAlive foreground service
Browse files Browse the repository at this point in the history
  • Loading branch information
hardcore-sushi committed Jul 16, 2024
1 parent 52a29b0 commit 33d565b
Show file tree
Hide file tree
Showing 37 changed files with 521 additions and 208 deletions.
2 changes: 1 addition & 1 deletion BUILD.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ $ wget https://openssl.org/source/openssl-3.3.1.tar.gz
```
Verify OpenSSL signature:
```
$ https://openssl.org/source/openssl-3.3.1.tar.gz.asc
$ wget https://openssl.org/source/openssl-3.3.1.tar.gz.asc
$ gpg --verify openssl-3.3.1.tar.gz.asc openssl-3.3.1.tar.gz
```
Continue **ONLY** if the signature is **VALID**.
Expand Down
3 changes: 2 additions & 1 deletion TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ Here's a list of features that it would be nice to have in DroidFS. As this is a

## UX
- File associations editor
- Optional discovery before file operations
- Discovery before exporting
- Making discovery before file operations optional
- Modifiable CryFS scrypt parameters
- Alert dialog showing details of file operations
- Internal file browser to select volumes
Expand Down
5 changes: 3 additions & 2 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,11 @@
<activity android:name=".CameraActivity" android:screenOrientation="nosensor" />
<activity android:name=".LogcatActivity"/>

<service android:name=".WiperService" android:exported="false" android:stopWithTask="false"/>
<service android:name=".KeepAliveService" android:exported="false" android:foregroundServiceType="dataSync" />
<service android:name=".ClosingService" android:exported="false" android:stopWithTask="false"/>
<service android:name=".file_operations.FileOperationService" android:exported="false" android:foregroundServiceType="dataSync"/>

<receiver android:name=".file_operations.NotificationBroadcastReceiver" android:exported="false">
<receiver android:name=".NotificationBroadcastReceiver" android:exported="false">
<intent-filter>
<action android:name="file_operation_cancel"/>
</intent-filter>
Expand Down
3 changes: 2 additions & 1 deletion app/src/main/java/sushi/hardcore/droidfs/BaseActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import android.content.SharedPreferences
import android.os.Bundle
import android.view.WindowManager
import androidx.appcompat.app.AppCompatActivity
import androidx.preference.PreferenceManager

open class BaseActivity: AppCompatActivity() {
protected lateinit var sharedPrefs: SharedPreferences
Expand All @@ -12,7 +13,7 @@ open class BaseActivity: AppCompatActivity() {

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
sharedPrefs = (application as VolumeManagerApp).sharedPreferences
sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this)
theme = Theme.fromSharedPrefs(sharedPrefs)
if (applyCustomTheme) {
setTheme(theme.toResourceId())
Expand Down
22 changes: 13 additions & 9 deletions app/src/main/java/sushi/hardcore/droidfs/CameraActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ import android.os.Build
import android.os.Bundle
import android.text.InputType
import android.util.Size
import android.view.*
import android.view.MotionEvent
import android.view.ScaleGestureDetector
import android.view.Surface
import android.view.View
import android.view.animation.Animation
import android.view.animation.LinearInterpolator
import android.view.animation.RotateAnimation
Expand Down Expand Up @@ -42,8 +45,8 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import sushi.hardcore.droidfs.databinding.ActivityCameraBinding
import sushi.hardcore.droidfs.filesystems.EncryptedVolume
import sushi.hardcore.droidfs.util.IntentUtils
import sushi.hardcore.droidfs.util.PathUtils
import sushi.hardcore.droidfs.util.finishOnClose
import sushi.hardcore.droidfs.video_recording.AsynchronousSeekableWriter
import sushi.hardcore.droidfs.video_recording.FFmpegMuxer
import sushi.hardcore.droidfs.video_recording.SeekableWriter
Expand All @@ -52,7 +55,9 @@ import sushi.hardcore.droidfs.widgets.EditTextDialog
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.text.SimpleDateFormat
import java.util.*
import java.util.Date
import java.util.Locale
import java.util.Random
import java.util.concurrent.Executor
import kotlin.math.pow
import kotlin.math.sqrt
Expand Down Expand Up @@ -113,7 +118,10 @@ class CameraActivity : BaseActivity(), SensorOrientationListener.Listener {
binding = ActivityCameraBinding.inflate(layoutInflater)
setContentView(binding.root)
supportActionBar?.hide()
encryptedVolume = IntentUtils.getParcelableExtra(intent, "volume")!!
encryptedVolume = (application as VolumeManagerApp).volumeManager.getVolume(
intent.getIntExtra("volumeId", -1)
)!!
finishOnClose(encryptedVolume)
outputDirectory = intent.getStringExtra("path")!!

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
Expand Down Expand Up @@ -577,11 +585,7 @@ class CameraActivity : BaseActivity(), SensorOrientationListener.Listener {

override fun onResume() {
super.onResume()
if (encryptedVolume.isClosed()) {
finish()
} else {
sensorOrientationListener.addListener(this)
}
sensorOrientationListener.addListener(this)
}

override fun onOrientationChange(newOrientation: Int) {
Expand Down
20 changes: 20 additions & 0 deletions app/src/main/java/sushi/hardcore/droidfs/ClosingService.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package sushi.hardcore.droidfs

import android.app.Service
import android.content.Intent

/**
* Dummy background service listening for application task removal in order to
* close all volumes still open on quit.
*
* Should only be running when usfBackground is enabled AND usfKeepOpen is disabled.
*/
class ClosingService : Service() {
override fun onBind(intent: Intent) = null

override fun onTaskRemoved(rootIntent: Intent) {
super.onTaskRemoved(rootIntent)
(application as VolumeManagerApp).volumeManager.closeAll()
stopSelf()
}
}
120 changes: 120 additions & 0 deletions app/src/main/java/sushi/hardcore/droidfs/KeepAliveService.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package sushi.hardcore.droidfs

import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.app.Service
import android.content.Intent
import android.content.pm.ServiceInfo
import android.os.Build
import android.os.Parcel
import android.os.Parcelable
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import androidx.core.app.ServiceCompat
import androidx.core.content.IntentCompat

class KeepAliveService: Service() {
internal class NotificationDetails(
val channel: String,
val title: String,
val text: String,
val action: NotificationAction,
) : Parcelable {
internal class NotificationAction(
val icon: Int,
val title: String,
val action: String,
)

constructor(parcel: Parcel) : this(
parcel.readString()!!,
parcel.readString()!!,
parcel.readString()!!,
NotificationAction(
parcel.readInt(),
parcel.readString()!!,
parcel.readString()!!,
)
)

override fun writeToParcel(parcel: Parcel, flags: Int) {
with (parcel) {
writeString(channel)
writeString(title)
writeString(text)
writeInt(action.icon)
writeString(action.title)
writeString(action.action)
}
}

override fun describeContents() = 0

companion object CREATOR : Parcelable.Creator<NotificationDetails> {
override fun createFromParcel(parcel: Parcel) = NotificationDetails(parcel)
override fun newArray(size: Int) = arrayOfNulls<NotificationDetails>(size)
}

}

companion object {
const val ACTION_START = "start"

/**
* If [startForeground] is called before notification permission is granted,
* the notification won't appear.
*
* This action can be used once the permission is granted, to make the service
* call [startForeground] again in order to properly show the notification.
*/
const val ACTION_FOREGROUND = "foreground"
const val NOTIFICATION_CHANNEL_ID = "KeepAlive"
}

private val notificationManager by lazy {
NotificationManagerCompat.from(this)
}
private var notification: Notification? = null

override fun onBind(intent: Intent?) = null

override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
if (intent.action == ACTION_START) {
val notificationDetails = IntentCompat.getParcelableExtra(intent, "notification", NotificationDetails::class.java)!!
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
notificationManager.createNotificationChannel(
NotificationChannel(
NOTIFICATION_CHANNEL_ID,
notificationDetails.channel,
NotificationManager.IMPORTANCE_LOW
)
)
}
notification = NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID)
.setSmallIcon(R.drawable.ic_notification)
.setContentTitle(notificationDetails.title)
.setContentText(notificationDetails.text)
.addAction(NotificationCompat.Action(
notificationDetails.action.icon,
notificationDetails.action.title,
PendingIntent.getBroadcast(
this,
0,
Intent(this, NotificationBroadcastReceiver::class.java).apply {
action = notificationDetails.action.action
},
PendingIntent.FLAG_IMMUTABLE
)
))
.build()
}
ServiceCompat.startForeground(this, startId, notification!!, if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC // is there a better use case flag?
} else {
0
})
return START_NOT_STICKY
}
}
12 changes: 3 additions & 9 deletions app/src/main/java/sushi/hardcore/droidfs/MainActivity.kt
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
package sushi.hardcore.droidfs

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.IBinder
import android.view.Menu
import android.view.MenuItem
import android.view.View
Expand Down Expand Up @@ -131,7 +127,6 @@ class MainActivity : BaseActivity(), VolumeAdapter.Listener {
}
}
}
startService(Intent(this, WiperService::class.java))
FileOperationService.bind(this) {
fileOperationService = it
}
Expand Down Expand Up @@ -184,9 +179,7 @@ class MainActivity : BaseActivity(), VolumeAdapter.Listener {
}

private fun unselect(position: Int) {
volumeAdapter.selectedItems.remove(position)
volumeAdapter.onVolumeChanged(position)
onSelectionChanged(0) // unselect() is always called when only one element is selected
volumeAdapter.unselect(position)
invalidateOptionsMenu()
}

Expand Down Expand Up @@ -285,7 +278,7 @@ class MainActivity : BaseActivity(), VolumeAdapter.Listener {
R.id.delete_password_hash -> {
for (i in volumeAdapter.selectedItems) {
if (volumeDatabase.removeHash(volumeAdapter.volumes[i]))
volumeAdapter.onVolumeChanged(i)
volumeAdapter.onVolumeDataChanged(i)
}
unselectAll(false)
true
Expand Down Expand Up @@ -475,6 +468,7 @@ class MainActivity : BaseActivity(), VolumeAdapter.Listener {
if (success) {
volumeDatabase.renameVolume(volume, newDBName)
VolumeProvider.notifyRootsChanged(this)
volumeAdapter.onVolumeDataChanged(position)
unselect(position)
if (volume.name == volumeOpener.defaultVolumeName) {
with (sharedPrefs.edit()) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package sushi.hardcore.droidfs

import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import sushi.hardcore.droidfs.file_operations.FileOperationService

class NotificationBroadcastReceiver: BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
when (intent.action) {
FileOperationService.ACTION_CANCEL -> {
intent.getBundleExtra("bundle")?.let { bundle ->
// TODO: use peekService instead?
val binder = (bundle.getBinder("binder") as FileOperationService.LocalBinder?)
binder?.getService()?.cancelOperation(bundle.getInt("taskId"))
}
}
VolumeManagerApp.ACTION_CLOSE_ALL_VOLUMES -> {
(context.applicationContext as VolumeManagerApp).volumeManager.closeAll()
}
}
}
}
Loading

0 comments on commit 33d565b

Please sign in to comment.