Skip to content

Commit

Permalink
Merge pull request #38 from anoosragh69/14
Browse files Browse the repository at this point in the history
Add support for GameSpace
  • Loading branch information
ReveRTX committed May 13, 2024
2 parents 94d03c0 + 7696bd5 commit de98f27
Show file tree
Hide file tree
Showing 8 changed files with 267 additions and 0 deletions.
23 changes: 23 additions & 0 deletions core/java/android/provider/Settings.java
Original file line number Diff line number Diff line change
Expand Up @@ -6341,6 +6341,20 @@ public static void setShowGTalkServiceStatusForUser(ContentResolver cr, boolean
*/
public static final String QS_BT_AUTO_ON = "qs_bt_auto_on";

/**
* GameSpace: List of added games by user
* @hide
*/
@Readable
public static final String GAMESPACE_GAME_LIST = "gamespace_game_list";

/**
* GameSpace: Whether fullscreen intent will be suppressed while in game session
* @hide
*/
@Readable
public static final String GAMESPACE_SUPPRESS_FULLSCREEN_INTENT = "gamespace_suppress_fullscreen_intent";

/**
* Keys we no longer back up under the current schema, but want to continue to
* process when restoring historical backup datasets.
Expand Down Expand Up @@ -12244,6 +12258,15 @@ public static boolean putFloatForUser(ContentResolver cr, String name, float val
*/
public static final String SHOW_FPS_OVERLAY = "show_fps_overlay";

/**
* Our GameSpace can't write to device_config directly [GTS]
* Use this as intermediate to pass device_config property
* from our GameSpace to com.android.server.app.GameManagerService
* so we can set the device_config property from there.
* @hide
*/
public static final String GAME_OVERLAY = "game_overlay";

/**
* Keys we no longer back up under the current schema, but want to continue to
* process when restoring historical backup datasets.
Expand Down
4 changes: 4 additions & 0 deletions core/res/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -844,6 +844,10 @@
<!-- Used for long press power torch feature - automatic turn off on timeout -->
<protected-broadcast android:name="com.android.server.policy.PhoneWindowManager.ACTION_TORCH_OFF" />

<!-- GameSpace -->
<protected-broadcast android:name="io.chaldeaprjkt.gamespace.action.GAME_START" />
<protected-broadcast android:name="io.chaldeaprjkt.gamespace.action.GAME_STOP" />

<!-- ====================================================================== -->
<!-- RUNTIME PERMISSIONS -->
<!-- ====================================================================== -->
Expand Down
3 changes: 3 additions & 0 deletions packages/SystemUI/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,9 @@
<!-- Kill button in notification guts -->
<uses-permission android:name="android.permission.FORCE_STOP_PACKAGES" />

<!-- To broadcast status of the GameSpaceManager -->
<uses-permission android:name="android.permission.MANAGE_GAME_MODE" />

<!-- DataSwitch tile -->
<uses-permission android:name="android.permission.WRITE_APN_SETTINGS" />

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
import com.android.systemui.qs.QSPanelController;
import com.android.systemui.shared.system.RemoteAnimationRunnerCompat;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.policy.GameSpaceManager;
import com.android.systemui.util.Compile;

import java.io.PrintWriter;
Expand Down Expand Up @@ -336,4 +337,6 @@ ActivityLaunchAnimator.Controller getAnimatorControllerFromNotification(
void updateDismissAllButton();

void setBlockedGesturalNavigation(boolean blocked);

GameSpaceManager getGameSpaceManager();
}
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,7 @@
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
import com.android.systemui.statusbar.policy.ExtensionController;
import com.android.systemui.statusbar.policy.GameSpaceManager;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
Expand Down Expand Up @@ -486,6 +487,8 @@ public QSPanelController getQSPanelController() {
private final TunerService mTunerService;
private final ActivityStarter mActivityStarter;

private GameSpaceManager mGameSpaceManager;

private final DisplayMetrics mDisplayMetrics;

// XXX: gesture research
Expand Down Expand Up @@ -842,6 +845,8 @@ public CentralSurfacesImpl(
mActivityIntentHelper = new ActivityIntentHelper(mContext);
mActivityLaunchAnimator = activityLaunchAnimator;

mGameSpaceManager = new GameSpaceManager(mContext, mKeyguardStateController);

// TODO(b/190746471): Find a better home for this.
DateTimeView.setReceiverHandler(timeTickHandler);

Expand Down Expand Up @@ -1452,6 +1457,7 @@ protected void registerBroadcastReceiver() {
filter.addAction(Intent.ACTION_SCREEN_OFF);
filter.addAction(lineageos.content.Intent.ACTION_SCREEN_CAMERA_GESTURE);
mBroadcastDispatcher.registerReceiver(mBroadcastReceiver, filter, null, UserHandle.ALL);
mGameSpaceManager.observe();
}

@Override
Expand Down Expand Up @@ -1497,6 +1503,11 @@ public void setBlockedGesturalNavigation(boolean blocked) {
}
}

@Override
public GameSpaceManager getGameSpaceManager() {
return mGameSpaceManager;
}

protected QS createDefaultQSFragment() {
return mFragmentService
.getFragmentHostManager(getNotificationShadeWindowView())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowDragController;
import com.android.systemui.statusbar.notification.row.OnUserInteractionCallback;
import com.android.systemui.statusbar.policy.GameSpaceManager;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.HeadsUpUtil;
import com.android.systemui.statusbar.policy.KeyguardStateController;
Expand Down Expand Up @@ -122,6 +123,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
private final MetricsLogger mMetricsLogger;
private final StatusBarNotificationActivityStarterLogger mLogger;

private final CentralSurfaces mCentralSurfaces;
private final NotificationPresenter mPresenter;
private final ShadeViewController mShadeViewController;
private final NotificationShadeWindowController mNotificationShadeWindowController;
Expand Down Expand Up @@ -158,6 +160,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
MetricsLogger metricsLogger,
StatusBarNotificationActivityStarterLogger logger,
OnUserInteractionCallback onUserInteractionCallback,
CentralSurfaces centralSurfaces,
NotificationPresenter presenter,
ShadeViewController shadeViewController,
NotificationShadeWindowController notificationShadeWindowController,
Expand Down Expand Up @@ -191,6 +194,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
mShadeAnimationInteractor = shadeAnimationInteractor;
mMetricsLogger = metricsLogger;
mLogger = logger;
mCentralSurfaces = centralSurfaces;
mOnUserInteractionCallback = onUserInteractionCallback;
mPresenter = presenter;
mShadeViewController = shadeViewController;
Expand Down Expand Up @@ -576,6 +580,11 @@ private void removeHunAfterClick(ExpandableNotificationRow row) {

@VisibleForTesting
void launchFullScreenIntent(NotificationEntry entry) {
GameSpaceManager gameSpace = mCentralSurfaces.getGameSpaceManager();
if (gameSpace != null && gameSpace.shouldSuppressFullScreenIntent()) {
return;
}

// Skip if device is in VR mode.
if (mPresenter.isDeviceInVrMode()) {
mLogger.logFullScreenIntentSuppressedByVR(entry);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
/*
* Copyright (C) 2021 Chaldeaprjkt
* Copyright (C) 2022-2024 crDroid Android Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/

package com.android.systemui.statusbar.policy

import android.app.ActivityTaskManager
import android.content.BroadcastReceiver
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.os.Handler
import android.os.Looper
import android.os.Message
import android.os.PowerManager
import android.os.RemoteException
import android.os.UserHandle
import android.provider.Settings
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.shared.system.TaskStackChangeListener
import com.android.systemui.shared.system.TaskStackChangeListeners

import java.util.Arrays
import javax.inject.Inject

@SysUISingleton
class GameSpaceManager @Inject constructor(
private val context: Context,
private val keyguardStateController: KeyguardStateController,
) {
private val handler by lazy { GameSpaceHandler(Looper.getMainLooper()) }
private val taskManager by lazy { ActivityTaskManager.getService() }

private var activeGame: String? = null
private var isRegistered = false

private val taskStackChangeListener = object : TaskStackChangeListener {
override fun onTaskStackChanged() {
handler.sendEmptyMessage(MSG_UPDATE_FOREGROUND_APP)
}
}

private val interactivityReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
when (intent?.action) {
Intent.ACTION_SCREEN_OFF -> {
activeGame = null
handler.sendEmptyMessage(MSG_DISPATCH_FOREGROUND_APP)
}
}
}
}

private val keyguardStateCallback = object : KeyguardStateController.Callback {
override fun onKeyguardShowingChanged() {
if (keyguardStateController.isShowing) return
handler.sendEmptyMessage(MSG_UPDATE_FOREGROUND_APP)
}
}

private inner class GameSpaceHandler(looper: Looper) : Handler(looper, null, true) {
override fun handleMessage(msg: Message) {
when (msg.what) {
MSG_UPDATE_FOREGROUND_APP -> checkForegroundApp()
MSG_DISPATCH_FOREGROUND_APP -> dispatchForegroundApp()
}
}
}

private fun checkForegroundApp() {
try {
val info = taskManager.focusedRootTaskInfo
info?.topActivity ?: return
val packageName = info.topActivity?.packageName
activeGame = checkGameList(packageName)
handler.sendEmptyMessage(MSG_DISPATCH_FOREGROUND_APP)
} catch (e: RemoteException) {
}
}

private fun dispatchForegroundApp() {
val pm = context.getSystemService(Context.POWER_SERVICE) as PowerManager
if (!pm.isInteractive && activeGame != null) return
val action = if (activeGame != null) ACTION_GAME_START else ACTION_GAME_STOP
Intent(action).apply {
setPackage(GAMESPACE_PACKAGE)
component = ComponentName.unflattenFromString(RECEIVER_CLASS)
putExtra(EXTRA_CALLER_NAME, context.packageName)
if (activeGame != null) putExtra(EXTRA_ACTIVE_GAME, activeGame)
addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
or Intent.FLAG_RECEIVER_FOREGROUND
or Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND)
context.sendBroadcastAsUser(this, UserHandle.CURRENT,
android.Manifest.permission.MANAGE_GAME_MODE)
}
}

fun observe() {
val taskStackChangeListeners = TaskStackChangeListeners.getInstance();
if (isRegistered) {
taskStackChangeListeners.unregisterTaskStackListener(taskStackChangeListener)
}
taskStackChangeListeners.registerTaskStackListener(taskStackChangeListener)
isRegistered = true;
handler.sendEmptyMessage(MSG_UPDATE_FOREGROUND_APP)
context.registerReceiver(interactivityReceiver, IntentFilter().apply {
addAction(Intent.ACTION_SCREEN_OFF)
}, Context.RECEIVER_NOT_EXPORTED)
keyguardStateController.addCallback(keyguardStateCallback)
}

fun unobserve() {
val taskStackChangeListeners = TaskStackChangeListeners.getInstance();
if (!isRegistered) {
taskStackChangeListeners.unregisterTaskStackListener(taskStackChangeListener)
}
isRegistered = false;
context.unregisterReceiver(interactivityReceiver)
keyguardStateController.removeCallback(keyguardStateCallback)
}

fun isGameActive() = activeGame != null

fun shouldSuppressFullScreenIntent() =
Settings.System.getIntForUser(
context.contentResolver,
Settings.System.GAMESPACE_SUPPRESS_FULLSCREEN_INTENT, 0,
UserHandle.USER_CURRENT) == 1 && isGameActive()

private fun checkGameList(packageName: String?): String? {
packageName ?: return null
val games = Settings.System.getStringForUser(
context.contentResolver,
Settings.System.GAMESPACE_GAME_LIST,
UserHandle.USER_CURRENT)

if (games.isNullOrEmpty())
return null

return games.split(";")
.map { it.split("=").first() }
.firstOrNull { it == packageName }
}

companion object {
private const val ACTION_GAME_START = "io.chaldeaprjkt.gamespace.action.GAME_START"
private const val ACTION_GAME_STOP = "io.chaldeaprjkt.gamespace.action.GAME_STOP"
private const val GAMESPACE_PACKAGE = "io.chaldeaprjkt.gamespace"
private const val RECEIVER_CLASS = "io.chaldeaprjkt.gamespace/.gamebar.GameBroadcastReceiver"
private const val EXTRA_CALLER_NAME = "source"
private const val EXTRA_ACTIVE_GAME = "package_name"
private const val MSG_UPDATE_FOREGROUND_APP = 0
private const val MSG_DISPATCH_FOREGROUND_APP = 1
}
}
Loading

0 comments on commit de98f27

Please sign in to comment.