Skip to content
Merged
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
13 changes: 13 additions & 0 deletions app/android/src/uk/co/lutraconsulting/CameraActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ protected void onCreate(Bundle savedInstanceState) {

takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
takePictureIntent.putExtra("__RESULT__", "takePictureIntent__RESULT__");
startForegroundService(new Intent(this, CameraForegroundService.class));
Comment thread
Withalion marked this conversation as resolved.
startActivityForResult(takePictureIntent, CAMERA_CODE);
} else {
Intent activityIntent = getIntent();
Expand Down Expand Up @@ -115,6 +116,11 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
Log.d(TAG, "resultCode: " + resultCode);
orientationSensor.Unregister();

if (requestCode == CAMERA_CODE) {
// no-op if it isn't running (e.g. process was killed and recreated), stops it either way
stopService(new Intent(this, CameraForegroundService.class));
}

if (requestCode == CAMERA_CODE && resultCode == Activity.RESULT_OK) {
Log.d(TAG, "tmp exists: " + cameraFile.exists());
Log.d(TAG, "tmp path: " + cameraFile.getAbsolutePath());
Expand Down Expand Up @@ -142,6 +148,13 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
finish();
}

@Override
protected void onDestroy() {
super.onDestroy();
// no-op if it was already stopped in onActivityResult() or never started
stopService(new Intent(this, CameraForegroundService.class));
}

private void extendGPSExifData(long captureTime) {
int direction = getValueByTime(orientationSensor.m_azimuth_data, captureTime);
if (direction < 0) {
Expand Down
77 changes: 77 additions & 0 deletions app/android/src/uk/co/lutraconsulting/CameraForegroundService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/

package uk.co.lutraconsulting;

import android.os.Build;
import android.os.IBinder;
import android.app.Service;
import android.app.PendingIntent;
import android.content.Intent;
import android.content.pm.ServiceInfo;

import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;

/**
* Runs in the app's default process for as long as CameraActivity is waiting on the external
* camera app, to raise this process's priority and make it much less likely to be killed while
* the camera is in the foreground.
* This is a mitigation, not a guarantee -- under severe enough memory pressure the process can
* still be killed; CameraActivity does not currently resume state after that, so a kill mid-wait
* causes the camera capture to restart from scratch on the recreated process.
*/
public class CameraForegroundService extends Service {

private static final String CHANNEL_ID = "CameraForegroundServiceChannel";
private static final int SERVICE_ID = 1011;

@Override
public IBinder onBind(Intent intent) {
return null;
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
NotificationChannel serviceChannel = new NotificationChannel(
CHANNEL_ID,
"Camera Foreground Service Channel",
NotificationManager.IMPORTANCE_LOW
);

NotificationManager manager = getSystemService(NotificationManager.class);
manager.createNotificationChannel(serviceChannel);

Intent notificationIntent = new Intent(this, MMActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, PendingIntent.FLAG_IMMUTABLE);

Notification notification = new Notification.Builder(this, CHANNEL_ID)
.setSmallIcon(R.drawable.ic_notification)
.setContentTitle("Waiting for photo")
.setColor(getResources().getColor(R.color.grassColor))
.setContentIntent(pendingIntent)
.build();

// We never request the POST_NOTIFICATIONS runtime permission from the user, so this
// notification will silently not be shown unless the user has manually enabled it for
// the app in system Settings. startForeground() still succeeds and still elevates the
// process's priority either way -- the permission only gates the notification's
// visibility, not the foreground service state itself.
if (Build.VERSION.SDK_INT >= 35) {
startForeground(SERVICE_ID, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_SHORT_SERVICE);
} else {
startForeground(SERVICE_ID, notification);
}

// if this service alone gets killed there is nothing useful to resume, it only exists to
// keep the process's priority elevated while CameraActivity waits on startActivityForResult().
return START_NOT_STICKY;
}
}
7 changes: 7 additions & 0 deletions cmake_templates/AndroidManifest.xml.in
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,13 @@
android:stopWithTask="true"
android:exported="false"
/>

<service
android:name=".CameraForegroundService"
android:foregroundServiceType="shortService"
android:stopWithTask="true"
android:exported="false"
/>
</application>

<!-- Explicitly mention that we need to use external app for capturing an image and open file-->
Expand Down
Loading