1 /* 2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.service.games; 18 19 import android.content.Context; 20 import android.graphics.Color; 21 import android.graphics.Rect; 22 import android.graphics.Region; 23 import android.service.games.testing.GameSessionEventInfo; 24 import android.service.games.testing.OnSystemBarVisibilityChangedInfo; 25 import android.view.Gravity; 26 import android.view.View; 27 import android.view.ViewGroup; 28 import android.widget.FrameLayout; 29 import android.widget.TextView; 30 31 import androidx.annotation.GuardedBy; 32 import androidx.annotation.Nullable; 33 34 import java.util.ArrayList; 35 import java.util.HashSet; 36 import java.util.Set; 37 38 39 /** 40 * Test implementation of {@link GameSessionService}. 41 */ 42 public final class TestGameSessionService extends GameSessionService { 43 private static final Object sLock = new Object(); 44 @GuardedBy("sLock") 45 private static final Set<String> sActiveSessions = new HashSet<>(); 46 @GuardedBy("sLock") 47 @Nullable 48 private static TestGameSession sFocusedSession; 49 @GuardedBy("sLock") 50 @Nullable 51 private static ArrayList<GameSessionEventInfo> sGameSessionEventHistory = new ArrayList<>(); 52 53 @Override onNewSession(CreateGameSessionRequest createGameSessionRequest)54 public GameSession onNewSession(CreateGameSessionRequest createGameSessionRequest) { 55 return new TestGameSession(this, createGameSessionRequest.getGamePackageName(), 56 createGameSessionRequest.getTaskId()); 57 } 58 reset()59 static void reset() { 60 synchronized (sLock) { 61 sActiveSessions.clear(); 62 sFocusedSession = null; 63 sGameSessionEventHistory.clear(); 64 } 65 } 66 getActiveSessions()67 static Set<String> getActiveSessions() { 68 synchronized (sLock) { 69 return sActiveSessions; 70 } 71 } 72 getGameSessionEventHistory()73 static ArrayList<GameSessionEventInfo> getGameSessionEventHistory() { 74 synchronized (sLock) { 75 return sGameSessionEventHistory; 76 } 77 } 78 79 @Nullable getFocusedSession()80 static TestGameSession getFocusedSession() { 81 synchronized (sLock) { 82 return sFocusedSession; 83 } 84 } 85 86 static final class TestGameSession extends GameSession { 87 private final Context mContext; 88 private final String mPackageName; 89 private final int mTaskId; 90 private final Rect mTouchableBounds = new Rect(); 91 private final FrameLayout mRootView; 92 private final OnSystemBarVisibilityChangedInfo mOnSystemBarVisibilityChangedInfo; 93 TestGameSession(Context context, String packageName, int taskId)94 private TestGameSession(Context context, String packageName, int taskId) { 95 mContext = context; 96 mPackageName = packageName; 97 mTaskId = taskId; 98 mRootView = new FrameLayout(context); 99 mOnSystemBarVisibilityChangedInfo = new OnSystemBarVisibilityChangedInfo(); 100 } 101 getPackageName()102 String getPackageName() { 103 return mPackageName; 104 } 105 getTaskId()106 int getTaskId() { 107 return mTaskId; 108 } 109 getTouchableBounds()110 Rect getTouchableBounds() { 111 return new Rect(mTouchableBounds); 112 } 113 getOnSystemBarVisibilityChangedInfo()114 OnSystemBarVisibilityChangedInfo getOnSystemBarVisibilityChangedInfo() { 115 return mOnSystemBarVisibilityChangedInfo; 116 } 117 118 @Override onCreate()119 public void onCreate() { 120 synchronized (sLock) { 121 sActiveSessions.add(mPackageName); 122 sGameSessionEventHistory.add( 123 GameSessionEventInfo.create( 124 mPackageName, 125 mTaskId, 126 GameSessionEventInfo.GAME_SESSION_EVENT_CREATED)); 127 } 128 129 final TextView textView = new TextView(mContext); 130 textView.setText("Overlay was rendered on: " + mPackageName); 131 textView.setBackgroundColor(Color.MAGENTA); 132 final FrameLayout.LayoutParams textViewLayoutParams = 133 new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, 134 ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.CENTER); 135 textViewLayoutParams.leftMargin = 100; 136 textViewLayoutParams.rightMargin = 100; 137 textView.setLayoutParams(textViewLayoutParams); 138 mRootView.addView(textView); 139 140 setTaskOverlayView(mRootView, 141 new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 142 ViewGroup.LayoutParams.MATCH_PARENT)); 143 144 mRootView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() { 145 @Override 146 public void onViewAttachedToWindow(View v) { 147 v.getRootSurfaceControl().setTouchableRegion(new Region()); 148 v.removeOnAttachStateChangeListener(this); 149 } 150 151 @Override 152 public void onViewDetachedFromWindow(View v) { 153 } 154 }); 155 textView.addOnLayoutChangeListener( 156 (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> { 157 boolean isViewVisible = v.getGlobalVisibleRect(mTouchableBounds); 158 if (!isViewVisible) { 159 mTouchableBounds.setEmpty(); 160 } 161 v.getRootSurfaceControl().setTouchableRegion(new Region(mTouchableBounds)); 162 }); 163 } 164 165 @Override onTransientSystemBarVisibilityFromRevealGestureChanged( boolean visibleDueToGesture)166 public void onTransientSystemBarVisibilityFromRevealGestureChanged( 167 boolean visibleDueToGesture) { 168 if (visibleDueToGesture) { 169 mOnSystemBarVisibilityChangedInfo.incrementTimesShown(); 170 } else { 171 mOnSystemBarVisibilityChangedInfo.incrementTimesHidden(); 172 } 173 } 174 175 @Override onGameTaskFocusChanged(boolean focused)176 public void onGameTaskFocusChanged(boolean focused) { 177 if (focused) { 178 synchronized (sLock) { 179 sFocusedSession = this; 180 } 181 return; 182 } 183 184 synchronized (sLock) { 185 if (sFocusedSession == this) { 186 sFocusedSession = null; 187 } 188 } 189 } 190 191 @Override onDestroy()192 public void onDestroy() { 193 synchronized (sLock) { 194 sActiveSessions.remove(mPackageName); 195 sGameSessionEventHistory.add( 196 GameSessionEventInfo.create( 197 mPackageName, 198 mTaskId, 199 GameSessionEventInfo.GAME_SESSION_EVENT_DESTROYED)); 200 } 201 } 202 } 203 } 204