Commit a42eb196 authored by Theresa Wellington's avatar Theresa Wellington Committed by Commit Bot

Change Show in folder to single pulse + test

Bug: 994293
Change-Id: I2446ed845f08b5173ac25fe836e50b114f58cf45
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1756545
Commit-Queue: Theresa  <twellington@chromium.org>
Reviewed-by: default avatarDavid Trainor <dtrainor@chromium.org>
Cr-Commit-Position: refs/heads/master@{#688295}
parent 10e7f893
...@@ -218,8 +218,10 @@ class ReorderBookmarkItemsAdapter extends DragReorderableListAdapter<BookmarkIte ...@@ -218,8 +218,10 @@ class ReorderBookmarkItemsAdapter extends DragReorderableListAdapter<BookmarkIte
}); });
// Turn on the highlight for the currently highlighted bookmark. // Turn on the highlight for the currently highlighted bookmark.
if (id.equals(mHighlightedBookmark)) { if (id.equals(mHighlightedBookmark)) {
ViewHighlighter.turnOnHighlight(holder.itemView, false); ViewHighlighter.pulseHighlight(holder.itemView, false, 1);
clearHighlight();
} else { } else {
// We need this in case we are change state during a pulse.
ViewHighlighter.turnOffHighlight(holder.itemView); ViewHighlighter.turnOffHighlight(holder.itemView);
} }
} }
......
...@@ -33,6 +33,32 @@ public class PulseDrawable extends Drawable implements Animatable { ...@@ -33,6 +33,32 @@ public class PulseDrawable extends Drawable implements Animatable {
private static final long PULSE_DURATION_MS = 2500; private static final long PULSE_DURATION_MS = 2500;
private static final long FRAME_RATE = 60; private static final long FRAME_RATE = 60;
/**
* Informs the PulseDrawable about whether it can continue pulsing, and specifies a callback to
* be run when the PulseDrawable is finished pulsing.
*/
public interface PulseEndAuthority {
/**
* Called at the end of one pulse animation, to decide whether the PulseDrawable can pulse
* again.
*
* @return True iff the PulseDrawable can continue pulsing.
*/
boolean canPulseAgain();
}
/**
* A PulseEndAuthority which allows the PulseDrawable to pulse forever.
*/
private static class EndlessPulser implements PulseEndAuthority {
// PulseEndAuthority implementation.
@Override
public boolean canPulseAgain() {
return true;
}
}
/** /**
* An interface that does the actual drawing work for this {@link Drawable}. Not meant to be * An interface that does the actual drawing work for this {@link Drawable}. Not meant to be
* stateful, as this could be shared across multiple instances of this drawable if it gets * stateful, as this could be shared across multiple instances of this drawable if it gets
...@@ -80,7 +106,7 @@ public class PulseDrawable extends Drawable implements Animatable { ...@@ -80,7 +106,7 @@ public class PulseDrawable extends Drawable implements Animatable {
} }
private static Painter createCirclePainter(Bounds boundsFn) { private static Painter createCirclePainter(Bounds boundsFn) {
return new PulseDrawable.Painter() { return new Painter() {
@Override @Override
public void modifyDrawable(PulseDrawable drawable, float interpolation) { public void modifyDrawable(PulseDrawable drawable, float interpolation) {
drawable.invalidateSelf(); drawable.invalidateSelf();
...@@ -93,7 +119,8 @@ public class PulseDrawable extends Drawable implements Animatable { ...@@ -93,7 +119,8 @@ public class PulseDrawable extends Drawable implements Animatable {
float minRadiusPx = boundsFn.getMinRadiusPx(bounds); float minRadiusPx = boundsFn.getMinRadiusPx(bounds);
float maxRadiusPx = boundsFn.getMaxRadiusPx(bounds); float maxRadiusPx = boundsFn.getMaxRadiusPx(bounds);
float radius = MathUtils.interpolate(minRadiusPx, maxRadiusPx, interpolation); float radius =
MathUtils.interpolate(minRadiusPx, maxRadiusPx, 1.0f - interpolation);
canvas.drawCircle(bounds.exactCenterX(), bounds.exactCenterY(), radius, paint); canvas.drawCircle(bounds.exactCenterX(), bounds.exactCenterY(), radius, paint);
} }
...@@ -103,13 +130,15 @@ public class PulseDrawable extends Drawable implements Animatable { ...@@ -103,13 +130,15 @@ public class PulseDrawable extends Drawable implements Animatable {
/** /**
* Creates a {@link PulseDrawable} that will fill the bounds with a pulsing color. * Creates a {@link PulseDrawable} that will fill the bounds with a pulsing color.
* @param context The {@link Context} under which the drawable is created. * @param context The {@link Context} under which the drawable is created.
* @param pulseEndAuthority The {@link PulseEndAuthority} associated with this drawable.
* @return A new {@link PulseDrawable} instance. * @return A new {@link PulseDrawable} instance.
*/ */
public static PulseDrawable createHighlight(Context context) { public static PulseDrawable createHighlight(
PulseDrawable.Painter painter = new PulseDrawable.Painter() { Context context, PulseEndAuthority pulseEndAuthority) {
Painter painter = new Painter() {
@Override @Override
public void modifyDrawable(PulseDrawable drawable, float interpolation) { public void modifyDrawable(PulseDrawable drawable, float interpolation) {
drawable.setAlpha((int) MathUtils.interpolate(12, 75, 1.f - interpolation)); drawable.setAlpha((int) MathUtils.interpolate(12, 75, interpolation));
} }
@Override @Override
...@@ -119,15 +148,28 @@ public class PulseDrawable extends Drawable implements Animatable { ...@@ -119,15 +148,28 @@ public class PulseDrawable extends Drawable implements Animatable {
} }
}; };
return new PulseDrawable(context, new FastOutSlowInInterpolator(), painter); return new PulseDrawable(
context, new FastOutSlowInInterpolator(), painter, pulseEndAuthority);
}
/**
* Creates a {@link PulseDrawable} that will fill the bounds with a pulsing color. The {@link
* PulseDrawable} will continue pulsing forever (if this is not the desired behavior, please use
* {@link PulseEndAuthority}).
* @param context The {@link Context} under which the drawable is created.
* @return A new {@link PulseDrawable} instance.
*/
public static PulseDrawable createHighlight(Context context) {
return createHighlight(context, new EndlessPulser());
} }
/** /**
* Creates a {@link PulseDrawable} that will draw a pulsing circle inside the bounds. * Creates a {@link PulseDrawable} that will draw a pulsing circle inside the bounds.
* @param context The {@link Context} under which the drawable is created. * @param context The {@link Context} under which the drawable is created.
* @param pulseEndAuthority The {@link PulseEndAuthority} associated with this drawable.
* @return A new {@link PulseDrawable} instance. * @return A new {@link PulseDrawable} instance.
*/ */
public static PulseDrawable createCircle(Context context) { public static PulseDrawable createCircle(Context context, PulseEndAuthority pulseEndAuthority) {
final int startingPulseRadiusPx = final int startingPulseRadiusPx =
context.getResources().getDimensionPixelSize(R.dimen.iph_pulse_baseline_radius); context.getResources().getDimensionPixelSize(R.dimen.iph_pulse_baseline_radius);
...@@ -142,7 +184,18 @@ public class PulseDrawable extends Drawable implements Animatable { ...@@ -142,7 +184,18 @@ public class PulseDrawable extends Drawable implements Animatable {
return Math.min( return Math.min(
startingPulseRadiusPx, Math.min(bounds.width(), bounds.height()) / 2.f); startingPulseRadiusPx, Math.min(bounds.width(), bounds.height()) / 2.f);
} }
}); }, pulseEndAuthority);
}
/**
* Creates a {@link PulseDrawable} that will draw a pulsing circle inside the bounds. The {@link
* PulseDrawable} will continue pulsing forever (if this is not the desired behavior, please use
* {@link PulseEndAuthority}).
* @param context The {@link Context} under which the drawable is created.
* @return A new {@link PulseDrawable} instance.
*/
public static PulseDrawable createCircle(Context context) {
return createCircle(context, new EndlessPulser());
} }
/** /**
...@@ -151,15 +204,26 @@ public class PulseDrawable extends Drawable implements Animatable { ...@@ -151,15 +204,26 @@ public class PulseDrawable extends Drawable implements Animatable {
* @param context The {@link Context} under which the drawable is created. * @param context The {@link Context} under which the drawable is created.
* @return A new {@link PulseDrawable} instance. * @return A new {@link PulseDrawable} instance.
*/ */
public static PulseDrawable createCustomCircle(Context context, Bounds boundsfn) { public static PulseDrawable createCustomCircle(
Context context, Bounds boundsfn, PulseEndAuthority pulseEndAuthority) {
Painter painter = createCirclePainter(boundsfn); Painter painter = createCirclePainter(boundsfn);
PulseDrawable drawable = new PulseDrawable( PulseDrawable drawable = new PulseDrawable(context,
context, PathInterpolatorCompat.create(.8f, 0.f, .6f, 1.f), painter); PathInterpolatorCompat.create(.8f, 0.f, .6f, 1.f), painter, pulseEndAuthority);
drawable.setAlpha(76); drawable.setAlpha(76);
return drawable; return drawable;
} }
/**
* Creates a {@link PulseDrawable} that will draw a pulsing circle as large as possible inside
* the bounds.
* @param context The {@link Context} under which the drawable is created.
* @return A new {@link PulseDrawable} instance.
*/
public static PulseDrawable createCustomCircle(Context context, Bounds boundsfn) {
return createCustomCircle(context, boundsfn, new EndlessPulser());
}
private final Runnable mNextFrame = new Runnable() { private final Runnable mNextFrame = new Runnable() {
@Override @Override
public void run() { public void run() {
...@@ -176,20 +240,30 @@ public class PulseDrawable extends Drawable implements Animatable { ...@@ -176,20 +240,30 @@ public class PulseDrawable extends Drawable implements Animatable {
private PulseState mState; private PulseState mState;
private boolean mMutated; private boolean mMutated;
private boolean mRunning; private boolean mRunning;
private long mLastUpdateTime;
private final PulseEndAuthority mPulseEndAuthority;
/** /**
* Creates a new {@link PulseDrawable} instance. * Creates a new {@link PulseDrawable} instance.
* @param context The {@link Context} under which the drawable is created. * @param context The {@link Context} under which the drawable is created.
* @param interpolator An {@link Interpolator} that defines how the pulse will fade in and out. * @param interpolator An {@link Interpolator} that defines how the pulse will fade in and out.
* @param painter The {@link Painter} that will be responsible for drawing the pulse. * @param painter The {@link Painter} that will be responsible for drawing the pulse.
* @param pulseEndAuthority The {@link PulseEndAuthority} that is associated with this drawable.
*/ */
private PulseDrawable(Context context, Interpolator interpolator, Painter painter) { private PulseDrawable(Context context, Interpolator interpolator, Painter painter,
this(new PulseState(interpolator, painter)); PulseEndAuthority pulseEndAuthority) {
this(new PulseState(interpolator, painter), pulseEndAuthority);
setUseLightPulseColor(context.getResources(), false); setUseLightPulseColor(context.getResources(), false);
} }
private PulseDrawable(PulseState state) { private PulseDrawable(PulseState state, PulseEndAuthority pulseEndAuthority) {
mState = state; mState = state;
mPulseEndAuthority = pulseEndAuthority;
}
private PulseDrawable(PulseState state) {
this(state, new EndlessPulser());
} }
/** /**
...@@ -222,7 +296,10 @@ public class PulseDrawable extends Drawable implements Animatable { ...@@ -222,7 +296,10 @@ public class PulseDrawable extends Drawable implements Animatable {
scheduleSelf(mNextFrame, SystemClock.uptimeMillis() + 1000 / FRAME_RATE); scheduleSelf(mNextFrame, SystemClock.uptimeMillis() + 1000 / FRAME_RATE);
} else { } else {
mRunning = true; mRunning = true;
if (mState.startTime == 0) mState.startTime = SystemClock.uptimeMillis(); if (mState.startTime == 0) {
mState.startTime = SystemClock.uptimeMillis();
mLastUpdateTime = mState.startTime;
}
mNextFrame.run(); mNextFrame.run();
} }
} }
...@@ -312,16 +389,25 @@ public class PulseDrawable extends Drawable implements Animatable { ...@@ -312,16 +389,25 @@ public class PulseDrawable extends Drawable implements Animatable {
private void stepPulse() { private void stepPulse() {
long curTime = SystemClock.uptimeMillis(); long curTime = SystemClock.uptimeMillis();
// If we are on a new pulse
if ((mLastUpdateTime - mState.startTime) / PULSE_DURATION_MS
!= (curTime - mState.startTime) / PULSE_DURATION_MS) {
if (!(mPulseEndAuthority.canPulseAgain())) {
stop();
return;
}
}
long msIntoAnim = (curTime - mState.startTime) % PULSE_DURATION_MS; long msIntoAnim = (curTime - mState.startTime) % PULSE_DURATION_MS;
float progress = ((float) msIntoAnim) / ((float) PULSE_DURATION_MS); float timeProgress = ((float) msIntoAnim) / ((float) PULSE_DURATION_MS);
mState.progress = mState.interpolator.getInterpolation(progress); mState.progress = mState.interpolator.getInterpolation(timeProgress);
mState.painter.modifyDrawable(PulseDrawable.this, mState.progress); mState.painter.modifyDrawable(PulseDrawable.this, mState.progress);
mLastUpdateTime = curTime;
} }
/** /**
* The {@link ConstantState} subclass for this {@link PulseDrawable}. * The {@link ConstantState} subclass for this {@link PulseDrawable}.
*/ */
private static final class PulseState extends ConstantState { static final class PulseState extends ConstantState {
// Current Paint State. // Current Paint State.
/** The current color, including alpha, to draw. */ /** The current color, including alpha, to draw. */
public int drawColor; public int drawColor;
......
...@@ -23,8 +23,8 @@ public class PulseInterpolator implements Interpolator { ...@@ -23,8 +23,8 @@ public class PulseInterpolator implements Interpolator {
@Override @Override
public float getInterpolation(float input) { public float getInterpolation(float input) {
if (input < 0.4f) return 0.f; if (input < 0.2) return mInterpolator.getInterpolation(input / 0.2f);
if (input < 0.8f) return mInterpolator.getInterpolation((input - 0.4f) / 0.4f); if (input < 0.6) return 1.f;
return mInterpolator.getInterpolation(1.f - (input - 0.8f) / 0.2f); return mInterpolator.getInterpolation(1.f - (input - 0.6f) / 0.4f);
} }
} }
\ No newline at end of file
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
package org.chromium.chrome.browser.widget; package org.chromium.chrome.browser.widget;
import static org.chromium.chrome.browser.widget.PulseDrawable.createCircle;
import android.content.res.Resources; import android.content.res.Resources;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable; import android.graphics.drawable.LayerDrawable;
...@@ -27,13 +29,44 @@ public class ViewHighlighter { ...@@ -27,13 +29,44 @@ public class ViewHighlighter {
*/ */
public static final int IPH_MIN_DELAY_BETWEEN_TWO_HIGHLIGHTS = 200; public static final int IPH_MIN_DELAY_BETWEEN_TWO_HIGHLIGHTS = 200;
/**
* Allows its associated PulseDrawable to pulse a specified number of times, then turns off the
* PulseDrawable highlight.
*/
public static class NumberPulser implements PulseDrawable.PulseEndAuthority {
private final View mView;
private int mNumPulsesRemaining;
NumberPulser(View view, int numPulses) {
mView = view;
mNumPulsesRemaining = numPulses;
}
@Override
public boolean canPulseAgain() {
mNumPulsesRemaining--;
if (mNumPulsesRemaining == 0) ViewHighlighter.turnOffHighlight(mView);
return mNumPulsesRemaining > 0;
}
}
public static void pulseHighlight(View view, boolean circular, int numPulses) {
if (view == null) return;
PulseDrawable pulseDrawable = circular
? createCircle(view.getContext(), new NumberPulser(view, numPulses))
: PulseDrawable.createHighlight(
view.getContext(), new NumberPulser(view, numPulses));
attachViewAsHighlight(view, pulseDrawable);
}
/** /**
* Create a highlight layer over the view. * Create a highlight layer over the view.
* @param view The view to be highlighted. * @param view The view to be highlighted.
* @param circular Whether the highlight should be a circle or rectangle. * @param circular Whether the highlight should be a circle or rectangle.
*/ */
public static void turnOnHighlight(View view, boolean circular) { public static void turnOnHighlight(View view, boolean circular) {
if (view == null) return;
PulseDrawable pulseDrawable = circular ? PulseDrawable.createCircle(view.getContext()) PulseDrawable pulseDrawable = circular ? PulseDrawable.createCircle(view.getContext())
: PulseDrawable.createHighlight(view.getContext()); : PulseDrawable.createHighlight(view.getContext());
......
...@@ -9,6 +9,7 @@ import android.graphics.drawable.LayerDrawable; ...@@ -9,6 +9,7 @@ import android.graphics.drawable.LayerDrawable;
import android.view.View; import android.view.View;
import org.chromium.chrome.browser.widget.PulseDrawable; import org.chromium.chrome.browser.widget.PulseDrawable;
import org.chromium.content_public.browser.test.util.CriteriaHelper;
/** /**
* Allows for testing of views which are highlightable via ViewHighlighter. * Allows for testing of views which are highlightable via ViewHighlighter.
...@@ -40,4 +41,39 @@ public class ViewHighlighterTestUtils { ...@@ -40,4 +41,39 @@ public class ViewHighlighterTestUtils {
public static boolean checkHighlightOff(View view) { public static boolean checkHighlightOff(View view) {
return !(view.getBackground() instanceof LayerDrawable); return !(view.getBackground() instanceof LayerDrawable);
} }
/**
* Checks that the view is highlighted with a pulse highlight.
*
* @param view The view of interest.
* @param timeoutDuration The timeout duration (should be set depending on the number of pulses
* and the pulse duration).
* @return True iff the view was highlighted, and then turned off.
*/
public static boolean checkHighlightPulse(View view, long timeoutDuration) {
try {
CriteriaHelper.pollUiThread(()
-> checkHighlightOn(view),
"Expected highlight to pulse on!", timeoutDuration,
CriteriaHelper.DEFAULT_POLLING_INTERVAL);
CriteriaHelper.pollUiThread(()
-> checkHighlightOff(view),
"Expected highlight to turn off!", timeoutDuration,
CriteriaHelper.DEFAULT_POLLING_INTERVAL);
} catch (AssertionError e) {
e.printStackTrace();
return false;
}
return true;
}
/**
* Checks that the view is highlighted with a pulse highlight.
*
* @param view The view of interest.
* @return True iff the view was highlighted, and then turned off.
*/
public static boolean checkHighlightPulse(View view) {
return checkHighlightPulse(view, CriteriaHelper.DEFAULT_MAX_TIME_TO_POLL);
}
} }
\ No newline at end of file
...@@ -10,7 +10,7 @@ import static android.support.test.espresso.assertion.ViewAssertions.doesNotExis ...@@ -10,7 +10,7 @@ import static android.support.test.espresso.assertion.ViewAssertions.doesNotExis
import static android.support.test.espresso.matcher.ViewMatchers.withText; import static android.support.test.espresso.matcher.ViewMatchers.withText;
import static org.chromium.chrome.browser.ViewHighlighterTestUtils.checkHighlightOff; import static org.chromium.chrome.browser.ViewHighlighterTestUtils.checkHighlightOff;
import static org.chromium.chrome.browser.ViewHighlighterTestUtils.checkHighlightOn; import static org.chromium.chrome.browser.ViewHighlighterTestUtils.checkHighlightPulse;
import android.support.test.filters.MediumTest; import android.support.test.filters.MediumTest;
import android.support.v7.widget.RecyclerView.ViewHolder; import android.support.v7.widget.RecyclerView.ViewHolder;
...@@ -628,9 +628,9 @@ public class BookmarkReorderTest extends BookmarkTest { ...@@ -628,9 +628,9 @@ public class BookmarkReorderTest extends BookmarkTest {
TestThreadUtils.runOnUiThreadBlocking(more::performClick); TestThreadUtils.runOnUiThreadBlocking(more::performClick);
onView(withText("Show in folder")).perform(click()); onView(withText("Show in folder")).perform(click());
Assert.assertTrue( // Assert that the view pulses.
"Expected bookmark row to be highlighted after clicking \"show in folder\"", Assert.assertTrue("Expected bookmark row to pulse after clicking \"show in folder\"!",
checkHighlightOn(testFolder)); checkHighlightPulse(testFolder));
// Enter search mode again. // Enter search mode again.
searchButton = mManager.getToolbarForTests().findViewById(R.id.search_menu_id); searchButton = mManager.getToolbarForTests().findViewById(R.id.search_menu_id);
...@@ -645,11 +645,9 @@ public class BookmarkReorderTest extends BookmarkTest { ...@@ -645,11 +645,9 @@ public class BookmarkReorderTest extends BookmarkTest {
// Click "Show in folder" again. // Click "Show in folder" again.
TestThreadUtils.runOnUiThreadBlocking(more::performClick); TestThreadUtils.runOnUiThreadBlocking(more::performClick);
onView(withText("Show in folder")).perform(click()); onView(withText("Show in folder")).perform(click());
Assert.assertTrue(
// Check that the highlight is on. "Expected bookmark row to pulse after clicking \"show in folder\" a 2nd time!",
Assert.assertTrue("Expected highlight to successfully come back on" checkHighlightPulse(testFolder));
+ " after clicking \"show in folder\" a 2nd time",
checkHighlightOn(testFolder));
} }
@Test @Test
...@@ -691,8 +689,8 @@ public class BookmarkReorderTest extends BookmarkTest { ...@@ -691,8 +689,8 @@ public class BookmarkReorderTest extends BookmarkTest {
"Expected list to scroll bookmark item into view", testFolderInList == null); "Expected list to scroll bookmark item into view", testFolderInList == null);
Assert.assertEquals("Wrong bookmark item selected.", TEST_FOLDER_TITLE, Assert.assertEquals("Wrong bookmark item selected.", TEST_FOLDER_TITLE,
((BookmarkFolderRow) testFolderInList.itemView).getTitle()); ((BookmarkFolderRow) testFolderInList.itemView).getTitle());
Assert.assertTrue("Expected bookmark item to be highlighted after scrolling to it.", Assert.assertTrue("Expected highlight to pulse on after scrolling to the item!",
checkHighlightOn(testFolderInList.itemView)); checkHighlightPulse(testFolderInList.itemView));
} }
@Test @Test
...@@ -726,9 +724,9 @@ public class BookmarkReorderTest extends BookmarkTest { ...@@ -726,9 +724,9 @@ public class BookmarkReorderTest extends BookmarkTest {
View itemA = mItemsContainer.findViewHolderForAdapterPosition(1).itemView; View itemA = mItemsContainer.findViewHolderForAdapterPosition(1).itemView;
Assert.assertEquals("Wrong bookmark item selected.", TEST_TITLE_A, Assert.assertEquals("Wrong bookmark item selected.", TEST_TITLE_A,
((BookmarkItemRow) itemA).getTitle()); ((BookmarkItemRow) itemA).getTitle());
Assert.assertTrue(
"Expected bookmark item to be highlighted after opening it in new folder.", Assert.assertTrue("Expected highlight to pulse after opening an item in another folder!",
checkHighlightOn(itemA)); checkHighlightPulse(itemA));
// Open mobile bookmarks folder, then go back to the subfolder. // Open mobile bookmarks folder, then go back to the subfolder.
TestThreadUtils.runOnUiThreadBlocking(() -> { TestThreadUtils.runOnUiThreadBlocking(() -> {
...@@ -737,12 +735,12 @@ public class BookmarkReorderTest extends BookmarkTest { ...@@ -737,12 +735,12 @@ public class BookmarkReorderTest extends BookmarkTest {
}); });
RecyclerViewTestUtils.waitForStableRecyclerView(mItemsContainer); RecyclerViewTestUtils.waitForStableRecyclerView(mItemsContainer);
itemA = mItemsContainer.findViewHolderForAdapterPosition(1).itemView; View itemASecondView = mItemsContainer.findViewHolderForAdapterPosition(1).itemView;
Assert.assertEquals("Wrong bookmark item selected.", TEST_TITLE_A, Assert.assertEquals("Wrong bookmark item selected.", TEST_TITLE_A,
((BookmarkItemRow) itemA).getTitle()); ((BookmarkItemRow) itemASecondView).getTitle());
Assert.assertTrue("Expected bookmark item to not be highlighted after " Assert.assertTrue(
+ "exiting and re-entering folder.", "Expected highlight to not be highlighted after exiting and re-entering folder!",
checkHighlightOff(itemA)); checkHighlightOff(itemASecondView));
} }
@Override @Override
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment