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