diff --git a/res/drawable-nodpi/nfc_detection_point.png b/res/drawable-nodpi/nfc_detection_point.png new file mode 100644 index 00000000000..1914264c087 Binary files /dev/null and b/res/drawable-nodpi/nfc_detection_point.png differ diff --git a/res/layout/nfc_detection_point.xml b/res/layout/nfc_detection_point.xml new file mode 100644 index 00000000000..8f938d1926e --- /dev/null +++ b/res/layout/nfc_detection_point.xml @@ -0,0 +1,29 @@ + + + + + + + + diff --git a/res/layout/notification_history.xml b/res/layout/notification_history.xml index b397eef6540..0e7f6b0210d 100644 --- a/res/layout/notification_history.xml +++ b/res/layout/notification_history.xml @@ -72,6 +72,35 @@ android:focusable="true" android:textAppearance="?android:attr/textAppearanceSmall" android:text="@string/notification_history_off_summary" /> + + + + + + + false diff --git a/res/values/dimens.xml b/res/values/dimens.xml index aadd62d5696..c2539fa9fea 100755 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -67,6 +67,8 @@ 6 + 300dp + 64dp 20dp 4dp diff --git a/res/values/strings.xml b/res/values/strings.xml index 588223a7599..8a642ac1fa9 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -7185,6 +7185,8 @@ + + @@ -8110,6 +8112,12 @@ Turn on notification history to see recent notifications and snoozed notifications + + No recent notifications + + + Your recent and snoozed notifications will appear here + view notification settings diff --git a/res/xml/nfc_and_payment_settings.xml b/res/xml/nfc_and_payment_settings.xml index f240388279a..8ff4983579b 100644 --- a/res/xml/nfc_and_payment_settings.xml +++ b/res/xml/nfc_and_payment_settings.xml @@ -19,6 +19,12 @@ xmlns:settings="http://schemas.android.com/apk/res-auto" android:title="@string/nfc_quick_toggle_title"> + + - \ No newline at end of file + diff --git a/src/com/android/settings/accessibility/AccessibilityControlTimeoutPreferenceFragment.java b/src/com/android/settings/accessibility/AccessibilityControlTimeoutPreferenceFragment.java index b37fdfb8519..0550908f3f2 100644 --- a/src/com/android/settings/accessibility/AccessibilityControlTimeoutPreferenceFragment.java +++ b/src/com/android/settings/accessibility/AccessibilityControlTimeoutPreferenceFragment.java @@ -19,7 +19,6 @@ package com.android.settings.accessibility; import android.app.settings.SettingsEnums; import android.content.Context; import android.content.res.Resources; -import android.provider.SearchIndexableResource; import androidx.preference.Preference; @@ -31,7 +30,6 @@ import com.android.settingslib.core.lifecycle.Lifecycle; import com.android.settingslib.search.SearchIndexable; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; @SearchIndexable @@ -88,6 +86,11 @@ public final class AccessibilityControlTimeoutPreferenceFragment extends Dashboa return buildPreferenceControllers(context, getSettingsLifecycle()); } + @Override + public int getHelpResource() { + return R.string.help_url_timeout; + } + private static List buildPreferenceControllers(Context context, Lifecycle lifecycle) { if (sControllers.size() == 0) { diff --git a/src/com/android/settings/applications/appinfo/AppPermissionPreferenceController.java b/src/com/android/settings/applications/appinfo/AppPermissionPreferenceController.java index 12393ad13f7..cf0be207472 100644 --- a/src/com/android/settings/applications/appinfo/AppPermissionPreferenceController.java +++ b/src/com/android/settings/applications/appinfo/AppPermissionPreferenceController.java @@ -19,6 +19,7 @@ package com.android.settings.applications.appinfo; import android.content.ActivityNotFoundException; import android.content.Context; import android.content.Intent; +import android.content.pm.PackageManager; import android.content.res.Resources; import android.icu.text.ListFormatter; import android.util.Log; @@ -28,15 +29,24 @@ import androidx.preference.Preference; import com.android.settings.R; import com.android.settingslib.applications.PermissionsSummaryHelper; +import com.android.settingslib.core.lifecycle.LifecycleObserver; +import com.android.settingslib.core.lifecycle.events.OnStart; +import com.android.settingslib.core.lifecycle.events.OnStop; import java.util.ArrayList; import java.util.List; -public class AppPermissionPreferenceController extends AppInfoPreferenceControllerBase { +/** + * A PreferenceController handling the logic for permissions of apps. + */ +public class AppPermissionPreferenceController extends AppInfoPreferenceControllerBase implements + LifecycleObserver, OnStart, OnStop { private static final String TAG = "PermissionPrefControl"; private static final String EXTRA_HIDE_INFO_BUTTON = "hideInfoButton"; + private final PackageManager mPackageManager; + private String mPackageName; @VisibleForTesting @@ -73,8 +83,22 @@ public class AppPermissionPreferenceController extends AppInfoPreferenceControll } }; + private final PackageManager.OnPermissionsChangedListener mOnPermissionsChangedListener = + uid -> updateState(mPreference); + public AppPermissionPreferenceController(Context context, String key) { super(context, key); + mPackageManager = context.getPackageManager(); + } + + @Override + public void onStart() { + mPackageManager.addOnPermissionsChangeListener(mOnPermissionsChangedListener); + } + + @Override + public void onStop() { + mPackageManager.removeOnPermissionsChangeListener(mOnPermissionsChangedListener); } @Override diff --git a/src/com/android/settings/media/MediaOutputSlice.java b/src/com/android/settings/media/MediaOutputSlice.java index 9dbf948ec99..27960e123ea 100644 --- a/src/com/android/settings/media/MediaOutputSlice.java +++ b/src/com/android/settings/media/MediaOutputSlice.java @@ -197,6 +197,8 @@ public class MediaOutputSlice implements CustomSliceable { } listBuilder.addInputRange(builder); } else { + Log.d(TAG, "addRow device = " + device.getName() + " MaxVolume = " + + device.getMaxVolume()); final ListBuilder.RowBuilder builder = getMediaDeviceRow(device); // Check end item visibility if (device.getDeviceType() == MediaDevice.MediaDeviceType.TYPE_CAST_DEVICE @@ -266,16 +268,24 @@ public class MediaOutputSlice implements CustomSliceable { if (device.getDeviceType() == MediaDevice.MediaDeviceType.TYPE_BLUETOOTH_DEVICE && !device.isConnected()) { - // Append status to title only for the disconnected Bluetooth device. - final SpannableString spannableTitle = new SpannableString( - mContext.getString(R.string.media_output_disconnected_status, deviceName)); - spannableTitle.setSpan(new ForegroundColorSpan( - Utils.getColorAttrDefaultColor(mContext, android.R.attr.textColorSecondary)), - deviceName.length(), - spannableTitle.length(), SPAN_EXCLUSIVE_EXCLUSIVE); - rowBuilder.setTitle(spannableTitle); - rowBuilder.setPrimaryAction(SliceAction.create(broadcastAction, deviceIcon, - ListBuilder.ICON_IMAGE, spannableTitle)); + if (device.getState() == LocalMediaManager.MediaDeviceState.STATE_CONNECTING) { + rowBuilder.setTitle(deviceName); + rowBuilder.setPrimaryAction(SliceAction.create(broadcastAction, deviceIcon, + ListBuilder.ICON_IMAGE, deviceName)); + rowBuilder.setSubtitle(mContext.getText(R.string.media_output_switching)); + } else { + // Append status to title only for the disconnected Bluetooth device. + final SpannableString spannableTitle = new SpannableString( + mContext.getString(R.string.media_output_disconnected_status, deviceName)); + spannableTitle.setSpan(new ForegroundColorSpan( + Utils.getColorAttrDefaultColor(mContext, + android.R.attr.textColorSecondary)), + deviceName.length(), + spannableTitle.length(), SPAN_EXCLUSIVE_EXCLUSIVE); + rowBuilder.setTitle(spannableTitle); + rowBuilder.setPrimaryAction(SliceAction.create(broadcastAction, deviceIcon, + ListBuilder.ICON_IMAGE, spannableTitle)); + } } else { rowBuilder.setTitle(deviceName); rowBuilder.setPrimaryAction(SliceAction.create(broadcastAction, deviceIcon, diff --git a/src/com/android/settings/nfc/NfcDetectionPointController.java b/src/com/android/settings/nfc/NfcDetectionPointController.java new file mode 100644 index 00000000000..b2e8f6abdea --- /dev/null +++ b/src/com/android/settings/nfc/NfcDetectionPointController.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2020 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.settings.nfc; + +import android.content.Context; + +import androidx.annotation.VisibleForTesting; + +import com.android.settings.R; +import com.android.settings.core.BasePreferenceController; +/** + * Controller that used to show nfc detection point guidance + */ +public class NfcDetectionPointController extends BasePreferenceController { + private boolean mEnabled; + + public NfcDetectionPointController(Context context, String preferenceKey) { + super(context, preferenceKey); + mEnabled = mContext.getResources().getBoolean(R.bool.config_nfc_detection_point); + } + + @Override + public int getAvailabilityStatus() { + if (!mEnabled) { + return UNSUPPORTED_ON_DEVICE; + } + return AVAILABLE; + } + + @VisibleForTesting + public void setConfig(boolean value) { + mEnabled = value; + } +} diff --git a/src/com/android/settings/notification/history/NotificationHistoryActivity.java b/src/com/android/settings/notification/history/NotificationHistoryActivity.java index 8d7695ff3d8..d41268dcc53 100644 --- a/src/com/android/settings/notification/history/NotificationHistoryActivity.java +++ b/src/com/android/settings/notification/history/NotificationHistoryActivity.java @@ -70,6 +70,8 @@ public class NotificationHistoryActivity extends Activity { private PackageManager mPm; private HistoryLoader.OnHistoryLoaderListener mOnHistoryLoaderListener = notifications -> { + findViewById(R.id.today_list).setVisibility( + notifications.isEmpty() ? View.GONE : View.VISIBLE); // for each package, new header and recycler view for (NotificationHistoryPackage nhp : notifications) { View viewForPackage = LayoutInflater.from(this) @@ -182,6 +184,10 @@ public class NotificationHistoryActivity extends Activity { } else { mHistoryOn.setVisibility(View.GONE); mHistoryOff.setVisibility(View.VISIBLE); + mHistoryOff.findViewById(R.id.history_off_title).setVisibility(View.VISIBLE); + mHistoryOff.findViewById(R.id.history_off_summary).setVisibility(View.VISIBLE); + mHistoryOff.findViewById(R.id.history_toggled_on_title).setVisibility(View.GONE); + mHistoryOff.findViewById(R.id.history_toggled_on_summary).setVisibility(View.GONE); mTodayView.removeAllViews(); } } @@ -232,7 +238,17 @@ public class NotificationHistoryActivity extends Activity { Settings.Secure.putInt(getContentResolver(), NOTIFICATION_HISTORY_ENABLED, isChecked ? 1 : 0); - toggleViews(isChecked); + mHistoryOn.setVisibility(View.GONE); + mHistoryOff.findViewById(R.id.history_off_title).setVisibility( + isChecked ? View.GONE : View.VISIBLE); + mHistoryOff.findViewById(R.id.history_off_summary).setVisibility( + isChecked ? View.GONE : View.VISIBLE); + mHistoryOff.findViewById(R.id.history_toggled_on_title).setVisibility( + isChecked ? View.VISIBLE : View.GONE); + mHistoryOff.findViewById(R.id.history_toggled_on_summary).setVisibility( + isChecked ? View.VISIBLE : View.GONE); + mTodayView.removeAllViews(); + mHistoryOff.setVisibility(View.VISIBLE); }; private final NotificationListenerService mListener = new NotificationListenerService() { diff --git a/src/com/android/settings/wifi/details2/WifiNetworkDetailsFragment2.java b/src/com/android/settings/wifi/details2/WifiNetworkDetailsFragment2.java index 6ad4c94a8d9..1b54e1a2f31 100644 --- a/src/com/android/settings/wifi/details2/WifiNetworkDetailsFragment2.java +++ b/src/com/android/settings/wifi/details2/WifiNetworkDetailsFragment2.java @@ -235,6 +235,13 @@ public class WifiNetworkDetailsFragment2 extends DashboardFragment implements public void refreshPreferences() { final PreferenceScreen screen = getPreferenceScreen(); for (AbstractPreferenceController controller : mControllers) { + // WifiDetailPreferenceController2 gets the callback WifiEntryCallback#onUpdated, + // it can control the visibility change by itself. + // And WifiDetailPreferenceController2#updatePreference renew mEntityHeaderController + // instance which will cause icon reset. + if (controller instanceof WifiDetailPreferenceController2) { + continue; + } controller.displayPreference(screen); } } diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/AppPermissionPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/AppPermissionPreferenceControllerTest.java index 022de97cbb0..f00471d9ecc 100644 --- a/tests/robotests/src/com/android/settings/applications/appinfo/AppPermissionPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/applications/appinfo/AppPermissionPreferenceControllerTest.java @@ -28,6 +28,7 @@ import static org.mockito.Mockito.when; import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; import androidx.preference.Preference; import androidx.preference.PreferenceScreen; @@ -43,6 +44,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; +import org.robolectric.util.ReflectionHelpers; import java.util.ArrayList; @@ -57,6 +59,8 @@ public class AppPermissionPreferenceControllerTest { private PreferenceScreen mScreen; @Mock private Preference mPreference; + @Mock + private PackageManager mPackageManager; private Context mContext; private AppPermissionPreferenceController mController; @@ -68,6 +72,7 @@ public class AppPermissionPreferenceControllerTest { mController = new AppPermissionPreferenceController(mContext, "permission_settings"); mController.setPackageName("package1"); mController.setParentFragment(mFragment); + ReflectionHelpers.setField(mController, "mPackageManager", mPackageManager); when(mScreen.findPreference(any())).thenReturn(mPreference); final String key = mController.getPreferenceKey(); @@ -75,10 +80,26 @@ public class AppPermissionPreferenceControllerTest { when(mFragment.getActivity()).thenReturn(mActivity); } + @Test + public void onStart_shouldAddPermissionsChangeListener() { + mController.onStart(); + + verify(mPackageManager).addOnPermissionsChangeListener( + any(PackageManager.OnPermissionsChangedListener.class)); + } + + @Test + public void onStop_shouldRemovePermissionsChangeListener() { + mController.onStop(); + + verify(mPackageManager).removeOnPermissionsChangeListener( + any(PackageManager.OnPermissionsChangedListener.class)); + } + @Test public void getAvailabilityStatus_isAlwaysAvailable() { assertThat(mController.getAvailabilityStatus()) - .isEqualTo(AppPermissionPreferenceController.AVAILABLE); + .isEqualTo(AppPermissionPreferenceController.AVAILABLE); } @Test diff --git a/tests/robotests/src/com/android/settings/media/MediaOutputSliceTest.java b/tests/robotests/src/com/android/settings/media/MediaOutputSliceTest.java index 305d1ab72b9..d4590b5a455 100644 --- a/tests/robotests/src/com/android/settings/media/MediaOutputSliceTest.java +++ b/tests/robotests/src/com/android/settings/media/MediaOutputSliceTest.java @@ -473,6 +473,43 @@ public class MediaOutputSliceTest { R.string.bluetooth_pairing_pref_title))).isEqualTo(-1); } + @Test + public void getSlice_disconnectedBtOnTransferring_containTransferringSubtitle() { + final List mSelectedDevices = new ArrayList<>(); + final List mSelectableDevices = new ArrayList<>(); + mDevices.clear(); + final MediaDevice device = mock(MediaDevice.class); + when(device.getName()).thenReturn(TEST_DEVICE_1_NAME); + when(device.getIcon()).thenReturn(mTestDrawable); + when(device.getMaxVolume()).thenReturn(100); + when(device.isConnected()).thenReturn(true); + when(device.getDeviceType()).thenReturn(MediaDevice.MediaDeviceType.TYPE_BLUETOOTH_DEVICE); + when(device.getId()).thenReturn(TEST_DEVICE_1_ID); + final MediaDevice device2 = mock(MediaDevice.class); + when(device2.getName()).thenReturn(TEST_DEVICE_2_NAME); + when(device2.getIcon()).thenReturn(mTestDrawable); + when(device2.getMaxVolume()).thenReturn(100); + when(device2.isConnected()).thenReturn(false); + when(device2.getState()).thenReturn(LocalMediaManager.MediaDeviceState.STATE_CONNECTING); + when(device2.getDeviceType()).thenReturn(MediaDevice.MediaDeviceType.TYPE_BLUETOOTH_DEVICE); + when(device2.getId()).thenReturn(TEST_DEVICE_2_ID); + mSelectedDevices.add(device); + mSelectableDevices.add(device2); + when(mLocalMediaManager.getCurrentConnectedDevice()).thenReturn(device); + mDevices.add(device); + mDevices.add(device2); + when(mLocalMediaManager.getSelectedMediaDevice()).thenReturn(mSelectedDevices); + when(mLocalMediaManager.getSelectableMediaDevice()).thenReturn(mSelectableDevices); + mMediaDeviceUpdateWorker.onDeviceListUpdate(mDevices); + + final Slice mediaSlice = mMediaOutputSlice.getSlice(); + final String sliceInfo = SliceQuery.findAll(mediaSlice, FORMAT_SLICE, HINT_LIST_ITEM, + null).toString(); + + assertThat(TextUtils.indexOf(sliceInfo, mContext.getText(R.string.media_output_switching))) + .isNotEqualTo(-1); + } + @Test public void onNotifyChange_foundMediaDevice_connect() { mDevices.clear(); diff --git a/tests/robotests/src/com/android/settings/nfc/NfcDetectionPointControllerTest.java b/tests/robotests/src/com/android/settings/nfc/NfcDetectionPointControllerTest.java new file mode 100644 index 00000000000..31ac7d67e02 --- /dev/null +++ b/tests/robotests/src/com/android/settings/nfc/NfcDetectionPointControllerTest.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2020 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.settings.nfc; + +import static com.google.common.truth.Truth.assertThat; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; + +@RunWith(RobolectricTestRunner.class) +public class NfcDetectionPointControllerTest { + + private NfcDetectionPointController mController; + + @Before + public void setUp() { + mController = new NfcDetectionPointController(RuntimeEnvironment.application, "fakeKey"); + } + + @Test + public void getAvailabilityStatus_withConfigIsTrue_returnAvailable() { + mController.setConfig(true); + assertThat(mController.getAvailabilityStatus()) + .isEqualTo(NfcDetectionPointController.AVAILABLE); + } + + @Test + public void getAvailabilityStatus_withConfigIsFalse_returnUnavailable() { + mController.setConfig(false); + assertThat(mController.getAvailabilityStatus()) + .isNotEqualTo(NfcDetectionPointController.AVAILABLE); + } +}