Loading android/app/src/com/android/bluetooth/AlertActivity.java +8 −0 Original line number Diff line number Diff line Loading @@ -27,6 +27,9 @@ import android.os.Bundle; import android.view.ViewGroup; import android.view.Window; import android.view.accessibility.AccessibilityEvent; import android.widget.Button; import com.android.internal.annotations.VisibleForTesting; /** * An activity that follows the visual style of an AlertDialog. Loading Loading @@ -119,6 +122,11 @@ public abstract class AlertActivity extends Activity implements DialogInterface. mAlert.getButton(identifier).setEnabled(enable); } @VisibleForTesting public Button getButton(int identifier) { return mAlert.getButton(identifier); } @Override protected void onDestroy() { if (mAlert != null) { Loading android/app/src/com/android/bluetooth/pbap/BluetoothPbapActivity.java +12 −5 Original line number Diff line number Diff line Loading @@ -54,6 +54,7 @@ import android.widget.EditText; import android.widget.TextView; import com.android.bluetooth.R; import com.android.internal.annotations.VisibleForTesting; /** * PbapActivity shows two dialogues: One for accepting incoming pbap request and Loading @@ -68,7 +69,8 @@ public class BluetoothPbapActivity extends AlertActivity private static final int BLUETOOTH_OBEX_AUTHKEY_MAX_LENGTH = 16; private static final int DIALOG_YES_NO_AUTH = 1; @VisibleForTesting static final int DIALOG_YES_NO_AUTH = 1; private static final String KEY_USER_TIMEOUT = "user_timeout"; Loading @@ -80,7 +82,8 @@ public class BluetoothPbapActivity extends AlertActivity private String mSessionKey = ""; private int mCurrentDialog; @VisibleForTesting int mCurrentDialog; private boolean mTimeout = false; Loading @@ -90,7 +93,8 @@ public class BluetoothPbapActivity extends AlertActivity private BluetoothDevice mDevice; private BroadcastReceiver mReceiver = new BroadcastReceiver() { @VisibleForTesting BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (!BluetoothPbapService.USER_CONFIRM_TIMEOUT_ACTION.equals(intent.getAction())) { Loading Loading @@ -164,7 +168,8 @@ public class BluetoothPbapActivity extends AlertActivity } } private void onPositive() { @VisibleForTesting void onPositive() { if (mCurrentDialog == DIALOG_YES_NO_AUTH) { mSessionKey = mKeyView.getText().toString(); } Loading @@ -180,7 +185,8 @@ public class BluetoothPbapActivity extends AlertActivity finish(); } private void onNegative() { @VisibleForTesting void onNegative() { if (mCurrentDialog == DIALOG_YES_NO_AUTH) { sendIntentToReceiver(BluetoothPbapService.AUTH_CANCELLED_ACTION, null, null); mKeyView.removeTextChangedListener(this); Loading @@ -199,6 +205,7 @@ public class BluetoothPbapActivity extends AlertActivity sendBroadcast(intent); } @VisibleForTesting private void onTimeout() { mTimeout = true; if (mCurrentDialog == DIALOG_YES_NO_AUTH) { Loading android/app/tests/unit/src/com/android/bluetooth/pbap/BluetoothPbapActivityTest.java 0 → 100644 +173 −0 Original line number Diff line number Diff line /* * Copyright 2022 The Android Open Source 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.bluetooth.pbap; import static android.content.DialogInterface.BUTTON_POSITIVE; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED; import static android.content.pm.PackageManager.DONT_KILL_APP; import static androidx.lifecycle.Lifecycle.State; import static androidx.lifecycle.Lifecycle.State.DESTROYED; import static androidx.lifecycle.Lifecycle.State.RESUMED; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.text.Editable; import android.text.SpannableStringBuilder; import androidx.test.core.app.ActivityScenario; import androidx.test.filters.LargeTest; import androidx.test.platform.app.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import java.util.concurrent.atomic.AtomicBoolean; @LargeTest @RunWith(AndroidJUnit4.class) public class BluetoothPbapActivityTest { Context mTargetContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); Intent mIntent; ActivityScenario<BluetoothPbapActivity> mActivityScenario; @Before public void setUp() { mIntent = new Intent(); mIntent.setClass(mTargetContext, BluetoothPbapActivity.class); mIntent.setAction(BluetoothPbapService.AUTH_CHALL_ACTION); enableActivity(true); mActivityScenario = ActivityScenario.launch(mIntent); } @After public void tearDown() throws Exception { if (mActivityScenario != null) { // Workaround for b/159805732. Without this, test hangs for 45 seconds. Thread.sleep(1_000); mActivityScenario.close(); } enableActivity(false); } @Test public void activityIsDestroyed_whenLaunchedWithoutIntentAction() throws Exception { mActivityScenario.close(); mIntent.setAction(null); mActivityScenario = ActivityScenario.launch(mIntent); assertActivityState(DESTROYED); } @Test public void onPreferenceChange_returnsTrue() throws Exception { AtomicBoolean result = new AtomicBoolean(false); mActivityScenario.onActivity(activity -> result.set( activity.onPreferenceChange(/*preference=*/null, /*newValue=*/null))); assertThat(result.get()).isTrue(); } @Test public void onPositive_finishesActivity() throws Exception { mActivityScenario.onActivity(activity -> { activity.onPositive(); }); assertActivityState(DESTROYED); } @Test public void onNegative_finishesActivity() throws Exception { mActivityScenario.onActivity(activity -> { activity.onNegative(); }); assertActivityState(DESTROYED); } @Test public void onReceiveTimeoutIntent_finishesActivity() throws Exception { Intent intent = new Intent(BluetoothPbapService.USER_CONFIRM_TIMEOUT_ACTION); mActivityScenario.onActivity(activity -> { activity.mReceiver.onReceive(activity, intent); }); assertActivityState(DESTROYED); } @Test public void afterTextChanged() throws Exception { Editable editable = new SpannableStringBuilder("An editable text"); AtomicBoolean result = new AtomicBoolean(false); mActivityScenario.onActivity(activity -> { activity.afterTextChanged(editable); result.set(activity.getButton(BUTTON_POSITIVE).isEnabled()); }); assertThat(result.get()).isTrue(); } // TODO: Test onSaveInstanceState and onRestoreInstanceState. // Note: Activity.recreate() fails. The Activity just finishes itself when recreated. // Fix the bug and test those methods. @Test public void emptyMethods_doesNotThrowException() throws Exception { try { mActivityScenario.onActivity(activity -> { activity.beforeTextChanged(null, 0, 0, 0); activity.onTextChanged(null, 0, 0, 0); }); } catch (Exception ex) { assertWithMessage("Exception should not happen!").fail(); } } private void assertActivityState(State state) throws Exception { // TODO: Change this into an event driven systems Thread.sleep(3_000); assertThat(mActivityScenario.getState()).isEqualTo(state); } private void enableActivity(boolean enable) { int enabledState = enable ? COMPONENT_ENABLED_STATE_ENABLED : COMPONENT_ENABLED_STATE_DEFAULT; mTargetContext.getPackageManager().setApplicationEnabledSetting( mTargetContext.getPackageName(), enabledState, DONT_KILL_APP); ComponentName activityName = new ComponentName(mTargetContext, BluetoothPbapActivity.class); mTargetContext.getPackageManager().setComponentEnabledSetting( activityName, enabledState, DONT_KILL_APP); } } Loading
android/app/src/com/android/bluetooth/AlertActivity.java +8 −0 Original line number Diff line number Diff line Loading @@ -27,6 +27,9 @@ import android.os.Bundle; import android.view.ViewGroup; import android.view.Window; import android.view.accessibility.AccessibilityEvent; import android.widget.Button; import com.android.internal.annotations.VisibleForTesting; /** * An activity that follows the visual style of an AlertDialog. Loading Loading @@ -119,6 +122,11 @@ public abstract class AlertActivity extends Activity implements DialogInterface. mAlert.getButton(identifier).setEnabled(enable); } @VisibleForTesting public Button getButton(int identifier) { return mAlert.getButton(identifier); } @Override protected void onDestroy() { if (mAlert != null) { Loading
android/app/src/com/android/bluetooth/pbap/BluetoothPbapActivity.java +12 −5 Original line number Diff line number Diff line Loading @@ -54,6 +54,7 @@ import android.widget.EditText; import android.widget.TextView; import com.android.bluetooth.R; import com.android.internal.annotations.VisibleForTesting; /** * PbapActivity shows two dialogues: One for accepting incoming pbap request and Loading @@ -68,7 +69,8 @@ public class BluetoothPbapActivity extends AlertActivity private static final int BLUETOOTH_OBEX_AUTHKEY_MAX_LENGTH = 16; private static final int DIALOG_YES_NO_AUTH = 1; @VisibleForTesting static final int DIALOG_YES_NO_AUTH = 1; private static final String KEY_USER_TIMEOUT = "user_timeout"; Loading @@ -80,7 +82,8 @@ public class BluetoothPbapActivity extends AlertActivity private String mSessionKey = ""; private int mCurrentDialog; @VisibleForTesting int mCurrentDialog; private boolean mTimeout = false; Loading @@ -90,7 +93,8 @@ public class BluetoothPbapActivity extends AlertActivity private BluetoothDevice mDevice; private BroadcastReceiver mReceiver = new BroadcastReceiver() { @VisibleForTesting BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (!BluetoothPbapService.USER_CONFIRM_TIMEOUT_ACTION.equals(intent.getAction())) { Loading Loading @@ -164,7 +168,8 @@ public class BluetoothPbapActivity extends AlertActivity } } private void onPositive() { @VisibleForTesting void onPositive() { if (mCurrentDialog == DIALOG_YES_NO_AUTH) { mSessionKey = mKeyView.getText().toString(); } Loading @@ -180,7 +185,8 @@ public class BluetoothPbapActivity extends AlertActivity finish(); } private void onNegative() { @VisibleForTesting void onNegative() { if (mCurrentDialog == DIALOG_YES_NO_AUTH) { sendIntentToReceiver(BluetoothPbapService.AUTH_CANCELLED_ACTION, null, null); mKeyView.removeTextChangedListener(this); Loading @@ -199,6 +205,7 @@ public class BluetoothPbapActivity extends AlertActivity sendBroadcast(intent); } @VisibleForTesting private void onTimeout() { mTimeout = true; if (mCurrentDialog == DIALOG_YES_NO_AUTH) { Loading
android/app/tests/unit/src/com/android/bluetooth/pbap/BluetoothPbapActivityTest.java 0 → 100644 +173 −0 Original line number Diff line number Diff line /* * Copyright 2022 The Android Open Source 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.bluetooth.pbap; import static android.content.DialogInterface.BUTTON_POSITIVE; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED; import static android.content.pm.PackageManager.DONT_KILL_APP; import static androidx.lifecycle.Lifecycle.State; import static androidx.lifecycle.Lifecycle.State.DESTROYED; import static androidx.lifecycle.Lifecycle.State.RESUMED; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.text.Editable; import android.text.SpannableStringBuilder; import androidx.test.core.app.ActivityScenario; import androidx.test.filters.LargeTest; import androidx.test.platform.app.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import java.util.concurrent.atomic.AtomicBoolean; @LargeTest @RunWith(AndroidJUnit4.class) public class BluetoothPbapActivityTest { Context mTargetContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); Intent mIntent; ActivityScenario<BluetoothPbapActivity> mActivityScenario; @Before public void setUp() { mIntent = new Intent(); mIntent.setClass(mTargetContext, BluetoothPbapActivity.class); mIntent.setAction(BluetoothPbapService.AUTH_CHALL_ACTION); enableActivity(true); mActivityScenario = ActivityScenario.launch(mIntent); } @After public void tearDown() throws Exception { if (mActivityScenario != null) { // Workaround for b/159805732. Without this, test hangs for 45 seconds. Thread.sleep(1_000); mActivityScenario.close(); } enableActivity(false); } @Test public void activityIsDestroyed_whenLaunchedWithoutIntentAction() throws Exception { mActivityScenario.close(); mIntent.setAction(null); mActivityScenario = ActivityScenario.launch(mIntent); assertActivityState(DESTROYED); } @Test public void onPreferenceChange_returnsTrue() throws Exception { AtomicBoolean result = new AtomicBoolean(false); mActivityScenario.onActivity(activity -> result.set( activity.onPreferenceChange(/*preference=*/null, /*newValue=*/null))); assertThat(result.get()).isTrue(); } @Test public void onPositive_finishesActivity() throws Exception { mActivityScenario.onActivity(activity -> { activity.onPositive(); }); assertActivityState(DESTROYED); } @Test public void onNegative_finishesActivity() throws Exception { mActivityScenario.onActivity(activity -> { activity.onNegative(); }); assertActivityState(DESTROYED); } @Test public void onReceiveTimeoutIntent_finishesActivity() throws Exception { Intent intent = new Intent(BluetoothPbapService.USER_CONFIRM_TIMEOUT_ACTION); mActivityScenario.onActivity(activity -> { activity.mReceiver.onReceive(activity, intent); }); assertActivityState(DESTROYED); } @Test public void afterTextChanged() throws Exception { Editable editable = new SpannableStringBuilder("An editable text"); AtomicBoolean result = new AtomicBoolean(false); mActivityScenario.onActivity(activity -> { activity.afterTextChanged(editable); result.set(activity.getButton(BUTTON_POSITIVE).isEnabled()); }); assertThat(result.get()).isTrue(); } // TODO: Test onSaveInstanceState and onRestoreInstanceState. // Note: Activity.recreate() fails. The Activity just finishes itself when recreated. // Fix the bug and test those methods. @Test public void emptyMethods_doesNotThrowException() throws Exception { try { mActivityScenario.onActivity(activity -> { activity.beforeTextChanged(null, 0, 0, 0); activity.onTextChanged(null, 0, 0, 0); }); } catch (Exception ex) { assertWithMessage("Exception should not happen!").fail(); } } private void assertActivityState(State state) throws Exception { // TODO: Change this into an event driven systems Thread.sleep(3_000); assertThat(mActivityScenario.getState()).isEqualTo(state); } private void enableActivity(boolean enable) { int enabledState = enable ? COMPONENT_ENABLED_STATE_ENABLED : COMPONENT_ENABLED_STATE_DEFAULT; mTargetContext.getPackageManager().setApplicationEnabledSetting( mTargetContext.getPackageName(), enabledState, DONT_KILL_APP); ComponentName activityName = new ComponentName(mTargetContext, BluetoothPbapActivity.class); mTargetContext.getPackageManager().setComponentEnabledSetting( activityName, enabledState, DONT_KILL_APP); } }