1 /* 2 * Copyright (C) 2023 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 com.android.sdksandboxcode_1; 18 19 import static android.widget.LinearLayout.VERTICAL; 20 21 import static com.android.sdksandbox.flags.Flags.sandboxActivitySdkBasedContext; 22 23 import android.annotation.NonNull; 24 import android.annotation.Nullable; 25 import android.app.Activity; 26 import android.app.Application; 27 import android.content.Context; 28 import android.content.Intent; 29 import android.content.pm.ActivityInfo; 30 import android.hardware.Sensor; 31 import android.hardware.SensorEvent; 32 import android.hardware.SensorEventListener; 33 import android.hardware.SensorManager; 34 import android.net.Uri; 35 import android.os.Bundle; 36 import android.view.View; 37 import android.view.ViewGroup; 38 import android.widget.Button; 39 import android.widget.LinearLayout; 40 import android.widget.LinearLayout.LayoutParams; 41 import android.widget.TextView; 42 import android.widget.Toast; 43 import android.window.OnBackInvokedCallback; 44 import android.window.OnBackInvokedDispatcher; 45 46 public class ActivityHandler implements SensorEventListener { 47 private static final String DISABLE_BACK_NAVIGATION = "Disable Back Navigation"; 48 private static final String ENABLE_BACK_NAVIGATION = "Enable Back Navigation"; 49 private static final String ORIENTATION_PORTRAIT = "Set SCREEN_ORIENTATION_PORTRAIT"; 50 private static final String ORIENTATION_LANDSCAPE = "Set SCREEN_ORIENTATION_LANDSCAPE"; 51 private static final String DESTROY_ACTIVITY = "Destroy Activity"; 52 private static final String OPEN_LANDING_PAGE = "Open Landing Page"; 53 private final Activity mActivity; 54 private final View mView; 55 private Button mBackButton; 56 private Button mOrientationPortraitButton; 57 private Button mOrientationLandscapeButton; 58 private Button mDestroyButton; 59 private Button mOpenLandingPage; 60 private TextView mAxisX; 61 private final SensorManager mSensorManager; 62 private final Sensor mGyroSensor; 63 private final boolean mIsCustomizedSdkContextEnabled; 64 ActivityHandler( Activity activity, Context sdkContext, View view, boolean isCustomizedSdkContextEnabled)65 public ActivityHandler( 66 Activity activity, 67 Context sdkContext, 68 View view, 69 boolean isCustomizedSdkContextEnabled) { 70 mActivity = activity; 71 mView = view; 72 mSensorManager = sdkContext.getSystemService(SensorManager.class); 73 mGyroSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE); 74 mIsCustomizedSdkContextEnabled = isCustomizedSdkContextEnabled; 75 } 76 buildLayout()77 public void buildLayout() { 78 final LinearLayout mediaViewContainer; 79 if (sandboxActivitySdkBasedContext() && mIsCustomizedSdkContextEnabled) { 80 makeToast("Context flags are enabled, building layout from resources"); 81 mediaViewContainer = buildLayoutFromResources(); 82 } else { 83 makeToast("Context flags are disabled, building layout programmatically"); 84 mediaViewContainer = buildLayoutProgrammatically(); 85 } 86 87 if (mView.getParent() != null) { 88 ((ViewGroup) mView.getParent()).removeView(mView); 89 } 90 mView.setLayoutParams( 91 new ViewGroup.LayoutParams( 92 LinearLayout.LayoutParams.MATCH_PARENT, 93 LinearLayout.LayoutParams.MATCH_PARENT)); 94 mediaViewContainer.addView(mView); 95 96 registerBackEnableButton(); 97 registerPortraitButton(); 98 registerLandscapeButton(); 99 registerDestroyActivityButton(); 100 registerOpenLandingPageButton(); 101 102 registerLifecycleListener(); 103 } 104 105 /** 106 * Using the SDK resources to build the activity layout. 107 * 108 * @return the container layout for the media view (WebView or media) 109 */ buildLayoutFromResources()110 public LinearLayout buildLayoutFromResources() { 111 mActivity.setContentView(R.layout.sdk_activity_layout); 112 113 mBackButton = mActivity.findViewById(R.id.back_button); 114 mOrientationPortraitButton = mActivity.findViewById(R.id.portrait_orientation_button); 115 mOrientationLandscapeButton = mActivity.findViewById(R.id.landscape_orientation_button); 116 mDestroyButton = mActivity.findViewById(R.id.destroy_activity_button); 117 mOpenLandingPage = mActivity.findViewById(R.id.landing_page_button); 118 mAxisX = mActivity.findViewById(R.id.x_axis_button); 119 return mActivity.findViewById(R.id.media_view_container); 120 } 121 122 /** 123 * Building the activity layout programmatically. 124 * 125 * @return the container layout for the media view (WebView or media) 126 */ buildLayoutProgrammatically()127 public LinearLayout buildLayoutProgrammatically() { 128 LinearLayout mainLayout = new LinearLayout(mActivity); 129 mainLayout.setOrientation(VERTICAL); 130 mainLayout.setLayoutParams( 131 new ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); 132 133 mBackButton = new Button(mActivity); 134 mBackButton.setText(DISABLE_BACK_NAVIGATION); 135 mainLayout.addView(mBackButton); 136 137 mOrientationPortraitButton = new Button(mActivity); 138 mOrientationPortraitButton.setText(ORIENTATION_PORTRAIT); 139 mainLayout.addView(mOrientationPortraitButton); 140 141 mOrientationLandscapeButton = new Button(mActivity); 142 mOrientationLandscapeButton.setText(ORIENTATION_LANDSCAPE); 143 mainLayout.addView(mOrientationLandscapeButton); 144 145 mDestroyButton = new Button(mActivity); 146 mDestroyButton.setText(DESTROY_ACTIVITY); 147 mainLayout.addView(mDestroyButton); 148 149 mOpenLandingPage = new Button(mActivity); 150 mOpenLandingPage.setText(OPEN_LANDING_PAGE); 151 mainLayout.addView(mOpenLandingPage); 152 153 mAxisX = new TextView(mActivity); 154 mainLayout.addView(mAxisX); 155 156 mActivity.setContentView(mainLayout); 157 return mainLayout; 158 } 159 registerBackEnableButton()160 private void registerBackEnableButton() { 161 final BackNavigationDisabler disabler = new BackNavigationDisabler(mActivity, mBackButton); 162 mBackButton.setOnClickListener(v -> disabler.toggle()); 163 } 164 registerPortraitButton()165 private void registerPortraitButton() { 166 mOrientationPortraitButton.setOnClickListener( 167 v -> mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)); 168 } 169 registerLandscapeButton()170 private void registerLandscapeButton() { 171 mOrientationLandscapeButton.setOnClickListener( 172 v -> mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE)); 173 } 174 registerDestroyActivityButton()175 private void registerDestroyActivityButton() { 176 mDestroyButton.setOnClickListener(v -> mActivity.finish()); 177 } 178 registerOpenLandingPageButton()179 private void registerOpenLandingPageButton() { 180 mOpenLandingPage.setOnClickListener( 181 v -> { 182 Intent visitUrl = new Intent(Intent.ACTION_VIEW); 183 visitUrl.setData(Uri.parse("https://www.google.com")); 184 visitUrl.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 185 mActivity.startActivity(visitUrl); 186 }); 187 } 188 registerLifecycleListener()189 private void registerLifecycleListener() { 190 ActivityHandler activityHandler = this; 191 mActivity.registerActivityLifecycleCallbacks( 192 new Application.ActivityLifecycleCallbacks() { 193 @Override 194 public void onActivityCreated( 195 @NonNull Activity activity, @Nullable Bundle savedInstanceState) { 196 makeToast("Activity on create!"); 197 } 198 199 @Override 200 public void onActivityStarted(@NonNull Activity activity) { 201 makeToast("Activity on start!"); 202 } 203 204 @Override 205 public void onActivityResumed(@NonNull Activity activity) { 206 makeToast("Activity on resume!"); 207 mSensorManager.registerListener( 208 activityHandler, mGyroSensor, SensorManager.SENSOR_DELAY_NORMAL); 209 } 210 211 @Override 212 public void onActivityPaused(@NonNull Activity activity) { 213 makeToast("Activity on pause!"); 214 mSensorManager.unregisterListener(activityHandler); 215 } 216 217 @Override 218 public void onActivityStopped(@NonNull Activity activity) { 219 makeToast("Activity on stop!"); 220 } 221 222 @Override 223 public void onActivitySaveInstanceState( 224 @NonNull Activity activity, @NonNull Bundle outState) { 225 makeToast("Activity on saveInstanceState!"); 226 } 227 228 @Override 229 public void onActivityDestroyed(@NonNull Activity activity) { 230 makeToast("Activity on destroy!"); 231 } 232 }); 233 } 234 235 @Override onSensorChanged(SensorEvent event)236 public void onSensorChanged(SensorEvent event) { 237 mAxisX.setText("axisX = %s".formatted(event.values[0])); 238 } 239 240 @Override onAccuracyChanged(Sensor sensor, int accuracy)241 public void onAccuracyChanged(Sensor sensor, int accuracy) {} 242 243 private class BackNavigationDisabler { 244 private final OnBackInvokedDispatcher mDispatcher; 245 246 private final OnBackInvokedCallback mBackNavigationDisablingCallback; 247 248 private boolean mBackNavigationDisabled; // default is back enabled. 249 250 private final Button mBackButton; 251 BackNavigationDisabler(Activity activity, Button backButton)252 BackNavigationDisabler(Activity activity, Button backButton) { 253 mDispatcher = activity.getOnBackInvokedDispatcher(); 254 mBackNavigationDisablingCallback = () -> makeToast("Can not go back!"); 255 mBackButton = backButton; 256 } 257 toggle()258 public synchronized void toggle() { 259 if (mBackNavigationDisabled) { 260 mDispatcher.unregisterOnBackInvokedCallback(mBackNavigationDisablingCallback); 261 mBackButton.setText(DISABLE_BACK_NAVIGATION); 262 } else { 263 mDispatcher.registerOnBackInvokedCallback( 264 OnBackInvokedDispatcher.PRIORITY_DEFAULT, mBackNavigationDisablingCallback); 265 mBackButton.setText(ENABLE_BACK_NAVIGATION); 266 } 267 mBackNavigationDisabled = !mBackNavigationDisabled; 268 } 269 } 270 makeToast(String message)271 private void makeToast(String message) { 272 mActivity.runOnUiThread( 273 () -> Toast.makeText(mActivity, message, Toast.LENGTH_SHORT).show()); 274 } 275 } 276