Repository navigation
Conversation
A new setting, off by default: Keep measuring in the background (Advanced, next to the proximity lock). With it on, startMeasurement starts MeasurementService while the activity is visible, which Android requires for the location and microphone types. The service declares specialUse plus location, microphone and connectedDevice and requests only the types the loaded experiment needs; it holds a partial wake lock (a foreground service alone does not keep the CPU awake with the screen off) and shows an ongoing notification whose tap returns to the existing task and whose Stop action ends the run. Swiping the task away ends it too. Experiment.onStop keeps inputs, connections and the analysis thread running when the service owns the run and the activity is not finishing; only the view loop rests, and the depth camera closes as the system would revoke it anyway. onRestart resumes the loop, onDestroy is the real teardown, so recreation (locale, dark mode, split screen) still stops a run as before. Only the owning activity can stop the service, so a second experiment screen closing over a background run cannot end it. Camera experiments still pause in the background. Switching the setting on requests POST_NOTIFICATIONS on Android 13+; without it the run still continues, listed in the system's task manager. LifecycleTest gains a test with the setting on (home, still measuring, return, still measuring); the existing test keeps pinning the default. t1_instrumented.sh grants the notification permission.
MeasurementRun now owns a measurement: the measuring state, the analysis thread, the timed-run countdowns with their beeps, and the foreground service. The Experiment activity attaches to a run and detaches from it. A run with the service goes on when Android destroys its activity (Recents trimming or a swiped-away task, which the app cannot tell apart, so removing the task no longer stops a background run; the notification's Stop does), and the notification's tap attaches a new Experiment screen to the same experiment and data. A recreated activity (dark mode, language, font size) attaches to the run instead of ending it, with or without the setting. Camera and depth runs still stop and never use the service. One run at a time goes on without its screen: starting a second asks whether to end it. MeasurementService: an end that arrives before startForeground waits for it, as Android kills the app otherwise (a zero-length timed run did). A run that ends while nobody watches (its timer, the notification's Stop, a remote stop) keeps the service with a "Measurement ended in the background" notification until the user opens the result or closes the notification, so the unsaved data is not reclaimed with a cached process; its connections close as leaving the screen would. A swiped-away notification of a running measurement (Android 14+) is posted again. The tap starts Experiment with CLEAR_TOP and SINGLE_TOP instead of the launcher intent, which pushed the experiment list on Android 10 and older and for runs opened from another app. The service's statics are only touched on the main thread, and an old instance's onDestroy no longer resets the state of a newer start. Experiment: the service starts with a timed run's countdown, so leaving during the countdown no longer cancels the run. The remote server keeps serving a background run and the view loop carries out its commands. The proximity lock is released in the background and taken again on return. A remote start queued before shutdownIO no longer starts a run. The countdown refreshes the menu at most every 0.1 s and only on screen. Bluetooth battery and signal information goes to the screen of the device's own experiment (a weak delegate per device instead of a static), and so do a device's commands (a second weak delegate instead of the activity that loaded the device, which may be gone; the commands read the run's state). The local network permission is only requested from a screen in the foreground, where Android shows the request. LifecycleTest reads the service state from dumpsys and the notification from NotificationManager instead of the app's own flags, releases any run in its cleanup, sends the home key through the shell (UiDevice.pressHome waits for an idle screen, which a running countdown never gives) and gains tests for a zero-length timed run, recreation, removal of the task, the notification's return, Stop, Close and swipe, a timed run and a remote stop ending in the background, the countdown in the background, the proximity lock and a second experiment. 16/16 on the API 36 emulator. BluetoothCommandTest checks that commands go to the screen of the run, not the one that loaded the device.
dumpsys activity services prints the "Last ANR service" of any app whatever the filter. After a SystemUI ANR on a slow first emulator boot, that record made every "the service outlived the run" check fail (6 of 16), as any ServiceRecord counted as the measurement service. The checks now look for a ServiceRecord of the app's own MeasurementService and read isForeground from that record only. Verified on the API 36 emulator with a SystemUI ANR record present for the whole run.
A screen that takes a run over (recreated by a configuration change, or opened from the notification after the run's own screen was gone) attached its own SensorManager to the experiment's sensor inputs. Each Context has its own SensorManager and a listener can only be unregistered through the one that registered it, so a stop from that screen left the sensors on. Found on a Galaxy S23: after a run stopped that way the accelerometer stayed on at 416 Hz, still appending to the run's data. Experiment now takes the application context's SensorManager and LocationManager, which every screen shares. LifecycleTest gains two tests on accelerometer.phyphox that stop a run from a recreated screen and from the screen of the notification, then look for a connection of phyphox's SensorInput in dumpsys sensorservice. Both failed before the change and pass after, on the API 36 emulator and on the phone.
|
Thanks, this is a feature that has often been requested and so far we have shied away from it because of the massive refactor needed and because it is unclear which features would still work reliably. Since we have started to tackle larger tasks with AI support, the former becomes less of a problem, but the latter is still something that requires extensive testing. Also, it is not clear whether the same can be implemented for iOS, so we need to check if feature parity can be achieved. Nevertheless, your solution of making it opt in is elegant insofar that these things can be considered less imperative, especially if we first slap an "experimental" label onto the option. So, what I would do is setting up extensive tests on real hardware. In particular in regard to interaction with other apps, because my biggest fear for this has always been resource cleanups, other apps requesting sensors and behavior inconsistencies between Android versions and even manufacturers. This is not trivial and not a small test as we need a wide range of devices combined with many phyphox features. btw, judging from the speed and how the existing coding style was imitated almost obsessively, I would guess you used an AI for this? Am I right? Can you tell me which model and a rough idea for the workflow, so I have an idea what to look for in a review? Unfortunately, it will be a while before I get around to this as I am wrapping up the last features for 1.3.0 and this feature here is too big to cram it in there quickly. But thanks a lot - if it works as advertised, it will make many users very happy. |
|
@Staacks thanks for the reply. Yeah, I totally get it, feel free to close and just use it for reference in the upcoming refactoring. I needed this feature for my own experiments, but I can work from my fork without any issues. Yes, I used Claude Code as part of this, with Opus 5.5. My workflow was:
I also did a few review passes with Claude in between. It's also quite good at doing repetitive mundane tasks, like running tests through |
Adds a setting, "Keep measuring in the background" (off by default). With it on, a running measurement continues when you leave phyphox, swipe it away or turn the screen off.
With the setting off, behaviour is unchanged, except that a dark mode, language or font size change no longer ends a run.
Uses a foreground service (
MeasurementService) declaring the types specialUse, location, microphone and connectedDevice. It only requests the ones the experiment uses. Google Play will want the foreground service declaration for these.Tested with the instrumented
LifecycleTest(18 tests) on an API 36 emulator and on a Galaxy S23 (Android 16).