1 /* 2 * Copyright (C) 2017 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.view.inputmethod.cts.util; 18 19 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED; 20 21 import android.app.Activity; 22 import android.content.Intent; 23 import android.os.Bundle; 24 import android.view.View; 25 import android.view.Window; 26 import android.view.WindowManager; 27 28 import androidx.annotation.AnyThread; 29 import androidx.annotation.NonNull; 30 import androidx.annotation.UiThread; 31 import androidx.test.InstrumentationRegistry; 32 33 import java.util.concurrent.atomic.AtomicBoolean; 34 import java.util.concurrent.atomic.AtomicReference; 35 import java.util.function.Function; 36 37 public final class TestActivity extends Activity { 38 39 private static final AtomicReference<Function<TestActivity, View>> sInitializer = 40 new AtomicReference<>(); 41 42 private Function<TestActivity, View> mInitializer = null; 43 44 private AtomicBoolean mIgnoreBackKey = new AtomicBoolean(); 45 46 private long mOnBackPressedCallCount; 47 48 /** 49 * Controls how {@link #onBackPressed()} behaves. 50 * 51 * <p>TODO: Use {@link android.app.AppComponentFactory} instead to customise the behavior of 52 * {@link TestActivity}.</p> 53 * 54 * @param ignore {@code true} when {@link TestActivity} should do nothing when 55 * {@link #onBackPressed()} is called 56 */ 57 @AnyThread setIgnoreBackKey(boolean ignore)58 public void setIgnoreBackKey(boolean ignore) { 59 mIgnoreBackKey.set(ignore); 60 } 61 62 @UiThread getOnBackPressedCallCount()63 public long getOnBackPressedCallCount() { 64 return mOnBackPressedCallCount; 65 } 66 67 /** 68 * {@inheritDoc} 69 */ 70 @Override onCreate(Bundle savedInstanceState)71 protected void onCreate(Bundle savedInstanceState) { 72 super.onCreate(savedInstanceState); 73 if (mInitializer == null) { 74 mInitializer = sInitializer.get(); 75 } 76 // Currently SOFT_INPUT_STATE_UNSPECIFIED isn't appropriate for CTS test because there is no 77 // clear spec about how it behaves. In order to make our tests deterministic, currently we 78 // must use SOFT_INPUT_STATE_UNCHANGED. 79 // TODO(Bug 77152727): Remove the following code once we define how 80 // SOFT_INPUT_STATE_UNSPECIFIED actually behaves. 81 setSoftInputState(SOFT_INPUT_STATE_UNCHANGED); 82 setContentView(mInitializer.apply(this)); 83 } 84 85 /** 86 * {@inheritDoc} 87 */ 88 @Override onBackPressed()89 public void onBackPressed() { 90 ++mOnBackPressedCallCount; 91 if (mIgnoreBackKey.get()) { 92 return; 93 } 94 super.onBackPressed(); 95 } 96 97 /** 98 * Launches {@link TestActivity} with the given initialization logic for content view. 99 * 100 * <p>As long as you are using {@link androidx.test.runner.AndroidJUnitRunner}, the test 101 * runner automatically calls {@link Activity#finish()} for the {@link Activity} launched when 102 * the test finished. You do not need to explicitly call {@link Activity#finish()}.</p> 103 * 104 * @param activityInitializer initializer to supply {@link View} to be passed to 105 * {@link Activity#setContentView(View)} 106 * @return {@link TestActivity} launched 107 */ startSync( @onNull Function<TestActivity, View> activityInitializer)108 public static TestActivity startSync( 109 @NonNull Function<TestActivity, View> activityInitializer) { 110 return startSync(activityInitializer, 0 /* noAnimation */); 111 } 112 113 /** 114 * Launches {@link TestActivity} with the given initialization logic for content view. 115 * 116 * <p>As long as you are using {@link androidx.test.runner.AndroidJUnitRunner}, the test 117 * runner automatically calls {@link Activity#finish()} for the {@link Activity} launched when 118 * the test finished. You do not need to explicitly call {@link Activity#finish()}.</p> 119 * 120 * @param activityInitializer initializer to supply {@link View} to be passed to 121 * {@link Activity#setContentView(View)} 122 * @param additionalFlags flags to be set to {@link Intent#setFlags(int)} 123 * @return {@link TestActivity} launched 124 */ startSync( @onNull Function<TestActivity, View> activityInitializer, int additionalFlags)125 public static TestActivity startSync( 126 @NonNull Function<TestActivity, View> activityInitializer, 127 int additionalFlags) { 128 sInitializer.set(activityInitializer); 129 final Intent intent = new Intent() 130 .setAction(Intent.ACTION_MAIN) 131 .setClass(InstrumentationRegistry.getContext(), TestActivity.class) 132 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) 133 .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK) 134 .addFlags(additionalFlags); 135 return (TestActivity) InstrumentationRegistry 136 .getInstrumentation().startActivitySync(intent); 137 } 138 139 /** 140 * Updates {@link WindowManager.LayoutParams#softInputMode}. 141 * 142 * @param newState One of {@link WindowManager.LayoutParams#SOFT_INPUT_STATE_UNSPECIFIED}, 143 * {@link WindowManager.LayoutParams#SOFT_INPUT_STATE_UNCHANGED}, 144 * {@link WindowManager.LayoutParams#SOFT_INPUT_STATE_HIDDEN}, 145 * {@link WindowManager.LayoutParams#SOFT_INPUT_STATE_ALWAYS_HIDDEN}, 146 * {@link WindowManager.LayoutParams#SOFT_INPUT_STATE_VISIBLE}, 147 * {@link WindowManager.LayoutParams#SOFT_INPUT_STATE_ALWAYS_VISIBLE} 148 */ setSoftInputState(int newState)149 private void setSoftInputState(int newState) { 150 final Window window = getWindow(); 151 final int currentSoftInputMode = window.getAttributes().softInputMode; 152 final int newSoftInputMode = 153 (currentSoftInputMode & ~WindowManager.LayoutParams.SOFT_INPUT_MASK_STATE) 154 | newState; 155 window.setSoftInputMode(newSoftInputMode); 156 } 157 } 158