diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 0aed9e015a3..ae87edb5628 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -1452,8 +1452,7 @@ + android:noHistory="true"> @@ -1523,7 +1522,6 @@ @@ -1537,8 +1535,7 @@ + android:exported="true"> @@ -1913,8 +1910,7 @@ + android:label="@string/usage_access_title"> diff --git a/res/values/strings.xml b/res/values/strings.xml index e10405262a2..6832e080d4b 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -1044,8 +1044,12 @@ You\u2019ve reached the maximum number of attempts - - Use your fingerprint to unlock your phone or verify it\u2019s you, like when you sign in to apps\n\nLearn more + + Use your fingerprint to unlock your tablet or verify it\u2019s you, like when you sign in to apps + + Use your fingerprint to unlock your device or verify it\u2019s you, like when you sign in to apps + + Use your fingerprint to unlock your phone or verify it\u2019s you, like when you sign in to apps @@ -1402,7 +1406,7 @@ Choose your backup screen lock method - This lock can\u2019t be reset by your IT admin. LINK_BEGINSet a separate work lock insteadLINK_END + If you forget your screen lock, your IT admin can\u2019t reset it. Set a separate work lock If you forget this lock, ask your IT admin to reset it diff --git a/src/com/android/settings/applications/manageapplications/ManageApplications.java b/src/com/android/settings/applications/manageapplications/ManageApplications.java index 062bfc576b2..c081ef9af1e 100644 --- a/src/com/android/settings/applications/manageapplications/ManageApplications.java +++ b/src/com/android/settings/applications/manageapplications/ManageApplications.java @@ -173,6 +173,8 @@ public class ManageApplications extends InstrumentedFragment private static final String EXTRA_HAS_BRIDGE = "hasBridge"; private static final String EXTRA_FILTER_TYPE = "filterType"; @VisibleForTesting + static final String EXTRA_SEARCH_QUERY = "search_query"; + @VisibleForTesting static final String EXTRA_EXPAND_SEARCH_VIEW = "expand_search_view"; // attributes used as keys when passing values to AppInfoDashboardFragment activity @@ -253,6 +255,8 @@ public class ManageApplications extends InstrumentedFragment // Whether or not search view is expanded. @VisibleForTesting boolean mExpandSearch; + @VisibleForTesting + CharSequence mPreQuery; private View mRootView; private Spinner mFilterSpinner; @@ -358,6 +362,7 @@ public class ManageApplications extends InstrumentedFragment mFilterType = savedInstanceState.getInt(EXTRA_FILTER_TYPE, AppFilterRegistry.FILTER_APPS_ALL); mExpandSearch = savedInstanceState.getBoolean(EXTRA_EXPAND_SEARCH_VIEW); + mPreQuery = savedInstanceState.getCharSequence(EXTRA_SEARCH_QUERY); } mInvalidSizeStr = activity.getText(R.string.invalid_size_value); @@ -544,6 +549,7 @@ public class ManageApplications extends InstrumentedFragment outState.putBoolean(EXTRA_SHOW_SYSTEM, mShowSystem); if (mSearchView != null) { outState.putBoolean(EXTRA_EXPAND_SEARCH_VIEW, !mSearchView.isIconified()); + outState.putCharSequence(EXTRA_SEARCH_QUERY, mSearchView.getQuery()); } if (mApplications != null) { outState.putBoolean(EXTRA_HAS_ENTRIES, mApplications.mHasReceivedLoadEntries); @@ -683,6 +689,9 @@ public class ManageApplications extends InstrumentedFragment if (mExpandSearch) { searchMenuItem.expandActionView(); } + if (!TextUtils.isEmpty(mPreQuery)) { + mSearchView.setQuery(mPreQuery, true); + } } updateOptionsMenu(); diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java b/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java index 22821920722..408f8596aac 100644 --- a/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java +++ b/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java @@ -145,6 +145,7 @@ public class FingerprintSettings extends SubSettings { private Drawable mHighlightDrawable; private int mUserId; private CharSequence mFooterTitle; + private View.OnClickListener mLearnMoreListener = null; private boolean mEnrollClicked; private long mChallenge; @@ -372,9 +373,8 @@ public class FingerprintSettings extends SubSettings { mFooterTitle = AnnotationSpan.linkify(footerText, linkInfo, adminLinkInfo); } else { - mFooterTitle = AnnotationSpan.linkify( - getText(R.string.security_settings_fingerprint_v2_home_screen), - linkInfo, adminLinkInfo); + mFooterTitle = getText(R.string.security_settings_fingerprint_v2_home_screen_text); + mLearnMoreListener = (v) -> activity.startActivityForResult(helpIntent, 0); } @@ -474,8 +474,12 @@ public class FingerprintSettings extends SubSettings { if (context == null) { return; } - root.addPreference(new FooterPreference.Builder(context).setTitle( - mFooterTitle).build()); + final FooterPreference footer = new FooterPreference.Builder(context) + .setTitle(mFooterTitle).build(); + if (mLearnMoreListener != null) { + footer.setLearnMoreAction(mLearnMoreListener); + } + root.addPreference(footer); } private static String genKey(int id) { diff --git a/src/com/android/settings/bluetooth/BluetoothFindBroadcastsFragment.java b/src/com/android/settings/bluetooth/BluetoothFindBroadcastsFragment.java index 9a26a57f0fc..07a31560d24 100644 --- a/src/com/android/settings/bluetooth/BluetoothFindBroadcastsFragment.java +++ b/src/com/android/settings/bluetooth/BluetoothFindBroadcastsFragment.java @@ -75,6 +75,7 @@ public class BluetoothFindBroadcastsFragment extends RestrictedDashboardFragment CachedBluetoothDevice mCachedDevice; @VisibleForTesting PreferenceCategory mBroadcastSourceListCategory; + BluetoothFindBroadcastsHeaderController mBluetoothFindBroadcastsHeaderController; private LocalBluetoothLeBroadcastAssistant mLeBroadcastAssistant; private BluetoothBroadcastSourcePreference mSelectedPreference; private Executor mExecutor; @@ -93,7 +94,6 @@ public class BluetoothFindBroadcastsFragment extends RestrictedDashboardFragment @Override public void onSearchStartFailed(int reason) { Log.d(TAG, "onSearchStartFailed: " + reason); - } @Override @@ -240,6 +240,28 @@ public class BluetoothFindBroadcastsFragment extends RestrictedDashboardFragment return SettingsEnums.PAGE_UNKNOWN; } + /** + * Starts to scan broadcast source by the BluetoothLeBroadcastAssistant. + */ + public void scanBroadcastSource() { + if (mLeBroadcastAssistant == null) { + Log.w(TAG, "scanBroadcastSource: LeBroadcastAssistant is null!"); + return; + } + mLeBroadcastAssistant.startSearchingForSources(getScanFilter()); + } + + /** + * Leaves the broadcast source by the BluetoothLeBroadcastAssistant. + */ + public void leaveBroadcastSession() { + if (mLeBroadcastAssistant == null || mCachedDevice == null) { + Log.w(TAG, "leaveBroadcastSession: LeBroadcastAssistant or CachedDevice is null!"); + return; + } + mLeBroadcastAssistant.removeSource(mCachedDevice.getDevice(), getSourceId()); + } + @Override protected String getLogTag() { return TAG; @@ -256,13 +278,18 @@ public class BluetoothFindBroadcastsFragment extends RestrictedDashboardFragment if (mCachedDevice != null) { Lifecycle lifecycle = getSettingsLifecycle(); - controllers.add(new BluetoothFindBroadcastsHeaderController(context, this, - mCachedDevice, lifecycle, mManager)); + mBluetoothFindBroadcastsHeaderController = new BluetoothFindBroadcastsHeaderController( + context, this, mCachedDevice, lifecycle, mManager); + controllers.add(mBluetoothFindBroadcastsHeaderController); } return controllers; } - private LocalBluetoothLeBroadcastAssistant getLeBroadcastAssistant() { + /** + * Gets the LocalBluetoothLeBroadcastAssistant + * @return the LocalBluetoothLeBroadcastAssistant + */ + public LocalBluetoothLeBroadcastAssistant getLeBroadcastAssistant() { if (mManager == null) { Log.w(TAG, "getLeBroadcastAssistant: LocalBluetoothManager is null!"); return null; @@ -292,6 +319,11 @@ public class BluetoothFindBroadcastsFragment extends RestrictedDashboardFragment } item.updateMetadataAndRefreshUi(source, isConnected); item.setOrder(isConnected ? 0 : 1); + + //refresh the header + if (mBluetoothFindBroadcastsHeaderController != null) { + mBluetoothFindBroadcastsHeaderController.refreshUi(); + } } private BluetoothBroadcastSourcePreference createBluetoothBroadcastSourcePreference( diff --git a/src/com/android/settings/bluetooth/BluetoothFindBroadcastsHeaderController.java b/src/com/android/settings/bluetooth/BluetoothFindBroadcastsHeaderController.java index dfdcae05109..1527f2145fe 100644 --- a/src/com/android/settings/bluetooth/BluetoothFindBroadcastsHeaderController.java +++ b/src/com/android/settings/bluetooth/BluetoothFindBroadcastsHeaderController.java @@ -16,22 +16,24 @@ package com.android.settings.bluetooth; +import android.bluetooth.BluetoothProfile; import android.content.Context; -import android.util.Log; +import android.content.Intent; import android.view.View; import android.widget.Button; import android.widget.LinearLayout; import android.widget.TextView; import androidx.preference.PreferenceCategory; -import androidx.preference.PreferenceFragmentCompat; import androidx.preference.PreferenceScreen; import com.android.settings.R; +import com.android.settingslib.bluetooth.BluetoothBroadcastUtils; import com.android.settingslib.bluetooth.CachedBluetoothDevice; +import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant; import com.android.settingslib.bluetooth.LocalBluetoothManager; -import com.android.settingslib.bluetooth.LocalBluetoothProfileManager; import com.android.settingslib.core.lifecycle.Lifecycle; +import com.android.settingslib.qrcode.QrCodeScanModeActivity; import com.android.settingslib.widget.LayoutPreference; /** @@ -52,11 +54,12 @@ public class BluetoothFindBroadcastsHeaderController extends BluetoothDetailsCon LinearLayout mBtnBroadcastLayout; Button mBtnLeaveBroadcast; Button mBtnScanQrCode; - + BluetoothFindBroadcastsFragment mBluetoothFindBroadcastsFragment; public BluetoothFindBroadcastsHeaderController(Context context, - PreferenceFragmentCompat fragment, CachedBluetoothDevice device, Lifecycle lifecycle, - LocalBluetoothManager bluetoothManager) { + BluetoothFindBroadcastsFragment fragment, CachedBluetoothDevice device, + Lifecycle lifecycle, LocalBluetoothManager bluetoothManager) { super(context, fragment, device, lifecycle); + mBluetoothFindBroadcastsFragment = fragment; } @Override @@ -101,20 +104,41 @@ public class BluetoothFindBroadcastsHeaderController extends BluetoothDetailsCon mBtnFindBroadcast.setVisibility(View.VISIBLE); mBtnBroadcastLayout.setVisibility(View.GONE); } + + mBtnLeaveBroadcast.setEnabled(false); + if (mBluetoothFindBroadcastsFragment != null && mCachedDevice != null) { + LocalBluetoothLeBroadcastAssistant broadcastAssistant = + mBluetoothFindBroadcastsFragment.getLeBroadcastAssistant(); + if (broadcastAssistant != null + && broadcastAssistant.getConnectionStatus(mCachedDevice.getDevice()) + == BluetoothProfile.STATE_CONNECTED) { + mBtnLeaveBroadcast.setEnabled(true); + } + } } private void scanBroadcastSource() { - // TODO(b/228258236) : Call the LocalBluetoothLeBroadcastAssistant - // to start searching for source + // TODO(b/231543455) : Using the BluetoothDeviceUpdater to refactor it. + if (mBluetoothFindBroadcastsFragment == null) { + return; + } + mBluetoothFindBroadcastsFragment.scanBroadcastSource(); } private void leaveBroadcastSession() { - // TODO(b/228258236) : Call the LocalBluetoothLeBroadcastAssistant - // to leave the broadcast session + if (mBluetoothFindBroadcastsFragment == null) { + return; + } + mBluetoothFindBroadcastsFragment.leaveBroadcastSession(); } private void launchQrCodeScanner() { - // TODO(b/228259065) : Launch the QR code scanner page by intent + final Intent intent = new Intent(mContext, QrCodeScanModeActivity.class); + intent.setAction(BluetoothBroadcastUtils.ACTION_BLUETOOTH_LE_AUDIO_QR_CODE_SCANNER) + .putExtra(BluetoothBroadcastUtils.EXTRA_BLUETOOTH_SINK_IS_GROUP, false) + .putExtra(BluetoothBroadcastUtils.EXTRA_BLUETOOTH_DEVICE_SINK, + mCachedDevice.getDevice()); + mContext.startActivity(intent); } @Override @@ -128,4 +152,11 @@ public class BluetoothFindBroadcastsHeaderController extends BluetoothDetailsCon public String getPreferenceKey() { return KEY_BROADCAST_HEADER; } + + /** + * Updates the UI + */ + public void refreshUi() { + updateHeaderLayout(); + } } diff --git a/src/com/android/settings/password/ChooseLockGeneric.java b/src/com/android/settings/password/ChooseLockGeneric.java index e340b505356..e2b91c753fd 100644 --- a/src/com/android/settings/password/ChooseLockGeneric.java +++ b/src/com/android/settings/password/ChooseLockGeneric.java @@ -67,7 +67,6 @@ import com.android.internal.widget.LockPatternUtils; import com.android.internal.widget.LockscreenCredential; import com.android.settings.EncryptionInterstitial; import com.android.settings.EventLogTags; -import com.android.settings.LinkifyUtils; import com.android.settings.R; import com.android.settings.SettingsActivity; import com.android.settings.SettingsPreferenceFragment; @@ -78,7 +77,9 @@ import com.android.settings.core.SubSettingLauncher; import com.android.settings.core.instrumentation.InstrumentedDialogFragment; import com.android.settings.safetycenter.LockScreenSafetySource; import com.android.settings.search.SearchFeatureProvider; +import com.android.settings.utils.AnnotationSpan; import com.android.settingslib.RestrictedPreference; +import com.android.settingslib.widget.FooterPreference; import com.google.android.setupcompat.util.WizardManagerHelper; @@ -403,32 +404,7 @@ public class ChooseLockGeneric extends SettingsActivity { WORK_PROFILE_SCREEN_LOCK_SETUP_MESSAGE, () -> getString(R.string.lock_settings_picker_profile_message))); } else { - int profileUserId = Utils.getManagedProfileId(mUserManager, mUserId); - if (mController.isScreenLockRestrictedByAdmin() - && profileUserId != UserHandle.USER_NULL) { - final StringBuilder description = new StringBuilder( - mDpm.getResources().getString( - WORK_PROFILE_IT_ADMIN_CANT_RESET_SCREEN_LOCK, - () -> getString( - R.string.lock_settings_picker_admin_restricted_personal_message))); - final LinkifyUtils.OnClickListener clickListener = () -> { - final Bundle extras = new Bundle(); - extras.putInt(Intent.EXTRA_USER_ID, profileUserId); - if (mUserPassword != null) { - extras.putParcelable(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD, - mUserPassword); - } - new SubSettingLauncher(getActivity()) - .setDestination(ChooseLockGenericFragment.class.getName()) - .setSourceMetricsCategory(getMetricsCategory()) - .setArguments(extras) - .launch(); - finish(); - }; - LinkifyUtils.linkify(textView, description, clickListener); - } else { - textView.setText(""); - } + textView.setText(""); } } } @@ -633,10 +609,39 @@ public class ChooseLockGeneric extends SettingsActivity { protected void addPreferences() { addPreferencesFromResource(R.xml.security_settings_picker); - final Preference footer = findPreference(KEY_LOCK_SETTINGS_FOOTER); + int profileUserId = Utils.getManagedProfileId(mUserManager, mUserId); + final FooterPreference footer = findPreference(KEY_LOCK_SETTINGS_FOOTER); if (!TextUtils.isEmpty(mCallerAppName) && !mIsCallingAppAdmin) { footer.setVisible(true); footer.setTitle(getFooterString()); + } else if (!mForFace && !mForBiometrics && !mForFingerprint && !mIsManagedProfile + && mController.isScreenLockRestrictedByAdmin() + && profileUserId != UserHandle.USER_NULL) { + CharSequence description = + mDpm.getResources().getString(WORK_PROFILE_IT_ADMIN_CANT_RESET_SCREEN_LOCK, + () -> null); + if (description == null) { + description = getText( + R.string.lock_settings_picker_admin_restricted_personal_message); + } + final AnnotationSpan.LinkInfo linkInfo = new AnnotationSpan.LinkInfo( + AnnotationSpan.LinkInfo.DEFAULT_ANNOTATION, (view) -> { + final Bundle extras = new Bundle(); + extras.putInt(Intent.EXTRA_USER_ID, profileUserId); + if (mUserPassword != null) { + extras.putParcelable(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD, + mUserPassword); + } + new SubSettingLauncher(getActivity()) + .setDestination(ChooseLockGenericFragment.class.getName()) + .setSourceMetricsCategory(getMetricsCategory()) + .setArguments(extras) + .launch(); + finish(); + }); + CharSequence footerText = AnnotationSpan.linkify(description, linkInfo); + footer.setVisible(true); + footer.setTitle(footerText); } else { footer.setVisible(false); } diff --git a/tests/robotests/src/com/android/settings/applications/manageapplications/ManageApplicationsTest.java b/tests/robotests/src/com/android/settings/applications/manageapplications/ManageApplicationsTest.java index da17f1143bb..ec4f8f2e8b6 100644 --- a/tests/robotests/src/com/android/settings/applications/manageapplications/ManageApplicationsTest.java +++ b/tests/robotests/src/com/android/settings/applications/manageapplications/ManageApplicationsTest.java @@ -188,6 +188,24 @@ public class ManageApplicationsTest { verify(searchMenu).expandActionView(); } + @Test + public void onCreateOptionsMenu_hasPreQuery_shouldSetQuery() { + final SearchView searchView = mock(SearchView.class); + final MenuItem searchMenu = mock(MenuItem.class); + final MenuItem helpMenu = mock(MenuItem.class); + when(searchMenu.getActionView()).thenReturn(searchView); + when(mMenu.findItem(R.id.search_app_list_menu)).thenReturn(searchMenu); + when(mMenu.add(anyInt() /* groupId */, anyInt() /* itemId */, anyInt() /* order */, + anyInt() /* titleRes */)).thenReturn(helpMenu); + doReturn("Test").when(mFragment).getText(anyInt() /* resId */); + doNothing().when(mFragment).updateOptionsMenu(); + + mFragment.mPreQuery = "test"; + mFragment.onCreateOptionsMenu(mMenu, mock(MenuInflater.class)); + + verify(searchView).setQuery("test", true); + } + @Test public void onQueryTextChange_shouldFilterSearchInApplicationsAdapter() { final ManageApplications.ApplicationsAdapter adapter = @@ -518,6 +536,38 @@ public class ManageApplicationsTest { assertThat(bundle.getBoolean(ManageApplications.EXTRA_EXPAND_SEARCH_VIEW)).isFalse(); } + @Test + public void onSaveInstanceState_noSearchView_shouldNotSaveQuery() { + final Bundle bundle = new Bundle(); + ReflectionHelpers.setField(mFragment, "mResetAppsHelper", mock(ResetAppsHelper.class)); + ReflectionHelpers.setField(mFragment, "mFilter", mock(AppFilterItem.class)); + ReflectionHelpers.setField(mFragment, "mApplications", + mock(ManageApplications.ApplicationsAdapter.class)); + + mFragment.onSaveInstanceState(bundle); + + assertThat(bundle.containsKey(ManageApplications.EXTRA_SEARCH_QUERY)).isFalse(); + } + + @Test + public void onSaveInstanceState_searchViewSet_shouldSaveQuery() { + final SearchView searchView = mock(SearchView.class); + final Bundle bundle = new Bundle(); + ReflectionHelpers.setField(mFragment, "mResetAppsHelper", mock(ResetAppsHelper.class)); + ReflectionHelpers.setField(mFragment, "mFilter", mock(AppFilterItem.class)); + ReflectionHelpers.setField(mFragment, "mApplications", + mock(ManageApplications.ApplicationsAdapter.class)); + ReflectionHelpers.setField(mFragment, "mSearchView", searchView); + when(searchView.isIconified()).thenReturn(true); + when(searchView.getQuery()).thenReturn("test"); + + mFragment.onSaveInstanceState(bundle); + + assertThat(bundle.containsKey(ManageApplications.EXTRA_SEARCH_QUERY)).isTrue(); + assertThat(bundle.getCharSequence(ManageApplications.EXTRA_SEARCH_QUERY)) + .isEqualTo("test"); + } + @Test public void createHeader_batteryListType_hasCorrectItems() { ReflectionHelpers.setField(mFragment, "mListType", ManageApplications.LIST_TYPE_HIGH_POWER);