Temporary build: Quest KeepAwake APK - #54
Conversation
Deploying switchbotcontroller with
|
| Latest commit: |
b1b443e
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://00906b89.switchbotcontroller.pages.dev |
| Branch Preview URL: | https://temp-quest-keepawake-build-2.switchbotcontroller.pages.dev |
There was a problem hiding this comment.
Code Review
This pull request introduces a new Android application, Quest KeepAwake, designed to keep Meta Quest devices awake by running a foreground service that holds a partial wake lock, modifies system sleep settings, and periodically broadcasts a proximity sensor event. The review feedback highlights several critical issues and improvement opportunities: adding directBootAware to the service to ensure it can start on boot before unlock, handling potential ForegroundServiceStartNotAllowedException in the boot receiver to prevent crashes on Android 12+, requesting the WRITE_SETTINGS permission at runtime in MainActivity so that system settings can be successfully modified, and adding a null check for PowerManager to prevent a potential NullPointerException.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| <receiver android:name=".BootReceiver" android:exported="true" android:directBootAware="true"> | ||
| <intent-filter><action android:name="android.intent.action.BOOT_COMPLETED"/><action android:name="android.intent.action.LOCKED_BOOT_COMPLETED"/></intent-filter> | ||
| </receiver> | ||
| <service android:name=".KeepAwakeService" android:exported="false" /> |
There was a problem hiding this comment.
BootReceiver に android:directBootAware="true" が設定されており、デバイスの起動直後(ロック解除前)に送信される LOCKED_BOOT_COMPLETED を受信した際に KeepAwakeService を起動しようとします。
しかし、起動対象の KeepAwakeService 自体に android:directBootAware="true" が設定されていないため、ロック解除前にサービスを起動しようとするとシステムによって起動が拒否されるか、正常に動作しません。
デバイスの起動直後から確実にサービスを動作させるために、KeepAwakeService にも android:directBootAware="true" を指定することをお勧めします。
| <service android:name=".KeepAwakeService" android:exported="false" /> | |
| <service android:name=".KeepAwakeService" android:exported="false" android:directBootAware="true" /> |
| import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; | ||
| public class BootReceiver extends BroadcastReceiver { @Override public void onReceive(Context c, Intent i){ c.startForegroundService(new Intent(c, KeepAwakeService.class)); } } |
There was a problem hiding this comment.
Android 12 (API 31) 以降では、バックグラウンドからフォアグラウンドサービスを開始する際、特定の条件下で ForegroundServiceStartNotAllowedException(IllegalStateException のサブクラス)がスローされる可能性があります。
ブロードキャストレシーバー内での未キャッチの例外はアプリのクラッシュ(「アプリが繰り返し停止しています」など)を引き起こすため、startForegroundService の呼び出しを try-catch ブロックで囲み、安全に例外を処理することをお勧めします。
| import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; | |
| public class BootReceiver extends BroadcastReceiver { @Override public void onReceive(Context c, Intent i){ c.startForegroundService(new Intent(c, KeepAwakeService.class)); } } | |
| import android.content.BroadcastReceiver; | |
| import android.content.Context; | |
| import android.content.Intent; | |
| import android.util.Log; | |
| public class BootReceiver extends BroadcastReceiver { | |
| @Override | |
| public void onReceive(Context c, Intent i) { | |
| try { | |
| c.startForegroundService(new Intent(c, KeepAwakeService.class)); | |
| } catch (Exception e) { | |
| Log.e("BootReceiver", "Failed to start KeepAwakeService on boot", e); | |
| } | |
| } | |
| } |
| import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.widget.TextView; | ||
| public class MainActivity extends Activity { | ||
| @Override public void onCreate(Bundle b){ super.onCreate(b); startForegroundService(new Intent(this, KeepAwakeService.class)); TextView v=new TextView(this); v.setText("KeepAwake service is running.\nprox_close is sent every 2 seconds."); v.setTextSize(20); v.setPadding(32,32,32,32); setContentView(v); } |
There was a problem hiding this comment.
Android 6.0 (API 23) 以降では、Settings.System への書き込み(SCREEN_OFF_TIMEOUT の変更など)を行うために、マニフェストでの宣言に加えて、実行時にユーザーから「システム設定の変更」権限(WRITE_SETTINGS)を明示的に許可してもらう必要があります。
現在、権限がない状態で Settings.System.putInt を呼び出すと SecurityException が発生し、try-catch でキャッチされてサイレントに失敗します(画面消灯時間が変更されません)。
MainActivity の起動時などに Settings.System.canWrite(this) をチェックし、許可されていない場合は設定画面を開いてユーザーに許可を求めるように改善することをお勧めします。
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.provider.Settings;
import android.widget.TextView;
public class MainActivity extends Activity {
@Override
public void onCreate(Bundle b) {
super.onCreate(b);
if (!Settings.System.canWrite(this)) {
Intent intent = new Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS);
intent.setData(Uri.parse("package:" + getPackageName()));
startActivity(intent);
}
startForegroundService(new Intent(this, KeepAwakeService.class));
TextView v = new TextView(this);
v.setText("KeepAwake service is running.\\nprox_close is sent every 2 seconds.");
v.setTextSize(20);
v.setPadding(32, 32, 32, 32);
setContentView(v);
}| private final Handler handler=new Handler(Looper.getMainLooper()); private boolean running; private PowerManager.WakeLock wakeLock; | ||
| private final Runnable keepAwakeRunnable=new Runnable(){ @Override public void run(){ if(!running)return; sendProxClose(); handler.postDelayed(this,INTERVAL_MS); }}; | ||
| @Override public void onCreate(){ super.onCreate(); } | ||
| @Override public int onStartCommand(Intent intent,int flags,int startId){ if(running)return START_STICKY; running=true; createChannel(); startForeground(1,new Notification.Builder(this,CHANNEL_ID).setContentTitle("Quest KeepAwake").setContentText("Always-on keep-awake is active").setSmallIcon(android.R.drawable.ic_lock_idle_lock).setOngoing(true).build()); PowerManager pm=(PowerManager)getSystemService(POWER_SERVICE); wakeLock=pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,"QuestNavKeepAwake::StayAwake"); wakeLock.acquire(); applySettings(); sendProxClose(); handler.postDelayed(keepAwakeRunnable,INTERVAL_MS); Log.i(TAG,"Always-on service started"); return START_STICKY; } |
There was a problem hiding this comment.
防御的プログラミングの観点から、getSystemService(POWER_SERVICE) が null を返した場合に NullPointerException が発生するのを防ぐため、PowerManager のヌルチェックを行うことをお勧めします。また、コードの可読性向上のために適切に改行とインデントを適用しています。
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (running) return START_STICKY;
running = true;
createChannel();
startForeground(1, new Notification.Builder(this, CHANNEL_ID)
.setContentTitle("Quest KeepAwake")
.setContentText("Always-on keep-awake is active")
.setSmallIcon(android.R.drawable.ic_lock_idle_lock)
.setOngoing(true)
.build());
PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE);
if (pm != null) {
wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "QuestNavKeepAwake::StayAwake");
wakeLock.acquire();
}
applySettings();
sendProxClose();
handler.postDelayed(keepAwakeRunnable, INTERVAL_MS);
Log.i(TAG, "Always-on service started");
return START_STICKY;
}
一時的なAndroid APKビルド用PRです。成果物取得後に閉じます。既存アプリのコードは変更せず、専用ディレクトリと一時ワークフローのみ追加しています。