Merge "Remove the char limit of unrestricted_app" into main
diff --git a/src/com/android/settings/datausage/lib/NetworkStatsRepository.kt b/src/com/android/settings/datausage/lib/NetworkStatsRepository.kt
index 56b1931..484a8a3 100644
--- a/src/com/android/settings/datausage/lib/NetworkStatsRepository.kt
+++ b/src/com/android/settings/datausage/lib/NetworkStatsRepository.kt
@@ -93,13 +93,15 @@
val buckets = mutableListOf<Bucket>()
val bucket = NetworkStats.Bucket()
while (getNextBucket(bucket)) {
- buckets += Bucket(
- uid = bucket.uid,
- bytes = bucket.bytes,
- state = bucket.state,
- startTimeStamp = bucket.startTimeStamp,
- endTimeStamp = bucket.endTimeStamp,
- )
+ if (bucket.bytes > 0) {
+ buckets += Bucket(
+ uid = bucket.uid,
+ bytes = bucket.bytes,
+ state = bucket.state,
+ startTimeStamp = bucket.startTimeStamp,
+ endTimeStamp = bucket.endTimeStamp,
+ )
+ }
}
buckets
}
diff --git a/src/com/android/settings/widget/HighlightablePreferenceGroupAdapter.java b/src/com/android/settings/widget/HighlightablePreferenceGroupAdapter.java
index 7145460..82ef58b 100644
--- a/src/com/android/settings/widget/HighlightablePreferenceGroupAdapter.java
+++ b/src/com/android/settings/widget/HighlightablePreferenceGroupAdapter.java
@@ -40,6 +40,7 @@
import com.android.settings.R;
import com.android.settings.SettingsPreferenceFragment;
+import com.android.settings.accessibility.AccessibilityUtil;
import com.google.android.material.appbar.AppBarLayout;
@@ -50,6 +51,8 @@
static final long DELAY_COLLAPSE_DURATION_MILLIS = 300L;
@VisibleForTesting
static final long DELAY_HIGHLIGHT_DURATION_MILLIS = 600L;
+ @VisibleForTesting
+ static final long DELAY_HIGHLIGHT_DURATION_MILLIS_A11Y = 300L;
private static final long HIGHLIGHT_DURATION = 15000L;
private static final long HIGHLIGHT_FADE_OUT_DURATION = 500L;
private static final long HIGHLIGHT_FADE_IN_DURATION = 200L;
@@ -59,6 +62,7 @@
@VisibleForTesting
boolean mFadeInAnimated;
+ private final Context mContext;
private final int mNormalBackgroundRes;
private final String mHighlightKey;
private boolean mHighlightRequested;
@@ -102,12 +106,12 @@
super(preferenceGroup);
mHighlightKey = key;
mHighlightRequested = highlightRequested;
- final Context context = preferenceGroup.getContext();
+ mContext = preferenceGroup.getContext();
final TypedValue outValue = new TypedValue();
- context.getTheme().resolveAttribute(android.R.attr.selectableItemBackground,
+ mContext.getTheme().resolveAttribute(android.R.attr.selectableItemBackground,
outValue, true /* resolveRefs */);
mNormalBackgroundRes = outValue.resourceId;
- mHighlightColor = context.getColor(R.color.preference_highlight_color);
+ mHighlightColor = mContext.getColor(R.color.preference_highlight_color);
}
@Override
@@ -121,12 +125,11 @@
View v = holder.itemView;
if (position == mHighlightPosition
&& (mHighlightKey != null
- && TextUtils.equals(mHighlightKey, getItem(position).getKey()))) {
+ && TextUtils.equals(mHighlightKey, getItem(position).getKey()))
+ && v.isShown()) {
// This position should be highlighted. If it's highlighted before - skip animation.
+ v.requestAccessibilityFocus();
addHighlightBackground(holder, !mFadeInAnimated);
- if (v != null) {
- v.requestAccessibilityFocus();
- }
} else if (Boolean.TRUE.equals(v.getTag(R.id.preference_highlighted))) {
// View with highlight is reused for a view that should not have highlight
removeHighlightBackground(holder, false /* animate */);
@@ -157,13 +160,14 @@
// Remove the animator as early as possible to avoid a RecyclerView crash.
recyclerView.setItemAnimator(null);
- // Scroll to correct position after 600 milliseconds.
+ // Scroll to correct position after a short delay.
root.postDelayed(() -> {
if (ensureHighlightPosition()) {
recyclerView.smoothScrollToPosition(mHighlightPosition);
highlightAndFocusTargetItem(recyclerView, mHighlightPosition);
}
- }, DELAY_HIGHLIGHT_DURATION_MILLIS);
+ }, AccessibilityUtil.isTouchExploreEnabled(mContext)
+ ? DELAY_HIGHLIGHT_DURATION_MILLIS_A11Y : DELAY_HIGHLIGHT_DURATION_MILLIS);
}
private void highlightAndFocusTargetItem(RecyclerView recyclerView, int highlightPosition) {
diff --git a/tests/robotests/src/com/android/settings/widget/HighlightablePreferenceGroupAdapterTest.java b/tests/robotests/src/com/android/settings/widget/HighlightablePreferenceGroupAdapterTest.java
index 29560ab..03684ad 100644
--- a/tests/robotests/src/com/android/settings/widget/HighlightablePreferenceGroupAdapterTest.java
+++ b/tests/robotests/src/com/android/settings/widget/HighlightablePreferenceGroupAdapterTest.java
@@ -33,6 +33,7 @@
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
import android.view.View;
+import android.view.accessibility.AccessibilityManager;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
@@ -54,6 +55,8 @@
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
+import org.robolectric.shadow.api.Shadow;
+import org.robolectric.shadows.ShadowAccessibilityManager;
import org.robolectric.util.ReflectionHelpers;
@RunWith(RobolectricTestRunner.class)
@@ -67,7 +70,7 @@
@Mock
private View mRoot;
@Mock
- private PreferenceCategory mPreferenceCatetory;
+ private PreferenceCategory mPreferenceCategory;
@Mock
private SettingsPreferenceFragment mFragment;
@@ -82,8 +85,8 @@
mContext = RuntimeEnvironment.application;
mPreference = new Preference(mContext);
mPreference.setKey(TEST_KEY);
- when(mPreferenceCatetory.getContext()).thenReturn(mContext);
- mAdapter = spy(new HighlightablePreferenceGroupAdapter(mPreferenceCatetory, TEST_KEY,
+ when(mPreferenceCategory.getContext()).thenReturn(mContext);
+ mAdapter = spy(new HighlightablePreferenceGroupAdapter(mPreferenceCategory, TEST_KEY,
false /* highlighted*/));
when(mAdapter.getItem(anyInt())).thenReturn(mPreference);
mViewHolder = PreferenceViewHolder.createInstanceForTests(
@@ -102,6 +105,18 @@
}
@Test
+ public void requestHighlight_enableTouchExploration_shouldHaveA11yHighlightDelay() {
+ ShadowAccessibilityManager am = Shadow.extract(AccessibilityManager.getInstance(mContext));
+ am.setTouchExplorationEnabled(true);
+ when(mAdapter.getPreferenceAdapterPosition(anyString())).thenReturn(1);
+ mAdapter.requestHighlight(mRoot, mock(RecyclerView.class), mock(AppBarLayout.class));
+
+ // DELAY_HIGHLIGHT_DURATION_MILLIS_A11Y = DELAY_COLLAPSE_DURATION_MILLIS
+ verify(mRoot, times(2)).postDelayed(any(),
+ eq(HighlightablePreferenceGroupAdapter.DELAY_HIGHLIGHT_DURATION_MILLIS_A11Y));
+ }
+
+ @Test
public void requestHighlight_noKey_highlightedBefore_noRecyclerView_shouldNotRequest() {
ReflectionHelpers.setField(mAdapter, "mHighlightKey", null);
ReflectionHelpers.setField(mAdapter, "mHighlightRequested", false);
@@ -178,12 +193,24 @@
assertThat(mViewHolder.itemView.getTag(R.id.preference_highlighted)).isNull();
}
+ @Test
+ public void updateBackground_itemViewIsInvisible_shouldNotSetHighlightedTag() {
+ ReflectionHelpers.setField(mAdapter, "mHighlightPosition", 10);
+ ReflectionHelpers.setField(mViewHolder, "itemView", spy(mViewHolder.itemView));
+ when(mViewHolder.itemView.isShown()).thenReturn(false);
+
+ mAdapter.updateBackground(mViewHolder, 0);
+
+ assertThat(mViewHolder.itemView.getTag(R.id.preference_highlighted)).isNull();
+ }
+
/**
* When background is being updated, we also request the a11y focus on the preference
*/
@Test
public void updateBackground_shouldRequestAccessibilityFocus() {
View viewItem = mock(View.class);
+ when(viewItem.isShown()).thenReturn(true);
mViewHolder = PreferenceViewHolder.createInstanceForTests(viewItem);
ReflectionHelpers.setField(mAdapter, "mHighlightPosition", 10);
@@ -195,6 +222,8 @@
@Test
public void updateBackground_highlight_shouldAnimateBackgroundAndSetHighlightedTag() {
ReflectionHelpers.setField(mAdapter, "mHighlightPosition", 10);
+ ReflectionHelpers.setField(mViewHolder, "itemView", spy(mViewHolder.itemView));
+ when(mViewHolder.itemView.isShown()).thenReturn(true);
assertThat(mAdapter.mFadeInAnimated).isFalse();
mAdapter.updateBackground(mViewHolder, 10);
@@ -206,9 +235,21 @@
}
@Test
+ public void updateBackground_highlight_itemViewIsInvisible_shouldNotAnimate() {
+ ReflectionHelpers.setField(mAdapter, "mHighlightPosition", 10);
+ ReflectionHelpers.setField(mViewHolder, "itemView", spy(mViewHolder.itemView));
+ when(mViewHolder.itemView.isShown()).thenReturn(false);
+
+ mAdapter.updateBackground(mViewHolder, 10);
+
+ assertThat(mAdapter.mFadeInAnimated).isFalse();
+ }
+
+ @Test
public void updateBackgroundTwice_highlight_shouldAnimateOnce() {
ReflectionHelpers.setField(mAdapter, "mHighlightPosition", 10);
ReflectionHelpers.setField(mViewHolder, "itemView", spy(mViewHolder.itemView));
+ when(mViewHolder.itemView.isShown()).thenReturn(true);
assertThat(mAdapter.mFadeInAnimated).isFalse();
mAdapter.updateBackground(mViewHolder, 10);
// mFadeInAnimated change from false to true - indicating background change is scheduled
diff --git a/tests/spa_unit/src/com/android/settings/datausage/lib/AppDataUsageDetailsRepositoryTest.kt b/tests/spa_unit/src/com/android/settings/datausage/lib/AppDataUsageDetailsRepositoryTest.kt
index fda3dc9..8476a49 100644
--- a/tests/spa_unit/src/com/android/settings/datausage/lib/AppDataUsageDetailsRepositoryTest.kt
+++ b/tests/spa_unit/src/com/android/settings/datausage/lib/AppDataUsageDetailsRepositoryTest.kt
@@ -125,6 +125,32 @@
)
}
+ @Test
+ fun queryDetailsForCycles_appWithZeroUsage_filtered(): Unit = runBlocking {
+ networkStatsRepository.stub {
+ on { queryBuckets(CYCLE1_END_TIME, CYCLE2_END_TIME) } doReturn listOf(
+ Bucket(
+ uid = UID,
+ bytes = 0L,
+ startTimeStamp = 0L,
+ endTimeStamp = 0L,
+ ),
+ )
+ }
+ val repository = AppDataUsageDetailsRepository(
+ context = context,
+ cycles = null,
+ template = template,
+ uids = listOf(UID),
+ networkCycleDataRepository = networkCycleDataRepository,
+ networkStatsRepository = networkStatsRepository,
+ )
+
+ val detailsForCycles = repository.queryDetailsForCycles()
+
+ assertThat(detailsForCycles).isEmpty()
+ }
+
private companion object {
const val CYCLE1_START_TIME = 1694444444000L
const val CYCLE1_END_TIME = 1695555555000L