Fix Conversation page flickers
In this page, 3 conversation lists are implemented by the ConversationListPreferenceController, these lists updates its contents in updateState(), which is after the preference screen view created. So when the first time this page is showed, animations of added contents will be shown. The improvement is when the first time, update the list in the onCreate(), which is called before view creation, instead of the updateState(). And also do the same thing for RecentConversationsPreferenceController. Also, to reduce latency, 1. Because currently there are duplicated calls in NoConversationsPreferenceController to check whether conversations are exists or not, by removing the duplicated calls and reuse the result from other controllers, the latency could be reduced. 2. Currently, there are seperated api calls, the mBackend.getConversations(false) in AllConversationsPreferenceController and the mBackend.getConversations(true) in PriorityConversationsPreferenceController, use one mBackend.getConversations(false) in ConversationListSettings to improve, this does not change the behavior because the result is filtered in matchesFilter() both before and after. 3. Currently, we sort conversations first then filter them, change to filter first then sort to reduce latency. Fix: 215073227 Test: visual check & robo tests Change-Id: I028a7fabbbf64cf5627e6615372282a36eb784e5
This commit is contained in:
@@ -21,7 +21,6 @@ import static com.google.common.truth.Truth.assertThat;
|
||||
import static org.mockito.Mockito.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
@@ -35,47 +34,49 @@ import android.provider.Settings;
|
||||
import android.service.notification.ConversationChannelWrapper;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.Spanned;
|
||||
import android.text.style.BulletSpan;
|
||||
import android.text.style.QuoteSpan;
|
||||
import android.text.style.SubscriptSpan;
|
||||
import android.text.style.UnderlineSpan;
|
||||
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceCategory;
|
||||
import androidx.preference.PreferenceGroup;
|
||||
import androidx.preference.PreferenceManager;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
|
||||
import com.android.settings.applications.AppInfoBase;
|
||||
import com.android.settings.notification.NotificationBackend;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Answers;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.mockito.Spy;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
import org.robolectric.shadows.ShadowApplication;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class ConversationListPreferenceControllerTest {
|
||||
|
||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
||||
private Context mContext;
|
||||
private final Context mContext = ApplicationProvider.getApplicationContext();
|
||||
@Mock
|
||||
private NotificationBackend mBackend;
|
||||
@Spy
|
||||
private PreferenceGroup mPreferenceGroup = new PreferenceCategory(mContext);
|
||||
|
||||
private TestPreferenceController mController;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
ShadowApplication shadowApplication = ShadowApplication.getInstance();
|
||||
mContext = RuntimeEnvironment.application;
|
||||
mController = new TestPreferenceController(mContext, mBackend);
|
||||
|
||||
PreferenceManager preferenceManager = new PreferenceManager(mContext);
|
||||
PreferenceScreen preferenceScreen = preferenceManager.createPreferenceScreen(mContext);
|
||||
mPreferenceGroup.setKey(mController.getPreferenceKey());
|
||||
preferenceScreen.addPreference(mPreferenceGroup);
|
||||
mController.displayPreference(preferenceScreen);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -84,39 +85,30 @@ public class ConversationListPreferenceControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPopulateList_hideIfNoConversations() {
|
||||
PreferenceCategory outerContainer = mock(PreferenceCategory.class);
|
||||
public void testUpdateList_hideIfNoConversations() {
|
||||
boolean hasContent = mController.updateList(ImmutableList.of());
|
||||
|
||||
mController.populateList(new ArrayList<>(), outerContainer);
|
||||
|
||||
verify(outerContainer).setVisible(false);
|
||||
verify(outerContainer, never()).addPreference(any());
|
||||
assertThat(hasContent).isFalse();
|
||||
verify(mPreferenceGroup).setVisible(false);
|
||||
verify(mPreferenceGroup, never()).addPreference(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPopulateList_validConversations() {
|
||||
final PreferenceManager preferenceManager = new PreferenceManager(mContext);
|
||||
PreferenceScreen ps = preferenceManager.createPreferenceScreen(mContext);
|
||||
PreferenceCategory outerContainer = spy(new PreferenceCategory(mContext));
|
||||
ps.addPreference(outerContainer);
|
||||
|
||||
public void testUpdateList_validConversations() {
|
||||
ConversationChannelWrapper ccw = new ConversationChannelWrapper();
|
||||
ccw.setNotificationChannel(mock(NotificationChannel.class));
|
||||
ccw.setPkg("pkg");
|
||||
ccw.setUid(1);
|
||||
ccw.setShortcutInfo(mock(ShortcutInfo.class));
|
||||
|
||||
ArrayList<ConversationChannelWrapper> list = new ArrayList<>();
|
||||
list.add(ccw);
|
||||
boolean hasContent = mController.updateList(ImmutableList.of(ccw));
|
||||
|
||||
mController.populateList(list, outerContainer);
|
||||
verify(outerContainer, times(1)).addPreference(any());
|
||||
assertThat(hasContent).isTrue();
|
||||
verify(mPreferenceGroup, times(1)).addPreference(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void populateConversations() {
|
||||
PreferenceCategory container = mock(PreferenceCategory.class);
|
||||
|
||||
ConversationChannelWrapper ccw = new ConversationChannelWrapper();
|
||||
ccw.setNotificationChannel(mock(NotificationChannel.class));
|
||||
ccw.setPkg("pkg");
|
||||
@@ -131,13 +123,9 @@ public class ConversationListPreferenceControllerTest {
|
||||
ccwDemoted.setUid(1);
|
||||
ccwDemoted.setShortcutInfo(mock(ShortcutInfo.class));
|
||||
|
||||
ArrayList<ConversationChannelWrapper> list = new ArrayList<>();
|
||||
list.add(ccw);
|
||||
list.add(ccwDemoted);
|
||||
mController.populateConversations(ImmutableList.of(ccw, ccwDemoted));
|
||||
|
||||
mController.populateConversations(list, container);
|
||||
|
||||
verify(container, times(1)).addPreference(any());
|
||||
verify(mPreferenceGroup, times(1)).addPreference(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -240,7 +228,8 @@ public class ConversationListPreferenceControllerTest {
|
||||
assertThat(mController.mConversationComparator.compare(one, two)).isLessThan(0);
|
||||
}
|
||||
|
||||
private final class TestPreferenceController extends ConversationListPreferenceController {
|
||||
private static final class TestPreferenceController extends
|
||||
ConversationListPreferenceController {
|
||||
|
||||
private TestPreferenceController(Context context, NotificationBackend backend) {
|
||||
super(context, backend);
|
||||
|
@@ -37,9 +37,7 @@ import android.os.Bundle;
|
||||
import android.os.UserHandle;
|
||||
import android.provider.Settings;
|
||||
import android.text.SpannedString;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceCategory;
|
||||
@@ -47,42 +45,46 @@ import androidx.preference.PreferenceGroup;
|
||||
import androidx.preference.PreferenceManager;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
import androidx.preference.PreferenceViewHolder;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.applications.AppInfoBase;
|
||||
import com.android.settings.notification.NotificationBackend;
|
||||
import com.android.settingslib.widget.LayoutPreference;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Answers;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.mockito.Spy;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
import org.robolectric.shadows.ShadowApplication;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class RecentConversationsPreferenceControllerTest {
|
||||
|
||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
||||
private Context mContext;
|
||||
private final Context mContext = ApplicationProvider.getApplicationContext();
|
||||
@Mock
|
||||
private NotificationBackend mBackend;
|
||||
@Mock
|
||||
private IPeopleManager mPs;
|
||||
@Spy
|
||||
private PreferenceGroup mPreferenceGroup = new PreferenceCategory(mContext);
|
||||
|
||||
private RecentConversationsPreferenceController mController;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
ShadowApplication shadowApplication = ShadowApplication.getInstance();
|
||||
mContext = RuntimeEnvironment.application;
|
||||
mController = new RecentConversationsPreferenceController(mContext, mBackend, mPs);
|
||||
|
||||
PreferenceManager preferenceManager = new PreferenceManager(mContext);
|
||||
PreferenceScreen preferenceScreen = preferenceManager.createPreferenceScreen(mContext);
|
||||
mPreferenceGroup.setKey(mController.getPreferenceKey());
|
||||
preferenceScreen.addPreference(mPreferenceGroup);
|
||||
mController.displayPreference(preferenceScreen);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -92,38 +94,29 @@ public class RecentConversationsPreferenceControllerTest {
|
||||
|
||||
@Test
|
||||
public void testPopulateList_hideIfNoConversations() {
|
||||
PreferenceCategory outerContainer = mock(PreferenceCategory.class);
|
||||
boolean hasContent = mController.populateList(ImmutableList.of());
|
||||
|
||||
mController.populateList(new ArrayList<>(), outerContainer);
|
||||
|
||||
verify(outerContainer).setVisible(false);
|
||||
verify(outerContainer, never()).addPreference(any());
|
||||
assertThat(hasContent).isFalse();
|
||||
verify(mPreferenceGroup).setVisible(false);
|
||||
verify(mPreferenceGroup, never()).addPreference(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPopulateList_validConversations() {
|
||||
final PreferenceManager preferenceManager = new PreferenceManager(mContext);
|
||||
PreferenceScreen ps = preferenceManager.createPreferenceScreen(mContext);
|
||||
PreferenceCategory outerContainer = spy(new PreferenceCategory(mContext));
|
||||
ps.addPreference(outerContainer);
|
||||
|
||||
ConversationChannel ccw = new ConversationChannel(mock(ShortcutInfo.class), 6,
|
||||
new NotificationChannel("hi", "hi", 4),
|
||||
new NotificationChannelGroup("hi", "hi"), 7,
|
||||
false);
|
||||
|
||||
ArrayList<ConversationChannel> list = new ArrayList<>();
|
||||
list.add(ccw);
|
||||
boolean hasContent = mController.populateList(ImmutableList.of(ccw));
|
||||
|
||||
mController.populateList(list, outerContainer);
|
||||
assertThat(hasContent).isTrue();
|
||||
// one for the preference, one for the button ro clear all
|
||||
verify(outerContainer, times(2)).addPreference(any());
|
||||
verify(mPreferenceGroup, times(2)).addPreference(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void populateConversations_blocked() {
|
||||
PreferenceCategory container = mock(PreferenceCategory.class);
|
||||
|
||||
ConversationChannel ccw = new ConversationChannel(mock(ShortcutInfo.class), 6,
|
||||
new NotificationChannel("hi", "hi", 4),
|
||||
new NotificationChannelGroup("hi", "hi"), 7,
|
||||
@@ -141,14 +134,10 @@ public class RecentConversationsPreferenceControllerTest {
|
||||
blockedGroup, 7,
|
||||
false);
|
||||
|
||||
ArrayList<ConversationChannel> list = new ArrayList<>();
|
||||
list.add(ccw);
|
||||
list.add(ccw2);
|
||||
list.add(ccw3);
|
||||
boolean hasContent = mController.populateConversations(ImmutableList.of(ccw, ccw2, ccw3));
|
||||
|
||||
mController.populateConversations(list, container);
|
||||
|
||||
verify(container, times(1)).addPreference(any());
|
||||
assertThat(hasContent).isTrue();
|
||||
verify(mPreferenceGroup, times(1)).addPreference(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -223,8 +212,7 @@ public class RecentConversationsPreferenceControllerTest {
|
||||
new NotificationChannelGroup("hi", "group"), 7,
|
||||
true);
|
||||
|
||||
Preference pref = mController.createConversationPref(new PreferenceCategory(mContext),
|
||||
ccw, 100);
|
||||
Preference pref = mController.createConversationPref(ccw);
|
||||
try {
|
||||
pref.performClick();
|
||||
} catch (RuntimeException e) {
|
||||
@@ -244,9 +232,7 @@ public class RecentConversationsPreferenceControllerTest {
|
||||
new NotificationChannelGroup("hi", "group"), 7,
|
||||
false);
|
||||
|
||||
RecentConversationPreference pref =
|
||||
(RecentConversationPreference) mController.createConversationPref(
|
||||
new PreferenceCategory(mContext), ccw, 100);
|
||||
RecentConversationPreference pref = mController.createConversationPref(ccw);
|
||||
final View view = View.inflate(mContext, pref.getLayoutResource(), null);
|
||||
PreferenceViewHolder holder = spy(PreferenceViewHolder.createInstanceForTests(view));
|
||||
View delete = View.inflate(mContext, pref.getSecondTargetResId(), null);
|
||||
@@ -274,34 +260,28 @@ public class RecentConversationsPreferenceControllerTest {
|
||||
new NotificationChannelGroup("hi", "group"), 7,
|
||||
true);
|
||||
|
||||
PreferenceCategory group = new PreferenceCategory(mContext);
|
||||
PreferenceScreen screen = new PreferenceManager(mContext).createPreferenceScreen(mContext);
|
||||
screen.addPreference(group);
|
||||
|
||||
RecentConversationPreference pref = mController.createConversationPref(
|
||||
new PreferenceCategory(mContext), ccw, 100);
|
||||
RecentConversationPreference pref = mController.createConversationPref(ccw);
|
||||
final View view = View.inflate(mContext, pref.getLayoutResource(), null);
|
||||
PreferenceViewHolder holder = spy(PreferenceViewHolder.createInstanceForTests(view));
|
||||
View delete = View.inflate(mContext, pref.getSecondTargetResId(), null);
|
||||
when(holder.findViewById(pref.getClearId())).thenReturn(delete);
|
||||
group.addPreference(pref);
|
||||
mPreferenceGroup.addPreference(pref);
|
||||
|
||||
RecentConversationPreference pref2 = mController.createConversationPref(
|
||||
new PreferenceCategory(mContext), ccw2, 100);
|
||||
RecentConversationPreference pref2 = mController.createConversationPref(ccw2);
|
||||
final View view2 = View.inflate(mContext, pref2.getLayoutResource(), null);
|
||||
PreferenceViewHolder holder2 = spy(PreferenceViewHolder.createInstanceForTests(view2));
|
||||
View delete2 = View.inflate(mContext, pref2.getSecondTargetResId(), null);
|
||||
when(holder2.findViewById(pref.getClearId())).thenReturn(delete2);
|
||||
group.addPreference(pref2);
|
||||
mPreferenceGroup.addPreference(pref2);
|
||||
|
||||
LayoutPreference clearAll = mController.getClearAll(group);
|
||||
group.addPreference(clearAll);
|
||||
LayoutPreference clearAll = mController.getClearAll(mPreferenceGroup);
|
||||
mPreferenceGroup.addPreference(clearAll);
|
||||
|
||||
clearAll.findViewById(R.id.conversation_settings_clear_recents).performClick();
|
||||
|
||||
verify(mPs).removeAllRecentConversations();
|
||||
assertThat((Preference) group.findPreference("hi:person")).isNull();
|
||||
assertThat((Preference) group.findPreference("bye:person")).isNotNull();
|
||||
assertThat((Preference) mPreferenceGroup.findPreference("hi:person")).isNull();
|
||||
assertThat((Preference) mPreferenceGroup.findPreference("bye:person")).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -314,30 +294,23 @@ public class RecentConversationsPreferenceControllerTest {
|
||||
new NotificationChannelGroup("hi", "group"), 7,
|
||||
true);
|
||||
|
||||
RecentConversationPreference pref =
|
||||
(RecentConversationPreference) mController.createConversationPref(
|
||||
new PreferenceCategory(mContext), ccw, 100);
|
||||
RecentConversationPreference pref = mController.createConversationPref(ccw);
|
||||
|
||||
assertThat(pref.hasClearListener()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPopulateList_onlyNonremoveableConversations() {
|
||||
final PreferenceManager preferenceManager = new PreferenceManager(mContext);
|
||||
PreferenceScreen ps = preferenceManager.createPreferenceScreen(mContext);
|
||||
PreferenceCategory outerContainer = spy(new PreferenceCategory(mContext));
|
||||
ps.addPreference(outerContainer);
|
||||
|
||||
ConversationChannel ccw = new ConversationChannel(mock(ShortcutInfo.class), 6,
|
||||
new NotificationChannel("hi", "hi", 4),
|
||||
new NotificationChannelGroup("hi", "hi"), 7,
|
||||
true /* hasactivenotifs */);
|
||||
|
||||
ArrayList<ConversationChannel> list = new ArrayList<>();
|
||||
list.add(ccw);
|
||||
boolean hasContent = mController.populateList(ImmutableList.of(ccw));
|
||||
|
||||
mController.populateList(list, outerContainer);
|
||||
assertThat(hasContent).isTrue();
|
||||
// one for the preference, none for 'clear all'
|
||||
verify(outerContainer, times(1)).addPreference(any());
|
||||
verify(mPreferenceGroup, times(1)).addPreference(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
Reference in New Issue
Block a user