/* * Copyright (C) 2016 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 android.text; import android.app.Activity; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Paint.FontMetricsInt; import android.os.Bundle; import android.perftests.utils.BenchmarkState; import android.perftests.utils.PerfStatusReporter; import android.perftests.utils.StubActivity; import android.support.test.InstrumentationRegistry; import android.support.test.filters.LargeTest; import android.support.test.rule.ActivityTestRule; import android.support.test.runner.AndroidJUnit4; import android.text.style.ReplacementSpan; import android.util.ArraySet; import static android.text.Layout.Alignment.ALIGN_NORMAL; import java.util.Arrays; import java.util.Collection; import java.util.Locale; import java.util.Random; import org.junit.Assert; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized.Parameters; import org.junit.runners.Parameterized; @LargeTest @RunWith(Parameterized.class) public class DynamicLayoutPerfTest { @Parameters(name = "{0}") public static Collection cases() { return Arrays.asList(new Object[][] { { "0%", 0.0f}, { "1%", 0.01f}, { "5%", 0.05f}, { "30%", 0.3f}, { "100%", 1.0f}, }); } private final String mMetricKey; private final float mProbability; public DynamicLayoutPerfTest(String metricKey, float probability) { mMetricKey = metricKey; mProbability = probability; } private static class MockReplacementSpan extends ReplacementSpan { @Override public int getSize(Paint paint, CharSequence text, int start, int end, FontMetricsInt fm) { return 10; } @Override public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint) { } } @Rule public ActivityTestRule mActivityRule = new ActivityTestRule(StubActivity.class); @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); private final static String ALPHABETS = "abcdefghijklmnopqrstuvwxyz"; private SpannableStringBuilder getText() { final long seed = 1234567890; final Random r = new Random(seed); final SpannableStringBuilder builder = new SpannableStringBuilder(); final int paragraphCount = 100; for (int i = 0; i < paragraphCount; i++) { final int wordCount = 5 + r.nextInt(20); final boolean containsReplacementSpan = r.nextFloat() < mProbability; final int replacedWordIndex = containsReplacementSpan ? r.nextInt(wordCount) : -1; for (int j = 0; j < wordCount; j++) { final int startIndex = builder.length(); final int wordLength = 1 + r.nextInt(10); for (int k = 0; k < wordLength; k++) { char c = ALPHABETS.charAt(r.nextInt(ALPHABETS.length())); builder.append(c); } if (replacedWordIndex == j) { builder.setSpan(new MockReplacementSpan(), startIndex, builder.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); } builder.append(' '); } builder.append('\n'); } return builder; } @Test public void testGetBlocksAlwaysNeedToBeRedrawn() { final SpannableStringBuilder text = getText(); final DynamicLayout layout = new DynamicLayout(text, new TextPaint(), 1000, ALIGN_NORMAL, 0, 0, false); BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); final int steps = 10; while (state.keepRunning()) { for (int i = 0; i < steps; i++) { int offset = (text.length() * i) / steps; text.insert(offset, "\n"); text.delete(offset, offset + 1); final ArraySet set = layout.getBlocksAlwaysNeedToBeRedrawn(); if (set != null) { for (int j = 0; j < set.size(); j++) { layout.getBlockIndex(set.valueAt(j)); } } } } } }