Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: keep screen awake during inference, deactivate when idle #210

Merged
merged 3 commits into from
Feb 16, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Next Next commit
feat: keep screen awake during inference and deactivate when idle
  • Loading branch information
a-ghorbani committed Feb 16, 2025
commit 551895bb0204ec3b16e033df0f7ee42dc51d03f6
28 changes: 28 additions & 0 deletions android/app/src/main/java/com/pocketpalai/KeepAwakeModule.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.pocketpal

import android.app.Activity
import android.view.WindowManager
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.ReactContextBaseJavaModule
import com.facebook.react.bridge.ReactMethod

class KeepAwakeModule(private val reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {

override fun getName(): String = "KeepAwakeModule"

@ReactMethod
fun activate() {
val activity = reactContext.currentActivity
activity?.runOnUiThread {
activity.window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
}
}

@ReactMethod
fun deactivate() {
val activity = reactContext.currentActivity
activity?.runOnUiThread {
activity.window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
}
}
}
16 changes: 16 additions & 0 deletions android/app/src/main/java/com/pocketpalai/KeepAwakePackage.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.pocketpal

import com.facebook.react.ReactPackage
import com.facebook.react.bridge.NativeModule
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.uimanager.ViewManager

class KeepAwakePackage : ReactPackage {
override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
return listOf(KeepAwakeModule(reactContext))
}

override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
return emptyList()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ class MainApplication : Application(), ReactApplication {
// Packages that cannot be autolinked yet can be added manually here, for example:
// add(MyReactNativePackage())
add(DeviceInfoPackage())
add(KeepAwakePackage())
}

override fun getJSMainModuleName(): String = "index"
Expand Down
4 changes: 4 additions & 0 deletions ios/PocketPal/KeepAwakeModule.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#import <React/RCTBridgeModule.h>

@interface KeepAwakeModule : NSObject <RCTBridgeModule>
@end
22 changes: 22 additions & 0 deletions ios/PocketPal/KeepAwakeModule.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#import "KeepAwakeModule.h"
#import <UIKit/UIKit.h>

@implementation KeepAwakeModule

RCT_EXPORT_MODULE(KeepAwakeModule);

RCT_EXPORT_METHOD(activate)
{
dispatch_async(dispatch_get_main_queue(), ^{
[[UIApplication sharedApplication] setIdleTimerDisabled:YES];
});
}

RCT_EXPORT_METHOD(deactivate)
{
dispatch_async(dispatch_get_main_queue(), ^{
[[UIApplication sharedApplication] setIdleTimerDisabled:NO];
});
}

@end
26 changes: 26 additions & 0 deletions src/hooks/useChatSession.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React, {useRef, useCallback} from 'react';

import {toJS} from 'mobx';
import throttle from 'lodash.throttle';

Expand All @@ -8,6 +9,7 @@ import {chatSessionStore, modelStore} from '../store';

import {MessageType, User} from '../utils/types';
import {applyChatTemplate, convertToChatMessages} from '../utils/chat';
import {activateKeepAwake, deactivateKeepAwake} from '../utils/keepAwake';

export const useChatSession = (
currentMessageInfo: React.MutableRefObject<{
Expand Down Expand Up @@ -86,6 +88,14 @@ export const useChatSession = (
modelStore.setIsStreaming(false);
chatSessionStore.setIsGenerating(true);

// Keep screen awake during completion
try {
activateKeepAwake();
} catch (error) {
console.error('Failed to activate keep awake during chat:', error);
// Continue with chat even if keep awake fails
}

const id = randId();
const createdAt = Date.now();
currentMessageInfo.current = {createdAt, id};
Expand Down Expand Up @@ -166,6 +176,13 @@ export const useChatSession = (
} else {
addSystemMessage(`Completion failed: ${errorMessage}`);
}
} finally {
// Always try to deactivate keep awake in finally block
try {
deactivateKeepAwake();
} catch (error) {
console.error('Failed to deactivate keep awake after chat:', error);
}
}
};

Expand All @@ -190,6 +207,15 @@ export const useChatSession = (
}
modelStore.setInferencing(false);
modelStore.setIsStreaming(false);
// Deactivate keep awake when stopping completion
try {
deactivateKeepAwake();
} catch (error) {
console.error(
'Failed to deactivate keep awake after stopping chat:',
error,
);
}
};

return {
Expand Down
32 changes: 32 additions & 0 deletions src/hooks/useKeepAwake.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import {useEffect} from 'react';
import {activateKeepAwake, deactivateKeepAwake} from '../utils/keepAwake';

/**
* React hook that prevents the screen from going to sleep while the component is mounted.
*
* @example
* ```tsx
* function VideoPlayer() {
* useKeepAwake(); // Screen will stay awake while VideoPlayer is mounted
* return <Video source={source} />;
* }
* ```
*/
export function useKeepAwake(): void {
useEffect(() => {
try {
activateKeepAwake();
return () => {
try {
deactivateKeepAwake();
} catch (error) {
console.error('Failed to deactivate keep awake in cleanup:', error);
}
};
} catch (error) {
console.error('Failed to activate keep awake:', error);
// We don't rethrow here as it would crash the component
// Instead, we log the error and let the screen timeout normally
}
}, []);
}
38 changes: 38 additions & 0 deletions src/utils/keepAwake.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import {NativeModules} from 'react-native';

const {KeepAwakeModule} = NativeModules;

if (!KeepAwakeModule) {
throw new Error(
'KeepAwakeModule is not available. Make sure:\n' +
'- You rebuilt the app after adding the native modules\n' +
'- The native module is properly linked\n' +
'- You are not using Expo managed workflow',
);
}

/**
* Activates keep awake functionality to prevent the screen from going to sleep
* @throws {Error} If the native module fails to activate
*/
export const activateKeepAwake = (): void => {
try {
KeepAwakeModule.activate();
} catch (error) {
console.error('Failed to activate keep awake:', error);
throw error;
}
};

/**
* Deactivates keep awake functionality allowing the screen to go to sleep
* @throws {Error} If the native module fails to deactivate
*/
export const deactivateKeepAwake = (): void => {
try {
KeepAwakeModule.deactivate();
} catch (error) {
console.error('Failed to deactivate keep awake:', error);
throw error;
}
};