blob: 0ab086cae7916c0b716ff77540ff2a33c3b81231 [file] [log] [blame]
/*
* Copyright (C) 2017 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.widget.cts;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.PointF;
import android.graphics.Rect;
import android.support.test.annotation.UiThreadTest;
import android.support.test.filters.SmallTest;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
import android.util.DisplayMetrics;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.LinearLayout.LayoutParams;
import android.widget.Magnifier;
import com.android.compatibility.common.util.PollingCheck;
import com.android.compatibility.common.util.WidgetTestUtils;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
* Tests for {@link Magnifier}.
*/
@SmallTest
@RunWith(AndroidJUnit4.class)
public class MagnifierTest {
private static final String TIME_LIMIT_EXCEEDED =
"Completing the magnifier operation took too long";
private Activity mActivity;
private LinearLayout mLayout;
private Magnifier mMagnifier;
@Rule
public ActivityTestRule<MagnifierCtsActivity> mActivityRule =
new ActivityTestRule<>(MagnifierCtsActivity.class);
@Before
public void setup() {
mActivity = mActivityRule.getActivity();
PollingCheck.waitFor(mActivity::hasWindowFocus);
mLayout = mActivity.findViewById(R.id.magnifier_activity_basic_layout);
// Do not run the tests, unless the device screen is big enough to fit the magnifier.
assumeTrue(isScreenBigEnough());
}
private boolean isScreenBigEnough() {
// Get the size of the screen in dp.
final DisplayMetrics displayMetrics = mActivity.getResources().getDisplayMetrics();
final float dpScreenWidth = displayMetrics.widthPixels / displayMetrics.density;
final float dpScreenHeight = displayMetrics.heightPixels / displayMetrics.density;
// Get the size of the magnifier window in dp.
final PointF dpMagnifier = Magnifier.getMagnifierDefaultSize();
return dpScreenWidth >= dpMagnifier.x * 1.1 && dpScreenHeight >= dpMagnifier.y * 1.1;
}
@Test
public void testConstructor() {
new Magnifier(new View(mActivity));
}
@Test(expected = NullPointerException.class)
public void testConstructor_NPE() {
new Magnifier(null);
}
@Test
@UiThreadTest
public void testShow() {
final View view = new View(mActivity);
mLayout.addView(view, new LayoutParams(200, 200));
mMagnifier = new Magnifier(view);
// Valid coordinates.
mMagnifier.show(0, 0);
// Invalid coordinates, should both be clamped to 0.
mMagnifier.show(-1, -1);
// Valid coordinates.
mMagnifier.show(10, 10);
// Same valid coordinate as above, should skip making another copy request.
mMagnifier.show(10, 10);
}
@Test
@UiThreadTest
public void testDismiss() {
final View view = new View(mActivity);
mLayout.addView(view, new LayoutParams(200, 200));
mMagnifier = new Magnifier(view);
// Valid coordinates.
mMagnifier.show(10, 10);
mMagnifier.dismiss();
// Should be no-op.
mMagnifier.dismiss();
}
@Test
@UiThreadTest
public void testUpdate() {
final View view = new View(mActivity);
mLayout.addView(view, new LayoutParams(200, 200));
mMagnifier = new Magnifier(view);
// Should be no-op.
mMagnifier.update();
// Valid coordinates.
mMagnifier.show(10, 10);
// Should not crash.
mMagnifier.update();
mMagnifier.dismiss();
// Should be no-op.
mMagnifier.update();
}
@Test
@UiThreadTest
public void testSizeAndZoom_areValid() {
final View view = new View(mActivity);
mLayout.addView(view, new LayoutParams(200, 200));
mMagnifier = new Magnifier(view);
mMagnifier.show(10, 10);
// Size should be non-zero.
assertTrue(mMagnifier.getWidth() > 0);
assertTrue(mMagnifier.getHeight() > 0);
// The magnified view region should be zoomed in, not out.
assertTrue(mMagnifier.getZoom() > 1.0f);
}
@Test
public void testWindowContent() throws Throwable {
prepareFourQuadrantsScenario();
final CountDownLatch latch = new CountDownLatch(1);
mMagnifier.setOnOperationCompleteCallback(latch::countDown);
// Show the magnifier at the center of the activity.
mActivityRule.runOnUiThread(() -> {
mMagnifier.show(mLayout.getWidth() / 2, mLayout.getHeight() / 2);
});
assertTrue(TIME_LIMIT_EXCEEDED, latch.await(1, TimeUnit.SECONDS));
assertEquals(mMagnifier.getWidth(), mMagnifier.getContent().getWidth());
assertEquals(mMagnifier.getHeight(), mMagnifier.getContent().getHeight());
assertFourQuadrants(mMagnifier.getContent());
}
@Test
public void testWindowPosition() throws Throwable {
prepareFourQuadrantsScenario();
final CountDownLatch latch = new CountDownLatch(1);
mMagnifier.setOnOperationCompleteCallback(latch::countDown);
// Show the magnifier at the center of the activity.
mActivityRule.runOnUiThread(() -> {
mMagnifier.show(mLayout.getWidth() / 2, mLayout.getHeight() / 2);
});
assertTrue(TIME_LIMIT_EXCEEDED, latch.await(1, TimeUnit.SECONDS));
// Assert that the magnifier position represents a valid rectangle on screen.
final Rect position = mMagnifier.getWindowPositionOnScreen();
assertFalse(position.isEmpty());
assertTrue(0 <= position.left && position.right <= mLayout.getWidth());
assertTrue(0 <= position.top && position.bottom <= mLayout.getHeight());
}
@Test
public void testWindowContent_modifiesAfterUpdate() throws Throwable {
prepareFourQuadrantsScenario();
// Show the magnifier at the center of the activity.
final CountDownLatch latchForShow = new CountDownLatch(1);
mMagnifier.setOnOperationCompleteCallback(latchForShow::countDown);
mActivityRule.runOnUiThread(() -> {
mMagnifier.show(mLayout.getWidth() / 2, mLayout.getHeight() / 2);
});
assertTrue(TIME_LIMIT_EXCEEDED, latchForShow.await(1, TimeUnit.SECONDS));
final Bitmap initialBitmap = mMagnifier.getContent()
.copy(mMagnifier.getContent().getConfig(), true);
assertFourQuadrants(initialBitmap);
// Make the one quadrant white.
final View quadrant1 =
mActivity.findViewById(R.id.magnifier_activity_four_quadrants_layout_quadrant_1);
WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, quadrant1, () -> {
quadrant1.setBackground(null);
});
// Update the magnifier.
final CountDownLatch latchForUpdate = new CountDownLatch(1);
mMagnifier.setOnOperationCompleteCallback(latchForUpdate::countDown);
mActivityRule.runOnUiThread(mMagnifier::update);
assertTrue(TIME_LIMIT_EXCEEDED, latchForUpdate.await(1, TimeUnit.SECONDS));
final Bitmap newBitmap = mMagnifier.getContent();
assertFourQuadrants(newBitmap);
assertFalse(newBitmap.sameAs(initialBitmap));
}
/**
* Sets the activity to contain four equal quadrants coloured differently and
* instantiates a magnifier. This method should not be called on the UI thread.
*/
private void prepareFourQuadrantsScenario() throws Throwable {
WidgetTestUtils.runOnMainAndLayoutSync(mActivityRule, () -> {
mActivity.setContentView(R.layout.magnifier_activity_four_quadrants_layout);
mLayout = mActivity.findViewById(R.id.magnifier_activity_four_quadrants_layout);
mMagnifier = new Magnifier(mLayout);
}, false);
WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mLayout, null);
}
/**
* Asserts that the current bitmap contains four different dominant colors, which
* are (almost) equally distributed. The test takes into account an amount of
* noise, possible consequence of upscaling and filtering the magnified content.
*
* @param bitmap the bitmap to be checked
*/
private void assertFourQuadrants(final Bitmap bitmap) {
final int expectedQuadrants = 4;
final int totalPixels = bitmap.getWidth() * bitmap.getHeight();
final Map<Integer, Integer> colorCount = new HashMap<>();
for (int x = 0; x < bitmap.getWidth(); ++x) {
for (int y = 0; y < bitmap.getHeight(); ++y) {
final int currentColor = bitmap.getPixel(x, y);
colorCount.put(currentColor, colorCount.getOrDefault(currentColor, 0) + 1);
}
}
assertTrue(colorCount.size() >= expectedQuadrants);
final List<Integer> counts = new ArrayList<>(colorCount.values());
Collections.sort(counts);
int quadrantsTotal = 0;
for (int i = counts.size() - expectedQuadrants; i < counts.size(); ++i) {
quadrantsTotal += counts.get(i);
}
assertTrue(1.0f * (totalPixels - quadrantsTotal) / totalPixels <= 0.1f);
for (int i = counts.size() - expectedQuadrants; i < counts.size(); ++i) {
final float proportion = 1.0f
* Math.abs(expectedQuadrants * counts.get(i) - quadrantsTotal) / quadrantsTotal;
assertTrue(proportion <= 0.1f);
}
}
}