Re-enable testUninstallAllUserCaCerts_failIfNotProfileOwner am: 536bf217ec am: 4f05412f41
am: c9591872c0
Change-Id: I19c1eb6c07dad2d03a4055cb2841eba992b33d35
diff --git a/OldCtsTestCaseList.mk b/OldCtsTestCaseList.mk
index 739bae0..f0bc3aa 100644
--- a/OldCtsTestCaseList.mk
+++ b/OldCtsTestCaseList.mk
@@ -270,7 +270,7 @@
CtsAtraceHostTestCases \
CtsCppToolsTestCases \
CtsDevicePolicyManagerTestCases \
- CtsDragAndDropHostTestCases \
+ CtsWindowManagerHostTestCases \
CtsDumpsysHostTestCases \
CtsHostsideNetworkTests \
CtsJdwpSecurityHostTestCases \
diff --git a/apps/CameraITS/build/envsetup.sh b/apps/CameraITS/build/envsetup.sh
index bcf294a..7daf049 100644
--- a/apps/CameraITS/build/envsetup.sh
+++ b/apps/CameraITS/build/envsetup.sh
@@ -18,6 +18,7 @@
# is correct).
CAMERA_ITS_TOP=$PWD
+export CAMERA_ITS_TOP="$CAMERA_ITS_TOP"
[[ "${BASH_SOURCE[0]}" != "${0}" ]] || \
{ echo ">> Script must be sourced with 'source $0'" >&2; exit 1; }
diff --git a/apps/CameraITS/pymodules/its/caps.py b/apps/CameraITS/pymodules/its/caps.py
index a33757d..d9270f7 100644
--- a/apps/CameraITS/pymodules/its/caps.py
+++ b/apps/CameraITS/pymodules/its/caps.py
@@ -394,6 +394,59 @@
"android.edge.availableEdgeModes") and mode \
in props["android.edge.availableEdgeModes"];
+
+def lens_calibrated(props):
+ """Returns whether lens position is calibrated or not.
+
+ android.lens.info.focusDistanceCalibration has 3 modes.
+ 0: Uncalibrated
+ 1: Approximate
+ 2: Calibrated
+
+ Args:
+ props: Camera properties objects.
+
+ Returns:
+ Boolean.
+ """
+ return props.has_key("android.lens.info.focusDistanceCalibration") and \
+ props["android.lens.info.focusDistanceCalibration"] == 2
+
+
+def lens_approx_calibrated(props):
+ """Returns whether lens position is calibrated or not.
+
+ android.lens.info.focusDistanceCalibration has 3 modes.
+ 0: Uncalibrated
+ 1: Approximate
+ 2: Calibrated
+
+ Args:
+ props: Camera properties objects.
+
+ Returns:
+ Boolean.
+ """
+ return props.has_key("android.lens.info.focusDistanceCalibration") and \
+ (props["android.lens.info.focusDistanceCalibration"] == 1 or
+ props["android.lens.info.focusDistanceCalibration"] == 2)
+
+
+def fixed_focus(props):
+ """Returns whether a device is fixed focus.
+
+ props[android.lens.info.minimumFocusDistance] == 0 is fixed focus
+
+ Args:
+ props: Camera properties objects.
+
+ Returns:
+ Boolean.
+ """
+ return props.has_key("android.lens.info.minimumFocusDistance") and \
+ props["android.lens.info.minimumFocusDistance"] == 0
+
+
class __UnitTest(unittest.TestCase):
"""Run a suite of unit tests on this module.
"""
diff --git a/apps/CameraITS/pymodules/its/image.py b/apps/CameraITS/pymodules/its/image.py
index 0057eb7..97ab389 100644
--- a/apps/CameraITS/pymodules/its/image.py
+++ b/apps/CameraITS/pymodules/its/image.py
@@ -25,6 +25,8 @@
import cStringIO
import scipy.stats
import copy
+import cv2
+import os
DEFAULT_YUV_TO_RGB_CCM = numpy.matrix([
[1.000, 0.000, 1.402],
@@ -720,6 +722,7 @@
img = numpy.vstack(chs).T.reshape(h/f,w/f,chans)
return img
+
def compute_image_sharpness(img):
"""Calculate the sharpness of input image.
@@ -732,13 +735,74 @@
"""
chans = img.shape[2]
assert(chans == 1 or chans == 3)
- luma = img
- if (chans == 3):
+ if (chans == 1):
+ luma = img[:, :, 0]
+ elif (chans == 3):
luma = 0.299 * img[:,:,0] + 0.587 * img[:,:,1] + 0.114 * img[:,:,2]
[gy, gx] = numpy.gradient(luma)
return numpy.average(numpy.sqrt(gy*gy + gx*gx))
+
+def scale_img(img, scale=1.0):
+ """Scale and image based on a real number scale factor."""
+ dim = (int(img.shape[1]*scale), int(img.shape[0]*scale))
+ return cv2.resize(img.copy(), dim, interpolation=cv2.INTER_AREA)
+
+
+def find_chart(chart, img, scale_start, scale_stop, scale_step):
+ """Find the chart in the image.
+
+ Args:
+ chart: numpy array; chart image
+ img: numpy array; camera image containing chart
+ scale_start: float; start of scaling factor
+ scale_stop: float; stop of scaling factor
+ scale_step: float; step of scaling factor
+
+ Returns:
+ bounding box (top_right, bottom_left) coordinates in img
+ """
+ max_match = []
+ # check for normalized image
+ if numpy.amax(img) <= 1.0:
+ img = (img * 255.0).astype(numpy.uint8)
+ if len(img.shape) == 2:
+ img_gray = img.copy()
+ elif len(img.shape) == 3:
+ if img.shape[2] == 1:
+ img_gray = img[:, :, 0]
+ else:
+ img_gray = cv2.cvtColor(img.copy(), cv2.COLOR_RGB2GRAY)
+ print 'Finding chart in image...'
+ for scale in numpy.arange(scale_start, scale_stop, scale_step):
+ img_scaled = scale_img(img_gray, scale)
+ result = cv2.matchTemplate(img_scaled, chart, cv2.TM_CCOEFF)
+ _, opt_val, _, top_left_scaled = cv2.minMaxLoc(result)
+ # print out scale and match
+ print ' scale factor: %.3f, optimization val: %.f' % (scale, opt_val)
+ max_match.append((opt_val, top_left_scaled))
+
+ # determine if optimization results are valid
+ opt_values = [x[0] for x in max_match]
+ if 2.0*min(opt_values) > max(opt_values):
+ estring = ('Unable to find chart in scene!\n'
+ 'Check camera distance and self-reported '
+ 'pixel pitch, focal length and hyperfocal distance.')
+ raise its.error.Error(estring)
+ # find max and draw bbox
+ match_index = max_match.index(max(max_match, key=lambda x: x[0]))
+ scale = scale_start + scale_step * match_index
+ print 'Optimum scale factor: %.3f' % scale
+ top_left_scaled = max_match[match_index][1]
+ h, w = chart.shape
+ bottom_right_scaled = (top_left_scaled[0] + w, top_left_scaled[1] + h)
+ top_left = (int(top_left_scaled[0]/scale), int(top_left_scaled[1]/scale))
+ bottom_right = (int(bottom_right_scaled[0]/scale),
+ int(bottom_right_scaled[1]/scale))
+ return (top_left, bottom_right)
+
+
class __UnitTest(unittest.TestCase):
"""Run a suite of unit tests on this module.
"""
@@ -755,7 +819,7 @@
[ 7 8 9 ] [ 0.3 ] [ 5.0 ]
mat x y
"""
- mat = numpy.array([[1,2,3],[4,5,6],[7,8,9]])
+ mat = numpy.array([[1,2,3], [4,5,6], [7,8,9]])
x = numpy.array([0.1,0.2,0.3]).reshape(1,1,3)
y = apply_matrix_to_image(x, mat).reshape(3).tolist()
y_ref = [1.4,3.2,5.0]
@@ -763,7 +827,7 @@
self.assertTrue(passed)
def test_apply_lut_to_image(self):
- """ Unit test for apply_lut_to_image.
+ """Unit test for apply_lut_to_image.
Test by using a canned set of values on a 1x1 pixel image. The LUT will
simply double the value of the index:
@@ -777,6 +841,29 @@
passed = all([math.fabs(y[i] - y_ref[i]) < 0.001 for i in xrange(3)])
self.assertTrue(passed)
+ def test_compute_image_sharpness(self):
+ """Unit test for compute_img_sharpness.
+
+ Test by using PNG of ISO12233 chart and blurring intentionally.
+ 'sharpness' should drop off by sqrt(2) for 2x blur of image.
+
+ We do one level of blur as PNG image is not perfect.
+ """
+ yuv_full_scale = 1023.0
+ chart_file = os.path.join(os.environ['CAMERA_ITS_TOP'], 'pymodules',
+ 'its', 'test_images', 'ISO12233.png')
+ chart = cv2.imread(chart_file, cv2.IMREAD_ANYDEPTH)
+ white_level = numpy.amax(chart).astype(float)
+ sharpness = {}
+ for j in [2, 4, 8]:
+ blur = cv2.blur(chart, (j, j))
+ blur = blur[:, :, numpy.newaxis]
+ sharpness[j] = yuv_full_scale * compute_image_sharpness(blur /
+ white_level)
+ self.assertTrue(numpy.isclose(sharpness[2]/sharpness[4],
+ numpy.sqrt(2), atol=0.1))
+ self.assertTrue(numpy.isclose(sharpness[4]/sharpness[8],
+ numpy.sqrt(2), atol=0.1))
+
if __name__ == '__main__':
unittest.main()
-
diff --git a/apps/CameraITS/pymodules/its/test_images/ISO12233.png b/apps/CameraITS/pymodules/its/test_images/ISO12233.png
new file mode 100644
index 0000000..f1ffce8
--- /dev/null
+++ b/apps/CameraITS/pymodules/its/test_images/ISO12233.png
Binary files differ
diff --git a/apps/CameraITS/tests/scene3/test_lens_movement_reporting.py b/apps/CameraITS/tests/scene3/test_lens_movement_reporting.py
new file mode 100644
index 0000000..7c1b579
--- /dev/null
+++ b/apps/CameraITS/tests/scene3/test_lens_movement_reporting.py
@@ -0,0 +1,259 @@
+# Copyright 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.
+
+import os
+
+import cv2
+import its.caps
+import its.device
+import its.image
+import its.objects
+import numpy as np
+
+NUM_IMGS = 12
+NUM_TRYS = 8
+FRAME_TIME_TOL = 10 # ms
+SHARPNESS_TOL = 0.10 # percentage
+POSITION_TOL = 0.10 # percentage
+CHART_FILE = os.path.join(os.environ['CAMERA_ITS_TOP'], 'pymodules', 'its',
+ 'test_images', 'ISO12233.png')
+CHART_HEIGHT = 16.5 # cm
+CHART_DISTANCE = 40.0 # cm
+CHART_SCALE_START = 0.75
+CHART_SCALE_STOP = 1.25
+CHART_SCALE_STEP = 0.05
+VGA_WIDTH = 640
+VGA_HEIGHT = 480
+NAME = os.path.basename(__file__).split('.')[0]
+
+
+def normalize_img(img):
+ """Normalize the image values to between 0 and 1.
+
+ Args:
+ img: 2-D numpy array of image values
+ Returns:
+ Normalized image
+ """
+ return (img - np.amin(img))/(np.amax(img) - np.amin(img))
+
+
+def find_chart_bbox(img, chart, scale_factor):
+ """Find the crop area for the chart."""
+ scale_start = CHART_SCALE_START * scale_factor
+ scale_stop = CHART_SCALE_STOP * scale_factor
+ scale_step = CHART_SCALE_STEP * scale_factor
+ bbox = its.image.find_chart(chart, img,
+ scale_start, scale_stop, scale_step)
+ # convert bbox to (xnorm, ynorm, wnorm, hnorm)
+ wnorm = float((bbox[1][0]) - bbox[0][0]) / img.shape[1]
+ hnorm = float((bbox[1][1]) - bbox[0][1]) / img.shape[0]
+ xnorm = float(bbox[0][0]) / img.shape[1]
+ ynorm = float(bbox[0][1]) / img.shape[0]
+ return xnorm, ynorm, wnorm, hnorm
+
+
+def find_af_chart(cam, props, sensitivity, exp, af_fd):
+ """Take an AF image to find the chart location.
+
+ Args:
+ cam: An open device session.
+ props: Properties of cam
+ sensitivity: Sensitivity for the AF request as defined in
+ android.sensor.sensitivity
+ exp: Exposure time for the AF request as defined in
+ android.sensor.exposureTime
+ af_fd: float; autofocus lens position
+ Returns:
+ xnorm: float; x location normalized to [0, 1]
+ ynorm: float; y location normalized to [0, 1]
+ wnorm: float; width normalized to [0, 1]
+ hnorm: float; height normalized to [0, 1]
+ """
+ # find maximum size 4:3 format
+ fmts = props['android.scaler.streamConfigurationMap']['availableStreamConfigurations']
+ fmts = [f for f in fmts if f['format'] == 256]
+ fmt = {'format': 'yuv', 'width': fmts[0]['width'],
+ 'height': fmts[0]['height']}
+ req = its.objects.manual_capture_request(sensitivity, exp)
+ req['android.lens.focusDistance'] = af_fd
+ trys = 0
+ done = False
+ while not done:
+ print 'Waiting for lens to move to correct location...'
+ cap_chart = cam.do_capture(req, fmt)
+ done = (cap_chart['metadata']['android.lens.state'] == 0)
+ print ' status: ', done
+ trys += 1
+ if trys == NUM_TRYS:
+ raise its.error.Error('Error: cannot settle lens in %d trys!'
+ % (trys))
+ y, _, _ = its.image.convert_capture_to_planes(cap_chart, props)
+ its.image.write_image(y, '%s_AF_image.jpg' % NAME)
+ template = cv2.imread(CHART_FILE, cv2.IMREAD_ANYDEPTH)
+ focal_l = cap_chart['metadata']['android.lens.focalLength']
+ pixel_pitch = (props['android.sensor.info.physicalSize']['height'] /
+ y.shape[0])
+ print ' Chart distance: %.2fcm' % CHART_DISTANCE
+ print ' Chart height: %.2fcm' % CHART_HEIGHT
+ print ' Focal length: %.2fmm' % focal_l
+ print ' Pixel pitch: %.2fum' % (pixel_pitch*1E3)
+ print ' Template height: %d' % template.shape[0]
+ chart_pixel_h = CHART_HEIGHT * focal_l / (CHART_DISTANCE * pixel_pitch)
+ scale_factor = template.shape[0] / chart_pixel_h
+ print 'Chart/image scale factor = %.2f' % scale_factor
+ xnorm, ynorm, wnorm, hnorm = find_chart_bbox(y, template, scale_factor)
+ chart = its.image.get_image_patch(y, xnorm, ynorm, wnorm, hnorm)
+ its.image.write_image(chart, '%s_AF_chart.jpg' % NAME)
+ return xnorm, ynorm, wnorm, hnorm
+
+
+def test_lens_movement_reporting(cam, props, fmt, sensitivity, exp, af_fd):
+ """Return fd, sharpness, lens state of the output images.
+
+ Args:
+ cam: An open device session.
+ props: Properties of cam
+ fmt: dict; capture format
+ sensitivity: Sensitivity for the 3A request as defined in
+ android.sensor.sensitivity
+ exp: Exposure time for the 3A request as defined in
+ android.sensor.exposureTime
+ af_fd: Focus distance for the 3A request as defined in
+ android.lens.focusDistance
+
+ Returns:
+ Object containing reported sharpness of the output image, keyed by
+ the following string:
+ 'sharpness'
+ """
+
+ print 'Take AF image for chart locator.'
+ xnorm, ynorm, wnorm, hnorm = find_af_chart(cam, props, sensitivity,
+ exp, af_fd)
+ data_set = {}
+ white_level = int(props['android.sensor.info.whiteLevel'])
+ min_fd = props['android.lens.info.minimumFocusDistance']
+ fds = [af_fd, min_fd]
+ fds = sorted(fds * NUM_IMGS)
+ reqs = []
+ for i, fd in enumerate(fds):
+ reqs.append(its.objects.manual_capture_request(sensitivity, exp))
+ reqs[i]['android.lens.focusDistance'] = fd
+ cap = cam.do_capture(reqs, fmt)
+ for i, _ in enumerate(reqs):
+ data = {'fd': fds[i]}
+ data['loc'] = cap[i]['metadata']['android.lens.focusDistance']
+ data['lens_moving'] = (cap[i]['metadata']['android.lens.state']
+ == 1)
+ timestamp = cap[i]['metadata']['android.sensor.timestamp']
+ if i == 0:
+ timestamp_init = timestamp
+ timestamp -= timestamp_init
+ timestamp *= 1E-6
+ data['timestamp'] = timestamp
+ print ' focus distance (diopters): %.3f' % data['fd']
+ print ' current lens location (diopters): %.3f' % data['loc']
+ print ' lens moving %r' % data['lens_moving']
+ y, _, _ = its.image.convert_capture_to_planes(cap[i], props)
+ chart = normalize_img(its.image.get_image_patch(y, xnorm, ynorm,
+ wnorm, hnorm))
+ its.image.write_image(chart, '%s_i=%d_chart.jpg' % (NAME, i))
+ data['sharpness'] = white_level*its.image.compute_image_sharpness(chart)
+ print 'Chart sharpness: %.1f\n' % data['sharpness']
+ data_set[i] = data
+ return data_set
+
+
+def main():
+ """Test if focus distance is properly reported.
+
+ Capture images at a variety of focus locations.
+ """
+
+ print '\nStarting test_lens_movement_reporting.py'
+ with its.device.ItsSession() as cam:
+ props = cam.get_camera_properties()
+ its.caps.skip_unless(not its.caps.fixed_focus(props))
+ its.caps.skip_unless(its.caps.lens_approx_calibrated(props))
+ min_fd = props['android.lens.info.minimumFocusDistance']
+ fmt = {'format': 'yuv', 'width': VGA_WIDTH, 'height': VGA_HEIGHT}
+
+ # Get proper sensitivity, exposure time, and focus distance with 3A.
+ s, e, _, _, fd = cam.do_3a(get_results=True)
+
+ # Get sharpness for each focal distance
+ d = test_lens_movement_reporting(cam, props, fmt, s, e, fd)
+ for k in sorted(d):
+ print ('i: %d\tfd: %.3f\tlens location (diopters): %.3f \t'
+ 'sharpness: %.1f \tlens_moving: %r \t'
+ 'timestamp: %.1fms' % (k, d[k]['fd'], d[k]['loc'],
+ d[k]['sharpness'],
+ d[k]['lens_moving'],
+ d[k]['timestamp']))
+
+ # assert frames are consecutive
+ print 'Asserting frames are consecutive'
+ times = [v['timestamp'] for v in d.itervalues()]
+ diffs = np.gradient(times)
+ print diffs
+ assert np.isclose(np.amax(diffs)-np.amax(diffs), 0, atol=FRAME_TIME_TOL)
+
+ # remove data when lens is moving
+ for k in sorted(d):
+ if d[k]['lens_moving']:
+ del d[k]
+
+ # split data into min_fd and af data for processing
+ d_min_fd = {}
+ d_af_fd = {}
+ for k in sorted(d):
+ if d[k]['fd'] == min_fd:
+ d_min_fd[k] = d[k]
+ if d[k]['fd'] == fd:
+ d_af_fd[k] = d[k]
+
+ # assert reported locations are close at af_fd
+ print 'Asserting lens location of af_fd data'
+ min_loc = min([v['loc'] for v in d_af_fd.itervalues()])
+ max_loc = max([v['loc'] for v in d_af_fd.itervalues()])
+ assert np.isclose(min_loc, max_loc, rtol=POSITION_TOL)
+ # assert reported sharpness is close at af_fd
+ print 'Asserting sharpness of af_fd data'
+ min_sharp = min([v['sharpness'] for v in d_af_fd.itervalues()])
+ max_sharp = max([v['sharpness'] for v in d_af_fd.itervalues()])
+ assert np.isclose(min_sharp, max_sharp, rtol=SHARPNESS_TOL)
+ # assert reported location is close to assign location for af_fd
+ print 'Asserting lens location close to assigned fd for af_fd data'
+ assert np.isclose(d_af_fd[0]['loc'], d_af_fd[0]['fd'],
+ rtol=POSITION_TOL)
+
+ # assert reported location is close for min_fd captures
+ print 'Asserting lens location similar min_fd data'
+ min_loc = min([v['loc'] for v in d_min_fd.itervalues()])
+ max_loc = max([v['loc'] for v in d_min_fd.itervalues()])
+ assert np.isclose(min_loc, max_loc, rtol=POSITION_TOL)
+ # assert reported sharpness is close at min_fd
+ print 'Asserting sharpness of min_fd data'
+ min_sharp = min([v['sharpness'] for v in d_min_fd.itervalues()])
+ max_sharp = max([v['sharpness'] for v in d_min_fd.itervalues()])
+ assert np.isclose(min_sharp, max_sharp, rtol=SHARPNESS_TOL)
+ # assert reported location is close to assign location for min_fd
+ print 'Asserting lens location close to assigned fd for min_fd data'
+ assert np.isclose(d_min_fd[NUM_IMGS*2-1]['loc'],
+ d_min_fd[NUM_IMGS*2-1]['fd'], rtol=POSITION_TOL)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/apps/CameraITS/tools/run_all_tests.py b/apps/CameraITS/tools/run_all_tests.py
index 52780eb..d6dca72 100644
--- a/apps/CameraITS/tools/run_all_tests.py
+++ b/apps/CameraITS/tools/run_all_tests.py
@@ -58,7 +58,7 @@
"scene0" : None,
"scene1" : "A grey card covering at least the middle 30% of the scene",
"scene2" : "A picture containing human faces",
- "scene3" : "A chart containing sharp edges like ISO 12233",
+ "scene3" : "The ISO 12233 chart",
"scene4" : "A specific test page of a circle covering at least the "
"middle 50% of the scene. See CameraITS.pdf section 2.3.4 "
"for more details",
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index 83f99c5..7b3a328 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -2200,6 +2200,20 @@
</intent-filter>
</activity-alias>
+ <!-- 6DoF sensor test -->
+ <activity
+ android:name="com.android.cts.verifier.sensors.sixdof.Activities.StartActivity"
+ android:label="@string/six_dof_test">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.cts.intent.category.MANUAL_TEST"/>
+ </intent-filter>
+ <meta-data android:name="test_category" android:value="@string/test_category_sensors"/>
+ </activity>
+ <activity
+ android:name="com.android.cts.verifier.sensors.sixdof.Activities.TestActivity"
+ android:label="@string/title_activity_cts">
+ </activity>
</application>
</manifest>
diff --git a/apps/CtsVerifier/res/drawable-hdpi/ic_place_white_24dp.png b/apps/CtsVerifier/res/drawable-hdpi/ic_place_white_24dp.png
new file mode 100644
index 0000000..7c281c3
--- /dev/null
+++ b/apps/CtsVerifier/res/drawable-hdpi/ic_place_white_24dp.png
Binary files differ
diff --git a/apps/CtsVerifier/res/drawable-hdpi/ic_save.png b/apps/CtsVerifier/res/drawable-hdpi/ic_save.png
new file mode 100644
index 0000000..a046efc
--- /dev/null
+++ b/apps/CtsVerifier/res/drawable-hdpi/ic_save.png
Binary files differ
diff --git a/apps/CtsVerifier/res/drawable-mdpi/ic_place_white_24dp.png b/apps/CtsVerifier/res/drawable-mdpi/ic_place_white_24dp.png
new file mode 100644
index 0000000..933eb51
--- /dev/null
+++ b/apps/CtsVerifier/res/drawable-mdpi/ic_place_white_24dp.png
Binary files differ
diff --git a/apps/CtsVerifier/res/drawable-mdpi/ic_save.png b/apps/CtsVerifier/res/drawable-mdpi/ic_save.png
new file mode 100644
index 0000000..c36e990
--- /dev/null
+++ b/apps/CtsVerifier/res/drawable-mdpi/ic_save.png
Binary files differ
diff --git a/apps/CtsVerifier/res/drawable-xhdpi/ic_place_white_24dp.png b/apps/CtsVerifier/res/drawable-xhdpi/ic_place_white_24dp.png
new file mode 100644
index 0000000..814ca8d
--- /dev/null
+++ b/apps/CtsVerifier/res/drawable-xhdpi/ic_place_white_24dp.png
Binary files differ
diff --git a/apps/CtsVerifier/res/drawable-xhdpi/ic_save.png b/apps/CtsVerifier/res/drawable-xhdpi/ic_save.png
new file mode 100644
index 0000000..94d699e
--- /dev/null
+++ b/apps/CtsVerifier/res/drawable-xhdpi/ic_save.png
Binary files differ
diff --git a/apps/CtsVerifier/res/drawable-xxhdpi/ic_place_white_24dp.png b/apps/CtsVerifier/res/drawable-xxhdpi/ic_place_white_24dp.png
new file mode 100644
index 0000000..078b10d
--- /dev/null
+++ b/apps/CtsVerifier/res/drawable-xxhdpi/ic_place_white_24dp.png
Binary files differ
diff --git a/apps/CtsVerifier/res/drawable-xxhdpi/ic_save.png b/apps/CtsVerifier/res/drawable-xxhdpi/ic_save.png
new file mode 100644
index 0000000..5a0fbe4
--- /dev/null
+++ b/apps/CtsVerifier/res/drawable-xxhdpi/ic_save.png
Binary files differ
diff --git a/apps/CtsVerifier/res/drawable-xxxhdpi/ic_place_white_24dp.png b/apps/CtsVerifier/res/drawable-xxxhdpi/ic_place_white_24dp.png
new file mode 100644
index 0000000..8bcb6f6
--- /dev/null
+++ b/apps/CtsVerifier/res/drawable-xxxhdpi/ic_place_white_24dp.png
Binary files differ
diff --git a/apps/CtsVerifier/res/drawable-xxxhdpi/ic_save.png b/apps/CtsVerifier/res/drawable-xxxhdpi/ic_save.png
new file mode 100644
index 0000000..264ce97
--- /dev/null
+++ b/apps/CtsVerifier/res/drawable-xxxhdpi/ic_save.png
Binary files differ
diff --git a/apps/CtsVerifier/res/drawable/round_button.xml b/apps/CtsVerifier/res/drawable/round_button.xml
new file mode 100644
index 0000000..af5bff3
--- /dev/null
+++ b/apps/CtsVerifier/res/drawable/round_button.xml
@@ -0,0 +1,106 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item android:top="8px">
+ <layer-list>
+ <item>
+ <shape android:shape="oval">
+ <solid android:color="#08000000"/>
+ <padding
+ android:bottom="3px"
+ android:left="3px"
+ android:right="3px"
+ android:top="3px"
+ />
+ </shape>
+ </item>
+ <item>
+ <shape android:shape="oval">
+ <solid android:color="#09000000"/>
+ <padding
+ android:bottom="2px"
+ android:left="2px"
+ android:right="2px"
+ android:top="2px"
+ />
+ </shape>
+ </item>
+ <item>
+ <shape android:shape="oval">
+ <solid android:color="#10000000"/>
+ <padding
+ android:bottom="2px"
+ android:left="2px"
+ android:right="2px"
+ android:top="2px"
+ />
+ </shape>
+ </item>
+ <item>
+ <shape android:shape="oval">
+ <solid android:color="#11000000"/>
+ <padding
+ android:bottom="1px"
+ android:left="1px"
+ android:right="1px"
+ android:top="1px"
+ />
+ </shape>
+ </item>
+ <item>
+ <shape android:shape="oval">
+ <solid android:color="#12000000"/>
+ <padding
+ android:bottom="1px"
+ android:left="1px"
+ android:right="1px"
+ android:top="1px"
+ />
+ </shape>
+ </item>
+ <item>
+ <shape android:shape="oval">
+ <solid android:color="#13000000"/>
+ <padding
+ android:bottom="1px"
+ android:left="1px"
+ android:right="1px"
+ android:top="1px"
+ />
+ </shape>
+ </item>
+ <item>
+ <shape android:shape="oval">
+ <solid android:color="#14000000"/>
+ <padding
+ android:bottom="1px"
+ android:left="1px"
+ android:right="1px"
+ android:top="1px"
+ />
+ </shape>
+ </item>
+ <item>
+ <shape android:shape="oval">
+ <solid android:color="#15000000"/>
+ <padding
+ android:bottom="1px"
+ android:left="1px"
+ android:right="1px"
+ android:top="1px"
+ />
+ </shape>
+ </item>
+
+ <item >
+ <shape android:shape="oval">
+ <solid android:color="#00bfa5" />
+ </shape>
+
+ </item>
+
+ </layer-list>
+ </item>
+
+
+</layer-list>
\ No newline at end of file
diff --git a/apps/CtsVerifier/res/layout-port/fragment_accuracy.xml b/apps/CtsVerifier/res/layout-port/fragment_accuracy.xml
new file mode 100644
index 0000000..4ac4cdb
--- /dev/null
+++ b/apps/CtsVerifier/res/layout-port/fragment_accuracy.xml
@@ -0,0 +1,59 @@
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <LinearLayout android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <!-- Deliberately empty so that we can add camera preview to it dynamically-->
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:orientation="vertical"
+ android:layout_weight=".5"
+ android:layout_height="0dp"
+ android:id="@+id/llCamera">
+ </LinearLayout>
+
+ <RelativeLayout
+ android:padding="4dp"
+ android:layout_width="match_parent"
+ android:orientation="vertical"
+ android:layout_weight=".5"
+ android:layout_height="0dp">
+
+ <include layout="@layout/verifier_buttons"/>
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textSize="17sp"
+ android:layout_below="@+id/llPassFail"
+ android:id="@+id/tvObjective"/>
+
+ <TextView
+ android:layout_below="@+id/tvObjective"
+ android:paddingTop="20dp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/tvTranslations"/>
+
+ <TextView
+ android:paddingTop="4dp"
+ android:layout_below="@+id/tvTranslations"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/tvMarkers"/>
+ </RelativeLayout>
+ </LinearLayout>
+
+ <ImageButton
+ android:id="@+id/fabPlaceWaypoint"
+ android:layout_width="70dp"
+ android:layout_height="70dp"
+ android:layout_alignParentBottom="true"
+ android:layout_alignParentRight="true"
+ android:layout_margin="16dp"
+ android:background="@drawable/round_button"
+ android:src="@drawable/ic_place_white_24dp"/>
+</RelativeLayout>
diff --git a/apps/CtsVerifier/res/layout-port/fragment_complex_movement.xml b/apps/CtsVerifier/res/layout-port/fragment_complex_movement.xml
new file mode 100644
index 0000000..d2c40c7
--- /dev/null
+++ b/apps/CtsVerifier/res/layout-port/fragment_complex_movement.xml
@@ -0,0 +1,52 @@
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <LinearLayout android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <!-- Deliberately empty so that we can add camera preview to it dynamically-->
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:orientation="vertical"
+ android:layout_weight=".5"
+ android:layout_height="0dp"
+ android:id="@+id/llCamera">
+ </LinearLayout>
+
+ <RelativeLayout
+ android:padding="4dp"
+ android:layout_width="match_parent"
+ android:orientation="vertical"
+ android:layout_weight=".5"
+ android:layout_height="0dp">
+
+ <include layout="@layout/verifier_buttons"/>
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textSize="17sp"
+ android:layout_below="@+id/llPassFail"
+ android:id="@+id/tvObjective"/>
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@+id/tvObjective"
+ android:id="@+id/tvRings"/>
+
+ </RelativeLayout>
+ </LinearLayout>
+
+ <ImageButton
+ android:id="@+id/fabPlaceWaypoint"
+ android:layout_width="70dp"
+ android:layout_height="70dp"
+ android:layout_alignParentBottom="true"
+ android:layout_alignParentRight="true"
+ android:layout_margin="16dp"
+ android:background="@drawable/round_button"
+ android:src="@drawable/ic_place_white_24dp"/>
+</RelativeLayout>
diff --git a/apps/CtsVerifier/res/layout-port/fragment_robustness.xml b/apps/CtsVerifier/res/layout-port/fragment_robustness.xml
new file mode 100644
index 0000000..b825082
--- /dev/null
+++ b/apps/CtsVerifier/res/layout-port/fragment_robustness.xml
@@ -0,0 +1,61 @@
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <LinearLayout android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <!-- Deliberately empty so that we can add camera preview to it dynamically-->
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:orientation="vertical"
+ android:layout_weight=".5"
+ android:layout_height="0dp"
+ android:id="@+id/llCamera">
+ </LinearLayout>
+
+ <RelativeLayout
+ android:padding="4dp"
+ android:layout_width="match_parent"
+ android:orientation="vertical"
+ android:layout_weight=".5"
+ android:layout_height="0dp">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textSize="17sp"
+ android:layout_below="@+id/llPassFail"
+ android:id="@+id/tvObjective"/>
+
+ <TextView
+ android:layout_below="@+id/tvObjective"
+ android:paddingTop="20dp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/tvTimer"/>
+
+ <TextView
+ android:id="@+id/tvPassColour"
+ android:layout_width="match_parent"
+ android:layout_height="50dp"
+ android:layout_marginTop="50dp"
+ android:layout_below="@id/tvTimer"
+ android:background="@color/green"/>
+
+ <include layout="@layout/verifier_buttons"/>
+
+ </RelativeLayout>
+ </LinearLayout>
+
+ <ImageButton
+ android:id="@+id/fabPlaceWaypoint"
+ android:layout_width="70dp"
+ android:layout_height="70dp"
+ android:layout_alignParentBottom="true"
+ android:layout_alignParentRight="true"
+ android:layout_margin="16dp"
+ android:background="@drawable/round_button"
+ android:src="@drawable/ic_place_white_24dp"/>
+</RelativeLayout>
diff --git a/apps/CtsVerifier/res/layout/activity_cts.xml b/apps/CtsVerifier/res/layout/activity_cts.xml
new file mode 100644
index 0000000..ef1459e
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/activity_cts.xml
@@ -0,0 +1,12 @@
+<RelativeLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ android:keepScreenOn="true">
+
+ <FrameLayout
+ android:id="@+id/contentFragment"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"/>
+
+</RelativeLayout>
diff --git a/apps/CtsVerifier/res/layout/activity_start.xml b/apps/CtsVerifier/res/layout/activity_start.xml
new file mode 100644
index 0000000..e7e7273
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/activity_start.xml
@@ -0,0 +1,25 @@
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingLeft="@dimen/activity_horizontal_margin"
+ android:paddingRight="@dimen/activity_horizontal_margin"
+ android:paddingTop="@dimen/activity_vertical_margin"
+ android:paddingBottom="@dimen/activity_vertical_margin"
+ tools:context=".StartActivity">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerHorizontal="true"
+ android:id="@+id/tvDesc"
+ android:text="@string/overall_instructions"/>
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@+id/tvDesc"
+ android:layout_centerHorizontal="true"
+ android:id="@+id/btnStart"
+ android:text="@string/start"/>
+
+</RelativeLayout>
diff --git a/apps/CtsVerifier/res/layout/dialog_result_accuracy.xml b/apps/CtsVerifier/res/layout/dialog_result_accuracy.xml
new file mode 100644
index 0000000..c499248
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/dialog_result_accuracy.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical" android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <LinearLayout android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <TextView android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:text="@string/waypoint_differences"
+ android:layout_weight="1"/>
+ <TextView android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:id="@+id/tvWaypointResult"
+ android:layout_weight="1"/>
+ </LinearLayout>
+
+ <LinearLayout android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="10dp"
+ android:orientation="horizontal">
+
+ <TextView android:layout_width="0dp"
+ android:gravity="center"
+ android:text="@string/path_differences"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"/>
+ <TextView android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:id="@+id/tvPathResult"
+ android:layout_weight="1"/>
+ </LinearLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/apps/CtsVerifier/res/layout/dialog_result_complex_movement.xml b/apps/CtsVerifier/res/layout/dialog_result_complex_movement.xml
new file mode 100644
index 0000000..f3946ec
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/dialog_result_complex_movement.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical" android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <LinearLayout android:layout_width="match_parent"
+ android:paddingTop="10dp"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <TextView android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:text="@string/waypoint_differences"
+ android:layout_weight="1"/>
+ <TextView android:layout_width="0dp"
+ android:gravity="center"
+ android:id="@+id/tvWaypointResult"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"/>
+ </LinearLayout>
+
+ <LinearLayout android:layout_width="match_parent"
+ android:paddingTop="10dp"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <TextView android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:text="@string/rings"
+ android:layout_weight="1"/>
+ <TextView android:layout_width="0dp"
+ android:gravity="center"
+ android:id="@+id/tvRingsResult"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"/>
+ </LinearLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/apps/CtsVerifier/res/layout/dialog_result_robustness.xml b/apps/CtsVerifier/res/layout/dialog_result_robustness.xml
new file mode 100644
index 0000000..50c6bb9
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/dialog_result_robustness.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical" android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <LinearLayout android:layout_width="match_parent"
+ android:paddingTop="10dp"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <TextView android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:text="@string/waypoint_differences"
+ android:layout_weight="1"/>
+ <TextView android:layout_width="0dp"
+ android:gravity="center"
+ android:id="@+id/tvWaypointResult"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"/>
+ </LinearLayout>
+
+ <LinearLayout android:layout_width="match_parent"
+ android:paddingTop="10dp"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <TextView android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:text="@string/path_differences"
+ android:layout_weight="1"/>
+ <TextView android:layout_width="0dp"
+ android:gravity="center"
+ android:id="@+id/tvPathResult"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"/>
+ </LinearLayout>
+
+ <LinearLayout android:layout_width="match_parent"
+ android:paddingTop="10dp"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <TextView android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:text="@string/time"
+ android:layout_weight="1"/>
+ <TextView android:layout_width="0dp"
+ android:gravity="center"
+ android:id="@+id/tvTimeResult"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"/>
+ </LinearLayout>
+
+ <LinearLayout android:layout_width="match_parent"
+ android:paddingTop="10dp"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <TextView android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:text="@string/rotation_result"
+ android:layout_weight="1"/>
+ <TextView android:layout_width="0dp"
+ android:gravity="center"
+ android:id="@+id/tvRotationResult"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"/>
+ </LinearLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/apps/CtsVerifier/res/layout/fragment_accuracy.xml b/apps/CtsVerifier/res/layout/fragment_accuracy.xml
new file mode 100644
index 0000000..c5c8b0b
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/fragment_accuracy.xml
@@ -0,0 +1,58 @@
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <LinearLayout android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <!-- Deliberately empty so that we can add camera preview to it dynamically-->
+ <LinearLayout
+ android:layout_width="0dp"
+ android:orientation="vertical"
+ android:layout_weight=".75"
+ android:layout_height="match_parent"
+ android:id="@+id/llCamera">
+ </LinearLayout>
+
+ <RelativeLayout
+ android:padding="4dp"
+ android:layout_width="0dp"
+ android:orientation="vertical"
+ android:layout_weight=".25"
+ android:layout_height="match_parent">
+
+ <include layout="@layout/verifier_buttons"/>
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textSize="17sp"
+ android:layout_below="@+id/llPassFail"
+ android:id="@+id/tvObjective"/>
+
+ <TextView
+ android:layout_below="@+id/tvObjective"
+ android:paddingTop="20dp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/tvTranslations"/>
+
+ <TextView
+ android:paddingTop="4dp"
+ android:layout_below="@+id/tvTranslations"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/tvMarkers"/>
+ </RelativeLayout>
+ </LinearLayout>
+
+ <ImageButton
+ android:id="@+id/fabPlaceWaypoint"
+ android:layout_width="70dp"
+ android:layout_height="70dp"
+ android:layout_alignParentBottom="true"
+ android:layout_alignParentRight="true"
+ android:layout_margin="16dp"
+ android:background="@drawable/round_button"
+ android:src="@drawable/ic_place_white_24dp"/>
+</RelativeLayout>
diff --git a/apps/CtsVerifier/res/layout/fragment_complex_movement.xml b/apps/CtsVerifier/res/layout/fragment_complex_movement.xml
new file mode 100644
index 0000000..f0c30e2
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/fragment_complex_movement.xml
@@ -0,0 +1,51 @@
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <LinearLayout android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <!-- Deliberately empty so that we can add camera preview to it dynamically-->
+ <LinearLayout
+ android:layout_width="0dp"
+ android:orientation="vertical"
+ android:layout_weight=".75"
+ android:layout_height="match_parent"
+ android:id="@+id/llCamera">
+ </LinearLayout>
+
+ <RelativeLayout
+ android:padding="4dp"
+ android:layout_width="0dp"
+ android:orientation="vertical"
+ android:layout_weight=".25"
+ android:layout_height="match_parent">
+
+ <include layout="@layout/verifier_buttons"/>
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textSize="17sp"
+ android:layout_below="@+id/llPassFail"
+ android:id="@+id/tvObjective"/>
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@+id/tvObjective"
+ android:id="@+id/tvRings"/>
+
+ </RelativeLayout>
+ </LinearLayout>
+
+ <ImageButton
+ android:id="@+id/fabPlaceWaypoint"
+ android:layout_width="70dp"
+ android:layout_height="70dp"
+ android:layout_alignParentBottom="true"
+ android:layout_alignParentRight="true"
+ android:layout_margin="16dp"
+ android:background="@drawable/round_button"
+ android:src="@drawable/ic_place_white_24dp"/>
+</RelativeLayout>
diff --git a/apps/CtsVerifier/res/layout/fragment_robustness.xml b/apps/CtsVerifier/res/layout/fragment_robustness.xml
new file mode 100644
index 0000000..13f1e89
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/fragment_robustness.xml
@@ -0,0 +1,60 @@
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <LinearLayout android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <!-- Deliberately empty so that we can add camera preview to it dynamically-->
+ <LinearLayout
+ android:layout_width="0dp"
+ android:orientation="vertical"
+ android:layout_weight=".75"
+ android:layout_height="match_parent"
+ android:id="@+id/llCamera">
+ </LinearLayout>
+
+ <RelativeLayout
+ android:padding="4dp"
+ android:layout_width="0dp"
+ android:orientation="vertical"
+ android:layout_weight=".25"
+ android:layout_height="match_parent">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textSize="17sp"
+ android:layout_below="@+id/llPassFail"
+ android:id="@+id/tvObjective"/>
+
+ <TextView
+ android:layout_below="@+id/tvObjective"
+ android:paddingTop="20dp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/tvTimer"/>
+
+ <TextView
+ android:id="@+id/tvPassColour"
+ android:layout_width="match_parent"
+ android:layout_height="50dp"
+ android:layout_marginTop="50dp"
+ android:layout_below="@id/tvTimer"
+ android:background="@color/green"/>
+
+ <include layout="@layout/verifier_buttons"/>
+
+ </RelativeLayout>
+ </LinearLayout>
+
+ <ImageButton
+ android:id="@+id/fabPlaceWaypoint"
+ android:layout_width="70dp"
+ android:layout_height="70dp"
+ android:layout_alignParentBottom="true"
+ android:layout_alignParentRight="true"
+ android:layout_margin="16dp"
+ android:background="@drawable/round_button"
+ android:src="@drawable/ic_place_white_24dp"/>
+</RelativeLayout>
diff --git a/apps/CtsVerifier/res/layout/fragment_start_phase.xml b/apps/CtsVerifier/res/layout/fragment_start_phase.xml
new file mode 100644
index 0000000..73c5802
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/fragment_start_phase.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:paddingLeft="@dimen/activity_horizontal_margin"
+ android:paddingRight="@dimen/activity_horizontal_margin"
+ android:paddingTop="@dimen/activity_vertical_margin"
+ android:paddingBottom="@dimen/activity_vertical_margin"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/phase1_description"
+ android:id="@+id/tvDesc"/>
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:id="@+id/btnStart"
+ android:text="@string/start"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/apps/CtsVerifier/res/layout/verifier_buttons.xml b/apps/CtsVerifier/res/layout/verifier_buttons.xml
new file mode 100644
index 0000000..6a80316
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/verifier_buttons.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/llPassFail"
+ android:layout_alignParentTop="true"
+ android:background="@android:color/transparent"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <Button
+ android:id="@+id/btnPass"
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:text="@string/pass"
+ android:enabled="false"
+ android:drawableTop="@drawable/fs_good"/>
+
+ <Button
+ android:id="@+id/btnInfo"
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:text="@string/info"
+ android:drawableTop="@drawable/fs_indeterminate"/>
+
+ <Button
+ android:id="@+id/btnFail"
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:text="@string/fail"
+ android:drawableTop="@drawable/fs_error"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/apps/CtsVerifier/res/menu/menu_cts.xml b/apps/CtsVerifier/res/menu/menu_cts.xml
new file mode 100644
index 0000000..f6a51b1
--- /dev/null
+++ b/apps/CtsVerifier/res/menu/menu_cts.xml
@@ -0,0 +1,8 @@
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ tools:context=".Activities.TestActivity">
+ <item android:id="@+id/action_save_results" android:title="@string/save"
+ android:icon="@drawable/ic_save" android:showAsAction="ifRoom"/>
+ <item android:id="@+id/action_xml" android:title="@string/action_xml"
+ android:showAsAction="ifRoom"/>
+</menu>
diff --git a/apps/CtsVerifier/res/raw/cone_obj b/apps/CtsVerifier/res/raw/cone_obj
new file mode 100644
index 0000000..c95136a
--- /dev/null
+++ b/apps/CtsVerifier/res/raw/cone_obj
@@ -0,0 +1,132 @@
+# Blender v2.76 (sub 0) OBJ File: 'cone.blend'
+# www.blender.org
+o Cone
+v 0.000000 -0.075000 0.125000
+v 0.000000 -0.000000 -0.125000
+v 0.014632 -0.073559 0.125000
+v 0.028701 -0.069291 0.125000
+v 0.041668 -0.062360 0.125000
+v 0.053033 -0.053033 0.125000
+v 0.062360 -0.041668 0.125000
+v 0.069291 -0.028701 0.125000
+v 0.073559 -0.014632 0.125000
+v 0.075000 -0.000000 0.125000
+v 0.073559 0.014632 0.125000
+v 0.069291 0.028701 0.125000
+v 0.062360 0.041668 0.125000
+v 0.053033 0.053033 0.125000
+v 0.041668 0.062360 0.125000
+v 0.028701 0.069291 0.125000
+v 0.014632 0.073559 0.125000
+v -0.000000 0.075000 0.125000
+v -0.014632 0.073559 0.125000
+v -0.028701 0.069291 0.125000
+v -0.041668 0.062360 0.125000
+v -0.053033 0.053033 0.125000
+v -0.062360 0.041668 0.125000
+v -0.069291 0.028701 0.125000
+v -0.073559 0.014632 0.125000
+v -0.075000 -0.000000 0.125000
+v -0.073559 -0.014632 0.125000
+v -0.069291 -0.028701 0.125000
+v -0.062360 -0.041668 0.125000
+v -0.053033 -0.053033 0.125000
+v -0.041668 -0.062360 0.125000
+v -0.028701 -0.069291 0.125000
+v -0.014632 -0.073559 0.125000
+vn -0.278200 -0.916900 -0.286100
+vn 0.093900 -0.953600 -0.286100
+vn -0.451700 -0.845100 -0.286100
+vn -0.607900 -0.740700 -0.286100
+vn -0.740700 -0.607900 -0.286100
+vn -0.845100 -0.451700 -0.286100
+vn -0.916900 -0.278200 -0.286100
+vn -0.953600 -0.093900 -0.286100
+vn -0.953600 0.093900 -0.286100
+vn -0.916900 0.278200 -0.286100
+vn -0.845100 0.451700 -0.286100
+vn -0.740700 0.607900 -0.286100
+vn -0.607900 0.740700 -0.286100
+vn -0.451700 0.845100 -0.286100
+vn -0.278200 0.916900 -0.286100
+vn -0.093900 0.953600 -0.286100
+vn 0.093900 0.953600 -0.286100
+vn 0.278200 0.916900 -0.286100
+vn 0.451700 0.845100 -0.286100
+vn 0.607900 0.740700 -0.286100
+vn 0.740700 0.607900 -0.286100
+vn 0.845100 0.451700 -0.286100
+vn 0.916900 0.278200 -0.286100
+vn 0.953600 0.093900 -0.286100
+vn 0.953600 -0.093900 -0.286100
+vn 0.916900 -0.278200 -0.286100
+vn 0.845100 -0.451700 -0.286100
+vn 0.740700 -0.607900 -0.286100
+vn 0.607900 -0.740700 -0.286100
+vn 0.451700 -0.845100 -0.286100
+vn -0.093900 -0.953600 -0.286100
+vn 0.278200 -0.916900 -0.286100
+vn 0.000000 0.000000 1.000000
+s off
+f 32//1 2//1 33//1
+f 1//2 2//2 3//2
+f 31//3 2//3 32//3
+f 30//4 2//4 31//4
+f 29//5 2//5 30//5
+f 28//6 2//6 29//6
+f 27//7 2//7 28//7
+f 26//8 2//8 27//8
+f 25//9 2//9 26//9
+f 24//10 2//10 25//10
+f 23//11 2//11 24//11
+f 22//12 2//12 23//12
+f 21//13 2//13 22//13
+f 20//14 2//14 21//14
+f 19//15 2//15 20//15
+f 18//16 2//16 19//16
+f 17//17 2//17 18//17
+f 16//18 2//18 17//18
+f 15//19 2//19 16//19
+f 14//20 2//20 15//20
+f 13//21 2//21 14//21
+f 12//22 2//22 13//22
+f 11//23 2//23 12//23
+f 10//24 2//24 11//24
+f 9//25 2//25 10//25
+f 8//26 2//26 9//26
+f 7//27 2//27 8//27
+f 6//28 2//28 7//28
+f 5//29 2//29 6//29
+f 4//30 2//30 5//30
+f 33//31 2//31 1//31
+f 3//32 2//32 4//32
+f 17//33 25//33 9//33
+f 33//33 1//33 3//33
+f 3//33 4//33 5//33
+f 5//33 6//33 7//33
+f 7//33 8//33 5//33
+f 9//33 10//33 13//33
+f 11//33 12//33 13//33
+f 13//33 14//33 15//33
+f 15//33 16//33 17//33
+f 17//33 18//33 19//33
+f 19//33 20//33 21//33
+f 21//33 22//33 23//33
+f 23//33 24//33 25//33
+f 25//33 26//33 27//33
+f 27//33 28//33 29//33
+f 29//33 30//33 31//33
+f 31//33 32//33 33//33
+f 33//33 3//33 9//33
+f 5//33 8//33 9//33
+f 10//33 11//33 13//33
+f 13//33 15//33 9//33
+f 17//33 19//33 25//33
+f 21//33 23//33 25//33
+f 25//33 27//33 33//33
+f 29//33 31//33 33//33
+f 3//33 5//33 9//33
+f 9//33 15//33 17//33
+f 19//33 21//33 25//33
+f 27//33 29//33 33//33
+f 33//33 9//33 25//33
diff --git a/apps/CtsVerifier/res/raw/ring_obj b/apps/CtsVerifier/res/raw/ring_obj
new file mode 100644
index 0000000..01b6e7c
--- /dev/null
+++ b/apps/CtsVerifier/res/raw/ring_obj
@@ -0,0 +1,8144 @@
+# Blender v2.76 (sub 0) OBJ File: 'ring3.blend'
+# www.blender.org
+mtllib ring.mtl
+o Torus
+v 0.172500 0.000000 0.000000
+v 0.171733 -0.000000 0.005823
+v 0.169486 -0.000000 0.011250
+v 0.165910 -0.000000 0.015910
+v 0.161250 -0.000000 0.019486
+v 0.155823 -0.000000 0.021733
+v 0.150000 -0.000000 0.022500
+v 0.144177 -0.000000 0.021733
+v 0.138750 -0.000000 0.019486
+v 0.134090 -0.000000 0.015910
+v 0.130514 -0.000000 0.011250
+v 0.128267 -0.000000 0.005823
+v 0.127500 -0.000000 0.000000
+v 0.128267 0.000000 -0.005823
+v 0.130514 0.000000 -0.011250
+v 0.134090 0.000000 -0.015910
+v 0.138750 0.000000 -0.019486
+v 0.144177 0.000000 -0.021733
+v 0.150000 0.000000 -0.022500
+v 0.155823 0.000000 -0.021733
+v 0.161250 0.000000 -0.019486
+v 0.165910 0.000000 -0.015910
+v 0.169486 0.000000 -0.011250
+v 0.171733 0.000000 -0.005823
+v 0.172131 0.011282 0.000000
+v 0.171366 0.011232 0.005823
+v 0.169123 0.011085 0.011250
+v 0.165555 0.010851 0.015910
+v 0.160905 0.010546 0.019486
+v 0.155490 0.010191 0.021733
+v 0.149679 0.009810 0.022500
+v 0.143868 0.009430 0.021733
+v 0.138453 0.009075 0.019486
+v 0.133803 0.008770 0.015910
+v 0.130235 0.008536 0.011250
+v 0.127992 0.008389 0.005823
+v 0.127227 0.008339 0.000000
+v 0.127992 0.008389 -0.005823
+v 0.130235 0.008536 -0.011250
+v 0.133803 0.008770 -0.015910
+v 0.138453 0.009075 -0.019486
+v 0.143868 0.009430 -0.021733
+v 0.149679 0.009810 -0.022500
+v 0.155490 0.010191 -0.021733
+v 0.160905 0.010546 -0.019486
+v 0.165555 0.010851 -0.015910
+v 0.169123 0.011085 -0.011250
+v 0.171366 0.011232 -0.005823
+v 0.171024 0.022516 0.000000
+v 0.170264 0.022416 0.005823
+v 0.168036 0.022122 0.011250
+v 0.164491 0.021656 0.015910
+v 0.159871 0.021047 0.019486
+v 0.154490 0.020339 0.021733
+v 0.148717 0.019579 0.022500
+v 0.142943 0.018819 0.021733
+v 0.137563 0.018111 0.019486
+v 0.132943 0.017502 0.015910
+v 0.129398 0.017036 0.011250
+v 0.127169 0.016742 0.005823
+v 0.126409 0.016642 0.000000
+v 0.127169 0.016742 -0.005823
+v 0.129398 0.017036 -0.011250
+v 0.132943 0.017502 -0.015910
+v 0.137563 0.018111 -0.019486
+v 0.142943 0.018819 -0.021733
+v 0.148717 0.019579 -0.022500
+v 0.154490 0.020339 -0.021733
+v 0.159871 0.021047 -0.019486
+v 0.164491 0.021656 -0.015910
+v 0.168036 0.022122 -0.011250
+v 0.170264 0.022416 -0.005823
+v 0.169185 0.033653 0.000000
+v 0.168434 0.033504 0.005823
+v 0.166229 0.033065 0.011250
+v 0.162722 0.032367 0.015910
+v 0.158152 0.031458 0.019486
+v 0.152829 0.030400 0.021733
+v 0.147118 0.029264 0.022500
+v 0.141406 0.028127 0.021733
+v 0.136084 0.027069 0.019486
+v 0.131514 0.026160 0.015910
+v 0.128007 0.025462 0.011250
+v 0.125802 0.025024 0.005823
+v 0.125050 0.024874 0.000000
+v 0.125802 0.025024 -0.005823
+v 0.128007 0.025462 -0.011250
+v 0.131514 0.026160 -0.015910
+v 0.136084 0.027069 -0.019486
+v 0.141406 0.028127 -0.021733
+v 0.147118 0.029264 -0.022500
+v 0.152829 0.030400 -0.021733
+v 0.158152 0.031458 -0.019486
+v 0.162722 0.032367 -0.015910
+v 0.166229 0.033065 -0.011250
+v 0.168434 0.033504 -0.005823
+v 0.166622 0.044646 0.000000
+v 0.165882 0.044448 0.005823
+v 0.163711 0.043866 0.011250
+v 0.160257 0.042941 0.015910
+v 0.155756 0.041735 0.019486
+v 0.150514 0.040330 0.021733
+v 0.144889 0.038823 0.022500
+v 0.139264 0.037316 0.021733
+v 0.134022 0.035911 0.019486
+v 0.129521 0.034705 0.015910
+v 0.126067 0.033780 0.011250
+v 0.123896 0.033198 0.005823
+v 0.123156 0.032999 0.000000
+v 0.123896 0.033198 -0.005823
+v 0.126067 0.033780 -0.011250
+v 0.129521 0.034705 -0.015910
+v 0.134022 0.035911 -0.019486
+v 0.139264 0.037316 -0.021733
+v 0.144889 0.038823 -0.022500
+v 0.150514 0.040330 -0.021733
+v 0.155756 0.041735 -0.019486
+v 0.160257 0.042941 -0.015910
+v 0.163711 0.043866 -0.011250
+v 0.165882 0.044448 -0.005823
+v 0.163345 0.055448 0.000000
+v 0.162619 0.055202 0.005823
+v 0.160491 0.054479 0.011250
+v 0.157105 0.053330 0.015910
+v 0.152692 0.051832 0.019486
+v 0.147554 0.050088 0.021733
+v 0.142040 0.048216 0.022500
+v 0.136525 0.046344 0.021733
+v 0.131387 0.044600 0.019486
+v 0.126974 0.043102 0.015910
+v 0.123588 0.041952 0.011250
+v 0.121460 0.041230 0.005823
+v 0.120734 0.040984 0.000000
+v 0.121460 0.041230 -0.005823
+v 0.123588 0.041952 -0.011250
+v 0.126974 0.043102 -0.015910
+v 0.131387 0.044600 -0.019486
+v 0.136525 0.046344 -0.021733
+v 0.142040 0.048216 -0.022500
+v 0.147554 0.050088 -0.021733
+v 0.152692 0.051832 -0.019486
+v 0.157105 0.053330 -0.015910
+v 0.160491 0.054479 -0.011250
+v 0.162619 0.055202 -0.005823
+v 0.159369 0.066013 0.000000
+v 0.158661 0.065719 0.005823
+v 0.156584 0.064859 0.011250
+v 0.153281 0.063491 0.015910
+v 0.148976 0.061708 0.019486
+v 0.143962 0.059631 0.021733
+v 0.138582 0.057403 0.022500
+v 0.133202 0.055174 0.021733
+v 0.128188 0.053097 0.019486
+v 0.123883 0.051314 0.015910
+v 0.120580 0.049946 0.011250
+v 0.118503 0.049086 0.005823
+v 0.117795 0.048792 0.000000
+v 0.118503 0.049086 -0.005823
+v 0.120580 0.049946 -0.011250
+v 0.123883 0.051314 -0.015910
+v 0.128188 0.053097 -0.019486
+v 0.133202 0.055174 -0.021733
+v 0.138582 0.057403 -0.022500
+v 0.143962 0.059631 -0.021733
+v 0.148976 0.061708 -0.019486
+v 0.153281 0.063491 -0.015910
+v 0.156584 0.064859 -0.011250
+v 0.158661 0.065719 -0.005823
+v 0.154711 0.076295 0.000000
+v 0.154023 0.075956 0.005823
+v 0.152007 0.074962 0.011250
+v 0.148800 0.073380 0.015910
+v 0.144621 0.071319 0.019486
+v 0.139754 0.068919 0.021733
+v 0.134531 0.066343 0.022500
+v 0.129308 0.063768 0.021733
+v 0.124441 0.061368 0.019486
+v 0.120262 0.059307 0.015910
+v 0.117055 0.057725 0.011250
+v 0.115039 0.056731 0.005823
+v 0.114351 0.056392 0.000000
+v 0.115039 0.056731 -0.005823
+v 0.117055 0.057725 -0.011250
+v 0.120262 0.059307 -0.015910
+v 0.124441 0.061368 -0.019486
+v 0.129308 0.063768 -0.021733
+v 0.134531 0.066343 -0.022500
+v 0.139754 0.068919 -0.021733
+v 0.144621 0.071319 -0.019486
+v 0.148800 0.073380 -0.015910
+v 0.152007 0.074962 -0.011250
+v 0.154023 0.075956 -0.005823
+v 0.149389 0.086250 0.000000
+v 0.148725 0.085867 0.005823
+v 0.146779 0.084743 0.011250
+v 0.143682 0.082955 0.015910
+v 0.139647 0.080625 0.019486
+v 0.134947 0.077912 0.021733
+v 0.129904 0.075000 0.022500
+v 0.124861 0.072088 0.021733
+v 0.120161 0.069375 0.019486
+v 0.116125 0.067045 0.015910
+v 0.113029 0.065257 0.011250
+v 0.111082 0.064133 0.005823
+v 0.110418 0.063750 0.000000
+v 0.111082 0.064133 -0.005823
+v 0.113029 0.065257 -0.011250
+v 0.116125 0.067045 -0.015910
+v 0.120161 0.069375 -0.019486
+v 0.124861 0.072088 -0.021733
+v 0.129904 0.075000 -0.022500
+v 0.134947 0.077912 -0.021733
+v 0.139647 0.080625 -0.019486
+v 0.143682 0.082955 -0.015910
+v 0.146779 0.084743 -0.011250
+v 0.148725 0.085867 -0.005823
+v 0.143429 0.095836 0.000000
+v 0.142791 0.095410 0.005823
+v 0.140922 0.094161 0.011250
+v 0.137949 0.092175 0.015910
+v 0.134074 0.089586 0.019486
+v 0.129562 0.086571 0.021733
+v 0.124720 0.083336 0.022500
+v 0.119878 0.080100 0.021733
+v 0.115366 0.077085 0.019486
+v 0.111492 0.074496 0.015910
+v 0.108519 0.072510 0.011250
+v 0.106650 0.071261 0.005823
+v 0.106012 0.070835 0.000000
+v 0.106650 0.071261 -0.005823
+v 0.108519 0.072510 -0.011250
+v 0.111492 0.074496 -0.015910
+v 0.115366 0.077085 -0.019486
+v 0.119878 0.080100 -0.021733
+v 0.124720 0.083336 -0.022500
+v 0.129562 0.086571 -0.021733
+v 0.134074 0.089586 -0.019486
+v 0.137949 0.092175 -0.015910
+v 0.140922 0.094161 -0.011250
+v 0.142791 0.095410 -0.005823
+v 0.136853 0.105011 0.000000
+v 0.136245 0.104545 0.005823
+v 0.134462 0.103176 0.011250
+v 0.131625 0.101000 0.015910
+v 0.127928 0.098163 0.019486
+v 0.123623 0.094859 0.021733
+v 0.119003 0.091314 0.022500
+v 0.114383 0.087769 0.021733
+v 0.110078 0.084466 0.019486
+v 0.106381 0.081629 0.015910
+v 0.103544 0.079452 0.011250
+v 0.101761 0.078084 0.005823
+v 0.101153 0.077617 0.000000
+v 0.101761 0.078084 -0.005823
+v 0.103544 0.079452 -0.011250
+v 0.106381 0.081629 -0.015910
+v 0.110078 0.084466 -0.019486
+v 0.114383 0.087769 -0.021733
+v 0.119003 0.091314 -0.022500
+v 0.123623 0.094859 -0.021733
+v 0.127928 0.098163 -0.019486
+v 0.131625 0.101000 -0.015910
+v 0.134462 0.103176 -0.011250
+v 0.136245 0.104545 -0.005823
+v 0.129692 0.113737 0.000000
+v 0.129116 0.113232 0.005823
+v 0.127426 0.111750 0.011250
+v 0.124738 0.109392 0.015910
+v 0.121234 0.106320 0.019486
+v 0.117154 0.102742 0.021733
+v 0.112776 0.098902 0.022500
+v 0.108398 0.095062 0.021733
+v 0.104318 0.091484 0.019486
+v 0.100814 0.088412 0.015910
+v 0.098126 0.086054 0.011250
+v 0.096436 0.084572 0.005823
+v 0.095860 0.084067 0.000000
+v 0.096436 0.084572 -0.005823
+v 0.098126 0.086054 -0.011250
+v 0.100814 0.088412 -0.015910
+v 0.104318 0.091484 -0.019486
+v 0.108398 0.095062 -0.021733
+v 0.112776 0.098902 -0.022500
+v 0.117154 0.102742 -0.021733
+v 0.121234 0.106320 -0.019486
+v 0.124738 0.109392 -0.015910
+v 0.127426 0.111750 -0.011250
+v 0.129116 0.113232 -0.005823
+v 0.121976 0.121976 0.000000
+v 0.121434 0.121434 0.005823
+v 0.119844 0.119844 0.011250
+v 0.117316 0.117316 0.015910
+v 0.114021 0.114021 0.019486
+v 0.110184 0.110184 0.021733
+v 0.106066 0.106066 0.022500
+v 0.101948 0.101948 0.021733
+v 0.098111 0.098111 0.019486
+v 0.094816 0.094816 0.015910
+v 0.092288 0.092288 0.011250
+v 0.090698 0.090698 0.005823
+v 0.090156 0.090156 0.000000
+v 0.090698 0.090698 -0.005823
+v 0.092288 0.092288 -0.011250
+v 0.094816 0.094816 -0.015910
+v 0.098111 0.098111 -0.019486
+v 0.101948 0.101948 -0.021733
+v 0.106066 0.106066 -0.022500
+v 0.110184 0.110184 -0.021733
+v 0.114021 0.114021 -0.019486
+v 0.117316 0.117316 -0.015910
+v 0.119844 0.119844 -0.011250
+v 0.121434 0.121434 -0.005823
+v 0.113737 0.129692 0.000000
+v 0.113232 0.129116 0.005823
+v 0.111750 0.127426 0.011250
+v 0.109392 0.124738 0.015910
+v 0.106320 0.121234 0.019486
+v 0.102742 0.117154 0.021733
+v 0.098902 0.112776 0.022500
+v 0.095062 0.108398 0.021733
+v 0.091484 0.104318 0.019486
+v 0.088412 0.100814 0.015910
+v 0.086054 0.098126 0.011250
+v 0.084572 0.096436 0.005823
+v 0.084067 0.095860 0.000000
+v 0.084572 0.096436 -0.005823
+v 0.086054 0.098126 -0.011250
+v 0.088412 0.100814 -0.015910
+v 0.091484 0.104318 -0.019486
+v 0.095062 0.108398 -0.021733
+v 0.098902 0.112776 -0.022500
+v 0.102742 0.117154 -0.021733
+v 0.106320 0.121234 -0.019486
+v 0.109392 0.124738 -0.015910
+v 0.111750 0.127426 -0.011250
+v 0.113232 0.129116 -0.005823
+v 0.105011 0.136853 0.000000
+v 0.104545 0.136245 0.005823
+v 0.103176 0.134462 0.011250
+v 0.101000 0.131625 0.015910
+v 0.098163 0.127928 0.019486
+v 0.094859 0.123623 0.021733
+v 0.091314 0.119003 0.022500
+v 0.087769 0.114383 0.021733
+v 0.084466 0.110078 0.019486
+v 0.081629 0.106381 0.015910
+v 0.079452 0.103544 0.011250
+v 0.078084 0.101761 0.005823
+v 0.077617 0.101153 0.000000
+v 0.078084 0.101761 -0.005823
+v 0.079452 0.103544 -0.011250
+v 0.081629 0.106381 -0.015910
+v 0.084466 0.110078 -0.019486
+v 0.087769 0.114383 -0.021733
+v 0.091314 0.119003 -0.022500
+v 0.094859 0.123623 -0.021733
+v 0.098163 0.127928 -0.019486
+v 0.101000 0.131625 -0.015910
+v 0.103176 0.134462 -0.011250
+v 0.104545 0.136245 -0.005823
+v 0.095836 0.143429 0.000000
+v 0.095410 0.142791 0.005823
+v 0.094161 0.140922 0.011250
+v 0.092175 0.137949 0.015910
+v 0.089586 0.134074 0.019486
+v 0.086571 0.129562 0.021733
+v 0.083336 0.124720 0.022500
+v 0.080100 0.119878 0.021733
+v 0.077085 0.115366 0.019486
+v 0.074496 0.111492 0.015910
+v 0.072510 0.108519 0.011250
+v 0.071261 0.106650 0.005823
+v 0.070835 0.106012 0.000000
+v 0.071261 0.106650 -0.005823
+v 0.072510 0.108519 -0.011250
+v 0.074496 0.111492 -0.015910
+v 0.077085 0.115366 -0.019486
+v 0.080100 0.119878 -0.021733
+v 0.083336 0.124720 -0.022500
+v 0.086571 0.129562 -0.021733
+v 0.089586 0.134074 -0.019486
+v 0.092175 0.137949 -0.015910
+v 0.094161 0.140922 -0.011250
+v 0.095410 0.142791 -0.005823
+v 0.086250 0.149389 0.000000
+v 0.085867 0.148725 0.005823
+v 0.084743 0.146779 0.011250
+v 0.082955 0.143682 0.015910
+v 0.080625 0.139647 0.019486
+v 0.077912 0.134947 0.021733
+v 0.075000 0.129904 0.022500
+v 0.072088 0.124861 0.021733
+v 0.069375 0.120161 0.019486
+v 0.067045 0.116125 0.015910
+v 0.065257 0.113029 0.011250
+v 0.064133 0.111082 0.005823
+v 0.063750 0.110418 0.000000
+v 0.064133 0.111082 -0.005823
+v 0.065257 0.113029 -0.011250
+v 0.067045 0.116125 -0.015910
+v 0.069375 0.120161 -0.019486
+v 0.072088 0.124861 -0.021733
+v 0.075000 0.129904 -0.022500
+v 0.077912 0.134947 -0.021733
+v 0.080625 0.139647 -0.019486
+v 0.082955 0.143682 -0.015910
+v 0.084743 0.146779 -0.011250
+v 0.085867 0.148725 -0.005823
+v 0.076295 0.154711 0.000000
+v 0.075956 0.154023 0.005823
+v 0.074962 0.152007 0.011250
+v 0.073380 0.148800 0.015910
+v 0.071319 0.144621 0.019486
+v 0.068919 0.139754 0.021733
+v 0.066343 0.134531 0.022500
+v 0.063768 0.129308 0.021733
+v 0.061368 0.124441 0.019486
+v 0.059307 0.120262 0.015910
+v 0.057725 0.117055 0.011250
+v 0.056731 0.115039 0.005823
+v 0.056392 0.114351 0.000000
+v 0.056731 0.115039 -0.005823
+v 0.057725 0.117055 -0.011250
+v 0.059307 0.120262 -0.015910
+v 0.061368 0.124441 -0.019486
+v 0.063768 0.129308 -0.021733
+v 0.066343 0.134531 -0.022500
+v 0.068919 0.139754 -0.021733
+v 0.071319 0.144621 -0.019486
+v 0.073380 0.148800 -0.015910
+v 0.074962 0.152007 -0.011250
+v 0.075956 0.154023 -0.005823
+v 0.066013 0.159369 0.000000
+v 0.065720 0.158661 0.005823
+v 0.064859 0.156584 0.011250
+v 0.063491 0.153281 0.015910
+v 0.061708 0.148976 0.019486
+v 0.059631 0.143962 0.021733
+v 0.057403 0.138582 0.022500
+v 0.055174 0.133202 0.021733
+v 0.053097 0.128188 0.019486
+v 0.051314 0.123883 0.015910
+v 0.049946 0.120580 0.011250
+v 0.049086 0.118503 0.005823
+v 0.048792 0.117795 0.000000
+v 0.049086 0.118503 -0.005823
+v 0.049946 0.120580 -0.011250
+v 0.051314 0.123883 -0.015910
+v 0.053097 0.128188 -0.019486
+v 0.055174 0.133202 -0.021733
+v 0.057403 0.138582 -0.022500
+v 0.059631 0.143962 -0.021733
+v 0.061708 0.148976 -0.019486
+v 0.063491 0.153281 -0.015910
+v 0.064859 0.156584 -0.011250
+v 0.065720 0.158661 -0.005823
+v 0.055448 0.163345 0.000000
+v 0.055202 0.162619 0.005823
+v 0.054479 0.160491 0.011250
+v 0.053330 0.157105 0.015910
+v 0.051832 0.152692 0.019486
+v 0.050088 0.147554 0.021733
+v 0.048216 0.142040 0.022500
+v 0.046344 0.136525 0.021733
+v 0.044600 0.131387 0.019486
+v 0.043102 0.126974 0.015910
+v 0.041952 0.123588 0.011250
+v 0.041230 0.121460 0.005823
+v 0.040984 0.120734 0.000000
+v 0.041230 0.121460 -0.005823
+v 0.041952 0.123588 -0.011250
+v 0.043102 0.126974 -0.015910
+v 0.044600 0.131387 -0.019486
+v 0.046344 0.136525 -0.021733
+v 0.048216 0.142040 -0.022500
+v 0.050088 0.147554 -0.021733
+v 0.051832 0.152692 -0.019486
+v 0.053330 0.157105 -0.015910
+v 0.054479 0.160491 -0.011250
+v 0.055202 0.162619 -0.005823
+v 0.044646 0.166622 0.000000
+v 0.044448 0.165882 0.005823
+v 0.043866 0.163710 0.011250
+v 0.042941 0.160257 0.015910
+v 0.041735 0.155756 0.019486
+v 0.040330 0.150514 0.021733
+v 0.038823 0.144889 0.022500
+v 0.037316 0.139264 0.021733
+v 0.035911 0.134022 0.019486
+v 0.034705 0.129521 0.015910
+v 0.033780 0.126067 0.011250
+v 0.033198 0.123896 0.005823
+v 0.032999 0.123156 0.000000
+v 0.033198 0.123896 -0.005823
+v 0.033780 0.126067 -0.011250
+v 0.034705 0.129521 -0.015910
+v 0.035911 0.134022 -0.019486
+v 0.037316 0.139264 -0.021733
+v 0.038823 0.144889 -0.022500
+v 0.040330 0.150514 -0.021733
+v 0.041735 0.155756 -0.019486
+v 0.042941 0.160257 -0.015910
+v 0.043866 0.163710 -0.011250
+v 0.044448 0.165882 -0.005823
+v 0.033653 0.169185 0.000000
+v 0.033504 0.168434 0.005823
+v 0.033065 0.166229 0.011250
+v 0.032367 0.162722 0.015910
+v 0.031458 0.158152 0.019486
+v 0.030400 0.152829 0.021733
+v 0.029264 0.147118 0.022500
+v 0.028127 0.141406 0.021733
+v 0.027069 0.136084 0.019486
+v 0.026160 0.131514 0.015910
+v 0.025462 0.128007 0.011250
+v 0.025024 0.125802 0.005823
+v 0.024874 0.125050 0.000000
+v 0.025024 0.125802 -0.005823
+v 0.025462 0.128007 -0.011250
+v 0.026160 0.131514 -0.015910
+v 0.027069 0.136084 -0.019486
+v 0.028127 0.141406 -0.021733
+v 0.029264 0.147118 -0.022500
+v 0.030400 0.152829 -0.021733
+v 0.031458 0.158152 -0.019486
+v 0.032367 0.162722 -0.015910
+v 0.033065 0.166229 -0.011250
+v 0.033504 0.168434 -0.005823
+v 0.022516 0.171024 0.000000
+v 0.022416 0.170264 0.005823
+v 0.022122 0.168036 0.011250
+v 0.021656 0.164491 0.015910
+v 0.021047 0.159871 0.019486
+v 0.020339 0.154490 0.021733
+v 0.019579 0.148717 0.022500
+v 0.018819 0.142943 0.021733
+v 0.018110 0.137563 0.019486
+v 0.017502 0.132943 0.015910
+v 0.017036 0.129398 0.011250
+v 0.016742 0.127169 0.005823
+v 0.016642 0.126409 0.000000
+v 0.016742 0.127169 -0.005823
+v 0.017036 0.129398 -0.011250
+v 0.017502 0.132943 -0.015910
+v 0.018110 0.137563 -0.019486
+v 0.018819 0.142943 -0.021733
+v 0.019579 0.148717 -0.022500
+v 0.020339 0.154490 -0.021733
+v 0.021047 0.159871 -0.019486
+v 0.021656 0.164491 -0.015910
+v 0.022122 0.168036 -0.011250
+v 0.022416 0.170264 -0.005823
+v 0.011282 0.172131 0.000000
+v 0.011232 0.171366 0.005823
+v 0.011085 0.169123 0.011250
+v 0.010851 0.165555 0.015910
+v 0.010546 0.160905 0.019486
+v 0.010191 0.155490 0.021733
+v 0.009810 0.149679 0.022500
+v 0.009430 0.143868 0.021733
+v 0.009075 0.138453 0.019486
+v 0.008770 0.133803 0.015910
+v 0.008536 0.130235 0.011250
+v 0.008389 0.127992 0.005823
+v 0.008339 0.127227 0.000000
+v 0.008389 0.127992 -0.005823
+v 0.008536 0.130235 -0.011250
+v 0.008770 0.133803 -0.015910
+v 0.009075 0.138453 -0.019486
+v 0.009430 0.143868 -0.021733
+v 0.009810 0.149679 -0.022500
+v 0.010191 0.155490 -0.021733
+v 0.010546 0.160905 -0.019486
+v 0.010851 0.165555 -0.015910
+v 0.011085 0.169123 -0.011250
+v 0.011232 0.171366 -0.005823
+v 0.000000 0.172500 0.000000
+v 0.000000 0.171733 0.005823
+v 0.000000 0.169486 0.011250
+v 0.000000 0.165910 0.015910
+v 0.000000 0.161250 0.019486
+v 0.000000 0.155823 0.021733
+v 0.000000 0.150000 0.022500
+v 0.000000 0.144177 0.021733
+v 0.000000 0.138750 0.019486
+v 0.000000 0.134090 0.015910
+v 0.000000 0.130514 0.011250
+v 0.000000 0.128267 0.005823
+v 0.000000 0.127500 0.000000
+v 0.000000 0.128267 -0.005823
+v 0.000000 0.130514 -0.011250
+v 0.000000 0.134090 -0.015910
+v 0.000000 0.138750 -0.019486
+v 0.000000 0.144177 -0.021733
+v 0.000000 0.150000 -0.022500
+v 0.000000 0.155823 -0.021733
+v 0.000000 0.161250 -0.019486
+v 0.000000 0.165910 -0.015910
+v 0.000000 0.169486 -0.011250
+v 0.000000 0.171733 -0.005823
+v -0.011282 0.172131 0.000000
+v -0.011232 0.171366 0.005823
+v -0.011085 0.169123 0.011250
+v -0.010851 0.165555 0.015910
+v -0.010546 0.160905 0.019486
+v -0.010191 0.155490 0.021733
+v -0.009810 0.149679 0.022500
+v -0.009430 0.143868 0.021733
+v -0.009075 0.138453 0.019486
+v -0.008770 0.133803 0.015910
+v -0.008536 0.130235 0.011250
+v -0.008389 0.127992 0.005823
+v -0.008339 0.127227 0.000000
+v -0.008389 0.127992 -0.005823
+v -0.008536 0.130235 -0.011250
+v -0.008770 0.133803 -0.015910
+v -0.009075 0.138453 -0.019486
+v -0.009430 0.143868 -0.021733
+v -0.009810 0.149679 -0.022500
+v -0.010191 0.155490 -0.021733
+v -0.010546 0.160905 -0.019486
+v -0.010851 0.165555 -0.015910
+v -0.011085 0.169123 -0.011250
+v -0.011232 0.171366 -0.005823
+v -0.022516 0.171024 0.000000
+v -0.022416 0.170264 0.005823
+v -0.022122 0.168036 0.011250
+v -0.021656 0.164491 0.015910
+v -0.021047 0.159871 0.019486
+v -0.020339 0.154490 0.021733
+v -0.019579 0.148717 0.022500
+v -0.018819 0.142943 0.021733
+v -0.018111 0.137563 0.019486
+v -0.017502 0.132943 0.015910
+v -0.017036 0.129398 0.011250
+v -0.016742 0.127169 0.005823
+v -0.016642 0.126409 0.000000
+v -0.016742 0.127169 -0.005823
+v -0.017036 0.129398 -0.011250
+v -0.017502 0.132943 -0.015910
+v -0.018111 0.137563 -0.019486
+v -0.018819 0.142943 -0.021733
+v -0.019579 0.148717 -0.022500
+v -0.020339 0.154490 -0.021733
+v -0.021047 0.159871 -0.019486
+v -0.021656 0.164491 -0.015910
+v -0.022122 0.168036 -0.011250
+v -0.022416 0.170264 -0.005823
+v -0.033653 0.169185 0.000000
+v -0.033504 0.168434 0.005823
+v -0.033065 0.166229 0.011250
+v -0.032367 0.162722 0.015910
+v -0.031458 0.158152 0.019486
+v -0.030400 0.152829 0.021733
+v -0.029264 0.147118 0.022500
+v -0.028127 0.141406 0.021733
+v -0.027069 0.136084 0.019486
+v -0.026160 0.131514 0.015910
+v -0.025462 0.128007 0.011250
+v -0.025024 0.125802 0.005823
+v -0.024874 0.125050 0.000000
+v -0.025024 0.125802 -0.005823
+v -0.025462 0.128007 -0.011250
+v -0.026160 0.131514 -0.015910
+v -0.027069 0.136084 -0.019486
+v -0.028127 0.141406 -0.021733
+v -0.029264 0.147118 -0.022500
+v -0.030400 0.152829 -0.021733
+v -0.031458 0.158152 -0.019486
+v -0.032367 0.162722 -0.015910
+v -0.033065 0.166229 -0.011250
+v -0.033504 0.168434 -0.005823
+v -0.044646 0.166622 0.000000
+v -0.044448 0.165882 0.005823
+v -0.043866 0.163710 0.011250
+v -0.042941 0.160257 0.015910
+v -0.041735 0.155756 0.019486
+v -0.040330 0.150514 0.021733
+v -0.038823 0.144889 0.022500
+v -0.037316 0.139264 0.021733
+v -0.035911 0.134022 0.019486
+v -0.034705 0.129521 0.015910
+v -0.033780 0.126067 0.011250
+v -0.033198 0.123896 0.005823
+v -0.032999 0.123156 0.000000
+v -0.033198 0.123896 -0.005823
+v -0.033780 0.126067 -0.011250
+v -0.034705 0.129521 -0.015910
+v -0.035911 0.134022 -0.019486
+v -0.037316 0.139264 -0.021733
+v -0.038823 0.144889 -0.022500
+v -0.040330 0.150514 -0.021733
+v -0.041735 0.155756 -0.019486
+v -0.042941 0.160257 -0.015910
+v -0.043866 0.163710 -0.011250
+v -0.044448 0.165882 -0.005823
+v -0.055448 0.163345 0.000000
+v -0.055202 0.162619 0.005823
+v -0.054479 0.160491 0.011250
+v -0.053330 0.157105 0.015910
+v -0.051832 0.152692 0.019486
+v -0.050088 0.147554 0.021733
+v -0.048216 0.142040 0.022500
+v -0.046344 0.136525 0.021733
+v -0.044600 0.131387 0.019486
+v -0.043102 0.126974 0.015910
+v -0.041953 0.123588 0.011250
+v -0.041230 0.121460 0.005823
+v -0.040984 0.120734 0.000000
+v -0.041230 0.121460 -0.005823
+v -0.041953 0.123588 -0.011250
+v -0.043102 0.126974 -0.015910
+v -0.044600 0.131387 -0.019486
+v -0.046344 0.136525 -0.021733
+v -0.048216 0.142040 -0.022500
+v -0.050088 0.147554 -0.021733
+v -0.051832 0.152692 -0.019486
+v -0.053330 0.157105 -0.015910
+v -0.054479 0.160491 -0.011250
+v -0.055202 0.162619 -0.005823
+v -0.066013 0.159369 0.000000
+v -0.065720 0.158661 0.005823
+v -0.064859 0.156584 0.011250
+v -0.063491 0.153281 0.015910
+v -0.061708 0.148976 0.019486
+v -0.059631 0.143962 0.021733
+v -0.057403 0.138582 0.022500
+v -0.055174 0.133202 0.021733
+v -0.053097 0.128188 0.019486
+v -0.051314 0.123883 0.015910
+v -0.049946 0.120580 0.011250
+v -0.049086 0.118503 0.005823
+v -0.048792 0.117795 0.000000
+v -0.049086 0.118503 -0.005823
+v -0.049946 0.120580 -0.011250
+v -0.051314 0.123883 -0.015910
+v -0.053097 0.128188 -0.019486
+v -0.055174 0.133202 -0.021733
+v -0.057403 0.138582 -0.022500
+v -0.059631 0.143962 -0.021733
+v -0.061708 0.148976 -0.019486
+v -0.063491 0.153281 -0.015910
+v -0.064859 0.156584 -0.011250
+v -0.065720 0.158661 -0.005823
+v -0.076295 0.154711 0.000000
+v -0.075956 0.154023 0.005823
+v -0.074962 0.152007 0.011250
+v -0.073380 0.148800 0.015910
+v -0.071319 0.144621 0.019486
+v -0.068919 0.139754 0.021733
+v -0.066343 0.134531 0.022500
+v -0.063768 0.129308 0.021733
+v -0.061368 0.124441 0.019486
+v -0.059307 0.120262 0.015910
+v -0.057725 0.117055 0.011250
+v -0.056731 0.115039 0.005823
+v -0.056392 0.114351 0.000000
+v -0.056731 0.115039 -0.005823
+v -0.057725 0.117055 -0.011250
+v -0.059307 0.120262 -0.015910
+v -0.061368 0.124441 -0.019486
+v -0.063768 0.129308 -0.021733
+v -0.066343 0.134531 -0.022500
+v -0.068919 0.139754 -0.021733
+v -0.071319 0.144621 -0.019486
+v -0.073380 0.148800 -0.015910
+v -0.074962 0.152007 -0.011250
+v -0.075956 0.154023 -0.005823
+v -0.086250 0.149389 0.000000
+v -0.085867 0.148725 0.005823
+v -0.084743 0.146779 0.011250
+v -0.082955 0.143682 0.015910
+v -0.080625 0.139647 0.019486
+v -0.077912 0.134947 0.021733
+v -0.075000 0.129904 0.022500
+v -0.072088 0.124861 0.021733
+v -0.069375 0.120161 0.019486
+v -0.067045 0.116125 0.015910
+v -0.065257 0.113029 0.011250
+v -0.064133 0.111082 0.005823
+v -0.063750 0.110418 0.000000
+v -0.064133 0.111082 -0.005823
+v -0.065257 0.113029 -0.011250
+v -0.067045 0.116125 -0.015910
+v -0.069375 0.120161 -0.019486
+v -0.072088 0.124861 -0.021733
+v -0.075000 0.129904 -0.022500
+v -0.077912 0.134947 -0.021733
+v -0.080625 0.139647 -0.019486
+v -0.082955 0.143682 -0.015910
+v -0.084743 0.146779 -0.011250
+v -0.085867 0.148725 -0.005823
+v -0.095836 0.143429 0.000000
+v -0.095410 0.142791 0.005823
+v -0.094161 0.140922 0.011250
+v -0.092175 0.137949 0.015910
+v -0.089586 0.134075 0.019486
+v -0.086571 0.129562 0.021733
+v -0.083335 0.124720 0.022500
+v -0.080100 0.119878 0.021733
+v -0.077085 0.115366 0.019486
+v -0.074496 0.111492 0.015910
+v -0.072510 0.108519 0.011250
+v -0.071261 0.106650 0.005823
+v -0.070835 0.106012 0.000000
+v -0.071261 0.106650 -0.005823
+v -0.072510 0.108519 -0.011250
+v -0.074496 0.111492 -0.015910
+v -0.077085 0.115366 -0.019486
+v -0.080100 0.119878 -0.021733
+v -0.083335 0.124720 -0.022500
+v -0.086571 0.129562 -0.021733
+v -0.089586 0.134075 -0.019486
+v -0.092175 0.137949 -0.015910
+v -0.094161 0.140922 -0.011250
+v -0.095410 0.142791 -0.005823
+v -0.105011 0.136853 0.000000
+v -0.104545 0.136245 0.005823
+v -0.103176 0.134462 0.011250
+v -0.101000 0.131625 0.015910
+v -0.098163 0.127928 0.019486
+v -0.094859 0.123623 0.021733
+v -0.091314 0.119003 0.022500
+v -0.087769 0.114383 0.021733
+v -0.084466 0.110078 0.019486
+v -0.081629 0.106381 0.015910
+v -0.079452 0.103544 0.011250
+v -0.078084 0.101761 0.005823
+v -0.077617 0.101153 0.000000
+v -0.078084 0.101761 -0.005823
+v -0.079452 0.103544 -0.011250
+v -0.081629 0.106381 -0.015910
+v -0.084466 0.110078 -0.019486
+v -0.087769 0.114383 -0.021733
+v -0.091314 0.119003 -0.022500
+v -0.094859 0.123623 -0.021733
+v -0.098163 0.127928 -0.019486
+v -0.101000 0.131625 -0.015910
+v -0.103176 0.134462 -0.011250
+v -0.104545 0.136245 -0.005823
+v -0.113737 0.129692 0.000000
+v -0.113232 0.129116 0.005823
+v -0.111750 0.127426 0.011250
+v -0.109392 0.124738 0.015910
+v -0.106320 0.121234 0.019486
+v -0.102742 0.117154 0.021733
+v -0.098902 0.112776 0.022500
+v -0.095062 0.108398 0.021733
+v -0.091484 0.104318 0.019486
+v -0.088412 0.100814 0.015910
+v -0.086054 0.098126 0.011250
+v -0.084572 0.096436 0.005823
+v -0.084067 0.095860 0.000000
+v -0.084572 0.096436 -0.005823
+v -0.086054 0.098126 -0.011250
+v -0.088412 0.100814 -0.015910
+v -0.091484 0.104318 -0.019486
+v -0.095062 0.108398 -0.021733
+v -0.098902 0.112776 -0.022500
+v -0.102742 0.117154 -0.021733
+v -0.106320 0.121234 -0.019486
+v -0.109392 0.124738 -0.015910
+v -0.111750 0.127426 -0.011250
+v -0.113232 0.129116 -0.005823
+v -0.121976 0.121976 0.000000
+v -0.121434 0.121434 0.005823
+v -0.119844 0.119844 0.011250
+v -0.117316 0.117316 0.015910
+v -0.114021 0.114021 0.019486
+v -0.110184 0.110184 0.021733
+v -0.106066 0.106066 0.022500
+v -0.101948 0.101948 0.021733
+v -0.098111 0.098111 0.019486
+v -0.094816 0.094816 0.015910
+v -0.092288 0.092288 0.011250
+v -0.090698 0.090698 0.005823
+v -0.090156 0.090156 0.000000
+v -0.090698 0.090698 -0.005823
+v -0.092288 0.092288 -0.011250
+v -0.094816 0.094816 -0.015910
+v -0.098111 0.098111 -0.019486
+v -0.101948 0.101948 -0.021733
+v -0.106066 0.106066 -0.022500
+v -0.110184 0.110184 -0.021733
+v -0.114021 0.114021 -0.019486
+v -0.117316 0.117316 -0.015910
+v -0.119844 0.119844 -0.011250
+v -0.121434 0.121434 -0.005823
+v -0.129692 0.113737 0.000000
+v -0.129116 0.113232 0.005823
+v -0.127426 0.111750 0.011250
+v -0.124738 0.109392 0.015910
+v -0.121234 0.106320 0.019486
+v -0.117154 0.102742 0.021733
+v -0.112776 0.098902 0.022500
+v -0.108398 0.095062 0.021733
+v -0.104318 0.091484 0.019486
+v -0.100814 0.088412 0.015910
+v -0.098126 0.086054 0.011250
+v -0.096436 0.084572 0.005823
+v -0.095860 0.084067 0.000000
+v -0.096436 0.084572 -0.005823
+v -0.098126 0.086054 -0.011250
+v -0.100814 0.088412 -0.015910
+v -0.104318 0.091484 -0.019486
+v -0.108398 0.095062 -0.021733
+v -0.112776 0.098902 -0.022500
+v -0.117154 0.102742 -0.021733
+v -0.121234 0.106320 -0.019486
+v -0.124738 0.109392 -0.015910
+v -0.127426 0.111750 -0.011250
+v -0.129116 0.113232 -0.005823
+v -0.136853 0.105011 0.000000
+v -0.136245 0.104545 0.005823
+v -0.134462 0.103176 0.011250
+v -0.131625 0.101000 0.015910
+v -0.127928 0.098163 0.019486
+v -0.123623 0.094859 0.021733
+v -0.119003 0.091314 0.022500
+v -0.114383 0.087769 0.021733
+v -0.110078 0.084466 0.019486
+v -0.106381 0.081629 0.015910
+v -0.103544 0.079452 0.011250
+v -0.101761 0.078084 0.005823
+v -0.101153 0.077617 0.000000
+v -0.101761 0.078084 -0.005823
+v -0.103544 0.079452 -0.011250
+v -0.106381 0.081629 -0.015910
+v -0.110078 0.084466 -0.019486
+v -0.114383 0.087769 -0.021733
+v -0.119003 0.091314 -0.022500
+v -0.123623 0.094859 -0.021733
+v -0.127928 0.098163 -0.019486
+v -0.131625 0.101000 -0.015910
+v -0.134462 0.103176 -0.011250
+v -0.136245 0.104545 -0.005823
+v -0.143428 0.095836 0.000000
+v -0.142791 0.095410 0.005823
+v -0.140922 0.094161 0.011250
+v -0.137949 0.092175 0.015910
+v -0.134074 0.089586 0.019486
+v -0.129562 0.086571 0.021733
+v -0.124720 0.083336 0.022500
+v -0.119878 0.080100 0.021733
+v -0.115366 0.077085 0.019486
+v -0.111492 0.074496 0.015910
+v -0.108519 0.072510 0.011250
+v -0.106650 0.071261 0.005823
+v -0.106012 0.070835 0.000000
+v -0.106650 0.071261 -0.005823
+v -0.108519 0.072510 -0.011250
+v -0.111492 0.074496 -0.015910
+v -0.115366 0.077085 -0.019486
+v -0.119878 0.080100 -0.021733
+v -0.124720 0.083336 -0.022500
+v -0.129562 0.086571 -0.021733
+v -0.134074 0.089586 -0.019486
+v -0.137949 0.092175 -0.015910
+v -0.140922 0.094161 -0.011250
+v -0.142791 0.095410 -0.005823
+v -0.149389 0.086250 0.000000
+v -0.148725 0.085867 0.005823
+v -0.146779 0.084743 0.011250
+v -0.143682 0.082955 0.015910
+v -0.139647 0.080625 0.019486
+v -0.134947 0.077912 0.021733
+v -0.129904 0.075000 0.022500
+v -0.124861 0.072088 0.021733
+v -0.120161 0.069375 0.019486
+v -0.116125 0.067045 0.015910
+v -0.113029 0.065257 0.011250
+v -0.111082 0.064133 0.005823
+v -0.110418 0.063750 0.000000
+v -0.111082 0.064133 -0.005823
+v -0.113029 0.065257 -0.011250
+v -0.116125 0.067045 -0.015910
+v -0.120161 0.069375 -0.019486
+v -0.124861 0.072088 -0.021733
+v -0.129904 0.075000 -0.022500
+v -0.134947 0.077912 -0.021733
+v -0.139647 0.080625 -0.019486
+v -0.143682 0.082955 -0.015910
+v -0.146779 0.084743 -0.011250
+v -0.148725 0.085867 -0.005823
+v -0.154711 0.076295 0.000000
+v -0.154023 0.075956 0.005823
+v -0.152007 0.074962 0.011250
+v -0.148800 0.073380 0.015910
+v -0.144621 0.071319 0.019486
+v -0.139754 0.068919 0.021733
+v -0.134531 0.066343 0.022500
+v -0.129308 0.063768 0.021733
+v -0.124441 0.061368 0.019486
+v -0.120262 0.059307 0.015910
+v -0.117055 0.057725 0.011250
+v -0.115039 0.056731 0.005823
+v -0.114351 0.056392 0.000000
+v -0.115039 0.056731 -0.005823
+v -0.117055 0.057725 -0.011250
+v -0.120262 0.059307 -0.015910
+v -0.124441 0.061368 -0.019486
+v -0.129308 0.063768 -0.021733
+v -0.134531 0.066343 -0.022500
+v -0.139754 0.068919 -0.021733
+v -0.144621 0.071319 -0.019486
+v -0.148800 0.073380 -0.015910
+v -0.152007 0.074962 -0.011250
+v -0.154023 0.075956 -0.005823
+v -0.159369 0.066013 0.000000
+v -0.158661 0.065720 0.005823
+v -0.156584 0.064859 0.011250
+v -0.153281 0.063491 0.015910
+v -0.148976 0.061708 0.019486
+v -0.143962 0.059631 0.021733
+v -0.138582 0.057403 0.022500
+v -0.133202 0.055174 0.021733
+v -0.128188 0.053097 0.019486
+v -0.123883 0.051314 0.015910
+v -0.120580 0.049946 0.011250
+v -0.118503 0.049086 0.005823
+v -0.117795 0.048792 0.000000
+v -0.118503 0.049086 -0.005823
+v -0.120580 0.049946 -0.011250
+v -0.123883 0.051314 -0.015910
+v -0.128188 0.053097 -0.019486
+v -0.133202 0.055174 -0.021733
+v -0.138582 0.057403 -0.022500
+v -0.143962 0.059631 -0.021733
+v -0.148976 0.061708 -0.019486
+v -0.153281 0.063491 -0.015910
+v -0.156584 0.064859 -0.011250
+v -0.158661 0.065720 -0.005823
+v -0.163345 0.055448 0.000000
+v -0.162620 0.055202 0.005823
+v -0.160491 0.054479 0.011250
+v -0.157105 0.053330 0.015910
+v -0.152693 0.051832 0.019486
+v -0.147554 0.050088 0.021733
+v -0.142040 0.048216 0.022500
+v -0.136525 0.046344 0.021733
+v -0.131387 0.044600 0.019486
+v -0.126974 0.043102 0.015910
+v -0.123588 0.041952 0.011250
+v -0.121460 0.041230 0.005823
+v -0.120734 0.040984 0.000000
+v -0.121460 0.041230 -0.005823
+v -0.123588 0.041952 -0.011250
+v -0.126974 0.043102 -0.015910
+v -0.131387 0.044600 -0.019486
+v -0.136525 0.046344 -0.021733
+v -0.142040 0.048216 -0.022500
+v -0.147554 0.050088 -0.021733
+v -0.152693 0.051832 -0.019486
+v -0.157105 0.053330 -0.015910
+v -0.160491 0.054479 -0.011250
+v -0.162620 0.055202 -0.005823
+v -0.166622 0.044646 0.000000
+v -0.165882 0.044448 0.005823
+v -0.163711 0.043866 0.011250
+v -0.160257 0.042941 0.015910
+v -0.155756 0.041735 0.019486
+v -0.150514 0.040330 0.021733
+v -0.144889 0.038823 0.022500
+v -0.139264 0.037316 0.021733
+v -0.134022 0.035911 0.019486
+v -0.129521 0.034705 0.015910
+v -0.126067 0.033780 0.011250
+v -0.123896 0.033198 0.005823
+v -0.123156 0.032999 0.000000
+v -0.123896 0.033198 -0.005823
+v -0.126067 0.033780 -0.011250
+v -0.129521 0.034705 -0.015910
+v -0.134022 0.035911 -0.019486
+v -0.139264 0.037316 -0.021733
+v -0.144889 0.038823 -0.022500
+v -0.150514 0.040330 -0.021733
+v -0.155756 0.041735 -0.019486
+v -0.160257 0.042941 -0.015910
+v -0.163711 0.043866 -0.011250
+v -0.165882 0.044448 -0.005823
+v -0.169185 0.033653 0.000000
+v -0.168434 0.033504 0.005823
+v -0.166229 0.033065 0.011250
+v -0.162722 0.032367 0.015910
+v -0.158152 0.031458 0.019486
+v -0.152829 0.030400 0.021733
+v -0.147118 0.029264 0.022500
+v -0.141406 0.028127 0.021733
+v -0.136084 0.027069 0.019486
+v -0.131514 0.026160 0.015910
+v -0.128007 0.025462 0.011250
+v -0.125802 0.025024 0.005823
+v -0.125050 0.024874 0.000000
+v -0.125802 0.025024 -0.005823
+v -0.128007 0.025462 -0.011250
+v -0.131514 0.026160 -0.015910
+v -0.136084 0.027069 -0.019486
+v -0.141406 0.028127 -0.021733
+v -0.147118 0.029264 -0.022500
+v -0.152829 0.030400 -0.021733
+v -0.158152 0.031458 -0.019486
+v -0.162722 0.032367 -0.015910
+v -0.166229 0.033065 -0.011250
+v -0.168434 0.033504 -0.005823
+v -0.171024 0.022516 0.000000
+v -0.170264 0.022416 0.005823
+v -0.168036 0.022122 0.011250
+v -0.164491 0.021656 0.015910
+v -0.159871 0.021047 0.019486
+v -0.154490 0.020339 0.021733
+v -0.148717 0.019579 0.022500
+v -0.142943 0.018819 0.021733
+v -0.137563 0.018111 0.019486
+v -0.132943 0.017502 0.015910
+v -0.129398 0.017036 0.011250
+v -0.127169 0.016742 0.005823
+v -0.126409 0.016642 0.000000
+v -0.127169 0.016742 -0.005823
+v -0.129398 0.017036 -0.011250
+v -0.132943 0.017502 -0.015910
+v -0.137563 0.018111 -0.019486
+v -0.142943 0.018819 -0.021733
+v -0.148717 0.019579 -0.022500
+v -0.154490 0.020339 -0.021733
+v -0.159871 0.021047 -0.019486
+v -0.164491 0.021656 -0.015910
+v -0.168036 0.022122 -0.011250
+v -0.170264 0.022416 -0.005823
+v -0.172131 0.011282 0.000000
+v -0.171366 0.011232 0.005823
+v -0.169123 0.011085 0.011250
+v -0.165555 0.010851 0.015910
+v -0.160905 0.010546 0.019486
+v -0.155490 0.010191 0.021733
+v -0.149679 0.009811 0.022500
+v -0.143868 0.009430 0.021733
+v -0.138453 0.009075 0.019486
+v -0.133803 0.008770 0.015910
+v -0.130235 0.008536 0.011250
+v -0.127992 0.008389 0.005823
+v -0.127227 0.008339 0.000000
+v -0.127992 0.008389 -0.005823
+v -0.130235 0.008536 -0.011250
+v -0.133803 0.008770 -0.015910
+v -0.138453 0.009075 -0.019486
+v -0.143868 0.009430 -0.021733
+v -0.149679 0.009811 -0.022500
+v -0.155490 0.010191 -0.021733
+v -0.160905 0.010546 -0.019486
+v -0.165555 0.010851 -0.015910
+v -0.169123 0.011085 -0.011250
+v -0.171366 0.011232 -0.005823
+v -0.172500 0.000000 0.000000
+v -0.171733 0.000000 0.005823
+v -0.169486 0.000000 0.011250
+v -0.165910 0.000000 0.015910
+v -0.161250 0.000000 0.019486
+v -0.155823 0.000000 0.021733
+v -0.150000 0.000000 0.022500
+v -0.144177 0.000000 0.021733
+v -0.138750 0.000000 0.019486
+v -0.134090 0.000000 0.015910
+v -0.130514 0.000000 0.011250
+v -0.128267 0.000000 0.005823
+v -0.127500 0.000000 0.000000
+v -0.128267 0.000000 -0.005823
+v -0.130514 0.000000 -0.011250
+v -0.134090 0.000000 -0.015910
+v -0.138750 0.000000 -0.019486
+v -0.144177 0.000000 -0.021733
+v -0.150000 0.000000 -0.022500
+v -0.155823 0.000000 -0.021733
+v -0.161250 0.000000 -0.019486
+v -0.165910 0.000000 -0.015910
+v -0.169486 0.000000 -0.011250
+v -0.171733 0.000000 -0.005823
+v -0.172131 -0.011282 -0.000000
+v -0.171366 -0.011232 0.005823
+v -0.169123 -0.011085 0.011250
+v -0.165555 -0.010851 0.015910
+v -0.160905 -0.010546 0.019486
+v -0.155490 -0.010191 0.021733
+v -0.149679 -0.009810 0.022500
+v -0.143868 -0.009430 0.021733
+v -0.138453 -0.009075 0.019486
+v -0.133803 -0.008770 0.015910
+v -0.130235 -0.008536 0.011250
+v -0.127992 -0.008389 0.005823
+v -0.127227 -0.008339 -0.000000
+v -0.127992 -0.008389 -0.005823
+v -0.130235 -0.008536 -0.011250
+v -0.133803 -0.008770 -0.015910
+v -0.138453 -0.009075 -0.019486
+v -0.143868 -0.009430 -0.021733
+v -0.149679 -0.009810 -0.022500
+v -0.155490 -0.010191 -0.021733
+v -0.160905 -0.010546 -0.019486
+v -0.165555 -0.010851 -0.015910
+v -0.169123 -0.011085 -0.011250
+v -0.171366 -0.011232 -0.005823
+v -0.171024 -0.022516 -0.000000
+v -0.170264 -0.022416 0.005823
+v -0.168036 -0.022122 0.011250
+v -0.164491 -0.021656 0.015910
+v -0.159871 -0.021047 0.019486
+v -0.154490 -0.020339 0.021733
+v -0.148717 -0.019579 0.022500
+v -0.142943 -0.018819 0.021733
+v -0.137563 -0.018111 0.019486
+v -0.132943 -0.017502 0.015910
+v -0.129398 -0.017036 0.011250
+v -0.127169 -0.016742 0.005823
+v -0.126409 -0.016642 -0.000000
+v -0.127169 -0.016742 -0.005823
+v -0.129398 -0.017036 -0.011250
+v -0.132943 -0.017502 -0.015910
+v -0.137563 -0.018111 -0.019486
+v -0.142943 -0.018819 -0.021733
+v -0.148717 -0.019579 -0.022500
+v -0.154490 -0.020339 -0.021733
+v -0.159871 -0.021047 -0.019486
+v -0.164491 -0.021656 -0.015910
+v -0.168036 -0.022122 -0.011250
+v -0.170264 -0.022416 -0.005823
+v -0.169185 -0.033653 -0.000000
+v -0.168434 -0.033504 0.005823
+v -0.166229 -0.033065 0.011250
+v -0.162722 -0.032367 0.015910
+v -0.158152 -0.031458 0.019486
+v -0.152829 -0.030400 0.021733
+v -0.147118 -0.029264 0.022500
+v -0.141406 -0.028127 0.021733
+v -0.136084 -0.027069 0.019486
+v -0.131514 -0.026160 0.015910
+v -0.128007 -0.025462 0.011250
+v -0.125802 -0.025024 0.005823
+v -0.125050 -0.024874 -0.000000
+v -0.125802 -0.025024 -0.005823
+v -0.128007 -0.025462 -0.011250
+v -0.131514 -0.026160 -0.015910
+v -0.136084 -0.027069 -0.019486
+v -0.141406 -0.028127 -0.021733
+v -0.147118 -0.029264 -0.022500
+v -0.152829 -0.030400 -0.021733
+v -0.158152 -0.031458 -0.019486
+v -0.162722 -0.032367 -0.015910
+v -0.166229 -0.033065 -0.011250
+v -0.168434 -0.033504 -0.005823
+v -0.166622 -0.044646 -0.000000
+v -0.165882 -0.044448 0.005823
+v -0.163711 -0.043866 0.011250
+v -0.160257 -0.042941 0.015910
+v -0.155756 -0.041735 0.019486
+v -0.150514 -0.040330 0.021733
+v -0.144889 -0.038823 0.022500
+v -0.139264 -0.037316 0.021733
+v -0.134022 -0.035911 0.019486
+v -0.129521 -0.034705 0.015910
+v -0.126067 -0.033780 0.011250
+v -0.123896 -0.033198 0.005823
+v -0.123156 -0.032999 -0.000000
+v -0.123896 -0.033198 -0.005823
+v -0.126067 -0.033780 -0.011250
+v -0.129521 -0.034705 -0.015910
+v -0.134022 -0.035911 -0.019486
+v -0.139264 -0.037316 -0.021733
+v -0.144889 -0.038823 -0.022500
+v -0.150514 -0.040330 -0.021733
+v -0.155756 -0.041735 -0.019486
+v -0.160257 -0.042941 -0.015910
+v -0.163711 -0.043866 -0.011250
+v -0.165882 -0.044448 -0.005823
+v -0.163345 -0.055448 -0.000000
+v -0.162620 -0.055202 0.005823
+v -0.160491 -0.054479 0.011250
+v -0.157105 -0.053330 0.015910
+v -0.152693 -0.051832 0.019486
+v -0.147554 -0.050088 0.021733
+v -0.142040 -0.048216 0.022500
+v -0.136525 -0.046344 0.021733
+v -0.131387 -0.044600 0.019486
+v -0.126974 -0.043102 0.015910
+v -0.123588 -0.041952 0.011250
+v -0.121460 -0.041230 0.005823
+v -0.120734 -0.040984 -0.000000
+v -0.121460 -0.041230 -0.005823
+v -0.123588 -0.041952 -0.011250
+v -0.126974 -0.043102 -0.015910
+v -0.131387 -0.044600 -0.019486
+v -0.136525 -0.046344 -0.021733
+v -0.142040 -0.048216 -0.022500
+v -0.147554 -0.050088 -0.021733
+v -0.152693 -0.051832 -0.019486
+v -0.157105 -0.053330 -0.015910
+v -0.160491 -0.054479 -0.011250
+v -0.162620 -0.055202 -0.005823
+v -0.159369 -0.066013 -0.000000
+v -0.158661 -0.065719 0.005823
+v -0.156584 -0.064859 0.011250
+v -0.153281 -0.063491 0.015910
+v -0.148976 -0.061708 0.019486
+v -0.143962 -0.059631 0.021733
+v -0.138582 -0.057402 0.022500
+v -0.133202 -0.055174 0.021733
+v -0.128188 -0.053097 0.019486
+v -0.123883 -0.051314 0.015910
+v -0.120580 -0.049946 0.011250
+v -0.118503 -0.049085 0.005823
+v -0.117795 -0.048792 -0.000000
+v -0.118503 -0.049085 -0.005823
+v -0.120580 -0.049946 -0.011250
+v -0.123883 -0.051314 -0.015910
+v -0.128188 -0.053097 -0.019486
+v -0.133202 -0.055174 -0.021733
+v -0.138582 -0.057402 -0.022500
+v -0.143962 -0.059631 -0.021733
+v -0.148976 -0.061708 -0.019486
+v -0.153281 -0.063491 -0.015910
+v -0.156584 -0.064859 -0.011250
+v -0.158661 -0.065719 -0.005823
+v -0.154711 -0.076295 -0.000000
+v -0.154023 -0.075956 0.005823
+v -0.152007 -0.074962 0.011250
+v -0.148800 -0.073380 0.015910
+v -0.144621 -0.071319 0.019486
+v -0.139754 -0.068919 0.021733
+v -0.134531 -0.066343 0.022500
+v -0.129308 -0.063768 0.021733
+v -0.124441 -0.061368 0.019486
+v -0.120262 -0.059307 0.015910
+v -0.117055 -0.057725 0.011250
+v -0.115039 -0.056731 0.005823
+v -0.114351 -0.056392 -0.000000
+v -0.115039 -0.056731 -0.005823
+v -0.117055 -0.057725 -0.011250
+v -0.120262 -0.059307 -0.015910
+v -0.124441 -0.061368 -0.019486
+v -0.129308 -0.063768 -0.021733
+v -0.134531 -0.066343 -0.022500
+v -0.139754 -0.068919 -0.021733
+v -0.144621 -0.071319 -0.019486
+v -0.148800 -0.073380 -0.015910
+v -0.152007 -0.074962 -0.011250
+v -0.154023 -0.075956 -0.005823
+v -0.149389 -0.086250 -0.000000
+v -0.148725 -0.085867 0.005823
+v -0.146779 -0.084743 0.011250
+v -0.143682 -0.082955 0.015910
+v -0.139647 -0.080625 0.019486
+v -0.134947 -0.077912 0.021733
+v -0.129904 -0.075000 0.022500
+v -0.124861 -0.072088 0.021733
+v -0.120161 -0.069375 0.019486
+v -0.116125 -0.067045 0.015910
+v -0.113029 -0.065257 0.011250
+v -0.111082 -0.064133 0.005823
+v -0.110418 -0.063750 -0.000000
+v -0.111082 -0.064133 -0.005823
+v -0.113029 -0.065257 -0.011250
+v -0.116125 -0.067045 -0.015910
+v -0.120161 -0.069375 -0.019486
+v -0.124861 -0.072088 -0.021733
+v -0.129904 -0.075000 -0.022500
+v -0.134947 -0.077912 -0.021733
+v -0.139647 -0.080625 -0.019486
+v -0.143682 -0.082955 -0.015910
+v -0.146779 -0.084743 -0.011250
+v -0.148725 -0.085867 -0.005823
+v -0.143428 -0.095836 -0.000000
+v -0.142791 -0.095410 0.005823
+v -0.140922 -0.094161 0.011250
+v -0.137949 -0.092175 0.015910
+v -0.134074 -0.089586 0.019486
+v -0.129562 -0.086571 0.021733
+v -0.124720 -0.083336 0.022500
+v -0.119878 -0.080100 0.021733
+v -0.115366 -0.077085 0.019486
+v -0.111492 -0.074496 0.015910
+v -0.108519 -0.072510 0.011250
+v -0.106650 -0.071261 0.005823
+v -0.106012 -0.070835 -0.000000
+v -0.106650 -0.071261 -0.005823
+v -0.108519 -0.072510 -0.011250
+v -0.111492 -0.074496 -0.015910
+v -0.115366 -0.077085 -0.019486
+v -0.119878 -0.080100 -0.021733
+v -0.124720 -0.083336 -0.022500
+v -0.129562 -0.086571 -0.021733
+v -0.134074 -0.089586 -0.019486
+v -0.137949 -0.092175 -0.015910
+v -0.140922 -0.094161 -0.011250
+v -0.142791 -0.095410 -0.005823
+v -0.136853 -0.105011 -0.000000
+v -0.136245 -0.104545 0.005823
+v -0.134462 -0.103176 0.011250
+v -0.131625 -0.101000 0.015910
+v -0.127928 -0.098163 0.019486
+v -0.123623 -0.094859 0.021733
+v -0.119003 -0.091314 0.022500
+v -0.114383 -0.087769 0.021733
+v -0.110078 -0.084466 0.019486
+v -0.106381 -0.081629 0.015910
+v -0.103544 -0.079452 0.011250
+v -0.101761 -0.078084 0.005823
+v -0.101153 -0.077617 -0.000000
+v -0.101761 -0.078084 -0.005823
+v -0.103544 -0.079452 -0.011250
+v -0.106381 -0.081629 -0.015910
+v -0.110078 -0.084466 -0.019486
+v -0.114383 -0.087769 -0.021733
+v -0.119003 -0.091314 -0.022500
+v -0.123623 -0.094859 -0.021733
+v -0.127928 -0.098163 -0.019486
+v -0.131625 -0.101000 -0.015910
+v -0.134462 -0.103176 -0.011250
+v -0.136245 -0.104545 -0.005823
+v -0.129692 -0.113737 -0.000000
+v -0.129116 -0.113232 0.005823
+v -0.127426 -0.111750 0.011250
+v -0.124738 -0.109392 0.015910
+v -0.121234 -0.106320 0.019486
+v -0.117154 -0.102742 0.021733
+v -0.112776 -0.098902 0.022500
+v -0.108398 -0.095062 0.021733
+v -0.104318 -0.091484 0.019486
+v -0.100814 -0.088412 0.015910
+v -0.098126 -0.086054 0.011250
+v -0.096436 -0.084572 0.005823
+v -0.095860 -0.084067 -0.000000
+v -0.096436 -0.084572 -0.005823
+v -0.098126 -0.086054 -0.011250
+v -0.100814 -0.088412 -0.015910
+v -0.104318 -0.091484 -0.019486
+v -0.108398 -0.095062 -0.021733
+v -0.112776 -0.098902 -0.022500
+v -0.117154 -0.102742 -0.021733
+v -0.121234 -0.106320 -0.019486
+v -0.124738 -0.109392 -0.015910
+v -0.127426 -0.111750 -0.011250
+v -0.129116 -0.113232 -0.005823
+v -0.121976 -0.121976 -0.000000
+v -0.121434 -0.121434 0.005823
+v -0.119844 -0.119844 0.011250
+v -0.117316 -0.117316 0.015910
+v -0.114021 -0.114021 0.019486
+v -0.110184 -0.110184 0.021733
+v -0.106066 -0.106066 0.022500
+v -0.101948 -0.101948 0.021733
+v -0.098111 -0.098111 0.019486
+v -0.094816 -0.094816 0.015910
+v -0.092288 -0.092288 0.011250
+v -0.090698 -0.090698 0.005823
+v -0.090156 -0.090156 -0.000000
+v -0.090698 -0.090698 -0.005823
+v -0.092288 -0.092288 -0.011250
+v -0.094816 -0.094816 -0.015910
+v -0.098111 -0.098111 -0.019486
+v -0.101948 -0.101948 -0.021733
+v -0.106066 -0.106066 -0.022500
+v -0.110184 -0.110184 -0.021733
+v -0.114021 -0.114021 -0.019486
+v -0.117316 -0.117316 -0.015910
+v -0.119844 -0.119844 -0.011250
+v -0.121434 -0.121434 -0.005823
+v -0.113737 -0.129692 -0.000000
+v -0.113232 -0.129116 0.005823
+v -0.111750 -0.127426 0.011250
+v -0.109392 -0.124738 0.015910
+v -0.106320 -0.121234 0.019486
+v -0.102742 -0.117154 0.021733
+v -0.098902 -0.112776 0.022500
+v -0.095062 -0.108398 0.021733
+v -0.091484 -0.104318 0.019486
+v -0.088412 -0.100814 0.015910
+v -0.086054 -0.098126 0.011250
+v -0.084572 -0.096436 0.005823
+v -0.084067 -0.095860 -0.000000
+v -0.084572 -0.096436 -0.005823
+v -0.086054 -0.098126 -0.011250
+v -0.088412 -0.100814 -0.015910
+v -0.091484 -0.104318 -0.019486
+v -0.095062 -0.108398 -0.021733
+v -0.098902 -0.112776 -0.022500
+v -0.102742 -0.117154 -0.021733
+v -0.106320 -0.121234 -0.019486
+v -0.109392 -0.124738 -0.015910
+v -0.111750 -0.127426 -0.011250
+v -0.113232 -0.129116 -0.005823
+v -0.105011 -0.136853 -0.000000
+v -0.104545 -0.136245 0.005823
+v -0.103176 -0.134462 0.011250
+v -0.101000 -0.131625 0.015910
+v -0.098163 -0.127928 0.019486
+v -0.094859 -0.123623 0.021733
+v -0.091314 -0.119003 0.022500
+v -0.087769 -0.114383 0.021733
+v -0.084466 -0.110078 0.019486
+v -0.081629 -0.106381 0.015910
+v -0.079452 -0.103544 0.011250
+v -0.078084 -0.101761 0.005823
+v -0.077617 -0.101153 -0.000000
+v -0.078084 -0.101761 -0.005823
+v -0.079452 -0.103544 -0.011250
+v -0.081629 -0.106381 -0.015910
+v -0.084466 -0.110078 -0.019486
+v -0.087769 -0.114383 -0.021733
+v -0.091314 -0.119003 -0.022500
+v -0.094859 -0.123623 -0.021733
+v -0.098163 -0.127928 -0.019486
+v -0.101000 -0.131625 -0.015910
+v -0.103176 -0.134462 -0.011250
+v -0.104545 -0.136245 -0.005823
+v -0.095836 -0.143428 -0.000000
+v -0.095410 -0.142791 0.005823
+v -0.094161 -0.140922 0.011250
+v -0.092175 -0.137949 0.015910
+v -0.089586 -0.134074 0.019486
+v -0.086571 -0.129562 0.021733
+v -0.083336 -0.124720 0.022500
+v -0.080100 -0.119878 0.021733
+v -0.077085 -0.115366 0.019486
+v -0.074497 -0.111492 0.015910
+v -0.072510 -0.108519 0.011250
+v -0.071261 -0.106650 0.005823
+v -0.070835 -0.106012 -0.000000
+v -0.071261 -0.106650 -0.005823
+v -0.072510 -0.108519 -0.011250
+v -0.074497 -0.111492 -0.015910
+v -0.077085 -0.115366 -0.019486
+v -0.080100 -0.119878 -0.021733
+v -0.083336 -0.124720 -0.022500
+v -0.086571 -0.129562 -0.021733
+v -0.089586 -0.134074 -0.019486
+v -0.092175 -0.137949 -0.015910
+v -0.094161 -0.140922 -0.011250
+v -0.095410 -0.142791 -0.005823
+v -0.086250 -0.149389 -0.000000
+v -0.085867 -0.148725 0.005823
+v -0.084743 -0.146779 0.011250
+v -0.082955 -0.143682 0.015910
+v -0.080625 -0.139647 0.019486
+v -0.077912 -0.134947 0.021733
+v -0.075000 -0.129904 0.022500
+v -0.072088 -0.124861 0.021733
+v -0.069375 -0.120161 0.019486
+v -0.067045 -0.116125 0.015910
+v -0.065257 -0.113029 0.011250
+v -0.064133 -0.111082 0.005823
+v -0.063750 -0.110418 -0.000000
+v -0.064133 -0.111082 -0.005823
+v -0.065257 -0.113029 -0.011250
+v -0.067045 -0.116125 -0.015910
+v -0.069375 -0.120161 -0.019486
+v -0.072088 -0.124861 -0.021733
+v -0.075000 -0.129904 -0.022500
+v -0.077912 -0.134947 -0.021733
+v -0.080625 -0.139647 -0.019486
+v -0.082955 -0.143682 -0.015910
+v -0.084743 -0.146779 -0.011250
+v -0.085867 -0.148725 -0.005823
+v -0.076295 -0.154711 -0.000000
+v -0.075956 -0.154023 0.005823
+v -0.074962 -0.152007 0.011250
+v -0.073380 -0.148800 0.015910
+v -0.071319 -0.144621 0.019486
+v -0.068919 -0.139754 0.021733
+v -0.066343 -0.134531 0.022500
+v -0.063768 -0.129308 0.021733
+v -0.061368 -0.124441 0.019486
+v -0.059307 -0.120262 0.015910
+v -0.057725 -0.117055 0.011250
+v -0.056731 -0.115039 0.005823
+v -0.056392 -0.114351 -0.000000
+v -0.056731 -0.115039 -0.005823
+v -0.057725 -0.117055 -0.011250
+v -0.059307 -0.120262 -0.015910
+v -0.061368 -0.124441 -0.019486
+v -0.063768 -0.129308 -0.021733
+v -0.066343 -0.134531 -0.022500
+v -0.068919 -0.139754 -0.021733
+v -0.071319 -0.144621 -0.019486
+v -0.073380 -0.148800 -0.015910
+v -0.074962 -0.152007 -0.011250
+v -0.075956 -0.154023 -0.005823
+v -0.066013 -0.159369 -0.000000
+v -0.065720 -0.158661 0.005823
+v -0.064859 -0.156584 0.011250
+v -0.063491 -0.153281 0.015910
+v -0.061708 -0.148976 0.019486
+v -0.059631 -0.143962 0.021733
+v -0.057403 -0.138582 0.022500
+v -0.055174 -0.133202 0.021733
+v -0.053097 -0.128188 0.019486
+v -0.051314 -0.123883 0.015910
+v -0.049946 -0.120580 0.011250
+v -0.049086 -0.118503 0.005823
+v -0.048792 -0.117795 -0.000000
+v -0.049086 -0.118503 -0.005823
+v -0.049946 -0.120580 -0.011250
+v -0.051314 -0.123883 -0.015910
+v -0.053097 -0.128188 -0.019486
+v -0.055174 -0.133202 -0.021733
+v -0.057403 -0.138582 -0.022500
+v -0.059631 -0.143962 -0.021733
+v -0.061708 -0.148976 -0.019486
+v -0.063491 -0.153281 -0.015910
+v -0.064859 -0.156584 -0.011250
+v -0.065720 -0.158661 -0.005823
+v -0.055448 -0.163345 -0.000000
+v -0.055202 -0.162619 0.005823
+v -0.054479 -0.160491 0.011250
+v -0.053330 -0.157105 0.015910
+v -0.051832 -0.152692 0.019486
+v -0.050088 -0.147554 0.021733
+v -0.048216 -0.142040 0.022500
+v -0.046344 -0.136525 0.021733
+v -0.044600 -0.131387 0.019486
+v -0.043102 -0.126974 0.015910
+v -0.041953 -0.123588 0.011250
+v -0.041230 -0.121460 0.005823
+v -0.040984 -0.120734 -0.000000
+v -0.041230 -0.121460 -0.005823
+v -0.041953 -0.123588 -0.011250
+v -0.043102 -0.126974 -0.015910
+v -0.044600 -0.131387 -0.019486
+v -0.046344 -0.136525 -0.021733
+v -0.048216 -0.142040 -0.022500
+v -0.050088 -0.147554 -0.021733
+v -0.051832 -0.152692 -0.019486
+v -0.053330 -0.157105 -0.015910
+v -0.054479 -0.160491 -0.011250
+v -0.055202 -0.162619 -0.005823
+v -0.044646 -0.166622 -0.000000
+v -0.044448 -0.165882 0.005823
+v -0.043866 -0.163710 0.011250
+v -0.042941 -0.160257 0.015910
+v -0.041735 -0.155756 0.019486
+v -0.040330 -0.150514 0.021733
+v -0.038823 -0.144889 0.022500
+v -0.037316 -0.139264 0.021733
+v -0.035911 -0.134022 0.019486
+v -0.034705 -0.129521 0.015910
+v -0.033780 -0.126067 0.011250
+v -0.033198 -0.123896 0.005823
+v -0.032999 -0.123156 -0.000000
+v -0.033198 -0.123896 -0.005823
+v -0.033780 -0.126067 -0.011250
+v -0.034705 -0.129521 -0.015910
+v -0.035911 -0.134022 -0.019486
+v -0.037316 -0.139264 -0.021733
+v -0.038823 -0.144889 -0.022500
+v -0.040330 -0.150514 -0.021733
+v -0.041735 -0.155756 -0.019486
+v -0.042941 -0.160257 -0.015910
+v -0.043866 -0.163710 -0.011250
+v -0.044448 -0.165882 -0.005823
+v -0.033653 -0.169185 -0.000000
+v -0.033504 -0.168434 0.005823
+v -0.033065 -0.166229 0.011250
+v -0.032367 -0.162722 0.015910
+v -0.031458 -0.158152 0.019486
+v -0.030400 -0.152829 0.021733
+v -0.029264 -0.147118 0.022500
+v -0.028128 -0.141406 0.021733
+v -0.027069 -0.136084 0.019486
+v -0.026160 -0.131514 0.015910
+v -0.025462 -0.128007 0.011250
+v -0.025024 -0.125802 0.005823
+v -0.024874 -0.125050 -0.000000
+v -0.025024 -0.125802 -0.005823
+v -0.025462 -0.128007 -0.011250
+v -0.026160 -0.131514 -0.015910
+v -0.027069 -0.136084 -0.019486
+v -0.028128 -0.141406 -0.021733
+v -0.029264 -0.147118 -0.022500
+v -0.030400 -0.152829 -0.021733
+v -0.031458 -0.158152 -0.019486
+v -0.032367 -0.162722 -0.015910
+v -0.033065 -0.166229 -0.011250
+v -0.033504 -0.168434 -0.005823
+v -0.022516 -0.171024 -0.000000
+v -0.022416 -0.170264 0.005823
+v -0.022122 -0.168036 0.011250
+v -0.021656 -0.164490 0.015910
+v -0.021047 -0.159870 0.019486
+v -0.020339 -0.154490 0.021733
+v -0.019579 -0.148717 0.022500
+v -0.018819 -0.142943 0.021733
+v -0.018111 -0.137563 0.019486
+v -0.017502 -0.132943 0.015910
+v -0.017036 -0.129398 0.011250
+v -0.016742 -0.127169 0.005823
+v -0.016642 -0.126409 -0.000000
+v -0.016742 -0.127169 -0.005823
+v -0.017036 -0.129398 -0.011250
+v -0.017502 -0.132943 -0.015910
+v -0.018111 -0.137563 -0.019486
+v -0.018819 -0.142943 -0.021733
+v -0.019579 -0.148717 -0.022500
+v -0.020339 -0.154490 -0.021733
+v -0.021047 -0.159870 -0.019486
+v -0.021656 -0.164490 -0.015910
+v -0.022122 -0.168036 -0.011250
+v -0.022416 -0.170264 -0.005823
+v -0.011282 -0.172131 -0.000000
+v -0.011232 -0.171366 0.005823
+v -0.011085 -0.169123 0.011250
+v -0.010851 -0.165555 0.015910
+v -0.010546 -0.160905 0.019486
+v -0.010191 -0.155490 0.021733
+v -0.009810 -0.149679 0.022500
+v -0.009430 -0.143868 0.021733
+v -0.009075 -0.138453 0.019486
+v -0.008770 -0.133803 0.015910
+v -0.008536 -0.130235 0.011250
+v -0.008389 -0.127992 0.005823
+v -0.008339 -0.127227 -0.000000
+v -0.008389 -0.127992 -0.005823
+v -0.008536 -0.130235 -0.011250
+v -0.008770 -0.133803 -0.015910
+v -0.009075 -0.138453 -0.019486
+v -0.009430 -0.143868 -0.021733
+v -0.009810 -0.149679 -0.022500
+v -0.010191 -0.155490 -0.021733
+v -0.010546 -0.160905 -0.019486
+v -0.010851 -0.165555 -0.015910
+v -0.011085 -0.169123 -0.011250
+v -0.011232 -0.171366 -0.005823
+v 0.000000 -0.172500 -0.000000
+v 0.000000 -0.171733 0.005823
+v 0.000000 -0.169486 0.011250
+v 0.000000 -0.165910 0.015910
+v 0.000000 -0.161250 0.019486
+v 0.000000 -0.155823 0.021733
+v 0.000000 -0.150000 0.022500
+v 0.000000 -0.144177 0.021733
+v 0.000000 -0.138750 0.019486
+v 0.000000 -0.134090 0.015910
+v 0.000000 -0.130514 0.011250
+v 0.000000 -0.128267 0.005823
+v 0.000000 -0.127500 -0.000000
+v 0.000000 -0.128267 -0.005823
+v 0.000000 -0.130514 -0.011250
+v 0.000000 -0.134090 -0.015910
+v 0.000000 -0.138750 -0.019486
+v 0.000000 -0.144177 -0.021733
+v 0.000000 -0.150000 -0.022500
+v 0.000000 -0.155823 -0.021733
+v 0.000000 -0.161250 -0.019486
+v 0.000000 -0.165910 -0.015910
+v 0.000000 -0.169486 -0.011250
+v 0.000000 -0.171733 -0.005823
+v 0.011282 -0.172131 -0.000000
+v 0.011232 -0.171366 0.005823
+v 0.011085 -0.169123 0.011250
+v 0.010851 -0.165555 0.015910
+v 0.010546 -0.160905 0.019486
+v 0.010191 -0.155490 0.021733
+v 0.009810 -0.149679 0.022500
+v 0.009430 -0.143868 0.021733
+v 0.009075 -0.138453 0.019486
+v 0.008770 -0.133803 0.015910
+v 0.008536 -0.130235 0.011250
+v 0.008389 -0.127992 0.005823
+v 0.008339 -0.127227 -0.000000
+v 0.008389 -0.127992 -0.005823
+v 0.008536 -0.130235 -0.011250
+v 0.008770 -0.133803 -0.015910
+v 0.009075 -0.138453 -0.019486
+v 0.009430 -0.143868 -0.021733
+v 0.009810 -0.149679 -0.022500
+v 0.010191 -0.155490 -0.021733
+v 0.010546 -0.160905 -0.019486
+v 0.010851 -0.165555 -0.015910
+v 0.011085 -0.169123 -0.011250
+v 0.011232 -0.171366 -0.005823
+v 0.022516 -0.171024 -0.000000
+v 0.022416 -0.170264 0.005823
+v 0.022122 -0.168036 0.011250
+v 0.021656 -0.164491 0.015910
+v 0.021047 -0.159871 0.019486
+v 0.020339 -0.154490 0.021733
+v 0.019579 -0.148717 0.022500
+v 0.018819 -0.142943 0.021733
+v 0.018110 -0.137563 0.019486
+v 0.017502 -0.132943 0.015910
+v 0.017036 -0.129398 0.011250
+v 0.016742 -0.127169 0.005823
+v 0.016642 -0.126409 -0.000000
+v 0.016742 -0.127169 -0.005823
+v 0.017036 -0.129398 -0.011250
+v 0.017502 -0.132943 -0.015910
+v 0.018110 -0.137563 -0.019486
+v 0.018819 -0.142943 -0.021733
+v 0.019579 -0.148717 -0.022500
+v 0.020339 -0.154490 -0.021733
+v 0.021047 -0.159871 -0.019486
+v 0.021656 -0.164491 -0.015910
+v 0.022122 -0.168036 -0.011250
+v 0.022416 -0.170264 -0.005823
+v 0.033653 -0.169185 -0.000000
+v 0.033503 -0.168434 0.005823
+v 0.033065 -0.166229 0.011250
+v 0.032367 -0.162722 0.015910
+v 0.031458 -0.158152 0.019486
+v 0.030400 -0.152829 0.021733
+v 0.029264 -0.147118 0.022500
+v 0.028127 -0.141406 0.021733
+v 0.027069 -0.136084 0.019486
+v 0.026160 -0.131514 0.015910
+v 0.025462 -0.128007 0.011250
+v 0.025024 -0.125802 0.005823
+v 0.024874 -0.125050 -0.000000
+v 0.025024 -0.125802 -0.005823
+v 0.025462 -0.128007 -0.011250
+v 0.026160 -0.131514 -0.015910
+v 0.027069 -0.136084 -0.019486
+v 0.028127 -0.141406 -0.021733
+v 0.029264 -0.147118 -0.022500
+v 0.030400 -0.152829 -0.021733
+v 0.031458 -0.158152 -0.019486
+v 0.032367 -0.162722 -0.015910
+v 0.033065 -0.166229 -0.011250
+v 0.033503 -0.168434 -0.005823
+v 0.044646 -0.166622 -0.000000
+v 0.044448 -0.165882 0.005823
+v 0.043866 -0.163711 0.011250
+v 0.042941 -0.160257 0.015910
+v 0.041734 -0.155756 0.019486
+v 0.040330 -0.150514 0.021733
+v 0.038823 -0.144889 0.022500
+v 0.037316 -0.139264 0.021733
+v 0.035911 -0.134022 0.019486
+v 0.034705 -0.129521 0.015910
+v 0.033780 -0.126067 0.011250
+v 0.033198 -0.123896 0.005823
+v 0.032999 -0.123156 -0.000000
+v 0.033198 -0.123896 -0.005823
+v 0.033780 -0.126067 -0.011250
+v 0.034705 -0.129521 -0.015910
+v 0.035911 -0.134022 -0.019486
+v 0.037316 -0.139264 -0.021733
+v 0.038823 -0.144889 -0.022500
+v 0.040330 -0.150514 -0.021733
+v 0.041734 -0.155756 -0.019486
+v 0.042941 -0.160257 -0.015910
+v 0.043866 -0.163711 -0.011250
+v 0.044448 -0.165882 -0.005823
+v 0.055448 -0.163345 -0.000000
+v 0.055202 -0.162620 0.005823
+v 0.054479 -0.160491 0.011250
+v 0.053330 -0.157105 0.015910
+v 0.051832 -0.152693 0.019486
+v 0.050088 -0.147554 0.021733
+v 0.048216 -0.142040 0.022500
+v 0.046344 -0.136525 0.021733
+v 0.044600 -0.131387 0.019486
+v 0.043102 -0.126974 0.015910
+v 0.041952 -0.123588 0.011250
+v 0.041230 -0.121460 0.005823
+v 0.040983 -0.120734 -0.000000
+v 0.041230 -0.121460 -0.005823
+v 0.041952 -0.123588 -0.011250
+v 0.043102 -0.126974 -0.015910
+v 0.044600 -0.131387 -0.019486
+v 0.046344 -0.136525 -0.021733
+v 0.048216 -0.142040 -0.022500
+v 0.050088 -0.147554 -0.021733
+v 0.051832 -0.152693 -0.019486
+v 0.053330 -0.157105 -0.015910
+v 0.054479 -0.160491 -0.011250
+v 0.055202 -0.162620 -0.005823
+v 0.066013 -0.159369 -0.000000
+v 0.065720 -0.158661 0.005823
+v 0.064859 -0.156584 0.011250
+v 0.063491 -0.153281 0.015910
+v 0.061708 -0.148976 0.019486
+v 0.059631 -0.143962 0.021733
+v 0.057403 -0.138582 0.022500
+v 0.055174 -0.133202 0.021733
+v 0.053097 -0.128188 0.019486
+v 0.051314 -0.123883 0.015910
+v 0.049946 -0.120580 0.011250
+v 0.049086 -0.118503 0.005823
+v 0.048792 -0.117795 -0.000000
+v 0.049086 -0.118503 -0.005823
+v 0.049946 -0.120580 -0.011250
+v 0.051314 -0.123883 -0.015910
+v 0.053097 -0.128188 -0.019486
+v 0.055174 -0.133202 -0.021733
+v 0.057403 -0.138582 -0.022500
+v 0.059631 -0.143962 -0.021733
+v 0.061708 -0.148976 -0.019486
+v 0.063491 -0.153281 -0.015910
+v 0.064859 -0.156584 -0.011250
+v 0.065720 -0.158661 -0.005823
+v 0.076295 -0.154711 -0.000000
+v 0.075956 -0.154023 0.005823
+v 0.074962 -0.152007 0.011250
+v 0.073380 -0.148800 0.015910
+v 0.071319 -0.144621 0.019486
+v 0.068919 -0.139754 0.021733
+v 0.066343 -0.134531 0.022500
+v 0.063768 -0.129308 0.021733
+v 0.061368 -0.124441 0.019486
+v 0.059307 -0.120262 0.015910
+v 0.057725 -0.117055 0.011250
+v 0.056731 -0.115039 0.005823
+v 0.056392 -0.114351 -0.000000
+v 0.056731 -0.115039 -0.005823
+v 0.057725 -0.117055 -0.011250
+v 0.059307 -0.120262 -0.015910
+v 0.061368 -0.124441 -0.019486
+v 0.063768 -0.129308 -0.021733
+v 0.066343 -0.134531 -0.022500
+v 0.068919 -0.139754 -0.021733
+v 0.071319 -0.144621 -0.019486
+v 0.073380 -0.148800 -0.015910
+v 0.074962 -0.152007 -0.011250
+v 0.075956 -0.154023 -0.005823
+v 0.086250 -0.149389 -0.000000
+v 0.085867 -0.148725 0.005823
+v 0.084743 -0.146779 0.011250
+v 0.082955 -0.143682 0.015910
+v 0.080625 -0.139647 0.019486
+v 0.077912 -0.134947 0.021733
+v 0.075000 -0.129904 0.022500
+v 0.072088 -0.124861 0.021733
+v 0.069375 -0.120161 0.019486
+v 0.067045 -0.116125 0.015910
+v 0.065257 -0.113029 0.011250
+v 0.064133 -0.111082 0.005823
+v 0.063750 -0.110418 -0.000000
+v 0.064133 -0.111082 -0.005823
+v 0.065257 -0.113029 -0.011250
+v 0.067045 -0.116125 -0.015910
+v 0.069375 -0.120161 -0.019486
+v 0.072088 -0.124861 -0.021733
+v 0.075000 -0.129904 -0.022500
+v 0.077912 -0.134947 -0.021733
+v 0.080625 -0.139647 -0.019486
+v 0.082955 -0.143682 -0.015910
+v 0.084743 -0.146779 -0.011250
+v 0.085867 -0.148725 -0.005823
+v 0.095836 -0.143429 -0.000000
+v 0.095410 -0.142791 0.005823
+v 0.094161 -0.140922 0.011250
+v 0.092175 -0.137949 0.015910
+v 0.089586 -0.134074 0.019486
+v 0.086571 -0.129562 0.021733
+v 0.083336 -0.124720 0.022500
+v 0.080100 -0.119878 0.021733
+v 0.077085 -0.115366 0.019486
+v 0.074496 -0.111492 0.015910
+v 0.072510 -0.108519 0.011250
+v 0.071261 -0.106650 0.005823
+v 0.070835 -0.106012 -0.000000
+v 0.071261 -0.106650 -0.005823
+v 0.072510 -0.108519 -0.011250
+v 0.074496 -0.111492 -0.015910
+v 0.077085 -0.115366 -0.019486
+v 0.080100 -0.119878 -0.021733
+v 0.083336 -0.124720 -0.022500
+v 0.086571 -0.129562 -0.021733
+v 0.089586 -0.134074 -0.019486
+v 0.092175 -0.137949 -0.015910
+v 0.094161 -0.140922 -0.011250
+v 0.095410 -0.142791 -0.005823
+v 0.105011 -0.136853 -0.000000
+v 0.104545 -0.136245 0.005823
+v 0.103176 -0.134462 0.011250
+v 0.101000 -0.131625 0.015910
+v 0.098163 -0.127928 0.019486
+v 0.094859 -0.123623 0.021733
+v 0.091314 -0.119003 0.022500
+v 0.087769 -0.114383 0.021733
+v 0.084466 -0.110078 0.019486
+v 0.081629 -0.106381 0.015910
+v 0.079452 -0.103544 0.011250
+v 0.078084 -0.101761 0.005823
+v 0.077617 -0.101153 -0.000000
+v 0.078084 -0.101761 -0.005823
+v 0.079452 -0.103544 -0.011250
+v 0.081629 -0.106381 -0.015910
+v 0.084466 -0.110078 -0.019486
+v 0.087769 -0.114383 -0.021733
+v 0.091314 -0.119003 -0.022500
+v 0.094859 -0.123623 -0.021733
+v 0.098163 -0.127928 -0.019486
+v 0.101000 -0.131625 -0.015910
+v 0.103176 -0.134462 -0.011250
+v 0.104545 -0.136245 -0.005823
+v 0.113737 -0.129692 -0.000000
+v 0.113232 -0.129116 0.005823
+v 0.111750 -0.127426 0.011250
+v 0.109392 -0.124738 0.015910
+v 0.106319 -0.121234 0.019486
+v 0.102741 -0.117154 0.021733
+v 0.098902 -0.112776 0.022500
+v 0.095062 -0.108398 0.021733
+v 0.091484 -0.104318 0.019486
+v 0.088412 -0.100814 0.015910
+v 0.086054 -0.098126 0.011250
+v 0.084572 -0.096436 0.005823
+v 0.084067 -0.095860 -0.000000
+v 0.084572 -0.096436 -0.005823
+v 0.086054 -0.098126 -0.011250
+v 0.088412 -0.100814 -0.015910
+v 0.091484 -0.104318 -0.019486
+v 0.095062 -0.108398 -0.021733
+v 0.098902 -0.112776 -0.022500
+v 0.102741 -0.117154 -0.021733
+v 0.106319 -0.121234 -0.019486
+v 0.109392 -0.124738 -0.015910
+v 0.111750 -0.127426 -0.011250
+v 0.113232 -0.129116 -0.005823
+v 0.121976 -0.121976 -0.000000
+v 0.121434 -0.121434 0.005823
+v 0.119844 -0.119844 0.011250
+v 0.117316 -0.117316 0.015910
+v 0.114021 -0.114021 0.019486
+v 0.110184 -0.110184 0.021733
+v 0.106066 -0.106066 0.022500
+v 0.101948 -0.101948 0.021733
+v 0.098111 -0.098111 0.019486
+v 0.094816 -0.094816 0.015910
+v 0.092288 -0.092288 0.011250
+v 0.090698 -0.090698 0.005823
+v 0.090156 -0.090156 -0.000000
+v 0.090698 -0.090698 -0.005823
+v 0.092288 -0.092288 -0.011250
+v 0.094816 -0.094816 -0.015910
+v 0.098111 -0.098111 -0.019486
+v 0.101948 -0.101948 -0.021733
+v 0.106066 -0.106066 -0.022500
+v 0.110184 -0.110184 -0.021733
+v 0.114021 -0.114021 -0.019486
+v 0.117316 -0.117316 -0.015910
+v 0.119844 -0.119844 -0.011250
+v 0.121434 -0.121434 -0.005823
+v 0.129692 -0.113737 -0.000000
+v 0.129116 -0.113232 0.005823
+v 0.127426 -0.111750 0.011250
+v 0.124738 -0.109392 0.015910
+v 0.121234 -0.106319 0.019486
+v 0.117154 -0.102742 0.021733
+v 0.112776 -0.098902 0.022500
+v 0.108398 -0.095062 0.021733
+v 0.104318 -0.091484 0.019486
+v 0.100814 -0.088412 0.015910
+v 0.098126 -0.086054 0.011250
+v 0.096436 -0.084572 0.005823
+v 0.095860 -0.084067 -0.000000
+v 0.096436 -0.084572 -0.005823
+v 0.098126 -0.086054 -0.011250
+v 0.100814 -0.088412 -0.015910
+v 0.104318 -0.091484 -0.019486
+v 0.108398 -0.095062 -0.021733
+v 0.112776 -0.098902 -0.022500
+v 0.117154 -0.102742 -0.021733
+v 0.121234 -0.106319 -0.019486
+v 0.124738 -0.109392 -0.015910
+v 0.127426 -0.111750 -0.011250
+v 0.129116 -0.113232 -0.005823
+v 0.136853 -0.105011 -0.000000
+v 0.136245 -0.104545 0.005823
+v 0.134462 -0.103176 0.011250
+v 0.131625 -0.101000 0.015910
+v 0.127928 -0.098163 0.019486
+v 0.123623 -0.094859 0.021733
+v 0.119003 -0.091314 0.022500
+v 0.114383 -0.087769 0.021733
+v 0.110078 -0.084466 0.019486
+v 0.106381 -0.081629 0.015910
+v 0.103544 -0.079452 0.011250
+v 0.101761 -0.078084 0.005823
+v 0.101153 -0.077617 -0.000000
+v 0.101761 -0.078084 -0.005823
+v 0.103544 -0.079452 -0.011250
+v 0.106381 -0.081629 -0.015910
+v 0.110078 -0.084466 -0.019486
+v 0.114383 -0.087769 -0.021733
+v 0.119003 -0.091314 -0.022500
+v 0.123623 -0.094859 -0.021733
+v 0.127928 -0.098163 -0.019486
+v 0.131625 -0.101000 -0.015910
+v 0.134462 -0.103176 -0.011250
+v 0.136245 -0.104545 -0.005823
+v 0.143429 -0.095836 -0.000000
+v 0.142791 -0.095410 0.005823
+v 0.140922 -0.094161 0.011250
+v 0.137949 -0.092175 0.015910
+v 0.134074 -0.089586 0.019486
+v 0.129562 -0.086571 0.021733
+v 0.124720 -0.083336 0.022500
+v 0.119878 -0.080100 0.021733
+v 0.115366 -0.077085 0.019486
+v 0.111492 -0.074496 0.015910
+v 0.108519 -0.072510 0.011250
+v 0.106650 -0.071261 0.005823
+v 0.106012 -0.070835 -0.000000
+v 0.106650 -0.071261 -0.005823
+v 0.108519 -0.072510 -0.011250
+v 0.111492 -0.074496 -0.015910
+v 0.115366 -0.077085 -0.019486
+v 0.119878 -0.080100 -0.021733
+v 0.124720 -0.083336 -0.022500
+v 0.129562 -0.086571 -0.021733
+v 0.134074 -0.089586 -0.019486
+v 0.137949 -0.092175 -0.015910
+v 0.140922 -0.094161 -0.011250
+v 0.142791 -0.095410 -0.005823
+v 0.149389 -0.086250 -0.000000
+v 0.148725 -0.085867 0.005823
+v 0.146779 -0.084743 0.011250
+v 0.143682 -0.082955 0.015910
+v 0.139647 -0.080625 0.019486
+v 0.134947 -0.077912 0.021733
+v 0.129904 -0.075000 0.022500
+v 0.124861 -0.072088 0.021733
+v 0.120161 -0.069375 0.019486
+v 0.116125 -0.067045 0.015910
+v 0.113029 -0.065257 0.011250
+v 0.111082 -0.064133 0.005823
+v 0.110418 -0.063750 -0.000000
+v 0.111082 -0.064133 -0.005823
+v 0.113029 -0.065257 -0.011250
+v 0.116125 -0.067045 -0.015910
+v 0.120161 -0.069375 -0.019486
+v 0.124861 -0.072088 -0.021733
+v 0.129904 -0.075000 -0.022500
+v 0.134947 -0.077912 -0.021733
+v 0.139647 -0.080625 -0.019486
+v 0.143682 -0.082955 -0.015910
+v 0.146779 -0.084743 -0.011250
+v 0.148725 -0.085867 -0.005823
+v 0.154711 -0.076295 -0.000000
+v 0.154023 -0.075956 0.005823
+v 0.152007 -0.074962 0.011250
+v 0.148800 -0.073380 0.015910
+v 0.144621 -0.071319 0.019486
+v 0.139754 -0.068919 0.021733
+v 0.134531 -0.066343 0.022500
+v 0.129308 -0.063768 0.021733
+v 0.124441 -0.061368 0.019486
+v 0.120262 -0.059307 0.015910
+v 0.117055 -0.057725 0.011250
+v 0.115039 -0.056731 0.005823
+v 0.114351 -0.056392 -0.000000
+v 0.115039 -0.056731 -0.005823
+v 0.117055 -0.057725 -0.011250
+v 0.120262 -0.059307 -0.015910
+v 0.124441 -0.061368 -0.019486
+v 0.129308 -0.063768 -0.021733
+v 0.134531 -0.066343 -0.022500
+v 0.139754 -0.068919 -0.021733
+v 0.144621 -0.071319 -0.019486
+v 0.148800 -0.073380 -0.015910
+v 0.152007 -0.074962 -0.011250
+v 0.154023 -0.075956 -0.005823
+v 0.159369 -0.066013 -0.000000
+v 0.158661 -0.065720 0.005823
+v 0.156584 -0.064859 0.011250
+v 0.153281 -0.063491 0.015910
+v 0.148976 -0.061708 0.019486
+v 0.143962 -0.059631 0.021733
+v 0.138582 -0.057403 0.022500
+v 0.133202 -0.055174 0.021733
+v 0.128188 -0.053097 0.019486
+v 0.123883 -0.051314 0.015910
+v 0.120580 -0.049946 0.011250
+v 0.118503 -0.049086 0.005823
+v 0.117795 -0.048792 -0.000000
+v 0.118503 -0.049086 -0.005823
+v 0.120580 -0.049946 -0.011250
+v 0.123883 -0.051314 -0.015910
+v 0.128188 -0.053097 -0.019486
+v 0.133202 -0.055174 -0.021733
+v 0.138582 -0.057403 -0.022500
+v 0.143962 -0.059631 -0.021733
+v 0.148976 -0.061708 -0.019486
+v 0.153281 -0.063491 -0.015910
+v 0.156584 -0.064859 -0.011250
+v 0.158661 -0.065720 -0.005823
+v 0.163345 -0.055448 -0.000000
+v 0.162619 -0.055202 0.005823
+v 0.160491 -0.054479 0.011250
+v 0.157105 -0.053330 0.015910
+v 0.152692 -0.051832 0.019486
+v 0.147554 -0.050088 0.021733
+v 0.142040 -0.048216 0.022500
+v 0.136525 -0.046344 0.021733
+v 0.131387 -0.044600 0.019486
+v 0.126974 -0.043102 0.015910
+v 0.123588 -0.041953 0.011250
+v 0.121460 -0.041230 0.005823
+v 0.120734 -0.040984 -0.000000
+v 0.121460 -0.041230 -0.005823
+v 0.123588 -0.041953 -0.011250
+v 0.126974 -0.043102 -0.015910
+v 0.131387 -0.044600 -0.019486
+v 0.136525 -0.046344 -0.021733
+v 0.142040 -0.048216 -0.022500
+v 0.147554 -0.050088 -0.021733
+v 0.152692 -0.051832 -0.019486
+v 0.157105 -0.053330 -0.015910
+v 0.160491 -0.054479 -0.011250
+v 0.162619 -0.055202 -0.005823
+v 0.166622 -0.044646 -0.000000
+v 0.165882 -0.044448 0.005823
+v 0.163711 -0.043866 0.011250
+v 0.160257 -0.042941 0.015910
+v 0.155756 -0.041735 0.019486
+v 0.150514 -0.040330 0.021733
+v 0.144889 -0.038823 0.022500
+v 0.139264 -0.037316 0.021733
+v 0.134022 -0.035911 0.019486
+v 0.129521 -0.034705 0.015910
+v 0.126067 -0.033780 0.011250
+v 0.123896 -0.033198 0.005823
+v 0.123156 -0.032999 -0.000000
+v 0.123896 -0.033198 -0.005823
+v 0.126067 -0.033780 -0.011250
+v 0.129521 -0.034705 -0.015910
+v 0.134022 -0.035911 -0.019486
+v 0.139264 -0.037316 -0.021733
+v 0.144889 -0.038823 -0.022500
+v 0.150514 -0.040330 -0.021733
+v 0.155756 -0.041735 -0.019486
+v 0.160257 -0.042941 -0.015910
+v 0.163711 -0.043866 -0.011250
+v 0.165882 -0.044448 -0.005823
+v 0.169185 -0.033653 -0.000000
+v 0.168434 -0.033503 0.005823
+v 0.166229 -0.033065 0.011250
+v 0.162722 -0.032367 0.015910
+v 0.158152 -0.031458 0.019486
+v 0.152829 -0.030400 0.021733
+v 0.147118 -0.029264 0.022500
+v 0.141406 -0.028127 0.021733
+v 0.136084 -0.027069 0.019486
+v 0.131514 -0.026160 0.015910
+v 0.128007 -0.025462 0.011250
+v 0.125802 -0.025024 0.005823
+v 0.125050 -0.024874 -0.000000
+v 0.125802 -0.025024 -0.005823
+v 0.128007 -0.025462 -0.011250
+v 0.131514 -0.026160 -0.015910
+v 0.136084 -0.027069 -0.019486
+v 0.141406 -0.028127 -0.021733
+v 0.147118 -0.029264 -0.022500
+v 0.152829 -0.030400 -0.021733
+v 0.158152 -0.031458 -0.019486
+v 0.162722 -0.032367 -0.015910
+v 0.166229 -0.033065 -0.011250
+v 0.168434 -0.033503 -0.005823
+v 0.171024 -0.022516 -0.000000
+v 0.170264 -0.022416 0.005823
+v 0.168036 -0.022122 0.011250
+v 0.164491 -0.021656 0.015910
+v 0.159871 -0.021047 0.019486
+v 0.154490 -0.020339 0.021733
+v 0.148717 -0.019579 0.022500
+v 0.142943 -0.018819 0.021733
+v 0.137563 -0.018111 0.019486
+v 0.132943 -0.017502 0.015910
+v 0.129398 -0.017036 0.011250
+v 0.127169 -0.016742 0.005823
+v 0.126409 -0.016642 -0.000000
+v 0.127169 -0.016742 -0.005823
+v 0.129398 -0.017036 -0.011250
+v 0.132943 -0.017502 -0.015910
+v 0.137563 -0.018111 -0.019486
+v 0.142943 -0.018819 -0.021733
+v 0.148717 -0.019579 -0.022500
+v 0.154490 -0.020339 -0.021733
+v 0.159871 -0.021047 -0.019486
+v 0.164491 -0.021656 -0.015910
+v 0.168036 -0.022122 -0.011250
+v 0.170264 -0.022416 -0.005823
+v 0.172131 -0.011282 -0.000000
+v 0.171366 -0.011232 0.005823
+v 0.169123 -0.011085 0.011250
+v 0.165555 -0.010851 0.015910
+v 0.160905 -0.010546 0.019486
+v 0.155490 -0.010191 0.021733
+v 0.149679 -0.009810 0.022500
+v 0.143868 -0.009430 0.021733
+v 0.138453 -0.009075 0.019486
+v 0.133803 -0.008770 0.015910
+v 0.130235 -0.008536 0.011250
+v 0.127992 -0.008389 0.005823
+v 0.127227 -0.008339 -0.000000
+v 0.127992 -0.008389 -0.005823
+v 0.130235 -0.008536 -0.011250
+v 0.133803 -0.008770 -0.015910
+v 0.138453 -0.009075 -0.019486
+v 0.143868 -0.009430 -0.021733
+v 0.149679 -0.009810 -0.022500
+v 0.155490 -0.010191 -0.021733
+v 0.160905 -0.010546 -0.019486
+v 0.165555 -0.010851 -0.015910
+v 0.169123 -0.011085 -0.011250
+v 0.171366 -0.011232 -0.005823
+vn 0.990900 0.032400 0.130500
+vn 0.923500 0.030200 0.382500
+vn 0.793100 0.026000 0.608600
+vn 0.608600 0.019900 0.793200
+vn 0.382700 0.012500 0.923800
+vn 0.130500 0.004300 0.991400
+vn -0.130500 -0.004300 0.991400
+vn -0.382700 -0.012500 0.923800
+vn -0.608600 -0.019900 0.793200
+vn -0.793100 -0.026000 0.608600
+vn -0.923500 -0.030200 0.382500
+vn -0.990900 -0.032400 0.130500
+vn -0.990900 -0.032400 -0.130500
+vn -0.923500 -0.030200 -0.382500
+vn -0.793100 -0.026000 -0.608600
+vn -0.608600 -0.019900 -0.793200
+vn -0.382700 -0.012500 -0.923800
+vn -0.130500 -0.004300 -0.991400
+vn 0.130500 0.004300 -0.991400
+vn 0.382700 0.012500 -0.923800
+vn 0.608600 0.019900 -0.793200
+vn 0.793100 0.026000 -0.608600
+vn 0.923500 0.030200 -0.382500
+vn 0.990900 0.032400 -0.130500
+vn 0.986700 0.097200 0.130500
+vn 0.919500 0.090600 0.382500
+vn 0.789700 0.077800 0.608600
+vn 0.606000 0.059700 0.793200
+vn 0.381000 0.037500 0.923800
+vn 0.130000 0.012800 0.991400
+vn -0.130000 -0.012800 0.991400
+vn -0.381000 -0.037500 0.923800
+vn -0.606000 -0.059700 0.793200
+vn -0.789700 -0.077800 0.608600
+vn -0.919500 -0.090600 0.382500
+vn -0.986700 -0.097200 0.130500
+vn -0.986700 -0.097200 -0.130500
+vn -0.919500 -0.090600 -0.382500
+vn -0.789700 -0.077800 -0.608600
+vn -0.606000 -0.059700 -0.793200
+vn -0.381000 -0.037500 -0.923800
+vn -0.130000 -0.012800 -0.991400
+vn 0.130000 0.012800 -0.991400
+vn 0.381000 0.037500 -0.923800
+vn 0.606000 0.059700 -0.793200
+vn 0.789700 0.077800 -0.608600
+vn 0.919500 0.090600 -0.382500
+vn 0.986700 0.097200 -0.130500
+vn 0.978200 0.161500 0.130500
+vn 0.911600 0.150500 0.382500
+vn 0.782900 0.129300 0.608600
+vn 0.600800 0.099200 0.793200
+vn 0.377700 0.062400 0.923800
+vn 0.128900 0.021300 0.991400
+vn -0.128900 -0.021300 0.991400
+vn -0.377700 -0.062400 0.923800
+vn -0.600800 -0.099200 0.793200
+vn -0.782900 -0.129300 0.608600
+vn -0.911600 -0.150500 0.382500
+vn -0.978200 -0.161500 0.130500
+vn -0.978200 -0.161500 -0.130500
+vn -0.911600 -0.150500 -0.382500
+vn -0.782900 -0.129300 -0.608600
+vn -0.600800 -0.099200 -0.793200
+vn -0.377700 -0.062400 -0.923800
+vn -0.128900 -0.021300 -0.991400
+vn 0.128900 0.021300 -0.991400
+vn 0.377700 0.062400 -0.923800
+vn 0.600800 0.099200 -0.793200
+vn 0.782900 0.129300 -0.608600
+vn 0.911600 0.150500 -0.382500
+vn 0.978200 0.161500 -0.130500
+vn 0.965600 0.225100 0.130500
+vn 0.899800 0.209800 0.382500
+vn 0.772800 0.180200 0.608600
+vn 0.593100 0.138300 0.793200
+vn 0.372900 0.086900 0.923800
+vn 0.127200 0.029700 0.991400
+vn -0.127200 -0.029700 0.991400
+vn -0.372900 -0.086900 0.923800
+vn -0.593100 -0.138300 0.793200
+vn -0.772800 -0.180200 0.608600
+vn -0.899800 -0.209800 0.382500
+vn -0.965600 -0.225100 0.130500
+vn -0.965600 -0.225100 -0.130500
+vn -0.899800 -0.209800 -0.382500
+vn -0.772800 -0.180200 -0.608600
+vn -0.593100 -0.138300 -0.793200
+vn -0.372900 -0.086900 -0.923800
+vn -0.127200 -0.029700 -0.991400
+vn 0.127200 0.029700 -0.991400
+vn 0.372900 0.086900 -0.923800
+vn 0.593100 0.138300 -0.793200
+vn 0.772800 0.180200 -0.608600
+vn 0.899800 0.209800 -0.382500
+vn 0.965600 0.225100 -0.130500
+vn 0.948800 0.287800 0.130500
+vn 0.884200 0.268200 0.382500
+vn 0.759300 0.230300 0.608600
+vn 0.582700 0.176800 0.793200
+vn 0.366400 0.111100 0.923800
+vn 0.125000 0.037900 0.991400
+vn -0.125000 -0.037900 0.991400
+vn -0.366400 -0.111100 0.923800
+vn -0.582700 -0.176800 0.793200
+vn -0.759300 -0.230300 0.608600
+vn -0.884200 -0.268200 0.382500
+vn -0.948800 -0.287800 0.130500
+vn -0.948800 -0.287800 -0.130500
+vn -0.884200 -0.268200 -0.382500
+vn -0.759300 -0.230300 -0.608600
+vn -0.582700 -0.176800 -0.793200
+vn -0.366400 -0.111100 -0.923800
+vn -0.125000 -0.037900 -0.991400
+vn 0.125000 0.037900 -0.991400
+vn 0.366400 0.111100 -0.923800
+vn 0.582700 0.176800 -0.793200
+vn 0.759300 0.230300 -0.608600
+vn 0.884200 0.268200 -0.382500
+vn 0.948800 0.287800 -0.130500
+vn 0.927900 0.349200 0.130500
+vn 0.864700 0.325500 0.382500
+vn 0.742700 0.279500 0.608600
+vn 0.569900 0.214500 0.793200
+vn 0.358300 0.134900 0.923800
+vn 0.122200 0.046000 0.991400
+vn -0.122200 -0.046000 0.991400
+vn -0.358300 -0.134900 0.923800
+vn -0.569900 -0.214500 0.793200
+vn -0.742700 -0.279500 0.608600
+vn -0.864700 -0.325500 0.382500
+vn -0.927900 -0.349200 0.130500
+vn -0.927900 -0.349200 -0.130500
+vn -0.864700 -0.325500 -0.382500
+vn -0.742700 -0.279500 -0.608600
+vn -0.569900 -0.214500 -0.793200
+vn -0.358300 -0.134900 -0.923800
+vn -0.122200 -0.046000 -0.991400
+vn 0.122200 0.046000 -0.991400
+vn 0.358300 0.134900 -0.923800
+vn 0.569900 0.214500 -0.793200
+vn 0.742700 0.279500 -0.608600
+vn 0.864700 0.325500 -0.382500
+vn 0.927900 0.349200 -0.130500
+vn 0.903100 0.409200 0.130500
+vn 0.841600 0.381300 0.382500
+vn 0.722800 0.327500 0.608600
+vn 0.554700 0.251300 0.793200
+vn 0.348700 0.158000 0.923800
+vn 0.119000 0.053900 0.991400
+vn -0.119000 -0.053900 0.991400
+vn -0.348700 -0.158000 0.923800
+vn -0.554700 -0.251300 0.793200
+vn -0.722800 -0.327500 0.608600
+vn -0.841600 -0.381300 0.382500
+vn -0.903100 -0.409200 0.130500
+vn -0.903100 -0.409200 -0.130500
+vn -0.841600 -0.381300 -0.382500
+vn -0.722800 -0.327500 -0.608600
+vn -0.554700 -0.251300 -0.793200
+vn -0.348700 -0.158000 -0.923800
+vn -0.119000 -0.053900 -0.991400
+vn 0.119000 0.053900 -0.991400
+vn 0.348700 0.158000 -0.923800
+vn 0.554700 0.251300 -0.793200
+vn 0.722800 0.327500 -0.608600
+vn 0.841600 0.381300 -0.382500
+vn 0.903100 0.409200 -0.130500
+vn 0.874400 0.467400 0.130500
+vn 0.814900 0.435600 0.382500
+vn 0.699800 0.374100 0.608600
+vn 0.537100 0.287100 0.793200
+vn 0.337700 0.180500 0.923800
+vn 0.115200 0.061600 0.991400
+vn -0.115200 -0.061600 0.991400
+vn -0.337700 -0.180500 0.923800
+vn -0.537100 -0.287100 0.793200
+vn -0.699800 -0.374100 0.608600
+vn -0.814900 -0.435500 0.382500
+vn -0.874400 -0.467400 0.130500
+vn -0.874400 -0.467400 -0.130500
+vn -0.814900 -0.435500 -0.382500
+vn -0.699800 -0.374100 -0.608600
+vn -0.537100 -0.287100 -0.793200
+vn -0.337700 -0.180500 -0.923800
+vn -0.115200 -0.061600 -0.991400
+vn 0.115200 0.061600 -0.991400
+vn 0.337700 0.180500 -0.923800
+vn 0.537100 0.287100 -0.793200
+vn 0.699800 0.374100 -0.608600
+vn 0.814900 0.435500 -0.382500
+vn 0.874400 0.467400 -0.130500
+vn 0.841900 0.523600 0.130500
+vn 0.784600 0.487900 0.382500
+vn 0.673800 0.419000 0.608600
+vn 0.517100 0.321600 0.793200
+vn 0.325100 0.202200 0.923800
+vn 0.110900 0.069000 0.991400
+vn -0.110900 -0.069000 0.991400
+vn -0.325100 -0.202200 0.923800
+vn -0.517100 -0.321600 0.793200
+vn -0.673900 -0.419000 0.608600
+vn -0.784600 -0.487900 0.382500
+vn -0.841900 -0.523600 0.130500
+vn -0.841900 -0.523600 -0.130500
+vn -0.784600 -0.487900 -0.382500
+vn -0.673900 -0.419000 -0.608600
+vn -0.517100 -0.321600 -0.793200
+vn -0.325100 -0.202200 -0.923800
+vn -0.110900 -0.069000 -0.991400
+vn 0.110900 0.069000 -0.991400
+vn 0.325100 0.202200 -0.923800
+vn 0.517100 0.321600 -0.793200
+vn 0.673800 0.419000 -0.608600
+vn 0.784600 0.487900 -0.382500
+vn 0.841900 0.523600 -0.130500
+vn 0.805900 0.577500 0.130500
+vn 0.751000 0.538200 0.382500
+vn 0.645000 0.462200 0.608600
+vn 0.495000 0.354700 0.793200
+vn 0.311200 0.223000 0.923800
+vn 0.106200 0.076100 0.991400
+vn -0.106200 -0.076100 0.991400
+vn -0.311200 -0.223000 0.923800
+vn -0.495000 -0.354700 0.793200
+vn -0.645000 -0.462200 0.608600
+vn -0.751000 -0.538200 0.382500
+vn -0.805900 -0.577500 0.130500
+vn -0.805900 -0.577500 -0.130500
+vn -0.751000 -0.538200 -0.382500
+vn -0.645000 -0.462200 -0.608600
+vn -0.495000 -0.354700 -0.793200
+vn -0.311200 -0.223000 -0.923800
+vn -0.106200 -0.076100 -0.991400
+vn 0.106200 0.076100 -0.991400
+vn 0.311200 0.223000 -0.923800
+vn 0.495000 0.354700 -0.793200
+vn 0.645000 0.462200 -0.608600
+vn 0.751000 0.538200 -0.382500
+vn 0.805900 0.577500 -0.130500
+vn 0.766400 0.629000 0.130500
+vn 0.714200 0.586100 0.382500
+vn 0.613400 0.503400 0.608600
+vn 0.470700 0.386300 0.793200
+vn 0.296000 0.242900 0.923800
+vn 0.101000 0.082800 0.991400
+vn -0.101000 -0.082800 0.991400
+vn -0.296000 -0.242900 0.923800
+vn -0.470700 -0.386300 0.793200
+vn -0.613400 -0.503400 0.608600
+vn -0.714200 -0.586100 0.382500
+vn -0.766400 -0.629000 0.130500
+vn -0.766400 -0.629000 -0.130500
+vn -0.714200 -0.586100 -0.382500
+vn -0.613400 -0.503400 -0.608600
+vn -0.470700 -0.386300 -0.793200
+vn -0.296000 -0.242900 -0.923800
+vn -0.101000 -0.082800 -0.991400
+vn 0.101000 0.082800 -0.991400
+vn 0.296000 0.242900 -0.923800
+vn 0.470700 0.386300 -0.793200
+vn 0.613400 0.503400 -0.608600
+vn 0.714200 0.586100 -0.382500
+vn 0.766400 0.629000 -0.130500
+vn 0.723600 0.677800 0.130500
+vn 0.674400 0.631600 0.382500
+vn 0.579200 0.542400 0.608600
+vn 0.444500 0.416300 0.793200
+vn 0.279400 0.261700 0.923800
+vn 0.095300 0.089300 0.991400
+vn -0.095300 -0.089300 0.991400
+vn -0.279400 -0.261700 0.923800
+vn -0.444500 -0.416300 0.793200
+vn -0.579200 -0.542400 0.608600
+vn -0.674400 -0.631600 0.382500
+vn -0.723600 -0.677700 0.130500
+vn -0.723600 -0.677800 -0.130500
+vn -0.674400 -0.631600 -0.382500
+vn -0.579200 -0.542400 -0.608600
+vn -0.444500 -0.416300 -0.793200
+vn -0.279400 -0.261700 -0.923800
+vn -0.095300 -0.089300 -0.991400
+vn 0.095300 0.089300 -0.991400
+vn 0.279400 0.261700 -0.923800
+vn 0.444500 0.416300 -0.793200
+vn 0.579200 0.542400 -0.608600
+vn 0.674400 0.631600 -0.382500
+vn 0.723600 0.677800 -0.130500
+vn 0.677800 0.723600 0.130500
+vn 0.631600 0.674400 0.382500
+vn 0.542400 0.579200 0.608600
+vn 0.416300 0.444500 0.793200
+vn 0.261700 0.279400 0.923800
+vn 0.089300 0.095300 0.991400
+vn -0.089300 -0.095300 0.991400
+vn -0.261700 -0.279400 0.923800
+vn -0.416300 -0.444500 0.793200
+vn -0.542400 -0.579200 0.608600
+vn -0.631600 -0.674400 0.382500
+vn -0.677800 -0.723600 0.130500
+vn -0.677800 -0.723600 -0.130500
+vn -0.631600 -0.674400 -0.382500
+vn -0.542400 -0.579200 -0.608600
+vn -0.416300 -0.444500 -0.793200
+vn -0.261700 -0.279400 -0.923800
+vn -0.089300 -0.095300 -0.991400
+vn 0.089300 0.095300 -0.991400
+vn 0.261700 0.279400 -0.923800
+vn 0.416300 0.444500 -0.793200
+vn 0.542400 0.579200 -0.608600
+vn 0.631600 0.674400 -0.382500
+vn 0.677800 0.723600 -0.130500
+vn 0.629000 0.766400 0.130500
+vn 0.586100 0.714200 0.382500
+vn 0.503400 0.613400 0.608600
+vn 0.386300 0.470700 0.793200
+vn 0.242900 0.296000 0.923800
+vn 0.082800 0.101000 0.991400
+vn -0.082800 -0.101000 0.991400
+vn -0.242900 -0.296000 0.923800
+vn -0.386300 -0.470700 0.793200
+vn -0.503400 -0.613400 0.608600
+vn -0.586100 -0.714200 0.382500
+vn -0.629000 -0.766400 0.130500
+vn -0.629000 -0.766400 -0.130500
+vn -0.586100 -0.714200 -0.382500
+vn -0.503400 -0.613400 -0.608600
+vn -0.386300 -0.470700 -0.793200
+vn -0.242900 -0.296000 -0.923800
+vn -0.082800 -0.101000 -0.991400
+vn 0.082800 0.101000 -0.991400
+vn 0.242900 0.296000 -0.923800
+vn 0.386300 0.470700 -0.793200
+vn 0.503400 0.613400 -0.608600
+vn 0.586100 0.714200 -0.382500
+vn 0.629000 0.766400 -0.130500
+vn 0.577500 0.805900 0.130500
+vn 0.538200 0.751000 0.382500
+vn 0.462200 0.645000 0.608600
+vn 0.354700 0.495000 0.793200
+vn 0.223000 0.311200 0.923800
+vn 0.076100 0.106200 0.991400
+vn -0.076100 -0.106200 0.991400
+vn -0.223000 -0.311200 0.923800
+vn -0.354700 -0.495000 0.793200
+vn -0.462200 -0.645000 0.608600
+vn -0.538200 -0.751000 0.382500
+vn -0.577500 -0.805900 0.130500
+vn -0.577500 -0.805900 -0.130500
+vn -0.538200 -0.751000 -0.382500
+vn -0.462200 -0.645000 -0.608600
+vn -0.354700 -0.495000 -0.793200
+vn -0.223000 -0.311200 -0.923800
+vn -0.076100 -0.106200 -0.991400
+vn 0.076100 0.106200 -0.991400
+vn 0.223000 0.311200 -0.923800
+vn 0.354700 0.495000 -0.793200
+vn 0.462200 0.645000 -0.608600
+vn 0.538200 0.751000 -0.382500
+vn 0.577500 0.805900 -0.130500
+vn 0.523600 0.841900 0.130500
+vn 0.487900 0.784600 0.382500
+vn 0.419000 0.673800 0.608600
+vn 0.321600 0.517100 0.793200
+vn 0.202200 0.325100 0.923800
+vn 0.069000 0.110900 0.991400
+vn -0.069000 -0.110900 0.991400
+vn -0.202200 -0.325100 0.923800
+vn -0.321600 -0.517100 0.793200
+vn -0.419000 -0.673900 0.608600
+vn -0.487900 -0.784600 0.382500
+vn -0.523600 -0.841900 0.130500
+vn -0.523600 -0.841900 -0.130500
+vn -0.487900 -0.784600 -0.382500
+vn -0.419000 -0.673900 -0.608600
+vn -0.321600 -0.517100 -0.793200
+vn -0.202200 -0.325100 -0.923800
+vn -0.069000 -0.110900 -0.991400
+vn 0.069000 0.110900 -0.991400
+vn 0.202200 0.325100 -0.923800
+vn 0.321600 0.517100 -0.793200
+vn 0.419000 0.673900 -0.608600
+vn 0.487900 0.784600 -0.382500
+vn 0.523600 0.841900 -0.130500
+vn 0.467400 0.874400 0.130500
+vn 0.435500 0.814900 0.382500
+vn 0.374100 0.699800 0.608600
+vn 0.287100 0.537100 0.793200
+vn 0.180500 0.337700 0.923800
+vn 0.061600 0.115200 0.991400
+vn -0.061600 -0.115200 0.991400
+vn -0.180500 -0.337700 0.923800
+vn -0.287100 -0.537100 0.793200
+vn -0.374100 -0.699800 0.608600
+vn -0.435500 -0.814900 0.382500
+vn -0.467400 -0.874400 0.130500
+vn -0.467400 -0.874400 -0.130500
+vn -0.435500 -0.814900 -0.382500
+vn -0.374100 -0.699800 -0.608600
+vn -0.287100 -0.537100 -0.793200
+vn -0.180500 -0.337700 -0.923800
+vn -0.061600 -0.115200 -0.991400
+vn 0.061600 0.115200 -0.991400
+vn 0.180500 0.337700 -0.923800
+vn 0.287100 0.537100 -0.793200
+vn 0.374100 0.699800 -0.608600
+vn 0.435500 0.814900 -0.382500
+vn 0.467400 0.874400 -0.130500
+vn 0.409200 0.903100 0.130500
+vn 0.381300 0.841600 0.382500
+vn 0.327500 0.722800 0.608600
+vn 0.251300 0.554700 0.793200
+vn 0.158000 0.348700 0.923800
+vn 0.053900 0.119000 0.991400
+vn -0.053900 -0.119000 0.991400
+vn -0.158000 -0.348700 0.923800
+vn -0.251300 -0.554700 0.793200
+vn -0.327500 -0.722800 0.608600
+vn -0.381300 -0.841600 0.382500
+vn -0.409200 -0.903100 0.130500
+vn -0.409200 -0.903100 -0.130500
+vn -0.381300 -0.841600 -0.382500
+vn -0.327500 -0.722800 -0.608600
+vn -0.251300 -0.554700 -0.793200
+vn -0.158000 -0.348700 -0.923800
+vn -0.053900 -0.119000 -0.991400
+vn 0.053900 0.119000 -0.991400
+vn 0.158000 0.348700 -0.923800
+vn 0.251300 0.554700 -0.793200
+vn 0.327500 0.722800 -0.608600
+vn 0.381300 0.841600 -0.382500
+vn 0.409200 0.903100 -0.130500
+vn 0.349200 0.927900 0.130500
+vn 0.325500 0.864700 0.382500
+vn 0.279500 0.742600 0.608600
+vn 0.214500 0.569900 0.793200
+vn 0.134900 0.358300 0.923800
+vn 0.046000 0.122200 0.991400
+vn -0.046000 -0.122200 0.991400
+vn -0.134900 -0.358300 0.923800
+vn -0.214500 -0.569900 0.793200
+vn -0.279500 -0.742700 0.608600
+vn -0.325500 -0.864700 0.382500
+vn -0.349200 -0.927900 0.130500
+vn -0.349200 -0.927900 -0.130500
+vn -0.325500 -0.864700 -0.382500
+vn -0.279500 -0.742700 -0.608600
+vn -0.214500 -0.569900 -0.793200
+vn -0.134900 -0.358300 -0.923800
+vn -0.046000 -0.122200 -0.991400
+vn 0.046000 0.122200 -0.991400
+vn 0.134900 0.358300 -0.923800
+vn 0.214500 0.569900 -0.793200
+vn 0.279500 0.742600 -0.608600
+vn 0.325500 0.864700 -0.382500
+vn 0.349200 0.927900 -0.130500
+vn 0.287800 0.948800 0.130500
+vn 0.268200 0.884200 0.382500
+vn 0.230300 0.759300 0.608600
+vn 0.176800 0.582700 0.793200
+vn 0.111100 0.366400 0.923800
+vn 0.037900 0.125000 0.991400
+vn -0.037900 -0.125000 0.991400
+vn -0.111100 -0.366400 0.923800
+vn -0.176800 -0.582700 0.793200
+vn -0.230300 -0.759300 0.608600
+vn -0.268200 -0.884200 0.382500
+vn -0.287800 -0.948800 0.130500
+vn -0.287800 -0.948800 -0.130500
+vn -0.268200 -0.884200 -0.382500
+vn -0.230300 -0.759300 -0.608600
+vn -0.176800 -0.582700 -0.793200
+vn -0.111100 -0.366400 -0.923800
+vn -0.037900 -0.125000 -0.991400
+vn 0.037900 0.125000 -0.991400
+vn 0.111100 0.366400 -0.923800
+vn 0.176800 0.582700 -0.793200
+vn 0.230300 0.759300 -0.608600
+vn 0.268200 0.884200 -0.382500
+vn 0.287800 0.948800 -0.130500
+vn 0.225100 0.965600 0.130500
+vn 0.209800 0.899800 0.382500
+vn 0.180200 0.772800 0.608600
+vn 0.138300 0.593100 0.793200
+vn 0.086900 0.372900 0.923800
+vn 0.029700 0.127200 0.991400
+vn -0.029700 -0.127200 0.991400
+vn -0.086900 -0.372900 0.923800
+vn -0.138300 -0.593100 0.793200
+vn -0.180200 -0.772800 0.608600
+vn -0.209800 -0.899800 0.382500
+vn -0.225100 -0.965600 0.130500
+vn -0.225100 -0.965600 -0.130500
+vn -0.209800 -0.899800 -0.382500
+vn -0.180200 -0.772800 -0.608600
+vn -0.138300 -0.593100 -0.793200
+vn -0.086900 -0.372900 -0.923800
+vn -0.029700 -0.127200 -0.991400
+vn 0.029700 0.127200 -0.991400
+vn 0.086900 0.372900 -0.923800
+vn 0.138300 0.593100 -0.793200
+vn 0.180200 0.772800 -0.608600
+vn 0.209800 0.899800 -0.382500
+vn 0.225100 0.965600 -0.130500
+vn 0.161500 0.978200 0.130500
+vn 0.150500 0.911600 0.382500
+vn 0.129300 0.782900 0.608600
+vn 0.099200 0.600800 0.793200
+vn 0.062400 0.377700 0.923800
+vn 0.021300 0.128900 0.991400
+vn -0.021300 -0.128900 0.991400
+vn -0.062400 -0.377700 0.923800
+vn -0.099200 -0.600800 0.793200
+vn -0.129300 -0.782900 0.608600
+vn -0.150500 -0.911600 0.382500
+vn -0.161500 -0.978200 0.130500
+vn -0.161500 -0.978200 -0.130500
+vn -0.150500 -0.911600 -0.382500
+vn -0.129300 -0.782900 -0.608600
+vn -0.099200 -0.600800 -0.793200
+vn -0.062400 -0.377700 -0.923800
+vn -0.021300 -0.128900 -0.991400
+vn 0.021300 0.128900 -0.991400
+vn 0.062400 0.377700 -0.923800
+vn 0.099200 0.600800 -0.793200
+vn 0.129300 0.782900 -0.608600
+vn 0.150500 0.911600 -0.382500
+vn 0.161500 0.978200 -0.130500
+vn 0.097200 0.986700 0.130500
+vn 0.090600 0.919500 0.382500
+vn 0.077800 0.789700 0.608600
+vn 0.059700 0.606000 0.793200
+vn 0.037500 0.381000 0.923800
+vn 0.012800 0.130000 0.991400
+vn -0.012800 -0.130000 0.991400
+vn -0.037500 -0.381000 0.923800
+vn -0.059700 -0.606000 0.793200
+vn -0.077800 -0.789700 0.608600
+vn -0.090600 -0.919500 0.382500
+vn -0.097200 -0.986700 0.130500
+vn -0.097200 -0.986700 -0.130500
+vn -0.090600 -0.919500 -0.382500
+vn -0.077800 -0.789700 -0.608600
+vn -0.059700 -0.606000 -0.793200
+vn -0.037500 -0.381000 -0.923800
+vn -0.012800 -0.130000 -0.991400
+vn 0.012800 0.130000 -0.991400
+vn 0.037500 0.381000 -0.923800
+vn 0.059700 0.606000 -0.793200
+vn 0.077800 0.789700 -0.608600
+vn 0.090600 0.919500 -0.382500
+vn 0.097200 0.986700 -0.130500
+vn 0.032400 0.990900 0.130500
+vn 0.030200 0.923500 0.382500
+vn 0.026000 0.793100 0.608600
+vn 0.019900 0.608600 0.793200
+vn 0.012500 0.382700 0.923800
+vn 0.004300 0.130500 0.991400
+vn -0.004300 -0.130500 0.991400
+vn -0.012500 -0.382700 0.923800
+vn -0.019900 -0.608600 0.793200
+vn -0.026000 -0.793100 0.608600
+vn -0.030200 -0.923500 0.382500
+vn -0.032400 -0.990900 0.130500
+vn -0.032400 -0.990900 -0.130500
+vn -0.030200 -0.923500 -0.382500
+vn -0.026000 -0.793100 -0.608600
+vn -0.019900 -0.608600 -0.793200
+vn -0.012500 -0.382700 -0.923800
+vn -0.004300 -0.130500 -0.991400
+vn 0.004300 0.130500 -0.991400
+vn 0.012500 0.382700 -0.923800
+vn 0.019900 0.608600 -0.793200
+vn 0.026000 0.793100 -0.608600
+vn 0.030200 0.923500 -0.382500
+vn 0.032400 0.990900 -0.130500
+vn -0.032400 0.990900 0.130500
+vn -0.030200 0.923500 0.382500
+vn -0.026000 0.793100 0.608600
+vn -0.019900 0.608600 0.793200
+vn -0.012500 0.382700 0.923800
+vn -0.004300 0.130500 0.991400
+vn 0.004300 -0.130500 0.991400
+vn 0.012500 -0.382700 0.923800
+vn 0.019900 -0.608600 0.793200
+vn 0.026000 -0.793100 0.608600
+vn 0.030200 -0.923500 0.382500
+vn 0.032400 -0.990900 0.130500
+vn 0.032400 -0.990900 -0.130500
+vn 0.030200 -0.923500 -0.382500
+vn 0.026000 -0.793100 -0.608600
+vn 0.019900 -0.608600 -0.793200
+vn 0.012500 -0.382700 -0.923800
+vn 0.004300 -0.130500 -0.991400
+vn -0.004300 0.130500 -0.991400
+vn -0.012500 0.382700 -0.923800
+vn -0.019900 0.608600 -0.793200
+vn -0.026000 0.793100 -0.608600
+vn -0.030200 0.923500 -0.382500
+vn -0.032400 0.990900 -0.130500
+vn -0.097200 0.986700 0.130500
+vn -0.090600 0.919500 0.382500
+vn -0.077800 0.789700 0.608600
+vn -0.059700 0.606000 0.793200
+vn -0.037500 0.381000 0.923800
+vn -0.012800 0.130000 0.991400
+vn 0.012800 -0.130000 0.991400
+vn 0.037500 -0.381000 0.923800
+vn 0.059700 -0.606000 0.793200
+vn 0.077800 -0.789700 0.608600
+vn 0.090600 -0.919500 0.382500
+vn 0.097200 -0.986700 0.130500
+vn 0.097200 -0.986700 -0.130500
+vn 0.090600 -0.919500 -0.382500
+vn 0.077800 -0.789700 -0.608600
+vn 0.059700 -0.606000 -0.793200
+vn 0.037500 -0.381000 -0.923800
+vn 0.012800 -0.130000 -0.991400
+vn -0.012800 0.130000 -0.991400
+vn -0.037500 0.381000 -0.923800
+vn -0.059700 0.606000 -0.793200
+vn -0.077800 0.789700 -0.608600
+vn -0.090600 0.919500 -0.382500
+vn -0.097200 0.986700 -0.130500
+vn -0.161500 0.978200 0.130500
+vn -0.150500 0.911600 0.382500
+vn -0.129300 0.782900 0.608600
+vn -0.099200 0.600800 0.793200
+vn -0.062400 0.377700 0.923800
+vn -0.021300 0.128900 0.991400
+vn 0.021300 -0.128800 0.991400
+vn 0.062400 -0.377700 0.923800
+vn 0.099200 -0.600800 0.793200
+vn 0.129300 -0.782900 0.608600
+vn 0.150500 -0.911600 0.382500
+vn 0.161500 -0.978200 0.130500
+vn 0.161500 -0.978200 -0.130500
+vn 0.150500 -0.911600 -0.382500
+vn 0.129300 -0.782900 -0.608600
+vn 0.099200 -0.600800 -0.793200
+vn 0.062400 -0.377700 -0.923800
+vn 0.021300 -0.128900 -0.991400
+vn -0.021300 0.128900 -0.991400
+vn -0.062400 0.377700 -0.923800
+vn -0.099200 0.600800 -0.793200
+vn -0.129300 0.782900 -0.608600
+vn -0.150500 0.911600 -0.382500
+vn -0.161500 0.978200 -0.130500
+vn -0.225100 0.965600 0.130500
+vn -0.209800 0.899800 0.382500
+vn -0.180200 0.772800 0.608600
+vn -0.138300 0.593100 0.793200
+vn -0.086900 0.372900 0.923800
+vn -0.029700 0.127200 0.991400
+vn 0.029700 -0.127200 0.991400
+vn 0.086900 -0.372900 0.923800
+vn 0.138300 -0.593100 0.793200
+vn 0.180200 -0.772800 0.608600
+vn 0.209800 -0.899800 0.382500
+vn 0.225100 -0.965600 0.130500
+vn 0.225100 -0.965600 -0.130500
+vn 0.209800 -0.899800 -0.382500
+vn 0.180200 -0.772800 -0.608600
+vn 0.138300 -0.593100 -0.793200
+vn 0.086900 -0.372900 -0.923800
+vn 0.029700 -0.127200 -0.991400
+vn -0.029700 0.127200 -0.991400
+vn -0.086900 0.372900 -0.923800
+vn -0.138300 0.593100 -0.793200
+vn -0.180200 0.772800 -0.608600
+vn -0.209800 0.899800 -0.382500
+vn -0.225100 0.965600 -0.130500
+vn -0.287800 0.948800 0.130500
+vn -0.268200 0.884200 0.382500
+vn -0.230300 0.759300 0.608600
+vn -0.176800 0.582700 0.793200
+vn -0.111100 0.366400 0.923800
+vn -0.037900 0.125000 0.991400
+vn 0.037900 -0.125000 0.991400
+vn 0.111100 -0.366400 0.923800
+vn 0.176800 -0.582700 0.793200
+vn 0.230300 -0.759300 0.608600
+vn 0.268200 -0.884200 0.382500
+vn 0.287800 -0.948800 0.130500
+vn 0.287800 -0.948800 -0.130500
+vn 0.268200 -0.884200 -0.382500
+vn 0.230300 -0.759300 -0.608600
+vn 0.176800 -0.582700 -0.793200
+vn 0.111100 -0.366400 -0.923800
+vn 0.037900 -0.125000 -0.991400
+vn -0.037900 0.125000 -0.991400
+vn -0.111100 0.366400 -0.923800
+vn -0.176800 0.582700 -0.793200
+vn -0.230300 0.759300 -0.608600
+vn -0.268200 0.884200 -0.382500
+vn -0.287800 0.948800 -0.130500
+vn -0.349200 0.927900 0.130500
+vn -0.325500 0.864700 0.382500
+vn -0.279500 0.742600 0.608600
+vn -0.214500 0.569900 0.793200
+vn -0.134900 0.358300 0.923800
+vn -0.046000 0.122200 0.991400
+vn 0.046000 -0.122200 0.991400
+vn 0.134900 -0.358300 0.923800
+vn 0.214500 -0.569900 0.793200
+vn 0.279500 -0.742700 0.608600
+vn 0.325500 -0.864700 0.382500
+vn 0.349200 -0.927900 0.130500
+vn 0.349200 -0.927900 -0.130500
+vn 0.325500 -0.864700 -0.382500
+vn 0.279500 -0.742700 -0.608600
+vn 0.214500 -0.569900 -0.793200
+vn 0.134900 -0.358300 -0.923800
+vn 0.046000 -0.122200 -0.991400
+vn -0.046000 0.122200 -0.991400
+vn -0.134900 0.358300 -0.923800
+vn -0.214500 0.569900 -0.793200
+vn -0.279500 0.742600 -0.608600
+vn -0.325500 0.864700 -0.382500
+vn -0.349200 0.927900 -0.130500
+vn -0.409200 0.903100 0.130500
+vn -0.381300 0.841600 0.382500
+vn -0.327500 0.722800 0.608600
+vn -0.251300 0.554700 0.793200
+vn -0.158000 0.348700 0.923800
+vn -0.053900 0.119000 0.991400
+vn 0.053900 -0.119000 0.991400
+vn 0.158000 -0.348700 0.923800
+vn 0.251300 -0.554700 0.793200
+vn 0.327500 -0.722800 0.608600
+vn 0.381300 -0.841600 0.382500
+vn 0.409200 -0.903100 0.130500
+vn 0.409200 -0.903100 -0.130500
+vn 0.381300 -0.841600 -0.382500
+vn 0.327500 -0.722800 -0.608600
+vn 0.251300 -0.554700 -0.793200
+vn 0.158000 -0.348700 -0.923800
+vn 0.053900 -0.119000 -0.991400
+vn -0.053900 0.119000 -0.991400
+vn -0.158000 0.348700 -0.923800
+vn -0.251300 0.554700 -0.793200
+vn -0.327500 0.722800 -0.608600
+vn -0.381300 0.841600 -0.382500
+vn -0.409200 0.903100 -0.130500
+vn -0.467400 0.874400 0.130500
+vn -0.435500 0.814900 0.382500
+vn -0.374100 0.699800 0.608600
+vn -0.287100 0.537100 0.793200
+vn -0.180500 0.337700 0.923800
+vn -0.061600 0.115200 0.991400
+vn 0.061600 -0.115200 0.991400
+vn 0.180500 -0.337700 0.923800
+vn 0.287100 -0.537100 0.793200
+vn 0.374100 -0.699800 0.608600
+vn 0.435500 -0.814900 0.382500
+vn 0.467400 -0.874400 0.130500
+vn 0.467400 -0.874400 -0.130500
+vn 0.435500 -0.814900 -0.382500
+vn 0.374100 -0.699800 -0.608600
+vn 0.287100 -0.537100 -0.793200
+vn 0.180500 -0.337700 -0.923800
+vn 0.061600 -0.115200 -0.991400
+vn -0.061600 0.115200 -0.991400
+vn -0.180500 0.337700 -0.923800
+vn -0.287100 0.537100 -0.793200
+vn -0.374100 0.699800 -0.608600
+vn -0.435500 0.814900 -0.382500
+vn -0.467400 0.874400 -0.130500
+vn -0.523600 0.841900 0.130500
+vn -0.487900 0.784600 0.382500
+vn -0.419000 0.673900 0.608600
+vn -0.321600 0.517100 0.793200
+vn -0.202200 0.325100 0.923800
+vn -0.069000 0.110900 0.991400
+vn 0.069000 -0.110900 0.991400
+vn 0.202200 -0.325100 0.923800
+vn 0.321600 -0.517100 0.793200
+vn 0.419000 -0.673900 0.608600
+vn 0.487900 -0.784600 0.382500
+vn 0.523600 -0.841900 0.130500
+vn 0.523600 -0.841900 -0.130500
+vn 0.487900 -0.784600 -0.382500
+vn 0.419000 -0.673900 -0.608600
+vn 0.321600 -0.517100 -0.793200
+vn 0.202200 -0.325100 -0.923800
+vn 0.069000 -0.110900 -0.991400
+vn -0.069000 0.110900 -0.991400
+vn -0.202200 0.325100 -0.923800
+vn -0.321600 0.517100 -0.793200
+vn -0.419000 0.673900 -0.608600
+vn -0.487900 0.784600 -0.382500
+vn -0.523600 0.841900 -0.130500
+vn -0.577500 0.805900 0.130500
+vn -0.538200 0.751000 0.382500
+vn -0.462200 0.645000 0.608600
+vn -0.354700 0.495000 0.793200
+vn -0.223000 0.311200 0.923800
+vn -0.076100 0.106200 0.991400
+vn 0.076100 -0.106200 0.991400
+vn 0.223000 -0.311200 0.923800
+vn 0.354700 -0.495000 0.793200
+vn 0.462200 -0.645000 0.608600
+vn 0.538200 -0.751000 0.382500
+vn 0.577500 -0.805900 0.130500
+vn 0.577500 -0.805900 -0.130500
+vn 0.538200 -0.751000 -0.382500
+vn 0.462200 -0.645000 -0.608600
+vn 0.354700 -0.495000 -0.793200
+vn 0.223000 -0.311200 -0.923800
+vn 0.076100 -0.106200 -0.991400
+vn -0.076100 0.106200 -0.991400
+vn -0.223000 0.311200 -0.923800
+vn -0.354700 0.495000 -0.793200
+vn -0.462200 0.645000 -0.608600
+vn -0.538200 0.751000 -0.382500
+vn -0.577500 0.805900 -0.130500
+vn -0.629000 0.766400 0.130500
+vn -0.586100 0.714200 0.382500
+vn -0.503400 0.613400 0.608600
+vn -0.386300 0.470700 0.793200
+vn -0.242900 0.296000 0.923800
+vn -0.082800 0.101000 0.991400
+vn 0.082800 -0.101000 0.991400
+vn 0.242900 -0.296000 0.923800
+vn 0.386300 -0.470700 0.793200
+vn 0.503400 -0.613400 0.608600
+vn 0.586100 -0.714200 0.382500
+vn 0.629000 -0.766400 0.130500
+vn 0.629000 -0.766400 -0.130500
+vn 0.586100 -0.714200 -0.382500
+vn 0.503400 -0.613400 -0.608600
+vn 0.386300 -0.470700 -0.793200
+vn 0.242900 -0.296000 -0.923800
+vn 0.082800 -0.101000 -0.991400
+vn -0.082800 0.101000 -0.991400
+vn -0.242900 0.296000 -0.923800
+vn -0.386300 0.470700 -0.793200
+vn -0.503400 0.613400 -0.608600
+vn -0.586100 0.714200 -0.382500
+vn -0.629000 0.766400 -0.130500
+vn -0.677800 0.723600 0.130500
+vn -0.631600 0.674400 0.382500
+vn -0.542400 0.579200 0.608600
+vn -0.416300 0.444500 0.793200
+vn -0.261700 0.279400 0.923800
+vn -0.089300 0.095300 0.991400
+vn 0.089300 -0.095300 0.991400
+vn 0.261700 -0.279400 0.923800
+vn 0.416300 -0.444500 0.793200
+vn 0.542400 -0.579200 0.608600
+vn 0.631600 -0.674400 0.382500
+vn 0.677800 -0.723600 0.130500
+vn 0.677800 -0.723600 -0.130500
+vn 0.631600 -0.674400 -0.382500
+vn 0.542400 -0.579200 -0.608600
+vn 0.416300 -0.444500 -0.793200
+vn 0.261700 -0.279400 -0.923800
+vn 0.089300 -0.095300 -0.991400
+vn -0.089300 0.095300 -0.991400
+vn -0.261700 0.279400 -0.923800
+vn -0.416300 0.444500 -0.793200
+vn -0.542400 0.579200 -0.608600
+vn -0.631600 0.674400 -0.382500
+vn -0.677800 0.723600 -0.130500
+vn -0.723600 0.677800 0.130500
+vn -0.674400 0.631600 0.382500
+vn -0.579200 0.542400 0.608600
+vn -0.444500 0.416300 0.793200
+vn -0.279400 0.261700 0.923800
+vn -0.095300 0.089300 0.991400
+vn 0.095300 -0.089300 0.991400
+vn 0.279400 -0.261700 0.923800
+vn 0.444500 -0.416300 0.793200
+vn 0.579200 -0.542400 0.608600
+vn 0.674400 -0.631600 0.382500
+vn 0.723600 -0.677800 0.130500
+vn 0.723600 -0.677800 -0.130500
+vn 0.674400 -0.631600 -0.382500
+vn 0.579200 -0.542400 -0.608600
+vn 0.444500 -0.416300 -0.793200
+vn 0.279400 -0.261700 -0.923800
+vn 0.095300 -0.089300 -0.991400
+vn -0.095300 0.089300 -0.991400
+vn -0.279400 0.261700 -0.923800
+vn -0.444500 0.416300 -0.793200
+vn -0.579200 0.542400 -0.608600
+vn -0.674400 0.631600 -0.382500
+vn -0.723600 0.677700 -0.130500
+vn -0.766400 0.629000 0.130500
+vn -0.714200 0.586100 0.382500
+vn -0.613400 0.503400 0.608600
+vn -0.470700 0.386300 0.793200
+vn -0.296000 0.242900 0.923800
+vn -0.101000 0.082800 0.991400
+vn 0.101000 -0.082800 0.991400
+vn 0.296000 -0.242900 0.923800
+vn 0.470700 -0.386300 0.793200
+vn 0.613400 -0.503400 0.608600
+vn 0.714200 -0.586200 0.382500
+vn 0.766400 -0.629000 0.130500
+vn 0.766400 -0.629000 -0.130500
+vn 0.714200 -0.586100 -0.382500
+vn 0.613400 -0.503400 -0.608600
+vn 0.470700 -0.386300 -0.793200
+vn 0.296000 -0.242900 -0.923800
+vn 0.101000 -0.082800 -0.991400
+vn -0.101000 0.082800 -0.991400
+vn -0.296000 0.242900 -0.923800
+vn -0.470700 0.386300 -0.793200
+vn -0.613400 0.503400 -0.608600
+vn -0.714200 0.586100 -0.382500
+vn -0.766400 0.629000 -0.130500
+vn -0.805900 0.577500 0.130500
+vn -0.751000 0.538200 0.382500
+vn -0.645000 0.462200 0.608600
+vn -0.495000 0.354700 0.793200
+vn -0.311200 0.223000 0.923800
+vn -0.106200 0.076100 0.991400
+vn 0.106200 -0.076100 0.991400
+vn 0.311200 -0.223000 0.923800
+vn 0.495000 -0.354700 0.793200
+vn 0.645000 -0.462200 0.608600
+vn 0.751000 -0.538200 0.382500
+vn 0.805900 -0.577500 0.130500
+vn 0.805900 -0.577500 -0.130500
+vn 0.751000 -0.538200 -0.382500
+vn 0.645000 -0.462200 -0.608600
+vn 0.495000 -0.354700 -0.793200
+vn 0.311200 -0.223000 -0.923800
+vn 0.106200 -0.076100 -0.991400
+vn -0.106200 0.076100 -0.991400
+vn -0.311200 0.223000 -0.923800
+vn -0.495000 0.354700 -0.793200
+vn -0.645000 0.462200 -0.608600
+vn -0.751000 0.538200 -0.382500
+vn -0.805900 0.577500 -0.130500
+vn -0.841900 0.523600 0.130500
+vn -0.784600 0.487900 0.382500
+vn -0.673800 0.419000 0.608600
+vn -0.517100 0.321600 0.793200
+vn -0.325100 0.202200 0.923800
+vn -0.110900 0.069000 0.991400
+vn 0.110900 -0.069000 0.991400
+vn 0.325100 -0.202200 0.923800
+vn 0.517100 -0.321600 0.793200
+vn 0.673800 -0.419000 0.608600
+vn 0.784600 -0.487900 0.382500
+vn 0.841900 -0.523600 0.130500
+vn 0.841900 -0.523600 -0.130500
+vn 0.784600 -0.487900 -0.382500
+vn 0.673800 -0.419000 -0.608600
+vn 0.517100 -0.321600 -0.793200
+vn 0.325100 -0.202200 -0.923800
+vn 0.110900 -0.069000 -0.991400
+vn -0.110900 0.069000 -0.991400
+vn -0.325100 0.202200 -0.923800
+vn -0.517100 0.321600 -0.793200
+vn -0.673900 0.419000 -0.608600
+vn -0.784600 0.487900 -0.382500
+vn -0.841900 0.523600 -0.130500
+vn -0.874400 0.467400 0.130500
+vn -0.814900 0.435500 0.382500
+vn -0.699800 0.374100 0.608600
+vn -0.537100 0.287100 0.793200
+vn -0.337700 0.180500 0.923800
+vn -0.115200 0.061600 0.991400
+vn 0.115200 -0.061600 0.991400
+vn 0.337700 -0.180500 0.923800
+vn 0.537100 -0.287100 0.793200
+vn 0.699800 -0.374100 0.608600
+vn 0.814900 -0.435500 0.382500
+vn 0.874400 -0.467400 0.130500
+vn 0.874400 -0.467400 -0.130500
+vn 0.814900 -0.435500 -0.382500
+vn 0.699800 -0.374100 -0.608600
+vn 0.537100 -0.287100 -0.793200
+vn 0.337700 -0.180500 -0.923800
+vn 0.115200 -0.061600 -0.991400
+vn -0.115200 0.061600 -0.991400
+vn -0.337700 0.180500 -0.923800
+vn -0.537100 0.287100 -0.793200
+vn -0.699800 0.374100 -0.608600
+vn -0.814900 0.435500 -0.382500
+vn -0.874400 0.467400 -0.130500
+vn -0.903100 0.409200 0.130500
+vn -0.841600 0.381300 0.382500
+vn -0.722800 0.327500 0.608600
+vn -0.554700 0.251300 0.793200
+vn -0.348700 0.158000 0.923800
+vn -0.119000 0.053900 0.991400
+vn 0.119000 -0.053900 0.991400
+vn 0.348700 -0.158000 0.923800
+vn 0.554700 -0.251300 0.793200
+vn 0.722800 -0.327500 0.608600
+vn 0.841600 -0.381300 0.382500
+vn 0.903100 -0.409200 0.130500
+vn 0.903100 -0.409200 -0.130500
+vn 0.841600 -0.381300 -0.382500
+vn 0.722800 -0.327500 -0.608600
+vn 0.554700 -0.251300 -0.793200
+vn 0.348700 -0.158000 -0.923800
+vn 0.119000 -0.053900 -0.991400
+vn -0.119000 0.053900 -0.991400
+vn -0.348700 0.158000 -0.923800
+vn -0.554700 0.251300 -0.793200
+vn -0.722800 0.327500 -0.608600
+vn -0.841600 0.381300 -0.382500
+vn -0.903100 0.409200 -0.130500
+vn -0.927900 0.349200 0.130500
+vn -0.864700 0.325500 0.382500
+vn -0.742600 0.279500 0.608600
+vn -0.569900 0.214500 0.793200
+vn -0.358300 0.134900 0.923800
+vn -0.122200 0.046000 0.991400
+vn 0.122200 -0.046000 0.991400
+vn 0.358300 -0.134900 0.923800
+vn 0.569900 -0.214500 0.793200
+vn 0.742700 -0.279500 0.608600
+vn 0.864700 -0.325500 0.382500
+vn 0.927900 -0.349200 0.130500
+vn 0.927900 -0.349200 -0.130500
+vn 0.864700 -0.325500 -0.382500
+vn 0.742700 -0.279500 -0.608600
+vn 0.569900 -0.214500 -0.793200
+vn 0.358300 -0.134900 -0.923800
+vn 0.122200 -0.046000 -0.991400
+vn -0.122200 0.046000 -0.991400
+vn -0.358300 0.134900 -0.923800
+vn -0.569900 0.214500 -0.793200
+vn -0.742600 0.279500 -0.608600
+vn -0.864700 0.325500 -0.382500
+vn -0.927900 0.349200 -0.130500
+vn -0.948800 0.287800 0.130500
+vn -0.884200 0.268200 0.382500
+vn -0.759300 0.230300 0.608600
+vn -0.582700 0.176800 0.793200
+vn -0.366400 0.111100 0.923800
+vn -0.125000 0.037900 0.991400
+vn 0.125000 -0.037900 0.991400
+vn 0.366400 -0.111100 0.923800
+vn 0.582700 -0.176800 0.793200
+vn 0.759300 -0.230300 0.608600
+vn 0.884200 -0.268200 0.382500
+vn 0.948800 -0.287800 0.130500
+vn 0.948800 -0.287800 -0.130500
+vn 0.884200 -0.268200 -0.382500
+vn 0.759300 -0.230300 -0.608600
+vn 0.582700 -0.176800 -0.793200
+vn 0.366400 -0.111100 -0.923800
+vn 0.125000 -0.037900 -0.991400
+vn -0.125000 0.037900 -0.991400
+vn -0.366400 0.111100 -0.923800
+vn -0.582700 0.176800 -0.793200
+vn -0.759300 0.230300 -0.608600
+vn -0.884200 0.268200 -0.382500
+vn -0.948800 0.287800 -0.130500
+vn -0.965600 0.225100 0.130500
+vn -0.899800 0.209800 0.382500
+vn -0.772800 0.180200 0.608600
+vn -0.593100 0.138300 0.793200
+vn -0.372900 0.086900 0.923800
+vn -0.127200 0.029700 0.991400
+vn 0.127200 -0.029700 0.991400
+vn 0.372900 -0.086900 0.923800
+vn 0.593100 -0.138300 0.793200
+vn 0.772800 -0.180200 0.608600
+vn 0.899800 -0.209800 0.382500
+vn 0.965600 -0.225100 0.130500
+vn 0.965600 -0.225100 -0.130500
+vn 0.899800 -0.209800 -0.382500
+vn 0.772800 -0.180200 -0.608600
+vn 0.593100 -0.138300 -0.793200
+vn 0.372900 -0.086900 -0.923800
+vn 0.127200 -0.029700 -0.991400
+vn -0.127200 0.029700 -0.991400
+vn -0.372900 0.086900 -0.923800
+vn -0.593100 0.138300 -0.793200
+vn -0.772800 0.180200 -0.608600
+vn -0.899800 0.209800 -0.382500
+vn -0.965600 0.225100 -0.130500
+vn -0.978200 0.161500 0.130500
+vn -0.911600 0.150500 0.382500
+vn -0.782900 0.129300 0.608600
+vn -0.600800 0.099200 0.793200
+vn -0.377700 0.062400 0.923800
+vn -0.128900 0.021300 0.991400
+vn 0.128900 -0.021300 0.991400
+vn 0.377700 -0.062400 0.923800
+vn 0.600800 -0.099200 0.793200
+vn 0.782900 -0.129300 0.608600
+vn 0.911600 -0.150500 0.382500
+vn 0.978200 -0.161500 0.130500
+vn 0.978200 -0.161500 -0.130500
+vn 0.911600 -0.150500 -0.382500
+vn 0.782900 -0.129300 -0.608600
+vn 0.600800 -0.099200 -0.793200
+vn 0.377700 -0.062400 -0.923800
+vn 0.128900 -0.021300 -0.991400
+vn -0.128900 0.021300 -0.991400
+vn -0.377700 0.062400 -0.923800
+vn -0.600800 0.099200 -0.793200
+vn -0.782900 0.129300 -0.608600
+vn -0.911600 0.150500 -0.382500
+vn -0.978200 0.161500 -0.130500
+vn -0.986700 0.097200 0.130500
+vn -0.919500 0.090600 0.382500
+vn -0.789700 0.077800 0.608600
+vn -0.606000 0.059700 0.793200
+vn -0.381000 0.037500 0.923800
+vn -0.130000 0.012800 0.991400
+vn 0.130000 -0.012800 0.991400
+vn 0.381000 -0.037500 0.923800
+vn 0.606000 -0.059700 0.793200
+vn 0.789700 -0.077800 0.608600
+vn 0.919500 -0.090600 0.382500
+vn 0.986700 -0.097200 0.130500
+vn 0.986700 -0.097200 -0.130500
+vn 0.919500 -0.090600 -0.382500
+vn 0.789700 -0.077800 -0.608600
+vn 0.606000 -0.059700 -0.793200
+vn 0.381000 -0.037500 -0.923800
+vn 0.130000 -0.012800 -0.991400
+vn -0.130000 0.012800 -0.991400
+vn -0.381000 0.037500 -0.923800
+vn -0.606000 0.059700 -0.793200
+vn -0.789700 0.077800 -0.608600
+vn -0.919500 0.090600 -0.382500
+vn -0.986700 0.097200 -0.130500
+vn -0.990900 0.032400 0.130500
+vn -0.923500 0.030200 0.382500
+vn -0.793100 0.026000 0.608600
+vn -0.608600 0.019900 0.793200
+vn -0.382700 0.012500 0.923800
+vn -0.130500 0.004300 0.991400
+vn 0.130500 -0.004300 0.991400
+vn 0.382700 -0.012500 0.923800
+vn 0.608600 -0.019900 0.793200
+vn 0.793100 -0.026000 0.608600
+vn 0.923500 -0.030200 0.382500
+vn 0.990900 -0.032400 0.130500
+vn 0.990900 -0.032400 -0.130500
+vn 0.923500 -0.030200 -0.382500
+vn 0.793100 -0.026000 -0.608600
+vn 0.608600 -0.019900 -0.793200
+vn 0.382700 -0.012500 -0.923800
+vn 0.130500 -0.004300 -0.991400
+vn -0.130500 0.004300 -0.991400
+vn -0.382700 0.012500 -0.923800
+vn -0.608600 0.019900 -0.793200
+vn -0.793100 0.026000 -0.608600
+vn -0.923500 0.030200 -0.382500
+vn -0.990900 0.032400 -0.130500
+vn -0.128800 -0.021300 0.991400
+vn 0.128800 0.021300 0.991400
+vn 0.814900 0.435500 0.382500
+vn 0.673900 0.419000 0.608600
+vn 0.673900 0.419000 -0.608600
+vn -0.723600 -0.677800 0.130500
+vn 0.723600 0.677700 0.130500
+vn 0.723600 0.677700 -0.130500
+vn -0.629000 -0.766400 0.130400
+vn -0.586200 -0.714200 0.382500
+vn 0.586200 0.714200 0.382500
+vn 0.586200 0.714200 -0.382500
+vn -0.586200 -0.714200 -0.382500
+vn -0.629000 -0.766400 -0.130400
+vn 0.419000 0.673900 0.608600
+vn 0.279500 0.742700 0.608600
+vn 0.279500 0.742700 -0.608600
+vn -0.032400 -0.990900 0.130400
+vn -0.032400 -0.990900 -0.130400
+vn 0.021300 -0.128900 0.991400
+vn 0.279500 -0.742600 0.608600
+vn -0.279500 0.742700 0.608600
+vn -0.279500 0.742700 -0.608600
+vn 0.279500 -0.742600 -0.608600
+vn -0.180500 0.337600 0.923800
+vn -0.180500 0.337600 -0.923800
+vn 0.419000 -0.673800 0.608600
+vn 0.419000 -0.673800 -0.608600
+vn 0.586200 -0.714200 0.382500
+vn 0.586200 -0.714200 -0.382500
+vn 0.677700 -0.723600 0.130500
+vn -0.677700 0.723600 -0.130500
+vn 0.714200 -0.586100 0.382500
+vn 0.673900 -0.419000 0.608600
+vn -0.673900 0.419000 0.608600
+vn 0.673900 -0.419000 -0.608600
+vn 0.337600 -0.180500 -0.923800
+vn 0.742600 -0.279500 0.608600
+vn -0.742700 0.279500 0.608600
+vn -0.742700 0.279500 -0.608600
+vn 0.742600 -0.279500 -0.608600
+vn -0.128800 -0.021300 -0.991400
+vn 0.128800 0.021300 -0.991400
+vn 0.337600 0.180500 0.923800
+vn 0.677700 0.723600 0.130500
+vn -0.677700 -0.723600 0.130500
+vn 0.419000 0.673800 -0.608600
+vn -0.180500 -0.337600 0.923800
+vn -0.180500 -0.337600 -0.923800
+vn 0.180500 0.337600 -0.923800
+vn -0.279500 -0.742600 0.608600
+vn -0.279500 -0.742600 -0.608600
+vn -0.180200 -0.772800 0.608500
+vn -0.032400 0.990900 0.130400
+vn -0.032400 0.990900 -0.130400
+vn -0.021300 0.128800 0.991400
+vn -0.419000 0.673800 -0.608600
+vn 0.723600 -0.677700 0.130500
+vn 0.723600 -0.677700 -0.130500
+vn -0.723600 0.677800 -0.130500
+vn 0.128800 -0.021300 -0.991400
+vn -0.128800 0.021300 -0.991400
+vn -0.742600 -0.279500 0.608600
+vn -0.742600 -0.279500 -0.608600
+vn -0.673800 -0.419000 0.608600
+vn -0.673800 -0.419000 -0.608600
+vn -0.419000 -0.673800 0.608600
+vn -0.419000 -0.673800 -0.608600
+vn -0.021300 -0.128800 -0.991400
+vn 0.021300 -0.128800 -0.991400
+vn -0.677700 0.723600 0.130500
+vn 0.677700 -0.723600 -0.130500
+vn 0.814900 -0.435600 0.382500
+vn -0.814900 0.435600 -0.382500
+vn 0.814900 -0.435600 -0.382500
+s off
+f 25//1 26//1 2//1
+f 26//2 27//2 3//2
+f 3//3 27//3 28//3
+f 28//4 29//4 5//4
+f 5//5 29//5 30//5
+f 30//6 31//6 7//6
+f 31//7 32//7 8//7
+f 32//8 33//8 9//8
+f 9//9 33//9 34//9
+f 34//10 35//10 11//10
+f 35//11 36//11 12//11
+f 36//12 37//12 13//12
+f 13//13 37//13 38//13
+f 14//14 38//14 39//14
+f 39//15 40//15 16//15
+f 40//16 41//16 17//16
+f 41//17 42//17 18//17
+f 18//18 42//18 43//18
+f 19//19 43//19 44//19
+f 44//20 45//20 21//20
+f 45//21 46//21 22//21
+f 46//22 47//22 23//22
+f 23//23 47//23 48//23
+f 24//24 48//24 25//24
+f 49//25 50//25 26//25
+f 26//26 50//26 51//26
+f 27//27 51//27 52//27
+f 28//28 52//28 53//28
+f 29//29 53//29 54//29
+f 54//30 55//30 31//30
+f 31//31 55//31 56//31
+f 32//32 56//32 57//32
+f 33//33 57//33 58//33
+f 34//34 58//34 59//34
+f 35//35 59//35 60//35
+f 36//36 60//36 61//36
+f 61//37 62//37 38//37
+f 62//38 63//38 39//38
+f 63//39 64//39 40//39
+f 64//40 65//40 41//40
+f 65//41 66//41 42//41
+f 66//42 67//42 43//42
+f 67//43 68//43 44//43
+f 68//44 69//44 45//44
+f 69//45 70//45 46//45
+f 70//46 71//46 47//46
+f 71//47 72//47 48//47
+f 48//48 72//48 49//48
+f 73//49 74//49 50//49
+f 50//50 74//50 75//50
+f 75//51 76//51 52//51
+f 76//52 77//52 53//52
+f 77//53 78//53 54//53
+f 54//54 78//54 79//54
+f 79//55 80//55 56//55
+f 80//56 81//56 57//56
+f 81//57 82//57 58//57
+f 82//58 83//58 59//58
+f 59//59 83//59 84//59
+f 60//60 84//60 85//60
+f 85//61 86//61 62//61
+f 86//62 87//62 63//62
+f 87//63 88//63 64//63
+f 64//64 88//64 89//64
+f 89//65 90//65 66//65
+f 90//66 91//66 67//66
+f 67//67 91//67 92//67
+f 68//68 92//68 93//68
+f 69//69 93//69 94//69
+f 70//70 94//70 95//70
+f 95//71 96//71 72//71
+f 96//72 73//72 49//72
+f 97//73 98//73 74//73
+f 98//74 99//74 75//74
+f 75//75 99//75 100//75
+f 76//76 100//76 101//76
+f 77//77 101//77 102//77
+f 78//78 102//78 103//78
+f 79//79 103//79 104//79
+f 80//80 104//80 105//80
+f 81//81 105//81 106//81
+f 106//82 107//82 83//82
+f 83//83 107//83 108//83
+f 108//84 109//84 85//84
+f 85//85 109//85 110//85
+f 110//86 111//86 87//86
+f 87//87 111//87 112//87
+f 112//88 113//88 89//88
+f 113//89 114//89 90//89
+f 114//90 115//90 91//90
+f 115//91 116//91 92//91
+f 116//92 117//92 93//92
+f 117//93 118//93 94//93
+f 118//94 119//94 95//94
+f 95//95 119//95 120//95
+f 120//96 97//96 73//96
+f 121//97 122//97 98//97
+f 98//98 122//98 123//98
+f 123//99 124//99 100//99
+f 124//100 125//100 101//100
+f 125//101 126//101 102//101
+f 126//102 127//102 103//102
+f 127//103 128//103 104//103
+f 128//104 129//104 105//104
+f 129//105 130//105 106//105
+f 130//106 131//106 107//106
+f 131//107 132//107 108//107
+f 132//108 133//108 109//108
+f 133//109 134//109 110//109
+f 110//110 134//110 135//110
+f 111//111 135//111 136//111
+f 112//112 136//112 137//112
+f 113//113 137//113 138//113
+f 114//114 138//114 139//114
+f 115//115 139//115 140//115
+f 116//116 140//116 141//116
+f 117//117 141//117 142//117
+f 118//118 142//118 143//118
+f 143//119 144//119 120//119
+f 120//120 144//120 121//120
+f 145//121 146//121 122//121
+f 122//122 146//122 147//122
+f 123//123 147//123 148//123
+f 124//124 148//124 149//124
+f 125//125 149//125 150//125
+f 150//126 151//126 127//126
+f 127//127 151//127 152//127
+f 152//128 153//128 129//128
+f 153//129 154//129 130//129
+f 130//130 154//130 155//130
+f 155//131 156//131 132//131
+f 132//132 156//132 157//132
+f 157//133 158//133 134//133
+f 158//134 159//134 135//134
+f 159//135 160//135 136//135
+f 160//136 161//136 137//136
+f 137//137 161//137 162//137
+f 162//138 163//138 139//138
+f 163//139 164//139 140//139
+f 164//140 165//140 141//140
+f 165//141 166//141 142//141
+f 166//142 167//142 143//142
+f 167//143 168//143 144//143
+f 168//144 145//144 121//144
+f 169//145 170//145 146//145
+f 170//146 171//146 147//146
+f 147//147 171//147 172//147
+f 172//148 173//148 149//148
+f 173//149 174//149 150//149
+f 174//150 175//150 151//150
+f 175//151 176//151 152//151
+f 176//152 177//152 153//152
+f 177//153 178//153 154//153
+f 178//154 179//154 155//154
+f 179//155 180//155 156//155
+f 180//156 181//156 157//156
+f 157//157 181//157 182//157
+f 158//158 182//158 183//158
+f 159//159 183//159 184//159
+f 160//160 184//160 185//160
+f 161//161 185//161 186//161
+f 162//162 186//162 187//162
+f 187//163 188//163 164//163
+f 164//164 188//164 189//164
+f 165//165 189//165 190//165
+f 190//166 191//166 167//166
+f 167//167 191//167 192//167
+f 168//168 192//168 169//168
+f 193//169 194//169 170//169
+f 170//170 194//170 195//170
+f 171//171 195//171 196//171
+f 172//172 196//172 197//172
+f 173//173 197//173 198//173
+f 174//174 198//174 199//174
+f 199//175 200//175 176//175
+f 176//176 200//176 201//176
+f 201//177 202//177 178//177
+f 178//178 202//178 203//178
+f 179//179 203//179 204//179
+f 204//180 205//180 181//180
+f 181//181 205//181 206//181
+f 206//182 207//182 183//182
+f 207//183 208//183 184//183
+f 208//184 209//184 185//184
+f 209//185 210//185 186//185
+f 210//186 211//186 187//186
+f 187//187 211//187 212//187
+f 212//188 213//188 189//188
+f 213//189 214//189 190//189
+f 214//190 215//190 191//190
+f 215//191 216//191 192//191
+f 216//192 193//192 169//192
+f 217//193 218//193 194//193
+f 218//194 219//194 195//194
+f 195//195 219//195 220//195
+f 220//196 221//196 197//196
+f 197//197 221//197 222//197
+f 198//198 222//198 223//198
+f 199//199 223//199 224//199
+f 224//200 225//200 201//200
+f 225//201 226//201 202//201
+f 226//202 227//202 203//202
+f 227//203 228//203 204//203
+f 204//204 228//204 229//204
+f 229//205 230//205 206//205
+f 206//206 230//206 231//206
+f 207//207 231//207 232//207
+f 208//208 232//208 233//208
+f 233//209 234//209 210//209
+f 234//210 235//210 211//210
+f 235//211 236//211 212//211
+f 236//212 237//212 213//212
+f 237//213 238//213 214//213
+f 238//214 239//214 215//214
+f 215//215 239//215 240//215
+f 216//216 240//216 217//216
+f 241//217 242//217 218//217
+f 242//218 243//218 219//218
+f 219//219 243//219 244//219
+f 244//220 245//220 221//220
+f 245//221 246//221 222//221
+f 246//222 247//222 223//222
+f 223//223 247//223 248//223
+f 224//224 248//224 249//224
+f 225//225 249//225 250//225
+f 226//226 250//226 251//226
+f 227//227 251//227 252//227
+f 252//228 253//228 229//228
+f 253//229 254//229 230//229
+f 230//230 254//230 255//230
+f 255//231 256//231 232//231
+f 256//232 257//232 233//232
+f 233//233 257//233 258//233
+f 258//234 259//234 235//234
+f 235//235 259//235 260//235
+f 236//236 260//236 261//236
+f 237//237 261//237 262//237
+f 262//238 263//238 239//238
+f 239//239 263//239 264//239
+f 264//240 241//240 217//240
+f 241//241 265//241 266//241
+f 266//242 267//242 243//242
+f 267//243 268//243 244//243
+f 268//244 269//244 245//244
+f 269//245 270//245 246//245
+f 246//246 270//246 271//246
+f 271//247 272//247 248//247
+f 272//248 273//248 249//248
+f 249//249 273//249 274//249
+f 274//250 275//250 251//250
+f 275//251 276//251 252//251
+f 276//252 277//252 253//252
+f 253//253 277//253 278//253
+f 278//254 279//254 255//254
+f 255//255 279//255 280//255
+f 280//256 281//256 257//256
+f 257//257 281//257 282//257
+f 258//258 282//258 283//258
+f 283//259 284//259 260//259
+f 260//260 284//260 285//260
+f 261//261 285//261 286//261
+f 286//262 287//262 263//262
+f 287//263 288//263 264//263
+f 288//264 265//264 241//264
+f 265//265 289//265 290//265
+f 290//266 291//266 267//266
+f 267//267 291//267 292//267
+f 292//268 293//268 269//268
+f 293//269 294//269 270//269
+f 294//270 295//270 271//270
+f 295//271 296//271 272//271
+f 296//272 297//272 273//272
+f 273//273 297//273 298//273
+f 298//274 299//274 275//274
+f 299//275 300//275 276//275
+f 300//276 301//276 277//276
+f 301//277 302//277 278//277
+f 278//278 302//278 303//278
+f 279//279 303//279 304//279
+f 304//280 305//280 281//280
+f 305//281 306//281 282//281
+f 282//282 306//282 307//282
+f 283//283 307//283 308//283
+f 308//284 309//284 285//284
+f 285//285 309//285 310//285
+f 310//286 311//286 287//286
+f 311//287 312//287 288//287
+f 312//288 289//288 265//288
+f 289//289 313//289 314//289
+f 314//290 315//290 291//290
+f 291//291 315//291 316//291
+f 292//292 316//292 317//292
+f 293//293 317//293 318//293
+f 294//294 318//294 319//294
+f 295//295 319//295 320//295
+f 320//296 321//296 297//296
+f 321//297 322//297 298//297
+f 322//298 323//298 299//298
+f 323//299 324//299 300//299
+f 324//300 325//300 301//300
+f 325//301 326//301 302//301
+f 302//302 326//302 327//302
+f 303//303 327//303 328//303
+f 328//304 329//304 305//304
+f 329//305 330//305 306//305
+f 330//306 331//306 307//306
+f 307//307 331//307 332//307
+f 332//308 333//308 309//308
+f 333//309 334//309 310//309
+f 334//310 335//310 311//310
+f 311//311 335//311 336//311
+f 336//312 313//312 289//312
+f 337//313 338//313 314//313
+f 314//314 338//314 339//314
+f 315//315 339//315 340//315
+f 340//316 341//316 317//316
+f 341//317 342//317 318//317
+f 318//318 342//318 343//318
+f 343//319 344//319 320//319
+f 344//320 345//320 321//320
+f 321//321 345//321 346//321
+f 346//322 347//322 323//322
+f 323//323 347//323 348//323
+f 348//324 349//324 325//324
+f 325//325 349//325 350//325
+f 350//326 351//326 327//326
+f 327//327 351//327 352//327
+f 352//328 353//328 329//328
+f 353//329 354//329 330//329
+f 330//330 354//330 355//330
+f 355//331 356//331 332//331
+f 332//332 356//332 357//332
+f 333//333 357//333 358//333
+f 358//334 359//334 335//334
+f 359//335 360//335 336//335
+f 336//336 360//336 337//336
+f 337//337 361//337 362//337
+f 362//338 363//338 339//338
+f 339//339 363//339 364//339
+f 364//340 365//340 341//340
+f 365//341 366//341 342//341
+f 342//342 366//342 367//342
+f 367//343 368//343 344//343
+f 368//344 369//344 345//344
+f 345//345 369//345 370//345
+f 346//346 370//346 371//346
+f 347//347 371//347 372//347
+f 372//348 373//348 349//348
+f 373//349 374//349 350//349
+f 374//350 375//350 351//350
+f 375//351 376//351 352//351
+f 376//352 377//352 353//352
+f 377//353 378//353 354//353
+f 378//354 379//354 355//354
+f 379//355 380//355 356//355
+f 380//356 381//356 357//356
+f 381//357 382//357 358//357
+f 382//358 383//358 359//358
+f 359//359 383//359 384//359
+f 360//360 384//360 361//360
+f 385//361 386//361 362//361
+f 386//362 387//362 363//362
+f 363//363 387//363 388//363
+f 388//364 389//364 365//364
+f 365//365 389//365 390//365
+f 390//366 391//366 367//366
+f 391//367 392//367 368//367
+f 392//368 393//368 369//368
+f 393//369 394//369 370//369
+f 394//370 395//370 371//370
+f 371//371 395//371 396//371
+f 396//372 397//372 373//372
+f 373//373 397//373 398//373
+f 398//374 399//374 375//374
+f 375//375 399//375 400//375
+f 400//376 401//376 377//376
+f 377//377 401//377 402//377
+f 378//378 402//378 403//378
+f 379//379 403//379 404//379
+f 404//380 405//380 381//380
+f 381//381 405//381 406//381
+f 382//382 406//382 407//382
+f 383//383 407//383 408//383
+f 384//384 408//384 385//384
+f 385//385 409//385 410//385
+f 410//386 411//386 387//386
+f 387//387 411//387 412//387
+f 388//388 412//388 413//388
+f 413//389 414//389 390//389
+f 390//390 414//390 415//390
+f 391//391 415//391 416//391
+f 416//392 417//392 393//392
+f 393//393 417//393 418//393
+f 418//394 419//394 395//394
+f 395//395 419//395 420//395
+f 420//396 421//396 397//396
+f 421//397 422//397 398//397
+f 422//398 423//398 399//398
+f 399//399 423//399 424//399
+f 424//400 425//400 401//400
+f 401//401 425//401 426//401
+f 426//402 427//402 403//402
+f 427//403 428//403 404//403
+f 428//404 429//404 405//404
+f 429//405 430//405 406//405
+f 430//406 431//406 407//406
+f 407//407 431//407 432//407
+f 432//408 409//408 385//408
+f 409//409 433//409 434//409
+f 434//410 435//410 411//410
+f 411//411 435//411 436//411
+f 436//412 437//412 413//412
+f 437//413 438//413 414//413
+f 438//414 439//414 415//414
+f 439//415 440//415 416//415
+f 440//416 441//416 417//416
+f 417//417 441//417 442//417
+f 442//418 443//418 419//418
+f 443//419 444//419 420//419
+f 444//420 445//420 421//420
+f 445//421 446//421 422//421
+f 422//422 446//422 447//422
+f 423//423 447//423 448//423
+f 448//424 449//424 425//424
+f 425//425 449//425 450//425
+f 426//426 450//426 451//426
+f 427//427 451//427 452//427
+f 428//428 452//428 453//428
+f 429//429 453//429 454//429
+f 454//430 455//430 431//430
+f 431//431 455//431 456//431
+f 456//432 433//432 409//432
+f 457//433 458//433 434//433
+f 458//434 459//434 435//434
+f 435//435 459//435 460//435
+f 460//436 461//436 437//436
+f 437//437 461//437 462//437
+f 462//438 463//438 439//438
+f 439//439 463//439 464//439
+f 440//440 464//440 465//440
+f 441//441 465//441 466//441
+f 442//442 466//442 467//442
+f 467//443 468//443 444//443
+f 444//444 468//444 469//444
+f 469//445 470//445 446//445
+f 470//446 471//446 447//446
+f 471//447 472//447 448//447
+f 472//448 473//448 449//448
+f 473//449 474//449 450//449
+f 474//450 475//450 451//450
+f 451//451 475//451 476//451
+f 476//452 477//452 453//452
+f 453//453 477//453 478//453
+f 478//454 479//454 455//454
+f 479//455 480//455 456//455
+f 480//456 457//456 433//456
+f 481//457 482//457 458//457
+f 482//458 483//458 459//458
+f 483//459 484//459 460//459
+f 484//460 485//460 461//460
+f 461//461 485//461 486//461
+f 486//462 487//462 463//462
+f 487//463 488//463 464//463
+f 464//464 488//464 489//464
+f 489//465 490//465 466//465
+f 490//466 491//466 467//466
+f 491//467 492//467 468//467
+f 468//468 492//468 493//468
+f 493//469 494//469 470//469
+f 494//470 495//470 471//470
+f 471//471 495//471 496//471
+f 472//472 496//472 497//472
+f 497//473 498//473 474//473
+f 498//474 499//474 475//474
+f 475//475 499//475 500//475
+f 500//476 501//476 477//476
+f 477//477 501//477 502//477
+f 502//478 503//478 479//478
+f 479//479 503//479 504//479
+f 504//480 481//480 457//480
+f 481//481 505//481 506//481
+f 482//482 506//482 507//482
+f 507//483 508//483 484//483
+f 508//484 509//484 485//484
+f 509//485 510//485 486//485
+f 510//486 511//486 487//486
+f 487//487 511//487 512//487
+f 512//488 513//488 489//488
+f 513//489 514//489 490//489
+f 514//490 515//490 491//490
+f 515//491 516//491 492//491
+f 492//492 516//492 517//492
+f 517//493 518//493 494//493
+f 494//494 518//494 519//494
+f 519//495 520//495 496//495
+f 496//496 520//496 521//496
+f 521//497 522//497 498//497
+f 522//498 523//498 499//498
+f 499//499 523//499 524//499
+f 500//500 524//500 525//500
+f 501//501 525//501 526//501
+f 502//502 526//502 527//502
+f 503//503 527//503 528//503
+f 528//504 505//504 481//504
+f 505//505 529//505 530//505
+f 530//506 531//506 507//506
+f 531//507 532//507 508//507
+f 508//508 532//508 533//508
+f 533//509 534//509 510//509
+f 534//510 535//510 511//510
+f 511//511 535//511 536//511
+f 536//512 537//512 513//512
+f 537//513 538//513 514//513
+f 514//514 538//514 539//514
+f 539//515 540//515 516//515
+f 516//516 540//516 541//516
+f 541//517 542//517 518//517
+f 542//518 543//518 519//518
+f 543//519 544//519 520//519
+f 520//520 544//520 545//520
+f 521//521 545//521 546//521
+f 546//522 547//522 523//522
+f 547//523 548//523 524//523
+f 548//524 549//524 525//524
+f 549//525 550//525 526//525
+f 550//526 551//526 527//526
+f 527//527 551//527 552//527
+f 552//528 529//528 505//528
+f 529//529 553//529 554//529
+f 530//530 554//530 555//530
+f 531//531 555//531 556//531
+f 556//532 557//532 533//532
+f 557//533 558//533 534//533
+f 534//534 558//534 559//534
+f 535//535 559//535 560//535
+f 560//536 561//536 537//536
+f 561//537 562//537 538//537
+f 538//538 562//538 563//538
+f 539//539 563//539 564//539
+f 564//540 565//540 541//540
+f 565//541 566//541 542//541
+f 566//542 567//542 543//542
+f 567//543 568//543 544//543
+f 544//544 568//544 569//544
+f 569//545 570//545 546//545
+f 570//546 571//546 547//546
+f 547//547 571//547 572//547
+f 572//548 573//548 549//548
+f 549//549 573//549 574//549
+f 574//550 575//550 551//550
+f 575//551 576//551 552//551
+f 576//552 553//552 529//552
+f 577//553 578//553 554//553
+f 554//554 578//554 579//554
+f 579//555 580//555 556//555
+f 556//556 580//556 581//556
+f 581//557 582//557 558//557
+f 558//558 582//558 583//558
+f 559//559 583//559 584//559
+f 560//560 584//560 585//560
+f 561//561 585//561 586//561
+f 586//562 587//562 563//562
+f 563//563 587//563 588//563
+f 588//564 589//564 565//564
+f 565//565 589//565 590//565
+f 590//566 591//566 567//566
+f 567//567 591//567 592//567
+f 592//568 593//568 569//568
+f 593//569 594//569 570//569
+f 594//570 595//570 571//570
+f 595//571 596//571 572//571
+f 596//572 597//572 573//572
+f 597//573 598//573 574//573
+f 574//574 598//574 599//574
+f 599//575 600//575 576//575
+f 600//576 577//576 553//576
+f 577//577 601//577 602//577
+f 578//578 602//578 603//578
+f 603//579 604//579 580//579
+f 604//580 605//580 581//580
+f 605//581 606//581 582//581
+f 582//582 606//582 607//582
+f 607//583 608//583 584//583
+f 608//584 609//584 585//584
+f 585//585 609//585 610//585
+f 610//586 611//586 587//586
+f 587//587 611//587 612//587
+f 612//588 613//588 589//588
+f 589//589 613//589 614//589
+f 614//590 615//590 591//590
+f 615//591 616//591 592//591
+f 616//592 617//592 593//592
+f 593//593 617//593 618//593
+f 594//594 618//594 619//594
+f 619//595 620//595 596//595
+f 596//596 620//596 621//596
+f 597//597 621//597 622//597
+f 598//598 622//598 623//598
+f 623//599 624//599 600//599
+f 624//600 601//600 577//600
+f 601//601 625//601 626//601
+f 626//602 627//602 603//602
+f 603//603 627//603 628//603
+f 604//604 628//604 629//604
+f 629//605 630//605 606//605
+f 606//606 630//606 631//606
+f 607//607 631//607 632//607
+f 632//608 633//608 609//608
+f 633//609 634//609 610//609
+f 634//610 635//610 611//610
+f 635//611 636//611 612//611
+f 636//612 637//612 613//612
+f 637//613 638//613 614//613
+f 614//614 638//614 639//614
+f 615//615 639//615 640//615
+f 640//616 641//616 617//616
+f 641//617 642//617 618//617
+f 642//618 643//618 619//618
+f 643//619 644//619 620//619
+f 620//620 644//620 645//620
+f 645//621 646//621 622//621
+f 646//622 647//622 623//622
+f 623//623 647//623 648//623
+f 648//624 625//624 601//624
+f 649//625 650//625 626//625
+f 650//626 651//626 627//626
+f 651//627 652//627 628//627
+f 628//628 652//628 653//628
+f 653//629 654//629 630//629
+f 654//630 655//630 631//630
+f 655//631 656//631 632//631
+f 656//632 657//632 633//632
+f 633//633 657//633 658//633
+f 658//634 659//634 635//634
+f 635//635 659//635 660//635
+f 636//636 660//636 661//636
+f 661//637 662//637 638//637
+f 662//638 663//638 639//638
+f 663//639 664//639 640//639
+f 664//640 665//640 641//640
+f 665//641 666//641 642//641
+f 666//642 667//642 643//642
+f 643//643 667//643 668//643
+f 644//644 668//644 669//644
+f 669//645 670//645 646//645
+f 646//646 670//646 671//646
+f 647//647 671//647 672//647
+f 648//648 672//648 649//648
+f 649//649 673//649 674//649
+f 650//650 674//650 675//650
+f 675//651 676//651 652//651
+f 652//652 676//652 677//652
+f 653//653 677//653 678//653
+f 654//654 678//654 679//654
+f 655//655 679//655 680//655
+f 680//656 681//656 657//656
+f 657//657 681//657 682//657
+f 682//658 683//658 659//658
+f 659//659 683//659 684//659
+f 684//660 685//660 661//660
+f 685//661 686//661 662//661
+f 686//662 687//662 663//662
+f 663//663 687//663 688//663
+f 688//664 689//664 665//664
+f 689//665 690//665 666//665
+f 690//666 691//666 667//666
+f 691//667 692//667 668//667
+f 692//668 693//668 669//668
+f 669//669 693//669 694//669
+f 694//670 695//670 671//670
+f 695//671 696//671 672//671
+f 696//672 673//672 649//672
+f 697//673 698//673 674//673
+f 674//674 698//674 699//674
+f 699//675 700//675 676//675
+f 700//676 701//676 677//676
+f 701//677 702//677 678//677
+f 702//678 703//678 679//678
+f 679//679 703//679 704//679
+f 704//680 705//680 681//680
+f 705//681 706//681 682//681
+f 706//682 707//682 683//682
+f 707//683 708//683 684//683
+f 684//684 708//684 709//684
+f 709//685 710//685 686//685
+f 686//686 710//686 711//686
+f 687//687 711//687 712//687
+f 688//688 712//688 713//688
+f 689//689 713//689 714//689
+f 714//690 715//690 691//690
+f 691//691 715//691 716//691
+f 692//692 716//692 717//692
+f 693//693 717//693 718//693
+f 694//694 718//694 719//694
+f 719//695 720//695 696//695
+f 696//696 720//696 697//696
+f 697//697 721//697 722//697
+f 698//698 722//698 723//698
+f 723//699 724//699 700//699
+f 700//700 724//700 725//700
+f 701//701 725//701 726//701
+f 726//702 727//702 703//702
+f 727//703 728//703 704//703
+f 728//704 729//704 705//704
+f 729//705 730//705 706//705
+f 706//706 730//706 731//706
+f 731//707 732//707 708//707
+f 708//708 732//708 733//708
+f 733//709 734//709 710//709
+f 734//710 735//710 711//710
+f 735//711 736//711 712//711
+f 712//712 736//712 737//712
+f 737//713 738//713 714//713
+f 714//714 738//714 739//714
+f 739//715 740//715 716//715
+f 716//716 740//716 741//716
+f 741//717 742//717 718//717
+f 718//718 742//718 743//718
+f 743//719 744//719 720//719
+f 744//720 721//720 697//720
+f 721//721 745//721 746//721
+f 722//722 746//722 747//722
+f 747//723 748//723 724//723
+f 748//724 749//724 725//724
+f 749//725 750//725 726//725
+f 726//726 750//726 751//726
+f 751//727 752//727 728//727
+f 728//728 752//728 753//728
+f 753//729 754//729 730//729
+f 730//730 754//730 755//730
+f 755//731 756//731 732//731
+f 756//732 757//732 733//732
+f 733//733 757//733 758//733
+f 734//734 758//734 759//734
+f 759//735 760//735 736//735
+f 760//736 761//736 737//736
+f 761//737 762//737 738//737
+f 738//738 762//738 763//738
+f 763//739 764//739 740//739
+f 740//740 764//740 765//740
+f 741//741 765//741 766//741
+f 742//742 766//742 767//742
+f 767//743 768//743 744//743
+f 768//744 745//744 721//744
+f 745//745 769//745 770//745
+f 746//746 770//746 771//746
+f 747//747 771//747 772//747
+f 772//748 773//748 749//748
+f 749//749 773//749 774//749
+f 774//750 775//750 751//750
+f 751//751 775//751 776//751
+f 752//752 776//752 777//752
+f 753//753 777//753 778//753
+f 754//754 778//754 779//754
+f 779//755 780//755 756//755
+f 780//756 781//756 757//756
+f 757//757 781//757 782//757
+f 758//758 782//758 783//758
+f 783//759 784//759 760//759
+f 784//760 785//760 761//760
+f 785//761 786//761 762//761
+f 786//762 787//762 763//762
+f 787//763 788//763 764//763
+f 788//764 789//764 765//764
+f 765//765 789//765 790//765
+f 790//766 791//766 767//766
+f 791//767 792//767 768//767
+f 792//768 769//768 745//768
+f 769//769 793//769 794//769
+f 794//770 795//770 771//770
+f 795//771 796//771 772//771
+f 796//772 797//772 773//772
+f 797//773 798//773 774//773
+f 798//774 799//774 775//774
+f 799//775 800//775 776//775
+f 800//776 801//776 777//776
+f 801//777 802//777 778//777
+f 802//778 803//778 779//778
+f 803//779 804//779 780//779
+f 804//780 805//780 781//780
+f 805//781 806//781 782//781
+f 782//782 806//782 807//782
+f 783//783 807//783 808//783
+f 808//784 809//784 785//784
+f 785//785 809//785 810//785
+f 786//786 810//786 811//786
+f 787//787 811//787 812//787
+f 812//788 813//788 789//788
+f 789//789 813//789 814//789
+f 814//790 815//790 791//790
+f 791//791 815//791 816//791
+f 816//792 793//792 769//792
+f 793//793 817//793 818//793
+f 818//794 819//794 795//794
+f 795//795 819//795 820//795
+f 796//796 820//796 821//796
+f 797//797 821//797 822//797
+f 798//798 822//798 823//798
+f 799//799 823//799 824//799
+f 800//800 824//800 825//800
+f 825//801 826//801 802//801
+f 802//802 826//802 827//802
+f 827//803 828//803 804//803
+f 804//804 828//804 829//804
+f 829//805 830//805 806//805
+f 830//806 831//806 807//806
+f 831//807 832//807 808//807
+f 832//808 833//808 809//808
+f 833//809 834//809 810//809
+f 834//810 835//810 811//810
+f 835//811 836//811 812//811
+f 836//812 837//812 813//812
+f 837//813 838//813 814//813
+f 838//814 839//814 815//814
+f 815//815 839//815 840//815
+f 840//816 817//816 793//816
+f 841//817 842//817 818//817
+f 842//818 843//818 819//818
+f 819//819 843//819 844//819
+f 844//820 845//820 821//820
+f 821//821 845//821 846//821
+f 846//822 847//822 823//822
+f 847//823 848//823 824//823
+f 824//824 848//824 849//824
+f 849//825 850//825 826//825
+f 850//826 851//826 827//826
+f 851//827 852//827 828//827
+f 828//828 852//828 853//828
+f 853//829 854//829 830//829
+f 830//830 854//830 855//830
+f 831//831 855//831 856//831
+f 832//832 856//832 857//832
+f 857//833 858//833 834//833
+f 834//834 858//834 859//834
+f 859//835 860//835 836//835
+f 860//836 861//836 837//836
+f 861//837 862//837 838//837
+f 862//838 863//838 839//838
+f 863//839 864//839 840//839
+f 840//840 864//840 841//840
+f 865//841 866//841 842//841
+f 842//842 866//842 867//842
+f 867//843 868//843 844//843
+f 868//844 869//844 845//844
+f 845//845 869//845 870//845
+f 846//846 870//846 871//846
+f 871//847 872//847 848//847
+f 848//848 872//848 873//848
+f 849//849 873//849 874//849
+f 874//850 875//850 851//850
+f 875//851 876//851 852//851
+f 852//852 876//852 877//852
+f 877//853 878//853 854//853
+f 854//854 878//854 879//854
+f 855//855 879//855 880//855
+f 856//856 880//856 881//856
+f 881//857 882//857 858//857
+f 858//858 882//858 883//858
+f 859//859 883//859 884//859
+f 884//860 885//860 861//860
+f 885//861 886//861 862//861
+f 862//862 886//862 887//862
+f 887//863 888//863 864//863
+f 864//864 888//864 865//864
+f 889//865 890//865 866//865
+f 866//866 890//866 891//866
+f 891//867 892//867 868//867
+f 868//868 892//868 893//868
+f 893//869 894//869 870//869
+f 894//870 895//870 871//870
+f 895//871 896//871 872//871
+f 896//872 897//872 873//872
+f 873//873 897//873 898//873
+f 874//874 898//874 899//874
+f 875//875 899//875 900//875
+f 876//876 900//876 901//876
+f 901//877 902//877 878//877
+f 878//878 902//878 903//878
+f 903//879 904//879 880//879
+f 880//880 904//880 905//880
+f 905//881 906//881 882//881
+f 882//882 906//882 907//882
+f 883//883 907//883 908//883
+f 908//884 909//884 885//884
+f 909//885 910//885 886//885
+f 886//886 910//886 911//886
+f 911//887 912//887 888//887
+f 888//888 912//888 889//888
+f 913//889 914//889 890//889
+f 914//890 915//890 891//890
+f 915//891 916//891 892//891
+f 892//892 916//892 917//892
+f 917//893 918//893 894//893
+f 918//894 919//894 895//894
+f 919//895 920//895 896//895
+f 896//896 920//896 921//896
+f 921//897 922//897 898//897
+f 898//898 922//898 923//898
+f 923//899 924//899 900//899
+f 924//900 925//900 901//900
+f 925//901 926//901 902//901
+f 902//902 926//902 927//902
+f 927//903 928//903 904//903
+f 904//904 928//904 929//904
+f 929//905 930//905 906//905
+f 930//906 931//906 907//906
+f 907//907 931//907 932//907
+f 908//908 932//908 933//908
+f 933//909 934//909 910//909
+f 910//910 934//910 935//910
+f 935//911 936//911 912//911
+f 912//912 936//912 913//912
+f 913//913 937//913 938//913
+f 914//914 938//914 939//914
+f 939//915 940//915 916//915
+f 940//916 941//916 917//916
+f 917//917 941//917 942//917
+f 942//918 943//918 919//918
+f 943//919 944//919 920//919
+f 944//920 945//920 921//920
+f 945//921 946//921 922//921
+f 946//922 947//922 923//922
+f 947//923 948//923 924//923
+f 924//924 948//924 949//924
+f 925//925 949//925 950//925
+f 950//926 951//926 927//926
+f 927//927 951//927 952//927
+f 952//928 953//928 929//928
+f 929//929 953//929 954//929
+f 954//930 955//930 931//930
+f 931//931 955//931 956//931
+f 956//932 957//932 933//932
+f 957//933 958//933 934//933
+f 934//934 958//934 959//934
+f 959//935 960//935 936//935
+f 960//936 937//936 913//936
+f 937//937 961//937 962//937
+f 938//938 962//938 963//938
+f 939//939 963//939 964//939
+f 940//940 964//940 965//940
+f 941//941 965//941 966//941
+f 942//942 966//942 967//942
+f 943//943 967//943 968//943
+f 944//944 968//944 969//944
+f 945//945 969//945 970//945
+f 946//946 970//946 971//946
+f 947//947 971//947 972//947
+f 972//948 973//948 949//948
+f 949//949 973//949 974//949
+f 974//950 975//950 951//950
+f 975//951 976//951 952//951
+f 976//952 977//952 953//952
+f 977//953 978//953 954//953
+f 978//954 979//954 955//954
+f 979//955 980//955 956//955
+f 980//956 981//956 957//956
+f 981//957 982//957 958//957
+f 958//958 982//958 983//958
+f 983//959 984//959 960//959
+f 984//960 961//960 937//960
+f 961//961 985//961 986//961
+f 962//962 986//962 987//962
+f 963//963 987//963 988//963
+f 988//964 989//964 965//964
+f 989//965 990//965 966//965
+f 966//966 990//966 991//966
+f 991//967 992//967 968//967
+f 992//968 993//968 969//968
+f 993//969 994//969 970//969
+f 994//970 995//970 971//970
+f 971//971 995//971 996//971
+f 996//972 997//972 973//972
+f 973//973 997//973 998//973
+f 998//974 999//974 975//974
+f 999//975 1000//975 976//975
+f 1000//976 1001//976 977//976
+f 977//977 1001//977 1002//977
+f 1002//978 1003//978 979//978
+f 1003//979 1004//979 980//979
+f 980//980 1004//980 1005//980
+f 1005//981 1006//981 982//981
+f 1006//982 1007//982 983//982
+f 1007//983 1008//983 984//983
+f 1008//984 985//984 961//984
+f 1009//985 1010//985 986//985
+f 1010//986 1011//986 987//986
+f 1011//987 1012//987 988//987
+f 1012//988 1013//988 989//988
+f 1013//989 1014//989 990//989
+f 1014//990 1015//990 991//990
+f 1015//991 1016//991 992//991
+f 1016//992 1017//992 993//992
+f 1017//993 1018//993 994//993
+f 1018//994 1019//994 995//994
+f 1019//995 1020//995 996//995
+f 1020//996 1021//996 997//996
+f 1021//997 1022//997 998//997
+f 998//998 1022//998 1023//998
+f 1023//999 1024//999 1000//999
+f 1024//1000 1025//1000 1001//1000
+f 1001//1001 1025//1001 1026//1001
+f 1002//1002 1026//1002 1027//1002
+f 1003//1003 1027//1003 1028//1003
+f 1004//1004 1028//1004 1029//1004
+f 1029//1005 1030//1005 1006//1005
+f 1006//1006 1030//1006 1031//1006
+f 1007//1007 1031//1007 1032//1007
+f 1032//1008 1009//1008 985//1008
+f 1009//1009 1033//1009 1034//1009
+f 1010//1010 1034//1010 1035//1010
+f 1011//1011 1035//1011 1036//1011
+f 1036//1012 1037//1012 1013//1012
+f 1013//1013 1037//1013 1038//1013
+f 1014//1014 1038//1014 1039//1014
+f 1015//1015 1039//1015 1040//1015
+f 1016//1016 1040//1016 1041//1016
+f 1017//1017 1041//1017 1042//1017
+f 1018//1018 1042//1018 1043//1018
+f 1043//1019 1044//1019 1020//1019
+f 1020//1020 1044//1020 1045//1020
+f 1045//1021 1046//1021 1022//1021
+f 1046//1022 1047//1022 1023//1022
+f 1047//1023 1048//1023 1024//1023
+f 1048//1024 1049//1024 1025//1024
+f 1049//1025 1050//1025 1026//1025
+f 1050//1026 1051//1026 1027//1026
+f 1051//1027 1052//1027 1028//1027
+f 1052//1028 1053//1028 1029//1028
+f 1029//1029 1053//1029 1054//1029
+f 1054//1030 1055//1030 1031//1030
+f 1055//1031 1056//1031 1032//1031
+f 1056//1032 1033//1032 1009//1032
+f 1033//1033 1057//1033 1058//1033
+f 1058//1034 1059//1034 1035//1034
+f 1059//1035 1060//1035 1036//1035
+f 1036//1036 1060//1036 1061//1036
+f 1037//1037 1061//1037 1062//1037
+f 1062//1038 1063//1038 1039//1038
+f 1063//1039 1064//1039 1040//1039
+f 1040//1040 1064//1040 1065//1040
+f 1041//1041 1065//1041 1066//1041
+f 1042//1042 1066//1042 1067//1042
+f 1067//1043 1068//1043 1044//1043
+f 1068//1044 1069//1044 1045//1044
+f 1069//1045 1070//1045 1046//1045
+f 1046//1046 1070//1046 1071//1046
+f 1071//1047 1072//1047 1048//1047
+f 1072//1048 1073//1048 1049//1048
+f 1073//1049 1074//1049 1050//1049
+f 1050//1050 1074//1050 1075//1050
+f 1051//1051 1075//1051 1076//1051
+f 1076//1052 1077//1052 1053//1052
+f 1077//1053 1078//1053 1054//1053
+f 1078//1054 1079//1054 1055//1054
+f 1055//1055 1079//1055 1080//1055
+f 1080//1056 1057//1056 1033//1056
+f 1081//1057 1082//1057 1058//1057
+f 1058//1058 1082//1058 1083//1058
+f 1083//1059 1084//1059 1060//1059
+f 1084//1060 1085//1060 1061//1060
+f 1085//1061 1086//1061 1062//1061
+f 1086//1062 1087//1062 1063//1062
+f 1087//1063 1088//1063 1064//1063
+f 1088//1064 1089//1064 1065//1064
+f 1089//1065 1090//1065 1066//1065
+f 1066//1066 1090//1066 1091//1066
+f 1091//1067 1092//1067 1068//1067
+f 1092//1068 1093//1068 1069//1068
+f 1093//1069 1094//1069 1070//1069
+f 1070//1070 1094//1070 1095//1070
+f 1095//1071 1096//1071 1072//1071
+f 1072//1072 1096//1072 1097//1072
+f 1073//1073 1097//1073 1098//1073
+f 1074//1074 1098//1074 1099//1074
+f 1075//1075 1099//1075 1100//1075
+f 1076//1076 1100//1076 1101//1076
+f 1077//1077 1101//1077 1102//1077
+f 1078//1078 1102//1078 1103//1078
+f 1103//1079 1104//1079 1080//1079
+f 1080//1080 1104//1080 1081//1080
+f 1105//1081 1106//1081 1082//1081
+f 1106//1082 1107//1082 1083//1082
+f 1083//1083 1107//1083 1108//1083
+f 1084//1084 1108//1084 1109//1084
+f 1085//1085 1109//1085 1110//1085
+f 1086//1086 1110//1086 1111//1086
+f 1111//1087 1112//1087 1088//1087
+f 1088//1088 1112//1088 1113//1088
+f 1089//1089 1113//1089 1114//1089
+f 1090//1090 1114//1090 1115//1090
+f 1115//1091 1116//1091 1092//1091
+f 1116//1092 1117//1092 1093//1092
+f 1117//1093 1118//1093 1094//1093
+f 1118//1094 1119//1094 1095//1094
+f 1119//1095 1120//1095 1096//1095
+f 1120//1096 1121//1096 1097//1096
+f 1121//1097 1122//1097 1098//1097
+f 1122//1098 1123//1098 1099//1098
+f 1123//1099 1124//1099 1100//1099
+f 1124//1100 1125//1100 1101//1100
+f 1125//1101 1126//1101 1102//1101
+f 1126//1102 1127//1102 1103//1102
+f 1127//1103 1128//1103 1104//1103
+f 1104//1104 1128//1104 1105//1104
+f 1105//1105 1129//1105 1130//1105
+f 1130//1106 1131//1106 1107//1106
+f 1131//1107 1132//1107 1108//1107
+f 1132//1108 1133//1108 1109//1108
+f 1133//1109 1134//1109 1110//1109
+f 1134//1110 1135//1110 1111//1110
+f 1135//1111 1136//1111 1112//1111
+f 1136//1112 1137//1112 1113//1112
+f 1137//1113 1138//1113 1114//1113
+f 1138//1114 1139//1114 1115//1114
+f 1139//1115 1140//1115 1116//1115
+f 1140//1116 1141//1116 1117//1116
+f 1141//1117 1142//1117 1118//1117
+f 1118//1118 1142//1118 1143//1118
+f 1119//1119 1143//1119 1144//1119
+f 1120//1120 1144//1120 1145//1120
+f 1121//1121 1145//1121 1146//1121
+f 1122//1122 1146//1122 1147//1122
+f 1123//1123 1147//1123 1148//1123
+f 1148//1124 1149//1124 1125//1124
+f 1125//1125 1149//1125 1150//1125
+f 1126//1126 1150//1126 1151//1126
+f 1151//1127 1152//1127 1128//1127
+f 1152//1128 1129//1128 1105//1128
+f 1153//1129 1154//1129 1130//1129
+f 1130//1130 1154//1130 1155//1130
+f 1155//1131 1156//1131 1132//1131
+f 1156//1132 1157//1132 1133//1132
+f 1157//1133 1158//1133 1134//1133
+f 1158//1134 1159//1134 1135//1134
+f 1135//1135 1159//1135 1160//1135
+f 1160//1136 1161//1136 1137//1136
+f 1161//1137 1162//1137 1138//1137
+f 1162//1138 1163//1138 1139//1138
+f 1139//1139 1163//1139 1164//1139
+f 1164//1140 1165//1140 1141//1140
+f 1165//1141 1166//1141 1142//1141
+f 1166//1142 1167//1142 1143//1142
+f 1143//1143 1167//1143 1168//1143
+f 1168//1144 1169//1144 1145//1144
+f 1145//1145 1169//1145 1170//1145
+f 1170//1146 1171//1146 1147//1146
+f 1147//1147 1171//1147 1172//1147
+f 1148//1148 1172//1148 1173//1148
+f 1149//1149 1173//1149 1174//1149
+f 1150//1150 1174//1150 1175//1150
+f 1175//1151 1176//1151 1152//1151
+f 1176//1152 1153//1152 1129//1152
+f 1153//12 1177//12 1178//12
+f 1154//11 1178//11 1179//11
+f 1155//10 1179//10 1180//10
+f 1180//9 1181//9 1157//9
+f 1181//8 1182//8 1158//8
+f 1182//7 1183//7 1159//7
+f 1183//6 1184//6 1160//6
+f 1160//5 1184//5 1185//5
+f 1161//4 1185//4 1186//4
+f 1186//3 1187//3 1163//3
+f 1187//2 1188//2 1164//2
+f 1188//1 1189//1 1165//1
+f 1165//24 1189//24 1190//24
+f 1166//23 1190//23 1191//23
+f 1191//22 1192//22 1168//22
+f 1192//21 1193//21 1169//21
+f 1193//20 1194//20 1170//20
+f 1170//19 1194//19 1195//19
+f 1171//18 1195//18 1196//18
+f 1172//17 1196//17 1197//17
+f 1197//16 1198//16 1174//16
+f 1198//15 1199//15 1175//15
+f 1199//14 1200//14 1176//14
+f 1200//13 1177//13 1153//13
+f 1201//36 1202//36 1178//36
+f 1202//35 1203//35 1179//35
+f 1179//34 1203//34 1204//34
+f 1180//33 1204//33 1205//33
+f 1181//32 1205//32 1206//32
+f 1182//31 1206//31 1207//31
+f 1207//30 1208//30 1184//30
+f 1184//29 1208//29 1209//29
+f 1185//28 1209//28 1210//28
+f 1186//27 1210//27 1211//27
+f 1187//26 1211//26 1212//26
+f 1188//25 1212//25 1213//25
+f 1213//48 1214//48 1190//48
+f 1214//47 1215//47 1191//47
+f 1215//46 1216//46 1192//46
+f 1216//45 1217//45 1193//45
+f 1217//44 1218//44 1194//44
+f 1218//43 1219//43 1195//43
+f 1219//42 1220//42 1196//42
+f 1220//41 1221//41 1197//41
+f 1221//40 1222//40 1198//40
+f 1222//39 1223//39 1199//39
+f 1223//38 1224//38 1200//38
+f 1224//37 1201//37 1177//37
+f 1225//60 1226//60 1202//60
+f 1226//59 1227//59 1203//59
+f 1227//58 1228//58 1204//58
+f 1228//57 1229//57 1205//57
+f 1229//56 1230//56 1206//56
+f 1230//1153 1231//1153 1207//1153
+f 1231//1154 1232//1154 1208//1154
+f 1232//53 1233//53 1209//53
+f 1233//52 1234//52 1210//52
+f 1234//51 1235//51 1211//51
+f 1235//50 1236//50 1212//50
+f 1236//49 1237//49 1213//49
+f 1237//72 1238//72 1214//72
+f 1238//71 1239//71 1215//71
+f 1239//70 1240//70 1216//70
+f 1216//69 1240//69 1241//69
+f 1217//68 1241//68 1242//68
+f 1242//67 1243//67 1219//67
+f 1219//66 1243//66 1244//66
+f 1220//65 1244//65 1245//65
+f 1221//64 1245//64 1246//64
+f 1222//63 1246//63 1247//63
+f 1247//62 1248//62 1224//62
+f 1248//61 1225//61 1201//61
+f 1225//84 1249//84 1250//84
+f 1250//83 1251//83 1227//83
+f 1227//82 1251//82 1252//82
+f 1228//81 1252//81 1253//81
+f 1229//80 1253//80 1254//80
+f 1230//79 1254//79 1255//79
+f 1231//78 1255//78 1256//78
+f 1232//77 1256//77 1257//77
+f 1233//76 1257//76 1258//76
+f 1258//75 1259//75 1235//75
+f 1235//74 1259//74 1260//74
+f 1260//73 1261//73 1237//73
+f 1261//96 1262//96 1238//96
+f 1262//95 1263//95 1239//95
+f 1239//94 1263//94 1264//94
+f 1264//93 1265//93 1241//93
+f 1265//92 1266//92 1242//92
+f 1266//91 1267//91 1243//91
+f 1267//90 1268//90 1244//90
+f 1268//89 1269//89 1245//89
+f 1269//88 1270//88 1246//88
+f 1270//87 1271//87 1247//87
+f 1247//86 1271//86 1272//86
+f 1272//85 1249//85 1225//85
+f 1273//108 1274//108 1250//108
+f 1250//107 1274//107 1275//107
+f 1251//106 1275//106 1276//106
+f 1276//105 1277//105 1253//105
+f 1253//104 1277//104 1278//104
+f 1254//103 1278//103 1279//103
+f 1255//102 1279//102 1280//102
+f 1280//101 1281//101 1257//101
+f 1281//100 1282//100 1258//100
+f 1282//99 1283//99 1259//99
+f 1259//98 1283//98 1284//98
+f 1284//97 1285//97 1261//97
+f 1285//120 1286//120 1262//120
+f 1286//119 1287//119 1263//119
+f 1263//118 1287//118 1288//118
+f 1264//117 1288//117 1289//117
+f 1265//116 1289//116 1290//116
+f 1290//115 1291//115 1267//115
+f 1291//114 1292//114 1268//114
+f 1292//113 1293//113 1269//113
+f 1269//112 1293//112 1294//112
+f 1294//111 1295//111 1271//111
+f 1295//110 1296//110 1272//110
+f 1272//109 1296//109 1273//109
+f 1297//132 1298//132 1274//132
+f 1274//131 1298//131 1299//131
+f 1299//130 1300//130 1276//130
+f 1300//129 1301//129 1277//129
+f 1301//128 1302//128 1278//128
+f 1302//127 1303//127 1279//127
+f 1303//126 1304//126 1280//126
+f 1304//125 1305//125 1281//125
+f 1305//124 1306//124 1282//124
+f 1306//123 1307//123 1283//123
+f 1307//122 1308//122 1284//122
+f 1308//121 1309//121 1285//121
+f 1285//144 1309//144 1310//144
+f 1310//143 1311//143 1287//143
+f 1287//142 1311//142 1312//142
+f 1288//141 1312//141 1313//141
+f 1289//140 1313//140 1314//140
+f 1290//139 1314//139 1315//139
+f 1291//138 1315//138 1316//138
+f 1292//137 1316//137 1317//137
+f 1317//136 1318//136 1294//136
+f 1294//135 1318//135 1319//135
+f 1319//134 1320//134 1296//134
+f 1296//133 1320//133 1297//133
+f 1297//156 1321//156 1322//156
+f 1298//155 1322//155 1323//155
+f 1323//154 1324//154 1300//154
+f 1300//153 1324//153 1325//153
+f 1325//152 1326//152 1302//152
+f 1302//151 1326//151 1327//151
+f 1303//150 1327//150 1328//150
+f 1304//149 1328//149 1329//149
+f 1305//148 1329//148 1330//148
+f 1306//147 1330//147 1331//147
+f 1307//146 1331//146 1332//146
+f 1308//145 1332//145 1333//145
+f 1333//168 1334//168 1310//168
+f 1334//167 1335//167 1311//167
+f 1335//166 1336//166 1312//166
+f 1336//165 1337//165 1313//165
+f 1337//164 1338//164 1314//164
+f 1314//163 1338//163 1339//163
+f 1339//162 1340//162 1316//162
+f 1316//161 1340//161 1341//161
+f 1341//160 1342//160 1318//160
+f 1342//159 1343//159 1319//159
+f 1343//158 1344//158 1320//158
+f 1344//157 1321//157 1297//157
+f 1345//180 1346//180 1322//180
+f 1346//179 1347//179 1323//179
+f 1347//178 1348//178 1324//178
+f 1348//177 1349//177 1325//177
+f 1325//176 1349//176 1350//176
+f 1326//175 1350//175 1351//175
+f 1327//174 1351//174 1352//174
+f 1328//173 1352//173 1353//173
+f 1353//172 1354//172 1330//172
+f 1330//171 1354//171 1355//171
+f 1355//1155 1356//1155 1332//1155
+f 1356//169 1357//169 1333//169
+f 1357//192 1358//192 1334//192
+f 1358//191 1359//191 1335//191
+f 1359//190 1360//190 1336//190
+f 1360//189 1361//189 1337//189
+f 1361//188 1362//188 1338//188
+f 1362//187 1363//187 1339//187
+f 1363//186 1364//186 1340//186
+f 1364//185 1365//185 1341//185
+f 1365//184 1366//184 1342//184
+f 1342//183 1366//183 1367//183
+f 1343//182 1367//182 1368//182
+f 1368//181 1345//181 1321//181
+f 1369//204 1370//204 1346//204
+f 1370//203 1371//203 1347//203
+f 1371//202 1372//202 1348//202
+f 1372//201 1373//201 1349//201
+f 1373//200 1374//200 1350//200
+f 1350//199 1374//199 1375//199
+f 1375//198 1376//198 1352//198
+f 1376//197 1377//197 1353//197
+f 1377//196 1378//196 1354//196
+f 1378//1156 1379//1156 1355//1156
+f 1379//194 1380//194 1356//194
+f 1356//193 1380//193 1381//193
+f 1381//216 1382//216 1358//216
+f 1358//215 1382//215 1383//215
+f 1359//1157 1383//1157 1384//1157
+f 1360//213 1384//213 1385//213
+f 1361//212 1385//212 1386//212
+f 1362//211 1386//211 1387//211
+f 1387//210 1388//210 1364//210
+f 1364//209 1388//209 1389//209
+f 1365//208 1389//208 1390//208
+f 1366//207 1390//207 1391//207
+f 1367//206 1391//206 1392//206
+f 1392//205 1369//205 1345//205
+f 1393//228 1394//228 1370//228
+f 1394//227 1395//227 1371//227
+f 1371//226 1395//226 1396//226
+f 1396//225 1397//225 1373//225
+f 1397//224 1398//224 1374//224
+f 1374//223 1398//223 1399//223
+f 1399//222 1400//222 1376//222
+f 1376//221 1400//221 1401//221
+f 1401//220 1402//220 1378//220
+f 1378//219 1402//219 1403//219
+f 1403//218 1404//218 1380//218
+f 1404//217 1405//217 1381//217
+f 1405//240 1406//240 1382//240
+f 1406//239 1407//239 1383//239
+f 1407//238 1408//238 1384//238
+f 1384//237 1408//237 1409//237
+f 1409//236 1410//236 1386//236
+f 1410//235 1411//235 1387//235
+f 1411//234 1412//234 1388//234
+f 1388//233 1412//233 1413//233
+f 1413//232 1414//232 1390//232
+f 1414//231 1415//231 1391//231
+f 1391//230 1415//230 1416//230
+f 1392//229 1416//229 1393//229
+f 1417//252 1418//252 1394//252
+f 1418//251 1419//251 1395//251
+f 1395//250 1419//250 1420//250
+f 1420//249 1421//249 1397//249
+f 1397//248 1421//248 1422//248
+f 1398//247 1422//247 1423//247
+f 1423//246 1424//246 1400//246
+f 1400//245 1424//245 1425//245
+f 1401//244 1425//244 1426//244
+f 1426//243 1427//243 1403//243
+f 1403//242 1427//242 1428//242
+f 1404//241 1428//241 1429//241
+f 1429//264 1430//264 1406//264
+f 1430//263 1431//263 1407//263
+f 1407//262 1431//262 1432//262
+f 1432//261 1433//261 1409//261
+f 1433//260 1434//260 1410//260
+f 1410//259 1434//259 1435//259
+f 1435//258 1436//258 1412//258
+f 1436//257 1437//257 1413//257
+f 1413//256 1437//256 1438//256
+f 1438//255 1439//255 1415//255
+f 1439//254 1440//254 1416//254
+f 1440//253 1417//253 1393//253
+f 1417//1158 1441//1158 1442//1158
+f 1442//275 1443//275 1419//275
+f 1419//274 1443//274 1444//274
+f 1444//273 1445//273 1421//273
+f 1445//272 1446//272 1422//272
+f 1422//271 1446//271 1447//271
+f 1423//270 1447//270 1448//270
+f 1448//269 1449//269 1425//269
+f 1425//268 1449//268 1450//268
+f 1450//267 1451//267 1427//267
+f 1427//266 1451//266 1452//266
+f 1452//1159 1453//1159 1429//1159
+f 1429//1160 1453//1160 1454//1160
+f 1430//287 1454//287 1455//287
+f 1431//286 1455//286 1456//286
+f 1432//285 1456//285 1457//285
+f 1457//284 1458//284 1434//284
+f 1458//283 1459//283 1435//283
+f 1459//282 1460//282 1436//282
+f 1460//281 1461//281 1437//281
+f 1437//280 1461//280 1462//280
+f 1462//279 1463//279 1439//279
+f 1439//278 1463//278 1464//278
+f 1464//277 1441//277 1417//277
+f 1441//300 1465//300 1466//300
+f 1466//299 1467//299 1443//299
+f 1467//298 1468//298 1444//298
+f 1468//297 1469//297 1445//297
+f 1469//296 1470//296 1446//296
+f 1470//295 1471//295 1447//295
+f 1471//294 1472//294 1448//294
+f 1472//293 1473//293 1449//293
+f 1473//292 1474//292 1450//292
+f 1474//291 1475//291 1451//291
+f 1451//290 1475//290 1476//290
+f 1452//289 1476//289 1477//289
+f 1477//312 1478//312 1454//312
+f 1478//311 1479//311 1455//311
+f 1479//310 1480//310 1456//310
+f 1456//309 1480//309 1481//309
+f 1481//308 1482//308 1458//308
+f 1482//307 1483//307 1459//307
+f 1483//306 1484//306 1460//306
+f 1460//305 1484//305 1485//305
+f 1461//304 1485//304 1486//304
+f 1462//303 1486//303 1487//303
+f 1463//302 1487//302 1488//302
+f 1488//301 1465//301 1441//301
+f 1465//1161 1489//1161 1490//1161
+f 1490//1162 1491//1162 1467//1162
+f 1467//322 1491//322 1492//322
+f 1468//321 1492//321 1493//321
+f 1469//320 1493//320 1494//320
+f 1470//319 1494//319 1495//319
+f 1471//318 1495//318 1496//318
+f 1472//317 1496//317 1497//317
+f 1473//316 1497//316 1498//316
+f 1474//315 1498//315 1499//315
+f 1475//1163 1499//1163 1500//1163
+f 1500//313 1501//313 1477//313
+f 1501//336 1502//336 1478//336
+f 1502//1164 1503//1164 1479//1164
+f 1503//334 1504//334 1480//334
+f 1504//333 1505//333 1481//333
+f 1505//332 1506//332 1482//332
+f 1506//331 1507//331 1483//331
+f 1507//330 1508//330 1484//330
+f 1508//329 1509//329 1485//329
+f 1509//328 1510//328 1486//328
+f 1510//327 1511//327 1487//327
+f 1511//1165 1512//1165 1488//1165
+f 1512//1166 1489//1166 1465//1166
+f 1513//348 1514//348 1490//348
+f 1490//347 1514//347 1515//347
+f 1515//346 1516//346 1492//346
+f 1492//345 1516//345 1517//345
+f 1517//344 1518//344 1494//344
+f 1518//343 1519//343 1495//343
+f 1519//342 1520//342 1496//342
+f 1520//341 1521//341 1497//341
+f 1521//340 1522//340 1498//340
+f 1522//339 1523//339 1499//339
+f 1523//338 1524//338 1500//338
+f 1524//337 1525//337 1501//337
+f 1525//360 1526//360 1502//360
+f 1502//359 1526//359 1527//359
+f 1503//358 1527//358 1528//358
+f 1504//357 1528//357 1529//357
+f 1505//356 1529//356 1530//356
+f 1530//355 1531//355 1507//355
+f 1507//354 1531//354 1532//354
+f 1508//353 1532//353 1533//353
+f 1533//352 1534//352 1510//352
+f 1510//351 1534//351 1535//351
+f 1535//350 1536//350 1512//350
+f 1512//349 1536//349 1513//349
+f 1537//372 1538//372 1514//372
+f 1514//371 1538//371 1539//371
+f 1539//370 1540//370 1516//370
+f 1516//369 1540//369 1541//369
+f 1541//368 1542//368 1518//368
+f 1518//367 1542//367 1543//367
+f 1519//366 1543//366 1544//366
+f 1520//365 1544//365 1545//365
+f 1545//364 1546//364 1522//364
+f 1546//1167 1547//1167 1523//1167
+f 1523//362 1547//362 1548//362
+f 1524//361 1548//361 1549//361
+f 1549//384 1550//384 1526//384
+f 1550//383 1551//383 1527//383
+f 1551//382 1552//382 1528//382
+f 1552//381 1553//381 1529//381
+f 1553//380 1554//380 1530//380
+f 1554//379 1555//379 1531//379
+f 1555//378 1556//378 1532//378
+f 1556//377 1557//377 1533//377
+f 1557//376 1558//376 1534//376
+f 1534//375 1558//375 1559//375
+f 1559//374 1560//374 1536//374
+f 1536//373 1560//373 1537//373
+f 1537//396 1561//396 1562//396
+f 1562//395 1563//395 1539//395
+f 1563//394 1564//394 1540//394
+f 1540//393 1564//393 1565//393
+f 1565//392 1566//392 1542//392
+f 1566//391 1567//391 1543//391
+f 1567//390 1568//390 1544//390
+f 1568//389 1569//389 1545//389
+f 1569//388 1570//388 1546//388
+f 1570//387 1571//387 1547//387
+f 1547//386 1571//386 1572//386
+f 1548//385 1572//385 1573//385
+f 1573//408 1574//408 1550//408
+f 1574//407 1575//407 1551//407
+f 1551//406 1575//406 1576//406
+f 1552//405 1576//405 1577//405
+f 1553//404 1577//404 1578//404
+f 1554//403 1578//403 1579//403
+f 1579//402 1580//402 1556//402
+f 1556//401 1580//401 1581//401
+f 1581//400 1582//400 1558//400
+f 1558//399 1582//399 1583//399
+f 1559//398 1583//398 1584//398
+f 1560//397 1584//397 1561//397
+f 1561//420 1585//420 1586//420
+f 1586//419 1587//419 1563//419
+f 1563//418 1587//418 1588//418
+f 1564//417 1588//417 1589//417
+f 1565//416 1589//416 1590//416
+f 1590//415 1591//415 1567//415
+f 1591//414 1592//414 1568//414
+f 1592//413 1593//413 1569//413
+f 1569//412 1593//412 1594//412
+f 1594//411 1595//411 1571//411
+f 1571//410 1595//410 1596//410
+f 1572//409 1596//409 1597//409
+f 1597//432 1598//432 1574//432
+f 1598//431 1599//431 1575//431
+f 1575//430 1599//430 1600//430
+f 1576//429 1600//429 1601//429
+f 1577//428 1601//428 1602//428
+f 1602//427 1603//427 1579//427
+f 1579//426 1603//426 1604//426
+f 1604//425 1605//425 1581//425
+f 1605//424 1606//424 1582//424
+f 1606//423 1607//423 1583//423
+f 1583//422 1607//422 1608//422
+f 1584//421 1608//421 1585//421
+f 1609//444 1610//444 1586//444
+f 1610//443 1611//443 1587//443
+f 1587//442 1611//442 1612//442
+f 1612//441 1613//441 1589//441
+f 1589//440 1613//440 1614//440
+f 1614//439 1615//439 1591//439
+f 1591//438 1615//438 1616//438
+f 1592//437 1616//437 1617//437
+f 1593//436 1617//436 1618//436
+f 1618//1168 1619//1168 1595//1168
+f 1619//434 1620//434 1596//434
+f 1620//433 1621//433 1597//433
+f 1597//456 1621//456 1622//456
+f 1622//455 1623//455 1599//455
+f 1599//1169 1623//1169 1624//1169
+f 1624//453 1625//453 1601//453
+f 1625//452 1626//452 1602//452
+f 1626//451 1627//451 1603//451
+f 1627//450 1628//450 1604//450
+f 1604//449 1628//449 1629//449
+f 1605//448 1629//448 1630//448
+f 1630//447 1631//447 1607//447
+f 1607//446 1631//446 1632//446
+f 1608//445 1632//445 1609//445
+f 1609//468 1633//468 1634//468
+f 1634//467 1635//467 1611//467
+f 1611//466 1635//466 1636//466
+f 1636//465 1637//465 1613//465
+f 1637//464 1638//464 1614//464
+f 1614//463 1638//463 1639//463
+f 1639//462 1640//462 1616//462
+f 1616//461 1640//461 1641//461
+f 1641//460 1642//460 1618//460
+f 1618//459 1642//459 1643//459
+f 1643//458 1644//458 1620//458
+f 1644//457 1645//457 1621//457
+f 1621//480 1645//480 1646//480
+f 1646//479 1647//479 1623//479
+f 1647//478 1648//478 1624//478
+f 1648//477 1649//477 1625//477
+f 1649//476 1650//476 1626//476
+f 1626//475 1650//475 1651//475
+f 1651//474 1652//474 1628//474
+f 1628//473 1652//473 1653//473
+f 1653//472 1654//472 1630//472
+f 1654//471 1655//471 1631//471
+f 1631//470 1655//470 1656//470
+f 1656//469 1633//469 1609//469
+f 1657//492 1658//492 1634//492
+f 1658//491 1659//491 1635//491
+f 1635//490 1659//490 1660//490
+f 1636//489 1660//489 1661//489
+f 1661//488 1662//488 1638//488
+f 1662//487 1663//487 1639//487
+f 1639//486 1663//486 1664//486
+f 1640//485 1664//485 1665//485
+f 1665//484 1666//484 1642//484
+f 1666//483 1667//483 1643//483
+f 1643//482 1667//482 1668//482
+f 1644//481 1668//481 1669//481
+f 1669//504 1670//504 1646//504
+f 1670//503 1671//503 1647//503
+f 1671//502 1672//502 1648//502
+f 1648//501 1672//501 1673//501
+f 1673//500 1674//500 1650//500
+f 1674//499 1675//499 1651//499
+f 1651//498 1675//498 1676//498
+f 1652//497 1676//497 1677//497
+f 1677//496 1678//496 1654//496
+f 1678//495 1679//495 1655//495
+f 1655//494 1679//494 1680//494
+f 1680//493 1657//493 1633//493
+f 1681//516 1682//516 1658//516
+f 1682//515 1683//515 1659//515
+f 1683//514 1684//514 1660//514
+f 1684//513 1685//513 1661//513
+f 1685//512 1686//512 1662//512
+f 1662//511 1686//511 1687//511
+f 1687//510 1688//510 1664//510
+f 1664//509 1688//509 1689//509
+f 1689//508 1690//508 1666//508
+f 1690//507 1691//507 1667//507
+f 1691//506 1692//506 1668//506
+f 1692//505 1693//505 1669//505
+f 1669//528 1693//528 1694//528
+f 1670//527 1694//527 1695//527
+f 1695//526 1696//526 1672//526
+f 1672//525 1696//525 1697//525
+f 1697//524 1698//524 1674//524
+f 1674//523 1698//523 1699//523
+f 1699//522 1700//522 1676//522
+f 1676//521 1700//521 1701//521
+f 1677//520 1701//520 1702//520
+f 1678//519 1702//519 1703//519
+f 1679//518 1703//518 1704//518
+f 1680//517 1704//517 1681//517
+f 1705//540 1706//540 1682//540
+f 1682//539 1706//539 1707//539
+f 1683//538 1707//538 1708//538
+f 1708//537 1709//537 1685//537
+f 1685//536 1709//536 1710//536
+f 1710//535 1711//535 1687//535
+f 1711//534 1712//534 1688//534
+f 1688//533 1712//533 1713//533
+f 1713//532 1714//532 1690//532
+f 1690//531 1714//531 1715//531
+f 1715//530 1716//530 1692//530
+f 1716//529 1717//529 1693//529
+f 1693//552 1717//552 1718//552
+f 1694//551 1718//551 1719//551
+f 1719//550 1720//550 1696//550
+f 1720//549 1721//549 1697//549
+f 1721//548 1722//548 1698//548
+f 1698//547 1722//547 1723//547
+f 1699//546 1723//546 1724//546
+f 1724//545 1725//545 1701//545
+f 1701//544 1725//544 1726//544
+f 1726//543 1727//543 1703//543
+f 1727//542 1728//542 1704//542
+f 1704//541 1728//541 1705//541
+f 1729//1170 1730//1170 1706//1170
+f 1730//563 1731//563 1707//563
+f 1707//562 1731//562 1732//562
+f 1708//561 1732//561 1733//561
+f 1709//560 1733//560 1734//560
+f 1734//559 1735//559 1711//559
+f 1711//558 1735//558 1736//558
+f 1712//557 1736//557 1737//557
+f 1713//556 1737//556 1738//556
+f 1738//555 1739//555 1715//555
+f 1739//554 1740//554 1716//554
+f 1716//553 1740//553 1741//553
+f 1741//576 1742//576 1718//576
+f 1718//575 1742//575 1743//575
+f 1743//574 1744//574 1720//574
+f 1720//573 1744//573 1745//573
+f 1745//572 1746//572 1722//572
+f 1746//571 1747//571 1723//571
+f 1723//570 1747//570 1748//570
+f 1748//569 1749//569 1725//569
+f 1749//568 1750//568 1726//568
+f 1750//567 1751//567 1727//567
+f 1727//566 1751//566 1752//566
+f 1728//1171 1752//1171 1729//1171
+f 1753//588 1754//588 1730//588
+f 1754//587 1755//587 1731//587
+f 1731//586 1755//586 1756//586
+f 1756//585 1757//585 1733//585
+f 1757//584 1758//584 1734//584
+f 1758//583 1759//583 1735//583
+f 1759//582 1760//582 1736//582
+f 1760//581 1761//581 1737//581
+f 1761//580 1762//580 1738//580
+f 1738//579 1762//579 1763//579
+f 1763//578 1764//578 1740//578
+f 1740//577 1764//577 1765//577
+f 1765//600 1766//600 1742//600
+f 1742//599 1766//599 1767//599
+f 1767//598 1768//598 1744//598
+f 1744//597 1768//597 1769//597
+f 1745//596 1769//596 1770//596
+f 1746//595 1770//595 1771//595
+f 1747//594 1771//594 1772//594
+f 1772//593 1773//593 1749//593
+f 1749//592 1773//592 1774//592
+f 1774//591 1775//591 1751//591
+f 1751//590 1775//590 1776//590
+f 1776//589 1753//589 1729//589
+f 1777//612 1778//612 1754//612
+f 1778//611 1779//611 1755//611
+f 1779//610 1780//610 1756//610
+f 1756//609 1780//609 1781//609
+f 1781//608 1782//608 1758//608
+f 1758//607 1782//607 1783//607
+f 1783//606 1784//606 1760//606
+f 1760//605 1784//605 1785//605
+f 1761//604 1785//604 1786//604
+f 1786//603 1787//603 1763//603
+f 1787//602 1788//602 1764//602
+f 1764//601 1788//601 1789//601
+f 1789//624 1790//624 1766//624
+f 1766//623 1790//623 1791//623
+f 1767//622 1791//622 1792//622
+f 1792//621 1793//621 1769//621
+f 1793//620 1794//620 1770//620
+f 1770//619 1794//619 1795//619
+f 1795//618 1796//618 1772//618
+f 1796//617 1797//617 1773//617
+f 1797//616 1798//616 1774//616
+f 1798//615 1799//615 1775//615
+f 1775//614 1799//614 1800//614
+f 1776//613 1800//613 1777//613
+f 1777//636 1801//636 1802//636
+f 1802//635 1803//635 1779//635
+f 1803//634 1804//634 1780//634
+f 1780//633 1804//633 1805//633
+f 1781//632 1805//632 1806//632
+f 1782//1172 1806//1172 1807//1172
+f 1807//630 1808//630 1784//630
+f 1808//629 1809//629 1785//629
+f 1809//628 1810//628 1786//628
+f 1786//627 1810//627 1811//627
+f 1811//626 1812//626 1788//626
+f 1788//625 1812//625 1813//625
+f 1813//648 1814//648 1790//648
+f 1814//647 1815//647 1791//647
+f 1815//646 1816//646 1792//646
+f 1816//645 1817//645 1793//645
+f 1793//644 1817//644 1818//644
+f 1818//643 1819//643 1795//643
+f 1819//642 1820//642 1796//642
+f 1820//641 1821//641 1797//641
+f 1821//640 1822//640 1798//640
+f 1798//639 1822//639 1823//639
+f 1799//638 1823//638 1824//638
+f 1824//637 1801//637 1777//637
+f 1801//660 1825//660 1826//660
+f 1826//659 1827//659 1803//659
+f 1827//658 1828//658 1804//658
+f 1804//657 1828//657 1829//657
+f 1829//656 1830//656 1806//656
+f 1830//655 1831//655 1807//655
+f 1831//654 1832//654 1808//654
+f 1832//653 1833//653 1809//653
+f 1833//652 1834//652 1810//652
+f 1834//651 1835//651 1811//651
+f 1835//650 1836//650 1812//650
+f 1836//649 1837//649 1813//649
+f 1813//672 1837//672 1838//672
+f 1814//671 1838//671 1839//671
+f 1815//670 1839//670 1840//670
+f 1816//669 1840//669 1841//669
+f 1817//668 1841//668 1842//668
+f 1818//667 1842//667 1843//667
+f 1819//666 1843//666 1844//666
+f 1820//665 1844//665 1845//665
+f 1845//664 1846//664 1822//664
+f 1822//663 1846//663 1847//663
+f 1823//662 1847//662 1848//662
+f 1848//661 1825//661 1801//661
+f 1825//684 1849//684 1850//684
+f 1850//683 1851//683 1827//683
+f 1851//682 1852//682 1828//682
+f 1828//681 1852//681 1853//681
+f 1829//680 1853//680 1854//680
+f 1830//679 1854//679 1855//679
+f 1831//678 1855//678 1856//678
+f 1856//677 1857//677 1833//677
+f 1833//676 1857//676 1858//676
+f 1834//675 1858//675 1859//675
+f 1859//674 1860//674 1836//674
+f 1860//673 1861//673 1837//673
+f 1837//696 1861//696 1862//696
+f 1838//695 1862//695 1863//695
+f 1863//694 1864//694 1840//694
+f 1864//693 1865//693 1841//693
+f 1841//692 1865//692 1866//692
+f 1866//691 1867//691 1843//691
+f 1843//690 1867//690 1868//690
+f 1868//689 1869//689 1845//689
+f 1869//688 1870//688 1846//688
+f 1846//687 1870//687 1871//687
+f 1847//686 1871//686 1872//686
+f 1872//685 1849//685 1825//685
+f 1873//708 1874//708 1850//708
+f 1874//707 1875//707 1851//707
+f 1875//1173 1876//1173 1852//1173
+f 1876//705 1877//705 1853//705
+f 1853//704 1877//704 1878//704
+f 1878//703 1879//703 1855//703
+f 1855//702 1879//702 1880//702
+f 1880//701 1881//701 1857//701
+f 1881//700 1882//700 1858//700
+f 1882//1174 1883//1174 1859//1174
+f 1883//698 1884//698 1860//698
+f 1860//697 1884//697 1885//697
+f 1885//720 1886//720 1862//720
+f 1862//719 1886//719 1887//719
+f 1863//1175 1887//1175 1888//1175
+f 1864//717 1888//717 1889//717
+f 1865//716 1889//716 1890//716
+f 1890//715 1891//715 1867//715
+f 1867//714 1891//714 1892//714
+f 1892//713 1893//713 1869//713
+f 1869//712 1893//712 1894//712
+f 1870//1176 1894//1176 1895//1176
+f 1895//710 1896//710 1872//710
+f 1896//709 1873//709 1849//709
+f 1897//732 1898//732 1874//732
+f 1874//731 1898//731 1899//731
+f 1875//730 1899//730 1900//730
+f 1876//729 1900//729 1901//729
+f 1877//728 1901//728 1902//728
+f 1878//727 1902//727 1903//727
+f 1879//726 1903//726 1904//726
+f 1880//725 1904//725 1905//725
+f 1905//724 1906//724 1882//724
+f 1882//723 1906//723 1907//723
+f 1883//722 1907//722 1908//722
+f 1908//721 1909//721 1885//721
+f 1909//744 1910//744 1886//744
+f 1910//743 1911//743 1887//743
+f 1911//742 1912//742 1888//742
+f 1912//741 1913//741 1889//741
+f 1913//740 1914//740 1890//740
+f 1914//739 1915//739 1891//739
+f 1915//738 1916//738 1892//738
+f 1916//737 1917//737 1893//737
+f 1917//736 1918//736 1894//736
+f 1894//735 1918//735 1919//735
+f 1919//734 1920//734 1896//734
+f 1896//733 1920//733 1897//733
+f 1921//756 1922//756 1898//756
+f 1898//755 1922//755 1923//755
+f 1923//754 1924//754 1900//754
+f 1924//753 1925//753 1901//753
+f 1925//752 1926//752 1902//752
+f 1926//751 1927//751 1903//751
+f 1927//750 1928//750 1904//750
+f 1904//1177 1928//1177 1929//1177
+f 1929//748 1930//748 1906//748
+f 1906//747 1930//747 1931//747
+f 1931//746 1932//746 1908//746
+f 1908//745 1932//745 1933//745
+f 1933//768 1934//768 1910//768
+f 1910//767 1934//767 1935//767
+f 1935//766 1936//766 1912//766
+f 1912//765 1936//765 1937//765
+f 1937//1178 1938//1178 1914//1178
+f 1914//763 1938//763 1939//763
+f 1915//762 1939//762 1940//762
+f 1940//761 1941//761 1917//761
+f 1917//760 1941//760 1942//760
+f 1918//759 1942//759 1943//759
+f 1943//758 1944//758 1920//758
+f 1920//757 1944//757 1921//757
+f 1921//780 1945//780 1946//780
+f 1922//779 1946//779 1947//779
+f 1923//1179 1947//1179 1948//1179
+f 1924//777 1948//777 1949//777
+f 1949//776 1950//776 1926//776
+f 1926//775 1950//775 1951//775
+f 1927//774 1951//774 1952//774
+f 1952//773 1953//773 1929//773
+f 1929//772 1953//772 1954//772
+f 1930//771 1954//771 1955//771
+f 1955//770 1956//770 1932//770
+f 1932//769 1956//769 1957//769
+f 1957//792 1958//792 1934//792
+f 1934//791 1958//791 1959//791
+f 1959//790 1960//790 1936//790
+f 1960//789 1961//789 1937//789
+f 1961//788 1962//788 1938//788
+f 1962//787 1963//787 1939//787
+f 1963//786 1964//786 1940//786
+f 1940//785 1964//785 1965//785
+f 1965//784 1966//784 1942//784
+f 1966//1180 1967//1180 1943//1180
+f 1967//782 1968//782 1944//782
+f 1968//781 1945//781 1921//781
+f 1969//804 1970//804 1946//804
+f 1946//803 1970//803 1971//803
+f 1971//802 1972//802 1948//802
+f 1948//801 1972//801 1973//801
+f 1973//800 1974//800 1950//800
+f 1974//799 1975//799 1951//799
+f 1951//798 1975//798 1976//798
+f 1952//797 1976//797 1977//797
+f 1977//796 1978//796 1954//796
+f 1954//795 1978//795 1979//795
+f 1979//794 1980//794 1956//794
+f 1980//793 1981//793 1957//793
+f 1957//816 1981//816 1982//816
+f 1958//815 1982//815 1983//815
+f 1983//814 1984//814 1960//814
+f 1984//813 1985//813 1961//813
+f 1985//812 1986//812 1962//812
+f 1986//811 1987//811 1963//811
+f 1963//810 1987//810 1988//810
+f 1988//809 1989//809 1965//809
+f 1989//808 1990//808 1966//808
+f 1966//807 1990//807 1991//807
+f 1991//806 1992//806 1968//806
+f 1968//805 1992//805 1969//805
+f 1993//828 1994//828 1970//828
+f 1970//1181 1994//1181 1995//1181
+f 1995//826 1996//826 1972//826
+f 1996//825 1997//825 1973//825
+f 1973//824 1997//824 1998//824
+f 1974//823 1998//823 1999//823
+f 1999//822 2000//822 1976//822
+f 2000//821 2001//821 1977//821
+f 2001//820 2002//820 1978//820
+f 2002//819 2003//819 1979//819
+f 2003//818 2004//818 1980//818
+f 2004//817 2005//817 1981//817
+f 1981//840 2005//840 2006//840
+f 1982//839 2006//839 2007//839
+f 1983//838 2007//838 2008//838
+f 1984//837 2008//837 2009//837
+f 2009//836 2010//836 1986//836
+f 1986//835 2010//835 2011//835
+f 2011//834 2012//834 1988//834
+f 2012//833 2013//833 1989//833
+f 1989//832 2013//832 2014//832
+f 1990//831 2014//831 2015//831
+f 2015//1182 2016//1182 1992//1182
+f 2016//829 1993//829 1969//829
+f 2017//1183 2018//1183 1994//1183
+f 1994//851 2018//851 2019//851
+f 2019//850 2020//850 1996//850
+f 1996//849 2020//849 2021//849
+f 2021//848 2022//848 1998//848
+f 1998//847 2022//847 2023//847
+f 1999//846 2023//846 2024//846
+f 2000//845 2024//845 2025//845
+f 2001//844 2025//844 2026//844
+f 2026//843 2027//843 2003//843
+f 2003//842 2027//842 2028//842
+f 2028//841 2029//841 2005//841
+f 2005//1184 2029//1184 2030//1184
+f 2030//863 2031//863 2007//863
+f 2031//862 2032//862 2008//862
+f 2032//861 2033//861 2009//861
+f 2033//860 2034//860 2010//860
+f 2034//859 2035//859 2011//859
+f 2011//858 2035//858 2036//858
+f 2012//857 2036//857 2037//857
+f 2037//856 2038//856 2014//856
+f 2014//855 2038//855 2039//855
+f 2039//854 2040//854 2016//854
+f 2040//853 2017//853 1993//853
+f 2041//876 2042//876 2018//876
+f 2018//875 2042//875 2043//875
+f 2043//874 2044//874 2020//874
+f 2020//873 2044//873 2045//873
+f 2045//872 2046//872 2022//872
+f 2046//871 2047//871 2023//871
+f 2023//870 2047//870 2048//870
+f 2024//869 2048//869 2049//869
+f 2049//868 2050//868 2026//868
+f 2050//867 2051//867 2027//867
+f 2027//866 2051//866 2052//866
+f 2052//865 2053//865 2029//865
+f 2029//888 2053//888 2054//888
+f 2054//887 2055//887 2031//887
+f 2031//886 2055//886 2056//886
+f 2056//885 2057//885 2033//885
+f 2057//884 2058//884 2034//884
+f 2034//883 2058//883 2059//883
+f 2059//882 2060//882 2036//882
+f 2036//881 2060//881 2061//881
+f 2061//880 2062//880 2038//880
+f 2038//879 2062//879 2063//879
+f 2063//878 2064//878 2040//878
+f 2040//877 2064//877 2041//877
+f 2065//900 2066//900 2042//900
+f 2042//1185 2066//1185 2067//1185
+f 2067//898 2068//898 2044//898
+f 2068//897 2069//897 2045//897
+f 2045//896 2069//896 2070//896
+f 2070//895 2071//895 2047//895
+f 2071//894 2072//894 2048//894
+f 2048//893 2072//893 2073//893
+f 2073//892 2074//892 2050//892
+f 2050//891 2074//891 2075//891
+f 2075//890 2076//890 2052//890
+f 2076//889 2077//889 2053//889
+f 2053//912 2077//912 2078//912
+f 2054//911 2078//911 2079//911
+f 2079//910 2080//910 2056//910
+f 2056//909 2080//909 2081//909
+f 2081//908 2082//908 2058//908
+f 2058//907 2082//907 2083//907
+f 2059//906 2083//906 2084//906
+f 2084//905 2085//905 2061//905
+f 2085//904 2086//904 2062//904
+f 2062//903 2086//903 2087//903
+f 2087//902 2088//902 2064//902
+f 2088//901 2065//901 2041//901
+f 2089//924 2090//924 2066//924
+f 2066//923 2090//923 2091//923
+f 2091//922 2092//922 2068//922
+f 2068//921 2092//921 2093//921
+f 2069//920 2093//920 2094//920
+f 2070//919 2094//919 2095//919
+f 2095//918 2096//918 2072//918
+f 2096//917 2097//917 2073//917
+f 2097//916 2098//916 2074//916
+f 2098//915 2099//915 2075//915
+f 2075//914 2099//914 2100//914
+f 2100//913 2101//913 2077//913
+f 2101//936 2102//936 2078//936
+f 2102//935 2103//935 2079//935
+f 2079//934 2103//934 2104//934
+f 2080//933 2104//933 2105//933
+f 2081//932 2105//932 2106//932
+f 2082//931 2106//931 2107//931
+f 2107//930 2108//930 2084//930
+f 2108//929 2109//929 2085//929
+f 2109//928 2110//928 2086//928
+f 2086//927 2110//927 2111//927
+f 2111//926 2112//926 2088//926
+f 2088//925 2112//925 2089//925
+f 2089//948 2113//948 2114//948
+f 2090//947 2114//947 2115//947
+f 2115//1186 2116//1186 2092//1186
+f 2116//945 2117//945 2093//945
+f 2117//944 2118//944 2094//944
+f 2118//943 2119//943 2095//943
+f 2119//942 2120//942 2096//942
+f 2120//941 2121//941 2097//941
+f 2097//940 2121//940 2122//940
+f 2098//1187 2122//1187 2123//1187
+f 2099//938 2123//938 2124//938
+f 2124//937 2125//937 2101//937
+f 2101//960 2125//960 2126//960
+f 2126//959 2127//959 2103//959
+f 2127//958 2128//958 2104//958
+f 2128//957 2129//957 2105//957
+f 2105//956 2129//956 2130//956
+f 2106//955 2130//955 2131//955
+f 2107//954 2131//954 2132//954
+f 2108//953 2132//953 2133//953
+f 2109//952 2133//952 2134//952
+f 2110//1188 2134//1188 2135//1188
+f 2135//950 2136//950 2112//950
+f 2136//949 2113//949 2089//949
+f 2113//972 2137//972 2138//972
+f 2138//971 2139//971 2115//971
+f 2115//970 2139//970 2140//970
+f 2116//969 2140//969 2141//969
+f 2141//968 2142//968 2118//968
+f 2142//967 2143//967 2119//967
+f 2119//966 2143//966 2144//966
+f 2144//965 2145//965 2121//965
+f 2145//964 2146//964 2122//964
+f 2146//963 2147//963 2123//963
+f 2147//962 2148//962 2124//962
+f 2124//961 2148//961 2149//961
+f 2149//984 2150//984 2126//984
+f 2150//983 2151//983 2127//983
+f 2127//982 2151//982 2152//982
+f 2128//981 2152//981 2153//981
+f 2129//980 2153//980 2154//980
+f 2154//979 2155//979 2131//979
+f 2131//978 2155//978 2156//978
+f 2156//1189 2157//1189 2133//1189
+f 2157//976 2158//976 2134//976
+f 2158//975 2159//975 2135//975
+f 2159//974 2160//974 2136//974
+f 2160//973 2137//973 2113//973
+f 2137//996 2161//996 2162//996
+f 2162//995 2163//995 2139//995
+f 2163//994 2164//994 2140//994
+f 2164//993 2165//993 2141//993
+f 2165//992 2166//992 2142//992
+f 2166//991 2167//991 2143//991
+f 2167//990 2168//990 2144//990
+f 2168//989 2169//989 2145//989
+f 2145//988 2169//988 2170//988
+f 2170//987 2171//987 2147//987
+f 2147//986 2171//986 2172//986
+f 2148//985 2172//985 2173//985
+f 2173//1008 2174//1008 2150//1008
+f 2174//1007 2175//1007 2151//1007
+f 2151//1006 2175//1006 2176//1006
+f 2176//1005 2177//1005 2153//1005
+f 2153//1004 2177//1004 2178//1004
+f 2154//1003 2178//1003 2179//1003
+f 2155//1002 2179//1002 2180//1002
+f 2180//1001 2181//1001 2157//1001
+f 2157//1000 2181//1000 2182//1000
+f 2182//999 2183//999 2159//999
+f 2159//998 2183//998 2184//998
+f 2184//997 2161//997 2137//997
+f 2161//1020 2185//1020 2186//1020
+f 2186//1019 2187//1019 2163//1019
+f 2163//1190 2187//1190 2188//1190
+f 2188//1017 2189//1017 2165//1017
+f 2165//1016 2189//1016 2190//1016
+f 2166//1015 2190//1015 2191//1015
+f 2167//1014 2191//1014 2192//1014
+f 2168//1013 2192//1013 2193//1013
+f 2169//1012 2193//1012 2194//1012
+f 2194//1191 2195//1191 2171//1191
+f 2195//1010 2196//1010 2172//1010
+f 2196//1009 2197//1009 2173//1009
+f 2173//1032 2197//1032 2198//1032
+f 2198//1031 2199//1031 2175//1031
+f 2199//1192 2200//1192 2176//1192
+f 2200//1029 2201//1029 2177//1029
+f 2201//1028 2202//1028 2178//1028
+f 2202//1027 2203//1027 2179//1027
+f 2203//1026 2204//1026 2180//1026
+f 2204//1025 2205//1025 2181//1025
+f 2205//1024 2206//1024 2182//1024
+f 2206//1193 2207//1193 2183//1193
+f 2183//1022 2207//1022 2208//1022
+f 2208//1021 2185//1021 2161//1021
+f 2185//1044 2209//1044 2210//1044
+f 2210//1043 2211//1043 2187//1043
+f 2211//1042 2212//1042 2188//1042
+f 2212//1041 2213//1041 2189//1041
+f 2213//1040 2214//1040 2190//1040
+f 2214//1039 2215//1039 2191//1039
+f 2215//1038 2216//1038 2192//1038
+f 2192//1037 2216//1037 2217//1037
+f 2193//1036 2217//1036 2218//1036
+f 2218//1035 2219//1035 2195//1035
+f 2195//1034 2219//1034 2220//1034
+f 2220//1033 2221//1033 2197//1033
+f 2197//1056 2221//1056 2222//1056
+f 2222//1055 2223//1055 2199//1055
+f 2199//1054 2223//1054 2224//1054
+f 2224//1053 2225//1053 2201//1053
+f 2201//1052 2225//1052 2226//1052
+f 2202//1051 2226//1051 2227//1051
+f 2203//1050 2227//1050 2228//1050
+f 2204//1049 2228//1049 2229//1049
+f 2229//1048 2230//1048 2206//1048
+f 2206//1047 2230//1047 2231//1047
+f 2207//1046 2231//1046 2232//1046
+f 2232//1045 2209//1045 2185//1045
+f 2209//1068 2233//1068 2234//1068
+f 2210//1067 2234//1067 2235//1067
+f 2235//1066 2236//1066 2212//1066
+f 2212//1065 2236//1065 2237//1065
+f 2237//1064 2238//1064 2214//1064
+f 2238//1063 2239//1063 2215//1063
+f 2239//1062 2240//1062 2216//1062
+f 2240//1061 2241//1061 2217//1061
+f 2241//1060 2242//1060 2218//1060
+f 2242//1059 2243//1059 2219//1059
+f 2243//1058 2244//1058 2220//1058
+f 2244//1057 2245//1057 2221//1057
+f 2245//1080 2246//1080 2222//1080
+f 2222//1079 2246//1079 2247//1079
+f 2223//1078 2247//1078 2248//1078
+f 2248//1077 2249//1077 2225//1077
+f 2225//1076 2249//1076 2250//1076
+f 2250//1075 2251//1075 2227//1075
+f 2227//1074 2251//1074 2252//1074
+f 2228//1073 2252//1073 2253//1073
+f 2229//1072 2253//1072 2254//1072
+f 2230//1071 2254//1071 2255//1071
+f 2255//1070 2256//1070 2232//1070
+f 2256//1069 2233//1069 2209//1069
+f 2257//1092 2258//1092 2234//1092
+f 2258//1091 2259//1091 2235//1091
+f 2235//1090 2259//1090 2260//1090
+f 2236//1089 2260//1089 2261//1089
+f 2237//1088 2261//1088 2262//1088
+f 2238//1087 2262//1087 2263//1087
+f 2263//1086 2264//1086 2240//1086
+f 2240//1085 2264//1085 2265//1085
+f 2241//1084 2265//1084 2266//1084
+f 2266//1083 2267//1083 2243//1083
+f 2267//1082 2268//1082 2244//1082
+f 2244//1081 2268//1081 2269//1081
+f 2269//1104 2270//1104 2246//1104
+f 2270//1103 2271//1103 2247//1103
+f 2271//1102 2272//1102 2248//1102
+f 2248//1101 2272//1101 2273//1101
+f 2273//1100 2274//1100 2250//1100
+f 2250//1099 2274//1099 2275//1099
+f 2275//1098 2276//1098 2252//1098
+f 2276//1097 2277//1097 2253//1097
+f 2277//1096 2278//1096 2254//1096
+f 2278//1095 2279//1095 2255//1095
+f 2255//1094 2279//1094 2280//1094
+f 2256//1093 2280//1093 2257//1093
+f 2257//1116 2281//1116 2282//1116
+f 2282//1115 2283//1115 2259//1115
+f 2283//1114 2284//1114 2260//1114
+f 2284//1113 2285//1113 2261//1113
+f 2285//1112 2286//1112 2262//1112
+f 2286//1111 2287//1111 2263//1111
+f 2287//1110 2288//1110 2264//1110
+f 2288//1109 2289//1109 2265//1109
+f 2289//1108 2290//1108 2266//1108
+f 2290//1107 2291//1107 2267//1107
+f 2291//1106 2292//1106 2268//1106
+f 2292//1105 2293//1105 2269//1105
+f 2269//1128 2293//1128 2294//1128
+f 2270//1127 2294//1127 2295//1127
+f 2271//1126 2295//1126 2296//1126
+f 2272//1125 2296//1125 2297//1125
+f 2273//1124 2297//1124 2298//1124
+f 2274//1123 2298//1123 2299//1123
+f 2275//1122 2299//1122 2300//1122
+f 2276//1121 2300//1121 2301//1121
+f 2277//1120 2301//1120 2302//1120
+f 2302//1119 2303//1119 2279//1119
+f 2279//1118 2303//1118 2304//1118
+f 2304//1117 2281//1117 2257//1117
+f 1//1140 2//1140 2282//1140
+f 2//1139 3//1139 2283//1139
+f 3//1138 4//1138 2284//1138
+f 4//1137 5//1137 2285//1137
+f 5//1136 6//1136 2286//1136
+f 6//1135 7//1135 2287//1135
+f 2287//1134 7//1134 8//1134
+f 8//1133 9//1133 2289//1133
+f 9//1132 10//1132 2290//1132
+f 2290//1131 10//1131 11//1131
+f 2291//1130 11//1130 12//1130
+f 2292//1129 12//1129 13//1129
+f 13//1152 14//1152 2294//1152
+f 14//1151 15//1151 2295//1151
+f 15//1150 16//1150 2296//1150
+f 2296//1149 16//1149 17//1149
+f 2297//1148 17//1148 18//1148
+f 18//1147 19//1147 2299//1147
+f 19//1146 20//1146 2300//1146
+f 20//1145 21//1145 2301//1145
+f 21//1144 22//1144 2302//1144
+f 2302//1143 22//1143 23//1143
+f 23//1142 24//1142 2304//1142
+f 24//1141 1//1141 2281//1141
+f 1//1 25//1 2//1
+f 2//2 26//2 3//2
+f 4//3 3//3 28//3
+f 4//4 28//4 5//4
+f 6//5 5//5 30//5
+f 6//6 30//6 7//6
+f 7//7 31//7 8//7
+f 8//8 32//8 9//8
+f 10//9 9//9 34//9
+f 10//10 34//10 11//10
+f 11//11 35//11 12//11
+f 12//12 36//12 13//12
+f 14//13 13//13 38//13
+f 15//14 14//14 39//14
+f 15//15 39//15 16//15
+f 16//16 40//16 17//16
+f 17//17 41//17 18//17
+f 19//18 18//18 43//18
+f 20//19 19//19 44//19
+f 20//20 44//20 21//20
+f 21//21 45//21 22//21
+f 22//22 46//22 23//22
+f 24//23 23//23 48//23
+f 1//24 24//24 25//24
+f 25//25 49//25 26//25
+f 27//26 26//26 51//26
+f 28//27 27//27 52//27
+f 29//28 28//28 53//28
+f 30//29 29//29 54//29
+f 30//30 54//30 31//30
+f 32//31 31//31 56//31
+f 33//32 32//32 57//32
+f 34//33 33//33 58//33
+f 35//34 34//34 59//34
+f 36//35 35//35 60//35
+f 37//36 36//36 61//36
+f 37//37 61//37 38//37
+f 38//38 62//38 39//38
+f 39//39 63//39 40//39
+f 40//40 64//40 41//40
+f 41//41 65//41 42//41
+f 42//42 66//42 43//42
+f 43//43 67//43 44//43
+f 44//44 68//44 45//44
+f 45//45 69//45 46//45
+f 46//46 70//46 47//46
+f 47//47 71//47 48//47
+f 25//48 48//48 49//48
+f 49//49 73//49 50//49
+f 51//50 50//50 75//50
+f 51//51 75//51 52//51
+f 52//52 76//52 53//52
+f 53//53 77//53 54//53
+f 55//1154 54//1154 79//1154
+f 55//1153 79//1153 56//1153
+f 56//56 80//56 57//56
+f 57//57 81//57 58//57
+f 58//58 82//58 59//58
+f 60//59 59//59 84//59
+f 61//60 60//60 85//60
+f 61//61 85//61 62//61
+f 62//62 86//62 63//62
+f 63//63 87//63 64//63
+f 65//64 64//64 89//64
+f 65//65 89//65 66//65
+f 66//1194 90//1194 67//1194
+f 68//1195 67//1195 92//1195
+f 69//68 68//68 93//68
+f 70//69 69//69 94//69
+f 71//70 70//70 95//70
+f 71//71 95//71 72//71
+f 72//72 96//72 49//72
+f 73//73 97//73 74//73
+f 74//74 98//74 75//74
+f 76//75 75//75 100//75
+f 77//76 76//76 101//76
+f 78//77 77//77 102//77
+f 79//78 78//78 103//78
+f 80//79 79//79 104//79
+f 81//80 80//80 105//80
+f 82//81 81//81 106//81
+f 82//82 106//82 83//82
+f 84//83 83//83 108//83
+f 84//84 108//84 85//84
+f 86//85 85//85 110//85
+f 86//86 110//86 87//86
+f 88//87 87//87 112//87
+f 88//88 112//88 89//88
+f 89//89 113//89 90//89
+f 90//90 114//90 91//90
+f 91//91 115//91 92//91
+f 92//92 116//92 93//92
+f 93//93 117//93 94//93
+f 94//94 118//94 95//94
+f 96//95 95//95 120//95
+f 96//96 120//96 73//96
+f 97//97 121//97 98//97
+f 99//98 98//98 123//98
+f 99//99 123//99 100//99
+f 100//100 124//100 101//100
+f 101//101 125//101 102//101
+f 102//102 126//102 103//102
+f 103//103 127//103 104//103
+f 104//104 128//104 105//104
+f 105//105 129//105 106//105
+f 106//106 130//106 107//106
+f 107//107 131//107 108//107
+f 108//108 132//108 109//108
+f 109//109 133//109 110//109
+f 111//110 110//110 135//110
+f 112//111 111//111 136//111
+f 113//112 112//112 137//112
+f 114//113 113//113 138//113
+f 115//114 114//114 139//114
+f 116//115 115//115 140//115
+f 117//116 116//116 141//116
+f 118//117 117//117 142//117
+f 119//118 118//118 143//118
+f 119//119 143//119 120//119
+f 97//120 120//120 121//120
+f 121//121 145//121 122//121
+f 123//122 122//122 147//122
+f 124//123 123//123 148//123
+f 125//124 124//124 149//124
+f 126//125 125//125 150//125
+f 126//126 150//126 127//126
+f 128//127 127//127 152//127
+f 128//128 152//128 129//128
+f 129//129 153//129 130//129
+f 131//130 130//130 155//130
+f 131//131 155//131 132//131
+f 133//132 132//132 157//132
+f 133//133 157//133 134//133
+f 134//134 158//134 135//134
+f 135//135 159//135 136//135
+f 136//136 160//136 137//136
+f 138//137 137//137 162//137
+f 138//138 162//138 139//138
+f 139//139 163//139 140//139
+f 140//140 164//140 141//140
+f 141//141 165//141 142//141
+f 142//142 166//142 143//142
+f 143//143 167//143 144//143
+f 144//144 168//144 121//144
+f 145//145 169//145 146//145
+f 146//146 170//146 147//146
+f 148//147 147//147 172//147
+f 148//148 172//148 149//148
+f 149//149 173//149 150//149
+f 150//150 174//150 151//150
+f 151//151 175//151 152//151
+f 152//152 176//152 153//152
+f 153//153 177//153 154//153
+f 154//154 178//154 155//154
+f 155//155 179//155 156//155
+f 156//156 180//156 157//156
+f 158//157 157//157 182//157
+f 159//158 158//158 183//158
+f 160//159 159//159 184//159
+f 161//160 160//160 185//160
+f 162//161 161//161 186//161
+f 163//162 162//162 187//162
+f 163//163 187//163 164//163
+f 165//164 164//164 189//164
+f 166//165 165//165 190//165
+f 166//166 190//166 167//166
+f 168//167 167//167 192//167
+f 145//168 168//168 169//168
+f 169//169 193//169 170//169
+f 171//1155 170//1155 195//1155
+f 172//171 171//171 196//171
+f 173//172 172//172 197//172
+f 174//1196 173//1196 198//1196
+f 175//174 174//174 199//174
+f 175//175 199//175 176//175
+f 177//176 176//176 201//176
+f 177//177 201//177 178//177
+f 179//178 178//178 203//178
+f 180//179 179//179 204//179
+f 180//180 204//180 181//180
+f 182//181 181//181 206//181
+f 182//182 206//182 183//182
+f 183//183 207//183 184//183
+f 184//184 208//184 185//184
+f 185//185 209//185 186//185
+f 186//186 210//186 187//186
+f 188//187 187//187 212//187
+f 188//188 212//188 189//188
+f 189//189 213//189 190//189
+f 190//190 214//190 191//190
+f 191//191 215//191 192//191
+f 192//192 216//192 169//192
+f 193//193 217//193 194//193
+f 194//194 218//194 195//194
+f 196//1156 195//1156 220//1156
+f 196//196 220//196 197//196
+f 198//197 197//197 222//197
+f 199//198 198//198 223//198
+f 200//199 199//199 224//199
+f 200//200 224//200 201//200
+f 201//201 225//201 202//201
+f 202//202 226//202 203//202
+f 203//203 227//203 204//203
+f 205//204 204//204 229//204
+f 205//205 229//205 206//205
+f 207//206 206//206 231//206
+f 208//207 207//207 232//207
+f 209//208 208//208 233//208
+f 209//209 233//209 210//209
+f 210//210 234//210 211//210
+f 211//211 235//211 212//211
+f 212//212 236//212 213//212
+f 213//213 237//213 214//213
+f 214//1157 238//1157 215//1157
+f 216//215 215//215 240//215
+f 193//216 216//216 217//216
+f 217//217 241//217 218//217
+f 218//218 242//218 219//218
+f 220//219 219//219 244//219
+f 220//220 244//220 221//220
+f 221//221 245//221 222//221
+f 222//222 246//222 223//222
+f 224//223 223//223 248//223
+f 225//224 224//224 249//224
+f 226//225 225//225 250//225
+f 227//226 226//226 251//226
+f 228//227 227//227 252//227
+f 228//228 252//228 229//228
+f 229//229 253//229 230//229
+f 231//230 230//230 255//230
+f 231//231 255//231 232//231
+f 232//232 256//232 233//232
+f 234//233 233//233 258//233
+f 234//234 258//234 235//234
+f 236//235 235//235 260//235
+f 237//236 236//236 261//236
+f 238//237 237//237 262//237
+f 238//238 262//238 239//238
+f 240//239 239//239 264//239
+f 240//240 264//240 217//240
+f 242//241 241//241 266//241
+f 242//242 266//242 243//242
+f 243//243 267//243 244//243
+f 244//244 268//244 245//244
+f 245//245 269//245 246//245
+f 247//246 246//246 271//246
+f 247//247 271//247 248//247
+f 248//248 272//248 249//248
+f 250//249 249//249 274//249
+f 250//250 274//250 251//250
+f 251//251 275//251 252//251
+f 252//252 276//252 253//252
+f 254//253 253//253 278//253
+f 254//254 278//254 255//254
+f 256//255 255//255 280//255
+f 256//256 280//256 257//256
+f 258//257 257//257 282//257
+f 259//258 258//258 283//258
+f 259//259 283//259 260//259
+f 261//260 260//260 285//260
+f 262//261 261//261 286//261
+f 262//262 286//262 263//262
+f 263//263 287//263 264//263
+f 264//264 288//264 241//264
+f 266//265 265//265 290//265
+f 266//266 290//266 267//266
+f 268//267 267//267 292//267
+f 268//268 292//268 269//268
+f 269//269 293//269 270//269
+f 270//270 294//270 271//270
+f 271//271 295//271 272//271
+f 272//272 296//272 273//272
+f 274//273 273//273 298//273
+f 274//274 298//274 275//274
+f 275//275 299//275 276//275
+f 276//1158 300//1158 277//1158
+f 277//277 301//277 278//277
+f 279//278 278//278 303//278
+f 280//279 279//279 304//279
+f 280//280 304//280 281//280
+f 281//281 305//281 282//281
+f 283//282 282//282 307//282
+f 284//283 283//283 308//283
+f 284//284 308//284 285//284
+f 286//285 285//285 310//285
+f 286//286 310//286 287//286
+f 287//287 311//287 288//287
+f 288//288 312//288 265//288
+f 290//1197 289//1197 314//1197
+f 290//290 314//290 291//290
+f 292//291 291//291 316//291
+f 293//292 292//292 317//292
+f 294//293 293//293 318//293
+f 295//294 294//294 319//294
+f 296//295 295//295 320//295
+f 296//296 320//296 297//296
+f 297//297 321//297 298//297
+f 298//298 322//298 299//298
+f 299//299 323//299 300//299
+f 300//1198 324//1198 301//1198
+f 301//301 325//301 302//301
+f 303//302 302//302 327//302
+f 304//303 303//303 328//303
+f 304//304 328//304 305//304
+f 305//305 329//305 306//305
+f 306//306 330//306 307//306
+f 308//307 307//307 332//307
+f 308//308 332//308 309//308
+f 309//309 333//309 310//309
+f 310//310 334//310 311//310
+f 312//311 311//311 336//311
+f 312//312 336//312 289//312
+f 313//313 337//313 314//313
+f 315//314 314//314 339//314
+f 316//315 315//315 340//315
+f 316//316 340//316 317//316
+f 317//317 341//317 318//317
+f 319//318 318//318 343//318
+f 319//319 343//319 320//319
+f 320//320 344//320 321//320
+f 322//321 321//321 346//321
+f 322//322 346//322 323//322
+f 324//323 323//323 348//323
+f 324//324 348//324 325//324
+f 326//325 325//325 350//325
+f 326//326 350//326 327//326
+f 328//327 327//327 352//327
+f 328//328 352//328 329//328
+f 329//329 353//329 330//329
+f 331//330 330//330 355//330
+f 331//331 355//331 332//331
+f 333//332 332//332 357//332
+f 334//333 333//333 358//333
+f 334//334 358//334 335//334
+f 335//335 359//335 336//335
+f 313//336 336//336 337//336
+f 338//337 337//337 362//337
+f 338//338 362//338 339//338
+f 340//339 339//339 364//339
+f 340//340 364//340 341//340
+f 341//341 365//341 342//341
+f 343//342 342//342 367//342
+f 343//343 367//343 344//343
+f 344//344 368//344 345//344
+f 346//345 345//345 370//345
+f 347//346 346//346 371//346
+f 348//347 347//347 372//347
+f 348//348 372//348 349//348
+f 349//349 373//349 350//349
+f 350//350 374//350 351//350
+f 351//351 375//351 352//351
+f 352//352 376//352 353//352
+f 353//353 377//353 354//353
+f 354//354 378//354 355//354
+f 355//355 379//355 356//355
+f 356//356 380//356 357//356
+f 357//357 381//357 358//357
+f 358//358 382//358 359//358
+f 360//359 359//359 384//359
+f 337//360 360//360 361//360
+f 361//361 385//361 362//361
+f 362//362 386//362 363//362
+f 364//1167 363//1167 388//1167
+f 364//364 388//364 365//364
+f 366//365 365//365 390//365
+f 366//366 390//366 367//366
+f 367//367 391//367 368//367
+f 368//368 392//368 369//368
+f 369//369 393//369 370//369
+f 370//370 394//370 371//370
+f 372//371 371//371 396//371
+f 372//372 396//372 373//372
+f 374//373 373//373 398//373
+f 374//374 398//374 375//374
+f 376//375 375//375 400//375
+f 376//376 400//376 377//376
+f 378//377 377//377 402//377
+f 379//378 378//378 403//378
+f 380//379 379//379 404//379
+f 380//380 404//380 381//380
+f 382//381 381//381 406//381
+f 383//1199 382//1199 407//1199
+f 384//383 383//383 408//383
+f 361//384 384//384 385//384
+f 386//385 385//385 410//385
+f 386//386 410//386 387//386
+f 388//387 387//387 412//387
+f 389//388 388//388 413//388
+f 389//389 413//389 390//389
+f 391//390 390//390 415//390
+f 392//391 391//391 416//391
+f 392//1200 416//1200 393//1200
+f 394//393 393//393 418//393
+f 394//394 418//394 395//394
+f 396//395 395//395 420//395
+f 396//396 420//396 397//396
+f 397//397 421//397 398//397
+f 398//398 422//398 399//398
+f 400//399 399//399 424//399
+f 400//400 424//400 401//400
+f 402//1201 401//1201 426//1201
+f 402//402 426//402 403//402
+f 403//403 427//403 404//403
+f 404//1202 428//1202 405//1202
+f 405//405 429//405 406//405
+f 406//406 430//406 407//406
+f 408//407 407//407 432//407
+f 408//408 432//408 385//408
+f 410//409 409//409 434//409
+f 410//410 434//410 411//410
+f 412//411 411//411 436//411
+f 412//412 436//412 413//412
+f 413//413 437//413 414//413
+f 414//414 438//414 415//414
+f 415//415 439//415 416//415
+f 416//416 440//416 417//416
+f 418//417 417//417 442//417
+f 418//418 442//418 419//418
+f 419//419 443//419 420//419
+f 420//420 444//420 421//420
+f 421//421 445//421 422//421
+f 423//422 422//422 447//422
+f 424//423 423//423 448//423
+f 424//424 448//424 425//424
+f 426//425 425//425 450//425
+f 427//426 426//426 451//426
+f 428//427 427//427 452//427
+f 429//428 428//428 453//428
+f 430//429 429//429 454//429
+f 430//430 454//430 431//430
+f 432//431 431//431 456//431
+f 432//432 456//432 409//432
+f 433//433 457//433 434//433
+f 434//434 458//434 435//434
+f 436//1168 435//1168 460//1168
+f 436//436 460//436 437//436
+f 438//437 437//437 462//437
+f 438//438 462//438 439//438
+f 440//439 439//439 464//439
+f 441//440 440//440 465//440
+f 442//441 441//441 466//441
+f 443//1203 442//1203 467//1203
+f 443//443 467//443 444//443
+f 445//444 444//444 469//444
+f 445//445 469//445 446//445
+f 446//446 470//446 447//446
+f 447//1204 471//1204 448//1204
+f 448//448 472//448 449//448
+f 449//449 473//449 450//449
+f 450//450 474//450 451//450
+f 452//451 451//451 476//451
+f 452//452 476//452 453//452
+f 454//453 453//453 478//453
+f 454//1169 478//1169 455//1169
+f 455//455 479//455 456//455
+f 456//456 480//456 433//456
+f 457//457 481//457 458//457
+f 458//458 482//458 459//458
+f 459//459 483//459 460//459
+f 460//460 484//460 461//460
+f 462//461 461//461 486//461
+f 462//462 486//462 463//462
+f 463//463 487//463 464//463
+f 465//464 464//464 489//464
+f 465//465 489//465 466//465
+f 466//466 490//466 467//466
+f 467//467 491//467 468//467
+f 469//468 468//468 493//468
+f 469//469 493//469 470//469
+f 470//470 494//470 471//470
+f 472//471 471//471 496//471
+f 473//472 472//472 497//472
+f 473//473 497//473 474//473
+f 474//474 498//474 475//474
+f 476//475 475//475 500//475
+f 476//476 500//476 477//476
+f 478//477 477//477 502//477
+f 478//478 502//478 479//478
+f 480//479 479//479 504//479
+f 480//480 504//480 457//480
+f 482//481 481//481 506//481
+f 483//482 482//482 507//482
+f 483//483 507//483 484//483
+f 484//484 508//484 485//484
+f 485//485 509//485 486//485
+f 486//486 510//486 487//486
+f 488//487 487//487 512//487
+f 488//488 512//488 489//488
+f 489//489 513//489 490//489
+f 490//1205 514//1205 491//1205
+f 491//491 515//491 492//491
+f 493//492 492//492 517//492
+f 493//493 517//493 494//493
+f 495//494 494//494 519//494
+f 495//495 519//495 496//495
+f 497//496 496//496 521//496
+f 497//497 521//497 498//497
+f 498//498 522//498 499//498
+f 500//499 499//499 524//499
+f 501//500 500//500 525//500
+f 502//501 501//501 526//501
+f 503//502 502//502 527//502
+f 504//503 503//503 528//503
+f 504//504 528//504 481//504
+f 506//505 505//505 530//505
+f 506//506 530//506 507//506
+f 507//507 531//507 508//507
+f 509//508 508//508 533//508
+f 509//509 533//509 510//509
+f 510//510 534//510 511//510
+f 512//511 511//511 536//511
+f 512//512 536//512 513//512
+f 513//513 537//513 514//513
+f 515//514 514//514 539//514
+f 515//515 539//515 516//515
+f 517//516 516//516 541//516
+f 517//517 541//517 518//517
+f 518//518 542//518 519//518
+f 519//519 543//519 520//519
+f 521//520 520//520 545//520
+f 522//521 521//521 546//521
+f 522//522 546//522 523//522
+f 523//523 547//523 524//523
+f 524//524 548//524 525//524
+f 525//525 549//525 526//525
+f 526//526 550//526 527//526
+f 528//527 527//527 552//527
+f 528//528 552//528 505//528
+f 530//529 529//529 554//529
+f 531//530 530//530 555//530
+f 532//531 531//531 556//531
+f 532//532 556//532 533//532
+f 533//533 557//533 534//533
+f 535//534 534//534 559//534
+f 536//535 535//535 560//535
+f 536//536 560//536 537//536
+f 537//537 561//537 538//537
+f 539//538 538//538 563//538
+f 540//539 539//539 564//539
+f 540//540 564//540 541//540
+f 541//541 565//541 542//541
+f 542//542 566//542 543//542
+f 543//543 567//543 544//543
+f 545//544 544//544 569//544
+f 545//545 569//545 546//545
+f 546//546 570//546 547//546
+f 548//547 547//547 572//547
+f 548//548 572//548 549//548
+f 550//549 549//549 574//549
+f 550//550 574//550 551//550
+f 551//551 575//551 552//551
+f 552//552 576//552 529//552
+f 553//553 577//553 554//553
+f 555//554 554//554 579//554
+f 555//555 579//555 556//555
+f 557//556 556//556 581//556
+f 557//557 581//557 558//557
+f 559//558 558//558 583//558
+f 560//559 559//559 584//559
+f 561//560 560//560 585//560
+f 562//561 561//561 586//561
+f 562//562 586//562 563//562
+f 564//563 563//563 588//563
+f 564//564 588//564 565//564
+f 566//565 565//565 590//565
+f 566//566 590//566 567//566
+f 568//567 567//567 592//567
+f 568//568 592//568 569//568
+f 569//569 593//569 570//569
+f 570//570 594//570 571//570
+f 571//571 595//571 572//571
+f 572//572 596//572 573//572
+f 573//573 597//573 574//573
+f 575//574 574//574 599//574
+f 575//575 599//575 576//575
+f 576//576 600//576 553//576
+f 578//1206 577//1206 602//1206
+f 579//578 578//578 603//578
+f 579//579 603//579 580//579
+f 580//580 604//580 581//580
+f 581//581 605//581 582//581
+f 583//582 582//582 607//582
+f 583//583 607//583 584//583
+f 584//584 608//584 585//584
+f 586//585 585//585 610//585
+f 586//586 610//586 587//586
+f 588//587 587//587 612//587
+f 588//588 612//588 589//588
+f 590//589 589//589 614//589
+f 590//590 614//590 591//590
+f 591//591 615//591 592//591
+f 592//592 616//592 593//592
+f 594//593 593//593 618//593
+f 595//594 594//594 619//594
+f 595//595 619//595 596//595
+f 597//596 596//596 621//596
+f 598//597 597//597 622//597
+f 599//598 598//598 623//598
+f 599//599 623//599 600//599
+f 600//1207 624//1207 577//1207
+f 602//601 601//601 626//601
+f 602//602 626//602 603//602
+f 604//603 603//603 628//603
+f 605//604 604//604 629//604
+f 605//605 629//605 606//605
+f 607//606 606//606 631//606
+f 608//607 607//607 632//607
+f 608//608 632//608 609//608
+f 609//609 633//609 610//609
+f 610//610 634//610 611//610
+f 611//611 635//611 612//611
+f 612//612 636//612 613//612
+f 613//613 637//613 614//613
+f 615//614 614//614 639//614
+f 616//615 615//615 640//615
+f 616//616 640//616 617//616
+f 617//617 641//617 618//617
+f 618//618 642//618 619//618
+f 619//619 643//619 620//619
+f 621//620 620//620 645//620
+f 621//621 645//621 622//621
+f 622//622 646//622 623//622
+f 624//623 623//623 648//623
+f 624//624 648//624 601//624
+f 625//625 649//625 626//625
+f 626//626 650//626 627//626
+f 627//627 651//627 628//627
+f 629//628 628//628 653//628
+f 629//629 653//629 630//629
+f 630//1208 654//1208 631//1208
+f 631//1172 655//1172 632//1172
+f 632//632 656//632 633//632
+f 634//633 633//633 658//633
+f 634//634 658//634 635//634
+f 636//635 635//635 660//635
+f 637//636 636//636 661//636
+f 637//637 661//637 638//637
+f 638//638 662//638 639//638
+f 639//639 663//639 640//639
+f 640//640 664//640 641//640
+f 641//641 665//641 642//641
+f 642//642 666//642 643//642
+f 644//643 643//643 668//643
+f 645//644 644//644 669//644
+f 645//645 669//645 646//645
+f 647//646 646//646 671//646
+f 648//647 647//647 672//647
+f 625//648 648//648 649//648
+f 650//649 649//649 674//649
+f 651//650 650//650 675//650
+f 651//651 675//651 652//651
+f 653//652 652//652 677//652
+f 654//653 653//653 678//653
+f 655//654 654//654 679//654
+f 656//655 655//655 680//655
+f 656//656 680//656 657//656
+f 658//657 657//657 682//657
+f 658//658 682//658 659//658
+f 660//659 659//659 684//659
+f 660//660 684//660 661//660
+f 661//661 685//661 662//661
+f 662//662 686//662 663//662
+f 664//663 663//663 688//663
+f 664//664 688//664 665//664
+f 665//665 689//665 666//665
+f 666//666 690//666 667//666
+f 667//667 691//667 668//667
+f 668//668 692//668 669//668
+f 670//669 669//669 694//669
+f 670//670 694//670 671//670
+f 671//671 695//671 672//671
+f 672//672 696//672 649//672
+f 673//673 697//673 674//673
+f 675//674 674//674 699//674
+f 675//675 699//675 676//675
+f 676//676 700//676 677//676
+f 677//677 701//677 678//677
+f 678//678 702//678 679//678
+f 680//679 679//679 704//679
+f 680//680 704//680 681//680
+f 681//681 705//681 682//681
+f 682//682 706//682 683//682
+f 683//683 707//683 684//683
+f 685//684 684//684 709//684
+f 685//685 709//685 686//685
+f 687//686 686//686 711//686
+f 688//687 687//687 712//687
+f 689//688 688//688 713//688
+f 690//689 689//689 714//689
+f 690//690 714//690 691//690
+f 692//691 691//691 716//691
+f 693//692 692//692 717//692
+f 694//693 693//693 718//693
+f 695//694 694//694 719//694
+f 695//695 719//695 696//695
+f 673//696 696//696 697//696
+f 698//697 697//697 722//697
+f 699//698 698//698 723//698
+f 699//1174 723//1174 700//1174
+f 701//700 700//700 725//700
+f 702//701 701//701 726//701
+f 702//702 726//702 703//702
+f 703//703 727//703 704//703
+f 704//704 728//704 705//704
+f 705//705 729//705 706//705
+f 707//706 706//706 731//706
+f 707//707 731//707 708//707
+f 709//708 708//708 733//708
+f 709//709 733//709 710//709
+f 710//710 734//710 711//710
+f 711//711 735//711 712//711
+f 713//712 712//712 737//712
+f 713//713 737//713 714//713
+f 715//714 714//714 739//714
+f 715//715 739//715 716//715
+f 717//716 716//716 741//716
+f 717//717 741//717 718//717
+f 719//1175 718//1175 743//1175
+f 719//719 743//719 720//719
+f 720//720 744//720 697//720
+f 722//721 721//721 746//721
+f 723//722 722//722 747//722
+f 723//723 747//723 724//723
+f 724//724 748//724 725//724
+f 725//725 749//725 726//725
+f 727//726 726//726 751//726
+f 727//727 751//727 728//727
+f 729//728 728//728 753//728
+f 729//729 753//729 730//729
+f 731//730 730//730 755//730
+f 731//731 755//731 732//731
+f 732//732 756//732 733//732
+f 734//733 733//733 758//733
+f 735//734 734//734 759//734
+f 735//735 759//735 736//735
+f 736//736 760//736 737//736
+f 737//737 761//737 738//737
+f 739//738 738//738 763//738
+f 739//739 763//739 740//739
+f 741//740 740//740 765//740
+f 742//741 741//741 766//741
+f 743//742 742//742 767//742
+f 743//743 767//743 744//743
+f 744//744 768//744 721//744
+f 746//745 745//745 770//745
+f 747//746 746//746 771//746
+f 748//747 747//747 772//747
+f 748//748 772//748 749//748
+f 750//749 749//749 774//749
+f 750//750 774//750 751//750
+f 752//751 751//751 776//751
+f 753//752 752//752 777//752
+f 754//753 753//753 778//753
+f 755//754 754//754 779//754
+f 755//755 779//755 756//755
+f 756//756 780//756 757//756
+f 758//757 757//757 782//757
+f 759//758 758//758 783//758
+f 759//759 783//759 760//759
+f 760//760 784//760 761//760
+f 761//761 785//761 762//761
+f 762//762 786//762 763//762
+f 763//763 787//763 764//763
+f 764//764 788//764 765//764
+f 766//765 765//765 790//765
+f 766//766 790//766 767//766
+f 767//767 791//767 768//767
+f 768//768 792//768 745//768
+f 770//769 769//769 794//769
+f 770//770 794//770 771//770
+f 771//771 795//771 772//771
+f 772//772 796//772 773//772
+f 773//773 797//773 774//773
+f 774//774 798//774 775//774
+f 775//775 799//775 776//775
+f 776//776 800//776 777//776
+f 777//777 801//777 778//777
+f 778//1179 802//1179 779//1179
+f 779//779 803//779 780//779
+f 780//780 804//780 781//780
+f 781//781 805//781 782//781
+f 783//782 782//782 807//782
+f 784//1180 783//1180 808//1180
+f 784//784 808//784 785//784
+f 786//785 785//785 810//785
+f 787//786 786//786 811//786
+f 788//787 787//787 812//787
+f 788//788 812//788 789//788
+f 790//789 789//789 814//789
+f 790//1209 814//1209 791//1209
+f 792//791 791//791 816//791
+f 792//792 816//792 769//792
+f 794//793 793//793 818//793
+f 794//794 818//794 795//794
+f 796//795 795//795 820//795
+f 797//796 796//796 821//796
+f 798//797 797//797 822//797
+f 799//798 798//798 823//798
+f 800//799 799//799 824//799
+f 801//800 800//800 825//800
+f 801//801 825//801 802//801
+f 803//802 802//802 827//802
+f 803//803 827//803 804//803
+f 805//804 804//804 829//804
+f 805//805 829//805 806//805
+f 806//806 830//806 807//806
+f 807//807 831//807 808//807
+f 808//808 832//808 809//808
+f 809//809 833//809 810//809
+f 810//810 834//810 811//810
+f 811//811 835//811 812//811
+f 812//812 836//812 813//812
+f 813//813 837//813 814//813
+f 814//814 838//814 815//814
+f 816//815 815//815 840//815
+f 816//816 840//816 793//816
+f 817//817 841//817 818//817
+f 818//818 842//818 819//818
+f 820//819 819//819 844//819
+f 820//820 844//820 821//820
+f 822//821 821//821 846//821
+f 822//822 846//822 823//822
+f 823//823 847//823 824//823
+f 825//824 824//824 849//824
+f 825//825 849//825 826//825
+f 826//826 850//826 827//826
+f 827//827 851//827 828//827
+f 829//828 828//828 853//828
+f 829//829 853//829 830//829
+f 831//830 830//830 855//830
+f 832//831 831//831 856//831
+f 833//832 832//832 857//832
+f 833//833 857//833 834//833
+f 835//834 834//834 859//834
+f 835//835 859//835 836//835
+f 836//836 860//836 837//836
+f 837//837 861//837 838//837
+f 838//838 862//838 839//838
+f 839//839 863//839 840//839
+f 817//840 840//840 841//840
+f 841//841 865//841 842//841
+f 843//842 842//842 867//842
+f 843//843 867//843 844//843
+f 844//844 868//844 845//844
+f 846//845 845//845 870//845
+f 847//846 846//846 871//846
+f 847//847 871//847 848//847
+f 849//848 848//848 873//848
+f 850//849 849//849 874//849
+f 850//850 874//850 851//850
+f 851//851 875//851 852//851
+f 853//852 852//852 877//852
+f 853//853 877//853 854//853
+f 855//854 854//854 879//854
+f 856//855 855//855 880//855
+f 857//856 856//856 881//856
+f 857//857 881//857 858//857
+f 859//858 858//858 883//858
+f 860//859 859//859 884//859
+f 860//860 884//860 861//860
+f 861//861 885//861 862//861
+f 863//862 862//862 887//862
+f 863//863 887//863 864//863
+f 841//864 864//864 865//864
+f 865//865 889//865 866//865
+f 867//866 866//866 891//866
+f 867//867 891//867 868//867
+f 869//868 868//868 893//868
+f 869//869 893//869 870//869
+f 870//870 894//870 871//870
+f 871//871 895//871 872//871
+f 872//872 896//872 873//872
+f 874//873 873//873 898//873
+f 875//874 874//874 899//874
+f 876//875 875//875 900//875
+f 877//1210 876//1210 901//1210
+f 877//1211 901//1211 878//1211
+f 879//878 878//878 903//878
+f 879//879 903//879 880//879
+f 881//880 880//880 905//880
+f 881//881 905//881 882//881
+f 883//882 882//882 907//882
+f 884//883 883//883 908//883
+f 884//884 908//884 885//884
+f 885//885 909//885 886//885
+f 887//886 886//886 911//886
+f 887//887 911//887 888//887
+f 865//1212 888//1212 889//1212
+f 889//889 913//889 890//889
+f 890//890 914//890 891//890
+f 891//891 915//891 892//891
+f 893//892 892//892 917//892
+f 893//893 917//893 894//893
+f 894//894 918//894 895//894
+f 895//895 919//895 896//895
+f 897//896 896//896 921//896
+f 897//897 921//897 898//897
+f 899//898 898//898 923//898
+f 899//1185 923//1185 900//1185
+f 900//900 924//900 901//900
+f 901//901 925//901 902//901
+f 903//902 902//902 927//902
+f 903//903 927//903 904//903
+f 905//904 904//904 929//904
+f 905//905 929//905 906//905
+f 906//906 930//906 907//906
+f 908//907 907//907 932//907
+f 909//908 908//908 933//908
+f 909//909 933//909 910//909
+f 911//910 910//910 935//910
+f 911//911 935//911 912//911
+f 889//912 912//912 913//912
+f 914//913 913//913 938//913
+f 915//914 914//914 939//914
+f 915//915 939//915 916//915
+f 916//916 940//916 917//916
+f 918//917 917//917 942//917
+f 918//918 942//918 919//918
+f 919//919 943//919 920//919
+f 920//920 944//920 921//920
+f 921//921 945//921 922//921
+f 922//922 946//922 923//922
+f 923//923 947//923 924//923
+f 925//924 924//924 949//924
+f 926//925 925//925 950//925
+f 926//926 950//926 927//926
+f 928//927 927//927 952//927
+f 928//928 952//928 929//928
+f 930//929 929//929 954//929
+f 930//930 954//930 931//930
+f 932//931 931//931 956//931
+f 932//932 956//932 933//932
+f 933//933 957//933 934//933
+f 935//934 934//934 959//934
+f 935//935 959//935 936//935
+f 936//936 960//936 913//936
+f 938//937 937//937 962//937
+f 939//938 938//938 963//938
+f 940//1187 939//1187 964//1187
+f 941//940 940//940 965//940
+f 942//941 941//941 966//941
+f 943//942 942//942 967//942
+f 944//943 943//943 968//943
+f 945//944 944//944 969//944
+f 946//945 945//945 970//945
+f 947//1186 946//1186 971//1186
+f 948//947 947//947 972//947
+f 948//948 972//948 949//948
+f 950//949 949//949 974//949
+f 950//950 974//950 951//950
+f 951//1188 975//1188 952//1188
+f 952//952 976//952 953//952
+f 953//953 977//953 954//953
+f 954//954 978//954 955//954
+f 955//955 979//955 956//955
+f 956//956 980//956 957//956
+f 957//957 981//957 958//957
+f 959//958 958//958 983//958
+f 959//959 983//959 960//959
+f 960//960 984//960 937//960
+f 962//961 961//961 986//961
+f 963//962 962//962 987//962
+f 964//963 963//963 988//963
+f 964//964 988//964 965//964
+f 965//965 989//965 966//965
+f 967//966 966//966 991//966
+f 967//967 991//967 968//967
+f 968//968 992//968 969//968
+f 969//969 993//969 970//969
+f 970//970 994//970 971//970
+f 972//971 971//971 996//971
+f 972//972 996//972 973//972
+f 974//973 973//973 998//973
+f 974//974 998//974 975//974
+f 975//975 999//975 976//975
+f 976//976 1000//976 977//976
+f 978//977 977//977 1002//977
+f 978//978 1002//978 979//978
+f 979//979 1003//979 980//979
+f 981//980 980//980 1005//980
+f 981//981 1005//981 982//981
+f 982//982 1006//982 983//982
+f 983//983 1007//983 984//983
+f 984//984 1008//984 961//984
+f 985//985 1009//985 986//985
+f 986//986 1010//986 987//986
+f 987//987 1011//987 988//987
+f 988//988 1012//988 989//988
+f 989//989 1013//989 990//989
+f 990//990 1014//990 991//990
+f 991//991 1015//991 992//991
+f 992//992 1016//992 993//992
+f 993//993 1017//993 994//993
+f 994//994 1018//994 995//994
+f 995//995 1019//995 996//995
+f 996//996 1020//996 997//996
+f 997//997 1021//997 998//997
+f 999//998 998//998 1023//998
+f 999//999 1023//999 1000//999
+f 1000//1000 1024//1000 1001//1000
+f 1002//1001 1001//1001 1026//1001
+f 1003//1002 1002//1002 1027//1002
+f 1004//1003 1003//1003 1028//1003
+f 1005//1004 1004//1004 1029//1004
+f 1005//1005 1029//1005 1006//1005
+f 1007//1006 1006//1006 1031//1006
+f 1008//1007 1007//1007 1032//1007
+f 1008//1008 1032//1008 985//1008
+f 1010//1009 1009//1009 1034//1009
+f 1011//1010 1010//1010 1035//1010
+f 1012//1191 1011//1191 1036//1191
+f 1012//1012 1036//1012 1013//1012
+f 1014//1013 1013//1013 1038//1013
+f 1015//1014 1014//1014 1039//1014
+f 1016//1015 1015//1015 1040//1015
+f 1017//1016 1016//1016 1041//1016
+f 1018//1017 1017//1017 1042//1017
+f 1019//1018 1018//1018 1043//1018
+f 1019//1019 1043//1019 1020//1019
+f 1021//1020 1020//1020 1045//1020
+f 1021//1021 1045//1021 1022//1021
+f 1022//1022 1046//1022 1023//1022
+f 1023//1023 1047//1023 1024//1023
+f 1024//1024 1048//1024 1025//1024
+f 1025//1025 1049//1025 1026//1025
+f 1026//1026 1050//1026 1027//1026
+f 1027//1027 1051//1027 1028//1027
+f 1028//1028 1052//1028 1029//1028
+f 1030//1029 1029//1029 1054//1029
+f 1030//1192 1054//1192 1031//1192
+f 1031//1031 1055//1031 1032//1031
+f 1032//1032 1056//1032 1009//1032
+f 1034//1033 1033//1033 1058//1033
+f 1034//1034 1058//1034 1035//1034
+f 1035//1035 1059//1035 1036//1035
+f 1037//1036 1036//1036 1061//1036
+f 1038//1037 1037//1037 1062//1037
+f 1038//1038 1062//1038 1039//1038
+f 1039//1039 1063//1039 1040//1039
+f 1041//1040 1040//1040 1065//1040
+f 1042//1041 1041//1041 1066//1041
+f 1043//1042 1042//1042 1067//1042
+f 1043//1043 1067//1043 1044//1043
+f 1044//1044 1068//1044 1045//1044
+f 1045//1045 1069//1045 1046//1045
+f 1047//1046 1046//1046 1071//1046
+f 1047//1047 1071//1047 1048//1047
+f 1048//1048 1072//1048 1049//1048
+f 1049//1049 1073//1049 1050//1049
+f 1051//1050 1050//1050 1075//1050
+f 1052//1051 1051//1051 1076//1051
+f 1052//1052 1076//1052 1053//1052
+f 1053//1053 1077//1053 1054//1053
+f 1054//1054 1078//1054 1055//1054
+f 1056//1055 1055//1055 1080//1055
+f 1056//1056 1080//1056 1033//1056
+f 1057//1057 1081//1057 1058//1057
+f 1059//1058 1058//1058 1083//1058
+f 1059//1059 1083//1059 1060//1059
+f 1060//1060 1084//1060 1061//1060
+f 1061//1061 1085//1061 1062//1061
+f 1062//1062 1086//1062 1063//1062
+f 1063//1063 1087//1063 1064//1063
+f 1064//1064 1088//1064 1065//1064
+f 1065//1065 1089//1065 1066//1065
+f 1067//1066 1066//1066 1091//1066
+f 1067//1067 1091//1067 1068//1067
+f 1068//1068 1092//1068 1069//1068
+f 1069//1069 1093//1069 1070//1069
+f 1071//1070 1070//1070 1095//1070
+f 1071//1071 1095//1071 1072//1071
+f 1073//1072 1072//1072 1097//1072
+f 1074//1073 1073//1073 1098//1073
+f 1075//1074 1074//1074 1099//1074
+f 1076//1075 1075//1075 1100//1075
+f 1077//1076 1076//1076 1101//1076
+f 1078//1077 1077//1077 1102//1077
+f 1079//1078 1078//1078 1103//1078
+f 1079//1079 1103//1079 1080//1079
+f 1057//1080 1080//1080 1081//1080
+f 1081//1081 1105//1081 1082//1081
+f 1082//1082 1106//1082 1083//1082
+f 1084//1083 1083//1083 1108//1083
+f 1085//1084 1084//1084 1109//1084
+f 1086//1085 1085//1085 1110//1085
+f 1087//1086 1086//1086 1111//1086
+f 1087//1087 1111//1087 1088//1087
+f 1089//1088 1088//1088 1113//1088
+f 1090//1089 1089//1089 1114//1089
+f 1091//1090 1090//1090 1115//1090
+f 1091//1091 1115//1091 1092//1091
+f 1092//1092 1116//1092 1093//1092
+f 1093//1093 1117//1093 1094//1093
+f 1094//1094 1118//1094 1095//1094
+f 1095//1095 1119//1095 1096//1095
+f 1096//1096 1120//1096 1097//1096
+f 1097//1097 1121//1097 1098//1097
+f 1098//1213 1122//1213 1099//1213
+f 1099//1214 1123//1214 1100//1214
+f 1100//1100 1124//1100 1101//1100
+f 1101//1101 1125//1101 1102//1101
+f 1102//1102 1126//1102 1103//1102
+f 1103//1103 1127//1103 1104//1103
+f 1081//1104 1104//1104 1105//1104
+f 1106//1105 1105//1105 1130//1105
+f 1106//1106 1130//1106 1107//1106
+f 1107//1107 1131//1107 1108//1107
+f 1108//1108 1132//1108 1109//1108
+f 1109//1109 1133//1109 1110//1109
+f 1110//1110 1134//1110 1111//1110
+f 1111//1111 1135//1111 1112//1111
+f 1112//1112 1136//1112 1113//1112
+f 1113//1113 1137//1113 1114//1113
+f 1114//1114 1138//1114 1115//1114
+f 1115//1115 1139//1115 1116//1115
+f 1116//1116 1140//1116 1117//1116
+f 1117//1117 1141//1117 1118//1117
+f 1119//1118 1118//1118 1143//1118
+f 1120//1119 1119//1119 1144//1119
+f 1121//1120 1120//1120 1145//1120
+f 1122//1121 1121//1121 1146//1121
+f 1123//1122 1122//1122 1147//1122
+f 1124//1123 1123//1123 1148//1123
+f 1124//1124 1148//1124 1125//1124
+f 1126//1125 1125//1125 1150//1125
+f 1127//1126 1126//1126 1151//1126
+f 1127//1127 1151//1127 1128//1127
+f 1128//1128 1152//1128 1105//1128
+f 1129//1129 1153//1129 1130//1129
+f 1131//1130 1130//1130 1155//1130
+f 1131//1131 1155//1131 1132//1131
+f 1132//1132 1156//1132 1133//1132
+f 1133//1133 1157//1133 1134//1133
+f 1134//1134 1158//1134 1135//1134
+f 1136//1135 1135//1135 1160//1135
+f 1136//1136 1160//1136 1137//1136
+f 1137//1137 1161//1137 1138//1137
+f 1138//1138 1162//1138 1139//1138
+f 1140//1139 1139//1139 1164//1139
+f 1140//1140 1164//1140 1141//1140
+f 1141//1141 1165//1141 1142//1141
+f 1142//1142 1166//1142 1143//1142
+f 1144//1143 1143//1143 1168//1143
+f 1144//1144 1168//1144 1145//1144
+f 1146//1145 1145//1145 1170//1145
+f 1146//1146 1170//1146 1147//1146
+f 1148//1147 1147//1147 1172//1147
+f 1149//1148 1148//1148 1173//1148
+f 1150//1149 1149//1149 1174//1149
+f 1151//1150 1150//1150 1175//1150
+f 1151//1151 1175//1151 1152//1151
+f 1152//1152 1176//1152 1129//1152
+f 1154//12 1153//12 1178//12
+f 1155//11 1154//11 1179//11
+f 1156//10 1155//10 1180//10
+f 1156//9 1180//9 1157//9
+f 1157//8 1181//8 1158//8
+f 1158//7 1182//7 1159//7
+f 1159//6 1183//6 1160//6
+f 1161//5 1160//5 1185//5
+f 1162//4 1161//4 1186//4
+f 1162//3 1186//3 1163//3
+f 1163//2 1187//2 1164//2
+f 1164//1 1188//1 1165//1
+f 1166//24 1165//24 1190//24
+f 1167//23 1166//23 1191//23
+f 1167//22 1191//22 1168//22
+f 1168//21 1192//21 1169//21
+f 1169//20 1193//20 1170//20
+f 1171//19 1170//19 1195//19
+f 1172//18 1171//18 1196//18
+f 1173//17 1172//17 1197//17
+f 1173//16 1197//16 1174//16
+f 1174//15 1198//15 1175//15
+f 1175//14 1199//14 1176//14
+f 1176//13 1200//13 1153//13
+f 1177//36 1201//36 1178//36
+f 1178//35 1202//35 1179//35
+f 1180//34 1179//34 1204//34
+f 1181//33 1180//33 1205//33
+f 1182//32 1181//32 1206//32
+f 1183//31 1182//31 1207//31
+f 1183//30 1207//30 1184//30
+f 1185//29 1184//29 1209//29
+f 1186//28 1185//28 1210//28
+f 1187//27 1186//27 1211//27
+f 1188//26 1187//26 1212//26
+f 1189//25 1188//25 1213//25
+f 1189//48 1213//48 1190//48
+f 1190//47 1214//47 1191//47
+f 1191//46 1215//46 1192//46
+f 1192//45 1216//45 1193//45
+f 1193//44 1217//44 1194//44
+f 1194//43 1218//43 1195//43
+f 1195//42 1219//42 1196//42
+f 1196//41 1220//41 1197//41
+f 1197//40 1221//40 1198//40
+f 1198//39 1222//39 1199//39
+f 1199//38 1223//38 1200//38
+f 1200//37 1224//37 1177//37
+f 1201//60 1225//60 1202//60
+f 1202//59 1226//59 1203//59
+f 1203//58 1227//58 1204//58
+f 1204//57 1228//57 1205//57
+f 1205//56 1229//56 1206//56
+f 1206//55 1230//55 1207//55
+f 1207//54 1231//54 1208//54
+f 1208//53 1232//53 1209//53
+f 1209//52 1233//52 1210//52
+f 1210//51 1234//51 1211//51
+f 1211//50 1235//50 1212//50
+f 1212//49 1236//49 1213//49
+f 1213//72 1237//72 1214//72
+f 1214//71 1238//71 1215//71
+f 1215//70 1239//70 1216//70
+f 1217//69 1216//69 1241//69
+f 1218//68 1217//68 1242//68
+f 1218//67 1242//67 1219//67
+f 1220//66 1219//66 1244//66
+f 1221//65 1220//65 1245//65
+f 1222//64 1221//64 1246//64
+f 1223//63 1222//63 1247//63
+f 1223//62 1247//62 1224//62
+f 1224//61 1248//61 1201//61
+f 1226//84 1225//84 1250//84
+f 1226//83 1250//83 1227//83
+f 1228//82 1227//82 1252//82
+f 1229//81 1228//81 1253//81
+f 1230//80 1229//80 1254//80
+f 1231//79 1230//79 1255//79
+f 1232//78 1231//78 1256//78
+f 1233//77 1232//77 1257//77
+f 1234//76 1233//76 1258//76
+f 1234//75 1258//75 1235//75
+f 1236//74 1235//74 1260//74
+f 1236//73 1260//73 1237//73
+f 1237//96 1261//96 1238//96
+f 1238//95 1262//95 1239//95
+f 1240//94 1239//94 1264//94
+f 1240//93 1264//93 1241//93
+f 1241//92 1265//92 1242//92
+f 1242//91 1266//91 1243//91
+f 1243//90 1267//90 1244//90
+f 1244//89 1268//89 1245//89
+f 1245//88 1269//88 1246//88
+f 1246//87 1270//87 1247//87
+f 1248//86 1247//86 1272//86
+f 1248//85 1272//85 1225//85
+f 1249//108 1273//108 1250//108
+f 1251//107 1250//107 1275//107
+f 1252//106 1251//106 1276//106
+f 1252//105 1276//105 1253//105
+f 1254//104 1253//104 1278//104
+f 1255//103 1254//103 1279//103
+f 1256//102 1255//102 1280//102
+f 1256//101 1280//101 1257//101
+f 1257//100 1281//100 1258//100
+f 1258//99 1282//99 1259//99
+f 1260//98 1259//98 1284//98
+f 1260//97 1284//97 1261//97
+f 1261//120 1285//120 1262//120
+f 1262//119 1286//119 1263//119
+f 1264//118 1263//118 1288//118
+f 1265//117 1264//117 1289//117
+f 1266//116 1265//116 1290//116
+f 1266//115 1290//115 1267//115
+f 1267//114 1291//114 1268//114
+f 1268//113 1292//113 1269//113
+f 1270//112 1269//112 1294//112
+f 1270//111 1294//111 1271//111
+f 1271//110 1295//110 1272//110
+f 1249//109 1272//109 1273//109
+f 1273//132 1297//132 1274//132
+f 1275//131 1274//131 1299//131
+f 1275//1215 1299//1215 1276//1215
+f 1276//129 1300//129 1277//129
+f 1277//128 1301//128 1278//128
+f 1278//127 1302//127 1279//127
+f 1279//126 1303//126 1280//126
+f 1280//125 1304//125 1281//125
+f 1281//124 1305//124 1282//124
+f 1282//123 1306//123 1283//123
+f 1283//122 1307//122 1284//122
+f 1284//121 1308//121 1285//121
+f 1286//144 1285//144 1310//144
+f 1286//143 1310//143 1287//143
+f 1288//142 1287//142 1312//142
+f 1289//141 1288//141 1313//141
+f 1290//140 1289//140 1314//140
+f 1291//139 1290//139 1315//139
+f 1292//138 1291//138 1316//138
+f 1293//137 1292//137 1317//137
+f 1293//136 1317//136 1294//136
+f 1295//1216 1294//1216 1319//1216
+f 1295//134 1319//134 1296//134
+f 1273//133 1296//133 1297//133
+f 1298//156 1297//156 1322//156
+f 1299//155 1298//155 1323//155
+f 1299//154 1323//154 1300//154
+f 1301//153 1300//153 1325//153
+f 1301//152 1325//152 1302//152
+f 1303//151 1302//151 1327//151
+f 1304//150 1303//150 1328//150
+f 1305//149 1304//149 1329//149
+f 1306//148 1305//148 1330//148
+f 1307//147 1306//147 1331//147
+f 1308//146 1307//146 1332//146
+f 1309//145 1308//145 1333//145
+f 1309//168 1333//168 1310//168
+f 1310//167 1334//167 1311//167
+f 1311//166 1335//166 1312//166
+f 1312//165 1336//165 1313//165
+f 1313//164 1337//164 1314//164
+f 1315//163 1314//163 1339//163
+f 1315//162 1339//162 1316//162
+f 1317//161 1316//161 1341//161
+f 1317//160 1341//160 1318//160
+f 1318//159 1342//159 1319//159
+f 1319//158 1343//158 1320//158
+f 1320//157 1344//157 1297//157
+f 1321//180 1345//180 1322//180
+f 1322//179 1346//179 1323//179
+f 1323//178 1347//178 1324//178
+f 1324//177 1348//177 1325//177
+f 1326//176 1325//176 1350//176
+f 1327//175 1326//175 1351//175
+f 1328//174 1327//174 1352//174
+f 1329//173 1328//173 1353//173
+f 1329//172 1353//172 1330//172
+f 1331//171 1330//171 1355//171
+f 1331//1155 1355//1155 1332//1155
+f 1332//169 1356//169 1333//169
+f 1333//192 1357//192 1334//192
+f 1334//191 1358//191 1335//191
+f 1335//190 1359//190 1336//190
+f 1336//189 1360//189 1337//189
+f 1337//188 1361//188 1338//188
+f 1338//187 1362//187 1339//187
+f 1339//186 1363//186 1340//186
+f 1340//185 1364//185 1341//185
+f 1341//184 1365//184 1342//184
+f 1343//183 1342//183 1367//183
+f 1344//182 1343//182 1368//182
+f 1344//181 1368//181 1321//181
+f 1345//204 1369//204 1346//204
+f 1346//203 1370//203 1347//203
+f 1347//1217 1371//1217 1348//1217
+f 1348//201 1372//201 1349//201
+f 1349//200 1373//200 1350//200
+f 1351//199 1350//199 1375//199
+f 1351//198 1375//198 1352//198
+f 1352//197 1376//197 1353//197
+f 1353//196 1377//196 1354//196
+f 1354//195 1378//195 1355//195
+f 1355//194 1379//194 1356//194
+f 1357//193 1356//193 1381//193
+f 1357//216 1381//216 1358//216
+f 1359//215 1358//215 1383//215
+f 1360//214 1359//214 1384//214
+f 1361//213 1360//213 1385//213
+f 1362//212 1361//212 1386//212
+f 1363//211 1362//211 1387//211
+f 1363//210 1387//210 1364//210
+f 1365//209 1364//209 1389//209
+f 1366//208 1365//208 1390//208
+f 1367//1218 1366//1218 1391//1218
+f 1368//206 1367//206 1392//206
+f 1368//205 1392//205 1345//205
+f 1369//228 1393//228 1370//228
+f 1370//227 1394//227 1371//227
+f 1372//226 1371//226 1396//226
+f 1372//225 1396//225 1373//225
+f 1373//224 1397//224 1374//224
+f 1375//223 1374//223 1399//223
+f 1375//222 1399//222 1376//222
+f 1377//221 1376//221 1401//221
+f 1377//220 1401//220 1378//220
+f 1379//219 1378//219 1403//219
+f 1379//218 1403//218 1380//218
+f 1380//217 1404//217 1381//217
+f 1381//240 1405//240 1382//240
+f 1382//239 1406//239 1383//239
+f 1383//238 1407//238 1384//238
+f 1385//237 1384//237 1409//237
+f 1385//236 1409//236 1386//236
+f 1386//235 1410//235 1387//235
+f 1387//234 1411//234 1388//234
+f 1389//233 1388//233 1413//233
+f 1389//232 1413//232 1390//232
+f 1390//231 1414//231 1391//231
+f 1392//230 1391//230 1416//230
+f 1369//229 1392//229 1393//229
+f 1393//252 1417//252 1394//252
+f 1394//251 1418//251 1395//251
+f 1396//250 1395//250 1420//250
+f 1396//249 1420//249 1397//249
+f 1398//248 1397//248 1422//248
+f 1399//247 1398//247 1423//247
+f 1399//246 1423//246 1400//246
+f 1401//245 1400//245 1425//245
+f 1402//244 1401//244 1426//244
+f 1402//243 1426//243 1403//243
+f 1404//242 1403//242 1428//242
+f 1405//241 1404//241 1429//241
+f 1405//264 1429//264 1406//264
+f 1406//263 1430//263 1407//263
+f 1408//262 1407//262 1432//262
+f 1408//261 1432//261 1409//261
+f 1409//260 1433//260 1410//260
+f 1411//259 1410//259 1435//259
+f 1411//258 1435//258 1412//258
+f 1412//257 1436//257 1413//257
+f 1414//256 1413//256 1438//256
+f 1414//255 1438//255 1415//255
+f 1415//254 1439//254 1416//254
+f 1416//253 1440//253 1393//253
+f 1418//1158 1417//1158 1442//1158
+f 1418//275 1442//275 1419//275
+f 1420//274 1419//274 1444//274
+f 1420//273 1444//273 1421//273
+f 1421//272 1445//272 1422//272
+f 1423//271 1422//271 1447//271
+f 1424//270 1423//270 1448//270
+f 1424//269 1448//269 1425//269
+f 1426//268 1425//268 1450//268
+f 1426//267 1450//267 1427//267
+f 1428//266 1427//266 1452//266
+f 1428//265 1452//265 1429//265
+f 1430//288 1429//288 1454//288
+f 1431//287 1430//287 1455//287
+f 1432//286 1431//286 1456//286
+f 1433//285 1432//285 1457//285
+f 1433//284 1457//284 1434//284
+f 1434//283 1458//283 1435//283
+f 1435//282 1459//282 1436//282
+f 1436//281 1460//281 1437//281
+f 1438//280 1437//280 1462//280
+f 1438//279 1462//279 1439//279
+f 1440//278 1439//278 1464//278
+f 1440//277 1464//277 1417//277
+f 1442//300 1441//300 1466//300
+f 1442//299 1466//299 1443//299
+f 1443//298 1467//298 1444//298
+f 1444//297 1468//297 1445//297
+f 1445//296 1469//296 1446//296
+f 1446//295 1470//295 1447//295
+f 1447//294 1471//294 1448//294
+f 1448//293 1472//293 1449//293
+f 1449//292 1473//292 1450//292
+f 1450//291 1474//291 1451//291
+f 1452//290 1451//290 1476//290
+f 1453//289 1452//289 1477//289
+f 1453//312 1477//312 1454//312
+f 1454//311 1478//311 1455//311
+f 1455//310 1479//310 1456//310
+f 1457//309 1456//309 1481//309
+f 1457//308 1481//308 1458//308
+f 1458//307 1482//307 1459//307
+f 1459//306 1483//306 1460//306
+f 1461//305 1460//305 1485//305
+f 1462//304 1461//304 1486//304
+f 1463//303 1462//303 1487//303
+f 1464//302 1463//302 1488//302
+f 1464//301 1488//301 1441//301
+f 1466//324 1465//324 1490//324
+f 1466//1162 1490//1162 1467//1162
+f 1468//322 1467//322 1492//322
+f 1469//321 1468//321 1493//321
+f 1470//320 1469//320 1494//320
+f 1471//319 1470//319 1495//319
+f 1472//318 1471//318 1496//318
+f 1473//317 1472//317 1497//317
+f 1474//316 1473//316 1498//316
+f 1475//315 1474//315 1499//315
+f 1476//314 1475//314 1500//314
+f 1476//313 1500//313 1477//313
+f 1477//336 1501//336 1478//336
+f 1478//335 1502//335 1479//335
+f 1479//334 1503//334 1480//334
+f 1480//333 1504//333 1481//333
+f 1481//332 1505//332 1482//332
+f 1482//331 1506//331 1483//331
+f 1483//330 1507//330 1484//330
+f 1484//329 1508//329 1485//329
+f 1485//328 1509//328 1486//328
+f 1486//327 1510//327 1487//327
+f 1487//326 1511//326 1488//326
+f 1488//325 1512//325 1465//325
+f 1489//348 1513//348 1490//348
+f 1491//347 1490//347 1515//347
+f 1491//346 1515//346 1492//346
+f 1493//345 1492//345 1517//345
+f 1493//344 1517//344 1494//344
+f 1494//343 1518//343 1495//343
+f 1495//342 1519//342 1496//342
+f 1496//341 1520//341 1497//341
+f 1497//340 1521//340 1498//340
+f 1498//339 1522//339 1499//339
+f 1499//338 1523//338 1500//338
+f 1500//337 1524//337 1501//337
+f 1501//360 1525//360 1502//360
+f 1503//359 1502//359 1527//359
+f 1504//358 1503//358 1528//358
+f 1505//357 1504//357 1529//357
+f 1506//356 1505//356 1530//356
+f 1506//355 1530//355 1507//355
+f 1508//354 1507//354 1532//354
+f 1509//353 1508//353 1533//353
+f 1509//352 1533//352 1510//352
+f 1511//351 1510//351 1535//351
+f 1511//350 1535//350 1512//350
+f 1489//349 1512//349 1513//349
+f 1513//372 1537//372 1514//372
+f 1515//371 1514//371 1539//371
+f 1515//1219 1539//1219 1516//1219
+f 1517//369 1516//369 1541//369
+f 1517//368 1541//368 1518//368
+f 1519//367 1518//367 1543//367
+f 1520//366 1519//366 1544//366
+f 1521//365 1520//365 1545//365
+f 1521//364 1545//364 1522//364
+f 1522//1167 1546//1167 1523//1167
+f 1524//362 1523//362 1548//362
+f 1525//361 1524//361 1549//361
+f 1525//384 1549//384 1526//384
+f 1526//383 1550//383 1527//383
+f 1527//1199 1551//1199 1528//1199
+f 1528//381 1552//381 1529//381
+f 1529//380 1553//380 1530//380
+f 1530//379 1554//379 1531//379
+f 1531//378 1555//378 1532//378
+f 1532//377 1556//377 1533//377
+f 1533//376 1557//376 1534//376
+f 1535//1220 1534//1220 1559//1220
+f 1535//374 1559//374 1536//374
+f 1513//373 1536//373 1537//373
+f 1538//396 1537//396 1562//396
+f 1538//395 1562//395 1539//395
+f 1539//394 1563//394 1540//394
+f 1541//393 1540//393 1565//393
+f 1541//392 1565//392 1542//392
+f 1542//391 1566//391 1543//391
+f 1543//390 1567//390 1544//390
+f 1544//389 1568//389 1545//389
+f 1545//388 1569//388 1546//388
+f 1546//387 1570//387 1547//387
+f 1548//386 1547//386 1572//386
+f 1549//385 1548//385 1573//385
+f 1549//408 1573//408 1550//408
+f 1550//407 1574//407 1551//407
+f 1552//406 1551//406 1576//406
+f 1553//405 1552//405 1577//405
+f 1554//404 1553//404 1578//404
+f 1555//403 1554//403 1579//403
+f 1555//402 1579//402 1556//402
+f 1557//401 1556//401 1581//401
+f 1557//400 1581//400 1558//400
+f 1559//399 1558//399 1583//399
+f 1560//398 1559//398 1584//398
+f 1537//397 1560//397 1561//397
+f 1562//420 1561//420 1586//420
+f 1562//419 1586//419 1563//419
+f 1564//418 1563//418 1588//418
+f 1565//417 1564//417 1589//417
+f 1566//416 1565//416 1590//416
+f 1566//415 1590//415 1567//415
+f 1567//414 1591//414 1568//414
+f 1568//413 1592//413 1569//413
+f 1570//412 1569//412 1594//412
+f 1570//411 1594//411 1571//411
+f 1572//410 1571//410 1596//410
+f 1573//409 1572//409 1597//409
+f 1573//432 1597//432 1574//432
+f 1574//431 1598//431 1575//431
+f 1576//430 1575//430 1600//430
+f 1577//429 1576//429 1601//429
+f 1578//428 1577//428 1602//428
+f 1578//427 1602//427 1579//427
+f 1580//426 1579//426 1604//426
+f 1580//425 1604//425 1581//425
+f 1581//424 1605//424 1582//424
+f 1582//423 1606//423 1583//423
+f 1584//422 1583//422 1608//422
+f 1561//421 1584//421 1585//421
+f 1585//444 1609//444 1586//444
+f 1586//443 1610//443 1587//443
+f 1588//1203 1587//1203 1612//1203
+f 1588//441 1612//441 1589//441
+f 1590//440 1589//440 1614//440
+f 1590//439 1614//439 1591//439
+f 1592//438 1591//438 1616//438
+f 1593//437 1592//437 1617//437
+f 1594//436 1593//436 1618//436
+f 1594//1168 1618//1168 1595//1168
+f 1595//434 1619//434 1596//434
+f 1596//433 1620//433 1597//433
+f 1598//456 1597//456 1622//456
+f 1598//455 1622//455 1599//455
+f 1600//1169 1599//1169 1624//1169
+f 1600//453 1624//453 1601//453
+f 1601//452 1625//452 1602//452
+f 1602//451 1626//451 1603//451
+f 1603//450 1627//450 1604//450
+f 1605//449 1604//449 1629//449
+f 1606//448 1605//448 1630//448
+f 1606//1204 1630//1204 1607//1204
+f 1608//446 1607//446 1632//446
+f 1585//445 1608//445 1609//445
+f 1610//468 1609//468 1634//468
+f 1610//467 1634//467 1611//467
+f 1612//466 1611//466 1636//466
+f 1612//465 1636//465 1613//465
+f 1613//464 1637//464 1614//464
+f 1615//463 1614//463 1639//463
+f 1615//462 1639//462 1616//462
+f 1617//461 1616//461 1641//461
+f 1617//460 1641//460 1618//460
+f 1619//459 1618//459 1643//459
+f 1619//458 1643//458 1620//458
+f 1620//457 1644//457 1621//457
+f 1622//480 1621//480 1646//480
+f 1622//479 1646//479 1623//479
+f 1623//478 1647//478 1624//478
+f 1624//477 1648//477 1625//477
+f 1625//476 1649//476 1626//476
+f 1627//475 1626//475 1651//475
+f 1627//474 1651//474 1628//474
+f 1629//473 1628//473 1653//473
+f 1629//472 1653//472 1630//472
+f 1630//471 1654//471 1631//471
+f 1632//470 1631//470 1656//470
+f 1632//469 1656//469 1609//469
+f 1633//492 1657//492 1634//492
+f 1634//491 1658//491 1635//491
+f 1636//490 1635//490 1660//490
+f 1637//489 1636//489 1661//489
+f 1637//488 1661//488 1638//488
+f 1638//487 1662//487 1639//487
+f 1640//486 1639//486 1664//486
+f 1641//485 1640//485 1665//485
+f 1641//484 1665//484 1642//484
+f 1642//483 1666//483 1643//483
+f 1644//482 1643//482 1668//482
+f 1645//481 1644//481 1669//481
+f 1645//504 1669//504 1646//504
+f 1646//503 1670//503 1647//503
+f 1647//502 1671//502 1648//502
+f 1649//501 1648//501 1673//501
+f 1649//500 1673//500 1650//500
+f 1650//499 1674//499 1651//499
+f 1652//498 1651//498 1676//498
+f 1653//497 1652//497 1677//497
+f 1653//496 1677//496 1654//496
+f 1654//495 1678//495 1655//495
+f 1656//494 1655//494 1680//494
+f 1656//493 1680//493 1633//493
+f 1657//516 1681//516 1658//516
+f 1658//515 1682//515 1659//515
+f 1659//514 1683//514 1660//514
+f 1660//513 1684//513 1661//513
+f 1661//512 1685//512 1662//512
+f 1663//511 1662//511 1687//511
+f 1663//510 1687//510 1664//510
+f 1665//509 1664//509 1689//509
+f 1665//508 1689//508 1666//508
+f 1666//507 1690//507 1667//507
+f 1667//506 1691//506 1668//506
+f 1668//505 1692//505 1669//505
+f 1670//528 1669//528 1694//528
+f 1671//527 1670//527 1695//527
+f 1671//526 1695//526 1672//526
+f 1673//525 1672//525 1697//525
+f 1673//524 1697//524 1674//524
+f 1675//523 1674//523 1699//523
+f 1675//1221 1699//1221 1676//1221
+f 1677//521 1676//521 1701//521
+f 1678//520 1677//520 1702//520
+f 1679//519 1678//519 1703//519
+f 1680//518 1679//518 1704//518
+f 1657//517 1680//517 1681//517
+f 1681//540 1705//540 1682//540
+f 1683//539 1682//539 1707//539
+f 1684//538 1683//538 1708//538
+f 1684//537 1708//537 1685//537
+f 1686//536 1685//536 1710//536
+f 1686//535 1710//535 1687//535
+f 1687//534 1711//534 1688//534
+f 1689//533 1688//533 1713//533
+f 1689//532 1713//532 1690//532
+f 1691//531 1690//531 1715//531
+f 1691//530 1715//530 1692//530
+f 1692//529 1716//529 1693//529
+f 1694//552 1693//552 1718//552
+f 1695//551 1694//551 1719//551
+f 1695//550 1719//550 1696//550
+f 1696//549 1720//549 1697//549
+f 1697//548 1721//548 1698//548
+f 1699//547 1698//547 1723//547
+f 1700//546 1699//546 1724//546
+f 1700//545 1724//545 1701//545
+f 1702//544 1701//544 1726//544
+f 1702//543 1726//543 1703//543
+f 1703//542 1727//542 1704//542
+f 1681//541 1704//541 1705//541
+f 1705//564 1729//564 1706//564
+f 1706//563 1730//563 1707//563
+f 1708//562 1707//562 1732//562
+f 1709//561 1708//561 1733//561
+f 1710//560 1709//560 1734//560
+f 1710//559 1734//559 1711//559
+f 1712//558 1711//558 1736//558
+f 1713//557 1712//557 1737//557
+f 1714//556 1713//556 1738//556
+f 1714//555 1738//555 1715//555
+f 1715//554 1739//554 1716//554
+f 1717//553 1716//553 1741//553
+f 1717//576 1741//576 1718//576
+f 1719//575 1718//575 1743//575
+f 1719//574 1743//574 1720//574
+f 1721//573 1720//573 1745//573
+f 1721//572 1745//572 1722//572
+f 1722//571 1746//571 1723//571
+f 1724//570 1723//570 1748//570
+f 1724//569 1748//569 1725//569
+f 1725//568 1749//568 1726//568
+f 1726//567 1750//567 1727//567
+f 1728//566 1727//566 1752//566
+f 1705//565 1728//565 1729//565
+f 1729//588 1753//588 1730//588
+f 1730//587 1754//587 1731//587
+f 1732//586 1731//586 1756//586
+f 1732//585 1756//585 1733//585
+f 1733//584 1757//584 1734//584
+f 1734//583 1758//583 1735//583
+f 1735//582 1759//582 1736//582
+f 1736//581 1760//581 1737//581
+f 1737//580 1761//580 1738//580
+f 1739//579 1738//579 1763//579
+f 1739//578 1763//578 1740//578
+f 1741//577 1740//577 1765//577
+f 1741//600 1765//600 1742//600
+f 1743//599 1742//599 1767//599
+f 1743//598 1767//598 1744//598
+f 1745//597 1744//597 1769//597
+f 1746//596 1745//596 1770//596
+f 1747//595 1746//595 1771//595
+f 1748//594 1747//594 1772//594
+f 1748//593 1772//593 1749//593
+f 1750//592 1749//592 1774//592
+f 1750//591 1774//591 1751//591
+f 1752//590 1751//590 1776//590
+f 1752//589 1776//589 1729//589
+f 1753//612 1777//612 1754//612
+f 1754//611 1778//611 1755//611
+f 1755//610 1779//610 1756//610
+f 1757//609 1756//609 1781//609
+f 1757//608 1781//608 1758//608
+f 1759//607 1758//607 1783//607
+f 1759//606 1783//606 1760//606
+f 1761//605 1760//605 1785//605
+f 1762//604 1761//604 1786//604
+f 1762//603 1786//603 1763//603
+f 1763//602 1787//602 1764//602
+f 1765//601 1764//601 1789//601
+f 1765//624 1789//624 1766//624
+f 1767//623 1766//623 1791//623
+f 1768//622 1767//622 1792//622
+f 1768//621 1792//621 1769//621
+f 1769//620 1793//620 1770//620
+f 1771//619 1770//619 1795//619
+f 1771//618 1795//618 1772//618
+f 1772//617 1796//617 1773//617
+f 1773//616 1797//616 1774//616
+f 1774//615 1798//615 1775//615
+f 1776//614 1775//614 1800//614
+f 1753//613 1776//613 1777//613
+f 1778//636 1777//636 1802//636
+f 1778//635 1802//635 1779//635
+f 1779//634 1803//634 1780//634
+f 1781//633 1780//633 1805//633
+f 1782//632 1781//632 1806//632
+f 1783//1172 1782//1172 1807//1172
+f 1783//630 1807//630 1784//630
+f 1784//629 1808//629 1785//629
+f 1785//628 1809//628 1786//628
+f 1787//627 1786//627 1811//627
+f 1787//626 1811//626 1788//626
+f 1789//625 1788//625 1813//625
+f 1789//648 1813//648 1790//648
+f 1790//647 1814//647 1791//647
+f 1791//646 1815//646 1792//646
+f 1792//645 1816//645 1793//645
+f 1794//644 1793//644 1818//644
+f 1794//643 1818//643 1795//643
+f 1795//1222 1819//1222 1796//1222
+f 1796//641 1820//641 1797//641
+f 1797//640 1821//640 1798//640
+f 1799//639 1798//639 1823//639
+f 1800//638 1799//638 1824//638
+f 1800//637 1824//637 1777//637
+f 1802//660 1801//660 1826//660
+f 1802//659 1826//659 1803//659
+f 1803//658 1827//658 1804//658
+f 1805//657 1804//657 1829//657
+f 1805//656 1829//656 1806//656
+f 1806//655 1830//655 1807//655
+f 1807//654 1831//654 1808//654
+f 1808//653 1832//653 1809//653
+f 1809//652 1833//652 1810//652
+f 1810//651 1834//651 1811//651
+f 1811//650 1835//650 1812//650
+f 1812//649 1836//649 1813//649
+f 1814//672 1813//672 1838//672
+f 1815//671 1814//671 1839//671
+f 1816//670 1815//670 1840//670
+f 1817//669 1816//669 1841//669
+f 1818//668 1817//668 1842//668
+f 1819//667 1818//667 1843//667
+f 1820//666 1819//666 1844//666
+f 1821//665 1820//665 1845//665
+f 1821//664 1845//664 1822//664
+f 1823//663 1822//663 1847//663
+f 1824//662 1823//662 1848//662
+f 1824//661 1848//661 1801//661
+f 1826//684 1825//684 1850//684
+f 1826//683 1850//683 1827//683
+f 1827//682 1851//682 1828//682
+f 1829//681 1828//681 1853//681
+f 1830//680 1829//680 1854//680
+f 1831//679 1830//679 1855//679
+f 1832//678 1831//678 1856//678
+f 1832//677 1856//677 1833//677
+f 1834//676 1833//676 1858//676
+f 1835//675 1834//675 1859//675
+f 1835//674 1859//674 1836//674
+f 1836//673 1860//673 1837//673
+f 1838//696 1837//696 1862//696
+f 1839//695 1838//695 1863//695
+f 1839//694 1863//694 1840//694
+f 1840//693 1864//693 1841//693
+f 1842//692 1841//692 1866//692
+f 1842//691 1866//691 1843//691
+f 1844//690 1843//690 1868//690
+f 1844//689 1868//689 1845//689
+f 1845//688 1869//688 1846//688
+f 1847//687 1846//687 1871//687
+f 1848//686 1847//686 1872//686
+f 1848//685 1872//685 1825//685
+f 1849//708 1873//708 1850//708
+f 1850//707 1874//707 1851//707
+f 1851//706 1875//706 1852//706
+f 1852//705 1876//705 1853//705
+f 1854//704 1853//704 1878//704
+f 1854//703 1878//703 1855//703
+f 1856//702 1855//702 1880//702
+f 1856//701 1880//701 1857//701
+f 1857//700 1881//700 1858//700
+f 1858//1174 1882//1174 1859//1174
+f 1859//698 1883//698 1860//698
+f 1861//697 1860//697 1885//697
+f 1861//720 1885//720 1862//720
+f 1863//719 1862//719 1887//719
+f 1864//1175 1863//1175 1888//1175
+f 1865//717 1864//717 1889//717
+f 1866//716 1865//716 1890//716
+f 1866//715 1890//715 1867//715
+f 1868//714 1867//714 1892//714
+f 1868//713 1892//713 1869//713
+f 1870//712 1869//712 1894//712
+f 1871//711 1870//711 1895//711
+f 1871//710 1895//710 1872//710
+f 1872//709 1896//709 1849//709
+f 1873//732 1897//732 1874//732
+f 1875//731 1874//731 1899//731
+f 1876//730 1875//730 1900//730
+f 1877//729 1876//729 1901//729
+f 1878//728 1877//728 1902//728
+f 1879//727 1878//727 1903//727
+f 1880//726 1879//726 1904//726
+f 1881//725 1880//725 1905//725
+f 1881//724 1905//724 1882//724
+f 1883//723 1882//723 1907//723
+f 1884//722 1883//722 1908//722
+f 1884//721 1908//721 1885//721
+f 1885//744 1909//744 1886//744
+f 1886//743 1910//743 1887//743
+f 1887//742 1911//742 1888//742
+f 1888//741 1912//741 1889//741
+f 1889//740 1913//740 1890//740
+f 1890//739 1914//739 1891//739
+f 1891//738 1915//738 1892//738
+f 1892//737 1916//737 1893//737
+f 1893//736 1917//736 1894//736
+f 1895//735 1894//735 1919//735
+f 1895//734 1919//734 1896//734
+f 1873//733 1896//733 1897//733
+f 1897//756 1921//756 1898//756
+f 1899//755 1898//755 1923//755
+f 1899//754 1923//754 1900//754
+f 1900//753 1924//753 1901//753
+f 1901//752 1925//752 1902//752
+f 1902//751 1926//751 1903//751
+f 1903//750 1927//750 1904//750
+f 1905//749 1904//749 1929//749
+f 1905//748 1929//748 1906//748
+f 1907//747 1906//747 1931//747
+f 1907//746 1931//746 1908//746
+f 1909//745 1908//745 1933//745
+f 1909//768 1933//768 1910//768
+f 1911//767 1910//767 1935//767
+f 1911//766 1935//766 1912//766
+f 1913//765 1912//765 1937//765
+f 1913//764 1937//764 1914//764
+f 1915//763 1914//763 1939//763
+f 1916//762 1915//762 1940//762
+f 1916//761 1940//761 1917//761
+f 1918//760 1917//760 1942//760
+f 1919//759 1918//759 1943//759
+f 1919//758 1943//758 1920//758
+f 1897//757 1920//757 1921//757
+f 1922//780 1921//780 1946//780
+f 1923//779 1922//779 1947//779
+f 1924//778 1923//778 1948//778
+f 1925//777 1924//777 1949//777
+f 1925//776 1949//776 1926//776
+f 1927//775 1926//775 1951//775
+f 1928//774 1927//774 1952//774
+f 1928//773 1952//773 1929//773
+f 1930//772 1929//772 1954//772
+f 1931//771 1930//771 1955//771
+f 1931//770 1955//770 1932//770
+f 1933//769 1932//769 1957//769
+f 1933//792 1957//792 1934//792
+f 1935//791 1934//791 1959//791
+f 1935//790 1959//790 1936//790
+f 1936//789 1960//789 1937//789
+f 1937//788 1961//788 1938//788
+f 1938//787 1962//787 1939//787
+f 1939//786 1963//786 1940//786
+f 1941//785 1940//785 1965//785
+f 1941//784 1965//784 1942//784
+f 1942//783 1966//783 1943//783
+f 1943//782 1967//782 1944//782
+f 1944//781 1968//781 1921//781
+f 1945//804 1969//804 1946//804
+f 1947//803 1946//803 1971//803
+f 1947//802 1971//802 1948//802
+f 1949//801 1948//801 1973//801
+f 1949//800 1973//800 1950//800
+f 1950//799 1974//799 1951//799
+f 1952//798 1951//798 1976//798
+f 1953//797 1952//797 1977//797
+f 1953//796 1977//796 1954//796
+f 1955//795 1954//795 1979//795
+f 1955//794 1979//794 1956//794
+f 1956//793 1980//793 1957//793
+f 1958//816 1957//816 1982//816
+f 1959//815 1958//815 1983//815
+f 1959//814 1983//814 1960//814
+f 1960//813 1984//813 1961//813
+f 1961//812 1985//812 1962//812
+f 1962//811 1986//811 1963//811
+f 1964//810 1963//810 1988//810
+f 1964//809 1988//809 1965//809
+f 1965//808 1989//808 1966//808
+f 1967//807 1966//807 1991//807
+f 1967//806 1991//806 1968//806
+f 1945//805 1968//805 1969//805
+f 1969//828 1993//828 1970//828
+f 1971//827 1970//827 1995//827
+f 1971//826 1995//826 1972//826
+f 1972//825 1996//825 1973//825
+f 1974//824 1973//824 1998//824
+f 1975//823 1974//823 1999//823
+f 1975//822 1999//822 1976//822
+f 1976//821 2000//821 1977//821
+f 1977//820 2001//820 1978//820
+f 1978//819 2002//819 1979//819
+f 1979//818 2003//818 1980//818
+f 1980//817 2004//817 1981//817
+f 1982//840 1981//840 2006//840
+f 1983//839 1982//839 2007//839
+f 1984//838 1983//838 2008//838
+f 1985//837 1984//837 2009//837
+f 1985//836 2009//836 1986//836
+f 1987//835 1986//835 2011//835
+f 1987//834 2011//834 1988//834
+f 1988//833 2012//833 1989//833
+f 1990//832 1989//832 2014//832
+f 1991//831 1990//831 2015//831
+f 1991//830 2015//830 1992//830
+f 1992//829 2016//829 1969//829
+f 1993//852 2017//852 1994//852
+f 1995//851 1994//851 2019//851
+f 1995//850 2019//850 1996//850
+f 1997//849 1996//849 2021//849
+f 1997//848 2021//848 1998//848
+f 1999//847 1998//847 2023//847
+f 2000//846 1999//846 2024//846
+f 2001//845 2000//845 2025//845
+f 2002//844 2001//844 2026//844
+f 2002//843 2026//843 2003//843
+f 2004//842 2003//842 2028//842
+f 2004//1223 2028//1223 2005//1223
+f 2006//1184 2005//1184 2030//1184
+f 2006//863 2030//863 2007//863
+f 2007//862 2031//862 2008//862
+f 2008//861 2032//861 2009//861
+f 2009//860 2033//860 2010//860
+f 2010//859 2034//859 2011//859
+f 2012//858 2011//858 2036//858
+f 2013//857 2012//857 2037//857
+f 2013//856 2037//856 2014//856
+f 2015//855 2014//855 2039//855
+f 2015//854 2039//854 2016//854
+f 2016//1224 2040//1224 1993//1224
+f 2017//876 2041//876 2018//876
+f 2019//875 2018//875 2043//875
+f 2019//874 2043//874 2020//874
+f 2021//873 2020//873 2045//873
+f 2021//872 2045//872 2022//872
+f 2022//871 2046//871 2023//871
+f 2024//870 2023//870 2048//870
+f 2025//869 2024//869 2049//869
+f 2025//868 2049//868 2026//868
+f 2026//867 2050//867 2027//867
+f 2028//866 2027//866 2052//866
+f 2028//865 2052//865 2029//865
+f 2030//1212 2029//1212 2054//1212
+f 2030//887 2054//887 2031//887
+f 2032//886 2031//886 2056//886
+f 2032//885 2056//885 2033//885
+f 2033//884 2057//884 2034//884
+f 2035//883 2034//883 2059//883
+f 2035//882 2059//882 2036//882
+f 2037//881 2036//881 2061//881
+f 2037//880 2061//880 2038//880
+f 2039//879 2038//879 2063//879
+f 2039//878 2063//878 2040//878
+f 2017//1211 2040//1211 2041//1211
+f 2041//900 2065//900 2042//900
+f 2043//1185 2042//1185 2067//1185
+f 2043//898 2067//898 2044//898
+f 2044//897 2068//897 2045//897
+f 2046//896 2045//896 2070//896
+f 2046//895 2070//895 2047//895
+f 2047//894 2071//894 2048//894
+f 2049//893 2048//893 2073//893
+f 2049//892 2073//892 2050//892
+f 2051//891 2050//891 2075//891
+f 2051//890 2075//890 2052//890
+f 2052//889 2076//889 2053//889
+f 2054//912 2053//912 2078//912
+f 2055//911 2054//911 2079//911
+f 2055//910 2079//910 2056//910
+f 2057//909 2056//909 2081//909
+f 2057//908 2081//908 2058//908
+f 2059//907 2058//907 2083//907
+f 2060//906 2059//906 2084//906
+f 2060//905 2084//905 2061//905
+f 2061//904 2085//904 2062//904
+f 2063//903 2062//903 2087//903
+f 2063//902 2087//902 2064//902
+f 2064//901 2088//901 2041//901
+f 2065//924 2089//924 2066//924
+f 2067//923 2066//923 2091//923
+f 2067//922 2091//922 2068//922
+f 2069//921 2068//921 2093//921
+f 2070//920 2069//920 2094//920
+f 2071//919 2070//919 2095//919
+f 2071//918 2095//918 2072//918
+f 2072//917 2096//917 2073//917
+f 2073//916 2097//916 2074//916
+f 2074//915 2098//915 2075//915
+f 2076//914 2075//914 2100//914
+f 2076//913 2100//913 2077//913
+f 2077//936 2101//936 2078//936
+f 2078//935 2102//935 2079//935
+f 2080//934 2079//934 2104//934
+f 2081//933 2080//933 2105//933
+f 2082//932 2081//932 2106//932
+f 2083//931 2082//931 2107//931
+f 2083//930 2107//930 2084//930
+f 2084//929 2108//929 2085//929
+f 2085//928 2109//928 2086//928
+f 2087//927 2086//927 2111//927
+f 2087//926 2111//926 2088//926
+f 2065//925 2088//925 2089//925
+f 2090//948 2089//948 2114//948
+f 2091//947 2090//947 2115//947
+f 2091//946 2115//946 2092//946
+f 2092//945 2116//945 2093//945
+f 2093//944 2117//944 2094//944
+f 2094//943 2118//943 2095//943
+f 2095//942 2119//942 2096//942
+f 2096//941 2120//941 2097//941
+f 2098//940 2097//940 2122//940
+f 2099//1187 2098//1187 2123//1187
+f 2100//938 2099//938 2124//938
+f 2100//937 2124//937 2101//937
+f 2102//960 2101//960 2126//960
+f 2102//959 2126//959 2103//959
+f 2103//958 2127//958 2104//958
+f 2104//957 2128//957 2105//957
+f 2106//956 2105//956 2130//956
+f 2107//955 2106//955 2131//955
+f 2108//954 2107//954 2132//954
+f 2109//953 2108//953 2133//953
+f 2110//952 2109//952 2134//952
+f 2111//951 2110//951 2135//951
+f 2111//950 2135//950 2112//950
+f 2112//949 2136//949 2089//949
+f 2114//972 2113//972 2138//972
+f 2114//1225 2138//1225 2115//1225
+f 2116//970 2115//970 2140//970
+f 2117//969 2116//969 2141//969
+f 2117//968 2141//968 2118//968
+f 2118//967 2142//967 2119//967
+f 2120//966 2119//966 2144//966
+f 2120//965 2144//965 2121//965
+f 2121//964 2145//964 2122//964
+f 2122//963 2146//963 2123//963
+f 2123//962 2147//962 2124//962
+f 2125//961 2124//961 2149//961
+f 2125//984 2149//984 2126//984
+f 2126//1226 2150//1226 2127//1226
+f 2128//982 2127//982 2152//982
+f 2129//981 2128//981 2153//981
+f 2130//980 2129//980 2154//980
+f 2130//979 2154//979 2131//979
+f 2132//978 2131//978 2156//978
+f 2132//977 2156//977 2133//977
+f 2133//976 2157//976 2134//976
+f 2134//975 2158//975 2135//975
+f 2135//1227 2159//1227 2136//1227
+f 2136//973 2160//973 2113//973
+f 2138//996 2137//996 2162//996
+f 2138//995 2162//995 2139//995
+f 2139//994 2163//994 2140//994
+f 2140//993 2164//993 2141//993
+f 2141//992 2165//992 2142//992
+f 2142//991 2166//991 2143//991
+f 2143//990 2167//990 2144//990
+f 2144//989 2168//989 2145//989
+f 2146//988 2145//988 2170//988
+f 2146//987 2170//987 2147//987
+f 2148//986 2147//986 2172//986
+f 2149//985 2148//985 2173//985
+f 2149//1008 2173//1008 2150//1008
+f 2150//1007 2174//1007 2151//1007
+f 2152//1006 2151//1006 2176//1006
+f 2152//1005 2176//1005 2153//1005
+f 2154//1004 2153//1004 2178//1004
+f 2155//1003 2154//1003 2179//1003
+f 2156//1002 2155//1002 2180//1002
+f 2156//1001 2180//1001 2157//1001
+f 2158//1000 2157//1000 2182//1000
+f 2158//999 2182//999 2159//999
+f 2160//998 2159//998 2184//998
+f 2160//997 2184//997 2137//997
+f 2162//1020 2161//1020 2186//1020
+f 2162//1019 2186//1019 2163//1019
+f 2164//1018 2163//1018 2188//1018
+f 2164//1017 2188//1017 2165//1017
+f 2166//1016 2165//1016 2190//1016
+f 2167//1015 2166//1015 2191//1015
+f 2168//1014 2167//1014 2192//1014
+f 2169//1013 2168//1013 2193//1013
+f 2170//1012 2169//1012 2194//1012
+f 2170//1191 2194//1191 2171//1191
+f 2171//1010 2195//1010 2172//1010
+f 2172//1009 2196//1009 2173//1009
+f 2174//1032 2173//1032 2198//1032
+f 2174//1031 2198//1031 2175//1031
+f 2175//1192 2199//1192 2176//1192
+f 2176//1029 2200//1029 2177//1029
+f 2177//1028 2201//1028 2178//1028
+f 2178//1027 2202//1027 2179//1027
+f 2179//1026 2203//1026 2180//1026
+f 2180//1025 2204//1025 2181//1025
+f 2181//1024 2205//1024 2182//1024
+f 2182//1023 2206//1023 2183//1023
+f 2184//1022 2183//1022 2208//1022
+f 2184//1021 2208//1021 2161//1021
+f 2186//1044 2185//1044 2210//1044
+f 2186//1043 2210//1043 2187//1043
+f 2187//1042 2211//1042 2188//1042
+f 2188//1041 2212//1041 2189//1041
+f 2189//1040 2213//1040 2190//1040
+f 2190//1039 2214//1039 2191//1039
+f 2191//1038 2215//1038 2192//1038
+f 2193//1037 2192//1037 2217//1037
+f 2194//1036 2193//1036 2218//1036
+f 2194//1035 2218//1035 2195//1035
+f 2196//1034 2195//1034 2220//1034
+f 2196//1033 2220//1033 2197//1033
+f 2198//1056 2197//1056 2222//1056
+f 2198//1055 2222//1055 2199//1055
+f 2200//1054 2199//1054 2224//1054
+f 2200//1053 2224//1053 2201//1053
+f 2202//1052 2201//1052 2226//1052
+f 2203//1051 2202//1051 2227//1051
+f 2204//1050 2203//1050 2228//1050
+f 2205//1049 2204//1049 2229//1049
+f 2205//1048 2229//1048 2206//1048
+f 2207//1047 2206//1047 2231//1047
+f 2208//1046 2207//1046 2232//1046
+f 2208//1045 2232//1045 2185//1045
+f 2210//1068 2209//1068 2234//1068
+f 2211//1067 2210//1067 2235//1067
+f 2211//1066 2235//1066 2212//1066
+f 2213//1065 2212//1065 2237//1065
+f 2213//1064 2237//1064 2214//1064
+f 2214//1063 2238//1063 2215//1063
+f 2215//1062 2239//1062 2216//1062
+f 2216//1061 2240//1061 2217//1061
+f 2217//1060 2241//1060 2218//1060
+f 2218//1059 2242//1059 2219//1059
+f 2219//1058 2243//1058 2220//1058
+f 2220//1057 2244//1057 2221//1057
+f 2221//1080 2245//1080 2222//1080
+f 2223//1079 2222//1079 2247//1079
+f 2224//1078 2223//1078 2248//1078
+f 2224//1077 2248//1077 2225//1077
+f 2226//1076 2225//1076 2250//1076
+f 2226//1075 2250//1075 2227//1075
+f 2228//1074 2227//1074 2252//1074
+f 2229//1073 2228//1073 2253//1073
+f 2230//1072 2229//1072 2254//1072
+f 2231//1071 2230//1071 2255//1071
+f 2231//1070 2255//1070 2232//1070
+f 2232//1069 2256//1069 2209//1069
+f 2233//1092 2257//1092 2234//1092
+f 2234//1091 2258//1091 2235//1091
+f 2236//1090 2235//1090 2260//1090
+f 2237//1089 2236//1089 2261//1089
+f 2238//1088 2237//1088 2262//1088
+f 2239//1087 2238//1087 2263//1087
+f 2239//1086 2263//1086 2240//1086
+f 2241//1085 2240//1085 2265//1085
+f 2242//1084 2241//1084 2266//1084
+f 2242//1083 2266//1083 2243//1083
+f 2243//1082 2267//1082 2244//1082
+f 2245//1081 2244//1081 2269//1081
+f 2245//1104 2269//1104 2246//1104
+f 2246//1103 2270//1103 2247//1103
+f 2247//1102 2271//1102 2248//1102
+f 2249//1101 2248//1101 2273//1101
+f 2249//1100 2273//1100 2250//1100
+f 2251//1099 2250//1099 2275//1099
+f 2251//1098 2275//1098 2252//1098
+f 2252//1097 2276//1097 2253//1097
+f 2253//1096 2277//1096 2254//1096
+f 2254//1095 2278//1095 2255//1095
+f 2256//1094 2255//1094 2280//1094
+f 2233//1093 2256//1093 2257//1093
+f 2258//1116 2257//1116 2282//1116
+f 2258//1115 2282//1115 2259//1115
+f 2259//1114 2283//1114 2260//1114
+f 2260//1113 2284//1113 2261//1113
+f 2261//1112 2285//1112 2262//1112
+f 2262//1111 2286//1111 2263//1111
+f 2263//1110 2287//1110 2264//1110
+f 2264//1109 2288//1109 2265//1109
+f 2265//1108 2289//1108 2266//1108
+f 2266//1107 2290//1107 2267//1107
+f 2267//1106 2291//1106 2268//1106
+f 2268//1105 2292//1105 2269//1105
+f 2270//1128 2269//1128 2294//1128
+f 2271//1127 2270//1127 2295//1127
+f 2272//1126 2271//1126 2296//1126
+f 2273//1125 2272//1125 2297//1125
+f 2274//1124 2273//1124 2298//1124
+f 2275//1123 2274//1123 2299//1123
+f 2276//1122 2275//1122 2300//1122
+f 2277//1121 2276//1121 2301//1121
+f 2278//1120 2277//1120 2302//1120
+f 2278//1119 2302//1119 2279//1119
+f 2280//1118 2279//1118 2304//1118
+f 2280//1117 2304//1117 2257//1117
+f 2281//1140 1//1140 2282//1140
+f 2282//1139 2//1139 2283//1139
+f 2283//1138 3//1138 2284//1138
+f 2284//1137 4//1137 2285//1137
+f 2285//1136 5//1136 2286//1136
+f 2286//1135 6//1135 2287//1135
+f 2288//1134 2287//1134 8//1134
+f 2288//1133 8//1133 2289//1133
+f 2289//1132 9//1132 2290//1132
+f 2291//1131 2290//1131 11//1131
+f 2292//1130 2291//1130 12//1130
+f 2293//1129 2292//1129 13//1129
+f 2293//1152 13//1152 2294//1152
+f 2294//1151 14//1151 2295//1151
+f 2295//1150 15//1150 2296//1150
+f 2297//1149 2296//1149 17//1149
+f 2298//1148 2297//1148 18//1148
+f 2298//1147 18//1147 2299//1147
+f 2299//1146 19//1146 2300//1146
+f 2300//1145 20//1145 2301//1145
+f 2301//1144 21//1144 2302//1144
+f 2303//1143 2302//1143 23//1143
+f 2303//1142 23//1142 2304//1142
+f 2304//1141 24//1141 2281//1141
diff --git a/apps/CtsVerifier/res/raw/ring_sound.wav b/apps/CtsVerifier/res/raw/ring_sound.wav
new file mode 100644
index 0000000..763ef14
--- /dev/null
+++ b/apps/CtsVerifier/res/raw/ring_sound.wav
Binary files differ
diff --git a/apps/CtsVerifier/res/values/colors.xml b/apps/CtsVerifier/res/values/colors.xml
index c855d5f..b026260 100644
--- a/apps/CtsVerifier/res/values/colors.xml
+++ b/apps/CtsVerifier/res/values/colors.xml
@@ -12,4 +12,12 @@
<color name="snsr_warning">#75540C</color>
<color name="snsr_information_background">#D0D2F6</color>
<color name="snsr_information">#41576B</color>
+
+ <!-- 6DoF test colors -->
+ <item name="textPrimary" type="color">#FFFFFF</item>
+ <item name="primary" type="color">#43A047</item>
+ <item name="primaryPressed" type="color">#81C784</item>
+ <color name="backgroundGrey">#303030</color>
+ <color name="textSecondary">#B3FFFFFF</color>
+ <color name="yellow">#C0FFFF00</color>
</resources>
diff --git a/apps/CtsVerifier/res/values/dimens.xml b/apps/CtsVerifier/res/values/dimens.xml
index 8df5b35..0f1ec44 100644
--- a/apps/CtsVerifier/res/values/dimens.xml
+++ b/apps/CtsVerifier/res/values/dimens.xml
@@ -38,4 +38,7 @@
<dimen name="js_padding">10dp</dimen>
-</resources>
\ No newline at end of file
+ <!-- Default screen margins, per the Android Design guidelines. -->
+ <dimen name="activity_horizontal_margin">16dp</dimen>
+ <dimen name="activity_vertical_margin">16dp</dimen>
+</resources>
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index ef77c84..5cd2cb4 100644
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -2860,4 +2860,138 @@
<string name="audio_frequency_unprocessed_test1_btn">Test 1</string>
<string name="audio_frequency_unprocessed_results_text">Results...</string>
+ <!-- Strings for 6DoF test -->
+ <string name="six_dof_test">6DoF Test</string>
+ <string name="action_settings">Settings</string>
+ <string name="start">Start</string>
+ <string name="motion_tracking_permission">"Motion Tracking permission needed!"</string>
+ <string name="translations">Translations:</string>
+ <string name="rotations">Rotations:</string>
+ <string name="action_place_marker">Place Marker</string>
+ <string name="markers">Markers:\n</string>
+ <string name="title_activity_cts">CTSActivity</string>
+ <string name="stop">Stop!</string>
+ <string name="stop_message">Mark where you are standing with something like a piece of card and
+ then press ready to record this waypoint
+ </string>
+ <string name="ready">Ready</string>
+ <string name="initial">First Waypoint</string>
+ <string name="phase1_initial_message">When you are ready to place your first waypoint, place a
+ marker at the devices current location. Use something that will allow you to put the device
+ in the exact same position and orientation on the second lap. Then, while the device is on
+ the marker, click the turquoise action button to record the first waypoint.\n\nStart walking
+ in a direction. The button will appear again when you have walked far enough. Place a marker
+ at the device\'s location and press the button to record a waypoint. You cannot place
+ waypoints in a straight line, so for your 3rd waypoint turn 90 degrees and walk in a
+ direction that will make a triangle out of your 3 waypoints.
+ </string>
+ <string name="phase2_initial_message">You now need to visit each waypoint, clicking the
+ turquoise action button at each one. You need to reach each waypoint in 20 seconds. While
+ you are on your way to the waypoints, the camera preview will rotate. You will need to
+ rotate the device to match the rotation of the camera preview. A box will be shown to help
+ you. It shows the device\'s current rotation and will change colour based on whether you are
+ close enough to the required rotation. Red if you are failing, green if you are passing.
+ When you have reached the next waypoint, the device can be returned to it\'s original
+ rotation.
+ </string>
+ <string name="last">Go back to the first waypoint</string>
+ <string name="last_message">Now go back to the first waypoint and press ready to finish the
+ first lap.
+ </string>
+ <string name="got_it">Got it!</string>
+ <string name="lap2_instructions">Now, go to each waypoint (including your initial
+ one) and click the turquoise action button. Then move on to the next waypoint and do the
+ same.\nThe last waypoint should be the first waypoint again.
+ </string>
+ <string name="test1_pass2">Pass 2</string>
+ <string name="results">Results</string>
+ <string name="overall_instructions">These tests are designed to verify the correctness and
+ accuracy of a 6DoF enabled device. There will be 3 phases to these tests, each testing a
+ different part of 6DOF functionality.
+ </string>
+ <string name="phase1_description">This first test will test the accuracy of the device. It will
+ ask you to mark out 4 waypoints and then return to the original waypoint to complete the
+ lap. After this you will then need to do another lap without any HUD information. At the
+ end, the pairs of waypoints and the path you travelled will be compared and you will be
+ given a result.
+ </string>
+ <string name="phase2_description">This test will test the robustness of the device. Using the
+ same 4 waypoints as before, you will need to do a lap, reaching each waypoint in the
+ allotted time.\nWhile you are on your way to the waypoints, the camera preview will rotate.
+ You will need to rotate the device to match the rotation of the camera preview. A box will
+ be shown to help you. It shows the device\'s current rotation and will change colour based
+ on whether you are close enough to the required rotation. Red if you are failing, green if
+ you are passing. When you have reached the next waypoint, the device can be returned to
+ it\'s original rotation.
+ </string>
+ <string name="phase3_description">Now we will test the AR capability of the device. Again, you
+ will need to do a lap of the waypoints, but this time between each waypoint the device will
+ overlay hoops that you must pass the device through as you go along.
+ </string>
+ <string name="distance">Distance from last waypoint:</string>
+ <string name="obj_return_to_initial_waypoint">Return to initial waypoint</string>
+ <string name="waypointPlaced">Waypoint placed!</string>
+ <string name="undo">undo</string>
+ <string name="distance_remaining">Distance Remaining:\n</string>
+ <string name="waypoint_differences">Waypoints</string>
+ <string name="path_differences">Paths</string>
+ <string name="error_distance">Not far away enough from other waypoints!</string>
+ <string name="error_area">Not enough area covered.</string>
+ <string name="passed">Passed!</string>
+ <string name="failed">Failed!</string>
+ <string name="test_failed">Test Failed!</string>
+ <string name="error_start_point">Not close enough to initial waypoint!</string>
+ <string name="waypoint_distance">Waypoint %1$s: %2$s</string>
+ <string name="total">Total %1$s</string>
+ <string name="path">Path %1$s: %2$s</string>
+ <string name="fail">Fail</string>
+ <string name="pass">Pass</string>
+ <string name="info">Info</string>
+ <string name="coming_soon">Coming soon!</string>
+ <string name="motion_tracking_invalid_state">Motion Tracking has entered an invalid state
+ </string>
+ <string name="action_xml">XML</string>
+ <string name="error_null_fragment">UI fragment was null, couldn\'t do callback to listener
+ </string>
+ <string name="error_retained_fragment_null">DataFragment is null</string>
+ <string name="time_remaining">%1$s seconds remaining to get to next waypoint</string>
+ <string name="action_skip_to_2nd">Skip to 2nd Test</string>
+ <string name="failed_pause_resume">Test Failed because Test Activity was paused</string>
+ <string name="action_overlapping_outlines">Overlapping Outlines</string>
+ <string name="action_overlapping_filled">Overlapping Filled</string>
+ <string name="time">Time</string>
+ <string name="rotation_result">Rotation</string>
+ <string name="action_separate_outlines">Separate Outlines</string>
+ <string name="action_separate_filled">Separate Filled</string>
+ <string name="action_one">One Rectangle</string>
+ <string name="rotation_mode">Change rotation mode</string>
+ <string name="phase3_initial_message">Go through the rings as you visit waypoints.</string>
+ <string name="rings">Rings</string>
+ <string name="rings_entered">Rings collected %1$s/%2$s</string>
+ <string name="error_rings_not_entered">You haven\'t collected all the rings in this path!
+ </string>
+ <string name="save">Save</string>
+
+ <string-array name="initial_waypoint">
+ <item>Find a place for your first waypoint</item>
+ <item>Go to initial waypoint</item>
+ </string-array>
+
+ <string-array name="next_waypoint">
+ <item>Find a suitable place for waypoint 0</item>
+ <item>Go to waypoint 0</item>
+ </string-array>
+
+ <string-array name="phase">
+ <item>Phase 1</item>
+ <item>Phase 2</item>
+ <item>Phase 3</item>
+ </string-array>
+
+ <string-array name="phase_descriptions">
+ <item>@string/phase1_description</item>
+ <item>@string/phase2_description</item>
+ <item>@string/phase3_description</item>
+ </string-array>
+
</resources>
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Activities/StartActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Activities/StartActivity.java
new file mode 100644
index 0000000..ab8d084
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Activities/StartActivity.java
@@ -0,0 +1,89 @@
+/*
+ * 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 com.android.cts.verifier.sensors.sixdof.Activities;
+
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.sensors.sixdof.Utils.ReportExporter;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.app.Activity;
+import android.view.View;
+import android.widget.Button;
+import android.widget.Toast;
+
+/**
+ * Launcher activity that gives brief instructions of tests.
+ */
+public class StartActivity extends PassFailButtons.Activity {
+ private Button mBtnStart;
+
+ // Unique code that ensures we get the result from the activity that want to get a result
+ private static final int REQUEST_CODE_6DOF = 5555;
+
+ public enum ResultCode {
+ FAILED_PAUSE_AND_RESUME,
+ FAILED,
+ PASSED
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_start);
+
+ mBtnStart = (Button) findViewById(R.id.btnStart);
+ mBtnStart.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ startPhase1();
+ }
+ });
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ }
+
+ private void startPhase1() {
+ Intent startPhase1 = new Intent(this, TestActivity.class);
+ startActivityForResult(startPhase1, REQUEST_CODE_6DOF);
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ // Check which request we're responding to.
+ if (requestCode == REQUEST_CODE_6DOF) {
+ if (resultCode == RESULT_CANCELED) {
+ Toast.makeText(this, R.string.test_failed, Toast.LENGTH_SHORT).show();
+ } else { // RESULT_OK
+ ResultCode result = (ResultCode) data.getSerializableExtra(TestActivity.EXTRA_RESULT_ID);
+ if (result == ResultCode.FAILED_PAUSE_AND_RESUME) {
+ Toast.makeText(this, R.string.failed_pause_resume, Toast.LENGTH_SHORT).show();
+ } else if (result == ResultCode.FAILED) {
+ Toast.makeText(this, R.string.failed, Toast.LENGTH_SHORT).show();
+ } else if (result == ResultCode.PASSED) {
+ Toast.makeText(this, R.string.passed, Toast.LENGTH_SHORT).show();
+ }
+
+ String testReport = data.getStringExtra(TestActivity.EXTRA_REPORT);
+ new ReportExporter(this, testReport).execute();
+ }
+ }
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Activities/TestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Activities/TestActivity.java
new file mode 100644
index 0000000..9ca5f2c
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Activities/TestActivity.java
@@ -0,0 +1,457 @@
+/*
+ * 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 com.android.cts.verifier.sensors.sixdof.Activities;
+
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.sensors.sixdof.Activities.StartActivity.ResultCode;
+import com.android.cts.verifier.sensors.sixdof.Fragments.AccuracyFragment;
+import com.android.cts.verifier.sensors.sixdof.Fragments.ComplexMovementFragment;
+import com.android.cts.verifier.sensors.sixdof.Fragments.DataFragment;
+import com.android.cts.verifier.sensors.sixdof.Fragments.PhaseStartFragment;
+import com.android.cts.verifier.sensors.sixdof.Fragments.RobustnessFragment;
+import com.android.cts.verifier.sensors.sixdof.Interfaces.AccuracyListener;
+import com.android.cts.verifier.sensors.sixdof.Interfaces.BaseUiListener;
+import com.android.cts.verifier.sensors.sixdof.Interfaces.ComplexMovementListener;
+import com.android.cts.verifier.sensors.sixdof.Interfaces.RobustnessListener;
+import com.android.cts.verifier.sensors.sixdof.Utils.ReportExporter;
+import com.android.cts.verifier.sensors.sixdof.Utils.TestReport;
+import com.android.cts.verifier.sensors.sixdof.Utils.Exceptions.WaypointAreaCoveredException;
+import com.android.cts.verifier.sensors.sixdof.Utils.Exceptions.WaypointDistanceException;
+import com.android.cts.verifier.sensors.sixdof.Utils.Exceptions.WaypointRingNotEnteredException;
+import com.android.cts.verifier.sensors.sixdof.Utils.Exceptions.WaypointStartPointException;
+import com.android.cts.verifier.sensors.sixdof.Utils.Manager.Lap;
+import com.android.cts.verifier.sensors.sixdof.Utils.Path.PathUtilityClasses.Ring;
+import com.android.cts.verifier.sensors.sixdof.Utils.Path.PathUtilityClasses.RotationData;
+import com.android.cts.verifier.sensors.sixdof.Utils.Path.PathUtilityClasses.Waypoint;
+import com.android.cts.verifier.sensors.sixdof.Utils.PoseProvider.PoseProvider;
+import com.android.cts.verifier.sensors.sixdof.Utils.ResultObjects.ResultObject;
+
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.os.Bundle;
+import android.app.Fragment;
+import android.app.FragmentManager;
+import android.app.FragmentTransaction;
+import android.app.AlertDialog;
+import android.app.Activity;
+import android.util.Log;
+import android.view.Display;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.Surface;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+/**
+ * Main Activity for 6DOF tests Handles calls between UI fragments and the Data fragment. The
+ * controller in the MVC structure.
+ */
+public class TestActivity extends Activity implements BaseUiListener, AccuracyListener,
+ RobustnessListener, ComplexMovementListener {
+
+ private static final String TAG = "TestActivity";
+ private static final String TAG_DATA_FRAGMENT = "data_fragment";
+ public static final String EXTRA_RESULT_ID = "extraResult";
+ public static final String EXTRA_REPORT = "extraReport";
+ public static final String EXTRA_ON_RESTART = "6dof_verifier_restart";
+ public static final Object POSE_LOCK = new Object();
+
+ private DataFragment mDataFragment;
+
+ private BaseUiListener mUiListener;
+ private AccuracyListener mAccuracyListener;
+ private RobustnessListener mRobustnessListener;
+ private ComplexMovementListener mComplexMovementListener;
+
+ private CTSTest mCurrentTest = CTSTest.ACCURACY;
+
+ private boolean mHasBeenPaused = false;
+
+ public enum CTSTest {
+ ACCURACY,
+ ROBUSTNESS,
+ COMPLEX_MOVEMENT
+ }
+
+ /**
+ * Initialises camera preview, looks for a retained data fragment if we have one and adds UI
+ * fragment.
+ */
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ // If we are restarting, kill the test as data is invalid.
+ if (savedInstanceState != null) {
+ if (savedInstanceState.getBoolean(EXTRA_ON_RESTART)) {
+ Intent intent = this.getIntent();
+ intent.putExtra(EXTRA_RESULT_ID, ResultCode.FAILED_PAUSE_AND_RESUME);
+ this.setResult(RESULT_OK, intent);
+ finish();
+ }
+ }
+
+ setContentView(R.layout.activity_cts);
+
+ // Add the first instructions fragment.
+ Fragment fragment = PhaseStartFragment.newInstance(CTSTest.ACCURACY);
+ FragmentManager fragmentManager = getFragmentManager();
+ FragmentTransaction transaction = fragmentManager.beginTransaction();
+ transaction.replace(R.id.contentFragment, fragment);
+ transaction.commit();
+
+ mDataFragment = new DataFragment();
+ fragmentManager.beginTransaction().add(mDataFragment, TAG_DATA_FRAGMENT).commit();
+
+ // Lock the screen to its current rotation
+ lockRotation();
+ }
+
+ /**
+ * Lock the orientation of the device in its current state.
+ */
+ private void lockRotation() {
+ final Display display = getWindowManager().getDefaultDisplay();
+ int naturalOrientation = Configuration.ORIENTATION_LANDSCAPE;
+ int configOrientation = getResources().getConfiguration().orientation;
+ switch (display.getRotation()) {
+ case Surface.ROTATION_0:
+ case Surface.ROTATION_180:
+ // We are currently in the same basic orientation as the natural orientation
+ naturalOrientation = configOrientation;
+ break;
+ case Surface.ROTATION_90:
+ case Surface.ROTATION_270:
+ // We are currently in the other basic orientation to the natural orientation
+ naturalOrientation = (configOrientation == Configuration.ORIENTATION_LANDSCAPE) ?
+ Configuration.ORIENTATION_PORTRAIT : Configuration.ORIENTATION_LANDSCAPE;
+ break;
+ }
+
+ int[] orientationMap = {
+ ActivityInfo.SCREEN_ORIENTATION_PORTRAIT,
+ ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE,
+ ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT,
+ ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE
+ };
+ // Since the map starts at portrait, we need to offset if this device's natural orientation
+ // is landscape.
+ int indexOffset = 0;
+ if (naturalOrientation == Configuration.ORIENTATION_LANDSCAPE) {
+ indexOffset = 1;
+ }
+
+ // The map assumes default rotation. Check for reverse rotation and correct map if required
+ try {
+ if (getResources().getBoolean(getResources().getSystem().getIdentifier(
+ "config_reverseDefaultRotation", "bool", "android"))) {
+ orientationMap[0] = ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;
+ orientationMap[2] = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
+ }
+ } catch (Resources.NotFoundException e) {
+ // If resource is not found, assume default rotation and continue
+ Log.d(TAG, "Cannot determine device rotation direction, assuming default");
+ }
+
+ setRequestedOrientation(orientationMap[(display.getRotation() + indexOffset) % 4]);
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+
+ // 6DoF is reset after a recreation of activity, which invalidates the tests.
+ if (mHasBeenPaused) {
+ Intent intent = this.getIntent();
+ intent.putExtra(EXTRA_RESULT_ID, ResultCode.FAILED_PAUSE_AND_RESUME);
+ this.setResult(RESULT_OK, intent);
+ finish();
+ }
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ mHasBeenPaused = true;
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ // Inflate the menu; this adds items to the action bar if it is present.
+ getMenuInflater().inflate(R.menu.menu_cts, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ // Handle action bar item clicks here.
+ int id = item.getItemId();
+
+ switch (id) {
+ case R.id.action_save_results:
+ saveResults();
+ return true;
+ case R.id.action_xml:
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+
+ try {
+ builder.setMessage(mDataFragment.getTestReport().getContents())
+ .setTitle(R.string.results)
+ .setPositiveButton(R.string.got_it, null);
+ } catch (IOException e) {
+ Log.e(TAG, e.toString());
+ }
+
+ AlertDialog dialog = builder.create();
+ dialog.show();
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ public void saveResults() {
+ try {
+ new ReportExporter(this, getTestReport().getContents()).execute();
+ } catch (IOException e) {
+ Log.e(TAG, "Couldn't create test report.");
+ }
+ }
+
+ public TestReport getTestReport() {
+ return mDataFragment.getTestReport();
+ }
+
+ public void listenFor6DofData(Fragment listener) {
+ mUiListener = (BaseUiListener) listener;
+ switch (mCurrentTest) {
+ case ACCURACY:
+ mAccuracyListener = (AccuracyListener) listener;
+ mRobustnessListener = null;
+ mComplexMovementListener = null;
+ break;
+ case ROBUSTNESS:
+ mAccuracyListener = null;
+ mRobustnessListener = (RobustnessListener) listener;
+ mComplexMovementListener = null;
+ break;
+ case COMPLEX_MOVEMENT:
+ mAccuracyListener = null;
+ mRobustnessListener = null;
+ mComplexMovementListener = (ComplexMovementListener) listener;
+ break;
+ default:
+ throw new AssertionError("mCurrentTest is a test that doesn't exist!");
+ }
+ }
+
+ public boolean isPoseProviderReady() {
+ if (mDataFragment != null) {
+ return mDataFragment.isPoseProviderReady();
+ } else {
+ return false;
+ }
+
+ }
+
+ public ArrayList<Waypoint> getUserGeneratedWaypoints(Lap lap) {
+ return mDataFragment.getUserGeneratedWaypoints(lap);
+ }
+
+ public Lap getLap() {
+ return mDataFragment.getLap();
+ }
+
+ public ArrayList<Ring> getRings() {
+ return mDataFragment.getRings();
+ }
+
+ @Override
+ public void onPoseProviderReady() {
+ if (mUiListener != null) {
+ mUiListener.onPoseProviderReady();
+ } else {
+ Log.e(TAG, getString(R.string.error_null_fragment));
+ }
+
+ // Possible for this to be called while switching UI fragments, so mUiListener is null
+ // but we want to start the test anyway.
+ mDataFragment.testStarted();
+ }
+
+ @Override
+ public void onWaypointPlaced() {
+ if (mUiListener != null) {
+ mUiListener.onWaypointPlaced();
+ } else {
+ Log.e(TAG, getString(R.string.error_null_fragment));
+ }
+ }
+
+ @Override
+ public void onResult(ResultObject result) {
+ if (mUiListener != null) {
+ mUiListener.onResult(result);
+ } else {
+ Log.e(TAG, getString(R.string.error_null_fragment));
+ }
+ }
+
+ @Override
+ public void onReset() {
+ if (mAccuracyListener != null) {
+ if (mCurrentTest == CTSTest.ACCURACY) {
+ mAccuracyListener.onReset();
+ } else {
+ throw new RuntimeException("We are in the wrong test for this listener to be called.");
+ }
+ } else {
+ Log.e(TAG, getString(R.string.error_null_fragment));
+ }
+ }
+
+ @Override
+ public void lap1Complete() {
+ if (mAccuracyListener != null) {
+ mAccuracyListener.lap1Complete();
+ } else {
+ Log.e(TAG, getString(R.string.error_null_fragment));
+ }
+ }
+
+ public void attemptWaypointPlacement() throws WaypointAreaCoveredException, WaypointDistanceException, WaypointStartPointException, WaypointRingNotEnteredException {
+ mDataFragment.onWaypointPlacementAttempt();
+ }
+
+ public void undoWaypointPlacement() {
+ if (mDataFragment != null) {
+ mDataFragment.undoWaypointPlacement();
+ } else {
+ Log.e(TAG, getString(R.string.error_retained_fragment_null));
+ }
+ }
+
+ public void readyForLap2() {
+ mDataFragment.startTest(CTSTest.ACCURACY);
+ }
+
+ public float getLatestDistanceData() {
+ return mDataFragment.getLatestDistanceData();
+ }
+
+ public float getTimeRemaining() {
+ return mDataFragment.getTimeRemaining();
+ }
+
+ public PoseProvider getPoseProvider() {
+ return mDataFragment.getPoseProvider();
+ }
+
+ @Override
+ public void onNewRotationData(RotationData data) {
+ if (mRobustnessListener != null) {
+ mRobustnessListener.onNewRotationData(data);
+ } else {
+ Log.e(TAG, getString(R.string.error_null_fragment));
+ }
+ }
+
+ @Override
+ public void onRingEntered(Ring ring) {
+ if (mComplexMovementListener != null) {
+ mComplexMovementListener.onRingEntered(ring);
+ } else {
+ Log.e(TAG, getString(R.string.error_null_fragment));
+ }
+ }
+
+ /**
+ * Loads test fragment for a particular phase.
+ *
+ * @param phase test to be started.
+ */
+ public void switchToTestFragment(CTSTest phase) {
+ Log.d(TAG, "switchToTestFragment");
+ Fragment fragment;
+
+ switch (phase) {
+ case ACCURACY:
+ fragment = AccuracyFragment.newInstance();
+ break;
+ case ROBUSTNESS:
+ fragment = RobustnessFragment.newInstance();
+ break;
+ case COMPLEX_MOVEMENT:
+ fragment = ComplexMovementFragment.newInstance(); //Complex Motion
+ break;
+ default:
+ throw new AssertionError("Trying to start a test that doesn't exist!");
+ }
+ FragmentManager fm = getFragmentManager();
+ FragmentTransaction transaction = fm.beginTransaction();
+ transaction.replace(R.id.contentFragment, fragment);
+ transaction.commit();
+ }
+
+ /**
+ * Loads start instruction fragment for a particular test.
+ *
+ * @param phase test to show instruction screen for.
+ */
+ public void switchToStartFragment(CTSTest phase) {
+ Log.e(TAG, "switchToStartFragment");
+ mUiListener = null;
+ mAccuracyListener = null;
+ mRobustnessListener = null;
+ mComplexMovementListener = null;
+
+ mCurrentTest = phase;
+ mDataFragment.startTest(mCurrentTest);
+ Fragment fragment = PhaseStartFragment.newInstance(phase);
+ FragmentManager fm = getFragmentManager();
+ FragmentTransaction transaction = fm.beginTransaction();
+ transaction.replace(R.id.contentFragment, fragment);
+ transaction.commit();
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ // We are always going to be restarting if this is called.
+ outState.putBoolean(EXTRA_ON_RESTART, true);
+ super.onSaveInstanceState(outState);
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ onDestroyUi();
+ mUiListener = null;
+ mAccuracyListener = null;
+ mRobustnessListener = null;
+ mComplexMovementListener = null;
+ mDataFragment = null;
+ }
+
+ @Override
+ public void onDestroyUi() {
+ if (mUiListener != null) {
+ mUiListener.onDestroyUi();
+ }
+ }
+}
diff --git a/hostsidetests/services/activitymanager/app/src/android/server/app/BottomLeftLayoutActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/BuildConfig.java
similarity index 82%
copy from hostsidetests/services/activitymanager/app/src/android/server/app/BottomLeftLayoutActivity.java
copy to apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/BuildConfig.java
index 27a6fe2..dddbde7 100644
--- a/hostsidetests/services/activitymanager/app/src/android/server/app/BottomLeftLayoutActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/BuildConfig.java
@@ -14,9 +14,8 @@
* limitations under the License.
*/
-package android.server.app;
+package com.android.cts.verifier.sensors.sixdof;
-import android.app.Activity;
-
-public class BottomLeftLayoutActivity extends Activity {
+public class BuildConfig {
+ public static final boolean DEBUG = false;
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Dialogs/AccuracyResultDialog.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Dialogs/AccuracyResultDialog.java
new file mode 100644
index 0000000..1228c95
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Dialogs/AccuracyResultDialog.java
@@ -0,0 +1,51 @@
+/*
+ * 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 com.android.cts.verifier.sensors.sixdof.Dialogs;
+
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.sensors.sixdof.Utils.ResultObjects.ResultObject;
+
+import android.app.Dialog;
+import android.os.Bundle;
+import android.app.AlertDialog;
+import android.view.LayoutInflater;
+import android.widget.TextView;
+
+/**
+ * Dialog that displays the results of the first test.
+ */
+public class AccuracyResultDialog extends BaseResultsDialog {
+
+ public static AccuracyResultDialog newInstance(ResultObject resultObject) {
+ AccuracyResultDialog dialog = new AccuracyResultDialog();
+ dialog.setup(resultObject);
+ return dialog;
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ mBuilder = new AlertDialog.Builder(getActivity());
+ // Get the layout inflater.
+ LayoutInflater inflater = getActivity().getLayoutInflater();
+ mRootView = inflater.inflate(R.layout.dialog_result_accuracy, null);
+
+ mTextViews.put(ResultType.WAYPOINT, (TextView) mRootView.findViewById(R.id.tvWaypointResult));
+ mTextViews.put(ResultType.PATH, (TextView) mRootView.findViewById(R.id.tvPathResult));
+
+ // Parent class will actually create the dialog.
+ return super.onCreateDialog(savedInstanceState);
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Dialogs/BaseResultsDialog.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Dialogs/BaseResultsDialog.java
new file mode 100644
index 0000000..1cb8aa2
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Dialogs/BaseResultsDialog.java
@@ -0,0 +1,73 @@
+/*
+ * 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 com.android.cts.verifier.sensors.sixdof.Dialogs;
+
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.sensors.sixdof.Utils.ResultObjects.ResultObject;
+
+import android.app.Dialog;
+import android.os.Bundle;
+import android.app.DialogFragment;
+import android.app.AlertDialog;
+import android.view.View;
+import android.widget.TextView;
+
+import java.util.HashMap;
+
+/**
+ * Class that sets up the displaying of test results in a dialog.
+ */
+public abstract class BaseResultsDialog extends DialogFragment {
+ public enum ResultType {
+ WAYPOINT,
+ PATH,
+ TIME,
+ ROTATION,
+ RINGS
+ }
+
+ protected ResultObject mResult;
+ protected HashMap<ResultType, TextView> mTextViews;
+ protected AlertDialog.Builder mBuilder;
+ protected View mRootView;
+
+ protected void setup(ResultObject result) {
+ mResult = result;
+ mTextViews = new HashMap<>();
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ // Please override and call super.onCreateDialog after setting up mRootView and mBuilder.
+ String title = getString(R.string.passed);
+ for (ResultType resultType : mResult.getResults().keySet()) {
+ if (mResult.getResults().get(resultType)) {
+ mTextViews.get(resultType).setText(getString(R.string.passed));
+ } else {
+ title = getString(R.string.failed);
+ mTextViews.get(resultType).setText(getString(R.string.failed));
+ }
+ }
+
+ mBuilder.setView(mRootView);
+
+ mBuilder.setTitle(title)
+ .setPositiveButton(R.string.got_it, null);
+
+ // Create the AlertDialog object and return it.
+ return mBuilder.create();
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Dialogs/ComplexMovementResultDialog.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Dialogs/ComplexMovementResultDialog.java
new file mode 100644
index 0000000..cef2fde
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Dialogs/ComplexMovementResultDialog.java
@@ -0,0 +1,51 @@
+/*
+ * 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 com.android.cts.verifier.sensors.sixdof.Dialogs;
+
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.sensors.sixdof.Utils.ResultObjects.ResultObject;
+
+import android.app.Dialog;
+import android.os.Bundle;
+import android.app.AlertDialog;
+import android.view.LayoutInflater;
+import android.widget.TextView;
+
+/**
+ * Dialog that displays the results of the first test.
+ */
+public class ComplexMovementResultDialog extends BaseResultsDialog {
+
+ public static ComplexMovementResultDialog newInstance(ResultObject resultObject) {
+ ComplexMovementResultDialog dialog = new ComplexMovementResultDialog();
+ dialog.setup(resultObject);
+ return dialog;
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ mBuilder = new AlertDialog.Builder(getActivity());
+ // Get the layout inflater.
+ LayoutInflater inflater = getActivity().getLayoutInflater();
+ mRootView = inflater.inflate(R.layout.dialog_result_complex_movement, null);
+
+ mTextViews.put(ResultType.WAYPOINT, (TextView) mRootView.findViewById(R.id.tvWaypointResult));
+ mTextViews.put(ResultType.RINGS, (TextView) mRootView.findViewById(R.id.tvRingsResult));
+
+ // Create the AlertDialog object and return it.
+ return super.onCreateDialog(savedInstanceState);
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Dialogs/Lap2Dialog.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Dialogs/Lap2Dialog.java
new file mode 100644
index 0000000..a38d11b
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Dialogs/Lap2Dialog.java
@@ -0,0 +1,74 @@
+/*
+ * 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 com.android.cts.verifier.sensors.sixdof.Dialogs;
+
+import com.android.cts.verifier.R;
+
+import android.app.Activity;
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.app.DialogFragment;
+import android.app.AlertDialog;
+
+/**
+ * Dialog for instructions on what to to on lap 2
+ */
+public class Lap2Dialog extends DialogFragment {
+ Lap2DialogListener mListener;
+
+ public static Lap2Dialog newInstance() {
+ Lap2Dialog dialog = new Lap2Dialog();
+ return dialog;
+ }
+
+ public interface Lap2DialogListener {
+ void onLap2Start();
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ // Use the Builder class for convenient dialog construction.
+ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+
+ // Inflate and set the layout for the dialog.
+ // Pass null as the parent view because its going in the dialog layout.
+ builder.setTitle(R.string.test1_pass2)
+ .setMessage(R.string.lap2_instructions)
+ .setNegativeButton(R.string.got_it, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ mListener.onLap2Start();
+ }
+ });
+
+ // Create the AlertDialog object and return it.
+ return builder.create();
+ }
+
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+ // Verify that the host fragment implements the callback interface.
+ try {
+ mListener = (Lap2DialogListener) getTargetFragment();
+ mListener.onLap2Start();
+ } catch (ClassCastException e) {
+ // The activity doesn't implement the interface, throw exception.
+ throw new ClassCastException(activity.toString()
+ + " must implement Lap2DialogListener");
+ }
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Dialogs/RobustnessResultDialog.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Dialogs/RobustnessResultDialog.java
new file mode 100644
index 0000000..522c7c1
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Dialogs/RobustnessResultDialog.java
@@ -0,0 +1,52 @@
+/*
+ * 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 com.android.cts.verifier.sensors.sixdof.Dialogs;
+
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.sensors.sixdof.Utils.ResultObjects.ResultObject;
+
+import android.app.Dialog;
+import android.os.Bundle;
+import android.app.AlertDialog;
+import android.view.LayoutInflater;
+import android.widget.TextView;
+
+/**
+ * Dialog that displays the results of the first test.
+ */
+public class RobustnessResultDialog extends BaseResultsDialog {
+
+ public static RobustnessResultDialog newInstance(ResultObject resultObject) {
+ RobustnessResultDialog dialog = new RobustnessResultDialog();
+ dialog.setup(resultObject);
+ return dialog;
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ mBuilder = new AlertDialog.Builder(getActivity());
+ // Get the layout inflater.
+ LayoutInflater inflater = getActivity().getLayoutInflater();
+ mRootView = inflater.inflate(R.layout.dialog_result_robustness, null);
+
+ mTextViews.put(ResultType.WAYPOINT, (TextView) mRootView.findViewById(R.id.tvWaypointResult));
+ mTextViews.put(ResultType.PATH, (TextView) mRootView.findViewById(R.id.tvPathResult));
+ mTextViews.put(ResultType.TIME, (TextView) mRootView.findViewById(R.id.tvTimeResult));
+ mTextViews.put(ResultType.ROTATION, (TextView) mRootView.findViewById(R.id.tvRotationResult));
+
+ return super.onCreateDialog(savedInstanceState);
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Fragments/AccuracyFragment.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Fragments/AccuracyFragment.java
new file mode 100644
index 0000000..64c44c7
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Fragments/AccuracyFragment.java
@@ -0,0 +1,303 @@
+/*
+ * 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 com.android.cts.verifier.sensors.sixdof.Fragments;
+
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.sensors.sixdof.BuildConfig;
+import com.android.cts.verifier.sensors.sixdof.Activities.TestActivity;
+import com.android.cts.verifier.sensors.sixdof.Dialogs.AccuracyResultDialog;
+import com.android.cts.verifier.sensors.sixdof.Dialogs.Lap2Dialog;
+import com.android.cts.verifier.sensors.sixdof.Interfaces.AccuracyListener;
+import com.android.cts.verifier.sensors.sixdof.Renderer.AccuracyRenderer;
+import com.android.cts.verifier.sensors.sixdof.Utils.Manager;
+import com.android.cts.verifier.sensors.sixdof.Utils.MathsUtils;
+import com.android.cts.verifier.sensors.sixdof.Utils.Exceptions.WaypointAreaCoveredException;
+import com.android.cts.verifier.sensors.sixdof.Utils.Exceptions.WaypointDistanceException;
+import com.android.cts.verifier.sensors.sixdof.Utils.Exceptions.WaypointRingNotEnteredException;
+import com.android.cts.verifier.sensors.sixdof.Utils.Exceptions.WaypointStartPointException;
+import com.android.cts.verifier.sensors.sixdof.Utils.Path.PathUtilityClasses.Waypoint;
+import com.android.cts.verifier.sensors.sixdof.Utils.ResultObjects.ResultObject;
+
+import android.app.Activity;
+import android.opengl.GLSurfaceView;
+import android.os.Bundle;
+import android.app.DialogFragment;
+import android.app.AlertDialog;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageButton;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import java.text.DecimalFormat;
+import java.util.ArrayList;
+
+/**
+ * UI fragment for the first test.
+ */
+public class AccuracyFragment extends BaseUiFragment implements AccuracyListener,
+ Lap2Dialog.Lap2DialogListener {
+ private static final String TAG = "AccuracyFragment";
+
+ private String mCurrentObjective = "";
+
+ private TextView mTvDistanceRemaining;
+ private TextView mTvMarkers;
+ private TextView mTvObjective;
+
+ /**
+ * Necessary empty constructor.
+ */
+ public AccuracyFragment() {
+ }
+
+ /**
+ * Standard practice to have a static newInstance constructor. Used to pass in arguments to the
+ * fragment. We don't have any at the moment, but this is good structure for the future.
+ *
+ * @return a new Accuracy test fragment.
+ */
+ public static AccuracyFragment newInstance() {
+ AccuracyFragment fragment = new AccuracyFragment();
+ return fragment;
+ }
+
+ /**
+ * Called when the parent activity has been created. Adds the GLSurfaceView to the fragment
+ * layout.
+ */
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+
+ GLSurfaceView surfaceView = new GLSurfaceView(getActivity());
+ surfaceView.setEGLContextClientVersion(2);
+ mRenderer = new AccuracyRenderer(getActivity());
+ surfaceView.setRenderer(mRenderer);
+ mLLCameraLayout = (LinearLayout) getView().findViewById(R.id.llCamera);
+ mLLCameraLayout.addView(surfaceView);
+ Log.d(TAG, "Camera Preview add to layout");
+ }
+
+ /**
+ * Initialises all of the UI elements
+ */
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.fragment_accuracy, container, false);
+ getActivity().setTitle(getResources().getStringArray(R.array.phase)[TestActivity.CTSTest.ACCURACY.ordinal()]);
+ mTvDistanceRemaining = (TextView) view.findViewById(R.id.tvTranslations);
+ mTvMarkers = (TextView) view.findViewById(R.id.tvMarkers);
+ mTvObjective = (TextView) view.findViewById(R.id.tvObjective);
+ mPlaceWaypointButton = (ImageButton) view.findViewById(R.id.fabPlaceWaypoint);
+ mPlaceWaypointButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ try {
+ mActivity.attemptWaypointPlacement();
+ } catch (WaypointDistanceException e) {
+ Toast.makeText(getActivity(),
+ getString(R.string.error_distance), Toast.LENGTH_SHORT).show();
+ } catch (WaypointAreaCoveredException e) {
+ Toast.makeText(getActivity(),
+ getString(R.string.error_area), Toast.LENGTH_SHORT).show();
+ } catch (WaypointStartPointException e) {
+ Toast.makeText(getActivity(),
+ getString(R.string.error_start_point), Toast.LENGTH_SHORT).show();
+ } catch (WaypointRingNotEnteredException e) {
+ throw new AssertionError(
+ "WaypointRingNotEnteredException when not in 3rd test", e);
+ }
+ }
+ });
+
+ // Setup buttons for pass/info/fail
+ setupButtons(view, TestActivity.CTSTest.ACCURACY);
+
+ return view;
+ }
+
+ /**
+ * Called after onCreateView. Starts listening for 6DoF events.
+ */
+ @Override
+ public void onViewCreated(View view, Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ mActivity.listenFor6DofData(this);
+ }
+
+ @Override
+ protected void setupUILoop() {
+ Runnable runnable = new Runnable() {
+
+ @Override
+ public void run() {
+ DecimalFormat oneDecimalFormat = new DecimalFormat("0.0");
+
+ if (mActivity == null || getActivity() == null) {
+ return;
+ }
+
+ String distanceString = "";
+ String markerString = "";
+ ArrayList<Waypoint> referenceWaypoints;
+
+ switch (mActivity.getLap()) {
+ case LAP_1:
+ referenceWaypoints = mActivity.getUserGeneratedWaypoints(Manager.Lap.LAP_1);
+
+ float distanceRemaining = 0f;
+
+ if (referenceWaypoints.size() > 0) {
+ distanceRemaining = mActivity.getLatestDistanceData();
+ float adjustedDistanceRemaining = Math.max(distanceRemaining, 0);
+ distanceString = getResources().getString(R.string.distance_remaining) +
+ oneDecimalFormat.format(adjustedDistanceRemaining);
+
+ markerString = getResources().getString(R.string.markers);
+ for (Waypoint waypoint : referenceWaypoints) {
+ markerString +=
+ MathsUtils.coordinatesToString(waypoint.getCoordinates()) + "\n";
+ }
+ }
+
+ if (distanceRemaining <= 0 || referenceWaypoints.size() == 0) {
+ mPlaceWaypointButton.setVisibility(View.VISIBLE);
+ } else {
+ mPlaceWaypointButton.setVisibility(View.INVISIBLE);
+ }
+ break;
+ case LAP_2:
+ referenceWaypoints = mActivity.getUserGeneratedWaypoints(Manager.Lap.LAP_2);
+
+ if (referenceWaypoints.size() == Manager.MAX_MARKER_NUMBER) {
+ mPlaceWaypointButton.setVisibility(View.INVISIBLE);
+ } else {
+ mPlaceWaypointButton.setVisibility(View.VISIBLE);
+ }
+ break;
+ default:
+ //Possible for this state to be entered when switching fragments
+ Log.e(TAG, "Trying to run UI on Accuracy Test on a lap greater than 2");
+
+ //Use an empty list as not interested in this state
+ referenceWaypoints = new ArrayList<Waypoint>();
+ }
+
+ mCurrentObjective = getObjectiveText(mActivity.getLap(), referenceWaypoints.size());
+
+ mTvDistanceRemaining.setText(distanceString);
+ mTvMarkers.setText(markerString);
+ mTvObjective.setText(mCurrentObjective);
+
+ //Update the UI again in x milliseconds.
+ if (mHandler != null) {
+ mHandler.postDelayed(this, UI_UPDATE_DELAY);
+ }
+ }
+ };
+
+ super.initUIHandler(runnable);
+ }
+
+ /**
+ * Called when this phase is done and a result is ready. Shows the results dialog and enables
+ * pass button if test has been passed.
+ */
+ @Override
+ public void onResult(ResultObject result) {
+ AccuracyResultDialog dialog = AccuracyResultDialog.newInstance(result);
+ dialog.setTargetFragment(AccuracyFragment.this, DIALOG_FRAGMENT);
+ dialog.show(getActivity().getFragmentManager(), "ResultDialogFragment");
+ mPlaceWaypointButton.setVisibility(View.INVISIBLE);
+
+ if (result.hasPassed() || BuildConfig.DEBUG) {
+ mBtnPass.setEnabled(true);
+ mBtnPass.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ onReadyForPhase2();
+ }
+ });
+ }
+ }
+
+ /**
+ * Resets UI to how it is at the start of test. Currently called when first waypoint is undone.
+ */
+ @Override
+ public void onReset() {
+ mActivity.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ mPlaceWaypointButton.setVisibility(View.VISIBLE);
+ mTvDistanceRemaining.setText("");
+ mTvMarkers.setText("");
+ mCurrentObjective = getResources().getStringArray(R.array.initial_waypoint)[0];
+ mTvObjective.setText(mCurrentObjective);
+ }
+ });
+ }
+
+ @Override
+ public void lap1Complete() {
+ onBackToFirstWaypoint();
+ mActivity.readyForLap2();
+ }
+
+ /**
+ * Shows initial instruction dialog
+ */
+ @Override
+ protected void showInitialDialog() {
+ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+
+ builder.setMessage(R.string.phase1_initial_message)
+ .setTitle(R.string.initial)
+ .setPositiveButton(R.string.got_it, null);
+
+ AlertDialog dialog = builder.create();
+ dialog.show();
+ }
+
+ /**
+ * Called when user finishes the first lap
+ */
+ public void onBackToFirstWaypoint() {
+ DialogFragment dialog = Lap2Dialog.newInstance();
+ dialog.setTargetFragment(AccuracyFragment.this, DIALOG_FRAGMENT);
+ dialog.show(getActivity().getFragmentManager(), "Lap2DialogFragment");
+ }
+
+ /**
+ * Move to next test
+ */
+ public void onReadyForPhase2() {
+ mActivity.switchToStartFragment(TestActivity.CTSTest.ROBUSTNESS);
+ }
+
+ /**
+ * Called when lap 2 starts.
+ */
+ @Override
+ public void onLap2Start() {
+ mPlaceWaypointButton.setVisibility(View.VISIBLE);
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Fragments/BaseUiFragment.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Fragments/BaseUiFragment.java
new file mode 100644
index 0000000..12f0652
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Fragments/BaseUiFragment.java
@@ -0,0 +1,198 @@
+/*
+ * 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 com.android.cts.verifier.sensors.sixdof.Fragments;
+
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.sensors.sixdof.Activities.StartActivity;
+import com.android.cts.verifier.sensors.sixdof.Activities.TestActivity;
+import com.android.cts.verifier.sensors.sixdof.Interfaces.BaseUiListener;
+import com.android.cts.verifier.sensors.sixdof.Renderer.BaseRenderer;
+import com.android.cts.verifier.sensors.sixdof.Utils.Manager;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Handler;
+import android.app.Fragment;
+import android.app.AlertDialog;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+import android.widget.ImageButton;
+import android.widget.LinearLayout;
+
+import java.io.IOException;
+
+/**
+ * Abstract class that UI Fragments for each test inherit from,
+ */
+public abstract class BaseUiFragment extends Fragment implements BaseUiListener {
+ private static final String TAG = "BaseUiFragment";
+ protected static final long UI_UPDATE_DELAY = 200;
+
+ protected static final int DIALOG_FRAGMENT = 1;
+
+ protected Button mBtnPass;
+ protected Button mBtnInfo;
+ protected Button mBtnFail;
+ protected ImageButton mPlaceWaypointButton;
+
+ protected LinearLayout mLLCameraLayout;
+
+ protected TestActivity mActivity;
+
+ protected Handler mHandler;
+ protected Runnable mUIUpdateRunnable;
+
+ protected BaseRenderer mRenderer;
+
+ /**
+ * Called when this fragment is attached to an activity. Starts the test if the Pose service is
+ * ready.
+ */
+ @Override
+ public void onAttach(Activity context) {
+ super.onAttach(context);
+ mActivity = (TestActivity) getActivity();
+
+ if (mActivity.isPoseProviderReady()) {
+ onPoseProviderReady();
+ }
+ }
+
+ protected void initUIHandler(Runnable uiRunnable) {
+ mHandler = new Handler();
+ mUIUpdateRunnable = uiRunnable;
+ mHandler.postDelayed(mUIUpdateRunnable, UI_UPDATE_DELAY);
+ }
+
+ protected void setupButtons(View fragmentView, TestActivity.CTSTest currentPhase) {
+ final int phaseIndex = currentPhase.ordinal();
+ mBtnPass = (Button) fragmentView.findViewById(R.id.btnPass);
+ mBtnInfo = (Button) fragmentView.findViewById(R.id.btnInfo);
+ mBtnFail = (Button) fragmentView.findViewById(R.id.btnFail);
+
+ mBtnInfo.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+
+ builder.setMessage(getResources().getStringArray(R.array.phase_descriptions)[phaseIndex])
+ .setTitle(getResources().getStringArray(R.array.phase)[phaseIndex])
+ .setPositiveButton(R.string.got_it, null);
+
+ AlertDialog dialog = builder.create();
+ dialog.show();
+ }
+ });
+
+ mBtnFail.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ Intent resultIntent = getActivity().getIntent();
+ String report = "Couldn't create test report.";
+ try {
+ report = mActivity.getTestReport().getContents();
+ } catch (IOException e) {
+ Log.e(TAG, report);
+ }
+ resultIntent.putExtra(TestActivity.EXTRA_REPORT, report);
+ resultIntent.putExtra(TestActivity.EXTRA_RESULT_ID, StartActivity.ResultCode.FAILED);
+ getActivity().setResult(Activity.RESULT_OK, resultIntent);
+ getActivity().finish();
+ }
+ });
+ }
+
+ protected abstract void setupUILoop();
+
+ protected abstract void showInitialDialog();
+
+ protected String getObjectiveText(Manager.Lap lap, int waypointCount) {
+ String currentObjective = "";
+ int lapIndex = lap.ordinal();
+ if (lapIndex > 1) lapIndex = 1; // Text is same for indexes 1, 2, 3
+
+ switch (waypointCount) {
+ case 0:
+ currentObjective = getResources()
+ .getStringArray(R.array.initial_waypoint)[lapIndex];
+ break;
+ case Manager.MAX_MARKER_NUMBER - 1:
+ currentObjective = getString(R.string.obj_return_to_initial_waypoint);
+ break;
+ case Manager.MAX_MARKER_NUMBER:
+ currentObjective = "";
+ mPlaceWaypointButton.setVisibility(View.INVISIBLE);
+ break;
+ default:
+ currentObjective = getResources()
+ .getStringArray(R.array.next_waypoint)[lapIndex]
+ .replace('0', Character.forDigit(waypointCount, 10));
+ break;
+ }
+
+ return currentObjective;
+ }
+
+ /**
+ * Nullify activity to avoid memory leak.
+ */
+ @Override
+ public void onDetach() {
+ super.onDetach();
+
+ mActivity = null;
+ mHandler = null;
+ mUIUpdateRunnable = null;
+ }
+
+ @Override
+ public void onDestroyUi() {
+ if (mRenderer != null) {
+ mRenderer.onDestroy();
+ }
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ if (mRenderer != null) {
+ mRenderer.disconnectCamera();
+ }
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ if (mRenderer != null) {
+ mRenderer.connectCamera(mActivity.getPoseProvider(), getActivity());
+ }
+ }
+
+ @Override
+ public void onPoseProviderReady() {
+ showInitialDialog();
+ setupUILoop();
+ }
+
+ /**
+ * Called when a waypoint has been successfully placed by user. Shows undo snackbar.
+ */
+ @Override
+ public void onWaypointPlaced() {
+ mPlaceWaypointButton.setVisibility(View.INVISIBLE);
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Fragments/ComplexMovementFragment.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Fragments/ComplexMovementFragment.java
new file mode 100644
index 0000000..d919075
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Fragments/ComplexMovementFragment.java
@@ -0,0 +1,228 @@
+/*
+ * 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 com.android.cts.verifier.sensors.sixdof.Fragments;
+
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.sensors.sixdof.BuildConfig;
+import com.android.cts.verifier.sensors.sixdof.Activities.StartActivity;
+import com.android.cts.verifier.sensors.sixdof.Activities.TestActivity;
+import com.android.cts.verifier.sensors.sixdof.Dialogs.ComplexMovementResultDialog;
+import com.android.cts.verifier.sensors.sixdof.Interfaces.ComplexMovementListener;
+import com.android.cts.verifier.sensors.sixdof.Renderer.ComplexMovementRenderer;
+import com.android.cts.verifier.sensors.sixdof.Utils.Manager;
+import com.android.cts.verifier.sensors.sixdof.Utils.Exceptions.WaypointAreaCoveredException;
+import com.android.cts.verifier.sensors.sixdof.Utils.Exceptions.WaypointDistanceException;
+import com.android.cts.verifier.sensors.sixdof.Utils.Exceptions.WaypointRingNotEnteredException;
+import com.android.cts.verifier.sensors.sixdof.Utils.Exceptions.WaypointStartPointException;
+import com.android.cts.verifier.sensors.sixdof.Utils.Path.ComplexMovementPath;
+import com.android.cts.verifier.sensors.sixdof.Utils.Path.PathUtilityClasses.Ring;
+import com.android.cts.verifier.sensors.sixdof.Utils.ResultObjects.ResultObject;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.opengl.GLSurfaceView;
+import android.os.Bundle;
+import android.app.AlertDialog;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageButton;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import java.io.IOException;
+
+/**
+ * UI fragment for the third test.
+ */
+public class ComplexMovementFragment extends BaseUiFragment implements ComplexMovementListener {
+ private static final String TAG = "ComplexMovementFragment";
+
+ private TextView mTvObjective;
+ private TextView mTvRings;
+
+ /**
+ * Standard practice to have a static newInstance constructor. Used to pass in arguments to the
+ * fragment. We don't have any at the moment, but this is good structure for the future.
+ *
+ * @return a new Robustness test fragment.
+ */
+ public static ComplexMovementFragment newInstance() {
+ return new ComplexMovementFragment();
+ }
+
+ /**
+ * Called when the parent activity has been created. Adds the GLSurfaceView to the fragment
+ * layout.
+ */
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+
+ GLSurfaceView surfaceView = new GLSurfaceView(getActivity());
+ surfaceView.setEGLContextClientVersion(2);
+ mRenderer = new ComplexMovementRenderer(getActivity(), mActivity.getRings());
+ surfaceView.setRenderer(mRenderer);
+ mLLCameraLayout = (LinearLayout) getView().findViewById(R.id.llCamera);
+ mLLCameraLayout.addView(surfaceView);
+ Log.d(TAG, "Camera Preview add to layout");
+ }
+
+ /**
+ * Initialises all of the UI elements
+ */
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.fragment_complex_movement, container, false);
+ getActivity().setTitle(getResources().getStringArray(R.array.phase)[TestActivity.CTSTest.COMPLEX_MOVEMENT.ordinal()]);
+
+ // Set up pass/info/fail buttons.
+ setupButtons(view, TestActivity.CTSTest.COMPLEX_MOVEMENT);
+
+ mPlaceWaypointButton = (ImageButton) view.findViewById(R.id.fabPlaceWaypoint);
+ mPlaceWaypointButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ try {
+ mActivity.attemptWaypointPlacement();
+ } catch (WaypointAreaCoveredException e) {
+ Toast.makeText(getActivity(),
+ getString(R.string.error_area), Toast.LENGTH_SHORT).show();
+ } catch (WaypointDistanceException e) {
+ Toast.makeText(getActivity(),
+ getString(R.string.error_distance), Toast.LENGTH_SHORT).show();
+ } catch (WaypointStartPointException e) {
+ Toast.makeText(getActivity(),
+ getString(R.string.error_start_point), Toast.LENGTH_SHORT).show();
+ } catch (WaypointRingNotEnteredException e) {
+ Toast.makeText(getActivity(),
+ getString(R.string.error_rings_not_entered), Toast.LENGTH_SHORT).show();
+ }
+ }
+ });
+
+ mTvObjective = (TextView) view.findViewById(R.id.tvObjective);
+ mTvRings = (TextView) view.findViewById(R.id.tvRings);
+
+ return view;
+ }
+
+ /**
+ * Called after onCreateView. Starts listening for 6DoF events.
+ */
+ @Override
+ public void onViewCreated(View view, Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ mActivity.listenFor6DofData(this);
+ }
+
+ @Override
+ protected void setupUILoop() {
+ Runnable runnable = new Runnable() {
+ @Override
+ public void run() {
+ if (mActivity == null || getActivity() == null) {
+ return;
+ }
+
+ int waypointCount = mActivity.getUserGeneratedWaypoints(Manager.Lap.LAP_4).size();
+ mTvObjective.setText(getObjectiveText(Manager.Lap.LAP_4, waypointCount));
+
+ int ringCount = 0;
+ for (Ring ring : mActivity.getRings()) {
+ if (ring.getPathNumber() == waypointCount && ring.isEntered()) {
+ ringCount++;
+ }
+ }
+
+ mTvRings.setText(String.format(getString(R.string.rings_entered),
+ ringCount, ComplexMovementPath.RINGS_PER_PATH));
+
+ if (waypointCount < Manager.MAX_MARKER_NUMBER) {
+ mPlaceWaypointButton.setVisibility(View.VISIBLE);
+ }
+
+ // Update the UI again in x milliseconds.
+ if (mHandler != null) {
+ mHandler.postDelayed(this, UI_UPDATE_DELAY);
+ }
+ }
+ };
+
+ super.initUIHandler(runnable);
+ }
+
+ /**
+ * Shows initial instruction dialog
+ */
+ @Override
+ protected void showInitialDialog() {
+ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+
+ builder.setMessage(R.string.phase3_initial_message)
+ .setTitle(getResources().getStringArray(R.array.phase)[TestActivity.CTSTest.COMPLEX_MOVEMENT.ordinal()])
+ .setPositiveButton(R.string.got_it, null);
+
+ AlertDialog dialog = builder.create();
+ dialog.show();
+ }
+
+ @Override
+ public void onWaypointPlaced() {
+ super.onWaypointPlaced();
+ ((ComplexMovementRenderer) mRenderer).onWaypointPlaced(mActivity.getUserGeneratedWaypoints(Manager.Lap.LAP_4).size());
+ }
+
+ @Override
+ public void onRingEntered(Ring ring) {
+ ((ComplexMovementRenderer) mRenderer).onRingEntered(ring);
+ }
+
+ @Override
+ public void onResult(ResultObject result) {
+ ComplexMovementResultDialog dialog = ComplexMovementResultDialog.newInstance(result);
+ dialog.setTargetFragment(ComplexMovementFragment.this, DIALOG_FRAGMENT);
+ dialog.show(getActivity().getFragmentManager(), "ResultDialogFragment");
+ mPlaceWaypointButton.setVisibility(View.INVISIBLE);
+
+ if (result.hasPassed() || BuildConfig.DEBUG) {
+ mBtnPass.setEnabled(true);
+ mBtnPass.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ finishTest();
+ }
+ });
+ }
+ }
+
+ private void finishTest() {
+ Intent resultIntent = getActivity().getIntent();
+ String report = "Couldn't create test report.";
+ try {
+ report = mActivity.getTestReport().getContents();
+ } catch (IOException e) {
+ Log.e(TAG, report);
+ }
+ resultIntent.putExtra(TestActivity.EXTRA_REPORT, report);
+ resultIntent.putExtra(TestActivity.EXTRA_RESULT_ID, StartActivity.ResultCode.PASSED);
+ getActivity().setResult(Activity.RESULT_OK, resultIntent);
+ getActivity().finish();
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Fragments/DataFragment.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Fragments/DataFragment.java
new file mode 100644
index 0000000..baa01f6
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Fragments/DataFragment.java
@@ -0,0 +1,221 @@
+/*
+ * 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 com.android.cts.verifier.sensors.sixdof.Fragments;
+
+
+import com.android.cts.verifier.sensors.sixdof.Activities.TestActivity;
+import com.android.cts.verifier.sensors.sixdof.Utils.Manager;
+import com.android.cts.verifier.sensors.sixdof.Utils.TestReport;
+import com.android.cts.verifier.sensors.sixdof.Utils.Exceptions.WaypointAreaCoveredException;
+import com.android.cts.verifier.sensors.sixdof.Utils.Exceptions.WaypointDistanceException;
+import com.android.cts.verifier.sensors.sixdof.Utils.Exceptions.WaypointRingNotEnteredException;
+import com.android.cts.verifier.sensors.sixdof.Utils.Exceptions.WaypointStartPointException;
+import com.android.cts.verifier.sensors.sixdof.Utils.Path.PathUtilityClasses.Ring;
+import com.android.cts.verifier.sensors.sixdof.Utils.Path.PathUtilityClasses.Waypoint;
+import com.android.cts.verifier.sensors.sixdof.Utils.PoseProvider.AndroidPoseProvider;
+import com.android.cts.verifier.sensors.sixdof.Utils.PoseProvider.PoseData;
+import com.android.cts.verifier.sensors.sixdof.Utils.PoseProvider.PoseProvider;
+
+import android.app.Activity;
+import android.content.Context;
+import android.app.Fragment;
+
+import java.util.ArrayList;
+
+/**
+ * This currently deals with the pose data and what to do with it.
+ */
+public class DataFragment extends Fragment implements PoseProvider.PoseProviderListener {
+ private final static String TAG = "DataFragment";
+
+ private TestReport mTestReport;
+ private Manager mManager;
+
+ private PoseProvider mPoseProvider;
+ protected boolean mIsPoseProviderReady = false;
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ mPoseProvider = new AndroidPoseProvider(getActivity(), this);
+ mPoseProvider.setup();
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ mPoseProvider = null;
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ mPoseProvider.onStopPoseProviding();
+ mIsPoseProviderReady = false;
+ }
+
+ /**
+ * Start PoseProvider.
+ */
+ @Override
+ public void onSetupComplete() {
+ mPoseProvider.onStartPoseProviding();
+ }
+
+ @Override
+ public void onNewPoseData(PoseData newPoseData) {
+ if (!mIsPoseProviderReady) {
+ mIsPoseProviderReady = true;
+ mManager.onPoseProviderReady();
+ }
+
+ mManager.onNewPoseData(newPoseData);
+ }
+
+ /**
+ * Assign the listener when this fragment is attached to an activity.
+ *
+ * @param activity the activity that this fragment is attached to.
+ */
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+ initManager(activity);
+ }
+
+ private void initManager(Context context) {
+ mTestReport = new TestReport(getActivity());
+ mManager = new Manager(mTestReport);
+ mManager.setupListeners(context);
+ }
+
+ /**
+ * Nullify the listener to avoid leaking the activity.
+ */
+ @Override
+ public void onDetach() {
+ super.onDetach();
+ mManager.stopListening();
+ }
+
+ /**
+ * @return PoseProvider object associated with these tests.
+ */
+ public PoseProvider getPoseProvider() {
+ return mPoseProvider;
+ }
+
+ /**
+ * @return true if we are connected to the pose provider.
+ */
+ public boolean isPoseProviderReady() {
+ return mIsPoseProviderReady;
+ }
+
+ /**
+ * Gets all the markers (user generated waypoints) for the specified phase.
+ *
+ * @param lap the lap of the test to get the markers from
+ * @return a list of the markers
+ */
+ public ArrayList<Waypoint> getUserGeneratedWaypoints(Manager.Lap lap) {
+ switch (lap) {
+ case LAP_1:
+ return mManager.getReferencePathMarkers();
+ case LAP_2:
+ return mManager.getTestPathMarkers();
+ case LAP_3:
+ return mManager.getRobustnessMarker();
+ case LAP_4:
+ return mManager.getComplexMovementTestMarkers();
+ default:
+ throw new AssertionError("Unrecognised Lap!", null);
+ }
+ }
+
+ /**
+ * Returns a reference to the mTestReport object.
+ */
+ public TestReport getTestReport() {
+ return mTestReport;
+ }
+
+ /**
+ * Initiates the adding of a waypoint and checks if the state of the current test need to be
+ * changed.
+ *
+ * @throws WaypointDistanceException if the location is too close to another
+ * @throws WaypointAreaCoveredException if the area covered by the user is too little
+ * @throws WaypointStartPointException if the location is not close enough to the start
+ */
+ public void onWaypointPlacementAttempt()
+ throws WaypointStartPointException, WaypointDistanceException,
+ WaypointAreaCoveredException, WaypointRingNotEnteredException {
+ synchronized (TestActivity.POSE_LOCK) {
+ mManager.addPoseDataToPath(
+ mPoseProvider.getLatestPoseData().getTranslationAsFloats(), true);
+ }
+ }
+
+ /**
+ * Removes the last marker added in the current test phase.
+ */
+ public void undoWaypointPlacement() {
+ mManager.removeLastAddedMarker();
+ }
+
+ /**
+ * Returns the current phase of the test.
+ */
+ public Manager.Lap getLap() {
+ return mManager.getLap();
+ }
+
+ /**
+ * Sets the test status to executed.
+ */
+ public void testStarted() {
+ mTestReport.setTestState(TestReport.TestStatus.EXECUTED);
+ }
+
+ public void startTest(TestActivity.CTSTest newTest) {
+ switch (newTest) {
+ case ACCURACY:
+ mManager.startAccuracyTest();
+ break;
+ case ROBUSTNESS:
+ mManager.startRobustnessTest();
+ break;
+ case COMPLEX_MOVEMENT:
+ mManager.startComplexMovementTest();
+ break;
+ default:
+ throw new AssertionError("Test not recognised!");
+ }
+ }
+
+ public float getLatestDistanceData() {
+ return mManager.getRemainingPath();
+ }
+
+ public float getTimeRemaining() {
+ return mManager.getTimeRemaining();
+ }
+
+ public ArrayList<Ring> getRings() {
+ return mManager.getRings();
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Fragments/PhaseStartFragment.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Fragments/PhaseStartFragment.java
new file mode 100644
index 0000000..2013c12
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Fragments/PhaseStartFragment.java
@@ -0,0 +1,86 @@
+/*
+ * 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 com.android.cts.verifier.sensors.sixdof.Fragments;
+
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.sensors.sixdof.Activities.TestActivity;
+
+import android.os.Bundle;
+import android.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.TextView;
+
+/**
+ * Provides the instructions for a particular phase before it starts.
+ */
+public class PhaseStartFragment extends Fragment {
+ // Identifier for setting and retrieving the phase this Fragment was designed for.
+ private static final String ARG_PHASE = "ArgPhase";
+
+ Button mBtnStart;
+ TextView mTvDesc;
+
+ TestActivity.CTSTest mPhase;
+ TestActivity mActivity;
+
+ public static PhaseStartFragment newInstance(TestActivity.CTSTest phase) {
+ PhaseStartFragment fragment = new PhaseStartFragment();
+ Bundle arguments = new Bundle();
+ arguments.putSerializable(ARG_PHASE, phase);
+ fragment.setArguments(arguments);
+
+ return fragment;
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ View rootView = inflater.inflate(R.layout.fragment_start_phase, container, false);
+ mBtnStart = (Button) rootView.findViewById(R.id.btnStart);
+ mTvDesc = (TextView) rootView.findViewById(R.id.tvDesc);
+ mActivity = (TestActivity) getActivity();
+
+ mPhase = (TestActivity.CTSTest) getArguments().getSerializable(ARG_PHASE);
+
+ switch (mPhase) {
+ case ACCURACY:
+ mTvDesc.setText(getString(R.string.phase1_description));
+ getActivity().setTitle(getResources().getStringArray(R.array.phase)[TestActivity.CTSTest.ACCURACY.ordinal()]);
+ break;
+ case ROBUSTNESS:
+ mTvDesc.setText(getString(R.string.phase2_description));
+ getActivity().setTitle(getResources().getStringArray(R.array.phase)[TestActivity.CTSTest.ROBUSTNESS.ordinal()]);
+ break;
+ case COMPLEX_MOVEMENT:
+ mTvDesc.setText(getString(R.string.phase3_description));
+ getActivity().setTitle(getResources().getStringArray(R.array.phase)[TestActivity.CTSTest.COMPLEX_MOVEMENT.ordinal()]);
+ break;
+ default:
+ throw new AssertionError("Trying to start a test that doesn't exist");
+ }
+
+ mBtnStart.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ mActivity.switchToTestFragment(mPhase);
+ }
+ });
+
+ return rootView;
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Fragments/RobustnessFragment.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Fragments/RobustnessFragment.java
new file mode 100644
index 0000000..f7b089b
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Fragments/RobustnessFragment.java
@@ -0,0 +1,243 @@
+/*
+ * 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 com.android.cts.verifier.sensors.sixdof.Fragments;
+
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.sensors.sixdof.BuildConfig;
+import com.android.cts.verifier.sensors.sixdof.Activities.TestActivity;
+import com.android.cts.verifier.sensors.sixdof.Dialogs.RobustnessResultDialog;
+import com.android.cts.verifier.sensors.sixdof.Interfaces.RobustnessListener;
+import com.android.cts.verifier.sensors.sixdof.Renderer.RobustnessRenderer;
+import com.android.cts.verifier.sensors.sixdof.Renderer.RenderUtils.Colour;
+import com.android.cts.verifier.sensors.sixdof.Utils.Manager;
+import com.android.cts.verifier.sensors.sixdof.Utils.Exceptions.WaypointAreaCoveredException;
+import com.android.cts.verifier.sensors.sixdof.Utils.Exceptions.WaypointDistanceException;
+import com.android.cts.verifier.sensors.sixdof.Utils.Exceptions.WaypointRingNotEnteredException;
+import com.android.cts.verifier.sensors.sixdof.Utils.Exceptions.WaypointStartPointException;
+import com.android.cts.verifier.sensors.sixdof.Utils.Path.PathUtilityClasses.RotationData;
+import com.android.cts.verifier.sensors.sixdof.Utils.ResultObjects.ResultObject;
+
+import android.app.Activity;
+import android.opengl.GLSurfaceView;
+import android.os.Bundle;
+import android.app.AlertDialog;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageButton;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+import android.widget.Toast;
+
+/**
+ * UI fragment for the second test.
+ */
+public class RobustnessFragment extends BaseUiFragment implements RobustnessListener {
+ private static final String TAG = "RobustnessFragment";
+ private static final Object TIMER_LOCK = new Object();
+
+ private TextView mTvTime;
+ private TextView mTvPassColour;
+ private TextView mTvObjective;
+
+ private boolean mIsPassing = false;
+ private boolean mResultGiven = false;
+
+ /**
+ * Standard practice to have a static newInstance constructor. Used to pass in arguments to the
+ * fragment. We don't have any at the moment, but this is good structure for the future.
+ *
+ * @return a new Robustness test fragment.
+ */
+ public static RobustnessFragment newInstance() {
+ RobustnessFragment fragment = new RobustnessFragment();
+ return fragment;
+ }
+
+ /**
+ * Called when the parent activity has been created. Adds the GLSurfaceView to the fragment
+ * layout.
+ */
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+
+ GLSurfaceView surfaceView = new GLSurfaceView(getActivity());
+ surfaceView.setEGLContextClientVersion(2);
+ mRenderer = new RobustnessRenderer(getActivity());
+ surfaceView.setRenderer(mRenderer);
+ mLLCameraLayout = (LinearLayout) getView().findViewById(R.id.llCamera);
+ mLLCameraLayout.addView(surfaceView);
+ Log.d(TAG, "Camera Preview add to layout");
+ }
+
+ /**
+ * Initialises all of the UI elements.
+ */
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.fragment_robustness, container, false);
+ getActivity().setTitle(getResources().getStringArray(R.array.phase)[TestActivity.CTSTest.ROBUSTNESS.ordinal()]);
+
+ // Set up pass/info/fail buttons
+ setupButtons(view, TestActivity.CTSTest.ROBUSTNESS);
+
+ mPlaceWaypointButton = (ImageButton) view.findViewById(R.id.fabPlaceWaypoint);
+ mPlaceWaypointButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ try {
+ mActivity.attemptWaypointPlacement();
+ } catch (WaypointDistanceException e) {
+ Toast.makeText(getActivity(),
+ getString(R.string.error_distance), Toast.LENGTH_SHORT).show();
+ } catch (WaypointAreaCoveredException e) {
+ Toast.makeText(getActivity(),
+ getString(R.string.error_area), Toast.LENGTH_SHORT).show();
+ } catch (WaypointStartPointException e) {
+ Toast.makeText(getActivity(),
+ getString(R.string.error_start_point), Toast.LENGTH_SHORT).show();
+ } catch (WaypointRingNotEnteredException e) {
+ throw new AssertionError(
+ "WaypointRingNotEnteredException when not in 3rd test", e);
+ }
+ }
+ });
+
+ mTvTime = (TextView) view.findViewById(R.id.tvTimer);
+ mTvPassColour = (TextView) view.findViewById(R.id.tvPassColour);
+ mTvObjective = (TextView) view.findViewById(R.id.tvObjective);
+
+ return view;
+ }
+
+ /**
+ * Called after onCreateView. Starts listening for 6DoF events.
+ */
+ @Override
+ public void onViewCreated(View view, Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ mActivity.listenFor6DofData(this);
+ }
+
+ @Override
+ protected void setupUILoop() {
+ Runnable runnable = new Runnable() {
+ @Override
+ public void run() {
+ if (mActivity == null || getActivity() == null) {
+ return;
+ }
+
+ String stringTimeRemaining;
+ String decimalTimeRemaining = (mActivity.getTimeRemaining() / 1000f) + "";
+ synchronized (TIMER_LOCK) {
+ stringTimeRemaining = String.format(getString(R.string.time_remaining), decimalTimeRemaining);
+ }
+
+ synchronized (TIMER_LOCK) {
+ if (mIsPassing) {
+ mTvPassColour.setBackgroundColor(getResources().getColor(R.color.green));
+ } else {
+ mTvPassColour.setBackgroundColor(getResources().getColor(R.color.red));
+ }
+ }
+
+ int waypointCount = mActivity.getUserGeneratedWaypoints(Manager.Lap.LAP_3).size();
+ mTvObjective.setText(getObjectiveText(Manager.Lap.LAP_3, waypointCount));
+
+ if (waypointCount < Manager.MAX_MARKER_NUMBER && !mResultGiven) {
+ mPlaceWaypointButton.setVisibility(View.VISIBLE);
+ mTvTime.setText(stringTimeRemaining);
+ } else {
+ mTvTime.setText("");
+ }
+
+ //Update the UI again in x milliseconds.
+ if (mHandler != null) {
+ mHandler.postDelayed(this, UI_UPDATE_DELAY);
+ }
+ }
+ };
+
+ super.initUIHandler(runnable);
+ }
+
+ /**
+ * Shows initial instruction dialog.
+ */
+ @Override
+ protected void showInitialDialog() {
+ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+
+ builder.setMessage(R.string.phase2_initial_message)
+ .setTitle(getResources().getStringArray(R.array.phase)[TestActivity.CTSTest.ROBUSTNESS.ordinal()])
+ .setPositiveButton(R.string.got_it, null);
+
+ AlertDialog dialog = builder.create();
+ dialog.show();
+ }
+
+ @Override
+ public void onResult(final ResultObject result) {
+ getActivity().runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ mResultGiven = true;
+ RobustnessResultDialog dialog = RobustnessResultDialog.newInstance(result);
+ dialog.setTargetFragment(RobustnessFragment.this, DIALOG_FRAGMENT);
+ dialog.show(getActivity().getFragmentManager(), "ResultDialogFragment");
+ mPlaceWaypointButton.setVisibility(View.INVISIBLE);
+
+ if (result.hasPassed() || BuildConfig.DEBUG) {
+ mBtnPass.setEnabled(true);
+ mBtnPass.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ onReadyForPhase3();
+ }
+ });
+ }
+ }
+ });
+ }
+
+ private void onReadyForPhase3() {
+ mActivity.switchToStartFragment(TestActivity.CTSTest.COMPLEX_MOVEMENT);
+ }
+
+ @Override
+ public void onNewRotationData(RotationData data) {
+ synchronized (TIMER_LOCK) {
+ mIsPassing = data.getRotationTestState();
+ }
+
+ if (mRenderer != null) {
+ if (data.getRotationTestState()) {
+ ((RobustnessRenderer) mRenderer).setLineColor(Colour.GREEN);
+ } else {
+ ((RobustnessRenderer) mRenderer).setLineColor(Colour.RED);
+ }
+
+ if (mActivity.getUserGeneratedWaypoints(Manager.Lap.LAP_3).size() > 0) {
+ ((RobustnessRenderer) mRenderer).updateCurrentAngle(data.getCurrentAngle());
+ ((RobustnessRenderer) mRenderer).updateTargetAngle(data.getTargetAngle());
+ }
+ }
+ }
+}
diff --git a/hostsidetests/services/activitymanager/app/src/android/server/app/BottomLeftLayoutActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Interfaces/AccuracyListener.java
similarity index 72%
copy from hostsidetests/services/activitymanager/app/src/android/server/app/BottomLeftLayoutActivity.java
copy to apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Interfaces/AccuracyListener.java
index 27a6fe2..4b188f1 100644
--- a/hostsidetests/services/activitymanager/app/src/android/server/app/BottomLeftLayoutActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Interfaces/AccuracyListener.java
@@ -13,10 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package com.android.cts.verifier.sensors.sixdof.Interfaces;
-package android.server.app;
+/**
+ * Interface that handles UI listeners that are only used in the Accuracy test.
+ */
+public interface AccuracyListener {
+ void onReset();
-import android.app.Activity;
-
-public class BottomLeftLayoutActivity extends Activity {
+ void lap1Complete();
}
diff --git a/hostsidetests/services/activitymanager/app/src/android/server/app/BottomLeftLayoutActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Interfaces/BaseUiListener.java
similarity index 61%
copy from hostsidetests/services/activitymanager/app/src/android/server/app/BottomLeftLayoutActivity.java
copy to apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Interfaces/BaseUiListener.java
index 27a6fe2..302bdd6 100644
--- a/hostsidetests/services/activitymanager/app/src/android/server/app/BottomLeftLayoutActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Interfaces/BaseUiListener.java
@@ -13,10 +13,19 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package com.android.cts.verifier.sensors.sixdof.Interfaces;
-package android.server.app;
+import com.android.cts.verifier.sensors.sixdof.Utils.ResultObjects.ResultObject;
-import android.app.Activity;
+/**
+ * Interface that handles UI listeners that are used in every test.
+ */
+public interface BaseUiListener {
+ void onPoseProviderReady();
-public class BottomLeftLayoutActivity extends Activity {
+ void onWaypointPlaced();
+
+ void onResult(ResultObject result);
+
+ void onDestroyUi();
}
diff --git a/hostsidetests/services/activitymanager/app/src/android/server/app/BottomLeftLayoutActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Interfaces/ComplexMovementListener.java
similarity index 65%
copy from hostsidetests/services/activitymanager/app/src/android/server/app/BottomLeftLayoutActivity.java
copy to apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Interfaces/ComplexMovementListener.java
index 27a6fe2..7f3bfe9 100644
--- a/hostsidetests/services/activitymanager/app/src/android/server/app/BottomLeftLayoutActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Interfaces/ComplexMovementListener.java
@@ -13,10 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package com.android.cts.verifier.sensors.sixdof.Interfaces;
-package android.server.app;
+import com.android.cts.verifier.sensors.sixdof.Utils.Path.PathUtilityClasses.Ring;
-import android.app.Activity;
-
-public class BottomLeftLayoutActivity extends Activity {
+/**
+ * Interface that handles UI listeners that are only used in the Complex Movement test.
+ */
+public interface ComplexMovementListener {
+ void onRingEntered(Ring ring);
}
diff --git a/hostsidetests/services/activitymanager/app/src/android/server/app/BottomLeftLayoutActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Interfaces/RobustnessListener.java
similarity index 65%
copy from hostsidetests/services/activitymanager/app/src/android/server/app/BottomLeftLayoutActivity.java
copy to apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Interfaces/RobustnessListener.java
index 27a6fe2..7821322 100644
--- a/hostsidetests/services/activitymanager/app/src/android/server/app/BottomLeftLayoutActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Interfaces/RobustnessListener.java
@@ -13,10 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package com.android.cts.verifier.sensors.sixdof.Interfaces;
-package android.server.app;
+import com.android.cts.verifier.sensors.sixdof.Utils.Path.PathUtilityClasses.RotationData;
-import android.app.Activity;
-
-public class BottomLeftLayoutActivity extends Activity {
+/**
+ * Interface that handles UI listeners that are only used in the Robustness test.
+ */
+public interface RobustnessListener {
+ void onNewRotationData(RotationData data);
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Renderer/AccuracyRenderer.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Renderer/AccuracyRenderer.java
new file mode 100644
index 0000000..287b31a
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Renderer/AccuracyRenderer.java
@@ -0,0 +1,40 @@
+/*
+ * 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 com.android.cts.verifier.sensors.sixdof.Renderer;
+
+import android.content.Context;
+
+/**
+ * Renderer for the Accuracy test.
+ */
+public class AccuracyRenderer extends BaseRenderer {
+ public AccuracyRenderer(Context context) {
+ super(context);
+ }
+
+ @Override
+ protected void doPreRenderingSetup() {
+ // Set view and projection matrix to orthogonal so that camera preview fills the screen.
+ mViewMatrix = mOrthogonalViewMatrix;
+ mProjectionMatrix = mOrthogonalProjectionMatrix;
+ }
+
+ @Override
+ protected void doTestSpecificRendering() {
+ // Update the texture with the latest camera frame if there is an update pending.
+ updateCameraTexture();
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Renderer/BaseRenderer.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Renderer/BaseRenderer.java
new file mode 100644
index 0000000..bac1935
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Renderer/BaseRenderer.java
@@ -0,0 +1,344 @@
+/*
+ * 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 com.android.cts.verifier.sensors.sixdof.Renderer;
+
+import com.android.cts.verifier.sensors.sixdof.Renderer.RenderUtils.CameraStreamManager;
+import com.android.cts.verifier.sensors.sixdof.Renderer.RenderUtils.DrawParameters;
+import com.android.cts.verifier.sensors.sixdof.Renderer.Renderable.CameraPreviewRenderable;
+import com.android.cts.verifier.sensors.sixdof.Utils.MathsUtils;
+import com.android.cts.verifier.sensors.sixdof.Utils.PoseProvider.PoseProvider;
+
+import static com.android.cts.verifier.sensors.sixdof.Utils.MathsUtils.MATRIX_4X4;
+import static com.android.cts.verifier.sensors.sixdof.Utils.MathsUtils.ORIENTATION_360_ANTI_CLOCKWISE;
+import static com.android.cts.verifier.sensors.sixdof.Utils.MathsUtils.X;
+import static com.android.cts.verifier.sensors.sixdof.Utils.MathsUtils.Y;
+import static com.android.cts.verifier.sensors.sixdof.Utils.MathsUtils.Z;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.opengl.GLES20;
+import android.opengl.GLSurfaceView;
+import android.opengl.Matrix;
+import android.util.Log;
+import android.view.Display;
+import android.view.Surface;
+import android.view.WindowManager;
+
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.opengles.GL10;
+
+/**
+ * Abstract class that connects to Android Camera to use as an OpenGL texture.
+ */
+public abstract class BaseRenderer implements GLSurfaceView.Renderer {
+ private static final String TAG = "BaseRenderer";
+ private static final int ORIENTATION_COUNT = 4;
+
+ protected float[] mViewMatrix = new float[MATRIX_4X4];
+ protected float[] mOrthogonalViewMatrix = new float[MATRIX_4X4];
+ protected float[] mProjectionMatrix = new float[MATRIX_4X4];
+
+ protected float[] mOrthogonalProjectionMatrix = new float[MATRIX_4X4];
+ protected float[] mFrustrumProjectionMatrix = new float[MATRIX_4X4];
+
+ protected DrawParameters mDrawParameters;
+
+ protected float[] mCameraCoordinates;
+
+ protected PoseProvider mPoseProvider;
+ protected boolean mIsValid = false;
+
+ protected CameraPreviewRenderable mCameraPreview;
+ protected double mLastRGBFrameTimestamp = -1;
+
+ private int mCameraPreviewRotation = 0;
+
+ protected int mOpenGlRotation = 0;
+ protected float[] mOpenGlUpVector;
+
+ private Context mContext;
+
+ public BaseRenderer(Context context) {
+ mContext = context;
+ mOpenGlRotation = getDeviceRotation(context);
+ mOpenGlUpVector = getUpVector(mOpenGlRotation);
+ mCameraPreviewRotation = CameraStreamManager.getRotation(context, mOpenGlRotation);
+ }
+
+ @Override
+ public void onSurfaceCreated(GL10 glUnused, EGLConfig config) {
+ // Set the background clear color to black.
+ GLES20.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+
+ // Enable depth testing
+ GLES20.glEnable(GLES20.GL_DEPTH_TEST);
+
+ mCameraPreview = new CameraPreviewRenderable();
+
+ resetViewMatrix();
+ }
+
+ protected void resetViewMatrix() {
+ // Position the eye in front of the origin.
+ final float eyeX = 0.0f;
+ final float eyeY = 0.0f;
+ final float eyeZ = 0.0f;
+
+ // We are looking toward the distance
+ final float lookX = 0.0f;
+ final float lookY = 0.0f;
+ final float lookZ = -5.0f;
+
+ // Set our up vector. This is where our head would be pointing were we holding the camera.
+ float[] upVector = getUpVector(mCameraPreviewRotation);
+
+ // Set the view matrix.
+ Matrix.setLookAtM(mViewMatrix, 0,
+ eyeX, eyeY, eyeZ,
+ lookX, lookY, lookZ,
+ upVector[X], upVector[Y], upVector[Z]);
+ Matrix.setLookAtM(mOrthogonalViewMatrix, 0,
+ eyeX, eyeY, eyeZ,
+ lookX, lookY, lookZ,
+ upVector[X], upVector[Y], upVector[Z]);
+ }
+
+ @Override
+ public void onSurfaceChanged(GL10 glUnused, int width, int height) {
+ // Set the OpenGL viewport to the same size as the surface.
+ GLES20.glViewport(0, 0, width, height);
+
+ // Create a new perspective projection matrix. The height will stay the same
+ // while the width will vary as per aspect ratio.
+ // This project matrix does not take into account the camera intrinsics and should not be
+ // used for AR purposes.
+ final float ratio = (float) width / height;
+ float left = -ratio;
+ float right = ratio;
+ float bottom = -1.0f;
+ float top = 1.0f;
+ final float near = 1.0f;
+ final float far = 10.0f;
+
+ boolean invertAxis = false;
+
+ switch (mCameraPreviewRotation) {
+ case MathsUtils.ORIENTATION_0:
+ case MathsUtils.ORIENTATION_180_ANTI_CLOCKWISE:
+ case MathsUtils.ORIENTATION_360_ANTI_CLOCKWISE:
+ break;
+ case MathsUtils.ORIENTATION_90_ANTI_CLOCKWISE:
+ case MathsUtils.ORIENTATION_270_ANTI_CLOCKWISE:
+ // Invert aspect ratio.
+ invertAxis = true;
+ bottom = -ratio;
+ top = ratio;
+ left = -1.0f;
+ right = 1.0f;
+ break;
+ default:
+ // Unexpected orientation, error out.
+ throw new RuntimeException("Unexpected orientation that cannot be dealt with!");
+ }
+
+ mCameraCoordinates = getCameraCoordinates(left, right, bottom, top);
+
+ // Give camera preview reference to the context so that it can connect to the camera.
+ mCameraPreview.initialiseCameraPreview(mCameraCoordinates, invertAxis, mContext);
+
+ Matrix.orthoM(mOrthogonalProjectionMatrix, 0, left, right, bottom, top, near, far);
+ Matrix.frustumM(mFrustrumProjectionMatrix, 0, left, right, bottom, top, near, far);
+
+ mProjectionMatrix = mOrthogonalProjectionMatrix;
+
+ mDrawParameters = new DrawParameters();
+
+ mIsValid = true;
+ }
+
+ @Override
+ public void onDrawFrame(GL10 glUnused) {
+ GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
+ doPreRenderingSetup();
+ doCoreRendering();
+ doTestSpecificRendering();
+ }
+
+ private void doCoreRendering() {
+ mDrawParameters.update(mViewMatrix, mProjectionMatrix);
+ mCameraPreview.draw(mDrawParameters);
+ }
+
+ protected synchronized void updateCameraTexture() {
+ mLastRGBFrameTimestamp = mCameraPreview.updateTexture();
+ }
+
+ /**
+ * Setup up view and projecttion matrices to be the ones you want for this draw call.
+ */
+ protected abstract void doPreRenderingSetup();
+
+ /**
+ * Do rendering that is unique to each test.
+ */
+ protected abstract void doTestSpecificRendering();
+
+ /**
+ * Where to position the camera preview on the screen. Can be overridden by sub classes.
+ */
+ protected float[] getCameraCoordinates(float left, float right, float bottom, float top) {
+ switch (mCameraPreviewRotation) {
+ case MathsUtils.ORIENTATION_0:
+ case MathsUtils.ORIENTATION_180_ANTI_CLOCKWISE:
+ case MathsUtils.ORIENTATION_360_ANTI_CLOCKWISE:
+ // Normal aspect ratio.
+ return new float[]{
+ left, top, 0.0f,
+ left, bottom, 0.0f,
+ right, top, 0.0f,
+ left, bottom, 0.0f,
+ right, bottom, 0.0f,
+ right, top, 0.0f,
+ };
+ case MathsUtils.ORIENTATION_90_ANTI_CLOCKWISE:
+ case MathsUtils.ORIENTATION_270_ANTI_CLOCKWISE:
+ // Inverted aspect ratio.
+ return new float[]{
+ bottom, right, 0.0f,
+ bottom, left, 0.0f,
+ top, right, 0.0f,
+ bottom, left, 0.0f,
+ top, left, 0.0f,
+ top, right, 0.0f,
+ };
+ default:
+ // Unexpected orientation, error out.
+ throw new RuntimeException("Unexpected orientation that cannot be dealt with!");
+ }
+ }
+
+
+ /**
+ * Saves PoseProvider object for later so we can connect to camera at appropriate time.
+ */
+ public void connectCamera(PoseProvider poseProvider, Context context) {
+ // Save these for later so we can connect to camera after mCameraPreview has been
+ // initialised. Also used to setup extrinsics in ComplexMovementRenderer.
+ mPoseProvider = poseProvider;
+ mContext = context;
+ }
+
+ public void disconnectCamera() {
+ mCameraPreview.disconnectCamera();
+ }
+
+ public void onDestroy() {
+ mPoseProvider = null;
+ mContext = null;
+
+ if (mCameraPreview != null) {
+ mCameraPreview.destroy();
+ mCameraPreview = null;
+ }
+ }
+
+ private static float[] getUpVector(int rotation) {
+ float [] upVector = new float[MathsUtils.VECTOR_3D];
+
+ switch (rotation) {
+ case MathsUtils.ORIENTATION_0:
+ case ORIENTATION_360_ANTI_CLOCKWISE:
+ upVector[X] = 0.0f;
+ upVector[Y] = 1.0f;
+ upVector[Z] = 0.0f;
+ break;
+ case MathsUtils.ORIENTATION_90_ANTI_CLOCKWISE:
+ upVector[X] = -1.0f;
+ upVector[Y] = 0.0f;
+ upVector[Z] = 0.0f;
+ break;
+ case MathsUtils.ORIENTATION_180_ANTI_CLOCKWISE:
+ upVector[X] = 0.0f;
+ upVector[Y] = -1.0f;
+ upVector[Z] = 0.0f;
+ break;
+ case MathsUtils.ORIENTATION_270_ANTI_CLOCKWISE:
+ upVector[X] = 1.0f;
+ upVector[Y] = 0.0f;
+ upVector[Z] = 0.0f;
+ break;
+ default:
+ // Unexpected orientation, error out.
+ throw new RuntimeException("Unexpected orientation that cannot be dealt with!");
+ }
+
+ return upVector;
+ }
+
+ public static int getDeviceRotation(Context context) {
+ WindowManager windowManager = (WindowManager)
+ context.getSystemService(Context.WINDOW_SERVICE);
+ final Display display = windowManager.getDefaultDisplay();
+ int naturalOrientation = Configuration.ORIENTATION_LANDSCAPE;
+ int configOrientation = context.getResources().getConfiguration().orientation;
+ switch (display.getRotation()) {
+ case Surface.ROTATION_0:
+ case Surface.ROTATION_180:
+ // We are currently in the same basic orientation as the natural orientation.
+ naturalOrientation = configOrientation;
+ break;
+ case Surface.ROTATION_90:
+ case Surface.ROTATION_270:
+ // We are currently in the other basic orientation to the natural orientation.
+ naturalOrientation = (configOrientation == Configuration.ORIENTATION_LANDSCAPE) ?
+ Configuration.ORIENTATION_PORTRAIT : Configuration.ORIENTATION_LANDSCAPE;
+ break;
+ default:
+ // Unexpected orientation, error out.
+ throw new RuntimeException("Unexpected orientation that cannot be dealt with!");
+ }
+
+ // Since the map starts at portrait, we need to offset if this device's natural orientation
+ // is landscape.
+ int indexOffset = 0;
+ if (naturalOrientation == Configuration.ORIENTATION_LANDSCAPE) {
+ indexOffset = 1;
+ }
+
+ // Get rotation as a clockwise rotation.
+ int currentRotation = ORIENTATION_COUNT - display.getRotation();
+
+ // Check for reverse rotation direction and currentRotation if required.
+ try {
+ if (context.getResources().getBoolean(context.getResources().getSystem().getIdentifier(
+ "config_reverseDefaultRotation", "bool", "android"))) {
+ currentRotation = display.getRotation();
+ }
+ } catch (Resources.NotFoundException e) {
+ // If resource is not found, assume default rotation and continue.
+ Log.d(TAG, "Cannot determine device rotation direction, assuming default");
+ }
+
+ int currentOrientation = (currentRotation + indexOffset);
+ int defaultOrientation = indexOffset;
+
+ int difference = (currentOrientation - defaultOrientation) % ORIENTATION_COUNT;
+ difference = difference * 90;
+
+ return difference;
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Renderer/ComplexMovementRenderer.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Renderer/ComplexMovementRenderer.java
new file mode 100644
index 0000000..3ac6b21
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Renderer/ComplexMovementRenderer.java
@@ -0,0 +1,262 @@
+/*
+ * 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 com.android.cts.verifier.sensors.sixdof.Renderer;
+
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.sensors.sixdof.Renderer.RenderUtils.ModelMatrixCalculator;
+import com.android.cts.verifier.sensors.sixdof.Renderer.RenderUtils.ObjImporter;
+import com.android.cts.verifier.sensors.sixdof.Renderer.Renderable.ConeRenderable;
+import com.android.cts.verifier.sensors.sixdof.Renderer.Renderable.Light;
+import com.android.cts.verifier.sensors.sixdof.Renderer.Renderable.RingRenderable;
+import com.android.cts.verifier.sensors.sixdof.Utils.MathsUtils;
+import com.android.cts.verifier.sensors.sixdof.Utils.Path.PathUtilityClasses.Ring;
+import com.android.cts.verifier.sensors.sixdof.Utils.PoseProvider.Intrinsics;
+import com.android.cts.verifier.sensors.sixdof.Utils.PoseProvider.PoseData;
+
+import static com.android.cts.verifier.sensors.sixdof.Utils.MathsUtils.MATRIX_4X4;
+
+import android.content.Context;
+import android.media.MediaPlayer;
+import android.opengl.GLES20;
+import android.opengl.Matrix;
+
+import java.util.ArrayList;
+
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.opengles.GL10;
+
+/**
+ * Renderer for the robustness test
+ */
+public class ComplexMovementRenderer extends BaseRenderer {
+ private static final String TAG = "ComplexMovementRenderer";
+ private static final float[] DEFAULT_LIGHT_POSITION = new float[]{
+ 0.0f, 3.0f, 0.0f};
+ private static final Object RING_LOCK = new Object();
+ private ModelMatrixCalculator mCameraModelMatrixCalculator;
+ private ConeRenderable mCone;
+ private Light mLight;
+ private float[] mPoseViewMatrix = new float[MATRIX_4X4];
+ private float[] mAugmentedRealityProjectMatrix = new float[MATRIX_4X4];
+
+ protected boolean mIsCameraConfigured = false;
+
+ protected double mCameraPoseTimestamp = 0;
+ private PoseData mLastFramePose;
+
+ private Context mContext;
+
+ private int mWaypointCount = 0;
+ private MediaPlayer mMediaPlayer;
+ private ArrayList<Ring> mRings;
+
+ public ComplexMovementRenderer(Context context, ArrayList<Ring> rings) {
+ super(context);
+ mCameraModelMatrixCalculator = new ModelMatrixCalculator(mOpenGlRotation);
+ mContext = context;
+ mMediaPlayer = MediaPlayer.create(context, R.raw.ring_sound);
+ mRings = rings;
+ }
+
+ @Override
+ public void onSurfaceCreated(GL10 glUnused, EGLConfig config) {
+ super.onSurfaceCreated(glUnused, config);
+ mCone = new ConeRenderable(mOpenGlRotation, mOpenGlUpVector);
+ mLight = new Light(DEFAULT_LIGHT_POSITION, 2.0f);
+ setUpExtrinsics();
+
+ ObjImporter.ObjectData ringData = ObjImporter.parse(mContext.getResources(), R.raw.ring_obj);
+
+ for (Ring ring : mRings) {
+ final float[] position =
+ MathsUtils.convertToOpenGlCoordinates(ring.getLocation(), mOpenGlRotation);
+ final float[] rotation =
+ MathsUtils.convertToOpenGlCoordinates(ring.getRingRotation(), mOpenGlRotation);
+ RingRenderable ringRenderable = new RingRenderable(position, rotation, mOpenGlUpVector);
+ ringRenderable.initialise(ringData);
+ ring.setRingRenderable(ringRenderable);
+ }
+
+ ObjImporter.ObjectData coneData = ObjImporter.parse(mContext.getResources(), R.raw.cone_obj);
+ mCone.initialise(coneData);
+ }
+
+ @Override
+ protected void doPreRenderingSetup() {
+ // Set up drawing of background camera preview (orthogonal).
+ mViewMatrix = mOrthogonalViewMatrix;
+ mProjectionMatrix = mOrthogonalProjectionMatrix;
+ }
+
+ @Override
+ protected void doTestSpecificRendering() {
+ GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT);
+ if (mPoseProvider != null) {
+ // Update the texture with the latest camera frame.
+ updateCameraTexture();
+
+ // We delay the camera set-up until now because if we do it earlier (i.e., when the
+ // camera is connected to the renderer) the PoseProvider service may still not have the
+ // necessary intrinsic and extrinsic transformation information available.
+ if (!mIsCameraConfigured) {
+ configureCamera();
+ }
+
+ // Calculate the device pose at the camera frame update time.
+ mLastFramePose = mPoseProvider.getLatestPoseData();
+ // Update the camera pose from the renderer
+ updateRenderCameraPose(mLastFramePose);
+ // Update the MV matrix with new pose data.
+ updatePoseViewMatrix();
+ // Update light with new translation.
+ mLight.updateLightPosition(MathsUtils.convertToOpenGlCoordinates(
+ mLastFramePose.getTranslationAsFloats(), mOpenGlRotation));
+ mCameraPoseTimestamp = mLastFramePose.timestamp;
+ }
+
+ // Render objects with latest pose information available.
+ renderAugmentedRealityObjects();
+ }
+
+ private void renderAugmentedRealityObjects() {
+ // Set up projection matrix to match camera intrinsics.
+ mProjectionMatrix = mAugmentedRealityProjectMatrix;
+ // Set up view matrix to match current device positioning.
+ mViewMatrix = mPoseViewMatrix;
+
+ mDrawParameters.update(mViewMatrix, mProjectionMatrix, mLight);
+ for (Ring ring : mRings) {
+ // If we have placed the initial waypoint, we want rings for the first path, path 0.
+ if (ring.getPathNumber() == mWaypointCount && !ring.isEntered()) {
+ // Only draw the rings that are on our current path and have not been entered.
+ ring.getRingRenderable().draw(mDrawParameters);
+ }
+ }
+ // Clear depth buffer so cone does not clip with rings.
+ GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT);
+
+ // Set cone to look at nearest ring.
+ boolean lookingAt = false;
+ for (Ring ring : mRings) {
+ if (!ring.isEntered() && !lookingAt && ring.getPathNumber() == mWaypointCount) {
+ // If the ring has not been entered, the cone has not been set to look at anything
+ // yet, and we are on the correct lap for this ring.
+
+ mCone.updateModelMatrix(mLastFramePose.getTranslationAsFloats(),
+ mLastFramePose.getRotationAsFloats(), ring.getLocation());
+ lookingAt = true;
+ }
+ }
+
+ if (lookingAt) {
+ // Only draw the cone if it has something to look at.
+ mCone.draw(mDrawParameters);
+ }
+ }
+
+ protected void configureCamera() {
+ // This should never happen, but it never hurts to double-check.
+ if (mPoseProvider == null) {
+ return;
+ }
+
+ Intrinsics intrinsics = mPoseProvider.getIntrinsics();
+
+ mAugmentedRealityProjectMatrix = calculateProjectionMatrix(
+ intrinsics.getWidth(), intrinsics.getHeight(),
+ intrinsics.getFocalLengthInPixelsX(), intrinsics.getFocalLengthInPixelsY());
+ mIsCameraConfigured = true;
+ }
+
+ /**
+ * Called when a waypoint is placed in the last test. Used to show and hide rings.
+ *
+ * @param waypointCount Number of waypoints placed.
+ */
+ public void onWaypointPlaced(int waypointCount) {
+ mWaypointCount = waypointCount;
+ }
+
+ /**
+ * Called when a ring has been entered. Plays a sound and then hides the ring.
+ *
+ * @param ring Ring that has just been entered.
+ */
+ public void onRingEntered(Ring ring) {
+ synchronized (RING_LOCK) {
+ ring.setSoundPlayed(true);
+ }
+ mMediaPlayer.start();
+ }
+
+ /**
+ * Setup the extrinsics of the device.
+ */
+ private void setUpExtrinsics() {
+ }
+
+ /**
+ * Update the scene camera based on the provided pose. The
+ * device pose should match the pose of the device at the time the last rendered RGB frame.
+ */
+ public void updateRenderCameraPose(PoseData devicePose) {
+ mCameraModelMatrixCalculator.updateModelMatrix(devicePose.getTranslationAsFloats(),
+ devicePose.getRotationAsFloats());
+ }
+
+ /**
+ * Update the view matrix of the Renderer to follow the position of the device in the current
+ * perspective.
+ */
+ public void updatePoseViewMatrix() {
+ float[] invertModelMat = new float[MATRIX_4X4];
+ Matrix.setIdentityM(invertModelMat, 0);
+
+ float[] temporaryMatrix = new float[MATRIX_4X4];
+ Matrix.setIdentityM(temporaryMatrix, 0);
+
+ Matrix.setIdentityM(mPoseViewMatrix, 0);
+ Matrix.invertM(invertModelMat, 0,
+ mCameraModelMatrixCalculator.getModelMatrix(), 0);
+ Matrix.multiplyMM(temporaryMatrix, 0, mPoseViewMatrix, 0,
+ invertModelMat, 0);
+ System.arraycopy(temporaryMatrix, 0, mPoseViewMatrix, 0, MATRIX_4X4);
+ }
+
+ /**
+ * Use camera intrinsics to calculate the projection Matrix.
+ */
+ private float[] calculateProjectionMatrix(int width, int height,
+ double focalLengthX, double focalLengthY) {
+ // Uses frustumM to create a projection matrix taking into account calibrated camera
+ // intrinsic parameter.
+ // Reference: http://ksimek.github.io/2013/06/03/calibrated_cameras_in_opengl/
+ float near = 0.1f;
+ float far = 100f;
+
+ float xScale = (float) (near / focalLengthX);
+ float yScale = (float) (near / focalLengthY);
+
+ float[] projectionMatrix = new float[16];
+ Matrix.frustumM(projectionMatrix, 0,
+ xScale * -width / 2.0f,
+ xScale * width / 2.0f,
+ yScale * -height / 2.0f,
+ yScale * height / 2.0f,
+ near, far);
+ return projectionMatrix;
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Renderer/RenderUtils/CameraStreamManager.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Renderer/RenderUtils/CameraStreamManager.java
new file mode 100644
index 0000000..c2d6f80
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Renderer/RenderUtils/CameraStreamManager.java
@@ -0,0 +1,325 @@
+/*
+ * Copyright 2014 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 com.android.cts.verifier.sensors.sixdof.Renderer.RenderUtils;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.graphics.SurfaceTexture;
+import android.hardware.camera2.CameraAccessException;
+import android.hardware.camera2.CameraCaptureSession;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CameraManager;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.CaptureResult;
+import android.hardware.camera2.TotalCaptureResult;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.util.Log;
+import android.util.Size;
+import android.view.Display;
+import android.view.Surface;
+import android.view.WindowManager;
+
+import java.util.Arrays;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+@TargetApi(21)
+public class CameraStreamManager {
+
+ private Context mContext;
+ private SurfaceTexture mSurfaceTextureToStreamTo;
+ private int mWidth;
+ private int mHeight;
+
+ /**
+ * Tag for the {@link Log}.
+ */
+ private static final String TAG = "Camera2BasicFragment";
+
+ /**
+ * ID of the current {@link CameraDevice}.
+ */
+ private static final String CAMERA_ID = "0";
+
+ /**
+ * A {@link CameraCaptureSession } for camera preview.
+ */
+ private CameraCaptureSession mCaptureSession;
+
+ /**
+ * A reference to the opened {@link CameraDevice}.
+ */
+ private CameraDevice mCameraDevice;
+
+ /**
+ * The {@link android.util.Size} of camera preview.
+ */
+ private Size mPreviewSize;
+
+ /**
+ * {@link CameraDevice.StateCallback} is called when {@link CameraDevice} changes its state.
+ */
+ private final CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() {
+
+ @Override
+ public void onOpened(CameraDevice cameraDevice) {
+ // This method is called when the camera is opened. We start camera preview here.
+ mCameraOpenCloseLock.release();
+ mCameraDevice = cameraDevice;
+ createCameraPreviewSession();
+ }
+
+ @Override
+ public void onDisconnected(CameraDevice cameraDevice) {
+ mCameraOpenCloseLock.release();
+ cameraDevice.close();
+ mCameraDevice = null;
+ }
+
+ @Override
+ public void onError(CameraDevice cameraDevice, int error) {
+ mCameraOpenCloseLock.release();
+ cameraDevice.close();
+ mCameraDevice = null;
+ Log.e(TAG, "onError " + error);
+ }
+
+ };
+
+ /**
+ * An additional thread for running tasks that shouldn't block the UI.
+ */
+ private HandlerThread mBackgroundThread;
+
+ /**
+ * A {@link Handler} for running tasks in the background.
+ */
+ private Handler mBackgroundHandler;
+
+ /**
+ * {@link CaptureRequest.Builder} for the camera preview
+ */
+ private CaptureRequest.Builder mPreviewRequestBuilder;
+
+ /**
+ * {@link CaptureRequest} generated by {@link #mPreviewRequestBuilder}
+ */
+ private CaptureRequest mPreviewRequest;
+
+ /**
+ * A {@link Semaphore} to prevent the app from exiting before closing the camera.
+ */
+ private Semaphore mCameraOpenCloseLock = new Semaphore(1);
+
+ /**
+ * A {@link CameraCaptureSession.CaptureCallback} that handles events related to JPEG capture.
+ */
+ /**
+ * A {@link CameraCaptureSession.CaptureCallback} that handles events related to JPEG capture.
+ */
+ private CameraCaptureSession.CaptureCallback mCaptureCallback
+ = new CameraCaptureSession.CaptureCallback() {
+
+ @Override
+ public void onCaptureProgressed(CameraCaptureSession session,
+ CaptureRequest request,
+ CaptureResult partialResult) {
+ // Don't need to do anything here.
+ }
+
+ @Override
+ public void onCaptureCompleted(CameraCaptureSession session,
+ CaptureRequest request,
+ TotalCaptureResult result) {
+ // Don't need to do anything here.
+ }
+
+ };
+
+ public CameraStreamManager(Context context, SurfaceTexture textureToStreamTo,
+ int width, int height) {
+ mContext = context;
+ mSurfaceTextureToStreamTo = textureToStreamTo;
+ mWidth = width;
+ mHeight = height;
+ }
+
+ public void onStartCameraStream() {
+ startBackgroundThread();
+ openCamera(mWidth, mHeight);
+ }
+
+ public void onStopCameraStream() {
+ closeCamera();
+ stopBackgroundThread();
+ }
+
+ /**
+ * Sets up member variables related to camera.
+ *
+ * @param width The width of available size for camera preview
+ * @param height The height of available size for camera preview
+ */
+ private void setUpCameraOutputs(int width, int height) {
+ // Danger, W.R.! Attempting to use too large a preview size could exceed the camera
+ // bus' bandwidth limitation, resulting in gorgeous previews but the storage of
+ // garbage capture data.
+ mPreviewSize = new Size(width, height);
+ }
+
+ /**
+ * Opens the camera specified by {@link CameraStreamManager#CAMERA_ID}.
+ */
+ private void openCamera(int width, int height) {
+ setUpCameraOutputs(width, height);
+ CameraManager manager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
+ try {
+ if (!mCameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) {
+ throw new RuntimeException("Time out waiting to lock camera opening.");
+ }
+ manager.openCamera(CAMERA_ID, mStateCallback, mBackgroundHandler);
+ } catch (CameraAccessException e) {
+ e.printStackTrace();
+ } catch (InterruptedException e) {
+ throw new RuntimeException("Interrupted while trying to lock camera opening.", e);
+ }
+ }
+
+ /**
+ * Closes the current {@link CameraDevice}.
+ */
+ private void closeCamera() {
+ try {
+ mCameraOpenCloseLock.acquire();
+ if (null != mCaptureSession) {
+ mCaptureSession.close();
+ mCaptureSession = null;
+ }
+ if (null != mCameraDevice) {
+ mCameraDevice.close();
+ mCameraDevice = null;
+ }
+ } catch (InterruptedException e) {
+ throw new RuntimeException("Interrupted while trying to lock camera closing.", e);
+ } finally {
+ mCameraOpenCloseLock.release();
+ }
+ }
+
+ /**
+ * Starts a background thread and its {@link Handler}.
+ */
+ private void startBackgroundThread() {
+ mBackgroundThread = new HandlerThread("CameraBackground");
+ mBackgroundThread.start();
+ mBackgroundHandler = new Handler(mBackgroundThread.getLooper());
+ }
+
+ /**
+ * Stops the background thread and its {@link Handler}.
+ */
+ private void stopBackgroundThread() {
+ if (mBackgroundThread != null) {
+ mBackgroundThread.quitSafely();
+ try {
+ mBackgroundThread.join();
+ mBackgroundThread = null;
+ mBackgroundHandler = null;
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ /**
+ * Creates a new {@link CameraCaptureSession} for camera preview.
+ */
+ private void createCameraPreviewSession() {
+ try {
+ assert mSurfaceTextureToStreamTo != null;
+
+ // We configure the size of default buffer to be the size of camera preview we want.
+ mSurfaceTextureToStreamTo.setDefaultBufferSize(mPreviewSize.getWidth(),
+ mPreviewSize.getHeight());
+
+ // This is the output Surface we need to start preview.
+ Surface surface = new Surface(mSurfaceTextureToStreamTo);
+
+ // We set up a CaptureRequest.Builder with the output Surface.
+ mPreviewRequestBuilder
+ = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+ mPreviewRequestBuilder.addTarget(surface);
+
+ // Here, we create a CameraCaptureSession for camera preview.
+ mCameraDevice.createCaptureSession(Arrays.asList(surface),
+ new CameraCaptureSession.StateCallback() {
+
+ @Override
+ public void onConfigured(CameraCaptureSession cameraCaptureSession) {
+ // The camera is already closed.
+ if (null == mCameraDevice) {
+ return;
+ }
+
+ // When the session is ready, we start displaying the preview.
+ mCaptureSession = cameraCaptureSession;
+ try {
+ // Auto focus should be continuous for camera preview.
+ mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE,
+ CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
+
+ // Finally, we start displaying the camera preview.
+ mPreviewRequest = mPreviewRequestBuilder.build();
+ mCaptureSession.setRepeatingRequest(mPreviewRequest,
+ mCaptureCallback, mBackgroundHandler);
+ } catch (CameraAccessException e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public void onConfigureFailed(
+ CameraCaptureSession cameraCaptureSession) {
+ Log.e(TAG, "Camera configuration failed.");
+ }
+ }, null
+ );
+ } catch (CameraAccessException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public static int getRotation(Context context, int deviceRotation) {
+ // Get offset from the RGB camera.
+ CameraManager manager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
+ CameraCharacteristics characteristics = null;
+ try {
+ characteristics = manager.getCameraCharacteristics(CAMERA_ID + "");
+ } catch (CameraAccessException e) {
+ e.printStackTrace();
+ }
+ int toOrientate = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
+
+ // Add RGB offset to current device rotation.
+ return toOrientate + deviceRotation;
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Renderer/RenderUtils/Colour.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Renderer/RenderUtils/Colour.java
new file mode 100644
index 0000000..201d697
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Renderer/RenderUtils/Colour.java
@@ -0,0 +1,37 @@
+/*
+ * 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 com.android.cts.verifier.sensors.sixdof.Renderer.RenderUtils;
+
+/**
+ * Contains static float arrays of colours. RGBA.
+ */
+public class Colour {
+ public static final float[] GREEN = new float[]{
+ 0.0f, 1.0f, 0.0f, 0.0f
+ };
+
+ public static final float[] RED = new float[]{
+ 1.0f, 0.0f, 0.0f, 0.0f
+ };
+
+ public static final float[] YELLOW = new float[]{
+ 1.0f, 1.0f, 0.0f, 0.0f
+ };
+
+ public static final float[] WHITE = new float[]{
+ 1.0f, 1.0f, 1.0f, 0.0f
+ };
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Renderer/RenderUtils/ConeModelMatrixCalculator.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Renderer/RenderUtils/ConeModelMatrixCalculator.java
new file mode 100644
index 0000000..578dc9c
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Renderer/RenderUtils/ConeModelMatrixCalculator.java
@@ -0,0 +1,80 @@
+/*
+ * 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 com.android.cts.verifier.sensors.sixdof.Renderer.RenderUtils;
+
+import com.android.cts.verifier.sensors.sixdof.Renderer.Renderable.ConeRenderable;
+import com.android.cts.verifier.sensors.sixdof.Utils.MathsUtils;
+
+import static com.android.cts.verifier.sensors.sixdof.Utils.MathsUtils.X;
+import static com.android.cts.verifier.sensors.sixdof.Utils.MathsUtils.Y;
+import static com.android.cts.verifier.sensors.sixdof.Utils.MathsUtils.Z;
+
+import android.opengl.Matrix;
+
+/**
+ * Manages the model matrix of the direction cone.
+ */
+public class ConeModelMatrixCalculator extends ModelMatrixCalculator {
+ float[] mUpVector;
+
+ public ConeModelMatrixCalculator(int toRotate, float[] upVector) {
+ super(toRotate);
+ mUpVector = upVector;
+ }
+
+ public void updateModelMatrix(float[] translation, float[] quaternion, float[] lookAtPosition) {
+ float[] convertedTranslation = MathsUtils.convertToOpenGlCoordinates(translation, mToRotate);
+ // Calculate the extrinsics based model matrix with current pose data.
+ float[] newModelMatrix = calculateModelMatrix(convertedTranslation, quaternion);
+
+ // Extract the information we need from calculated model matrix. (Just the translation).
+ float[] translationMatrix = new float[MathsUtils.MATRIX_4X4];
+ Matrix.setIdentityM(translationMatrix, 0);
+ Matrix.translateM(translationMatrix, 0, newModelMatrix[MATRIX_4X4_TRANSLATION_X],
+ newModelMatrix[MATRIX_4X4_TRANSLATION_Y], newModelMatrix[MATRIX_4X4_TRANSLATION_Z]);
+
+ float[] openGlRingPosition = MathsUtils.convertToOpenGlCoordinates(lookAtPosition, mToRotate);
+ float[] rotationTransformation = new float[MathsUtils.MATRIX_4X4];
+ // Calculate direction vector.
+ float[] relativeVector = new float[MathsUtils.VECTOR_3D];
+ for (int i = 0; i < relativeVector.length; i++) {
+ relativeVector[i] = openGlRingPosition[i] - convertedTranslation[i];
+ }
+ Matrix.setIdentityM(rotationTransformation, 0);
+ // Calculate look at rotation transformation.
+ // Has to be relative to the origin otherwise we get some warping of the cone.
+ MathsUtils.setLookAtM(rotationTransformation,
+ // Where we are.
+ 0.0f, 0.0f, 0.0f,
+ // What we want to look at.
+ relativeVector[X], relativeVector[Y], relativeVector[Z],
+ // Up direction.
+ mUpVector[X], mUpVector[Y], mUpVector[Z]);
+
+ // Apply translation to the look at matrix.
+ Matrix.multiplyMM(mModelMatrix, 0, translationMatrix, 0, rotationTransformation, 0);
+ }
+
+ /**
+ * Rotations that need to be done before rotating. Used for calculating the CONE_OFFSET.
+ *
+ * @return The offset that the cone needs to be at.
+ */
+ @Override
+ protected float[] getRequiredTranslations() {
+ return ConeRenderable.CONE_OFFSET;
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Renderer/RenderUtils/DrawParameters.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Renderer/RenderUtils/DrawParameters.java
new file mode 100644
index 0000000..8664c72
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Renderer/RenderUtils/DrawParameters.java
@@ -0,0 +1,49 @@
+/*
+ * 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 com.android.cts.verifier.sensors.sixdof.Renderer.RenderUtils;
+
+import com.android.cts.verifier.sensors.sixdof.Renderer.Renderable.Light;
+
+/**
+ * Parameters to be passed on a draw call
+ */
+public class DrawParameters {
+ private float[] mViewMatrix;
+ private float[] mProjectionMatrix;
+ private Light mLight;
+
+ public void update(float[] viewMatrix, float[] projectionMatrix) {
+ mViewMatrix = viewMatrix;
+ mProjectionMatrix = projectionMatrix;
+ }
+
+ public void update(float[] viewMatrix, float[] projectionMatrix, Light light) {
+ update(viewMatrix, projectionMatrix);
+ mLight = light;
+ }
+
+ public float[] getViewMatrix() {
+ return mViewMatrix;
+ }
+
+ public float[] getProjectionMatrix() {
+ return mProjectionMatrix;
+ }
+
+ public Light getLight() {
+ return mLight;
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Renderer/RenderUtils/ModelMatrixCalculator.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Renderer/RenderUtils/ModelMatrixCalculator.java
new file mode 100644
index 0000000..3713305
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Renderer/RenderUtils/ModelMatrixCalculator.java
@@ -0,0 +1,159 @@
+/*
+ * 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 com.android.cts.verifier.sensors.sixdof.Renderer.RenderUtils;
+
+import static com.android.cts.verifier.sensors.sixdof.Utils.MathsUtils.MATRIX_4X4;
+import static com.android.cts.verifier.sensors.sixdof.Utils.MathsUtils.X;
+import static com.android.cts.verifier.sensors.sixdof.Utils.MathsUtils.Y;
+import static com.android.cts.verifier.sensors.sixdof.Utils.MathsUtils.Z;
+
+import android.opengl.Matrix;
+
+import com.android.cts.verifier.sensors.sixdof.Utils.MathsUtils;
+
+
+/**
+ * Utility class to manage the calculation of a Model Matrix from the translation and quaternion
+ * arrays obtained from an PoseData object.
+ */
+public class ModelMatrixCalculator {
+
+ protected static final int MATRIX_4X4_TRANSLATION_X = 12;
+ protected static final int MATRIX_4X4_TRANSLATION_Y = 13;
+ protected static final int MATRIX_4X4_TRANSLATION_Z = 14;
+
+ public static final float[] TANGO_TO_OPENGL = new float[]{1.0f, 0.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, -1.0f, 0.0f,
+ 0.0f, 1.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, 0.0f, 1.0f};
+
+ protected float[] mModelMatrix = new float[MATRIX_4X4];
+
+ // Set these to identity matrix.
+ protected float[] mDevice2IMUMatrix = new float[]{1.0f, 0.0f, 0.0f, 0.0f,
+ 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
+ 1.0f};
+ protected float[] mColorCamera2IMUMatrix = new float[]{1.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f,
+ 0.0f, 1.0f};
+ protected float[] mOpengl2ColorCameraMatrix = new float[]{1.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f,
+ 0.0f, 1.0f};
+
+ protected int mToRotate = 0;
+
+ public ModelMatrixCalculator(int toRotate) {
+ Matrix.setIdentityM(mModelMatrix, 0);
+ mToRotate = toRotate;
+ }
+
+ /**
+ * Calculates a new model matrix, taking into account extrinsics and the latest pose
+ * data.
+ *
+ * @param translationInOpenGlCoordinates latest translation from pose data in OpenGl coordinate
+ * system.
+ * @param quaternion latest rotation from pose data.
+ * @return the new model matrix.
+ */
+ protected float[] calculateModelMatrix(float[] translationInOpenGlCoordinates,
+ float[] quaternion) {
+ float[] newModelMatrix = new float[MATRIX_4X4];
+
+ // Calculate an initial matrix with extrinsics taken into account.
+ float[] imu2OpenGlMatrix = new float[MATRIX_4X4];
+ Matrix.setIdentityM(imu2OpenGlMatrix, 0);
+ Matrix.multiplyMM(imu2OpenGlMatrix, 0, mColorCamera2IMUMatrix, 0,
+ mOpengl2ColorCameraMatrix, 0);
+ float[] invertedDevice2ImuMatrix = new float[MATRIX_4X4];
+ Matrix.setIdentityM(invertedDevice2ImuMatrix, 0);
+ Matrix.invertM(invertedDevice2ImuMatrix, 0, mDevice2IMUMatrix, 0);
+ float[] extrinsicsBasedMatrix = new float[MATRIX_4X4];
+ Matrix.setIdentityM(extrinsicsBasedMatrix, 0);
+ Matrix.multiplyMM(extrinsicsBasedMatrix, 0, invertedDevice2ImuMatrix, 0,
+ imu2OpenGlMatrix, 0);
+
+ // Do any translations that need to be done before rotating. Only used for the Cone offset.
+ float[] requiredTranslations = getRequiredTranslations();
+ Matrix.translateM(extrinsicsBasedMatrix, 0, requiredTranslations[X], requiredTranslations[Y],
+ requiredTranslations[Z]);
+
+ // Rotate based on rotation pose data.
+ float[] quaternionMatrix = new float[MATRIX_4X4];
+ Matrix.setIdentityM(quaternionMatrix, 0);
+ quaternionMatrix = MathsUtils.quaternionMatrixOpenGL(quaternion);
+ float[] rotatedMatrix = new float[MATRIX_4X4];
+ float[] deviceOrientationMatrix = new float[MATRIX_4X4];
+ Matrix.setIdentityM(rotatedMatrix, 0);
+ Matrix.setIdentityM(newModelMatrix, 0);
+ Matrix.setIdentityM(deviceOrientationMatrix, 0);
+ Matrix.multiplyMM(rotatedMatrix, 0, quaternionMatrix, 0,
+ extrinsicsBasedMatrix, 0);
+ Matrix.multiplyMM(deviceOrientationMatrix, 0, TANGO_TO_OPENGL, 0,
+ rotatedMatrix, 0);
+
+ Matrix.multiplyMM(newModelMatrix, 0, deviceOrientationMatrix, 0,
+ MathsUtils.getDeviceOrientationMatrix(mToRotate), 0);
+
+ // Finally, add the translations from the pose data.
+ newModelMatrix[MATRIX_4X4_TRANSLATION_X] += translationInOpenGlCoordinates[X];
+ newModelMatrix[MATRIX_4X4_TRANSLATION_Y] += translationInOpenGlCoordinates[Y];
+ newModelMatrix[MATRIX_4X4_TRANSLATION_Z] += translationInOpenGlCoordinates[Z];
+
+ return newModelMatrix;
+ }
+
+ /**
+ * Updates the model matrix (rotation and translation).
+ *
+ * @param translation a three-element array of translation data.
+ * @param quaternion a four-element array of rotation data.
+ */
+ public void updateModelMatrix(float[] translation, float[] quaternion) {
+ float[] convertedTranslation = MathsUtils.convertToOpenGlCoordinates(translation, mToRotate);
+ mModelMatrix = calculateModelMatrix(convertedTranslation, quaternion);
+ }
+
+ public void setDevice2IMUMatrix(float[] translation, float[] quaternion) {
+ mDevice2IMUMatrix = MathsUtils.quaternionMatrixOpenGL(quaternion);
+ mDevice2IMUMatrix[MATRIX_4X4_TRANSLATION_X] = translation[X];
+ mDevice2IMUMatrix[MATRIX_4X4_TRANSLATION_Y] = translation[Y];
+ mDevice2IMUMatrix[MATRIX_4X4_TRANSLATION_Z] = translation[Z];
+ }
+
+ public void setColorCamera2IMUMatrix(float[] translation, float[] quaternion) {
+ mOpengl2ColorCameraMatrix = new float[]{1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
+ -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
+ 1.0f};
+ mColorCamera2IMUMatrix = MathsUtils.quaternionMatrixOpenGL(quaternion);
+ mColorCamera2IMUMatrix[MATRIX_4X4_TRANSLATION_X] = translation[X];
+ mColorCamera2IMUMatrix[MATRIX_4X4_TRANSLATION_Y] = translation[Y];
+ mColorCamera2IMUMatrix[MATRIX_4X4_TRANSLATION_Z] = translation[Z];
+ }
+
+ public float[] getModelMatrix() {
+ return mModelMatrix;
+ }
+
+ /**
+ * Translations that need to be done before rotating. Used for calculating the CONE_OFFSET.
+ *
+ * @return no translation.
+ */
+ protected float[] getRequiredTranslations() {
+ return new float[]{0.0f, 0.0f, 0.0f};
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Renderer/RenderUtils/ObjImporter.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Renderer/RenderUtils/ObjImporter.java
new file mode 100644
index 0000000..e6f01be
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Renderer/RenderUtils/ObjImporter.java
@@ -0,0 +1,209 @@
+/*
+ * 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 com.android.cts.verifier.sensors.sixdof.Renderer.RenderUtils;
+
+import android.content.res.Resources;
+import android.util.Log;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.StringTokenizer;
+
+/**
+ * Imports an .obj file into an ObjectData class so that it can be rendered by OpenGl. Based on the
+ * .obj importer from Rajawali.
+ */
+public class ObjImporter {
+ protected static final String TAG = "ObjImporter";
+ protected static final String VERTEX = "v";
+ protected static final String FACE = "f";
+ protected static final String NORMAL = "vn";
+
+ protected static class ObjIndexData {
+
+ public ArrayList<Integer> vertexIndices;
+ public ArrayList<Integer> texCoordIndices;
+ public ArrayList<Integer> colorIndices;
+ public ArrayList<Integer> normalIndices;
+
+ public ObjIndexData() {
+ vertexIndices = new ArrayList<Integer>();
+ texCoordIndices = new ArrayList<Integer>();
+ colorIndices = new ArrayList<Integer>();
+ normalIndices = new ArrayList<Integer>();
+ }
+ }
+
+ public static class ObjectData {
+ float[] mVertexData;
+ float[] mNormalsData;
+ int[] mIndicesData;
+
+ protected ObjectData(float[] vertexData, float[] normalsData, int[] indicesData) {
+ mVertexData = vertexData;
+ mNormalsData = normalsData;
+ mIndicesData = indicesData;
+ }
+
+ public float[] getVertexData() {
+ return mVertexData;
+ }
+
+ public float[] getNormalsData() {
+ return mNormalsData;
+ }
+
+ public int[] getIndicesData() {
+ return mIndicesData;
+ }
+ }
+
+ public static ObjectData parse(Resources mResources, int mResourceId) {
+ BufferedReader buffer = null;
+ InputStream fileIn = mResources.openRawResource(mResourceId);
+ buffer = new BufferedReader(new InputStreamReader(fileIn));
+ String line;
+ ObjIndexData currObjIndexData = new ObjIndexData();
+
+ ArrayList<Float> vertices = new ArrayList<Float>();
+ ArrayList<Float> texCoords = new ArrayList<Float>();
+ ArrayList<Float> normals = new ArrayList<Float>();
+
+ try {
+ while ((line = buffer.readLine()) != null) {
+ // Skip comments and empty lines.
+ if (line.length() == 0 || line.charAt(0) == '#')
+ continue;
+ StringTokenizer parts = new StringTokenizer(line, " ");
+ int numTokens = parts.countTokens();
+
+ if (numTokens == 0)
+ continue;
+ String type = parts.nextToken();
+
+ if (type.equals(VERTEX)) {
+ vertices.add(Float.parseFloat(parts.nextToken()));
+ vertices.add(Float.parseFloat(parts.nextToken()));
+ vertices.add(Float.parseFloat(parts.nextToken()));
+ } else if (type.equals(FACE)) {
+ boolean isQuad = numTokens == 5;
+ int[] quadvids = new int[4];
+ int[] quadtids = new int[4];
+ int[] quadnids = new int[4];
+
+ boolean emptyVt = line.indexOf("//") > -1;
+ if (emptyVt) line = line.replace("//", "/");
+
+ parts = new StringTokenizer(line);
+
+ parts.nextToken();
+ StringTokenizer subParts = new StringTokenizer(parts.nextToken(), "/");
+ int partLength = subParts.countTokens();
+
+ boolean hasuv = partLength >= 2 && !emptyVt;
+ boolean hasn = partLength == 3 || (partLength == 2 && emptyVt);
+ int idx;
+
+ for (int i = 1; i < numTokens; i++) {
+ if (i > 1)
+ subParts = new StringTokenizer(parts.nextToken(), "/");
+ idx = Integer.parseInt(subParts.nextToken());
+
+ if (idx < 0) idx = (vertices.size() / 3) + idx;
+ else idx -= 1;
+ if (!isQuad)
+ currObjIndexData.vertexIndices.add(idx);
+ else
+ quadvids[i - 1] = idx;
+ if (hasuv) {
+ idx = Integer.parseInt(subParts.nextToken());
+ if (idx < 0) idx = (texCoords.size() / 2) + idx;
+ else idx -= 1;
+ if (!isQuad)
+ currObjIndexData.texCoordIndices.add(idx);
+ else
+ quadtids[i - 1] = idx;
+ }
+ if (hasn) {
+ idx = Integer.parseInt(subParts.nextToken());
+ if (idx < 0) idx = (normals.size() / 3) + idx;
+ else idx -= 1;
+ if (!isQuad)
+ currObjIndexData.normalIndices.add(idx);
+ else
+ quadnids[i - 1] = idx;
+ }
+ }
+
+ if (isQuad) {
+ int[] indices = new int[]{0, 1, 2, 0, 2, 3};
+
+ for (int i = 0; i < 6; ++i) {
+ int index = indices[i];
+ currObjIndexData.vertexIndices.add(quadvids[index]);
+ currObjIndexData.texCoordIndices.add(quadtids[index]);
+ currObjIndexData.normalIndices.add(quadnids[index]);
+ }
+ }
+ } else if (type.equals(NORMAL)) {
+ normals.add(Float.parseFloat(parts.nextToken()));
+ normals.add(Float.parseFloat(parts.nextToken()));
+ normals.add(Float.parseFloat(parts.nextToken()));
+ }
+ }
+
+ buffer.close();
+ } catch (IOException e) {
+ Log.e(TAG, "failed to parse", e);
+ }
+
+ int i;
+ float[] aVertices = new float[currObjIndexData.vertexIndices.size() * 3];
+ float[] aNormals = new float[currObjIndexData.normalIndices.size() * 3];
+ int[] aIndices = new int[currObjIndexData.vertexIndices.size()];
+
+ for (i = 0; i < currObjIndexData.vertexIndices.size(); ++i) {
+ int faceIndex = currObjIndexData.vertexIndices.get(i) * 3;
+ int vertexIndex = i * 3;
+ try {
+ aVertices[vertexIndex] = vertices.get(faceIndex);
+ aVertices[vertexIndex + 1] = vertices.get(faceIndex + 1);
+ aVertices[vertexIndex + 2] = vertices.get(faceIndex + 2);
+ aIndices[i] = i;
+ } catch (ArrayIndexOutOfBoundsException e) {
+ Log.d(TAG, "Obj array index out of bounds: " + vertexIndex + ", " + faceIndex);
+ }
+ }
+ for (i = 0; i < currObjIndexData.normalIndices.size(); ++i) {
+ int normalIndex = currObjIndexData.normalIndices.get(i) * 3;
+ int ni = i * 3;
+ if (normals.size() == 0) {
+ Log.e(TAG, "There are no normals specified for this model. " +
+ "Please re-export with normals.");
+ throw new RuntimeException("[" + TAG + "] There are no normals specified " +
+ "for this model. Please re-export with normals.");
+ }
+ aNormals[ni] = normals.get(normalIndex);
+ aNormals[ni + 1] = normals.get(normalIndex + 1);
+ aNormals[ni + 2] = normals.get(normalIndex + 2);
+ }
+
+ return new ObjectData(aVertices, aNormals, aIndices);
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Renderer/RenderUtils/ShaderHelper.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Renderer/RenderUtils/ShaderHelper.java
new file mode 100644
index 0000000..1188989
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Renderer/RenderUtils/ShaderHelper.java
@@ -0,0 +1,216 @@
+/*
+ * 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 com.android.cts.verifier.sensors.sixdof.Renderer.RenderUtils;
+
+import android.opengl.GLES20;
+import android.util.Log;
+
+/**
+ * Contains shader code and helper functions for compiling them.
+ */
+public class ShaderHelper {
+ private static final String TAG = "ShaderHelper";
+
+ public static String getCameraPreviewFragmentShader() {
+ return "#extension GL_OES_EGL_image_external : require\n" +
+ "precision mediump float;\n" +
+ "uniform samplerExternalOES u_Texture;\n" +
+ "\n" +
+ "varying vec3 v_Position;\n" +
+ "varying vec2 v_TexCoordinate;\n" +
+ "\n" +
+ "void main()\n" +
+ "{\n" +
+ " gl_FragColor = texture2D(u_Texture, v_TexCoordinate);\n" +
+ "}";
+ }
+
+ public static String getCameraPreviewVertexShader() {
+ return "uniform mat4 u_MVPMatrix;\n" +
+ "uniform mat4 u_MVMatrix;\n" +
+ "attribute vec4 a_Position;\n" +
+ "attribute vec2 a_TexCoordinate;\n" +
+ "\n" +
+ "varying vec3 v_Position;\n" +
+ "varying vec2 v_TexCoordinate;\n" +
+ "\n" +
+ "void main()\n" +
+ "{\n" +
+ " v_Position = vec3(u_MVMatrix * a_Position);\n" +
+ "\n" +
+ " v_TexCoordinate = a_TexCoordinate;\n" +
+ "\n" +
+ " gl_Position = u_MVPMatrix * a_Position;\n" +
+ "}";
+ }
+
+ public static String getRectangleFragmentShader() {
+ return "precision mediump float;" +
+ "varying vec4 v_Color;" +
+ "varying vec3 v_Position;" +
+ "void main() {" +
+ " gl_FragColor = v_Color;" +
+ "}";
+
+ }
+
+ public static String getRectangleVertexShader() {
+ return "uniform mat4 u_MVPMatrix;" +
+ "uniform mat4 u_MVMatrix;" +
+ "varying vec3 v_Position;" +
+ "varying vec4 v_Color;" +
+ "attribute vec4 a_Position;" +
+ "attribute vec4 a_Color;" +
+ "void main() {" +
+ " v_Position = vec3(u_MVMatrix * a_Position);" +
+ " v_Color = a_Color;" +
+ " gl_Position = u_MVPMatrix * a_Position;" +
+ "}";
+ }
+
+ /**
+ * Contains lighting information for shadows that enhance AR effect.
+ *
+ * @return the vertex shader.
+ */
+ public static String getAugmentedRealityVertexShader() {
+ return "uniform mat4 u_MVPMatrix;\n"
+ + "uniform mat4 u_MVMatrix;\n"
+
+ + "attribute vec4 a_Position;\n"
+ + "attribute vec4 a_Color;\n"
+ + "attribute vec3 a_Normal;\n"
+
+ + "varying vec3 v_Position;\n"
+ + "varying vec4 v_Color;\n"
+ + "varying vec3 v_Normal;\n"
+
+ + "void main()\n"
+ + "{\n"
+ + " v_Position = vec3(u_MVMatrix * a_Position);\n"
+ + " v_Color = a_Color;\n"
+ + " v_Normal = vec3(u_MVMatrix * vec4(a_Normal, 0.0));\n"
+ + " gl_Position = u_MVPMatrix * a_Position;\n"
+ + "}\n";
+ }
+
+ /**
+ * Contains lighting information for shadows that enhance AR effect.
+ *
+ * @return the fragment shader.
+ */
+ public static String getAugmentedRealityFragmentShader() {
+ return "precision mediump float;\n"
+ + "uniform vec3 u_LightPos;\n"
+ + "uniform float u_LightStrength;\n"
+ + "varying vec3 v_Position;\n"
+ + "varying vec4 v_Color;\n"
+ + "varying vec3 v_Normal;\n"
+
+ + "void main()\n"
+ + "{\n"
+ + " float distance = length(u_LightPos - v_Position);\n"
+ + " vec3 lightVector = normalize(u_LightPos - v_Position);\n"
+ + " float diffuse = max(dot(v_Normal, lightVector), 0.25);\n"
+ + " diffuse = diffuse * (u_LightStrength / (1.0 + (0.25 * distance * distance)));\n"
+ + " gl_FragColor = v_Color * diffuse;\n"
+ + "}";
+ }
+
+ /**
+ * Helper function to compile a shader.
+ *
+ * @param shaderType The shader type.
+ * @param shaderSource The shader source code.
+ * @return An OpenGL handle to the shader.
+ */
+ public static int compileShader(final int shaderType, final String shaderSource) {
+ int shaderHandle = GLES20.glCreateShader(shaderType);
+
+ if (shaderHandle != 0) {
+ // Pass in the shader source.
+ GLES20.glShaderSource(shaderHandle, shaderSource);
+
+ // Compile the shader.
+ GLES20.glCompileShader(shaderHandle);
+
+ // Get the compilation status.
+ final int[] compileStatus = new int[1];
+ GLES20.glGetShaderiv(shaderHandle, GLES20.GL_COMPILE_STATUS, compileStatus, 0);
+
+ // If the compilation failed, delete the shader.
+ if (compileStatus[0] == 0) {
+ Log.e(TAG, "Error compiling shader: " + GLES20.glGetShaderInfoLog(shaderHandle));
+ GLES20.glDeleteShader(shaderHandle);
+ shaderHandle = 0;
+ }
+ }
+
+ if (shaderHandle == 0) {
+ throw new RuntimeException("Error creating shader.");
+ }
+
+ return shaderHandle;
+ }
+
+ /**
+ * Helper function to compile and link a program.
+ *
+ * @param vertexShaderHandle An OpenGL handle to an already-compiled vertex shader.
+ * @param fragmentShaderHandle An OpenGL handle to an already-compiled fragment shader.
+ * @param attributes Attributes that need to be bound to the program.
+ * @return An OpenGL handle to the program.
+ */
+ public static int createAndLinkProgram(final int vertexShaderHandle, final int fragmentShaderHandle, final String[] attributes) {
+ int programHandle = GLES20.glCreateProgram();
+
+ if (programHandle != 0) {
+ // Bind the vertex shader to the program.
+ GLES20.glAttachShader(programHandle, vertexShaderHandle);
+
+ // Bind the fragment shader to the program.
+ GLES20.glAttachShader(programHandle, fragmentShaderHandle);
+
+ // Bind attributes
+ if (attributes != null) {
+ final int size = attributes.length;
+ for (int i = 0; i < size; i++) {
+ GLES20.glBindAttribLocation(programHandle, i, attributes[i]);
+ }
+ }
+
+ // Link the two shaders together into a program.
+ GLES20.glLinkProgram(programHandle);
+
+ // Get the link status.
+ final int[] linkStatus = new int[1];
+ GLES20.glGetProgramiv(programHandle, GLES20.GL_LINK_STATUS, linkStatus, 0);
+
+ // If the link failed, delete the program.
+ if (linkStatus[0] == 0) {
+ Log.e(TAG, "Error compiling program: " + GLES20.glGetProgramInfoLog(programHandle));
+ GLES20.glDeleteProgram(programHandle);
+ programHandle = 0;
+ }
+ }
+
+ if (programHandle == 0) {
+ throw new RuntimeException("Error creating program.");
+ }
+
+ return programHandle;
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Renderer/Renderable/CameraPreviewRenderable.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Renderer/Renderable/CameraPreviewRenderable.java
new file mode 100644
index 0000000..2e9b685
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Renderer/Renderable/CameraPreviewRenderable.java
@@ -0,0 +1,229 @@
+/*
+ * 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 com.android.cts.verifier.sensors.sixdof.Renderer.Renderable;
+
+import static com.android.cts.verifier.sensors.sixdof.Utils.MathsUtils.X;
+import static com.android.cts.verifier.sensors.sixdof.Utils.MathsUtils.Y;
+import static com.android.cts.verifier.sensors.sixdof.Utils.MathsUtils.Z;
+
+import android.content.Context;
+import android.graphics.SurfaceTexture;
+import android.opengl.GLES11Ext;
+import android.opengl.GLES20;
+import android.opengl.Matrix;
+import android.util.Log;
+
+import com.android.cts.verifier.sensors.sixdof.Renderer.RenderUtils.CameraStreamManager;
+import com.android.cts.verifier.sensors.sixdof.Renderer.RenderUtils.DrawParameters;
+import com.android.cts.verifier.sensors.sixdof.Renderer.RenderUtils.ShaderHelper;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.FloatBuffer;
+
+/**
+ * Shows the camera preview as an opengl texture.
+ */
+public class CameraPreviewRenderable extends Renderable {
+ private static final String TAG = "CameraPreviewRenderable";
+ private final int TEXTURE_COORDINATE_DATA_SIZE = 2;
+ private static final float[] CAMERA_TEXTURE_DATA = {
+ 0.0f, 0.0f,
+ 0.0f, 1.0f,
+ 1.0f, 0.0f,
+ 0.0f, 1.0f,
+ 1.0f, 1.0f,
+ 1.0f, 0.0f
+ };
+ private static final float[] CAMERA_PREVIEW_POSITION = {0.0f, 0.0f, -3.0f};
+
+ private FloatBuffer mPositionBuffer;
+ private FloatBuffer mTextureBuffer;
+
+ private int mTextureUniformHandle;
+ private int mTextureCoordinateHandle;
+
+ protected int mCameraTextureId = -1;
+
+ private SurfaceTexture mCameraSurfaceTexture;
+ private Context mContext;
+ private CameraStreamManager mCameraStreamManager;
+ private boolean mInvertAxis;
+
+ public CameraPreviewRenderable() {
+ // Reset the model matrix to the identity and move it so the OpenGL camera is looking at it.
+ Matrix.setIdentityM(getModelMatrix(), 0);
+ Matrix.translateM(getModelMatrix(), 0,
+ CAMERA_PREVIEW_POSITION[X], CAMERA_PREVIEW_POSITION[Y], CAMERA_PREVIEW_POSITION[Z]);
+ }
+
+ public void initialiseCameraPreview(float[] cameraPreviewPositionData, boolean invertAxis, Context context) {
+ // float count / floats per vertex.
+ mVertexCount = cameraPreviewPositionData.length / POSITION_DATA_SIZE;
+
+ // Initialize the buffers.
+ mPositionBuffer = ByteBuffer.allocateDirect(cameraPreviewPositionData.length * BYTES_PER_FLOAT)
+ .order(ByteOrder.nativeOrder()).asFloatBuffer();
+
+ mTextureBuffer = ByteBuffer.allocateDirect(CAMERA_TEXTURE_DATA.length * BYTES_PER_FLOAT)
+ .order(ByteOrder.nativeOrder()).asFloatBuffer();
+
+ mPositionBuffer.put(cameraPreviewPositionData).position(0);
+ mTextureBuffer.put(CAMERA_TEXTURE_DATA).position(0);
+
+ final String vertexShader = ShaderHelper.getCameraPreviewVertexShader();
+ final String fragmentShader = ShaderHelper.getCameraPreviewFragmentShader();
+
+ final int vertexShaderHandle =
+ ShaderHelper.compileShader(GLES20.GL_VERTEX_SHADER, vertexShader);
+ final int fragmentShaderHandle =
+ ShaderHelper.compileShader(GLES20.GL_FRAGMENT_SHADER, fragmentShader);
+
+ mProgramHandle = ShaderHelper.createAndLinkProgram(vertexShaderHandle, fragmentShaderHandle,
+ new String[]{"a_Position", "a_TexCoordinate"});
+
+ mContext = context;
+ mInvertAxis = invertAxis;
+ connectCamera();
+ }
+
+ @Override
+ public void draw(DrawParameters drawParameters) {
+ GLES20.glUseProgram(mProgramHandle);
+
+ // Set program handles for camera preview drawing.
+ mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgramHandle, "u_MVPMatrix");
+ mMVMatrixHandle = GLES20.glGetUniformLocation(mProgramHandle, "u_MVMatrix");
+ mTextureUniformHandle = GLES20.glGetUniformLocation(mProgramHandle, "u_Texture");
+ mPositionHandle = GLES20.glGetAttribLocation(mProgramHandle, "a_Position");
+ mTextureCoordinateHandle = GLES20.glGetAttribLocation(mProgramHandle, "a_TexCoordinate");
+
+ // Set the active texture unit to texture unit 0.
+ GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
+
+ // Bind the texture to this unit.
+ if (mCameraTextureId != -1) {
+ GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, mCameraTextureId);
+ }
+
+ // Tell the texture uniform sampler to use this texture in the shader by binding to texture unit 0.
+ GLES20.glUniform1i(mTextureUniformHandle, 0);
+
+ // Compose the model, view, and projection matrices into a single m-v-p matrix
+ updateMvpMatrix(drawParameters.getViewMatrix(), drawParameters.getProjectionMatrix());
+
+ drawCameraPreview();
+ }
+
+ /**
+ * Draws a camera preview.
+ */
+ private void drawCameraPreview() {
+ // Pass in the position information
+ mPositionBuffer.position(0);
+ GLES20.glVertexAttribPointer(mPositionHandle, POSITION_DATA_SIZE, GLES20.GL_FLOAT, false,
+ 0, mPositionBuffer);
+
+ GLES20.glEnableVertexAttribArray(mPositionHandle);
+
+ // Pass in the texture coordinate information
+ mTextureBuffer.position(0);
+ GLES20.glVertexAttribPointer(mTextureCoordinateHandle, TEXTURE_COORDINATE_DATA_SIZE, GLES20.GL_FLOAT, false,
+ 0, mTextureBuffer);
+
+ GLES20.glEnableVertexAttribArray(mTextureCoordinateHandle);
+
+ // Pass in the modelview matrix.
+ GLES20.glUniformMatrix4fv(mMVMatrixHandle, 1, false, getMvMatrix(), 0);
+
+ // Pass in the combined matrix.
+ GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, getMvpMatrix(), 0);
+
+ // Draw the camera preview.
+ GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, mVertexCount);
+ }
+
+ /**
+ * Updates the texture with the latest camera data.
+ *
+ * @return the timestamp of the RGB image rendered into the texture.
+ */
+ public synchronized double updateTexture() {
+ double latestCameraFrameTimestamp = -1.0;
+ if (mCameraTextureId != -1) {
+ // Copy the camera frame from the camera to the OpenGL texture
+ mCameraSurfaceTexture.updateTexImage();
+ latestCameraFrameTimestamp = mCameraSurfaceTexture.getTimestamp();
+ }
+ return latestCameraFrameTimestamp;
+ }
+
+ /**
+ * Connects the camera to the OpenGl context
+ */
+ public void connectCamera() {
+ this.mCameraTextureId = connectCameraTexture();
+ }
+
+ public void disconnectCamera() {
+ mCameraStreamManager.onStopCameraStream();
+ }
+
+ /**
+ * Connects a texture to an Android camera
+ *
+ * @return textureId of texture with camera attached/
+ */
+ private int connectCameraTexture() {
+ if (mCameraTextureId == -1) {
+ mCameraTextureId = createEmptyTexture();
+ mCameraSurfaceTexture = new SurfaceTexture(mCameraTextureId);
+ int width = mInvertAxis ? 1080 : 1920;
+ int height = mInvertAxis ? 1920 : 1080;
+ mCameraStreamManager = new CameraStreamManager(mContext, mCameraSurfaceTexture, width, height);
+ mCameraStreamManager.onStartCameraStream();
+ }
+ return mCameraTextureId;
+ }
+
+ /**
+ * Creates an empty texture.
+ *
+ * @return textureId of empty texture.
+ */
+ public static int createEmptyTexture() {
+ final int[] textureHandle = new int[1];
+
+ GLES20.glGenTextures(1, textureHandle, 0);
+
+ if (textureHandle[0] != 0) {
+ return textureHandle[0];
+ }
+
+ return -1;
+ }
+
+ @Override
+ public void destroy() {
+ if (mCameraStreamManager != null) {
+ mCameraStreamManager.onStopCameraStream();
+ mCameraStreamManager = null;
+ }
+
+ mPositionBuffer = null;
+ mTextureBuffer = null;
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Renderer/Renderable/ConeRenderable.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Renderer/Renderable/ConeRenderable.java
new file mode 100644
index 0000000..e0dcb6a
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Renderer/Renderable/ConeRenderable.java
@@ -0,0 +1,179 @@
+/*
+ * 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 com.android.cts.verifier.sensors.sixdof.Renderer.Renderable;
+
+import static com.android.cts.verifier.sensors.sixdof.Utils.MathsUtils.X;
+import static com.android.cts.verifier.sensors.sixdof.Utils.MathsUtils.Y;
+import static com.android.cts.verifier.sensors.sixdof.Utils.MathsUtils.Z;
+
+import android.opengl.GLES20;
+
+import com.android.cts.verifier.sensors.sixdof.Renderer.RenderUtils.Colour;
+import com.android.cts.verifier.sensors.sixdof.Renderer.RenderUtils.ConeModelMatrixCalculator;
+import com.android.cts.verifier.sensors.sixdof.Renderer.RenderUtils.DrawParameters;
+import com.android.cts.verifier.sensors.sixdof.Renderer.RenderUtils.ObjImporter;
+import com.android.cts.verifier.sensors.sixdof.Renderer.RenderUtils.ShaderHelper;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.FloatBuffer;
+import java.nio.IntBuffer;
+
+/**
+ * Object that needs to be collected by user in last test.
+ */
+public class ConeRenderable extends Renderable {
+ private static final int BYTES_PER_INT = 4;
+ public static final float[] CONE_OFFSET = new float[]{
+ 0.2f, -0.1f, -1.0f}; // Offset from camera position.
+
+ private FloatBuffer mPositionBuffer;
+ private IntBuffer mIndicesBuffer;
+ private FloatBuffer mNormalsBuffer;
+ private FloatBuffer mColorBuffer;
+ private int mColorHandle;
+ private int mLightPosHandle;
+ private int mLightStrengthHandle;
+ private int mNormalHandle;
+
+ private ConeModelMatrixCalculator mModelMatrixCalculator;
+
+ public ConeRenderable(int toRotate, float[] upVector) {
+ mModelMatrixCalculator = new ConeModelMatrixCalculator(toRotate, upVector);
+ }
+
+ public void initialise(ObjImporter.ObjectData coneData) {
+ mVertexCount = coneData.getIndicesData().length;
+
+ int colourCount = mVertexCount * COLOUR_DATA_SIZE; // Vertex count * rgba
+ float[] colours = new float[colourCount];
+
+ for (int i = 0; i < colourCount; i++) {
+ int index = i % COLOUR_DATA_SIZE;
+ colours[i] = Colour.WHITE[index];
+ }
+
+ // Initialize the buffers.
+ mPositionBuffer = ByteBuffer.allocateDirect(coneData.getVertexData().length * BYTES_PER_FLOAT)
+ .order(ByteOrder.nativeOrder()).asFloatBuffer();
+ mColorBuffer = ByteBuffer.allocateDirect(colours.length * BYTES_PER_FLOAT)
+ .order(ByteOrder.nativeOrder()).asFloatBuffer();
+ mNormalsBuffer = ByteBuffer.allocateDirect(coneData.getNormalsData().length * BYTES_PER_FLOAT)
+ .order(ByteOrder.nativeOrder()).asFloatBuffer();
+ mIndicesBuffer = ByteBuffer.allocateDirect(coneData.getIndicesData().length * BYTES_PER_INT)
+ .order(ByteOrder.nativeOrder()).asIntBuffer();
+
+ mPositionBuffer.put(coneData.getVertexData()).position(0);
+ mColorBuffer.put(colours).position(0);
+ mNormalsBuffer.put(coneData.getNormalsData()).position(0);
+ mIndicesBuffer.put(coneData.getIndicesData()).position(0);
+
+ final String vertexShader = ShaderHelper.getAugmentedRealityVertexShader();
+ final String fragmentShader = ShaderHelper.getAugmentedRealityFragmentShader();
+
+ final int vertexShaderHandle =
+ ShaderHelper.compileShader(GLES20.GL_VERTEX_SHADER, vertexShader);
+ final int fragmentShaderHandle =
+ ShaderHelper.compileShader(GLES20.GL_FRAGMENT_SHADER, fragmentShader);
+
+ mProgramHandle = ShaderHelper.createAndLinkProgram(vertexShaderHandle, fragmentShaderHandle,
+ new String[]{"a_Position", "a_Color", "a_Normal"});
+ }
+
+ @Override
+ public float[] getModelMatrix() {
+ // We want a model matrix that has camera extrinsics taken into account.
+ return mModelMatrixCalculator.getModelMatrix();
+ }
+
+ public void updateModelMatrix(float[] translation, float[] rotation, float[] lookAtPosition) {
+ mModelMatrixCalculator.updateModelMatrix(translation, rotation, lookAtPosition);
+ }
+
+ public void setDevice2IMUMatrix(float[] translation, float[] quaternion) {
+ mModelMatrixCalculator.setDevice2IMUMatrix(translation, quaternion);
+ }
+
+ public void setColorCamera2IMUMatrix(float[] translation, float[] quaternion) {
+ mModelMatrixCalculator.setColorCamera2IMUMatrix(translation, quaternion);
+ }
+
+ @Override
+ public void draw(DrawParameters drawParameters) {
+ GLES20.glUseProgram(mProgramHandle);
+
+ // Set program handles for cone drawing.
+ mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgramHandle, "u_MVPMatrix");
+ mMVMatrixHandle = GLES20.glGetUniformLocation(mProgramHandle, "u_MVMatrix");
+ mPositionHandle = GLES20.glGetAttribLocation(mProgramHandle, "a_Position");
+ mColorHandle = GLES20.glGetAttribLocation(mProgramHandle, "a_Color");
+
+ // Used to calculate shadows.
+ mLightPosHandle = GLES20.glGetUniformLocation(mProgramHandle, "u_LightPos");
+ mLightStrengthHandle = GLES20.glGetUniformLocation(mProgramHandle, "u_LightStrength");
+ mNormalHandle = GLES20.glGetAttribLocation(mProgramHandle, "a_Normal");
+
+ // Calculate lighting information
+ float[] lightPosInEyeSpace = drawParameters.getLight()
+ .getPositionInEyeSpace(drawParameters.getViewMatrix());
+ GLES20.glUniform3f(mLightPosHandle,
+ lightPosInEyeSpace[X], lightPosInEyeSpace[Y], lightPosInEyeSpace[Z]);
+ GLES20.glUniform1f(mLightStrengthHandle, drawParameters.getLight().getStrength());
+
+ updateMvpMatrix(drawParameters.getViewMatrix(), drawParameters.getProjectionMatrix());
+ drawCone();
+ }
+
+ private void drawCone() {
+ // Pass in the position information
+ mPositionBuffer.position(0);
+ GLES20.glVertexAttribPointer(mPositionHandle, POSITION_DATA_SIZE, GLES20.GL_FLOAT, false,
+ 0, mPositionBuffer);
+
+ GLES20.glEnableVertexAttribArray(mPositionHandle);
+
+ // Pass in the modelview matrix.
+ GLES20.glUniformMatrix4fv(mMVMatrixHandle, 1, false, getMvMatrix(), 0);
+
+ // Pass in the combined matrix.
+ GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, getMvpMatrix(), 0);
+
+ // Pass in the color information
+ mColorBuffer.position(0);
+ GLES20.glVertexAttribPointer(mColorHandle, COLOUR_DATA_SIZE, GLES20.GL_FLOAT, false,
+ 0, mColorBuffer);
+
+ GLES20.glEnableVertexAttribArray(mColorHandle);
+
+ // Pass in the normal information
+ mNormalsBuffer.position(0);
+ GLES20.glVertexAttribPointer(mNormalHandle, NORMAL_DATA_SIZE, GLES20.GL_FLOAT, false,
+ 0, mNormalsBuffer);
+
+ GLES20.glEnableVertexAttribArray(mNormalHandle);
+
+ // Draw the cone.
+ GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, mVertexCount);
+ }
+
+ @Override
+ public void destroy() {
+ mPositionBuffer = null;
+ mIndicesBuffer = null;
+ mNormalsBuffer = null;
+ mColorBuffer = null;
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Renderer/Renderable/Light.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Renderer/Renderable/Light.java
new file mode 100644
index 0000000..43b1001
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Renderer/Renderable/Light.java
@@ -0,0 +1,86 @@
+/*
+ * 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 com.android.cts.verifier.sensors.sixdof.Renderer.Renderable;
+
+import static com.android.cts.verifier.sensors.sixdof.Utils.MathsUtils.X;
+import static com.android.cts.verifier.sensors.sixdof.Utils.MathsUtils.Y;
+import static com.android.cts.verifier.sensors.sixdof.Utils.MathsUtils.Z;
+
+import android.opengl.Matrix;
+
+import com.android.cts.verifier.sensors.sixdof.Renderer.RenderUtils.DrawParameters;
+
+/**
+ * Light object for applying shadows. Not actually rendered, but we make use of matrices in
+ * Renderable class.
+ */
+public class Light extends Renderable {
+ private static final float DEFAULT_LIGHT_STRENGTH = 1.0f;
+
+ private float mStrength = DEFAULT_LIGHT_STRENGTH;
+
+ /**
+ * Used to hold the transformed position of the light in eye space (after transformation via
+ * modelview matrix)
+ */
+ private final float[] mLightPosInEyeSpace = new float[4];
+
+ /**
+ * Creates a light at the given position.
+ *
+ * @param position coordinates in open gl coordinate system.
+ */
+ public Light(float[] position) {
+ new Light(position, DEFAULT_LIGHT_STRENGTH);
+ }
+
+ public float getStrength() {
+ return mStrength;
+ }
+
+ /**
+ * Creates a light at the given position with a given strength.
+ *
+ * @param position coordinates in open gl coordinate system.
+ * @param strength strength of light.
+ */
+ public Light(float[] position, float strength) {
+ mStrength = strength;
+
+ Matrix.setIdentityM(getModelMatrix(), 0);
+ Matrix.translateM(getModelMatrix(), 0, position[X], position[Y], position[Z]);
+ }
+
+ @Override
+ public void draw(DrawParameters drawParameters) {
+ // Don't actually need to draw anything here.
+ }
+
+ public synchronized float[] getPositionInEyeSpace(float[] viewMatrix) {
+ Matrix.multiplyMV(mLightPosInEyeSpace, 0, viewMatrix, 0, getModelMatrix(), 0);
+ return mLightPosInEyeSpace;
+ }
+
+ public synchronized void updateLightPosition(float[] translation) {
+ Matrix.setIdentityM(getModelMatrix(), 0);
+ Matrix.translateM(getModelMatrix(), 0, translation[X], translation[Y], translation[Z]);
+ }
+
+ @Override
+ public void destroy() {
+ // Nothing to destroy.
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Renderer/Renderable/RectangleRenderable.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Renderer/Renderable/RectangleRenderable.java
new file mode 100644
index 0000000..4d59d2d
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Renderer/Renderable/RectangleRenderable.java
@@ -0,0 +1,150 @@
+/*
+ * 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 com.android.cts.verifier.sensors.sixdof.Renderer.Renderable;
+
+import static com.android.cts.verifier.sensors.sixdof.Utils.MathsUtils.X;
+import static com.android.cts.verifier.sensors.sixdof.Utils.MathsUtils.Y;
+import static com.android.cts.verifier.sensors.sixdof.Utils.MathsUtils.Z;
+
+import android.opengl.GLES20;
+import android.opengl.Matrix;
+
+import com.android.cts.verifier.sensors.sixdof.Renderer.RenderUtils.Colour;
+import com.android.cts.verifier.sensors.sixdof.Renderer.RenderUtils.DrawParameters;
+import com.android.cts.verifier.sensors.sixdof.Renderer.RenderUtils.ShaderHelper;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.FloatBuffer;
+
+/**
+ * Rotating Rectangle for the robustness test.
+ */
+public class RectangleRenderable extends Renderable {
+ private static final int LINE_WIDTH = 8;
+ protected static final float[] RECTANGLE_POSITION = {0.0f, 0.0f, -2.99f};
+
+ private FloatBuffer mPositionBuffer;
+ private FloatBuffer mColorBuffer;
+ private int mColorHandle;
+
+ private float[] mRectanglePositionData;
+
+ public RectangleRenderable() {
+ // Reset the model matrix to the identity and move it infront of the camera preview
+ Matrix.setIdentityM(getModelMatrix(), 0);
+ Matrix.translateM(getModelMatrix(), 0,
+ RECTANGLE_POSITION[X], RECTANGLE_POSITION[Y], RECTANGLE_POSITION[Z]);
+ }
+
+ public void initialiseRectangle(float[] rectanglePositionData) {
+ mRectanglePositionData = rectanglePositionData;
+
+ // Initialize the buffers.
+ mPositionBuffer = ByteBuffer.allocateDirect(mRectanglePositionData.length * BYTES_PER_FLOAT)
+ .order(ByteOrder.nativeOrder()).asFloatBuffer();
+ mPositionBuffer.put(mRectanglePositionData).position(0);
+
+ // float count / floats per vertex
+ mVertexCount = mRectanglePositionData.length / POSITION_DATA_SIZE;
+ int colourCount = mVertexCount * COLOUR_DATA_SIZE; // Vertex count * rgba
+ float[] colours = new float[colourCount];
+
+ for (int i = 0; i < colourCount; i++) {
+ int index = i % COLOUR_DATA_SIZE;
+ colours[i] = Colour.GREEN[index];
+ }
+
+ mColorBuffer = ByteBuffer.allocateDirect(colours.length * BYTES_PER_FLOAT)
+ .order(ByteOrder.nativeOrder()).asFloatBuffer();
+ mColorBuffer.put(colours).position(0);
+
+ final String vertexShader = ShaderHelper.getRectangleVertexShader();
+ final String fragmentShader = ShaderHelper.getRectangleFragmentShader();
+
+ final int vertexShaderHandle =
+ ShaderHelper.compileShader(GLES20.GL_VERTEX_SHADER, vertexShader);
+ final int fragmentShaderHandle =
+ ShaderHelper.compileShader(GLES20.GL_FRAGMENT_SHADER, fragmentShader);
+
+ mProgramHandle = ShaderHelper.createAndLinkProgram(vertexShaderHandle, fragmentShaderHandle,
+ new String[]{"a_Position", "a_Color"});
+ }
+
+ @Override
+ public void draw(DrawParameters drawParameters) {
+ GLES20.glUseProgram(mProgramHandle);
+
+ // Set program handles for camera preview drawing.
+ mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgramHandle, "u_MVPMatrix");
+ mMVMatrixHandle = GLES20.glGetUniformLocation(mProgramHandle, "u_MVMatrix");
+ mPositionHandle = GLES20.glGetAttribLocation(mProgramHandle, "a_Position");
+ mColorHandle = GLES20.glGetAttribLocation(mProgramHandle, "a_Color");
+
+ updateMvpMatrix(drawParameters.getViewMatrix(), drawParameters.getProjectionMatrix());
+ drawRectangle();
+ }
+
+ private void drawRectangle() {
+ // Pass in the position information
+ mPositionBuffer.position(0);
+ GLES20.glVertexAttribPointer(mPositionHandle, POSITION_DATA_SIZE, GLES20.GL_FLOAT, false,
+ 0, mPositionBuffer);
+
+ GLES20.glEnableVertexAttribArray(mPositionHandle);
+
+ // Pass in the modelview matrix.
+ GLES20.glUniformMatrix4fv(mMVMatrixHandle, 1, false, getMvMatrix(), 0);
+
+ // Pass in the combined matrix.
+ GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, getMvpMatrix(), 0);
+
+ synchronized (this) {
+ // Pass in the color information
+ mColorBuffer.position(0);
+ GLES20.glVertexAttribPointer(mColorHandle, COLOUR_DATA_SIZE, GLES20.GL_FLOAT, false,
+ 0, mColorBuffer);
+
+ GLES20.glEnableVertexAttribArray(mColorHandle);
+ }
+
+ // Draw the rectangle.
+ GLES20.glLineWidth(LINE_WIDTH);
+ GLES20.glDrawArrays(GLES20.GL_LINES, 0, mVertexCount); // 2 points per line * 4 lines = 8
+ }
+
+ public void setLineColor(float[] newColor) {
+ synchronized (this) {
+ // float count / floats per vertex
+ int vertexCount = mRectanglePositionData.length / POSITION_DATA_SIZE;
+ int colourCount = vertexCount * COLOUR_DATA_SIZE; // Vertex count * rgba
+ float[] colours = new float[colourCount];
+
+ for (int i = 0; i < colourCount; i++) {
+ int index = i % COLOUR_DATA_SIZE;
+ colours[i] = newColor[index];
+ }
+
+ mColorBuffer.put(colours).position(0);
+ }
+ }
+
+ @Override
+ public void destroy() {
+ mPositionBuffer = null;
+ mColorBuffer = null;
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Renderer/Renderable/Renderable.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Renderer/Renderable/Renderable.java
new file mode 100644
index 0000000..ad2de00
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Renderer/Renderable/Renderable.java
@@ -0,0 +1,93 @@
+/*
+ * 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 com.android.cts.verifier.sensors.sixdof.Renderer.Renderable;
+
+import com.android.cts.verifier.sensors.sixdof.Renderer.RenderUtils.DrawParameters;
+import com.android.cts.verifier.sensors.sixdof.Utils.MathsUtils;
+
+import static com.android.cts.verifier.sensors.sixdof.Utils.MathsUtils.X;
+import static com.android.cts.verifier.sensors.sixdof.Utils.MathsUtils.Y;
+import static com.android.cts.verifier.sensors.sixdof.Utils.MathsUtils.Z;
+
+import android.opengl.Matrix;
+
+/**
+ * Base class for all Renderables
+ */
+public abstract class Renderable {
+ protected static final int BYTES_PER_FLOAT = 4;
+ protected static final int POSITION_DATA_SIZE = 3;
+ protected static final int COLOUR_DATA_SIZE = 4;
+ protected static final int NORMAL_DATA_SIZE = 3;
+
+ protected int mVertexCount;
+
+ protected int mProgramHandle;
+ protected int mPositionHandle;
+ protected int mMVPMatrixHandle;
+ protected int mMVMatrixHandle;
+
+ private float[] mModelMatrix = new float[MathsUtils.MATRIX_4X4];
+ private float[] mMvMatrix = new float[MathsUtils.MATRIX_4X4];
+ private float[] mMvpMatrix = new float[MathsUtils.MATRIX_4X4];
+
+ /**
+ * Applies the view and projection matrices and draws the Renderable.
+ *
+ * @param drawParameters parameters needed for drawing objects.
+ */
+ public abstract void draw(DrawParameters drawParameters);
+
+ public synchronized void updateMvpMatrix(float[] viewMatrix,
+ float[] projectionMatrix) {
+ // Compose the model, view, and projection matrices into a single mvp
+ // matrix
+ Matrix.setIdentityM(mMvMatrix, 0);
+ Matrix.setIdentityM(mMvpMatrix, 0);
+ Matrix.multiplyMM(mMvMatrix, 0, viewMatrix, 0, getModelMatrix(), 0);
+ Matrix.multiplyMM(mMvpMatrix, 0, projectionMatrix, 0, mMvMatrix, 0);
+ }
+
+ public float[] getModelMatrix() {
+ return mModelMatrix;
+ }
+
+ public void setModelMatrix(float[] modelMatrix) {
+ mModelMatrix = modelMatrix;
+ }
+
+ public float[] getMvMatrix() {
+ return mMvMatrix;
+ }
+
+ public float[] getMvpMatrix() {
+ return mMvpMatrix;
+ }
+
+ public synchronized void setRotationAngle(float newAngle) {
+ // Rotate around the Z axis. (only used in robustness test).
+ float[] translations = new float[]
+ {getModelMatrix()[12], getModelMatrix()[13],getModelMatrix()[14]};
+ synchronized (this) {
+ Matrix.setIdentityM(getModelMatrix(), 0);
+ Matrix.rotateM(getModelMatrix(), 0, newAngle, 0.0f, 0.0f, 1.0f);
+ Matrix.translateM(getModelMatrix(), 0,
+ translations[X], translations[Y], translations[Z]);
+ }
+ }
+
+ public abstract void destroy();
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Renderer/Renderable/RingRenderable.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Renderer/Renderable/RingRenderable.java
new file mode 100644
index 0000000..6750343
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Renderer/Renderable/RingRenderable.java
@@ -0,0 +1,187 @@
+/*
+ * 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 com.android.cts.verifier.sensors.sixdof.Renderer.Renderable;
+
+import com.android.cts.verifier.sensors.sixdof.Renderer.RenderUtils.Colour;
+import com.android.cts.verifier.sensors.sixdof.Renderer.RenderUtils.DrawParameters;
+import com.android.cts.verifier.sensors.sixdof.Renderer.RenderUtils.ObjImporter;
+import com.android.cts.verifier.sensors.sixdof.Renderer.RenderUtils.ShaderHelper;
+import com.android.cts.verifier.sensors.sixdof.Utils.MathsUtils;
+
+import static com.android.cts.verifier.sensors.sixdof.Utils.MathsUtils.X;
+import static com.android.cts.verifier.sensors.sixdof.Utils.MathsUtils.Y;
+import static com.android.cts.verifier.sensors.sixdof.Utils.MathsUtils.Z;
+
+import android.opengl.GLES20;
+import android.opengl.Matrix;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.FloatBuffer;
+import java.nio.IntBuffer;
+
+/**
+ * Object that needs to be collected by user in last test.
+ */
+public class RingRenderable extends Renderable {
+ private static final int BYTES_PER_INT = 4;
+
+ private FloatBuffer mPositionBuffer;
+ private IntBuffer mIndicesBuffer;
+ private FloatBuffer mNormalsBuffer;
+ private FloatBuffer mColorBuffer;
+ private int mColorHandle;
+ private int mLightPosHandle;
+ private int mLightStrengthHandle;
+ private int mNormalHandle;
+
+ public RingRenderable(float[] position, float[] rotation, float[] upVector) {
+ // Reset the model matrix to the identity.
+ Matrix.setIdentityM(getModelMatrix(), 0);
+
+ float[] overallTransformation = new float[MathsUtils.MATRIX_4X4];
+ Matrix.setIdentityM(overallTransformation, 0);
+
+ // Rotation
+ float[] rotationTransformation = new float[MathsUtils.MATRIX_4X4];
+ Matrix.setIdentityM(rotationTransformation, 0);
+ // The rotation given is relative to the position of the ring, so we have to calculate the
+ // rotation as if we where at the origin.
+ MathsUtils.setLookAtM(rotationTransformation,
+ 0.0f, 0.0f, 0.0f,
+ rotation[X], rotation[Y], rotation[Z],
+ upVector[X], upVector[Y], upVector[Z]);
+
+ Matrix.multiplyMM(overallTransformation, 0, rotationTransformation, 0, overallTransformation, 0);
+
+ // Translation
+ float[] translationTransformation = new float[MathsUtils.MATRIX_4X4];
+ Matrix.setIdentityM(translationTransformation, 0);
+ Matrix.translateM(translationTransformation, 0, position[X], position[Y], position[Z]);
+
+ // Apply translation to rotation.
+ Matrix.multiplyMM(overallTransformation, 0, translationTransformation, 0, overallTransformation, 0);
+ // Apply transformation to model matrix.
+ Matrix.multiplyMM(getModelMatrix(), 0, overallTransformation, 0, getModelMatrix(), 0);
+ }
+
+ /**
+ * Initialise the ring with data from the .obj file.
+ */
+ public void initialise(ObjImporter.ObjectData ringData) {
+ mVertexCount = ringData.getIndicesData().length;
+
+ int colourCount = mVertexCount * COLOUR_DATA_SIZE; // Vertex count * rgba
+ float[] colours = new float[colourCount];
+
+ for (int i = 0; i < colourCount; i++) {
+ int index = i % COLOUR_DATA_SIZE;
+ colours[i] = Colour.YELLOW[index];
+ }
+
+ // Initialize the buffers.
+ mPositionBuffer = ByteBuffer.allocateDirect(ringData.getVertexData().length * BYTES_PER_FLOAT)
+ .order(ByteOrder.nativeOrder()).asFloatBuffer();
+ mColorBuffer = ByteBuffer.allocateDirect(colours.length * BYTES_PER_FLOAT)
+ .order(ByteOrder.nativeOrder()).asFloatBuffer();
+ mNormalsBuffer = ByteBuffer.allocateDirect(ringData.getNormalsData().length * BYTES_PER_FLOAT)
+ .order(ByteOrder.nativeOrder()).asFloatBuffer();
+ mIndicesBuffer = ByteBuffer.allocateDirect(ringData.getIndicesData().length * BYTES_PER_INT)
+ .order(ByteOrder.nativeOrder()).asIntBuffer();
+
+ mPositionBuffer.put(ringData.getVertexData()).position(0);
+ mColorBuffer.put(colours).position(0);
+ mNormalsBuffer.put(ringData.getNormalsData()).position(0);
+ mIndicesBuffer.put(ringData.getIndicesData()).position(0);
+
+ final String vertexShader = ShaderHelper.getAugmentedRealityVertexShader();
+ final String fragmentShader = ShaderHelper.getAugmentedRealityFragmentShader();
+
+ final int vertexShaderHandle =
+ ShaderHelper.compileShader(GLES20.GL_VERTEX_SHADER, vertexShader);
+ final int fragmentShaderHandle =
+ ShaderHelper.compileShader(GLES20.GL_FRAGMENT_SHADER, fragmentShader);
+
+ mProgramHandle = ShaderHelper.createAndLinkProgram(vertexShaderHandle, fragmentShaderHandle,
+ new String[]{"a_Position", "a_Color", "a_Normal"});
+ }
+
+ @Override
+ public void draw(DrawParameters drawParameters) {
+ GLES20.glUseProgram(mProgramHandle);
+
+ // Set program handles for camera preview drawing.
+ mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgramHandle, "u_MVPMatrix");
+ mMVMatrixHandle = GLES20.glGetUniformLocation(mProgramHandle, "u_MVMatrix");
+ mPositionHandle = GLES20.glGetAttribLocation(mProgramHandle, "a_Position");
+ mColorHandle = GLES20.glGetAttribLocation(mProgramHandle, "a_Color");
+
+ // Used to calculate shadows.
+ mLightPosHandle = GLES20.glGetUniformLocation(mProgramHandle, "u_LightPos");
+ mLightStrengthHandle = GLES20.glGetUniformLocation(mProgramHandle, "u_LightStrength");
+ mNormalHandle = GLES20.glGetAttribLocation(mProgramHandle, "a_Normal");
+
+ // Calculate lighting information
+ float[] lightPosInEyeSpace = drawParameters.getLight()
+ .getPositionInEyeSpace(drawParameters.getViewMatrix());
+ GLES20.glUniform3f(mLightPosHandle,
+ lightPosInEyeSpace[X], lightPosInEyeSpace[Y], lightPosInEyeSpace[Z]);
+ GLES20.glUniform1f(mLightStrengthHandle, drawParameters.getLight().getStrength());
+
+ updateMvpMatrix(drawParameters.getViewMatrix(), drawParameters.getProjectionMatrix());
+ drawRing();
+ }
+
+ private void drawRing() {
+ // Pass in the position information
+ mPositionBuffer.position(0);
+ GLES20.glVertexAttribPointer(mPositionHandle, POSITION_DATA_SIZE, GLES20.GL_FLOAT, false,
+ 0, mPositionBuffer);
+
+ GLES20.glEnableVertexAttribArray(mPositionHandle);
+
+ // Pass in the modelview matrix.
+ GLES20.glUniformMatrix4fv(mMVMatrixHandle, 1, false, getMvMatrix(), 0);
+
+ // Pass in the combined matrix.
+ GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, getMvpMatrix(), 0);
+
+ // Pass in the color information
+ mColorBuffer.position(0);
+ GLES20.glVertexAttribPointer(mColorHandle, COLOUR_DATA_SIZE, GLES20.GL_FLOAT, false,
+ 0, mColorBuffer);
+
+ GLES20.glEnableVertexAttribArray(mColorHandle);
+
+ // Pass in the normal information
+ mNormalsBuffer.position(0);
+ GLES20.glVertexAttribPointer(mNormalHandle, NORMAL_DATA_SIZE, GLES20.GL_FLOAT, false,
+ 0, mNormalsBuffer);
+
+ GLES20.glEnableVertexAttribArray(mNormalHandle);
+
+ // Draw the ring.
+ GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, mVertexCount);
+ }
+
+ @Override
+ public void destroy() {
+ mPositionBuffer = null;
+ mIndicesBuffer = null;
+ mNormalsBuffer = null;
+ mColorBuffer = null;
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Renderer/RobustnessRenderer.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Renderer/RobustnessRenderer.java
new file mode 100644
index 0000000..6a5f9e7
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Renderer/RobustnessRenderer.java
@@ -0,0 +1,111 @@
+/*
+ * 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 com.android.cts.verifier.sensors.sixdof.Renderer;
+
+import android.content.Context;
+
+import com.android.cts.verifier.sensors.sixdof.Renderer.Renderable.RectangleRenderable;
+
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.opengles.GL10;
+
+/**
+ * Renderer for the robustness test
+ */
+public class RobustnessRenderer extends BaseRenderer {
+
+ private float[] mRectanglePositionData;
+ private RectangleRenderable mRectangle;
+
+ public RobustnessRenderer(Context context) {
+ super(context);
+ }
+
+ @Override
+ public void onSurfaceCreated(GL10 glUnused, EGLConfig config) {
+ super.onSurfaceCreated(glUnused, config);
+ mRectangle = new RectangleRenderable();
+ }
+
+ @Override
+ public void onSurfaceChanged(GL10 glUnused, int width, int height) {
+ super.onSurfaceChanged(glUnused, width, height);
+ mRectangle.initialiseRectangle(mRectanglePositionData);
+ mProjectionMatrix = mFrustrumProjectionMatrix;
+ }
+
+ public void setLineColor(float[] newColor) {
+ if (mIsValid) {
+ mRectangle.setLineColor(newColor);
+ }
+ }
+
+ public void updateCurrentAngle(float newAngle) {
+ if (mIsValid) {
+ mRectangle.setRotationAngle(newAngle);
+ }
+ }
+
+ public void updateTargetAngle(float newAngle) {
+ if (mIsValid) {
+ mCameraPreview.setRotationAngle(newAngle);
+ }
+ }
+
+ @Override
+ protected void doPreRenderingSetup() {
+ // Set view matrix to one that doesn't move.
+ mViewMatrix = mOrthogonalViewMatrix;
+ // Set projection matrix to show camera preview slightly set back.
+ mProjectionMatrix = mFrustrumProjectionMatrix;
+ }
+
+ @Override
+ protected void doTestSpecificRendering() {
+ // Update the texture with the latest camera frame if there is an update pending.
+ updateCameraTexture();
+
+ mDrawParameters.update(mViewMatrix, mProjectionMatrix);
+ mRectangle.draw(mDrawParameters);
+ }
+
+ @Override
+ protected float[] getCameraCoordinates(float left, float right, float bottom, float top) {
+ // Set rectangle coordinates to be the exact same as the camera preview.
+ mRectanglePositionData = new float[]{
+ 2 * left, 2 * top, 0.0f,
+ 2 * left, 2 * bottom, 0.0f,
+
+ 2 * left, 2 * bottom, 0.0f,
+ 2 * right, 2 * bottom, 0.0f,
+
+ 2 * right, 2 * bottom, 0.0f,
+ 2 * right, 2 * top, 0.0f,
+
+ 2 * right, 2 * top, 0.0f,
+ 2 * left, 2 * top, 0.0f,
+ };
+
+ return new float[]{
+ 2 * left, 2 * top, 0.0f,
+ 2 * left, 2 * bottom, 0.0f,
+ 2 * right, 2 * top, 0.0f,
+ 2 * left, 2 * bottom, 0.0f,
+ 2 * right, 2 * bottom, 0.0f,
+ 2 * right, 2 * top, 0.0f,
+ };
+ }
+}
diff --git a/hostsidetests/services/activitymanager/app/src/android/server/app/BottomLeftLayoutActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Utils/Exceptions/WaypointAreaCoveredException.java
similarity index 74%
copy from hostsidetests/services/activitymanager/app/src/android/server/app/BottomLeftLayoutActivity.java
copy to apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Utils/Exceptions/WaypointAreaCoveredException.java
index 27a6fe2..8de7bb6 100644
--- a/hostsidetests/services/activitymanager/app/src/android/server/app/BottomLeftLayoutActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Utils/Exceptions/WaypointAreaCoveredException.java
@@ -13,10 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package com.android.cts.verifier.sensors.sixdof.Utils.Exceptions;
-package android.server.app;
-
-import android.app.Activity;
-
-public class BottomLeftLayoutActivity extends Activity {
+/**
+ * Exception class for not enough area covered by waypoints.
+ */
+public class WaypointAreaCoveredException extends WaypointException {
}
diff --git a/hostsidetests/services/activitymanager/app/src/android/server/app/BottomLeftLayoutActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Utils/Exceptions/WaypointDistanceException.java
similarity index 74%
copy from hostsidetests/services/activitymanager/app/src/android/server/app/BottomLeftLayoutActivity.java
copy to apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Utils/Exceptions/WaypointDistanceException.java
index 27a6fe2..c09d12f 100644
--- a/hostsidetests/services/activitymanager/app/src/android/server/app/BottomLeftLayoutActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Utils/Exceptions/WaypointDistanceException.java
@@ -13,10 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package com.android.cts.verifier.sensors.sixdof.Utils.Exceptions;
-package android.server.app;
-
-import android.app.Activity;
-
-public class BottomLeftLayoutActivity extends Activity {
+/**
+ * Exception class for being too close to the marker waypoints.
+ */
+public class WaypointDistanceException extends WaypointException {
}
diff --git a/hostsidetests/services/activitymanager/app/src/android/server/app/BottomLeftLayoutActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Utils/Exceptions/WaypointException.java
similarity index 75%
copy from hostsidetests/services/activitymanager/app/src/android/server/app/BottomLeftLayoutActivity.java
copy to apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Utils/Exceptions/WaypointException.java
index 27a6fe2..d1fc1d9 100644
--- a/hostsidetests/services/activitymanager/app/src/android/server/app/BottomLeftLayoutActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Utils/Exceptions/WaypointException.java
@@ -13,10 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package com.android.cts.verifier.sensors.sixdof.Utils.Exceptions;
-package android.server.app;
-
-import android.app.Activity;
-
-public class BottomLeftLayoutActivity extends Activity {
+/**
+ * Generic abstract exception class used by the other exceptions.
+ */
+public abstract class WaypointException extends Exception {
}
diff --git a/hostsidetests/services/activitymanager/app/src/android/server/app/BottomLeftLayoutActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Utils/Exceptions/WaypointRingNotEnteredException.java
similarity index 74%
copy from hostsidetests/services/activitymanager/app/src/android/server/app/BottomLeftLayoutActivity.java
copy to apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Utils/Exceptions/WaypointRingNotEnteredException.java
index 27a6fe2..855bfce 100644
--- a/hostsidetests/services/activitymanager/app/src/android/server/app/BottomLeftLayoutActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Utils/Exceptions/WaypointRingNotEnteredException.java
@@ -13,10 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package com.android.cts.verifier.sensors.sixdof.Utils.Exceptions;
-package android.server.app;
-
-import android.app.Activity;
-
-public class BottomLeftLayoutActivity extends Activity {
+/**
+ * Exception class for when a ring has not been entered.
+ */
+public class WaypointRingNotEnteredException extends WaypointException {
}
diff --git a/hostsidetests/services/activitymanager/app/src/android/server/app/BottomLeftLayoutActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Utils/Exceptions/WaypointStartPointException.java
similarity index 73%
copy from hostsidetests/services/activitymanager/app/src/android/server/app/BottomLeftLayoutActivity.java
copy to apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Utils/Exceptions/WaypointStartPointException.java
index 27a6fe2..d2664f9 100644
--- a/hostsidetests/services/activitymanager/app/src/android/server/app/BottomLeftLayoutActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Utils/Exceptions/WaypointStartPointException.java
@@ -13,10 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package com.android.cts.verifier.sensors.sixdof.Utils.Exceptions;
-package android.server.app;
-
-import android.app.Activity;
-
-public class BottomLeftLayoutActivity extends Activity {
+/**
+ * Exception class for the user not going back to the initial marker.
+ */
+public class WaypointStartPointException extends WaypointException {
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Utils/Manager.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Utils/Manager.java
new file mode 100644
index 0000000..f45071c
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Utils/Manager.java
@@ -0,0 +1,374 @@
+/*
+ * 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 com.android.cts.verifier.sensors.sixdof.Utils;
+
+import com.android.cts.verifier.sensors.sixdof.Dialogs.BaseResultsDialog;
+import com.android.cts.verifier.sensors.sixdof.Interfaces.AccuracyListener;
+import com.android.cts.verifier.sensors.sixdof.Interfaces.BaseUiListener;
+import com.android.cts.verifier.sensors.sixdof.Interfaces.ComplexMovementListener;
+import com.android.cts.verifier.sensors.sixdof.Interfaces.RobustnessListener;
+import com.android.cts.verifier.sensors.sixdof.Renderer.BaseRenderer;
+import com.android.cts.verifier.sensors.sixdof.Utils.Exceptions.WaypointAreaCoveredException;
+import com.android.cts.verifier.sensors.sixdof.Utils.Exceptions.WaypointDistanceException;
+import com.android.cts.verifier.sensors.sixdof.Utils.Exceptions.WaypointException;
+import com.android.cts.verifier.sensors.sixdof.Utils.Exceptions.WaypointRingNotEnteredException;
+import com.android.cts.verifier.sensors.sixdof.Utils.Exceptions.WaypointStartPointException;
+import com.android.cts.verifier.sensors.sixdof.Utils.Path.ReferencePath;
+import com.android.cts.verifier.sensors.sixdof.Utils.Path.PathUtilityClasses.Ring;
+import com.android.cts.verifier.sensors.sixdof.Utils.Path.PathUtilityClasses.RotationData;
+import com.android.cts.verifier.sensors.sixdof.Utils.Path.PathUtilityClasses.Waypoint;
+import com.android.cts.verifier.sensors.sixdof.Utils.PoseProvider.PoseData;
+import com.android.cts.verifier.sensors.sixdof.Utils.ResultObjects.ResultObject;
+import com.android.cts.verifier.sensors.sixdof.Utils.TestPhase.AccuracyTest;
+import com.android.cts.verifier.sensors.sixdof.Utils.TestPhase.ComplexMovementTest;
+import com.android.cts.verifier.sensors.sixdof.Utils.TestPhase.RobustnessTest;
+
+import android.content.Context;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+/**
+ * Manages all of the tests.
+ */
+public class Manager {
+ private Lap mLap = Lap.LAP_1;
+ public static final int MAX_MARKER_NUMBER = 5;
+ private ReferencePath mReferencePath = new ReferencePath();
+ private AccuracyTest mAccuracyTest;
+ private RobustnessTest mRobustnessTest;
+ private ComplexMovementTest mComplexMovementTest;
+ private TestReport mTestReport;
+ private float mRemainingPath;
+ private long mTimeRemaining;
+
+ public enum Lap {
+ LAP_1,
+ LAP_2,
+ LAP_3,
+ LAP_4,
+ }
+
+ private ComplexMovementListener mComplexMovementListener;
+ private RobustnessListener mRobustnessListener;
+ private AccuracyListener mAccuracyListener;
+ private BaseUiListener mBaseUiListener;
+
+ /**
+ * Links the listeners to the activity.
+ *
+ * @param context reference to the activity.
+ */
+ public void setupListeners(Context context) {
+ mAccuracyListener = (AccuracyListener) context;
+ mRobustnessListener = (RobustnessListener) context;
+ mComplexMovementListener = (ComplexMovementListener) context;
+ mBaseUiListener = (BaseUiListener) context;
+ }
+
+ /**
+ * Removes the references to the activity so that the activity can be properly terminated.
+ */
+ public void stopListening() {
+ mRobustnessListener = null;
+ mAccuracyListener = null;
+ mBaseUiListener = null;
+ mComplexMovementListener = null;
+ }
+
+ public void ringEntered(Ring ring) {
+ mComplexMovementListener.onRingEntered(ring);
+ }
+
+ /**
+ * Indicated that the pose provider is ready.
+ */
+ public void onPoseProviderReady() {
+ mBaseUiListener.onPoseProviderReady();
+ }
+
+ /**
+ * Constructor for the class.
+ *
+ * @param testReport a reference to the test report to be used to record failures.
+ */
+ public Manager(TestReport testReport) {
+ mTestReport = testReport;
+ }
+
+ /**
+ * Adds the waypoint data to the appropriate path.
+ *
+ * @param coordinates the coordinates to use for the waypoint.
+ * @param userGenerated indicates whether the data was user created or system created.
+ * @throws WaypointDistanceException if the location is too close to another.
+ * @throws WaypointAreaCoveredException if the area covered by the user is too little.
+ * @throws WaypointStartPointException if the location is not close enough to the start.
+ */
+ public void addPoseDataToPath(
+ float[] coordinates, boolean userGenerated)
+ throws WaypointAreaCoveredException, WaypointDistanceException,
+ WaypointStartPointException, WaypointRingNotEnteredException {
+ switch (mLap) {
+ case LAP_1:
+ try {
+ mReferencePath.createWaypointAndAddToPath(coordinates, userGenerated, mLap);
+ } catch (WaypointStartPointException exception) {
+ float[] initialCoords = mReferencePath.getPathMarkers().get(0).getCoordinates();
+ String initialWaypointCoords =
+ MathsUtils.coordinatesToString(initialCoords);
+ String distance = String.valueOf(
+ MathsUtils.distanceCalculationInXYZSpace(
+ initialCoords, coordinates));
+ String details = "Not close enough to initial waypoint:\n"
+ + "Distance:"
+ + distance
+ + "\nInitial Waypoint Coordinates: "
+ + initialWaypointCoords
+ + "\nAttempted placement coordinates: "
+ + MathsUtils.coordinatesToString(coordinates);
+ mTestReport.setFailDetails(details);
+
+ // We still need to give the exception to UI to display message.
+ throw exception;
+ }
+
+ if (mReferencePath.getPathMarkersSize() == MAX_MARKER_NUMBER) {
+ mAccuracyListener.lap1Complete();
+ }
+ break;
+ case LAP_2:
+ mAccuracyTest.addWaypointDataToPath(coordinates, userGenerated, mLap);
+ break;
+ case LAP_3:
+ mRobustnessTest.addWaypointDataToPath(coordinates, userGenerated, mLap);
+ break;
+ case LAP_4:
+ mComplexMovementTest.addWaypointDataToPath(coordinates, userGenerated, mLap);
+ break;
+ default:
+ throw new AssertionError("addPoseDataToPath default: Unrecognised lap", null);
+ }
+ if (userGenerated) {
+ mBaseUiListener.onWaypointPlaced();
+ }
+ }
+
+ /**
+ * Removes the last marker from the current lap.
+ */
+ public void removeLastAddedMarker() {
+ boolean resetTest;
+ switch (mLap) {
+ case LAP_1:
+ resetTest = mReferencePath.removeLastMarker();
+ break;
+ case LAP_2:
+ resetTest = mAccuracyTest.removeLastAddedMarker();
+ break;
+ case LAP_3:
+ resetTest = mRobustnessTest.removeLastAddedMarker();
+ break;
+ case LAP_4:
+ resetTest = mComplexMovementTest.removeLastAddedMarker();
+ break;
+ default:
+ throw new AssertionError("removeLastAddedMarker default: Unrecognised lap", null);
+ }
+ if (resetTest) {
+ mAccuracyListener.onReset();
+ }
+ }
+
+ /**
+ * Initiates the accuracy test.
+ */
+ public void startAccuracyTest() {
+ mAccuracyTest = new AccuracyTest(mReferencePath, mTestReport, this);
+ mLap = Lap.LAP_2;
+ }
+
+ /**
+ * Initiates the robustness test.
+ */
+ public void startRobustnessTest() {
+ mRobustnessTest = new RobustnessTest(mReferencePath, mTestReport, this,
+ BaseRenderer.getDeviceRotation((Context) mBaseUiListener));
+ mLap = Lap.LAP_3;
+
+ }
+
+ /**
+ * Initiates the complex movement test.
+ */
+ public void startComplexMovementTest() {
+ mComplexMovementTest = new ComplexMovementTest(mReferencePath, mTestReport, this);
+ mLap = Lap.LAP_4;
+ }
+
+ /**
+ * Indicates that the accuracy test has been completed.
+ *
+ * @param passList A list to indicate whether the test passes or not.
+ */
+ public void onAccuracyTestCompleted(HashMap<BaseResultsDialog.ResultType, Boolean> passList) {
+ mBaseUiListener.onResult(new ResultObject(passList));
+ }
+
+ /**
+ * Indicates that the robustness test has been completed.
+ *
+ * @param robustnessTestResults List containing information about whether the tests failed or
+ * passed.
+ */
+ public void onRobustnessTestCompleted(HashMap<BaseResultsDialog.ResultType, Boolean> robustnessTestResults) {
+ ResultObject robustnessResult = new ResultObject(robustnessTestResults);
+ mBaseUiListener.onResult(robustnessResult);
+ }
+
+ /**
+ * Indicates that the complex movement test has been completed.
+ *
+ * @param complexMovementTestResults List containing information about whether the tests failed
+ * or passed.
+ */
+ public void onComplexMovementTestCompleted(HashMap<BaseResultsDialog.ResultType, Boolean> complexMovementTestResults) {
+ ResultObject complexMovementResult = new ResultObject(complexMovementTestResults);
+
+ if (complexMovementResult.hasPassed()) {
+ mTestReport.setTestState(TestReport.TestStatus.PASS);
+ }
+
+ mBaseUiListener.onResult(complexMovementResult);
+ }
+
+ /**
+ * Sets the path remaining for the user to travel.
+ */
+ public void calculateRemainingPath() {
+ mRemainingPath = mReferencePath.calculatePathRemaining();
+ }
+
+ /**
+ * Uses the current rotation and location to calculate the rotation detail's. Also gives the UI
+ * information about the rotation.
+ *
+ * @param rotations Quaternion containing the current rotation.
+ * @param translation The location the rotation occurred.
+ */
+ public void calculateRotationData(float[] rotations, float[] translation) {
+ RotationData rotationData = mRobustnessTest.getRotationData(rotations, translation);
+ if (rotationData != null) {
+ mRobustnessListener.onNewRotationData(rotationData);
+ }
+ }
+
+ /**
+ * Sets the time remaining to place a waypoint.
+ */
+ public void calculateTimeRemaining() {
+ mTimeRemaining = mRobustnessTest.getTimeRemaining();
+ }
+
+ /**
+ * Handles new pose data.
+ *
+ * @param currentPose The current pose data.
+ */
+ public void onNewPoseData(PoseData currentPose) {
+ if (mReferencePath.getCurrentPathSize() != 0) {
+ switch (mLap) {
+ case LAP_1:
+ calculateRemainingPath();
+ break;
+ case LAP_2:
+ break;
+ case LAP_3:
+ if (mRobustnessTest.getTestPathMarkersSize() > 0) {
+ calculateTimeRemaining();
+ calculateRotationData(currentPose.getRotationAsFloats(), currentPose.getTranslationAsFloats());
+ }
+ break;
+ case LAP_4:
+ mComplexMovementTest.checkIfARingHasBeenPassed(currentPose.getTranslationAsFloats());
+ break;
+ }
+ try {
+ addPoseDataToPath(currentPose.getTranslationAsFloats(),
+ false);
+ } catch (WaypointException e) {
+ throw new AssertionError(
+ "System added waypoint should not be validated", e);
+ }
+ }
+ }
+
+ /**
+ * Returns the distance remaining to travel by the user.
+ */
+ public float getRemainingPath() {
+ return mRemainingPath;
+ }
+
+ /**
+ * Returns the makers in the reference path.
+ */
+ public ArrayList<Waypoint> getReferencePathMarkers() {
+ return mReferencePath.getPathMarkers();
+ }
+
+ /**
+ * Returns the makers in the accuracy test path.
+ */
+ public ArrayList<Waypoint> getTestPathMarkers() {
+ return mAccuracyTest.getTestPathMarkers();
+ }
+
+ /**
+ * Returns the time remaining to place the marker.
+ */
+ public long getTimeRemaining() {
+ return mTimeRemaining;
+ }
+
+ /**
+ * Returns the markers in the robustness test path.
+ */
+ public ArrayList<Waypoint> getRobustnessMarker() {
+ return mRobustnessTest.getTestPathMarkers();
+ }
+
+ /**
+ * Returns the current phase of the test.
+ */
+ public Lap getLap() {
+ return mLap;
+ }
+
+ /**
+ * Returns the rings in the ComplexMovement path.
+ */
+ public ArrayList<Ring> getRings() {
+ return mComplexMovementTest.getRings();
+ }
+
+ /**
+ * Returns the makers in the ComplexMovement test path.
+ */
+ public ArrayList<Waypoint> getComplexMovementTestMarkers() {
+ return mComplexMovementTest.getTestPathMarkers();
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Utils/MathsUtils.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Utils/MathsUtils.java
new file mode 100644
index 0000000..ee0ab2d
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Utils/MathsUtils.java
@@ -0,0 +1,309 @@
+/*
+ * 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 com.android.cts.verifier.sensors.sixdof.Utils;
+
+import android.opengl.Matrix;
+import android.util.Log;
+
+import com.android.cts.verifier.sensors.sixdof.Utils.PoseProvider.PoseData;
+
+import java.text.DecimalFormat;
+
+/**
+ * Contains functions that are used throughout the app.
+ */
+public class MathsUtils {
+ public static final int X = PoseData.INDEX_TRANSLATION_X;
+ public static final int Y = PoseData.INDEX_TRANSLATION_Y;
+ public static final int Z = PoseData.INDEX_TRANSLATION_Z;
+
+ public static final int MATRIX_4X4 = 16;
+ public static final int VECTOR_2D = 2;
+ public static final int VECTOR_3D = 3;
+
+ public static final int ORIENTATION_0 = 0;
+ public static final int ORIENTATION_90_ANTI_CLOCKWISE = 90;
+ public static final int ORIENTATION_180_ANTI_CLOCKWISE = 180;
+ public static final int ORIENTATION_270_ANTI_CLOCKWISE = 270;
+ public static final int ORIENTATION_360_ANTI_CLOCKWISE = 360;
+
+ /**
+ * Converts from float array in 6DoF coordinate system to a Vector3 in OpenGl coordinate
+ * system.
+ *
+ * @param location float array to convert.
+ * @return the Vector3 in OpenGL coord system.
+ */
+ public static float[] convertToOpenGlCoordinates(float[] location, int toRotate) {
+ // Have to swap Y and Z as they are different in OpenGl and 6DoF. Also invert Z.
+ float[] inDefaultOrientation = new float[]{location[X], location[Z], -location[Y]};
+
+ return rotateCoordinates(inDefaultOrientation, toRotate);
+ }
+
+ public static float[] rotateCoordinates(float[] coordinates, int toRotate) {
+ final float[] inCurrentOrientation;
+
+ switch (toRotate) {
+ case ORIENTATION_0:
+ case ORIENTATION_360_ANTI_CLOCKWISE:
+ inCurrentOrientation = coordinates;
+ break;
+ case ORIENTATION_90_ANTI_CLOCKWISE:
+ inCurrentOrientation = new float[]{coordinates[Y], -coordinates[X],
+ coordinates[Z]};
+ break;
+ case ORIENTATION_180_ANTI_CLOCKWISE:
+ inCurrentOrientation = new float[]{coordinates[X], coordinates[Y],
+ coordinates[Z]};
+ break;
+ case ORIENTATION_270_ANTI_CLOCKWISE:
+ inCurrentOrientation = new float[]{-coordinates[Y], coordinates[X],
+ coordinates[Z]};
+ break;
+ default:
+ throw new RuntimeException("Unexpected orientation that cannot be dealt with!");
+ }
+
+ return inCurrentOrientation;
+ }
+
+ /**
+ * Produce a rotation transformation that looks at a point 'center' from a point 'eye'.
+ */
+ public static void setLookAtM(float[] rm, float eyeX, float eyeY, float eyeZ,
+ float centerX, float centerY, float centerZ,
+ float upX, float upY, float upZ) {
+ // Algorithm taken from DirectX documentation.
+ // https://msdn.microsoft.com/en-us/library/bb205343.aspx
+
+ float zAxisX = eyeX - centerX;
+ float zAxisY = eyeY - centerY;
+ float zAxisZ = eyeZ - centerZ;
+
+ // Normalize zAxis
+ float rlf = 1.0f / Matrix.length(zAxisX, zAxisY, zAxisZ);
+ zAxisX *= rlf;
+ zAxisY *= rlf;
+ zAxisZ *= rlf;
+
+ // compute xAxis = up x zAxis (x means "cross product")
+ float xAxisX = upY * zAxisZ - upZ * zAxisY;
+ float xAxisY = upZ * zAxisX - upX * zAxisZ;
+ float xAxisZ = upX * zAxisY - upY * zAxisX;
+
+ // and normalize xAxis
+ float rls = 1.0f / Matrix.length(xAxisX, xAxisY, xAxisZ);
+ xAxisX *= rls;
+ xAxisY *= rls;
+ xAxisZ *= rls;
+
+ // compute yAxis = zAxis x xAxis
+ float yAxisX = zAxisY * xAxisZ - zAxisZ * xAxisY;
+ float yAxisY = zAxisZ * xAxisX - zAxisX * xAxisZ;
+ float yAxisZ = zAxisX * xAxisY - zAxisY * xAxisX;
+
+ rm[0] = xAxisX;
+ rm[1] = xAxisY;
+ rm[2] = xAxisZ;
+ rm[3] = (xAxisX * eyeX) + (xAxisY * eyeY) + (xAxisZ * eyeZ);
+
+ rm[4] = yAxisX;
+ rm[5] = yAxisY;
+ rm[6] = yAxisZ;
+ rm[7] = (yAxisX * eyeX) + (yAxisY * eyeY) + (yAxisZ * eyeZ);
+
+ rm[8] = zAxisX;
+ rm[9] = zAxisY;
+ rm[10] = zAxisZ;
+ rm[11] = (zAxisX * eyeX) + (zAxisY * eyeY) + (zAxisZ * eyeZ);
+
+ rm[12] = 0.0f;
+ rm[13] = 0.0f;
+ rm[14] = 0.0f;
+ rm[15] = 1.0f;
+ }
+
+ /**
+ * A function to convert a quaternion to quaternion Matrix. Please note that Opengl.Matrix is
+ * Column Major and so we construct the matrix in Column Major Format. - - - - | 0 4 8 12 | | 1
+ * 5 9 13 | | 2 6 10 14 | | 3 7 11 15 | - - - -
+ *
+ * @param quaternion Input quaternion with float[4]
+ * @return Quaternion Matrix of float[16]
+ */
+ public static float[] quaternionMatrixOpenGL(float[] quaternion) {
+ float[] matrix = new float[16];
+ normalizeVector(quaternion);
+
+ float x = quaternion[0];
+ float y = quaternion[1];
+ float z = quaternion[2];
+ float w = quaternion[3];
+
+ float x2 = x * x;
+ float y2 = y * y;
+ float z2 = z * z;
+ float xy = x * y;
+ float xz = x * z;
+ float yz = y * z;
+ float wx = w * x;
+ float wy = w * y;
+ float wz = w * z;
+
+ matrix[0] = 1f - 2f * (y2 + z2);
+ matrix[4] = 2f * (xy - wz);
+ matrix[8] = 2f * (xz + wy);
+ matrix[12] = 0f;
+
+ matrix[1] = 2f * (xy + wz);
+ matrix[5] = 1f - 2f * (x2 + z2);
+ matrix[9] = 2f * (yz - wx);
+ matrix[13] = 0f;
+
+ matrix[2] = 2f * (xz - wy);
+ matrix[6] = 2f * (yz + wx);
+ matrix[10] = 1f - 2f * (x2 + y2);
+ matrix[14] = 0f;
+
+ matrix[3] = 0f;
+ matrix[7] = 0f;
+ matrix[11] = 0f;
+ matrix[15] = 1f;
+
+ return matrix;
+ }
+
+ /**
+ * Calculates the dot product between two vectors.
+ *
+ * @param vector1 the first vector.
+ * @param vector2 the second vector.
+ * @return the dot product.
+ */
+ public static double dotProduct(float[] vector1, float[] vector2, int dimensions) {
+ double total = 0.0;
+ for (int i = 0; i < dimensions; i++) {
+ total += vector1[i] * vector2[i];
+ }
+ return total;
+ }
+
+ /**
+ * Creates a unit vector in the direction of an arbitrary vector. The original vector is
+ * modified in place.
+ *
+ * @param v the vector to normalize.
+ */
+ public static void normalizeVector(float[] v) {
+ float mag2 = v[0] * v[0] + v[1] * v[1] + v[2] * v[2] + v[3] * v[3];
+ if (Math.abs(mag2) > 0.00001f && Math.abs(mag2 - 1.0f) > 0.00001f) {
+ float mag = (float) Math.sqrt(mag2);
+ v[0] = v[0] / mag;
+ v[1] = v[1] / mag;
+ v[2] = v[2] / mag;
+ v[3] = v[3] / mag;
+ }
+ }
+
+ /**
+ * Calculates the distance between 2 points in 2D space.
+ *
+ * @param point1 the mCoordinates of the first point.
+ * @param point2 the mCoordinates of the second point.
+ * @return the distance between the 2 points.
+ */
+ public static float distanceCalculationOnXYPlane(float[] point1, float[] point2) {
+ float yDifference = point2[Y] - point1[Y];
+ float xDifference = point2[X] - point1[X];
+ return (float) Math.sqrt((yDifference * yDifference) + (xDifference * xDifference));
+ }
+
+ /**
+ * Calculates the distance between 2 points in 3D space.
+ *
+ * @param point1 the mCoordinates of the first point.
+ * @param point2 the mCoordinates of the second point.
+ * @return the distance between the 2 points.
+ */
+ public static float distanceCalculationInXYZSpace(float[] point1, float[] point2) {
+ float zDifference = point2[Z] - point1[Z];
+ float yDifference = point2[Y] - point1[Y];
+ float xDifference = point2[X] - point1[X];
+ return (float) Math.sqrt((zDifference * zDifference) + (yDifference * yDifference) +
+ (xDifference * xDifference));
+ }
+
+ /**
+ * Puts the given coordinates in a printable format.
+ *
+ * @param coordinates the mCoordinates to print.
+ * @return the mCoordinates formatted.
+ */
+ public static String coordinatesToString(float[] coordinates) {
+ DecimalFormat threeDec = new DecimalFormat("0.0");
+ String formattedCoordinates = "[";
+ for (int i = 0; i < coordinates.length; i++) {
+ formattedCoordinates += threeDec.format(coordinates[i]);
+ if (i < (coordinates.length - 1)) {
+ formattedCoordinates += ", ";
+ } else {
+ formattedCoordinates += "]";
+ }
+ }
+ return formattedCoordinates;
+ }
+
+ public static float[] getDeviceOrientationMatrix(int toRotate) {
+ float[] deviceMatrix;
+
+ switch (toRotate) {
+ case ORIENTATION_0:
+ case ORIENTATION_360_ANTI_CLOCKWISE:
+ deviceMatrix = new float[]{1.0f, 0.0f, 0.0f, 0.0f,
+ 0.0f, 1.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, 1.0f, 0.0f,
+ 0.0f, 0.0f, 0.0f, 1.0f};
+ break;
+ case ORIENTATION_90_ANTI_CLOCKWISE:
+ deviceMatrix = new float[]{
+ 0.0f, -1.0f, 0.0f, 0.0f,
+ 1.0f, 0.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, 1.0f, 0.0f,
+ 0.0f, 0.0f, 0.0f, 1.0f};
+ break;
+ case ORIENTATION_180_ANTI_CLOCKWISE:
+ deviceMatrix = new float[]{
+ -1.0f, 0.0f, 0.0f, 0.0f,
+ 0.0f, -1.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, 1.0f, 0.0f,
+ 0.0f, 0.0f, 0.0f, 1.0f};
+ break;
+ case ORIENTATION_270_ANTI_CLOCKWISE:
+ deviceMatrix = new float[]{
+ 0.0f, 1.0f, 0.0f, 0.0f,
+ -1.0f, 0.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, 1.0f, 0.0f,
+ 0.0f, 0.0f, 0.0f, 1.0f};
+ break;
+ default:
+ throw new RuntimeException("Unexpected orientation that cannot be dealt with!");
+ }
+
+ return deviceMatrix;
+ }
+}
+
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Utils/Path/AccuracyPath.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Utils/Path/AccuracyPath.java
new file mode 100644
index 0000000..22e3bf4
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Utils/Path/AccuracyPath.java
@@ -0,0 +1,32 @@
+/*
+ * 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 com.android.cts.verifier.sensors.sixdof.Utils.Path;
+
+/**
+ * Class with the implementation for the Accuracy path
+ */
+public class AccuracyPath extends Path {
+ /**
+ * Implementation of the abstract class in path but left empty because there is nothing extra to
+ * check for.
+ *
+ * @param coordinates the coordinates for the waypoint
+ */
+ @Override
+ public void additionalChecks(float[] coordinates) {
+ // No additional checks required in this test.
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Utils/Path/ComplexMovementPath.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Utils/Path/ComplexMovementPath.java
new file mode 100644
index 0000000..bc7c2c4
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Utils/Path/ComplexMovementPath.java
@@ -0,0 +1,333 @@
+/*
+ * 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 com.android.cts.verifier.sensors.sixdof.Utils.Path;
+
+import java.util.ArrayList;
+import java.util.Random;
+
+import static com.android.cts.verifier.sensors.sixdof.Utils.MathsUtils.VECTOR_2D;
+import static com.android.cts.verifier.sensors.sixdof.Utils.MathsUtils.X;
+import static com.android.cts.verifier.sensors.sixdof.Utils.MathsUtils.Y;
+import static com.android.cts.verifier.sensors.sixdof.Utils.MathsUtils.Z;
+import static com.android.cts.verifier.sensors.sixdof.Utils.MathsUtils.dotProduct;
+
+import com.android.cts.verifier.sensors.sixdof.Utils.MathsUtils;
+import com.android.cts.verifier.sensors.sixdof.Utils.Exceptions.WaypointRingNotEnteredException;
+import com.android.cts.verifier.sensors.sixdof.Utils.Path.PathUtilityClasses.Ring;
+import com.android.cts.verifier.sensors.sixdof.Utils.Path.PathUtilityClasses.Waypoint;
+
+/**
+ * Handles all the path properties of the ComplexMovement Path.
+ */
+public class ComplexMovementPath extends com.android.cts.verifier.sensors.sixdof.Utils.Path.Path {
+ public static final float DISTANCE_FOR_RING_POSITION = 0.25f;
+ public static final int RINGS_PER_PATH = 5;
+
+ private ArrayList<Ring> mRings = new ArrayList<>();
+ private Random mRandomGenerator = new Random();
+ private int mCurrentLap = 0;
+ private float mLocationMapping[][];
+
+ /**
+ * Possible locations for a ring.
+ */
+ private enum RingLocations {
+ ORIGINAL,
+ TOP,
+ DOWN,
+ LEFT,
+ RIGHT,
+ TOP_LEFT,
+ TOP_RIGHT,
+ BOTTOM_LEFT,
+ BOTTOM_RIGHT,
+ }
+
+ /**
+ * Constructor for this class does the mapping and the creation of rings.
+ *
+ * @param referencePathDistances The distance between the markers in the reference path
+ * @param referencePath The reference path
+ */
+ public ComplexMovementPath(
+ ArrayList<Float> referencePathDistances, ArrayList<Waypoint> referencePath) {
+ mapNineRingLocations();
+ generatePathRings(referencePathDistances, referencePath);
+ }
+
+ /**
+ * Defines the different ring locations that can be used when adding the rings.
+ */
+ private void mapNineRingLocations() {
+ mLocationMapping = new float[RingLocations.values().length][2];
+ mLocationMapping[RingLocations.ORIGINAL.ordinal()] = new float[]{0f, 0f};
+ mLocationMapping[RingLocations.TOP.ordinal()] =
+ new float[]{0f, DISTANCE_FOR_RING_POSITION};
+ mLocationMapping[RingLocations.DOWN.ordinal()] =
+ new float[]{0f, -DISTANCE_FOR_RING_POSITION};
+ mLocationMapping[RingLocations.LEFT.ordinal()] =
+ new float[]{-DISTANCE_FOR_RING_POSITION, 0f};
+ mLocationMapping[RingLocations.RIGHT.ordinal()] =
+ new float[]{DISTANCE_FOR_RING_POSITION, 0f};
+ mLocationMapping[RingLocations.TOP_LEFT.ordinal()] =
+ new float[]{-DISTANCE_FOR_RING_POSITION, DISTANCE_FOR_RING_POSITION};
+ mLocationMapping[RingLocations.TOP_RIGHT.ordinal()] =
+ new float[]{DISTANCE_FOR_RING_POSITION, DISTANCE_FOR_RING_POSITION};
+ mLocationMapping[RingLocations.BOTTOM_LEFT.ordinal()] =
+ new float[]{-DISTANCE_FOR_RING_POSITION, -DISTANCE_FOR_RING_POSITION};
+ mLocationMapping[RingLocations.BOTTOM_RIGHT.ordinal()] =
+ new float[]{DISTANCE_FOR_RING_POSITION, -DISTANCE_FOR_RING_POSITION};
+ }
+
+ /**
+ * Performs ComplexMovement path related checks on a marker.
+ *
+ * @param coordinates the coordinates for the waypoint
+ * @throws WaypointRingNotEnteredException if a ring is not entered
+ */
+ @Override
+ public void additionalChecks(float[] coordinates) throws WaypointRingNotEnteredException {
+ if (mCurrentLap != 0) {
+ for (Ring ring : mRings) {
+ if (ring.getPathNumber() == mCurrentLap && !ring.isEntered()) {
+ throw new WaypointRingNotEnteredException();
+ }
+ }
+ }
+ mCurrentLap++;
+ }
+
+ /**
+ * Generates the rings for this path.
+ *
+ * @param referencePathDistances The distance between the markers in the reference path
+ * @param referencePath The reference path
+ */
+ private void generatePathRings(
+ ArrayList<Float> referencePathDistances, ArrayList<Waypoint> referencePath) {
+ ArrayList<Float> distanceBetweenRingSections;
+ distanceBetweenRingSections = calculateSectionDistance(referencePathDistances);
+ addRingsToPath(referencePath, distanceBetweenRingSections);
+ }
+
+ /**
+ * Calculates the distance between the rings in a path.
+ *
+ * @param referencePathDistances The distance between the markers in the reference path.
+ * @return The length of a section in the different paths.
+ */
+ private ArrayList<Float> calculateSectionDistance(ArrayList<Float> referencePathDistances) {
+ ArrayList<Float> arrayToReturn = new ArrayList<>();
+ for (Float distance : referencePathDistances) {
+ arrayToReturn.add(distance / (RINGS_PER_PATH + 1f));
+ }
+ return arrayToReturn;
+ }
+
+ /**
+ * Calculates the location for the ring and adds it to the path.
+ *
+ * @param referencePath The reference path.
+ * @param distanceBetweenRingSections The length of a section in the different paths.
+ */
+ private void addRingsToPath(
+ ArrayList<Waypoint> referencePath, ArrayList<Float> distanceBetweenRingSections) {
+ int currentPath = 0;
+ Waypoint currentWaypoint = referencePath.get(0);
+ for (Float pathIntervalDistance : distanceBetweenRingSections) {
+ currentPath++;
+ for (int i = 0; i < RINGS_PER_PATH; i++) {
+ currentWaypoint = calculateRingLocationOnPath(
+ referencePath, referencePath.indexOf(currentWaypoint), pathIntervalDistance);
+ mRings.add(createRing(referencePath, currentWaypoint, currentPath));
+ }
+ while (!currentWaypoint.isUserGenerated()) {
+ currentWaypoint = referencePath.get(referencePath.indexOf(currentWaypoint) + 1);
+ }
+ }
+ }
+
+ /**
+ * Creates the ring that will be added onto the path.
+ *
+ * @param referencePath The reference path.
+ * @param waypoint The waypoint which the ring will be located at.
+ * @param currentPath The part of the lap in which the ring will be placed.
+ * @return A reference to the ring created.
+ */
+ private Ring createRing(ArrayList<Waypoint> referencePath, Waypoint waypoint, int currentPath) {
+ float[] ringCenter = waypoint.getCoordinates();
+ float[] pointRotation = calculateRingRotation(ringCenter,
+ referencePath.get(referencePath.indexOf(waypoint) - 1).getCoordinates());
+ int randomNumber = mRandomGenerator.nextInt(RingLocations.values().length);
+ RingLocations ringLocationDifference = RingLocations.values()[randomNumber];
+ ringCenter[X] += mLocationMapping[ringLocationDifference.ordinal()][0];
+ ringCenter[Z] += mLocationMapping[ringLocationDifference.ordinal()][1];
+ ArrayList<float[]> rotatedRect = calculateRectangleHitbox(ringCenter, pointRotation);
+ return new Ring(ringCenter, currentPath, pointRotation, rotatedRect);
+ }
+
+ /**
+ * Calculates the orientation of the ring.
+ *
+ * @param location1 The location of the first point.
+ * @param location2 The location of the second point.
+ * @return the rotation needed to get the orientation of the ring.
+ */
+ private float[] calculateRingRotation(float[] location1, float[] location2) {
+ float[] rotation = new float[3];
+ rotation[X] = location2[X] - location1[X];
+ rotation[Y] = location2[Y] - location1[Y];
+ rotation[Z] = location2[Z] - location1[Z];
+ return rotation;
+ }
+
+ /**
+ * Calculates the next possible position for the ring to be placed at.
+ *
+ * @param referencePath The reference path.
+ * @param currentLocation The location to start calculating from.
+ * @param pathIntervalDistance The distance indicating how far apart the rings are going to be.
+ * @return The waypoint where the ring will be placed at.
+ */
+ private Waypoint calculateRingLocationOnPath(
+ ArrayList<Waypoint> referencePath, int currentLocation, Float pathIntervalDistance) {
+ float pathRemaining = 0;
+ while (currentLocation < referencePath.size() - 1) {
+ pathRemaining += MathsUtils.distanceCalculationOnXYPlane(
+ referencePath.get(currentLocation).getCoordinates(),
+ referencePath.get(currentLocation + 1).getCoordinates());
+ if (pathRemaining >= pathIntervalDistance) {
+ return referencePath.get(currentLocation);
+ }
+ currentLocation++;
+ }
+ throw new AssertionError(
+ "calculateRingLocationOnPath: Ring number and section number don't seem to match up");
+ }
+
+ /**
+ * Calculates the rectangular hit box for the ring.
+ *
+ * @param centre the middle location of the ring.
+ * @param rotation the rotation to get the same orientation of the ring.
+ * @return The four corners of the rectangle.
+ */
+ private ArrayList<float[]> calculateRectangleHitbox(float[] centre, float[] rotation) {
+ ArrayList<float[]> rectangle = new ArrayList<>();
+ float magnitude = (float) Math.sqrt(Math.pow(rotation[X], 2) +
+ Math.pow(rotation[Z], 2));
+ float lengthScaleFactor = 0.02f / magnitude;
+ float widthScaleFactor = 0.17f / magnitude;
+
+ float[] rotationInverse = {0 - rotation[X], 0 - rotation[Y]};
+ float[] rotationNinety = {rotation[Y], 0 - rotation[X]};
+ float[] rotationNinetyInverse = {0 - rotation[Y], rotation[X]};
+
+ float[] midFront = new float[2];
+ midFront[X] = centre[X] + (lengthScaleFactor * rotation[X]);
+ midFront[Y] = centre[Y] + (lengthScaleFactor * rotation[Y]);
+ float[] midRear = new float[2];
+ midRear[X] = centre[X] + (lengthScaleFactor * rotationInverse[X]);
+ midRear[Y] = centre[Y] + (lengthScaleFactor * rotationInverse[Y]);
+
+ float[] frontLeft = new float[3];
+ frontLeft[Z] = centre[Z];
+ frontLeft[X] = midFront[X] + (widthScaleFactor * rotationNinetyInverse[X]);
+ frontLeft[Y] = midFront[Y] + (widthScaleFactor * rotationNinetyInverse[Y]);
+ float[] frontRight = new float[3];
+ frontRight[Z] = centre[Z];
+ frontRight[X] = midFront[X] + (widthScaleFactor * rotationNinety[X]);
+ frontRight[Y] = midFront[Y] + (widthScaleFactor * rotationNinety[Y]);
+ float[] rearLeft = new float[3];
+ rearLeft[Z] = centre[Z];
+ rearLeft[X] = midRear[X] + (widthScaleFactor * rotationNinetyInverse[X]);
+ rearLeft[Y] = midRear[Y] + (widthScaleFactor * rotationNinetyInverse[Y]);
+ float[] rearRight = new float[3];
+ rearRight[Z] = centre[Z];
+ rearRight[X] = midRear[X] + (widthScaleFactor * rotationNinety[X]);
+ rearRight[Y] = midRear[Y] + (widthScaleFactor * rotationNinety[Y]);
+
+ rectangle.add(frontLeft);
+ rectangle.add(frontRight);
+ rectangle.add(rearRight);
+ rectangle.add(rearLeft);
+ return rectangle;
+ }
+
+ /**
+ * Check to see if a ring has been entered.
+ *
+ * @param location the location of the user to be tested.
+ */
+ public Ring hasRingBeenEntered(float[] location) {
+ float xDifference, yDifference, zDifference;
+ for (int i = 0; i < mRings.size(); i++) {
+ if (mRings.get(i).getPathNumber() == mCurrentLap) {
+ xDifference = Math.abs(mRings.get(i).getLocation()[X] - location[X]);
+ yDifference = Math.abs(mRings.get(i).getLocation()[Y] - location[Y]);
+ zDifference = Math.abs(mRings.get(i).getLocation()[Z] - location[Z]);
+ if (xDifference < 0.17 && yDifference < 0.17 && zDifference < 0.17) {
+ if (checkCollision(mRings.get(i), location)) {
+ return mRings.get(i);
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Calculates whether the location of the user is in the rectangular hit box or not.
+ *
+ * @param ring the ring to be tested.
+ * @param location the location of the user.
+ * @return true if the ring is entered and false if it is not.
+ */
+ private boolean checkCollision(Ring ring, float[] location) {
+ float[] rectangleVector1 = new float[2];
+ rectangleVector1[X] = ring.getRectangleHitBox().get(0)[X] - ring.getRectangleHitBox().get(3)[X];
+ rectangleVector1[Y] = ring.getRectangleHitBox().get(0)[Y] - ring.getRectangleHitBox().get(3)[Y];
+
+ float[] rectangleVector2 = new float[2];
+ rectangleVector2[X] = ring.getRectangleHitBox().get(2)[X] - ring.getRectangleHitBox().get(3)[X];
+ rectangleVector2[Y] = ring.getRectangleHitBox().get(2)[Y] - ring.getRectangleHitBox().get(3)[Y];
+
+ float[] locationVector = new float[2];
+ locationVector[X] = location[X] - ring.getRectangleHitBox().get(3)[X];
+ locationVector[Y] = location[Y] - ring.getRectangleHitBox().get(3)[Y];
+
+ if (dotProduct(rectangleVector1, locationVector, VECTOR_2D) > 0) {
+ if (dotProduct(rectangleVector1, rectangleVector1, VECTOR_2D)
+ > dotProduct(rectangleVector1, locationVector, VECTOR_2D)) {
+ if (dotProduct(rectangleVector2, locationVector, VECTOR_2D) > 0) {
+ if (dotProduct(rectangleVector2, rectangleVector2, VECTOR_2D)
+ > dotProduct(rectangleVector2, locationVector, VECTOR_2D)) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns the list of rings.
+ */
+ public ArrayList<Ring> getRings() {
+ return new ArrayList<>(mRings);
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Utils/Path/Path.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Utils/Path/Path.java
new file mode 100644
index 0000000..e0f21ab
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Utils/Path/Path.java
@@ -0,0 +1,126 @@
+/*
+ * 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 com.android.cts.verifier.sensors.sixdof.Utils.Path;
+
+import com.android.cts.verifier.sensors.sixdof.Utils.Manager;
+import com.android.cts.verifier.sensors.sixdof.Utils.MathsUtils;
+import com.android.cts.verifier.sensors.sixdof.Utils.Exceptions.WaypointAreaCoveredException;
+import com.android.cts.verifier.sensors.sixdof.Utils.Exceptions.WaypointDistanceException;
+import com.android.cts.verifier.sensors.sixdof.Utils.Exceptions.WaypointRingNotEnteredException;
+import com.android.cts.verifier.sensors.sixdof.Utils.Exceptions.WaypointStartPointException;
+import com.android.cts.verifier.sensors.sixdof.Utils.Path.PathUtilityClasses.Waypoint;
+
+import java.util.ArrayList;
+
+/**
+ * Contains all the information of the current path.
+ */
+public abstract class Path {
+ protected ArrayList<Waypoint> mCurrentPath = new ArrayList<>();
+ protected ArrayList<Waypoint> mPathMarkers = new ArrayList<>();
+
+ /**
+ * Creates a waypoint and adds it to the path.
+ *
+ * @param coordinates the coordinates to use for the waypoint.
+ * @param userGenerated indicates whether the data was user created or system created.
+ * @param currentLap the lap the data was created in.
+ * @throws WaypointDistanceException if the location is too close to another.
+ * @throws WaypointAreaCoveredException if the area covered by the user is too little.
+ * @throws WaypointStartPointException if the location is not close enough to the start.
+ * @throws WaypointRingNotEnteredException if a ring is not entered.
+ */
+ public void createWaypointAndAddToPath(
+ float[] coordinates, boolean userGenerated, Manager.Lap currentLap)
+ throws WaypointStartPointException, WaypointDistanceException,
+ WaypointAreaCoveredException, WaypointRingNotEnteredException {
+ if (userGenerated) {
+ additionalChecks(coordinates);
+ }
+ Waypoint waypoint = new Waypoint(coordinates, userGenerated, currentLap);
+ mCurrentPath.add(waypoint);
+ if (waypoint.isUserGenerated()) {
+ mPathMarkers.add(waypoint);
+ }
+ }
+
+ protected float getLengthOfCurrentPath() {
+ float length = 0.0f;
+
+ // Start at index 1.
+ for (int i = 1; i < mCurrentPath.size(); i++) {
+ float distance = MathsUtils.distanceCalculationOnXYPlane(
+ mCurrentPath.get(i).getCoordinates(),
+ mCurrentPath.get(i - 1).getCoordinates());
+ length += Math.abs(distance);
+ }
+
+ return length;
+ }
+
+ /**
+ * Abstract method used by classes that extend this one to run additional functionality.
+ *
+ * @param coordinates the coordinates for the waypoint.
+ * @throws WaypointDistanceException if the location is too close to another.
+ * @throws WaypointAreaCoveredException if the area covered by the user is too little.
+ * @throws WaypointStartPointException if the location is not close enough to the start.
+ * @throws WaypointRingNotEnteredException if a ring is not entered.
+ */
+ public abstract void additionalChecks(float[] coordinates)
+ throws WaypointStartPointException, WaypointDistanceException,
+ WaypointAreaCoveredException, WaypointRingNotEnteredException;
+
+ /**
+ * Removes the last maker in the current path.
+ *
+ * @return true of the first marker false if any other marker.
+ */
+ public boolean removeLastMarker() {
+ Waypoint markerToRemove = mPathMarkers.get(mPathMarkers.size() - 1);
+ mCurrentPath.remove(markerToRemove);
+ mPathMarkers.remove(markerToRemove);
+ return false;
+ }
+
+ /**
+ * Returns the current path.
+ */
+ public ArrayList<Waypoint> getCurrentPath() {
+ return new ArrayList<>(mCurrentPath);
+ }
+
+ /**
+ * Returns the markers for the current path.
+ */
+ public ArrayList<Waypoint> getPathMarkers() {
+ return new ArrayList<>(mPathMarkers);
+ }
+
+ /**
+ * Returns the size of the path.
+ */
+ public int getCurrentPathSize() {
+ return mCurrentPath.size();
+ }
+
+ /**
+ * Returns the number if markers.
+ */
+ public int getPathMarkersSize() {
+ return mPathMarkers.size();
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Utils/Path/PathUtilityClasses/Ring.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Utils/Path/PathUtilityClasses/Ring.java
new file mode 100644
index 0000000..636710e
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Utils/Path/PathUtilityClasses/Ring.java
@@ -0,0 +1,120 @@
+/*
+ * 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 com.android.cts.verifier.sensors.sixdof.Utils.Path.PathUtilityClasses;
+
+import com.android.cts.verifier.sensors.sixdof.Renderer.Renderable.RingRenderable;
+
+import java.util.ArrayList;
+
+/**
+ * Ring object, contains all the information about a ring.
+ */
+public class Ring {
+ private final float[] mLocation;
+ private final ArrayList<float[]> mRectangleHitBox;
+ private final float[] mRotation;
+ private final int mPathNumber;
+ private boolean mEntered;
+ private RingRenderable mRingRenderable;
+ private boolean mSoundPlayed = false;
+
+ /**
+ * Constructor to the ring. The ring is always initialised to not entered.
+ *
+ * @param location the location of the center of the ring
+ * @param pathNumber the path that the ring is located along
+ * @param rotation the orientation of the ring
+ * @param rectangleHitBox the four corners of the rectangular hit box covered by the ring in a
+ * top down view
+ */
+ public Ring(float[] location, int pathNumber,
+ float[] rotation, ArrayList<float[]> rectangleHitBox) {
+ mLocation = location;
+ mEntered = false;
+ mPathNumber = pathNumber;
+ mRotation = rotation;
+ mRectangleHitBox = rectangleHitBox;
+ mSoundPlayed = false;
+ }
+
+ /**
+ * Sets whether the ring has been entered or not.
+ *
+ * @param entered true if the ring is entered, false if the ring has not been entered
+ */
+ public void setEntered(boolean entered) {
+ mEntered = entered;
+ }
+
+ /**
+ * Sets whether the sound has been played or not.
+ *
+ * @param soundPlayed the state of whether the sound has been played or not
+ */
+ public void setSoundPlayed(boolean soundPlayed) {
+ mSoundPlayed = soundPlayed;
+ }
+
+ /**
+ * Returns the location if the center of the ring.
+ */
+ public float[] getLocation() {
+ return mLocation;
+ }
+
+ /**
+ * Returns the path the ring is located along.
+ */
+ public int getPathNumber() {
+ return mPathNumber;
+ }
+
+ /**
+ * Returns the coordinates of the four corners of the rectangular hit box.
+ */
+ public ArrayList<float[]> getRectangleHitBox() {
+ return new ArrayList<>(mRectangleHitBox);
+ }
+
+ /**
+ * Returns the orientation the ring is at.
+ */
+ public float[] getRingRotation() {
+ return mRotation;
+ }
+
+ /**
+ * Returns true if the ring had been entered, false if the ring has not been entered.
+ */
+ public boolean isEntered() {
+ return mEntered;
+ }
+
+ public RingRenderable getRingRenderable() {
+ return mRingRenderable;
+ }
+
+ /**
+ * Returns true if the sound has been played, false if the sound has not been played.
+ */
+ public boolean isSoundPlayed() {
+ return mSoundPlayed;
+ }
+
+ public void setRingRenderable(RingRenderable mRingRenderable) {
+ this.mRingRenderable = mRingRenderable;
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Utils/Path/PathUtilityClasses/RotationData.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Utils/Path/PathUtilityClasses/RotationData.java
new file mode 100644
index 0000000..45f080e
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Utils/Path/PathUtilityClasses/RotationData.java
@@ -0,0 +1,75 @@
+/*
+ * 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 com.android.cts.verifier.sensors.sixdof.Utils.Path.PathUtilityClasses;
+
+/**
+ * Contains information about a rotation.
+ */
+public class RotationData {
+ private final float mTargetRotation;
+ private final float mCurrentRotation;
+ private final boolean mRotationTestState;
+ private final float[] mLocation;
+ private final boolean mRotationState;
+
+ /**
+ * Constructor for this class.
+ *
+ * @param targetRotation The rotation to aim for
+ * @param currentRotation The current rotation on the device
+ * @param rotationTestState true of the currentRotation the same as the targetRotation, false if
+ * they are different
+ * @param location The location the rotation was made
+ * @param rotationState true if the rotation is testable, false if it is not
+ */
+ public RotationData(float targetRotation, float currentRotation,
+ boolean rotationTestState, float[] location, boolean rotationState) {
+ mTargetRotation = targetRotation;
+ mCurrentRotation = currentRotation;
+ mRotationTestState = rotationTestState;
+ mLocation = location;
+ mRotationState = rotationState;
+ }
+
+ /**
+ * Returns the rotation to aim for.
+ */
+ public float getTargetAngle() {
+ return mTargetRotation;
+ }
+
+ /**
+ * Returns the current rotation of the device.
+ */
+ public float getCurrentAngle() {
+ return mCurrentRotation;
+ }
+
+ /**
+ * Returns whether the rotation passed or failed.
+ */
+ public boolean getRotationTestState() {
+ return mRotationTestState;
+ }
+
+ /**
+ * Returns whether or not the rotation is testable.
+ */
+ public boolean getRotationState() {
+ return mRotationState;
+ }
+
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Utils/Path/PathUtilityClasses/Waypoint.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Utils/Path/PathUtilityClasses/Waypoint.java
new file mode 100644
index 0000000..e938009
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Utils/Path/PathUtilityClasses/Waypoint.java
@@ -0,0 +1,61 @@
+/*
+ * 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 com.android.cts.verifier.sensors.sixdof.Utils.Path.PathUtilityClasses;
+
+import com.android.cts.verifier.sensors.sixdof.Utils.Manager;
+
+/**
+ * Waypoint class used to give a waypoint a structure.
+ */
+public class Waypoint {
+ private final float[] mCoordinates;
+ private final boolean mUserGenerated;
+ private final Manager.Lap mLap;
+
+ /**
+ * Constructor for the class used to create the waypoint.
+ *
+ * @param coordinates the location of the new waypoint
+ * @param userGenerated indicates whether it is a marker or a path point
+ * @param lap the phase of the test the waypoint is in
+ */
+ public Waypoint(float[] coordinates, boolean userGenerated, Manager.Lap lap) {
+ this.mCoordinates = coordinates;
+ this.mUserGenerated = userGenerated;
+ this.mLap = lap;
+ }
+
+ /**
+ * Returns the mCoordinates of the waypoint.
+ */
+ public float[] getCoordinates() {
+ return mCoordinates;
+ }
+
+ /**
+ * Returns who placed the waypoint.
+ */
+ public boolean isUserGenerated() {
+ return mUserGenerated;
+ }
+
+ /**
+ * Returns the mLap the waypoint was placed on.
+ */
+ public Manager.Lap getLap() {
+ return mLap;
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Utils/Path/ReferencePath.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Utils/Path/ReferencePath.java
new file mode 100644
index 0000000..57b540e
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Utils/Path/ReferencePath.java
@@ -0,0 +1,174 @@
+/*
+ * 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 com.android.cts.verifier.sensors.sixdof.Utils.Path;
+
+import com.android.cts.verifier.sensors.sixdof.BuildConfig;
+import com.android.cts.verifier.sensors.sixdof.Utils.Manager;
+import com.android.cts.verifier.sensors.sixdof.Utils.MathsUtils;
+import com.android.cts.verifier.sensors.sixdof.Utils.Exceptions.WaypointAreaCoveredException;
+import com.android.cts.verifier.sensors.sixdof.Utils.Exceptions.WaypointDistanceException;
+import com.android.cts.verifier.sensors.sixdof.Utils.Exceptions.WaypointStartPointException;
+import com.android.cts.verifier.sensors.sixdof.Utils.Path.PathUtilityClasses.Waypoint;
+
+/**
+ * Class that deals with reference waypoints.
+ */
+public class ReferencePath extends Path {
+ private static final float FAILURE_TOLERANCE_PERCENTAGE = 0.025f; // 2.5%
+
+ // If in Debug mode, have values that are easier to pass tests with.
+ private static final float MINIMUM_DISTANCE_FROM_WAYPOINT = (BuildConfig.DEBUG ? 0f : 3f);
+ private static final float MINIMUM_AREA_OF_TRIANGLE = (BuildConfig.DEBUG ? 0f : 2f);
+ private static final float MINIMUM_PATH_DISTANCE = (BuildConfig.DEBUG ? 0f : 10f);
+
+ private float mFailureTolerance = 0.0f;
+
+ /**
+ * @param coordinates the coordinates to use for the waypoint.
+ * @throws WaypointDistanceException if the location is too close to another
+ * @throws WaypointAreaCoveredException if the area covered by the user is too little.
+ * @throws WaypointStartPointException if the location is not close enough to the start.
+ */
+ @Override
+ public void additionalChecks(float[] coordinates)
+ throws WaypointStartPointException, WaypointDistanceException,
+ WaypointAreaCoveredException {
+ testValidationSelection(coordinates);
+ }
+
+ /**
+ * Checks if the marker to remove is the first marker and removes all current path details.
+ *
+ * @return true of the first marker false if any other marker
+ */
+ @Override
+ public boolean removeLastMarker() {
+ if (mPathMarkers.size() == 1) {
+ mCurrentPath.clear();
+ mPathMarkers.clear();
+ return true;
+ } else {
+ return super.removeLastMarker();
+ }
+ }
+
+ /**
+ * Calculates the path that the user still has to travel.
+ *
+ * @return The distance the user still has to travel.
+ */
+ public float calculatePathRemaining() {
+ float distance, pathRemaining = 0;
+
+ int currentLocation = mCurrentPath.indexOf(mPathMarkers.get(mPathMarkers.size() - 1));
+
+ while (currentLocation < mCurrentPath.size() - 1) {
+ distance = MathsUtils.distanceCalculationOnXYPlane(
+ mCurrentPath.get(currentLocation).getCoordinates(),
+ mCurrentPath.get(currentLocation + 1).getCoordinates());
+ pathRemaining += distance;
+ currentLocation++;
+ }
+ pathRemaining = MINIMUM_PATH_DISTANCE - pathRemaining;
+ return pathRemaining;
+ }
+
+ /**
+ * Executes the validation tests for the given waypoint.
+ *
+ * @param coordinates the location of the point to perform validations on.
+ * @throws WaypointDistanceException if the location is too close to another.
+ * @throws WaypointAreaCoveredException if the area covered by the user is too little.
+ * @throws WaypointStartPointException if the location is not close enough to the start.
+ */
+ public void testValidationSelection(float[] coordinates) throws WaypointDistanceException,
+ WaypointAreaCoveredException, WaypointStartPointException {
+ if (mPathMarkers.size() < Manager.MAX_MARKER_NUMBER - 1) {
+ validateWaypointDistance(coordinates);
+ if (mPathMarkers.size() == 2) {
+ validateAreaCovered(coordinates);
+ }
+ } else if (mPathMarkers.size() == Manager.MAX_MARKER_NUMBER - 1) {
+ validateBackToStart(coordinates);
+ }
+ }
+
+ /**
+ * Checks to make sure the waypoints added are away from other waypoints.
+ *
+ * @param coordinates the location of the point to validate the distance.
+ * @throws WaypointDistanceException WaypointDistanceException if the location is too close to
+ * another.
+ */
+ private void validateWaypointDistance(float[] coordinates) throws WaypointDistanceException {
+ for (Waypoint waypoint : mPathMarkers) {
+ if (MathsUtils.distanceCalculationInXYZSpace(waypoint.getCoordinates(),
+ coordinates) < MINIMUM_DISTANCE_FROM_WAYPOINT) {
+ throw new WaypointDistanceException();
+ }
+ }
+ }
+
+ /**
+ * Checks to make sure enough distance is covered before adding the third waypoint.
+ *
+ * @param point3 the location used to validate the area.
+ * @throws WaypointAreaCoveredException if the area covered by the user is too little.
+ */
+ private void validateAreaCovered(float[] point3) throws WaypointAreaCoveredException {
+ float[] A = mPathMarkers.get(0).getCoordinates();
+ float[] B = mPathMarkers.get(1).getCoordinates();
+
+ /* The equation used to calculate the area is:
+ * area = 1/2|(Ax - Cx)*(By - Ay) - (Ax - Bx)*(Cy - Ay) */
+ try {
+ float part1 = (A[0] - point3[0]) * (B[1] - A[1]);
+ float part2 = (A[0] - B[0]) * (point3[1] - A[1]);
+ float area = 0.5f * Math.abs((part1 - part2));
+ if (area <= MINIMUM_AREA_OF_TRIANGLE) {
+ throw new WaypointAreaCoveredException();
+ }
+ } catch (ArrayIndexOutOfBoundsException e) {
+ throw new AssertionError(
+ "validateAreaCovered was given an array with a length less than 3", e);
+ }
+
+ }
+
+ /**
+ * Check the last waypoint of the first phase goes back to the start.
+ *
+ * @param coordinates the location of the point to validate the distance from the start marker.
+ * @throws WaypointStartPointException if the location is not close enough to the start.
+ */
+ private void validateBackToStart(float[] coordinates) throws WaypointStartPointException {
+ float[] firstMarkerCoordinates = mPathMarkers.get(0).getCoordinates();
+ float distance = MathsUtils.distanceCalculationInXYZSpace(
+ firstMarkerCoordinates, coordinates);
+
+ mFailureTolerance = FAILURE_TOLERANCE_PERCENTAGE * getLengthOfCurrentPath();
+
+ float maximumDistanceFromFirstWaypoint = (BuildConfig.DEBUG ? 1000f : mFailureTolerance);
+
+ if (distance > maximumDistanceFromFirstWaypoint) {
+ throw new WaypointStartPointException();
+ }
+ }
+
+ public float getFailureTolerance() {
+ return mFailureTolerance;
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Utils/Path/RobustnessPath.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Utils/Path/RobustnessPath.java
new file mode 100644
index 0000000..f4488be
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Utils/Path/RobustnessPath.java
@@ -0,0 +1,300 @@
+/*
+ * 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 com.android.cts.verifier.sensors.sixdof.Utils.Path;
+
+import com.android.cts.verifier.sensors.sixdof.Activities.TestActivity;
+import com.android.cts.verifier.sensors.sixdof.Utils.MathsUtils;
+import com.android.cts.verifier.sensors.sixdof.Utils.Exceptions.WaypointAreaCoveredException;
+import com.android.cts.verifier.sensors.sixdof.Utils.Exceptions.WaypointDistanceException;
+import com.android.cts.verifier.sensors.sixdof.Utils.Exceptions.WaypointStartPointException;
+import com.android.cts.verifier.sensors.sixdof.Utils.Path.PathUtilityClasses.RotationData;
+import com.android.cts.verifier.sensors.sixdof.Utils.Path.PathUtilityClasses.Waypoint;
+
+import android.opengl.Matrix;
+import java.util.ArrayList;
+import java.util.Timer;
+import java.util.TimerTask;
+
+/**
+ * Handles all the path properties of the robustness path.
+ */
+public class RobustnessPath extends Path {
+ public static final long TIME_TO_ADD_MARKER = 20000;
+ private static final int MAXIMUM_ROTATION_ANGLE = 40;
+ private static final int MINIMUM_ROTATION_ANGLE = MAXIMUM_ROTATION_ANGLE * -1;
+ private static final long TARGET_ROTATION_TIMER_INTERVALS = 100;
+ private static final float CHANGE_IN_ANGLE = 0.5f;
+ private static final int MAXIMUM_ROTATION_INACCURACY = 10;
+ private static final double DISTANCE_FROM_MARKER = 0.5F;
+
+
+ private static final float[] X_AXIS = new float[]{1, 0, 0, 0};
+
+ private float mTargetRotation = 0;
+ private boolean mRotationPhase = true;
+ private ArrayList<RotationData> mPathRotations = new ArrayList<>();
+ private ArrayList<Long> mMarkerTimeStamp = new ArrayList<>();
+ private float mDistanceOfPathFailedRotation = 0;
+
+ private int mOpenGlRotation = 0;
+
+ /**
+ * Constructor which starts the timer which changes the targetRotation.
+ */
+ public RobustnessPath(int openGlRotation) {
+ mOpenGlRotation = openGlRotation;
+ startChangingTargetRotation();
+ }
+
+ /**
+ * Performs robustness path related checks on a marker.
+ *
+ * @param coordinates the coordinates to use for the waypoint
+ * @throws WaypointDistanceException if the location is too close to another.
+ * @throws WaypointAreaCoveredException if the area covered by the user is too little.
+ * @throws WaypointStartPointException if the location is not close enough to the start.
+ */
+ @Override
+ public void additionalChecks(float[] coordinates)
+ throws WaypointStartPointException, WaypointDistanceException,
+ WaypointAreaCoveredException {
+ mMarkerTimeStamp.add(System.currentTimeMillis());
+ if (mPathMarkers.size() == 0) {
+ mTargetRotation = 0;
+ }
+ }
+
+ /**
+ * Starts a timer which changes the target rotation at specified intervals.
+ */
+ private void startChangingTargetRotation() {
+ Timer timer = new Timer();
+ timer.scheduleAtFixedRate(new TimerTask() {
+
+ @Override
+ public void run() {
+ synchronized (TestActivity.POSE_LOCK) {
+ setRotationToMake();
+ }
+ }
+ }, 0, TARGET_ROTATION_TIMER_INTERVALS);
+ }
+
+ /**
+ * Performs the change to the target rotation.
+ */
+ private void setRotationToMake() {
+ if (mRotationPhase) {
+ mTargetRotation = mTargetRotation - CHANGE_IN_ANGLE;
+ if (mTargetRotation <= MINIMUM_ROTATION_ANGLE) {
+ mRotationPhase = false;
+ }
+ } else {
+ mTargetRotation = mTargetRotation + CHANGE_IN_ANGLE;
+ if (mTargetRotation >= MAXIMUM_ROTATION_ANGLE) {
+ mRotationPhase = true;
+ }
+ }
+ }
+
+ /**
+ * Calculates the time left for the user to place the waypoint.
+ *
+ * @return the time left based on the current timestamp and the timestamp of the last marker.
+ */
+ public long calculateTimeRemaining() {
+ long timeRemaining;
+ if (!mMarkerTimeStamp.isEmpty()) {
+ int lastTimestamp = mMarkerTimeStamp.size() - 1;
+ timeRemaining = System.currentTimeMillis() - mMarkerTimeStamp.get(lastTimestamp);
+ return TIME_TO_ADD_MARKER - timeRemaining;
+ }
+ return TIME_TO_ADD_MARKER;
+ }
+
+ /**
+ * Converts the rotation from quaternion to euler.
+ *
+ * @param rotationQuaternion The quaternions of the current rotation.
+ * @return The euler rotation.
+ */
+ private float calculateRotation(float[] rotationQuaternion) {
+ float qx = rotationQuaternion[0];
+ float qy = rotationQuaternion[1];
+ float qz = rotationQuaternion[2];
+ float qw = rotationQuaternion[3];
+
+ // Set initial Vector to be -(X Axis).
+ double x = -X_AXIS[0];
+ double y = X_AXIS[1];
+ double z = X_AXIS[2];
+
+ // Create quaternion based rotation matrix and extract the values that we need.
+ final double X = x * (qy * qy + qx * qx - qz * qz - qw * qw)
+ + y * (2 * qy * qz - 2 * qx * qw)
+ + z * (2 * qy * qw + 2 * qx * qz);
+ final double Y = x * (2 * qx * qw + 2 * qy * qz)
+ + y * (qx * qx - qy * qy + qz * qz - qw * qw)
+ + z * (-2 * qx * qy + 2 * qz * qw);
+ final double Z = x * (-2 * qx * qz + 2 * qy * qw)
+ + y * (2 * qx * qy + 2 * qz * qw)
+ + z * (qx * qx - qy * qy - qz * qz + qw * qw);
+
+ // Invert X and Z axis.
+ float[] values = {(float) Z, (float) Y, (float) X, 0.0f};
+ MathsUtils.normalizeVector(values);
+
+ // Rotate the X axis based on the orientation of the device.
+ float[] adjustedXAxis = new float[4];
+ Matrix.multiplyMV(adjustedXAxis, 0, MathsUtils.getDeviceOrientationMatrix(mOpenGlRotation),
+ 0, X_AXIS, 0);
+
+ // Calculate angle between current pose and adjusted X axis.
+ double angle = Math.acos(MathsUtils.dotProduct(values, adjustedXAxis, MathsUtils.VECTOR_3D));
+
+ // Set our angle to be 0 based when upright.
+ angle = Math.toDegrees(angle) - MathsUtils.ORIENTATION_90_ANTI_CLOCKWISE;
+ angle *= -1;
+
+ return (float) angle;
+ }
+
+ /**
+ * Test the rotation and create a rotation object.
+ *
+ * @param rotationQuaternion The quaternions of the current rotation.
+ * @param rotationLocation The location of the point with the rotation.
+ * @param referencePathMarkers The list of markers in the reference path.
+ * @param maximumDistanceToFail The distance that auto fails the test.
+ * @return The rotation data if the rotation doesn't cause the test to be invalid, null if the
+ * rotation causes the rest to be invalid.
+ */
+ public RotationData handleRotation(float[] rotationQuaternion, float[] rotationLocation,
+ ArrayList<Waypoint> referencePathMarkers,
+ float maximumDistanceToFail) {
+ float eulerRotation = calculateRotation(rotationQuaternion);
+ boolean rotationTest = testRotation(eulerRotation, rotationLocation);
+ boolean rotationTestable = checkIfRotationTestable(rotationLocation, referencePathMarkers);
+ if (mDistanceOfPathFailedRotation > maximumDistanceToFail) {
+ return null;
+ } else {
+ return createRotation(eulerRotation, rotationTest, rotationLocation, rotationTestable);
+ }
+ }
+
+ /**
+ * Tests the current rotation against the target rotation.
+ *
+ * @param eulerRotation The rotation as a euler angle.
+ * @param rotationLocation The location of the current rotation.
+ * @return True if the rotation passes, and false if the rotation fails.
+ */
+ private boolean testRotation(double eulerRotation, float[] rotationLocation) {
+ boolean rotationTestState = true;
+ double rotationDifference = Math.abs(eulerRotation - mTargetRotation);
+ if (rotationDifference > MAXIMUM_ROTATION_INACCURACY) {
+ mDistanceOfPathFailedRotation += MathsUtils.distanceCalculationOnXYPlane(
+ rotationLocation, mCurrentPath.get(mCurrentPath.size() - 1).getCoordinates());
+ rotationTestState = false;
+ }
+ return rotationTestState;
+ }
+
+ /**
+ * Checks to make sure the rotation not close to other markers.
+ *
+ * @param rotationLocation The location of the point to validate the distance.
+ * @param referencePathMarkers The list of markers in the reference path.
+ * @return true if the location is not close to a marker, false if the location is close to a
+ * marker.
+ */
+ private boolean checkIfRotationTestable(
+ float[] rotationLocation, ArrayList<Waypoint> referencePathMarkers) {
+ for (Waypoint marker : referencePathMarkers) {
+ if (MathsUtils.distanceCalculationInXYZSpace(marker.getCoordinates(),
+ rotationLocation) < DISTANCE_FROM_MARKER) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Creates a rotation data object.
+ *
+ * @param currentRotation The rotation of the current point.
+ * @param rotationTestState Indicates whether the rotation fails or passes the test.
+ * @param rotationLocation The location of the current point.
+ * @param testableRotationState Indicates whether the rotation is valid for testing.
+ * @return Reference to the rotation data object which contains the rotation.
+ */
+ private RotationData createRotation(
+ float currentRotation, boolean rotationTestState, float[] rotationLocation,
+ boolean testableRotationState) {
+ RotationData rotationData = new RotationData(
+ mTargetRotation, currentRotation, rotationTestState, rotationLocation,
+ testableRotationState);
+ mPathRotations.add(rotationData);
+ return rotationData;
+ }
+
+ /**
+ * Returns the timestamps for the markers in the path.
+ */
+ public ArrayList<Long> getMarkerTimeStamp() {
+ return new ArrayList<>(mMarkerTimeStamp);
+ }
+
+ /**
+ * Returns the number of timestamps collected.
+ */
+ public int getMarkerTimeStampSize() {
+ return mMarkerTimeStamp.size();
+ }
+
+ /**
+ * Returns the rotations recorded for this path.
+ */
+ public int getRobustnessPathRotationsSize() {
+ return mPathRotations.size();
+ }
+
+ /**
+ * Returns the number of failed rotations.
+ */
+ public int getFailedRotationsSize() {
+ ArrayList<RotationData> failedRotations = new ArrayList<>();
+ for (RotationData rotationObject : mPathRotations) {
+ if (!rotationObject.getRotationTestState() && rotationObject.getRotationState()) {
+ failedRotations.add(rotationObject);
+ }
+ }
+ return failedRotations.size();
+ }
+
+ /**
+ * Returns the number of passed rotations.
+ */
+ public int getPassedRotationsSize() {
+ ArrayList<RotationData> passedRotations = new ArrayList<>();
+ for (RotationData rotationObject : mPathRotations) {
+ if (rotationObject.getRotationTestState() && rotationObject.getRotationState()) {
+ passedRotations.add(rotationObject);
+ }
+ }
+ return passedRotations.size();
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Utils/PoseProvider/AndroidPoseProvider.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Utils/PoseProvider/AndroidPoseProvider.java
new file mode 100644
index 0000000..6bf7a6d
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Utils/PoseProvider/AndroidPoseProvider.java
@@ -0,0 +1,70 @@
+/*
+ * 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 com.android.cts.verifier.sensors.sixdof.Utils.PoseProvider;
+
+import android.content.Context;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+
+/**
+ * Provides pose data using Android Sensors.
+ */
+public class AndroidPoseProvider extends PoseProvider {
+ private static final int SENSOR_TYPE_POSE = 26; //28;
+ private SensorManager mSensorManager;
+ private Sensor m6DoFSensor;
+
+ private SensorEventListener mSensorListener = new SensorEventListener() {
+ @Override
+ public void onSensorChanged(SensorEvent event) {
+ synchronized (POSE_LOCK) {
+ mLatestPoseData = new PoseData(event.values, event.timestamp);
+ }
+
+ onNewPoseData(mLatestPoseData);
+ }
+
+ @Override
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {
+ }
+ };
+
+ public AndroidPoseProvider(Context context, PoseProviderListener poseListener) {
+ super(context, poseListener);
+ mIntrinsics = new Intrinsics();
+ }
+
+ @Override
+ public void onStartPoseProviding() {
+ mSensorManager = (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE);
+
+ m6DoFSensor = mSensorManager.getDefaultSensor(SENSOR_TYPE_POSE);
+ mSensorManager.registerListener(mSensorListener, m6DoFSensor, SensorManager.SENSOR_DELAY_FASTEST);
+ }
+
+ @Override
+ public void onStopPoseProviding() {
+ mSensorManager.unregisterListener(mSensorListener);
+ }
+
+ @Override
+ public void setup() {
+ // Don't need to do anything here.
+ mPoseProviderListener.onSetupComplete();
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Utils/PoseProvider/Intrinsics.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Utils/PoseProvider/Intrinsics.java
new file mode 100644
index 0000000..f8992c86
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Utils/PoseProvider/Intrinsics.java
@@ -0,0 +1,69 @@
+/*
+ * 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 com.android.cts.verifier.sensors.sixdof.Utils.PoseProvider;
+
+/**
+ * Contains camera intrinsic information. Can be set manually or use some dummy values.
+ */
+public class Intrinsics {
+ private static final double DEFAULT_FOCAL_LENGTH = 3.5;
+ private static final double DEFAULT_SENSOR_WIDTH = 5.376;
+ private static final double DEFAULT_SENSOR_HEIGHT = 3.04;
+ /**
+ * Can any value other than 0 by default as they cancel each other out in the maths in
+ * ComplexMovementRenderer.calculateProjectionMatrix(..)
+ */
+ private static final int DEFAULT_WIDTH = 1;
+ /**
+ * Can any value other than 0 by default as they cancel each other out in the maths in
+ * ComplexMovementRenderer.calculateProjectionMatrix(..)
+ */
+ private static final int DEFAULT_HEIGHT = 1;
+
+ private int mWidth;
+ private int mHeight;
+ private double mFocalLengthInPixelsX;
+ private double mFocalLengthInPixelsY;
+
+ public Intrinsics() {
+ double focalLengthX = DEFAULT_FOCAL_LENGTH * DEFAULT_WIDTH / DEFAULT_SENSOR_WIDTH;
+ double focalLengthY = DEFAULT_FOCAL_LENGTH * DEFAULT_SENSOR_HEIGHT / DEFAULT_SENSOR_HEIGHT;
+ new Intrinsics(DEFAULT_WIDTH, DEFAULT_HEIGHT, focalLengthX, focalLengthY);
+ }
+
+ public Intrinsics(int width, int height, double focalLengthInPixelsX, double focalLengthInPixelsY) {
+ mWidth = width;
+ mHeight = height;
+ mFocalLengthInPixelsX = focalLengthInPixelsX;
+ mFocalLengthInPixelsY = focalLengthInPixelsY;
+ }
+
+ public int getWidth() {
+ return mWidth;
+ }
+
+ public int getHeight() {
+ return mHeight;
+ }
+
+ public double getFocalLengthInPixelsX() {
+ return mFocalLengthInPixelsX;
+ }
+
+ public double getFocalLengthInPixelsY() {
+ return mFocalLengthInPixelsY;
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Utils/PoseProvider/PoseData.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Utils/PoseProvider/PoseData.java
new file mode 100644
index 0000000..19ba4e7
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Utils/PoseProvider/PoseData.java
@@ -0,0 +1,106 @@
+/*
+ * 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 com.android.cts.verifier.sensors.sixdof.Utils.PoseProvider;
+
+public class PoseData {
+ /** Index of the X-value in the translation array. */
+ public static final int INDEX_TRANSLATION_X = 0;
+ /** Index of the Y-value in the translation array. */
+ public static final int INDEX_TRANSLATION_Y = 1;
+ /** Index of the Z-value in the translation array. */
+ public static final int INDEX_TRANSLATION_Z = 2;
+
+ /** Index of the quaternion X-value in the rotation array. */
+ public static final int INDEX_ROTATION_X = 0;
+ /** Index of the quaternion Y-value in the rotation array. */
+ public static final int INDEX_ROTATION_Y = 1;
+ /** Index of the quaternion Z-value in the rotation array. */
+ public static final int INDEX_ROTATION_Z = 2;
+ /** Index of the quaternion W-value in the rotation array. */
+ public static final int INDEX_ROTATION_W = 3;
+
+ public double timestamp = 0;
+
+ /**
+ * Orientation, as a quaternion, of the pose of the target frame with reference to to the base
+ * frame.
+ * <p>
+ * Specified as (x,y,z,w) where RotationAngle is in radians:
+ * <pre>
+ * x = RotationAxis.x * sin(RotationAngle / 2)
+ * y = RotationAxis.y * sin(RotationAngle / 2)
+ * z = RotationAxis.z * sin(RotationAngle / 2)
+ * w = cos(RotationAngle / 2)
+ * </pre>
+ */
+ public float mRotation[] = {
+ 0.0f, 0.0f, 0.0f, 1.0f };
+
+ /**
+ * Translation, ordered x, y, z, of the pose of the target frame relative to the reference
+ * frame.
+ */
+ public float mTranslation[] = {
+ 0.0f, 0.0f, 0.0f };
+
+ public PoseData(float[] sixDoFSensorValues, long timestamp){
+ this.timestamp = timestamp;
+ mRotation[0] = sixDoFSensorValues[0];
+ mRotation[1] = sixDoFSensorValues[1];
+ mRotation[2] = sixDoFSensorValues[2];
+ mRotation[3] = sixDoFSensorValues[3];
+ mTranslation[0] = sixDoFSensorValues[4];
+ mTranslation[1] = sixDoFSensorValues[5];
+ mTranslation[2] = sixDoFSensorValues[6];
+ }
+
+ public PoseData(float[] translation, float[] rotation, long timestamp){
+ this.timestamp = timestamp;
+ mRotation[0] = rotation[0];
+ mRotation[1] = rotation[1];
+ mRotation[2] = rotation[2];
+ mRotation[3] = rotation[3];
+ mTranslation[0] = translation[0];
+ mTranslation[1] = translation[1];
+ mTranslation[2] = translation[2];
+ }
+
+ /**
+ * Convenience function to get the rotation casted as an array of floats.
+ *
+ * @return The pose rotation.
+ */
+ public float[] getRotationAsFloats() {
+ float[] out = new float[4];
+ for (int i = 0; i < 4; i++) {
+ out[i] = mRotation[i];
+ }
+ return out;
+ }
+
+ /**
+ * Convenience function to get the translation casted as an array of floats.
+ *
+ * @return The pose translation.
+ */
+ public float[] getTranslationAsFloats() {
+ float[] out = new float[3];
+ for (int i = 0; i < 3; i++) {
+ out[i] = mTranslation[i];
+ }
+ return out;
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Utils/PoseProvider/PoseProvider.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Utils/PoseProvider/PoseProvider.java
new file mode 100644
index 0000000..5ec8ed3
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Utils/PoseProvider/PoseProvider.java
@@ -0,0 +1,64 @@
+/*
+ * 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 com.android.cts.verifier.sensors.sixdof.Utils.PoseProvider;
+
+import android.content.Context;
+
+/**
+ * Base class for objects that provide pose data to the app.
+ */
+public abstract class PoseProvider {
+ protected Context mContext;
+ protected PoseProviderListener mPoseProviderListener;
+
+ protected PoseData mLatestPoseData;
+ protected Intrinsics mIntrinsics;
+
+ public static final Object POSE_LOCK = new Object();
+
+ public interface PoseProviderListener {
+ void onSetupComplete();
+
+ void onNewPoseData(PoseData newPoseData);
+ }
+
+ public PoseProvider(Context context, PoseProviderListener listener) {
+ mContext = context;
+ mPoseProviderListener = listener;
+ }
+
+ public abstract void onStartPoseProviding();
+
+ public abstract void onStopPoseProviding();
+
+ public abstract void setup();
+
+ protected void onNewPoseData(PoseData newPoseData){
+ if (mPoseProviderListener != null) {
+ mPoseProviderListener.onNewPoseData(newPoseData);
+ }
+ }
+
+ public PoseData getLatestPoseData() {
+ synchronized (POSE_LOCK) {
+ return mLatestPoseData;
+ }
+ }
+
+ public Intrinsics getIntrinsics() {
+ return mIntrinsics;
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Utils/ReportExporter.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Utils/ReportExporter.java
new file mode 100644
index 0000000..add7a47
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Utils/ReportExporter.java
@@ -0,0 +1,109 @@
+/*
+ * 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 com.android.cts.verifier.sensors.sixdof.Utils;
+
+import com.android.cts.verifier.R;
+
+import android.content.Context;
+import android.os.AsyncTask;
+import android.os.Build;
+import android.os.Environment;
+import android.app.AlertDialog;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+
+/**
+ * Exports the xml report to a file.
+ */
+public class ReportExporter extends AsyncTask<Void, Void, String> {
+ public static final String REPORT_DIRECTORY = "6dofVerifierReports";
+ protected static final Logger LOG = Logger.getLogger(ReportExporter.class.getName());
+
+ private final Context mContext;
+ private final String mTestReport;
+
+ public ReportExporter(Context context, String report) {
+ this.mContext = context;
+ this.mTestReport = report;
+ }
+
+ @Override
+ protected String doInBackground(Void... params) {
+ if (!Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
+ LOG.log(Level.WARNING, "External storage is not writable.");
+ return mContext.getString(R.string.no_storage);
+ }
+ byte[] contents = mTestReport.getBytes();
+ String storagePath = System.getenv("EXTERNAL_STORAGE");
+ if (storagePath.length() == 0 || storagePath.equals("")) {
+ storagePath = Environment.getExternalStorageDirectory().getPath();
+ }
+ File reportPath = new File(storagePath, REPORT_DIRECTORY);
+ reportPath.mkdirs();
+
+ String baseName = getReportBaseName();
+ File reportFile = new File(reportPath, baseName + ".zip");
+ ZipOutputStream out = null;
+ try {
+ out = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(reportFile)));
+ ZipEntry entry = new ZipEntry(baseName + ".xml");
+ out.putNextEntry(entry);
+ out.write(contents);
+ } catch (IOException e) {
+ LOG.log(Level.WARNING, "I/O exception writing report to storage.", e);
+ return mContext.getString(R.string.no_storage);
+ } finally {
+ try {
+ if (out != null) {
+ out.close();
+ }
+ } catch (IOException e) {
+ LOG.log(Level.WARNING, "I/O exception closing report.", e);
+ }
+ }
+
+ return mContext.getString(R.string.report_saved, reportFile.getPath());
+ }
+
+ private String getReportBaseName() {
+ SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy.MM.dd-HH.mm.ss", Locale.ENGLISH);
+ String date = dateFormat.format(new Date());
+ return "6dofVerifierReport"
+ + "-" + date
+ + "-" + Build.MANUFACTURER
+ + "-" + Build.PRODUCT
+ + "-" + Build.DEVICE
+ + "-" + Build.ID;
+ }
+
+ @Override
+ protected void onPostExecute(String result) {
+ new AlertDialog.Builder(mContext)
+ .setMessage(result)
+ .setPositiveButton(android.R.string.ok, null)
+ .show();
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Utils/ResultObjects/ResultObject.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Utils/ResultObjects/ResultObject.java
new file mode 100644
index 0000000..8944fcb
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Utils/ResultObjects/ResultObject.java
@@ -0,0 +1,56 @@
+/*
+ * 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 com.android.cts.verifier.sensors.sixdof.Utils.ResultObjects;
+
+import com.android.cts.verifier.sensors.sixdof.Dialogs.AccuracyResultDialog;
+import com.android.cts.verifier.sensors.sixdof.Dialogs.BaseResultsDialog;
+
+import java.util.HashMap;
+
+/**
+ * Handles the results from the tests
+ */
+public class ResultObject {
+ private HashMap<BaseResultsDialog.ResultType, Boolean> mResults;
+
+ /**
+ * Constructor for this class.
+ *
+ * @param results List to indicate whether a test has failed or passed.
+ */
+ public ResultObject(HashMap<BaseResultsDialog.ResultType, Boolean> results) {
+ mResults = results;
+ }
+
+ /**
+ * Returns true if all tests pass and false for anything else.
+ */
+ public boolean hasPassed() {
+ for (Boolean result : mResults.values()) {
+ if (!result) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Returns List to indicate whether a test has failed or passed.
+ */
+ public HashMap<AccuracyResultDialog.ResultType, Boolean> getResults() {
+ return new HashMap<>(mResults);
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Utils/TestPhase/AccuracyTest.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Utils/TestPhase/AccuracyTest.java
new file mode 100644
index 0000000..43889b3
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Utils/TestPhase/AccuracyTest.java
@@ -0,0 +1,52 @@
+/*
+ * 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 com.android.cts.verifier.sensors.sixdof.Utils.TestPhase;
+
+import com.android.cts.verifier.sensors.sixdof.Utils.Manager;
+import com.android.cts.verifier.sensors.sixdof.Utils.TestReport;
+import com.android.cts.verifier.sensors.sixdof.Utils.Path.ReferencePath;
+
+import android.util.Log;
+
+/**
+ * Class to handle accuracy test specific operations.
+ */
+public class AccuracyTest extends Test {
+ private boolean resultsGiven = false;
+
+ /**
+ * Calls the constructor of the super class.
+ *
+ * @param referencePath Reference the the reference path.
+ * @param testReport The test report object to record the tests.
+ * @param manager The manager to call when the test is done.
+ */
+ public AccuracyTest(ReferencePath referencePath, TestReport testReport, Manager manager) {
+ super(referencePath, testReport, manager, "Accuracy Test");
+ }
+
+ /**
+ * Accuracy test specific checks. Checks to see if the test is done.
+ */
+ @Override
+ protected void runAdditionalMethods() {
+ if (mTestPath.getPathMarkersSize() == MAX_MARKER_NUMBER && !resultsGiven) {
+ Log.e("testPhase addPoseData:", "giving the results");
+ resultsGiven = true;
+ mManager.onAccuracyTestCompleted(super.executeTests(true, true));
+ }
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Utils/TestPhase/ComplexMovementTest.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Utils/TestPhase/ComplexMovementTest.java
new file mode 100644
index 0000000..47e8927
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Utils/TestPhase/ComplexMovementTest.java
@@ -0,0 +1,122 @@
+/*
+ * 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 com.android.cts.verifier.sensors.sixdof.Utils.TestPhase;
+
+import com.android.cts.verifier.sensors.sixdof.Dialogs.BaseResultsDialog;
+import com.android.cts.verifier.sensors.sixdof.Utils.Manager;
+import com.android.cts.verifier.sensors.sixdof.Utils.TestReport;
+import com.android.cts.verifier.sensors.sixdof.Utils.Path.ComplexMovementPath;
+import com.android.cts.verifier.sensors.sixdof.Utils.Path.ReferencePath;
+import com.android.cts.verifier.sensors.sixdof.Utils.Path.PathUtilityClasses.Ring;
+
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+/**
+ * Handles all the ComplexMovement test related features.
+ */
+public class ComplexMovementTest extends Test {
+ private boolean mResultsGiven = false;
+
+ /**
+ * Created a new ComplexMovement path which is to be used in this test.
+ *
+ * @param referencePath Reference the the reference path.
+ * @param testReport The test report object to record the tests.
+ * @param manager The manager to call when the test is done.
+ */
+ public ComplexMovementTest(ReferencePath referencePath, TestReport testReport, Manager manager) {
+ super(referencePath, testReport, manager, "Complex Movement Test");
+ mTestPath = new ComplexMovementPath(mReferencePathDistances, mReferencePath.getCurrentPath());
+ }
+
+ /**
+ * Implementation of the abstract method which check whether the test is complete.
+ */
+ @Override
+ protected void runAdditionalMethods() {
+ if (mTestPath.getPathMarkersSize() == MAX_MARKER_NUMBER && !mResultsGiven) {
+ mResultsGiven = true;
+ executeComplexMovementTests();
+ }
+ }
+
+ /**
+ * Starts the ComplexMovement tests.
+ */
+ private void executeComplexMovementTests() {
+ HashMap<BaseResultsDialog.ResultType, Boolean> complexMovementTestResults;
+ complexMovementTestResults = executeTests(true, false);
+ complexMovementTestResults.put(BaseResultsDialog.ResultType.RINGS, testRings());
+ mManager.onComplexMovementTestCompleted(complexMovementTestResults);
+ }
+
+ /**
+ * Tests whether the current location enters a ring.
+ *
+ * @param location the current location of the user
+ */
+ public void checkIfARingHasBeenPassed(float[] location) {
+ Ring ring = ((ComplexMovementPath) mTestPath).hasRingBeenEntered(location);
+ if (ring != null && !ring.isEntered()) {
+ // If ring has not already been entered.
+ mManager.ringEntered(ring);
+ ring.setEntered(true);
+ }
+ }
+
+ /**
+ * Finds the rings that have not been entered.
+ *
+ * @return true if all rings are entered and false if there is at least one ring not entered
+ */
+ public boolean testRings() {
+ ArrayList<Ring> testArray = ((ComplexMovementPath) mTestPath).getRings();
+ boolean state = true;
+ for (int i = 0; i < testArray.size(); i++) {
+ if (!testArray.get(i).isEntered()) {
+ recordRingTestResults(i);
+ state = false;
+ }
+ }
+ return state;
+ }
+
+ /**
+ * Forms a string for the failed ring and updates the test report with the string.
+ *
+ * @param ringIndex the index of the array the ring is in
+ */
+ private void recordRingTestResults(int ringIndex) {
+ Ring ring = ((ComplexMovementPath) mTestPath).getRings().get(ringIndex);
+ String testDetails =
+ "Ring Test: Ring was not entered. Path number: " + ring.getPathNumber() +
+ "Ring number:" + ((ringIndex % ComplexMovementPath.RINGS_PER_PATH) + 1) + "\n";
+ Log.e("Ring Result", testDetails);
+ mTestReport.setFailDetails(testDetails);
+
+ }
+
+ /**
+ * Returns the rings in the path.
+ */
+ public ArrayList<Ring> getRings() {
+ return ((ComplexMovementPath) mTestPath).getRings();
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Utils/TestPhase/RobustnessTest.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Utils/TestPhase/RobustnessTest.java
new file mode 100644
index 0000000..ae73922
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Utils/TestPhase/RobustnessTest.java
@@ -0,0 +1,209 @@
+/*
+ * 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 com.android.cts.verifier.sensors.sixdof.Utils.TestPhase;
+
+
+import com.android.cts.verifier.sensors.sixdof.Dialogs.BaseResultsDialog;
+import com.android.cts.verifier.sensors.sixdof.Utils.Manager;
+import com.android.cts.verifier.sensors.sixdof.Utils.MathsUtils;
+import com.android.cts.verifier.sensors.sixdof.Utils.TestReport;
+import com.android.cts.verifier.sensors.sixdof.Utils.Path.ReferencePath;
+import com.android.cts.verifier.sensors.sixdof.Utils.Path.RobustnessPath;
+import com.android.cts.verifier.sensors.sixdof.Utils.Path.PathUtilityClasses.RotationData;
+
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+/**
+ * Handles all the Robustness test related features.
+ */
+public class RobustnessTest extends Test {
+ private static final float MAXIMUM_PERCENT_ROTATION_FAILURE = 50f;
+ private boolean mResultsGiven = false;
+ private ArrayList<Long> mTimeDifferences = new ArrayList<>();
+ private float mDistanceOfPathToFail;
+
+ /**
+ * Created a new robustness path which is to be used in this test.
+ *
+ * @param referencePath Reference the the reference path.
+ * @param testReport The test report object to record the tests.
+ * @param manager The manager to call when the test is done.
+ */
+ public RobustnessTest(ReferencePath referencePath, TestReport testReport, Manager manager,
+ int openGlRotation) {
+ super(referencePath, testReport, manager, "Robustness Test");
+ mTestPath = new RobustnessPath(openGlRotation);
+ float mPathTotalDistance = 0;
+ for (float distance : mReferencePathDistances) {
+ mPathTotalDistance += distance;
+ }
+ mDistanceOfPathToFail = (MAXIMUM_PERCENT_ROTATION_FAILURE / 100f) * mPathTotalDistance;
+ }
+
+ /**
+ * Implementation of the abstract method which check whether the test is complete.
+ */
+ @Override
+ protected void runAdditionalMethods() {
+ if (mTestPath.getPathMarkersSize() == MAX_MARKER_NUMBER && !mResultsGiven) {
+ mResultsGiven = true;
+ executeRobustnessTests();
+ }
+ }
+
+ /**
+ * Starts the robustness tests.
+ */
+ private void executeRobustnessTests() {
+ HashMap<BaseResultsDialog.ResultType, Boolean> robustnessTestResults;
+ robustnessTestResults = executeTests(true, true);
+ robustnessTestResults.put(BaseResultsDialog.ResultType.TIME, timerTest());
+ robustnessTestResults.put(BaseResultsDialog.ResultType.ROTATION, rotationTest());
+ mManager.onRobustnessTestCompleted(robustnessTestResults);
+ }
+
+ /**
+ * Test to check whether the waypoint was placed in the appropriate time.
+ *
+ * @return true if all waypoint times were met, fail if a waypoint was placed after the time
+ * expired
+ */
+ private boolean timerTest() {
+ calculateTimeBetweenMarkers();
+ boolean state = true;
+ for (int i = 0; i < mTimeDifferences.size(); i++) {
+ if (mTimeDifferences.get(i) > RobustnessPath.TIME_TO_ADD_MARKER) {
+ recordTimerTestResults(i);
+ state = false;
+ }
+ }
+ return state;
+ }
+
+ /**
+ * Calculates the time it took to place a waypoint.
+ */
+ private void calculateTimeBetweenMarkers() {
+ long timeDifference;
+ ArrayList<Long> markerTimeStamps = ((RobustnessPath) mTestPath).getMarkerTimeStamp();
+ for (int i = 1; i < ((RobustnessPath) mTestPath).getMarkerTimeStampSize(); i++) {
+ timeDifference = markerTimeStamps.get(i) - markerTimeStamps.get(i - 1);
+ mTimeDifferences.add(timeDifference);
+ }
+ }
+
+ /**
+ * Formats the failed times into a string to add it to the test report.
+ *
+ * @param markerLocation The marker location which failed the test. Used to get the data needed
+ * for the test report
+ */
+ private void recordTimerTestResults(int markerLocation) {
+ long failedTime = mTimeDifferences.get(markerLocation);
+ String markerToPlace = MathsUtils.coordinatesToString(
+ mTestPath.getPathMarkers().get(markerLocation).getCoordinates());
+ String testDetails =
+ "Timer test: Marker placement was too slow that timer expired. Target time: "
+ + RobustnessPath.TIME_TO_ADD_MARKER / 1000 + " Completed time: " + Math.abs(failedTime) / 1000 +
+ " Marker: " + markerLocation + " Coordinates:" + markerToPlace + "\n";
+ Log.e("Timer Result", testDetails);
+ mTestReport.setFailDetails(testDetails);
+ }
+
+ /**
+ * Test to check whether the rotation test has passed based on the percent of failed rotations.
+ *
+ * @return true if the test passes, false if the test fails
+ */
+ private boolean rotationTest() {
+ float failedRotations = ((RobustnessPath) mTestPath).getFailedRotationsSize();
+ float totalRotations = ((RobustnessPath) mTestPath).getRobustnessPathRotationsSize();
+ float percentage = (failedRotations / totalRotations) * 100;
+ if (totalRotations == 0) {
+ Log.e("rotationResult", "Total was 0");
+ return false;
+ }
+ if (percentage > MAXIMUM_PERCENT_ROTATION_FAILURE) {
+ Log.d("rotationResult", "failed");
+ recordRotationTestResults(percentage, failedRotations, totalRotations);
+ return false;
+ } else {
+ Log.d("getFailedRotationSize", "" + failedRotations);
+ Log.d("total", "" + totalRotations);
+ Log.d("rotationResult", "passed ");
+ Log.d("rotationResult", "" + percentage);
+ return true;
+ }
+ }
+
+ /**
+ * Formats the failed rotations into a string to add it to the test report.
+ *
+ * @param percentFailed Percentage of failed rotations
+ * @param failedRotations number of failed rotations
+ * @param totalRotations number of rotations made
+ */
+ private void recordRotationTestResults(float percentFailed, float failedRotations, float totalRotations) {
+ String testDetails =
+ "Rotation test: Rotation fails were too great. Target rotation percent: "
+ + MAXIMUM_PERCENT_ROTATION_FAILURE + " GivenRotation percent: " + percentFailed +
+ " Failed rotation: " + failedRotations + " Total rotations:" + totalRotations + "\n";
+ Log.e("Timer Result", testDetails);
+ mTestReport.setFailDetails(testDetails);
+ }
+
+ /**
+ * gets the result of comparing the current rotation
+ *
+ * @param rotationQuaternion The quaternions of the current rotation
+ * @param location The location of the point with the rotation
+ * @return The rotation about the current rotation
+ */
+ public RotationData getRotationData(float[] rotationQuaternion, float[] location) {
+ RotationData rotation = ((RobustnessPath) mTestPath).handleRotation(
+ rotationQuaternion, location, mReferencePath.getPathMarkers(), mDistanceOfPathToFail);
+ if (rotation == null) {
+ if (!mResultsGiven) {
+ mResultsGiven = true;
+ HashMap<BaseResultsDialog.ResultType, Boolean> testFailed = new HashMap<>();
+ testFailed.put(BaseResultsDialog.ResultType.WAYPOINT, false);
+ testFailed.put(BaseResultsDialog.ResultType.PATH, false);
+ testFailed.put(BaseResultsDialog.ResultType.TIME, false);
+ testFailed.put(BaseResultsDialog.ResultType.ROTATION, false);
+ String testDetails = "Test terminated as it its impossible to pass the remaining rotations";
+ Log.e("Rotation test:", mDistanceOfPathToFail + "");
+ Log.e("Rotation test:", testDetails);
+ mTestReport.setFailDetails(testDetails);
+ mManager.onRobustnessTestCompleted(testFailed);
+ }
+ return null;
+ } else {
+ return rotation;
+ }
+
+ }
+
+ /**
+ * Returns the time remaining for the user to place the marker
+ */
+ public long getTimeRemaining() {
+ return ((RobustnessPath) mTestPath).calculateTimeRemaining();
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Utils/TestPhase/Test.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Utils/TestPhase/Test.java
new file mode 100644
index 0000000..f69c2a2
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Utils/TestPhase/Test.java
@@ -0,0 +1,279 @@
+/*
+ * 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 com.android.cts.verifier.sensors.sixdof.Utils.TestPhase;
+
+import com.android.cts.verifier.sensors.sixdof.Dialogs.BaseResultsDialog;
+import com.android.cts.verifier.sensors.sixdof.Utils.Manager;
+import com.android.cts.verifier.sensors.sixdof.Utils.MathsUtils;
+import com.android.cts.verifier.sensors.sixdof.Utils.TestReport;
+import com.android.cts.verifier.sensors.sixdof.Utils.Exceptions.WaypointAreaCoveredException;
+import com.android.cts.verifier.sensors.sixdof.Utils.Exceptions.WaypointDistanceException;
+import com.android.cts.verifier.sensors.sixdof.Utils.Exceptions.WaypointRingNotEnteredException;
+import com.android.cts.verifier.sensors.sixdof.Utils.Exceptions.WaypointStartPointException;
+import com.android.cts.verifier.sensors.sixdof.Utils.Path.AccuracyPath;
+import com.android.cts.verifier.sensors.sixdof.Utils.Path.Path;
+import com.android.cts.verifier.sensors.sixdof.Utils.Path.ReferencePath;
+import com.android.cts.verifier.sensors.sixdof.Utils.Path.PathUtilityClasses.Waypoint;
+
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+/**
+ * TestPhase generic class will be inherited by the other tests.
+ */
+public abstract class Test {
+ public static final int MAX_MARKER_NUMBER = 5;
+ private static final float FAILURE_TOLERANCE_PERCENTAGE = 0.025f; // 2.5%
+ private String mTestPhaseName;
+
+ protected ArrayList<Float> mMarkerAccuracy = new ArrayList<>();
+ protected ArrayList<Float> mPathAccuracy = new ArrayList<>();
+ protected ArrayList<Float> mReferencePathDistances = new ArrayList<>();
+ private ArrayList<Float> mTestPathDistances = new ArrayList<>();
+
+ protected ReferencePath mReferencePath;
+ protected Path mTestPath;
+ protected TestReport mTestReport;
+ protected Manager mManager;
+
+ /**
+ * Constructor for this class.
+ *
+ * @param referencePath Reference the the reference path.
+ * @param testReport The test report object to record the tests.
+ * @param manager The manager to call when the test is done.
+ */
+ public Test(ReferencePath referencePath, TestReport testReport, Manager manager, String testPhase) {
+ if (referencePath != null) {
+ mReferencePath = referencePath;
+ } else {
+ throw new AssertionError("TestPhase received a null referencePath", null);
+ }
+ mTestPhaseName = testPhase;
+ mTestReport = testReport;
+ mManager = manager;
+ mTestPath = new AccuracyPath();
+ mReferencePathDistances = calculatePathDistance(mReferencePath.getCurrentPath(), mReferencePath.getPathMarkers());
+ }
+
+ /**
+ * Adds the current waypoint to the test path.
+ *
+ * @param coordinates the coordinates to use for the waypoint.
+ * @param userGenerated indicates whether the data was user created or system created.
+ * @param currentLap the lap the data was created in.
+ * @throws WaypointDistanceException if the location is too close to another.
+ * @throws WaypointAreaCoveredException if the area covered by the user is too little.
+ * @throws WaypointStartPointException if the location is not close enough to the start.
+ */
+ public void addWaypointDataToPath(
+ float[] coordinates, boolean userGenerated, Manager.Lap currentLap)
+ throws WaypointAreaCoveredException, WaypointDistanceException,
+ WaypointStartPointException, WaypointRingNotEnteredException {
+ mTestPath.createWaypointAndAddToPath(coordinates, userGenerated, currentLap);
+ runAdditionalMethods();
+ }
+
+ /**
+ * Abstract method that is used but subclasses.
+ */
+ protected abstract void runAdditionalMethods();
+
+ /**
+ * Removes the last marker from the chosen lap.
+ *
+ * @return true of the first marker false if any other marker
+ */
+ public boolean removeLastAddedMarker() {
+ return mTestPath.removeLastMarker();
+ }
+
+ /**
+ * Performs the tests for this test phase.
+ *
+ * @return the state of the tests, true if they pass false if they fail.
+ */
+ protected HashMap<BaseResultsDialog.ResultType, Boolean> executeTests(boolean includeMarkerTest, boolean includePathTest) {
+ HashMap<BaseResultsDialog.ResultType, Boolean> testResults = new HashMap<>();
+ if (includePathTest) {
+ testResults.put(BaseResultsDialog.ResultType.PATH, pathTest());
+ }
+ if (includeMarkerTest) {
+ testResults.put(BaseResultsDialog.ResultType.WAYPOINT, markerTest());
+ }
+ return testResults;
+ }
+
+ /**
+ * Calculates the difference between the markers of the laps and executes the marker related
+ * test.
+ *
+ * @return true if the test passes and false if the rest fails.
+ */
+ private boolean markerTest() {
+ float distance;
+ for (int i = 0; i < mReferencePath.getPathMarkersSize(); i++) {
+ distance = MathsUtils.distanceCalculationInXYZSpace(
+ mReferencePath.getPathMarkers().get(i).getCoordinates(),
+ mTestPath.getPathMarkers().get(i).getCoordinates());
+ mMarkerAccuracy.add(distance);
+ }
+ return markerAccuracyTest();
+ }
+
+ /**
+ * Runs a check to find any markers that have failed the test and adds them to the test report.
+ *
+ * @return true if the test passes and false if the rest fails
+ */
+ private boolean markerAccuracyTest() {
+ boolean testState = true;
+ for (float markerDifference : mMarkerAccuracy) {
+ if (markerDifference > mReferencePath.getFailureTolerance()) {
+ recordMarkerTestResults(markerDifference);
+ testState = false;
+ }
+ }
+ return testState;
+ }
+
+ /**
+ * Formats the failed markers into a string to add it to the test report.
+ *
+ * @param markerDifference the difference which caused the marker to fail
+ */
+ private void recordMarkerTestResults(float markerDifference) {
+ int markerNumber = mMarkerAccuracy.indexOf(markerDifference);
+ String referenceMarker = MathsUtils.coordinatesToString(
+ mReferencePath.getPathMarkers().get(markerNumber).getCoordinates());
+ String testMarker = MathsUtils.coordinatesToString(
+ mTestPath.getPathMarkers().get(markerNumber).getCoordinates());
+ String testDetails = mTestPhaseName +
+ " Marker Accuracy: Distance between the markers too great. Marker: " + markerNumber +
+ " Difference: " + markerDifference +
+ " Coordinates " + referenceMarker + " " + testMarker + "\n";
+ Log.e("Marker Result", testDetails);
+ mTestReport.setFailDetails(testDetails);
+ }
+
+ /**
+ * Executes the the path related tests.
+ *
+ * @return true if the test passes, false if the test fails
+ */
+ private boolean pathTest() {
+ mTestPathDistances = calculatePathDistance(mTestPath.getCurrentPath(), mTestPath.getPathMarkers());
+ calculatePathDifferences();
+ return pathAccuracyTest();
+ }
+
+ /**
+ * Calculates the distance between the markers for the given path.
+ *
+ * @param pathToCalculate The path that we want to calculate the distances for
+ * @param markers The locations of the user generated markers in that path
+ * @return the list of distances for that path
+ */
+ protected ArrayList<Float> calculatePathDistance(ArrayList<Waypoint> pathToCalculate,
+ ArrayList<Waypoint> markers) {
+ ArrayList<Float> pathDistances = new ArrayList<>();
+ float totalDistance, distance;
+ int currentLocation = pathToCalculate.indexOf(markers.get(0));
+
+ while (currentLocation < pathToCalculate.size() - 1) {
+ totalDistance = 0;
+ do {
+ distance = MathsUtils.distanceCalculationOnXYPlane(
+ pathToCalculate.get(currentLocation).getCoordinates(),
+ pathToCalculate.get(currentLocation + 1).getCoordinates());
+ totalDistance += distance;
+ currentLocation++;
+ } while (!pathToCalculate.get(currentLocation).isUserGenerated());
+ pathDistances.add(Math.abs(totalDistance));
+ if (currentLocation == markers.size() - 1) {
+ break;
+ }
+ }
+ return pathDistances;
+ }
+
+ /**
+ * Calculates the difference between paths on different laps.
+ */
+ private void calculatePathDifferences() {
+ float difference;
+
+ if (!mReferencePathDistances.isEmpty() && !mTestPathDistances.isEmpty()) {
+ for (int i = 0; i < mReferencePathDistances.size(); i++) {
+ difference = mReferencePathDistances.get(i) - mTestPathDistances.get(i);
+ mPathAccuracy.add(Math.abs(difference));
+ }
+ } else {
+ throw new AssertionError("calculatePathDifference has one of the arrays empty", null);
+ }
+ }
+
+ /**
+ * Checks to see if any of the path differences have failed the test and adds them to the test
+ * report.
+ *
+ * @return True if the test passes and false if there is a fail
+ */
+ private boolean pathAccuracyTest() {
+ boolean testState = true;
+ for (float path : mPathAccuracy) {
+ if (path > mReferencePath.getFailureTolerance()) {
+ recordPathTestResults(path);
+ testState = false;
+ }
+ }
+ return testState;
+ }
+
+ /**
+ * Formats the failed paths into a string to add it to the test report.
+ *
+ * @param difference The distance that failed the test
+ */
+ private void recordPathTestResults(float difference) {
+ int pathNumber = mPathAccuracy.indexOf(difference);
+ String referencePath = String.valueOf(mReferencePathDistances.get(pathNumber));
+ String testPath = String.valueOf(mTestPathDistances.get(pathNumber));
+ String testDetails = mTestPhaseName +
+ " Path Length: Path length difference was too great. Path: " + pathNumber +
+ " Difference: " + difference +
+ " Paths: " + referencePath + " " + testPath + "\n";
+ Log.e("Path Result", testDetails);
+ mTestReport.setFailDetails(testDetails);
+ }
+
+ /**
+ * Returns the makers in the test path.
+ */
+ public ArrayList<Waypoint> getTestPathMarkers() {
+ return mTestPath.getPathMarkers();
+ }
+
+ /**
+ * Returns the size of the current path.
+ */
+ public int getTestPathMarkersSize() {
+ return mTestPath.getPathMarkers().size();
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Utils/TestReport.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Utils/TestReport.java
new file mode 100644
index 0000000..aeeb8b3
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Utils/TestReport.java
@@ -0,0 +1,167 @@
+/*
+ * 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 com.android.cts.verifier.sensors.sixdof.Utils;
+
+import android.content.Context;
+import android.os.Build;
+import android.util.Xml;
+
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.Locale;
+
+/**
+ * Handles all the XML to print to the user.
+ */
+public class TestReport {
+
+ public enum TestStatus {
+ NOT_EXECUTED,
+ EXECUTED,
+ PASS,
+ FAIL,
+ }
+
+ private static final int REPORT_VERSION = 1;
+ private static DateFormat DATE_FORMAT = new SimpleDateFormat(
+ "EEE MMM dd HH:mm:ss z yyyy", Locale.ENGLISH);
+ private static final String TEST_RESULTS_REPORT_TAG = "test-results-report";
+ private static final String VERIFIER_INFO_TAG = "verifier-info";
+ private static final String DEVICE_INFO_TAG = "device-info";
+ private static final String BUILD_INFO_TAG = "build-info";
+ private static final String TEST_RESULTS_TAG = "test-results";
+ private static final String TEST_TAG = "test";
+ private static final String TEST_DETAILS_TAG = "details";
+ private String mTestStatus = "not-executed";
+ private Context mContext;
+ private ArrayList<String> mTestDetails = new ArrayList<>();
+
+ /**
+ * Sets the context of this test.
+ *
+ * @param context reference to the activity this test is in.
+ */
+ public TestReport(Context context) {
+ mContext = context;
+ }
+
+ /**
+ * Produces the XML for the test.
+ *
+ * @return the XML of the test to display.
+ */
+ public String getContents()
+ throws IllegalArgumentException, IllegalStateException, IOException {
+
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+
+ XmlSerializer xml = Xml.newSerializer();
+
+ xml.setOutput(outputStream, "utf-8");
+ xml.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
+ xml.startDocument("utf-8", true);
+
+ xml.startTag(null, TEST_RESULTS_REPORT_TAG);
+ xml.attribute(null, "report-version", Integer.toString(REPORT_VERSION));
+ xml.attribute(null, "creation-time", DATE_FORMAT.format(new Date()));
+
+ xml.startTag(null, VERIFIER_INFO_TAG);
+ xml.attribute(null, "version-name", Version.getVersionName(mContext));
+ xml.attribute(null, "version-code", Integer.toString(Version.getVersionCode(mContext)));
+ xml.endTag(null, VERIFIER_INFO_TAG);
+
+ xml.startTag(null, DEVICE_INFO_TAG);
+ xml.startTag(null, BUILD_INFO_TAG);
+ xml.attribute(null, "board", Build.BOARD);
+ xml.attribute(null, "brand", Build.BRAND);
+ xml.attribute(null, "device", Build.DEVICE);
+ xml.attribute(null, "display", Build.DISPLAY);
+ xml.attribute(null, "fingerprint", Build.FINGERPRINT);
+ xml.attribute(null, "id", Build.ID);
+ xml.attribute(null, "model", Build.MODEL);
+ xml.attribute(null, "product", Build.PRODUCT);
+ xml.attribute(null, "release", Build.VERSION.RELEASE);
+ xml.attribute(null, "sdk", Integer.toString(Build.VERSION.SDK_INT));
+ xml.endTag(null, BUILD_INFO_TAG);
+ xml.endTag(null, DEVICE_INFO_TAG);
+
+ xml.startTag(null, TEST_RESULTS_TAG);
+ xml.startTag(null, TEST_TAG);
+ xml.attribute(null, "title", "6dof accuracy test");
+ xml.attribute(null, "class-name", "com.android.cts.verifier.sixdof.Activities.TestActivity");
+
+ if (mTestDetails.isEmpty()) {
+ xml.attribute(null, "result", mTestStatus);
+ } else {
+ setTestState(TestStatus.FAIL);
+ xml.attribute(null, "result", mTestStatus);
+ xml.startTag(null, TEST_DETAILS_TAG);
+
+ for (int i = 0; i < mTestDetails.size(); i++) {
+ xml.text(mTestDetails.get(i));
+ }
+
+ xml.endTag(null, TEST_DETAILS_TAG);
+ }
+
+ xml.endTag(null, TEST_TAG);
+ xml.endTag(null, TEST_RESULTS_TAG);
+
+ xml.endTag(null, TEST_RESULTS_REPORT_TAG);
+ xml.endDocument();
+
+ return outputStream.toString("utf-8");
+ }
+
+ /**
+ * Adds the failed results to the details.
+ *
+ * @param failedPart the failed test result.
+ */
+ public void setFailDetails(String failedPart) {
+ mTestDetails.add(failedPart);
+ }
+
+ /**
+ * Sets the status the test is currently in.
+ *
+ * @param state the status the test is in.
+ */
+ public void setTestState(TestStatus state) {
+ switch (state) {
+ case EXECUTED:
+ mTestStatus = "executed";
+ break;
+ case PASS:
+ mTestStatus = "passed";
+ break;
+ case FAIL:
+ mTestStatus = "failed";
+ break;
+ case NOT_EXECUTED:
+ mTestStatus = "not-executed";
+ break;
+ default:
+ throw new AssertionError("TestExecuted default we should not be in", null);
+ }
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Utils/Version.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Utils/Version.java
new file mode 100644
index 0000000..50af74f
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/sixdof/Utils/Version.java
@@ -0,0 +1,64 @@
+/*
+ * 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 com.android.cts.verifier.sensors.sixdof.Utils;
+
+/*
+ * Copyright (C) 2011 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.
+ */
+
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+
+/**
+ * Taken from Android CTS Verifier.
+ */
+public class Version {
+ static String getVersionName(Context context) {
+ return getVersionNameStrings(context)[0];
+ }
+
+ static int getVersionCode(Context context) {
+ return getPackageInfo(context).versionCode;
+ }
+
+ static private String[] getVersionNameStrings(Context context) {
+ return getPackageInfo(context).versionName.split(" ");
+ }
+
+ static PackageInfo getPackageInfo(Context context) {
+ try {
+ PackageManager packageManager = context.getPackageManager();
+ return packageManager.getPackageInfo(context.getPackageName(), 0);
+ } catch (NameNotFoundException e) {
+ throw new RuntimeException("Could not get find package information for "
+ + context.getPackageName());
+ }
+ }
+}
\ No newline at end of file
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/command/CompatibilityConsole.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/command/CompatibilityConsole.java
index d7fa7a7..98a63c2 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/command/CompatibilityConsole.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/command/CompatibilityConsole.java
@@ -151,7 +151,7 @@
helpBuilder.append(" help: show this message.\n");
helpBuilder.append(" help all: show the complete tradefed help.\n");
helpBuilder.append(" version: show the version.\n");
- helpBuilder.append(" exit: gracefully exit the compatibiltiy console, waiting until all ");
+ helpBuilder.append(" exit: gracefully exit the compatibility console, waiting until all ");
helpBuilder.append("invocations have completed.\n");
helpBuilder.append("Run:\n");
final String runPrompt = " run <plan> ";
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/Abi.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/Abi.java
deleted file mode 100644
index fab990e..0000000
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/Abi.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2015 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 com.android.compatibility.common.tradefed.testtype;
-
-import com.android.tradefed.testtype.IAbi;
-
-/**
- * A class representing an ABI.
- *
- * TODO(stuartscott): should be in TradeFed.
- */
-public class Abi implements IAbi {
-
- private final String mName;
- private final String mBitness;
-
- public Abi(String name, String bitness) {
- mName = name;
- mBitness = bitness;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public String getName() {
- return mName;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public String getBitness() {
- return mBitness;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public String toString() {
- return mName;
- }
-
-}
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/CompatibilityTest.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/CompatibilityTest.java
index e75131f..98bc29b 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/CompatibilityTest.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/CompatibilityTest.java
@@ -49,6 +49,7 @@
import com.android.tradefed.result.InputStreamSource;
import com.android.tradefed.result.LogDataType;
import com.android.tradefed.targetprep.ITargetPreparer;
+import com.android.tradefed.testtype.Abi;
import com.android.tradefed.testtype.IAbi;
import com.android.tradefed.testtype.IBuildReceiver;
import com.android.tradefed.testtype.IDeviceTest;
diff --git a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/ModuleDefTest.java b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/ModuleDefTest.java
index 2ab884e..7ea5ccd 100644
--- a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/ModuleDefTest.java
+++ b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/ModuleDefTest.java
@@ -21,6 +21,7 @@
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.result.ITestInvocationListener;
import com.android.tradefed.targetprep.ITargetPreparer;
+import com.android.tradefed.testtype.Abi;
import com.android.tradefed.testtype.IAbi;
import com.android.tradefed.testtype.IAbiReceiver;
import com.android.tradefed.testtype.IRemoteTest;
diff --git a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/ModuleRepoTest.java b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/ModuleRepoTest.java
index 24c7e3d..ecbab07 100644
--- a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/ModuleRepoTest.java
+++ b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/ModuleRepoTest.java
@@ -21,6 +21,7 @@
import com.android.compatibility.common.tradefed.testtype.IModuleDef;
import com.android.compatibility.common.util.AbiUtils;
import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.testtype.Abi;
import com.android.tradefed.testtype.IAbi;
import com.android.tradefed.testtype.IRemoteTest;
import com.android.tradefed.testtype.IShardableTest;
diff --git a/hostsidetests/compilation/src/android/cts/compilation/AdbRootDependentCompilationTest.java b/hostsidetests/compilation/src/android/cts/compilation/AdbRootDependentCompilationTest.java
index ab7dc43..24fad97 100644
--- a/hostsidetests/compilation/src/android/cts/compilation/AdbRootDependentCompilationTest.java
+++ b/hostsidetests/compilation/src/android/cts/compilation/AdbRootDependentCompilationTest.java
@@ -36,9 +36,17 @@
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import static com.google.common.base.Preconditions.checkNotNull;
+
/**
- * Tests that profile guided compilation succeeds regardless of whether a runtime
- * profile of the target application is present on the device.
+ * Various integration tests for dex to oat compilation, with or without profiles.
+ * When changing this test, make sure it still passes in each of the following
+ * configurations:
+ * <ul>
+ * <li>On a 'user' build</li>
+ * <li>On a 'userdebug' build with system property 'dalvik.vm.usejitprofiles' set to false</li>
+ * <li>On a 'userdebug' build with system property 'dalvik.vm.usejitprofiles' set to true</li>
+ * </ul>
*/
public class AdbRootDependentCompilationTest extends DeviceTestCase {
private static final String TAG = AdbRootDependentCompilationTest.class.getSimpleName();
@@ -66,8 +74,6 @@
private ITestDevice mDevice;
private byte[] profileBytes;
private File localProfileFile;
- private String odexFilePath;
- private byte[] initialOdexFileContents;
private File apkFile;
private boolean mIsRoot;
private boolean mNewlyObtainedRoot;
@@ -101,20 +107,6 @@
localProfileFile = File.createTempFile("compilationtest", "prof");
Files.write(profileBytes, localProfileFile);
assertTrue("empty profile", profileBytes.length > 0); // sanity check
-
- if (mIsRoot) {
- // ensure no profiles initially present
- for (ProfileLocation profileLocation : ProfileLocation.values()) {
- String clientPath = profileLocation.getPath();
- if (mDevice.doesFileExist(clientPath)) {
- executeAdbCommand(0, "shell", "rm", clientPath);
- }
- }
- executeCompile(/* force */ true);
- this.odexFilePath = getOdexFilePath();
- this.initialOdexFileContents = readFileOnClient(odexFilePath);
- assertTrue("empty odex file", initialOdexFileContents.length > 0); // sanity check
- }
}
@Override
@@ -128,6 +120,29 @@
super.tearDown();
}
+ /**
+ * Tests compilation using {@code -r bg-dexopt -f}.
+ */
+ public void testCompile_bgDexopt() throws Exception {
+ if (!canRunTest(EnumSet.noneOf(ProfileLocation.class))) {
+ return;
+ }
+ // Usually "interpret-only"
+ String expectedInstallFilter = checkNotNull(mDevice.getProperty("pm.dexopt.install"));
+ // Usually "speed-profile"
+ String expectedBgDexoptFilter = checkNotNull(mDevice.getProperty("pm.dexopt.bg-dexopt"));
+
+ String odexPath = getOdexFilePath();
+ assertEquals(expectedInstallFilter, getCompilerFilter(odexPath));
+
+ // Without -f, the compiler would only run if it judged the bg-dexopt filter to
+ // be "better" than the install filter. However manufacturers can change those
+ // values so we don't want to depend here on the resulting filter being better.
+ executeCompile("-r", "bg-dexopt", "-f");
+
+ assertEquals(expectedBgDexoptFilter, getCompilerFilter(odexPath));
+ }
+
/*
The tests below test the remaining combinations of the "ref" (reference) and
"cur" (current) profile being available. The "cur" profile gets moved/merged
@@ -140,53 +155,31 @@
profile_assistant, it may only be available in "ref".
*/
- public void testForceCompile_noProfile() throws Exception {
- Set<ProfileLocation> profileLocations = EnumSet.noneOf(ProfileLocation.class);
- if (!canRunTest(profileLocations)) {
- return;
- }
- compileWithProfilesAndCheckFilter(profileLocations);
- byte[] odexFileContents = readFileOnClient(odexFilePath);
- assertBytesEqual(initialOdexFileContents, odexFileContents);
+ public void testCompile_noProfile() throws Exception {
+ compileWithProfilesAndCheckFilter(false /* expectOdexChange */,
+ EnumSet.noneOf(ProfileLocation.class));
}
- public void testForceCompile_curProfile() throws Exception {
- Set<ProfileLocation> profileLocations = EnumSet.of(ProfileLocation.CUR);
- if (!canRunTest(profileLocations)) {
- return;
+ public void testCompile_curProfile() throws Exception {
+ boolean didRun = compileWithProfilesAndCheckFilter(true /* expectOdexChange */,
+ EnumSet.of(ProfileLocation.CUR));
+ if (didRun) {
+ assertTrue("ref profile should have been created by the compiler",
+ mDevice.doesFileExist(ProfileLocation.REF.getPath()));
}
- compileWithProfilesAndCheckFilter(profileLocations);
- assertTrue("ref profile should have been created by the compiler",
- mDevice.doesFileExist(ProfileLocation.REF.getPath()));
- assertFalse("odex compiled with cur profile should differ from the initial one without",
- Arrays.equals(initialOdexFileContents, readFileOnClient(odexFilePath)));
}
- public void testForceCompile_refProfile() throws Exception {
- Set<ProfileLocation> profileLocations = EnumSet.of(ProfileLocation.REF);
- if (!canRunTest(profileLocations)) {
- return;
- }
- compileWithProfilesAndCheckFilter(profileLocations);
+ public void testCompile_refProfile() throws Exception {
+ compileWithProfilesAndCheckFilter(false /* expectOdexChange */,
+ EnumSet.of(ProfileLocation.REF));
// We assume that the compiler isn't smart enough to realize that the
// previous odex was compiled before the ref profile was in place, even
// though theoretically it could be.
- byte[] odexFileContents = readFileOnClient(odexFilePath);
- assertBytesEqual(initialOdexFileContents, odexFileContents);
}
- public void testForceCompile_curAndRefProfile() throws Exception {
- Set<ProfileLocation> profileLocations = EnumSet.of(
- ProfileLocation.CUR, ProfileLocation.REF);
- if (!canRunTest(profileLocations)) {
- return;
- }
- compileWithProfilesAndCheckFilter(profileLocations);
- // We assume that the compiler isn't smart enough to realize that the
- // previous odex was compiled before the ref profile was in place, even
- // though theoretically it could be.
- byte[] odexFileContents = readFileOnClient(odexFilePath);
- assertBytesEqual(initialOdexFileContents, odexFileContents);
+ public void testCompile_curAndRefProfile() throws Exception {
+ compileWithProfilesAndCheckFilter(false /* expectOdexChange */,
+ EnumSet.of(ProfileLocation.CUR, ProfileLocation.REF));
}
private byte[] readFileOnClient(String clientPath) throws Exception {
@@ -204,29 +197,58 @@
/**
* Places {@link #profileBytes} in the specified locations, recompiles (without -f)
* and checks the compiler-filter in the odex file.
+ *
+ * @return whether the test ran (as opposed to early exit)
*/
- private void compileWithProfilesAndCheckFilter(Set<ProfileLocation> profileLocations)
+ private boolean compileWithProfilesAndCheckFilter(boolean expectOdexChange,
+ Set<ProfileLocation> profileLocations)
throws Exception {
+ if (!canRunTest(profileLocations)) {
+ return false;
+ }
+ // ensure no profiles initially present
+ for (ProfileLocation profileLocation : ProfileLocation.values()) {
+ String clientPath = profileLocation.getPath();
+ if (mDevice.doesFileExist(clientPath)) {
+ executeAdbCommand(0, "shell", "rm", clientPath);
+ }
+ }
+ executeCompile("-m", "speed-profile", "-f");
+ String odexFilePath = getOdexFilePath();
+ byte[] initialOdexFileContents = readFileOnClient(odexFilePath);
+ assertTrue("empty odex file", initialOdexFileContents.length > 0); // sanity check
+
for (ProfileLocation profileLocation : profileLocations) {
writeProfile(profileLocation);
}
- executeCompile(/* force */ false);
+ executeCompile("-m", "speed-profile");
// Confirm the compiler-filter used in creating the odex file
String compilerFilter = getCompilerFilter(odexFilePath);
assertEquals("compiler-filter", "speed-profile", compilerFilter);
+
+ byte[] odexFileContents = readFileOnClient(odexFilePath);
+ boolean odexChanged = !(Arrays.equals(initialOdexFileContents, odexFileContents));
+ if (odexChanged && !expectOdexChange) {
+ String msg = String.format(Locale.US, "Odex file without filters (%d bytes) "
+ + "unexpectedly different from odex file (%d bytes) compiled with filters: %s",
+ initialOdexFileContents.length, odexFileContents.length, profileLocations);
+ fail(msg);
+ } else if (!odexChanged && expectOdexChange) {
+ fail("odex file should have changed when recompiling with " + profileLocations);
+ }
+ return true;
}
/**
* Invokes the dex2oat compiler on the client.
+ *
+ * @param compileOptions extra options to pass to the compiler on the command line
*/
- private void executeCompile(boolean force) throws Exception {
- List<String> command = new ArrayList<>(Arrays.asList("shell", "cmd", "package", "compile",
- "-m", "speed-profile"));
- if (force) {
- command.add("-f");
- }
+ private void executeCompile(String... compileOptions) throws Exception {
+ List<String> command = new ArrayList<>(Arrays.asList("shell", "cmd", "package", "compile"));
+ command.addAll(Arrays.asList(compileOptions));
command.add(APPLICATION_PACKAGE);
String[] commandArray = command.toArray(new String[0]);
assertEquals("Success", executeAdbCommand(1, commandArray)[0]);
@@ -292,8 +314,8 @@
}
/**
- * Returns whether a test can run in the current device configuration
- * and for the given profileLocations. This allows tests to exit early.
+ * Returns whether a test that uses the given profileLocations can run
+ * in the current device configuration. This allows tests to exit early.
*
* <p>Ideally we'd like tests to be marked as skipped/ignored or similar
* rather than passing if they can't run on the current device, but that
@@ -332,10 +354,4 @@
String[] lines = output.equals("") ? new String[0] : output.split("\n");
return lines;
}
-
- private static void assertBytesEqual(byte[] expected, byte[] actual) {
- String msg = String.format("Expected %d bytes differ from actual %d bytes",
- expected.length, actual.length);
- assertTrue(msg, Arrays.equals(expected, actual));
- }
}
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/api23/Android.mk b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/api23/Android.mk
index b1c35aa..5d32955 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/api23/Android.mk
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/api23/Android.mk
@@ -26,7 +26,7 @@
LOCAL_STATIC_JAVA_LIBRARIES = android-support-v4 ctsdeviceutil ctstestrunner ub-uiautomator
-LOCAL_SDK_VERSION := current
+LOCAL_SDK_VERSION := test_current
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/../res
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/latest/Android.mk b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/latest/Android.mk
index 7d08bc3..884bb0b 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/latest/Android.mk
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/latest/Android.mk
@@ -26,7 +26,7 @@
LOCAL_STATIC_JAVA_LIBRARIES = android-support-v4 ctsdeviceutil ctstestrunner ub-uiautomator
-LOCAL_SDK_VERSION := current
+LOCAL_SDK_VERSION := test_current
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/../res
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/latest/AndroidManifest.xml b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/latest/AndroidManifest.xml
index 744db8f..1c9d24d 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/latest/AndroidManifest.xml
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/latest/AndroidManifest.xml
@@ -25,7 +25,8 @@
<uses-permission android:name="android.permission.SET_WALLPAPER" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
- <application
+ <!-- Add a network security config that trusts user added CAs for tests -->
+ <application android:networkSecurityConfig="@xml/network_security_config"
android:testOnly="true">
<uses-library android:name="android.test.runner" />
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/res/xml/network_security_config.xml b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/res/xml/network_security_config.xml
similarity index 100%
rename from hostsidetests/devicepolicy/app/DeviceOwner/res/xml/network_security_config.xml
rename to hostsidetests/devicepolicy/app/DeviceAndProfileOwner/res/xml/network_security_config.xml
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/AlwaysOnVpnTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/AlwaysOnVpnTest.java
index 34566a1..01a5a3a 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/AlwaysOnVpnTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/AlwaysOnVpnTest.java
@@ -17,6 +17,7 @@
package com.android.cts.deviceandprofileowner;
import android.os.Bundle;
+import android.os.UserManager;
import com.android.cts.deviceandprofileowner.vpn.VpnTestHelper;
@@ -63,6 +64,18 @@
VpnTestHelper.checkPing(TEST_ADDRESS);
}
+ public void testDisallowConfigVpn() throws Exception {
+ mDevicePolicyManager.addUserRestriction(
+ ADMIN_RECEIVER_COMPONENT, UserManager.DISALLOW_CONFIG_VPN);
+ try {
+ testAlwaysOnVpn();
+ } finally {
+ // clear the user restriction
+ mDevicePolicyManager.clearUserRestriction(ADMIN_RECEIVER_COMPONENT,
+ UserManager.DISALLOW_CONFIG_VPN);
+ }
+ }
+
public void testAllowedApps() throws Exception {
final Bundle restrictions = new Bundle();
restrictions.putStringArray(RESTRICTION_ALLOWED, new String[] {mPackageName});
@@ -97,4 +110,3 @@
assertNull(mDevicePolicyManager.getAlwaysOnVpnPackage(ADMIN_RECEIVER_COMPONENT));
}
}
-
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/CaCertManagementTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/CaCertManagementTest.java
similarity index 83%
rename from hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/CaCertManagementTest.java
rename to hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/CaCertManagementTest.java
index 3e692c3..54222035 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/CaCertManagementTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/CaCertManagementTest.java
@@ -13,10 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.cts.deviceowner;
+package com.android.cts.deviceandprofileowner;
+import android.content.ComponentName;
import android.net.http.X509TrustManagerExtensions;
-import android.security.NetworkSecurityPolicy;
import java.io.ByteArrayInputStream;
import java.security.GeneralSecurityException;
@@ -32,17 +32,19 @@
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
-import static com.android.cts.deviceowner.FakeKeys.FAKE_DSA_1;
-import static com.android.cts.deviceowner.FakeKeys.FAKE_RSA_1;
+import static android.cts.util.FakeKeys.FAKE_DSA_1;
+import static android.cts.util.FakeKeys.FAKE_RSA_1;
-public class CaCertManagementTest extends BaseDeviceOwnerTest {
+public class CaCertManagementTest extends BaseDeviceAdminTest {
+ private final ComponentName mAdmin = ADMIN_RECEIVER_COMPONENT;
+
/**
* Test: device admins should be able to list all installed certs.
*
* <p>The list of certificates must never be {@code null}.
*/
public void testCanRetrieveListOfInstalledCaCerts() {
- List<byte[]> caCerts = mDevicePolicyManager.getInstalledCaCerts(getWho());
+ List<byte[]> caCerts = mDevicePolicyManager.getInstalledCaCerts(mAdmin);
assertNotNull(caCerts);
}
@@ -54,11 +56,11 @@
assertUninstalled(FAKE_RSA_1.caCertificate);
assertUninstalled(FAKE_DSA_1.caCertificate);
- assertTrue(mDevicePolicyManager.installCaCert(getWho(), FAKE_RSA_1.caCertificate));
+ assertTrue(mDevicePolicyManager.installCaCert(mAdmin, FAKE_RSA_1.caCertificate));
assertInstalled(FAKE_RSA_1.caCertificate);
assertUninstalled(FAKE_DSA_1.caCertificate);
- mDevicePolicyManager.uninstallCaCert(getWho(), FAKE_RSA_1.caCertificate);
+ mDevicePolicyManager.uninstallCaCert(mAdmin, FAKE_RSA_1.caCertificate);
assertUninstalled(FAKE_RSA_1.caCertificate);
assertUninstalled(FAKE_DSA_1.caCertificate);
}
@@ -68,14 +70,14 @@
*/
public void testUninstallationIsSelective()
throws CertificateException, GeneralSecurityException {
- assertTrue(mDevicePolicyManager.installCaCert(getWho(), FAKE_RSA_1.caCertificate));
- assertTrue(mDevicePolicyManager.installCaCert(getWho(), FAKE_DSA_1.caCertificate));
+ assertTrue(mDevicePolicyManager.installCaCert(mAdmin, FAKE_RSA_1.caCertificate));
+ assertTrue(mDevicePolicyManager.installCaCert(mAdmin, FAKE_DSA_1.caCertificate));
- mDevicePolicyManager.uninstallCaCert(getWho(), FAKE_DSA_1.caCertificate);
+ mDevicePolicyManager.uninstallCaCert(mAdmin, FAKE_DSA_1.caCertificate);
assertInstalled(FAKE_RSA_1.caCertificate);
assertUninstalled(FAKE_DSA_1.caCertificate);
- mDevicePolicyManager.uninstallCaCert(getWho(), FAKE_RSA_1.caCertificate);
+ mDevicePolicyManager.uninstallCaCert(mAdmin, FAKE_RSA_1.caCertificate);
}
/**
@@ -84,10 +86,10 @@
*/
public void testCanUninstallAllUserCaCerts()
throws CertificateException, GeneralSecurityException {
- assertTrue(mDevicePolicyManager.installCaCert(getWho(), FAKE_RSA_1.caCertificate));
- assertTrue(mDevicePolicyManager.installCaCert(getWho(), FAKE_DSA_1.caCertificate));
+ assertTrue(mDevicePolicyManager.installCaCert(mAdmin, FAKE_RSA_1.caCertificate));
+ assertTrue(mDevicePolicyManager.installCaCert(mAdmin, FAKE_DSA_1.caCertificate));
- mDevicePolicyManager.uninstallAllUserCaCerts(getWho());
+ mDevicePolicyManager.uninstallAllUserCaCerts(mAdmin);
assertUninstalled(FAKE_RSA_1.caCertificate);
assertUninstalled(FAKE_DSA_1.caCertificate);
}
@@ -131,17 +133,15 @@
*/
private boolean isCaCertInstalledAndTrusted(Certificate caCert)
throws GeneralSecurityException, CertificateException {
- boolean installed = mDevicePolicyManager.hasCaCertInstalled(getWho(), caCert.getEncoded());
+ boolean installed = mDevicePolicyManager.hasCaCertInstalled(mAdmin, caCert.getEncoded());
boolean listed = false;
- for (byte[] certBuffer : mDevicePolicyManager.getInstalledCaCerts(getWho())) {
+ for (byte[] certBuffer : mDevicePolicyManager.getInstalledCaCerts(mAdmin)) {
if (caCert.equals(readCertificate(certBuffer))) {
listed = true;
}
}
- NetworkSecurityPolicy.getInstance().handleTrustStorageUpdate();
-
// Verify that the user added CA is reflected in the default X509TrustManager.
final TrustManagerFactory tmf =
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
@@ -153,7 +153,7 @@
boolean userAddedCertificate = xtm.isUserAddedCertificate((X509Certificate) caCert);
// All three responses should match - if an installed certificate isn't trusted or (worse)
- // a trusted certificate isn't even installed we should
+ // a trusted certificate isn't even installed we should fail now, loudly.
assertEquals(installed, listed);
assertEquals(installed, trusted);
assertEquals(installed, userAddedCertificate);
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/vpn/VpnTestHelper.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/vpn/VpnTestHelper.java
index 3072251..41bb626 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/vpn/VpnTestHelper.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/vpn/VpnTestHelper.java
@@ -65,7 +65,6 @@
private static final int ICMP_ECHO_REQUEST = 0x08;
private static final int ICMP_ECHO_REPLY = 0x00;
private static final int NETWORK_TIMEOUT_MS = 5000;
- private static final int NETWORK_SETTLE_GRACE_MS = 100;
private static final ComponentName ADMIN_RECEIVER_COMPONENT =
BaseDeviceAdminTest.ADMIN_RECEIVER_COMPONENT;
@@ -93,8 +92,6 @@
if (!vpnLatch.await(NETWORK_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
fail("Took too long waiting to establish a VPN-backed connection");
}
- // Give the VPN a moment to start transmitting data.
- Thread.sleep(NETWORK_SETTLE_GRACE_MS);
} catch (InterruptedException | PackageManager.NameNotFoundException e) {
fail("Failed to send ping: " + e);
} finally {
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/Android.mk b/hostsidetests/devicepolicy/app/DeviceOwner/Android.mk
index 18d1b8b..197f5a7 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/Android.mk
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/Android.mk
@@ -26,7 +26,7 @@
LOCAL_JAVA_LIBRARIES := android.test.runner cts-junit
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner compatibility-device-util
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner ctsdeviceutil compatibility-device-util
LOCAL_SDK_VERSION := test_current
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/AndroidManifest.xml b/hostsidetests/devicepolicy/app/DeviceOwner/AndroidManifest.xml
index 64d4079..07d006c 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/AndroidManifest.xml
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/AndroidManifest.xml
@@ -25,8 +25,7 @@
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
- <!-- Add a network security config that trusts user added CAs for tests -->
- <application android:networkSecurityConfig="@xml/network_security_config"
+ <application
android:testOnly="true">
<uses-library android:name="android.test.runner" />
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/KeyManagementTest.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/KeyManagementTest.java
index 219dfc2..cc4a8b0 100755
--- a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/KeyManagementTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/KeyManagementTest.java
@@ -15,8 +15,8 @@
*/
package com.android.cts.deviceowner;
+import static android.cts.util.FakeKeys.FAKE_RSA_1;
import static com.android.cts.deviceowner.BaseDeviceOwnerTest.getWho;
-import static com.android.cts.deviceowner.FakeKeys.FAKE_RSA_1;
import android.app.Activity;
import android.app.admin.DevicePolicyManager;
diff --git a/hostsidetests/devicepolicy/app/LauncherTests/Android.mk b/hostsidetests/devicepolicy/app/LauncherTests/Android.mk
index eb4a091..19fd3bd 100644
--- a/hostsidetests/devicepolicy/app/LauncherTests/Android.mk
+++ b/hostsidetests/devicepolicy/app/LauncherTests/Android.mk
@@ -26,7 +26,7 @@
LOCAL_JAVA_LIBRARIES := android.test.runner cts-junit
-LOCAL_STATIC_JAVA_LIBRARIES = android-support-v4 ctstestrunner
+LOCAL_STATIC_JAVA_LIBRARIES = android-support-v4 ctstestrunner android-support-test
LOCAL_SDK_VERSION := current
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/Android.mk b/hostsidetests/devicepolicy/app/ManagedProfile/Android.mk
index 85c74f9..7003012 100644
--- a/hostsidetests/devicepolicy/app/ManagedProfile/Android.mk
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/Android.mk
@@ -27,7 +27,7 @@
LOCAL_JAVA_LIBRARIES := android.test.runner cts-junit
LOCAL_STATIC_JAVA_LIBRARIES = android-support-v4 ctstestrunner compatibility-device-util \
- ub-uiautomator
+ ub-uiautomator android-support-test
LOCAL_SDK_VERSION := current
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
index 7b40ff1..c2a9bc1 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
@@ -110,6 +110,13 @@
super.tearDown();
}
+ public void testCaCertManagement() throws Exception {
+ if (!mHasFeature) {
+ return;
+ }
+ executeDeviceTestClass(".CaCertManagementTest");
+ }
+
public void testResetPassword() throws Exception {
if (!mHasFeature) {
return;
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
index 4748b35..a877525 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
@@ -84,10 +84,6 @@
super.tearDown();
}
- public void testCaCertManagement() throws Exception {
- executeDeviceOwnerTest("CaCertManagementTest");
- }
-
public void testDeviceOwnerSetup() throws Exception {
executeDeviceOwnerTest("DeviceOwnerSetupTest");
}
diff --git a/hostsidetests/net/aidl/Android.mk b/hostsidetests/net/aidl/Android.mk
index a7ec6ef..4aa55b6 100644
--- a/hostsidetests/net/aidl/Android.mk
+++ b/hostsidetests/net/aidl/Android.mk
@@ -19,4 +19,4 @@
LOCAL_SDK_VERSION := current
LOCAL_SRC_FILES := com/android/cts/net/hostside/IRemoteSocketFactory.aidl
LOCAL_MODULE := CtsHostsideNetworkTestsAidl
-include $(BUILD_JAVA_LIBRARY)
+include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/hostsidetests/numberblocking/app/Android.mk b/hostsidetests/numberblocking/app/Android.mk
index 492a2ec..5755f84 100644
--- a/hostsidetests/numberblocking/app/Android.mk
+++ b/hostsidetests/numberblocking/app/Android.mk
@@ -27,7 +27,7 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner android-support-test
# tag this module as a cts test artifact
LOCAL_COMPATIBILITY_SUITE := cts
diff --git a/hostsidetests/services/activityandwindowmanager/Android.mk b/hostsidetests/services/activityandwindowmanager/Android.mk
new file mode 100644
index 0000000..178cb8a
--- /dev/null
+++ b/hostsidetests/services/activityandwindowmanager/Android.mk
@@ -0,0 +1,17 @@
+#
+# Copyright (C) 2015 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.
+#
+
+include $(call all-subdir-makefiles)
diff --git a/hostsidetests/services/activitymanager/Android.mk b/hostsidetests/services/activityandwindowmanager/activitymanager/Android.mk
similarity index 96%
rename from hostsidetests/services/activitymanager/Android.mk
rename to hostsidetests/services/activityandwindowmanager/activitymanager/Android.mk
index 0cf1f2b..972e747 100644
--- a/hostsidetests/services/activitymanager/Android.mk
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/Android.mk
@@ -24,6 +24,7 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_JAVA_LIBRARIES := cts-tradefed tradefed-prebuilt
+LOCAL_STATIC_JAVA_LIBRARIES := cts-amwm-util
LOCAL_CTS_TEST_PACKAGE := android.server
diff --git a/hostsidetests/services/activitymanager/AndroidTest.xml b/hostsidetests/services/activityandwindowmanager/activitymanager/AndroidTest.xml
similarity index 100%
rename from hostsidetests/services/activitymanager/AndroidTest.xml
rename to hostsidetests/services/activityandwindowmanager/activitymanager/AndroidTest.xml
diff --git a/hostsidetests/services/activitymanager/OldAndroidTest.xml b/hostsidetests/services/activityandwindowmanager/activitymanager/OldAndroidTest.xml
similarity index 100%
rename from hostsidetests/services/activitymanager/OldAndroidTest.xml
rename to hostsidetests/services/activityandwindowmanager/activitymanager/OldAndroidTest.xml
diff --git a/hostsidetests/services/activitymanager/app/Android.mk b/hostsidetests/services/activityandwindowmanager/activitymanager/app/Android.mk
similarity index 100%
rename from hostsidetests/services/activitymanager/app/Android.mk
rename to hostsidetests/services/activityandwindowmanager/activitymanager/app/Android.mk
diff --git a/hostsidetests/services/activitymanager/app/AndroidManifest.xml b/hostsidetests/services/activityandwindowmanager/activitymanager/app/AndroidManifest.xml
similarity index 99%
rename from hostsidetests/services/activitymanager/app/AndroidManifest.xml
rename to hostsidetests/services/activityandwindowmanager/activitymanager/app/AndroidManifest.xml
index 00c6352..0a690e3 100755
--- a/hostsidetests/services/activitymanager/app/AndroidManifest.xml
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/app/AndroidManifest.xml
@@ -17,7 +17,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
- package="android.server.app">
+ package="android.server.cts">
<application>
<activity android:name=".TestActivity"
diff --git a/hostsidetests/services/activitymanager/app/res/values/styles.xml b/hostsidetests/services/activityandwindowmanager/activitymanager/app/res/values/styles.xml
similarity index 100%
rename from hostsidetests/services/activitymanager/app/res/values/styles.xml
rename to hostsidetests/services/activityandwindowmanager/activitymanager/app/res/values/styles.xml
diff --git a/hostsidetests/services/activitymanager/app/src/android/server/app/AbstractLifecycleLogActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/AbstractLifecycleLogActivity.java
similarity index 97%
rename from hostsidetests/services/activitymanager/app/src/android/server/app/AbstractLifecycleLogActivity.java
rename to hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/AbstractLifecycleLogActivity.java
index e3026c9..8086ce9 100644
--- a/hostsidetests/services/activitymanager/app/src/android/server/app/AbstractLifecycleLogActivity.java
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/AbstractLifecycleLogActivity.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package android.server.app;
+package android.server.cts;
import android.app.Activity;
import android.content.res.Configuration;
diff --git a/hostsidetests/services/activitymanager/app/src/android/server/app/AlwaysFocusablePipActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/AlwaysFocusablePipActivity.java
similarity index 97%
rename from hostsidetests/services/activitymanager/app/src/android/server/app/AlwaysFocusablePipActivity.java
rename to hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/AlwaysFocusablePipActivity.java
index b5c736d..84b4b45 100644
--- a/hostsidetests/services/activitymanager/app/src/android/server/app/AlwaysFocusablePipActivity.java
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/AlwaysFocusablePipActivity.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package android.server.app;
+package android.server.cts;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
diff --git a/hostsidetests/services/activitymanager/app/src/android/server/app/AutoEnterPipActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/AutoEnterPipActivity.java
similarity index 96%
rename from hostsidetests/services/activitymanager/app/src/android/server/app/AutoEnterPipActivity.java
rename to hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/AutoEnterPipActivity.java
index 97bc041..9d68603 100644
--- a/hostsidetests/services/activitymanager/app/src/android/server/app/AutoEnterPipActivity.java
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/AutoEnterPipActivity.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package android.server.app;
+package android.server.cts;
import android.app.Activity;
diff --git a/hostsidetests/services/activitymanager/app/src/android/server/app/BottomLeftLayoutActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/BottomLeftLayoutActivity.java
similarity index 95%
rename from hostsidetests/services/activitymanager/app/src/android/server/app/BottomLeftLayoutActivity.java
rename to hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/BottomLeftLayoutActivity.java
index 27a6fe2..8de1cda 100644
--- a/hostsidetests/services/activitymanager/app/src/android/server/app/BottomLeftLayoutActivity.java
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/BottomLeftLayoutActivity.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.server.app;
+package android.server.cts;
import android.app.Activity;
diff --git a/hostsidetests/services/activitymanager/app/src/android/server/app/BottomRightLayoutActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/BottomRightLayoutActivity.java
similarity index 95%
rename from hostsidetests/services/activitymanager/app/src/android/server/app/BottomRightLayoutActivity.java
rename to hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/BottomRightLayoutActivity.java
index 7a91510..24fa2fc 100644
--- a/hostsidetests/services/activitymanager/app/src/android/server/app/BottomRightLayoutActivity.java
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/BottomRightLayoutActivity.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.server.app;
+package android.server.cts;
import android.app.Activity;
diff --git a/hostsidetests/services/activitymanager/app/src/android/server/app/DockedActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/DockedActivity.java
similarity index 95%
rename from hostsidetests/services/activitymanager/app/src/android/server/app/DockedActivity.java
rename to hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/DockedActivity.java
index 427fd29..007df5f 100644
--- a/hostsidetests/services/activitymanager/app/src/android/server/app/DockedActivity.java
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/DockedActivity.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.server.app;
+package android.server.cts;
import android.app.Activity;
diff --git a/hostsidetests/services/activitymanager/app/src/android/server/app/FreeformActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/FreeformActivity.java
similarity index 97%
rename from hostsidetests/services/activitymanager/app/src/android/server/app/FreeformActivity.java
rename to hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/FreeformActivity.java
index a1cb54c..c5f7cae 100644
--- a/hostsidetests/services/activitymanager/app/src/android/server/app/FreeformActivity.java
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/FreeformActivity.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package android.server.app;
+package android.server.cts;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
diff --git a/hostsidetests/services/activitymanager/app/src/android/server/app/LaunchIntoPinnedStackPipActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/LaunchIntoPinnedStackPipActivity.java
similarity index 96%
rename from hostsidetests/services/activitymanager/app/src/android/server/app/LaunchIntoPinnedStackPipActivity.java
rename to hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/LaunchIntoPinnedStackPipActivity.java
index 45386bf..23298a0 100644
--- a/hostsidetests/services/activitymanager/app/src/android/server/app/LaunchIntoPinnedStackPipActivity.java
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/LaunchIntoPinnedStackPipActivity.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package android.server.app;
+package android.server.cts;
import android.app.Activity;
diff --git a/hostsidetests/services/activitymanager/app/src/android/server/app/LaunchPipOnPipActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/LaunchPipOnPipActivity.java
similarity index 96%
rename from hostsidetests/services/activitymanager/app/src/android/server/app/LaunchPipOnPipActivity.java
rename to hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/LaunchPipOnPipActivity.java
index d7b4cc1..1b9b729 100644
--- a/hostsidetests/services/activitymanager/app/src/android/server/app/LaunchPipOnPipActivity.java
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/LaunchPipOnPipActivity.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package android.server.app;
+package android.server.cts;
import android.app.Activity;
diff --git a/hostsidetests/services/activitymanager/app/src/android/server/app/LaunchToSideActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/LaunchToSideActivity.java
similarity index 97%
rename from hostsidetests/services/activitymanager/app/src/android/server/app/LaunchToSideActivity.java
rename to hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/LaunchToSideActivity.java
index 6cdcbde..9607d1b 100644
--- a/hostsidetests/services/activitymanager/app/src/android/server/app/LaunchToSideActivity.java
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/LaunchToSideActivity.java
@@ -1,4 +1,4 @@
-package android.server.app;
+package android.server.cts;
import static android.content.Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT;
import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
diff --git a/hostsidetests/services/activitymanager/app/src/android/server/app/NoRelaunchActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/NoRelaunchActivity.java
similarity index 96%
rename from hostsidetests/services/activitymanager/app/src/android/server/app/NoRelaunchActivity.java
rename to hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/NoRelaunchActivity.java
index 0058c13..9cc3b9d 100644
--- a/hostsidetests/services/activitymanager/app/src/android/server/app/NoRelaunchActivity.java
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/NoRelaunchActivity.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.server.app;
+package android.server.cts;
public class NoRelaunchActivity extends AbstractLifecycleLogActivity {
diff --git a/hostsidetests/services/activitymanager/app/src/android/server/app/NonResizeableActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/NonResizeableActivity.java
similarity index 96%
rename from hostsidetests/services/activitymanager/app/src/android/server/app/NonResizeableActivity.java
rename to hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/NonResizeableActivity.java
index 6312b47..b871a8d 100644
--- a/hostsidetests/services/activitymanager/app/src/android/server/app/NonResizeableActivity.java
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/NonResizeableActivity.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.server.app;
+package android.server.cts;
public class NonResizeableActivity extends AbstractLifecycleLogActivity {
diff --git a/hostsidetests/services/activitymanager/app/src/android/server/app/PipActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/PipActivity.java
similarity index 95%
rename from hostsidetests/services/activitymanager/app/src/android/server/app/PipActivity.java
rename to hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/PipActivity.java
index b750389..1747acd 100644
--- a/hostsidetests/services/activitymanager/app/src/android/server/app/PipActivity.java
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/PipActivity.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package android.server.app;
+package android.server.cts;
import android.app.Activity;
diff --git a/hostsidetests/services/activitymanager/app/src/android/server/app/ResizeableActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/ResizeableActivity.java
similarity index 98%
rename from hostsidetests/services/activitymanager/app/src/android/server/app/ResizeableActivity.java
rename to hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/ResizeableActivity.java
index 747bfc1..02360a4 100644
--- a/hostsidetests/services/activitymanager/app/src/android/server/app/ResizeableActivity.java
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/ResizeableActivity.java
@@ -13,7 +13,7 @@
* License for the specific language governing permissions and limitations under
* the License.
*/
-package android.server.app;
+package android.server.cts;
import android.content.res.Configuration;
import android.graphics.Point;
diff --git a/hostsidetests/services/activitymanager/app/src/android/server/app/SingleInstanceActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/SingleInstanceActivity.java
similarity index 95%
rename from hostsidetests/services/activitymanager/app/src/android/server/app/SingleInstanceActivity.java
rename to hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/SingleInstanceActivity.java
index b745adc..e0eaecf 100644
--- a/hostsidetests/services/activitymanager/app/src/android/server/app/SingleInstanceActivity.java
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/SingleInstanceActivity.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package android.server.app;
+package android.server.cts;
import android.app.Activity;
diff --git a/hostsidetests/services/activitymanager/app/src/android/server/app/SingleTaskActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/SingleTaskActivity.java
similarity index 95%
rename from hostsidetests/services/activitymanager/app/src/android/server/app/SingleTaskActivity.java
rename to hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/SingleTaskActivity.java
index 0a99ea3..7ffde13 100644
--- a/hostsidetests/services/activitymanager/app/src/android/server/app/SingleTaskActivity.java
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/SingleTaskActivity.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package android.server.app;
+package android.server.cts;
import android.app.Activity;
diff --git a/hostsidetests/services/activitymanager/app/src/android/server/app/SlowCreateActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/SlowCreateActivity.java
similarity index 96%
rename from hostsidetests/services/activitymanager/app/src/android/server/app/SlowCreateActivity.java
rename to hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/SlowCreateActivity.java
index 481f5be..a757165 100644
--- a/hostsidetests/services/activitymanager/app/src/android/server/app/SlowCreateActivity.java
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/SlowCreateActivity.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.server.app;
+package android.server.cts;
import android.app.Activity;
import android.os.Bundle;
diff --git a/hostsidetests/services/activitymanager/app/src/android/server/app/TestActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TestActivity.java
similarity index 96%
rename from hostsidetests/services/activitymanager/app/src/android/server/app/TestActivity.java
rename to hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TestActivity.java
index a8e51d5..7dfcea3 100644
--- a/hostsidetests/services/activitymanager/app/src/android/server/app/TestActivity.java
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TestActivity.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.server.app;
+package android.server.cts;
public class TestActivity extends AbstractLifecycleLogActivity {
diff --git a/hostsidetests/services/activitymanager/app/src/android/server/app/TopLeftLayoutActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TopLeftLayoutActivity.java
similarity index 95%
rename from hostsidetests/services/activitymanager/app/src/android/server/app/TopLeftLayoutActivity.java
rename to hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TopLeftLayoutActivity.java
index 3a0267b..2305582 100644
--- a/hostsidetests/services/activitymanager/app/src/android/server/app/TopLeftLayoutActivity.java
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TopLeftLayoutActivity.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.server.app;
+package android.server.cts;
import android.app.Activity;
diff --git a/hostsidetests/services/activitymanager/app/src/android/server/app/TopRightLayoutActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TopRightLayoutActivity.java
similarity index 95%
rename from hostsidetests/services/activitymanager/app/src/android/server/app/TopRightLayoutActivity.java
rename to hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TopRightLayoutActivity.java
index eb3b1b0..701d3db 100644
--- a/hostsidetests/services/activitymanager/app/src/android/server/app/TopRightLayoutActivity.java
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TopRightLayoutActivity.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.server.app;
+package android.server.cts;
import android.app.Activity;
diff --git a/hostsidetests/services/activitymanager/app/src/android/server/app/TrampolineActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TrampolineActivity.java
similarity index 97%
rename from hostsidetests/services/activitymanager/app/src/android/server/app/TrampolineActivity.java
rename to hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TrampolineActivity.java
index 4b80482..b3f9444 100644
--- a/hostsidetests/services/activitymanager/app/src/android/server/app/TrampolineActivity.java
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TrampolineActivity.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.server.app;
+package android.server.cts;
import android.os.Bundle;
diff --git a/hostsidetests/services/activitymanager/app/src/android/server/app/TranslucentActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TranslucentActivity.java
similarity index 95%
rename from hostsidetests/services/activitymanager/app/src/android/server/app/TranslucentActivity.java
rename to hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TranslucentActivity.java
index 800080e..96697aa 100644
--- a/hostsidetests/services/activitymanager/app/src/android/server/app/TranslucentActivity.java
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TranslucentActivity.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package android.server.app;
+package android.server.cts;
import android.app.Activity;
diff --git a/hostsidetests/services/activitymanager/app/src/android/server/app/TurnScreenOnActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TurnScreenOnActivity.java
similarity index 97%
rename from hostsidetests/services/activitymanager/app/src/android/server/app/TurnScreenOnActivity.java
rename to hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TurnScreenOnActivity.java
index 838a1d6..79e31b3 100644
--- a/hostsidetests/services/activitymanager/app/src/android/server/app/TurnScreenOnActivity.java
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TurnScreenOnActivity.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package android.server.app;
+package android.server.cts;
import android.app.Activity;
import android.os.Bundle;
diff --git a/hostsidetests/services/activitymanager/app/src/android/server/app/VisibleBehindActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/VisibleBehindActivity.java
similarity index 96%
rename from hostsidetests/services/activitymanager/app/src/android/server/app/VisibleBehindActivity.java
rename to hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/VisibleBehindActivity.java
index cc8f273..29feba9 100644
--- a/hostsidetests/services/activitymanager/app/src/android/server/app/VisibleBehindActivity.java
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/VisibleBehindActivity.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package android.server.app;
+package android.server.cts;
import android.app.Activity;
import android.util.Log;
diff --git a/hostsidetests/services/activitymanager/appDisplaySize/Android.mk b/hostsidetests/services/activityandwindowmanager/activitymanager/appDisplaySize/Android.mk
similarity index 100%
rename from hostsidetests/services/activitymanager/appDisplaySize/Android.mk
rename to hostsidetests/services/activityandwindowmanager/activitymanager/appDisplaySize/Android.mk
diff --git a/hostsidetests/services/activitymanager/appDisplaySize/AndroidManifest.xml b/hostsidetests/services/activityandwindowmanager/activitymanager/appDisplaySize/AndroidManifest.xml
similarity index 100%
rename from hostsidetests/services/activitymanager/appDisplaySize/AndroidManifest.xml
rename to hostsidetests/services/activityandwindowmanager/activitymanager/appDisplaySize/AndroidManifest.xml
diff --git a/hostsidetests/services/activitymanager/appDisplaySize/src/android/displaysize/app/SmallestWidthActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/appDisplaySize/src/android/displaysize/app/SmallestWidthActivity.java
similarity index 92%
rename from hostsidetests/services/activitymanager/appDisplaySize/src/android/displaysize/app/SmallestWidthActivity.java
rename to hostsidetests/services/activityandwindowmanager/activitymanager/appDisplaySize/src/android/displaysize/app/SmallestWidthActivity.java
index b3f8c39..5da44c7 100644
--- a/hostsidetests/services/activitymanager/appDisplaySize/src/android/displaysize/app/SmallestWidthActivity.java
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/appDisplaySize/src/android/displaysize/app/SmallestWidthActivity.java
@@ -31,7 +31,7 @@
if (extras != null && extras.getBoolean("launch_another_activity")) {
Intent startIntent = new Intent();
startIntent.setComponent(
- new ComponentName("android.server.app", "android.server.app.TestActivity"));
+ new ComponentName("android.server.cts", "android.server.cts.TestActivity"));
startActivity(startIntent);
}
}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityAndWindowManagerOverrideConfigTests.java b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityAndWindowManagerOverrideConfigTests.java
new file mode 100644
index 0000000..ec985eb
--- /dev/null
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityAndWindowManagerOverrideConfigTests.java
@@ -0,0 +1,99 @@
+/*
+ * 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.server.cts;
+
+import com.android.ddmlib.Log.LogLevel;
+import com.android.tradefed.device.CollectingOutputReceiver;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.log.LogUtil.CLog;
+
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class ActivityAndWindowManagerOverrideConfigTests extends ActivityManagerTestBase {
+ private static final String TEST_ACTIVITY_NAME = "LogConfigurationActivity";
+
+ private static final String AM_MOVE_TASK = "am stack movetask ";
+ private static final String AM_RESIZE_TASK = "am task resize ";
+
+ private class ConfigurationChangeObserver {
+ private final Pattern mConfigurationChangedPattern =
+ Pattern.compile("(.+): Configuration changed: ");
+ private final LinkedList<String> mLogs = new LinkedList();
+
+ public ConfigurationChangeObserver() {
+ }
+
+ public boolean findConfigurationChange(String activityName, int widthDp, int heightDp) throws DeviceNotAvailableException, InterruptedException {
+ int tries = 0;
+ boolean observed = false;
+ final Pattern mSpecificConfigurationChangedPattern =
+ Pattern.compile("(.+): Configuration changed: " + widthDp + "," + heightDp);
+ while (tries < 5 && !observed) {
+ final String logs = mDevice.executeAdbCommand(
+ "logcat", "-v", "brief", "-d", activityName + ":I", "*:S");
+ Collections.addAll(mLogs, logs.split("\\n"));
+ CLog.logAndDisplay(LogLevel.INFO, "Looking at logcat");
+ while (!mLogs.isEmpty()) {
+ final String line = mLogs.pop().trim();
+ CLog.logAndDisplay(LogLevel.INFO, line);
+ Matcher matcher = mConfigurationChangedPattern.matcher(line);
+ Matcher specificMatcher = mSpecificConfigurationChangedPattern.matcher(line);
+ if (specificMatcher.matches()) {
+ observed = true;
+ } else {
+ assertFalse("Expected configuration change with (" + widthDp + "," + heightDp + ") but found " + line, matcher.matches());
+ }
+ }
+ tries++;
+ Thread.sleep(500);
+ }
+ return observed;
+ }
+ }
+
+ private void launchTestActivityInFreeform(final String activityName) throws Exception {
+ mDevice.executeShellCommand(getAmStartCmd(activityName));
+ mAmWmState.computeState(mDevice, new String[] {activityName});
+
+ final int taskId = getActivityTaskId(activityName);
+ mDevice.executeShellCommand(AM_MOVE_TASK + taskId + " " + FREEFORM_WORKSPACE_STACK_ID + " true");
+ }
+
+private void resizeTask(final String activityName, int width, int height) throws Exception {
+ final int taskId = getActivityTaskId(activityName);
+ final String cmd = AM_RESIZE_TASK + taskId + " " + 0 + "," + 0 +
+ "," + width + "," + height;
+ mDevice.executeShellCommand(cmd);
+ }
+
+public void testReceiveOverrideConfigFromRelayout() throws Exception {
+ launchTestActivityInFreeform(TEST_ACTIVITY_NAME);
+
+ clearLogcat();
+ resizeTask(TEST_ACTIVITY_NAME, 100, 100);
+ ConfigurationChangeObserver c = new ConfigurationChangeObserver();
+ assertTrue("Expected to observe configuration change when resizing", c.findConfigurationChange(TEST_ACTIVITY_NAME, 100, 100));
+
+ clearLogcat();
+ setDeviceRotation(2);
+ assertTrue("Expected to observe configuration change after rotation", c.findConfigurationChange(TEST_ACTIVITY_NAME, 100, 100));
+ }
+}
+
diff --git a/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerActivityVisiblityTests.java b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerActivityVisiblityTests.java
similarity index 100%
rename from hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerActivityVisiblityTests.java
rename to hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerActivityVisiblityTests.java
diff --git a/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerAmStartOptionsTests.java b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerAmStartOptionsTests.java
similarity index 98%
rename from hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerAmStartOptionsTests.java
rename to hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerAmStartOptionsTests.java
index 0083be4..2c5255e 100644
--- a/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerAmStartOptionsTests.java
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerAmStartOptionsTests.java
@@ -29,7 +29,7 @@
public void testDashD() throws Exception {
final String[] waitForActivitiesVisible = new String[] {TEST_ACTIVITY_NAME};
- AmStartLogcatVerifier verifier = new AmStartLogcatVerifier("android.server.app", TEST_ACTIVITY_NAME);
+ AmStartLogcatVerifier verifier = new AmStartLogcatVerifier("android.server.cts", TEST_ACTIVITY_NAME);
// Run at least 2 rounds to verify that -D works with an existing process.
// -D could fail in this case if the force stop of process is broken.
@@ -53,7 +53,7 @@
private void testDashW(final String entryActivity, final String actualActivity)
throws Exception {
- AmStartLogcatVerifier verifier = new AmStartLogcatVerifier("android.server.app", actualActivity);
+ AmStartLogcatVerifier verifier = new AmStartLogcatVerifier("android.server.cts", actualActivity);
// Test cold start
startActivityAndVerifyResult(verifier, entryActivity, actualActivity, true);
@@ -205,7 +205,7 @@
activityName = matcher.group(2);
// Ignore activitiy displays from other packages, we don't
// want some random activity starts to ruin our test.
- if (!activityName.startsWith("android.server.app")) {
+ if (!activityName.startsWith("android.server.cts")) {
continue;
}
if (!shouldStart) {
diff --git a/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerAppConfigurationTests.java b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerAppConfigurationTests.java
similarity index 100%
rename from hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerAppConfigurationTests.java
rename to hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerAppConfigurationTests.java
diff --git a/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerConfigChangeTests.java b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerConfigChangeTests.java
similarity index 100%
rename from hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerConfigChangeTests.java
rename to hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerConfigChangeTests.java
diff --git a/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerDockedStackTests.java b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerDockedStackTests.java
similarity index 100%
rename from hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerDockedStackTests.java
rename to hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerDockedStackTests.java
diff --git a/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerFreeformStackTests.java b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerFreeformStackTests.java
similarity index 100%
rename from hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerFreeformStackTests.java
rename to hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerFreeformStackTests.java
diff --git a/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerManifestLayoutTests.java b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerManifestLayoutTests.java
similarity index 100%
rename from hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerManifestLayoutTests.java
rename to hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerManifestLayoutTests.java
diff --git a/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerPinnedStackTests.java b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerPinnedStackTests.java
similarity index 100%
rename from hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerPinnedStackTests.java
rename to hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerPinnedStackTests.java
diff --git a/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerReplaceWindowTests.java b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerReplaceWindowTests.java
similarity index 100%
rename from hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerReplaceWindowTests.java
rename to hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerReplaceWindowTests.java
diff --git a/hostsidetests/services/activitymanager/src/android/server/cts/DisplaySizeTest.java b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/DisplaySizeTest.java
similarity index 97%
rename from hostsidetests/services/activitymanager/src/android/server/cts/DisplaySizeTest.java
rename to hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/DisplaySizeTest.java
index 4dbb704..b093021 100644
--- a/hostsidetests/services/activitymanager/src/android/server/cts/DisplaySizeTest.java
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/DisplaySizeTest.java
@@ -52,7 +52,7 @@
// Ensure app process is stopped.
forceStopPackage("android.displaysize.app");
- forceStopPackage("android.server.app");
+ forceStopPackage("android.server.cts");
} catch (DeviceNotAvailableException e) {
// Do nothing.
}
@@ -60,7 +60,7 @@
public void testCompatibilityDialog() throws Exception {
// Launch some other app (not to perform density change on launcher).
- startActivity("android.server.app", "TestActivity");
+ startActivity("android.server.cts", "TestActivity");
verifyWindowDisplayed("TestActivity", ACTIVITY_TIMEOUT_MILLIS);
setUnsupportedDensity();
diff --git a/hostsidetests/services/activityandwindowmanager/util/Android.mk b/hostsidetests/services/activityandwindowmanager/util/Android.mk
new file mode 100644
index 0000000..ac91d35
--- /dev/null
+++ b/hostsidetests/services/activityandwindowmanager/util/Android.mk
@@ -0,0 +1,34 @@
+# Copyright (C) 2012 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := \
+ $(call all-java-files-under, src)
+
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+
+LOCAL_JAVA_LIBRARIES := cts-tradefed tradefed-prebuilt
+
+LOCAL_MODULE := cts-amwm-util
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_HOST_JAVA_LIBRARY)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/hostsidetests/services/activitymanager/src/android/server/cts/ActivityAndWindowManagersState.java b/hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/ActivityAndWindowManagersState.java
similarity index 98%
rename from hostsidetests/services/activitymanager/src/android/server/cts/ActivityAndWindowManagersState.java
rename to hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/ActivityAndWindowManagersState.java
index bb7f521..0ee9522 100644
--- a/hostsidetests/services/activitymanager/src/android/server/cts/ActivityAndWindowManagersState.java
+++ b/hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/ActivityAndWindowManagersState.java
@@ -35,7 +35,7 @@
import static android.server.cts.StateLogger.log;
/** Combined state of the activity manager and window manager. */
-class ActivityAndWindowManagersState extends Assert {
+public class ActivityAndWindowManagersState extends Assert {
// Clone of android DisplayMetrics.DENSITY_DEFAULT (DENSITY_MEDIUM)
// (Needed in host-side tests to convert dp to px.)
@@ -57,7 +57,7 @@
* @param device test device.
* @param waitForActivitiesVisible array of activity names to wait for.
*/
- void computeState(ITestDevice device, String[] waitForActivitiesVisible) throws Exception {
+ public void computeState(ITestDevice device, String[] waitForActivitiesVisible) throws Exception {
computeState(device, waitForActivitiesVisible, true);
}
@@ -222,7 +222,7 @@
return mAmState;
}
- WindowManagerState getWmState() {
+ public WindowManagerState getWmState() {
return mWmState;
}
diff --git a/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerState.java b/hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/ActivityManagerState.java
similarity index 97%
rename from hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerState.java
rename to hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/ActivityManagerState.java
index d392e00..fea300c 100644
--- a/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerState.java
+++ b/hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/ActivityManagerState.java
@@ -45,18 +45,18 @@
private final Pattern mDisplayIdPattern = Pattern.compile("Display #(\\d+)");
private final Pattern mStackIdPattern = Pattern.compile("Stack #(\\d+)\\:");
- private final Pattern mFocusedActivityPattern =
- Pattern.compile("mFocusedActivity\\: ActivityRecord\\{(.+) u(\\d+) (\\S+) (\\S+)\\}");
+ private final Pattern mResumedActivityPattern =
+ Pattern.compile("ResumedActivity\\: ActivityRecord\\{(.+) u(\\d+) (\\S+) (\\S+)\\}");
private final Pattern mFocusedStackPattern =
Pattern.compile("mFocusedStack=ActivityStack\\{(.+) stackId=(\\d+), (.+)\\}(.+)");
private final Pattern[] mExtractStackExitPatterns =
- { mStackIdPattern, mFocusedActivityPattern, mFocusedStackPattern};
+ { mStackIdPattern, mResumedActivityPattern, mFocusedStackPattern};
// Stacks in z-order with the top most at the front of the list.
private final List<ActivityStack> mStacks = new ArrayList();
private int mFocusedStackId = -1;
- private String mFocusedActivityRecord = null;
+ private String mResumedActivityRecord = null;
private final List<String> mResumedActivities = new ArrayList();
private final LinkedList<String> mSysDump = new LinkedList();
@@ -88,7 +88,7 @@
dump = outputReceiver.getOutput();
parseSysDump(dump);
- retry = mStacks.isEmpty() || mFocusedStackId == -1 || mFocusedActivityRecord == null
+ retry = mStacks.isEmpty() || mFocusedStackId == -1 || mResumedActivityRecord == null
|| mResumedActivities.isEmpty();
} while (retry && retriesLeft-- > 0);
@@ -102,7 +102,7 @@
if (mFocusedStackId == -1) {
logE("No focused stack found...");
}
- if (mFocusedActivityRecord == null) {
+ if (mResumedActivityRecord == null) {
logE("No focused activity found...");
}
if (mResumedActivities.isEmpty()) {
@@ -139,11 +139,11 @@
continue;
}
- matcher = mFocusedActivityPattern.matcher(line);
+ matcher = mResumedActivityPattern.matcher(line);
if (matcher.matches()) {
log(line);
- mFocusedActivityRecord = matcher.group(3);
- log(mFocusedActivityRecord);
+ mResumedActivityRecord = matcher.group(3);
+ log(mResumedActivityRecord);
continue;
}
@@ -160,7 +160,7 @@
private void reset() {
mStacks.clear();
mFocusedStackId = -1;
- mFocusedActivityRecord = null;
+ mResumedActivityRecord = null;
mResumedActivities.clear();
mSysDump.clear();
}
@@ -174,7 +174,7 @@
}
String getFocusedActivity() {
- return mFocusedActivityRecord;
+ return mResumedActivityRecord;
}
String getResumedActivity() {
diff --git a/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerTestBase.java b/hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/ActivityManagerTestBase.java
similarity index 96%
rename from hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerTestBase.java
rename to hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/ActivityManagerTestBase.java
index 7ef163e..4dd3aa8 100644
--- a/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerTestBase.java
+++ b/hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/ActivityManagerTestBase.java
@@ -60,7 +60,7 @@
private static final String AM_STACK_LIST = "am stack list";
- private static final String AM_FORCE_STOP_TEST_PACKAGE = "am force-stop android.server.app";
+ private static final String AM_FORCE_STOP_TEST_PACKAGE = "am force-stop android.server.cts";
private static final String AM_REMOVE_STACK = "am stack remove ";
@@ -76,6 +76,8 @@
private static final String INPUT_KEYEVENT_HOME = "input keyevent 3";
+ static String componentName = "android.server.cts";
+
/** A reference to the device under test. */
protected ITestDevice mDevice;
@@ -85,16 +87,29 @@
return "am start -n " + getActivityComponentName(activityName);
}
+ protected static String getAmStartCmd(final String activityName,
+ final String extraDataName,
+ final String extraDataValue) {
+ String base = getAmStartCmd(activityName);
+ return base + " --es " + extraDataName + " " + extraDataValue;
+ }
+
protected static String getAmStartCmdOverHome(final String activityName) {
return "am start --activity-task-on-home -n " + getActivityComponentName(activityName);
}
static String getActivityComponentName(final String activityName) {
- return "android.server.app/." + activityName;
+ return componentName + "/." + activityName;
+ }
+
+ // A little ugly, but lets avoid having to strip static everywhere for
+ // now.
+ public static void setComponentName(String name) {
+ componentName = name;
}
static String getWindowName(final String activityName) {
- return "android.server.app/android.server.app." + activityName;
+ return componentName + "/" + componentName + "." + activityName;
}
protected ActivityAndWindowManagersState mAmWmState = new ActivityAndWindowManagersState();
diff --git a/hostsidetests/services/activitymanager/src/android/server/cts/StateLogger.java b/hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/StateLogger.java
similarity index 100%
rename from hostsidetests/services/activitymanager/src/android/server/cts/StateLogger.java
rename to hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/StateLogger.java
diff --git a/hostsidetests/services/activitymanager/src/android/server/cts/WindowManagerState.java b/hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/WindowManagerState.java
similarity index 89%
rename from hostsidetests/services/activitymanager/src/android/server/cts/WindowManagerState.java
rename to hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/WindowManagerState.java
index a057013..82afb70 100644
--- a/hostsidetests/services/activitymanager/src/android/server/cts/WindowManagerState.java
+++ b/hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/WindowManagerState.java
@@ -34,7 +34,7 @@
import static android.server.cts.StateLogger.log;
import static android.server.cts.StateLogger.logE;
-class WindowManagerState {
+public class WindowManagerState {
private static final String DUMPSYS_WINDOWS_APPS = "dumpsys window apps";
private static final String DUMPSYS_WINDOWS_VISIBLE_APPS = "dumpsys window visible-apps";
@@ -215,7 +215,7 @@
}
}
- void getMatchingWindowState(final String windowName, List<WindowState> windowList) {
+ public void getMatchingWindowState(final String windowName, List<WindowState> windowList) {
windowList.clear();
for (WindowState ws : mWindowStates) {
if (windowName.equals(ws.getName())) {
@@ -586,14 +586,23 @@
}
}
- static class WindowState extends WindowContainer {
+ public static class WindowState extends WindowContainer {
private static final String TAG = "[WindowState] ";
private static final String RECT_STR = "\\[(\\d+),(\\d+)\\]\\[(\\d+),(\\d+)\\]";
+ private static final Pattern sMainFramePattern = Pattern.compile("mFrame=" + RECT_STR + ".+");
private static final Pattern sFramePattern =
Pattern.compile("Frames: containing=" + RECT_STR + " parent=" + RECT_STR);
+ private static final Pattern sContentFramePattern =
+ Pattern.compile("content=" + RECT_STR + " .+");
private static final Pattern sWindowAssociationPattern =
Pattern.compile("mDisplayId=(\\d+) stackId=(\\d+) (.+)");
+ private static final Pattern sSurfaceInsetsPattern =
+ Pattern.compile("Cur insets.+surface=" + RECT_STR + ".+");
+ private static final Pattern sLayerPattern =
+ Pattern.compile("Surface:.+layer=(\\d+).+");
+ private static final Pattern sCropPattern =
+ Pattern.compile(".+mLastClipRect=" + RECT_STR + ".*");
private final String mName;
private final String mAppToken;
@@ -601,8 +610,13 @@
private final boolean mExiting;
private int mDisplayId;
private int mStackId;
+ private int mLayer;
private Rectangle mContainingFrame = new Rectangle();
private Rectangle mParentFrame = new Rectangle();
+ private Rectangle mContentFrame = new Rectangle();
+ private Rectangle mFrame = new Rectangle();
+ private Rectangle mSurfaceInsets = new Rectangle();
+ private Rectangle mCrop = new Rectangle();
private WindowState(Matcher matcher, boolean starting, boolean exiting) {
mName = matcher.group(4);
@@ -611,7 +625,7 @@
mExiting = exiting;
}
- String getName() {
+ public String getName() {
return mName;
}
@@ -635,14 +649,34 @@
return mStackId;
}
+ int getLayer() {
+ return mLayer;
+ }
+
Rectangle getContainingFrame() {
return mContainingFrame;
}
+ Rectangle getFrame() {
+ return mFrame;
+ }
+
+ Rectangle getSurfaceInsets() {
+ return mSurfaceInsets;
+ }
+
+ Rectangle getContentFrame() {
+ return mContentFrame;
+ }
+
Rectangle getParentFrame() {
return mParentFrame;
}
+ Rectangle getCrop() {
+ return mCrop;
+ }
+
static WindowState create(LinkedList<String> dump, Pattern[] exitPatterns) {
final String line = dump.peek().trim();
@@ -685,6 +719,13 @@
continue;
}
+ matcher = sMainFramePattern.matcher(line);
+ if (matcher.matches()) {
+ log(TAG + "MAIN WINDOW FRAME: " + line);
+ mFrame = extractBounds(matcher);
+ continue;
+ }
+
matcher = sFramePattern.matcher(line);
if (matcher.matches()) {
log(TAG + "FRAME: " + line);
@@ -692,6 +733,29 @@
continue;
}
+ matcher = sContentFramePattern.matcher(line);
+ if (matcher.matches()) {
+ log(TAG + "CONTENT FRAME: " + line);
+ mContentFrame = extractBounds(matcher);
+ }
+
+ matcher = sSurfaceInsetsPattern.matcher(line);
+ if (matcher.matches()) {
+ log(TAG + "INSETS: " + line);
+ mSurfaceInsets = extractBounds(matcher);
+ }
+
+ matcher = sLayerPattern.matcher(line);
+ if (matcher.matches()) {
+ log(TAG + "LAYER: " + line);
+ mLayer = Integer.valueOf(matcher.group(1));
+ }
+
+ matcher = sCropPattern.matcher(line);
+ if (matcher.matches()) {
+ log(TAG + "CROP: " + line);
+ mCrop = extractBounds(matcher);
+ }
// Extract other info here if needed
}
}
diff --git a/hostsidetests/services/windowmanager/Android.mk b/hostsidetests/services/activityandwindowmanager/windowmanager/Android.mk
similarity index 83%
rename from hostsidetests/services/windowmanager/Android.mk
rename to hostsidetests/services/activityandwindowmanager/windowmanager/Android.mk
index eeac27b..0dda353 100644
--- a/hostsidetests/services/windowmanager/Android.mk
+++ b/hostsidetests/services/activityandwindowmanager/windowmanager/Android.mk
@@ -19,13 +19,14 @@
LOCAL_MODULE_TAGS := tests
# Must match the package name in OldCtsTestCaseList.mk
-LOCAL_MODULE := CtsDragAndDropHostTestCases
+LOCAL_MODULE := CtsWindowManagerHostTestCases
LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_JAVA_LIBRARIES := cts-tradefed tradefed-prebuilt
+LOCAL_JAVA_LIBRARIES := cts-tradefed tradefed-prebuilt CtsServicesHostTestCases
+LOCAL_STATIC_JAVA_LIBRARIES := cts-amwm-util
-LOCAL_CTS_TEST_PACKAGE := android.wm.cts
+LOCAL_CTS_TEST_PACKAGE := android.server.cts
LOCAL_CTS_MODULE_CONFIG := $(LOCAL_PATH)/Old$(CTS_MODULE_TEST_CONFIG)
diff --git a/hostsidetests/services/windowmanager/AndroidTest.xml b/hostsidetests/services/activityandwindowmanager/windowmanager/AndroidTest.xml
similarity index 88%
rename from hostsidetests/services/windowmanager/AndroidTest.xml
rename to hostsidetests/services/activityandwindowmanager/windowmanager/AndroidTest.xml
index 8346b72..a66f8f1 100644
--- a/hostsidetests/services/windowmanager/AndroidTest.xml
+++ b/hostsidetests/services/activityandwindowmanager/windowmanager/AndroidTest.xml
@@ -19,8 +19,9 @@
<option name="test-file-name" value="CtsDragAndDropSourceApp.apk" />
<option name="test-file-name" value="CtsDragAndDropTargetApp.apk" />
<option name="test-file-name" value="CtsDragAndDropTargetAppSdk23.apk" />
+ <option name="test-file-name" value="CtsDeviceWindowFramesTestApp.apk" />
</target_preparer>
<test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
- <option name="jar" value="CtsDragAndDropHostTestCases.jar" />
+ <option name="jar" value="CtsWindowManagerHostTestCases.jar" />
</test>
</configuration>
diff --git a/hostsidetests/services/windowmanager/OldAndroidTest.xml b/hostsidetests/services/activityandwindowmanager/windowmanager/OldAndroidTest.xml
similarity index 100%
rename from hostsidetests/services/windowmanager/OldAndroidTest.xml
rename to hostsidetests/services/activityandwindowmanager/windowmanager/OldAndroidTest.xml
diff --git a/hostsidetests/services/windowmanager/dndsourceapp/Android.mk b/hostsidetests/services/activityandwindowmanager/windowmanager/dndsourceapp/Android.mk
similarity index 100%
rename from hostsidetests/services/windowmanager/dndsourceapp/Android.mk
rename to hostsidetests/services/activityandwindowmanager/windowmanager/dndsourceapp/Android.mk
diff --git a/hostsidetests/services/windowmanager/dndsourceapp/AndroidManifest.xml b/hostsidetests/services/activityandwindowmanager/windowmanager/dndsourceapp/AndroidManifest.xml
similarity index 100%
rename from hostsidetests/services/windowmanager/dndsourceapp/AndroidManifest.xml
rename to hostsidetests/services/activityandwindowmanager/windowmanager/dndsourceapp/AndroidManifest.xml
diff --git a/hostsidetests/services/windowmanager/dndsourceapp/res/layout/source_activity.xml b/hostsidetests/services/activityandwindowmanager/windowmanager/dndsourceapp/res/layout/source_activity.xml
similarity index 100%
rename from hostsidetests/services/windowmanager/dndsourceapp/res/layout/source_activity.xml
rename to hostsidetests/services/activityandwindowmanager/windowmanager/dndsourceapp/res/layout/source_activity.xml
diff --git a/hostsidetests/services/windowmanager/dndsourceapp/src/android/wm/cts/dndsourceapp/DragSource.java b/hostsidetests/services/activityandwindowmanager/windowmanager/dndsourceapp/src/android/wm/cts/dndsourceapp/DragSource.java
similarity index 100%
rename from hostsidetests/services/windowmanager/dndsourceapp/src/android/wm/cts/dndsourceapp/DragSource.java
rename to hostsidetests/services/activityandwindowmanager/windowmanager/dndsourceapp/src/android/wm/cts/dndsourceapp/DragSource.java
diff --git a/hostsidetests/services/windowmanager/dndsourceapp/src/android/wm/cts/dndsourceapp/DragSourceContentProvider.java b/hostsidetests/services/activityandwindowmanager/windowmanager/dndsourceapp/src/android/wm/cts/dndsourceapp/DragSourceContentProvider.java
similarity index 100%
rename from hostsidetests/services/windowmanager/dndsourceapp/src/android/wm/cts/dndsourceapp/DragSourceContentProvider.java
rename to hostsidetests/services/activityandwindowmanager/windowmanager/dndsourceapp/src/android/wm/cts/dndsourceapp/DragSourceContentProvider.java
diff --git a/hostsidetests/services/windowmanager/dndsourceapp/src/android/wm/cts/dndsourceapp/DragSourceCursor.java b/hostsidetests/services/activityandwindowmanager/windowmanager/dndsourceapp/src/android/wm/cts/dndsourceapp/DragSourceCursor.java
similarity index 100%
rename from hostsidetests/services/windowmanager/dndsourceapp/src/android/wm/cts/dndsourceapp/DragSourceCursor.java
rename to hostsidetests/services/activityandwindowmanager/windowmanager/dndsourceapp/src/android/wm/cts/dndsourceapp/DragSourceCursor.java
diff --git a/hostsidetests/services/windowmanager/dndtargetapp/Android.mk b/hostsidetests/services/activityandwindowmanager/windowmanager/dndtargetapp/Android.mk
similarity index 100%
rename from hostsidetests/services/windowmanager/dndtargetapp/Android.mk
rename to hostsidetests/services/activityandwindowmanager/windowmanager/dndtargetapp/Android.mk
diff --git a/hostsidetests/services/windowmanager/dndtargetapp/AndroidManifest.xml b/hostsidetests/services/activityandwindowmanager/windowmanager/dndtargetapp/AndroidManifest.xml
similarity index 100%
rename from hostsidetests/services/windowmanager/dndtargetapp/AndroidManifest.xml
rename to hostsidetests/services/activityandwindowmanager/windowmanager/dndtargetapp/AndroidManifest.xml
diff --git a/hostsidetests/services/windowmanager/dndtargetapp/res/layout/target_activity.xml b/hostsidetests/services/activityandwindowmanager/windowmanager/dndtargetapp/res/layout/target_activity.xml
similarity index 100%
rename from hostsidetests/services/windowmanager/dndtargetapp/res/layout/target_activity.xml
rename to hostsidetests/services/activityandwindowmanager/windowmanager/dndtargetapp/res/layout/target_activity.xml
diff --git a/hostsidetests/services/windowmanager/dndtargetapp/src/android/wm/cts/dndtargetapp/DropTarget.java b/hostsidetests/services/activityandwindowmanager/windowmanager/dndtargetapp/src/android/wm/cts/dndtargetapp/DropTarget.java
similarity index 72%
rename from hostsidetests/services/windowmanager/dndtargetapp/src/android/wm/cts/dndtargetapp/DropTarget.java
rename to hostsidetests/services/activityandwindowmanager/windowmanager/dndtargetapp/src/android/wm/cts/dndtargetapp/DropTarget.java
index 904a422..8892c6f 100644
--- a/hostsidetests/services/windowmanager/dndtargetapp/src/android/wm/cts/dndtargetapp/DropTarget.java
+++ b/hostsidetests/services/activityandwindowmanager/windowmanager/dndtargetapp/src/android/wm/cts/dndtargetapp/DropTarget.java
@@ -18,6 +18,7 @@
import android.app.Activity;
import android.content.ClipData;
+import android.content.ClipDescription;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
@@ -33,12 +34,20 @@
public static final String LOG_TAG = "DropTarget";
private static final String RESULT_KEY_DRAG_STARTED = "DRAG_STARTED";
+ private static final String RESULT_KEY_DRAG_ENDED = "DRAG_ENDED";
private static final String RESULT_KEY_EXTRAS = "EXTRAS";
private static final String RESULT_KEY_DROP_RESULT = "DROP";
private static final String RESULT_KEY_DETAILS = "DETAILS";
+ private static final String RESULT_KEY_ACCESS_AFTER = "AFTER";
+ private static final String RESULT_KEY_ACCESS_BEFORE = "BEFORE";
+ private static final String RESULT_KEY_CLIP_DATA_ERROR = "CLIP_DATA_ERROR";
+ private static final String RESULT_KEY_CLIP_DESCR_ERROR = "CLIP_DESCR_ERROR";
+ private static final String RESULT_KEY_LOCAL_STATE_ERROR = "LOCAL_STATE_ERROR";
public static final String RESULT_OK = "OK";
public static final String RESULT_EXCEPTION = "Exception";
+ public static final String RESULT_MISSING = "MISSING";
+ public static final String RESULT_LEAKING = "LEAKING";
protected static final String MAGIC_VALUE = "42";
@@ -94,6 +103,8 @@
@Override
public boolean onDrag(View v, DragEvent event) {
+ checkDragEvent(event);
+
switch (event.getAction()) {
case DragEvent.ACTION_DRAG_STARTED:
logResult(RESULT_KEY_DRAG_STARTED, RESULT_OK);
@@ -110,17 +121,18 @@
return true;
case DragEvent.ACTION_DROP:
- String result;
- try {
- result = processDrop(event, requestPermissions);
- } catch (Exception e) {
- result = RESULT_EXCEPTION;
- logResult(RESULT_KEY_DETAILS, e.getMessage());
- }
- logResult(RESULT_KEY_DROP_RESULT, result);
+ // Try accessing the Uri without the permissions grant.
+ accessContent(event, RESULT_KEY_ACCESS_BEFORE, false);
+
+ // Try accessing the Uri with the permission grant (if required);
+ accessContent(event, RESULT_KEY_DROP_RESULT, requestPermissions);
+
+ // Try accessing the Uri after the permissions have been released.
+ accessContent(event, RESULT_KEY_ACCESS_AFTER, false);
return true;
case DragEvent.ACTION_DRAG_ENDED:
+ logResult(RESULT_KEY_DRAG_ENDED, RESULT_OK);
return true;
default:
@@ -128,6 +140,19 @@
}
}
+ private void accessContent(DragEvent event, String resultKey, boolean requestPermissions) {
+ String result;
+ try {
+ result = processDrop(event, requestPermissions);
+ } catch (SecurityException e) {
+ result = RESULT_EXCEPTION;
+ if (resultKey.equals(RESULT_KEY_DROP_RESULT)) {
+ logResult(RESULT_KEY_DETAILS, e.getMessage());
+ }
+ }
+ logResult(resultKey, result);
+ }
+
private String processDrop(DragEvent event, boolean requestPermissions) {
final ClipData clipData = event.getClipData();
if (clipData == null) {
@@ -165,6 +190,40 @@
abstract protected String processUri(Uri uri);
}
+ private void checkDragEvent(DragEvent event) {
+ final int action = event.getAction();
+
+ // ClipData should be available for ACTION_DROP only.
+ final ClipData clipData = event.getClipData();
+ if (action == DragEvent.ACTION_DROP) {
+ if (clipData == null) {
+ logResult(RESULT_KEY_CLIP_DATA_ERROR, RESULT_MISSING);
+ }
+ } else {
+ if (clipData != null) {
+ logResult(RESULT_KEY_CLIP_DATA_ERROR, RESULT_LEAKING + action);
+ }
+ }
+
+ // ClipDescription should be always available except for ACTION_DRAG_ENDED.
+ final ClipDescription clipDescription = event.getClipDescription();
+ if (action != DragEvent.ACTION_DRAG_ENDED) {
+ if (clipDescription == null) {
+ logResult(RESULT_KEY_CLIP_DESCR_ERROR, RESULT_MISSING + action);
+ }
+ } else {
+ if (clipDescription != null) {
+ logResult(RESULT_KEY_CLIP_DESCR_ERROR, RESULT_LEAKING);
+ }
+ }
+
+ // Local state should be always null for cross-app drags.
+ final Object localState = event.getLocalState();
+ if (localState != null) {
+ logResult(RESULT_KEY_LOCAL_STATE_ERROR, RESULT_LEAKING + action);
+ }
+ }
+
private class OnDragUriReadListener extends OnDragUriListener {
OnDragUriReadListener(boolean requestPermissions) {
super(requestPermissions);
diff --git a/hostsidetests/services/windowmanager/dndtargetappsdk23/Android.mk b/hostsidetests/services/activityandwindowmanager/windowmanager/dndtargetappsdk23/Android.mk
similarity index 100%
rename from hostsidetests/services/windowmanager/dndtargetappsdk23/Android.mk
rename to hostsidetests/services/activityandwindowmanager/windowmanager/dndtargetappsdk23/Android.mk
diff --git a/hostsidetests/services/windowmanager/dndtargetappsdk23/AndroidManifest.xml b/hostsidetests/services/activityandwindowmanager/windowmanager/dndtargetappsdk23/AndroidManifest.xml
similarity index 100%
rename from hostsidetests/services/windowmanager/dndtargetappsdk23/AndroidManifest.xml
rename to hostsidetests/services/activityandwindowmanager/windowmanager/dndtargetappsdk23/AndroidManifest.xml
diff --git a/hostsidetests/services/windowmanager/dndtargetappsdk23/res/layout/target_activity.xml b/hostsidetests/services/activityandwindowmanager/windowmanager/dndtargetappsdk23/res/layout/target_activity.xml
similarity index 100%
rename from hostsidetests/services/windowmanager/dndtargetappsdk23/res/layout/target_activity.xml
rename to hostsidetests/services/activityandwindowmanager/windowmanager/dndtargetappsdk23/res/layout/target_activity.xml
diff --git a/hostsidetests/services/windowmanager/dndtargetappsdk23/src/android/wm/cts/dndtargetappsdk23/DropTarget.java b/hostsidetests/services/activityandwindowmanager/windowmanager/dndtargetappsdk23/src/android/wm/cts/dndtargetappsdk23/DropTarget.java
similarity index 100%
rename from hostsidetests/services/windowmanager/dndtargetappsdk23/src/android/wm/cts/dndtargetappsdk23/DropTarget.java
rename to hostsidetests/services/activityandwindowmanager/windowmanager/dndtargetappsdk23/src/android/wm/cts/dndtargetappsdk23/DropTarget.java
diff --git a/hostsidetests/services/windowmanager/Android.mk b/hostsidetests/services/activityandwindowmanager/windowmanager/frametestapp/Android.mk
similarity index 64%
copy from hostsidetests/services/windowmanager/Android.mk
copy to hostsidetests/services/activityandwindowmanager/windowmanager/frametestapp/Android.mk
index eeac27b..e5aa610 100644
--- a/hostsidetests/services/windowmanager/Android.mk
+++ b/hostsidetests/services/activityandwindowmanager/windowmanager/frametestapp/Android.mk
@@ -12,27 +12,20 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-LOCAL_PATH := $(call my-dir)
+LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
+# Don't include this package in any target.
LOCAL_MODULE_TAGS := tests
-# Must match the package name in OldCtsTestCaseList.mk
-LOCAL_MODULE := CtsDragAndDropHostTestCases
-
LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_JAVA_LIBRARIES := cts-tradefed tradefed-prebuilt
-
-LOCAL_CTS_TEST_PACKAGE := android.wm.cts
-
-LOCAL_CTS_MODULE_CONFIG := $(LOCAL_PATH)/Old$(CTS_MODULE_TEST_CONFIG)
+LOCAL_SDK_VERSION := test_current
# Tag this module as a cts test artifact
LOCAL_COMPATIBILITY_SUITE := cts
-include $(BUILD_CTS_HOST_JAVA_LIBRARY)
+LOCAL_PACKAGE_NAME := CtsDeviceWindowFramesTestApp
-# Build the test APKs using their own makefiles
-include $(call all-makefiles-under,$(LOCAL_PATH))
+include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/frametestapp/AndroidManifest.xml b/hostsidetests/services/activityandwindowmanager/windowmanager/frametestapp/AndroidManifest.xml
new file mode 100755
index 0000000..3d230c5
--- /dev/null
+++ b/hostsidetests/services/activityandwindowmanager/windowmanager/frametestapp/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2015 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ package="android.server.FrameTestApp">
+ <application>
+ <activity android:name=".DialogTestActivity"
+ android:exported="true"
+ />
+ <activity android:name=".SurfaceViewTestActivity"
+ android:exported="true"
+ />
+ </application>
+</manifest>
+
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/frametestapp/src/android/server/frametestapp/DialogTestActivity.java b/hostsidetests/services/activityandwindowmanager/windowmanager/frametestapp/src/android/server/frametestapp/DialogTestActivity.java
new file mode 100644
index 0000000..593cf34
--- /dev/null
+++ b/hostsidetests/services/activityandwindowmanager/windowmanager/frametestapp/src/android/server/frametestapp/DialogTestActivity.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2015 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.server.FrameTestApp;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.WindowManager;
+import android.view.Window;
+import android.view.Gravity;
+
+public class DialogTestActivity extends Activity {
+
+ AlertDialog mDialog;
+
+ protected void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ }
+
+ protected void onStop() {
+ super.onStop();
+ mDialog.dismiss();
+ }
+ protected void onResume() {
+ super.onResume();
+ setupTest(getIntent());
+ }
+
+ private void setupTest(Intent intent) {
+ String testCase = intent.getStringExtra(
+ "android.server.FrameTestApp.DialogTestCase");
+ switch (testCase) {
+ case "MatchParent": {
+ testMatchParent();
+ break;
+ } case "MatchParentLayoutInOverscan": {
+ testMatchParentLayoutInOverscan();
+ } break;
+ case "ExplicitSize": {
+ testExplicitSize();
+ break;
+ }
+ case "ExplicitSizeTopLeftGravity": {
+ testExplicitSizeTopLeftGravity();
+ break;
+ }
+ case "ExplicitSizeBottomRightGravity": {
+ testExplicitSizeBottomRightGravity();
+ break;
+ }
+ case "OversizedDimensions": {
+ testOversizedDimensions();
+ break;
+ }
+ case "OversizedDimensionsNoLimits": {
+ testOversizedDimensionsNoLimits();
+ break;
+ }
+ case "ExplicitPositionMatchParent": {
+ testExplicitPositionMatchParent();
+ break;
+ }
+ case "ExplicitPositionMatchParentNoLimits": {
+ testExplicitPositionMatchParentNoLimits();
+ break;
+ }
+ case "NoFocus": {
+ testNoFocus();
+ break;
+ }
+ case "WithMargins": {
+ testWithMargins();
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ interface DialogLayoutParamsTest {
+ void doSetup(WindowManager.LayoutParams p);
+ }
+
+ private void doLayoutParamTest(DialogLayoutParamsTest t) {
+ mDialog = new AlertDialog.Builder(this).create();
+
+ mDialog.setMessage("Testing is fun!");
+ mDialog.setTitle("android.server.FrameTestApp/android.server.FrameTestApp.TestDialog");
+ mDialog.create();
+
+ Window w = mDialog.getWindow();
+ final WindowManager.LayoutParams params = w.getAttributes();
+ t.doSetup(params);
+ w.setAttributes(params);
+
+ mDialog.show();
+ }
+
+ private void testMatchParent() {
+ doLayoutParamTest((WindowManager.LayoutParams params) -> {
+ params.width = WindowManager.LayoutParams.MATCH_PARENT;
+ params.height = WindowManager.LayoutParams.MATCH_PARENT;
+ });
+ }
+
+ private void testMatchParentLayoutInOverscan() {
+ doLayoutParamTest((WindowManager.LayoutParams params) -> {
+ params.width = WindowManager.LayoutParams.MATCH_PARENT;
+ params.height = WindowManager.LayoutParams.MATCH_PARENT;
+ params.flags |= WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
+ params.flags |= WindowManager.LayoutParams.FLAG_LAYOUT_IN_OVERSCAN;
+ });
+ }
+
+ private void testExplicitSize() {
+ doLayoutParamTest((WindowManager.LayoutParams params) -> {
+ params.width = 200;
+ params.height = 200;
+ });
+ }
+
+ private void testExplicitSizeTopLeftGravity() {
+ doLayoutParamTest((WindowManager.LayoutParams params) -> {
+ params.width = 200;
+ params.height = 200;
+ params.gravity = Gravity.TOP | Gravity.LEFT;
+ });
+ }
+
+ private void testExplicitSizeBottomRightGravity() {
+ doLayoutParamTest((WindowManager.LayoutParams params) -> {
+ params.width = 200;
+ params.height = 200;
+ params.gravity = Gravity.BOTTOM | Gravity.RIGHT;
+ });
+ }
+
+ private void testOversizedDimensions() {
+ doLayoutParamTest((WindowManager.LayoutParams params) -> {
+ params.width = 100000;
+ params.height = 100000;
+ });
+ }
+
+ private void testOversizedDimensionsNoLimits() {
+ doLayoutParamTest((WindowManager.LayoutParams params) -> {
+ params.width = 5000;
+ params.height = 5000;
+ params.flags |= WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
+ params.gravity = Gravity.LEFT | Gravity.TOP;
+ });
+ }
+
+ private void testExplicitPositionMatchParent() {
+ doLayoutParamTest((WindowManager.LayoutParams params) -> {
+ params.width = WindowManager.LayoutParams.MATCH_PARENT;
+ params.height = WindowManager.LayoutParams.MATCH_PARENT;
+ params.x = 100;
+ params.y = 100;
+ });
+ }
+
+ private void testExplicitPositionMatchParentNoLimits() {
+ doLayoutParamTest((WindowManager.LayoutParams params) -> {
+ params.width = WindowManager.LayoutParams.MATCH_PARENT;
+ params.height = WindowManager.LayoutParams.MATCH_PARENT;
+ params.gravity = Gravity.LEFT | Gravity.TOP;
+ params.flags |= WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
+ params.x = 100;
+ params.y = 100;
+ });
+ }
+
+ private void testNoFocus() {
+ doLayoutParamTest((WindowManager.LayoutParams params) -> {
+ params.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+ });
+ }
+
+ private void testWithMargins() {
+ doLayoutParamTest((WindowManager.LayoutParams params) -> {
+ params.gravity = Gravity.LEFT | Gravity.TOP;
+ params.horizontalMargin = .25f;
+ params.verticalMargin = .35f;
+ params.width = 200;
+ params.height = 200;
+ params.x = 0;
+ params.y = 0;
+ });
+ }
+}
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/frametestapp/src/android/server/frametestapp/SurfaceViewTestActivity.java b/hostsidetests/services/activityandwindowmanager/windowmanager/frametestapp/src/android/server/frametestapp/SurfaceViewTestActivity.java
new file mode 100644
index 0000000..0092619
--- /dev/null
+++ b/hostsidetests/services/activityandwindowmanager/windowmanager/frametestapp/src/android/server/frametestapp/SurfaceViewTestActivity.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2015 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.server.FrameTestApp;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.WindowManager;
+import android.view.Window;
+import android.view.Gravity;
+import android.view.SurfaceView;
+import android.view.ViewGroup.LayoutParams;
+import android.widget.FrameLayout;
+
+public class SurfaceViewTestActivity extends Activity {
+
+ SurfaceView mSurfaceView;
+
+ protected void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ }
+
+ protected void onResume() {
+ super.onResume();
+ setupTest(getIntent());
+ }
+
+ private void setupTest(Intent intent) {
+ String testCase = intent.getStringExtra(
+ "android.server.FrameTestApp.SurfaceViewTestCase");
+ switch (testCase) {
+ case "OnBottom": {
+ doOnBottomTest();
+ break;
+ }
+ case "OnTop": {
+ doOnTopTest();
+ break;
+ }
+ case "Oversized": {
+ doOversizedTest();
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ void doOnBottomTest() {
+ mSurfaceView = new SurfaceView(this);
+ setContentView(mSurfaceView);
+ }
+
+ void doOnTopTest() {
+ mSurfaceView = new SurfaceView(this);
+ mSurfaceView.setZOrderOnTop(true);
+ setContentView(mSurfaceView);
+ }
+
+ void doOversizedTest() {
+ mSurfaceView = new SurfaceView(this);
+ LayoutParams p = new LayoutParams(8000, 8000);
+ setContentView(mSurfaceView, p);
+ }
+}
diff --git a/hostsidetests/services/windowmanager/src/android/wm/cts/CrossAppDragAndDropTests.java b/hostsidetests/services/activityandwindowmanager/windowmanager/src/android/server/cts/CrossAppDragAndDropTests.java
similarity index 90%
rename from hostsidetests/services/windowmanager/src/android/wm/cts/CrossAppDragAndDropTests.java
rename to hostsidetests/services/activityandwindowmanager/windowmanager/src/android/server/cts/CrossAppDragAndDropTests.java
index 8fc3264..904600e 100644
--- a/hostsidetests/services/windowmanager/src/android/wm/cts/CrossAppDragAndDropTests.java
+++ b/hostsidetests/services/activityandwindowmanager/windowmanager/src/android/server/cts/CrossAppDragAndDropTests.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.wm.cts;
+package android.server.cts;
import com.android.tradefed.device.CollectingOutputReceiver;
import com.android.tradefed.device.DeviceNotAvailableException;
@@ -75,8 +75,14 @@
private static final String TARGET_LOG_TAG = "DropTarget";
private static final String RESULT_KEY_DRAG_STARTED = "DRAG_STARTED";
+ private static final String RESULT_KEY_DRAG_ENDED = "DRAG_ENDED";
private static final String RESULT_KEY_EXTRAS = "EXTRAS";
private static final String RESULT_KEY_DROP_RESULT = "DROP";
+ private static final String RESULT_KEY_ACCESS_BEFORE = "BEFORE";
+ private static final String RESULT_KEY_ACCESS_AFTER = "AFTER";
+ private static final String RESULT_KEY_CLIP_DATA_ERROR = "CLIP_DATA_ERROR";
+ private static final String RESULT_KEY_CLIP_DESCR_ERROR = "CLIP_DESCR_ERROR";
+ private static final String RESULT_KEY_LOCAL_STATE_ERROR = "LOCAL_STATE_ERROR";
private static final String RESULT_OK = "OK";
private static final String RESULT_EXCEPTION = "Exception";
@@ -265,7 +271,7 @@
}
private Map<String, String> getLogResults(String className) throws Exception {
- int retryCount = 3;
+ int retryCount = 10;
Map<String, String> output = new HashMap<String, String>();
do {
@@ -279,7 +285,7 @@
}
}
}
- if (output.containsKey(RESULT_KEY_DROP_RESULT)) {
+ if (output.containsKey(RESULT_KEY_DRAG_ENDED)) {
return output;
}
} while (retryCount-- > 0);
@@ -301,8 +307,22 @@
mResults = getLogResults(TARGET_LOG_TAG);
assertResult(RESULT_KEY_DROP_RESULT, expectedDropResult);
+ if (expectedDropResult != null) {
+ assertResult(RESULT_KEY_ACCESS_BEFORE, RESULT_EXCEPTION);
+ assertResult(RESULT_KEY_ACCESS_AFTER, RESULT_EXCEPTION);
+ assertListenerResults(RESULT_OK);
+ }
}
+ private void assertListenerResults(String expectedResult) {
+ assertResult(RESULT_KEY_DRAG_STARTED, expectedResult);
+ assertResult(RESULT_KEY_DRAG_ENDED, expectedResult);
+ assertResult(RESULT_KEY_EXTRAS, expectedResult);
+
+ assertResult(RESULT_KEY_CLIP_DATA_ERROR, null);
+ assertResult(RESULT_KEY_CLIP_DESCR_ERROR, null);
+ assertResult(RESULT_KEY_LOCAL_STATE_ERROR, null);
+ }
private void assertResult(String resultKey, String expectedResult) {
if (expectedResult == null) {
@@ -311,31 +331,28 @@
}
} else {
assertTrue("Missing " + resultKey, mResults.containsKey(resultKey));
- assertEquals(expectedResult, mResults.get(resultKey));
+ assertEquals(resultKey + " result mismatch,", expectedResult, mResults.get(resultKey));
}
}
public void testCancelSoon() throws Exception {
doTestDragAndDrop(CANCEL_SOON, REQUEST_NONE, null);
- assertResult(RESULT_KEY_DRAG_STARTED, RESULT_OK);
- assertResult(RESULT_KEY_EXTRAS, RESULT_OK);
+ assertListenerResults(RESULT_OK);
}
public void testDisallowGlobal() throws Exception {
doTestDragAndDrop(DISALLOW_GLOBAL, REQUEST_NONE, null);
- assertResult(RESULT_KEY_DRAG_STARTED, null);
+ assertListenerResults(null);
}
public void testDisallowGlobalBelowSdk24() throws Exception {
mTargetPackageName = TARGET_23_PACKAGE_NAME;
doTestDragAndDrop(GRANT_NONE, REQUEST_NONE, null);
- assertResult(RESULT_KEY_DRAG_STARTED, null);
+ assertListenerResults(null);
}
public void testGrantNoneRequestNone() throws Exception {
doTestDragAndDrop(GRANT_NONE, REQUEST_NONE, RESULT_EXCEPTION);
- assertResult(RESULT_KEY_DRAG_STARTED, RESULT_OK);
- assertResult(RESULT_KEY_EXTRAS, RESULT_OK);
}
public void testGrantNoneRequestRead() throws Exception {
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/src/android/server/cts/DialogFrameTests.java b/hostsidetests/services/activityandwindowmanager/windowmanager/src/android/server/cts/DialogFrameTests.java
new file mode 100644
index 0000000..ad8fac3
--- /dev/null
+++ b/hostsidetests/services/activityandwindowmanager/windowmanager/src/android/server/cts/DialogFrameTests.java
@@ -0,0 +1,255 @@
+/*
+ * 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.server.cts;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.awt.Rectangle;
+
+import com.android.ddmlib.Log.LogLevel;
+import com.android.tradefed.device.CollectingOutputReceiver;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.testtype.DeviceTestCase;
+
+import android.server.cts.ActivityManagerTestBase;
+import android.server.cts.WindowManagerState.WindowState;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class DialogFrameTests extends ActivityManagerTestBase {
+ static final String ACTIVITY_NAME = "DialogTestActivity";
+ static final String INTENT_KEY = "android.server.FrameTestApp.DialogTestCase";
+ static final String baseWindowName =
+ "android.server.FrameTestApp/android.server.FrameTestApp.";
+
+ private List<WindowState> mWindowList = new ArrayList();
+
+ public void startTestCase(String testCase) throws Exception{
+ setComponentName("android.server.FrameTestApp");
+ String cmd = getAmStartCmd(ACTIVITY_NAME, INTENT_KEY, testCase);
+ CLog.logAndDisplay(LogLevel.INFO, cmd);
+ executeShellCommand(cmd);
+ }
+
+ public void startTestCaseDocked(String testCase) throws Exception{
+ setComponentName("android.server.FrameTestApp");
+ String cmd = getAmStartCmd(ACTIVITY_NAME, INTENT_KEY, testCase);
+ CLog.logAndDisplay(LogLevel.INFO, cmd);
+ executeShellCommand(cmd);
+ moveActivityToDockStack("DialogTestActivity");
+ }
+
+ void stopTestCase() throws Exception{
+ executeShellCommand("am force-stop android.server.FrameTestApp");
+ }
+
+ WindowState getSingleWindow(String windowName) {
+ try {
+ mAmWmState.getWmState().getMatchingWindowState(baseWindowName + windowName, mWindowList);
+ return mWindowList.get(0);
+ } catch (Exception e) {
+ CLog.logAndDisplay(LogLevel.INFO, "Couldn't find window: " + windowName);
+ return null;
+ }
+ }
+
+ interface DialogWindowTest {
+ void doTest(WindowState parent, WindowState dialog);
+ }
+
+ void doSingleTest(DialogWindowTest t) throws Exception {
+ final String[] waitForVisible = new String[] { "TestDialog" };
+
+ mAmWmState.computeState(mDevice, waitForVisible);
+ WindowState dialog = getSingleWindow("TestDialog");
+ WindowState parent = getSingleWindow("DialogTestActivity");
+
+ t.doTest(parent, dialog);
+ stopTestCase();
+ }
+
+ void doFullscreenTest(String testCase, DialogWindowTest t) throws Exception {
+ CLog.logAndDisplay(LogLevel.INFO, "Running test fullscreen");
+ startTestCase(testCase);
+ doSingleTest(t);
+ }
+
+ void doDockedTest(String testCase, DialogWindowTest t) throws Exception {
+ CLog.logAndDisplay(LogLevel.INFO, "Running test docked");
+ startTestCaseDocked(testCase);
+ doSingleTest(t);
+ }
+
+ void doDialogTest(String testCase, DialogWindowTest t) throws Exception {
+ doFullscreenTest(testCase, t);
+ doDockedTest(testCase, t);
+ }
+
+ // With Width and Height as MATCH_PARENT we should fill
+ // the same content frame as the main activity window
+ public void testMatchParentDialog() throws Exception {
+ doDialogTest("MatchParent",
+ (WindowState parent, WindowState dialog) -> {
+ assertEquals(parent.getContentFrame(), dialog.getFrame());
+ });
+ }
+
+ // If we have LAYOUT_IN_SCREEN and LAYOUT_IN_OVERSCAN with MATCH_PARENT,
+ // we will not be constrained to the insets and so we will be the same size
+ // as the main window main frame.
+ public void testMatchParentDialogLayoutInOverscan() throws Exception {
+ doDialogTest("MatchParentLayoutInOverscan",
+ (WindowState parent, WindowState dialog) -> {
+ assertEquals(parent.getFrame(), dialog.getFrame());
+ });
+ }
+
+ static final int explicitDimension = 200;
+
+ // The default gravity for dialogs should center them.
+ public void testExplicitSizeDefaultGravity() throws Exception {
+ doDialogTest("ExplicitSize",
+ (WindowState parent, WindowState dialog) -> {
+ Rectangle contentFrame = parent.getContentFrame();
+ Rectangle expectedFrame = new Rectangle(
+ contentFrame.x + (contentFrame.width - explicitDimension)/2,
+ contentFrame.y + (contentFrame.height - explicitDimension)/2,
+ explicitDimension, explicitDimension);
+ assertEquals(expectedFrame, dialog.getFrame());
+ });
+ }
+
+ public void testExplicitSizeTopLeftGravity() throws Exception {
+ doDialogTest("ExplicitSizeTopLeftGravity",
+ (WindowState parent, WindowState dialog) -> {
+ Rectangle contentFrame = parent.getContentFrame();
+ Rectangle expectedFrame = new Rectangle(
+ contentFrame.x,
+ contentFrame.y,
+ explicitDimension,
+ explicitDimension);
+ assertEquals(expectedFrame, dialog.getFrame());
+ });
+ }
+
+ public void testExplicitSizeBottomRightGravity() throws Exception {
+ doDialogTest("ExplicitSizeBottomRightGravity",
+ (WindowState parent, WindowState dialog) -> {
+ Rectangle contentFrame = parent.getContentFrame();
+ Rectangle expectedFrame = new Rectangle(
+ contentFrame.x + contentFrame.width - explicitDimension,
+ contentFrame.y + contentFrame.height - explicitDimension,
+ explicitDimension, explicitDimension);
+ assertEquals(expectedFrame, dialog.getFrame());
+ });
+ }
+
+ // TODO: Commented out for now because it doesn't work. We end up
+ // insetting the decor on the bottom. I think this is a bug
+ // probably in the default dialog flags:
+ // b/30127373
+ // public void testOversizedDimensions() throws Exception {
+ // doDialogTest("OversizedDimensions",
+ // (WindowState parent, WindowState dialog) -> {
+ // With the default flags oversize should result in clipping to
+ // parent frame.
+ // assertEquals(parent.getContentFrame(), dialog.getFrame());
+ // });
+ // }
+
+ static final int oversizedDimension = 5000;
+ // With FLAG_LAYOUT_NO_LIMITS we should get the size we request, even if its much
+ // larger than the screen.
+ public void testOversizedDimensionsNoLimits() throws Exception {
+ doDialogTest("OversizedDimensionsNoLimits",
+ (WindowState parent, WindowState dialog) -> {
+ Rectangle contentFrame = parent.getContentFrame();
+ Rectangle expectedFrame = new Rectangle(contentFrame.x, contentFrame.y,
+ oversizedDimension, oversizedDimension);
+ assertEquals(expectedFrame, dialog.getFrame());
+ });
+ }
+
+ // If we request the MATCH_PARENT and a non-zero position, we wouldn't be
+ // able to fit all of our content, so we should be adjusted to just fit the
+ // content frame.
+ public void testExplicitPositionMatchParent() throws Exception {
+ doDialogTest("ExplicitPositionMatchParent",
+ (WindowState parent, WindowState dialog) -> {
+ assertEquals(parent.getContentFrame(),
+ dialog.getFrame());
+ });
+ }
+
+ // Unless we pass NO_LIMITS in which case our requested position should
+ // be honored.
+ public void testExplicitPositionMatchParentNoLimits() throws Exception {
+ final int explicitPosition = 100;
+ doDialogTest("ExplicitPositionMatchParentNoLimits",
+ (WindowState parent, WindowState dialog) -> {
+ Rectangle contentFrame = parent.getContentFrame();
+ Rectangle expectedFrame = new Rectangle(contentFrame.x + explicitPosition,
+ contentFrame.y + explicitPosition,
+ contentFrame.width,
+ contentFrame.height);
+ });
+ }
+
+ // We run the two focus tests fullscreen only because switching to the
+ // docked stack will strip away focus from the task anyway.
+ public void testDialogReceivesFocus() throws Exception {
+ doFullscreenTest("MatchParent",
+ (WindowState parent, WindowState dialog) -> {
+ assertEquals(dialog.getName(), mAmWmState.getWmState().getFocusedWindow());
+ });
+ }
+
+ public void testNoFocusDialog() throws Exception {
+ doFullscreenTest("NoFocus",
+ (WindowState parent, WindowState dialog) -> {
+ assertEquals(parent.getName(), mAmWmState.getWmState().getFocusedWindow());
+ });
+ }
+
+ public void testMarginsArePercentagesOfContentFrame() throws Exception {
+ float horizontalMargin = .25f;
+ float verticalMargin = .35f;
+ doDialogTest("WithMargins",
+ (WindowState parent, WindowState dialog) -> {
+ Rectangle frame = parent.getContentFrame();
+ Rectangle expectedFrame = new Rectangle(
+ (int)(horizontalMargin*frame.width + frame.x),
+ (int)(verticalMargin*frame.height + frame.y),
+ explicitDimension,
+ explicitDimension);
+ assertEquals(expectedFrame, dialog.getFrame());
+ });
+ }
+
+ public void testDialogPlacedAboveParent() throws Exception {
+ doDialogTest("MatchParent",
+ (WindowState parent, WindowState dialog) -> {
+ // Not only should the dialog be higher, but it should be
+ // leave multiple layers of space inbetween for DimLayers,
+ // etc...
+ assertTrue(dialog.getLayer() - parent.getLayer() >= 5);
+ });
+ }
+}
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/src/android/server/cts/SurfaceViewTests.java b/hostsidetests/services/activityandwindowmanager/windowmanager/src/android/server/cts/SurfaceViewTests.java
new file mode 100644
index 0000000..b929e98
--- /dev/null
+++ b/hostsidetests/services/activityandwindowmanager/windowmanager/src/android/server/cts/SurfaceViewTests.java
@@ -0,0 +1,111 @@
+/*
+ * 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.server.cts;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.awt.Rectangle;
+
+import com.android.ddmlib.Log.LogLevel;
+import com.android.tradefed.device.CollectingOutputReceiver;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.testtype.DeviceTestCase;
+
+import android.server.cts.ActivityManagerTestBase;
+import android.server.cts.WindowManagerState.WindowState;
+
+public class SurfaceViewTests extends ActivityManagerTestBase {
+ static final String ACTIVITY_NAME = "SurfaceViewTestActivity";
+ static final String INTENT_KEY = "android.server.FrameTestApp.SurfaceViewTestCase";
+ static final String baseWindowName =
+ "android.server.FrameTestApp/android.server.FrameTestApp.";
+
+ private List<WindowState> mWindowList = new ArrayList();
+
+ public void startTestCase(String testCase) throws Exception{
+ setComponentName("android.server.FrameTestApp");
+ String cmd = getAmStartCmd(ACTIVITY_NAME, INTENT_KEY, testCase);
+ CLog.logAndDisplay(LogLevel.INFO, cmd);
+ executeShellCommand(cmd);
+ }
+
+ void stopTestCase() throws Exception{
+ executeShellCommand("am force-stop android.server.FrameTestApp");
+ }
+
+ WindowState getSingleWindow(String fullWindowName) {
+ try {
+ mAmWmState.getWmState().getMatchingWindowState(fullWindowName, mWindowList);
+ return mWindowList.get(0);
+ } catch (Exception e) {
+ CLog.logAndDisplay(LogLevel.INFO, "Couldn't find window: " + fullWindowName);
+ return null;
+ }
+ }
+
+ interface SurfaceViewTest {
+ void doTest(WindowState parent, WindowState surfaceView);
+ }
+
+ void doSurfaceViewTest(String testCase, SurfaceViewTest t) throws Exception {
+ startTestCase(testCase);
+
+ String svName = "SurfaceView - " + baseWindowName + ACTIVITY_NAME;
+ final String[] waitForVisible = new String[] { svName };
+
+ mAmWmState.computeState(mDevice, waitForVisible);
+ WindowState sv = getSingleWindow(svName);
+ WindowState parent = getSingleWindow(baseWindowName + "SurfaceViewTestActivity");
+
+ t.doTest(parent, sv);
+ stopTestCase();
+ }
+
+ public void testSurfaceViewOnBottom() throws Exception {
+ doSurfaceViewTest("OnBottom",
+ (WindowState parent, WindowState sv) -> {
+ assertFalse(sv.getLayer() >= parent.getLayer());
+ });
+ }
+
+ public void testSurfaceViewOnTop() throws Exception {
+ doSurfaceViewTest("OnTop",
+ (WindowState parent, WindowState sv) -> {
+ assertFalse(parent.getLayer() >= sv.getLayer());
+ });
+ }
+
+ public void testSurfaceViewOversized() throws Exception {
+ final int oversizedDimension = 8000;
+ doSurfaceViewTest("Oversized",
+ (WindowState parent, WindowState sv) -> {
+ // The SurfaceView is allowed to be as big as it wants,
+ // but we should verify it's visually cropped to the parent bounds.
+ Rectangle parentFrame = parent.getFrame();
+ Rectangle frame = sv.getFrame();
+ assertEquals(oversizedDimension, frame.width);
+ assertEquals(oversizedDimension, frame.height);
+
+ Rectangle expectedCrop = new Rectangle(0, 0,
+ parentFrame.width - frame.x,
+ parentFrame.height - frame.y);
+ assertEquals(expectedCrop, sv.getCrop());
+ });
+ }
+}
diff --git a/libs/deviceutil/Android.mk b/libs/deviceutil/Android.mk
index bb039ca..5682b64 100644
--- a/libs/deviceutil/Android.mk
+++ b/libs/deviceutil/Android.mk
@@ -22,7 +22,7 @@
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-LOCAL_STATIC_JAVA_LIBRARIES := compatibility-device-util
+LOCAL_STATIC_JAVA_LIBRARIES := compatibility-device-util android-support-test mockito-target
LOCAL_MODULE_TAGS := optional
diff --git a/libs/deviceutil/src/android/cts/util/CtsKeyEventUtil.java b/libs/deviceutil/src/android/cts/util/CtsKeyEventUtil.java
new file mode 100644
index 0000000..dec71d7
--- /dev/null
+++ b/libs/deviceutil/src/android/cts/util/CtsKeyEventUtil.java
@@ -0,0 +1,293 @@
+/*
+ * 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.cts.util;
+
+import android.app.Instrumentation;
+import android.os.Looper;
+import android.os.SystemClock;
+import android.util.Log;
+import android.view.InputDevice;
+import android.view.KeyCharacterMap;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.inputmethod.InputMethodManager;
+
+import java.lang.reflect.Field;
+
+/**
+ * Utility class to send KeyEvents bypassing the IME. The code is similar to functions in
+ * {@link Instrumentation} and {@link android.test.InstrumentationTestCase} classes. It uses
+ * {@link InputMethodManager#dispatchKeyEventFromInputMethod(View, KeyEvent)} to send the events.
+ * After sending the events waits for idle.
+ */
+public final class CtsKeyEventUtil {
+
+ private CtsKeyEventUtil() {}
+
+ /**
+ * Sends the key events corresponding to the text to the app being instrumented.
+ *
+ * @param instrumentation the instrumentation used to run the test.
+ * @param targetView View to find the ViewRootImpl and dispatch.
+ * @param text The text to be sent. Null value returns immediately.
+ */
+ public static void sendString(final Instrumentation instrumentation, final View targetView,
+ final String text) {
+ if (text == null) {
+ return;
+ }
+
+ KeyEvent[] events = getKeyEvents(text);
+
+ if (events != null) {
+ for (int i = 0; i < events.length; i++) {
+ // We have to change the time of an event before injecting it because
+ // all KeyEvents returned by KeyCharacterMap.getEvents() have the same
+ // time stamp and the system rejects too old events. Hence, it is
+ // possible for an event to become stale before it is injected if it
+ // takes too long to inject the preceding ones.
+ sendKey(instrumentation, targetView, KeyEvent.changeTimeRepeat(
+ events[i], SystemClock.uptimeMillis(), 0 /* newRepeat */));
+ }
+ }
+ }
+
+ /**
+ * Sends a series of key events through instrumentation. For instance:
+ * sendKeys(view, KEYCODE_DPAD_LEFT, KEYCODE_DPAD_CENTER).
+ *
+ * @param instrumentation the instrumentation used to run the test.
+ * @param targetView View to find the ViewRootImpl and dispatch.
+ * @param keys The series of key codes.
+ */
+ public static void sendKeys(final Instrumentation instrumentation, final View targetView,
+ final int...keys) {
+ final int count = keys.length;
+
+ for (int i = 0; i < count; i++) {
+ try {
+ sendKeyDownUp(instrumentation, targetView, keys[i]);
+ } catch (SecurityException e) {
+ // Ignore security exceptions that are now thrown
+ // when trying to send to another app, to retain
+ // compatibility with existing tests.
+ }
+ }
+ }
+
+ /**
+ * Sends a series of key events through instrumentation. The sequence of keys is a string
+ * containing the key names as specified in KeyEvent, without the KEYCODE_ prefix. For
+ * instance: sendKeys(view, "DPAD_LEFT A B C DPAD_CENTER"). Each key can be repeated by using
+ * the N* prefix. For instance, to send two KEYCODE_DPAD_LEFT, use the following:
+ * sendKeys(view, "2*DPAD_LEFT").
+ *
+ * @param instrumentation the instrumentation used to run the test.
+ * @param targetView View to find the ViewRootImpl and dispatch.
+ * @param keysSequence The sequence of keys.
+ */
+ public static void sendKeys(final Instrumentation instrumentation, final View targetView,
+ final String keysSequence) {
+ final String[] keys = keysSequence.split(" ");
+ final int count = keys.length;
+
+ for (int i = 0; i < count; i++) {
+ String key = keys[i];
+ int repeater = key.indexOf('*');
+
+ int keyCount;
+ try {
+ keyCount = repeater == -1 ? 1 : Integer.parseInt(key.substring(0, repeater));
+ } catch (NumberFormatException e) {
+ Log.w("ActivityTestCase", "Invalid repeat count: " + key);
+ continue;
+ }
+
+ if (repeater != -1) {
+ key = key.substring(repeater + 1);
+ }
+
+ for (int j = 0; j < keyCount; j++) {
+ try {
+ final Field keyCodeField = KeyEvent.class.getField("KEYCODE_" + key);
+ final int keyCode = keyCodeField.getInt(null);
+ try {
+ sendKeyDownUp(instrumentation, targetView, keyCode);
+ } catch (SecurityException e) {
+ // Ignore security exceptions that are now thrown
+ // when trying to send to another app, to retain
+ // compatibility with existing tests.
+ }
+ } catch (NoSuchFieldException e) {
+ Log.w("ActivityTestCase", "Unknown keycode: KEYCODE_" + key);
+ break;
+ } catch (IllegalAccessException e) {
+ Log.w("ActivityTestCase", "Unknown keycode: KEYCODE_" + key);
+ break;
+ }
+ }
+ }
+ }
+
+ /**
+ * Sends an up and down key events.
+ *
+ * @param instrumentation the instrumentation used to run the test.
+ * @param targetView View to find the ViewRootImpl and dispatch.
+ * @param key The integer keycode for the event to be sent.
+ */
+ public static void sendKeyDownUp(final Instrumentation instrumentation, final View targetView,
+ final int key) {
+ sendKey(instrumentation, targetView, new KeyEvent(KeyEvent.ACTION_DOWN, key));
+ sendKey(instrumentation, targetView, new KeyEvent(KeyEvent.ACTION_UP, key));
+ }
+
+ /**
+ * Sends a key event.
+ *
+ * @param instrumentation the instrumentation used to run the test.
+ * @param targetView View to find the ViewRootImpl and dispatch.
+ * @param event KeyEvent to be send.
+ */
+ public static void sendKey(final Instrumentation instrumentation, final View targetView,
+ final KeyEvent event) {
+ validateNotAppThread();
+
+ long downTime = event.getDownTime();
+ long eventTime = event.getEventTime();
+ int action = event.getAction();
+ int code = event.getKeyCode();
+ int repeatCount = event.getRepeatCount();
+ int metaState = event.getMetaState();
+ int deviceId = event.getDeviceId();
+ int scanCode = event.getScanCode();
+ int source = event.getSource();
+ int flags = event.getFlags();
+ if (source == InputDevice.SOURCE_UNKNOWN) {
+ source = InputDevice.SOURCE_KEYBOARD;
+ }
+ if (eventTime == 0) {
+ eventTime = SystemClock.uptimeMillis();
+ }
+ if (downTime == 0) {
+ downTime = eventTime;
+ }
+
+ final KeyEvent newEvent = new KeyEvent(downTime, eventTime, action, code, repeatCount,
+ metaState, deviceId, scanCode, flags, source);
+
+ InputMethodManager imm = targetView.getContext().getSystemService(InputMethodManager.class);
+ imm.dispatchKeyEventFromInputMethod(null, newEvent);
+ instrumentation.waitForIdleSync();
+ }
+
+ /**
+ * Sends a key event while holding another modifier key down, then releases both keys and
+ * waits for idle sync. Useful for sending combinations like shift + tab.
+ *
+ * @param instrumentation the instrumentation used to run the test.
+ * @param targetView View to find the ViewRootImpl and dispatch.
+ * @param keyCodeToSend The integer keycode for the event to be sent.
+ * @param modifierKeyCodeToHold The integer keycode of the modifier to be held.
+ */
+ public static void sendKeyWhileHoldingModifier(final Instrumentation instrumentation,
+ final View targetView, final int keyCodeToSend,
+ final int modifierKeyCodeToHold) {
+ final int metaState = getMetaStateForModifierKeyCode(modifierKeyCodeToHold);
+ final long downTime = SystemClock.uptimeMillis();
+
+ final KeyEvent holdKeyDown = new KeyEvent(downTime, downTime, KeyEvent.ACTION_DOWN,
+ modifierKeyCodeToHold, 0 /* repeat */);
+ sendKey(instrumentation ,targetView, holdKeyDown);
+
+ final KeyEvent keyDown = new KeyEvent(downTime, downTime, KeyEvent.ACTION_DOWN,
+ keyCodeToSend, 0 /* repeat */, metaState);
+ sendKey(instrumentation, targetView, keyDown);
+
+ final KeyEvent keyUp = new KeyEvent(downTime, downTime, KeyEvent.ACTION_UP,
+ keyCodeToSend, 0 /* repeat */, metaState);
+ sendKey(instrumentation, targetView, keyUp);
+
+ final KeyEvent holdKeyUp = new KeyEvent(downTime, downTime, KeyEvent.ACTION_UP,
+ modifierKeyCodeToHold, 0 /* repeat */);
+ sendKey(instrumentation, targetView, holdKeyUp);
+
+ instrumentation.waitForIdleSync();
+ }
+
+ private static int getMetaStateForModifierKeyCode(int modifierKeyCode) {
+ if (!KeyEvent.isModifierKey(modifierKeyCode)) {
+ throw new IllegalArgumentException("Modifier key expected, but got: "
+ + KeyEvent.keyCodeToString(modifierKeyCode));
+ }
+
+ int metaState;
+ switch (modifierKeyCode) {
+ case KeyEvent.KEYCODE_SHIFT_LEFT:
+ metaState = KeyEvent.META_SHIFT_LEFT_ON;
+ break;
+ case KeyEvent.KEYCODE_SHIFT_RIGHT:
+ metaState = KeyEvent.META_SHIFT_RIGHT_ON;
+ break;
+ case KeyEvent.KEYCODE_ALT_LEFT:
+ metaState = KeyEvent.META_ALT_LEFT_ON;
+ break;
+ case KeyEvent.KEYCODE_ALT_RIGHT:
+ metaState = KeyEvent.META_ALT_RIGHT_ON;
+ break;
+ case KeyEvent.KEYCODE_CTRL_LEFT:
+ metaState = KeyEvent.META_CTRL_LEFT_ON;
+ break;
+ case KeyEvent.KEYCODE_CTRL_RIGHT:
+ metaState = KeyEvent.META_CTRL_RIGHT_ON;
+ break;
+ case KeyEvent.KEYCODE_META_LEFT:
+ metaState = KeyEvent.META_META_LEFT_ON;
+ break;
+ case KeyEvent.KEYCODE_META_RIGHT:
+ metaState = KeyEvent.META_META_RIGHT_ON;
+ break;
+ case KeyEvent.KEYCODE_SYM:
+ metaState = KeyEvent.META_SYM_ON;
+ break;
+ case KeyEvent.KEYCODE_NUM:
+ metaState = KeyEvent.META_NUM_LOCK_ON;
+ break;
+ case KeyEvent.KEYCODE_FUNCTION:
+ metaState = KeyEvent.META_FUNCTION_ON;
+ break;
+ default:
+ // Safety net: all modifier keys need to have at least one meta state associated.
+ throw new UnsupportedOperationException("No meta state associated with "
+ + "modifier key: " + KeyEvent.keyCodeToString(modifierKeyCode));
+ }
+
+ return KeyEvent.normalizeMetaState(metaState);
+ }
+
+ private static KeyEvent[] getKeyEvents(final String text) {
+ KeyCharacterMap keyCharacterMap = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
+ return keyCharacterMap.getEvents(text.toCharArray());
+ }
+
+ private static void validateNotAppThread() {
+ if (Looper.myLooper() == Looper.getMainLooper()) {
+ throw new RuntimeException(
+ "This method can not be called from the main application thread");
+ }
+ }
+}
diff --git a/libs/deviceutil/src/android/cts/util/CtsTouchUtils.java b/libs/deviceutil/src/android/cts/util/CtsTouchUtils.java
new file mode 100644
index 0000000..6f1429c
--- /dev/null
+++ b/libs/deviceutil/src/android/cts/util/CtsTouchUtils.java
@@ -0,0 +1,307 @@
+/*
+ * 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.cts.util;
+
+import android.app.Instrumentation;
+import android.app.UiAutomation;
+import android.os.SystemClock;
+import android.view.InputDevice;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.ViewGroup;
+
+/**
+ * Test utilities for touch emulation.
+ */
+public final class CtsTouchUtils {
+
+ private CtsTouchUtils() {}
+
+ /**
+ * Emulates a tap in the center of the passed {@link View}.
+ *
+ * @param instrumentation the instrumentation used to run the test
+ * @param view the view to "tap"
+ */
+ public static void emulateTapOnViewCenter(Instrumentation instrumentation, View view) {
+ emulateTapOnScreen(instrumentation, view, view.getWidth() / 2, view.getHeight() / 2);
+ }
+
+ /**
+ * Emulates a tap on a point relative to the top-left corner of the passed {@link View}. Offset
+ * parameters are used to compute the final screen coordinates of the tap point.
+ *
+ * @param instrumentation the instrumentation used to run the test
+ * @param anchorView the anchor view to determine the tap location on the screen
+ * @param offsetX extra X offset for the tap
+ * @param offsetY extra Y offset for the tap
+ */
+ public static void emulateTapOnScreen(Instrumentation instrumentation, View anchorView,
+ int offsetX, int offsetY) {
+ // Get anchor coordinates on the screen
+ final int[] viewOnScreenXY = new int[2];
+ anchorView.getLocationOnScreen(viewOnScreenXY);
+ int emulatedTapX = viewOnScreenXY[0] + offsetX;
+ int emulatedTapY = viewOnScreenXY[1] + offsetY;
+
+ // Inject DOWN event
+ long downTime = SystemClock.uptimeMillis();
+ MotionEvent eventDown = MotionEvent.obtain(
+ downTime, downTime, MotionEvent.ACTION_DOWN, emulatedTapX, emulatedTapY, 1);
+ instrumentation.sendPointerSync(eventDown);
+ eventDown.recycle();
+
+ // Inject MOVE event
+ long moveTime = SystemClock.uptimeMillis();
+ final int touchSlop = ViewConfiguration.get(anchorView.getContext()).getScaledTouchSlop();
+ MotionEvent eventMove = MotionEvent.obtain(downTime, moveTime, MotionEvent.ACTION_MOVE,
+ emulatedTapX + (touchSlop / 2.0f), emulatedTapY + (touchSlop / 2.0f), 1);
+ instrumentation.sendPointerSync(eventMove);
+ eventMove.recycle();
+
+ // Inject UP event
+ long upTime = SystemClock.uptimeMillis();
+ MotionEvent eventUp = MotionEvent.obtain(
+ upTime, upTime, MotionEvent.ACTION_UP, emulatedTapX, emulatedTapY, 1);
+ instrumentation.sendPointerSync(eventUp);
+ eventUp.recycle();
+
+ // Wait for the system to process all events in the queue
+ instrumentation.waitForIdleSync();
+ }
+
+ /**
+ * Emulates a drag gesture across the screen.
+ *
+ * @param instrumentation the instrumentation used to run the test
+ * @param dragStartX Start X of the emulated drag gesture
+ * @param dragStartY Start Y of the emulated drag gesture
+ * @param dragAmountX X amount of the emulated drag gesture
+ * @param dragAmountY Y amount of the emulated drag gesture
+ */
+ public static void emulateDragGesture(Instrumentation instrumentation,
+ int dragStartX, int dragStartY, int dragAmountX, int dragAmountY) {
+ emulateDragGesture(instrumentation, dragStartX, dragStartY, dragAmountX, dragAmountY,
+ 2000, 20);
+ }
+
+ private static void emulateDragGesture(Instrumentation instrumentation,
+ int dragStartX, int dragStartY, int dragAmountX, int dragAmountY,
+ int dragDurationMs, int moveEventCount) {
+ // We are using the UiAutomation object to inject events so that drag works
+ // across view / window boundaries (such as for the emulated drag and drop
+ // sequences)
+ final UiAutomation uiAutomation = instrumentation.getUiAutomation();
+
+ // Inject DOWN event
+ long downTime = SystemClock.uptimeMillis();
+ MotionEvent eventDown = MotionEvent.obtain(
+ downTime, downTime, MotionEvent.ACTION_DOWN, dragStartX, dragStartY, 1);
+ eventDown.setSource(InputDevice.SOURCE_TOUCHSCREEN);
+ uiAutomation.injectInputEvent(eventDown, true);
+ eventDown.recycle();
+
+ // Inject a sequence of MOVE events that emulate the "move" part of the gesture
+ for (int i = 0; i < moveEventCount; i++) {
+ long moveTime = SystemClock.uptimeMillis();
+ // Note that we divide by (moveEventCount - 1) so that our last MOVE event is
+ // at the same coordinates as the subsequent UP event.
+ final int moveX = dragStartX + dragAmountX * i / (moveEventCount - 1);
+ final int moveY = dragStartY + dragAmountY * i / (moveEventCount - 1);
+ MotionEvent eventMove = MotionEvent.obtain(
+ moveTime, moveTime, MotionEvent.ACTION_MOVE, moveX, moveY, 1);
+ eventMove.setSource(InputDevice.SOURCE_TOUCHSCREEN);
+ uiAutomation.injectInputEvent(eventMove, true);
+ eventMove.recycle();
+ // sleep for a bit to emulate the overall swipe gesture
+ SystemClock.sleep(dragDurationMs / moveEventCount);
+ }
+
+ // Inject UP event
+ long upTime = SystemClock.uptimeMillis();
+ MotionEvent eventUp = MotionEvent.obtain(
+ upTime, upTime, MotionEvent.ACTION_UP, dragStartX + dragAmountX,
+ dragStartY + dragAmountY, 1);
+ eventUp.setSource(InputDevice.SOURCE_TOUCHSCREEN);
+ uiAutomation.injectInputEvent(eventUp, true);
+ eventUp.recycle();
+
+ // Wait for the system to process all events in the queue
+ instrumentation.waitForIdleSync();
+ }
+
+ /**
+ * Emulates a fling gesture across the horizontal center of the passed view.
+ *
+ * @param instrumentation the instrumentation used to run the test
+ * @param view the view to fling
+ * @param isDownwardsFlingGesture if <code>true</code>, the emulated fling will
+ * be a downwards gesture
+ * @return The vertical amount of emulated fling in pixels
+ */
+ public static int emulateFlingGesture(Instrumentation instrumentation,
+ View view, boolean isDownwardsFlingGesture) {
+ final ViewConfiguration configuration = ViewConfiguration.get(view.getContext());
+ final int flingVelocity = (configuration.getScaledMinimumFlingVelocity() +
+ configuration.getScaledMaximumFlingVelocity()) / 2;
+ // Get view coordinates on the screen
+ final int[] viewOnScreenXY = new int[2];
+ view.getLocationOnScreen(viewOnScreenXY);
+
+ // Our fling gesture will be from 25% height of the view to 75% height of the view
+ // for downwards fling gesture, and the other way around for upwards fling gesture
+ final int viewHeight = view.getHeight();
+ final int x = viewOnScreenXY[0] + view.getWidth() / 2;
+ final int startY = isDownwardsFlingGesture ? viewOnScreenXY[1] + viewHeight / 4
+ : viewOnScreenXY[1] + 3 * viewHeight / 4;
+ final int amountY = isDownwardsFlingGesture ? viewHeight / 2 : -viewHeight / 2;
+
+ // Compute fling gesture duration based on the distance (50% height of the view) and
+ // fling velocity
+ final int durationMs = (1000 * viewHeight) / (2 * flingVelocity);
+
+ // And do the same event injection sequence as our generic drag gesture
+ emulateDragGesture(instrumentation, x, startY, 0, amountY, durationMs, 3);
+
+ return amountY;
+ }
+
+ private static class ViewStateSnapshot {
+ final View mFirst;
+ final View mLast;
+ final int mFirstTop;
+ final int mLastBottom;
+ final int mChildCount;
+ private ViewStateSnapshot(ViewGroup viewGroup) {
+ mChildCount = viewGroup.getChildCount();
+ if (mChildCount == 0) {
+ mFirst = mLast = null;
+ mFirstTop = mLastBottom = Integer.MIN_VALUE;
+ } else {
+ mFirst = viewGroup.getChildAt(0);
+ mLast = viewGroup.getChildAt(mChildCount - 1);
+ mFirstTop = mFirst.getTop();
+ mLastBottom = mLast.getBottom();
+ }
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ final ViewStateSnapshot that = (ViewStateSnapshot) o;
+ return mFirstTop == that.mFirstTop &&
+ mLastBottom == that.mLastBottom &&
+ mFirst == that.mFirst &&
+ mLast == that.mLast &&
+ mChildCount == that.mChildCount;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = mFirst != null ? mFirst.hashCode() : 0;
+ result = 31 * result + (mLast != null ? mLast.hashCode() : 0);
+ result = 31 * result + mFirstTop;
+ result = 31 * result + mLastBottom;
+ result = 31 * result + mChildCount;
+ return result;
+ }
+ }
+
+ /**
+ * Emulates a scroll to the bottom of the specified {@link ViewGroup}.
+ *
+ * @param instrumentation the instrumentation used to run the test
+ * @param viewGroup View group
+ */
+ public static void emulateScrollToBottom(Instrumentation instrumentation, ViewGroup viewGroup) {
+ final int[] viewGroupOnScreenXY = new int[2];
+ viewGroup.getLocationOnScreen(viewGroupOnScreenXY);
+
+ final int emulatedX = viewGroupOnScreenXY[0] + viewGroup.getWidth() / 2;
+ final int emulatedStartY = viewGroupOnScreenXY[1] + 3 * viewGroup.getHeight() / 4;
+ final int swipeAmount = viewGroup.getHeight() / 2;
+
+ ViewStateSnapshot prev;
+ ViewStateSnapshot next = new ViewStateSnapshot(viewGroup);
+ do {
+ prev = next;
+ emulateDragGesture(instrumentation, emulatedX, emulatedStartY, 0, -swipeAmount,
+ 300, 10);
+ next = new ViewStateSnapshot(viewGroup);
+ } while (!prev.equals(next));
+ }
+
+ /**
+ * Emulates a long click in the center of the passed {@link View}.
+ *
+ * @param instrumentation the instrumentation used to run the test
+ * @param view the view to "long click"
+ */
+ public static void emulateLongClick(Instrumentation instrumentation, View view) {
+ emulateLongClick(instrumentation, view, 0);
+ }
+
+ /**
+ * Emulates a long click in the center of the passed {@link View}.
+ *
+ * @param instrumentation the instrumentation used to run the test
+ * @param view the view to "long click"
+ * @param extraWaitMs the duration of emulated long click in milliseconds starting
+ * after system-level long press timeout.
+ */
+ public static void emulateLongClick(Instrumentation instrumentation, View view,
+ long extraWaitMs) {
+ // Use instrumentation to emulate a tap on the spinner to bring down its popup
+ final int[] viewOnScreenXY = new int[2];
+ view.getLocationOnScreen(viewOnScreenXY);
+ int emulatedTapX = viewOnScreenXY[0] + view.getWidth() / 2;
+ int emulatedTapY = viewOnScreenXY[1] + view.getHeight() / 2;
+
+ // Inject DOWN event
+ long downTime = SystemClock.uptimeMillis();
+ MotionEvent eventDown = MotionEvent.obtain(
+ downTime, downTime, MotionEvent.ACTION_DOWN, emulatedTapX, emulatedTapY, 1);
+ instrumentation.sendPointerSync(eventDown);
+ eventDown.recycle();
+
+ // Inject MOVE event
+ long moveTime = SystemClock.uptimeMillis();
+ MotionEvent eventMove = MotionEvent.obtain(
+ moveTime, moveTime, MotionEvent.ACTION_MOVE, emulatedTapX, emulatedTapY, 1);
+ instrumentation.sendPointerSync(eventMove);
+ eventMove.recycle();
+
+ SystemClock.sleep((long) (ViewConfiguration.getLongPressTimeout() * 1.5f) + extraWaitMs);
+
+ // Inject UP event
+ long upTime = SystemClock.uptimeMillis();
+ MotionEvent eventUp = MotionEvent.obtain(
+ upTime, upTime, MotionEvent.ACTION_UP, emulatedTapX, emulatedTapY, 1);
+ instrumentation.sendPointerSync(eventUp);
+ eventUp.recycle();
+
+ // Wait for the system to process all events in the queue
+ instrumentation.waitForIdleSync();
+ }
+}
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/FakeKeys.java b/libs/deviceutil/src/android/cts/util/FakeKeys.java
similarity index 99%
rename from hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/FakeKeys.java
rename to libs/deviceutil/src/android/cts/util/FakeKeys.java
index 11df8e5..f3814a4 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/FakeKeys.java
+++ b/libs/deviceutil/src/android/cts/util/FakeKeys.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.cts.deviceowner;
+package android.cts.util;
// Copied from cts/tests/tests/keystore/src/android/keystore/cts/AndroidKeyStoreTest.java
diff --git a/libs/deviceutil/src/android/cts/util/KeyEventUtil.java b/libs/deviceutil/src/android/cts/util/KeyEventUtil.java
deleted file mode 100644
index b6d09d8..0000000
--- a/libs/deviceutil/src/android/cts/util/KeyEventUtil.java
+++ /dev/null
@@ -1,202 +0,0 @@
-/*
- * 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.cts.util;
-
-import android.app.Instrumentation;
-import android.os.Looper;
-import android.os.SystemClock;
-import android.util.Log;
-import android.view.InputDevice;
-import android.view.KeyCharacterMap;
-import android.view.KeyEvent;
-import android.view.View;
-import android.view.inputmethod.InputMethodManager;
-
-import java.lang.reflect.Field;
-
-/**
- * Utility class to send KeyEvents to TextView bypassing the IME. The code is similar to functions
- * in {@link Instrumentation} and {@link android.test.InstrumentationTestCase} classes. It uses
- * {@link InputMethodManager#dispatchKeyEventFromInputMethod(View, KeyEvent)} to send the events.
- * After sending the events waits for idle.
- */
-public class KeyEventUtil {
- private final Instrumentation mInstrumentation;
-
- public KeyEventUtil(Instrumentation instrumentation) {
- this.mInstrumentation = instrumentation;
- }
-
- /**
- * Sends the key events corresponding to the text to the app being instrumented.
- *
- * @param targetView View to find the ViewRootImpl and dispatch.
- * @param text The text to be sent. Null value returns immediately.
- */
- public final void sendString(final View targetView, final String text) {
- if (text == null) {
- return;
- }
-
- KeyEvent[] events = getKeyEvents(text);
-
- if (events != null) {
- for (int i = 0; i < events.length; i++) {
- // We have to change the time of an event before injecting it because
- // all KeyEvents returned by KeyCharacterMap.getEvents() have the same
- // time stamp and the system rejects too old events. Hence, it is
- // possible for an event to become stale before it is injected if it
- // takes too long to inject the preceding ones.
- sendKey(targetView, KeyEvent.changeTimeRepeat(events[i], SystemClock.uptimeMillis(),
- 0));
- }
- }
- }
-
- /**
- * Sends a series of key events through instrumentation. For instance:
- * sendKeys(view, KEYCODE_DPAD_LEFT, KEYCODE_DPAD_CENTER).
- *
- * @param targetView View to find the ViewRootImpl and dispatch.
- * @param keys The series of key codes.
- */
- public final void sendKeys(final View targetView, final int...keys) {
- final int count = keys.length;
-
- for (int i = 0; i < count; i++) {
- try {
- sendKeyDownUp(targetView, keys[i]);
- } catch (SecurityException e) {
- // Ignore security exceptions that are now thrown
- // when trying to send to another app, to retain
- // compatibility with existing tests.
- }
- }
- }
-
- /**
- * Sends a series of key events through instrumentation. The sequence of keys is a string
- * containing the key names as specified in KeyEvent, without the KEYCODE_ prefix. For
- * instance: sendKeys(view, "DPAD_LEFT A B C DPAD_CENTER"). Each key can be repeated by using
- * the N* prefix. For instance, to send two KEYCODE_DPAD_LEFT, use the following:
- * sendKeys(view, "2*DPAD_LEFT").
- *
- * @param targetView View to find the ViewRootImpl and dispatch.
- * @param keysSequence The sequence of keys.
- */
- public final void sendKeys(final View targetView, final String keysSequence) {
- final String[] keys = keysSequence.split(" ");
- final int count = keys.length;
-
- for (int i = 0; i < count; i++) {
- String key = keys[i];
- int repeater = key.indexOf('*');
-
- int keyCount;
- try {
- keyCount = repeater == -1 ? 1 : Integer.parseInt(key.substring(0, repeater));
- } catch (NumberFormatException e) {
- Log.w("ActivityTestCase", "Invalid repeat count: " + key);
- continue;
- }
-
- if (repeater != -1) {
- key = key.substring(repeater + 1);
- }
-
- for (int j = 0; j < keyCount; j++) {
- try {
- final Field keyCodeField = KeyEvent.class.getField("KEYCODE_" + key);
- final int keyCode = keyCodeField.getInt(null);
- try {
- sendKeyDownUp(targetView, keyCode);
- } catch (SecurityException e) {
- // Ignore security exceptions that are now thrown
- // when trying to send to another app, to retain
- // compatibility with existing tests.
- }
- } catch (NoSuchFieldException e) {
- Log.w("ActivityTestCase", "Unknown keycode: KEYCODE_" + key);
- break;
- } catch (IllegalAccessException e) {
- Log.w("ActivityTestCase", "Unknown keycode: KEYCODE_" + key);
- break;
- }
- }
- }
- }
-
- /**
- * Sends an up and down key events.
- *
- * @param targetView View to find the ViewRootImpl and dispatch.
- * @param key The integer keycode for the event to be send.
- */
- public final void sendKeyDownUp(final View targetView, final int key) {
- sendKey(targetView, new KeyEvent(KeyEvent.ACTION_DOWN, key));
- sendKey(targetView, new KeyEvent(KeyEvent.ACTION_UP, key));
- }
-
- /**
- * Sends a key event.
- *
- * @param targetView View to find the ViewRootImpl and dispatch.
- * @param event KeyEvent to be send.
- */
- public final void sendKey(final View targetView, final KeyEvent event) {
- validateNotAppThread();
-
- long downTime = event.getDownTime();
- long eventTime = event.getEventTime();
- int action = event.getAction();
- int code = event.getKeyCode();
- int repeatCount = event.getRepeatCount();
- int metaState = event.getMetaState();
- int deviceId = event.getDeviceId();
- int scancode = event.getScanCode();
- int source = event.getSource();
- int flags = event.getFlags();
- if (source == InputDevice.SOURCE_UNKNOWN) {
- source = InputDevice.SOURCE_KEYBOARD;
- }
- if (eventTime == 0) {
- eventTime = SystemClock.uptimeMillis();
- }
- if (downTime == 0) {
- downTime = eventTime;
- }
-
- final KeyEvent newEvent = new KeyEvent(downTime, eventTime, action, code, repeatCount,
- metaState, deviceId, scancode, flags, source);
-
- InputMethodManager imm = targetView.getContext().getSystemService(InputMethodManager.class);
- imm.dispatchKeyEventFromInputMethod(null, newEvent);
- mInstrumentation.waitForIdleSync();
- }
-
- private KeyEvent[] getKeyEvents(final String text) {
- KeyCharacterMap keyCharacterMap = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
- return keyCharacterMap.getEvents(text.toCharArray());
- }
-
- private void validateNotAppThread() {
- if (Looper.myLooper() == Looper.getMainLooper()) {
- throw new RuntimeException(
- "This method can not be called from the main application thread");
- }
- }
-}
diff --git a/libs/deviceutil/src/android/cts/util/PollingCheck.java b/libs/deviceutil/src/android/cts/util/PollingCheck.java
index 3a08c7e..0e255a1 100644
--- a/libs/deviceutil/src/android/cts/util/PollingCheck.java
+++ b/libs/deviceutil/src/android/cts/util/PollingCheck.java
@@ -82,4 +82,13 @@
}
}.run();
}
+
+ public static void waitFor(long timeout, final PollingCheckCondition condition) {
+ new PollingCheck(timeout) {
+ @Override
+ protected boolean check() {
+ return condition.canProceed();
+ }
+ }.run();
+ }
}
diff --git a/libs/deviceutil/src/android/cts/util/WidgetTestUtils.java b/libs/deviceutil/src/android/cts/util/WidgetTestUtils.java
index 813672e..a1197cc 100644
--- a/libs/deviceutil/src/android/cts/util/WidgetTestUtils.java
+++ b/libs/deviceutil/src/android/cts/util/WidgetTestUtils.java
@@ -16,27 +16,42 @@
package android.cts.util;
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
+import android.app.Instrumentation;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
-
-import java.io.IOException;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.text.Editable;
+import android.text.TextUtils;
+import android.view.View;
+import android.view.ViewTreeObserver;
import junit.framework.Assert;
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.Description;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import static android.view.ViewTreeObserver.*;
+import static org.mockito.Matchers.*;
+
/**
* The useful methods for widget test.
*/
public class WidgetTestUtils {
/**
- * Assert that two bitmaps are equal.
+ * Assert that two bitmaps have identical content (same dimensions, same configuration,
+ * same pixel content).
*
- * @param Bitmap b1 the first bitmap which needs to compare.
- * @param Bitmap b2 the second bitmap which needs to compare.
+ * @param b1 the first bitmap which needs to compare.
+ * @param b2 the second bitmap which needs to compare.
*/
public static void assertEquals(Bitmap b1, Bitmap b2) {
if (b1 == b2) {
@@ -135,4 +150,94 @@
options.inPreferredConfig = config;
return BitmapFactory.decodeResource(resources, resId, options);
}
+
+ /**
+ * Argument matcher for equality check of a CharSequence.
+ *
+ * @param expected expected CharSequence
+ *
+ * @return
+ */
+ public static CharSequence sameCharSequence(final CharSequence expected) {
+ return argThat(new BaseMatcher<CharSequence>() {
+ @Override
+ public boolean matches(Object o) {
+ if (o instanceof CharSequence) {
+ return TextUtils.equals(expected, (CharSequence) o);
+ }
+ return false;
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("doesn't match " + expected);
+ }
+ });
+ }
+
+ /**
+ * Argument matcher for equality check of an Editable.
+ *
+ * @param expected expected Editable
+ *
+ * @return
+ */
+ public static Editable sameEditable(final Editable expected) {
+ return argThat(new BaseMatcher<Editable>() {
+ @Override
+ public boolean matches(Object o) {
+ if (o instanceof Editable) {
+ return TextUtils.equals(expected, (Editable) o);
+ }
+ return false;
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("doesn't match " + expected);
+ }
+ });
+ }
+
+ /**
+ * Runs the specified Runnable on the main thread and ensures that the specified View's tree is
+ * drawn before returning.
+ *
+ * @param instrumentation the instrumentation used to run the test
+ * @param view the view whose tree should be drawn before returning
+ * @param runner the runnable to run on the main thread, or {@code null} to
+ * simply force invalidation and a draw pass
+ */
+ public static void runOnMainAndDrawSync(@NonNull Instrumentation instrumentation,
+ @NonNull final View view,
+ @Nullable final Runnable runner) {
+ final CountDownLatch latch = new CountDownLatch(1);
+
+ instrumentation.runOnMainSync(() -> {
+ final ViewTreeObserver observer = view.getViewTreeObserver();
+ final OnDrawListener listener = new OnDrawListener() {
+ @Override
+ public void onDraw() {
+ observer.removeOnDrawListener(this);
+ view.post(() -> latch.countDown());
+ }
+ };
+
+ observer.addOnDrawListener(listener);
+
+ if (runner != null) {
+ runner.run();
+ } else {
+ view.invalidate();
+ }
+ });
+
+ try {
+ Assert.assertTrue("Expected draw pass occurred within 5 seconds",
+ latch.await(5, TimeUnit.SECONDS));
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
}
diff --git a/tests/app/Android.mk b/tests/app/Android.mk
index 775e679..6ce0148 100644
--- a/tests/app/Android.mk
+++ b/tests/app/Android.mk
@@ -23,7 +23,7 @@
LOCAL_JAVA_LIBRARIES := android.test.runner telephony-common voip-common org.apache.http.legacy
-LOCAL_STATIC_JAVA_LIBRARIES := ctsdeviceutil ctstestrunner ctstestserver mockito-target
+LOCAL_STATIC_JAVA_LIBRARIES := ctsdeviceutil ctstestrunner ctstestserver mockito-target android-support-test
LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/app/app/AndroidManifest.xml b/tests/app/app/AndroidManifest.xml
index 10a6792..4057aa0 100644
--- a/tests/app/app/AndroidManifest.xml
+++ b/tests/app/app/AndroidManifest.xml
@@ -196,7 +196,7 @@
</intent-filter>
</activity>
- <activity android:name="android.app.stubs.ChildTabActivity" android:label="ChildTabActivity" />
+ <activity android:name="android.app.stubs.FragmentResultActivity" android:label="FragmentResultActivity" />
<activity android:name="android.app.stubs.LauncherActivityStub"
android:label="LauncherActivityStub" >
@@ -329,6 +329,9 @@
<activity android:name="android.app.stubs.KeyboardShortcutsActivity" />
+ <activity android:name="android.app.stubs.NewDocumentTestActivity"
+ android:documentLaunchMode="intoExisting" />
+
</application>
</manifest>
diff --git a/tests/app/app/src/android/app/stubs/ChildTabActivity.java b/tests/app/app/src/android/app/stubs/ChildTabActivity.java
deleted file mode 100644
index 1f0fb37..0000000
--- a/tests/app/app/src/android/app/stubs/ChildTabActivity.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright (C) 2009 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.app.stubs;
-
-import android.app.Activity;
-
-/**
- * An empty activity for the TabActivity test
- */
-public class ChildTabActivity extends Activity {
-}
diff --git a/tests/app/app/src/android/app/stubs/FragmentResultActivity.java b/tests/app/app/src/android/app/stubs/FragmentResultActivity.java
new file mode 100644
index 0000000..364d093
--- /dev/null
+++ b/tests/app/app/src/android/app/stubs/FragmentResultActivity.java
@@ -0,0 +1,39 @@
+/*
+ * 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.app.stubs;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+
+/**
+ * A simple Activity used to return a result.
+ */
+public class FragmentResultActivity extends Activity {
+ public static final String EXTRA_RESULT_CODE = "result";
+ public static final String EXTRA_RESULT_CONTENT = "result_content";
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ int resultCode = getIntent().getIntExtra(EXTRA_RESULT_CODE, Activity.RESULT_OK);
+ String result = getIntent().getStringExtra(EXTRA_RESULT_CONTENT);
+ Intent intent = new Intent();
+ intent.putExtra(EXTRA_RESULT_CONTENT, result);
+ setResult(resultCode, intent);
+ finish();
+ }
+}
diff --git a/tests/app/app/src/android/app/stubs/FragmentTestActivity.java b/tests/app/app/src/android/app/stubs/FragmentTestActivity.java
index a421895..a5f54bc 100644
--- a/tests/app/app/src/android/app/stubs/FragmentTestActivity.java
+++ b/tests/app/app/src/android/app/stubs/FragmentTestActivity.java
@@ -19,6 +19,7 @@
import android.app.Activity;
import android.app.Fragment;
import android.content.Context;
+import android.content.Intent;
import android.os.Bundle;
import android.transition.Transition;
import android.transition.Transition.TransitionListener;
diff --git a/tests/app/app/src/android/app/stubs/MockTabActivity.java b/tests/app/app/src/android/app/stubs/MockTabActivity.java
index 247cfe0..722ac69 100644
--- a/tests/app/app/src/android/app/stubs/MockTabActivity.java
+++ b/tests/app/app/src/android/app/stubs/MockTabActivity.java
@@ -40,7 +40,7 @@
final TabHost tabHost = getTabHost();
tabHost.addTab(tabHost.newTabSpec(TAB1).setIndicator(TAB1)
- .setContent(new Intent(this, ChildTabActivity.class)));
+ .setContent(new Intent(this, MockActivity.class)));
tabHost.addTab(tabHost.newTabSpec(TAB2).setIndicator(TAB2)
.setContent(new Intent(this, MockActivity.class)));
diff --git a/tests/app/app/src/android/app/stubs/NewDocumentTestActivity.java b/tests/app/app/src/android/app/stubs/NewDocumentTestActivity.java
new file mode 100644
index 0000000..b12d969
--- /dev/null
+++ b/tests/app/app/src/android/app/stubs/NewDocumentTestActivity.java
@@ -0,0 +1,36 @@
+/*
+ * 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.app.stubs;
+
+import android.app.Activity;
+import android.content.Intent;
+
+public class NewDocumentTestActivity extends Activity {
+ public static final String NOTIFY_RESUME = "android.app.stubs.NOTIFY_RESUME";
+ public static final String NOTIFY_NEW_INTENT = "android.app.stubs.NOTIFY_NEW_INTENT";
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ sendBroadcast(new Intent(NOTIFY_RESUME));
+ }
+
+ @Override
+ public void onNewIntent(Intent intent) {
+ sendBroadcast(new Intent(NOTIFY_NEW_INTENT));
+ }
+}
diff --git a/tests/app/src/android/app/cts/ActivityActionModeTest.java b/tests/app/src/android/app/cts/ActivityActionModeTest.java
new file mode 100644
index 0000000..b74e737
--- /dev/null
+++ b/tests/app/src/android/app/cts/ActivityActionModeTest.java
@@ -0,0 +1,79 @@
+/*
+ * 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.app.cts;
+
+import static org.mockito.Mockito.*;
+
+import android.app.stubs.MockActivity;
+import android.support.test.annotation.UiThreadTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.view.ActionMode;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.*;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ActivityActionModeTest {
+
+ private ActionMode.Callback mCallback;
+
+ @Rule
+ public ActivityTestRule<MockActivity> mActivityRule =
+ new ActivityTestRule<>(MockActivity.class);
+
+ @Before
+ public void setUp() {
+ mCallback = mock(ActionMode.Callback.class);
+ when(mCallback.onCreateActionMode(any(), any())).thenReturn(true);
+ when(mCallback.onPrepareActionMode(any(), any())).thenReturn(true);
+ }
+
+ @Test
+ @UiThreadTest
+ public void testStartPrimaryActionMode() {
+ final ActionMode mode = mActivityRule.getActivity().startActionMode(
+ mCallback, ActionMode.TYPE_PRIMARY);
+
+ assertNotNull(mode);
+ assertEquals(ActionMode.TYPE_PRIMARY, mode.getType());
+ }
+
+ @Test
+ @UiThreadTest
+ public void testStartFloatingActionMode() {
+ final ActionMode mode = mActivityRule.getActivity().startActionMode(
+ mCallback, ActionMode.TYPE_FLOATING);
+
+ assertNotNull(mode);
+ assertEquals(ActionMode.TYPE_FLOATING, mode.getType());
+ }
+
+ @Test
+ @UiThreadTest
+ public void testStartTypelessActionMode() {
+ final ActionMode mode = mActivityRule.getActivity().startActionMode(mCallback);
+
+ assertNotNull(mode);
+ assertEquals(ActionMode.TYPE_PRIMARY, mode.getType());
+ }
+}
diff --git a/tests/app/src/android/app/cts/FragmentReceiveResultTest.java b/tests/app/src/android/app/cts/FragmentReceiveResultTest.java
new file mode 100644
index 0000000..f20113b
--- /dev/null
+++ b/tests/app/src/android/app/cts/FragmentReceiveResultTest.java
@@ -0,0 +1,156 @@
+/*
+ * 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.app.cts;
+
+import static org.mockito.Mockito.*;
+
+import android.app.Activity;
+import android.app.Fragment;
+import android.app.PendingIntent;
+import android.app.stubs.FragmentResultActivity;
+import android.app.stubs.FragmentTestActivity;
+import android.app.stubs.R;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.mockito.ArgumentCaptor;
+
+/**
+ * Tests Fragment's startActivityForResult and startIntentSenderForResult.
+ */
+public class FragmentReceiveResultTest extends
+ ActivityInstrumentationTestCase2<FragmentTestActivity> {
+
+ private FragmentTestActivity mActivity;
+ private Fragment mFragment;
+
+ public FragmentReceiveResultTest() {
+ super(FragmentTestActivity.class);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mActivity = getActivity();
+ mFragment = attachTestFragment();
+ }
+
+ @SmallTest
+ public void testStartActivityForResultOk() {
+ startActivityForResult(10, Activity.RESULT_OK, "content 10");
+
+ ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
+ verify(mFragment, times(1))
+ .onActivityResult(eq(10), eq(Activity.RESULT_OK), captor.capture());
+ final String data = captor.getValue()
+ .getStringExtra(FragmentResultActivity.EXTRA_RESULT_CONTENT);
+ assertEquals("content 10", data);
+ }
+
+ @SmallTest
+ public void testStartActivityForResultCanceled() {
+ startActivityForResult(20, Activity.RESULT_CANCELED, "content 20");
+
+ ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
+ verify(mFragment, times(1))
+ .onActivityResult(eq(20), eq(Activity.RESULT_CANCELED), captor.capture());
+ final String data = captor.getValue()
+ .getStringExtra(FragmentResultActivity.EXTRA_RESULT_CONTENT);
+ assertEquals("content 20", data);
+ }
+
+ @SmallTest
+ public void testStartIntentSenderForResultOk() {
+ startIntentSenderForResult(30, Activity.RESULT_OK, "content 30");
+
+ ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
+ verify(mFragment, times(1))
+ .onActivityResult(eq(30), eq(Activity.RESULT_OK), captor.capture());
+ final String data = captor.getValue()
+ .getStringExtra(FragmentResultActivity.EXTRA_RESULT_CONTENT);
+ assertEquals("content 30", data);
+ }
+
+ @SmallTest
+ public void testStartIntentSenderForResultCanceled() {
+ startIntentSenderForResult(40, Activity.RESULT_CANCELED, "content 40");
+
+ ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
+ verify(mFragment, times(1))
+ .onActivityResult(eq(40), eq(Activity.RESULT_CANCELED), captor.capture());
+ final String data = captor.getValue()
+ .getStringExtra(FragmentResultActivity.EXTRA_RESULT_CONTENT);
+ assertEquals("content 40", data);
+ }
+
+ private Fragment attachTestFragment() {
+ final Fragment fragment = spy(new Fragment());
+ getInstrumentation().waitForIdleSync();
+ getInstrumentation().runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ mActivity.getFragmentManager().beginTransaction()
+ .add(R.id.content, fragment)
+ .addToBackStack(null)
+ .commitAllowingStateLoss();
+ mActivity.getFragmentManager().executePendingTransactions();
+ }
+ });
+ getInstrumentation().waitForIdleSync();
+ return fragment;
+ }
+
+ private void startActivityForResult(final int requestCode, final int resultCode,
+ final String content) {
+ getInstrumentation().runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ Intent intent = new Intent(mActivity, FragmentResultActivity.class);
+ intent.putExtra(FragmentResultActivity.EXTRA_RESULT_CODE, resultCode);
+ intent.putExtra(FragmentResultActivity.EXTRA_RESULT_CONTENT, content);
+
+ mFragment.startActivityForResult(intent, requestCode);
+ }
+ });
+ getInstrumentation().waitForIdleSync();
+ }
+
+ private void startIntentSenderForResult(final int requestCode, final int resultCode,
+ final String content) {
+ getInstrumentation().runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ Intent intent = new Intent(mActivity, FragmentResultActivity.class);
+ intent.putExtra(FragmentResultActivity.EXTRA_RESULT_CODE, resultCode);
+ intent.putExtra(FragmentResultActivity.EXTRA_RESULT_CONTENT, content);
+
+ PendingIntent pendingIntent = PendingIntent.getActivity(mActivity,
+ requestCode, intent, 0);
+
+ try {
+ mFragment.startIntentSenderForResult(pendingIntent.getIntentSender(),
+ requestCode, null, 0, 0, 0, null);
+ } catch (IntentSender.SendIntentException e) {
+ fail("IntentSender failed");
+ }
+ }
+ });
+ getInstrumentation().waitForIdleSync();
+ }
+
+}
diff --git a/tests/app/src/android/app/cts/FragmentTransactionTest.java b/tests/app/src/android/app/cts/FragmentTransactionTest.java
new file mode 100644
index 0000000..2ff69a7
--- /dev/null
+++ b/tests/app/src/android/app/cts/FragmentTransactionTest.java
@@ -0,0 +1,169 @@
+/*
+ * 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.app.cts;
+
+import android.app.Fragment;
+import android.app.stubs.FragmentTestActivity;
+import android.app.stubs.R;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static junit.framework.TestCase.*;
+
+/**
+ * Tests usage of the {@link android.app.FragmentTransaction} class.
+ */
+@RunWith(AndroidJUnit4.class)
+public class FragmentTransactionTest {
+
+ @Rule
+ public ActivityTestRule<FragmentTestActivity> mActivityRule =
+ new ActivityTestRule<>(FragmentTestActivity.class);
+
+ private FragmentTestActivity mActivity;
+
+ @Before
+ public void setUp() {
+ mActivity = mActivityRule.getActivity();
+ }
+
+ @Test
+ public void testAddTransactionWithValidFragment() {
+ final Fragment fragment = new CorrectFragment();
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ mActivity.getFragmentManager().beginTransaction()
+ .add(R.id.content, fragment)
+ .addToBackStack(null)
+ .commit();
+ mActivity.getFragmentManager().executePendingTransactions();
+ }
+ });
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+ assertTrue(fragment.isAdded());
+ }
+
+ @Test
+ public void testAddTransactionWithPrivateFragment() {
+ final Fragment fragment = new PrivateFragment();
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ boolean exceptionThrown = false;
+ try {
+ mActivity.getFragmentManager().beginTransaction()
+ .add(R.id.content, fragment)
+ .addToBackStack(null)
+ .commit();
+ mActivity.getFragmentManager().executePendingTransactions();
+ } catch (IllegalStateException e) {
+ exceptionThrown = true;
+ } finally {
+ assertTrue("Exception should be thrown", exceptionThrown);
+ assertFalse("Fragment shouldn't be added", fragment.isAdded());
+ }
+ }
+ });
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+ }
+
+ @Test
+ public void testAddTransactionWithPackagePrivateFragment() {
+ final Fragment fragment = new PackagePrivateFragment();
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ boolean exceptionThrown = false;
+ try {
+ mActivity.getFragmentManager().beginTransaction()
+ .add(R.id.content, fragment)
+ .addToBackStack(null)
+ .commit();
+ mActivity.getFragmentManager().executePendingTransactions();
+ } catch (IllegalStateException e) {
+ exceptionThrown = true;
+ } finally {
+ assertTrue("Exception should be thrown", exceptionThrown);
+ assertFalse("Fragment shouldn't be added", fragment.isAdded());
+ }
+ }
+ });
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+ }
+
+ @Test
+ public void testAddTransactionWithAnonymousFragment() {
+ final Fragment fragment = new Fragment() {};
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ boolean exceptionThrown = false;
+ try {
+ mActivity.getFragmentManager().beginTransaction()
+ .add(R.id.content, fragment)
+ .addToBackStack(null)
+ .commit();
+ mActivity.getFragmentManager().executePendingTransactions();
+ } catch (IllegalStateException e) {
+ exceptionThrown = true;
+ } finally {
+ assertTrue("Exception should be thrown", exceptionThrown);
+ assertFalse("Fragment shouldn't be added", fragment.isAdded());
+ }
+ }
+ });
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+ }
+
+ @Test
+ public void testAddTransactionWithNonStaticFragment() {
+ final Fragment fragment = new NonStaticFragment();
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ boolean exceptionThrown = false;
+ try {
+ mActivity.getFragmentManager().beginTransaction()
+ .add(R.id.content, fragment)
+ .addToBackStack(null)
+ .commit();
+ mActivity.getFragmentManager().executePendingTransactions();
+ } catch (IllegalStateException e) {
+ exceptionThrown = true;
+ } finally {
+ assertTrue("Exception should be thrown", exceptionThrown);
+ assertFalse("Fragment shouldn't be added", fragment.isAdded());
+ }
+ }
+ });
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+ }
+
+ public static class CorrectFragment extends Fragment {}
+
+ private static class PrivateFragment extends Fragment {}
+
+ private static class PackagePrivateFragment extends Fragment {}
+
+ private class NonStaticFragment extends Fragment {}
+}
diff --git a/tests/app/src/android/app/cts/NewDocumentTest.java b/tests/app/src/android/app/cts/NewDocumentTest.java
new file mode 100644
index 0000000..52d8df9
--- /dev/null
+++ b/tests/app/src/android/app/cts/NewDocumentTest.java
@@ -0,0 +1,73 @@
+/*
+ * 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.app.cts;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import android.app.stubs.NewDocumentTestActivity;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.Uri;
+import android.test.AndroidTestCase;
+
+public class NewDocumentTest extends AndroidTestCase {
+ private static Uri TEST_URI = Uri.parse("test_uri");
+ private static long TIMEOUT_MS = 3000;
+
+ public void testNewDocument() throws InterruptedException {
+ final Intent intent = new Intent();
+ intent.setClass(getContext(), NewDocumentTestActivity.class);
+ intent.setData(TEST_URI);
+
+ try (final Receiver receiver = new Receiver(NewDocumentTestActivity.NOTIFY_RESUME)) {
+ getContext().startActivity(intent);
+ receiver.await();
+ }
+
+ try (final Receiver receiver = new Receiver(NewDocumentTestActivity.NOTIFY_NEW_INTENT)) {
+ getContext().startActivity(intent);
+ receiver.await();
+ }
+ }
+
+ private class Receiver extends BroadcastReceiver implements AutoCloseable {
+ private final CountDownLatch latch = new CountDownLatch(1);
+
+ Receiver(String action) {
+ getContext().registerReceiver(this, new IntentFilter(action));
+ }
+
+ void await() throws InterruptedException {
+ assertTrue(
+ "Timeout for broadcast from activity",
+ latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ latch.countDown();
+ }
+
+ @Override
+ public void close() {
+ getContext().unregisterReceiver(this);
+ }
+ }
+}
diff --git a/tests/aslr/src/AslrMallocTest.cpp b/tests/aslr/src/AslrMallocTest.cpp
index 6e773cb..91a36e2 100644
--- a/tests/aslr/src/AslrMallocTest.cpp
+++ b/tests/aslr/src/AslrMallocTest.cpp
@@ -157,7 +157,9 @@
return EXIT_FAILURE;
}
- printf("%p", malloc(size));
+ void* p = malloc(size);
+ printf("%p", p);
+ free(p);
return EXIT_SUCCESS;
}
#endif
diff --git a/tests/camera/libctscamera2jni/native-camera-jni.cpp b/tests/camera/libctscamera2jni/native-camera-jni.cpp
index db687bf..4a3620e 100644
--- a/tests/camera/libctscamera2jni/native-camera-jni.cpp
+++ b/tests/camera/libctscamera2jni/native-camera-jni.cpp
@@ -345,7 +345,7 @@
class StaticInfo {
public:
- StaticInfo(ACameraMetadata* chars) : mChars(chars) {}
+ explicit StaticInfo(ACameraMetadata* chars) : mChars(chars) {}
bool isColorOutputSupported() {
return isCapabilitySupported(ACAMERA_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE);
diff --git a/tests/expectations/knownfailures.txt b/tests/expectations/knownfailures.txt
index 60a461c..26fd83e 100644
--- a/tests/expectations/knownfailures.txt
+++ b/tests/expectations/knownfailures.txt
@@ -237,19 +237,6 @@
bug: 23779168
},
{
- description: "New assist tests that do not yet have a track record.",
- names: [
- "android.assist.cts.AssistantContentViewTest",
- "android.assist.cts.ExtraAssistDataTest",
- "android.assist.cts.FocusChangeTest",
- "android.assist.cts.LargeViewHierarchyTest",
- "android.assist.cts.ScreenshotTest",
- "android.assist.cts.TextViewTest",
- "android.assist.cts.WebViewTest"
- ],
- bug: 21668302
-},
-{
description: "ConnectivityConstraintTest job scheduler not working.",
names: [
"android.jobscheduler.cts.ConnectivityConstraintTest#testConnectivityConstraintExecutes_withWifi",
diff --git a/tests/fragment/res/layout/double_container.xml b/tests/fragment/res/layout/double_container.xml
new file mode 100644
index 0000000..c24d5ce
--- /dev/null
+++ b/tests/fragment/res/layout/double_container.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <FrameLayout
+ android:id="@+id/fragmentContainer1"
+ android:layout_weight="1"
+ android:layout_width="match_parent"
+ android:layout_height="0px"/>
+ <FrameLayout
+ android:id="@+id/fragmentContainer2"
+ android:layout_weight="1"
+ android:layout_width="match_parent"
+ android:layout_height="0px"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/fragment/res/layout/simple_container.xml b/tests/fragment/res/layout/simple_container.xml
new file mode 100644
index 0000000..f231c1ec
--- /dev/null
+++ b/tests/fragment/res/layout/simple_container.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/fragmentContainer"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+</FrameLayout>
\ No newline at end of file
diff --git a/tests/fragment/src/android/fragment/cts/FragmentAnimatorTest.java b/tests/fragment/src/android/fragment/cts/FragmentAnimatorTest.java
new file mode 100644
index 0000000..2d75395
--- /dev/null
+++ b/tests/fragment/src/android/fragment/cts/FragmentAnimatorTest.java
@@ -0,0 +1,296 @@
+/*
+ * 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.fragment.cts;
+
+import static junit.framework.Assert.*;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ValueAnimator;
+import android.app.Fragment;
+import android.app.FragmentManager;
+import android.app.Instrumentation;
+import android.os.Debug;
+import android.os.SystemClock;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.View;
+import android.view.ViewGroup;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class FragmentAnimatorTest {
+ // These are pretend resource IDs for animators. We don't need real ones since we
+ // load them by overriding onCreateAnimator
+ private final static int ENTER = 1;
+ private final static int EXIT = 2;
+ private final static int POP_ENTER = 3;
+ private final static int POP_EXIT = 4;
+
+ @Rule
+ public ActivityTestRule<FragmentTestActivity> mActivityRule =
+ new ActivityTestRule<FragmentTestActivity>(FragmentTestActivity.class);
+
+ private Instrumentation mInstrumentation;
+
+ @Before
+ public void setupContainer() {
+ mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+ }
+
+ // Ensure that adding and popping a Fragment uses the enter and popExit animators
+ @Test
+ public void addAnimators() throws Throwable {
+ final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
+
+ // One fragment with a view
+ final AnimatorFragment fragment = new AnimatorFragment();
+ fm.beginTransaction()
+ .setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
+ .add(R.id.fragmentContainer, fragment)
+ .addToBackStack(null)
+ .commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ assertEnterPopExit(fragment);
+ }
+
+ // Ensure that removing and popping a Fragment uses the exit and popEnter animators
+ @Test
+ public void removeAnimators() throws Throwable {
+ final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
+
+ // One fragment with a view
+ final AnimatorFragment fragment = new AnimatorFragment();
+ fm.beginTransaction().add(R.id.fragmentContainer, fragment, "1").commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ fm.beginTransaction()
+ .setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
+ .remove(fragment)
+ .addToBackStack(null)
+ .commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ assertExitPopEnter(fragment);
+ }
+
+ // Ensure that showing and popping a Fragment uses the enter and popExit animators
+ @Test
+ public void showAnimators() throws Throwable {
+ final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
+
+ // One fragment with a view
+ final AnimatorFragment fragment = new AnimatorFragment();
+ fm.beginTransaction().add(R.id.fragmentContainer, fragment).hide(fragment).commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ fm.beginTransaction()
+ .setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
+ .show(fragment)
+ .addToBackStack(null)
+ .commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ assertEnterPopExit(fragment);
+ }
+
+ // Ensure that hiding and popping a Fragment uses the exit and popEnter animators
+ @Test
+ public void hideAnimators() throws Throwable {
+ final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
+
+ // One fragment with a view
+ final AnimatorFragment fragment = new AnimatorFragment();
+ fm.beginTransaction().add(R.id.fragmentContainer, fragment, "1").commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ fm.beginTransaction()
+ .setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
+ .hide(fragment)
+ .addToBackStack(null)
+ .commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ assertExitPopEnter(fragment);
+ }
+
+ // Ensure that attaching and popping a Fragment uses the enter and popExit animators
+ @Test
+ public void attachAnimators() throws Throwable {
+ final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
+
+ // One fragment with a view
+ final AnimatorFragment fragment = new AnimatorFragment();
+ fm.beginTransaction().add(R.id.fragmentContainer, fragment).detach(fragment).commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ fm.beginTransaction()
+ .setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
+ .attach(fragment)
+ .addToBackStack(null)
+ .commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ assertEnterPopExit(fragment);
+ }
+
+ // Ensure that detaching and popping a Fragment uses the exit and popEnter animators
+ @Test
+ public void detachAnimators() throws Throwable {
+ final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
+
+ // One fragment with a view
+ final AnimatorFragment fragment = new AnimatorFragment();
+ fm.beginTransaction().add(R.id.fragmentContainer, fragment, "1").commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ fm.beginTransaction()
+ .setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
+ .detach(fragment)
+ .addToBackStack(null)
+ .commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ assertExitPopEnter(fragment);
+ }
+
+ // Replace should exit the existing fragments and enter the added fragment, then
+ // popping should popExit the removed fragment and popEnter the added fragments
+ @Test
+ public void replaceAnimators() throws Throwable {
+ final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
+
+ // One fragment with a view
+ final AnimatorFragment fragment1 = new AnimatorFragment();
+ final AnimatorFragment fragment2 = new AnimatorFragment();
+ fm.beginTransaction()
+ .add(R.id.fragmentContainer, fragment1, "1")
+ .add(R.id.fragmentContainer, fragment2, "2")
+ .commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ final AnimatorFragment fragment3 = new AnimatorFragment();
+ fm.beginTransaction()
+ .setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
+ .replace(R.id.fragmentContainer, fragment3)
+ .addToBackStack(null)
+ .commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ assertFragmentAnimation(fragment1, 1, false, EXIT);
+ assertFragmentAnimation(fragment2, 1, false, EXIT);
+ assertFragmentAnimation(fragment3, 1, true, ENTER);
+
+ mInstrumentation.waitForIdleSync();
+
+ fm.popBackStack();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ assertFragmentAnimation(fragment3, 2, false, POP_EXIT);
+ final AnimatorFragment replacement1 = (AnimatorFragment) fm.findFragmentByTag("1");
+ final AnimatorFragment replacement2 = (AnimatorFragment) fm.findFragmentByTag("1");
+ int expectedAnimations = replacement1 == fragment1 ? 2 : 1;
+ assertFragmentAnimation(replacement1, expectedAnimations, true, POP_ENTER);
+ assertFragmentAnimation(replacement2, expectedAnimations, true, POP_ENTER);
+ }
+
+ private void assertEnterPopExit(AnimatorFragment fragment) throws Throwable {
+ assertFragmentAnimation(fragment, 1, true, ENTER);
+ mInstrumentation.waitForIdleSync();
+
+ final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
+ fm.popBackStack();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ assertFragmentAnimation(fragment, 2, false, POP_EXIT);
+ }
+
+ private void assertExitPopEnter(AnimatorFragment fragment) throws Throwable {
+ assertFragmentAnimation(fragment, 1, false, EXIT);
+ mInstrumentation.waitForIdleSync();
+
+ final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
+ fm.popBackStack();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ AnimatorFragment replacement = (AnimatorFragment) fm.findFragmentByTag("1");
+
+ boolean isSameFragment = replacement == fragment;
+ int expectedAnimators = isSameFragment ? 2 : 1;
+ assertFragmentAnimation(replacement, expectedAnimators, true, POP_ENTER);
+ }
+
+ private void assertFragmentAnimation(AnimatorFragment fragment, int numAnimators,
+ boolean isEnter, int animatorResourceId) throws InterruptedException {
+ assertEquals(numAnimators, fragment.numAnimators);
+ assertEquals(isEnter, fragment.enter);
+ assertEquals(animatorResourceId, fragment.resourceId);
+ assertNotNull(fragment.animator);
+ assertTrue(fragment.wasStarted);
+ assertTrue(fragment.endLatch.await(100, TimeUnit.MILLISECONDS));
+ }
+
+ public static class AnimatorFragment extends StrictViewFragment {
+ int numAnimators;
+ Animator animator;
+ boolean enter;
+ int resourceId;
+ boolean wasStarted;
+ CountDownLatch endLatch;
+
+ @Override
+ public Animator onCreateAnimator(int transit, boolean enter, int nextAnim) {
+ if (nextAnim == 0) {
+ return null;
+ }
+ this.numAnimators++;
+ this.wasStarted = false;
+ this.animator = ValueAnimator.ofFloat(0, 1).setDuration(1);
+ this.endLatch = new CountDownLatch(1);
+ this.animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ wasStarted = true;
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ endLatch.countDown();
+ }
+ });
+ this.resourceId = nextAnim;
+ this.enter = enter;
+ return this.animator;
+ }
+ }
+}
diff --git a/tests/fragment/src/android/fragment/cts/FragmentTestUtil.java b/tests/fragment/src/android/fragment/cts/FragmentTestUtil.java
new file mode 100644
index 0000000..c83d4fa
--- /dev/null
+++ b/tests/fragment/src/android/fragment/cts/FragmentTestUtil.java
@@ -0,0 +1,44 @@
+/*
+ * 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.fragment.cts;
+
+import android.app.Instrumentation;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.rule.ActivityTestRule;
+
+public class FragmentTestUtil {
+ public static void executePendingTransactions(final ActivityTestRule<FragmentTestActivity> rule)
+ throws Throwable {
+ Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+ instrumentation.runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ rule.getActivity().getFragmentManager().executePendingTransactions();
+ }
+ });
+ }
+
+ public static void setContentView(final ActivityTestRule<FragmentTestActivity> rule,
+ final int layoutId) {
+ Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+ instrumentation.runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ rule.getActivity().setContentView(layoutId);
+ }
+ });
+ }
+}
diff --git a/tests/fragment/src/android/fragment/cts/FragmentViewTests.java b/tests/fragment/src/android/fragment/cts/FragmentViewTests.java
new file mode 100644
index 0000000..ef28a1d
--- /dev/null
+++ b/tests/fragment/src/android/fragment/cts/FragmentViewTests.java
@@ -0,0 +1,770 @@
+/*
+ * 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.fragment.cts;
+
+import static junit.framework.Assert.*;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.app.Fragment;
+import android.app.FragmentManager;
+import android.app.Instrumentation;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.View;
+import android.view.ViewGroup;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class FragmentViewTests {
+ @Rule
+ public ActivityTestRule<FragmentTestActivity> mActivityRule =
+ new ActivityTestRule<FragmentTestActivity>(FragmentTestActivity.class);
+
+ private Instrumentation mInstrumentation;
+
+ @Before
+ public void setupInstrumentation() {
+ mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ }
+
+ // Test that adding a fragment adds the Views in the proper order. Popping the back stack
+ // should remove the correct Views.
+ @Test
+ public void addFragments() throws Throwable {
+ FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+ ViewGroup container = (ViewGroup)
+ mActivityRule.getActivity().findViewById(R.id.fragmentContainer);
+ final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
+
+ // One fragment with a view
+ final StrictViewFragment fragment1 = new StrictViewFragment();
+ fm.beginTransaction().add(R.id.fragmentContainer, fragment1).addToBackStack(null).commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ assertChildren(container, fragment1);
+
+ // Add another on top
+ final StrictViewFragment fragment2 = new StrictViewFragment();
+ fm.beginTransaction().add(R.id.fragmentContainer, fragment2).addToBackStack(null).commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ assertChildren(container, fragment1, fragment2);
+
+ // Now add two in one transaction:
+ final StrictViewFragment fragment3 = new StrictViewFragment();
+ final StrictViewFragment fragment4 = new StrictViewFragment();
+ fm.beginTransaction()
+ .add(R.id.fragmentContainer, fragment3)
+ .add(R.id.fragmentContainer, fragment4)
+ .addToBackStack(null)
+ .commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ assertChildren(container, fragment1, fragment2, fragment3, fragment4);
+
+ fm.popBackStack();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ assertChildren(container, fragment1, fragment2);
+
+ fm.popBackStack();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ assertEquals(1, container.getChildCount());
+ assertChildren(container, fragment1);
+
+ fm.popBackStack();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ assertChildren(container);
+ }
+
+ // Add fragments to multiple containers in the same transaction. Make sure that
+ // they pop correctly, too.
+ @Test
+ public void addTwoContainers() throws Throwable {
+ FragmentTestUtil.setContentView(mActivityRule, R.layout.double_container);
+ ViewGroup container1 = (ViewGroup)
+ mActivityRule.getActivity().findViewById(R.id.fragmentContainer1);
+ ViewGroup container2 = (ViewGroup)
+ mActivityRule.getActivity().findViewById(R.id.fragmentContainer2);
+ final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
+
+ final StrictViewFragment fragment1 = new StrictViewFragment();
+ fm.beginTransaction().add(R.id.fragmentContainer1, fragment1).addToBackStack(null).commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ assertChildren(container1, fragment1);
+
+ final StrictViewFragment fragment2 = new StrictViewFragment();
+ fm.beginTransaction().add(R.id.fragmentContainer2, fragment2).addToBackStack(null).commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ assertChildren(container2, fragment2);
+
+ final StrictViewFragment fragment3 = new StrictViewFragment();
+ final StrictViewFragment fragment4 = new StrictViewFragment();
+ fm.beginTransaction()
+ .add(R.id.fragmentContainer1, fragment3)
+ .add(R.id.fragmentContainer2, fragment4)
+ .addToBackStack(null)
+ .commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ assertChildren(container1, fragment1, fragment3);
+ assertChildren(container2, fragment2, fragment4);
+
+ fm.popBackStack();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ assertChildren(container1, fragment1);
+ assertChildren(container2, fragment2);
+
+ fm.popBackStack();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ assertChildren(container1, fragment1);
+ assertChildren(container2);
+
+ fm.popBackStack();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ assertEquals(0, container1.getChildCount());
+ }
+
+ // When you add a fragment that's has already been added, it should throw.
+ @Test
+ public void doubleAdd() throws Throwable {
+ FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+ final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
+ final StrictViewFragment fragment1 = new StrictViewFragment();
+ fm.beginTransaction().add(R.id.fragmentContainer, fragment1).commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ mInstrumentation.runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ fm.beginTransaction()
+ .add(R.id.fragmentContainer, fragment1)
+ .addToBackStack(null)
+ .commit();
+ fm.executePendingTransactions();
+ fail("Adding a fragment that is already added should be an error");
+ } catch (IllegalStateException e) {
+ // expected
+ }
+ }
+ });
+ }
+
+ // Make sure that removed fragments remove the right Views. Popping the back stack should
+ // add the Views back properly
+ @Test
+ public void removeFragments() throws Throwable {
+ FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+ ViewGroup container = (ViewGroup)
+ mActivityRule.getActivity().findViewById(R.id.fragmentContainer);
+ final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
+ final StrictViewFragment fragment1 = new StrictViewFragment();
+ final StrictViewFragment fragment2 = new StrictViewFragment();
+ final StrictViewFragment fragment3 = new StrictViewFragment();
+ final StrictViewFragment fragment4 = new StrictViewFragment();
+ fm.beginTransaction()
+ .add(R.id.fragmentContainer, fragment1, "1")
+ .add(R.id.fragmentContainer, fragment2, "2")
+ .add(R.id.fragmentContainer, fragment3, "3")
+ .add(R.id.fragmentContainer, fragment4, "4")
+ .commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ assertChildren(container, fragment1, fragment2, fragment3, fragment4);
+
+ // Remove a view
+ fm.beginTransaction().remove(fragment4).addToBackStack(null).commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ assertEquals(3, container.getChildCount());
+ assertChildren(container, fragment1, fragment2, fragment3);
+
+ // remove another one
+ fm.beginTransaction().remove(fragment2).addToBackStack(null).commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ assertChildren(container, fragment1, fragment3);
+
+ // Now remove the remaining:
+ fm.beginTransaction()
+ .remove(fragment3)
+ .remove(fragment1)
+ .addToBackStack(null)
+ .commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ assertChildren(container);
+
+ fm.popBackStack();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ final Fragment replacement1 = fm.findFragmentByTag("1");
+ final Fragment replacement3 = fm.findFragmentByTag("3");
+ assertChildren(container, replacement1, replacement3);
+
+ fm.popBackStack();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ final Fragment replacement2 = fm.findFragmentByTag("2");
+ assertChildren(container, replacement1, replacement3, replacement2);
+
+ fm.popBackStack();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ final Fragment replacement4 = fm.findFragmentByTag("4");
+ assertChildren(container, replacement1, replacement3, replacement2, replacement4);
+ }
+
+ // Removing a hidden fragment should remove the View and popping should bring it back hidden
+ @Test
+ public void removeHiddenView() throws Throwable {
+ FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+ ViewGroup container = (ViewGroup)
+ mActivityRule.getActivity().findViewById(R.id.fragmentContainer);
+ final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
+ final StrictViewFragment fragment1 = new StrictViewFragment();
+ fm.beginTransaction().add(R.id.fragmentContainer, fragment1, "1").hide(fragment1).commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ assertChildren(container, fragment1);
+ assertTrue(fragment1.isHidden());
+
+ fm.beginTransaction().remove(fragment1).addToBackStack(null).commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ assertChildren(container);
+
+ fm.popBackStack();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ final Fragment replacement1 = fm.findFragmentByTag("1");
+ assertChildren(container, replacement1);
+ assertTrue(replacement1.isHidden());
+ assertEquals(View.GONE, replacement1.getView().getVisibility());
+ mInstrumentation.waitForIdleSync();
+ }
+
+ // Removing a detached fragment should do nothing to the View and popping should bring
+ // the Fragment back detached
+ @Test
+ public void removeDetatchedView() throws Throwable {
+ FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+ ViewGroup container = (ViewGroup)
+ mActivityRule.getActivity().findViewById(R.id.fragmentContainer);
+ final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
+ final StrictViewFragment fragment1 = new StrictViewFragment();
+ fm.beginTransaction()
+ .add(R.id.fragmentContainer, fragment1, "1")
+ .detach(fragment1)
+ .commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ assertChildren(container);
+ assertTrue(fragment1.isDetached());
+
+ fm.beginTransaction().remove(fragment1).addToBackStack(null).commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ assertChildren(container);
+
+ fm.popBackStack();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ final Fragment replacement1 = fm.findFragmentByTag("1");
+ assertChildren(container);
+ assertTrue(replacement1.isDetached());
+ }
+
+ // Unlike adding the same fragment twice, you should be able to add and then remove and then
+ // add the same fragment in one transaction.
+ @Test
+ public void addRemoveAdd() throws Throwable {
+ FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+ ViewGroup container = (ViewGroup)
+ mActivityRule.getActivity().findViewById(R.id.fragmentContainer);
+ final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
+ final StrictViewFragment fragment = new StrictViewFragment();
+ fm.beginTransaction()
+ .add(R.id.fragmentContainer, fragment)
+ .remove(fragment)
+ .add(R.id.fragmentContainer, fragment)
+ .addToBackStack(null)
+ .commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ assertChildren(container, fragment);
+
+ fm.popBackStack();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ assertChildren(container);
+ }
+
+ // Removing a fragment that isn't in should throw
+ @Test
+ public void removeNothThere() throws Throwable {
+ FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+ final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
+ final StrictViewFragment fragment = new StrictViewFragment();
+ fm.beginTransaction().remove(fragment).commit();
+ try {
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ fail("Removing a fragment that isn't in should throw an exception");
+ } catch (Throwable t) {
+ // expected
+ }
+ }
+
+ // Hide a fragment and its View should be GONE. Then pop it and the View should be VISIBLE
+ @Test
+ public void hideFragment() throws Throwable {
+ FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+ ViewGroup container = (ViewGroup)
+ mActivityRule.getActivity().findViewById(R.id.fragmentContainer);
+ final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
+ final StrictViewFragment fragment = new StrictViewFragment();
+ fm.beginTransaction().add(R.id.fragmentContainer, fragment).commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ assertChildren(container, fragment);
+ assertEquals(View.VISIBLE, fragment.getView().getVisibility());
+
+ fm.beginTransaction().hide(fragment).addToBackStack(null).commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ assertChildren(container, fragment);
+ assertTrue(fragment.isHidden());
+ assertEquals(View.GONE, fragment.getView().getVisibility());
+
+ fm.popBackStack();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ assertChildren(container, fragment);
+ assertFalse(fragment.isHidden());
+ assertEquals(View.VISIBLE, fragment.getView().getVisibility());
+ }
+
+ // Hiding a hidden fragment should throw
+ @Test
+ public void doubleHide() throws Throwable {
+ FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+ final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
+ final StrictViewFragment fragment = new StrictViewFragment();
+ fm.beginTransaction()
+ .add(R.id.fragmentContainer, fragment)
+ .hide(fragment)
+ .hide(fragment)
+ .commit();
+ try {
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ fail("Hiding a hidden fragment should throw an exception");
+ } catch (Throwable t) {
+ // expected
+ }
+ }
+
+ // Hiding a non-existing fragment should throw
+ @Test
+ public void hideUnAdded() throws Throwable {
+ FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+ final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
+ final StrictViewFragment fragment = new StrictViewFragment();
+ fm.beginTransaction()
+ .hide(fragment)
+ .commit();
+ try {
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ fail("Hiding a non-existing fragment should throw an exception");
+ } catch (Throwable t) {
+ // expected
+ }
+ }
+
+ // Show a hidden fragment and its View should be VISIBLE. Then pop it and the View should be
+ // BONE.
+ @Test
+ public void showFragment() throws Throwable {
+ FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+ ViewGroup container = (ViewGroup)
+ mActivityRule.getActivity().findViewById(R.id.fragmentContainer);
+ final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
+ final StrictViewFragment fragment = new StrictViewFragment();
+ fm.beginTransaction().add(R.id.fragmentContainer, fragment).hide(fragment).commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ assertChildren(container, fragment);
+ assertTrue(fragment.isHidden());
+ assertEquals(View.GONE, fragment.getView().getVisibility());
+
+ fm.beginTransaction().show(fragment).addToBackStack(null).commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ assertChildren(container, fragment);
+ assertFalse(fragment.isHidden());
+ assertEquals(View.VISIBLE, fragment.getView().getVisibility());
+
+ fm.popBackStack();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ assertChildren(container, fragment);
+ assertTrue(fragment.isHidden());
+ assertEquals(View.GONE, fragment.getView().getVisibility());
+ }
+
+ // Showing a shown fragment should throw
+ @Test
+ public void showShown() throws Throwable {
+ FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+ final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
+ final StrictViewFragment fragment = new StrictViewFragment();
+ fm.beginTransaction()
+ .add(R.id.fragmentContainer, fragment)
+ .show(fragment)
+ .commit();
+ try {
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ fail("Showing a visible fragment should throw an exception");
+ } catch (Throwable t) {
+ // expected
+ }
+ }
+
+ // Showing a non-existing fragment should throw
+ @Test
+ public void showUnAdded() throws Throwable {
+ FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+ final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
+ final StrictViewFragment fragment = new StrictViewFragment();
+ fm.beginTransaction()
+ .show(fragment)
+ .commit();
+ try {
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ fail("Showing a non-existing fragment should throw an exception");
+ } catch (Throwable t) {
+ // expected
+ }
+ }
+
+ // Detaching a fragment should remove the View from the hierarchy. Then popping it should
+ // bring it back VISIBLE
+ @Test
+ public void detachFragment() throws Throwable {
+ FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+ ViewGroup container = (ViewGroup)
+ mActivityRule.getActivity().findViewById(R.id.fragmentContainer);
+ final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
+ final StrictViewFragment fragment = new StrictViewFragment();
+ fm.beginTransaction().add(R.id.fragmentContainer, fragment).commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ assertChildren(container, fragment);
+ assertFalse(fragment.isDetached());
+ assertEquals(View.VISIBLE, fragment.getView().getVisibility());
+
+ fm.beginTransaction().detach(fragment).addToBackStack(null).commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ assertChildren(container);
+ assertTrue(fragment.isDetached());
+
+ fm.popBackStack();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ assertChildren(container, fragment);
+ assertFalse(fragment.isDetached());
+ assertEquals(View.VISIBLE, fragment.getView().getVisibility());
+ }
+
+ // Detaching a hidden fragment should remove the View from the hierarchy. Then popping it should
+ // bring it back hidden
+ @Test
+ public void detachHiddenFragment() throws Throwable {
+ FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+ ViewGroup container = (ViewGroup)
+ mActivityRule.getActivity().findViewById(R.id.fragmentContainer);
+ final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
+ final StrictViewFragment fragment = new StrictViewFragment();
+ fm.beginTransaction().add(R.id.fragmentContainer, fragment).hide(fragment).commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ assertChildren(container, fragment);
+ assertFalse(fragment.isDetached());
+ assertTrue(fragment.isHidden());
+ assertEquals(View.GONE, fragment.getView().getVisibility());
+
+ fm.beginTransaction().detach(fragment).addToBackStack(null).commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ assertChildren(container);
+ assertTrue(fragment.isHidden());
+ assertTrue(fragment.isDetached());
+
+ fm.popBackStack();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ assertChildren(container, fragment);
+ assertTrue(fragment.isHidden());
+ assertFalse(fragment.isDetached());
+ assertEquals(View.GONE, fragment.getView().getVisibility());
+ }
+
+ // Detaching a detached fragment should throw
+ @Test
+ public void detachDetatched() throws Throwable {
+ FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+ final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
+ final StrictViewFragment fragment = new StrictViewFragment();
+ fm.beginTransaction()
+ .add(R.id.fragmentContainer, fragment)
+ .detach(fragment)
+ .detach(fragment)
+ .commit();
+ try {
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ fail("Detaching a detached fragment should throw an exception");
+ } catch (Throwable t) {
+ // expected
+ }
+ }
+
+ // Detaching a non-existing fragment should throw
+ @Test
+ public void detachUnAdded() throws Throwable {
+ FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+ final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
+ final StrictViewFragment fragment = new StrictViewFragment();
+ fm.beginTransaction()
+ .detach(fragment)
+ .commit();
+ try {
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ fail("Detaching a non-existing fragment should throw an exception");
+ } catch (Throwable t) {
+ // expected
+ }
+ }
+
+ // Attaching a fragment should add the View back into the hierarchy. Then popping it should
+ // remove it again
+ @Test
+ public void attachFragment() throws Throwable {
+ FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+ ViewGroup container = (ViewGroup)
+ mActivityRule.getActivity().findViewById(R.id.fragmentContainer);
+ final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
+ final StrictViewFragment fragment = new StrictViewFragment();
+ fm.beginTransaction().add(R.id.fragmentContainer, fragment).detach(fragment).commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ assertChildren(container);
+ assertTrue(fragment.isDetached());
+
+ fm.beginTransaction().attach(fragment).addToBackStack(null).commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ assertChildren(container, fragment);
+ assertFalse(fragment.isDetached());
+ assertEquals(View.VISIBLE, fragment.getView().getVisibility());
+
+ fm.popBackStack();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ assertChildren(container);
+ assertTrue(fragment.isDetached());
+ }
+
+ // Attaching a hidden fragment should add the View as GONE the hierarchy. Then popping it should
+ // remove it again.
+ @Test
+ public void attachHiddenFragment() throws Throwable {
+ FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+ ViewGroup container = (ViewGroup)
+ mActivityRule.getActivity().findViewById(R.id.fragmentContainer);
+ final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
+ final StrictViewFragment fragment = new StrictViewFragment();
+ fm.beginTransaction()
+ .add(R.id.fragmentContainer, fragment)
+ .hide(fragment)
+ .detach(fragment)
+ .commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ assertChildren(container);
+ assertTrue(fragment.isDetached());
+ assertTrue(fragment.isHidden());
+
+ fm.beginTransaction().attach(fragment).addToBackStack(null).commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ assertChildren(container, fragment);
+ assertTrue(fragment.isHidden());
+ assertFalse(fragment.isDetached());
+ assertEquals(View.GONE, fragment.getView().getVisibility());
+
+ fm.popBackStack();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ assertChildren(container);
+ assertTrue(fragment.isDetached());
+ assertTrue(fragment.isHidden());
+ }
+
+ // Attaching an attached fragment should throw
+ @Test
+ public void attachAttached() throws Throwable {
+ FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+ final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
+ final StrictViewFragment fragment = new StrictViewFragment();
+ fm.beginTransaction()
+ .add(R.id.fragmentContainer, fragment)
+ .attach(fragment)
+ .commit();
+ try {
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ fail("Attaching an attached fragment should throw an exception");
+ } catch (Throwable t) {
+ // expected
+ }
+ }
+
+ // Attaching a non-existing fragment should throw
+ @Test
+ public void attachUnAdded() throws Throwable {
+ FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+ final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
+ final StrictViewFragment fragment = new StrictViewFragment();
+ fm.beginTransaction()
+ .attach(fragment)
+ .commit();
+ try {
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ fail("Attaching a non-existing fragment should throw an exception");
+ } catch (Throwable t) {
+ // expected
+ }
+ }
+
+ // Simple replace of one fragment in a container. Popping should replace it back again
+ @Test
+ public void replaceOne() throws Throwable {
+ FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+ ViewGroup container = (ViewGroup)
+ mActivityRule.getActivity().findViewById(R.id.fragmentContainer);
+ final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
+ final StrictViewFragment fragment1 = new StrictViewFragment();
+ fm.beginTransaction().add(R.id.fragmentContainer, fragment1, "1").commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ assertChildren(container, fragment1);
+
+ final StrictViewFragment fragment2 = new StrictViewFragment();
+ fm.beginTransaction()
+ .replace(R.id.fragmentContainer, fragment2)
+ .addToBackStack(null)
+ .commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ assertChildren(container, fragment2);
+ assertEquals(View.VISIBLE, fragment2.getView().getVisibility());
+
+ fm.popBackStack();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ Fragment replacement1 = fm.findFragmentByTag("1");
+ assertNotNull(replacement1);
+ assertChildren(container, replacement1);
+ assertFalse(replacement1.isHidden());
+ assertTrue(replacement1.isAdded());
+ assertFalse(replacement1.isDetached());
+ assertEquals(View.VISIBLE, replacement1.getView().getVisibility());
+ }
+
+ // Replace of multiple fragments in a container. Popping should replace it back again
+ @Test
+ public void replaceTwo() throws Throwable {
+ FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+ ViewGroup container = (ViewGroup)
+ mActivityRule.getActivity().findViewById(R.id.fragmentContainer);
+ final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
+ final StrictViewFragment fragment1 = new StrictViewFragment();
+ final StrictViewFragment fragment2 = new StrictViewFragment();
+ fm.beginTransaction()
+ .add(R.id.fragmentContainer, fragment1, "1")
+ .add(R.id.fragmentContainer, fragment2, "2")
+ .hide(fragment2)
+ .commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ assertChildren(container, fragment1, fragment2);
+
+ final StrictViewFragment fragment3 = new StrictViewFragment();
+ fm.beginTransaction()
+ .replace(R.id.fragmentContainer, fragment3)
+ .addToBackStack(null)
+ .commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ assertChildren(container, fragment3);
+ assertEquals(View.VISIBLE, fragment3.getView().getVisibility());
+
+ fm.popBackStack();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ Fragment replacement1 = fm.findFragmentByTag("1");
+ Fragment replacement2 = fm.findFragmentByTag("2");
+ assertNotNull(replacement1);
+ assertNotNull(replacement2);
+ assertChildren(container, replacement1, replacement2);
+ assertFalse(replacement1.isHidden());
+ assertTrue(replacement1.isAdded());
+ assertFalse(replacement1.isDetached());
+ assertEquals(View.VISIBLE, replacement1.getView().getVisibility());
+
+ // fragment2 was hidden, so it should be returned hidden
+ assertTrue(replacement2.isHidden());
+ assertTrue(replacement2.isAdded());
+ assertFalse(replacement2.isDetached());
+ assertEquals(View.GONE, replacement2.getView().getVisibility());
+ }
+
+ // Replace of empty container. Should act as add and popping should just remove the fragment
+ @Test
+ public void replaceZero() throws Throwable {
+ FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+ ViewGroup container = (ViewGroup)
+ mActivityRule.getActivity().findViewById(R.id.fragmentContainer);
+ final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
+
+ final StrictViewFragment fragment = new StrictViewFragment();
+ fm.beginTransaction()
+ .replace(R.id.fragmentContainer, fragment)
+ .addToBackStack(null)
+ .commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ assertChildren(container, fragment);
+ assertEquals(View.VISIBLE, fragment.getView().getVisibility());
+
+ fm.popBackStack();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ assertChildren(container);
+ }
+
+ private void assertChildren(ViewGroup container, Fragment... fragments) {
+ final int numFragments = fragments == null ? 0 : fragments.length;
+ assertEquals("There aren't the correct number of fragment Views in its container",
+ numFragments, container.getChildCount());
+ for (int i = 0; i < numFragments; i++) {
+ assertEquals("Wrong Fragment View order for [" + i + "]", container.getChildAt(i),
+ fragments[i].getView());
+ }
+ }
+}
diff --git a/tests/tests/accounts/AndroidManifest.xml b/tests/tests/accounts/AndroidManifest.xml
index d882690..0d4a534 100644
--- a/tests/tests/accounts/AndroidManifest.xml
+++ b/tests/tests/accounts/AndroidManifest.xml
@@ -36,6 +36,8 @@
<activity android:name="android.accounts.cts.AccountRemovalDummyActivity" >
</activity>
+ <activity android:name="android.accounts.cts.AccountAuthenticatorDummyActivity" />
+
<service android:name="MockAccountService" android:exported="true"
android:process="android.accounts.cts">
<intent-filter>
diff --git a/tests/tests/accounts/common/src/android/accounts/cts/common/Fixtures.java b/tests/tests/accounts/common/src/android/accounts/cts/common/Fixtures.java
index f8636a0..024ea3c 100644
--- a/tests/tests/accounts/common/src/android/accounts/cts/common/Fixtures.java
+++ b/tests/tests/accounts/common/src/android/accounts/cts/common/Fixtures.java
@@ -70,5 +70,7 @@
public static final String KEY_RESULT = "test:result";
public static final String KEY_TOKEN_EXPIRY = "test:token_duration";
+ public static final String KEY_ACCOUNT_SESSION_BUNDLE = "test:account_session_bundle";
+
private Fixtures() {}
}
diff --git a/tests/tests/accounts/src/android/accounts/cts/AccountAuthenticatorDummyActivity.java b/tests/tests/accounts/src/android/accounts/cts/AccountAuthenticatorDummyActivity.java
new file mode 100644
index 0000000..6c2d29e
--- /dev/null
+++ b/tests/tests/accounts/src/android/accounts/cts/AccountAuthenticatorDummyActivity.java
@@ -0,0 +1,27 @@
+package android.accounts.cts;
+
+import android.accounts.AccountAuthenticatorResponse;
+import android.accounts.cts.common.Fixtures;
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+
+/**
+ * Activity used by {@link android.accounts.cts.MockAccountAuthenticator} to test the
+ * behavior of {@link AccountManager} when authenticator returns intent.
+ */
+public class AccountAuthenticatorDummyActivity extends Activity {
+
+ @Override
+ protected void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ Intent intent = getIntent();
+ AccountAuthenticatorResponse response = intent.getParcelableExtra(Fixtures.KEY_CALLBACK);
+ Intent result = intent.getParcelableExtra(Fixtures.KEY_RESULT);
+ if (response != null) {
+ response.onResult(result.getExtras());
+ }
+ setResult(RESULT_OK, result);
+ finish();
+ }
+}
diff --git a/tests/tests/accounts/src/android/accounts/cts/AccountManagerTest.java b/tests/tests/accounts/src/android/accounts/cts/AccountManagerTest.java
index 3520d9c..930324f 100644
--- a/tests/tests/accounts/src/android/accounts/cts/AccountManagerTest.java
+++ b/tests/tests/accounts/src/android/accounts/cts/AccountManagerTest.java
@@ -24,6 +24,7 @@
import android.accounts.AuthenticatorException;
import android.accounts.OnAccountsUpdateListener;
import android.accounts.OperationCanceledException;
+import android.accounts.cts.common.Fixtures;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
@@ -62,6 +63,8 @@
public static final String ACCOUNT_PASSWORD = "android.accounts.cts.account.password";
+ public static final String ACCOUNT_STATUS_TOKEN = "android.accounts.cts.account.status.token";
+
public static final String AUTH_TOKEN_TYPE = "mockAuthTokenType";
public static final String AUTH_EXPIRING_TOKEN_TYPE = "mockAuthExpiringTokenType";
public static final String AUTH_TOKEN_LABEL = "mockAuthTokenLabel";
@@ -98,6 +101,12 @@
public static final Account CUSTOM_TOKEN_ACCOUNT =
new Account(ACCOUNT_NAME,ACCOUNT_TYPE_CUSTOM);
+ public static final Bundle SESSION_BUNDLE = new Bundle();
+ public static final String SESSION_DATA_NAME_1 = "session.data.name.1";
+ public static final String SESSION_DATA_VALUE_1 = "session.data.value.1";
+
+ public static final String ERROR_MESSAGE = "android.accounts.cts.account.error.message";
+
private static MockAccountAuthenticator mockAuthenticator;
private static final int LATCH_TIMEOUT_MS = 500;
private static AccountManager am;
@@ -127,6 +136,8 @@
USERDATA_BUNDLE.putString(USERDATA_NAME_1, USERDATA_VALUE_1);
+ SESSION_BUNDLE.putString(SESSION_DATA_NAME_1, SESSION_DATA_VALUE_1);
+
getMockAuthenticator(mContext);
am = AccountManager.get(mContext);
@@ -2040,4 +2051,405 @@
}
}
+ /**
+ * Tests a basic startAddAccountSession() which returns a bundle containing
+ * encrypted session bundle, account password and status token.
+ */
+ public void testStartAddAccountSession()
+ throws IOException, AuthenticatorException, OperationCanceledException {
+ final String accountName = Fixtures.PREFIX_NAME_SUCCESS + "@"
+ + Fixtures.SUFFIX_NAME_FIXTURE;
+ final Bundle options = createOptionsWithAccountName(accountName);
+
+ Bundle resultBundle = startAddAccountSession(am, ACCOUNT_TYPE, AUTH_TOKEN_TYPE,
+ REQUIRED_FEATURES,
+ options, null /* activity */, null /* callback */, null /* handler */);
+
+ validateStartAddAccountSessionParametersAndOptions(accountName, options);
+
+ // Assert returned result
+ // Assert that auth token was stripped.
+ assertNull(resultBundle.get(AccountManager.KEY_AUTHTOKEN));
+ validateSessionBundleAndPasswordAndStatusTokenResult(resultBundle);
+ }
+
+ /**
+ * Tests startAddAccountSession() with null session bundle. Only account
+ * password and status token should be included in the result as session
+ * bundle is not inspected.
+ */
+ public void testStartAddAccountSessionWithNullSessionBundle()
+ throws IOException, AuthenticatorException, OperationCanceledException {
+ final Bundle options = new Bundle();
+ final String accountName = Fixtures.PREFIX_NAME_SUCCESS + "@"
+ + Fixtures.SUFFIX_NAME_FIXTURE;
+ options.putString(Fixtures.KEY_ACCOUNT_NAME, accountName);
+ options.putAll(OPTIONS_BUNDLE);
+
+ Bundle resultBundle = startAddAccountSession(am, ACCOUNT_TYPE, AUTH_TOKEN_TYPE,
+ REQUIRED_FEATURES,
+ options, null /* activity */, null /* callback */, null /* handler */);
+
+ validateStartAddAccountSessionParametersAndOptions(accountName, options);
+
+ // Assert returned result
+ // Assert that auth token was stripped.
+ assertNull(resultBundle.get(AccountManager.KEY_AUTHTOKEN));
+ assertNull(resultBundle.getBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE));
+ assertEquals(ACCOUNT_PASSWORD, resultBundle.getString(AccountManager.KEY_PASSWORD));
+ assertEquals(ACCOUNT_STATUS_TOKEN,
+ resultBundle.getString(AccountManager.KEY_ACCOUNT_STATUS_TOKEN));
+ }
+
+ /**
+ * Tests startAddAccountSession() with empty session bundle. An encrypted
+ * session bundle, account password and status token should be included in
+ * the result as session bundle is not inspected.
+ */
+ public void testStartAddAccountSessionWithEmptySessionBundle()
+ throws IOException, AuthenticatorException, OperationCanceledException {
+ final Bundle options = new Bundle();
+ final String accountName = Fixtures.PREFIX_NAME_SUCCESS + "@"
+ + Fixtures.SUFFIX_NAME_FIXTURE;
+ options.putString(Fixtures.KEY_ACCOUNT_NAME, accountName);
+ options.putBundle(Fixtures.KEY_ACCOUNT_SESSION_BUNDLE, new Bundle());
+ options.putAll(OPTIONS_BUNDLE);
+
+ Bundle resultBundle = startAddAccountSession(am, ACCOUNT_TYPE, AUTH_TOKEN_TYPE,
+ REQUIRED_FEATURES,
+ options, null /* activity */, null /* callback */, null /* handler */);
+
+ validateStartAddAccountSessionParametersAndOptions(accountName, options);
+
+ // Assert returned result
+ // Assert that auth token was stripped.
+ assertNull(resultBundle.get(AccountManager.KEY_AUTHTOKEN));
+ validateSessionBundleAndPasswordAndStatusTokenResult(resultBundle);
+ }
+
+ /**
+ * Tests startAddAccountSession with authenticator activity started. When
+ * Activity is provided, AccountManager would start the resolution Intent
+ * and return the final result which contains an encrypted session bundle,
+ * account password and status token.
+ */
+ public void testStartAddAccountSessionIntervene()
+ throws IOException, AuthenticatorException, OperationCanceledException {
+ final String accountName = Fixtures.PREFIX_NAME_INTERVENE + "@"
+ + Fixtures.SUFFIX_NAME_FIXTURE;
+ final Bundle options = createOptionsWithAccountName(accountName);
+
+ Bundle resultBundle = startAddAccountSession(am, ACCOUNT_TYPE, AUTH_TOKEN_TYPE,
+ REQUIRED_FEATURES,
+ options, mActivity, null /* callback */, null /* handler */);
+
+ validateStartAddAccountSessionParametersAndOptions(accountName, options);
+
+ // Assert returned result
+ assertNull(resultBundle.getParcelable(AccountManager.KEY_INTENT));
+ // Assert that auth token was stripped.
+ assertNull(resultBundle.get(AccountManager.KEY_AUTHTOKEN));
+ validateSessionBundleAndPasswordAndStatusTokenResult(resultBundle);
+ }
+
+ /**
+ * Tests startAddAccountSession with KEY_INTENT returned but not started
+ * automatically. When no Activity is provided and authenticator requires
+ * additional data from user, KEY_INTENT will be returned by AccountManager.
+ */
+ public void testStartAddAccountSessionWithReturnIntent()
+ throws IOException, AuthenticatorException, OperationCanceledException {
+ final String accountName = Fixtures.PREFIX_NAME_INTERVENE + "@"
+ + Fixtures.SUFFIX_NAME_FIXTURE;
+ final Bundle options = createOptionsWithAccountName(accountName);
+
+ Bundle resultBundle = startAddAccountSession(am, ACCOUNT_TYPE, AUTH_TOKEN_TYPE,
+ REQUIRED_FEATURES,
+ options, null /* activity */, null /* callback */, null /* handler */);
+
+ validateStartAddAccountSessionParametersAndOptions(accountName, options);
+
+ // Assert returned result
+ Intent returnIntent = resultBundle.getParcelable(AccountManager.KEY_INTENT);
+ // Assert that KEY_INTENT is returned.
+ assertNotNull(returnIntent);
+ assertNotNull(returnIntent.getParcelableExtra(Fixtures.KEY_RESULT));
+ // Assert that no other data is returned.
+ assertNull(resultBundle.getString(AccountManager.KEY_ACCOUNT_STATUS_TOKEN));
+ assertNull(resultBundle.getString(AccountManager.KEY_PASSWORD));
+ assertNull(resultBundle.getString(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE));
+ assertNull(resultBundle.get(AccountManager.KEY_AUTHTOKEN));
+ }
+
+ /**
+ * Tests startAddAccountSession error case. AuthenticatorException is
+ * expected when authenticator return
+ * {@link AccountManager#ERROR_CODE_INVALID_RESPONSE} error code.
+ */
+ public void testStartAddAccountSessionError() throws IOException, OperationCanceledException {
+ final String accountName = Fixtures.PREFIX_NAME_ERROR + "@"
+ + Fixtures.SUFFIX_NAME_FIXTURE;
+ final Bundle options = createOptionsWithAccountName(accountName);
+ options.putInt(AccountManager.KEY_ERROR_CODE, AccountManager.ERROR_CODE_INVALID_RESPONSE);
+ options.putString(AccountManager.KEY_ERROR_MESSAGE, ERROR_MESSAGE);
+
+ try {
+ startAddAccountSession(am, ACCOUNT_TYPE, AUTH_TOKEN_TYPE, REQUIRED_FEATURES,
+ options, null /* activity */, null /* callback */, null /* handler */);
+ fail("startAddAccountSession should throw AuthenticatorException in error case.");
+ } catch (AuthenticatorException e) {
+ }
+ }
+
+ /**
+ * Tests startAddAccountSession() with callback and handler. An encrypted
+ * session bundle, account password and status token should be included in
+ * the result. Callback should be triggered with the result regardless of a
+ * handler is provider or not.
+ */
+ public void testStartAddAccountSessionWithCallbackAndHandler()
+ throws IOException, AuthenticatorException, OperationCanceledException {
+ testStartAddAccountSessionWithCallbackAndHandler(null /* handler */);
+ testStartAddAccountSessionWithCallbackAndHandler(new Handler(Looper.getMainLooper()));
+ }
+
+ /**
+ * Tests startAddAccountSession() with callback and handler and activity
+ * started. When Activity is provided, AccountManager would start the
+ * resolution Intent and return the final result which contains an encrypted
+ * session bundle, account password and status token. Callback should be
+ * triggered with the result regardless of a handler is provider or not.
+ */
+ public void testStartAddAccountSessionWithCallbackAndHandlerWithIntervene()
+ throws IOException, AuthenticatorException, OperationCanceledException {
+ testStartAddAccountSessionWithCallbackAndHandlerWithIntervene(null /* handler */);
+ testStartAddAccountSessionWithCallbackAndHandlerWithIntervene(
+ new Handler(Looper.getMainLooper()));
+ }
+
+ /**
+ * Tests startAddAccountSession() with callback and handler with KEY_INTENT
+ * returned. When no Activity is provided and authenticator requires
+ * additional data from user, KEY_INTENT will be returned by AccountManager
+ * in callback regardless of a handler is provider or not.
+ */
+ public void testStartAddAccountSessionWithCallbackAndHandlerWithReturnIntent()
+ throws IOException, AuthenticatorException, OperationCanceledException {
+ testStartAddAccountSessionWithCallbackAndHandlerWithReturnIntent(null /* handler */);
+ testStartAddAccountSessionWithCallbackAndHandlerWithReturnIntent(
+ new Handler(Looper.getMainLooper()));
+ }
+
+ /**
+ * Tests startAddAccountSession() error case with callback and handler.
+ * AuthenticatorException is expected when authenticator return
+ * {@link AccountManager#ERROR_CODE_INVALID_RESPONSE} error code.
+ */
+ public void testStartAddAccountSessionErrorWithCallbackAndHandler()
+ throws IOException, OperationCanceledException {
+ testStartAddAccountSessionErrorWithCallbackAndHandler(null /* handler */);
+ testStartAddAccountSessionErrorWithCallbackAndHandler(new Handler(Looper.getMainLooper()));
+ }
+
+ private void testStartAddAccountSessionWithCallbackAndHandler(Handler handler)
+ throws IOException, AuthenticatorException, OperationCanceledException {
+ final String accountName = Fixtures.PREFIX_NAME_SUCCESS + "@"
+ + Fixtures.SUFFIX_NAME_FIXTURE;
+ final Bundle options = createOptionsWithAccountName(accountName);
+
+ // Wait with timeout for the callback to do its work
+ final CountDownLatch latch = new CountDownLatch(1);
+
+ AccountManagerCallback<Bundle> callback = new AccountManagerCallback<Bundle>() {
+ @Override
+ public void run(AccountManagerFuture<Bundle> bundleFuture) {
+ Bundle resultBundle = getResultExpectNoException(bundleFuture);
+
+ validateStartAddAccountSessionParametersAndOptions(accountName, options);
+
+ // Assert returned result
+ // Assert that auth token was stripped.
+ assertNull(resultBundle.get(AccountManager.KEY_AUTHTOKEN));
+ validateSessionBundleAndPasswordAndStatusTokenResult(resultBundle);
+
+ latch.countDown();
+ }
+ };
+
+ startAddAccountSession(am, ACCOUNT_TYPE, AUTH_TOKEN_TYPE, REQUIRED_FEATURES, options,
+ mActivity, callback, handler);
+ waitForLatch(latch);
+ }
+
+ private void testStartAddAccountSessionWithCallbackAndHandlerWithIntervene(Handler handler)
+ throws IOException, AuthenticatorException, OperationCanceledException {
+ final String accountName = Fixtures.PREFIX_NAME_INTERVENE + "@"
+ + Fixtures.SUFFIX_NAME_FIXTURE;
+ final Bundle options = createOptionsWithAccountName(accountName);
+
+ // Wait with timeout for the callback to do its work
+ final CountDownLatch latch = new CountDownLatch(1);
+
+ AccountManagerCallback<Bundle> callback = new AccountManagerCallback<Bundle>() {
+ @Override
+ public void run(AccountManagerFuture<Bundle> bundleFuture) {
+ Bundle resultBundle = getResultExpectNoException(bundleFuture);
+
+ validateStartAddAccountSessionParametersAndOptions(accountName, options);
+
+ // Assert returned result
+ assertNull(resultBundle.getParcelable(AccountManager.KEY_INTENT));
+ // Assert that auth token was stripped.
+ assertNull(resultBundle.get(AccountManager.KEY_AUTHTOKEN));
+ validateSessionBundleAndPasswordAndStatusTokenResult(resultBundle);
+
+ latch.countDown();
+ }
+ };
+
+ startAddAccountSession(am, ACCOUNT_TYPE, AUTH_TOKEN_TYPE, REQUIRED_FEATURES, options,
+ mActivity, callback, handler);
+ waitForLatch(latch);
+ }
+
+ private void testStartAddAccountSessionWithCallbackAndHandlerWithReturnIntent(Handler handler)
+ throws IOException, AuthenticatorException, OperationCanceledException {
+ final String accountName = Fixtures.PREFIX_NAME_INTERVENE + "@"
+ + Fixtures.SUFFIX_NAME_FIXTURE;
+ final Bundle options = createOptionsWithAccountName(accountName);
+
+ // Wait with timeout for the callback to do its work
+ final CountDownLatch latch = new CountDownLatch(1);
+
+ AccountManagerCallback<Bundle> callback = new AccountManagerCallback<Bundle>() {
+ @Override
+ public void run(AccountManagerFuture<Bundle> bundleFuture) {
+ Bundle resultBundle = getResultExpectNoException(bundleFuture);
+
+ validateStartAddAccountSessionParametersAndOptions(accountName, options);
+
+ // Assert returned result
+ Intent returnIntent = resultBundle.getParcelable(AccountManager.KEY_INTENT);
+ // Assert KEY_INTENT is returned.
+ assertNotNull(returnIntent);
+ assertNotNull(returnIntent.getParcelableExtra(Fixtures.KEY_RESULT));
+ // Assert that no other data is returned.
+ assertNull(resultBundle.getString(AccountManager.KEY_ACCOUNT_STATUS_TOKEN));
+ assertNull(resultBundle.getString(AccountManager.KEY_PASSWORD));
+ assertNull(resultBundle.getString(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE));
+ assertNull(resultBundle.get(AccountManager.KEY_AUTHTOKEN));
+
+ latch.countDown();
+ }
+ };
+
+ startAddAccountSession(am, ACCOUNT_TYPE, AUTH_TOKEN_TYPE, REQUIRED_FEATURES, options, null,
+ callback, handler);
+ waitForLatch(latch);
+ }
+
+ private void testStartAddAccountSessionErrorWithCallbackAndHandler(Handler handler)
+ throws IOException, OperationCanceledException {
+ final String accountName = Fixtures.PREFIX_NAME_ERROR + "@" + Fixtures.SUFFIX_NAME_FIXTURE;
+ final Bundle options = createOptionsWithAccountName(accountName);
+ options.putInt(AccountManager.KEY_ERROR_CODE, AccountManager.ERROR_CODE_INVALID_RESPONSE);
+ options.putString(AccountManager.KEY_ERROR_MESSAGE, ERROR_MESSAGE);
+
+ // Wait with timeout for the callback to do its work
+ final CountDownLatch latch = new CountDownLatch(1);
+
+ AccountManagerCallback<Bundle> callback = new AccountManagerCallback<Bundle>() {
+ @Override
+ public void run(AccountManagerFuture<Bundle> bundleFuture) {
+ try {
+ bundleFuture.getResult();
+ fail("should have thrown an AuthenticatorException");
+ } catch (OperationCanceledException e) {
+ fail("should not throw an OperationCanceledException");
+ } catch (IOException e) {
+ fail("should not throw an IOException");
+ } catch (AuthenticatorException e) {
+ latch.countDown();
+ }
+ }
+ };
+
+ try {
+ startAddAccountSession(am, ACCOUNT_TYPE, AUTH_TOKEN_TYPE, REQUIRED_FEATURES, options,
+ mActivity, callback, handler);
+ // AuthenticatorException should be thrown when authenticator
+ // returns AccountManager.ERROR_CODE_INVALID_RESPONSE.
+ fail("should have thrown an AuthenticatorException");
+ } catch (AuthenticatorException e1) {
+ }
+
+ waitForLatch(latch);
+ }
+
+ private Bundle startAddAccountSession(AccountManager am, String accountType,
+ String authTokenType,
+ String[] requiredFeatures, Bundle options, Activity activity,
+ AccountManagerCallback<Bundle> callback, Handler handler)
+ throws IOException, AuthenticatorException, OperationCanceledException {
+
+ AccountManagerFuture<Bundle> futureBundle = am.startAddAccountSession(accountType,
+ authTokenType,
+ requiredFeatures, options, activity, callback, handler);
+
+ Bundle resultBundle = futureBundle.getResult();
+ assertTrue(futureBundle.isDone());
+ assertNotNull(resultBundle);
+
+ return resultBundle;
+ }
+
+ private Bundle createOptionsWithAccountName(final String accountName) {
+ Bundle options = new Bundle();
+ options.putString(Fixtures.KEY_ACCOUNT_NAME, accountName);
+ options.putBundle(Fixtures.KEY_ACCOUNT_SESSION_BUNDLE, SESSION_BUNDLE);
+ options.putAll(OPTIONS_BUNDLE);
+ return options;
+ }
+
+ private void validateStartAddAccountSessionParametersAndOptions(
+ String accountName, Bundle options) {
+ // Assert parameters has been passed correctly
+ validateAccountAndAuthTokenType();
+ validateFeatures();
+
+ // Validate options
+ validateOptions(options, mockAuthenticator.mOptionsStartAddAccountSession);
+ assertNotNull(mockAuthenticator.mOptionsStartAddAccountSession);
+ assertEquals(accountName, mockAuthenticator.mOptionsStartAddAccountSession
+ .getString(Fixtures.KEY_ACCOUNT_NAME));
+
+ validateSystemOptions(mockAuthenticator.mOptionsStartAddAccountSession);
+ validateOptions(null, mockAuthenticator.mOptionsUpdateCredentials);
+ validateOptions(null, mockAuthenticator.mOptionsConfirmCredentials);
+ validateOptions(null, mockAuthenticator.mOptionsGetAuthToken);
+ validateOptions(null, mockAuthenticator.mOptionsAddAccount);
+ }
+
+ private void validateSessionBundleAndPasswordAndStatusTokenResult(Bundle resultBundle) {
+ Bundle sessionBundle = resultBundle.getBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE);
+ assertNotNull(sessionBundle);
+ // Assert that session bundle is encrypted and hence data not visible.
+ assertNull(sessionBundle.getString(SESSION_DATA_NAME_1));
+ assertEquals(ACCOUNT_PASSWORD, resultBundle.getString(AccountManager.KEY_PASSWORD));
+ assertEquals(ACCOUNT_STATUS_TOKEN,
+ resultBundle.getString(AccountManager.KEY_ACCOUNT_STATUS_TOKEN));
+ }
+
+ private Bundle getResultExpectNoException(AccountManagerFuture<Bundle> bundleFuture) {
+ try {
+ return bundleFuture.getResult();
+ } catch (OperationCanceledException e) {
+ fail("should not throw an OperationCanceledException");
+ } catch (IOException e) {
+ fail("should not throw an IOException");
+ } catch (AuthenticatorException e) {
+ fail("should not throw an AuthenticatorException");
+ }
+ return null;
+ }
}
diff --git a/tests/tests/accounts/src/android/accounts/cts/AccountManagerUnaffiliatedAuthenticatorTests.java b/tests/tests/accounts/src/android/accounts/cts/AccountManagerUnaffiliatedAuthenticatorTests.java
index 2068f4c..b3f7a82 100644
--- a/tests/tests/accounts/src/android/accounts/cts/AccountManagerUnaffiliatedAuthenticatorTests.java
+++ b/tests/tests/accounts/src/android/accounts/cts/AccountManagerUnaffiliatedAuthenticatorTests.java
@@ -25,6 +25,7 @@
import android.accounts.cts.common.Fixtures;
import android.content.ContentProviderClient;
import android.content.ContentResolver;
+import android.os.Bundle;
import android.os.RemoteException;
import android.test.AndroidTestCase;
@@ -222,5 +223,41 @@
getContext().getPackageName());
assertEquals(0, accounts.length);
}
+
+ /**
+ * Tests startAddAccountSession default implementation. An encrypted session
+ * bundle should always be returned without password or status token.
+ */
+ public void testStartAddAccountSessionDefaultImpl()
+ throws OperationCanceledException, AuthenticatorException, IOException {
+ Bundle options = new Bundle();
+ String accountName = Fixtures.PREFIX_NAME_SUCCESS + "@" + Fixtures.SUFFIX_NAME_FIXTURE;
+ options.putString(Fixtures.KEY_ACCOUNT_NAME, accountName);
+
+ AccountManagerFuture<Bundle> future = mAccountManager.startAddAccountSession(
+ Fixtures.TYPE_STANDARD_UNAFFILIATED, null /* authTokenType */,
+ null /* requiredFeatures */, options, null /* activity */, null /* callback */,
+ null /* handler */);
+
+ Bundle result = future.getResult();
+
+ // Validate that auth token was stripped from result.
+ assertNull(result.get(AccountManager.KEY_AUTHTOKEN));
+
+ // Validate that no password nor status token is returned in the result
+ // for default implementation.
+ validateNullPasswordAndStatusToken(result);
+
+ Bundle sessionBundle = result.getBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE);
+ // Validate session bundle is returned but data in the bundle is
+ // encrypted and hence not visible.
+ assertNotNull(sessionBundle);
+ assertNull(sessionBundle.getString(AccountManager.KEY_ACCOUNT_TYPE));
+ }
+
+ private void validateNullPasswordAndStatusToken(Bundle result) {
+ assertNull(result.getString(AccountManager.KEY_PASSWORD));
+ assertNull(result.getString(AccountManager.KEY_ACCOUNT_STATUS_TOKEN));
+ }
}
diff --git a/tests/tests/accounts/src/android/accounts/cts/MockAccountAuthenticator.java b/tests/tests/accounts/src/android/accounts/cts/MockAccountAuthenticator.java
index 470b629..95d6ed6 100644
--- a/tests/tests/accounts/src/android/accounts/cts/MockAccountAuthenticator.java
+++ b/tests/tests/accounts/src/android/accounts/cts/MockAccountAuthenticator.java
@@ -21,6 +21,7 @@
import android.accounts.AccountAuthenticatorResponse;
import android.accounts.AccountManager;
import android.accounts.NetworkErrorException;
+import android.accounts.cts.common.Fixtures;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
@@ -56,6 +57,7 @@
public Bundle mOptionsConfirmCredentials;
public Bundle mOptionsAddAccount;
public Bundle mOptionsGetAuthToken;
+ public Bundle mOptionsStartAddAccountSession;
Account mAccount;
String[] mFeatures;
@@ -116,6 +118,7 @@
mOptionsAddAccount = null;
mOptionsGetAuthToken = null;
mOptionsConfirmCredentials = null;
+ mOptionsStartAddAccountSession = null;
mAccount = null;
mFeatures = null;
}
@@ -312,4 +315,75 @@
return super.getAccountCredentialsForCloning(response, account);
}
+
+ /**
+ * Start add account flow of the specified accountType to authenticate user.
+ */
+ @Override
+ public Bundle startAddAccountSession(AccountAuthenticatorResponse response, String accountType,
+ String authTokenType, String[] requiredFeatures, Bundle options)
+ throws NetworkErrorException {
+
+ this.mResponse = response;
+ this.mAccountType = accountType;
+ this.mAuthTokenType = authTokenType;
+ this.mRequiredFeatures = requiredFeatures;
+ this.mOptionsStartAddAccountSession = options;
+
+ String accountName = null;
+ boolean isCallbackRequired = false;
+ Bundle sessionBundle = null;
+ if (options != null) {
+ accountName = options.getString(Fixtures.KEY_ACCOUNT_NAME);
+ isCallbackRequired = options.getBoolean(Fixtures.KEY_CALLBACK_REQUIRED, false);
+ sessionBundle = options.getBundle(Fixtures.KEY_ACCOUNT_SESSION_BUNDLE);
+ }
+
+ Bundle result = new Bundle();
+ if (accountName.startsWith(Fixtures.PREFIX_NAME_SUCCESS)) {
+ // fill bundle with a success result.
+ result.putBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE, sessionBundle);
+ result.putString(AccountManager.KEY_ACCOUNT_STATUS_TOKEN,
+ AccountManagerTest.ACCOUNT_STATUS_TOKEN);
+ result.putString(AccountManager.KEY_PASSWORD, AccountManagerTest.ACCOUNT_PASSWORD);
+ result.putString(AccountManager.KEY_AUTHTOKEN,
+ Integer.toString(mTokenCounter.incrementAndGet()));
+ } else if (accountName.startsWith(Fixtures.PREFIX_NAME_INTERVENE)) {
+ // Specify data to be returned by the eventual activity.
+ Intent eventualActivityResultData = new Intent();
+ eventualActivityResultData.putExtra(AccountManager.KEY_AUTHTOKEN,
+ Integer.toString(mTokenCounter.incrementAndGet()));
+ eventualActivityResultData.putExtra(AccountManager.KEY_ACCOUNT_STATUS_TOKEN,
+ AccountManagerTest.ACCOUNT_STATUS_TOKEN);
+ eventualActivityResultData.putExtra(AccountManager.KEY_PASSWORD,
+ AccountManagerTest.ACCOUNT_PASSWORD);
+ eventualActivityResultData.putExtra(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE,
+ sessionBundle);
+ // Fill result with Intent.
+ Intent intent = new Intent(mContext, AccountAuthenticatorDummyActivity.class);
+ intent.putExtra(Fixtures.KEY_RESULT, eventualActivityResultData);
+ intent.putExtra(Fixtures.KEY_CALLBACK, response);
+
+ result.putParcelable(AccountManager.KEY_INTENT, intent);
+ } else {
+ // fill with error
+ int errorCode = AccountManager.ERROR_CODE_INVALID_RESPONSE;
+ String errorMsg = "Default Error Message";
+ if (options != null) {
+ errorCode = options.getInt(AccountManager.KEY_ERROR_CODE);
+ errorMsg = options.getString(AccountManager.KEY_ERROR_MESSAGE);
+ }
+ result.putInt(AccountManager.KEY_ERROR_CODE, errorCode);
+ result.putString(AccountManager.KEY_ERROR_MESSAGE, errorMsg);
+ }
+
+ try {
+ return (isCallbackRequired) ? null : result;
+ } finally {
+ if (isCallbackRequired) {
+ response.onResult(result);
+ }
+ }
+ }
+
}
diff --git a/tests/tests/animation/Android.mk b/tests/tests/animation/Android.mk
index 205adda..ed42d74 100644
--- a/tests/tests/animation/Android.mk
+++ b/tests/tests/animation/Android.mk
@@ -24,13 +24,19 @@
# When built, explicitly put it in the data partition.
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
+LOCAL_STATIC_JAVA_LIBRARIES += \
+ android-support-test \
+ mockito-target \
+ android-common \
+ ctsdeviceutil \
+ ctstestrunner \
+ platform-test-annotations
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
LOCAL_SRC_FILES := $(call all-java-files-under, src)
# Tag this module as a cts test artifact
LOCAL_COMPATIBILITY_SUITE := cts
-LOCAL_SDK_VERSION := current
-
include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/animation/src/android/animation/cts/AnimatorListenerAdapterTest.java b/tests/tests/animation/src/android/animation/cts/AnimatorListenerAdapterTest.java
new file mode 100644
index 0000000..c6c0bc4
--- /dev/null
+++ b/tests/tests/animation/src/android/animation/cts/AnimatorListenerAdapterTest.java
@@ -0,0 +1,45 @@
+/*
+ * 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.animation.cts;
+
+import android.animation.AnimatorListenerAdapter;
+import android.support.test.filters.SmallTest;
+
+import org.junit.Test;
+
+@SmallTest
+public class AnimatorListenerAdapterTest {
+ /**
+ * AnimatorListenerAdapter has a noop implementation of the AnimatorListener interface.
+ * It should do nothing, including when nulls are passed to it.
+ * <p>
+ * Mostly this test pokes the implementation so that it is counted as tested. There isn't
+ * much to test here since it has no implementation.
+ */
+ @Test
+ public void testNullOk() throws Throwable {
+ AnimatorListenerAdapter adapter = new MyAdapter();
+ adapter.onAnimationStart(null);
+ adapter.onAnimationEnd(null);
+ adapter.onAnimationRepeat(null);
+ adapter.onAnimationCancel(null);
+ adapter.onAnimationPause(null);
+ adapter.onAnimationResume(null);
+ }
+
+ private static class MyAdapter extends AnimatorListenerAdapter {
+ }
+}
diff --git a/tests/tests/animation/src/android/animation/cts/AnimatorSetTest.java b/tests/tests/animation/src/android/animation/cts/AnimatorSetTest.java
index 3bf86de..77b1583 100644
--- a/tests/tests/animation/src/android/animation/cts/AnimatorSetTest.java
+++ b/tests/tests/animation/src/android/animation/cts/AnimatorSetTest.java
@@ -438,6 +438,82 @@
assertSame(animator2.getInterpolator(), clone2.getInterpolator());
}
+ public void testSetSynchronized() throws Throwable {
+ final TargetObj obj1 = new TargetObj();
+ final TargetObj obj2 = new TargetObj();
+ ObjectAnimator anim1 = ObjectAnimator.ofFloat(obj1, "val", 0, 1);
+ ObjectAnimator anim2 = ObjectAnimator.ofFloat(obj1, "val", 0, 1);
+ ObjectAnimator anim3 = ObjectAnimator.ofFloat(obj1, "val", 0, 1);
+ anim1.setStartDelay(100);
+ anim2.setStartDelay(400);
+ anim3.setStartDelay(700);
+ final AnimatorSet combined1 = new AnimatorSet();
+ combined1.playTogether(anim1, anim2, anim3);
+
+ ObjectAnimator anim4 = ObjectAnimator.ofFloat(obj2, "val", 0, 1);
+ ObjectAnimator anim5 = ObjectAnimator.ofFloat(obj2, "val", 0, 1);
+ ObjectAnimator anim6 = ObjectAnimator.ofFloat(obj2, "val", 0, 1);
+ final AnimatorSet combined2 = new AnimatorSet();
+ combined2.playSequentially(anim4, anim5, anim6);
+ combined2.setStartDelay(100);
+
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ combined1.start();
+ combined2.start();
+ }
+ });
+
+ while (combined1.isRunning()) {
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ assertEquals(obj1.value, obj2.value);
+ }
+ });
+ }
+ }
+
+ public void testNotifiesAfterEnd() throws Throwable {
+ final ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
+ Animator.AnimatorListener listener = new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ assertTrue(animation.isStarted());
+ assertTrue(animation.isRunning());
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ assertFalse(animation.isRunning());
+ assertFalse(animation.isStarted());
+ super.onAnimationEnd(animation);
+ }
+ };
+ animator.addListener(listener);
+ final AnimatorSet animatorSet = new AnimatorSet();
+ animatorSet.playTogether(animator);
+ animatorSet.addListener(listener);
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ animatorSet.start();
+ animator.end();
+ assertFalse(animator.isStarted());
+ assertFalse(animatorSet.isStarted());
+ }
+ });
+ }
+
+ static class TargetObj {
+ public float value = 0;
+
+ public void setVal(float value) {
+ this.value = value;
+ }
+ }
+
class AnimateObject {
int x = 1;
int y = 2;
diff --git a/tests/tests/animation/src/android/animation/cts/EvaluatorTest.java b/tests/tests/animation/src/android/animation/cts/EvaluatorTest.java
index 0812351..9c90f40 100644
--- a/tests/tests/animation/src/android/animation/cts/EvaluatorTest.java
+++ b/tests/tests/animation/src/android/animation/cts/EvaluatorTest.java
@@ -27,10 +27,12 @@
import android.graphics.PointF;
import android.graphics.Rect;
import android.test.InstrumentationTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
/**
* Tests for the various Evaluator classes in android.animation
*/
+@SmallTest
public class EvaluatorTest extends InstrumentationTestCase {
public void testFloatEvaluator() {
@@ -50,10 +52,18 @@
}
public void testFloatArrayEvaluator() {
+ FloatArrayEvaluator evaluator = new FloatArrayEvaluator();
+ floatArrayEvaluatorTestImpl(evaluator, null);
+
+ float[] reusableArray = new float[2];
+ FloatArrayEvaluator evaluator2 = new FloatArrayEvaluator(reusableArray);
+ floatArrayEvaluatorTestImpl(evaluator2, reusableArray);
+ }
+
+ private void floatArrayEvaluatorTestImpl(FloatArrayEvaluator evaluator, float[] reusedArray) {
float[] start = {0f, 0f};
float[] end = {.8f, 1.0f};
float fraction = 0.5f;
- FloatArrayEvaluator evaluator = new FloatArrayEvaluator();
float[] result = evaluator.evaluate(0, start, end);
assertEquals(start[0], result[0], .001f);
@@ -66,51 +76,63 @@
result = evaluator.evaluate(1, start, end);
assertEquals(end[0], result[0], .001f);
assertEquals(end[1], result[1], .001f);
+
+ if (reusedArray != null) {
+ assertEquals(reusedArray, result);
+ }
}
public void testArgbEvaluator() throws Throwable {
- final int RED = 0xffFF8080;
- final int BLUE = 0xff8080FF;
- int aRED = Color.alpha(RED);
- int rRED = Color.red(RED);
- int gRED = Color.green(RED);
- int bRED = Color.blue(RED);
- int aBLUE = Color.alpha(BLUE);
- int rBLUE = Color.red(BLUE);
- int gBLUE = Color.green(BLUE);
- int bBLUE = Color.blue(BLUE);
+ final int START = 0xffFF8080;
+ final int END = 0xff8080FF;
+ int aSTART = Color.alpha(START);
+ int rSTART = Color.red(START);
+ int gSTART = Color.green(START);
+ int bSTART = Color.blue(START);
+ int aEND = Color.alpha(END);
+ int rEND = Color.red(END);
+ int gEND = Color.green(END);
+ int bEND = Color.blue(END);
final ArgbEvaluator evaluator = new ArgbEvaluator();
- int result = (Integer) evaluator.evaluate(0, RED, BLUE);
+ int result = (Integer) evaluator.evaluate(0, START, END);
int aResult = Color.alpha(result);
int rResult = Color.red(result);
int gResult = Color.green(result);
int bResult = Color.blue(result);
- assertEquals(aRED, aResult);
- assertEquals(rRED, rResult);
- assertEquals(gRED, gResult);
- assertEquals(bRED, bResult);
+ assertEquals(aSTART, aResult);
+ assertEquals(rSTART, rResult);
+ assertEquals(gSTART, gResult);
+ assertEquals(bSTART, bResult);
- result = (Integer) evaluator.evaluate(.5f, RED, BLUE);
+ result = (Integer) evaluator.evaluate(.5f, START, END);
aResult = Color.alpha(result);
rResult = Color.red(result);
gResult = Color.green(result);
bResult = Color.blue(result);
assertEquals(0xff, aResult);
- assertEquals(rRED + (int)(.5f * (rBLUE - rRED)), rResult);
- assertEquals(gRED + (int)(.5f * (gBLUE - gRED)), gResult);
- assertEquals(bRED + (int)(.5f * (bBLUE - bRED)), bResult);
+ assertEquals(0x80, gResult);
+ if (rSTART < rEND) {
+ assertTrue(rResult > rSTART && rResult < rEND);
+ } else {
+ assertTrue(rResult < rSTART && rResult > rEND);
+ }
+ if (bSTART < bEND) {
+ assertTrue(bResult > bSTART && bResult < bEND);
+ } else {
+ assertTrue(bResult < bSTART && bResult > bEND);
+ }
- result = (Integer) evaluator.evaluate(1, RED, BLUE);
+ result = (Integer) evaluator.evaluate(1, START, END);
aResult = Color.alpha(result);
rResult = Color.red(result);
gResult = Color.green(result);
bResult = Color.blue(result);
- assertEquals(aBLUE, aResult);
- assertEquals(rBLUE, rResult);
- assertEquals(gBLUE, gResult);
- assertEquals(bBLUE, bResult);
+ assertEquals(aEND, aResult);
+ assertEquals(rEND, rResult);
+ assertEquals(gEND, gResult);
+ assertEquals(bEND, bResult);
}
public void testIntEvaluator() throws Throwable {
@@ -130,10 +152,18 @@
}
public void testIntArrayEvaluator() {
+ IntArrayEvaluator evaluator = new IntArrayEvaluator();
+ intArrayEvaluatorTestImpl(evaluator, null);
+
+ int[] reusableArray = new int[2];
+ IntArrayEvaluator evaluator2 = new IntArrayEvaluator(reusableArray);
+ intArrayEvaluatorTestImpl(evaluator2, reusableArray);
+ }
+
+ private void intArrayEvaluatorTestImpl(IntArrayEvaluator evaluator, int[] reusedArray) {
int[] start = {0, 0};
int[] end = {80, 100};
float fraction = 0.5f;
- IntArrayEvaluator evaluator = new IntArrayEvaluator();
int[] result = evaluator.evaluate(0, start, end);
assertEquals(start[0], result[0]);
@@ -146,10 +176,22 @@
result = evaluator.evaluate(1, start, end);
assertEquals(end[0], result[0]);
assertEquals(end[1], result[1]);
+
+ if (reusedArray != null) {
+ assertEquals(reusedArray, result);
+ }
}
public void testRectEvaluator() throws Throwable {
final RectEvaluator evaluator = new RectEvaluator();
+ rectEvaluatorTestImpl(evaluator, null);
+
+ Rect reusableRect = new Rect();
+ final RectEvaluator evaluator2 = new RectEvaluator(reusableRect);
+ rectEvaluatorTestImpl(evaluator2, reusableRect);
+ }
+
+ private void rectEvaluatorTestImpl(RectEvaluator evaluator, Rect reusedRect) {
final Rect start = new Rect(0, 0, 0, 0);
final Rect end = new Rect(100, 200, 300, 400);
final float fraction = 0.5f;
@@ -171,10 +213,22 @@
assertEquals(end.top, result.top, .001f);
assertEquals(end.right, result.right, .001f);
assertEquals(end.bottom, result.bottom, .001f);
+
+ if (reusedRect != null) {
+ assertEquals(reusedRect, result);
+ }
}
public void testPointFEvaluator() throws Throwable {
final PointFEvaluator evaluator = new PointFEvaluator();
+ pointFEvaluatorTestImpl(evaluator, null);
+
+ PointF reusablePoint = new PointF();
+ final PointFEvaluator evaluator2 = new PointFEvaluator(reusablePoint);
+ pointFEvaluatorTestImpl(evaluator2, reusablePoint);
+ }
+
+ private void pointFEvaluatorTestImpl(PointFEvaluator evaluator, PointF reusedPoint) {
final PointF start = new PointF(0, 0);
final PointF end = new PointF(100, 200);
final float fraction = 0.5f;
@@ -190,6 +244,10 @@
result = evaluator.evaluate(1, start, end);
assertEquals(end.x, result.x, .001f);
assertEquals(end.y, result.y, .001f);
+
+ if (reusedPoint != null) {
+ assertEquals(reusedPoint, result);
+ }
}
/**
diff --git a/tests/tests/animation/src/android/animation/cts/ObjectAnimatorTest.java b/tests/tests/animation/src/android/animation/cts/ObjectAnimatorTest.java
index 6ff1cf9..a96b8c9 100644
--- a/tests/tests/animation/src/android/animation/cts/ObjectAnimatorTest.java
+++ b/tests/tests/animation/src/android/animation/cts/ObjectAnimatorTest.java
@@ -16,17 +16,39 @@
package android.animation.cts;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
import android.animation.ArgbEvaluator;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
+import android.animation.TypeConverter;
import android.animation.ValueAnimator;
+import android.graphics.Color;
+import android.graphics.Path;
+import android.graphics.PointF;
import android.test.ActivityInstrumentationTestCase2;
import android.util.Property;
+import android.view.View;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.Interpolator;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
public class ObjectAnimatorTest extends
ActivityInstrumentationTestCase2<AnimationActivity> {
+ private static final float LINE1_START = -32f;
+ private static final float LINE1_END = -2f;
+ private static final float LINE1_Y = 0f;
+ private static final float LINE2_START = 2f;
+ private static final float LINE2_END = 12f;
+ private static final float QUADRATIC_CTRL_PT1_X = 0f;
+ private static final float QUADRATIC_CTRL_PT1_Y = 0f;
+ private static final float QUADRATIC_CTRL_PT2_X = 50f;
+ private static final float QUADRATIC_CTRL_PT2_Y = 20f;
+ private static final float QUADRATIC_CTRL_PT3_X = 100f;
+ private static final float QUADRATIC_CTRL_PT3_Y = 0f;
+
private AnimationActivity mActivity;
private ObjectAnimator mObjectAnimator;
private long mDuration = 1000;
@@ -162,6 +184,59 @@
assertTrue(endColor <= i.intValue());
}
+ public void testOfArgb() throws Throwable {
+ Object object = mActivity.view;
+ String property = "backgroundColor";
+ int start = 0xffff0000;
+ int end = 0xff0000ff;
+ int[] values = {start, end};
+ int startRed = Color.red(start);
+ int startBlue = Color.blue(start);
+ int endRed = Color.red(end);
+ int endBlue = Color.blue(end);
+ final ObjectAnimator animator = ObjectAnimator.ofArgb(object, property, start, end);
+ animator.setDuration(mDuration);
+
+ final CountDownLatch latch = new CountDownLatch(1);
+ animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ if (animation.getAnimatedFraction() > .05f) {
+ latch.countDown();
+ }
+ }
+ });
+
+ this.runTestOnUiThread(new Runnable(){
+ public void run() {
+ animator.start();
+ }
+ });
+ boolean isRunning = animator.isRunning();
+ assertTrue(isRunning);
+
+ assertTrue(latch.await(500, TimeUnit.MILLISECONDS));
+
+ Integer animatedValue = (Integer) animator.getAnimatedValue();
+ int alpha = Color.alpha(animatedValue);
+ int red = Color.red(animatedValue);
+ int green = Color.green(animatedValue);
+ int blue = Color.blue(animatedValue);
+ assertTrue(red < startRed);
+ assertTrue(red > endRed);
+ assertTrue(blue > startBlue);
+ assertTrue(blue < endBlue);
+ assertEquals(255, alpha);
+ assertEquals(0, green);
+
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ animator.cancel();
+ }
+ });
+ }
+
public void testGetPropertyName() throws Throwable {
Object object = mActivity.view.newBall;
String propertyName = "backgroundColor";
@@ -230,6 +305,473 @@
assertEquals(interpolator, cloneAnimator.getInterpolator());
}
+ public void testOfFloat_Path() throws Throwable {
+ // Test for ObjectAnimator.ofFloat(Object, String, String, Path)
+ // Create a path that contains two disconnected line segments. Check that the animated
+ // property x and property y always stay on the line segments.
+ Path path = new Path();
+ path.moveTo(LINE1_START, LINE1_Y);
+ path.lineTo(LINE1_END, LINE1_Y);
+ path.moveTo(LINE2_START, LINE2_START);
+ path.lineTo(LINE2_END, LINE2_END);
+ final double totalLength = (LINE1_END - LINE1_START) + Math.sqrt(
+ (LINE2_END - LINE2_START) * (LINE2_END - LINE2_START) +
+ (LINE2_END - LINE2_START) * (LINE2_END - LINE2_START));
+ final double firstSegEndFraction = (LINE1_END - LINE1_START) / totalLength;
+ final float delta = 0.01f;
+
+ Object target = new Object() {
+ public void setX(float x) {
+ }
+
+ public void setY(float y) {
+ }
+ };
+ final CountDownLatch endLatch = new CountDownLatch(1);
+
+ final ObjectAnimator anim = ObjectAnimator.ofFloat(target, "x", "y", path);
+ anim.setDuration(200);
+ // Linear interpolator
+ anim.setInterpolator(null);
+ anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ float fraction = animation.getAnimatedFraction();
+ float x = (Float) animation.getAnimatedValue("x");
+ float y = (Float) animation.getAnimatedValue("y");
+
+ // Check that the point is on the path.
+ if (x <= 0) {
+ // First line segment is a horizontal line.
+ assertTrue(x >= LINE1_START);
+ assertTrue(x <= LINE1_END);
+ assertEquals(LINE1_Y, y);
+
+ // Check that the time animation stays on the first segment is proportional to
+ // the length of the first line segment.
+ assertTrue(fraction < firstSegEndFraction + delta);
+ } else {
+ assertTrue(x >= LINE2_START);
+ assertTrue(x <= LINE2_END);
+ assertEquals(x, y);
+
+ // Check that the time animation stays on the second segment is proportional to
+ // the length of the second line segment.
+ assertTrue(fraction > firstSegEndFraction - delta);
+ }
+ }
+ });
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ endLatch.countDown();
+ }
+ });
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ anim.start();
+ }
+ });
+ assertTrue(endLatch.await(400, TimeUnit.MILLISECONDS));
+ }
+
+ public void testOfInt_Path() throws Throwable {
+ // Test for ObjectAnimator.ofInt(Object, String, String, Path)
+ // Create a path that contains two disconnected line segments. Check that the animated
+ // property x and property y always stay on the line segments.
+ Path path = new Path();
+ path.moveTo(LINE1_START, -LINE1_START);
+ path.lineTo(LINE1_END, -LINE1_END);
+ path.moveTo(LINE2_START, LINE2_START);
+ path.lineTo(LINE2_END, LINE2_END);
+
+ Object target = new Object() {
+ public void setX(float x) {
+ }
+
+ public void setY(float y) {
+ }
+ };
+ final CountDownLatch endLatch = new CountDownLatch(1);
+ final ObjectAnimator anim = ObjectAnimator.ofInt(target, "x", "y", path);
+ anim.setDuration(200);
+
+ // Linear interpolator
+ anim.setInterpolator(null);
+ anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ float fraction = animation.getAnimatedFraction();
+ int x = (Integer) animation.getAnimatedValue("x");
+ int y = (Integer) animation.getAnimatedValue("y");
+
+ // Check that the point is on the path.
+ if (x <= 0) {
+ // Check that the time animation stays on the first segment is proportional to
+ // the length of the first line segment.
+ assertTrue(x >= LINE1_START);
+ assertTrue(x <= LINE1_END);
+ assertEquals(x, -y);
+
+ // First line segment is 3 times as long as the second line segment, so the
+ // 3/4 of the animation duration will be spent on the first line segment.
+ assertTrue(fraction <= 0.75f);
+ } else {
+ // Check that the time animation stays on the second segment is proportional to
+ // the length of the second line segment.
+ assertTrue(x >= LINE2_START);
+ assertTrue(x <= LINE2_END);
+ assertEquals(x, y);
+
+ assertTrue(fraction >= 0.75f);
+ }
+ }
+ });
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ endLatch.countDown();
+ }
+ });
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ anim.start();
+ }
+ });
+ assertTrue(endLatch.await(400, TimeUnit.MILLISECONDS));
+
+ }
+
+ public void testOfMultiFloat_Path() throws Throwable {
+ // Test for ObjectAnimator.ofMultiFloat(Object, String, Path);
+ // Create a quadratic bezier curve that are symmetric about the vertical line (x = 50).
+ // Expect when fraction < 0.5, x < 50, otherwise, x >= 50.
+ Path path = new Path();
+ path.moveTo(QUADRATIC_CTRL_PT1_X, QUADRATIC_CTRL_PT1_Y);
+ path.quadTo(QUADRATIC_CTRL_PT2_X, QUADRATIC_CTRL_PT2_Y,
+ QUADRATIC_CTRL_PT3_X, QUADRATIC_CTRL_PT3_Y);
+
+ Object target = new Object() {
+ public void setPosition(float x, float y) {
+ }
+ };
+
+ final CountDownLatch endLatch = new CountDownLatch(1);
+ final ObjectAnimator anim = ObjectAnimator.ofMultiFloat(target, "position", path);
+ // Linear interpolator
+ anim.setInterpolator(null);
+ anim.setDuration(200);
+
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ endLatch.countDown();
+ }
+ });
+
+ anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ float lastFraction = 0;
+ float lastX = 0;
+ float lastY = 0;
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ float[] values = (float[]) animation.getAnimatedValue();
+ assertEquals(2, values.length);
+ float x = values[0];
+ float y = values[1];
+ float fraction = animation.getAnimatedFraction();
+ // Given that the curve is symmetric about the line (x = 50), x should be less than
+ // 50 for half of the animation duration.
+ if (fraction < 0.5) {
+ assertTrue(x < QUADRATIC_CTRL_PT2_X);
+ } else {
+ assertTrue(x >= QUADRATIC_CTRL_PT2_X);
+ }
+
+ if (lastFraction > 0.5) {
+ // x should be increasing, y should be decreasing
+ assertTrue(x >= lastX);
+ assertTrue(y <= lastY);
+ } else if (fraction <= 0.5) {
+ // when fraction <= 0.5, both x, y should be increasing
+ assertTrue(x >= lastX);
+ assertTrue(y >= lastY);
+ }
+ lastX = x;
+ lastY = y;
+ lastFraction = fraction;
+ }
+ });
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ anim.start();
+ }
+ });
+ assertTrue(endLatch.await(400, TimeUnit.MILLISECONDS));
+ }
+
+ public void testOfMultiFloat() throws Throwable {
+ // Test for ObjectAnimator.ofMultiFloat(Object, String, float[][]);
+ final float[][] data = new float[10][];
+ for (int i = 0; i < data.length; i++) {
+ data[i] = new float[3];
+ data[i][0] = i;
+ data[i][1] = i * 2;
+ data[i][2] = 0f;
+ }
+
+ Object target = new Object() {
+ public void setPosition(float x, float y, float z) {
+ }
+ };
+ final CountDownLatch endLatch = new CountDownLatch(1);
+ final ObjectAnimator anim = ObjectAnimator.ofMultiFloat(target, "position", data);
+ anim.setInterpolator(null);
+ anim.setDuration(60);
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ endLatch.countDown();
+ }
+ });
+
+ anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ float fraction = animation.getAnimatedFraction();
+ float[] values = (float[]) animation.getAnimatedValue();
+ assertEquals(3, values.length);
+
+ float expectedX = fraction * (data.length - 1);
+
+ assertEquals(expectedX, values[0]);
+ assertEquals(expectedX * 2, values[1]);
+ assertEquals(0f, values[2]);
+ }
+ });
+
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ anim.start();
+ }
+ });
+ assertTrue(endLatch.await(200, TimeUnit.MILLISECONDS));
+ }
+
+ public void testOfMultiInt_Path() throws Throwable {
+ // Test for ObjectAnimator.ofMultiInt(Object, String, Path);
+ // Create a quadratic bezier curve that are symmetric about the vertical line (x = 50).
+ // Expect when fraction < 0.5, x < 50, otherwise, x >= 50.
+ Path path = new Path();
+ path.moveTo(QUADRATIC_CTRL_PT1_X, QUADRATIC_CTRL_PT1_Y);
+ path.quadTo(QUADRATIC_CTRL_PT2_X, QUADRATIC_CTRL_PT2_Y,
+ QUADRATIC_CTRL_PT3_X, QUADRATIC_CTRL_PT3_Y);
+
+ Object target = new Object() {
+ public void setPosition(int x, int y) {
+ }
+ };
+
+ final CountDownLatch endLatch = new CountDownLatch(1);
+ final ObjectAnimator anim = ObjectAnimator.ofMultiInt(target, "position", path);
+ // Linear interpolator
+ anim.setInterpolator(null);
+ anim.setDuration(200);
+
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ endLatch.countDown();
+ }
+ });
+
+ anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ float lastFraction = 0;
+ int lastX = 0;
+ int lastY = 0;
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ int[] values = (int[]) animation.getAnimatedValue();
+ assertEquals(2, values.length);
+ int x = values[0];
+ int y = values[1];
+ float fraction = animation.getAnimatedFraction();
+ // Given that the curve is symmetric about the line (x = 50), x should be less than
+ // 50 for half of the animation duration.
+ if (fraction < 0.5) {
+ assertTrue(x < QUADRATIC_CTRL_PT2_X);
+ } else {
+ assertTrue(x >= QUADRATIC_CTRL_PT2_X);
+ }
+
+ if (lastFraction > 0.5) {
+ // x should be increasing, y should be decreasing
+ assertTrue(x >= lastX);
+ assertTrue(y <= lastY);
+ } else if (fraction <= 0.5) {
+ // when fraction <= 0.5, both x, y should be increasing
+ assertTrue(x >= lastX);
+ assertTrue(y >= lastY);
+ }
+ lastX = x;
+ lastY = y;
+ lastFraction = fraction;
+ }
+ });
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ anim.start();
+ }
+ });
+ assertTrue(endLatch.await(400, TimeUnit.MILLISECONDS));
+ }
+
+ public void testOfMultiInt() throws Throwable {
+ // Test for ObjectAnimator.ofMultiFloat(Object, String, int[][]);
+ final int[][] data = new int[10][];
+ for (int i = 0; i < data.length; i++) {
+ data[i] = new int[3];
+ data[i][0] = i;
+ data[i][1] = i * 2;
+ data[i][2] = 0;
+ }
+
+ Object target = new Object() {
+ public void setPosition(int x, int y, int z) {
+ }
+ };
+ final CountDownLatch endLatch = new CountDownLatch(1);
+ final ObjectAnimator anim = ObjectAnimator.ofMultiInt(target, "position", data);
+ anim.setInterpolator(null);
+ anim.setDuration(60);
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ endLatch.countDown();
+ }
+ });
+
+ anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ float fraction = animation.getAnimatedFraction();
+ int[] values = (int[]) animation.getAnimatedValue();
+ assertEquals(3, values.length);
+
+ int expectedX = Math.round(fraction * (data.length - 1));
+ int expectedY = Math.round(fraction * (data.length - 1) * 2);
+
+ // Allow a delta of 1 for rounding errors.
+ assertEquals(expectedX, values[0], 1);
+ assertEquals(expectedY, values[1], 1);
+ assertEquals(0, values[2]);
+ }
+ });
+
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ anim.start();
+ }
+ });
+ assertTrue(endLatch.await(200, TimeUnit.MILLISECONDS));
+ }
+
+ public void testOfObject_Converter() throws Throwable {
+ // Test for ObjectAnimator.ofObject(Object, String, TypeConverter<T, V>, Path)
+ // Create a path that contains two disconnected line segments. Check that the animated
+ // property x and property y always stay on the line segments.
+ Path path = new Path();
+ path.moveTo(LINE1_START, -LINE1_START);
+ path.lineTo(LINE1_END, -LINE1_END);
+ path.moveTo(LINE2_START, LINE2_START);
+ path.lineTo(LINE2_END, LINE2_END);
+
+ Object target1 = new Object() {
+ public void setDistance(float distance) {
+ }
+ };
+ Object target2 = new Object() {
+ public void setPosition(PointF pos) {
+ }
+ };
+ TypeConverter<PointF, Float> converter = new TypeConverter<PointF, Float>(
+ PointF.class, Float.class) {
+ @Override
+ public Float convert(PointF value) {
+ return (float) Math.sqrt(value.x * value.x + value.y * value.y);
+ }
+ };
+ final CountDownLatch endLatch = new CountDownLatch(2);
+
+ // Create two animators. One use a converter that converts the point to distance to origin.
+ // The other one does not have a type converter.
+ final ObjectAnimator anim1 = ObjectAnimator.ofObject(target1, "distance", converter, path);
+ anim1.setDuration(100);
+ anim1.setInterpolator(null);
+ anim1.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ endLatch.countDown();
+ }
+ });
+
+ final ObjectAnimator anim2 = ObjectAnimator.ofObject(target2, "position", null, path);
+ anim2.setDuration(100);
+ anim2.setInterpolator(null);
+ anim2.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ endLatch.countDown();
+ }
+ });
+ anim2.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ // Set the initial value of the distance to the distance between the first point on
+ // the path to the origin.
+ float mLastDistance = (float) (32 * Math.sqrt(2));
+ float mLastFraction = 0f;
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ float fraction = anim1.getAnimatedFraction();
+ assertEquals(fraction, anim2.getAnimatedFraction());
+ float distance = (Float) anim1.getAnimatedValue();
+ PointF position = (PointF) anim2.getAnimatedValue();
+
+ // Manually calculate the distance for the animator that doesn't have a
+ // TypeConverter, and expect the result to be the same as the animation value from
+ // the type converter.
+ float distanceFromPosition = (float) Math.sqrt(
+ position.x * position.x + position.y * position.y);
+ assertEquals(distance, distanceFromPosition, 0.0001f);
+
+ if (mLastFraction > 0.75) {
+ // In the 2nd line segment of the path, distance to origin should be increasing.
+ assertTrue(distance >= mLastDistance);
+ } else if (fraction < 0.75) {
+ assertTrue(distance <= mLastDistance);
+ }
+ mLastDistance = distance;
+ mLastFraction = fraction;
+ }
+ });
+
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ anim1.start();
+ anim2.start();
+ }
+ });
+
+ // Wait until both of the animations finish
+ assertTrue(endLatch.await(200, TimeUnit.MILLISECONDS));
+ }
+
public void testIsStarted() throws Throwable {
Object object = mActivity.view.newBall;
String property = "y";
@@ -308,6 +850,46 @@
});
}
+ public void testCachedValues() throws Throwable {
+ final AnimTarget target = new AnimTarget();
+ final ObjectAnimator anim = ObjectAnimator.ofFloat(target, "testValue", 100);
+ anim.setDuration(200);
+ final CountDownLatch twoFramesLatch = new CountDownLatch(2);
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ anim.start();
+ final View decor = getActivity().getWindow().getDecorView();
+ decor.postOnAnimation(new Runnable() {
+ @Override
+ public void run() {
+ if (twoFramesLatch.getCount() > 0) {
+ twoFramesLatch.countDown();
+ decor.postOnAnimation(this);
+ }
+ }
+ });
+ }
+ });
+
+ assertTrue("Animation didn't start in a reasonable time",
+ twoFramesLatch.await(100, TimeUnit.MILLISECONDS));
+
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ assertTrue("Start value should readjust to current position",
+ target.getTestValue() != 0);
+ anim.cancel();
+ anim.setupStartValues();
+ anim.start();
+ assertTrue("Start value should readjust to current position",
+ target.getTestValue() != 0);
+ anim.cancel();
+ }
+ });
+ }
+
static class AnimTarget {
private float mTestValue = 0;
diff --git a/tests/tests/animation/src/android/animation/cts/PropertyValuesHolderTest.java b/tests/tests/animation/src/android/animation/cts/PropertyValuesHolderTest.java
index 200ebce..5e23b29 100644
--- a/tests/tests/animation/src/android/animation/cts/PropertyValuesHolderTest.java
+++ b/tests/tests/animation/src/android/animation/cts/PropertyValuesHolderTest.java
@@ -21,9 +21,14 @@
import android.animation.Keyframe;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
+import android.animation.TypeConverter;
import android.animation.ValueAnimator;
+import android.graphics.Color;
+import android.graphics.Path;
+import android.graphics.PointF;
import android.graphics.drawable.ShapeDrawable;
import android.test.ActivityInstrumentationTestCase2;
+import android.util.FloatProperty;
import android.util.Property;
import android.view.View;
import android.view.animation.AccelerateInterpolator;
@@ -33,6 +38,18 @@
public class PropertyValuesHolderTest extends
ActivityInstrumentationTestCase2<AnimationActivity> {
+ private static final float LINE1_START = -32f;
+ private static final float LINE1_END = -2f;
+ private static final float LINE1_Y = 0f;
+ private static final float LINE2_START = 2f;
+ private static final float LINE2_END = 12f;
+ private static final float QUADRATIC_CTRL_PT1_X = 0f;
+ private static final float QUADRATIC_CTRL_PT1_Y = 0f;
+ private static final float QUADRATIC_CTRL_PT2_X = 50f;
+ private static final float QUADRATIC_CTRL_PT2_Y = 20f;
+ private static final float QUADRATIC_CTRL_PT3_X = 100f;
+ private static final float QUADRATIC_CTRL_PT3_Y = 0f;
+
private AnimationActivity mActivity;
private Animator mAnimator;
private long mDuration = 1000;
@@ -199,7 +216,7 @@
assertEquals("Animation should run as expected", 100f, mActivity.view.newBall.getY());
}
- public void testOffloat() throws Throwable {
+ public void testOfFloat() throws Throwable {
float[] values = {mStartY, mEndY};
PropertyValuesHolder pVHolder = PropertyValuesHolder.ofFloat(mProperty, values);
assertNotNull(pVHolder);
@@ -272,10 +289,425 @@
mDuration);
startAnimation(objectAnimator, colorAnimator);
Thread.sleep(1000);
- Integer i = (Integer) colorAnimator.getAnimatedValue();
- //We are going from less negative value to a more negative value
- assertTrue(i.intValue() <= startColor);
- assertTrue(endColor <= i.intValue());
+ Integer animatedValue = (Integer) colorAnimator.getAnimatedValue();
+ int redMin = Math.min(Color.red(startColor), Color.red(endColor));
+ int redMax = Math.max(Color.red(startColor), Color.red(endColor));
+ int blueMin = Math.min(Color.blue(startColor), Color.blue(endColor));
+ int blueMax = Math.max(Color.blue(startColor), Color.blue(endColor));
+ assertTrue(Color.red(animatedValue) >= redMin);
+ assertTrue(Color.red(animatedValue) <= redMax);
+ assertTrue(Color.blue(animatedValue) >= blueMin);
+ assertTrue(Color.blue(animatedValue) <= blueMax);
+ }
+
+ public void testOfMultiFloat_Path() throws Throwable {
+ // Test for PropertyValuesHolder.ofMultiFloat(String, Path);
+ // Create a quadratic bezier curve that are symmetric about the vertical line (x = 50).
+ // Expect when fraction < 0.5, x < 50, otherwise, x >= 50.
+ Path path = new Path();
+ path.moveTo(QUADRATIC_CTRL_PT1_X, QUADRATIC_CTRL_PT1_Y);
+ path.quadTo(QUADRATIC_CTRL_PT2_X, QUADRATIC_CTRL_PT2_Y,
+ QUADRATIC_CTRL_PT3_X, QUADRATIC_CTRL_PT3_Y);
+
+ PropertyValuesHolder pvh = PropertyValuesHolder.ofMultiFloat("position", path);
+ final ValueAnimator anim = ValueAnimator.ofPropertyValuesHolder(pvh);
+
+ final CountDownLatch endLatch = new CountDownLatch(1);
+ // Linear interpolator
+ anim.setInterpolator(null);
+ anim.setDuration(200);
+
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ endLatch.countDown();
+ }
+ });
+
+ anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ float lastFraction = 0;
+ float lastX = 0;
+ float lastY = 0;
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ float[] values = (float[]) animation.getAnimatedValue();
+ assertEquals(2, values.length);
+ float x = values[0];
+ float y = values[1];
+ float fraction = animation.getAnimatedFraction();
+ // Given that the curve is symmetric about the line (x = 50), x should be less than
+ // 50 for half of the animation duration.
+ if (fraction < 0.5) {
+ assertTrue(x < QUADRATIC_CTRL_PT2_X);
+ } else {
+ assertTrue(x >= QUADRATIC_CTRL_PT2_X);
+ }
+
+ if (lastFraction > 0.5) {
+ // x should be increasing, y should be decreasing
+ assertTrue(x >= lastX);
+ assertTrue(y <= lastY);
+ } else if (fraction <= 0.5) {
+ // when fraction <= 0.5, both x, y should be increasing
+ assertTrue(x >= lastX);
+ assertTrue(y >= lastY);
+ }
+ lastX = x;
+ lastY = y;
+ lastFraction = fraction;
+ }
+ });
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ anim.start();
+ }
+ });
+ assertTrue(endLatch.await(400, TimeUnit.MILLISECONDS));
+ }
+
+ public void testOfMultiFloat_Array() throws Throwable {
+ // Test for PropertyValuesHolder.ofMultiFloat(String, float[][]);
+ final float[][] data = new float[10][];
+ for (int i = 0; i < data.length; i++) {
+ data[i] = new float[3];
+ data[i][0] = i;
+ data[i][1] = i * 2;
+ data[i][2] = 0f;
+ }
+ final CountDownLatch endLatch = new CountDownLatch(1);
+ final PropertyValuesHolder pvh = PropertyValuesHolder.ofMultiFloat("position", data);
+
+ final ValueAnimator anim = ValueAnimator.ofPropertyValuesHolder(pvh);
+ anim.setInterpolator(null);
+ anim.setDuration(60);
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ endLatch.countDown();
+ }
+ });
+
+ anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ float fraction = animation.getAnimatedFraction();
+ float[] values = (float[]) animation.getAnimatedValue();
+ assertEquals(3, values.length);
+
+ float expectedX = fraction * (data.length - 1);
+
+ assertEquals(expectedX, values[0]);
+ assertEquals(expectedX * 2, values[1]);
+ assertEquals(0f, values[2]);
+ }
+ });
+
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ anim.start();
+ }
+ });
+ assertTrue(endLatch.await(200, TimeUnit.MILLISECONDS));
+ }
+
+ public void testOfMultiInt_Path() throws Throwable {
+ // Test for PropertyValuesHolder.ofMultiInt(String, Path);
+ // Create a quadratic bezier curve that are symmetric about the vertical line (x = 50).
+ // Expect when fraction < 0.5, x < 50, otherwise, x >= 50.
+ Path path = new Path();
+ path.moveTo(QUADRATIC_CTRL_PT1_X, QUADRATIC_CTRL_PT1_Y);
+ path.quadTo(QUADRATIC_CTRL_PT2_X, QUADRATIC_CTRL_PT2_Y,
+ QUADRATIC_CTRL_PT3_X, QUADRATIC_CTRL_PT3_Y);
+
+ final CountDownLatch endLatch = new CountDownLatch(1);
+ final PropertyValuesHolder pvh = PropertyValuesHolder.ofMultiInt("position", path);
+ final ValueAnimator anim = ValueAnimator.ofPropertyValuesHolder(pvh);
+ // Linear interpolator
+ anim.setInterpolator(null);
+ anim.setDuration(200);
+
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ endLatch.countDown();
+ }
+ });
+
+ anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ float lastFraction = 0;
+ int lastX = 0;
+ int lastY = 0;
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ int[] values = (int[]) animation.getAnimatedValue();
+ assertEquals(2, values.length);
+ int x = values[0];
+ int y = values[1];
+ float fraction = animation.getAnimatedFraction();
+ // Given that the curve is symmetric about the line (x = 50), x should be less than
+ // 50 for half of the animation duration.
+ if (fraction < 0.5) {
+ assertTrue(x < QUADRATIC_CTRL_PT2_X);
+ } else {
+ assertTrue(x >= QUADRATIC_CTRL_PT2_X);
+ }
+
+ if (lastFraction > 0.5) {
+ // x should be increasing, y should be decreasing
+ assertTrue(x >= lastX);
+ assertTrue(y <= lastY);
+ } else if (fraction <= 0.5) {
+ // when fraction <= 0.5, both x, y should be increasing
+ assertTrue(x >= lastX);
+ assertTrue(y >= lastY);
+ }
+ lastX = x;
+ lastY = y;
+ lastFraction = fraction;
+ }
+ });
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ anim.start();
+ }
+ });
+ assertTrue(endLatch.await(400, TimeUnit.MILLISECONDS));
+ }
+
+ public void testOfMultiInt_Array() throws Throwable {
+ // Test for PropertyValuesHolder.ofMultiFloat(String, int[][]);
+ final int[][] data = new int[10][];
+ for (int i = 0; i < data.length; i++) {
+ data[i] = new int[3];
+ data[i][0] = i;
+ data[i][1] = i * 2;
+ data[i][2] = 0;
+ }
+
+ final CountDownLatch endLatch = new CountDownLatch(1);
+ final PropertyValuesHolder pvh = PropertyValuesHolder.ofMultiInt("position", data);
+ final ValueAnimator anim = ValueAnimator.ofPropertyValuesHolder(pvh);
+ anim.setInterpolator(null);
+ anim.setDuration(60);
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ endLatch.countDown();
+ }
+ });
+
+ anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ float fraction = animation.getAnimatedFraction();
+ int[] values = (int[]) animation.getAnimatedValue();
+ assertEquals(3, values.length);
+
+ int expectedX = Math.round(fraction * (data.length - 1));
+ int expectedY = Math.round(fraction * (data.length - 1) * 2);
+
+ // Allow a delta of 1 for rounding errors.
+ assertEquals(expectedX, values[0], 1);
+ assertEquals(expectedY, values[1], 1);
+ assertEquals(0, values[2]);
+ }
+ });
+
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ anim.start();
+ }
+ });
+ assertTrue(endLatch.await(200, TimeUnit.MILLISECONDS));
+ }
+
+ public void testOfObject_Converter() throws Throwable {
+ // Test for PropertyValuesHolder.ofObject(String, TypeConverter<T, V>, Path)
+ // and for PropertyValuesHolder.ofObject(Property, TypeConverter<T, V>, Path)
+ // Create a path that contains two disconnected line segments. Check that the animated
+ // property x and property y always stay on the line segments.
+ Path path = new Path();
+ path.moveTo(LINE1_START, -LINE1_START);
+ path.lineTo(LINE1_END, -LINE1_END);
+ path.moveTo(LINE2_START, LINE2_START);
+ path.lineTo(LINE2_END, LINE2_END);
+ TypeConverter<PointF, Float> converter = new TypeConverter<PointF, Float>(
+ PointF.class, Float.class) {
+ @Override
+ public Float convert(PointF value) {
+ return (float) Math.sqrt(value.x * value.x + value.y * value.y);
+ }
+ };
+ final CountDownLatch endLatch = new CountDownLatch(3);
+
+ // Create three animators. The first one use a converter that converts the point to distance
+ // to origin. The second one does not have a type converter. The third animator uses a
+ // converter to changes sign of the x, y value of the input pointF.
+ FloatProperty property = new FloatProperty("distance") {
+ @Override
+ public void setValue(Object object, float value) {
+ }
+
+ @Override
+ public Object get(Object object) {
+ return null;
+ }
+ };
+ final PropertyValuesHolder pvh1 =
+ PropertyValuesHolder.ofObject(property, converter, path);
+ final ValueAnimator anim1 = ValueAnimator.ofPropertyValuesHolder(pvh1);
+ anim1.setDuration(100);
+ anim1.setInterpolator(null);
+ anim1.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ endLatch.countDown();
+ }
+ });
+
+ final PropertyValuesHolder pvh2 =
+ PropertyValuesHolder.ofObject("position", null, path);
+ final ValueAnimator anim2 = ValueAnimator.ofPropertyValuesHolder(pvh2);
+ anim2.setDuration(100);
+ anim2.setInterpolator(null);
+ anim2.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ endLatch.countDown();
+ }
+ });
+
+ TypeConverter<PointF, PointF> converter3 = new TypeConverter<PointF, PointF>(
+ PointF.class, PointF.class) {
+ PointF mValue = new PointF();
+ @Override
+ public PointF convert(PointF value) {
+ mValue.x = -value.x;
+ mValue.y = -value.y;
+ return mValue;
+ }
+ };
+ final PropertyValuesHolder pvh3 =
+ PropertyValuesHolder.ofObject("position", converter3, path);
+ final ValueAnimator anim3 = ValueAnimator.ofPropertyValuesHolder(pvh3);
+ anim3.setDuration(100);
+ anim3.setInterpolator(null);
+ anim3.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ endLatch.countDown();
+ }
+ });
+
+ anim3.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ // Set the initial value of the distance to the distance between the first point on
+ // the path to the origin.
+ float mLastDistance = (float) (32 * Math.sqrt(2));
+ float mLastFraction = 0f;
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ float fraction = anim1.getAnimatedFraction();
+ assertEquals(fraction, anim2.getAnimatedFraction());
+ assertEquals(fraction, anim3.getAnimatedFraction());
+ float distance = (Float) anim1.getAnimatedValue();
+ PointF position = (PointF) anim2.getAnimatedValue();
+ PointF positionReverseSign = (PointF) anim3.getAnimatedValue();
+ assertEquals(position.x, -positionReverseSign.x);
+ assertEquals(position.y, -positionReverseSign.y);
+
+ // Manually calculate the distance for the animator that doesn't have a
+ // TypeConverter, and expect the result to be the same as the animation value from
+ // the type converter.
+ float distanceFromPosition = (float) Math.sqrt(
+ position.x * position.x + position.y * position.y);
+ assertEquals(distance, distanceFromPosition, 0.0001f);
+
+ if (mLastFraction > 0.75) {
+ // In the 2nd line segment of the path, distance to origin should be increasing.
+ assertTrue(distance >= mLastDistance);
+ } else if (fraction < 0.75) {
+ assertTrue(distance <= mLastDistance);
+ }
+ mLastDistance = distance;
+ mLastFraction = fraction;
+ }
+ });
+
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ anim1.start();
+ anim2.start();
+ anim3.start();
+ }
+ });
+
+ // Wait until both of the animations finish
+ assertTrue(endLatch.await(200, TimeUnit.MILLISECONDS));
+ }
+
+ public void testSetConverter() throws Throwable {
+ // Test for PropertyValuesHolder.setConverter()
+ PropertyValuesHolder pvh = PropertyValuesHolder.ofObject("", null, 0f, 1f);
+ // Reverse the sign of the float in the converter, and use that value as the new type
+ // PointF's x value.
+ pvh.setConverter(new TypeConverter<Float, PointF>(Float.class, PointF.class) {
+ PointF mValue = new PointF();
+ @Override
+ public PointF convert(Float value) {
+ mValue.x = value * (-1f);
+ mValue.y = 0f;
+ return mValue;
+ }
+ });
+ final CountDownLatch endLatch = new CountDownLatch(2);
+
+ final ValueAnimator anim1 = ValueAnimator.ofPropertyValuesHolder(pvh);
+ anim1.setInterpolator(null);
+ anim1.setDuration(100);
+ anim1.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ endLatch.countDown();
+ }
+ });
+
+ final ValueAnimator anim2 = ValueAnimator.ofFloat(0f, 1f);
+ anim2.setInterpolator(null);
+ anim2.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ assertEquals(anim1.getAnimatedFraction(), anim2.getAnimatedFraction());
+ // Check that the pvh with type converter did reverse the sign of float, and set
+ // the x value of the PointF with it.
+ PointF value1 = (PointF) anim1.getAnimatedValue();
+ float value2 = (Float) anim2.getAnimatedValue();
+ assertEquals(value2, -value1.x);
+ assertEquals(0f, value1.y);
+ }
+ });
+ anim2.setDuration(100);
+ anim2.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ endLatch.countDown();
+ }
+ });
+
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ anim1.start();
+ anim2.start();
+ }
+ });
+
+ // Wait until both of the animations finish
+ assertTrue(endLatch.await(200, TimeUnit.MILLISECONDS));
}
public void testSetProperty() throws Throwable {
diff --git a/tests/tests/animation/src/android/animation/cts/ValueAnimatorTest.java b/tests/tests/animation/src/android/animation/cts/ValueAnimatorTest.java
index b778530..b43cc3a 100644
--- a/tests/tests/animation/src/android/animation/cts/ValueAnimatorTest.java
+++ b/tests/tests/animation/src/android/animation/cts/ValueAnimatorTest.java
@@ -19,6 +19,7 @@
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
+import android.graphics.Color;
import android.test.ActivityInstrumentationTestCase2;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.LinearInterpolator;
@@ -393,6 +394,57 @@
assertTrue(animatedValue <= end);
}
+ public void testOfArgb() throws Throwable {
+ int start = 0xffff0000;
+ int end = 0xff0000ff;
+ int[] values = {start, end};
+ int startRed = Color.red(start);
+ int startBlue = Color.blue(start);
+ int endRed = Color.red(end);
+ int endBlue = Color.blue(end);
+ final ValueAnimator valueAnimatorLocal = ValueAnimator.ofArgb(values);
+ valueAnimatorLocal.setDuration(mDuration);
+
+ final CountDownLatch latch = new CountDownLatch(1);
+ valueAnimatorLocal.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ if (animation.getAnimatedFraction() > .05f) {
+ latch.countDown();
+ }
+ }
+ });
+
+ this.runTestOnUiThread(new Runnable(){
+ public void run() {
+ valueAnimatorLocal.start();
+ }
+ });
+ boolean isRunning = valueAnimatorLocal.isRunning();
+ assertTrue(isRunning);
+
+ assertTrue(latch.await(500, TimeUnit.MILLISECONDS));
+
+ Integer animatedValue = (Integer) valueAnimatorLocal.getAnimatedValue();
+ int alpha = Color.alpha(animatedValue);
+ int red = Color.red(animatedValue);
+ int green = Color.green(animatedValue);
+ int blue = Color.blue(animatedValue);
+ assertTrue(red < startRed);
+ assertTrue(red > endRed);
+ assertTrue(blue > startBlue);
+ assertTrue(blue < endBlue);
+ assertEquals(255, alpha);
+ assertEquals(0, green);
+
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ valueAnimatorLocal.cancel();
+ }
+ });
+ }
+
public void testNoDelayOnSeekAnimation() throws Throwable {
ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
animator.setInterpolator(new LinearInterpolator());
@@ -418,6 +470,31 @@
});
}
+ public void testNotifiesAfterEnd() throws Throwable {
+ final ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ assertTrue(animation.isStarted());
+ assertTrue(animation.isRunning());
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ assertFalse(animation.isRunning());
+ assertFalse(animation.isStarted());
+ super.onAnimationEnd(animation);
+ }
+ });
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ animator.start();
+ animator.end();
+ }
+ });
+ }
+
private ValueAnimator getAnimator() {
Object object = mActivity.view.newBall;
String property = "y";
diff --git a/tests/tests/app.usage/Android.mk b/tests/tests/app.usage/Android.mk
index 4cc3606..0303dbd 100644
--- a/tests/tests/app.usage/Android.mk
+++ b/tests/tests/app.usage/Android.mk
@@ -26,7 +26,7 @@
LOCAL_JAVA_LIBRARIES := android.test.runner
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner android-support-test
LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/tests/app.usage/src/android/app/usage/cts/NetworkUsageStatsTest.java b/tests/tests/app.usage/src/android/app/usage/cts/NetworkUsageStatsTest.java
index bd96d76..2c3c2ad 100644
--- a/tests/tests/app.usage/src/android/app/usage/cts/NetworkUsageStatsTest.java
+++ b/tests/tests/app.usage/src/android/app/usage/cts/NetworkUsageStatsTest.java
@@ -26,6 +26,9 @@
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
import android.net.NetworkRequest;
+import android.net.TrafficStats;
+import android.os.Handler;
+import android.os.HandlerThread;
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.RemoteException;
@@ -53,6 +56,12 @@
private static final long MINUTE = 1000 * 60;
private static final int TIMEOUT_MILLIS = 15000;
+ private static final String CHECK_CONNECTIVITY_URL = "http://www.265.com/";
+ private static final String CHECK_CALLBACK_URL = CHECK_CONNECTIVITY_URL;
+
+ private static final int NETWORK_TAG = 0xf00d;
+ private static final long THRESHOLD_BYTES = 2 * 1024 * 1024; // 2 MB
+
private interface NetworkInterfaceToTest {
int getNetworkType();
int getTransportType();
@@ -119,7 +128,7 @@
private String mWriteSettingsMode;
private String mUsageStatsMode;
- private void exerciseRemoteHost(Network network) throws Exception {
+ private void exerciseRemoteHost(Network network, URL url) throws Exception {
NetworkInfo networkInfo = mCm.getNetworkInfo(network);
if (networkInfo == null) {
Log.w(LOG_TAG, "Network info is null");
@@ -131,8 +140,8 @@
String originalKeepAlive = System.getProperty("http.keepAlive");
System.setProperty("http.keepAlive", "false");
try {
- urlc = (HttpURLConnection) network.openConnection(new URL(
- "http://www.265.com/"));
+ TrafficStats.setThreadStatsTag(NETWORK_TAG);
+ urlc = (HttpURLConnection) network.openConnection(url);
urlc.setConnectTimeout(TIMEOUT_MILLIS);
urlc.setUseCaches(false);
urlc.connect();
@@ -147,6 +156,7 @@
} catch (Exception e) {
Log.i(LOG_TAG, "Badness during exercising remote server: " + e);
} finally {
+ TrafficStats.clearThreadStatsTag();
if (in != null) {
try {
in.close();
@@ -229,10 +239,12 @@
private class NetworkCallback extends ConnectivityManager.NetworkCallback {
private long mTolerance;
+ private URL mUrl;
public boolean success;
- NetworkCallback(long tolerance) {
+ NetworkCallback(long tolerance, URL url) {
mTolerance = tolerance;
+ mUrl = url;
success = false;
}
@@ -240,7 +252,7 @@
public void onAvailable(Network network) {
try {
mStartTime = System.currentTimeMillis() - mTolerance;
- exerciseRemoteHost(network);
+ exerciseRemoteHost(network, mUrl);
mEndTime = System.currentTimeMillis() + mTolerance;
success = true;
synchronized(NetworkUsageStatsTest.this) {
@@ -260,7 +272,7 @@
if (!hasFeature) {
return false;
}
- NetworkCallback callback = new NetworkCallback(tolerance);
+ NetworkCallback callback = new NetworkCallback(tolerance, new URL(CHECK_CONNECTIVITY_URL));
mCm.requestNetwork(new NetworkRequest.Builder()
.addTransportType(sNetworkInterfacesToTest[networkTypeIndex].getTransportType())
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
@@ -520,10 +532,99 @@
}
}
+ public void testTagDetails() throws Exception {
+ for (int i = 0; i < sNetworkInterfacesToTest.length; ++i) {
+ // Relatively large tolerance to accommodate for history bucket size.
+ if (!shouldTestThisNetworkType(i, MINUTE * 120)) {
+ continue;
+ }
+ setAppOpsMode(AppOpsManager.OPSTR_GET_USAGE_STATS, "allow");
+ NetworkStats result = null;
+ try {
+ result = mNsm.queryDetailsForUidTag(
+ sNetworkInterfacesToTest[i].getNetworkType(), getSubscriberId(i),
+ mStartTime, mEndTime, Process.myUid(), NETWORK_TAG);
+ assertTrue(result != null);
+ NetworkStats.Bucket bucket = new NetworkStats.Bucket();
+ long totalTxPackets = 0;
+ long totalRxPackets = 0;
+ long totalTxBytes = 0;
+ long totalRxBytes = 0;
+ while (result.hasNextBucket()) {
+ assertTrue(result.getNextBucket(bucket));
+ assertTimestamps(bucket);
+ assertEquals(bucket.getState(), NetworkStats.Bucket.STATE_ALL);
+ assertEquals(bucket.getUid(), Process.myUid());
+ if (bucket.getTag() == NETWORK_TAG) {
+ totalTxPackets += bucket.getTxPackets();
+ totalRxPackets += bucket.getRxPackets();
+ totalTxBytes += bucket.getTxBytes();
+ totalRxBytes += bucket.getRxBytes();
+ }
+ }
+ assertTrue("No Rx bytes tagged with 0x" + Integer.toHexString(NETWORK_TAG)
+ + " for uid " + Process.myUid(), totalRxBytes > 0);
+ assertTrue("No Rx packets tagged with " + Integer.toHexString(NETWORK_TAG)
+ + " for uid " + Process.myUid(), totalRxPackets > 0);
+ assertTrue("No Tx bytes tagged with 0x" + Integer.toHexString(NETWORK_TAG)
+ + " for uid " + Process.myUid(), totalTxBytes > 0);
+ assertTrue("No Tx packets tagged with 0x" + Integer.toHexString(NETWORK_TAG)
+ + " for uid " + Process.myUid(), totalTxPackets > 0);
+ } catch (SecurityException e) {
+ fail("testUidDetails fails with exception: " + e.toString());
+ } finally {
+ if (result != null) {
+ result.close();
+ }
+ }
+ setAppOpsMode(AppOpsManager.OPSTR_GET_USAGE_STATS, "deny");
+ try {
+ result = mNsm.queryDetailsForUidTag(
+ sNetworkInterfacesToTest[i].getNetworkType(), getSubscriberId(i),
+ mStartTime, mEndTime, Process.myUid(), NETWORK_TAG);
+ fail("negative testUidDetails fails: no exception thrown.");
+ } catch (SecurityException e) {
+ // expected outcome
+ }
+ }
+ }
+
+ public void testCallback() throws Exception {
+ for (int i = 0; i < sNetworkInterfacesToTest.length; ++i) {
+ // Relatively large tolerance to accommodate for history bucket size.
+ if (!shouldTestThisNetworkType(i, MINUTE/2)) {
+ continue;
+ }
+ setAppOpsMode(AppOpsManager.OPSTR_GET_USAGE_STATS, "allow");
+
+ TestUsageCallback usageCallback = new TestUsageCallback();
+ HandlerThread thread = new HandlerThread("callback-thread");
+ thread.start();
+ Handler handler = new Handler(thread.getLooper());
+ mNsm.registerUsageCallback(sNetworkInterfacesToTest[i].getNetworkType(),
+ getSubscriberId(i), THRESHOLD_BYTES, usageCallback, handler);
+
+ // TODO: Force traffic and check whether the callback is invoked.
+ // Right now the test only covers whether the callback can be registered, but not
+ // whether it is invoked upon data usage since we don't have a scalable way of
+ // storing files of >2MB in CTS.
+
+ mNsm.unregisterUsageCallback(usageCallback);
+ }
+ }
+
private void assertTimestamps(final NetworkStats.Bucket bucket) {
assertTrue("Start timestamp " + bucket.getStartTimeStamp() + " is less than " +
mStartTime, bucket.getStartTimeStamp() >= mStartTime);
assertTrue("End timestamp " + bucket.getEndTimeStamp() + " is greater than " +
mEndTime, bucket.getEndTimeStamp() <= mEndTime);
}
+
+ private static class TestUsageCallback extends NetworkStatsManager.UsageCallback {
+ @Override
+ public void onThresholdReached(int networkType, String subscriberId) {
+ Log.v(LOG_TAG, "Called onThresholdReached for networkType=" + networkType
+ + " subscriberId=" + subscriberId);
+ }
+ }
}
diff --git a/tests/tests/app.usage/src/android/app/usage/cts/UsageStatsTest.java b/tests/tests/app.usage/src/android/app/usage/cts/UsageStatsTest.java
index d3890df..d900292 100644
--- a/tests/tests/app.usage/src/android/app/usage/cts/UsageStatsTest.java
+++ b/tests/tests/app.usage/src/android/app/usage/cts/UsageStatsTest.java
@@ -30,6 +30,7 @@
import android.test.InstrumentationTestCase;
import java.io.FileInputStream;
+import java.io.IOException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
@@ -37,8 +38,6 @@
import android.util.SparseLongArray;
import junit.framework.AssertionFailedError;
-import libcore.io.IoUtils;
-import libcore.io.Streams;
import org.junit.Ignore;
/**
@@ -94,7 +93,8 @@
private static void assertLessThanOrEqual(long left, long right) {
if (left > right) {
- throw new AssertionFailedError("Expected " + left + " to be less than or equal to " + right);
+ throw new AssertionFailedError("Expected " + left + " to be less than or equal to "
+ + right);
}
}
@@ -103,16 +103,23 @@
getInstrumentation().getContext().getPackageName(), mode);
ParcelFileDescriptor pfd = getInstrumentation().getUiAutomation()
.executeShellCommand(command);
- try {
- Streams.readFully(new FileInputStream(pfd.getFileDescriptor()));
+ try (FileInputStream fis = new FileInputStream(pfd.getFileDescriptor())){
+ final byte[] buffer = new byte[4096];
+ while (fis.read(buffer) != -1) { }
} finally {
- IoUtils.closeQuietly(pfd.getFileDescriptor());
+ try {
+ pfd.close();
+ } catch (IOException e) {
+ // Ignore.
+ }
}
}
private void launchSubActivity(Class<? extends Activity> clazz) {
- final Instrumentation.ActivityResult result = new Instrumentation.ActivityResult(0, new Intent());
- final Instrumentation.ActivityMonitor monitor = new Instrumentation.ActivityMonitor(clazz.getName(), result, false);
+ final Instrumentation.ActivityResult result =
+ new Instrumentation.ActivityResult(0, new Intent());
+ final Instrumentation.ActivityMonitor monitor =
+ new Instrumentation.ActivityMonitor(clazz.getName(), result, false);
getInstrumentation().addMonitor(monitor);
launchActivity(mTargetPackage, clazz, null);
mStartedActivities.add(monitor.waitForActivity());
@@ -198,7 +205,8 @@
long endTime = System.currentTimeMillis();
long startTime = endTime - MINUTE;
- Map<String, UsageStats> statsMap = mUsageStatsManager.queryAndAggregateUsageStats(startTime, endTime);
+ Map<String, UsageStats> statsMap = mUsageStatsManager.queryAndAggregateUsageStats(startTime,
+ endTime);
assertFalse(statsMap.isEmpty());
assertTrue(statsMap.containsKey(mTargetPackage));
final UsageStats before = statsMap.get(mTargetPackage);
@@ -284,20 +292,23 @@
final int intervalType = intervalLengths.keyAt(i);
final long intervalDuration = intervalLengths.valueAt(i);
final long startTime = endTime - (2 * intervalDuration);
- final List<UsageStats> statsList = mUsageStatsManager.queryUsageStats(intervalType, startTime, endTime);
+ final List<UsageStats> statsList = mUsageStatsManager.queryUsageStats(intervalType,
+ startTime, endTime);
assertFalse(statsList.isEmpty());
boolean foundPackage = false;
for (UsageStats stats : statsList) {
// Verify that each period is a day long.
- assertLessThanOrEqual(stats.getLastTimeStamp() - stats.getFirstTimeStamp(), intervalDuration);
+ assertLessThanOrEqual(stats.getLastTimeStamp() - stats.getFirstTimeStamp(),
+ intervalDuration);
if (stats.getPackageName().equals(mTargetPackage) &&
stats.getLastTimeUsed() >= beforeTime - TIME_DIFF_THRESHOLD) {
foundPackage = true;
}
}
- assertTrue("Did not find package " + mTargetPackage + " in interval " + intervalType, foundPackage);
+ assertTrue("Did not find package " + mTargetPackage + " in interval " + intervalType,
+ foundPackage);
}
}
diff --git a/tests/tests/app/Android.mk b/tests/tests/app/Android.mk
index 08201a6..cf76aa0 100644
--- a/tests/tests/app/Android.mk
+++ b/tests/tests/app/Android.mk
@@ -26,7 +26,7 @@
LOCAL_JAVA_LIBRARIES := android.test.runner
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner android-support-test
LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/tests/appwidget/src/android/appwidget/cts/AppWidgetTest.java b/tests/tests/appwidget/src/android/appwidget/cts/AppWidgetTest.java
index 61b964d..313f009 100644
--- a/tests/tests/appwidget/src/android/appwidget/cts/AppWidgetTest.java
+++ b/tests/tests/appwidget/src/android/appwidget/cts/AppWidgetTest.java
@@ -90,10 +90,10 @@
}
private static final String GRANT_BIND_APP_WIDGET_PERMISSION_COMMAND =
- "appwidget grantbind --package android.cts.appwidget --user 0";
+ "appwidget grantbind --package android.appwidget.cts --user 0";
private static final String REVOKE_BIND_APP_WIDGET_PERMISSION_COMMAND =
- "appwidget revokebind --package android.cts.appwidget --user 0";
+ "appwidget revokebind --package android.appwidget.cts --user 0";
private boolean hasAppWidgets() {
diff --git a/tests/tests/car/Android.mk b/tests/tests/car/Android.mk
index 0138317..cebd099 100644
--- a/tests/tests/car/Android.mk
+++ b/tests/tests/car/Android.mk
@@ -24,7 +24,7 @@
# When built, explicitly put it in the data partition.
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner android-support-test
LOCAL_JAVA_LIBRARIES := android.car
diff --git a/tests/tests/car/src/android/car/cts/CarAppContextManagerTest.java b/tests/tests/car/src/android/car/cts/CarAppContextManagerTest.java
deleted file mode 100644
index dffdd3e..0000000
--- a/tests/tests/car/src/android/car/cts/CarAppContextManagerTest.java
+++ /dev/null
@@ -1,238 +0,0 @@
-/*
- * 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.car.cts;
-
-import android.car.Car;
-import android.car.CarAppContextManager;
-import android.util.Log;
-
-import java.util.concurrent.Semaphore;
-import java.util.concurrent.TimeUnit;
-
-public class CarAppContextManagerTest extends CarApiTestBase {
- private static final String TAG = CarAppContextManager.class.getSimpleName();
- private CarAppContextManager mManager;
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mManager = (CarAppContextManager) getCar().getCarManager(Car.APP_CONTEXT_SERVICE);
- assertNotNull(mManager);
- }
-
- public void testSetActiveNullListener() throws Exception {
- try {
- mManager.setActiveContexts(null, CarAppContextManager.APP_CONTEXT_NAVIGATION);
- fail();
- } catch (IllegalStateException e) {
- // Expected.
- }
- }
-
- public void testRegisterNull() throws Exception {
- try {
- mManager.registerContextListener(null, 0);
- fail();
- } catch (IllegalArgumentException e) {
- // Expected.
- }
- }
-
- public void testRegisterUnregister() throws Exception {
- ContextChangeListener listener = new ContextChangeListener();
- ContextChangeListener listener2 = new ContextChangeListener();
- mManager.registerContextListener(listener, 0);
- mManager.registerContextListener(listener2, 0);
- mManager.unregisterContextListener();
- // this one is no-op
- mManager.unregisterContextListener();
- }
-
- public void testContextChange() throws Exception {
- DefaultServiceConnectionListener connectionListener =
- new DefaultServiceConnectionListener();
- Car car2 = Car.createCar(getContext(), connectionListener, null);
- car2.connect();
- connectionListener.waitForConnection(DEFAULT_WAIT_TIMEOUT_MS);
- CarAppContextManager manager2 = (CarAppContextManager)
- car2.getCarManager(Car.APP_CONTEXT_SERVICE);
- assertNotNull(manager2);
-
- assertEquals(0, mManager.getActiveAppContexts());
- ContextChangeListener change = new ContextChangeListener();
- ContextChangeListener change2 = new ContextChangeListener();
- ContextOwnershipChangeListener owner = new ContextOwnershipChangeListener();
- ContextOwnershipChangeListener owner2 = new ContextOwnershipChangeListener();
- mManager.registerContextListener(change, CarAppContextManager.APP_CONTEXT_NAVIGATION |
- CarAppContextManager.APP_CONTEXT_VOICE_COMMAND);
- manager2.registerContextListener(change2, CarAppContextManager.APP_CONTEXT_NAVIGATION |
- CarAppContextManager.APP_CONTEXT_VOICE_COMMAND);
-
- mManager.setActiveContexts(owner, CarAppContextManager.APP_CONTEXT_NAVIGATION);
- int expectedContexts = CarAppContextManager.APP_CONTEXT_NAVIGATION;
- assertEquals(expectedContexts, mManager.getActiveAppContexts());
- assertEquals(expectedContexts, manager2.getActiveAppContexts());
- assertTrue(mManager.isOwningContext(expectedContexts));
- assertFalse(mManager.isOwningContext(CarAppContextManager.APP_CONTEXT_VOICE_COMMAND));
- assertFalse(mManager.isOwningContext(CarAppContextManager.APP_CONTEXT_NAVIGATION |
- CarAppContextManager.APP_CONTEXT_VOICE_COMMAND));
- assertFalse(manager2.isOwningContext(CarAppContextManager.APP_CONTEXT_NAVIGATION));
- assertFalse(manager2.isOwningContext(CarAppContextManager.APP_CONTEXT_VOICE_COMMAND));
- assertTrue(change2.waitForContextChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
- expectedContexts));
- // change should not get notification for its own change
- assertFalse(change.waitForContextChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS, 0));
-
- mManager.setActiveContexts(owner, CarAppContextManager.APP_CONTEXT_VOICE_COMMAND);
- expectedContexts = CarAppContextManager.APP_CONTEXT_NAVIGATION |
- CarAppContextManager.APP_CONTEXT_VOICE_COMMAND;
- assertTrue(mManager.isOwningContext(CarAppContextManager.APP_CONTEXT_NAVIGATION));
- assertTrue(mManager.isOwningContext(CarAppContextManager.APP_CONTEXT_VOICE_COMMAND));
- assertTrue(mManager.isOwningContext(CarAppContextManager.APP_CONTEXT_NAVIGATION |
- CarAppContextManager.APP_CONTEXT_VOICE_COMMAND));
- assertFalse(manager2.isOwningContext(CarAppContextManager.APP_CONTEXT_NAVIGATION));
- assertFalse(manager2.isOwningContext(CarAppContextManager.APP_CONTEXT_VOICE_COMMAND));
- assertEquals(expectedContexts, mManager.getActiveAppContexts());
- assertEquals(expectedContexts, manager2.getActiveAppContexts());
- assertTrue(change2.waitForContextChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
- expectedContexts));
- // change should not get notification for its own change
- assertFalse(change.waitForContextChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS, 0));
-
- // this should be no-op
- mManager.setActiveContexts(owner, CarAppContextManager.APP_CONTEXT_NAVIGATION);
- assertEquals(expectedContexts, mManager.getActiveAppContexts());
- assertEquals(expectedContexts, manager2.getActiveAppContexts());
- assertFalse(change2.waitForContextChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS, 0));
- assertFalse(change.waitForContextChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS, 0));
-
- manager2.setActiveContexts(owner2, CarAppContextManager.APP_CONTEXT_NAVIGATION);
- assertFalse(mManager.isOwningContext(CarAppContextManager.APP_CONTEXT_NAVIGATION));
- assertTrue(mManager.isOwningContext(CarAppContextManager.APP_CONTEXT_VOICE_COMMAND));
- assertTrue(manager2.isOwningContext(CarAppContextManager.APP_CONTEXT_NAVIGATION));
- assertFalse(manager2.isOwningContext(CarAppContextManager.APP_CONTEXT_VOICE_COMMAND));
- assertEquals(expectedContexts, mManager.getActiveAppContexts());
- assertEquals(expectedContexts, manager2.getActiveAppContexts());
- assertTrue(owner.waitForOwnershipLossAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
- CarAppContextManager.APP_CONTEXT_NAVIGATION));
-
- // no-op as it is not owning it
- mManager.resetActiveContexts(CarAppContextManager.APP_CONTEXT_NAVIGATION);
- assertFalse(mManager.isOwningContext(CarAppContextManager.APP_CONTEXT_NAVIGATION));
- assertTrue(mManager.isOwningContext(CarAppContextManager.APP_CONTEXT_VOICE_COMMAND));
- assertTrue(manager2.isOwningContext(CarAppContextManager.APP_CONTEXT_NAVIGATION));
- assertFalse(manager2.isOwningContext(CarAppContextManager.APP_CONTEXT_VOICE_COMMAND));
- assertEquals(expectedContexts, mManager.getActiveAppContexts());
- assertEquals(expectedContexts, manager2.getActiveAppContexts());
-
- mManager.resetActiveContexts(CarAppContextManager.APP_CONTEXT_VOICE_COMMAND);
- assertFalse(mManager.isOwningContext(CarAppContextManager.APP_CONTEXT_NAVIGATION));
- assertFalse(mManager.isOwningContext(CarAppContextManager.APP_CONTEXT_VOICE_COMMAND));
- assertTrue(manager2.isOwningContext(CarAppContextManager.APP_CONTEXT_NAVIGATION));
- assertFalse(manager2.isOwningContext(CarAppContextManager.APP_CONTEXT_VOICE_COMMAND));
- expectedContexts = CarAppContextManager.APP_CONTEXT_NAVIGATION;
- assertEquals(expectedContexts, mManager.getActiveAppContexts());
- assertEquals(expectedContexts, manager2.getActiveAppContexts());
- assertTrue(change2.waitForContextChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
- CarAppContextManager.APP_CONTEXT_NAVIGATION));
- assertFalse(change.waitForContextChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS, 0));
-
- manager2.resetActiveContexts(CarAppContextManager.APP_CONTEXT_NAVIGATION);
- assertFalse(mManager.isOwningContext(CarAppContextManager.APP_CONTEXT_NAVIGATION));
- assertFalse(mManager.isOwningContext(CarAppContextManager.APP_CONTEXT_VOICE_COMMAND));
- assertFalse(manager2.isOwningContext(CarAppContextManager.APP_CONTEXT_NAVIGATION));
- assertFalse(manager2.isOwningContext(CarAppContextManager.APP_CONTEXT_VOICE_COMMAND));
- expectedContexts = 0;
- assertEquals(expectedContexts, mManager.getActiveAppContexts());
- assertEquals(expectedContexts, manager2.getActiveAppContexts());
- assertTrue(change.waitForContextChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS, 0));
- mManager.unregisterContextListener();
- manager2.unregisterContextListener();
- }
-
- public void testFilter() throws Exception {
- DefaultServiceConnectionListener connectionListener =
- new DefaultServiceConnectionListener();
- Car car2 = Car.createCar(getContext(), connectionListener);
- car2.connect();
- connectionListener.waitForConnection(DEFAULT_WAIT_TIMEOUT_MS);
- CarAppContextManager manager2 = (CarAppContextManager)
- car2.getCarManager(Car.APP_CONTEXT_SERVICE);
- assertNotNull(manager2);
-
- assertEquals(0, mManager.getActiveAppContexts());
- ContextChangeListener change = new ContextChangeListener();
- ContextChangeListener listener = new ContextChangeListener();
- ContextOwnershipChangeListener owner = new ContextOwnershipChangeListener();
- mManager.registerContextListener(change, CarAppContextManager.APP_CONTEXT_NAVIGATION |
- CarAppContextManager.APP_CONTEXT_VOICE_COMMAND);
- manager2.registerContextListener(listener, CarAppContextManager.APP_CONTEXT_NAVIGATION);
- mManager.setActiveContexts(owner, CarAppContextManager.APP_CONTEXT_NAVIGATION);
- assertTrue(listener.waitForContextChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
- CarAppContextManager.APP_CONTEXT_NAVIGATION));
- mManager.setActiveContexts(owner, CarAppContextManager.APP_CONTEXT_VOICE_COMMAND);
- assertFalse(listener.waitForContextChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS, 0));
- mManager.resetActiveContexts(CarAppContextManager.APP_CONTEXT_VOICE_COMMAND);
- assertFalse(listener.waitForContextChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS, 0));
- mManager.resetActiveContexts(CarAppContextManager.APP_CONTEXT_NAVIGATION);
- assertTrue(listener.waitForContextChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS, 0));
- }
-
- private class ContextChangeListener implements CarAppContextManager.AppContextChangeListener {
- private int mLastChangeEvent;
- private final Semaphore mChangeWait = new Semaphore(0);
-
- public boolean waitForContextChangeAndAssert(long timeoutMs, int expectedContexts)
- throws Exception {
- if (!mChangeWait.tryAcquire(timeoutMs, TimeUnit.MILLISECONDS)) {
- return false;
- }
- assertEquals(expectedContexts, mLastChangeEvent);
- return true;
- }
-
- @Override
- public void onAppContextChange(int activeContexts) {
- Log.i(TAG, "onAppContextChange " + Integer.toHexString(activeContexts));
- assertMainThread();
- mLastChangeEvent = activeContexts;
- mChangeWait.release();
- }
- }
-
- private class ContextOwnershipChangeListener
- implements CarAppContextManager.AppContextOwnershipChangeListener {
- private int mLastLossEvent;
- private final Semaphore mLossEventWait = new Semaphore(0);
-
- public boolean waitForOwnershipLossAndAssert(long timeoutMs, int expectedContexts)
- throws Exception {
- if (!mLossEventWait.tryAcquire(timeoutMs, TimeUnit.MILLISECONDS)) {
- return false;
- }
- assertEquals(expectedContexts, mLastLossEvent);
- return true;
- }
-
- @Override
- public void onAppContextOwnershipLoss(int context) {
- Log.i(TAG, "onAppContextOwnershipLoss " + Integer.toHexString(context));
- assertMainThread();
- mLastLossEvent = context;
- mLossEventWait.release();
- }
- }
-}
diff --git a/tests/tests/car/src/android/car/cts/CarAppFocusManagerTest.java b/tests/tests/car/src/android/car/cts/CarAppFocusManagerTest.java
new file mode 100644
index 0000000..83fa276
--- /dev/null
+++ b/tests/tests/car/src/android/car/cts/CarAppFocusManagerTest.java
@@ -0,0 +1,331 @@
+/*
+ * 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.car.cts;
+
+import android.car.Car;
+import android.car.CarAppFocusManager;
+import android.util.Log;
+
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+import org.junit.Assert;
+
+public class CarAppFocusManagerTest extends CarApiTestBase {
+ private static final String TAG = CarAppFocusManagerTest.class.getSimpleName();
+ private CarAppFocusManager mManager;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mManager = (CarAppFocusManager) getCar().getCarManager(Car.APP_FOCUS_SERVICE);
+ assertNotNull(mManager);
+
+ // Request all application focuses and abandon them to ensure no active context is present
+ // when test starts.
+ FocusOwnershipChangeListerner owner = new FocusOwnershipChangeListerner();
+ mManager.requestAppFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
+ mManager.requestAppFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND);
+ mManager.abandonAppFocus(owner);
+ }
+
+ public void testSetActiveNullListener() throws Exception {
+ try {
+ mManager.requestAppFocus(null, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
+ fail();
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+ }
+
+ public void testRegisterNull() throws Exception {
+ try {
+ mManager.registerFocusListener(null, 0);
+ fail();
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+ }
+
+ public void testRegisterUnregister() throws Exception {
+ FocusChangeListerner listener = new FocusChangeListerner();
+ FocusChangeListerner listener2 = new FocusChangeListerner();
+ mManager.registerFocusListener(listener, 1);
+ mManager.registerFocusListener(listener2, 1);
+ mManager.unregisterFocusListener(listener);
+ mManager.unregisterFocusListener(listener2);
+ }
+
+ public void testFocusChange() throws Exception {
+ DefaultServiceConnectionListener connectionListener =
+ new DefaultServiceConnectionListener();
+ Car car2 = Car.createCar(getContext(), connectionListener, null);
+ car2.connect();
+ connectionListener.waitForConnection(DEFAULT_WAIT_TIMEOUT_MS);
+ CarAppFocusManager manager2 = (CarAppFocusManager)
+ car2.getCarManager(Car.APP_FOCUS_SERVICE);
+ assertNotNull(manager2);
+ final int[] emptyFocus = new int[0];
+
+ Assert.assertArrayEquals(emptyFocus, mManager.getActiveAppTypes());
+ FocusChangeListerner change = new FocusChangeListerner();
+ FocusChangeListerner change2 = new FocusChangeListerner();
+ FocusOwnershipChangeListerner owner = new FocusOwnershipChangeListerner();
+ FocusOwnershipChangeListerner owner2 = new FocusOwnershipChangeListerner();
+ mManager.registerFocusListener(change, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
+ mManager.registerFocusListener(change, CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND);
+ manager2.registerFocusListener(change2, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
+ manager2.registerFocusListener(change2, CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND);
+
+ mManager.requestAppFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
+ int[] expectedFocuses = new int[] {CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION};
+ Assert.assertArrayEquals(expectedFocuses, mManager.getActiveAppTypes());
+ Assert.assertArrayEquals(expectedFocuses, manager2.getActiveAppTypes());
+ assertTrue(mManager.isOwningFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION));
+ assertFalse(mManager.isOwningFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND));
+ assertFalse(manager2.isOwningFocus(owner2, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION));
+ assertFalse(manager2.isOwningFocus(owner2,
+ CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND));
+ assertTrue(change2.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION, true));
+ assertTrue(change.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION, true));
+
+ mManager.requestAppFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND);
+ expectedFocuses = new int[] {
+ CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION,
+ CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND };
+ assertTrue(mManager.isOwningFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION));
+ assertTrue(mManager.isOwningFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND));
+ assertFalse(manager2.isOwningFocus(owner2, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION));
+ assertFalse(manager2.isOwningFocus(owner2,
+ CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND));
+ Assert.assertArrayEquals(expectedFocuses, mManager.getActiveAppTypes());
+ Assert.assertArrayEquals(expectedFocuses, manager2.getActiveAppTypes());
+ assertTrue(change2.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND, true));
+ assertTrue(change.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND, true));
+
+ // this should be no-op
+ change.reset();
+ change2.reset();
+ mManager.requestAppFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
+ Assert.assertArrayEquals(expectedFocuses, mManager.getActiveAppTypes());
+ Assert.assertArrayEquals(expectedFocuses, manager2.getActiveAppTypes());
+ assertFalse(change2.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION, true));
+ assertFalse(change.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION, true));
+
+ manager2.requestAppFocus(owner2, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
+ assertFalse(mManager.isOwningFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION));
+ assertTrue(mManager.isOwningFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND));
+ assertTrue(manager2.isOwningFocus(owner2, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION));
+ assertFalse(manager2.isOwningFocus(owner2,
+ CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND));
+ Assert.assertArrayEquals(expectedFocuses, mManager.getActiveAppTypes());
+ Assert.assertArrayEquals(expectedFocuses, manager2.getActiveAppTypes());
+ assertTrue(owner.waitForOwnershipLossAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION));
+
+ // no-op as it is not owning it
+ change.reset();
+ change2.reset();
+ mManager.abandonAppFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
+ assertFalse(mManager.isOwningFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION));
+ assertTrue(mManager.isOwningFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND));
+ assertTrue(manager2.isOwningFocus(owner2, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION));
+ assertFalse(manager2.isOwningFocus(owner2,
+ CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND));
+ Assert.assertArrayEquals(expectedFocuses, mManager.getActiveAppTypes());
+ Assert.assertArrayEquals(expectedFocuses, manager2.getActiveAppTypes());
+
+ change.reset();
+ change2.reset();
+ mManager.abandonAppFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND);
+ assertFalse(mManager.isOwningFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION));
+ assertFalse(mManager.isOwningFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND));
+ assertTrue(manager2.isOwningFocus(owner2, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION));
+ assertFalse(manager2.isOwningFocus(owner2,
+ CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND));
+ expectedFocuses = new int[] {CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION};
+ Assert.assertArrayEquals(expectedFocuses, mManager.getActiveAppTypes());
+ Assert.assertArrayEquals(expectedFocuses, manager2.getActiveAppTypes());
+ assertTrue(change2.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND, false));
+ assertTrue(change.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND, false));
+
+ change.reset();
+ change2.reset();
+ manager2.abandonAppFocus(owner2, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
+ assertFalse(mManager.isOwningFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION));
+ assertFalse(mManager.isOwningFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND));
+ assertFalse(manager2.isOwningFocus(owner2, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION));
+ assertFalse(manager2.isOwningFocus(owner2,
+ CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND));
+ expectedFocuses = emptyFocus;
+ Assert.assertArrayEquals(expectedFocuses, mManager.getActiveAppTypes());
+ Assert.assertArrayEquals(expectedFocuses, manager2.getActiveAppTypes());
+ assertTrue(change.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION, false));
+ mManager.unregisterFocusListener(change);
+ manager2.unregisterFocusListener(change2);
+ }
+
+ public void testFilter() throws Exception {
+ DefaultServiceConnectionListener connectionListener =
+ new DefaultServiceConnectionListener();
+ Car car2 = Car.createCar(getContext(), connectionListener);
+ car2.connect();
+ connectionListener.waitForConnection(DEFAULT_WAIT_TIMEOUT_MS);
+ CarAppFocusManager manager2 = (CarAppFocusManager)
+ car2.getCarManager(Car.APP_FOCUS_SERVICE);
+ assertNotNull(manager2);
+
+ Assert.assertArrayEquals(new int[0], mManager.getActiveAppTypes());
+
+ FocusChangeListerner listener = new FocusChangeListerner();
+ FocusChangeListerner listener2 = new FocusChangeListerner();
+ FocusOwnershipChangeListerner owner = new FocusOwnershipChangeListerner();
+ mManager.registerFocusListener(listener, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
+ mManager.registerFocusListener(listener, CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND);
+ manager2.registerFocusListener(listener2, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
+
+ mManager.requestAppFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
+ assertTrue(listener.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION, true));
+ assertTrue(listener2.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION, true));
+
+ listener.reset();
+ listener2.reset();
+ mManager.requestAppFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND);
+ assertTrue(listener.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND, true));
+ assertFalse(listener2.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND, true));
+
+ listener.reset();
+ listener2.reset();
+ mManager.abandonAppFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND);
+ assertTrue(listener.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND, false));
+ assertFalse(listener2.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND, false));
+
+ listener.reset();
+ listener2.reset();
+ mManager.abandonAppFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
+ assertTrue(listener.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION, false));
+ assertTrue(listener2.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION, false));
+ }
+
+ public void testMultipleChangeListenersPerManager() throws Exception {
+ FocusChangeListerner listener = new FocusChangeListerner();
+ FocusChangeListerner listener2 = new FocusChangeListerner();
+ FocusOwnershipChangeListerner owner = new FocusOwnershipChangeListerner();
+ mManager.registerFocusListener(listener, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
+ mManager.registerFocusListener(listener, CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND);
+ mManager.registerFocusListener(listener2, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
+
+ mManager.requestAppFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
+ assertTrue(listener.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION, true));
+ assertTrue(listener2.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION, true));
+
+ listener.reset();
+ listener2.reset();
+ mManager.requestAppFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND);
+ assertTrue(listener.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND, true));
+ assertFalse(listener2.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND, true));
+
+ listener.reset();
+ listener2.reset();
+ mManager.abandonAppFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND);
+ assertTrue(listener.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND, false));
+ assertFalse(listener2.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND, false));
+
+ listener.reset();
+ listener2.reset();
+ mManager.abandonAppFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
+ assertTrue(listener.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION, false));
+ assertTrue(listener2.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION, false));
+ }
+
+ private class FocusChangeListerner implements CarAppFocusManager.AppFocusChangeListener {
+ private int mLastChangeAppType;
+ private boolean mLastChangeAppActive;
+ private final Semaphore mChangeWait = new Semaphore(0);
+
+ public boolean waitForFocusChangeAndAssert(long timeoutMs, int expectedAppType,
+ boolean expectedAppActive) throws Exception {
+ if (!mChangeWait.tryAcquire(timeoutMs, TimeUnit.MILLISECONDS)) {
+ return false;
+ }
+ assertEquals(expectedAppType, mLastChangeAppType);
+ assertEquals(expectedAppActive, mLastChangeAppActive);
+ return true;
+ }
+
+ public void reset() {
+ mLastChangeAppType = 0;
+ mLastChangeAppActive = false;
+ }
+
+ @Override
+ public void onAppFocusChange(int appType, boolean active) {
+ Log.i(TAG, "onAppFocusChange appType=" + appType + " active=" + active);
+ assertMainThread();
+ mLastChangeAppType = appType;
+ mLastChangeAppActive = active;
+ mChangeWait.release();
+ }
+ }
+
+ private class FocusOwnershipChangeListerner
+ implements CarAppFocusManager.AppFocusOwnershipChangeListener {
+ private int mLastLossEvent;
+ private final Semaphore mLossEventWait = new Semaphore(0);
+
+ public boolean waitForOwnershipLossAndAssert(long timeoutMs, int expectedAppType)
+ throws Exception {
+ if (!mLossEventWait.tryAcquire(timeoutMs, TimeUnit.MILLISECONDS)) {
+ return false;
+ }
+ assertEquals(expectedAppType, mLastLossEvent);
+ return true;
+ }
+
+ @Override
+ public void onAppFocusOwnershipLoss(int appType) {
+ Log.i(TAG, "onAppFocusOwnershipLoss " + appType);
+ assertMainThread();
+ mLastLossEvent = appType;
+ mLossEventWait.release();
+ }
+ }
+}
diff --git a/tests/tests/carrierapi/src/android/carrierapi/cts/CarrierApiTest.java b/tests/tests/carrierapi/src/android/carrierapi/cts/CarrierApiTest.java
index d7fb5b7..1a66442 100644
--- a/tests/tests/carrierapi/src/android/carrierapi/cts/CarrierApiTest.java
+++ b/tests/tests/carrierapi/src/android/carrierapi/cts/CarrierApiTest.java
@@ -108,4 +108,27 @@
}
}
+ public void testGetIccAuthentication() {
+ // EAP-SIM rand is 16 bytes.
+ String base64Challenge = "ECcTqwuo6OfY8ddFRboD9WM=";
+ String base64Challenge2 = "EMNxjsFrPCpm+KcgCmQGnwQ=";
+ if (!hasCellular) return;
+ try {
+ assertNull("getIccAuthentication should return null for empty data.",
+ mTelephonyManager.getIccAuthentication(TelephonyManager.APPTYPE_USIM,
+ TelephonyManager.AUTHTYPE_EAP_AKA, ""));
+ String response = mTelephonyManager.getIccAuthentication(TelephonyManager.APPTYPE_USIM,
+ TelephonyManager.AUTHTYPE_EAP_SIM, base64Challenge);
+ assertTrue("Response to EAP-SIM Challenge must not be Null.", response != null);
+ // response is base64 encoded. After decoding, the value should be:
+ // 1 length byte + SRES(4 bytes) + 1 length byte + Kc(8 bytes)
+ byte[] result = android.util.Base64.decode(response, android.util.Base64.DEFAULT);
+ assertTrue("Result length must be 14 bytes.", 14 == result.length);
+ String response2 = mTelephonyManager.getIccAuthentication(TelephonyManager.APPTYPE_USIM,
+ TelephonyManager.AUTHTYPE_EAP_SIM, base64Challenge2);
+ assertTrue("Two responses must be different.", !response.equals(response2));
+ } catch (SecurityException e) {
+ failMessage();
+ }
+ }
}
diff --git a/tests/tests/content/Android.mk b/tests/tests/content/Android.mk
index a22d539..73191c3 100644
--- a/tests/tests/content/Android.mk
+++ b/tests/tests/content/Android.mk
@@ -26,9 +26,16 @@
LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4 ctsdeviceutil ctstestrunner services.core
# Resource unit tests use a private locale and some densities
-LOCAL_AAPT_FLAGS = -c xx_YY -c cs -c small -c normal -c large -c xlarge \
+LOCAL_AAPT_FLAGS = -c small -c normal -c large -c xlarge \
-c 320dpi -c 240dpi -c 160dpi -c 32dpi \
- -c kok,kok_IN,kok_419,kok_419_VARIANT,kok_Knda_419,kok_Knda_419_VARIANT,kok_VARIANT,kok_Knda,tgl,tgl_PH
+ -c cs \
+ -c fil,fil_SA \
+ -c iw,iw_IL \
+ -c kok,kok_IN,kok_419,kok_419_VARIANT,kok_Knda_419,kok_Knda_419_VARIANT,kok_VARIANT,kok_Knda \
+ -c mk,mk_MK \
+ -c tgl,tgl_PH \
+ -c tlh \
+ -c xx_YY
LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/tests/content/res/values-b+fil+SA/configVarying.xml b/tests/tests/content/res/values-b+fil+SA/configVarying.xml
new file mode 100644
index 0000000..ba3c653
--- /dev/null
+++ b/tests/tests/content/res/values-b+fil+SA/configVarying.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 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.
+-->
+
+<resources>
+ <bag type="configVarying" name="bag">
+ <item name="testString">bag fil SA</item>
+ </bag>
+</resources>
diff --git a/tests/tests/content/res/values-b+fil/configVarying.xml b/tests/tests/content/res/values-b+fil/configVarying.xml
new file mode 100644
index 0000000..2fa8f9f
--- /dev/null
+++ b/tests/tests/content/res/values-b+fil/configVarying.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 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.
+-->
+
+<resources>
+ <item type="configVarying" name="simple">simple fil</item>
+</resources>
diff --git a/tests/tests/content/res/values-b+tlh/configVarying.xml b/tests/tests/content/res/values-b+tlh/configVarying.xml
new file mode 100644
index 0000000..48730f3
--- /dev/null
+++ b/tests/tests/content/res/values-b+tlh/configVarying.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 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.
+-->
+
+<resources>
+ <item type="configVarying" name="simple">simple tlh</item>
+ <bag type="configVarying" name="bag">
+ <item name="testString">bag tlh</item>
+ </bag>
+</resources>
diff --git a/tests/tests/content/res/values-iw-rIL/configVarying.xml b/tests/tests/content/res/values-iw-rIL/configVarying.xml
new file mode 100644
index 0000000..a42f026
--- /dev/null
+++ b/tests/tests/content/res/values-iw-rIL/configVarying.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 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.
+-->
+
+<resources>
+ <item type="configVarying" name="simple">simple iw IL</item>
+ <bag type="configVarying" name="bag">
+ <item name="testString">bag iw IL</item>
+ </bag>
+</resources>
diff --git a/tests/tests/content/res/values-iw/configVarying.xml b/tests/tests/content/res/values-iw/configVarying.xml
new file mode 100644
index 0000000..8eabd93
--- /dev/null
+++ b/tests/tests/content/res/values-iw/configVarying.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 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.
+-->
+
+<resources>
+ <item type="configVarying" name="simple">simple iw</item>
+ <bag type="configVarying" name="bag">
+ <item name="testString">bag iw</item>
+ </bag>
+</resources>
diff --git a/tests/tests/content/res/values-mk-rMK/configVarying.xml b/tests/tests/content/res/values-mk-rMK/configVarying.xml
new file mode 100644
index 0000000..14265ef
--- /dev/null
+++ b/tests/tests/content/res/values-mk-rMK/configVarying.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 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.
+-->
+
+<resources>
+ <item type="configVarying" name="simple">simple mk MK</item>
+ <bag type="configVarying" name="bag">
+ <item name="testString">bag mk MK</item>
+ </bag>
+</resources>
diff --git a/tests/tests/content/res/values-mk/configVarying.xml b/tests/tests/content/res/values-mk/configVarying.xml
new file mode 100644
index 0000000..edd1099
--- /dev/null
+++ b/tests/tests/content/res/values-mk/configVarying.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 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.
+-->
+
+<resources>
+ <item type="configVarying" name="simple">simple mk</item>
+ <bag type="configVarying" name="bag">
+ <item name="testString">bag mk</item>
+ </bag>
+</resources>
diff --git a/tests/tests/content/res/values/resources_test.xml b/tests/tests/content/res/values/resources_test.xml
index 91c2c4a..7838518 100644
--- a/tests/tests/content/res/values/resources_test.xml
+++ b/tests/tests/content/res/values/resources_test.xml
@@ -22,6 +22,10 @@
will be displayed in the app launcher and elsewhere. -->
<dimen name="app_icon_size">48px</dimen>
<dimen name="toast_y_offset">64dip</dimen>
+ <dimen name="pos_dimen_149">1.49px</dimen>
+ <dimen name="pos_dimen_151">1.51px</dimen>
+ <dimen name="neg_dimen_149">-1.49px</dimen>
+ <dimen name="neg_dimen_151">-1.51px</dimen>
<plurals name="plurals_test">
<item quantity="one">A dog</item>
<item quantity="other">Some dogs</item>
diff --git a/tests/tests/content/src/android/content/res/cts/AssetManagerTest.java b/tests/tests/content/src/android/content/res/cts/AssetManagerTest.java
index 4893ec1..1387be0 100644
--- a/tests/tests/content/src/android/content/res/cts/AssetManagerTest.java
+++ b/tests/tests/content/src/android/content/res/cts/AssetManagerTest.java
@@ -139,13 +139,16 @@
public void testGetNonSystemLocales() {
// This is the list of locales built into this test package. It is basically the locales
// specified in the Android.mk files (assuming they have corresponding resources), plus the
- // special case for Filipino.
+ // special cases for Filipino.
final String KNOWN_LOCALES[] = {
"cs",
"fil",
"fil-PH",
+ "fil-SA",
"fr",
"fr-FR",
+ "iw",
+ "iw-IL",
"kok",
"kok-419",
"kok-419-variant",
@@ -154,8 +157,11 @@
"kok-Knda-419",
"kok-Knda-419-variant",
"kok-variant",
+ "mk",
+ "mk-MK",
"tgl",
"tgl-PH",
+ "tlh",
"xx",
"xx-YY"
};
diff --git a/tests/tests/content/src/android/content/res/cts/ConfigTest.java b/tests/tests/content/src/android/content/res/cts/ConfigTest.java
index 2390146..912de38 100644
--- a/tests/tests/content/src/android/content/res/cts/ConfigTest.java
+++ b/tests/tests/content/src/android/content/res/cts/ConfigTest.java
@@ -1153,6 +1153,42 @@
}
@MediumTest
+ public void testNormalLocales() {
+ Resources res;
+ TotalConfig config = makeClassicConfig();
+ // Hebrew
+ config.setProperty(Properties.LANGUAGE, "iw");
+ res = config.getResources();
+ checkValue(res, R.configVarying.simple, "simple iw");
+ checkValue(res, R.configVarying.bag,
+ R.styleable.TestConfig, new String[]{"bag iw"});
+
+ // Hebrew for Israel
+ config.setProperty(Properties.LANGUAGE, "iw");
+ config.setProperty(Properties.COUNTRY, "IL");
+ res = config.getResources();
+ checkValue(res, R.configVarying.simple, "simple iw IL");
+ checkValue(res, R.configVarying.bag,
+ R.styleable.TestConfig, new String[]{"bag iw IL"});
+
+ config = makeClassicConfig();
+ // Macedonian
+ config.setProperty(Properties.LANGUAGE, "mk");
+ res = config.getResources();
+ checkValue(res, R.configVarying.simple, "simple mk");
+ checkValue(res, R.configVarying.bag,
+ R.styleable.TestConfig, new String[]{"bag mk"});
+
+ // Macedonian for Macedonia
+ config.setProperty(Properties.LANGUAGE, "mk");
+ config.setProperty(Properties.COUNTRY, "MK");
+ res = config.getResources();
+ checkValue(res, R.configVarying.simple, "simple mk MK");
+ checkValue(res, R.configVarying.bag,
+ R.styleable.TestConfig, new String[]{"bag mk MK"});
+ }
+
+ @MediumTest
public void testExtendedLocales() {
TotalConfig config = makeClassicConfig();
// BCP 47 Locale kok
@@ -1225,9 +1261,9 @@
config.setProperty(Properties.LANGUAGE, "fil");
config.setProperty(Properties.COUNTRY, "US");
Resources res = config.getResources();
- checkValue(res, R.configVarying.simple, "simple tl");
+ checkValue(res, R.configVarying.simple, "simple fil"); // We have this resource in 'fil'
checkValue(res, R.configVarying.bag,
- R.styleable.TestConfig, new String[] { "bag tl" });
+ R.styleable.TestConfig, new String[] { "bag tl" }); // But this comes from 'tl'
// Ensure that "fil-PH" is mapped to "tl-PH" correctly.
config = makeClassicConfig();
@@ -1238,6 +1274,24 @@
checkValue(res, R.configVarying.bag,
R.styleable.TestConfig, new String[] { "bag tl PH" });
+ // Ensure that "fil-SA" works with no "tl" version.
+ config = makeClassicConfig();
+ config.setProperty(Properties.LANGUAGE, "fil");
+ config.setProperty(Properties.COUNTRY, "SA");
+ res = config.getResources();
+ checkValue(res, R.configVarying.simple, "simple fil"); // This comes from 'fil'
+ checkValue(res, R.configVarying.bag,
+ R.styleable.TestConfig, new String[] { "bag fil SA" }); // And this from 'fil-SA'
+
+ // Ensure that "tlh" is not mistakenly treated as a "tl" variant.
+ config = makeClassicConfig();
+ config.setProperty(Properties.LANGUAGE, "tlh");
+ config.setProperty(Properties.COUNTRY, "US");
+ res = config.getResources();
+ checkValue(res, R.configVarying.simple, "simple tlh");
+ checkValue(res, R.configVarying.bag,
+ R.styleable.TestConfig, new String[] { "bag tlh" });
+
config = makeClassicConfig();
config.setProperty(Properties.LANGUAGE, "tgl");
res = config.getResources();
@@ -1276,8 +1330,9 @@
}
assertEquals(0, tlLocales.size());
- assertEquals(2, filLocales.size());
+ assertEquals(3, filLocales.size());
assertTrue(filLocales.contains("fil"));
assertTrue(filLocales.contains("fil-PH"));
+ assertTrue(filLocales.contains("fil-SA"));
}
}
diff --git a/tests/tests/content/src/android/content/res/cts/ConfigurationTest.java b/tests/tests/content/src/android/content/res/cts/ConfigurationTest.java
index dbc3e21..12e4f73 100644
--- a/tests/tests/content/src/android/content/res/cts/ConfigurationTest.java
+++ b/tests/tests/content/src/android/content/res/cts/ConfigurationTest.java
@@ -34,6 +34,7 @@
protected void setUp() throws Exception {
super.setUp();
mConfigDefault = new Configuration();
+ mConfigDefault.setToDefaults();
makeConfiguration();
}
@@ -312,11 +313,60 @@
}
public void testSetToDefaults() {
- final Configuration temp = new Configuration(mConfig);
- assertFalse(temp.equals(mConfigDefault));
- temp.setToDefaults();
- assertTrue(temp.equals(mConfigDefault));
- assertTrue(temp.getLocales().isEmpty());
+ final Configuration config = new Configuration(mConfig);
+ assertFalse(config.equals(mConfigDefault));
+
+ config.setToDefaults();
+ assertTrue(config.equals(mConfigDefault));
+
+ assertEquals(1.0f, config.fontScale);
+ assertEquals(0, config.mcc);
+ assertEquals(0, config.mnc);
+ assertTrue(config.getLocales().isEmpty());
+ assertEquals(null, config.locale);
+ assertFalse(config.userSetLocale);
+ assertEquals(Configuration.TOUCHSCREEN_UNDEFINED, config.touchscreen);
+ assertEquals(Configuration.KEYBOARD_UNDEFINED, config.keyboard);
+ assertEquals(Configuration.KEYBOARDHIDDEN_UNDEFINED, config.keyboardHidden);
+ assertEquals(Configuration.HARDKEYBOARDHIDDEN_UNDEFINED, config.hardKeyboardHidden);
+ assertEquals(Configuration.NAVIGATION_UNDEFINED, config.navigation);
+ assertEquals(Configuration.NAVIGATIONHIDDEN_UNDEFINED, config.navigationHidden);
+ assertEquals(Configuration.ORIENTATION_UNDEFINED, config.orientation);
+ assertEquals(Configuration.SCREENLAYOUT_UNDEFINED, config.screenLayout);
+ assertEquals(Configuration.UI_MODE_TYPE_UNDEFINED, config.uiMode);
+ assertEquals(Configuration.SCREEN_WIDTH_DP_UNDEFINED, config.screenWidthDp);
+ assertEquals(Configuration.SCREEN_WIDTH_DP_UNDEFINED, config.compatScreenWidthDp);
+ assertEquals(Configuration.SCREEN_HEIGHT_DP_UNDEFINED, config.screenHeightDp);
+ assertEquals(Configuration.SCREEN_HEIGHT_DP_UNDEFINED, config.compatScreenHeightDp);
+ assertEquals(Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED,
+ config.smallestScreenWidthDp);
+ assertEquals(Configuration.DENSITY_DPI_UNDEFINED, config.densityDpi);
+ }
+
+ public void testUnset() {
+ Configuration config = new Configuration();
+ assertEquals(0.0f, config.fontScale);
+ assertEquals(0, config.mcc);
+ assertEquals(0, config.mnc);
+ assertTrue(config.getLocales().isEmpty());
+ assertEquals(null, config.locale);
+ assertFalse(config.userSetLocale);
+ assertEquals(Configuration.TOUCHSCREEN_UNDEFINED, config.touchscreen);
+ assertEquals(Configuration.KEYBOARD_UNDEFINED, config.keyboard);
+ assertEquals(Configuration.KEYBOARDHIDDEN_UNDEFINED, config.keyboardHidden);
+ assertEquals(Configuration.HARDKEYBOARDHIDDEN_UNDEFINED, config.hardKeyboardHidden);
+ assertEquals(Configuration.NAVIGATION_UNDEFINED, config.navigation);
+ assertEquals(Configuration.NAVIGATIONHIDDEN_UNDEFINED, config.navigationHidden);
+ assertEquals(Configuration.ORIENTATION_UNDEFINED, config.orientation);
+ assertEquals(Configuration.SCREENLAYOUT_UNDEFINED, config.screenLayout);
+ assertEquals(Configuration.UI_MODE_TYPE_UNDEFINED, config.uiMode);
+ assertEquals(Configuration.SCREEN_WIDTH_DP_UNDEFINED, config.screenWidthDp);
+ assertEquals(Configuration.SCREEN_WIDTH_DP_UNDEFINED, config.compatScreenWidthDp);
+ assertEquals(Configuration.SCREEN_HEIGHT_DP_UNDEFINED, config.screenHeightDp);
+ assertEquals(Configuration.SCREEN_HEIGHT_DP_UNDEFINED, config.compatScreenHeightDp);
+ assertEquals(Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED,
+ config.smallestScreenWidthDp);
+ assertEquals(Configuration.DENSITY_DPI_UNDEFINED, config.densityDpi);
}
public void testToString() {
diff --git a/tests/tests/content/src/android/content/res/cts/ResourcesTest.java b/tests/tests/content/src/android/content/res/cts/ResourcesTest.java
index 0f40ebd..0c51099 100644
--- a/tests/tests/content/src/android/content/res/cts/ResourcesTest.java
+++ b/tests/tests/content/src/android/content/res/cts/ResourcesTest.java
@@ -288,6 +288,10 @@
// app_icon_size is 48px, as defined in cts/tests/res/values/resources_test.xml
final int size = mResources.getDimensionPixelSize(R.dimen.app_icon_size);
assertEquals(48, size);
+ assertEquals(1, mResources.getDimensionPixelSize(R.dimen.pos_dimen_149));
+ assertEquals(2, mResources.getDimensionPixelSize(R.dimen.pos_dimen_151));
+ assertEquals(-1, mResources.getDimensionPixelSize(R.dimen.neg_dimen_149));
+ assertEquals(-2, mResources.getDimensionPixelSize(R.dimen.neg_dimen_151));
}
public void testGetDrawable() {
diff --git a/tests/tests/graphics/Android.mk b/tests/tests/graphics/Android.mk
index 7e26e07..40beee6 100644
--- a/tests/tests/graphics/Android.mk
+++ b/tests/tests/graphics/Android.mk
@@ -22,7 +22,7 @@
LOCAL_JAVA_LIBRARIES := android.test.runner
-LOCAL_STATIC_JAVA_LIBRARIES := mockito-target ctsdeviceutil ctstestrunner
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-annotations android-support-test mockito-target ctsdeviceutil ctstestrunner
LOCAL_JNI_SHARED_LIBRARIES := libctsgraphics_jni
LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_group_clip_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_group_clip_golden.png
new file mode 100644
index 0000000..e74c181
--- /dev/null
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_group_clip_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable/avd_empty_animator.xml b/tests/tests/graphics/res/drawable/avd_empty_animator.xml
new file mode 100644
index 0000000..cf6dd9a
--- /dev/null
+++ b/tests/tests/graphics/res/drawable/avd_empty_animator.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<animated-vector xmlns:aapt="http://schemas.android.com/aapt"
+ xmlns:android="http://schemas.android.com/apk/res/android">
+ <aapt:attr name="android:drawable">
+ <vector
+ android:width="32dp"
+ android:viewportWidth="32"
+ android:height="32dp"
+ android:viewportHeight="32">
+ <group
+ android:name="btn_radio_to_off_mtrl_0"
+ android:translateX="16"
+ android:translateY="16">
+ <group
+ android:name="ring_outer">
+ <path
+ android:name="ring_outer_path"
+ android:fillColor="#FF000000"
+ android:pathData="M 0.0,-9.0 c 4.9705627482,0.0 9.0,4.0294372518 9.0,9.0 c 0.0,4.9705627482 -4.0294372518,9.0 -9.0,9.0 c -4.9705627482,0.0 -9.0,-4.0294372518 -9.0,-9.0 c 0.0,-4.9705627482 4.0294372518,-9.0 9.0,-9.0 Z"/>
+ </group>
+ </group>
+ </vector>
+ </aapt:attr>
+ <target android:name="ring_outer_path">
+ <aapt:attr name="android:animation">
+ <!-- Empty animator set -->
+ <set/>
+ </aapt:attr>
+ </target>
+</animated-vector>
diff --git a/tests/tests/graphics/res/drawable/vector_icon_group_clip.xml b/tests/tests/graphics/res/drawable/vector_icon_group_clip.xml
new file mode 100644
index 0000000..9574d7e
--- /dev/null
+++ b/tests/tests/graphics/res/drawable/vector_icon_group_clip.xml
@@ -0,0 +1,50 @@
+<!--
+ 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="48dp"
+ android:width="48dp"
+ android:viewportHeight="48"
+ android:viewportWidth="48">
+
+ <group>
+ <clip-path
+ android:name="clip1"
+ android:pathData="M 0, 0 l 48, 0 l 0, 30 l -48, 0 z"/>
+
+ <group>
+ <clip-path
+ android:name="clip2"
+ android:pathData="M 0, 18 l 48, 0 l 0, 30 l -48, 0 z"/>
+
+ <path
+ android:name="plus1"
+ android:pathData="M20 16h-4v8h-8v4h8v8h4v-8h8v-4h-8zm9-3.84v3.64l5-1v21.2h4v-26z"
+ android:fillColor="#ff00ff00"/>
+ </group>
+
+
+ <group android:name="backgroundGroup" >
+ <path
+ android:name="background1"
+ android:fillColor="#80000000"
+ android:pathData="M 0,0 l 24,0 l 0,24 l -24, 0 z" />
+ <path
+ android:name="background2"
+ android:fillColor="#80000000"
+ android:pathData="M 24,24 l 24,0 l 0, 24 l -24, 0 z" />
+ </group>
+ </group>
+</vector>
diff --git a/tests/tests/graphics/res/raw/a4_portrait_rgbb.pdf b/tests/tests/graphics/res/raw/a4_portrait_rgbb.pdf
new file mode 100644
index 0000000..ed373b7
--- /dev/null
+++ b/tests/tests/graphics/res/raw/a4_portrait_rgbb.pdf
Binary files differ
diff --git a/tests/tests/graphics/res/raw/a5_portrait_rgbb.pdf b/tests/tests/graphics/res/raw/a5_portrait_rgbb.pdf
new file mode 100644
index 0000000..44e5a7a
--- /dev/null
+++ b/tests/tests/graphics/res/raw/a5_portrait_rgbb.pdf
Binary files differ
diff --git a/tests/tests/graphics/res/raw/a5_portrait_rgbb_1_6_printscaling_default.pdf b/tests/tests/graphics/res/raw/a5_portrait_rgbb_1_6_printscaling_default.pdf
new file mode 100644
index 0000000..1f4ec0b
--- /dev/null
+++ b/tests/tests/graphics/res/raw/a5_portrait_rgbb_1_6_printscaling_default.pdf
Binary files differ
diff --git a/tests/tests/graphics/res/raw/a5_portrait_rgbb_1_6_printscaling_none.pdf b/tests/tests/graphics/res/raw/a5_portrait_rgbb_1_6_printscaling_none.pdf
new file mode 100644
index 0000000..4d7a0f4
--- /dev/null
+++ b/tests/tests/graphics/res/raw/a5_portrait_rgbb_1_6_printscaling_none.pdf
Binary files differ
diff --git a/tests/tests/graphics/res/raw/two_pages.pdf b/tests/tests/graphics/res/raw/two_pages.pdf
new file mode 100644
index 0000000..ee73ae6
--- /dev/null
+++ b/tests/tests/graphics/res/raw/two_pages.pdf
Binary files differ
diff --git a/tests/tests/graphics/src/android/graphics/cts/CameraTest.java b/tests/tests/graphics/src/android/graphics/cts/CameraTest.java
index a28d6ff..4fbe88b 100644
--- a/tests/tests/graphics/src/android/graphics/cts/CameraTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/CameraTest.java
@@ -18,8 +18,10 @@
import android.graphics.Camera;
import android.graphics.Canvas;
import android.graphics.Matrix;
+import android.support.test.filters.SmallTest;
import android.test.AndroidTestCase;
+@SmallTest
public class CameraTest extends AndroidTestCase {
private Camera mCamera;
@@ -127,6 +129,35 @@
assertEquals(1.0f, f[8]);
}
+ public void testRotate() {
+ Matrix m1 = new Matrix();
+ preCompare(m1);
+
+ mCamera.rotate(15.0f, 30.0f, 45.0f);
+ Matrix m2 = new Matrix();
+ mCamera.getMatrix(m2);
+ assertFalse(m1.equals(m2));
+
+ float[] f = new float[9];
+ m2.getValues(f);
+ assertEquals(0.6123724f, f[0]);
+ assertEquals(0.6123724f, f[1]);
+ assertEquals(0.0f, f[2]);
+ assertEquals(-0.5915063f, f[3]);
+ assertEquals(0.774519f, f[4]);
+ assertEquals(0.0f, f[5]);
+ assertEquals(0.0009106233f, f[6]);
+ assertEquals(0.00027516257f, f[7]);
+ assertEquals(1.0f, f[8]);
+ }
+
+ public void testLocationAccessors() {
+ mCamera.setLocation(10.0f, 20.0f, 30.0f);
+ assertEquals(10.0f, mCamera.getLocationX());
+ assertEquals(20.0f, mCamera.getLocationY());
+ assertEquals(30.0f, mCamera.getLocationZ());
+ }
+
public void testApplyToCanvas() {
Canvas c1 = new Canvas();
mCamera.applyToCanvas(c1);
diff --git a/tests/tests/graphics/src/android/graphics/cts/ColorTest.java b/tests/tests/graphics/src/android/graphics/cts/ColorTest.java
index 760e3f9..c180906 100644
--- a/tests/tests/graphics/src/android/graphics/cts/ColorTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/ColorTest.java
@@ -16,31 +16,43 @@
package android.graphics.cts;
import android.graphics.Color;
-import android.test.AndroidTestCase;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
-public class ColorTest extends AndroidTestCase {
+import static org.junit.Assert.*;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ColorTest {
+ @Test
public void testAlpha(){
assertEquals(0xff, Color.alpha(Color.RED));
assertEquals(0xff, Color.alpha(Color.YELLOW));
new Color();
}
+ @Test
public void testArgb(){
assertEquals(Color.RED, Color.argb(0xff, 0xff, 0x00, 0x00));
assertEquals(Color.YELLOW, Color.argb(0xff, 0xff, 0xff, 0x00));
}
+ @Test
public void testBlue(){
assertEquals(0x00, Color.blue(Color.RED));
assertEquals(0x00, Color.blue(Color.YELLOW));
}
+ @Test
public void testGreen(){
assertEquals(0x00, Color.green(Color.RED));
assertEquals(0xff, Color.green(Color.GREEN));
}
+ @Test
public void testHSVToColor1(){
//abnormal case: hsv length less than 3
try{
@@ -56,6 +68,7 @@
assertEquals(Color.RED, Color.HSVToColor(hsv));
}
+ @Test
public void testHSVToColor2(){
//abnormal case: hsv length less than 3
try{
@@ -71,6 +84,7 @@
assertEquals(Color.RED, Color.HSVToColor(0xff, hsv));
}
+ @Test
public void testParseColor(){
//abnormal case: colorString starts with '#' but length is neither 7 nor 9
try{
@@ -104,16 +118,19 @@
assertEquals(Color.MAGENTA, Color.parseColor("magenta"));
}
+ @Test
public void testRed(){
assertEquals(0xff, Color.red(Color.RED));
assertEquals(0xff, Color.red(Color.YELLOW));
}
+ @Test
public void testRgb(){
assertEquals(Color.RED, Color.rgb(0xff, 0x00, 0x00));
assertEquals(Color.YELLOW, Color.rgb(0xff, 0xff, 0x00));
}
+ @Test
public void testRGBToHSV(){
//abnormal case: hsv length less than 3
try{
@@ -128,4 +145,14 @@
Color.RGBToHSV(0xff, 0x00, 0x00, hsv);
assertEquals(Color.RED, Color.HSVToColor(hsv));
}
+
+ @Test
+ public void testLuminance() {
+ assertEquals(0, Color.luminance(Color.BLACK), 0);
+ float eps = 0.000001f;
+ assertEquals(0.0722, Color.luminance(Color.BLUE), eps);
+ assertEquals(0.2126, Color.luminance(Color.RED), eps);
+ assertEquals(0.7152, Color.luminance(Color.GREEN), eps);
+ assertEquals(1, Color.luminance(Color.WHITE), 0);
+ }
}
diff --git a/tests/tests/graphics/src/android/graphics/cts/MatrixTest.java b/tests/tests/graphics/src/android/graphics/cts/MatrixTest.java
index 29e4f5f..5b600f6 100644
--- a/tests/tests/graphics/src/android/graphics/cts/MatrixTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/MatrixTest.java
@@ -15,39 +15,68 @@
*/
package android.graphics.cts;
+import android.graphics.Camera;
import android.graphics.Matrix;
import android.graphics.RectF;
import android.graphics.Matrix.ScaleToFit;
-import android.test.AndroidTestCase;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
-public class MatrixTest extends AndroidTestCase {
+import static org.junit.Assert.*;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class MatrixTest {
private Matrix mMatrix;
private float[] mValues;
- @Override
- protected void setUp() throws Exception {
- super.setUp();
+ @Before
+ public void setUp() {
mMatrix = new Matrix();
mValues = new float[9];
}
- public void testConstractor() {
- new Matrix();
- new Matrix(mMatrix);
+ @Test
+ public void testConstructor() {
+ assertTrue(new Matrix().isIdentity());
+ assertTrue(new Matrix(mMatrix).isIdentity());
}
+ @Test
public void testIsIdentity() {
assertTrue(mMatrix.isIdentity());
mMatrix.setScale(0f, 0f);
assertFalse(mMatrix.isIdentity());
}
+ @Test
public void testRectStaysRect() {
assertTrue(mMatrix.rectStaysRect());
mMatrix.postRotate(80);
assertFalse(mMatrix.rectStaysRect());
}
+ @Test
+ public void testIsAffine() {
+ assertTrue(mMatrix.isAffine());
+
+ // translate/scale/rotateZ don't affect whether matrix is affine
+ mMatrix.postTranslate(50, 50);
+ mMatrix.postScale(20, 4);
+ mMatrix.postRotate(80);
+ assertTrue(mMatrix.isAffine());
+
+ Camera camera = new Camera();
+ camera.setLocation(0, 0, 100);
+ camera.rotateX(20);
+ camera.getMatrix(mMatrix);
+ assertFalse(mMatrix.isAffine());
+ }
+
+ @Test
public void testSet() {
mValues[0] = 1000;
mMatrix.getValues(mValues);
@@ -56,9 +85,10 @@
mValues = new float[9];
mValues[0] = 2000;
matrix.getValues(mValues);
- assertEquals(1f, mValues[0]);
+ assertEquals(1f, mValues[0], 0f);
}
+ @Test
public void testEquals() {
mMatrix.setScale(1f, 2f);
Matrix matrix = new Matrix();
@@ -68,6 +98,7 @@
assertTrue(mMatrix.equals(matrix));
}
+ @Test
public void testReset() {
mMatrix.setScale(1f, 2f, 3f, 4f);
String expect = "[1.0, 0.0, 0.0][0.0, 2.0, -4.0][0.0, 0.0, 1.0]";
@@ -77,6 +108,7 @@
assertEquals(expect, mMatrix.toShortString());
}
+ @Test
public void testSetScale() {
String expect = "[1.0, 0.0, 0.0][0.0, 1.0, 0.0][0.0, 0.0, 1.0]";
assertEquals(expect, mMatrix.toShortString());
@@ -85,6 +117,7 @@
assertEquals(expect, mMatrix.toShortString());
}
+ @Test
public void testSetScale2() {
String expect = "[1.0, 0.0, 0.0][0.0, 1.0, 0.0][0.0, 0.0, 1.0]";
assertEquals(expect, mMatrix.toShortString());
@@ -94,6 +127,7 @@
assertEquals(expect, mMatrix.toShortString());
}
+ @Test
public void testSetRotate() {
mMatrix.setRotate(1f);
String expect = "[0.9998477, -0.017452406, 0.0]"
@@ -101,6 +135,7 @@
assertEquals(expect, mMatrix.toShortString());
}
+ @Test
public void testSetRotate2() {
mMatrix.setRotate(1f, 2f, 3f);
String expect = "[0.9998477, -0.017452406, 0.0526618]"
@@ -108,30 +143,35 @@
assertEquals(expect, mMatrix.toShortString());
}
+ @Test
public void testSetSinCos() {
mMatrix.setSinCos(1f, 2f);
String expect = "[2.0, -1.0, 0.0][1.0, 2.0, 0.0][0.0, 0.0, 1.0]";
assertEquals(expect, mMatrix.toShortString());
}
+ @Test
public void testSetSinCos2() {
mMatrix.setSinCos(1f, 2f, 3f, 4f);
String expect = "[2.0, -1.0, 1.0][1.0, 2.0, -7.0][0.0, 0.0, 1.0]";
assertEquals(expect, mMatrix.toShortString());
}
+ @Test
public void testSetSkew() {
mMatrix.setSkew(1f, 2f);
String expect = "[1.0, 1.0, 0.0][2.0, 1.0, 0.0][0.0, 0.0, 1.0]";
assertEquals(expect, mMatrix.toShortString());
}
+ @Test
public void testSetSkew2() {
mMatrix.setSkew(1f, 2f, 3f, 4f);
String expect = "[1.0, 1.0, -4.0][2.0, 1.0, -6.0][0.0, 0.0, 1.0]";
assertEquals(expect, mMatrix.toShortString());
}
+ @Test
public void testSetConcat() {
Matrix a = new Matrix();
Matrix b = new Matrix();
@@ -151,24 +191,28 @@
assertEquals(expect, mMatrix.toShortString());
}
+ @Test
public void testPreTranslate() {
assertTrue(mMatrix.preTranslate(1f, 2f));
String expect = "[1.0, 0.0, 1.0][0.0, 1.0, 2.0][0.0, 0.0, 1.0]";
assertEquals(expect, mMatrix.toShortString());
}
+ @Test
public void testPreScale() {
assertTrue(mMatrix.preScale(1f, 2f));
String expect = "[1.0, 0.0, 0.0][0.0, 2.0, 0.0][0.0, 0.0, 1.0]";
assertEquals(expect, mMatrix.toShortString());
}
+ @Test
public void testPreScale2() {
assertTrue(mMatrix.preScale(1f, 2f, 3f, 4f));
String expect = "[1.0, 0.0, 0.0][0.0, 2.0, -4.0][0.0, 0.0, 1.0]";
assertEquals(expect, mMatrix.toShortString());
}
+ @Test
public void testPreRotate() {
assertTrue(mMatrix.preRotate(1f));
String expect = "[0.9998477, -0.017452406, 0.0][0.017452406, 0.9998477, "
@@ -176,6 +220,7 @@
assertEquals(expect, mMatrix.toShortString());
}
+ @Test
public void testPreRotate2() {
assertTrue(mMatrix.preRotate(1f, 2f, 3f));
float[] values = new float[9];
@@ -185,18 +230,21 @@
assertEquals(expect, mMatrix.toShortString());
}
+ @Test
public void testPreSkew() {
assertTrue(mMatrix.preSkew(1f, 2f));
String expect = "[1.0, 1.0, 0.0][2.0, 1.0, 0.0][0.0, 0.0, 1.0]";
assertEquals(expect, mMatrix.toShortString());
}
+ @Test
public void testPreSkew2() {
assertTrue(mMatrix.preSkew(1f, 2f, 3f, 4f));
String expect = "[1.0, 1.0, -4.0][2.0, 1.0, -6.0][0.0, 0.0, 1.0]";
assertEquals(expect, mMatrix.toShortString());
}
+ @Test
public void testPreConcat() {
float[] values = new float[9];
values[0] = 1000;
@@ -207,24 +255,28 @@
assertEquals(expect, mMatrix.toShortString());
}
+ @Test
public void testPostTranslate() {
assertTrue(mMatrix.postTranslate(1f, 2f));
String expect = "[1.0, 0.0, 1.0][0.0, 1.0, 2.0][0.0, 0.0, 1.0]";
assertEquals(expect, mMatrix.toShortString());
}
+ @Test
public void testPostScale() {
assertTrue(mMatrix.postScale(1f, 2f));
String expect = "[1.0, 0.0, 0.0][0.0, 2.0, 0.0][0.0, 0.0, 1.0]";
assertEquals(expect, mMatrix.toShortString());
}
+ @Test
public void testPostScale2() {
assertTrue(mMatrix.postScale(1f, 2f, 3f, 4f));
String expect = "[1.0, 0.0, 0.0][0.0, 2.0, -4.0][0.0, 0.0, 1.0]";
assertEquals(expect, mMatrix.toShortString());
}
+ @Test
public void testPostRotate() {
assertTrue(mMatrix.postRotate(1f));
String expect = "[0.9998477, -0.017452406, 0.0]" +
@@ -232,6 +284,7 @@
assertEquals(expect, mMatrix.toShortString());
}
+ @Test
public void testPostRotate2() {
assertTrue(mMatrix.postRotate(1f, 2f, 3f));
String expect = "[0.9998477, -0.017452406, 0.0526618]" +
@@ -239,18 +292,21 @@
assertEquals(expect, mMatrix.toShortString());
}
+ @Test
public void testPostSkew() {
assertTrue(mMatrix.postSkew(1f, 2f));
String expect = "[1.0, 1.0, 0.0][2.0, 1.0, 0.0][0.0, 0.0, 1.0]";
assertEquals(expect, mMatrix.toShortString());
}
+ @Test
public void testPostSkew2() {
assertTrue(mMatrix.postSkew(1f, 2f, 3f, 4f));
String expect = "[1.0, 1.0, -4.0][2.0, 1.0, -6.0][0.0, 0.0, 1.0]";
assertEquals(expect, mMatrix.toShortString());
}
+ @Test
public void testPostConcat() {
Matrix matrix = new Matrix();
float[] values = new float[9];
@@ -262,6 +318,7 @@
assertEquals(expect, mMatrix.toShortString());
}
+ @Test
public void testSetRectToRect() {
RectF r1 = new RectF();
r1.set(1f, 2f, 3f, 3f);
@@ -292,10 +349,11 @@
try {
mMatrix.setRectToRect(null, null, ScaleToFit.CENTER);
fail("should throw exception");
- } catch (Exception e) {
+ } catch (Exception ignored) {
}
}
+ @Test
public void testInvert() {
Matrix matrix = new Matrix();
float[] values = new float[9];
@@ -310,11 +368,12 @@
try {
result = mMatrix.invert(null);
fail("should throw exception");
- } catch (Exception e) {
+ } catch (Exception ignored) {
}
assertFalse(result);
}
+ @Test
public void testSetPolyToPoly() {
float[] src = new float[9];
src[0] = 100f;
@@ -327,138 +386,147 @@
try {
mMatrix.setPolyToPoly(src, 0, dst, 0, 5);
fail("should throw exception");
- } catch (Exception e) {
+ } catch (Exception ignored) {
}
}
+ @Test
public void testMapPoints() {
float[] value = new float[9];
value[0] = 100f;
mMatrix.mapPoints(value);
- assertEquals(value[0], 100f);
+ assertEquals(value[0], 100f, 0f);
try {
mMatrix.mapPoints(null);
fail("should throw exception");
- } catch (Exception e) {
+ } catch (Exception ignored) {
}
}
+ @Test
public void testMapPoints2() {
float[] dst = new float[9];
dst[0] = 100f;
float[] src = new float[9];
src[0] = 200f;
mMatrix.mapPoints(dst, src);
- assertEquals(dst[0], 200f);
+ assertEquals(dst[0], 200f, 0f);
try {
mMatrix.mapPoints(new float[8], new float[9]);
fail("should throw exception");
- } catch (Exception e) {
+ } catch (Exception ignored) {
}
}
+ @Test
public void testMapPoints3() {
float[] dst = new float[9];
dst[0] = 100f;
float[] src = new float[9];
src[0] = 200f;
mMatrix.mapPoints(dst, 0, src, 0, 9 >> 1);
- assertEquals(dst[0], 200f);
+ assertEquals(dst[0], 200f, 0f);
try {
mMatrix.mapPoints(null, 0, new float[9], 0, 1);
fail("should throw exception");
- } catch (Exception e) {
+ } catch (Exception ignored) {
}
}
+ @Test
public void testMapVectors() {
float[] values = new float[9];
- values = new float[9];
values[0] = 100f;
mMatrix.mapVectors(values);
- assertEquals(values[0], 100f);
+ assertEquals(values[0], 100f, 0f);
try {
mMatrix.mapVectors(null);
fail("should throw exception");
- } catch (Exception e) {
+ } catch (Exception ignored) {
}
}
+ @Test
public void testMapVectors2() {
float[] src = new float[9];
src[0] = 100f;
float[] dst = new float[9];
dst[0] = 200f;
mMatrix.mapVectors(dst, src);
- assertEquals(dst[0], 100f);
+ assertEquals(dst[0], 100f, 0f);
try {
mMatrix.mapVectors(new float[9], new float[8]);
fail("should throw exception");
- } catch (Exception e) {
+ } catch (Exception ignored) {
}
}
+ @Test
public void testMapVectors3() {
float[] src = new float[9];
src[0] = 100f;
float[] dst = new float[9];
dst[0] = 200f;
mMatrix.mapVectors(dst, 0, src, 0, 1);
- assertEquals(dst[0], 100f);
+ assertEquals(dst[0], 100f, 0f);
try {
mMatrix.mapVectors(dst, 0, src, 0, 10);
fail("should throw exception");
- } catch (Exception e) {
+ } catch (Exception ignored) {
}
}
+ @Test
public void testMapRadius() {
- assertEquals(mMatrix.mapRadius(100f), 100f);
+ assertEquals(mMatrix.mapRadius(100f), 100f, 0f);
assertEquals(mMatrix.mapRadius(Float.MAX_VALUE),
- Float.POSITIVE_INFINITY);
- assertEquals(mMatrix.mapRadius(Float.MIN_VALUE), 0f);
+ Float.POSITIVE_INFINITY, 0f);
+ assertEquals(mMatrix.mapRadius(Float.MIN_VALUE), 0f, 0f);
}
+ @Test
public void testMapRect() {
RectF r = new RectF();
r.set(1f, 2f, 3f, 4f);
assertTrue(mMatrix.mapRect(r));
- assertEquals(1f, r.left);
- assertEquals(2f, r.top);
- assertEquals(3f, r.right);
- assertEquals(4f, r.bottom);
+ assertEquals(1f, r.left, 0f);
+ assertEquals(2f, r.top, 0f);
+ assertEquals(3f, r.right, 0f);
+ assertEquals(4f, r.bottom, 0f);
try {
mMatrix.mapRect(null);
fail("should throw exception");
- } catch (Exception e) {
+ } catch (Exception ignored) {
}
}
+ @Test
public void testMapRect2() {
RectF dst = new RectF();
dst.set(100f, 100f, 200f, 200f);
RectF src = new RectF();
dst.set(10f, 10f, 20f, 20f);
assertTrue(mMatrix.mapRect(dst, src));
- assertEquals(0f, dst.left);
- assertEquals(0f, dst.top);
- assertEquals(0f, dst.right);
- assertEquals(0f, dst.bottom);
+ assertEquals(0f, dst.left, 0f);
+ assertEquals(0f, dst.top, 0f);
+ assertEquals(0f, dst.right, 0f);
+ assertEquals(0f, dst.bottom, 0f);
- assertEquals(0f, src.left);
- assertEquals(0f, src.top);
- assertEquals(0f, src.right);
- assertEquals(0f, src.bottom);
+ assertEquals(0f, src.left, 0f);
+ assertEquals(0f, src.top, 0f);
+ assertEquals(0f, src.right, 0f);
+ assertEquals(0f, src.bottom, 0f);
try {
mMatrix.mapRect(null, null);
fail("should throw exception");
- } catch (Exception e) {
+ } catch (Exception ignored) {
}
}
+ @Test
public void testAccessValues() {
Matrix matrix = new Matrix();
mMatrix.invert(matrix);
@@ -478,15 +546,18 @@
+ values[6] + ", " + values[7] + ", " + values[8] + "]";
}
+ @Test
public void testToString() {
assertNotNull(mMatrix.toString());
}
+ @Test
public void testToShortString() {
String expect = "[1.0, 0.0, 0.0][0.0, 1.0, 0.0][0.0, 0.0, 1.0]";
assertEquals(expect, mMatrix.toShortString());
}
+ @Test
public void testSetTranslate() {
mMatrix.setTranslate(2f, 3f);
String expect = "[1.0, 0.0, 2.0][0.0, 1.0, 3.0][0.0, 0.0, 1.0]";
diff --git a/tests/tests/graphics/src/android/graphics/cts/NinePatchTest.java b/tests/tests/graphics/src/android/graphics/cts/NinePatchTest.java
index 8b73380..8dcd32d 100644
--- a/tests/tests/graphics/src/android/graphics/cts/NinePatchTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/NinePatchTest.java
@@ -16,9 +16,6 @@
package android.graphics.cts;
-import android.graphics.cts.R;
-
-
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
@@ -29,8 +26,10 @@
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Region;
+import android.support.test.filters.SmallTest;
import android.test.AndroidTestCase;
+@SmallTest
public class NinePatchTest extends AndroidTestCase {
private static int ALPHA_OPAQUE = 0xFF;
@@ -57,13 +56,37 @@
}
public void testConstructor() {
- mNinePatch = null;
+ try {
+ mNinePatch = new NinePatch(mBitmap, new byte[2]);
+ fail("should throw exception");
+ } catch (Exception e) {
+ }
+
try {
mNinePatch = new NinePatch(mBitmap, new byte[2], NAME);
fail("should throw exception");
} catch (Exception e) {
}
+
+ mNinePatch = new NinePatch(mBitmap, mChunk);
+ assertEquals(mBitmap, mNinePatch.getBitmap());
+ assertEquals(null, mNinePatch.getName());
+
mNinePatch = new NinePatch(mBitmap, mChunk, NAME);
+ assertEquals(mBitmap, mNinePatch.getBitmap());
+ assertEquals(NAME, mNinePatch.getName());
+ }
+
+ public void testPaintAccessors() {
+ Paint p = new Paint();
+ mNinePatch = new NinePatch(mBitmap, mChunk, NAME);
+ assertNull(mNinePatch.getPaint());
+
+ mNinePatch.setPaint(p);
+ assertEquals(p, mNinePatch.getPaint());
+
+ mNinePatch.setPaint(null);
+ assertNull(mNinePatch.getPaint());
}
public void testIsNinePatchChunk() {
@@ -71,7 +94,6 @@
Bitmap bitmap = Bitmap.createBitmap(COLOR, 10, 10, Bitmap.Config.ARGB_4444);
assertFalse(NinePatch.isNinePatchChunk(bitmap.getNinePatchChunk()));
assertFalse(NinePatch.isNinePatchChunk(null));
-
}
public void testDraw() {
diff --git a/tests/tests/graphics/src/android/graphics/cts/OutlineTest.java b/tests/tests/graphics/src/android/graphics/cts/OutlineTest.java
index 4b01916..57a20ee 100644
--- a/tests/tests/graphics/src/android/graphics/cts/OutlineTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/OutlineTest.java
@@ -19,14 +19,15 @@
import android.graphics.Outline;
import android.graphics.Path;
import android.graphics.Rect;
+import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
import org.junit.Test;
+import org.junit.runner.RunWith;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.*;
@SmallTest
+@RunWith(AndroidJUnit4.class)
public class OutlineTest {
@Test
public void testDefaults() {
@@ -40,6 +41,20 @@
}
@Test
+ public void testCopyConstructor() {
+ Outline orig = new Outline();
+ orig.setAlpha(0.5f);
+ orig.setRect(10, 10, 20, 20);
+
+ Outline copy = new Outline(orig);
+ assertEquals(0.5f, copy.getAlpha(), 0.0f);
+
+ Rect copyRect = new Rect();
+ copy.getRect(copyRect);
+ assertEquals(new Rect(10, 10, 20, 20), copyRect);
+ }
+
+ @Test
public void testGetSetAlpha() {
Outline outline = new Outline();
@@ -60,6 +75,21 @@
}
@Test
+ public void testSet() {
+ Outline orig = new Outline();
+ orig.setAlpha(0.5f);
+ orig.setRect(10, 10, 20, 20);
+
+ Outline copy = new Outline();
+ copy.set(orig);
+ assertEquals(0.5f, copy.getAlpha(), 0.0f);
+
+ Rect copyRect = new Rect();
+ copy.getRect(copyRect);
+ assertEquals(new Rect(10, 10, 20, 20), copyRect);
+ }
+
+ @Test
public void testSetRect() {
Outline outline = new Outline();
Rect outRect = new Rect();
@@ -135,6 +165,11 @@
assertTrue(outline.getRect(outRect)); // is round rect, so works
assertTrue(outline.canClip()); // is round rect, so works
assertFalse(outline.isEmpty());
+
+ outline.setOval(new Rect(0, 0, 50, 50)); // same x & y radii, so round rect
+ assertTrue(outline.getRect(outRect)); // is round rect, so works
+ assertTrue(outline.canClip()); // is round rect, so works
+ assertFalse(outline.isEmpty());
}
@Test
diff --git a/tests/tests/graphics/src/android/graphics/cts/PaintTest.java b/tests/tests/graphics/src/android/graphics/cts/PaintTest.java
index bc04657..c4a9223 100644
--- a/tests/tests/graphics/src/android/graphics/cts/PaintTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/PaintTest.java
@@ -1178,7 +1178,7 @@
assertTrue(p.hasGlyph("\uD83D\uDD75")); // SLEUTH OR SPY is introduced in Unicode 7.0
assertTrue(p.hasGlyph("\uD83C\uDF2E")); // TACO is introduced in Unicode 8.0
assertTrue(p.hasGlyph("\uD83E\uDD33")); // SELFIE is introduced in Unicode 9.0
-
+
// We don't require gender-neutral emoji, but if present, results must be consistent
// whether VS is present or not.
assertTrue(p.hasGlyph("\uD83D\uDC69\u200D\u2695") == // WOMAN, ZWJ, STAFF OF AESCULAPIUS
@@ -1555,4 +1555,23 @@
}
}
}
+
+ public void testElegantText() {
+ final Paint p = new Paint();
+ p.setTextSize(10);
+ assertFalse(p.isElegantTextHeight());
+ final float nonElegantTop = p.getFontMetrics().top;
+ final float nonElegantBottom = p.getFontMetrics().bottom;
+
+ p.setElegantTextHeight(true);
+ assertTrue(p.isElegantTextHeight());
+ final float elegantTop = p.getFontMetrics().top;
+ final float elegantBottom = p.getFontMetrics().bottom;
+
+ assertTrue(elegantTop < nonElegantTop);
+ assertTrue(elegantBottom > nonElegantBottom);
+
+ p.setElegantTextHeight(false);
+ assertFalse(p.isElegantTextHeight());
+ }
}
diff --git a/tests/tests/graphics/src/android/graphics/cts/PathTest.java b/tests/tests/graphics/src/android/graphics/cts/PathTest.java
index 5958a84..e7e8abb 100644
--- a/tests/tests/graphics/src/android/graphics/cts/PathTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/PathTest.java
@@ -16,8 +16,6 @@
package android.graphics.cts;
-import junit.framework.TestCase;
-
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
@@ -25,8 +23,16 @@
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
+import org.junit.Test;
+import org.junit.runner.RunWith;
-public class PathTest extends TestCase {
+import static org.junit.Assert.*;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class PathTest {
// Test constants
private static final float LEFT = 10.0f;
@@ -36,6 +42,7 @@
private static final float XCOORD = 40.0f;
private static final float YCOORD = 40.0f;
+ @Test
public void testConstructor() {
// new the Path instance
new Path();
@@ -44,9 +51,8 @@
new Path(new Path());
}
+ @Test
public void testAddRect1() {
-
- // new the Path instance
Path path = new Path();
assertTrue(path.isEmpty());
RectF rect = new RectF(LEFT, TOP, RIGHT, BOTTOM);
@@ -54,34 +60,34 @@
assertFalse(path.isEmpty());
}
+ @Test
public void testAddRect2() {
-
- // new the Path instance
Path path = new Path();
assertTrue(path.isEmpty());
path.addRect(LEFT, TOP, RIGHT, BOTTOM, Path.Direction.CW);
assertFalse(path.isEmpty());
}
+ @Test
public void testMoveTo() {
- // new the Path instance
Path path = new Path();
path.moveTo(10.0f, 10.0f);
}
+ @Test
public void testSet() {
- // new the Path instance
Path path = new Path();
assertTrue(path.isEmpty());
Path path1 = new Path();
- setPath(path1);
+ addRectToPath(path1);
path.set(path1);
assertPathsAreEquivalent(path, path1);
}
+ @Test
public void testSetCleanOld() {
Path path = new Path();
- setPath(path);
+ addRectToPath(path);
path.addRect(new RectF(0, 0, 10, 10), Path.Direction.CW);
Path path1 = new Path();
path1.addRect(new RectF(10, 10, 20, 20), Path.Direction.CW);
@@ -89,15 +95,16 @@
assertPathsAreEquivalent(path, path1);
}
+ @Test
public void testSetEmptyPath() {
- // new the Path instance
Path path = new Path();
- setPath(path);
+ addRectToPath(path);
Path path1 = new Path();
path.set(path1);
assertPathsAreEquivalent(path, path1);
}
+ @Test
public void testAccessFillType() {
// set the expected value
Path.FillType expected1 = Path.FillType.EVEN_ODD;
@@ -118,52 +125,49 @@
assertEquals(expected4, path.getFillType());
}
+ @Test
public void testRQuadTo() {
- // new the Path instance
Path path = new Path();
assertTrue(path.isEmpty());
path.rQuadTo(5.0f, 5.0f, 10.0f, 10.0f);
assertFalse(path.isEmpty());
}
+ @Test
public void testTransform1() {
- // new the Path instance
Path path = new Path();
assertTrue(path.isEmpty());
Path dst = new Path();
- setPath(path);
+ addRectToPath(path);
path.transform(new Matrix(), dst);
assertFalse(dst.isEmpty());
}
- public void testTransform2() {
-
- }
-
+ @Test
public void testLineTo() {
- // new the Path instance
Path path = new Path();
assertTrue(path.isEmpty());
path.lineTo(XCOORD, YCOORD);
assertFalse(path.isEmpty());
}
+ @Test
public void testClose() {
- // new the Path instance
Path path = new Path();
assertTrue(path.isEmpty());
- setPath(path);
+ addRectToPath(path);
path.close();
}
+ @Test
public void testQuadTo() {
- // new the Path instance
Path path = new Path();
assertTrue(path.isEmpty());
path.quadTo(20.0f, 20.0f, 40.0f, 40.0f);
assertFalse(path.isEmpty());
}
+ @Test
public void testAddCircle() {
// new the Path instance
Path path = new Path();
@@ -172,8 +176,8 @@
assertFalse(path.isEmpty());
}
+ @Test
public void testArcTo1() {
- // new the Path instance
Path path = new Path();
assertTrue(path.isEmpty());
RectF oval = new RectF(LEFT, TOP, RIGHT, BOTTOM);
@@ -181,8 +185,8 @@
assertFalse(path.isEmpty());
}
+ @Test
public void testArcTo2() {
- // new the Path instance
Path path = new Path();
assertTrue(path.isEmpty());
RectF oval = new RectF(LEFT, TOP, RIGHT, BOTTOM);
@@ -190,81 +194,77 @@
assertFalse(path.isEmpty());
}
+ @Test
public void testComputeBounds1() {
-
RectF expected = new RectF(0.0f, 0.0f, 0.0f, 0.0f);
- // new the Path instance
Path path = new Path();
assertTrue(path.isEmpty());
RectF bounds = new RectF();
path.computeBounds(bounds, true);
- assertEquals(expected.width(), bounds.width());
- assertEquals(expected.height(), bounds.height());
+ assertEquals(expected.width(), bounds.width(), 0.0f);
+ assertEquals(expected.height(), bounds.height(), 0.0f);
path.computeBounds(bounds, false);
- assertEquals(expected.width(), bounds.width());
- assertEquals(expected.height(), bounds.height());
+ assertEquals(expected.width(), bounds.width(), 0.0f);
+ assertEquals(expected.height(), bounds.height(), 0.0f);
}
+ @Test
public void testComputeBounds2() {
-
RectF expected = new RectF(LEFT, TOP, RIGHT, BOTTOM);
- // new the Path instance
Path path = new Path();
assertTrue(path.isEmpty());
RectF bounds = new RectF(LEFT, TOP, RIGHT, BOTTOM);
path.addRect(bounds, Path.Direction.CW);
path.computeBounds(bounds, true);
- assertEquals(expected.width(), bounds.width());
- assertEquals(expected.height(), bounds.height());
+ assertEquals(expected.width(), bounds.width(), 0.0f);
+ assertEquals(expected.height(), bounds.height(), 0.0f);
path.computeBounds(bounds, false);
- assertEquals(expected.width(), bounds.width());
- assertEquals(expected.height(), bounds.height());
+ assertEquals(expected.width(), bounds.width(), 0.0f);
+ assertEquals(expected.height(), bounds.height(), 0.0f);
}
+ @Test
public void testRMoveTo() {
- // new the Path instance
}
+ @Test
public void testSetLastPoint() {
- // new the Path instance
Path path = new Path();
path.setLastPoint(10.0f, 10.0f);
}
+ @Test
public void testRLineTo() {
- // new the Path instance
Path path = new Path();
assertTrue(path.isEmpty());
path.rLineTo(10.0f, 10.0f);
assertFalse(path.isEmpty());
}
+ @Test
public void testIsEmpty() {
- // new the Path instance
Path path = new Path();
assertTrue(path.isEmpty());
- setPath(path);
+ addRectToPath(path);
assertFalse(path.isEmpty());
}
+ @Test
public void testRewind() {
-
- // set the expected value
Path.FillType expected = Path.FillType.EVEN_ODD;
- // new the Path instance
Path path = new Path();
assertTrue(path.isEmpty());
- setPath(path);
+ addRectToPath(path);
path.rewind();
path.setFillType(Path.FillType.EVEN_ODD);
assertTrue(path.isEmpty());
assertEquals(expected, path.getFillType());
}
+ @Test
public void testAddOval() {
- // new the Path instance
Path path = new Path();
assertTrue(path.isEmpty());
RectF oval = new RectF(LEFT, TOP, RIGHT, BOTTOM);
@@ -272,50 +272,46 @@
assertFalse(path.isEmpty());
}
+ @Test
public void testIsRect() {
-
- // new the Path instance
Path path = new Path();
assertTrue(path.isEmpty());
- setPath(path);
+ addRectToPath(path);
}
- public void testIncReserve() {
- }
-
+ @Test
public void testAddPath1() {
- // new the Path instance
Path path = new Path();
assertTrue(path.isEmpty());
Path src = new Path();
- setPath(src);
+ addRectToPath(src);
path.addPath(src, 10.0f, 10.0f);
assertFalse(path.isEmpty());
}
+ @Test
public void testAddPath2() {
- // new the Path instance
Path path = new Path();
assertTrue(path.isEmpty());
Path src = new Path();
- setPath(src);
+ addRectToPath(src);
path.addPath(src);
assertFalse(path.isEmpty());
}
+ @Test
public void testAddPath3() {
- // new the Path instance
Path path = new Path();
assertTrue(path.isEmpty());
Path src = new Path();
- setPath(src);
+ addRectToPath(src);
Matrix matrix = new Matrix();
path.addPath(src, matrix);
assertFalse(path.isEmpty());
}
+ @Test
public void testAddRoundRect1() {
- // new the Path instance
Path path = new Path();
assertTrue(path.isEmpty());
RectF rect = new RectF(LEFT, TOP, RIGHT, BOTTOM);
@@ -323,8 +319,8 @@
assertFalse(path.isEmpty());
}
+ @Test
public void testAddRoundRect2() {
- // new the Path instance
Path path = new Path();
assertTrue(path.isEmpty());
RectF rect = new RectF(LEFT, TOP, RIGHT, BOTTOM);
@@ -336,59 +332,84 @@
assertFalse(path.isEmpty());
}
- public void testIsInverseFillType() {
+ @Test
+ public void testIsConvex1() {
+ Path path = new Path();
+ path.addRect(0, 0, 100, 10, Path.Direction.CW);
+ assertTrue(path.isConvex());
- // new the Path instance
+ path.addRect(0, 0, 10, 100, Path.Direction.CW);
+ assertFalse(path.isConvex()); // path is concave
+ }
+
+ @Test
+ public void testIsConvex2() {
+ Path path = new Path();
+ path.addRect(0, 0, 40, 40, Path.Direction.CW);
+ assertTrue(path.isConvex());
+
+ path.addRect(10, 10, 30, 30, Path.Direction.CCW);
+ assertFalse(path.isConvex()); // path has hole, isn't convex
+ }
+
+ @Test
+ public void testIsConvex3() {
+ Path path = new Path();
+ path.addRect(0, 0, 10, 10, Path.Direction.CW);
+ assertTrue(path.isConvex());
+
+ path.addRect(0, 20, 10, 10, Path.Direction.CW);
+ assertFalse(path.isConvex()); // path isn't one convex shape
+ }
+
+ @Test
+ public void testIsInverseFillType() {
Path path = new Path();
assertFalse(path.isInverseFillType());
path.setFillType(Path.FillType.INVERSE_EVEN_ODD);
assertTrue(path.isInverseFillType());
}
+ @Test
public void testOffset1() {
- // new the Path instance
Path path = new Path();
assertTrue(path.isEmpty());
- setPath(path);
+ addRectToPath(path);
Path dst = new Path();
path.offset(XCOORD, YCOORD, dst);
assertFalse(dst.isEmpty());
}
- public void testOffset2() {
- // new the Path instance
- }
-
+ @Test
public void testCubicTo() {
- // new the Path instance
Path path = new Path();
assertTrue(path.isEmpty());
path.cubicTo(10.0f, 10.0f, 20.0f, 20.0f, 30.0f, 30.0f);
assertFalse(path.isEmpty());
}
+ @Test
public void testReset() {
- // new the Path instance
Path path = new Path();
assertTrue(path.isEmpty());
Path path1 = new Path();
- setPath(path1);
+ addRectToPath(path1);
path.set(path1);
assertFalse(path.isEmpty());
path.reset();
assertTrue(path.isEmpty());
}
+ @Test
public void testToggleInverseFillType() {
- // new the Path instance
Path path = new Path();
assertTrue(path.isEmpty());
path.toggleInverseFillType();
assertTrue(path.isInverseFillType());
}
+ @Test
public void testAddArc() {
- // new the Path instance
Path path = new Path();
assertTrue(path.isEmpty());
RectF oval = new RectF(LEFT, TOP, RIGHT, BOTTOM);
@@ -396,8 +417,8 @@
assertFalse(path.isEmpty());
}
+ @Test
public void testRCubicTo() {
- // new the Path instance
Path path = new Path();
assertTrue(path.isEmpty());
path.rCubicTo(10.0f, 10.0f, 11.0f, 11.0f, 12.0f, 12.0f);
@@ -441,7 +462,7 @@
return bitmap;
}
- private void setPath(Path path) {
+ private void addRectToPath(Path path) {
RectF rect = new RectF(LEFT, TOP, RIGHT, BOTTOM);
path.addRect(rect, Path.Direction.CW);
}
diff --git a/tests/tests/graphics/src/android/graphics/cts/PointFTest.java b/tests/tests/graphics/src/android/graphics/cts/PointFTest.java
index 77b72d4..0bef3e7 100644
--- a/tests/tests/graphics/src/android/graphics/cts/PointFTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/PointFTest.java
@@ -18,33 +18,21 @@
import android.graphics.Point;
import android.graphics.PointF;
+import android.os.Parcel;
+import android.support.test.filters.SmallTest;
import android.test.AndroidTestCase;
+@SmallTest
public class PointFTest extends AndroidTestCase {
private PointF mPointF;
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mPointF = null;
- }
-
public void testConstructor() {
-
- mPointF = null;
- // new the PointF instance
mPointF = new PointF();
-
- mPointF = null;
- // new the PointF instance
mPointF = new PointF(10.0f, 10.0f);
- mPointF = null;
Point point = new Point(10, 10);
- // new the PointF instance
mPointF = new PointF(point);
-
}
public void testNegate() {
@@ -91,4 +79,20 @@
assertEquals(11.1f, mPointF.y);
}
+ public void testDescribeContents() {
+ mPointF = new PointF(10.0f, 20.0f);
+ assertEquals(0, mPointF.describeContents());
+ }
+
+ public void testParceling() {
+ mPointF = new PointF(10.0f, 20.0f);
+ Parcel p = Parcel.obtain();
+ mPointF.writeToParcel(p, 0);
+ p.setDataPosition(0);
+
+ mPointF = new PointF();
+ mPointF.readFromParcel(p);
+ assertEquals(10.0f, mPointF.x);
+ assertEquals(20.0f, mPointF.y);
+ }
}
diff --git a/tests/tests/graphics/src/android/graphics/cts/PointTest.java b/tests/tests/graphics/src/android/graphics/cts/PointTest.java
index e4ede58..bee4034 100644
--- a/tests/tests/graphics/src/android/graphics/cts/PointTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/PointTest.java
@@ -17,30 +17,21 @@
package android.graphics.cts;
import android.graphics.Point;
+import android.os.Parcel;
+import android.support.test.filters.SmallTest;
import android.test.AndroidTestCase;
+@SmallTest
public class PointTest extends AndroidTestCase {
private Point mPoint;
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mPoint = null;
- }
-
public void testConstructor() {
-
- // new the Point instance
mPoint = new Point();
-
- // new the Point instance
mPoint = new Point(10, 10);
Point point = new Point(10, 10);
- // new the Point instance
mPoint = new Point(point);
-
}
public void testSet() {
@@ -89,4 +80,20 @@
assertEquals(-10, mPoint.y);
}
+ public void testDescribeContents() {
+ mPoint = new Point(10, 20);
+ assertEquals(0, mPoint.describeContents());
+ }
+
+ public void testParceling() {
+ mPoint = new Point(10, 20);
+ Parcel p = Parcel.obtain();
+ mPoint.writeToParcel(p, 0);
+ p.setDataPosition(0);
+
+ mPoint = new Point();
+ mPoint.readFromParcel(p);
+ assertEquals(10, mPoint.x);
+ assertEquals(20, mPoint.y);
+ }
}
diff --git a/tests/tests/graphics/src/android/graphics/cts/RectFTest.java b/tests/tests/graphics/src/android/graphics/cts/RectFTest.java
index f5788af..0240690 100644
--- a/tests/tests/graphics/src/android/graphics/cts/RectFTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/RectFTest.java
@@ -16,46 +16,28 @@
package android.graphics.cts;
-
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Parcel;
+import android.support.test.filters.SmallTest;
import android.test.AndroidTestCase;
+@SmallTest
public class RectFTest extends AndroidTestCase {
-
private RectF mRectF;
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mRectF = null;
- }
-
public void testConstructor() {
-
- mRectF = null;
- // new the RectF instance
mRectF = new RectF();
-
- mRectF = null;
- // new the RectF instance
mRectF = new RectF(1.5f, 2.5f, 20.3f, 40.9f);
- mRectF = null;
RectF rectF = new RectF(1.5f, 2.5f, 20.3f, 40.9f);
- // new the RectF instance
mRectF = new RectF(rectF);
- mRectF = null;
Rect rect = new Rect(0, 0, 10, 10);
- // new the RectF instance
mRectF = new RectF(rect);
-
}
public void testSort() {
-
mRectF = new RectF(10, 10, 5, 5);
assertEquals(10.0f, mRectF.left);
assertEquals(10.0f, mRectF.top);
@@ -67,22 +49,18 @@
assertEquals(5.0f, mRectF.top);
assertEquals(10.0f, mRectF.right);
assertEquals(10.0f, mRectF.bottom);
-
}
public void testSet1() {
-
mRectF = new RectF();
mRectF.set(1.0f, 2.0f, 3.0f, 4.0f);
assertEquals(1.0f, mRectF.left);
assertEquals(2.0f, mRectF.top);
assertEquals(3.0f, mRectF.right);
assertEquals(4.0f, mRectF.bottom);
-
}
public void testSet2() {
-
RectF rectF = new RectF(1.0f, 2.0f, 3.0f, 4.0f);
mRectF = new RectF();
mRectF.set(rectF);
@@ -90,11 +68,9 @@
assertEquals(2.0f, mRectF.top);
assertEquals(3.0f, mRectF.right);
assertEquals(4.0f, mRectF.bottom);
-
}
public void testSet3() {
-
Rect rect = new Rect(1, 2, 3, 4);
mRectF = new RectF();
mRectF.set(rect);
@@ -102,11 +78,9 @@
assertEquals(2.0f, mRectF.top);
assertEquals(3.0f, mRectF.right);
assertEquals(4.0f, mRectF.bottom);
-
}
public void testIntersects1() {
-
mRectF = new RectF(0, 0, 10, 10);
assertTrue(mRectF.intersects(5, 5, 15, 15));
assertEquals(0.0f, mRectF.left);
@@ -120,11 +94,9 @@
assertEquals(0.0f, mRectF.top);
assertEquals(10.0f, mRectF.right);
assertEquals(10.0f, mRectF.bottom);
-
}
public void testIntersects2() {
-
RectF rectF1;
RectF rectF2;
@@ -135,11 +107,9 @@
rectF1 = new RectF(0, 0, 10, 10);
rectF2 = new RectF(15, 15, 25, 25);
assertFalse(RectF.intersects(rectF1, rectF2));
-
}
public void testIntersect1() {
-
mRectF = new RectF(0, 0, 10, 10);
assertTrue(mRectF.intersect(5, 5, 15, 15));
assertEquals(5.0f, mRectF.left);
@@ -153,11 +123,9 @@
assertEquals(0.0f, mRectF.top);
assertEquals(10.0f, mRectF.right);
assertEquals(10.0f, mRectF.bottom);
-
}
public void testIntersect2() {
-
RectF rectF;
mRectF = new RectF(0, 0, 10, 10);
@@ -175,11 +143,9 @@
assertEquals(0.0f, mRectF.top);
assertEquals(10.0f, mRectF.right);
assertEquals(10.0f, mRectF.bottom);
-
}
public void testUnion1() {
-
// Both rect1 and rect2 are not empty.
// 1. left < right, top < bottom
// this.left < this.right, this.top < this.bottom
@@ -225,11 +191,9 @@
assertEquals(0.0f, mRectF.left);
assertEquals(1.0f, mRectF.right);
assertEquals(1.0f, mRectF.bottom);
-
}
public void testUnion2() {
-
RectF rectF;
// Both rect1 and rect2 are not empty.
@@ -282,11 +246,9 @@
assertEquals(0.0f, mRectF.left);
assertEquals(1.0f, mRectF.right);
assertEquals(1.0f, mRectF.bottom);
-
}
public void testUnion3() {
-
// rect1 is not empty (x > right, y > bottom).
mRectF = new RectF(0.0f, 0.0f, 1.0f, 1.0f);
mRectF.union(2.0f, 2.0f);
@@ -318,21 +280,17 @@
assertEquals(0.0f, mRectF.left);
assertEquals(2.0f, mRectF.right);
assertEquals(2.0f, mRectF.bottom);
-
}
public void testContains1() {
-
mRectF = new RectF(1.0f, 1.0f, 20.0f, 20.0f);
assertFalse(mRectF.contains(0.9f, 0.9f));
assertTrue(mRectF.contains(1.0f, 1.0f));
assertTrue(mRectF.contains(19.9f, 19.9f));
assertFalse(mRectF.contains(20.0f, 20.0f));
-
}
public void testContains2() {
-
mRectF = new RectF(1.0f, 1.0f, 20.0f, 20.0f);
assertTrue(mRectF.contains(1.0f, 1.0f, 20.0f, 20.0f));
assertTrue(mRectF.contains(2.0f, 2.0f, 19.0f, 19.0f));
@@ -341,7 +299,6 @@
}
public void testContains3() {
-
RectF rectF;
mRectF = new RectF(1.0f, 1.0f, 20.0f, 20.0f);
rectF = new RectF(1.0f, 1.0f, 20.0f, 20.0f);
@@ -352,22 +309,18 @@
assertFalse(mRectF.contains(rectF));
rectF = new RectF(0.0f, 0.0f, 19.0f, 19.0f);
assertFalse(mRectF.contains(rectF));
-
}
public void testOffset() {
-
mRectF = new RectF(5, 5, 10, 10);
mRectF.offset(1.0f, 1.0f);
assertEquals(6.0f, mRectF.left);
assertEquals(6.0f, mRectF.top);
assertEquals(11.0f, mRectF.right);
assertEquals(11.0f, mRectF.bottom);
-
}
public void testInset() {
-
mRectF = new RectF(5.0f, 5.0f, 10.0f, 10.0f);
mRectF.inset(1.0f, 1.0f);
assertEquals(6.0f, mRectF.left);
@@ -381,7 +334,6 @@
assertEquals(4.0f, mRectF.top);
assertEquals(11.0f, mRectF.right);
assertEquals(11.0f, mRectF.bottom);
-
}
public void testHeight() {
@@ -395,18 +347,15 @@
}
public void testOffsetTo() {
-
mRectF = new RectF(5, 5, 10, 10);
mRectF.offsetTo(1.0f, 1.0f);
assertEquals(1.0f, mRectF.left);
assertEquals(1.0f, mRectF.top);
assertEquals(6.0f, mRectF.right);
assertEquals(6.0f, mRectF.bottom);
-
}
public void testSetEmpty() {
-
// Before setEmpty()
mRectF = new RectF(1, 2, 3, 4);
assertEquals(1.0f, mRectF.left);
@@ -420,11 +369,9 @@
assertEquals(0.0f, mRectF.top);
assertEquals(0.0f, mRectF.right);
assertEquals(0.0f, mRectF.bottom);
-
}
public void testIsEmpty() {
-
mRectF = new RectF();
assertTrue(mRectF.isEmpty());
mRectF = new RectF(1.0f, 1.0f, 1.0f, 1.0f);
@@ -433,22 +380,18 @@
assertTrue(mRectF.isEmpty());
mRectF = new RectF(1.0f, 1.0f, 20.0f, 20.0f);
assertFalse(mRectF.isEmpty());
-
}
public void testCenterX() {
-
mRectF = new RectF(10.0f, 10.0f, 20.0f, 20.0f);
assertEquals(15.0f, mRectF.centerX());
mRectF = new RectF(10.5f, 10.0f, 20.0f, 20.0f);
assertEquals(15.25f, mRectF.centerX());
mRectF = new RectF(10.4f, 10.0f, 20.0f, 20.0f);
assertEquals(15.2f, mRectF.centerX());
-
}
public void testCenterY() {
-
mRectF = new RectF(10.0f, 10.0f, 20.0f, 20.0f);
assertEquals(15.0f, mRectF.centerY());
mRectF = new RectF(10.0f, 10.5f, 20.0f, 20.0f);
@@ -458,12 +401,16 @@
}
public void testToString() {
- mRectF = new RectF();
- assertNotNull(mRectF.toString());
+ mRectF = new RectF(10.0f, 20.0f, 30.0f, 40.0f);
+ assertEquals("RectF(10.0, 20.0, 30.0, 40.0)", mRectF.toString());
+ }
+
+ public void testToShortString() {
+ mRectF = new RectF(10.0f, 20.0f, 30.0f, 40.0f);
+ assertEquals("[10.0,20.0][30.0,40.0]", mRectF.toShortString());
}
public void testSetIntersect() {
-
RectF rectF1 = new RectF(0, 0, 10, 10);
RectF rectF2 = new RectF(5, 5, 15, 15);
@@ -482,11 +429,9 @@
assertEquals(5.0f, mRectF.top);
assertEquals(10.0f, mRectF.right);
assertEquals(10.0f, mRectF.bottom);
-
}
public void testRoundOut() {
-
Rect rect = new Rect();
mRectF = new RectF(1.2f, 1.8f, 5.2f, 5.8f);
mRectF.roundOut(rect);
@@ -494,11 +439,9 @@
assertEquals(1, rect.top);
assertEquals(6, rect.right);
assertEquals(6, rect.bottom);
-
}
public void testRound() {
-
Rect rect = new Rect();
mRectF = new RectF(1.2f, 1.8f, 5.2f, 5.8f);
mRectF.round(rect);
@@ -506,7 +449,6 @@
assertEquals(2, rect.top);
assertEquals(5, rect.right);
assertEquals(6, rect.bottom);
-
}
public void testWriteReadParcel() {
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/Animatable2_AnimationCallbackTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/Animatable2_AnimationCallbackTest.java
new file mode 100644
index 0000000..deb0e87
--- /dev/null
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/Animatable2_AnimationCallbackTest.java
@@ -0,0 +1,33 @@
+/*
+ * 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.graphics.drawable.cts;
+
+import junit.framework.TestCase;
+
+import android.graphics.drawable.Animatable2.AnimationCallback;
+import android.support.test.filters.SmallTest;
+
+@SmallTest
+public class Animatable2_AnimationCallbackTest extends TestCase {
+
+ public void testCallback() {
+ // These are no-op methods. Just make sure they don't crash.
+ AnimationCallback callback = new AnimationCallback() {};
+ callback.onAnimationStart(null);
+ callback.onAnimationEnd(null);
+ }
+}
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/AnimatedVectorDrawableTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/AnimatedVectorDrawableTest.java
index 5dabdfd..7ab3d35 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/AnimatedVectorDrawableTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/AnimatedVectorDrawableTest.java
@@ -50,11 +50,12 @@
private static final int IMAGE_WIDTH = 64;
private static final int IMAGE_HEIGHT = 64;
+ private static final long MAX_TIMEOUT_MS = 1000;
+ private static final long MAX_START_TIMEOUT_MS = 5000;
+ private static final int MS_TO_NS = 1000000;
private DrawableStubActivity mActivity;
private Resources mResources;
- private Bitmap mBitmap;
- private Canvas mCanvas;
private static final boolean DBG_DUMP_PNG = false;
private final int mResId = R.drawable.animation_vector_drawable_grouping_1;
private final int mLayoutId = R.layout.animated_vector_drawable_source;
@@ -69,9 +70,6 @@
protected void setUp() throws Exception {
super.setUp();
- mBitmap = Bitmap.createBitmap(IMAGE_WIDTH, IMAGE_HEIGHT, Bitmap.Config.ARGB_8888);
- mCanvas = new Canvas(mBitmap);
-
mActivity = getActivity();
mResources = mActivity.getResources();
}
@@ -123,23 +121,26 @@
if (type != XmlPullParser.START_TAG) {
throw new XmlPullParserException("No start tag found");
}
+ Bitmap bitmap = Bitmap.createBitmap(IMAGE_WIDTH, IMAGE_HEIGHT, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(bitmap);
AnimatedVectorDrawable drawable = new AnimatedVectorDrawable();
drawable.inflate(mResources, parser, attrs);
drawable.setBounds(0, 0, IMAGE_WIDTH, IMAGE_HEIGHT);
- mBitmap.eraseColor(0);
- drawable.draw(mCanvas);
- int sunColor = mBitmap.getPixel(IMAGE_WIDTH / 2, IMAGE_HEIGHT / 2);
- int earthColor = mBitmap.getPixel(IMAGE_WIDTH * 3 / 4 + 2, IMAGE_HEIGHT / 2);
+ bitmap.eraseColor(0);
+ drawable.draw(canvas);
+ int sunColor = bitmap.getPixel(IMAGE_WIDTH / 2, IMAGE_HEIGHT / 2);
+ int earthColor = bitmap.getPixel(IMAGE_WIDTH * 3 / 4 + 2, IMAGE_HEIGHT / 2);
assertTrue(sunColor == 0xFFFF8000);
assertTrue(earthColor == 0xFF5656EA);
if (DBG_DUMP_PNG) {
- saveVectorDrawableIntoPNG(mBitmap, mResId);
+ saveVectorDrawableIntoPNG(bitmap, mResId);
}
}
@MediumTest
- public void testSingleFrameAnimation() throws Exception {
+ public void testSingleFrameAnimation() {
+ final MyCallback callback = new MyCallback();
int resId = R.drawable.avd_single_frame;
final AnimatedVectorDrawable d1 =
(AnimatedVectorDrawable) mResources.getDrawable(resId);
@@ -147,25 +148,47 @@
mActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
+ Bitmap bitmap =
+ Bitmap.createBitmap(IMAGE_WIDTH, IMAGE_HEIGHT, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(bitmap);
+
mActivity.setContentView(mLayoutId);
ImageView imageView = (ImageView) mActivity.findViewById(mImageViewId);
imageView.setImageDrawable(d1);
d1.start();
d1.stop();
d1.setBounds(0, 0, IMAGE_WIDTH, IMAGE_HEIGHT);
- mBitmap.eraseColor(0);
- d1.draw(mCanvas);
+ bitmap.eraseColor(0);
+ d1.draw(canvas);
+ int endColor = bitmap.getPixel(IMAGE_WIDTH / 2, IMAGE_HEIGHT / 2);
+ assertEquals("Center point's color must be green", 0xFF00FF00, endColor);
}
});
+ }
- getInstrumentation().waitForIdleSync();
- int endColor = mBitmap.getPixel(IMAGE_WIDTH / 2, IMAGE_HEIGHT / 2);
-
- if (DBG_DUMP_PNG) {
- saveVectorDrawableIntoPNG(mBitmap, resId);
- }
-
- assertEquals("Center point's color must be green", 0xFF00FF00, endColor);
+ @SmallTest
+ public void testEmptyAnimatorSet() throws InterruptedException {
+ int resId = R.drawable.avd_empty_animator;
+ final MyCallback callback = new MyCallback();
+ final AnimatedVectorDrawable d1 =
+ (AnimatedVectorDrawable) mResources.getDrawable(resId);
+ d1.registerAnimationCallback(callback);
+ mActivity.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ mActivity.setContentView(mLayoutId);
+ ImageView imageView = (ImageView) mActivity.findViewById(mImageViewId);
+ imageView.setImageDrawable(d1);
+ d1.registerAnimationCallback(callback);
+ d1.start();
+ callback.notifyStarted();
+ }
+ });
+ callback.waitForStart();
+ waitForAVDStop(callback, MAX_TIMEOUT_MS);
+ // Check that the AVD with empty AnimatorSet has finished
+ callback.assertEnded(true);
+ callback.assertAVDRuntime(0, 64 * MS_TO_NS); // 4 frames
}
@SmallTest
@@ -252,33 +275,36 @@
@MediumTest
public void testReset() {
+ final MyCallback callback = new MyCallback();
final AnimatedVectorDrawable d1 = (AnimatedVectorDrawable) mResources.getDrawable(mResId);
// The AVD has a duration as 100ms.
mActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
+ d1.registerAnimationCallback(callback);
d1.start();
d1.reset();
}
});
- getInstrumentation().waitForIdleSync();
+ waitForAVDStop(callback, MAX_TIMEOUT_MS);
assertFalse(d1.isRunning());
}
@MediumTest
public void testStop() {
+ final MyCallback callback = new MyCallback();
final AnimatedVectorDrawable d1 = (AnimatedVectorDrawable) mResources.getDrawable(mResId);
// The AVD has a duration as 100ms.
mActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
+ d1.registerAnimationCallback(callback);
d1.start();
d1.stop();
-
}
});
- getInstrumentation().waitForIdleSync();
+ waitForAVDStop(callback, MAX_TIMEOUT_MS);
assertFalse(d1.isRunning());
}
@@ -294,12 +320,13 @@
AnimatedVectorDrawable d1 = (AnimatedVectorDrawable) imageView.getDrawable();
d1.registerAnimationCallback(callback);
d1.start();
+ callback.notifyStarted();
}
});
- getInstrumentation().waitForIdleSync();
- sleep(200);
- assertTrue(callback.mStart);
- assertTrue(callback.mEnd);
+ callback.waitForStart();
+ waitForAVDStop(callback, MAX_TIMEOUT_MS);
+ callback.assertStarted(true);
+ callback.assertEnded(true);
}
@MediumTest
@@ -317,12 +344,14 @@
d1.reset();
d1.registerAnimationCallback(callback);
d1.start();
+ callback.notifyStarted();
}
});
- getInstrumentation().waitForIdleSync();
- sleep(200);
- assertTrue(callback.mStart);
- assertTrue(callback.mEnd);
+ callback.waitForStart();
+ waitForAVDStop(callback, MAX_TIMEOUT_MS);
+
+ callback.assertStarted(true);
+ callback.assertEnded(true);
}
@MediumTest
@@ -337,17 +366,20 @@
AnimatedVectorDrawable d1 = (AnimatedVectorDrawable) imageView.getDrawable();
d1.start();
d1.registerAnimationCallback(callback);
+ callback.notifyStarted();
}
});
- getInstrumentation().waitForIdleSync();
- sleep(200);
+ callback.waitForStart();
+
+ waitForAVDStop(callback, MAX_TIMEOUT_MS);
// Whether or not the callback.start is true could vary when running on Render Thread.
- // Therefore, we don't make assertion here. The most useful flag is the callback.mEnd.
- assertTrue(callback.mEnd);
+ // Therefore, we don't make assertion here. The most useful flag is the callback.mEnded.
+ callback.assertEnded(true);
+ callback.assertAVDRuntime(0, 400 * MS_TO_NS); // 4 times of the duration of the AVD.
}
@MediumTest
- public void testRemoveCallback() {
+ public void testRemoveCallback() throws InterruptedException {
final MyCallback callback = new MyCallback();
// The AVD has a duration as 100ms.
mActivity.runOnUiThread(new Runnable() {
@@ -359,16 +391,18 @@
d1.registerAnimationCallback(callback);
assertTrue(d1.unregisterAnimationCallback(callback));
d1.start();
+ callback.notifyStarted();
}
});
- getInstrumentation().waitForIdleSync();
+ callback.waitForStart();
- assertFalse(callback.mStart);
- assertFalse(callback.mEnd);
+ waitForAVDStop(callback, MAX_TIMEOUT_MS);
+ callback.assertStarted(false);
+ callback.assertEnded(false);
}
@MediumTest
- public void testClearCallback() {
+ public void testClearCallback() throws InterruptedException {
final MyCallback callback = new MyCallback();
// The AVD has a duration as 100ms.
@@ -381,27 +415,91 @@
d1.registerAnimationCallback(callback);
d1.clearAnimationCallbacks();
d1.start();
+ callback.notifyStarted();
}
});
+ callback.waitForStart();
- getInstrumentation().waitForIdleSync();
-
- assertFalse(callback.mStart);
- assertFalse(callback.mEnd);
+ waitForAVDStop(callback, MAX_TIMEOUT_MS);
+ callback.assertStarted(false);
+ callback.assertEnded(false);
}
+ // The time out is expected when the listener is removed successfully.
+ // Such that we don't get the end event.
+ private void waitForAVDStop(MyCallback callback, long timeout) {
+ try {
+ callback.waitForEnd(timeout);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ fail("We should not see the AVD run this long time!");
+ }
+ }
+
+ // Now this class can not only listen to the events, but also synchronize the key events,
+ // logging the event timestamp, and centralize some assertions.
class MyCallback extends Animatable2.AnimationCallback {
- boolean mStart = false;
- boolean mEnd = false;
+ private boolean mStarted = false;
+ private boolean mEnded = false;
+
+ private long mStartNs = Long.MAX_VALUE;
+ private long mEndNs = Long.MIN_VALUE;
+
+ // Use this lock to make sure the onAnimationEnd() has been called.
+ // Each sub test should have its own lock.
+ private final Object mEndLock = new Object();
+
+ // Use this lock to make sure the test thread know when the AVD.start() has been called.
+ // Each sub test should have its own lock.
+ private final Object mStartLock = new Object();
+
+ public void waitForEnd(long timeoutMs) throws InterruptedException {
+ synchronized (mEndLock) {
+ mEndLock.wait(timeoutMs);
+ }
+ }
+
+ public void waitForStart() throws InterruptedException {
+ synchronized(mStartLock) {
+ mStartLock.wait(MAX_START_TIMEOUT_MS);
+ }
+ }
+
+ public void notifyStarted() {
+ mStartNs = System.nanoTime();
+ synchronized(mStartLock) {
+ mStartLock.notify();
+ }
+ }
@Override
public void onAnimationStart(Drawable drawable) {
- mStart = true;
+ mStarted = true;
}
@Override
public void onAnimationEnd(Drawable drawable) {
- mEnd = true;
+ mEndNs = System.nanoTime();
+ mEnded = true;
+ synchronized (mEndLock) {
+ mEndLock.notify();
+ }
+ }
+
+ public void assertStarted(boolean started) {
+ assertEquals(started, mStarted);
+ }
+
+ public void assertEnded(boolean ended) {
+ assertEquals(ended, mEnded);
+ }
+
+ public void assertAVDRuntime(long min, long max) {
+ assertTrue(mStartNs != Long.MAX_VALUE);
+ assertTrue(mEndNs != Long.MIN_VALUE);
+ long durationNs = mEndNs - mStartNs;
+ assertTrue("current duration " + durationNs + " should be within " +
+ min + "," + max, durationNs <= max && durationNs >= min);
}
}
}
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/DrawableContainerStateTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/DrawableContainerStateTest.java
index 8cbecab..726466c 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/DrawableContainerStateTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/DrawableContainerStateTest.java
@@ -16,6 +16,7 @@
package android.graphics.drawable.cts;
+import junit.framework.TestCase;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
@@ -23,12 +24,12 @@
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.DrawableContainer;
-import android.graphics.drawable.LevelListDrawable;
import android.graphics.drawable.DrawableContainer.DrawableContainerState;
+import android.graphics.drawable.LevelListDrawable;
+import android.support.test.filters.SmallTest;
-import junit.framework.TestCase;
-
-public class DrawableContainerStateTest extends TestCase{
+@SmallTest
+public class DrawableContainerStateTest extends TestCase {
private DrawableContainerState mDrawableContainerState;
private DrawableContainer mDrawableContainer;
@@ -36,6 +37,7 @@
@Override
protected void setUp() throws Exception {
super.setUp();
+
// DrawableContainerState has no public constructor. Obtain an instance through
// LevelListDrawable.getConstants(). This is fine for testing the final methods of
// DrawableContainerState.
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/DrawableTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/DrawableTest.java
index bc452f3..81d9386 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/DrawableTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/DrawableTest.java
@@ -16,14 +16,30 @@
package android.graphics.drawable.cts;
-import android.content.res.Resources.Theme;
-import android.graphics.drawable.Drawable.Callback;
-import android.view.View;
-import android.graphics.cts.R;
-
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
+import android.content.ContentResolver;
+import android.content.res.Resources;
+import android.content.res.Resources.Theme;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.PixelFormat;
+import android.graphics.PorterDuff;
+import android.graphics.Rect;
+import android.graphics.cts.R;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Drawable.Callback;
+import android.net.Uri;
+import android.support.test.filters.SmallTest;
+import android.test.AndroidTestCase;
+import android.util.AttributeSet;
+import android.util.StateSet;
+import android.util.TypedValue;
+import android.util.Xml;
+import android.view.View;
+
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
@@ -32,24 +48,11 @@
import java.io.InputStream;
import java.io.OutputStream;
-import android.content.ContentResolver;
-import android.content.res.Resources;
-import android.graphics.BitmapFactory;
-import android.graphics.Canvas;
-import android.graphics.ColorFilter;
-import android.graphics.PixelFormat;
-import android.graphics.PorterDuff;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.net.Uri;
-import android.test.AndroidTestCase;
-import android.util.AttributeSet;
-import android.util.StateSet;
-import android.util.TypedValue;
-import android.util.Xml;
-
+import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+@SmallTest
public class DrawableTest extends AndroidTestCase {
Resources mResources;
@@ -546,13 +549,22 @@
}
public void testScheduleSelf() {
- MockDrawable mockDrawable = new MockDrawable();
- MockCallback mockCallback = new MockCallback();
- mockDrawable.setCallback(mockCallback);
+ Drawable mockDrawable = new MockDrawable();
mockDrawable.scheduleSelf(null, 1000L);
- assertEquals(mockDrawable, mockCallback.getScheduleDrawable());
- assertNull(mockCallback.getRunnable());
- assertEquals(1000L, mockCallback.getWhen());
+
+ Runnable runnable = mock(Runnable.class);
+ mockDrawable.scheduleSelf(runnable, 1000L);
+
+ Callback mockCallback = mock(Callback.class);
+ mockDrawable.setCallback(mockCallback);
+ mockDrawable.scheduleSelf(runnable, 1000L);
+ verify(mockCallback).scheduleDrawable(eq(mockDrawable), eq(runnable), eq(1000L));
+
+ mockDrawable.scheduleSelf(runnable, 0L);
+ verify(mockCallback).scheduleDrawable(eq(mockDrawable), eq(runnable), eq(0L));
+
+ mockDrawable.scheduleSelf(runnable, -1000L);
+ verify(mockCallback).scheduleDrawable(eq(mockDrawable), eq(runnable), eq(-1000L));
}
public void testAccessCallback() {
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/GradientDrawableTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/GradientDrawableTest.java
index 713f61a..b062073 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/GradientDrawableTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/GradientDrawableTest.java
@@ -16,9 +16,13 @@
package android.graphics.drawable.cts;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
+import android.app.Activity;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.Resources.Theme;
@@ -28,11 +32,14 @@
import android.graphics.ColorFilter;
import android.graphics.PixelFormat;
import android.graphics.Rect;
+import android.graphics.cts.MockActivity;
import android.graphics.cts.R;
import android.graphics.drawable.Drawable.ConstantState;
import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.GradientDrawable.Orientation;
-import android.test.AndroidTestCase;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+
import android.test.suitebuilder.annotation.MediumTest;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.AttributeSet;
@@ -42,9 +49,21 @@
import java.util.Arrays;
import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
-public class GradientDrawableTest extends AndroidTestCase {
+@RunWith(AndroidJUnit4.class)
+public class GradientDrawableTest {
+
+ @Rule
+ public ActivityTestRule<MockActivity> mActivityRule
+ = new ActivityTestRule<>(MockActivity.class);
+
@SmallTest
+ @Test
public void testConstructor() {
int[] color = new int[] {1, 2, 3};
@@ -54,6 +73,7 @@
}
@SmallTest
+ @Test
public void testGetOpacity() {
GradientDrawable gradientDrawable = new GradientDrawable();
assertEquals("Default opacity is TRANSLUCENT",
@@ -87,6 +107,7 @@
}
@SmallTest
+ @Test
public void testSetOrientation() {
GradientDrawable gradientDrawable = new GradientDrawable();
Orientation orientation;
@@ -98,6 +119,7 @@
}
@SmallTest
+ @Test
public void testSetCornerRadii() {
float[] radii = new float[] {1.0f, 2.0f, 3.0f};
@@ -116,6 +138,7 @@
}
@SmallTest
+ @Test
public void testSetCornerRadius() {
GradientDrawable gradientDrawable = new GradientDrawable();
@@ -124,6 +147,22 @@
}
@SmallTest
+ @Test
+ public void testGetCornerRadius() {
+ GradientDrawable gradientDrawable = new GradientDrawable();
+ gradientDrawable.setCornerRadius(5.5f);
+ assertEquals(gradientDrawable.getCornerRadius(), 5.5f, 0);
+ float[] radii = new float[] {1.0f, 2.0f, 3.0f};
+ gradientDrawable.setCornerRadii(radii);
+ assertEquals(5.5f, gradientDrawable.getCornerRadius(), 0);
+ gradientDrawable.setShape(GradientDrawable.OVAL);
+ assertEquals(5.5f, gradientDrawable.getCornerRadius(), 0);
+ gradientDrawable.setCornerRadii(null);
+ assertEquals(0, gradientDrawable.getCornerRadius(), 0);
+ }
+
+ @SmallTest
+ @Test
public void testSetStroke() {
helpTestSetStroke(2, Color.RED);
helpTestSetStroke(-2, Color.TRANSPARENT);
@@ -137,6 +176,7 @@
}
@SmallTest
+ @Test
public void testSetStroke_WidthGap() {
helpTestSetStroke_WidthGap(2, Color.RED, 3.4f, 5.5f);
helpTestSetStroke_WidthGap(-2, Color.TRANSPARENT, -3.4f, -5.5f);
@@ -151,6 +191,7 @@
}
@SmallTest
+ @Test
public void testSetStrokeList() {
helpTestSetStrokeList(2, ColorStateList.valueOf(Color.RED));
helpTestSetStrokeList(-2, ColorStateList.valueOf(Color.TRANSPARENT));
@@ -165,6 +206,7 @@
}
@SmallTest
+ @Test
public void testSetStrokeList_WidthGap() {
helpTestSetStrokeList_WidthGap(2, ColorStateList.valueOf(Color.RED), 3.4f, 5.5f);
helpTestSetStrokeList_WidthGap(-2, ColorStateList.valueOf(Color.TRANSPARENT), -3.4f, -5.5f);
@@ -179,6 +221,7 @@
}
@SmallTest
+ @Test
public void testSetSize() {
helpTestSetSize(6, 4);
helpTestSetSize(-30, -40);
@@ -194,6 +237,7 @@
}
@SmallTest
+ @Test
public void testSetShape() {
GradientDrawable gradientDrawable = new GradientDrawable();
int shape;
@@ -210,6 +254,7 @@
}
@SmallTest
+ @Test
public void testSetGradientType() {
GradientDrawable gradientDrawable = new GradientDrawable();
int gradientType;
@@ -226,6 +271,7 @@
}
@SmallTest
+ @Test
public void testSetGradientCenter() {
GradientDrawable gradientDrawable = new GradientDrawable();
float centerX;
@@ -245,11 +291,12 @@
centerX = 0.0f;
centerY = 0.0f;
gradientDrawable.setGradientCenter(centerX, centerY);
- assertEquals(centerX, gradientDrawable.getGradientCenterX());
- assertEquals(centerY, gradientDrawable.getGradientCenterY());
+ assertEquals(centerX, gradientDrawable.getGradientCenterX(), 0.01f);
+ assertEquals(centerY, gradientDrawable.getGradientCenterY(), 0.01f);
}
@SmallTest
+ @Test
public void testSetGradientRadius() {
GradientDrawable gradientDrawable = new GradientDrawable();
@@ -258,6 +305,7 @@
}
@SmallTest
+ @Test
public void testSetUseLevel() {
GradientDrawable gradientDrawable = new GradientDrawable();
boolean useLevel;
@@ -276,6 +324,7 @@
}
@SmallTest
+ @Test
public void testDraw() {
GradientDrawable gradientDrawable = new GradientDrawable();
Canvas c = new Canvas();
@@ -286,6 +335,7 @@
}
@SmallTest
+ @Test
public void testSetColor() {
GradientDrawable gradientDrawable = new GradientDrawable();
int color;
@@ -302,6 +352,7 @@
}
@SmallTest
+ @Test
public void testSetColors() {
GradientDrawable gradientDrawable = new GradientDrawable();
int[] colors;
@@ -318,6 +369,7 @@
}
@SmallTest
+ @Test
public void testSetColorList() {
GradientDrawable gradientDrawable = new GradientDrawable();
ColorStateList color;
@@ -332,6 +384,7 @@
}
@SmallTest
+ @Test
public void testGetChangingConfigurations() {
GradientDrawable gradientDrawable = new GradientDrawable();
assertEquals(0, gradientDrawable.getChangingConfigurations());
@@ -344,6 +397,7 @@
}
@SmallTest
+ @Test
public void testSetAlpha() {
GradientDrawable gradientDrawable = new GradientDrawable();
@@ -352,6 +406,7 @@
}
@SmallTest
+ @Test
public void testSetDither() {
GradientDrawable gradientDrawable = new GradientDrawable();
@@ -360,6 +415,7 @@
}
@SmallTest
+ @Test
public void testSetColorFilter() {
GradientDrawable gradientDrawable = new GradientDrawable();
ColorFilter cf = new ColorFilter();
@@ -370,6 +426,7 @@
}
@SmallTest
+ @Test
public void testInflate() throws XmlPullParserException, IOException {
GradientDrawable gradientDrawable = new GradientDrawable();
Rect rect = new Rect();
@@ -379,7 +436,7 @@
assertEquals(0, rect.right);
assertEquals(0, rect.bottom);
- Resources resources = mContext.getResources();
+ Resources resources = mActivityRule.getActivity().getResources();
XmlPullParser parser = resources.getXml(R.drawable.gradientdrawable);
AttributeSet attrs = Xml.asAttributeSet(parser);
@@ -415,9 +472,10 @@
}
@SmallTest
+ @Test
public void testInflateGradientRadius() throws XmlPullParserException, IOException {
Rect parentBounds = new Rect(0, 0, 100, 100);
- Resources resources = mContext.getResources();
+ Resources resources = mActivityRule.getActivity().getResources();
GradientDrawable gradientDrawable;
float radius;
@@ -436,6 +494,7 @@
}
@SmallTest
+ @Test
public void testGetIntrinsicWidth() {
GradientDrawable gradientDrawable = new GradientDrawable();
gradientDrawable.setSize(6, 4);
@@ -446,6 +505,7 @@
}
@SmallTest
+ @Test
public void testGetIntrinsicHeight() {
GradientDrawable gradientDrawable = new GradientDrawable();
gradientDrawable.setSize(5, 3);
@@ -456,14 +516,16 @@
}
@SmallTest
+ @Test
public void testGetConstantState() {
GradientDrawable gradientDrawable = new GradientDrawable();
assertNotNull(gradientDrawable.getConstantState());
}
@SmallTest
+ @Test
public void testMutate() {
- Resources resources = mContext.getResources();
+ Resources resources = mActivityRule.getActivity().getResources();
GradientDrawable d1 = (GradientDrawable) resources.getDrawable(R.drawable.gradientdrawable);
GradientDrawable d2 = (GradientDrawable) resources.getDrawable(R.drawable.gradientdrawable);
GradientDrawable d3 = (GradientDrawable) resources.getDrawable(R.drawable.gradientdrawable);
@@ -495,8 +557,9 @@
}
@MediumTest
+ @Test
public void testPreloadDensity() throws XmlPullParserException, IOException {
- final Resources res = getContext().getResources();
+ final Resources res = mActivityRule.getActivity().getResources();
final int densityDpi = res.getConfiguration().densityDpi;
try {
testPreloadDensityInner(res, densityDpi);
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/ShapeDrawable_ShaderFactoryTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/ShapeDrawable_ShaderFactoryTest.java
index b736519..0447fb2 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/ShapeDrawable_ShaderFactoryTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/ShapeDrawable_ShaderFactoryTest.java
@@ -17,10 +17,26 @@
package android.graphics.drawable.cts;
import junit.framework.TestCase;
-import android.graphics.drawable.ShapeDrawable;
+import android.graphics.Shader;
+import android.graphics.drawable.ShapeDrawable.ShaderFactory;
+import android.support.test.filters.SmallTest;
+
+@SmallTest
public class ShapeDrawable_ShaderFactoryTest extends TestCase {
+
public void testResize() {
- // resize is an abstract function.
+ // This is an abstract function, but coverage
+ // complains if we don't call it.
+ ShaderFactory impl = new ShaderFactoryImpl();
+ assertNull(impl.resize(0, 0));
+ }
+
+ private class ShaderFactoryImpl extends ShaderFactory {
+
+ @Override
+ public Shader resize(int width, int height) {
+ return null;
+ }
}
}
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/VectorDrawableTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/VectorDrawableTest.java
index e2fe8c5..005fe52 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/VectorDrawableTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/VectorDrawableTest.java
@@ -93,6 +93,7 @@
R.drawable.vector_icon_stroke_3,
R.drawable.vector_icon_scale_1,
R.drawable.vector_icon_scale_2,
+ R.drawable.vector_icon_group_clip,
};
private static final int[] L_M_GOLDEN_IMAGES = new int[] {
@@ -109,6 +110,7 @@
R.drawable.vector_icon_stroke_3_golden,
R.drawable.vector_icon_scale_1_golden,
R.drawable.vector_icon_scale_2_golden,
+ R.drawable.vector_icon_group_clip_golden,
};
private static final int[] N_ICON_RES_IDS = new int[] {
diff --git a/tests/tests/graphics/src/android/graphics/pdf/cts/PdfDocumentTest.java b/tests/tests/graphics/src/android/graphics/pdf/cts/PdfDocumentTest.java
new file mode 100644
index 0000000..09c6cd0
--- /dev/null
+++ b/tests/tests/graphics/src/android/graphics/pdf/cts/PdfDocumentTest.java
@@ -0,0 +1,424 @@
+/*
+ * 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.graphics.pdf.cts;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.graphics.pdf.PdfDocument;
+import android.graphics.pdf.PdfRenderer;
+import android.os.ParcelFileDescriptor;
+import android.support.annotation.NonNull;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.OutputStream;
+
+import static android.graphics.pdf.cts.Utils.assertException;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Tests {@link PdfDocument}
+ */
+@RunWith(AndroidJUnit4.class)
+public class PdfDocumentTest {
+ private Context mContext;
+
+ @Before
+ public void setUp() {
+ mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ }
+
+ @Test
+ public void getPagesEmptyDocAfterClose() throws Exception {
+ PdfDocument doc = new PdfDocument();
+ doc.close();
+ assertEquals(0, doc.getPages().size());
+ }
+
+ @Test
+ public void closeClosedDoc() throws Exception {
+ PdfDocument doc = new PdfDocument();
+ doc.close();
+
+ // legacy behavior, double close does nothing
+ doc.close();
+ }
+
+ @Test
+ public void writeClosedDoc() throws Exception {
+ PdfDocument doc = new PdfDocument();
+ doc.close();
+
+ OutputStream os = new FileOutputStream(File.createTempFile("tmp", "pdf",
+ mContext.getCacheDir()));
+
+ assertException(() -> doc.writeTo(os), IllegalStateException.class);
+ }
+
+ @Test
+ public void startPageClosedDoc() throws Exception {
+ PdfDocument doc = new PdfDocument();
+ doc.close();
+
+ assertException(() -> doc.startPage(new PdfDocument.PageInfo.Builder(100, 100, 0).create()),
+ IllegalStateException.class);
+ }
+
+ @Test
+ public void finishPageTwiceDoc() throws Exception {
+ PdfDocument doc = new PdfDocument();
+
+ PdfDocument.Page page = doc
+ .startPage(new PdfDocument.PageInfo.Builder(100, 100, 0).create());
+ doc.finishPage(page);
+ assertException(() -> doc.finishPage(page), IllegalStateException.class);
+
+ doc.close();
+ }
+
+ @Test
+ public void closeWithOpenPage() throws Exception {
+ PdfDocument doc = new PdfDocument();
+
+ PdfDocument.Page page = doc
+ .startPage(new PdfDocument.PageInfo.Builder(100, 100, 0).create());
+ assertException(doc::close, IllegalStateException.class);
+ doc.finishPage(page);
+ doc.close();
+ }
+
+ @Test
+ public void writeEmptyDoc() throws Exception {
+ PdfDocument doc = new PdfDocument();
+
+ // Legacy behavior. Writing an empty doc does not fail.
+ File pdfFile = File.createTempFile("tmp", "pdf", mContext.getCacheDir());
+ try (OutputStream os = new FileOutputStream(pdfFile)) {
+ doc.writeTo(os);
+ }
+ doc.close();
+ }
+
+ @Test
+ public void writeWithOpenPage() throws Exception {
+ PdfDocument doc = new PdfDocument();
+
+ PdfDocument.Page page = doc
+ .startPage(new PdfDocument.PageInfo.Builder(100, 100, 0).create());
+
+ File pdfFile = File.createTempFile("tmp", "pdf", mContext.getCacheDir());
+ try (OutputStream os = new FileOutputStream(pdfFile)) {
+ assertException(() -> doc.writeTo(os), IllegalStateException.class);
+ }
+
+ doc.finishPage(page);
+ doc.close();
+ }
+
+ @Test
+ public void openTwoPages() throws Exception {
+ PdfDocument doc = new PdfDocument();
+
+ PdfDocument.Page page = doc
+ .startPage(new PdfDocument.PageInfo.Builder(100, 100, 0).create());
+ assertException(() -> doc.startPage(new PdfDocument.PageInfo.Builder(100, 100, 1).create()),
+ IllegalStateException.class);
+
+ doc.finishPage(page);
+ doc.close();
+ }
+
+ @Test
+ public void finishPageFromWrongDoc() throws Exception {
+ PdfDocument doc1 = new PdfDocument();
+ PdfDocument doc2 = new PdfDocument();
+
+ PdfDocument.Page page1 = doc1
+ .startPage(new PdfDocument.PageInfo.Builder(100, 100, 0).create());
+ assertException(() -> doc2.finishPage(page1), IllegalStateException.class);
+
+ PdfDocument.Page page2 = doc2
+ .startPage(new PdfDocument.PageInfo.Builder(100, 100, 0).create());
+ assertException(() -> doc1.finishPage(page2), IllegalStateException.class);
+
+ doc1.finishPage(page1);
+ doc2.finishPage(page2);
+ doc1.close();
+ doc2.close();
+ }
+
+ @Test
+ public void writeTwoPageDocWithSameIndex() throws Exception {
+ PdfDocument doc = new PdfDocument();
+
+ PdfDocument.Page page0 = doc
+ .startPage(new PdfDocument.PageInfo.Builder(101, 100, 0).create());
+ doc.finishPage(page0);
+ PdfDocument.Page page1 = doc
+ .startPage(new PdfDocument.PageInfo.Builder(201, 200, 0).create());
+ doc.finishPage(page1);
+ assertEquals(2, doc.getPages().size());
+
+ File pdfFile = File.createTempFile("tmp", "pdf", mContext.getCacheDir());
+ try (OutputStream os = new FileOutputStream(pdfFile)) {
+ doc.writeTo(os);
+ }
+
+ try (ParcelFileDescriptor fd = ParcelFileDescriptor.open(pdfFile,
+ ParcelFileDescriptor.MODE_READ_ONLY)) {
+ PdfRenderer renderer = new PdfRenderer(fd);
+ assertEquals(2, renderer.getPageCount());
+ try (PdfRenderer.Page page = renderer.openPage(0)) {
+ assertEquals(0, page.getIndex());
+ assertEquals(101, page.getWidth());
+ assertEquals(100, page.getHeight());
+ }
+ try (PdfRenderer.Page page = renderer.openPage(1)) {
+ assertEquals(1, page.getIndex());
+ assertEquals(201, page.getWidth());
+ assertEquals(200, page.getHeight());
+ }
+ }
+
+ doc.close();
+ }
+
+ /**
+ * Replacement for non existing <code>{@link PdfDocument.PageInfo}#equals()</code>
+ *
+ * @param a The first info, can not be null
+ * @param b The second info, can not be null
+ *
+ * @return If a is equal to b
+ */
+ private boolean pageInfoEquals(@NonNull PdfDocument.PageInfo a,
+ @NonNull PdfDocument.PageInfo b) {
+ return a.getContentRect().equals(b.getContentRect()) &&
+ a.getPageHeight() == b.getPageHeight() && a.getPageWidth() == b.getPageWidth() &&
+ a.getPageNumber() == b.getPageNumber();
+ }
+
+ @Test
+ public void writeTwoPageDoc() throws Exception {
+ PdfDocument doc = new PdfDocument();
+
+ assertEquals(0, doc.getPages().size());
+
+ PdfDocument.Page page0 = doc
+ .startPage(new PdfDocument.PageInfo.Builder(101, 100, 0).create());
+
+ assertEquals(0, doc.getPages().size());
+ doc.finishPage(page0);
+ assertEquals(1, doc.getPages().size());
+ assertTrue(pageInfoEquals(page0.getInfo(), doc.getPages().get(0)));
+
+ File page1File = File.createTempFile("tmp", "pdf", mContext.getCacheDir());
+ try (OutputStream os = new FileOutputStream(page1File)) {
+ doc.writeTo(os);
+ }
+
+ try (ParcelFileDescriptor fd = ParcelFileDescriptor.open(page1File,
+ ParcelFileDescriptor.MODE_READ_ONLY)) {
+ PdfRenderer renderer = new PdfRenderer(fd);
+ assertEquals(1, renderer.getPageCount());
+ try (PdfRenderer.Page page = renderer.openPage(0)) {
+ assertEquals(0, page.getIndex());
+ assertEquals(101, page.getWidth());
+ assertEquals(100, page.getHeight());
+ }
+ }
+
+ PdfDocument.Page page1 = doc
+ .startPage(new PdfDocument.PageInfo.Builder(201, 200, 1).create());
+
+ doc.finishPage(page1);
+ assertEquals(2, doc.getPages().size());
+ assertTrue(pageInfoEquals(page0.getInfo(), doc.getPages().get(0)));
+ assertTrue(pageInfoEquals(page1.getInfo(), doc.getPages().get(1)));
+
+ File page2File = File.createTempFile("tmp", "pdf", mContext.getCacheDir());
+ try (OutputStream os = new FileOutputStream(page2File)) {
+ doc.writeTo(os);
+ }
+
+ try (ParcelFileDescriptor fd = ParcelFileDescriptor.open(page2File,
+ ParcelFileDescriptor.MODE_READ_ONLY)) {
+ PdfRenderer renderer = new PdfRenderer(fd);
+ assertEquals(2, renderer.getPageCount());
+ try (PdfRenderer.Page page = renderer.openPage(0)) {
+ assertEquals(0, page.getIndex());
+ assertEquals(101, page.getWidth());
+ assertEquals(100, page.getHeight());
+ }
+ try (PdfRenderer.Page page = renderer.openPage(1)) {
+ assertEquals(1, page.getIndex());
+ assertEquals(201, page.getWidth());
+ assertEquals(200, page.getHeight());
+ }
+ }
+
+ doc.close();
+ }
+
+ @Test
+ public void writeToNull() throws Exception {
+ PdfDocument doc = new PdfDocument();
+ assertException(() -> doc.writeTo(null), IllegalArgumentException.class);
+ doc.close();
+ }
+
+ @Test
+ public void startNullPage() throws Exception {
+ PdfDocument doc = new PdfDocument();
+ assertException(() -> doc.startPage(null), IllegalArgumentException.class);
+ doc.close();
+ }
+
+ @Test
+ public void finishNullPage() throws Exception {
+ PdfDocument doc = new PdfDocument();
+ assertException(() -> doc.finishPage(null), IllegalArgumentException.class);
+ doc.close();
+ }
+
+ @Test
+ public void zeroWidthPage() throws Exception {
+ assertException(() -> new PdfDocument.PageInfo.Builder(0, 200, 0),
+ IllegalArgumentException.class);
+ }
+
+ @Test
+ public void negativeWidthPage() throws Exception {
+ assertException(() -> new PdfDocument.PageInfo.Builder(-1, 200, 0),
+ IllegalArgumentException.class);
+ }
+
+ @Test
+ public void zeroHeightPage() throws Exception {
+ assertException(() -> new PdfDocument.PageInfo.Builder(100, 0, 0),
+ IllegalArgumentException.class);
+ }
+
+ @Test
+ public void negativeHeightPage() throws Exception {
+ assertException(() -> new PdfDocument.PageInfo.Builder(100, -1, 0),
+ IllegalArgumentException.class);
+ }
+
+ @Test
+ public void negativePageNumber() throws Exception {
+ assertException(() -> new PdfDocument.PageInfo.Builder(100, 200, -1),
+ IllegalArgumentException.class);
+ }
+
+ @Test
+ public void contentRectLeftNegative() throws Exception {
+ assertException(() -> new PdfDocument.PageInfo.Builder(100, 200, 0)
+ .setContentRect(new Rect(-1, 0, 100, 200)),
+ IllegalArgumentException.class);
+ }
+
+ @Test
+ public void contentRectTopNegative() throws Exception {
+ assertException(() -> new PdfDocument.PageInfo.Builder(100, 200, 0)
+ .setContentRect(new Rect(0, -1, 100, 200)),
+ IllegalArgumentException.class);
+ }
+
+ @Test
+ public void contentRectRightToHigh() throws Exception {
+ assertException(() -> new PdfDocument.PageInfo.Builder(100, 200, 0)
+ .setContentRect(new Rect(0, 0, 101, 200)),
+ IllegalArgumentException.class);
+ }
+
+ @Test
+ public void contentRectBottomToHigh() throws Exception {
+ assertException(() -> new PdfDocument.PageInfo.Builder(100, 200, 0)
+ .setContentRect(new Rect(0, 0, 100, 201)),
+ IllegalArgumentException.class);
+ }
+
+ @Test
+ public void createPageWithFullContentRect() throws Exception {
+ PdfDocument doc = new PdfDocument();
+ Rect contentRect = new Rect(0, 0, 100, 200);
+ PdfDocument.Page page = doc.startPage((new PdfDocument.PageInfo.Builder(100, 200, 0))
+ .setContentRect(contentRect).create());
+ assertEquals(page.getInfo().getContentRect(), contentRect);
+ assertEquals(100, page.getCanvas().getWidth());
+ assertEquals(200, page.getCanvas().getHeight());
+ doc.finishPage(page);
+ doc.close();
+ }
+
+ @Test
+ public void createPageWithPartialContentRect() throws Exception {
+ PdfDocument doc = new PdfDocument();
+ Rect contentRect = new Rect(10, 20, 90, 180);
+ PdfDocument.Page page = doc.startPage((new PdfDocument.PageInfo.Builder(100, 200, 0))
+ .setContentRect(contentRect).create());
+ assertEquals(page.getInfo().getContentRect(), contentRect);
+ assertEquals(80, page.getCanvas().getWidth());
+ assertEquals(160, page.getCanvas().getHeight());
+ doc.finishPage(page);
+ doc.close();
+ }
+
+ @Test
+ public void createPageWithEmptyContentRect() throws Exception {
+ PdfDocument doc = new PdfDocument();
+ Rect contentRect = new Rect(50, 100, 50, 100);
+ PdfDocument.Page page = doc.startPage((new PdfDocument.PageInfo.Builder(100, 200, 0))
+ .setContentRect(contentRect).create());
+ assertEquals(page.getInfo().getContentRect(), contentRect);
+ assertEquals(0, page.getCanvas().getWidth());
+ assertEquals(0, page.getCanvas().getHeight());
+ doc.finishPage(page);
+ doc.close();
+ }
+
+ @Test
+ public void createPageWithInverseContentRect() throws Exception {
+ PdfDocument doc = new PdfDocument();
+
+ // A Rect can have a lower right than left and bottom than top. Of course this does not make
+ // sense for a content rect. For legacy reasons this is treated as we have a empty content
+ // rect.
+ Rect contentRect = new Rect(90, 180, 10, 20);
+ PdfDocument.Page page = doc.startPage((new PdfDocument.PageInfo.Builder(100, 200, 0))
+ .setContentRect(contentRect).create());
+ assertEquals(page.getInfo().getContentRect(), contentRect);
+ assertEquals(0, page.getCanvas().getWidth());
+ assertEquals(0, page.getCanvas().getHeight());
+ doc.finishPage(page);
+
+ doc.close();
+ }
+
+ @Test
+ public void defaultContentRectIsFullRect() throws Exception {
+ PdfDocument.PageInfo info =(new PdfDocument.PageInfo.Builder(100, 200, 0)).create();
+ assertEquals(info.getContentRect(), new Rect(0, 0, 100, 200));
+ }
+}
diff --git a/tests/tests/graphics/src/android/graphics/pdf/cts/PdfRendererTest.java b/tests/tests/graphics/src/android/graphics/pdf/cts/PdfRendererTest.java
new file mode 100644
index 0000000..cc3ea7e
--- /dev/null
+++ b/tests/tests/graphics/src/android/graphics/pdf/cts/PdfRendererTest.java
@@ -0,0 +1,362 @@
+/*
+ * 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.graphics.pdf.cts;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Color;
+import android.graphics.Matrix;
+import android.graphics.Rect;
+import android.graphics.cts.R;
+import android.graphics.pdf.PdfRenderer;
+import android.graphics.pdf.PdfRenderer.Page;
+import android.os.ParcelFileDescriptor;
+import android.support.annotation.NonNull;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.IOException;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * All test for {@link PdfRenderer} beside the valid transformation parameter tests of {@link
+ * PdfRenderer.Page#render}.
+ */
+@RunWith(AndroidJUnit4.class)
+public class PdfRendererTest {
+ private static final int A5_PORTRAIT_PRINTSCALING_DEFAULT =
+ R.raw.a5_portrait_rgbb_1_6_printscaling_default;
+ private static final int A5_PORTRAIT_PRINTSCALING_NONE =
+ R.raw.a5_portrait_rgbb_1_6_printscaling_none;
+ private static final int TWO_PAGES = R.raw.two_pages;
+
+ private Context mContext;
+
+ @Before
+ public void setUp() {
+ mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ }
+
+ @Test
+ public void constructRendererNull() throws Exception {
+ Utils.assertException(() -> new PdfRenderer(null), NullPointerException.class);
+ }
+
+ @Test
+ @Ignore("Makes all subsequent tests fail")
+ public void constructRendererFromNonPDF() throws Exception {
+ // Open jpg as if it was a PDF
+ ParcelFileDescriptor fd = mContext.getResources().openRawResourceFd(R.raw.testimage)
+ .getParcelFileDescriptor();
+ Utils.assertException(() -> new PdfRenderer(fd), IOException.class);
+ }
+
+ @Test
+ public void useRendererAfterClose() throws Exception {
+ PdfRenderer renderer = Utils.createRenderer(Utils.A4_PORTRAIT, mContext);
+ renderer.close();
+
+ Utils.assertException(renderer::close, IllegalStateException.class);
+ Utils.assertException(renderer::getPageCount, IllegalStateException.class);
+ Utils.assertException(renderer::shouldScaleForPrinting, IllegalStateException.class);
+ Utils.assertException(() -> renderer.openPage(0), IllegalStateException.class);
+ }
+
+ @Test
+ public void usePageAfterClose() throws Exception {
+ PdfRenderer renderer = Utils.createRenderer(Utils.A4_PORTRAIT, mContext);
+ Page page = renderer.openPage(0);
+ page.close();
+
+ // Legacy behavior: The properties are cached, hence they are still available after the page
+ // is closed
+ page.getHeight();
+ page.getWidth();
+ page.getIndex();
+ Utils.assertException(page::close, IllegalStateException.class);
+
+ // Legacy support. An IllegalStateException would be nice by unfortunately the legacy
+ // implementation returned NullPointerException
+ Utils.assertException(() -> page.render(null, null, null, Page.RENDER_MODE_FOR_DISPLAY),
+ NullPointerException.class);
+
+ renderer.close();
+ }
+
+ @Test
+ public void closeWithOpenPage() throws Exception {
+ PdfRenderer renderer = Utils.createRenderer(Utils.A4_PORTRAIT, mContext);
+ Page page = renderer.openPage(0);
+
+ Utils.assertException(renderer::close, IllegalStateException.class);
+
+ page.close();
+ renderer.close();
+ }
+
+ @Test
+ public void openTwoPages() throws Exception {
+ try (PdfRenderer renderer = Utils.createRenderer(TWO_PAGES, mContext)) {
+ // Cannot open two pages at once
+ Page page = renderer.openPage(0);
+ Utils.assertException(() -> renderer.openPage(1), IllegalStateException.class);
+
+ page.close();
+ }
+ }
+
+ @Test
+ public void testPageCount() throws Exception {
+ try (PdfRenderer renderer = Utils.createRenderer(TWO_PAGES, mContext)) {
+ assertEquals(2, renderer.getPageCount());
+ }
+ }
+
+ @Test
+ public void testOpenPage() throws Exception {
+ try (PdfRenderer renderer = Utils.createRenderer(TWO_PAGES, mContext)) {
+ Utils.assertException(() -> renderer.openPage(-1), IllegalArgumentException.class);
+ Page page0 = renderer.openPage(0);
+ page0.close();
+ Page page1 = renderer.openPage(1);
+ page1.close();
+ Utils.assertException(() -> renderer.openPage(2), IllegalArgumentException.class);
+ }
+ }
+
+ @Test
+ public void testPageSize() throws Exception {
+ try (PdfRenderer renderer = Utils.createRenderer(Utils.A4_PORTRAIT, mContext)) {
+ try (Page page = renderer.openPage(0)) {
+ assertEquals(Utils.A4_HEIGHT_PTS, page.getHeight());
+ assertEquals(Utils.A4_WIDTH_PTS, page.getWidth());
+ }
+ }
+ }
+
+ @Test
+ public void testPrintScaleDefault() throws Exception {
+ try (PdfRenderer renderer = Utils.createRenderer(Utils.A5_PORTRAIT, mContext)) {
+ assertTrue(renderer.shouldScaleForPrinting());
+ }
+ }
+
+ @Test
+ public void testPrintScalePDF16Default() throws Exception {
+ try (PdfRenderer renderer = Utils
+ .createRenderer(A5_PORTRAIT_PRINTSCALING_DEFAULT, mContext)) {
+ assertTrue(renderer.shouldScaleForPrinting());
+ }
+ }
+
+ @Test
+ public void testPrintScalePDF16None() throws Exception {
+ try (PdfRenderer renderer = Utils.createRenderer(A5_PORTRAIT_PRINTSCALING_NONE, mContext)) {
+ assertFalse(renderer.shouldScaleForPrinting());
+ }
+ }
+
+ /**
+ * Take 16 color probes in the middle of the 16 segments of the page in the following pattern:
+ * <pre>
+ * +----+----+----+----+
+ * | 0 : 1 : 2 : 3 |
+ * +....:....:....:....+
+ * | 4 : 5 : 6 : 7 |
+ * +....:....:....:....+
+ * | 8 : 9 : 10 : 11 |
+ * +....:....:....:....+
+ * | 12 : 13 : 14 : 15 |
+ * +----+----+----+----+
+ * </pre>
+ *
+ * @param bm The bitmap to probe
+ *
+ * @return The color at the probes
+ */
+ private @NonNull int[] getColorProbes(@NonNull Bitmap bm) {
+ int[] probes = new int[16];
+
+ for (int row = 0; row < 4; row++) {
+ for (int column = 0; column < 4; column++) {
+ probes[row * 4 + column] = bm.getPixel((int) (bm.getWidth() * (column + 0.5) / 4),
+ (int) (bm.getHeight() * (row + 0.5) / 4));
+ }
+ }
+
+ return probes;
+ }
+
+ /**
+ * Implementation for {@link #renderNoTransformationAndComparePointsForScreen} and {@link
+ * #renderNoTransformationAndComparePointsForPrint}.
+ *
+ * @param renderMode The render mode to use
+ *
+ * @throws Exception If anything was unexpected
+ */
+ private void renderNoTransformationAndComparePoints(int renderMode) throws Exception {
+ Bitmap bm = Utils.renderWithTransform(Utils.A4_WIDTH_PTS, Utils.A4_HEIGHT_PTS,
+ Utils.A4_PORTRAIT, null, null, renderMode, mContext);
+ int[] probes = getColorProbes(bm);
+
+ // Compare rendering to expected result. This ensures that all other tests in this class do
+ // not accidentally all compare empty bitmaps.
+ assertEquals(Color.RED, probes[0]);
+ assertEquals(Color.RED, probes[1]);
+ assertEquals(Color.GREEN, probes[2]);
+ assertEquals(Color.GREEN, probes[3]);
+ assertEquals(Color.RED, probes[4]);
+ assertEquals(Color.RED, probes[5]);
+ assertEquals(Color.GREEN, probes[6]);
+ assertEquals(Color.GREEN, probes[7]);
+ assertEquals(Color.BLUE, probes[8]);
+ assertEquals(Color.BLUE, probes[9]);
+ assertEquals(Color.BLACK, probes[10]);
+ assertEquals(Color.BLACK, probes[11]);
+ assertEquals(Color.BLUE, probes[12]);
+ assertEquals(Color.BLUE, probes[13]);
+ assertEquals(Color.BLACK, probes[14]);
+ assertEquals(Color.BLACK, probes[15]);
+ }
+
+ @Test
+ public void renderNoTransformationAndComparePointsForScreen() throws Exception {
+ renderNoTransformationAndComparePoints(Page.RENDER_MODE_FOR_DISPLAY);
+ }
+
+ @Test
+ public void renderNoTransformationAndComparePointsForPrint() throws Exception {
+ renderNoTransformationAndComparePoints(Page.RENDER_MODE_FOR_PRINT);
+ }
+
+ @Test
+ public void renderPerspective() throws Exception {
+ Matrix transform = new Matrix();
+
+ transform.setValues(new float[] { 1, 1, 1, 1, 1, 1, 1, 1, 1 });
+
+ Utils.assertException(() -> Utils
+ .renderWithTransform(Utils.A4_WIDTH_PTS, Utils.A4_HEIGHT_PTS,
+ Utils.A4_PORTRAIT, null, transform, Page.RENDER_MODE_FOR_DISPLAY,
+ mContext), IllegalArgumentException.class);
+ }
+
+ @Test
+ public void render45degreeRotationTranslationAndScaleAndClip() throws Exception {
+ Matrix transform = new Matrix();
+ // Rotate on top left corner
+ transform.postRotate(45);
+ // Move
+ transform.postTranslate(Utils.A4_WIDTH_PTS / 4, Utils.A4_HEIGHT_PTS / 4);
+ // Scale to 75%
+ transform.postScale(0.75f, 0.75f);
+ // Clip
+ Rect clip = new Rect(20, 20, Utils.A4_WIDTH_PTS - 20, Utils.A4_HEIGHT_PTS - 20);
+
+ Utils.renderAndCompare(Utils.A4_WIDTH_PTS, Utils.A4_HEIGHT_PTS, Utils.A4_PORTRAIT, clip,
+ transform, Page.RENDER_MODE_FOR_DISPLAY, mContext);
+ }
+
+ @Test
+ public void renderStreched() throws Exception {
+ Utils.renderAndCompare(Utils.A4_WIDTH_PTS * 4 / 3, Utils.A4_HEIGHT_PTS * 3 / 4,
+ Utils.A4_PORTRAIT, null, null, Page.RENDER_MODE_FOR_DISPLAY, mContext);
+ }
+
+ @Test
+ public void renderWithClip() throws Exception {
+ Rect clip = new Rect(20, 20, Utils.A4_WIDTH_PTS - 50, Utils.A4_HEIGHT_PTS - 50);
+ Utils.renderAndCompare(Utils.A4_WIDTH_PTS, Utils.A4_HEIGHT_PTS,
+ Utils.A4_PORTRAIT, clip, null, Page.RENDER_MODE_FOR_DISPLAY, mContext);
+ }
+
+ @Test
+ public void renderWithAllClipped() throws Exception {
+ Rect clip = new Rect(Utils.A4_WIDTH_PTS / 2, Utils.A4_HEIGHT_PTS / 2,
+ Utils.A4_WIDTH_PTS / 2, Utils.A4_HEIGHT_PTS / 2);
+ Utils.renderAndCompare(Utils.A4_WIDTH_PTS, Utils.A4_HEIGHT_PTS, Utils.A4_PORTRAIT, clip,
+ null, Page.RENDER_MODE_FOR_DISPLAY, mContext);
+ }
+
+ @Test
+ public void renderWithBadLowerCornerOfClip() throws Exception {
+ Rect clip = new Rect(0, 0, Utils.A4_WIDTH_PTS + 20, Utils.A4_HEIGHT_PTS + 20);
+ Utils.assertException(() -> Utils
+ .renderWithTransform(Utils.A4_WIDTH_PTS, Utils.A4_HEIGHT_PTS,
+ Utils.A4_PORTRAIT, clip, null, Page.RENDER_MODE_FOR_DISPLAY,
+ mContext),
+ IllegalArgumentException.class);
+ }
+
+ @Test
+ public void renderWithBadUpperCornerOfClip() throws Exception {
+ Rect clip = new Rect(-20, -20, Utils.A4_WIDTH_PTS, Utils.A4_HEIGHT_PTS);
+ Utils.assertException(() -> Utils
+ .renderWithTransform(Utils.A4_WIDTH_PTS, Utils.A4_HEIGHT_PTS,
+ Utils.A4_PORTRAIT, clip, null, Page.RENDER_MODE_FOR_DISPLAY,
+ mContext),
+ IllegalArgumentException.class);
+ }
+
+ @Test
+ public void renderTwoModes() throws Exception {
+ Utils.assertException(() -> Utils
+ .renderWithTransform(Utils.A4_WIDTH_PTS, Utils.A4_HEIGHT_PTS, Utils.A4_PORTRAIT,
+ null, null, Page.RENDER_MODE_FOR_DISPLAY | Page.RENDER_MODE_FOR_PRINT,
+ mContext), IllegalArgumentException.class);
+ }
+
+ @Test
+ public void renderBadMode() throws Exception {
+ Utils.assertException(() -> Utils
+ .renderWithTransform(Utils.A4_WIDTH_PTS, Utils.A4_HEIGHT_PTS, Utils.A4_PORTRAIT,
+ null, null, 1 << 30, mContext), IllegalArgumentException.class);
+ }
+
+ @Test
+ public void renderAllModes() throws Exception {
+ Utils.assertException(() -> Utils
+ .renderWithTransform(Utils.A4_WIDTH_PTS, Utils.A4_HEIGHT_PTS, Utils.A4_PORTRAIT,
+ null, null, -1, mContext), IllegalArgumentException.class);
+ }
+
+ @Test
+ public void renderNoMode() throws Exception {
+ Utils.assertException(() -> Utils
+ .renderWithTransform(Utils.A4_WIDTH_PTS, Utils.A4_HEIGHT_PTS, Utils.A4_PORTRAIT,
+ null, null, 0, mContext), IllegalArgumentException.class);
+ }
+
+ @Test
+ public void renderOnNullBitmap() throws Exception {
+ try (PdfRenderer renderer = Utils.createRenderer(Utils.A4_PORTRAIT, mContext)) {
+ try (Page page = renderer.openPage(0)) {
+ Utils.assertException(() -> page.render(null, null, null, Page.RENDER_MODE_FOR_DISPLAY),
+ NullPointerException.class);
+ }
+ }
+ }
+
+}
diff --git a/tests/tests/graphics/src/android/graphics/pdf/cts/PdfRendererTransformTest.java b/tests/tests/graphics/src/android/graphics/pdf/cts/PdfRendererTransformTest.java
new file mode 100644
index 0000000..facbd4e
--- /dev/null
+++ b/tests/tests/graphics/src/android/graphics/pdf/cts/PdfRendererTransformTest.java
@@ -0,0 +1,121 @@
+/*
+ * 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.graphics.pdf.cts;
+
+import android.content.Context;
+import android.graphics.Matrix;
+import android.graphics.Rect;
+import android.graphics.pdf.PdfRenderer;
+import android.graphics.pdf.PdfRenderer.Page;
+import android.support.annotation.Nullable;
+import android.support.annotation.RawRes;
+import android.support.test.InstrumentationRegistry;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+/**
+ * Test for the {@link PdfRenderer}
+ */
+@RunWith(Parameterized.class)
+public class PdfRendererTransformTest {
+ private Context mContext;
+ private int mWidth;
+ private int mHeight;
+ private int mDocRes;
+ private @Nullable Rect mClipping;
+ private @Nullable Matrix mTransformation;
+ private int mRenderMode;
+
+ public PdfRendererTransformTest(int width, int height, @RawRes int docRes,
+ @Nullable Rect clipping, @Nullable Matrix transformation, int renderMode) {
+ mWidth = width;
+ mHeight = height;
+ mDocRes = docRes;
+ mClipping = clipping;
+ mTransformation = transformation;
+ mRenderMode = renderMode;
+ }
+
+ @Parameterized.Parameters
+ public static Collection<Object[]> getParameters() {
+ int[] widths = new int[] { Utils.A4_WIDTH_PTS * 3 / 4, Utils.A4_WIDTH_PTS,
+ Utils.A4_WIDTH_PTS * 4 / 3
+ };
+ int[] heights = new int[] { Utils.A4_HEIGHT_PTS * 3 / 4, Utils.A4_HEIGHT_PTS,
+ Utils.A4_HEIGHT_PTS * 4 / 3
+ };
+ int[] rotations = new int[] { 0, 15, 90, 180 };
+ int[] translations = new int[] { -Utils.A4_HEIGHT_PTS / 2, 0, Utils.A4_HEIGHT_PTS / 2 };
+ float[] scales = { -0.5f, 0, 1, 1.5f };
+
+ Collection<Object[]> params = new ArrayList<>();
+
+ for (int rotation : rotations) {
+ for (float scaleX : scales) {
+ for (float scaleY : scales) {
+ for (int translateX : translations) {
+ for (int translateY : translations) {
+ Matrix transformation = new Matrix();
+ if (rotation != 0 || translateX != 0 || translateY != 0
+ || scaleX != 0 || scaleY != 0) {
+ if (rotation != 0) {
+ transformation.postRotate(rotation);
+ }
+
+ if (scaleX != 0 || scaleY != 0) {
+ transformation.postScale(scaleX, scaleY);
+ }
+
+ if (translateX != 0 || translateY != 0) {
+ transformation.postTranslate(translateX,
+ translateY);
+ }
+ }
+
+ for (int width : widths) {
+ for (int height : heights) {
+ params.add(
+ new Object[] { width, height, Utils.A4_PORTRAIT, null,
+ transformation, Page.RENDER_MODE_FOR_DISPLAY
+ });
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return params;
+ }
+
+ @Before
+ public void setUp() {
+ mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ }
+
+ @Test
+ public void test() throws Exception {
+ Utils.renderAndCompare(mWidth, mHeight, mDocRes, mClipping, mTransformation, mRenderMode,
+ mContext);
+ }
+}
diff --git a/tests/tests/graphics/src/android/graphics/pdf/cts/Utils.java b/tests/tests/graphics/src/android/graphics/pdf/cts/Utils.java
new file mode 100644
index 0000000..b6989a7
--- /dev/null
+++ b/tests/tests/graphics/src/android/graphics/pdf/cts/Utils.java
@@ -0,0 +1,299 @@
+/*
+ * 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.graphics.pdf.cts;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.graphics.Rect;
+import android.graphics.pdf.PdfRenderer;
+import android.os.ParcelFileDescriptor;
+import android.support.annotation.FloatRange;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.RawRes;
+import android.util.ArrayMap;
+import android.util.Log;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Arrays;
+import java.util.Map;
+
+import static org.junit.Assert.fail;
+
+/**
+ * Utilities for this package
+ */
+class Utils {
+ private static final String LOG_TAG = "Utils";
+
+ private static Map<Integer, File> sFiles = new ArrayMap<>();
+ private static Map<Integer, Bitmap> sRenderedBitmaps = new ArrayMap<>();
+
+ static final int A4_WIDTH_PTS = 595;
+ static final int A4_HEIGHT_PTS = 841;
+ static final int A4_PORTRAIT = android.graphics.cts.R.raw.a4_portrait_rgbb;
+ static final int A5_PORTRAIT = android.graphics.cts.R.raw.a5_portrait_rgbb;
+
+ /**
+ * Create a {@link PdfRenderer} pointing to a file copied from a resource.
+ *
+ * @param docRes The resource to load
+ * @param context The context to use for creating the renderer
+ *
+ * @return the renderer
+ *
+ * @throws IOException If anything went wrong
+ */
+ static @NonNull PdfRenderer createRenderer(@RawRes int docRes, @NonNull Context context)
+ throws IOException {
+ File pdfFile = sFiles.get(docRes);
+
+ if (pdfFile == null) {
+ pdfFile = File.createTempFile("pdf", null, context.getCacheDir());
+
+ // Copy resource to file so that we can open it as a ParcelFileDescriptor
+ try (OutputStream os = new BufferedOutputStream(new FileOutputStream(pdfFile))) {
+ try (InputStream is = new BufferedInputStream(
+ context.getResources().openRawResource(docRes))) {
+ byte buffer[] = new byte[1024];
+
+ while (true) {
+ int numRead = is.read(buffer, 0, buffer.length);
+
+ if (numRead == -1) {
+ break;
+ }
+
+ os.write(Arrays.copyOf(buffer, numRead));
+ }
+
+ os.flush();
+ }
+ }
+
+ sFiles.put(docRes, pdfFile);
+ }
+
+ return new PdfRenderer(
+ ParcelFileDescriptor.open(pdfFile, ParcelFileDescriptor.MODE_READ_ONLY));
+ }
+
+ /**
+ * Render a pdf onto a bitmap <u>while</u> applying the transformation <u>in the</u>
+ * PDFRenderer. Hence use PdfRenderer.*'s translation and clipping methods.
+ *
+ * @param bmWidth The width of the destination bitmap
+ * @param bmHeight The height of the destination bitmap
+ * @param docRes The resolution of the doc
+ * @param clipping The clipping for the PDF document
+ * @param transformation The transformation of the PDF
+ * @param renderMode The render mode to use to render the PDF
+ * @param context The context to use for creating the renderer
+ *
+ * @return The rendered bitmap
+ */
+ static @NonNull Bitmap renderWithTransform(int bmWidth, int bmHeight, @RawRes int docRes,
+ @Nullable Rect clipping, @Nullable Matrix transformation, int renderMode,
+ @NonNull Context context)
+ throws IOException {
+ try (PdfRenderer renderer = createRenderer(docRes, context)) {
+ try (PdfRenderer.Page page = renderer.openPage(0)) {
+ Bitmap bm = Bitmap.createBitmap(bmWidth, bmHeight, Bitmap.Config.ARGB_8888);
+
+ page.render(bm, clipping, transformation, renderMode);
+
+ return bm;
+ }
+ }
+ }
+
+ /**
+ * Render a pdf onto a bitmap <u>and then</u> apply then render the resulting bitmap onto
+ * another bitmap while applying the transformation. Hence use canvas' translation and clipping
+ * methods.
+ *
+ * @param bmWidth The width of the destination bitmap
+ * @param bmHeight The height of the destination bitmap
+ * @param docRes The resolution of the doc
+ * @param clipping The clipping for the PDF document
+ * @param transformation The transformation of the PDF
+ * @param renderMode The render mode to use to render the PDF
+ * @param context The context to use for creating the renderer
+ *
+ * @return The rendered bitmap
+ */
+ private static @NonNull Bitmap renderAndThenTransform(int bmWidth, int bmHeight,
+ @RawRes int docRes, @Nullable Rect clipping, @Nullable Matrix transformation,
+ int renderMode, @NonNull Context context) throws IOException {
+ Bitmap renderedBm;
+
+ renderedBm = sRenderedBitmaps.get(docRes);
+
+ if (renderedBm == null) {
+ try (PdfRenderer renderer = Utils.createRenderer(docRes, context)) {
+ try (PdfRenderer.Page page = renderer.openPage(0)) {
+ renderedBm = Bitmap.createBitmap(page.getWidth(), page.getHeight(),
+ Bitmap.Config.ARGB_8888);
+ page.render(renderedBm, null, null, renderMode);
+ }
+ }
+ sRenderedBitmaps.put(docRes, renderedBm);
+ }
+
+ if (transformation == null) {
+ // According to PdfRenderer.page#render transformation == null means that the bitmap
+ // should be stretched to clipping (if provided) or otherwise destination size
+ transformation = new Matrix();
+
+ if (clipping != null) {
+ transformation.postScale((float) clipping.width() / renderedBm.getWidth(),
+ (float) clipping.height() / renderedBm.getHeight());
+ transformation.postTranslate(clipping.left, clipping.top);
+ } else {
+ transformation.postScale((float) bmWidth / renderedBm.getWidth(),
+ (float) bmHeight / renderedBm.getHeight());
+ }
+ }
+
+ Bitmap transformedBm = Bitmap.createBitmap(bmWidth, bmHeight, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(transformedBm);
+ canvas.drawBitmap(renderedBm, transformation, null);
+
+ Bitmap clippedBm;
+ if (clipping != null) {
+ clippedBm = Bitmap.createBitmap(bmWidth, bmHeight, Bitmap.Config.ARGB_8888);
+ canvas = new Canvas(clippedBm);
+ canvas.drawBitmap(transformedBm, clipping, clipping, null);
+ transformedBm.recycle();
+ } else {
+ clippedBm = transformedBm;
+ }
+
+ return clippedBm;
+ }
+
+ /**
+ * Get the fraction of non-matching pixels of two bitmaps. 1 == no pixels match, 0 == all pixels
+ * match.
+ *
+ * @param a The first bitmap
+ * @param b The second bitmap
+ *
+ * @return The fraction of non-matching pixels.
+ */
+ private static @FloatRange(from = 0, to = 1) float getNonMatching(@NonNull Bitmap a,
+ @NonNull Bitmap b) {
+ if (a.getWidth() != b.getWidth() || a.getHeight() != b.getHeight()) {
+ return 1;
+ }
+
+ int[] aPx = new int[a.getWidth() * a.getHeight()];
+ int[] bPx = new int[b.getWidth() * b.getHeight()];
+ a.getPixels(aPx, 0, a.getWidth(), 0, 0, a.getWidth(), a.getHeight());
+ b.getPixels(bPx, 0, b.getWidth(), 0, 0, b.getWidth(), b.getHeight());
+
+ int badPixels = 0;
+ for (int i = 0; i < a.getWidth() * a.getHeight(); i++) {
+ if (aPx[i] != bPx[i]) {
+ badPixels++;
+ }
+ }
+
+ return ((float) badPixels) / (a.getWidth() * a.getHeight());
+ }
+
+ /**
+ * Render the PDF two times. Once with applying the transformation and clipping in the {@link
+ * PdfRenderer}. The other time render the PDF onto a bitmap and then clip and transform that
+ * image. The result should be the same beside some minor aliasing.
+ *
+ * @param width The width of the resulting bitmap
+ * @param height The height of the resulting bitmap
+ * @param docRes The resource of the PDF document
+ * @param clipping The clipping to apply
+ * @param transformation The transformation to apply
+ * @param renderMode The render mode to use
+ * @param context The context to use for creating the renderer
+ *
+ * @throws IOException
+ */
+ static void renderAndCompare(int width, int height, @RawRes int docRes,
+ @Nullable Rect clipping, @Nullable Matrix transformation, int renderMode,
+ @NonNull Context context) throws IOException {
+ Bitmap a = renderWithTransform(width, height, docRes, clipping, transformation,
+ renderMode, context);
+ Bitmap b = renderAndThenTransform(width, height, docRes, clipping, transformation,
+ renderMode, context);
+
+ try {
+ // We allow 1% aliasing error
+ float nonMatching = getNonMatching(a, b);
+
+ if (nonMatching == 0) {
+ Log.d(LOG_TAG, "bitmaps match");
+ } else if (nonMatching > 0.01) {
+ fail("Testing width:" + width + ", height:" + height + ", docRes:" + docRes +
+ ", clipping:" + clipping + ", transform:" + transformation + ". Bitmaps " +
+ "differ by " + Math.ceil(nonMatching * 10000) / 100 +
+ "%. That is too much.");
+ } else {
+ Log.d(LOG_TAG, "bitmaps differ by " + Math.ceil(nonMatching * 10000) / 100 + "%");
+ }
+ } finally {
+ a.recycle();
+ b.recycle();
+ }
+ }
+
+ /**
+ * Run a runnable and expect and exception of a certain type.
+ *
+ * @param r The {@link Invokable} to run
+ * @param expectedClass The expected exception type
+ */
+ static void assertException(@NonNull Invokable r,
+ @NonNull Class<? extends Exception> expectedClass) {
+ try {
+ r.run();
+ } catch (Exception e) {
+ if (e.getClass().isAssignableFrom(expectedClass)) {
+ return;
+ } else {
+ Log.e(LOG_TAG, "Incorrect exception", e);
+ throw new AssertionError("Expected: " + expectedClass.getName() + ", got: "
+ + e.getClass().getName());
+ }
+ }
+
+ throw new AssertionError("No exception thrown");
+ }
+
+ /**
+ * A runnable that can throw an exception.
+ */
+ interface Invokable {
+ void run() throws Exception;
+ }
+}
diff --git a/tests/tests/graphics/src/android/opengl/cts/OpenGlEsVersionTest.java b/tests/tests/graphics/src/android/opengl/cts/OpenGlEsVersionTest.java
index 77c6da1..5a76816 100644
--- a/tests/tests/graphics/src/android/opengl/cts/OpenGlEsVersionTest.java
+++ b/tests/tests/graphics/src/android/opengl/cts/OpenGlEsVersionTest.java
@@ -25,6 +25,7 @@
import android.test.ActivityInstrumentationTestCase2;
import android.util.Log;
+import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.microedition.khronos.egl.EGL10;
@@ -61,20 +62,16 @@
int detectedMajorVersion = getDetectedMajorVersion();
int reportedVersion = getVersionFromActivityManager(mActivity);
- assertEquals("Detected OpenGL ES major version " + detectedMajorVersion
- + " but Activity Manager is reporting " + getMajorVersion(reportedVersion)
- + " (Check ro.opengles.version)",
- detectedMajorVersion, getMajorVersion(reportedVersion));
assertEquals("Reported OpenGL ES version from ActivityManager differs from PackageManager",
reportedVersion, getVersionFromPackageManager(mActivity));
- assertGlVersionString(1);
+ assertGlVersionString(1, 1);
if (detectedMajorVersion == 2) {
restartActivityWithClientVersion(2);
- assertGlVersionString(2);
+ assertGlVersionString(2, getMinorVersion(reportedVersion));
} else if (detectedMajorVersion == 3) {
restartActivityWithClientVersion(3);
- assertGlVersionString(3);
+ assertGlVersionString(3, getMinorVersion(reportedVersion));
}
}
@@ -123,14 +120,11 @@
restartActivityWithClientVersion(3);
String extensions = mActivity.getExtensionsString();
- if (!hasExtension(extensions, "ANDROID_extension_pack_es31a")) {
- assertFalse("FEATURE_OPENGLES_EXTENSION_PACK is available but ANDROID_extension_pack_es31a isn't in the extension list",
- hasAepFeature);
- return;
- }
-
- assertTrue("ANDROID_extension_pack_es31a is present, but support is incomplete",
- mActivity.getAepEs31Support());
+ boolean hasAepExtension = hasExtension(extensions, "GL_ANDROID_extension_pack_es31a");
+ assertEquals("System feature FEATURE_OPENGLES_EXTENSION_PACK is "
+ + (hasAepFeature ? "" : "not ") + "available, but extension GL_ANDROID_extension_pack_es31a is "
+ + (hasAepExtension ? "" : "not ") + "in the OpenGL ES extension list.",
+ hasAepFeature, hasAepExtension);
}
public void testOpenGlEsVersionForVrHighPerformance() throws InterruptedException {
@@ -288,15 +282,21 @@
}
/**
- * Check that the version string has some form of "Open GL ES X.Y" in it where X is the major
- * version and Y must be some digit.
+ * Check that the version string has the form "OpenGL ES(-CM)? (\d+)\.(\d+)", where the two
+ * numbers match the major and minor parameters.
*/
- private void assertGlVersionString(int majorVersion) throws InterruptedException {
- String versionString = "" + majorVersion;
- String message = "OpenGL version string '" + mActivity.getVersionString()
- + "' is not " + majorVersion + ".0+.";
- assertTrue(message, Pattern.matches(".*OpenGL.*ES.*" + versionString + "\\.\\d.*",
- mActivity.getVersionString()));
+ private void assertGlVersionString(int major, int minor) throws InterruptedException {
+ Matcher matcher = Pattern.compile("OpenGL ES(?:-CM)? (\\d+)\\.(\\d+).*")
+ .matcher(mActivity.getVersionString());
+ assertTrue("OpenGL ES version string is not of the required form "
+ + "'OpenGL ES(-CM)? (\\d+)\\.(\\d+).*'",
+ matcher.matches());
+ int stringMajor = Integer.parseInt(matcher.group(1));
+ int stringMinor = Integer.parseInt(matcher.group(2));
+ assertEquals("GL_VERSION string doesn't match ActivityManager major version (check ro.opengles.version property)",
+ major, stringMajor);
+ assertEquals("GL_VERSION string doesn't match ActivityManager minor version (check ro.opengles.version property)",
+ minor, stringMinor);
}
/** Restart {@link GLSurfaceViewCtsActivity} with a specific client version. */
diff --git a/tests/tests/icu/Android.mk b/tests/tests/icu/Android.mk
index 6f183d5..39d1285 100644
--- a/tests/tests/icu/Android.mk
+++ b/tests/tests/icu/Android.mk
@@ -30,9 +30,12 @@
# The aim of this package is to run tests against the implementation in use by
# the current android system.
+LOCAL_JAVA_LIBRARIES := cts-core-test-runner
+
LOCAL_STATIC_JAVA_LIBRARIES := \
- cts-core-test-runner \
- android-icu4j-tests
+ android-icu4j-tests \
+ ctstestrunner \
+ android-support-test
# Tag this module as a cts test artifact
LOCAL_COMPATIBILITY_SUITE := cts
diff --git a/tests/tests/jni/Android.mk b/tests/tests/jni/Android.mk
index 7a9504d..4a81a85 100644
--- a/tests/tests/jni/Android.mk
+++ b/tests/tests/jni/Android.mk
@@ -30,7 +30,7 @@
# Tag this module as a cts test artifact
LOCAL_COMPATIBILITY_SUITE := cts
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner android-support-test
LOCAL_JNI_SHARED_LIBRARIES := libjnitest libnativehelper_compat_libc++
diff --git a/tests/tests/jni/libjnitest/helper.c b/tests/tests/jni/libjnitest/helper.c
index 8f7f2f8..2281795 100644
--- a/tests/tests/jni/libjnitest/helper.c
+++ b/tests/tests/jni/libjnitest/helper.c
@@ -70,6 +70,7 @@
free(result);
if (newResult == NULL) {
// Shouldn't happen, but deal as gracefully as possible.
+ va_end(args);
return NULL;
}
result = newResult;
diff --git a/tests/tests/jni/libjnitest/macroized_tests.c b/tests/tests/jni/libjnitest/macroized_tests.c
index 76b5481..130b378 100644
--- a/tests/tests/jni/libjnitest/macroized_tests.c
+++ b/tests/tests/jni/libjnitest/macroized_tests.c
@@ -192,6 +192,7 @@
"returnBoolean", "()Z");
if (method == NULL) {
+ va_end(args);
return msg;
}
@@ -240,6 +241,7 @@
break;
}
default: {
+ va_end(args);
return failure("shouldn't happen");
}
}
@@ -295,6 +297,7 @@
"returnByte", "()B");
if (method == NULL) {
+ va_end(args);
return msg;
}
@@ -343,6 +346,7 @@
break;
}
default: {
+ va_end(args);
return failure("shouldn't happen");
}
}
@@ -398,6 +402,7 @@
"returnShort", "()S");
if (method == NULL) {
+ va_end(args);
return msg;
}
@@ -446,6 +451,7 @@
break;
}
default: {
+ va_end(args);
return failure("shouldn't happen");
}
}
@@ -501,6 +507,7 @@
"returnChar", "()C");
if (method == NULL) {
+ va_end(args);
return msg;
}
@@ -549,6 +556,7 @@
break;
}
default: {
+ va_end(args);
return failure("shouldn't happen");
}
}
@@ -604,6 +612,7 @@
"returnInt", "()I");
if (method == NULL) {
+ va_end(args);
return msg;
}
@@ -652,6 +661,7 @@
break;
}
default: {
+ va_end(args);
return failure("shouldn't happen");
}
}
@@ -707,6 +717,7 @@
"returnLong", "()J");
if (method == NULL) {
+ va_end(args);
return msg;
}
@@ -755,6 +766,7 @@
break;
}
default: {
+ va_end(args);
return failure("shouldn't happen");
}
}
@@ -810,6 +822,7 @@
"returnFloat", "()F");
if (method == NULL) {
+ va_end(args);
return msg;
}
@@ -858,6 +871,7 @@
break;
}
default: {
+ va_end(args);
return failure("shouldn't happen");
}
}
@@ -913,6 +927,7 @@
"returnDouble", "()D");
if (method == NULL) {
+ va_end(args);
return msg;
}
@@ -961,6 +976,7 @@
break;
}
default: {
+ va_end(args);
return failure("shouldn't happen");
}
}
@@ -1016,6 +1032,7 @@
"nop", "()V");
if (method == NULL) {
+ va_end(args);
return msg;
}
@@ -1064,6 +1081,7 @@
break;
}
default: {
+ va_end(args);
return failure("shouldn't happen");
}
}
@@ -1119,6 +1137,7 @@
"returnString", "()Ljava/lang/String;");
if (method == NULL) {
+ va_end(args);
return msg;
}
@@ -1171,6 +1190,7 @@
break;
}
default: {
+ va_end(args);
return failure("shouldn't happen");
}
}
@@ -1240,6 +1260,7 @@
"takeOneOfEach", "(DFJICSBZLjava/lang/String;)Z");
if (method == NULL) {
+ va_end(args);
return msg;
}
@@ -1299,6 +1320,7 @@
break;
}
default: {
+ va_end(args);
return failure("shouldn't happen");
}
}
@@ -1361,6 +1383,7 @@
"(IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII)Z");
if (method == NULL) {
+ va_end(args);
return msg;
}
@@ -1441,6 +1464,7 @@
break;
}
default: {
+ va_end(args);
return failure("shouldn't happen");
}
}
diff --git a/tests/tests/keystore/Android.mk b/tests/tests/keystore/Android.mk
index 4688846..8fe7a3b 100644
--- a/tests/tests/keystore/Android.mk
+++ b/tests/tests/keystore/Android.mk
@@ -26,6 +26,7 @@
LOCAL_JAVA_LIBRARIES := bouncycastle
LOCAL_STATIC_JAVA_LIBRARIES := \
+ android-support-test \
core-tests-support \
ctsdeviceutil \
ctstestrunner \
diff --git a/tests/tests/media/Android.mk b/tests/tests/media/Android.mk
index eda78e8..d61556d 100644
--- a/tests/tests/media/Android.mk
+++ b/tests/tests/media/Android.mk
@@ -44,7 +44,7 @@
# include both the 32 and 64 bit versions
LOCAL_MULTILIB := both
-LOCAL_STATIC_JAVA_LIBRARIES := ctsmediautil ctsdeviceutil compatibility-device-util ctstestserver ctstestrunner ndkaudio
+LOCAL_STATIC_JAVA_LIBRARIES := ctsmediautil ctsdeviceutil compatibility-device-util ctstestserver ctstestrunner ndkaudio android-support-test
LOCAL_JNI_SHARED_LIBRARIES := libctsmediacodec_jni libaudio_jni libnativehelper_compat_libc++ libndkaudioLib
diff --git a/tests/tests/media/src/android/media/cts/DecoderTest.java b/tests/tests/media/src/android/media/cts/DecoderTest.java
index 9125f87..76d9199 100755
--- a/tests/tests/media/src/android/media/cts/DecoderTest.java
+++ b/tests/tests/media/src/android/media/cts/DecoderTest.java
@@ -2820,6 +2820,32 @@
}
/**
+ * Returns true if there exists a codec supporting the given MIME type that meets the
+ * minimum specification for VR high performance requirements.
+ *
+ * The requirements are as follows:
+ * - At least 243000 blocks per second (where blocks are defined as 16x16 -- note this
+ * is equivalent to 1920x1080@30fps)
+ * - Feature adaptive-playback present
+ */
+ private static boolean doesMimeTypeHaveMinimumSpecVrReadyCodec(String mimeType) {
+ List<CodecCapabilities> caps = getCodecCapabilitiesForMimeType(mimeType);
+ for (CodecCapabilities c : caps) {
+ if (!c.isFeatureSupported(CodecCapabilities.FEATURE_AdaptivePlayback)) {
+ continue;
+ }
+
+ if (!c.getVideoCapabilities().areSizeAndRateSupported(1920, 1080, 30.0)) {
+ continue;
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
* Returns true if there exists a codec supporting the given MIME type that meets VR high
* performance requirements.
*
@@ -2939,6 +2965,13 @@
return;
}
+ // Test minimum mandatory requirements.
+ assertTrue(doesMimeTypeHaveMinimumSpecVrReadyCodec(MediaFormat.MIMETYPE_VIDEO_HEVC));
+ decodeInParallel(
+ // using the 60fps sample to save on apk size, but decoding only at 30fps @ 5Mbps
+ R.raw.bbb_s2_1920x1080_mp4_hevc_mp41_10mbps_60fps_aac_lc_6ch_384kbps_22050hz,
+ 300, 30 /* fps */, 1);
+
boolean hevcIsReady = doesMimeTypeHaveVrReadyCodec(MediaFormat.MIMETYPE_VIDEO_HEVC);
if (!hevcIsReady) {
MediaUtils.skipTest(TAG, "HEVC isn't required to be VR ready");
@@ -2958,6 +2991,13 @@
return;
}
+ // Test minimum mandatory requirements.
+ assertTrue(doesMimeTypeHaveMinimumSpecVrReadyCodec(MediaFormat.MIMETYPE_VIDEO_VP9));
+ decodeInParallel(
+ // using the 60fps sample to save on apk size, but decoding only at 30fps @ 5Mbps
+ R.raw.bbb_s2_1920x1080_webm_vp9_0p41_10mbps_60fps_vorbis_6ch_384kbps_22050hz,
+ 300, 30 /* fps */, 1);
+
boolean vp9IsReady = doesMimeTypeHaveVrReadyCodec(MediaFormat.MIMETYPE_VIDEO_VP9);
if (!vp9IsReady) {
MediaUtils.skipTest(TAG, "VP9 isn't required to be VR ready");
diff --git a/tests/tests/media/src/android/media/cts/IvfWriter.java b/tests/tests/media/src/android/media/cts/IvfWriter.java
index 075f73c..36fb679 100644
--- a/tests/tests/media/src/android/media/cts/IvfWriter.java
+++ b/tests/tests/media/src/android/media/cts/IvfWriter.java
@@ -16,6 +16,8 @@
package android.media.cts;
+import android.media.MediaFormat;
+
import java.io.IOException;
import java.io.RandomAccessFile;
@@ -34,6 +36,7 @@
private int mScale;
private int mRate;
private int mFrameCount;
+ private String mMimeType;
/**
* Initializes the IVF file writer.
@@ -43,15 +46,17 @@
* with this timebase value.
*
* @param filename name of the IVF file
+ * @param mimeType mime type of the codec
* @param width frame width
* @param height frame height
* @param scale timebase scale (or numerator of the timebase fraction)
* @param rate timebase rate (or denominator of the timebase fraction)
*/
- public IvfWriter(String filename,
- int width, int height,
- int scale, int rate) throws IOException {
+ public IvfWriter(
+ String filename, String mimeType, int width, int height, int scale,
+ int rate) throws IOException {
mOutputFile = new RandomAccessFile(filename, "rw");
+ mMimeType = mimeType;
mWidth = width;
mHeight = height;
mScale = scale;
@@ -67,11 +72,12 @@
* Microsecond timebase is default for OMX thus stagefright.
*
* @param filename name of the IVF file
+ * @param mimeType mime type of the codec
* @param width frame width
* @param height frame height
*/
- public IvfWriter(String filename, int width, int height) throws IOException {
- this(filename, width, height, 1, 1000000);
+ public IvfWriter(String filename, String mimeType, int width, int height) throws IOException {
+ this(filename, mimeType, width, height, 1, 1000000);
}
/**
@@ -80,7 +86,7 @@
public void close() throws IOException{
// Write header now
mOutputFile.seek(0);
- mOutputFile.write(makeIvfHeader(mFrameCount, mWidth, mHeight, mScale, mRate));
+ mOutputFile.write(makeIvfHeader(mFrameCount, mWidth, mHeight, mScale, mRate, mMimeType));
mOutputFile.close();
}
@@ -107,7 +113,8 @@
* @param scale timebase scale (or numerator of the timebase fraction)
* @param rate timebase rate (or denominator of the timebase fraction)
*/
- private static byte[] makeIvfHeader(int frameCount, int width, int height, int scale, int rate){
+ private static byte[] makeIvfHeader(
+ int frameCount, int width, int height, int scale, int rate, String mimeType) {
byte[] ivfHeader = new byte[32];
ivfHeader[0] = 'D';
ivfHeader[1] = 'K';
@@ -117,7 +124,7 @@
lay16Bits(ivfHeader, 6, 32); // header size
ivfHeader[8] = 'V'; // fourcc
ivfHeader[9] = 'P';
- ivfHeader[10] = '8';
+ ivfHeader[10] = (byte) (MediaFormat.MIMETYPE_VIDEO_VP8.equals(mimeType) ? '8' : '9');
ivfHeader[11] = '0';
lay16Bits(ivfHeader, 12, width);
lay16Bits(ivfHeader, 14, height);
diff --git a/tests/tests/media/src/android/media/cts/MediaBrowserTest.java b/tests/tests/media/src/android/media/cts/MediaBrowserTest.java
index 23f464e..56e2a6f 100644
--- a/tests/tests/media/src/android/media/cts/MediaBrowserTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaBrowserTest.java
@@ -22,6 +22,7 @@
import android.os.Bundle;
import android.test.InstrumentationTestCase;
+import java.util.ArrayList;
import java.util.List;
/**
@@ -30,6 +31,17 @@
public class MediaBrowserTest extends InstrumentationTestCase {
// The maximum time to wait for an operation.
private static final long TIME_OUT_MS = 3000L;
+
+ /**
+ * To check {@link MediaBrowser#unsubscribe} works properly,
+ * we notify to the browser after the unsubscription that the media items have changed.
+ * Then {@link MediaBrowser.SubscriptionCallback#onChildrenLoaded} should not be called.
+ *
+ * The measured time from calling {@link StubMediaBrowserService#notifyChildrenChanged}
+ * to {@link MediaBrowser.SubscriptionCallback#onChildrenLoaded} being called is about 50ms.
+ * So we make the thread sleep for 100ms to properly check that the callback is not called.
+ */
+ private static final long SLEEP_MS = 100L;
private static final ComponentName TEST_BROWSER_SERVICE = new ComponentName(
"android.media.cts", "android.media.cts.StubMediaBrowserService");
private static final ComponentName TEST_INVALID_BROWSER_SERVICE = new ComponentName(
@@ -56,7 +68,12 @@
mMediaBrowser.getSessionToken());
mMediaBrowser.disconnect();
- assertEquals(false, mMediaBrowser.isConnected());
+ new PollingCheck(TIME_OUT_MS) {
+ @Override
+ protected boolean check() {
+ return !mMediaBrowser.isConnected();
+ }
+ }.run();
}
public void testConnectTwice() {
@@ -116,6 +133,21 @@
assertEquals(StubMediaBrowserService.MEDIA_ID_CHILDREN[i],
mSubscriptionCallback.mLastChildMediaItems.get(i).getMediaId());
}
+
+ // Test unsubscribe.
+ resetCallbacks();
+ mMediaBrowser.unsubscribe(StubMediaBrowserService.MEDIA_ID_ROOT);
+
+ // After unsubscribing, make StubMediaBrowserService notify that the children are changed.
+ StubMediaBrowserService.sInstance.notifyChildrenChanged(
+ StubMediaBrowserService.MEDIA_ID_ROOT);
+ try {
+ Thread.sleep(SLEEP_MS);
+ } catch (InterruptedException e) {
+ fail("Unexpected InterruptedException occurred.");
+ }
+ // onChildrenLoaded should not be called.
+ assertEquals(0, mSubscriptionCallback.mChildrenLoadedCount);
}
public void testSubscribeWithOptions() {
@@ -150,6 +182,21 @@
mSubscriptionCallback.mLastChildMediaItems.get(i).getMediaId());
}
}
+
+ // Test unsubscribe with callback argument.
+ resetCallbacks();
+ mMediaBrowser.unsubscribe(StubMediaBrowserService.MEDIA_ID_ROOT, mSubscriptionCallback);
+
+ // After unsubscribing, make StubMediaBrowserService notify that the children are changed.
+ StubMediaBrowserService.sInstance.notifyChildrenChanged(
+ StubMediaBrowserService.MEDIA_ID_ROOT);
+ try {
+ Thread.sleep(SLEEP_MS);
+ } catch (InterruptedException e) {
+ fail("Unexpected InterruptedException occurred.");
+ }
+ // onChildrenLoaded should not be called.
+ assertEquals(0, mSubscriptionCallback.mChildrenLoadedCount);
}
public void testSubscribeInvalidItem() {
@@ -192,6 +239,111 @@
mSubscriptionCallback.mLastOptions.getInt(MediaBrowser.EXTRA_PAGE_SIZE));
}
+ public void testUnsubscribeForMultipleSubscriptions() {
+ createMediaBrowser(TEST_BROWSER_SERVICE);
+ connectMediaBrowserService();
+ final List<StubSubscriptionCallback> subscriptionCallbacks = new ArrayList<>();
+ final int pageSize = 1;
+
+ // Subscribe four pages, one item per page.
+ for (int page = 0; page < 4; page++) {
+ final StubSubscriptionCallback callback = new StubSubscriptionCallback();
+ subscriptionCallbacks.add(callback);
+
+ Bundle options = new Bundle();
+ options.putInt(MediaBrowser.EXTRA_PAGE, page);
+ options.putInt(MediaBrowser.EXTRA_PAGE_SIZE, pageSize);
+ mMediaBrowser.subscribe(StubMediaBrowserService.MEDIA_ID_ROOT, options, callback);
+
+ // Each onChildrenLoaded() must be called.
+ new PollingCheck(TIME_OUT_MS) {
+ @Override
+ protected boolean check() {
+ return callback.mChildrenLoadedWithOptionCount == 1;
+ }
+ }.run();
+ }
+
+ // Reset callbacks and unsubscribe.
+ for (StubSubscriptionCallback callback : subscriptionCallbacks) {
+ callback.reset();
+ }
+ mMediaBrowser.unsubscribe(StubMediaBrowserService.MEDIA_ID_ROOT);
+
+ // After unsubscribing, make StubMediaBrowserService notify that the children are changed.
+ StubMediaBrowserService.sInstance.notifyChildrenChanged(
+ StubMediaBrowserService.MEDIA_ID_ROOT);
+ try {
+ Thread.sleep(SLEEP_MS);
+ } catch (InterruptedException e) {
+ fail("Unexpected InterruptedException occurred.");
+ }
+
+ // onChildrenLoaded should not be called.
+ for (StubSubscriptionCallback callback : subscriptionCallbacks) {
+ assertEquals(0, callback.mChildrenLoadedWithOptionCount);
+ }
+ }
+
+ public void testUnsubscribeWithSubscriptionCallbackForMultipleSubscriptions() {
+ createMediaBrowser(TEST_BROWSER_SERVICE);
+ connectMediaBrowserService();
+ final List<StubSubscriptionCallback> subscriptionCallbacks = new ArrayList<>();
+ final int pageSize = 1;
+
+ // Subscribe four pages, one item per page.
+ for (int page = 0; page < 4; page++) {
+ final StubSubscriptionCallback callback = new StubSubscriptionCallback();
+ subscriptionCallbacks.add(callback);
+
+ Bundle options = new Bundle();
+ options.putInt(MediaBrowser.EXTRA_PAGE, page);
+ options.putInt(MediaBrowser.EXTRA_PAGE_SIZE, pageSize);
+ mMediaBrowser.subscribe(StubMediaBrowserService.MEDIA_ID_ROOT, options, callback);
+
+ // Each onChildrenLoaded() must be called.
+ new PollingCheck(TIME_OUT_MS) {
+ @Override
+ protected boolean check() {
+ return callback.mChildrenLoadedWithOptionCount == 1;
+ }
+ }.run();
+ }
+
+ // Unsubscribe existing subscriptions one-by-one.
+ final int[] orderOfRemovingCallbacks = {2, 0, 3, 1};
+ for (int i = 0; i < orderOfRemovingCallbacks.length; i++) {
+ // Reset callbacks
+ for (StubSubscriptionCallback callback : subscriptionCallbacks) {
+ callback.reset();
+ }
+
+ // Remove one subscription
+ mMediaBrowser.unsubscribe(StubMediaBrowserService.MEDIA_ID_ROOT,
+ subscriptionCallbacks.get(orderOfRemovingCallbacks[i]));
+
+ // Make StubMediaBrowserService notify that the children are changed.
+ StubMediaBrowserService.sInstance.notifyChildrenChanged(
+ StubMediaBrowserService.MEDIA_ID_ROOT);
+ try {
+ Thread.sleep(SLEEP_MS);
+ } catch (InterruptedException e) {
+ fail("Unexpected InterruptedException occurred.");
+ }
+
+ // Only the remaining subscriptionCallbacks should be called.
+ for (int j = 0; j < 4; j++) {
+ int childrenLoadedWithOptionsCount = subscriptionCallbacks
+ .get(orderOfRemovingCallbacks[j]).mChildrenLoadedWithOptionCount;
+ if (j <= i) {
+ assertEquals(0, childrenLoadedWithOptionsCount);
+ } else {
+ assertEquals(1, childrenLoadedWithOptionsCount);
+ }
+ }
+ }
+ }
+
public void testGetItem() {
resetCallbacks();
createMediaBrowser(TEST_BROWSER_SERVICE);
diff --git a/tests/tests/media/src/android/media/cts/MediaCodecCapabilitiesTest.java b/tests/tests/media/src/android/media/cts/MediaCodecCapabilitiesTest.java
index 446cd3c..3c751a5 100644
--- a/tests/tests/media/src/android/media/cts/MediaCodecCapabilitiesTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaCodecCapabilitiesTest.java
@@ -54,12 +54,8 @@
private static final int TIMEOUT_US = 1000000; // 1 sec
private static final int IFRAME_INTERVAL = 10; // 10 seconds between I-frames
- private final MediaCodecList mRegularCodecs =
- new MediaCodecList(MediaCodecList.REGULAR_CODECS);
private final MediaCodecList mAllCodecs =
new MediaCodecList(MediaCodecList.ALL_CODECS);
- private final MediaCodecInfo[] mRegularInfos =
- mRegularCodecs.getCodecInfos();
private final MediaCodecInfo[] mAllInfos =
mAllCodecs.getCodecInfos();
@@ -388,7 +384,7 @@
// check if there is an adaptive decoder for each
for (String mime : supportedFormats) {
skipped = false;
- // implicit assumption that QVGA video is always valid.
+ // implicit assumption that QCIF video is always valid.
MediaFormat format = MediaFormat.createVideoFormat(mime, 176, 144);
format.setFeatureEnabled(CodecCapabilities.FEATURE_AdaptivePlayback, true);
String codec = mAllCodecs.findDecoderForFormat(format);
@@ -498,7 +494,7 @@
MediaFormat format = null;
try {
codec = MediaCodec.createByCodecName(info.getName());
- // implicit assumption that QVGA video is always valid.
+ // implicit assumption that QCIF video is always valid.
format = createReasonableVideoFormat(caps, mime, isEncoder, 176, 144);
format.setInteger(
MediaFormat.KEY_COLOR_FORMAT,
diff --git a/tests/tests/media/src/android/media/cts/MediaControllerTest.java b/tests/tests/media/src/android/media/cts/MediaControllerTest.java
index b8ec617..a6da2d9 100644
--- a/tests/tests/media/src/android/media/cts/MediaControllerTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaControllerTest.java
@@ -15,6 +15,7 @@
*/
package android.media.cts;
+import android.media.AudioAttributes;
import android.media.AudioManager;
import android.media.Rating;
import android.media.VolumeProvider;
@@ -52,6 +53,22 @@
mController = mSession.getController();
}
+ public void testGetPackageName() {
+ assertEquals(getContext().getPackageName(), mController.getPackageName());
+ }
+
+ public void testGetRatingType() {
+ assertEquals("Default rating type of a session must be Rating.RATING_NONE",
+ Rating.RATING_NONE, mController.getRatingType());
+
+ mSession.setRatingType(Rating.RATING_5_STARS);
+ assertEquals(Rating.RATING_5_STARS, mController.getRatingType());
+ }
+
+ public void testGetSessionToken() throws Exception {
+ assertEquals(mSession.getSessionToken(), mController.getSessionToken());
+ }
+
public void testSendCommand() throws Exception {
synchronized (mWaitLock) {
mCallback.reset();
@@ -243,6 +260,23 @@
}
}
+ public void testPlaybackInfo() {
+ final int playbackType = MediaController.PlaybackInfo.PLAYBACK_TYPE_LOCAL;
+ final int volumeControl = VolumeProvider.VOLUME_CONTROL_ABSOLUTE;
+ final int maxVolume = 10;
+ final int currentVolume = 3;
+
+ AudioAttributes audioAttributes = new AudioAttributes.Builder().build();
+ MediaController.PlaybackInfo info = new MediaController.PlaybackInfo(
+ playbackType, audioAttributes, volumeControl, maxVolume, currentVolume);
+
+ assertEquals(playbackType, info.getPlaybackType());
+ assertEquals(audioAttributes, info.getAudioAttributes());
+ assertEquals(volumeControl, info.getVolumeControl());
+ assertEquals(maxVolume, info.getMaxVolume());
+ assertEquals(currentVolume, info.getCurrentVolume());
+ }
+
private class MediaSessionCallback extends MediaSession.Callback {
private long mSeekPosition;
private long mQueueItemId;
@@ -415,7 +449,7 @@
@Override
public void onCustomAction(String action, Bundle extras) {
synchronized (mWaitLock) {
- mOnCustomActionCalled= true;
+ mOnCustomActionCalled = true;
mAction = action;
mExtras = extras;
mWaitLock.notify();
diff --git a/tests/tests/media/src/android/media/cts/MediaItemTest.java b/tests/tests/media/src/android/media/cts/MediaItemTest.java
index 4eefaa7..dc12b97 100644
--- a/tests/tests/media/src/android/media/cts/MediaItemTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaItemTest.java
@@ -40,6 +40,7 @@
assertEquals(MediaItem.FLAG_BROWSABLE, mediaItem.getFlags());
assertTrue(mediaItem.isBrowsable());
assertFalse(mediaItem.isPlayable());
+ assertEquals(0, mediaItem.describeContents());
// Test writeToParcel
Parcel p = Parcel.obtain();
@@ -62,6 +63,7 @@
assertEquals(MediaItem.FLAG_PLAYABLE, mediaItem.getFlags());
assertFalse(mediaItem.isBrowsable());
assertTrue(mediaItem.isPlayable());
+ assertEquals(0, mediaItem.describeContents());
// Test writeToParcel
Parcel p = Parcel.obtain();
diff --git a/tests/tests/media/src/android/media/cts/MediaRecorderTest.java b/tests/tests/media/src/android/media/cts/MediaRecorderTest.java
index dffb653..25cf0ba 100644
--- a/tests/tests/media/src/android/media/cts/MediaRecorderTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaRecorderTest.java
@@ -449,7 +449,7 @@
}
public void testRecorderAudio() throws Exception {
- if (!hasMicrophone() || !hasAmrNb()) {
+ if (!hasMicrophone() || !hasAac()) {
MediaUtils.skipTest("no audio codecs or microphone");
return;
}
@@ -457,7 +457,7 @@
assertEquals(0, mMediaRecorder.getMaxAmplitude());
mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
mMediaRecorder.setOutputFile(OUTPUT_PATH);
- mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
+ mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
mMediaRecorder.setAudioChannels(AUDIO_NUM_CHANNELS);
mMediaRecorder.setAudioSamplingRate(AUDIO_SAMPLE_RATE_HZ);
mMediaRecorder.setAudioEncodingBitRate(AUDIO_BIT_RATE_IN_BPS);
@@ -465,14 +465,14 @@
}
public void testOnInfoListener() throws Exception {
- if (!hasMicrophone() || !hasAmrNb()) {
+ if (!hasMicrophone() || !hasAac()) {
MediaUtils.skipTest("no audio codecs or microphone");
return;
}
mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
mMediaRecorder.setMaxDuration(MAX_DURATION_MSEC);
- mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
+ mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
mMediaRecorder.prepare();
mMediaRecorder.start();
Thread.sleep(RECORD_TIME_MS);
@@ -480,7 +480,7 @@
}
public void testSetMaxDuration() throws Exception {
- if (!hasMicrophone() || !hasAmrNb()) {
+ if (!hasMicrophone() || !hasAac()) {
MediaUtils.skipTest("no audio codecs or microphone");
return;
}
@@ -491,7 +491,7 @@
mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
mMediaRecorder.setMaxDuration((int)durationMs);
- mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
+ mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
mMediaRecorder.prepare();
mMediaRecorder.start();
long startTimeMs = System.currentTimeMillis();
@@ -556,13 +556,13 @@
}
public void testOnErrorListener() throws Exception {
- if (!hasMicrophone() || !hasAmrNb()) {
+ if (!hasMicrophone() || !hasAac()) {
MediaUtils.skipTest("no audio codecs or microphone");
return;
}
mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT);
mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
- mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
+ mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
recordMedia(MAX_FILE_SIZE, mOutFile);
// TODO: how can we trigger a recording error?
diff --git a/tests/tests/media/src/android/media/cts/MediaSessionTest.java b/tests/tests/media/src/android/media/cts/MediaSessionTest.java
index 3ebe6e4..4ed6a78 100644
--- a/tests/tests/media/src/android/media/cts/MediaSessionTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaSessionTest.java
@@ -16,6 +16,7 @@
package android.media.cts;
import android.app.PendingIntent;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.media.AudioAttributes;
@@ -31,9 +32,9 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
-import android.os.Message;
import android.os.Parcel;
import android.test.AndroidTestCase;
+import android.view.KeyEvent;
import java.util.ArrayList;
import java.util.List;
@@ -230,7 +231,7 @@
}
/**
- * Tests for setPlaybackToLocal and setPlaybackToRemote.
+ * Test {@link MediaSession#setPlaybackToLocal} and {@link MediaSession#setPlaybackToRemote}.
*/
public void testPlaybackToLocalAndRemote() throws Exception {
MediaController controller = mSession.getController();
@@ -291,7 +292,91 @@
}
/**
- * Tests MediaSession.QueueItem.
+ * Test {@link MediaSession.Callback#onMediaButtonEvent}.
+ */
+ public void testCallbackOnMediaButtonEvent() throws Exception {
+ MediaSessionCallback sessionCallback = new MediaSessionCallback();
+ mSession.setCallback(sessionCallback, new Handler(Looper.getMainLooper()));
+ mSession.setFlags(MediaSession.FLAG_HANDLES_MEDIA_BUTTONS);
+ mSession.setActive(true);
+
+ Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON).setComponent(
+ new ComponentName(getContext(), getContext().getClass()));
+ PendingIntent pi = PendingIntent.getBroadcast(getContext(), 0, mediaButtonIntent, 0);
+ mSession.setMediaButtonReceiver(pi);
+
+ long supportedActions = PlaybackState.ACTION_PLAY | PlaybackState.ACTION_PAUSE
+ | PlaybackState.ACTION_PLAY_PAUSE | PlaybackState.ACTION_STOP
+ | PlaybackState.ACTION_SKIP_TO_NEXT | PlaybackState.ACTION_SKIP_TO_PREVIOUS
+ | PlaybackState.ACTION_FAST_FORWARD | PlaybackState.ACTION_REWIND;
+
+ // Set state to STATE_PLAYING to get higher priority.
+ PlaybackState defaultState = new PlaybackState.Builder().setActions(supportedActions)
+ .setState(PlaybackState.STATE_PLAYING, 0L, 0.0f).build();
+ mSession.setPlaybackState(defaultState);
+
+ synchronized (mWaitLock) {
+ sessionCallback.reset();
+ simulateMediaKeyInput(KeyEvent.KEYCODE_MEDIA_PLAY);
+ mWaitLock.wait(TIME_OUT_MS);
+ assertTrue(sessionCallback.mOnPlayCalled);
+
+ sessionCallback.reset();
+ simulateMediaKeyInput(KeyEvent.KEYCODE_MEDIA_PAUSE);
+ mWaitLock.wait(TIME_OUT_MS);
+ assertTrue(sessionCallback.mOnPauseCalled);
+
+ sessionCallback.reset();
+ simulateMediaKeyInput(KeyEvent.KEYCODE_MEDIA_NEXT);
+ mWaitLock.wait(TIME_OUT_MS);
+ assertTrue(sessionCallback.mOnSkipToNextCalled);
+
+ sessionCallback.reset();
+ simulateMediaKeyInput(KeyEvent.KEYCODE_MEDIA_PREVIOUS);
+ mWaitLock.wait(TIME_OUT_MS);
+ assertTrue(sessionCallback.mOnSkipToPreviousCalled);
+
+ sessionCallback.reset();
+ simulateMediaKeyInput(KeyEvent.KEYCODE_MEDIA_STOP);
+ mWaitLock.wait(TIME_OUT_MS);
+ assertTrue(sessionCallback.mOnStopCalled);
+
+ sessionCallback.reset();
+ simulateMediaKeyInput(KeyEvent.KEYCODE_MEDIA_FAST_FORWARD);
+ mWaitLock.wait(TIME_OUT_MS);
+ assertTrue(sessionCallback.mOnFastForwardCalled);
+
+ sessionCallback.reset();
+ simulateMediaKeyInput(KeyEvent.KEYCODE_MEDIA_REWIND);
+ mWaitLock.wait(TIME_OUT_MS);
+ assertTrue(sessionCallback.mOnRewindCalled);
+
+ // Test PLAY_PAUSE button twice.
+ // First, simulate PLAY_PAUSE button while in STATE_PAUSED.
+ sessionCallback.reset();
+ mSession.setPlaybackState(new PlaybackState.Builder().setActions(supportedActions)
+ .setState(PlaybackState.STATE_PAUSED, 0L, 0.0f).build());
+ simulateMediaKeyInput(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE);
+ mWaitLock.wait(TIME_OUT_MS);
+ assertTrue(sessionCallback.mOnPlayCalled);
+
+ // Next, simulate PLAY_PAUSE button while in STATE_PLAYING.
+ sessionCallback.reset();
+ mSession.setPlaybackState(new PlaybackState.Builder().setActions(supportedActions)
+ .setState(PlaybackState.STATE_PLAYING, 0L, 0.0f).build());
+ simulateMediaKeyInput(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE);
+ mWaitLock.wait(TIME_OUT_MS);
+ assertTrue(sessionCallback.mOnPauseCalled);
+ }
+ }
+
+ private void simulateMediaKeyInput(int keyCode) {
+ mAudioManager.dispatchMediaKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, keyCode));
+ mAudioManager.dispatchMediaKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, keyCode));
+ }
+
+ /**
+ * Tests {@link MediaSession.QueueItem}.
*/
public void testQueueItem() {
QueueItem item = new QueueItem(new MediaDescription.Builder()
@@ -299,6 +384,7 @@
assertEquals(TEST_QUEUE_ID, item.getQueueId());
assertEquals("media-id", item.getDescription().getMediaId());
assertEquals("title", item.getDescription().getTitle());
+ assertEquals(0, item.describeContents());
Parcel p = Parcel.obtain();
item.writeToParcel(p, 0);
@@ -448,4 +534,80 @@
}
}
}
+
+ private class MediaSessionCallback extends MediaSession.Callback {
+ private boolean mOnPlayCalled;
+ private boolean mOnPauseCalled;
+ private boolean mOnStopCalled;
+ private boolean mOnFastForwardCalled;
+ private boolean mOnRewindCalled;
+ private boolean mOnSkipToPreviousCalled;
+ private boolean mOnSkipToNextCalled;
+
+ public void reset() {
+ mOnPlayCalled = false;
+ mOnPauseCalled = false;
+ mOnStopCalled = false;
+ mOnFastForwardCalled = false;
+ mOnRewindCalled = false;
+ mOnSkipToPreviousCalled = false;
+ mOnSkipToNextCalled = false;
+ }
+
+ @Override
+ public void onPlay() {
+ synchronized (mWaitLock) {
+ mOnPlayCalled = true;
+ mWaitLock.notify();
+ }
+ }
+
+ @Override
+ public void onPause() {
+ synchronized (mWaitLock) {
+ mOnPauseCalled = true;
+ mWaitLock.notify();
+ }
+ }
+
+ @Override
+ public void onStop() {
+ synchronized (mWaitLock) {
+ mOnStopCalled = true;
+ mWaitLock.notify();
+ }
+ }
+
+ @Override
+ public void onFastForward() {
+ synchronized (mWaitLock) {
+ mOnFastForwardCalled = true;
+ mWaitLock.notify();
+ }
+ }
+
+ @Override
+ public void onRewind() {
+ synchronized (mWaitLock) {
+ mOnRewindCalled = true;
+ mWaitLock.notify();
+ }
+ }
+
+ @Override
+ public void onSkipToPrevious() {
+ synchronized (mWaitLock) {
+ mOnSkipToPreviousCalled = true;
+ mWaitLock.notify();
+ }
+ }
+
+ @Override
+ public void onSkipToNext() {
+ synchronized (mWaitLock) {
+ mOnSkipToNextCalled = true;
+ mWaitLock.notify();
+ }
+ }
+ }
}
diff --git a/tests/tests/media/src/android/media/cts/PlaybackStateTest.java b/tests/tests/media/src/android/media/cts/PlaybackStateTest.java
new file mode 100644
index 0000000..ba96784
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/PlaybackStateTest.java
@@ -0,0 +1,263 @@
+/*
+ * 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.media.cts;
+
+import android.media.session.MediaSession;
+import android.media.session.PlaybackState;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.test.AndroidTestCase;
+
+import java.util.ArrayList;
+
+/**
+ * Test {@link android.media.session.PlaybackState}.
+ */
+public class PlaybackStateTest extends AndroidTestCase {
+
+ private static final long TEST_POSITION = 20000L;
+ private static final long TEST_BUFFERED_POSITION = 15000L;
+ private static final long TEST_UPDATE_TIME = 100000L;
+ private static final long TEST_ACTIONS =
+ PlaybackState.ACTION_PLAY | PlaybackState.ACTION_STOP | PlaybackState.ACTION_SEEK_TO;
+ private static final long TEST_QUEUE_ITEM_ID = 23L;
+ private static final float TEST_PLAYBACK_SPEED = 3.0f;
+ private static final float TEST_PLAYBACK_SPEED_ON_REWIND = -2.0f;
+ private static final float DELTA = 1e-7f;
+
+ private static final String TEST_ERROR_MSG = "test-error-msg";
+ private static final String TEST_CUSTOM_ACTION = "test-custom-action";
+ private static final String TEST_CUSTOM_ACTION_NAME = "test-custom-action-name";
+ private static final int TEST_ICON_RESOURCE_ID = android.R.drawable.ic_media_next;
+
+ private static final String EXTRAS_KEY = "test-key";
+ private static final String EXTRAS_VALUE = "test-value";
+
+ /**
+ * Test default values of {@link PlaybackState}.
+ */
+ public void testBuilder() {
+ PlaybackState state = new PlaybackState.Builder().build();
+
+ assertEquals(new ArrayList<PlaybackState.CustomAction>(), state.getCustomActions());
+ assertEquals(0, state.getState());
+ assertEquals(0L, state.getPosition());
+ assertEquals(0L, state.getBufferedPosition());
+ assertEquals(0.0f, state.getPlaybackSpeed(), DELTA);
+ assertEquals(0L, state.getActions());
+ assertNull(state.getErrorMessage());
+ assertEquals(0L, state.getLastPositionUpdateTime());
+ assertEquals(MediaSession.QueueItem.UNKNOWN_ID, state.getActiveQueueItemId());
+ assertNull(state.getExtras());
+ }
+
+ /**
+ * Test following setter methods of {@link PlaybackState.Builder}:
+ * {@link PlaybackState.Builder#setState(int, long, float)}
+ * {@link PlaybackState.Builder#setActions(long)}
+ * {@link PlaybackState.Builder#setActiveQueueItemId(long)}
+ * {@link PlaybackState.Builder#setBufferedPosition(long)}
+ * {@link PlaybackState.Builder#setErrorMessage(CharSequence)}
+ * {@link PlaybackState.Builder#setExtras(Bundle)}
+ */
+ public void testBuilder_setterMethods() {
+ Bundle extras = new Bundle();
+ extras.putString(EXTRAS_KEY, EXTRAS_VALUE);
+
+ PlaybackState state = new PlaybackState.Builder()
+ .setState(PlaybackState.STATE_PLAYING, TEST_POSITION, TEST_PLAYBACK_SPEED)
+ .setActions(TEST_ACTIONS)
+ .setActiveQueueItemId(TEST_QUEUE_ITEM_ID)
+ .setBufferedPosition(TEST_BUFFERED_POSITION)
+ .setErrorMessage(TEST_ERROR_MSG)
+ .setExtras(extras)
+ .build();
+ assertEquals(PlaybackState.STATE_PLAYING, state.getState());
+ assertEquals(TEST_POSITION, state.getPosition());
+ assertEquals(TEST_PLAYBACK_SPEED, state.getPlaybackSpeed(), DELTA);
+ assertEquals(TEST_ACTIONS, state.getActions());
+ assertEquals(TEST_QUEUE_ITEM_ID, state.getActiveQueueItemId());
+ assertEquals(TEST_BUFFERED_POSITION, state.getBufferedPosition());
+ assertEquals(TEST_ERROR_MSG, state.getErrorMessage().toString());
+ assertEquals(EXTRAS_VALUE, state.getExtras().get(EXTRAS_KEY));
+ }
+
+ /**
+ * Test {@link PlaybackState.Builder#setState(int, long, float, long)}.
+ */
+ public void testBuilder_setStateWithUpdateTime() {
+ PlaybackState state = new PlaybackState.Builder().setState(
+ PlaybackState.STATE_REWINDING, TEST_POSITION,
+ TEST_PLAYBACK_SPEED_ON_REWIND, TEST_UPDATE_TIME).build();
+ assertEquals(PlaybackState.STATE_REWINDING, state.getState());
+ assertEquals(TEST_POSITION, state.getPosition());
+ assertEquals(TEST_PLAYBACK_SPEED_ON_REWIND, state.getPlaybackSpeed(), DELTA);
+ assertEquals(TEST_UPDATE_TIME, state.getLastPositionUpdateTime());
+ }
+
+ /**
+ * Test {@link PlaybackState.Builder#addCustomAction(String, String, int)}.
+ */
+ public void testBuilder_addCustomAction() {
+ ArrayList<PlaybackState.CustomAction> actions = new ArrayList<>();
+ PlaybackState.Builder builder = new PlaybackState.Builder();
+
+ for (int i = 0; i < 5; i++) {
+ actions.add(new PlaybackState.CustomAction.Builder(
+ TEST_CUSTOM_ACTION + i, TEST_CUSTOM_ACTION_NAME + i, TEST_ICON_RESOURCE_ID + i)
+ .build());
+ builder.addCustomAction(
+ TEST_CUSTOM_ACTION + i, TEST_CUSTOM_ACTION_NAME + i, TEST_ICON_RESOURCE_ID + i);
+ }
+
+ PlaybackState state = builder.build();
+ assertEquals(actions.size(), state.getCustomActions().size());
+ for (int i = 0; i < actions.size(); i++) {
+ assertCustomActionEquals(actions.get(i), state.getCustomActions().get(i));
+ }
+ }
+
+ /**
+ * Test {@link PlaybackState.Builder#addCustomAction(PlaybackState.CustomAction)}.
+ */
+ public void testBuilder_addCustomActionWithCustomActionObject() {
+ Bundle extras = new Bundle();
+ extras.putString(EXTRAS_KEY, EXTRAS_VALUE);
+
+ ArrayList<PlaybackState.CustomAction> actions = new ArrayList<>();
+ PlaybackState.Builder builder = new PlaybackState.Builder();
+
+ for (int i = 0; i < 5; i++) {
+ actions.add(new PlaybackState.CustomAction.Builder(
+ TEST_CUSTOM_ACTION + i, TEST_CUSTOM_ACTION_NAME + i, TEST_ICON_RESOURCE_ID + i)
+ .setExtras(extras)
+ .build());
+ builder.addCustomAction(new PlaybackState.CustomAction.Builder(
+ TEST_CUSTOM_ACTION + i, TEST_CUSTOM_ACTION_NAME + i, TEST_ICON_RESOURCE_ID + i)
+ .setExtras(extras)
+ .build());
+ }
+
+ PlaybackState state = builder.build();
+ assertEquals(actions.size(), state.getCustomActions().size());
+ for (int i = 0; i < actions.size(); i++) {
+ assertCustomActionEquals(actions.get(i), state.getCustomActions().get(i));
+ }
+ }
+
+ /**
+ * Test {@link PlaybackState#writeToParcel(Parcel, int)}.
+ */
+ public void testWriteToParcel() {
+ Bundle extras = new Bundle();
+ extras.putString(EXTRAS_KEY, EXTRAS_VALUE);
+
+ PlaybackState.CustomAction customAction1 = new PlaybackState.CustomAction
+ .Builder(TEST_CUSTOM_ACTION, TEST_CUSTOM_ACTION_NAME, TEST_ICON_RESOURCE_ID)
+ .setExtras(extras)
+ .build();
+
+ PlaybackState.Builder builder =
+ new PlaybackState.Builder().setState(PlaybackState.STATE_CONNECTING, TEST_POSITION,
+ TEST_PLAYBACK_SPEED, TEST_UPDATE_TIME)
+ .setActions(TEST_ACTIONS)
+ .setActiveQueueItemId(TEST_QUEUE_ITEM_ID)
+ .setBufferedPosition(TEST_BUFFERED_POSITION)
+ .setErrorMessage(TEST_ERROR_MSG)
+ .setExtras(extras);
+
+ for (int i = 0; i < 5; i++) {
+ builder.addCustomAction(new PlaybackState.CustomAction.Builder(
+ TEST_CUSTOM_ACTION + i, TEST_CUSTOM_ACTION_NAME + i, TEST_ICON_RESOURCE_ID + i)
+ .setExtras(extras)
+ .build());
+ }
+ PlaybackState state = builder.build();
+
+ Parcel parcel = Parcel.obtain();
+ state.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+
+ PlaybackState stateOut = PlaybackState.CREATOR.createFromParcel(parcel);
+ assertEquals(PlaybackState.STATE_CONNECTING, stateOut.getState());
+ assertEquals(TEST_POSITION, stateOut.getPosition());
+ assertEquals(TEST_PLAYBACK_SPEED, stateOut.getPlaybackSpeed(), DELTA);
+ assertEquals(TEST_UPDATE_TIME, stateOut.getLastPositionUpdateTime());
+ assertEquals(TEST_BUFFERED_POSITION, stateOut.getBufferedPosition());
+ assertEquals(TEST_ACTIONS, stateOut.getActions());
+ assertEquals(TEST_QUEUE_ITEM_ID, stateOut.getActiveQueueItemId());
+ assertEquals(TEST_ERROR_MSG, stateOut.getErrorMessage());
+ assertEquals(EXTRAS_VALUE, stateOut.getExtras().get(EXTRAS_KEY));
+
+ assertEquals(state.getCustomActions().size(), stateOut.getCustomActions().size());
+ for (int i = 0; i < state.getCustomActions().size(); i++) {
+ assertCustomActionEquals(state.getCustomActions().get(i),
+ stateOut.getCustomActions().get(i));
+ }
+ parcel.recycle();
+ }
+
+ /**
+ * Test {@link PlaybackState#describeContents()}.
+ */
+ public void testDescribeContents() {
+ assertEquals(0, new PlaybackState.Builder().build().describeContents());
+ }
+
+ /**
+ * Test {@link PlaybackState.CustomAction}.
+ */
+ public void testCustomAction() {
+ Bundle extras = new Bundle();
+ extras.putString(EXTRAS_KEY, EXTRAS_VALUE);
+
+ // Test Builder/Getters
+ PlaybackState.CustomAction customAction = new PlaybackState.CustomAction
+ .Builder(TEST_CUSTOM_ACTION, TEST_CUSTOM_ACTION_NAME, TEST_ICON_RESOURCE_ID)
+ .setExtras(extras)
+ .build();
+ assertEquals(TEST_CUSTOM_ACTION, customAction.getAction());
+ assertEquals(TEST_CUSTOM_ACTION_NAME, customAction.getName().toString());
+ assertEquals(TEST_ICON_RESOURCE_ID, customAction.getIcon());
+ assertEquals(EXTRAS_VALUE, customAction.getExtras().get(EXTRAS_KEY));
+
+ // Test describeContents
+ assertEquals(0, customAction.describeContents());
+
+ // Test writeToParcel
+ Parcel parcel = Parcel.obtain();
+ customAction.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+
+ assertCustomActionEquals(customAction,
+ PlaybackState.CustomAction.CREATOR.createFromParcel(parcel));
+ parcel.recycle();
+ }
+
+ private void assertCustomActionEquals(PlaybackState.CustomAction action1,
+ PlaybackState.CustomAction action2) {
+ assertEquals(action1.getAction(), action2.getAction());
+ assertEquals(action1.getName(), action2.getName());
+ assertEquals(action1.getIcon(), action2.getIcon());
+
+ // To be the same, two extras should be both null or both not null.
+ assertEquals(action1.getExtras() != null, action2.getExtras() != null);
+ if (action1.getExtras() != null) {
+ assertEquals(action1.getExtras().get(EXTRAS_KEY), action2.getExtras().get(EXTRAS_KEY));
+ }
+ }
+}
diff --git a/tests/tests/media/src/android/media/cts/Vp8CodecTestBase.java b/tests/tests/media/src/android/media/cts/VpxCodecTestBase.java
similarity index 96%
rename from tests/tests/media/src/android/media/cts/Vp8CodecTestBase.java
rename to tests/tests/media/src/android/media/cts/VpxCodecTestBase.java
index 0395ec7..ea72225 100644
--- a/tests/tests/media/src/android/media/cts/Vp8CodecTestBase.java
+++ b/tests/tests/media/src/android/media/cts/VpxCodecTestBase.java
@@ -43,17 +43,18 @@
import java.util.concurrent.CountDownLatch;
/**
- * Verification test for vp8 encoder and decoder.
+ * Verification test for vpx encoder and decoder.
*
* A raw yv12 stream is encoded at various settings and written to an IVF
* file. Encoded stream bitrate and key frame interval are checked against target values.
- * The stream is later decoded by vp8 decoder to verify frames are decodable and to
+ * The stream is later decoded by the decoder to verify frames are decodable and to
* calculate PSNR values for various bitrates.
*/
-public class Vp8CodecTestBase extends AndroidTestCase {
+public class VpxCodecTestBase extends AndroidTestCase {
- protected static final String TAG = "VP8CodecTestBase";
+ protected static final String TAG = "VPxCodecTestBase";
protected static final String VP8_MIME = MediaFormat.MIMETYPE_VIDEO_VP8;
+ protected static final String VP9_MIME = MediaFormat.MIMETYPE_VIDEO_VP9;
private static final String GOOGLE_CODEC_PREFIX = "omx.google.";
protected static final String SDCARD_DIR =
Environment.getExternalStorageDirectory().getAbsolutePath();
@@ -91,7 +92,7 @@
}
/**
- * VP8 codec properties generated by getVp8CodecProperties() function.
+ * VPx codec properties generated by getVpxCodecProperties() function.
*/
private class CodecProperties {
CodecProperties(String codecName, int colorFormat) {
@@ -102,12 +103,12 @@
return codecName.toLowerCase().startsWith(GOOGLE_CODEC_PREFIX);
}
- public final String codecName; // OpenMax component name for VP8 codec.
+ public final String codecName; // OpenMax component name for VPx codec.
public final int colorFormat; // Color format supported by codec.
}
/**
- * Function to find VP8 codec.
+ * Function to find VPx codec.
*
* Iterates through the list of available codecs and tries to find
* VPX codec, which can support either YUV420 planar or NV12 color formats.
@@ -197,7 +198,9 @@
int inputResourceId;
// Name of the IVF file to write encoded bitsream
public String outputIvfFilename;
- // Force to use Google VP8 encoder.
+ // Mime Type of the Encoded content.
+ public String codecMimeType;
+ // Force to use Google VPx encoder.
boolean forceGoogleEncoder;
// Number of frames to encode.
int frameCount;
@@ -236,6 +239,7 @@
protected ArrayList<EncoderOutputStreamParameters> getDefaultEncodingParameterList(
String inputYuvName,
String outputIvfBaseName,
+ String codecMimeType,
int encodeSeconds,
int[] resolutionScales,
int frameWidth,
@@ -258,8 +262,10 @@
params.scaledYuvFilename = SDCARD_DIR + File.separator +
outputIvfBaseName + resolutionScales[i]+ ".yuv";
params.inputResourceId = R.raw.football_qvga;
+ params.codecMimeType = codecMimeType;
+ String codecSuffix = VP8_MIME.equals(codecMimeType) ? "vp8" : "vp9";
params.outputIvfFilename = SDCARD_DIR + File.separator +
- outputIvfBaseName + resolutionScales[i] + ".ivf";
+ outputIvfBaseName + resolutionScales[i] + "_" + codecSuffix + ".ivf";
params.forceGoogleEncoder = false;
params.frameCount = encodeSeconds * frameRate;
params.frameRate = frameRate;
@@ -286,6 +292,7 @@
protected EncoderOutputStreamParameters getDefaultEncodingParameters(
String inputYuvName,
String outputIvfBaseName,
+ String codecMimeType,
int encodeSeconds,
int frameWidth,
int frameHeight,
@@ -298,6 +305,7 @@
return getDefaultEncodingParameterList(
inputYuvName,
outputIvfBaseName,
+ codecMimeType,
encodeSeconds,
scaleValues,
frameWidth,
@@ -500,17 +508,18 @@
* md5 sum comparison wouldn't work.)
*
* Indeed, MediaCodec will raise an IllegalStateException
- * whenever vp8 decoder fails to decode a frame, and
+ * whenever vpx decoder fails to decode a frame, and
* this test uses that fact to verify the bitstream.
*
* @param inputIvfFilename The name of the IVF file containing encoded bitsream.
* @param outputYuvFilename The name of the output YUV file (optional).
* @param frameRate Frame rate of input file in frames per second
- * @param forceGoogleDecoder Force to use Google VP8 decoder.
+ * @param forceGoogleDecoder Force to use Google VPx decoder.
*/
protected ArrayList<MediaCodec.BufferInfo> decode(
String inputIvfFilename,
String outputYuvFilename,
+ String codecMimeType,
int frameRate,
boolean forceGoogleDecoder) throws Exception {
ArrayList<MediaCodec.BufferInfo> bufferInfos = new ArrayList<MediaCodec.BufferInfo>();
@@ -528,7 +537,7 @@
// Create decoder.
MediaFormat format = MediaFormat.createVideoFormat(
- VP8_MIME, ivf.getWidth(), ivf.getHeight());
+ codecMimeType, ivf.getWidth(), ivf.getHeight());
CodecProperties properties = getVpxCodecProperties(
false /* encoder */, format, forceGoogleDecoder);
if (properties == null) {
@@ -1243,13 +1252,13 @@
}
/**
- * Vp8 encoding loop supporting encoding single streams with an option
+ * Vpx encoding loop supporting encoding single streams with an option
* to run in a looper thread and use buffer ready notification callbacks.
*
* Output stream is described by encodingParams parameters.
*
* MediaCodec will raise an IllegalStateException
- * whenever vp8 encoder fails to encode a frame.
+ * whenever vpx encoder fails to encode a frame.
*
* Color format of input file should be YUV420, and frameWidth,
* frameHeight should be supplied correctly as raw input file doesn't
@@ -1268,7 +1277,8 @@
// Create minimal media format signifying desired output.
MediaFormat format = MediaFormat.createVideoFormat(
- VP8_MIME, streamParams.frameWidth, streamParams.frameHeight);
+ streamParams.codecMimeType, streamParams.frameWidth,
+ streamParams.frameHeight);
format.setInteger(MediaFormat.KEY_BIT_RATE, bitrate);
CodecProperties properties = getVpxCodecProperties(
true, format, streamParams.forceGoogleEncoder);
@@ -1280,7 +1290,8 @@
InputStream yuvStream = OpenFileOrResourceId(
streamParams.inputYuvFilename, streamParams.inputResourceId);
IvfWriter ivf = new IvfWriter(
- streamParams.outputIvfFilename, streamParams.frameWidth, streamParams.frameHeight);
+ streamParams.outputIvfFilename, streamParams.codecMimeType,
+ streamParams.frameWidth, streamParams.frameHeight);
// Create a media format signifying desired output.
if (streamParams.bitrateType == VIDEO_ControlRateConstant) {
@@ -1416,12 +1427,12 @@
}
/**
- * Vp8 encoding run in a looper thread and use buffer ready callbacks.
+ * Vpx encoding run in a looper thread and use buffer ready callbacks.
*
* Output stream is described by encodingParams parameters.
*
* MediaCodec will raise an IllegalStateException
- * whenever vp8 encoder fails to encode a frame.
+ * whenever vpx encoder fails to encode a frame.
*
* Color format of input file should be YUV420, and frameWidth,
* frameHeight should be supplied correctly as raw input file doesn't
@@ -1443,7 +1454,8 @@
// Create minimal media format signifying desired output.
MediaFormat format = MediaFormat.createVideoFormat(
- VP8_MIME, streamParams.frameWidth, streamParams.frameHeight);
+ streamParams.codecMimeType, streamParams.frameWidth,
+ streamParams.frameHeight);
format.setInteger(MediaFormat.KEY_BIT_RATE, bitrate);
CodecProperties properties = getVpxCodecProperties(
true, format, streamParams.forceGoogleEncoder);
@@ -1453,7 +1465,8 @@
// Open input/output
IvfWriter ivf = new IvfWriter(
- streamParams.outputIvfFilename, streamParams.frameWidth, streamParams.frameHeight);
+ streamParams.outputIvfFilename, streamParams.codecMimeType,
+ streamParams.frameWidth, streamParams.frameHeight);
// Create a media format signifying desired output.
if (streamParams.bitrateType == VIDEO_ControlRateConstant) {
@@ -1495,13 +1508,13 @@
}
/**
- * Vp8 encoding loop supporting encoding multiple streams at a time.
+ * Vpx encoding loop supporting encoding multiple streams at a time.
* Each output stream is described by encodingParams parameters allowing
* simultaneous encoding of various resolutions, bitrates with an option to
* control key frame and dynamic bitrate for each output stream indepandently.
*
* MediaCodec will raise an IllegalStateException
- * whenever vp8 encoder fails to encode a frame.
+ * whenever vpx encoder fails to encode a frame.
*
* Color format of input file should be YUV420, and frameWidth,
* frameHeight should be supplied correctly as raw input file doesn't
@@ -1546,8 +1559,9 @@
int bitrate = params.bitrateSet[0];
// Create minimal media format signifying desired output.
- format[i] = MediaFormat.createVideoFormat(VP8_MIME,
- params.frameWidth, params.frameHeight);
+ format[i] = MediaFormat.createVideoFormat(
+ params.codecMimeType, params.frameWidth,
+ params.frameHeight);
format[i].setInteger(MediaFormat.KEY_BIT_RATE, bitrate);
CodecProperties properties = getVpxCodecProperties(
true, format[i], params.forceGoogleEncoder);
@@ -1572,7 +1586,9 @@
yuvStream[i] = new FileInputStream(params.scaledYuvFilename);
// Create IVF writer
- ivf[i] = new IvfWriter(params.outputIvfFilename, params.frameWidth, params.frameHeight);
+ ivf[i] = new IvfWriter(
+ params.outputIvfFilename, params.codecMimeType,
+ params.frameWidth, params.frameHeight);
// Frame buffer
int frameSize = params.frameWidth * params.frameHeight * 3 / 2;
@@ -1731,8 +1747,8 @@
/**
* Some encoding statistics.
*/
- protected class Vp8EncodingStatistics {
- Vp8EncodingStatistics() {
+ protected class VpxEncodingStatistics {
+ VpxEncodingStatistics() {
mBitrates = new ArrayList<Integer>();
mFrames = new ArrayList<Integer>();
mKeyFrames = new ArrayList<Integer>();
@@ -1756,9 +1772,9 @@
* mKeyFrames array will contain the position of key frames in the encoded stream and
* mKeyFrameInterval - average key frame interval.
*/
- protected Vp8EncodingStatistics computeEncodingStatistics(int encoderId,
+ protected VpxEncodingStatistics computeEncodingStatistics(int encoderId,
ArrayList<MediaCodec.BufferInfo> bufferInfos ) {
- Vp8EncodingStatistics statistics = new Vp8EncodingStatistics();
+ VpxEncodingStatistics statistics = new VpxEncodingStatistics();
int totalSize = 0;
int frames = 0;
@@ -1838,18 +1854,18 @@
return statistics;
}
- protected Vp8EncodingStatistics computeEncodingStatistics(
+ protected VpxEncodingStatistics computeEncodingStatistics(
ArrayList<MediaCodec.BufferInfo> bufferInfos ) {
return computeEncodingStatistics(0, bufferInfos);
}
- protected ArrayList<Vp8EncodingStatistics> computeSimulcastEncodingStatistics(
+ protected ArrayList<VpxEncodingStatistics> computeSimulcastEncodingStatistics(
ArrayList<ArrayList<MediaCodec.BufferInfo>> bufferInfos) {
int numCodecs = bufferInfos.size();
- ArrayList<Vp8EncodingStatistics> statistics = new ArrayList<Vp8EncodingStatistics>();
+ ArrayList<VpxEncodingStatistics> statistics = new ArrayList<VpxEncodingStatistics>();
for (int i = 0; i < numCodecs; i++) {
- Vp8EncodingStatistics currentStatistics =
+ VpxEncodingStatistics currentStatistics =
computeEncodingStatistics(i, bufferInfos.get(i));
statistics.add(currentStatistics);
}
@@ -1872,8 +1888,8 @@
/**
* Decoding PSNR statistics.
*/
- protected class Vp8DecodingStatistics {
- Vp8DecodingStatistics() {
+ protected class VpxDecodingStatistics {
+ VpxDecodingStatistics() {
mMinimumPSNR = Integer.MAX_VALUE;
}
public double mAveragePSNR;
@@ -1901,13 +1917,13 @@
* set of reference and decoded video frames.
* Runs PSNR calculation for the full duration of the decoded data.
*/
- protected Vp8DecodingStatistics computeDecodingStatistics(
+ protected VpxDecodingStatistics computeDecodingStatistics(
String referenceYuvFilename,
int referenceYuvRawId,
String decodedYuvFilename,
int width,
int height) throws Exception {
- Vp8DecodingStatistics statistics = new Vp8DecodingStatistics();
+ VpxDecodingStatistics statistics = new VpxDecodingStatistics();
InputStream referenceStream =
OpenFileOrResourceId(referenceYuvFilename, referenceYuvRawId);
InputStream decodedStream = new FileInputStream(decodedYuvFilename);
diff --git a/tests/tests/media/src/android/media/cts/Vp8EncoderTest.java b/tests/tests/media/src/android/media/cts/VpxEncoderTest.java
similarity index 85%
rename from tests/tests/media/src/android/media/cts/Vp8EncoderTest.java
rename to tests/tests/media/src/android/media/cts/VpxEncoderTest.java
index 5552f6c..0e9c940 100644
--- a/tests/tests/media/src/android/media/cts/Vp8EncoderTest.java
+++ b/tests/tests/media/src/android/media/cts/VpxEncoderTest.java
@@ -28,14 +28,14 @@
import java.util.Arrays;
/**
- * Verification test for vp8 encoder and decoder.
+ * Verification test for vp8/vp9 encoder and decoder.
*
* A raw yv12 stream is encoded at various settings and written to an IVF
* file. Encoded stream bitrate and key frame interval are checked against target values.
- * The stream is later decoded by vp8 decoder to verify frames are decodable and to
+ * The stream is later decoded by vp8/vp9 decoder to verify frames are decodable and to
* calculate PSNR values for various bitrates.
*/
-public class Vp8EncoderTest extends Vp8CodecTestBase {
+public class VpxEncoderTest extends VpxCodecTestBase {
private static final String ENCODED_IVF_BASE = "football";
private static final String INPUT_YUV = null;
@@ -54,9 +54,9 @@
private static final int[] TEST_BITRATES_SET = { 300000, 500000, 700000, 900000 };
// Maximum allowed bitrate variation from the target value.
private static final double MAX_BITRATE_VARIATION = 0.2;
- // Average PSNR values for reference Google VP8 codec for the above bitrates.
+ // Average PSNR values for reference Google VPx codec for the above bitrates.
private static final double[] REFERENCE_AVERAGE_PSNR = { 33.1, 35.2, 36.6, 37.8 };
- // Minimum PSNR values for reference Google VP8 codec for the above bitrates.
+ // Minimum PSNR values for reference Google VPx codec for the above bitrates.
private static final double[] REFERENCE_MINIMUM_PSNR = { 25.9, 27.5, 28.4, 30.3 };
// Maximum allowed average PSNR difference of encoder comparing to reference Google encoder.
private static final double MAX_AVERAGE_PSNR_DIFFERENCE = 2;
@@ -75,13 +75,13 @@
private static final int MAX_KEYFRAME_INTERVAL_VARIATION = 3;
/**
- * A basic test for VP8 encoder.
+ * A basic test for VPx encoder.
*
* Encodes 9 seconds of raw stream with default configuration options,
* and then decodes it to verify the bitstream.
* Also checks the average bitrate is within MAX_BITRATE_VARIATION of the target value.
*/
- public void testBasic() throws Exception {
+ private void internalTestBasic(String codecMimeType) throws Exception {
int encodeSeconds = 9;
boolean skipped = true;
@@ -89,6 +89,7 @@
EncoderOutputStreamParameters params = getDefaultEncodingParameters(
INPUT_YUV,
ENCODED_IVF_BASE,
+ codecMimeType,
encodeSeconds,
WIDTH,
HEIGHT,
@@ -102,14 +103,14 @@
}
skipped = false;
- Vp8EncodingStatistics statistics = computeEncodingStatistics(bufInfo);
+ VpxEncodingStatistics statistics = computeEncodingStatistics(bufInfo);
assertEquals("Stream bitrate " + statistics.mAverageBitrate +
" is different from the target " + targetBitrate,
targetBitrate, statistics.mAverageBitrate,
MAX_BITRATE_VARIATION * targetBitrate);
- decode(params.outputIvfFilename, null, FPS, params.forceGoogleEncoder);
+ decode(params.outputIvfFilename, null, codecMimeType, FPS, params.forceGoogleEncoder);
}
if (skipped) {
@@ -118,13 +119,13 @@
}
/**
- * Asynchronous encoding test for VP8 encoder.
+ * Asynchronous encoding test for VPx encoder.
*
* Encodes 9 seconds of raw stream using synchronous and asynchronous calls.
* Checks the PSNR difference between the encoded and decoded output and reference yuv input
* does not change much for two different ways of the encoder call.
*/
- public void testAsyncEncoding() throws Exception {
+ private void internalTestAsyncEncoding(String codecMimeType) throws Exception {
int encodeSeconds = 9;
// First test the encoder running in a looper thread with buffer callbacks enabled.
@@ -132,6 +133,7 @@
EncoderOutputStreamParameters params = getDefaultEncodingParameters(
INPUT_YUV,
ENCODED_IVF_BASE,
+ codecMimeType,
encodeSeconds,
WIDTH,
HEIGHT,
@@ -145,8 +147,8 @@
return;
}
computeEncodingStatistics(bufInfos);
- decode(params.outputIvfFilename, OUTPUT_YUV, FPS, params.forceGoogleEncoder);
- Vp8DecodingStatistics statisticsAsync = computeDecodingStatistics(
+ decode(params.outputIvfFilename, OUTPUT_YUV, codecMimeType, FPS, params.forceGoogleEncoder);
+ VpxDecodingStatistics statisticsAsync = computeDecodingStatistics(
params.inputYuvFilename, R.raw.football_qvga, OUTPUT_YUV,
params.frameWidth, params.frameHeight);
@@ -156,6 +158,7 @@
params = getDefaultEncodingParameters(
INPUT_YUV,
ENCODED_IVF_BASE,
+ codecMimeType,
encodeSeconds,
WIDTH,
HEIGHT,
@@ -169,8 +172,8 @@
return;
}
computeEncodingStatistics(bufInfos);
- decode(params.outputIvfFilename, OUTPUT_YUV, FPS, params.forceGoogleEncoder);
- Vp8DecodingStatistics statisticsSync = computeDecodingStatistics(
+ decode(params.outputIvfFilename, OUTPUT_YUV, codecMimeType, FPS, params.forceGoogleEncoder);
+ VpxDecodingStatistics statisticsSync = computeDecodingStatistics(
params.inputYuvFilename, R.raw.football_qvga, OUTPUT_YUV,
params.frameWidth, params.frameHeight);
@@ -193,12 +196,13 @@
* Encodes 9 seconds of raw stream and requests a sync frame every second (30 frames).
* The test does not verify the output stream.
*/
- public void testSyncFrame() throws Exception {
+ private void internalTestSyncFrame(String codecMimeType) throws Exception {
int encodeSeconds = 9;
EncoderOutputStreamParameters params = getDefaultEncodingParameters(
INPUT_YUV,
ENCODED_IVF_BASE,
+ codecMimeType,
encodeSeconds,
WIDTH,
HEIGHT,
@@ -214,7 +218,7 @@
return;
}
- Vp8EncodingStatistics statistics = computeEncodingStatistics(bufInfo);
+ VpxEncodingStatistics statistics = computeEncodingStatistics(bufInfo);
// First check if we got expected number of key frames.
int actualKeyFrames = statistics.mKeyFrames.size();
@@ -242,13 +246,14 @@
* Run the the encoder for 12 seconds. Request changes to the
* bitrate after 6 seconds and ensure the encoder responds.
*/
- public void testDynamicBitrateChange() throws Exception {
+ private void internalTestDynamicBitrateChange(String codecMimeType) throws Exception {
int encodeSeconds = 12; // Encoding sequence duration in seconds.
int[] bitrateTargetValues = { 400000, 800000 }; // List of bitrates to test.
EncoderOutputStreamParameters params = getDefaultEncodingParameters(
INPUT_YUV,
ENCODED_IVF_BASE,
+ codecMimeType,
encodeSeconds,
WIDTH,
HEIGHT,
@@ -274,7 +279,7 @@
return;
}
- Vp8EncodingStatistics statistics = computeEncodingStatistics(bufInfo);
+ VpxEncodingStatistics statistics = computeEncodingStatistics(bufInfo);
// Calculate actual average bitrates for every [stepSeconds] second.
int[] bitrateActualValues = new int[bitrateTargetValues.length];
@@ -309,11 +314,11 @@
* and then run parallel encoding and decoding of the same streams.
* Compares average bitrate and PSNR for sequential and parallel runs.
*/
- public void testParallelEncodingAndDecoding() throws Exception {
+ private void internalTestParallelEncodingAndDecoding(String codecMimeType) throws Exception {
// check for encoder up front, as by the time we detect lack of
// encoder support, we may have already started decoding.
MediaCodecList mcl = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
- MediaFormat format = MediaFormat.createVideoFormat(VP8_MIME, WIDTH, HEIGHT);
+ MediaFormat format = MediaFormat.createVideoFormat(codecMimeType, WIDTH, HEIGHT);
if (mcl.findEncoderForFormat(format) == null) {
Log.i(TAG, "SKIPPING testParallelEncodingAndDecoding(): no suitable encoder found");
return;
@@ -327,6 +332,7 @@
final EncoderOutputStreamParameters params = getDefaultEncodingParameters(
INPUT_YUV,
ENCODED_IVF_BASE,
+ codecMimeType,
encodeSeconds,
WIDTH,
HEIGHT,
@@ -340,7 +346,7 @@
public void run() {
try {
ArrayList<MediaCodec.BufferInfo> bufInfo = encode(params);
- Vp8EncodingStatistics statistics = computeEncodingStatistics(bufInfo);
+ VpxEncodingStatistics statistics = computeEncodingStatistics(bufInfo);
bitrate[0] = statistics.mAverageBitrate;
} catch (Exception e) {
Log.e(TAG, "Encoder error: " + e.toString());
@@ -351,8 +357,8 @@
Runnable runDecoder = new Runnable() {
public void run() {
try {
- decode(inputIvfFilename, OUTPUT_YUV, FPS, params.forceGoogleEncoder);
- Vp8DecodingStatistics statistics = computeDecodingStatistics(
+ decode(inputIvfFilename, OUTPUT_YUV, codecMimeType, FPS, params.forceGoogleEncoder);
+ VpxDecodingStatistics statistics = computeDecodingStatistics(
params.inputYuvFilename, R.raw.football_qvga, OUTPUT_YUV,
params.frameWidth, params.frameHeight);
psnr[0] = statistics.mAveragePSNR;
@@ -410,7 +416,7 @@
* Video streams with higher bitrates should have higher PSNRs.
* Also compares average and minimum PSNR of codec with PSNR values of reference Google codec.
*/
- public void testEncoderQuality() throws Exception {
+ private void internalTestEncoderQuality(String codecMimeType) throws Exception {
int encodeSeconds = 9; // Encoding sequence duration in seconds for each bitrate.
double[] psnrPlatformCodecAverage = new double[TEST_BITRATES_SET.length];
double[] psnrPlatformCodecMin = new double[TEST_BITRATES_SET.length];
@@ -423,6 +429,7 @@
EncoderOutputStreamParameters params = getDefaultEncodingParameters(
INPUT_YUV,
ENCODED_IVF_BASE,
+ codecMimeType,
encodeSeconds,
WIDTH,
HEIGHT,
@@ -438,8 +445,8 @@
completed[i] = true;
skipped = false;
- decode(params.outputIvfFilename, OUTPUT_YUV, FPS, params.forceGoogleEncoder);
- Vp8DecodingStatistics statistics = computeDecodingStatistics(
+ decode(params.outputIvfFilename, OUTPUT_YUV, codecMimeType, FPS, params.forceGoogleEncoder);
+ VpxDecodingStatistics statistics = computeDecodingStatistics(
params.inputYuvFilename, R.raw.football_qvga, OUTPUT_YUV,
params.frameWidth, params.frameHeight);
psnrPlatformCodecAverage[i] = statistics.mAveragePSNR;
@@ -500,5 +507,32 @@
}
}
}
+
+ public void testBasicVP8() throws Exception { internalTestBasic(VP8_MIME); }
+ public void testBasicVP9() throws Exception { internalTestBasic(VP9_MIME); }
+
+ public void testAsyncEncodingVP8() throws Exception { internalTestAsyncEncoding(VP8_MIME); }
+ public void testAsyncEncodingVP9() throws Exception { internalTestAsyncEncoding(VP9_MIME); }
+
+ public void testSyncFrameVP8() throws Exception { internalTestSyncFrame(VP8_MIME); }
+ public void testSyncFrameVP9() throws Exception { internalTestSyncFrame(VP9_MIME); }
+
+ public void testDynamicBitrateChangeVP8() throws Exception {
+ internalTestDynamicBitrateChange(VP8_MIME);
+ }
+ public void testDynamicBitrateChangeVP9() throws Exception {
+ internalTestDynamicBitrateChange(VP9_MIME);
+ }
+
+ public void testParallelEncodingAndDecodingVP8() throws Exception {
+ internalTestParallelEncodingAndDecoding(VP8_MIME);
+ }
+ public void testParallelEncodingAndDecodingVP9() throws Exception {
+ internalTestParallelEncodingAndDecoding(VP9_MIME);
+ }
+
+ public void testEncoderQualityVP8() throws Exception { internalTestEncoderQuality(VP8_MIME); }
+ public void testEncoderQualityVP9() throws Exception { internalTestEncoderQuality(VP9_MIME); }
+
}
diff --git a/tests/tests/os/Android.mk b/tests/tests/os/Android.mk
index 5397fc6a..abbad8f 100644
--- a/tests/tests/os/Android.mk
+++ b/tests/tests/os/Android.mk
@@ -25,7 +25,7 @@
LOCAL_MULTILIB := both
LOCAL_STATIC_JAVA_LIBRARIES := \
- ctsdeviceutil ctstestrunner guava
+ android-support-test ctsdeviceutil ctstestrunner guava platform-test-annotations
LOCAL_JNI_SHARED_LIBRARIES := libcts_jni libctsos_jni libnativehelper_compat_libc++
diff --git a/tests/tests/permission2/res/raw/android_manifest.xml b/tests/tests/permission2/res/raw/android_manifest.xml
index 4ebe527..3d8ab32 100644
--- a/tests/tests/permission2/res/raw/android_manifest.xml
+++ b/tests/tests/permission2/res/raw/android_manifest.xml
@@ -18,8 +18,8 @@
*/
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="android" coreApp="true" android:sharedUserId="android.uid.system"
- android:sharedUserLabel="@string/android_system_label">
+ package="android" coreApp="true" android:sharedUserId="android.uid.system"
+ android:sharedUserLabel="@string/android_system_label">
<!-- ================================================ -->
<!-- Special broadcasts that only the system can send -->
@@ -138,49 +138,46 @@
<protected-broadcast android:name="android.bluetooth.device.action.CONNECTION_ACCESS_REPLY" />
<protected-broadcast android:name="android.bluetooth.device.action.CONNECTION_ACCESS_CANCEL" />
<protected-broadcast android:name="android.bluetooth.device.action.CONNECTION_ACCESS_REQUEST" />
- <protected-broadcast android:name="android.bluetooth.device.action.SDP_RECORD" />
<protected-broadcast android:name="android.bluetooth.devicepicker.action.LAUNCH" />
<protected-broadcast android:name="android.bluetooth.devicepicker.action.DEVICE_SELECTED" />
<protected-broadcast
- android:name="android.bluetooth.headset.profile.action.CONNECTION_STATE_CHANGED" />
+ android:name="android.bluetooth.headset.profile.action.CONNECTION_STATE_CHANGED" />
<protected-broadcast
- android:name="android.bluetooth.headset.profile.action.AUDIO_STATE_CHANGED" />
+ android:name="android.bluetooth.headset.profile.action.AUDIO_STATE_CHANGED" />
<protected-broadcast
- android:name="android.bluetooth.headset.action.VENDOR_SPECIFIC_HEADSET_EVENT" />
+ android:name="android.bluetooth.headset.action.VENDOR_SPECIFIC_HEADSET_EVENT" />
<protected-broadcast
- android:name="android.bluetooth.headsetclient.profile.action.CONNECTION_STATE_CHANGED" />
+ android:name="android.bluetooth.headsetclient.profile.action.CONNECTION_STATE_CHANGED" />
<protected-broadcast
- android:name="android.bluetooth.headsetclient.profile.action.AUDIO_STATE_CHANGED" />
+ android:name="android.bluetooth.headsetclient.profile.action.AUDIO_STATE_CHANGED" />
<protected-broadcast
- android:name="android.bluetooth.headsetclient.profile.action.AG_EVENT" />
+ android:name="android.bluetooth.headsetclient.profile.action.AG_EVENT" />
<protected-broadcast
- android:name="android.bluetooth.headsetclient.profile.action.AG_CALL_CHANGED" />
+ android:name="android.bluetooth.headsetclient.profile.action.AG_CALL_CHANGED" />
<protected-broadcast
- android:name="android.bluetooth.headsetclient.profile.action.RESULT" />
+ android:name="android.bluetooth.headsetclient.profile.action.RESULT" />
<protected-broadcast
- android:name="android.bluetooth.headsetclient.profile.action.LAST_VTAG" />
+ android:name="android.bluetooth.headsetclient.profile.action.LAST_VTAG" />
<protected-broadcast
- android:name="android.bluetooth.a2dp.profile.action.CONNECTION_STATE_CHANGED" />
+ android:name="android.bluetooth.a2dp.profile.action.CONNECTION_STATE_CHANGED" />
<protected-broadcast
- android:name="android.bluetooth.a2dp.profile.action.PLAYING_STATE_CHANGED" />
+ android:name="android.bluetooth.a2dp.profile.action.PLAYING_STATE_CHANGED" />
<protected-broadcast
- android:name="android.bluetooth.a2dp-sink.profile.action.CONNECTION_STATE_CHANGED" />
+ android:name="android.bluetooth.a2dp-sink.profile.action.CONNECTION_STATE_CHANGED" />
<protected-broadcast
- android:name="android.bluetooth.a2dp-sink.profile.action.PLAYING_STATE_CHANGED" />
+ android:name="android.bluetooth.a2dp-sink.profile.action.PLAYING_STATE_CHANGED" />
<protected-broadcast
- android:name="android.bluetooth.a2dp-sink.profile.action.AUDIO_CONFIG_CHANGED" />
+ android:name="android.bluetooth.a2dp-sink.profile.action.AUDIO_CONFIG_CHANGED" />
<protected-broadcast
- android:name="android.bluetooth.avrcp-controller.profile.action.CONNECTION_STATE_CHANGED" />
+ android:name="android.bluetooth.avrcp-controller.profile.action.CONNECTION_STATE_CHANGED" />
<protected-broadcast
- android:name="android.bluetooth.input.profile.action.CONNECTION_STATE_CHANGED" />
+ android:name="android.bluetooth.input.profile.action.CONNECTION_STATE_CHANGED" />
<protected-broadcast
- android:name="android.bluetooth.input.profile.action.PROTOCOL_MODE_CHANGED" />
+ android:name="android.bluetooth.input.profile.action.PROTOCOL_MODE_CHANGED" />
<protected-broadcast
- android:name="android.bluetooth.input.profile.action.VIRTUAL_UNPLUG_STATUS" />
+ android:name="android.bluetooth.input.profile.action.VIRTUAL_UNPLUG_STATUS" />
<protected-broadcast
- android:name="android.bluetooth.map.profile.action.CONNECTION_STATE_CHANGED" />
- <protected-broadcast
- android:name="android.bluetooth.pan.profile.action.CONNECTION_STATE_CHANGED" />
+ android:name="android.bluetooth.pan.profile.action.CONNECTION_STATE_CHANGED" />
<protected-broadcast android:name="android.bluetooth.pbap.intent.action.PBAP_STATE_CHANGED" />
<protected-broadcast android:name="android.btopp.intent.action.INCOMING_FILE_NOTIFICATION" />
<protected-broadcast android:name="android.btopp.intent.action.USER_CONFIRMATION_TIMEOUT" />
@@ -192,7 +189,6 @@
<protected-broadcast android:name="android.btopp.intent.action.RETRY" />
<protected-broadcast android:name="android.btopp.intent.action.OPEN" />
<protected-broadcast android:name="android.btopp.intent.action.OPEN_INBOUND" />
- <protected-broadcast android:name="android.btopp.intent.action.TRANSFER_COMPLETE" />
<protected-broadcast android:name="com.android.bluetooth.pbap.authchall" />
<protected-broadcast android:name="com.android.bluetooth.pbap.userconfirmtimeout" />
<protected-broadcast android:name="com.android.bluetooth.pbap.authresponse" />
@@ -310,7 +306,7 @@
<protected-broadcast android:name="android.intent.action.ACTION_IDLE_MAINTENANCE_START" />
<protected-broadcast android:name="android.intent.action.ACTION_IDLE_MAINTENANCE_END" />
- <protected-broadcast android:name="com.android.server.ACTION_TRIGGER_IDLE" />
+ <protected-broadcast android:name="com.android.server.task.controllers.IdleController.ACTION_TRIGGER_IDLE" />
<protected-broadcast android:name="android.intent.action.HDMI_PLUGGED" />
@@ -330,11 +326,11 @@
<protected-broadcast android:name="android.provider.Telephony.MMS_DOWNLOADED" />
<protected-broadcast
- android:name="com.android.server.connectivityservice.CONNECTED_TO_PROVISIONING_NETWORK_ACTION" />
+ android:name="com.android.server.connectivityservice.CONNECTED_TO_PROVISIONING_NETWORK_ACTION" />
<!-- Defined in RestrictionsManager -->
<protected-broadcast
- android:name="android.intent.action.PERMISSION_RESPONSE_RECEIVED" />
+ android:name="android.intent.action.PERMISSION_RESPONSE_RECEIVED" />
<!-- Defined in RestrictionsManager -->
<protected-broadcast android:name="android.intent.action.REQUEST_PERMISSION" />
@@ -496,28 +492,28 @@
<!-- Used for runtime permissions related to contacts and profiles on this
device. -->
<permission-group android:name="android.permission-group.CONTACTS"
- android:icon="@drawable/perm_group_contacts"
- android:label="@string/permgrouplab_contacts"
- android:description="@string/permgroupdesc_contacts"
- android:priority="100" />
+ android:icon="@drawable/perm_group_contacts"
+ android:label="@string/permgrouplab_contacts"
+ android:description="@string/permgroupdesc_contacts"
+ android:priority="100" />
<!-- Allows an application to read the user's contacts data.
<p>Protection level: dangerous
-->
<permission android:name="android.permission.READ_CONTACTS"
- android:permissionGroup="android.permission-group.CONTACTS"
- android:label="@string/permlab_readContacts"
- android:description="@string/permdesc_readContacts"
- android:protectionLevel="dangerous" />
+ android:permissionGroup="android.permission-group.CONTACTS"
+ android:label="@string/permlab_readContacts"
+ android:description="@string/permdesc_readContacts"
+ android:protectionLevel="dangerous" />
<!-- Allows an application to write the user's contacts data.
<p>Protection level: dangerous
-->
<permission android:name="android.permission.WRITE_CONTACTS"
- android:permissionGroup="android.permission-group.CONTACTS"
- android:label="@string/permlab_writeContacts"
- android:description="@string/permdesc_writeContacts"
- android:protectionLevel="dangerous" />
+ android:permissionGroup="android.permission-group.CONTACTS"
+ android:label="@string/permlab_writeContacts"
+ android:description="@string/permdesc_writeContacts"
+ android:protectionLevel="dangerous" />
<!-- ====================================================================== -->
<!-- Permissions for accessing user's calendar -->
@@ -526,28 +522,28 @@
<!-- Used for runtime permissions related to user's calendar. -->
<permission-group android:name="android.permission-group.CALENDAR"
- android:icon="@drawable/perm_group_calendar"
- android:label="@string/permgrouplab_calendar"
- android:description="@string/permgroupdesc_calendar"
- android:priority="200" />
+ android:icon="@drawable/perm_group_calendar"
+ android:label="@string/permgrouplab_calendar"
+ android:description="@string/permgroupdesc_calendar"
+ android:priority="200" />
<!-- Allows an application to read the user's calendar data.
<p>Protection level: dangerous
-->
<permission android:name="android.permission.READ_CALENDAR"
- android:permissionGroup="android.permission-group.CALENDAR"
- android:label="@string/permlab_readCalendar"
- android:description="@string/permdesc_readCalendar"
- android:protectionLevel="dangerous" />
+ android:permissionGroup="android.permission-group.CALENDAR"
+ android:label="@string/permlab_readCalendar"
+ android:description="@string/permdesc_readCalendar"
+ android:protectionLevel="dangerous" />
<!-- Allows an application to write the user's calendar data.
<p>Protection level: dangerous
-->
<permission android:name="android.permission.WRITE_CALENDAR"
- android:permissionGroup="android.permission-group.CALENDAR"
- android:label="@string/permlab_writeCalendar"
- android:description="@string/permdesc_writeCalendar"
- android:protectionLevel="dangerous" />
+ android:permissionGroup="android.permission-group.CALENDAR"
+ android:label="@string/permlab_writeCalendar"
+ android:description="@string/permdesc_writeCalendar"
+ android:protectionLevel="dangerous" />
<!-- ====================================================================== -->
<!-- Permissions for accessing and modifying user's SMS messages -->
@@ -556,56 +552,56 @@
<!-- Used for runtime permissions related to user's SMS messages. -->
<permission-group android:name="android.permission-group.SMS"
- android:icon="@drawable/perm_group_sms"
- android:label="@string/permgrouplab_sms"
- android:description="@string/permgroupdesc_sms"
- android:priority="300" />
+ android:icon="@drawable/perm_group_sms"
+ android:label="@string/permgrouplab_sms"
+ android:description="@string/permgroupdesc_sms"
+ android:priority="300" />
<!-- Allows an application to send SMS messages.
<p>Protection level: dangerous
-->
<permission android:name="android.permission.SEND_SMS"
- android:permissionGroup="android.permission-group.SMS"
- android:label="@string/permlab_sendSms"
- android:description="@string/permdesc_sendSms"
- android:permissionFlags="costsMoney"
- android:protectionLevel="dangerous" />
+ android:permissionGroup="android.permission-group.SMS"
+ android:label="@string/permlab_sendSms"
+ android:description="@string/permdesc_sendSms"
+ android:permissionFlags="costsMoney"
+ android:protectionLevel="dangerous" />
<!-- Allows an application to receive SMS messages.
<p>Protection level: dangerous
-->
<permission android:name="android.permission.RECEIVE_SMS"
- android:permissionGroup="android.permission-group.SMS"
- android:label="@string/permlab_receiveSms"
- android:description="@string/permdesc_receiveSms"
- android:protectionLevel="dangerous"/>
+ android:permissionGroup="android.permission-group.SMS"
+ android:label="@string/permlab_receiveSms"
+ android:description="@string/permdesc_receiveSms"
+ android:protectionLevel="dangerous"/>
<!-- Allows an application to read SMS messages.
<p>Protection level: dangerous
-->
<permission android:name="android.permission.READ_SMS"
- android:permissionGroup="android.permission-group.SMS"
- android:label="@string/permlab_readSms"
- android:description="@string/permdesc_readSms"
- android:protectionLevel="dangerous" />
+ android:permissionGroup="android.permission-group.SMS"
+ android:label="@string/permlab_readSms"
+ android:description="@string/permdesc_readSms"
+ android:protectionLevel="dangerous" />
<!-- Allows an application to receive WAP push messages.
<p>Protection level: dangerous
-->
<permission android:name="android.permission.RECEIVE_WAP_PUSH"
- android:permissionGroup="android.permission-group.SMS"
- android:label="@string/permlab_receiveWapPush"
- android:description="@string/permdesc_receiveWapPush"
- android:protectionLevel="dangerous" />
+ android:permissionGroup="android.permission-group.SMS"
+ android:label="@string/permlab_receiveWapPush"
+ android:description="@string/permdesc_receiveWapPush"
+ android:protectionLevel="dangerous" />
<!-- Allows an application to monitor incoming MMS messages.
<p>Protection level: dangerous
-->
<permission android:name="android.permission.RECEIVE_MMS"
- android:permissionGroup="android.permission-group.SMS"
- android:label="@string/permlab_receiveMms"
- android:description="@string/permdesc_receiveMms"
- android:protectionLevel="dangerous" />
+ android:permissionGroup="android.permission-group.SMS"
+ android:label="@string/permlab_receiveMms"
+ android:description="@string/permdesc_receiveMms"
+ android:protectionLevel="dangerous" />
<!-- Allows an application to read previously received cell broadcast
messages and to register a content observer to get notifications when
@@ -620,10 +616,10 @@
<p>Protection level: dangerous
@hide Pending API council approval -->
<permission android:name="android.permission.READ_CELL_BROADCASTS"
- android:permissionGroup="android.permission-group.SMS"
- android:label="@string/permlab_readCellBroadcasts"
- android:description="@string/permdesc_readCellBroadcasts"
- android:protectionLevel="dangerous" />
+ android:permissionGroup="android.permission-group.SMS"
+ android:label="@string/permlab_readCellBroadcasts"
+ android:description="@string/permdesc_readCellBroadcasts"
+ android:protectionLevel="dangerous" />
<!-- ====================================================================== -->
<!-- Permissions for accessing external storage -->
@@ -632,10 +628,10 @@
<!-- Used for runtime permissions related to the shared external storage. -->
<permission-group android:name="android.permission-group.STORAGE"
- android:icon="@drawable/perm_group_storage"
- android:label="@string/permgrouplab_storage"
- android:description="@string/permgroupdesc_storage"
- android:priority="900" />
+ android:icon="@drawable/perm_group_storage"
+ android:label="@string/permgrouplab_storage"
+ android:description="@string/permgroupdesc_storage"
+ android:priority="900" />
<!-- Allows an application to read from external storage.
<p>Any app that declares the {@link #WRITE_EXTERNAL_STORAGE} permission is implicitly
@@ -660,10 +656,10 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.READ_EXTERNAL_STORAGE"
- android:permissionGroup="android.permission-group.STORAGE"
- android:label="@string/permlab_sdcardRead"
- android:description="@string/permdesc_sdcardRead"
- android:protectionLevel="dangerous" />
+ android:permissionGroup="android.permission-group.STORAGE"
+ android:label="@string/permlab_sdcardRead"
+ android:description="@string/permdesc_sdcardRead"
+ android:protectionLevel="dangerous" />
<!-- Allows an application to write to external storage.
<p class="note"><strong>Note:</strong> If <em>both</em> your <a
@@ -681,10 +677,10 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
- android:permissionGroup="android.permission-group.STORAGE"
- android:label="@string/permlab_sdcardWrite"
- android:description="@string/permdesc_sdcardWrite"
- android:protectionLevel="dangerous" />
+ android:permissionGroup="android.permission-group.STORAGE"
+ android:label="@string/permlab_sdcardWrite"
+ android:description="@string/permdesc_sdcardWrite"
+ android:protectionLevel="dangerous" />
<!-- ====================================================================== -->
<!-- Permissions for accessing the device location -->
@@ -693,28 +689,28 @@
<!-- Used for permissions that allow accessing the device location. -->
<permission-group android:name="android.permission-group.LOCATION"
- android:icon="@drawable/perm_group_location"
- android:label="@string/permgrouplab_location"
- android:description="@string/permgroupdesc_location"
- android:priority="400" />
+ android:icon="@drawable/perm_group_location"
+ android:label="@string/permgrouplab_location"
+ android:description="@string/permgroupdesc_location"
+ android:priority="400" />
<!-- Allows an app to access precise location.
<p>Protection level: dangerous
-->
<permission android:name="android.permission.ACCESS_FINE_LOCATION"
- android:permissionGroup="android.permission-group.LOCATION"
- android:label="@string/permlab_accessFineLocation"
- android:description="@string/permdesc_accessFineLocation"
- android:protectionLevel="dangerous" />
+ android:permissionGroup="android.permission-group.LOCATION"
+ android:label="@string/permlab_accessFineLocation"
+ android:description="@string/permdesc_accessFineLocation"
+ android:protectionLevel="dangerous" />
<!-- Allows an app to access approximate location.
<p>Protection level: dangerous
-->
<permission android:name="android.permission.ACCESS_COARSE_LOCATION"
- android:permissionGroup="android.permission-group.LOCATION"
- android:label="@string/permlab_accessCoarseLocation"
- android:description="@string/permdesc_accessCoarseLocation"
- android:protectionLevel="dangerous" />
+ android:permissionGroup="android.permission-group.LOCATION"
+ android:label="@string/permlab_accessCoarseLocation"
+ android:description="@string/permdesc_accessCoarseLocation"
+ android:protectionLevel="dangerous" />
<!-- ====================================================================== -->
<!-- Permissions for accessing the device telephony -->
@@ -723,10 +719,10 @@
<!-- Used for permissions that are associated telephony features. -->
<permission-group android:name="android.permission-group.PHONE"
- android:icon="@drawable/perm_group_phone_calls"
- android:label="@string/permgrouplab_phone"
- android:description="@string/permgroupdesc_phone"
- android:priority="500" />
+ android:icon="@drawable/perm_group_phone_calls"
+ android:label="@string/permgrouplab_phone"
+ android:description="@string/permgroupdesc_phone"
+ android:priority="500" />
<!-- Allows read only access to phone state, including the phone number of the device,
current cellular network information, the status of any ongoing calls, and a list of any
@@ -742,21 +738,21 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.READ_PHONE_STATE"
- android:permissionGroup="android.permission-group.PHONE"
- android:label="@string/permlab_readPhoneState"
- android:description="@string/permdesc_readPhoneState"
- android:protectionLevel="dangerous" />
+ android:permissionGroup="android.permission-group.PHONE"
+ android:label="@string/permlab_readPhoneState"
+ android:description="@string/permdesc_readPhoneState"
+ android:protectionLevel="dangerous" />
<!-- Allows an application to initiate a phone call without going through
the Dialer user interface for the user to confirm the call.
<p>Protection level: dangerous
-->
<permission android:name="android.permission.CALL_PHONE"
- android:permissionGroup="android.permission-group.PHONE"
- android:permissionFlags="costsMoney"
- android:label="@string/permlab_callPhone"
- android:description="@string/permdesc_callPhone"
- android:protectionLevel="dangerous" />
+ android:permissionGroup="android.permission-group.PHONE"
+ android:permissionFlags="costsMoney"
+ android:label="@string/permlab_callPhone"
+ android:description="@string/permdesc_callPhone"
+ android:protectionLevel="dangerous" />
<!-- Allows an application to access the IMS call service: making and
modifying a call
@@ -764,10 +760,10 @@
@hide
-->
<permission android:name="android.permission.ACCESS_IMS_CALL_SERVICE"
- android:permissionGroup="android.permission-group.PHONE"
- android:label="@string/permlab_accessImsCallService"
- android:description="@string/permdesc_accessImsCallService"
- android:protectionLevel="signature|privileged" />
+ android:permissionGroup="android.permission-group.PHONE"
+ android:label="@string/permlab_accessImsCallService"
+ android:description="@string/permdesc_accessImsCallService"
+ android:protectionLevel="signature|privileged" />
<!-- Allows an application to read the user's call log.
<p class="note"><strong>Note:</strong> If your app uses the
@@ -782,10 +778,10 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.READ_CALL_LOG"
- android:permissionGroup="android.permission-group.PHONE"
- android:label="@string/permlab_readCallLog"
- android:description="@string/permdesc_readCallLog"
- android:protectionLevel="dangerous" />
+ android:permissionGroup="android.permission-group.PHONE"
+ android:label="@string/permlab_readCallLog"
+ android:description="@string/permdesc_readCallLog"
+ android:protectionLevel="dangerous" />
<!-- Allows an application to write (but not read) the user's
call log data.
@@ -801,28 +797,28 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.WRITE_CALL_LOG"
- android:permissionGroup="android.permission-group.PHONE"
- android:label="@string/permlab_writeCallLog"
- android:description="@string/permdesc_writeCallLog"
- android:protectionLevel="dangerous" />
+ android:permissionGroup="android.permission-group.PHONE"
+ android:label="@string/permlab_writeCallLog"
+ android:description="@string/permdesc_writeCallLog"
+ android:protectionLevel="dangerous" />
<!-- Allows an application to add voicemails into the system.
<p>Protection level: dangerous
-->
<permission android:name="com.android.voicemail.permission.ADD_VOICEMAIL"
- android:permissionGroup="android.permission-group.PHONE"
- android:label="@string/permlab_addVoicemail"
- android:description="@string/permdesc_addVoicemail"
- android:protectionLevel="dangerous" />
+ android:permissionGroup="android.permission-group.PHONE"
+ android:label="@string/permlab_addVoicemail"
+ android:description="@string/permdesc_addVoicemail"
+ android:protectionLevel="dangerous" />
<!-- Allows an application to use SIP service.
<p>Protection level: dangerous
-->
<permission android:name="android.permission.USE_SIP"
- android:permissionGroup="android.permission-group.PHONE"
- android:description="@string/permdesc_use_sip"
- android:label="@string/permlab_use_sip"
- android:protectionLevel="dangerous"/>
+ android:permissionGroup="android.permission-group.PHONE"
+ android:description="@string/permdesc_use_sip"
+ android:label="@string/permlab_use_sip"
+ android:protectionLevel="dangerous"/>
<!-- Allows an application to see the number being dialed during an outgoing
call with the option to redirect the call to a different number or
@@ -830,10 +826,10 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.PROCESS_OUTGOING_CALLS"
- android:permissionGroup="android.permission-group.PHONE"
- android:label="@string/permlab_processOutgoingCalls"
- android:description="@string/permdesc_processOutgoingCalls"
- android:protectionLevel="dangerous" />
+ android:permissionGroup="android.permission-group.PHONE"
+ android:label="@string/permlab_processOutgoingCalls"
+ android:description="@string/permdesc_processOutgoingCalls"
+ android:protectionLevel="dangerous" />
<!-- ====================================================================== -->
<!-- Permissions for accessing the device microphone -->
@@ -844,19 +840,19 @@
microphone audio from the device. Note that phone calls also capture audio
but are in a separate (more visible) permission group. -->
<permission-group android:name="android.permission-group.MICROPHONE"
- android:icon="@drawable/perm_group_microphone"
- android:label="@string/permgrouplab_microphone"
- android:description="@string/permgroupdesc_microphone"
- android:priority="600" />
+ android:icon="@drawable/perm_group_microphone"
+ android:label="@string/permgrouplab_microphone"
+ android:description="@string/permgroupdesc_microphone"
+ android:priority="600" />
<!-- Allows an application to record audio.
<p>Protection level: dangerous
-->
<permission android:name="android.permission.RECORD_AUDIO"
- android:permissionGroup="android.permission-group.MICROPHONE"
- android:label="@string/permlab_recordAudio"
- android:description="@string/permdesc_recordAudio"
- android:protectionLevel="dangerous"/>
+ android:permissionGroup="android.permission-group.MICROPHONE"
+ android:label="@string/permlab_recordAudio"
+ android:description="@string/permdesc_recordAudio"
+ android:protectionLevel="dangerous"/>
<!-- ====================================================================== -->
<!-- Permissions for accessing the UCE Service -->
@@ -866,15 +862,15 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.ACCESS_UCE_PRESENCE_SERVICE"
- android:permissionGroup="android.permission-group.PHONE"
- android:protectionLevel="signatureOrSystem"/>
+ android:permissionGroup="android.permission-group.PHONE"
+ android:protectionLevel="signatureOrSystem"/>
<!-- @hide Allows an application to Access UCE-OPTIONS.
<p>Protection level: dangerous
-->
<permission android:name="android.permission.ACCESS_UCE_OPTIONS_SERVICE"
- android:permissionGroup="android.permission-group.PHONE"
- android:protectionLevel="signatureOrSystem"/>
+ android:permissionGroup="android.permission-group.PHONE"
+ android:protectionLevel="signatureOrSystem"/>
@@ -886,10 +882,10 @@
<!-- Used for permissions that are associated with accessing
camera or capturing images/video from the device. -->
<permission-group android:name="android.permission-group.CAMERA"
- android:icon="@drawable/perm_group_camera"
- android:label="@string/permgrouplab_camera"
- android:description="@string/permgroupdesc_camera"
- android:priority="700" />
+ android:icon="@drawable/perm_group_camera"
+ android:label="@string/permgrouplab_camera"
+ android:description="@string/permgroupdesc_camera"
+ android:priority="700" />
<!-- Required to be able to access the camera device.
<p>This will automatically enforce the <a
@@ -901,10 +897,10 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.CAMERA"
- android:permissionGroup="android.permission-group.CAMERA"
- android:label="@string/permlab_camera"
- android:description="@string/permdesc_camera"
- android:protectionLevel="dangerous" />
+ android:permissionGroup="android.permission-group.CAMERA"
+ android:label="@string/permlab_camera"
+ android:description="@string/permdesc_camera"
+ android:protectionLevel="dangerous" />
<!-- ====================================================================== -->
@@ -915,28 +911,28 @@
<!-- Used for permissions that are associated with accessing
camera or capturing images/video from the device. -->
<permission-group android:name="android.permission-group.SENSORS"
- android:icon="@drawable/perm_group_sensors"
- android:label="@string/permgrouplab_sensors"
- android:description="@string/permgroupdesc_sensors"
- android:priority="800" />
+ android:icon="@drawable/perm_group_sensors"
+ android:label="@string/permgrouplab_sensors"
+ android:description="@string/permgroupdesc_sensors"
+ android:priority="800" />
<!-- Allows an application to access data from sensors that the user uses to
measure what is happening inside his/her body, such as heart rate.
<p>Protection level: dangerous -->
<permission android:name="android.permission.BODY_SENSORS"
- android:permissionGroup="android.permission-group.SENSORS"
- android:label="@string/permlab_bodySensors"
- android:description="@string/permdesc_bodySensors"
- android:protectionLevel="dangerous" />
+ android:permissionGroup="android.permission-group.SENSORS"
+ android:label="@string/permlab_bodySensors"
+ android:description="@string/permdesc_bodySensors"
+ android:protectionLevel="dangerous" />
<!-- Allows an app to use fingerprint hardware.
<p>Protection level: normal
-->
<permission android:name="android.permission.USE_FINGERPRINT"
- android:permissionGroup="android.permission-group.SENSORS"
- android:label="@string/permlab_useFingerprint"
- android:description="@string/permdesc_useFingerprint"
- android:protectionLevel="normal" />
+ android:permissionGroup="android.permission-group.SENSORS"
+ android:label="@string/permlab_useFingerprint"
+ android:description="@string/permdesc_useFingerprint"
+ android:protectionLevel="normal" />
<!-- ====================================================================== -->
<!-- REMOVED PERMISSIONS -->
@@ -944,78 +940,78 @@
<!-- @hide We need to keep this around for backwards compatibility -->
<permission android:name="android.permission.READ_PROFILE"
- android:protectionLevel="normal"
- android:permissionFlags="removed"/>
+ android:protectionLevel="normal"
+ android:permissionFlags="removed"/>
<!-- @hide We need to keep this around for backwards compatibility -->
<permission android:name="android.permission.WRITE_PROFILE"
- android:protectionLevel="normal"
- android:permissionFlags="removed"/>
+ android:protectionLevel="normal"
+ android:permissionFlags="removed"/>
<!-- @hide We need to keep this around for backwards compatibility -->
<permission android:name="android.permission.READ_SOCIAL_STREAM"
- android:protectionLevel="normal"
- android:permissionFlags="removed"/>
+ android:protectionLevel="normal"
+ android:permissionFlags="removed"/>
<!-- @hide We need to keep this around for backwards compatibility -->
<permission android:name="android.permission.WRITE_SOCIAL_STREAM"
- android:protectionLevel="normal"
- android:permissionFlags="removed"/>
+ android:protectionLevel="normal"
+ android:permissionFlags="removed"/>
<!-- @hide We need to keep this around for backwards compatibility -->
<permission android:name="android.permission.READ_USER_DICTIONARY"
- android:protectionLevel="normal"
- android:permissionFlags="removed"/>
+ android:protectionLevel="normal"
+ android:permissionFlags="removed"/>
<!-- @hide We need to keep this around for backwards compatibility -->
<permission android:name="android.permission.WRITE_USER_DICTIONARY"
- android:protectionLevel="normal"
- android:permissionFlags="removed"/>
+ android:protectionLevel="normal"
+ android:permissionFlags="removed"/>
<!-- @hide We need to keep this around for backwards compatibility -->
<permission android:name="android.permission.WRITE_SMS"
- android:protectionLevel="normal"
- android:permissionFlags="removed"/>
+ android:protectionLevel="normal"
+ android:permissionFlags="removed"/>
<!-- @hide We need to keep this around for backwards compatibility -->
<permission android:name="com.android.browser.permission.READ_HISTORY_BOOKMARKS"
- android:protectionLevel="normal"
- android:permissionFlags="removed"/>
+ android:protectionLevel="normal"
+ android:permissionFlags="removed"/>
<!-- @hide We need to keep this around for backwards compatibility -->
<permission android:name="com.android.browser.permission.WRITE_HISTORY_BOOKMARKS"
- android:protectionLevel="normal"
- android:permissionFlags="removed"/>
+ android:protectionLevel="normal"
+ android:permissionFlags="removed"/>
<!-- @hide We need to keep this around for backwards compatibility -->
<permission android:name="android.permission.AUTHENTICATE_ACCOUNTS"
- android:protectionLevel="normal"
- android:permissionFlags="removed"/>
+ android:protectionLevel="normal"
+ android:permissionFlags="removed"/>
<!-- @hide We need to keep this around for backwards compatibility -->
<permission android:name="android.permission.MANAGE_ACCOUNTS"
- android:protectionLevel="normal"
- android:permissionFlags="removed"/>
+ android:protectionLevel="normal"
+ android:permissionFlags="removed"/>
<!-- @hide We need to keep this around for backwards compatibility -->
<permission android:name="android.permission.USE_CREDENTIALS"
- android:protectionLevel="normal"
- android:permissionFlags="removed"/>
+ android:protectionLevel="normal"
+ android:permissionFlags="removed"/>
<!-- @hide We need to keep this around for backwards compatibility -->
<permission android:name="android.permission.SUBSCRIBED_FEEDS_READ"
- android:protectionLevel="normal"
- android:permissionFlags="removed"/>
+ android:protectionLevel="normal"
+ android:permissionFlags="removed"/>
<!-- @hide We need to keep this around for backwards compatibility -->
<permission android:name="android.permission.SUBSCRIBED_FEEDS_WRITE"
- android:protectionLevel="normal"
- android:permissionFlags="removed"/>
+ android:protectionLevel="normal"
+ android:permissionFlags="removed"/>
<!-- @hide We need to keep this around for backwards compatibility -->
<permission android:name="android.permission.FLASHLIGHT"
- android:protectionLevel="normal"
- android:permissionFlags="removed"/>
+ android:protectionLevel="normal"
+ android:permissionFlags="removed"/>
<!-- ====================================================================== -->
<!-- INSTALL PERMISSIONS -->
@@ -1030,37 +1026,35 @@
to handle the respond-via-message action during incoming calls.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.SEND_RESPOND_VIA_MESSAGE"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows an application to send SMS to premium shortcodes without user permission.
- <p>Not for use by third-party applications.
- @hide -->
+ <p>Not for use by third-party applications. -->
<permission android:name="android.permission.SEND_SMS_NO_CONFIRMATION"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- Allows an application to filter carrier specific sms.
@hide -->
<permission android:name="android.permission.CARRIER_FILTER_SMS"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows an application to receive emergency cell broadcast messages,
to record or display them to the user.
- <p>Not for use by third-party applications.
- @hide -->
+ <p>Not for use by third-party applications. -->
<permission android:name="android.permission.RECEIVE_EMERGENCY_BROADCAST"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- Allows an application to monitor incoming Bluetooth MAP messages, to record
or perform processing on them. -->
<!-- @hide -->
<permission android:name="android.permission.RECEIVE_BLUETOOTH_MAP"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi @hide Allows an application to execute contacts directory search.
This should only be used by ContactsProvider.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.BIND_DIRECTORY_SEARCH"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi @hide Allows an application to modify cell broadcasts through the content provider.
<p>Not for use by third-party applications. -->
@@ -1076,9 +1070,9 @@
<p>Protection level: normal
-->
<permission android:name="com.android.alarm.permission.SET_ALARM"
- android:label="@string/permlab_setAlarm"
- android:description="@string/permdesc_setAlarm"
- android:protectionLevel="normal" />
+ android:label="@string/permlab_setAlarm"
+ android:description="@string/permdesc_setAlarm"
+ android:protectionLevel="normal" />
<!-- =============================================================== -->
<!-- Permissions for accessing the user voicemail -->
@@ -1089,13 +1083,13 @@
<p>Protection level: signature|privileged
-->
<permission android:name="com.android.voicemail.permission.WRITE_VOICEMAIL"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- Allows an application to read voicemails in the system.
<p>Protection level: signature|privileged
-->
<permission android:name="com.android.voicemail.permission.READ_VOICEMAIL"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- ======================================= -->
<!-- Permissions for accessing location info -->
@@ -1106,26 +1100,26 @@
<p>Protection level: normal
-->
<permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS"
- android:label="@string/permlab_accessLocationExtraCommands"
- android:description="@string/permdesc_accessLocationExtraCommands"
- android:protectionLevel="normal" />
+ android:label="@string/permlab_accessLocationExtraCommands"
+ android:description="@string/permdesc_accessLocationExtraCommands"
+ android:protectionLevel="normal" />
<!-- @SystemApi Allows an application to install a location provider into the Location Manager.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.INSTALL_LOCATION_PROVIDER"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi @hide Allows HDMI-CEC service to access device and configuration files.
This should only be used by HDMI-CEC service.
-->
<permission android:name="android.permission.HDMI_CEC"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows an application to use location features in hardware,
such as the geofencing api.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.LOCATION_HARDWARE"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<uses-permission android:name="android.permission.LOCATION_HARDWARE"/>
<!-- @SystemApi Allows an application to create mock location providers for testing.
@@ -1133,7 +1127,7 @@
@hide
-->
<permission android:name="android.permission.ACCESS_MOCK_LOCATION"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- ======================================= -->
<!-- Permissions for accessing networks -->
@@ -1144,73 +1138,73 @@
<p>Protection level: normal
-->
<permission android:name="android.permission.INTERNET"
- android:description="@string/permdesc_createNetworkSockets"
- android:label="@string/permlab_createNetworkSockets"
- android:protectionLevel="normal" />
+ android:description="@string/permdesc_createNetworkSockets"
+ android:label="@string/permlab_createNetworkSockets"
+ android:protectionLevel="normal" />
<!-- Allows applications to access information about networks.
<p>Protection level: normal
-->
<permission android:name="android.permission.ACCESS_NETWORK_STATE"
- android:description="@string/permdesc_accessNetworkState"
- android:label="@string/permlab_accessNetworkState"
- android:protectionLevel="normal" />
+ android:description="@string/permdesc_accessNetworkState"
+ android:label="@string/permlab_accessNetworkState"
+ android:protectionLevel="normal" />
<!-- Allows applications to access information about Wi-Fi networks.
<p>Protection level: normal
-->
<permission android:name="android.permission.ACCESS_WIFI_STATE"
- android:description="@string/permdesc_accessWifiState"
- android:label="@string/permlab_accessWifiState"
- android:protectionLevel="normal" />
+ android:description="@string/permdesc_accessWifiState"
+ android:label="@string/permlab_accessWifiState"
+ android:protectionLevel="normal" />
<!-- Allows applications to change Wi-Fi connectivity state.
<p>Protection level: normal
-->
<permission android:name="android.permission.CHANGE_WIFI_STATE"
- android:description="@string/permdesc_changeWifiState"
- android:label="@string/permlab_changeWifiState"
- android:protectionLevel="normal" />
+ android:description="@string/permdesc_changeWifiState"
+ android:label="@string/permlab_changeWifiState"
+ android:protectionLevel="normal" />
<!-- @SystemApi @hide Allows applications to read Wi-Fi credential.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.READ_WIFI_CREDENTIAL"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi @hide Allows applications to change tether state and run
tether carrier provisioning.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.TETHER_PRIVILEGED"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi @hide Allow system apps to receive broadcast
when a wifi network credential is changed.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.RECEIVE_WIFI_CREDENTIAL_CHANGE"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi @hide Allows an application to modify any wifi configuration, even if created
by another application. Once reconfigured the original creator cannot make any further
modifications.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.OVERRIDE_WIFI_CONFIG"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @hide -->
<permission android:name="android.permission.ACCESS_WIMAX_STATE"
- android:description="@string/permdesc_accessWimaxState"
- android:label="@string/permlab_accessWimaxState"
- android:protectionLevel="normal" />
+ android:description="@string/permdesc_accessWimaxState"
+ android:label="@string/permlab_accessWimaxState"
+ android:protectionLevel="normal" />
<!-- @hide -->
<permission android:name="android.permission.CHANGE_WIMAX_STATE"
- android:description="@string/permdesc_changeWimaxState"
- android:label="@string/permlab_changeWimaxState"
- android:protectionLevel="normal" />
+ android:description="@string/permdesc_changeWimaxState"
+ android:label="@string/permlab_changeWimaxState"
+ android:protectionLevel="normal" />
<!-- Allows applications to act as network scorers. @hide @SystemApi-->
<permission android:name="android.permission.SCORE_NETWORKS"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- ======================================= -->
<!-- Permissions for short range, peripheral networks -->
@@ -1221,68 +1215,68 @@
<p>Protection level: normal
-->
<permission android:name="android.permission.BLUETOOTH"
- android:description="@string/permdesc_bluetooth"
- android:label="@string/permlab_bluetooth"
- android:protectionLevel="normal" />
+ android:description="@string/permdesc_bluetooth"
+ android:label="@string/permlab_bluetooth"
+ android:protectionLevel="normal" />
<!-- Allows applications to discover and pair bluetooth devices.
<p>Protection level: normal
-->
<permission android:name="android.permission.BLUETOOTH_ADMIN"
- android:description="@string/permdesc_bluetoothAdmin"
- android:label="@string/permlab_bluetoothAdmin"
- android:protectionLevel="normal" />
+ android:description="@string/permdesc_bluetoothAdmin"
+ android:label="@string/permlab_bluetoothAdmin"
+ android:protectionLevel="normal" />
<!-- @SystemApi Allows applications to pair bluetooth devices without user interaction, and to
allow or disallow phonebook access or message access.
This is not available to third party applications. -->
<permission android:name="android.permission.BLUETOOTH_PRIVILEGED"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- Control access to email providers exclusively for Bluetooth
@hide
-->
<permission android:name="android.permission.BLUETOOTH_MAP"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Allows bluetooth stack to access files
@hide This should only be used by Bluetooth apk.
-->
<permission android:name="android.permission.BLUETOOTH_STACK"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Allows applications to perform I/O operations over NFC.
<p>Protection level: normal
-->
<permission android:name="android.permission.NFC"
- android:description="@string/permdesc_nfc"
- android:label="@string/permlab_nfc"
- android:protectionLevel="normal" />
+ android:description="@string/permdesc_nfc"
+ android:label="@string/permlab_nfc"
+ android:protectionLevel="normal" />
<!-- @SystemApi Allows an internal user to use privileged ConnectivityManager APIs.
@hide -->
<permission android:name="android.permission.CONNECTIVITY_INTERNAL"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- Allows a system application to access hardware packet offload capabilities.
@hide -->
<permission android:name="android.permission.PACKET_KEEPALIVE_OFFLOAD"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi
@hide -->
<permission android:name="android.permission.RECEIVE_DATA_ACTIVITY_CHANGE"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows access to the loop radio (Android@Home mesh network) device.
@hide -->
<permission android:name="android.permission.LOOP_RADIO"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- Allows sending and receiving handover transfer status from Wifi and Bluetooth
@hide -->
<permission android:name="android.permission.NFC_HANDOVER_STATUS"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- ================================== -->
<!-- Permissions for accessing accounts -->
@@ -1301,15 +1295,15 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.GET_ACCOUNTS"
- android:permissionGroup="android.permission-group.CONTACTS"
- android:protectionLevel="dangerous"
- android:description="@string/permdesc_getAccounts"
- android:label="@string/permlab_getAccounts" />
+ android:permissionGroup="android.permission-group.CONTACTS"
+ android:protectionLevel="dangerous"
+ android:description="@string/permdesc_getAccounts"
+ android:label="@string/permlab_getAccounts" />
<!-- @SystemApi Allows applications to call into AccountAuthenticators.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.ACCOUNT_MANAGER"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- ================================== -->
<!-- Permissions for accessing hardware that may effect battery life-->
@@ -1320,34 +1314,34 @@
<p>Protection level: normal
-->
<permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE"
- android:description="@string/permdesc_changeWifiMulticastState"
- android:label="@string/permlab_changeWifiMulticastState"
- android:protectionLevel="normal" />
+ android:description="@string/permdesc_changeWifiMulticastState"
+ android:label="@string/permlab_changeWifiMulticastState"
+ android:protectionLevel="normal" />
<!-- Allows access to the vibrator.
<p>Protection level: normal
-->
<permission android:name="android.permission.VIBRATE"
- android:label="@string/permlab_vibrate"
- android:description="@string/permdesc_vibrate"
- android:protectionLevel="normal" />
+ android:label="@string/permlab_vibrate"
+ android:description="@string/permdesc_vibrate"
+ android:protectionLevel="normal" />
<!-- Allows using PowerManager WakeLocks to keep processor from sleeping or screen
from dimming.
<p>Protection level: normal
-->
<permission android:name="android.permission.WAKE_LOCK"
- android:label="@string/permlab_wakeLock"
- android:description="@string/permdesc_wakeLock"
- android:protectionLevel="normal" />
+ android:label="@string/permlab_wakeLock"
+ android:description="@string/permdesc_wakeLock"
+ android:protectionLevel="normal" />
<!-- Allows using the device's IR transmitter, if available.
<p>Protection level: normal
-->
<permission android:name="android.permission.TRANSMIT_IR"
- android:label="@string/permlab_transmitIr"
- android:description="@string/permdesc_transmitIr"
- android:protectionLevel="normal" />
+ android:label="@string/permlab_transmitIr"
+ android:description="@string/permdesc_transmitIr"
+ android:protectionLevel="normal" />
<!-- ==================================================== -->
<!-- Permissions related to changing audio settings -->
@@ -1358,9 +1352,9 @@
<p>Protection level: normal
-->
<permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"
- android:label="@string/permlab_modifyAudioSettings"
- android:description="@string/permdesc_modifyAudioSettings"
- android:protectionLevel="normal" />
+ android:label="@string/permlab_modifyAudioSettings"
+ android:description="@string/permdesc_modifyAudioSettings"
+ android:protectionLevel="normal" />
<!-- ================================== -->
<!-- Permissions for accessing hardware -->
@@ -1370,53 +1364,53 @@
<!-- @SystemApi Allows an application to manage preferences and permissions for USB devices
@hide -->
<permission android:name="android.permission.MANAGE_USB"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows an application to access the MTP USB kernel driver.
For use only by the device side MTP implementation.
@hide -->
<permission android:name="android.permission.ACCESS_MTP"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows access to hardware peripherals. Intended only for hardware testing.
<p>Not for use by third-party applications.
@hide
-->
<permission android:name="android.permission.HARDWARE_TEST"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @SystemApi Allows access to FM
@hide This is not a third-party API (intended for system apps).-->
<permission android:name="android.permission.ACCESS_FM_RADIO"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- Allows access to configure network interfaces, configure/use IPSec, etc.
@hide -->
<permission android:name="android.permission.NET_ADMIN"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Allows registration for remote audio playback. @hide -->
<permission android:name="android.permission.REMOTE_AUDIO_PLAYBACK"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @SystemApi Allows TvInputService to access underlying TV input hardware such as
built-in tuners and HDMI-in's.
@hide This should only be used by OEM's TvInputService's.
-->
<permission android:name="android.permission.TV_INPUT_HARDWARE"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows to capture a frame of TV input hardware such as
built-in tuners and HDMI-in's.
@hide <p>Not for use by third-party applications.
-->
<permission android:name="android.permission.CAPTURE_TV_INPUT"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @hide Allows TvInputService to access DVB device.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.DVB_DEVICE"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows reading the OEM unlock state
@hide <p>Not for use by third-party applications. -->
@@ -1426,17 +1420,17 @@
<!-- @hide Allows enabling/disabling OEM unlock
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.OEM_UNLOCK_STATE"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @hide Allows querying state of PersistentDataBlock
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.ACCESS_PDB_STATE"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @hide Allows system update service to notify device owner about pending updates.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.NOTIFY_PENDING_SYSTEM_UPDATE"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- =========================================== -->
<!-- Permissions associated with camera and image capture -->
@@ -1447,12 +1441,12 @@
a camera is in use by an application.
@hide -->
<permission android:name="android.permission.CAMERA_DISABLE_TRANSMIT_LED"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- Allows sending the camera service notifications about system-wide events.
@hide -->
<permission android:name="android.permission.CAMERA_SEND_SYSTEM_EVENTS"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- =========================================== -->
<!-- Permissions associated with telephony state -->
@@ -1463,50 +1457,50 @@
Does not include placing calls.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.MODIFY_PHONE_STATE"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- Allows read only access to precise phone state.
@hide Pending API council approval -->
<permission android:name="android.permission.READ_PRECISE_PHONE_STATE"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows read access to privileged phone state.
@hide Used internally. -->
<permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Protects the ability to register any PhoneAccount with
PhoneAccount#CAPABILITY_SIM_SUBSCRIPTION. This capability indicates that the PhoneAccount
corresponds to a device SIM.
@hide -->
<permission android:name="android.permission.REGISTER_SIM_SUBSCRIPTION"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Protects the ability to register any PhoneAccount with
PhoneAccount#CAPABILITY_CALL_PROVIDER.
@hide -->
<permission android:name="android.permission.REGISTER_CALL_PROVIDER"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Protects the ability to register any PhoneAccount with
PhoneAccount#CAPABILITY_CONNECTION_MANAGER
@hide -->
<permission android:name="android.permission.REGISTER_CONNECTION_MANAGER"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- Must be required by a {@link android.telecom.InCallService},
to ensure that only the system can bind to it.
<p>Protection level: signature|privileged
-->
<permission android:name="android.permission.BIND_INCALL_SERVICE"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- Must be required by a {@link android.telecom.CallScreeningService},
to ensure that only the system can bind to it.
<p>Protection level: signature|privileged
-->
<permission android:name="android.permission.BIND_SCREENING_SERVICE"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- Must be required by a {@link android.telecom.ConnectionService},
to ensure that only the system can bind to it.
@@ -1515,24 +1509,24 @@
@SystemApi
@hide -->
<permission android:name="android.permission.BIND_CONNECTION_SERVICE"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- Must be required by a {@link android.telecom.ConnectionService},
to ensure that only the system can bind to it.
<p>Protection level: signature|privileged
-->
<permission android:name="android.permission.BIND_TELECOM_CONNECTION_SERVICE"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows an application to control the in-call experience.
@hide -->
<permission android:name="android.permission.CONTROL_INCALL_EXPERIENCE"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- Allows an application to receive STK related commands.
@hide -->
<permission android:name="android.permission.RECEIVE_STK_COMMANDS"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- ================================== -->
<!-- Permissions for sdcard interaction -->
@@ -1542,7 +1536,7 @@
<!-- @SystemApi Allows an application to write to internal media storage
@hide -->
<permission android:name="android.permission.WRITE_MEDIA_STORAGE"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- Allows an application to manage access to documents, usually as part
of a document picker.
@@ -1552,14 +1546,14 @@
<p>Protection level: signature
-->
<permission android:name="android.permission.MANAGE_DOCUMENTS"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @hide Allows an application to cache content.
<p>Not for use by third-party applications.
<p>Protection level: signature
-->
<permission android:name="android.permission.CACHE_CONTENT"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- ================================== -->
<!-- Permissions for screenlock -->
@@ -1570,9 +1564,9 @@
<p>Protection level: normal
-->
<permission android:name="android.permission.DISABLE_KEYGUARD"
- android:description="@string/permdesc_disableKeyguard"
- android:label="@string/permlab_disableKeyguard"
- android:protectionLevel="normal" />
+ android:description="@string/permdesc_disableKeyguard"
+ android:label="@string/permlab_disableKeyguard"
+ android:protectionLevel="normal" />
<!-- ================================== -->
<!-- Permissions to access other installed applications -->
@@ -1581,9 +1575,9 @@
<!-- @deprecated No longer enforced. -->
<permission android:name="android.permission.GET_TASKS"
- android:label="@string/permlab_getTasks"
- android:description="@string/permdesc_getTasks"
- android:protectionLevel="normal" />
+ android:label="@string/permlab_getTasks"
+ android:description="@string/permdesc_getTasks"
+ android:protectionLevel="normal" />
<!-- New version of GET_TASKS that apps can request, since GET_TASKS doesn't really
give access to task information. We need this new one because there are
@@ -1596,32 +1590,32 @@
@hide
@SystemApi -->
<permission android:name="android.permission.REAL_GET_TASKS"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- Allows an application to start a task from a ActivityManager#RecentTaskInfo.
@hide -->
<permission android:name="android.permission.START_TASKS_FROM_RECENTS"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi @hide Allows an application to call APIs that allow it to do interactions
across the users on the device, using singleton services and
user-targeted broadcasts. This permission is not available to
third party applications. -->
<permission android:name="android.permission.INTERACT_ACROSS_USERS"
- android:protectionLevel="signature|privileged|development" />
+ android:protectionLevel="signature|privileged|development" />
<!-- @SystemApi Fuller form of {@link android.Manifest.permission#INTERACT_ACROSS_USERS}
that removes restrictions on where broadcasts can be sent and allows other
types of interactions
@hide -->
<permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL"
- android:protectionLevel="signature|installer" />
+ android:protectionLevel="signature|installer" />
<!-- @SystemApi @hide Allows an application to call APIs that allow it to query and manage
users on the device. This permission is not available to
third party applications. -->
<permission android:name="android.permission.MANAGE_USERS"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @hide Allows an application to create, remove users and get the list of
users on the device. Applications holding this permission can only create restricted,
@@ -1634,69 +1628,69 @@
<!-- @hide Allows an application to set the profile owners and the device owner.
This permission is not available to third party applications.-->
<permission android:name="android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS"
- android:protectionLevel="signature"
- android:label="@string/permlab_manageProfileAndDeviceOwners"
- android:description="@string/permdesc_manageProfileAndDeviceOwners" />
+ android:protectionLevel="signature"
+ android:label="@string/permlab_manageProfileAndDeviceOwners"
+ android:description="@string/permdesc_manageProfileAndDeviceOwners" />
<!-- Allows an application to get full detailed information about
recently running tasks, with full fidelity to the real state.
@hide -->
<permission android:name="android.permission.GET_DETAILED_TASKS"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Allows an application to change the Z-order of tasks.
<p>Protection level: normal
-->
<permission android:name="android.permission.REORDER_TASKS"
- android:label="@string/permlab_reorderTasks"
- android:description="@string/permdesc_reorderTasks"
- android:protectionLevel="normal" />
+ android:label="@string/permlab_reorderTasks"
+ android:description="@string/permdesc_reorderTasks"
+ android:protectionLevel="normal" />
<!-- @hide Allows an application to change to remove/kill tasks -->
<permission android:name="android.permission.REMOVE_TASKS"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @SystemApi @hide Allows an application to create/manage/remove stacks -->
<permission android:name="android.permission.MANAGE_ACTIVITY_STACKS"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- Allows an application to start any activity, regardless of permission
protection or exported state.
@hide -->
<permission android:name="android.permission.START_ANY_ACTIVITY"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @deprecated The {@link android.app.ActivityManager#restartPackage}
API is no longer supported. -->
<permission android:name="android.permission.RESTART_PACKAGES"
- android:label="@string/permlab_killBackgroundProcesses"
- android:description="@string/permdesc_killBackgroundProcesses"
- android:protectionLevel="normal" />
+ android:label="@string/permlab_killBackgroundProcesses"
+ android:description="@string/permdesc_killBackgroundProcesses"
+ android:protectionLevel="normal" />
<!-- Allows an application to call
{@link android.app.ActivityManager#killBackgroundProcesses}.
<p>Protection level: normal
-->
<permission android:name="android.permission.KILL_BACKGROUND_PROCESSES"
- android:label="@string/permlab_killBackgroundProcesses"
- android:description="@string/permdesc_killBackgroundProcesses"
- android:protectionLevel="normal" />
+ android:label="@string/permlab_killBackgroundProcesses"
+ android:description="@string/permdesc_killBackgroundProcesses"
+ android:protectionLevel="normal" />
<!-- @SystemApi @hide Allows an application to query process states and current
OOM adjustment scores.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.GET_PROCESS_STATE_AND_OOM_SCORE"
- android:protectionLevel="signature|privileged|development" />
+ android:protectionLevel="signature|privileged|development" />
<!-- @SystemApi @hide Allows an application to retrieve a package's importance.
This permission is not available to third party applications. -->
<permission android:name="android.permission.GET_PACKAGE_IMPORTANCE"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- Allows use of PendingIntent.getIntent().
@hide -->
<permission android:name="android.permission.GET_INTENT_SENDER_INTENT"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- ================================== -->
<!-- Permissions affecting the display of other applications -->
@@ -1719,9 +1713,9 @@
Settings.canDrawOverlays()}.
<p>Protection level: signature -->
<permission android:name="android.permission.SYSTEM_ALERT_WINDOW"
- android:label="@string/permlab_systemAlertWindow"
- android:description="@string/permdesc_systemAlertWindow"
- android:protectionLevel="signature|preinstalled|appop|pre23|development" />
+ android:label="@string/permlab_systemAlertWindow"
+ android:description="@string/permdesc_systemAlertWindow"
+ android:protectionLevel="signature|preinstalled|appop|pre23|development" />
<!-- ================================== -->
<!-- Permissions affecting the system wallpaper -->
@@ -1732,17 +1726,17 @@
<p>Protection level: normal
-->
<permission android:name="android.permission.SET_WALLPAPER"
- android:label="@string/permlab_setWallpaper"
- android:description="@string/permdesc_setWallpaper"
- android:protectionLevel="normal" />
+ android:label="@string/permlab_setWallpaper"
+ android:description="@string/permdesc_setWallpaper"
+ android:protectionLevel="normal" />
<!-- Allows applications to set the wallpaper hints.
<p>Protection level: normal
-->
<permission android:name="android.permission.SET_WALLPAPER_HINTS"
- android:label="@string/permlab_setWallpaperHints"
- android:description="@string/permdesc_setWallpaperHints"
- android:protectionLevel="normal" />
+ android:label="@string/permlab_setWallpaperHints"
+ android:description="@string/permdesc_setWallpaperHints"
+ android:protectionLevel="normal" />
<!-- ============================================ -->
<!-- Permissions for changing the system clock -->
@@ -1752,15 +1746,15 @@
<!-- @SystemApi Allows applications to set the system time.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.SET_TIME"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- Allows applications to set the system time zone.
<p>Protection level: normal
-->
<permission android:name="android.permission.SET_TIME_ZONE"
- android:label="@string/permlab_setTimeZone"
- android:description="@string/permdesc_setTimeZone"
- android:protectionLevel="normal" />
+ android:label="@string/permlab_setTimeZone"
+ android:description="@string/permdesc_setTimeZone"
+ android:protectionLevel="normal" />
<!-- ==================================================== -->
<!-- Permissions related to changing status bar -->
@@ -1771,9 +1765,9 @@
<p>Protection level: normal
-->
<permission android:name="android.permission.EXPAND_STATUS_BAR"
- android:label="@string/permlab_expandStatusBar"
- android:description="@string/permdesc_expandStatusBar"
- android:protectionLevel="normal" />
+ android:label="@string/permlab_expandStatusBar"
+ android:description="@string/permdesc_expandStatusBar"
+ android:protectionLevel="normal" />
<!-- ============================================================== -->
<!-- Permissions related to adding/removing shortcuts from Launcher -->
@@ -1784,17 +1778,17 @@
<p>Protection level: normal
-->
<permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT"
- android:label="@string/permlab_install_shortcut"
- android:description="@string/permdesc_install_shortcut"
- android:protectionLevel="normal"/>
+ android:label="@string/permlab_install_shortcut"
+ android:description="@string/permdesc_install_shortcut"
+ android:protectionLevel="normal"/>
<!-- Allows an application to uninstall a shortcut in Launcher.
<p>Protection level: normal
-->
<permission android:name="com.android.launcher.permission.UNINSTALL_SHORTCUT"
- android:label="@string/permlab_uninstall_shortcut"
- android:description="@string/permdesc_uninstall_shortcut"
- android:protectionLevel="normal"/>
+ android:label="@string/permlab_uninstall_shortcut"
+ android:description="@string/permdesc_uninstall_shortcut"
+ android:protectionLevel="normal"/>
<!-- ==================================================== -->
<!-- Permissions related to accessing sync settings -->
@@ -1805,25 +1799,25 @@
<p>Protection level: normal
-->
<permission android:name="android.permission.READ_SYNC_SETTINGS"
- android:description="@string/permdesc_readSyncSettings"
- android:label="@string/permlab_readSyncSettings"
- android:protectionLevel="normal" />
+ android:description="@string/permdesc_readSyncSettings"
+ android:label="@string/permlab_readSyncSettings"
+ android:protectionLevel="normal" />
<!-- Allows applications to write the sync settings.
<p>Protection level: normal
-->
<permission android:name="android.permission.WRITE_SYNC_SETTINGS"
- android:description="@string/permdesc_writeSyncSettings"
- android:label="@string/permlab_writeSyncSettings"
- android:protectionLevel="normal" />
+ android:description="@string/permdesc_writeSyncSettings"
+ android:label="@string/permlab_writeSyncSettings"
+ android:protectionLevel="normal" />
<!-- Allows applications to read the sync stats.
<p>Protection level: normal
-->
<permission android:name="android.permission.READ_SYNC_STATS"
- android:description="@string/permdesc_readSyncStats"
- android:label="@string/permlab_readSyncStats"
- android:protectionLevel="normal" />
+ android:description="@string/permdesc_readSyncStats"
+ android:label="@string/permlab_readSyncStats"
+ android:protectionLevel="normal" />
<!-- ============================================ -->
<!-- Permissions for low-level system interaction -->
@@ -1832,12 +1826,12 @@
<!-- @SystemApi @hide Change the screen compatibility mode of applications -->
<permission android:name="android.permission.SET_SCREEN_COMPATIBILITY"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @SystemApi Allows an application to modify the current configuration, such
as locale. -->
<permission android:name="android.permission.CHANGE_CONFIGURATION"
- android:protectionLevel="signature|privileged|development" />
+ android:protectionLevel="signature|privileged|development" />
<!-- Allows an application to read or write the system settings.
@@ -1852,51 +1846,51 @@
<p>Protection level: signature
-->
<permission android:name="android.permission.WRITE_SETTINGS"
- android:label="@string/permlab_writeSettings"
- android:description="@string/permdesc_writeSettings"
- android:protectionLevel="signature|preinstalled|appop|pre23" />
+ android:label="@string/permlab_writeSettings"
+ android:description="@string/permdesc_writeSettings"
+ android:protectionLevel="signature|preinstalled|appop|pre23" />
<!-- @SystemApi Allows an application to modify the Google service map.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.WRITE_GSERVICES"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows an application to call
{@link android.app.ActivityManager#forceStopPackage}.
@hide -->
<permission android:name="android.permission.FORCE_STOP_PACKAGES"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi @hide Allows an application to retrieve the content of the active window
An active window is the window that has fired an accessibility event. -->
<permission android:name="android.permission.RETRIEVE_WINDOW_CONTENT"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Modify the global animation scaling factor.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.SET_ANIMATION_SCALE"
- android:protectionLevel="signature|privileged|development" />
+ android:protectionLevel="signature|privileged|development" />
<!-- @deprecated This functionality will be removed in the future; please do
not use. Allow an application to make its activities persistent. -->
<permission android:name="android.permission.PERSISTENT_ACTIVITY"
- android:label="@string/permlab_persistentActivity"
- android:description="@string/permdesc_persistentActivity"
- android:protectionLevel="normal" />
+ android:label="@string/permlab_persistentActivity"
+ android:description="@string/permdesc_persistentActivity"
+ android:protectionLevel="normal" />
<!-- Allows an application to find out the space used by any package.
<p>Protection level: normal
-->
<permission android:name="android.permission.GET_PACKAGE_SIZE"
- android:label="@string/permlab_getPackageSize"
- android:description="@string/permdesc_getPackageSize"
- android:protectionLevel="normal" />
+ android:label="@string/permlab_getPackageSize"
+ android:description="@string/permdesc_getPackageSize"
+ android:protectionLevel="normal" />
<!-- @deprecated No longer useful, see
{@link android.content.pm.PackageManager#addPackageToPreferred}
for details. -->
<permission android:name="android.permission.SET_PREFERRED_APPLICATIONS"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Allows an application to receive the
{@link android.content.Intent#ACTION_BOOT_COMPLETED} that is
@@ -1912,9 +1906,9 @@
<p>Protection level: normal
-->
<permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"
- android:label="@string/permlab_receiveBootCompleted"
- android:description="@string/permdesc_receiveBootCompleted"
- android:protectionLevel="normal" />
+ android:label="@string/permlab_receiveBootCompleted"
+ android:description="@string/permdesc_receiveBootCompleted"
+ android:protectionLevel="normal" />
<!-- Allows an application to broadcast sticky intents. These are
broadcasts whose data is held by the system after being finished,
@@ -1923,90 +1917,90 @@
<p>Protection level: normal
-->
<permission android:name="android.permission.BROADCAST_STICKY"
- android:label="@string/permlab_broadcastSticky"
- android:description="@string/permdesc_broadcastSticky"
- android:protectionLevel="normal" />
+ android:label="@string/permlab_broadcastSticky"
+ android:description="@string/permdesc_broadcastSticky"
+ android:protectionLevel="normal" />
<!-- @SystemApi Allows mounting and unmounting file systems for removable storage.
<p>Not for use by third-party applications.-->
<permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows formatting file systems for removable storage.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.MOUNT_FORMAT_FILESYSTEMS"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @hide -->
<permission android:name="android.permission.STORAGE_INTERNAL"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Allows access to ASEC non-destructive API calls
@hide -->
<permission android:name="android.permission.ASEC_ACCESS"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Allows creation of ASEC volumes
@hide -->
<permission android:name="android.permission.ASEC_CREATE"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Allows destruction of ASEC volumes
@hide -->
<permission android:name="android.permission.ASEC_DESTROY"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Allows mount / unmount of ASEC volumes
@hide -->
<permission android:name="android.permission.ASEC_MOUNT_UNMOUNT"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Allows rename of ASEC volumes
@hide -->
<permission android:name="android.permission.ASEC_RENAME"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @SystemApi Allows applications to write the apn settings.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.WRITE_APN_SETTINGS"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- Allows applications to change network connectivity state.
<p>Protection level: normal
-->
<permission android:name="android.permission.CHANGE_NETWORK_STATE"
- android:description="@string/permdesc_changeNetworkState"
- android:label="@string/permlab_changeNetworkState"
- android:protectionLevel="normal" />
+ android:description="@string/permdesc_changeNetworkState"
+ android:label="@string/permlab_changeNetworkState"
+ android:protectionLevel="normal" />
<!-- Allows an application to clear the caches of all installed
applications on the device.
<p>Protection level: system|signature
-->
<permission android:name="android.permission.CLEAR_APP_CACHE"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows an application to use any media decoder when decoding for playback
@hide -->
<permission android:name="android.permission.ALLOW_ANY_CODEC_FOR_PLAYBACK"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows an application to install and/or uninstall CA certificates on
behalf of the user.
@hide -->
<permission android:name="android.permission.MANAGE_CA_CERTIFICATES"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows an application to do certain operations needed for
interacting with the recovery (system update) system.
@hide -->
<permission android:name="android.permission.RECOVERY"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- Allows the system to bind to an application's task services
@hide -->
<permission android:name="android.permission.BIND_JOB_SERVICE"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<uses-permission android:name="android.permission.BIND_JOB_SERVICE"/>
<!-- Allows an application to initiate configuration updates
@@ -2015,12 +2009,7 @@
it off to the various individual installer components
@hide -->
<permission android:name="android.permission.UPDATE_CONFIG"
- android:protectionLevel="signature|privileged" />
-
- <!-- Allows the system to reset throttling in shortcut manager.
- @hide -->
- <permission android:name="android.permission.RESET_SHORTCUT_MANAGER_THROTTLING"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature|privileged" />
<!-- ========================================= -->
<!-- Permissions for special development tools -->
@@ -2030,40 +2019,40 @@
<!-- @SystemApi Allows an application to read or write the secure system settings.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.WRITE_SECURE_SETTINGS"
- android:protectionLevel="signature|privileged|development" />
+ android:protectionLevel="signature|privileged|development" />
<!-- @SystemApi Allows an application to retrieve state dump information from system services.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.DUMP"
- android:protectionLevel="signature|privileged|development" />
+ android:protectionLevel="signature|privileged|development" />
<!-- @SystemApi Allows an application to read the low-level system log files.
<p>Not for use by third-party applications, because
Log entries can contain the user's private information. -->
<permission android:name="android.permission.READ_LOGS"
- android:protectionLevel="signature|privileged|development" />
+ android:protectionLevel="signature|privileged|development" />
<!-- @SystemApi Configure an application for debugging.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.SET_DEBUG_APP"
- android:protectionLevel="signature|privileged|development" />
+ android:protectionLevel="signature|privileged|development" />
<!-- @SystemApi Allows an application to set the maximum number of (not needed)
application processes that can be running.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.SET_PROCESS_LIMIT"
- android:protectionLevel="signature|privileged|development" />
+ android:protectionLevel="signature|privileged|development" />
<!-- @SystemApi Allows an application to control whether activities are immediately
finished when put in the background.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.SET_ALWAYS_FINISH"
- android:protectionLevel="signature|privileged|development" />
+ android:protectionLevel="signature|privileged|development" />
<!-- @SystemApi Allow an application to request that a signal be sent to all persistent processes.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.SIGNAL_PERSISTENT_PROCESSES"
- android:protectionLevel="signature|privileged|development" />
+ android:protectionLevel="signature|privileged|development" />
<!-- ==================================== -->
<!-- Private permissions -->
@@ -2072,34 +2061,34 @@
<!-- @SystemApi Allows access to the list of accounts in the Accounts Service. -->
<permission android:name="android.permission.GET_ACCOUNTS_PRIVILEGED"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- Allows but does not guarantee access to user passwords at the conclusion of add account
@hide -->
<permission android:name="android.permission.GET_PASSWORD"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @SystemApi Allows applications to RW to diagnostic resources.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.DIAGNOSTIC"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @SystemApi Allows an application to open, close, or disable the status bar
and its icons.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.STATUS_BAR"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- Allows an application to be the status bar. Currently used only by SystemUI.apk
@hide -->
<permission android:name="android.permission.STATUS_BAR_SERVICE"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Allows an application to bind to third party quick settings tiles.
<p>Should only be requested by the System, should be required by
TileService declarations.-->
<permission android:name="android.permission.BIND_QUICK_SETTINGS_TILE"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @SystemApi Allows an application to force a BACK operation on whatever is the
top activity.
@@ -2107,28 +2096,28 @@
@hide
-->
<permission android:name="android.permission.FORCE_BACK"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @SystemApi Allows an application to update device statistics.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.UPDATE_DEVICE_STATS"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi @hide Allows an application to collect battery statistics -->
<permission android:name="android.permission.GET_APP_OPS_STATS"
- android:protectionLevel="signature|privileged|development" />
+ android:protectionLevel="signature|privileged|development" />
<!-- @SystemApi Allows an application to update application operation statistics. Not for
use by third party apps.
@hide -->
<permission android:name="android.permission.UPDATE_APP_OPS_STATS"
- android:protectionLevel="signature|privileged|installer" />
+ android:protectionLevel="signature|privileged|installer" />
<!-- @SystemApi Allows an application to update the user app op restrictions.
Not for use by third party apps.
@hide -->
<permission android:name="android.permission.MANAGE_APP_OPS_RESTRICTIONS"
- android:protectionLevel="signature|installer" />
+ android:protectionLevel="signature|installer" />
<!-- @SystemApi Allows an application to open windows that are for use by parts
of the system user interface.
@@ -2136,7 +2125,7 @@
@hide
-->
<permission android:name="android.permission.INTERNAL_SYSTEM_WINDOW"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @SystemApi Allows an application to manage (create, destroy,
Z-order) application tokens in the window manager.
@@ -2144,17 +2133,17 @@
@hide
-->
<permission android:name="android.permission.MANAGE_APP_TOKENS"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Allows System UI to register listeners for events from Window Manager.
@hide -->
<permission android:name="android.permission.REGISTER_WINDOW_MANAGER_LISTENERS"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @hide Allows the application to temporarily freeze the screen for a
full-screen transition. -->
<permission android:name="android.permission.FREEZE_SCREEN"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @SystemApi Allows an application to inject user events (keys, touch, trackball)
into the event stream and deliver them to ANY window. Without this
@@ -2163,24 +2152,24 @@
@hide
-->
<permission android:name="android.permission.INJECT_EVENTS"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @hide Allows an application to register an input filter which filters the stream
of user events (keys, touch, trackball) before they are dispatched to any window. -->
<permission android:name="android.permission.FILTER_EVENTS"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @hide Allows an application to retrieve the window token from the accessibility manager. -->
<permission android:name="android.permission.RETRIEVE_WINDOW_TOKEN"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @hide Allows an application to collect frame statistics -->
<permission android:name="android.permission.FRAME_STATS"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @hide Allows an application to temporary enable accessibility on the device. -->
<permission android:name="android.permission.TEMPORARY_ENABLE_ACCESSIBILITY"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @SystemApi Allows an application to watch and control how activities are
started globally in the system. Only for is in debugging
@@ -2189,13 +2178,13 @@
@hide
-->
<permission android:name="android.permission.SET_ACTIVITY_WATCHER"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @SystemApi Allows an application to call the activity manager shutdown() API
to put the higher-level system there into a shutdown state.
@hide -->
<permission android:name="android.permission.SHUTDOWN"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows an application to tell the activity manager to temporarily
stop application switches, putting it into a special mode that
@@ -2203,7 +2192,7 @@
critical UI such as the home screen.
@hide -->
<permission android:name="android.permission.STOP_APP_SWITCHES"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows an application to retrieve private information about
the current top activity, such as any assist context it can provide.
@@ -2211,42 +2200,42 @@
@hide
-->
<permission android:name="android.permission.GET_TOP_ACTIVITY_INFO"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Allows an application to retrieve the current state of keys and
switches.
<p>Not for use by third-party applications.
@deprecated The API that used this permission has been removed. -->
<permission android:name="android.permission.READ_INPUT_STATE"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Must be required by an {@link android.inputmethodservice.InputMethodService},
to ensure that only the system can bind to it.
<p>Protection level: signature
-->
<permission android:name="android.permission.BIND_INPUT_METHOD"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Must be required by an {@link android.media.midi.MidiDeviceService},
to ensure that only the system can bind to it.
<p>Protection level: signature
-->
<permission android:name="android.permission.BIND_MIDI_DEVICE_SERVICE"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Must be required by an {@link android.accessibilityservice.AccessibilityService},
to ensure that only the system can bind to it.
<p>Protection level: signature
-->
<permission android:name="android.permission.BIND_ACCESSIBILITY_SERVICE"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Must be required by a {@link android.printservice.PrintService},
to ensure that only the system can bind to it.
<p>Protection level: signature
-->
<permission android:name="android.permission.BIND_PRINT_SERVICE"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Must be required by a {@link android.printservice.recommendation.RecommendationService},
to ensure that only the system can bind to it.
@@ -2255,7 +2244,7 @@
<p>Protection level: signature
-->
<permission android:name="android.permission.BIND_PRINT_RECOMMENDATION_SERVICE"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Must be required by a {@link android.nfc.cardemulation.HostApduService}
or {@link android.nfc.cardemulation.OffHostApduService} to ensure that only
@@ -2263,65 +2252,65 @@
<p>Protection level: signature
-->
<permission android:name="android.permission.BIND_NFC_SERVICE"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Must be required by the PrintSpooler to ensure that only the system can bind to it.
@hide -->
<permission android:name="android.permission.BIND_PRINT_SPOOLER_SERVICE"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @SystemApi Must be required by the RuntimePermissionPresenterService to ensure
that only the system can bind to it.
@hide -->
<permission android:name="android.permission.BIND_RUNTIME_PERMISSION_PRESENTER_SERVICE"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Must be required by a TextService (e.g. SpellCheckerService)
to ensure that only the system can bind to it.
<p>Protection level: signature
-->
<permission android:name="android.permission.BIND_TEXT_SERVICE"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Must be required by a {@link android.net.VpnService},
to ensure that only the system can bind to it.
<p>Protection level: signature
-->
<permission android:name="android.permission.BIND_VPN_SERVICE"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Must be required by a {@link android.service.wallpaper.WallpaperService},
to ensure that only the system can bind to it.
<p>Protection level: system|signature
-->
<permission android:name="android.permission.BIND_WALLPAPER"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- Must be required by a {@link android.service.voice.VoiceInteractionService},
to ensure that only the system can bind to it.
<p>Protection level: signature
-->
<permission android:name="android.permission.BIND_VOICE_INTERACTION"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Must be required by hotword enrollment application,
to ensure that only the system can interact with it.
@hide <p>Not for use by third-party applications.</p> -->
<permission android:name="android.permission.MANAGE_VOICE_KEYPHRASES"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- Must be required by a {@link com.android.media.remotedisplay.RemoteDisplayProvider},
to ensure that only the system can bind to it.
@hide -->
<permission android:name="android.permission.BIND_REMOTE_DISPLAY"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Must be required by a {@link android.media.tv.TvInputService}
to ensure that only the system can bind to it.
<p>Protection level: signature
-->
<permission android:name="android.permission.BIND_TV_INPUT"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi
Must be required by a {@link com.android.media.tv.remoteprovider.TvRemoteProvider}
@@ -2330,7 +2319,7 @@
<p>Not for use by third-party applications. </p>
@hide -->
<permission android:name="android.permission.BIND_TV_REMOTE_SERVICE"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi
Must be required for a virtual remote controller for TV.
@@ -2338,32 +2327,32 @@
<p>Not for use by third-party applications. </p>
@hide -->
<permission android:name="android.permission.TV_VIRTUAL_REMOTE_CONTROLLER"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows an application to modify parental controls
<p>Not for use by third-party applications.
@hide -->
<permission android:name="android.permission.MODIFY_PARENTAL_CONTROLS"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- Must be required by a {@link android.media.routing.MediaRouteService}
to ensure that only the system can interact with it.
@hide -->
<permission android:name="android.permission.BIND_ROUTE_PROVIDER"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Must be required by device administration receiver, to ensure that only the
system can interact with it.
<p>Protection level: signature
-->
<permission android:name="android.permission.BIND_DEVICE_ADMIN"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @SystemApi Required to add or remove another application as a device admin.
<p>Not for use by third-party applications.
@hide -->
<permission android:name="android.permission.MANAGE_DEVICE_ADMINS"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows low-level access to setting the orientation (actually
rotation) of the screen.
@@ -2371,33 +2360,33 @@
@hide
-->
<permission android:name="android.permission.SET_ORIENTATION"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @SystemApi Allows low-level access to setting the pointer speed.
<p>Not for use by third-party applications.
@hide
-->
<permission android:name="android.permission.SET_POINTER_SPEED"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Allows low-level access to setting input device calibration.
<p>Not for use by normal applications.
@hide -->
<permission android:name="android.permission.SET_INPUT_CALIBRATION"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Allows low-level access to setting the keyboard layout.
<p>Not for use by third-party applications.
@hide -->
<permission android:name="android.permission.SET_KEYBOARD_LAYOUT"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Allows an application to query tablet mode state and monitor changes
in it.
<p>Not for use by third-party applications.
@hide -->
<permission android:name="android.permission.TABLET_MODE"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Allows an application to request installing packages. Apps
targeting APIs greater than 22 must hold this permission in
@@ -2405,41 +2394,41 @@
<p>Protection level: normal
-->
<permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"
- android:label="@string/permlab_requestInstallPackages"
- android:description="@string/permdesc_requestInstallPackages"
- android:protectionLevel="normal" />
+ android:label="@string/permlab_requestInstallPackages"
+ android:description="@string/permdesc_requestInstallPackages"
+ android:protectionLevel="normal" />
<!-- @SystemApi Allows an application to install packages.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.INSTALL_PACKAGES"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows an application to clear user data.
<p>Not for use by third-party applications
@hide
-->
<permission android:name="android.permission.CLEAR_APP_USER_DATA"
- android:protectionLevel="signature|installer" />
+ android:protectionLevel="signature|installer" />
<!-- @hide Allows an application to get the URI permissions
granted to another application.
<p>Not for use by third-party applications
-->
<permission android:name="android.permission.GET_APP_GRANTED_URI_PERMISSIONS"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @hide Allows an application to clear the URI permissions
granted to another application.
<p>Not for use by third-party applications
-->
<permission
- android:name="android.permission.CLEAR_APP_GRANTED_URI_PERMISSIONS"
- android:protectionLevel="signature" />
+ android:name="android.permission.CLEAR_APP_GRANTED_URI_PERMISSIONS"
+ android:protectionLevel="signature" />
<!-- @SystemApi Allows an application to delete cache files.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.DELETE_CACHE_FILES"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows an application to delete packages.
<p>Not for use by third-party applications.
@@ -2447,251 +2436,251 @@
when the application deleting the package is not the same application that installed the
package. -->
<permission android:name="android.permission.DELETE_PACKAGES"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows an application to move location of installed package.
@hide -->
<permission android:name="android.permission.MOVE_PACKAGE"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows an application to change whether an application component (other than its own) is
enabled or not.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.CHANGE_COMPONENT_ENABLED_STATE"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows an application to grant specific permissions.
@hide -->
<permission android:name="android.permission.GRANT_RUNTIME_PERMISSIONS"
- android:protectionLevel="signature|installer|verifier" />
+ android:protectionLevel="signature|installer|verifier" />
<!-- @SystemApi Allows an app that has this permission and the permissions to install packages
to request certain runtime permissions to be granted at installation.
@hide -->
<permission android:name="android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS"
- android:protectionLevel="signature|installer|verifier" />
+ android:protectionLevel="signature|installer|verifier" />
<!-- @SystemApi Allows an application to revoke specific permissions.
@hide -->
<permission android:name="android.permission.REVOKE_RUNTIME_PERMISSIONS"
- android:protectionLevel="signature|installer|verifier" />
+ android:protectionLevel="signature|installer|verifier" />
<!-- @hide Allows an application to observe permission changes. -->
<permission android:name="android.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows an application to use SurfaceFlinger's low level features.
<p>Not for use by third-party applications.
@hide
-->
<permission android:name="android.permission.ACCESS_SURFACE_FLINGER"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @SystemApi Allows an application to take screen shots and more generally
get access to the frame buffer data.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.READ_FRAME_BUFFER"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- Allows an application to use InputFlinger's low level features.
@hide -->
<permission android:name="android.permission.ACCESS_INPUT_FLINGER"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Allows an application to configure and connect to Wifi displays
@hide -->
<permission android:name="android.permission.CONFIGURE_WIFI_DISPLAY"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Allows an application to control low-level features of Wifi displays
such as opening an RTSP socket. This permission should only be used
by the display manager.
@hide -->
<permission android:name="android.permission.CONTROL_WIFI_DISPLAY"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Allows an application to control the color transforms applied to
displays system-wide.
<p>Not for use by third-party applications.</p>
@hide -->
<permission android:name="android.permission.CONFIGURE_DISPLAY_COLOR_TRANSFORM"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @SystemApi Allows an application to control VPN.
<p>Not for use by third-party applications.</p>
@hide -->
<permission android:name="android.permission.CONTROL_VPN"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<uses-permission android:name="android.permission.CONTROL_VPN" />
<!-- @SystemApi Allows an application to capture audio output.
<p>Not for use by third-party applications.</p> -->
<permission android:name="android.permission.CAPTURE_AUDIO_OUTPUT"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows an application to capture audio for hotword detection.
<p>Not for use by third-party applications.</p>
@hide -->
<permission android:name="android.permission.CAPTURE_AUDIO_HOTWORD"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows an application to modify audio routing and override policy decisions.
<p>Not for use by third-party applications.</p>
@hide -->
<permission android:name="android.permission.MODIFY_AUDIO_ROUTING"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows an application to capture video output.
<p>Not for use by third-party applications.</p> -->
<permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows an application to capture secure video output.
<p>Not for use by third-party applications.</p> -->
<permission android:name="android.permission.CAPTURE_SECURE_VIDEO_OUTPUT"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows an application to know what content is playing and control its playback.
<p>Not for use by third-party applications due to privacy of media consumption</p> -->
<permission android:name="android.permission.MEDIA_CONTENT_CONTROL"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Required to be able to disable the device (very dangerous!).
<p>Not for use by third-party applications.
@hide
-->
<permission android:name="android.permission.BRICK"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @SystemApi Required to be able to reboot the device.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.REBOOT"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
- <!-- @SystemApi Allows low-level access to power management.
- <p>Not for use by third-party applications.
- @hide
- -->
- <permission android:name="android.permission.DEVICE_POWER"
- android:protectionLevel="signature" />
+ <!-- @SystemApi Allows low-level access to power management.
+ <p>Not for use by third-party applications.
+ @hide
+ -->
+ <permission android:name="android.permission.DEVICE_POWER"
+ android:protectionLevel="signature" />
- <!-- Allows access to the PowerManager.userActivity function.
- <p>Not for use by third-party applications. @hide @SystemApi -->
+ <!-- Allows access to the PowerManager.userActivity function.
+ <p>Not for use by third-party applications. @hide @SystemApi -->
<permission android:name="android.permission.USER_ACTIVITY"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
- <!-- @hide Allows low-level access to tun tap driver -->
+ <!-- @hide Allows low-level access to tun tap driver -->
<permission android:name="android.permission.NET_TUNNELING"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Run as a manufacturer test application, running as the root user.
Only available when the device is running in manufacturer test mode.
<p>Not for use by third-party applications.
-->
<permission android:name="android.permission.FACTORY_TEST"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Allows an application to broadcast a notification that an application
package has been removed.
<p>Not for use by third-party applications.
-->
<permission android:name="android.permission.BROADCAST_PACKAGE_REMOVED"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Allows an application to broadcast an SMS receipt notification.
<p>Not for use by third-party applications.
-->
<permission android:name="android.permission.BROADCAST_SMS"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Allows an application to broadcast a WAP PUSH receipt notification.
<p>Not for use by third-party applications.
-->
<permission android:name="android.permission.BROADCAST_WAP_PUSH"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @SystemApi Allows an application to broadcast privileged networking requests.
<p>Not for use by third-party applications. @hide -->
<permission android:name="android.permission.BROADCAST_NETWORK_PRIVILEGED"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Not for use by third-party applications. -->
<permission android:name="android.permission.MASTER_CLEAR"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows an application to call any phone number, including emergency
numbers, without going through the Dialer user interface for the user
to confirm the call being placed.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.CALL_PRIVILEGED"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows an application to perform CDMA OTA provisioning @hide -->
<permission android:name="android.permission.PERFORM_CDMA_PROVISIONING"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows an application to perform SIM Activation @hide -->
<permission android:name="android.permission.PERFORM_SIM_ACTIVATION"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows enabling/disabling location update notifications from
the radio.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.CONTROL_LOCATION_UPDATES"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows read/write access to the "properties" table in the checkin
database, to change values that get uploaded.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.ACCESS_CHECKIN_PROPERTIES"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows an application to collect component usage
statistics
<p>Declaring the permission implies intention to use the API and the user of the
device can grant permission through the Settings application. -->
<permission android:name="android.permission.PACKAGE_USAGE_STATS"
- android:protectionLevel="signature|privileged|development|appop" />
+ android:protectionLevel="signature|privileged|development|appop" />
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
<!-- @hide Allows an application to change the app idle state of an app.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.CHANGE_APP_IDLE_STATE"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @hide @SystemApi Allows an application to temporarily whitelist an inactive app to
access the network and acquire wakelocks.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- Permission an application must hold in order to use
{@link android.provider.Settings#ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS}.
This is a normal permission: an app requesting it will always be granted the
permission, without the user needing to approve or see it. -->
<permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"
- android:protectionLevel="normal" />
+ android:protectionLevel="normal" />
<!-- @SystemApi Allows an application to collect battery statistics -->
<permission android:name="android.permission.BATTERY_STATS"
- android:protectionLevel="signature|privileged|development" />
+ android:protectionLevel="signature|privileged|development" />
<!-- @SystemApi Allows an application to control the backup and restore process.
<p>Not for use by third-party applications.
@hide pending API council -->
<permission android:name="android.permission.BACKUP"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- Allows a package to launch the secure full-backup confirmation UI.
ONLY the system process may hold this permission.
@hide -->
<permission android:name="android.permission.CONFIRM_FULL_BACKUP"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @SystemApi Must be required by a {@link android.widget.RemoteViewsService},
to ensure that only the system can bind to it. -->
<permission android:name="android.permission.BIND_REMOTEVIEWS"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows an application to tell the AppWidget service which application
can access AppWidget's data. The normal user flow is that a user
@@ -2700,25 +2689,25 @@
An application that has this permission should honor that contract.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.BIND_APPWIDGET"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Private permission, to restrict who can bring up a dialog to add a new
keyguard widget
@hide -->
<permission android:name="android.permission.BIND_KEYGUARD_APPWIDGET"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Internal permission allowing an application to query/set which
applications can bind AppWidgets.
@hide -->
<permission android:name="android.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- Allows applications to change the background data setting.
<p>Not for use by third-party applications.
@hide pending API council -->
<permission android:name="android.permission.CHANGE_BACKGROUND_DATA_SETTING"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @SystemApi This permission can be used on content providers to allow the global
search system to access their data. Typically it used when the
@@ -2729,7 +2718,7 @@
it is used by applications to protect themselves from everyone else
besides global search. -->
<permission android:name="android.permission.GLOBAL_SEARCH"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- Internal permission protecting access to the global search
system: ensures that only the system can access the provider
@@ -2739,33 +2728,33 @@
ranking).
@hide -->
<permission android:name="android.permission.GLOBAL_SEARCH_CONTROL"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @SystemApi Internal permission to allows an application to read indexable data.
@hide -->
<permission android:name="android.permission.READ_SEARCH_INDEXABLES"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows applications to set a live wallpaper.
@hide XXX Change to signature once the picker is moved to its
own apk as Ghod Intended. -->
<permission android:name="android.permission.SET_WALLPAPER_COMPONENT"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows applications to read dream settings and dream state.
@hide -->
<permission android:name="android.permission.READ_DREAM_STATE"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows applications to write dream settings, and start or stop dreaming.
@hide -->
<permission android:name="android.permission.WRITE_DREAM_STATE"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allow an application to read and write the cache partition.
@hide -->
<permission android:name="android.permission.ACCESS_CACHE_FILESYSTEM"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- Must be required by default container service so that only
the system can bind to it and use it to copy
@@ -2773,67 +2762,67 @@
accessible to the system.
@hide -->
<permission android:name="android.permission.COPY_PROTECTED_DATA"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @SystemApi Internal permission protecting access to the encryption methods
@hide
-->
<permission android:name="android.permission.CRYPT_KEEPER"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows an application to read historical network usage for
specific networks and applications. @hide -->
<permission android:name="android.permission.READ_NETWORK_USAGE_HISTORY"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- Allows an application to manage network policies (such as warning and disable
limits) and to define application-specific rules. @hide -->
<permission android:name="android.permission.MANAGE_NETWORK_POLICY"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @SystemApi Allows an application to account its network traffic against other UIDs. Used
by system services like download manager and media server. Not for use by
third party apps. @hide -->
<permission android:name="android.permission.MODIFY_NETWORK_ACCOUNTING"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- C2DM permission.
@hide Used internally.
-->
<permission android:name="android.intent.category.MASTER_CLEAR.permission.C2D_MESSAGE"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<uses-permission android:name="android.intent.category.MASTER_CLEAR.permission.C2D_MESSAGE"/>
<!-- @SystemApi @hide Package verifier needs to have this permission before the PackageManager will
trust it to verify packages.
-->
<permission android:name="android.permission.PACKAGE_VERIFICATION_AGENT"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- Must be required by package verifier receiver, to ensure that only the
system can interact with it.
@hide
-->
<permission android:name="android.permission.BIND_PACKAGE_VERIFIER"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @SystemApi @hide Intent filter verifier needs to have this permission before the
PackageManager will trust it to verify intent filters.
-->
<permission android:name="android.permission.INTENT_FILTER_VERIFICATION_AGENT"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- Must be required by intent filter verifier receiver, to ensure that only the
system can interact with it.
@hide
-->
<permission android:name="android.permission.BIND_INTENT_FILTER_VERIFIER"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @SystemApi Allows applications to access serial ports via the SerialManager.
@hide -->
<permission android:name="android.permission.SERIAL_PORT"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- Allows the holder to access content providers from outside an ApplicationThread.
This permission is enforced by the ActivityManagerService on the corresponding APIs,
@@ -2842,27 +2831,27 @@
@hide
-->
<permission android:name="android.permission.ACCESS_CONTENT_PROVIDERS_EXTERNALLY"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @SystemApi Allows an application to hold an UpdateLock, recommending that a headless
OTA reboot *not* occur while the lock is held.
@hide -->
<permission android:name="android.permission.UPDATE_LOCK"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows an application to read the current set of notifications, including
any metadata and intents attached.
@hide -->
<permission android:name="android.permission.ACCESS_NOTIFICATIONS"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- Marker permission for applications that wish to access notification policy.
<p>Protection level: normal
-->
<permission android:name="android.permission.ACCESS_NOTIFICATION_POLICY"
- android:description="@string/permdesc_access_notification_policy"
- android:label="@string/permlab_access_notification_policy"
- android:protectionLevel="normal" />
+ android:description="@string/permdesc_access_notification_policy"
+ android:label="@string/permlab_access_notification_policy"
+ android:protectionLevel="normal" />
<!-- Allows modification of do not disturb rules and policies. Only allowed for system
processes.
@@ -2873,42 +2862,42 @@
<!-- Allows access to keyguard secure storage. Only allowed for system processes.
@hide -->
<permission android:name="android.permission.ACCESS_KEYGUARD_SECURE_STORAGE"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Allows managing (adding, removing) fingerprint templates. Reserved for the system. @hide -->
<permission android:name="android.permission.MANAGE_FINGERPRINT"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- Allows an app to reset fingerprint attempt counter. Reserved for the system. @hide -->
<permission android:name="android.permission.RESET_FINGERPRINT_LOCKOUT"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Allows an application to control keyguard. Only allowed for system processes.
@hide -->
<permission android:name="android.permission.CONTROL_KEYGUARD"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Allows an application to listen to trust changes. Only allowed for system processes.
@hide -->
<permission android:name="android.permission.TRUST_LISTENER"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @SystemApi Allows an application to provide a trust agent.
@hide For security reasons, this is a platform-only permission. -->
<permission android:name="android.permission.PROVIDE_TRUST_AGENT"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- Allows an application to launch the trust agent settings activity.
@hide -->
<permission android:name="android.permission.LAUNCH_TRUST_AGENT_SETTINGS"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Must be required by an {@link
android.service.trust.TrustAgentService},
to ensure that only the system can bind to it.
@hide -->
<permission android:name="android.permission.BIND_TRUST_AGENT"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Must be required by an {@link
android.service.notification.NotificationListenerService},
@@ -2916,7 +2905,7 @@
<p>Protection level: signature
-->
<permission android:name="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Must be required by an {@link
android.service.notification.NotificationRankerService to ensure that only the system can bind to it.
@@ -2924,7 +2913,7 @@
@hide This is not a third-party API (intended for system apps). -->
-->
<permission android:name="android.permission.BIND_NOTIFICATION_RANKER_SERVICE"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Must be required by a {@link
android.service.chooser.ChooserTargetService}, to ensure that
@@ -2932,7 +2921,7 @@
<p>Protection level: signature
-->
<permission android:name="android.permission.BIND_CHOOSER_TARGET_SERVICE"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Must be required by a {@link
android.service.notification.ConditionProviderService},
@@ -2940,57 +2929,57 @@
<p>Protection level: signature
-->
<permission android:name="android.permission.BIND_CONDITION_PROVIDER_SERVICE"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Must be required by an {@link android.service.dreams.DreamService},
to ensure that only the system can bind to it.
<p>Protection level: signature
-->
<permission android:name="android.permission.BIND_DREAM_SERVICE"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @SystemApi Allows an application to call into a carrier setup flow. It is up to the
carrier setup application to enforce that this permission is required
@hide This is not a third-party API (intended for OEMs and system apps). -->
<permission android:name="android.permission.INVOKE_CARRIER_SETUP"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows an application to listen for network condition observations.
@hide This is not a third-party API (intended for system apps). -->
<permission android:name="android.permission.ACCESS_NETWORK_CONDITIONS"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows an application to provision and access DRM certificates
@hide This is not a third-party API (intended for system apps). -->
<permission android:name="android.permission.ACCESS_DRM_CERTIFICATES"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- Api Allows an application to manage media projection sessions.
@hide This is not a third-party API (intended for system apps). -->
<permission android:name="android.permission.MANAGE_MEDIA_PROJECTION"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @SystemApi Allows an application to read install sessions
@hide This is not a third-party API (intended for system apps). -->
<permission android:name="android.permission.READ_INSTALL_SESSIONS"
- android:label="@string/permlab_readInstallSessions"
- android:description="@string/permdesc_readInstallSessions"
- android:protectionLevel="normal"/>
+ android:label="@string/permlab_readInstallSessions"
+ android:description="@string/permdesc_readInstallSessions"
+ android:protectionLevel="normal"/>
<!-- @SystemApi Allows an application to remove DRM certificates
@hide This is not a third-party API (intended for system apps). -->
<permission android:name="android.permission.REMOVE_DRM_CERTIFICATES"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @deprecated Use {@link android.Manifest.permission#BIND_CARRIER_SERVICES} instead -->
<permission android:name="android.permission.BIND_CARRIER_MESSAGING_SERVICE"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- Allows an application to interact with the currently active
{@link android.service.voice.VoiceInteractionService}.
@hide -->
<permission android:name="android.permission.ACCESS_VOICE_INTERACTION_SERVICE"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- The system process that is allowed to bind to services in carrier apps will
have this permission. Carrier apps should use this permission to protect
@@ -2998,9 +2987,9 @@
<p>Protection level: system|signature
-->
<permission android:name="android.permission.BIND_CARRIER_SERVICES"
- android:label="@string/permlab_bindCarrierServices"
- android:description="@string/permdesc_bindCarrierServices"
- android:protectionLevel="signature|privileged" />
+ android:label="@string/permlab_bindCarrierServices"
+ android:description="@string/permdesc_bindCarrierServices"
+ android:protectionLevel="signature|privileged" />
<!-- Allows an application to query whether DO_NOT_ASK_CREDENTIALS_ON_BOOT
flag is set.
@@ -3046,7 +3035,7 @@
<!-- Allows the holder to access the ephemeral applications on the device.
@hide -->
<permission android:name="android.permission.ACCESS_EPHEMERAL_APPS"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Allows receiving the usage of media resource e.g. video/audio codec and
graphic memory.
@@ -3058,7 +3047,7 @@
APIs given by {@link SoundTriggerManager}.
@hide <p>Not for use by third-party applications.</p> -->
<permission android:name="android.permission.MANAGE_SOUND_TRIGGER"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows trusted applications to dispatch managed provisioning message to Managed
Provisioning app. If requesting app does not have permission, it will be ignored.
@@ -3082,17 +3071,17 @@
the system can bind to it.
<p>Protection level: signature -->
<permission android:name="android.permission.BIND_VR_LISTENER_SERVICE"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Required to make calls to {@link android.service.vr.IVrManager}.
@hide -->
<permission android:name="android.permission.ACCESS_VR_MANAGER"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- Allows an application to whitelist tasks during lock task mode
@hide <p>Not for use by third-party applications.</p> -->
<permission android:name="android.permission.UPDATE_LOCK_TASK_PACKAGES"
- android:protectionLevel="signature|setup" />
+ android:protectionLevel="signature|setup" />
<!-- @SystemApi Allows an application to replace the app name displayed alongside notifications
in the N-release and later.
@@ -3113,13 +3102,12 @@
android:defaultToDeviceProtectedStorage="true"
android:directBootAware="true">
<activity android:name="com.android.internal.app.ChooserActivity"
- android:theme="@style/Theme.DeviceDefault.Resolver"
- android:finishOnCloseSystemDialogs="true"
- android:excludeFromRecents="true"
- android:documentLaunchMode="never"
- android:relinquishTaskIdentity="true"
- android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation|keyboard|keyboardHidden"
- android:process=":ui">
+ android:theme="@style/Theme.DeviceDefault.Resolver"
+ android:finishOnCloseSystemDialogs="true"
+ android:excludeFromRecents="true"
+ android:documentLaunchMode="never"
+ android:relinquishTaskIdentity="true"
+ android:process=":ui">
<intent-filter>
<action android:name="android.intent.action.CHOOSER" />
<category android:name="android.intent.category.DEFAULT" />
@@ -3127,102 +3115,102 @@
</intent-filter>
</activity>
<activity android:name="com.android.internal.app.IntentForwarderActivity"
- android:finishOnCloseSystemDialogs="true"
- android:theme="@style/Theme.NoDisplay"
- android:excludeFromRecents="true"
- android:label="@string/user_owner_label"
- android:exported="true"
- >
+ android:finishOnCloseSystemDialogs="true"
+ android:theme="@style/Theme.NoDisplay"
+ android:excludeFromRecents="true"
+ android:label="@string/user_owner_label"
+ android:exported="true"
+ >
</activity>
<activity-alias android:name="com.android.internal.app.ForwardIntentToParent"
- android:targetActivity="com.android.internal.app.IntentForwarderActivity"
- android:exported="true"
- android:label="@string/user_owner_label">
+ android:targetActivity="com.android.internal.app.IntentForwarderActivity"
+ android:exported="true"
+ android:label="@string/user_owner_label">
</activity-alias>
<activity-alias android:name="com.android.internal.app.ForwardIntentToManagedProfile"
- android:targetActivity="com.android.internal.app.IntentForwarderActivity"
- android:icon="@drawable/ic_corp_icon"
- android:exported="true"
- android:label="@string/managed_profile_label">
+ android:targetActivity="com.android.internal.app.IntentForwarderActivity"
+ android:icon="@drawable/ic_corp_icon"
+ android:exported="true"
+ android:label="@string/managed_profile_label">
</activity-alias>
<activity android:name="com.android.internal.app.HeavyWeightSwitcherActivity"
- android:theme="@style/Theme.Material.Light.Dialog"
- android:label="@string/heavy_weight_switcher_title"
- android:finishOnCloseSystemDialogs="true"
- android:excludeFromRecents="true"
- android:process=":ui">
+ android:theme="@style/Theme.Material.Light.Dialog"
+ android:label="@string/heavy_weight_switcher_title"
+ android:finishOnCloseSystemDialogs="true"
+ android:excludeFromRecents="true"
+ android:process=":ui">
</activity>
<activity android:name="com.android.internal.app.PlatLogoActivity"
- android:theme="@style/Theme.Wallpaper.NoTitleBar.Fullscreen"
- android:configChanges="orientation|keyboardHidden"
- android:process=":ui">
+ android:theme="@style/Theme.Wallpaper.NoTitleBar.Fullscreen"
+ android:configChanges="orientation|keyboardHidden"
+ android:process=":ui">
</activity>
<activity android:name="com.android.internal.app.DisableCarModeActivity"
- android:theme="@style/Theme.NoDisplay"
- android:excludeFromRecents="true"
- android:process=":ui">
+ android:theme="@style/Theme.NoDisplay"
+ android:excludeFromRecents="true"
+ android:process=":ui">
</activity>
<activity android:name="com.android.internal.app.DumpHeapActivity"
- android:theme="@style/Theme.Translucent.NoTitleBar"
- android:label="@string/dump_heap_title"
- android:finishOnCloseSystemDialogs="true"
- android:noHistory="true"
- android:excludeFromRecents="true"
- android:process=":ui">
+ android:theme="@style/Theme.Translucent.NoTitleBar"
+ android:label="@string/dump_heap_title"
+ android:finishOnCloseSystemDialogs="true"
+ android:noHistory="true"
+ android:excludeFromRecents="true"
+ android:process=":ui">
</activity>
<provider android:name="com.android.server.am.DumpHeapProvider"
- android:authorities="com.android.server.heapdump"
- android:grantUriPermissions="true"
- android:multiprocess="false"
- android:singleUser="true" />
+ android:authorities="com.android.server.heapdump"
+ android:grantUriPermissions="true"
+ android:multiprocess="false"
+ android:singleUser="true" />
<activity android:name="android.accounts.ChooseAccountActivity"
- android:excludeFromRecents="true"
- android:exported="true"
- android:theme="@style/Theme.Material.Light.Dialog"
- android:label="@string/choose_account_label"
- android:process=":ui">
+ android:excludeFromRecents="true"
+ android:exported="true"
+ android:theme="@style/Theme.Material.Light.Dialog"
+ android:label="@string/choose_account_label"
+ android:process=":ui">
</activity>
<activity android:name="android.accounts.ChooseTypeAndAccountActivity"
- android:excludeFromRecents="true"
- android:exported="true"
- android:theme="@style/Theme.Material.Light.Dialog"
- android:label="@string/choose_account_label"
- android:process=":ui">
+ android:excludeFromRecents="true"
+ android:exported="true"
+ android:theme="@style/Theme.Material.Light.Dialog"
+ android:label="@string/choose_account_label"
+ android:process=":ui">
</activity>
<activity android:name="android.accounts.ChooseAccountTypeActivity"
- android:excludeFromRecents="true"
- android:theme="@style/Theme.Material.Light.Dialog"
- android:label="@string/choose_account_label"
- android:process=":ui">
+ android:excludeFromRecents="true"
+ android:theme="@style/Theme.Material.Light.Dialog"
+ android:label="@string/choose_account_label"
+ android:process=":ui">
</activity>
<activity android:name="android.accounts.CantAddAccountActivity"
- android:excludeFromRecents="true"
- android:exported="true"
- android:theme="@style/Theme.Material.Light.Dialog.NoActionBar"
- android:process=":ui">
+ android:excludeFromRecents="true"
+ android:exported="true"
+ android:theme="@style/Theme.Material.Light.Dialog.NoActionBar"
+ android:process=":ui">
</activity>
<activity android:name="android.accounts.GrantCredentialsPermissionActivity"
- android:excludeFromRecents="true"
- android:exported="true"
- android:theme="@style/Theme.Material.Light.DialogWhenLarge"
- android:process=":ui">
+ android:excludeFromRecents="true"
+ android:exported="true"
+ android:theme="@style/Theme.Material.Light.DialogWhenLarge"
+ android:process=":ui">
</activity>
<activity android:name="android.content.SyncActivityTooManyDeletes"
- android:theme="@style/Theme.Material.Light.Dialog"
- android:label="@string/sync_too_many_deletes"
- android:process=":ui">
+ android:theme="@style/Theme.Material.Light.Dialog"
+ android:label="@string/sync_too_many_deletes"
+ android:process=":ui">
</activity>
<activity android:name="com.android.internal.app.ShutdownActivity"
- android:permission="android.permission.SHUTDOWN"
- android:theme="@style/Theme.NoDisplay"
- android:excludeFromRecents="true">
+ android:permission="android.permission.SHUTDOWN"
+ android:theme="@style/Theme.NoDisplay"
+ android:excludeFromRecents="true">
<intent-filter>
<action android:name="android.intent.action.ACTION_REQUEST_SHUTDOWN" />
<category android:name="android.intent.category.DEFAULT" />
@@ -3234,9 +3222,9 @@
</activity>
<activity android:name="com.android.internal.app.NetInitiatedActivity"
- android:theme="@style/Theme.Material.Light.Dialog.Alert"
- android:excludeFromRecents="true"
- android:process=":ui">
+ android:theme="@style/Theme.Material.Light.Dialog.Alert"
+ android:excludeFromRecents="true"
+ android:process=":ui">
</activity>
<activity android:name="com.android.internal.app.SystemUserHomeActivity"
@@ -3253,9 +3241,9 @@
<!-- Activity to prompt user if it's ok to create a new user sandbox for a
specified account. -->
<activity android:name="com.android.internal.app.ConfirmUserCreationActivity"
- android:excludeFromRecents="true"
- android:process=":ui"
- android:theme="@style/Theme.Material.Light.Dialog.Alert">
+ android:excludeFromRecents="true"
+ android:process=":ui"
+ android:theme="@style/Theme.Material.Light.Dialog.Alert">
<intent-filter android:priority="1000">
<action android:name="android.os.action.CREATE_USER" />
<category android:name="android.intent.category.DEFAULT" />
@@ -3263,20 +3251,20 @@
</activity>
<activity android:name="com.android.internal.app.UnlaunchableAppActivity"
- android:theme="@style/Theme.Material.Light.Dialog.Alert"
- android:excludeFromRecents="true"
- android:process=":ui">
+ android:theme="@style/Theme.Material.Light.Dialog.Alert"
+ android:excludeFromRecents="true"
+ android:process=":ui">
</activity>
<receiver android:name="com.android.server.BootReceiver"
- android:systemUserOnly="true">
+ android:systemUserOnly="true">
<intent-filter android:priority="1000">
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<receiver android:name="com.android.server.updates.CertPinInstallReceiver"
- android:permission="android.permission.UPDATE_CONFIG">
+ android:permission="android.permission.UPDATE_CONFIG">
<intent-filter>
<action android:name="android.intent.action.UPDATE_PINS" />
<data android:scheme="content" android:host="*" android:mimeType="*/*" />
@@ -3284,7 +3272,7 @@
</receiver>
<receiver android:name="com.android.server.updates.IntentFirewallInstallReceiver"
- android:permission="android.permission.UPDATE_CONFIG">
+ android:permission="android.permission.UPDATE_CONFIG">
<intent-filter>
<action android:name="android.intent.action.UPDATE_INTENT_FIREWALL" />
<data android:scheme="content" android:host="*" android:mimeType="*/*" />
@@ -3292,7 +3280,7 @@
</receiver>
<receiver android:name="com.android.server.updates.SmsShortCodesInstallReceiver"
- android:permission="android.permission.UPDATE_CONFIG">
+ android:permission="android.permission.UPDATE_CONFIG">
<intent-filter>
<action android:name="android.intent.action.UPDATE_SMS_SHORT_CODES" />
<data android:scheme="content" android:host="*" android:mimeType="*/*" />
@@ -3300,7 +3288,7 @@
</receiver>
<receiver android:name="com.android.server.updates.ApnDbInstallReceiver"
- android:permission="android.permission.UPDATE_CONFIG">
+ android:permission="android.permission.UPDATE_CONFIG">
<intent-filter>
<action android:name="android.intent.action.UPDATE_APN_DB" />
<data android:scheme="content" android:host="*" android:mimeType="*/*" />
@@ -3308,7 +3296,7 @@
</receiver>
<receiver android:name="com.android.server.updates.CarrierProvisioningUrlsInstallReceiver"
- android:permission="android.permission.UPDATE_CONFIG">
+ android:permission="android.permission.UPDATE_CONFIG">
<intent-filter>
<action android:name="android.intent.action.UPDATE_CARRIER_PROVISIONING_URLS" />
<data android:scheme="content" android:host="*" android:mimeType="*/*" />
@@ -3316,23 +3304,15 @@
</receiver>
<receiver android:name="com.android.server.updates.TzDataInstallReceiver"
- android:permission="android.permission.UPDATE_CONFIG">
+ android:permission="android.permission.UPDATE_CONFIG">
<intent-filter>
<action android:name="android.intent.action.UPDATE_TZDATA" />
<data android:scheme="content" android:host="*" android:mimeType="*/*" />
</intent-filter>
</receiver>
- <receiver android:name="com.android.server.updates.SELinuxPolicyInstallReceiver"
- android:permission="android.permission.UPDATE_CONFIG">
- <intent-filter>
- <action android:name="android.intent.action.UPDATE_SEPOLICY" />
- <data android:scheme="content" android:host="*" android:mimeType="*/*" />
- </intent-filter>
- </receiver>
-
<receiver android:name="com.android.server.MasterClearReceiver"
- android:permission="android.permission.MASTER_CLEAR">
+ android:permission="android.permission.MASTER_CLEAR">
<intent-filter
android:priority="100" >
<!-- For Checkin, Settings, etc.: action=MASTER_CLEAR -->
@@ -3345,12 +3325,12 @@
</receiver>
<service android:name="android.hardware.location.GeofenceHardwareService"
- android:permission="android.permission.LOCATION_HARDWARE"
- android:exported="false" />
+ android:permission="android.permission.LOCATION_HARDWARE"
+ android:exported="false" />
<service android:name="com.android.internal.backup.LocalTransportService"
- android:permission="android.permission.CONFIRM_FULL_BACKUP"
- android:exported="false">
+ android:permission="android.permission.CONFIRM_FULL_BACKUP"
+ android:exported="false">
<intent-filter>
<action android:name="android.backup.TRANSPORT_HOST" />
</intent-filter>
@@ -3375,9 +3355,9 @@
</service>
<service
- android:name="com.android.server.pm.BackgroundDexOptService"
- android:exported="true"
- android:permission="android.permission.BIND_JOB_SERVICE">
+ android:name="com.android.server.pm.BackgroundDexOptService"
+ android:exported="true"
+ android:permission="android.permission.BIND_JOB_SERVICE">
</service>
</application>
diff --git a/tests/tests/permission2/src/android/permission2/cts/PermissionPolicyTest.java b/tests/tests/permission2/src/android/permission2/cts/PermissionPolicyTest.java
index e8de02d..7406645 100644
--- a/tests/tests/permission2/src/android/permission2/cts/PermissionPolicyTest.java
+++ b/tests/tests/permission2/src/android/permission2/cts/PermissionPolicyTest.java
@@ -106,7 +106,7 @@
// OEMs cannot define permissions in the platform namespace
for (String permission : declaredPermissionsMap.keySet()) {
- assertFalse("Cannot define permission " + permission + " in android namespace",
+ assertFalse("Cannot define permission in android namespace",
permission.startsWith(PLATFORM_ROOT_NAMESPACE));
}
diff --git a/tests/tests/preference2/src/android/preference2/cts/PreferenceTest.java b/tests/tests/preference2/src/android/preference2/cts/PreferenceTest.java
index b2907f3..6b36fee 100644
--- a/tests/tests/preference2/src/android/preference2/cts/PreferenceTest.java
+++ b/tests/tests/preference2/src/android/preference2/cts/PreferenceTest.java
@@ -24,6 +24,11 @@
import android.preference2.cts.R;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
public class PreferenceTest
extends ActivityInstrumentationTestCase2<PreferenceFromCodeActivity> {
@@ -232,6 +237,24 @@
}
}
+ public void testPersistStringSet() {
+ CustomPreference mCustomPreference = new CustomPreference(mActivity);
+ String key = "" + Math.random();
+ mCustomPreference.setKey(key);
+ PreferenceGroup mPreferenceGroup = (PreferenceGroup) mActivity.findPreference(
+ "pref-group");
+ mPreferenceGroup.addPreference(mCustomPreference);
+ try {
+ Set<String> expected = Stream.of("a", "b", "c").collect(Collectors.toSet());
+ boolean result = mCustomPreference.persistStringSet(expected);
+ assertTrue(result);
+ Set<String> actual = mCustomPreference.getPersistedStringSet(new HashSet<>());
+ assertEquals(expected, actual);
+ } finally {
+ mPreferenceGroup.removePreference(mCustomPreference);
+ }
+ }
+
public void testPersistFloat() {
CustomPreference mCustomPreference = new CustomPreference(mActivity);
String key = "" + Math.random();
diff --git a/tests/tests/print/Android.mk b/tests/tests/print/Android.mk
index bceaacc..d0c1a81 100644
--- a/tests/tests/print/Android.mk
+++ b/tests/tests/print/Android.mk
@@ -27,7 +27,7 @@
LOCAL_PACKAGE_NAME := CtsPrintTestCases
-LOCAL_STATIC_JAVA_LIBRARIES := mockito-target ctstestrunner ub-uiautomator ctsdeviceutil
+LOCAL_STATIC_JAVA_LIBRARIES := mockito-target ctstestrunner ub-uiautomator ctsdeviceutil android-support-test
LOCAL_SDK_VERSION := test_current
diff --git a/tests/tests/print/AndroidManifest.xml b/tests/tests/print/AndroidManifest.xml
index bd58d3b..564bcb1 100644
--- a/tests/tests/print/AndroidManifest.xml
+++ b/tests/tests/print/AndroidManifest.xml
@@ -64,7 +64,8 @@
<activity
android:name="android.print.cts.services.CustomPrintOptionsActivity"
android:permission="android.permission.START_PRINT_SERVICE_CONFIG_ACTIVITY"
- android:exported="true">
+ android:exported="true"
+ android:theme="@style/Theme.Translucent">
</activity>
</application>
diff --git a/tests/tests/print/res/raw/yellow.png b/tests/tests/print/res/raw/yellow.png
new file mode 100644
index 0000000..3e2bbd3
--- /dev/null
+++ b/tests/tests/print/res/raw/yellow.png
Binary files differ
diff --git a/tests/tests/print/res/raw/yellow_printer.png b/tests/tests/print/res/raw/yellow_printer.png
deleted file mode 100644
index 2e1cc8d..0000000
--- a/tests/tests/print/res/raw/yellow_printer.png
+++ /dev/null
Binary files differ
diff --git a/tests/tests/print/res/values/themes.xml b/tests/tests/print/res/values/themes.xml
new file mode 100644
index 0000000..f5c9b977
--- /dev/null
+++ b/tests/tests/print/res/values/themes.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ 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.
+-->
+
+<resources>
+ <style name="Theme.Translucent" parent="@android:style/Theme.DeviceDefault">
+ <item name="android:windowIsTranslucent">true</item>
+ </style>
+</resources>
diff --git a/tests/tests/print/src/android/print/cts/BasePrintTest.java b/tests/tests/print/src/android/print/cts/BasePrintTest.java
index 7f0ae5d..af67e13 100644
--- a/tests/tests/print/src/android/print/cts/BasePrintTest.java
+++ b/tests/tests/print/src/android/print/cts/BasePrintTest.java
@@ -16,6 +16,7 @@
package android.print.cts;
+import static android.print.cts.Utils.getPrintManager;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.argThat;
import static org.mockito.Matchers.eq;
@@ -24,15 +25,12 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
-import android.content.Context;
import android.content.pm.PackageManager;
-import android.content.res.Configuration;
import android.content.res.Resources;
import android.cts.util.SystemUtil;
import android.graphics.pdf.PdfDocument;
import android.os.Bundle;
import android.os.CancellationSignal;
-import android.os.LocaleList;
import android.os.ParcelFileDescriptor;
import android.os.SystemClock;
import android.print.PageRange;
@@ -40,22 +38,23 @@
import android.print.PrintDocumentAdapter;
import android.print.PrintDocumentAdapter.LayoutResultCallback;
import android.print.PrintDocumentAdapter.WriteResultCallback;
-import android.print.PrintManager;
+import android.print.PrintDocumentInfo;
import android.print.PrinterId;
import android.print.cts.services.PrintServiceCallbacks;
import android.print.cts.services.PrinterDiscoverySessionCallbacks;
+import android.print.cts.services.StubbablePrintService;
import android.print.cts.services.StubbablePrinterDiscoverySession;
import android.print.pdf.PrintedPdfDocument;
import android.printservice.CustomPrinterIconCallback;
import android.printservice.PrintJob;
-import android.printservice.PrintService;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
import android.support.test.uiautomator.By;
import android.support.test.uiautomator.UiDevice;
import android.support.test.uiautomator.UiObject;
import android.support.test.uiautomator.UiObjectNotFoundException;
import android.support.test.uiautomator.UiSelector;
import android.test.InstrumentationTestCase;
-import android.util.DisplayMetrics;
import android.util.Log;
import org.hamcrest.BaseMatcher;
@@ -71,7 +70,6 @@
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
-import java.util.Locale;
import java.util.concurrent.TimeoutException;
/**
* This is the base class for print tests.
@@ -79,14 +77,16 @@
public abstract class BasePrintTest extends InstrumentationTestCase {
private final static String LOG_TAG = "BasePrintTest";
- protected static final long OPERATION_TIMEOUT_MILLIS = 60000;
+ static final long OPERATION_TIMEOUT_MILLIS = 60000;
+ static final String PRINT_JOB_NAME = "Test";
+
private static final String PRINT_SPOOLER_PACKAGE_NAME = "com.android.printspooler";
- protected static final String PRINT_JOB_NAME = "Test";
private static final String PM_CLEAR_SUCCESS_OUTPUT = "Success";
private static final String COMMAND_LIST_ENABLED_IME_COMPONENTS = "ime list -s";
private static final String COMMAND_PREFIX_ENABLE_IME = "ime enable ";
private static final String COMMAND_PREFIX_DISABLE_IME = "ime disable ";
private static final int CURRENT_USER_ID = -2; // Mirrors UserHandle.USER_CURRENT
+ private static final String PRINTSPOOLER_PACKAGE = "com.android.printspooler";
private static PrintDocumentActivity sActivity;
private UiDevice mUiDevice;
@@ -100,8 +100,6 @@
return mUiDevice;
}
- private LocaleList mOldLocale;
-
private CallCounter mCancelOperationCounter;
private CallCounter mLayoutCallCounter;
private CallCounter mWriteCallCounter;
@@ -180,18 +178,6 @@
System.setProperty("dexmaker.dexcache", getInstrumentation()
.getTargetContext().getCacheDir().getPath());
- // Set to US locale.
- Log.d(LOG_TAG, "set locale");
- Resources resources = getInstrumentation().getTargetContext().getResources();
- Configuration oldConfiguration = resources.getConfiguration();
- if (!oldConfiguration.getLocales().get(0).equals(Locale.US)) {
- mOldLocale = oldConfiguration.getLocales();
- DisplayMetrics displayMetrics = resources.getDisplayMetrics();
- Configuration newConfiguration = new Configuration(oldConfiguration);
- newConfiguration.setLocale(Locale.US);
- resources.updateConfiguration(newConfiguration, displayMetrics);
- }
-
// Initialize the latches.
Log.d(LOG_TAG, "init counters");
mCancelOperationCounter = new CallCounter();
@@ -226,17 +212,6 @@
Log.d(LOG_TAG, "enableImes()");
enableImes();
- // Restore the locale if needed.
- Log.d(LOG_TAG, "restore locale");
- if (mOldLocale != null) {
- Resources resources = getInstrumentation().getTargetContext().getResources();
- DisplayMetrics displayMetrics = resources.getDisplayMetrics();
- Configuration newConfiguration = new Configuration(resources.getConfiguration());
- newConfiguration.setLocales(mOldLocale);
- mOldLocale = null;
- resources.updateConfiguration(newConfiguration, displayMetrics);
- }
-
// Make sure the spooler is cleaned, this also un-approves all services
Log.d(LOG_TAG, "clearPrintSpoolerData()");
clearPrintSpoolerData();
@@ -246,30 +221,40 @@
}
protected void print(final PrintDocumentAdapter adapter, final PrintAttributes attributes) {
- // Initiate printing as if coming from the app.
- getInstrumentation().runOnMainSync(new Runnable() {
- @Override
- public void run() {
- PrintManager printManager = (PrintManager) getActivity()
- .getSystemService(Context.PRINT_SERVICE);
- printManager.print("Print job", adapter, attributes);
- }
- });
+ print(adapter, "Print job", attributes);
}
protected void print(PrintDocumentAdapter adapter) {
- print(adapter, null);
+ print(adapter, (PrintAttributes) null);
}
- protected void onCancelOperationCalled() {
+ protected void print(PrintDocumentAdapter adapter, String printJobName) {
+ print(adapter, printJobName, null);
+ }
+
+ /**
+ * Start printing
+ *
+ * @param adapter Adapter supplying data to print
+ * @param printJobName The name of the print job
+ */
+ protected void print(@NonNull PrintDocumentAdapter adapter, @NonNull String printJobName,
+ @Nullable PrintAttributes attributes) {
+ // Initiate printing as if coming from the app.
+ getInstrumentation()
+ .runOnMainSync(() -> getPrintManager(getActivity()).print(printJobName, adapter,
+ attributes));
+ }
+
+ void onCancelOperationCalled() {
mCancelOperationCounter.call();
}
- protected void onLayoutCalled() {
+ void onLayoutCalled() {
mLayoutCallCounter.call();
}
- protected int getWriteCallCount() {
+ int getWriteCallCount() {
return mWriteCallCounter.getCallCount();
}
@@ -277,15 +262,15 @@
mWriteCallCounter.call();
}
- protected void onFinishCalled() {
+ void onFinishCalled() {
mFinishCallCounter.call();
}
- protected void onPrintJobQueuedCalled() {
+ void onPrintJobQueuedCalled() {
mPrintJobQueuedCallCounter.call();
}
- protected void onPrinterDiscoverySessionCreateCalled() {
+ void onPrinterDiscoverySessionCreateCalled() {
mCreateSessionCallCounter.call();
}
@@ -293,37 +278,37 @@
mDestroySessionCallCounter.call();
}
- protected void waitForCancelOperationCallbackCalled() {
+ void waitForCancelOperationCallbackCalled() {
waitForCallbackCallCount(mCancelOperationCounter, 1,
"Did not get expected call to onCancel for the current operation.");
}
- protected void waitForPrinterDiscoverySessionCreateCallbackCalled() {
+ void waitForPrinterDiscoverySessionCreateCallbackCalled() {
waitForCallbackCallCount(mCreateSessionCallCounter, 1,
"Did not get expected call to onCreatePrinterDiscoverySession.");
}
- protected void waitForPrinterDiscoverySessionDestroyCallbackCalled(int count) {
+ void waitForPrinterDiscoverySessionDestroyCallbackCalled(int count) {
waitForCallbackCallCount(mDestroySessionCallCounter, count,
"Did not get expected call to onDestroyPrinterDiscoverySession.");
}
- protected void waitForServiceOnPrintJobQueuedCallbackCalled(int count) {
+ void waitForServiceOnPrintJobQueuedCallbackCalled(int count) {
waitForCallbackCallCount(mPrintJobQueuedCallCounter, count,
"Did not get expected call to onPrintJobQueued.");
}
- protected void waitForAdapterFinishCallbackCalled() {
+ void waitForAdapterFinishCallbackCalled() {
waitForCallbackCallCount(mFinishCallCounter, 1,
"Did not get expected call to finish.");
}
- protected void waitForLayoutAdapterCallbackCount(int count) {
+ void waitForLayoutAdapterCallbackCount(int count) {
waitForCallbackCallCount(mLayoutCallCounter, count,
"Did not get expected call to layout.");
}
- protected void waitForWriteAdapterCallback(int count) {
+ void waitForWriteAdapterCallback(int count) {
waitForCallbackCallCount(mWriteCallCounter, count, "Did not get expected call to write.");
}
@@ -355,7 +340,7 @@
*
* @return The number of onDestroy calls on the print activity.
*/
- protected static int getActivityDestroyCallbackCallCount() {
+ static int getActivityDestroyCallbackCallCount() {
return sDestroyActivityCallCounter.getCallCount();
}
@@ -364,7 +349,7 @@
*
* @return The number of onCreate calls on the print activity.
*/
- protected static int getActivityCreateCallbackCallCount() {
+ private static int getActivityCreateCallbackCallCount() {
return sCreateActivityCallCounter.getCallCount();
}
@@ -381,7 +366,7 @@
/**
* Reset all counters.
*/
- protected void resetCounters() {
+ void resetCounters() {
mCancelOperationCounter.reset();
mLayoutCallCounter.reset();
mWriteCallCounter.reset();
@@ -393,7 +378,7 @@
sCreateActivityCallCounter.reset();
}
- protected void selectPrinter(String printerName) throws UiObjectNotFoundException, IOException {
+ void selectPrinter(String printerName) throws UiObjectNotFoundException, IOException {
try {
long delay = 100;
while (true) {
@@ -425,7 +410,6 @@
if (delay <= OPERATION_TIMEOUT_MILLIS) {
Log.w(LOG_TAG, "Cannot find printer " + printerName + ", retrying.");
delay *= 2;
- continue;
} else {
throw new UiObjectNotFoundException("Could find printer " + printerName +
" even though we retried");
@@ -438,19 +422,17 @@
}
}
- protected void answerPrintServicesWarning(boolean confirm) throws UiObjectNotFoundException {
- UiDevice uiDevice = UiDevice.getInstance(getInstrumentation());
+ void answerPrintServicesWarning(boolean confirm) throws UiObjectNotFoundException {
UiObject button;
if (confirm) {
- button = uiDevice.findObject(new UiSelector().resourceId("android:id/button1"));
+ button = mUiDevice.findObject(new UiSelector().resourceId("android:id/button1"));
} else {
- button = uiDevice.findObject(new UiSelector().resourceId("android:id/button2"));
+ button = mUiDevice.findObject(new UiSelector().resourceId("android:id/button2"));
}
button.click();
}
- protected void changeOrientation(String orientation)
- throws UiObjectNotFoundException, IOException {
+ void changeOrientation(String orientation) throws UiObjectNotFoundException, IOException {
try {
UiObject orientationSpinner = mUiDevice.findObject(new UiSelector().resourceId(
"com.android.printspooler:id/orientation_spinner"));
@@ -474,7 +456,7 @@
}
}
- protected void changeMediaSize(String mediaSize) throws UiObjectNotFoundException, IOException {
+ void changeMediaSize(String mediaSize) throws UiObjectNotFoundException, IOException {
try {
UiObject mediaSizeSpinner = mUiDevice.findObject(new UiSelector().resourceId(
"com.android.printspooler:id/paper_size_spinner"));
@@ -487,18 +469,7 @@
}
}
- protected String getMediaSize() throws UiObjectNotFoundException, IOException {
- try {
- UiObject mediaSizeSpinner = mUiDevice.findObject(new UiSelector().resourceId(
- "com.android.printspooler:id/paper_size_spinner"));
- return mediaSizeSpinner.getText();
- } catch (UiObjectNotFoundException e) {
- dumpWindowHierarchy();
- throw e;
- }
- }
-
- protected void changeColor(String color) throws UiObjectNotFoundException, IOException {
+ void changeColor(String color) throws UiObjectNotFoundException, IOException {
try {
UiObject colorSpinner = mUiDevice.findObject(new UiSelector().resourceId(
"com.android.printspooler:id/color_spinner"));
@@ -522,7 +493,7 @@
}
}
- protected void changeDuplex(String duplex) throws UiObjectNotFoundException, IOException {
+ void changeDuplex(String duplex) throws UiObjectNotFoundException, IOException {
try {
UiObject duplexSpinner = mUiDevice.findObject(new UiSelector().resourceId(
"com.android.printspooler:id/duplex_spinner"));
@@ -535,11 +506,11 @@
}
}
- protected String getDuplex() throws UiObjectNotFoundException, IOException {
+ void changeCopies(int newCopies) throws UiObjectNotFoundException, IOException {
try {
- UiObject duplexSpinner = mUiDevice.findObject(new UiSelector().resourceId(
- "com.android.printspooler:id/duplex_spinner"));
- return duplexSpinner.getText();
+ UiObject copies = mUiDevice.findObject(new UiSelector().resourceId(
+ "com.android.printspooler:id/copies_edittext"));
+ copies.setText(Integer.valueOf(newCopies).toString());
} catch (UiObjectNotFoundException e) {
dumpWindowHierarchy();
throw e;
@@ -557,7 +528,7 @@
}
}
- protected void clickPrintButton() throws UiObjectNotFoundException, IOException {
+ void clickPrintButton() throws UiObjectNotFoundException, IOException {
try {
UiObject printButton = mUiDevice.findObject(new UiSelector().resourceId(
"com.android.printspooler:id/print_button"));
@@ -568,7 +539,18 @@
}
}
- protected void dumpWindowHierarchy() throws IOException {
+ void clickRetryButton() throws UiObjectNotFoundException, IOException {
+ try {
+ UiObject retryButton = mUiDevice.findObject(new UiSelector().resourceId(
+ "com.android.printspooler:id/action_button"));
+ retryButton.click();
+ } catch (UiObjectNotFoundException e) {
+ dumpWindowHierarchy();
+ throw e;
+ }
+ }
+
+ void dumpWindowHierarchy() throws IOException {
ByteArrayOutputStream os = new ByteArrayOutputStream();
mUiDevice.dumpWindowHierarchy(os);
@@ -582,7 +564,7 @@
return sActivity;
}
- protected void createActivity() {
+ private void createActivity() {
int createBefore = getActivityCreateCallbackCallCount();
launchActivity(getInstrumentation().getTargetContext().getPackageName(),
@@ -591,26 +573,26 @@
waitForActivityCreateCallbackCalled(createBefore + 1);
}
- protected void openPrintOptions() throws UiObjectNotFoundException {
+ void openPrintOptions() throws UiObjectNotFoundException {
UiObject expandHandle = mUiDevice.findObject(new UiSelector().resourceId(
"com.android.printspooler:id/expand_collapse_handle"));
expandHandle.click();
}
- protected void openCustomPrintOptions() throws UiObjectNotFoundException {
+ void openCustomPrintOptions() throws UiObjectNotFoundException {
UiObject expandHandle = mUiDevice.findObject(new UiSelector().resourceId(
"com.android.printspooler:id/more_options_button"));
expandHandle.click();
}
- protected void clearPrintSpoolerData() throws Exception {
+ void clearPrintSpoolerData() throws Exception {
assertTrue("failed to clear print spooler data",
SystemUtil.runShellCommand(getInstrumentation(), String.format(
"pm clear --user %d %s", CURRENT_USER_ID, PRINT_SPOOLER_PACKAGE_NAME))
.contains(PM_CLEAR_SUCCESS_OUTPUT));
}
- protected void verifyLayoutCall(InOrder inOrder, PrintDocumentAdapter mock,
+ void verifyLayoutCall(InOrder inOrder, PrintDocumentAdapter mock,
PrintAttributes oldAttributes, PrintAttributes newAttributes,
final boolean forPreview) {
inOrder.verify(mock).onLayout(eq(oldAttributes), eq(newAttributes),
@@ -630,7 +612,7 @@
}));
}
- protected PrintDocumentAdapter createMockPrintDocumentAdapter(Answer<Void> layoutAnswer,
+ PrintDocumentAdapter createMockPrintDocumentAdapter(Answer<Void> layoutAnswer,
Answer<Void> writeAnswer, Answer<Void> finishAnswer) {
// Create a mock print adapter.
PrintDocumentAdapter adapter = mock(PrintDocumentAdapter.class);
@@ -650,6 +632,49 @@
return adapter;
}
+ /**
+ * Create a mock {@link PrintDocumentAdapter} that provides one empty page.
+ *
+ * @return The mock adapter
+ */
+ @NonNull PrintDocumentAdapter createDefaultPrintDocumentAdapter(int numPages) {
+ final PrintAttributes[] printAttributes = new PrintAttributes[1];
+
+ return createMockPrintDocumentAdapter(
+ invocation -> {
+ PrintAttributes oldAttributes = (PrintAttributes) invocation.getArguments()[0];
+ printAttributes[0] = (PrintAttributes) invocation.getArguments()[1];
+ PrintDocumentAdapter.LayoutResultCallback callback =
+ (PrintDocumentAdapter.LayoutResultCallback) invocation
+ .getArguments()[3];
+
+ callback.onLayoutFinished(new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
+ .setPageCount(numPages).build(),
+ !oldAttributes.equals(printAttributes[0]));
+
+ oldAttributes = printAttributes[0];
+
+ onLayoutCalled();
+ return null;
+ }, invocation -> {
+ Object[] args = invocation.getArguments();
+ PageRange[] pages = (PageRange[]) args[0];
+ ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
+ PrintDocumentAdapter.WriteResultCallback callback =
+ (PrintDocumentAdapter.WriteResultCallback) args[3];
+
+ writeBlankPages(printAttributes[0], fd, pages[0].getStart(), pages[0].getEnd());
+ fd.close();
+ callback.onWriteFinished(pages);
+
+ onWriteCalled();
+ return null;
+ }, invocation -> {
+ onFinishCalled();
+ return null;
+ });
+ }
+
@SuppressWarnings("unchecked")
protected PrinterDiscoverySessionCallbacks createMockPrinterDiscoverySessionCallbacks(
Answer<Void> onStartPrinterDiscovery, Answer<Void> onStopPrinterDiscovery,
@@ -697,7 +722,7 @@
Answer<Void> onPrintJobQueued, Answer<Void> onRequestCancelPrintJob) {
final PrintServiceCallbacks service = mock(PrintServiceCallbacks.class);
- doCallRealMethod().when(service).setService(any(PrintService.class));
+ doCallRealMethod().when(service).setService(any(StubbablePrintService.class));
when(service.getService()).thenCallRealMethod();
if (onCreatePrinterDiscoverySessionCallbacks != null) {
@@ -725,10 +750,28 @@
}
FileOutputStream fos = new FileOutputStream(output.getFileDescriptor());
document.writeTo(fos);
+ fos.flush();
document.close();
}
- protected static final class CallCounter {
+ protected void selectPages(String pages, int totalPages) throws Exception {
+ UiObject pagesSpinner = getUiDevice().findObject(new UiSelector().resourceId(
+ "com.android.printspooler:id/range_options_spinner"));
+ pagesSpinner.click();
+
+ UiObject rangeOption = getUiDevice().findObject(new UiSelector().textContains(
+ getPrintSpoolerStringOneParam("template_page_range", totalPages)));
+ rangeOption.click();
+
+ UiObject pagesEditText = getUiDevice().findObject(new UiSelector().resourceId(
+ "com.android.printspooler:id/page_range_edittext"));
+ pagesEditText.setText(pages);
+
+ // Hide the keyboard.
+ getUiDevice().pressBack();
+ }
+
+ private static final class CallCounter {
private final Object mLock = new Object();
private int mCallCount;
@@ -740,7 +783,7 @@
}
}
- public int getCallCount() {
+ int getCallCount() {
synchronized (mLock) {
return mCallCount;
}
@@ -779,7 +822,7 @@
* @param adapter The {@link PrintDocumentAdapter} used
* @throws Exception If the printer could not be made default
*/
- protected void makeDefaultPrinter(PrintDocumentAdapter adapter, String printerName)
+ void makeDefaultPrinter(PrintDocumentAdapter adapter, String printerName)
throws Exception {
// Perform a full print operation on the printer
Log.d(LOG_TAG, "print");
@@ -807,4 +850,76 @@
return getInstrumentation().getContext().getPackageManager()
.hasSystemFeature(PackageManager.FEATURE_PRINTING);
}
+
+ /**
+ * Get a string array from the print spooler's resources.
+ *
+ * @param resourceName The name of the array resource
+ * @return The localized string array
+ *
+ * @throws Exception If anything is unexpected
+ */
+ String[] getPrintSpoolerStringArray(String resourceName) throws Exception {
+ PackageManager pm = getActivity().getPackageManager();
+ Resources printSpoolerRes = pm.getResourcesForApplication(PRINTSPOOLER_PACKAGE);
+ int id = printSpoolerRes.getIdentifier(resourceName, "array", PRINTSPOOLER_PACKAGE);
+ return printSpoolerRes.getStringArray(id);
+ }
+
+ /**
+ * Get a string from the print spooler's resources.
+ *
+ * @param resourceName The name of the string resource
+ * @return The localized string
+ *
+ * @throws Exception If anything is unexpected
+ */
+ String getPrintSpoolerString(String resourceName) throws Exception {
+ PackageManager pm = getActivity().getPackageManager();
+ Resources printSpoolerRes = pm.getResourcesForApplication(PRINTSPOOLER_PACKAGE);
+ int id = printSpoolerRes.getIdentifier(resourceName, "string", PRINTSPOOLER_PACKAGE);
+ return printSpoolerRes.getString(id);
+ }
+
+ /**
+ * Get a string with one parameter from the print spooler's resources.
+ *
+ * @param resourceName The name of the string resource
+ * @return The localized string
+ *
+ * @throws Exception If anything is unexpected
+ */
+ String getPrintSpoolerStringOneParam(String resourceName, Object p)
+ throws Exception {
+ PackageManager pm = getActivity().getPackageManager();
+ Resources printSpoolerRes = pm.getResourcesForApplication(PRINTSPOOLER_PACKAGE);
+ int id = printSpoolerRes.getIdentifier(resourceName, "string", PRINTSPOOLER_PACKAGE);
+ return printSpoolerRes.getString(id, p);
+ }
+
+ /**
+ * Get the default media size for the current locale.
+ *
+ * @return The default media size for the current locale
+ *
+ * @throws Exception If anything is unexpected
+ */
+ PrintAttributes.MediaSize getDefaultMediaSize() throws Exception {
+ PackageManager pm = getActivity().getPackageManager();
+ Resources printSpoolerRes = pm.getResourcesForApplication(PRINTSPOOLER_PACKAGE);
+ int defaultMediaSizeResId = printSpoolerRes.getIdentifier("mediasize_default", "string",
+ PRINTSPOOLER_PACKAGE);
+ String defaultMediaSizeName = printSpoolerRes.getString(defaultMediaSizeResId);
+
+ switch (defaultMediaSizeName) {
+ case "NA_LETTER":
+ return PrintAttributes.MediaSize.NA_LETTER;
+ case "JIS_B5":
+ return PrintAttributes.MediaSize.JIS_B5;
+ case "ISO_A4":
+ return PrintAttributes.MediaSize.ISO_A4;
+ default:
+ throw new Exception("Unknown default media size " + defaultMediaSizeName);
+ }
+ }
}
diff --git a/tests/tests/print/src/android/print/cts/ClassParametersTest.java b/tests/tests/print/src/android/print/cts/ClassParametersTest.java
index bad47f8..f216dae 100644
--- a/tests/tests/print/src/android/print/cts/ClassParametersTest.java
+++ b/tests/tests/print/src/android/print/cts/ClassParametersTest.java
@@ -24,39 +24,24 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import static android.print.cts.Utils.assertException;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
/**
- * Test that the print attributes are correctly propagated through the print framework
+ * Test that the print attributes can be constructed correctly. This does not test that the
+ * attributes have the desired effect when send to the print framework.
*/
@RunWith(AndroidJUnit4.class)
public class ClassParametersTest {
/**
- * Run a runnable and expect and exception of a certain type.
- *
- * @param r The runnable to run
- * @param expectedClass The expected exception type
- */
- private void assertException(Runnable r, Class<? extends RuntimeException> expectedClass) {
- try {
- r.run();
- } catch (Exception e) {
- if (e.getClass().isAssignableFrom(expectedClass)) {
- return;
- } else {
- throw new AssertionError("Expected: " + expectedClass.getName() + ", got: "
- + e.getClass().getName());
- }
- }
-
- throw new AssertionError("No exception thrown");
- }
-
- /**
- * Test that we cannot create PrintAttributes with illegal parameters.
+ * Test that we cannot create PrintAttributes.colorModes with illegal parameters.
*
* @throws Exception If anything is unexpected
*/
@Test
- public void testIllegalPrintAttributes() throws Exception {
+ public void testIllegalPrintAttributesColorMode() throws Throwable {
assertException(() -> (new PrintAttributes.Builder()).setColorMode(-1),
IllegalArgumentException.class);
assertException(() -> (new PrintAttributes.Builder()).setColorMode(0),
@@ -64,7 +49,15 @@
assertException(() -> (new PrintAttributes.Builder()).setColorMode(
PrintAttributes.COLOR_MODE_COLOR | PrintAttributes.COLOR_MODE_MONOCHROME),
IllegalArgumentException.class);
+ }
+ /**
+ * Test that we cannot create PrintAttributes.duplexMode with illegal parameters.
+ *
+ * @throws Exception If anything is unexpected
+ */
+ @Test
+ public void testIllegalPrintAttributesDuplexMode() throws Throwable {
assertException(() -> (new PrintAttributes.Builder()).setDuplexMode(-1),
IllegalArgumentException.class);
assertException(() -> (new PrintAttributes.Builder()).setDuplexMode(0),
@@ -72,7 +65,15 @@
assertException(() -> (new PrintAttributes.Builder()).setDuplexMode(
PrintAttributes.DUPLEX_MODE_LONG_EDGE | PrintAttributes.DUPLEX_MODE_NONE),
IllegalArgumentException.class);
+ }
+ /**
+ * Test that we cannot create PrintAttributes.resolution with illegal parameters.
+ *
+ * @throws Exception If anything is unexpected
+ */
+ @Test
+ public void testIllegalPrintAttributesResolution() throws Throwable {
assertException(() -> new Resolution(null, "label", 10, 10),
IllegalArgumentException.class);
assertException(() -> new Resolution("", "label", 10, 10),
@@ -89,7 +90,45 @@
IllegalArgumentException.class);
assertException(() -> new Resolution("id", "label", 10, 0),
IllegalArgumentException.class);
+ }
+ /**
+ * Test that we can create PrintAttributes.resolution with legal parameters.
+ *
+ * @throws Exception If anything is unexpected
+ */
+ @Test
+ public void testLegalPrintAttributesResolution() throws Exception {
+ // Small resolution
+ Resolution testResolution = new Resolution("testId", "testLabel", 1, 2);
+ assertEquals("testId", testResolution.getId());
+ assertEquals("testLabel", testResolution.getLabel());
+ assertEquals(1, testResolution.getHorizontalDpi());
+ assertEquals(2, testResolution.getVerticalDpi());
+
+ // Small even resolution
+ Resolution testResolution2 = new Resolution("testId2", "testLabel2", 1, 1);
+ assertEquals("testId2", testResolution2.getId());
+ assertEquals("testLabel2", testResolution2.getLabel());
+ assertEquals(1, testResolution2.getHorizontalDpi());
+ assertEquals(1, testResolution2.getVerticalDpi());
+
+ // Large even resolution
+ Resolution testResolution3 = new Resolution("testId3", "testLabel3", Integer.MAX_VALUE,
+ Integer.MAX_VALUE);
+ assertEquals("testId3", testResolution3.getId());
+ assertEquals("testLabel3", testResolution3.getLabel());
+ assertEquals(Integer.MAX_VALUE, testResolution3.getHorizontalDpi());
+ assertEquals(Integer.MAX_VALUE, testResolution3.getVerticalDpi());
+ }
+
+ /**
+ * Test that we cannot create PrintAttributes.mediaSize with illegal parameters.
+ *
+ * @throws Exception If anything is unexpected
+ */
+ @Test
+ public void testIllegalPrintAttributesMediaSize() throws Throwable {
assertException(() -> new MediaSize(null, "label", 10, 10),
IllegalArgumentException.class);
assertException(() -> new MediaSize("", "label", 10, 10),
@@ -106,8 +145,81 @@
IllegalArgumentException.class);
assertException(() -> new MediaSize("id", "label", 10, 0),
IllegalArgumentException.class);
+ }
- // There is no restrictions on what parameters to set for minMargins.
+ /**
+ * Test that we can create PrintAttributes.mediaSize with legal parameters.
+ *
+ * @throws Exception If anything is unexpected
+ */
+ @Test
+ public void testLegalPrintAttributesMediaSize() throws Exception {
+ // Small portrait paper
+ MediaSize testMediaSize1 = new MediaSize("testId", "testLabel", 1, 2);
+ assertEquals("testId", testMediaSize1.getId());
+ assertEquals("testLabel", testMediaSize1.getLabel(null));
+ assertEquals(1, testMediaSize1.getWidthMils());
+ assertEquals(2, testMediaSize1.getHeightMils());
+ assertTrue(testMediaSize1.isPortrait());
+
+ MediaSize testMediaSize1L = testMediaSize1.asLandscape();
+ assertEquals("testId", testMediaSize1L.getId());
+ assertEquals("testLabel", testMediaSize1L.getLabel(null));
+ assertEquals(2, testMediaSize1L.getWidthMils());
+ assertEquals(1, testMediaSize1L.getHeightMils());
+ assertFalse(testMediaSize1L.isPortrait());
+
+ MediaSize testMediaSize1P = testMediaSize1.asPortrait();
+ assertEquals("testId", testMediaSize1P.getId());
+ assertEquals("testLabel", testMediaSize1P.getLabel(null));
+ assertEquals(1, testMediaSize1P.getWidthMils());
+ assertEquals(2, testMediaSize1P.getHeightMils());
+ assertTrue(testMediaSize1P.isPortrait());
+
+ // Small square paper
+ MediaSize testMediaSize2 = new MediaSize("testId2", "testLabel2", 1, 1);
+ assertEquals("testId2", testMediaSize2.getId());
+ assertEquals("testLabel2", testMediaSize2.getLabel(null));
+ assertEquals(1, testMediaSize2.getWidthMils());
+ assertEquals(1, testMediaSize2.getHeightMils());
+ assertTrue(testMediaSize2.isPortrait());
+
+ MediaSize testMediaSize2L = testMediaSize2.asLandscape();
+ assertEquals("testId2", testMediaSize2L.getId());
+ assertEquals("testLabel2", testMediaSize2L.getLabel(null));
+ assertEquals(1, testMediaSize2L.getWidthMils());
+ assertEquals(1, testMediaSize2L.getHeightMils());
+ assertTrue(testMediaSize2L.isPortrait());
+
+ MediaSize testMediaSize2P = testMediaSize2.asPortrait();
+ assertEquals("testId2", testMediaSize2P.getId());
+ assertEquals("testLabel2", testMediaSize2P.getLabel(null));
+ assertEquals(1, testMediaSize2P.getWidthMils());
+ assertEquals(1, testMediaSize2P.getHeightMils());
+ assertTrue(testMediaSize2P.isPortrait());
+
+ // Large landscape paper
+ MediaSize testMediaSize3 = new MediaSize("testId3", "testLabel3", Integer.MAX_VALUE,
+ Integer.MAX_VALUE - 1);
+ assertEquals("testId3", testMediaSize3.getId());
+ assertEquals("testLabel3", testMediaSize3.getLabel(null));
+ assertEquals(Integer.MAX_VALUE, testMediaSize3.getWidthMils());
+ assertEquals(Integer.MAX_VALUE - 1, testMediaSize3.getHeightMils());
+ assertFalse(testMediaSize3.isPortrait());
+
+ MediaSize testMediaSize3L = testMediaSize3.asLandscape();
+ assertEquals("testId3", testMediaSize3L.getId());
+ assertEquals("testLabel3", testMediaSize3L.getLabel(null));
+ assertEquals(Integer.MAX_VALUE, testMediaSize3L.getWidthMils());
+ assertEquals(Integer.MAX_VALUE - 1, testMediaSize3L.getHeightMils());
+ assertFalse(testMediaSize3L.isPortrait());
+
+ MediaSize testMediaSize3P = testMediaSize3.asPortrait();
+ assertEquals("testId3", testMediaSize3P.getId());
+ assertEquals("testLabel3", testMediaSize3P.getLabel(null));
+ assertEquals(Integer.MAX_VALUE - 1, testMediaSize3P.getWidthMils());
+ assertEquals(Integer.MAX_VALUE, testMediaSize3P.getHeightMils());
+ assertTrue(testMediaSize3P.isPortrait());
}
/**
@@ -116,7 +228,7 @@
* @throws Exception If anything is unexpected
*/
@Test
- public void testIllegalPrintDocumentInfo() throws Exception {
+ public void testIllegalPrintDocumentInfo() throws Throwable {
assertException(() -> new PrintDocumentInfo.Builder(null),
IllegalArgumentException.class);
assertException(() -> new PrintDocumentInfo.Builder(""),
@@ -124,8 +236,45 @@
assertException(() -> new PrintDocumentInfo.Builder("doc").setPageCount(-2),
IllegalArgumentException.class);
- // -1 == UNKNOWN and 0 are allowed
+ }
- // Content type is not restricted
+ /**
+ * Test that we can create PrintDocumentInfo with legal parameters.
+ *
+ * @throws Exception If anything is unexpected
+ */
+ @Test
+ public void testLegalPrintDocumentInfo() throws Exception {
+ PrintDocumentInfo defaultInfo = new PrintDocumentInfo.Builder("doc").build();
+ assertEquals(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT, defaultInfo.getContentType());
+ assertEquals(PrintDocumentInfo.PAGE_COUNT_UNKNOWN, defaultInfo.getPageCount());
+ assertEquals(0, defaultInfo.getDataSize());
+ assertEquals("doc", defaultInfo.getName());
+
+ PrintDocumentInfo info = new PrintDocumentInfo.Builder("doc")
+ .setContentType(PrintDocumentInfo.CONTENT_TYPE_UNKNOWN).build();
+ assertEquals(PrintDocumentInfo.CONTENT_TYPE_UNKNOWN, info.getContentType());
+
+ PrintDocumentInfo info2 = new PrintDocumentInfo.Builder("doc")
+ .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT).build();
+ assertEquals(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT, info2.getContentType());
+
+ PrintDocumentInfo info3 = new PrintDocumentInfo.Builder("doc")
+ .setContentType(PrintDocumentInfo.CONTENT_TYPE_PHOTO).build();
+ assertEquals(PrintDocumentInfo.CONTENT_TYPE_PHOTO, info3.getContentType());
+
+ PrintDocumentInfo info4 = new PrintDocumentInfo.Builder("doc").setContentType(-23).build();
+ assertEquals(-23, info4.getContentType());
+
+ PrintDocumentInfo info5 = new PrintDocumentInfo.Builder("doc").setPageCount(0).build();
+ assertEquals(PrintDocumentInfo.PAGE_COUNT_UNKNOWN, info5.getPageCount());
+
+ PrintDocumentInfo info6 = new PrintDocumentInfo.Builder("doc")
+ .setPageCount(PrintDocumentInfo.PAGE_COUNT_UNKNOWN).build();
+ assertEquals(PrintDocumentInfo.PAGE_COUNT_UNKNOWN, info6.getPageCount());
+
+ PrintDocumentInfo info7 = new PrintDocumentInfo.Builder("doc")
+ .setPageCount(Integer.MAX_VALUE).build();
+ assertEquals(Integer.MAX_VALUE, info7.getPageCount());
}
}
diff --git a/tests/tests/print/src/android/print/cts/CustomPrintOptionsTest.java b/tests/tests/print/src/android/print/cts/CustomPrintOptionsTest.java
index 54b4d23..9dd8bc9 100644
--- a/tests/tests/print/src/android/print/cts/CustomPrintOptionsTest.java
+++ b/tests/tests/print/src/android/print/cts/CustomPrintOptionsTest.java
@@ -49,6 +49,8 @@
import java.util.List;
import java.util.concurrent.TimeoutException;
+import static android.print.cts.Utils.eventually;
+
/**
* This test verifies changes to the printer capabilities are applied correctly.
*/
@@ -87,7 +89,8 @@
* @throws Exception If something was unexpected
*/
private PageRange[] getPages() throws Exception {
- if (getUiDevice().hasObject(By.text("All 3"))) {
+ if (getUiDevice().hasObject(By.text(getPrintSpoolerStringOneParam("template_all_pages",
+ 3)))) {
return PAGESS[2];
}
@@ -344,25 +347,14 @@
}
Log.d(LOG_TAG, "Check pages");
- while (true) {
- try {
- PageRange[] actualPages = getPages();
- if (!Arrays.equals(newPages, actualPages)) {
- new AssertionError(
- "Expected " + Arrays.toString(newPages) + ", actual " +
- Arrays.toString(actualPages));
- }
-
- break;
- } catch (Throwable e) {
- if (endTime < System.currentTimeMillis()) {
- throw e;
- } else {
- Log.e(LOG_TAG, "Could not verify pages, retrying", e);
- Thread.sleep(100);
- }
+ eventually(() -> {
+ PageRange[] actualPages = getPages();
+ if (!Arrays.equals(newPages, actualPages)) {
+ throw new AssertionError(
+ "Expected " + Arrays.toString(newPages) + ", actual " +
+ Arrays.toString(actualPages));
}
- }
+ });
break;
} catch (Throwable e) {
@@ -378,13 +370,15 @@
}
// Abort printing
- getActivity().finish();
+ getUiDevice().pressBack();
+ getUiDevice().pressBack();
+ getUiDevice().pressBack();
waitForPrinterDiscoverySessionDestroyCallbackCalled(1);
}
- public void testChangeToChangeEveryThing() throws Throwable {
- testCase(false, 2, PAGESS[1], MEDIA_SIZES[1], false, COLOR_MODES[1], DUPLEX_MODES[1],
+ public void testChangeToChangeEveryThingButPages() throws Throwable {
+ testCase(false, 2, null, MEDIA_SIZES[1], false, COLOR_MODES[1], DUPLEX_MODES[1],
RESOLUTIONS[1]);
}
@@ -427,4 +421,8 @@
public void testChangeToAllPages() throws Throwable {
testCase(false, null, PAGESS[2], null, true, null, null, null);
}
+
+ public void testChangeToSomePages() throws Throwable {
+ testCase(false, null, PAGESS[1], null, true, null, null, null);
+ }
}
diff --git a/tests/tests/print/src/android/print/cts/InterfaceForAppsTest.java b/tests/tests/print/src/android/print/cts/InterfaceForAppsTest.java
new file mode 100644
index 0000000..d080815
--- /dev/null
+++ b/tests/tests/print/src/android/print/cts/InterfaceForAppsTest.java
@@ -0,0 +1,382 @@
+/*
+ * 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.print.cts;
+
+import android.print.PrintAttributes;
+import android.print.PrintDocumentAdapter;
+import android.print.PrintJob;
+import android.print.PrintJobInfo;
+import android.print.PrinterCapabilitiesInfo;
+import android.print.PrinterId;
+import android.print.PrinterInfo;
+import android.print.cts.services.FirstPrintService;
+import android.print.cts.services.PrintServiceCallbacks;
+import android.print.cts.services.PrinterDiscoverySessionCallbacks;
+import android.print.cts.services.SecondPrintService;
+import android.print.cts.services.StubbablePrinterDiscoverySession;
+import android.support.annotation.NonNull;
+import android.util.Log;
+
+import java.util.ArrayList;
+
+import static android.print.cts.Utils.eventually;
+import static android.print.cts.Utils.getPrintJob;
+import static android.print.cts.Utils.getPrintManager;
+
+/**
+ * Test interface from the application to the print service.
+ */
+public class InterfaceForAppsTest extends BasePrintTest {
+ private static final String TEST_PRINTER = "Test printer";
+ private static final String LOG_TAG = "InterfaceForAppsTest";
+
+ private static final PrintAttributes.Resolution TWO_HUNDRED_DPI =
+ new PrintAttributes.Resolution("200x200", "200dpi", 200, 200);
+
+ /**
+ * Create a mock {@link PrinterDiscoverySessionCallbacks} that discovers a simple test printer.
+ *
+ * @return The mock session callbacks
+ */
+ private @NonNull PrinterDiscoverySessionCallbacks createFirstMockPrinterDiscoverySessionCallbacks() {
+ return createMockPrinterDiscoverySessionCallbacks(invocation -> {
+ StubbablePrinterDiscoverySession session = ((PrinterDiscoverySessionCallbacks) invocation
+ .getMock()).getSession();
+
+ if (session.getPrinters().isEmpty()) {
+ PrinterId printerId = session.getService().generatePrinterId(TEST_PRINTER);
+ PrinterInfo.Builder printer = new PrinterInfo.Builder(
+ session.getService().generatePrinterId(TEST_PRINTER), TEST_PRINTER,
+ PrinterInfo.STATUS_IDLE);
+
+ printer.setCapabilities(new PrinterCapabilitiesInfo.Builder(printerId)
+ .addMediaSize(PrintAttributes.MediaSize.ISO_A5, true)
+ .addMediaSize(PrintAttributes.MediaSize.ISO_A3, false)
+ .addResolution(TWO_HUNDRED_DPI, true)
+ .setColorModes(PrintAttributes.COLOR_MODE_MONOCHROME
+ | PrintAttributes.COLOR_MODE_COLOR,
+ PrintAttributes.COLOR_MODE_MONOCHROME)
+ .setDuplexModes(PrintAttributes.DUPLEX_MODE_NONE
+ | PrintAttributes.DUPLEX_MODE_LONG_EDGE,
+ PrintAttributes.DUPLEX_MODE_NONE)
+ .setMinMargins(new PrintAttributes.Margins(0, 0, 0, 0)).build());
+
+ ArrayList<PrinterInfo> printers = new ArrayList<>(1);
+ printers.add(printer.build());
+
+ session.addPrinters(printers);
+ }
+ return null;
+ }, null, null, invocation -> null, null, null, invocation -> {
+ // Take a note onDestroy was called.
+ onPrinterDiscoverySessionDestroyCalled();
+ return null;
+ });
+ }
+
+ /**
+ * Create mock service callback for a session. Once the job is queued the test function is
+ * called.
+ *
+ * @param sessionCallbacks The callbacks of the session
+ * @param blockAfterState The state the print services should progress to
+ */
+ private @NonNull PrintServiceCallbacks createFirstMockPrinterServiceCallbacks(
+ final @NonNull PrinterDiscoverySessionCallbacks sessionCallbacks, int blockAfterState) {
+ return createMockPrintServiceCallbacks(
+ invocation -> sessionCallbacks, invocation -> {
+ android.printservice.PrintJob job = (android.printservice.PrintJob) invocation
+ .getArguments()[0];
+
+ switch (blockAfterState) {
+ case PrintJobInfo.STATE_CREATED:
+ eventually(() -> assertEquals(PrintJobInfo.STATE_CREATED,
+ job.getInfo().getState()));
+ break;
+ case PrintJobInfo.STATE_STARTED:
+ eventually(() -> assertTrue(job.isQueued()));
+ job.start();
+ break;
+ case PrintJobInfo.STATE_QUEUED:
+ eventually(() -> assertTrue(job.isQueued()));
+ break;
+ case PrintJobInfo.STATE_BLOCKED:
+ eventually(() -> assertTrue(job.isQueued()));
+ job.start();
+ job.block("test block");
+ break;
+ case PrintJobInfo.STATE_FAILED:
+ eventually(() -> assertTrue(job.isQueued()));
+ job.start();
+ job.fail("test fail");
+ break;
+ case PrintJobInfo.STATE_COMPLETED:
+ eventually(() -> assertTrue(job.isQueued()));
+ job.start();
+ job.complete();
+ break;
+ default:
+ throw new Exception("Should not be reached");
+ }
+
+ return null;
+ }, invocation -> {
+ android.printservice.PrintJob job = (android.printservice.PrintJob) invocation
+ .getArguments()[0];
+
+ job.cancel();
+ Log.d(LOG_TAG, "job.cancel()");
+
+ return null;
+ });
+ }
+
+ /**
+ * Setup mock print subsystem
+ *
+ * @param blockAfterState Tell the print service to block all print jobs at this state
+ *
+ * @return The print document adapter to be used for printing
+ */
+ private @NonNull PrintDocumentAdapter setupPrint(int blockAfterState) {
+ // Create the session of the printers that we will be checking.
+ PrinterDiscoverySessionCallbacks sessionCallbacks = createFirstMockPrinterDiscoverySessionCallbacks();
+
+ // Create the service callbacks for the first print service.
+ PrintServiceCallbacks serviceCallbacks = createFirstMockPrinterServiceCallbacks(
+ sessionCallbacks, blockAfterState);
+
+ // Configure the print services.
+ FirstPrintService.setCallbacks(serviceCallbacks);
+
+ // We don't use the second service, but we have to still configure it
+ SecondPrintService.setCallbacks(createMockPrintServiceCallbacks(null, null, null));
+
+ return createDefaultPrintDocumentAdapter(1);
+ }
+
+ /**
+ * Base test for all cancel print job tests
+ *
+ * @param cancelAfterState The print job state state to progress to canceling
+ * @param printJobName The print job name to use
+ *
+ * @throws Exception If anything is unexpected
+ */
+ private void cancelPrintJobBaseTest(int cancelAfterState, @NonNull String printJobName)
+ throws Throwable {
+ PrintDocumentAdapter adapter = setupPrint(cancelAfterState);
+
+ print(adapter, printJobName);
+ waitForWriteAdapterCallback(1);
+
+ selectPrinter(TEST_PRINTER);
+ waitForWriteAdapterCallback(2);
+
+ clickPrintButton();
+ answerPrintServicesWarning(true);
+
+ PrintJob job = getPrintJob(getPrintManager(getActivity()), printJobName);
+
+ // Check getState
+ eventually(() -> assertEquals(cancelAfterState, job.getInfo().getState()));
+
+ // Check
+ switch (cancelAfterState) {
+ case PrintJobInfo.STATE_QUEUED:
+ assertTrue(job.isQueued());
+ break;
+ case PrintJobInfo.STATE_STARTED:
+ assertTrue(job.isStarted());
+ break;
+ case PrintJobInfo.STATE_BLOCKED:
+ assertTrue(job.isBlocked());
+ break;
+ case PrintJobInfo.STATE_COMPLETED:
+ assertTrue(job.isCompleted());
+ break;
+ case PrintJobInfo.STATE_FAILED:
+ assertTrue(job.isFailed());
+ break;
+ case PrintJobInfo.STATE_CANCELED:
+ assertTrue(job.isCancelled());
+ break;
+ }
+
+ job.cancel();
+ eventually(() -> assertTrue(job.isCancelled()));
+
+ waitForPrinterDiscoverySessionDestroyCallbackCalled(1);
+ }
+
+ public void testAttemptCancelCreatedPrintJob() throws Throwable {
+ PrintDocumentAdapter adapter = setupPrint(PrintJobInfo.STATE_STARTED);
+
+ print(adapter, "testAttemptCancelCreatedPrintJob");
+ waitForWriteAdapterCallback(1);
+
+ selectPrinter(TEST_PRINTER);
+ waitForWriteAdapterCallback(2);
+
+ PrintJob job = getPrintJob(getPrintManager(getActivity()),
+ "testAttemptCancelCreatedPrintJob");
+
+ // Cancel does not have an effect on created jobs
+ job.cancel();
+ eventually(() -> assertEquals(PrintJobInfo.STATE_CREATED, job.getInfo().getState()));
+
+ // Cancel printing by exiting print activity
+ getUiDevice().pressBack();
+ eventually(() -> assertTrue(job.isCancelled()));
+
+ waitForPrinterDiscoverySessionDestroyCallbackCalled(1);
+ }
+
+ public void testCancelStartedPrintJob() throws Throwable {
+ cancelPrintJobBaseTest(PrintJobInfo.STATE_STARTED, "testCancelStartedPrintJob");
+ }
+
+ public void testCancelBlockedPrintJob() throws Throwable {
+ cancelPrintJobBaseTest(PrintJobInfo.STATE_BLOCKED, "testCancelBlockedPrintJob");
+ }
+
+ public void testCancelFailedPrintJob() throws Throwable {
+ cancelPrintJobBaseTest(PrintJobInfo.STATE_FAILED, "testCancelFailedPrintJob");
+ }
+
+ public void testRestartFailedPrintJob() throws Throwable {
+ PrintDocumentAdapter adapter = setupPrint(PrintJobInfo.STATE_FAILED);
+
+ print(adapter, "testRestartFailedPrintJob");
+ waitForWriteAdapterCallback(1);
+
+ selectPrinter(TEST_PRINTER);
+ waitForWriteAdapterCallback(2);
+
+ clickPrintButton();
+ answerPrintServicesWarning(true);
+
+ PrintJob job = getPrintJob(getPrintManager(getActivity()), "testRestartFailedPrintJob");
+
+ eventually(() -> assertTrue(job.isFailed()));
+
+ // Restart goes from failed right to queued, so stop the print job at "queued" now
+ setupPrint(PrintJobInfo.STATE_QUEUED);
+
+ job.restart();
+ eventually(() -> assertTrue(job.isQueued()));
+
+ job.cancel();
+ eventually(() -> assertTrue(job.isCancelled()));
+
+ waitForPrinterDiscoverySessionDestroyCallbackCalled(1);
+ }
+
+ public void testGetTwoPrintJobStates() throws Throwable {
+ PrintDocumentAdapter adapter = setupPrint(PrintJobInfo.STATE_BLOCKED);
+ print(adapter, "testGetTwoPrintJobStates-block");
+ waitForWriteAdapterCallback(1);
+
+ selectPrinter(TEST_PRINTER);
+ waitForWriteAdapterCallback(2);
+
+ clickPrintButton();
+ answerPrintServicesWarning(true);
+
+ waitForPrinterDiscoverySessionDestroyCallbackCalled(1);
+
+ PrintJob job1 = getPrintJob(getPrintManager(getActivity()),
+ "testGetTwoPrintJobStates-block");
+ eventually(() -> assertTrue(job1.isBlocked()));
+
+ adapter = setupPrint(PrintJobInfo.STATE_COMPLETED);
+ print(adapter, "testGetTwoPrintJobStates-complete");
+ waitForWriteAdapterCallback(3);
+ clickPrintButton();
+
+ PrintJob job2 = getPrintJob(getPrintManager(getActivity()),
+ "testGetTwoPrintJobStates-complete");
+ eventually(() -> assertTrue(job2.isCompleted()));
+
+ // Ids have to be unique
+ assertFalse(job1.getId().equals(job2.getId()));
+ assertFalse(job1.equals(job2));
+
+ // Ids have to be the same in job and info and if we find the same job again
+ assertEquals(job1.getId(), job1.getInfo().getId());
+ assertEquals(job1.getId(),
+ getPrintJob(getPrintManager(getActivity()),
+ "testGetTwoPrintJobStates-block").getId());
+ assertEquals(job1, getPrintJob(getPrintManager(getActivity()),
+ "testGetTwoPrintJobStates-block"));
+ assertEquals(job2.getId(), job2.getInfo().getId());
+ assertEquals(job2.getId(), getPrintJob(getPrintManager(getActivity()),
+ "testGetTwoPrintJobStates-complete").getId());
+ assertEquals(job2, getPrintJob(getPrintManager(getActivity()),
+ "testGetTwoPrintJobStates-complete"));
+
+ // First print job should still be there
+ PrintJob job1again = getPrintJob(getPrintManager(getActivity()),
+ "testGetTwoPrintJobStates-block");
+ assertTrue(job1again.isBlocked());
+
+ waitForPrinterDiscoverySessionDestroyCallbackCalled(2);
+ }
+
+ public void testChangedPrintJobInfo() throws Throwable {
+ PrintDocumentAdapter adapter = setupPrint(PrintJobInfo.STATE_COMPLETED);
+ long beforeStart = System.currentTimeMillis();
+ print(adapter, "testPrintJobInfo");
+ waitForWriteAdapterCallback(1);
+ long afterStart = System.currentTimeMillis();
+
+ selectPrinter(TEST_PRINTER);
+ waitForWriteAdapterCallback(2);
+
+ PrintJob job = getPrintJob(getPrintManager(getActivity()), "testPrintJobInfo");
+
+ // Set some non default options
+ openPrintOptions();
+ changeCopies(2);
+ changeColor(getPrintSpoolerStringArray("color_mode_labels")[1]);
+ // Leave duplex as default to test that defaults are retained
+ changeMediaSize(
+ PrintAttributes.MediaSize.ISO_A3.getLabel(getActivity().getPackageManager()));
+ changeOrientation(getPrintSpoolerStringArray("orientation_labels")[1]);
+
+ // Print and wait until it is completed
+ clickPrintButton();
+ answerPrintServicesWarning(true);
+ waitForPrinterDiscoverySessionDestroyCallbackCalled(1);
+ eventually(job::isCompleted);
+
+ // Make sure all options were applied
+ assertEquals(TEST_PRINTER, job.getInfo().getPrinterId().getLocalId());
+ assertEquals(2, job.getInfo().getCopies());
+ assertEquals(PrintAttributes.COLOR_MODE_COLOR,
+ job.getInfo().getAttributes().getColorMode());
+ assertEquals(PrintAttributes.DUPLEX_MODE_NONE,
+ job.getInfo().getAttributes().getDuplexMode());
+ assertEquals(PrintAttributes.MediaSize.ISO_A3.asLandscape(),
+ job.getInfo().getAttributes().getMediaSize());
+ assertEquals(TWO_HUNDRED_DPI, job.getInfo().getAttributes().getResolution());
+
+ // Check creation time with 5 sec jitter allowance
+ assertTrue(beforeStart - 5000 <= job.getInfo().getCreationTime());
+ assertTrue(job.getInfo().getCreationTime() <= afterStart + 5000);
+ }
+}
diff --git a/tests/tests/print/src/android/print/cts/PageRangeAdjustmentTest.java b/tests/tests/print/src/android/print/cts/PageRangeAdjustmentTest.java
index df142b9..5d7ff3a 100644
--- a/tests/tests/print/src/android/print/cts/PageRangeAdjustmentTest.java
+++ b/tests/tests/print/src/android/print/cts/PageRangeAdjustmentTest.java
@@ -41,13 +41,8 @@
import android.print.cts.services.StubbablePrinterDiscoverySession;
import android.printservice.PrintJob;
import android.printservice.PrintService;
-import android.support.test.uiautomator.UiObject;
-import android.support.test.uiautomator.UiObjectNotFoundException;
-import android.support.test.uiautomator.UiSelector;
import org.mockito.InOrder;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
import java.util.ArrayList;
import java.util.List;
@@ -70,68 +65,22 @@
// Create a callback for the target print service.
PrintServiceCallbacks firstServiceCallbacks = createMockPrintServiceCallbacks(
- new Answer<PrinterDiscoverySessionCallbacks>() {
- @Override
- public PrinterDiscoverySessionCallbacks answer(InvocationOnMock invocation) {
- return createMockFirstPrinterDiscoverySessionCallbacks();
- }
- },
- new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) {
- PrintJob printJob = (PrintJob) invocation.getArguments()[0];
- PageRange[] pages = printJob.getInfo().getPages();
- assertTrue(pages.length == 1 && PageRange.ALL_PAGES.equals(pages[0]));
- printJob.complete();
- onPrintJobQueuedCalled();
- return null;
- }
- }, null);
-
- final PrintAttributes[] printAttributes = new PrintAttributes[1];
+ invocation -> createMockFirstPrinterDiscoverySessionCallbacks(),
+ invocation -> {
+ PrintJob printJob = (PrintJob) invocation.getArguments()[0];
+ PageRange[] pages = printJob.getInfo().getPages();
+ assertTrue(pages.length == 1 && PageRange.ALL_PAGES.equals(pages[0]));
+ printJob.complete();
+ onPrintJobQueuedCalled();
+ return null;
+ }, null);
// Configure the print services.
FirstPrintService.setCallbacks(firstServiceCallbacks);
SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks());
// Create a mock print adapter.
- final PrintDocumentAdapter adapter = createMockPrintDocumentAdapter(
- new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- printAttributes[0] = (PrintAttributes) invocation.getArguments()[1];
- LayoutResultCallback callback = (LayoutResultCallback) invocation.getArguments()[3];
- PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
- .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
- .setPageCount(PAGE_COUNT)
- .build();
- callback.onLayoutFinished(info, false);
- // Mark layout was called.
- onLayoutCalled();
- return null;
- }
- }, new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- Object[] args = invocation.getArguments();
- PageRange[] pages = (PageRange[]) args[0];
- ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
- WriteResultCallback callback = (WriteResultCallback) args[3];
- writeBlankPages(printAttributes[0], fd, 0, PAGE_COUNT - 1);
- fd.close();
- callback.onWriteFinished(pages);
- // Mark write was called.
- onWriteCalled();
- return null;
- }
- }, new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- // Mark finish was called.
- onFinishCalled();
- return null;
- }
- });
+ final PrintDocumentAdapter adapter = createDefaultPrintDocumentAdapter(PAGE_COUNT);
// Start printing.
print(adapter);
@@ -176,25 +125,17 @@
// Create a callback for the target print service.
PrintServiceCallbacks firstServiceCallbacks = createMockPrintServiceCallbacks(
- new Answer<PrinterDiscoverySessionCallbacks>() {
- @Override
- public PrinterDiscoverySessionCallbacks answer(InvocationOnMock invocation) {
- return createMockFirstPrinterDiscoverySessionCallbacks();
- }
- },
- new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) {
- PrintJob printJob = (PrintJob) invocation.getArguments()[0];
- PageRange[] pages = printJob.getInfo().getPages();
- // We asked for some pages, the app wrote more, but the system
- // pruned extra pages, hence we expect to print all pages.
- assertTrue(pages.length == 1 && PageRange.ALL_PAGES.equals(pages[0]));
- printJob.complete();
- onPrintJobQueuedCalled();
- return null;
- }
- }, null);
+ invocation -> createMockFirstPrinterDiscoverySessionCallbacks(),
+ invocation -> {
+ PrintJob printJob = (PrintJob) invocation.getArguments()[0];
+ PageRange[] pages = printJob.getInfo().getPages();
+ // We asked for some pages, the app wrote more, but the system
+ // pruned extra pages, hence we expect to print all pages.
+ assertTrue(pages.length == 1 && PageRange.ALL_PAGES.equals(pages[0]));
+ printJob.complete();
+ onPrintJobQueuedCalled();
+ return null;
+ }, null);
final PrintAttributes[] printAttributes = new PrintAttributes[1];
@@ -204,43 +145,34 @@
// Create a mock print adapter.
final PrintDocumentAdapter adapter = createMockPrintDocumentAdapter(
- new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- printAttributes[0] = (PrintAttributes) invocation.getArguments()[1];
- LayoutResultCallback callback = (LayoutResultCallback) invocation.getArguments()[3];
- PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
- .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
- .setPageCount(PAGE_COUNT)
- .build();
- callback.onLayoutFinished(info, false);
- // Mark layout was called.
- onLayoutCalled();
- return null;
- }
- }, new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- Object[] args = invocation.getArguments();
- PageRange[] pages = (PageRange[]) args[0];
- assertTrue(pages[pages.length - 1].getEnd() < PAGE_COUNT);
- ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
- WriteResultCallback callback = (WriteResultCallback) args[3];
- writeBlankPages(printAttributes[0], fd, 0, PAGE_COUNT - 1);
- fd.close();
- callback.onWriteFinished(new PageRange[] {PageRange.ALL_PAGES});
- // Mark write was called.
- onWriteCalled();
- return null;
- }
- }, new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- // Mark finish was called.
- onFinishCalled();
- return null;
- }
- });
+ invocation -> {
+ printAttributes[0] = (PrintAttributes) invocation.getArguments()[1];
+ LayoutResultCallback callback = (LayoutResultCallback) invocation.getArguments()[3];
+ PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
+ .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
+ .setPageCount(PAGE_COUNT)
+ .build();
+ callback.onLayoutFinished(info, false);
+ // Mark layout was called.
+ onLayoutCalled();
+ return null;
+ }, invocation -> {
+ Object[] args = invocation.getArguments();
+ PageRange[] pages = (PageRange[]) args[0];
+ assertTrue(pages[pages.length - 1].getEnd() < PAGE_COUNT);
+ ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
+ WriteResultCallback callback = (WriteResultCallback) args[3];
+ writeBlankPages(printAttributes[0], fd, 0, PAGE_COUNT - 1);
+ fd.close();
+ callback.onWriteFinished(new PageRange[] {PageRange.ALL_PAGES});
+ // Mark write was called.
+ onWriteCalled();
+ return null;
+ }, invocation -> {
+ // Mark finish was called.
+ onFinishCalled();
+ return null;
+ });
// Start printing.
print(adapter);
@@ -258,7 +190,7 @@
waitForLayoutAdapterCallbackCount(2);
// Select only the second page.
- selectPages("2");
+ selectPages("2", PAGE_COUNT);
// Click the print button.
clickPrintButton();
@@ -293,28 +225,20 @@
// Create a callback for the target print service.
PrintServiceCallbacks firstServiceCallbacks = createMockPrintServiceCallbacks(
- new Answer<PrinterDiscoverySessionCallbacks>() {
- @Override
- public PrinterDiscoverySessionCallbacks answer(InvocationOnMock invocation) {
- return createMockFirstPrinterDiscoverySessionCallbacks();
- }
- },
- new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) {
- PrintJob printJob = (PrintJob) invocation.getArguments()[0];
- PrintJobInfo printJobInfo = printJob.getInfo();
- PageRange[] pages = printJobInfo.getPages();
- // We asked only for page 55 (index 54) but got 55 and 56 (indices
- // 54, 55), but the system pruned the extra page, hence we expect
- // to print all pages.
- assertTrue(pages.length == 1 && PageRange.ALL_PAGES.equals(pages[0]));
- assertSame(printJob.getDocument().getInfo().getPageCount(), 1);
- printJob.complete();
- onPrintJobQueuedCalled();
- return null;
- }
- }, null);
+ invocation -> createMockFirstPrinterDiscoverySessionCallbacks(),
+ invocation -> {
+ PrintJob printJob = (PrintJob) invocation.getArguments()[0];
+ PrintJobInfo printJobInfo = printJob.getInfo();
+ PageRange[] pages = printJobInfo.getPages();
+ // We asked only for page 55 (index 54) but got 55 and 56 (indices
+ // 54, 55), but the system pruned the extra page, hence we expect
+ // to print all pages.
+ assertTrue(pages.length == 1 && PageRange.ALL_PAGES.equals(pages[0]));
+ assertSame(printJob.getDocument().getInfo().getPageCount(), 1);
+ printJob.complete();
+ onPrintJobQueuedCalled();
+ return null;
+ }, null);
final PrintAttributes[] printAttributes = new PrintAttributes[1];
@@ -324,57 +248,48 @@
// Create a mock print adapter.
final PrintDocumentAdapter adapter = createMockPrintDocumentAdapter(
- new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- printAttributes[0] = (PrintAttributes) invocation.getArguments()[1];
- LayoutResultCallback callback = (LayoutResultCallback) invocation.getArguments()[3];
- PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
- .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
- .setPageCount(PAGE_COUNT)
- .build();
- callback.onLayoutFinished(info, false);
- // Mark layout was called.
- onLayoutCalled();
- return null;
- }
- }, new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- Object[] args = invocation.getArguments();
- PageRange[] pages = (PageRange[]) args[0];
- ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
- WriteResultCallback callback = (WriteResultCallback) args[3];
- // We expect a single range as it is either the pages for
- // preview or the page we selected in the UI.
- assertSame(pages.length, 1);
+ invocation -> {
+ printAttributes[0] = (PrintAttributes) invocation.getArguments()[1];
+ LayoutResultCallback callback = (LayoutResultCallback) invocation.getArguments()[3];
+ PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
+ .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
+ .setPageCount(PAGE_COUNT)
+ .build();
+ callback.onLayoutFinished(info, false);
+ // Mark layout was called.
+ onLayoutCalled();
+ return null;
+ }, invocation -> {
+ Object[] args = invocation.getArguments();
+ PageRange[] pages = (PageRange[]) args[0];
+ ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
+ WriteResultCallback callback = (WriteResultCallback) args[3];
+ // We expect a single range as it is either the pages for
+ // preview or the page we selected in the UI.
+ assertSame(pages.length, 1);
- // The first write request for some pages to preview.
- if (getWriteCallCount() == 0) {
- // Write all requested pages.
- writeBlankPages(printAttributes[0], fd, pages[0].getStart(), pages[0].getEnd());
- callback.onWriteFinished(pages);
- } else {
- // Otherwise write a page more that the one we selected.
- writeBlankPages(printAttributes[0], fd, REQUESTED_PAGE - 1, REQUESTED_PAGE);
- callback.onWriteFinished(new PageRange[] {new PageRange(REQUESTED_PAGE - 1,
- REQUESTED_PAGE)});
- }
+ // The first write request for some pages to preview.
+ if (getWriteCallCount() == 0) {
+ // Write all requested pages.
+ writeBlankPages(printAttributes[0], fd, pages[0].getStart(), pages[0].getEnd());
+ callback.onWriteFinished(pages);
+ } else {
+ // Otherwise write a page more that the one we selected.
+ writeBlankPages(printAttributes[0], fd, REQUESTED_PAGE - 1, REQUESTED_PAGE);
+ callback.onWriteFinished(new PageRange[] {new PageRange(REQUESTED_PAGE - 1,
+ REQUESTED_PAGE)});
+ }
- fd.close();
+ fd.close();
- // Mark write was called.
- onWriteCalled();
- return null;
- }
- }, new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- // Mark finish was called.
- onFinishCalled();
- return null;
- }
- });
+ // Mark write was called.
+ onWriteCalled();
+ return null;
+ }, invocation -> {
+ // Mark finish was called.
+ onFinishCalled();
+ return null;
+ });
// Start printing.
print(adapter);
@@ -392,7 +307,7 @@
waitForLayoutAdapterCallbackCount(2);
// Select a page not written for preview.
- selectPages(Integer.valueOf(REQUESTED_PAGE).toString());
+ selectPages(Integer.valueOf(REQUESTED_PAGE).toString(), PAGE_COUNT);
// Click the print button.
clickPrintButton();
@@ -425,12 +340,7 @@
// Create a callback for the target print service.
PrintServiceCallbacks firstServiceCallbacks = createMockPrintServiceCallbacks(
- new Answer<PrinterDiscoverySessionCallbacks>() {
- @Override
- public PrinterDiscoverySessionCallbacks answer(InvocationOnMock invocation) {
- return createMockFirstPrinterDiscoverySessionCallbacks();
- }
- },
+ invocation -> createMockFirstPrinterDiscoverySessionCallbacks(),
null, null);
final PrintAttributes[] printAttributes = new PrintAttributes[1];
@@ -441,49 +351,40 @@
// Create a mock print adapter.
final PrintDocumentAdapter adapter = createMockPrintDocumentAdapter(
- new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- printAttributes[0] = (PrintAttributes) invocation.getArguments()[1];
- LayoutResultCallback callback = (LayoutResultCallback) invocation.getArguments()[3];
- PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
- .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
- .setPageCount(PAGE_COUNT)
- .build();
- callback.onLayoutFinished(info, false);
- // Mark layout was called.
- onLayoutCalled();
- return null;
- }
- }, new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- Object[] args = invocation.getArguments();
- PageRange[] pages = (PageRange[]) args[0];
- ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
- WriteResultCallback callback = (WriteResultCallback) args[3];
- assertSame(pages.length, 1);
+ invocation -> {
+ printAttributes[0] = (PrintAttributes) invocation.getArguments()[1];
+ LayoutResultCallback callback = (LayoutResultCallback) invocation.getArguments()[3];
+ PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
+ .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
+ .setPageCount(PAGE_COUNT)
+ .build();
+ callback.onLayoutFinished(info, false);
+ // Mark layout was called.
+ onLayoutCalled();
+ return null;
+ }, invocation -> {
+ Object[] args = invocation.getArguments();
+ PageRange[] pages = (PageRange[]) args[0];
+ ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
+ WriteResultCallback callback = (WriteResultCallback) args[3];
+ assertSame(pages.length, 1);
- // We should be asked for some pages...
- assertSame(pages[0].getStart(), 0);
- assertTrue(pages[0].getEnd() == MAX_PREVIEW_PAGES_BATCH - 1);
+ // We should be asked for some pages...
+ assertSame(pages[0].getStart(), 0);
+ assertTrue(pages[0].getEnd() == MAX_PREVIEW_PAGES_BATCH - 1);
- writeBlankPages(printAttributes[0], fd, pages[0].getStart(), pages[0].getEnd());
- fd.close();
- callback.onWriteFinished(new PageRange[]{new PageRange(1, 1)});
+ writeBlankPages(printAttributes[0], fd, pages[0].getStart(), pages[0].getEnd());
+ fd.close();
+ callback.onWriteFinished(new PageRange[]{new PageRange(1, 1)});
- // Mark write was called.
- onWriteCalled();
- return null;
- }
- }, new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- // Mark finish was called.
- onFinishCalled();
- return null;
- }
- });
+ // Mark write was called.
+ onWriteCalled();
+ return null;
+ }, invocation -> {
+ // Mark finish was called.
+ onFinishCalled();
+ return null;
+ });
// Start printing.
print(adapter);
@@ -517,28 +418,20 @@
// Create a callback for the target print service.
PrintServiceCallbacks firstServiceCallbacks = createMockPrintServiceCallbacks(
- new Answer<PrinterDiscoverySessionCallbacks>() {
- @Override
- public PrinterDiscoverySessionCallbacks answer(InvocationOnMock invocation) {
- return createMockFirstPrinterDiscoverySessionCallbacks();
- }
- }, new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) {
- PrintJob printJob = (PrintJob) invocation.getArguments()[0];
- PrintJobInfo printJobInfo = printJob.getInfo();
- PageRange[] pages = printJobInfo.getPages();
- // We asked only for page 3 (index 2) but got this page when
- // we were getting the pages for preview (indices 0 - 49),
- // but the framework pruned extra pages, hence we should be asked
- // to print all pages from a single page document.
- assertTrue(pages.length == 1 && PageRange.ALL_PAGES.equals(pages[0]));
- assertSame(printJob.getDocument().getInfo().getPageCount(), 1);
- printJob.complete();
- onPrintJobQueuedCalled();
- return null;
- }
- }, null);
+ invocation -> createMockFirstPrinterDiscoverySessionCallbacks(), invocation -> {
+ PrintJob printJob = (PrintJob) invocation.getArguments()[0];
+ PrintJobInfo printJobInfo = printJob.getInfo();
+ PageRange[] pages = printJobInfo.getPages();
+ // We asked only for page 3 (index 2) but got this page when
+ // we were getting the pages for preview (indices 0 - 49),
+ // but the framework pruned extra pages, hence we should be asked
+ // to print all pages from a single page document.
+ assertTrue(pages.length == 1 && PageRange.ALL_PAGES.equals(pages[0]));
+ assertSame(printJob.getDocument().getInfo().getPageCount(), 1);
+ printJob.complete();
+ onPrintJobQueuedCalled();
+ return null;
+ }, null);
final PrintAttributes[] printAttributes = new PrintAttributes[1];
@@ -548,48 +441,39 @@
// Create a mock print adapter.
final PrintDocumentAdapter adapter = createMockPrintDocumentAdapter(
- new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- printAttributes[0] = (PrintAttributes) invocation.getArguments()[1];
- LayoutResultCallback callback = (LayoutResultCallback) invocation.getArguments()[3];
- PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
- .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
- .setPageCount(PAGE_COUNT)
- .build();
- callback.onLayoutFinished(info, false);
- // Mark layout was called.
- onLayoutCalled();
- return null;
- }
- }, new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- Object[] args = invocation.getArguments();
- PageRange[] pages = (PageRange[]) args[0];
- ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
- WriteResultCallback callback = (WriteResultCallback) args[3];
- // We expect a single range as it is either the pages for
- // preview or the page we selected in the UI.
- assertSame(pages.length, 1);
+ invocation -> {
+ printAttributes[0] = (PrintAttributes) invocation.getArguments()[1];
+ LayoutResultCallback callback = (LayoutResultCallback) invocation.getArguments()[3];
+ PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
+ .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
+ .setPageCount(PAGE_COUNT)
+ .build();
+ callback.onLayoutFinished(info, false);
+ // Mark layout was called.
+ onLayoutCalled();
+ return null;
+ }, invocation -> {
+ Object[] args = invocation.getArguments();
+ PageRange[] pages = (PageRange[]) args[0];
+ ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
+ WriteResultCallback callback = (WriteResultCallback) args[3];
+ // We expect a single range as it is either the pages for
+ // preview or the page we selected in the UI.
+ assertSame(pages.length, 1);
- // Write all requested pages.
- writeBlankPages(printAttributes[0], fd, pages[0].getStart(), pages[0].getEnd());
- callback.onWriteFinished(pages);
- fd.close();
+ // Write all requested pages.
+ writeBlankPages(printAttributes[0], fd, pages[0].getStart(), pages[0].getEnd());
+ callback.onWriteFinished(pages);
+ fd.close();
- // Mark write was called.
- onWriteCalled();
- return null;
- }
- }, new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- // Mark finish was called.
- onFinishCalled();
- return null;
- }
- });
+ // Mark write was called.
+ onWriteCalled();
+ return null;
+ }, invocation -> {
+ // Mark finish was called.
+ onFinishCalled();
+ return null;
+ });
// Start printing.
print(adapter);
@@ -607,7 +491,7 @@
waitForLayoutAdapterCallbackCount(2);
// Select a page not written for preview.
- selectPages("3");
+ selectPages("3", PAGE_COUNT);
// Click the print button.
clickPrintButton();
@@ -633,57 +517,38 @@
any(PrintJob.class));
}
- private void selectPages(String pages) throws UiObjectNotFoundException {
- UiObject pagesSpinner = getUiDevice().findObject(new UiSelector().resourceId(
- "com.android.printspooler:id/range_options_spinner"));
- pagesSpinner.click();
-
- UiObject rangeOption = getUiDevice().findObject(new UiSelector().textContains("Range"));
- rangeOption.click();
-
- UiObject pagesEditText = getUiDevice().findObject(new UiSelector().resourceId(
- "com.android.printspooler:id/page_range_edittext"));
- pagesEditText.setText(pages);
-
- // Hide the keyboard.
- getUiDevice().pressBack();
- }
-
private PrinterDiscoverySessionCallbacks createMockFirstPrinterDiscoverySessionCallbacks() {
- return createMockPrinterDiscoverySessionCallbacks(new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) {
- PrinterDiscoverySessionCallbacks mock = (PrinterDiscoverySessionCallbacks)
- invocation.getMock();
+ return createMockPrinterDiscoverySessionCallbacks(invocation -> {
+ PrinterDiscoverySessionCallbacks mock = (PrinterDiscoverySessionCallbacks)
+ invocation.getMock();
- StubbablePrinterDiscoverySession session = mock.getSession();
- PrintService service = session.getService();
+ StubbablePrinterDiscoverySession session = mock.getSession();
+ PrintService service = session.getService();
- if (session.getPrinters().isEmpty()) {
- List<PrinterInfo> printers = new ArrayList<>();
+ if (session.getPrinters().isEmpty()) {
+ List<PrinterInfo> printers = new ArrayList<>();
- // Add one printer.
- PrinterId firstPrinterId = service.generatePrinterId("first_printer");
- PrinterCapabilitiesInfo firstCapabilities =
- new PrinterCapabilitiesInfo.Builder(firstPrinterId)
- .setMinMargins(new Margins(200, 200, 200, 200))
- .addMediaSize(MediaSize.ISO_A4, true)
- .addMediaSize(MediaSize.ISO_A5, false)
- .addResolution(new Resolution("300x300", "300x300", 300, 300), true)
- .setColorModes(PrintAttributes.COLOR_MODE_COLOR,
- PrintAttributes.COLOR_MODE_COLOR)
- .build();
- PrinterInfo firstPrinter = new PrinterInfo.Builder(firstPrinterId,
- FIRST_PRINTER, PrinterInfo.STATUS_IDLE)
- .setCapabilities(firstCapabilities)
- .build();
- printers.add(firstPrinter);
+ // Add one printer.
+ PrinterId firstPrinterId = service.generatePrinterId("first_printer");
+ PrinterCapabilitiesInfo firstCapabilities =
+ new PrinterCapabilitiesInfo.Builder(firstPrinterId)
+ .setMinMargins(new Margins(200, 200, 200, 200))
+ .addMediaSize(MediaSize.ISO_A4, true)
+ .addMediaSize(MediaSize.ISO_A5, false)
+ .addResolution(new Resolution("300x300", "300x300", 300, 300), true)
+ .setColorModes(PrintAttributes.COLOR_MODE_COLOR,
+ PrintAttributes.COLOR_MODE_COLOR)
+ .build();
+ PrinterInfo firstPrinter = new PrinterInfo.Builder(firstPrinterId,
+ FIRST_PRINTER, PrinterInfo.STATUS_IDLE)
+ .setCapabilities(firstCapabilities)
+ .build();
+ printers.add(firstPrinter);
- session.addPrinters(printers);
- }
-
- return null;
+ session.addPrinters(printers);
}
+
+ return null;
}, null, null, null, null, null, null);
}
diff --git a/tests/tests/print/src/android/print/cts/PrintAttributesTest.java b/tests/tests/print/src/android/print/cts/PrintAttributesTest.java
index 5459c31..0700e7c 100644
--- a/tests/tests/print/src/android/print/cts/PrintAttributesTest.java
+++ b/tests/tests/print/src/android/print/cts/PrintAttributesTest.java
@@ -37,8 +37,6 @@
import android.printservice.PrintJob;
import android.util.Log;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
import java.util.ArrayList;
import java.util.Arrays;
@@ -140,73 +138,62 @@
final MediaSize defaultMediaSize, final int colorModes[], final int defaultColorMode,
final int duplexModes[], final int defaultDuplexMode, final Resolution resolutions[],
final Resolution defaultResolution) {
- return createMockPrinterDiscoverySessionCallbacks(new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) {
- StubbablePrinterDiscoverySession session =
- ((PrinterDiscoverySessionCallbacks) invocation.getMock()).getSession();
+ return createMockPrinterDiscoverySessionCallbacks(invocation -> {
+ StubbablePrinterDiscoverySession session =
+ ((PrinterDiscoverySessionCallbacks) invocation.getMock()).getSession();
- if (session.getPrinters().isEmpty()) {
- List<PrinterInfo> printers = new ArrayList<PrinterInfo>();
- PrinterId printerId = session.getService().generatePrinterId(PRINTER_NAME);
+ if (session.getPrinters().isEmpty()) {
+ List<PrinterInfo> printers = new ArrayList<>();
+ PrinterId printerId = session.getService().generatePrinterId(PRINTER_NAME);
- PrinterCapabilitiesInfo.Builder builder =
- new PrinterCapabilitiesInfo.Builder(printerId);
+ PrinterCapabilitiesInfo.Builder builder =
+ new PrinterCapabilitiesInfo.Builder(printerId);
- builder.setMinMargins(minMargins);
+ builder.setMinMargins(minMargins);
- int mediaSizesLength = mediaSizes.length;
- for (int i = 0; i < mediaSizesLength; i++) {
- if (mediaSizes[i].equals(defaultMediaSize)) {
- builder.addMediaSize(mediaSizes[i], true);
- } else {
- builder.addMediaSize(mediaSizes[i], false);
- }
+ int mediaSizesLength = mediaSizes.length;
+ for (int i = 0; i < mediaSizesLength; i++) {
+ if (mediaSizes[i].equals(defaultMediaSize)) {
+ builder.addMediaSize(mediaSizes[i], true);
+ } else {
+ builder.addMediaSize(mediaSizes[i], false);
}
-
- int colorModesMask = 0;
- int colorModesLength = colorModes.length;
- for (int i = 0; i < colorModesLength; i++) {
- colorModesMask |= colorModes[i];
- }
- builder.setColorModes(colorModesMask, defaultColorMode);
-
- int duplexModesMask = 0;
- int duplexModeLength = duplexModes.length;
- for (int i = 0; i < duplexModeLength; i++) {
- duplexModesMask |= duplexModes[i];
- }
- builder.setDuplexModes(duplexModesMask, defaultDuplexMode);
-
- int resolutionsLength = resolutions.length;
- for (int i = 0; i < resolutionsLength; i++) {
- if (resolutions[i].equals(defaultResolution)) {
- builder.addResolution(resolutions[i], true);
- } else {
- builder.addResolution(resolutions[i], false);
- }
- }
-
- PrinterInfo printer = new PrinterInfo.Builder(printerId, PRINTER_NAME,
- PrinterInfo.STATUS_IDLE).setCapabilities(builder.build()).build();
- printers.add(printer);
-
- session.addPrinters(printers);
}
- return null;
+
+ int colorModesMask = 0;
+ int colorModesLength = colorModes.length;
+ for (int i = 0; i < colorModesLength; i++) {
+ colorModesMask |= colorModes[i];
+ }
+ builder.setColorModes(colorModesMask, defaultColorMode);
+
+ int duplexModesMask = 0;
+ int duplexModeLength = duplexModes.length;
+ for (int i = 0; i < duplexModeLength; i++) {
+ duplexModesMask |= duplexModes[i];
+ }
+ builder.setDuplexModes(duplexModesMask, defaultDuplexMode);
+
+ int resolutionsLength = resolutions.length;
+ for (int i = 0; i < resolutionsLength; i++) {
+ if (resolutions[i].equals(defaultResolution)) {
+ builder.addResolution(resolutions[i], true);
+ } else {
+ builder.addResolution(resolutions[i], false);
+ }
+ }
+
+ PrinterInfo printer = new PrinterInfo.Builder(printerId, PRINTER_NAME,
+ PrinterInfo.STATUS_IDLE).setCapabilities(builder.build()).build();
+ printers.add(printer);
+
+ session.addPrinters(printers);
}
- }, null, null, new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- return null;
- }
- }, null, null, new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- // Take a note onDestroy was called.
- onPrinterDiscoverySessionDestroyCalled();
- return null;
- }
+ return null;
+ }, null, null, invocation -> null, null, null, invocation -> {
+ // Take a note onDestroy was called.
+ onPrinterDiscoverySessionDestroyCalled();
+ return null;
});
}
@@ -229,43 +216,34 @@
*/
private PrintDocumentAdapter createMockPrintDocumentAdapter() {
return createMockPrintDocumentAdapter(
- new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- mLayoutAttributes = (PrintAttributes) invocation.getArguments()[1];
- LayoutResultCallback callback =
- (LayoutResultCallback) invocation.getArguments()[3];
- PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
- .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
- .setPageCount(1)
- .build();
- callback.onLayoutFinished(info, false);
- // Mark layout was called.
- onLayoutCalled();
- return null;
- }
- }, new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- Object[] args = invocation.getArguments();
- PageRange[] pages = (PageRange[]) args[0];
- ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
- WriteResultCallback callback = (WriteResultCallback) args[3];
- writeBlankPages(mLayoutAttributes, fd, pages[0].getStart(),
- pages[0].getEnd());
- fd.close();
- callback.onWriteFinished(pages);
- // Mark write was called.
- onWriteCalled();
- return null;
- }
- }, new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- // Mark finish was called.
- onFinishCalled();
- return null;
- }
+ invocation -> {
+ mLayoutAttributes = (PrintAttributes) invocation.getArguments()[1];
+ LayoutResultCallback callback =
+ (LayoutResultCallback) invocation.getArguments()[3];
+ PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
+ .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
+ .setPageCount(1)
+ .build();
+ callback.onLayoutFinished(info, false);
+ // Mark layout was called.
+ onLayoutCalled();
+ return null;
+ }, invocation -> {
+ Object[] args = invocation.getArguments();
+ PageRange[] pages = (PageRange[]) args[0];
+ ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
+ WriteResultCallback callback = (WriteResultCallback) args[3];
+ writeBlankPages(mLayoutAttributes, fd, pages[0].getStart(),
+ pages[0].getEnd());
+ fd.close();
+ callback.onWriteFinished(pages);
+ // Mark write was called.
+ onWriteCalled();
+ return null;
+ }, invocation -> {
+ // Mark finish was called.
+ onFinishCalled();
+ return null;
});
}
@@ -293,20 +271,12 @@
defaultDuplexMode, resolutions, defaultResolution);
PrintServiceCallbacks serviceCallbacks = createMockPrintServiceCallbacks(
- new Answer<PrinterDiscoverySessionCallbacks>() {
- @Override
- public PrinterDiscoverySessionCallbacks answer(InvocationOnMock invocation) {
- return sessionCallbacks;
- }
- },
- new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) {
- PrintJob printJob = (PrintJob) invocation.getArguments()[0];
- // We pretend the job is handled immediately.
- printJob.complete();
- return null;
- }
+ invocation -> sessionCallbacks,
+ invocation -> {
+ PrintJob printJob = (PrintJob) invocation.getArguments()[0];
+ // We pretend the job is handled immediately.
+ printJob.complete();
+ return null;
}, null);
// Configure the print services.
diff --git a/tests/tests/print/src/android/print/cts/PrintDocumentAdapterContractTest.java b/tests/tests/print/src/android/print/cts/PrintDocumentAdapterContractTest.java
index f171884..37a86fb 100644
--- a/tests/tests/print/src/android/print/cts/PrintDocumentAdapterContractTest.java
+++ b/tests/tests/print/src/android/print/cts/PrintDocumentAdapterContractTest.java
@@ -16,13 +16,13 @@
package android.print.cts;
+import static android.print.cts.Utils.eventually;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import android.os.CancellationSignal;
-import android.os.CancellationSignal.OnCancelListener;
import android.os.ParcelFileDescriptor;
import android.print.PageRange;
import android.print.PrintAttributes;
@@ -43,11 +43,11 @@
import android.print.cts.services.StubbablePrinterDiscoverySession;
import android.printservice.PrintJob;
import android.printservice.PrintService;
+import android.util.Log;
import org.mockito.InOrder;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
+import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@@ -58,6 +58,8 @@
*/
public class PrintDocumentAdapterContractTest extends BasePrintTest {
+ private static final String LOG_TAG = "PrintDocumentAdapterContractTest";
+
public void testNoPrintOptionsOrPrinterChange() throws Exception {
if (!supportsPrinting()) {
return;
@@ -70,42 +72,33 @@
// Create a mock print adapter.
final PrintDocumentAdapter adapter = createMockPrintDocumentAdapter(
- new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- printAttributes[0] = (PrintAttributes) invocation.getArguments()[1];
- LayoutResultCallback callback = (LayoutResultCallback) invocation.getArguments()[3];
- PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
- .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
- .setPageCount(2)
- .build();
- callback.onLayoutFinished(info, false);
- // Mark layout was called.
- onLayoutCalled();
- return null;
- }
- }, new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- Object[] args = invocation.getArguments();
- PageRange[] pages = (PageRange[]) args[0];
- ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
- WriteResultCallback callback = (WriteResultCallback) args[3];
- writeBlankPages(printAttributes[0], fd, pages[0].getStart(), pages[0].getEnd());
- fd.close();
- callback.onWriteFinished(pages);
- // Mark write was called.
- onWriteCalled();
- return null;
- }
- }, new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- // Mark finish was called.
- onFinishCalled();
- return null;
- }
- });
+ invocation -> {
+ printAttributes[0] = (PrintAttributes) invocation.getArguments()[1];
+ LayoutResultCallback callback = (LayoutResultCallback) invocation.getArguments()[3];
+ PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
+ .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
+ .setPageCount(2)
+ .build();
+ callback.onLayoutFinished(info, false);
+ // Mark layout was called.
+ onLayoutCalled();
+ return null;
+ }, invocation -> {
+ Object[] args = invocation.getArguments();
+ PageRange[] pages = (PageRange[]) args[0];
+ ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
+ WriteResultCallback callback = (WriteResultCallback) args[3];
+ writeBlankPages(printAttributes[0], fd, pages[0].getStart(), pages[0].getEnd());
+ fd.close();
+ callback.onWriteFinished(pages);
+ // Mark write was called.
+ onWriteCalled();
+ return null;
+ }, invocation -> {
+ // Mark finish was called.
+ onFinishCalled();
+ return null;
+ });
// Start printing.
print(adapter);
@@ -141,7 +134,7 @@
// there are other printers but none of them was used.
PrintAttributes firstOldAttributes = new PrintAttributes.Builder().build();
PrintAttributes firstNewAttributes = new PrintAttributes.Builder()
- .setMediaSize(MediaSize.NA_LETTER)
+ .setMediaSize(getDefaultMediaSize())
.setResolution(new Resolution("PDF resolution", "PDF resolution", 300, 300))
.setMinMargins(Margins.NO_MARGINS)
.setColorMode(PrintAttributes.COLOR_MODE_COLOR)
@@ -190,45 +183,8 @@
FirstPrintService.setCallbacks(createFirstMockPrintServiceCallbacks());
SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks());
- final PrintAttributes[] printAttributes = new PrintAttributes[1];
-
// Create a mock print adapter.
- final PrintDocumentAdapter adapter = createMockPrintDocumentAdapter(
- new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- printAttributes[0] = (PrintAttributes) invocation.getArguments()[1];
- LayoutResultCallback callback = (LayoutResultCallback)
- invocation.getArguments()[3];
- PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
- .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
- .setPageCount(1)
- .build();
- callback.onLayoutFinished(info, false);
- return null;
- }
- }, new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- Object[] args = invocation.getArguments();
- PageRange[] pages = (PageRange[]) args[0];
- ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
- WriteResultCallback callback = (WriteResultCallback) args[3];
- writeBlankPages(printAttributes[0], fd, pages[0].getStart(), pages[0].getEnd());
- fd.close();
- callback.onWriteFinished(pages);
- // Mark write was called.
- onWriteCalled();
- return null;
- }
- }, new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- // Mark finish was called.
- onFinishCalled();
- return null;
- }
- });
+ PrintDocumentAdapter adapter = createDefaultPrintDocumentAdapter(1);
// Start printing.
print(adapter);
@@ -256,7 +212,7 @@
// there are other printers but none of them was used.
PrintAttributes firstOldAttributes = new PrintAttributes.Builder().build();
PrintAttributes firstNewAttributes = new PrintAttributes.Builder()
- .setMediaSize(MediaSize.NA_LETTER)
+ .setMediaSize(getDefaultMediaSize())
.setResolution(new Resolution("PDF resolution", "PDF resolution", 300, 300))
.setMinMargins(Margins.NO_MARGINS)
.setColorMode(PrintAttributes.COLOR_MODE_COLOR)
@@ -276,6 +232,77 @@
verifyNoMoreInteractions(adapter);
}
+ public void testNonCallingBackWrite() throws Exception {
+ if (!supportsPrinting()) {
+ return;
+ }
+
+ // Configure the print services.
+ FirstPrintService.setCallbacks(createFirstMockPrintServiceCallbacks());
+ SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks());
+
+ final PrintAttributes[] printAttributes = new PrintAttributes[1];
+ final boolean[] isWriteBroken = new boolean[1];
+
+ // Create a mock print adapter.
+ PrintDocumentAdapter adapter = createMockPrintDocumentAdapter(
+ invocation -> {
+ printAttributes[0] = (PrintAttributes) invocation.getArguments()[1];
+ PrintDocumentAdapter.LayoutResultCallback callback =
+ (PrintDocumentAdapter.LayoutResultCallback) invocation
+ .getArguments()[3];
+
+ callback.onLayoutFinished(new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
+ .setPageCount(PrintDocumentInfo.PAGE_COUNT_UNKNOWN).build(),
+ false);
+
+ onLayoutCalled();
+ return null;
+ }, invocation -> {
+ Object[] args = invocation.getArguments();
+ PrintDocumentAdapter.WriteResultCallback callback =
+ (PrintDocumentAdapter.WriteResultCallback) args[3];
+
+ if (isWriteBroken[0]) {
+ ((CancellationSignal) args[2])
+ .setOnCancelListener(() -> callback.onWriteCancelled());
+ } else {
+ try (ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1]) {
+ writeBlankPages(printAttributes[0], fd, 0, 1);
+ }
+ callback.onWriteFinished(new PageRange[]{new PageRange(0, 0)});
+ }
+
+ onWriteCalled();
+ return null;
+ }, invocation -> {
+ onFinishCalled();
+ return null;
+ });
+
+ // never return from writes until we repair the write call later
+ isWriteBroken[0] = true;
+
+ // Start printing.
+ print(adapter);
+
+ // Wait for write. This will happen as the the first layout always triggers a write
+ waitForWriteAdapterCallback(1);
+
+ // Finally return useful data from the adapter's write call
+ isWriteBroken[0] = false;
+
+ selectPrinter("Third printer");
+ clickPrintButton();
+ answerPrintServicesWarning(true);
+
+ // The layout reports that the doc did not change and none of the print attributes changed.
+ // Still: As the pages are not written yet we still get a write call
+ waitForWriteAdapterCallback(2);
+
+ waitForPrinterDiscoverySessionDestroyCallbackCalled(1);
+ }
+
public void testPrintOptionsChangeAndNoPrinterChange() throws Exception {
if (!supportsPrinting()) {
return;
@@ -288,43 +315,34 @@
// Create a mock print adapter.
final PrintDocumentAdapter adapter = createMockPrintDocumentAdapter(
- new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- printAttributes[0] = (PrintAttributes) invocation.getArguments()[1];
- LayoutResultCallback callback = (LayoutResultCallback)
- invocation.getArguments()[3];
- PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
- .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
- .setPageCount(1)
- .build();
- callback.onLayoutFinished(info, false);
- // Mark layout was called.
- onLayoutCalled();
- return null;
- }
- }, new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- Object[] args = invocation.getArguments();
- PageRange[] pages = (PageRange[]) args[0];
- ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
- WriteResultCallback callback = (WriteResultCallback) args[3];
- writeBlankPages(printAttributes[0], fd, pages[0].getStart(), pages[0].getEnd());
- fd.close();
- callback.onWriteFinished(pages);
- // Mark write was called.
- onWriteCalled();
- return null;
- }
- }, new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- // Mark finish was called.
- onFinishCalled();
- return null;
- }
- });
+ invocation -> {
+ printAttributes[0] = (PrintAttributes) invocation.getArguments()[1];
+ LayoutResultCallback callback = (LayoutResultCallback)
+ invocation.getArguments()[3];
+ PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
+ .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
+ .setPageCount(1)
+ .build();
+ callback.onLayoutFinished(info, false);
+ // Mark layout was called.
+ onLayoutCalled();
+ return null;
+ }, invocation -> {
+ Object[] args = invocation.getArguments();
+ PageRange[] pages = (PageRange[]) args[0];
+ ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
+ WriteResultCallback callback = (WriteResultCallback) args[3];
+ writeBlankPages(printAttributes[0], fd, pages[0].getStart(), pages[0].getEnd());
+ fd.close();
+ callback.onWriteFinished(pages);
+ // Mark write was called.
+ onWriteCalled();
+ return null;
+ }, invocation -> {
+ // Mark finish was called.
+ onFinishCalled();
+ return null;
+ });
// Start printing.
print(adapter);
@@ -342,25 +360,25 @@
waitForLayoutAdapterCallbackCount(2);
// Change the orientation.
- changeOrientation("Landscape");
+ changeOrientation(getPrintSpoolerStringArray("orientation_labels")[1]);
// Wait for layout.
waitForLayoutAdapterCallbackCount(3);
// Change the media size.
- changeMediaSize("ISO A4");
+ changeMediaSize(MediaSize.ISO_A0.getLabel(getActivity().getPackageManager()));
// Wait for layout.
waitForLayoutAdapterCallbackCount(4);
// Change the color.
- changeColor("Black & White");
+ changeColor(getPrintSpoolerStringArray("color_mode_labels")[0]);
// Wait for layout.
waitForLayoutAdapterCallbackCount(5);
// Change the duplex.
- changeDuplex("Short edge");
+ changeDuplex(getPrintSpoolerStringArray("duplex_mode_labels")[2]);
// Wait for layout.
waitForLayoutAdapterCallbackCount(6);
@@ -387,7 +405,7 @@
// there are other printers but none of them was used.
PrintAttributes firstOldAttributes = new PrintAttributes.Builder().build();
PrintAttributes firstNewAttributes = new PrintAttributes.Builder()
- .setMediaSize(MediaSize.NA_LETTER)
+ .setMediaSize(getDefaultMediaSize())
.setResolution(new Resolution("PDF resolution", "PDF resolution", 300, 300))
.setMinMargins(Margins.NO_MARGINS)
.setColorMode(PrintAttributes.COLOR_MODE_COLOR)
@@ -432,7 +450,7 @@
// there shouldn't be a next call to write.
PrintAttributes fourthOldAttributes = thirdNewAttributes;
PrintAttributes fourthNewAttributes = new PrintAttributes.Builder()
- .setMediaSize(MediaSize.ISO_A4.asLandscape())
+ .setMediaSize(MediaSize.ISO_A0.asLandscape())
.setResolution(new Resolution("300x300", "300x300", 300, 300))
.setMinMargins(Margins.NO_MARGINS)
.setColorMode(PrintAttributes.COLOR_MODE_COLOR)
@@ -445,7 +463,7 @@
// there shouldn't be a next call to write.
PrintAttributes fifthOldAttributes = fourthNewAttributes;
PrintAttributes fifthNewAttributes = new PrintAttributes.Builder()
- .setMediaSize(MediaSize.ISO_A4.asLandscape())
+ .setMediaSize(MediaSize.ISO_A0.asLandscape())
.setResolution(new Resolution("300x300", "300x300", 300, 300))
.setMinMargins(Margins.NO_MARGINS)
.setColorMode(PrintAttributes.COLOR_MODE_MONOCHROME)
@@ -458,7 +476,7 @@
// there shouldn't be a next call to write.
PrintAttributes sixthOldAttributes = fifthNewAttributes;
PrintAttributes sixthNewAttributes = new PrintAttributes.Builder()
- .setMediaSize(MediaSize.ISO_A4.asLandscape())
+ .setMediaSize(MediaSize.ISO_A0.asLandscape())
.setResolution(new Resolution("300x300", "300x300", 300, 300))
.setMinMargins(Margins.NO_MARGINS)
.setColorMode(PrintAttributes.COLOR_MODE_MONOCHROME)
@@ -492,43 +510,34 @@
// Create a mock print adapter.
final PrintDocumentAdapter adapter = createMockPrintDocumentAdapter(
- new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- printAttributes[0] = (PrintAttributes) invocation.getArguments()[1];
- LayoutResultCallback callback = (LayoutResultCallback)
- invocation.getArguments()[3];
- PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
- .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
- .setPageCount(1)
- .build();
- callback.onLayoutFinished(info, false);
- // Mark layout was called.
- onLayoutCalled();
- return null;
- }
- }, new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- Object[] args = invocation.getArguments();
- PageRange[] pages = (PageRange[]) args[0];
- ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
- WriteResultCallback callback = (WriteResultCallback) args[3];
- writeBlankPages(printAttributes[0], fd, pages[0].getStart(), pages[0].getEnd());
- fd.close();
- callback.onWriteFinished(pages);
- // Mark write was called.
- onWriteCalled();
- return null;
- }
- }, new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- // Mark finish was called.
- onFinishCalled();
- return null;
- }
- });
+ invocation -> {
+ printAttributes[0] = (PrintAttributes) invocation.getArguments()[1];
+ LayoutResultCallback callback = (LayoutResultCallback)
+ invocation.getArguments()[3];
+ PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
+ .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
+ .setPageCount(1)
+ .build();
+ callback.onLayoutFinished(info, false);
+ // Mark layout was called.
+ onLayoutCalled();
+ return null;
+ }, invocation -> {
+ Object[] args = invocation.getArguments();
+ PageRange[] pages = (PageRange[]) args[0];
+ ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
+ WriteResultCallback callback = (WriteResultCallback) args[3];
+ writeBlankPages(printAttributes[0], fd, pages[0].getStart(), pages[0].getEnd());
+ fd.close();
+ callback.onWriteFinished(pages);
+ // Mark write was called.
+ onWriteCalled();
+ return null;
+ }, invocation -> {
+ // Mark finish was called.
+ onFinishCalled();
+ return null;
+ });
// Start printing.
print(adapter);
@@ -546,7 +555,7 @@
waitForLayoutAdapterCallbackCount(2);
// Change the color.
- changeColor("Black & White");
+ changeColor(getPrintSpoolerStringArray("color_mode_labels")[0]);
// Wait for layout.
waitForLayoutAdapterCallbackCount(3);
@@ -580,7 +589,7 @@
// there are other printers but none of them was used.
PrintAttributes firstOldAttributes = new PrintAttributes.Builder().build();
PrintAttributes firstNewAttributes = new PrintAttributes.Builder()
- .setMediaSize(MediaSize.NA_LETTER)
+ .setMediaSize(getDefaultMediaSize())
.setResolution(new Resolution("PDF resolution", "PDF resolution", 300, 300))
.setMinMargins(Margins.NO_MARGINS)
.setColorMode(PrintAttributes.COLOR_MODE_COLOR)
@@ -622,7 +631,7 @@
// new printer which results in a layout pass. Same for color.
PrintAttributes fourthOldAttributes = thirdNewAttributes;
PrintAttributes fourthNewAttributes = new PrintAttributes.Builder()
- .setMediaSize(MediaSize.ISO_A4)
+ .setMediaSize(MediaSize.ISO_A0)
.setResolution(new Resolution("PDF resolution", "PDF resolution", 300, 300))
.setMinMargins(new Margins(200, 200, 200, 200))
.setColorMode(PrintAttributes.COLOR_MODE_COLOR)
@@ -657,43 +666,34 @@
// Create a mock print adapter.
final PrintDocumentAdapter adapter = createMockPrintDocumentAdapter(
- new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- printAttributes[0] = (PrintAttributes) invocation.getArguments()[1];
- LayoutResultCallback callback = (LayoutResultCallback) invocation.getArguments()[3];
- PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
- .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
- .setPageCount(1)
- .build();
- // The content changes after every layout.
- callback.onLayoutFinished(info, true);
- // Mark layout was called.
- onLayoutCalled();
- return null;
- }
- }, new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- Object[] args = invocation.getArguments();
- PageRange[] pages = (PageRange[]) args[0];
- ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
- WriteResultCallback callback = (WriteResultCallback) args[3];
- writeBlankPages(printAttributes[0], fd, pages[0].getStart(), pages[0].getEnd());
- fd.close();
- callback.onWriteFinished(pages);
- // Mark write was called.
- onWriteCalled();
- return null;
- }
- }, new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- // Mark finish was called.
- onFinishCalled();
- return null;
- }
- });
+ invocation -> {
+ printAttributes[0] = (PrintAttributes) invocation.getArguments()[1];
+ LayoutResultCallback callback = (LayoutResultCallback) invocation.getArguments()[3];
+ PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
+ .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
+ .setPageCount(1)
+ .build();
+ // The content changes after every layout.
+ callback.onLayoutFinished(info, true);
+ // Mark layout was called.
+ onLayoutCalled();
+ return null;
+ }, invocation -> {
+ Object[] args = invocation.getArguments();
+ PageRange[] pages = (PageRange[]) args[0];
+ ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
+ WriteResultCallback callback = (WriteResultCallback) args[3];
+ writeBlankPages(printAttributes[0], fd, pages[0].getStart(), pages[0].getEnd());
+ fd.close();
+ callback.onWriteFinished(pages);
+ // Mark write was called.
+ onWriteCalled();
+ return null;
+ }, invocation -> {
+ // Mark finish was called.
+ onFinishCalled();
+ return null;
+ });
// Start printing.
print(adapter);
@@ -732,7 +732,7 @@
// there are other printers but none of them was used.
PrintAttributes firstOldAttributes = new PrintAttributes.Builder().build();
PrintAttributes firstNewAttributes = new PrintAttributes.Builder()
- .setMediaSize(MediaSize.NA_LETTER)
+ .setMediaSize(getDefaultMediaSize())
.setResolution(new Resolution("PDF resolution", "PDF resolution", 300, 300))
.setMinMargins(Margins.NO_MARGINS).setColorMode(PrintAttributes.COLOR_MODE_COLOR)
.setDuplexMode(PrintAttributes.DUPLEX_MODE_NONE)
@@ -790,41 +790,32 @@
// Create a mock print adapter.
final PrintDocumentAdapter adapter = createMockPrintDocumentAdapter(
- new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- printAttributes[0] = (PrintAttributes) invocation.getArguments()[1];
- LayoutResultCallback callback = (LayoutResultCallback) invocation.getArguments()[3];
- PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
- .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
- .setPageCount(1)
- .build();
- // The content changes after every layout.
- callback.onLayoutFinished(info, true);
- return null;
- }
- }, new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- Object[] args = invocation.getArguments();
- PageRange[] pages = (PageRange[]) args[0];
- ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
- WriteResultCallback callback = (WriteResultCallback) args[3];
- writeBlankPages(printAttributes[0], fd, pages[0].getStart(), pages[0].getEnd());
- fd.close();
- callback.onWriteFinished(pages);
- // Mark write was called.
- onWriteCalled();
- return null;
- }
- }, new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- // Mark finish was called.
- onFinishCalled();
- return null;
- }
- });
+ invocation -> {
+ printAttributes[0] = (PrintAttributes) invocation.getArguments()[1];
+ LayoutResultCallback callback = (LayoutResultCallback) invocation.getArguments()[3];
+ PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
+ .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
+ .setPageCount(1)
+ .build();
+ // The content changes after every layout.
+ callback.onLayoutFinished(info, true);
+ return null;
+ }, invocation -> {
+ Object[] args = invocation.getArguments();
+ PageRange[] pages = (PageRange[]) args[0];
+ ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
+ WriteResultCallback callback = (WriteResultCallback) args[3];
+ writeBlankPages(printAttributes[0], fd, pages[0].getStart(), pages[0].getEnd());
+ fd.close();
+ callback.onWriteFinished(pages);
+ // Mark write was called.
+ onWriteCalled();
+ return null;
+ }, invocation -> {
+ // Mark finish was called.
+ onFinishCalled();
+ return null;
+ });
// Start printing.
print(adapter);
@@ -860,7 +851,7 @@
// there are other printers but none of them was used.
PrintAttributes firstOldAttributes = new PrintAttributes.Builder().build();
PrintAttributes firstNewAttributes = new PrintAttributes.Builder()
- .setMediaSize(MediaSize.NA_LETTER)
+ .setMediaSize(getDefaultMediaSize())
.setResolution(new Resolution("PDF resolution", "PDF resolution", 300, 300))
.setMinMargins(Margins.NO_MARGINS).setColorMode(PrintAttributes.COLOR_MODE_COLOR)
.setDuplexMode(PrintAttributes.DUPLEX_MODE_NONE)
@@ -899,42 +890,33 @@
// Create a mock print adapter.
final PrintDocumentAdapter adapter = createMockPrintDocumentAdapter(
- new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- printAttributes[0] = (PrintAttributes) invocation.getArguments()[1];
- LayoutResultCallback callback = (LayoutResultCallback) invocation.getArguments()[3];
- PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
- .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
- .setPageCount(3)
- .build();
- callback.onLayoutFinished(info, false);
- // Mark layout was called.
- onLayoutCalled();
- return null;
- }
- }, new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- Object[] args = invocation.getArguments();
- ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
- WriteResultCallback callback = (WriteResultCallback) args[3];
- PageRange[] pages = (PageRange[]) args[0];
- writeBlankPages(printAttributes[0], fd, pages[0].getStart(), pages[0].getEnd());
- fd.close();
- callback.onWriteFinished(new PageRange[] {PageRange.ALL_PAGES});
- // Mark write was called.
- onWriteCalled();
- return null;
- }
- }, new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- // Mark finish was called.
- onFinishCalled();
- return null;
- }
- });
+ invocation -> {
+ printAttributes[0] = (PrintAttributes) invocation.getArguments()[1];
+ LayoutResultCallback callback = (LayoutResultCallback) invocation.getArguments()[3];
+ PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
+ .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
+ .setPageCount(3)
+ .build();
+ callback.onLayoutFinished(info, false);
+ // Mark layout was called.
+ onLayoutCalled();
+ return null;
+ }, invocation -> {
+ Object[] args = invocation.getArguments();
+ ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
+ WriteResultCallback callback = (WriteResultCallback) args[3];
+ PageRange[] pages = (PageRange[]) args[0];
+ writeBlankPages(printAttributes[0], fd, pages[0].getStart(), pages[0].getEnd());
+ fd.close();
+ callback.onWriteFinished(new PageRange[] {PageRange.ALL_PAGES});
+ // Mark write was called.
+ onWriteCalled();
+ return null;
+ }, invocation -> {
+ // Mark finish was called.
+ onFinishCalled();
+ return null;
+ });
// Start printing.
print(adapter);
@@ -973,7 +955,7 @@
// there are other printers but none of them was used.
PrintAttributes firstOldAttributes = new PrintAttributes.Builder().build();
PrintAttributes firstNewAttributes = new PrintAttributes.Builder()
- .setMediaSize(MediaSize.NA_LETTER)
+ .setMediaSize(getDefaultMediaSize())
.setResolution(new Resolution("PDF resolution", "PDF resolution", 300, 300))
.setMinMargins(Margins.NO_MARGINS).setColorMode(PrintAttributes.COLOR_MODE_COLOR)
.setDuplexMode(PrintAttributes.DUPLEX_MODE_NONE)
@@ -1027,30 +1009,21 @@
// Create a mock print adapter.
final PrintDocumentAdapter adapter = createMockPrintDocumentAdapter(
- new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- CancellationSignal cancellation = (CancellationSignal) invocation.getArguments()[2];
- final LayoutResultCallback callback = (LayoutResultCallback) invocation
- .getArguments()[3];
- cancellation.setOnCancelListener(new OnCancelListener() {
- @Override
- public void onCancel() {
+ invocation -> {
+ CancellationSignal cancellation = (CancellationSignal) invocation.getArguments()[2];
+ final LayoutResultCallback callback = (LayoutResultCallback) invocation
+ .getArguments()[3];
+ cancellation.setOnCancelListener(() -> {
onCancelOperationCalled();
callback.onLayoutCancelled();
- }
+ });
+ onLayoutCalled();
+ return null;
+ }, null, invocation -> {
+ // Mark finish was called.
+ onFinishCalled();
+ return null;
});
- onLayoutCalled();
- return null;
- }
- }, null, new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- // Mark finish was called.
- onFinishCalled();
- return null;
- }
- });
// Start printing.
print(adapter);
@@ -1081,7 +1054,7 @@
// there are other printers but none of them was used.
PrintAttributes firstOldAttributes = new PrintAttributes.Builder().build();
PrintAttributes firstNewAttributes = new PrintAttributes.Builder()
- .setMediaSize(MediaSize.NA_LETTER)
+ .setMediaSize(getDefaultMediaSize())
.setResolution(new Resolution("PDF resolution", "PDF resolution", 300, 300))
.setMinMargins(Margins.NO_MARGINS).setColorMode(PrintAttributes.COLOR_MODE_COLOR)
.setDuplexMode(PrintAttributes.DUPLEX_MODE_NONE)
@@ -1105,26 +1078,19 @@
// Create a mock print adapter.
final PrintDocumentAdapter adapter = createMockPrintDocumentAdapter(
- new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- LayoutResultCallback callback = (LayoutResultCallback) invocation.getArguments()[3];
- PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
- .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT).setPageCount(1)
- .build();
- callback.onLayoutFinished(info, false);
- return null;
- }
- }, new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- Object[] args = invocation.getArguments();
- final ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
- final CancellationSignal cancellation = (CancellationSignal) args[2];
- final WriteResultCallback callback = (WriteResultCallback) args[3];
- cancellation.setOnCancelListener(new OnCancelListener() {
- @Override
- public void onCancel() {
+ invocation -> {
+ LayoutResultCallback callback = (LayoutResultCallback) invocation.getArguments()[3];
+ PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
+ .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT).setPageCount(1)
+ .build();
+ callback.onLayoutFinished(info, false);
+ return null;
+ }, invocation -> {
+ Object[] args = invocation.getArguments();
+ final ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
+ final CancellationSignal cancellation = (CancellationSignal) args[2];
+ final WriteResultCallback callback = (WriteResultCallback) args[3];
+ cancellation.setOnCancelListener(() -> {
try {
fd.close();
} catch (IOException ioe) {
@@ -1132,20 +1098,15 @@
}
onCancelOperationCalled();
callback.onWriteCancelled();
- }
+ });
+ // Mark write was called.
+ onWriteCalled();
+ return null;
+ }, invocation -> {
+ // Mark finish was called.
+ onFinishCalled();
+ return null;
});
- // Mark write was called.
- onWriteCalled();
- return null;
- }
- }, new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- // Mark finish was called.
- onFinishCalled();
- return null;
- }
- });
// Start printing.
print(adapter);
@@ -1176,7 +1137,7 @@
// there are other printers but none of them was used.
PrintAttributes firstOldAttributes = new PrintAttributes.Builder().build();
PrintAttributes firstNewAttributes = new PrintAttributes.Builder()
- .setMediaSize(MediaSize.NA_LETTER)
+ .setMediaSize(getDefaultMediaSize())
.setResolution(new Resolution("PDF resolution", "PDF resolution", 300, 300))
.setMinMargins(Margins.NO_MARGINS).setColorMode(PrintAttributes.COLOR_MODE_COLOR)
.setDuplexMode(PrintAttributes.DUPLEX_MODE_NONE)
@@ -1205,23 +1166,17 @@
// Create a mock print adapter.
final PrintDocumentAdapter adapter = createMockPrintDocumentAdapter(
- new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- LayoutResultCallback callback = (LayoutResultCallback) invocation.getArguments()[3];
- callback.onLayoutFailed(null);
- // Mark layout was called.
- onLayoutCalled();
- return null;
- }
- }, null, new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- // Mark finish was called.
- onFinishCalled();
- return null;
- }
- });
+ invocation -> {
+ LayoutResultCallback callback = (LayoutResultCallback) invocation.getArguments()[3];
+ callback.onLayoutFailed(null);
+ // Mark layout was called.
+ onLayoutCalled();
+ return null;
+ }, null, invocation -> {
+ // Mark finish was called.
+ onFinishCalled();
+ return null;
+ });
// Start printing.
print(adapter);
@@ -1249,7 +1204,7 @@
// there are other printers but none of them was used.
PrintAttributes firstOldAttributes = new PrintAttributes.Builder().build();
PrintAttributes firstNewAttributes = new PrintAttributes.Builder()
- .setMediaSize(MediaSize.NA_LETTER)
+ .setMediaSize(getDefaultMediaSize())
.setResolution(new Resolution("PDF resolution", "PDF resolution", 300, 300))
.setMinMargins(Margins.NO_MARGINS).setColorMode(PrintAttributes.COLOR_MODE_COLOR)
.setDuplexMode(PrintAttributes.DUPLEX_MODE_NONE)
@@ -1275,36 +1230,27 @@
// Create a mock print adapter.
final PrintDocumentAdapter adapter = createMockPrintDocumentAdapter(
- new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- LayoutResultCallback callback = (LayoutResultCallback) invocation.getArguments()[3];
- PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
- .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT).setPageCount(1)
- .build();
- callback.onLayoutFinished(info, false);
- return null;
- }
- }, new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- Object[] args = invocation.getArguments();
- ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
- WriteResultCallback callback = (WriteResultCallback) args[3];
- fd.close();
- callback.onWriteFailed(null);
- // Mark write was called.
- onWriteCalled();
- return null;
- }
- }, new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- // Mark finish was called.
- onFinishCalled();
- return null;
- }
- });
+ invocation -> {
+ LayoutResultCallback callback = (LayoutResultCallback) invocation.getArguments()[3];
+ PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
+ .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT).setPageCount(1)
+ .build();
+ callback.onLayoutFinished(info, false);
+ return null;
+ }, invocation -> {
+ Object[] args = invocation.getArguments();
+ ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
+ WriteResultCallback callback = (WriteResultCallback) args[3];
+ fd.close();
+ callback.onWriteFailed(null);
+ // Mark write was called.
+ onWriteCalled();
+ return null;
+ }, invocation -> {
+ // Mark finish was called.
+ onFinishCalled();
+ return null;
+ });
// Start printing.
print(adapter);
@@ -1332,7 +1278,7 @@
// there are other printers but none of them was used.
PrintAttributes firstOldAttributes = new PrintAttributes.Builder().build();
PrintAttributes firstNewAttributes = new PrintAttributes.Builder()
- .setMediaSize(MediaSize.NA_LETTER)
+ .setMediaSize(getDefaultMediaSize())
.setResolution(new Resolution("PDF resolution", "PDF resolution", 300, 300))
.setMinMargins(Margins.NO_MARGINS).setColorMode(PrintAttributes.COLOR_MODE_COLOR)
.setDuplexMode(PrintAttributes.DUPLEX_MODE_NONE)
@@ -1351,6 +1297,150 @@
verifyNoMoreInteractions(adapter);
}
+ public void testUnexpectedLayoutCancel() throws Exception {
+ if (!supportsPrinting()) {
+ return;
+ }
+ // Configure the print services.
+ FirstPrintService.setCallbacks(createFirstMockPrintServiceCallbacks());
+ SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks());
+
+ final PrintAttributes[] printAttributes = new PrintAttributes[1];
+ final int[] numLayoutCalls = new int[1];
+
+ // Create a mock print adapter.
+ final PrintDocumentAdapter adapter = createMockPrintDocumentAdapter(
+ invocation -> {
+ printAttributes[0] = (PrintAttributes) invocation.getArguments()[1];
+ LayoutResultCallback callback = (LayoutResultCallback) invocation.getArguments()[3];
+
+ // Returned cancelled for the second layout call which is unexpected
+ if (numLayoutCalls[0] == 1) {
+ callback.onLayoutCancelled();
+ } else {
+ callback.onLayoutFinished(
+ (new PrintDocumentInfo.Builder(PRINT_JOB_NAME)).build(), false);
+ }
+ numLayoutCalls[0]++;
+ // Mark layout was called.
+ onLayoutCalled();
+ return null;
+ }, invocation -> {
+ Object[] args = invocation.getArguments();
+ PageRange[] pages = (PageRange[]) args[0];
+ ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
+ WriteResultCallback callback = (WriteResultCallback) args[3];
+ writeBlankPages(printAttributes[0], fd, 0, 1);
+ fd.close();
+ callback.onWriteFinished(pages);
+ // Mark write was called.
+ onWriteCalled();
+ return null;
+ }, invocation -> {
+ // Mark finish was called.
+ onFinishCalled();
+ return null;
+ });
+
+ // Start printing.
+ print(adapter);
+
+ // Wait for layout.
+ waitForLayoutAdapterCallbackCount(1);
+
+ // Select the second printer.
+ selectPrinter("Second printer");
+
+ // Wait for layout.
+ waitForLayoutAdapterCallbackCount(2);
+
+ // Retry layout (which should succeed) as the layout call will stop canceling after the
+ // second time
+ clickRetryButton();
+
+ // Click the print button.
+ clickPrintButton();
+
+ // Answer the dialog for the print service cloud warning
+ answerPrintServicesWarning(true);
+
+ // Wait for the session to be destroyed to isolate tests.
+ waitForPrinterDiscoverySessionDestroyCallbackCalled(1);
+ }
+
+ public void testUnexpectedWriteCancel() throws Exception {
+ if (!supportsPrinting()) {
+ return;
+ }
+ // Configure the print services.
+ FirstPrintService.setCallbacks(createFirstMockPrintServiceCallbacks());
+ SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks());
+
+ final PrintAttributes[] printAttributes = new PrintAttributes[1];
+ final int[] numWriteCalls = new int[1];
+
+ // Create a mock print adapter.
+ final PrintDocumentAdapter adapter = createMockPrintDocumentAdapter(
+ invocation -> {
+ printAttributes[0] = (PrintAttributes) invocation.getArguments()[1];
+ LayoutResultCallback callback = (LayoutResultCallback) invocation.getArguments()[3];
+
+ callback.onLayoutFinished(
+ (new PrintDocumentInfo.Builder(PRINT_JOB_NAME)).build(), true);
+
+ // Mark layout was called.
+ onLayoutCalled();
+ return null;
+ }, invocation -> {
+ Object[] args = invocation.getArguments();
+ WriteResultCallback callback = (WriteResultCallback) args[3];
+
+ // Returned cancelled for the second write call which is unexpected
+ if (numWriteCalls[0] == 1) {
+ callback.onWriteCancelled();
+ } else {
+ PageRange[] pages = (PageRange[]) args[0];
+ ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
+ writeBlankPages(printAttributes[0], fd, 0, 1);
+ fd.close();
+ callback.onWriteFinished(pages);
+ }
+ numWriteCalls[0]++;
+
+ // Mark write was called.
+ onWriteCalled();
+ return null;
+ }, invocation -> {
+ // Mark finish was called.
+ onFinishCalled();
+ return null;
+ });
+
+ // Start printing.
+ print(adapter);
+
+ waitForWriteAdapterCallback(1);
+
+ // Select the second printer.
+ selectPrinter("Second printer");
+
+ // Wait for layout.
+ waitForWriteAdapterCallback(2);
+
+ // Retry write (which should succeed) as the write call will stop canceling after the
+ // second time
+ clickRetryButton();
+
+ // Click the print button.
+ clickPrintButton();
+
+ // Answer the dialog for the print service cloud warning
+ answerPrintServicesWarning(true);
+
+ // Wait for the session to be destroyed to isolate tests.
+ waitForPrinterDiscoverySessionDestroyCallbackCalled(1);
+ }
+
public void testRequestedPagesNotWritten() throws Exception {
if (!supportsPrinting()) {
return;
@@ -1363,40 +1453,31 @@
// Create a mock print adapter.
final PrintDocumentAdapter adapter = createMockPrintDocumentAdapter(
- new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- printAttributes[0] = (PrintAttributes) invocation.getArguments()[1];
- LayoutResultCallback callback = (LayoutResultCallback) invocation.getArguments()[3];
- PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
- .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT).setPageCount(1)
- .build();
- callback.onLayoutFinished(info, false);
- return null;
- }
- }, new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- Object[] args = invocation.getArguments();
- ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
- WriteResultCallback callback = (WriteResultCallback) args[3];
- writeBlankPages(printAttributes[0], fd, Integer.MAX_VALUE, Integer.MAX_VALUE);
- fd.close();
- // Write wrong pages.
- callback.onWriteFinished(new PageRange[] {
- new PageRange(Integer.MAX_VALUE,Integer.MAX_VALUE)});
- // Mark write was called.
- onWriteCalled();
- return null;
- }
- }, new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- // Mark finish was called.
- onFinishCalled();
- return null;
- }
- });
+ invocation -> {
+ printAttributes[0] = (PrintAttributes) invocation.getArguments()[1];
+ LayoutResultCallback callback = (LayoutResultCallback) invocation.getArguments()[3];
+ PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
+ .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT).setPageCount(1)
+ .build();
+ callback.onLayoutFinished(info, false);
+ return null;
+ }, invocation -> {
+ Object[] args = invocation.getArguments();
+ ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
+ WriteResultCallback callback = (WriteResultCallback) args[3];
+ writeBlankPages(printAttributes[0], fd, Integer.MAX_VALUE, Integer.MAX_VALUE);
+ fd.close();
+ // Write wrong pages.
+ callback.onWriteFinished(new PageRange[] {
+ new PageRange(Integer.MAX_VALUE,Integer.MAX_VALUE)});
+ // Mark write was called.
+ onWriteCalled();
+ return null;
+ }, invocation -> {
+ // Mark finish was called.
+ onFinishCalled();
+ return null;
+ });
// Start printing.
print(adapter);
@@ -1424,7 +1505,7 @@
// there are other printers but none of them was used.
PrintAttributes firstOldAttributes = new PrintAttributes.Builder().build();
PrintAttributes firstNewAttributes = new PrintAttributes.Builder()
- .setMediaSize(MediaSize.NA_LETTER)
+ .setMediaSize(getDefaultMediaSize())
.setResolution(new Resolution("PDF resolution", "PDF resolution", 300, 300))
.setMinMargins(Margins.NO_MARGINS).setColorMode(PrintAttributes.COLOR_MODE_COLOR)
.setDuplexMode(PrintAttributes.DUPLEX_MODE_NONE)
@@ -1453,22 +1534,16 @@
// Create a mock print adapter.
final PrintDocumentAdapter adapter = createMockPrintDocumentAdapter(
- new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- // Break the contract and never call the callback.
- // Mark layout called.
- onLayoutCalled();
- return null;
- }
- }, null, new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- // Mark finish was called.
- onFinishCalled();
- return null;
- }
- });
+ invocation -> {
+ // Break the contract and never call the callback.
+ // Mark layout called.
+ onLayoutCalled();
+ return null;
+ }, null, invocation -> {
+ // Mark finish was called.
+ onFinishCalled();
+ return null;
+ });
// Start printing.
print(adapter);
@@ -1496,7 +1571,7 @@
// there are other printers but none of them was used.
PrintAttributes firstOldAttributes = new PrintAttributes.Builder().build();
PrintAttributes firstNewAttributes = new PrintAttributes.Builder()
- .setMediaSize(MediaSize.NA_LETTER)
+ .setMediaSize(getDefaultMediaSize())
.setResolution(new Resolution("PDF resolution", "PDF resolution", 300, 300))
.setMinMargins(Margins.NO_MARGINS).setColorMode(PrintAttributes.COLOR_MODE_COLOR)
.setDuplexMode(PrintAttributes.DUPLEX_MODE_NONE)
@@ -1520,35 +1595,26 @@
// Create a mock print adapter.
final PrintDocumentAdapter adapter = createMockPrintDocumentAdapter(
- new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- LayoutResultCallback callback = (LayoutResultCallback) invocation.getArguments()[3];
- PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
- .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT).setPageCount(1)
- .build();
- callback.onLayoutFinished(info, false);
- return null;
- }
- }, new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- Object[] args = invocation.getArguments();
- ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
- fd.close();
- // Break the contract and never call the callback.
- // Mark write was called.
- onWriteCalled();
- return null;
- }
- }, new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- // Mark finish was called.
- onFinishCalled();
- return null;
- }
- });
+ invocation -> {
+ LayoutResultCallback callback = (LayoutResultCallback) invocation.getArguments()[3];
+ PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
+ .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT).setPageCount(1)
+ .build();
+ callback.onLayoutFinished(info, false);
+ return null;
+ }, invocation -> {
+ Object[] args = invocation.getArguments();
+ ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
+ fd.close();
+ // Break the contract and never call the callback.
+ // Mark write was called.
+ onWriteCalled();
+ return null;
+ }, invocation -> {
+ // Mark finish was called.
+ onFinishCalled();
+ return null;
+ });
// Start printing.
print(adapter);
@@ -1576,7 +1642,7 @@
// there are other printers but none of them was used.
PrintAttributes firstOldAttributes = new PrintAttributes.Builder().build();
PrintAttributes firstNewAttributes = new PrintAttributes.Builder()
- .setMediaSize(MediaSize.NA_LETTER)
+ .setMediaSize(getDefaultMediaSize())
.setResolution(new Resolution("PDF resolution", "PDF resolution", 300, 300))
.setMinMargins(Margins.NO_MARGINS).setColorMode(PrintAttributes.COLOR_MODE_COLOR)
.setDuplexMode(PrintAttributes.DUPLEX_MODE_NONE)
@@ -1595,6 +1661,174 @@
verifyNoMoreInteractions(adapter);
}
+ public void testLayoutCallbackCalledTwice() throws Exception {
+ if (!supportsPrinting()) {
+ return;
+ }
+ // Configure the print services.
+ FirstPrintService.setCallbacks(createFirstMockPrintServiceCallbacks());
+ SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks());
+
+ // Create a mock print adapter.
+ final PrintDocumentAdapter adapter = createMockPrintDocumentAdapter(
+ invocation -> {
+ PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
+ .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
+ .setPageCount(1)
+ .build();
+ LayoutResultCallback callback = (LayoutResultCallback) invocation
+ .getArguments()[3];
+
+ // Break the contract and call the callback twice.
+ callback.onLayoutFinished(info, true);
+ callback.onLayoutFinished(info, true);
+
+ // Mark layout called.
+ onLayoutCalled();
+ return null;
+ }, null, invocation -> {
+ // Mark finish was called.
+ onFinishCalled();
+ return null;
+ });
+
+ // Start printing.
+ print(adapter);
+
+ // Wait for layout.
+ waitForLayoutAdapterCallbackCount(1);
+
+ // Cancel printing.
+ getUiDevice().pressBack(); // wakes up the device.
+ getUiDevice().pressBack();
+
+ // Wait for a finish.
+ waitForAdapterFinishCallbackCalled();
+
+ // Wait for the session to be destroyed to isolate tests.
+ waitForPrinterDiscoverySessionDestroyCallbackCalled(1);
+
+ // Verify the expected calls.
+ InOrder inOrder = inOrder(adapter);
+
+ // Start is always called first.
+ inOrder.verify(adapter).onStart();
+
+ // Start is always followed by a layout. The PDF printer is selected if
+ // there are other printers but none of them was used.
+ PrintAttributes firstOldAttributes = new PrintAttributes.Builder().build();
+ PrintAttributes firstNewAttributes = new PrintAttributes.Builder()
+ .setMediaSize(getDefaultMediaSize())
+ .setResolution(new Resolution("PDF resolution", "PDF resolution", 300, 300))
+ .setMinMargins(Margins.NO_MARGINS).setColorMode(PrintAttributes.COLOR_MODE_COLOR)
+ .setDuplexMode(PrintAttributes.DUPLEX_MODE_NONE)
+ .build();
+ verifyLayoutCall(inOrder, adapter, firstOldAttributes, firstNewAttributes, true);
+
+ PageRange[] firstPage = new PageRange[] {new PageRange(0, 0)};
+ inOrder.verify(adapter).onWrite(eq(firstPage), any(ParcelFileDescriptor.class),
+ any(CancellationSignal.class), any(WriteResultCallback.class));
+
+ // Finish is always called last.
+ inOrder.verify(adapter).onFinish();
+
+ // No other call are expected.
+ verifyNoMoreInteractions(adapter);
+ }
+
+ public void testWriteCallbackCalledTwice() throws Exception {
+ if (!supportsPrinting()) {
+ return;
+ }
+ // Configure the print services.
+ FirstPrintService.setCallbacks(createFirstMockPrintServiceCallbacks());
+ SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks());
+
+ final PrintAttributes[] printAttributes = new PrintAttributes[1];
+
+ // Create a mock print adapter.
+ final PrintDocumentAdapter adapter = createMockPrintDocumentAdapter(
+ invocation -> {
+ printAttributes[0] = (PrintAttributes) invocation.getArguments()[1];
+ PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
+ .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
+ .setPageCount(1)
+ .build();
+ LayoutResultCallback callback = (LayoutResultCallback) invocation
+ .getArguments()[3];
+
+ callback.onLayoutFinished(info, true);
+
+ // Mark layout called.
+ onLayoutCalled();
+ return null;
+ }, invocation -> {
+ Object[] args = invocation.getArguments();
+ PageRange[] pages = (PageRange[]) args[0];
+ ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
+ WriteResultCallback callback = (WriteResultCallback) args[3];
+
+ // Write only one pages
+ writeBlankPages(printAttributes[0], fd, 0, 0);
+ fd.close();
+
+ // Break the contract and call callback twice
+ callback.onWriteFinished(pages);
+ callback.onWriteFinished(pages);
+
+ // Mark write called
+ onWriteCalled();
+ return null;
+ }, invocation -> {
+ // Mark finish was called.
+ onFinishCalled();
+ return null;
+ });
+
+ // Start printing.
+ print(adapter);
+
+ // Wait for layout.
+ waitForLayoutAdapterCallbackCount(1);
+
+ // Cancel printing.
+ getUiDevice().pressBack(); // wakes up the device.
+ getUiDevice().pressBack();
+
+ // Wait for a finish.
+ waitForAdapterFinishCallbackCalled();
+
+ // Wait for the session to be destroyed to isolate tests.
+ waitForPrinterDiscoverySessionDestroyCallbackCalled(1);
+
+ // Verify the expected calls.
+ InOrder inOrder = inOrder(adapter);
+
+ // Start is always called first.
+ inOrder.verify(adapter).onStart();
+
+ // Start is always followed by a layout. The PDF printer is selected if
+ // there are other printers but none of them was used.
+ PrintAttributes firstOldAttributes = new PrintAttributes.Builder().build();
+ PrintAttributes firstNewAttributes = new PrintAttributes.Builder()
+ .setMediaSize(getDefaultMediaSize())
+ .setResolution(new Resolution("PDF resolution", "PDF resolution", 300, 300))
+ .setMinMargins(Margins.NO_MARGINS).setColorMode(PrintAttributes.COLOR_MODE_COLOR)
+ .setDuplexMode(PrintAttributes.DUPLEX_MODE_NONE)
+ .build();
+ verifyLayoutCall(inOrder, adapter, firstOldAttributes, firstNewAttributes, true);
+
+ PageRange[] firstPage = new PageRange[] {new PageRange(0, 0)};
+ inOrder.verify(adapter).onWrite(eq(firstPage), any(ParcelFileDescriptor.class),
+ any(CancellationSignal.class), any(WriteResultCallback.class));
+
+ // Finish is always called last.
+ inOrder.verify(adapter).onFinish();
+
+ // No other call are expected.
+ verifyNoMoreInteractions(adapter);
+ }
+
/**
* Pretend to have written two pages, but only actually write one page
*
@@ -1611,44 +1845,35 @@
final PrintAttributes[] printAttributes = new PrintAttributes[1];
final PrintDocumentAdapter adapter = createMockPrintDocumentAdapter(
- new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- printAttributes[0] = (PrintAttributes) invocation.getArguments()[1];
- LayoutResultCallback callback = (LayoutResultCallback) invocation
- .getArguments()[3];
+ invocation -> {
+ printAttributes[0] = (PrintAttributes) invocation.getArguments()[1];
+ LayoutResultCallback callback = (LayoutResultCallback) invocation
+ .getArguments()[3];
- // Lay out two pages
- PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
- .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
- .setPageCount(2)
- .build();
- callback.onLayoutFinished(info, true);
- return null;
- }
- }, new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- Object[] args = invocation.getArguments();
- PageRange[] pages = (PageRange[]) args[0];
- ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
- WriteResultCallback callback = (WriteResultCallback) args[3];
+ // Lay out two pages
+ PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
+ .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
+ .setPageCount(2)
+ .build();
+ callback.onLayoutFinished(info, true);
+ return null;
+ }, invocation -> {
+ Object[] args = invocation.getArguments();
+ PageRange[] pages = (PageRange[]) args[0];
+ ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
+ WriteResultCallback callback = (WriteResultCallback) args[3];
- // Write only one pages
- writeBlankPages(printAttributes[0], fd, 0, 0);
- fd.close();
+ // Write only one pages
+ writeBlankPages(printAttributes[0], fd, 0, 0);
+ fd.close();
- // Break the contract and report that two pages were written
- callback.onWriteFinished(pages);
- onWriteCalled();
- return null;
- }
- }, new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- onFinishCalled();
- return null;
- }
+ // Break the contract and report that two pages were written
+ callback.onWriteFinished(pages);
+ onWriteCalled();
+ return null;
+ }, invocation -> {
+ onFinishCalled();
+ return null;
});
print(adapter);
@@ -1668,52 +1893,65 @@
/**
* Executes a print process with a given print document info
*
- * @param info The print document info to declare on layout
+ * @param name The name of the document info
+ * @param contentType The content type of the document
+ * @param pageCount The number of pages in the document
*/
- private void printDocumentInfoBaseTest(final PrintDocumentInfo info) throws Exception {
+ private void printDocumentBaseTest(String name, Integer contentType, Integer pageCount)
+ throws Throwable {
if (!supportsPrinting()) {
return;
}
- // Configure the print services.
- FirstPrintService.setCallbacks(createFirstMockPrintServiceCallbacks());
+
+ PrintDocumentInfo.Builder b = new PrintDocumentInfo.Builder(name);
+ if (contentType != null) {
+ b.setContentType(contentType);
+ }
+ if (pageCount != null) {
+ b.setPageCount(pageCount);
+ }
+ PrintDocumentInfo info = b.build();
+
+ PrintDocumentInfo queuedInfo[] = new PrintDocumentInfo[1];
+ ParcelFileDescriptor queuedData[] = new ParcelFileDescriptor[1];
+
+ PrinterDiscoverySessionCallbacks printerDiscoverySessionCallbacks =
+ createFirstMockDiscoverySessionCallbacks();
+ PrintServiceCallbacks printServiceCallbacks = createMockPrintServiceCallbacks(
+ invocation -> printerDiscoverySessionCallbacks,
+ invocation -> {
+ PrintJob printJob = (PrintJob) invocation.getArguments()[0];
+ queuedInfo[0] = printJob.getDocument().getInfo();
+ queuedData[0] = printJob.getDocument().getData();
+ printJob.complete();
+ return null;
+ }, null);
+
+ FirstPrintService.setCallbacks(printServiceCallbacks);
SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks());
final PrintAttributes[] printAttributes = new PrintAttributes[1];
// Create a mock print adapter.
final PrintDocumentAdapter adapter = createMockPrintDocumentAdapter(
- new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- printAttributes[0] = (PrintAttributes) invocation.getArguments()[1];
- LayoutResultCallback callback = (LayoutResultCallback) invocation.getArguments()[3];
- callback.onLayoutFinished(info, false);
- // Mark layout was called.
- onLayoutCalled();
- return null;
- }
- }, new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- Object[] args = invocation.getArguments();
- PageRange[] pages = (PageRange[]) args[0];
- ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
- WriteResultCallback callback = (WriteResultCallback) args[3];
- writeBlankPages(printAttributes[0], fd, 0, 1);
- fd.close();
- callback.onWriteFinished(pages);
- // Mark write was called.
- onWriteCalled();
- return null;
- }
- }, new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- // Mark finish was called.
- onFinishCalled();
- return null;
- }
- });
+ invocation -> {
+ printAttributes[0] = (PrintAttributes) invocation.getArguments()[1];
+ LayoutResultCallback callback = (LayoutResultCallback) invocation
+ .getArguments()[3];
+ callback.onLayoutFinished(info, false);
+ // Mark layout was called.
+ onLayoutCalled();
+ return null;
+ }, invocation -> {
+ Object[] args = invocation.getArguments();
+ PageRange[] pages = (PageRange[]) args[0];
+ ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
+ WriteResultCallback callback = (WriteResultCallback) args[3];
+ writeBlankPages(printAttributes[0], fd, 0, 1);
+ fd.close();
+ callback.onWriteFinished(pages);
+ return null;
+ }, invocation -> null);
// Start printing.
print(adapter);
@@ -1732,6 +1970,40 @@
// Wait for the session to be destroyed to isolate tests.
waitForPrinterDiscoverySessionDestroyCallbackCalled(1);
+
+ // Check that the document name was carried over 1:1
+ eventually(() -> assertEquals(name, queuedInfo[0].getName()));
+
+ // Content type is set to document by default, but is otherwise unrestricted
+ if (contentType != null) {
+ assertEquals(contentType, Integer.valueOf(queuedInfo[0].getContentType()));
+ } else {
+ assertEquals(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT, queuedInfo[0].getContentType());
+ }
+
+ // Page count is set to the real value if unknown, 0 or unset.
+ // Otherwise the set value is used
+ if (pageCount != null && pageCount != PrintDocumentInfo.PAGE_COUNT_UNKNOWN
+ && pageCount != 0) {
+ assertEquals(pageCount, Integer.valueOf(queuedInfo[0].getPageCount()));
+ } else {
+ assertEquals(2, queuedInfo[0].getPageCount());
+ }
+
+ // Verify data (== pdf file) size
+ assertTrue(queuedInfo[0].getDataSize() > 0);
+
+ long bytesRead = 0;
+ try (FileInputStream is = new FileInputStream(queuedData[0].getFileDescriptor())) {
+ while (true) {
+ int ret = is.read();
+ if (ret == -1) {
+ break;
+ }
+ bytesRead++;
+ }
+ }
+ assertEquals(queuedInfo[0].getDataSize(), bytesRead);
}
/**
@@ -1739,8 +2011,8 @@
*
* @throws Exception If anything unexpected happens
*/
- public void testDocumentInfoNothingSet() throws Exception {
- printDocumentInfoBaseTest((new PrintDocumentInfo.Builder(PRINT_JOB_NAME)).build());
+ public void testDocumentInfoNothingSet() throws Throwable {
+ printDocumentBaseTest(PRINT_JOB_NAME, null, null);
}
/**
@@ -1748,9 +2020,8 @@
*
* @throws Exception If anything unexpected happens
*/
- public void testDocumentInfoUnknownPageCount() throws Exception {
- printDocumentInfoBaseTest((new PrintDocumentInfo.Builder(PRINT_JOB_NAME))
- .setPageCount(PrintDocumentInfo.PAGE_COUNT_UNKNOWN).build());
+ public void testDocumentInfoUnknownPageCount() throws Throwable {
+ printDocumentBaseTest(PRINT_JOB_NAME, null, PrintDocumentInfo.PAGE_COUNT_UNKNOWN);
}
/**
@@ -1758,9 +2029,8 @@
*
* @throws Exception If anything unexpected happens
*/
- public void testDocumentInfoZeroPageCount() throws Exception {
- printDocumentInfoBaseTest((new PrintDocumentInfo.Builder(PRINT_JOB_NAME))
- .setPageCount(0).build());
+ public void testDocumentInfoZeroPageCount() throws Throwable {
+ printDocumentBaseTest(PRINT_JOB_NAME, null, 0);
}
/**
@@ -1768,9 +2038,8 @@
*
* @throws Exception If anything unexpected happens
*/
- public void testDocumentInfoOnePageCount() throws Exception {
- printDocumentInfoBaseTest((new PrintDocumentInfo.Builder(PRINT_JOB_NAME))
- .setPageCount(1).build());
+ public void testDocumentInfoOnePageCount() throws Throwable {
+ printDocumentBaseTest(PRINT_JOB_NAME, null, 1);
}
/**
@@ -1778,9 +2047,8 @@
*
* @throws Exception If anything unexpected happens
*/
- public void testDocumentInfoThreePageCount() throws Exception {
- printDocumentInfoBaseTest((new PrintDocumentInfo.Builder(PRINT_JOB_NAME))
- .setPageCount(3).build());
+ public void testDocumentInfoThreePageCount() throws Throwable {
+ printDocumentBaseTest(PRINT_JOB_NAME, null, 3);
}
/**
@@ -1788,9 +2056,8 @@
*
* @throws Exception If anything unexpected happens
*/
- public void testDocumentInfoContentTypePhoto() throws Exception {
- printDocumentInfoBaseTest((new PrintDocumentInfo.Builder(PRINT_JOB_NAME))
- .setContentType(PrintDocumentInfo.CONTENT_TYPE_PHOTO).build());
+ public void testDocumentInfoContentTypePhoto() throws Throwable {
+ printDocumentBaseTest(PRINT_JOB_NAME, PrintDocumentInfo.CONTENT_TYPE_PHOTO, null);
}
/**
@@ -1798,9 +2065,8 @@
*
* @throws Exception If anything unexpected happens
*/
- public void testDocumentInfoContentTypeUnknown() throws Exception {
- printDocumentInfoBaseTest((new PrintDocumentInfo.Builder(PRINT_JOB_NAME))
- .setContentType(PrintDocumentInfo.CONTENT_TYPE_UNKNOWN).build());
+ public void testDocumentInfoContentTypeUnknown() throws Throwable {
+ printDocumentBaseTest(PRINT_JOB_NAME, PrintDocumentInfo.CONTENT_TYPE_UNKNOWN, null);
}
/**
@@ -1808,104 +2074,99 @@
*
* @throws Exception If anything unexpected happens
*/
- public void testDocumentInfoContentTypeNonDefined() throws Exception {
- printDocumentInfoBaseTest((new PrintDocumentInfo.Builder(PRINT_JOB_NAME))
- .setContentType(-23).build());
+ public void testDocumentInfoContentTypeNonDefined() throws Throwable {
+ printDocumentBaseTest(PRINT_JOB_NAME, -23, null);
+ }
+
+ private PrinterDiscoverySessionCallbacks createFirstMockDiscoverySessionCallbacks() {
+ return createMockPrinterDiscoverySessionCallbacks(invocation -> {
+ PrinterDiscoverySessionCallbacks mock = (PrinterDiscoverySessionCallbacks)
+ invocation.getMock();
+
+ StubbablePrinterDiscoverySession session = mock.getSession();
+ PrintService service = session.getService();
+
+ if (session.getPrinters().isEmpty()) {
+ List<PrinterInfo> printers = new ArrayList<>();
+
+ // Add the first printer.
+ PrinterId firstPrinterId = service.generatePrinterId("first_printer");
+ PrinterCapabilitiesInfo firstCapabilities =
+ new PrinterCapabilitiesInfo.Builder(firstPrinterId)
+ .setMinMargins(new Margins(200, 200, 200, 200))
+ .addMediaSize(MediaSize.ISO_A0, true)
+ .addMediaSize(MediaSize.ISO_A5, false)
+ .addResolution(new Resolution("300x300", "300x300", 300, 300), true)
+ .setColorModes(PrintAttributes.COLOR_MODE_COLOR,
+ PrintAttributes.COLOR_MODE_COLOR)
+ .build();
+ PrinterInfo firstPrinter = new PrinterInfo.Builder(firstPrinterId,
+ "First printer", PrinterInfo.STATUS_IDLE)
+ .setCapabilities(firstCapabilities)
+ .build();
+ printers.add(firstPrinter);
+
+ // Add the second printer.
+ PrinterId secondPrinterId = service.generatePrinterId("second_printer");
+ PrinterCapabilitiesInfo secondCapabilities =
+ new PrinterCapabilitiesInfo.Builder(secondPrinterId)
+ .addMediaSize(MediaSize.ISO_A3, true)
+ .addMediaSize(MediaSize.ISO_A0, false)
+ .addResolution(new Resolution("200x200", "200x200", 200, 200), true)
+ .addResolution(new Resolution("300x300", "300x300", 300, 300),
+ false)
+ .setColorModes(PrintAttributes.COLOR_MODE_COLOR
+ | PrintAttributes.COLOR_MODE_MONOCHROME,
+ PrintAttributes.COLOR_MODE_MONOCHROME
+ )
+ .setDuplexModes(PrintAttributes.DUPLEX_MODE_LONG_EDGE
+ | PrintAttributes.DUPLEX_MODE_SHORT_EDGE,
+ PrintAttributes.DUPLEX_MODE_LONG_EDGE
+ )
+ .build();
+ PrinterInfo secondPrinter = new PrinterInfo.Builder(secondPrinterId,
+ "Second printer", PrinterInfo.STATUS_IDLE)
+ .setCapabilities(secondCapabilities)
+ .build();
+ printers.add(secondPrinter);
+
+ // Add the third printer.
+ PrinterId thirdPrinterId = service.generatePrinterId("third_printer");
+ PrinterCapabilitiesInfo thirdCapabilities =
+ null;
+ try {
+ thirdCapabilities = new PrinterCapabilitiesInfo.Builder(thirdPrinterId)
+ .addMediaSize(getDefaultMediaSize(), true)
+ .addResolution(new Resolution("300x300", "300x300", 300, 300), true)
+ .setColorModes(PrintAttributes.COLOR_MODE_COLOR,
+ PrintAttributes.COLOR_MODE_COLOR)
+ .build();
+ } catch (Exception e) {
+ Log.e(LOG_TAG, "Cannot create third printer", e);
+ }
+ PrinterInfo thirdPrinter = new PrinterInfo.Builder(thirdPrinterId,
+ "Third printer", PrinterInfo.STATUS_IDLE)
+ .setCapabilities(thirdCapabilities)
+ .build();
+ printers.add(thirdPrinter);
+
+ session.addPrinters(printers);
+ }
+ return null;
+ }, null, null, null, null, null, invocation -> {
+ // Take a note onDestroy was called.
+ onPrinterDiscoverySessionDestroyCalled();
+ return null;
+ });
}
private PrintServiceCallbacks createFirstMockPrintServiceCallbacks() {
final PrinterDiscoverySessionCallbacks callbacks =
- createMockPrinterDiscoverySessionCallbacks(new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) {
- PrinterDiscoverySessionCallbacks mock = (PrinterDiscoverySessionCallbacks)
- invocation.getMock();
-
- StubbablePrinterDiscoverySession session = mock.getSession();
- PrintService service = session.getService();
-
- if (session.getPrinters().isEmpty()) {
- List<PrinterInfo> printers = new ArrayList<PrinterInfo>();
-
- // Add the first printer.
- PrinterId firstPrinterId = service.generatePrinterId("first_printer");
- PrinterCapabilitiesInfo firstCapabilities =
- new PrinterCapabilitiesInfo.Builder(firstPrinterId)
- .setMinMargins(new Margins(200, 200, 200, 200))
- .addMediaSize(MediaSize.ISO_A4, true)
- .addMediaSize(MediaSize.ISO_A5, false)
- .addResolution(new Resolution("300x300", "300x300", 300, 300), true)
- .setColorModes(PrintAttributes.COLOR_MODE_COLOR,
- PrintAttributes.COLOR_MODE_COLOR)
- .build();
- PrinterInfo firstPrinter = new PrinterInfo.Builder(firstPrinterId,
- "First printer", PrinterInfo.STATUS_IDLE)
- .setCapabilities(firstCapabilities)
- .build();
- printers.add(firstPrinter);
-
- // Add the second printer.
- PrinterId secondPrinterId = service.generatePrinterId("second_printer");
- PrinterCapabilitiesInfo secondCapabilities =
- new PrinterCapabilitiesInfo.Builder(secondPrinterId)
- .addMediaSize(MediaSize.ISO_A3, true)
- .addMediaSize(MediaSize.ISO_A4, false)
- .addResolution(new Resolution("200x200", "200x200", 200, 200), true)
- .addResolution(new Resolution("300x300", "300x300", 300, 300), false)
- .setColorModes(PrintAttributes.COLOR_MODE_COLOR
- | PrintAttributes.COLOR_MODE_MONOCHROME,
- PrintAttributes.COLOR_MODE_MONOCHROME
- )
- .setDuplexModes(PrintAttributes.DUPLEX_MODE_LONG_EDGE
- | PrintAttributes.DUPLEX_MODE_SHORT_EDGE,
- PrintAttributes.DUPLEX_MODE_LONG_EDGE
- )
- .build();
- PrinterInfo secondPrinter = new PrinterInfo.Builder(secondPrinterId,
- "Second printer", PrinterInfo.STATUS_IDLE)
- .setCapabilities(secondCapabilities)
- .build();
- printers.add(secondPrinter);
-
- // Add the third printer.
- PrinterId thirdPrinterId = service.generatePrinterId("third_printer");
- PrinterCapabilitiesInfo thirdCapabilities =
- new PrinterCapabilitiesInfo.Builder(thirdPrinterId)
- .addMediaSize(MediaSize.NA_LETTER, true)
- .addResolution(new Resolution("300x300", "300x300", 300, 300), true)
- .setColorModes(PrintAttributes.COLOR_MODE_COLOR,
- PrintAttributes.COLOR_MODE_COLOR)
- .build();
- PrinterInfo thirdPrinter = new PrinterInfo.Builder(thirdPrinterId,
- "Third printer", PrinterInfo.STATUS_IDLE)
- .setCapabilities(thirdCapabilities)
- .build();
- printers.add(thirdPrinter);
-
- session.addPrinters(printers);
- }
- return null;
- }
- }, null, null, null, null, null, new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- // Take a note onDestroy was called.
- onPrinterDiscoverySessionDestroyCalled();
- return null;
- }
- });
- return createMockPrintServiceCallbacks(new Answer<PrinterDiscoverySessionCallbacks>() {
- @Override
- public PrinterDiscoverySessionCallbacks answer(InvocationOnMock invocation) {
- return callbacks;
- }
- }, new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- PrintJob printJob = (PrintJob) invocation.getArguments()[0];
- printJob.complete();
- return null;
- }
+ createFirstMockDiscoverySessionCallbacks();
+ return createMockPrintServiceCallbacks(invocation -> callbacks, invocation -> {
+ PrintJob printJob = (PrintJob) invocation.getArguments()[0];
+ printJob.complete();
+ return null;
}, null);
}
diff --git a/tests/tests/print/src/android/print/cts/PrintJobTest.java b/tests/tests/print/src/android/print/cts/PrintJobTest.java
index b0f3742e..e3d8eab 100644
--- a/tests/tests/print/src/android/print/cts/PrintJobTest.java
+++ b/tests/tests/print/src/android/print/cts/PrintJobTest.java
@@ -37,19 +37,11 @@
import android.print.cts.services.SecondPrintService;
import android.print.cts.services.StubbablePrinterDiscoverySession;
import android.printservice.PrintJob;
-import android.support.test.uiautomator.UiObject;
-import android.support.test.uiautomator.UiSelector;
import android.util.Log;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
import java.util.ArrayList;
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertNotNull;
-import static junit.framework.Assert.assertNull;
-import static junit.framework.Assert.assertTrue;
-import static junit.framework.Assert.assertFalse;
+import static android.print.cts.Utils.eventually;
/**
@@ -71,53 +63,7 @@
private final boolean testSuccess[] = new boolean[1];
/** The printer discovery session used in this test */
- private static StubbablePrinterDiscoverySession mDiscoverySession;
-
- /**
- * Create a mock {@link PrintDocumentAdapter} that provides one empty page.
- *
- * @return The mock adapter
- */
- private PrintDocumentAdapter createMockPrintDocumentAdapter() {
- final PrintAttributes[] printAttributes = new PrintAttributes[1];
-
- return createMockPrintDocumentAdapter(
- new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- printAttributes[0] = (PrintAttributes) invocation.getArguments()[1];
- LayoutResultCallback callback = (LayoutResultCallback) invocation
- .getArguments()[3];
-
- PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
- .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
- .setPageCount(1)
- .build();
-
- callback.onLayoutFinished(info, false);
- return null;
- }
- }, new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- Object[] args = invocation.getArguments();
- PageRange[] pages = (PageRange[]) args[0];
- ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
- WriteResultCallback callback = (WriteResultCallback) args[3];
-
- writeBlankPages(printAttributes[0], fd, pages[0].getStart(),
- pages[0].getEnd());
- fd.close();
- callback.onWriteFinished(pages);
- return null;
- }
- }, new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- return null;
- }
- });
- }
+ private static StubbablePrinterDiscoverySession sDiscoverySession;
/**
* Create a mock {@link PrinterDiscoverySessionCallbacks} that discovers a simple test printer.
@@ -125,51 +71,40 @@
* @return The mock session callbacks
*/
private PrinterDiscoverySessionCallbacks createFirstMockPrinterDiscoverySessionCallbacks() {
- return createMockPrinterDiscoverySessionCallbacks(new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) {
- // Get the session.
- mDiscoverySession = ((PrinterDiscoverySessionCallbacks) invocation.getMock())
- .getSession();
+ return createMockPrinterDiscoverySessionCallbacks(invocation -> {
+ // Get the session.
+ sDiscoverySession = ((PrinterDiscoverySessionCallbacks) invocation.getMock())
+ .getSession();
- if (mDiscoverySession.getPrinters().isEmpty()) {
- PrinterId printerId =
- mDiscoverySession.getService().generatePrinterId(PRINTER_NAME);
- PrinterInfo.Builder printer = new PrinterInfo.Builder(
- mDiscoverySession.getService().generatePrinterId(PRINTER_NAME),
- PRINTER_NAME, PrinterInfo.STATUS_IDLE);
+ if (sDiscoverySession.getPrinters().isEmpty()) {
+ PrinterId printerId =
+ sDiscoverySession.getService().generatePrinterId(PRINTER_NAME);
+ PrinterInfo.Builder printer = new PrinterInfo.Builder(
+ sDiscoverySession.getService().generatePrinterId(PRINTER_NAME),
+ PRINTER_NAME, PrinterInfo.STATUS_IDLE);
- printer.setCapabilities(new PrinterCapabilitiesInfo.Builder(printerId)
- .addMediaSize(MediaSize.ISO_A4, true)
- .addResolution(new Resolution("300x300", "300dpi", 300, 300), true)
- .setColorModes(PrintAttributes.COLOR_MODE_COLOR,
- PrintAttributes.COLOR_MODE_COLOR)
- .setMinMargins(new Margins(0, 0, 0, 0)).build());
+ printer.setCapabilities(new PrinterCapabilitiesInfo.Builder(printerId)
+ .addMediaSize(MediaSize.ISO_A4, true)
+ .addResolution(new Resolution("300x300", "300dpi", 300, 300), true)
+ .setColorModes(PrintAttributes.COLOR_MODE_COLOR,
+ PrintAttributes.COLOR_MODE_COLOR)
+ .setMinMargins(new Margins(0, 0, 0, 0)).build());
- ArrayList<PrinterInfo> printers = new ArrayList<>(1);
- printers.add(printer.build());
+ ArrayList<PrinterInfo> printers = new ArrayList<>(1);
+ printers.add(printer.build());
- mDiscoverySession.addPrinters(printers);
- }
- return null;
+ sDiscoverySession.addPrinters(printers);
}
- }, null, null, new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- return null;
- }
- }, null, null, new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- // Take a note onDestroy was called.
- onPrinterDiscoverySessionDestroyCalled();
- return null;
- }
+ return null;
+ }, null, null, invocation -> null, null, null, invocation -> {
+ // Take a note onDestroy was called.
+ onPrinterDiscoverySessionDestroyCalled();
+ return null;
});
}
private interface PrintJobTestFn {
- void onPrintJobQueued(PrintJob printJob) throws Exception;
+ void onPrintJobQueued(PrintJob printJob) throws Throwable;
}
/**
@@ -183,57 +118,20 @@
final PrinterDiscoverySessionCallbacks sessionCallbacks,
final PrintJobTestFn printJobTest) {
return createMockPrintServiceCallbacks(
- new Answer<PrinterDiscoverySessionCallbacks>() {
- @Override
- public PrinterDiscoverySessionCallbacks answer(InvocationOnMock invocation) {
- return sessionCallbacks;
- }
- }, new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) {
- PrintJob printJob = (PrintJob) invocation.getArguments()[0];
+ invocation -> sessionCallbacks, invocation -> {
+ PrintJob printJob = (PrintJob) invocation.getArguments()[0];
- try {
- printJobTest.onPrintJobQueued(printJob);
- testSuccess[0] = true;
- } catch (Exception e) {
- Log.e(LOG_TAG, "Test function failed", e);
- }
-
- onPrintJobQueuedCalled();
-
- return null;
- }
- }, null);
- }
-
- /**
- * Make sure that a runnable eventually finishes without throwing a exception.
- *
- * @param r The runnable to run.
- */
- private static void eventually(Runnable r) {
- final long TIMEOUT_MILLS = 5000;
- long start = System.currentTimeMillis();
-
- while (true) {
- try {
- r.run();
- break;
- } catch (Exception e) {
- if (System.currentTimeMillis() - start < TIMEOUT_MILLS) {
- Log.e(LOG_TAG, "Ignoring exception as we know that the print spooler does " +
- "not guarantee to process commands in order", e);
try {
- Thread.sleep(100);
- } catch (InterruptedException e1) {
- Log.e(LOG_TAG, "Interrupted", e);
+ printJobTest.onPrintJobQueued(printJob);
+ testSuccess[0] = true;
+ } catch (Throwable t) {
+ throw new RuntimeException(t);
}
- } else {
- throw e;
- }
- }
- }
+
+ onPrintJobQueuedCalled();
+
+ return null;
+ }, null);
}
/**
@@ -265,7 +163,7 @@
SecondPrintService.setCallbacks(createMockPrintServiceCallbacks(null, null, null));
// Create a print adapter that respects the print contract.
- PrintDocumentAdapter adapter = createMockPrintDocumentAdapter();
+ PrintDocumentAdapter adapter = createDefaultPrintDocumentAdapter(1);
// Start printing.
print(adapter);
@@ -355,7 +253,7 @@
}
}
- private static void checkState(PrintJob job, int state) {
+ private static void checkState(PrintJob job, int state) throws Throwable {
eventually(() -> assertEquals(state, job.getInfo().getState()));
switch (state) {
case PrintJobInfo.STATE_QUEUED:
@@ -416,43 +314,40 @@
Log.i(LOG_TAG, "Test " + state1 + " -> " + state2 + " -> " + state3);
- baseTest(new PrintJobTestFn() {
- @Override
- public void onPrintJobQueued(PrintJob printJob) throws Exception {
- knownFailures[PrintJobInfo.STATE_QUEUED][state1] = true;
+ baseTest(printJob -> {
+ knownFailures[PrintJobInfo.STATE_QUEUED][state1] = true;
- boolean success = setState(printJob, state1);
- assertEquals(isStateTransitionAllowed(PrintJobInfo.STATE_QUEUED,
- state1), success);
- if (!success) {
- return;
- }
- checkState(printJob, state1);
-
- knownFailures[PrintJobInfo.STATE_QUEUED][state1] = false;
-
- knownFailures[state1][state2] = true;
-
- success = setState(printJob, state2);
- assertEquals(isStateTransitionAllowed(state1, state2), success);
- if (!success) {
- return;
- }
- checkState(printJob, state2);
-
- knownFailures[state1][state2] = false;
-
- knownFailures[state2][state3] = true;
-
- success = setState(printJob, state3);
- assertEquals(isStateTransitionAllowed(state2, state3), success);
- if (!success) {
- return;
- }
- checkState(printJob, state3);
-
- knownFailures[state2][state3] = false;
+ boolean success = setState(printJob, state1);
+ assertEquals(isStateTransitionAllowed(PrintJobInfo.STATE_QUEUED,
+ state1), success);
+ if (!success) {
+ return;
}
+ checkState(printJob, state1);
+
+ knownFailures[PrintJobInfo.STATE_QUEUED][state1] = false;
+
+ knownFailures[state1][state2] = true;
+
+ success = setState(printJob, state2);
+ assertEquals(isStateTransitionAllowed(state1, state2), success);
+ if (!success) {
+ return;
+ }
+ checkState(printJob, state2);
+
+ knownFailures[state1][state2] = false;
+
+ knownFailures[state2][state3] = true;
+
+ success = setState(printJob, state3);
+ assertEquals(isStateTransitionAllowed(state2, state3), success);
+ if (!success) {
+ return;
+ }
+ checkState(printJob, state3);
+
+ knownFailures[state2][state3] = false;
}, testCaseNum);
testCaseNum++;
@@ -462,70 +357,61 @@
}
public void testBlockWithReason() throws Exception {
- baseTest(new PrintJobTestFn() {
- @Override
- public void onPrintJobQueued(PrintJob printJob) throws Exception {
- printJob.start();
- checkState(printJob, PrintJobInfo.STATE_STARTED);
+ baseTest(printJob -> {
+ printJob.start();
+ checkState(printJob, PrintJobInfo.STATE_STARTED);
- printJob.setStatus(R.string.testStr1);
- eventually(() -> assertEquals(getActivity().getString(R.string.testStr1),
- printJob.getInfo().getStatus(getActivity().getPackageManager())));
+ printJob.setStatus(R.string.testStr1);
+ eventually(() -> assertEquals(getActivity().getString(R.string.testStr1),
+ printJob.getInfo().getStatus(getActivity().getPackageManager())));
- boolean success = printJob.block("test reason");
- assertTrue(success);
- checkState(printJob, PrintJobInfo.STATE_BLOCKED);
- eventually(() -> assertEquals("test reason",
- printJob.getInfo().getStatus(getActivity().getPackageManager())));
+ boolean success = printJob.block("test reason");
+ assertTrue(success);
+ checkState(printJob, PrintJobInfo.STATE_BLOCKED);
+ eventually(() -> assertEquals("test reason",
+ printJob.getInfo().getStatus(getActivity().getPackageManager())));
- success = printJob.block("another reason");
- assertFalse(success);
- checkState(printJob, PrintJobInfo.STATE_BLOCKED);
- eventually(() -> assertEquals("test reason",
- printJob.getInfo().getStatus(getActivity().getPackageManager())));
+ success = printJob.block("another reason");
+ assertFalse(success);
+ checkState(printJob, PrintJobInfo.STATE_BLOCKED);
+ eventually(() -> assertEquals("test reason",
+ printJob.getInfo().getStatus(getActivity().getPackageManager())));
- printJob.setStatus(R.string.testStr2);
- eventually(() -> assertEquals(getActivity().getString(R.string.testStr2),
- printJob.getInfo().getStatus(getActivity().getPackageManager())));
- }
+ printJob.setStatus(R.string.testStr2);
+ eventually(() -> assertEquals(getActivity().getString(R.string.testStr2),
+ printJob.getInfo().getStatus(getActivity().getPackageManager())));
}, 0);
}
public void testFailWithReason() throws Exception {
- baseTest(new PrintJobTestFn() {
- @Override
- public void onPrintJobQueued(PrintJob printJob) throws Exception {
- printJob.start();
- checkState(printJob, PrintJobInfo.STATE_STARTED);
+ baseTest(printJob -> {
+ printJob.start();
+ checkState(printJob, PrintJobInfo.STATE_STARTED);
- boolean success = printJob.fail("test reason");
- assertTrue(success);
- checkState(printJob, PrintJobInfo.STATE_FAILED);
- eventually(() -> assertEquals("test reason",
- printJob.getInfo().getStatus(getActivity().getPackageManager())));
+ boolean success = printJob.fail("test reason");
+ assertTrue(success);
+ checkState(printJob, PrintJobInfo.STATE_FAILED);
+ eventually(() -> assertEquals("test reason",
+ printJob.getInfo().getStatus(getActivity().getPackageManager())));
- success = printJob.fail("another reason");
- assertFalse(success);
- checkState(printJob, PrintJobInfo.STATE_FAILED);
- eventually(() -> assertEquals("test reason",
- printJob.getInfo().getStatus(getActivity().getPackageManager())));
- }
+ success = printJob.fail("another reason");
+ assertFalse(success);
+ checkState(printJob, PrintJobInfo.STATE_FAILED);
+ eventually(() -> assertEquals("test reason",
+ printJob.getInfo().getStatus(getActivity().getPackageManager())));
}, 0);
}
public void testTag() throws Exception {
- baseTest(new PrintJobTestFn() {
- @Override
- public void onPrintJobQueued(PrintJob printJob) throws Exception {
- // Default value should be null
- assertNull(printJob.getTag());
+ baseTest(printJob -> {
+ // Default value should be null
+ assertNull(printJob.getTag());
- printJob.setTag("testTag");
- eventually(() -> assertEquals("testTag", printJob.getTag()));
+ printJob.setTag("testTag");
+ eventually(() -> assertEquals("testTag", printJob.getTag()));
- printJob.setTag(null);
- eventually(() -> assertNull(printJob.getTag()));
- }
+ printJob.setTag(null);
+ eventually(() -> assertNull(printJob.getTag()));
}, 0);
}
@@ -542,54 +428,47 @@
// Create the service callbacks for the first print service.
PrintServiceCallbacks serviceCallbacks = createFirstMockPrinterServiceCallbacks(
- sessionCallbacks, new PrintJobTestFn() {
- @Override
- public void onPrintJobQueued(PrintJob printJob) throws Exception {
- assertTrue(printJob.hasAdvancedOption(VALID_STRING_KEY));
- assertEquals(STRING_VALUE, printJob.getAdvancedStringOption(VALID_STRING_KEY));
+ sessionCallbacks, printJob -> {
+ assertTrue(printJob.hasAdvancedOption(VALID_STRING_KEY));
+ assertEquals(STRING_VALUE, printJob.getAdvancedStringOption(VALID_STRING_KEY));
- assertFalse(printJob.hasAdvancedOption(INVALID_STRING_KEY));
- assertNull(printJob.getAdvancedStringOption(INVALID_STRING_KEY));
+ assertFalse(printJob.hasAdvancedOption(INVALID_STRING_KEY));
+ assertNull(printJob.getAdvancedStringOption(INVALID_STRING_KEY));
- assertTrue(printJob.hasAdvancedOption(VALID_INT_KEY));
- assertEquals(INT_VALUE, printJob.getAdvancedIntOption(VALID_INT_KEY));
+ assertTrue(printJob.hasAdvancedOption(VALID_INT_KEY));
+ assertEquals(INT_VALUE, printJob.getAdvancedIntOption(VALID_INT_KEY));
- assertTrue(printJob.hasAdvancedOption(VALID_NULL_KEY));
- assertNull(printJob.getAdvancedStringOption(VALID_NULL_KEY));
+ assertTrue(printJob.hasAdvancedOption(VALID_NULL_KEY));
+ assertNull(printJob.getAdvancedStringOption(VALID_NULL_KEY));
- assertFalse(printJob.hasAdvancedOption(INVALID_INT_KEY));
- assertEquals(0, printJob.getAdvancedIntOption(INVALID_INT_KEY));
+ assertFalse(printJob.hasAdvancedOption(INVALID_INT_KEY));
+ assertEquals(0, printJob.getAdvancedIntOption(INVALID_INT_KEY));
- assertNull(printJob.getAdvancedStringOption(VALID_INT_KEY));
- assertEquals(0, printJob.getAdvancedIntOption(VALID_STRING_KEY));
- }
+ assertNull(printJob.getAdvancedStringOption(VALID_INT_KEY));
+ assertEquals(0, printJob.getAdvancedIntOption(VALID_STRING_KEY));
});
CustomPrintOptionsActivity.setCallBack(
- new CustomPrintOptionsActivity.CustomPrintOptionsCallback() {
- @Override
- public PrintJobInfo executeCustomPrintOptionsActivity(
- PrintJobInfo printJob, PrinterInfo printer) {
- PrintJobInfo.Builder printJobBuilder = new PrintJobInfo.Builder(printJob);
+ (printJob, printer) -> {
+ PrintJobInfo.Builder printJobBuilder = new PrintJobInfo.Builder(printJob);
- try {
- printJobBuilder.putAdvancedOption(null, STRING_VALUE);
- throw new RuntimeException("Should not be able to use a null key");
- } catch (NullPointerException e) {
- // expected
- }
-
- // Second put overrides the first
- printJobBuilder.putAdvancedOption(VALID_STRING_KEY, "something");
- printJobBuilder.putAdvancedOption(VALID_STRING_KEY, STRING_VALUE);
-
- printJobBuilder.putAdvancedOption(VALID_INT_KEY, "something");
- printJobBuilder.putAdvancedOption(VALID_INT_KEY, INT_VALUE);
-
- printJobBuilder.putAdvancedOption(VALID_NULL_KEY, null);
-
- return printJobBuilder.build();
+ try {
+ printJobBuilder.putAdvancedOption(null, STRING_VALUE);
+ throw new RuntimeException("Should not be able to use a null key");
+ } catch (NullPointerException e) {
+ // expected
}
+
+ // Second put overrides the first
+ printJobBuilder.putAdvancedOption(VALID_STRING_KEY, "something");
+ printJobBuilder.putAdvancedOption(VALID_STRING_KEY, STRING_VALUE);
+
+ printJobBuilder.putAdvancedOption(VALID_INT_KEY, "something");
+ printJobBuilder.putAdvancedOption(VALID_INT_KEY, INT_VALUE);
+
+ printJobBuilder.putAdvancedOption(VALID_NULL_KEY, null);
+
+ return printJobBuilder.build();
});
// Configure the print services.
@@ -599,7 +478,7 @@
SecondPrintService.setCallbacks(createMockPrintServiceCallbacks(null, null, null));
// Create a print adapter that respects the print contract.
- PrintDocumentAdapter adapter = createMockPrintDocumentAdapter();
+ PrintDocumentAdapter adapter = createDefaultPrintDocumentAdapter(1);
// Start printing.
print(adapter);
@@ -622,57 +501,51 @@
}
public void testOther() throws Exception {
- baseTest(new PrintJobTestFn() {
- @Override
- public void onPrintJobQueued(PrintJob printJob) throws Exception {
- assertNotNull(printJob.getDocument());
- assertNotNull(printJob.getId());
- }
+ baseTest(printJob -> {
+ assertNotNull(printJob.getDocument());
+ assertNotNull(printJob.getId());
}, 0);
}
public void testSetStatus() throws Exception {
- baseTest(new PrintJobTestFn() {
- @Override
- public void onPrintJobQueued(PrintJob printJob) throws Exception {
- printJob.start();
+ baseTest(printJob -> {
+ printJob.start();
- printJob.setStatus(R.string.testStr1);
- eventually(() -> assertEquals(getActivity().getString(R.string.testStr1),
- printJob.getInfo().getStatus(getActivity().getPackageManager())));
+ printJob.setStatus(R.string.testStr1);
+ eventually(() -> assertEquals(getActivity().getString(R.string.testStr1),
+ printJob.getInfo().getStatus(getActivity().getPackageManager())));
- printJob.setStatus("testStr3");
- eventually(() -> assertEquals("testStr3",
- printJob.getInfo().getStatus(getActivity().getPackageManager())));
+ printJob.setStatus("testStr3");
+ eventually(() -> assertEquals("testStr3",
+ printJob.getInfo().getStatus(getActivity().getPackageManager())));
- printJob.setStatus(R.string.testStr2);
- eventually(() -> assertEquals(getActivity().getString(R.string.testStr2),
- printJob.getInfo().getStatus(getActivity().getPackageManager())));
+ printJob.setStatus(R.string.testStr2);
+ eventually(() -> assertEquals(getActivity().getString(R.string.testStr2),
+ printJob.getInfo().getStatus(getActivity().getPackageManager())));
- printJob.setStatus(null);
- eventually(() -> assertNull(
- printJob.getInfo().getStatus(getActivity().getPackageManager())));
+ printJob.setStatus(null);
+ eventually(() -> assertNull(
+ printJob.getInfo().getStatus(getActivity().getPackageManager())));
- printJob.block("testStr4");
- eventually(() -> assertEquals("testStr4",
- printJob.getInfo().getStatus(getActivity().getPackageManager())));
+ printJob.block("testStr4");
+ eventually(() -> assertEquals("testStr4",
+ printJob.getInfo().getStatus(getActivity().getPackageManager())));
- printJob.setStatus(R.string.testStr2);
- eventually(() -> assertEquals(getActivity().getString(R.string.testStr2),
- printJob.getInfo().getStatus(getActivity().getPackageManager())));
+ printJob.setStatus(R.string.testStr2);
+ eventually(() -> assertEquals(getActivity().getString(R.string.testStr2),
+ printJob.getInfo().getStatus(getActivity().getPackageManager())));
- printJob.setStatus(0);
- eventually(() -> assertNull(
- printJob.getInfo().getStatus(getActivity().getPackageManager())));
+ printJob.setStatus(0);
+ eventually(() -> assertNull(
+ printJob.getInfo().getStatus(getActivity().getPackageManager())));
- printJob.setStatus("testStr3");
- eventually(() -> assertEquals("testStr3",
- printJob.getInfo().getStatus(getActivity().getPackageManager())));
+ printJob.setStatus("testStr3");
+ eventually(() -> assertEquals("testStr3",
+ printJob.getInfo().getStatus(getActivity().getPackageManager())));
- printJob.setStatus(-1);
- eventually(() -> assertNull(
- printJob.getInfo().getStatus(getActivity().getPackageManager())));
- }
+ printJob.setStatus(-1);
+ eventually(() -> assertNull(
+ printJob.getInfo().getStatus(getActivity().getPackageManager())));
}, 0);
}
}
diff --git a/tests/tests/print/src/android/print/cts/PrintServicesTest.java b/tests/tests/print/src/android/print/cts/PrintServicesTest.java
index ccb1f07..ddfcd05 100644
--- a/tests/tests/print/src/android/print/cts/PrintServicesTest.java
+++ b/tests/tests/print/src/android/print/cts/PrintServicesTest.java
@@ -19,6 +19,7 @@
import android.app.Activity;
import android.app.PendingIntent;
import android.content.ComponentName;
+import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
@@ -28,19 +29,12 @@
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.ParcelFileDescriptor;
-import android.os.SystemClock;
-import android.print.PageRange;
import android.print.PrintAttributes;
import android.print.PrintAttributes.Margins;
import android.print.PrintAttributes.MediaSize;
import android.print.PrintAttributes.Resolution;
import android.print.PrintDocumentAdapter;
-import android.print.PrintDocumentAdapter.LayoutResultCallback;
-import android.print.PrintDocumentAdapter.WriteResultCallback;
-import android.print.PrintDocumentInfo;
+import android.print.PrintManager;
import android.print.PrinterCapabilitiesInfo;
import android.print.PrinterId;
import android.print.PrinterInfo;
@@ -48,6 +42,7 @@
import android.print.cts.services.PrintServiceCallbacks;
import android.print.cts.services.PrinterDiscoverySessionCallbacks;
import android.print.cts.services.SecondPrintService;
+import android.print.cts.services.StubbablePrintService;
import android.print.cts.services.StubbablePrinterDiscoverySession;
import android.printservice.CustomPrinterIconCallback;
import android.printservice.PrintJob;
@@ -55,288 +50,162 @@
import android.support.test.uiautomator.UiObject;
import android.support.test.uiautomator.UiSelector;
-import junit.framework.AssertionFailedError;
-
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
-
import java.util.ArrayList;
-import java.util.Date;
import java.util.List;
-import java.util.concurrent.TimeoutException;
+
+import static android.print.cts.Utils.assertException;
+import static android.print.cts.Utils.eventually;
+import static android.print.cts.Utils.getPrintJob;
+import static android.print.cts.Utils.runOnMainThread;
/**
* Test the interface from a print service to the print manager
*/
public class PrintServicesTest extends BasePrintTest {
private static final String PRINTER_NAME = "Test printer";
- private static final int NUM_PAGES = 2;
/** The print job processed in the test */
- private static PrintJob mPrintJob;
-
- /** The current progress of #mPrintJob once read from the system */
- private static float mPrintProgress;
+ private static PrintJob sPrintJob;
/** Printer under test */
- private static PrinterInfo mPrinter;
-
- /** The printer discovery session used in this test */
- private static StubbablePrinterDiscoverySession mDiscoverySession;
-
- /** The current status of #mPrintJob once read from the system */
- private static CharSequence mPrintStatus;
+ private static PrinterInfo sPrinter;
/** The custom printer icon to use */
private Icon mIcon;
/**
- * Create a mock {@link PrintDocumentAdapter} that provides {@link #NUM_PAGES} empty pages.
- *
- * @return The mock adapter
- */
- private PrintDocumentAdapter createMockPrintDocumentAdapter() {
- final PrintAttributes[] printAttributes = new PrintAttributes[1];
-
- return createMockPrintDocumentAdapter(
- new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- printAttributes[0] = (PrintAttributes) invocation.getArguments()[1];
- LayoutResultCallback callback = (LayoutResultCallback) invocation
- .getArguments()[3];
-
- PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
- .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
- .setPageCount(NUM_PAGES)
- .build();
-
- callback.onLayoutFinished(info, false);
-
- // Mark layout was called.
- onLayoutCalled();
- return null;
- }
- }, new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- Object[] args = invocation.getArguments();
- PageRange[] pages = (PageRange[]) args[0];
- ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
- WriteResultCallback callback = (WriteResultCallback) args[3];
-
- writeBlankPages(printAttributes[0], fd, pages[0].getStart(),
- pages[0].getEnd());
- fd.close();
- callback.onWriteFinished(pages);
-
- // Mark write was called.
- onWriteCalled();
- return null;
- }
- }, new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- // Mark finish was called.
- onFinishCalled();
- return null;
- }
- });
- }
-
- /**
* Create a mock {@link PrinterDiscoverySessionCallbacks} that discovers a single printer with
* minimal capabilities.
*
* @return The mock session callbacks
*/
- private PrinterDiscoverySessionCallbacks createFirstMockPrinterDiscoverySessionCallbacks() {
- return createMockPrinterDiscoverySessionCallbacks(new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) {
- // Get the session.
- mDiscoverySession = ((PrinterDiscoverySessionCallbacks) invocation.getMock())
- .getSession();
+ private PrinterDiscoverySessionCallbacks createMockPrinterDiscoverySessionCallbacks(
+ String printerName) {
+ return createMockPrinterDiscoverySessionCallbacks(invocation -> {
+ // Get the session.
+ StubbablePrinterDiscoverySession session =
+ ((PrinterDiscoverySessionCallbacks) invocation.getMock()).getSession();
- if (mDiscoverySession.getPrinters().isEmpty()) {
- List<PrinterInfo> printers = new ArrayList<PrinterInfo>();
+ if (session.getPrinters().isEmpty()) {
+ List<PrinterInfo> printers = new ArrayList<>();
- // Add the printer.
- PrinterId printerId = mDiscoverySession.getService()
- .generatePrinterId(PRINTER_NAME);
+ // Add the printer.
+ PrinterId printerId = session.getService()
+ .generatePrinterId(printerName);
- PrinterCapabilitiesInfo capabilities = new PrinterCapabilitiesInfo.Builder(
- printerId)
- .setMinMargins(new Margins(200, 200, 200, 200))
- .addMediaSize(MediaSize.ISO_A4, true)
- .addResolution(new Resolution("300x300", "300x300", 300, 300),
- true)
- .setColorModes(PrintAttributes.COLOR_MODE_COLOR,
- PrintAttributes.COLOR_MODE_COLOR)
- .build();
+ PrinterCapabilitiesInfo capabilities = new PrinterCapabilitiesInfo.Builder(
+ printerId)
+ .setMinMargins(new Margins(200, 200, 200, 200))
+ .addMediaSize(MediaSize.ISO_A4, true)
+ .addResolution(new Resolution("300x300", "300x300", 300, 300),
+ true)
+ .setColorModes(PrintAttributes.COLOR_MODE_COLOR,
+ PrintAttributes.COLOR_MODE_COLOR)
+ .build();
- Intent infoIntent = new Intent(getActivity(), Activity.class);
- PendingIntent infoPendingIntent = PendingIntent.getActivity(getActivity(), 0,
- infoIntent, PendingIntent.FLAG_IMMUTABLE);
+ Intent infoIntent = new Intent(getActivity(), Activity.class);
+ PendingIntent infoPendingIntent = PendingIntent.getActivity(getActivity(), 0,
+ infoIntent, PendingIntent.FLAG_IMMUTABLE);
- mPrinter = new PrinterInfo.Builder(printerId, PRINTER_NAME,
- PrinterInfo.STATUS_IDLE)
- .setCapabilities(capabilities)
- .setDescription("Minimal capabilities")
- .setInfoIntent(infoPendingIntent)
- .build();
- printers.add(mPrinter);
+ sPrinter = new PrinterInfo.Builder(printerId, printerName,
+ PrinterInfo.STATUS_IDLE)
+ .setCapabilities(capabilities)
+ .setDescription("Minimal capabilities")
+ .setInfoIntent(infoPendingIntent)
+ .build();
+ printers.add(sPrinter);
- mDiscoverySession.addPrinters(printers);
- }
- return null;
+ session.addPrinters(printers);
}
- }, null, null, new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- return null;
- }
- }, new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- CustomPrinterIconCallback callback = (CustomPrinterIconCallback) invocation
- .getArguments()[2];
- if (mIcon != null) {
- callback.onCustomPrinterIconLoaded(mIcon);
- }
- return null;
+ onPrinterDiscoverySessionCreateCalled();
+
+ return null;
+ }, null, null, invocation -> null, invocation -> {
+ CustomPrinterIconCallback callback = (CustomPrinterIconCallback) invocation
+ .getArguments()[2];
+
+ if (mIcon != null) {
+ callback.onCustomPrinterIconLoaded(mIcon);
}
- }, null, new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- // Take a note onDestroy was called.
- onPrinterDiscoverySessionDestroyCalled();
- return null;
- }
+ return null;
+ }, null, invocation -> {
+ // Take a note onDestroy was called.
+ onPrinterDiscoverySessionDestroyCalled();
+ return null;
});
}
/**
- * Get the current progress of #mPrintJob
+ * Get the current progress of #sPrintJob
*
* @return The current progress
+ *
* @throws InterruptedException If the thread was interrupted while setting the progress
+ * @throws Throwable If anything is unexpected.
*/
- private float getProgress() throws InterruptedException {
- final PrintServicesTest synchronizer = PrintServicesTest.this;
+ private float getProgress() throws Throwable {
+ float[] printProgress = new float[1];
+ runOnMainThread(() -> printProgress[0] = sPrintJob.getInfo().getProgress());
- synchronized (synchronizer) {
- Runnable getter = new Runnable() {
- @Override
- public void run() {
- synchronized (synchronizer) {
- mPrintProgress = mPrintJob.getInfo().getProgress();
-
- synchronizer.notify();
- }
- }
- };
-
- (new Handler(Looper.getMainLooper())).post(getter);
-
- synchronizer.wait();
- }
-
- return mPrintProgress;
+ return printProgress[0];
}
/**
- * Get the current status of #mPrintJob
+ * Get the current status of #sPrintJob
*
* @return The current status
+ *
* @throws InterruptedException If the thread was interrupted while getting the status
+ * @throws Throwable If anything is unexpected.
*/
- private CharSequence getStatus() throws InterruptedException {
- final PrintServicesTest synchronizer = PrintServicesTest.this;
+ private CharSequence getStatus() throws Throwable {
+ CharSequence[] printStatus = new CharSequence[1];
+ runOnMainThread(() -> printStatus[0] = sPrintJob.getInfo().getStatus(getActivity()
+ .getPackageManager()));
- synchronized (synchronizer) {
- Runnable getter = new Runnable() {
- @Override
- public void run() {
- synchronized (synchronizer) {
- mPrintStatus = mPrintJob.getInfo()
- .getStatus(getActivity().getPackageManager());
-
- synchronizer.notify();
- }
- }
- };
-
- (new Handler(Looper.getMainLooper())).post(getter);
-
- synchronizer.wait();
- }
-
- return mPrintStatus;
+ return printStatus[0];
}
/**
* Check if a print progress is correct.
*
* @param desiredProgress The expected @{link PrintProgresses}
- * @throws Exception If anything goes wrong or this takes more than 5 seconds
+ *
+ * @throws Throwable If anything goes wrong or this takes more than 5 seconds
*/
- private void checkNotification(float desiredProgress,
- CharSequence desiredStatus) throws Exception {
- final long TIMEOUT = 5000;
- final Date start = new Date();
-
- while ((new Date()).getTime() - start.getTime() < TIMEOUT) {
- if (desiredProgress == getProgress()
- && desiredStatus.toString().equals(getStatus().toString())) {
- return;
- }
-
- Thread.sleep(200);
- }
-
- throw new TimeoutException("Progress or status not updated in " + TIMEOUT + " ms");
+ private void checkNotification(float desiredProgress, CharSequence desiredStatus)
+ throws Throwable {
+ eventually(() -> assertEquals(desiredProgress, getProgress()));
+ eventually(() -> assertEquals(desiredStatus.toString(), getStatus().toString()));
}
/**
- * Set a new progress and status for #mPrintJob
+ * Set a new progress and status for #sPrintJob
*
* @param progress The new progress to set
- * @param status The new status to set
+ * @param status The new status to set
+ *
* @throws InterruptedException If the thread was interrupted while setting
+ * @throws Throwable If anything is unexpected.
*/
private void setProgressAndStatus(final float progress, final CharSequence status)
- throws InterruptedException {
- final PrintServicesTest synchronizer = PrintServicesTest.this;
-
- synchronized (synchronizer) {
- Runnable completer = new Runnable() {
- @Override
- public void run() {
- synchronized (synchronizer) {
- mPrintJob.setProgress(progress);
- mPrintJob.setStatus(status);
-
- synchronizer.notify();
- }
- }
- };
-
- (new Handler(Looper.getMainLooper())).post(completer);
-
- synchronizer.wait();
- }
+ throws Throwable {
+ runOnMainThread(() -> {
+ sPrintJob.setProgress(progress);
+ sPrintJob.setStatus(status);
+ });
}
/**
* Progress print job and check the print job state.
*
* @param progress How much to progress
- * @param status The status to set
- * @throws Exception If anything goes wrong.
+ * @param status The status to set
+ *
+ * @throws Throwable If anything goes wrong.
*/
- private void progress(float progress, CharSequence status) throws Exception {
+ private void progress(float progress, CharSequence status) throws Throwable {
setProgressAndStatus(progress, status);
// Check that progress of job is correct
@@ -348,43 +217,39 @@
*
* @param sessionCallbacks The callbacks of the sessopm
*/
- private PrintServiceCallbacks createFirstMockPrinterServiceCallbacks(
+ private PrintServiceCallbacks createMockPrinterServiceCallbacks(
final PrinterDiscoverySessionCallbacks sessionCallbacks) {
return createMockPrintServiceCallbacks(
- new Answer<PrinterDiscoverySessionCallbacks>() {
- @Override
- public PrinterDiscoverySessionCallbacks answer(InvocationOnMock invocation) {
- return sessionCallbacks;
- }
- },
- new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) {
- mPrintJob = (PrintJob) invocation.getArguments()[0];
- mPrintJob.start();
- onPrintJobQueuedCalled();
+ invocation -> sessionCallbacks,
+ invocation -> {
+ sPrintJob = (PrintJob) invocation.getArguments()[0];
+ sPrintJob.start();
+ onPrintJobQueuedCalled();
- return null;
- }
- }, null);
+ return null;
+ }, invocation -> {
+ sPrintJob = (PrintJob) invocation.getArguments()[0];
+ sPrintJob.cancel();
+
+ return null;
+ });
}
/**
* Test that the progress and status is propagated correctly.
*
- * @throws Exception If anything is unexpected.
+ * @throws Throwable If anything is unexpected.
*/
- public void testProgress()
- throws Exception {
+ public void testProgress() throws Throwable {
if (!supportsPrinting()) {
return;
}
// Create the session callbacks that we will be checking.
PrinterDiscoverySessionCallbacks sessionCallbacks
- = createFirstMockPrinterDiscoverySessionCallbacks();
+ = createMockPrinterDiscoverySessionCallbacks(PRINTER_NAME);
// Create the service callbacks for the first print service.
- PrintServiceCallbacks serviceCallbacks = createFirstMockPrinterServiceCallbacks(
+ PrintServiceCallbacks serviceCallbacks = createMockPrinterServiceCallbacks(
sessionCallbacks);
// Configure the print services.
@@ -394,7 +259,7 @@
SecondPrintService.setCallbacks(createMockPrintServiceCallbacks(null, null, null));
// Create a print adapter that respects the print contract.
- PrintDocumentAdapter adapter = createMockPrintDocumentAdapter();
+ PrintDocumentAdapter adapter = createDefaultPrintDocumentAdapter(1);
// Start printing.
print(adapter);
@@ -411,7 +276,7 @@
// Answer the dialog for the print service cloud warning
answerPrintServicesWarning(true);
- // Wait until the print job is queued and #mPrintJob is set
+ // Wait until the print job is queued and #sPrintJob is set
waitForServiceOnPrintJobQueuedCallbackCalled(1);
// Progress print job and check for appropriate notifications
@@ -420,16 +285,7 @@
progress(1, "printed 100");
// Call complete from the main thread
- Handler handler = new Handler(Looper.getMainLooper());
-
- Runnable completer = new Runnable() {
- @Override
- public void run() {
- mPrintJob.complete();
- }
- };
-
- handler.post(completer);
+ runOnMainThread(sPrintJob::complete);
// Wait for all print jobs to be handled after which the session destroyed.
waitForPrinterDiscoverySessionDestroyCallbackCalled(1);
@@ -439,6 +295,7 @@
* Render a {@link Drawable} into a {@link Bitmap}.
*
* @param d the drawable to be rendered
+ *
* @return the rendered bitmap
*/
private static Bitmap renderDrawable(Drawable d) {
@@ -455,34 +312,22 @@
/**
* Update the printer
*
+ * @param sessionCallbacks The callbacks for the service the printer belongs to
* @param printer the new printer to use
+ *
* @throws InterruptedException If we were interrupted while the printer was updated.
+ * @throws Throwable If anything is unexpected.
*/
- private void updatePrinter(final PrinterInfo printer)
- throws InterruptedException {
- final PrintServicesTest synchronizer = PrintServicesTest.this;
-
- synchronized (synchronizer) {
- Runnable updated = new Runnable() {
- @Override
- public void run() {
- synchronized (synchronizer) {
- ArrayList<PrinterInfo> printers = new ArrayList<>(1);
- printers.add(printer);
- mDiscoverySession.addPrinters(printers);
-
- synchronizer.notifyAll();
- }
- }
- };
-
- (new Handler(Looper.getMainLooper())).post(updated);
-
- synchronizer.wait();
- }
+ private void updatePrinter(PrinterDiscoverySessionCallbacks sessionCallbacks,
+ final PrinterInfo printer) throws Throwable {
+ runOnMainThread(() -> {
+ ArrayList<PrinterInfo> printers = new ArrayList<>(1);
+ printers.add(printer);
+ sessionCallbacks.getSession().addPrinters(printers);
+ });
// Update local copy of printer
- mPrinter = printer;
+ sPrinter = printer;
}
/**
@@ -490,46 +335,29 @@
* we try up to 5 seconds.
*
* @param bitmap The bitmap to match
+ *
+ * @throws Throwable If anything is unexpected.
*/
- private void assertThatIconIs(Bitmap bitmap) {
- final long TIMEOUT = 5000;
-
- final long startMillis = SystemClock.uptimeMillis();
- while (true) {
- try {
- if (bitmap.sameAs(renderDrawable(mPrinter.loadIcon(getActivity())))) {
- return;
- }
- final long elapsedMillis = SystemClock.uptimeMillis() - startMillis;
- final long waitMillis = TIMEOUT - elapsedMillis;
- if (waitMillis <= 0) {
- throw new AssertionFailedError("Icon does not match bitmap");
- }
-
- // We do not get notified about the icon update, hence wait and try again.
- Thread.sleep(1000);
- } catch (InterruptedException ie) {
- /* ignore */
- }
- }
+ private void assertThatIconIs(Bitmap bitmap) throws Throwable {
+ eventually(
+ () -> assertTrue(bitmap.sameAs(renderDrawable(sPrinter.loadIcon(getActivity())))));
}
/**
* Test that the icon get be updated.
*
- * @throws Exception If anything is unexpected.
+ * @throws Throwable If anything is unexpected.
*/
- public void testUpdateIcon()
- throws Exception {
+ public void testUpdateIcon() throws Throwable {
if (!supportsPrinting()) {
return;
}
// Create the session callbacks that we will be checking.
final PrinterDiscoverySessionCallbacks sessionCallbacks
- = createFirstMockPrinterDiscoverySessionCallbacks();
+ = createMockPrinterDiscoverySessionCallbacks(PRINTER_NAME);
// Create the service callbacks for the first print service.
- PrintServiceCallbacks serviceCallbacks = createFirstMockPrinterServiceCallbacks(
+ PrintServiceCallbacks serviceCallbacks = createMockPrinterServiceCallbacks(
sessionCallbacks);
// Configure the print services.
@@ -539,7 +367,7 @@
SecondPrintService.setCallbacks(createMockPrintServiceCallbacks(null, null, null));
// Create a print adapter that respects the print contract.
- PrintDocumentAdapter adapter = createMockPrintDocumentAdapter();
+ PrintDocumentAdapter adapter = createDefaultPrintDocumentAdapter(1);
// Start printing.
print(adapter);
@@ -560,18 +388,154 @@
assertThatIconIs(renderDrawable(printServiceIcon));
// Update icon to resource
- updatePrinter((new PrinterInfo.Builder(mPrinter)).setIconResourceId(R.drawable.red_printer)
+ updatePrinter(sessionCallbacks,
+ (new PrinterInfo.Builder(sPrinter)).setIconResourceId(R.drawable.red_printer)
.build());
assertThatIconIs(renderDrawable(getActivity().getDrawable(R.drawable.red_printer)));
// Update icon to bitmap
Bitmap bm = BitmapFactory.decodeResource(getActivity().getResources(),
- R.raw.yellow_printer);
+ R.raw.yellow);
// Icon will be picked up from the discovery session once setHasCustomPrinterIcon is set
mIcon = Icon.createWithBitmap(bm);
- updatePrinter((new PrinterInfo.Builder(mPrinter)).setHasCustomPrinterIcon(true).build());
+ updatePrinter(sessionCallbacks,
+ (new PrinterInfo.Builder(sPrinter)).setHasCustomPrinterIcon(true).build());
assertThatIconIs(renderDrawable(mIcon.loadDrawable(getActivity())));
}
+
+ /**
+ * Test that we cannot call attachBaseContext
+ *
+ * @throws Throwable If anything is unexpected.
+ */
+ public void testCannotUseAttachBaseContext() throws Throwable {
+ if (!supportsPrinting()) {
+ return;
+ }
+ // Create the session callbacks that we will be checking.
+ final PrinterDiscoverySessionCallbacks sessionCallbacks
+ = createMockPrinterDiscoverySessionCallbacks(PRINTER_NAME);
+
+ // Create the service callbacks for the first print service.
+ PrintServiceCallbacks serviceCallbacks = createMockPrinterServiceCallbacks(
+ sessionCallbacks);
+
+ // Configure the print services.
+ FirstPrintService.setCallbacks(serviceCallbacks);
+
+ // Create a print adapter that respects the print contract.
+ PrintDocumentAdapter adapter = createDefaultPrintDocumentAdapter(1);
+
+ // We don't use the second service, but we have to still configure it
+ SecondPrintService.setCallbacks(createMockPrintServiceCallbacks(null, null, null));
+
+ // Start printing to set serviceCallbacks.getService()
+ print(adapter);
+ eventually(() -> assertNotNull(serviceCallbacks.getService()));
+
+ // attachBaseContext should always throw an exception no matter what input value
+ assertException(() -> serviceCallbacks.getService().callAttachBaseContext(null),
+ IllegalStateException.class);
+ assertException(() -> serviceCallbacks.getService().callAttachBaseContext(getActivity()),
+ IllegalStateException.class);
+ }
+
+ /**
+ * Test that the active print jobs can be read
+ *
+ * @throws Throwable If anything is unexpected.
+ */
+ public void testGetActivePrintJobs() throws Throwable {
+ if (!supportsPrinting()) {
+ return;
+ }
+
+ PrintManager pm = (PrintManager) getActivity().getSystemService(Context.PRINT_SERVICE);
+
+ // Configure first print service
+ PrinterDiscoverySessionCallbacks sessionCallbacks1
+ = createMockPrinterDiscoverySessionCallbacks("Printer1");
+ PrintServiceCallbacks serviceCallbacks1 = createMockPrinterServiceCallbacks(
+ sessionCallbacks1);
+ FirstPrintService.setCallbacks(serviceCallbacks1);
+
+ // Configure second print service
+ PrinterDiscoverySessionCallbacks sessionCallbacks2
+ = createMockPrinterDiscoverySessionCallbacks("Printer2");
+ PrintServiceCallbacks serviceCallbacks2 = createMockPrinterServiceCallbacks(
+ sessionCallbacks2);
+ SecondPrintService.setCallbacks(serviceCallbacks2);
+
+ // Create a print adapter that respects the print contract.
+ PrintDocumentAdapter adapter = createDefaultPrintDocumentAdapter(1);
+
+ runOnMainThread(() -> pm.print("job1", adapter, null));
+
+ // Init services
+ waitForPrinterDiscoverySessionCreateCallbackCalled();
+ StubbablePrintService firstService = serviceCallbacks1.getService();
+
+ waitForWriteAdapterCallback(1);
+ selectPrinter("Printer1");
+
+ // Job is not yet confirmed, hence it is not yet "active"
+ runOnMainThread(() -> assertEquals(0, firstService.callGetActivePrintJobs().size()));
+
+ clickPrintButton();
+ answerPrintServicesWarning(true);
+ onPrintJobQueuedCalled();
+
+ eventually(() -> runOnMainThread(
+ () -> assertEquals(1, firstService.callGetActivePrintJobs().size())));
+
+ // Add another print job to first service
+ resetCounters();
+ runOnMainThread(() -> pm.print("job2", adapter, null));
+ waitForWriteAdapterCallback(1);
+ clickPrintButton();
+ onPrintJobQueuedCalled();
+
+ eventually(() -> runOnMainThread(
+ () -> assertEquals(2, firstService.callGetActivePrintJobs().size())));
+
+ // Create print job in second service
+ resetCounters();
+ runOnMainThread(() -> pm.print("job3", adapter, null));
+
+ waitForPrinterDiscoverySessionCreateCallbackCalled();
+
+ StubbablePrintService secondService = serviceCallbacks2.getService();
+ runOnMainThread(() -> assertEquals(0, secondService.callGetActivePrintJobs().size()));
+
+ waitForWriteAdapterCallback(1);
+ selectPrinter("Printer2");
+ clickPrintButton();
+ answerPrintServicesWarning(true);
+ onPrintJobQueuedCalled();
+
+ eventually(() -> runOnMainThread(
+ () -> assertEquals(1, secondService.callGetActivePrintJobs().size())));
+ runOnMainThread(() -> assertEquals(2, firstService.callGetActivePrintJobs().size()));
+
+ // Block last print job. Blocked jobs are still considered active
+ runOnMainThread(() -> sPrintJob.block(null));
+ eventually(() -> runOnMainThread(() -> assertTrue(sPrintJob.isBlocked())));
+ runOnMainThread(() -> assertEquals(1, secondService.callGetActivePrintJobs().size()));
+
+ // Fail last print job. Failed job are not active
+ runOnMainThread(() -> sPrintJob.fail(null));
+ eventually(() -> runOnMainThread(() -> assertTrue(sPrintJob.isFailed())));
+ runOnMainThread(() -> assertEquals(0, secondService.callGetActivePrintJobs().size()));
+
+ // Cancel job. Canceled jobs are not active
+ runOnMainThread(() -> assertEquals(2, firstService.callGetActivePrintJobs().size()));
+ android.print.PrintJob job2 = getPrintJob(pm, "job2");
+ runOnMainThread(job2::cancel);
+ eventually(() -> runOnMainThread(() -> assertTrue(job2.isCancelled())));
+ runOnMainThread(() -> assertEquals(1, firstService.callGetActivePrintJobs().size()));
+
+ waitForPrinterDiscoverySessionDestroyCallbackCalled(1);
+ }
}
diff --git a/tests/tests/print/src/android/print/cts/PrinterCapabilitiesTest.java b/tests/tests/print/src/android/print/cts/PrinterCapabilitiesTest.java
index 8e3f984..ec8676a 100644
--- a/tests/tests/print/src/android/print/cts/PrinterCapabilitiesTest.java
+++ b/tests/tests/print/src/android/print/cts/PrinterCapabilitiesTest.java
@@ -37,8 +37,6 @@
import android.util.Log;
import android.support.test.uiautomator.UiObject;
import android.support.test.uiautomator.UiSelector;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
import java.util.ArrayList;
import java.util.List;
@@ -46,6 +44,8 @@
import java.util.function.Consumer;
import java.util.function.Function;
+import static android.print.cts.Utils.assertException;
+
/**
* This test verifies changes to the printer capabilities are applied correctly.
*/
@@ -133,14 +133,10 @@
*/
private void changeCapabilities(final StubbablePrinterDiscoverySession session,
final PrinterId printerId, final MediaSize mediaSize, final boolean isAvailable) {
- getInstrumentation().runOnMainSync(new Runnable() {
- @Override
- public void run() {
- session.addPrinters(generatePrinters(printerId, mediaSize, isAvailable ?
+ getInstrumentation().runOnMainSync(
+ () -> session.addPrinters(generatePrinters(printerId, mediaSize, isAvailable ?
PrinterInfo.STATUS_IDLE :
- PrinterInfo.STATUS_UNAVAILABLE));
- }
- });
+ PrinterInfo.STATUS_UNAVAILABLE)));
}
/**
@@ -149,7 +145,8 @@
* @throws Exception If anything was unexpected.
*/
private void waitForPrinterUnavailable() throws Exception {
- final String PRINTER_UNAVAILABLE_MSG = "This printer isn't available right now.";
+ final String PRINTER_UNAVAILABLE_MSG =
+ getPrintSpoolerString("print_error_printer_unavailable");
UiObject message = getUiDevice().findObject(new UiSelector().resourceId(
"com.android.printspooler:id/message"));
@@ -186,47 +183,40 @@
final PrintAttributes[] writeAttributes = new PrintAttributes[1];
PrintDocumentAdapter adapter = createMockPrintDocumentAdapter(
- new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- LayoutResultCallback callback = (LayoutResultCallback) invocation
- .getArguments()[3];
- PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
- .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
- .setPageCount(1)
- .build();
+ invocation -> {
+ LayoutResultCallback callback = (LayoutResultCallback) invocation
+ .getArguments()[3];
+ PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
+ .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
+ .setPageCount(1)
+ .build();
- synchronized (PrinterCapabilitiesTest.this) {
- layoutAttributes[0] = (PrintAttributes) invocation.getArguments()[1];
+ synchronized (PrinterCapabilitiesTest.this) {
+ layoutAttributes[0] = (PrintAttributes) invocation.getArguments()[1];
- PrinterCapabilitiesTest.this.notify();
- }
-
- callback.onLayoutFinished(info, true);
- return null;
+ PrinterCapabilitiesTest.this.notify();
}
- },
- new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- Object[] args = invocation.getArguments();
- PageRange[] pages = (PageRange[]) args[0];
- ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
- WriteResultCallback callback = (WriteResultCallback) args[3];
- writeBlankPages(layoutAttributes[0], fd, pages[0].getStart(),
- pages[0].getEnd());
- fd.close();
+ callback.onLayoutFinished(info, true);
+ return null;
+ }, invocation -> {
+ Object[] args = invocation.getArguments();
+ PageRange[] pages = (PageRange[]) args[0];
+ ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
+ WriteResultCallback callback = (WriteResultCallback) args[3];
- synchronized (PrinterCapabilitiesTest.this) {
- writeAttributes[0] = layoutAttributes[0];
+ writeBlankPages(layoutAttributes[0], fd, pages[0].getStart(),
+ pages[0].getEnd());
+ fd.close();
- PrinterCapabilitiesTest.this.notify();
- }
+ synchronized (PrinterCapabilitiesTest.this) {
+ writeAttributes[0] = layoutAttributes[0];
- callback.onWriteFinished(pages);
- return null;
+ PrinterCapabilitiesTest.this.notify();
}
+
+ callback.onWriteFinished(pages);
+ return null;
}, null);
// Start printing.
@@ -275,34 +265,23 @@
// Create the session[0] callbacks that we will be checking.
final PrinterDiscoverySessionCallbacks firstSessionCallbacks =
- createMockPrinterDiscoverySessionCallbacks(new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) {
- session[0] = ((PrinterDiscoverySessionCallbacks) invocation.getMock())
- .getSession();
+ createMockPrinterDiscoverySessionCallbacks(invocation -> {
+ session[0] = ((PrinterDiscoverySessionCallbacks) invocation.getMock())
+ .getSession();
- printerId[0] = session[0].getService().generatePrinterId(PRINTER_NAME);
+ printerId[0] = session[0].getService().generatePrinterId(PRINTER_NAME);
- session[0].addPrinters(generatePrinters(printerId[0], MediaSize.NA_LETTER,
- PrinterInfo.STATUS_IDLE));
- return null;
- }
- }, null, null, null, null, null, new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) {
- onPrinterDiscoverySessionDestroyCalled();
- return null;
- }
+ session[0].addPrinters(generatePrinters(printerId[0], MediaSize.NA_LETTER,
+ PrinterInfo.STATUS_IDLE));
+ return null;
+ }, null, null, null, null, null, invocation -> {
+ onPrinterDiscoverySessionDestroyCalled();
+ return null;
});
// Create the service callbacks for the first print service.
PrintServiceCallbacks firstServiceCallbacks = createMockPrintServiceCallbacks(
- new Answer<PrinterDiscoverySessionCallbacks>() {
- @Override
- public PrinterDiscoverySessionCallbacks answer(InvocationOnMock invocation) {
- return firstSessionCallbacks;
- }
- }, null, null);
+ invocation -> firstSessionCallbacks, null, null);
// Configure the print services.
FirstPrintService.setCallbacks(firstServiceCallbacks);
@@ -335,27 +314,6 @@
}
/**
- * Run a runnable and expect and exception of a certain type.
- *
- * @param r The runnable to run
- * @param expectedClass The expected exception type
- */
- private void assertException(Runnable r, Class<? extends RuntimeException> expectedClass) {
- try {
- r.run();
- } catch (Exception e) {
- if (e.getClass().isAssignableFrom(expectedClass)) {
- return;
- } else {
- throw new AssertionError("Expected: " + expectedClass.getName() + ", got: "
- + e.getClass().getName());
- }
- }
-
- throw new AssertionError("No exception thrown");
- }
-
- /**
* That that you cannot create illegal PrinterCapabilityInfos.
*
* @throws Exception If anything is unexpected
@@ -366,105 +324,94 @@
}
final PrinterDiscoverySessionCallbacks firstSessionCallbacks =
- createMockPrinterDiscoverySessionCallbacks(new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) {
- StubbablePrinterDiscoverySession session =
- ((PrinterDiscoverySessionCallbacks)
- invocation.getMock()).getSession();
+ createMockPrinterDiscoverySessionCallbacks(invocation -> {
+ StubbablePrinterDiscoverySession session =
+ ((PrinterDiscoverySessionCallbacks)
+ invocation.getMock()).getSession();
- PrinterId printerId = session.getService().generatePrinterId(PRINTER_NAME);
+ PrinterId printerId = session.getService().generatePrinterId(PRINTER_NAME);
- // printerId need to be set
- assertException(() -> new PrinterCapabilitiesInfo.Builder(null),
- IllegalArgumentException.class);
+ // printerId need to be set
+ assertException(() -> new PrinterCapabilitiesInfo.Builder(null),
+ IllegalArgumentException.class);
- // All capability fields (beside duplex) need to be initialized:
- // Test no color
- assertException(() ->
- (new PrinterCapabilitiesInfo.Builder(printerId))
- .setMinMargins(DEFAULT_MARGINS)
- .addMediaSize(MediaSize.ISO_A4, true)
- .addResolution(RESOLUTION_300, true).build(),
- IllegalStateException.class);
- // Test bad colors
- assertException(() ->
- (new PrinterCapabilitiesInfo.Builder(printerId))
- .setColorModes(0xffff,
- PrintAttributes.COLOR_MODE_MONOCHROME),
- IllegalArgumentException.class);
- // Test bad duplex mode
- assertException(() ->
- (new PrinterCapabilitiesInfo.Builder(printerId))
- .setDuplexModes(0xffff,
- PrintAttributes.DUPLEX_MODE_NONE),
- IllegalArgumentException.class);
- // Test no mediasize
- assertException(() ->
- (new PrinterCapabilitiesInfo.Builder(printerId))
- .setColorModes(PrintAttributes.COLOR_MODE_COLOR,
- PrintAttributes.COLOR_MODE_COLOR)
- .setMinMargins(DEFAULT_MARGINS)
- .addResolution(RESOLUTION_300, true).build(),
- IllegalStateException.class);
- // Test no default mediasize
- assertException(() ->
- (new PrinterCapabilitiesInfo.Builder(printerId))
- .setColorModes(PrintAttributes.COLOR_MODE_COLOR,
- PrintAttributes.COLOR_MODE_COLOR)
- .setMinMargins(DEFAULT_MARGINS)
- .addMediaSize(MediaSize.ISO_A4, false)
- .addResolution(RESOLUTION_300, true).build(),
- IllegalStateException.class);
- // Test two default mediasizes
- assertException(() ->
- (new PrinterCapabilitiesInfo.Builder(printerId))
- .addMediaSize(MediaSize.ISO_A4, true)
- .addMediaSize(MediaSize.ISO_A5, true),
- IllegalArgumentException.class);
- // Test no resolution
- assertException(() ->
- (new PrinterCapabilitiesInfo.Builder(printerId))
- .setColorModes(PrintAttributes.COLOR_MODE_COLOR,
- PrintAttributes.COLOR_MODE_COLOR)
- .setMinMargins(DEFAULT_MARGINS)
- .addMediaSize(MediaSize.ISO_A4, true).build(),
- IllegalStateException.class);
- // Test no default resolution
- assertException(() ->
- (new PrinterCapabilitiesInfo.Builder(printerId))
- .setColorModes(PrintAttributes.COLOR_MODE_COLOR,
- PrintAttributes.COLOR_MODE_COLOR)
- .setMinMargins(DEFAULT_MARGINS)
- .addMediaSize(MediaSize.ISO_A4, true)
- .addResolution(RESOLUTION_300, false).build(),
- IllegalStateException.class);
- // Test two default resolutions
- assertException(() ->
- (new PrinterCapabilitiesInfo.Builder(printerId))
- .addResolution(RESOLUTION_300, true)
- .addResolution(RESOLUTION_600, true),
- IllegalArgumentException.class);
+ // All capability fields (beside duplex) need to be initialized:
+ // Test no color
+ assertException(() ->
+ (new PrinterCapabilitiesInfo.Builder(printerId))
+ .setMinMargins(DEFAULT_MARGINS)
+ .addMediaSize(MediaSize.ISO_A4, true)
+ .addResolution(RESOLUTION_300, true).build(),
+ IllegalStateException.class);
+ // Test bad colors
+ assertException(() ->
+ (new PrinterCapabilitiesInfo.Builder(printerId))
+ .setColorModes(0xffff,
+ PrintAttributes.COLOR_MODE_MONOCHROME),
+ IllegalArgumentException.class);
+ // Test bad duplex mode
+ assertException(() ->
+ (new PrinterCapabilitiesInfo.Builder(printerId))
+ .setDuplexModes(0xffff,
+ PrintAttributes.DUPLEX_MODE_NONE),
+ IllegalArgumentException.class);
+ // Test no mediasize
+ assertException(() ->
+ (new PrinterCapabilitiesInfo.Builder(printerId))
+ .setColorModes(PrintAttributes.COLOR_MODE_COLOR,
+ PrintAttributes.COLOR_MODE_COLOR)
+ .setMinMargins(DEFAULT_MARGINS)
+ .addResolution(RESOLUTION_300, true).build(),
+ IllegalStateException.class);
+ // Test no default mediasize
+ assertException(() ->
+ (new PrinterCapabilitiesInfo.Builder(printerId))
+ .setColorModes(PrintAttributes.COLOR_MODE_COLOR,
+ PrintAttributes.COLOR_MODE_COLOR)
+ .setMinMargins(DEFAULT_MARGINS)
+ .addMediaSize(MediaSize.ISO_A4, false)
+ .addResolution(RESOLUTION_300, true).build(),
+ IllegalStateException.class);
+ // Test two default mediasizes
+ assertException(() ->
+ (new PrinterCapabilitiesInfo.Builder(printerId))
+ .addMediaSize(MediaSize.ISO_A4, true)
+ .addMediaSize(MediaSize.ISO_A5, true),
+ IllegalArgumentException.class);
+ // Test no resolution
+ assertException(() ->
+ (new PrinterCapabilitiesInfo.Builder(printerId))
+ .setColorModes(PrintAttributes.COLOR_MODE_COLOR,
+ PrintAttributes.COLOR_MODE_COLOR)
+ .setMinMargins(DEFAULT_MARGINS)
+ .addMediaSize(MediaSize.ISO_A4, true).build(),
+ IllegalStateException.class);
+ // Test no default resolution
+ assertException(() ->
+ (new PrinterCapabilitiesInfo.Builder(printerId))
+ .setColorModes(PrintAttributes.COLOR_MODE_COLOR,
+ PrintAttributes.COLOR_MODE_COLOR)
+ .setMinMargins(DEFAULT_MARGINS)
+ .addMediaSize(MediaSize.ISO_A4, true)
+ .addResolution(RESOLUTION_300, false).build(),
+ IllegalStateException.class);
+ // Test two default resolutions
+ assertException(() ->
+ (new PrinterCapabilitiesInfo.Builder(printerId))
+ .addResolution(RESOLUTION_300, true)
+ .addResolution(RESOLUTION_600, true),
+ IllegalArgumentException.class);
- onPrinterDiscoverySessionCreateCalled();
- return null;
- }
- }, null, null, null, null, null, new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) {
- onPrinterDiscoverySessionDestroyCalled();
- return null;
- }
+ onPrinterDiscoverySessionCreateCalled();
+ return null;
+ }, null, null, null, null, null, invocation -> {
+ onPrinterDiscoverySessionDestroyCalled();
+ return null;
});
// Create the service callbacks for the first print service.
PrintServiceCallbacks firstServiceCallbacks = createMockPrintServiceCallbacks(
- new Answer<PrinterDiscoverySessionCallbacks>() {
- @Override
- public PrinterDiscoverySessionCallbacks answer(InvocationOnMock invocation) {
- return firstSessionCallbacks;
- }
- }, null, null);
+ invocation -> firstSessionCallbacks, null, null);
// Configure the print services.
FirstPrintService.setCallbacks(firstServiceCallbacks);
@@ -493,91 +440,114 @@
}
final PrinterDiscoverySessionCallbacks firstSessionCallbacks =
- createMockPrinterDiscoverySessionCallbacks(new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) {
- StubbablePrinterDiscoverySession session =
- ((PrinterDiscoverySessionCallbacks)
- invocation.getMock()).getSession();
+ createMockPrinterDiscoverySessionCallbacks(invocation -> {
+ StubbablePrinterDiscoverySession session =
+ ((PrinterDiscoverySessionCallbacks)
+ invocation.getMock()).getSession();
- MediaSize[] mediaSizes = {MediaSize.ISO_A0, MediaSize.ISO_A0,
- MediaSize.ISO_A1};
- Resolution[] resolutions = {RESOLUTION_300, RESOLUTION_300,
- RESOLUTION_600};
- int[] colorModes = {PrintAttributes.COLOR_MODE_MONOCHROME,
- PrintAttributes.COLOR_MODE_COLOR};
- int[] duplexModes = {PrintAttributes.DUPLEX_MODE_NONE,
- PrintAttributes.DUPLEX_MODE_LONG_EDGE,
- PrintAttributes.DUPLEX_MODE_SHORT_EDGE};
+ MediaSize[] mediaSizes = {MediaSize.ISO_A0, MediaSize.ISO_A0,
+ MediaSize.ISO_A1};
+ Resolution[] resolutions = {RESOLUTION_300, RESOLUTION_300,
+ RESOLUTION_600};
+ int[] colorModes = {PrintAttributes.COLOR_MODE_MONOCHROME,
+ PrintAttributes.COLOR_MODE_COLOR};
+ int[] duplexModes = {PrintAttributes.DUPLEX_MODE_NONE,
+ PrintAttributes.DUPLEX_MODE_LONG_EDGE,
+ PrintAttributes.DUPLEX_MODE_SHORT_EDGE};
- ArrayList<PrinterInfo> printers = new ArrayList<>();
- for (int mediaSizeIndex = 1; mediaSizeIndex < mediaSizes.length;
- mediaSizeIndex++) {
- for (int resolutionIndex = 1; resolutionIndex < mediaSizes.length;
- resolutionIndex++) {
- for (int colorIndex = 1; colorIndex < colorModes.length;
- colorIndex++) {
- for (int duplexIndex = 1; duplexIndex < duplexModes.length;
- duplexIndex++) {
- PrinterId printerId = session.getService()
- .generatePrinterId(Integer.valueOf(printers.size())
- .toString());
+ ArrayList<PrinterInfo> printers = new ArrayList<>();
+ for (int mediaSizeIndex = 1; mediaSizeIndex < mediaSizes.length;
+ mediaSizeIndex++) {
+ for (int resolutionIndex = 1; resolutionIndex < mediaSizes.length;
+ resolutionIndex++) {
+ for (int colorIndex = 1; colorIndex < colorModes.length;
+ colorIndex++) {
+ for (int duplexIndex = 1; duplexIndex < duplexModes.length;
+ duplexIndex++) {
+ PrinterId printerId = session.getService()
+ .generatePrinterId(Integer.valueOf(printers.size())
+ .toString());
- PrinterCapabilitiesInfo.Builder b =
- new PrinterCapabilitiesInfo.Builder(printerId);
+ // Setup capabilities
+ PrinterCapabilitiesInfo.Builder b =
+ new PrinterCapabilitiesInfo.Builder(printerId);
- for (int i = 0; i < mediaSizeIndex; i++) {
- b.addMediaSize(mediaSizes[i], i == mediaSizeIndex - 1);
- }
-
- for (int i = 0; i < resolutionIndex; i++) {
- b.addResolution(resolutions[i],
- i == resolutionIndex - 1);
- }
-
- int allColors = 0;
- for (int i = 0; i < colorIndex; i++) {
- allColors |= colorModes[i];
- }
- b.setColorModes(allColors, colorModes[colorIndex - 1]);
-
- int allDuplexModes = 0;
- for (int i = 0; i < duplexIndex; i++) {
- allDuplexModes |= duplexModes[i];
- }
- b.setDuplexModes(allDuplexModes,
- duplexModes[duplexIndex - 1]);
-
- printers.add((new PrinterInfo.Builder(printerId,
- Integer.valueOf(printers.size()).toString(),
- PrinterInfo.STATUS_IDLE)).setCapabilities(b.build())
- .build());
+ for (int i = 0; i < mediaSizeIndex; i++) {
+ b.addMediaSize(mediaSizes[i], i == mediaSizeIndex - 1);
}
+
+ for (int i = 0; i < resolutionIndex; i++) {
+ b.addResolution(resolutions[i],
+ i == resolutionIndex - 1);
+ }
+
+ int allColors = 0;
+ for (int i = 0; i < colorIndex; i++) {
+ allColors |= colorModes[i];
+ }
+ b.setColorModes(allColors, colorModes[colorIndex - 1]);
+
+ int allDuplexModes = 0;
+ for (int i = 0; i < duplexIndex; i++) {
+ allDuplexModes |= duplexModes[i];
+ }
+ b.setDuplexModes(allDuplexModes,
+ duplexModes[duplexIndex - 1]);
+
+ b.setMinMargins(DEFAULT_MARGINS);
+
+ // Create printer
+ PrinterInfo printer = (new PrinterInfo.Builder(printerId,
+ Integer.valueOf(printers.size()).toString(),
+ PrinterInfo.STATUS_IDLE)).setCapabilities(b.build())
+ .build();
+
+ // Verify capabilities
+ PrinterCapabilitiesInfo cap = printer.getCapabilities();
+
+ assertEquals(mediaSizeIndex, cap.getMediaSizes().size());
+ assertEquals(mediaSizes[mediaSizeIndex - 1],
+ cap.getDefaults().getMediaSize());
+ for (int i = 0; i < mediaSizeIndex; i++) {
+ assertTrue(cap.getMediaSizes().contains(mediaSizes[i]));
+ }
+
+ assertEquals(resolutionIndex, cap.getResolutions().size());
+ assertEquals(resolutions[resolutionIndex - 1],
+ cap.getDefaults().getResolution());
+ for (int i = 0; i < resolutionIndex; i++) {
+ assertTrue(cap.getResolutions().contains(resolutions[i]));
+ }
+
+ assertEquals(allColors, cap.getColorModes());
+ assertEquals(colorModes[colorIndex - 1],
+ cap.getDefaults().getColorMode());
+
+ assertEquals(allDuplexModes, cap.getDuplexModes());
+ assertEquals(duplexModes[duplexIndex - 1],
+ cap.getDefaults().getDuplexMode());
+
+ assertEquals(DEFAULT_MARGINS, cap.getMinMargins());
+
+ // Add printer
+ printers.add(printer);
}
}
}
-
- session.addPrinters(printers);
-
- onPrinterDiscoverySessionCreateCalled();
- return null;
}
- }, null, null, null, null, null, new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) {
- onPrinterDiscoverySessionDestroyCalled();
- return null;
- }
+
+ session.addPrinters(printers);
+
+ onPrinterDiscoverySessionCreateCalled();
+ return null;
+ }, null, null, null, null, null, invocation -> {
+ onPrinterDiscoverySessionDestroyCalled();
+ return null;
});
// Create the service callbacks for the first print service.
PrintServiceCallbacks firstServiceCallbacks = createMockPrintServiceCallbacks(
- new Answer<PrinterDiscoverySessionCallbacks>() {
- @Override
- public PrinterDiscoverySessionCallbacks answer(InvocationOnMock invocation) {
- return firstSessionCallbacks;
- }
- }, null, null);
+ invocation -> firstSessionCallbacks, null, null);
// Configure the print services.
FirstPrintService.setCallbacks(firstServiceCallbacks);
@@ -608,42 +578,31 @@
}
final PrinterDiscoverySessionCallbacks firstSessionCallbacks =
- createMockPrinterDiscoverySessionCallbacks(new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) {
- StubbablePrinterDiscoverySession session =
- ((PrinterDiscoverySessionCallbacks)
- invocation.getMock()).getSession();
+ createMockPrinterDiscoverySessionCallbacks(invocation -> {
+ StubbablePrinterDiscoverySession session =
+ ((PrinterDiscoverySessionCallbacks)
+ invocation.getMock()).getSession();
- PrinterId printerId = session.getService()
- .generatePrinterId(PRINTER_NAME);
+ PrinterId printerId = session.getService()
+ .generatePrinterId(PRINTER_NAME);
- ArrayList<PrinterInfo> printers = new ArrayList<>();
- printers.add((new PrinterInfo.Builder(printerId, PRINTER_NAME,
- PrinterInfo.STATUS_IDLE))
- .setCapabilities(capBuilder.apply(printerId)).build());
+ ArrayList<PrinterInfo> printers = new ArrayList<>();
+ printers.add((new PrinterInfo.Builder(printerId, PRINTER_NAME,
+ PrinterInfo.STATUS_IDLE))
+ .setCapabilities(capBuilder.apply(printerId)).build());
- session.addPrinters(printers);
+ session.addPrinters(printers);
- onPrinterDiscoverySessionCreateCalled();
- return null;
- }
- }, null, null, null, null, null, new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) {
- onPrinterDiscoverySessionDestroyCalled();
- return null;
- }
+ onPrinterDiscoverySessionCreateCalled();
+ return null;
+ }, null, null, null, null, null, invocation -> {
+ onPrinterDiscoverySessionDestroyCalled();
+ return null;
});
// Create the service callbacks for the first print service.
PrintServiceCallbacks firstServiceCallbacks = createMockPrintServiceCallbacks(
- new Answer<PrinterDiscoverySessionCallbacks>() {
- @Override
- public PrinterDiscoverySessionCallbacks answer(InvocationOnMock invocation) {
- return firstSessionCallbacks;
- }
- }, null, null);
+ invocation -> firstSessionCallbacks, null, null);
// Configure the print services.
FirstPrintService.setCallbacks(firstServiceCallbacks);
@@ -652,35 +611,29 @@
final PrintAttributes[] layoutAttributes = new PrintAttributes[1];
PrintDocumentAdapter adapter = createMockPrintDocumentAdapter(
- new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- LayoutResultCallback callback = (LayoutResultCallback) invocation
- .getArguments()[3];
- PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
- .setPageCount(1)
- .build();
- layoutAttributes[0] = (PrintAttributes) invocation.getArguments()[1];
+ invocation -> {
+ LayoutResultCallback callback = (LayoutResultCallback) invocation
+ .getArguments()[3];
+ PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
+ .setPageCount(1)
+ .build();
+ layoutAttributes[0] = (PrintAttributes) invocation.getArguments()[1];
- callback.onLayoutFinished(info, true);
- return null;
- }
+ callback.onLayoutFinished(info, true);
+ return null;
},
- new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- Object[] args = invocation.getArguments();
- PageRange[] pages = (PageRange[]) args[0];
- ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
- WriteResultCallback callback = (WriteResultCallback) args[3];
+ invocation -> {
+ Object[] args = invocation.getArguments();
+ PageRange[] pages = (PageRange[]) args[0];
+ ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
+ WriteResultCallback callback = (WriteResultCallback) args[3];
- writeBlankPages(layoutAttributes[0], fd, pages[0].getStart(),
- pages[0].getEnd());
- fd.close();
+ writeBlankPages(layoutAttributes[0], fd, pages[0].getStart(),
+ pages[0].getEnd());
+ fd.close();
- callback.onWriteFinished(pages);
- return null;
- }
+ callback.onWriteFinished(pages);
+ return null;
}, null);
// Start printing.
diff --git a/tests/tests/print/src/android/print/cts/PrinterDiscoverySessionLifecycleTest.java b/tests/tests/print/src/android/print/cts/PrinterDiscoverySessionLifecycleTest.java
index defbee3..3830770 100644
--- a/tests/tests/print/src/android/print/cts/PrinterDiscoverySessionLifecycleTest.java
+++ b/tests/tests/print/src/android/print/cts/PrinterDiscoverySessionLifecycleTest.java
@@ -16,18 +16,15 @@
package android.print.cts;
+import static android.print.cts.Utils.eventually;
+import static android.print.cts.Utils.runOnMainThread;
import static org.mockito.Mockito.inOrder;
-import android.os.ParcelFileDescriptor;
-import android.print.PageRange;
import android.print.PrintAttributes;
import android.print.PrintAttributes.Margins;
import android.print.PrintAttributes.MediaSize;
import android.print.PrintAttributes.Resolution;
import android.print.PrintDocumentAdapter;
-import android.print.PrintDocumentAdapter.LayoutResultCallback;
-import android.print.PrintDocumentAdapter.WriteResultCallback;
-import android.print.PrintDocumentInfo;
import android.print.PrinterCapabilitiesInfo;
import android.print.PrinterId;
import android.print.PrinterInfo;
@@ -41,8 +38,6 @@
import org.mockito.InOrder;
import org.mockito.exceptions.verification.VerificationInOrderFailure;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
import java.util.ArrayList;
import java.util.Collections;
@@ -59,7 +54,9 @@
private static final String FIRST_PRINTER_LOCAL_ID= "first_printer";
private static final String SECOND_PRINTER_LOCAL_ID = "second_printer";
- public void testNormalLifecycle() throws Exception {
+ private static StubbablePrinterDiscoverySession sSession;
+
+ public void testNormalLifecycle() throws Throwable {
if (!supportsPrinting()) {
return;
}
@@ -69,28 +66,20 @@
// Create the service callbacks for the first print service.
PrintServiceCallbacks firstServiceCallbacks = createMockPrintServiceCallbacks(
- new Answer<PrinterDiscoverySessionCallbacks>() {
- @Override
- public PrinterDiscoverySessionCallbacks answer(InvocationOnMock invocation) {
- return firstSessionCallbacks;
- }
- },
- new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) {
+ invocation -> firstSessionCallbacks,
+ invocation -> {
PrintJob printJob = (PrintJob) invocation.getArguments()[0];
// We pretend the job is handled immediately.
printJob.complete();
return null;
- }
- }, null);
+ }, null);
// Configure the print services.
FirstPrintService.setCallbacks(firstServiceCallbacks);
SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks());
// Create a print adapter that respects the print contract.
- PrintDocumentAdapter adapter = createMockPrintDocumentAdapter();
+ PrintDocumentAdapter adapter = createDefaultPrintDocumentAdapter(1);
// Start printing.
print(adapter);
@@ -98,9 +87,17 @@
// Wait for write of the first page.
waitForWriteAdapterCallback(1);
+ runOnMainThread(() -> assertFalse(sSession.isDestroyed()));
+ runOnMainThread(() -> assertEquals(0, sSession.getTrackedPrinters().size()));
+
// Select the first printer.
selectPrinter(FIRST_PRINTER_NAME);
+ eventually(() -> runOnMainThread(() -> assertEquals(FIRST_PRINTER_LOCAL_ID,
+ sSession.getTrackedPrinters().get(0).getLocalId())));
+ runOnMainThread(() -> assertTrue(sSession.isPrinterDiscoveryStarted()));
+ runOnMainThread(() -> assertEquals(1, sSession.getTrackedPrinters().size()));
+
// Wait for layout as the printer has different capabilities.
waitForLayoutAdapterCallbackCount(2);
@@ -108,13 +105,17 @@
// one so no layout should happen).
selectPrinter(SECOND_PRINTER_NAME);
+ eventually(() -> runOnMainThread(() -> assertEquals(SECOND_PRINTER_LOCAL_ID,
+ sSession.getTrackedPrinters().get(0).getLocalId())));
+ runOnMainThread(() -> assertEquals(1, sSession.getTrackedPrinters().size()));
+
// While the printer discovery session is still alive store the
// ids of printers as we want to make some assertions about them
// but only the print service can create printer ids which means
// that we need to get the created ones.
- PrinterId firstPrinterId = getAddedPrinterIdForLocalId(firstSessionCallbacks,
+ PrinterId firstPrinterId = getAddedPrinterIdForLocalId(
FIRST_PRINTER_LOCAL_ID);
- PrinterId secondPrinterId = getAddedPrinterIdForLocalId(firstSessionCallbacks,
+ PrinterId secondPrinterId = getAddedPrinterIdForLocalId(
SECOND_PRINTER_LOCAL_ID);
assertNotNull("Coundn't find printer:" + FIRST_PRINTER_LOCAL_ID, firstPrinterId);
assertNotNull("Coundn't find printer:" + SECOND_PRINTER_LOCAL_ID, secondPrinterId);
@@ -128,6 +129,10 @@
// Wait for all print jobs to be handled after which the session destroyed.
waitForPrinterDiscoverySessionDestroyCallbackCalled(1);
+ runOnMainThread(() -> assertTrue(sSession.isDestroyed()));
+ runOnMainThread(() -> assertFalse(sSession.isPrinterDiscoveryStarted()));
+ runOnMainThread(() -> assertEquals(0, sSession.getTrackedPrinters().size()));
+
// Verify the expected calls.
InOrder inOrder = inOrder(firstSessionCallbacks);
@@ -159,7 +164,7 @@
inOrder.verify(firstSessionCallbacks).onDestroy();
}
- public void testCancelPrintServicesAlertDialog() throws Exception {
+ public void testCancelPrintServicesAlertDialog() throws Throwable {
if (!supportsPrinting()) {
return;
}
@@ -169,20 +174,12 @@
// Create the service callbacks for the first print service.
PrintServiceCallbacks firstServiceCallbacks = createMockPrintServiceCallbacks(
- new Answer<PrinterDiscoverySessionCallbacks>() {
- @Override
- public PrinterDiscoverySessionCallbacks answer(InvocationOnMock invocation) {
- return firstSessionCallbacks;
- }
- },
- new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) {
- PrintJob printJob = (PrintJob) invocation.getArguments()[0];
- // We pretend the job is handled immediately.
- printJob.complete();
- return null;
- }
+ invocation -> firstSessionCallbacks,
+ invocation -> {
+ PrintJob printJob = (PrintJob) invocation.getArguments()[0];
+ // We pretend the job is handled immediately.
+ printJob.complete();
+ return null;
}, null);
// Configure the print services.
@@ -190,7 +187,7 @@
SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks());
// Create a print adapter that respects the print contract.
- PrintDocumentAdapter adapter = createMockPrintDocumentAdapter();
+ PrintDocumentAdapter adapter = createDefaultPrintDocumentAdapter(1);
// Start printing.
print(adapter);
@@ -198,14 +195,22 @@
// Wait for write of the first page.
waitForWriteAdapterCallback(1);
+ runOnMainThread(() -> assertFalse(sSession.isDestroyed()));
+ runOnMainThread(() -> assertEquals(0, sSession.getTrackedPrinters().size()));
+
// Select the first printer.
selectPrinter(FIRST_PRINTER_NAME);
+ eventually(() -> runOnMainThread(() -> assertEquals(FIRST_PRINTER_LOCAL_ID,
+ sSession.getTrackedPrinters().get(0).getLocalId())));
+ runOnMainThread(() -> assertTrue(sSession.isPrinterDiscoveryStarted()));
+ runOnMainThread(() -> assertEquals(1, sSession.getTrackedPrinters().size()));
+
// While the printer discovery session is still alive store the
// ids of printers as we want to make some assertions about them
// but only the print service can create printer ids which means
// that we need to get the created ones.
- PrinterId firstPrinterId = getAddedPrinterIdForLocalId(firstSessionCallbacks,
+ PrinterId firstPrinterId = getAddedPrinterIdForLocalId(
FIRST_PRINTER_LOCAL_ID);
assertNotNull("Coundn't find printer:" + FIRST_PRINTER_LOCAL_ID, firstPrinterId);
@@ -224,6 +229,10 @@
// Wait for all print jobs to be handled after which the session destroyed.
waitForPrinterDiscoverySessionDestroyCallbackCalled(1);
+ runOnMainThread(() -> assertTrue(sSession.isDestroyed()));
+ runOnMainThread(() -> assertFalse(sSession.isPrinterDiscoveryStarted()));
+ runOnMainThread(() -> assertEquals(0, sSession.getTrackedPrinters().size()));
+
// Verify the expected calls.
InOrder inOrder = inOrder(firstSessionCallbacks);
@@ -247,7 +256,7 @@
inOrder.verify(firstSessionCallbacks).onDestroy();
}
- public void testStartPrinterDiscoveryWithHistoricalPrinters() throws Exception {
+ public void testStartPrinterDiscoveryWithHistoricalPrinters() throws Throwable {
if (!supportsPrinting()) {
return;
}
@@ -257,28 +266,20 @@
// Create the service callbacks for the first print service.
PrintServiceCallbacks firstServiceCallbacks = createMockPrintServiceCallbacks(
- new Answer<PrinterDiscoverySessionCallbacks>() {
- @Override
- public PrinterDiscoverySessionCallbacks answer(InvocationOnMock invocation) {
- return firstSessionCallbacks;
- }
- },
- new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) {
+ invocation -> firstSessionCallbacks,
+ invocation -> {
PrintJob printJob = (PrintJob) invocation.getArguments()[0];
// We pretend the job is handled immediately.
printJob.complete();
return null;
- }
- }, null);
+ }, null);
// Configure the print services.
FirstPrintService.setCallbacks(firstServiceCallbacks);
SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks());
// Create a print adapter that respects the print contract.
- PrintDocumentAdapter adapter = createMockPrintDocumentAdapter();
+ PrintDocumentAdapter adapter = createDefaultPrintDocumentAdapter(1);
// Start printing.
print(adapter);
@@ -286,9 +287,17 @@
// Wait for write of the first page.
waitForWriteAdapterCallback(1);
+ runOnMainThread(() -> assertFalse(sSession.isDestroyed()));
+ runOnMainThread(() -> assertEquals(0, sSession.getTrackedPrinters().size()));
+
// Select the first printer.
selectPrinter(FIRST_PRINTER_NAME);
+ eventually(() -> runOnMainThread(() -> assertEquals(FIRST_PRINTER_LOCAL_ID,
+ sSession.getTrackedPrinters().get(0).getLocalId())));
+ runOnMainThread(() -> assertTrue(sSession.isPrinterDiscoveryStarted()));
+ runOnMainThread(() -> assertEquals(1, sSession.getTrackedPrinters().size()));
+
// Wait for a layout to finish - first layout was for the
// PDF printer, second for the first printer in preview mode.
waitForLayoutAdapterCallbackCount(2);
@@ -298,7 +307,7 @@
// but only the print service can create printer ids which means
// that we need to get the created one.
PrinterId firstPrinterId = getAddedPrinterIdForLocalId(
- firstSessionCallbacks, FIRST_PRINTER_LOCAL_ID);
+ FIRST_PRINTER_LOCAL_ID);
// Click the print button.
clickPrintButton();
@@ -326,11 +335,15 @@
// Wait for all print jobs to be handled after which the is session destroyed.
waitForPrinterDiscoverySessionDestroyCallbackCalled(1);
+ runOnMainThread(() -> assertTrue(sSession.isDestroyed()));
+ runOnMainThread(() -> assertFalse(sSession.isPrinterDiscoveryStarted()));
+ runOnMainThread(() -> assertEquals(0, sSession.getTrackedPrinters().size()));
+
// Verify the expected calls.
InOrder inOrder = inOrder(firstSessionCallbacks);
// We start discovery with no printer history.
- List<PrinterId> priorityList = new ArrayList<PrinterId>();
+ List<PrinterId> priorityList = new ArrayList<>();
inOrder.verify(firstSessionCallbacks).onStartPrinterDiscovery(
priorityList);
@@ -371,16 +384,86 @@
inOrder.verify(firstSessionCallbacks).onDestroy();
}
- private PrinterId getAddedPrinterIdForLocalId(
- final PrinterDiscoverySessionCallbacks sessionCallbacks, String printerLocalId) {
- final List<PrinterInfo> reportedPrinters = new ArrayList<PrinterInfo>();
- getInstrumentation().runOnMainSync(new Runnable() {
- @Override
- public void run() {
- // Grab the printer ids as only the service can create such.
- StubbablePrinterDiscoverySession session = sessionCallbacks.getSession();
- reportedPrinters.addAll(session.getPrinters());
- }
+ public void testAddRemovePrinters() throws Throwable {
+ if (!supportsPrinting()) {
+ return;
+ }
+
+ StubbablePrinterDiscoverySession[] session = new StubbablePrinterDiscoverySession[1];
+
+ // Create the session callbacks that we will be checking.
+ final PrinterDiscoverySessionCallbacks firstSessionCallbacks =
+ createMockPrinterDiscoverySessionCallbacks(invocation -> {
+ session[0] = ((PrinterDiscoverySessionCallbacks)
+ invocation.getMock()).getSession();
+
+ onPrinterDiscoverySessionCreateCalled();
+ return null;
+ }, null, null, null, null, null, invocation -> {
+ onPrinterDiscoverySessionDestroyCalled();
+ return null;
+ });
+
+ // Create the service callbacks for the first print service.
+ PrintServiceCallbacks firstServiceCallbacks = createMockPrintServiceCallbacks(
+ invocation -> firstSessionCallbacks, null, null);
+
+ // Configure the print services.
+ FirstPrintService.setCallbacks(firstServiceCallbacks);
+ SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks());
+
+ print(createDefaultPrintDocumentAdapter(1));
+
+ waitForPrinterDiscoverySessionCreateCallbackCalled();
+
+ runOnMainThread(() -> assertEquals(0, session[0].getPrinters().size()));
+
+ PrinterId[] printerIds = new PrinterId[3];
+ runOnMainThread(() -> {
+ printerIds[0] = session[0].getService().generatePrinterId("0");
+ printerIds[1] = session[0].getService().generatePrinterId("1");
+ printerIds[2] = session[0].getService().generatePrinterId("2");
+ });
+
+ PrinterInfo printer1 = (new PrinterInfo.Builder(printerIds[0], "0",
+ PrinterInfo.STATUS_IDLE)).build();
+
+ PrinterInfo printer2 = (new PrinterInfo.Builder(printerIds[1], "1",
+ PrinterInfo.STATUS_IDLE)).build();
+
+ PrinterInfo printer3 = (new PrinterInfo.Builder(printerIds[2], "2",
+ PrinterInfo.STATUS_IDLE)).build();
+
+ ArrayList<PrinterInfo> printers = new ArrayList<>();
+ printers.add(printer1);
+ runOnMainThread(() -> session[0].addPrinters(printers));
+ eventually(() -> runOnMainThread(() -> assertEquals(1, session[0].getPrinters().size())));
+
+ printers.add(printer2);
+ printers.add(printer3);
+ runOnMainThread(() -> session[0].addPrinters(printers));
+ eventually(() -> runOnMainThread(() -> assertEquals(3, session[0].getPrinters().size())));
+
+ ArrayList<PrinterId> printerIdsToRemove = new ArrayList<>();
+ printerIdsToRemove.add(printer1.getId());
+ runOnMainThread(() -> session[0].removePrinters(printerIdsToRemove));
+ eventually(() -> runOnMainThread(() -> assertEquals(2, session[0].getPrinters().size())));
+
+ printerIdsToRemove.add(printer2.getId());
+ printerIdsToRemove.add(printer3.getId());
+ runOnMainThread(() -> session[0].removePrinters(printerIdsToRemove));
+ eventually(() -> runOnMainThread(() -> assertEquals(0, session[0].getPrinters().size())));
+
+ getUiDevice().pressBack();
+
+ waitForPrinterDiscoverySessionDestroyCallbackCalled(1);
+ }
+
+ private PrinterId getAddedPrinterIdForLocalId(String printerLocalId) throws Throwable {
+ final List<PrinterInfo> reportedPrinters = new ArrayList<>();
+ runOnMainThread(() -> {
+ // Grab the printer ids as only the service can create such.
+ reportedPrinters.addAll(sSession.getPrinters());
});
final int reportedPrinterCount = reportedPrinters.size();
@@ -400,133 +483,92 @@
}
private PrinterDiscoverySessionCallbacks createFirstMockPrinterDiscoverySessionCallbacks() {
- return createMockPrinterDiscoverySessionCallbacks(new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) {
- // Get the session.
- StubbablePrinterDiscoverySession session = ((PrinterDiscoverySessionCallbacks)
- invocation.getMock()).getSession();
+ return createMockPrinterDiscoverySessionCallbacks(invocation -> {
+ // Get the session.
+ sSession = ((PrinterDiscoverySessionCallbacks)
+ invocation.getMock()).getSession();
- if (session.getPrinters().isEmpty()) {
- List<PrinterInfo> printers = new ArrayList<PrinterInfo>();
+ assertTrue(sSession.isPrinterDiscoveryStarted());
- // Add the first printer.
- PrinterId firstPrinterId = session.getService().generatePrinterId(
- FIRST_PRINTER_LOCAL_ID);
- PrinterInfo firstPrinter = new PrinterInfo.Builder(firstPrinterId,
- FIRST_PRINTER_NAME, PrinterInfo.STATUS_IDLE)
- .build();
- printers.add(firstPrinter);
+ if (sSession.getPrinters().isEmpty()) {
+ List<PrinterInfo> printers = new ArrayList<>();
- // Add the first printer.
- PrinterId secondPrinterId = session.getService().generatePrinterId(
- SECOND_PRINTER_LOCAL_ID);
- PrinterInfo secondPrinter = new PrinterInfo.Builder(secondPrinterId,
- SECOND_PRINTER_NAME, PrinterInfo.STATUS_IDLE)
- .build();
- printers.add(secondPrinter);
+ // Add the first printer.
+ PrinterId firstPrinterId = sSession.getService().generatePrinterId(
+ FIRST_PRINTER_LOCAL_ID);
+ PrinterInfo firstPrinter = new PrinterInfo.Builder(firstPrinterId,
+ FIRST_PRINTER_NAME, PrinterInfo.STATUS_IDLE)
+ .build();
+ printers.add(firstPrinter);
- session.addPrinters(printers);
- }
- return null;
+ // Add the first printer.
+ PrinterId secondPrinterId = sSession.getService().generatePrinterId(
+ SECOND_PRINTER_LOCAL_ID);
+ PrinterInfo secondPrinter = new PrinterInfo.Builder(secondPrinterId,
+ SECOND_PRINTER_NAME, PrinterInfo.STATUS_IDLE)
+ .build();
+ printers.add(secondPrinter);
+
+ sSession.addPrinters(printers);
}
- }, null, null, new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- // Get the session.
- StubbablePrinterDiscoverySession session = ((PrinterDiscoverySessionCallbacks)
- invocation.getMock()).getSession();
+ return null;
+ }, invocation -> {
+ assertFalse(sSession.isPrinterDiscoveryStarted());
+ return null;
+ }, null, invocation -> {
+ // Get the session.
+ StubbablePrinterDiscoverySession session = ((PrinterDiscoverySessionCallbacks)
+ invocation.getMock()).getSession();
- PrinterId trackedPrinterId = (PrinterId) invocation.getArguments()[0];
- List<PrinterInfo> reportedPrinters = session.getPrinters();
+ PrinterId trackedPrinterId = (PrinterId) invocation.getArguments()[0];
+ List<PrinterInfo> reportedPrinters = session.getPrinters();
- // We should be tracking a printer that we added.
- PrinterInfo trackedPrinter = null;
- final int reportedPrinterCount = reportedPrinters.size();
- for (int i = 0; i < reportedPrinterCount; i++) {
- PrinterInfo reportedPrinter = reportedPrinters.get(i);
- if (reportedPrinter.getId().equals(trackedPrinterId)) {
- trackedPrinter = reportedPrinter;
- break;
- }
+ // We should be tracking a printer that we added.
+ PrinterInfo trackedPrinter = null;
+ final int reportedPrinterCount = reportedPrinters.size();
+ for (int i = 0; i < reportedPrinterCount; i++) {
+ PrinterInfo reportedPrinter = reportedPrinters.get(i);
+ if (reportedPrinter.getId().equals(trackedPrinterId)) {
+ trackedPrinter = reportedPrinter;
+ break;
}
- assertNotNull("Can track only added printers", trackedPrinter);
+ }
+ assertNotNull("Can track only added printers", trackedPrinter);
- // If the printer does not have capabilities reported add them.
- if (trackedPrinter.getCapabilities() == null) {
+ assertTrue(sSession.getTrackedPrinters().contains(trackedPrinter.getId()));
+ assertEquals(1, sSession.getTrackedPrinters().size());
- // Add the capabilities to emulate lazy discovery.
- // Same for each printer is fine for what we test.
- PrinterCapabilitiesInfo capabilities =
- new PrinterCapabilitiesInfo.Builder(trackedPrinterId)
- .setMinMargins(new Margins(200, 200, 200, 200))
- .addMediaSize(MediaSize.ISO_A4, true)
- .addMediaSize(MediaSize.ISO_A5, false)
- .addResolution(new Resolution("300x300", "300x300", 300, 300), true)
- .setColorModes(PrintAttributes.COLOR_MODE_COLOR,
- PrintAttributes.COLOR_MODE_COLOR)
- .build();
- PrinterInfo updatedPrinter = new PrinterInfo.Builder(trackedPrinter)
+ // If the printer does not have capabilities reported add them.
+ if (trackedPrinter.getCapabilities() == null) {
+
+ // Add the capabilities to emulate lazy discovery.
+ // Same for each printer is fine for what we test.
+ PrinterCapabilitiesInfo capabilities =
+ new PrinterCapabilitiesInfo.Builder(trackedPrinterId)
+ .setMinMargins(new Margins(200, 200, 200, 200))
+ .addMediaSize(MediaSize.ISO_A4, true)
+ .addMediaSize(MediaSize.ISO_A5, false)
+ .addResolution(new Resolution("300x300", "300x300", 300, 300), true)
+ .setColorModes(PrintAttributes.COLOR_MODE_COLOR,
+ PrintAttributes.COLOR_MODE_COLOR)
+ .build();
+ PrinterInfo updatedPrinter = new PrinterInfo.Builder(trackedPrinter)
.setCapabilities(capabilities)
.build();
- // Update the printer.
- List<PrinterInfo> printers = new ArrayList<PrinterInfo>();
- printers.add(updatedPrinter);
- session.addPrinters(printers);
- }
+ // Update the printer.
+ List<PrinterInfo> printers = new ArrayList<>();
+ printers.add(updatedPrinter);
+ session.addPrinters(printers);
+ }
- return null;
- }
- }, null, null, new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- // Take a note onDestroy was called.
- onPrinterDiscoverySessionDestroyCalled();
- return null;
- }
- });
- }
+ return null;
+ }, null, null, invocation -> {
+ assertTrue(sSession.isDestroyed());
- public PrintDocumentAdapter createMockPrintDocumentAdapter() {
- final PrintAttributes[] printAttributes = new PrintAttributes[1];
-
- return createMockPrintDocumentAdapter(
- new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- printAttributes[0] = (PrintAttributes) invocation.getArguments()[1];
- LayoutResultCallback callback = (LayoutResultCallback) invocation.getArguments()[3];
- PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
- .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
- .setPageCount(3)
- .build();
- callback.onLayoutFinished(info, false);
- // Mark layout was called.
- onLayoutCalled();
- return null;
- }
- }, new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- Object[] args = invocation.getArguments();
- PageRange[] pages = (PageRange[]) args[0];
- ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
- WriteResultCallback callback = (WriteResultCallback) args[3];
- writeBlankPages(printAttributes[0], fd, pages[0].getStart(), pages[0].getEnd());
- fd.close();
- callback.onWriteFinished(pages);
- // Mark write was called.
- onWriteCalled();
- return null;
- }
- }, new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- // Mark finish was called.
- onFinishCalled();
- return null;
- }
+ // Take a note onDestroy was called.
+ onPrinterDiscoverySessionDestroyCalled();
+ return null;
});
}
}
diff --git a/tests/tests/print/src/android/print/cts/PrinterInfoTest.java b/tests/tests/print/src/android/print/cts/PrinterInfoTest.java
index 72f2866..9d691d0 100644
--- a/tests/tests/print/src/android/print/cts/PrinterInfoTest.java
+++ b/tests/tests/print/src/android/print/cts/PrinterInfoTest.java
@@ -19,7 +19,6 @@
import android.app.Activity;
import android.app.PendingIntent;
import android.content.Intent;
-import android.graphics.drawable.Icon;
import android.os.ParcelFileDescriptor;
import android.print.PageRange;
import android.print.PrintAttributes;
@@ -38,14 +37,10 @@
import android.print.cts.services.PrinterDiscoverySessionCallbacks;
import android.print.cts.services.SecondPrintService;
import android.print.cts.services.StubbablePrinterDiscoverySession;
-import android.printservice.CustomPrinterIconCallback;
import android.support.test.uiautomator.UiObject;
import android.support.test.uiautomator.UiSelector;
import android.text.TextUtils;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
-
import java.util.ArrayList;
import java.util.List;
@@ -53,69 +48,10 @@
* Tests all allowed types of printerInfo
*/
public class PrinterInfoTest extends BasePrintTest {
- private static final int NUM_PAGES = 2;
-
private static final String NAMED_PRINTERS_NAME_PREFIX = "Printer ";
/** The printer discovery session used in this test */
- private static StubbablePrinterDiscoverySession mDiscoverySession;
-
- /** The custom printer icon to use */
- private Icon mIcon;
-
- /**
- * Create a mock {@link PrintDocumentAdapter} that provides {@link #NUM_PAGES} empty pages.
- *
- * @return The mock adapter
- */
- private PrintDocumentAdapter createMockPrintDocumentAdapter() {
- final PrintAttributes[] printAttributes = new PrintAttributes[1];
-
- return createMockPrintDocumentAdapter(
- new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- printAttributes[0] = (PrintAttributes) invocation.getArguments()[1];
- LayoutResultCallback callback = (LayoutResultCallback) invocation
- .getArguments()[3];
-
- PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
- .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
- .setPageCount(NUM_PAGES)
- .build();
-
- callback.onLayoutFinished(info, false);
-
- // Mark layout was called.
- onLayoutCalled();
- return null;
- }
- }, new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- Object[] args = invocation.getArguments();
- PageRange[] pages = (PageRange[]) args[0];
- ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
- WriteResultCallback callback = (WriteResultCallback) args[3];
-
- writeBlankPages(printAttributes[0], fd, pages[0].getStart(),
- pages[0].getEnd());
- fd.close();
- callback.onWriteFinished(pages);
-
- // Mark write was called.
- onWriteCalled();
- return null;
- }
- }, new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- // Mark finish was called.
- onFinishCalled();
- return null;
- }
- });
- }
+ private static StubbablePrinterDiscoverySession sDiscoverySession;
private boolean isValidStatus(int status) {
return status == PrinterInfo.STATUS_IDLE
@@ -130,222 +66,219 @@
* @return The mock session callbacks
*/
private PrinterDiscoverySessionCallbacks createFirstMockPrinterDiscoverySessionCallbacks() {
- return createMockPrinterDiscoverySessionCallbacks(new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) {
- // Get the session.
- mDiscoverySession = ((PrinterDiscoverySessionCallbacks) invocation.getMock())
- .getSession();
+ return createMockPrinterDiscoverySessionCallbacks(invocation -> {
+ // Get the session.
+ sDiscoverySession = ((PrinterDiscoverySessionCallbacks) invocation.getMock())
+ .getSession();
- if (mDiscoverySession.getPrinters().isEmpty()) {
- final int INVALID_RES_ID = 0xffffffff;
+ if (sDiscoverySession.getPrinters().isEmpty()) {
+ final int INVALID_RES_ID = 0xffffffff;
- final PrinterInfo.Builder badPrinter = new PrinterInfo.Builder(
- mDiscoverySession.getService().generatePrinterId("bad printer"),
- "badPrinter", PrinterInfo.STATUS_UNAVAILABLE);
+ final PrinterInfo.Builder badPrinter = new PrinterInfo.Builder(
+ sDiscoverySession.getService().generatePrinterId("bad printer"),
+ "badPrinter", PrinterInfo.STATUS_UNAVAILABLE);
- String[] localPrinterIds = {
- "Printer", null
- };
+ String[] localPrinterIds = {
+ "Printer", null
+ };
- String[] names = {
- NAMED_PRINTERS_NAME_PREFIX, "", null
- };
- int[] statusList = {
- PrinterInfo.STATUS_IDLE, PrinterInfo.STATUS_BUSY,
- PrinterInfo.STATUS_UNAVAILABLE, 0, 42
- };
- int[] iconResourceIds = {
- R.drawable.red_printer, 0, INVALID_RES_ID, -1
- };
+ String[] names = {
+ NAMED_PRINTERS_NAME_PREFIX, "", null
+ };
+ int[] statusList = {
+ PrinterInfo.STATUS_IDLE, PrinterInfo.STATUS_BUSY,
+ PrinterInfo.STATUS_UNAVAILABLE, 0, 42
+ };
+ int[] iconResourceIds = {
+ R.drawable.red_printer, 0, INVALID_RES_ID, -1
+ };
- boolean[] hasCustomPrinterIcons = {
- true, false
- };
+ boolean[] hasCustomPrinterIcons = {
+ true, false
+ };
- String[] descriptions = {
- "Description", "", null
- };
+ String[] descriptions = {
+ "Description", "", null
+ };
- PendingIntent[] infoIntents = {
- PendingIntent.getActivity(getActivity(), 0,
- new Intent(getActivity(), Activity.class),
- PendingIntent.FLAG_IMMUTABLE),
- null
- };
+ PendingIntent[] infoIntents = {
+ PendingIntent.getActivity(getActivity(), 0,
+ new Intent(getActivity(), Activity.class),
+ PendingIntent.FLAG_IMMUTABLE),
+ null
+ };
- PrinterCapabilitiesInfo[] capabilityList = {
- // The printerId not used in PrinterCapabilitiesInfo
- new PrinterCapabilitiesInfo.Builder(mDiscoverySession.getService()
- .generatePrinterId("unused"))
- .setMinMargins(new Margins(200, 200, 200, 200))
- .addMediaSize(MediaSize.ISO_A4, true)
- .addResolution(
- new Resolution("300x300", "300x300", 300, 300),
- true)
- .setColorModes(PrintAttributes.COLOR_MODE_COLOR,
- PrintAttributes.COLOR_MODE_COLOR)
- .build(),
- null
- };
+ PrinterCapabilitiesInfo[] capabilityList = {
+ // The printerId not used in PrinterCapabilitiesInfo
+ new PrinterCapabilitiesInfo.Builder(sDiscoverySession.getService()
+ .generatePrinterId("unused"))
+ .setMinMargins(new Margins(200, 200, 200, 200))
+ .addMediaSize(MediaSize.ISO_A4, true)
+ .addResolution(
+ new Resolution("300x300", "300x300", 300, 300),
+ true)
+ .setColorModes(PrintAttributes.COLOR_MODE_COLOR,
+ PrintAttributes.COLOR_MODE_COLOR)
+ .build(),
+ null
+ };
- List<PrinterInfo> printers = new ArrayList<PrinterInfo>();
- for (String localPrinterId : localPrinterIds) {
- for (String name : names) {
- for (int status : statusList) {
- for (int iconResourceId : iconResourceIds) {
- for (boolean hasCustomPrinterIcon : hasCustomPrinterIcons) {
- for (String description : descriptions) {
- for (PendingIntent infoIntent : infoIntents) {
- for (PrinterCapabilitiesInfo capabilities
- : capabilityList) {
- // printerId
- RuntimeException e = null;
- PrinterId printerId = null;
- try {
- if (localPrinterId == null) {
- printerId = mDiscoverySession
- .getService()
- .generatePrinterId(
- localPrinterId);
- } else {
- printerId = mDiscoverySession
- .getService()
- .generatePrinterId(
- localPrinterId
- + printers
- .size());
- }
- } catch (RuntimeException ex) {
- e = ex;
- }
-
- // Expect exception if localId is null
+ List<PrinterInfo> printers = new ArrayList<>();
+ for (String localPrinterId : localPrinterIds) {
+ for (String name : names) {
+ for (int status : statusList) {
+ for (int iconResourceId : iconResourceIds) {
+ for (boolean hasCustomPrinterIcon : hasCustomPrinterIcons) {
+ for (String description : descriptions) {
+ for (PendingIntent infoIntent : infoIntents) {
+ for (PrinterCapabilitiesInfo capabilities
+ : capabilityList) {
+ // printerId
+ RuntimeException e = null;
+ PrinterId printerId = null;
+ try {
if (localPrinterId == null) {
- if (e == null) {
- throw new IllegalStateException();
- }
- } else if (e != null) {
- throw e;
+ printerId = sDiscoverySession
+ .getService()
+ .generatePrinterId(
+ localPrinterId);
+ } else {
+ printerId = sDiscoverySession
+ .getService()
+ .generatePrinterId(
+ localPrinterId
+ + printers
+ .size());
}
+ } catch (RuntimeException ex) {
+ e = ex;
+ }
- // Constructor
- PrinterInfo.Builder b = null;
- e = null;
- try {
- b = new PrinterInfo.Builder(
- printerId, name, status);
- } catch (RuntimeException ex) {
- e = ex;
- }
-
- // Expect exception if any of the parameters was
- // bad
- if (printerId == null
- || TextUtils.isEmpty(name)
- || !isValidStatus(status)) {
- if (e == null) {
- throw new IllegalStateException();
- } else {
- b = badPrinter;
- }
- } else if (e != null) {
- throw e;
- }
-
- // IconResourceId
- e = null;
- try {
- b.setIconResourceId(iconResourceId);
- } catch (RuntimeException ex) {
- e = ex;
- }
-
- // Expect exception if iconResourceId was bad
- // We do allow invalid Ids as the printerInfo
- // might be created after the package of the
- // printer is already gone if the printer is a
- // historical printer.
- if (iconResourceId < 0) {
- if (e == null) {
- throw new IllegalStateException();
- } else {
- b = badPrinter;
- }
- } else if (e != null) {
- throw e;
- }
-
- // Status
- e = null;
- try {
- b.setStatus(status);
- } catch (RuntimeException ex) {
- e = ex;
- }
-
- // Expect exception is status is not valid
- if (!isValidStatus(status)) {
- if (e == null) {
- throw new IllegalStateException();
- } else {
- b = badPrinter;
- }
- } else if (e != null) {
- throw e;
- }
-
- // Name
- e = null;
- try {
- b.setName(name);
- } catch (RuntimeException ex) {
- e = ex;
- }
-
- // Expect exception if name is empty
- if (TextUtils.isEmpty(name)) {
- if (e == null) {
- throw new IllegalStateException();
- } else {
- b = badPrinter;
- }
- } else if (e != null) {
- throw e;
- }
-
- // hasCustomPrinterIcon
- b.setHasCustomPrinterIcon(hasCustomPrinterIcon);
-
- // Description
- b.setDescription(description);
-
- // InfoIntent
- b.setInfoIntent(infoIntent);
-
- // Capabilities
- b.setCapabilities(capabilities);
-
- PrinterInfo printer = b.build();
-
- // Don't create bad printers
- if (b == badPrinter) {
- continue;
- }
-
- // Verify get* methods
- if (printer.getId() != printerId
- || printer.getName() != name
- || printer.getStatus() != status
- || printer
- .getDescription() != description
- || printer.getCapabilities()
- != capabilities) {
+ // Expect exception if localId is null
+ if (localPrinterId == null) {
+ if (e == null) {
throw new IllegalStateException();
}
-
- printers.add(printer);
+ } else if (e != null) {
+ throw e;
}
+
+ // Constructor
+ PrinterInfo.Builder b = null;
+ e = null;
+ try {
+ b = new PrinterInfo.Builder(
+ printerId, name, status);
+ } catch (RuntimeException ex) {
+ e = ex;
+ }
+
+ // Expect exception if any of the parameters was
+ // bad
+ if (printerId == null
+ || TextUtils.isEmpty(name)
+ || !isValidStatus(status)) {
+ if (e == null) {
+ throw new IllegalStateException();
+ } else {
+ b = badPrinter;
+ }
+ } else if (e != null) {
+ throw e;
+ }
+
+ // IconResourceId
+ e = null;
+ try {
+ b.setIconResourceId(iconResourceId);
+ } catch (RuntimeException ex) {
+ e = ex;
+ }
+
+ // Expect exception if iconResourceId was bad
+ // We do allow invalid Ids as the printerInfo
+ // might be created after the package of the
+ // printer is already gone if the printer is a
+ // historical printer.
+ if (iconResourceId < 0) {
+ if (e == null) {
+ throw new IllegalStateException();
+ } else {
+ b = badPrinter;
+ }
+ } else if (e != null) {
+ throw e;
+ }
+
+ // Status
+ e = null;
+ try {
+ b.setStatus(status);
+ } catch (RuntimeException ex) {
+ e = ex;
+ }
+
+ // Expect exception is status is not valid
+ if (!isValidStatus(status)) {
+ if (e == null) {
+ throw new IllegalStateException();
+ } else {
+ b = badPrinter;
+ }
+ } else if (e != null) {
+ throw e;
+ }
+
+ // Name
+ e = null;
+ try {
+ b.setName(name);
+ } catch (RuntimeException ex) {
+ e = ex;
+ }
+
+ // Expect exception if name is empty
+ if (TextUtils.isEmpty(name)) {
+ if (e == null) {
+ throw new IllegalStateException();
+ } else {
+ b = badPrinter;
+ }
+ } else if (e != null) {
+ throw e;
+ }
+
+ // hasCustomPrinterIcon
+ b.setHasCustomPrinterIcon(hasCustomPrinterIcon);
+
+ // Description
+ b.setDescription(description);
+
+ // InfoIntent
+ b.setInfoIntent(infoIntent);
+
+ // Capabilities
+ b.setCapabilities(capabilities);
+
+ PrinterInfo printer = b.build();
+
+ // Don't create bad printers
+ if (b == badPrinter) {
+ continue;
+ }
+
+ // Verify get* methods
+ if (printer.getId() != printerId
+ || printer.getName() != name
+ || printer.getStatus() != status
+ || printer
+ .getDescription() != description
+ || printer.getCapabilities()
+ != capabilities) {
+ throw new IllegalStateException();
+ }
+
+ printers.add(printer);
}
}
}
@@ -353,34 +286,15 @@
}
}
}
-
- mDiscoverySession.addPrinters(printers);
}
- return null;
- }
- }, null, null, new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- return null;
- }
- }, new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- CustomPrinterIconCallback callback = (CustomPrinterIconCallback) invocation
- .getArguments()[2];
- if (mIcon != null) {
- callback.onCustomPrinterIconLoaded(mIcon);
- }
- return null;
+ sDiscoverySession.addPrinters(printers);
}
- }, null, new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- // Take a note onDestroy was called.
- onPrinterDiscoverySessionDestroyCalled();
- return null;
- }
+ return null;
+ }, null, null, invocation -> null, invocation -> null, null, invocation -> {
+ // Take a note onDestroy was called.
+ onPrinterDiscoverySessionDestroyCalled();
+ return null;
});
}
@@ -392,12 +306,7 @@
private PrintServiceCallbacks createFirstMockPrinterServiceCallbacks(
final PrinterDiscoverySessionCallbacks sessionCallbacks) {
return createMockPrintServiceCallbacks(
- new Answer<PrinterDiscoverySessionCallbacks>() {
- @Override
- public PrinterDiscoverySessionCallbacks answer(InvocationOnMock invocation) {
- return sessionCallbacks;
- }
- },
+ invocation -> sessionCallbacks,
null, null);
}
@@ -426,7 +335,7 @@
SecondPrintService.setCallbacks(createMockPrintServiceCallbacks(null, null, null));
// Create a print adapter that respects the print contract.
- PrintDocumentAdapter adapter = createMockPrintDocumentAdapter();
+ PrintDocumentAdapter adapter = createDefaultPrintDocumentAdapter(1);
// Start printing.
print(adapter);
diff --git a/tests/tests/print/src/android/print/cts/Utils.java b/tests/tests/print/src/android/print/cts/Utils.java
new file mode 100644
index 0000000..93f5632
--- /dev/null
+++ b/tests/tests/print/src/android/print/cts/Utils.java
@@ -0,0 +1,148 @@
+/*
+ * 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.print.cts;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.Looper;
+import android.print.PrintJob;
+import android.print.PrintManager;
+import android.support.annotation.NonNull;
+import android.util.Log;
+
+/**
+ * Utilities for print tests
+ */
+public class Utils {
+ private static final String LOG_TAG = "Utils";
+
+ /**
+ * A {@link Runnable} that can throw an {@link Throwable}.
+ */
+ public interface Invokable {
+ void run() throws Throwable;
+ }
+
+ /**
+ * Run a {@link Invokable} and expect and {@link Throwable} of a certain type.
+ *
+ * @param r The {@link Invokable} to run
+ * @param expectedClass The expected {@link Throwable} type
+ */
+ public static void assertException(@NonNull Invokable r,
+ @NonNull Class<? extends Throwable> expectedClass) throws Throwable {
+ try {
+ r.run();
+ } catch (Throwable e) {
+ if (e.getClass().isAssignableFrom(expectedClass)) {
+ return;
+ } else {
+ Log.e(LOG_TAG, "Expected: " + expectedClass.getName() + ", got: "
+ + e.getClass().getName());
+ throw e;
+ }
+ }
+
+ throw new AssertionError("No throwable thrown");
+ }
+
+ /**
+ * Run a {@link Invokable} on the main thread and forward the {@link Throwable} if one was
+ * thrown.
+ *
+ * @param r The {@link Invokable} to run
+ *
+ * @throws Throwable If the {@link Runnable} caused an issue
+ */
+ static void runOnMainThread(@NonNull final Invokable r) throws Throwable {
+ final Object synchronizer = new Object();
+ final Throwable[] thrown = new Throwable[1];
+
+ synchronized (synchronizer) {
+ (new Handler(Looper.getMainLooper())).post(() -> {
+ synchronized (synchronizer) {
+ try {
+ r.run();
+ } catch (Throwable t) {
+ thrown[0] = t;
+ }
+
+ synchronizer.notify();
+ }
+ });
+
+ synchronizer.wait();
+ }
+
+ if (thrown[0] != null) {
+ throw thrown[0];
+ }
+ }
+
+ /**
+ * Make sure that a {@link Invokable} eventually finishes without throwing a {@link Throwable}.
+ *
+ * @param r The {@link Invokable} to run.
+ */
+ public static void eventually(@NonNull Invokable r) throws Throwable {
+ long start = System.currentTimeMillis();
+
+ while (true) {
+ try {
+ r.run();
+ break;
+ } catch (Throwable e) {
+ if (System.currentTimeMillis() - start < BasePrintTest.OPERATION_TIMEOUT_MILLIS) {
+ Log.e(LOG_TAG, "Ignoring exception", e);
+
+ try {
+ Thread.sleep(500);
+ } catch (InterruptedException e1) {
+ Log.e(LOG_TAG, "Interrupted", e);
+ }
+ } else {
+ throw e;
+ }
+ }
+ }
+ }
+
+ /**
+ * @param name Name of print job
+ *
+ * @return The print job for the name
+ *
+ * @throws Exception If print job could not be found
+ */
+ static @NonNull PrintJob getPrintJob(@NonNull PrintManager pm, @NonNull String name)
+ throws Exception {
+ for (android.print.PrintJob job : pm.getPrintJobs()) {
+ if (job.getInfo().getLabel().equals(name)) {
+ return job;
+ }
+ }
+
+ throw new Exception("Print job " + name + " not found in " + pm.getPrintJobs());
+ }
+
+ /**
+ * @return The print manager
+ */
+ static @NonNull PrintManager getPrintManager(@NonNull Context context) {
+ return (PrintManager) context.getSystemService(Context.PRINT_SERVICE);
+ }
+}
diff --git a/tests/tests/print/src/android/print/cts/services/CustomPrintOptionsActivity.java b/tests/tests/print/src/android/print/cts/services/CustomPrintOptionsActivity.java
index dd5ac44..cf52ece 100644
--- a/tests/tests/print/src/android/print/cts/services/CustomPrintOptionsActivity.java
+++ b/tests/tests/print/src/android/print/cts/services/CustomPrintOptionsActivity.java
@@ -28,7 +28,7 @@
*/
public class CustomPrintOptionsActivity extends Activity {
/** Lock for {@link #sCallback} */
- private static Object sLock = new Object();
+ private static final Object sLock = new Object();
/** Currently registered callback for _both_ first and second print service. */
private static CustomPrintOptionsCallback sCallback = null;
diff --git a/tests/tests/print/src/android/print/cts/services/PrintServiceCallbacks.java b/tests/tests/print/src/android/print/cts/services/PrintServiceCallbacks.java
index ff0245f..749e8a9 100644
--- a/tests/tests/print/src/android/print/cts/services/PrintServiceCallbacks.java
+++ b/tests/tests/print/src/android/print/cts/services/PrintServiceCallbacks.java
@@ -17,17 +17,16 @@
package android.print.cts.services;
import android.printservice.PrintJob;
-import android.printservice.PrintService;
public abstract class PrintServiceCallbacks {
- private PrintService mService;
+ private StubbablePrintService mService;
- public PrintService getService() {
+ public StubbablePrintService getService() {
return mService;
}
- public void setService(PrintService service) {
+ public void setService(StubbablePrintService service) {
mService = service;
}
diff --git a/tests/tests/print/src/android/print/cts/services/StubbablePrintService.java b/tests/tests/print/src/android/print/cts/services/StubbablePrintService.java
index 2686b41..8c3b89b 100644
--- a/tests/tests/print/src/android/print/cts/services/StubbablePrintService.java
+++ b/tests/tests/print/src/android/print/cts/services/StubbablePrintService.java
@@ -16,10 +16,13 @@
package android.print.cts.services;
+import android.content.Context;
import android.printservice.PrintJob;
import android.printservice.PrintService;
import android.printservice.PrinterDiscoverySession;
+import java.util.List;
+
public abstract class StubbablePrintService extends PrintService {
@Override
@@ -49,4 +52,13 @@
}
protected abstract PrintServiceCallbacks getCallbacks();
+
+ public void callAttachBaseContext(Context base) {
+ attachBaseContext(base);
+ }
+
+ public List<PrintJob> callGetActivePrintJobs() {
+ return getActivePrintJobs();
+ }
+
}
diff --git a/tests/tests/print/src/android/print/cts/services/StubbablePrinterDiscoverySession.java b/tests/tests/print/src/android/print/cts/services/StubbablePrinterDiscoverySession.java
index b7cccaa..e0ec1c1 100644
--- a/tests/tests/print/src/android/print/cts/services/StubbablePrinterDiscoverySession.java
+++ b/tests/tests/print/src/android/print/cts/services/StubbablePrinterDiscoverySession.java
@@ -16,6 +16,7 @@
package android.print.cts.services;
+import android.support.annotation.NonNull;
import android.os.CancellationSignal;
import android.print.PrinterId;
import android.printservice.CustomPrinterIconCallback;
@@ -42,7 +43,7 @@
}
@Override
- public void onStartPrinterDiscovery(List<PrinterId> priorityList) {
+ public void onStartPrinterDiscovery(@NonNull List<PrinterId> priorityList) {
if (mCallbacks != null) {
mCallbacks.onStartPrinterDiscovery(priorityList);
}
@@ -56,29 +57,30 @@
}
@Override
- public void onValidatePrinters(List<PrinterId> printerIds) {
+ public void onValidatePrinters(@NonNull List<PrinterId> printerIds) {
if (mCallbacks != null) {
mCallbacks.onValidatePrinters(printerIds);
}
}
@Override
- public void onStartPrinterStateTracking(PrinterId printerId) {
+ public void onStartPrinterStateTracking(@NonNull PrinterId printerId) {
if (mCallbacks != null) {
mCallbacks.onStartPrinterStateTracking(printerId);
}
}
@Override
- public void onRequestCustomPrinterIcon(PrinterId printerId,
- CancellationSignal cancellationSignal, CustomPrinterIconCallback callback) {
+ public void onRequestCustomPrinterIcon(@NonNull PrinterId printerId,
+ @NonNull CancellationSignal cancellationSignal,
+ @NonNull CustomPrinterIconCallback callback) {
if (mCallbacks != null) {
mCallbacks.onRequestCustomPrinterIcon(printerId, cancellationSignal, callback);
}
}
@Override
- public void onStopPrinterStateTracking(PrinterId printerId) {
+ public void onStopPrinterStateTracking(@NonNull PrinterId printerId) {
if (mCallbacks != null) {
mCallbacks.onStopPrinterStateTracking(printerId);
}
diff --git a/tests/tests/print/src/android/print/pdf/cts/PrintedPdfDocumentTest.java b/tests/tests/print/src/android/print/pdf/cts/PrintedPdfDocumentTest.java
new file mode 100644
index 0000000..b34c8cc
--- /dev/null
+++ b/tests/tests/print/src/android/print/pdf/cts/PrintedPdfDocumentTest.java
@@ -0,0 +1,191 @@
+/*
+ * 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.print.pdf.cts;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.graphics.pdf.PdfDocument;
+import android.print.PrintAttributes;
+import android.print.pdf.PrintedPdfDocument;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static android.print.cts.Utils.assertException;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Tests {@link PrintedPdfDocument}. This class is a subclass of {@link PdfDocument}, hence only the
+ * overridden methods are tested.
+ */
+@RunWith(AndroidJUnit4.class)
+public class PrintedPdfDocumentTest {
+ private static final PrintAttributes.Margins ZERO_MARGINS = new PrintAttributes.Margins(0, 0, 0,
+ 0);
+ private Context mContext;
+
+ @Before
+ public void setUp() {
+ mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ }
+
+ @Test
+ public void createWithNullAttributes() throws Throwable {
+ assertException(() -> new PrintedPdfDocument(mContext, null), NullPointerException.class);
+ }
+
+ @Test
+ public void createWithNullMediaSize() throws Throwable {
+ PrintAttributes attr = new PrintAttributes.Builder().setMinMargins(ZERO_MARGINS).build();
+ assertException(() -> new PrintedPdfDocument(mContext, attr),
+ NullPointerException.class);
+ }
+
+ @Test
+ public void createWithNullMargins() throws Throwable {
+ PrintAttributes attr = new PrintAttributes.Builder()
+ .setMediaSize(PrintAttributes.MediaSize.ISO_A4).build();
+ assertException(() -> new PrintedPdfDocument(mContext, attr),
+ NullPointerException.class);
+ }
+
+ @Test
+ public void createWithNullContext() throws Exception {
+ PrintAttributes attr = new PrintAttributes.Builder().setMinMargins(ZERO_MARGINS)
+ .setMediaSize(PrintAttributes.MediaSize.ISO_A4).build();
+
+ // Legacy: Context is not used and not checked for null-ness
+ PrintedPdfDocument doc = new PrintedPdfDocument(null, attr);
+ doc.close();
+ }
+
+ @Test
+ public void startPage() throws Exception {
+ PrintAttributes attr = new PrintAttributes.Builder().setMinMargins(ZERO_MARGINS)
+ .setMediaSize(PrintAttributes.MediaSize.ISO_A4).build();
+
+ PrintedPdfDocument doc = new PrintedPdfDocument(mContext, attr);
+ PdfDocument.Page page = doc.startPage(0);
+ doc.finishPage(page);
+ doc.close();
+ }
+
+ @Test
+ public void oneMilPageSize() throws Throwable {
+ PrintAttributes attr = new PrintAttributes.Builder().setMinMargins(ZERO_MARGINS)
+ .setMediaSize(new PrintAttributes.MediaSize("oneMil", "oneMil", 1, 1)).build();
+
+ PrintedPdfDocument doc = new PrintedPdfDocument(mContext, attr);
+
+ // We get an illegal argument exception here as a single mil of page size is converted to 0
+ // pts.
+ assertEquals(0, milsToPts(attr.getMediaSize().getHeightMils()));
+ assertException(() -> doc.startPage(0), IllegalArgumentException.class);
+
+ doc.close();
+ }
+
+ /**
+ * Converts mils (1000th of an inch) to postscript points (72th of an inch).
+ *
+ * @param mils The distance in mils
+ *
+ * @return The distance in Postscript points
+ */
+ private int milsToPts(int mils) {
+ return (int) (((float) mils / 1000) * 72);
+ }
+
+ @Test
+ public void getPageWidth() throws Exception {
+ PrintAttributes attr = new PrintAttributes.Builder().setMinMargins(ZERO_MARGINS)
+ .setMediaSize(PrintAttributes.MediaSize.ISO_A4).build();
+
+ PrintedPdfDocument doc = new PrintedPdfDocument(mContext, attr);
+ assertEquals(milsToPts(attr.getMediaSize().getWidthMils()), doc.getPageWidth());
+ doc.close();
+ }
+
+ @Test
+ public void getPageHeight() throws Exception {
+ PrintAttributes attr = new PrintAttributes.Builder().setMinMargins(ZERO_MARGINS)
+ .setMediaSize(PrintAttributes.MediaSize.ISO_A4).build();
+
+ PrintedPdfDocument doc = new PrintedPdfDocument(mContext, attr);
+ assertEquals(milsToPts(attr.getMediaSize().getHeightMils()), doc.getPageHeight());
+ doc.close();
+ }
+
+ @Test
+ public void getContentRect() throws Exception {
+ PrintAttributes attr = new PrintAttributes.Builder().setMinMargins(ZERO_MARGINS)
+ .setMediaSize(PrintAttributes.MediaSize.ISO_A4).build();
+
+ PrintedPdfDocument doc = new PrintedPdfDocument(mContext, attr);
+ assertEquals(new Rect(0, 0, milsToPts(attr.getMediaSize().getWidthMils()),
+ milsToPts(attr.getMediaSize().getHeightMils())), doc.getPageContentRect());
+ doc.close();
+ }
+
+ @Test
+ public void getContentRectBigMargins() throws Exception {
+ PrintAttributes.Margins margins = new PrintAttributes.Margins(50, 60, 70, 80);
+ PrintAttributes attr = new PrintAttributes.Builder().setMinMargins(margins)
+ .setMediaSize(PrintAttributes.MediaSize.ISO_A4).build();
+
+ PrintedPdfDocument doc = new PrintedPdfDocument(mContext, attr);
+ assertEquals(new Rect(milsToPts(margins.getLeftMils()), milsToPts(margins.getTopMils()),
+ milsToPts(attr.getMediaSize().getWidthMils()) - milsToPts(margins.getRightMils()),
+ milsToPts(attr.getMediaSize().getHeightMils()) -
+ milsToPts(margins.getBottomMils())),
+ doc.getPageContentRect());
+ doc.close();
+ }
+
+ @Test
+ public void getPageHeightAfterClose() throws Exception {
+ PrintAttributes attr = new PrintAttributes.Builder().setMinMargins(ZERO_MARGINS)
+ .setMediaSize(PrintAttributes.MediaSize.ISO_A4).build();
+
+ PrintedPdfDocument doc = new PrintedPdfDocument(mContext, attr);
+ doc.close();
+ assertEquals(milsToPts(attr.getMediaSize().getHeightMils()), doc.getPageHeight());
+ }
+
+ @Test
+ public void getPageWidthAfterClose() throws Exception {
+ PrintAttributes attr = new PrintAttributes.Builder().setMinMargins(ZERO_MARGINS)
+ .setMediaSize(PrintAttributes.MediaSize.ISO_A4).build();
+
+ PrintedPdfDocument doc = new PrintedPdfDocument(mContext, attr);
+ doc.close();
+ assertEquals(milsToPts(attr.getMediaSize().getWidthMils()), doc.getPageWidth());
+ }
+
+ @Test
+ public void getContentRectAfterClose() throws Exception {
+ PrintAttributes attr = new PrintAttributes.Builder().setMinMargins(ZERO_MARGINS)
+ .setMediaSize(PrintAttributes.MediaSize.ISO_A4).build();
+
+ PrintedPdfDocument doc = new PrintedPdfDocument(mContext, attr);
+ doc.close();
+ assertEquals(new Rect(0, 0, milsToPts(attr.getMediaSize().getWidthMils()),
+ milsToPts(attr.getMediaSize().getHeightMils())), doc.getPageContentRect());
+ }
+}
diff --git a/tests/tests/provider/src/android/provider/cts/ContactsContract_ContactsTest.java b/tests/tests/provider/src/android/provider/cts/ContactsContract_ContactsTest.java
index f8f610a..0c6f411 100644
--- a/tests/tests/provider/src/android/provider/cts/ContactsContract_ContactsTest.java
+++ b/tests/tests/provider/src/android/provider/cts/ContactsContract_ContactsTest.java
@@ -29,6 +29,7 @@
import android.provider.ContactsContract;
import android.provider.ContactsContract.CommonDataKinds.StructuredName;
import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.Directory;
import android.provider.cts.ContactsContract_TestDataBuilder.TestContact;
import android.provider.cts.ContactsContract_TestDataBuilder.TestRawContact;
import android.provider.cts.contacts.ContactUtil;
@@ -228,6 +229,15 @@
new long[]{contact.getId()}
);
+ // Contacts.CONTENT_FILTER_URI
+ DatabaseAsserts.checkProjection(mResolver,
+ Contacts.ENTERPRISE_CONTENT_FILTER_URI.buildUpon().appendEncodedPath("xxx")
+ .appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY,
+ String.valueOf(Directory.DEFAULT)).build(),
+ PROJECTION,
+ new long[]{contact.getId()}
+ );
+
// Contacts.CONTENT_LOOKUP_URI
DatabaseAsserts.checkProjection(mResolver,
Contacts.getLookupUri(contactId, lookupKey),
diff --git a/tests/tests/provider/src/android/provider/cts/ContactsContract_DataTest.java b/tests/tests/provider/src/android/provider/cts/ContactsContract_DataTest.java
index 33d3a28..62c6fdd 100644
--- a/tests/tests/provider/src/android/provider/cts/ContactsContract_DataTest.java
+++ b/tests/tests/provider/src/android/provider/cts/ContactsContract_DataTest.java
@@ -36,6 +36,7 @@
import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.Contacts.Entity;
import android.provider.ContactsContract.Data;
+import android.provider.ContactsContract.Directory;
import android.provider.ContactsContract.RawContacts;
import android.provider.ContactsContract.RawContactsEntity;
import android.provider.cts.ContactsContract_TestDataBuilder.TestContact;
@@ -533,6 +534,37 @@
assertCursorStoredValuesWithRawContactsFilter(uri, ids, sContentValues[6]);
}
+ public void testEnterpriseCallableFilterByNameOrOrganization_returnsCorrectDataRows()
+ throws Exception {
+ long[] ids = setupContactablesTestData();
+ Uri uri = Uri.withAppendedPath(Callable.ENTERPRISE_CONTENT_FILTER_URI, "doe").buildUpon()
+ .appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY,
+ String.valueOf(Directory.DEFAULT))
+ .build();
+ // Only callables belonging to John Doe (name) and Cold Tamago (organization) are returned.
+ assertCursorStoredValuesWithRawContactsFilter(uri, ids, sContentValues[5],
+ sContentValues[6]);
+ }
+
+ public void testEnterpriseCallableFilterByNumber_returnsCorrectDataRows() throws Exception {
+ long[] ids = setupContactablesTestData();
+ Uri uri = Uri.withAppendedPath(Callable.ENTERPRISE_CONTENT_FILTER_URI, "510").buildUpon()
+ .appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY,
+ String.valueOf(Directory.DEFAULT))
+ .build();
+ assertCursorStoredValuesWithRawContactsFilter(uri, ids, sContentValues[1]);
+ }
+
+ public void testEnterpriseCallableFilterBySipAddress_returnsCorrectDataRows()
+ throws Exception {
+ long[] ids = setupContactablesTestData();
+ Uri uri = Uri.withAppendedPath(Callable.ENTERPRISE_CONTENT_FILTER_URI, "mysip").buildUpon()
+ .appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY,
+ String.valueOf(Directory.DEFAULT))
+ .build();
+ assertCursorStoredValuesWithRawContactsFilter(uri, ids, sContentValues[6]);
+ }
+
public void testDataInsert_updatesContactLastUpdatedTimestamp() {
DatabaseAsserts.ContactIdPair ids = DatabaseAsserts.assertAndCreateContact(mResolver);
long baseTime = ContactUtil.queryContactLastUpdatedTimestamp(mResolver, ids.mContactId);
diff --git a/tests/tests/provider/src/android/provider/cts/ContactsContract_SearchSnippetsTest.java b/tests/tests/provider/src/android/provider/cts/ContactsContract_SearchSnippetsTest.java
index 847e357..b049996 100644
--- a/tests/tests/provider/src/android/provider/cts/ContactsContract_SearchSnippetsTest.java
+++ b/tests/tests/provider/src/android/provider/cts/ContactsContract_SearchSnippetsTest.java
@@ -28,6 +28,7 @@
import android.provider.ContactsContract.CommonDataKinds.StructuredName;
import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.Directory;
import android.provider.ContactsContract.RawContacts;
import android.provider.ContactsContract.SearchSnippets;
import android.provider.cts.ContactsContract_TestDataBuilder.TestContact;
@@ -85,6 +86,15 @@
assertCursorStoredValuesWithContactsFilter(uri, ids);
}
+ public void testEnterpriseSearchSnippets_NoMatch() throws Exception {
+ long[] ids = setupTestData();
+ final Uri uri = ContactsContract.Contacts.ENTERPRISE_CONTENT_FILTER_URI.buildUpon()
+ .appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY,
+ String.valueOf(Directory.DEFAULT)).appendPath("nomatch").build();
+
+ assertCursorStoredValuesWithContactsFilter(uri, ids);
+ }
+
public void testSearchSnippets_MatchEmailAddressCorrectSnippet() throws Exception {
long[] ids = setupTestData();
final Uri uri = ContactsContract.Contacts.CONTENT_FILTER_URI.buildUpon()
@@ -96,6 +106,18 @@
assertCursorStoredValuesWithContactsFilter(uri, ids, expected);
}
+ public void testEnterpriseSearchSnippets_MatchEmailAddressCorrectSnippet() throws Exception {
+ long[] ids = setupTestData();
+ final Uri uri = ContactsContract.Contacts.ENTERPRISE_CONTENT_FILTER_URI.buildUpon()
+ .appendPath("farm").appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY,
+ String.valueOf(Directory.DEFAULT)).build();
+
+ final ContentValues expected = new ContentValues();
+ expected.put(Contacts._ID, ids[1]);
+ expected.put(SearchSnippets.SNIPPET, "eggs@[farmers].org");
+ assertCursorStoredValuesWithContactsFilter(uri, ids, expected);
+ }
+
public void testSearchSnippets_MatchPhoneNumberCorrectSnippet() throws Exception {
long[] ids = setupTestData();
final Uri uri = ContactsContract.Contacts.CONTENT_FILTER_URI.buildUpon()
@@ -107,6 +129,18 @@
assertCursorStoredValuesWithContactsFilter(uri, ids, expected);
}
+ public void testEnterpriseSearchSnippets_MatchPhoneNumberCorrectSnippet() throws Exception {
+ long[] ids = setupTestData();
+ final Uri uri = ContactsContract.Contacts.ENTERPRISE_CONTENT_FILTER_URI.buildUpon()
+ .appendPath("510").appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY,
+ String.valueOf(Directory.DEFAULT)).build();
+
+ final ContentValues expected = new ContentValues();
+ expected.put(Contacts._ID, ids[0]);
+ expected.put(SearchSnippets.SNIPPET, "[510-123-5769]");
+ assertCursorStoredValuesWithContactsFilter(uri, ids, expected);
+ }
+
public void testSearchSnippets_MatchPostalAddressCorrectSnippet() throws Exception {
long[] ids = setupTestData();
final Uri uri = ContactsContract.Contacts.CONTENT_FILTER_URI.buildUpon()
@@ -118,6 +152,18 @@
assertCursorStoredValuesWithContactsFilter(uri, ids, expected);
}
+ public void testEnterpriseSearchSnippets_MatchPostalAddressCorrectSnippet() throws Exception {
+ long[] ids = setupTestData();
+ final Uri uri = ContactsContract.Contacts.ENTERPRISE_CONTENT_FILTER_URI.buildUpon()
+ .appendPath("street").appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY,
+ String.valueOf(Directory.DEFAULT)).build();
+
+ final ContentValues expected = new ContentValues();
+ expected.put(Contacts._ID, ids[2]);
+ expected.put(SearchSnippets.SNIPPET, "123 Main [Street] Unit 3113\u2026");
+ assertCursorStoredValuesWithContactsFilter(uri, ids, expected);
+ }
+
public void testSearchSnippets_LongMatchTruncation() throws Exception {
long[] ids = setupTestData();
final Uri uri = ContactsContract.Contacts.CONTENT_FILTER_URI.buildUpon()
@@ -129,6 +175,18 @@
assertCursorStoredValuesWithContactsFilter(uri, ids, expected);
}
+ public void testEnterpriseSearchSnippets_LongMatchTruncation() throws Exception {
+ long[] ids = setupTestData();
+ final Uri uri = ContactsContract.Contacts.ENTERPRISE_CONTENT_FILTER_URI.buildUpon()
+ .appendPath("over").appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY,
+ String.valueOf(Directory.DEFAULT)).build();
+
+ final ContentValues expected = new ContentValues();
+ expected.put(Contacts._ID, ids[2]);
+ expected.put(SearchSnippets.SNIPPET, "\u2026dog jumps [over] the quick\u2026");
+ assertCursorStoredValuesWithContactsFilter(uri, ids, expected);
+ }
+
public void testSearchSnippets_MultipleMatchesCorrectSnippet() throws Exception {
long[] ids = setupTestData();
final Uri uri = ContactsContract.Contacts.CONTENT_FILTER_URI.buildUpon()
@@ -145,6 +203,23 @@
assertCursorStoredValuesWithContactsFilter(uri, ids, expected, expected2);
}
+ public void testEnterpriseSearchSnippets_MultipleMatchesCorrectSnippet() throws Exception {
+ long[] ids = setupTestData();
+ final Uri uri = ContactsContract.Contacts.ENTERPRISE_CONTENT_FILTER_URI.buildUpon()
+ .appendPath("123").appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY,
+ String.valueOf(Directory.DEFAULT)).build();
+
+ final ContentValues expected = new ContentValues();
+ expected.put(Contacts._ID, ids[1]);
+ expected.put(SearchSnippets.SNIPPET, "[123-456-7890]");
+
+ final ContentValues expected2 = new ContentValues();
+ expected2.put(Contacts._ID, ids[2]);
+ expected2.put(SearchSnippets.SNIPPET, "[123] Main Street Unit 3113\u2026");
+
+ assertCursorStoredValuesWithContactsFilter(uri, ids, expected, expected2);
+ }
+
/**
* Tests that if deferred snippeting is indicated, the provider will not perform formatting
* of the snippet for a single word query.
@@ -163,6 +238,25 @@
}
/**
+ * Tests that if deferred snippeting is indicated, the provider will not perform formatting
+ * of the snippet for a single word query.
+ */
+ public void testEnterpriseSearchSnippets_DeferredSnippetingSingleWordQuerySnippetDeferred()
+ throws Exception {
+ long[] ids = setupTestData();
+ final Uri uri = ContactsContract.Contacts.ENTERPRISE_CONTENT_FILTER_URI.buildUpon()
+ .appendQueryParameter(SearchSnippets.DEFERRED_SNIPPETING_KEY, "1")
+ .appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY,
+ String.valueOf(Directory.DEFAULT))
+ .appendPath("510").build();
+
+ final ContentValues expected = new ContentValues();
+ expected.put(Contacts._ID, ids[0]);
+ expected.put(SearchSnippets.SNIPPET, "510-123-5769");
+ assertCursorStoredValuesWithContactsFilter(uri, ids, expected);
+ }
+
+ /**
* Tests that even if deferred snippeting is indicated, a multi-word query will have formatting
* of the snippet done by the provider using SQLite
*/
@@ -180,6 +274,25 @@
}
/**
+ * Tests that even if deferred snippeting is indicated, a multi-word query will have formatting
+ * of the snippet done by the provider using SQLite
+ */
+ public void testEnterpriseSearchSnippets_DeferredSnippetingMultiWordQuerySnippetNotDeferred()
+ throws Exception {
+ long[] ids = setupTestData();
+ final Uri uri = ContactsContract.Contacts.ENTERPRISE_CONTENT_FILTER_URI.buildUpon()
+ .appendQueryParameter(SearchSnippets.DEFERRED_SNIPPETING_KEY, "1")
+ .appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY,
+ String.valueOf(Directory.DEFAULT))
+ .appendPath("jumps over").build();
+
+ final ContentValues expected = new ContentValues();
+ expected.put(Contacts._ID, ids[2]);
+ expected.put(SearchSnippets.SNIPPET, "\u2026lazy dog [jumps] [over] the\u2026");
+ assertCursorStoredValuesWithContactsFilter(uri, ids, expected);
+ }
+
+ /**
* Given a uri, performs a query on the contacts provider for that uri and asserts that the
* cursor returned from the query matches the expected results.
*
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/reduce.rs b/tests/tests/renderscript/src/android/renderscript/cts/reduce.rs
index d731561..d5f7099 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/reduce.rs
+++ b/tests/tests/renderscript/src/android/renderscript/cts/reduce.rs
@@ -67,17 +67,17 @@
me.val = in;
me.idx = x;
- if (me.val < accum->min.val)
+ if (me.val <= accum->min.val)
accum->min = me;
- if (me.val > accum->max.val)
+ if (me.val >= accum->max.val)
accum->max = me;
}
static void fMMCombiner(MinAndMax *accum,
const MinAndMax *val) {
- if (val->min.val < accum->min.val)
+ if ((accum->min.idx < 0) || (val->min.val < accum->min.val))
accum->min = val->min;
- if (val->max.val > accum->max.val)
+ if ((accum->max.idx < 0) || (val->max.val > accum->max.val))
accum->max = val->max;
}
@@ -428,8 +428,32 @@
/////////////////////////////////////////////////////////////////////////
// Test out-of-range result.
+
+// When a result is ulong, it can take on values not representable on
+// the Java side, where there are no unsigned integral types and long
+// is the largest integral type -- i.e., all values in the range
+// (MAX_LONG, MAX_ULONG] are not representable in Java. The reflected
+// result_*.get() methods throw an exception if the result value is
+// out of range. The globals and reduction kernels below allow a test
+// case on the Java side to describe what kind of result we should
+// produce -- in particular, what to use for an in-range value and an
+// out-of-range value, and where (if anywhere) to put an out-of-range
+// value within the result (which might be scalar, vector, array of
+// scalar, or array of vector).
+
// We don't care about the input at all.
// We use these globals to configure the generation of the result.
+// A kernel puts 2*oorrBadResultHalf in the position (if any) of the result
+// given by oorrBadResult, and oorrGoodResult everywhere else.
+// The oorrBadPos encoding is as follows:
+// - For scalar result, 0 = scalar; anything else = nowhere
+// - For vector result, 0..length(vector)-1 = corresponding vector component
+// (0 = x, 1 = y, 2 = z, 3 = w); anything else = nowhere
+// - For array of scalar result, 0..length(array)-1 = corresponding array element;
+// anything else = nowhere
+// - For array of vector result, 0..length(vector)*length(array)-1 = corresponding
+// vector component C of corresponding array element E; anything else = nowhere
+// (encoding is C + length(vector)*E)
ulong oorrGoodResult; // the value of a good result
ulong oorrBadResultHalf; // half the value of a bad result
// ("half" because Java can only set the global from long not from ulong)
diff --git a/tests/tests/security/AndroidManifest.xml b/tests/tests/security/AndroidManifest.xml
index 7a67b08..1423aca 100644
--- a/tests/tests/security/AndroidManifest.xml
+++ b/tests/tests/security/AndroidManifest.xml
@@ -37,6 +37,14 @@
<service android:name="android.security.cts.activity.SecureRandomService"
android:process=":secureRandom"/>
+ <activity
+ android:name="android.security.cts.DisplayDriverActivity"
+ android:label="Test qhwc::MDPCompLowRes::allocLayerPipes (Qualcomm)">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST"/>
+ </intent-filter>
+ </activity>
</application>
<instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
diff --git a/tests/tests/security/res/layout/activity_displaydriver.xml b/tests/tests/security/res/layout/activity_displaydriver.xml
new file mode 100755
index 0000000..9009dbf
--- /dev/null
+++ b/tests/tests/security/res/layout/activity_displaydriver.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * 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.
+ -->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:gravity="center"
+ android:orientation="vertical">
+
+ <VideoView
+ android:id="@+id/videoview"
+ android:layout_width="300dp"
+ android:layout_height="250dp"
+ android:layout_gravity="center"/>
+
+</LinearLayout>
+
+
+
+
diff --git a/tests/tests/security/res/raw/blacklist_test_chain.pem b/tests/tests/security/res/raw/blacklist_test_chain.pem
new file mode 100644
index 0000000..6f1f297
--- /dev/null
+++ b/tests/tests/security/res/raw/blacklist_test_chain.pem
@@ -0,0 +1,35 @@
+-----BEGIN CERTIFICATE-----
+MIICxzCCAa+gAwIBAgIDAopPMA0GCSqGSIb3DQEBCwUAMBwxGjAYBgNVBAMTEWJs
+YWNrbGlzdCB0ZXN0IENBMCoYEzIwMTUwMTAxMDAwMDAwKzAwMDAYEzIwMjUwMTAx
+MDAwMDAwKzAwMDAwDzENMAsGA1UEAxMEbGVhZjCCASIwDQYJKoZIhvcNAQEBBQAD
+ggEPADCCAQoCggEBAOA1rNFofKivnO6f/UjNnkUZX4qG+MBXw5eeingfrLrAbyTP
+qf/YCN3F8JOcot1QUEojcjIrm54rDgi1+o9qDDY0CfbJ8UGmjgh0h5odlxnZbsF2
+0Tzy3lEFHPUUBj6160itB95giHDKH1meW91L1ah8Z+nWES9GGBIAS/1XpeXtiE7/
+IuVmEuE8veAbwdMC9qRSEeq2zUWhA4m/KzTuli/GNErkXlazj3hlBs5WJ207ztTp
+HRGrAEjQgRKb3Ap2leowiE/u9D1Ean53g4v4gzDV1gx5uTZ395WfuWteO9ZUc9bo
+XMeGJiPcvyr2i8Do25ZWw+wW1T2TbcEAtyfOmgkCAwEAAaMTMBEwDwYDVR0TAQH/
+BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAPY6VDC0kfY+kGrM5YiNEjs6PjNjj
+ojWU5/UMjwfRqF8JtkNnV9LnBiNlNGUIV8b5OCyXKGjDlc+ISNRvpgGbZ4dzVIwE
+OKKL9tUB4IFwRAxO0UbLtVkwFeX3clzTezLkei7ahgUe04c2ZjFeO/mf/nuQWvWY
+hprLz5uiGtKWPcirec4wPLkuyHzQMv7Dx/5VYARuxhHkkplteVQ4k9nTxag372Re
+eHgH4KKgLTXEBjV55RoAtOsug+k+KT8U9FzU2ul/j+89tJihllkD1udqIkic8RMx
+qn/mBaIe/ENb88TzrSXcp2xE9rth+QtjpNAVGnE4hP87QukVgedq7JKV7Q==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIC1DCCAbygAwIBAgIDDYaqMA0GCSqGSIb3DQEBCwUAMBwxGjAYBgNVBAMTEWJs
+YWNrbGlzdCB0ZXN0IENBMCoYEzIwMTUwMTAxMDAwMDAwKzAwMDAYEzIwMjUwMTAx
+MDAwMDAwKzAwMDAwHDEaMBgGA1UEAxMRYmxhY2tsaXN0IHRlc3QgQ0EwggEiMA0G
+CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDafYp6+Gs5ZjfLfU2EH/NYpvdBUyPz
+veQBJCE4PhBYhOm+Z+J6aX0rSHqU4VTJ8H0TOb6Fh54zBUkIQJHx8YTIsXVmDj0O
+louWAa3uYpIOeBz46knJxdTI9NG6XnsHMYUICZPM8CHtHhoaYnhaRFTcGIg+Y9Hl
+BxMTYXXtqjicg10YuSuEkwMuDT7CbmnmYon8Gt5+ygHIe8YFWdCicpzm5wlPvRu4
+D+WiH2mTgfFG5D5QDoRnxnHWAcO8/+UenFtnbfRip9h6TrzXoJSHtuYW3rMCDVG3
+owVwUE3+ExMcbWKn+qaqGQsjrLlwyYEcKjhH67iPFcTtvZfCsgv8YG75AgMBAAGj
+EzARMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAILNS2XgO4Qa
+plyF7wbQFvVlFFe1QIiEiPZcopqb0zEse73IPBGUnoIt3C9keCv8Q6d7h0x2fe2N
+IqD4P9WXGQYiobBnTci1d2nW5dBq1WVDcpK4cNVsDX7SBE6sd19JEAazNSPIQJ6T
+sts2JXXdTssAyVqGAnq6TwQ2U5ArzuC5pCmr7FcfYAH0sCZM5VWw+ffJylDMBfeG
+oWyjH6f+TmkDd7yvIDh+ptn7Qv+LRxIjHDLPOxG9Y6JaDYtVqKJWh7er5/HFlwUi
+E6gpIuFM6It5ogUtmik2B19bPWpcnGFhv01IKBgmihpzd8LyCmxTtkK11KMxS1JF
+xZSCP3mJTbQ=
+-----END CERTIFICATE-----
diff --git a/tests/tests/security/res/raw/blacklist_test_valid_ca.pem b/tests/tests/security/res/raw/blacklist_test_valid_ca.pem
new file mode 100644
index 0000000..19148c7
--- /dev/null
+++ b/tests/tests/security/res/raw/blacklist_test_valid_ca.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICwDCCAaigAwIBAgIDBWa1MA0GCSqGSIb3DQEBCwUAMBIxEDAOBgNVBAMTB1Rl
+c3QgQ2EwKhgTMjAxNTAxMDEwMDAwMDArMDAwMBgTMjAyNTAxMDEwMDAwMDArMDAw
+MDASMRAwDgYDVQQDEwdUZXN0IENhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
+CgKCAQEAuk5Hq/uHOz7E3gEZFXKb0ZFslODflO7vB/VT3dmHyGXxuDK5fgQB4xPz
+uoU1VSpD9Pxpe9u+6jNlShEZ5xN34c2F6g+stU4lUS5udqCZVEtB6/etOOpMuiWU
+Ud2DVkEAn9weWkJmKy2gkLQ8p2Iw+0mPlhKKFI9brhGTEpQDTvW9sbLmSQFSEk30
+Ia5rxii/cgu8j5AQmsvUQA06vHXq6/xIsQIj1UFMycBmPz8BvrVO/c891vD9f2Uq
+gQg4p084rmsc6a7PAhBibTOFs3m91HNyZuY2M3pA1r1oLPRQ3WYXb8Wt+kHVtKAr
+L6qDXtofCU3RGhAruwjmuOWftgNsGwIDAQABoxMwETAPBgNVHRMBAf8EBTADAQH/
+MA0GCSqGSIb3DQEBCwUAA4IBAQCkFKi9HmsOyn4Wh6RpzwSh39L6e48IdOMcNpOK
+O38tO2rO/uNnhGn2ptnUJkwYS7XOy/A09q1iZNbiuwBweIxc8Z17vpNmk7cF4TMw
+lMRJjDE3Dm2qjTs/lDOknGuzB38Q8tGN/ZbYUHjjf4TZHk5FXCzRUUp+UMzxlOuc
+HAuNuBlQUW88s26h7NdNr115VCbouAI7YkWJRmS2FbeQD5Nwx/kZcvLtJyKasx+n
+imeqy3kCW1NzP9nwlsx2vwW6ydGsdHxqsfrRpdRSLHjzQDA+5m5xMNV0oTr86Iex
+mkqHtIMbOOUpzrE3PACA4m7IgA6AcSkV5gGM9AgqcYccC3St
+-----END CERTIFICATE-----
diff --git a/tests/tests/security/res/raw/blacklist_test_valid_chain.pem b/tests/tests/security/res/raw/blacklist_test_valid_chain.pem
new file mode 100644
index 0000000..e763a05
--- /dev/null
+++ b/tests/tests/security/res/raw/blacklist_test_valid_chain.pem
@@ -0,0 +1,87 @@
+-----BEGIN CERTIFICATE-----
+MIICwjCCAaqgAwIBAgIDD37fMA0GCSqGSIb3DQEBCwUAMBcxFTATBgNVBAMTDGlu
+dGVybWVkaWF0ZTAqGBMyMDE1MDEwMTAwMDAwMCswMDAwGBMyMDI1MDEwMTAwMDAw
+MCswMDAwMA8xDTALBgNVBAMTBGxlYWYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
+ggEKAoIBAQDNq1OWpxGZfNFw8E8ZPvYEXeaXEa1twBSKDe1EUctuck+F8Ethb0O1
+ooWA9egJh8GWSbxFdPfoJ/yyuor3sH5kkUtq94NO/1IXPn4xnrwrfvkeVR8e3pXn
+kAQm7MH8c8iPmQ59arfBjFfX9ZZhPiLDPq1bsQa8WqaajyylVVDzQcYseDSHoR/7
+3QmcfUZjH5qxYf7jcS8QdtfnD6faZuczM30qL7N3BLn2gcA5I5jVkrxQBKfLBPfl
+6k3aO6ekxSSxhSHqBv7x5VIzoiq666DGdelLuwrmMksx7Ni7cnXws3rlBYCr6wly
+Hux62YJ9Og3rC5lb3pjkmSzj31VVfFnpAgMBAAGjEzARMA8GA1UdEwEB/wQFMAMB
+Af8wDQYJKoZIhvcNAQELBQADggEBAISuXogfdMHZiAs3CJwsOTjCLW0MGsqWH88j
+wdkbotZuTAb84Iq2vpoX/w95WlyakseFGHnAaexy4nzSNyn8LC5b4JMNQopn8Gxs
+y3p0Z+XC/PNC/4lVxQB8KARFvhtW7Ltw1jjIqbTq2ZTWVSCuqb+1ZnMihP4MYinb
+Ml/Q9N/pitaLolQ/pewm4YjqUA8rGC3OkyL06huz+Ow382TvMDVLk0nctMvCrg1h
+IJFlCD5I8xhcIAqp7wzEHVHQ9jRT9NjElG+PF6FwGi6IW3A8wL8fGru2N84OeJbs
+ROrn33HqVsoqZUdXSPG5YGxM7c7wfUBx3g1/Ou3gxLlqp4a/kX0=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIICzzCCAbegAwIBAgIDDeS0MA0GCSqGSIb3DQEBCwUAMBwxGjAYBgNVBAMTEWJs
+YWNrbGlzdCB0ZXN0IENBMCoYEzIwMTUwMTAxMDAwMDAwKzAwMDAYEzIwMjUwMTAx
+MDAwMDAwKzAwMDAwFzEVMBMGA1UEAxMMaW50ZXJtZWRpYXRlMIIBIjANBgkqhkiG
+9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2v+yrEqrNHOD30WnfdjgDQx+bWF46/zUArFU
+aeHSzjKEaZcSXI4a3vtFS5NH7AGE8kcrOWRSpgG9WC1CSSW9doHqz0eVK/vBDa62
+J3eZPh0kc2pgrwPRWZjzoQLpaApIq1j7xskp5PC21GA3mDKQCI/Z/TpuBoD38jwR
+TzmJOA4/+0zf+5dH4qyzHtE+K/WrUdNnonZ9ohK9WAlDhKAZ8N4VFb75VQJOYhdK
+sBiqQqBiw1Wg9IRSCeDSq3O6zjDznzQAa0hmKanqq+VVwgq8z9GRCXa3y2RawnU6
+oVfRKTQnRqUxtRobjXUCArDatsZ4xr1A4fDMCPcLyaEMOCG7CQIDAQABoxMwETAP
+BgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQClkXYt95OajuH5Xqut
+KO52XMAeJSC+lAp7sT32tUAZgsV0x5YEAKbo6W9hWuSPdx99r9bPgfQ0sqc1gRgS
+wyW3ZbB/rNrQqQqNYzBWwKrg3uTCGA1o85SqX8aiDJuygBySllTzGztENTApWYwN
+LJATS9yPgn/31BHyfZ29v+6fa3cnuRitVGIW/thDwz8IPqPSNqGTO8Obf/6WDOK/
+7pkji2rHG25Gi/3mWOvnjejbKwb4w4ZlihcNc60ra+0qEM5xstGz6dMJ3sd/w/Fq
+7d/4qhAEpJ7GPg/A5eVGyTYhpYuBA68KoQrrPf2CCGUFQxLQm6UQlICB5AREWOmi
+hZGG
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIICxTCCAa2gAwIBAgIDAIddMA0GCSqGSIb3DQEBCwUAMBIxEDAOBgNVBAMTB1Rl
+c3QgQ2EwKhgTMjAxNTAxMDEwMDAwMDArMDAwMBgTMjAyNTAxMDEwMDAwMDArMDAw
+MDAXMRUwEwYDVQQDEwxpbnRlcm1lZGlhdGUwggEiMA0GCSqGSIb3DQEBAQUAA4IB
+DwAwggEKAoIBAQDa/7KsSqs0c4PfRad92OANDH5tYXjr/NQCsVRp4dLOMoRplxJc
+jhre+0VLk0fsAYTyRys5ZFKmAb1YLUJJJb12gerPR5Ur+8ENrrYnd5k+HSRzamCv
+A9FZmPOhAuloCkirWPvGySnk8LbUYDeYMpAIj9n9Om4GgPfyPBFPOYk4Dj/7TN/7
+l0firLMe0T4r9atR02eidn2iEr1YCUOEoBnw3hUVvvlVAk5iF0qwGKpCoGLDVaD0
+hFIJ4NKrc7rOMPOfNABrSGYpqeqr5VXCCrzP0ZEJdrfLZFrCdTqhV9EpNCdGpTG1
+GhuNdQICsNq2xnjGvUDh8MwI9wvJoQw4IbsJAgMBAAGjEzARMA8GA1UdEwEB/wQF
+MAMBAf8wDQYJKoZIhvcNAQELBQADggEBAJnqAkMbjA4+FFLAapxM+MMLjIVmtSB7
+je7U8APLI0jeY/Wye/OAsOI2vmn7PYVsCTQXr14sCJz863UHrrlDF8ejf0nqSfUM
+bSXvc23XuDmcDqoM2UroHqRmZa0SC1cFC6aJ5ODwioB98cSiPzr24aWcr43dtO4P
+OOjmDXzpC7E67amn3luUIpDJ8epHPIT8+hxP2FP7CHlYUxKQFh3l/t3ftlVF9QId
+992TbF9dDluhzWVh7jsNRJrq2cEIPn6dBsPRPncOcvYton4nvpmDaeS9/d5ktkij
+LCpJv0ECxC/kcPQu65twBWhPwER/hOV0Tq9VYVDpgP3k/K4YdXs1UhY=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIC1DCCAbygAwIBAgIDDYaqMA0GCSqGSIb3DQEBCwUAMBwxGjAYBgNVBAMTEWJs
+YWNrbGlzdCB0ZXN0IENBMCoYEzIwMTUwMTAxMDAwMDAwKzAwMDAYEzIwMjUwMTAx
+MDAwMDAwKzAwMDAwHDEaMBgGA1UEAxMRYmxhY2tsaXN0IHRlc3QgQ0EwggEiMA0G
+CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDafYp6+Gs5ZjfLfU2EH/NYpvdBUyPz
+veQBJCE4PhBYhOm+Z+J6aX0rSHqU4VTJ8H0TOb6Fh54zBUkIQJHx8YTIsXVmDj0O
+louWAa3uYpIOeBz46knJxdTI9NG6XnsHMYUICZPM8CHtHhoaYnhaRFTcGIg+Y9Hl
+BxMTYXXtqjicg10YuSuEkwMuDT7CbmnmYon8Gt5+ygHIe8YFWdCicpzm5wlPvRu4
+D+WiH2mTgfFG5D5QDoRnxnHWAcO8/+UenFtnbfRip9h6TrzXoJSHtuYW3rMCDVG3
+owVwUE3+ExMcbWKn+qaqGQsjrLlwyYEcKjhH67iPFcTtvZfCsgv8YG75AgMBAAGj
+EzARMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAILNS2XgO4Qa
+plyF7wbQFvVlFFe1QIiEiPZcopqb0zEse73IPBGUnoIt3C9keCv8Q6d7h0x2fe2N
+IqD4P9WXGQYiobBnTci1d2nW5dBq1WVDcpK4cNVsDX7SBE6sd19JEAazNSPIQJ6T
+sts2JXXdTssAyVqGAnq6TwQ2U5ArzuC5pCmr7FcfYAH0sCZM5VWw+ffJylDMBfeG
+oWyjH6f+TmkDd7yvIDh+ptn7Qv+LRxIjHDLPOxG9Y6JaDYtVqKJWh7er5/HFlwUi
+E6gpIuFM6It5ogUtmik2B19bPWpcnGFhv01IKBgmihpzd8LyCmxTtkK11KMxS1JF
+xZSCP3mJTbQ=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIICwDCCAaigAwIBAgIDBWa1MA0GCSqGSIb3DQEBCwUAMBIxEDAOBgNVBAMTB1Rl
+c3QgQ2EwKhgTMjAxNTAxMDEwMDAwMDArMDAwMBgTMjAyNTAxMDEwMDAwMDArMDAw
+MDASMRAwDgYDVQQDEwdUZXN0IENhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
+CgKCAQEAuk5Hq/uHOz7E3gEZFXKb0ZFslODflO7vB/VT3dmHyGXxuDK5fgQB4xPz
+uoU1VSpD9Pxpe9u+6jNlShEZ5xN34c2F6g+stU4lUS5udqCZVEtB6/etOOpMuiWU
+Ud2DVkEAn9weWkJmKy2gkLQ8p2Iw+0mPlhKKFI9brhGTEpQDTvW9sbLmSQFSEk30
+Ia5rxii/cgu8j5AQmsvUQA06vHXq6/xIsQIj1UFMycBmPz8BvrVO/c891vD9f2Uq
+gQg4p084rmsc6a7PAhBibTOFs3m91HNyZuY2M3pA1r1oLPRQ3WYXb8Wt+kHVtKAr
+L6qDXtofCU3RGhAruwjmuOWftgNsGwIDAQABoxMwETAPBgNVHRMBAf8EBTADAQH/
+MA0GCSqGSIb3DQEBCwUAA4IBAQCkFKi9HmsOyn4Wh6RpzwSh39L6e48IdOMcNpOK
+O38tO2rO/uNnhGn2ptnUJkwYS7XOy/A09q1iZNbiuwBweIxc8Z17vpNmk7cF4TMw
+lMRJjDE3Dm2qjTs/lDOknGuzB38Q8tGN/ZbYUHjjf4TZHk5FXCzRUUp+UMzxlOuc
+HAuNuBlQUW88s26h7NdNr115VCbouAI7YkWJRmS2FbeQD5Nwx/kZcvLtJyKasx+n
+imeqy3kCW1NzP9nwlsx2vwW6ydGsdHxqsfrRpdRSLHjzQDA+5m5xMNV0oTr86Iex
+mkqHtIMbOOUpzrE3PACA4m7IgA6AcSkV5gGM9AgqcYccC3St
+-----END CERTIFICATE-----
diff --git a/tests/tests/security/res/raw/fuzz.webm b/tests/tests/security/res/raw/fuzz.webm
new file mode 100755
index 0000000..e5d95ce
--- /dev/null
+++ b/tests/tests/security/res/raw/fuzz.webm
Binary files differ
diff --git a/tests/tests/security/res/raw/test_blacklist_ca.pem b/tests/tests/security/res/raw/test_blacklist_ca.pem
new file mode 100644
index 0000000..b087d56
--- /dev/null
+++ b/tests/tests/security/res/raw/test_blacklist_ca.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC1DCCAbygAwIBAgIDDYaqMA0GCSqGSIb3DQEBCwUAMBwxGjAYBgNVBAMTEWJs
+YWNrbGlzdCB0ZXN0IENBMCoYEzIwMTUwMTAxMDAwMDAwKzAwMDAYEzIwMjUwMTAx
+MDAwMDAwKzAwMDAwHDEaMBgGA1UEAxMRYmxhY2tsaXN0IHRlc3QgQ0EwggEiMA0G
+CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDafYp6+Gs5ZjfLfU2EH/NYpvdBUyPz
+veQBJCE4PhBYhOm+Z+J6aX0rSHqU4VTJ8H0TOb6Fh54zBUkIQJHx8YTIsXVmDj0O
+louWAa3uYpIOeBz46knJxdTI9NG6XnsHMYUICZPM8CHtHhoaYnhaRFTcGIg+Y9Hl
+BxMTYXXtqjicg10YuSuEkwMuDT7CbmnmYon8Gt5+ygHIe8YFWdCicpzm5wlPvRu4
+D+WiH2mTgfFG5D5QDoRnxnHWAcO8/+UenFtnbfRip9h6TrzXoJSHtuYW3rMCDVG3
+owVwUE3+ExMcbWKn+qaqGQsjrLlwyYEcKjhH67iPFcTtvZfCsgv8YG75AgMBAAGj
+EzARMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAILNS2XgO4Qa
+plyF7wbQFvVlFFe1QIiEiPZcopqb0zEse73IPBGUnoIt3C9keCv8Q6d7h0x2fe2N
+IqD4P9WXGQYiobBnTci1d2nW5dBq1WVDcpK4cNVsDX7SBE6sd19JEAazNSPIQJ6T
+sts2JXXdTssAyVqGAnq6TwQ2U5ArzuC5pCmr7FcfYAH0sCZM5VWw+ffJylDMBfeG
+oWyjH6f+TmkDd7yvIDh+ptn7Qv+LRxIjHDLPOxG9Y6JaDYtVqKJWh7er5/HFlwUi
+E6gpIuFM6It5ogUtmik2B19bPWpcnGFhv01IKBgmihpzd8LyCmxTtkK11KMxS1JF
+xZSCP3mJTbQ=
+-----END CERTIFICATE-----
diff --git a/tests/tests/security/src/android/security/cts/CertBlacklistTest.java b/tests/tests/security/src/android/security/cts/CertBlacklistTest.java
new file mode 100644
index 0000000..d7a199e
--- /dev/null
+++ b/tests/tests/security/src/android/security/cts/CertBlacklistTest.java
@@ -0,0 +1,133 @@
+/*
+ * 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.security.cts;
+
+import android.content.Context;
+import android.test.AndroidTestCase;
+
+import java.io.InputStream;
+import java.util.Collection;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.security.KeyStore;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import javax.net.ssl.X509TrustManager;
+
+/**
+ * End to end version of org.conscrypt.CertBlacklistTest that tests the platform default
+ * {@link X509TrustManager}.
+ *
+ * The test blacklisted CA's private key can be found in
+ * external/conscrypt/src/test/resources/blacklist_ca_key.pem
+ */
+public class CertBlacklistTest extends AndroidTestCase {
+
+ private static final int BLACKLIST_CA = R.raw.test_blacklist_ca;
+ private static final int BLACKLISTED_CHAIN = R.raw.blacklist_test_chain;
+ private static final int BLACKLIST_FALLBACK_VALID_CA = R.raw.blacklist_test_valid_ca;
+ private static final int BLACKLISTED_VALID_CHAIN = R.raw.blacklist_test_valid_chain;
+
+ /**
+ * Checks that the blacklisted CA is rejected even if it used as a root of trust
+ */
+ public void testBlacklistedCaUntrusted() throws Exception {
+ X509Certificate blacklistedCa = loadCertificate(BLACKLIST_CA);
+ assertUntrusted(new X509Certificate[] {blacklistedCa}, getTrustManager(blacklistedCa));
+ }
+
+ /**
+ * Checks that a chain that is rooted in a blacklisted trusted CA is rejected.
+ */
+ public void testBlacklistedRootOfTrust() throws Exception {
+ // Chain is leaf -> blacklisted
+ X509Certificate[] chain = loadCertificates(BLACKLISTED_CHAIN);
+ X509Certificate blacklistedCa = loadCertificate(BLACKLIST_CA);
+ assertUntrusted(chain, getTrustManager(blacklistedCa));
+ }
+
+ /**
+ * Tests that the path building correctly routes around a blacklisted cert where there are
+ * other valid paths available. This prevents breakage where a cert was cross signed by a
+ * blacklisted CA but is still valid due to also being cross signed by CAs that remain trusted.
+ * Path:
+ *
+ * leaf -> intermediate -> blacklisted_ca
+ * \
+ * -------> trusted_ca
+ */
+ public void testBlacklistedIntermediateFallback() throws Exception {
+ X509Certificate[] chain = loadCertificates(BLACKLISTED_VALID_CHAIN);
+ X509Certificate blacklistedCa = loadCertificate(BLACKLIST_CA);
+ X509Certificate validCa = loadCertificate(BLACKLIST_FALLBACK_VALID_CA);
+ assertTrusted(chain, getTrustManager(blacklistedCa, validCa));
+ // Check that without the trusted_ca the chain is invalid (since it only chains to a
+ // blacklisted ca)
+ assertUntrusted(chain, getTrustManager(blacklistedCa));
+ }
+
+ private X509Certificate loadCertificate(int resId) throws Exception {
+ return loadCertificates(resId)[0];
+ }
+
+ private X509Certificate[] loadCertificates(int resId) throws Exception {
+ CertificateFactory factory = CertificateFactory.getInstance("X.509");
+ try (InputStream is = getContext().getResources().openRawResource(resId)) {
+ Collection<? extends Certificate> collection = factory.generateCertificates(is);
+ X509Certificate[] certs = new X509Certificate[collection.size()];
+ int i = 0;
+ for (Certificate cert : collection) {
+ certs[i++] = (X509Certificate) cert;
+ }
+ return certs;
+ }
+ }
+
+ private static X509TrustManager getTrustManager(X509Certificate... trustedCas)
+ throws Exception {
+ KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
+ ks.load(null);
+ int i = 0;
+ for (X509Certificate ca : trustedCas) {
+ ks.setCertificateEntry(String.valueOf(i++), ca);
+ }
+ TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX");
+ tmf.init(ks);
+ for (TrustManager tm : tmf.getTrustManagers()) {
+ if (tm instanceof X509TrustManager) {
+ return (X509TrustManager) tm;
+ }
+ }
+ fail("Could not find default X509TrustManager");
+ return null;
+ }
+
+ private static void assertTrusted(X509Certificate[] certs, X509TrustManager tm)
+ throws Exception {
+ tm.checkServerTrusted(certs, "RSA");
+ }
+
+ private static void assertUntrusted(X509Certificate[] certs, X509TrustManager tm) {
+ try {
+ tm.checkServerTrusted(certs, "RSA");
+ fail();
+ } catch (CertificateException expected) {
+ }
+ }
+}
diff --git a/hostsidetests/services/activitymanager/app/src/android/server/app/SlowCreateActivity.java b/tests/tests/security/src/android/security/cts/DisplayDriverActivity.java
old mode 100644
new mode 100755
similarity index 81%
copy from hostsidetests/services/activitymanager/app/src/android/server/app/SlowCreateActivity.java
copy to tests/tests/security/src/android/security/cts/DisplayDriverActivity.java
index 481f5be..ca6c898
--- a/hostsidetests/services/activitymanager/app/src/android/server/app/SlowCreateActivity.java
+++ b/tests/tests/security/src/android/security/cts/DisplayDriverActivity.java
@@ -1,30 +1,30 @@
-/*
- * 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.server.app;
-
-import android.app.Activity;
-import android.os.Bundle;
-
-public class SlowCreateActivity extends Activity {
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- try {
- Thread.sleep(2000);
- } catch(InterruptedException e) {}
- super.onCreate(savedInstanceState);
- }
-}
+/*
+ * 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.security.cts;
+
+import android.security.cts.R;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class DisplayDriverActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_displaydriver);
+ }
+}
diff --git a/tests/tests/security/src/android/security/cts/DisplayDriverTest.java b/tests/tests/security/src/android/security/cts/DisplayDriverTest.java
new file mode 100755
index 0000000..7f3a0d4
--- /dev/null
+++ b/tests/tests/security/src/android/security/cts/DisplayDriverTest.java
@@ -0,0 +1,115 @@
+/*
+ * 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.security.cts;
+
+import android.security.cts.R;
+
+import android.app.Activity;
+import android.media.MediaPlayer;
+import android.media.MediaPlayer.OnErrorListener;
+import android.net.Uri;
+import android.os.SystemClock;
+import android.test.ActivityInstrumentationTestCase2;
+import android.widget.MediaController;
+import android.widget.VideoView;
+
+public class DisplayDriverTest extends
+ ActivityInstrumentationTestCase2<DisplayDriverActivity> {
+ /**
+ * The maximum time to wait for an operation.
+ */
+ private static final int TIMEOUT_ASYNC_PROCESSING = 3000;
+ private Activity mActivity;
+
+ public DisplayDriverTest() {
+ super(DisplayDriverActivity.class);
+ }
+
+ /**
+ * Checks whether video crashed or not
+ * 1. Initializes mTriggered to false
+ * 2. sets mTriggered to true if onError() occurred while playing video
+ */
+ private static class MockListener {
+ private boolean mTriggered;
+
+ MockListener() {
+ mTriggered = false;
+ }
+
+ public boolean isTriggered() {
+ return mTriggered;
+ }
+
+ protected void onEvent() {
+ mTriggered = true;
+ }
+ }
+
+ private static class MockOnErrorListener extends MockListener implements
+ OnErrorListener {
+ public boolean onError(MediaPlayer mp, int what, int extra) {
+ super.onEvent();
+ return false;
+ }
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mActivity = getActivity();
+ assertNotNull("Failed to get the activity instance", mActivity);
+ }
+
+ /**
+ * 1. Runs the vulnerable video by registering OnErrorListener for VideoView
+ * 2. Wait for max time taken by video to crash and hit OnErrorListener
+ * 3. if video crashed - Pass the test case otherwise Fail the test case
+ */
+ public void testDisplayDriver_cve_2015_6634() {
+ final MockOnErrorListener listener = new MockOnErrorListener();
+ mActivity.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ MediaController mMediaControls =
+ new MediaController(mActivity);
+ VideoView mVideoView =
+ (VideoView) mActivity.findViewById(R.id.videoview);
+ mVideoView.setMediaController(mMediaControls);
+ mVideoView.setOnErrorListener(listener);
+ mVideoView.setVideoURI(Uri.parse("android.resource://" +
+ mActivity.getPackageName() + "/" + R.raw.fuzz));
+ mVideoView.start();
+ } catch (Exception e) {
+ listener.onError(null, 0, 0);
+ }
+ }
+ });
+ SystemClock.sleep(TIMEOUT_ASYNC_PROCESSING);
+ assertTrue("Test case failed due to vulnerability in the video: " +
+ "Device is vulenrable to CVE-2015-6634", listener.isTriggered());
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ if (mActivity != null) {
+ mActivity.finish();
+ }
+ super.tearDown();
+ }
+}
diff --git a/tests/tests/speech/Android.mk b/tests/tests/speech/Android.mk
index 3d95053..68e0cba 100755
--- a/tests/tests/speech/Android.mk
+++ b/tests/tests/speech/Android.mk
@@ -21,7 +21,7 @@
# and when built explicitly put it in the data partition
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner android-support-test
LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/tests/systemui/Android.mk b/tests/tests/systemui/Android.mk
index 536d2a8..c31d825 100644
--- a/tests/tests/systemui/Android.mk
+++ b/tests/tests/systemui/Android.mk
@@ -26,7 +26,7 @@
LOCAL_JAVA_LIBRARIES := android.test.runner
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner android-support-test
LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/tests/telecom/Android.mk b/tests/tests/telecom/Android.mk
index 5a8922e..27947af 100644
--- a/tests/tests/telecom/Android.mk
+++ b/tests/tests/telecom/Android.mk
@@ -24,7 +24,7 @@
# When built, explicitly put it in the data partition.
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner android-support-test
LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/tests/telephony/src/android/telephony/cts/SimRestrictedApisTest.java b/tests/tests/telephony/src/android/telephony/cts/SimRestrictedApisTest.java
index 0f09314..b15a4e8 100644
--- a/tests/tests/telephony/src/android/telephony/cts/SimRestrictedApisTest.java
+++ b/tests/tests/telephony/src/android/telephony/cts/SimRestrictedApisTest.java
@@ -265,4 +265,20 @@
} catch (SecurityException expected) {
}
}
+
+ /**
+ * Tests the TelephonyManager.getIccAuthentication() API. This makes a call to
+ * getIccAuthentication() API and expects a SecurityException since the test apk is not
+ * signed by certificate on the SIM.
+ */
+ public void testGetIccAuthentication() {
+ try {
+ if (isSimCardPresent()) {
+ TelephonyManager.getDefault().getIccAuthentication(TelephonyManager.APPTYPE_USIM,
+ TelephonyManager.AUTHTYPE_EAP_AKA, "");
+ fail("Expected SecurityException. App doesn't have carrier privileges.");
+ }
+ } catch (SecurityException expected) {
+ }
+ }
}
diff --git a/tests/tests/text/Android.mk b/tests/tests/text/Android.mk
index 65a93fb..fbc7a59 100644
--- a/tests/tests/text/Android.mk
+++ b/tests/tests/text/Android.mk
@@ -23,7 +23,12 @@
LOCAL_JAVA_LIBRARIES := android.test.runner
-LOCAL_STATIC_JAVA_LIBRARIES := ctsdeviceutil ctsdeviceutillegacy ctstestrunner
+LOCAL_STATIC_JAVA_LIBRARIES += \
+ ctsdeviceutil \
+ ctsdeviceutillegacy \
+ ctstestrunner \
+ android-support-test \
+ mockito-target \
LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/tests/text/AndroidManifest.xml b/tests/tests/text/AndroidManifest.xml
index fea237f..40a0b50 100644
--- a/tests/tests/text/AndroidManifest.xml
+++ b/tests/tests/text/AndroidManifest.xml
@@ -68,7 +68,7 @@
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
- <data android:scheme="ctstest" />
+ <data android:scheme="ctstesttext" />
</intent-filter>
</activity>
diff --git a/tests/tests/text/res/values/strings.xml b/tests/tests/text/res/values/strings.xml
index c167278..b3db2a6 100644
--- a/tests/tests/text/res/values/strings.xml
+++ b/tests/tests/text/res/values/strings.xml
@@ -165,7 +165,7 @@
<string name="themes_prompt">Select a Theme:</string>
<string name="sample_text">Sample text goes here. I wanted something creative and whimsical
but then I just got bored...</string>
- <string name="long_text">This is a really long string which exceeds the width of the view.
+ <string name="long_text">This is a really long string which exceeds the width of the view.
New devices have a much larger screen which actually enables long strings to be displayed
with no fading. I have made this string longer to fix this case. If you are correcting this
text, I would love to see the kind of devices you guys now use! Guys, maybe some devices need longer string!
@@ -176,4 +176,8 @@
text, I would love to see the kind of devices you guys now use! Guys, maybe some devices need longer string!
I think so, so how about double this string, like copy and paste! </string>
<string name="rectangle200">"M 0,0 l 200,0 l 0, 200 l -200, 0 z"</string>
+ <plurals name="list_ellipsize_test">
+ <item quantity="one">one more</item>
+ <item quantity="other"><xliff:g id="number">%d</xliff:g> more</item>
+ </plurals>
</resources>
diff --git a/tests/tests/text/src/android/text/cts/AutoTextTest.java b/tests/tests/text/src/android/text/cts/AutoTextTest.java
index cbea4d9..867b34e 100644
--- a/tests/tests/text/src/android/text/cts/AutoTextTest.java
+++ b/tests/tests/text/src/android/text/cts/AutoTextTest.java
@@ -16,11 +16,12 @@
package android.text.cts;
-import java.util.Locale;
+import android.content.res.Configuration;
import android.test.AndroidTestCase;
import android.text.AutoText;
import android.view.View;
-import android.content.res.Configuration;
+
+import java.util.Locale;
public class AutoTextTest extends AndroidTestCase {
diff --git a/tests/tests/text/src/android/text/cts/BidiFormatterTest.java b/tests/tests/text/src/android/text/cts/BidiFormatterTest.java
index 3e5db4d..b7aec1b 100644
--- a/tests/tests/text/src/android/text/cts/BidiFormatterTest.java
+++ b/tests/tests/text/src/android/text/cts/BidiFormatterTest.java
@@ -16,9 +16,13 @@
package android.text.cts;
+import android.icu.util.ULocale;
import android.test.AndroidTestCase;
import android.text.BidiFormatter;
+import android.text.SpannableString;
+import android.text.Spanned;
import android.text.TextDirectionHeuristics;
+import android.text.style.RelativeSizeSpan;
import java.util.Locale;
@@ -233,4 +237,83 @@
LRE + HE + EN + HE + PDF,
RTL_FMT_EXIT_RESET.unicodeWrap(HE + EN + HE, TextDirectionHeuristics.LTR, false));
}
+
+ public void testGetStereoReset() {
+ assertTrue(LTR_FMT.getStereoReset());
+ assertTrue(RTL_FMT.getStereoReset());
+ assertFalse(LTR_FMT_EXIT_RESET.getStereoReset());
+ assertFalse(RTL_FMT_EXIT_RESET.getStereoReset());
+ }
+
+ public void testBuilder_construction() {
+ final BidiFormatter defaultFmt = new BidiFormatter.Builder().build();
+ // Test that the default locale and the BidiFormatter's locale have the same direction.
+ assertEquals(ULocale.getDefault().isRightToLeft(), defaultFmt.isRtlContext());
+
+ final BidiFormatter ltrFmt = new BidiFormatter.Builder(false).build();
+ assertFalse(ltrFmt.isRtlContext());
+
+ final BidiFormatter rtlFmt = new BidiFormatter.Builder(true).build();
+ assertTrue(rtlFmt.isRtlContext());
+
+ final BidiFormatter englishFmt = new BidiFormatter.Builder(Locale.ENGLISH).build();
+ assertFalse(englishFmt.isRtlContext());
+
+ final BidiFormatter arabicFmt =
+ new BidiFormatter.Builder(Locale.forLanguageTag("ar")).build();
+ assertTrue(arabicFmt.isRtlContext());
+ }
+
+ public void testBuilder_setTextDirectionHeuristic() {
+ final BidiFormatter defaultFmt = new BidiFormatter.Builder().build();
+ assertFalse(defaultFmt.isRtl(EN + HE + EN));
+
+ final BidiFormatter modifiedFmt = new BidiFormatter.Builder().setTextDirectionHeuristic(
+ TextDirectionHeuristics.ANYRTL_LTR).build();
+ assertTrue(modifiedFmt.isRtl(EN + HE + EN));
+ }
+
+ public void testCharSequenceApis() {
+ final CharSequence CS_HE = new SpannableString(HE);
+ assertEquals(true, BidiFormatter.getInstance(true).isRtl(CS_HE));
+
+ final SpannableString CS_EN_HE = new SpannableString(EN + HE);
+ final Object RELATIVE_SIZE_SPAN = new RelativeSizeSpan(1.2f);
+ CS_EN_HE.setSpan(RELATIVE_SIZE_SPAN, 0, EN.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+
+ Spanned wrapped;
+ Object[] spans;
+
+ wrapped = (Spanned) LTR_FMT.unicodeWrap(CS_EN_HE);
+ assertEquals(EN + HE + LRM, wrapped.toString());
+ spans = wrapped.getSpans(0, wrapped.length(), Object.class);
+ assertEquals(1, spans.length);
+ assertEquals(RELATIVE_SIZE_SPAN, spans[0]);
+ assertEquals(0, wrapped.getSpanStart(RELATIVE_SIZE_SPAN));
+ assertEquals(EN.length(), wrapped.getSpanEnd(RELATIVE_SIZE_SPAN));
+
+ wrapped = (Spanned) LTR_FMT.unicodeWrap(CS_EN_HE, TextDirectionHeuristics.LTR);
+ assertEquals(EN + HE + LRM, wrapped.toString());
+ spans = wrapped.getSpans(0, wrapped.length(), Object.class);
+ assertEquals(1, spans.length);
+ assertEquals(RELATIVE_SIZE_SPAN, spans[0]);
+ assertEquals(0, wrapped.getSpanStart(RELATIVE_SIZE_SPAN));
+ assertEquals(EN.length(), wrapped.getSpanEnd(RELATIVE_SIZE_SPAN));
+
+ wrapped = (Spanned) LTR_FMT.unicodeWrap(CS_EN_HE, false);
+ assertEquals(EN + HE, wrapped.toString());
+ spans = wrapped.getSpans(0, wrapped.length(), Object.class);
+ assertEquals(1, spans.length);
+ assertEquals(RELATIVE_SIZE_SPAN, spans[0]);
+ assertEquals(0, wrapped.getSpanStart(RELATIVE_SIZE_SPAN));
+ assertEquals(EN.length(), wrapped.getSpanEnd(RELATIVE_SIZE_SPAN));
+
+ wrapped = (Spanned) LTR_FMT.unicodeWrap(CS_EN_HE, TextDirectionHeuristics.LTR, false);
+ assertEquals(EN + HE, wrapped.toString());
+ spans = wrapped.getSpans(0, wrapped.length(), Object.class);
+ assertEquals(1, spans.length);
+ assertEquals(RELATIVE_SIZE_SPAN, spans[0]);
+ assertEquals(0, wrapped.getSpanStart(RELATIVE_SIZE_SPAN));
+ assertEquals(EN.length(), wrapped.getSpanEnd(RELATIVE_SIZE_SPAN));
+ }
}
diff --git a/tests/tests/text/src/android/text/cts/BoringLayoutTest.java b/tests/tests/text/src/android/text/cts/BoringLayoutTest.java
index 13465ae..d181d01 100644
--- a/tests/tests/text/src/android/text/cts/BoringLayoutTest.java
+++ b/tests/tests/text/src/android/text/cts/BoringLayoutTest.java
@@ -16,17 +16,22 @@
package android.text.cts;
+import static android.cts.util.WidgetTestUtils.sameCharSequence;
+
+import static org.mockito.Matchers.*;
+import static org.mockito.Mockito.*;
+
import android.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
import android.graphics.Canvas;
import android.graphics.Paint;
-import android.graphics.Bitmap.Config;
import android.test.AndroidTestCase;
import android.text.BoringLayout;
import android.text.BoringLayout.Metrics;
import android.text.Layout;
+import android.text.Layout.Alignment;
import android.text.TextPaint;
import android.text.TextUtils;
-import android.text.Layout.Alignment;
public class BoringLayoutTest extends AndroidTestCase {
private static final float SPACING_MULT_NO_SCALE = 1.0f;
@@ -282,7 +287,7 @@
}
public void testDraw() {
- BoringLayout boringLayout = BoringLayout.make((String)DEFAULT_CHAR_SEQUENCE,
+ BoringLayout boringLayout = BoringLayout.make(DEFAULT_CHAR_SEQUENCE,
DEFAULT_PAINT,
DEFAULT_OUTER_WIDTH,
Alignment.ALIGN_NORMAL,
@@ -291,24 +296,24 @@
DEFAULT_METRICS,
true);
- Bitmap mMutableBitmap = Bitmap.createBitmap(10, 28, Config.ARGB_8888);
- MockCanvas c = new MockCanvas(mMutableBitmap);
- boringLayout.draw(c, null, null, 0);
- assertTrue(c.isCanvasCalling);
- }
+ Bitmap mutableBitmap = Bitmap.createBitmap(10, 28, Config.ARGB_8888);
+ Canvas canvas = spy(new Canvas(mutableBitmap));
+ boringLayout.draw(canvas, null, null, 0);
+ verify(canvas, times(1)).drawText(eq(DEFAULT_CHAR_SEQUENCE.toString()),
+ anyFloat(), anyFloat(), any(Paint.class));
- private class MockCanvas extends Canvas {
- public boolean isCanvasCalling = false;
-
- public MockCanvas(Bitmap bitmap) {
- super(bitmap);
- }
-
- @Override
- public void drawText(String text, float x, float y, Paint paint) {
- super.drawText(text, x, y, paint);
- isCanvasCalling = true;
- }
+ reset(canvas);
+ boringLayout = BoringLayout.make(DEFAULT_CHAR_SEQUENCE,
+ DEFAULT_PAINT,
+ DEFAULT_OUTER_WIDTH,
+ Alignment.ALIGN_OPPOSITE,
+ SPACING_MULT_NO_SCALE,
+ SPACING_ADD_NO_SCALE,
+ DEFAULT_METRICS,
+ true);
+ boringLayout.draw(canvas, null, null, 0);
+ verify(canvas, times(1)).drawText(sameCharSequence(DEFAULT_CHAR_SEQUENCE),
+ anyInt(), anyInt(), anyFloat(), anyFloat(), any(Paint.class));
}
private static Metrics createMetrics(
diff --git a/tests/tests/text/src/android/text/cts/Editable_FactoryTest.java b/tests/tests/text/src/android/text/cts/Editable_FactoryTest.java
index 8dde216..815f5eb 100644
--- a/tests/tests/text/src/android/text/cts/Editable_FactoryTest.java
+++ b/tests/tests/text/src/android/text/cts/Editable_FactoryTest.java
@@ -18,8 +18,8 @@
import android.test.AndroidTestCase;
import android.text.Editable;
-import android.text.SpannableStringBuilder;
import android.text.Editable.Factory;
+import android.text.SpannableStringBuilder;
public class Editable_FactoryTest extends AndroidTestCase {
diff --git a/tests/tests/text/src/android/text/cts/EmojiCtsActivity.java b/tests/tests/text/src/android/text/cts/EmojiCtsActivity.java
index cdbc867..fa7e2b8 100644
--- a/tests/tests/text/src/android/text/cts/EmojiCtsActivity.java
+++ b/tests/tests/text/src/android/text/cts/EmojiCtsActivity.java
@@ -16,11 +16,10 @@
package android.text.cts;
-import android.text.cts.R;
-
import android.app.Activity;
-import android.os.Bundle;
import android.cts.util.NullWebViewUtils;
+import android.os.Bundle;
+import android.text.cts.R;
import android.webkit.WebView;
public class EmojiCtsActivity extends Activity {
diff --git a/tests/tests/text/src/android/text/cts/EmojiTest.java b/tests/tests/text/src/android/text/cts/EmojiTest.java
index 4bc586e..b67f8c9 100644
--- a/tests/tests/text/src/android/text/cts/EmojiTest.java
+++ b/tests/tests/text/src/android/text/cts/EmojiTest.java
@@ -25,11 +25,10 @@
import android.test.ActivityInstrumentationTestCase2;
import android.util.TypedValue;
import android.view.KeyEvent;
-import android.view.Menu;
import android.view.View;
-import android.widget.TextView;
-import android.widget.EditText;
import android.webkit.cts.WebViewOnUiThread;
+import android.widget.EditText;
+import android.widget.TextView;
public class EmojiTest extends ActivityInstrumentationTestCase2<EmojiCtsActivity> {
@@ -155,13 +154,9 @@
newStr = editText.getText().toString();
assertEquals(newStr.codePointCount(0, newStr.length()), origStr.length() + 1);
- runTestOnUiThread(new Runnable() {
- public void run() {
- // Delete added character by sending KEYCODE_DEL event
- editText.dispatchKeyEvent(
- new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL));
- }
- });
+ // Delete added character by sending KEYCODE_DEL event
+ runTestOnUiThread(() -> editText.dispatchKeyEvent(
+ new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL)));
getInstrumentation().waitForIdleSync();
newStr = editText.getText().toString();
diff --git a/tests/tests/text/src/android/text/cts/GetCharsTest.java b/tests/tests/text/src/android/text/cts/GetCharsTest.java
new file mode 100644
index 0000000..41e7ccc
--- /dev/null
+++ b/tests/tests/text/src/android/text/cts/GetCharsTest.java
@@ -0,0 +1,79 @@
+/*
+ * 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.cts;
+
+import android.support.test.filters.SmallTest;
+import android.test.AndroidTestCase;
+import android.text.GetChars;
+import android.text.SpannableString;
+import android.text.SpannableStringBuilder;
+import android.text.SpannedString;
+
+public class GetCharsTest extends AndroidTestCase {
+
+ // Returns an array of three GetChars objects, of three different classes:
+ // SpannableString, SpannableStringBuilder, and SpannedString.
+ private static GetChars[] makeGetChars(String s) {
+ return new GetChars[]{
+ new SpannableString(s),
+ new SpannableStringBuilder(s),
+ new SpannedString(s)};
+ }
+
+ @SmallTest
+ public void testGetChars() {
+ final GetChars[] getCharsCases = makeGetChars("\uD83D\uDE00"); // U+1F600 GRINNING FACE
+ for (GetChars getChars : getCharsCases) {
+ final char[] target = new char[getChars.length()];
+ getChars.getChars(0, getChars.length(), target, 0);
+ assertEquals('\uD83D', target[0]);
+ assertEquals('\uDE00', target[1]);
+
+ try {
+ getChars.getChars(-1, getChars.length(), target, 0);
+ fail("should throw IndexOutOfBoundsException here");
+ } catch (IndexOutOfBoundsException e) {
+ }
+
+ try {
+ getChars.getChars(1, 0, target, 0);
+ fail("should throw IndexOutOfBoundsException here");
+ } catch (IndexOutOfBoundsException e) {
+ }
+
+ try {
+ getChars.getChars(0, getChars.length() + 1, target, 0);
+ fail("should throw IndexOutOfBoundsException here");
+ } catch (IndexOutOfBoundsException e) {
+ }
+
+ try {
+ getChars.getChars(0, getChars.length(), target, -1);
+ fail("should throw IndexOutOfBoundsException here");
+ } catch (IndexOutOfBoundsException e) {
+ }
+
+ try {
+ getChars.getChars(0, getChars.length(), target, 1);
+ fail("should throw IndexOutOfBoundsException here");
+ } catch (IndexOutOfBoundsException e) {
+ }
+ }
+ }
+}
+
+
diff --git a/tests/tests/text/src/android/text/cts/HtmlTest.java b/tests/tests/text/src/android/text/cts/HtmlTest.java
index ad4d2aa..076d0a4 100644
--- a/tests/tests/text/src/android/text/cts/HtmlTest.java
+++ b/tests/tests/text/src/android/text/cts/HtmlTest.java
@@ -17,8 +17,6 @@
package android.text.cts;
import static org.hamcrest.MatcherAssert.assertThat;
-import org.hamcrest.Description;
-import org.hamcrest.BaseMatcher;
import android.graphics.Typeface;
import android.test.AndroidTestCase;
@@ -27,8 +25,6 @@
import android.text.Spannable;
import android.text.SpannableString;
import android.text.Spanned;
-import android.text.Html.ImageGetter;
-import android.text.Html.TagHandler;
import android.text.style.AlignmentSpan;
import android.text.style.BackgroundColorSpan;
import android.text.style.BulletSpan;
@@ -42,6 +38,9 @@
import android.text.style.URLSpan;
import android.text.style.UnderlineSpan;
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.Description;
+
public class HtmlTest extends AndroidTestCase {
private final static int SPAN_EXCLUSIVE_INCLUSIVE = Spannable.SPAN_EXCLUSIVE_INCLUSIVE;
diff --git a/tests/tests/text/src/android/text/cts/InputFilter_AllCapsTest.java b/tests/tests/text/src/android/text/cts/InputFilter_AllCapsTest.java
index 2eec7d7..93ee064 100644
--- a/tests/tests/text/src/android/text/cts/InputFilter_AllCapsTest.java
+++ b/tests/tests/text/src/android/text/cts/InputFilter_AllCapsTest.java
@@ -18,8 +18,8 @@
import android.test.AndroidTestCase;
import android.text.InputFilter;
-import android.text.SpannableStringBuilder;
import android.text.InputFilter.AllCaps;
+import android.text.SpannableStringBuilder;
public class InputFilter_AllCapsTest extends AndroidTestCase {
diff --git a/tests/tests/text/src/android/text/cts/InputFilter_LengthFilterTest.java b/tests/tests/text/src/android/text/cts/InputFilter_LengthFilterTest.java
index db06ef6..49006df 100644
--- a/tests/tests/text/src/android/text/cts/InputFilter_LengthFilterTest.java
+++ b/tests/tests/text/src/android/text/cts/InputFilter_LengthFilterTest.java
@@ -18,8 +18,8 @@
import android.test.AndroidTestCase;
import android.text.InputFilter;
-import android.text.SpannableStringBuilder;
import android.text.InputFilter.LengthFilter;
+import android.text.SpannableStringBuilder;
public class InputFilter_LengthFilterTest extends AndroidTestCase {
@@ -31,6 +31,8 @@
LengthFilter lengthFilter = new LengthFilter(10);
InputFilter[] filters = {lengthFilter};
+ assertEquals(10, lengthFilter.getMax());
+
// filter() implicitly invoked. If the total length > filter length, the filter will
// cut off the source CharSequence from beginning to fit the filter length.
source = "abc";
diff --git a/tests/tests/text/src/android/text/cts/LayoutTest.java b/tests/tests/text/src/android/text/cts/LayoutTest.java
index 973d883..b8e4bd1 100644
--- a/tests/tests/text/src/android/text/cts/LayoutTest.java
+++ b/tests/tests/text/src/android/text/cts/LayoutTest.java
@@ -17,15 +17,24 @@
package android.text.cts;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Path;
import android.graphics.Rect;
+import android.graphics.RectF;
+import android.support.test.filters.SmallTest;
import android.test.AndroidTestCase;
import android.text.Layout;
+import android.text.Layout.Alignment;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.TextPaint;
-import android.text.Layout.Alignment;
import android.text.style.StrikethroughSpan;
+import java.util.ArrayList;
+import java.util.List;
+
public class LayoutTest extends AndroidTestCase {
private final static int LINE_COUNT = 5;
private final static int LINE_HEIGHT = 12;
@@ -322,7 +331,7 @@
@Override
public Directions getLineDirections(int line) {
- return null;
+ return Layout.DIRS_ALL_LEFT_TO_RIGHT;
}
@Override
@@ -351,4 +360,109 @@
return 0;
}
}
+
+ @SmallTest
+ public void testGetLineWidth() {
+ Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth,
+ mAlign, mSpacingmult, mSpacingadd);
+ for (int i = 0; i < LINE_COUNT; i++) {
+ int start = layout.getLineStart(i);
+ int end = layout.getLineEnd(i);
+ String text = LAYOUT_TEXT.toString().substring(start, end);
+ assertEquals(mTextPaint.measureText(text), layout.getLineWidth(i), 1.0f);
+ }
+ }
+
+ @SmallTest
+ public void testGetCursorPath() {
+ Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth,
+ mAlign, mSpacingmult, mSpacingadd);
+ Path path = new Path();
+ final float epsilon = 1.0f;
+ for (int i = 0; i < LINE_COUNT; i++) {
+ layout.getCursorPath(i, path, LAYOUT_TEXT);
+ RectF bounds = new RectF();
+ path.computeBounds(bounds, false);
+ assertTrue(bounds.top >= layout.getLineTop(i) - epsilon);
+ assertTrue(bounds.bottom <= layout.getLineBottom(i) + epsilon);
+ }
+ }
+
+ @SmallTest
+ public void testDraw() {
+ Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth,
+ mAlign, mSpacingmult, mSpacingadd);
+ final int width = 256;
+ final int height = 256;
+ MockCanvas c = new MockCanvas(width, height);
+ layout.draw(c);
+ List<MockCanvas.DrawCommand> drawCommands = c.getDrawCommands();
+ assertEquals(LINE_COUNT, drawCommands.size());
+ for (int i = 0; i < LINE_COUNT; i++) {
+ MockCanvas.DrawCommand drawCommand = drawCommands.get(i);
+ int start = layout.getLineStart(i);
+ int end = layout.getLineEnd(i);
+ assertEquals(LAYOUT_TEXT.toString().substring(start, end), drawCommand.text);
+ float expected_y = (i + 1) * LINE_HEIGHT - LINE_DESCENT;
+ assertEquals(expected_y, drawCommand.y);
+ }
+ }
+
+ private final class MockCanvas extends Canvas {
+
+ class DrawCommand {
+ final String text;
+ final float x;
+ final float y;
+
+ DrawCommand(String text, float x, float y) {
+ this.text = text;
+ this.x = x;
+ this.y = y;
+ }
+ }
+
+ List<DrawCommand> mDrawCommands;
+
+ public MockCanvas(int width, int height) {
+ super();
+ mDrawCommands = new ArrayList<>();
+ Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ setBitmap(bitmap);
+ }
+
+ // Drawing text with either drawText or drawTextRun is valid; we don't care which.
+ // We also don't care which of the string representations is used.
+
+ @Override
+ public void drawText(String text, int start, int end, float x, float y, Paint p) {
+ mDrawCommands.add(new DrawCommand(text.substring(start, end), x, y));
+ }
+
+ @Override
+ public void drawText(CharSequence text, int start, int end, float x, float y, Paint p) {
+ drawText(text.toString(), start, end, x, y, p);
+ }
+
+ @Override
+ public void drawText(char[] text, int index, int count, float x, float y, Paint p) {
+ mDrawCommands.add(new DrawCommand(new String(text, index, count), x, y));
+ }
+
+ @Override
+ public void drawTextRun(CharSequence text, int start, int end, int contextStart,
+ int contextEnd, float x, float y, boolean isRtl, Paint paint) {
+ drawText(text, start, end, x, y, paint);
+ }
+
+ @Override
+ public void drawTextRun(char[] text, int index, int count, int contextIndex,
+ int contextCount, float x, float y, boolean isRtl, Paint paint) {
+ drawText(text, index, count, x, y, paint);
+ }
+
+ List<DrawCommand> getDrawCommands() {
+ return mDrawCommands;
+ }
+ }
}
diff --git a/tests/tests/text/src/android/text/cts/LoginFilterTest.java b/tests/tests/text/src/android/text/cts/LoginFilterTest.java
index 76f5d04..c968bfa 100644
--- a/tests/tests/text/src/android/text/cts/LoginFilterTest.java
+++ b/tests/tests/text/src/android/text/cts/LoginFilterTest.java
@@ -16,12 +16,17 @@
package android.text.cts;
-import junit.framework.TestCase;
+import static org.mockito.Matchers.*;
+import static org.mockito.Mockito.*;
+
import android.text.LoginFilter;
+import android.text.LoginFilter.UsernameFilterGeneric;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.SpannedString;
+import junit.framework.TestCase;
+
public class LoginFilterTest extends TestCase {
@Override
@@ -31,57 +36,57 @@
public void testFilter() {
CharSequence result;
- MockLoginFilter loginFilter = new MockLoginFilter();
+ LoginFilter loginFilter = spy(new UsernameFilterGeneric());
Spanned dest1 = new SpannedString("dest_without_invalid_char");
Spanned dest2 = new SpannedString("&*dest_with_invalid_char#$");
String source1 = "source_without_invalid_char";
String source2 = "+=source_with_invalid_char%!";
Spanned spannedSource = new SpannedString("&*spanned_source_with_invalid_char#$");
- assertFalse(loginFilter.isStarted());
- assertFalse(loginFilter.isStopped());
- assertEquals(0, loginFilter.getInvalidCharacterCount());
+ verify(loginFilter, never()).onStart();
+ verify(loginFilter, never()).onStop();
+ verify(loginFilter, never()).onInvalidCharacter(anyChar());
assertNull(loginFilter.filter(source1, 0, source1.length(), dest1, 0, dest1.length()));
- assertTrue(loginFilter.isStarted());
- assertTrue(loginFilter.isStopped());
- assertEquals(0, loginFilter.getInvalidCharacterCount());
+ verify(loginFilter, times(1)).onStart();
+ verify(loginFilter, times(1)).onStop();
+ verify(loginFilter, never()).onInvalidCharacter(anyChar());
- loginFilter.reset();
+ reset(loginFilter);
assertNull(loginFilter.filter(source1, 0, source1.length(), dest2, 5, 6));
- assertTrue(loginFilter.isStarted());
- assertTrue(loginFilter.isStopped());
- assertEquals(4, loginFilter.getInvalidCharacterCount());
+ verify(loginFilter, times(1)).onStart();
+ verify(loginFilter, times(1)).onStop();
+ verify(loginFilter, times(4)).onInvalidCharacter(anyChar());
- loginFilter = new MockLoginFilter(true);
+ loginFilter = spy(new UsernameFilterGeneric(true));
assertNull(loginFilter.filter(source2, 0, source2.length(),
dest1, 0, dest1.length()));
- assertTrue(loginFilter.isStarted());
- assertTrue(loginFilter.isStopped());
- assertEquals(3, loginFilter.getInvalidCharacterCount());
+ verify(loginFilter, times(1)).onStart();
+ verify(loginFilter, times(1)).onStop();
+ verify(loginFilter, times(3)).onInvalidCharacter(anyChar());
- loginFilter.reset();
+ reset(loginFilter);
assertNull(loginFilter.filter(spannedSource, 0, spannedSource.length(),
dest1, 0, dest1.length()));
- assertTrue(loginFilter.isStarted());
- assertTrue(loginFilter.isStopped());
- assertEquals(4, loginFilter.getInvalidCharacterCount());
+ verify(loginFilter, times(1)).onStart();
+ verify(loginFilter, times(1)).onStop();
+ verify(loginFilter, times(4)).onInvalidCharacter(anyChar());
- loginFilter = new MockLoginFilter(false);
+ loginFilter = spy(new UsernameFilterGeneric(false));
result = loginFilter.filter(source2, 0, source2.length(), dest1, 0, dest1.length());
assertFalse(result instanceof SpannableString);
assertEquals("+source_with_invalid_char", result.toString());
- assertTrue(loginFilter.isStarted());
- assertTrue(loginFilter.isStopped());
- assertEquals(3, loginFilter.getInvalidCharacterCount());
+ verify(loginFilter, times(1)).onStart();
+ verify(loginFilter, times(1)).onStop();
+ verify(loginFilter, times(3)).onInvalidCharacter(anyChar());
- loginFilter.reset();
+ reset(loginFilter);
result = loginFilter.filter(spannedSource, 0, spannedSource.length(),
dest1, 0, dest1.length());
assertEquals("spanned_source_with_invalid_char", result.toString());
- assertTrue(loginFilter.isStarted());
- assertTrue(loginFilter.isStopped());
- assertEquals(4, loginFilter.getInvalidCharacterCount());
+ verify(loginFilter, times(1)).onStart();
+ verify(loginFilter, times(1)).onStop();
+ verify(loginFilter, times(4)).onInvalidCharacter(anyChar());
try {
loginFilter.filter(null, 0, source1.length(), dest1, 0, dest1.length());
@@ -117,71 +122,21 @@
// This method does nothing. we only test onInvalidCharacter function here,
// the callback should be tested in testFilter()
public void testOnInvalidCharacter() {
- LoginFilter loginFilter = new MockLoginFilter();
+ LoginFilter loginFilter = new UsernameFilterGeneric();
loginFilter.onInvalidCharacter('a');
}
// This method does nothing. we only test onStop function here,
// the callback should be tested in testFilter()
public void testOnStop() {
- LoginFilter loginFilter = new MockLoginFilter();
+ LoginFilter loginFilter = new UsernameFilterGeneric();
loginFilter.onStop();
}
// This method does nothing. we only test onStart function here,
// the callback should be tested in testFilter()
public void testOnStart() {
- LoginFilter loginFilter = new LoginFilter.UsernameFilterGeneric();
+ LoginFilter loginFilter = new UsernameFilterGeneric();
loginFilter.onStart();
}
-
- private final class MockLoginFilter extends LoginFilter.UsernameFilterGeneric {
- private int mInvalidCharacterCount;
- private boolean mIsStarted = false;
- private boolean mIsStopped = false;
-
- public MockLoginFilter() {
- super();
- }
-
- public MockLoginFilter(boolean appendInvalid) {
- super(appendInvalid);
- }
-
- @Override
- public void onInvalidCharacter(char c) {
- mInvalidCharacterCount++;
- super.onInvalidCharacter(c);
- }
-
- public int getInvalidCharacterCount() {
- return mInvalidCharacterCount;
- }
-
- @Override
- public void onStart() {
- mIsStarted = true;
- super.onStart();
- }
-
- public boolean isStarted() {
- return mIsStarted;
- }
-
- @Override
- public void onStop() {
- mIsStopped = true;
- super.onStop();
- }
-
- public boolean isStopped() {
- return mIsStopped;
- }
-
- public void reset() {
- mInvalidCharacterCount = 0;
- mIsStarted = false;
- mIsStopped = false;
- }
- }
}
diff --git a/tests/tests/text/src/android/text/cts/SpannableStringBuilderSpanTest.java b/tests/tests/text/src/android/text/cts/SpannableStringBuilderSpanTest.java
index 14bcc8e..020dd88 100644
--- a/tests/tests/text/src/android/text/cts/SpannableStringBuilderSpanTest.java
+++ b/tests/tests/text/src/android/text/cts/SpannableStringBuilderSpanTest.java
@@ -16,8 +16,6 @@
package android.text.cts;
-import java.util.ArrayList;
-
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
import android.text.Html;
@@ -29,6 +27,8 @@
import android.text.style.ParagraphStyle;
import android.text.style.QuoteSpan;
+import java.util.ArrayList;
+
/**
* Test {@link SpannableStringBuilder}.
*/
diff --git a/tests/tests/text/src/android/text/cts/SpannableStringBuilderTest.java b/tests/tests/text/src/android/text/cts/SpannableStringBuilderTest.java
index 6dee5e9..09b5dbf 100644
--- a/tests/tests/text/src/android/text/cts/SpannableStringBuilderTest.java
+++ b/tests/tests/text/src/android/text/cts/SpannableStringBuilderTest.java
@@ -21,19 +21,20 @@
import android.test.suitebuilder.annotation.SmallTest;
import android.text.Editable;
import android.text.InputFilter;
+import android.text.Selection;
import android.text.SpannableString;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.TextWatcher;
import android.text.style.BulletSpan;
-import android.text.style.ParagraphStyle;
import android.text.style.QuoteSpan;
-import android.text.style.RelativeSizeSpan;
import android.text.style.StrikethroughSpan;
import android.text.style.SubscriptSpan;
import android.text.style.TabStopSpan;
import android.text.style.UnderlineSpan;
+import java.util.Arrays;
+
/**
* Test {@link SpannableStringBuilder}.
*/
@@ -377,6 +378,28 @@
}
}
+ @SmallTest
+ public void testAppend_textWithSpan() {
+ final QuoteSpan span = new QuoteSpan();
+ final SpannableStringBuilder builder = new SpannableStringBuilder("hello ");
+ final int spanStart = builder.length();
+ builder.append("planet", span, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ final int spanEnd = builder.length();
+ builder.append(" earth");
+
+ assertEquals("SpannableStringBuilder.append should append text to existing whole text",
+ "hello planet earth", builder.toString());
+
+ final Object[] spans = builder.getSpans(0, builder.length(), Object.class);
+ assertNotNull("Appended text included a Quote span", spans);
+ assertEquals("Appended text included a Quote span", 1, spans.length);
+ assertSame("Should be the same span instance", span, spans[0]);
+ assertEquals("Appended span should start at appended text start",
+ spanStart, builder.getSpanStart(spans[0]));
+ assertEquals("Appended span should end at appended text end",
+ spanEnd, builder.getSpanEnd(spans[0]));
+ }
+
public void testClearSpans() {
SpannableStringBuilder builder = new SpannableStringBuilder("hello, world");
@@ -688,6 +711,19 @@
assertEquals(0, builder.length());
}
+ public void testReplace_shouldNotThrowIndexOutOfBoundsExceptionForLongText() {
+ final char[] charArray = new char[75000];
+ Arrays.fill(charArray, 'a');
+ final String text = new String(charArray, 0, 50000);
+ final String copiedText = new String(charArray);
+ final SpannableStringBuilder spannable = new SpannableStringBuilder(text);
+ Selection.setSelection(spannable, text.length());
+
+ spannable.replace(0, text.length(), copiedText);
+
+ assertEquals(copiedText.length(), spannable.length());
+ }
+
public void testDelete() {
SpannableStringBuilder builder = new SpannableStringBuilder("hello,world");
assertEquals("hello,world", builder.toString());
diff --git a/tests/tests/text/src/android/text/cts/SpannableStringTest.java b/tests/tests/text/src/android/text/cts/SpannableStringTest.java
index 1eca046..81dbf9b 100644
--- a/tests/tests/text/src/android/text/cts/SpannableStringTest.java
+++ b/tests/tests/text/src/android/text/cts/SpannableStringTest.java
@@ -16,8 +16,8 @@
package android.text.cts;
+import android.support.test.filters.SmallTest;
import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.style.LocaleSpan;
@@ -28,6 +28,7 @@
public class SpannableStringTest extends AndroidTestCase {
+ @SmallTest
public void testConstructor() {
new SpannableString("test");
@@ -38,6 +39,7 @@
}
}
+ @SmallTest
public void testValueOf() {
String text = "test valueOf";
SpannableString spannable = SpannableString.valueOf(text);
@@ -54,6 +56,7 @@
}
}
+ @SmallTest
public void testSetSpan() {
String text = "hello, world";
SpannableString spannable = new SpannableString(text);
@@ -84,6 +87,7 @@
}
}
+ @SmallTest
public void testRemoveSpan() {
SpannableString spannable = new SpannableString("hello, world");
@@ -107,6 +111,7 @@
assertEquals(0, spannable.getSpanFlags(underlineSpan));
}
+ @SmallTest
public void testSubSequence() {
String text = "hello, world";
SpannableString spannable = new SpannableString(text);
diff --git a/tests/tests/text/src/android/text/cts/Spannable_FactoryTest.java b/tests/tests/text/src/android/text/cts/Spannable_FactoryTest.java
index eca6d6d..e3ad7ca 100644
--- a/tests/tests/text/src/android/text/cts/Spannable_FactoryTest.java
+++ b/tests/tests/text/src/android/text/cts/Spannable_FactoryTest.java
@@ -18,8 +18,8 @@
import android.test.AndroidTestCase;
import android.text.Spannable;
-import android.text.SpannableString;
import android.text.Spannable.Factory;
+import android.text.SpannableString;
public class Spannable_FactoryTest extends AndroidTestCase {
diff --git a/tests/tests/text/src/android/text/cts/SpannedTest.java b/tests/tests/text/src/android/text/cts/SpannedTest.java
new file mode 100644
index 0000000..949ddb5
--- /dev/null
+++ b/tests/tests/text/src/android/text/cts/SpannedTest.java
@@ -0,0 +1,106 @@
+/*
+ * 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.cts;
+
+import android.support.test.filters.SmallTest;
+import android.test.AndroidTestCase;
+import android.text.Spannable;
+import android.text.SpannableString;
+import android.text.SpannableStringBuilder;
+import android.text.Spanned;
+import android.text.SpannedString;
+import android.text.style.LocaleSpan;
+import android.text.style.QuoteSpan;
+import android.text.style.UnderlineSpan;
+
+import java.util.Locale;
+
+public class SpannedTest extends AndroidTestCase {
+
+ // Returns an array of three Spanned objects, of three different classes:
+ // SpannableString, SpannableStringBuilder, and SpannedString.
+ private static Spanned[] makeSpanned(CharSequence s) {
+ return new Spanned[]{
+ new SpannableString(s),
+ new SpannableStringBuilder(s),
+ new SpannedString(s)};
+ }
+
+ @SmallTest
+ public void testCharAt() {
+ final Spanned[] spannedCases = makeSpanned("\uD83D\uDE00"); // U+1F600 GRINNING FACE
+ for (Spanned spanned : spannedCases) {
+ assertEquals('\uD83D', spanned.charAt(0));
+ assertEquals('\uDE00', spanned.charAt(1));
+
+ try {
+ spanned.charAt(-1);
+ fail("should throw IndexOutOfBoundsException here");
+ } catch (IndexOutOfBoundsException e) {
+ }
+
+ try {
+ spanned.charAt(spanned.length());
+ fail("should throw IndexOutOfBoundsException here");
+ } catch (IndexOutOfBoundsException e) {
+ }
+ }
+ }
+
+ @SmallTest
+ public void testNextSpanTransition() {
+ final int flags = Spannable.SPAN_INCLUSIVE_INCLUSIVE;
+ final SpannableString text = new SpannableString("0123 5678");
+ text.setSpan(new QuoteSpan(), 0, 4, flags);
+ text.setSpan(new LocaleSpan((Locale) null), 2, 7, flags);
+ text.setSpan(new UnderlineSpan(), 5, text.length(), flags);
+ // Now there are span transitions at 0, 2, 4, 5, 7, and the end of string.
+
+ final Spanned[] spannedCases = makeSpanned(text);
+ for (Spanned spanned : spannedCases) {
+
+ assertEquals(4, spanned.nextSpanTransition(1, spanned.length(), QuoteSpan.class));
+ assertEquals(spanned.length(),
+ spanned.nextSpanTransition(4, spanned.length(), QuoteSpan.class));
+ assertEquals(5, spanned.nextSpanTransition(4, spanned.length(), UnderlineSpan.class));
+
+ assertEquals(2, spanned.nextSpanTransition(0, spanned.length(), Object.class));
+ assertEquals(4, spanned.nextSpanTransition(2, spanned.length(), Object.class));
+ assertEquals(5, spanned.nextSpanTransition(4, spanned.length(), Object.class));
+ assertEquals(7, spanned.nextSpanTransition(5, spanned.length(), Object.class));
+ assertEquals(spanned.length(),
+ spanned.nextSpanTransition(7, spanned.length(), Object.class));
+
+ // Test that 'null' catches all spans.
+ assertEquals(2, spanned.nextSpanTransition(0, spanned.length(), null));
+ assertEquals(4, spanned.nextSpanTransition(2, spanned.length(), null));
+ assertEquals(5, spanned.nextSpanTransition(4, spanned.length(), null));
+ assertEquals(7, spanned.nextSpanTransition(5, spanned.length(), null));
+ assertEquals(spanned.length(), spanned.nextSpanTransition(7, spanned.length(), null));
+
+ // 'start' can be negative.
+ assertEquals(0, spanned.nextSpanTransition(-1, spanned.length(), QuoteSpan.class));
+
+ // 'limit' can be high.
+ final int highLimit = spanned.length() + 1;
+ assertEquals(highLimit, spanned.nextSpanTransition(5, highLimit, QuoteSpan.class));
+
+ // 'limit' can be lower than 'start'. In such a case, limit should be returned.
+ assertEquals(1, spanned.nextSpanTransition(5, 1, QuoteSpan.class));
+ }
+ }
+}
diff --git a/tests/tests/text/src/android/text/cts/StaticLayoutLineBreakingTest.java b/tests/tests/text/src/android/text/cts/StaticLayoutLineBreakingTest.java
index 6e89ee5..d990ffb 100644
--- a/tests/tests/text/src/android/text/cts/StaticLayoutLineBreakingTest.java
+++ b/tests/tests/text/src/android/text/cts/StaticLayoutLineBreakingTest.java
@@ -17,12 +17,12 @@
package android.text.cts;
import android.test.AndroidTestCase;
+import android.text.Layout.Alignment;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.StaticLayout;
import android.text.TextDirectionHeuristics;
import android.text.TextPaint;
-import android.text.Layout.Alignment;
import android.text.style.MetricAffectingSpan;
import android.util.Log;
diff --git a/tests/tests/text/src/android/text/cts/StaticLayoutTest.java b/tests/tests/text/src/android/text/cts/StaticLayoutTest.java
index ed3f1ea..f66f4c0 100644
--- a/tests/tests/text/src/android/text/cts/StaticLayoutTest.java
+++ b/tests/tests/text/src/android/text/cts/StaticLayoutTest.java
@@ -100,8 +100,6 @@
maxLines);
}
-
-
/**
* Constructor test
*/
@@ -523,37 +521,53 @@
TextUtils.TruncateAt.MARQUEE, 1);
assertTrue(layout.getEllipsisCount(0) == 0);
}
+ {
+ final String text = "\u3042" // HIRAGANA LETTER A
+ + "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz";
+ final float textWidth = mDefaultPaint.measureText(text);
+ final int halfWidth = (int)(textWidth / 2.0f);
+ {
+ StaticLayout layout = new StaticLayout(text, 0, text.length(), mDefaultPaint,
+ halfWidth, DEFAULT_ALIGN, TextDirectionHeuristics.FIRSTSTRONG_LTR,
+ SPACE_MULTI, SPACE_ADD, false, TextUtils.TruncateAt.END, halfWidth, 1);
+ assertTrue(layout.getEllipsisCount(0) > 0);
+ assertTrue(layout.getEllipsisStart(0) > 0);
+ }
+ {
+ StaticLayout layout = new StaticLayout(text, 0, text.length(), mDefaultPaint,
+ halfWidth, DEFAULT_ALIGN, TextDirectionHeuristics.FIRSTSTRONG_LTR,
+ SPACE_MULTI, SPACE_ADD, false, TextUtils.TruncateAt.START, halfWidth, 1);
+ assertTrue(layout.getEllipsisCount(0) > 0);
+ assertEquals(0, mDefaultLayout.getEllipsisStart(0));
+ }
+ {
+ StaticLayout layout = new StaticLayout(text, 0, text.length(), mDefaultPaint,
+ halfWidth, DEFAULT_ALIGN, TextDirectionHeuristics.FIRSTSTRONG_LTR,
+ SPACE_MULTI, SPACE_ADD, false, TextUtils.TruncateAt.MIDDLE, halfWidth, 1);
+ assertTrue(layout.getEllipsisCount(0) > 0);
+ assertTrue(layout.getEllipsisStart(0) > 0);
+ }
+ {
+ StaticLayout layout = new StaticLayout(text, 0, text.length(), mDefaultPaint,
+ halfWidth, DEFAULT_ALIGN, TextDirectionHeuristics.FIRSTSTRONG_LTR,
+ SPACE_MULTI, SPACE_ADD, false, TextUtils.TruncateAt.MARQUEE, halfWidth, 1);
+ assertEquals(0, layout.getEllipsisCount(0));
+ }
+ }
- final String text = "\u3042" // HIRAGANA LETTER A
- + "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz";
- final float textWidth = mDefaultPaint.measureText(text);
- final int halfWidth = (int)(textWidth / 2.0f);
{
- StaticLayout layout = new StaticLayout(text, 0, text.length(), mDefaultPaint,
- halfWidth, DEFAULT_ALIGN, TextDirectionHeuristics.FIRSTSTRONG_LTR,
- SPACE_MULTI, SPACE_ADD, false, TextUtils.TruncateAt.END, halfWidth, 1);
- assertTrue(layout.getEllipsisCount(0) > 0);
- assertTrue(layout.getEllipsisStart(0) > 0);
- }
- {
- StaticLayout layout = new StaticLayout(text, 0, text.length(), mDefaultPaint,
- halfWidth, DEFAULT_ALIGN, TextDirectionHeuristics.FIRSTSTRONG_LTR,
- SPACE_MULTI, SPACE_ADD, false, TextUtils.TruncateAt.START, halfWidth, 1);
- assertTrue(layout.getEllipsisCount(0) > 0);
- assertEquals(0, mDefaultLayout.getEllipsisStart(0));
- }
- {
- StaticLayout layout = new StaticLayout(text, 0, text.length(), mDefaultPaint,
- halfWidth, DEFAULT_ALIGN, TextDirectionHeuristics.FIRSTSTRONG_LTR,
- SPACE_MULTI, SPACE_ADD, false, TextUtils.TruncateAt.MIDDLE, halfWidth, 1);
- assertTrue(layout.getEllipsisCount(0) > 0);
- assertTrue(layout.getEllipsisStart(0) > 0);
- }
- {
- StaticLayout layout = new StaticLayout(text, 0, text.length(), mDefaultPaint,
- halfWidth, DEFAULT_ALIGN, TextDirectionHeuristics.FIRSTSTRONG_LTR,
- SPACE_MULTI, SPACE_ADD, false, TextUtils.TruncateAt.MARQUEE, halfWidth, 1);
- assertEquals(0, layout.getEllipsisCount(0));
+ // The white spaces in this text will be trailing if maxLines is larger than 1, but
+ // width of the trailing white spaces must not be ignored if ellipsis is applied.
+ final String text = "abc def";
+ final float textWidth = mDefaultPaint.measureText(text);
+ final int halfWidth = (int)(textWidth / 2.0f);
+ {
+ StaticLayout layout = new StaticLayout(text, 0, text.length(), mDefaultPaint,
+ halfWidth, DEFAULT_ALIGN, TextDirectionHeuristics.FIRSTSTRONG_LTR,
+ SPACE_MULTI, SPACE_ADD, false, TextUtils.TruncateAt.END, halfWidth, 1);
+ assertTrue(layout.getEllipsisCount(0) > 0);
+ assertTrue(layout.getEllipsisStart(0) > 0);
+ }
}
}
diff --git a/tests/tests/text/src/android/text/cts/TextPaintTest.java b/tests/tests/text/src/android/text/cts/TextPaintTest.java
index fef492b..85fb300 100644
--- a/tests/tests/text/src/android/text/cts/TextPaintTest.java
+++ b/tests/tests/text/src/android/text/cts/TextPaintTest.java
@@ -17,6 +17,7 @@
package android.text.cts;
import android.graphics.Color;
+import android.graphics.Paint;
import android.graphics.Typeface;
import android.test.AndroidTestCase;
import android.text.TextPaint;
@@ -37,6 +38,11 @@
textPaint = new TextPaint(TextPaint.DITHER_FLAG);
assertEquals((TextPaint.DITHER_FLAG | DEFAULT_PAINT_FLAGS),
textPaint.getFlags());
+
+ final Paint paint = new Paint();
+ paint.setTextSize(42f);
+ textPaint = new TextPaint(paint);
+ assertEquals(paint.getTextSize(), textPaint.getTextSize());
}
public void testSet() {
diff --git a/tests/tests/text/src/android/text/cts/TextUtilsTest.java b/tests/tests/text/src/android/text/cts/TextUtilsTest.java
index 0da1eb4..77aa845 100644
--- a/tests/tests/text/src/android/text/cts/TextUtilsTest.java
+++ b/tests/tests/text/src/android/text/cts/TextUtilsTest.java
@@ -16,16 +16,19 @@
package android.text.cts;
-
import static android.view.View.LAYOUT_DIRECTION_LTR;
import static android.view.View.LAYOUT_DIRECTION_RTL;
+import static org.mockito.Matchers.*;
+import static org.mockito.Mockito.*;
+
+import android.content.Context;
import android.content.res.ColorStateList;
-import android.graphics.Canvas;
+import android.content.res.Configuration;
+import android.content.res.Resources;
import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.Paint.FontMetricsInt;
import android.graphics.Typeface;
+import android.os.LocaleList;
import android.os.Parcel;
import android.os.Parcelable;
import android.test.AndroidTestCase;
@@ -41,10 +44,11 @@
import android.text.style.ReplacementSpan;
import android.text.style.TextAppearanceSpan;
import android.text.style.URLSpan;
-import android.util.Log;
import android.util.StringBuilderPrinter;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
import java.util.Locale;
import java.util.regex.Pattern;
@@ -80,6 +84,109 @@
return re.substring(0, re.indexOf("x"));
}
+ /**
+ * @return the number of times the code unit appears in the CharSequence.
+ */
+ private int countChars(CharSequence s, char c) {
+ int count = 0;
+ for (int i = 0; i < s.length(); i++) {
+ if (s.charAt(i) == c) {
+ count++;
+ }
+ }
+ return count;
+ }
+
+ public void testListEllipsize() {
+ final Context context = getContext();
+ final TextPaint paint = new TextPaint();
+ final int moreId = R.plurals.list_ellipsize_test; // "one more" for 1, "%d more" for other
+
+ final List fullList = Arrays.asList("A", "B", "C", "D", "E", "F", "G", "H", "I", "J");
+ final String separator = ", ";
+ final String fullString = TextUtils.join(separator, fullList);
+ final float fullWidth = paint.measureText(fullString);
+ assertEquals("",
+ TextUtils.listEllipsize(context, null, separator, paint, fullWidth, moreId));
+
+ final List<CharSequence> emptyList = new ArrayList<CharSequence>();
+ assertEquals("",
+ TextUtils.listEllipsize(context, emptyList, separator, paint, fullWidth, moreId));
+
+ // Null context should cause ellipsis to be used at the end.
+ final String ellipsizedWithNull = TextUtils.listEllipsize(
+ null, fullList, separator, paint, fullWidth / 2, 0).toString();
+ assertTrue(ellipsizedWithNull.endsWith(getEllipsis()));
+
+ // Test that the empty string gets returned if there's no space.
+ assertEquals("",
+ TextUtils.listEllipsize(context, fullList, separator, paint, 1.0f, moreId));
+
+ // Test that the full string itself can get returned if there's enough space.
+ assertEquals(fullString,
+ TextUtils.listEllipsize(context, fullList, separator, paint, fullWidth, moreId)
+ .toString());
+ assertEquals(fullString,
+ TextUtils.listEllipsize(context, fullList, separator, paint, fullWidth * 2,
+ moreId).toString());
+
+ final float epsilon = fullWidth / 20;
+ for (float width = epsilon; width < fullWidth - epsilon / 2; width += epsilon) {
+ final String ellipsized = TextUtils.listEllipsize(
+ context, fullList, separator, paint, width, moreId).toString();
+ // Since we don't have the full space, test that we are not getting the full string.
+ assertFalse(fullString.equals(ellipsized));
+
+ if (!ellipsized.isEmpty()) {
+ assertTrue(ellipsized.endsWith(" more"));
+ // Test that the number of separators (which equals the number of output elements),
+ // plus the number output before more always equals the number of original elements.
+ final int lastSpace = ellipsized.lastIndexOf(' ');
+ final int penultimateSpace = ellipsized.lastIndexOf(' ', lastSpace - 1);
+ assertEquals(',', ellipsized.charAt(penultimateSpace - 1));
+ final String moreCountString = ellipsized.substring(
+ penultimateSpace + 1, lastSpace);
+ final int moreCount = (moreCountString.equals("one"))
+ ? 1 : Integer.parseInt(moreCountString);
+ final int commaCount = countChars(ellipsized, ',');
+ assertEquals(fullList.size(), commaCount + moreCount);
+ }
+ }
+}
+
+ public void testListEllipsize_rtl() {
+ final Context context = getContext();
+ final Resources res = context.getResources();
+ final Configuration newConfig = new Configuration(res.getConfiguration());
+
+ // save the locales and set them to just Arabic
+ final LocaleList previousLocales = newConfig.getLocales();
+ newConfig.setLocales(LocaleList.forLanguageTags("ar"));
+ res.updateConfiguration(newConfig, null);
+
+ try {
+ final TextPaint paint = new TextPaint();
+ final int moreId = R.plurals.list_ellipsize_test; // "one more" for 1, else "%d more"
+ final String RLM = "\u200F";
+ final String LRE = "\u202A";
+ final String PDF = "\u202C";
+
+ final List fullList = Arrays.asList("A", "B");
+ final String separator = ", ";
+ final String expectedString =
+ RLM + LRE + "A" + PDF + RLM + ", " + RLM + LRE + "B" + PDF + RLM;
+ final float enoughWidth = paint.measureText(expectedString);
+
+ assertEquals(expectedString,
+ TextUtils.listEllipsize(context, fullList, separator, paint, enoughWidth,
+ moreId).toString());
+ } finally {
+ // Restore the original locales
+ newConfig.setLocales(previousLocales);
+ res.updateConfiguration(newConfig, null);
+ }
+ }
+
public void testCommaEllipsize() {
TextPaint p = new TextPaint();
String text = "long, string, to, truncate";
@@ -887,10 +994,6 @@
private class MockCharSequence implements CharSequence {
private char mText[];
- public MockCharSequence() {
- this("");
- }
-
public MockCharSequence(String text) {
mText = text.toCharArray();
}
@@ -932,7 +1035,8 @@
TextUtils.getOffsetAfter(text, POS_FIRST_DBFF));
// the CharSequence string has a span.
- MockReplacementSpan mockReplacementSpan = new MockReplacementSpan();
+ ReplacementSpan mockReplacementSpan = mock(ReplacementSpan.class);
+ when(mockReplacementSpan.getSize(any(), any(), anyInt(), anyInt(), any())).thenReturn(0);
text.setSpan(mockReplacementSpan, POS_FIRST_D800 - 1, text.length() - 1,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
assertEquals(text.length() - 1, TextUtils.getOffsetAfter(text, POS_FIRST_D800));
@@ -957,21 +1061,6 @@
}
}
- /**
- * MockReplacementSpan for test.
- */
- private class MockReplacementSpan extends ReplacementSpan {
- @Override
- public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top,
- int y, int bottom, Paint paint) {
- }
-
- @Override
- public int getSize(Paint paint, CharSequence text, int start, int end, FontMetricsInt fm) {
- return 0;
- }
- }
-
public void testGetOffsetBefore() {
// the first '\uDC00' is index 10, the second 'uDC00' is index 17
// the '\uDFFF' is index 27
@@ -993,7 +1082,8 @@
TextUtils.getOffsetBefore(text, POS_FIRST_DFFF + 1));
// the CharSequence string has a span.
- MockReplacementSpan mockReplacementSpan = new MockReplacementSpan();
+ ReplacementSpan mockReplacementSpan = mock(ReplacementSpan.class);
+ when(mockReplacementSpan.getSize(any(), any(), anyInt(), anyInt(), any())).thenReturn(0);
text.setSpan(mockReplacementSpan, 0, POS_FIRST_DC00 + 1,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
assertEquals(0, TextUtils.getOffsetBefore(text, POS_FIRST_DC00));
diff --git a/tests/tests/text/src/android/text/cts/TextUtils_SimpleStringSplitterTest.java b/tests/tests/text/src/android/text/cts/TextUtils_SimpleStringSplitterTest.java
index 70bfe54..fb58c3c 100644
--- a/tests/tests/text/src/android/text/cts/TextUtils_SimpleStringSplitterTest.java
+++ b/tests/tests/text/src/android/text/cts/TextUtils_SimpleStringSplitterTest.java
@@ -16,11 +16,11 @@
package android.text.cts;
-import java.util.Iterator;
-
import android.test.AndroidTestCase;
import android.text.TextUtils.SimpleStringSplitter;
+import java.util.Iterator;
+
/**
* Test {@link SimpleStringSplitter}.
*/
diff --git a/tests/tests/text/src/android/text/format/cts/FormatterTest.java b/tests/tests/text/src/android/text/format/cts/FormatterTest.java
index 6acfb84..5ae19e7 100644
--- a/tests/tests/text/src/android/text/format/cts/FormatterTest.java
+++ b/tests/tests/text/src/android/text/format/cts/FormatterTest.java
@@ -20,16 +20,18 @@
import java.math.MathContext;
import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
import android.text.format.Formatter;
public class FormatterTest extends AndroidTestCase {
+ @SmallTest
public void testFormatFileSize() {
// test null Context
assertEquals("", Formatter.formatFileSize(null, 0));
MathContext mc = MathContext.DECIMAL64;
- BigDecimal bd = new BigDecimal((long) 1024, mc);
+ BigDecimal bd = new BigDecimal((long) 1000, mc);
// test different long values with various length
assertEquals("0 B", Formatter.formatFileSize(mContext, 0));
@@ -39,9 +41,9 @@
assertEquals("99 B", Formatter.formatFileSize(mContext, 99));
assertEquals("100 B", Formatter.formatFileSize(mContext, 100));
assertEquals("900 B", Formatter.formatFileSize(mContext, 900));
- assertEquals("0.88 KB", Formatter.formatFileSize(mContext, 901));
+ assertEquals("0.90 kB", Formatter.formatFileSize(mContext, 901));
- assertEquals("1.00 KB", Formatter.formatFileSize(mContext, bd.pow(1).longValue()));
+ assertEquals("1.00 kB", Formatter.formatFileSize(mContext, bd.pow(1).longValue()));
assertEquals("1.00 MB", Formatter.formatFileSize(mContext, bd.pow(2).longValue()));
@@ -51,12 +53,13 @@
assertEquals("1.00 PB", Formatter.formatFileSize(mContext, bd.pow(5).longValue()));
- assertEquals("1024 PB", Formatter.formatFileSize(mContext, bd.pow(6).longValue()));
+ assertEquals("1000 PB", Formatter.formatFileSize(mContext, bd.pow(6).longValue()));
// test Negative value
assertEquals("-1 B", Formatter.formatFileSize(mContext, -1));
}
+ @SmallTest
public void testFormatIpAddress() {
assertEquals("1.0.168.192", Formatter.formatIpAddress(0xC0A80001));
assertEquals("1.0.0.127", Formatter.formatIpAddress(0x7F000001));
diff --git a/tests/tests/text/src/android/text/method/cts/ArrowKeyMovementMethodTest.java b/tests/tests/text/src/android/text/method/cts/ArrowKeyMovementMethodTest.java
index b82b955..08c5596 100644
--- a/tests/tests/text/src/android/text/method/cts/ArrowKeyMovementMethodTest.java
+++ b/tests/tests/text/src/android/text/method/cts/ArrowKeyMovementMethodTest.java
@@ -68,15 +68,13 @@
initTextViewWithNullLayout();
- getInstrumentation().runOnMainSync(new Runnable() {
- public void run() {
- getActivity().getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON |
- WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
+ getInstrumentation().runOnMainSync(() -> {
+ getActivity().getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON |
+ WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
- getActivity().setContentView(mTextView);
- mTextView.setFocusable(true);
- mTextView.requestFocus();
- }
+ getActivity().setContentView(mTextView);
+ mTextView.setFocusable(true);
+ mTextView.requestFocus();
});
getInstrumentation().waitForIdleSync();
assertNotNull(mTextView.getLayout());
@@ -111,47 +109,35 @@
* and checking the assertion at last.
*/
assertSelection(-1);
- runTestOnUiThread(new Runnable() {
- public void run() {
- Selection.removeSelection(mEditable);
- mArrowKeyMovementMethod.onTakeFocus(mTextView, mEditable, View.FOCUS_UP);
- }
+ runTestOnUiThread(() -> {
+ Selection.removeSelection(mEditable);
+ mArrowKeyMovementMethod.onTakeFocus(mTextView, mEditable, View.FOCUS_UP);
});
getInstrumentation().waitForIdleSync();
assertSelection(END_OF_ALL_TEXT);
- runTestOnUiThread(new Runnable() {
- public void run() {
- Selection.removeSelection(mEditable);
- mArrowKeyMovementMethod.onTakeFocus(mTextView, mEditable, View.FOCUS_LEFT);
- }
+ runTestOnUiThread(() -> {
+ Selection.removeSelection(mEditable);
+ mArrowKeyMovementMethod.onTakeFocus(mTextView, mEditable, View.FOCUS_LEFT);
});
getInstrumentation().waitForIdleSync();
assertSelection(END_OF_ALL_TEXT);
- runTestOnUiThread(new Runnable() {
- public void run() {
- mTextView.setSingleLine();
- }
- });
+ runTestOnUiThread(mTextView::setSingleLine);
// wait until the textView gets layout
getInstrumentation().waitForIdleSync();
assertNotNull(mTextView.getLayout());
assertEquals(1, mTextView.getLayout().getLineCount());
- runTestOnUiThread(new Runnable() {
- public void run() {
- Selection.removeSelection(mEditable);
- mArrowKeyMovementMethod.onTakeFocus(mTextView, mEditable, View.FOCUS_UP);
- }
+ runTestOnUiThread(() -> {
+ Selection.removeSelection(mEditable);
+ mArrowKeyMovementMethod.onTakeFocus(mTextView, mEditable, View.FOCUS_UP);
});
assertSelection(END_OF_ALL_TEXT);
- runTestOnUiThread(new Runnable() {
- public void run() {
- Selection.removeSelection(mEditable);
- mArrowKeyMovementMethod.onTakeFocus(mTextView, mEditable, View.FOCUS_LEFT);
- }
+ runTestOnUiThread(() -> {
+ Selection.removeSelection(mEditable);
+ mArrowKeyMovementMethod.onTakeFocus(mTextView, mEditable, View.FOCUS_LEFT);
});
assertSelection(END_OF_ALL_TEXT);
}
diff --git a/tests/tests/text/src/android/text/method/cts/BackspaceTest.java b/tests/tests/text/src/android/text/method/cts/BackspaceTest.java
index 1705326..3a66e81 100644
--- a/tests/tests/text/src/android/text/method/cts/BackspaceTest.java
+++ b/tests/tests/text/src/android/text/method/cts/BackspaceTest.java
@@ -35,22 +35,16 @@
// Sync the state to the TextView and call onKeyDown with KEYCODE_DEL key event.
// Then update the state to the result of TextView.
private void backspace(final EditorState state, int modifiers) {
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- mTextView.setText(state.mText, BufferType.EDITABLE);
- mTextView.setKeyListener(mKeyListener);
- mTextView.setSelection(state.mSelectionStart, state.mSelectionEnd);
- }
+ mInstrumentation.runOnMainSync(() -> {
+ mTextView.setText(state.mText, BufferType.EDITABLE);
+ mTextView.setKeyListener(mKeyListener);
+ mTextView.setSelection(state.mSelectionStart, state.mSelectionEnd);
});
mInstrumentation.waitForIdleSync();
assertTrue(mTextView.hasWindowFocus());
final KeyEvent keyEvent = getKey(KeyEvent.KEYCODE_DEL, modifiers);
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- mTextView.onKeyDown(keyEvent.getKeyCode(), keyEvent);
- }
- });
+ mInstrumentation.runOnMainSync(() -> mTextView.onKeyDown(keyEvent.getKeyCode(), keyEvent));
mInstrumentation.waitForIdleSync();
state.mText = mTextView.getText();
@@ -59,6 +53,31 @@
}
@SmallTest
+ public void testCRLF() {
+ EditorState state = new EditorState();
+
+ // U+000A is LINE FEED.
+ state.setByString("U+000A |");
+ backspace(state, 0);
+ state.assertEquals("|");
+
+ // U+000D is CARRIAGE RETURN.
+ state.setByString("U+000D |");
+ backspace(state, 0);
+ state.assertEquals("|");
+
+ state.setByString("U+000D U+000A |");
+ backspace(state, 0);
+ state.assertEquals("|");
+
+ state.setByString("U+000A U+000D |");
+ backspace(state, 0);
+ state.assertEquals("U+000A |");
+ backspace(state, 0);
+ state.assertEquals("|");
+ }
+
+ @SmallTest
public void testSurrogatePairs() {
EditorState state = new EditorState();
diff --git a/tests/tests/text/src/android/text/method/cts/BaseKeyListenerTest.java b/tests/tests/text/src/android/text/method/cts/BaseKeyListenerTest.java
index d913cf9..146f3d5 100644
--- a/tests/tests/text/src/android/text/method/cts/BaseKeyListenerTest.java
+++ b/tests/tests/text/src/android/text/method/cts/BaseKeyListenerTest.java
@@ -16,12 +16,13 @@
package android.text.method.cts;
-import android.cts.util.KeyEventUtil;
+import android.cts.util.CtsKeyEventUtil;
import android.os.SystemClock;
import android.text.Editable;
import android.text.InputType;
import android.text.Selection;
import android.text.Spannable;
+import android.text.SpannableStringBuilder;
import android.text.method.BaseKeyListener;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
@@ -33,20 +34,12 @@
public class BaseKeyListenerTest extends KeyListenerTestCase {
private static final CharSequence TEST_STRING = "123456";
- private KeyEventUtil mKeyEventUtil;
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mKeyEventUtil = new KeyEventUtil(getInstrumentation());
- }
-
public void testBackspace() {
testBackspace(0);
}
private void testBackspace(int modifiers) {
- final MockBaseKeyListener mockBaseKeyListener = new MockBaseKeyListener();
+ final BaseKeyListener mockBaseKeyListener = new MockBaseKeyListener();
final KeyEvent event = getKey(KeyEvent.KEYCODE_DEL, modifiers);
Editable content = Editable.Factory.getInstance().newEditable(TEST_STRING);
@@ -108,7 +101,7 @@
}
public void testBackspace_withAlt() {
- final MockBaseKeyListener mockBaseKeyListener = new MockBaseKeyListener();
+ final BaseKeyListener mockBaseKeyListener = new MockBaseKeyListener();
Editable content = Editable.Factory.getInstance().newEditable(TEST_STRING);
// Delete the entire line with ALT + DEL, even if we're at the head...
@@ -132,31 +125,33 @@
}
public void testBackspace_withSendKeys() {
- final MockBaseKeyListener mockBaseKeyListener = new MockBaseKeyListener();
+ final BaseKeyListener mockBaseKeyListener = new MockBaseKeyListener();
// Delete the first character '1'
prepTextViewSync(TEST_STRING, mockBaseKeyListener, true, 1, 1);
- mKeyEventUtil.sendKeys(mTextView, KeyEvent.KEYCODE_DEL);
+ CtsKeyEventUtil.sendKeys(mInstrumentation, mTextView, KeyEvent.KEYCODE_DEL);
assertEquals("23456", mTextView.getText().toString());
// Delete character '2' and '3'
prepTextViewSync(TEST_STRING, mockBaseKeyListener, true, 1, 3);
- mKeyEventUtil.sendKeys(mTextView, KeyEvent.KEYCODE_DEL);
+ CtsKeyEventUtil.sendKeys(mInstrumentation, mTextView, KeyEvent.KEYCODE_DEL);
assertEquals("1456", mTextView.getText().toString());
// Delete everything on the line the cursor is on.
prepTextViewSync(TEST_STRING, mockBaseKeyListener, true, 0, 0);
- sendAltDelete();
+ CtsKeyEventUtil.sendKeyWhileHoldingModifier(
+ mInstrumentation, mTextView, KeyEvent.KEYCODE_DEL, KeyEvent.KEYCODE_ALT_LEFT);
assertEquals("", mTextView.getText().toString());
// ALT+DEL deletes the selection only.
prepTextViewSync(TEST_STRING, mockBaseKeyListener, true, 2, 4);
- sendAltDelete();
+ CtsKeyEventUtil.sendKeyWhileHoldingModifier(
+ mInstrumentation, mTextView, KeyEvent.KEYCODE_DEL, KeyEvent.KEYCODE_ALT_LEFT);
assertEquals("1256", mTextView.getText().toString());
// DEL key does not take effect when TextView does not have BaseKeyListener.
prepTextViewSync(TEST_STRING, null, true, 1, 1);
- mKeyEventUtil.sendKeys(mTextView, KeyEvent.KEYCODE_DEL);
+ CtsKeyEventUtil.sendKeys(mInstrumentation, mTextView, KeyEvent.KEYCODE_DEL);
assertEquals(TEST_STRING, mTextView.getText().toString());
}
@@ -166,7 +161,7 @@
}
public void testBackspace_withCtrl() {
- final MockBaseKeyListener mockBaseKeyListener = new MockBaseKeyListener();
+ final BaseKeyListener mockBaseKeyListener = new MockBaseKeyListener();
// If the contents only having symbolic characters, delete all characters.
String testText = "!#$%&'()`{*}_?+";
@@ -321,7 +316,7 @@
}
public void testForwardDelete_withCtrl() {
- final MockBaseKeyListener mockBaseKeyListener = new MockBaseKeyListener();
+ final BaseKeyListener mockBaseKeyListener = new MockBaseKeyListener();
// If the contents only having symbolic characters, delete all characters.
String testText = "!#$%&'()`{*}_?+";
@@ -502,41 +497,64 @@
* 3. ACTION_MULTIPLE KEYCODE_UNKNOWN by inserting the event's text into the content.
*/
public void testPressKey() {
- final MockBaseKeyListener mockBaseKeyListener = new MockBaseKeyListener();
+ final BaseKeyListener mockBaseKeyListener = new MockBaseKeyListener();
// press '0' key.
prepTextViewSync(TEST_STRING, mockBaseKeyListener, true, 0, 0);
- mKeyEventUtil.sendKeys(mTextView, KeyEvent.KEYCODE_0);
+ CtsKeyEventUtil.sendKeys(mInstrumentation, mTextView, KeyEvent.KEYCODE_0);
assertEquals("123456", mTextView.getText().toString());
// delete character '2'
prepTextViewSync(mTextView.getText(), mockBaseKeyListener, true, 1, 2);
- mKeyEventUtil.sendKeys(mTextView, KeyEvent.KEYCODE_DEL);
+ CtsKeyEventUtil.sendKeys(mInstrumentation, mTextView, KeyEvent.KEYCODE_DEL);
assertEquals("13456", mTextView.getText().toString());
// test ACTION_MULTIPLE KEYCODE_UNKNOWN key event.
KeyEvent event = new KeyEvent(SystemClock.uptimeMillis(), "abcd",
KeyCharacterMap.BUILT_IN_KEYBOARD, 0);
prepTextViewSync(mTextView.getText(), mockBaseKeyListener, true, 2, 2);
- mKeyEventUtil.sendKey(mTextView, event);
+ CtsKeyEventUtil.sendKey(mInstrumentation, mTextView, event);
mInstrumentation.waitForIdleSync();
// the text of TextView is never changed, onKeyOther never works.
// assertEquals("13abcd456", mTextView.getText().toString());
}
- private void executeAltBackspace(Editable content, MockBaseKeyListener listener) {
+ public void testOnKeyOther() {
+ final BaseKeyListener mockBaseKeyListener = new MockBaseKeyListener();
+ final String string = "abc";
+ final SpannableStringBuilder content = new SpannableStringBuilder(string);
+
+ KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_UNKNOWN);
+ assertFalse(mockBaseKeyListener.onKeyOther(mTextView, content, event));
+ assertEquals(string, content.toString());
+
+ event = new KeyEvent(KeyEvent.ACTION_MULTIPLE, KeyEvent.KEYCODE_0);
+ assertFalse(mockBaseKeyListener.onKeyOther(mTextView, content, event));
+ assertEquals(string, content.toString());
+
+ Selection.setSelection(content, 1, 0);
+ event = new KeyEvent(KeyEvent.ACTION_MULTIPLE, KeyEvent.KEYCODE_UNKNOWN);
+ assertFalse(mockBaseKeyListener.onKeyOther(mTextView, content, event));
+ assertEquals(string, content.toString());
+
+ event = new KeyEvent(SystemClock.uptimeMillis(), "b", 0, 0);
+ assertTrue(mockBaseKeyListener.onKeyOther(mTextView, content, event));
+ assertEquals("bbc", content.toString());
+ }
+
+ private void executeAltBackspace(Editable content, BaseKeyListener listener) {
final KeyEvent delKeyEvent = getKey(KeyEvent.KEYCODE_DEL,
KeyEvent.META_ALT_ON | KeyEvent.META_ALT_LEFT_ON);
listener.backspace(mTextView, content, KeyEvent.KEYCODE_DEL, delKeyEvent);
}
- private void executeCtrlBackspace(Editable content, MockBaseKeyListener listener) {
+ private void executeCtrlBackspace(Editable content, BaseKeyListener listener) {
final KeyEvent delKeyEvent = getKey(KeyEvent.KEYCODE_DEL,
KeyEvent.META_CTRL_ON | KeyEvent.META_CTRL_LEFT_ON);
listener.backspace(mTextView, content, KeyEvent.KEYCODE_DEL, delKeyEvent);
}
- private void executeCtrlForwardDelete(Editable content, MockBaseKeyListener listener) {
+ private void executeCtrlForwardDelete(Editable content, BaseKeyListener listener) {
final KeyEvent delKeyEvent = getKey(KeyEvent.KEYCODE_FORWARD_DEL,
KeyEvent.META_CTRL_ON | KeyEvent.META_CTRL_LEFT_ON);
listener.forwardDelete(mTextView, content, KeyEvent.KEYCODE_FORWARD_DEL, delKeyEvent);
@@ -548,34 +566,18 @@
*/
private void prepTextViewSync(final CharSequence content, final BaseKeyListener keyListener,
final boolean selectInTextView, final int selectionStart, final int selectionEnd) {
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- mTextView.setText(content, BufferType.EDITABLE);
- mTextView.setKeyListener(keyListener);
- Selection.setSelection(
- (Spannable) (selectInTextView ? mTextView.getText() : content),
- selectionStart, selectionEnd);
- }
+ mInstrumentation.runOnMainSync(() -> {
+ mTextView.setText(content, BufferType.EDITABLE);
+ mTextView.setKeyListener(keyListener);
+ Selection.setSelection(
+ selectInTextView ? mTextView.getText() : (Spannable) content,
+ selectionStart, selectionEnd);
});
mInstrumentation.waitForIdleSync();
assertTrue(mTextView.hasWindowFocus());
}
/**
- * Sends alt-delete key combo via {@link #sendKeys(int... keys)}.
- */
- private void sendAltDelete() {
- mKeyEventUtil.sendKey(mTextView, new KeyEvent(KeyEvent.ACTION_DOWN,
- KeyEvent.KEYCODE_ALT_LEFT));
- mKeyEventUtil.sendKey(mTextView, new KeyEvent(0, 0, KeyEvent.ACTION_DOWN,
- KeyEvent.KEYCODE_DEL, 0, KeyEvent.META_ALT_ON));
- mKeyEventUtil.sendKey(mTextView, new KeyEvent(0, 0, KeyEvent.ACTION_UP,
- KeyEvent.KEYCODE_DEL, 0, KeyEvent.META_ALT_ON));
- mKeyEventUtil.sendKey(mTextView, new KeyEvent(KeyEvent.ACTION_UP,
- KeyEvent.KEYCODE_ALT_LEFT));
- }
-
- /**
* A mocked {@link android.text.method.BaseKeyListener} for testing purposes.
*/
private class MockBaseKeyListener extends BaseKeyListener {
diff --git a/tests/tests/text/src/android/text/method/cts/BaseMovementMethodTest.java b/tests/tests/text/src/android/text/method/cts/BaseMovementMethodTest.java
new file mode 100644
index 0000000..4c5300a
--- /dev/null
+++ b/tests/tests/text/src/android/text/method/cts/BaseMovementMethodTest.java
@@ -0,0 +1,218 @@
+/*
+ * 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.method.cts;
+
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+import static android.widget.TextView.BufferType.EDITABLE;
+
+import static org.junit.Assert.*;
+
+import android.annotation.NonNull;
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.cts.util.WidgetTestUtils;
+import android.os.SystemClock;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.text.Spannable;
+import android.text.method.BaseMovementMethod;
+import android.view.InputDevice;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.MotionEvent.PointerProperties;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import android.widget.TextView;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test {@link BaseMovementMethod}.
+ */
+@RunWith(AndroidJUnit4.class)
+public class BaseMovementMethodTest {
+
+ private BaseMovementMethod mMovementMethod;
+
+ @Rule
+ public ActivityTestRule<CtsActivity> mActivityRule = new ActivityTestRule<>(CtsActivity.class);
+
+ private Instrumentation mInstrumentation;
+
+ @Before
+ public void setUp() throws Exception {
+ mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ mMovementMethod = new BaseMovementMethod();
+ }
+
+ @MediumTest
+ @Test
+ public void testOnGenericMotionEvent_horizontalScroll() {
+ final String testLine = "some text some text";
+ final String testString = testLine + "\n" + testLine;
+
+ final TextView textView = createTextView();
+ // limit lines for horizontal scroll
+ textView.setSingleLine();
+ textView.setText(testString, EDITABLE);
+
+ // limit width for horizontal scroll
+
+ setContentView(textView, (int) textView.getPaint().measureText(testLine) / 3);
+ // assert the default scroll position
+ assertEquals(0, textView.getScrollX());
+
+ final Spannable text = (Spannable) textView.getText();
+ final double lineSpacing = Math.ceil(textView.getPaint().getFontSpacing());
+
+ // scroll right
+ MotionEvent event = createScrollEvent(1, 0);
+ assertTrue(mMovementMethod.onGenericMotionEvent(textView, text, event));
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, textView, null);
+ assertEquals(lineSpacing, textView.getScrollX(), lineSpacing / 4);
+ event.recycle();
+
+ // scroll left
+ event = createScrollEvent(-1, 0);
+ assertTrue(mMovementMethod.onGenericMotionEvent(textView, text, event));
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, textView, null);
+ assertEquals(0, textView.getScrollX());
+ event.recycle();
+
+ // cannot scroll to left
+ event = createScrollEvent(-1, 0);
+ assertFalse(mMovementMethod.onGenericMotionEvent(textView, text, event));
+ event.recycle();
+
+ // cannot scroll to right
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, textView,
+ () -> textView.scrollTo((int) textView.getLayout().getLineWidth(0), 0));
+ event = createScrollEvent(1, 0);
+ assertFalse(mMovementMethod.onGenericMotionEvent(textView, text, event));
+ event.recycle();
+
+ // meta shift on
+ // reset scroll
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, textView,
+ () -> textView.scrollTo(0, 0));
+
+ // scroll top becomes scroll right
+ event = createScrollEvent(0, 1, KeyEvent.META_SHIFT_ON);
+ assertTrue(mMovementMethod.onGenericMotionEvent(textView, text, event));
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, textView, null);
+ assertEquals(lineSpacing, textView.getScrollX(), lineSpacing / 4);
+ event.recycle();
+
+ // scroll down becomes scroll left
+ event = createScrollEvent(0, -1, KeyEvent.META_SHIFT_ON);
+ assertTrue(mMovementMethod.onGenericMotionEvent(textView, text, event));
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, textView, null);
+ assertEquals(0, textView.getScrollX());
+ event.recycle();
+ }
+
+ @MediumTest
+ @Test
+ public void testOnGenericMotionEvent_verticalScroll() {
+ final String testLine = "some text some text";
+ final String testString = testLine + "\n" + testLine;
+
+ final TextView textView = createTextView();
+ // limit lines for vertical scroll
+ textView.setMaxLines(1);
+ textView.setText(testString, EDITABLE);
+ setContentView(textView, WRAP_CONTENT);
+ // assert the default scroll positions
+ assertEquals(0, textView.getScrollY());
+
+ final Spannable text = (Spannable) textView.getText();
+ final int lineHeight = textView.getLineHeight();
+
+ // scroll down
+ MotionEvent event = createScrollEvent(0, -1);
+ assertTrue(mMovementMethod.onGenericMotionEvent(textView, text, event));
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, textView, null);
+ assertEquals(lineHeight, textView.getScrollY(), lineHeight / 4);
+ event.recycle();
+
+ // scroll up
+ event = createScrollEvent(0, 1);
+ assertTrue(mMovementMethod.onGenericMotionEvent(textView, text, event));
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, textView, null);
+ assertEquals(0, textView.getScrollY());
+ event.recycle();
+
+ // cannot scroll up
+ event = createScrollEvent(0, 1);
+ assertFalse(mMovementMethod.onGenericMotionEvent(textView, text, event));
+ event.recycle();
+
+ // cannot scroll down
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, textView,
+ () -> textView.scrollTo(0, textView.getLayout().getHeight()));
+ event = createScrollEvent(0, -1);
+ assertFalse(mMovementMethod.onGenericMotionEvent(textView, text, event));
+ event.recycle();
+ }
+
+ private TextView createTextView() {
+ final TextView textView = new TextViewNoIme(mActivityRule.getActivity());
+ textView.setFocusable(true);
+ textView.setMovementMethod(mMovementMethod);
+ textView.setTextDirection(View.TEXT_DIRECTION_LTR);
+ return textView;
+ }
+
+ private void setContentView(@NonNull TextView textView, int textWidth) {
+ final Activity activity = mActivityRule.getActivity();
+ final FrameLayout layout = new FrameLayout(activity);
+ layout.addView(textView, new ViewGroup.LayoutParams(textWidth, WRAP_CONTENT));
+
+ mInstrumentation.runOnMainSync(() -> {
+ activity.setContentView(layout, new ViewGroup.LayoutParams(MATCH_PARENT,
+ MATCH_PARENT));
+ textView.requestFocus();
+ });
+ mInstrumentation.waitForIdleSync();
+ assertTrue(textView.isFocused());
+ }
+
+ private static MotionEvent createScrollEvent(int horizontal, int vertical) {
+ return createScrollEvent(horizontal, vertical, 0);
+ }
+
+ private static MotionEvent createScrollEvent(int horizontal, int vertical, int meta) {
+ final PointerProperties[] pointerProperties = new PointerProperties[1];
+ pointerProperties[0] = new PointerProperties();
+ pointerProperties[0].id = 0;
+ final MotionEvent.PointerCoords[] coords = new MotionEvent.PointerCoords[1];
+ coords[0] = new MotionEvent.PointerCoords();
+ coords[0].setAxisValue(MotionEvent.AXIS_VSCROLL, vertical);
+ coords[0].setAxisValue(MotionEvent.AXIS_HSCROLL, horizontal);
+ final long time = SystemClock.uptimeMillis();
+ return MotionEvent.obtain(time, time, MotionEvent.ACTION_SCROLL, 1,
+ pointerProperties, coords, meta, 0, 1.0f, 1.0f, 0, 0,
+ InputDevice.SOURCE_CLASS_POINTER, 0);
+ }
+}
diff --git a/tests/tests/text/src/android/text/method/cts/DateKeyListenerTest.java b/tests/tests/text/src/android/text/method/cts/DateKeyListenerTest.java
index 7642d91..fd2761e 100644
--- a/tests/tests/text/src/android/text/method/cts/DateKeyListenerTest.java
+++ b/tests/tests/text/src/android/text/method/cts/DateKeyListenerTest.java
@@ -16,7 +16,7 @@
package android.text.method.cts;
-import android.cts.util.KeyEventUtil;
+import android.cts.util.CtsKeyEventUtil;
import android.text.InputType;
import android.text.method.DateKeyListener;
import android.view.KeyEvent;
@@ -26,14 +26,6 @@
*/
public class DateKeyListenerTest extends KeyListenerTestCase {
- private KeyEventUtil mKeyEventUtil;
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mKeyEventUtil = new KeyEventUtil(getInstrumentation());
- }
-
public void testConstructor() {
new DateKeyListener();
}
@@ -77,26 +69,26 @@
assertEquals("", mTextView.getText().toString());
// press '1' key.
- mKeyEventUtil.sendKeys(mTextView, KeyEvent.KEYCODE_1);
+ CtsKeyEventUtil.sendKeys(mInstrumentation, mTextView, KeyEvent.KEYCODE_1);
assertEquals("1", mTextView.getText().toString());
// press '2' key.
- mKeyEventUtil.sendKeys(mTextView, KeyEvent.KEYCODE_2);
+ CtsKeyEventUtil.sendKeys(mInstrumentation, mTextView, KeyEvent.KEYCODE_2);
assertEquals("12", mTextView.getText().toString());
// press an unaccepted key if it exists.
int keyCode = TextMethodUtils.getUnacceptedKeyCode(DateKeyListener.CHARACTERS);
if (-1 != keyCode) {
- mKeyEventUtil.sendKeys(mTextView, keyCode);
+ CtsKeyEventUtil.sendKeys(mInstrumentation, mTextView, keyCode);
assertEquals("12", mTextView.getText().toString());
}
// press '-' key.
- mKeyEventUtil.sendKeys(mTextView, KeyEvent.KEYCODE_MINUS);
+ CtsKeyEventUtil.sendKeys(mInstrumentation, mTextView, KeyEvent.KEYCODE_MINUS);
assertEquals("12-", mTextView.getText().toString());
// press '/' key.
- mKeyEventUtil.sendKeys(mTextView, KeyEvent.KEYCODE_SLASH);
+ CtsKeyEventUtil.sendKeys(mInstrumentation, mTextView, KeyEvent.KEYCODE_SLASH);
assertEquals("12-/", mTextView.getText().toString());
// remove DateKeyListener
@@ -104,7 +96,7 @@
assertEquals("12-/", mTextView.getText().toString());
// press '/' key, it will not be accepted.
- mKeyEventUtil.sendKeys(mTextView, KeyEvent.KEYCODE_SLASH);
+ CtsKeyEventUtil.sendKeys(mInstrumentation, mTextView, KeyEvent.KEYCODE_SLASH);
assertEquals("12-/", mTextView.getText().toString());
}
diff --git a/tests/tests/text/src/android/text/method/cts/DateTimeKeyListenerTest.java b/tests/tests/text/src/android/text/method/cts/DateTimeKeyListenerTest.java
index 9dfc38a..6276125 100644
--- a/tests/tests/text/src/android/text/method/cts/DateTimeKeyListenerTest.java
+++ b/tests/tests/text/src/android/text/method/cts/DateTimeKeyListenerTest.java
@@ -16,7 +16,7 @@
package android.text.method.cts;
-import android.cts.util.KeyEventUtil;
+import android.cts.util.CtsKeyEventUtil;
import android.text.InputType;
import android.text.method.DateTimeKeyListener;
import android.view.KeyCharacterMap;
@@ -27,14 +27,6 @@
*/
public class DateTimeKeyListenerTest extends KeyListenerTestCase {
- private KeyEventUtil mKeyEventUtil;
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mKeyEventUtil = new KeyEventUtil(getInstrumentation());
- }
-
public void testConstructor() {
new DateTimeKeyListener();
}
@@ -80,12 +72,12 @@
assertEquals(expectedText, mTextView.getText().toString());
// press '1' key.
- mKeyEventUtil.sendString(mTextView, "1");
+ CtsKeyEventUtil.sendString(mInstrumentation, mTextView, "1");
expectedText += "1";
assertEquals(expectedText, mTextView.getText().toString());
// press '2' key.
- mKeyEventUtil.sendString(mTextView, "2");
+ CtsKeyEventUtil.sendString(mInstrumentation, mTextView, "2");
expectedText += "2";
assertEquals(expectedText, mTextView.getText().toString());
@@ -93,28 +85,28 @@
KeyCharacterMap kcm = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
if ('a' == kcm.getMatch(KeyEvent.KEYCODE_A, DateTimeKeyListener.CHARACTERS)) {
expectedText += "a";
- mKeyEventUtil.sendKeyDownUp(mTextView, KeyEvent.KEYCODE_A);
+ CtsKeyEventUtil.sendKeyDownUp(mInstrumentation, mTextView, KeyEvent.KEYCODE_A);
assertEquals(expectedText, mTextView.getText().toString());
}
// press 'p' key if producible
if ('p' == kcm.getMatch(KeyEvent.KEYCODE_P, DateTimeKeyListener.CHARACTERS)) {
expectedText += "p";
- mKeyEventUtil.sendKeyDownUp(mTextView, KeyEvent.KEYCODE_P);
+ CtsKeyEventUtil.sendKeyDownUp(mInstrumentation, mTextView, KeyEvent.KEYCODE_P);
assertEquals(expectedText, mTextView.getText().toString());
}
// press 'm' key if producible
if ('m' == kcm.getMatch(KeyEvent.KEYCODE_M, DateTimeKeyListener.CHARACTERS)) {
expectedText += "m";
- mKeyEventUtil.sendKeyDownUp(mTextView, KeyEvent.KEYCODE_M);
+ CtsKeyEventUtil.sendKeyDownUp(mInstrumentation, mTextView, KeyEvent.KEYCODE_M);
assertEquals(expectedText, mTextView.getText().toString());
}
// press an unaccepted key if it exists.
int keyCode = TextMethodUtils.getUnacceptedKeyCode(DateTimeKeyListener.CHARACTERS);
if (-1 != keyCode) {
- mKeyEventUtil.sendKeys(mTextView, keyCode);
+ CtsKeyEventUtil.sendKeys(mInstrumentation, mTextView, keyCode);
assertEquals(expectedText, mTextView.getText().toString());
}
@@ -122,7 +114,7 @@
setKeyListenerSync(null);
assertEquals(expectedText, mTextView.getText().toString());
- mKeyEventUtil.sendString(mTextView, "1");
+ CtsKeyEventUtil.sendString(mInstrumentation, mTextView, "1");
assertEquals(expectedText, mTextView.getText().toString());
}
diff --git a/tests/tests/text/src/android/text/method/cts/DigitsKeyListenerTest.java b/tests/tests/text/src/android/text/method/cts/DigitsKeyListenerTest.java
index 3170482..2587b60 100644
--- a/tests/tests/text/src/android/text/method/cts/DigitsKeyListenerTest.java
+++ b/tests/tests/text/src/android/text/method/cts/DigitsKeyListenerTest.java
@@ -16,7 +16,7 @@
package android.text.method.cts;
-import android.cts.util.KeyEventUtil;
+import android.cts.util.CtsKeyEventUtil;
import android.text.InputType;
import android.text.Spannable;
import android.text.SpannableString;
@@ -29,14 +29,6 @@
*/
public class DigitsKeyListenerTest extends KeyListenerTestCase {
- private KeyEventUtil mKeyEventUtil;
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mKeyEventUtil = new KeyEventUtil(getInstrumentation());
- }
-
public void testConstructor() {
new DigitsKeyListener();
@@ -403,19 +395,19 @@
assertEquals("", mTextView.getText().toString());
// press '-' key.
- mKeyEventUtil.sendKeys(mTextView, KeyEvent.KEYCODE_MINUS);
+ CtsKeyEventUtil.sendKeys(mInstrumentation, mTextView, KeyEvent.KEYCODE_MINUS);
assertEquals("", mTextView.getText().toString());
// press '1' key.
- mKeyEventUtil.sendKeys(mTextView, KeyEvent.KEYCODE_1);
+ CtsKeyEventUtil.sendKeys(mInstrumentation, mTextView, KeyEvent.KEYCODE_1);
assertEquals("1", mTextView.getText().toString());
// press '.' key.
- mKeyEventUtil.sendKeys(mTextView, KeyEvent.KEYCODE_PERIOD);
+ CtsKeyEventUtil.sendKeys(mInstrumentation, mTextView, KeyEvent.KEYCODE_PERIOD);
assertEquals("1", mTextView.getText().toString());
// press '2' key.
- mKeyEventUtil.sendKeys(mTextView, KeyEvent.KEYCODE_2);
+ CtsKeyEventUtil.sendKeys(mInstrumentation, mTextView, KeyEvent.KEYCODE_2);
assertEquals("12", mTextView.getText().toString());
}
@@ -437,27 +429,27 @@
assertEquals("", mTextView.getText().toString());
// press '-' key.
- mKeyEventUtil.sendKeys(mTextView, KeyEvent.KEYCODE_MINUS);
+ CtsKeyEventUtil.sendKeys(mInstrumentation, mTextView, KeyEvent.KEYCODE_MINUS);
assertEquals("-", mTextView.getText().toString());
// press '1' key.
- mKeyEventUtil.sendKeys(mTextView, KeyEvent.KEYCODE_1);
+ CtsKeyEventUtil.sendKeys(mInstrumentation, mTextView, KeyEvent.KEYCODE_1);
assertEquals("-1", mTextView.getText().toString());
// press '.' key.
- mKeyEventUtil.sendKeys(mTextView, KeyEvent.KEYCODE_PERIOD);
+ CtsKeyEventUtil.sendKeys(mInstrumentation, mTextView, KeyEvent.KEYCODE_PERIOD);
assertEquals("-1", mTextView.getText().toString());
// press '+' key.
- mKeyEventUtil.sendKeys(mTextView, KeyEvent.KEYCODE_PLUS);
+ CtsKeyEventUtil.sendKeys(mInstrumentation, mTextView, KeyEvent.KEYCODE_PLUS);
assertEquals("-1", mTextView.getText().toString());
// press '2' key.
- mKeyEventUtil.sendKeys(mTextView, KeyEvent.KEYCODE_2);
+ CtsKeyEventUtil.sendKeys(mInstrumentation, mTextView, KeyEvent.KEYCODE_2);
assertEquals("-12", mTextView.getText().toString());
// press '-' key.
- mKeyEventUtil.sendKeys(mTextView, KeyEvent.KEYCODE_MINUS);
+ CtsKeyEventUtil.sendKeys(mInstrumentation, mTextView, KeyEvent.KEYCODE_MINUS);
assertEquals("-12", mTextView.getText().toString());
}
@@ -479,27 +471,27 @@
assertEquals("", mTextView.getText().toString());
// press '-' key.
- mKeyEventUtil.sendKeys(mTextView, KeyEvent.KEYCODE_MINUS);
+ CtsKeyEventUtil.sendKeys(mInstrumentation, mTextView, KeyEvent.KEYCODE_MINUS);
assertEquals("", mTextView.getText().toString());
// press '+' key.
- mKeyEventUtil.sendKeys(mTextView, KeyEvent.KEYCODE_PLUS);
+ CtsKeyEventUtil.sendKeys(mInstrumentation, mTextView, KeyEvent.KEYCODE_PLUS);
assertEquals("", mTextView.getText().toString());
// press '1' key.
- mKeyEventUtil.sendKeys(mTextView, KeyEvent.KEYCODE_1);
+ CtsKeyEventUtil.sendKeys(mInstrumentation, mTextView, KeyEvent.KEYCODE_1);
assertEquals("1", mTextView.getText().toString());
// press '.' key.
- mKeyEventUtil.sendKeys(mTextView, KeyEvent.KEYCODE_PERIOD);
+ CtsKeyEventUtil.sendKeys(mInstrumentation, mTextView, KeyEvent.KEYCODE_PERIOD);
assertEquals("1.", mTextView.getText().toString());
// press '2' key.
- mKeyEventUtil.sendKeys(mTextView, KeyEvent.KEYCODE_2);
+ CtsKeyEventUtil.sendKeys(mInstrumentation, mTextView, KeyEvent.KEYCODE_2);
assertEquals("1.2", mTextView.getText().toString());
// press '.' key.
- mKeyEventUtil.sendKeys(mTextView, KeyEvent.KEYCODE_PERIOD);
+ CtsKeyEventUtil.sendKeys(mInstrumentation, mTextView, KeyEvent.KEYCODE_PERIOD);
assertEquals("1.2", mTextView.getText().toString());
}
@@ -523,27 +515,27 @@
assertEquals("", mTextView.getText().toString());
// press '+' key.
- mKeyEventUtil.sendKeys(mTextView, KeyEvent.KEYCODE_PLUS);
+ CtsKeyEventUtil.sendKeys(mInstrumentation, mTextView, KeyEvent.KEYCODE_PLUS);
assertEquals("+", mTextView.getText().toString());
// press '1' key.
- mKeyEventUtil.sendKeys(mTextView, KeyEvent.KEYCODE_1);
+ CtsKeyEventUtil.sendKeys(mInstrumentation, mTextView, KeyEvent.KEYCODE_1);
assertEquals("+1", mTextView.getText().toString());
// press '.' key.
- mKeyEventUtil.sendKeys(mTextView, KeyEvent.KEYCODE_PERIOD);
+ CtsKeyEventUtil.sendKeys(mInstrumentation, mTextView, KeyEvent.KEYCODE_PERIOD);
assertEquals("+1.", mTextView.getText().toString());
// press '2' key.
- mKeyEventUtil.sendKeys(mTextView, KeyEvent.KEYCODE_2);
+ CtsKeyEventUtil.sendKeys(mInstrumentation, mTextView, KeyEvent.KEYCODE_2);
assertEquals("+1.2", mTextView.getText().toString());
// press '-' key.
- mKeyEventUtil.sendKeys(mTextView, KeyEvent.KEYCODE_MINUS);
+ CtsKeyEventUtil.sendKeys(mInstrumentation, mTextView, KeyEvent.KEYCODE_MINUS);
assertEquals("+1.2", mTextView.getText().toString());
// press '.' key.
- mKeyEventUtil.sendKeys(mTextView, KeyEvent.KEYCODE_PERIOD);
+ CtsKeyEventUtil.sendKeys(mInstrumentation, mTextView, KeyEvent.KEYCODE_PERIOD);
assertEquals("+1.2", mTextView.getText().toString());
}
@@ -564,33 +556,31 @@
assertEquals("", mTextView.getText().toString());
// press '1' key.
- mKeyEventUtil.sendKeys(mTextView, KeyEvent.KEYCODE_1);
+ CtsKeyEventUtil.sendKeys(mInstrumentation, mTextView, KeyEvent.KEYCODE_1);
assertEquals("", mTextView.getText().toString());
// press '5' key.
- mKeyEventUtil.sendKeys(mTextView, KeyEvent.KEYCODE_5);
+ CtsKeyEventUtil.sendKeys(mInstrumentation, mTextView, KeyEvent.KEYCODE_5);
assertEquals("5", mTextView.getText().toString());
// press '.' key.
- mKeyEventUtil.sendKeys(mTextView, KeyEvent.KEYCODE_PERIOD);
+ CtsKeyEventUtil.sendKeys(mInstrumentation, mTextView, KeyEvent.KEYCODE_PERIOD);
assertEquals("5", mTextView.getText().toString());
// press '-' key.
- mKeyEventUtil.sendKeys(mTextView, KeyEvent.KEYCODE_MINUS);
+ CtsKeyEventUtil.sendKeys(mInstrumentation, mTextView, KeyEvent.KEYCODE_MINUS);
assertEquals("5", mTextView.getText().toString());
// remove DigitsKeyListener
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- mTextView.setKeyListener(null);
- mTextView.requestFocus();
- }
+ mInstrumentation.runOnMainSync(() -> {
+ mTextView.setKeyListener(null);
+ mTextView.requestFocus();
});
mInstrumentation.waitForIdleSync();
assertEquals("5", mTextView.getText().toString());
// press '5' key.
- mKeyEventUtil.sendKeys(mTextView, KeyEvent.KEYCODE_5);
+ CtsKeyEventUtil.sendKeys(mInstrumentation, mTextView, KeyEvent.KEYCODE_5);
assertEquals("5", mTextView.getText().toString());
}
diff --git a/tests/tests/text/src/android/text/method/cts/ForwardDeleteTest.java b/tests/tests/text/src/android/text/method/cts/ForwardDeleteTest.java
index 40ed593..50250ea 100644
--- a/tests/tests/text/src/android/text/method/cts/ForwardDeleteTest.java
+++ b/tests/tests/text/src/android/text/method/cts/ForwardDeleteTest.java
@@ -35,22 +35,16 @@
// Sync the state to the TextView and call onKeyDown with KEYCODE_FORWARD_DEL key event.
// Then update the state to the result of TextView.
private void forwardDelete(final EditorState state, int modifiers) {
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- mTextView.setText(state.mText, BufferType.EDITABLE);
- mTextView.setKeyListener(mKeyListener);
- mTextView.setSelection(state.mSelectionStart, state.mSelectionEnd);
- }
+ mInstrumentation.runOnMainSync(() -> {
+ mTextView.setText(state.mText, BufferType.EDITABLE);
+ mTextView.setKeyListener(mKeyListener);
+ mTextView.setSelection(state.mSelectionStart, state.mSelectionEnd);
});
mInstrumentation.waitForIdleSync();
assertTrue(mTextView.hasWindowFocus());
final KeyEvent keyEvent = getKey(KeyEvent.KEYCODE_FORWARD_DEL, modifiers);
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- mTextView.onKeyDown(keyEvent.getKeyCode(), keyEvent);
- }
- });
+ mActivity.runOnUiThread(() -> mTextView.onKeyDown(keyEvent.getKeyCode(), keyEvent));
mInstrumentation.waitForIdleSync();
state.mText = mTextView.getText();
@@ -59,6 +53,30 @@
}
@SmallTest
+ public void testCRLF() {
+ EditorState state = new EditorState();
+
+ // U+000A is LINE FEED.
+ state.setByString("| U+000A");
+ forwardDelete(state, 0);
+ state.assertEquals("|");
+
+ // U+000D is CARRIAGE RETURN.
+ state.setByString("| U+000D");
+ forwardDelete(state, 0);
+ state.assertEquals("|");
+
+ state.setByString("| U+000D U+000A");
+ forwardDelete(state, 0);
+ state.assertEquals("|");
+
+ state.setByString("| U+000A U+000D");
+ forwardDelete(state, 0);
+ state.assertEquals("| U+000D");
+ forwardDelete(state, 0);
+ }
+
+ @SmallTest
public void testSurrogatePairs() {
EditorState state = new EditorState();
diff --git a/tests/tests/text/src/android/text/method/cts/KeyListenerTestCase.java b/tests/tests/text/src/android/text/method/cts/KeyListenerTestCase.java
index ec9c48b..ea503f3 100644
--- a/tests/tests/text/src/android/text/method/cts/KeyListenerTestCase.java
+++ b/tests/tests/text/src/android/text/method/cts/KeyListenerTestCase.java
@@ -34,7 +34,7 @@
* {@link MultiTapKeyListenerTest}
* {@link NumberKeyListenerTest}
* {@link QwertyKeyListenerTest}
- * {@link TextKeyKeyListenerTest}
+ * {@link TextKeyListenerTest}
*
* @see BaseKeyListenerTest
* @see DateKeyListenerTest
@@ -43,7 +43,7 @@
* @see MultiTapKeyListenerTest
* @see NumberKeyListenerTest
* @see QwertyKeyListenerTest
- * @see TextKeyKeyListenerTest
+ * @see TextKeyListenerTest
*/
public abstract class KeyListenerTestCase extends
ActivityInstrumentationTestCase2<KeyListenerCtsActivity> {
@@ -63,12 +63,8 @@
mInstrumentation = getInstrumentation();
mTextView = (EditText) mActivity.findViewById(R.id.keylistener_textview);
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- // Ensure that the screen is on for this test.
- mTextView.setKeepScreenOn(true);
- }
- });
+ // Ensure that the screen is on for this test.
+ mInstrumentation.runOnMainSync(() -> mTextView.setKeepScreenOn(true));
mInstrumentation.waitForIdleSync();
assertTrue(mActivity.waitForWindowFocus(5 * DateUtils.SECOND_IN_MILLIS));
}
@@ -77,11 +73,7 @@
* Synchronously sets mTextView's key listener on the UI thread.
*/
protected void setKeyListenerSync(final KeyListener keyListener) {
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- mTextView.setKeyListener(keyListener);
- }
- });
+ mInstrumentation.runOnMainSync(() -> mTextView.setKeyListener(keyListener));
mInstrumentation.waitForIdleSync();
}
diff --git a/tests/tests/text/src/android/text/method/cts/LinkMovementMethodTest.java b/tests/tests/text/src/android/text/method/cts/LinkMovementMethodTest.java
index 03fc1e7..34533f9 100644
--- a/tests/tests/text/src/android/text/method/cts/LinkMovementMethodTest.java
+++ b/tests/tests/text/src/android/text/method/cts/LinkMovementMethodTest.java
@@ -34,6 +34,9 @@
import android.widget.TextView;
import android.widget.TextView.BufferType;
+import static org.mockito.Matchers.*;
+import static org.mockito.Mockito.*;
+
/**
* Test {@link LinkMovementMethod}. The class is an implementation of interface
* {@link MovementMethod}. The typical usage of {@link MovementMethod} is tested in
@@ -52,9 +55,9 @@
private Spannable mSpannable;
- private MockClickableSpan mClickable0;
+ private ClickableSpan mClickable0;
- private MockClickableSpan mClickable1;
+ private ClickableSpan mClickable1;
public LinkMovementMethodTest() {
super("android.text.cts", CtsActivity.class);
@@ -69,11 +72,7 @@
mView = new TextViewNoIme(getActivity());
mView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 10);
mView.setText(CONTENT, BufferType.SPANNABLE);
- getInstrumentation().runOnMainSync(new Runnable() {
- public void run() {
- getActivity().setContentView(mView);
- }
- });
+ getInstrumentation().runOnMainSync(() -> getActivity().setContentView(mView));
getInstrumentation().waitForIdleSync();
mSpannable = (Spannable) mView.getText();
@@ -142,51 +141,51 @@
public void testOnKeyDown() {
// no selection
assertSelection(mSpannable, -1);
- mClickable0.reset();
- mClickable1.reset();
+ reset(mClickable0);
+ reset(mClickable1);
assertFalse(mMethod.onKeyDown(mView, mSpannable, KeyEvent.KEYCODE_ENTER,
new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER)));
- assertFalse(mClickable0.hasCalledOnClick());
- assertFalse(mClickable1.hasCalledOnClick());
+ verify(mClickable0, never()).onClick(any());
+ verify(mClickable1, never()).onClick(any());
// select clickable0
Selection.setSelection(mSpannable, mSpannable.getSpanStart(mClickable0),
mSpannable.getSpanEnd(mClickable0));
- mClickable0.reset();
- mClickable1.reset();
+ reset(mClickable0);
+ reset(mClickable1);
assertFalse(mMethod.onKeyDown(mView, mSpannable, KeyEvent.KEYCODE_DPAD_CENTER,
new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_CENTER)));
- assertTrue(mClickable0.hasCalledOnClick());
- assertFalse(mClickable1.hasCalledOnClick());
+ verify(mClickable0, times(1)).onClick(any());
+ verify(mClickable1, never()).onClick(any());
// select unclickable
Selection.setSelection(mSpannable, mSpannable.getSpanEnd(mClickable0),
mSpannable.getSpanStart(mClickable1));
- mClickable0.reset();
- mClickable1.reset();
+ reset(mClickable0);
+ reset(mClickable1);
assertFalse(mMethod.onKeyDown(mView, mSpannable, KeyEvent.KEYCODE_ENTER,
new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER)));
- assertFalse(mClickable0.hasCalledOnClick());
- assertFalse(mClickable1.hasCalledOnClick());
+ verify(mClickable0, never()).onClick(any());
+ verify(mClickable1, never()).onClick(any());
// select all clickables(more than one)
Selection.selectAll(mSpannable);
- mClickable0.reset();
- mClickable1.reset();
+ reset(mClickable0);
+ reset(mClickable1);
assertFalse(mMethod.onKeyDown(mView, mSpannable, KeyEvent.KEYCODE_DPAD_CENTER,
new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_CENTER)));
- assertFalse(mClickable0.hasCalledOnClick());
- assertFalse(mClickable1.hasCalledOnClick());
+ verify(mClickable0, never()).onClick(any());
+ verify(mClickable1, never()).onClick(any());
// part of selection is clickable
Selection.setSelection(mSpannable, mSpannable.getSpanEnd(mClickable0),
mSpannable.getSpanEnd(mClickable1));
- mClickable0.reset();
- mClickable1.reset();
+ reset(mClickable0);
+ reset(mClickable1);
assertFalse(mMethod.onKeyDown(mView, mSpannable, KeyEvent.KEYCODE_DPAD_CENTER,
new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_CENTER)));
- assertFalse(mClickable0.hasCalledOnClick());
- assertTrue(mClickable1.hasCalledOnClick());
+ verify(mClickable0, never()).onClick(any());
+ verify(mClickable1, times(1)).onClick(any());
// selection contains only clickable1 and repeat count of the event is not 0
Selection.setSelection(mSpannable, mSpannable.getSpanEnd(mClickable0),
@@ -195,11 +194,11 @@
KeyEvent event = new KeyEvent(now, now, KeyEvent.ACTION_DOWN,
KeyEvent.KEYCODE_DPAD_CENTER, 1);
- mClickable0.reset();
- mClickable1.reset();
+ reset(mClickable0);
+ reset(mClickable1);
assertFalse(mMethod.onKeyDown(mView, mSpannable, KeyEvent.KEYCODE_DPAD_CENTER, event));
- assertFalse(mClickable0.hasCalledOnClick());
- assertFalse(mClickable1.hasCalledOnClick());
+ verify(mClickable0, never()).onClick(any());
+ verify(mClickable1, never()).onClick(any());
// null parameters
try {
@@ -245,9 +244,9 @@
assertSelectClickableLeftToRight(mSpannable, mClickable0);
// release on first line
- assertFalse(mClickable0.hasCalledOnClick());
+ verify(mClickable0, never()).onClick(any());
assertTrue(releaseOnLine(0));
- assertTrue(mClickable0.hasCalledOnClick());
+ verify(mClickable0, times(1)).onClick(any());
// press on second line (unclickable)
assertSelectClickableLeftToRight(mSpannable, mClickable0);
@@ -260,9 +259,9 @@
assertSelectClickableLeftToRight(mSpannable, mClickable1);
// release on last line
- assertFalse(mClickable1.hasCalledOnClick());
+ verify(mClickable1, never()).onClick(any());
assertTrue(releaseOnLine(2));
- assertTrue(mClickable1.hasCalledOnClick());
+ verify(mClickable1, times(1)).onClick(any());
// release on second line (unclickable)
assertSelectClickableLeftToRight(mSpannable, mClickable1);
@@ -457,13 +456,10 @@
}
}
- private MockClickableSpan markClickable(final int start, final int end) {
- final MockClickableSpan clickableSpan = new MockClickableSpan();
- getInstrumentation().runOnMainSync(new Runnable() {
- public void run() {
- mSpannable.setSpan(clickableSpan, start, end, Spanned.SPAN_MARK_MARK);
- }
- });
+ private ClickableSpan markClickable(final int start, final int end) {
+ final ClickableSpan clickableSpan = spy(new MockClickableSpan());
+ getInstrumentation().runOnMainSync(() -> mSpannable.setSpan(clickableSpan, start, end,
+ Spanned.SPAN_MARK_MARK));
getInstrumentation().waitForIdleSync();
return clickableSpan;
}
@@ -495,13 +491,13 @@
}
private void assertSelectClickableLeftToRight(Spannable spannable,
- MockClickableSpan clickableSpan) {
+ ClickableSpan clickableSpan) {
assertSelection(spannable, spannable.getSpanStart(clickableSpan),
spannable.getSpanEnd(clickableSpan));
}
private void assertSelectClickableRightToLeft(Spannable spannable,
- MockClickableSpan clickableSpan) {
+ ClickableSpan clickableSpan) {
assertSelection(spannable, spannable.getSpanEnd(clickableSpan),
spannable.getSpanStart(clickableSpan));
}
@@ -528,20 +524,9 @@
}
}
- private static class MockClickableSpan extends ClickableSpan {
- private boolean mHasCalledOnClick;
-
+ public static class MockClickableSpan extends ClickableSpan {
@Override
public void onClick(View widget) {
- mHasCalledOnClick = true;
- }
-
- public boolean hasCalledOnClick() {
- return mHasCalledOnClick;
- }
-
- public void reset() {
- mHasCalledOnClick = false;
}
}
}
diff --git a/tests/tests/text/src/android/text/method/cts/MetaKeyKeyListenerTest.java b/tests/tests/text/src/android/text/method/cts/MetaKeyKeyListenerTest.java
index c6eb78c..3cfa07f 100644
--- a/tests/tests/text/src/android/text/method/cts/MetaKeyKeyListenerTest.java
+++ b/tests/tests/text/src/android/text/method/cts/MetaKeyKeyListenerTest.java
@@ -16,11 +16,13 @@
package android.text.method.cts;
+import static org.mockito.Matchers.*;
+import static org.mockito.Mockito.*;
+
import android.text.Editable;
import android.text.Selection;
import android.text.Spannable;
import android.text.Spanned;
-import android.text.method.cts.KeyListenerTestCase;
import android.text.method.DateKeyListener;
import android.text.method.MetaKeyKeyListener;
import android.view.KeyCharacterMap;
@@ -191,6 +193,37 @@
MetaKeyKeyListener.META_SYM_ON));
}
+ public void testGetMetaState_withCharSequenceAndKeyEvent() {
+ KeyEvent event = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_0, 0,
+ KeyEvent.META_SHIFT_MASK);
+
+ assertEquals(KeyEvent.META_SHIFT_MASK, MetaKeyKeyListener.getMetaState(null, event));
+ assertEquals(KeyEvent.META_SHIFT_MASK, MetaKeyKeyListener.getMetaState("", event));
+ }
+
+ public void testGetMetaState_withCharSequenceAndMetaAndKeyEvent() {
+ KeyEvent event = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_0, 0,
+ KeyEvent.META_CTRL_ON);
+
+ assertEquals(0, MetaKeyKeyListener.getMetaState("", MetaKeyKeyListener.META_SHIFT_ON,
+ event));
+
+ event = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_0, 0,
+ KeyEvent.META_SHIFT_ON);
+
+ assertEquals(1, MetaKeyKeyListener.getMetaState("", MetaKeyKeyListener.META_SHIFT_ON,
+ event));
+
+ event = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_0, 0,
+ KeyEvent.META_SYM_LOCKED);
+
+ assertEquals(2, MetaKeyKeyListener.getMetaState("", MetaKeyKeyListener.META_SYM_ON,
+ event));
+
+ assertEquals(2, MetaKeyKeyListener.getMetaState(null, MetaKeyKeyListener.META_SYM_ON,
+ event));
+ }
+
public void testIsMetaTracker() {
assertFalse(MetaKeyKeyListener.isMetaTracker("123456", new Object()));
assertFalse(MetaKeyKeyListener.isMetaTracker("abc", new Object()));
@@ -206,18 +239,18 @@
public void testResetLockedMeta() {
MockMetaKeyKeyListener mockMetaKeyKeyListener = new MockMetaKeyKeyListener();
- MockSpannable str = new MockSpannable();
+ MockSpannable str = spy(new MockSpannable());
str.setSpan(new Object(), 0, 0, Spannable.SPAN_MARK_MARK
| (4 << Spannable.SPAN_USER_SHIFT));
- assertFalse(str.hasCalledRemoveSpan());
+ verify(str, never()).removeSpan(any());
mockMetaKeyKeyListener.callResetLockedMeta(str);
- assertTrue(str.hasCalledRemoveSpan());
+ verify(str, atLeastOnce()).removeSpan(any());
- str = new MockSpannable();
+ str = spy(new MockSpannable());
str.setSpan(new Object(), 0, 0, Spannable.SPAN_MARK_POINT);
- assertFalse(str.hasCalledRemoveSpan());
+ verify(str, never()).removeSpan(any());
mockMetaKeyKeyListener.callResetLockedMeta(str);
- assertFalse(str.hasCalledRemoveSpan());
+ verify(str, never()).removeSpan(any());
try {
mockMetaKeyKeyListener.callResetLockedMeta(null);
@@ -357,20 +390,14 @@
/**
* A mocked {@link android.text.Spannable} for testing purposes.
*/
- private class MockSpannable implements Spannable {
+ public static class MockSpannable implements Spannable {
private int mFlags;
- private boolean mCalledRemoveSpan = false;
-
- public boolean hasCalledRemoveSpan() {
- return mCalledRemoveSpan;
- }
public void setSpan(Object what, int start, int end, int flags) {
mFlags = flags;
}
public void removeSpan(Object what) {
- mCalledRemoveSpan = true;
}
public <T> T[] getSpans(int start, int end, Class<T> type) {
diff --git a/tests/tests/text/src/android/text/method/cts/MultiTapKeyListenerTest.java b/tests/tests/text/src/android/text/method/cts/MultiTapKeyListenerTest.java
index e537b24..eaaf005 100644
--- a/tests/tests/text/src/android/text/method/cts/MultiTapKeyListenerTest.java
+++ b/tests/tests/text/src/android/text/method/cts/MultiTapKeyListenerTest.java
@@ -20,14 +20,11 @@
import android.text.Selection;
import android.text.Spannable;
import android.text.SpannableStringBuilder;
-import android.text.method.cts.KeyListenerTestCase;
import android.text.method.MultiTapKeyListener;
import android.text.method.TextKeyListener.Capitalize;
import android.view.KeyEvent;
-import android.widget.TextView;
import android.widget.TextView.BufferType;
-import java.util.concurrent.TimeUnit;
public class MultiTapKeyListenerTest extends KeyListenerTestCase {
/**
@@ -49,11 +46,9 @@
final Spannable text = new SpannableStringBuilder("123456");
assertFalse(mockMultiTapKeyListener.hadAddedSpan());
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- mTextView.setKeyListener(mockMultiTapKeyListener);
- mTextView.setText(text, BufferType.EDITABLE);
- }
+ mInstrumentation.runOnMainSync(() -> {
+ mTextView.setKeyListener(mockMultiTapKeyListener);
+ mTextView.setText(text, BufferType.EDITABLE);
});
mInstrumentation.waitForIdleSync();
@@ -169,11 +164,9 @@
}
private void prepareEmptyTextView() {
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- mTextView.setText("", BufferType.EDITABLE);
- Selection.setSelection(mTextView.getEditableText(), 0, 0);
- }
+ mInstrumentation.runOnMainSync(() -> {
+ mTextView.setText("", BufferType.EDITABLE);
+ Selection.setSelection(mTextView.getEditableText(), 0, 0);
});
mInstrumentation.waitForIdleSync();
assertEquals("", mTextView.getText().toString());
@@ -181,12 +174,10 @@
private void callOnKeyDown(final MultiTapKeyListener keyListener, final int keyCode,
final int numTimes) {
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- for (int i = 0; i < numTimes; i++) {
- keyListener.onKeyDown(mTextView, mTextView.getEditableText(), keyCode,
- new KeyEvent(KeyEvent.ACTION_DOWN, keyCode));
- }
+ mInstrumentation.runOnMainSync(() -> {
+ for (int i = 0; i < numTimes; i++) {
+ keyListener.onKeyDown(mTextView, mTextView.getEditableText(), keyCode,
+ new KeyEvent(KeyEvent.ACTION_DOWN, keyCode));
}
});
mInstrumentation.waitForIdleSync();
@@ -200,11 +191,7 @@
}
private void addSpace() {
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- mTextView.append(" ");
- }
- });
+ mInstrumentation.runOnMainSync(() -> mTextView.append(" "));
mInstrumentation.waitForIdleSync();
}
diff --git a/tests/tests/text/src/android/text/method/cts/NumberKeyListenerTest.java b/tests/tests/text/src/android/text/method/cts/NumberKeyListenerTest.java
index 09053f1..8aeefb2 100644
--- a/tests/tests/text/src/android/text/method/cts/NumberKeyListenerTest.java
+++ b/tests/tests/text/src/android/text/method/cts/NumberKeyListenerTest.java
@@ -16,8 +16,7 @@
package android.text.method.cts;
-import android.cts.util.KeyEventUtil;
-import android.text.Editable;
+import android.cts.util.CtsKeyEventUtil;
import android.text.Selection;
import android.text.Spannable;
import android.text.SpannableString;
@@ -30,14 +29,6 @@
public class NumberKeyListenerTest extends KeyListenerTestCase {
private MockNumberKeyListener mMockNumberKeyListener;
- private KeyEventUtil mKeyEventUtil;
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mKeyEventUtil = new KeyEventUtil(getInstrumentation());
- }
-
/**
* Check point:
* 1. Filter "Android test", return "".
@@ -133,37 +124,33 @@
final MockNumberKeyListener mockNumberKeyListener =
new MockNumberKeyListener(MockNumberKeyListener.DIGITS);
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- mTextView.setText(text, BufferType.EDITABLE);
- mTextView.setKeyListener(mockNumberKeyListener);
- mTextView.requestFocus();
- Selection.setSelection((Editable) mTextView.getText(), 0, 0);
- }
+ mActivity.runOnUiThread(() -> {
+ mTextView.setText(text, BufferType.EDITABLE);
+ mTextView.setKeyListener(mockNumberKeyListener);
+ mTextView.requestFocus();
+ Selection.setSelection(mTextView.getText(), 0, 0);
});
mInstrumentation.waitForIdleSync();
assertEquals("123456", mTextView.getText().toString());
// press '0' key.
- mKeyEventUtil.sendKeys(mTextView, KeyEvent.KEYCODE_0);
+ CtsKeyEventUtil.sendKeys(mInstrumentation, mTextView, KeyEvent.KEYCODE_0);
assertEquals("0123456", mTextView.getText().toString());
// an unaccepted key if it exists.
int keyCode = TextMethodUtils.getUnacceptedKeyCode(MockNumberKeyListener.DIGITS);
if (-1 != keyCode) {
- mKeyEventUtil.sendKeys(mTextView, keyCode);
+ CtsKeyEventUtil.sendKeys(mInstrumentation, mTextView, keyCode);
// text of TextView will not be changed.
assertEquals("0123456", mTextView.getText().toString());
}
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- mTextView.setKeyListener(null);
- mTextView.requestFocus();
- }
+ mInstrumentation.runOnMainSync(() -> {
+ mTextView.setKeyListener(null);
+ mTextView.requestFocus();
});
mInstrumentation.waitForIdleSync();
// press '0' key.
- mKeyEventUtil.sendKeys(mTextView, KeyEvent.KEYCODE_0);
+ CtsKeyEventUtil.sendKeys(mInstrumentation, mTextView, KeyEvent.KEYCODE_0);
assertEquals("0123456", mTextView.getText().toString());
}
diff --git a/tests/tests/text/src/android/text/method/cts/PasswordTransformationMethodTest.java b/tests/tests/text/src/android/text/method/cts/PasswordTransformationMethodTest.java
index 1131e6c..b872a96 100644
--- a/tests/tests/text/src/android/text/method/cts/PasswordTransformationMethodTest.java
+++ b/tests/tests/text/src/android/text/method/cts/PasswordTransformationMethodTest.java
@@ -17,18 +17,16 @@
package android.text.method.cts;
-import android.cts.util.KeyEventUtil;
+import android.app.Instrumentation;
+import android.cts.util.CtsKeyEventUtil;
import android.cts.util.PollingCheck;
-import android.graphics.Rect;
import android.os.ParcelFileDescriptor;
import android.provider.Settings.SettingNotFoundException;
import android.provider.Settings.System;
import android.test.ActivityInstrumentationTestCase2;
-import android.text.Editable;
import android.text.method.PasswordTransformationMethod;
import android.util.TypedValue;
import android.view.KeyCharacterMap;
-import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
@@ -38,6 +36,8 @@
import java.io.InputStream;
import java.util.Scanner;
+import static org.mockito.Mockito.*;
+
/**
* Test {@link PasswordTransformationMethod}.
*/
@@ -58,18 +58,18 @@
private CtsActivity mActivity;
- private MockPasswordTransformationMethod mMethod;
+ private PasswordTransformationMethod mMethod;
private EditText mEditText;
private CharSequence mTransformedText;
+ private Instrumentation mInstrumentation;
+
public PasswordTransformationMethodTest() {
super("android.text.cts", CtsActivity.class);
}
- private KeyEventUtil mKeyEventUtil;
-
@Override
protected void setUp() throws Exception {
super.setUp();
@@ -80,35 +80,32 @@
return mActivity.hasWindowFocus();
}
}.run();
- mMethod = new MockPasswordTransformationMethod();
+ mInstrumentation = getInstrumentation();
+ mMethod = spy(new PasswordTransformationMethod());
try {
- runTestOnUiThread(new Runnable() {
- public void run() {
- EditText editText = new EditTextNoIme(mActivity);
- editText.setTextSize(TypedValue.COMPLEX_UNIT_SP, 10);
- editText.setId(EDIT_TXT_ID);
- editText.setTransformationMethod(mMethod);
- Button button = new Button(mActivity);
- LinearLayout layout = new LinearLayout(mActivity);
- layout.setOrientation(LinearLayout.VERTICAL);
- layout.addView(editText, new LayoutParams(LayoutParams.MATCH_PARENT,
- LayoutParams.WRAP_CONTENT));
- layout.addView(button, new LayoutParams(LayoutParams.MATCH_PARENT,
- LayoutParams.WRAP_CONTENT));
- mActivity.setContentView(layout);
- editText.requestFocus();
- }
+ runTestOnUiThread(() -> {
+ EditText editText = new EditTextNoIme(mActivity);
+ editText.setTextSize(TypedValue.COMPLEX_UNIT_SP, 10);
+ editText.setId(EDIT_TXT_ID);
+ editText.setTransformationMethod(mMethod);
+ Button button = new Button(mActivity);
+ LinearLayout layout = new LinearLayout(mActivity);
+ layout.setOrientation(LinearLayout.VERTICAL);
+ layout.addView(editText, new LayoutParams(LayoutParams.MATCH_PARENT,
+ LayoutParams.WRAP_CONTENT));
+ layout.addView(button, new LayoutParams(LayoutParams.MATCH_PARENT,
+ LayoutParams.WRAP_CONTENT));
+ mActivity.setContentView(layout);
+ editText.requestFocus();
});
} catch (Throwable e) {
fail("Exception thrown is UI thread:" + e.getMessage());
}
- getInstrumentation().waitForIdleSync();
+ mInstrumentation.waitForIdleSync();
mEditText = (EditText) getActivity().findViewById(EDIT_TXT_ID);
assertTrue(mEditText.isFocused());
- mKeyEventUtil = new KeyEventUtil(getInstrumentation());
-
enableAppOps();
savePasswordPref();
switchShowPassword(true);
@@ -117,19 +114,19 @@
private void enableAppOps() {
StringBuilder cmd = new StringBuilder();
cmd.append("appops set ");
- cmd.append(getInstrumentation().getContext().getPackageName());
+ cmd.append(mInstrumentation.getContext().getPackageName());
cmd.append(" android:write_settings allow");
- getInstrumentation().getUiAutomation().executeShellCommand(cmd.toString());
+ mInstrumentation.getUiAutomation().executeShellCommand(cmd.toString());
StringBuilder query = new StringBuilder();
query.append("appops get ");
- query.append(getInstrumentation().getContext().getPackageName());
+ query.append(mInstrumentation.getContext().getPackageName());
query.append(" android:write_settings");
String queryStr = query.toString();
String result = "No operations.";
while (result.contains("No operations")) {
- ParcelFileDescriptor pfd = getInstrumentation().getUiAutomation().executeShellCommand(
+ ParcelFileDescriptor pfd = mInstrumentation.getUiAutomation().executeShellCommand(
queryStr);
InputStream inputStream = new FileInputStream(pfd.getFileDescriptor());
result = convertStreamToString(inputStream);
@@ -153,50 +150,40 @@
}
public void testTextChangedCallBacks() throws Throwable {
- runTestOnUiThread(new Runnable() {
- public void run() {
- mTransformedText = mMethod.getTransformation(mEditText.getText(), mEditText);
- }
+ runTestOnUiThread(() -> {
+ mTransformedText = mMethod.getTransformation(mEditText.getText(), mEditText);
});
- mMethod.reset();
+ reset(mMethod);
// 12-key support
KeyCharacterMap keymap = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
if (keymap.getKeyboardType() == KeyCharacterMap.NUMERIC) {
// "HELLO" in case of 12-key(NUMERIC) keyboard
- mKeyEventUtil.sendKeys(mEditText, "6*4 6*3 7*5 DPAD_RIGHT 7*5 7*6 DPAD_RIGHT");
+ CtsKeyEventUtil.sendKeys(mInstrumentation, mEditText,
+ "6*4 6*3 7*5 DPAD_RIGHT 7*5 7*6 DPAD_RIGHT");
}
else {
- mKeyEventUtil.sendKeys(mEditText, "H E 2*L O");
+ CtsKeyEventUtil.sendKeys(mInstrumentation, mEditText, "H E 2*L O");
}
- assertTrue(mMethod.hasCalledBeforeTextChanged());
- assertTrue(mMethod.hasCalledOnTextChanged());
- assertTrue(mMethod.hasCalledAfterTextChanged());
+ verify(mMethod, atLeastOnce()).beforeTextChanged(any(), anyInt(), anyInt(), anyInt());
+ verify(mMethod, atLeastOnce()).onTextChanged(any(), anyInt(), anyInt(), anyInt());
+ verify(mMethod, atLeastOnce()).afterTextChanged(any());
- mMethod.reset();
+ reset(mMethod);
- runTestOnUiThread(new Runnable() {
- public void run() {
- mEditText.append(" ");
- }
- });
+ runTestOnUiThread(() -> mEditText.append(" "));
// the appended string will not get transformed immediately
// "***** "
assertEquals("\u2022\u2022\u2022\u2022\u2022 ", mTransformedText.toString());
- assertTrue(mMethod.hasCalledBeforeTextChanged());
- assertTrue(mMethod.hasCalledOnTextChanged());
- assertTrue(mMethod.hasCalledAfterTextChanged());
+ verify(mMethod, atLeastOnce()).beforeTextChanged(any(), anyInt(), anyInt(), anyInt());
+ verify(mMethod, atLeastOnce()).onTextChanged(any(), anyInt(), anyInt(), anyInt());
+ verify(mMethod, atLeastOnce()).afterTextChanged(any());
// it will get transformed after a while
- new PollingCheck() {
- @Override
- protected boolean check() {
- // "******"
- return mTransformedText.toString()
- .equals("\u2022\u2022\u2022\u2022\u2022\u2022");
- }
- }.run();
+ // "******"
+ PollingCheck.waitFor(() -> mTransformedText.toString()
+ .equals("\u2022\u2022\u2022\u2022\u2022\u2022"));
}
public void testGetTransformation() {
@@ -226,18 +213,18 @@
public void testOnFocusChanged() {
// lose focus
- mMethod.reset();
+ reset(mMethod);
assertTrue(mEditText.isFocused());
- mKeyEventUtil.sendKeys(mEditText, "DPAD_DOWN");
+ CtsKeyEventUtil.sendKeys(mInstrumentation, mEditText, "DPAD_DOWN");
assertFalse(mEditText.isFocused());
- assertTrue(mMethod.hasCalledOnFocusChanged());
+ verify(mMethod, atLeastOnce()).onFocusChanged(any(), any(), anyBoolean(), anyInt(), any());
// gain focus
- mMethod.reset();
+ reset(mMethod);
assertFalse(mEditText.isFocused());
- mKeyEventUtil.sendKeys(mEditText, "DPAD_UP");
+ CtsKeyEventUtil.sendKeys(mInstrumentation, mEditText, "DPAD_UP");
assertTrue(mEditText.isFocused());
- assertTrue(mMethod.hasCalledOnFocusChanged());
+ verify(mMethod, atLeastOnce()).onFocusChanged(any(), any(), anyBoolean(), anyInt(), any());
}
private void savePasswordPref() {
@@ -261,62 +248,4 @@
System.putInt(mActivity.getContentResolver(), System.TEXT_SHOW_PASSWORD,
on ? 1 : 0);
}
-
- private static class MockPasswordTransformationMethod extends PasswordTransformationMethod {
- private boolean mHasCalledBeforeTextChanged;
-
- private boolean mHasCalledOnTextChanged;
-
- private boolean mHasCalledAfterTextChanged;
-
- private boolean mHasCalledOnFocusChanged;
-
- @Override
- public void afterTextChanged(Editable s) {
- super.afterTextChanged(s);
- mHasCalledAfterTextChanged = true;
- }
-
- @Override
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {
- super.beforeTextChanged(s, start, count, after);
- mHasCalledBeforeTextChanged = true;
- }
-
- @Override
- public void onFocusChanged(View view, CharSequence sourceText, boolean focused,
- int direction, Rect previouslyFocusedRect) {
- super.onFocusChanged(view, sourceText, focused, direction, previouslyFocusedRect);
- mHasCalledOnFocusChanged = true;
- }
-
- @Override
- public void onTextChanged(CharSequence s, int start, int before, int count) {
- super.onTextChanged(s, start, before, count);
- mHasCalledOnTextChanged = true;
- }
-
- public boolean hasCalledBeforeTextChanged() {
- return mHasCalledBeforeTextChanged;
- }
-
- public boolean hasCalledOnTextChanged() {
- return mHasCalledOnTextChanged;
- }
-
- public boolean hasCalledAfterTextChanged() {
- return mHasCalledAfterTextChanged;
- }
-
- public boolean hasCalledOnFocusChanged() {
- return mHasCalledOnFocusChanged;
- }
-
- public void reset() {
- mHasCalledBeforeTextChanged = false;
- mHasCalledOnTextChanged = false;
- mHasCalledAfterTextChanged = false;
- mHasCalledOnFocusChanged = false;
- }
- }
}
diff --git a/tests/tests/text/src/android/text/method/cts/QwertyKeyListenerTest.java b/tests/tests/text/src/android/text/method/cts/QwertyKeyListenerTest.java
index 39deda3..9fc06c2 100644
--- a/tests/tests/text/src/android/text/method/cts/QwertyKeyListenerTest.java
+++ b/tests/tests/text/src/android/text/method/cts/QwertyKeyListenerTest.java
@@ -35,7 +35,7 @@
}
public void testOnKeyDown_capitalizeNone() {
- QwertyKeyListener keyListener = QwertyKeyListener.getInstance(false, Capitalize.NONE);
+ final QwertyKeyListener keyListener = QwertyKeyListener.getInstance(false, Capitalize.NONE);
prepareEmptyTextView();
@@ -56,7 +56,7 @@
}
public void testOnKeyDown_capitalizeCharacters() {
- QwertyKeyListener keyListener = QwertyKeyListener.getInstance(false,
+ final QwertyKeyListener keyListener = QwertyKeyListener.getInstance(false,
Capitalize.CHARACTERS);
prepareEmptyTextView();
@@ -78,7 +78,7 @@
}
public void testOnKeyDown_capitalizeSentences() {
- QwertyKeyListener keyListener = QwertyKeyListener.getInstance(false,
+ final QwertyKeyListener keyListener = QwertyKeyListener.getInstance(false,
Capitalize.SENTENCES);
prepareEmptyTextView();
@@ -106,7 +106,7 @@
}
public void testOnKeyDown_capitalizeWords() {
- QwertyKeyListener keyListener = QwertyKeyListener.getInstance(false,
+ final QwertyKeyListener keyListener = QwertyKeyListener.getInstance(false,
Capitalize.WORDS);
prepareEmptyTextView();
@@ -131,31 +131,27 @@
}
private void prepareEmptyTextView() {
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- mTextView.setText("", BufferType.EDITABLE);
- Selection.setSelection(mTextView.getEditableText(), 0, 0);
- }
+ mInstrumentation.runOnMainSync(() -> {
+ mTextView.setText("", BufferType.EDITABLE);
+ Selection.setSelection(mTextView.getEditableText(), 0, 0);
});
mInstrumentation.waitForIdleSync();
assertEquals("", mTextView.getText().toString());
}
private void callOnKeyDown(final QwertyKeyListener keyListener, final int keyCode) {
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- keyListener.onKeyDown(mTextView, mTextView.getEditableText(), keyCode,
- new KeyEvent(KeyEvent.ACTION_DOWN, keyCode));
- }
- });
+ mInstrumentation.runOnMainSync(() -> keyListener.onKeyDown(mTextView,
+ mTextView.getEditableText(), keyCode,
+ new KeyEvent(KeyEvent.ACTION_DOWN, keyCode)));
mInstrumentation.waitForIdleSync();
}
public void testGetInstance() {
- QwertyKeyListener listener1 = QwertyKeyListener.getInstance(true, Capitalize.WORDS);
- QwertyKeyListener listener2 = QwertyKeyListener.getInstance(true, Capitalize.WORDS);
- QwertyKeyListener listener3 = QwertyKeyListener.getInstance(false, Capitalize.WORDS);
- QwertyKeyListener listener4 = QwertyKeyListener.getInstance(true, Capitalize.SENTENCES);
+ final QwertyKeyListener listener1 = QwertyKeyListener.getInstance(true, Capitalize.WORDS);
+ final QwertyKeyListener listener2 = QwertyKeyListener.getInstance(true, Capitalize.WORDS);
+ final QwertyKeyListener listener3 = QwertyKeyListener.getInstance(false, Capitalize.WORDS);
+ final QwertyKeyListener listener4 = QwertyKeyListener.getInstance(true,
+ Capitalize.SENTENCES);
assertNotNull(listener1);
assertNotNull(listener2);
@@ -166,8 +162,16 @@
assertNotSame(listener4, listener3);
}
+ public void testGetInstanceForFullKeyboard() {
+ final QwertyKeyListener listener = QwertyKeyListener.getInstanceForFullKeyboard();
+
+ assertNotNull(listener);
+ // auto correct and cap flags should not be set
+ assertEquals(InputType.TYPE_CLASS_TEXT, listener.getInputType());
+ }
+
public void testMarkAsReplaced() {
- SpannableStringBuilder content = new SpannableStringBuilder("123456");
+ final SpannableStringBuilder content = new SpannableStringBuilder("123456");
Object[] repl = content.getSpans(0, content.length(), Object.class);
assertEquals(0, repl.length);
@@ -201,11 +205,10 @@
public void testGetInputType() {
QwertyKeyListener listener = QwertyKeyListener.getInstance(false, Capitalize.NONE);
- int expected = InputType.TYPE_CLASS_TEXT;
- assertEquals(expected, listener.getInputType());
+ assertEquals(InputType.TYPE_CLASS_TEXT, listener.getInputType());
listener = QwertyKeyListener.getInstance(false, Capitalize.CHARACTERS);
- expected = InputType.TYPE_CLASS_TEXT
+ final int expected = InputType.TYPE_CLASS_TEXT
| InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS;
assertEquals(expected, listener.getInputType());
}
diff --git a/tests/tests/text/src/android/text/method/cts/ScrollingMovementMethodTest.java b/tests/tests/text/src/android/text/method/cts/ScrollingMovementMethodTest.java
index 8419850..79d52f1 100644
--- a/tests/tests/text/src/android/text/method/cts/ScrollingMovementMethodTest.java
+++ b/tests/tests/text/src/android/text/method/cts/ScrollingMovementMethodTest.java
@@ -30,7 +30,6 @@
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
-import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.widget.TextView;
import android.widget.TextView.BufferType;
@@ -73,38 +72,28 @@
}
public void testGetInstance() {
- MovementMethod method0 = ScrollingMovementMethod.getInstance();
+ final MovementMethod method0 = ScrollingMovementMethod.getInstance();
assertTrue(method0 instanceof ScrollingMovementMethod);
- MovementMethod method1 = ScrollingMovementMethod.getInstance();
+ final MovementMethod method1 = ScrollingMovementMethod.getInstance();
assertSame(method0, method1);
}
public void testOnTouchEventHorizontalMotion() throws Throwable {
- /*
- * All these assertions depends on whether the TextView has a layout.The text view will not
- * get layout in setContent method but in otherhandler's function. Assertion which is
- * following the setContent will not get the expecting result. It have to wait all the
- * handlers' operations on the UiTread to finish. So all these cases are divided into
- * several steps,setting the content at first, waiting the layout, and checking the
- * assertion at last.
- */
final ScrollingMovementMethod method = new ScrollingMovementMethod();
- runActionOnUiThread(new Runnable() {
- public void run() {
- mTextView.setText("hello world", BufferType.SPANNABLE);
- mTextView.setSingleLine();
- mSpannable = (Spannable) mTextView.getText();
- int width = WidgetTestUtils.convertDipToPixels(getActivity(), LITTLE_SPACE);
- getActivity().setContentView(mTextView,
- new ViewGroup.LayoutParams(width, ViewGroup.LayoutParams.WRAP_CONTENT));
- }
+ runActionOnUiThread(() -> {
+ mTextView.setText("hello world", BufferType.SPANNABLE);
+ mTextView.setSingleLine();
+ mSpannable = (Spannable) mTextView.getText();
+ final int width = WidgetTestUtils.convertDipToPixels(getActivity(), LITTLE_SPACE);
+ getActivity().setContentView(mTextView,
+ new LayoutParams(width, LayoutParams.WRAP_CONTENT));
});
assertNotNull(mTextView.getLayout());
- float rightMost = mTextView.getLayout().getLineRight(0) - mTextView.getWidth()
+ final float rightMost = mTextView.getLayout().getLineRight(0) - mTextView.getWidth()
+ mTextView.getTotalPaddingLeft() + mTextView.getTotalPaddingRight();
- int leftMost = mTextView.getScrollX();
+ final int leftMost = mTextView.getScrollX();
final long now = SystemClock.uptimeMillis();
assertTrue(getActionResult(new ActionRunnerWithResult() {
@@ -187,27 +176,17 @@
}
public void testOnTouchEventVerticalMotion() throws Throwable {
- /*
- * All these assertions depends on whether the TextView has a layout.The text view will not
- * get layout in setContent method but in otherhandler's function. Assertion which is
- * following the setContent will not get the expecting result. It have to wait all the
- * handlers' operations on the UiTread to finish. So all these cases are divided into
- * several steps,setting the content at first, waiting the layout, and checking the
- * assertion at last.
- */
final ScrollingMovementMethod method = new ScrollingMovementMethod();
- runActionOnUiThread(new Runnable() {
- public void run() {
- mTextView.setLines(1);
- getActivity().setContentView(mTextView,
- new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
- }
+ runActionOnUiThread(() -> {
+ mTextView.setLines(1);
+ getActivity().setContentView(mTextView,
+ new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
});
assertNotNull(mTextView.getLayout());
- float bottom = mTextView.getLayout().getHeight() - mTextView.getHeight()
+ final float bottom = mTextView.getLayout().getHeight() - mTextView.getHeight()
+ mTextView.getTotalPaddingTop() + mTextView.getTotalPaddingBottom();
- int top = mTextView.getScrollY();
+ final int top = mTextView.getScrollY();
final long now = SystemClock.uptimeMillis();
assertTrue(getActionResult(new ActionRunnerWithResult() {
@@ -290,75 +269,63 @@
}
public void testOnTouchEventExceptional() throws Throwable {
- /*
- * All these assertions depends on whether the TextView has a layout.The text view will not
- * get layout in setContent method but in otherhandler's function. Assertion which is
- * following the setContent will not get the expecting result. It have to wait all the
- * handlers' operations on the UiTread to finish. So all these cases are divided into
- * several steps,setting the content at first, waiting the layout, and checking the
- * assertion at last.
- */
- runActionOnUiThread(new Runnable() {
- public void run() {
- int width = WidgetTestUtils.convertDipToPixels(getActivity(), LITTLE_SPACE);
- getActivity().setContentView(mTextView,
- new ViewGroup.LayoutParams(width, ViewGroup.LayoutParams.WRAP_CONTENT));
- }
+ runActionOnUiThread(() -> {
+ final int width = WidgetTestUtils.convertDipToPixels(getActivity(), LITTLE_SPACE);
+ getActivity().setContentView(mTextView,
+ new LayoutParams(width, LayoutParams.WRAP_CONTENT));
});
assertNotNull(mTextView.getLayout());
- runActionOnUiThread(new Runnable() {
- public void run() {
- try {
- new ScrollingMovementMethod().onTouchEvent(mTextView, mSpannable, null);
- } catch (NullPointerException e) {
- // NPE is acceptable
- }
+ runActionOnUiThread(() -> {
+ try {
+ new ScrollingMovementMethod().onTouchEvent(mTextView, mSpannable, null);
+ } catch (NullPointerException e) {
+ // NPE is acceptable
+ }
- long now = SystemClock.uptimeMillis();
- try {
- new ScrollingMovementMethod().onTouchEvent(mTextView, null,
- MotionEvent.obtain(now, now, MotionEvent.ACTION_DOWN, 0, 0, 0));
- } catch (NullPointerException e) {
- // NPE is acceptable
- }
-
- try {
- new ScrollingMovementMethod().onTouchEvent(null, mSpannable,
- MotionEvent.obtain(now, now, MotionEvent.ACTION_DOWN, 0, 0, 0));
- } catch (NullPointerException e) {
- // NPE is acceptable
- }
-
- new ScrollingMovementMethod().onTouchEvent(mTextView, mSpannable,
+ long now = SystemClock.uptimeMillis();
+ try {
+ new ScrollingMovementMethod().onTouchEvent(mTextView, null,
MotionEvent.obtain(now, now, MotionEvent.ACTION_DOWN, 0, 0, 0));
- try {
- new ScrollingMovementMethod().onTouchEvent(null, mSpannable,
- MotionEvent.obtain(now, now, MotionEvent.ACTION_MOVE, - 10000, 0, 0));
- } catch (NullPointerException e) {
- // NPE is acceptable
- }
+ } catch (NullPointerException e) {
+ // NPE is acceptable
+ }
- try {
- new ScrollingMovementMethod().onTouchEvent(mTextView, null,
- MotionEvent.obtain(now, now, MotionEvent.ACTION_MOVE, - 10000, 0, 0));
- } catch (NullPointerException e) {
- // NPE is acceptable
- }
+ try {
+ new ScrollingMovementMethod().onTouchEvent(null, mSpannable,
+ MotionEvent.obtain(now, now, MotionEvent.ACTION_DOWN, 0, 0, 0));
+ } catch (NullPointerException e) {
+ // NPE is acceptable
+ }
- try {
- new ScrollingMovementMethod().onTouchEvent(null, mSpannable,
- MotionEvent.obtain(now, now, MotionEvent.ACTION_UP, - 10000, 0, 0));
- } catch (NullPointerException e) {
- // NPE is acceptable
- }
+ new ScrollingMovementMethod().onTouchEvent(mTextView, mSpannable,
+ MotionEvent.obtain(now, now, MotionEvent.ACTION_DOWN, 0, 0, 0));
+ try {
+ new ScrollingMovementMethod().onTouchEvent(null, mSpannable,
+ MotionEvent.obtain(now, now, MotionEvent.ACTION_MOVE, -10000, 0, 0));
+ } catch (NullPointerException e) {
+ // NPE is acceptable
+ }
- try {
- new ScrollingMovementMethod().onTouchEvent(mTextView, null,
- MotionEvent.obtain(now, now, MotionEvent.ACTION_UP, - 10000, 0, 0));
- } catch (NullPointerException e) {
- // NPE is acceptable
- }
+ try {
+ new ScrollingMovementMethod().onTouchEvent(mTextView, null,
+ MotionEvent.obtain(now, now, MotionEvent.ACTION_MOVE, -10000, 0, 0));
+ } catch (NullPointerException e) {
+ // NPE is acceptable
+ }
+
+ try {
+ new ScrollingMovementMethod().onTouchEvent(null, mSpannable,
+ MotionEvent.obtain(now, now, MotionEvent.ACTION_UP, -10000, 0, 0));
+ } catch (NullPointerException e) {
+ // NPE is acceptable
+ }
+
+ try {
+ new ScrollingMovementMethod().onTouchEvent(mTextView, null,
+ MotionEvent.obtain(now, now, MotionEvent.ACTION_UP, -10000, 0, 0));
+ } catch (NullPointerException e) {
+ // NPE is acceptable
}
});
}
@@ -368,151 +335,85 @@
}
public void testOnKeyDownVerticalMovement() throws Throwable {
- /*
- * All these assertions depends on whether the TextView has a layout.The text view will not
- * get layout in setContent method but in otherhandler's function. Assertion which is
- * following the setContent will not get the expecting result. It have to wait all the
- * handlers' operations on the UiTread to finish. So all these cases are divided into
- * several steps,setting the content at first, waiting the layout, and checking the
- * assertion at last.
- */
- runActionOnUiThread(new Runnable() {
- public void run() {
- getActivity().setContentView(mTextView);
- }
- });
+ runActionOnUiThread(() -> getActivity().setContentView(mTextView));
assertNotNull(mTextView.getLayout());
assertVisibleLineInTextView(0);
final MyScrollingMovementMethod method = new MyScrollingMovementMethod();
- runActionOnUiThread(new Runnable() {
- public void run() {
- method.onKeyDown(mTextView, null, KeyEvent.KEYCODE_DPAD_DOWN,
- new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_DOWN));
- }
- });
+ runActionOnUiThread(() -> method.onKeyDown(mTextView, null, KeyEvent.KEYCODE_DPAD_DOWN,
+ new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_DOWN)));
assertVisibleLineInTextView(1);
- runActionOnUiThread(new Runnable() {
- public void run() {
- method.onKeyDown(mTextView, null, KeyEvent.KEYCODE_DPAD_UP,
- new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_UP));
- }
- });
+ runActionOnUiThread(() -> method.onKeyDown(mTextView, null, KeyEvent.KEYCODE_DPAD_UP,
+ new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_UP)));
assertVisibleLineInTextView(0);
}
public void testOnKeyDownHorizontalMovement() throws Throwable {
- /*
- * All these assertions depends on whether the TextView has a layout.The text view will not
- * get layout in setContent method but in otherhandler's function. Assertion which is
- * following the setContent will not get the expecting result. It have to wait all the
- * handlers' operations on the UiTread to finish. So all these cases are divided into
- * several steps,setting the content at first, waiting the layout, and checking the
- * assertion at last.
- */
- runActionOnUiThread(new Runnable() {
- public void run() {
- mTextView.setText("short");
- mTextView.setSingleLine();
- int width = WidgetTestUtils.convertDipToPixels(getActivity(), LITTLE_SPACE);
- getActivity().setContentView(mTextView,
- new ViewGroup.LayoutParams(width, ViewGroup.LayoutParams.WRAP_CONTENT));
- }
+ runActionOnUiThread(() -> {
+ mTextView.setText("short");
+ mTextView.setSingleLine();
+ final int width = WidgetTestUtils.convertDipToPixels(getActivity(), LITTLE_SPACE);
+ getActivity().setContentView(mTextView,
+ new LayoutParams(width, LayoutParams.WRAP_CONTENT));
});
assertNotNull(mTextView.getLayout());
final MyScrollingMovementMethod method = new MyScrollingMovementMethod();
int previousScrollX = mTextView.getScrollX();
- runActionOnUiThread(new Runnable() {
- public void run() {
- method.onKeyDown(mTextView, (Spannable) mTextView.getText(),
- KeyEvent.KEYCODE_DPAD_RIGHT, new KeyEvent(KeyEvent.ACTION_DOWN,
- KeyEvent.KEYCODE_DPAD_RIGHT));
- }
- });
+ runActionOnUiThread(() -> method.onKeyDown(mTextView, (Spannable) mTextView.getText(),
+ KeyEvent.KEYCODE_DPAD_RIGHT, new KeyEvent(KeyEvent.ACTION_DOWN,
+ KeyEvent.KEYCODE_DPAD_RIGHT)));
assertTrue(mTextView.getScrollX() > previousScrollX);
previousScrollX = mTextView.getScrollX();
- runActionOnUiThread(new Runnable() {
- public void run() {
- method.onKeyDown(mTextView, (Spannable) mTextView.getText(),
- KeyEvent.KEYCODE_DPAD_LEFT, new KeyEvent(KeyEvent.ACTION_DOWN,
- KeyEvent.KEYCODE_DPAD_LEFT));
- }
- });
+ runActionOnUiThread(() -> method.onKeyDown(mTextView, (Spannable) mTextView.getText(),
+ KeyEvent.KEYCODE_DPAD_LEFT, new KeyEvent(KeyEvent.ACTION_DOWN,
+ KeyEvent.KEYCODE_DPAD_LEFT)));
assertTrue(mTextView.getScrollX() < previousScrollX);
previousScrollX = mTextView.getScrollX();
assertVisibleLineInTextView(0);
- runActionOnUiThread(new Runnable() {
- public void run() {
- assertFalse(method.onKeyDown(mTextView, mSpannable, 0,
- new KeyEvent(KeyEvent.ACTION_DOWN, 0)));
- }
- });
+ runActionOnUiThread(() -> assertFalse(method.onKeyDown(mTextView, mSpannable, 0,
+ new KeyEvent(KeyEvent.ACTION_DOWN, 0))));
assertEquals(previousScrollX, mTextView.getScrollX());
assertVisibleLineInTextView(0);
}
public void testOnKeyDownExceptions() throws Throwable {
- /*
- * All these assertions depends on whether the TextView has a layout.The text view will not
- * get layout in setContent method but in otherhandler's function. Assertion which is
- * following the setContent will not get the expecting result. It have to wait all the
- * handlers' operations on the UiTread to finish. So all these cases are divided into
- * several steps,setting the content at first, waiting the layout, and checking the
- * assertion at last.
- */
- runActionOnUiThread(new Runnable() {
- public void run() {
- getActivity().setContentView(mTextView);
- }
- });
+ runActionOnUiThread(() -> getActivity().setContentView(mTextView));
assertNotNull(mTextView.getLayout());
final MyScrollingMovementMethod method = new MyScrollingMovementMethod();
- runActionOnUiThread(new Runnable() {
- public void run() {
- try {
- method.onKeyDown(null, mSpannable, KeyEvent.KEYCODE_DPAD_RIGHT,
- new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_UP));
- } catch (NullPointerException e) {
- // NPE is acceptable
- }
+ runActionOnUiThread(() -> {
+ try {
+ method.onKeyDown(null, mSpannable, KeyEvent.KEYCODE_DPAD_RIGHT,
+ new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_UP));
+ } catch (NullPointerException e) {
+ // NPE is acceptable
+ }
- try {
- method.onKeyDown(mTextView, null, KeyEvent.KEYCODE_DPAD_RIGHT,
- new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_UP));
- } catch (NullPointerException e) {
- // NPE is acceptable
- }
+ try {
+ method.onKeyDown(mTextView, null, KeyEvent.KEYCODE_DPAD_RIGHT,
+ new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_UP));
+ } catch (NullPointerException e) {
+ // NPE is acceptable
+ }
- try {
- method.onKeyDown(mTextView, mSpannable, KeyEvent.KEYCODE_DPAD_RIGHT, null);
- } catch (NullPointerException e) {
- // NPE is acceptable
- }
+ try {
+ method.onKeyDown(mTextView, mSpannable, KeyEvent.KEYCODE_DPAD_RIGHT, null);
+ } catch (NullPointerException e) {
+ // NPE is acceptable
}
});
}
public void testVerticalMovement() throws Throwable {
- /*
- * All these assertions depends on whether the TextView has a layout.The text view will not
- * get layout in setContent method but in otherhandler's function. Assertion which is
- * following the setContent will not get the expecting result. It have to wait all the
- * handlers' operations on the UiTread to finish. So all these cases are divided into
- * several steps,setting the content at first, waiting the layout, and checking the
- * assertion at last.
- */
final MyScrollingMovementMethod method = new MyScrollingMovementMethod();
- runActionOnUiThread(new Runnable() {
- public void run() {
- mTextView.setLines(1);
- getActivity().setContentView(mTextView,
- new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
- }
+ runActionOnUiThread(() -> {
+ mTextView.setLines(1);
+ getActivity().setContentView(mTextView,
+ new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
});
assertNotNull(mTextView.getLayout());
@@ -558,31 +459,29 @@
}));
assertVisibleLineInTextView(0);
- runActionOnUiThread(new Runnable() {
- public void run() {
- try {
- method.up(null, mSpannable);
- } catch (NullPointerException e) {
- // NPE is acceptable
- }
+ runActionOnUiThread(() -> {
+ try {
+ method.up(null, mSpannable);
+ } catch (NullPointerException e) {
+ // NPE is acceptable
+ }
- try {
- method.up(mTextView, null);
- } catch (NullPointerException e) {
- // NPE is acceptable
- }
+ try {
+ method.up(mTextView, null);
+ } catch (NullPointerException e) {
+ // NPE is acceptable
+ }
- try {
- method.down(null, mSpannable);
- } catch (NullPointerException e) {
- // NPE is acceptable
- }
+ try {
+ method.down(null, mSpannable);
+ } catch (NullPointerException e) {
+ // NPE is acceptable
+ }
- try {
- method.down(mTextView, null);
- } catch (NullPointerException e) {
- // NPE is acceptable
- }
+ try {
+ method.down(mTextView, null);
+ } catch (NullPointerException e) {
+ // NPE is acceptable
}
});
}
@@ -613,7 +512,7 @@
// NPE is acceptable
}
- long now = SystemClock.uptimeMillis();
+ final long now = SystemClock.uptimeMillis();
try {
KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_RIGHT);
new ScrollingMovementMethod().onKeyDown(mTextView, mSpannable,
@@ -637,9 +536,9 @@
}
public void testOnTrackballEvent() {
- long now = SystemClock.uptimeMillis();
- MotionEvent event = MotionEvent.obtain(now, now, 0, 2, -2, 0);
- MyScrollingMovementMethod mockMethod = new MyScrollingMovementMethod();
+ final long now = SystemClock.uptimeMillis();
+ final MotionEvent event = MotionEvent.obtain(now, now, 0, 2, -2, 0);
+ final MyScrollingMovementMethod mockMethod = new MyScrollingMovementMethod();
assertFalse(mockMethod.onTrackballEvent(mTextView, mSpannable, event));
assertFalse(mockMethod.onTrackballEvent(null, mSpannable, event));
@@ -648,10 +547,10 @@
}
public void testOnKeyUp() {
- ScrollingMovementMethod method = new ScrollingMovementMethod();
- SpannableString spannable = new SpannableString("Test Content");
- KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_0);
- TextView view = new TextViewNoIme(getActivity());
+ final ScrollingMovementMethod method = new ScrollingMovementMethod();
+ final SpannableString spannable = new SpannableString("Test Content");
+ final KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_0);
+ final TextView view = new TextViewNoIme(getActivity());
view.setTextSize(TypedValue.COMPLEX_UNIT_SP, 12);
assertFalse(method.onKeyUp(view, spannable, KeyEvent.KEYCODE_0, event));
@@ -663,14 +562,6 @@
}
public void testOnTakeFocus() throws Throwable {
- /*
- * All these assertions depends on whether the TextView has a layout.The text view will not
- * get layout in setContent method but in otherhandler's function. Assertion which is
- * following the setContent will not get the expecting result. It have to wait all the
- * handlers' operations on the UiTread to finish. So all these cases are divided into
- * several steps,setting the content at first, waiting the layout, and checking the
- * assertion at last.
- */
final ScrollingMovementMethod method = new ScrollingMovementMethod();
// wait until the text view gets layout
assertNull(mTextView.getLayout());
@@ -680,71 +571,49 @@
// NPE is acceptable
}
- runActionOnUiThread(new Runnable() {
- public void run() {
- int height = WidgetTestUtils.convertDipToPixels(getActivity(), LITTLE_SPACE);
- getActivity().setContentView(mTextView,
- new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
- height));
- }
+ runActionOnUiThread(() -> {
+ final int height = WidgetTestUtils.convertDipToPixels(getActivity(), LITTLE_SPACE);
+ getActivity().setContentView(mTextView,
+ new LayoutParams(LayoutParams.MATCH_PARENT,
+ height));
});
- Layout layout = mTextView.getLayout();
+ final Layout layout = mTextView.getLayout();
assertNotNull(layout);
int previousScrollY = mTextView.getScrollY();
- runActionOnUiThread(new Runnable() {
- public void run() {
- method.onTakeFocus(mTextView, mSpannable, View.FOCUS_BACKWARD);
- }
- });
+ runActionOnUiThread(() -> method.onTakeFocus(mTextView, mSpannable, View.FOCUS_BACKWARD));
assertTrue(mTextView.getScrollY() >= previousScrollY);
assertVisibleLineInTextView(2);
previousScrollY = mTextView.getScrollY();
- runActionOnUiThread(new Runnable() {
- public void run() {
- method.onTakeFocus(mTextView, mSpannable, View.FOCUS_FORWARD);
- }
- });
+ runActionOnUiThread(() -> method.onTakeFocus(mTextView, mSpannable, View.FOCUS_FORWARD));
assertTrue(mTextView.getScrollY() <= previousScrollY);
assertVisibleLineInTextView(0);
- runActionOnUiThread(new Runnable() {
- public void run() {
- try {
- method.onTakeFocus(null, mSpannable, View.FOCUS_FORWARD);
- } catch (NullPointerException e) {
- // NPE is acceptable
- }
+ runActionOnUiThread(() -> {
+ try {
+ method.onTakeFocus(null, mSpannable, View.FOCUS_FORWARD);
+ } catch (NullPointerException e) {
+ // NPE is acceptable
+ }
- try {
- method.onTakeFocus(mTextView, null, View.FOCUS_FORWARD);
- } catch (NullPointerException e) {
- // NPE is acceptable
- }
+ try {
+ method.onTakeFocus(mTextView, null, View.FOCUS_FORWARD);
+ } catch (NullPointerException e) {
+ // NPE is acceptable
}
});
}
public void testHorizontalMovement() throws Throwable {
- /*
- * All these assertions depends on whether the TextView has a layout.The text view will not
- * get layout in setContent method but in otherhandler's function. Assertion which is
- * following the setContent will not get the expecting result. It have to wait all the
- * handlers' operations on the UiTread to finish. So all these cases are divided into
- * several steps,setting the content at first, waiting the layout, and checking the
- * assertion at last.
- */
final MyScrollingMovementMethod method = new MyScrollingMovementMethod();
- runActionOnUiThread(new Runnable() {
- public void run() {
- mTextView.setText("short");
- mTextView.setSingleLine();
- DisplayMetrics dm = getActivity().getResources().getDisplayMetrics();
- int width = (int) (LITTLE_SPACE * dm.scaledDensity);
- getActivity().setContentView(mTextView,
- new ViewGroup.LayoutParams(width, ViewGroup.LayoutParams.WRAP_CONTENT));
- }
+ runActionOnUiThread(() -> {
+ mTextView.setText("short");
+ mTextView.setSingleLine();
+ final DisplayMetrics dm = getActivity().getResources().getDisplayMetrics();
+ final int width = (int) (LITTLE_SPACE * dm.scaledDensity);
+ getActivity().setContentView(mTextView,
+ new LayoutParams(width, LayoutParams.WRAP_CONTENT));
});
assertNotNull(mTextView.getLayout());
@@ -783,38 +652,26 @@
}
public void testOnKeyOther() throws Throwable {
- runActionOnUiThread(new Runnable() {
- public void run() {
- getActivity().setContentView(mTextView);
- }
- });
+ runActionOnUiThread(() -> getActivity().setContentView(mTextView));
assertNotNull(mTextView.getLayout());
assertVisibleLineInTextView(0);
final MyScrollingMovementMethod method = new MyScrollingMovementMethod();
- runActionOnUiThread(new Runnable() {
- public void run() {
- method.onKeyOther(mTextView, null,
- new KeyEvent(0, 0, KeyEvent.ACTION_MULTIPLE,
- KeyEvent.KEYCODE_DPAD_DOWN, 2));
- }
- });
+ runActionOnUiThread(() -> method.onKeyOther(mTextView, null,
+ new KeyEvent(0, 0, KeyEvent.ACTION_MULTIPLE,
+ KeyEvent.KEYCODE_DPAD_DOWN, 2)));
assertVisibleLineInTextView(1);
- runActionOnUiThread(new Runnable() {
- public void run() {
- method.onKeyOther(mTextView, null,
- new KeyEvent(0, 0, KeyEvent.ACTION_MULTIPLE,
- KeyEvent.KEYCODE_DPAD_UP, 2));
- }
- });
+ runActionOnUiThread(() -> method.onKeyOther(mTextView, null,
+ new KeyEvent(0, 0, KeyEvent.ACTION_MULTIPLE,
+ KeyEvent.KEYCODE_DPAD_UP, 2)));
assertVisibleLineInTextView(0);
}
private void assertVisibleLineInTextView(int line) {
- Layout layout = mTextView.getLayout();
- int scrollY = mTextView.getScrollY();
- int padding = mTextView.getTotalPaddingTop() + mTextView.getTotalPaddingBottom();
+ final Layout layout = mTextView.getLayout();
+ final int scrollY = mTextView.getScrollY();
+ final int padding = mTextView.getTotalPaddingTop() + mTextView.getTotalPaddingBottom();
assertTrue(layout.getLineForVertical(scrollY) <= line);
assertTrue(layout.getLineForVertical(scrollY + mTextView.getHeight() - padding) >= line);
}
diff --git a/tests/tests/text/src/android/text/method/cts/TextKeyListenerTest.java b/tests/tests/text/src/android/text/method/cts/TextKeyListenerTest.java
index 19003cf..47c0a6c 100644
--- a/tests/tests/text/src/android/text/method/cts/TextKeyListenerTest.java
+++ b/tests/tests/text/src/android/text/method/cts/TextKeyListenerTest.java
@@ -16,7 +16,7 @@
package android.text.method.cts;
-import android.cts.util.KeyEventUtil;
+import android.cts.util.CtsKeyEventUtil;
import android.os.SystemClock;
import android.test.UiThreadTest;
import android.text.Editable;
@@ -30,20 +30,14 @@
import android.view.KeyEvent;
import android.widget.TextView.BufferType;
+import static org.mockito.Mockito.*;
+
public class TextKeyListenerTest extends KeyListenerTestCase {
/**
* time out of MultiTapKeyListener. longer than 2000ms in case the system is sluggish.
*/
private static final long TIME_OUT = 3000;
- private KeyEventUtil mKeyEventUtil;
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mKeyEventUtil = new KeyEventUtil(getInstrumentation());
- }
-
public void testConstructor() {
new TextKeyListener(Capitalize.NONE, true);
@@ -83,20 +77,17 @@
}
public void testOnSpanAdded() {
- final MockTextKeyListener mockTextKeyListener
- = new MockTextKeyListener(Capitalize.CHARACTERS, true);
+ final TextKeyListener mockTextKeyListener = spy(
+ new TextKeyListener(Capitalize.CHARACTERS, true));
final Spannable text = new SpannableStringBuilder("123456");
- assertFalse(mockTextKeyListener.hadAddedSpan());
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- mTextView.setKeyListener(mockTextKeyListener);
- mTextView.setText(text, BufferType.EDITABLE);
- }
+ verify(mockTextKeyListener, never()).onSpanAdded(any(), any(), anyInt(), anyInt());
+ mInstrumentation.runOnMainSync(() -> {
+ mTextView.setKeyListener(mockTextKeyListener);
+ mTextView.setText(text, BufferType.EDITABLE);
});
mInstrumentation.waitForIdleSync();
-
- assertTrue(mockTextKeyListener.hadAddedSpan());
+ verify(mockTextKeyListener, atLeastOnce()).onSpanAdded(any(), any(), anyInt(), anyInt());
mockTextKeyListener.release();
}
@@ -152,7 +143,7 @@
CharSequence text = "123456";
mTextView.setText(text, BufferType.EDITABLE);
- Editable content = (Editable) mTextView.getText();
+ Editable content = mTextView.getText();
assertEquals(text, content.toString());
TextKeyListener.clear(content);
@@ -187,18 +178,16 @@
final TextKeyListener textKeyListener
= TextKeyListener.getInstance(false, Capitalize.NONE);
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- mTextView.setText("", BufferType.EDITABLE);
- Selection.setSelection((Editable) mTextView.getText(), 0, 0);
- mTextView.setKeyListener(textKeyListener);
- mTextView.requestFocus();
- }
+ mInstrumentation.runOnMainSync(() -> {
+ mTextView.setText("", BufferType.EDITABLE);
+ Selection.setSelection(mTextView.getText(), 0, 0);
+ mTextView.setKeyListener(textKeyListener);
+ mTextView.requestFocus();
});
mInstrumentation.waitForIdleSync();
assertEquals("", mTextView.getText().toString());
- mKeyEventUtil.sendKeys(mTextView, KeyEvent.KEYCODE_4);
+ CtsKeyEventUtil.sendKeys(mInstrumentation, mTextView, KeyEvent.KEYCODE_4);
waitForListenerTimeout();
String text = mTextView.getText().toString();
int keyType = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD).getKeyboardType();
@@ -219,12 +208,10 @@
final TextKeyListener textKeyListener
= TextKeyListener.getInstance(false, Capitalize.NONE);
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- mTextView.setText("", BufferType.EDITABLE);
- Selection.setSelection((Editable) mTextView.getText(), 0, 0);
- mTextView.setKeyListener(textKeyListener);
- }
+ mInstrumentation.runOnMainSync(() -> {
+ mTextView.setText("", BufferType.EDITABLE);
+ Selection.setSelection(mTextView.getText(), 0, 0);
+ mTextView.setKeyListener(textKeyListener);
});
mInstrumentation.waitForIdleSync();
assertEquals("", mTextView.getText().toString());
@@ -232,7 +219,7 @@
// test ACTION_MULTIPLE KEYCODE_UNKNOWN key event.
KeyEvent event = new KeyEvent(SystemClock.uptimeMillis(), text,
1, KeyEvent.FLAG_WOKE_HERE);
- mKeyEventUtil.sendKey(mTextView, event);
+ CtsKeyEventUtil.sendKey(mInstrumentation, mTextView, event);
mInstrumentation.waitForIdleSync();
// the text of TextView is never changed, onKeyOther never works.
// assertEquals(text, mTextView.getText().toString()); issue 1731439
@@ -253,27 +240,4 @@
listener.release();
}
- /**
- * A mocked {@link android.text.method.TextKeyListener} for testing purposes.
- *
- * Tracks whether {@link MockTextKeyListener#onSpanAdded(Spannable, Object, int, int)} has been
- * called.
- */
- private class MockTextKeyListener extends TextKeyListener {
- private boolean mHadAddedSpan;
-
- public MockTextKeyListener(Capitalize cap, boolean autotext) {
- super(cap, autotext);
- }
-
- @Override
- public void onSpanAdded(Spannable s, Object what, int start, int end) {
- mHadAddedSpan = true;
- super.onSpanAdded(s, what, start, end);
- }
-
- public boolean hadAddedSpan() {
- return mHadAddedSpan;
- }
- }
}
diff --git a/tests/tests/text/src/android/text/method/cts/TimeKeyListenerTest.java b/tests/tests/text/src/android/text/method/cts/TimeKeyListenerTest.java
index 4f68d54..9fed354 100644
--- a/tests/tests/text/src/android/text/method/cts/TimeKeyListenerTest.java
+++ b/tests/tests/text/src/android/text/method/cts/TimeKeyListenerTest.java
@@ -16,7 +16,7 @@
package android.text.method.cts;
-import android.cts.util.KeyEventUtil;
+import android.cts.util.CtsKeyEventUtil;
import android.text.InputType;
import android.text.method.TimeKeyListener;
import android.view.KeyCharacterMap;
@@ -24,14 +24,6 @@
public class TimeKeyListenerTest extends KeyListenerTestCase {
- private KeyEventUtil mKeyEventUtil;
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mKeyEventUtil = new KeyEventUtil(getInstrumentation());
- }
-
public void testConstructor() {
new TimeKeyListener();
}
@@ -76,12 +68,12 @@
assertEquals(expectedText, mTextView.getText().toString());
// press '1' key.
- mKeyEventUtil.sendString(mTextView, "1");
+ CtsKeyEventUtil.sendString(mInstrumentation, mTextView, "1");
expectedText += "1";
assertEquals(expectedText, mTextView.getText().toString());
// press '2' key.
- mKeyEventUtil.sendString(mTextView, "2");
+ CtsKeyEventUtil.sendString(mInstrumentation, mTextView, "2");
expectedText += "2";
assertEquals("12", mTextView.getText().toString());
@@ -89,35 +81,35 @@
KeyCharacterMap kcm = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
if ('a' == kcm.getMatch(KeyEvent.KEYCODE_A, TimeKeyListener.CHARACTERS)) {
expectedText += "a";
- mKeyEventUtil.sendKeyDownUp(mTextView, KeyEvent.KEYCODE_A);
+ CtsKeyEventUtil.sendKeyDownUp(mInstrumentation, mTextView, KeyEvent.KEYCODE_A);
assertEquals(expectedText, mTextView.getText().toString());
}
// press 'p' key if producible
if ('p' == kcm.getMatch(KeyEvent.KEYCODE_P, TimeKeyListener.CHARACTERS)) {
expectedText += "p";
- mKeyEventUtil.sendKeyDownUp(mTextView, KeyEvent.KEYCODE_P);
+ CtsKeyEventUtil.sendKeyDownUp(mInstrumentation, mTextView, KeyEvent.KEYCODE_P);
assertEquals(expectedText, mTextView.getText().toString());
}
// press 'm' key if producible
if ('m' == kcm.getMatch(KeyEvent.KEYCODE_M, TimeKeyListener.CHARACTERS)) {
expectedText += "m";
- mKeyEventUtil.sendKeyDownUp(mTextView, KeyEvent.KEYCODE_M);
+ CtsKeyEventUtil.sendKeyDownUp(mInstrumentation, mTextView, KeyEvent.KEYCODE_M);
assertEquals(expectedText, mTextView.getText().toString());
}
// press an unaccepted key if it exists.
int keyCode = TextMethodUtils.getUnacceptedKeyCode(TimeKeyListener.CHARACTERS);
if (-1 != keyCode) {
- mKeyEventUtil.sendKeys(mTextView, keyCode);
+ CtsKeyEventUtil.sendKeys(mInstrumentation, mTextView, keyCode);
assertEquals(expectedText, mTextView.getText().toString());
}
setKeyListenerSync(null);
// press '1' key.
- mKeyEventUtil.sendString(mTextView, "1");
+ CtsKeyEventUtil.sendString(mInstrumentation, mTextView, "1");
assertEquals(expectedText, mTextView.getText().toString());
}
diff --git a/tests/tests/text/src/android/text/method/cts/TouchTest.java b/tests/tests/text/src/android/text/method/cts/TouchTest.java
index 233539c..cc672f9 100644
--- a/tests/tests/text/src/android/text/method/cts/TouchTest.java
+++ b/tests/tests/text/src/android/text/method/cts/TouchTest.java
@@ -56,51 +56,33 @@
final TextView tv = new TextViewNoIme(mActivity);
tv.setTextSize(TypedValue.COMPLEX_UNIT_SP, 10);
- runTestOnUiThread(new Runnable() {
- public void run() {
- mActivity.setContentView(tv);
- tv.setSingleLine(true);
- tv.setLines(2);
- }
+ runTestOnUiThread(() -> {
+ mActivity.setContentView(tv);
+ tv.setSingleLine(true);
+ tv.setLines(2);
});
getInstrumentation().waitForIdleSync();
TextPaint paint = tv.getPaint();
final Layout layout = tv.getLayout();
- runTestOnUiThread(new Runnable() {
- public void run() {
- tv.setText(LONG_TEXT);
- }
- });
+ runTestOnUiThread(() -> tv.setText(LONG_TEXT));
getInstrumentation().waitForIdleSync();
// get the total length of string
final int width = getTextWidth(LONG_TEXT, paint);
- runTestOnUiThread(new Runnable() {
- public void run() {
- Touch.scrollTo(tv, layout, width - tv.getWidth() - 1, 0);
- }
- });
+ runTestOnUiThread(() -> Touch.scrollTo(tv, layout, width - tv.getWidth() - 1, 0));
getInstrumentation().waitForIdleSync();
assertEquals(width - tv.getWidth() - 1, tv.getScrollX());
assertEquals(0, tv.getScrollY());
// the X to which scroll is greater than the total length of string.
- runTestOnUiThread(new Runnable() {
- public void run() {
- Touch.scrollTo(tv, layout, width + 100, 5);
- }
- });
+ runTestOnUiThread(() -> Touch.scrollTo(tv, layout, width + 100, 5));
getInstrumentation().waitForIdleSync();
assertEquals(width - tv.getWidth(), tv.getScrollX(), 1.0f);
assertEquals(5, tv.getScrollY());
- runTestOnUiThread(new Runnable() {
- public void run() {
- Touch.scrollTo(tv, layout, width - 10, 5);
- }
- });
+ runTestOnUiThread(() -> Touch.scrollTo(tv, layout, width - 10, 5));
getInstrumentation().waitForIdleSync();
assertEquals(width - tv.getWidth(), tv.getScrollX(), 1.0f);
assertEquals(5, tv.getScrollY());
@@ -126,12 +108,10 @@
assertTrue(dragAmount > 0);
final String finalText = text;
final SpannableString spannable = new SpannableString(finalText);
- runTestOnUiThread(new Runnable() {
- public void run() {
- mActivity.setContentView(tv);
- tv.setSingleLine(true);
- tv.setText(finalText);
- }
+ runTestOnUiThread(() -> {
+ mActivity.setContentView(tv);
+ tv.setSingleLine(true);
+ tv.setText(finalText);
});
getInstrumentation().waitForIdleSync();
@@ -146,11 +126,7 @@
assertEquals(0, tv.getScrollX());
assertEquals(0, tv.getScrollY());
mReturnFromTouchEvent = false;
- runTestOnUiThread(new Runnable() {
- public void run() {
- mReturnFromTouchEvent = Touch.onTouchEvent(tv, spannable, event1);
- }
- });
+ runTestOnUiThread(() -> mReturnFromTouchEvent = Touch.onTouchEvent(tv, spannable, event1));
getInstrumentation().waitForIdleSync();
assertTrue(mReturnFromTouchEvent);
// TextView has not been scrolled.
@@ -160,11 +136,7 @@
assertEquals(0, Touch.getInitialScrollY(tv, spannable));
mReturnFromTouchEvent = false;
- runTestOnUiThread(new Runnable() {
- public void run() {
- mReturnFromTouchEvent = Touch.onTouchEvent(tv, spannable, event2);
- }
- });
+ runTestOnUiThread(() -> mReturnFromTouchEvent = Touch.onTouchEvent(tv, spannable, event2));
getInstrumentation().waitForIdleSync();
assertTrue(mReturnFromTouchEvent);
// TextView has been scrolled.
@@ -174,11 +146,7 @@
assertEquals(0, Touch.getInitialScrollY(tv, spannable));
mReturnFromTouchEvent = false;
- runTestOnUiThread(new Runnable() {
- public void run() {
- mReturnFromTouchEvent = Touch.onTouchEvent(tv, spannable, event3);
- }
- });
+ runTestOnUiThread(() -> mReturnFromTouchEvent = Touch.onTouchEvent(tv, spannable, event3));
getInstrumentation().waitForIdleSync();
assertTrue(mReturnFromTouchEvent);
// TextView has not been scrolled.
diff --git a/tests/tests/text/src/android/text/style/cts/ImageSpanTest.java b/tests/tests/text/src/android/text/style/cts/ImageSpanTest.java
index 90282b8..3c83f68 100644
--- a/tests/tests/text/src/android/text/style/cts/ImageSpanTest.java
+++ b/tests/tests/text/src/android/text/style/cts/ImageSpanTest.java
@@ -40,6 +40,10 @@
new ImageSpan(b, DynamicDrawableSpan.ALIGN_BOTTOM);
new ImageSpan(b, DynamicDrawableSpan.ALIGN_BASELINE);
+ new ImageSpan(mContext, b);
+ new ImageSpan(mContext, b, DynamicDrawableSpan.ALIGN_BOTTOM);
+ new ImageSpan(mContext, b, DynamicDrawableSpan.ALIGN_BASELINE);
+
Drawable d = mContext.getResources().getDrawable(R.drawable.pass);
new ImageSpan(d);
new ImageSpan(d, DynamicDrawableSpan.ALIGN_BOTTOM);
diff --git a/tests/tests/text/src/android/text/style/cts/SuggestionSpanTest.java b/tests/tests/text/src/android/text/style/cts/SuggestionSpanTest.java
index ff5167c..86f7fe9 100644
--- a/tests/tests/text/src/android/text/style/cts/SuggestionSpanTest.java
+++ b/tests/tests/text/src/android/text/style/cts/SuggestionSpanTest.java
@@ -16,16 +16,136 @@
package android.text.style.cts;
-import junit.framework.TestCase;
+import static org.junit.Assert.assertArrayEquals;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.os.LocaleList;
import android.os.Parcel;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
import android.text.style.SuggestionSpan;
import java.util.Locale;
-public class SuggestionSpanTest extends TestCase {
+/**
+ * Test {@link SuggestionSpan}.
+ */
+public class SuggestionSpanTest extends AndroidTestCase {
+
+ @SmallTest
+ public void testConstructorWithContext() {
+ final String[] suggestions = new String[]{"suggestion1", "suggestion2"};
+ final Configuration overrideConfig = new Configuration();
+ final Locale locale = Locale.forLanguageTag("az-Arab");
+ overrideConfig.setLocales(new LocaleList(locale));
+ final Context context = getContext().createConfigurationContext(overrideConfig);
+
+ final SuggestionSpan span = new SuggestionSpan(context, suggestions,
+ SuggestionSpan.FLAG_AUTO_CORRECTION);
+
+ assertEquals(locale, span.getLocaleObject());
+ assertArrayEquals(suggestions, span.getSuggestions());
+ assertEquals(SuggestionSpan.FLAG_AUTO_CORRECTION, span.getFlags());
+ }
+
+ @SmallTest
+ public void testGetSuggestionSpans() {
+ final String[] suggestions = new String[]{"suggestion1", "suggestion2"};
+ final SuggestionSpan span = new SuggestionSpan(Locale.forLanguageTag("en"), suggestions,
+ SuggestionSpan.FLAG_AUTO_CORRECTION);
+ assertArrayEquals("Should return the correct suggestions array",
+ suggestions, span.getSuggestions());
+
+ final SuggestionSpan clonedSpan = cloneViaParcel(span);
+ assertArrayEquals("Should (de)serialize suggestions",
+ suggestions, clonedSpan.getSuggestions());
+ }
+
+ @SmallTest
+ public void testGetSuggestionSpans_emptySuggestions() {
+ final String[] suggestions = new String[0];
+ final SuggestionSpan span = new SuggestionSpan(Locale.forLanguageTag("en"), suggestions,
+ SuggestionSpan.FLAG_AUTO_CORRECTION);
+ assertArrayEquals("Span should return empty suggestion array",
+ suggestions, span.getSuggestions());
+
+ // also test parceling
+ final SuggestionSpan clonedSpan = cloneViaParcel(span);
+ assertArrayEquals("Should (de)serialize empty suggestions array",
+ suggestions, clonedSpan.getSuggestions());
+ }
+
+ @SmallTest
+ public void testGetSuggestionSpans_suggestionsWithNullValue() {
+ final String[] suggestions = new String[]{"suggestion", null};
+ final SuggestionSpan span = new SuggestionSpan(Locale.forLanguageTag("en"), suggestions,
+ SuggestionSpan.FLAG_AUTO_CORRECTION);
+ assertArrayEquals("Should accept and return null suggestions",
+ suggestions, span.getSuggestions());
+
+ final SuggestionSpan clonedSpan = cloneViaParcel(span);
+ assertArrayEquals("Should (de)serialize null in suggestions array",
+ suggestions, clonedSpan.getSuggestions());
+ }
+
+ @SmallTest
+ public void testGetFlags() {
+ final String[] anySuggestions = new String[0];
+ final int flag = SuggestionSpan.FLAG_AUTO_CORRECTION;
+ SuggestionSpan span = new SuggestionSpan(Locale.forLanguageTag("en"), anySuggestions, flag);
+
+ assertEquals("Should return the flag passed in constructor",
+ flag, span.getFlags());
+
+ final SuggestionSpan clonedSpan = cloneViaParcel(span);
+ assertEquals("Should (de)serialize flags", flag, clonedSpan.getFlags());
+ }
+
+ @SmallTest
+ public void testEquals_returnsTrueForDeserializedInstances() {
+ final SuggestionSpan span1 = new SuggestionSpan(null, Locale.forLanguageTag("en"),
+ new String[0], SuggestionSpan.FLAG_AUTO_CORRECTION, SuggestionSpan.class);
+ final SuggestionSpan span2 = cloneViaParcel(span1);
+
+ assertTrue("(De)serialized instances should be equal", span1.equals(span2));
+ }
+
+ @SmallTest
+ public void testEquals_returnsTrueIfTheFlagsAreDifferent() {
+ final SuggestionSpan span1 = new SuggestionSpan(null, Locale.forLanguageTag("en"),
+ new String[0], SuggestionSpan.FLAG_AUTO_CORRECTION, SuggestionSpan.class);
+ final SuggestionSpan span2 = cloneViaParcel(span1);
+ span2.setFlags(SuggestionSpan.FLAG_EASY_CORRECT);
+
+ assertEquals("Should return the flag passed in set function",
+ SuggestionSpan.FLAG_EASY_CORRECT, span2.getFlags());
+
+ assertTrue("Instances with different flags should be equal", span1.equals(span2));
+ }
+
+ @SmallTest
+ public void testEquals_returnsFalseIfCreationTimeIsNotSame() {
+ final Locale anyLocale = Locale.forLanguageTag("en");
+ final String[] anySuggestions = new String[0];
+ final int anyFlags = SuggestionSpan.FLAG_AUTO_CORRECTION;
+ final Class anyClass = SuggestionSpan.class;
+
+ final SuggestionSpan span1 = new SuggestionSpan(null, anyLocale, anySuggestions, anyFlags,
+ anyClass);
+ try {
+ // let some time pass before constructing the other span
+ Thread.sleep(2);
+ } catch (InterruptedException e) {
+ // ignore
+ }
+ final SuggestionSpan span2 = new SuggestionSpan(null, anyLocale, anySuggestions, anyFlags,
+ anyClass);
+
+ assertFalse("Instances created at different time should not be equal", span2.equals(span1));
+ }
/**
* @param locale a {@link Locale} object.
@@ -62,6 +182,7 @@
assertEquals(getNonNullLocaleString(locale), cloned.getLocale());
}
+ @SmallTest
public void testGetLocaleObject() {
checkGetLocaleObject(Locale.forLanguageTag("en"));
checkGetLocaleObject(Locale.forLanguageTag("en-GB"));
diff --git a/tests/tests/text/src/android/text/style/cts/TtsSpanTest.java b/tests/tests/text/src/android/text/style/cts/TtsSpanTest.java
index c718a37..ef0b4ce 100644
--- a/tests/tests/text/src/android/text/style/cts/TtsSpanTest.java
+++ b/tests/tests/text/src/android/text/style/cts/TtsSpanTest.java
@@ -20,6 +20,7 @@
import android.os.PersistableBundle;
import android.test.suitebuilder.annotation.SmallTest;
import android.text.style.TtsSpan;
+
import junit.framework.TestCase;
public class TtsSpanTest extends TestCase {
@@ -38,7 +39,7 @@
@SmallTest
public void testGetArgs() {
TtsSpan t = new TtsSpan("test.type.one", bundle);
- PersistableBundle args = t.getArgs();
+ final PersistableBundle args = t.getArgs();
assertEquals(4, args.size());
assertEquals("value.one", args.getString("argument.one"));
assertEquals("value.two", args.getString("argument.two"));
@@ -75,7 +76,7 @@
TtsSpan t = new TtsSpan(p);
assertEquals("test.type.five", t.getType());
- PersistableBundle args = t.getArgs();
+ final PersistableBundle args = t.getArgs();
assertEquals(4, args.size());
assertEquals("value.one", args.getString("argument.one"));
assertEquals("value.two", args.getString("argument.two"));
@@ -88,13 +89,13 @@
@SmallTest
public void testBuilder() {
- final TtsSpan t = (new TtsSpan.Builder<>("test.type.builder"))
+ final TtsSpan t = new TtsSpan.Builder<>("test.type.builder")
.setStringArgument("argument.string", "value")
.setIntArgument("argument.int", Integer.MAX_VALUE)
.setLongArgument("argument.long", Long.MAX_VALUE)
.build();
assertEquals("test.type.builder", t.getType());
- PersistableBundle args = t.getArgs();
+ final PersistableBundle args = t.getArgs();
assertEquals(3, args.size());
assertEquals("value", args.getString("argument.string"));
assertEquals(Integer.MAX_VALUE, args.getInt("argument.int"));
@@ -103,14 +104,14 @@
@SmallTest
public void testSemioticClassBuilder() {
- final TtsSpan t = (new TtsSpan.SemioticClassBuilder<>("test.type.semioticClassBuilder"))
+ final TtsSpan t = new TtsSpan.SemioticClassBuilder<>("test.type.semioticClassBuilder")
.setGender(TtsSpan.GENDER_FEMALE)
.setAnimacy(TtsSpan.ANIMACY_ANIMATE)
.setMultiplicity(TtsSpan.MULTIPLICITY_SINGLE)
.setCase(TtsSpan.CASE_NOMINATIVE)
.build();
assertEquals("test.type.semioticClassBuilder", t.getType());
- PersistableBundle args = t.getArgs();
+ final PersistableBundle args = t.getArgs();
assertEquals(4, args.size());
assertEquals(TtsSpan.GENDER_FEMALE, args.getString(TtsSpan.ARG_GENDER));
assertEquals(TtsSpan.ANIMACY_ANIMATE, args.getString(TtsSpan.ARG_ANIMACY));
@@ -121,18 +122,18 @@
@SmallTest
public void testTextBuilder() {
{
- final TtsSpan t = (new TtsSpan.TextBuilder())
+ final TtsSpan t = new TtsSpan.TextBuilder()
.setText("text")
.build();
assertEquals(TtsSpan.TYPE_TEXT, t.getType());
- PersistableBundle args = t.getArgs();
+ final PersistableBundle args = t.getArgs();
assertEquals(1, args.size());
assertEquals("text", args.getString(TtsSpan.ARG_TEXT));
}
{
- final TtsSpan t = (new TtsSpan.TextBuilder("text")).build();
+ final TtsSpan t = new TtsSpan.TextBuilder("text").build();
assertEquals(TtsSpan.TYPE_TEXT, t.getType());
- PersistableBundle args = t.getArgs();
+ final PersistableBundle args = t.getArgs();
assertEquals(1, args.size());
assertEquals("text", args.getString(TtsSpan.ARG_TEXT));
}
@@ -141,34 +142,34 @@
@SmallTest
public void testCardinalBuilder() {
{
- final TtsSpan t = (new TtsSpan.CardinalBuilder())
+ final TtsSpan t = new TtsSpan.CardinalBuilder()
.setNumber(Long.MAX_VALUE)
.build();
assertEquals(TtsSpan.TYPE_CARDINAL, t.getType());
- PersistableBundle args = t.getArgs();
+ final PersistableBundle args = t.getArgs();
assertEquals(1, args.size());
assertEquals(String.valueOf(Long.MAX_VALUE), args.getString(TtsSpan.ARG_NUMBER));
}
{
- final TtsSpan t = (new TtsSpan.CardinalBuilder())
+ final TtsSpan t = new TtsSpan.CardinalBuilder()
.setNumber("10")
.build();
assertEquals(TtsSpan.TYPE_CARDINAL, t.getType());
- PersistableBundle args = t.getArgs();
+ final PersistableBundle args = t.getArgs();
assertEquals(1, args.size());
assertEquals("10", args.getString(TtsSpan.ARG_NUMBER));
}
{
- final TtsSpan t = (new TtsSpan.CardinalBuilder(Long.MAX_VALUE)).build();
+ final TtsSpan t = new TtsSpan.CardinalBuilder(Long.MAX_VALUE).build();
assertEquals(TtsSpan.TYPE_CARDINAL, t.getType());
- PersistableBundle args = t.getArgs();
+ final PersistableBundle args = t.getArgs();
assertEquals(1, args.size());
assertEquals(String.valueOf(Long.MAX_VALUE), args.getString(TtsSpan.ARG_NUMBER));
}
{
- final TtsSpan t = (new TtsSpan.CardinalBuilder("10")).build();
+ final TtsSpan t = new TtsSpan.CardinalBuilder("10").build();
assertEquals(TtsSpan.TYPE_CARDINAL, t.getType());
- PersistableBundle args = t.getArgs();
+ final PersistableBundle args = t.getArgs();
assertEquals(1, args.size());
assertEquals("10", args.getString(TtsSpan.ARG_NUMBER));
}
@@ -177,34 +178,34 @@
@SmallTest
public void testOrdinalBuilder() {
{
- final TtsSpan t = (new TtsSpan.OrdinalBuilder())
+ final TtsSpan t = new TtsSpan.OrdinalBuilder()
.setNumber(Long.MAX_VALUE)
.build();
assertEquals(TtsSpan.TYPE_ORDINAL, t.getType());
- PersistableBundle args = t.getArgs();
+ final PersistableBundle args = t.getArgs();
assertEquals(1, args.size());
assertEquals(String.valueOf(Long.MAX_VALUE), args.getString(TtsSpan.ARG_NUMBER));
}
{
- final TtsSpan t = (new TtsSpan.OrdinalBuilder())
+ final TtsSpan t = new TtsSpan.OrdinalBuilder()
.setNumber("10")
.build();
assertEquals(TtsSpan.TYPE_ORDINAL, t.getType());
- PersistableBundle args = t.getArgs();
+ final PersistableBundle args = t.getArgs();
assertEquals(1, args.size());
assertEquals("10", args.getString(TtsSpan.ARG_NUMBER));
}
{
- final TtsSpan t = (new TtsSpan.OrdinalBuilder(Long.MAX_VALUE)).build();
+ final TtsSpan t = new TtsSpan.OrdinalBuilder(Long.MAX_VALUE).build();
assertEquals(TtsSpan.TYPE_ORDINAL, t.getType());
- PersistableBundle args = t.getArgs();
+ final PersistableBundle args = t.getArgs();
assertEquals(1, args.size());
assertEquals(String.valueOf(Long.MAX_VALUE), args.getString(TtsSpan.ARG_NUMBER));
}
{
- final TtsSpan t = (new TtsSpan.OrdinalBuilder("10")).build();
+ final TtsSpan t = new TtsSpan.OrdinalBuilder("10").build();
assertEquals(TtsSpan.TYPE_ORDINAL, t.getType());
- PersistableBundle args = t.getArgs();
+ final PersistableBundle args = t.getArgs();
assertEquals(1, args.size());
assertEquals("10", args.getString(TtsSpan.ARG_NUMBER));
}
@@ -213,137 +214,250 @@
@SmallTest
public void testDecimalBuilder() {
{
- final TtsSpan t = (new TtsSpan.DecimalBuilder())
+ final TtsSpan t = new TtsSpan.DecimalBuilder()
.setArgumentsFromDouble(10.25, 1, 2)
.build();
assertEquals(TtsSpan.TYPE_DECIMAL, t.getType());
- PersistableBundle args = t.getArgs();
+ final PersistableBundle args = t.getArgs();
assertEquals(2, args.size());
assertEquals("10", args.getString(TtsSpan.ARG_INTEGER_PART));
assertEquals("25", args.getString(TtsSpan.ARG_FRACTIONAL_PART));
}
{
- final TtsSpan t = (new TtsSpan.DecimalBuilder(10.25, 1, 2)).build();
+ final TtsSpan t = new TtsSpan.DecimalBuilder(10.25, 1, 2).build();
assertEquals(TtsSpan.TYPE_DECIMAL, t.getType());
- PersistableBundle args = t.getArgs();
+ final PersistableBundle args = t.getArgs();
assertEquals(2, args.size());
assertEquals("10", args.getString(TtsSpan.ARG_INTEGER_PART));
assertEquals("25", args.getString(TtsSpan.ARG_FRACTIONAL_PART));
}
{
- final TtsSpan t = (new TtsSpan.DecimalBuilder())
+ final TtsSpan t = new TtsSpan.DecimalBuilder()
.setArgumentsFromDouble(10, 0, 0)
.build();
assertEquals(TtsSpan.TYPE_DECIMAL, t.getType());
- PersistableBundle args = t.getArgs();
+ final PersistableBundle args = t.getArgs();
assertEquals(1, args.size());
assertEquals("10", args.getString(TtsSpan.ARG_INTEGER_PART));
}
{
- final TtsSpan t = (new TtsSpan.DecimalBuilder(10, 0, 0)).build();
+ final TtsSpan t = new TtsSpan.DecimalBuilder(10, 0, 0).build();
assertEquals(TtsSpan.TYPE_DECIMAL, t.getType());
- PersistableBundle args = t.getArgs();
+ final PersistableBundle args = t.getArgs();
assertEquals(1, args.size());
assertEquals("10", args.getString(TtsSpan.ARG_INTEGER_PART));
}
{
- final TtsSpan t = (new TtsSpan.DecimalBuilder())
+ final TtsSpan t = new TtsSpan.DecimalBuilder()
.setArgumentsFromDouble(10.25, 10, 10)
.build();
assertEquals(TtsSpan.TYPE_DECIMAL, t.getType());
- PersistableBundle args = t.getArgs();
+ final PersistableBundle args = t.getArgs();
assertEquals(2, args.size());
assertEquals("10", args.getString(TtsSpan.ARG_INTEGER_PART));
assertEquals("2500000000", args.getString(TtsSpan.ARG_FRACTIONAL_PART));
}
+ {
+ final TtsSpan t = new TtsSpan.DecimalBuilder("10", "25").build();
+ assertEquals(TtsSpan.TYPE_DECIMAL, t.getType());
+ final PersistableBundle args = t.getArgs();
+ assertEquals(2, args.size());
+ assertEquals("10", args.getString(TtsSpan.ARG_INTEGER_PART));
+ assertEquals("25", args.getString(TtsSpan.ARG_FRACTIONAL_PART));
+ }
+ {
+ final TtsSpan t = new TtsSpan.DecimalBuilder(null, null).build();
+ assertEquals(TtsSpan.TYPE_DECIMAL, t.getType());
+ final PersistableBundle args = t.getArgs();
+ assertEquals(2, args.size());
+ assertEquals(null, args.getString(TtsSpan.ARG_INTEGER_PART));
+ assertEquals(null, args.getString(TtsSpan.ARG_FRACTIONAL_PART));
+ }
+ {
+ final TtsSpan t = new TtsSpan.DecimalBuilder().setIntegerPart(10).build();
+ assertEquals(TtsSpan.TYPE_DECIMAL, t.getType());
+ final PersistableBundle args = t.getArgs();
+ assertEquals(1, args.size());
+ assertEquals("10", args.getString(TtsSpan.ARG_INTEGER_PART));
+ }
+ {
+ final TtsSpan t = new TtsSpan.DecimalBuilder().setIntegerPart("10").build();
+ assertEquals(TtsSpan.TYPE_DECIMAL, t.getType());
+ final PersistableBundle args = t.getArgs();
+ assertEquals(1, args.size());
+ assertEquals("10", args.getString(TtsSpan.ARG_INTEGER_PART));
+ }
+ {
+ final TtsSpan t = new TtsSpan.DecimalBuilder().setIntegerPart(null).build();
+ assertEquals(TtsSpan.TYPE_DECIMAL, t.getType());
+ final PersistableBundle args = t.getArgs();
+ assertEquals(1, args.size());
+ assertEquals(null, args.getString(TtsSpan.ARG_INTEGER_PART));
+ }
+ {
+ final TtsSpan t = new TtsSpan.DecimalBuilder().setFractionalPart("25").build();
+ assertEquals(TtsSpan.TYPE_DECIMAL, t.getType());
+ final PersistableBundle args = t.getArgs();
+ assertEquals(1, args.size());
+ assertEquals("25", args.getString(TtsSpan.ARG_FRACTIONAL_PART));
+ }
+ {
+ final TtsSpan t = new TtsSpan.DecimalBuilder().setFractionalPart(null).build();
+ assertEquals(TtsSpan.TYPE_DECIMAL, t.getType());
+ final PersistableBundle args = t.getArgs();
+ assertEquals(1, args.size());
+ assertEquals(null, args.getString(TtsSpan.ARG_FRACTIONAL_PART));
+ }
+ {
+ final TtsSpan t = new TtsSpan.DecimalBuilder().build();
+ assertEquals(TtsSpan.TYPE_DECIMAL, t.getType());
+ final PersistableBundle args = t.getArgs();
+ assertEquals(0, args.size());
+ }
}
@SmallTest
public void testFractionBuilder() {
{
- final TtsSpan t = (new TtsSpan.FractionBuilder())
+ final TtsSpan t = new TtsSpan.FractionBuilder()
.setIntegerPart(10)
.setNumerator(3)
.setDenominator(100)
.build();
assertEquals(TtsSpan.TYPE_FRACTION, t.getType());
- PersistableBundle args = t.getArgs();
+ final PersistableBundle args = t.getArgs();
assertEquals(3, args.size());
assertEquals("10", args.getString(TtsSpan.ARG_INTEGER_PART));
assertEquals("3", args.getString(TtsSpan.ARG_NUMERATOR));
assertEquals("100", args.getString(TtsSpan.ARG_DENOMINATOR));
}
{
- final TtsSpan t = (new TtsSpan.FractionBuilder(10, 3, 100)).build();
+ final TtsSpan t = new TtsSpan.FractionBuilder(10, 3, 100).build();
assertEquals(TtsSpan.TYPE_FRACTION, t.getType());
- PersistableBundle args = t.getArgs();
+ final PersistableBundle args = t.getArgs();
assertEquals(3, args.size());
assertEquals("10", args.getString(TtsSpan.ARG_INTEGER_PART));
assertEquals("3", args.getString(TtsSpan.ARG_NUMERATOR));
assertEquals("100", args.getString(TtsSpan.ARG_DENOMINATOR));
}
+ {
+ final TtsSpan t = new TtsSpan.FractionBuilder().setIntegerPart("10").build();
+ assertEquals(TtsSpan.TYPE_FRACTION, t.getType());
+ final PersistableBundle args = t.getArgs();
+ assertEquals(1, args.size());
+ assertEquals("10", args.getString(TtsSpan.ARG_INTEGER_PART));
+ assertEquals(null, args.getString(TtsSpan.ARG_NUMERATOR));
+ assertEquals(null, args.getString(TtsSpan.ARG_DENOMINATOR));
+ }
+ {
+ final TtsSpan t = new TtsSpan.FractionBuilder().setNumerator("3").build();
+ assertEquals(TtsSpan.TYPE_FRACTION, t.getType());
+ final PersistableBundle args = t.getArgs();
+ assertEquals(1, args.size());
+ assertEquals("3", args.getString(TtsSpan.ARG_NUMERATOR));
+ }
+ {
+ final TtsSpan t = new TtsSpan.FractionBuilder().setDenominator("100").build();
+ assertEquals(TtsSpan.TYPE_FRACTION, t.getType());
+ final PersistableBundle args = t.getArgs();
+ assertEquals(1, args.size());
+ assertEquals("100", args.getString(TtsSpan.ARG_DENOMINATOR));
+ }
+ {
+ final TtsSpan t = new TtsSpan.FractionBuilder().build();
+ assertEquals(TtsSpan.TYPE_FRACTION, t.getType());
+ final PersistableBundle args = t.getArgs();
+ assertEquals(0, args.size());
+ }
}
@SmallTest
public void testMeasureBuilder() {
{
- final TtsSpan t = (new TtsSpan.MeasureBuilder())
+ final TtsSpan t = new TtsSpan.MeasureBuilder()
.setNumber(10)
.setUnit("unit")
.build();
assertEquals(TtsSpan.TYPE_MEASURE, t.getType());
- PersistableBundle args = t.getArgs();
+ final PersistableBundle args = t.getArgs();
assertEquals(2, args.size());
assertEquals("10", args.getString(TtsSpan.ARG_NUMBER));
assertEquals("unit", args.getString(TtsSpan.ARG_UNIT));
}
{
- final TtsSpan t = (new TtsSpan.MeasureBuilder())
+ final TtsSpan t = new TtsSpan.MeasureBuilder()
.setIntegerPart(10)
.setFractionalPart("25")
.setUnit("unit")
.build();
assertEquals(TtsSpan.TYPE_MEASURE, t.getType());
- PersistableBundle args = t.getArgs();
+ final PersistableBundle args = t.getArgs();
assertEquals(3, args.size());
assertEquals("10", args.getString(TtsSpan.ARG_INTEGER_PART));
assertEquals("25", args.getString(TtsSpan.ARG_FRACTIONAL_PART));
assertEquals("unit", args.getString(TtsSpan.ARG_UNIT));
}
{
- final TtsSpan t = (new TtsSpan.MeasureBuilder())
+ final TtsSpan t = new TtsSpan.MeasureBuilder()
.setIntegerPart(10)
.setNumerator(3)
.setDenominator(100)
.setUnit("unit")
.build();
assertEquals(TtsSpan.TYPE_MEASURE, t.getType());
- PersistableBundle args = t.getArgs();
+ final PersistableBundle args = t.getArgs();
assertEquals(4, args.size());
assertEquals("10", args.getString(TtsSpan.ARG_INTEGER_PART));
assertEquals("3", args.getString(TtsSpan.ARG_NUMERATOR));
assertEquals("100", args.getString(TtsSpan.ARG_DENOMINATOR));
assertEquals("unit", args.getString(TtsSpan.ARG_UNIT));
}
+ {
+ final TtsSpan t = new TtsSpan.MeasureBuilder().setIntegerPart("10").build();
+ assertEquals(TtsSpan.TYPE_MEASURE, t.getType());
+ final PersistableBundle args = t.getArgs();
+ assertEquals(1, args.size());
+ assertEquals("10", args.getString(TtsSpan.ARG_INTEGER_PART));
+ }
+ {
+ final TtsSpan t = new TtsSpan.MeasureBuilder().setNumerator("3").build();
+ assertEquals(TtsSpan.TYPE_MEASURE, t.getType());
+ final PersistableBundle args = t.getArgs();
+ assertEquals(1, args.size());
+ assertEquals("3", args.getString(TtsSpan.ARG_NUMERATOR));
+ }
+ {
+ final TtsSpan t = new TtsSpan.MeasureBuilder().setDenominator("100").build();
+ assertEquals(TtsSpan.TYPE_MEASURE, t.getType());
+ final PersistableBundle args = t.getArgs();
+ assertEquals(1, args.size());
+ assertEquals("100", args.getString(TtsSpan.ARG_DENOMINATOR));
+ }
+ {
+ final TtsSpan t = new TtsSpan.MeasureBuilder().build();
+ assertEquals(TtsSpan.TYPE_MEASURE, t.getType());
+ final PersistableBundle args = t.getArgs();
+ assertEquals(0, args.size());
+ }
}
@SmallTest
public void testTimeBuilder() {
{
- final TtsSpan t = (new TtsSpan.TimeBuilder())
+ final TtsSpan t = new TtsSpan.TimeBuilder()
.setHours(20)
.setMinutes(50)
.build();
assertEquals(TtsSpan.TYPE_TIME, t.getType());
- PersistableBundle args = t.getArgs();
+ final PersistableBundle args = t.getArgs();
assertEquals(2, args.size());
assertEquals(20, args.getInt(TtsSpan.ARG_HOURS));
assertEquals(50, args.getInt(TtsSpan.ARG_MINUTES));
}
{
- final TtsSpan t = (new TtsSpan.TimeBuilder(20, 50)).build();
+ final TtsSpan t = new TtsSpan.TimeBuilder(20, 50).build();
assertEquals(TtsSpan.TYPE_TIME, t.getType());
- PersistableBundle args = t.getArgs();
+ final PersistableBundle args = t.getArgs();
assertEquals(2, args.size());
assertEquals(20, args.getInt(TtsSpan.ARG_HOURS));
assertEquals(50, args.getInt(TtsSpan.ARG_MINUTES));
@@ -353,14 +467,14 @@
@SmallTest
public void testDateBuilder() {
{
- final TtsSpan t = (new TtsSpan.DateBuilder())
+ final TtsSpan t = new TtsSpan.DateBuilder()
.setWeekday(3)
.setDay(16)
.setMonth(3)
.setYear(2016)
.build();
assertEquals(TtsSpan.TYPE_DATE, t.getType());
- PersistableBundle args = t.getArgs();
+ final PersistableBundle args = t.getArgs();
assertEquals(4, args.size());
assertEquals(3, args.getInt(TtsSpan.ARG_WEEKDAY));
assertEquals(16, args.getInt(TtsSpan.ARG_DAY));
@@ -368,9 +482,9 @@
assertEquals(2016, args.getInt(TtsSpan.ARG_YEAR));
}
{
- final TtsSpan t = (new TtsSpan.DateBuilder(3, 16, 3, 2016)).build();
+ final TtsSpan t = new TtsSpan.DateBuilder(3, 16, 3, 2016).build();
assertEquals(TtsSpan.TYPE_DATE, t.getType());
- PersistableBundle args = t.getArgs();
+ final PersistableBundle args = t.getArgs();
assertEquals(4, args.size());
assertEquals(3, args.getInt(TtsSpan.ARG_WEEKDAY));
assertEquals(16, args.getInt(TtsSpan.ARG_DAY));
@@ -378,9 +492,9 @@
assertEquals(2016, args.getInt(TtsSpan.ARG_YEAR));
}
{
- final TtsSpan t = (new TtsSpan.DateBuilder(3, 16, null, null)).build();
+ final TtsSpan t = new TtsSpan.DateBuilder(3, 16, null, null).build();
assertEquals(TtsSpan.TYPE_DATE, t.getType());
- PersistableBundle args = t.getArgs();
+ final PersistableBundle args = t.getArgs();
assertEquals(2, args.size());
assertEquals(3, args.getInt(TtsSpan.ARG_WEEKDAY));
assertEquals(16, args.getInt(TtsSpan.ARG_DAY));
@@ -389,40 +503,56 @@
@SmallTest
public void testMoneyBuilder() {
- final TtsSpan t = (new TtsSpan.MoneyBuilder())
- .setIntegerPart(10)
- .setFractionalPart("25")
- .setCurrency("USD")
- .setQuantity("1000")
- .build();
- assertEquals(TtsSpan.TYPE_MONEY, t.getType());
- PersistableBundle args = t.getArgs();
- assertEquals(4, args.size());
- assertEquals("10", args.getString(TtsSpan.ARG_INTEGER_PART));
- assertEquals("25", args.getString(TtsSpan.ARG_FRACTIONAL_PART));
- assertEquals("USD", args.getString(TtsSpan.ARG_CURRENCY));
- assertEquals("1000", args.getString(TtsSpan.ARG_QUANTITY));
+ {
+ final TtsSpan t = new TtsSpan.MoneyBuilder()
+ .setIntegerPart(10)
+ .setFractionalPart("25")
+ .setCurrency("USD")
+ .setQuantity("1000")
+ .build();
+ assertEquals(TtsSpan.TYPE_MONEY, t.getType());
+ final PersistableBundle args = t.getArgs();
+ assertEquals(4, args.size());
+ assertEquals("10", args.getString(TtsSpan.ARG_INTEGER_PART));
+ assertEquals("25", args.getString(TtsSpan.ARG_FRACTIONAL_PART));
+ assertEquals("USD", args.getString(TtsSpan.ARG_CURRENCY));
+ assertEquals("1000", args.getString(TtsSpan.ARG_QUANTITY));
+ }
+ {
+ final TtsSpan t = new TtsSpan.MoneyBuilder().setIntegerPart("10").build();
+ assertEquals(TtsSpan.TYPE_MONEY, t.getType());
+ final PersistableBundle args = t.getArgs();
+ assertEquals(1, args.size());
+ assertEquals("10", args.getString(TtsSpan.ARG_INTEGER_PART));
+ }
+ {
+ final TtsSpan t = new TtsSpan.MoneyBuilder().build();
+ assertEquals(TtsSpan.TYPE_MONEY, t.getType());
+ final PersistableBundle args = t.getArgs();
+ assertEquals(0, args.size());
+ }
+
}
@SmallTest
public void testTelephoneBuilder() {
{
- final TtsSpan t = (new TtsSpan.TelephoneBuilder())
+ final TtsSpan t = new TtsSpan.TelephoneBuilder()
.setCountryCode("+01")
.setNumberParts("000-000-0000")
.setExtension("0000")
.build();
assertEquals(TtsSpan.TYPE_TELEPHONE, t.getType());
- PersistableBundle args = t.getArgs();
+ final PersistableBundle args = t.getArgs();
assertEquals(3, args.size());
assertEquals("+01", args.getString(TtsSpan.ARG_COUNTRY_CODE));
assertEquals("000-000-0000", args.getString(TtsSpan.ARG_NUMBER_PARTS));
assertEquals("0000", args.getString(TtsSpan.ARG_EXTENSION));
}
{
- final TtsSpan t = (new TtsSpan.TelephoneBuilder("000-000-0000")).build();
+ final TtsSpan t = new TtsSpan.TelephoneBuilder("000-000-0000").build();
assertEquals(TtsSpan.TYPE_TELEPHONE, t.getType());
- PersistableBundle args = t.getArgs();
+ final PersistableBundle args = t.getArgs();
assertEquals(1, args.size());
assertEquals("000-000-0000", args.getString(TtsSpan.ARG_NUMBER_PARTS));
}
@@ -431,17 +561,17 @@
@SmallTest
public void testElectronicBuilder() {
{
- final TtsSpan t = (new TtsSpan.ElectronicBuilder())
+ final TtsSpan t = new TtsSpan.ElectronicBuilder()
.setEmailArguments("example", "example.com")
.build();
assertEquals(TtsSpan.TYPE_ELECTRONIC, t.getType());
- PersistableBundle args = t.getArgs();
+ final PersistableBundle args = t.getArgs();
assertEquals(2, args.size());
assertEquals("example", args.getString(TtsSpan.ARG_USERNAME));
assertEquals("example.com", args.getString(TtsSpan.ARG_DOMAIN));
}
{
- final TtsSpan t = (new TtsSpan.ElectronicBuilder())
+ final TtsSpan t = new TtsSpan.ElectronicBuilder()
.setProtocol("http")
.setDomain("example.com")
.setPort(80)
@@ -452,7 +582,7 @@
.setPassword("password")
.build();
assertEquals(TtsSpan.TYPE_ELECTRONIC, t.getType());
- PersistableBundle args = t.getArgs();
+ final PersistableBundle args = t.getArgs();
assertEquals(8, args.size());
assertEquals("http", args.getString(TtsSpan.ARG_PROTOCOL));
assertEquals("example.com", args.getString(TtsSpan.ARG_DOMAIN));
@@ -468,18 +598,18 @@
@SmallTest
public void testDigitsBuilder() {
{
- final TtsSpan t = (new TtsSpan.DigitsBuilder())
+ final TtsSpan t = new TtsSpan.DigitsBuilder()
.setDigits("12345")
.build();
assertEquals(TtsSpan.TYPE_DIGITS, t.getType());
- PersistableBundle args = t.getArgs();
+ final PersistableBundle args = t.getArgs();
assertEquals(1, args.size());
assertEquals("12345", args.getString(TtsSpan.ARG_DIGITS));
}
{
- final TtsSpan t = (new TtsSpan.DigitsBuilder("12345")).build();
+ final TtsSpan t = new TtsSpan.DigitsBuilder("12345").build();
assertEquals(TtsSpan.TYPE_DIGITS, t.getType());
- PersistableBundle args = t.getArgs();
+ final PersistableBundle args = t.getArgs();
assertEquals(1, args.size());
assertEquals("12345", args.getString(TtsSpan.ARG_DIGITS));
}
@@ -488,18 +618,18 @@
@SmallTest
public void testVerbatimBuilder() {
{
- final TtsSpan t = (new TtsSpan.VerbatimBuilder())
+ final TtsSpan t = new TtsSpan.VerbatimBuilder()
.setVerbatim("abcdefg")
.build();
assertEquals(TtsSpan.TYPE_VERBATIM, t.getType());
- PersistableBundle args = t.getArgs();
+ final PersistableBundle args = t.getArgs();
assertEquals(1, args.size());
assertEquals("abcdefg", args.getString(TtsSpan.ARG_VERBATIM));
}
{
- final TtsSpan t = (new TtsSpan.VerbatimBuilder("abcdefg")).build();
+ final TtsSpan t = new TtsSpan.VerbatimBuilder("abcdefg").build();
assertEquals(TtsSpan.TYPE_VERBATIM, t.getType());
- PersistableBundle args = t.getArgs();
+ final PersistableBundle args = t.getArgs();
assertEquals(1, args.size());
assertEquals("abcdefg", args.getString(TtsSpan.ARG_VERBATIM));
}
diff --git a/tests/tests/text/src/android/text/style/cts/URLSpanTest.java b/tests/tests/text/src/android/text/style/cts/URLSpanTest.java
index 330db77..8fac2dd 100644
--- a/tests/tests/text/src/android/text/style/cts/URLSpanTest.java
+++ b/tests/tests/text/src/android/text/style/cts/URLSpanTest.java
@@ -16,20 +16,18 @@
package android.text.style.cts;
-import android.text.cts.R;
-
-
import android.app.Activity;
import android.app.Instrumentation;
import android.app.Instrumentation.ActivityMonitor;
import android.os.Parcel;
import android.test.ActivityInstrumentationTestCase2;
+import android.text.cts.R;
import android.text.style.URLSpan;
import android.widget.TextView;
public class URLSpanTest extends ActivityInstrumentationTestCase2<URLSpanCtsActivity> {
- // The scheme of TEST_URL must be "ctstest" to launch MockURLSpanTestActivity
- private static final String TEST_URL = "ctstest://urlSpan/test";
+ // The scheme of TEST_URL must be "ctstesttext" to launch MockURLSpanTestActivity
+ private static final String TEST_URL = "ctstesttext://urlSpan/test";
private Activity mActivity;
public URLSpanTest() {
@@ -69,11 +67,7 @@
null, false);
try {
- runTestOnUiThread(new Runnable() {
- public void run() {
- urlSpan.onClick(textView);
- }
- });
+ runTestOnUiThread(() -> urlSpan.onClick(textView));
} catch (Throwable e) {
fail("Exception error!");
}
diff --git a/tests/tests/text/src/android/text/util/cts/LinkifyTest.java b/tests/tests/text/src/android/text/util/cts/LinkifyTest.java
index a159da9..431c754 100644
--- a/tests/tests/text/src/android/text/util/cts/LinkifyTest.java
+++ b/tests/tests/text/src/android/text/util/cts/LinkifyTest.java
@@ -592,13 +592,6 @@
}
@SmallTest
- public void testAddLinks_partiallyMatchesUrlWithInvalidRequestParameter() throws Exception {
- String url = "http://android.com?p=value";
- assertAddLinksWithWebUrlPartiallyMatches("Should partially match URL with invalid " +
- "request parameter", "http://android.com", url);
- }
-
- @SmallTest
public void testAddLinks_matchesValidUrlWithEmoji() throws Exception {
String url = "Thank\u263A.com";
assertAddLinksWithWebUrlSucceeds("Should match URL with emoji", url);
@@ -669,6 +662,48 @@
}
}
+ @SmallTest
+ public void testAddLinks_matchesDomainNameWithDash() throws Exception {
+ String url = "http://a-nd.r-oid.com";
+ assertAddLinksWithWebUrlSucceeds("Should match domain name with '-'", url);
+
+ url = "a-nd.r-oid.com";
+ assertAddLinksWithWebUrlSucceeds("Should match domain name with '-'", url);
+ }
+
+ @SmallTest
+ public void testAddLinks_matchesDomainNameWithUnderscore() throws Exception {
+ String url = "http://a_nd.r_oid.com";
+ assertAddLinksWithWebUrlSucceeds("Should match domain name with '_'", url);
+
+ url = "a_nd.r_oid.com";
+ assertAddLinksWithWebUrlSucceeds("Should match domain name with '_'", url);
+ }
+
+ @SmallTest
+ public void testAddLinks_matchesPathAndQueryWithDollarSign() throws Exception {
+ String url = "http://android.com/path$?v=$val";
+ assertAddLinksWithWebUrlSucceeds("Should match path and query with '$'", url);
+
+ url = "android.com/path$?v=$val";
+ assertAddLinksWithWebUrlSucceeds("Should match path and query with '$'", url);
+ }
+
+ @SmallTest
+ public void testAddLinks_matchesEmptyPathWithQueryParams() throws Exception {
+ String url = "http://android.com?q=v";
+ assertAddLinksWithWebUrlSucceeds("Should match empty path with query params", url);
+
+ url = "android.com?q=v";
+ assertAddLinksWithWebUrlSucceeds("Should match empty path with query params", url);
+
+ url = "http://android.com/?q=v";
+ assertAddLinksWithWebUrlSucceeds("Should match empty path with query params", url);
+
+ url = "android.com/?q=v";
+ assertAddLinksWithWebUrlSucceeds("Should match empty path with query params", url);
+ }
+
// EMAIL_ADDRESSES Related Tests
public void testAddLinks_email_matchesShortValidEmail() throws Exception {
diff --git a/tests/tests/text/src/android/text/util/cts/Rfc822TokenizerTest.java b/tests/tests/text/src/android/text/util/cts/Rfc822TokenizerTest.java
index 4d145f1..b3aa714 100644
--- a/tests/tests/text/src/android/text/util/cts/Rfc822TokenizerTest.java
+++ b/tests/tests/text/src/android/text/util/cts/Rfc822TokenizerTest.java
@@ -18,17 +18,24 @@
import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
import android.text.util.Rfc822Token;
import android.text.util.Rfc822Tokenizer;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* Test {@link Rfc822Tokenizer}.
*/
public class Rfc822TokenizerTest extends AndroidTestCase {
+
+ @SmallTest
public void testConstructor() {
new Rfc822Tokenizer();
}
+ @SmallTest
public void testFindTokenStart() {
Rfc822Tokenizer rfc822Tokenizer = new Rfc822Tokenizer();
@@ -61,6 +68,7 @@
}
}
+ @SmallTest
public void testFindTokenEnd() {
Rfc822Tokenizer rfc822Tokenizer = new Rfc822Tokenizer();
@@ -96,6 +104,7 @@
}
}
+ @SmallTest
public void testTerminateToken() {
Rfc822Tokenizer rfc822Tokenizer = new Rfc822Tokenizer();
@@ -110,21 +119,22 @@
assertEquals(text + comma + space, rfc822Tokenizer.terminateToken(null));
}
+ @SmallTest
public void testTokenize() {
Rfc822Token[] tokens = Rfc822Tokenizer.tokenize("");
assertEquals(0, tokens.length);
- String text = "\"Berg\" (home) <berg\\@google.com>, tom\\@google.com (work)";
+ String text = "\"Berg\" (home) <berg\\@example.com>, tom\\@example.com (work)";
tokens = Rfc822Tokenizer.tokenize(text);
assertEquals(2, tokens.length);
- localAssertEquals(tokens[0], "Berg", "berg\\@google.com", "home");
- localAssertEquals(tokens[1], null, "tom\\@google.com", "work");
+ localAssertEquals(tokens[0], "Berg", "berg\\@example.com", "home");
+ localAssertEquals(tokens[1], null, "tom\\@example.com", "work");
- text = "Foo Bar (something) <foo\\@google.com>, blah\\@google.com (something)";
+ text = "Foo Bar (something) <foo\\@example.com>, blah\\@example.com (something)";
tokens = Rfc822Tokenizer.tokenize(text);
assertEquals(2, tokens.length);
- localAssertEquals(tokens[0], "Foo Bar", "foo\\@google.com", "something");
- localAssertEquals(tokens[1], null, "blah\\@google.com", "something");
+ localAssertEquals(tokens[0], "Foo Bar", "foo\\@example.com", "something");
+ localAssertEquals(tokens[1], null, "blah\\@example.com", "something");
try {
Rfc822Tokenizer.tokenize(null);
@@ -134,6 +144,32 @@
}
}
+ @SmallTest
+ public void testTokenize_withListParam() {
+ final List<Rfc822Token> list = new ArrayList<>();
+ Rfc822Tokenizer.tokenize("", list);
+ assertEquals(0, list.size());
+
+ String text = "\"Berg\" (home) <berg\\@example.com>, tom\\@example.com (work)";
+ Rfc822Tokenizer.tokenize(text, list);
+ assertEquals(2, list.size());
+ localAssertEquals(list.get(0), "Berg", "berg\\@example.com", "home");
+ localAssertEquals(list.get(1), null, "tom\\@example.com", "work");
+
+ text = "Foo Bar (something) <foo\\@example.com>, blah\\@example.com (something)";
+ list.clear();
+ Rfc822Tokenizer.tokenize(text, list);
+ assertEquals(2, list.size());
+ localAssertEquals(list.get(0), "Foo Bar", "foo\\@example.com", "something");
+ localAssertEquals(list.get(1), null, "blah\\@example.com", "something");
+
+ try {
+ Rfc822Tokenizer.tokenize(null);
+ fail("Should throw NullPointerException");
+ } catch (NullPointerException e) {
+ }
+ }
+
/**
* Assert the specified token's name, address and comment all equal specified ones.
* @param token the Rfc822Token to be asserted.
diff --git a/tests/tests/transition/Android.mk b/tests/tests/transition/Android.mk
index af8b4eb..5370d4f 100644
--- a/tests/tests/transition/Android.mk
+++ b/tests/tests/transition/Android.mk
@@ -24,13 +24,19 @@
# When built, explicitly put it in the data partition.
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
+LOCAL_STATIC_JAVA_LIBRARIES += \
+ android-support-test \
+ mockito-target \
+ android-common \
+ ctsdeviceutil \
+ ctstestrunner \
+ platform-test-annotations
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
LOCAL_SRC_FILES := $(call all-java-files-under, src)
# Tag this module as a cts test artifact
LOCAL_COMPATIBILITY_SUITE := cts
-LOCAL_SDK_VERSION := current
-
include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/transition/res/layout/scene11.xml b/tests/tests/transition/res/layout/scene11.xml
new file mode 100644
index 0000000..8ecad8d
--- /dev/null
+++ b/tests/tests/transition/res/layout/scene11.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:transitionName="holder"
+ android:id="@+id/holder">
+ <ImageView
+ android:id="@+id/redSquare"
+ android:src="#F00"
+ android:layout_width="10dp"
+ android:layout_height="10dp"/>
+</RelativeLayout>
diff --git a/tests/tests/transition/res/transition/arc_motion.xml b/tests/tests/transition/res/transition/arc_motion.xml
new file mode 100644
index 0000000..264f074
--- /dev/null
+++ b/tests/tests/transition/res/transition/arc_motion.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<changeBounds xmlns:android="http://schemas.android.com/apk/res/android">
+ <arcMotion android:minimumVerticalAngle="1"
+ android:minimumHorizontalAngle="2"
+ android:maximumAngle="53"/>
+</changeBounds>
\ No newline at end of file
diff --git a/tests/tests/transition/res/transition/auto_transition.xml b/tests/tests/transition/res/transition/auto_transition.xml
new file mode 100644
index 0000000..e285105
--- /dev/null
+++ b/tests/tests/transition/res/transition/auto_transition.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<autoTransition/>
\ No newline at end of file
diff --git a/tests/tests/transition/res/transition/change_bounds.xml b/tests/tests/transition/res/transition/change_bounds.xml
new file mode 100644
index 0000000..162372c
--- /dev/null
+++ b/tests/tests/transition/res/transition/change_bounds.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<changeBounds xmlns:android="http://schemas.android.com/apk/res/android"
+ android:resizeClip="true"/>
diff --git a/tests/tests/transition/res/transition/change_clip_bounds.xml b/tests/tests/transition/res/transition/change_clip_bounds.xml
new file mode 100644
index 0000000..70a7dc6
--- /dev/null
+++ b/tests/tests/transition/res/transition/change_clip_bounds.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<changeClipBounds/>
diff --git a/tests/tests/transition/res/transition/change_image_transform.xml b/tests/tests/transition/res/transition/change_image_transform.xml
new file mode 100644
index 0000000..b37ab04
--- /dev/null
+++ b/tests/tests/transition/res/transition/change_image_transform.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<changeImageTransform/>
diff --git a/tests/tests/transition/res/transition/change_scroll.xml b/tests/tests/transition/res/transition/change_scroll.xml
new file mode 100644
index 0000000..9054686
--- /dev/null
+++ b/tests/tests/transition/res/transition/change_scroll.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<changeScroll/>
\ No newline at end of file
diff --git a/tests/tests/transition/res/transition/change_transform.xml b/tests/tests/transition/res/transition/change_transform.xml
new file mode 100644
index 0000000..c87c549
--- /dev/null
+++ b/tests/tests/transition/res/transition/change_transform.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<changeTransform xmlns:android="http://schemas.android.com/apk/res/android"
+ android:reparent="false" android:reparentWithOverlay="false"/>
diff --git a/tests/tests/transition/res/transition/custom_path_motion.xml b/tests/tests/transition/res/transition/custom_path_motion.xml
new file mode 100644
index 0000000..1704419
--- /dev/null
+++ b/tests/tests/transition/res/transition/custom_path_motion.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 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.
+-->
+<changeBounds xmlns:android="http://schemas.android.com/apk/res/android">
+ <pathMotion class="android.transition.cts.TransitionInflaterTest$CustomPathMotion"/>
+</changeBounds>
\ No newline at end of file
diff --git a/tests/tests/transition/res/transition/custom_transition.xml b/tests/tests/transition/res/transition/custom_transition.xml
new file mode 100644
index 0000000..35ee285
--- /dev/null
+++ b/tests/tests/transition/res/transition/custom_transition.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<transition class="android.transition.cts.TransitionInflaterTest$CustomTransition"/>
\ No newline at end of file
diff --git a/tests/tests/transition/res/transition/explode.xml b/tests/tests/transition/res/transition/explode.xml
new file mode 100644
index 0000000..a038d8b
--- /dev/null
+++ b/tests/tests/transition/res/transition/explode.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<explode xmlns:android="http://schemas.android.com/apk/res/android"
+ android:transitionVisibilityMode="mode_in"/>
diff --git a/tests/tests/transition/res/transition/fade.xml b/tests/tests/transition/res/transition/fade.xml
new file mode 100644
index 0000000..2057014
--- /dev/null
+++ b/tests/tests/transition/res/transition/fade.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<fade xmlns:android="http://schemas.android.com/apk/res/android"
+ android:fadingMode="fade_out"/>
diff --git a/tests/tests/transition/res/transition/pattern_path_motion.xml b/tests/tests/transition/res/transition/pattern_path_motion.xml
new file mode 100644
index 0000000..f794447
--- /dev/null
+++ b/tests/tests/transition/res/transition/pattern_path_motion.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<changeBounds xmlns:android="http://schemas.android.com/apk/res/android">
+ <patternPathMotion android:patternPathData="M0 0 L0 100 L100 100"/>
+</changeBounds>
\ No newline at end of file
diff --git a/tests/tests/transition/res/transition/slide.xml b/tests/tests/transition/res/transition/slide.xml
new file mode 100644
index 0000000..9fde010
--- /dev/null
+++ b/tests/tests/transition/res/transition/slide.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<slide xmlns:android="http://schemas.android.com/apk/res/android"
+ android:slideEdge="top"/>
diff --git a/tests/tests/transition/res/transition/target_classes.xml b/tests/tests/transition/res/transition/target_classes.xml
new file mode 100644
index 0000000..01d1e2d
--- /dev/null
+++ b/tests/tests/transition/res/transition/target_classes.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<changeBounds xmlns:android="http://schemas.android.com/apk/res/android">
+ <targets>
+ <target android:targetClass="android.widget.TextView"/>
+ <target android:targetClass="android.widget.ImageView"/>
+ </targets>
+</changeBounds>
\ No newline at end of file
diff --git a/tests/tests/transition/res/transition/target_ids.xml b/tests/tests/transition/res/transition/target_ids.xml
new file mode 100644
index 0000000..2340e46
--- /dev/null
+++ b/tests/tests/transition/res/transition/target_ids.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<changeBounds xmlns:android="http://schemas.android.com/apk/res/android">
+ <targets>
+ <target android:targetId="@+id/hello"/>
+ <target android:targetId="@+id/world"/>
+ </targets>
+</changeBounds>
\ No newline at end of file
diff --git a/tests/tests/transition/res/transition/target_names.xml b/tests/tests/transition/res/transition/target_names.xml
new file mode 100644
index 0000000..b90cf19
--- /dev/null
+++ b/tests/tests/transition/res/transition/target_names.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<changeBounds xmlns:android="http://schemas.android.com/apk/res/android">
+ <targets>
+ <target android:targetName="hello"/>
+ <target android:targetName="world"/>
+ </targets>
+</changeBounds>
\ No newline at end of file
diff --git a/tests/tests/transition/res/transition/transition_constructors.xml b/tests/tests/transition/res/transition/transition_constructors.xml
new file mode 100644
index 0000000..e442187
--- /dev/null
+++ b/tests/tests/transition/res/transition/transition_constructors.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
+ <transition class="android.transition.cts.TransitionInflaterTest$InflationFade"/>
+ <transition class="android.transition.cts.TransitionInflaterTest$InflationChangeBounds"/>
+ <transition class="android.transition.cts.TransitionInflaterTest$InflationChangeImageTransform"/>
+ <transition class="android.transition.cts.TransitionInflaterTest$InflationChangeTransform"/>
+ <transition class="android.transition.cts.TransitionInflaterTest$InflationAutoTransition"/>
+ <transition class="android.transition.cts.TransitionInflaterTest$InflationChangeClipBounds"/>
+ <transition class="android.transition.cts.TransitionInflaterTest$InflationChangeScroll"/>
+ <transition class="android.transition.cts.TransitionInflaterTest$InflationExplode"/>
+ <transition class="android.transition.cts.TransitionInflaterTest$InflationSlide"/>
+ <transition class="android.transition.cts.TransitionInflaterTest$InflationTransitionSet"/>
+</transitionSet>
\ No newline at end of file
diff --git a/tests/tests/transition/res/transition/transition_manager.xml b/tests/tests/transition/res/transition/transition_manager.xml
new file mode 100644
index 0000000..8d3186c
--- /dev/null
+++ b/tests/tests/transition/res/transition/transition_manager.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<transitionManager xmlns:android="http://schemas.android.com/apk/res/android">
+ <transition android:toScene="@layout/scene1"
+ android:transition="@transition/fade"/>
+ <transition android:fromScene="@layout/scene1" android:toScene="@layout/scene2"
+ android:transition="@transition/change_bounds"/>
+</transitionManager>
\ No newline at end of file
diff --git a/tests/tests/transition/res/transition/transition_set.xml b/tests/tests/transition/res/transition/transition_set.xml
new file mode 100644
index 0000000..3b1ff3c
--- /dev/null
+++ b/tests/tests/transition/res/transition/transition_set.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<transitionSet xmlns:android="http://schemas.android.com/apk/res/android"
+ android:transitionOrdering="sequential">
+ <changeBounds/>
+ <fade/>
+</transitionSet>
diff --git a/tests/tests/transition/src/android/transition/cts/BaseTransitionTest.java b/tests/tests/transition/src/android/transition/cts/BaseTransitionTest.java
index 944ff91..a5da03e 100644
--- a/tests/tests/transition/src/android/transition/cts/BaseTransitionTest.java
+++ b/tests/tests/transition/src/android/transition/cts/BaseTransitionTest.java
@@ -89,7 +89,7 @@
}
protected Scene loadScene(final View layout) throws Throwable {
- Scene[] scene = new Scene[1];
+ final Scene[] scene = new Scene[1];
runTestOnUiThread(new Runnable() {
@Override
public void run() {
@@ -101,7 +101,7 @@
}
protected Scene loadScene(final int layoutId) throws Throwable {
- Scene scene[] = new Scene[1];
+ final Scene scene[] = new Scene[1];
runTestOnUiThread(new Runnable() {
@Override
public void run() {
diff --git a/tests/tests/transition/src/android/transition/cts/CaptureValuesTest.java b/tests/tests/transition/src/android/transition/cts/CaptureValuesTest.java
new file mode 100644
index 0000000..84b154a
--- /dev/null
+++ b/tests/tests/transition/src/android/transition/cts/CaptureValuesTest.java
@@ -0,0 +1,246 @@
+/*
+ * 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.transition.cts;
+
+import android.support.test.filters.MediumTest;
+import android.support.test.filters.SmallTest;
+import android.transition.AutoTransition;
+import android.transition.ChangeBounds;
+import android.transition.ChangeClipBounds;
+import android.transition.ChangeImageTransform;
+import android.transition.ChangeScroll;
+import android.transition.ChangeTransform;
+import android.transition.Explode;
+import android.transition.Fade;
+import android.transition.Slide;
+import android.transition.Transition;
+import android.transition.TransitionManager;
+import android.transition.TransitionSet;
+import android.transition.TransitionValues;
+import android.util.ArrayMap;
+
+@MediumTest
+public class CaptureValuesTest extends BaseTransitionTest {
+ private ArrayMap<Class<?>, Boolean> mStartCaptured = new ArrayMap<>();
+ private ArrayMap<Class<?>, Boolean> mEndCaptured = new ArrayMap<>();
+
+ /**
+ * Ensures captureValues is called on all Transitions and the proper values are captured.
+ */
+ public void testCaptureValues() throws Throwable {
+ final TransitionSetCaptureValues set = new TransitionSetCaptureValues();
+ set.addTransition(new FadeCaptureValues());
+ set.addTransition(new ChangeBoundsCaptureValues());
+ set.addTransition(new ChangeImageTransformCaptureValues());
+ set.addTransition(new ChangeTransformCaptureValues());
+ set.addTransition(new AutoTransitionCaptureValues());
+ set.addTransition(new ChangeClipBoundsCaptureValues());
+ set.addTransition(new ChangeScrollCaptureValues());
+ set.addTransition(new ExplodeCaptureValues());
+ set.addTransition(new SlideCaptureValues());
+
+ enterScene(R.layout.scene11);
+ set.addTarget(getActivity().findViewById(R.id.redSquare));
+ mTransition = set;
+ resetListener();
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ TransitionManager.beginDelayedTransition(mSceneRoot, set);
+ mSceneRoot.invalidate();
+ }
+ });
+ waitForStart();
+ // no transition needs to run, but they should have all captured values.
+
+ for (int i = 0; i < set.getTransitionCount(); i++) {
+ Transition transition = set.getTransitionAt(i);
+ String className = transition.getClass().getSuperclass().getSimpleName().toString();
+ assertNotNull("captureStartValues not called for " + className,
+ mStartCaptured.get(transition.getClass()));
+ assertNotNull("captureEndValues not called for " + className,
+ mEndCaptured.get(transition.getClass()));
+ }
+ assertNotNull(mStartCaptured.get(set.getClass()));
+ assertNotNull(mEndCaptured.get(set.getClass()));
+ }
+
+ private void testCapturedValues(Transition transition, TransitionValues values,
+ boolean isStart) {
+ String[] properties = transition.getTransitionProperties();
+ if (transition instanceof TransitionSet) {
+ assertNull(properties);
+ } else {
+ String className = transition.getClass().getSuperclass().getSimpleName().toString();
+ assertNotNull(className + " should have non-null transition properties", properties);
+ assertTrue(properties.length > 0);
+
+ for (String property : properties) {
+ assertTrue(className + " should have written to property " + property,
+ values.values.keySet().contains(property));
+ }
+ }
+ if (isStart) {
+ mStartCaptured.put(transition.getClass(), true);
+ } else {
+ mEndCaptured.put(transition.getClass(), true);
+ }
+ }
+
+ public class FadeCaptureValues extends Fade {
+ @Override
+ public void captureStartValues(TransitionValues transitionValues) {
+ super.captureStartValues(transitionValues);
+ testCapturedValues(this, transitionValues, true);
+ }
+
+ @Override
+ public void captureEndValues(TransitionValues transitionValues) {
+ super.captureEndValues(transitionValues);
+ testCapturedValues(this, transitionValues, false);
+ }
+ }
+
+ public class ChangeBoundsCaptureValues extends ChangeBounds {
+ public ChangeBoundsCaptureValues() {
+ super();
+ setResizeClip(true);
+ setReparent(true);
+ }
+ @Override
+ public void captureStartValues(TransitionValues transitionValues) {
+ super.captureStartValues(transitionValues);
+ testCapturedValues(this, transitionValues, true);
+ }
+
+ @Override
+ public void captureEndValues(TransitionValues transitionValues) {
+ super.captureEndValues(transitionValues);
+ testCapturedValues(this, transitionValues, false);
+ }
+ }
+
+ public class ChangeImageTransformCaptureValues extends ChangeImageTransform {
+ @Override
+ public void captureStartValues(TransitionValues transitionValues) {
+ super.captureStartValues(transitionValues);
+ testCapturedValues(this, transitionValues, true);
+ }
+
+ @Override
+ public void captureEndValues(TransitionValues transitionValues) {
+ super.captureEndValues(transitionValues);
+ testCapturedValues(this, transitionValues, false);
+ }
+ }
+
+ public class ChangeTransformCaptureValues extends ChangeTransform {
+ @Override
+ public void captureStartValues(TransitionValues transitionValues) {
+ super.captureStartValues(transitionValues);
+ testCapturedValues(this, transitionValues, true);
+ }
+
+ @Override
+ public void captureEndValues(TransitionValues transitionValues) {
+ super.captureEndValues(transitionValues);
+ testCapturedValues(this, transitionValues, false);
+ }
+ }
+
+ public class AutoTransitionCaptureValues extends AutoTransition {
+ @Override
+ public void captureStartValues(TransitionValues transitionValues) {
+ super.captureStartValues(transitionValues);
+ testCapturedValues(this, transitionValues, true);
+ }
+
+ @Override
+ public void captureEndValues(TransitionValues transitionValues) {
+ super.captureEndValues(transitionValues);
+ testCapturedValues(this, transitionValues, false);
+ }
+ }
+
+ public class ChangeClipBoundsCaptureValues extends ChangeClipBounds {
+ @Override
+ public void captureStartValues(TransitionValues transitionValues) {
+ super.captureStartValues(transitionValues);
+ testCapturedValues(this, transitionValues, true);
+ }
+
+ @Override
+ public void captureEndValues(TransitionValues transitionValues) {
+ super.captureEndValues(transitionValues);
+ testCapturedValues(this, transitionValues, false);
+ }
+ }
+
+ public class ChangeScrollCaptureValues extends ChangeScroll {
+ @Override
+ public void captureStartValues(TransitionValues transitionValues) {
+ super.captureStartValues(transitionValues);
+ testCapturedValues(this, transitionValues, true);
+ }
+
+ @Override
+ public void captureEndValues(TransitionValues transitionValues) {
+ super.captureEndValues(transitionValues);
+ testCapturedValues(this, transitionValues, false);
+ }
+ }
+
+ public class ExplodeCaptureValues extends Explode {
+ @Override
+ public void captureStartValues(TransitionValues transitionValues) {
+ super.captureStartValues(transitionValues);
+ testCapturedValues(this, transitionValues, true);
+ }
+
+ @Override
+ public void captureEndValues(TransitionValues transitionValues) {
+ super.captureEndValues(transitionValues);
+ testCapturedValues(this, transitionValues, false);
+ }
+ }
+
+ public class SlideCaptureValues extends Slide {
+ @Override
+ public void captureStartValues(TransitionValues transitionValues) {
+ super.captureStartValues(transitionValues);
+ testCapturedValues(this, transitionValues, true);
+ }
+
+ @Override
+ public void captureEndValues(TransitionValues transitionValues) {
+ super.captureEndValues(transitionValues);
+ testCapturedValues(this, transitionValues, false);
+ }
+ }
+ public class TransitionSetCaptureValues extends TransitionSet {
+ @Override
+ public void captureStartValues(TransitionValues transitionValues) {
+ super.captureStartValues(transitionValues);
+ testCapturedValues(this, transitionValues, true);
+ }
+
+ @Override
+ public void captureEndValues(TransitionValues transitionValues) {
+ super.captureEndValues(transitionValues);
+ testCapturedValues(this, transitionValues, false);
+ }
+ }
+}
diff --git a/tests/tests/transition/src/android/transition/cts/PatternPathMotionTest.java b/tests/tests/transition/src/android/transition/cts/PatternPathMotionTest.java
index 5485b4a..6655213 100644
--- a/tests/tests/transition/src/android/transition/cts/PatternPathMotionTest.java
+++ b/tests/tests/transition/src/android/transition/cts/PatternPathMotionTest.java
@@ -47,5 +47,14 @@
assertPathMatches(expected, pathMotion.getPath(0, 0, 0, 100));
}
+
+ public void testSetPatternPath() throws Throwable {
+ Path pattern = new Path();
+ pattern.addArc(0, 0, 100, 100, 0, 180);
+
+ PatternPathMotion patternPathMotion = new PatternPathMotion();
+ patternPathMotion.setPatternPath(pattern);
+ assertSame(pattern, patternPathMotion.getPatternPath());
+ }
}
diff --git a/tests/tests/transition/src/android/transition/cts/PropagationTest.java b/tests/tests/transition/src/android/transition/cts/PropagationTest.java
new file mode 100644
index 0000000..7b49c15
--- /dev/null
+++ b/tests/tests/transition/src/android/transition/cts/PropagationTest.java
@@ -0,0 +1,217 @@
+/*
+ * 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.transition.cts;
+
+import android.graphics.Rect;
+import android.transition.CircularPropagation;
+import android.transition.SidePropagation;
+import android.transition.Transition;
+import android.transition.TransitionValues;
+import android.view.Gravity;
+import android.view.View;
+
+public class PropagationTest extends BaseTransitionTest {
+ public void testCircularPropagation() throws Throwable {
+ enterScene(R.layout.scene10);
+ CircularPropagation propagation = new CircularPropagation();
+ mTransition.setPropagation(propagation);
+ final TransitionValues redValues = new TransitionValues();
+ redValues.view = getActivity().findViewById(R.id.redSquare);
+ propagation.captureValues(redValues);
+
+ // Only the reported propagation properties are set
+ for (String prop : propagation.getPropagationProperties()) {
+ assertTrue(redValues.values.keySet().contains(prop));
+ }
+ assertEquals(propagation.getPropagationProperties().length, redValues.values.size());
+
+ // check the visibility
+ assertEquals(View.VISIBLE, propagation.getViewVisibility(redValues));
+ assertEquals(View.GONE, propagation.getViewVisibility(null));
+
+ // Check the positions
+ int[] pos = new int[2];
+ redValues.view.getLocationOnScreen(pos);
+ pos[0] += redValues.view.getWidth() / 2;
+ pos[1] += redValues.view.getHeight() / 2;
+ assertEquals(pos[0], propagation.getViewX(redValues));
+ assertEquals(pos[1], propagation.getViewY(redValues));
+
+ mTransition.setEpicenterCallback(new Transition.EpicenterCallback() {
+ @Override
+ public Rect onGetEpicenter(Transition transition) {
+ return new Rect(0, 0, redValues.view.getWidth(), redValues.view.getHeight());
+ }
+ });
+
+ long redDelay = getDelay(R.id.redSquare);
+ // red square's delay should be roughly 0 since it is at the epicenter
+ assertEquals(0f, redDelay, 30f);
+
+ // The green square is on the upper-right
+ long greenDelay = getDelay(R.id.greenSquare);
+ assertTrue(greenDelay < redDelay);
+
+ // The blue square is on the lower-right
+ long blueDelay = getDelay(R.id.blueSquare);
+ assertTrue(blueDelay < greenDelay);
+
+ // Test propagation speed
+ propagation.setPropagationSpeed(1000000000f);
+ assertEquals(0, getDelay(R.id.blueSquare));
+ }
+
+ public void testSidePropagationBottom() throws Throwable {
+ SidePropagation propagation = new SidePropagation();
+ propagation.setSide(Gravity.BOTTOM);
+ mTransition.setEpicenterCallback(new Transition.EpicenterCallback() {
+ @Override
+ public Rect onGetEpicenter(Transition transition) {
+ return new Rect(0, 0, 1, 1);
+ }
+ });
+ mTransition.setPropagation(propagation);
+
+ enterScene(R.layout.scene10);
+
+ // The red square is on the upper-left
+ long redDelay = getDelay(R.id.redSquare);
+
+ // The green square is on the upper-right
+ long greenDelay = getDelay(R.id.greenSquare);
+
+ // The blue square is on the lower-right
+ long blueDelay = getDelay(R.id.blueSquare);
+
+ // The yellow square is on the lower-left
+ long yellowDelay = getDelay(R.id.yellowSquare);
+
+ assertTrue(redDelay > greenDelay);
+ assertTrue(redDelay > yellowDelay);
+ assertTrue(greenDelay > blueDelay);
+ assertTrue(yellowDelay > blueDelay);
+
+ // Test propagation speed
+ propagation.setPropagationSpeed(1000000000f);
+ assertEquals(0, getDelay(R.id.blueSquare));
+ }
+
+ public void testSidePropagationTop() throws Throwable {
+ SidePropagation propagation = new SidePropagation();
+ propagation.setSide(Gravity.TOP);
+ mTransition.setEpicenterCallback(new Transition.EpicenterCallback() {
+ @Override
+ public Rect onGetEpicenter(Transition transition) {
+ return new Rect(0, 0, 1, 1);
+ }
+ });
+ mTransition.setPropagation(propagation);
+
+ enterScene(R.layout.scene10);
+
+ // The red square is on the upper-left
+ long redDelay = getDelay(R.id.redSquare);
+
+ // The green square is on the upper-right
+ long greenDelay = getDelay(R.id.greenSquare);
+
+ // The blue square is on the lower-right
+ long blueDelay = getDelay(R.id.blueSquare);
+
+ // The yellow square is on the lower-left
+ long yellowDelay = getDelay(R.id.yellowSquare);
+
+ assertTrue(yellowDelay > redDelay);
+ assertTrue(yellowDelay > blueDelay);
+ assertTrue(redDelay > greenDelay);
+ assertTrue(blueDelay > greenDelay);
+ }
+
+ public void testSidePropagationRight() throws Throwable {
+ SidePropagation propagation = new SidePropagation();
+ propagation.setSide(Gravity.RIGHT);
+ mTransition.setEpicenterCallback(new Transition.EpicenterCallback() {
+ @Override
+ public Rect onGetEpicenter(Transition transition) {
+ return new Rect(0, 0, 1, 1);
+ }
+ });
+ mTransition.setPropagation(propagation);
+
+ enterScene(R.layout.scene10);
+
+ // The red square is on the upper-left
+ long redDelay = getDelay(R.id.redSquare);
+
+ // The green square is on the upper-right
+ long greenDelay = getDelay(R.id.greenSquare);
+
+ // The blue square is on the lower-right
+ long blueDelay = getDelay(R.id.blueSquare);
+
+ // The yellow square is on the lower-left
+ long yellowDelay = getDelay(R.id.yellowSquare);
+
+ assertTrue(redDelay > greenDelay);
+ assertTrue(redDelay > yellowDelay);
+ assertTrue(yellowDelay > blueDelay);
+ assertTrue(greenDelay > blueDelay);
+ }
+
+ public void testSidePropagationLeft() throws Throwable {
+ SidePropagation propagation = new SidePropagation();
+ propagation.setSide(Gravity.LEFT);
+ mTransition.setEpicenterCallback(new Transition.EpicenterCallback() {
+ @Override
+ public Rect onGetEpicenter(Transition transition) {
+ return new Rect(0, 0, 1, 1);
+ }
+ });
+ mTransition.setPropagation(propagation);
+
+ enterScene(R.layout.scene10);
+
+ // The red square is on the upper-left
+ long redDelay = getDelay(R.id.redSquare);
+
+ // The green square is on the upper-right
+ long greenDelay = getDelay(R.id.greenSquare);
+
+ // The blue square is on the lower-right
+ long blueDelay = getDelay(R.id.blueSquare);
+
+ // The yellow square is on the lower-left
+ long yellowDelay = getDelay(R.id.yellowSquare);
+
+ assertTrue(greenDelay > redDelay);
+ assertTrue(greenDelay > blueDelay);
+ assertTrue(redDelay > yellowDelay);
+ assertTrue(blueDelay > yellowDelay);
+ }
+
+ private TransitionValues capturePropagationValues(int viewId) {
+ TransitionValues transitionValues = new TransitionValues();
+ transitionValues.view = mSceneRoot.findViewById(viewId);
+ mTransition.getPropagation().captureValues(transitionValues);
+ return transitionValues;
+ }
+
+ private long getDelay(int viewId) {
+ TransitionValues transitionValues = capturePropagationValues(viewId);
+ return mTransition.getPropagation().
+ getStartDelay(mSceneRoot, mTransition, transitionValues, null);
+ }
+}
diff --git a/tests/tests/transition/src/android/transition/cts/SceneTest.java b/tests/tests/transition/src/android/transition/cts/SceneTest.java
index 2785714..48fb6c1 100644
--- a/tests/tests/transition/src/android/transition/cts/SceneTest.java
+++ b/tests/tests/transition/src/android/transition/cts/SceneTest.java
@@ -17,12 +17,12 @@
import android.transition.Scene;
import android.view.View;
+import android.view.ViewGroup;
public class SceneTest extends BaseTransitionTest {
-
- public SceneTest() {
- }
-
+ /**
+ * Test Scene(ViewGroup) with enterAction and exitAction
+ */
public void testDynamicConstructor() throws Throwable {
Scene scene = new Scene(mSceneRoot);
assertEquals(mSceneRoot, scene.getSceneRoot());
@@ -52,32 +52,34 @@
assertNull(mSceneRoot.findViewById(R.id.redSquare));
}
+ /**
+ * Test Scene(ViewGroup, View)
+ */
public void testViewConstructor() throws Throwable {
View view = loadLayout(R.layout.scene1);
- Scene scene = new Scene(mSceneRoot, view);
- assertEquals(mSceneRoot, scene.getSceneRoot());
- CallCheck enterCheck = new CallCheck();
- scene.setEnterAction(enterCheck);
- CallCheck exitCheck = new CallCheck();
- scene.setExitAction(exitCheck);
- enterScene(scene);
-
- assertTrue(enterCheck.wasRun);
- assertFalse(exitCheck.wasRun);
-
- View redSquare = mActivity.findViewById(R.id.redSquare);
- assertNotNull(redSquare);
-
- exitScene(scene);
- assertNotNull(mSceneRoot.findViewById(R.id.redSquare));
- assertTrue(exitCheck.wasRun);
-
- enterScene(R.layout.scene4);
- assertNull(mSceneRoot.findViewById(R.id.redSquare));
+ constructorTest(new Scene(mSceneRoot, view));
}
+ /**
+ * Test Scene(ViewGroup, ViewGroup)
+ */
+ public void testDeprecatedConstructor() throws Throwable {
+ View view = loadLayout(R.layout.scene1);
+ constructorTest(new Scene(mSceneRoot, (ViewGroup) view));
+ }
+
+ /**
+ * Test Scene.getSceneForLayout
+ */
public void testFactory() throws Throwable {
Scene scene = loadScene(R.layout.scene1);
+ constructorTest(scene);
+ }
+
+ /**
+ * Tests that the Scene was constructed properly from a scene1
+ */
+ private void constructorTest(Scene scene) throws Throwable {
assertEquals(mSceneRoot, scene.getSceneRoot());
CallCheck enterCheck = new CallCheck();
scene.setEnterAction(enterCheck);
@@ -94,8 +96,6 @@
exitScene(scene);
assertNotNull(mSceneRoot.findViewById(R.id.redSquare));
assertTrue(exitCheck.wasRun);
- enterScene(R.layout.scene4);
- assertNull(mSceneRoot.findViewById(R.id.redSquare));
}
private static class CallCheck implements Runnable {
diff --git a/tests/tests/transition/src/android/transition/cts/TransitionInflaterTest.java b/tests/tests/transition/src/android/transition/cts/TransitionInflaterTest.java
new file mode 100644
index 0000000..22c0616
--- /dev/null
+++ b/tests/tests/transition/src/android/transition/cts/TransitionInflaterTest.java
@@ -0,0 +1,308 @@
+/*
+ * 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.transition.cts;
+
+import android.content.Context;
+import android.graphics.Path;
+import android.graphics.PathMeasure;
+import android.support.test.filters.MediumTest;
+import android.transition.ArcMotion;
+import android.transition.AutoTransition;
+import android.transition.ChangeBounds;
+import android.transition.ChangeClipBounds;
+import android.transition.ChangeImageTransform;
+import android.transition.ChangeScroll;
+import android.transition.ChangeTransform;
+import android.transition.Explode;
+import android.transition.Fade;
+import android.transition.PathMotion;
+import android.transition.PatternPathMotion;
+import android.transition.Scene;
+import android.transition.Slide;
+import android.transition.Transition;
+import android.transition.TransitionInflater;
+import android.transition.TransitionManager;
+import android.transition.TransitionSet;
+import android.transition.TransitionValues;
+import android.transition.Visibility;
+import android.transition.cts.R;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import java.util.List;
+
+@MediumTest
+public class TransitionInflaterTest extends BaseTransitionTest {
+ public void testInflationConstructors() throws Throwable {
+ TransitionInflater inflater = TransitionInflater.from(mActivity);
+ Transition transition = inflater.inflateTransition(R.transition.transition_constructors);
+ assertTrue(transition instanceof TransitionSet);
+ TransitionSet set = (TransitionSet) transition;
+ assertEquals(10, set.getTransitionCount());
+ }
+
+ public void testInflation() {
+ TransitionInflater inflater = TransitionInflater.from(mActivity);
+ assureFadeProperties(inflater.inflateTransition(R.transition.fade));
+ assureChangeBoundsProperties(inflater.inflateTransition(R.transition.change_bounds));
+ assureSlideProperties(inflater.inflateTransition(R.transition.slide));
+ assureExplodeProperties(inflater.inflateTransition(R.transition.explode));
+ assureChangeImageTransformProperties(
+ inflater.inflateTransition(R.transition.change_image_transform));
+ assureChangeTransformProperties(inflater.inflateTransition(R.transition.change_transform));
+ assureChangeClipBoundsProperties(
+ inflater.inflateTransition(R.transition.change_clip_bounds));
+ assureAutoTransitionProperties(inflater.inflateTransition(R.transition.auto_transition));
+ assureChangeScrollProperties(inflater.inflateTransition(R.transition.change_scroll));
+ assureTransitionSetProperties(inflater.inflateTransition(R.transition.transition_set));
+ assureCustomTransitionProperties(
+ inflater.inflateTransition(R.transition.custom_transition));
+ testTargetIds(inflater.inflateTransition(R.transition.target_ids));
+ testTargetNames(inflater.inflateTransition(R.transition.target_names));
+ testTargetClass(inflater.inflateTransition(R.transition.target_classes));
+ testArcMotion(inflater.inflateTransition(R.transition.arc_motion));
+ testCustomPathMotion(inflater.inflateTransition(R.transition.custom_path_motion));
+ testPatternPathMotion(inflater.inflateTransition(R.transition.pattern_path_motion));
+ }
+
+ public void testInflateTransitionManager() throws Throwable {
+ TransitionInflater inflater = TransitionInflater.from(mActivity);
+ TransitionManager transitionManager =
+ inflater.inflateTransitionManager(R.transition.transition_manager, mSceneRoot);
+ assertNotNull(transitionManager);
+
+ Scene scene1 = Scene.getSceneForLayout(mSceneRoot, R.layout.scene1, mActivity);
+ Transition transition = transitionManager.getTransition(scene1);
+ assertNotNull(transition);
+ assertTrue(transition instanceof Fade);
+ enterScene(scene1);
+
+ Scene scene2 = Scene.getSceneForLayout(mSceneRoot, R.layout.scene2, mActivity);
+ transition = transitionManager.getTransition(scene2);
+ assertNotNull(transition);
+ assertTrue(transition instanceof ChangeBounds);
+ }
+
+ private void assureFadeProperties(Transition transition) {
+ assertTrue(transition instanceof Fade);
+ Fade fade = (Fade) transition;
+ assertEquals(Fade.OUT, fade.getMode());
+ }
+
+ private void assureChangeBoundsProperties(Transition transition) {
+ assertTrue(transition instanceof ChangeBounds);
+ ChangeBounds changeBounds = (ChangeBounds) transition;
+ assertTrue(changeBounds.getResizeClip());
+ }
+
+ private void assureSlideProperties(Transition transition) {
+ assertTrue(transition instanceof Slide);
+ Slide slide = (Slide) transition;
+ assertEquals(Gravity.TOP, slide.getSlideEdge());
+ }
+
+ private void assureExplodeProperties(Transition transition) {
+ assertTrue(transition instanceof Explode);
+ Visibility visibility = (Visibility) transition;
+ assertEquals(Visibility.MODE_IN, visibility.getMode());
+ }
+
+ private void assureChangeImageTransformProperties(Transition transition) {
+ assertTrue(transition instanceof ChangeImageTransform);
+ }
+
+ private void assureChangeTransformProperties(Transition transition) {
+ assertTrue(transition instanceof ChangeTransform);
+ ChangeTransform changeTransform = (ChangeTransform) transition;
+ assertFalse(changeTransform.getReparent());
+ assertFalse(changeTransform.getReparentWithOverlay());
+ }
+
+ private void assureChangeClipBoundsProperties(Transition transition) {
+ assertTrue(transition instanceof ChangeClipBounds);
+ }
+
+ private void assureAutoTransitionProperties(Transition transition) {
+ assertTrue(transition instanceof AutoTransition);
+ }
+
+ private void assureChangeScrollProperties(Transition transition) {
+ assertTrue(transition instanceof ChangeScroll);
+ }
+
+ private void assureTransitionSetProperties(Transition transition) {
+ assertTrue(transition instanceof TransitionSet);
+ TransitionSet set = (TransitionSet) transition;
+ assertEquals(TransitionSet.ORDERING_SEQUENTIAL, set.getOrdering());
+ assertEquals(2, set.getTransitionCount());
+ assertTrue(set.getTransitionAt(0) instanceof ChangeBounds);
+ assertTrue(set.getTransitionAt(1) instanceof Fade);
+ }
+
+ private void assureCustomTransitionProperties(Transition transition) {
+ assertTrue(transition instanceof CustomTransition);
+ }
+
+ private void testTargetIds(Transition transition) {
+ List<Integer> targets = transition.getTargetIds();
+ assertNotNull(targets);
+ assertEquals(2, targets.size());
+ assertEquals(R.id.hello, (int) targets.get(0));
+ assertEquals(R.id.world, (int) targets.get(1));
+ }
+
+ private void testTargetNames(Transition transition) {
+ List<String> targets = transition.getTargetNames();
+ assertNotNull(targets);
+ assertEquals(2, targets.size());
+ assertEquals("hello", targets.get(0));
+ assertEquals("world", targets.get(1));
+ }
+
+ private void testTargetClass(Transition transition) {
+ List<Class> targets = transition.getTargetTypes();
+ assertNotNull(targets);
+ assertEquals(2, targets.size());
+ assertEquals(TextView.class, targets.get(0));
+ assertEquals(ImageView.class, targets.get(1));
+ }
+
+ private void testArcMotion(Transition transition) {
+ assertNotNull(transition);
+ PathMotion motion = transition.getPathMotion();
+ assertNotNull(motion);
+ assertTrue(motion instanceof ArcMotion);
+ ArcMotion arcMotion = (ArcMotion) motion;
+ assertEquals(1f, arcMotion.getMinimumVerticalAngle(), 0.01f);
+ assertEquals(2f, arcMotion.getMinimumHorizontalAngle(), 0.01f);
+ assertEquals(53f, arcMotion.getMaximumAngle(), 0.01f);
+ }
+
+ private void testCustomPathMotion(Transition transition) {
+ assertNotNull(transition);
+ PathMotion motion = transition.getPathMotion();
+ assertNotNull(motion);
+ assertTrue(motion instanceof CustomPathMotion);
+ }
+
+ private void testPatternPathMotion(Transition transition) {
+ assertNotNull(transition);
+ PathMotion motion = transition.getPathMotion();
+ assertNotNull(motion);
+ assertTrue(motion instanceof PatternPathMotion);
+ PatternPathMotion pattern = (PatternPathMotion) motion;
+ Path path = pattern.getPatternPath();
+ PathMeasure measure = new PathMeasure(path, false);
+ assertEquals(200f, measure.getLength(), 0.1f);
+ }
+
+ public static class CustomTransition extends Transition {
+ public CustomTransition() {
+ fail("Default constructor was not expected");
+ }
+
+ public CustomTransition(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ public void captureStartValues(TransitionValues transitionValues) {
+
+ }
+
+ @Override
+ public void captureEndValues(TransitionValues transitionValues) {
+
+ }
+ }
+
+ public static class CustomPathMotion extends PathMotion {
+ public CustomPathMotion() {
+ fail("default constructor shouldn't be called.");
+ }
+
+ public CustomPathMotion(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ public Path getPath(float startX, float startY, float endX, float endY) {
+ return null;
+ }
+ }
+
+ public static class InflationFade extends Fade {
+ public InflationFade(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+ }
+
+ public static class InflationChangeBounds extends ChangeBounds {
+ public InflationChangeBounds(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+ }
+
+ public static class InflationSlide extends Slide {
+ public InflationSlide(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+ }
+
+ public static class InflationTransitionSet extends TransitionSet {
+ public InflationTransitionSet(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+ }
+
+ public static class InflationChangeImageTransform extends ChangeImageTransform {
+ public InflationChangeImageTransform(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+ }
+
+ public static class InflationChangeTransform extends ChangeTransform {
+ public InflationChangeTransform(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+ }
+
+ public static class InflationAutoTransition extends AutoTransition {
+ public InflationAutoTransition(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+ }
+
+ public static class InflationChangeClipBounds extends ChangeClipBounds {
+ public InflationChangeClipBounds(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+ }
+
+ public static class InflationChangeScroll extends ChangeScroll {
+ public InflationChangeScroll(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+ }
+
+ public static class InflationExplode extends Explode {
+ public InflationExplode(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+ }
+}
diff --git a/tests/tests/transition/src/android/transition/cts/TransitionManagerTest.java b/tests/tests/transition/src/android/transition/cts/TransitionManagerTest.java
index 9eacd1b..a8a653d 100644
--- a/tests/tests/transition/src/android/transition/cts/TransitionManagerTest.java
+++ b/tests/tests/transition/src/android/transition/cts/TransitionManagerTest.java
@@ -15,11 +15,13 @@
*/
package android.transition.cts;
+import android.graphics.Rect;
import android.transition.cts.R;
import android.transition.Scene;
import android.transition.TransitionManager;
import android.view.View;
+import android.view.ViewTreeObserver;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -57,6 +59,49 @@
});
}
+ public void testDefaultBeginDelayedTransition() throws Throwable {
+ enterScene(R.layout.scene1);
+ final CountDownLatch startLatch = new CountDownLatch(1);
+ mSceneRoot.getViewTreeObserver().addOnPreDrawListener(
+ new ViewTreeObserver.OnPreDrawListener() {
+ @Override
+ public boolean onPreDraw() {
+ mSceneRoot.getViewTreeObserver().removeOnPreDrawListener(this);
+ startLatch.countDown();
+ return true;
+ }
+ });
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ TransitionManager.beginDelayedTransition(mSceneRoot);
+ }
+ });
+ enterScene(R.layout.scene6);
+ assertTrue(startLatch.await(500, TimeUnit.MILLISECONDS));
+ ensureRedSquareIsMoving();
+ endTransition();
+ }
+
+ private void ensureRedSquareIsMoving() throws InterruptedException {
+ final View view = getActivity().findViewById(R.id.redSquare);
+ assertNotNull(view);
+ // We should see a ChangeBounds on redSquare
+ final Rect position = new Rect(view.getLeft(), view.getTop(), view.getRight(),
+ view.getBottom());
+ final CountDownLatch latch = new CountDownLatch(1);
+ view.postOnAnimationDelayed(new Runnable() {
+ @Override
+ public void run() {
+ Rect next = new Rect(view.getLeft(), view.getTop(), view.getRight(),
+ view.getBottom());
+ assertTrue(!next.equals(position));
+ latch.countDown();
+ }
+ }, 20);
+ assertTrue(latch.await(500, TimeUnit.MILLISECONDS));
+ }
+
public void testGo() throws Throwable {
startTransition(R.layout.scene1);
waitForStart();
@@ -77,6 +122,30 @@
});
}
+ public void testDefaultGo() throws Throwable {
+ enterScene(R.layout.scene1);
+ final CountDownLatch startLatch = new CountDownLatch(1);
+ mSceneRoot.getViewTreeObserver().addOnPreDrawListener(
+ new ViewTreeObserver.OnPreDrawListener() {
+ @Override
+ public boolean onPreDraw() {
+ mSceneRoot.getViewTreeObserver().removeOnPreDrawListener(this);
+ startLatch.countDown();
+ return true;
+ }
+ });
+ final Scene scene6 = loadScene(R.layout.scene6);
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ TransitionManager.go(scene6);
+ }
+ });
+ assertTrue(startLatch.await(500, TimeUnit.MILLISECONDS));
+ ensureRedSquareIsMoving();
+ endTransition();
+ }
+
public void testSetTransition1() throws Throwable {
final TransitionManager transitionManager = new TransitionManager();
diff --git a/tests/tests/transition/src/android/transition/cts/TransitionTest.java b/tests/tests/transition/src/android/transition/cts/TransitionTest.java
index 9ae568a..71b22fe 100644
--- a/tests/tests/transition/src/android/transition/cts/TransitionTest.java
+++ b/tests/tests/transition/src/android/transition/cts/TransitionTest.java
@@ -23,6 +23,7 @@
import android.transition.ArcMotion;
import android.transition.AutoTransition;
import android.transition.ChangeBounds;
+import android.transition.CircularPropagation;
import android.transition.PathMotion;
import android.transition.Scene;
import android.transition.Transition;
@@ -240,6 +241,7 @@
public void testIsTransitionRequired() throws Throwable {
enterScene(R.layout.scene1);
mTransition = new NotRequiredTransition();
+ assertFalse(mTransition.isTransitionRequired(null, null));
resetListener();
runTestOnUiThread(new Runnable() {
@Override
@@ -315,6 +317,19 @@
endTransition();
}
+ public void testExcludeChildrenClass() throws Throwable {
+ enterScene(R.layout.scene1);
+ mTransition.excludeChildren(RelativeLayout.class, true);
+ startTransition(R.layout.scene2);
+ waitForEnd(0); // Should already be ended, since no children are transitioning
+
+ resetListener();
+ mTransition.excludeChildren(RelativeLayout.class, false); // remove it
+ startTransition(R.layout.scene1);
+ assertEquals(1, mListener.endLatch.getCount()); // It is running
+ endTransition();
+ }
+
public void testExcludeTargetView() throws Throwable {
View layout1 = loadLayout(R.layout.scene1);
Scene scene1 = loadScene(layout1);
@@ -383,6 +398,7 @@
}
public void testEpicenter() throws Throwable {
+ assertNull(mTransition.getEpicenter());
EpicenterCallback callback = new EpicenterCallback() {
@Override
public Rect onGetEpicenter(Transition transition) {
@@ -390,7 +406,8 @@
}
};
mTransition.setEpicenterCallback(callback);
- assertEquals(callback, mTransition.getEpicenterCallback());
+ assertSame(callback, mTransition.getEpicenterCallback());
+ assertEquals(new Rect(0, 0, 1, 1), mTransition.getEpicenter());
}
public void testInterpolator() throws Throwable {
@@ -467,6 +484,14 @@
endTransition();
}
+ public void testSetPropagation() throws Throwable {
+ Transition transition = new ChangeBounds();
+ assertNull(transition.getPropagation());
+ TransitionPropagation propagation = new CircularPropagation();
+ transition.setPropagation(propagation);
+ assertSame(propagation, transition.getPropagation());
+ }
+
public void testStartDelay() throws Throwable {
CaptureAnimatorTransition transition = new CaptureAnimatorTransition();
mTransition = transition;
@@ -489,10 +514,13 @@
public void testTransitionValues() throws Throwable {
enterScene(R.layout.scene1);
- mTransition = new CheckTransitionValuesTransition();
+ CheckTransitionValuesTransition transition = new CheckTransitionValuesTransition();
+ mTransition = transition;
mTransition.setDuration(10);
resetListener();
- startTransition(R.layout.scene4);
+ startTransition(R.layout.scene2);
+ assertTrue(transition.onDisappearCalled.await(500, TimeUnit.MILLISECONDS));
+ assertTrue(transition.onAppearCalled.await(500, TimeUnit.MILLISECONDS));
// The transition has all the asserts in it, so we can just end it now.
endTransition();
}
@@ -515,6 +543,29 @@
waitForEnd(400);
}
+ public void testGetTransitionProperties() throws Throwable {
+ enterScene(R.layout.scene1);
+ // Make the transition make changes to properties in getTransitionProperties.
+ TransitionPropertiesTransition transition = new TransitionPropertiesTransition(false);
+ mTransition = transition;
+ resetListener();
+ startTransition(R.layout.scene2);
+ assertTrue(transition.latch.await(500, TimeUnit.MILLISECONDS));
+ endTransition();
+
+ // Now make the transition only make changes to unimportant properties.
+ transition = new TransitionPropertiesTransition(true);
+ mTransition = transition;
+ resetListener();
+ startTransition(R.layout.scene1);
+ assertTrue(mListener.endLatch.await(500, TimeUnit.MILLISECONDS));
+ // createAnimator shouldn't have been called.
+ assertEquals(1, transition.latch.getCount());
+
+ assertNotNull(transition.getTransitionProperties());
+ assertEquals(1, transition.getTransitionProperties().length);
+ }
+
private class NotRequiredTransition extends TestTransition {
@Override
public boolean isTransitionRequired(TransitionValues startValues,
@@ -551,9 +602,12 @@
}
private class CheckTransitionValuesTransition extends TestTransition {
+ public CountDownLatch onAppearCalled = new CountDownLatch(1);
+ public CountDownLatch onDisappearCalled = new CountDownLatch(1);
@Override
public Animator onAppear(ViewGroup sceneRoot, View view, TransitionValues startValues,
TransitionValues endValues) {
+ onAppearCalled.countDown();
assertNull(getTransitionValues(endValues.view, true));
assertEquals(endValues, getTransitionValues(endValues.view, false));
return super.onAppear(sceneRoot, view, startValues, endValues);
@@ -562,6 +616,7 @@
@Override
public Animator onDisappear(ViewGroup sceneRoot, View view, TransitionValues startValues,
TransitionValues endValues) {
+ onDisappearCalled.countDown();
assertNull(getTransitionValues(startValues.view, false));
assertEquals(startValues, getTransitionValues(startValues.view, true));
return super.onDisappear(sceneRoot, view, startValues, endValues);
@@ -576,5 +631,48 @@
startLatch.countDown();
}
}
+
+ private static class TransitionPropertiesTransition extends Transition {
+ private static final String SIDE_PROP = "prop1";
+ private static final String IMPORTANT_PROP = "prop2";
+ private static final String[] PROPERTIES = {
+ IMPORTANT_PROP
+ };
+
+ private boolean mOnlyUnimportant;
+ public CountDownLatch latch = new CountDownLatch(1);
+
+ public TransitionPropertiesTransition(boolean onlyUnimportant) {
+ mOnlyUnimportant = onlyUnimportant;
+ }
+
+ @Override
+ public String[] getTransitionProperties() {
+ return PROPERTIES;
+ }
+
+ @Override
+ public void captureStartValues(TransitionValues transitionValues) {
+ transitionValues.values.put(SIDE_PROP, 1);
+ transitionValues.values.put(IMPORTANT_PROP, 1);
+ }
+
+ @Override
+ public void captureEndValues(TransitionValues transitionValues) {
+ transitionValues.values.put(SIDE_PROP, 2);
+ int val = mOnlyUnimportant ? 1 : 2;
+ transitionValues.values.put(IMPORTANT_PROP, val);
+ }
+
+ @Override
+ public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues,
+ TransitionValues endValues) {
+ if (startValues != null && endValues != null) {
+ latch.countDown();
+ }
+
+ return null;
+ }
+ }
}
diff --git a/tests/tests/transition/src/android/transition/cts/VisibilityTest.java b/tests/tests/transition/src/android/transition/cts/VisibilityTest.java
index 9b91a36..81977a4 100644
--- a/tests/tests/transition/src/android/transition/cts/VisibilityTest.java
+++ b/tests/tests/transition/src/android/transition/cts/VisibilityTest.java
@@ -15,9 +15,15 @@
*/
package android.transition.cts;
+import android.animation.Animator;
+import android.transition.TransitionManager;
import android.transition.TransitionValues;
import android.transition.Visibility;
import android.view.View;
+import android.view.ViewGroup;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
public class VisibilityTest extends BaseTransitionTest {
Visibility mVisibilityTransition;
@@ -104,5 +110,102 @@
mTransition.captureStartValues(goneValues);
assertFalse(mVisibilityTransition.isVisible(goneValues));
}
+
+ public void testOnAppear() throws Throwable {
+ enterScene(R.layout.scene4);
+ AppearTransition transition = new AppearTransition();
+ mTransition = transition;
+ resetListener();
+ startTransition(R.layout.scene5);
+ assertTrue(transition.onAppearCalled.await(500, TimeUnit.MILLISECONDS));
+ // No need to end the transition since AppearTransition doesn't create
+ // any animators.
+ }
+
+ public void testOnDisppear() throws Throwable {
+ // First, test with overlay
+ enterScene(R.layout.scene5);
+ DisappearTransition transition = new DisappearTransition(true);
+ mTransition = transition;
+ resetListener();
+ startTransition(R.layout.scene4);
+ assertTrue(transition.onDisppearCalled.await(500, TimeUnit.MILLISECONDS));
+ // No need to end the transition since DisappearTransition doesn't create
+ // any animators.
+
+ // Next test without overlay
+ enterScene(R.layout.scene5);
+ transition = new DisappearTransition(false);
+ mTransition = transition;
+ resetListener();
+ final View text = getActivity().findViewById(R.id.text);
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ TransitionManager.beginDelayedTransition(mSceneRoot, mTransition);
+ text.setVisibility(View.GONE);
+ }
+ });
+ assertTrue(transition.onDisppearCalled.await(500, TimeUnit.MILLISECONDS));
+ // No need to end the transition since DisappearTransition doesn't create
+ // any animators.
+ }
+
+ static class AppearTransition extends Visibility {
+ private View mExpectedView;
+ public CountDownLatch onAppearCalled = new CountDownLatch(1);
+ @Override
+ public Animator onAppear(ViewGroup sceneRoot, TransitionValues startValues,
+ int startVisibility, TransitionValues endValues, int endVisibility) {
+ assertNotNull(endValues);
+ mExpectedView = endValues.view;
+ return super.onAppear(sceneRoot, startValues, startVisibility, endValues,
+ endVisibility);
+ }
+
+ @Override
+ public Animator onAppear(ViewGroup sceneRoot, View view, TransitionValues startValues,
+ TransitionValues endValues) {
+ assertSame(mExpectedView, view);
+ onAppearCalled.countDown();
+ return null;
+ }
+ }
+
+ static class DisappearTransition extends Visibility {
+ private View mExpectedView;
+ private final boolean mExpectingOverlay;
+ public CountDownLatch onDisppearCalled = new CountDownLatch(1);
+
+ public DisappearTransition(boolean expectingOverlay) {
+ mExpectingOverlay = expectingOverlay;
+ }
+
+ @Override
+ public Animator onDisappear(ViewGroup sceneRoot, TransitionValues startValues,
+ int startVisibility, TransitionValues endValues, int endVisibility) {
+ assertNotNull(startValues);
+ if (mExpectingOverlay) {
+ assertNull(endValues);
+ mExpectedView = null;
+ } else {
+ assertNotNull(endValues);
+ mExpectedView = endValues.view;
+ }
+ return super.onDisappear(sceneRoot, startValues, startVisibility, endValues,
+ endVisibility);
+ }
+
+ @Override
+ public Animator onDisappear(ViewGroup sceneRoot, View view, TransitionValues startValues,
+ TransitionValues endValues) {
+ assertNotNull(view);
+ if (mExpectedView != null) {
+ assertSame(mExpectedView, view);
+ }
+ onDisppearCalled.countDown();
+ return null;
+ }
+ }
}
diff --git a/tests/tests/uirendering/Android.mk b/tests/tests/uirendering/Android.mk
index fd01e10..2aeea5e 100644
--- a/tests/tests/uirendering/Android.mk
+++ b/tests/tests/uirendering/Android.mk
@@ -26,7 +26,10 @@
LOCAL_JAVA_LIBRARIES := android.test.runner
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ ctstestrunner \
+ mockito-target \
+ android-support-test
LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-renderscript-files-under, src)
diff --git a/tests/tests/uirendering/AndroidManifest.xml b/tests/tests/uirendering/AndroidManifest.xml
index 24cdd8a..41ec318 100644
--- a/tests/tests/uirendering/AndroidManifest.xml
+++ b/tests/tests/uirendering/AndroidManifest.xml
@@ -21,9 +21,11 @@
<uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application>
-<!-- android:theme="@style/WhiteBackgroundTheme"> -->
<activity android:name="android.uirendering.cts.testinfrastructure.DrawActivity"
android:theme="@style/WhiteBackgroundTheme"></activity>
+ <activity android:name="android.uirendering.cts.testinfrastructure.MaterialActivity"
+ android:theme="@android:style/Theme.Material.Light">
+ </activity>
<uses-library android:name="android.test.runner" />
</application>
diff --git a/tests/tests/uirendering/res/drawable-nodpi/edge_effect_color.png b/tests/tests/uirendering/res/drawable-nodpi/edge_effect_color.png
new file mode 100644
index 0000000..7ca4067
--- /dev/null
+++ b/tests/tests/uirendering/res/drawable-nodpi/edge_effect_color.png
Binary files differ
diff --git a/tests/tests/uirendering/res/drawable-nodpi/edge_effect_displacement_0.png b/tests/tests/uirendering/res/drawable-nodpi/edge_effect_displacement_0.png
new file mode 100644
index 0000000..e315dfd
--- /dev/null
+++ b/tests/tests/uirendering/res/drawable-nodpi/edge_effect_displacement_0.png
Binary files differ
diff --git a/tests/tests/uirendering/res/drawable-nodpi/edge_effect_displacement_1.png b/tests/tests/uirendering/res/drawable-nodpi/edge_effect_displacement_1.png
new file mode 100644
index 0000000..5e719b2
--- /dev/null
+++ b/tests/tests/uirendering/res/drawable-nodpi/edge_effect_displacement_1.png
Binary files differ
diff --git a/tests/tests/uirendering/res/drawable-nodpi/edge_effect_green.png b/tests/tests/uirendering/res/drawable-nodpi/edge_effect_green.png
new file mode 100644
index 0000000..321250b
--- /dev/null
+++ b/tests/tests/uirendering/res/drawable-nodpi/edge_effect_green.png
Binary files differ
diff --git a/tests/tests/uirendering/res/drawable-nodpi/edge_effect_red.png b/tests/tests/uirendering/res/drawable-nodpi/edge_effect_red.png
new file mode 100644
index 0000000..6429808
--- /dev/null
+++ b/tests/tests/uirendering/res/drawable-nodpi/edge_effect_red.png
Binary files differ
diff --git a/tests/tests/uirendering/res/drawable-nodpi/edge_effect_size.png b/tests/tests/uirendering/res/drawable-nodpi/edge_effect_size.png
new file mode 100644
index 0000000..d2af211
--- /dev/null
+++ b/tests/tests/uirendering/res/drawable-nodpi/edge_effect_size.png
Binary files differ
diff --git a/tests/tests/uirendering/res/drawable-nodpi/golden_blue_circle.png b/tests/tests/uirendering/res/drawable-nodpi/golden_blue_circle.png
new file mode 100644
index 0000000..f587fb7
--- /dev/null
+++ b/tests/tests/uirendering/res/drawable-nodpi/golden_blue_circle.png
Binary files differ
diff --git a/tests/tests/uirendering/res/drawable-nodpi/golden_dashed_oval.png b/tests/tests/uirendering/res/drawable-nodpi/golden_dashed_oval.png
new file mode 100644
index 0000000..c95568a
--- /dev/null
+++ b/tests/tests/uirendering/res/drawable-nodpi/golden_dashed_oval.png
Binary files differ
diff --git a/tests/tests/uirendering/res/drawable-nodpi/text_on_path.png b/tests/tests/uirendering/res/drawable-nodpi/text_on_path.png
new file mode 100644
index 0000000..d0dee6c
--- /dev/null
+++ b/tests/tests/uirendering/res/drawable-nodpi/text_on_path.png
Binary files differ
diff --git a/tests/tests/uirendering/res/drawable/dashed_oval.xml b/tests/tests/uirendering/res/drawable/dashed_oval.xml
new file mode 100644
index 0000000..904b016
--- /dev/null
+++ b/tests/tests/uirendering/res/drawable/dashed_oval.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+ -->
+<shape
+ android:shape="oval"
+ xmlns:android="http://schemas.android.com/apk/res/android">
+ <stroke
+ android:width="2px"
+ android:dashGap="6px"
+ android:dashWidth="2px"
+ android:color="@android:color/black"
+ />
+</shape>
diff --git a/tests/tests/uirendering/res/layout/frame_layout.xml b/tests/tests/uirendering/res/layout/frame_layout.xml
index 0eb1f9b..4ceac5d 100644
--- a/tests/tests/uirendering/res/layout/frame_layout.xml
+++ b/tests/tests/uirendering/res/layout/frame_layout.xml
@@ -17,4 +17,3 @@
android:id="@+id/frame_layout"
android:layout_width="@dimen/test_width"
android:layout_height="@dimen/test_height"/>
-
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/GoldenImageVerifier.java b/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/GoldenImageVerifier.java
index 42e8960..b9816db 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/GoldenImageVerifier.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/GoldenImageVerifier.java
@@ -15,7 +15,9 @@
*/
package android.uirendering.cts.bitmapverifiers;
+import android.content.Context;
import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
import android.uirendering.cts.bitmapcomparers.BitmapComparer;
import android.uirendering.cts.differencevisualizers.PassFailVisualizer;
import android.uirendering.cts.testinfrastructure.ActivityTestBase;
@@ -31,6 +33,10 @@
mBitmapComparer = bitmapComparer;
}
+ public GoldenImageVerifier(Context context, int goldenResId, BitmapComparer bitmapComparer) {
+ this(BitmapFactory.decodeResource(context.getResources(), goldenResId), bitmapComparer);
+ }
+
@Override
public boolean verify(int[] bitmap, int offset, int stride, int width, int height) {
boolean success = mBitmapComparer.verifySame(mGoldenBitmapArray, bitmap, offset, stride,
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/CanvasStateTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/CanvasStateTests.java
index 1e7a832..8360948 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/CanvasStateTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/CanvasStateTests.java
@@ -16,12 +16,18 @@
package android.uirendering.cts.testclasses;
+import android.graphics.Bitmap;
import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.graphics.Region;
import android.test.suitebuilder.annotation.MediumTest;
+import android.uirendering.cts.bitmapverifiers.ColorVerifier;
import android.uirendering.cts.testinfrastructure.ActivityTestBase;
+import android.util.DisplayMetrics;
+
import org.junit.Test;
import static org.junit.Assert.assertFalse;
@@ -114,4 +120,68 @@
})
.runWithoutVerification();
}
+
+ private void testFailureOnBitmapDraw(Bitmap bitmap) {
+ createTest()
+ .addCanvasClient((canvas, width, height) -> {
+ boolean sawException = false;
+ try {
+ canvas.drawBitmap(bitmap, 0, 0, null);
+ } catch (RuntimeException e) {
+ sawException = true;
+ }
+ assertTrue(sawException);
+ })
+ .runWithoutVerification();
+ }
+
+ @Test
+ public void testFailureOnDrawRecycledBitmap() {
+ Bitmap recycledBitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
+ recycledBitmap.recycle();
+ testFailureOnBitmapDraw(recycledBitmap);
+ }
+
+ @Test
+ public void testFailureOnNonPremultipliedBitmap() {
+ Bitmap nonPremultipliedBitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
+ nonPremultipliedBitmap.setPremultiplied(false);
+ nonPremultipliedBitmap.setHasAlpha(true);
+ testFailureOnBitmapDraw(nonPremultipliedBitmap);
+ }
+
+ @Test
+ public void testDrawScreenWideBitmap() {
+ createTest()
+ .addCanvasClient((canvas, width, height) -> {
+ DisplayMetrics displayMetrics =
+ getActivity().getResources().getDisplayMetrics();
+ assertTrue(displayMetrics.widthPixels <= canvas.getMaximumBitmapWidth());
+ assertTrue(displayMetrics.heightPixels <= canvas.getMaximumBitmapHeight());
+ Bitmap bitmap = Bitmap.createBitmap(displayMetrics.widthPixels,
+ displayMetrics.heightPixels, Bitmap.Config.ARGB_8888);
+ bitmap.eraseColor(Color.RED);
+ canvas.drawBitmap(bitmap, 0, 0, null);
+ })
+ .runWithVerifier(new ColorVerifier(Color.RED, 0));
+ }
+
+ @Test
+ public void testDrawLargeBitmap() {
+ // verify that HW and SW pipelines can both draw screen-and-a-half sized bitmap
+ createTest()
+ .addCanvasClient((canvas, width, height) -> {
+ DisplayMetrics displayMetrics =
+ getActivity().getResources().getDisplayMetrics();
+
+ int bWidth = displayMetrics.widthPixels * 3 / 2;
+ int bHeight = displayMetrics.heightPixels * 3 / 2;
+ bWidth = Math.min(bWidth, canvas.getMaximumBitmapWidth());
+ bHeight = Math.min(bHeight, canvas.getMaximumBitmapHeight());
+ Bitmap bitmap = Bitmap.createBitmap(bWidth, bHeight, Bitmap.Config.ARGB_8888);
+ bitmap.eraseColor(Color.RED);
+ canvas.drawBitmap(bitmap, 0, 0, null);
+ })
+ .runWithVerifier(new ColorVerifier(Color.RED, 0));
+ }
}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/EdgeEffectTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/EdgeEffectTests.java
new file mode 100644
index 0000000..a5137af
--- /dev/null
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/EdgeEffectTests.java
@@ -0,0 +1,214 @@
+/*
+ * 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.uirendering.cts.testclasses;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.app.Activity;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.uirendering.cts.R;
+import android.uirendering.cts.bitmapcomparers.MSSIMComparer;
+import android.uirendering.cts.bitmapverifiers.GoldenImageVerifier;
+import android.uirendering.cts.testinfrastructure.MaterialActivity;
+import android.uirendering.cts.util.BitmapAsserter;
+import android.widget.EdgeEffect;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestName;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.fail;
+import static org.mockito.Matchers.anyFloat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class EdgeEffectTests {
+
+ private static final int WIDTH = 90;
+ private static final int HEIGHT = 90;
+
+ @Rule
+ public TestName name = new TestName();
+
+ @Rule
+ public ActivityTestRule<MaterialActivity> mActivityRule = new ActivityTestRule<>(
+ MaterialActivity.class);
+
+ private BitmapAsserter mBitmapAsserter = new BitmapAsserter(this.getClass().getSimpleName(),
+ name.getMethodName());
+
+ interface EdgeEffectInitializer {
+ void initialize(EdgeEffect edgeEffect);
+ }
+
+ private Activity getActivity() {
+ return mActivityRule.getActivity();
+ }
+
+ @Before
+ public void setUp() {
+ mBitmapAsserter.setUp(getActivity());
+ }
+
+ private void assertEdgeEffect(EdgeEffectInitializer initializer, int goldenId) {
+ Bitmap bitmap = Bitmap.createBitmap(WIDTH, HEIGHT, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(bitmap);
+ canvas.drawColor(Color.WHITE);
+ EdgeEffect edgeEffect = new EdgeEffect(getActivity());
+ edgeEffect.setSize(WIDTH, HEIGHT);
+ edgeEffect.setColor(Color.RED);
+ initializer.initialize(edgeEffect);
+ edgeEffect.draw(canvas);
+
+ GoldenImageVerifier verifier = new GoldenImageVerifier(getActivity(), goldenId,
+ new MSSIMComparer(0.99));
+ mBitmapAsserter.assertBitmapIsVerified(bitmap, verifier,
+ name.getMethodName(), "EdgeEffect doesn't match expected");
+ }
+
+ @Test
+ public void testOnPull() {
+ assertEdgeEffect(edgeEffect -> {
+ edgeEffect.onPull(1);
+ }, R.drawable.edge_effect_red);
+ }
+
+ @Test
+ public void testSetSize() {
+ assertEdgeEffect(edgeEffect -> {
+ edgeEffect.setSize(70, 70);
+ edgeEffect.onPull(1);
+ }, R.drawable.edge_effect_size);
+ }
+
+ @Test
+ public void testSetColor() {
+ assertEdgeEffect(edgeEffect -> {
+ edgeEffect.setColor(Color.GREEN);
+ edgeEffect.onPull(1);
+ }, R.drawable.edge_effect_green);
+ }
+
+ @Test
+ public void testOnPullWithDisplacement() {
+ assertEdgeEffect(edgeEffect -> {
+ edgeEffect.onPull(1, 0);
+ }, R.drawable.edge_effect_displacement_0);
+
+ assertEdgeEffect(edgeEffect -> {
+ edgeEffect.onPull(1, 1);
+ }, R.drawable.edge_effect_displacement_1);
+ }
+
+ @Test
+ public void testIsFinished() {
+ EdgeEffect effect = new EdgeEffect(getActivity());
+ assertTrue(effect.isFinished());
+ effect.onPull(0.5f);
+ assertFalse(effect.isFinished());
+ }
+
+ @Test
+ public void testFinish() {
+ EdgeEffect effect = new EdgeEffect(getActivity());
+ effect.onPull(1);
+ effect.finish();
+ assertTrue(effect.isFinished());
+
+ effect.onAbsorb(1000);
+ effect.finish();
+ assertFalse(effect.draw(new Canvas()));
+ }
+
+ @Test
+ public void testGetColor() {
+ EdgeEffect effect = new EdgeEffect(getActivity());
+ effect.setColor(Color.GREEN);
+ assertEquals(Color.GREEN, effect.getColor());
+ }
+
+ @Test
+ public void testGetMaxHeight() {
+ EdgeEffect edgeEffect = new EdgeEffect(getActivity());
+ edgeEffect.setSize(200, 200);
+ assertTrue(edgeEffect.getMaxHeight() <= 200 * 2 + 1);
+ edgeEffect.setSize(200, 0);
+ assertEquals(0, edgeEffect.getMaxHeight());
+ }
+
+ private interface AlphaVerifier {
+ void verify(int oldAlpha, int newAlpha);
+ }
+
+ // validates changes to the alpha of draw commands produced by EdgeEffect
+ // over the course of an animation
+ private void verifyAlpha(EdgeEffectInitializer initializer, AlphaVerifier alphaVerifier) {
+ Canvas canvas = mock(Canvas.class);
+ ArgumentCaptor<Paint> captor = ArgumentCaptor.forClass(Paint.class);
+ EdgeEffect edgeEffect = new EdgeEffect(getActivity());
+ edgeEffect.setSize(200, 200);
+ initializer.initialize(edgeEffect);
+ edgeEffect.draw(canvas);
+ verify(canvas).drawCircle(anyFloat(), anyFloat(), anyFloat(), captor.capture());
+ int oldAlpha = captor.getValue().getAlpha();
+ for (int i = 0; i < 3; i++) {
+ try {
+ Thread.sleep(20);
+ } catch (InterruptedException e) {
+ fail();
+ }
+ canvas = mock(Canvas.class);
+ edgeEffect.draw(canvas);
+ verify(canvas).drawCircle(anyFloat(), anyFloat(), anyFloat(), captor.capture());
+ int newAlpha = captor.getValue().getAlpha();
+ alphaVerifier.verify(oldAlpha, newAlpha);
+ oldAlpha = newAlpha;
+ }
+ }
+
+ @Test
+ public void testOnAbsorb() {
+ verifyAlpha(edgeEffect -> {
+ edgeEffect.onAbsorb(10000);
+ }, ((oldAlpha, newAlpha) -> {
+ assertTrue("Alpha should grow", oldAlpha < newAlpha);
+ }));
+ }
+
+ @Test
+ public void testOnRelease() {
+ verifyAlpha(edgeEffect -> {
+ edgeEffect.onPull(1);
+ edgeEffect.onRelease();
+ }, ((oldAlpha, newAlpha) -> {
+ assertTrue("Alpha should decrease", oldAlpha > newAlpha);
+ }));
+ }
+
+}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ExactCanvasTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ExactCanvasTests.java
index 6f90433..d0bfbf2 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ExactCanvasTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ExactCanvasTests.java
@@ -16,8 +16,11 @@
package android.uirendering.cts.testclasses;
+import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.Picture;
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.drawable.NinePatchDrawable;
@@ -26,6 +29,8 @@
import android.uirendering.cts.bitmapcomparers.ExactComparer;
import android.uirendering.cts.bitmapverifiers.BitmapVerifier;
import android.uirendering.cts.bitmapverifiers.RectVerifier;
+import android.uirendering.cts.bitmapcomparers.MSSIMComparer;
+import android.uirendering.cts.bitmapverifiers.GoldenImageVerifier;
import android.uirendering.cts.testinfrastructure.ActivityTestBase;
import android.uirendering.cts.R;
import org.junit.Test;
@@ -138,6 +143,44 @@
.runWithComparer(mExactComparer);
}
+ private void drawTestTextOnPath(Canvas canvas) {
+ final String testString = "THIS IS A TEST ON A CIRCLE PATH";
+ Path path = new Path();
+ path.addCircle(45, 45, 30, Path.Direction.CW);
+ Paint p = new Paint();
+ p.setColor(Color.BLACK);
+ p.setAntiAlias(true);
+ canvas.drawTextOnPath(testString, path, 0f, 0f, p);
+ }
+
+ @Test
+ public void testTextOnPath() {
+ createTest()
+ .addCanvasClient((canvas, width, height) -> {
+ drawTestTextOnPath(canvas);
+ })
+ .runWithVerifier(new GoldenImageVerifier(getActivity(),
+ // HWUI's texts are blurry, so we lower the threshold.
+ // Note that 0.7 will fail the test.
+ R.drawable.text_on_path, new MSSIMComparer(0.6)));
+ }
+
+ @Test
+ public void testTextOnPathUsingPicture() {
+ createTest()
+ .addCanvasClient((canvas, width, height) -> {
+ Picture picture = new Picture();
+ Canvas pictureCanvas = picture.beginRecording(90, 90);
+ drawTestTextOnPath(pictureCanvas);
+ picture.endRecording();
+ picture.draw(canvas);
+ })
+ .runWithVerifier(new GoldenImageVerifier(getActivity(),
+ // HWUI's texts are blurry, so we lower the threshold.
+ // Note that 0.7 will fail the test.
+ R.drawable.text_on_path, new MSSIMComparer(0.6)));
+ }
+
@Test
public void testBasicColorXfermode() {
createTest()
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ShapeTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ShapeTests.java
new file mode 100644
index 0000000..18fd2bc
--- /dev/null
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ShapeTests.java
@@ -0,0 +1,37 @@
+/*
+ * 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.uirendering.cts.testclasses;
+
+import android.graphics.*;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.uirendering.cts.R;
+import android.uirendering.cts.bitmapcomparers.MSSIMComparer;
+import android.uirendering.cts.bitmapverifiers.GoldenImageVerifier;
+import android.uirendering.cts.testinfrastructure.ActivityTestBase;
+import org.junit.Test;
+
+@MediumTest
+public class ShapeTests extends ActivityTestBase {
+ @Test
+ public void testDashedOval() {
+ createTest()
+ .addLayout(R.layout.frame_layout,
+ view -> view.setBackgroundResource(R.drawable.dashed_oval))
+ .runWithVerifier(new GoldenImageVerifier(getActivity(),
+ R.drawable.golden_dashed_oval, new MSSIMComparer(0.99)));
+ }
+}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ViewAnimationUtilsTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ViewAnimationUtilsTests.java
new file mode 100644
index 0000000..4a3e7cf
--- /dev/null
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ViewAnimationUtilsTests.java
@@ -0,0 +1,41 @@
+/*
+ * 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.uirendering.cts.testclasses;
+
+import android.test.suitebuilder.annotation.MediumTest;
+import android.uirendering.cts.R;
+import android.uirendering.cts.bitmapcomparers.MSSIMComparer;
+import android.uirendering.cts.bitmapverifiers.GoldenImageVerifier;
+import android.uirendering.cts.testinfrastructure.ActivityTestBase;
+import android.uirendering.cts.testinfrastructure.ViewInitializer;
+import android.view.ViewAnimationUtils;
+import org.junit.Test;
+
+@MediumTest
+public class ViewAnimationUtilsTests extends ActivityTestBase {
+ @Test
+ public void testCreateCircularReveal() {
+ createTest()
+ .addLayout(R.layout.blue_padded_layout, (ViewInitializer) view -> {
+ ViewAnimationUtils.createCircularReveal(view, 45, 45, 45, 45)
+ .setDuration(10000) // 10 sec, longer than animation
+ .start();
+ }, true)
+ .runWithVerifier(new GoldenImageVerifier(getActivity(),
+ R.drawable.golden_blue_circle, new MSSIMComparer(0.99)));
+ }
+}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/ActivityTestBase.java b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/ActivityTestBase.java
index 458f69b..b983a48 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/ActivityTestBase.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/ActivityTestBase.java
@@ -19,14 +19,10 @@
import android.app.Instrumentation;
import android.graphics.Bitmap;
import android.graphics.Point;
-import android.renderscript.Allocation;
-import android.renderscript.RenderScript;
import android.support.test.rule.ActivityTestRule;
import android.uirendering.cts.bitmapcomparers.BitmapComparer;
import android.uirendering.cts.bitmapverifiers.BitmapVerifier;
-import android.uirendering.cts.differencevisualizers.DifferenceVisualizer;
-import android.uirendering.cts.differencevisualizers.PassFailVisualizer;
-import android.uirendering.cts.util.BitmapDumper;
+import android.uirendering.cts.util.BitmapAsserter;
import android.util.Log;
import android.support.test.InstrumentationRegistry;
@@ -40,8 +36,6 @@
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
-import static org.junit.Assert.assertTrue;
-
/**
* This class contains the basis for the graphics hardware test classes. Contained within this class
* are several methods that help with the execution of tests, and should be extended to gain the
@@ -50,16 +44,11 @@
public abstract class ActivityTestBase {
public static final String TAG = "ActivityTestBase";
public static final boolean DEBUG = false;
- public static final boolean USE_RS = false;
//The minimum height and width of a device
public static final int TEST_WIDTH = 90;
public static final int TEST_HEIGHT = 90;
- private int[] mHardwareArray = new int[TEST_HEIGHT * TEST_WIDTH];
- private int[] mSoftwareArray = new int[TEST_HEIGHT * TEST_WIDTH];
- private DifferenceVisualizer mDifferenceVisualizer;
- private RenderScript mRenderScript;
private TestCaseBuilder mTestCaseBuilder;
@Rule
@@ -69,21 +58,8 @@
@Rule
public TestName name = new TestName();
- /**
- * The default constructor creates the package name and sets the DrawActivity as the class that
- * we would use.
- */
- public ActivityTestBase() {
- mDifferenceVisualizer = new PassFailVisualizer();
-
- // Create a location for the files to be held, if it doesn't exist already
- BitmapDumper.createSubDirectory(this.getClass().getSimpleName());
-
- // If we have a test currently, let's remove the older files if they exist
- if (getName() != null) {
- BitmapDumper.deleteFileInClassFolder(this.getClass().getSimpleName(), getName());
- }
- }
+ private BitmapAsserter mBitmapAsserter = new BitmapAsserter(this.getClass().getSimpleName(),
+ name.getMethodName());
protected DrawActivity getActivity() {
return mActivityRule.getActivity();
@@ -99,10 +75,7 @@
@Before
public void setUp() {
- mDifferenceVisualizer = new PassFailVisualizer();
- if (USE_RS) {
- mRenderScript = RenderScript.create(getActivity().getApplicationContext());
- }
+ mBitmapAsserter.setUp(getActivity());
}
@After
@@ -153,54 +126,6 @@
return takeScreenshot(testOffset);
}
- /**
- * Compares the two bitmaps saved using the given test. If they fail, the files are saved using
- * the test name.
- */
- protected void assertBitmapsAreSimilar(Bitmap bitmap1, Bitmap bitmap2,
- BitmapComparer comparer, String debugMessage) {
- boolean success;
-
- if (USE_RS && comparer.supportsRenderScript()) {
- Allocation idealAllocation = Allocation.createFromBitmap(mRenderScript, bitmap1,
- Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
- Allocation givenAllocation = Allocation.createFromBitmap(mRenderScript, bitmap2,
- Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
- success = comparer.verifySameRS(getActivity().getResources(), idealAllocation,
- givenAllocation, 0, TEST_WIDTH, TEST_WIDTH, TEST_HEIGHT, mRenderScript);
- } else {
- bitmap1.getPixels(mSoftwareArray, 0, TEST_WIDTH, 0, 0, TEST_WIDTH, TEST_HEIGHT);
- bitmap2.getPixels(mHardwareArray, 0, TEST_WIDTH, 0, 0, TEST_WIDTH, TEST_HEIGHT);
- success = comparer.verifySame(mSoftwareArray, mHardwareArray, 0, TEST_WIDTH, TEST_WIDTH,
- TEST_HEIGHT);
- }
-
- if (!success) {
- BitmapDumper.dumpBitmaps(bitmap1, bitmap2, getName(), this.getClass().getSimpleName(),
- mDifferenceVisualizer);
- }
-
- assertTrue(debugMessage, success);
- }
-
- /**
- * Tests to see if a bitmap passes a verifier's test. If it doesn't the bitmap is saved to the
- * sdcard.
- */
- protected void assertBitmapIsVerified(Bitmap bitmap, BitmapVerifier bitmapVerifier,
- String debugMessage) {
- bitmap.getPixels(mSoftwareArray, 0, TEST_WIDTH, 0, 0,
- TEST_WIDTH, TEST_HEIGHT);
- boolean success = bitmapVerifier.verify(mSoftwareArray, 0, TEST_WIDTH, TEST_WIDTH, TEST_HEIGHT);
- if (!success) {
- Bitmap croppedBitmap = Bitmap.createBitmap(bitmap, 0, 0, TEST_WIDTH, TEST_HEIGHT);
- BitmapDumper.dumpBitmap(croppedBitmap, getName(), this.getClass().getSimpleName());
- BitmapDumper.dumpBitmap(bitmapVerifier.getDifferenceBitmap(), getName() + "_verifier",
- this.getClass().getSimpleName());
- }
- assertTrue(debugMessage, success);
- }
-
protected TestCaseBuilder createTest() {
mTestCaseBuilder = new TestCaseBuilder();
return mTestCaseBuilder;
@@ -229,8 +154,8 @@
for (TestCase testCase : mTestCases) {
Bitmap testCaseBitmap = captureRenderSpec(testCase);
- assertBitmapsAreSimilar(idealBitmap, testCaseBitmap, bitmapComparer,
- testCase.getDebugString());
+ mBitmapAsserter.assertBitmapsAreSimilar(idealBitmap, testCaseBitmap, bitmapComparer,
+ getName(), testCase.getDebugString());
}
}
@@ -245,7 +170,8 @@
for (TestCase testCase : mTestCases) {
Bitmap testCaseBitmap = captureRenderSpec(testCase);
- assertBitmapIsVerified(testCaseBitmap, bitmapVerifier, testCase.getDebugString());
+ mBitmapAsserter.assertBitmapIsVerified(testCaseBitmap, bitmapVerifier,
+ getName(), testCase.getDebugString());
}
}
@@ -274,8 +200,8 @@
e.printStackTrace();
}
Bitmap testCaseBitmap = takeScreenshot(testOffset);
- assertBitmapIsVerified(testCaseBitmap, bitmapVerifier,
- testCase.getDebugString());
+ mBitmapAsserter.assertBitmapIsVerified(testCaseBitmap, bitmapVerifier,
+ getName(), testCase.getDebugString());
}
}
}
diff --git a/hostsidetests/services/activitymanager/app/src/android/server/app/BottomLeftLayoutActivity.java b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/MaterialActivity.java
similarity index 85%
copy from hostsidetests/services/activitymanager/app/src/android/server/app/BottomLeftLayoutActivity.java
copy to tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/MaterialActivity.java
index 27a6fe2..53df12c 100644
--- a/hostsidetests/services/activitymanager/app/src/android/server/app/BottomLeftLayoutActivity.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/MaterialActivity.java
@@ -14,9 +14,10 @@
* limitations under the License.
*/
-package android.server.app;
+package android.uirendering.cts.testinfrastructure;
import android.app.Activity;
-public class BottomLeftLayoutActivity extends Activity {
+public class MaterialActivity extends Activity {
+
}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/util/BitmapAsserter.java b/tests/tests/uirendering/src/android/uirendering/cts/util/BitmapAsserter.java
new file mode 100644
index 0000000..51e7dde
--- /dev/null
+++ b/tests/tests/uirendering/src/android/uirendering/cts/util/BitmapAsserter.java
@@ -0,0 +1,115 @@
+/*
+ * 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.uirendering.cts.util;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.renderscript.Allocation;
+import android.renderscript.RenderScript;
+import android.uirendering.cts.bitmapcomparers.BitmapComparer;
+import android.uirendering.cts.bitmapverifiers.BitmapVerifier;
+import android.uirendering.cts.differencevisualizers.DifferenceVisualizer;
+import android.uirendering.cts.differencevisualizers.PassFailVisualizer;
+
+public class BitmapAsserter {
+ public static final boolean USE_RS = false;
+ private DifferenceVisualizer mDifferenceVisualizer;
+ private RenderScript mRenderScript;
+ private Context mContext;
+ private String mClassName;
+
+ public BitmapAsserter(String className, String name) {
+ mClassName = className;
+ mDifferenceVisualizer = new PassFailVisualizer();
+
+ // Create a location for the files to be held, if it doesn't exist already
+ BitmapDumper.createSubDirectory(mClassName);
+
+ // If we have a test currently, let's remove the older files if they exist
+ if (name != null) {
+ BitmapDumper.deleteFileInClassFolder(mClassName, name);
+ }
+ }
+
+ public void setUp(Context context) {
+ mDifferenceVisualizer = new PassFailVisualizer();
+ mContext = context;
+ if (USE_RS) {
+ mRenderScript = RenderScript.create(context.getApplicationContext());
+ }
+ }
+
+ /**
+ * Compares the two bitmaps saved using the given test. If they fail, the files are saved using
+ * the test name.
+ */
+ public void assertBitmapsAreSimilar(Bitmap bitmap1, Bitmap bitmap2, BitmapComparer comparer,
+ String testName, String debugMessage) {
+ boolean success;
+ int width = bitmap1.getWidth();
+ int height = bitmap1.getHeight();
+
+ if (width != bitmap2.getWidth() || height != bitmap2.getHeight()) {
+ fail("Can't compare bitmaps of different sizes");
+ }
+
+ if (USE_RS && comparer.supportsRenderScript()) {
+ Allocation idealAllocation = Allocation.createFromBitmap(mRenderScript, bitmap1,
+ Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
+ Allocation givenAllocation = Allocation.createFromBitmap(mRenderScript, bitmap2,
+ Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
+ success = comparer.verifySameRS(mContext.getResources(), idealAllocation,
+ givenAllocation, 0, width, width, height, mRenderScript);
+ } else {
+ int[] pixels1 = new int[width * height];
+ int[] pixels2 = new int[width * height];
+ bitmap1.getPixels(pixels1, 0, width, 0, 0, width, height);
+ bitmap2.getPixels(pixels2, 0, width, 0, 0, width, height);
+ success = comparer.verifySame(pixels1, pixels2, 0, width, width, height);
+ }
+
+ if (!success) {
+ BitmapDumper.dumpBitmaps(bitmap1, bitmap2, testName, mClassName, mDifferenceVisualizer);
+ }
+
+ assertTrue(debugMessage, success);
+ }
+
+ /**
+ * Tests to see if a bitmap passes a verifier's test. If it doesn't the bitmap is saved to the
+ * sdcard.
+ */
+ public void assertBitmapIsVerified(Bitmap bitmap, BitmapVerifier bitmapVerifier,
+ String testName, String debugMessage) {
+ int width = bitmap.getWidth();
+ int height = bitmap.getHeight();
+ int[] pixels = new int[width * height];
+ bitmap.getPixels(pixels, 0, width, 0, 0, width, height);
+ boolean success = bitmapVerifier.verify(pixels, 0, width, width, height);
+ if (!success) {
+ Bitmap croppedBitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height);
+ BitmapDumper.dumpBitmap(croppedBitmap, testName, mClassName);
+ BitmapDumper.dumpBitmap(bitmapVerifier.getDifferenceBitmap(), testName + "_verifier",
+ this.getClass().getSimpleName());
+ }
+ assertTrue(debugMessage, success);
+ }
+
+
+}
diff --git a/tests/tests/util/Android.mk b/tests/tests/util/Android.mk
index cb1c70a..866e566 100644
--- a/tests/tests/util/Android.mk
+++ b/tests/tests/util/Android.mk
@@ -24,7 +24,7 @@
# Tag this module as a cts test artifact
LOCAL_COMPATIBILITY_SUITE := cts
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-annotations android-support-test ctstestrunner
LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/tests/util/src/android/util/cts/LongSparseArrayTest.java b/tests/tests/util/src/android/util/cts/LongSparseArrayTest.java
index e5b23f8..cb4a153 100644
--- a/tests/tests/util/src/android/util/cts/LongSparseArrayTest.java
+++ b/tests/tests/util/src/android/util/cts/LongSparseArrayTest.java
@@ -179,4 +179,19 @@
assertEquals(Long.MIN_VALUE, sparseArray.valueAt(3).longValue());
}
+ public void testIndexOfValueByValue() {
+ LongSparseArray<String> sparseArray = new LongSparseArray<String>();
+ // Insert a number of String Objects into array
+ sparseArray.put(1L, "Index 0");
+ sparseArray.put(15L, "Index 1");
+ sparseArray.put(25L, "Index 2");
+ sparseArray.put(50L, "Index 3");
+ sparseArray.put(51L, "Index 4");
+
+ assertTrue(sparseArray.indexOfValueByValue("Index 0") == 0);
+ assertTrue(sparseArray.indexOfValueByValue("Index 1") == 1);
+ assertTrue(sparseArray.indexOfValueByValue("Index 2") == 2);
+ assertTrue(sparseArray.indexOfValueByValue("Index 3") == 3);
+ assertTrue(sparseArray.indexOfValueByValue("Index 4") == 4);
+ }
}
diff --git a/tests/tests/util/src/android/util/cts/MutableTest.java b/tests/tests/util/src/android/util/cts/MutableTest.java
new file mode 100644
index 0000000..e4f6fd9
--- /dev/null
+++ b/tests/tests/util/src/android/util/cts/MutableTest.java
@@ -0,0 +1,77 @@
+/*
+ * 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.util.cts;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.*;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.*;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class MutableTest {
+ @Test
+ public void testMutableBoolean() {
+ MutableBoolean mut = new MutableBoolean(false);
+ assertFalse(mut.value);
+ mut = new MutableBoolean(true);
+ assertTrue(mut.value);
+ }
+
+ @Test
+ public void testMutableByte() {
+ MutableByte mut = new MutableByte((byte) 127);
+ assertEquals(127, mut.value);
+ mut = new MutableByte((byte) -128);
+ assertEquals(-128, mut.value);
+ }
+
+ @Test
+ public void testMutableChar() {
+ MutableChar mut = new MutableChar('a');
+ assertEquals('a', mut.value);
+ mut = new MutableChar('b');
+ assertEquals('b', mut.value);
+ }
+
+ @Test
+ public void testMutableDouble() {
+ MutableDouble mut = new MutableDouble(0);
+ assertEquals(0, mut.value, 0);
+ mut = new MutableDouble(Double.MAX_VALUE);
+ assertEquals(Double.MAX_VALUE, mut.value, 0);
+ }
+
+ @Test
+ public void testMutableFloat() {
+ MutableFloat mut = new MutableFloat(0f);
+ assertEquals(0f, mut.value, 0);
+ mut = new MutableFloat(Float.MAX_VALUE);
+ assertEquals(Float.MAX_VALUE, mut.value, 0);
+ }
+
+ @Test
+ public void testMutableShort() {
+ MutableShort mut = new MutableShort((short) 0);
+ assertEquals(0, mut.value);
+ mut = new MutableShort(Short.MAX_VALUE);
+ assertEquals(Short.MAX_VALUE, mut.value);
+ }
+}
diff --git a/tests/tests/util/src/android/util/cts/PropertyTest.java b/tests/tests/util/src/android/util/cts/PropertyTest.java
index 22ad2c4..3d76e36 100644
--- a/tests/tests/util/src/android/util/cts/PropertyTest.java
+++ b/tests/tests/util/src/android/util/cts/PropertyTest.java
@@ -17,51 +17,62 @@
package android.util.cts;
import android.graphics.Point;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
import android.util.FloatProperty;
import android.util.IntProperty;
import android.util.Property;
-import junit.framework.TestCase;
+import org.junit.Test;
+import org.junit.runner.RunWith;
-public class PropertyTest extends TestCase {
+import static org.junit.Assert.*;
- float mFloatValue = -1;
- int mIntValue = -2;
- Point mPointValue = new Point(-3, -4);
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class PropertyTest {
+ private float mFloatValue = -1;
+ private int mIntValue = -2;
+ private Point mPointValue = new Point(-3, -4);
+ @Test
public void testProperty() throws Exception {
float testFloatValue = 5;
Point testPointValue = new Point(10, 20);
assertFalse(getFloatProp() == testFloatValue);
assertFalse(getPointProp().equals(testPointValue));
- assertEquals(RAW_FLOAT_PROP.get(this), getFloatProp());
- assertEquals(RAW_POINT_PROP.get(this), getPointProp());
+ assertEquals(getFloatProp(), RAW_FLOAT_PROP.get(this), 0f);
+ assertEquals(getPointProp(), RAW_POINT_PROP.get(this));
RAW_FLOAT_PROP.set(this, testFloatValue);
- assertEquals(RAW_FLOAT_PROP.get(this), mFloatValue);
+ assertEquals(mFloatValue, RAW_FLOAT_PROP.get(this), 0f);
RAW_POINT_PROP.set(this, testPointValue);
- assertEquals(RAW_POINT_PROP.get(this), testPointValue);
+ assertEquals(testPointValue, RAW_POINT_PROP.get(this));
}
+ @Test
public void testFloatProperty() throws Exception {
- float testFloatValue = 5;
+ assertFalse(getFloatProp() == 5);
+ assertEquals(getFloatProp(), FLOAT_PROP.get(this), 0f);
- assertFalse(getFloatProp() == testFloatValue);
- assertEquals(FLOAT_PROP.get(this), getFloatProp());
+ FLOAT_PROP.set(this, 5f);
+ assertEquals(5f, FLOAT_PROP.get(this), 0f);
- FLOAT_PROP.set(this, testFloatValue);
- assertEquals(FLOAT_PROP.get(this), testFloatValue);
+ FLOAT_PROP.setValue(this, 10);
+ assertEquals(10f, FLOAT_PROP.get(this), 0f);
}
+ @Test
public void testIntProperty() throws Exception {
- int testIntValue = 5;
+ assertFalse(getIntProp() == 5);
+ assertEquals(getIntProp(), INT_PROP.get(this).intValue());
- assertFalse(getIntProp() == testIntValue);
- assertEquals(INT_PROP.get(this).intValue(), getIntProp());
+ INT_PROP.set(this, 5);
+ assertEquals(5, INT_PROP.get(this).intValue());
- INT_PROP.set(this, testIntValue);
- assertEquals(INT_PROP.get(this).intValue(), testIntValue);
+ INT_PROP.setValue(this, 10);
+ assertEquals(10, INT_PROP.get(this).intValue());
}
// Utility methods to get/set instance values. Used by Property classes below.
@@ -91,9 +102,9 @@
}
// Properties. RAW subclass from the generic Property class, the others subclass from
- // the primtive-friendly IntProperty and FloatProperty subclasses.
+ // the primitive-friendly IntProperty and FloatProperty subclasses.
- public static final Property<PropertyTest, Point> RAW_POINT_PROP =
+ private static final Property<PropertyTest, Point> RAW_POINT_PROP =
new Property<PropertyTest, Point>(Point.class, "rawPoint") {
@Override
public void set(PropertyTest object, Point value) {
@@ -106,7 +117,7 @@
}
};
- public static final Property<PropertyTest, Float> RAW_FLOAT_PROP =
+ private static final Property<PropertyTest, Float> RAW_FLOAT_PROP =
new Property<PropertyTest, Float>(Float.class, "rawFloat") {
@Override
public void set(PropertyTest object, Float value) {
@@ -119,7 +130,7 @@
}
};
- public static final Property<PropertyTest, Float> FLOAT_PROP =
+ private static final FloatProperty<PropertyTest> FLOAT_PROP =
new FloatProperty<PropertyTest>("float") {
@Override
@@ -133,7 +144,7 @@
}
};
- public static final Property<PropertyTest, Integer> INT_PROP =
+ private static final IntProperty<PropertyTest> INT_PROP =
new IntProperty<PropertyTest>("int") {
@Override
diff --git a/tests/tests/util/src/android/util/cts/RationalTest.java b/tests/tests/util/src/android/util/cts/RationalTest.java
index ab5c063..dabb29d 100644
--- a/tests/tests/util/src/android/util/cts/RationalTest.java
+++ b/tests/tests/util/src/android/util/cts/RationalTest.java
@@ -369,6 +369,34 @@
}
}
+ @SmallTest
+ public void testParseRational() {
+ assertEquals(new Rational(1, 2), Rational.parseRational("3:+6"));
+ assertEquals(new Rational(1, 2), Rational.parseRational("-3:-6"));
+ assertEquals(Rational.NaN, Rational.parseRational("NaN"));
+ assertEquals(Rational.POSITIVE_INFINITY, Rational.parseRational("Infinity"));
+ assertEquals(Rational.NEGATIVE_INFINITY, Rational.parseRational("-Infinity"));
+ assertEquals(Rational.ZERO, Rational.parseRational("0/261"));
+ assertEquals(Rational.NaN, Rational.parseRational("0/-0"));
+ assertEquals(Rational.POSITIVE_INFINITY, Rational.parseRational("1000/+0"));
+ assertEquals(Rational.NEGATIVE_INFINITY, Rational.parseRational("-1000/-0"));
+
+ try {
+ fail("Parsing " + Rational.parseRational("1.5") + " should not have succeeded");
+ } catch (NumberFormatException exception) {
+ // OK
+ }
+
+ try {
+ fail("Parsing " + Rational.parseRational("239") + " should not have succeeded");
+ } catch (NumberFormatException exception) {
+ // OK
+ }
+
+ Rational r = new Rational(10, 15);
+ assertEquals(r, Rational.parseRational(r.toString()));
+ }
+
private static void assertValueEquals(Rational object, float expected) {
assertEquals("Checking floatValue() for " + object + ";",
expected, object.floatValue());
diff --git a/tests/tests/util/src/android/util/cts/SizeTest.java b/tests/tests/util/src/android/util/cts/SizeTest.java
new file mode 100644
index 0000000..b0cc89b
--- /dev/null
+++ b/tests/tests/util/src/android/util/cts/SizeTest.java
@@ -0,0 +1,67 @@
+/*
+ * 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.util.cts;
+
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Size;
+import android.util.SizeF;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.*;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class SizeTest {
+ @Test
+ public void testConstructors() {
+ Size size = new Size(100, 200);
+ assertEquals(100, size.getWidth());
+ assertEquals(200, size.getHeight());
+
+ SizeF sizeF = new SizeF(100, 200);
+ assertEquals(100, sizeF.getWidth(), 0f);
+ assertEquals(200, sizeF.getHeight(), 0f);
+ }
+
+ @Test
+ public void testParseSize() {
+ try {
+ Size.parseSize("2by4");
+ fail("Should not be able to parse");
+ } catch (NumberFormatException ignored) {
+ }
+
+ assertEquals(new Size(100, 200), Size.parseSize("100*200"));
+ assertEquals(new Size(10, 20), Size.parseSize("10x20"));
+ assertEquals(new SizeF(9999, 9999), SizeF.parseSizeF("9999x9999"));
+ }
+
+ @Test
+ public void testParseSizeF() {
+ try {
+ SizeF.parseSizeF("2by4");
+ fail("Should not be able to parse");
+ } catch (NumberFormatException ignored) {
+ }
+
+ assertEquals(new SizeF(100f, 200f), SizeF.parseSizeF("100*200"));
+ assertEquals(new SizeF(10f, 20f), SizeF.parseSizeF("10x20"));
+ assertEquals(new SizeF(1000000f, 2.4f), SizeF.parseSizeF("1e6x2.4"));
+ }
+}
diff --git a/tests/tests/util/src/android/util/cts/SparseArrayTest.java b/tests/tests/util/src/android/util/cts/SparseArrayTest.java
index 6dbb571..3f4aadf 100644
--- a/tests/tests/util/src/android/util/cts/SparseArrayTest.java
+++ b/tests/tests/util/src/android/util/cts/SparseArrayTest.java
@@ -174,4 +174,20 @@
assertEquals(20L, sparseArray.valueAt(2).longValue());
assertEquals(Long.MIN_VALUE, sparseArray.valueAt(3).longValue());
}
+
+ public void testIndexOfValueByValue() {
+ SparseArray<String> sparseArray = new SparseArray<String>();
+ // Insert a number of String Objects into array
+ sparseArray.put(1, "Index 0");
+ sparseArray.put(15, "Index 1");
+ sparseArray.put(25, "Index 2");
+ sparseArray.put(50, "Index 3");
+ sparseArray.put(51, "Index 4");
+
+ assertTrue(sparseArray.indexOfValueByValue("Index 0") == 0);
+ assertTrue(sparseArray.indexOfValueByValue("Index 1") == 1);
+ assertTrue(sparseArray.indexOfValueByValue("Index 2") == 2);
+ assertTrue(sparseArray.indexOfValueByValue("Index 3") == 3);
+ assertTrue(sparseArray.indexOfValueByValue("Index 4") == 4);
+ }
}
diff --git a/tests/tests/util/src/android/util/cts/TypedValueTest.java b/tests/tests/util/src/android/util/cts/TypedValueTest.java
index 2ab91d9..735074f 100644
--- a/tests/tests/util/src/android/util/cts/TypedValueTest.java
+++ b/tests/tests/util/src/android/util/cts/TypedValueTest.java
@@ -205,4 +205,24 @@
assertEquals(TypedValue.complexToDimension(10, dm), tv.getDimension(dm));
}
+
+ public void testGetComplexUnit() {
+ TypedValue tv = new TypedValue();
+ tv.data = 256;
+ assertEquals(TypedValue.COMPLEX_UNIT_PX, tv.getComplexUnit());
+ tv.data = 257;
+ assertEquals(TypedValue.COMPLEX_UNIT_DIP, tv.getComplexUnit());
+ tv.data = 258;
+ assertEquals(TypedValue.COMPLEX_UNIT_SP, tv.getComplexUnit());
+ tv.data = 259;
+ assertEquals(TypedValue.COMPLEX_UNIT_PT, tv.getComplexUnit());
+ tv.data = 260;
+ assertEquals(TypedValue.COMPLEX_UNIT_IN, tv.getComplexUnit());
+ tv.data = 261;
+ assertEquals(TypedValue.COMPLEX_UNIT_MM, tv.getComplexUnit());
+ tv.data = 21474864;
+ assertEquals(TypedValue.COMPLEX_UNIT_FRACTION, tv.getComplexUnit());
+ tv.data = 21474865;
+ assertEquals(TypedValue.COMPLEX_UNIT_FRACTION_PARENT, tv.getComplexUnit());
+ }
}
diff --git a/tests/tests/view/Android.mk b/tests/tests/view/Android.mk
index ba4be93..d6100c8 100644
--- a/tests/tests/view/Android.mk
+++ b/tests/tests/view/Android.mk
@@ -29,11 +29,12 @@
LOCAL_JAVA_LIBRARIES := android.test.runner
LOCAL_STATIC_JAVA_LIBRARIES := \
+ android-support-test \
ctsdeviceutil \
ctstestrunner \
mockito-target \
- ub-uiautomator \
- android-support-test
+ platform-test-annotations \
+ ub-uiautomator
LOCAL_JNI_SHARED_LIBRARIES := libctsview_jni libnativehelper_compat_libc++
diff --git a/tests/tests/view/jni/Android.mk b/tests/tests/view/jni/Android.mk
index ac7b844..288e250 100644
--- a/tests/tests/view/jni/Android.mk
+++ b/tests/tests/view/jni/Android.mk
@@ -18,6 +18,8 @@
LOCAL_MODULE := libctsview_jni
+LOCAL_CFLAGS += -Werror
+
LOCAL_MODULE_TAGS := tests
LOCAL_SRC_FILES := \
diff --git a/tests/tests/view/jni/CtsViewJniOnLoad.cpp b/tests/tests/view/jni/CtsViewJniOnLoad.cpp
index 2c1e643..1a7ef3c 100644
--- a/tests/tests/view/jni/CtsViewJniOnLoad.cpp
+++ b/tests/tests/view/jni/CtsViewJniOnLoad.cpp
@@ -16,8 +16,8 @@
*/
#include <jni.h>
-#include <utils/Log.h>
#define LOG_TAG "CtsViewJniOnLoad"
+#include <utils/Log.h>
extern int register_android_view_cts_ChoreographerNativeTest(JNIEnv* env);
diff --git a/tests/tests/view/res/layout/textview_layout.xml b/tests/tests/view/res/layout/textview_layout.xml
index c09b93a..8b8418b 100644
--- a/tests/tests/view/res/layout/textview_layout.xml
+++ b/tests/tests/view/res/layout/textview_layout.xml
@@ -60,7 +60,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
- <android.view.cts.MockTextView
+ <TextView
android:id="@+id/mock_textview_left"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -70,7 +70,7 @@
android:gravity="left"
/>
- <android.view.cts.MockTextView
+ <TextView
android:id="@+id/mock_textview_right"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -80,7 +80,7 @@
android:gravity="right"
/>
- <android.view.cts.MockTextView
+ <TextView
android:id="@+id/mock_textview_center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
diff --git a/tests/tests/view/src/android/view/cts/AbsSavedStateTest.java b/tests/tests/view/src/android/view/cts/AbsSavedStateTest.java
index 3662e2c..e4165bb 100644
--- a/tests/tests/view/src/android/view/cts/AbsSavedStateTest.java
+++ b/tests/tests/view/src/android/view/cts/AbsSavedStateTest.java
@@ -18,83 +18,128 @@
import android.os.Parcel;
import android.os.Parcelable;
+import android.support.test.filters.SmallTest;
import android.test.InstrumentationTestCase;
import android.view.AbsSavedState;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+@SmallTest
public class AbsSavedStateTest extends InstrumentationTestCase {
- // constant for test of writeToParcel
- public static final int TEST_NUMBER = 1;
-
public void testConstructor() {
- MockParcelable superState = new MockParcelable();
- assertNotNull(superState);
- new MockAbsSavedState(superState);
+ try {
+ new AbsSavedStateImpl((Parcelable) null);
+ fail("Expected NullPointerException");
+ } catch (IllegalArgumentException e) {
+ // Expected.
+ }
+
+ try {
+ new AbsSavedStateImpl((Parcel) null);
+ fail("Expected NullPointerException");
+ } catch (NullPointerException e) {
+ // Expected.
+ }
+
+ try {
+ new AbsSavedStateImpl(null, null);
+ fail("Expected NullPointerException");
+ } catch (NullPointerException e) {
+ // Expected.
+ }
+
+ AbsSavedState superState = new AbsSavedStateImpl(Parcel.obtain());
+ assertNull(superState.getSuperState());
+
+ AbsSavedState s = new AbsSavedStateImpl(superState);
+ assertSame(superState, s.getSuperState());
Parcel source = Parcel.obtain();
- new MockAbsSavedState(source);
+ source.writeParcelable(superState, 0);
+ source.setDataPosition(0);
+ s = new AbsSavedStateImpl(source);
+ assertTrue(s.getSuperState() instanceof AbsSavedState);
- MockAbsSavedState savedState = new MockAbsSavedState(source);
- assertEquals(0, savedState.describeContents());
+ source = Parcel.obtain();
+ s = new AbsSavedStateImpl(source);
+ assertSame(AbsSavedState.EMPTY_STATE, s.getSuperState());
+
+ ClassLoader loader = AbsSavedState.class.getClassLoader();
+ source = Parcel.obtain();
+ source.writeParcelable(superState, 0);
+ source.setDataPosition(0);
+ s = new AbsSavedStateImpl(source, loader);
+ assertTrue(s.getSuperState() instanceof AbsSavedState);
+
+ source = Parcel.obtain();
+ s = new AbsSavedStateImpl(source, loader);
+ assertSame(AbsSavedState.EMPTY_STATE, s.getSuperState());
}
- public void testGetSuperState() {
- MockParcelable superState = new MockParcelable();
- assertNotNull(superState);
- MockAbsSavedState savedState = new MockAbsSavedState(superState);
+ public void testCreator() {
+ int size = 10;
+ AbsSavedState[] array = AbsSavedState.CREATOR.newArray(size);
+ assertNotNull(array);
+ assertEquals(size, array.length);
+ for (AbsSavedState state : array) {
+ assertNull(state);
+ }
- assertSame(superState, savedState.getSuperState());
+ AbsSavedState state = new AbsSavedStateImpl(AbsSavedState.EMPTY_STATE);
+ Parcel parcel = Parcel.obtain();
+ state.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ AbsSavedState unparceled = AbsSavedState.CREATOR.createFromParcel(parcel);
+ assertNotNull(unparceled);
+ assertEquals(AbsSavedState.EMPTY_STATE, unparceled.getSuperState());
+
+ AbsSavedState stateWithSuper = new AbsSavedStateImpl(state);
+ parcel = Parcel.obtain();
+ stateWithSuper.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ try {
+ AbsSavedState.CREATOR.createFromParcel(parcel);
+ fail("Expected IllegalStateException");
+ } catch (IllegalStateException e) {
+ // Expected.
+ }
}
public void testWriteToParcel() {
- MockParcelable superState = new MockParcelable();
- assertNotNull(superState);
- MockAbsSavedState savedState = new MockAbsSavedState(superState);
-
+ Parcelable superState = mock(Parcelable.class);
+ AbsSavedState savedState = new AbsSavedStateImpl(superState);
Parcel dest = Parcel.obtain();
int flags = 2;
savedState.writeToParcel(dest, flags);
-
- // we instantiate the writeToParcel of Parcalable
- // and give a return for test
- assertEquals(TEST_NUMBER, superState.writeToParcelRunSymbol());
- assertEquals(flags, superState.getFlags());
+ verify(superState).writeToParcel(eq(dest), eq(flags));
}
- static class MockAbsSavedState extends AbsSavedState {
-
- public MockAbsSavedState(Parcelable superState) {
+ private static class AbsSavedStateImpl extends AbsSavedState {
+ AbsSavedStateImpl(Parcelable superState) {
super(superState);
}
- public MockAbsSavedState(Parcel source) {
+ AbsSavedStateImpl(Parcel source) {
super(source);
}
- }
- static class MockParcelable implements Parcelable {
-
- // Test for writeToParcel
- private int mTest;
- private int mFlags;
-
- public int describeContents() {
- return 0;
+ AbsSavedStateImpl(Parcel source, ClassLoader loader) {
+ super(source, loader);
}
- // Instantiate writeToParcel
- public void writeToParcel(Parcel dest, int flags) {
- mTest = TEST_NUMBER;
- mFlags = flags;
- }
+ public static final Creator<AbsSavedStateImpl> CREATOR = new Creator<AbsSavedStateImpl>() {
+ @Override
+ public AbsSavedStateImpl createFromParcel(Parcel source) {
+ return new AbsSavedStateImpl(source);
+ }
- // For test of writeToParcel
- public int writeToParcelRunSymbol() {
- return mTest;
- }
-
- public int getFlags() {
- return mFlags;
- }
+ @Override
+ public AbsSavedStateImpl[] newArray(int size) {
+ return new AbsSavedStateImpl[size];
+ }
+ };
}
}
diff --git a/tests/tests/view/src/android/view/cts/MockTextView.java b/tests/tests/view/src/android/view/cts/MockTextView.java
deleted file mode 100644
index 0c73614..0000000
--- a/tests/tests/view/src/android/view/cts/MockTextView.java
+++ /dev/null
@@ -1,224 +0,0 @@
-/*
- * Copyright (C) 2011 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.view.cts;
-
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.os.Bundle;
-import android.text.method.MovementMethod;
-import android.util.AttributeSet;
-import android.view.ContextMenu;
-import android.view.KeyEvent;
-import android.widget.TextView;
-
-public class MockTextView extends TextView {
- private boolean mHasCalledOnCreateContextMenu;
- private boolean mHasCalledOnFocusChanged;
- private boolean mHasCalledOnMeasure;
- private boolean mHasCalledOnTextChanged;
- private boolean mHasCalledDrawableStateChanged;
- private boolean mHasCalledOnWindowFocusChanged;
- private boolean mHasCalledOnPrivateIMECommand;
- private boolean mHasCalledOnKeyMultiple;
-
- public MockTextView(Context context) {
- super(context);
- }
-
- public MockTextView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public MockTextView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- }
-
- public boolean hasCalledOnWindowFocusChanged() {
- return mHasCalledOnWindowFocusChanged;
- }
-
- public boolean hasCalledOnCreateContextMenu() {
- return mHasCalledOnCreateContextMenu;
- }
-
- public boolean hasCalledDrawableStateChanged() {
- return mHasCalledDrawableStateChanged;
- }
-
- public boolean hasCalledOnFocusChanged() {
- return mHasCalledOnFocusChanged;
- }
-
- public boolean hasCalledOnMeasure() {
- return mHasCalledOnMeasure;
- }
-
- public boolean hasCalledOnTextChanged() {
- return mHasCalledOnTextChanged;
- }
-
- public boolean hasCalledOnPrivateIMECommand() {
- return mHasCalledOnPrivateIMECommand;
- }
-
- public boolean hasCalledOnKeyMultiple(){
- return mHasCalledOnKeyMultiple;
- }
-
- public void reset() {
- mHasCalledOnWindowFocusChanged = false;
- mHasCalledDrawableStateChanged = false;
- mHasCalledOnCreateContextMenu = false;
- mHasCalledOnFocusChanged = false;
- mHasCalledOnMeasure = false;
- mHasCalledOnTextChanged = false;
- mHasCalledOnPrivateIMECommand = false;
- mHasCalledOnKeyMultiple = false;
- }
-
- public int computeHorizontalScrollRange() {
- return super.computeHorizontalScrollRange();
- }
-
- public int computeVerticalScrollRange() {
- return super.computeVerticalScrollRange();
- }
-
- @Override
- protected void drawableStateChanged() {
- super.drawableStateChanged();
- mHasCalledDrawableStateChanged = true;
- }
-
- public boolean getDefaultEditable() {
- return super.getDefaultEditable();
- }
-
- public MovementMethod getDefaultMovementMethod() {
- return super.getDefaultMovementMethod();
- }
-
- @Override
- protected void onCreateContextMenu(ContextMenu menu) {
- super.onCreateContextMenu(menu);
- mHasCalledOnCreateContextMenu = true;
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
- }
-
- @Override
- protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
- super.onFocusChanged(focused, direction, previouslyFocusedRect);
- mHasCalledOnFocusChanged = true;
- }
-
- @Override
- public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) {
- mHasCalledOnKeyMultiple = true;
- return super.onKeyMultiple(keyCode, repeatCount, event);
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- mHasCalledOnMeasure = true;
- }
-
- @Override
- protected void onTextChanged(CharSequence text, int start, int before, int after) {
- super.onTextChanged(text, start, before, after);
- mHasCalledOnTextChanged = true;
- }
-
- public boolean setFrame(int l, int t, int r, int b) {
- return super.setFrame(l, t, r, b);
- }
-
- @Override
- public void onWindowFocusChanged(boolean hasWindowFocus) {
- super.onWindowFocusChanged(hasWindowFocus);
- mHasCalledOnWindowFocusChanged = true;
- }
-
- public float getLeftFadingEdgeStrength() {
- return super.getLeftFadingEdgeStrength();
- }
-
- public float getRightFadingEdgeStrength() {
- return super.getRightFadingEdgeStrength();
- }
-
- @Override
- public boolean onPrivateIMECommand(String action, Bundle data) {
- mHasCalledOnPrivateIMECommand = true;
- return super.onPrivateIMECommand(action, data);
- }
-
- public int getFrameLeft() {
- return getLeft();
- }
-
- public int getFrameTop() {
- return getTop();
- }
-
- public int getFrameRight() {
- return getRight();
- }
-
- public int getFrameBottom() {
- return getBottom();
- }
-
- public int getBottomPaddingOffset() {
- return super.getBottomPaddingOffset();
- }
-
- public int getLeftPaddingOffset() {
- return super.getLeftPaddingOffset();
- }
-
- public int getRightPaddingOffset() {
- return super.getRightPaddingOffset();
- }
-
- public int getTopPaddingOffset() {
- return super.getTopPaddingOffset();
- }
-
- public boolean isPaddingOffsetRequired() {
- return super.isPaddingOffsetRequired();
- }
-
- public boolean verifyDrawable(Drawable who) {
- return super.verifyDrawable(who);
- }
-
- public int computeVerticalScrollExtent() {
- return super.computeVerticalScrollExtent();
- }
-}
diff --git a/tests/tests/view/src/android/view/cts/NumberPickerTest.java b/tests/tests/view/src/android/view/cts/NumberPickerTest.java
deleted file mode 100644
index 2e2733e..0000000
--- a/tests/tests/view/src/android/view/cts/NumberPickerTest.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2012 Google Inc.
- *
- * 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.view.cts;
-
-import android.test.AndroidTestCase;
-import android.widget.NumberPicker;
-
-public class NumberPickerTest extends AndroidTestCase {
-
- public void testSetDisplayedValues1() throws Exception {
- NumberPicker numberPicker = new NumberPicker(getContext());
- numberPicker.setMinValue(10);
- numberPicker.setMaxValue(12);
- numberPicker.setDisplayedValues(new String[]{"One", "Two", "Three"});
- }
-
- public void testSetDisplayedValues2() throws Exception {
- NumberPicker numberPicker = new NumberPicker(getContext());
- numberPicker.setMinValue(10);
- numberPicker.setMaxValue(14);
- try {
- numberPicker.setDisplayedValues(new String[]{"One", "Two", "Three"});
- fail("The size of the displayed values array must be equal to the selectable numbers!");
- } catch (Exception e) {
- /* expected */
- }
- }
-}
diff --git a/tests/tests/view/src/android/view/cts/TextureViewTest.java b/tests/tests/view/src/android/view/cts/TextureViewTest.java
index 12c689f..69eff68 100644
--- a/tests/tests/view/src/android/view/cts/TextureViewTest.java
+++ b/tests/tests/view/src/android/view/cts/TextureViewTest.java
@@ -17,6 +17,7 @@
package android.view.cts;
import android.app.Instrumentation;
+import android.cts.util.WidgetTestUtils;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.Point;
@@ -24,7 +25,6 @@
import android.support.test.filters.MediumTest;
import android.support.test.rule.ActivityTestRule;
import android.view.View;
-import android.view.cts.util.ViewTestUtils;
import org.junit.Before;
import org.junit.Rule;
@@ -74,8 +74,8 @@
updatedCount = mActivity.waitForSurfaceUpdateCount(1);
assertEquals(1, updatedCount);
assertEquals(Color.WHITE, getPixel(center));
- ViewTestUtils.runOnMainAndDrawSync(mInstrumentation, mActivity,
- () -> mActivity.removeCover());
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation,
+ mActivity.findViewById(android.R.id.content), () -> mActivity.removeCover());
int color;
color = waitForChange(center, Color.WHITE);
diff --git a/tests/tests/view/src/android/view/cts/ViewOutlineProviderTest.java b/tests/tests/view/src/android/view/cts/ViewOutlineProviderTest.java
new file mode 100644
index 0000000..bf21b30
--- /dev/null
+++ b/tests/tests/view/src/android/view/cts/ViewOutlineProviderTest.java
@@ -0,0 +1,126 @@
+/*
+ * 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.view.cts;
+
+import android.annotation.NonNull;
+import android.app.Activity;
+import android.graphics.Color;
+import android.graphics.Outline;
+import android.graphics.Rect;
+import android.graphics.drawable.ColorDrawable;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.view.View;
+import android.view.ViewOutlineProvider;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.*;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ViewOutlineProviderTest {
+ @Rule
+ public ActivityTestRule mActivityTestRule = new ActivityTestRule<>(MockActivity.class);
+
+ Activity getActivity() { return mActivityTestRule.getActivity(); }
+
+ @Test
+ public void testBackground() {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ View view = new View(mActivityTestRule.getActivity());
+ view.setLeftTopRightBottom(100, 200, 300, 400);
+
+
+ Outline outline = new Outline();
+ outline.setAlpha(1.0f);
+ Rect queryRect = new Rect();
+
+
+ // No background - outline is 0 alpha, width x height rect
+ ViewOutlineProvider.BACKGROUND.getOutline(view, outline);
+ outline.getRect(queryRect);
+ assertEquals(new Rect(0, 0, 200, 200), queryRect);
+ assertEquals(0f, outline.getAlpha(), 0f);
+
+
+ // With background - outline is passed directly from background
+ view.setBackground(new ColorDrawable(Color.BLACK) {
+ @Override
+ public void getOutline(@NonNull Outline outline) {
+ outline.setRect(1, 2, 3, 4);
+ outline.setAlpha(0.123f);
+ }
+ });
+ ViewOutlineProvider.BACKGROUND.getOutline(view, outline);
+ outline.getRect(queryRect);
+ assertEquals(new Rect(1, 2, 3, 4), queryRect);
+ assertEquals(0.123f, outline.getAlpha(), 0f);
+ });
+ }
+
+
+ @Test
+ public void testBounds() {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ View view = new View(mActivityTestRule.getActivity());
+
+ Outline outline = new Outline();
+ Rect queryRect = new Rect();
+ outline.setAlpha(0.123f);
+
+ view.setLeftTopRightBottom(1, 2, 3, 4);
+ ViewOutlineProvider.BOUNDS.getOutline(view, outline);
+ outline.getRect(queryRect);
+ assertEquals(new Rect(0, 0, 2, 2), queryRect); // local width/height
+ assertEquals(0.123f, outline.getAlpha(), 0f); // alpha not changed
+
+ view.setLeftTopRightBottom(100, 200, 300, 400);
+ ViewOutlineProvider.BOUNDS.getOutline(view, outline);
+ outline.getRect(queryRect);
+ assertEquals(new Rect(0, 0, 200, 200), queryRect); // local width/height
+ assertEquals(0.123f, outline.getAlpha(), 0f); // alpha not changed
+ });
+ }
+
+ @Test
+ public void testPaddedBounds() {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ View view = new View(mActivityTestRule.getActivity());
+
+ Outline outline = new Outline();
+ Rect queryRect = new Rect();
+ outline.setAlpha(0.123f);
+
+ view.setLeftTopRightBottom(10, 20, 30, 40);
+ view.setPadding(0, 0, 0, 0);
+ ViewOutlineProvider.PADDED_BOUNDS.getOutline(view, outline);
+ outline.getRect(queryRect);
+ assertEquals(new Rect(0, 0, 20, 20), queryRect); // local width/height
+ assertEquals(0.123f, outline.getAlpha(), 0f); // alpha not changed
+
+ view.setPadding(5, 5, 5, 5);
+ ViewOutlineProvider.PADDED_BOUNDS.getOutline(view, outline);
+ outline.getRect(queryRect);
+ assertEquals(new Rect(5, 5, 15, 15), queryRect); // local width/height, inset by 5
+ assertEquals(0.123f, outline.getAlpha(), 0f); // alpha not changed
+ });
+ }
+}
diff --git a/tests/tests/view/src/android/view/cts/ViewTest.java b/tests/tests/view/src/android/view/cts/ViewTest.java
index 7d11c68..44e8755 100644
--- a/tests/tests/view/src/android/view/cts/ViewTest.java
+++ b/tests/tests/view/src/android/view/cts/ViewTest.java
@@ -21,10 +21,12 @@
import android.graphics.BitmapFactory;
import com.android.internal.view.menu.ContextMenuBuilder;
+import android.content.ClipData;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
+import android.cts.util.CtsTouchUtils;
import android.cts.util.PollingCheck;
import android.graphics.Bitmap;
import android.graphics.Canvas;
@@ -42,7 +44,6 @@
import android.os.SystemClock;
import android.os.Vibrator;
import android.test.ActivityInstrumentationTestCase2;
-import android.test.TouchUtils;
import android.test.UiThreadTest;
import android.text.format.DateUtils;
import android.util.AttributeSet;
@@ -171,7 +172,7 @@
constructor.setAccessible(true);
constructor.newInstance(args);
} catch (Throwable t) {
- sCtorException = new Pair<Class<?>, Throwable>(clazz, t);
+ sCtorException = new Pair<>(clazz, t);
break;
}
}
@@ -259,12 +260,7 @@
final MockView view = (MockView) mActivity.findViewById(R.id.mock_view);
// check whether it has started
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- view.startAnimation(animation);
- }
- });
+ runTestOnUiThread(() -> view.startAnimation(animation));
getInstrumentation().waitForIdleSync();
new PollingCheck() {
@@ -328,13 +324,8 @@
final Button button = new Button(mActivity);
final int WRAP_CONTENT = ViewGroup.LayoutParams.WRAP_CONTENT;
final int btnHeight = view.getHeight()/3;
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- mActivity.addContentView(button,
- new LinearLayout.LayoutParams(WRAP_CONTENT, btnHeight));
- }
- });
+ runTestOnUiThread(() -> mActivity.addContentView(button,
+ new LinearLayout.LayoutParams(WRAP_CONTENT, btnHeight)));
getInstrumentation().waitForIdleSync();
button.getHitRect(rect);
MockTouchDelegate delegate = new MockTouchDelegate(rect, button);
@@ -344,7 +335,7 @@
view.setTouchDelegate(delegate);
assertSame(delegate, view.getTouchDelegate());
assertFalse(delegate.hasCalledOnTouchEvent());
- TouchUtils.clickView(this, view);
+ CtsTouchUtils.emulateTapOnViewCenter(getInstrumentation(), view);
assertTrue(view.hasCalledOnTouchEvent());
assertTrue(delegate.hasCalledOnTouchEvent());
@@ -484,12 +475,7 @@
final MockView mockView = new MockView(mActivity);
assertEquals(-1, mockView.getOldWOnSizeChanged());
assertEquals(-1, mockView.getOldHOnSizeChanged());
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- viewGroup.addView(mockView);
- }
- });
+ runTestOnUiThread(() -> viewGroup.addView(mockView));
getInstrumentation().waitForIdleSync();
assertTrue(mockView.hasCalledOnSizeChanged());
assertEquals(0, mockView.getOldWOnSizeChanged());
@@ -503,12 +489,7 @@
int oldw = view.getWidth();
int oldh = view.getHeight();
final LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(200, 100);
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- view.setLayoutParams(layoutParams);
- }
- });
+ runTestOnUiThread(() -> view.setLayoutParams(layoutParams));
getInstrumentation().waitForIdleSync();
assertTrue(view.hasCalledOnSizeChanged());
assertEquals(oldw, view.getOldWOnSizeChanged());
@@ -578,12 +559,7 @@
view.reset();
assertFalse(view.hasCalledOnLayout());
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- view.requestLayout();
- }
- });
+ runTestOnUiThread(view::requestLayout);
getInstrumentation().waitForIdleSync();
assertTrue(view.hasCalledOnLayout());
}
@@ -727,23 +703,13 @@
// width is 0
final LinearLayout.LayoutParams layoutParams1 = new LinearLayout.LayoutParams(0, 300);
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- view.setLayoutParams(layoutParams1);
- }
- });
+ runTestOnUiThread(() -> view.setLayoutParams(layoutParams1));
getInstrumentation().waitForIdleSync();
assertFalse(view.getGlobalVisibleRect(rect, point));
// height is -10
final LinearLayout.LayoutParams layoutParams2 = new LinearLayout.LayoutParams(200, -10);
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- view.setLayoutParams(layoutParams2);
- }
- });
+ runTestOnUiThread(() -> view.setLayoutParams(layoutParams2));
getInstrumentation().waitForIdleSync();
assertFalse(view.getGlobalVisibleRect(rect, point));
@@ -753,12 +719,7 @@
final LinearLayout.LayoutParams layoutParams3 =
new LinearLayout.LayoutParams(halfWidth, halfHeight);
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- view.setLayoutParams(layoutParams3);
- }
- });
+ runTestOnUiThread(() -> view.setLayoutParams(layoutParams3));
getInstrumentation().waitForIdleSync();
assertTrue(view.getGlobalVisibleRect(rect, point));
assertEquals(rcParent.left, rect.left);
@@ -784,23 +745,13 @@
// width is 0
final LinearLayout.LayoutParams layoutParams1 = new LinearLayout.LayoutParams(0, 300);
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- view.setLayoutParams(layoutParams1);
- }
- });
+ runTestOnUiThread(() -> view.setLayoutParams(layoutParams1));
getInstrumentation().waitForIdleSync();
assertFalse(view.getGlobalVisibleRect(rect));
// height is -10
final LinearLayout.LayoutParams layoutParams2 = new LinearLayout.LayoutParams(200, -10);
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- view.setLayoutParams(layoutParams2);
- }
- });
+ runTestOnUiThread(() -> view.setLayoutParams(layoutParams2));
getInstrumentation().waitForIdleSync();
assertFalse(view.getGlobalVisibleRect(rect));
@@ -810,12 +761,7 @@
final LinearLayout.LayoutParams layoutParams3 =
new LinearLayout.LayoutParams(halfWidth, halfHeight);
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- view.setLayoutParams(layoutParams3);
- }
- });
+ runTestOnUiThread(() -> view.setLayoutParams(layoutParams3));
getInstrumentation().waitForIdleSync();
assertTrue(view.getGlobalVisibleRect(rect));
assertEquals(rcParent.left, rect.left);
@@ -831,23 +777,13 @@
assertEquals(view.getWidth(), view.computeHorizontalScrollRange());
assertEquals(view.getWidth(), view.computeHorizontalScrollExtent());
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- view.scrollTo(12, 0);
- }
- });
+ runTestOnUiThread(() -> view.scrollTo(12, 0));
getInstrumentation().waitForIdleSync();
assertEquals(12, view.computeHorizontalScrollOffset());
assertEquals(view.getWidth(), view.computeHorizontalScrollRange());
assertEquals(view.getWidth(), view.computeHorizontalScrollExtent());
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- view.scrollBy(12, 0);
- }
- });
+ runTestOnUiThread(() -> view.scrollBy(12, 0));
getInstrumentation().waitForIdleSync();
assertEquals(24, view.computeHorizontalScrollOffset());
assertEquals(view.getWidth(), view.computeHorizontalScrollRange());
@@ -855,12 +791,7 @@
int newWidth = 200;
final LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(newWidth, 100);
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- view.setLayoutParams(layoutParams);
- }
- });
+ runTestOnUiThread(() -> view.setLayoutParams(layoutParams));
getInstrumentation().waitForIdleSync();
assertEquals(24, view.computeHorizontalScrollOffset());
assertEquals(newWidth, view.getWidth());
@@ -876,24 +807,14 @@
assertEquals(view.getHeight(), view.computeVerticalScrollExtent());
final int scrollToY = 34;
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- view.scrollTo(0, scrollToY);
- }
- });
+ runTestOnUiThread(() -> view.scrollTo(0, scrollToY));
getInstrumentation().waitForIdleSync();
assertEquals(scrollToY, view.computeVerticalScrollOffset());
assertEquals(view.getHeight(), view.computeVerticalScrollRange());
assertEquals(view.getHeight(), view.computeVerticalScrollExtent());
final int scrollByY = 200;
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- view.scrollBy(0, scrollByY);
- }
- });
+ runTestOnUiThread(() -> view.scrollBy(0, scrollByY));
getInstrumentation().waitForIdleSync();
assertEquals(scrollToY + scrollByY, view.computeVerticalScrollOffset());
assertEquals(view.getHeight(), view.computeVerticalScrollRange());
@@ -901,12 +822,7 @@
int newHeight = 333;
final LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(200, newHeight);
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- view.setLayoutParams(layoutParams);
- }
- });
+ runTestOnUiThread(() -> view.setLayoutParams(layoutParams));
getInstrumentation().waitForIdleSync();
assertEquals(scrollToY + scrollByY, view.computeVerticalScrollOffset());
assertEquals(newHeight, view.getHeight());
@@ -922,24 +838,14 @@
assertEquals(0f, view.getTopFadingEdgeStrength());
assertEquals(0f, view.getBottomFadingEdgeStrength());
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- view.scrollTo(10, 10);
- }
- });
+ runTestOnUiThread(() -> view.scrollTo(10, 10));
getInstrumentation().waitForIdleSync();
assertEquals(1f, view.getLeftFadingEdgeStrength());
assertEquals(0f, view.getRightFadingEdgeStrength());
assertEquals(1f, view.getTopFadingEdgeStrength());
assertEquals(0f, view.getBottomFadingEdgeStrength());
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- view.scrollTo(-10, -10);
- }
- });
+ runTestOnUiThread(() -> view.scrollTo(-10, -10));
getInstrumentation().waitForIdleSync();
assertEquals(0f, view.getLeftFadingEdgeStrength());
assertEquals(1f, view.getRightFadingEdgeStrength());
@@ -1143,7 +1049,7 @@
}
private void focusableInTouchModeTest(View view, boolean inTouchMode) {
- ArrayList<View> views = new ArrayList<View>();
+ ArrayList<View> views = new ArrayList<>();
view.setFocusableInTouchMode(false);
view.setFocusable(true);
@@ -1715,12 +1621,7 @@
assertEquals(200, view.getMeasuredHeight());
view.reset();
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- view.requestLayout();
- }
- });
+ runTestOnUiThread(view::requestLayout);
getInstrumentation().waitForIdleSync();
assertTrue(view.hasCalledOnMeasure());
assertEquals(100, view.getMeasuredWidth());
@@ -1728,12 +1629,7 @@
view.reset();
final LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(200, 100);
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- view.setLayoutParams(layoutParams);
- }
- });
+ runTestOnUiThread(() -> view.setLayoutParams(layoutParams));
getInstrumentation().waitForIdleSync();
assertTrue(view.hasCalledOnMeasure());
assertEquals(200, view.getMeasuredWidth());
@@ -1786,13 +1682,8 @@
public void testScheduleDrawable() {
View view = new View(mActivity);
Drawable drawable = new StateListDrawable();
- Runnable what = new Runnable() {
- @Override
- public void run() {
- // do nothing
- }
- };
-
+ // Does nothing.
+ Runnable what = () -> {};
// mAttachInfo is null
view.scheduleDrawable(drawable, what, 1000);
@@ -1814,11 +1705,8 @@
public void testUnscheduleDrawable() {
View view = new View(mActivity);
Drawable drawable = new StateListDrawable();
- Runnable what = new Runnable() {
- @Override
- public void run() {
- // do nothing
- }
+ Runnable what = () -> {
+ // do nothing
};
// mAttachInfo is null
@@ -2153,12 +2041,9 @@
public void testOnKeyShortcut() throws Throwable {
final MockView view = (MockView) mActivity.findViewById(R.id.mock_view);
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- view.setFocusable(true);
- view.requestFocus();
- }
+ runTestOnUiThread(() -> {
+ view.setFocusable(true);
+ view.requestFocus();
});
getInstrumentation().waitForIdleSync();
assertTrue(view.isFocused());
@@ -2172,12 +2057,7 @@
public void testOnKeyMultiple() throws Throwable {
final MockView view = (MockView) mActivity.findViewById(R.id.mock_view);
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- view.setFocusable(true);
- }
- });
+ runTestOnUiThread(() -> view.setFocusable(true));
assertFalse(view.hasCalledOnKeyMultiple());
view.dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_MULTIPLE, KeyEvent.KEYCODE_ENTER));
@@ -2202,13 +2082,10 @@
public void testOnTrackballEvent() throws Throwable {
final MockView view = (MockView) mActivity.findViewById(R.id.mock_view);
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- view.setEnabled(true);
- view.setFocusable(true);
- view.requestFocus();
- }
+ runTestOnUiThread(() -> {
+ view.setEnabled(true);
+ view.setFocusable(true);
+ view.requestFocus();
});
getInstrumentation().waitForIdleSync();
@@ -2254,12 +2131,9 @@
public void testDispatchUnhandledMove() throws Throwable {
final MockView view = (MockView) mActivity.findViewById(R.id.mock_view);
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- view.setFocusable(true);
- view.requestFocus();
- }
+ runTestOnUiThread(() -> {
+ view.setFocusable(true);
+ view.requestFocus();
});
getInstrumentation().waitForIdleSync();
@@ -2273,44 +2147,24 @@
final MockView mockView = new MockView(mActivity);
final ViewGroup viewGroup = (ViewGroup) mActivity.findViewById(R.id.viewlayout_root);
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- viewGroup.addView(mockView);
- }
- });
+ runTestOnUiThread(() -> viewGroup.addView(mockView));
getInstrumentation().waitForIdleSync();
assertTrue(mockView.hasCalledOnWindowVisibilityChanged());
mockView.reset();
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- getActivity().setVisible(false);
- }
- });
+ runTestOnUiThread(() -> getActivity().setVisible(false));
getInstrumentation().waitForIdleSync();
assertTrue(mockView.hasCalledDispatchWindowVisibilityChanged());
assertTrue(mockView.hasCalledOnWindowVisibilityChanged());
mockView.reset();
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- getActivity().setVisible(true);
- }
- });
+ runTestOnUiThread(() -> getActivity().setVisible(true));
getInstrumentation().waitForIdleSync();
assertTrue(mockView.hasCalledDispatchWindowVisibilityChanged());
assertTrue(mockView.hasCalledOnWindowVisibilityChanged());
mockView.reset();
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- viewGroup.removeView(mockView);
- }
- });
+ runTestOnUiThread(() -> viewGroup.removeView(mockView));
getInstrumentation().waitForIdleSync();
assertTrue(mockView.hasCalledOnWindowVisibilityChanged());
}
@@ -2326,22 +2180,12 @@
assertEquals(200, rect.bottom);
final LinearLayout.LayoutParams layoutParams1 = new LinearLayout.LayoutParams(0, 300);
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- view.setLayoutParams(layoutParams1);
- }
- });
+ runTestOnUiThread(() -> view.setLayoutParams(layoutParams1));
getInstrumentation().waitForIdleSync();
assertFalse(view.getLocalVisibleRect(rect));
final LinearLayout.LayoutParams layoutParams2 = new LinearLayout.LayoutParams(200, -10);
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- view.setLayoutParams(layoutParams2);
- }
- });
+ runTestOnUiThread(() -> view.setLayoutParams(layoutParams2));
getInstrumentation().waitForIdleSync();
assertFalse(view.getLocalVisibleRect(rect));
@@ -2351,12 +2195,9 @@
final LinearLayout.LayoutParams layoutParams3 =
new LinearLayout.LayoutParams(halfWidth, halfHeight);
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- view.setLayoutParams(layoutParams3);
- view.scrollTo(20, -30);
- }
+ runTestOnUiThread(() -> {
+ view.setLayoutParams(layoutParams3);
+ view.scrollTo(20, -30);
});
getInstrumentation().waitForIdleSync();
assertTrue(view.getLocalVisibleRect(rect));
@@ -2454,12 +2295,9 @@
public void testOnKeyDownOrUp() throws Throwable {
final MockView view = (MockView) mActivity.findViewById(R.id.mock_view);
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- view.setFocusable(true);
- view.requestFocus();
- }
+ runTestOnUiThread(() -> {
+ view.setFocusable(true);
+ view.requestFocus();
});
getInstrumentation().waitForIdleSync();
assertTrue(view.isFocused());
@@ -2481,12 +2319,9 @@
assertFalse(view.isPressed());
assertTrue(view.hasCalledOnKeyDown());
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- view.setEnabled(true);
- view.setClickable(true);
- }
+ runTestOnUiThread(() -> {
+ view.setEnabled(true);
+ view.setClickable(true);
});
view.reset();
OnClickListenerImpl listener = new OnClickListenerImpl();
@@ -2556,33 +2391,24 @@
// Add the child view to the parent, test that parent has same width as child
// Remove the child view from the parent, test that parent is 0xparentHeight
final CountDownLatch countDownLatch1 = new CountDownLatch(1);
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- viewGroup.removeAllViews();
- viewGroup.addView(parent);
- checkBounds(viewGroup, parent, countDownLatch1, 0, 0, 0, parentHeight);
- }
+ runTestOnUiThread(() -> {
+ viewGroup.removeAllViews();
+ viewGroup.addView(parent);
+ checkBounds(viewGroup, parent, countDownLatch1, 0, 0, 0, parentHeight);
});
countDownLatch1.await(500, TimeUnit.MILLISECONDS);
final CountDownLatch countDownLatch2 = new CountDownLatch(1);
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- parent.addView(child);
- checkBounds(viewGroup, parent, countDownLatch2, 0, 0, childWidth, parentHeight);
- }
+ runTestOnUiThread(() -> {
+ parent.addView(child);
+ checkBounds(viewGroup, parent, countDownLatch2, 0, 0, childWidth, parentHeight);
});
countDownLatch2.await(500, TimeUnit.MILLISECONDS);
final CountDownLatch countDownLatch3 = new CountDownLatch(1);
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- parent.removeView(child);
- checkBounds(viewGroup, parent, countDownLatch3, 0, 0, 0, parentHeight);
- }
+ runTestOnUiThread(() -> {
+ parent.removeView(child);
+ checkBounds(viewGroup, parent, countDownLatch3, 0, 0, 0, parentHeight);
});
countDownLatch3.await(500, TimeUnit.MILLISECONDS);
}
@@ -2694,12 +2520,7 @@
assertTrue(view.hasCalledOnDraw());
view.reset();
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- view.invalidate();
- }
- });
+ runTestOnUiThread(view::invalidate);
getInstrumentation().waitForIdleSync();
new PollingCheck() {
@Override
@@ -2709,12 +2530,9 @@
}.run();
view.reset();
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- view.setVisibility(View.INVISIBLE);
- view.invalidate();
- }
+ runTestOnUiThread(() -> {
+ view.setVisibility(View.INVISIBLE);
+ view.invalidate();
});
getInstrumentation().waitForIdleSync();
assertFalse(view.hasCalledOnDraw());
@@ -2733,12 +2551,7 @@
view.reset();
final Rect dirty = new Rect(view.getLeft() + 1, view.getTop() + 1,
view.getLeft() + view.getWidth() / 2, view.getTop() + view.getHeight() / 2);
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- view.invalidate(dirty);
- }
- });
+ runTestOnUiThread(() -> view.invalidate(dirty));
getInstrumentation().waitForIdleSync();
new PollingCheck() {
@Override
@@ -2748,12 +2561,9 @@
}.run();
view.reset();
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- view.setVisibility(View.INVISIBLE);
- view.invalidate(dirty);
- }
+ runTestOnUiThread(() -> {
+ view.setVisibility(View.INVISIBLE);
+ view.invalidate(dirty);
});
getInstrumentation().waitForIdleSync();
assertFalse(view.hasCalledOnDraw());
@@ -2766,12 +2576,7 @@
view.reset();
final Rect dirty = new Rect(view.getLeft() + 1, view.getTop() + 1,
view.getLeft() + view.getWidth() / 2, view.getTop() + view.getHeight() / 2);
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- view.invalidate(dirty.left, dirty.top, dirty.right, dirty.bottom);
- }
- });
+ runTestOnUiThread(() -> view.invalidate(dirty.left, dirty.top, dirty.right, dirty.bottom));
getInstrumentation().waitForIdleSync();
new PollingCheck() {
@Override
@@ -2781,12 +2586,9 @@
}.run();
view.reset();
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- view.setVisibility(View.INVISIBLE);
- view.invalidate(dirty.left, dirty.top, dirty.right, dirty.bottom);
- }
+ runTestOnUiThread(() -> {
+ view.setVisibility(View.INVISIBLE);
+ view.invalidate(dirty.left, dirty.top, dirty.right, dirty.bottom);
});
getInstrumentation().waitForIdleSync();
assertFalse(view.hasCalledOnDraw());
@@ -2798,12 +2600,9 @@
final Drawable d2 = mResources.getDrawable(R.drawable.pass);
view.reset();
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- view.setBackgroundDrawable(d1);
- view.invalidateDrawable(d1);
- }
+ runTestOnUiThread(() -> {
+ view.setBackgroundDrawable(d1);
+ view.invalidateDrawable(d1);
});
getInstrumentation().waitForIdleSync();
new PollingCheck() {
@@ -2814,12 +2613,7 @@
}.run();
view.reset();
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- view.invalidateDrawable(d2);
- }
- });
+ runTestOnUiThread(() -> view.invalidateDrawable(d2));
getInstrumentation().waitForIdleSync();
assertFalse(view.hasCalledOnDraw());
@@ -2921,12 +2715,7 @@
public void testDraw() throws Throwable {
final MockView view = (MockView) mActivity.findViewById(R.id.mock_view);
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- view.requestLayout();
- }
- });
+ runTestOnUiThread(view::requestLayout);
getInstrumentation().waitForIdleSync();
assertTrue(view.hasCalledOnDraw());
@@ -3103,16 +2892,13 @@
assertFalse(view.isClickable());
assertFalse(view.isLongClickable());
- TouchUtils.clickView(this, view);
+ CtsTouchUtils.emulateTapOnViewCenter(getInstrumentation(), view);
assertTrue(view.hasCalledOnTouchEvent());
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- view.setEnabled(true);
- view.setClickable(true);
- view.setLongClickable(true);
- }
+ runTestOnUiThread(() -> {
+ view.setEnabled(true);
+ view.setClickable(true);
+ view.setLongClickable(true);
});
getInstrumentation().waitForIdleSync();
assertTrue(view.isEnabled());
@@ -3396,45 +3182,34 @@
final InputMethodManager imm = (InputMethodManager)getActivity().getSystemService(
Context.INPUT_METHOD_SERVICE);
final LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(300, 500);
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- mockView.setBackgroundDrawable(d);
- mockView.setHorizontalFadingEdgeEnabled(true);
- mockView.setVerticalFadingEdgeEnabled(true);
- mockView.setLayoutParams(layoutParams);
- scrollView.setLayoutParams(layoutParams);
+ runTestOnUiThread(() -> {
+ mockView.setBackgroundDrawable(d);
+ mockView.setHorizontalFadingEdgeEnabled(true);
+ mockView.setVerticalFadingEdgeEnabled(true);
+ mockView.setLayoutParams(layoutParams);
+ scrollView.setLayoutParams(layoutParams);
- mockView.setFocusable(true);
- mockView.requestFocus();
- mockView.setScrollContainer(true);
- scrollView.setScrollContainer(false);
- imm.showSoftInput(mockView, 0);
- }
+ mockView.setFocusable(true);
+ mockView.requestFocus();
+ mockView.setScrollContainer(true);
+ scrollView.setScrollContainer(false);
+ imm.showSoftInput(mockView, 0);
});
getInstrumentation().waitForIdleSync();
// FIXME: why the size of view doesn't change?
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- imm.hideSoftInputFromInputMethod(mockView.getWindowToken(), 0);
- }
- });
+ runTestOnUiThread(() -> imm.hideSoftInputFromInputMethod(mockView.getWindowToken(), 0));
getInstrumentation().waitForIdleSync();
}
public void testTouchMode() throws Throwable {
final MockView mockView = (MockView) mActivity.findViewById(R.id.mock_view);
final View fitWindowsView = mActivity.findViewById(R.id.fit_windows);
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- mockView.setFocusableInTouchMode(true);
- fitWindowsView.setFocusable(true);
- fitWindowsView.requestFocus();
- }
+ runTestOnUiThread(() -> {
+ mockView.setFocusableInTouchMode(true);
+ fitWindowsView.setFocusable(true);
+ fitWindowsView.requestFocus();
});
getInstrumentation().waitForIdleSync();
assertTrue(mockView.isFocusableInTouchMode());
@@ -3446,23 +3221,13 @@
assertFalse(mockView.isInTouchMode());
assertFalse(fitWindowsView.isInTouchMode());
- TouchUtils.tapView(this, mockView);
+ CtsTouchUtils.emulateTapOnViewCenter(getInstrumentation(), mockView);
assertFalse(fitWindowsView.isFocused());
assertFalse(mockView.isFocused());
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- mockView.requestFocus();
- }
- });
+ runTestOnUiThread(mockView::requestFocus);
getInstrumentation().waitForIdleSync();
assertTrue(mockView.isFocused());
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- fitWindowsView.requestFocus();
- }
- });
+ runTestOnUiThread(fitWindowsView::requestFocus);
getInstrumentation().waitForIdleSync();
assertFalse(fitWindowsView.isFocused());
assertTrue(mockView.isInTouchMode());
@@ -3472,12 +3237,7 @@
getInstrumentation().sendKeySync(keyEvent);
assertTrue(mockView.isFocused());
assertFalse(fitWindowsView.isFocused());
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- fitWindowsView.requestFocus();
- }
- });
+ runTestOnUiThread(fitWindowsView::requestFocus);
getInstrumentation().waitForIdleSync();
assertFalse(mockView.isFocused());
assertTrue(fitWindowsView.isFocused());
@@ -3630,12 +3390,7 @@
assertFalse(view.hasCalledOnStartTemporaryDetach());
assertFalse(view.hasCalledOnFinishTemporaryDetach());
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- view.dispatchStartTemporaryDetach();
- }
- });
+ runTestOnUiThread(view::dispatchStartTemporaryDetach);
getInstrumentation().waitForIdleSync();
assertTrue(view.isTemporarilyDetached());
@@ -3644,12 +3399,7 @@
assertTrue(view.hasCalledOnStartTemporaryDetach());
assertFalse(view.hasCalledOnFinishTemporaryDetach());
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- view.dispatchFinishTemporaryDetach();
- }
- });
+ runTestOnUiThread(view::dispatchFinishTemporaryDetach);
getInstrumentation().waitForIdleSync();
assertFalse(view.isTemporarilyDetached());
@@ -3662,12 +3412,9 @@
public void testKeyPreIme() throws Throwable {
final MockView view = (MockView) mActivity.findViewById(R.id.mock_view);
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- view.setFocusable(true);
- view.requestFocus();
- }
+ runTestOnUiThread(() -> {
+ view.setFocusable(true);
+ view.requestFocus();
});
getInstrumentation().waitForIdleSync();
@@ -3704,22 +3451,14 @@
final ViewGroup viewGroup = (ViewGroup) mActivity.findViewById(R.id.viewlayout_root);
final MockEditText editText = new MockEditText(mActivity);
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- viewGroup.addView(editText);
- editText.requestFocus();
- }
+ runTestOnUiThread(() -> {
+ viewGroup.addView(editText);
+ editText.requestFocus();
});
getInstrumentation().waitForIdleSync();
assertTrue(editText.isFocused());
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- imm.showSoftInput(editText, 0);
- }
- });
+ runTestOnUiThread(() -> imm.showSoftInput(editText, 0));
getInstrumentation().waitForIdleSync();
new PollingCheck(TIMEOUT_DELTA) {
@@ -3731,14 +3470,11 @@
assertTrue(editText.hasCalledOnCheckIsTextEditor());
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- assertTrue(imm.isActive(editText));
- assertFalse(editText.hasCalledCheckInputConnectionProxy());
- imm.isActive(view);
- assertTrue(editText.hasCalledCheckInputConnectionProxy());
- }
+ runTestOnUiThread(() -> {
+ assertTrue(imm.isActive(editText));
+ assertFalse(editText.hasCalledCheckInputConnectionProxy());
+ imm.isActive(view);
+ assertTrue(editText.hasCalledCheckInputConnectionProxy());
});
}
@@ -3871,14 +3607,11 @@
assertTrue(mv.hasCalledOnVisibilityAggregated());
assertTrue(mv.getLastAggregatedVisibility());
- final Runnable reset = new Runnable() {
- @Override
- public void run() {
- grandparent.setVisibility(View.VISIBLE);
- parent.setVisibility(View.VISIBLE);
- mv.setVisibility(View.VISIBLE);
- mv.reset();
- }
+ final Runnable reset = () -> {
+ grandparent.setVisibility(View.VISIBLE);
+ parent.setVisibility(View.VISIBLE);
+ mv.setVisibility(View.VISIBLE);
+ mv.reset();
};
runTestOnUiThread(reset);
@@ -3896,40 +3629,29 @@
assertFalse(mv.getLastAggregatedVisibility());
runTestOnUiThread(reset);
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- grandparent.setVisibility(View.GONE);
- parent.setVisibility(View.GONE);
- mv.setVisibility(View.VISIBLE);
+ runTestOnUiThread(() -> {
+ grandparent.setVisibility(View.GONE);
+ parent.setVisibility(View.GONE);
+ mv.setVisibility(View.VISIBLE);
- grandparent.setVisibility(View.VISIBLE);
- }
+ grandparent.setVisibility(View.VISIBLE);
});
assertTrue(mv.hasCalledOnVisibilityAggregated());
assertFalse(mv.getLastAggregatedVisibility());
runTestOnUiThread(reset);
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- grandparent.setVisibility(View.GONE);
- parent.setVisibility(View.INVISIBLE);
+ runTestOnUiThread(() -> {
+ grandparent.setVisibility(View.GONE);
+ parent.setVisibility(View.INVISIBLE);
- grandparent.setVisibility(View.VISIBLE);
- }
+ grandparent.setVisibility(View.VISIBLE);
});
assertTrue(mv.hasCalledOnVisibilityAggregated());
assertFalse(mv.getLastAggregatedVisibility());
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- parent.setVisibility(View.VISIBLE);
- }
- });
+ runTestOnUiThread(() -> parent.setVisibility(View.VISIBLE));
assertTrue(mv.getLastAggregatedVisibility());
}
@@ -3967,13 +3689,42 @@
assertTrue(overridingView.hasOverlappingRendering());
}
+ public void testUpdateDragShadow() {
+ View view = mActivity.findViewById(R.id.fit_windows);
+ assertTrue(view.isAttachedToWindow());
+
+ View.DragShadowBuilder shadowBuilder = mock(View.DragShadowBuilder.class);
+ view.startDragAndDrop(ClipData.newPlainText("", ""), shadowBuilder, view, 0);
+ reset(shadowBuilder);
+
+ view.updateDragShadow(shadowBuilder);
+ // TODO: Verify with the canvas from the drag surface instead.
+ verify(shadowBuilder).onDrawShadow(any(Canvas.class));
+ }
+
+ public void testUpdateDragShadow_detachedView() {
+ View view = new View(mActivity);
+ assertFalse(view.isAttachedToWindow());
+
+ View.DragShadowBuilder shadowBuilder = mock(View.DragShadowBuilder.class);
+ view.startDragAndDrop(ClipData.newPlainText("", ""), shadowBuilder, view, 0);
+ reset(shadowBuilder);
+
+ view.updateDragShadow(shadowBuilder);
+ verify(shadowBuilder, never()).onDrawShadow(any(Canvas.class));
+ }
+
+ public void testUpdateDragShadow_noActiveDrag() {
+ View view = mActivity.findViewById(R.id.fit_windows);
+ assertTrue(view.isAttachedToWindow());
+
+ View.DragShadowBuilder shadowBuilder = mock(View.DragShadowBuilder.class);
+ view.updateDragShadow(shadowBuilder);
+ verify(shadowBuilder, never()).onDrawShadow(any(Canvas.class));
+ }
+
private void setVisibilityOnUiThread(final View view, final int visibility) throws Throwable {
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- view.setVisibility(visibility);
- }
- });
+ runTestOnUiThread(() -> view.setVisibility(visibility));
}
private static class MockOverlappingRenderingSubclass extends View {
diff --git a/tests/tests/view/src/android/view/cts/ViewTreeObserverTest.java b/tests/tests/view/src/android/view/cts/ViewTreeObserverTest.java
index b799e76..32528fa 100644
--- a/tests/tests/view/src/android/view/cts/ViewTreeObserverTest.java
+++ b/tests/tests/view/src/android/view/cts/ViewTreeObserverTest.java
@@ -20,9 +20,9 @@
import android.app.Activity;
import android.app.Instrumentation;
+import android.cts.util.CtsTouchUtils;
import android.cts.util.PollingCheck;
import android.test.ActivityInstrumentationTestCase2;
-import android.test.TouchUtils;
import android.view.View;
import android.view.ViewTreeObserver;
import android.view.ViewTreeObserver.OnGlobalFocusChangeListener;
@@ -60,12 +60,7 @@
}
private void layout(final int layoutId) {
- mInstrumentation.runOnMainSync(new Runnable() {
- @Override
- public void run() {
- mActivity.setContentView(layoutId);
- }
- });
+ mInstrumentation.runOnMainSync(() -> mActivity.setContentView(layoutId));
mInstrumentation.waitForIdleSync();
}
@@ -74,24 +69,14 @@
final View view1 = mActivity.findViewById(R.id.view1);
final View view2 = mActivity.findViewById(R.id.view2);
- mInstrumentation.runOnMainSync(new Runnable() {
- @Override
- public void run() {
- view1.requestFocus();
- }
- });
+ mInstrumentation.runOnMainSync(view1::requestFocus);
mViewTreeObserver = layout.getViewTreeObserver();
final MockOnGlobalFocusChangeListener listener = new MockOnGlobalFocusChangeListener();
mViewTreeObserver.addOnGlobalFocusChangeListener(listener);
assertFalse(listener.hasCalledOnGlobalFocusChanged());
- mInstrumentation.runOnMainSync(new Runnable() {
- @Override
- public void run() {
- view2.requestFocus();
- }
- });
+ mInstrumentation.runOnMainSync(view2::requestFocus);
mInstrumentation.waitForIdleSync();
new PollingCheck() {
@Override
@@ -129,7 +114,7 @@
final Button b = (Button) mActivity.findViewById(R.id.button1);
// let the button be touch mode.
- TouchUtils.tapView(this, b);
+ CtsTouchUtils.emulateTapOnViewCenter(mInstrumentation, b);
mViewTreeObserver = b.getViewTreeObserver();
@@ -137,12 +122,7 @@
assertFalse(listener.hasCalledOnTouchModeChanged());
mViewTreeObserver.addOnTouchModeChangeListener(listener);
- mInstrumentation.runOnMainSync(new Runnable() {
- @Override
- public void run() {
- b.requestFocusFromTouch();
- }
- });
+ mInstrumentation.runOnMainSync(b::requestFocusFromTouch);
mInstrumentation.waitForIdleSync();
new PollingCheck() {
@@ -204,23 +184,13 @@
final View view1 = mActivity.findViewById(R.id.view1);
final View view2 = mActivity.findViewById(R.id.view2);
- mInstrumentation.runOnMainSync(new Runnable() {
- @Override
- public void run() {
- view1.requestFocus();
- }
- });
+ mInstrumentation.runOnMainSync(view1::requestFocus);
mViewTreeObserver = layout.getViewTreeObserver();
final MockOnGlobalFocusChangeListener listener = new MockOnGlobalFocusChangeListener();
mViewTreeObserver.addOnGlobalFocusChangeListener(listener);
assertFalse(listener.hasCalledOnGlobalFocusChanged());
- mInstrumentation.runOnMainSync(new Runnable() {
- @Override
- public void run() {
- view2.requestFocus();
- }
- });
+ mInstrumentation.runOnMainSync(view2::requestFocus);
mInstrumentation.waitForIdleSync();
new PollingCheck() {
@Override
@@ -233,12 +203,7 @@
listener.reset();
mViewTreeObserver.removeOnGlobalFocusChangeListener(listener);
assertFalse(listener.hasCalledOnGlobalFocusChanged());
- mInstrumentation.runOnMainSync(new Runnable() {
- @Override
- public void run() {
- view1.requestFocus();
- }
- });
+ mInstrumentation.runOnMainSync(view1::requestFocus);
mInstrumentation.waitForIdleSync();
new PollingCheck() {
@Override
@@ -268,19 +233,14 @@
public void testRemoveOnTouchModeChangeListener() {
final Button b = (Button) mActivity.findViewById(R.id.button1);
// let the button be touch mode.
- TouchUtils.tapView(this, b);
+ CtsTouchUtils.emulateTapOnViewCenter(mInstrumentation, b);
mViewTreeObserver = b.getViewTreeObserver();
MockOnTouchModeChangeListener listener = new MockOnTouchModeChangeListener();
mViewTreeObserver.addOnTouchModeChangeListener(listener);
assertFalse(listener.hasCalledOnTouchModeChanged());
- mInstrumentation.runOnMainSync(new Runnable() {
- @Override
- public void run() {
- b.requestFocusFromTouch();
- }
- });
+ mInstrumentation.runOnMainSync(b::requestFocusFromTouch);
mInstrumentation.waitForIdleSync();
final MockOnTouchModeChangeListener l = listener;
new PollingCheck() {
@@ -292,12 +252,7 @@
listener = new MockOnTouchModeChangeListener();
assertFalse(listener.hasCalledOnTouchModeChanged());
- mInstrumentation.runOnMainSync(new Runnable() {
- @Override
- public void run() {
- b.requestFocusFromTouch();
- }
- });
+ mInstrumentation.runOnMainSync(b::requestFocusFromTouch);
mInstrumentation.waitForIdleSync();
final MockOnTouchModeChangeListener l2 = listener;
new PollingCheck() {
@@ -318,12 +273,7 @@
assertFalse(listener.hasCalledOnScrollChanged());
mViewTreeObserver.addOnScrollChangedListener(listener);
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- scrollView.fullScroll(View.FOCUS_DOWN);
- }
- });
+ runTestOnUiThread(() -> scrollView.fullScroll(View.FOCUS_DOWN));
mInstrumentation.waitForIdleSync();
new PollingCheck() {
@Override
@@ -336,12 +286,7 @@
assertFalse(listener.hasCalledOnScrollChanged());
mViewTreeObserver.removeOnScrollChangedListener(listener);
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- scrollView.fullScroll(View.FOCUS_UP);
- }
- });
+ runTestOnUiThread(() -> scrollView.fullScroll(View.FOCUS_UP));
assertFalse(listener.hasCalledOnScrollChanged());
}
diff --git a/tests/tests/view/src/android/view/cts/View_BaseSavedStateTest.java b/tests/tests/view/src/android/view/cts/View_BaseSavedStateTest.java
index ce64a18..8ce70f2 100644
--- a/tests/tests/view/src/android/view/cts/View_BaseSavedStateTest.java
+++ b/tests/tests/view/src/android/view/cts/View_BaseSavedStateTest.java
@@ -16,15 +16,86 @@
package android.view.cts;
-
import android.os.Parcel;
+import android.os.Parcelable;
+import android.support.test.filters.SmallTest;
import android.test.InstrumentationTestCase;
+import android.view.AbsSavedState;
import android.view.View.BaseSavedState;
-public class View_BaseSavedStateTest extends InstrumentationTestCase {
- public void testConstructors() {
- BaseSavedState state = new BaseSavedState(Parcel.obtain());
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
- new BaseSavedState(state);
+@SmallTest
+public class View_BaseSavedStateTest extends InstrumentationTestCase {
+
+ public void testConstructors() {
+ try {
+ new BaseSavedState((Parcelable) null);
+ fail("Expected NullPointerException");
+ } catch (IllegalArgumentException e) {
+ // Expected.
+ }
+
+ try {
+ new BaseSavedState((Parcel) null);
+ fail("Expected NullPointerException");
+ } catch (NullPointerException e) {
+ // Expected.
+ }
+
+ try {
+ new BaseSavedState(null, null);
+ fail("Expected NullPointerException");
+ } catch (NullPointerException e) {
+ // Expected.
+ }
+
+ BaseSavedState superState = new BaseSavedState(Parcel.obtain());
+ assertEquals(AbsSavedState.EMPTY_STATE, superState.getSuperState());
+
+ BaseSavedState s = new BaseSavedState(superState);
+ assertEquals(superState, s.getSuperState());
+
+ Parcel source = Parcel.obtain();
+ source.writeParcelable(superState, 0);
+ source.setDataPosition(0);
+ s = new BaseSavedState(source);
+ assertTrue(s.getSuperState() instanceof BaseSavedState);
+
+ ClassLoader loader = BaseSavedState.class.getClassLoader();
+ source = Parcel.obtain();
+ source.writeParcelable(superState, 0);
+ source.setDataPosition(0);
+ s = new BaseSavedState(source, loader);
+ assertTrue(s.getSuperState() instanceof BaseSavedState);
+ }
+
+ public void testCreator() {
+ int size = 10;
+ BaseSavedState[] array = BaseSavedState.CREATOR.newArray(size);
+ assertNotNull(array);
+ assertEquals(size, array.length);
+ for (BaseSavedState state : array) {
+ assertNull(state);
+ }
+
+ BaseSavedState state = new BaseSavedState(AbsSavedState.EMPTY_STATE);
+ Parcel parcel = Parcel.obtain();
+ state.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ BaseSavedState unparceled = BaseSavedState.CREATOR.createFromParcel(parcel);
+ assertNotNull(unparceled);
+ assertEquals(AbsSavedState.EMPTY_STATE, unparceled.getSuperState());
+ }
+
+ public void testWriteToParcel() {
+ Parcelable superState = mock(Parcelable.class);
+ BaseSavedState savedState = new BaseSavedState(superState);
+ Parcel dest = Parcel.obtain();
+ int flags = 2;
+ savedState.writeToParcel(dest, flags);
+ verify(superState).writeToParcel(eq(dest), eq(flags));
}
}
diff --git a/tests/tests/view/src/android/view/cts/View_UsingViewsTest.java b/tests/tests/view/src/android/view/cts/View_UsingViewsTest.java
index 413c356..697ed82 100644
--- a/tests/tests/view/src/android/view/cts/View_UsingViewsTest.java
+++ b/tests/tests/view/src/android/view/cts/View_UsingViewsTest.java
@@ -21,11 +21,11 @@
import android.app.Activity;
import android.app.Instrumentation;
+import android.cts.util.CtsTouchUtils;
import android.cts.util.PollingCheck;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.test.ActivityInstrumentationTestCase2;
-import android.test.TouchUtils;
import android.test.UiThreadTest;
import android.view.View;
import android.view.View.OnClickListener;
@@ -357,7 +357,7 @@
assertNull(onLongClickListener.getView());
mInstrumentation.waitForIdleSync();
- TouchUtils.longClickView(this, mEditText);
+ CtsTouchUtils.emulateLongClick(mInstrumentation, mEditText);
new PollingCheck() {
@Override
protected boolean check() {
@@ -367,55 +367,35 @@
assertSame(mEditText, onLongClickListener.getView());
// click the Cancel button
- runTestOnUiThread(new Runnable() {
- public void run() {
- mEditText.setText("Germany");
- }
- });
+ runTestOnUiThread(() -> mEditText.setText("Germany"));
mInstrumentation.waitForIdleSync();
- TouchUtils.clickView(this, mButtonCancel);
+ CtsTouchUtils.emulateTapOnViewCenter(mInstrumentation, mButtonCancel);
assertEquals("", mEditText.getText().toString());
// click the OK button
- runTestOnUiThread(new Runnable() {
- public void run() {
- mEditText.setText(ARGENTINA);
- }
- });
+ runTestOnUiThread(() -> mEditText.setText(ARGENTINA));
mInstrumentation.waitForIdleSync();
- TouchUtils.clickView(this, mButtonOk);
+ CtsTouchUtils.emulateTapOnViewCenter(mInstrumentation, mButtonOk);
assertEquals(ARGENTINA_SYMBOL, mSymbolTextView.getText().toString());
- runTestOnUiThread(new Runnable() {
- public void run() {
- mEditText.setText(AMERICA);
- }
- });
+ runTestOnUiThread(() -> mEditText.setText(AMERICA));
mInstrumentation.waitForIdleSync();
- TouchUtils.clickView(this, mButtonOk);
+ CtsTouchUtils.emulateTapOnViewCenter(mInstrumentation, mButtonOk);
assertEquals(AMERICA_SYMBOL, mSymbolTextView.getText().toString());
- runTestOnUiThread(new Runnable() {
- public void run() {
- mEditText.setText(CHINA);
- }
- });
+ runTestOnUiThread(() -> mEditText.setText(CHINA));
mInstrumentation.waitForIdleSync();
- TouchUtils.clickView(this, mButtonOk);
+ CtsTouchUtils.emulateTapOnViewCenter(mInstrumentation, mButtonOk);
assertEquals(CHINA_SYMBOL, mSymbolTextView.getText().toString());
- runTestOnUiThread(new Runnable() {
- public void run() {
- mEditText.setText("Unknown");
- }
- });
+ runTestOnUiThread(() -> mEditText.setText("Unknown"));
mInstrumentation.waitForIdleSync();
- TouchUtils.clickView(this, mButtonOk);
+ CtsTouchUtils.emulateTapOnViewCenter(mInstrumentation, mButtonOk);
assertEquals(View.VISIBLE, mWarningTextView.getVisibility());
}
diff --git a/tests/tests/view/src/android/view/cts/util/ViewTestUtils.java b/tests/tests/view/src/android/view/cts/util/ViewTestUtils.java
deleted file mode 100644
index b70e882..0000000
--- a/tests/tests/view/src/android/view/cts/util/ViewTestUtils.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * 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.view.cts.util;
-
-import junit.framework.Assert;
-
-import android.app.Activity;
-import android.app.Instrumentation;
-import android.view.View;
-import android.view.ViewTreeObserver;
-import android.view.ViewTreeObserver.OnDrawListener;
-
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Utilities for testing View behavior.
- */
-public class ViewTestUtils {
-
- /**
- * Runs the specified Runnable on the main thread and ensures that the
- * specified View's tree is drawn before returning.
- *
- * @param instrumentation the instrumentation used to run the test
- * @param view the view whose tree should be drawn before returning
- * @param runner the runnable to run on the main thread
- */
- public static void runOnMainAndDrawSync(Instrumentation instrumentation,
- final Activity activity, final Runnable runner) {
- final CountDownLatch latch = new CountDownLatch(1);
-
- instrumentation.runOnMainSync(() -> {
- final View view = activity.findViewById(android.R.id.content);
- final ViewTreeObserver observer = view.getViewTreeObserver();
- final OnDrawListener listener = new OnDrawListener() {
- @Override
- public void onDraw() {
- observer.removeOnDrawListener(this);
- view.post(() -> latch.countDown());
- }
- };
-
- observer.addOnDrawListener(listener);
- runner.run();
- });
-
- try {
- Assert.assertTrue("Expected draw pass occurred within 5 seconds",
- latch.await(5, TimeUnit.SECONDS));
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
- }
-}
\ No newline at end of file
diff --git a/tests/tests/widget/Android.mk b/tests/tests/widget/Android.mk
index 4bc9eee..3d4cead 100644
--- a/tests/tests/widget/Android.mk
+++ b/tests/tests/widget/Android.mk
@@ -22,10 +22,12 @@
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
LOCAL_STATIC_JAVA_LIBRARIES += \
+ android-support-test \
mockito-target \
android-common \
ctsdeviceutil \
- ctstestrunner
+ ctstestrunner \
+ platform-test-annotations
LOCAL_JAVA_LIBRARIES := android.test.runner
diff --git a/tests/tests/widget/AndroidManifest.xml b/tests/tests/widget/AndroidManifest.xml
index 224966d..0a60bf9 100644
--- a/tests/tests/widget/AndroidManifest.xml
+++ b/tests/tests/widget/AndroidManifest.xml
@@ -19,6 +19,7 @@
package="android.widget.cts">
<uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+
<application android:label="Android TestCase"
android:icon="@drawable/size_48x48"
android:maxRecents="1"
@@ -92,9 +93,25 @@
</intent-filter>
</activity>
+ <activity android:name="android.widget.cts.SpinnerCtsActivity"
+ android:label="SpinnerCtsActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
+ </intent-filter>
+ </activity>
+
<activity android:name="android.widget.cts.ToolbarCtsActivity"
- android:theme="@android:style/Theme.Material.Light.NoActionBar"
- android:label="ToolbarCtsActivity">
+ android:theme="@android:style/Theme.Material.Light.NoActionBar"
+ android:label="ToolbarCtsActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
+ </intent-filter>
+ </activity>
+
+ <activity android:name="android.widget.cts.ActionMenuViewCtsActivity"
+ android:label="ActionMenuViewCtsActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
@@ -149,6 +166,14 @@
</intent-filter>
</activity>
+ <activity android:name="android.widget.cts.AbsSeekBarCtsActivity"
+ android:label="AbsSeekBarCtsActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
+ </intent-filter>
+ </activity>
+
<activity android:name="android.widget.cts.ProgressBarCtsActivity"
android:label="ProgressBarCtsActivity">
<intent-filter>
@@ -189,8 +214,16 @@
</intent-filter>
</activity>
- <activity android:name="android.widget.cts.ExpandableListSimple"
- android:label="ExpandableListSimple">
+ <activity android:name="android.widget.cts.ExpandableListBasic"
+ android:label="ExpandableListBasic">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
+ </intent-filter>
+ </activity>
+
+ <activity android:name="android.widget.cts.ExpandableList"
+ android:label="ExpandableList">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
@@ -246,7 +279,15 @@
</activity>
<activity android:name="android.widget.cts.ListViewCtsActivity"
- android:label="ListViewCtsActivity">
+ android:label="ListViewCtsActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
+ </intent-filter>
+ </activity>
+
+ <activity android:name="android.widget.cts.ListViewFixedCtsActivity"
+ android:label="ListViewFixedCtsActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
@@ -388,6 +429,70 @@
</intent-filter>
</activity>
+ <activity android:name="android.widget.cts.DatePickerCtsActivity"
+ android:label="DatePickerCtsActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
+ </intent-filter>
+ </activity>
+
+ <activity android:name="android.widget.cts.SearchViewCtsActivity"
+ android:label="SearchViewCtsActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
+ </intent-filter>
+ </activity>
+
+ <activity android:name="android.widget.cts.ImageButtonCtsActivity"
+ android:label="ImageButtonCtsActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
+ </intent-filter>
+ </activity>
+
+ <activity android:name="android.widget.cts.NumberPickerCtsActivity"
+ android:label="NumberPickerCtsActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
+ </intent-filter>
+ </activity>
+
+ <activity android:name="android.widget.cts.CheckBoxCtsActivity"
+ android:label="CheckBoxCtsActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
+ </intent-filter>
+ </activity>
+
+ <activity android:name="android.widget.cts.RadioButtonCtsActivity"
+ android:label="RadioButtonCtsActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
+ </intent-filter>
+ </activity>
+
+ <activity android:name="android.widget.cts.ToggleButtonCtsActivity"
+ android:label="ToggleButtonCtsActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
+ </intent-filter>
+ </activity>
+
+ <activity android:name="android.widget.cts.TimePickerCtsActivity"
+ android:label="TimePickerCtsActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
+ </intent-filter>
+ </activity>
+
<activity android:name="android.app.ActivityGroup"
android:label="ActivityGroup" />
@@ -405,6 +510,17 @@
</intent-filter>
</activity>
+ <receiver android:name="android.widget.cts.appwidget.MyAppWidgetProvider" >
+ <intent-filter>
+ <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
+ </intent-filter>
+ <meta-data android:name="android.appwidget.provider"
+ android:resource="@xml/remoteviews_appwidget_info" />
+ </receiver>
+
+ <service android:name="android.widget.cts.appwidget.MyAppWidgetService"
+ android:permission="android.permission.BIND_REMOTEVIEWS"
+ android:exported="false" />
</application>
<instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
diff --git a/tests/tests/widget/res/drawable/blue_fill.xml b/tests/tests/widget/res/drawable/blue_fill.xml
index 5a24f08..383e7db 100644
--- a/tests/tests/widget/res/drawable/blue_fill.xml
+++ b/tests/tests/widget/res/drawable/blue_fill.xml
@@ -17,5 +17,5 @@
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
- <solid android:color="#0000FF" />
+ <solid android:color="#00F" />
</shape>
\ No newline at end of file
diff --git a/tests/tests/widget/res/drawable/green_fill.xml b/tests/tests/widget/res/drawable/green_fill.xml
new file mode 100644
index 0000000..76c1101
--- /dev/null
+++ b/tests/tests/widget/res/drawable/green_fill.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<shape
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <solid android:color="#0F0" />
+</shape>
\ No newline at end of file
diff --git a/tests/tests/widget/res/drawable/linear_layout_divider_magenta.xml b/tests/tests/widget/res/drawable/linear_layout_divider_magenta.xml
new file mode 100644
index 0000000..2dbd654
--- /dev/null
+++ b/tests/tests/widget/res/drawable/linear_layout_divider_magenta.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<shape
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <solid
+ android:color="#F0F" />
+ <size
+ android:width="@dimen/linear_layout_divider_size"
+ android:height="@dimen/linear_layout_divider_size" />
+</shape>
\ No newline at end of file
diff --git a/tests/tests/widget/res/drawable/linear_layout_divider_red.xml b/tests/tests/widget/res/drawable/linear_layout_divider_red.xml
new file mode 100644
index 0000000..56e88ef
--- /dev/null
+++ b/tests/tests/widget/res/drawable/linear_layout_divider_red.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<shape
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <solid
+ android:color="#F00" />
+ <size
+ android:width="@dimen/linear_layout_divider_size"
+ android:height="@dimen/linear_layout_divider_size" />
+</shape>
\ No newline at end of file
diff --git a/tests/tests/widget/res/drawable/magenta_fill.xml b/tests/tests/widget/res/drawable/magenta_fill.xml
index cbb594f..8b6da76 100644
--- a/tests/tests/widget/res/drawable/magenta_fill.xml
+++ b/tests/tests/widget/res/drawable/magenta_fill.xml
@@ -17,5 +17,5 @@
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
- <solid android:color="#FF00FF" />
+ <solid android:color="#F0F" />
</shape>
\ No newline at end of file
diff --git a/tests/tests/widget/res/drawable/red_fill.xml b/tests/tests/widget/res/drawable/red_fill.xml
index e443240..8baf5b6 100644
--- a/tests/tests/widget/res/drawable/red_fill.xml
+++ b/tests/tests/widget/res/drawable/red_fill.xml
@@ -17,5 +17,5 @@
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
- <solid android:color="#8F00" />
+ <solid android:color="#F00" />
</shape>
\ No newline at end of file
diff --git a/tests/tests/widget/res/drawable/red_translucent_fill.xml b/tests/tests/widget/res/drawable/red_translucent_fill.xml
new file mode 100644
index 0000000..edfbfa0
--- /dev/null
+++ b/tests/tests/widget/res/drawable/red_translucent_fill.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<shape
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <solid android:color="#80FF0000" />
+</shape>
\ No newline at end of file
diff --git a/tests/tests/widget/res/drawable/yellow_fill.xml b/tests/tests/widget/res/drawable/yellow_fill.xml
index 3bd8097..44f88c4 100644
--- a/tests/tests/widget/res/drawable/yellow_fill.xml
+++ b/tests/tests/widget/res/drawable/yellow_fill.xml
@@ -17,5 +17,5 @@
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
- <solid android:color="#FFFF00" />
+ <solid android:color="#FF0" />
</shape>
\ No newline at end of file
diff --git a/tests/tests/widget/res/layout/actionmenuview_layout.xml b/tests/tests/widget/res/layout/actionmenuview_layout.xml
new file mode 100644
index 0000000..5d2895e
--- /dev/null
+++ b/tests/tests/widget/res/layout/actionmenuview_layout.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="horizontal">
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/button" />
+
+ <ActionMenuView
+ android:id="@+id/action_menu_view"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+
+</LinearLayout>
+
diff --git a/tests/tests/widget/res/layout/autocompletetextview_layout.xml b/tests/tests/widget/res/layout/autocompletetextview_layout.xml
index 27eccab..8e023a1 100644
--- a/tests/tests/widget/res/layout/autocompletetextview_layout.xml
+++ b/tests/tests/widget/res/layout/autocompletetextview_layout.xml
@@ -24,10 +24,11 @@
android:layout_height="wrap_content"
android:text="@string/notify" />
- <android.widget.cts.AutoCompleteTextViewNoIme android:id="@+id/autocompletetv_edit"
- android:completionThreshold="1"
- android:completionHint="@string/tabs_1"
+ <android.widget.cts.AutoCompleteTextViewNoIme
+ android:id="@+id/autocompletetv_edit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:completionThreshold="1"
+ android:completionHint="@string/tabs_1"
android:inputType="none"/>
</LinearLayout>
diff --git a/tests/tests/widget/res/layout/calendarview_layout.xml b/tests/tests/widget/res/layout/calendarview_layout.xml
index 3bc5e47..507d48e 100644
--- a/tests/tests/widget/res/layout/calendarview_layout.xml
+++ b/tests/tests/widget/res/layout/calendarview_layout.xml
@@ -17,6 +17,7 @@
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/scroller"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true">
diff --git a/tests/tests/widget/res/layout/checkbox_layout.xml b/tests/tests/widget/res/layout/checkbox_layout.xml
index a1f1718..c922436 100644
--- a/tests/tests/widget/res/layout/checkbox_layout.xml
+++ b/tests/tests/widget/res/layout/checkbox_layout.xml
@@ -14,12 +14,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
-->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
- <CheckBox android:id="@+id/check_box"
+ <CheckBox
+ android:id="@+id/check_box"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hello_world" />
diff --git a/tests/tests/widget/res/layout/checkedtextview_layout.xml b/tests/tests/widget/res/layout/checkedtextview_layout.xml
index d5b9c1f..fbee93e 100644
--- a/tests/tests/widget/res/layout/checkedtextview_layout.xml
+++ b/tests/tests/widget/res/layout/checkedtextview_layout.xml
@@ -14,20 +14,22 @@
limitations under the License.
-->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
- <ListView android:id="@+id/checkedtextview_listview"
- android:orientation="vertical"
+ <ListView
+ android:id="@+id/checkedtextview_listview"
android:layout_width="match_parent"
- android:layout_height="match_parent">
- </ListView>
+ android:layout_height="0dip"
+ android:layout_weight="1" />
- <CheckedTextView android:id="@+id/checkedtextview_test"
- android:orientation="vertical"
+ <CheckedTextView
+ android:id="@+id/checkedtextview_test"
android:layout_width="match_parent"
- android:layout_height="match_parent">
- </CheckedTextView>
+ android:layout_height="wrap_content"
+ android:text="@string/hello_world" />
</LinearLayout>
diff --git a/tests/tests/widget/res/layout/datepicker_layout.xml b/tests/tests/widget/res/layout/datepicker_layout.xml
index 925674c..9857440 100644
--- a/tests/tests/widget/res/layout/datepicker_layout.xml
+++ b/tests/tests/widget/res/layout/datepicker_layout.xml
@@ -14,14 +14,29 @@
limitations under the License.
-->
-<RelativeLayout
+<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="match_parent">
- <DatePicker
- android:id="@+id/datePicker_dp"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_centerInParent="true" />
+ android:layout_height="match_parent"
+ android:fillViewport="true">
-</RelativeLayout>
+ <LinearLayout
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <DatePicker
+ android:id="@+id/date_picker_calendar_mode"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:datePickerMode="calendar" />
+
+ <DatePicker
+ android:id="@+id/date_picker_spinner_mode"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:datePickerMode="spinner" />
+
+ </LinearLayout>
+</ScrollView>
diff --git a/tests/tests/widget/res/layout/expandablelistview_child.xml b/tests/tests/widget/res/layout/expandablelistview_child.xml
new file mode 100644
index 0000000..b34f10b
--- /dev/null
+++ b/tests/tests/widget/res/layout/expandablelistview_child.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<TextView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textSize="18sp" />
diff --git a/tests/tests/widget/res/layout/expandablelistview_group.xml b/tests/tests/widget/res/layout/expandablelistview_group.xml
new file mode 100644
index 0000000..be929ee
--- /dev/null
+++ b/tests/tests/widget/res/layout/expandablelistview_group.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<TextView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textStyle="bold"
+ android:textSize="24sp" />
diff --git a/tests/tests/widget/res/layout/imagebutton_layout.xml b/tests/tests/widget/res/layout/imagebutton_layout.xml
new file mode 100644
index 0000000..f59f669
--- /dev/null
+++ b/tests/tests/widget/res/layout/imagebutton_layout.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * 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.
+ -->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <ImageButton
+ android:id="@+id/image_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/icon_red" />
+
+</LinearLayout>
diff --git a/tests/tests/widget/res/layout/imagebutton_test.xml b/tests/tests/widget/res/layout/imagebutton_test.xml
deleted file mode 100644
index 7d4b691..0000000
--- a/tests/tests/widget/res/layout/imagebutton_test.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- * Copyright (C) 2008 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.
- -->
-<ImageButton xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/imagebutton"
- android:layout_width="96px"
- android:layout_height="76px"
- android:soundEffectsEnabled="false"
-/>
-
diff --git a/tests/tests/widget/res/layout/linearlayout_layout.xml b/tests/tests/widget/res/layout/linearlayout_layout.xml
index 8881552..11d2a71 100644
--- a/tests/tests/widget/res/layout/linearlayout_layout.xml
+++ b/tests/tests/widget/res/layout/linearlayout_layout.xml
@@ -14,125 +14,228 @@
* See the License for the specific language governing permissions and
* limitations under the License.
-->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/linearlayout_root"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/linearlayout_root"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
- <LinearLayout android:id="@+id/horizontal"
- android:orientation="horizontal"
+ <LinearLayout
+ android:id="@+id/horizontal"
android:layout_width="wrap_content"
android:layout_height="100dip"
- android:background="#FF909090">
+ android:orientation="horizontal"
+ android:background="#888">
- <TextView android:id="@+id/gravity_top"
- android:background="#FF00FF00"
+ <TextView
+ android:id="@+id/gravity_top"
android:layout_width="wrap_content"
android:layout_height="50dip"
android:layout_gravity="top"
- android:text="@string/horizontal_text_1"/>
+ android:background="#0F0"
+ android:text="@string/horizontal_text_1" />
- <TextView android:id="@+id/gravity_center_vertical"
- android:background="#FF00FF00"
+ <TextView
+ android:id="@+id/gravity_center_vertical"
android:layout_width="wrap_content"
android:layout_height="50dip"
android:layout_gravity="center_vertical"
- android:text="@string/horizontal_text_2"/>
+ android:background="#0F0"
+ android:text="@string/horizontal_text_2" />
- <TextView android:id="@+id/gravity_bottom"
- android:background="#FF00FF00"
+ <TextView
+ android:id="@+id/gravity_bottom"
android:layout_width="wrap_content"
android:layout_height="50dip"
android:layout_gravity="bottom"
- android:text="@string/horizontal_text_3"/>
+ android:background="#0F0"
+ android:text="@string/horizontal_text_3" />
</LinearLayout>
- <LinearLayout android:id="@+id/vertical"
- android:orientation="vertical"
+ <LinearLayout
+ android:id="@+id/vertical"
android:layout_width="100dip"
android:layout_height="wrap_content"
- android:background="#FFFF0909">
+ android:orientation="vertical"
+ android:background="#F00">
- <TextView android:id="@+id/gravity_left"
- android:background="#FF00FF00"
+ <TextView
+ android:id="@+id/gravity_left"
android:layout_width="wrap_content"
android:layout_height="20dip"
android:layout_gravity="left"
- android:text="@string/vertical_text_1"/>
+ android:background="#0F0"
+ android:text="@string/vertical_text_1" />
- <TextView android:id="@+id/gravity_center_horizontal"
- android:background="#FF0000FF"
+ <TextView
+ android:id="@+id/gravity_center_horizontal"
android:layout_width="wrap_content"
android:layout_height="20dip"
android:layout_gravity="center_horizontal"
- android:text="@string/vertical_text_2"/>
+ android:background="#00F"
+ android:text="@string/vertical_text_2" />
- <TextView android:id="@+id/gravity_right"
- android:background="#FF00FF00"
+ <TextView
+ android:id="@+id/gravity_right"
android:layout_width="wrap_content"
android:layout_height="20dip"
android:layout_gravity="right"
- android:text="@string/vertical_text_3"/>
+ android:background="#0F0"
+ android:text="@string/vertical_text_3" />
</LinearLayout>
- <LinearLayout android:id="@+id/weightsum"
+ <LinearLayout
+ android:id="@+id/weightsum"
+ android:layout_width="100dip"
+ android:layout_height="100dip"
android:orientation="horizontal"
android:weightSum="1.0"
android:baselineAligned="false"
- android:layout_width="100dip"
- android:layout_height="100dip"
- android:background="#FF909090">
+ android:background="#888">
- <TextView android:id="@+id/weight_0_2"
- android:background="#FF0000FF"
+ <TextView
+ android:id="@+id/weight_0_2"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="0.2"
- android:text="@string/horizontal_text_1"/>
+ android:background="#00F"
+ android:text="@string/horizontal_text_1" />
- <TextView android:id="@+id/weight_0_5"
- android:background="#FFF00F0F"
+ <TextView
+ android:id="@+id/weight_0_5"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="0.5"
- android:text="@string/horizontal_text_2"/>
+ android:background="#F00"
+ android:text="@string/horizontal_text_2" />
- <TextView android:id="@+id/weight_0_3"
- android:background="#FF0000FF"
+ <TextView
+ android:id="@+id/weight_0_3"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="0.3"
- android:text="@string/horizontal_text_3"/>
+ android:background="#00F"
+ android:text="@string/horizontal_text_3" />
</LinearLayout>
- <LinearLayout android:id="@+id/baseline_aligned_child_index"
+ <LinearLayout
+ android:id="@+id/weightsum_vertical"
+ android:layout_width="100dip"
+ android:layout_height="100dip"
android:orientation="vertical"
- android:baselineAlignedChildIndex="1"
+ android:weightSum="1.0"
+ android:baselineAligned="false"
+ android:background="#888">
+
+ <TextView
+ android:id="@+id/weight_0_1"
+ android:layout_width="wrap_content"
+ android:layout_height="0dip"
+ android:layout_weight="0.1"
+ android:background="#00F"
+ android:text="@string/vertical_text_1" />
+
+ <TextView
+ android:id="@+id/weight_0_4"
+ android:layout_width="wrap_content"
+ android:layout_height="0dip"
+ android:layout_weight="0.4"
+ android:background="#F00"
+ android:text="@string/vertical_text_2" />
+
+ <TextView
+ android:id="@+id/weight_0_5"
+ android:layout_width="wrap_content"
+ android:layout_height="0dip"
+ android:layout_weight="0.5"
+ android:background="#00F"
+ android:text="@string/vertical_text_3" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/baseline_aligned_child_index"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:background="#FFFF0909">
+ android:orientation="vertical"
+ android:baselineAlignedChildIndex="1"
+ android:background="#F00">
- <TextView android:id="@+id/textview1"
- android:background="#FF00FF00"
+ <TextView
+ android:id="@+id/textview1"
android:layout_width="wrap_content"
android:layout_height="20dip"
android:layout_gravity="left"
- android:text="@string/vertical_text_1"/>
+ android:background="#0F0"
+ android:text="@string/vertical_text_1" />
- <TextView android:id="@+id/textview2"
- android:background="#FF0000FF"
+ <TextView
+ android:id="@+id/textview2"
android:layout_width="wrap_content"
android:layout_height="20dip"
android:layout_gravity="center_horizontal"
- android:text="@string/vertical_text_2"/>
+ android:background="#00F"
+ android:text="@string/vertical_text_2" />
- <TextView android:id="@+id/textview3"
- android:background="#FF00FF00"
+ <TextView
+ android:id="@+id/textview3"
android:layout_width="wrap_content"
android:layout_height="20dip"
android:layout_gravity="right"
- android:text="@string/vertical_text_3"/>
+ android:background="#0F0"
+ android:text="@string/vertical_text_3" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/vertical_with_divider"
+ android:layout_width="100px"
+ android:layout_height="100px"
+ android:orientation="vertical"
+ android:background="#FF0"
+ android:showDividers="middle"
+ android:divider="@drawable/linear_layout_divider_red"
+ android:dividerPadding="@dimen/linear_layout_divider_padding">
+
+ <View
+ android:id="@+id/child1"
+ android:layout_width="match_parent"
+ android:layout_height="0dip"
+ android:layout_weight="0.5"
+ android:background="#00F" />
+
+ <View
+ android:id="@+id/child2"
+ android:layout_width="match_parent"
+ android:layout_height="0dip"
+ android:layout_weight="0.5"
+ android:background="#0F0" />
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/horizontal_with_divider"
+ android:layout_width="100px"
+ android:layout_height="100px"
+ android:orientation="horizontal"
+ android:background="#FF0"
+ android:showDividers="middle"
+ android:divider="@drawable/linear_layout_divider_red"
+ android:dividerPadding="@dimen/linear_layout_divider_padding">
+
+ <View
+ android:id="@+id/child1"
+ android:layout_width="0dip"
+ android:layout_height="match_parent"
+ android:layout_weight="0.5"
+ android:background="#00F" />
+
+ <View
+ android:id="@+id/child2"
+ android:layout_width="0dip"
+ android:layout_height="match_parent"
+ android:layout_weight="0.5"
+ android:background="#0F0" />
+
</LinearLayout>
</LinearLayout>
diff --git a/tests/tests/widget/res/layout/listitemfixed_layout.xml b/tests/tests/widget/res/layout/listitemfixed_layout.xml
new file mode 100644
index 0000000..395a547
--- /dev/null
+++ b/tests/tests/widget/res/layout/listitemfixed_layout.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<TextView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@android:id/text1"
+ android:layout_width="match_parent"
+ android:layout_height="40px"
+ android:gravity="center_vertical"
+ android:textSize="36px"
+ android:textColor="#000" />
diff --git a/tests/tests/widget/res/layout/listview_layout.xml b/tests/tests/widget/res/layout/listview_layout.xml
index ee8b6de..3094a89 100644
--- a/tests/tests/widget/res/layout/listview_layout.xml
+++ b/tests/tests/widget/res/layout/listview_layout.xml
@@ -14,33 +14,48 @@
limitations under the License.
-->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="wrap_content">
+ android:layout_height="match_parent">
- <TextView android:id="@+id/headerview1"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/header_view" />
-
- <TextView android:id="@+id/headerview2"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/header_view" />
-
- <ListView android:id="@+id/listview_default"
+ <!-- This child is marked as GONE so that it won't interfere with the visuals of our
+ ListView on the screen. It's here to "hold" views that will be used as headers and
+ footers for the relevant tests. -->
+ <FrameLayout
android:layout_width="match_parent"
- android:layout_height="wrap_content"/>
-
- <TextView android:id="@+id/footerview1"
- android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:text="@string/footer_view" />
+ android:visibility="gone">
- <TextView android:id="@+id/footerview2"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/footer_view" />
-</LinearLayout>
+ <TextView
+ android:id="@+id/headerview1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/header_view" />
+
+ <TextView
+ android:id="@+id/headerview2"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/header_view" />
+
+ <TextView
+ android:id="@+id/footerview1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/footer_view" />
+
+ <TextView
+ android:id="@+id/footerview2"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/footer_view" />
+ </FrameLayout>
+
+ <ListView
+ android:id="@+id/listview_default"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
+
+</FrameLayout>
diff --git a/tests/tests/widget/res/layout/listviewfixed_layout.xml b/tests/tests/widget/res/layout/listviewfixed_layout.xml
new file mode 100644
index 0000000..158ce99
--- /dev/null
+++ b/tests/tests/widget/res/layout/listviewfixed_layout.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <ListView
+ android:id="@+id/listview_default"
+ android:layout_width="300px"
+ android:layout_height="300px"
+ android:divider="@null"
+ android:dividerHeight="0px"
+ android:background="#80FF0000"/>
+
+</FrameLayout>
+
diff --git a/tests/tests/widget/res/layout/numberpicker_layout.xml b/tests/tests/widget/res/layout/numberpicker_layout.xml
new file mode 100644
index 0000000..2e370d8
--- /dev/null
+++ b/tests/tests/widget/res/layout/numberpicker_layout.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <NumberPicker
+ android:id="@+id/number_picker"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/tests/widget/res/layout/progressbar_layout.xml b/tests/tests/widget/res/layout/progressbar_layout.xml
index a1786b8..f242135 100644
--- a/tests/tests/widget/res/layout/progressbar_layout.xml
+++ b/tests/tests/widget/res/layout/progressbar_layout.xml
@@ -14,28 +14,47 @@
limitations under the License.
-->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<ScrollView
+ xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="match_parent">
+ android:layout_height="match_parent"
+ android:fillViewport="true">
- <ProgressBar
- android:id="@+id/progress_tint"
- android:progressTint="@android:color/white"
- android:progressTintMode="src_over"
- android:progressBackgroundTint="@android:color/white"
- android:progressBackgroundTintMode="src_over"
- android:secondaryProgressTint="@android:color/white"
- android:secondaryProgressTintMode="src_over"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- style="@android:style/Widget.ProgressBar.Horizontal" />
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
- <ProgressBar
- android:id="@+id/indeterminate_tint"
- android:indeterminateTint="@android:color/white"
- android:indeterminateTintMode="src_over"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- style="@android:style/Widget.ProgressBar.Large" />
+ <ProgressBar
+ style="@android:style/Widget.Material.Light.ProgressBar"
+ android:id="@+id/progress"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
-</LinearLayout>
+ <ProgressBar
+ style="@android:style/Widget.Material.Light.ProgressBar.Horizontal"
+ android:id="@+id/progress_horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+ <ProgressBar
+ style="@android:style/Widget.Material.Light.ProgressBar.Horizontal"
+ android:id="@+id/progress_tint"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:progressTint="@android:color/white"
+ android:progressTintMode="src_over"
+ android:progressBackgroundTint="@android:color/white"
+ android:progressBackgroundTintMode="src_over"
+ android:secondaryProgressTint="@android:color/white"
+ android:secondaryProgressTintMode="src_over" />
+
+ <ProgressBar
+ style="@android:style/Widget.Material.Light.ProgressBar.Large"
+ android:id="@+id/indeterminate_tint"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:indeterminateTint="@android:color/white"
+ android:indeterminateTintMode="src_over" />
+
+ </LinearLayout>
+</ScrollView>
\ No newline at end of file
diff --git a/tests/tests/widget/res/layout/radiobutton_layout.xml b/tests/tests/widget/res/layout/radiobutton_layout.xml
new file mode 100644
index 0000000..23c1839
--- /dev/null
+++ b/tests/tests/widget/res/layout/radiobutton_layout.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * 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.
+ -->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <RadioButton
+ android:id="@+id/radio_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/hello_world" />
+
+</LinearLayout>
diff --git a/tests/tests/widget/res/layout/ratingbar_layout.xml b/tests/tests/widget/res/layout/ratingbar_layout.xml
index acc7fa8..b2fb4c7 100644
--- a/tests/tests/widget/res/layout/ratingbar_layout.xml
+++ b/tests/tests/widget/res/layout/ratingbar_layout.xml
@@ -14,18 +14,19 @@
limitations under the License.
-->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
- <RatingBar android:id="@+id/ratingbar_constructor"
+ <RatingBar
+ android:id="@+id/ratingbar_constructor"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
android:isIndicator="false"
android:numStars="50"
android:rating="1.2"
- android:stepSize="0.2"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="1"/>
+ android:stepSize="0.2" />
</LinearLayout>
diff --git a/tests/tests/widget/res/layout/remoteviews_adapter.xml b/tests/tests/widget/res/layout/remoteviews_adapter.xml
new file mode 100644
index 0000000..59115da
--- /dev/null
+++ b/tests/tests/widget/res/layout/remoteviews_adapter.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <StackView
+ android:id="@+id/remoteViews_stack"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="center"
+ android:loopViews="true" />
+ <TextView
+ android:id="@+id/remoteViews_empty"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="center" />
+ <ListView
+ android:id="@+id/remoteViews_list"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:visibility="gone" />
+</FrameLayout>
+
+
diff --git a/tests/tests/widget/res/layout/remoteviews_adapter_item.xml b/tests/tests/widget/res/layout/remoteviews_adapter_item.xml
new file mode 100644
index 0000000..ec621da
--- /dev/null
+++ b/tests/tests/widget/res/layout/remoteviews_adapter_item.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<TextView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/item"
+ android:layout_width="120dp"
+ android:layout_height="120dp"
+ android:gravity="center"
+ android:textStyle="bold"
+ android:textSize="44sp" />
diff --git a/tests/tests/widget/res/layout/remoteviews_extra.xml b/tests/tests/widget/res/layout/remoteviews_extra.xml
new file mode 100644
index 0000000..6a57ca3
--- /dev/null
+++ b/tests/tests/widget/res/layout/remoteviews_extra.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/remoteView_frame_extra"
+ android:layout_width="20dip"
+ android:layout_height="20dip" />
diff --git a/tests/tests/widget/res/layout/remoteviews_good.xml b/tests/tests/widget/res/layout/remoteviews_good.xml
index 8fdbc64..e322d0a 100644
--- a/tests/tests/widget/res/layout/remoteviews_good.xml
+++ b/tests/tests/widget/res/layout/remoteviews_good.xml
@@ -14,43 +14,79 @@
limitations under the License.
-->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/remoteViews_good"
- android:orientation="vertical"
android:layout_width="match_parent"
- android:layout_height="match_parent">
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
- <LinearLayout android:id="@+id/remoteView_linear"
+ <LinearLayout
+ android:id="@+id/remoteView_linear"
android:layout_width="10dip"
android:layout_height="10dip" />
- <TextView android:id="@+id/remoteView_text"
+ <TextView
+ android:id="@+id/remoteView_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
- <ImageView android:id="@+id/remoteView_image"
+ <TextView
+ android:id="@+id/remoteView_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
- <FrameLayout android:id="@+id/remoteView_frame"
+ <TextView
+ android:id="@+id/remoteView_text_ltr"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layoutDirection="ltr" />
+
+ <TextView
+ android:id="@+id/remoteView_text_rtl"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layoutDirection="rtl" />
+
+ <ImageView
+ android:id="@+id/remoteView_image"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+
+ <FrameLayout
+ android:id="@+id/remoteView_frame"
android:layout_width="10dip"
android:layout_height="10dip" />
- <RelativeLayout android:id="@+id/remoteView_relative"
+ <RelativeLayout
+ android:id="@+id/remoteView_relative"
android:layout_width="10dip"
android:layout_height="10dip" />
- <AbsoluteLayout android:id="@+id/remoteView_absolute"
+ <AbsoluteLayout
+ android:id="@+id/remoteView_absolute"
android:layout_width="10dip"
android:layout_height="10dip" />
- <ProgressBar android:id="@+id/remoteView_progress"
+ <ProgressBar
+ android:id="@+id/remoteView_progress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="?android:attr/progressBarStyleHorizontal" />
- <Chronometer android:id="@+id/remoteView_chronometer"
+ <Chronometer
+ android:id="@+id/remoteView_chronometer"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
-</LinearLayout>
+ <StackView
+ android:id="@+id/remoteView_stack"
+ android:layout_width="100dip"
+ android:layout_height="100dip" />
+
+ <android.widget.cts.MyRemotableView
+ android:id="@+id/remoteView_custom"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/tests/widget/res/layout/remoteviews_host.xml b/tests/tests/widget/res/layout/remoteviews_host.xml
index ace5903..ed0ba40 100644
--- a/tests/tests/widget/res/layout/remoteviews_host.xml
+++ b/tests/tests/widget/res/layout/remoteviews_host.xml
@@ -14,9 +14,9 @@
limitations under the License.
-->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/remoteView_host"
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:id="@+id/remoteView_host">
-</LinearLayout>
+ android:layout_height="match_parent">
+</FrameLayout>
diff --git a/tests/tests/widget/res/layout/searchview_layout.xml b/tests/tests/widget/res/layout/searchview_layout.xml
new file mode 100644
index 0000000..b8705e0
--- /dev/null
+++ b/tests/tests/widget/res/layout/searchview_layout.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <SearchView
+ android:id="@+id/search_view"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+ <SearchView
+ android:id="@+id/search_view_with_defaults"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:maxWidth="@dimen/search_view_max_width"
+ android:iconifiedByDefault="false"
+ android:queryHint="@string/search_query_hint"
+ android:inputType="textCapCharacters"
+ android:imeOptions="actionDone" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/tests/widget/res/layout/searchview_suggestion_item.xml b/tests/tests/widget/res/layout/searchview_suggestion_item.xml
new file mode 100644
index 0000000..91f02f5
--- /dev/null
+++ b/tests/tests/widget/res/layout/searchview_suggestion_item.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<TextView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@android:id/text1"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/search_view_suggestion_row_height"
+ android:textAppearance="?android:attr/textAppearanceListItemSmall"
+ android:gravity="center_vertical"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" />
diff --git a/tests/tests/widget/res/layout/simple_spinner_item_layout.xml b/tests/tests/widget/res/layout/simple_spinner_item_layout.xml
new file mode 100644
index 0000000..512b138
--- /dev/null
+++ b/tests/tests/widget/res/layout/simple_spinner_item_layout.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@android:id/text1"
+ style="?android:attr/spinnerItemStyle"
+ android:singleLine="true"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:ellipsize="marquee"
+ android:textAlignment="inherit"/>
diff --git a/tests/tests/widget/res/layout/spinner_layout.xml b/tests/tests/widget/res/layout/spinner_layout.xml
new file mode 100644
index 0000000..43a6411
--- /dev/null
+++ b/tests/tests/widget/res/layout/spinner_layout.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<ScrollView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:fillViewport="true">
+
+ <LinearLayout
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <Spinner
+ android:id="@+id/spinner_dropdown_mode"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:spinnerMode="dropdown"
+ android:prompt="@string/text_view_hello" />
+
+ <Spinner
+ android:id="@+id/spinner_dialog_mode"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:spinnerMode="dialog"
+ android:prompt="@string/text_view_hello" />
+
+ </LinearLayout>
+</ScrollView>
diff --git a/tests/tests/widget/res/layout/switch_layout.xml b/tests/tests/widget/res/layout/switch_layout.xml
index 12e10d2..880c6df 100644
--- a/tests/tests/widget/res/layout/switch_layout.xml
+++ b/tests/tests/widget/res/layout/switch_layout.xml
@@ -15,11 +15,31 @@
~ limitations under the License
-->
-<Switch xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/switch_view"
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <Switch
+ android:id="@+id/switch1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:thumbTint="@android:color/white"
android:thumbTintMode="src_over"
android:trackTint="@android:color/black"
android:trackTintMode="src_atop" />
+
+ <Switch
+ android:id="@+id/switch2"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:thumb="@drawable/icon_blue"
+ android:track="@drawable/red_translucent_fill" />
+
+ <Switch
+ android:id="@+id/switch3"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/tests/widget/res/layout/textview_layout.xml b/tests/tests/widget/res/layout/textview_layout.xml
index e3144eb..08354b1 100644
--- a/tests/tests/widget/res/layout/textview_layout.xml
+++ b/tests/tests/widget/res/layout/textview_layout.xml
@@ -14,209 +14,269 @@
limitations under the License.
-->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:id="@+id/layout_textviewtest">
+<ScrollView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/scroller"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:fillViewport="true">
- <ScrollView android:layout_width="match_parent"
- android:layout_height="match_parent">
+ <LinearLayout
+ android:id="@+id/layout_textviewtest"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
- <LinearLayout android:orientation="vertical"
+ <TextView
+ android:id="@+id/textview_textAttr"
+ android:fontFamily="@null"
+ android:text="@string/text_view_hello"
+ android:textColor="@drawable/black"
+ android:textColorHighlight="@drawable/yellow"
+ android:textColorHint="@drawable/red"
+ android:textColorLink="@drawable/blue"
+ android:textScaleX="1.2"
+ android:typeface="normal"
+ android:textSize="20px"
+ android:textStyle="normal"
+ android:textAppearance="@null"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+
+ <TextView
+ android:id="@+id/textview_password"
+ android:password="true"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+
+ <TextView
+ android:id="@+id/textview_singleLine"
+ android:singleLine="true"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+
+ <TextView
+ android:id="@+id/textview_text"
+ android:text="@string/text_view_hello"
+ android:breakStrategy="simple"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+
+ <TextView
+ android:id="@+id/textview_text_two_lines"
+ android:text="@string/text_view_hello_two_lines"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+
+ <android.widget.cts.MockTextView
+ android:id="@+id/mock_textview_left"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:requiresFadingEdge="horizontal"
+ android:singleLine="true"
+ android:text="@string/long_text"
+ android:gravity="left" />
+
+ <android.widget.cts.MockTextView
+ android:id="@+id/mock_textview_right"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:requiresFadingEdge="horizontal"
+ android:singleLine="true"
+ android:text="@string/long_text"
+ android:gravity="right" />
+
+ <android.widget.cts.MockTextView
+ android:id="@+id/mock_textview_center"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:requiresFadingEdge="horizontal"
+ android:singleLine="true"
+ android:text="@string/long_text"
+ android:gravity="center" />
+
+ <TextView
+ android:id="@+id/textview_ltr"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"/>
+
+ <TextView
+ android:id="@+id/textview_rtl"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"/>
+
+ <TextView
+ android:id="@+id/textview_drawable_1_1"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:drawableLeft="@drawable/icon_blue"
+ android:drawableRight="@drawable/icon_red"
+ android:drawableTop="@drawable/icon_green"
+ android:drawableBottom="@drawable/icon_yellow"
+ android:layoutDirection="ltr" />
+
+ <TextView
+ android:id="@+id/textview_drawable_1_2"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:drawableLeft="@drawable/icon_blue"
+ android:drawableRight="@drawable/icon_red"
+ android:drawableTop="@drawable/icon_green"
+ android:drawableBottom="@drawable/icon_yellow"
+ android:layoutDirection="rtl" />
+
+ <TextView
+ android:id="@+id/textview_drawable_2_1"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:drawableStart="@drawable/icon_blue"
+ android:drawableEnd="@drawable/icon_red"
+ android:drawableTop="@drawable/icon_green"
+ android:drawableBottom="@drawable/icon_yellow"
+ android:layoutDirection="ltr" />
+
+ <TextView
+ android:id="@+id/textview_drawable_2_2"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:drawableStart="@drawable/icon_blue"
+ android:drawableEnd="@drawable/icon_red"
+ android:drawableTop="@drawable/icon_green"
+ android:drawableBottom="@drawable/icon_yellow"
+ android:layoutDirection="rtl" />
+
+ <TextView
+ android:id="@+id/textview_drawable_3_1"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:drawableLeft="@drawable/icon_black"
+ android:drawableRight="@drawable/icon_black"
+ android:drawableStart="@drawable/icon_blue"
+ android:drawableEnd="@drawable/icon_red"
+ android:drawableTop="@drawable/icon_green"
+ android:drawableBottom="@drawable/icon_yellow"
+ android:layoutDirection="ltr" />
+
+ <TextView
+ android:id="@+id/textview_drawable_3_2"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:drawableLeft="@drawable/icon_black"
+ android:drawableRight="@drawable/icon_black"
+ android:drawableStart="@drawable/icon_blue"
+ android:drawableEnd="@drawable/icon_red"
+ android:drawableTop="@drawable/icon_green"
+ android:drawableBottom="@drawable/icon_yellow"
+ android:layoutDirection="rtl" />
+
+ <LinearLayout
+ android:orientation="vertical"
android:layout_width="match_parent"
- android:layout_height="match_parent">
+ android:layout_height="wrap_content"
+ android:layoutDirection="ltr">
- <TextView android:id="@+id/textview_textAttr"
- android:fontFamily="@null"
- android:text="@string/text_view_hello"
- android:textColor="@drawable/black"
- android:textColorHighlight="@drawable/yellow"
- android:textColorHint="@drawable/red"
- android:textColorLink="@drawable/blue"
- android:textScaleX="1.2"
- android:typeface="normal"
- android:textSize="20px"
- android:textStyle="normal"
- android:textAppearance="@null"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
-
- <TextView android:id="@+id/textview_password"
- android:password="true"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
-
- <TextView android:id="@+id/textview_singleLine"
- android:singleLine="true"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
-
- <TextView android:id="@+id/textview_text"
- android:text="@string/text_view_hello"
- android:breakStrategy="simple"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
-
- <TextView android:id="@+id/textview_text_two_lines"
- android:text="@string/text_view_hello_two_lines"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
-
- <android.widget.cts.MockTextView
- android:id="@+id/mock_textview_left"
- android:layout_width="wrap_content"
+ <TextView
+ android:id="@+id/textview_drawable_4_1"
android:layout_height="wrap_content"
- android:requiresFadingEdge="horizontal"
- android:singleLine="true"
- android:text="@string/long_text"
- android:gravity="left"
- />
-
- <android.widget.cts.MockTextView
- android:id="@+id/mock_textview_right"
android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:requiresFadingEdge="horizontal"
- android:singleLine="true"
- android:text="@string/long_text"
- android:gravity="right"
- />
+ android:drawableStart="@drawable/icon_blue"
+ android:drawableEnd="@drawable/icon_red"
+ android:drawableTop="@drawable/icon_green"
+ android:drawableBottom="@drawable/icon_yellow" />
- <android.widget.cts.MockTextView
- android:id="@+id/mock_textview_center"
- android:layout_width="wrap_content"
+ <TextView
+ android:id="@+id/textview_drawable_5_1"
android:layout_height="wrap_content"
- android:requiresFadingEdge="horizontal"
- android:singleLine="true"
- android:text="@string/long_text"
- android:gravity="center"
- />
+ android:layout_width="wrap_content"
+ android:drawableLeft="@drawable/icon_black"
+ android:drawableRight="@drawable/icon_black"
+ android:drawableStart="@drawable/icon_blue"
+ android:drawableEnd="@drawable/icon_red"
+ android:drawableTop="@drawable/icon_green"
+ android:drawableBottom="@drawable/icon_yellow" />
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layoutDirection="rtl">
+
+ <TextView
+ android:id="@+id/textview_drawable_4_2"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:drawableStart="@drawable/icon_blue"
+ android:drawableEnd="@drawable/icon_red"
+ android:drawableTop="@drawable/icon_green"
+ android:drawableBottom="@drawable/icon_yellow" />
+
+ <TextView
+ android:id="@+id/textview_drawable_5_2"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:drawableLeft="@drawable/icon_black"
+ android:drawableRight="@drawable/icon_black"
+ android:drawableStart="@drawable/icon_blue"
+ android:drawableEnd="@drawable/icon_red"
+ android:drawableTop="@drawable/icon_green"
+ android:drawableBottom="@drawable/icon_yellow" />
+
+ </LinearLayout>
+
+
+ <TextView
+ android:id="@+id/textview_compound_drawable_ltr"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:paddingLeft="@dimen/textview_padding_left"
+ android:paddingRight="@dimen/textview_padding_right"
+ android:paddingTop="@dimen/textview_padding_top"
+ android:paddingBottom="@dimen/textview_padding_bottom"
+ android:drawablePadding="@dimen/textview_drawable_padding"
+ android:drawableStart="@drawable/icon_blue"
+ android:drawableEnd="@drawable/icon_red"
+ android:drawableTop="@drawable/icon_green"
+ android:drawableBottom="@drawable/icon_yellow"
+ android:layoutDirection="ltr" />
+
+ <TextView
+ android:id="@+id/textview_compound_drawable_rtl"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:paddingLeft="@dimen/textview_padding_left"
+ android:paddingRight="@dimen/textview_padding_right"
+ android:paddingTop="@dimen/textview_padding_top"
+ android:paddingBottom="@dimen/textview_padding_bottom"
+ android:drawablePadding="@dimen/textview_drawable_padding"
+ android:drawableStart="@drawable/icon_blue"
+ android:drawableEnd="@drawable/icon_red"
+ android:drawableTop="@drawable/icon_green"
+ android:drawableBottom="@drawable/icon_yellow"
+ android:layoutDirection="rtl" />
+
+ <TextView
+ android:id="@+id/textview_with_shadow"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:text="@string/text_view_hello"
+ android:shadowDx="1.0"
+ android:shadowDy="2.0"
+ android:shadowRadius="3.0"
+ android:shadowColor="@color/testcolor1" />
+
+ <EditText
+ android:id="@+id/editview_text"
+ android:text="@string/text_view_hello"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+
+ <TextView
+ android:id="@+id/textview_pointer"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"/>
+
</LinearLayout>
- </ScrollView>
-
- <TextView android:id="@+id/textview_ltr"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"/>
-
- <TextView android:id="@+id/textview_rtl"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"/>
-
- <TextView android:id="@+id/textview_drawable_1_1"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:drawableLeft="@drawable/icon_blue"
- android:drawableRight="@drawable/icon_red"
- android:drawableTop="@drawable/icon_green"
- android:drawableBottom="@drawable/icon_yellow"
- />
-
- <TextView android:id="@+id/textview_drawable_1_2"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:drawableLeft="@drawable/icon_blue"
- android:drawableRight="@drawable/icon_red"
- android:drawableTop="@drawable/icon_green"
- android:drawableBottom="@drawable/icon_yellow"
- android:layoutDirection="rtl"
- />
-
- <TextView android:id="@+id/textview_drawable_2_1"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:drawableStart="@drawable/icon_blue"
- android:drawableEnd="@drawable/icon_red"
- android:drawableTop="@drawable/icon_green"
- android:drawableBottom="@drawable/icon_yellow"
- />
-
- <TextView android:id="@+id/textview_drawable_2_2"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:drawableStart="@drawable/icon_blue"
- android:drawableEnd="@drawable/icon_red"
- android:drawableTop="@drawable/icon_green"
- android:drawableBottom="@drawable/icon_yellow"
- android:layoutDirection="rtl"
- />
-
- <TextView android:id="@+id/textview_drawable_3_1"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:drawableLeft="@drawable/icon_black"
- android:drawableRight="@drawable/icon_black"
- android:drawableStart="@drawable/icon_blue"
- android:drawableEnd="@drawable/icon_red"
- android:drawableTop="@drawable/icon_green"
- android:drawableBottom="@drawable/icon_yellow"
- />
-
- <TextView android:id="@+id/textview_drawable_3_2"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:drawableLeft="@drawable/icon_black"
- android:drawableRight="@drawable/icon_black"
- android:drawableStart="@drawable/icon_blue"
- android:drawableEnd="@drawable/icon_red"
- android:drawableTop="@drawable/icon_green"
- android:drawableBottom="@drawable/icon_yellow"
- android:layoutDirection="rtl"
- />
-
-
- <LinearLayout android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layoutDirection="ltr">
-
- <TextView android:id="@+id/textview_drawable_4_1"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:drawableStart="@drawable/icon_blue"
- android:drawableEnd="@drawable/icon_red"
- android:drawableTop="@drawable/icon_green"
- android:drawableBottom="@drawable/icon_yellow"
- />
-
- <TextView android:id="@+id/textview_drawable_5_1"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:drawableLeft="@drawable/icon_black"
- android:drawableRight="@drawable/icon_black"
- android:drawableStart="@drawable/icon_blue"
- android:drawableEnd="@drawable/icon_red"
- android:drawableTop="@drawable/icon_green"
- android:drawableBottom="@drawable/icon_yellow"
- />
-
- </LinearLayout>
-
- <LinearLayout android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layoutDirection="rtl">
-
- <TextView android:id="@+id/textview_drawable_4_2"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:drawableStart="@drawable/icon_blue"
- android:drawableEnd="@drawable/icon_red"
- android:drawableTop="@drawable/icon_green"
- android:drawableBottom="@drawable/icon_yellow"
- />
-
- <TextView android:id="@+id/textview_drawable_5_2"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:drawableLeft="@drawable/icon_black"
- android:drawableRight="@drawable/icon_black"
- android:drawableStart="@drawable/icon_blue"
- android:drawableEnd="@drawable/icon_red"
- android:drawableTop="@drawable/icon_green"
- android:drawableBottom="@drawable/icon_yellow"
- />
-
- </LinearLayout>
-
-</LinearLayout>
+</ScrollView>
diff --git a/tests/tests/widget/res/layout/timepicker.xml b/tests/tests/widget/res/layout/timepicker.xml
index 352f69b..c545eba 100644
--- a/tests/tests/widget/res/layout/timepicker.xml
+++ b/tests/tests/widget/res/layout/timepicker.xml
@@ -14,6 +14,17 @@
limitations under the License.
-->
-<TimePicker xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+ <TimePicker
+ android:id="@+id/timepicker_clock"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+ <TimePicker
+ android:id="@+id/timepicker_spinner"
+ android:datePickerMode="spinner"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+</LinearLayout>
diff --git a/tests/tests/widget/res/layout/togglebutton_layout.xml b/tests/tests/widget/res/layout/togglebutton_layout.xml
index a6c08e1..de2faf3 100644
--- a/tests/tests/widget/res/layout/togglebutton_layout.xml
+++ b/tests/tests/widget/res/layout/togglebutton_layout.xml
@@ -15,8 +15,8 @@
* limitations under the License.
-->
-<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
+<ScrollView
+ xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
@@ -25,19 +25,35 @@
android:layout_width="match_parent"
android:layout_height="wrap_content">
- <ToggleButton android:id="@+id/toggle1"
+ <ToggleButton
+ android:id="@+id/toggle1"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
- <ToggleButton android:id="@+id/toggle2"
+ <ToggleButton
+ android:id="@+id/toggle2"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
- <ToggleButton android:id="@+id/button_tint"
+ <ToggleButton
+ android:id="@+id/toggle_with_defaults"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textOn="@string/toggle_text_on_alt"
+ android:textOff="@string/toggle_text_off_alt" />
+
+ <ToggleButton
+ style="@style/Theme_Toggle"
+ android:id="@+id/toggle_with_style"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+
+ <ToggleButton
+ android:id="@+id/button_tint"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
android:buttonTint="@android:color/white"
- android:buttonTintMode="src_over"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" />
+ android:buttonTintMode="src_over" />
</LinearLayout>
diff --git a/tests/tests/widget/res/layout/zoombutton_layout.xml b/tests/tests/widget/res/layout/zoombutton_layout.xml
index 1b1e0b2..49031a8 100644
--- a/tests/tests/widget/res/layout/zoombutton_layout.xml
+++ b/tests/tests/widget/res/layout/zoombutton_layout.xml
@@ -14,9 +14,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
-->
-<ZoomButton xmlns:android="http://schemas.android.com/apk/res/android"
+<ZoomButton
+ xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/zoombutton_test"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
-/>
+ android:src="@drawable/icon_red" />
diff --git a/tests/tests/widget/res/menu/cab_menu.xml b/tests/tests/widget/res/menu/cab_menu.xml
new file mode 100644
index 0000000..7e210cb
--- /dev/null
+++ b/tests/tests/widget/res/menu/cab_menu.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 Google Inc.
+
+ 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.
+-->
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:id="@+id/action_highlight"
+ android:title="@string/popup_menu_highlight"
+ android:icon="@drawable/ic_highlight"
+ android:showAsAction="always" />
+ <item android:id="@+id/action_delete"
+ android:title="@string/popup_menu_delete"
+ android:icon="@drawable/ic_delete"
+ android:showAsAction="always" />
+</menu>
diff --git a/tests/tests/widget/res/values/attrs.xml b/tests/tests/widget/res/values/attrs.xml
index 4c3d9db..b2bea6f 100644
--- a/tests/tests/widget/res/values/attrs.xml
+++ b/tests/tests/widget/res/values/attrs.xml
@@ -142,4 +142,7 @@
<attr name="themeGravity" />
<attr name="themeTileMode" />
<attr name="themeAngle" />
+
+ <attr name="chronometerStyle" format="string" />
+
</resources>
diff --git a/tests/tests/widget/res/values/dimens.xml b/tests/tests/widget/res/values/dimens.xml
index 3690039..7cbff3a 100644
--- a/tests/tests/widget/res/values/dimens.xml
+++ b/tests/tests/widget/res/values/dimens.xml
@@ -15,4 +15,33 @@
-->
<resources>
<dimen name="popup_row_height">48dp</dimen>
+
+ <dimen name="switch_padding">24dp</dimen>
+ <dimen name="switch_thumb_text_padding">12dp</dimen>
+ <dimen name="switch_min_width">160dp</dimen>
+ <dimen name="switch_min_width2">200dp</dimen>
+
+ <dimen name="autocomplete_textview_dropdown_height">120dp</dimen>
+ <dimen name="autocomplete_textview_dropdown_width">160dp</dimen>
+ <dimen name="autocomplete_textview_dropdown_offset_h">24dp</dimen>
+ <dimen name="autocomplete_textview_dropdown_offset_v">32dp</dimen>
+
+ <dimen name="spinner_dropdown_width">200dp</dimen>
+ <dimen name="spinner_dropdown_offset_h">64dp</dimen>
+ <dimen name="spinner_dropdown_offset_v">48dp</dimen>
+
+ <dimen name="search_view_max_width">160dp</dimen>
+ <dimen name="search_view_max_width2">200dp</dimen>
+ <dimen name="search_view_suggestion_row_height">48dp</dimen>
+
+ <dimen name="linear_layout_divider_size">10px</dimen>
+ <dimen name="linear_layout_divider_padding">8px</dimen>
+
+ <dimen name="textview_padding_left">4dip</dimen>
+ <dimen name="textview_padding_right">6dip</dimen>
+ <dimen name="textview_padding_start">8dip</dimen>
+ <dimen name="textview_padding_end">10dip</dimen>
+ <dimen name="textview_padding_top">2dip</dimen>
+ <dimen name="textview_padding_bottom">4dip</dimen>
+ <dimen name="textview_drawable_padding">2dip</dimen>
</resources>
\ No newline at end of file
diff --git a/tests/tests/widget/res/values/strings.xml b/tests/tests/widget/res/values/strings.xml
index 63ceffa..3820072 100644
--- a/tests/tests/widget/res/values/strings.xml
+++ b/tests/tests/widget/res/values/strings.xml
@@ -193,4 +193,15 @@
<string name="toolbar_subtitle">Toolbar subtitle</string>
<string name="toolbar_navigation">Toolbar navigation</string>
<string name="toolbar_logo">Toolbar logo</string>
+
+ <string name="search_query_hint">query hint</string>
+
+ <string name="remote_content_description">remote description</string>
+
+ <string name="toggle_text_on">On</string>
+ <string name="toggle_text_off">Off</string>
+ <string name="toggle_text_on_alt">On Alt</string>
+ <string name="toggle_text_off_alt">Off Alt</string>
+
+ <string name="chronometer_format">Current count: %s</string>
</resources>
diff --git a/tests/tests/widget/res/values/styles.xml b/tests/tests/widget/res/values/styles.xml
index 345b450..bddc891 100644
--- a/tests/tests/widget/res/values/styles.xml
+++ b/tests/tests/widget/res/values/styles.xml
@@ -202,4 +202,19 @@
</style>
<style name="ToolbarPopupTheme_Test" parent="@android:style/ThemeOverlay.Material.Light" />
+
+ <style name="Theme_Toggle" parent="@android:style/Widget.Material.Light.Button.Toggle">
+ <item name="android:textOn">@string/toggle_text_on</item>
+ <item name="android:textOff">@string/toggle_text_off</item>
+ </style>
+
+ <style name="ChronometerStyle" parent="@android:style/Widget.Material.Light.TextView">
+ <item name="android:format">@string/chronometer_format</item>
+ <item name="android:countDown">true</item>
+ </style>
+
+ <style name="ChronometerAwareTheme" parent="@android:style/Theme.Material.Light">
+ <item name="chronometerStyle">@style/ChronometerStyle</item>
+ </style>
+
</resources>
diff --git a/tests/tests/widget/res/xml/remoteviews_appwidget_info.xml b/tests/tests/widget/res/xml/remoteviews_appwidget_info.xml
new file mode 100644
index 0000000..e75ed72
--- /dev/null
+++ b/tests/tests/widget/res/xml/remoteviews_appwidget_info.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ 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.
+-->
+<appwidget-provider
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:minWidth="200dp"
+ android:minHeight="200dp"
+ android:minResizeWidth="300dp"
+ android:minResizeHeight="300dp"
+ android:updatePeriodMillis="86400000"
+ android:initialLayout="@layout/remoteviews_adapter"
+ android:resizeMode="horizontal|vertical"
+ android:widgetCategory="home_screen|keyguard"
+ android:previewImage="@drawable/icon_red">
+</appwidget-provider>
diff --git a/tests/tests/widget/src/android/widget/cts/AbsListViewTest.java b/tests/tests/widget/src/android/widget/cts/AbsListViewTest.java
index cf8012e..4e5d1a1 100644
--- a/tests/tests/widget/src/android/widget/cts/AbsListViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/AbsListViewTest.java
@@ -16,99 +16,134 @@
package android.widget.cts;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.widget.cts.R;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.verifyZeroInteractions;
import android.app.Activity;
import android.app.Instrumentation;
import android.content.Context;
+import android.cts.util.CtsTouchUtils;
import android.cts.util.PollingCheck;
import android.cts.util.WidgetTestUtils;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
-import android.test.ActivityInstrumentationTestCase2;
-import android.test.TouchUtils;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.test.suitebuilder.annotation.SmallTest;
import android.text.Editable;
import android.text.SpannableStringBuilder;
+import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Xml;
+import android.view.ActionMode;
import android.view.ContextMenu.ContextMenuInfo;
+import android.view.Menu;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
-import android.widget.AbsListView.RecyclerListener;
-import android.widget.AdapterView;
-import android.widget.AdapterView.OnItemLongClickListener;
import android.widget.ArrayAdapter;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.TextView;
+import android.widget.cts.util.TestUtils;
+import android.widget.cts.util.TestUtilsMatchers;
+
+import org.hamcrest.MatcherAssert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.InOrder;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
-public class AbsListViewTest extends ActivityInstrumentationTestCase2<ListViewCtsActivity> {
- private final String[] mShortList = new String[] {
- "This", "is", "short", "!",
- };
- private final String[] mCountryList = new String[] {
- "Argentina", "Australia", "China", "France", "Germany", "Italy", "Japan", "United States",
- "Argentina", "Australia", "China", "France", "Germany", "Italy", "Japan", "United States",
- "Argentina", "Australia", "China", "France", "Germany", "Italy", "Japan", "United States"
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class AbsListViewTest {
+ private static final String[] SHORT_LIST = new String[] { "This", "is", "short", "!" };
+
+ private static final String[] COUNTRY_LIST = new String[] {
+ "Argentina", "Armenia", "Aruba", "Australia", "Belarus", "Belgium", "Belize", "Benin",
+ "Botswana", "Brazil", "Cameroon", "China", "Colombia", "Costa Rica", "Cyprus",
+ "Denmark", "Djibouti", "Ethiopia", "Fiji", "Finland", "France", "Gabon", "Germany",
+ "Ghana", "Haiti", "Honduras", "Iceland", "India", "Indonesia", "Ireland", "Italy",
+ "Japan", "Kiribati", "Laos", "Lesotho", "Liberia", "Malaysia", "Mongolia", "Myanmar",
+ "Nauru", "Norway", "Oman", "Pakistan", "Philippines", "Portugal", "Romania", "Russia",
+ "Rwanda", "Singapore", "Slovakia", "Slovenia", "Somalia", "Swaziland", "Togo", "Tuvalu",
+ "Uganda", "Ukraine", "United States", "Vanuatu", "Venezuela", "Zimbabwe"
};
- private ListView mListView;
- private Activity mActivity;
+ @Rule
+ public ActivityTestRule<ListViewCtsActivity> mActivityRule
+ = new ActivityTestRule<>(ListViewCtsActivity.class);
+
+ private AbsListView mListView;
+ private Context mContext;
private Instrumentation mInstrumentation;
private AttributeSet mAttributeSet;
- private ArrayAdapter<String> mAdapter_short;
- private ArrayAdapter<String> mAdapter_countries;
+ private ArrayAdapter<String> mShortAdapter;
+ private ArrayAdapter<String> mCountriesAdapter;
+ private AbsListView.MultiChoiceModeListener mMultiChoiceModeListener;
private static final float DELTA = 0.001f;
- public AbsListViewTest() {
- super("android.widget.cts", ListViewCtsActivity.class);
- }
+ @Before
+ public void setup() throws Exception {
+ mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ mContext = mInstrumentation.getTargetContext();
- @Override
- protected void setUp() throws Exception {
- super.setUp();
+ final Activity activity = mActivityRule.getActivity();
- mActivity = getActivity();
- new PollingCheck() {
- @Override
- protected boolean check() {
- return mActivity.hasWindowFocus();
- }
- }.run();
- mInstrumentation = getInstrumentation();
+ PollingCheck.waitFor(activity::hasWindowFocus);
- XmlPullParser parser = mActivity.getResources().getXml(R.layout.listview_layout);
- WidgetTestUtils.beginDocument(parser, "LinearLayout");
+ XmlPullParser parser = mContext.getResources().getXml(R.layout.listview_layout);
+ WidgetTestUtils.beginDocument(parser, "FrameLayout");
mAttributeSet = Xml.asAttributeSet(parser);
- mAdapter_short = new ArrayAdapter<String>(mActivity,
- android.R.layout.simple_list_item_1, mShortList);
- mAdapter_countries = new ArrayAdapter<String>(mActivity,
- android.R.layout.simple_list_item_1, mCountryList);
+ mShortAdapter = new ArrayAdapter<>(mContext,
+ android.R.layout.simple_list_item_1, SHORT_LIST);
+ mCountriesAdapter = new ArrayAdapter<>(mContext,
+ android.R.layout.simple_list_item_1, COUNTRY_LIST);
- mListView = (ListView)mActivity.findViewById(R.id.listview_default);
+ mListView = (ListView) activity.findViewById(R.id.listview_default);
}
- public void testConstructor() {
- /**
- * We can not test the constructors.
- */
- }
-
+ @Test
public void testAccessFastScrollEnabled() {
mListView.setFastScrollEnabled(false);
assertFalse(mListView.isFastScrollEnabled());
@@ -117,6 +152,7 @@
assertTrue(mListView.isFastScrollEnabled());
}
+ @Test
public void testAccessSmoothScrollbarEnabled() {
mListView.setSmoothScrollbarEnabled(false);
assertFalse(mListView.isSmoothScrollbarEnabled());
@@ -125,6 +161,7 @@
assertTrue(mListView.isSmoothScrollbarEnabled());
}
+ @Test
public void testAccessScrollingCacheEnabled() {
mListView.setScrollingCacheEnabled(false);
assertFalse(mListView.isScrollingCacheEnabled());
@@ -133,117 +170,128 @@
assertTrue(mListView.isScrollingCacheEnabled());
}
- private void setAdapter() throws Throwable {
- setAdapter(mAdapter_countries);
+ private void setAdapter() {
+ setAdapter(mCountriesAdapter);
}
- private void setAdapter(final ListAdapter adapter) throws Throwable {
- runTestOnUiThread(new Runnable() {
- public void run() {
- mListView.setAdapter(adapter);
- }
- });
+ private void setAdapter(final ListAdapter adapter) {
+ mInstrumentation.runOnMainSync(() -> mListView.setAdapter(adapter));
mInstrumentation.waitForIdleSync();
}
- private void setListSelection(int index) throws Throwable {
- final int i = index;
-
- runTestOnUiThread(new Runnable() {
- public void run() {
- mListView.setSelection(i);
- }
- });
+ private void setListSelection(int index) {
+ mInstrumentation.runOnMainSync(() -> mListView.setSelection(index));
mInstrumentation.waitForIdleSync();
}
- public void testSetOnScrollListener() throws Throwable {
- MockOnScrollListener onScrollListener = new MockOnScrollListener();
- assertNull(onScrollListener.getView());
- assertEquals(0, onScrollListener.getFirstVisibleItem());
- assertEquals(0, onScrollListener.getVisibleItemCount());
- assertEquals(0, onScrollListener.getTotalItemCount());
- assertEquals(-1, onScrollListener.getScrollState());
+ @LargeTest
+ @Test
+ public void testSetOnScrollListener() {
+ AbsListView.OnScrollListener mockScrollListener =
+ mock(AbsListView.OnScrollListener.class);
- assertFalse(onScrollListener.isOnScrollCalled());
- assertFalse(onScrollListener.isOnScrollStateChangedCalled());
+ verifyZeroInteractions(mockScrollListener);
- mListView.setOnScrollListener(onScrollListener);
- assertSame(mListView, onScrollListener.getView());
- assertEquals(0, onScrollListener.getFirstVisibleItem());
- assertEquals(0, onScrollListener.getVisibleItemCount());
- assertEquals(0, onScrollListener.getTotalItemCount());
- assertEquals(-1, onScrollListener.getScrollState());
+ mListView.setOnScrollListener(mockScrollListener);
+ verify(mockScrollListener, times(1)).onScroll(mListView, 0, 0, 0);
+ verifyNoMoreInteractions(mockScrollListener);
- assertTrue(onScrollListener.isOnScrollCalled());
- assertFalse(onScrollListener.isOnScrollStateChangedCalled());
- onScrollListener.reset();
+ reset(mockScrollListener);
setAdapter();
+ verify(mockScrollListener, times(1)).onScroll(mListView, 0, mListView.getChildCount(),
+ COUNTRY_LIST.length);
+ verifyNoMoreInteractions(mockScrollListener);
- assertSame(mListView, onScrollListener.getView());
- assertEquals(0, onScrollListener.getFirstVisibleItem());
- assertEquals(mListView.getChildCount(), onScrollListener.getVisibleItemCount());
- assertEquals(mCountryList.length, onScrollListener.getTotalItemCount());
- assertEquals(-1, onScrollListener.getScrollState());
+ reset(mockScrollListener);
- assertTrue(onScrollListener.isOnScrollCalled());
- assertFalse(onScrollListener.isOnScrollStateChangedCalled());
- onScrollListener.reset();
+ CtsTouchUtils.emulateScrollToBottom(mInstrumentation, mListView);
- TouchUtils.scrollToBottom(this, mActivity, mListView);
- assertSame(mListView, onScrollListener.getView());
- assertEquals(mListView.getChildCount(), onScrollListener.getVisibleItemCount());
- assertEquals(mCountryList.length, onScrollListener.getTotalItemCount());
+ ArgumentCaptor<Integer> firstVisibleItemCaptor = ArgumentCaptor.forClass(Integer.class);
+ ArgumentCaptor<Integer> visibleItemCountCaptor = ArgumentCaptor.forClass(Integer.class);
+ verify(mockScrollListener, atLeastOnce()).onScroll(eq(mListView),
+ firstVisibleItemCaptor.capture(), visibleItemCountCaptor.capture(),
+ eq(COUNTRY_LIST.length));
- assertTrue(onScrollListener.isOnScrollCalled());
- assertTrue(onScrollListener.isOnScrollStateChangedCalled());
+ // We expect the first visible item values to be increasing
+ MatcherAssert.assertThat(firstVisibleItemCaptor.getAllValues(),
+ TestUtilsMatchers.inAscendingOrder());
+ // The number of visible items during scrolling may change depending on the specific
+ // scroll position. As such we only test this number at the very end
+ final List<Integer> capturedVisibleItemCounts = visibleItemCountCaptor.getAllValues();
+ assertEquals(mListView.getChildCount(),
+ (int) capturedVisibleItemCounts.get(capturedVisibleItemCounts.size() - 1));
+
+ ArgumentCaptor<Integer> scrollStateCaptor = ArgumentCaptor.forClass(Integer.class);
+ verify(mockScrollListener, atLeastOnce()).onScrollStateChanged(eq(mListView),
+ scrollStateCaptor.capture());
+
+ // Verify that the last scroll state is IDLE
+ final List<Integer> capturedScrollStates = scrollStateCaptor.getAllValues();
+ assertEquals(AbsListView.OnScrollListener.SCROLL_STATE_IDLE,
+ (int) capturedScrollStates.get(capturedScrollStates.size() - 1));
}
- public void testFling() throws Throwable {
- MockOnScrollListener onScrollListener = new MockOnScrollListener();
- mListView.setOnScrollListener(onScrollListener);
+ @LargeTest
+ @Test
+ public void testFling() {
+ AbsListView.OnScrollListener mockScrollListener = mock(AbsListView.OnScrollListener.class);
+ mListView.setOnScrollListener(mockScrollListener);
setAdapter();
// Fling down from top, expect a scroll.
- fling(10000, onScrollListener);
- assertTrue(onScrollListener.isOnScrollCalled());
- assertTrue(0 < onScrollListener.getFirstVisibleItem());
+ fling(10000, mockScrollListener);
+ ArgumentCaptor<Integer> firstVisibleItemCaptor = ArgumentCaptor.forClass(Integer.class);
+ verify(mockScrollListener, atLeastOnce()).onScroll(eq(mListView),
+ firstVisibleItemCaptor.capture(), anyInt(), eq(COUNTRY_LIST.length));
+ List<Integer> capturedFirstVisibleItems = firstVisibleItemCaptor.getAllValues();
+ assertTrue(capturedFirstVisibleItems.get(capturedFirstVisibleItems.size() - 1) > 0);
// Fling up the same amount, expect a scroll to the original position.
- fling(-10000, onScrollListener);
- assertTrue(onScrollListener.isOnScrollCalled());
- assertEquals(0, onScrollListener.getFirstVisibleItem());
+ fling(-10000, mockScrollListener);
+ firstVisibleItemCaptor = ArgumentCaptor.forClass(Integer.class);
+ verify(mockScrollListener, atLeastOnce()).onScroll(eq(mListView),
+ firstVisibleItemCaptor.capture(), anyInt(), eq(COUNTRY_LIST.length));
+ capturedFirstVisibleItems = firstVisibleItemCaptor.getAllValues();
+ assertTrue(capturedFirstVisibleItems.get(capturedFirstVisibleItems.size() - 1) == 0);
// Fling up again, expect no scroll, as the viewport is already at top.
- fling(-10000, onScrollListener);
- assertFalse(onScrollListener.isOnScrollCalled());
- assertEquals(0, onScrollListener.getFirstVisibleItem());
+ fling(-10000, mockScrollListener);
+ verify(mockScrollListener, never()).onScroll(any(AbsListView.class), anyInt(), anyInt(),
+ anyInt());
// Fling up again with a huge velocity, expect no scroll.
- fling(-50000, onScrollListener);
- assertFalse(onScrollListener.isOnScrollCalled());
- assertEquals(0, onScrollListener.getFirstVisibleItem());
+ fling(-50000, mockScrollListener);
+ verify(mockScrollListener, never()).onScroll(any(AbsListView.class), anyInt(), anyInt(),
+ anyInt());
}
- private void fling(int velocityY, MockOnScrollListener onScrollListener) throws Throwable {
- onScrollListener.reset();
+ private void fling(int velocityY, OnScrollListener mockScrollListener) {
+ reset(mockScrollListener);
- final int v = velocityY;
- runTestOnUiThread(new Runnable() {
- public void run() {
- mListView.fling(v);
- }
- });
+ // Create a count down latch and configure it to be counted down when our mock
+ // listener is invoked with IDLE state
+ final CountDownLatch countDownLatch = new CountDownLatch(1);
+ doAnswer((InvocationOnMock invocation) -> {
+ countDownLatch.countDown();
+ return null;
+ }).when(mockScrollListener).onScrollStateChanged(
+ mListView, OnScrollListener.SCROLL_STATE_IDLE);
- do {
- mInstrumentation.waitForIdleSync();
- } while (onScrollListener.getScrollState() != OnScrollListener.SCROLL_STATE_IDLE);
+ // Now fling the list view
+ mInstrumentation.runOnMainSync(() -> mListView.fling(velocityY));
+
+ // And wait for the latch to be triggered
+ try {
+ assertTrue(countDownLatch.await(20L, TimeUnit.SECONDS));
+ } catch (InterruptedException e) {
+ }
}
- public void testGetFocusedRect() throws Throwable {
- setAdapter(mAdapter_short);
+ @Test
+ public void testGetFocusedRect() {
+ setAdapter(mShortAdapter);
setListSelection(0);
Rect r1 = new Rect();
@@ -267,46 +315,48 @@
assertEquals(r1.right, r2.right);
}
- public void testAccessStackFromBottom() throws Throwable {
+ @Test
+ public void testAccessStackFromBottom() {
setAdapter();
- runTestOnUiThread(new Runnable() {
- public void run() {
- mListView.setStackFromBottom(false);
- }
- });
+ mInstrumentation.runOnMainSync(() -> mListView.setStackFromBottom(false));
assertFalse(mListView.isStackFromBottom());
assertEquals(0, mListView.getSelectedItemPosition());
- runTestOnUiThread(new Runnable() {
- public void run() {
- mListView.setStackFromBottom(true);
- }
- });
+ mInstrumentation.runOnMainSync(() -> mListView.setStackFromBottom(true));
mInstrumentation.waitForIdleSync();
assertTrue(mListView.isStackFromBottom());
// ensure last item in list is selected
- assertEquals(mCountryList.length-1, mListView.getSelectedItemPosition());
+ assertEquals(COUNTRY_LIST.length-1, mListView.getSelectedItemPosition());
}
- public void testAccessSelectedItem() throws Throwable {
+ @Test
+ public void testAccessSelectedItem() {
assertNull(mListView.getSelectedView());
setAdapter();
+
+ final int lastVisiblePosition = mListView.getLastVisiblePosition();
+
TextView tv = (TextView) mListView.getSelectedView();
- assertEquals(mCountryList[0], tv.getText().toString());
+ assertEquals(COUNTRY_LIST[0], tv.getText().toString());
- setListSelection(5);
- tv = (TextView) mListView.getSelectedView();
- assertEquals(mCountryList[5], tv.getText().toString());
+ if (lastVisiblePosition >= 5) {
+ setListSelection(5);
+ tv = (TextView) mListView.getSelectedView();
+ assertEquals(COUNTRY_LIST[5], tv.getText().toString());
+ }
- setListSelection(2);
- tv = (TextView) mListView.getSelectedView();
- assertEquals(mCountryList[2], tv.getText().toString());
+ if (lastVisiblePosition >= 2) {
+ setListSelection(2);
+ tv = (TextView) mListView.getSelectedView();
+ assertEquals(COUNTRY_LIST[2], tv.getText().toString());
+ }
}
- public void testAccessListPadding() throws Throwable {
+ @Test
+ public void testAccessListPadding() {
setAdapter();
assertEquals(0, mListView.getListPaddingLeft());
@@ -315,11 +365,8 @@
assertEquals(0, mListView.getListPaddingBottom());
final Rect r = new Rect(0, 0, 40, 60);
- runTestOnUiThread(new Runnable() {
- public void run() {
- mListView.setPadding(r.left, r.top, r.right, r.bottom);
- }
- });
+ mInstrumentation.runOnMainSync(
+ () -> mListView.setPadding(r.left, r.top, r.right, r.bottom));
mInstrumentation.waitForIdleSync();
assertEquals(r.left, mListView.getListPaddingLeft());
@@ -328,17 +375,14 @@
assertEquals(r.bottom, mListView.getListPaddingBottom());
}
- public void testAccessSelector() throws Throwable {
+ @Test
+ public void testAccessSelector() {
setAdapter();
- final Drawable d = mActivity.getResources().getDrawable(R.drawable.pass);
+ final Drawable d = mContext.getDrawable(R.drawable.pass);
mListView.setSelector(d);
- runTestOnUiThread(new Runnable() {
- public void run() {
- mListView.requestLayout();
- }
- });
+ mInstrumentation.runOnMainSync(mListView::requestLayout);
mInstrumentation.waitForIdleSync();
assertSame(d, mListView.getSelector());
assertTrue(mListView.verifyDrawable(d));
@@ -346,11 +390,7 @@
mListView.setSelector(R.drawable.failed);
mListView.setDrawSelectorOnTop(true);
- runTestOnUiThread(new Runnable() {
- public void run() {
- mListView.requestLayout();
- }
- });
+ mInstrumentation.runOnMainSync(mListView::requestLayout);
mInstrumentation.waitForIdleSync();
Drawable drawable = mListView.getSelector();
@@ -358,34 +398,28 @@
final Rect r = drawable.getBounds();
final TextView v = (TextView) mListView.getSelectedView();
- new PollingCheck() {
- @Override
- protected boolean check() {
- return v.getRight() == r.right;
- }
- }.run();
+ PollingCheck.waitFor(() -> v.getRight() == r.right);
assertEquals(v.getLeft(), r.left);
assertEquals(v.getTop(), r.top);
assertEquals(v.getBottom(), r.bottom);
}
- public void testSetScrollIndicators() throws Throwable {
- TextView tv1 = (TextView) mActivity.findViewById(R.id.headerview1);
- TextView tv2 = (TextView) mActivity.findViewById(R.id.footerview1);
+ @Test
+ public void testSetScrollIndicators() {
+ final Activity activity = mActivityRule.getActivity();
+ TextView tv1 = (TextView) activity.findViewById(R.id.headerview1);
+ TextView tv2 = (TextView) activity.findViewById(R.id.footerview1);
setAdapter();
mListView.setScrollIndicators(tv1, tv2);
- runTestOnUiThread(new Runnable() {
- public void run() {
- mListView.requestLayout();
- }
- });
+ mInstrumentation.runOnMainSync(mListView::requestLayout);
mInstrumentation.waitForIdleSync();
}
- public void testShowContextMenuForChild() throws Throwable {
+ @Test
+ public void testShowContextMenuForChild() {
setAdapter();
setListSelection(1);
@@ -395,7 +429,8 @@
// TODO: how to show the contextMenu success
}
- public void testPointToPosition() throws Throwable {
+ @Test
+ public void testPointToPosition() {
assertEquals(AbsListView.INVALID_POSITION, mListView.pointToPosition(-1, -1));
assertEquals(AbsListView.INVALID_ROW_ID, mListView.pointToRowId(-1, -1));
@@ -408,39 +443,37 @@
int position1 = mListView.pointToPosition(0, 0);
int position2 = mListView.pointToPosition(50, middleOfSecondRow);
- assertEquals(mAdapter_countries.getItemId(position1), mListView.pointToRowId(0, 0));
- assertEquals(mAdapter_countries.getItemId(position2),
+ assertEquals(mCountriesAdapter.getItemId(position1), mListView.pointToRowId(0, 0));
+ assertEquals(mCountriesAdapter.getItemId(position2),
mListView.pointToRowId(50, middleOfSecondRow));
assertTrue(position2 > position1);
}
- public void testDraw() {
- Canvas canvas = new Canvas();
- mListView.draw(canvas);
-
- MyListView listView = new MyListView(mActivity);
- listView.dispatchDraw(canvas);
-
- // TODO: how to check
- }
-
- public void testSetRecyclerListener() throws Throwable {
+ @Test
+ public void testSetRecyclerListener() {
setAdapter();
- MockRecyclerListener recyclerListener = new MockRecyclerListener();
- List<View> views = new ArrayList<View>();
+ AbsListView.RecyclerListener mockRecyclerListener =
+ mock(AbsListView.RecyclerListener.class);
+ verifyZeroInteractions(mockRecyclerListener);
- assertNull(recyclerListener.getView());
- mListView.setRecyclerListener(recyclerListener);
+ mListView.setRecyclerListener(mockRecyclerListener);
+ List<View> views = new ArrayList<>();
mListView.reclaimViews(views);
assertTrue(views.size() > 0);
- assertNotNull(recyclerListener.getView());
- assertSame(recyclerListener.getView(), views.get(views.size() - 1));
+ // Verify that onMovedToScrapHeap was called on each view in the order that they were
+ // put in the list that we passed to reclaimViews
+ final InOrder reclaimedOrder = inOrder(mockRecyclerListener);
+ for (View reclaimed : views) {
+ reclaimedOrder.verify(mockRecyclerListener, times(1)).onMovedToScrapHeap(reclaimed);
+ }
+ verifyNoMoreInteractions(mockRecyclerListener);
}
+ @Test
public void testAccessCacheColorHint() {
mListView.setCacheColorHint(Color.RED);
assertEquals(Color.RED, mListView.getCacheColorHint());
@@ -455,6 +488,7 @@
assertEquals(Color.GRAY, mListView.getSolidColor());
}
+ @Test
public void testAccessTranscriptMode() {
mListView.setTranscriptMode(AbsListView.TRANSCRIPT_MODE_ALWAYS_SCROLL);
assertEquals(AbsListView.TRANSCRIPT_MODE_ALWAYS_SCROLL, mListView.getTranscriptMode());
@@ -466,8 +500,9 @@
assertEquals(AbsListView.TRANSCRIPT_MODE_NORMAL, mListView.getTranscriptMode());
}
+ @Test
public void testCheckLayoutParams() {
- MyListView listView = new MyListView(mActivity);
+ MyListView listView = new MyListView(mContext);
AbsListView.LayoutParams param1 = new AbsListView.LayoutParams(10, 10);
assertTrue(listView.checkLayoutParams(param1));
@@ -476,15 +511,16 @@
assertFalse(listView.checkLayoutParams(param2));
}
+ @Test
public void testComputeVerticalScrollValues() {
- MyListView listView = new MyListView(mActivity);
+ MyListView listView = new MyListView(mContext);
assertEquals(0, listView.computeVerticalScrollRange());
assertEquals(0, listView.computeVerticalScrollOffset());
assertEquals(0, listView.computeVerticalScrollExtent());
- listView.setAdapter(mAdapter_countries);
+ listView.setAdapter(mCountriesAdapter);
listView.setSmoothScrollbarEnabled(false);
- assertEquals(mAdapter_countries.getCount(), listView.computeVerticalScrollRange());
+ assertEquals(mCountriesAdapter.getCount(), listView.computeVerticalScrollRange());
assertEquals(0, listView.computeVerticalScrollOffset());
assertEquals(0, listView.computeVerticalScrollExtent());
@@ -493,14 +529,15 @@
assertEquals(0, listView.computeVerticalScrollExtent());
}
+ @Test
public void testGenerateLayoutParams() throws XmlPullParserException, IOException {
ViewGroup.LayoutParams res = mListView.generateLayoutParams(mAttributeSet);
assertNotNull(res);
assertTrue(res instanceof AbsListView.LayoutParams);
- MyListView listView = new MyListView(mActivity);
- ViewGroup.LayoutParams p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.WRAP_CONTENT);
+ MyListView listView = new MyListView(mContext);
+ ViewGroup.LayoutParams p = new ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
res = listView.generateLayoutParams(p);
assertNotNull(res);
@@ -509,6 +546,7 @@
assertEquals(ViewGroup.LayoutParams.WRAP_CONTENT, res.height);
}
+ @Test
public void testBeforeAndAfterTextChanged() {
// The java doc says these two methods do nothing
CharSequence str = "test";
@@ -518,8 +556,8 @@
mListView.afterTextChanged(sb);
// test callback
- MyListView listView = new MyListView(mActivity);
- TextView tv = new TextView(mActivity);
+ MyListView listView = new MyListView(mContext);
+ TextView tv = new TextView(mContext);
assertFalse(listView.isBeforeTextChangedCalled());
assertFalse(listView.isOnTextChangedCalled());
@@ -536,8 +574,9 @@
assertTrue(listView.isAfterTextChangedCalled());
}
- public void testAddTouchables() throws Throwable {
- ArrayList<View> views = new ArrayList<View>();
+ @Test
+ public void testAddTouchables() {
+ ArrayList<View> views = new ArrayList<>();
assertEquals(0, views.size());
setAdapter();
@@ -546,78 +585,88 @@
assertEquals(mListView.getChildCount(), views.size());
}
- public void testInvalidateViews() throws Throwable {
- TextView tv1 = (TextView) mActivity.findViewById(R.id.headerview1);
- TextView tv2 = (TextView) mActivity.findViewById(R.id.footerview1);
+ @Test
+ public void testInvalidateViews() {
+ final Activity activity = mActivityRule.getActivity();
+ TextView tv1 = (TextView) activity.findViewById(R.id.headerview1);
+ TextView tv2 = (TextView) activity.findViewById(R.id.footerview1);
setAdapter();
mListView.setScrollIndicators(tv1, tv2);
- runTestOnUiThread(new Runnable() {
- public void run() {
- mListView.invalidateViews();
- }
- });
+ mInstrumentation.runOnMainSync(mListView::invalidateViews);
mInstrumentation.waitForIdleSync();
}
- public void testGetContextMenuInfo() throws Throwable {
- final MyListView listView = new MyListView(mActivity, mAttributeSet);
+ @Test
+ public void testGetContextMenuInfo() {
+ final MyListView listView = new MyListView(mContext, mAttributeSet);
- runTestOnUiThread(new Runnable() {
- public void run() {
- mActivity.setContentView(listView);
- listView.setAdapter(mAdapter_countries);
- listView.setSelection(2);
- }
+ mInstrumentation.runOnMainSync(() -> {
+ mActivityRule.getActivity().setContentView(listView);
+ listView.setAdapter(mCountriesAdapter);
+ listView.setSelection(2);
});
mInstrumentation.waitForIdleSync();
final TextView v = (TextView) listView.getSelectedView();
assertNull(listView.getContextMenuInfo());
- final MockOnItemLongClickListener listener = new MockOnItemLongClickListener();
- listView.setOnItemLongClickListener(listener);
+ final AbsListView.OnItemLongClickListener mockOnItemLongClickListener =
+ mock(AbsListView.OnItemLongClickListener.class);
+ listView.setOnItemLongClickListener(mockOnItemLongClickListener);
- assertNull(listener.getParent());
- assertNull(listener.getView());
- assertEquals(0, listener.getPosition());
- assertEquals(0, listener.getID());
+ verifyZeroInteractions(mockOnItemLongClickListener);
+
+ // Create a count down latch and configure it to be counted down when our mock
+ // listener is invoked with the expected view
+ final CountDownLatch countDownLatch = new CountDownLatch(1);
+ doAnswer(new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) {
+ countDownLatch.countDown();
+ return null;
+ }
+ }).when(mockOnItemLongClickListener).onItemLongClick(listView, v, 2,
+ listView.getItemIdAtPosition(2));
mInstrumentation.waitForIdleSync();
- TouchUtils.longClickView(this, v);
- new PollingCheck() {
- @Override
- protected boolean check() {
- return v == listener.getView();
- }
- }.run();
+ // Now long click our view
+ CtsTouchUtils.emulateLongClick(mInstrumentation, v, 500);
- assertSame(listView, listener.getParent());
- assertEquals(2, listener.getPosition());
- assertEquals(listView.getItemIdAtPosition(2), listener.getID());
+ // And wait for the latch to be triggered
+ try {
+ assertTrue(countDownLatch.await(20L, TimeUnit.SECONDS));
+ } catch (InterruptedException e) {
+ }
+
+ verify(mockOnItemLongClickListener, times(1)).onItemLongClick(listView, v, 2,
+ listView.getItemIdAtPosition(2));
ContextMenuInfo cmi = listView.getContextMenuInfo();
assertNotNull(cmi);
}
+ @Test
public void testGetTopBottomFadingEdgeStrength() {
- MyListView listView = new MyListView(mActivity);
+ MyListView listView = new MyListView(mContext);
assertEquals(0.0f, listView.getTopFadingEdgeStrength(), DELTA);
assertEquals(0.0f, listView.getBottomFadingEdgeStrength(), DELTA);
}
+ @Test
public void testHandleDataChanged() {
- MyListView listView = new MyListView(mActivity, mAttributeSet, 0);
+ MyListView listView = new MyListView(mContext, mAttributeSet, 0);
listView.handleDataChanged();
// TODO: how to check?
}
+ @Test
public void testSetFilterText() {
- MyListView listView = new MyListView(mActivity, mAttributeSet, 0);
+ MyListView listView = new MyListView(mContext, mAttributeSet, 0);
String filterText = "xyz";
assertFalse(listView.isTextFilterEnabled());
@@ -650,231 +699,420 @@
}
@MediumTest
- public void testSetItemChecked_multipleModeSameValue()
- throws Throwable {
+ @Test
+ public void testSetItemChecked_multipleModeSameValue() {
// Calling setItemChecked with the same value in multiple choice mode should not cause
// requestLayout
mListView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- mListView.setItemChecked(0, false);
- }
- });
+ mInstrumentation.runOnMainSync(() -> mListView.setItemChecked(0, false));
mInstrumentation.waitForIdleSync();
assertFalse(mListView.isLayoutRequested());
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- mListView.setItemChecked(0, false);
- }
- });
+ mInstrumentation.runOnMainSync(() -> mListView.setItemChecked(0, false));
assertFalse(mListView.isLayoutRequested());
}
@MediumTest
- public void testSetItemChecked_singleModeSameValue()
- throws Throwable {
+ @Test
+ public void testSetItemChecked_singleModeSameValue() {
// Calling setItemChecked with the same value in single choice mode should not cause
// requestLayout
mListView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- mListView.setItemChecked(0, false);
- }
- });
+ mInstrumentation.runOnMainSync(() -> mListView.setItemChecked(0, false));
mInstrumentation.waitForIdleSync();
assertFalse(mListView.isLayoutRequested());
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- mListView.setItemChecked(0, false);
- }
- });
+ mInstrumentation.runOnMainSync(() -> mListView.setItemChecked(0, false));
assertFalse(mListView.isLayoutRequested());
}
@MediumTest
- public void testSetItemChecked_multipleModeDifferentValue()
- throws Throwable {
+ @Test
+ public void testSetItemChecked_multipleModeDifferentValue() {
// Calling setItemChecked with a different value in multiple choice mode should cause
// requestLayout
mListView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- mListView.setItemChecked(0, false);
- }
- });
+ mInstrumentation.runOnMainSync(() -> mListView.setItemChecked(0, false));
mInstrumentation.waitForIdleSync();
assertFalse(mListView.isLayoutRequested());
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- mListView.setItemChecked(0, true);
- }
- });
+ mInstrumentation.runOnMainSync(() -> mListView.setItemChecked(0, true));
assertTrue(mListView.isLayoutRequested());
}
@MediumTest
- public void testSetItemChecked_singleModeDifferentValue()
- throws Throwable {
+ @Test
+ public void testSetItemChecked_singleModeDifferentValue() {
// Calling setItemChecked with a different value in single choice mode should cause
// requestLayout
mListView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- mListView.setItemChecked(0, false);
- }
- });
+ mInstrumentation.runOnMainSync(() -> mListView.setItemChecked(0, false));
mInstrumentation.waitForIdleSync();
assertFalse(mListView.isLayoutRequested());
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- mListView.setItemChecked(0, true);
- }
- });
+ mInstrumentation.runOnMainSync(() -> mListView.setItemChecked(0, true));
assertTrue(mListView.isLayoutRequested());
}
- public void testLayoutChildren() {
- /**
- * the subclass ListView and GridView override this method, so we can not test
- * this method.
- */
+ @LargeTest
+ @Test
+ public void testTextFilter() {
+ setAdapter();
+
+ // Default state - no text filter
+ assertFalse(mListView.isTextFilterEnabled());
+ assertFalse(mListView.hasTextFilter());
+ assertTrue(TextUtils.isEmpty(mListView.getTextFilter()));
+
+ // Enable text filter and verify that while it's enabled, the filtering is
+ // still no on
+ mInstrumentation.runOnMainSync(() -> mListView.setTextFilterEnabled(true));
+ assertTrue(mListView.isTextFilterEnabled());
+ assertFalse(mListView.hasTextFilter());
+ assertTrue(TextUtils.isEmpty(mListView.getTextFilter()));
+
+ // Verify the initial content of the list
+ assertEquals(COUNTRY_LIST.length, mListView.getCount());
+
+ // Set text filter to A - we expect four entries to be left displayed in the list
+ mInstrumentation.runOnMainSync(() -> mListView.setFilterText("A"));
+ PollingCheck.waitFor(() -> mListView.getCount() == 4);
+ assertTrue(mListView.isTextFilterEnabled());
+ assertTrue(mListView.hasTextFilter());
+ assertTrue(TextUtils.equals("A", mListView.getTextFilter()));
+
+ // Set text filter to Ar - we expect three entries to be left displayed in the list
+ mInstrumentation.runOnMainSync(() -> mListView.setFilterText("Ar"));
+ PollingCheck.waitFor(() -> mListView.getCount() == 3);
+ assertTrue(mListView.isTextFilterEnabled());
+ assertTrue(mListView.hasTextFilter());
+ assertTrue(TextUtils.equals("Ar", mListView.getTextFilter()));
+
+ // Clear text filter - we expect to go back to the initial content
+ mInstrumentation.runOnMainSync(() -> mListView.clearTextFilter());
+ PollingCheck.waitFor(() -> mListView.getCount() == COUNTRY_LIST.length);
+ assertTrue(mListView.isTextFilterEnabled());
+ assertFalse(mListView.hasTextFilter());
+ assertTrue(TextUtils.isEmpty(mListView.getTextFilter()));
+
+ // Set text filter to Be - we expect four entries to be left displayed in the list
+ mInstrumentation.runOnMainSync(() -> mListView.setFilterText("Be"));
+ PollingCheck.waitFor(() -> mListView.getCount() == 4);
+ assertTrue(mListView.isTextFilterEnabled());
+ assertTrue(mListView.hasTextFilter());
+ assertTrue(TextUtils.equals("Be", mListView.getTextFilter()));
+
+ // Set text filter to Q - we no entries displayed in the list
+ mInstrumentation.runOnMainSync(() -> mListView.setFilterText("Q"));
+ PollingCheck.waitFor(() -> mListView.getCount() == 0);
+ assertTrue(mListView.isTextFilterEnabled());
+ assertTrue(mListView.hasTextFilter());
+ assertTrue(TextUtils.equals("Q", mListView.getTextFilter()));
}
- public void testFoo() {
- /**
- * Do not test these APIs. They are callbacks which:
- *
- * 1. The callback machanism has been tested in super class
- * 2. The functionality is implmentation details, no need to test
- */
+ @Test
+ public void testOnFilterComplete() {
+ // Note that we're not using spy() due to Mockito not being able to spy on ListView,
+ // at least yet.
+ final MyListView listView = new MyListView(mContext, mAttributeSet);
+
+ mInstrumentation.runOnMainSync(() -> {
+ mActivityRule.getActivity().setContentView(listView);
+ listView.setAdapter(mCountriesAdapter);
+ listView.setTextFilterEnabled(true);
+ });
+ mInstrumentation.waitForIdleSync();
+
+ // Set text filter to A - we expect four entries to be left displayed in the list
+ mInstrumentation.runOnMainSync(() -> listView.setFilterText("A"));
+ PollingCheck.waitFor(() -> listView.getCount() == 4);
+ assertTrue(listView.isTextFilterEnabled());
+ assertTrue(listView.hasTextFilter());
+ assertTrue(TextUtils.equals("A", listView.getTextFilter()));
+
+ assertEquals(4, listView.getOnFilterCompleteCount());
}
- private static class MockOnScrollListener implements OnScrollListener {
- private AbsListView mView;
- private int mFirstVisibleItem;
- private int mVisibleItemCount;
- private int mTotalItemCount;
- private int mScrollState;
-
- private boolean mIsOnScrollCalled;
- private boolean mIsOnScrollStateChangedCalled;
-
- private MockOnScrollListener() {
- mView = null;
- mFirstVisibleItem = 0;
- mVisibleItemCount = 0;
- mTotalItemCount = 0;
- mScrollState = -1;
-
- mIsOnScrollCalled = false;
- mIsOnScrollStateChangedCalled = false;
+ private static class PositionArrayAdapter<T> extends ArrayAdapter<T> {
+ public PositionArrayAdapter(Context context, int resource, List<T> objects) {
+ super(context, resource, objects);
}
- public void onScroll(AbsListView view, int firstVisibleItem,
- int visibleItemCount, int totalItemCount) {
- mView = view;
- mFirstVisibleItem = firstVisibleItem;
- mVisibleItemCount = visibleItemCount;
- mTotalItemCount = totalItemCount;
- mIsOnScrollCalled = true;
- }
-
- public void onScrollStateChanged(AbsListView view, int scrollState) {
- mScrollState = scrollState;
- mIsOnScrollStateChangedCalled = true;
- }
-
- public AbsListView getView() {
- return mView;
- }
-
- public int getFirstVisibleItem() {
- return mFirstVisibleItem;
- }
-
- public int getVisibleItemCount() {
- return mVisibleItemCount;
- }
-
- public int getTotalItemCount() {
- return mTotalItemCount;
- }
-
- public int getScrollState() {
- return mScrollState;
- }
-
- public boolean isOnScrollCalled() {
- return mIsOnScrollCalled;
- }
-
- public boolean isOnScrollStateChangedCalled() {
- return mIsOnScrollStateChangedCalled;
- }
-
- public void reset() {
- mIsOnScrollCalled = false;
- mIsOnScrollStateChangedCalled = false;
- }
- }
-
- private static class MockRecyclerListener implements RecyclerListener {
- private View mView;
-
- private MockRecyclerListener() {
- mView = null;
- }
-
- public void onMovedToScrapHeap(View view) {
- mView = view;
- }
-
- public View getView() {
- return mView;
- }
- }
-
- private static class MockOnItemLongClickListener implements OnItemLongClickListener {
- private AdapterView<?> parent;
- private View view;
- private int position;
- private long id;
-
- public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
- this.parent = parent;
- this.view = view;
- this.position = position;
- this.id = id;
- return false;
- }
-
- public AdapterView<?> getParent() {
- return parent;
- }
-
- public View getView() {
- return view;
- }
-
- public int getPosition() {
+ @Override
+ public long getItemId(int position) {
return position;
}
- public long getID() {
- return id;
+ @Override
+ public boolean hasStableIds() {
+ return true;
}
}
+ private void verifyCheckedState(final long[] expectedCheckedItems) {
+ TestUtils.assertIdentical(expectedCheckedItems, mListView.getCheckedItemIds());
+
+ assertEquals(expectedCheckedItems.length, mListView.getCheckedItemCount());
+
+ final long expectedCheckedItemPosition =
+ (mListView.getChoiceMode() == AbsListView.CHOICE_MODE_SINGLE) &&
+ (expectedCheckedItems.length == 1)
+ ? expectedCheckedItems[0]
+ : AbsListView.INVALID_POSITION;
+ assertEquals(expectedCheckedItemPosition, mListView.getCheckedItemPosition());
+
+ // Note that getCheckedItemPositions doesn't have a guarantee that it only holds
+ // true values, which is why we're not doing the size() == 0 check even in the initial
+ // state
+ TestUtils.assertTrueValuesAtPositions(
+ expectedCheckedItems, mListView.getCheckedItemPositions());
+ }
+
+ @MediumTest
+ @Test
+ public void testCheckedItemsUnderNoneChoiceMode() {
+ final ArrayList<String> items = new ArrayList<>(Arrays.asList(COUNTRY_LIST));
+ final ArrayAdapter<String> adapter = new PositionArrayAdapter<>(mContext,
+ android.R.layout.simple_list_item_1, items);
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mListView,
+ () -> mListView.setAdapter(adapter));
+
+ mInstrumentation.runOnMainSync(
+ () -> mListView.setChoiceMode(AbsListView.CHOICE_MODE_NONE));
+ verifyCheckedState(new long[] {});
+
+ mInstrumentation.runOnMainSync(() -> mListView.setItemChecked(2, true));
+ verifyCheckedState(new long[] {});
+
+ mInstrumentation.runOnMainSync(() -> mListView.setItemChecked(4, true));
+ verifyCheckedState(new long[] {});
+
+ mInstrumentation.runOnMainSync(() -> mListView.setItemChecked(2, false));
+ verifyCheckedState(new long[] {});
+
+ mInstrumentation.runOnMainSync(() -> mListView.setItemChecked(4, false));
+ verifyCheckedState(new long[] {});
+ }
+
+ @MediumTest
+ @Test
+ public void testCheckedItemsUnderSingleChoiceMode() {
+ final ArrayList<String> items = new ArrayList<>(Arrays.asList(COUNTRY_LIST));
+ final ArrayAdapter<String> adapter = new PositionArrayAdapter<>(mContext,
+ android.R.layout.simple_list_item_1, items);
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mListView,
+ () -> mListView.setAdapter(adapter));
+
+ mInstrumentation.runOnMainSync(
+ () -> mListView.setChoiceMode(AbsListView.CHOICE_MODE_SINGLE));
+ verifyCheckedState(new long[] {});
+
+ mInstrumentation.runOnMainSync(() -> mListView.setItemChecked(2, true));
+ verifyCheckedState(new long[] { 2 });
+
+ mInstrumentation.runOnMainSync(() -> mListView.setItemChecked(4, true));
+ verifyCheckedState(new long[] { 4 });
+
+ mInstrumentation.runOnMainSync(() -> mListView.setItemChecked(2, false));
+ verifyCheckedState(new long[] { 4 });
+
+ mInstrumentation.runOnMainSync(() -> mListView.setItemChecked(4, false));
+ verifyCheckedState(new long[] {});
+ }
+
+ @MediumTest
+ @Test
+ public void testCheckedItemsUnderMultipleChoiceMode() {
+ final ArrayList<String> items = new ArrayList<>(Arrays.asList(COUNTRY_LIST));
+ final ArrayAdapter<String> adapter = new PositionArrayAdapter<>(mContext,
+ android.R.layout.simple_list_item_1, items);
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mListView,
+ () -> mListView.setAdapter(adapter));
+
+ mInstrumentation.runOnMainSync(
+ () -> mListView.setChoiceMode(AbsListView.CHOICE_MODE_MULTIPLE));
+ verifyCheckedState(new long[] {});
+
+ mInstrumentation.runOnMainSync(() -> mListView.setItemChecked(2, true));
+ verifyCheckedState(new long[] { 2 });
+
+ mInstrumentation.runOnMainSync(() -> mListView.setItemChecked(4, true));
+ verifyCheckedState(new long[] { 2, 4 });
+
+ mInstrumentation.runOnMainSync(() -> mListView.setItemChecked(2, false));
+ verifyCheckedState(new long[] { 4 });
+
+ mInstrumentation.runOnMainSync(() -> mListView.setItemChecked(4, false));
+ verifyCheckedState(new long[] {});
+ }
+
+ private void configureMultiChoiceModalState() {
+ final ArrayList<String> items = new ArrayList<>(Arrays.asList(COUNTRY_LIST));
+ final ArrayAdapter<String> adapter = new PositionArrayAdapter<>(mContext,
+ android.R.layout.simple_list_item_1, items);
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mListView,
+ () -> mListView.setAdapter(adapter));
+
+ // Configure a multi-choice mode listener to configure our test contextual action bar
+ // content. We will subsequently query that listener for calls to its
+ // onItemCheckedStateChanged method
+ mMultiChoiceModeListener =
+ mock(AbsListView.MultiChoiceModeListener.class);
+ doAnswer((InvocationOnMock invocation) -> {
+ final ActionMode actionMode = (ActionMode) invocation.getArguments() [0];
+ final Menu menu = (Menu) invocation.getArguments() [1];
+ actionMode.getMenuInflater().inflate(R.menu.cab_menu, menu);
+ return true;
+ }).when(mMultiChoiceModeListener).onCreateActionMode(
+ any(ActionMode.class), any(Menu.class));
+ mListView.setMultiChoiceModeListener(mMultiChoiceModeListener);
+
+ mInstrumentation.runOnMainSync(
+ () -> mListView.setChoiceMode(AbsListView.CHOICE_MODE_MULTIPLE_MODAL));
+ verifyCheckedState(new long[] {});
+ }
+
+ @MediumTest
+ @Test
+ public void testCheckedItemsUnderMultipleModalChoiceMode() {
+ configureMultiChoiceModalState();
+
+ mInstrumentation.runOnMainSync(() -> mListView.setItemChecked(2, true));
+ verifyCheckedState(new long[] { 2 });
+ verify(mMultiChoiceModeListener, times(1)).onItemCheckedStateChanged(
+ any(ActionMode.class), eq(2), eq(2L), eq(true));
+
+ reset(mMultiChoiceModeListener);
+ mInstrumentation.runOnMainSync(() -> mListView.setItemChecked(4, true));
+ verifyCheckedState(new long[] { 2, 4 });
+ verify(mMultiChoiceModeListener, times(1)).onItemCheckedStateChanged(
+ any(ActionMode.class), eq(4), eq(4L), eq(true));
+
+ reset(mMultiChoiceModeListener);
+ mInstrumentation.runOnMainSync(() -> mListView.setItemChecked(2, false));
+ verifyCheckedState(new long[] { 4 });
+ verify(mMultiChoiceModeListener, times(1)).onItemCheckedStateChanged(
+ any(ActionMode.class), eq(2), eq(2L), eq(false));
+
+ reset(mMultiChoiceModeListener);
+ mInstrumentation.runOnMainSync(() -> mListView.setItemChecked(4, false));
+ verifyCheckedState(new long[] {});
+ mListView.setMultiChoiceModeListener(mMultiChoiceModeListener);
+ verify(mMultiChoiceModeListener, times(1)).onItemCheckedStateChanged(
+ any(ActionMode.class), eq(4), eq(4L), eq(false));
+ }
+
+ private void configureMultiChoiceModeListenerOnItemChecked(final CountDownLatch countDownLatch,
+ int positionToBecomeChecked) {
+ doAnswer((InvocationOnMock invocation) -> {
+ countDownLatch.countDown();
+ return null;
+ }).when(mMultiChoiceModeListener).onItemCheckedStateChanged(
+ any(ActionMode.class), eq(positionToBecomeChecked),
+ eq((long) positionToBecomeChecked), eq(true));
+ }
+
+ @LargeTest
+ @Test
+ public void testMultiSelectionWithLongPressAndTaps() {
+ configureMultiChoiceModalState();
+
+ final int firstVisiblePosition = mListView.getFirstVisiblePosition();
+ final int lastVisiblePosition = mListView.getLastVisiblePosition();
+
+ // Emulate long-click on the middle item of the currently visible content
+ final int positionForInitialSelection = (firstVisiblePosition + lastVisiblePosition) / 2;
+ final CountDownLatch initialCheckedLatch = new CountDownLatch(1);
+ configureMultiChoiceModeListenerOnItemChecked(
+ initialCheckedLatch, positionForInitialSelection);
+ CtsTouchUtils.emulateLongClick(mInstrumentation,
+ mListView.getChildAt(positionForInitialSelection));
+ try {
+ assertTrue(initialCheckedLatch.await(1, TimeUnit.SECONDS));
+ } catch (InterruptedException ie) {
+ fail();
+ }
+ // and verify that the item is now checked
+ verifyCheckedState(new long[] { positionForInitialSelection });
+
+ if (firstVisiblePosition != positionForInitialSelection) {
+ final CountDownLatch secondCheckedLatch = new CountDownLatch(1);
+ configureMultiChoiceModeListenerOnItemChecked(
+ secondCheckedLatch, firstVisiblePosition);
+ // Tap the first element in our list
+ CtsTouchUtils.emulateTapOnViewCenter(mInstrumentation,
+ mListView.getChildAt(firstVisiblePosition));
+ try {
+ assertTrue(secondCheckedLatch.await(1, TimeUnit.SECONDS));
+ } catch (InterruptedException ie) {
+ fail();
+ }
+ // and verify that the item is now checked
+ verifyCheckedState(new long[] { firstVisiblePosition, positionForInitialSelection });
+ }
+
+ // Scroll down
+ CtsTouchUtils.emulateScrollToBottom(mInstrumentation, mListView);
+ final int lastListPosition = COUNTRY_LIST.length - 1;
+ if (lastListPosition != positionForInitialSelection) {
+ final CountDownLatch thirdCheckedLatch = new CountDownLatch(1);
+ configureMultiChoiceModeListenerOnItemChecked(thirdCheckedLatch, lastListPosition);
+ // Tap the last element in our list
+ CtsTouchUtils.emulateTapOnViewCenter(mInstrumentation,
+ mListView.getChildAt(mListView.getChildCount() - 1));
+ try {
+ assertTrue(thirdCheckedLatch.await(1, TimeUnit.SECONDS));
+ } catch (InterruptedException ie) {
+ fail();
+ }
+ // and verify that the item is now checked and our listener has been notified
+ verifyCheckedState(new long[] { firstVisiblePosition, positionForInitialSelection,
+ lastListPosition });
+ }
+ }
+
+ @LargeTest
+ @Test
+ public void testFastScroll() {
+ setAdapter();
+
+ final int lastVisiblePosition = mListView.getLastVisiblePosition();
+ if (lastVisiblePosition == (COUNTRY_LIST.length - 1)) {
+ // This can happen on very large screens - the entire content fits and there's
+ // nothing to scroll
+ return;
+ }
+
+ mListView.setFastScrollAlwaysVisible(true);
+ assertTrue(mListView.isFastScrollEnabled());
+ assertTrue(mListView.isFastScrollAlwaysVisible());
+
+ final int[] listViewOnScreenXY = new int[2];
+ mListView.getLocationOnScreen(listViewOnScreenXY);
+
+ final int topEdgeY = listViewOnScreenXY[1];
+ final int bottomEdgeY = listViewOnScreenXY[1] + mListView.getHeight();
+ final int rightEdgeX = listViewOnScreenXY[0] + mListView.getWidth();
+
+ // Emulate a downwards gesture that should bring us all the way to the last element
+ // of the list (when fast scroll is enabled)
+ CtsTouchUtils.emulateDragGesture(mInstrumentation,
+ rightEdgeX - 1, // X start of the drag
+ topEdgeY + 1, // Y start of the drag
+ 0, // X amount of the drag (vertical)
+ mListView.getHeight() - 2); // Y amount of the drag (downwards)
+
+ assertEquals(COUNTRY_LIST.length - 1, mListView.getLastVisiblePosition());
+
+ // Emulate an upwards gesture that should bring us all the way to the first element
+ // of the list (when fast scroll is enabled)
+ CtsTouchUtils.emulateDragGesture(mInstrumentation,
+ rightEdgeX - 1, // X start of the drag
+ bottomEdgeY - 1, // Y start of the drag
+ 0, // X amount of the drag (vertical)
+ -mListView.getHeight() + 2); // Y amount of the drag (upwards)
+
+ assertEquals(0, mListView.getFirstVisiblePosition());
+ }
+
/**
- * MyListView for test
+ * MyListView for test.
*/
private static class MyListView extends ListView {
public MyListView(Context context) {
@@ -982,5 +1220,17 @@
public boolean isAfterTextChangedCalled() {
return mIsAfterTextChangedCalled;
}
+
+ private int mOnFilterCompleteCount = -1;
+
+ @Override
+ public void onFilterComplete(int count) {
+ super.onFilterComplete(count);
+ mOnFilterCompleteCount = count;
+ }
+
+ public int getOnFilterCompleteCount() {
+ return mOnFilterCompleteCount;
+ }
}
}
diff --git a/tests/tests/widget/src/android/widget/cts/AbsListView_LayoutParamsTest.java b/tests/tests/widget/src/android/widget/cts/AbsListView_LayoutParamsTest.java
index 7e88a19..ca73a2f 100644
--- a/tests/tests/widget/src/android/widget/cts/AbsListView_LayoutParamsTest.java
+++ b/tests/tests/widget/src/android/widget/cts/AbsListView_LayoutParamsTest.java
@@ -16,37 +16,45 @@
package android.widget.cts;
-import android.widget.cts.R;
+import static org.junit.Assert.assertEquals;
-
-import org.xmlpull.v1.XmlPullParser;
-
+import android.content.Context;
import android.cts.util.WidgetTestUtils;
-import android.test.AndroidTestCase;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
import android.util.AttributeSet;
import android.util.Xml;
import android.widget.AbsListView;
import android.widget.AbsListView.LayoutParams;
-public class AbsListView_LayoutParamsTest extends AndroidTestCase {
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.xmlpull.v1.XmlPullParser;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class AbsListView_LayoutParamsTest {
+ private Context mContext;
private AttributeSet mAttributeSet;
- @Override
- protected void setUp() throws Exception {
- super.setUp();
-
+ @Before
+ public void setup() throws Exception {
+ mContext = InstrumentationRegistry.getTargetContext();
XmlPullParser parser = mContext.getResources().getXml(R.layout.abslistview_layout);
WidgetTestUtils.beginDocument(parser, "ViewGroup_Layout");
mAttributeSet = Xml.asAttributeSet(parser);
}
+ @Test
public void testConstructors() {
int TEST_WIDTH = 25;
int TEST_HEIGHT = 25;
int TEST_HEIGHT2 = 30;
AbsListView.LayoutParams layoutParams;
- layoutParams = new AbsListView.LayoutParams(getContext(), mAttributeSet);
+ layoutParams = new AbsListView.LayoutParams(mContext, mAttributeSet);
assertEquals(TEST_WIDTH, layoutParams.width);
assertEquals(TEST_HEIGHT, layoutParams.height);
diff --git a/tests/tests/widget/src/android/widget/cts/AbsListView_ScrollTest.java b/tests/tests/widget/src/android/widget/cts/AbsListView_ScrollTest.java
new file mode 100644
index 0000000..e3246d3
--- /dev/null
+++ b/tests/tests/widget/src/android/widget/cts/AbsListView_ScrollTest.java
@@ -0,0 +1,729 @@
+/*
+ * 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.widget.cts;
+
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.content.Context;
+import android.cts.util.CtsTouchUtils;
+import android.cts.util.PollingCheck;
+import android.os.SystemClock;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.ViewGroup;
+import android.widget.AbsListView;
+import android.widget.AbsListView.OnScrollListener;
+import android.widget.ArrayAdapter;
+import android.widget.BaseAdapter;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import static org.junit.Assert.*;
+
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class AbsListView_ScrollTest {
+ private static final int ROW_HEIGHT_PX = 40;
+ private static final String[] COUNTRY_LIST = new String[] {
+ "Argentina", "Armenia", "Aruba", "Australia", "Belarus", "Belgium", "Belize", "Benin",
+ "Botswana", "Brazil", "Cameroon", "China", "Colombia", "Costa Rica", "Cyprus",
+ "Denmark", "Djibouti", "Ethiopia", "Fiji", "Finland", "France", "Gabon", "Germany",
+ "Ghana", "Haiti", "Honduras", "Iceland", "India", "Indonesia", "Ireland", "Italy",
+ "Japan", "Kiribati", "Laos", "Lesotho", "Liberia", "Malaysia", "Mongolia", "Myanmar",
+ "Nauru", "Norway", "Oman", "Pakistan", "Philippines", "Portugal", "Romania", "Russia",
+ "Rwanda", "Singapore", "Slovakia", "Slovenia", "Somalia", "Swaziland", "Togo", "Tuvalu",
+ "Uganda", "Ukraine", "United States", "Vanuatu", "Venezuela", "Zimbabwe"
+ };
+
+ @Rule
+ public ActivityTestRule<ListViewFixedCtsActivity> mActivityRule
+ = new ActivityTestRule<>(ListViewFixedCtsActivity.class);
+
+ private AbsListView mListView;
+ private Context mContext;
+ private Instrumentation mInstrumentation;
+ private ArrayAdapter<String> mCountriesAdapter;
+
+ @Before
+ public void setup() throws Exception {
+ mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ mContext = mInstrumentation.getTargetContext();
+
+ final Activity activity = mActivityRule.getActivity();
+
+ PollingCheck.waitFor(() -> activity.hasWindowFocus());
+
+ mCountriesAdapter = new ArrayAdapter<>(mContext,
+ R.layout.listitemfixed_layout, COUNTRY_LIST);
+
+ mListView = (ListView) activity.findViewById(R.id.listview_default);
+ mInstrumentation.runOnMainSync(() -> mListView.setAdapter(mCountriesAdapter));
+ mInstrumentation.waitForIdleSync();
+ }
+
+ /**
+ * Listener that allows waiting for the end of a scroll. When the tracked
+ * {@link AbsListView} transitions to idle state, the passed {@link CountDownLatch}
+ * is notified.
+ */
+ private class ScrollIdleListListener implements OnScrollListener {
+ private CountDownLatch mLatchToNotify;
+
+ public ScrollIdleListListener(CountDownLatch latchToNotify) {
+ mLatchToNotify = latchToNotify;
+ }
+
+ @Override
+ public void onScrollStateChanged(AbsListView view, int scrollState) {
+ if (scrollState == OnScrollListener.SCROLL_STATE_IDLE) {
+ mListView.setOnScrollListener(null);
+ mLatchToNotify.countDown();
+ }
+ }
+
+ @Override
+ public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
+ int totalItemCount) {
+ }
+ }
+
+ /**
+ * Listener that allows waiting until a specific position in the list becomes visible.
+ * When the tracked position in the {@link AbsListView} becomes visible, the passed
+ * {@link CountDownLatch} is notified.
+ */
+ private class ScrollPositionListListener implements AbsListView.OnScrollListener {
+ private CountDownLatch mLatchToNotify;
+ private int mTargetPosition;
+
+ public ScrollPositionListListener(CountDownLatch latchToNotify, int targetPosition) {
+ mLatchToNotify = latchToNotify;
+ mTargetPosition = targetPosition;
+ }
+
+ @Override
+ public void onScrollStateChanged(AbsListView view, int scrollState) {
+ }
+
+ @Override
+ public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
+ int totalItemCount) {
+ // Is our target in current visible range?
+ int lastVisibleItem = firstVisibleItem + visibleItemCount - 1;
+ boolean isInRange = (mTargetPosition >= firstVisibleItem) &&
+ (mTargetPosition <= lastVisibleItem);
+ if (!isInRange) {
+ return;
+ }
+
+ // Is our target also fully visible?
+ int visibleIndexOfTarget = mTargetPosition - firstVisibleItem;
+ View targetChild = mListView.getChildAt(visibleIndexOfTarget);
+ boolean isTargetFullyVisible = (targetChild.getTop() >= 0) &&
+ (targetChild.getBottom() <= mListView.getHeight());
+ if (isTargetFullyVisible) {
+ mListView.setOnScrollListener(null);
+ mLatchToNotify.countDown();
+ }
+ }
+ }
+
+ private boolean isItemVisible(int position) {
+ return (position >= mListView.getFirstVisiblePosition()) &&
+ (position <= mListView.getLastVisiblePosition());
+ }
+
+ private void verifyScrollToPosition(int positionToScrollTo) {
+ final int firstVisiblePosition = mListView.getFirstVisiblePosition();
+ final int lastVisiblePosition = mListView.getLastVisiblePosition();
+
+ if ((positionToScrollTo >= firstVisiblePosition) &&
+ (positionToScrollTo <= lastVisiblePosition)) {
+ // If it's already on the screen, we won't have any real scrolling taking
+ // place, so our tracking based on scroll state change will time out. This
+ // is why we're returning here early.
+ return;
+ }
+
+ // Register a scroll listener on our ListView. The listener will notify our latch
+ // when the scroll state changes to IDLE. If that never happens, the latch will
+ // time out and fail the test.
+ final CountDownLatch latch = new CountDownLatch(1);
+ mListView.setOnScrollListener(new ScrollIdleListListener(latch));
+ mInstrumentation.runOnMainSync(() -> mListView.smoothScrollToPosition(
+ positionToScrollTo));
+
+ boolean result = false;
+ try {
+ result = latch.await(2, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ // ignore
+ }
+ assertTrue("Timed out while waiting for the scroll to complete", result);
+
+ // Verify that the position we've been asked to scroll to is visible
+ assertTrue("Asked to scroll to " + positionToScrollTo + ", first visible is "
+ + mListView.getFirstVisiblePosition() + ", last visible is "
+ + mListView.getLastVisiblePosition(), isItemVisible(positionToScrollTo));
+ }
+
+ @Test
+ public void testSmoothScrollToPositionDownUpDown() {
+ final int itemCount = COUNTRY_LIST.length;
+
+ // Scroll closer to the end of the list
+ verifyScrollToPosition(itemCount - 10);
+ // Scroll back towards the beginning of the list
+ verifyScrollToPosition(5);
+ // And then towards the end of the list again
+ verifyScrollToPosition(itemCount - 1);
+ // And back up to the middle of the list
+ verifyScrollToPosition(itemCount / 2);
+ }
+
+ @Test
+ public void testSmoothScrollToPositionEveryRow() {
+ final int itemCount = COUNTRY_LIST.length;
+
+ for (int i = 0; i < itemCount; i++) {
+ // Scroll one row down
+ verifyScrollToPosition(i);
+ }
+
+ for (int i = itemCount - 1; i >= 0; i--) {
+ // Scroll one row up
+ verifyScrollToPosition(i);
+ }
+ }
+
+ private void verifyScrollToPositionWithBound(int positionToScrollTo, int boundPosition,
+ boolean expectTargetPositionToBeVisibleAtEnd) {
+ final int firstVisiblePosition = mListView.getFirstVisiblePosition();
+ final int lastVisiblePosition = mListView.getLastVisiblePosition();
+
+ if ((positionToScrollTo >= firstVisiblePosition) &&
+ (positionToScrollTo <= lastVisiblePosition)) {
+ // If it's already on the screen, we won't have any real scrolling taking
+ // place, so our tracking based on scroll state change will time out. This
+ // is why we're returning here early.
+ return;
+ }
+
+ // Register a scroll listener on our ListView. The listener will notify our latch
+ // when the scroll state changes to IDLE. If that never happens, the latch will
+ // time out and fail the test.
+ final CountDownLatch latch = new CountDownLatch(1);
+ mListView.setOnScrollListener(new ScrollIdleListListener(latch));
+ mInstrumentation.runOnMainSync(() -> mListView.smoothScrollToPosition(
+ positionToScrollTo, boundPosition));
+
+ boolean result = false;
+ try {
+ result = latch.await(2, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ // ignore
+ }
+ assertTrue("Timed out while waiting for the scroll to complete", result);
+
+ // Verify that the bound position is visible
+ assertTrue("Asked to scroll to " + positionToScrollTo + " with bound " + boundPosition
+ + ", first visible is " + mListView.getFirstVisiblePosition()
+ + ", last visible is " + mListView.getLastVisiblePosition(),
+ isItemVisible(boundPosition));
+
+ assertEquals("Asked to scroll to " + positionToScrollTo + " with bound " + boundPosition
+ + ", first visible is " + mListView.getFirstVisiblePosition()
+ + ", last visible is " + mListView.getLastVisiblePosition(),
+ expectTargetPositionToBeVisibleAtEnd, isItemVisible(positionToScrollTo));
+ }
+
+ @Test
+ public void testSmoothScrollToPositionWithBound() {
+ // Our list is 300px high and each row is 40px high. Without being too precise,
+ // the logic in this method relies on at least 8 and at most 10 items on the screen
+ // at any time.
+
+ // Scroll to 20 with bound at 6. This should result in the scroll stopping before it
+ // gets to 20 so that 6 is still visible
+ verifyScrollToPositionWithBound(20, 6, false);
+
+ // Scroll to 40 with bound at 35. This should result in the scroll getting to 40 becoming
+ // visible with 35 visible as well
+ verifyScrollToPositionWithBound(40, 35, true);
+
+ // Scroll to 10 with bound at 25. This should result in the scroll stopping before it
+ // gets to 10 so that 25 is still visible
+ verifyScrollToPositionWithBound(10, 25, false);
+
+ // Scroll to 5 with bound at 8. This should result in the scroll getting to 5 becoming
+ // visible with 8 visible as well
+ verifyScrollToPositionWithBound(5, 8, true);
+ }
+
+ private void verifyScrollToPositionFromTop(int positionToScrollTo, int offset,
+ int durationMs) {
+ final int startTopPositionInListCoordinates =
+ mListView.getFirstVisiblePosition() * ROW_HEIGHT_PX -
+ mListView.getChildAt(0).getTop();
+ int targetTopPositionInListCoordinates = positionToScrollTo * ROW_HEIGHT_PX - offset;
+ // Need to clamp it to account for requests that would scroll the content outside
+ // of the available bounds
+ targetTopPositionInListCoordinates = Math.max(0, targetTopPositionInListCoordinates);
+ targetTopPositionInListCoordinates = Math.min(
+ COUNTRY_LIST.length * ROW_HEIGHT_PX - mListView.getHeight(),
+ targetTopPositionInListCoordinates);
+
+ if (targetTopPositionInListCoordinates == startTopPositionInListCoordinates) {
+ // If it's already at the target state, we won't have any real scrolling taking
+ // place, so our tracking based on scroll state change will time out. This
+ // is why we're returning here early.
+ return;
+ }
+
+ // Register a scroll listener on our ListView. The listener will notify our latch
+ // when the scroll state changes to IDLE. If that never happens, the latch will
+ // time out and fail the test.
+ final CountDownLatch latch = new CountDownLatch(1);
+ mListView.setOnScrollListener(new ScrollIdleListListener(latch));
+ if (durationMs > 0) {
+ mInstrumentation.runOnMainSync(() -> mListView.smoothScrollToPositionFromTop(
+ positionToScrollTo, offset, durationMs));
+ } else {
+ mInstrumentation.runOnMainSync(() -> mListView.smoothScrollToPositionFromTop(
+ positionToScrollTo, offset));
+ }
+
+ boolean result = false;
+ try {
+ // Since position-based scroll is emulated as a series of mini-flings, scrolling
+ // might take considerable time.
+ int timeoutMs = durationMs > 0 ? 5000 + durationMs : 5000;
+ result = latch.await(timeoutMs, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ // ignore
+ }
+ assertTrue("Timed out while waiting for the scroll to complete", result);
+
+ final int endTopPositionInListCoordinates =
+ mListView.getFirstVisiblePosition() * ROW_HEIGHT_PX -
+ mListView.getChildAt(0).getTop();
+
+ assertEquals(targetTopPositionInListCoordinates, endTopPositionInListCoordinates);
+ }
+
+ @Test
+ public void testSmoothScrollToPositionFromTop() {
+ // Ask to scroll so that the top of position 5 is 20 pixels below the top edge of the list
+ verifyScrollToPositionFromTop(5, 10, -1);
+
+ // Ask to scroll so that the top of position 10 is right at the top edge of the list
+ verifyScrollToPositionFromTop(10, 0, -1);
+
+ // Ask to scroll so that the top of position 5 is 80 pixels below the top edge of the list
+ // (which means that since row height is 40 pixels high, the top item should be 3
+ verifyScrollToPositionFromTop(5, 80, -1);
+
+ // Ask to scroll so that the top of position 20 is 20 pixels above the top edge of the list
+ verifyScrollToPositionFromTop(20, 20, -1);
+
+ // Ask to scroll so that the top of position 20 is right at the top edge of the list
+ verifyScrollToPositionFromTop(20, 0, -1);
+
+ // Ask to scroll so that the top of position 20 is 20 pixels below the top edge of the list
+ verifyScrollToPositionFromTop(20, 20, -1);
+
+ // Ask to scroll beyond the top of the content
+ verifyScrollToPositionFromTop(0, -20, -1);
+ verifyScrollToPositionFromTop(0, -60, -1);
+
+ // Ask to scroll beyond the bottom of the content
+ final int itemCount = COUNTRY_LIST.length;
+ verifyScrollToPositionFromTop(itemCount - 1, 0, -1);
+ verifyScrollToPositionFromTop(itemCount - 1, mListView.getHeight(), -1);
+ }
+
+ @Test
+ public void testSmoothScrollToPositionFromTopWithTime() {
+ // Ask to scroll so that the top of position 5 is 20 pixels below the top edge of the list
+ verifyScrollToPositionFromTop(5, 10, 200);
+
+ // Ask to scroll so that the top of position 10 is right at the top edge of the list
+ verifyScrollToPositionFromTop(10, 0, 1000);
+
+ // Ask to scroll so that the top of position 5 is 80 pixels below the top edge of the list
+ // (which means that since row height is 40 pixels high, the top item should be 3
+ verifyScrollToPositionFromTop(5, 80, 500);
+
+ // Ask to scroll so that the top of position 20 is 20 pixels above the top edge of the list
+ verifyScrollToPositionFromTop(20, 20, 100);
+
+ // Ask to scroll so that the top of position 20 is right at the top edge of the list
+ verifyScrollToPositionFromTop(20, 0, 700);
+
+ // Ask to scroll so that the top of position 20 is 20 pixels below the top edge of the list
+ verifyScrollToPositionFromTop(20, 20, 600);
+
+ // Ask to scroll beyond the top of the content
+ verifyScrollToPositionFromTop(0, -20, 2000);
+ verifyScrollToPositionFromTop(0, -60, 300);
+
+ // Ask to scroll beyond the bottom of the content
+ final int itemCount = COUNTRY_LIST.length;
+ verifyScrollToPositionFromTop(itemCount - 1, 0, 600);
+ verifyScrollToPositionFromTop(itemCount - 1, mListView.getHeight(), 200);
+ }
+
+ @Test
+ public void testCanScrollList() {
+ final int itemCount = COUNTRY_LIST.length;
+
+ assertEquals(0, mListView.getFirstVisiblePosition());
+
+ // Verify that when we're at the top of the list, we can't scroll up but we can scroll
+ // down.
+ assertFalse(mListView.canScrollList(-1));
+ assertTrue(mListView.canScrollList(1));
+
+ // Scroll down to the very end of the list
+ verifyScrollToPosition(itemCount - 1);
+ assertEquals(itemCount - 1, mListView.getLastVisiblePosition());
+
+ // Verify that when we're at the bottom of the list, we can't scroll down but we can scroll
+ // up.
+ assertFalse(mListView.canScrollList(1));
+ assertTrue(mListView.canScrollList(-1));
+
+ // Scroll up to the middle of the list
+ final int itemInTheMiddle = itemCount / 2;
+ verifyScrollToPosition(itemInTheMiddle);
+
+ // Verify that when we're in the middle of the list, we can scroll both up and down.
+ assertTrue(mListView.canScrollList(-1));
+ assertTrue(mListView.canScrollList(1));
+ }
+
+ private void verifyScrollBy(int y) {
+ // Here we rely on knowing the fixed pixel height of each row
+ final int startTopPositionInListCoordinates =
+ mListView.getFirstVisiblePosition() * ROW_HEIGHT_PX -
+ mListView.getChildAt(0).getTop();
+
+ // Since scrollListBy is a synchronous operation, we do not need to wait
+ // until we can proceed to test the result
+ mInstrumentation.runOnMainSync(() -> mListView.scrollListBy(y));
+
+ final int endTopPositionInListCoordinates =
+ mListView.getFirstVisiblePosition() * ROW_HEIGHT_PX -
+ mListView.getChildAt(0).getTop();
+
+ // As specified in the Javadocs of AbsListView.scrollListBy, the actual scroll amount
+ // will be capped by the list height minus one pixel
+ final int listHeight = mListView.getHeight();
+ final int expectedScrollAmount = (y > 0) ? Math.min(y, listHeight - 1)
+ : Math.max(y, -(listHeight - 1));
+ int expectedTopPositionInListCoordinates =
+ startTopPositionInListCoordinates + expectedScrollAmount;
+ // Need to clamp it to account for requests that would scroll the content outside
+ // of the available bounds
+ expectedTopPositionInListCoordinates = Math.max(0, expectedTopPositionInListCoordinates);
+ expectedTopPositionInListCoordinates = Math.min(
+ COUNTRY_LIST.length * ROW_HEIGHT_PX - mListView.getHeight(),
+ expectedTopPositionInListCoordinates);
+
+ assertEquals(expectedTopPositionInListCoordinates, endTopPositionInListCoordinates);
+ }
+
+ @Test
+ public void testScrollListBy() {
+ final int listHeight = mListView.getHeight();
+ final int itemCount = COUNTRY_LIST.length;
+
+ // Scroll down by half row height
+ verifyScrollBy(ROW_HEIGHT_PX / 2);
+
+ // Scroll up by full row height - verifying that we're going to stop at the top of the first
+ // row
+ verifyScrollBy(-ROW_HEIGHT_PX);
+
+ // Scroll down by slightly more than a screenful of rows - we expect it to be capped
+ // by the list height minus one pixel.
+ verifyScrollBy(listHeight + ROW_HEIGHT_PX);
+
+ // Scroll down by another half row
+ verifyScrollBy(ROW_HEIGHT_PX / 2);
+
+ // Scroll up by full row height
+ verifyScrollBy(-ROW_HEIGHT_PX);
+
+ // Now scroll all the way down (using position-based scrolling)
+ verifyScrollToPosition(itemCount - 1);
+ assertEquals(itemCount - 1, mListView.getLastVisiblePosition());
+
+ // Scroll up by half row height
+ verifyScrollBy(-ROW_HEIGHT_PX / 2);
+
+ // Scroll down by full row height - verifying that we're going to stop at the bottom of the
+ // last row
+ verifyScrollBy(ROW_HEIGHT_PX);
+
+ // Scroll up halfway into the list - we expect it to be capped by the list height minus
+ // one pixel.
+ verifyScrollBy(-itemCount * ROW_HEIGHT_PX / 2);
+ }
+
+ @Test
+ public void testListScrollAndTap() {
+ // Start a programmatic scroll to position 30. We register a scroll listener on the list
+ // to notify us when position 15 becomes visible.
+ final CountDownLatch scrollLatch = new CountDownLatch(1);
+ mListView.setOnScrollListener(new ScrollPositionListListener(scrollLatch, 15));
+ mInstrumentation.runOnMainSync(() -> mListView.smoothScrollToPosition(30));
+
+ boolean result = false;
+ try {
+ // Since position-based scroll is emulated as a series of mini-flings, scrolling
+ // might take considerable time.
+ result = scrollLatch.await(5, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ // ignore
+ }
+ assertTrue("Timed out while waiting for the scroll to complete", result);
+
+ // Verify that we're here in the middle of the programmatic scroll
+ assertTrue(mListView.getLastVisiblePosition() < 30);
+
+ // Emulate tap in the middle of the list - this should stop our programmatic scroll.
+ // Note that due to asynchronous nature of the moving pieces, we might still get one
+ // more scroll frame as the injected motion events that constitute an emulated tap
+ // are being processed by our list view.
+ CtsTouchUtils.emulateTapOnViewCenter(mInstrumentation, mListView);
+
+ // Sleep for a second
+ SystemClock.sleep(1000);
+
+ // and verify that we're still haven't scrolled down to position 30
+ assertTrue(mListView.getLastVisiblePosition() < 30);
+ }
+
+ private void verifyListScrollAndEmulateFlingGesture(boolean isDownwardsFlingGesture) {
+ // Start a programmatic scroll to position 30. We register a scroll listener on the list
+ // to notify us when position 15 becomes visible.
+ final CountDownLatch scrollLatch = new CountDownLatch(1);
+ mListView.setOnScrollListener(new ScrollPositionListListener(scrollLatch, 15));
+ mInstrumentation.runOnMainSync(() -> mListView.smoothScrollToPosition(30));
+
+ boolean result = false;
+ try {
+ // Since position-based scroll is emulated as a series of mini-flings, scrolling
+ // might take considerable time.
+ result = scrollLatch.await(5, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ // ignore
+ }
+ assertTrue("Timed out while waiting for the scroll to complete", result);
+
+ // Verify that we're here in the middle of the programmatic scroll
+ assertTrue(mListView.getLastVisiblePosition() < 30);
+
+ final int firstVisiblePositionBeforeFling = mListView.getFirstVisiblePosition();
+
+ // At this point the programmatic scroll is still going. Now emulate a fling
+ // gesture and verify that we're going to get to the IDLE state
+ final CountDownLatch flingLatch = new CountDownLatch(1);
+ mListView.setOnScrollListener(new ScrollIdleListListener(flingLatch));
+ CtsTouchUtils.emulateFlingGesture(mInstrumentation, mListView, isDownwardsFlingGesture);
+
+ try {
+ result = flingLatch.await(5, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ // ignore
+ }
+ assertTrue("Timed out while waiting for the fling to complete", result);
+
+ // Note that the actual position in the list at the end of the fling depends on
+ // the processing of the injected sequence of motion events that might differ at milli/micro
+ // second level from run to run
+ if (isDownwardsFlingGesture) {
+ // Verify that the fling gesture has been processed, getting us closer to the
+ // beginning of the list.
+ assertTrue(mListView.getFirstVisiblePosition() < firstVisiblePositionBeforeFling);
+ } else {
+ // Verify that the fling gesture has been processed, getting us closer to the
+ // end of the list.
+ assertTrue(mListView.getFirstVisiblePosition() > firstVisiblePositionBeforeFling);
+ }
+ }
+
+ @Test
+ public void testListScrollAndEmulateDownwardsFlingGesture() {
+ verifyListScrollAndEmulateFlingGesture(true);
+ }
+
+ @Test
+ public void testListScrollAndEmulateUpwardsFlingGesture() {
+ verifyListScrollAndEmulateFlingGesture(false);
+ }
+
+ @Test
+ public void testListFlingWithZeroVelocity() {
+ mListView.setVelocityScale(0.0f);
+
+ final CountDownLatch flingLatch = new CountDownLatch(1);
+ mListView.setOnScrollListener(new ScrollIdleListListener(flingLatch));
+ final int flingAmount =
+ CtsTouchUtils.emulateFlingGesture(mInstrumentation, mListView, false);
+
+ try {
+ assertTrue("Timed out while waiting for the fling to complete",
+ flingLatch.await(5, TimeUnit.SECONDS));
+ } catch (InterruptedException e) {
+ // ignore
+ }
+
+ // Since our velocity scale is 0, we expect that the emulated fling gesture didn't
+ // result in any fling, but just a simple scroll that stopped at the ACTION_UP
+ // event.
+ final int expectedTopOffsetAtFlingEnd = -flingAmount;
+ final int expectedBottomOffsetAtFlingEnd = mListView.getHeight() - flingAmount;
+ final int expectedTopPositionAtFlingEnd = expectedTopOffsetAtFlingEnd / ROW_HEIGHT_PX;
+ final int expectedBottomPositionAtFlingEnd = expectedBottomOffsetAtFlingEnd / ROW_HEIGHT_PX;
+
+ assertEquals(expectedTopPositionAtFlingEnd, mListView.getFirstVisiblePosition());
+ assertEquals(expectedBottomPositionAtFlingEnd, mListView.getLastVisiblePosition());
+ }
+
+ private static class LargeContentAdapter extends BaseAdapter {
+ private final Context mContext;
+ private final int mCount;
+ private final LayoutInflater mLayoutInflater;
+
+ public LargeContentAdapter(Context context, int count) {
+ mContext = context;
+ mCount = count;
+ mLayoutInflater = LayoutInflater.from(mContext);
+ }
+
+ @Override
+ public int getCount() {
+ return mCount;
+ }
+
+ @Override
+ public boolean hasStableIds() {
+ return true;
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+
+ @Override
+ public Object getItem(int position) {
+ return null;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ final TextView textView = (convertView != null) ? (TextView) convertView
+ : (TextView) mLayoutInflater.inflate(R.layout.listitemfixed_layout,
+ parent, false);
+ textView.setText("Item " + position);
+ return textView;
+ }
+ }
+
+ @Test
+ public void testFriction() {
+ // Set an adapter with 100K items so that no matter how fast our fling is, we won't
+ // get to the bottom of the list in one fling
+ mInstrumentation.runOnMainSync(
+ () -> mListView.setAdapter(new LargeContentAdapter(mContext, 100000)));
+ mInstrumentation.waitForIdleSync();
+
+ final CountDownLatch initialFlingLatch = new CountDownLatch(1);
+ mListView.setOnScrollListener(new ScrollIdleListListener(initialFlingLatch));
+ CtsTouchUtils.emulateFlingGesture(mInstrumentation, mListView, false);
+ try {
+ assertTrue("Timed out while waiting for the fling to complete",
+ initialFlingLatch.await(5, TimeUnit.SECONDS));
+ } catch (InterruptedException e) {
+ // ignore
+ }
+
+ final int lastVisiblePositionAfterDefaultFling = mListView.getLastVisiblePosition();
+
+ // Scroll back to the top of the list
+ verifyScrollToPosition(0);
+ // configure the fling to have less friction
+ mListView.setFriction(ViewConfiguration.getScrollFriction() / 2.0f);
+ // and do the fling again
+ final CountDownLatch fastFlingLatch = new CountDownLatch(1);
+ mListView.setOnScrollListener(new ScrollIdleListListener(fastFlingLatch));
+ CtsTouchUtils.emulateFlingGesture(mInstrumentation, mListView, false);
+ try {
+ assertTrue("Timed out while waiting for the fling to complete",
+ fastFlingLatch.await(5, TimeUnit.SECONDS));
+ } catch (InterruptedException e) {
+ // ignore
+ }
+
+ final int lastVisiblePositionAfterFastFling = mListView.getLastVisiblePosition();
+
+ // We expect a fast fling (with lower scroll friction) to end up scrolling more
+ // of our content
+ assertTrue("Default fling ended at " + lastVisiblePositionAfterDefaultFling
+ + ", while fast fling ended at " + lastVisiblePositionAfterFastFling,
+ lastVisiblePositionAfterFastFling > lastVisiblePositionAfterDefaultFling);
+
+ // Scroll back to the top of the list
+ verifyScrollToPosition(0);
+ // configure the fling to have more friction
+ mListView.setFriction(ViewConfiguration.getScrollFriction() * 2.0f);
+ // and do the fling again
+ final CountDownLatch slowFlingLatch = new CountDownLatch(1);
+ mListView.setOnScrollListener(new ScrollIdleListListener(slowFlingLatch));
+ CtsTouchUtils.emulateFlingGesture(mInstrumentation, mListView, false);
+ try {
+ assertTrue("Timed out while waiting for the fling to complete",
+ slowFlingLatch.await(5, TimeUnit.SECONDS));
+ } catch (InterruptedException e) {
+ // ignore
+ }
+
+ final int lastVisiblePositionAfterSlowFling = mListView.getLastVisiblePosition();
+
+ // We expect a slow fling (with higher scroll friction) to end up scrolling less
+ // of our content
+ assertTrue("Default fling ended at " + lastVisiblePositionAfterDefaultFling
+ + ", while slow fling ended at " + lastVisiblePositionAfterSlowFling,
+ lastVisiblePositionAfterSlowFling < lastVisiblePositionAfterDefaultFling);
+ }
+}
diff --git a/hostsidetests/services/activitymanager/app/src/android/server/app/SlowCreateActivity.java b/tests/tests/widget/src/android/widget/cts/AbsSeekBarCtsActivity.java
similarity index 67%
copy from hostsidetests/services/activitymanager/app/src/android/server/app/SlowCreateActivity.java
copy to tests/tests/widget/src/android/widget/cts/AbsSeekBarCtsActivity.java
index 481f5be..1bac1f5 100644
--- a/hostsidetests/services/activitymanager/app/src/android/server/app/SlowCreateActivity.java
+++ b/tests/tests/widget/src/android/widget/cts/AbsSeekBarCtsActivity.java
@@ -14,17 +14,22 @@
* limitations under the License.
*/
-package android.server.app;
+package android.widget.cts;
import android.app.Activity;
import android.os.Bundle;
+import android.widget.AbsSeekBar;
-public class SlowCreateActivity extends Activity {
+/**
+ * A minimal application for {@link AbsSeekBar} test
+ */
+public class AbsSeekBarCtsActivity extends Activity {
+ /**
+ * Called when the activity is first created.
+ */
@Override
- protected void onCreate(Bundle savedInstanceState) {
- try {
- Thread.sleep(2000);
- } catch(InterruptedException e) {}
+ public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ setContentView(R.layout.seekbar_layout);
}
}
diff --git a/tests/tests/widget/src/android/widget/cts/AbsSeekBarTest.java b/tests/tests/widget/src/android/widget/cts/AbsSeekBarTest.java
index 93032f6..94476c8 100644
--- a/tests/tests/widget/src/android/widget/cts/AbsSeekBarTest.java
+++ b/tests/tests/widget/src/android/widget/cts/AbsSeekBarTest.java
@@ -16,49 +16,75 @@
package android.widget.cts;
-import android.widget.cts.R;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
import android.app.Activity;
+import android.app.Instrumentation;
import android.content.Context;
import android.content.res.ColorStateList;
import android.cts.util.PollingCheck;
-import android.graphics.Canvas;
+import android.cts.util.WidgetTestUtils;
import android.graphics.Color;
-import android.graphics.ColorFilter;
import android.graphics.PorterDuff;
+import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
-import android.test.ActivityInstrumentationTestCase2;
-import android.test.UiThreadTest;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.widget.AbsSeekBar;
import android.widget.SeekBar;
+import android.widget.cts.util.TestUtils;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+
+import java.util.List;
/**
* Test {@link AbsSeekBar}.
*/
-public class AbsSeekBarTest extends ActivityInstrumentationTestCase2<ProgressBarCtsActivity> {
- public AbsSeekBarTest() {
- super("android.widget.cts", ProgressBarCtsActivity.class);
- }
-
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class AbsSeekBarTest {
+ private Instrumentation mInstrumentation;
private Activity mActivity;
- @Override
- protected void setUp() throws Exception {
- super.setUp();
+ @Rule
+ public ActivityTestRule<AbsSeekBarCtsActivity> mActivityRule
+ = new ActivityTestRule<>(AbsSeekBarCtsActivity.class);
- mActivity = getActivity();
+ @Before
+ public void setup() {
+ mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ mActivity = mActivityRule.getActivity();
}
+ @Test
public void testConstructor() {
new MyAbsSeekBar(mActivity);
new MyAbsSeekBar(mActivity, null);
new MyAbsSeekBar(mActivity, null, android.R.attr.progressBarStyle);
+
+ new MyAbsSeekBar(mActivity, null, 0, android.R.style.Widget_Material_Light_ProgressBar);
}
+ @Test
public void testAccessThumbOffset() {
AbsSeekBar myAbsSeekBar = new MyAbsSeekBar(mActivity);
final int positive = 5;
@@ -75,58 +101,80 @@
assertEquals(negative, myAbsSeekBar.getThumbOffset());
}
- public void testSetThumb() {
- MyAbsSeekBar myAbsSeekBar = new MyAbsSeekBar(mActivity);
+ @Test
+ public void testAccessThumb() {
+ // Both are pointing to the same object. This works around current limitation in CTS
+ // coverage report tool for properly reporting coverage of base class method calls.
+ final MyAbsSeekBar myAbsSeekBar = new MyAbsSeekBar(mActivity);
+ final AbsSeekBar absSeekBar = myAbsSeekBar;
+
Drawable drawable1 = mActivity.getDrawable(R.drawable.scenery);
Drawable drawable2 = mActivity.getDrawable(R.drawable.pass);
assertFalse(myAbsSeekBar.verifyDrawable(drawable1));
assertFalse(myAbsSeekBar.verifyDrawable(drawable2));
- myAbsSeekBar.setThumb(drawable1);
- assertSame(drawable1, myAbsSeekBar.getThumb());
+ absSeekBar.setThumb(drawable1);
+ assertSame(drawable1, absSeekBar.getThumb());
assertTrue(myAbsSeekBar.verifyDrawable(drawable1));
assertFalse(myAbsSeekBar.verifyDrawable(drawable2));
- myAbsSeekBar.setThumb(drawable2);
- assertSame(drawable2, myAbsSeekBar.getThumb());
+ absSeekBar.setThumb(drawable2);
+ assertSame(drawable2, absSeekBar.getThumb());
assertFalse(myAbsSeekBar.verifyDrawable(drawable1));
assertTrue(myAbsSeekBar.verifyDrawable(drawable2));
}
- public void testSetTickMark() {
- MyAbsSeekBar myAbsSeekBar = new MyAbsSeekBar(mActivity);
+ @Test
+ public void testAccessTickMark() {
+ // Both are pointing to the same object. This works around current limitation in CTS
+ // coverage report tool for properly reporting coverage of base class method calls.
+ final MyAbsSeekBar myAbsSeekBar = new MyAbsSeekBar(mActivity);
+ final AbsSeekBar absSeekBar = myAbsSeekBar;
+
Drawable drawable1 = mActivity.getDrawable(R.drawable.black);
Drawable drawable2 = mActivity.getDrawable(R.drawable.black);
assertFalse(myAbsSeekBar.verifyDrawable(drawable1));
assertFalse(myAbsSeekBar.verifyDrawable(drawable2));
- myAbsSeekBar.setTickMark(drawable1);
- assertSame(drawable1, myAbsSeekBar.getTickMark());
+ absSeekBar.setTickMark(drawable1);
+ assertSame(drawable1, absSeekBar.getTickMark());
assertTrue(myAbsSeekBar.verifyDrawable(drawable1));
assertFalse(myAbsSeekBar.verifyDrawable(drawable2));
- myAbsSeekBar.setTickMark(drawable2);
- assertSame(drawable2, myAbsSeekBar.getTickMark());
+ absSeekBar.setTickMark(drawable2);
+ assertSame(drawable2, absSeekBar.getTickMark());
assertFalse(myAbsSeekBar.verifyDrawable(drawable1));
assertTrue(myAbsSeekBar.verifyDrawable(drawable2));
}
+ @Test
public void testDrawableStateChanged() {
MyAbsSeekBar myAbsSeekBar = new MyAbsSeekBar(mActivity);
- MockDrawable drawable = new MockDrawable();
- myAbsSeekBar.setProgressDrawable(drawable);
+ Drawable mockProgressDrawable = spy(new ColorDrawable(Color.YELLOW));
+ myAbsSeekBar.setProgressDrawable(mockProgressDrawable);
+ ArgumentCaptor<Integer> alphaCaptor = ArgumentCaptor.forClass(Integer.class);
myAbsSeekBar.setEnabled(false);
myAbsSeekBar.drawableStateChanged();
- assertEquals(0, drawable.getAlpha());
+ verify(mockProgressDrawable, atLeastOnce()).setAlpha(alphaCaptor.capture());
+ // Verify that the last call to setAlpha was with argument 0x00
+ List<Integer> alphaCaptures = alphaCaptor.getAllValues();
+ assertTrue(!alphaCaptures.isEmpty());
+ assertEquals(Integer.valueOf(0x00), alphaCaptures.get(alphaCaptures.size() - 1));
+ alphaCaptor = ArgumentCaptor.forClass(Integer.class);
myAbsSeekBar.setEnabled(true);
myAbsSeekBar.drawableStateChanged();
- assertEquals(0xFF, drawable.getAlpha());
+ verify(mockProgressDrawable, atLeastOnce()).setAlpha(alphaCaptor.capture());
+ // Verify that the last call to setAlpha was with argument 0xFF
+ alphaCaptures = alphaCaptor.getAllValues();
+ assertTrue(!alphaCaptures.isEmpty());
+ assertEquals(Integer.valueOf(0xFF), alphaCaptures.get(alphaCaptures.size() - 1));
}
+ @Test
public void testVerifyDrawable() {
MyAbsSeekBar myAbsSeekBar = new MyAbsSeekBar(mActivity);
Drawable drawable1 = mActivity.getDrawable(R.drawable.scenery);
@@ -160,44 +208,35 @@
assertTrue(myAbsSeekBar.verifyDrawable(drawable4));
}
- public void testAccessKeyProgressIncrement() throws Throwable {
+ @Test
+ public void testAccessKeyProgressIncrement() {
// AbsSeekBar is an abstract class, use its subclass: SeekBar to do this test.
- runTestOnUiThread(new Runnable() {
- public void run() {
- mActivity.setContentView(R.layout.seekbar_layout);
- }
- });
- getInstrumentation().waitForIdleSync();
+ mInstrumentation.runOnMainSync(() -> mActivity.setContentView(R.layout.seekbar_layout));
+ mInstrumentation.waitForIdleSync();
final SeekBar seekBar = (SeekBar) mActivity.findViewById(R.id.seekBar);
final int keyProgressIncrement = 2;
- runTestOnUiThread(new Runnable() {
- public void run() {
- seekBar.setKeyProgressIncrement(keyProgressIncrement);
- seekBar.setFocusable(true);
- seekBar.requestFocus();
- }
+ mInstrumentation.runOnMainSync(() -> {
+ seekBar.setKeyProgressIncrement(keyProgressIncrement);
+ seekBar.setFocusable(true);
+ seekBar.requestFocus();
});
- new PollingCheck(1000) {
- @Override
- protected boolean check() {
- return seekBar.hasWindowFocus();
- }
- }.run();
+ PollingCheck.waitFor(1000, seekBar::hasWindowFocus);
assertEquals(keyProgressIncrement, seekBar.getKeyProgressIncrement());
int oldProgress = seekBar.getProgress();
KeyEvent keyEvent = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_RIGHT);
- getInstrumentation().sendKeySync(keyEvent);
+ mInstrumentation.sendKeySync(keyEvent);
assertEquals(oldProgress + keyProgressIncrement, seekBar.getProgress());
oldProgress = seekBar.getProgress();
keyEvent = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_LEFT);
- getInstrumentation().sendKeySync(keyEvent);
+ mInstrumentation.sendKeySync(keyEvent);
assertEquals(oldProgress - keyProgressIncrement, seekBar.getProgress());
}
- public void testSetMax() {
- MyAbsSeekBar myAbsSeekBar = new MyAbsSeekBar(mActivity, null, R.style.TestProgressBar);
+ @Test
+ public void testAccessMax() {
+ AbsSeekBar myAbsSeekBar = new MyAbsSeekBar(mActivity, null, R.style.TestProgressBar);
int progress = 10;
myAbsSeekBar.setProgress(progress);
@@ -224,60 +263,81 @@
assertEquals(keyProgressIncrement + 1, myAbsSeekBar.getKeyProgressIncrement());
}
- @UiThreadTest
+ @Test
public void testThumbTint() {
- mActivity.setContentView(R.layout.seekbar_layout);
-
- SeekBar inflatedView = (SeekBar) mActivity.findViewById(R.id.thumb_tint);
+ AbsSeekBar inflatedView = (AbsSeekBar) mActivity.findViewById(R.id.thumb_tint);
assertEquals("Thumb tint inflated correctly",
Color.WHITE, inflatedView.getThumbTintList().getDefaultColor());
assertEquals("Thumb tint mode inflated correctly",
PorterDuff.Mode.SRC_OVER, inflatedView.getThumbTintMode());
- MockDrawable thumb = new MockDrawable();
- SeekBar view = new SeekBar(mActivity);
+ Drawable mockThumb = spy(new ColorDrawable(Color.BLUE));
- view.setThumb(thumb);
- assertFalse("No thumb tint applied by default", thumb.hasCalledSetTint());
+ mInstrumentation.runOnMainSync(() -> inflatedView.setThumb(mockThumb));
+ verify(mockThumb, times(1)).setTintList(TestUtils.colorStateListOf(Color.WHITE));
- view.setThumbTintList(ColorStateList.valueOf(Color.WHITE));
- assertTrue("Thumb tint applied when setThumbTintList() called after setThumb()",
- thumb.hasCalledSetTint());
+ reset(mockThumb);
+ mInstrumentation.runOnMainSync(
+ () -> inflatedView.setThumbTintList(ColorStateList.valueOf(Color.RED)));
+ verify(mockThumb, times(1)).setTintList(TestUtils.colorStateListOf(Color.RED));
- thumb.reset();
- view.setThumb(null);
- view.setThumb(thumb);
- assertTrue("Thumb tint applied when setThumbTintList() called before setThumb()",
- thumb.hasCalledSetTint());
+ mInstrumentation.runOnMainSync(
+ () -> inflatedView.setThumbTintMode(PorterDuff.Mode.DST_ATOP));
+ assertEquals("Thumb tint mode changed correctly",
+ PorterDuff.Mode.DST_ATOP, inflatedView.getThumbTintMode());
+
+ reset(mockThumb);
+ mInstrumentation.runOnMainSync(() -> {
+ inflatedView.setThumb(null);
+ inflatedView.setThumb(mockThumb);
+ });
+ verify(mockThumb, times(1)).setTintList(TestUtils.colorStateListOf(Color.RED));
}
- @UiThreadTest
+ @Test
public void testTickMarkTint() {
- mActivity.setContentView(R.layout.seekbar_layout);
-
- SeekBar inflatedView = (SeekBar) mActivity.findViewById(R.id.tick_mark_tint);
+ AbsSeekBar inflatedView = (AbsSeekBar) mActivity.findViewById(R.id.tick_mark_tint);
assertEquals("TickMark tint inflated correctly",
Color.WHITE, inflatedView.getTickMarkTintList().getDefaultColor());
assertEquals("TickMark tint mode inflated correctly",
PorterDuff.Mode.SRC_OVER, inflatedView.getTickMarkTintMode());
- MockDrawable tickMark = new MockDrawable();
- SeekBar view = new SeekBar(mActivity);
+ Drawable mockTickMark = spy(new ColorDrawable(Color.BLUE));
- view.setTickMark(tickMark);
- assertFalse("No tickMark tint applied by default", tickMark.hasCalledSetTint());
+ mInstrumentation.runOnMainSync(() -> inflatedView.setTickMark(mockTickMark));
+ verify(mockTickMark, times(1)).setTintList(TestUtils.colorStateListOf(Color.WHITE));
- view.setTickMarkTintList(ColorStateList.valueOf(Color.WHITE));
- assertTrue("TickMark tint applied when setTickMarkTintList() called after setTickMark()",
- tickMark.hasCalledSetTint());
+ reset(mockTickMark);
+ mInstrumentation.runOnMainSync(
+ () -> inflatedView.setTickMarkTintList(ColorStateList.valueOf(Color.RED)));
+ verify(mockTickMark, times(1)).setTintList(TestUtils.colorStateListOf(Color.RED));
- tickMark.reset();
- view.setTickMark(null);
- view.setTickMark(tickMark);
- assertTrue("TickMark tint applied when setTickMarkTintList() called before setTickMark()",
- tickMark.hasCalledSetTint());
+ mInstrumentation.runOnMainSync(
+ () -> inflatedView.setTickMarkTintMode(PorterDuff.Mode.DARKEN));
+ assertEquals("TickMark tint mode changed correctly",
+ PorterDuff.Mode.DARKEN, inflatedView.getTickMarkTintMode());
+
+ reset(mockTickMark);
+ mInstrumentation.runOnMainSync(() -> {
+ inflatedView.setTickMark(null);
+ inflatedView.setTickMark(mockTickMark);
+ });
+ verify(mockTickMark, times(1)).setTintList(TestUtils.colorStateListOf(Color.RED));
+ }
+
+ @Test
+ public void testAccessSplitTrack() {
+ AbsSeekBar inflatedView = (AbsSeekBar) mActivity.findViewById(R.id.tick_mark_tint);
+
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, inflatedView,
+ () -> inflatedView.setSplitTrack(true));
+ assertTrue(inflatedView.getSplitTrack());
+
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, inflatedView,
+ () -> inflatedView.setSplitTrack(false));
+ assertFalse(inflatedView.getSplitTrack());
}
private static class MyAbsSeekBar extends AbsSeekBar {
@@ -293,6 +353,10 @@
super(context, attrs, defStyle);
}
+ public MyAbsSeekBar(Context context, AttributeSet attrs, int defStyle, int defStyleRes) {
+ super(context, attrs, defStyle, defStyleRes);
+ }
+
@Override
protected void drawableStateChanged() {
super.drawableStateChanged();
@@ -303,45 +367,4 @@
return super.verifyDrawable(who);
}
}
-
- private static class MockDrawable extends Drawable {
- private int mAlpha;
- private boolean mCalledDraw = false;
- private boolean mCalledSetTint = false;
-
- @Override
- public void draw(Canvas canvas) { }
-
- public void reset() {
- mCalledDraw = false;
- mCalledSetTint = false;
- }
-
- @Override
- public int getOpacity() {
- return 0;
- }
-
- @Override
- public void setAlpha(int alpha) {
- mAlpha = alpha;
- }
-
- public int getAlpha() {
- return mAlpha;
- }
-
- @Override
- public void setColorFilter(ColorFilter cf) { }
-
- @Override
- public void setTintList(ColorStateList tint) {
- super.setTintList(tint);
- mCalledSetTint = true;
- }
-
- public boolean hasCalledSetTint() {
- return mCalledSetTint;
- }
- }
}
diff --git a/tests/tests/widget/src/android/widget/cts/AbsSpinnerTest.java b/tests/tests/widget/src/android/widget/cts/AbsSpinnerTest.java
index e07e8b1..0a851de 100644
--- a/tests/tests/widget/src/android/widget/cts/AbsSpinnerTest.java
+++ b/tests/tests/widget/src/android/widget/cts/AbsSpinnerTest.java
@@ -16,17 +16,21 @@
package android.widget.cts;
-import android.widget.cts.R;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
-
-import org.xmlpull.v1.XmlPullParser;
-
-import android.content.Context;
+import android.app.Activity;
+import android.app.Instrumentation;
import android.database.DataSetObserver;
import android.graphics.Rect;
import android.os.Parcelable;
-import android.test.ActivityInstrumentationTestCase2;
-import android.test.UiThreadTest;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
import android.util.AttributeSet;
import android.util.Xml;
import android.view.View;
@@ -39,109 +43,116 @@
import android.widget.Spinner;
import android.widget.SpinnerAdapter;
-public class AbsSpinnerTest extends ActivityInstrumentationTestCase2<RelativeLayoutCtsActivity> {
- private Context mContext;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.xmlpull.v1.XmlPullParser;
- public AbsSpinnerTest() {
- super("android.widget.cts", RelativeLayoutCtsActivity.class);
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class AbsSpinnerTest {
+ private Instrumentation mInstrumentation;
+ private Activity mActivity;
+ private AbsSpinner mAbsSpinner;
+
+ @Rule
+ public ActivityTestRule<RelativeLayoutCtsActivity> mActivityRule
+ = new ActivityTestRule<>(RelativeLayoutCtsActivity.class);
+
+ @Before
+ public void setup() {
+ mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ mActivity = mActivityRule.getActivity();
+ mAbsSpinner = (AbsSpinner) mActivity.findViewById(R.id.spinner1);
}
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mContext = getInstrumentation().getTargetContext();
- }
-
- @UiThreadTest
+ @Test
public void testConstructor() {
- new Spinner(mContext);
+ new Spinner(mActivity);
- new Spinner(mContext, null);
+ new Spinner(mActivity, null);
- new Spinner(mContext, null, android.R.attr.spinnerStyle);
+ new Spinner(mActivity, null, android.R.attr.spinnerStyle);
- new Gallery(mContext);
- new Gallery(mContext, null);
- new Gallery(mContext, null, 0);
+ new Gallery(mActivity);
+ new Gallery(mActivity, null);
+ new Gallery(mActivity, null, 0);
- XmlPullParser parser = mContext.getResources().getXml(R.layout.gallery_test);
+ XmlPullParser parser = mActivity.getResources().getXml(R.layout.gallery_test);
AttributeSet attrs = Xml.asAttributeSet(parser);
- new Gallery(mContext, attrs);
- new Gallery(mContext, attrs, 0);
+ new Gallery(mActivity, attrs);
+ new Gallery(mActivity, attrs, 0);
}
- @UiThreadTest
/**
* Check points:
* 1. Jump to the specific item.
*/
+ @Test
public void testSetSelectionIntBoolean() {
- AbsSpinner absSpinner = (AbsSpinner) getActivity().findViewById(R.id.spinner1);
- ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(mContext,
+ ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(mActivity,
android.widget.cts.R.array.string, android.R.layout.simple_spinner_item);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
- absSpinner.setAdapter(adapter);
- assertEquals(0, absSpinner.getSelectedItemPosition());
+ mInstrumentation.runOnMainSync(() -> mAbsSpinner.setAdapter(adapter));
+ assertEquals(0, mAbsSpinner.getSelectedItemPosition());
- absSpinner.setSelection(1, true);
- assertEquals(1, absSpinner.getSelectedItemPosition());
+ mInstrumentation.runOnMainSync(() -> mAbsSpinner.setSelection(1, true));
+ assertEquals(1, mAbsSpinner.getSelectedItemPosition());
- absSpinner.setSelection(absSpinner.getCount() - 1, false);
- assertEquals(absSpinner.getCount() - 1, absSpinner.getSelectedItemPosition());
+ mInstrumentation.runOnMainSync(
+ () -> mAbsSpinner.setSelection(mAbsSpinner.getCount() - 1, false));
+ assertEquals(mAbsSpinner.getCount() - 1, mAbsSpinner.getSelectedItemPosition());
// The animation effect depends on implementation in AbsSpinner's subClass.
// It is not meaningful to check it.
}
- @UiThreadTest
/**
* Check points:
* 1. the currently selected item should be the one which set using this method.
*/
+ @Test
public void testSetSelectionInt() {
- AbsSpinner absSpinner = (AbsSpinner) getActivity().findViewById(R.id.spinner1);
- ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(mContext,
+ ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(mActivity,
android.widget.cts.R.array.string, android.R.layout.simple_spinner_item);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
- absSpinner.setAdapter(adapter);
- assertEquals(0, absSpinner.getSelectedItemPosition());
+ mInstrumentation.runOnMainSync(() -> mAbsSpinner.setAdapter(adapter));
+ assertEquals(0, mAbsSpinner.getSelectedItemPosition());
- absSpinner.setSelection(1);
- assertEquals(1, absSpinner.getSelectedItemPosition());
+ mInstrumentation.runOnMainSync(() -> mAbsSpinner.setSelection(1));
+ assertEquals(1, mAbsSpinner.getSelectedItemPosition());
- absSpinner.setSelection(absSpinner.getCount() - 1);
- assertEquals(absSpinner.getCount() - 1, absSpinner.getSelectedItemPosition());
+ mInstrumentation.runOnMainSync(
+ () -> mAbsSpinner.setSelection(mAbsSpinner.getCount() - 1));
+ assertEquals(mAbsSpinner.getCount() - 1, mAbsSpinner.getSelectedItemPosition());
}
- @UiThreadTest
/**
* Check points:
* 1. the adapter returned from getAdapter() should be the one specified using setAdapter().
* 2. the adapter provides methods to transform spinner items based on their position
* relative to the selected item.
*/
+ @Test
public void testAccessAdapter() {
- AbsSpinner absSpinner = (AbsSpinner) getActivity().findViewById(R.id.spinner1);
- ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(mContext,
+ ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(mActivity,
android.widget.cts.R.array.string, android.R.layout.simple_spinner_item);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
- absSpinner.setAdapter(adapter);
- assertSame(adapter, absSpinner.getAdapter());
- assertEquals(adapter.getCount(), absSpinner.getCount());
- assertEquals(0, absSpinner.getSelectedItemPosition());
- assertEquals(adapter.getItemId(0), absSpinner.getSelectedItemId());
- absSpinner.setSelection(1);
- assertEquals(1, absSpinner.getSelectedItemPosition());
- assertEquals(adapter.getItemId(1), absSpinner.getSelectedItemId());
-
- // issue 1695243, if adapter is null, NullPointerException will be thrown when do layout.
- // There is neither limit in code nor description about it in javadoc.
+ mInstrumentation.runOnMainSync(() -> mAbsSpinner.setAdapter(adapter));
+ assertSame(adapter, mAbsSpinner.getAdapter());
+ assertEquals(adapter.getCount(), mAbsSpinner.getCount());
+ assertEquals(0, mAbsSpinner.getSelectedItemPosition());
+ assertEquals(adapter.getItemId(0), mAbsSpinner.getSelectedItemId());
+ mInstrumentation.runOnMainSync(() -> mAbsSpinner.setSelection(1));
+ assertEquals(1, mAbsSpinner.getSelectedItemPosition());
+ assertEquals(adapter.getItemId(1), mAbsSpinner.getSelectedItemId());
}
- @UiThreadTest
+ @Test
public void testRequestLayout() {
- AbsSpinner absSpinner = new Spinner(mContext);
+ AbsSpinner absSpinner = new Spinner(mActivity);
absSpinner.layout(0, 0, 200, 300);
assertFalse(absSpinner.isLayoutRequested());
@@ -149,27 +160,25 @@
assertTrue(absSpinner.isLayoutRequested());
}
- @UiThreadTest
/**
* Check points:
* 1. The value returned from getCount() equals the count of Adapter associated with
* this AdapterView.
*/
+ @Test
public void testGetCount() {
- AbsSpinner absSpinner = (AbsSpinner) getActivity().findViewById(R.id.spinner1);
-
- ArrayAdapter<CharSequence> adapter1 = ArrayAdapter.createFromResource(mContext,
+ ArrayAdapter<CharSequence> adapter1 = ArrayAdapter.createFromResource(mActivity,
android.widget.cts.R.array.string, android.R.layout.simple_spinner_item);
- absSpinner.setAdapter(adapter1);
- assertEquals(adapter1.getCount(), absSpinner.getCount());
+ mInstrumentation.runOnMainSync(() -> mAbsSpinner.setAdapter(adapter1));
+ assertEquals(adapter1.getCount(), mAbsSpinner.getCount());
CharSequence anotherStringArray[] = { "another array string 1", "another array string 2" };
- ArrayAdapter<CharSequence> adapter2 = new ArrayAdapter<CharSequence>(mContext,
+ ArrayAdapter<CharSequence> adapter2 = new ArrayAdapter<>(mActivity,
android.R.layout.simple_spinner_item, anotherStringArray);
- absSpinner.setAdapter(adapter2);
- assertEquals(anotherStringArray.length, absSpinner.getCount());
+ mInstrumentation.runOnMainSync(() -> mAbsSpinner.setAdapter(adapter2));
+ assertEquals(anotherStringArray.length, mAbsSpinner.getCount());
}
/**
@@ -177,9 +186,9 @@
* 1. Should return the position of the item which contains the specified point.
* 2. Should return INVALID_POSITION if the point does not intersect an item
*/
- @UiThreadTest
+ @Test
public void testPointToPosition() {
- AbsSpinner absSpinner = new Gallery(mContext);
+ AbsSpinner absSpinner = new Gallery(mActivity);
MockSpinnerAdapter adapter = new MockSpinnerAdapter();
assertEquals(AdapterView.INVALID_POSITION, absSpinner.pointToPosition(10, 10));
@@ -212,9 +221,9 @@
* 1. Should return the view corresponding to the currently selected item.
* 2. Should return null if nothing is selected.
*/
- @UiThreadTest
+ @Test
public void testGetSelectedView() {
- AbsSpinner absSpinner = new Gallery(mContext);
+ AbsSpinner absSpinner = new Gallery(mActivity);
MockSpinnerAdapter adapter = new MockSpinnerAdapter();
assertNull(absSpinner.getSelectedView());
@@ -224,46 +233,35 @@
absSpinner.setSelection(1, true);
assertSame(absSpinner.getChildAt(1), absSpinner.getSelectedView());
-
}
- @UiThreadTest
/**
* Check points:
* 1. the view's current state saved by onSaveInstanceState() should be correctly restored
* after onRestoreInstanceState().
*/
+ @Test
public void testOnSaveAndRestoreInstanceState() {
- AbsSpinner absSpinner = (AbsSpinner) getActivity().findViewById(R.id.spinner1);
- ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(mContext,
+ ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(mActivity,
android.widget.cts.R.array.string, android.R.layout.simple_spinner_item);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
- absSpinner.setAdapter(adapter);
- assertEquals(0, absSpinner.getSelectedItemPosition());
- assertEquals(adapter.getItemId(0), absSpinner.getSelectedItemId());
- Parcelable parcelable = absSpinner.onSaveInstanceState();
+ mInstrumentation.runOnMainSync(() -> mAbsSpinner.setAdapter(adapter));
+ assertEquals(0, mAbsSpinner.getSelectedItemPosition());
+ assertEquals(adapter.getItemId(0), mAbsSpinner.getSelectedItemId());
+ Parcelable parcelable = mAbsSpinner.onSaveInstanceState();
- absSpinner.setSelection(1);
- assertEquals(1, absSpinner.getSelectedItemPosition());
- assertEquals(adapter.getItemId(1), absSpinner.getSelectedItemId());
+ mInstrumentation.runOnMainSync(() -> mAbsSpinner.setSelection(1));
+ assertEquals(1, mAbsSpinner.getSelectedItemPosition());
+ assertEquals(adapter.getItemId(1), mAbsSpinner.getSelectedItemId());
- absSpinner.onRestoreInstanceState(parcelable);
- absSpinner.measure(View.MeasureSpec.EXACTLY, View.MeasureSpec.EXACTLY);
- absSpinner.layout(absSpinner.getLeft(), absSpinner.getTop(), absSpinner.getRight(),
- absSpinner.getBottom());
- assertEquals(0, absSpinner.getSelectedItemPosition());
- assertEquals(adapter.getItemId(0), absSpinner.getSelectedItemId());
- }
-
- public void testGenerateDefaultLayoutParams() {
-// final MockSpinner absSpinner = new MockSpinner(mContext);
-// LayoutParams layoutParams = (LayoutParams) absSpinner.generateDefaultLayoutParams();
-// assertEquals(LayoutParams.MATCH_PARENT, layoutParams.width);
-// assertEquals(LayoutParams.WRAP_CONTENT, layoutParams.height);
- }
-
- public void testOnMeasure() {
- // onMeasure() is implementation details, do NOT test
+ mInstrumentation.runOnMainSync(() -> {
+ mAbsSpinner.onRestoreInstanceState(parcelable);
+ mAbsSpinner.measure(View.MeasureSpec.EXACTLY, View.MeasureSpec.EXACTLY);
+ mAbsSpinner.layout(mAbsSpinner.getLeft(), mAbsSpinner.getTop(),
+ mAbsSpinner.getRight(), mAbsSpinner.getBottom());
+ });
+ assertEquals(0, mAbsSpinner.getSelectedItemPosition());
+ assertEquals(adapter.getItemId(0), mAbsSpinner.getSelectedItemId());
}
/*
@@ -299,7 +297,7 @@
}
public View getView(int position, View convertView, ViewGroup parent) {
- return new ImageView(mContext);
+ return new ImageView(mActivity);
}
public int getViewTypeCount() {
diff --git a/tests/tests/widget/src/android/widget/cts/AbsoluteLayoutTest.java b/tests/tests/widget/src/android/widget/cts/AbsoluteLayoutTest.java
index a838f65..d1b67eb 100644
--- a/tests/tests/widget/src/android/widget/cts/AbsoluteLayoutTest.java
+++ b/tests/tests/widget/src/android/widget/cts/AbsoluteLayoutTest.java
@@ -16,43 +16,55 @@
package android.widget.cts;
-import android.widget.cts.R;
-
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
import android.app.Activity;
+import android.app.Instrumentation;
import android.content.Context;
import android.cts.util.WidgetTestUtils;
-import android.test.ActivityInstrumentationTestCase2;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
import android.util.AttributeSet;
import android.util.Xml;
import android.view.ViewGroup;
import android.widget.AbsoluteLayout;
import android.widget.AbsoluteLayout.LayoutParams;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
import java.io.IOException;
-@SuppressWarnings("deprecation")
-public class AbsoluteLayoutTest extends ActivityInstrumentationTestCase2<CtsActivity> {
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class AbsoluteLayoutTest {
private static final int DEFAULT_X = 5;
private static final int DEFAULT_Y = 10;
private static final int DEFAULT_WIDTH = 20;
private static final int DEFAULT_HEIGHT = 30;
+ private Instrumentation mInstrumentation;
private Activity mActivity;
private MyAbsoluteLayout mMyAbsoluteLayout;
private LayoutParams mAbsoluteLayoutParams;
- public AbsoluteLayoutTest() {
- super("android.widget.cts", CtsActivity.class);
- }
+ @Rule
+ public ActivityTestRule<CtsActivity> mActivityRule
+ = new ActivityTestRule<>(CtsActivity.class);
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mActivity = getActivity();
+ @Before
+ public void setup() {
+ mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ mActivity = mActivityRule.getActivity();
mMyAbsoluteLayout = new MyAbsoluteLayout(mActivity);
mAbsoluteLayoutParams = new LayoutParams(DEFAULT_WIDTH, DEFAULT_HEIGHT,
DEFAULT_X, DEFAULT_Y);
@@ -64,6 +76,7 @@
return Xml.asAttributeSet(parser);
}
+ @Test
public void testConstructor() throws XmlPullParserException, IOException {
AttributeSet attrs = getAttributeSet();
@@ -74,14 +87,7 @@
new AbsoluteLayout(mActivity, attrs, -1);
}
- public void testOnMeasure() {
- // onMeasure() is implementation details, do NOT test
- }
-
- public void testOnLayout() {
- // onMeasure() is implementation details, do NOT test
- }
-
+ @Test
public void testCheckLayoutParams() {
assertTrue(mMyAbsoluteLayout.checkLayoutParams(mAbsoluteLayoutParams));
@@ -90,13 +96,10 @@
assertFalse(mMyAbsoluteLayout.checkLayoutParams(null));
}
+ @Test
public void testGenerateLayoutParams1() throws Throwable {
- runTestOnUiThread(new Runnable() {
- public void run() {
- mActivity.setContentView(R.layout.absolute_layout);
- }
- });
- getInstrumentation().waitForIdleSync();
+ mInstrumentation.runOnMainSync(() -> mActivity.setContentView(R.layout.absolute_layout));
+ mInstrumentation.waitForIdleSync();
AbsoluteLayout layout = (AbsoluteLayout) mActivity.findViewById(R.id.absolute_view);
LayoutParams params = (LayoutParams) layout.generateLayoutParams(getAttributeSet());
@@ -107,6 +110,7 @@
assertEquals(0, params.y);
}
+ @Test
public void testGenerateLayoutParams2() {
LayoutParams params =
(LayoutParams) mMyAbsoluteLayout.generateLayoutParams(mAbsoluteLayoutParams);
@@ -115,15 +119,14 @@
assertEquals(DEFAULT_HEIGHT, params.height);
assertEquals(0, params.x);
assertEquals(0, params.y);
-
- try {
- mMyAbsoluteLayout.generateLayoutParams((LayoutParams) null);
- fail("did not throw NullPointerException when ViewGroup.LayoutParams is null.");
- } catch (NullPointerException e) {
- // expected, test success
- }
}
+ @Test(expected=NullPointerException.class)
+ public void testGenerateLayoutParamsFromNull() {
+ mMyAbsoluteLayout.generateLayoutParams((LayoutParams) null);
+ }
+
+ @Test
public void testGenerateDefaultLayoutParams() {
LayoutParams params = (LayoutParams) mMyAbsoluteLayout.generateDefaultLayoutParams();
diff --git a/tests/tests/widget/src/android/widget/cts/AbsoluteLayout_LayoutParamsTest.java b/tests/tests/widget/src/android/widget/cts/AbsoluteLayout_LayoutParamsTest.java
index 2723581..53ff4da 100644
--- a/tests/tests/widget/src/android/widget/cts/AbsoluteLayout_LayoutParamsTest.java
+++ b/tests/tests/widget/src/android/widget/cts/AbsoluteLayout_LayoutParamsTest.java
@@ -16,23 +16,36 @@
package android.widget.cts;
-import android.widget.cts.R;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
+import android.content.Context;
import android.cts.util.WidgetTestUtils;
-import android.test.AndroidTestCase;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
import android.util.AttributeSet;
import android.util.Xml;
import android.widget.AbsoluteLayout;
import android.widget.AbsoluteLayout.LayoutParams;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
import java.io.IOException;
-@SuppressWarnings("deprecation")
-public class AbsoluteLayout_LayoutParamsTest extends AndroidTestCase {
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class AbsoluteLayout_LayoutParamsTest {
+ private Context mContext;
+
+ @Before
+ public void setup() {
+ mContext = InstrumentationRegistry.getTargetContext();
+ }
private AttributeSet getAttributeSet() throws XmlPullParserException, IOException {
XmlPullParser parser = mContext.getResources().getLayout(R.layout.absolute_layout);
@@ -40,6 +53,7 @@
return Xml.asAttributeSet(parser);
}
+ @Test
public void testConstructor() throws XmlPullParserException, IOException {
LayoutParams layoutParams;
@@ -58,6 +72,7 @@
new AbsoluteLayout.LayoutParams(mContext, getAttributeSet());
}
+ @Test
public void testDebug() {
LayoutParams layoutParams = new AbsoluteLayout.LayoutParams(1, 2, 3, 4);
assertNotNull(layoutParams.debug("test: "));
diff --git a/hostsidetests/services/activitymanager/app/src/android/server/app/SlowCreateActivity.java b/tests/tests/widget/src/android/widget/cts/ActionMenuViewCtsActivity.java
similarity index 66%
copy from hostsidetests/services/activitymanager/app/src/android/server/app/SlowCreateActivity.java
copy to tests/tests/widget/src/android/widget/cts/ActionMenuViewCtsActivity.java
index 481f5be..1b58b63 100644
--- a/hostsidetests/services/activitymanager/app/src/android/server/app/SlowCreateActivity.java
+++ b/tests/tests/widget/src/android/widget/cts/ActionMenuViewCtsActivity.java
@@ -14,17 +14,23 @@
* limitations under the License.
*/
-package android.server.app;
+package android.widget.cts;
import android.app.Activity;
import android.os.Bundle;
+import android.widget.ActionMenuView;
-public class SlowCreateActivity extends Activity {
+/**
+ * A minimal application for {@link ActionMenuView} test.
+ */
+public class ActionMenuViewCtsActivity extends Activity {
+ /**
+ * Called with the activity is first created.
+ */
@Override
- protected void onCreate(Bundle savedInstanceState) {
- try {
- Thread.sleep(2000);
- } catch(InterruptedException e) {}
+ public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ setContentView(R.layout.actionmenuview_layout);
}
}
+
diff --git a/tests/tests/widget/src/android/widget/cts/ActionMenuViewTest.java b/tests/tests/widget/src/android/widget/cts/ActionMenuViewTest.java
new file mode 100644
index 0000000..0091074
--- /dev/null
+++ b/tests/tests/widget/src/android/widget/cts/ActionMenuViewTest.java
@@ -0,0 +1,174 @@
+/*
+ * 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.widget.cts;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.app.Instrumentation;
+import android.cts.util.WidgetTestUtils;
+import android.graphics.Color;
+import android.graphics.drawable.Drawable;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.view.Menu;
+import android.widget.ActionMenuView;
+import android.widget.cts.util.TestUtils;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class ActionMenuViewTest {
+ private Instrumentation mInstrumentation;
+ private ActionMenuViewCtsActivity mActivity;
+ private ActionMenuView mActionMenuView;
+
+ @Rule
+ public ActivityTestRule<ActionMenuViewCtsActivity> mActivityRule
+ = new ActivityTestRule<>(ActionMenuViewCtsActivity.class);
+
+ @Before
+ public void setup() {
+ mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ mActivity = mActivityRule.getActivity();
+ mActionMenuView = (ActionMenuView) mActivity.findViewById(R.id.action_menu_view);
+ }
+
+ @Test
+ public void testConstructor() {
+ new ActionMenuView(mActivity);
+
+ new ActionMenuView(mActivity, null);
+ }
+
+ @Test
+ public void testMenuContent() {
+ final Menu menu = mActionMenuView.getMenu();
+ assertNotNull(menu);
+
+ mInstrumentation.runOnMainSync(
+ () -> mActivity.getMenuInflater().inflate(R.menu.toolbar_menu, menu));
+
+ assertEquals(6, menu.size());
+ assertEquals(R.id.action_highlight, menu.getItem(0).getItemId());
+ assertEquals(R.id.action_edit, menu.getItem(1).getItemId());
+ assertEquals(R.id.action_delete, menu.getItem(2).getItemId());
+ assertEquals(R.id.action_ignore, menu.getItem(3).getItemId());
+ assertEquals(R.id.action_share, menu.getItem(4).getItemId());
+ assertEquals(R.id.action_print, menu.getItem(5).getItemId());
+
+ ActionMenuView.OnMenuItemClickListener menuItemClickListener =
+ mock(ActionMenuView.OnMenuItemClickListener.class);
+ mActionMenuView.setOnMenuItemClickListener(menuItemClickListener);
+
+ menu.performIdentifierAction(R.id.action_highlight, 0);
+ verify(menuItemClickListener, times(1)).onMenuItemClick(
+ menu.findItem(R.id.action_highlight));
+
+ menu.performIdentifierAction(R.id.action_share, 0);
+ verify(menuItemClickListener, times(1)).onMenuItemClick(
+ menu.findItem(R.id.action_share));
+ }
+
+ @Test
+ public void testMenuOverflowShowHide() {
+ // Inflate menu and check that we're not showing overflow menu yet
+ mInstrumentation.runOnMainSync(
+ () -> mActivity.getMenuInflater().inflate(
+ R.menu.toolbar_menu, mActionMenuView.getMenu()));
+ assertFalse(mActionMenuView.isOverflowMenuShowing());
+
+ // Ask to show overflow menu and check that it's showing
+ mInstrumentation.runOnMainSync(mActionMenuView::showOverflowMenu);
+ mInstrumentation.waitForIdleSync();
+ assertTrue(mActionMenuView.isOverflowMenuShowing());
+
+ // Ask to hide the overflow menu and check that it's not showing
+ mInstrumentation.runOnMainSync(mActionMenuView::hideOverflowMenu);
+ mInstrumentation.waitForIdleSync();
+ assertFalse(mActionMenuView.isOverflowMenuShowing());
+ }
+
+ @Test
+ public void testMenuOverflowSubmenu() {
+ // Inflate menu and check that we're not showing overflow menu yet
+ mInstrumentation.runOnMainSync(
+ () -> mActivity.getMenuInflater().inflate(
+ R.menu.toolbar_menu, mActionMenuView.getMenu()));
+ assertFalse(mActionMenuView.isOverflowMenuShowing());
+
+ // Ask to show overflow menu and check that it's showing
+ mInstrumentation.runOnMainSync(mActionMenuView::showOverflowMenu);
+ mInstrumentation.waitForIdleSync();
+ assertTrue(mActionMenuView.isOverflowMenuShowing());
+
+ // Register a mock menu item click listener on the toolbar
+ ActionMenuView.OnMenuItemClickListener menuItemClickListener =
+ mock(ActionMenuView.OnMenuItemClickListener.class);
+ mActionMenuView.setOnMenuItemClickListener(menuItemClickListener);
+
+ final Menu menu = mActionMenuView.getMenu();
+
+ // Ask to "perform" the share action and check that the menu click listener has
+ // been notified
+ mInstrumentation.runOnMainSync(() -> menu.performIdentifierAction(R.id.action_share, 0));
+ verify(menuItemClickListener, times(1)).onMenuItemClick(
+ menu.findItem(R.id.action_share));
+
+ // Ask to dismiss all the popups and check that we're not showing the overflow menu
+ mInstrumentation.runOnMainSync(mActionMenuView::dismissPopupMenus);
+ mInstrumentation.waitForIdleSync();
+ assertFalse(mActionMenuView.isOverflowMenuShowing());
+ }
+
+ @Test
+ public void testMenuOverflowIcon() {
+ // Inflate menu and check that we're not showing overflow menu yet
+ mInstrumentation.runOnMainSync(
+ () -> mActivity.getMenuInflater().inflate(
+ R.menu.toolbar_menu, mActionMenuView.getMenu()));
+
+ final Drawable overflowIcon = mActivity.getDrawable(R.drawable.icon_red);
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mActionMenuView,
+ () -> mActionMenuView.setOverflowIcon(overflowIcon));
+
+ final Drawable toolbarOverflowIcon = mActionMenuView.getOverflowIcon();
+ TestUtils.assertAllPixelsOfColor("Overflow icon is red", toolbarOverflowIcon,
+ toolbarOverflowIcon.getIntrinsicWidth(), toolbarOverflowIcon.getIntrinsicHeight(),
+ true, Color.RED, 1, false);
+ }
+
+ @Test
+ public void testPopupTheme() {
+ mInstrumentation.runOnMainSync(() -> {
+ mActivity.getMenuInflater().inflate(R.menu.toolbar_menu, mActionMenuView.getMenu());
+ mActionMenuView.setPopupTheme(R.style.ToolbarPopupTheme_Test);
+ });
+ assertEquals(R.style.ToolbarPopupTheme_Test, mActionMenuView.getPopupTheme());
+ }
+}
diff --git a/tests/tests/widget/src/android/widget/cts/AdapterViewCtsActivity.java b/tests/tests/widget/src/android/widget/cts/AdapterViewCtsActivity.java
index a85e365..6e8e7ed 100644
--- a/tests/tests/widget/src/android/widget/cts/AdapterViewCtsActivity.java
+++ b/tests/tests/widget/src/android/widget/cts/AdapterViewCtsActivity.java
@@ -16,17 +16,16 @@
package android.widget.cts;
-import java.util.ArrayList;
-import java.util.List;
-
import android.app.Activity;
import android.os.Bundle;
import android.view.ViewGroup.LayoutParams;
import android.widget.ArrayAdapter;
import android.widget.ListView;
-
import android.widget.cts.R;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* A minimal application for AdapterView test.
*/
diff --git a/tests/tests/widget/src/android/widget/cts/AdapterViewTest.java b/tests/tests/widget/src/android/widget/cts/AdapterViewTest.java
index 1b9137f..7c49f1b 100644
--- a/tests/tests/widget/src/android/widget/cts/AdapterViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/AdapterViewTest.java
@@ -16,12 +16,32 @@
package android.widget.cts;
-import org.xmlpull.v1.XmlPullParser;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.anyLong;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
-import android.app.Activity;
+import android.app.Instrumentation;
import android.content.Context;
+import android.cts.util.WidgetTestUtils;
import android.os.Parcelable;
-import android.test.ActivityInstrumentationTestCase2;
+import android.provider.Settings;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
import android.util.AttributeSet;
import android.util.SparseArray;
import android.util.Xml;
@@ -31,18 +51,20 @@
import android.view.animation.LayoutAnimationController;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
+import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.ListAdapter;
import android.widget.ListView;
-import android.widget.AdapterView.OnItemClickListener;
-import android.widget.AdapterView.OnItemLongClickListener;
-import android.widget.AdapterView.OnItemSelectedListener;
-import android.provider.Settings;
-import android.widget.cts.R;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.xmlpull.v1.XmlPullParser;
-
-public class AdapterViewTest extends ActivityInstrumentationTestCase2<AdapterViewCtsActivity> {
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class AdapterViewTest {
private final static int INVALID_ID = -1;
@@ -51,20 +73,20 @@
final String[] FRUIT = { "1", "2", "3", "4", "5", "6", "7", "8" };
- private Activity mActivity;
+ private AdapterViewCtsActivity mActivity;
private AdapterView<ListAdapter> mAdapterView;
- public AdapterViewTest() {
- super("android.widget.cts", AdapterViewCtsActivity.class);
- }
+ @Rule
+ public ActivityTestRule<AdapterViewCtsActivity> mActivityRule
+ = new ActivityTestRule<>(AdapterViewCtsActivity.class);
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mActivity = getActivity();
+ @Before
+ public void setup() {
+ mActivity = mActivityRule.getActivity();
mAdapterView = new ListView(mActivity);
}
+ @Test
public void testConstructor() {
XmlPullParser parser = mActivity.getResources().getXml(R.layout.adapterview_layout);
AttributeSet attrs = Xml.asAttributeSet(parser);
@@ -75,83 +97,60 @@
new MockAdapterView(mActivity, attrs, 0);
- try {
- new MockAdapterView(null);
- fail("Should throw NullPointerException");
- } catch (NullPointerException e) {
- //expected
- }
-
new MockAdapterView(mActivity, null, INVALID_ID);
}
- /**
- * test not supported methods, all should throw UnsupportedOperationException
- */
- public void testUnsupportedMethods() {
- ListView subView = new ListView(mActivity);
-
- try {
- mAdapterView.addView(subView);
- fail("addView(View) is not supported in AdapterView.");
- } catch (UnsupportedOperationException e) {
- //expected
- }
-
- try {
- mAdapterView.addView(subView, 0);
- fail("addView(View, int) is not supported in AdapterView.");
- } catch (UnsupportedOperationException e) {
- //expected
- }
-
- try {
- mAdapterView.addView(subView, (ViewGroup.LayoutParams) null);
- fail("addView(View, ViewGroup.LayoutParams) is not supported in AdapterView.");
- } catch (UnsupportedOperationException e) {
- //expected
- }
-
- try {
- mAdapterView.addView(subView, 0, (ViewGroup.LayoutParams) null);
- fail("addView(View, int, ViewGroup.LayoutParams) is not supported in AdapterView.");
- } catch (UnsupportedOperationException e) {
- //expected
- }
-
- try {
- mAdapterView.removeViewAt(0);
- fail("removeViewAt(int) is not supported in AdapterView");
- } catch (UnsupportedOperationException e) {
- //expected
- }
-
- try {
- mAdapterView.removeAllViews();
- fail("removeAllViews() is not supported in AdapterView");
- } catch (UnsupportedOperationException e) {
- //expected
- }
-
- try {
- mAdapterView.removeView(subView);
- fail("removeView(View) is not supported in AdapterView");
- } catch (UnsupportedOperationException e) {
- //expected
- }
-
- try {
- mAdapterView.setOnClickListener(new android.view.View.OnClickListener() {
- public void onClick(View v) {
- }
- });
- fail("function setOnClickListener(android.view.View.OnClickListener) "
- + "should throw out runtime exception");
- } catch (RuntimeException e) {
- // expected
- }
+ @Test(expected=NullPointerException.class)
+ public void testConstructorWithNullContext() {
+ new MockAdapterView(null);
}
+ @Test(expected=UnsupportedOperationException.class)
+ public void testAddView1() {
+ ListView subView = new ListView(mActivity);
+ mAdapterView.addView(subView);
+ }
+
+ @Test(expected=UnsupportedOperationException.class)
+ public void testAddView2() {
+ ListView subView = new ListView(mActivity);
+ mAdapterView.addView(subView, 0);
+ }
+
+ @Test(expected=UnsupportedOperationException.class)
+ public void testAddView3() {
+ ListView subView = new ListView(mActivity);
+ mAdapterView.addView(subView, (ViewGroup.LayoutParams) null);
+ }
+
+ @Test(expected=UnsupportedOperationException.class)
+ public void testAddView4() {
+ ListView subView = new ListView(mActivity);
+ mAdapterView.addView(subView, 0, (ViewGroup.LayoutParams) null);
+ }
+
+ @Test(expected=UnsupportedOperationException.class)
+ public void testRemoveView1() {
+ mAdapterView.removeViewAt(0);
+ }
+
+ @Test(expected=UnsupportedOperationException.class)
+ public void testRemoveView2() {
+ ListView subView = new ListView(mActivity);
+ mAdapterView.removeView(subView);
+ }
+
+ @Test(expected=UnsupportedOperationException.class)
+ public void testRemoveAllViews() {
+ mAdapterView.removeAllViews();
+ }
+
+ @Test(expected=RuntimeException.class)
+ public void testSetOnClickListener() {
+ mAdapterView.setOnClickListener((View v) -> {});
+ }
+
+ @Test
public void testGetCount() {
// Before setAdapter, the count should be zero.
assertEquals(0, mAdapterView.getCount());
@@ -162,6 +161,7 @@
assertEquals(FRUIT.length, mAdapterView.getCount());
}
+ @Test
public void testAccessEmptyView() {
ImageView emptyView = new ImageView(mActivity);
@@ -203,8 +203,8 @@
assertEquals(View.VISIBLE, emptyView.getVisibility());
}
+ @Test
public void testAccessVisiblePosition() {
-
assertEquals(0, mAdapterView.getFirstVisiblePosition());
// If no adapter has been set, the value should be -1;
assertEquals(-1, mAdapterView.getLastVisiblePosition());
@@ -212,7 +212,8 @@
setArrayAdapter(mAdapterView);
// LastVisiblePosition should be adapter's getCount - 1,by mocking method
- float fontScale = Settings.System.getFloat(mActivity.getContentResolver(), Settings.System.FONT_SCALE, 1);
+ float fontScale = Settings.System.getFloat(
+ mActivity.getContentResolver(), Settings.System.FONT_SCALE, 1);
if (fontScale < 1) {
fontScale = 1;
}
@@ -222,6 +223,7 @@
assertEquals(FRUIT.length - 1, mAdapterView.getLastVisiblePosition());
}
+ @Test
public void testItemOrItemIdAtPosition() {
// no adapter set
assertNull(mAdapterView.getItemAtPosition(0));
@@ -235,12 +237,6 @@
assertEquals(FRUIT[i], mAdapterView.getItemAtPosition(i));
}
assertNull(mAdapterView.getItemAtPosition(-1));
- try {
- mAdapterView.getItemAtPosition(FRUIT.length);
- fail("should throw IndexOutOfBoundsException");
- } catch (IndexOutOfBoundsException e) {
- //expected
- }
for (int i = 0; i < count; i++) {
assertEquals(i, mAdapterView.getItemIdAtPosition(i));
@@ -249,48 +245,76 @@
assertEquals(FRUIT.length, mAdapterView.getItemIdAtPosition(FRUIT.length));
}
+ @Test(expected=IndexOutOfBoundsException.class)
+ public void testItemAtPositionInvalidIndex() {
+ setArrayAdapter(mAdapterView);
+ mAdapterView.getItemAtPosition(FRUIT.length);
+ }
+
+ @Test
public void testAccessOnItemClickAndLongClickListener() {
- MockOnItemClickListener clickListener = new MockOnItemClickListener();
- MockOnItemLongClickListener longClickListener = new MockOnItemLongClickListener();
+ AdapterView.OnItemClickListener mockClickListener =
+ mock(AdapterView.OnItemClickListener.class);
+ AdapterView.OnItemLongClickListener mockLongClickListener =
+ mock(AdapterView.OnItemLongClickListener.class);
+ when(mockLongClickListener.onItemLongClick(
+ any(AdapterView.class), any(View.class), anyInt(), anyLong())).thenReturn(true);
+
+ assertNull(mAdapterView.getOnItemLongClickListener());
+ assertNull(mAdapterView.getOnItemClickListener());
assertFalse(mAdapterView.performItemClick(null, 0, 0));
- mAdapterView.setOnItemClickListener(clickListener);
- mAdapterView.setOnItemLongClickListener(longClickListener);
+ mAdapterView.setOnItemClickListener(mockClickListener);
+ mAdapterView.setOnItemLongClickListener(mockLongClickListener);
+ assertEquals(mockLongClickListener, mAdapterView.getOnItemLongClickListener());
- assertFalse(clickListener.isClicked());
+ verifyZeroInteractions(mockClickListener);
assertTrue(mAdapterView.performItemClick(null, 0, 0));
- assertTrue(clickListener.isClicked());
+ verify(mockClickListener, times(1)).onItemClick(eq(mAdapterView), any(View.class),
+ eq(0), eq(0L));
setArrayAdapter(mAdapterView);
- assertFalse(longClickListener.isClicked());
+ verifyZeroInteractions(mockLongClickListener);
mAdapterView.layout(0, 0, LAYOUT_WIDTH, LAYOUT_HEIGHT);
assertTrue(mAdapterView.showContextMenuForChild(mAdapterView.getChildAt(0)));
- assertTrue(longClickListener.isClicked());
+ verify(mockLongClickListener, times(1)).onItemLongClick(eq(mAdapterView), any(View.class),
+ eq(0), eq(0L));
}
+ @Test
public void testAccessOnItemSelectedListener() {
- // FIXME: we can not select the item in touch mode, how can we change the mode to test
- setArrayAdapter(mAdapterView);
- MockOnItemSelectedListener selectedListener = new MockOnItemSelectedListener();
- mAdapterView.setOnItemSelectedListener(selectedListener);
+ mAdapterView = mActivity.getListView();
+ final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+ WidgetTestUtils.runOnMainAndDrawSync(instrumentation, mAdapterView,
+ () -> mAdapterView.setLayoutParams(new FrameLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT)));
-// mAdapterView.layout(0, 0, LAYOUT_WIDTH, LAYOUT_HEIGHT);
-//
-// assertFalse(selectedListener.isItemSelected());
-// assertFalse(selectedListener.isNothingSelected());
-//
-// mAdapterView.setSelection(1);
-// assertTrue(selectedListener.isItemSelected());
-// assertFalse(selectedListener.isNothingSelected());
-//
-// mAdapterView.setSelection(-1);
-// assertTrue(selectedListener.isItemSelected());
-// assertTrue(selectedListener.isNothingSelected());
-//
-// mAdapterView.setSelection(FRUIT.length);
-// assertTrue(selectedListener.isItemSelected());
-// assertTrue(selectedListener.isNothingSelected());
+ instrumentation.runOnMainSync(() -> setArrayAdapter(mAdapterView));
+
+ AdapterView.OnItemSelectedListener mockSelectedListener =
+ mock(AdapterView.OnItemSelectedListener.class);
+ mAdapterView.setOnItemSelectedListener(mockSelectedListener);
+ assertEquals(mockSelectedListener, mAdapterView.getOnItemSelectedListener());
+
+ verifyZeroInteractions(mockSelectedListener);
+
+ // Select item #1 and verify that the listener has been notified
+ WidgetTestUtils.runOnMainAndDrawSync(instrumentation, mAdapterView,
+ () -> mAdapterView.setSelection(1));
+ verify(mockSelectedListener, times(1)).onItemSelected(eq(mAdapterView), any(View.class),
+ eq(1), eq(1L));
+ verifyNoMoreInteractions(mockSelectedListener);
+
+ // Select last item and verify that the listener has been notified
+ reset(mockSelectedListener);
+ WidgetTestUtils.runOnMainAndDrawSync(instrumentation, mAdapterView,
+ () -> mAdapterView.setSelection(FRUIT.length - 1));
+ verify(mockSelectedListener, times(1)).onItemSelected(
+ eq(mAdapterView), any(View.class), eq(FRUIT.length - 1),
+ eq((long) FRUIT.length - 1));
+ verifyNoMoreInteractions(mockSelectedListener);
}
/*
@@ -301,6 +325,7 @@
* it's hard to scroll the list in unit test, so we just test without scrolling
* this means the position of item is same as position of the children in parent layout
*/
+ @Test
public void testGetPositionForView() {
setArrayAdapter(mAdapterView);
mAdapterView.layout(0, 0, LAYOUT_WIDTH, LAYOUT_HEIGHT);
@@ -310,17 +335,18 @@
assertEquals(i, mAdapterView.getPositionForView(mAdapterView.getChildAt(i)));
}
- try {
- assertEquals(AdapterView.INVALID_POSITION, mAdapterView.getPositionForView(null));
- fail("Should throw NullPointerException");
- } catch (NullPointerException e) {
- //expected
- }
-
assertEquals(AdapterView.INVALID_POSITION,
mAdapterView.getPositionForView(new ImageView(mActivity)));
}
+ @Test(expected=NullPointerException.class)
+ public void testGetPositionForNull() {
+ setArrayAdapter(mAdapterView);
+ mAdapterView.layout(0, 0, LAYOUT_WIDTH, LAYOUT_HEIGHT);
+ mAdapterView.getPositionForView(null);
+ }
+
+ @Test
public void testChangeFocusable() {
assertFalse(mAdapterView.isFocusable());
assertFalse(mAdapterView.isFocusableInTouchMode());
@@ -344,16 +370,10 @@
}
/*
- * skip this test, no need to test
- */
- public void testOnLayout() {
- // onLayout() is implementation details, do NOT test
- }
-
- /*
* set and get the selected id, position and item.
* values will not change if invalid id given.
*/
+ @Test
public void testGetSelected() {
assertEquals(AdapterView.INVALID_ROW_ID, mAdapterView.getSelectedItemId());
assertEquals(AdapterView.INVALID_POSITION, mAdapterView.getSelectedItemPosition());
@@ -388,6 +408,7 @@
/*
* not update this test until the ViewGroup's test finish.
*/
+ @Test
public void testDispatchSaveInstanceState() {
MockAdapterView adapterView = new MockAdapterView(mActivity);
adapterView.setSaveEnabled(true);
@@ -400,6 +421,7 @@
/*
* not update this test until the ViewGroup's test finish.
*/
+ @Test
public void testDispatchRestoreInstanceState() {
MockAdapterView adapterView = new MockAdapterView(mActivity);
adapterView.setSaveEnabled(true);
@@ -413,11 +435,12 @@
* if no child added, it always return false
* this method is protected, so we involve the mock
*/
+ @Test
public void testCanAnimate() {
MockAdapterView adapterView = new MockAdapterView(mActivity);
LayoutAnimationController lAC = new LayoutAnimationController(new AnimationSet(true));
- // no child added, always false
+ // no child added, always false
assertNull(adapterView.getAdapter());
adapterView.setLayoutAnimation(lAC);
assertFalse(adapterView.canAnimate());
@@ -459,62 +482,7 @@
}
private void setArrayAdapter(AdapterView<ListAdapter> adapterView) {
- ((ListView) adapterView).setAdapter(new ArrayAdapter<String>(
+ adapterView.setAdapter(new ArrayAdapter<>(
mActivity, R.layout.adapterview_layout, FRUIT));
}
-
- /**
- * this is a mock item click listener for check out call back
- */
- private class MockOnItemClickListener implements OnItemClickListener {
- private boolean mClicked;
-
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- mClicked = true;
- }
-
- protected boolean isClicked() {
- return mClicked;
- }
- }
-
- /**
- * this is a mock long item click listener for check out call back
- */
- private class MockOnItemLongClickListener implements OnItemLongClickListener {
- private boolean mClicked;
-
- public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
- mClicked = true;
- return true;
- }
-
- protected boolean isClicked() {
- return mClicked;
- }
- }
-
- /**
- * this is a mock item selected listener for check out call lback
- */
- private class MockOnItemSelectedListener implements OnItemSelectedListener {
- private boolean mIsItemSelected;
- private boolean mIsNothingSelected;
-
- public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
- mIsItemSelected = true;
- }
-
- public void onNothingSelected(AdapterView<?> parent) {
- mIsNothingSelected = true;
- }
-
- protected boolean isItemSelected() {
- return mIsItemSelected;
- }
-
- protected boolean isNothingSelected() {
- return mIsNothingSelected;
- }
- }
}
diff --git a/tests/tests/widget/src/android/widget/cts/AdapterView_AdapterContextMenuInfoTest.java b/tests/tests/widget/src/android/widget/cts/AdapterView_AdapterContextMenuInfoTest.java
index 17c7a1e..0d6f6a4 100644
--- a/tests/tests/widget/src/android/widget/cts/AdapterView_AdapterContextMenuInfoTest.java
+++ b/tests/tests/widget/src/android/widget/cts/AdapterView_AdapterContextMenuInfoTest.java
@@ -19,7 +19,6 @@
import android.test.AndroidTestCase;
import android.view.View;
import android.widget.AdapterView;
-import android.widget.AdapterView.AdapterContextMenuInfo;
public class AdapterView_AdapterContextMenuInfoTest extends AndroidTestCase {
public void testConstructor() {
diff --git a/tests/tests/widget/src/android/widget/cts/AlphabetIndexerTest.java b/tests/tests/widget/src/android/widget/cts/AlphabetIndexerTest.java
index dd9393c..f217931 100644
--- a/tests/tests/widget/src/android/widget/cts/AlphabetIndexerTest.java
+++ b/tests/tests/widget/src/android/widget/cts/AlphabetIndexerTest.java
@@ -16,13 +16,21 @@
package android.widget.cts;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
import android.database.Cursor;
import android.database.MatrixCursor;
-import android.test.AndroidTestCase;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
import android.widget.AlphabetIndexer;
-public class AlphabetIndexerTest extends AndroidTestCase {
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class AlphabetIndexerTest {
private static final String[] COUNTRIES_LIST = new String[]
{"Argentina", "Australia", "China", "France", "Germany", "Italy", "Japan", "United States"};
private static final String[] NAMES_LIST = new String[]
@@ -38,6 +46,7 @@
private static final int INDEX_OF_MESSI = 5;
private static final int INDEX_OF_STEVEN = 7;
+ @Test
public void testAlphabetIndexer() {
Cursor c1 = createCursor("Country", COUNTRIES_LIST);
@@ -100,6 +109,7 @@
assertEquals(NAMES_LIST.length, indexer.getPositionForSection(index));
}
+ @Test
public void testCompare() {
Cursor cursor = createCursor("Country", COUNTRIES_LIST);
diff --git a/tests/tests/widget/src/android/widget/cts/AnalogClockTest.java b/tests/tests/widget/src/android/widget/cts/AnalogClockTest.java
index fae6516..a63d4b9b 100644
--- a/tests/tests/widget/src/android/widget/cts/AnalogClockTest.java
+++ b/tests/tests/widget/src/android/widget/cts/AnalogClockTest.java
@@ -16,80 +16,56 @@
package android.widget.cts;
-import android.widget.cts.R;
-
-
-import org.xmlpull.v1.XmlPullParser;
-
import android.app.Activity;
-import android.test.ActivityInstrumentationTestCase2;
-import android.test.UiThreadTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
import android.util.AttributeSet;
import android.util.Xml;
import android.widget.AnalogClock;
-public class AnalogClockTest extends ActivityInstrumentationTestCase2<FrameLayoutCtsActivity> {
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.xmlpull.v1.XmlPullParser;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class AnalogClockTest {
private AttributeSet mAttrSet;
private Activity mActivity;
- public AnalogClockTest() {
- super("android.widget.cts", FrameLayoutCtsActivity.class);
- }
+ @Rule
+ public ActivityTestRule<FrameLayoutCtsActivity> mActivityRule
+ = new ActivityTestRule<>(FrameLayoutCtsActivity.class);
- @Override
- protected void setUp() throws Exception {
- super.setUp();
-
- XmlPullParser parser = getActivity().getResources().getXml(R.layout.analogclock);
+ @Before
+ public void setup() throws Exception {
+ mActivity = mActivityRule.getActivity();
+ XmlPullParser parser = mActivity.getResources().getXml(R.layout.analogclock);
mAttrSet = Xml.asAttributeSet(parser);
- mActivity = getActivity();
}
- @UiThreadTest
+ @Test
public void testConstructor() {
new AnalogClock(mActivity);
new AnalogClock(mActivity, mAttrSet);
new AnalogClock(mActivity, mAttrSet, 0);
-
- try {
- new AnalogClock(null);
- fail("There should be a NullPointerException thrown out.");
- } catch (NullPointerException e) {
- // expected, test success.
- }
-
- try {
- new AnalogClock(null, null);
- fail("There should be a NullPointerException thrown out.");
- } catch (NullPointerException e) {
- // expected, test success.
- }
-
- try {
- new AnalogClock(null, null, -1);
- fail("There should be a NullPointerException thrown out.");
- } catch (NullPointerException e) {
- // expected, test success.
- }
}
- public void testOnMeasure() {
- // onMeasure() is implementation details, do NOT test
+ @Test(expected=NullPointerException.class)
+ public void testConstructorWithNullContext1() {
+ new AnalogClock(null);
}
- public void testOnSizeChanged() {
- // Do not test onSizeChanged(), implementation details
+ @Test(expected=NullPointerException.class)
+ public void testConstructorWithNullContext2() {
+ new AnalogClock(null, null);
}
- public void testOnDraw() {
- // Do not test, it's controlled by View. Implementation details
- }
-
- public void testOnDetachedFromWindow() {
- // Do not test
- }
-
- public void testOnAttachedToWindow() {
- // Do not test
+ @Test(expected=NullPointerException.class)
+ public void testConstructorWithNullContext3() {
+ new AnalogClock(null, null, -1);
}
}
diff --git a/tests/tests/widget/src/android/widget/cts/ArrayAdapterTest.java b/tests/tests/widget/src/android/widget/cts/ArrayAdapterTest.java
index 03677f2..f09c371 100644
--- a/tests/tests/widget/src/android/widget/cts/ArrayAdapterTest.java
+++ b/tests/tests/widget/src/android/widget/cts/ArrayAdapterTest.java
@@ -16,25 +16,38 @@
package android.widget.cts;
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.List;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertSame;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import android.app.Instrumentation;
import android.content.Context;
import android.content.res.Resources.Theme;
import android.database.DataSetObserver;
-import android.test.InstrumentationTestCase;
-import android.test.UiThreadTest;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
import android.widget.ArrayAdapter;
import android.widget.Filter;
import android.widget.TextView;
-import android.widget.cts.R;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import java.util.ArrayList;
+import java.util.List;
-public class ArrayAdapterTest extends InstrumentationTestCase {
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ArrayAdapterTest {
- private static final int INVALD_ID = -1;
+ private static final int INVALID_ID = -1;
private static final String STR1 = "string1";
private static final String STR2 = "string2";
private static final String STR3 = "string3";
@@ -42,87 +55,85 @@
private ArrayAdapter<String> mArrayAdapter;
private Context mContext;
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mContext = getInstrumentation().getTargetContext();
- mArrayAdapter = new ArrayAdapter<String>(mContext, R.layout.simple_dropdown_item_1line);
+ @Before
+ public void setup() {
+ mContext = InstrumentationRegistry.getTargetContext();
+ mArrayAdapter = new ArrayAdapter<>(mContext, R.layout.simple_dropdown_item_1line);
}
+ @Test
public void testConstructor() {
-
new ArrayAdapter<String>(mContext, R.layout.simple_dropdown_item_1line);
- new ArrayAdapter<String>(mContext, INVALD_ID);// invalid resource id
+ new ArrayAdapter<String>(mContext, INVALID_ID); // invalid resource id
new ArrayAdapter<String>(mContext, R.layout.simple_dropdown_item_1line, R.id.text1);
- new ArrayAdapter<String>(mContext, R.layout.simple_dropdown_item_1line, INVALD_ID);
+ new ArrayAdapter<String>(mContext, R.layout.simple_dropdown_item_1line, INVALID_ID);
- new ArrayAdapter<String>(mContext, R.layout.simple_dropdown_item_1line,
+ new ArrayAdapter<>(mContext, R.layout.simple_dropdown_item_1line,
new String[] {"str1", "str2"});
- new ArrayAdapter<String>(mContext, R.layout.simple_dropdown_item_1line, R.id.text1,
+ new ArrayAdapter<>(mContext, R.layout.simple_dropdown_item_1line, R.id.text1,
new String[] {"str1", "str2"});
- List<String> list = new ArrayList<String>();
+ List<String> list = new ArrayList<>();
list.add(STR1);
list.add(STR2);
- new ArrayAdapter<String>(mContext, R.layout.simple_dropdown_item_1line, list);
+ new ArrayAdapter<>(mContext, R.layout.simple_dropdown_item_1line, list);
- new ArrayAdapter<String>(mContext, R.layout.simple_dropdown_item_1line, R.id.text1, list);
-
- // invalid input
- try {
- new ArrayAdapter<String>(null, R.layout.simple_dropdown_item_1line);
- fail("should throw NullPointerException");
- } catch (NullPointerException e) {
- // expected exception
- }
+ new ArrayAdapter<>(mContext, R.layout.simple_dropdown_item_1line, R.id.text1, list);
}
+ @Test(expected=NullPointerException.class)
+ public void testConstructorWithNullContext() {
+ new ArrayAdapter<String>(null, R.layout.simple_dropdown_item_1line);
+ }
+
+ @Test
public void testDataChangeEvent() {
- final MockDataSetObserver mockDataSetObserver = new MockDataSetObserver();
+ final DataSetObserver mockDataSetObserver = mock(DataSetObserver.class);
mArrayAdapter.registerDataSetObserver(mockDataSetObserver);
// enable automatically notifying.
mArrayAdapter.setNotifyOnChange(true);
- assertEquals(0, mockDataSetObserver.getCalledOnChangedCount());
+ verifyZeroInteractions(mockDataSetObserver);
mArrayAdapter.add(STR1);
assertEquals(1, mArrayAdapter.getCount());
- assertEquals(1, mockDataSetObserver.getCalledOnChangedCount());
+ verify(mockDataSetObserver, times(1)).onChanged();
mArrayAdapter.add(STR2);
assertEquals(2, mArrayAdapter.getCount());
- assertEquals(2, mockDataSetObserver.getCalledOnChangedCount());
+ verify(mockDataSetObserver, times(2)).onChanged();
// reset data
mArrayAdapter.clear();
// clear notify changed
- assertEquals(3, mockDataSetObserver.getCalledOnChangedCount());
+ verify(mockDataSetObserver, times(3)).onChanged();
assertEquals(0, mArrayAdapter.getCount());
// if empty before, clear also notify changed
mArrayAdapter.clear();
- assertEquals(4, mockDataSetObserver.getCalledOnChangedCount());
- mockDataSetObserver.clearCount();
- assertEquals(0, mockDataSetObserver.getCalledOnChangedCount());
+ verify(mockDataSetObserver, times(4)).onChanged();
+
+ reset(mockDataSetObserver);
// disable auto notify
mArrayAdapter.setNotifyOnChange(false);
mArrayAdapter.add(STR3);
assertEquals(1, mArrayAdapter.getCount());
- assertEquals(0, mockDataSetObserver.getCalledOnChangedCount());
+ verifyZeroInteractions(mockDataSetObserver);
// manually notify
mArrayAdapter.notifyDataSetChanged();
- assertEquals(1, mockDataSetObserver.getCalledOnChangedCount());
+ verify(mockDataSetObserver, times(1)).onChanged();
// no data changed, but force notify
mArrayAdapter.notifyDataSetChanged();
- assertEquals(2, mockDataSetObserver.getCalledOnChangedCount());
+ verify(mockDataSetObserver, times(2)).onChanged();
// once called notify, auto notify enabled
mArrayAdapter.add(STR3);
- assertEquals(3, mockDataSetObserver.getCalledOnChangedCount());
+ verify(mockDataSetObserver, times(3)).onChanged();
}
+ @Test
public void testAccessView() {
final TextView textView = new TextView(mContext);
textView.setText(STR3);
@@ -145,40 +156,41 @@
assertSame(textView, mArrayAdapter.getView(0, textView, null));
assertSame(textView, mArrayAdapter.getDropDownView(0, textView, null));
assertEquals(STR1, textView.getText());
-
- try {
- assertEquals(textView, mArrayAdapter.getView(-1, textView, null));
- fail("should throw IndexOutOfBoundsException");
- } catch (IndexOutOfBoundsException e) {
- }
-
- try {
- assertEquals(textView, mArrayAdapter.getDropDownView(-1, textView, null));
- fail("should throw IndexOutOfBoundsException");
- } catch (IndexOutOfBoundsException e) {
- }
-
- try {
- assertEquals(textView,
- mArrayAdapter.getView(mArrayAdapter.getCount(), textView, null));
- fail("should throw IndexOutOfBoundsException");
- } catch (IndexOutOfBoundsException e) {
- }
-
- try {
- assertEquals(textView,
- mArrayAdapter.getDropDownView(mArrayAdapter.getCount(), textView, null));
- fail("should throw IndexOutOfBoundsException");
- } catch (IndexOutOfBoundsException e) {
- }
}
- @UiThreadTest
- public void testGetFilter() {
- Filter filter = mArrayAdapter.getFilter();
+ @Test(expected=IndexOutOfBoundsException.class)
+ public void testGetViewOutOfBoundsLow() {
+ final TextView textView = new TextView(mContext);
+ mArrayAdapter.getView(-1, textView, null);
+ }
- assertNotNull(mArrayAdapter.getFilter());
- assertSame(filter, mArrayAdapter.getFilter());
+ @Test(expected=IndexOutOfBoundsException.class)
+ public void testDropDownGetViewOutOfBoundsLow() {
+ final TextView textView = new TextView(mContext);
+ mArrayAdapter.getDropDownView(-1, textView, null);
+ }
+
+ @Test(expected=IndexOutOfBoundsException.class)
+ public void testGetViewOutOfBoundsHigh() {
+ final TextView textView = new TextView(mContext);
+ mArrayAdapter.getView(mArrayAdapter.getCount(), textView, null);
+ }
+
+ @Test(expected=IndexOutOfBoundsException.class)
+ public void testDropDownGetViewOutOfBoundsHigh() {
+ final TextView textView = new TextView(mContext);
+ mArrayAdapter.getDropDownView(mArrayAdapter.getCount(), textView, null);
+ }
+
+ @Test
+ public void testGetFilter() {
+ Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+ instrumentation.runOnMainSync(() -> {
+ Filter filter = mArrayAdapter.getFilter();
+
+ assertNotNull(mArrayAdapter.getFilter());
+ assertSame(filter, mArrayAdapter.getFilter());
+ });
}
/**
@@ -186,7 +198,8 @@
* we set a xml that not contain a textview, so exception should throw to lete us know
* sucessfully change the dropdown xml, but should not affect the normal view by getview
*/
- public void testSetDropDownViewResouce() {
+ @Test
+ public void testSetDropDownViewResource() {
mArrayAdapter.add(STR1);
mArrayAdapter.getDropDownView(0, null, null);
@@ -194,17 +207,18 @@
mArrayAdapter.setDropDownViewResource(R.layout.tabhost_layout);
// getview is ok
mArrayAdapter.getView(0, null, null);
- // getDropDownView error for it changed
- try {
- mArrayAdapter.getDropDownView(0, null, null);
- fail("should throw IllegalStateException");
- } catch (IllegalStateException e) {
- // expected exception
- }
- mArrayAdapter.setDropDownViewResource(INVALD_ID);
+ mArrayAdapter.setDropDownViewResource(INVALID_ID);
}
+ @Test(expected=IllegalStateException.class)
+ public void testSetDropDownViewResourceIllegal() {
+ mArrayAdapter.add(STR1);
+ mArrayAdapter.setDropDownViewResource(R.layout.tabhost_layout);
+ mArrayAdapter.getDropDownView(0, null, null);
+ }
+
+ @Test
public void testAccessDropDownViewTheme() {
Theme theme = mContext.getResources().newTheme();
mArrayAdapter.setDropDownViewTheme(theme);
@@ -215,15 +229,16 @@
* insert the item to the specific position, notify data changed
* check -1, normal, > count
*/
+ @Test
public void testInsert() {
mArrayAdapter.setNotifyOnChange(true);
- final MockDataSetObserver mockDataSetObserver = new MockDataSetObserver();
+ final DataSetObserver mockDataSetObserver = mock(DataSetObserver.class);
mArrayAdapter.registerDataSetObserver(mockDataSetObserver);
mArrayAdapter.insert(STR1, 0);
assertEquals(1, mArrayAdapter.getCount());
assertEquals(0, mArrayAdapter.getPosition(STR1));
- assertEquals(1, mockDataSetObserver.getCalledOnChangedCount());
+ verify(mockDataSetObserver, times(1)).onChanged();
mArrayAdapter.insert(STR2, 0);
assertEquals(2, mArrayAdapter.getCount());
@@ -235,26 +250,31 @@
mArrayAdapter.insert(null, 0);
assertEquals(0, mArrayAdapter.getPosition(null));
+ }
- try {
- mArrayAdapter.insert(STR1, -1);
- fail("should throw IndexOutOfBoundsException");
- } catch (IndexOutOfBoundsException e) {
- // expected exception
- }
+ @Test(expected=IndexOutOfBoundsException.class)
+ public void testInsertOutOfBoundsLow() {
+ mArrayAdapter.insert(STR1, 0);
+ mArrayAdapter.insert(STR2, 0);
+ mArrayAdapter.insert(null, 0);
- try {
- mArrayAdapter.insert(STR1, mArrayAdapter.getCount() + 1);
- fail("should throw IndexOutOfBoundsException");
- } catch (IndexOutOfBoundsException e) {
- // expected exception
- }
+ mArrayAdapter.insert(STR1, -1);
+ }
+
+ @Test(expected=IndexOutOfBoundsException.class)
+ public void testInsertOutOfBoundsHigh() {
+ mArrayAdapter.insert(STR1, 0);
+ mArrayAdapter.insert(STR2, 0);
+ mArrayAdapter.insert(null, 0);
+
+ mArrayAdapter.insert(STR1, mArrayAdapter.getCount() + 1);
}
/**
* return the given position obj
* test range: -1, normal, > count
*/
+ @Test
public void testGetItem() {
mArrayAdapter.add(STR1);
mArrayAdapter.add(STR2);
@@ -263,26 +283,30 @@
assertSame(STR1, mArrayAdapter.getItem(0));
assertSame(STR2, mArrayAdapter.getItem(1));
assertSame(STR3, mArrayAdapter.getItem(2));
+ }
- // test invalid input
- try {
- mArrayAdapter.getItem(-1);
- fail("should throw IndexOutOfBoundsException");
- } catch (IndexOutOfBoundsException e) {
- // expected exception
- }
+ @Test(expected=IndexOutOfBoundsException.class)
+ public void testGetItemOutOfBoundsLow() {
+ mArrayAdapter.add(STR1);
+ mArrayAdapter.add(STR2);
+ mArrayAdapter.add(STR3);
- try {
- mArrayAdapter.getItem(mArrayAdapter.getCount());
- fail("should throw IndexOutOfBoundsException");
- } catch (IndexOutOfBoundsException e) {
- // expected exception
- }
+ mArrayAdapter.getItem(-1);
+ }
+
+ @Test(expected=IndexOutOfBoundsException.class)
+ public void testGetItemOutOfBoundsHigh() {
+ mArrayAdapter.add(STR1);
+ mArrayAdapter.add(STR2);
+ mArrayAdapter.add(STR3);
+
+ mArrayAdapter.getItem(mArrayAdapter.getCount());
}
/**
* just return the given position
*/
+ @Test
public void testGetItemId() {
mArrayAdapter.add(STR1);
mArrayAdapter.add(STR2);
@@ -294,13 +318,13 @@
// test invalid input
assertEquals(-1, mArrayAdapter.getItemId(-1));
- assertEquals(mArrayAdapter.getCount(),
- mArrayAdapter.getItemId(mArrayAdapter.getCount()));
+ assertEquals(mArrayAdapter.getCount(), mArrayAdapter.getItemId(mArrayAdapter.getCount()));
}
/*
* return the obj position that in the array, if there are same objs, return the first one
*/
+ @Test
public void testGetPosition() {
mArrayAdapter.add(STR1);
mArrayAdapter.add(STR2);
@@ -321,24 +345,24 @@
* Removes the specified object from the array. notify data changed
* remove first one if duplicated string in the array
*/
+ @Test
public void testRemove() {
- final MockDataSetObserver mockDataSetObserver = new MockDataSetObserver();
+ final DataSetObserver mockDataSetObserver = mock(DataSetObserver.class);
mArrayAdapter.registerDataSetObserver(mockDataSetObserver);
mArrayAdapter.setNotifyOnChange(true);
// remove the not exist one
assertEquals(0, mArrayAdapter.getCount());
- assertEquals(0, mockDataSetObserver.getCalledOnChangedCount());
+ verifyZeroInteractions(mockDataSetObserver);
// remove the item not exist also notify change
mArrayAdapter.remove(STR1);
- assertEquals(1, mockDataSetObserver.getCalledOnChangedCount());
+ verify(mockDataSetObserver, times(1)).onChanged();
mArrayAdapter.add(STR1);
mArrayAdapter.add(STR2);
mArrayAdapter.add(STR3);
mArrayAdapter.add(STR2);
- mockDataSetObserver.clearCount();
- assertEquals(0, mockDataSetObserver.getCalledOnChangedCount());
+ reset(mockDataSetObserver);
assertEquals(4, mArrayAdapter.getCount());
mArrayAdapter.remove(STR1);
@@ -346,11 +370,11 @@
assertEquals(-1, mArrayAdapter.getPosition(STR1));
assertEquals(0, mArrayAdapter.getPosition(STR2));
assertEquals(1, mArrayAdapter.getPosition(STR3));
- assertEquals(1, mockDataSetObserver.getCalledOnChangedCount());
+ verify(mockDataSetObserver, times(1)).onChanged();
mArrayAdapter.remove(STR2);
assertEquals(2, mArrayAdapter.getCount());
- // remove the first one if dumplicated
+ // remove the first one if duplicated
assertEquals(1, mArrayAdapter.getPosition(STR2));
assertEquals(0, mArrayAdapter.getPosition(STR3));
@@ -364,51 +388,45 @@
* Creates a new ArrayAdapter from external resources. The content of the array is
* obtained through {@link android.content.res.Resources#getTextArray(int)}.
*/
+ @Test
public void testCreateFromResource() {
ArrayAdapter.createFromResource(mContext, R.array.string, R.layout.simple_spinner_item);
- // invalid input
- try {
- ArrayAdapter.createFromResource(null, R.array.string, R.layout.simple_spinner_item);
- fail("should throw NullPointerException");
- } catch (NullPointerException e) {
- // expected exception
- }
-
- try {
- ArrayAdapter.createFromResource(mContext, INVALD_ID, R.layout.simple_spinner_item);
- fail("should throw NullPointerException");
- } catch (NullPointerException e) {
- // expected exception
- }
-
- ArrayAdapter.createFromResource(mContext, R.array.string, INVALD_ID);
+ ArrayAdapter.createFromResource(mContext, R.array.string, INVALID_ID);
}
+ @Test(expected=NullPointerException.class)
+ public void testCreateFromResourceWithNullContext() {
+ ArrayAdapter.createFromResource(null, R.array.string, R.layout.simple_spinner_item);
+ }
+
+ @Test(expected=NullPointerException.class)
+ public void testCreateFromResourceWithInvalidId() {
+ ArrayAdapter.createFromResource(mContext, INVALID_ID, R.layout.simple_spinner_item);
+ }
+
+ @Test
public void testSort() {
- final MockDataSetObserver mockDataSetObserver = new MockDataSetObserver();
+ final DataSetObserver mockDataSetObserver = mock(DataSetObserver.class);
mArrayAdapter.registerDataSetObserver(mockDataSetObserver);
mArrayAdapter.setNotifyOnChange(true);
- assertEquals(0, mockDataSetObserver.getCalledOnChangedCount());
+ verifyZeroInteractions(mockDataSetObserver);
- mArrayAdapter.sort( new Comparator<String>() {
- public int compare(String o1, String o2) {
- return 0;
- }
- });
- assertEquals(1, mockDataSetObserver.getCalledOnChangedCount());
+ mArrayAdapter.sort((String o1, String o2) -> 0);
+ verify(mockDataSetObserver, times(1)).onChanged();
mArrayAdapter.sort(null);
- assertEquals(2, mockDataSetObserver.getCalledOnChangedCount());
+ verify(mockDataSetObserver, times(2)).onChanged();
}
/**
* insert multiple items via add, notify data changed
* check count and content
*/
+ @Test
public void testAdd() {
mArrayAdapter.setNotifyOnChange(true);
- final MockDataSetObserver mockDataSetObserver = new MockDataSetObserver();
+ final DataSetObserver mockDataSetObserver = mock(DataSetObserver.class);
mArrayAdapter.registerDataSetObserver(mockDataSetObserver);
mArrayAdapter.clear();
@@ -425,12 +443,13 @@
* insert multiple items via addAll, notify data changed
* check count and content
*/
+ @Test
public void testAddAllCollection() {
mArrayAdapter.setNotifyOnChange(true);
- final MockDataSetObserver mockDataSetObserver = new MockDataSetObserver();
+ final DataSetObserver mockDataSetObserver = mock(DataSetObserver.class);
mArrayAdapter.registerDataSetObserver(mockDataSetObserver);
- List<String> list = new ArrayList<String>();
+ List<String> list = new ArrayList<>();
list.add("");
list.add("hello");
list.add("android");
@@ -452,9 +471,10 @@
* insert multiple items via addAll, notify data changed
* check count and content
*/
+ @Test
public void testAddAllParams() {
mArrayAdapter.setNotifyOnChange(true);
- final MockDataSetObserver mockDataSetObserver = new MockDataSetObserver();
+ final DataSetObserver mockDataSetObserver = mock(DataSetObserver.class);
mArrayAdapter.registerDataSetObserver(mockDataSetObserver);
mArrayAdapter.clear();
@@ -468,35 +488,4 @@
assertEquals(mArrayAdapter.getItem(3), "unit");
assertEquals(mArrayAdapter.getItem(4), "test");
}
-
- private static class MockDataSetObserver extends DataSetObserver {
-
- private int mCalledOnChangedCount;
- private int mOnCalledInvalidatedCount;
-
- public MockDataSetObserver() {
- clearCount();
- }
-
- public int getCalledOnChangedCount() {
- return mCalledOnChangedCount;
- }
-
- public int getCalledOnInvalidatedCount() {
- return mOnCalledInvalidatedCount;
- }
-
- public void clearCount() {
- mCalledOnChangedCount = 0;
- mOnCalledInvalidatedCount = 0;
- }
-
- public void onChanged() {
- mCalledOnChangedCount++;
- }
-
- public void onInvalidated() {
- mOnCalledInvalidatedCount++;
- }
- }
}
diff --git a/tests/tests/widget/src/android/widget/cts/AutoCompleteCtsActivity.java b/tests/tests/widget/src/android/widget/cts/AutoCompleteCtsActivity.java
index 1a12d66..38133ca 100644
--- a/tests/tests/widget/src/android/widget/cts/AutoCompleteCtsActivity.java
+++ b/tests/tests/widget/src/android/widget/cts/AutoCompleteCtsActivity.java
@@ -18,7 +18,6 @@
import android.app.Activity;
import android.os.Bundle;
-
import android.widget.cts.R;
/**
diff --git a/tests/tests/widget/src/android/widget/cts/AutoCompleteTextViewTest.java b/tests/tests/widget/src/android/widget/cts/AutoCompleteTextViewTest.java
index eb22d6b..a46e3eb 100644
--- a/tests/tests/widget/src/android/widget/cts/AutoCompleteTextViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/AutoCompleteTextViewTest.java
@@ -16,21 +16,44 @@
package android.widget.cts;
-import android.widget.cts.R;
+import static android.cts.util.WidgetTestUtils.sameCharSequence;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.eq;
+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.verifyNoMoreInteractions;
+import static org.mockito.Mockito.verifyZeroInteractions;
import android.app.Activity;
import android.app.Instrumentation;
import android.app.UiModeManager;
import android.content.Context;
import android.content.res.Configuration;
+import android.content.res.Resources;
import android.cts.util.PollingCheck;
+import android.graphics.Color;
import android.graphics.Rect;
-import android.test.ActivityInstrumentationTestCase2;
-import android.test.UiThreadTest;
+import android.graphics.drawable.Drawable;
+import android.os.SystemClock;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.text.Editable;
+import android.text.TextWatcher;
import android.util.AttributeSet;
import android.util.Xml;
import android.view.KeyCharacterMap;
@@ -40,37 +63,37 @@
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.AutoCompleteTextView;
+import android.widget.AutoCompleteTextView.Validator;
import android.widget.Filter;
import android.widget.Filterable;
-import android.widget.AutoCompleteTextView.Validator;
+import android.widget.cts.util.TestUtils;
-import java.io.IOException;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.xmlpull.v1.XmlPullParser;
-public class AutoCompleteTextViewTest extends
- ActivityInstrumentationTestCase2<AutoCompleteCtsActivity> {
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class AutoCompleteTextViewTest {
+ private final static String[] WORDS =
+ new String[] { "testOne", "testTwo", "testThree", "testFour" };
+ private final static String STRING_TEST = "To be tested";
+ private final static String STRING_VALIDATED = "String Validated";
+ private final static String STRING_CHECK = "To be checked";
- /**
- * Instantiates a new text view test.
- */
- public AutoCompleteTextViewTest() {
- super("android.widget.cts", AutoCompleteCtsActivity.class);
- }
-
- /** The m activity. */
private Activity mActivity;
-
- /** The m instrumentation. */
private Instrumentation mInstrumentation;
private AutoCompleteTextView mAutoCompleteTextView;
private boolean mNumeric = false;
- ArrayAdapter<String> mAdapter;
- private final String[] WORDS = new String[] { "testOne", "testTwo", "testThree", "testFour" };
- boolean isOnFilterComplete = false;
- final String STRING_TEST = "To be tested";
- final String STRING_VALIDATED = "String Validated";
- final String STRING_CHECK = "To be checked";
- final String STRING_APPEND = "and be appended";
- Validator mValidator = new Validator() {
+ private ArrayAdapter<String> mAdapter;
+
+ @Rule
+ public ActivityTestRule<AutoCompleteCtsActivity> mActivityRule
+ = new ActivityTestRule<>(AutoCompleteCtsActivity.class);
+
+ private final Validator mValidator = new Validator() {
public CharSequence fixText(CharSequence invalidText) {
return STRING_VALIDATED;
}
@@ -80,22 +103,35 @@
}
};
- /*
- * (non-Javadoc)
- *
- * @see android.test.ActivityInstrumentationTestCase#setUp()
- */
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mActivity = getActivity();
- new PollingCheck() {
- @Override
- protected boolean check() {
- return mActivity.hasWindowFocus();
- }
- }.run();
- mInstrumentation = getInstrumentation();
+ protected class MyTextWatcher implements TextWatcher {
+ private CharSequence mExpectedAfter;
+
+ public MyTextWatcher(CharSequence expectedAfter) {
+ mExpectedAfter = expectedAfter;
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ assertEquals(mExpectedAfter.toString(), s.toString());
+ // This watcher is expected to be notified in the middle of completion
+ assertTrue(mAutoCompleteTextView.isPerformingCompletion());
+ }
+
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ }
+ }
+
+ @Before
+ public void setup() {
+ mActivity = mActivityRule.getActivity();
+ PollingCheck.waitFor(mActivity::hasWindowFocus);
+
+ mInstrumentation = InstrumentationRegistry.getInstrumentation();
mAutoCompleteTextView = (AutoCompleteTextView) mActivity
.findViewById(R.id.autocompletetv_edit);
mAdapter = new ArrayAdapter<String>(mActivity,
@@ -107,37 +143,41 @@
}
boolean isTvMode() {
- UiModeManager uiModeManager = (UiModeManager) getActivity().getSystemService(
+ UiModeManager uiModeManager = (UiModeManager) mActivity.getSystemService(
Context.UI_MODE_SERVICE);
return uiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_TELEVISION;
}
- @UiThreadTest
+ @Test
public void testConstructor() {
XmlPullParser parser;
- // new the AutoCompleteTextView instance
new AutoCompleteTextView(mActivity);
+ new AutoCompleteTextView(mActivity, null);
+ new AutoCompleteTextView(mActivity, null, android.R.attr.autoCompleteTextViewStyle);
+ new AutoCompleteTextView(mActivity, null, 0,
+ android.R.style.Widget_DeviceDefault_AutoCompleteTextView);
+ new AutoCompleteTextView(mActivity, null, 0,
+ android.R.style.Widget_DeviceDefault_Light_AutoCompleteTextView);
+ new AutoCompleteTextView(mActivity, null, 0,
+ android.R.style.Widget_Material_AutoCompleteTextView);
+ new AutoCompleteTextView(mActivity, null, 0,
+ android.R.style.Widget_Material_Light_AutoCompleteTextView);
+
+ final Resources.Theme popupTheme = mActivity.getResources().newTheme();
+ popupTheme.applyStyle(android.R.style.Theme_Material, true);
+ new AutoCompleteTextView(mActivity, null, 0,
+ android.R.style.Widget_Material_Light_AutoCompleteTextView, popupTheme);
// new the AutoCompleteTextView instance
parser = mActivity.getResources().getXml(R.layout.simple_dropdown_item_1line);
AttributeSet attributeSet = Xml.asAttributeSet(parser);
new AutoCompleteTextView(mActivity, attributeSet);
- new AutoCompleteTextView(mActivity, null);
// new the AutoCompleteTextView instance
parser = mActivity.getResources().getXml(R.layout.framelayout_layout);
attributeSet = Xml.asAttributeSet(parser);
new AutoCompleteTextView(mActivity, attributeSet, 0);
- new AutoCompleteTextView(mActivity, null, 0);
- // Test constructor with null Context, in fact, previous two functions will
- // finally invoke this version.
- try {
- // Test with null Context
- new AutoCompleteTextView(null, attributeSet, 0);
- fail("should throw NullPointerException");
- } catch (Exception e) {
- }
// Test for negative style resource ID
new AutoCompleteTextView(mActivity, attributeSet, -1);
@@ -145,178 +185,256 @@
new AutoCompleteTextView(mActivity, null, -1);
}
- public void testEnoughToFilter() throws Throwable {
+ @Test(expected=NullPointerException.class)
+ public void testConstructorWithNullContext() {
+ XmlPullParser parser = mActivity.getResources().getXml(R.layout.simple_dropdown_item_1line);
+ AttributeSet attributeSet = Xml.asAttributeSet(parser);
+ new AutoCompleteTextView(null, attributeSet, 0);
+ }
+
+ @Test
+ public void testEnoughToFilter() {
mAutoCompleteTextView.setThreshold(3);
assertEquals(3, mAutoCompleteTextView.getThreshold());
- runTestOnUiThread(new Runnable() {
- public void run() {
- String testString = "TryToTest";
- mAutoCompleteTextView.setText(testString);
- }
- });
+ mInstrumentation.runOnMainSync(() -> mAutoCompleteTextView.setText("TryToTest"));
mInstrumentation.waitForIdleSync();
assertTrue(mAutoCompleteTextView.enoughToFilter());
- runTestOnUiThread(new Runnable() {
- public void run() {
- String testString = "No";
- mAutoCompleteTextView.setText(testString);
- }
- });
+ mInstrumentation.runOnMainSync(() -> mAutoCompleteTextView.setText("No"));
mInstrumentation.waitForIdleSync();
assertFalse(mAutoCompleteTextView.enoughToFilter());
}
- @UiThreadTest
+ @Test
public void testAccessAdapter() {
+ mInstrumentation.runOnMainSync(() -> mAutoCompleteTextView.setAdapter(null));
+ assertNull(mAutoCompleteTextView.getAdapter());
+
+ mInstrumentation.runOnMainSync(() -> mAutoCompleteTextView.setAdapter(mAdapter));
+ assertSame(mAdapter, mAutoCompleteTextView.getAdapter());
+
+ // Re-set adapter to null
+ mInstrumentation.runOnMainSync(() -> mAutoCompleteTextView.setAdapter(null));
+ assertNull(mAutoCompleteTextView.getAdapter());
+ }
+
+ @Test
+ public void testAccessFilter() {
MockAutoCompleteTextView autoCompleteTextView = new MockAutoCompleteTextView(mActivity);
// Set Threshold to 4 characters
autoCompleteTextView.setThreshold(4);
- ArrayAdapter<String> adapter = null;
- autoCompleteTextView.setAdapter(adapter);
+ autoCompleteTextView.setAdapter(null);
assertNull(autoCompleteTextView.getAdapter());
assertNull(autoCompleteTextView.getFilter());
- Filter filter = mAdapter.getFilter();
- assertNotNull(filter);
- autoCompleteTextView.setAdapter(mAdapter);
- assertSame(mAdapter, autoCompleteTextView.getAdapter());
- assertSame(filter, autoCompleteTextView.getFilter());
+ mInstrumentation.runOnMainSync(() -> {
+ Filter filter = mAdapter.getFilter();
+ assertNotNull(filter);
+ autoCompleteTextView.setAdapter(mAdapter);
+ assertSame(mAdapter, autoCompleteTextView.getAdapter());
+ assertSame(filter, autoCompleteTextView.getFilter());
+ });
// Re-set adapter to null
- autoCompleteTextView.setAdapter(adapter);
+ autoCompleteTextView.setAdapter(null);
assertNull(autoCompleteTextView.getAdapter());
assertNull(autoCompleteTextView.getFilter());
}
- @SuppressWarnings("deprecation")
+ @Test
public void testAccessItemClickListener() {
- final MockOnItemClickListener testOnItemClickListener = new MockOnItemClickListener();
+ final AdapterView.OnItemClickListener mockItemClickListener =
+ mock(AdapterView.OnItemClickListener.class);
// To ensure null listener
mAutoCompleteTextView.setOnItemClickListener(null);
assertNull(mAutoCompleteTextView.getItemClickListener());
assertNull(mAutoCompleteTextView.getOnItemClickListener());
- assertNotNull(testOnItemClickListener);
- mAutoCompleteTextView.setOnItemClickListener(testOnItemClickListener);
- assertSame(testOnItemClickListener, mAutoCompleteTextView.getItemClickListener());
- assertSame(testOnItemClickListener, mAutoCompleteTextView.getOnItemClickListener());
+ mAutoCompleteTextView.setOnItemClickListener(mockItemClickListener);
+ assertSame(mockItemClickListener, mAutoCompleteTextView.getItemClickListener());
+ assertSame(mockItemClickListener, mAutoCompleteTextView.getOnItemClickListener());
+ verifyZeroInteractions(mockItemClickListener);
// re-clear listener by setOnItemClickListener
mAutoCompleteTextView.setOnItemClickListener(null);
assertNull(mAutoCompleteTextView.getItemClickListener());
assertNull(mAutoCompleteTextView.getOnItemClickListener());
+ verifyZeroInteractions(mockItemClickListener);
}
- @SuppressWarnings("deprecation")
+ @Test
public void testAccessItemSelectedListener() {
- MockOnItemSelectedListener testOnItemSelectedListener = new MockOnItemSelectedListener();
+ final AdapterView.OnItemSelectedListener mockItemSelectedListener =
+ mock(AdapterView.OnItemSelectedListener.class);
// To ensure null listener
mAutoCompleteTextView.setOnItemSelectedListener(null);
assertNull(mAutoCompleteTextView.getItemSelectedListener());
assertNull(mAutoCompleteTextView.getOnItemSelectedListener());
- assertNotNull(testOnItemSelectedListener);
- mAutoCompleteTextView.setOnItemSelectedListener(testOnItemSelectedListener);
- assertSame(testOnItemSelectedListener, mAutoCompleteTextView.getItemSelectedListener());
- assertSame(testOnItemSelectedListener, mAutoCompleteTextView.getOnItemSelectedListener());
+ mAutoCompleteTextView.setOnItemSelectedListener(mockItemSelectedListener);
+ assertSame(mockItemSelectedListener, mAutoCompleteTextView.getItemSelectedListener());
+ assertSame(mockItemSelectedListener, mAutoCompleteTextView.getOnItemSelectedListener());
+ verifyZeroInteractions(mockItemSelectedListener);
//re-clear listener by setOnItemClickListener
mAutoCompleteTextView.setOnItemSelectedListener(null);
assertNull(mAutoCompleteTextView.getItemSelectedListener());
assertNull(mAutoCompleteTextView.getOnItemSelectedListener());
+ verifyZeroInteractions(mockItemSelectedListener);
}
- @UiThreadTest
+ @Test
public void testConvertSelectionToString() {
MockAutoCompleteTextView autoCompleteTextView = new MockAutoCompleteTextView(mActivity);
// Set Threshold to 4 characters
- autoCompleteTextView.setThreshold(4);
- autoCompleteTextView.setAdapter(mAdapter);
+ mInstrumentation.runOnMainSync(() -> {
+ autoCompleteTextView.setThreshold(4);
+ autoCompleteTextView.setAdapter(mAdapter);
+ });
assertNotNull(autoCompleteTextView.getAdapter());
assertEquals("", autoCompleteTextView.convertSelectionToString(null));
assertEquals(STRING_TEST, autoCompleteTextView.convertSelectionToString(STRING_TEST));
}
- @UiThreadTest
+ @Test
public void testOnTextChanged() {
- MockAutoCompleteTextView autoCompleteTextView = new MockAutoCompleteTextView(mActivity);
+ final TextWatcher mockTextWatcher = mock(TextWatcher.class);
+ mAutoCompleteTextView.addTextChangedListener(mockTextWatcher);
+ verify(mockTextWatcher, never()).onTextChanged(any(CharSequence.class),
+ anyInt(), anyInt(), anyInt());
- assertFalse(autoCompleteTextView.isOnTextChanged());
- assertEquals("", autoCompleteTextView.getLastChangeText());
- assertEquals("", autoCompleteTextView.getText().toString());
- assertEquals(0, autoCompleteTextView.getStart());
- assertEquals(0, autoCompleteTextView.getBefore());
- assertEquals(0, autoCompleteTextView.getAfter());
-
- autoCompleteTextView.setText(STRING_TEST);
- assertEquals(STRING_TEST, autoCompleteTextView.getText().toString());
- assertTrue(autoCompleteTextView.isOnTextChanged());
- assertEquals(STRING_TEST, autoCompleteTextView.getLastChangeText());
- assertEquals(0, autoCompleteTextView.getStart());
- assertEquals(0, autoCompleteTextView.getBefore());
- assertEquals(STRING_TEST.length(), autoCompleteTextView.getAfter());
+ mInstrumentation.runOnMainSync(() -> mAutoCompleteTextView.setText(STRING_TEST));
+ verify(mockTextWatcher, times(1)).onTextChanged(sameCharSequence(STRING_TEST),
+ eq(0), eq(0), eq(STRING_TEST.length()));
// Test replacing text.
- autoCompleteTextView.resetStatus();
- autoCompleteTextView.setText(STRING_CHECK);
- assertEquals(STRING_CHECK, autoCompleteTextView.getText().toString());
- assertEquals(STRING_CHECK, autoCompleteTextView.getLastChangeText());
- assertEquals(0, autoCompleteTextView.getStart());
- assertEquals(STRING_TEST.length(), autoCompleteTextView.getBefore());
- assertEquals(STRING_CHECK.length(), autoCompleteTextView.getAfter());
+ mInstrumentation.runOnMainSync(() -> mAutoCompleteTextView.setText(STRING_CHECK));
+ verify(mockTextWatcher, times(1)).onTextChanged(sameCharSequence(STRING_CHECK),
+ eq(0), eq(STRING_TEST.length()), eq(STRING_CHECK.length()));
}
- @UiThreadTest
- public void testPopupWindow() throws XmlPullParserException, IOException {
- assertFalse(mAutoCompleteTextView.isPopupShowing());
- mAutoCompleteTextView.showDropDown();
- assertTrue(mAutoCompleteTextView.isPopupShowing());
+ @Test
+ public void testPopupWindow() {
+ final AutoCompleteTextView.OnDismissListener mockDismissListener =
+ mock(AutoCompleteTextView.OnDismissListener.class);
+ mAutoCompleteTextView.setOnDismissListener(mockDismissListener);
- mAutoCompleteTextView.dismissDropDown();
assertFalse(mAutoCompleteTextView.isPopupShowing());
-
- mAutoCompleteTextView.showDropDown();
+ mInstrumentation.runOnMainSync(mAutoCompleteTextView::showDropDown);
assertTrue(mAutoCompleteTextView.isPopupShowing());
+ verifyZeroInteractions(mockDismissListener);
+
+ mInstrumentation.runOnMainSync(mAutoCompleteTextView::dismissDropDown);
+ assertFalse(mAutoCompleteTextView.isPopupShowing());
+ verify(mockDismissListener, times(1)).onDismiss();
+
+ mInstrumentation.runOnMainSync(mAutoCompleteTextView::showDropDown);
+ assertTrue(mAutoCompleteTextView.isPopupShowing());
+ verify(mockDismissListener, times(1)).onDismiss();
final MockValidator validator = new MockValidator();
mAutoCompleteTextView.setValidator(validator);
- mAutoCompleteTextView.requestFocus();
- mAutoCompleteTextView.showDropDown();
- assertTrue(mAutoCompleteTextView.isPopupShowing());
- mAutoCompleteTextView.setText(STRING_TEST);
+ mInstrumentation.runOnMainSync(() -> {
+ mAutoCompleteTextView.requestFocus();
+ mAutoCompleteTextView.showDropDown();
+ mAutoCompleteTextView.setText(STRING_TEST);
+ });
assertEquals(STRING_TEST, mAutoCompleteTextView.getText().toString());
+
// clearFocus will trigger onFocusChanged, and onFocusChanged will validate the text.
- mAutoCompleteTextView.clearFocus();
+ mInstrumentation.runOnMainSync(mAutoCompleteTextView::clearFocus);
assertFalse(mAutoCompleteTextView.isPopupShowing());
assertEquals(STRING_VALIDATED, mAutoCompleteTextView.getText().toString());
+ verify(mockDismissListener, times(2)).onDismiss();
+
+ verifyNoMoreInteractions(mockDismissListener);
}
- @UiThreadTest
+ @Test
+ public void testDropDownMetrics() {
+ mInstrumentation.runOnMainSync(() -> mAutoCompleteTextView.setAdapter(mAdapter));
+
+ final Resources res = mActivity.getResources();
+ final int dropDownWidth =
+ res.getDimensionPixelSize(R.dimen.autocomplete_textview_dropdown_width);
+ final int dropDownHeight =
+ res.getDimensionPixelSize(R.dimen.autocomplete_textview_dropdown_height);
+ final int dropDownOffsetHorizontal =
+ res.getDimensionPixelSize(R.dimen.autocomplete_textview_dropdown_offset_h);
+ final int dropDownOffsetVertical =
+ res.getDimensionPixelSize(R.dimen.autocomplete_textview_dropdown_offset_v);
+
+ mInstrumentation.runOnMainSync(() -> {
+ mAutoCompleteTextView.setDropDownWidth(dropDownWidth);
+ mAutoCompleteTextView.setDropDownHeight(dropDownHeight);
+ mAutoCompleteTextView.setDropDownHorizontalOffset(dropDownOffsetHorizontal);
+ mAutoCompleteTextView.setDropDownVerticalOffset(dropDownOffsetVertical);
+
+ mAutoCompleteTextView.showDropDown();
+ });
+
+ assertEquals(dropDownWidth, mAutoCompleteTextView.getDropDownWidth());
+ assertEquals(dropDownHeight, mAutoCompleteTextView.getDropDownHeight());
+ assertEquals(dropDownOffsetHorizontal, mAutoCompleteTextView.getDropDownHorizontalOffset());
+ assertEquals(dropDownOffsetVertical, mAutoCompleteTextView.getDropDownVerticalOffset());
+ }
+
+ @Test
+ public void testDropDownBackground() {
+ mInstrumentation.runOnMainSync(() -> mAutoCompleteTextView.setAdapter(mAdapter));
+
+ mInstrumentation.runOnMainSync(() -> {
+ mAutoCompleteTextView.setDropDownBackgroundResource(R.drawable.blue_fill);
+ mAutoCompleteTextView.showDropDown();
+ });
+ mInstrumentation.waitForIdleSync();
+
+ Drawable dropDownBackground = mAutoCompleteTextView.getDropDownBackground();
+ TestUtils.assertAllPixelsOfColor("Drop down should be blue", dropDownBackground,
+ dropDownBackground.getBounds().width(), dropDownBackground.getBounds().height(),
+ false, Color.BLUE, 1, true);
+
+ mInstrumentation.runOnMainSync(() -> {
+ mAutoCompleteTextView.dismissDropDown();
+ mAutoCompleteTextView.setDropDownBackgroundDrawable(
+ mActivity.getDrawable(R.drawable.yellow_fill));
+ mAutoCompleteTextView.showDropDown();
+ });
+ mInstrumentation.waitForIdleSync();
+
+ dropDownBackground = mAutoCompleteTextView.getDropDownBackground();
+ TestUtils.assertAllPixelsOfColor("Drop down should be yellow", dropDownBackground,
+ dropDownBackground.getBounds().width(), dropDownBackground.getBounds().height(),
+ false, Color.YELLOW, 1, true);
+ }
+
+ @Test
public void testReplaceText() {
MockAutoCompleteTextView autoCompleteTextView = new MockAutoCompleteTextView(mActivity);
+ final TextWatcher mockTextWatcher = mock(TextWatcher.class);
+ autoCompleteTextView.addTextChangedListener(mockTextWatcher);
+ verify(mockTextWatcher, never()).onTextChanged(any(CharSequence.class),
+ anyInt(), anyInt(), anyInt());
- assertEquals("", autoCompleteTextView.getText().toString());
- assertFalse(autoCompleteTextView.isOnTextChanged());
autoCompleteTextView.replaceText("Text");
assertEquals("Text", autoCompleteTextView.getText().toString());
- assertTrue(autoCompleteTextView.isOnTextChanged());
+ verify(mockTextWatcher, times(1)).onTextChanged(sameCharSequence("Text"),
+ eq(0), eq(0), eq("Text".length()));
- autoCompleteTextView.resetStatus();
- assertFalse(autoCompleteTextView.isOnTextChanged());
autoCompleteTextView.replaceText("Another");
assertEquals("Another", autoCompleteTextView.getText().toString());
- assertTrue(autoCompleteTextView.isOnTextChanged());
+ verify(mockTextWatcher, times(1)).onTextChanged(sameCharSequence("Another"),
+ eq(0), eq("Text".length()), eq("Another".length()));
}
- @UiThreadTest
+ @Test
public void testSetFrame() {
MockAutoCompleteTextView autoCompleteTextView = new MockAutoCompleteTextView(mActivity);
@@ -341,24 +459,22 @@
assertEquals(5, autoCompleteTextView.getBottom());
}
+ @Test
public void testGetThreshold() {
- final AutoCompleteTextView autoCompleteTextView = (AutoCompleteTextView) mActivity
- .findViewById(R.id.autocompletetv_edit);
- assertNotNull(autoCompleteTextView);
-
- assertEquals(1, autoCompleteTextView.getThreshold());
- autoCompleteTextView.setThreshold(3);
- assertEquals(3, autoCompleteTextView.getThreshold());
+ assertEquals(1, mAutoCompleteTextView.getThreshold());
+ mAutoCompleteTextView.setThreshold(3);
+ assertEquals(3, mAutoCompleteTextView.getThreshold());
// Test negative value input
- autoCompleteTextView.setThreshold(-5);
- assertEquals(1, autoCompleteTextView.getThreshold());
+ mAutoCompleteTextView.setThreshold(-5);
+ assertEquals(1, mAutoCompleteTextView.getThreshold());
// Test zero
- autoCompleteTextView.setThreshold(0);
- assertEquals(1, autoCompleteTextView.getThreshold());
+ mAutoCompleteTextView.setThreshold(0);
+ assertEquals(1, mAutoCompleteTextView.getThreshold());
}
+ @Test
public void testAccessValidater() {
final MockValidator validator = new MockValidator();
@@ -371,7 +487,8 @@
assertNull(mAutoCompleteTextView.getValidator());
}
- public void testOnFilterComplete() throws Throwable {
+ @Test
+ public void testOnFilterComplete() {
// Set Threshold to 4 characters
mAutoCompleteTextView.setThreshold(4);
@@ -384,22 +501,15 @@
}
// Test the filter if the input string is not long enough to threshold
- runTestOnUiThread(new Runnable() {
- public void run() {
+ mInstrumentation.runOnMainSync(() -> {
mAutoCompleteTextView.setAdapter(mAdapter);
mAutoCompleteTextView.setText("");
mAutoCompleteTextView.requestFocus();
- }
});
mInstrumentation.sendStringSync(testString);
// onFilterComplete will close the popup.
- new PollingCheck() {
- @Override
- protected boolean check() {
- return !mAutoCompleteTextView.isPopupShowing();
- }
- }.run();
+ PollingCheck.waitFor(() -> !mAutoCompleteTextView.isPopupShowing());
if (mNumeric) {
// "that" in case of 12-key(NUMERIC) keyboard
@@ -408,20 +518,13 @@
testString = "that";
}
mInstrumentation.sendStringSync(testString);
- new PollingCheck() {
- @Override
- protected boolean check() {
- return !mAutoCompleteTextView.isPopupShowing();
- }
- }.run();
+ PollingCheck.waitFor(() -> !mAutoCompleteTextView.isPopupShowing());
// Test the expected filter matching scene
- runTestOnUiThread(new Runnable() {
- public void run() {
+ mInstrumentation.runOnMainSync(() -> {
mAutoCompleteTextView.setFocusable(true);
mAutoCompleteTextView.requestFocus();
mAutoCompleteTextView.setText("");
- }
});
if (mNumeric) {
// "test" in case of 12-key(NUMERIC) keyboard
@@ -431,20 +534,15 @@
}
assertTrue(mAutoCompleteTextView.hasFocus());
assertTrue(mAutoCompleteTextView.hasWindowFocus());
- new PollingCheck() {
- @Override
- protected boolean check() {
- return mAutoCompleteTextView.isPopupShowing();
- }
- }.run();
+ PollingCheck.waitFor(() -> mAutoCompleteTextView.isPopupShowing());
}
- public void testPerformFiltering() throws Throwable {
+ @Test
+ public void testPerformFiltering() {
if (isTvMode()) {
return;
}
- runTestOnUiThread(new Runnable() {
- public void run() {
+ mInstrumentation.runOnMainSync(() -> {
mAutoCompleteTextView.setAdapter(mAdapter);
mAutoCompleteTextView.setValidator(mValidator);
@@ -452,7 +550,6 @@
mAutoCompleteTextView.setFocusable(true);
mAutoCompleteTextView.requestFocus();
mAutoCompleteTextView.showDropDown();
- }
});
mInstrumentation.waitForIdleSync();
assertTrue(mAutoCompleteTextView.isPopupShowing());
@@ -461,11 +558,9 @@
// KeyBack will close the popup.
assertFalse(mAutoCompleteTextView.isPopupShowing());
- runTestOnUiThread(new Runnable() {
- public void run() {
+ mInstrumentation.runOnMainSync(() -> {
mAutoCompleteTextView.dismissDropDown();
mAutoCompleteTextView.setText(STRING_TEST);
- }
});
mInstrumentation.waitForIdleSync();
@@ -478,12 +573,10 @@
android.R.layout.simple_dropdown_item_1line, WORDS);
// Set Threshold to 4 charactersonKeyDown
- runTestOnUiThread(new Runnable() {
- public void run() {
+ mInstrumentation.runOnMainSync(() -> {
mAutoCompleteTextView.setAdapter(adapter);
mAutoCompleteTextView.requestFocus();
mAutoCompleteTextView.setText("");
- }
});
mInstrumentation.waitForIdleSync();
// Create and get the filter.
@@ -495,38 +588,28 @@
if (mNumeric) {
// "numeric" in case of 12-key(NUMERIC) keyboard
mInstrumentation.sendStringSync("6688633777444222");
- new PollingCheck() {
- @Override
- protected boolean check() {
- return "numeric".equals(filter.getResult());
- }
- }.run();
+ PollingCheck.waitFor(() -> "numeric".equals(filter.getResult()));
} else {
- Thread.sleep(200);
+ SystemClock.sleep(200);
mInstrumentation.sendStringSync(STRING_TEST);
- new PollingCheck() {
- @Override
- protected boolean check() {
- return STRING_TEST.equals(filter.getResult());
- }
- }.run();
+ PollingCheck.waitFor(() -> STRING_TEST.equals(filter.getResult()));
}
}
- public void testPerformCompletion() throws Throwable {
+ @Test
+ public void testPerformCompletionWithDPad() {
if (isTvMode()) {
return;
}
- final MockOnItemClickListener listener = new MockOnItemClickListener();
+ final AdapterView.OnItemClickListener mockItemClickListener =
+ mock(AdapterView.OnItemClickListener.class);
assertFalse(mAutoCompleteTextView.isPerformingCompletion());
- runTestOnUiThread(new Runnable() {
- public void run() {
- mAutoCompleteTextView.setOnItemClickListener(listener);
- mAutoCompleteTextView.setAdapter(mAdapter);
- mAutoCompleteTextView.requestFocus();
- mAutoCompleteTextView.showDropDown();
- }
+ mInstrumentation.runOnMainSync(() -> {
+ mAutoCompleteTextView.setOnItemClickListener(mockItemClickListener);
+ mAutoCompleteTextView.setAdapter(mAdapter);
+ mAutoCompleteTextView.requestFocus();
+ mAutoCompleteTextView.showDropDown();
});
mInstrumentation.waitForIdleSync();
assertFalse(mAutoCompleteTextView.isPerformingCompletion());
@@ -535,102 +618,153 @@
mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_DPAD_DOWN);
mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_ENTER);
mInstrumentation.waitForIdleSync();
- assertTrue(listener.isOnItemClicked());
-
+ verify(mockItemClickListener, times(1)).onItemClick(any(AdapterView.class), any(View.class),
+ eq(0), eq(0L));
assertEquals(WORDS[0], mAutoCompleteTextView.getText().toString());
- // re-set 'clicked' flag to false
- listener.clearItemClickedStatus();
-
- runTestOnUiThread(new Runnable() {
- public void run() {
- mAutoCompleteTextView.showDropDown();
- }
- });
+ mInstrumentation.runOnMainSync(mAutoCompleteTextView::showDropDown);
mInstrumentation.waitForIdleSync();
mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_DPAD_DOWN);
mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_DPAD_CENTER);
- assertTrue(listener.isOnItemClicked());
+ verify(mockItemClickListener, times(2)).onItemClick(any(AdapterView.class), any(View.class),
+ eq(0), eq(0L));
assertEquals(WORDS[0], mAutoCompleteTextView.getText().toString());
assertFalse(mAutoCompleteTextView.isPerformingCompletion());
- listener.clearItemClickedStatus();
- runTestOnUiThread(new Runnable() {
- public void run() {
- mAutoCompleteTextView.showDropDown();
- }
- });
+ mInstrumentation.runOnMainSync(mAutoCompleteTextView::showDropDown);
mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_DPAD_DOWN);
// Test normal key code.
mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_0);
- assertFalse(listener.isOnItemClicked());
+ verifyNoMoreInteractions(mockItemClickListener);
assertNotSame("", mAutoCompleteTextView.getText().toString());
assertFalse(mAutoCompleteTextView.isPerformingCompletion());
- listener.clearItemClickedStatus();
-
// Test the method on the scene of popup is closed.
- runTestOnUiThread(new Runnable() {
- public void run() {
- mAutoCompleteTextView.dismissDropDown();
- }
- });
+ mInstrumentation.runOnMainSync(mAutoCompleteTextView::dismissDropDown);
mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_DPAD_DOWN);
mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_ENTER);
- assertFalse(listener.isOnItemClicked());
+ verifyNoMoreInteractions(mockItemClickListener);
assertNotSame("", mAutoCompleteTextView.getText().toString());
assertFalse(mAutoCompleteTextView.isPerformingCompletion());
}
- @UiThreadTest
+ @Test
+ public void testPerformCompletionExplicit() {
+ final AdapterView.OnItemClickListener mockItemClickListener =
+ mock(AdapterView.OnItemClickListener.class);
+ assertFalse(mAutoCompleteTextView.isPerformingCompletion());
+
+ // Create a custom watcher that checks isPerformingCompletion to return true
+ // in the "middle" of the performCompletion processing. We also spy on this watcher
+ // to make sure that its onTextChanged is invoked.
+ final TextWatcher myTextWatcher = new MyTextWatcher(WORDS[1]);
+ final TextWatcher spyTextWatcher = spy(myTextWatcher);
+ mAutoCompleteTextView.addTextChangedListener(spyTextWatcher);
+
+ mInstrumentation.runOnMainSync(() -> {
+ mAutoCompleteTextView.setOnItemClickListener(mockItemClickListener);
+ mAutoCompleteTextView.setAdapter(mAdapter);
+ mAutoCompleteTextView.requestFocus();
+ mAutoCompleteTextView.showDropDown();
+ });
+ mInstrumentation.waitForIdleSync();
+
+ assertTrue(mAutoCompleteTextView.isPopupShowing());
+ assertFalse(mAutoCompleteTextView.isPerformingCompletion());
+
+ mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_DPAD_DOWN);
+ mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_DPAD_DOWN);
+ mInstrumentation.runOnMainSync(mAutoCompleteTextView::performCompletion);
+ verify(mockItemClickListener, times(1)).onItemClick(any(AdapterView.class), any(View.class),
+ eq(1), eq(1L));
+ assertEquals(WORDS[1], mAutoCompleteTextView.getText().toString());
+ assertFalse(mAutoCompleteTextView.isPerformingCompletion());
+ assertFalse(mAutoCompleteTextView.isPopupShowing());
+
+ verify(spyTextWatcher, atLeastOnce()).onTextChanged(sameCharSequence(WORDS[1]),
+ eq(0), eq(0), eq(WORDS[1].length()));
+ verifyNoMoreInteractions(mockItemClickListener);
+ }
+
+ @Test
+ public void testSetTextWithCompletion() {
+ final AdapterView.OnItemClickListener mockItemClickListener =
+ mock(AdapterView.OnItemClickListener.class);
+
+ mInstrumentation.runOnMainSync(() -> {
+ mAutoCompleteTextView.setOnItemClickListener(mockItemClickListener);
+ mAutoCompleteTextView.setAdapter(mAdapter);
+ });
+ mInstrumentation.waitForIdleSync();
+
+ assertFalse(mAutoCompleteTextView.isPopupShowing());
+
+ mInstrumentation.runOnMainSync(() -> mAutoCompleteTextView.setText("testO", true));
+ mInstrumentation.waitForIdleSync();
+
+ assertTrue(mAutoCompleteTextView.isPopupShowing());
+ verifyZeroInteractions(mockItemClickListener);
+ }
+
+ @Test
+ public void testSetTextWithNoCompletion() {
+ final AdapterView.OnItemClickListener mockItemClickListener =
+ mock(AdapterView.OnItemClickListener.class);
+
+ mInstrumentation.runOnMainSync(() -> {
+ mAutoCompleteTextView.setOnItemClickListener(mockItemClickListener);
+ mAutoCompleteTextView.setAdapter(mAdapter);
+ });
+ mInstrumentation.waitForIdleSync();
+
+ assertFalse(mAutoCompleteTextView.isPopupShowing());
+
+ mInstrumentation.runOnMainSync(() -> mAutoCompleteTextView.setText("testO", false));
+ mInstrumentation.waitForIdleSync();
+
+ assertFalse(mAutoCompleteTextView.isPopupShowing());
+ verifyZeroInteractions(mockItemClickListener);
+ }
+
+ @Test
public void testPerformValidation() {
final CharSequence text = "this";
- mAutoCompleteTextView.setValidator(mValidator);
- mAutoCompleteTextView.setAdapter((ArrayAdapter<String>) null);
- mAutoCompleteTextView.setText(text);
- mAutoCompleteTextView.performValidation();
+ mInstrumentation.runOnMainSync(() -> {
+ mAutoCompleteTextView.setValidator(mValidator);
+ mAutoCompleteTextView.setAdapter((ArrayAdapter<String>) null);
+ mAutoCompleteTextView.setText(text);
+ mAutoCompleteTextView.performValidation();
+ });
assertEquals(STRING_VALIDATED, mAutoCompleteTextView.getText().toString());
mAutoCompleteTextView.setValidator(null);
}
- public void testSetCompletionHint() {
+ @Test
+ public void testAccessCompletionHint() {
mAutoCompleteTextView.setCompletionHint("TEST HINT");
+ assertEquals("TEST HINT", mAutoCompleteTextView.getCompletionHint());
+
+ mAutoCompleteTextView.setCompletionHint(null);
+ assertNull(mAutoCompleteTextView.getCompletionHint());
}
- public void testOnAttachedToWindow() {
- // implement details, do not test
- }
+ @Test
+ public void testAccessListSelection() {
+ final AdapterView.OnItemClickListener mockItemClickListener =
+ mock(AdapterView.OnItemClickListener.class);
- public void testOnCommitCompletion() {
- // implement details, do not test
- }
-
- public void testOnDetachedFromWindow() {
- // implement details, do not test
- }
-
- public void testOnKeyPreIme() {
- // implement details, do not test
- }
-
- public void testAccessListSelection() throws Throwable {
- final MockOnItemClickListener listener = new MockOnItemClickListener();
-
- runTestOnUiThread(new Runnable() {
- public void run() {
- mAutoCompleteTextView.setOnItemClickListener(listener);
+ mInstrumentation.runOnMainSync(() -> {
+ mAutoCompleteTextView.setOnItemClickListener(mockItemClickListener);
mAutoCompleteTextView.setAdapter(mAdapter);
mAutoCompleteTextView.requestFocus();
mAutoCompleteTextView.showDropDown();
- }
});
mInstrumentation.waitForIdleSync();
- runTestOnUiThread(new Runnable() {
- public void run() {
+ mInstrumentation.runOnMainSync(() -> {
mAutoCompleteTextView.setListSelection(1);
assertEquals(1, mAutoCompleteTextView.getListSelection());
@@ -639,11 +773,11 @@
mAutoCompleteTextView.clearListSelection();
assertEquals(2, mAutoCompleteTextView.getListSelection());
- }
});
mInstrumentation.waitForIdleSync();
}
+ @Test
public void testAccessDropDownAnchor() {
mAutoCompleteTextView.setDropDownAnchor(View.NO_ID);
assertEquals(View.NO_ID, mAutoCompleteTextView.getDropDownAnchor());
@@ -652,6 +786,7 @@
assertEquals(0x5555, mAutoCompleteTextView.getDropDownAnchor());
}
+ @Test
public void testAccessDropDownWidth() {
mAutoCompleteTextView.setDropDownWidth(ViewGroup.LayoutParams.WRAP_CONTENT);
assertEquals(ViewGroup.LayoutParams.WRAP_CONTENT, mAutoCompleteTextView.getDropDownWidth());
@@ -660,75 +795,25 @@
assertEquals(ViewGroup.LayoutParams.MATCH_PARENT, mAutoCompleteTextView.getDropDownWidth());
}
- private static class MockOnItemClickListener implements AdapterView.OnItemClickListener {
- private boolean mOnItemClickedFlag = false;
-
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- mOnItemClickedFlag = true;
- return;
- }
-
- public boolean isOnItemClicked() {
- return mOnItemClickedFlag;
- }
-
- public void clearItemClickedStatus() {
- mOnItemClickedFlag = false;
- }
- }
-
- private static class MockOnItemSelectedListener implements AdapterView.OnItemSelectedListener {
- public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
- return;
- }
-
- public void onNothingSelected(AdapterView<?> parent) {
- return;
- }
- }
-
private class MockValidator implements AutoCompleteTextView.Validator {
public CharSequence fixText(CharSequence invalidText) {
return STRING_VALIDATED;
}
public boolean isValid(CharSequence text) {
- if (text == STRING_TEST) {
- return true;
- }
- return false;
+ return (text == STRING_TEST);
}
}
- private static class MockAutoCompleteTextView extends AutoCompleteTextView {
- private boolean mOnTextChangedFlag = false;
- private boolean mOnFilterCompleteFlag = false;
- private String lastChangeText = "";
- private int mStart = 0;
- private int mBefore = 0;
- private int mAfter = 0;
-
- public void resetStatus() {
- mOnTextChangedFlag = false;
- mOnFilterCompleteFlag = false;
- mStart = 0;
- mBefore = 0;
- mAfter = 0;
- }
-
+ protected static class MockAutoCompleteTextView extends AutoCompleteTextView {
public MockAutoCompleteTextView(Context context) {
super(context);
- resetStatus();
}
public MockAutoCompleteTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
- protected MockAutoCompleteTextView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- }
-
@Override
protected CharSequence convertSelectionToString(Object selectedItem) {
return super.convertSelectionToString(selectedItem);
@@ -745,16 +830,6 @@
}
@Override
- protected void onTextChanged(CharSequence text, int start, int before, int after) {
- super.onTextChanged(text, start, before, after);
- mOnTextChangedFlag = true;
- lastChangeText = text.toString();
- mStart = start;
- mBefore = before;
- mAfter = after;
- }
-
- @Override
protected void performFiltering(CharSequence text, int keyCode) {
super.performFiltering(text, keyCode);
}
@@ -768,36 +843,6 @@
protected boolean setFrame(int l, int t, int r, int b) {
return super.setFrame(l, t, r, b);
}
-
- @Override
- public void onFilterComplete(int count) {
- super.onFilterComplete(count);
- mOnFilterCompleteFlag = true;
- }
-
- protected boolean isOnTextChanged() {
- return mOnTextChangedFlag;
- }
-
- protected String getLastChangeText() {
- return lastChangeText;
- }
-
- protected boolean isOnFilterComplete() {
- return mOnFilterCompleteFlag;
- }
-
- protected int getStart() {
- return mStart;
- }
-
- protected int getBefore() {
- return mBefore;
- }
-
- protected int getAfter() {
- return mAfter;
- }
}
private static class MockFilter extends Filter {
diff --git a/tests/tests/widget/src/android/widget/cts/BaseAdapterTest.java b/tests/tests/widget/src/android/widget/cts/BaseAdapterTest.java
index 7659c1e..6b3d34f 100644
--- a/tests/tests/widget/src/android/widget/cts/BaseAdapterTest.java
+++ b/tests/tests/widget/src/android/widget/cts/BaseAdapterTest.java
@@ -16,78 +16,103 @@
package android.widget.cts;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+
import android.database.DataSetObserver;
-import android.test.AndroidTestCase;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
/**
* Test {@link BaseAdapter}.
*/
-public class BaseAdapterTest extends AndroidTestCase {
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class BaseAdapterTest {
+ @Test
public void testHasStableIds() {
BaseAdapter baseAdapter = new MockBaseAdapter();
assertFalse(baseAdapter.hasStableIds());
}
+ @Test
public void testDataSetObserver() {
BaseAdapter baseAdapter = new MockBaseAdapter();
- MockDataSetObserver dataSetObserver = new MockDataSetObserver();
+ DataSetObserver mockDataSetObserver = mock(DataSetObserver.class);
- assertFalse(dataSetObserver.hasCalledOnChanged());
+ verifyZeroInteractions(mockDataSetObserver);
baseAdapter.notifyDataSetChanged();
- assertFalse(dataSetObserver.hasCalledOnChanged());
+ verifyZeroInteractions(mockDataSetObserver);
- baseAdapter.registerDataSetObserver(dataSetObserver);
+ baseAdapter.registerDataSetObserver(mockDataSetObserver);
baseAdapter.notifyDataSetChanged();
- assertTrue(dataSetObserver.hasCalledOnChanged());
+ verify(mockDataSetObserver, times(1)).onChanged();
- dataSetObserver.reset();
- assertFalse(dataSetObserver.hasCalledOnChanged());
- baseAdapter.unregisterDataSetObserver(dataSetObserver);
+ reset(mockDataSetObserver);
+ verifyZeroInteractions(mockDataSetObserver);
+ baseAdapter.unregisterDataSetObserver(mockDataSetObserver);
baseAdapter.notifyDataSetChanged();
- assertFalse(dataSetObserver.hasCalledOnChanged());
+ verifyZeroInteractions(mockDataSetObserver);
}
+ @Test
public void testNotifyDataSetInvalidated() {
BaseAdapter baseAdapter = new MockBaseAdapter();
- MockDataSetObserver dataSetObserver = new MockDataSetObserver();
+ DataSetObserver mockDataSetObserver = mock(DataSetObserver.class);
- assertFalse(dataSetObserver.hasCalledOnInvalidated());
+ verifyZeroInteractions(mockDataSetObserver);
baseAdapter.notifyDataSetInvalidated();
- assertFalse(dataSetObserver.hasCalledOnInvalidated());
+ verifyZeroInteractions(mockDataSetObserver);
- baseAdapter.registerDataSetObserver(dataSetObserver);
+ baseAdapter.registerDataSetObserver(mockDataSetObserver);
baseAdapter.notifyDataSetInvalidated();
- assertTrue(dataSetObserver.hasCalledOnInvalidated());
+ verify(mockDataSetObserver, times(1)).onInvalidated();
}
+ @Test
public void testAreAllItemsEnabled() {
BaseAdapter baseAdapter = new MockBaseAdapter();
assertTrue(baseAdapter.areAllItemsEnabled());
}
+ @Test
public void testIsEnabled() {
BaseAdapter baseAdapter = new MockBaseAdapter();
assertTrue(baseAdapter.isEnabled(0));
}
+ @Test
public void testGetDropDownView() {
BaseAdapter baseAdapter = new MockBaseAdapter();
assertNull(baseAdapter.getDropDownView(0, null, null));
}
+ @Test
public void testGetItemViewType() {
BaseAdapter baseAdapter = new MockBaseAdapter();
assertEquals(0, baseAdapter.getItemViewType(0));
}
+ @Test
public void testGetViewTypeCount() {
BaseAdapter baseAdapter = new MockBaseAdapter();
assertEquals(1, baseAdapter.getViewTypeCount());
}
+ @Test
public void testIsEmpty() {
MockBaseAdapter baseAdapter = new MockBaseAdapter();
@@ -121,34 +146,4 @@
return null;
}
}
-
- private static class MockDataSetObserver extends DataSetObserver {
- private boolean mCalledOnChanged = false;
- private boolean mCalledOnInvalidated = false;
-
- @Override
- public void onChanged() {
- super.onChanged();
- mCalledOnChanged = true;
- }
-
- public boolean hasCalledOnChanged() {
- return mCalledOnChanged;
- }
-
- @Override
- public void onInvalidated() {
- super.onInvalidated();
- mCalledOnInvalidated = true;
- }
-
- public boolean hasCalledOnInvalidated() {
- return mCalledOnInvalidated;
- }
-
- public void reset() {
- mCalledOnChanged = false;
- mCalledOnInvalidated = false;
- }
- }
}
diff --git a/tests/tests/widget/src/android/widget/cts/BaseExpandableListAdapterTest.java b/tests/tests/widget/src/android/widget/cts/BaseExpandableListAdapterTest.java
index 1737cfd..79a4b91 100644
--- a/tests/tests/widget/src/android/widget/cts/BaseExpandableListAdapterTest.java
+++ b/tests/tests/widget/src/android/widget/cts/BaseExpandableListAdapterTest.java
@@ -16,21 +16,37 @@
package android.widget.cts;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+
import android.database.DataSetObserver;
-import android.test.InstrumentationTestCase;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseExpandableListAdapter;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
/**
* Test {@link BaseExpandableListAdapter}.
*/
-public class BaseExpandableListAdapterTest extends InstrumentationTestCase {
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class BaseExpandableListAdapterTest {
+ @Test
public void testAreAllItemsEnabled() {
MockBaseExpandableListAdapter adapter = new MockBaseExpandableListAdapter();
assertTrue(adapter.areAllItemsEnabled());
}
+ @Test
public void testGetCombinedId() {
MockBaseExpandableListAdapter adapter = new MockBaseExpandableListAdapter();
@@ -45,6 +61,7 @@
assertTrue(childID != groupID);
}
+ @Test
public void testIsEmpty() {
MockBaseExpandableListAdapter adapter = new MockBaseExpandableListAdapter();
assertTrue(adapter.isEmpty());
@@ -52,81 +69,57 @@
assertFalse(adapter.isEmpty());
}
+ @Test
public void testNotifyDataSetChanged() {
MockBaseExpandableListAdapter adapter = new MockBaseExpandableListAdapter();
- MockDataSetObserver dataSetObserver = new MockDataSetObserver();
- adapter.registerDataSetObserver(dataSetObserver);
+ DataSetObserver mockDataSetObserver = mock(DataSetObserver.class);
+ adapter.registerDataSetObserver(mockDataSetObserver);
- assertFalse(dataSetObserver.hasCalledOnChanged());
+ verifyZeroInteractions(mockDataSetObserver);
adapter.notifyDataSetChanged();
- assertTrue(dataSetObserver.hasCalledOnChanged());
+ verify(mockDataSetObserver, times(1)).onChanged();
}
+ @Test
public void testNotifyDataSetInvalidated() {
MockBaseExpandableListAdapter adapter = new MockBaseExpandableListAdapter();
- MockDataSetObserver dataSetObserver = new MockDataSetObserver();
- adapter.registerDataSetObserver(dataSetObserver);
+ DataSetObserver mockDataSetObserver = mock(DataSetObserver.class);
+ adapter.registerDataSetObserver(mockDataSetObserver);
- assertFalse(dataSetObserver.hasCalledOnInvalidated());
+ verifyZeroInteractions(mockDataSetObserver);
adapter.notifyDataSetInvalidated();
- assertTrue(dataSetObserver.hasCalledOnInvalidated());
+ verify(mockDataSetObserver, times(1)).onInvalidated();
}
+ @Test
public void testOnGroupCollapsed() {
MockBaseExpandableListAdapter adapter = new MockBaseExpandableListAdapter();
// this function is non-operation.
adapter.onGroupCollapsed(0);
}
+ @Test
public void testOnGroupExpanded() {
MockBaseExpandableListAdapter adapter = new MockBaseExpandableListAdapter();
// this function is non-operation.
adapter.onGroupExpanded(0);
}
+ @Test
public void testDataSetObserver() {
MockBaseExpandableListAdapter adapter = new MockBaseExpandableListAdapter();
- MockDataSetObserver dataSetObserver = new MockDataSetObserver();
- adapter.registerDataSetObserver(dataSetObserver);
+ DataSetObserver mockDataSetObserver = mock(DataSetObserver.class);
+ adapter.registerDataSetObserver(mockDataSetObserver);
- assertFalse(dataSetObserver.hasCalledOnChanged());
+ verifyZeroInteractions(mockDataSetObserver);
adapter.notifyDataSetChanged();
- assertTrue(dataSetObserver.hasCalledOnChanged());
+ verify(mockDataSetObserver, times(1)).onChanged();
- dataSetObserver.reset();
- assertFalse(dataSetObserver.hasCalledOnChanged());
- adapter.unregisterDataSetObserver(dataSetObserver);
+ reset(mockDataSetObserver);
+ verifyZeroInteractions(mockDataSetObserver);
+ adapter.unregisterDataSetObserver(mockDataSetObserver);
adapter.notifyDataSetChanged();
- assertFalse(dataSetObserver.hasCalledOnChanged());
- }
-
- private class MockDataSetObserver extends DataSetObserver {
- private boolean mCalledOnChanged = false;
- private boolean mCalledOnInvalidated = false;
-
- @Override
- public void onChanged() {
- super.onChanged();
- mCalledOnChanged = true;
- }
-
- public boolean hasCalledOnChanged() {
- return mCalledOnChanged;
- }
-
- @Override
- public void onInvalidated() {
- super.onInvalidated();
- mCalledOnInvalidated = true;
- }
-
- public boolean hasCalledOnInvalidated() {
- return mCalledOnInvalidated;
- }
-
- public void reset() {
- mCalledOnChanged = false;
- }
+ verifyZeroInteractions(mockDataSetObserver);
}
private class MockBaseExpandableListAdapter extends BaseExpandableListAdapter {
diff --git a/tests/tests/widget/src/android/widget/cts/ButtonTest.java b/tests/tests/widget/src/android/widget/cts/ButtonTest.java
index 9da2ecf..9a76c0f 100644
--- a/tests/tests/widget/src/android/widget/cts/ButtonTest.java
+++ b/tests/tests/widget/src/android/widget/cts/ButtonTest.java
@@ -16,45 +16,44 @@
package android.widget.cts;
-import org.xmlpull.v1.XmlPullParser;
-
import android.content.Context;
-import android.test.AndroidTestCase;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
import android.util.AttributeSet;
import android.util.Xml;
import android.widget.Button;
-import android.widget.cts.R;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.xmlpull.v1.XmlPullParser;
-
-public class ButtonTest extends AndroidTestCase {
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ButtonTest {
+ @Test
public void testConstructor() {
- XmlPullParser parser = mContext.getResources().getXml(R.layout.togglebutton_layout);
+ Context context = InstrumentationRegistry.getTargetContext();
+ XmlPullParser parser = context.getResources().getXml(R.layout.togglebutton_layout);
AttributeSet attrs = Xml.asAttributeSet(parser);
- new Button(mContext, attrs, 0);
- new Button(mContext, attrs);
- new Button(mContext);
+ new Button(context, attrs, 0);
+ new Button(context, attrs);
+ new Button(context);
+ }
- try {
- new Button(null, null, -1);
- fail("Should throw NullPointerException.");
- } catch (NullPointerException e) {
- // expected, test success.
- }
+ @Test(expected=NullPointerException.class)
+ public void testConstructorWithNullContext1() {
+ new Button(null);
+ }
- try {
- new Button(null, null);
- fail("Should throw NullPointerException.");
- } catch (NullPointerException e) {
- // expected, test success.
- }
+ @Test(expected=NullPointerException.class)
+ public void testConstructorWithNullContext2() {
+ new Button(null, null);
+ }
- try {
- new Button(null);
- fail("Should throw NullPointerException.");
- } catch (NullPointerException e) {
- // expected, test success.
- }
+ @Test(expected=NullPointerException.class)
+ public void testConstructorWithNullContext3() {
+ new Button(null, null, -1);
}
}
diff --git a/tests/tests/widget/src/android/widget/cts/CalendarViewCtsActivity.java b/tests/tests/widget/src/android/widget/cts/CalendarViewCtsActivity.java
index b0a15bf..99101fe 100644
--- a/tests/tests/widget/src/android/widget/cts/CalendarViewCtsActivity.java
+++ b/tests/tests/widget/src/android/widget/cts/CalendarViewCtsActivity.java
@@ -19,7 +19,6 @@
import android.app.Activity;
import android.os.Bundle;
import android.widget.CalendarView;
-import android.widget.Toolbar;
/**
* A minimal application for {@link CalendarView} test.
diff --git a/tests/tests/widget/src/android/widget/cts/CalendarViewTest.java b/tests/tests/widget/src/android/widget/cts/CalendarViewTest.java
index 31ad341..13031d9 100644
--- a/tests/tests/widget/src/android/widget/cts/CalendarViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/CalendarViewTest.java
@@ -16,41 +16,65 @@
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.Assert.fail;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.verifyZeroInteractions;
+
import android.annotation.ColorInt;
import android.app.Instrumentation;
-import android.test.ActivityInstrumentationTestCase2;
+import android.cts.util.CtsTouchUtils;
+import android.graphics.Rect;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.MediumTest;
+import android.view.ViewGroup;
import android.widget.CalendarView;
+import android.widget.ScrollView;
import android.widget.cts.util.TestUtils;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
import java.util.Calendar;
import java.util.GregorianCalendar;
@MediumTest
-public class CalendarViewTest extends ActivityInstrumentationTestCase2<CalendarViewCtsActivity> {
+@RunWith(AndroidJUnit4.class)
+public class CalendarViewTest {
+ private Instrumentation mInstrumentation;
private CalendarViewCtsActivity mActivity;
private CalendarView mCalendarViewMaterial;
private CalendarView mCalendarViewHolo;
- public CalendarViewTest() {
- super("android.widget.cts", CalendarViewCtsActivity.class);
- }
+ @Rule
+ public ActivityTestRule<CalendarViewCtsActivity> mActivityRule
+ = new ActivityTestRule<>(CalendarViewCtsActivity.class);
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mActivity = getActivity();
+ @Before
+ public void setup() {
+ mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ mActivity = mActivityRule.getActivity();
mCalendarViewMaterial = (CalendarView) mActivity.findViewById(R.id.calendar_view_material);
mCalendarViewHolo = (CalendarView) mActivity.findViewById(R.id.calendar_view_holoyolo);
// Initialize both calendar views to the current date
final long currentDate = new GregorianCalendar().getTime().getTime();
- getInstrumentation().runOnMainSync(() -> {
+ mInstrumentation.runOnMainSync(() -> {
mCalendarViewMaterial.setDate(currentDate);
mCalendarViewHolo.setDate(currentDate);
});
}
+ @Test
public void testConstructor() {
new CalendarView(mActivity);
@@ -58,18 +82,24 @@
new CalendarView(mActivity, null, android.R.attr.calendarViewStyle);
+ new CalendarView(mActivity, null, 0, android.R.style.Widget_DeviceDefault_CalendarView);
+
+ new CalendarView(mActivity, null, 0,
+ android.R.style.Widget_DeviceDefault_Light_CalendarView);
+
+ new CalendarView(mActivity, null, 0, android.R.style.Widget_Material_CalendarView);
+
new CalendarView(mActivity, null, 0, android.R.style.Widget_Material_Light_CalendarView);
}
+ @Test
public void testAccessDate() {
- final Instrumentation instrumentation = getInstrumentation();
-
// Go back one year
final Calendar newCalendar = new GregorianCalendar();
newCalendar.set(Calendar.YEAR, newCalendar.get(Calendar.YEAR) - 1);
final long yearAgoDate = newCalendar.getTime().getTime();
- instrumentation.runOnMainSync(
+ mInstrumentation.runOnMainSync(
() -> mCalendarViewMaterial.setDate(yearAgoDate));
assertEquals(yearAgoDate, mCalendarViewMaterial.getDate());
@@ -77,14 +107,13 @@
newCalendar.set(Calendar.YEAR, newCalendar.get(Calendar.YEAR) + 2);
final long yearHenceDate = newCalendar.getTime().getTime();
- instrumentation.runOnMainSync(
+ mInstrumentation.runOnMainSync(
() -> mCalendarViewMaterial.setDate(yearHenceDate, true, false));
assertEquals(yearHenceDate, mCalendarViewMaterial.getDate());
}
+ @Test
public void testAccessMinMaxDate() {
- final Instrumentation instrumentation = getInstrumentation();
-
// Use a range of minus/plus one year as min/max dates
final Calendar minCalendar = new GregorianCalendar();
minCalendar.set(Calendar.YEAR, minCalendar.get(Calendar.YEAR) - 1);
@@ -94,7 +123,7 @@
final long minDate = minCalendar.getTime().getTime();
final long maxDate = maxCalendar.getTime().getTime();
- instrumentation.runOnMainSync(() -> {
+ mInstrumentation.runOnMainSync(() -> {
mCalendarViewMaterial.setMinDate(minDate);
mCalendarViewMaterial.setMaxDate(maxDate);
});
@@ -103,6 +132,97 @@
assertEquals(mCalendarViewMaterial.getMaxDate(), maxDate);
}
+ @Test
+ public void testCalendarViewMinMaxRangeRestrictions() {
+ verifyMinMaxRangeRestrictions(mCalendarViewHolo);
+ verifyMinMaxRangeRestrictions(mCalendarViewMaterial);
+ }
+
+ private void verifyMinMaxRangeRestrictions(CalendarView calendarView) {
+ // Use a range of minus/plus one year as min/max dates.
+ final Calendar minCalendar = new GregorianCalendar();
+ minCalendar.set(Calendar.YEAR, minCalendar.get(Calendar.YEAR) - 1);
+ final Calendar maxCalendar = new GregorianCalendar();
+ maxCalendar.set(Calendar.YEAR, maxCalendar.get(Calendar.YEAR) + 1);
+ final long minDate = minCalendar.getTime().getTime();
+ final long maxDate = maxCalendar.getTime().getTime();
+
+ mInstrumentation.runOnMainSync(() -> {
+ calendarView.setMinDate(minDate);
+ calendarView.setMaxDate(maxDate);
+
+ try {
+ calendarView.setDate(minDate - 1);
+ fail("Should throw IllegalArgumentException, date is before minDate");
+ } catch (IllegalArgumentException e) {
+ }
+
+ try {
+ calendarView.setDate(maxDate + 1);
+ fail("Should throw IllegalArgumentException, date is after maxDate");
+ } catch (IllegalArgumentException e) {
+ }
+ });
+ }
+
+ private void verifyOnDateChangeListener(CalendarView calendarView,
+ boolean onlyAllowOneChangeEvent) {
+ final CalendarView.OnDateChangeListener mockDateChangeListener =
+ mock(CalendarView.OnDateChangeListener.class);
+ calendarView.setOnDateChangeListener(mockDateChangeListener);
+
+ // Go back to September 2008
+ final Calendar calendar = new GregorianCalendar();
+ calendar.set(Calendar.YEAR, 2008);
+ calendar.set(Calendar.MONTH, Calendar.SEPTEMBER);
+ calendar.set(Calendar.DAY_OF_MONTH, 16);
+ mInstrumentation.runOnMainSync(
+ () -> calendarView.setDate(calendar.getTime().getTime(), false, true));
+ mInstrumentation.waitForIdleSync();
+
+ // Get bounds of 09/23/2008
+ calendar.set(Calendar.DAY_OF_MONTH, 23);
+ final Rect dayBounds = new Rect();
+ final boolean getDayBoundsSuccess = calendarView.getBoundsForDate(
+ calendar.getTime().getTime(), dayBounds);
+ assertTrue(getDayBoundsSuccess);
+
+ if (onlyAllowOneChangeEvent) {
+ verifyZeroInteractions(mockDateChangeListener);
+ }
+
+ // Use instrumentation to emulate a tap on 09/23/2008
+ CtsTouchUtils.emulateTapOnScreen(mInstrumentation, calendarView,
+ dayBounds.left + dayBounds.width() / 2,
+ dayBounds.top + dayBounds.height() / 2);
+
+ verify(mockDateChangeListener, times(1)).onSelectedDayChange(calendarView,
+ 2008, Calendar.SEPTEMBER, 23);
+ if (onlyAllowOneChangeEvent) {
+ verifyNoMoreInteractions(mockDateChangeListener);
+ }
+ }
+
+ @Test
+ public void testOnDateChangeListenerHolo() {
+ // Scroll the Holo calendar view all the way up so it's fully visible
+ final ScrollView scroller = (ScrollView) mActivity.findViewById(R.id.scroller);
+ final ViewGroup container = (ViewGroup) scroller.findViewById(R.id.container);
+
+ mInstrumentation.runOnMainSync(() -> scroller.scrollTo(0, container.getHeight()));
+ // Note that in pre-Material world we are "allowing" the CalendarView to notify
+ // the date change listener on multiple occasions. This is the old behavior of the widget.
+ verifyOnDateChangeListener(mCalendarViewHolo, false);
+ }
+
+ @Test
+ public void testOnDateChangeListenerMaterial() {
+ // Note that in Material world only "real" date change events are allowed to be reported
+ // to our listener. This is the new behavior of the widget.
+ verifyOnDateChangeListener(mCalendarViewMaterial, true);
+ }
+
+ @Test
public void testAppearanceMaterial() {
// The logic in this method is performed on a Material-styled CalendarView and
// non-deprecated attributes / visual appearance APIs
@@ -114,22 +234,21 @@
assertEquals(R.style.TextAppearance_WithColorGreen,
mCalendarViewMaterial.getWeekDayTextAppearance());
- final Instrumentation instrumentation = getInstrumentation();
-
// Change the visual appearance of the widget
- instrumentation.runOnMainSync(() -> {
- mCalendarViewMaterial.setFirstDayOfWeek(3);
+ mInstrumentation.runOnMainSync(() -> {
+ mCalendarViewMaterial.setFirstDayOfWeek(Calendar.TUESDAY);
mCalendarViewMaterial.setDateTextAppearance(R.style.TextAppearance_WithColorBlue);
mCalendarViewMaterial.setWeekDayTextAppearance(R.style.TextAppearance_WithColorMagenta);
});
- assertEquals(3, mCalendarViewMaterial.getFirstDayOfWeek());
+ assertEquals(Calendar.TUESDAY, mCalendarViewMaterial.getFirstDayOfWeek());
assertEquals(R.style.TextAppearance_WithColorBlue,
mCalendarViewMaterial.getDateTextAppearance());
assertEquals(R.style.TextAppearance_WithColorMagenta,
mCalendarViewMaterial.getWeekDayTextAppearance());
}
+ @Test
public void testAppearanceHolo() {
// All the logic in this method is performed on a Holo-styled CalendarView, as
// under Material design we are ignoring most of these decorative attributes
@@ -151,8 +270,6 @@
TestUtils.assertAllPixelsOfColor("Selected date vertical bar blue",
mCalendarViewHolo.getSelectedDateVerticalBar(), 40, 40, true, 0xFF0000FF, 1, true);
- final Instrumentation instrumentation = getInstrumentation();
-
// Change the visual appearance of the widget
final @ColorInt int newSelectedWeekBackgroundColor =
mActivity.getColor(R.color.calendarview_week_background_new);
@@ -165,8 +282,8 @@
final @ColorInt int newWeekSeparatorLineColor =
mActivity.getColor(R.color.calendarview_week_separatorline_new);
- instrumentation.runOnMainSync(() -> {
- mCalendarViewHolo.setFirstDayOfWeek(1);
+ mInstrumentation.runOnMainSync(() -> {
+ mCalendarViewHolo.setFirstDayOfWeek(Calendar.SUNDAY);
mCalendarViewHolo.setShownWeekCount(4);
mCalendarViewHolo.setShowWeekNumber(true);
mCalendarViewHolo.setDateTextAppearance(R.style.TextAppearance_WithColorBlue);
@@ -178,7 +295,7 @@
mCalendarViewHolo.setWeekSeparatorLineColor(newWeekSeparatorLineColor);
});
- assertEquals(1, mCalendarViewHolo.getFirstDayOfWeek());
+ assertEquals(Calendar.SUNDAY, mCalendarViewHolo.getFirstDayOfWeek());
assertEquals(4, mCalendarViewHolo.getShownWeekCount());
assertTrue(mCalendarViewHolo.getShowWeekNumber());
assertEquals(R.style.TextAppearance_WithColorBlue,
@@ -196,12 +313,12 @@
assertEquals(newWeekSeparatorLineColor,
mCalendarViewHolo.getWeekSeparatorLineColor());
- instrumentation.runOnMainSync(
+ mInstrumentation.runOnMainSync(
() -> mCalendarViewHolo.setSelectedDateVerticalBar(R.drawable.yellow_fill));
TestUtils.assertAllPixelsOfColor("Selected date vertical bar yellow",
mCalendarViewHolo.getSelectedDateVerticalBar(), 40, 40, true, 0xFFFFFF00, 1, true);
- instrumentation.runOnMainSync(
+ mInstrumentation.runOnMainSync(
() -> mCalendarViewHolo.setSelectedDateVerticalBar(
mActivity.getDrawable(R.drawable.magenta_fill)));
TestUtils.assertAllPixelsOfColor("Selected date vertical bar magenta",
diff --git a/hostsidetests/services/activitymanager/app/src/android/server/app/SlowCreateActivity.java b/tests/tests/widget/src/android/widget/cts/CheckBoxCtsActivity.java
similarity index 68%
copy from hostsidetests/services/activitymanager/app/src/android/server/app/SlowCreateActivity.java
copy to tests/tests/widget/src/android/widget/cts/CheckBoxCtsActivity.java
index 481f5be..9ad0b23 100644
--- a/hostsidetests/services/activitymanager/app/src/android/server/app/SlowCreateActivity.java
+++ b/tests/tests/widget/src/android/widget/cts/CheckBoxCtsActivity.java
@@ -14,17 +14,22 @@
* limitations under the License.
*/
-package android.server.app;
+package android.widget.cts;
import android.app.Activity;
import android.os.Bundle;
+import android.widget.CheckBox;
-public class SlowCreateActivity extends Activity {
+/**
+ * A minimal application for {@link CheckBox} test.
+ */
+public class CheckBoxCtsActivity extends Activity {
+ /**
+ * Called with the activity is first created.
+ */
@Override
- protected void onCreate(Bundle savedInstanceState) {
- try {
- Thread.sleep(2000);
- } catch(InterruptedException e) {}
+ public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ setContentView(R.layout.checkbox_layout);
}
}
diff --git a/tests/tests/widget/src/android/widget/cts/CheckBoxTest.java b/tests/tests/widget/src/android/widget/cts/CheckBoxTest.java
index b3a6e67..ed2ab64 100644
--- a/tests/tests/widget/src/android/widget/cts/CheckBoxTest.java
+++ b/tests/tests/widget/src/android/widget/cts/CheckBoxTest.java
@@ -16,44 +16,184 @@
package android.widget.cts;
-import android.widget.cts.R;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.verifyZeroInteractions;
-
-import org.xmlpull.v1.XmlPullParser;
-
-import android.test.AndroidTestCase;
-import android.util.AttributeSet;
-import android.util.Xml;
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.cts.util.CtsTouchUtils;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.text.TextUtils;
import android.widget.CheckBox;
-public class CheckBoxTest extends AndroidTestCase {
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class CheckBoxTest {
+ private Instrumentation mInstrumentation;
+ private Activity mActivity;
+ private CheckBox mCheckBox;
+
+ @Rule
+ public ActivityTestRule<CheckBoxCtsActivity> mActivityRule
+ = new ActivityTestRule<>(CheckBoxCtsActivity.class);
+
+ @Before
+ public void setup() {
+ mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ mActivity = mActivityRule.getActivity();
+ mCheckBox = (CheckBox) mActivity.findViewById(R.id.check_box);
+ }
+
+ @Test
public void testConstructor() {
- XmlPullParser parser = mContext.getResources().getXml(R.layout.checkbox_layout);
- AttributeSet mAttrSet = Xml.asAttributeSet(parser);
+ new CheckBox(mActivity);
+ new CheckBox(mActivity, null);
+ new CheckBox(mActivity, null, android.R.attr.checkboxStyle);
+ new CheckBox(mActivity, null, 0,
+ android.R.style.Widget_DeviceDefault_CompoundButton_CheckBox);
+ new CheckBox(mActivity, null, 0,
+ android.R.style.Widget_DeviceDefault_Light_CompoundButton_CheckBox);
+ new CheckBox(mActivity, null, 0,
+ android.R.style.Widget_Material_CompoundButton_CheckBox);
+ new CheckBox(mActivity, null, 0,
+ android.R.style.Widget_Material_Light_CompoundButton_CheckBox);
+ }
- new CheckBox(mContext, mAttrSet, 0);
- new CheckBox(mContext, mAttrSet);
- new CheckBox(mContext);
+ @Test(expected=NullPointerException.class)
+ public void testConstructorWithNullContext1() {
+ new CheckBox(null);
+ }
- try {
- new CheckBox(null, null, -1);
- fail("Should throw NullPointerException.");
- } catch (NullPointerException e) {
- // expected, test success.
- }
+ @Test(expected=NullPointerException.class)
+ public void testConstructorWithNullContext2() {
+ new CheckBox(null, null);
+ }
- try {
- new CheckBox(null, null);
- fail("Should throw NullPointerException.");
- } catch (NullPointerException e) {
- // expected, test success.
- }
+ @Test(expected=NullPointerException.class)
+ public void testConstructorWithNullContext3() {
+ new CheckBox(null, null, -1);
+ }
- try {
- new CheckBox(null);
- fail("Should throw NullPointerException.");
- } catch (NullPointerException e) {
- // expected, test success.
- }
+ @Test
+ public void testText() {
+ assertTrue(TextUtils.equals(
+ mActivity.getString(R.string.hello_world), mCheckBox.getText()));
+
+ mInstrumentation.runOnMainSync(() -> mCheckBox.setText("new text"));
+ assertTrue(TextUtils.equals("new text", mCheckBox.getText()));
+
+ mInstrumentation.runOnMainSync(() -> mCheckBox.setText(R.string.text_name));
+ assertTrue(TextUtils.equals(mActivity.getString(R.string.text_name), mCheckBox.getText()));
+ }
+
+ @Test
+ public void testAccessChecked() {
+ final CheckBox.OnCheckedChangeListener mockCheckedChangeListener =
+ mock(CheckBox.OnCheckedChangeListener.class);
+ mCheckBox.setOnCheckedChangeListener(mockCheckedChangeListener);
+ verifyZeroInteractions(mockCheckedChangeListener);
+
+ assertFalse(mCheckBox.isChecked());
+
+ // not checked -> not checked
+ mInstrumentation.runOnMainSync(() -> mCheckBox.setChecked(false));
+ verifyZeroInteractions(mockCheckedChangeListener);
+ assertFalse(mCheckBox.isChecked());
+
+ // not checked -> checked
+ mInstrumentation.runOnMainSync(() -> mCheckBox.setChecked(true));
+ verify(mockCheckedChangeListener, times(1)).onCheckedChanged(mCheckBox, true);
+ assertTrue(mCheckBox.isChecked());
+
+ // checked -> checked
+ mInstrumentation.runOnMainSync(() -> mCheckBox.setChecked(true));
+ verify(mockCheckedChangeListener, times(1)).onCheckedChanged(mCheckBox, true);
+ assertTrue(mCheckBox.isChecked());
+
+ // checked -> not checked
+ mInstrumentation.runOnMainSync(() -> mCheckBox.setChecked(false));
+ verify(mockCheckedChangeListener, times(1)).onCheckedChanged(mCheckBox, false);
+ assertFalse(mCheckBox.isChecked());
+
+ verifyNoMoreInteractions(mockCheckedChangeListener);
+ }
+
+ @Test
+ public void testToggleViaApi() {
+ final CheckBox.OnCheckedChangeListener mockCheckedChangeListener =
+ mock(CheckBox.OnCheckedChangeListener.class);
+ mCheckBox.setOnCheckedChangeListener(mockCheckedChangeListener);
+ verifyZeroInteractions(mockCheckedChangeListener);
+
+ assertFalse(mCheckBox.isChecked());
+
+ // toggle to checked
+ mInstrumentation.runOnMainSync(mCheckBox::toggle);
+ verify(mockCheckedChangeListener, times(1)).onCheckedChanged(mCheckBox, true);
+ assertTrue(mCheckBox.isChecked());
+
+ // toggle to not checked
+ mInstrumentation.runOnMainSync(mCheckBox::toggle);
+ verify(mockCheckedChangeListener, times(1)).onCheckedChanged(mCheckBox, false);
+ assertFalse(mCheckBox.isChecked());
+
+ verifyNoMoreInteractions(mockCheckedChangeListener);
+ }
+
+ @Test
+ public void testToggleViaEmulatedTap() {
+ final CheckBox.OnCheckedChangeListener mockCheckedChangeListener =
+ mock(CheckBox.OnCheckedChangeListener.class);
+ mCheckBox.setOnCheckedChangeListener(mockCheckedChangeListener);
+ verifyZeroInteractions(mockCheckedChangeListener);
+
+ assertFalse(mCheckBox.isChecked());
+
+ // tap to checked
+ CtsTouchUtils.emulateTapOnViewCenter(mInstrumentation, mCheckBox);
+ verify(mockCheckedChangeListener, times(1)).onCheckedChanged(mCheckBox, true);
+ assertTrue(mCheckBox.isChecked());
+
+ // tap to not checked
+ CtsTouchUtils.emulateTapOnViewCenter(mInstrumentation, mCheckBox);
+ verify(mockCheckedChangeListener, times(1)).onCheckedChanged(mCheckBox, false);
+ assertFalse(mCheckBox.isChecked());
+
+ verifyNoMoreInteractions(mockCheckedChangeListener);
+ }
+
+ @Test
+ public void testToggleViaPerformClick() {
+ final CheckBox.OnCheckedChangeListener mockCheckedChangeListener =
+ mock(CheckBox.OnCheckedChangeListener.class);
+ mCheckBox.setOnCheckedChangeListener(mockCheckedChangeListener);
+ verifyZeroInteractions(mockCheckedChangeListener);
+
+ assertFalse(mCheckBox.isChecked());
+
+ // click to checked
+ mInstrumentation.runOnMainSync(mCheckBox::performClick);
+ verify(mockCheckedChangeListener, times(1)).onCheckedChanged(mCheckBox, true);
+ assertTrue(mCheckBox.isChecked());
+
+ // click to not checked
+ mInstrumentation.runOnMainSync(mCheckBox::performClick);
+ verify(mockCheckedChangeListener, times(1)).onCheckedChanged(mCheckBox, false);
+ assertFalse(mCheckBox.isChecked());
+
+ verifyNoMoreInteractions(mockCheckedChangeListener);
}
}
diff --git a/tests/tests/widget/src/android/widget/cts/CheckedTextViewCtsActivity.java b/tests/tests/widget/src/android/widget/cts/CheckedTextViewCtsActivity.java
index 52bef54..066697a 100644
--- a/tests/tests/widget/src/android/widget/cts/CheckedTextViewCtsActivity.java
+++ b/tests/tests/widget/src/android/widget/cts/CheckedTextViewCtsActivity.java
@@ -16,13 +16,12 @@
package android.widget.cts;
-import android.widget.cts.R;
-
import android.app.Activity;
import android.os.Bundle;
+import android.widget.CheckedTextView;
/**
- * A minimal application for CheckedTextView test.
+ * A minimal application for {@link CheckedTextView} test.
*/
public class CheckedTextViewCtsActivity extends Activity {
/**
diff --git a/tests/tests/widget/src/android/widget/cts/CheckedTextViewTest.java b/tests/tests/widget/src/android/widget/cts/CheckedTextViewTest.java
index 039ca70..d564df3 100644
--- a/tests/tests/widget/src/android/widget/cts/CheckedTextViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/CheckedTextViewTest.java
@@ -16,17 +16,25 @@
package android.widget.cts;
-import android.os.Parcelable;
-import android.widget.cts.R;
-
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
import android.app.Activity;
import android.app.Instrumentation;
import android.content.Context;
-import android.content.res.Resources;
+import android.content.res.ColorStateList;
+import android.cts.util.WidgetTestUtils;
import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
-import android.test.ActivityInstrumentationTestCase2;
+import android.os.Parcelable;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
import android.util.AttributeSet;
import android.util.StateSet;
import android.view.View;
@@ -35,84 +43,91 @@
import android.widget.CheckedTextView;
import android.widget.ListAdapter;
import android.widget.ListView;
+import android.widget.cts.util.TestUtils;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
import java.util.Arrays;
-public class CheckedTextViewTest extends
- ActivityInstrumentationTestCase2<CheckedTextViewCtsActivity> {
- private Resources mResources;
- private Activity mActivity;
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class CheckedTextViewTest {
private Instrumentation mInstrumentation;
+ private Activity mActivity;
+ private ListView mListView;
+ private CheckedTextView mCheckedTextView;
+ private Parcelable mState;
- public CheckedTextViewTest() {
- super("android.widget.cts", CheckedTextViewCtsActivity.class);
+ @Rule
+ public ActivityTestRule<CheckedTextViewCtsActivity> mActivityRule
+ = new ActivityTestRule<>(CheckedTextViewCtsActivity.class);
+
+ @Before
+ public void setup() {
+ mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ mActivity = mActivityRule.getActivity();
+ mListView = (ListView) mActivity.findViewById(R.id.checkedtextview_listview);
+ mCheckedTextView = (CheckedTextView) mActivity.findViewById(R.id.checkedtextview_test);
}
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mActivity = getActivity();
- mInstrumentation = getInstrumentation();
- mResources = mActivity.getResources();
- }
-
+ @Test
public void testConstructor() {
- new MockCheckedTextView(mActivity, null, 0);
- new MockCheckedTextView(mActivity, null);
- new MockCheckedTextView(mActivity);
-
- try {
- new MockCheckedTextView(null, null, -1);
- fail("Should throw NullPointerException.");
- } catch (NullPointerException e) {
- // expected, test success.
- }
-
- try {
- new MockCheckedTextView(null, null);
- fail("Should throw NullPointerException.");
- } catch (NullPointerException e) {
- // expected, test success.
- }
-
- try {
- new MockCheckedTextView(null);
- fail("Should throw NullPointerException.");
- } catch (NullPointerException e) {
- // expected, test success.
- }
+ new CheckedTextView(mActivity);
+ new CheckedTextView(mActivity, null);
+ new CheckedTextView(mActivity, null, android.R.attr.checkedTextViewStyle);
+ new CheckedTextView(mActivity, null, 0,
+ android.R.style.Widget_DeviceDefault_CheckedTextView);
+ new CheckedTextView(mActivity, null, 0,
+ android.R.style.Widget_DeviceDefault_Light_CheckedTextView);
+ new CheckedTextView(mActivity, null, 0,
+ android.R.style.Widget_Material_CheckedTextView);
+ new CheckedTextView(mActivity, null, 0,
+ android.R.style.Widget_Material_Light_CheckedTextView);
}
+ @Test(expected=NullPointerException.class)
+ public void testConstructorWithNullContext1() {
+ new CheckedTextView(null);
+ }
+
+ @Test(expected=NullPointerException.class)
+ public void testConstructorWithNullContext2() {
+ new CheckedTextView(null, null);
+ }
+
+ @Test(expected=NullPointerException.class)
+ public void testConstructorWithNullContext3() {
+ new CheckedTextView(null, null, -1);
+ }
+
+ @Test
public void testChecked() {
- final ListView lv = (ListView) mActivity.findViewById(R.id.checkedtextview_listview);
+ mInstrumentation.runOnMainSync(() -> {
+ mListView.setAdapter(new CheckedTextViewAdapter());
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- lv.setAdapter(new CheckedTextViewAdapter());
-
- lv.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
- lv.setItemChecked(1, true);
- }
+ mListView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
+ mListView.setItemChecked(1, true);
});
mInstrumentation.waitForIdleSync();
- assertEquals(1, lv.getCheckedItemPosition());
- assertTrue(lv.isItemChecked(1));
- assertFalse(lv.isItemChecked(0));
+ assertEquals(1, mListView.getCheckedItemPosition());
+ assertTrue(mListView.isItemChecked(1));
+ assertFalse(mListView.isItemChecked(0));
- ListAdapter adapter = lv.getAdapter();
- CheckedTextView view0 = (CheckedTextView) adapter.getView(0, null, null);
- CheckedTextView view1 = (CheckedTextView) adapter.getView(1, null, null);
- CheckedTextView view2 = (CheckedTextView) adapter.getView(2, null, null);
+ final ListAdapter adapter = mListView.getAdapter();
+ final CheckedTextView view0 = (CheckedTextView) adapter.getView(0, null, null);
+ final CheckedTextView view1 = (CheckedTextView) adapter.getView(1, null, null);
+ final CheckedTextView view2 = (CheckedTextView) adapter.getView(2, null, null);
assertFalse(view0.isChecked());
assertTrue(view1.isChecked());
assertFalse(view2.isChecked());
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- lv.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
- lv.setItemChecked(2, true);
- }
+ mInstrumentation.runOnMainSync(() -> {
+ mListView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
+ mListView.setItemChecked(2, true);
});
mInstrumentation.waitForIdleSync();
assertFalse(view0.isChecked());
@@ -127,185 +142,228 @@
assertFalse(view2.isChecked());
}
+ @Test
public void testToggle() {
- CheckedTextView checkedTextView = new MockCheckedTextView(mActivity);
- assertFalse(checkedTextView.isChecked());
+ assertFalse(mCheckedTextView.isChecked());
- checkedTextView.toggle();
- assertTrue(checkedTextView.isChecked());
+ mInstrumentation.runOnMainSync(() -> mCheckedTextView.toggle());
+ assertTrue(mCheckedTextView.isChecked());
- checkedTextView.toggle();
- assertFalse(checkedTextView.isChecked());
+ mInstrumentation.runOnMainSync(() -> mCheckedTextView.toggle());
+ assertFalse(mCheckedTextView.isChecked());
- checkedTextView.setChecked(true);
- checkedTextView.toggle();
- assertFalse(checkedTextView.isChecked());
+ mInstrumentation.runOnMainSync(() -> {
+ mCheckedTextView.setChecked(true);
+ mCheckedTextView.toggle();
+ });
+ assertFalse(mCheckedTextView.isChecked());
}
+ @Test
public void testDrawableStateChanged() {
MockCheckedTextView checkedTextView = new MockCheckedTextView(mActivity);
- checkedTextView.reset();
assertFalse(checkedTextView.hasDrawableStateChanged());
checkedTextView.refreshDrawableState();
assertTrue(checkedTextView.hasDrawableStateChanged());
}
+ @Test
public void testSetPadding() {
- final CheckedTextView lv
- = (CheckedTextView) mActivity.findViewById(R.id.checkedtextview_test);
- assertNotNull(lv);
-
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- lv.setPadding(1, 2, 3, 4);
- lv.requestLayout();
- }
+ mInstrumentation.runOnMainSync(() -> {
+ mListView.setPadding(1, 2, 3, 4);
+ mListView.requestLayout();
});
mInstrumentation.waitForIdleSync();
- int origTop = lv.getPaddingTop();
- int origBottom = lv.getPaddingBottom();
- int origLeft = lv.getPaddingLeft();
- int origRight = lv.getPaddingRight();
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- lv.setPadding(10, 20, 30, 40);
- lv.requestLayout();
- }
+ final int origTop = mListView.getPaddingTop();
+ final int origBottom = mListView.getPaddingBottom();
+ final int origLeft = mListView.getPaddingLeft();
+ final int origRight = mListView.getPaddingRight();
+
+ mInstrumentation.runOnMainSync(() -> {
+ mListView.setPadding(10, 20, 30, 40);
+ mListView.requestLayout();
});
mInstrumentation.waitForIdleSync();
- assertTrue(origTop < lv.getPaddingTop());
- assertTrue(origBottom < lv.getPaddingBottom());
- assertTrue(origLeft < lv.getPaddingLeft());
- assertTrue(origRight < lv.getPaddingRight());
+
+ assertTrue(origTop < mListView.getPaddingTop());
+ assertTrue(origBottom < mListView.getPaddingBottom());
+ assertTrue(origLeft < mListView.getPaddingLeft());
+ assertTrue(origRight < mListView.getPaddingRight());
}
private void cleanUpForceLayoutFlags(View view) {
if (view != null) {
- view.layout(0, 0, 0, 0);
+ mInstrumentation.runOnMainSync(() -> view.layout(0, 0, 0, 0));
assertFalse(view.isLayoutRequested());
}
}
+ @Test
public void testSetCheckMarkDrawableByDrawable() {
- CheckedTextView checkedTextView;
int basePaddingRight = 10;
// set drawable when checkedTextView is GONE
- checkedTextView = new MockCheckedTextView(mActivity);
- checkedTextView.setVisibility(View.GONE);
- Drawable firstDrawable = mResources.getDrawable(R.drawable.scenery);
+ mInstrumentation.runOnMainSync(() -> mCheckedTextView.setVisibility(View.GONE));
+ final Drawable firstDrawable = mActivity.getDrawable(R.drawable.scenery);
firstDrawable.setVisible(true, false);
assertEquals(StateSet.WILD_CARD, firstDrawable.getState());
- cleanUpForceLayoutFlags(checkedTextView);
+ cleanUpForceLayoutFlags(mCheckedTextView);
- checkedTextView.setCheckMarkDrawable(firstDrawable);
- assertEquals(firstDrawable.getIntrinsicWidth(), checkedTextView.getPaddingRight());
+ mInstrumentation.runOnMainSync(() -> mCheckedTextView.setCheckMarkDrawable(firstDrawable));
+ assertEquals(firstDrawable.getIntrinsicWidth(), mCheckedTextView.getPaddingRight());
assertFalse(firstDrawable.isVisible());
- assertTrue(Arrays.equals(checkedTextView.getDrawableState(), firstDrawable.getState()));
- assertTrue(checkedTextView.isLayoutRequested());
+ assertTrue(Arrays.equals(mCheckedTextView.getDrawableState(), firstDrawable.getState()));
+ assertTrue(mCheckedTextView.isLayoutRequested());
+
+ mInstrumentation.runOnMainSync(() -> mCheckedTextView.setCheckMarkDrawable(null));
// update drawable when checkedTextView is VISIBLE
- checkedTextView = new MockCheckedTextView(mActivity);
- checkedTextView.setVisibility(View.VISIBLE);
- checkedTextView.setPadding(0, 0, basePaddingRight, 0);
- Drawable secondDrawable = mResources.getDrawable(R.drawable.pass);
+ mInstrumentation.runOnMainSync(() -> {
+ mCheckedTextView.setVisibility(View.VISIBLE);
+ mCheckedTextView.setPadding(0, 0, basePaddingRight, 0);
+ });
+ final Drawable secondDrawable = mActivity.getDrawable(R.drawable.pass);
secondDrawable.setVisible(true, false);
assertEquals(StateSet.WILD_CARD, secondDrawable.getState());
- cleanUpForceLayoutFlags(checkedTextView);
+ cleanUpForceLayoutFlags(mCheckedTextView);
- checkedTextView.setCheckMarkDrawable(secondDrawable);
+ mInstrumentation.runOnMainSync(() -> mCheckedTextView.setCheckMarkDrawable(secondDrawable));
assertEquals(secondDrawable.getIntrinsicWidth() + basePaddingRight,
- checkedTextView.getPaddingRight());
+ mCheckedTextView.getPaddingRight());
assertTrue(secondDrawable.isVisible());
- assertTrue(Arrays.equals(checkedTextView.getDrawableState(), secondDrawable.getState()));
- assertTrue(checkedTextView.isLayoutRequested());
+ assertTrue(Arrays.equals(mCheckedTextView.getDrawableState(), secondDrawable.getState()));
+ assertTrue(mCheckedTextView.isLayoutRequested());
- cleanUpForceLayoutFlags(checkedTextView);
- checkedTextView.setCheckMarkDrawable(null);
- assertEquals(basePaddingRight, checkedTextView.getPaddingRight());
- assertTrue(checkedTextView.isLayoutRequested());
+ cleanUpForceLayoutFlags(mCheckedTextView);
+ mInstrumentation.runOnMainSync(() -> mCheckedTextView.setCheckMarkDrawable(null));
+ assertEquals(basePaddingRight, mCheckedTextView.getPaddingRight());
+ assertTrue(mCheckedTextView.isLayoutRequested());
}
+ @Test
public void testSetCheckMarkDrawableById() {
- CheckedTextView checkedTextView;
int basePaddingRight = 10;
// set drawable
- checkedTextView = new MockCheckedTextView(mActivity);
- checkedTextView.setPadding(0, 0, basePaddingRight, 0);
- Drawable firstDrawable = mResources.getDrawable(R.drawable.scenery);
- cleanUpForceLayoutFlags(checkedTextView);
+ mInstrumentation.runOnMainSync(
+ () -> mCheckedTextView.setPadding(0, 0, basePaddingRight, 0));
+ Drawable firstDrawable = mActivity.getDrawable(R.drawable.scenery);
+ cleanUpForceLayoutFlags(mCheckedTextView);
- checkedTextView.setCheckMarkDrawable(R.drawable.scenery);
+ mInstrumentation.runOnMainSync(
+ () -> mCheckedTextView.setCheckMarkDrawable(R.drawable.scenery));
assertEquals(firstDrawable.getIntrinsicWidth() + basePaddingRight,
- checkedTextView.getPaddingRight());
- assertTrue(checkedTextView.isLayoutRequested());
+ mCheckedTextView.getPaddingRight());
+ assertTrue(mCheckedTextView.isLayoutRequested());
// set the same drawable again
- cleanUpForceLayoutFlags(checkedTextView);
- checkedTextView.setCheckMarkDrawable(R.drawable.scenery);
+ cleanUpForceLayoutFlags(mCheckedTextView);
+ mInstrumentation.runOnMainSync(
+ () -> mCheckedTextView.setCheckMarkDrawable(R.drawable.scenery));
assertEquals(firstDrawable.getIntrinsicWidth() + basePaddingRight,
- checkedTextView.getPaddingRight());
- assertFalse(checkedTextView.isLayoutRequested());
+ mCheckedTextView.getPaddingRight());
+ assertFalse(mCheckedTextView.isLayoutRequested());
// update drawable
- Drawable secondDrawable = mResources.getDrawable(R.drawable.pass);
- checkedTextView.setCheckMarkDrawable(secondDrawable);
+ final Drawable secondDrawable = mActivity.getDrawable(R.drawable.pass);
+ mInstrumentation.runOnMainSync(() -> mCheckedTextView.setCheckMarkDrawable(secondDrawable));
assertEquals(secondDrawable.getIntrinsicWidth() + basePaddingRight,
- checkedTextView.getPaddingRight());
- assertTrue(checkedTextView.isLayoutRequested());
+ mCheckedTextView.getPaddingRight());
+ assertTrue(mCheckedTextView.isLayoutRequested());
+
+ mInstrumentation.runOnMainSync(() -> mCheckedTextView.setCheckMarkDrawable(null));
// resId is 0
- checkedTextView = new MockCheckedTextView(mActivity);
- checkedTextView.setPadding(0, 0, basePaddingRight, 0);
- cleanUpForceLayoutFlags(checkedTextView);
+ mInstrumentation.runOnMainSync(
+ () -> mCheckedTextView.setPadding(0, 0, basePaddingRight, 0));
+ cleanUpForceLayoutFlags(mCheckedTextView);
- checkedTextView.setCheckMarkDrawable(0);
- assertEquals(basePaddingRight, checkedTextView.getPaddingRight());
- assertFalse(checkedTextView.isLayoutRequested());
+ mInstrumentation.runOnMainSync(() -> mCheckedTextView.setCheckMarkDrawable(0));
+ assertEquals(basePaddingRight, mCheckedTextView.getPaddingRight());
+ assertFalse(mCheckedTextView.isLayoutRequested());
}
+ @Test
public void testSetCheckMarkByMixedTypes() {
- CheckedTextView checkedTextView = new MockCheckedTextView(mActivity);
- cleanUpForceLayoutFlags(checkedTextView);
+ cleanUpForceLayoutFlags(mCheckedTextView);
// Specifically test for b/22626247 (AOSP issue 180455).
- checkedTextView.setCheckMarkDrawable(R.drawable.scenery);
- checkedTextView.setCheckMarkDrawable(null);
- checkedTextView.setCheckMarkDrawable(R.drawable.scenery);
- assertNotNull(checkedTextView.getCheckMarkDrawable());
+ mInstrumentation.runOnMainSync(() -> {
+ mCheckedTextView.setCheckMarkDrawable(R.drawable.scenery);
+ mCheckedTextView.setCheckMarkDrawable(null);
+ mCheckedTextView.setCheckMarkDrawable(R.drawable.scenery);
+ });
+ assertNotNull(mCheckedTextView.getCheckMarkDrawable());
}
+ @Test
public void testAccessInstanceState() {
- CheckedTextView checkedTextView = new MockCheckedTextView(mActivity);
- Parcelable state;
+ assertFalse(mCheckedTextView.isChecked());
+ assertFalse(mCheckedTextView.getFreezesText());
- assertFalse(checkedTextView.isChecked());
- assertFalse(checkedTextView.getFreezesText());
+ mInstrumentation.runOnMainSync(() -> mState = mCheckedTextView.onSaveInstanceState());
+ assertNotNull(mState);
+ assertFalse(mCheckedTextView.getFreezesText());
- state = checkedTextView.onSaveInstanceState();
- assertNotNull(state);
- assertFalse(checkedTextView.getFreezesText());
+ mInstrumentation.runOnMainSync(() -> mCheckedTextView.setChecked(true));
- checkedTextView.setChecked(true);
-
- checkedTextView.onRestoreInstanceState(state);
- assertFalse(checkedTextView.isChecked());
- assertTrue(checkedTextView.isLayoutRequested());
+ mInstrumentation.runOnMainSync(() -> mCheckedTextView.onRestoreInstanceState(mState));
+ assertFalse(mCheckedTextView.isChecked());
+ assertTrue(mCheckedTextView.isLayoutRequested());
}
- public void testOnDraw() {
- // Do not test. Implementation details.
- }
+ @Test
+ public void testCheckMarkTinting() {
+ mInstrumentation.runOnMainSync(() -> mCheckedTextView.setChecked(true));
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mCheckedTextView,
+ () -> mCheckedTextView.setCheckMarkDrawable(R.drawable.icon_red));
- public void testOnCreateDrawableState() {
- // Do not test. Implementation details.
+ Drawable checkMark = mCheckedTextView.getCheckMarkDrawable();
+ TestUtils.assertAllPixelsOfColor("Initial state is red", checkMark,
+ checkMark.getBounds().width(), checkMark.getBounds().height(), false,
+ Color.RED, 1, true);
+
+ // With SRC_IN we're expecting the translucent tint color to "take over" the
+ // original red checkmark.
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mCheckedTextView, () -> {
+ mCheckedTextView.setCheckMarkTintMode(PorterDuff.Mode.SRC_IN);
+ mCheckedTextView.setCheckMarkTintList(ColorStateList.valueOf(0x8000FF00));
+ });
+
+ assertEquals(PorterDuff.Mode.SRC_IN, mCheckedTextView.getCheckMarkTintMode());
+ assertEquals(0x8000FF00, mCheckedTextView.getCheckMarkTintList().getDefaultColor());
+ checkMark = mCheckedTextView.getCheckMarkDrawable();
+ TestUtils.assertAllPixelsOfColor("Expected 50% green", checkMark,
+ checkMark.getIntrinsicWidth(), checkMark.getIntrinsicHeight(), false,
+ 0x8000FF00, 1, true);
+
+ // With SRC_OVER we're expecting the translucent tint color to be drawn on top
+ // of the original red checkmark, creating a composite color fill as the result.
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mCheckedTextView,
+ () -> mCheckedTextView.setCheckMarkTintMode(PorterDuff.Mode.SRC_OVER));
+
+ assertEquals(PorterDuff.Mode.SRC_OVER, mCheckedTextView.getCheckMarkTintMode());
+ assertEquals(0x8000FF00, mCheckedTextView.getCheckMarkTintList().getDefaultColor());
+ checkMark = mCheckedTextView.getCheckMarkDrawable();
+ TestUtils.assertAllPixelsOfColor("Expected 50% green over full red", checkMark,
+ checkMark.getIntrinsicWidth(), checkMark.getIntrinsicHeight(), false,
+ TestUtils.compositeColors(0x8000FF00, Color.RED), 1, true);
+
+ // Switch to a different color for the underlying checkmark and verify that the
+ // currently configured tinting (50% green overlay) is still respected
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mCheckedTextView,
+ () -> mCheckedTextView.setCheckMarkDrawable(R.drawable.icon_yellow));
+ assertEquals(PorterDuff.Mode.SRC_OVER, mCheckedTextView.getCheckMarkTintMode());
+ assertEquals(0x8000FF00, mCheckedTextView.getCheckMarkTintList().getDefaultColor());
+ checkMark = mCheckedTextView.getCheckMarkDrawable();
+ TestUtils.assertAllPixelsOfColor("Expected 50% green over full yellow", checkMark,
+ checkMark.getIntrinsicWidth(), checkMark.getIntrinsicHeight(), false,
+ TestUtils.compositeColors(0x8000FF00, Color.YELLOW), 1, true);
}
private static final class MockCheckedTextView extends CheckedTextView {
- private boolean mHasRefreshDrawableState = false;
private boolean mHasDrawableStateChanged = false;
public MockCheckedTextView(Context context) {
@@ -320,10 +378,6 @@
super(context, attrs, defStyle);
}
- public static int[] getSuperViewStateSet() {
- return ENABLED_STATE_SET;
- }
-
@Override
protected void drawableStateChanged() {
super.drawableStateChanged();
@@ -340,24 +394,9 @@
super.onDraw(canvas);
}
- @Override
- public void refreshDrawableState() {
- mHasRefreshDrawableState = true;
- super.refreshDrawableState();
- }
-
- public boolean hasRefreshDrawableState() {
- return mHasRefreshDrawableState;
- }
-
public boolean hasDrawableStateChanged() {
return mHasDrawableStateChanged;
}
-
- public void reset() {
- mHasRefreshDrawableState = false;
- mHasDrawableStateChanged = false;
- }
}
private class CheckedTextViewAdapter extends BaseAdapter {
diff --git a/tests/tests/widget/src/android/widget/cts/ChronometerCtsActivity.java b/tests/tests/widget/src/android/widget/cts/ChronometerCtsActivity.java
index 7925b49..affe740 100644
--- a/tests/tests/widget/src/android/widget/cts/ChronometerCtsActivity.java
+++ b/tests/tests/widget/src/android/widget/cts/ChronometerCtsActivity.java
@@ -16,11 +16,11 @@
package android.widget.cts;
-import android.widget.cts.R;
import android.app.Activity;
import android.os.Bundle;
import android.view.ViewGroup;
import android.widget.Chronometer;
+import android.widget.cts.R;
public class ChronometerCtsActivity extends Activity {
diff --git a/tests/tests/widget/src/android/widget/cts/ChronometerTest.java b/tests/tests/widget/src/android/widget/cts/ChronometerTest.java
index ff1435f..1d5854a 100644
--- a/tests/tests/widget/src/android/widget/cts/ChronometerTest.java
+++ b/tests/tests/widget/src/android/widget/cts/ChronometerTest.java
@@ -16,28 +16,52 @@
package android.widget.cts;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
-import android.test.ActivityInstrumentationTestCase2;
-import android.test.UiThreadTest;
+import android.app.Instrumentation;
+import android.content.Context;
+import android.os.SystemClock;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.view.ContextThemeWrapper;
import android.widget.Chronometer;
-import android.widget.Chronometer.OnChronometerTickListener;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
/**
* Test {@link Chronometer}.
*/
-public class ChronometerTest extends ActivityInstrumentationTestCase2<ChronometerCtsActivity> {
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ChronometerTest {
+ private Instrumentation mInstrumentation;
private ChronometerCtsActivity mActivity;
- public ChronometerTest() {
- super("android.widget.cts", ChronometerCtsActivity.class);
+
+ @Rule
+ public ActivityTestRule<ChronometerCtsActivity> mActivityRule
+ = new ActivityTestRule<>(ChronometerCtsActivity.class);
+
+ @Before
+ public void setup() {
+ mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ mActivity = mActivityRule.getActivity();
}
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mActivity = getActivity();
- }
-
- @UiThreadTest
+ @Test
public void testConstructor() {
new Chronometer(mActivity);
@@ -46,36 +70,48 @@
new Chronometer(mActivity, null, 0);
}
- @UiThreadTest
+ @Test
+ public void testConstructorFromAttr() {
+ final Context context = new ContextThemeWrapper(mActivity, R.style.ChronometerAwareTheme);
+ final Chronometer chronometer = new Chronometer(context, null, R.attr.chronometerStyle);
+ assertTrue(chronometer.isCountDown());
+ assertEquals(mActivity.getString(R.string.chronometer_format), chronometer.getFormat());
+ }
+
+ @Test
+ public void testConstructorFromStyle() {
+ final Chronometer chronometer = new Chronometer(mActivity, null, 0,
+ R.style.ChronometerStyle);
+ assertTrue(chronometer.isCountDown());
+ assertEquals(mActivity.getString(R.string.chronometer_format), chronometer.getFormat());
+ }
+
+ @Test
public void testAccessBase() {
Chronometer chronometer = mActivity.getChronometer();
CharSequence oldText = chronometer.getText();
- int expected = 100000;
- chronometer.setBase(expected);
- assertEquals(expected, chronometer.getBase());
+ mInstrumentation.runOnMainSync(() -> chronometer.setBase(100000));
+ assertEquals(100000, chronometer.getBase());
assertNotSame(oldText, chronometer.getText());
- expected = 100;
oldText = chronometer.getText();
- chronometer.setBase(expected);
- assertEquals(expected, chronometer.getBase());
+ mInstrumentation.runOnMainSync(() -> chronometer.setBase(100));
+ assertEquals(100, chronometer.getBase());
assertNotSame(oldText, chronometer.getText());
- expected = -1;
oldText = chronometer.getText();
- chronometer.setBase(expected);
- assertEquals(expected, chronometer.getBase());
+ mInstrumentation.runOnMainSync(() -> chronometer.setBase(-1));
+ assertEquals(-1, chronometer.getBase());
assertNotSame(oldText, chronometer.getText());
- expected = Integer.MAX_VALUE;
oldText = chronometer.getText();
- chronometer.setBase(expected);
- assertEquals(expected, chronometer.getBase());
+ mInstrumentation.runOnMainSync(() -> chronometer.setBase(Integer.MAX_VALUE));
+ assertEquals(Integer.MAX_VALUE, chronometer.getBase());
assertNotSame(oldText, chronometer.getText());
}
- @UiThreadTest
+ @Test
public void testAccessFormat() {
Chronometer chronometer = mActivity.getChronometer();
String expected = "header-%S-trail";
@@ -83,85 +119,106 @@
chronometer.setFormat(expected);
assertEquals(expected, chronometer.getFormat());
- chronometer.start();
+ mInstrumentation.runOnMainSync(() -> chronometer.start());
String text = chronometer.getText().toString();
assertTrue(text.startsWith("header"));
assertTrue(text.endsWith("trail"));
}
- public void testFoo() {
- // Do not test these APIs. They are callbacks which:
- // 1. The callback machanism has been tested in super class
- // 2. The functionality is implmentation details, no need to test
- }
-
- public void testStartAndStop() throws Throwable {
+ @Test
+ @LargeTest
+ public void testStartAndStop() {
final Chronometer chronometer = mActivity.getChronometer();
// we will check the text is really updated every 1000ms after start,
// so we need sleep a moment to wait wait this time. The sleep code shouldn't
// in the same thread with UI, that's why we use runOnMainSync here.
- runTestOnUiThread(new Runnable() {
- public void run() {
- // the text will update immediately when call start.
- CharSequence expected = chronometer.getText();
- chronometer.start();
- assertNotSame(expected, chronometer.getText());
- }
+ mInstrumentation.runOnMainSync(() -> {
+ // the text will update immediately when call start.
+ final CharSequence valueBeforeStart = chronometer.getText();
+ chronometer.start();
+ assertNotSame(valueBeforeStart, chronometer.getText());
});
- getInstrumentation().waitForIdleSync();
+ mInstrumentation.waitForIdleSync();
+
CharSequence expected = chronometer.getText();
- Thread.sleep(1500);
+ SystemClock.sleep(1500);
assertFalse(expected.equals(chronometer.getText()));
// we will check the text is really NOT updated anymore every 1000ms after stop,
// so we need sleep a moment to wait wait this time. The sleep code shouldn't
// in the same thread with UI, that's why we use runOnMainSync here.
- runTestOnUiThread(new Runnable() {
- public void run() {
- // the text will never be updated when call stop.
- CharSequence expected = chronometer.getText();
- chronometer.stop();
- assertSame(expected, chronometer.getText());
- }
+ mInstrumentation.runOnMainSync(() -> {
+ // the text will never be updated when call stop.
+ final CharSequence valueBeforeStop = chronometer.getText();
+ chronometer.stop();
+ assertSame(valueBeforeStop, chronometer.getText());
});
- getInstrumentation().waitForIdleSync();
+ mInstrumentation.waitForIdleSync();
+
expected = chronometer.getText();
- Thread.sleep(1500);
+ SystemClock.sleep(1500);
assertTrue(expected.equals(chronometer.getText()));
}
- public void testAccessOnChronometerTickListener() throws Throwable {
+ @Test
+ @LargeTest
+ public void testAccessOnChronometerTickListener() {
final Chronometer chronometer = mActivity.getChronometer();
- final MockOnChronometerTickListener listener = new MockOnChronometerTickListener();
+ final Chronometer.OnChronometerTickListener mockTickListener =
+ mock(Chronometer.OnChronometerTickListener.class);
- runTestOnUiThread(new Runnable() {
- public void run() {
- chronometer.setOnChronometerTickListener(listener);
- chronometer.start();
- }
+ mInstrumentation.runOnMainSync(() -> {
+ chronometer.setOnChronometerTickListener(mockTickListener);
+ chronometer.start();
});
- getInstrumentation().waitForIdleSync();
- assertEquals(listener, chronometer.getOnChronometerTickListener());
- assertTrue(listener.hasCalledOnChronometerTick());
- listener.reset();
- Thread.sleep(1500);
- assertTrue(listener.hasCalledOnChronometerTick());
+ mInstrumentation.waitForIdleSync();
+
+ assertEquals(mockTickListener, chronometer.getOnChronometerTickListener());
+ verify(mockTickListener, atLeastOnce()).onChronometerTick(chronometer);
+
+ reset(mockTickListener);
+ SystemClock.sleep(1500);
+ verify(mockTickListener, atLeastOnce()).onChronometerTick(chronometer);
}
- private static class MockOnChronometerTickListener implements OnChronometerTickListener {
- private boolean mCalledOnChronometerTick = false;
+ @Test
+ @LargeTest
+ public void testCountDown() {
+ final Chronometer chronometer = mActivity.getChronometer();
+ final Chronometer.OnChronometerTickListener mockTickListener =
+ mock(Chronometer.OnChronometerTickListener.class);
- public void onChronometerTick(Chronometer chronometer) {
- mCalledOnChronometerTick = true;
- }
+ mInstrumentation.runOnMainSync(() -> {
+ chronometer.setCountDown(true);
+ chronometer.setOnChronometerTickListener(mockTickListener);
+ chronometer.start();
+ });
+ mInstrumentation.waitForIdleSync();
- public boolean hasCalledOnChronometerTick() {
- return mCalledOnChronometerTick;
- }
+ assertTrue(chronometer.isCountDown());
- public void reset() {
- mCalledOnChronometerTick = false;
- }
+ SystemClock.sleep(5000);
+ verify(mockTickListener, atLeastOnce()).onChronometerTick(chronometer);
+ }
+
+ @Test
+ @LargeTest
+ public void testCountUp() {
+ final Chronometer chronometer = mActivity.getChronometer();
+ final Chronometer.OnChronometerTickListener mockTickListener =
+ mock(Chronometer.OnChronometerTickListener.class);
+
+ mInstrumentation.runOnMainSync(() -> {
+ chronometer.setCountDown(false);
+ chronometer.setOnChronometerTickListener(mockTickListener);
+ chronometer.start();
+ });
+ mInstrumentation.waitForIdleSync();
+
+ assertFalse(chronometer.isCountDown());
+
+ SystemClock.sleep(5000);
+ verify(mockTickListener, atLeastOnce()).onChronometerTick(chronometer);
}
}
diff --git a/tests/tests/widget/src/android/widget/cts/CompoundButtonTest.java b/tests/tests/widget/src/android/widget/cts/CompoundButtonTest.java
index 34d94a5..505a9b0 100644
--- a/tests/tests/widget/src/android/widget/cts/CompoundButtonTest.java
+++ b/tests/tests/widget/src/android/widget/cts/CompoundButtonTest.java
@@ -16,45 +16,65 @@
package android.widget.cts;
-import android.content.res.ColorStateList;
-import android.graphics.Color;
-import android.graphics.ColorFilter;
-import android.graphics.PorterDuff;
-import android.view.LayoutInflater;
-import android.widget.ToggleButton;
-import android.widget.cts.R;
-
-
-import org.xmlpull.v1.XmlPullParser;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
import android.content.Context;
+import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.PorterDuff;
import android.graphics.Rect;
+import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Parcelable;
-import android.test.AndroidTestCase;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
import android.util.AttributeSet;
import android.util.StateSet;
import android.util.Xml;
import android.view.Gravity;
+import android.view.LayoutInflater;
import android.view.View;
-import android.view.View.OnClickListener;
import android.widget.CompoundButton;
-import android.widget.CompoundButton.OnCheckedChangeListener;
+import android.widget.ToggleButton;
+import android.widget.cts.util.TestUtils;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.xmlpull.v1.XmlPullParser;
/**
* Test {@link CompoundButton}.
*/
-public class CompoundButtonTest extends AndroidTestCase {
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class CompoundButtonTest {
+ private Context mContext;
private Resources mResources;
- @Override
- protected void setUp() throws Exception {
- super.setUp();
+ @Before
+ public void setup() {
+ mContext = InstrumentationRegistry.getTargetContext();
mResources = mContext.getResources();
}
+ @Test
public void testConstructor() {
XmlPullParser parser = mContext.getResources().getXml(R.layout.togglebutton_layout);
AttributeSet mAttrSet = Xml.asAttributeSet(parser);
@@ -62,71 +82,66 @@
new MockCompoundButton(mContext, mAttrSet, 0);
new MockCompoundButton(mContext, mAttrSet);
new MockCompoundButton(mContext);
-
- try {
- new MockCompoundButton(null, null, -1);
- fail("Should throw NullPointerException.");
- } catch (NullPointerException e) {
- // expected, test success.
- }
-
- try {
- new MockCompoundButton(null, null);
- fail("Should throw NullPointerException.");
- } catch (NullPointerException e) {
- // expected, test success.
- }
-
- try {
- new MockCompoundButton(null);
- fail("Should throw NullPointerException.");
- } catch (NullPointerException e) {
- // expected, test success.
- }
}
+ @Test(expected=NullPointerException.class)
+ public void testConstructorWithNullContext1() {
+ new MockCompoundButton(null);
+ }
+
+ @Test(expected=NullPointerException.class)
+ public void testConstructorWithNullContext2() {
+ new MockCompoundButton(null, null);
+ }
+
+ @Test(expected=NullPointerException.class)
+ public void testConstructorWithNullContext3() {
+ new MockCompoundButton(null, null, -1);
+ }
+
+ @Test
public void testAccessChecked() {
CompoundButton compoundButton = new MockCompoundButton(mContext);
- MockOnCheckedChangeListener listener = new MockOnCheckedChangeListener();
- compoundButton.setOnCheckedChangeListener(listener);
+ CompoundButton.OnCheckedChangeListener mockCheckedChangeListener =
+ mock(CompoundButton.OnCheckedChangeListener.class);
+ compoundButton.setOnCheckedChangeListener(mockCheckedChangeListener);
assertFalse(compoundButton.isChecked());
- assertFalse(listener.hasCalledCheckedChange());
+ verifyZeroInteractions(mockCheckedChangeListener);
compoundButton.setChecked(true);
assertTrue(compoundButton.isChecked());
- assertTrue(listener.hasCalledCheckedChange());
- assertSame(compoundButton, listener.getInputCompoundButton());
- assertTrue(listener.getInputChecked());
+ verify(mockCheckedChangeListener, times(1)).onCheckedChanged(compoundButton, true);
- listener.reset();
+ reset(mockCheckedChangeListener);
compoundButton.setChecked(true);
assertTrue(compoundButton.isChecked());
- assertFalse(listener.hasCalledCheckedChange());
+ verifyZeroInteractions(mockCheckedChangeListener);
compoundButton.setChecked(false);
assertFalse(compoundButton.isChecked());
- assertTrue(listener.hasCalledCheckedChange());
- assertSame(compoundButton, listener.getInputCompoundButton());
- assertFalse(listener.getInputChecked());
+ verify(mockCheckedChangeListener, times(1)).onCheckedChanged(compoundButton, false);
}
+ @Test
public void testSetOnCheckedChangeListener() {
CompoundButton compoundButton = new MockCompoundButton(mContext);
- MockOnCheckedChangeListener listener = new MockOnCheckedChangeListener();
- compoundButton.setOnCheckedChangeListener(listener);
+ CompoundButton.OnCheckedChangeListener mockCheckedChangeListener =
+ mock(CompoundButton.OnCheckedChangeListener.class);
+ compoundButton.setOnCheckedChangeListener(mockCheckedChangeListener);
assertFalse(compoundButton.isChecked());
- assertFalse(listener.hasCalledCheckedChange());
+ verifyZeroInteractions(mockCheckedChangeListener);
compoundButton.setChecked(true);
- assertTrue(listener.hasCalledCheckedChange());
+ verify(mockCheckedChangeListener, times(1)).onCheckedChanged(compoundButton, true);
// set null
compoundButton.setOnCheckedChangeListener(null);
- listener.reset();
+ reset(mockCheckedChangeListener);
compoundButton.setChecked(false);
- assertFalse(listener.hasCalledCheckedChange());
+ verifyZeroInteractions(mockCheckedChangeListener);
}
+ @Test
public void testToggle() {
CompoundButton compoundButton = new MockCompoundButton(mContext);
assertFalse(compoundButton.isChecked());
@@ -142,6 +157,7 @@
assertFalse(compoundButton.isChecked());
}
+ @Test
public void testPerformClick() {
CompoundButton compoundButton = new MockCompoundButton(mContext);
assertFalse(compoundButton.isChecked());
@@ -154,10 +170,7 @@
assertFalse(compoundButton.isChecked());
// performClick with OnClickListener will return true.
- compoundButton.setOnClickListener(new OnClickListener() {
- public void onClick(View v) {
- }
- });
+ compoundButton.setOnClickListener((view) -> {});
assertTrue(compoundButton.performClick());
assertTrue(compoundButton.isChecked());
@@ -165,6 +178,7 @@
assertFalse(compoundButton.isChecked());
}
+ @Test
public void testDrawableStateChanged() {
MockCompoundButton compoundButton = new MockCompoundButton(mContext);
assertFalse(compoundButton.isChecked());
@@ -182,6 +196,7 @@
assertSame(compoundButton.getDrawableState(), drawable.getState());
}
+ @Test
public void testSetButtonDrawableByDrawable() {
CompoundButton compoundButton;
@@ -214,6 +229,7 @@
assertFalse(firstDrawable.isVisible());
}
+ @Test
public void testSetButtonDrawableById() {
CompoundButton compoundButton;
// resId is 0
@@ -231,6 +247,7 @@
compoundButton.setButtonDrawable(R.drawable.pass);
}
+ @Test
public void testOnCreateDrawableState() {
MockCompoundButton compoundButton;
@@ -252,6 +269,7 @@
assertEquals(0, state[state.length - 1]);
}
+ @Test
public void testOnDraw() {
int viewHeight;
int drawableWidth;
@@ -300,6 +318,7 @@
assertEquals( (viewHeight - drawableHeight) / 2 + drawableHeight, bounds.bottom);
}
+ @Test
public void testAccessInstanceState() {
CompoundButton compoundButton = new MockCompoundButton(mContext);
Parcelable state;
@@ -318,6 +337,7 @@
assertTrue(compoundButton.isLayoutRequested());
}
+ @Test
public void testVerifyDrawable() {
MockCompoundButton compoundButton = new MockCompoundButton(mContext);
Drawable drawable = mContext.getResources().getDrawable(R.drawable.scenery);
@@ -330,6 +350,7 @@
assertTrue(compoundButton.verifyDrawable(drawable));
}
+ @Test
public void testButtonTint() {
LayoutInflater inflater = LayoutInflater.from(mContext);
View layout = inflater.inflate(R.layout.togglebutton_layout, null);
@@ -340,53 +361,22 @@
assertEquals("Button tint mode inflated correctly",
PorterDuff.Mode.SRC_OVER, inflatedView.getButtonTintMode());
- MockDrawable button = new MockDrawable();
+ Drawable mockDrawable = spy(new ColorDrawable(Color.GREEN));
CompoundButton view = new ToggleButton(mContext);
- view.setButtonDrawable(button);
- assertFalse("No button tint applied by default", button.hasCalledSetTint());
+ view.setButtonDrawable(mockDrawable);
+ // No button tint applied by default
+ verify(mockDrawable, never()).setTintList(any(ColorStateList.class));
view.setButtonTintList(ColorStateList.valueOf(Color.WHITE));
- assertTrue("Button tint applied when setButtonTintList() called after setButton()",
- button.hasCalledSetTint());
+ // Button tint applied when setButtonTintList() called after setButton()
+ verify(mockDrawable, times(1)).setTintList(TestUtils.colorStateListOf(Color.WHITE));
- button.reset();
+ reset(mockDrawable);
view.setButtonDrawable(null);
- view.setButtonDrawable(button);
- assertTrue("Button tint applied when setButtonTintList() called before setButton()",
- button.hasCalledSetTint());
- }
-
- private static class MockDrawable extends Drawable {
- private boolean mCalledSetTint = false;
-
- @Override
- public void draw(Canvas canvas) {}
-
- @Override
- public void setAlpha(int alpha) {}
-
- @Override
- public void setColorFilter(ColorFilter cf) {}
-
- @Override
- public void setTintList(ColorStateList tint) {
- super.setTintList(tint);
- mCalledSetTint = true;
- }
-
- @Override
- public int getOpacity() {
- return 0;
- }
-
- public boolean hasCalledSetTint() {
- return mCalledSetTint;
- }
-
- public void reset() {
- mCalledSetTint = false;
- }
+ view.setButtonDrawable(mockDrawable);
+ // Button tint applied when setButtonTintList() called before setButton()
+ verify(mockDrawable, times(1)).setTintList(TestUtils.colorStateListOf(Color.WHITE));
}
private final class MockCompoundButton extends CompoundButton {
@@ -422,34 +412,4 @@
return super.verifyDrawable(who);
}
}
-
- private final class MockOnCheckedChangeListener implements OnCheckedChangeListener {
- private boolean mHasCalledChecked;
- private CompoundButton mCompoundButton;
- private boolean mIsChecked;
-
- public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
- mHasCalledChecked = true;
- mCompoundButton = buttonView;
- mIsChecked = isChecked;
- }
-
- public boolean getInputChecked() {
- return mIsChecked;
- }
-
- public CompoundButton getInputCompoundButton() {
- return mCompoundButton;
- }
-
- public boolean hasCalledCheckedChange() {
- return mHasCalledChecked;
- }
-
- public void reset() {
- mHasCalledChecked = false;
- mCompoundButton = null;
- mIsChecked = false;
- }
- }
}
diff --git a/tests/tests/widget/src/android/widget/cts/CursorTreeAdapterTest.java b/tests/tests/widget/src/android/widget/cts/CursorTreeAdapterTest.java
index 8ef414d..2cab66c 100644
--- a/tests/tests/widget/src/android/widget/cts/CursorTreeAdapterTest.java
+++ b/tests/tests/widget/src/android/widget/cts/CursorTreeAdapterTest.java
@@ -16,7 +16,7 @@
package android.widget.cts;
-import java.io.File;
+import static org.mockito.Mockito.*;
import android.content.Context;
import android.database.Cursor;
@@ -31,9 +31,10 @@
import android.widget.Filter;
import android.widget.FilterQueryProvider;
import android.widget.TextView;
-
import android.widget.cts.R;
+import java.io.File;
+
/**
* Test {@link CursorTreeAdapter}.
@@ -199,23 +200,23 @@
@UiThreadTest
public void testNotifyDataSetChangedBoolean() {
MockCursorTreeAdapter adapter = new MockCursorTreeAdapter(mGroupCursor, mContext);
- MockDataSetObserver observer = new MockDataSetObserver();
- adapter.registerDataSetObserver(observer);
+ DataSetObserver mockDataSetObserver = mock(DataSetObserver.class);
+ adapter.registerDataSetObserver(mockDataSetObserver);
// mChildrenCursorHelpers is empty
- assertFalse(observer.hasCalledOnChanged());
+ verifyZeroInteractions(mockDataSetObserver);
adapter.notifyDataSetChanged(false);
- assertTrue(observer.hasCalledOnChanged());
+ verify(mockDataSetObserver, times(1)).onChanged();
// add group 0 into mChildrenCursorHelpers
adapter.getChild(0, 0);
// add group 1 into mChildrenCursorHelpers
adapter.getChild(1, 0);
- observer.reset();
- assertFalse(observer.hasCalledOnChanged());
+ reset(mockDataSetObserver);
+ verifyZeroInteractions(mockDataSetObserver);
adapter.notifyDataSetChanged(true);
- assertTrue(observer.hasCalledOnChanged());
+ verify(mockDataSetObserver, times(1)).onChanged();
adapter.reset();
adapter.getChild(0, 0);
assertTrue(adapter.hasAddedChild1IntoCache());
@@ -223,11 +224,11 @@
assertTrue(adapter.hasAddedChild2IntoCache());
// both group 0 and group 1 are in mChildrenCursorHelpers
- observer.reset();
- assertFalse(observer.hasCalledOnChanged());
+ reset(mockDataSetObserver);
+ verifyZeroInteractions(mockDataSetObserver);
// does not release cursors
adapter.notifyDataSetChanged(false);
- assertTrue(observer.hasCalledOnChanged());
+ verify(mockDataSetObserver, times(1)).onChanged();
adapter.reset();
adapter.getChild(0, 0);
assertFalse(adapter.hasAddedChild1IntoCache());
@@ -238,22 +239,22 @@
@UiThreadTest
public void testNotifyDataSetChanged() {
MockCursorTreeAdapter adapter = new MockCursorTreeAdapter(mGroupCursor, mContext);
- MockDataSetObserver observer = new MockDataSetObserver();
- adapter.registerDataSetObserver(observer);
+ DataSetObserver mockDataSetObserver = mock(DataSetObserver.class);
+ adapter.registerDataSetObserver(mockDataSetObserver);
// mChildrenCursorHelpers is empty
- assertFalse(observer.hasCalledOnChanged());
+ verifyZeroInteractions(mockDataSetObserver);
adapter.notifyDataSetChanged();
- assertTrue(observer.hasCalledOnChanged());
+ verify(mockDataSetObserver, times(1)).onChanged();
// add group 0 into mChildrenCursorHelpers
adapter.getChild(0, 0);
// add group 1 into mChildrenCursorHelpers
adapter.getChild(1, 0);
- observer.reset();
- assertFalse(observer.hasCalledOnChanged());
+ reset(mockDataSetObserver);
+ verifyZeroInteractions(mockDataSetObserver);
adapter.notifyDataSetChanged();
- assertTrue(observer.hasCalledOnChanged());
+ verify(mockDataSetObserver, times(1)).onChanged();
adapter.reset();
adapter.getChild(0, 0);
assertTrue(adapter.hasAddedChild1IntoCache());
@@ -264,22 +265,22 @@
@UiThreadTest
public void testNotifyDataSetInvalidated() {
MockCursorTreeAdapter adapter = new MockCursorTreeAdapter(mGroupCursor, mContext);
- MockDataSetObserver observer = new MockDataSetObserver();
- adapter.registerDataSetObserver(observer);
+ DataSetObserver mockDataSetObserver = mock(DataSetObserver.class);
+ adapter.registerDataSetObserver(mockDataSetObserver);
- assertFalse(observer.hasCalledOnInvalidated());
+ verifyZeroInteractions(mockDataSetObserver);
// mChildrenCursorHelpers is empty
adapter.notifyDataSetInvalidated();
- assertTrue(observer.hasCalledOnInvalidated());
+ verify(mockDataSetObserver, times(1)).onInvalidated();
// add group 0 into mChildrenCursorHelpers
adapter.getChild(0, 0);
// add group 1 into mChildrenCursorHelpers
adapter.getChild(1, 0);
- observer.reset();
- assertFalse(observer.hasCalledOnInvalidated());
+ reset(mockDataSetObserver);
+ verifyZeroInteractions(mockDataSetObserver);
adapter.notifyDataSetInvalidated();
- assertTrue(observer.hasCalledOnInvalidated());
+ verify(mockDataSetObserver, times(1)).onInvalidated();
adapter.reset();
adapter.getChild(0, 0);
assertTrue(adapter.hasAddedChild1IntoCache());
@@ -707,34 +708,4 @@
return null;
}
}
-
- private final class MockDataSetObserver extends DataSetObserver {
- private boolean mHasCalledOnChanged = false;
- private boolean mHasCalledOnInvalidated = false;
-
- @Override
- public void onChanged() {
- super.onChanged();
- mHasCalledOnChanged = true;
- }
-
- @Override
- public void onInvalidated() {
- super.onInvalidated();
- mHasCalledOnInvalidated = true;
- }
-
- public boolean hasCalledOnChanged() {
- return mHasCalledOnChanged;
- }
-
- public boolean hasCalledOnInvalidated() {
- return mHasCalledOnInvalidated;
- }
-
- public void reset() {
- mHasCalledOnChanged = false;
- mHasCalledOnInvalidated = false;
- }
- }
}
diff --git a/hostsidetests/services/activitymanager/app/src/android/server/app/SlowCreateActivity.java b/tests/tests/widget/src/android/widget/cts/DatePickerCtsActivity.java
similarity index 67%
copy from hostsidetests/services/activitymanager/app/src/android/server/app/SlowCreateActivity.java
copy to tests/tests/widget/src/android/widget/cts/DatePickerCtsActivity.java
index 481f5be..81e6e69 100644
--- a/hostsidetests/services/activitymanager/app/src/android/server/app/SlowCreateActivity.java
+++ b/tests/tests/widget/src/android/widget/cts/DatePickerCtsActivity.java
@@ -14,17 +14,22 @@
* limitations under the License.
*/
-package android.server.app;
+package android.widget.cts;
import android.app.Activity;
import android.os.Bundle;
+import android.widget.DatePicker;
-public class SlowCreateActivity extends Activity {
+/**
+ * A minimal application for {@link DatePicker} test.
+ */
+public class DatePickerCtsActivity extends Activity {
+ /**
+ * Called with the activity is first created.
+ */
@Override
- protected void onCreate(Bundle savedInstanceState) {
- try {
- Thread.sleep(2000);
- } catch(InterruptedException e) {}
+ public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ setContentView(R.layout.datepicker_layout);
}
}
diff --git a/tests/tests/widget/src/android/widget/cts/DatePickerDialogTest.java b/tests/tests/widget/src/android/widget/cts/DatePickerDialogTest.java
index 1477f73..177a9f8 100644
--- a/tests/tests/widget/src/android/widget/cts/DatePickerDialogTest.java
+++ b/tests/tests/widget/src/android/widget/cts/DatePickerDialogTest.java
@@ -16,75 +16,77 @@
package android.widget.cts;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
import android.app.Activity;
import android.app.AlertDialog;
import android.app.DatePickerDialog;
-import android.content.Context;
-import android.test.ActivityInstrumentationTestCase2;
-import android.test.UiThreadTest;
+import android.app.Instrumentation;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.MediumTest;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
/**
* Test {@link DatePickerDialog}.
*/
-public class DatePickerDialogTest extends
- ActivityInstrumentationTestCase2<DatePickerDialogCtsActivity> {
-
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class DatePickerDialogTest {
+ private Instrumentation mInstrumentation;
private Activity mActivity;
+ private DatePickerDialog mDatePickerDialog;
- public DatePickerDialogTest() {
- super(DatePickerDialogCtsActivity.class);
+ @Rule
+ public ActivityTestRule<DatePickerDialogCtsActivity> mActivityRule
+ = new ActivityTestRule<>(DatePickerDialogCtsActivity.class);
+
+ @Before
+ public void setup() {
+ mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ mActivity = mActivityRule.getActivity();
}
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mActivity = getActivity();
- }
-
- @UiThreadTest
- @SuppressWarnings("deprecation")
+ @Test
public void testConstructor() {
- new DatePickerDialog(mActivity, null, 1970, 1, 1);
+ mInstrumentation.runOnMainSync(() -> {
+ new DatePickerDialog(mActivity, null, 1970, 1, 1);
- new DatePickerDialog(mActivity, AlertDialog.THEME_TRADITIONAL, null, 1970, 1, 1);
+ new DatePickerDialog(mActivity, AlertDialog.THEME_TRADITIONAL, null, 1970, 1, 1);
- new DatePickerDialog(mActivity, AlertDialog.THEME_HOLO_DARK, null, 1970, 1, 1);
+ new DatePickerDialog(mActivity, AlertDialog.THEME_HOLO_DARK, null, 1970, 1, 1);
- new DatePickerDialog(mActivity,
- android.R.style.Theme_Material_Dialog_Alert, null, 1970, 1, 1);
-
- try {
- new DatePickerDialog(null, null, 1970, 1, 1);
- fail("should throw NullPointerException");
- } catch (Exception e) {
- }
+ new DatePickerDialog(mActivity,
+ android.R.style.Theme_Material_Dialog_Alert, null, 1970, 1, 1);
+ });
}
- @UiThreadTest
+ @Test(expected=NullPointerException.class)
+ public void testConstructorWithNullContext() {
+ new DatePickerDialog(null, null, 1970, 1, 1);
+ }
+
+ @Test
public void testShowDismiss() {
- DatePickerDialog d = createDatePickerDialog();
+ mInstrumentation.runOnMainSync(
+ () -> mDatePickerDialog = new DatePickerDialog(mActivity, null, 1970, 1, 1));
- d.show();
- assertTrue("Showing date picker", d.isShowing());
+ mInstrumentation.runOnMainSync(mDatePickerDialog::show);
+ assertTrue("Showing date picker", mDatePickerDialog.isShowing());
- d.show();
- assertTrue("Date picker still showing", d.isShowing());
+ mInstrumentation.runOnMainSync(mDatePickerDialog::show);
+ assertTrue("Date picker still showing", mDatePickerDialog.isShowing());
- d.dismiss();
- assertFalse("Dismissed date picker", d.isShowing());
+ mInstrumentation.runOnMainSync(mDatePickerDialog::dismiss);
+ assertFalse("Dismissed date picker", mDatePickerDialog.isShowing());
- d.dismiss();
- assertFalse("Date picker still dismissed", d.isShowing());
- }
-
- private MockDatePickerDialog createDatePickerDialog() {
- return new MockDatePickerDialog(mActivity, null, 1970, 1, 1);
- }
-
- private class MockDatePickerDialog extends DatePickerDialog {
- public MockDatePickerDialog(Context context, OnDateSetListener callBack,
- int year, int monthOfYear, int dayOfMonth) {
- super(context, callBack, year, monthOfYear, dayOfMonth);
- }
+ mInstrumentation.runOnMainSync(mDatePickerDialog::dismiss);
+ assertFalse("Date picker still dismissed", mDatePickerDialog.isShowing());
}
}
diff --git a/tests/tests/widget/src/android/widget/cts/DatePickerTest.java b/tests/tests/widget/src/android/widget/cts/DatePickerTest.java
index c48f684..45a3aac 100644
--- a/tests/tests/widget/src/android/widget/cts/DatePickerTest.java
+++ b/tests/tests/widget/src/android/widget/cts/DatePickerTest.java
@@ -16,120 +16,258 @@
package android.widget.cts;
-import android.widget.cts.R;
+import static org.mockito.Mockito.*;
+import android.app.Activity;
+import android.app.Instrumentation;
import android.content.Context;
-import android.content.res.XmlResourceParser;
import android.os.Parcelable;
-import android.test.InstrumentationTestCase;
+import android.test.ActivityInstrumentationTestCase2;
import android.test.UiThreadTest;
-import android.util.AttributeSet;
+import android.test.suitebuilder.annotation.MediumTest;
import android.util.SparseArray;
-import android.util.Xml;
import android.view.View;
import android.widget.DatePicker;
-import android.widget.cts.util.XmlUtils;
+
+import java.util.Calendar;
+import java.util.GregorianCalendar;
/**
* Test {@link DatePicker}.
*/
-public class DatePickerTest extends InstrumentationTestCase {
+@MediumTest
+public class DatePickerTest extends ActivityInstrumentationTestCase2<DatePickerCtsActivity> {
+ private Activity mActivity;
+ private DatePicker mDatePickerSpinnerMode;
+ private DatePicker mDatePickerCalendarMode;
- private Context mContext;
+ public DatePickerTest() {
+ super("android.widget.cts", DatePickerCtsActivity.class);
+ }
@Override
- public void setUp() {
- mContext = getInstrumentation().getTargetContext();
+ protected void setUp() throws Exception {
+ super.setUp();
+ mActivity = getActivity();
+ mDatePickerSpinnerMode = (DatePicker) mActivity.findViewById(R.id.date_picker_spinner_mode);
+ mDatePickerCalendarMode =
+ (DatePicker) mActivity.findViewById(R.id.date_picker_calendar_mode);
}
- @UiThreadTest
public void testConstructor() {
- new DatePicker(mContext);
+ new DatePicker(mActivity);
- new DatePicker(mContext, null);
+ new DatePicker(mActivity, null);
- new DatePicker(mContext, getAttributeSet(R.layout.datepicker_layout));
+ new DatePicker(mActivity, null, android.R.attr.datePickerStyle);
- new DatePicker(mContext, getAttributeSet(R.layout.datepicker_layout), 0);
+ new DatePicker(mActivity, null, 0, android.R.style.Widget_DeviceDefault_DatePicker);
- // Test constructor with null Context, in fact, previous two functions will finally invoke
- // this version.
- try {
- // Test with null Context
- new DatePicker(null, getAttributeSet(R.layout.datepicker_layout), 0);
- fail("should throw NullPointerException");
- } catch (Exception e) {
- }
+ new DatePicker(mActivity, null, 0, android.R.style.Widget_Material_DatePicker);
+
+ new DatePicker(mActivity, null, 0, android.R.style.Widget_Material_Light_DatePicker);
}
- @UiThreadTest
public void testSetEnabled() {
- MockDatePicker datePicker = createDatePicker();
+ final Instrumentation instrumentation = getInstrumentation();
- assertTrue(datePicker.isEnabled());
+ assertTrue(mDatePickerCalendarMode.isEnabled());
- datePicker.setEnabled(false);
- assertFalse(datePicker.isEnabled());
+ instrumentation.runOnMainSync(() -> mDatePickerCalendarMode.setEnabled(false));
+ assertFalse(mDatePickerCalendarMode.isEnabled());
- datePicker.setEnabled(true);
- assertTrue(datePicker.isEnabled());
+ instrumentation.runOnMainSync(() -> mDatePickerCalendarMode.setEnabled(true));
+ assertTrue(mDatePickerCalendarMode.isEnabled());
}
- @UiThreadTest
+ private void verifyInit(DatePicker datePicker) {
+ final Instrumentation instrumentation = getInstrumentation();
+ final DatePicker.OnDateChangedListener mockDateChangeListener =
+ mock(DatePicker.OnDateChangedListener.class);
+
+ instrumentation.runOnMainSync(
+ () -> datePicker.init(2000, 10, 15, mockDateChangeListener));
+ assertEquals(2000, datePicker.getYear());
+ assertEquals(10, datePicker.getMonth());
+ assertEquals(15, datePicker.getDayOfMonth());
+
+ verifyZeroInteractions(mockDateChangeListener);
+ }
+
public void testInit() {
- MockOnDateChangedListener onDateChangedListener = new MockOnDateChangedListener();
- DatePicker datePicker = createDatePicker();
+ verifyInit(mDatePickerSpinnerMode);
+ verifyInit(mDatePickerCalendarMode);
+ }
- /* The month display uses 1-12 but our internal state stores it
- * 0-11 so add one when setting the display.
- */
- datePicker.init(2000, 10, 15, onDateChangedListener);
+ private void verifyAccessDate(DatePicker datePicker) {
+ final Instrumentation instrumentation = getInstrumentation();
+ final DatePicker.OnDateChangedListener mockDateChangeListener =
+ mock(DatePicker.OnDateChangedListener.class);
+
+ instrumentation.runOnMainSync(() -> datePicker.init(2000, 10, 15, mockDateChangeListener));
assertEquals(2000, datePicker.getYear());
assertEquals(10, datePicker.getMonth());
assertEquals(15, datePicker.getDayOfMonth());
+ verify(mockDateChangeListener, never()).onDateChanged(any(DatePicker.class), anyInt(),
+ anyInt(), anyInt());
+
+ instrumentation.runOnMainSync(() -> datePicker.updateDate(1989, 9, 19));
+ assertEquals(1989, datePicker.getYear());
+ assertEquals(9, datePicker.getMonth());
+ assertEquals(19, datePicker.getDayOfMonth());
+ verify(mockDateChangeListener, times(1)).onDateChanged(datePicker, 1989, 9, 19);
+
+ verifyNoMoreInteractions(mockDateChangeListener);
}
- @UiThreadTest
public void testAccessDate() {
- DatePicker datePicker = createDatePicker();
+ verifyAccessDate(mDatePickerSpinnerMode);
+ verifyAccessDate(mDatePickerCalendarMode);
+ }
- /* The month display uses 1-12 but our internal state stores it
- * 0-11 so add one when setting the display.
- */
- MockOnDateChangedListener onDateChangedListener = new MockOnDateChangedListener();
- datePicker.init(2000, 10, 15, onDateChangedListener);
+ private void verifySetOnDateChangedListener(DatePicker datePicker) {
+ final Instrumentation instrumentation = getInstrumentation();
+ final DatePicker.OnDateChangedListener mockDateChangeListener1 =
+ mock(DatePicker.OnDateChangedListener.class);
+ final DatePicker.OnDateChangedListener mockDateChangeListener2 =
+ mock(DatePicker.OnDateChangedListener.class);
+
+ instrumentation.runOnMainSync(() -> datePicker.init(2000, 10, 15, mockDateChangeListener1));
+ instrumentation.runOnMainSync(() -> datePicker.updateDate(1989, 9, 19));
+ assertEquals(1989, datePicker.getYear());
+ assertEquals(9, datePicker.getMonth());
+ assertEquals(19, datePicker.getDayOfMonth());
+ verify(mockDateChangeListener1, times(1)).onDateChanged(datePicker, 1989, 9, 19);
+ verify(mockDateChangeListener2, times(0)).onDateChanged(datePicker, 1989, 9, 19);
+
+ instrumentation.runOnMainSync(() -> datePicker.setOnDateChangedListener(
+ mockDateChangeListener2));
+ instrumentation.runOnMainSync(() -> datePicker.updateDate(2000, 10, 15));
assertEquals(2000, datePicker.getYear());
assertEquals(10, datePicker.getMonth());
assertEquals(15, datePicker.getDayOfMonth());
+ verify(mockDateChangeListener1, times(0)).onDateChanged(datePicker, 2000, 10, 15);
+ verify(mockDateChangeListener2, times(1)).onDateChanged(datePicker, 2000, 10, 15);
- datePicker.updateDate(1989, 9, 19);
+ }
+
+ public void testSetOnDateChangedListener() {
+ verifySetOnDateChangedListener(mDatePickerSpinnerMode);
+ verifySetOnDateChangedListener(mDatePickerCalendarMode);
+ }
+
+ private void verifyUpdateDate(DatePicker datePicker) {
+ final Instrumentation instrumentation = getInstrumentation();
+
+ instrumentation.runOnMainSync(() -> datePicker.updateDate(1989, 9, 19));
assertEquals(1989, datePicker.getYear());
assertEquals(9, datePicker.getMonth());
assertEquals(19, datePicker.getDayOfMonth());
}
- @UiThreadTest
public void testUpdateDate() {
- DatePicker datePicker = createDatePicker();
+ verifyUpdateDate(mDatePickerSpinnerMode);
+ verifyUpdateDate(mDatePickerCalendarMode);
+ }
- // Test normal input values
- /* The month display uses 1-12 but our internal state stores it
- * 0-11 so add one when setting the display.
- */
- datePicker.updateDate(1989, 9, 19);
- assertEquals(1989, datePicker.getYear());
- assertEquals(9, datePicker.getMonth());
- assertEquals(19, datePicker.getDayOfMonth());
+ private void verifyMinMaxDate(DatePicker datePicker) {
+ final Instrumentation instrumentation = getInstrumentation();
+
+ // Use a range of minus/plus one year as min/max dates
+ final Calendar minCalendar = new GregorianCalendar();
+ minCalendar.set(Calendar.YEAR, minCalendar.get(Calendar.YEAR) - 1);
+ final Calendar maxCalendar = new GregorianCalendar();
+ maxCalendar.set(Calendar.YEAR, maxCalendar.get(Calendar.YEAR) + 1);
+
+ final long minDate = minCalendar.getTime().getTime();
+ final long maxDate = maxCalendar.getTime().getTime();
+
+ instrumentation.runOnMainSync(() -> {
+ datePicker.setMinDate(minDate);
+ datePicker.setMaxDate(maxDate);
+ });
+
+ assertEquals(datePicker.getMinDate(), minDate);
+ assertEquals(datePicker.getMaxDate(), maxDate);
+ }
+
+ public void testMinMaxDate() {
+ verifyMinMaxDate(mDatePickerSpinnerMode);
+ verifyMinMaxDate(mDatePickerCalendarMode);
+ }
+
+ private void verifyFirstDayOfWeek(DatePicker datePicker) {
+ final Instrumentation instrumentation = getInstrumentation();
+
+ instrumentation.runOnMainSync(() -> datePicker.setFirstDayOfWeek(Calendar.TUESDAY));
+ assertEquals(Calendar.TUESDAY, datePicker.getFirstDayOfWeek());
+
+ instrumentation.runOnMainSync(() -> datePicker.setFirstDayOfWeek(Calendar.SUNDAY));
+ assertEquals(Calendar.SUNDAY, datePicker.getFirstDayOfWeek());
+ }
+
+ public void testFirstDayOfWeek() {
+ verifyFirstDayOfWeek(mDatePickerSpinnerMode);
+ verifyFirstDayOfWeek(mDatePickerCalendarMode);
+ }
+
+ public void testCalendarViewInSpinnerMode() {
+ final Instrumentation instrumentation = getInstrumentation();
+
+ assertNotNull(mDatePickerSpinnerMode.getCalendarView());
+
+ // Update the DatePicker and test that its CalendarView is synced to the same date
+ final Calendar calendar = new GregorianCalendar();
+ calendar.set(Calendar.YEAR, 2008);
+ calendar.set(Calendar.MONTH, Calendar.SEPTEMBER);
+ calendar.set(Calendar.DAY_OF_MONTH, 23);
+ instrumentation.runOnMainSync(
+ () -> mDatePickerSpinnerMode.updateDate(
+ calendar.get(Calendar.YEAR),
+ calendar.get(Calendar.MONTH),
+ calendar.get(Calendar.DAY_OF_MONTH)));
+
+ final Calendar calendarFromSpinner = new GregorianCalendar();
+ final long timeFromSpinnerCalendar = mDatePickerSpinnerMode.getCalendarView().getDate();
+ calendarFromSpinner.setTimeInMillis(timeFromSpinnerCalendar);
+
+ assertEquals(calendar.get(Calendar.YEAR), calendarFromSpinner.get(Calendar.YEAR));
+ assertEquals(calendar.get(Calendar.MONTH), calendarFromSpinner.get(Calendar.MONTH));
+ assertEquals(calendar.get(Calendar.DAY_OF_MONTH),
+ calendarFromSpinner.get(Calendar.DAY_OF_MONTH));
+ }
+
+ public void testPartsVisibilityInSpinnerMode() {
+ final Instrumentation instrumentation = getInstrumentation();
+
+ assertTrue(mDatePickerSpinnerMode.getSpinnersShown());
+ assertTrue(mDatePickerSpinnerMode.getCalendarViewShown());
+
+ instrumentation.runOnMainSync(() -> mDatePickerSpinnerMode.setSpinnersShown(false));
+ assertFalse(mDatePickerSpinnerMode.getSpinnersShown());
+ assertTrue(mDatePickerSpinnerMode.getCalendarViewShown());
+
+ instrumentation.runOnMainSync(() -> mDatePickerSpinnerMode.setCalendarViewShown(false));
+ assertFalse(mDatePickerSpinnerMode.getSpinnersShown());
+ assertFalse(mDatePickerSpinnerMode.getCalendarViewShown());
+
+ instrumentation.runOnMainSync(() -> mDatePickerSpinnerMode.setSpinnersShown(true));
+ assertTrue(mDatePickerSpinnerMode.getSpinnersShown());
+ assertFalse(mDatePickerSpinnerMode.getCalendarViewShown());
+
+ instrumentation.runOnMainSync(() -> mDatePickerSpinnerMode.setCalendarViewShown(true));
+ assertTrue(mDatePickerSpinnerMode.getSpinnersShown());
+ assertTrue(mDatePickerSpinnerMode.getCalendarViewShown());
}
@UiThreadTest
- public void testOnSaveInstanceState() {
- MockDatePicker datePicker = createDatePicker();
+ public void testAccessInstanceState() {
+ MockDatePicker datePicker = new MockDatePicker(mActivity);
datePicker.updateDate(2008, 9, 10);
SparseArray<Parcelable> container = new SparseArray<Parcelable>();
- // Test onSaveHierarchyState
+ // Test saveHierarchyState -> onSaveInstanceState path
assertEquals(View.NO_ID, datePicker.getId());
datePicker.setId(99);
assertFalse(datePicker.hasCalledOnSaveInstanceState());
@@ -137,43 +275,23 @@
assertEquals(1, datePicker.getChildCount());
assertTrue(datePicker.hasCalledOnSaveInstanceState());
- // Test dispatchRestoreInstanceState
- datePicker = createDatePicker();
+ // Test dispatchRestoreInstanceState -> onRestoreInstanceState path
+ datePicker = new MockDatePicker(mActivity);
datePicker.setId(99);
assertFalse(datePicker.hasCalledOnRestoreInstanceState());
datePicker.dispatchRestoreInstanceState(container);
assertEquals(2008, datePicker.getYear());
assertEquals(9, datePicker.getMonth());
assertEquals(10, datePicker.getDayOfMonth());
-
- // Test onRestoreInstanceState
assertTrue(datePicker.hasCalledOnRestoreInstanceState());
}
- private AttributeSet getAttributeSet(int resourceId) {
- final XmlResourceParser parser = mContext.getResources().getXml(resourceId);
- try {
- XmlUtils.beginDocument(parser, "RelativeLayout");
- } catch (Exception e) {
- fail("Found unexpected loading process error before invoking generateLayoutParams.");
- }
- final AttributeSet attr = Xml.asAttributeSet(parser);
- assertNotNull(attr);
- return attr;
- }
-
- private MockDatePicker createDatePicker() {
- MockDatePicker datePicker = new MockDatePicker(mContext,
- getAttributeSet(R.layout.datepicker_layout));
- return datePicker;
- }
-
private class MockDatePicker extends DatePicker {
private boolean mCalledOnSaveInstanceState = false;
private boolean mCalledOnRestoreInstanceState = false;
- public MockDatePicker(Context context, AttributeSet attrs) {
- super(context, attrs);
+ public MockDatePicker(Context context) {
+ super(context);
}
@Override
@@ -201,10 +319,4 @@
return mCalledOnRestoreInstanceState;
}
}
-
- private class MockOnDateChangedListener implements DatePicker.OnDateChangedListener {
- public void onDateChanged(DatePicker view, int year, int monthOfYear, int dayOfMonth) {
-
- }
- }
}
diff --git a/tests/tests/widget/src/android/widget/cts/DialerFilterTest.java b/tests/tests/widget/src/android/widget/cts/DialerFilterTest.java
index a99f56a..120f332 100644
--- a/tests/tests/widget/src/android/widget/cts/DialerFilterTest.java
+++ b/tests/tests/widget/src/android/widget/cts/DialerFilterTest.java
@@ -16,11 +16,6 @@
package android.widget.cts;
-import android.widget.cts.R;
-
-
-import org.xmlpull.v1.XmlPullParser;
-
import android.app.Activity;
import android.app.Instrumentation;
import android.content.Context;
@@ -34,10 +29,12 @@
import android.util.Log;
import android.util.Xml;
import android.view.KeyCharacterMap;
-import android.view.KeyEvent;
import android.widget.DialerFilter;
import android.widget.EditText;
import android.widget.RelativeLayout;
+import android.widget.cts.R;
+
+import org.xmlpull.v1.XmlPullParser;
public class DialerFilterTest extends ActivityInstrumentationTestCase2<DialerFilterCtsActivity> {
private Activity mActivity;
@@ -53,12 +50,7 @@
super.setUp();
mActivity = getActivity();
- new PollingCheck() {
- @Override
- protected boolean check() {
- return mActivity.hasWindowFocus();
- }
- }.run();
+ PollingCheck.waitFor(mActivity::hasWindowFocus);
mInstrumentation = getInstrumentation();
mDialerFilter = (DialerFilter) mActivity.findViewById(R.id.dialer_filter);
diff --git a/tests/tests/widget/src/android/widget/cts/DigitalClockCtsActivity.java b/tests/tests/widget/src/android/widget/cts/DigitalClockCtsActivity.java
index d92651e..07c0270 100644
--- a/tests/tests/widget/src/android/widget/cts/DigitalClockCtsActivity.java
+++ b/tests/tests/widget/src/android/widget/cts/DigitalClockCtsActivity.java
@@ -16,10 +16,9 @@
package android.widget.cts;
-import android.widget.cts.R;
-
import android.app.Activity;
import android.os.Bundle;
+import android.widget.cts.R;
/**
* A minimal application for TextView test.
diff --git a/tests/tests/widget/src/android/widget/cts/DigitalClockTest.java b/tests/tests/widget/src/android/widget/cts/DigitalClockTest.java
index 2bfcc59..cf1b166 100644
--- a/tests/tests/widget/src/android/widget/cts/DigitalClockTest.java
+++ b/tests/tests/widget/src/android/widget/cts/DigitalClockTest.java
@@ -16,10 +16,6 @@
package android.widget.cts;
-import android.widget.cts.R;
-
-import org.xmlpull.v1.XmlPullParserException;
-
import android.app.Activity;
import android.content.Context;
import android.content.res.XmlResourceParser;
@@ -29,8 +25,11 @@
import android.util.Xml;
import android.widget.DigitalClock;
import android.widget.LinearLayout;
+import android.widget.cts.R;
import android.widget.cts.util.XmlUtils;
+import org.xmlpull.v1.XmlPullParserException;
+
import java.io.IOException;
diff --git a/tests/tests/widget/src/android/widget/cts/EditTextTest.java b/tests/tests/widget/src/android/widget/cts/EditTextTest.java
index acf5f38..68c3ab3 100644
--- a/tests/tests/widget/src/android/widget/cts/EditTextTest.java
+++ b/tests/tests/widget/src/android/widget/cts/EditTextTest.java
@@ -16,8 +16,6 @@
package android.widget.cts;
-import org.xmlpull.v1.XmlPullParser;
-
import android.content.Context;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
@@ -28,9 +26,10 @@
import android.util.Xml;
import android.widget.EditText;
import android.widget.TextView.BufferType;
-
import android.widget.cts.R;
+import org.xmlpull.v1.XmlPullParser;
+
public class EditTextTest extends AndroidTestCase {
private Context mContext;
diff --git a/hostsidetests/services/activitymanager/app/src/android/server/app/NonResizeableActivity.java b/tests/tests/widget/src/android/widget/cts/ExpandableList.java
similarity index 67%
copy from hostsidetests/services/activitymanager/app/src/android/server/app/NonResizeableActivity.java
copy to tests/tests/widget/src/android/widget/cts/ExpandableList.java
index 6312b47..48fa5e9 100644
--- a/hostsidetests/services/activitymanager/app/src/android/server/app/NonResizeableActivity.java
+++ b/tests/tests/widget/src/android/widget/cts/ExpandableList.java
@@ -14,14 +14,18 @@
* limitations under the License.
*/
-package android.server.app;
+package android.widget.cts;
-public class NonResizeableActivity extends AbstractLifecycleLogActivity {
+import android.widget.ExpandableListAdapter;
- private static final String TAG = NonResizeableActivity.class.getSimpleName();
+public class ExpandableList extends ExpandableListBasic {
+ @Override
+ protected ExpandableListAdapter createAdapter() {
+ return null;
+ }
@Override
- protected String getTag() {
- return TAG;
+ protected boolean shouldRegisterItemClickListener() {
+ return false;
}
}
diff --git a/tests/tests/widget/src/android/widget/cts/ExpandableListSimple.java b/tests/tests/widget/src/android/widget/cts/ExpandableListBasic.java
similarity index 68%
rename from tests/tests/widget/src/android/widget/cts/ExpandableListSimple.java
rename to tests/tests/widget/src/android/widget/cts/ExpandableListBasic.java
index f2e87ee7..b314e15 100644
--- a/tests/tests/widget/src/android/widget/cts/ExpandableListSimple.java
+++ b/tests/tests/widget/src/android/widget/cts/ExpandableListBasic.java
@@ -18,26 +18,25 @@
import android.view.Menu;
import android.view.MenuItem;
-import android.view.MenuItem.OnMenuItemClickListener;
import android.widget.BaseExpandableListAdapter;
import android.widget.cts.util.ExpandableListScenario;
-public class ExpandableListSimple extends ExpandableListScenario {
- private static final int[] NUM_CHILDREN = {4, 3, 2, 1, 0};
+public class ExpandableListBasic extends ExpandableListScenario {
+ private static final int[] CHILD_COUNT = {4, 3, 2, 1, 0};
@Override
protected void init(ExpandableParams params) {
- params.setNumChildren(NUM_CHILDREN).setItemScreenSizeFactor(0.14);
+ params.setNumChildren(CHILD_COUNT).setItemScreenSizeFactor(0.14);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
- menu.add("Add item").setOnMenuItemClickListener(new OnMenuItemClickListener() {
- public boolean onMenuItemClick(MenuItem item) {
+ menu.add("Add item").setOnMenuItemClickListener((MenuItem item) -> {
mGroups.add(0, new MyGroup(2));
- ((BaseExpandableListAdapter) mAdapter).notifyDataSetChanged();
+ if (mAdapter != null) {
+ ((BaseExpandableListAdapter) mAdapter).notifyDataSetChanged();
+ }
return true;
- }
});
return true;
diff --git a/tests/tests/widget/src/android/widget/cts/ExpandableListViewBasicTest.java b/tests/tests/widget/src/android/widget/cts/ExpandableListViewBasicTest.java
index dd8d6a2..be3a3f5 100644
--- a/tests/tests/widget/src/android/widget/cts/ExpandableListViewBasicTest.java
+++ b/tests/tests/widget/src/android/widget/cts/ExpandableListViewBasicTest.java
@@ -16,7 +16,7 @@
package android.widget.cts;
-
+import android.app.Instrumentation;
import android.cts.util.PollingCheck;
import android.test.ActivityInstrumentationTestCase2;
import android.test.suitebuilder.annotation.MediumTest;
@@ -25,39 +25,36 @@
import android.widget.ExpandableListAdapter;
import android.widget.ExpandableListView;
import android.widget.cts.util.ExpandableListScenario;
-import android.widget.cts.util.ListUtil;
import android.widget.cts.util.ExpandableListScenario.MyGroup;
+import android.widget.cts.util.ListUtil;
import java.util.List;
+@MediumTest
public class ExpandableListViewBasicTest extends
- ActivityInstrumentationTestCase2<ExpandableListSimple> {
+ ActivityInstrumentationTestCase2<ExpandableListBasic> {
+ private Instrumentation mInstrumentation;
private ExpandableListScenario mActivity;
private ExpandableListView mExpandableListView;
private ExpandableListAdapter mAdapter;
private ListUtil mListUtil;
public ExpandableListViewBasicTest() {
- super(ExpandableListSimple.class);
+ super(ExpandableListBasic.class);
}
@Override
protected void setUp() throws Exception {
super.setUp();
+ mInstrumentation = getInstrumentation();
mActivity = getActivity();
- new PollingCheck() {
- @Override
- protected boolean check() {
- return mActivity.hasWindowFocus();
- }
- }.run();
+ PollingCheck.waitFor(mActivity::hasWindowFocus);
mExpandableListView = mActivity.getExpandableListView();
mAdapter = mExpandableListView.getExpandableListAdapter();
mListUtil = new ListUtil(mExpandableListView, getInstrumentation());
}
- @MediumTest
public void testPreconditions() {
assertNotNull(mActivity);
assertNotNull(mExpandableListView);
@@ -69,7 +66,7 @@
assertTrue("Could not find group to expand", groupPos >= 0);
assertFalse("Group is already expanded", mExpandableListView.isGroupExpanded(groupPos));
mListUtil.arrowScrollToSelectedPosition(groupPos);
- getInstrumentation().waitForIdleSync();
+ mInstrumentation.waitForIdleSync();
sendKeys(KeyEvent.KEYCODE_DPAD_CENTER);
getInstrumentation().waitForIdleSync();
assertTrue("Group did not expand", mExpandableListView.isGroupExpanded(groupPos));
@@ -77,32 +74,29 @@
return groupPos;
}
- @MediumTest
public void testExpandGroup() {
expandGroup(-1, true);
}
- @MediumTest
public void testCollapseGroup() {
final int groupPos = expandGroup(-1, true);
sendKeys(KeyEvent.KEYCODE_DPAD_CENTER);
- getInstrumentation().waitForIdleSync();
+ mInstrumentation.waitForIdleSync();
assertFalse("Group did not collapse", mExpandableListView.isGroupExpanded(groupPos));
}
- @MediumTest
public void testExpandedGroupMovement() {
// Expand the first group
mListUtil.arrowScrollToSelectedPosition(0);
sendKeys(KeyEvent.KEYCODE_DPAD_CENTER);
- getInstrumentation().waitForIdleSync();
+ mInstrumentation.waitForIdleSync();
// Ensure it expanded
assertTrue("Group did not expand", mExpandableListView.isGroupExpanded(0));
// Wait until that's all good
- getInstrumentation().waitForIdleSync();
+ mInstrumentation.waitForIdleSync();
// Make sure it expanded
assertTrue("Group did not expand", mExpandableListView.isGroupExpanded(0));
@@ -117,12 +111,8 @@
mAdapter instanceof BaseExpandableListAdapter);
final BaseExpandableListAdapter adapter = (BaseExpandableListAdapter) mAdapter;
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- adapter.notifyDataSetChanged();
- }
- });
- getInstrumentation().waitForIdleSync();
+ mInstrumentation.runOnMainSync(adapter::notifyDataSetChanged);
+ mInstrumentation.waitForIdleSync();
// Make sure the right group is expanded
assertTrue("The expanded state didn't stay with the proper group",
@@ -131,20 +121,17 @@
mExpandableListView.isGroupExpanded(0));
}
- @MediumTest
public void testContextMenus() {
ExpandableListTester tester = new ExpandableListTester(mExpandableListView, this);
tester.testContextMenus();
}
- @MediumTest
public void testConvertionBetweenFlatAndPacked() {
ExpandableListTester tester = new ExpandableListTester(mExpandableListView, this);
tester.testConvertionBetweenFlatAndPackedOnGroups();
tester.testConvertionBetweenFlatAndPackedOnChildren();
}
- @MediumTest
public void testSelectedPosition() {
ExpandableListTester tester = new ExpandableListTester(mExpandableListView, this);
tester.testSelectedPositionOnGroups();
diff --git a/tests/tests/widget/src/android/widget/cts/ExpandableListViewTest.java b/tests/tests/widget/src/android/widget/cts/ExpandableListViewTest.java
index e773ebf..ca9cc4c 100644
--- a/tests/tests/widget/src/android/widget/cts/ExpandableListViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/ExpandableListViewTest.java
@@ -16,43 +16,76 @@
package android.widget.cts;
-import android.widget.cts.R;
+import static org.mockito.Mockito.*;
-
-import org.xmlpull.v1.XmlPullParser;
-
+import android.app.Instrumentation;
import android.content.Context;
+import android.cts.util.PollingCheck;
import android.database.DataSetObserver;
import android.graphics.Canvas;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Parcelable;
-import android.test.AndroidTestCase;
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.UiThreadTest;
+import android.test.suitebuilder.annotation.MediumTest;
import android.util.AttributeSet;
import android.util.Xml;
+import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.AdapterView;
import android.widget.ExpandableListAdapter;
import android.widget.ExpandableListView;
import android.widget.ListAdapter;
-import android.widget.AdapterView.OnItemClickListener;
-import android.widget.ExpandableListView.OnChildClickListener;
-import android.widget.ExpandableListView.OnGroupClickListener;
+import android.widget.TextView;
+import android.widget.cts.util.ExpandableListScenario;
-public class ExpandableListViewTest extends AndroidTestCase {
+import org.xmlpull.v1.XmlPullParser;
+
+@MediumTest
+public class ExpandableListViewTest extends ActivityInstrumentationTestCase2<ExpandableList> {
+ private Instrumentation mInstrumentation;
+ private ExpandableListScenario mActivity;
+ private ExpandableListView mExpandableListView;
+
+ public ExpandableListViewTest() {
+ super(ExpandableList.class);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ mInstrumentation = getInstrumentation();
+ mActivity = getActivity();
+ PollingCheck.waitFor(mActivity::hasWindowFocus);
+ mExpandableListView = mActivity.getExpandableListView();
+ }
+
public void testConstructor() {
- new ExpandableListView(mContext);
+ new ExpandableListView(mActivity);
- new ExpandableListView(mContext, null);
+ new ExpandableListView(mActivity, null);
- new ExpandableListView(mContext, null, 0);
+ new ExpandableListView(mActivity, null, android.R.attr.expandableListViewStyle);
+
+ new ExpandableListView(mActivity, null, 0,
+ android.R.style.Widget_DeviceDefault_ExpandableListView);
+
+ new ExpandableListView(mActivity, null, 0,
+ android.R.style.Widget_DeviceDefault_Light_ExpandableListView);
+
+ new ExpandableListView(mActivity, null, 0,
+ android.R.style.Widget_Material_ExpandableListView);
+
+ new ExpandableListView(mActivity, null, 0,
+ android.R.style.Widget_Material_Light_ExpandableListView);
XmlPullParser parser =
- getContext().getResources().getXml(R.layout.expandablelistview_layout);
+ mActivity.getResources().getXml(R.layout.expandablelistview_layout);
AttributeSet attrs = Xml.asAttributeSet(parser);
- new ExpandableListView(mContext, attrs);
- new ExpandableListView(mContext, attrs, 0);
+ new ExpandableListView(mActivity, attrs);
+ new ExpandableListView(mActivity, attrs, 0);
try {
new ExpandableListView(null);
@@ -74,230 +107,292 @@
}
public void testSetChildDivider() {
- ExpandableListView expandableListView = new ExpandableListView(mContext);
- Drawable drawable = mContext.getResources().getDrawable(R.drawable.scenery);
- expandableListView.setChildDivider(drawable);
+ Drawable drawable = mActivity.getResources().getDrawable(R.drawable.scenery);
+ mExpandableListView.setChildDivider(drawable);
}
public void testSetAdapter() {
- ExpandableListView expandableListView = new ExpandableListView(mContext);
try {
- expandableListView.setAdapter((ListAdapter) null);
+ mExpandableListView.setAdapter((ListAdapter) null);
fail("setAdapter(ListAdapter) should throw RuntimeException here.");
} catch (RuntimeException e) {
}
}
+ @UiThreadTest
public void testGetAdapter() {
- ExpandableListView expandableListView = new ExpandableListView(mContext);
- assertNull(expandableListView.getAdapter());
+ assertNull(mExpandableListView.getAdapter());
ExpandableListAdapter expandableAdapter = new MockExpandableListAdapter();
- expandableListView.setAdapter(expandableAdapter);
- assertNotNull(expandableListView.getAdapter());
+ mExpandableListView.setAdapter(expandableAdapter);
+ assertNotNull(mExpandableListView.getAdapter());
}
+ @UiThreadTest
public void testAccessExpandableListAdapter() {
- ExpandableListView expandableListView = new ExpandableListView(mContext);
ExpandableListAdapter expandableAdapter = new MockExpandableListAdapter();
- assertNull(expandableListView.getExpandableListAdapter());
- expandableListView.setAdapter(expandableAdapter);
- assertSame(expandableAdapter, expandableListView.getExpandableListAdapter());
+ assertNull(mExpandableListView.getExpandableListAdapter());
+ mExpandableListView.setAdapter(expandableAdapter);
+ assertSame(expandableAdapter, mExpandableListView.getExpandableListAdapter());
}
+ @UiThreadTest
public void testPerformItemClick() {
- ExpandableListView expandableListView = new ExpandableListView(mContext);
+ assertFalse(mExpandableListView.performItemClick(null, 100, 99));
- assertFalse(expandableListView.performItemClick(null, 100, 99));
-
- MockOnItemClickListener onClickListener = new MockOnItemClickListener();
- expandableListView.setOnItemClickListener(onClickListener);
- assertTrue(expandableListView.performItemClick(null, 100, 99));
+ ExpandableListView.OnItemClickListener mockOnItemClickListener =
+ mock(ExpandableListView.OnItemClickListener.class);
+ mExpandableListView.setOnItemClickListener(mockOnItemClickListener);
+ assertTrue(mExpandableListView.performItemClick(null, 100, 99));
+ verify(mockOnItemClickListener, times(1)).onItemClick(eq(mExpandableListView),
+ any(View.class), eq(100), eq(99L));
}
public void testSetOnItemClickListener() {
- ExpandableListView expandableListView = new ExpandableListView(mContext);
- MockOnItemClickListener listener = new MockOnItemClickListener();
+ ExpandableListView.OnItemClickListener mockOnItemClickListener =
+ mock(ExpandableListView.OnItemClickListener.class);
- assertNull(expandableListView.getOnItemClickListener());
- expandableListView.setOnItemClickListener(listener);
- assertSame(listener, expandableListView.getOnItemClickListener());
+ assertNull(mExpandableListView.getOnItemClickListener());
+ mExpandableListView.setOnItemClickListener(mockOnItemClickListener);
+ assertSame(mockOnItemClickListener, mExpandableListView.getOnItemClickListener());
}
+ @UiThreadTest
public void testExpandGroup() {
- ExpandableListView expandableListView = new ExpandableListView(mContext);
ExpandableListAdapter expandableAdapter = new MockExpandableListAdapter();
- expandableListView.setAdapter(expandableAdapter);
+ mExpandableListView.setAdapter(expandableAdapter);
- MockOnGroupExpandListener mockOnGroupExpandListener = new MockOnGroupExpandListener();
- expandableListView.setOnGroupExpandListener(mockOnGroupExpandListener);
+ ExpandableListView.OnGroupExpandListener mockOnGroupExpandListener =
+ mock(ExpandableListView.OnGroupExpandListener.class);
+ mExpandableListView.setOnGroupExpandListener(mockOnGroupExpandListener);
- assertFalse(mockOnGroupExpandListener.hasCalledOnGroupExpand());
- assertTrue(expandableListView.expandGroup(0));
- assertTrue(mockOnGroupExpandListener.hasCalledOnGroupExpand());
- mockOnGroupExpandListener.reset();
- assertFalse(expandableListView.expandGroup(0));
- assertTrue(mockOnGroupExpandListener.hasCalledOnGroupExpand());
- mockOnGroupExpandListener.reset();
- assertTrue(expandableListView.expandGroup(1));
- assertTrue(mockOnGroupExpandListener.hasCalledOnGroupExpand());
- mockOnGroupExpandListener.reset();
- assertFalse(expandableListView.expandGroup(1));
- assertTrue(mockOnGroupExpandListener.hasCalledOnGroupExpand());
- mockOnGroupExpandListener.reset();
+ verifyZeroInteractions(mockOnGroupExpandListener);
- expandableListView.setAdapter((ExpandableListAdapter) null);
+ assertTrue(mExpandableListView.expandGroup(0));
+ verify(mockOnGroupExpandListener, times(1)).onGroupExpand(0);
+ assertTrue(mExpandableListView.isGroupExpanded(0));
+
+ reset(mockOnGroupExpandListener);
+ assertFalse(mExpandableListView.expandGroup(0));
+ verify(mockOnGroupExpandListener, times(1)).onGroupExpand(0);
+ assertTrue(mExpandableListView.isGroupExpanded(0));
+
+ reset(mockOnGroupExpandListener);
+ assertTrue(mExpandableListView.expandGroup(1));
+ verify(mockOnGroupExpandListener, times(1)).onGroupExpand(1);
+ assertTrue(mExpandableListView.isGroupExpanded(1));
+
+ reset(mockOnGroupExpandListener);
+ assertFalse(mExpandableListView.expandGroup(1));
+ verify(mockOnGroupExpandListener, times(1)).onGroupExpand(1);
+ assertTrue(mExpandableListView.isGroupExpanded(1));
+
+ reset(mockOnGroupExpandListener);
+ mExpandableListView.setAdapter((ExpandableListAdapter) null);
try {
- expandableListView.expandGroup(0);
+ mExpandableListView.expandGroup(0);
fail("should throw NullPointerException");
} catch (NullPointerException e) {
}
}
+ public void testExpandGroupSmooth() {
+ mInstrumentation.runOnMainSync(
+ () -> mExpandableListView.setAdapter(new MockExpandableListAdapter()));
+
+ ExpandableListView.OnGroupExpandListener mockOnGroupExpandListener =
+ mock(ExpandableListView.OnGroupExpandListener.class);
+ mExpandableListView.setOnGroupExpandListener(mockOnGroupExpandListener);
+
+ verifyZeroInteractions(mockOnGroupExpandListener);
+ mInstrumentation.runOnMainSync(() -> assertTrue(mExpandableListView.expandGroup(0, true)));
+ mInstrumentation.waitForIdleSync();
+ verify(mockOnGroupExpandListener, times(1)).onGroupExpand(0);
+ assertTrue(mExpandableListView.isGroupExpanded(0));
+
+ reset(mockOnGroupExpandListener);
+ mInstrumentation.runOnMainSync(() -> assertFalse(mExpandableListView.expandGroup(0, true)));
+ mInstrumentation.waitForIdleSync();
+ verify(mockOnGroupExpandListener, times(1)).onGroupExpand(0);
+ assertTrue(mExpandableListView.isGroupExpanded(0));
+
+ reset(mockOnGroupExpandListener);
+ mInstrumentation.runOnMainSync(() -> assertTrue(mExpandableListView.expandGroup(1, true)));
+ mInstrumentation.waitForIdleSync();
+ verify(mockOnGroupExpandListener, times(1)).onGroupExpand(1);
+ assertTrue(mExpandableListView.isGroupExpanded(1));
+
+ reset(mockOnGroupExpandListener);
+ mInstrumentation.runOnMainSync(() -> assertFalse(mExpandableListView.expandGroup(1, true)));
+ mInstrumentation.waitForIdleSync();
+ verify(mockOnGroupExpandListener, times(1)).onGroupExpand(1);
+ assertTrue(mExpandableListView.isGroupExpanded(1));
+
+ reset(mockOnGroupExpandListener);
+ mInstrumentation.runOnMainSync(() -> {
+ mExpandableListView.setAdapter((ExpandableListAdapter) null);
+ try {
+ mExpandableListView.expandGroup(0);
+ fail("should throw NullPointerException");
+ } catch (NullPointerException e) {
+ }
+ });
+ }
+
+ @UiThreadTest
public void testCollapseGroup() {
- ExpandableListView expandableListView = new ExpandableListView(mContext);
ExpandableListAdapter expandableAdapter = new MockExpandableListAdapter();
- expandableListView.setAdapter(expandableAdapter);
+ mExpandableListView.setAdapter(expandableAdapter);
- MockOnGroupCollapseListener mockOnGroupCollapseListener =
- new MockOnGroupCollapseListener();
- expandableListView.setOnGroupCollapseListener(mockOnGroupCollapseListener);
+ ExpandableListView.OnGroupCollapseListener mockOnGroupCollapseListener =
+ mock(ExpandableListView.OnGroupCollapseListener.class);
+ mExpandableListView.setOnGroupCollapseListener(mockOnGroupCollapseListener);
- assertFalse(mockOnGroupCollapseListener.hasCalledOnGroupCollapse());
- assertFalse(expandableListView.collapseGroup(0));
- assertTrue(mockOnGroupCollapseListener.hasCalledOnGroupCollapse());
- mockOnGroupCollapseListener.reset();
+ verifyZeroInteractions(mockOnGroupCollapseListener);
+ assertFalse(mExpandableListView.collapseGroup(0));
+ verify(mockOnGroupCollapseListener, times(1)).onGroupCollapse(0);
+ assertFalse(mExpandableListView.isGroupExpanded(0));
- expandableListView.expandGroup(0);
- assertTrue(expandableListView.collapseGroup(0));
- assertTrue(mockOnGroupCollapseListener.hasCalledOnGroupCollapse());
- mockOnGroupCollapseListener.reset();
- assertFalse(expandableListView.collapseGroup(1));
- assertTrue(mockOnGroupCollapseListener.hasCalledOnGroupCollapse());
- mockOnGroupCollapseListener.reset();
+ reset(mockOnGroupCollapseListener);
+ mExpandableListView.expandGroup(0);
+ assertTrue(mExpandableListView.collapseGroup(0));
+ verify(mockOnGroupCollapseListener, times(1)).onGroupCollapse(0);
+ assertFalse(mExpandableListView.isGroupExpanded(0));
- expandableListView.setAdapter((ExpandableListAdapter) null);
+ reset(mockOnGroupCollapseListener);
+ assertFalse(mExpandableListView.collapseGroup(1));
+ verify(mockOnGroupCollapseListener, times(1)).onGroupCollapse(1);
+ assertFalse(mExpandableListView.isGroupExpanded(1));
+
+ reset(mockOnGroupCollapseListener);
+ mExpandableListView.setAdapter((ExpandableListAdapter) null);
try {
- expandableListView.collapseGroup(0);
+ mExpandableListView.collapseGroup(0);
fail("should throw NullPointerException");
} catch (NullPointerException e) {
}
}
+ @UiThreadTest
public void testSetOnGroupClickListener() {
- ExpandableListView expandableListView = new ExpandableListView(mContext);
- expandableListView.setAdapter(new MockExpandableListAdapter());
- MockOnGroupClickListener listener = new MockOnGroupClickListener();
+ mExpandableListView.setAdapter(new MockExpandableListAdapter());
- expandableListView.setOnGroupClickListener(listener);
- assertFalse(listener.hasCalledOnGroupClick());
- expandableListView.performItemClick(null, 0, 0);
- assertTrue(listener.hasCalledOnGroupClick());
+ ExpandableListView.OnGroupClickListener mockOnGroupClickListener =
+ mock(ExpandableListView.OnGroupClickListener.class);
+
+ mExpandableListView.setOnGroupClickListener(mockOnGroupClickListener);
+ verifyZeroInteractions(mockOnGroupClickListener);
+
+ mExpandableListView.performItemClick(null, 0, 0);
+ verify(mockOnGroupClickListener, times(1)).onGroupClick(eq(mExpandableListView),
+ any(View.class), eq(0), eq(0L));
}
+ @UiThreadTest
public void testSetOnChildClickListener() {
- ExpandableListView expandableListView = new ExpandableListView(mContext);
- expandableListView.setAdapter(new MockExpandableListAdapter());
- MockOnChildClickListener listener = new MockOnChildClickListener();
+ mExpandableListView.setAdapter(new MockExpandableListAdapter());
- expandableListView.setOnChildClickListener(listener);
- assertFalse(listener.hasCalledOnChildClick());
+ ExpandableListView.OnChildClickListener mockOnChildClickListener =
+ mock(ExpandableListView.OnChildClickListener.class);
+
+ mExpandableListView.setOnChildClickListener(mockOnChildClickListener);
+ verifyZeroInteractions(mockOnChildClickListener);
+
// first let the list expand
- expandableListView.expandGroup(0);
+ mExpandableListView.expandGroup(0);
// click on the child list of the first group
- expandableListView.performItemClick(null, 1, 0);
- assertTrue(listener.hasCalledOnChildClick());
+ mExpandableListView.performItemClick(null, 1, 0);
+ verify(mockOnChildClickListener, times(1)).onChildClick(eq(mExpandableListView),
+ any(View.class), eq(0), eq(0), eq(0L));
}
+ @UiThreadTest
public void testGetExpandableListPosition() {
- ExpandableListView expandableListView = new ExpandableListView(mContext);
- expandableListView.setAdapter(new MockExpandableListAdapter());
+ mExpandableListView.setAdapter(new MockExpandableListAdapter());
- assertEquals(0, expandableListView.getExpandableListPosition(0));
+ assertEquals(0, mExpandableListView.getExpandableListPosition(0));
// Group 0 is not expanded, position 1 is invalid
assertEquals(ExpandableListView.PACKED_POSITION_VALUE_NULL,
- expandableListView.getExpandableListPosition(1));
+ mExpandableListView.getExpandableListPosition(1));
// Position 1 becomes valid when group 0 is expanded
- expandableListView.expandGroup(0);
+ mExpandableListView.expandGroup(0);
assertEquals(ExpandableListView.getPackedPositionForChild(0, 0),
- expandableListView.getExpandableListPosition(1));
+ mExpandableListView.getExpandableListPosition(1));
// Position 2 is still invalid (only one child).
assertEquals(ExpandableListView.PACKED_POSITION_VALUE_NULL,
- expandableListView.getExpandableListPosition(2));
+ mExpandableListView.getExpandableListPosition(2));
}
+ @UiThreadTest
public void testGetFlatListPosition() {
- ExpandableListView expandableListView = new ExpandableListView(mContext);
- expandableListView.setAdapter(new MockExpandableListAdapter());
+ mExpandableListView.setAdapter(new MockExpandableListAdapter());
try {
- expandableListView.getFlatListPosition(ExpandableListView.PACKED_POSITION_VALUE_NULL);
+ mExpandableListView.getFlatListPosition(ExpandableListView.PACKED_POSITION_VALUE_NULL);
} catch (NullPointerException e) {
}
- assertEquals(0, expandableListView.getFlatListPosition(
+ assertEquals(0, mExpandableListView.getFlatListPosition(
ExpandableListView.PACKED_POSITION_TYPE_CHILD<<32));
// 0x8000000100000000L means this is a child and group position is 1.
- assertEquals(1, expandableListView.getFlatListPosition(0x8000000100000000L));
+ assertEquals(1, mExpandableListView.getFlatListPosition(0x8000000100000000L));
}
+ @UiThreadTest
public void testGetSelectedPosition() {
- ExpandableListView expandableListView = new ExpandableListView(mContext);
-
assertEquals(ExpandableListView.PACKED_POSITION_VALUE_NULL,
- expandableListView.getSelectedPosition());
+ mExpandableListView.getSelectedPosition());
- expandableListView.setAdapter(new MockExpandableListAdapter());
+ mExpandableListView.setAdapter(new MockExpandableListAdapter());
- expandableListView.setSelectedGroup(0);
- assertEquals(0, expandableListView.getSelectedPosition());
+ mExpandableListView.setSelectedGroup(0);
+ assertEquals(0, mExpandableListView.getSelectedPosition());
- expandableListView.setSelectedGroup(1);
- assertEquals(0, expandableListView.getSelectedPosition());
+ mExpandableListView.setSelectedGroup(1);
+ assertEquals(0, mExpandableListView.getSelectedPosition());
}
+ @UiThreadTest
public void testGetSelectedId() {
- ExpandableListView expandableListView = new ExpandableListView(mContext);
+ assertEquals(-1, mExpandableListView.getSelectedId());
+ mExpandableListView.setAdapter(new MockExpandableListAdapter());
- assertEquals(-1, expandableListView.getSelectedId());
- expandableListView.setAdapter(new MockExpandableListAdapter());
+ mExpandableListView.setSelectedGroup(0);
+ assertEquals(0, mExpandableListView.getSelectedId());
- expandableListView.setSelectedGroup(0);
- assertEquals(0, expandableListView.getSelectedId());
-
- expandableListView.setSelectedGroup(1);
- assertEquals(0, expandableListView.getSelectedId());
+ mExpandableListView.setSelectedGroup(1);
+ assertEquals(0, mExpandableListView.getSelectedId());
}
+ @UiThreadTest
public void testSetSelectedGroup() {
- ExpandableListView expandableListView = new ExpandableListView(mContext);
- expandableListView.setAdapter(new MockExpandableListAdapter());
+ mExpandableListView.setAdapter(new MockExpandableListAdapter());
- expandableListView.setSelectedGroup(0);
- assertEquals(0, expandableListView.getSelectedPosition());
+ mExpandableListView.setSelectedGroup(0);
+ assertEquals(0, mExpandableListView.getSelectedPosition());
- expandableListView.setSelectedGroup(1);
- assertEquals(0, expandableListView.getSelectedPosition());
+ mExpandableListView.setSelectedGroup(1);
+ assertEquals(0, mExpandableListView.getSelectedPosition());
}
+ @UiThreadTest
public void testSetSelectedChild() {
- ExpandableListView expandableListView = new ExpandableListView(mContext);
- expandableListView.setAdapter(new MockExpandableListAdapter());
+ mExpandableListView.setAdapter(new MockExpandableListAdapter());
- assertTrue(expandableListView.setSelectedChild(0, 0, false));
- assertTrue(expandableListView.setSelectedChild(0, 1, true));
+ assertTrue(mExpandableListView.setSelectedChild(0, 0, false));
+ assertTrue(mExpandableListView.setSelectedChild(0, 1, true));
}
+ @UiThreadTest
public void testIsGroupExpanded() {
- ExpandableListView expandableListView = new ExpandableListView(mContext);
- expandableListView.setAdapter(new MockExpandableListAdapter());
+ mExpandableListView.setAdapter(new MockExpandableListAdapter());
- expandableListView.expandGroup(1);
- assertFalse(expandableListView.isGroupExpanded(0));
- assertTrue(expandableListView.isGroupExpanded(1));
+ mExpandableListView.expandGroup(1);
+ assertFalse(mExpandableListView.isGroupExpanded(0));
+ assertTrue(mExpandableListView.isGroupExpanded(1));
}
public void testGetPackedPositionType() {
@@ -356,31 +451,35 @@
}
public void testSetChildIndicator() {
- ExpandableListView expandableListView = new ExpandableListView(mContext);
- expandableListView.setChildIndicator(null);
+ mExpandableListView.setChildIndicator(null);
}
public void testSetChildIndicatorBounds() {
- ExpandableListView expandableListView = new ExpandableListView(mContext);
- expandableListView.setChildIndicatorBounds(10, 10);
+ mExpandableListView.setChildIndicatorBounds(10, 20);
+ }
+
+ public void testSetChildIndicatorBoundsRelative() {
+ mExpandableListView.setChildIndicatorBoundsRelative(10, 20);
}
public void testSetGroupIndicator() {
- ExpandableListView expandableListView = new ExpandableListView(mContext);
Drawable drawable = new BitmapDrawable();
- expandableListView.setGroupIndicator(drawable);
+ mExpandableListView.setGroupIndicator(drawable);
}
public void testSetIndicatorBounds() {
- ExpandableListView expandableListView = new ExpandableListView(mContext);
- expandableListView.setIndicatorBounds(10,10);
+ mExpandableListView.setIndicatorBounds(10, 30);
+ }
+
+ public void testSetIndicatorBoundsRelative() {
+ mExpandableListView.setIndicatorBoundsRelative(10, 30);
}
public void testOnSaveInstanceState() {
- ExpandableListView src = new ExpandableListView(mContext);
+ ExpandableListView src = new ExpandableListView(mActivity);
Parcelable p1 = src.onSaveInstanceState();
- ExpandableListView dest = new ExpandableListView(mContext);
+ ExpandableListView dest = new ExpandableListView(mActivity);
dest.onRestoreInstanceState(p1);
Parcelable p2 = dest.onSaveInstanceState();
@@ -389,11 +488,17 @@
}
public void testDispatchDraw() {
- MockExpandableListView expandableListView = new MockExpandableListView(mContext);
+ MockExpandableListView expandableListView = new MockExpandableListView(mActivity);
expandableListView.dispatchDraw(new Canvas());
}
private class MockExpandableListAdapter implements ExpandableListAdapter {
+ private final LayoutInflater mLayoutInflater;
+
+ public MockExpandableListAdapter() {
+ mLayoutInflater = LayoutInflater.from(mActivity);
+ }
+
public void registerDataSetObserver(DataSetObserver observer) {
}
@@ -443,12 +548,24 @@
public View getGroupView(int groupPosition, boolean isExpanded,
View convertView, ViewGroup parent) {
- return null;
+ TextView result = (TextView) convertView;
+ if (result == null) {
+ result = (TextView) mLayoutInflater.inflate(
+ R.layout.expandablelistview_group, parent, false);
+ }
+ result.setText("Group " + groupPosition);
+ return result;
}
public View getChildView(int groupPosition, int childPosition,
boolean isLastChild, View convertView, ViewGroup parent) {
- return null;
+ TextView result = (TextView) convertView;
+ if (result == null) {
+ result = (TextView) mLayoutInflater.inflate(
+ R.layout.expandablelistview_child, parent, false);
+ }
+ result.setText("Child " + childPosition);
+ return result;
}
public boolean isChildSelectable(int groupPosition, int childPosition) {
@@ -478,72 +595,6 @@
}
}
- private class MockOnGroupExpandListener implements ExpandableListView.OnGroupExpandListener {
- private boolean mCalledOnGroupExpand = false;
-
- public void onGroupExpand(int groupPosition) {
- mCalledOnGroupExpand = true;
- }
-
- public boolean hasCalledOnGroupExpand() {
- return mCalledOnGroupExpand;
- }
-
- public void reset() {
- mCalledOnGroupExpand = false;
- }
- }
-
- private class MockOnGroupCollapseListener implements
- ExpandableListView.OnGroupCollapseListener {
- private boolean mCalledOnGroupCollapse = false;
-
- public void onGroupCollapse(int groupPosition) {
- mCalledOnGroupCollapse = true;
- }
-
- public boolean hasCalledOnGroupCollapse() {
- return mCalledOnGroupCollapse;
- }
-
- public void reset() {
- mCalledOnGroupCollapse = false;
- }
- }
-
- private class MockOnItemClickListener implements OnItemClickListener {
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- }
- }
-
- private class MockOnGroupClickListener implements OnGroupClickListener {
- private boolean mCalledOnGroupClick = false;
-
- public boolean onGroupClick(ExpandableListView parent, View v,
- int groupPosition, long id) {
- mCalledOnGroupClick = true;
- return true;
- }
-
- public boolean hasCalledOnGroupClick() {
- return mCalledOnGroupClick;
- }
- }
-
- private class MockOnChildClickListener implements OnChildClickListener {
- private boolean mCalledOnChildClick = false;
-
- public boolean onChildClick(ExpandableListView parent, View v,
- int groupPosition, int childPosition, long id) {
- mCalledOnChildClick = true;
- return true;
- }
-
- public boolean hasCalledOnChildClick() {
- return mCalledOnChildClick;
- }
- }
-
private class MockExpandableListView extends ExpandableListView {
public MockExpandableListView(Context context) {
super(context);
diff --git a/tests/tests/widget/src/android/widget/cts/ExpandableListViewWithHeadersTest.java b/tests/tests/widget/src/android/widget/cts/ExpandableListViewWithHeadersTest.java
index 7f4715f..91ce4cd 100644
--- a/tests/tests/widget/src/android/widget/cts/ExpandableListViewWithHeadersTest.java
+++ b/tests/tests/widget/src/android/widget/cts/ExpandableListViewWithHeadersTest.java
@@ -39,12 +39,7 @@
super.setUp();
final ExpandableListWithHeaders activity = getActivity();
- new PollingCheck() {
- @Override
- protected boolean check() {
- return activity.hasWindowFocus();
- }
- }.run();
+ PollingCheck.waitFor(activity::hasWindowFocus);
mExpandableListView = activity.getExpandableListView();
mListUtil = new ListUtil(mExpandableListView, getInstrumentation());
}
diff --git a/tests/tests/widget/src/android/widget/cts/ExpandableListView_ExpandableListContextMenuInfoTest.java b/tests/tests/widget/src/android/widget/cts/ExpandableListView_ExpandableListContextMenuInfoTest.java
index bf5a23f..f62dcd2 100644
--- a/tests/tests/widget/src/android/widget/cts/ExpandableListView_ExpandableListContextMenuInfoTest.java
+++ b/tests/tests/widget/src/android/widget/cts/ExpandableListView_ExpandableListContextMenuInfoTest.java
@@ -18,10 +18,9 @@
package android.widget.cts;
import android.test.AndroidTestCase;
-import android.view.View;
import android.widget.ExpandableListView;
-import android.widget.ListView;
import android.widget.ExpandableListView.ExpandableListContextMenuInfo;
+import android.widget.ListView;
/**
* Test {@link ExpandableListContextMenuInfo}.
diff --git a/tests/tests/widget/src/android/widget/cts/ExpandableListWithHeaders.java b/tests/tests/widget/src/android/widget/cts/ExpandableListWithHeaders.java
index 259d443..17e8a59 100644
--- a/tests/tests/widget/src/android/widget/cts/ExpandableListWithHeaders.java
+++ b/tests/tests/widget/src/android/widget/cts/ExpandableListWithHeaders.java
@@ -22,14 +22,14 @@
import android.widget.cts.util.ExpandableListScenario;
public class ExpandableListWithHeaders extends ExpandableListScenario {
- private static final int[] sNumChildren = {1, 4, 3, 2, 6};
- private static final int sNumOfHeadersAndFooters = 12;
+ private static final int[] CHILD_COUNT = {1, 4, 3, 2, 6};
+ private static final int HEADER_FOOTER_COUNT = 12;
@Override
protected void init(ExpandableParams params) {
params.setStackFromBottom(false)
.setStartingSelectionPosition(-1)
- .setNumChildren(sNumChildren)
+ .setNumChildren(CHILD_COUNT)
.setItemScreenSizeFactor(0.14)
.setConnectAdapter(false);
}
@@ -41,13 +41,13 @@
final ExpandableListView expandableListView = getExpandableListView();
expandableListView.setItemsCanFocus(true);
- for (int i = 0; i < sNumOfHeadersAndFooters; i++) {
+ for (int i = 0; i < HEADER_FOOTER_COUNT; i++) {
Button header = new Button(this);
header.setText("Header View");
expandableListView.addHeaderView(header);
}
- for (int i = 0; i < sNumOfHeadersAndFooters; i++) {
+ for (int i = 0; i < HEADER_FOOTER_COUNT; i++) {
Button footer = new Button(this);
footer.setText("Footer View");
expandableListView.addFooterView(footer);
@@ -61,6 +61,6 @@
* @return The number of headers (and the same number of footers)
*/
public int getNumOfHeadersAndFooters() {
- return sNumOfHeadersAndFooters;
+ return HEADER_FOOTER_COUNT;
}
}
diff --git a/tests/tests/widget/src/android/widget/cts/FilterTest.java b/tests/tests/widget/src/android/widget/cts/FilterTest.java
index 7a1d111..97d75f4 100644
--- a/tests/tests/widget/src/android/widget/cts/FilterTest.java
+++ b/tests/tests/widget/src/android/widget/cts/FilterTest.java
@@ -16,14 +16,20 @@
package android.widget.cts;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Mockito.*;
import android.cts.util.PollingCheck;
-import android.cts.util.ReadElf;
import android.cts.util.TestThread;
import android.os.Looper;
import android.test.ActivityInstrumentationTestCase2;
import android.widget.Filter;
-import android.widget.Filter.FilterListener;
+
+import org.junit.Assert;
+import org.mockito.invocation.InvocationOnMock;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
public class FilterTest extends ActivityInstrumentationTestCase2<CtsActivity> {
private static final long TIME_OUT = 10000;
@@ -66,26 +72,22 @@
});
getInstrumentation().waitForIdleSync();
- new PollingCheck(TIME_OUT) {
- @Override
- protected boolean check() {
- return mMockFilter.hadPerformedFiltering();
- }
- }.run();
+ PollingCheck.waitFor(TIME_OUT, mMockFilter::hadPerformedFiltering);
assertEquals(TEST_CONSTRAINT, mMockFilter.getPerformFilteringConstraint());
- new PollingCheck(TIME_OUT) {
- @Override
- protected boolean check() {
- return mMockFilter.hadPublishedResults();
- }
- }.run();
+ PollingCheck.waitFor(TIME_OUT, mMockFilter::hadPublishedResults);
assertEquals(TEST_CONSTRAINT, mMockFilter.getPublishResultsConstraint());
assertSame(mMockFilter.getExpectResults(), mMockFilter.getResults());
}
public void testFilter2() {
- final MockFilterListener mockFilterListener = new MockFilterListener();
+ final CountDownLatch countDownLatch = new CountDownLatch(1);
+ final Filter.FilterListener mockFilterListener = mock(Filter.FilterListener.class);
+ doAnswer((InvocationOnMock invocation) -> {
+ countDownLatch.countDown();
+ return null;
+ }).when(mockFilterListener).onFilterComplete(anyInt());
+
getActivity().runOnUiThread(new Runnable() {
public void run() {
mMockFilter = new MockFilter();
@@ -94,29 +96,18 @@
});
getInstrumentation().waitForIdleSync();
- new PollingCheck(TIME_OUT) {
- @Override
- protected boolean check() {
- return mMockFilter.hadPerformedFiltering();
- }
- }.run();
+ PollingCheck.waitFor(TIME_OUT, mMockFilter::hadPerformedFiltering);
assertEquals(TEST_CONSTRAINT, mMockFilter.getPerformFilteringConstraint());
- new PollingCheck(TIME_OUT) {
- @Override
- protected boolean check() {
- return mMockFilter.hadPublishedResults();
- }
- }.run();
+ PollingCheck.waitFor(TIME_OUT, mMockFilter::hadPublishedResults);
assertEquals(TEST_CONSTRAINT, mMockFilter.getPublishResultsConstraint());
assertSame(mMockFilter.getExpectResults(), mMockFilter.getResults());
- new PollingCheck(TIME_OUT) {
- @Override
- protected boolean check() {
- return mockFilterListener.hasCalledOnFilterComplete();
- }
- }.run();
+ try {
+ countDownLatch.await(TIME_OUT, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException ie) {
+ Assert.fail(ie.toString());
+ }
}
private static class MockFilter extends Filter {
@@ -185,16 +176,4 @@
}
}
}
-
- private static class MockFilterListener implements FilterListener {
- private boolean mCalledOnFilterComplete = false;
-
- public void onFilterComplete(int count) {
- mCalledOnFilterComplete = true;
- }
-
- public boolean hasCalledOnFilterComplete() {
- return mCalledOnFilterComplete;
- }
- }
}
diff --git a/tests/tests/widget/src/android/widget/cts/FrameLayoutTest.java b/tests/tests/widget/src/android/widget/cts/FrameLayoutTest.java
index 4309dbb..ff399fc 100644
--- a/tests/tests/widget/src/android/widget/cts/FrameLayoutTest.java
+++ b/tests/tests/widget/src/android/widget/cts/FrameLayoutTest.java
@@ -16,8 +16,8 @@
package android.widget.cts;
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
+import static org.mockito.Matchers.*;
+import static org.mockito.Mockito.*;
import android.app.Activity;
import android.app.Instrumentation;
@@ -25,15 +25,15 @@
import android.content.res.ColorStateList;
import android.cts.util.PollingCheck;
import android.cts.util.WidgetTestUtils;
-import android.graphics.Canvas;
import android.graphics.Color;
-import android.graphics.ColorFilter;
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.Region;
import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.test.ActivityInstrumentationTestCase2;
+import android.test.suitebuilder.annotation.SmallTest;
import android.util.AttributeSet;
import android.util.Xml;
import android.view.Gravity;
@@ -44,11 +44,14 @@
import android.widget.FrameLayout.LayoutParams;
import android.widget.LinearLayout;
import android.widget.TextView;
-import android.widget.cts.R;
-import android.widget.cts.util.ViewTestUtils;
+
+import org.mockito.ArgumentCaptor;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
+@SmallTest
public class FrameLayoutTest extends ActivityInstrumentationTestCase2<FrameLayoutCtsActivity> {
private Activity mActivity;
private Instrumentation mInstrumentation;
@@ -87,30 +90,25 @@
assertTrue(mFrameLayout.getWidth() > foreground.getIntrinsicWidth());
assertNull(mFrameLayout.getForeground());
- ViewTestUtils.runOnMainAndDrawSync(mInstrumentation, mFrameLayout,
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mFrameLayout,
() -> mFrameLayout.setForeground(foreground));
assertSame(foreground, mFrameLayout.getForeground());
// check the default gravity FILL, it completely fills its container
assertTrue(foreground.isVisible());
final Rect rect = foreground.getBounds();
// foreground has been stretched
- new PollingCheck() {
- @Override
- protected boolean check() {
- return mFrameLayout.getHeight() == rect.bottom - rect.top;
- }
- }.run();
+ PollingCheck.waitFor(() -> mFrameLayout.getHeight() == rect.bottom - rect.top);
assertEquals(mFrameLayout.getWidth(), rect.right - rect.left);
// should get a new foreground again, because former foreground has been stretched
final BitmapDrawable newForeground =
- (BitmapDrawable) mActivity.getResources().getDrawable(R.drawable.size_48x48);
+ (BitmapDrawable) mActivity.getDrawable(R.drawable.size_48x48);
compareScaledPixels(48, newForeground.getIntrinsicHeight());
compareScaledPixels(48, newForeground.getIntrinsicWidth());
assertTrue(mFrameLayout.getHeight() > newForeground.getIntrinsicHeight());
assertTrue(mFrameLayout.getWidth() > foreground.getIntrinsicWidth());
- ViewTestUtils.runOnMainAndDrawSync(mInstrumentation, mFrameLayout, () -> {
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mFrameLayout, () -> {
mFrameLayout.setForeground(newForeground);
mFrameLayout.setForegroundGravity(Gravity.CENTER);
});
@@ -128,7 +126,7 @@
final LinearLayout container =
(LinearLayout) mActivity.findViewById(R.id.framelayout_container);
final Drawable foreground = mActivity.getResources().getDrawable(R.drawable.size_48x48);
- ViewTestUtils.runOnMainAndDrawSync(mInstrumentation, mFrameLayout, () -> {
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mFrameLayout, () -> {
mFrameLayout.setForeground(foreground);
mFrameLayout.setForegroundGravity(Gravity.CENTER);
});
@@ -136,7 +134,7 @@
Region region = new Region(foreground.getBounds());
assertTrue(mFrameLayout.gatherTransparentRegion(region));
- ViewTestUtils.runOnMainAndDrawSync(mInstrumentation, mFrameLayout,
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mFrameLayout,
() -> container.requestTransparentRegion(mFrameLayout));
mInstrumentation.waitForIdleSync();
region = new Region(foreground.getBounds());
@@ -156,7 +154,7 @@
assertEquals(textView.getMeasuredWidth(), frameLayout.getMeasuredWidth());
// measureAll is false and text view is GONE, text view will NOT be measured
- mActivity.runOnUiThread(() -> {
+ mInstrumentation.runOnMainSync(() -> {
textView.setVisibility(View.GONE);
frameLayout.requestLayout();
});
@@ -169,11 +167,12 @@
assertEquals(button.getMeasuredWidth(), frameLayout.getMeasuredWidth());
// measureAll is true and text view is GONE, text view will be measured
- mActivity.runOnUiThread(() -> {
+ mInstrumentation.runOnMainSync(() -> {
frameLayout.setMeasureAllChildren(true);
frameLayout.requestLayout();
});
mInstrumentation.waitForIdleSync();
+ assertTrue(frameLayout.getMeasureAllChildren());
assertTrue(frameLayout.getConsiderGoneChildrenWhenMeasuring());
assertEquals(textView.getMeasuredHeight(), frameLayout.getMeasuredHeight());
assertEquals(textView.getMeasuredWidth(), frameLayout.getMeasuredWidth());
@@ -285,21 +284,25 @@
assertEquals("Foreground tint mode inflated correctly",
PorterDuff.Mode.SRC_OVER, inflatedView.getForegroundTintMode());
- MockDrawable foreground = new MockDrawable();
+ final Drawable foreground = spy(new ColorDrawable());
FrameLayout view = new FrameLayout(mActivity);
view.setForeground(foreground);
- assertFalse("No foreground tint applied by default", foreground.hasCalledSetTint());
+ verify(foreground, never()).setTintList(any(ColorStateList.class));
- view.setForegroundTintList(ColorStateList.valueOf(Color.WHITE));
- assertTrue("Foreground tint applied when setForegroundTintList() called after setForeground()",
- foreground.hasCalledSetTint());
+ view.setForegroundTintList(ColorStateList.valueOf(Color.RED));
+ final ArgumentCaptor<ColorStateList> colorStateListCaptor =
+ ArgumentCaptor.forClass(ColorStateList.class);
+ verify(foreground, times(1)).setTintList(colorStateListCaptor.capture());
+ assertEquals(1, colorStateListCaptor.getValue().getColors().length);
+ assertEquals(Color.RED, colorStateListCaptor.getValue().getColors()[0]);
- foreground.reset();
+ reset(foreground);
view.setForeground(null);
view.setForeground(foreground);
- assertTrue("Foreground tint applied when setForegroundTintList() called before setForeground()",
- foreground.hasCalledSetTint());
+ verify(foreground, times(1)).setTintList(colorStateListCaptor.capture());
+ assertEquals(1, colorStateListCaptor.getValue().getColors().length);
+ assertEquals(Color.RED, colorStateListCaptor.getValue().getColors()[0]);
}
private static void assertCenterAligned(View container, Drawable drawable) {
@@ -319,38 +322,6 @@
return Xml.asAttributeSet(parser);
}
- private static class MockDrawable extends Drawable {
- private boolean mCalledSetTint = false;
-
- @Override
- public void draw(Canvas canvas) {}
-
- @Override
- public void setAlpha(int alpha) {}
-
- @Override
- public void setColorFilter(ColorFilter cf) {}
-
- @Override
- public void setTintList(ColorStateList tint) {
- super.setTintList(tint);
- mCalledSetTint = true;
- }
-
- @Override
- public int getOpacity() {
- return 0;
- }
-
- public boolean hasCalledSetTint() {
- return mCalledSetTint;
- }
-
- public void reset() {
- mCalledSetTint = false;
- }
- }
-
private static class MyFrameLayout extends FrameLayout {
public MyFrameLayout(Context context) {
super(context);
diff --git a/tests/tests/widget/src/android/widget/cts/FrameLayout_LayoutParamsTest.java b/tests/tests/widget/src/android/widget/cts/FrameLayout_LayoutParamsTest.java
index 5774b53..1b8f253 100644
--- a/tests/tests/widget/src/android/widget/cts/FrameLayout_LayoutParamsTest.java
+++ b/tests/tests/widget/src/android/widget/cts/FrameLayout_LayoutParamsTest.java
@@ -16,21 +16,19 @@
package android.widget.cts;
-import android.view.Gravity;
-import android.widget.cts.R;
-
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
import android.cts.util.WidgetTestUtils;
import android.test.AndroidTestCase;
import android.util.AttributeSet;
import android.util.Xml;
+import android.view.Gravity;
import android.view.ViewGroup;
import android.view.ViewGroup.MarginLayoutParams;
import android.widget.FrameLayout;
import android.widget.FrameLayout.LayoutParams;
+import android.widget.cts.R;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
diff --git a/tests/tests/widget/src/android/widget/cts/GalleryCtsActivity.java b/tests/tests/widget/src/android/widget/cts/GalleryCtsActivity.java
index 8039f47..8b80a96 100644
--- a/tests/tests/widget/src/android/widget/cts/GalleryCtsActivity.java
+++ b/tests/tests/widget/src/android/widget/cts/GalleryCtsActivity.java
@@ -16,8 +16,6 @@
package android.widget.cts;
-import android.widget.cts.R;
-
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
@@ -26,6 +24,7 @@
import android.widget.BaseAdapter;
import android.widget.Gallery;
import android.widget.ImageView;
+import android.widget.cts.R;
/**
* A minimal application for {@link Gallery} test.
diff --git a/tests/tests/widget/src/android/widget/cts/GalleryTest.java b/tests/tests/widget/src/android/widget/cts/GalleryTest.java
index dd39461..5394319 100644
--- a/tests/tests/widget/src/android/widget/cts/GalleryTest.java
+++ b/tests/tests/widget/src/android/widget/cts/GalleryTest.java
@@ -16,40 +16,29 @@
package android.widget.cts;
-import android.widget.cts.R;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
import android.app.Activity;
import android.app.Instrumentation;
-import android.content.ComponentName;
import android.content.Context;
-import android.content.Intent;
import android.cts.util.WidgetTestUtils;
-import android.graphics.drawable.Drawable;
import android.os.SystemClock;
import android.test.ActivityInstrumentationTestCase2;
import android.test.UiThreadTest;
import android.test.ViewAsserts;
import android.util.AttributeSet;
import android.util.Xml;
-import android.view.ContextMenu;
import android.view.Gravity;
import android.view.KeyEvent;
-import android.view.MenuItem;
-import android.view.SubMenu;
import android.view.View;
import android.view.ViewGroup;
-import android.view.ContextMenu.ContextMenuInfo;
-import android.view.View.OnCreateContextMenuListener;
import android.view.ViewGroup.LayoutParams;
import android.view.animation.Transformation;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.Gallery;
import android.widget.ImageView;
-import android.widget.AdapterView.OnItemSelectedListener;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
@@ -348,37 +337,4 @@
R.drawable.testimage,
};
}
-
- private static class MockOnItemSelectedListener implements OnItemSelectedListener {
- private boolean mIsItemSelected;
- private boolean mNothingSelected;
- private int mItemSelectedCalledCount;
-
- public boolean isItemSelected() {
- return mIsItemSelected;
- }
-
- public boolean hasNothingSelected() {
- return mNothingSelected;
- }
-
- public int getItemSelectedCalledCount() {
- return mItemSelectedCalledCount;
- }
-
- public void reset() {
- mIsItemSelected = false;
- mNothingSelected = true;
- mItemSelectedCalledCount = 0;
- }
-
- public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
- mIsItemSelected = true;
- mItemSelectedCalledCount++;
- }
-
- public void onNothingSelected(AdapterView<?> parent) {
- mNothingSelected = true;
- }
- }
}
diff --git a/tests/tests/widget/src/android/widget/cts/Gallery_LayoutParamsTest.java b/tests/tests/widget/src/android/widget/cts/Gallery_LayoutParamsTest.java
index eacb676..daa40b4 100644
--- a/tests/tests/widget/src/android/widget/cts/Gallery_LayoutParamsTest.java
+++ b/tests/tests/widget/src/android/widget/cts/Gallery_LayoutParamsTest.java
@@ -16,15 +16,13 @@
package android.widget.cts;
-import android.widget.cts.R;
-
-
-import org.xmlpull.v1.XmlPullParserException;
-
import android.content.res.XmlResourceParser;
import android.cts.util.WidgetTestUtils;
import android.test.AndroidTestCase;
import android.widget.Gallery.LayoutParams;
+import android.widget.cts.R;
+
+import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
diff --git a/tests/tests/widget/src/android/widget/cts/GridLayoutTest.java b/tests/tests/widget/src/android/widget/cts/GridLayoutTest.java
index 65e86b3..83dcfd0 100644
--- a/tests/tests/widget/src/android/widget/cts/GridLayoutTest.java
+++ b/tests/tests/widget/src/android/widget/cts/GridLayoutTest.java
@@ -16,6 +16,8 @@
package android.widget.cts;
+import static android.widget.GridLayout.spec;
+
import android.content.Context;
import android.test.ActivityInstrumentationTestCase;
import android.util.AttributeSet;
@@ -28,10 +30,8 @@
import android.widget.GridLayout;
import android.widget.TextView;
import android.widget.cts.R;
-import org.xmlpull.v1.XmlPullParser;
-import static android.view.ViewGroup.LAYOUT_MODE_OPTICAL_BOUNDS;
-import static android.widget.GridLayout.spec;
+import org.xmlpull.v1.XmlPullParser;
/**
* Test {@link android.widget.GridLayout}.
diff --git a/tests/tests/widget/src/android/widget/cts/GridViewCtsActivity.java b/tests/tests/widget/src/android/widget/cts/GridViewCtsActivity.java
index 7b2e7a3..47ad5d8 100644
--- a/tests/tests/widget/src/android/widget/cts/GridViewCtsActivity.java
+++ b/tests/tests/widget/src/android/widget/cts/GridViewCtsActivity.java
@@ -16,17 +16,16 @@
package android.widget.cts;
-import android.widget.cts.R;
-
import android.app.Activity;
import android.os.Bundle;
+import android.widget.GridView;
/**
* A minimal application for {@link GridView} test.
*/
public class GridViewCtsActivity extends Activity {
/**
- * Called with the activity is first created.
+ * Called when the activity is first created.
*/
@Override
public void onCreate(Bundle savedInstanceState) {
diff --git a/tests/tests/widget/src/android/widget/cts/GridViewTest.java b/tests/tests/widget/src/android/widget/cts/GridViewTest.java
index 7b83416..44b1741 100644
--- a/tests/tests/widget/src/android/widget/cts/GridViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/GridViewTest.java
@@ -16,23 +16,23 @@
package android.widget.cts;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.widget.cts.R;
-
-
-import org.xmlpull.v1.XmlPullParser;
+import static org.mockito.Matchers.*;
+import static org.mockito.Mockito.*;
import android.app.Activity;
import android.app.Instrumentation;
import android.content.Context;
import android.cts.util.PollingCheck;
+import android.cts.util.WidgetTestUtils;
import android.database.DataSetObservable;
import android.database.DataSetObserver;
import android.graphics.Rect;
import android.test.ActivityInstrumentationTestCase;
-import android.test.TouchUtils;
import android.test.UiThreadTest;
import android.test.ViewAsserts;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.test.suitebuilder.annotation.SmallTest;
import android.util.AttributeSet;
import android.util.Xml;
import android.view.Gravity;
@@ -47,12 +47,16 @@
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.ListAdapter;
-import android.widget.AdapterView.OnItemClickListener;
-import android.widget.cts.util.ViewTestUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
/**
* Test {@link GridView}.
*/
+@SmallTest
public class GridViewTest extends ActivityInstrumentationTestCase<GridViewCtsActivity> {
private GridView mGridView;
private Activity mActivity;
@@ -62,22 +66,15 @@
super("android.widget.cts", GridViewCtsActivity.class);
}
- private GridView findGridViewById(int id) {
- return (GridView) mActivity.findViewById(id);
- }
-
@Override
protected void setUp() throws Exception {
super.setUp();
- mGridView = null;
- mActivity = getActivity();
- new PollingCheck() {
- @Override
- protected boolean check() {
- return mActivity.hasWindowFocus();
- }
- }.run();
+
mInstrumentation = getInstrumentation();
+ mActivity = getActivity();
+ mGridView = (GridView) mActivity.findViewById(R.id.gridview);
+
+ PollingCheck.waitFor(mActivity::hasWindowFocus);
}
public void testConstructor() {
@@ -87,6 +84,14 @@
new GridView(mActivity, null, android.R.attr.gridViewStyle);
+ new GridView(mActivity, null, 0, android.R.style.Widget_DeviceDefault_GridView);
+
+ new GridView(mActivity, null, 0, android.R.style.Widget_DeviceDefault_Light_GridView);
+
+ new GridView(mActivity, null, 0, android.R.style.Widget_Material_GridView);
+
+ new GridView(mActivity, null, 0, android.R.style.Widget_Material_Light_GridView);
+
XmlPullParser parser = mActivity.getResources().getXml(R.layout.gridview_layout);
AttributeSet attrs = Xml.asAttributeSet(parser);
new GridView(mActivity, attrs);
@@ -111,8 +116,8 @@
}
}
+ @UiThreadTest
public void testAccessAdapter() {
- mGridView = new GridView(mActivity);
// set Adapter
ImageAdapter adapter = new ImageAdapter(mActivity);
mGridView.setAdapter(adapter);
@@ -122,8 +127,8 @@
assertNull(mGridView.getAdapter());
}
+ @UiThreadTest
public void testSetSelection() {
- mGridView = new GridView(mActivity);
mGridView.setSelection(0);
assertEquals(0, mGridView.getSelectedItemPosition());
@@ -136,10 +141,10 @@
public void testPressKey() {
final int NUM_COLUMNS = 3;
- mGridView = findGridViewById(R.id.gridview);
- MockOnItemClickListener listener = new MockOnItemClickListener();
- mGridView.setOnItemClickListener(listener);
+ GridView.OnItemClickListener mockItemClickListener =
+ mock(GridView.OnItemClickListener.class);
+ mGridView.setOnItemClickListener(mockItemClickListener);
// this test case can not be ran in UI thread.
mActivity.runOnUiThread(new Runnable() {
@@ -172,20 +177,19 @@
mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_DPAD_DOWN);
assertEquals(NUM_COLUMNS, mGridView.getSelectedItemPosition());
- assertFalse(listener.hasOnItemClickCalled());
+ verify(mockItemClickListener, never()).onItemClick(any(AdapterView.class), any(View.class),
+ anyInt(), anyLong());
mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_DPAD_CENTER);
- assertTrue(listener.hasOnItemClickCalled());
+ verify(mockItemClickListener, times(1)).onItemClick(eq(mGridView), any(View.class),
+ eq(NUM_COLUMNS), eq((long) NUM_COLUMNS));
- listener.reset();
- assertFalse(listener.hasOnItemClickCalled());
+ reset(mockItemClickListener);
mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_ENTER);
- assertTrue(listener.hasOnItemClickCalled());
+ verify(mockItemClickListener, times(1)).onItemClick(eq(mGridView), any(View.class),
+ eq(NUM_COLUMNS), eq((long) NUM_COLUMNS));
}
public void testSetGravity() {
- mGridView = findGridViewById(R.id.gridview);
-
- View child;
final int NUM_COLUMNS = 1;
// this test case can not be ran in UI thread.
mActivity.runOnUiThread(new Runnable() {
@@ -207,8 +211,8 @@
});
mInstrumentation.waitForIdleSync();
- child = mGridView.getChildAt(0); // get the first view.
- ViewAsserts.assertHorizontalCenterAligned(mGridView, child);
+ assertEquals(Gravity.CENTER_HORIZONTAL, mGridView.getGravity());
+ ViewAsserts.assertHorizontalCenterAligned(mGridView, mGridView.getChildAt(0));
mActivity.runOnUiThread(new Runnable() {
public void run() {
@@ -219,8 +223,9 @@
});
mInstrumentation.waitForIdleSync();
- child = mGridView.getChildAt(0); // get the first view.
- ViewAsserts.assertLeftAligned(mGridView, child, mGridView.getListPaddingLeft());
+ assertEquals(Gravity.LEFT, mGridView.getGravity());
+ ViewAsserts.assertLeftAligned(mGridView, mGridView.getChildAt(0),
+ mGridView.getListPaddingLeft());
mActivity.runOnUiThread(new Runnable() {
public void run() {
@@ -231,41 +236,34 @@
});
mInstrumentation.waitForIdleSync();
- child = mGridView.getChildAt(0); // get the first view.
- ViewAsserts.assertRightAligned(mGridView, child, mGridView.getListPaddingRight());
+ assertEquals(Gravity.RIGHT, mGridView.getGravity());
+ ViewAsserts.assertRightAligned(mGridView, mGridView.getChildAt(0),
+ mGridView.getListPaddingRight());
}
- public void testSetHorizontalSpacing() {
- testSetHorizontalSpacing(View.LAYOUT_DIRECTION_LTR);
+ public void testAccessHorizontalSpacing() {
+ verifyAccessHorizontalSpacing(View.LAYOUT_DIRECTION_LTR);
}
- public void testSetHorizontalSpacingRTL() {
- testSetHorizontalSpacing(View.LAYOUT_DIRECTION_RTL);
+ public void testAccessHorizontalSpacingRTL() {
+ verifyAccessHorizontalSpacing(View.LAYOUT_DIRECTION_RTL);
}
- public void testSetHorizontalSpacing(final int layoutDir) {
- mGridView = findGridViewById(R.id.gridview);
- mActivity.runOnUiThread(new Runnable() {
- @Override
- public void run() {
- mGridView.setLayoutDirection(layoutDir);
- }
- });
+ private void verifyAccessHorizontalSpacing(final int layoutDir) {
+ mActivity.runOnUiThread(() -> mGridView.setLayoutDirection(layoutDir));
mGridView.setStretchMode(GridView.NO_STRETCH);
// Number of columns should be big enough, otherwise the
// horizontal spacing cannot be correctly verified.
mGridView.setNumColumns(28);
-
- // this test case can not be ran in UI thread.
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- mGridView.setAdapter(new MockGridViewAdapter(3));
- mGridView.setHorizontalSpacing(0);
- }
+ mActivity.runOnUiThread(() -> {
+ mGridView.setAdapter(new MockGridViewAdapter(3));
+ mGridView.setHorizontalSpacing(0);
});
mInstrumentation.waitForIdleSync();
+ assertEquals(0, mGridView.getRequestedHorizontalSpacing());
+ assertEquals(0, mGridView.getHorizontalSpacing());
View child0 = mGridView.getChildAt(0);
View child1 = mGridView.getChildAt(1);
if (layoutDir == View.LAYOUT_DIRECTION_LTR) {
@@ -274,13 +272,11 @@
assertEquals(0, child0.getLeft() - child1.getRight());
}
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- mGridView.setHorizontalSpacing(5);
- }
- });
+ mActivity.runOnUiThread(() -> mGridView.setHorizontalSpacing(5));
mInstrumentation.waitForIdleSync();
+ assertEquals(5, mGridView.getRequestedHorizontalSpacing());
+ assertEquals(5, mGridView.getHorizontalSpacing());
child0 = mGridView.getChildAt(0);
child1 = mGridView.getChildAt(1);
if (layoutDir == View.LAYOUT_DIRECTION_LTR) {
@@ -290,36 +286,28 @@
}
}
- public void testSetVerticalSpacing() {
- mGridView = findGridViewById(R.id.gridview);
-
- // this test case can not be ran in UI thread.
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- mGridView.setAdapter(new MockGridViewAdapter(3));
- mGridView.setVerticalSpacing(0);
- }
+ public void testAccessVerticalSpacing() {
+ mActivity.runOnUiThread(() -> {
+ mGridView.setAdapter(new MockGridViewAdapter(3));
+ mGridView.setVerticalSpacing(0);
});
mInstrumentation.waitForIdleSync();
+ assertEquals(0, mGridView.getVerticalSpacing());
View child0 = mGridView.getChildAt(0);
View child1 = mGridView.getChildAt(1);
assertEquals(0, child1.getTop() - child0.getBottom());
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- mGridView.setVerticalSpacing(5);
- }
- });
+ mActivity.runOnUiThread(() -> mGridView.setVerticalSpacing(5));
mInstrumentation.waitForIdleSync();
+ assertEquals(5, mGridView.getVerticalSpacing());
child0 = mGridView.getChildAt(0);
child1 = mGridView.getChildAt(1);
assertEquals(5, child1.getTop() - child0.getBottom());
}
public void testAccessStretchMode() {
- mGridView = findGridViewById(R.id.gridview);
View child;
final int NUM_COLUMNS = 8;
@@ -417,8 +405,6 @@
}
public void testSetNumColumns() {
- mGridView = findGridViewById(R.id.gridview);
-
// this test case can not be ran in UI thread.
mActivity.runOnUiThread(new Runnable() {
public void run() {
@@ -459,13 +445,12 @@
}
}
+ public void testDefaultNumColumns() {
+ final GridView gridView = new GridView(mActivity);
+ assertEquals(gridView.getNumColumns(), GridView.AUTO_FIT);
+ }
+
public void testGetNumColumns() {
- mGridView = new GridView(mActivity);
-
- assertEquals(mGridView.getNumColumns(), GridView.AUTO_FIT);
-
- mGridView = findGridViewById(R.id.gridview);
-
mActivity.runOnUiThread(new Runnable() {
public void run() {
mGridView.setAdapter(new MockGridViewAdapter(10));
@@ -532,36 +517,33 @@
// Do not test it. It's implementation detail.
}
- public void testSetColumnWidth() {
- mGridView = findGridViewById(R.id.gridview);
-
- // this test case can not be ran in UI thread.
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- mGridView.setAdapter(new MockGridViewAdapter(10));
- mGridView.setNumColumns(GridView.AUTO_FIT);
- mGridView.setHorizontalSpacing(0);
- mGridView.setVerticalSpacing(0);
- mGridView.setColumnWidth(0);
- }
+ public void testAccessColumnWidth() {
+ mActivity.runOnUiThread(() -> {
+ mGridView.setAdapter(new MockGridViewAdapter(10));
+ mGridView.setNumColumns(GridView.AUTO_FIT);
+ mGridView.setHorizontalSpacing(0);
+ mGridView.setVerticalSpacing(0);
+ mGridView.setColumnWidth(0);
});
mInstrumentation.waitForIdleSync();
// Verify whether column number equals 2.
+ assertEquals(0, mGridView.getRequestedColumnWidth());
+ assertEquals(mGridView.getWidth() / 2, mGridView.getColumnWidth());
View child0 = mGridView.getChildAt(0);
View child1 = mGridView.getChildAt(1);
View child2 = mGridView.getChildAt(2);
assertEquals(child0.getBottom(), child1.getBottom());
assertEquals(child0.getLeft(), child2.getLeft());
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- mGridView.setNumColumns(GridView.AUTO_FIT);
- mGridView.setColumnWidth(Integer.MAX_VALUE);
- }
+ mActivity.runOnUiThread(() -> {
+ mGridView.setNumColumns(GridView.AUTO_FIT);
+ mGridView.setColumnWidth(Integer.MAX_VALUE);
});
mInstrumentation.waitForIdleSync();
+ assertEquals(Integer.MAX_VALUE, mGridView.getRequestedColumnWidth());
+ assertEquals(mGridView.getWidth(), mGridView.getColumnWidth());
child0 = mGridView.getChildAt(0);
child1 = mGridView.getChildAt(1);
assertEquals(child0.getBottom(), child1.getTop());
@@ -570,20 +552,19 @@
@MediumTest
public void testFullyDetachUnusedViewOnScroll() {
- mGridView = findGridViewById(R.id.gridview);
final AttachDetachAwareView theView = new AttachDetachAwareView(mActivity);
- ViewTestUtils.runOnMainAndDrawSync(getInstrumentation(), mGridView, () -> {
+ WidgetTestUtils.runOnMainAndDrawSync(getInstrumentation(), mGridView, () -> {
mGridView.setAdapter(new DummyAdapter(1000, theView));
});
assertEquals("test sanity", 1, theView.mOnAttachCount);
assertEquals("test sanity", 0, theView.mOnDetachCount);
- ViewTestUtils.runOnMainAndDrawSync(getInstrumentation(), mGridView, () -> {
+ WidgetTestUtils.runOnMainAndDrawSync(getInstrumentation(), mGridView, () -> {
mGridView.scrollListBy(mGridView.getHeight() * 2);
});
assertNull("test sanity, unused view should be removed", theView.getParent());
assertEquals("unused view should be detached", 1, theView.mOnDetachCount);
assertFalse(theView.isTemporarilyDetached());
- ViewTestUtils.runOnMainAndDrawSync(getInstrumentation(), mGridView, () -> {
+ WidgetTestUtils.runOnMainAndDrawSync(getInstrumentation(), mGridView, () -> {
mGridView.scrollListBy(-mGridView.getHeight() * 2);
// listview limits scroll to 1 page which is why we call it twice here.
mGridView.scrollListBy(-mGridView.getHeight() * 2);
@@ -596,20 +577,19 @@
@MediumTest
public void testFullyDetachUnusedViewOnReLayout() {
- mGridView = findGridViewById(R.id.gridview);
final AttachDetachAwareView theView = new AttachDetachAwareView(mActivity);
- ViewTestUtils.runOnMainAndDrawSync(getInstrumentation(), mGridView, () -> {
+ WidgetTestUtils.runOnMainAndDrawSync(getInstrumentation(), mGridView, () -> {
mGridView.setAdapter(new DummyAdapter(1000, theView));
});
assertEquals("test sanity", 1, theView.mOnAttachCount);
assertEquals("test sanity", 0, theView.mOnDetachCount);
- ViewTestUtils.runOnMainAndDrawSync(getInstrumentation(), mGridView, () -> {
+ WidgetTestUtils.runOnMainAndDrawSync(getInstrumentation(), mGridView, () -> {
mGridView.setSelection(800);
});
assertNull("test sanity, unused view should be removed", theView.getParent());
assertEquals("unused view should be detached", 1, theView.mOnDetachCount);
assertFalse(theView.isTemporarilyDetached());
- ViewTestUtils.runOnMainAndDrawSync(getInstrumentation(), mGridView, () -> {
+ WidgetTestUtils.runOnMainAndDrawSync(getInstrumentation(), mGridView, () -> {
mGridView.setSelection(0);
});
assertNotNull("test sanity, view should be re-added", theView.getParent());
@@ -620,9 +600,8 @@
@MediumTest
public void testFullyDetachUnusedViewOnScrollForFocus() {
- mGridView = findGridViewById(R.id.gridview);
final AttachDetachAwareView theView = new AttachDetachAwareView(mActivity);
- ViewTestUtils.runOnMainAndDrawSync(getInstrumentation(), mGridView, () -> {
+ WidgetTestUtils.runOnMainAndDrawSync(getInstrumentation(), mGridView, () -> {
mGridView.setAdapter(new DummyAdapter(1000, theView));
});
assertEquals("test sanity", 1, theView.mOnAttachCount);
@@ -630,33 +609,72 @@
while(theView.getParent() != null) {
assertEquals("the view should NOT be detached", 0, theView.mOnDetachCount);
sendKeys(KeyEvent.KEYCODE_DPAD_DOWN);
- ViewTestUtils.runOnMainAndDrawSync(getInstrumentation(), mGridView, null);
+ WidgetTestUtils.runOnMainAndDrawSync(getInstrumentation(), mGridView, null);
}
assertEquals("the view should be detached", 1, theView.mOnDetachCount);
assertFalse(theView.isTemporarilyDetached());
while(theView.getParent() == null) {
sendKeys(KeyEvent.KEYCODE_DPAD_UP);
- ViewTestUtils.runOnMainAndDrawSync(getInstrumentation(), mGridView, null);
+ WidgetTestUtils.runOnMainAndDrawSync(getInstrumentation(), mGridView, null);
}
assertEquals("the view should be re-attached", 2, theView.mOnAttachCount);
- assertEquals("the view should not recieve another detach", 1, theView.mOnDetachCount);
+ assertEquals("the view should not receive another detach", 1, theView.mOnDetachCount);
assertFalse(theView.isTemporarilyDetached());
}
- private static class MockGridView extends GridView {
- private boolean mCalledOnMeasure = false;
- private boolean mCalledOnFocusChanged = false;
+ @LargeTest
+ public void testSmoothScrollByOffset() {
+ final int itemCount = 300;
+ mActivity.runOnUiThread(() -> {
+ mGridView.setAdapter(new MockGridViewAdapter(itemCount));
+ mGridView.setNumColumns(GridView.AUTO_FIT);
+ mGridView.setHorizontalSpacing(0);
+ mGridView.setVerticalSpacing(0);
+ mGridView.setColumnWidth(Integer.MAX_VALUE);
+ });
+ mInstrumentation.waitForIdleSync();
- public boolean hasCalledOnMeasure() {
- return mCalledOnMeasure;
+ assertEquals(0, mGridView.getFirstVisiblePosition());
+
+ // Register a scroll listener on our GridView. The listener will notify our latch
+ // when the "target" item comes into view. If that never happens, the latch will
+ // time out and fail the test.
+ final CountDownLatch latch = new CountDownLatch(1);
+ final int positionToScrollTo = itemCount - 10;
+ mGridView.setOnScrollListener(new AbsListView.OnScrollListener() {
+ @Override
+ public void onScrollStateChanged(AbsListView view, int scrollState) {
+ }
+
+ @Override
+ public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
+ int totalItemCount) {
+ if ((positionToScrollTo >= firstVisibleItem) &&
+ (positionToScrollTo <= (firstVisibleItem + visibleItemCount))) {
+ latch.countDown();
+ }
+ }
+ });
+ int offset = positionToScrollTo - mGridView.getLastVisiblePosition();
+ mActivity.runOnUiThread(() -> mGridView.smoothScrollByOffset(offset));
+
+ boolean result = false;
+ try {
+ result = latch.await(20, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ // ignore
}
+ assertTrue("Timed out while waiting for the target view to be scrolled into view", result);
+ }
+
+ private static class MockGridView extends GridView {
+ private boolean mCalledOnFocusChanged = false;
public boolean hasCalledOnFocusChanged() {
return mCalledOnFocusChanged;
}
public void reset() {
- mCalledOnMeasure = false;
mCalledOnFocusChanged = false;
}
@@ -684,48 +702,11 @@
}
@Override
- protected int computeVerticalScrollExtent() {
- return super.computeVerticalScrollExtent();
- }
-
- @Override
- protected int computeVerticalScrollOffset() {
- return super.computeVerticalScrollOffset();
- }
-
- @Override
- protected int computeVerticalScrollRange() {
- return super.computeVerticalScrollRange();
- }
-
- @Override
protected void onFocusChanged(boolean gainFocus, int direction,
Rect previouslyFocusedRect) {
mCalledOnFocusChanged = true;
super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
}
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- mCalledOnMeasure = true;
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- }
- }
-
- class MockOnItemClickListener implements OnItemClickListener {
- private boolean mOnItemClickCalled = false;
-
- public boolean hasOnItemClickCalled() {
- return mOnItemClickCalled;
- }
-
- public void reset() {
- mOnItemClickCalled = false;
- }
-
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- mOnItemClickCalled = true;
- }
}
private class MockGridViewAdapter implements ListAdapter, Filterable {
@@ -735,10 +716,6 @@
mCount = count;
}
- MockGridViewAdapter() {
- this(1);
- }
-
public boolean areAllItemsEnabled() {
return true;
}
diff --git a/tests/tests/widget/src/android/widget/cts/HeaderViewListAdapterTest.java b/tests/tests/widget/src/android/widget/cts/HeaderViewListAdapterTest.java
index 2b92a4d..b57dc77 100644
--- a/tests/tests/widget/src/android/widget/cts/HeaderViewListAdapterTest.java
+++ b/tests/tests/widget/src/android/widget/cts/HeaderViewListAdapterTest.java
@@ -16,6 +16,8 @@
package android.widget.cts;
+import static org.mockito.Mockito.*;
+
import android.content.Context;
import android.database.DataSetObserver;
import android.test.InstrumentationTestCase;
@@ -334,21 +336,21 @@
HeaderViewFullAdapter fullAdapter = new HeaderViewFullAdapter();
HeaderViewListAdapter headerViewListAdapter =
new HeaderViewListAdapter(null, null, fullAdapter);
- DataSetObserver observer = new HeaderViewDataSetObserver();
- headerViewListAdapter.registerDataSetObserver(observer);
- assertSame(observer, fullAdapter.getDataSetObserver());
+ DataSetObserver mockDataSetObserver = mock(DataSetObserver.class);
+ headerViewListAdapter.registerDataSetObserver(mockDataSetObserver);
+ assertSame(mockDataSetObserver, fullAdapter.getDataSetObserver());
}
public void testUnregisterDataSetObserver() {
HeaderViewFullAdapter fullAdapter = new HeaderViewFullAdapter();
HeaderViewListAdapter headerViewListAdapter =
new HeaderViewListAdapter(null, null, fullAdapter);
- DataSetObserver observer = new HeaderViewDataSetObserver();
- headerViewListAdapter.registerDataSetObserver(observer);
+ DataSetObserver mockDataSetObserver = mock(DataSetObserver.class);
+ headerViewListAdapter.registerDataSetObserver(mockDataSetObserver);
headerViewListAdapter.unregisterDataSetObserver(null);
- assertSame(observer, fullAdapter.getDataSetObserver());
- headerViewListAdapter.unregisterDataSetObserver(observer);
+ assertSame(mockDataSetObserver, fullAdapter.getDataSetObserver());
+ headerViewListAdapter.unregisterDataSetObserver(mockDataSetObserver);
assertNull(fullAdapter.getDataSetObserver());
}
@@ -504,16 +506,4 @@
protected void publishResults(CharSequence constraint, Filter.FilterResults results) {
}
}
-
- private class HeaderViewDataSetObserver extends DataSetObserver {
- @Override
- public void onChanged() {
- // Do nothing
- }
-
- @Override
- public void onInvalidated() {
- // Do nothing
- }
- }
}
diff --git a/tests/tests/widget/src/android/widget/cts/HorizontalScrollViewCtsActivity.java b/tests/tests/widget/src/android/widget/cts/HorizontalScrollViewCtsActivity.java
index a10e63a..96d8279 100644
--- a/tests/tests/widget/src/android/widget/cts/HorizontalScrollViewCtsActivity.java
+++ b/tests/tests/widget/src/android/widget/cts/HorizontalScrollViewCtsActivity.java
@@ -16,10 +16,9 @@
package android.widget.cts;
-import android.widget.cts.R;
-
import android.app.Activity;
import android.os.Bundle;
+import android.widget.cts.R;
public class HorizontalScrollViewCtsActivity extends Activity {
@Override
diff --git a/tests/tests/widget/src/android/widget/cts/HorizontalScrollViewTest.java b/tests/tests/widget/src/android/widget/cts/HorizontalScrollViewTest.java
index afdc869..c8a7095 100644
--- a/tests/tests/widget/src/android/widget/cts/HorizontalScrollViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/HorizontalScrollViewTest.java
@@ -16,11 +16,8 @@
package android.widget.cts;
-import android.widget.FrameLayout;
-import android.widget.cts.R;
-
-
-import org.xmlpull.v1.XmlPullParser;
+import static org.mockito.Matchers.*;
+import static org.mockito.Mockito.*;
import android.app.Activity;
import android.content.Context;
@@ -32,13 +29,13 @@
import android.util.AttributeSet;
import android.util.Xml;
import android.view.View;
-import android.view.ViewGroup;
import android.view.View.MeasureSpec;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
import android.widget.HorizontalScrollView;
import android.widget.TextView;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
+
+import org.xmlpull.v1.XmlPullParser;
/**
* Test {@link HorizontalScrollView}.
@@ -800,55 +797,34 @@
}
if (fromY != toY) {
- new PollingCheck() {
- @Override
- protected boolean check() {
- return isInRange(mScrollView.getScrollY(), fromY, toY);
- }
- }.run();
+ PollingCheck.waitFor(() -> isInRange(mScrollView.getScrollY(), fromY, toY));
}
if (fromX != toX) {
- new PollingCheck() {
- @Override
- protected boolean check() {
- return isInRange(mScrollView.getScrollX(), fromX, toX);
- }
- }.run();
+ PollingCheck.waitFor(() -> isInRange(mScrollView.getScrollX(), fromX, toX));
}
- new PollingCheck() {
- @Override
- protected boolean check() {
- return toX == mScrollView.getScrollX() && toY == mScrollView.getScrollY();
- }
- }.run();
+ PollingCheck.waitFor(
+ () -> toX == mScrollView.getScrollX() && toY == mScrollView.getScrollY());
}
private void pollingCheckFling(final int startPosition, final boolean movingRight) {
- new PollingCheck() {
- @Override
- protected boolean check() {
- if (movingRight) {
- return mScrollView.getScrollX() > startPosition;
- }
- return mScrollView.getScrollX() < startPosition;
+ PollingCheck.waitFor(() -> {
+ if (movingRight) {
+ return mScrollView.getScrollX() > startPosition;
}
- }.run();
+ return mScrollView.getScrollX() < startPosition;
+ });
- new PollingCheck() {
- private int mPreviousScrollX = mScrollView.getScrollX();
-
- @Override
- protected boolean check() {
- if (mScrollView.getScrollX() == mPreviousScrollX) {
- return true;
- } else {
- mPreviousScrollX = mScrollView.getScrollX();
- return false;
- }
+ final int[] previousScrollX = new int[] { mScrollView.getScrollX() };
+ PollingCheck.waitFor(() -> {
+ if (mScrollView.getScrollX() == previousScrollX[0]) {
+ return true;
+ } else {
+ previousScrollX[0] = mScrollView.getScrollX();
+ return false;
}
- }.run();
+ });
}
public static class MyView extends View {
diff --git a/hostsidetests/services/activitymanager/app/src/android/server/app/SlowCreateActivity.java b/tests/tests/widget/src/android/widget/cts/ImageButtonCtsActivity.java
similarity index 67%
copy from hostsidetests/services/activitymanager/app/src/android/server/app/SlowCreateActivity.java
copy to tests/tests/widget/src/android/widget/cts/ImageButtonCtsActivity.java
index 481f5be..5f91950 100644
--- a/hostsidetests/services/activitymanager/app/src/android/server/app/SlowCreateActivity.java
+++ b/tests/tests/widget/src/android/widget/cts/ImageButtonCtsActivity.java
@@ -14,17 +14,22 @@
* limitations under the License.
*/
-package android.server.app;
+package android.widget.cts;
import android.app.Activity;
import android.os.Bundle;
+import android.widget.ImageButton;
-public class SlowCreateActivity extends Activity {
+/**
+ * A minimal application for {@link ImageButton} test.
+ */
+public class ImageButtonCtsActivity extends Activity {
+ /**
+ * Called with the activity is first created.
+ */
@Override
- protected void onCreate(Bundle savedInstanceState) {
- try {
- Thread.sleep(2000);
- } catch(InterruptedException e) {}
+ public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ setContentView(R.layout.imagebutton_layout);
}
}
diff --git a/tests/tests/widget/src/android/widget/cts/ImageButtonTest.java b/tests/tests/widget/src/android/widget/cts/ImageButtonTest.java
index 05c0e3f..485024b 100644
--- a/tests/tests/widget/src/android/widget/cts/ImageButtonTest.java
+++ b/tests/tests/widget/src/android/widget/cts/ImageButtonTest.java
@@ -16,27 +16,42 @@
package android.widget.cts;
-import android.widget.cts.R;
-
-
-import org.xmlpull.v1.XmlPullParser;
-
-import android.test.AndroidTestCase;
-import android.util.AttributeSet;
-import android.util.Xml;
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.graphics.Color;
+import android.graphics.drawable.Drawable;
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.suitebuilder.annotation.SmallTest;
import android.widget.ImageButton;
+import android.widget.cts.util.TestUtils;
-public class ImageButtonTest extends AndroidTestCase {
+@SmallTest
+public class ImageButtonTest extends ActivityInstrumentationTestCase2<ImageButtonCtsActivity> {
+ private Instrumentation mInstrumentation;
+ private Activity mActivity;
+ private ImageButton mImageButton;
+
+ public ImageButtonTest() {
+ super("android.widget.cts", ImageButtonCtsActivity.class);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ mInstrumentation = getInstrumentation();
+ mActivity = getActivity();
+ mImageButton = (ImageButton) mActivity.findViewById(R.id.image_button);
+ }
+
public void testConstructor() {
- XmlPullParser parser = getContext().getResources().getXml(R.layout.imagebutton_test);
- AttributeSet attrs = Xml.asAttributeSet(parser);
- assertNotNull(attrs);
-
- new ImageButton(getContext());
-
- new ImageButton(getContext(), attrs);
-
- new ImageButton(getContext(), attrs, 0);
+ new ImageButton(mActivity);
+ new ImageButton(mActivity, null);
+ new ImageButton(mActivity, null, android.R.attr.imageButtonStyle);
+ new ImageButton(mActivity, null, 0, android.R.style.Widget_DeviceDefault_ImageButton);
+ new ImageButton(mActivity, null, 0, android.R.style.Widget_DeviceDefault_Light_ImageButton);
+ new ImageButton(mActivity, null, 0, android.R.style.Widget_Material_ImageButton);
+ new ImageButton(mActivity, null, 0, android.R.style.Widget_Material_Light_ImageButton);
try {
new ImageButton(null);
@@ -57,7 +72,23 @@
}
}
- public void testOnSetAlpha() {
- // Do not test, it's controlled by View. Implementation details.
+ public void testImageSource() {
+ Drawable imageButtonDrawable = mImageButton.getDrawable();
+ TestUtils.assertAllPixelsOfColor("Default source is red", imageButtonDrawable,
+ imageButtonDrawable.getIntrinsicWidth(), imageButtonDrawable.getIntrinsicHeight(),
+ true, Color.RED, 1, false);
+
+ mInstrumentation.runOnMainSync(() -> mImageButton.setImageResource(R.drawable.icon_green));
+ imageButtonDrawable = mImageButton.getDrawable();
+ TestUtils.assertAllPixelsOfColor("New source is green", imageButtonDrawable,
+ imageButtonDrawable.getIntrinsicWidth(), imageButtonDrawable.getIntrinsicHeight(),
+ true, Color.GREEN, 1, false);
+
+ mInstrumentation.runOnMainSync(
+ () -> mImageButton.setImageDrawable(mActivity.getDrawable(R.drawable.icon_yellow)));
+ imageButtonDrawable = mImageButton.getDrawable();
+ TestUtils.assertAllPixelsOfColor("New source is yellow", imageButtonDrawable,
+ imageButtonDrawable.getIntrinsicWidth(), imageButtonDrawable.getIntrinsicHeight(),
+ true, Color.YELLOW, 1, false);
}
}
diff --git a/tests/tests/widget/src/android/widget/cts/ImageSwitcherTest.java b/tests/tests/widget/src/android/widget/cts/ImageSwitcherTest.java
index a7bca5c..9c559fa 100644
--- a/tests/tests/widget/src/android/widget/cts/ImageSwitcherTest.java
+++ b/tests/tests/widget/src/android/widget/cts/ImageSwitcherTest.java
@@ -16,16 +16,10 @@
package android.widget.cts;
-import android.widget.cts.R;
-
-
-import org.xmlpull.v1.XmlPullParser;
-
import android.content.Context;
import android.content.res.Resources;
import android.cts.util.WidgetTestUtils;
import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.net.Uri;
@@ -34,6 +28,9 @@
import android.util.Xml;
import android.widget.ImageSwitcher;
import android.widget.ImageView;
+import android.widget.cts.R;
+
+import org.xmlpull.v1.XmlPullParser;
import java.io.File;
import java.io.FileOutputStream;
diff --git a/tests/tests/widget/src/android/widget/cts/ImageViewCtsActivity.java b/tests/tests/widget/src/android/widget/cts/ImageViewCtsActivity.java
index e68c986..c3529ee 100644
--- a/tests/tests/widget/src/android/widget/cts/ImageViewCtsActivity.java
+++ b/tests/tests/widget/src/android/widget/cts/ImageViewCtsActivity.java
@@ -16,10 +16,9 @@
package android.widget.cts;
-import android.widget.cts.R;
-
import android.app.Activity;
import android.os.Bundle;
+import android.widget.cts.R;
/**
* A minimal application for {@link ImageView} test.
diff --git a/tests/tests/widget/src/android/widget/cts/ImageViewTest.java b/tests/tests/widget/src/android/widget/cts/ImageViewTest.java
index 844b922..664bdbe 100644
--- a/tests/tests/widget/src/android/widget/cts/ImageViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/ImageViewTest.java
@@ -16,48 +16,44 @@
package android.widget.cts;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-
-import android.content.res.ColorStateList;
-import android.graphics.Color;
-
-import android.graphics.drawable.ColorDrawable;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.widget.cts.util.TestUtils;
-import android.widget.cts.util.ViewTestUtils;
-import org.junit.Assert;
-import org.xmlpull.v1.XmlPullParser;
+import static org.mockito.Matchers.*;
+import static org.mockito.Mockito.*;
import android.app.Activity;
import android.content.Context;
+import android.content.res.ColorStateList;
import android.cts.util.WidgetTestUtils;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
+import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.Matrix;
import android.graphics.PorterDuff;
import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.graphics.drawable.PaintDrawable;
import android.net.Uri;
import android.test.ActivityInstrumentationTestCase;
import android.test.UiThreadTest;
+import android.test.suitebuilder.annotation.SmallTest;
import android.util.AttributeSet;
-import android.util.StateSet;
import android.util.Xml;
import android.widget.ImageView;
import android.widget.ImageView.ScaleType;
-
import android.widget.cts.R;
+import android.widget.cts.util.TestUtils;
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.*;
+import org.junit.Assert;
+import org.xmlpull.v1.XmlPullParser;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
/**
diff --git a/tests/tests/widget/src/android/widget/cts/LayoutDirectionTest.java b/tests/tests/widget/src/android/widget/cts/LayoutDirectionTest.java
index 2369c5a..73517cb 100644
--- a/tests/tests/widget/src/android/widget/cts/LayoutDirectionTest.java
+++ b/tests/tests/widget/src/android/widget/cts/LayoutDirectionTest.java
@@ -16,17 +16,21 @@
package android.widget.cts;
+import static android.view.View.LAYOUT_DIRECTION_INHERIT;
+import static android.view.View.LAYOUT_DIRECTION_LOCALE;
+import static android.view.View.LAYOUT_DIRECTION_LTR;
+import static android.view.View.LAYOUT_DIRECTION_RTL;
+
import android.test.ActivityInstrumentationTestCase2;
import android.test.UiThreadTest;
import android.view.ViewGroup;
-import android.widget.*;
+import android.widget.FrameLayout;
+import android.widget.GridLayout;
+import android.widget.LinearLayout;
+import android.widget.RelativeLayout;
+import android.widget.TableLayout;
import android.widget.cts.R;
-import static android.view.View.LAYOUT_DIRECTION_LTR;
-import static android.view.View.LAYOUT_DIRECTION_RTL;
-import static android.view.View.LAYOUT_DIRECTION_INHERIT;
-import static android.view.View.LAYOUT_DIRECTION_LOCALE;
-
public class LayoutDirectionTest extends ActivityInstrumentationTestCase2<LayoutDirectionCtsActivity> {
public LayoutDirectionTest() {
diff --git a/tests/tests/widget/src/android/widget/cts/LinearLayoutCtsActivity.java b/tests/tests/widget/src/android/widget/cts/LinearLayoutCtsActivity.java
index 4a04b52..c906342 100644
--- a/tests/tests/widget/src/android/widget/cts/LinearLayoutCtsActivity.java
+++ b/tests/tests/widget/src/android/widget/cts/LinearLayoutCtsActivity.java
@@ -16,10 +16,9 @@
package android.widget.cts;
-import android.widget.cts.R;
-
import android.app.Activity;
import android.os.Bundle;
+import android.widget.cts.R;
/**
* A minimal application for {@link LinearLayout} test.
diff --git a/tests/tests/widget/src/android/widget/cts/LinearLayoutTest.java b/tests/tests/widget/src/android/widget/cts/LinearLayoutTest.java
index 4d0903a..068c363 100644
--- a/tests/tests/widget/src/android/widget/cts/LinearLayoutTest.java
+++ b/tests/tests/widget/src/android/widget/cts/LinearLayoutTest.java
@@ -16,15 +16,21 @@
package android.widget.cts;
-import org.xmlpull.v1.XmlPullParser;
-
+import android.annotation.ColorInt;
import android.app.Activity;
+import android.app.Instrumentation;
import android.content.Context;
+import android.content.res.Resources;
+import android.cts.util.WidgetTestUtils;
import android.graphics.Color;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
import android.test.ActivityInstrumentationTestCase;
import android.test.ViewAsserts;
+import android.test.suitebuilder.annotation.SmallTest;
import android.util.AttributeSet;
import android.util.Xml;
+import android.view.Gravity;
import android.view.View;
import android.view.View.MeasureSpec;
import android.view.ViewGroup;
@@ -34,7 +40,9 @@
import android.widget.LinearLayout.LayoutParams;
import android.widget.ListView;
import android.widget.TextView;
-import android.widget.cts.R;
+import android.widget.cts.util.TestUtils;
+
+import org.xmlpull.v1.XmlPullParser;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -42,6 +50,7 @@
/**
* Test {@link LinearLayout}.
*/
+@SmallTest
public class LinearLayoutTest extends ActivityInstrumentationTestCase<LinearLayoutCtsActivity> {
private Context mContext;
private Activity mActivity;
@@ -152,9 +161,9 @@
*/
public void testAccessWeightSum() {
LinearLayout parent = (LinearLayout) mActivity.findViewById(R.id.weightsum);
- TextView weight02 = (TextView) mActivity.findViewById(R.id.weight_0_2);
- TextView weight05 = (TextView) mActivity.findViewById(R.id.weight_0_5);
- TextView weight03 = (TextView) mActivity.findViewById(R.id.weight_0_3);
+ TextView weight02 = (TextView) parent.findViewById(R.id.weight_0_2);
+ TextView weight05 = (TextView) parent.findViewById(R.id.weight_0_5);
+ TextView weight03 = (TextView) parent.findViewById(R.id.weight_0_3);
assertNotNull(parent);
assertNotNull(weight02);
@@ -399,6 +408,182 @@
assertEquals(parent.getWidth(), rightView.getRight());
}
+ public void testVerticalCenterGravityOnHorizontalLayout() {
+ final Instrumentation instrumentation = getInstrumentation();
+ LinearLayout parent = (LinearLayout) mActivity.findViewById(R.id.weightsum);
+ TextView leftView = (TextView) parent.findViewById(R.id.weight_0_2);
+ TextView centerView = (TextView) parent.findViewById(R.id.weight_0_5);
+ TextView rightView = (TextView) parent.findViewById(R.id.weight_0_3);
+
+ instrumentation.runOnMainSync(() -> parent.setLayoutDirection(View.LAYOUT_DIRECTION_LTR));
+ instrumentation.waitForIdleSync();
+
+ int originalLeftViewRight = leftView.getRight();
+ int originalCenterViewLeft = centerView.getLeft();
+ int originalCenterViewRight = centerView.getRight();
+ int originalRightViewLeft = rightView.getLeft();
+
+ instrumentation.runOnMainSync(() -> parent.setVerticalGravity(Gravity.CENTER_VERTICAL));
+ instrumentation.waitForIdleSync();
+
+ assertEquals(Gravity.CENTER_VERTICAL, parent.getGravity() & Gravity.VERTICAL_GRAVITY_MASK);
+
+ ViewAsserts.assertVerticalCenterAligned(parent, leftView);
+ ViewAsserts.assertVerticalCenterAligned(parent, centerView);
+ ViewAsserts.assertVerticalCenterAligned(parent, rightView);
+
+ final int parentHeight = parent.getHeight();
+
+ int verticalOffset = (parentHeight - leftView.getHeight()) / 2;
+ assertEquals(verticalOffset, leftView.getTop());
+ assertEquals(verticalOffset + leftView.getHeight(), leftView.getBottom());
+ assertEquals(0, leftView.getLeft());
+ assertEquals(originalLeftViewRight, leftView.getRight());
+
+ verticalOffset = (parentHeight - centerView.getHeight()) / 2;
+ assertEquals(verticalOffset, centerView.getTop());
+ assertEquals(verticalOffset + centerView.getHeight(), centerView.getBottom());
+ assertEquals(originalCenterViewLeft, centerView.getLeft());
+ assertEquals(originalCenterViewRight, centerView.getRight());
+
+ verticalOffset = (parentHeight - rightView.getHeight()) / 2;
+ assertEquals(verticalOffset, rightView.getTop());
+ assertEquals(verticalOffset + rightView.getHeight(), rightView.getBottom());
+ assertEquals(originalRightViewLeft, rightView.getLeft());
+ assertEquals(parent.getWidth(), rightView.getRight());
+ }
+
+ public void testBottomGravityOnHorizontalLayout() {
+ final Instrumentation instrumentation = getInstrumentation();
+ LinearLayout parent = (LinearLayout) mActivity.findViewById(R.id.weightsum);
+ TextView leftView = (TextView) parent.findViewById(R.id.weight_0_2);
+ TextView centerView = (TextView) parent.findViewById(R.id.weight_0_5);
+ TextView rightView = (TextView) parent.findViewById(R.id.weight_0_3);
+
+ instrumentation.runOnMainSync(() -> parent.setLayoutDirection(View.LAYOUT_DIRECTION_LTR));
+ instrumentation.waitForIdleSync();
+
+ int originalLeftViewRight = leftView.getRight();
+ int originalCenterViewLeft = centerView.getLeft();
+ int originalCenterViewRight = centerView.getRight();
+ int originalRightViewLeft = rightView.getLeft();
+
+ instrumentation.runOnMainSync(() -> parent.setVerticalGravity(Gravity.BOTTOM));
+ instrumentation.waitForIdleSync();
+
+ assertEquals(Gravity.BOTTOM, parent.getGravity() & Gravity.VERTICAL_GRAVITY_MASK);
+
+ ViewAsserts.assertBottomAligned(parent, leftView);
+ ViewAsserts.assertBottomAligned(parent, centerView);
+ ViewAsserts.assertBottomAligned(parent, rightView);
+
+ final int parentHeight = parent.getHeight();
+
+ assertEquals(parentHeight - leftView.getHeight(), leftView.getTop());
+ assertEquals(parentHeight, leftView.getBottom());
+ assertEquals(0, leftView.getLeft());
+ assertEquals(originalLeftViewRight, leftView.getRight());
+
+ assertEquals(parentHeight - centerView.getHeight(), centerView.getTop());
+ assertEquals(parentHeight, centerView.getBottom());
+ assertEquals(originalCenterViewLeft, centerView.getLeft());
+ assertEquals(originalCenterViewRight, centerView.getRight());
+
+ assertEquals(parentHeight - rightView.getHeight(), rightView.getTop());
+ assertEquals(parentHeight, rightView.getBottom());
+ assertEquals(originalRightViewLeft, rightView.getLeft());
+ assertEquals(parent.getWidth(), rightView.getRight());
+ }
+
+ public void testHorizontalCenterGravityOnVerticalLayout() {
+ final Instrumentation instrumentation = getInstrumentation();
+ LinearLayout parent = (LinearLayout) mActivity.findViewById(R.id.weightsum_vertical);
+ TextView topView = (TextView) parent.findViewById(R.id.weight_0_1);
+ TextView centerView = (TextView) parent.findViewById(R.id.weight_0_4);
+ TextView bottomView = (TextView) parent.findViewById(R.id.weight_0_5);
+
+ instrumentation.runOnMainSync(() -> parent.setLayoutDirection(View.LAYOUT_DIRECTION_LTR));
+ instrumentation.waitForIdleSync();
+
+ final int parentWidth = parent.getHeight();
+
+ int originalTopViewBottom = topView.getBottom();
+ int originalCenterViewTop = centerView.getTop();
+ int originalCenterViewBottom = centerView.getBottom();
+ int originalBottomViewTop = bottomView.getTop();
+
+ instrumentation.runOnMainSync(() -> parent.setHorizontalGravity(Gravity.CENTER_HORIZONTAL));
+ instrumentation.waitForIdleSync();
+
+ assertEquals(Gravity.CENTER_HORIZONTAL,
+ parent.getGravity() & Gravity.HORIZONTAL_GRAVITY_MASK);
+
+ ViewAsserts.assertHorizontalCenterAligned(parent, topView);
+ ViewAsserts.assertHorizontalCenterAligned(parent, centerView);
+ ViewAsserts.assertHorizontalCenterAligned(parent, bottomView);
+
+ int horizontalOffset = (parentWidth - topView.getWidth()) / 2;
+ assertEquals(0, topView.getTop());
+ assertEquals(originalTopViewBottom, topView.getBottom());
+ assertEquals(horizontalOffset, topView.getLeft());
+ assertEquals(horizontalOffset + topView.getWidth(), topView.getRight());
+
+ horizontalOffset = (parentWidth - centerView.getWidth()) / 2;
+ assertEquals(originalCenterViewTop, centerView.getTop());
+ assertEquals(originalCenterViewBottom, centerView.getBottom());
+ assertEquals(horizontalOffset, centerView.getLeft());
+ assertEquals(horizontalOffset + centerView.getWidth(), centerView.getRight());
+
+ horizontalOffset = (parentWidth - bottomView.getWidth()) / 2;
+ assertEquals(originalBottomViewTop, bottomView.getTop());
+ assertEquals(parent.getHeight(), bottomView.getBottom());
+ assertEquals(horizontalOffset, bottomView.getLeft());
+ assertEquals(horizontalOffset + bottomView.getWidth(), bottomView.getRight());
+ }
+
+ public void testRightGravityOnVerticalLayout() {
+ final Instrumentation instrumentation = getInstrumentation();
+ LinearLayout parent = (LinearLayout) mActivity.findViewById(R.id.weightsum_vertical);
+ TextView topView = (TextView) parent.findViewById(R.id.weight_0_1);
+ TextView centerView = (TextView) parent.findViewById(R.id.weight_0_4);
+ TextView bottomView = (TextView) parent.findViewById(R.id.weight_0_5);
+
+ instrumentation.runOnMainSync(() -> parent.setLayoutDirection(View.LAYOUT_DIRECTION_LTR));
+ instrumentation.waitForIdleSync();
+
+ final int parentWidth = parent.getHeight();
+
+ int originalTopViewBottom = topView.getBottom();
+ int originalCenterViewTop = centerView.getTop();
+ int originalCenterViewBottom = centerView.getBottom();
+ int originalBottomViewTop = bottomView.getTop();
+
+ instrumentation.runOnMainSync(() -> parent.setHorizontalGravity(Gravity.RIGHT));
+ instrumentation.waitForIdleSync();
+
+ assertEquals(Gravity.RIGHT,
+ parent.getGravity() & Gravity.HORIZONTAL_GRAVITY_MASK);
+
+ ViewAsserts.assertRightAligned(parent, topView);
+ ViewAsserts.assertRightAligned(parent, centerView);
+ ViewAsserts.assertRightAligned(parent, bottomView);
+
+ assertEquals(0, topView.getTop());
+ assertEquals(originalTopViewBottom, topView.getBottom());
+ assertEquals(parentWidth - topView.getWidth(), topView.getLeft());
+ assertEquals(parentWidth, topView.getRight());
+
+ assertEquals(originalCenterViewTop, centerView.getTop());
+ assertEquals(originalCenterViewBottom, centerView.getBottom());
+ assertEquals(parentWidth - centerView.getWidth(), centerView.getLeft());
+ assertEquals(parentWidth, centerView.getRight());
+
+ assertEquals(originalBottomViewTop, bottomView.getTop());
+ assertEquals(parent.getHeight(), bottomView.getBottom());
+ assertEquals(parentWidth - bottomView.getWidth(), bottomView.getLeft());
+ assertEquals(parentWidth, bottomView.getRight());
+ }
+
private void checkBounds(final ViewGroup viewGroup, final View view,
final CountDownLatch countDownLatch, final int left, final int top,
final int width, final int height) {
@@ -471,6 +656,523 @@
countDownLatch3.await(500, TimeUnit.MILLISECONDS);
}
+ private void verifyVisualsOfVerticalLayoutWithDivider(LinearLayout parent,
+ int expectedDividerPositionMask,
+ int expectedDividerSize, @ColorInt int expectedDividerColor,
+ int expectedDividerPadding) {
+ final int parentWidth = parent.getWidth();
+ final int parentHeight = parent.getHeight();
+
+ final boolean expectingTopDivider =
+ (expectedDividerPositionMask & LinearLayout.SHOW_DIVIDER_BEGINNING) != 0;
+ final boolean expectingMiddleDivider =
+ (expectedDividerPositionMask & LinearLayout.SHOW_DIVIDER_MIDDLE) != 0;
+ final boolean expectingBottomDivider =
+ (expectedDividerPositionMask & LinearLayout.SHOW_DIVIDER_END) != 0;
+ final int expectedDividerCount = (expectingTopDivider ? 1 : 0)
+ + (expectingMiddleDivider ? 1 : 0) + (expectingBottomDivider ? 1 : 0);
+
+ final int expectedChildHeight =
+ (parentHeight - expectedDividerCount * expectedDividerSize) / 2;
+
+ final int expectedTopChildTop = expectingTopDivider ? expectedDividerSize : 0;
+ TestUtils.assertRegionPixelsOfColor("Region of first child is blue", parent,
+ new Rect(0, expectedTopChildTop, parentWidth,
+ expectedTopChildTop + expectedChildHeight),
+ Color.BLUE, 1, true);
+
+ final int expectedBottomChildBottom =
+ expectingBottomDivider ? parentHeight - expectedDividerSize : parentHeight;
+ TestUtils.assertRegionPixelsOfColor("Region of second child is green", parent,
+ new Rect(0, expectedBottomChildBottom - expectedChildHeight, parentWidth,
+ expectedBottomChildBottom),
+ Color.GREEN, 1, true);
+
+ if (expectedDividerSize == 0) {
+ return;
+ }
+
+ // Do we expect top divider?
+ if (expectingTopDivider) {
+ TestUtils.assertRegionPixelsOfColor(
+ "Region of top divider is " + TestUtils.formatColorToHex(expectedDividerColor),
+ parent,
+ new Rect(expectedDividerPadding, 0, parentWidth - expectedDividerPadding,
+ expectedDividerSize),
+ expectedDividerColor, 1, true);
+ TestUtils.assertRegionPixelsOfColor("Region of left padding of top divider is yellow",
+ parent,
+ new Rect(0, 0, expectedDividerPadding, expectedDividerSize),
+ Color.YELLOW, 1, true);
+ TestUtils.assertRegionPixelsOfColor("Region of right padding of top divider is yellow",
+ parent,
+ new Rect(parentWidth - expectedDividerPadding, 0, parentWidth,
+ expectedDividerSize),
+ Color.YELLOW, 1, true);
+ }
+
+ // Do we expect middle divider?
+ if (expectingMiddleDivider) {
+ final int expectedMiddleDividerTop = expectedTopChildTop + expectedChildHeight;
+ TestUtils.assertRegionPixelsOfColor(
+ "Region of middle divider is " +
+ TestUtils.formatColorToHex(expectedDividerColor),
+ parent,
+ new Rect(expectedDividerPadding, expectedMiddleDividerTop,
+ parentWidth - expectedDividerPadding,
+ expectedMiddleDividerTop + expectedDividerSize),
+ expectedDividerColor, 1, true);
+ TestUtils.assertRegionPixelsOfColor(
+ "Region of left padding of middle divider is yellow",
+ parent,
+ new Rect(0, expectedMiddleDividerTop, expectedDividerPadding,
+ expectedMiddleDividerTop + expectedDividerSize),
+ Color.YELLOW, 1, true);
+ TestUtils.assertRegionPixelsOfColor(
+ "Region of right padding of middle divider is yellow",
+ parent,
+ new Rect(parentWidth - expectedDividerPadding, expectedMiddleDividerTop,
+ parentWidth, expectedMiddleDividerTop + expectedDividerSize),
+ Color.YELLOW, 1, true);
+ }
+
+ // Do we expect bottom divider?
+ if (expectingBottomDivider) {
+ final int expectedBottomDividerTop = expectedBottomChildBottom;
+ TestUtils.assertRegionPixelsOfColor(
+ "Region of bottom divider is " +
+ TestUtils.formatColorToHex(expectedDividerColor),
+ parent,
+ new Rect(expectedDividerPadding, expectedBottomDividerTop,
+ parentWidth - expectedDividerPadding,
+ expectedBottomDividerTop + expectedDividerSize),
+ expectedDividerColor, 1, true);
+ TestUtils.assertRegionPixelsOfColor(
+ "Region of left padding of bottom divider is yellow",
+ parent,
+ new Rect(0, expectedBottomDividerTop, expectedDividerPadding,
+ expectedBottomDividerTop + expectedDividerSize),
+ Color.YELLOW, 1, true);
+ TestUtils.assertRegionPixelsOfColor(
+ "Region of right padding of bottom divider is yellow",
+ parent,
+ new Rect(parentWidth - expectedDividerPadding, expectedBottomDividerTop,
+ parentWidth, expectedBottomDividerTop + expectedDividerSize),
+ Color.YELLOW, 1, true);
+ }
+ }
+
+ /**
+ * layout of vertical LinearLayout.
+ * -----------------------------------
+ * | ------------------------------- |
+ * | | child1 | |
+ * | ------------------------------- |
+ * | - - - - - - divider - - - - - - |
+ * | ------------------------------- |
+ * | | child2 | |
+ * | ------------------------------- |
+ * -----------------------------------
+ *
+ * Parent is filled with yellow color. Child 1 is filled with green and child 2 is filled
+ * with blue. Divider is red at the beginning. Throughout this method we reconfigure the
+ * visibility, drawable and paddings of the divider and verify the overall visuals of the
+ * container.
+ */
+ public void testDividersInVerticalLayout() {
+ final LinearLayout parent =
+ (LinearLayout) mActivity.findViewById(R.id.vertical_with_divider);
+
+ final Instrumentation instrumentation = getInstrumentation();
+
+ final Resources res = mActivity.getResources();
+ final int dividerSize = res.getDimensionPixelSize(R.dimen.linear_layout_divider_size);
+ final int dividerPadding = res.getDimensionPixelSize(R.dimen.linear_layout_divider_padding);
+
+ assertEquals(LinearLayout.SHOW_DIVIDER_MIDDLE, parent.getShowDividers());
+ assertEquals(dividerPadding, parent.getDividerPadding());
+ final Drawable dividerDrawable = parent.getDividerDrawable();
+ TestUtils.assertAllPixelsOfColor("Divider is red", dividerDrawable,
+ dividerDrawable.getIntrinsicWidth(), dividerDrawable.getIntrinsicHeight(),
+ false, Color.RED, 1, true);
+
+ // Test the initial visuals of the entire parent
+ verifyVisualsOfVerticalLayoutWithDivider(parent, LinearLayout.SHOW_DIVIDER_MIDDLE,
+ dividerSize, Color.RED, dividerPadding);
+
+ // Change the divider to magenta
+ WidgetTestUtils.runOnMainAndDrawSync(instrumentation, parent,
+ () -> parent.setDividerDrawable(
+ mActivity.getDrawable(R.drawable.linear_layout_divider_magenta)));
+ verifyVisualsOfVerticalLayoutWithDivider(parent, LinearLayout.SHOW_DIVIDER_MIDDLE,
+ dividerSize, Color.MAGENTA, dividerPadding);
+
+ // Change the divider to null (no divider effectively)
+ WidgetTestUtils.runOnMainAndDrawSync(instrumentation, parent,
+ () -> parent.setDividerDrawable(null));
+ verifyVisualsOfVerticalLayoutWithDivider(parent, LinearLayout.SHOW_DIVIDER_MIDDLE,
+ 0, Color.TRANSPARENT, 0);
+
+ // Change the divider back to red
+ WidgetTestUtils.runOnMainAndDrawSync(instrumentation, parent,
+ () -> parent.setDividerDrawable(
+ mActivity.getDrawable(R.drawable.linear_layout_divider_red)));
+ verifyVisualsOfVerticalLayoutWithDivider(parent, LinearLayout.SHOW_DIVIDER_MIDDLE,
+ dividerSize, Color.RED, dividerPadding);
+
+ // Change the padding to half the original size
+ final int halfPadding = dividerPadding / 2;
+ WidgetTestUtils.runOnMainAndDrawSync(instrumentation, parent,
+ () -> parent.setDividerPadding(halfPadding));
+ assertEquals(halfPadding, parent.getDividerPadding());
+ verifyVisualsOfVerticalLayoutWithDivider(parent, LinearLayout.SHOW_DIVIDER_MIDDLE,
+ dividerSize, Color.RED, halfPadding);
+
+ // Change the padding to twice the original size
+ final int doublePadding = dividerPadding * 2;
+ WidgetTestUtils.runOnMainAndDrawSync(instrumentation, parent,
+ () -> parent.setDividerPadding(doublePadding));
+ assertEquals(doublePadding, parent.getDividerPadding());
+ verifyVisualsOfVerticalLayoutWithDivider(parent, LinearLayout.SHOW_DIVIDER_MIDDLE,
+ dividerSize, Color.RED, doublePadding);
+
+ // And back to the original padding
+ WidgetTestUtils.runOnMainAndDrawSync(instrumentation, parent,
+ () -> parent.setDividerPadding(dividerPadding));
+ assertEquals(dividerPadding, parent.getDividerPadding());
+ verifyVisualsOfVerticalLayoutWithDivider(parent, LinearLayout.SHOW_DIVIDER_MIDDLE,
+ dividerSize, Color.RED, dividerPadding);
+
+ // Set show dividers to NONE (no divider effectively)
+ WidgetTestUtils.runOnMainAndDrawSync(instrumentation, parent,
+ () -> parent.setShowDividers(LinearLayout.SHOW_DIVIDER_NONE));
+ assertEquals(LinearLayout.SHOW_DIVIDER_NONE, parent.getShowDividers());
+ verifyVisualsOfVerticalLayoutWithDivider(parent, LinearLayout.SHOW_DIVIDER_NONE,
+ 0, Color.TRANSPARENT, 0);
+
+ // Show only top divider
+ WidgetTestUtils.runOnMainAndDrawSync(instrumentation, parent,
+ () -> parent.setShowDividers(LinearLayout.SHOW_DIVIDER_BEGINNING));
+ assertEquals(LinearLayout.SHOW_DIVIDER_BEGINNING, parent.getShowDividers());
+ verifyVisualsOfVerticalLayoutWithDivider(parent, LinearLayout.SHOW_DIVIDER_BEGINNING,
+ dividerSize, Color.RED, dividerPadding);
+
+ // Show only bottom divider
+ WidgetTestUtils.runOnMainAndDrawSync(instrumentation, parent,
+ () -> parent.setShowDividers(LinearLayout.SHOW_DIVIDER_END));
+ assertEquals(LinearLayout.SHOW_DIVIDER_END, parent.getShowDividers());
+ verifyVisualsOfVerticalLayoutWithDivider(parent, LinearLayout.SHOW_DIVIDER_END,
+ dividerSize, Color.RED, dividerPadding);
+
+ // Show top and bottom dividers
+ WidgetTestUtils.runOnMainAndDrawSync(instrumentation, parent,
+ () -> parent.setShowDividers(
+ LinearLayout.SHOW_DIVIDER_BEGINNING | LinearLayout.SHOW_DIVIDER_END));
+ assertEquals(LinearLayout.SHOW_DIVIDER_BEGINNING | LinearLayout.SHOW_DIVIDER_END,
+ parent.getShowDividers());
+ verifyVisualsOfVerticalLayoutWithDivider(parent,
+ LinearLayout.SHOW_DIVIDER_BEGINNING | LinearLayout.SHOW_DIVIDER_END,
+ dividerSize, Color.RED, dividerPadding);
+
+ // Show top and middle dividers
+ WidgetTestUtils.runOnMainAndDrawSync(instrumentation, parent,
+ () -> parent.setShowDividers(
+ LinearLayout.SHOW_DIVIDER_BEGINNING | LinearLayout.SHOW_DIVIDER_MIDDLE));
+ assertEquals(LinearLayout.SHOW_DIVIDER_BEGINNING | LinearLayout.SHOW_DIVIDER_MIDDLE,
+ parent.getShowDividers());
+ verifyVisualsOfVerticalLayoutWithDivider(parent,
+ LinearLayout.SHOW_DIVIDER_BEGINNING | LinearLayout.SHOW_DIVIDER_MIDDLE,
+ dividerSize, Color.RED, dividerPadding);
+
+ // Show middle and bottom dividers
+ WidgetTestUtils.runOnMainAndDrawSync(instrumentation, parent,
+ () -> parent.setShowDividers(
+ LinearLayout.SHOW_DIVIDER_MIDDLE | LinearLayout.SHOW_DIVIDER_END));
+ assertEquals(LinearLayout.SHOW_DIVIDER_MIDDLE | LinearLayout.SHOW_DIVIDER_END,
+ parent.getShowDividers());
+ verifyVisualsOfVerticalLayoutWithDivider(parent,
+ LinearLayout.SHOW_DIVIDER_MIDDLE | LinearLayout.SHOW_DIVIDER_END,
+ dividerSize, Color.RED, dividerPadding);
+
+ // Show top, middle and bottom dividers
+ WidgetTestUtils.runOnMainAndDrawSync(instrumentation, parent,
+ () -> parent.setShowDividers(
+ LinearLayout.SHOW_DIVIDER_BEGINNING | LinearLayout.SHOW_DIVIDER_MIDDLE
+ | LinearLayout.SHOW_DIVIDER_END));
+ assertEquals(
+ LinearLayout.SHOW_DIVIDER_BEGINNING | LinearLayout.SHOW_DIVIDER_MIDDLE
+ | LinearLayout.SHOW_DIVIDER_END,
+ parent.getShowDividers());
+ verifyVisualsOfVerticalLayoutWithDivider(parent,
+ LinearLayout.SHOW_DIVIDER_BEGINNING | LinearLayout.SHOW_DIVIDER_MIDDLE
+ | LinearLayout.SHOW_DIVIDER_END,
+ dividerSize, Color.RED, dividerPadding);
+ }
+
+ private void verifyVisualsOfHorizontalLayoutWithDivider(LinearLayout parent,
+ int expectedDividerPositionMask,
+ int expectedDividerSize, @ColorInt int expectedDividerColor,
+ int expectedDividerPadding) {
+ final int parentWidth = parent.getWidth();
+ final int parentHeight = parent.getHeight();
+
+ final boolean expectingLeftDivider =
+ (expectedDividerPositionMask & LinearLayout.SHOW_DIVIDER_BEGINNING) != 0;
+ final boolean expectingMiddleDivider =
+ (expectedDividerPositionMask & LinearLayout.SHOW_DIVIDER_MIDDLE) != 0;
+ final boolean expectingRightDivider =
+ (expectedDividerPositionMask & LinearLayout.SHOW_DIVIDER_END) != 0;
+ final int expectedDividerCount = (expectingLeftDivider ? 1 : 0)
+ + (expectingMiddleDivider ? 1 : 0) + (expectingRightDivider ? 1 : 0);
+
+ final int expectedChildWidth =
+ (parentWidth - expectedDividerCount * expectedDividerSize) / 2;
+
+ final int expectedLeftChildLeft = expectingLeftDivider ? expectedDividerSize : 0;
+ TestUtils.assertRegionPixelsOfColor("Region of first child is blue", parent,
+ new Rect(expectedLeftChildLeft, 0,
+ expectedLeftChildLeft + expectedChildWidth, parentHeight),
+ Color.BLUE, 1, true);
+
+ final int expectedRightChildRight =
+ expectingRightDivider ? parentWidth - expectedDividerSize : parentWidth;
+ TestUtils.assertRegionPixelsOfColor("Region of second child is green", parent,
+ new Rect(expectedRightChildRight - expectedChildWidth, 0, expectedRightChildRight,
+ parentHeight),
+ Color.GREEN, 1, true);
+
+ if (expectedDividerSize == 0) {
+ return;
+ }
+
+ // Do we expect left divider?
+ if (expectingLeftDivider) {
+ TestUtils.assertRegionPixelsOfColor(
+ "Region of left divider is " + TestUtils.formatColorToHex(expectedDividerColor),
+ parent,
+ new Rect(0, expectedDividerPadding, expectedDividerSize,
+ parentHeight - expectedDividerPadding),
+ expectedDividerColor, 1, true);
+ TestUtils.assertRegionPixelsOfColor(
+ "Region of top padding of left divider is yellow",
+ parent,
+ new Rect(0, 0, expectedDividerSize, expectedDividerPadding),
+ Color.YELLOW, 1, true);
+ TestUtils.assertRegionPixelsOfColor(
+ "Region of bottom padding of left divider is yellow",
+ parent,
+ new Rect(0, parentHeight - expectedDividerPadding, expectedDividerSize,
+ parentHeight),
+ Color.YELLOW, 1, true);
+ }
+
+ // Do we expect middle divider?
+ if (expectingMiddleDivider) {
+ final int expectedMiddleDividerLeft = expectedLeftChildLeft + expectedChildWidth;
+ TestUtils.assertRegionPixelsOfColor(
+ "Region of middle divider is " +
+ TestUtils.formatColorToHex(expectedDividerColor),
+ parent,
+ new Rect(expectedMiddleDividerLeft, expectedDividerPadding,
+ expectedMiddleDividerLeft + expectedDividerSize,
+ parentHeight - expectedDividerPadding),
+ expectedDividerColor, 1, true);
+ TestUtils.assertRegionPixelsOfColor(
+ "Region of top padding of middle divider is yellow",
+ parent,
+ new Rect(expectedMiddleDividerLeft, 0,
+ expectedMiddleDividerLeft + expectedDividerSize,
+ expectedDividerPadding),
+ Color.YELLOW, 1, true);
+ TestUtils.assertRegionPixelsOfColor(
+ "Region of bottom padding of middle divider is yellow",
+ parent,
+ new Rect(expectedMiddleDividerLeft, parentHeight - expectedDividerPadding,
+ expectedMiddleDividerLeft + expectedDividerSize, parentHeight),
+ Color.YELLOW, 1, true);
+ }
+
+ // Do we expect right divider?
+ if (expectingRightDivider) {
+ final int expectedRightDividerLeft = expectedRightChildRight;
+ TestUtils.assertRegionPixelsOfColor(
+ "Region of right divider is " +
+ TestUtils.formatColorToHex(expectedDividerColor),
+ parent,
+ new Rect(expectedRightDividerLeft, expectedDividerPadding,
+ expectedRightDividerLeft + expectedDividerSize,
+ parentHeight - expectedDividerPadding),
+ expectedDividerColor, 1, true);
+ TestUtils.assertRegionPixelsOfColor(
+ "Region of top padding of right divider is yellow",
+ parent,
+ new Rect(expectedRightDividerLeft, 0,
+ expectedRightDividerLeft + expectedDividerSize,
+ expectedDividerPadding),
+ Color.YELLOW, 1, true);
+ TestUtils.assertRegionPixelsOfColor(
+ "Region of bottom padding of right divider is yellow",
+ parent,
+ new Rect(expectedRightDividerLeft, parentHeight - expectedDividerPadding,
+ expectedRightDividerLeft + expectedDividerSize, parentHeight),
+ Color.YELLOW, 1, true);
+ }
+ }
+
+ /**
+ * layout of horizontal LinearLayout.
+ * -----------------------------------
+ * | ------------ | ------------- |
+ * | | | | | |
+ * | | | d | | |
+ * | | | i | | |
+ * | | | v | | |
+ * | | child1 | i | child2 | |
+ * | | | d | | |
+ * | | | e | | |
+ * | | | r | | |
+ * | | | | | |
+ * | ------------ | ------------- |
+ * -----------------------------------
+ *
+ * Parent is filled with yellow color. Child 1 is filled with green and child 2 is filled
+ * with blue. Divider is red at the beginning. Throughout this method we reconfigure the
+ * visibility, drawable and paddings of the divider and verify the overall visuals of the
+ * container.
+ */
+ public void testDividersInHorizontalLayout() {
+ final LinearLayout parent =
+ (LinearLayout) mActivity.findViewById(R.id.horizontal_with_divider);
+
+ final Instrumentation instrumentation = getInstrumentation();
+
+ instrumentation.runOnMainSync(() -> parent.setLayoutDirection(View.LAYOUT_DIRECTION_LTR));
+ instrumentation.waitForIdleSync();
+
+ final Resources res = mActivity.getResources();
+ final int dividerSize = res.getDimensionPixelSize(R.dimen.linear_layout_divider_size);
+ final int dividerPadding = res.getDimensionPixelSize(R.dimen.linear_layout_divider_padding);
+
+ assertEquals(LinearLayout.SHOW_DIVIDER_MIDDLE, parent.getShowDividers());
+ assertEquals(dividerPadding, parent.getDividerPadding());
+ final Drawable dividerDrawable = parent.getDividerDrawable();
+ TestUtils.assertAllPixelsOfColor("Divider is red", dividerDrawable,
+ dividerDrawable.getIntrinsicWidth(), dividerDrawable.getIntrinsicHeight(),
+ false, Color.RED, 1, true);
+
+ // Test the initial visuals of the entire parent
+ verifyVisualsOfHorizontalLayoutWithDivider(parent, LinearLayout.SHOW_DIVIDER_MIDDLE,
+ dividerSize, Color.RED, dividerPadding);
+
+ // Change the divider to magenta
+ WidgetTestUtils.runOnMainAndDrawSync(instrumentation, parent,
+ () -> parent.setDividerDrawable(
+ mActivity.getDrawable(R.drawable.linear_layout_divider_magenta)));
+ verifyVisualsOfHorizontalLayoutWithDivider(parent, LinearLayout.SHOW_DIVIDER_MIDDLE,
+ dividerSize, Color.MAGENTA, dividerPadding);
+
+ // Change the divider to null (no divider effectively)
+ WidgetTestUtils.runOnMainAndDrawSync(instrumentation, parent,
+ () -> parent.setDividerDrawable(null));
+ verifyVisualsOfHorizontalLayoutWithDivider(parent, LinearLayout.SHOW_DIVIDER_MIDDLE,
+ 0, Color.TRANSPARENT, 0);
+
+ // Change the divider back to red
+ WidgetTestUtils.runOnMainAndDrawSync(instrumentation, parent,
+ () -> parent.setDividerDrawable(
+ mActivity.getDrawable(R.drawable.linear_layout_divider_red)));
+ verifyVisualsOfHorizontalLayoutWithDivider(parent, LinearLayout.SHOW_DIVIDER_MIDDLE,
+ dividerSize, Color.RED, dividerPadding);
+
+ // Change the padding to half the original size
+ final int halfPadding = dividerPadding / 2;
+ WidgetTestUtils.runOnMainAndDrawSync(instrumentation, parent,
+ () -> parent.setDividerPadding(halfPadding));
+ assertEquals(halfPadding, parent.getDividerPadding());
+ verifyVisualsOfHorizontalLayoutWithDivider(parent, LinearLayout.SHOW_DIVIDER_MIDDLE,
+ dividerSize, Color.RED, halfPadding);
+
+ // Change the padding to twice the original size
+ final int doublePadding = dividerPadding * 2;
+ WidgetTestUtils.runOnMainAndDrawSync(instrumentation, parent,
+ () -> parent.setDividerPadding(doublePadding));
+ assertEquals(doublePadding, parent.getDividerPadding());
+ verifyVisualsOfHorizontalLayoutWithDivider(parent, LinearLayout.SHOW_DIVIDER_MIDDLE,
+ dividerSize, Color.RED, doublePadding);
+
+ // And back to the original padding
+ WidgetTestUtils.runOnMainAndDrawSync(instrumentation, parent,
+ () -> parent.setDividerPadding(dividerPadding));
+ assertEquals(dividerPadding, parent.getDividerPadding());
+ verifyVisualsOfHorizontalLayoutWithDivider(parent, LinearLayout.SHOW_DIVIDER_MIDDLE,
+ dividerSize, Color.RED, dividerPadding);
+
+ // Set show dividers to NONE (no divider effectively)
+ WidgetTestUtils.runOnMainAndDrawSync(instrumentation, parent,
+ () -> parent.setShowDividers(LinearLayout.SHOW_DIVIDER_NONE));
+ assertEquals(LinearLayout.SHOW_DIVIDER_NONE, parent.getShowDividers());
+ verifyVisualsOfHorizontalLayoutWithDivider(parent, LinearLayout.SHOW_DIVIDER_NONE,
+ 0, Color.TRANSPARENT, 0);
+
+ // Show only left divider
+ WidgetTestUtils.runOnMainAndDrawSync(instrumentation, parent,
+ () -> parent.setShowDividers(LinearLayout.SHOW_DIVIDER_BEGINNING));
+ assertEquals(LinearLayout.SHOW_DIVIDER_BEGINNING, parent.getShowDividers());
+ verifyVisualsOfHorizontalLayoutWithDivider(parent, LinearLayout.SHOW_DIVIDER_BEGINNING,
+ dividerSize, Color.RED, dividerPadding);
+
+ // Show only right divider
+ WidgetTestUtils.runOnMainAndDrawSync(instrumentation, parent,
+ () -> parent.setShowDividers(LinearLayout.SHOW_DIVIDER_END));
+ assertEquals(LinearLayout.SHOW_DIVIDER_END, parent.getShowDividers());
+ verifyVisualsOfHorizontalLayoutWithDivider(parent, LinearLayout.SHOW_DIVIDER_END,
+ dividerSize, Color.RED, dividerPadding);
+
+ // Show left and right dividers
+ WidgetTestUtils.runOnMainAndDrawSync(instrumentation, parent,
+ () -> parent.setShowDividers(
+ LinearLayout.SHOW_DIVIDER_BEGINNING | LinearLayout.SHOW_DIVIDER_END));
+ assertEquals(LinearLayout.SHOW_DIVIDER_BEGINNING | LinearLayout.SHOW_DIVIDER_END,
+ parent.getShowDividers());
+ verifyVisualsOfHorizontalLayoutWithDivider(parent,
+ LinearLayout.SHOW_DIVIDER_BEGINNING | LinearLayout.SHOW_DIVIDER_END,
+ dividerSize, Color.RED, dividerPadding);
+
+ // Show left and middle dividers
+ WidgetTestUtils.runOnMainAndDrawSync(instrumentation, parent,
+ () -> parent.setShowDividers(
+ LinearLayout.SHOW_DIVIDER_BEGINNING | LinearLayout.SHOW_DIVIDER_MIDDLE));
+ assertEquals(LinearLayout.SHOW_DIVIDER_BEGINNING | LinearLayout.SHOW_DIVIDER_MIDDLE,
+ parent.getShowDividers());
+ verifyVisualsOfHorizontalLayoutWithDivider(parent,
+ LinearLayout.SHOW_DIVIDER_BEGINNING | LinearLayout.SHOW_DIVIDER_MIDDLE,
+ dividerSize, Color.RED, dividerPadding);
+
+ // Show middle and right dividers
+ WidgetTestUtils.runOnMainAndDrawSync(instrumentation, parent,
+ () -> parent.setShowDividers(
+ LinearLayout.SHOW_DIVIDER_MIDDLE | LinearLayout.SHOW_DIVIDER_END));
+ assertEquals(LinearLayout.SHOW_DIVIDER_MIDDLE | LinearLayout.SHOW_DIVIDER_END,
+ parent.getShowDividers());
+ verifyVisualsOfHorizontalLayoutWithDivider(parent,
+ LinearLayout.SHOW_DIVIDER_MIDDLE | LinearLayout.SHOW_DIVIDER_END,
+ dividerSize, Color.RED, dividerPadding);
+
+ // Show left, middle and right dividers
+ WidgetTestUtils.runOnMainAndDrawSync(instrumentation, parent,
+ () -> parent.setShowDividers(
+ LinearLayout.SHOW_DIVIDER_BEGINNING | LinearLayout.SHOW_DIVIDER_MIDDLE
+ | LinearLayout.SHOW_DIVIDER_END));
+ assertEquals(
+ LinearLayout.SHOW_DIVIDER_BEGINNING | LinearLayout.SHOW_DIVIDER_MIDDLE
+ | LinearLayout.SHOW_DIVIDER_END,
+ parent.getShowDividers());
+ verifyVisualsOfHorizontalLayoutWithDivider(parent,
+ LinearLayout.SHOW_DIVIDER_BEGINNING | LinearLayout.SHOW_DIVIDER_MIDDLE
+ | LinearLayout.SHOW_DIVIDER_END,
+ dividerSize, Color.RED, dividerPadding);
+ }
+
private class MockListView extends ListView {
private final static int DEFAULT_CHILD_BASE_LINE = 1;
diff --git a/tests/tests/widget/src/android/widget/cts/LinearLayout_LayoutParamsTest.java b/tests/tests/widget/src/android/widget/cts/LinearLayout_LayoutParamsTest.java
index 4f36e94..d936984 100644
--- a/tests/tests/widget/src/android/widget/cts/LinearLayout_LayoutParamsTest.java
+++ b/tests/tests/widget/src/android/widget/cts/LinearLayout_LayoutParamsTest.java
@@ -16,17 +16,16 @@
package android.widget.cts;
-import android.widget.cts.R;
-
-import org.xmlpull.v1.XmlPullParserException;
-
import android.content.res.XmlResourceParser;
import android.test.AndroidTestCase;
+import android.view.Gravity;
import android.view.ViewGroup.LayoutParams;
import android.view.ViewGroup.MarginLayoutParams;
import android.widget.LinearLayout;
import android.widget.cts.util.XmlUtils;
+import org.xmlpull.v1.XmlPullParserException;
+
import java.io.IOException;
public class LinearLayout_LayoutParamsTest extends AndroidTestCase {
@@ -34,17 +33,47 @@
XmlResourceParser p = mContext.getResources().getLayout(R.layout.linearlayout_layout);
XmlUtils.beginDocument(p, "LinearLayout");
- new LinearLayout.LayoutParams(getContext(), p);
+ LinearLayout.LayoutParams linearLayoutParams =
+ new LinearLayout.LayoutParams(getContext(), p);
+ assertEquals(LayoutParams.MATCH_PARENT, linearLayoutParams.width);
+ assertEquals(LayoutParams.WRAP_CONTENT, linearLayoutParams.height);
+ assertEquals(0.0f, linearLayoutParams.weight);
+ assertEquals(-1, linearLayoutParams.gravity);
- new LinearLayout.LayoutParams(320, 240);
+ linearLayoutParams = new LinearLayout.LayoutParams(320, 240);
+ assertEquals(320, linearLayoutParams.width);
+ assertEquals(240, linearLayoutParams.height);
+ assertEquals(0.0f, linearLayoutParams.weight);
+ assertEquals(-1, linearLayoutParams.gravity);
- new LinearLayout.LayoutParams(320, 240, 0);
+ linearLayoutParams = new LinearLayout.LayoutParams(360, 320, 0.4f);
+ assertEquals(360, linearLayoutParams.width);
+ assertEquals(320, linearLayoutParams.height);
+ assertEquals(0.4f, linearLayoutParams.weight);
+ assertEquals(-1, linearLayoutParams.gravity);
- LayoutParams layoutParams = new LayoutParams(320, 480);
- new LinearLayout.LayoutParams(layoutParams);
+ LayoutParams layoutParams = new LayoutParams(200, 480);
+ linearLayoutParams = new LinearLayout.LayoutParams(layoutParams);
+ assertEquals(200, linearLayoutParams.width);
+ assertEquals(480, linearLayoutParams.height);
+ assertEquals(0.0f, linearLayoutParams.weight);
+ assertEquals(-1, linearLayoutParams.gravity);
- MarginLayoutParams marginLayoutParams = new MarginLayoutParams(320, 480);
- new LinearLayout.LayoutParams(marginLayoutParams);
+ MarginLayoutParams marginLayoutParams = new MarginLayoutParams(320, 200);
+ linearLayoutParams = new LinearLayout.LayoutParams(marginLayoutParams);
+ assertEquals(320, linearLayoutParams.width);
+ assertEquals(200, linearLayoutParams.height);
+ assertEquals(0.0f, linearLayoutParams.weight);
+ assertEquals(-1, linearLayoutParams.gravity);
+
+ LinearLayout.LayoutParams linearLayoutParams2 = new LinearLayout.LayoutParams(360, 720);
+ linearLayoutParams2.weight = 0.9f;
+ linearLayoutParams2.gravity = Gravity.RIGHT;
+ linearLayoutParams = new LinearLayout.LayoutParams(linearLayoutParams2);
+ assertEquals(360, linearLayoutParams.width);
+ assertEquals(720, linearLayoutParams.height);
+ assertEquals(0.9f, linearLayoutParams.weight);
+ assertEquals(Gravity.RIGHT, linearLayoutParams.gravity);
}
public void testDebug() {
diff --git a/tests/tests/widget/src/android/widget/cts/ListPopupWindowTest.java b/tests/tests/widget/src/android/widget/cts/ListPopupWindowTest.java
index b50f8c9..8848e99 100644
--- a/tests/tests/widget/src/android/widget/cts/ListPopupWindowTest.java
+++ b/tests/tests/widget/src/android/widget/cts/ListPopupWindowTest.java
@@ -16,14 +16,17 @@
package android.widget.cts;
+import static org.mockito.Mockito.*;
+
import android.app.Activity;
import android.app.Instrumentation;
import android.content.Context;
-import android.cts.util.KeyEventUtil;
+import android.cts.util.CtsKeyEventUtil;
+import android.cts.util.CtsTouchUtils;
+import android.cts.util.WidgetTestUtils;
import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
-import android.os.SystemClock;
import android.platform.test.annotations.Presubmit;
import android.support.test.InstrumentationRegistry;
import android.test.ActivityInstrumentationTestCase2;
@@ -32,7 +35,6 @@
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.LayoutInflater;
-import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
@@ -43,16 +45,12 @@
import android.widget.ListView;
import android.widget.PopupWindow;
import android.widget.TextView;
-import android.widget.cts.util.ViewTestUtils;
-
-import static org.mockito.Mockito.*;
@SmallTest
public class ListPopupWindowTest extends
ActivityInstrumentationTestCase2<ListPopupWindowCtsActivity> {
private Instrumentation mInstrumentation;
private Activity mActivity;
- private KeyEventUtil mKeyEventUtil;
private Builder mPopupWindowBuilder;
/** The list popup window. */
@@ -86,7 +84,6 @@
mInstrumentation = getInstrumentation();
mActivity = getActivity();
mItemClickListener = new PopupItemClickListener();
- mKeyEventUtil = new KeyEventUtil(mInstrumentation);
}
@Override
@@ -105,7 +102,16 @@
new ListPopupWindow(mActivity, null, android.R.attr.popupWindowStyle);
+ new ListPopupWindow(mActivity, null, 0,
+ android.R.style.Widget_DeviceDefault_ListPopupWindow);
+
+ new ListPopupWindow(mActivity, null, 0,
+ android.R.style.Widget_DeviceDefault_Light_ListPopupWindow);
+
new ListPopupWindow(mActivity, null, 0, android.R.style.Widget_Material_ListPopupWindow);
+
+ new ListPopupWindow(mActivity, null, 0,
+ android.R.style.Widget_Material_Light_ListPopupWindow);
}
public void testNoDefaultVisibility() {
@@ -358,7 +364,7 @@
mPopupWindow.getSoftInputMode());
}
- private void verifyDismissalViaTouch(boolean setupAsModal) throws Throwable {
+ private void verifyDismissalViaTouch(boolean setupAsModal) {
// Register a click listener on the top-level container
final View mainContainer = mActivity.findViewById(R.id.main_container);
View.OnClickListener mockContainerClickListener = mock(View.OnClickListener.class);
@@ -372,49 +378,20 @@
// Make sure that the modality of the popup window is set up correctly
assertEquals("Popup window modality", setupAsModal, mPopupWindow.isModal());
- // Determine the location of the popup on the screen so that we can emulate
- // a tap outside of its bounds to dismiss it
- final int[] popupOnScreenXY = new int[2];
- final Rect rect = new Rect();
- mPopupWindow.getListView().getLocationOnScreen(popupOnScreenXY);
- mPopupWindow.getBackground().getPadding(rect);
-
- int emulatedTapX = popupOnScreenXY[0] - rect.left - 20;
- int emulatedTapY = popupOnScreenXY[1] + mPopupWindow.getListView().getHeight() +
- rect.top + rect.bottom + 20;
-
// The logic below uses Instrumentation to emulate a tap outside the bounds of the
// displayed list popup window. This tap is then treated by the framework to be "split" as
// the ACTION_OUTSIDE for the popup itself, as well as DOWN / MOVE / UP for the underlying
// view root if the popup is not modal.
// It is not correct to emulate these two sequences separately in the test, as it
- // wouldn't emulate the user-facing interaction for this test. Note that usage
- // of Instrumentation is necessary here since Espresso's actions operate at the level
- // of view or data. Also, we don't want to use View.dispatchTouchEvent directly as
- // that would require emulation of two separate sequences as well.
-
- Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
-
- // Inject DOWN event
- long downTime = SystemClock.uptimeMillis();
- MotionEvent eventDown = MotionEvent.obtain(
- downTime, downTime, MotionEvent.ACTION_DOWN, emulatedTapX, emulatedTapY, 1);
- instrumentation.sendPointerSync(eventDown);
-
- // Inject MOVE event
- long moveTime = SystemClock.uptimeMillis();
- MotionEvent eventMove = MotionEvent.obtain(
- moveTime, moveTime, MotionEvent.ACTION_MOVE, emulatedTapX, emulatedTapY, 1);
- instrumentation.sendPointerSync(eventMove);
-
- // Inject UP event
- long upTime = SystemClock.uptimeMillis();
- MotionEvent eventUp = MotionEvent.obtain(
- upTime, upTime, MotionEvent.ACTION_UP, emulatedTapX, emulatedTapY, 1);
- instrumentation.sendPointerSync(eventUp);
-
- // Wait for the system to process all events in the queue
- instrumentation.waitForIdleSync();
+ // wouldn't emulate the user-facing interaction for this test. Also, we don't want to use
+ // View.dispatchTouchEvent directly as that would require emulation of two separate
+ // sequences as well.
+ final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+ final ListView popupListView = mPopupWindow.getListView();
+ final Rect rect = new Rect();
+ mPopupWindow.getBackground().getPadding(rect);
+ CtsTouchUtils.emulateTapOnScreen(instrumentation, popupListView,
+ -rect.left - 20, popupListView.getHeight() + rect.top + rect.bottom + 20);
// At this point our popup should not be showing and should have notified its
// dismiss listener
@@ -426,19 +403,19 @@
verify(mockContainerClickListener, times(setupAsModal ? 0 : 1)).onClick(mainContainer);
}
- public void testDismissalOutsideNonModal() throws Throwable {
+ public void testDismissalOutsideNonModal() {
verifyDismissalViaTouch(false);
}
- public void testDismissalOutsideModal() throws Throwable {
+ public void testDismissalOutsideModal() {
verifyDismissalViaTouch(true);
}
- public void testItemClicks() throws Throwable {
+ public void testItemClicks() {
mPopupWindowBuilder = new Builder().withItemClickListener().withDismissListener();
mPopupWindowBuilder.show();
- runTestOnUiThread(() -> mPopupWindow.performItemClick(2));
+ mInstrumentation.runOnMainSync(() -> mPopupWindow.performItemClick(2));
mInstrumentation.waitForIdleSync();
verify(mPopupWindowBuilder.mOnItemClickListener, times(1)).onItemClick(
@@ -448,7 +425,8 @@
verify(mPopupWindowBuilder.mOnDismissListener, times(1)).onDismiss();
mPopupWindowBuilder.showAgain();
- runTestOnUiThread(() -> mPopupWindow.getListView().performItemClick(null, 1, 1));
+ mInstrumentation.runOnMainSync(
+ () -> mPopupWindow.getListView().performItemClick(null, 1, 1));
mInstrumentation.waitForIdleSync();
verify(mPopupWindowBuilder.mOnItemClickListener, times(1)).onItemClick(
@@ -461,7 +439,7 @@
verifyNoMoreInteractions(mPopupWindowBuilder.mOnItemClickListener);
}
- public void testPromptViewAbove() throws Throwable {
+ public void testPromptViewAbove() {
final View promptView = LayoutInflater.from(mActivity).inflate(
R.layout.popupwindow_prompt, null);
mPopupWindowBuilder = new Builder().withPrompt(
@@ -477,7 +455,7 @@
promptView.getLocationOnScreen(promptViewOnScreenXY);
final ListView listView = mPopupWindow.getListView();
- ViewTestUtils.runOnMainAndDrawSync(mInstrumentation, listView, null);
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, listView, null);
final View firstListChild = listView.getChildAt(0);
final int[] firstChildOnScreenXY = new int[2];
@@ -486,7 +464,7 @@
assertTrue(promptViewOnScreenXY[1] + promptView.getHeight() <= firstChildOnScreenXY[1]);
}
- public void testPromptViewBelow() throws Throwable {
+ public void testPromptViewBelow() {
final View promptView = LayoutInflater.from(mActivity).inflate(
R.layout.popupwindow_prompt, null);
mPopupWindowBuilder = new Builder().withPrompt(
@@ -499,7 +477,7 @@
assertEquals(ListPopupWindow.POSITION_PROMPT_BELOW, mPopupWindow.getPromptPosition());
final ListView listView = mPopupWindow.getListView();
- ViewTestUtils.runOnMainAndDrawSync(mInstrumentation, listView, null);
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, listView, null);
final int[] promptViewOnScreenXY = new int[2];
promptView.getLocationOnScreen(promptViewOnScreenXY);
@@ -512,14 +490,14 @@
}
@Presubmit
- public void testAccessSelection() throws Throwable {
+ public void testAccessSelection() {
mPopupWindowBuilder = new Builder().withItemSelectedListener();
mPopupWindowBuilder.show();
final ListView listView = mPopupWindow.getListView();
// Select an item
- ViewTestUtils.runOnMainAndDrawSync(mInstrumentation, listView,
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, listView,
() -> mPopupWindow.setSelection(1));
// And verify the current selection state + selection listener invocation
@@ -534,7 +512,7 @@
((TextView) selectedView.findViewById(android.R.id.text1)).getText());
// Select another item
- ViewTestUtils.runOnMainAndDrawSync(mInstrumentation, listView,
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, listView,
() -> mPopupWindow.setSelection(3));
// And verify the new selection state + selection listener invocation
@@ -549,8 +527,8 @@
((TextView) selectedView.findViewById(android.R.id.text1)).getText());
// Clear selection
- ViewTestUtils.runOnMainAndDrawSync(mInstrumentation, listView,
- () -> mPopupWindow.clearListSelection());
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, listView,
+ mPopupWindow::clearListSelection);
// And verify empty selection state + no more selection listener invocation
verify(mPopupWindowBuilder.mOnItemSelectedListener, times(1)).onNothingSelected(
@@ -562,7 +540,7 @@
verifyNoMoreInteractions(mPopupWindowBuilder.mOnItemSelectedListener);
}
- public void testNoDefaultDismissalWithBackButton() throws Throwable {
+ public void testNoDefaultDismissalWithBackButton() {
mPopupWindowBuilder = new Builder().withDismissListener();
mPopupWindowBuilder.show();
@@ -574,7 +552,7 @@
assertTrue(mPopupWindow.isShowing());
}
- public void testCustomDismissalWithBackButton() throws Throwable {
+ public void testCustomDismissalWithBackButton() {
mPopupWindowBuilder = new Builder().withAnchor(R.id.anchor_upper_left)
.withDismissListener();
mPopupWindowBuilder.show();
@@ -584,7 +562,7 @@
(MockViewForListPopupWindow) mPopupWindow.getAnchorView();
anchor.wireTo(mPopupWindow);
// Request focus on our EditText
- runTestOnUiThread(() -> anchor.requestFocus());
+ mInstrumentation.runOnMainSync(anchor::requestFocus);
mInstrumentation.waitForIdleSync();
assertTrue(anchor.isFocused());
@@ -596,7 +574,7 @@
assertFalse(mPopupWindow.isShowing());
}
- public void testListSelectionWithDPad() throws Throwable {
+ public void testListSelectionWithDPad() {
mPopupWindowBuilder = new Builder().withAnchor(R.id.anchor_upper_left)
.withDismissListener().withItemSelectedListener();
mPopupWindowBuilder.show();
@@ -608,13 +586,13 @@
(MockViewForListPopupWindow) mPopupWindow.getAnchorView();
anchor.wireTo(mPopupWindow);
// Request focus on our EditText
- runTestOnUiThread(() -> anchor.requestFocus());
+ mInstrumentation.runOnMainSync(anchor::requestFocus);
mInstrumentation.waitForIdleSync();
assertTrue(anchor.isFocused());
// Select entry #1 in the popup list
final ListView listView = mPopupWindow.getListView();
- ViewTestUtils.runOnMainAndDrawSync(mInstrumentation, listView,
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, listView,
() -> mPopupWindow.setSelection(1));
verify(mPopupWindowBuilder.mOnItemSelectedListener, times(1)).onItemSelected(
any(AdapterView.class), any(View.class), eq(1), eq(1L));
@@ -622,10 +600,10 @@
// Send DPAD_DOWN key event. As our custom extension of EditText calls
// ListPopupWindow.onKeyDown and onKeyUp, the end result should be transfer of selection
// down one row
- mKeyEventUtil.sendKeyDownUp(listView, KeyEvent.KEYCODE_DPAD_DOWN);
+ CtsKeyEventUtil.sendKeyDownUp(mInstrumentation, listView, KeyEvent.KEYCODE_DPAD_DOWN);
mInstrumentation.waitForIdleSync();
- ViewTestUtils.runOnMainAndDrawSync(mInstrumentation, root, null);
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, root, null);
// At this point we expect that item #2 was selected
verify(mPopupWindowBuilder.mOnItemSelectedListener, times(1)).onItemSelected(
@@ -634,10 +612,10 @@
// Send a DPAD_UP key event. As our custom extension of EditText calls
// ListPopupWindow.onKeyDown and onKeyUp, the end result should be transfer of selection
// up one row
- mKeyEventUtil.sendKeyDownUp(listView, KeyEvent.KEYCODE_DPAD_UP);
+ CtsKeyEventUtil.sendKeyDownUp(mInstrumentation, listView, KeyEvent.KEYCODE_DPAD_UP);
mInstrumentation.waitForIdleSync();
- ViewTestUtils.runOnMainAndDrawSync(mInstrumentation, root, null);
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, root, null);
// At this point we expect that item #1 was selected
verify(mPopupWindowBuilder.mOnItemSelectedListener, times(2)).onItemSelected(
@@ -646,10 +624,10 @@
// Send one more DPAD_UP key event. As our custom extension of EditText calls
// ListPopupWindow.onKeyDown and onKeyUp, the end result should be transfer of selection
// up one more row
- mKeyEventUtil.sendKeyDownUp(listView, KeyEvent.KEYCODE_DPAD_UP);
+ CtsKeyEventUtil.sendKeyDownUp(mInstrumentation, listView, KeyEvent.KEYCODE_DPAD_UP);
mInstrumentation.waitForIdleSync();
- ViewTestUtils.runOnMainAndDrawSync(mInstrumentation, root, null);
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, root, null);
// At this point we expect that item #0 was selected
verify(mPopupWindowBuilder.mOnItemSelectedListener, times(1)).onItemSelected(
@@ -658,7 +636,7 @@
// Send ENTER key event. As our custom extension of EditText calls
// ListPopupWindow.onKeyDown and onKeyUp, the end result should be dismissal of
// the popup window
- mKeyEventUtil.sendKeyDownUp(listView, KeyEvent.KEYCODE_ENTER);
+ CtsKeyEventUtil.sendKeyDownUp(mInstrumentation,listView, KeyEvent.KEYCODE_ENTER);
mInstrumentation.waitForIdleSync();
verify(mPopupWindowBuilder.mOnDismissListener, times(1)).onDismiss();
@@ -668,41 +646,7 @@
verifyNoMoreInteractions(mPopupWindowBuilder.mOnDismissListener);
}
- /**
- * Emulates a drag-down gestures by injecting ACTION events with {@link Instrumentation}.
- */
- private void emulateDragDownGesture(int emulatedX, int emulatedStartY, int swipeAmount) {
- // The logic below uses Instrumentation to emulate a swipe / drag gesture to bring up
- // the popup content.
-
- // Inject DOWN event
- long downTime = SystemClock.uptimeMillis();
- MotionEvent eventDown = MotionEvent.obtain(
- downTime, downTime, MotionEvent.ACTION_DOWN, emulatedX, emulatedStartY, 1);
- mInstrumentation.sendPointerSync(eventDown);
-
- // Inject a sequence of MOVE events that emulate a "swipe down" gesture
- for (int i = 0; i < 10; i++) {
- long moveTime = SystemClock.uptimeMillis();
- final int moveY = emulatedStartY + swipeAmount * i / 10;
- MotionEvent eventMove = MotionEvent.obtain(
- moveTime, moveTime, MotionEvent.ACTION_MOVE, emulatedX, moveY, 1);
- mInstrumentation.sendPointerSync(eventMove);
- // sleep for a bit to emulate a 200ms swipe
- SystemClock.sleep(20);
- }
-
- // Inject UP event
- long upTime = SystemClock.uptimeMillis();
- MotionEvent eventUp = MotionEvent.obtain(
- upTime, upTime, MotionEvent.ACTION_UP, emulatedX, emulatedStartY + swipeAmount, 1);
- mInstrumentation.sendPointerSync(eventUp);
-
- // Wait for the system to process all events in the queue
- mInstrumentation.waitForIdleSync();
- }
-
- public void testCreateOnDragListener() throws Throwable {
+ public void testCreateOnDragListener() {
// In this test we want precise control over the height of the popup content since
// we need to know by how much to swipe down to end the emulated gesture over the
// specific item in the popup. This is why we're using a popup style that removes
@@ -738,7 +682,8 @@
int swipeAmount = 2 * popupRowHeight;
// Emulate drag-down gesture with a sequence of motion events
- emulateDragDownGesture(emulatedX, emulatedStartY, swipeAmount);
+ CtsTouchUtils.emulateDragGesture(getInstrumentation(), emulatedX, emulatedStartY,
+ 0, swipeAmount);
// We expect the swipe / drag gesture to result in clicking the second item in our list.
verify(mPopupWindowBuilder.mOnItemClickListener, times(1)).onItemClick(
@@ -952,7 +897,8 @@
if (mHasItemSelectedListener) {
mOnItemSelectedListener = mock(AdapterView.OnItemSelectedListener.class);
mPopupWindow.setOnItemSelectedListener(mOnItemSelectedListener);
- mPopupWindow.setListSelector(mActivity.getDrawable(R.drawable.red_fill));
+ mPopupWindow.setListSelector(
+ mActivity.getDrawable(R.drawable.red_translucent_fill));
}
if (mHasDismissListener) {
diff --git a/tests/tests/widget/src/android/widget/cts/ListViewCtsActivity.java b/tests/tests/widget/src/android/widget/cts/ListViewCtsActivity.java
index ce198b3..1d1b42f 100644
--- a/tests/tests/widget/src/android/widget/cts/ListViewCtsActivity.java
+++ b/tests/tests/widget/src/android/widget/cts/ListViewCtsActivity.java
@@ -16,10 +16,9 @@
package android.widget.cts;
-import android.widget.cts.R;
-
import android.app.Activity;
import android.os.Bundle;
+import android.widget.cts.R;
public class ListViewCtsActivity extends Activity {
@Override
diff --git a/hostsidetests/services/activitymanager/app/src/android/server/app/SlowCreateActivity.java b/tests/tests/widget/src/android/widget/cts/ListViewFixedCtsActivity.java
similarity index 76%
copy from hostsidetests/services/activitymanager/app/src/android/server/app/SlowCreateActivity.java
copy to tests/tests/widget/src/android/widget/cts/ListViewFixedCtsActivity.java
index 481f5be..f34a452 100644
--- a/hostsidetests/services/activitymanager/app/src/android/server/app/SlowCreateActivity.java
+++ b/tests/tests/widget/src/android/widget/cts/ListViewFixedCtsActivity.java
@@ -14,17 +14,15 @@
* limitations under the License.
*/
-package android.server.app;
+package android.widget.cts;
import android.app.Activity;
import android.os.Bundle;
-public class SlowCreateActivity extends Activity {
+public class ListViewFixedCtsActivity extends Activity {
@Override
- protected void onCreate(Bundle savedInstanceState) {
- try {
- Thread.sleep(2000);
- } catch(InterruptedException e) {}
+ public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ setContentView(R.layout.listviewfixed_layout);
}
}
diff --git a/tests/tests/widget/src/android/widget/cts/ListViewTest.java b/tests/tests/widget/src/android/widget/cts/ListViewTest.java
index 41fb14b..4324314 100644
--- a/tests/tests/widget/src/android/widget/cts/ListViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/ListViewTest.java
@@ -16,23 +16,25 @@
package android.widget.cts;
-import junit.framework.Assert;
-
-import org.xmlpull.v1.XmlPullParser;
+import static org.mockito.Mockito.*;
import android.app.ActionBar.LayoutParams;
import android.app.Activity;
import android.app.Instrumentation;
import android.content.Context;
import android.cts.util.PollingCheck;
+import android.cts.util.WidgetTestUtils;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
+import android.os.Parcelable;
import android.test.ActivityInstrumentationTestCase2;
import android.test.UiThreadTest;
+import android.test.suitebuilder.annotation.LargeTest;
import android.test.suitebuilder.annotation.MediumTest;
+import android.test.suitebuilder.annotation.SmallTest;
import android.util.AttributeSet;
import android.util.Pair;
import android.util.SparseArray;
@@ -43,48 +45,50 @@
import android.view.View.MeasureSpec;
import android.view.ViewGroup;
import android.view.animation.LayoutAnimationController;
+import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.FrameLayout;
-import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;
-import android.widget.cts.R;
-import android.widget.cts.util.ViewTestUtils;
+import android.widget.cts.util.TestUtils;
+
+import junit.framework.Assert;
+
+import org.xmlpull.v1.XmlPullParser;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.anyLong;
-import static org.mockito.Mockito.atLeast;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-
+@SmallTest
public class ListViewTest extends ActivityInstrumentationTestCase2<ListViewCtsActivity> {
private final String[] mCountryList = new String[] {
"Argentina", "Australia", "China", "France", "Germany", "Italy", "Japan", "United States"
};
+ private final String[] mLongCountryList = new String[] {
+ "Argentina", "Australia", "Belize", "Botswana", "Brazil", "Cameroon", "China", "Cyprus",
+ "Denmark", "Djibouti", "Ethiopia", "Fiji", "Finland", "France", "Gabon", "Germany",
+ "Ghana", "Haiti", "Honduras", "Iceland", "India", "Indonesia", "Ireland", "Italy",
+ "Japan", "Kiribati", "Laos", "Lesotho", "Liberia", "Malaysia", "Mongolia", "Myanmar",
+ "Nauru", "Norway", "Oman", "Pakistan", "Philippines", "Portugal", "Romania", "Russia",
+ "Rwanda", "Singapore", "Slovakia", "Slovenia", "Somalia", "Swaziland", "Togo", "Tuvalu",
+ "Uganda", "Ukraine", "United States", "Vanuatu", "Venezuela", "Zimbabwe"
+ };
private final String[] mNameList = new String[] {
"Jacky", "David", "Kevin", "Michael", "Andy"
};
- private final String[] mEmptyList = new String[0];
private ListView mListView;
private Activity mActivity;
private Instrumentation mInstrumentation;
private AttributeSet mAttributeSet;
private ArrayAdapter<String> mAdapter_countries;
+ private ArrayAdapter<String> mAdapter_longCountries;
private ArrayAdapter<String> mAdapter_names;
- private ArrayAdapter<String> mAdapter_empty;
public ListViewTest() {
super("android.widget.cts", ListViewCtsActivity.class);
@@ -98,12 +102,12 @@
XmlPullParser parser = mActivity.getResources().getXml(R.layout.listview_layout);
mAttributeSet = Xml.asAttributeSet(parser);
- mAdapter_countries = new ArrayAdapter<String>(mActivity,
+ mAdapter_countries = new ArrayAdapter<>(mActivity,
android.R.layout.simple_list_item_1, mCountryList);
- mAdapter_names = new ArrayAdapter<String>(mActivity, android.R.layout.simple_list_item_1,
+ mAdapter_longCountries = new ArrayAdapter<>(mActivity,
+ android.R.layout.simple_list_item_1, mLongCountryList);
+ mAdapter_names = new ArrayAdapter<>(mActivity, android.R.layout.simple_list_item_1,
mNameList);
- mAdapter_empty = new ArrayAdapter<String>(mActivity, android.R.layout.simple_list_item_1,
- mEmptyList);
mListView = (ListView) mActivity.findViewById(R.id.listview_default);
}
@@ -136,40 +140,40 @@
}
public void testGetMaxScrollAmount() {
- setAdapter(mAdapter_empty);
- int scrollAmount = mListView.getMaxScrollAmount();
- assertEquals(0, scrollAmount);
-
setAdapter(mAdapter_names);
- scrollAmount = mListView.getMaxScrollAmount();
+ int scrollAmount = mListView.getMaxScrollAmount();
assertTrue(scrollAmount > 0);
+
+ mInstrumentation.runOnMainSync(() -> {
+ mListView.getLayoutParams().height = 0;
+ mListView.requestLayout();
+ });
+ PollingCheck.waitFor(() -> mListView.getHeight() == 0);
+
+ scrollAmount = mListView.getMaxScrollAmount();
+ assertEquals(0, scrollAmount);
}
private void setAdapter(final ArrayAdapter<String> adapter) {
- ViewTestUtils.runOnMainAndDrawSync(mInstrumentation, mListView,
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mListView,
() -> mListView.setAdapter(adapter));
}
public void testAccessDividerHeight() {
- ViewTestUtils.runOnMainAndDrawSync(mInstrumentation, mListView,
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mListView,
() -> mListView.setAdapter(mAdapter_countries));
Drawable d = mListView.getDivider();
final Rect r = d.getBounds();
- new PollingCheck() {
- @Override
- protected boolean check() {
- return r.bottom - r.top > 0;
- }
- }.run();
+ PollingCheck.waitFor(() -> r.bottom - r.top > 0);
- ViewTestUtils.runOnMainAndDrawSync(mInstrumentation, mListView,
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mListView,
() -> mListView.setDividerHeight(20));
assertEquals(20, mListView.getDividerHeight());
assertEquals(20, r.bottom - r.top);
- ViewTestUtils.runOnMainAndDrawSync(mInstrumentation, mListView,
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mListView,
() -> mListView.setDividerHeight(10));
assertEquals(10, mListView.getDividerHeight());
@@ -187,13 +191,13 @@
}
public void testAccessAdapter() {
- ViewTestUtils.runOnMainAndDrawSync(mInstrumentation, mListView,
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mListView,
() -> mListView.setAdapter(mAdapter_countries));
assertSame(mAdapter_countries, mListView.getAdapter());
assertEquals(mCountryList.length, mListView.getCount());
- ViewTestUtils.runOnMainAndDrawSync(mInstrumentation, mListView,
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mListView,
() -> mListView.setAdapter(mAdapter_names));
assertSame(mAdapter_names, mListView.getAdapter());
@@ -280,25 +284,31 @@
footerView2.setText("footerview2");
mInstrumentation.runOnMainSync(() -> mListView.setFooterDividersEnabled(true));
+ assertTrue(mListView.areFooterDividersEnabled());
assertEquals(0, mListView.getFooterViewsCount());
mInstrumentation.runOnMainSync(() -> mListView.addFooterView(footerView1, null, true));
+ assertTrue(mListView.areFooterDividersEnabled());
assertEquals(1, mListView.getFooterViewsCount());
- mInstrumentation.runOnMainSync(() -> mListView.addFooterView(footerView2));
-
- mInstrumentation.waitForIdleSync();
+ mInstrumentation.runOnMainSync(() -> {
+ mListView.setFooterDividersEnabled(false);
+ mListView.addFooterView(footerView2);
+ });
+ assertFalse(mListView.areFooterDividersEnabled());
assertEquals(2, mListView.getFooterViewsCount());
- ViewTestUtils.runOnMainAndDrawSync(mInstrumentation, mListView,
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mListView,
() -> mListView.setAdapter(mAdapter_countries));
- ViewTestUtils.runOnMainAndDrawSync(mInstrumentation, mListView,
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mListView,
() -> mListView.removeFooterView(footerView1));
+ assertFalse(mListView.areFooterDividersEnabled());
assertEquals(1, mListView.getFooterViewsCount());
- ViewTestUtils.runOnMainAndDrawSync(mInstrumentation, mListView,
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mListView,
() -> mListView.removeFooterView(footerView2));
+ assertFalse(mListView.areFooterDividersEnabled());
assertEquals(0, mListView.getFooterViewsCount());
}
@@ -307,18 +317,28 @@
final TextView headerView2 = (TextView) mActivity.findViewById(R.id.headerview2);
mInstrumentation.runOnMainSync(() -> mListView.setHeaderDividersEnabled(true));
+ assertTrue(mListView.areHeaderDividersEnabled());
assertEquals(0, mListView.getHeaderViewsCount());
mInstrumentation.runOnMainSync(() -> mListView.addHeaderView(headerView2, null, true));
+ assertTrue(mListView.areHeaderDividersEnabled());
assertEquals(1, mListView.getHeaderViewsCount());
- mInstrumentation.runOnMainSync(() -> mListView.addHeaderView(headerView1));
+ mInstrumentation.runOnMainSync(() -> {
+ mListView.setHeaderDividersEnabled(false);
+ mListView.addHeaderView(headerView1);
+ });
+ assertFalse(mListView.areHeaderDividersEnabled());
assertEquals(2, mListView.getHeaderViewsCount());
+
+ mInstrumentation.runOnMainSync(() -> mListView.removeHeaderView(headerView2));
+ assertFalse(mListView.areHeaderDividersEnabled());
+ assertEquals(1, mListView.getHeaderViewsCount());
}
public void testHeaderFooterType() throws Throwable {
final TextView headerView = new TextView(getActivity());
- final List<Pair<View, View>> mismatch = new ArrayList<Pair<View, View>>();
+ final List<Pair<View, View>> mismatch = new ArrayList<>();
final ArrayAdapter adapter = new ArrayAdapter<String>(mActivity,
android.R.layout.simple_list_item_1, mNameList) {
@Override
@@ -331,7 +351,7 @@
public View getView(int position, View convertView, ViewGroup parent) {
if (position == 0) {
if (convertView != null && convertView != headerView) {
- mismatch.add(new Pair<View, View>(headerView, convertView));
+ mismatch.add(new Pair<>(headerView, convertView));
}
return headerView;
} else {
@@ -344,57 +364,52 @@
return super.getCount() + 1;
}
};
- ViewTestUtils.runOnMainAndDrawSync(mInstrumentation, mListView,
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mListView,
() -> mListView.setAdapter(adapter));
- ViewTestUtils.runOnMainAndDrawSync(mInstrumentation, mListView,
- () -> adapter.notifyDataSetChanged());
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mListView,
+ adapter::notifyDataSetChanged);
assertEquals(0, mismatch.size());
}
public void testAccessDivider() {
- ViewTestUtils.runOnMainAndDrawSync(mInstrumentation, mListView,
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mListView,
() -> mListView.setAdapter(mAdapter_countries));
Drawable defaultDrawable = mListView.getDivider();
final Rect r = defaultDrawable.getBounds();
- new PollingCheck() {
- @Override
- protected boolean check() {
- return r.bottom - r.top > 0;
- }
- }.run();
+ PollingCheck.waitFor(() -> r.bottom - r.top > 0);
final Drawable d = mActivity.getResources().getDrawable(R.drawable.scenery);
- ViewTestUtils.runOnMainAndDrawSync(mInstrumentation, mListView,
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mListView,
() -> mListView.setDivider(d));
assertSame(d, mListView.getDivider());
assertEquals(d.getBounds().height(), mListView.getDividerHeight());
- ViewTestUtils.runOnMainAndDrawSync(mInstrumentation, mListView,
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mListView,
() -> mListView.setDividerHeight(10));
assertEquals(10, mListView.getDividerHeight());
assertEquals(10, d.getBounds().height());
}
public void testSetSelection() {
- ViewTestUtils.runOnMainAndDrawSync(mInstrumentation, mListView,
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mListView,
() -> mListView.setAdapter(mAdapter_countries));
- ViewTestUtils.runOnMainAndDrawSync(mInstrumentation, mListView,
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mListView,
() -> mListView.setSelection(1));
String item = (String) mListView.getSelectedItem();
assertEquals(mCountryList[1], item);
- ViewTestUtils.runOnMainAndDrawSync(mInstrumentation, mListView,
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mListView,
() -> mListView.setSelectionFromTop(5, 0));
item = (String) mListView.getSelectedItem();
assertEquals(mCountryList[5], item);
- ViewTestUtils.runOnMainAndDrawSync(mInstrumentation, mListView,
- () -> mListView.setSelectionAfterHeaderView());
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mListView,
+ mListView::setSelectionAfterHeaderView);
item = (String) mListView.getSelectedItem();
assertEquals(mCountryList[0], item);
}
@@ -404,11 +419,11 @@
}
public void testPerformItemClick() {
- ViewTestUtils.runOnMainAndDrawSync(mInstrumentation, mListView,
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mListView,
() -> mListView.setAdapter(mAdapter_countries));
mListView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
- ViewTestUtils.runOnMainAndDrawSync(mInstrumentation, mListView,
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mListView,
() -> mListView.setSelection(2));
final TextView child = (TextView) mAdapter_countries.getView(2, null, mListView);
@@ -432,30 +447,46 @@
verifyNoMoreInteractions(onClickListener);
}
- public void testSaveAndRestoreInstanceState() {
- // implementation details, do NOT test
+ public void testSaveAndRestoreInstanceState_positionIsRestored() {
+ ListView listView = new ListView(mActivity);
+ listView.setAdapter(mAdapter_countries);
+ assertEquals(0, listView.getSelectedItemPosition());
+
+ int positionToTest = mAdapter_countries.getCount() - 1;
+ listView.setSelection(positionToTest);
+ assertEquals(positionToTest, listView.getSelectedItemPosition());
+ Parcelable savedState = listView.onSaveInstanceState();
+
+ listView.setSelection(positionToTest - 1);
+ assertEquals(positionToTest - 1, listView.getSelectedItemPosition());
+
+ listView.onRestoreInstanceState(savedState);
+ int measureSpec = View.MeasureSpec.makeMeasureSpec(100, View.MeasureSpec.EXACTLY);
+ listView.measure(measureSpec,measureSpec);
+ listView.layout(0, 0, 100, 100);
+ assertEquals(positionToTest, listView.getSelectedItemPosition());
}
public void testDispatchKeyEvent() {
- ViewTestUtils.runOnMainAndDrawSync(mInstrumentation, mListView,
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mListView,
() -> {
mListView.setAdapter(mAdapter_countries);
mListView.requestFocus();
});
assertTrue(mListView.hasFocus());
- ViewTestUtils.runOnMainAndDrawSync(mInstrumentation, mListView,
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mListView,
() -> mListView.setSelection(1));
String item = (String) mListView.getSelectedItem();
assertEquals(mCountryList[1], item);
- ViewTestUtils.runOnMainAndDrawSync(mInstrumentation, mListView,
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mListView,
() -> {
KeyEvent keyEvent = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_A);
mListView.dispatchKeyEvent(keyEvent);
});
- ViewTestUtils.runOnMainAndDrawSync(mInstrumentation, mListView,
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mListView,
() -> {
KeyEvent keyEvent = new KeyEvent(KeyEvent.ACTION_DOWN,
KeyEvent.KEYCODE_DPAD_DOWN);
@@ -468,7 +499,7 @@
}
public void testRequestChildRectangleOnScreen() {
- ViewTestUtils.runOnMainAndDrawSync(mInstrumentation, mListView,
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mListView,
() -> mListView.setAdapter(mAdapter_countries));
TextView child = (TextView) mAdapter_countries.getView(0, null, mListView);
@@ -588,7 +619,7 @@
ListView listView = new ListView(mActivity);
List<String> items = new ArrayList<>();
items.add("hello");
- Adapter<String> adapter = new Adapter<String>(mActivity, 0, items);
+ Adapter<String> adapter = new Adapter<>(mActivity, 0, items);
listView.setAdapter(adapter);
int measureSpec = View.MeasureSpec.makeMeasureSpec(100, View.MeasureSpec.EXACTLY);
@@ -613,7 +644,7 @@
listView.addHeaderView(new View(mActivity), null, false);
List<String> items = new ArrayList<>();
items.add("hello");
- Adapter<String> adapter = new Adapter<String>(mActivity, 0, items);
+ Adapter<String> adapter = new Adapter<>(mActivity, 0, items);
listView.setAdapter(adapter);
listView.setSelection(1);
@@ -634,13 +665,13 @@
@MediumTest
public void testFullDetachHeaderViewOnScroll() {
final AttachDetachAwareView header = new AttachDetachAwareView(mActivity);
- ViewTestUtils.runOnMainAndDrawSync(getInstrumentation(), mListView, () -> {
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mListView, () -> {
mListView.setAdapter(new DummyAdapter(1000));
mListView.addHeaderView(header);
});
assertEquals("test sanity", 1, header.mOnAttachCount);
assertEquals("test sanity", 0, header.mOnDetachCount);
- ViewTestUtils.runOnMainAndDrawSync(getInstrumentation(), mListView, () -> {
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mListView, () -> {
mListView.scrollListBy(mListView.getHeight() * 3);
});
assertNull("test sanity, header should be removed", header.getParent());
@@ -651,13 +682,13 @@
@MediumTest
public void testFullDetachHeaderViewOnRelayout() {
final AttachDetachAwareView header = new AttachDetachAwareView(mActivity);
- ViewTestUtils.runOnMainAndDrawSync(getInstrumentation(), mListView, () -> {
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mListView, () -> {
mListView.setAdapter(new DummyAdapter(1000));
mListView.addHeaderView(header);
});
assertEquals("test sanity", 1, header.mOnAttachCount);
assertEquals("test sanity", 0, header.mOnDetachCount);
- ViewTestUtils.runOnMainAndDrawSync(getInstrumentation(), mListView, () -> {
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mListView, () -> {
mListView.setSelection(800);
});
assertNull("test sanity, header should be removed", header.getParent());
@@ -668,7 +699,7 @@
@MediumTest
public void testFullDetachHeaderViewOnScrollForFocus() {
final AttachDetachAwareView header = new AttachDetachAwareView(mActivity);
- ViewTestUtils.runOnMainAndDrawSync(getInstrumentation(), mListView, () -> {
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mListView, () -> {
mListView.setAdapter(new DummyAdapter(1000));
mListView.addHeaderView(header);
});
@@ -677,7 +708,7 @@
while(header.getParent() != null) {
assertEquals("header view should NOT be detached", 0, header.mOnDetachCount);
sendKeys(KeyEvent.KEYCODE_DPAD_DOWN);
- ViewTestUtils.runOnMainAndDrawSync(getInstrumentation(), mListView, null);
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mListView, null);
}
assertEquals("header view should be detached", 1, header.mOnDetachCount);
assertFalse(header.isTemporarilyDetached());
@@ -686,18 +717,18 @@
@MediumTest
public void testFullyDetachUnusedViewOnScroll() {
final AttachDetachAwareView theView = new AttachDetachAwareView(mActivity);
- ViewTestUtils.runOnMainAndDrawSync(getInstrumentation(), mListView, () -> {
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mListView, () -> {
mListView.setAdapter(new DummyAdapter(1000, theView));
});
assertEquals("test sanity", 1, theView.mOnAttachCount);
assertEquals("test sanity", 0, theView.mOnDetachCount);
- ViewTestUtils.runOnMainAndDrawSync(getInstrumentation(), mListView, () -> {
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mListView, () -> {
mListView.scrollListBy(mListView.getHeight() * 2);
});
assertNull("test sanity, unused view should be removed", theView.getParent());
assertEquals("unused view should be detached", 1, theView.mOnDetachCount);
assertFalse(theView.isTemporarilyDetached());
- ViewTestUtils.runOnMainAndDrawSync(getInstrumentation(), mListView, () -> {
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mListView, () -> {
mListView.scrollListBy(-mListView.getHeight() * 2);
// listview limits scroll to 1 page which is why we call it twice here.
mListView.scrollListBy(-mListView.getHeight() * 2);
@@ -711,18 +742,18 @@
@MediumTest
public void testFullyDetachUnusedViewOnReLayout() {
final AttachDetachAwareView theView = new AttachDetachAwareView(mActivity);
- ViewTestUtils.runOnMainAndDrawSync(getInstrumentation(), mListView, () -> {
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mListView, () -> {
mListView.setAdapter(new DummyAdapter(1000, theView));
});
assertEquals("test sanity", 1, theView.mOnAttachCount);
assertEquals("test sanity", 0, theView.mOnDetachCount);
- ViewTestUtils.runOnMainAndDrawSync(getInstrumentation(), mListView, () -> {
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mListView, () -> {
mListView.setSelection(800);
});
assertNull("test sanity, unused view should be removed", theView.getParent());
assertEquals("unused view should be detached", 1, theView.mOnDetachCount);
assertFalse(theView.isTemporarilyDetached());
- ViewTestUtils.runOnMainAndDrawSync(getInstrumentation(), mListView, () -> {
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mListView, () -> {
mListView.setSelection(0);
});
assertNotNull("test sanity, view should be re-added", theView.getParent());
@@ -734,7 +765,7 @@
@MediumTest
public void testFullyDetachUnusedViewOnScrollForFocus() {
final AttachDetachAwareView theView = new AttachDetachAwareView(mActivity);
- ViewTestUtils.runOnMainAndDrawSync(getInstrumentation(), mListView, () -> {
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mListView, () -> {
mListView.setAdapter(new DummyAdapter(1000, theView));
});
assertEquals("test sanity", 1, theView.mOnAttachCount);
@@ -742,13 +773,13 @@
while(theView.getParent() != null) {
assertEquals("the view should NOT be detached", 0, theView.mOnDetachCount);
sendKeys(KeyEvent.KEYCODE_DPAD_DOWN);
- ViewTestUtils.runOnMainAndDrawSync(getInstrumentation(), mListView, null);
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mListView, null);
}
assertEquals("the view should be detached", 1, theView.mOnDetachCount);
assertFalse(theView.isTemporarilyDetached());
while(theView.getParent() == null) {
sendKeys(KeyEvent.KEYCODE_DPAD_UP);
- ViewTestUtils.runOnMainAndDrawSync(getInstrumentation(), mListView, null);
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mListView, null);
}
assertEquals("the view should be re-attached", 2, theView.mOnAttachCount);
assertEquals("the view should not recieve another detach", 1, theView.mOnDetachCount);
@@ -762,19 +793,19 @@
ViewGroup.LayoutParams.WRAP_CONTENT));
view.setMinimumHeight(30);
final DummyAdapter adapter = new DummyAdapter(2, view);
- ViewTestUtils.runOnMainAndDrawSync(getInstrumentation(), mListView, () -> {
- mListView.setLayoutParams(new LinearLayout.LayoutParams(200, 100));
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mListView, () -> {
+ mListView.setLayoutParams(new FrameLayout.LayoutParams(200, 100));
mListView.setAdapter(adapter);
});
assertEquals("test sanity", 200, mListView.getWidth());
assertEquals(200, view.getWidth());
- ViewTestUtils.runOnMainAndDrawSync(getInstrumentation(), mListView, () -> {
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mListView, () -> {
mListView.setPadding(10, 0, 5, 0);
assertTrue(view.isLayoutRequested());
});
assertEquals(185, view.getWidth());
assertFalse(view.isLayoutRequested());
- ViewTestUtils.runOnMainAndDrawSync(getInstrumentation(), mListView, () -> {
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mListView, () -> {
mListView.setPadding(10, 0, 5, 0);
assertFalse(view.isLayoutRequested());
});
@@ -782,17 +813,17 @@
@MediumTest
public void testResolveRtlOnReAttach() {
- View spacer = new View(getActivity());
+ View spacer = new View(mActivity);
spacer.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
250));
final DummyAdapter adapter = new DummyAdapter(50, spacer);
- ViewTestUtils.runOnMainAndDrawSync(getInstrumentation(), mListView, () -> {
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mListView, () -> {
mListView.setLayoutDirection(View.LAYOUT_DIRECTION_RTL);
- mListView.setLayoutParams(new LinearLayout.LayoutParams(200, 150));
+ mListView.setLayoutParams(new FrameLayout.LayoutParams(200, 150));
mListView.setAdapter(adapter);
});
assertEquals("test sanity", 1, mListView.getChildCount());
- ViewTestUtils.runOnMainAndDrawSync(getInstrumentation(), mListView, () -> {
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mListView, () -> {
// we scroll in pieces because list view caps scroll by its height
mListView.scrollListBy(100);
mListView.scrollListBy(100);
@@ -800,14 +831,14 @@
});
assertEquals("test sanity", 1, mListView.getChildCount());
assertEquals("test sanity", 1, mListView.getFirstVisiblePosition());
- ViewTestUtils.runOnMainAndDrawSync(getInstrumentation(), mListView, () -> {
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mListView, () -> {
mListView.scrollListBy(-100);
mListView.scrollListBy(-100);
mListView.scrollListBy(-60);
});
assertEquals("test sanity", 1, mListView.getChildCount());
assertEquals("item 0 should be visible", 0, mListView.getFirstVisiblePosition());
- ViewTestUtils.runOnMainAndDrawSync(getInstrumentation(), mListView, () -> {
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mListView, () -> {
mListView.scrollListBy(100);
mListView.scrollListBy(100);
mListView.scrollListBy(60);
@@ -874,11 +905,12 @@
assertNotNull(childView2);
// Make sure that the childView1 has focus.
- ViewTestUtils.runOnMainAndDrawSync(mInstrumentation, childView1, childView1::requestFocus);
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, childView1,
+ childView1::requestFocus);
assertTrue(childView1.isFocused());
// Make sure that ListView#requestLayout() is optimized when nothing is changed.
- ViewTestUtils.runOnMainAndDrawSync(mInstrumentation, listView, listView::requestLayout);
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, listView, listView::requestLayout);
assertEquals(childView0, listView.getChildAt(0));
assertEquals(childView1, listView.getChildAt(1));
assertEquals(childView2, listView.getChildAt(2));
@@ -1048,7 +1080,7 @@
final ArrayAdapter<String> adapter = new ArrayAdapter<String>(mActivity,
android.R.layout.simple_list_item_1, items);
- ViewTestUtils.runOnMainAndDrawSync(mInstrumentation, listView,
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, listView,
() -> listView.setAdapter(adapter));
final View oldItem = listView.getChildAt(2);
@@ -1056,7 +1088,7 @@
.getText();
oldItem.setHasTransientState(true);
- ViewTestUtils.runOnMainAndDrawSync(mInstrumentation, listView,
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, listView,
() -> {
adapter.remove(adapter.getItem(0));
adapter.notifyDataSetChanged();
@@ -1070,28 +1102,27 @@
}
public void testTransientStateStableIds() throws Exception {
- final ListView listView = mListView;
- final ArrayList<String> items = new ArrayList<String>(Arrays.asList(mCountryList));
- final StableArrayAdapter<String> adapter = new StableArrayAdapter<String>(mActivity,
+ final ArrayList<String> items = new ArrayList<>(Arrays.asList(mCountryList));
+ final StableArrayAdapter<String> adapter = new StableArrayAdapter<>(mActivity,
android.R.layout.simple_list_item_1, items);
- ViewTestUtils.runOnMainAndDrawSync(mInstrumentation, mListView,
- () -> listView.setAdapter(adapter));
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mListView,
+ () -> mListView.setAdapter(adapter));
final Object tag = new Object();
- final View oldItem = listView.getChildAt(2);
+ final View oldItem = mListView.getChildAt(2);
final CharSequence oldText = ((TextView) oldItem.findViewById(android.R.id.text1))
.getText();
oldItem.setHasTransientState(true);
oldItem.setTag(tag);
- ViewTestUtils.runOnMainAndDrawSync(mInstrumentation, listView,
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mListView,
() -> {
adapter.remove(adapter.getItem(0));
adapter.notifyDataSetChanged();
});
- final View newItem = listView.getChildAt(1);
+ final View newItem = mListView.getChildAt(1);
final CharSequence newText = ((TextView) newItem.findViewById(android.R.id.text1))
.getText();
@@ -1115,4 +1146,132 @@
return true;
}
}
+
+ @LargeTest
+ public void testSmoothScrollByOffset() {
+ final int itemCount = mLongCountryList.length;
+
+ mActivity.runOnUiThread(() -> mListView.setAdapter(mAdapter_longCountries));
+ mInstrumentation.waitForIdleSync();
+
+ assertEquals(0, mListView.getFirstVisiblePosition());
+
+ // If we're on a really big display, we might be in a situation where the position
+ // we're going to scroll to is already visible. In that case the logic in the rest
+ // of this test will never fire off a listener callback and then fail the test.
+ final int positionToScrollTo = itemCount - 10;
+ final int lastVisiblePosition = mListView.getLastVisiblePosition();
+ if (positionToScrollTo <= lastVisiblePosition) {
+ return;
+ }
+
+ // Register a scroll listener on our ListView. The listener will notify our latch
+ // when the "target" item comes into view. If that never happens, the latch will
+ // time out and fail the test.
+ final CountDownLatch latch = new CountDownLatch(1);
+ mListView.setOnScrollListener(new AbsListView.OnScrollListener() {
+ @Override
+ public void onScrollStateChanged(AbsListView view, int scrollState) {
+ }
+
+ @Override
+ public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
+ int totalItemCount) {
+ if ((positionToScrollTo >= firstVisibleItem) &&
+ (positionToScrollTo <= (firstVisibleItem + visibleItemCount))) {
+ latch.countDown();
+ }
+ }
+ });
+ int offset = positionToScrollTo - lastVisiblePosition;
+ mActivity.runOnUiThread(() -> mListView.smoothScrollByOffset(offset));
+
+ boolean result = false;
+ try {
+ result = latch.await(20, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ // ignore
+ }
+ assertTrue("Timed out while waiting for the target view to be scrolled into view", result);
+ }
+
+ private static class PositionArrayAdapter<T> extends ArrayAdapter<T> {
+ public PositionArrayAdapter(Context context, int resource, List<T> objects) {
+ super(context, resource, objects);
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+
+ @Override
+ public boolean hasStableIds() {
+ return true;
+ }
+ }
+
+ public void testGetCheckItemIds() {
+ final ArrayList<String> items = new ArrayList<>(Arrays.asList(mCountryList));
+ final ArrayAdapter<String> adapter = new PositionArrayAdapter<>(mActivity,
+ android.R.layout.simple_list_item_1, items);
+
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mListView,
+ () -> mListView.setAdapter(adapter));
+
+ mInstrumentation.runOnMainSync(
+ () -> mListView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE));
+ assertTrue(mListView.getCheckItemIds().length == 0);
+
+ mInstrumentation.runOnMainSync(() -> mListView.setItemChecked(2, true));
+ TestUtils.assertIdentical(new long[] { 2 }, mListView.getCheckItemIds());
+
+ mInstrumentation.runOnMainSync(() -> mListView.setItemChecked(4, true));
+ TestUtils.assertIdentical(new long[] { 2, 4 }, mListView.getCheckItemIds());
+
+ mInstrumentation.runOnMainSync(() -> mListView.setItemChecked(2, false));
+ TestUtils.assertIdentical(new long[] { 4 }, mListView.getCheckItemIds());
+
+ mInstrumentation.runOnMainSync(() -> mListView.setItemChecked(4, false));
+ assertTrue(mListView.getCheckItemIds().length == 0);
+ }
+
+ public void testAccessOverscrollHeader() {
+ final Drawable overscrollHeaderDrawable = spy(new ColorDrawable(Color.YELLOW));
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mListView, () -> {
+ mListView.setAdapter(mAdapter_longCountries);
+ mListView.setOverscrollHeader(overscrollHeaderDrawable);
+ });
+
+ assertEquals(overscrollHeaderDrawable, mListView.getOverscrollHeader());
+ verify(overscrollHeaderDrawable, never()).draw(any(Canvas.class));
+
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mListView,
+ () -> mListView.setScrollY(-mListView.getHeight() / 2));
+
+ verify(overscrollHeaderDrawable, atLeastOnce()).draw(any(Canvas.class));
+ }
+
+ public void testAccessOverscrollFooter() {
+ final Drawable overscrollFooterDrawable = spy(new ColorDrawable(Color.MAGENTA));
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mListView, () -> {
+ // Configure ListView to automatically scroll to the selected item
+ mListView.setStackFromBottom(true);
+ mListView.setTranscriptMode(AbsListView.TRANSCRIPT_MODE_ALWAYS_SCROLL);
+
+ mListView.setAdapter(mAdapter_longCountries);
+ mListView.setOverscrollFooter(overscrollFooterDrawable);
+
+ // Set selection to the last item
+ mListView.setSelection(mAdapter_longCountries.getCount() - 1);
+ });
+
+ assertEquals(overscrollFooterDrawable, mListView.getOverscrollFooter());
+ verify(overscrollFooterDrawable, never()).draw(any(Canvas.class));
+
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mListView,
+ () -> mListView.setScrollY(mListView.getHeight() / 2));
+
+ verify(overscrollFooterDrawable, atLeastOnce()).draw(any(Canvas.class));
+ }
}
diff --git a/tests/tests/widget/src/android/widget/cts/MediaControllerCtsActivity.java b/tests/tests/widget/src/android/widget/cts/MediaControllerCtsActivity.java
index ac8f100..ffbc3af 100644
--- a/tests/tests/widget/src/android/widget/cts/MediaControllerCtsActivity.java
+++ b/tests/tests/widget/src/android/widget/cts/MediaControllerCtsActivity.java
@@ -19,7 +19,6 @@
import android.app.Activity;
import android.os.Bundle;
import android.widget.MediaController;
-
import android.widget.cts.R;
/**
diff --git a/tests/tests/widget/src/android/widget/cts/MediaControllerTest.java b/tests/tests/widget/src/android/widget/cts/MediaControllerTest.java
index 2c60a91..705f990 100644
--- a/tests/tests/widget/src/android/widget/cts/MediaControllerTest.java
+++ b/tests/tests/widget/src/android/widget/cts/MediaControllerTest.java
@@ -16,10 +16,7 @@
package android.widget.cts;
-import android.widget.cts.R;
-
-
-import org.xmlpull.v1.XmlPullParser;
+import static org.mockito.Mockito.*;
import android.app.Activity;
import android.app.Instrumentation;
@@ -31,10 +28,11 @@
import android.util.Xml;
import android.view.MotionEvent;
import android.view.View;
-import android.view.View.OnClickListener;
import android.widget.MediaController;
import android.widget.VideoView;
+import org.xmlpull.v1.XmlPullParser;
+
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -47,7 +45,6 @@
private MediaController mMediaController;
private Activity mActivity;
private Instrumentation mInstrumentation;
- private static final long DEFAULT_TIMEOUT = 3000;
public MediaControllerTest() {
super("android.widget.cts", MediaControllerCtsActivity.class);
@@ -151,12 +148,7 @@
assertTrue(mMediaController.isShowing());
// isShowing() should return false, but MediaController still shows, this may be a bug.
- new PollingCheck(timeout + 500) {
- @Override
- protected boolean check() {
- return mMediaController.isShowing();
- }
- }.run();
+ PollingCheck.waitFor(500, mMediaController::isShowing);
}
private String prepareSampleVideo() {
@@ -236,9 +228,9 @@
mMediaController.setAnchorView(videoView);
mMediaController.setMediaPlayer(mediaPlayerControl);
- final MockOnClickListener next = new MockOnClickListener();
- final MockOnClickListener prev = new MockOnClickListener();
- mMediaController.setPrevNextListeners(next, prev);
+ final View.OnClickListener mockNextClickListener = mock(View.OnClickListener.class);
+ final View.OnClickListener mockPrevClickListener = mock(View.OnClickListener.class);
+ mMediaController.setPrevNextListeners(mockNextClickListener, mockPrevClickListener);
mMediaController.show();
@@ -258,9 +250,9 @@
mMediaController.setAnchorView(videoView);
mMediaController.setMediaPlayer(mediaPlayerControl);
- final MockOnClickListener next = new MockOnClickListener();
- final MockOnClickListener prev = new MockOnClickListener();
- mMediaController.setPrevNextListeners(next, prev);
+ final View.OnClickListener mockNextClickListener = mock(View.OnClickListener.class);
+ final View.OnClickListener mockPrevClickListener = mock(View.OnClickListener.class);
+ mMediaController.setPrevNextListeners(mockNextClickListener, mockPrevClickListener);
}
private static class MockMediaPlayerControl implements MediaController.MediaPlayerControl {
@@ -318,16 +310,4 @@
return 0;
}
}
-
- private static class MockOnClickListener implements OnClickListener {
- private boolean mOnClickCalled = false;
-
- public boolean hasOnClickCalled() {
- return mOnClickCalled;
- }
-
- public void onClick(View v) {
- mOnClickCalled = true;
- }
- }
}
diff --git a/tests/tests/widget/src/android/widget/cts/MockTextView.java b/tests/tests/widget/src/android/widget/cts/MockTextView.java
index 977e4b2..ffdcfde 100644
--- a/tests/tests/widget/src/android/widget/cts/MockTextView.java
+++ b/tests/tests/widget/src/android/widget/cts/MockTextView.java
@@ -16,208 +16,103 @@
package android.widget.cts;
+import android.annotation.Nullable;
import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Rect;
import android.graphics.drawable.Drawable;
-import android.os.Bundle;
import android.text.method.MovementMethod;
import android.util.AttributeSet;
-import android.view.ContextMenu;
-import android.view.KeyEvent;
import android.widget.TextView;
public class MockTextView extends TextView {
- private boolean mHasCalledOnCreateContextMenu;
- private boolean mHasCalledOnFocusChanged;
- private boolean mHasCalledOnMeasure;
- private boolean mHasCalledOnTextChanged;
- private boolean mHasCalledDrawableStateChanged;
- private boolean mHasCalledOnWindowFocusChanged;
- private boolean mHasCalledOnPrivateIMECommand;
- private boolean mHasCalledOnKeyMultiple;
public MockTextView(Context context) {
super(context);
}
- public MockTextView(Context context, AttributeSet attrs) {
+ public MockTextView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
- public MockTextView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
+ public MockTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
}
- public boolean hasCalledOnWindowFocusChanged() {
- return mHasCalledOnWindowFocusChanged;
- }
-
- public boolean hasCalledOnCreateContextMenu() {
- return mHasCalledOnCreateContextMenu;
- }
-
- public boolean hasCalledDrawableStateChanged() {
- return mHasCalledDrawableStateChanged;
- }
-
- public boolean hasCalledOnFocusChanged() {
- return mHasCalledOnFocusChanged;
- }
-
- public boolean hasCalledOnMeasure() {
- return mHasCalledOnMeasure;
- }
-
- public boolean hasCalledOnTextChanged() {
- return mHasCalledOnTextChanged;
- }
-
- public boolean hasCalledOnPrivateIMECommand() {
- return mHasCalledOnPrivateIMECommand;
- }
-
- public boolean hasCalledOnKeyMultiple(){
- return mHasCalledOnKeyMultiple;
- }
-
- public void reset() {
- mHasCalledOnWindowFocusChanged = false;
- mHasCalledDrawableStateChanged = false;
- mHasCalledOnCreateContextMenu = false;
- mHasCalledOnFocusChanged = false;
- mHasCalledOnMeasure = false;
- mHasCalledOnTextChanged = false;
- mHasCalledOnPrivateIMECommand = false;
- mHasCalledOnKeyMultiple = false;
- }
-
+ @Override
public int computeHorizontalScrollRange() {
return super.computeHorizontalScrollRange();
}
+ @Override
public int computeVerticalScrollRange() {
return super.computeVerticalScrollRange();
}
@Override
- protected void drawableStateChanged() {
- super.drawableStateChanged();
- mHasCalledDrawableStateChanged = true;
- }
-
public boolean getDefaultEditable() {
return super.getDefaultEditable();
}
+ @Override
public MovementMethod getDefaultMovementMethod() {
return super.getDefaultMovementMethod();
}
@Override
- protected void onCreateContextMenu(ContextMenu menu) {
- super.onCreateContextMenu(menu);
- mHasCalledOnCreateContextMenu = true;
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
- }
-
- @Override
- protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
- super.onFocusChanged(focused, direction, previouslyFocusedRect);
- mHasCalledOnFocusChanged = true;
- }
-
- @Override
- public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) {
- mHasCalledOnKeyMultiple = true;
- return super.onKeyMultiple(keyCode, repeatCount, event);
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- mHasCalledOnMeasure = true;
- }
-
- @Override
- protected void onTextChanged(CharSequence text, int start, int before, int after) {
- super.onTextChanged(text, start, before, after);
- mHasCalledOnTextChanged = true;
- }
-
public boolean setFrame(int l, int t, int r, int b) {
return super.setFrame(l, t, r, b);
}
@Override
- public void onWindowFocusChanged(boolean hasWindowFocus) {
- super.onWindowFocusChanged(hasWindowFocus);
- mHasCalledOnWindowFocusChanged = true;
- }
-
public float getLeftFadingEdgeStrength() {
return super.getLeftFadingEdgeStrength();
}
+ @Override
public float getRightFadingEdgeStrength() {
return super.getRightFadingEdgeStrength();
}
@Override
- public boolean onPrivateIMECommand(String action, Bundle data) {
- mHasCalledOnPrivateIMECommand = true;
- return super.onPrivateIMECommand(action, data);
- }
-
- public int getFrameLeft() {
- return getLeft();
- }
-
- public int getFrameTop() {
- return getTop();
- }
-
- public int getFrameRight() {
- return getRight();
- }
-
- public int getFrameBottom() {
- return getBottom();
- }
-
public int getBottomPaddingOffset() {
return super.getBottomPaddingOffset();
}
+ @Override
public int getLeftPaddingOffset() {
return super.getLeftPaddingOffset();
}
+ @Override
public int getRightPaddingOffset() {
return super.getRightPaddingOffset();
}
+ @Override
public int getTopPaddingOffset() {
return super.getTopPaddingOffset();
}
+ @Override
public boolean isPaddingOffsetRequired() {
return super.isPaddingOffsetRequired();
}
+ @Override
+ public void onSelectionChanged(int selStart, int selEnd) {
+ super.onSelectionChanged(selStart, selEnd);
+ }
+
+ @Override
+ public void drawableStateChanged() {
+ super.drawableStateChanged();
+ }
+
+ @Override
public boolean verifyDrawable(Drawable who) {
return super.verifyDrawable(who);
}
+ @Override
public int computeVerticalScrollExtent() {
return super.computeVerticalScrollExtent();
}
diff --git a/tests/tests/widget/src/android/widget/cts/MockURLSpanTestActivity.java b/tests/tests/widget/src/android/widget/cts/MockURLSpanTestActivity.java
index 3d27f9a..131d2fc 100644
--- a/tests/tests/widget/src/android/widget/cts/MockURLSpanTestActivity.java
+++ b/tests/tests/widget/src/android/widget/cts/MockURLSpanTestActivity.java
@@ -16,8 +16,6 @@
package android.widget.cts;
-import android.widget.cts.R;
-
import android.app.Activity;
import android.os.Bundle;
@@ -25,9 +23,19 @@
* A Mock application for {@link URLSpan} test.
*/
public class MockURLSpanTestActivity extends Activity {
+ public static final String KEY_PARAM = "MockURLSpanTestActivity.param";
+
+ private String mParam;
+
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+
+ mParam = getIntent().getStringExtra(KEY_PARAM);
setContentView(R.layout.urlspan_layout);
}
+
+ public String getParam() {
+ return mParam;
+ }
}
diff --git a/tests/tests/widget/src/android/widget/cts/MultiAutoCompleteTextViewCtsActivity.java b/tests/tests/widget/src/android/widget/cts/MultiAutoCompleteTextViewCtsActivity.java
index b5de67b..c36ba77 100644
--- a/tests/tests/widget/src/android/widget/cts/MultiAutoCompleteTextViewCtsActivity.java
+++ b/tests/tests/widget/src/android/widget/cts/MultiAutoCompleteTextViewCtsActivity.java
@@ -16,10 +16,9 @@
package android.widget.cts;
-import android.widget.cts.R;
-
import android.app.Activity;
import android.os.Bundle;
+import android.widget.cts.R;
/**
* A minimal application for MultiAutoCompleteTextView test.
diff --git a/tests/tests/widget/src/android/widget/cts/MultiAutoCompleteTextViewTest.java b/tests/tests/widget/src/android/widget/cts/MultiAutoCompleteTextViewTest.java
index 5c18030..fddc9a5 100644
--- a/tests/tests/widget/src/android/widget/cts/MultiAutoCompleteTextViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/MultiAutoCompleteTextViewTest.java
@@ -16,32 +16,35 @@
package android.widget.cts;
-import android.widget.cts.R;
-
-
-import org.xmlpull.v1.XmlPullParser;
+import static org.mockito.Matchers.*;
+import static org.mockito.Mockito.*;
import android.app.Activity;
+import android.app.Instrumentation;
import android.content.Context;
import android.test.ActivityInstrumentationTestCase2;
-import android.test.UiThreadTest;
+import android.test.suitebuilder.annotation.MediumTest;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.TextUtils;
-import android.util.AttributeSet;
-import android.util.Xml;
import android.view.KeyEvent;
import android.widget.ArrayAdapter;
+import android.widget.AutoCompleteTextView;
import android.widget.Filter;
import android.widget.MultiAutoCompleteTextView;
import android.widget.MultiAutoCompleteTextView.CommaTokenizer;
import android.widget.MultiAutoCompleteTextView.Tokenizer;
-public class MultiAutoCompleteTextViewTest extends ActivityInstrumentationTestCase2
- <MultiAutoCompleteTextViewCtsActivity> {
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+@MediumTest
+public class MultiAutoCompleteTextViewTest
+ extends ActivityInstrumentationTestCase2<MultiAutoCompleteTextViewCtsActivity> {
+ private Instrumentation mInstrumentation;
+ private Activity mActivity;
private MultiAutoCompleteTextView mMultiAutoCompleteTextView_country;
private MultiAutoCompleteTextView mMultiAutoCompleteTextView_name;
- private Activity mActivity;
public MultiAutoCompleteTextViewTest() {
super("android.widget.cts", MultiAutoCompleteTextViewCtsActivity.class);
@@ -51,23 +54,26 @@
protected void setUp() throws Exception {
super.setUp();
+ mInstrumentation = getInstrumentation();
mActivity = getActivity();
-
mMultiAutoCompleteTextView_country = (MultiAutoCompleteTextView)mActivity
.findViewById(R.id.country_edit);
mMultiAutoCompleteTextView_name = (MultiAutoCompleteTextView)mActivity
.findViewById(R.id.name_edit);
}
- @UiThreadTest
public void testConstructor() {
- XmlPullParser parser = mActivity.getResources()
- .getXml(R.layout.multi_auto_complete_text_view_layout);
- AttributeSet attr = Xml.asAttributeSet(parser);
-
new MultiAutoCompleteTextView(mActivity);
- new MultiAutoCompleteTextView(mActivity, attr);
- new MultiAutoCompleteTextView(mActivity, attr, 0);
+ new MultiAutoCompleteTextView(mActivity, null);
+ new MultiAutoCompleteTextView(mActivity, null, android.R.attr.autoCompleteTextViewStyle);
+ new MultiAutoCompleteTextView(mActivity, null, 0,
+ android.R.style.Widget_DeviceDefault_AutoCompleteTextView);
+ new MultiAutoCompleteTextView(mActivity, null, 0,
+ android.R.style.Widget_DeviceDefault_Light_AutoCompleteTextView);
+ new MultiAutoCompleteTextView(mActivity, null, 0,
+ android.R.style.Widget_Material_AutoCompleteTextView);
+ new MultiAutoCompleteTextView(mActivity, null, 0,
+ android.R.style.Widget_Material_Light_AutoCompleteTextView);
try {
new MultiAutoCompleteTextView(null);
@@ -91,19 +97,21 @@
}
}
- @UiThreadTest
- private void setText(MultiAutoCompleteTextView m, CharSequence c) {
- m.setText(c);
- m.setSelection(0, c.length());
+ private void setText(final MultiAutoCompleteTextView m, final CharSequence c) {
+ mInstrumentation.runOnMainSync(() -> {
+ m.setText(c);
+ m.setSelection(0, c.length());
+ });
}
- @UiThreadTest
public void testMultiAutoCompleteTextView() {
- mMultiAutoCompleteTextView_country.setTokenizer(new CommaTokenizer());
- mMultiAutoCompleteTextView_name.setTokenizer(new CommaTokenizer());
+ mInstrumentation.runOnMainSync(() -> {
+ mMultiAutoCompleteTextView_country.setTokenizer(new CommaTokenizer());
+ mMultiAutoCompleteTextView_name.setTokenizer(new CommaTokenizer());
- mMultiAutoCompleteTextView_country.setThreshold(3);
- mMultiAutoCompleteTextView_name.setThreshold(2);
+ mMultiAutoCompleteTextView_country.setThreshold(3);
+ mMultiAutoCompleteTextView_name.setThreshold(2);
+ });
assertFalse(mMultiAutoCompleteTextView_country.enoughToFilter());
assertFalse(mMultiAutoCompleteTextView_name.enoughToFilter());
@@ -125,76 +133,84 @@
setText(mMultiAutoCompleteTextView_name, "Jacky");
assertTrue(mMultiAutoCompleteTextView_name.enoughToFilter());
-
- MockValidator v = new MockValidator();
- v.setValid(true);
- mMultiAutoCompleteTextView_name.setValidator(v);
-
- // There will be an endless loop when using CommaTokenizer as the Tokenizer
- // mMultiAutoCompleteTextView_name.performValidation();
- mMultiAutoCompleteTextView_name.setValidator(null);
}
- @UiThreadTest
public void testPerformValidation() {
- MockValidator v = new MockValidator();
- v.setValid(true);
- mMultiAutoCompleteTextView_country.setValidator(v);
+ final AutoCompleteTextView.Validator validator = mock(AutoCompleteTextView.Validator.class);
+ when(validator.isValid(any(CharSequence.class))).thenReturn(true);
+ when(validator.fixText(any(CharSequence.class))).thenAnswer(
+ new Answer<CharSequence>() {
+ @Override
+ public CharSequence answer(InvocationOnMock invocation) throws Throwable {
+ // Return the originally passed parameter
+ return (CharSequence) invocation.getArguments()[0];
+ }
+ });
+
+ mInstrumentation.runOnMainSync(
+ () -> mMultiAutoCompleteTextView_country.setValidator(validator));
MockTokenizer t = new MockTokenizer();
- mMultiAutoCompleteTextView_country.setTokenizer(t);
+ mInstrumentation.runOnMainSync(() -> mMultiAutoCompleteTextView_country.setTokenizer(t));
String str = new String("Foo, Android Test, OH");
- mMultiAutoCompleteTextView_country.setText(str);
- mMultiAutoCompleteTextView_country.performValidation();
+ mInstrumentation.runOnMainSync(() -> {
+ mMultiAutoCompleteTextView_country.setText(str);
+ mMultiAutoCompleteTextView_country.performValidation();
+ });
assertEquals(str, mMultiAutoCompleteTextView_country.getText().toString());
- v.setValid(false);
- mMultiAutoCompleteTextView_country.performValidation();
+ when(validator.isValid(any(CharSequence.class))).thenReturn(false);
+ mInstrumentation.runOnMainSync(
+ mMultiAutoCompleteTextView_country::performValidation);
assertEquals(str + ", ", mMultiAutoCompleteTextView_country.getText().toString());
}
- @UiThreadTest
public void testPerformFiltering() {
MyMultiAutoCompleteTextView multiAutoCompleteTextView =
new MyMultiAutoCompleteTextView(mActivity);
CommaTokenizer t = new CommaTokenizer();
- multiAutoCompleteTextView.setTokenizer(t);
+ mInstrumentation.runOnMainSync(() -> multiAutoCompleteTextView.setTokenizer(t));
ArrayAdapter<String> adapter = new ArrayAdapter<String>(mActivity,
R.layout.simple_dropdown_item_1line);
assertNotNull(adapter);
- multiAutoCompleteTextView.setAdapter(adapter);
+ mInstrumentation.runOnMainSync(() -> multiAutoCompleteTextView.setAdapter(adapter));
assertNotNull(multiAutoCompleteTextView.getFilter());
String text = "Android test.";
- multiAutoCompleteTextView.setText(text);
- multiAutoCompleteTextView.setSelection(0, 12);
+ mInstrumentation.runOnMainSync(() -> {
+ multiAutoCompleteTextView.setText(text);
+ multiAutoCompleteTextView.setSelection(0, 12);
+ multiAutoCompleteTextView.performFiltering(text, KeyEvent.KEYCODE_0);
+ });
- multiAutoCompleteTextView.performFiltering(text, KeyEvent.KEYCODE_0);
assertNotNull(multiAutoCompleteTextView.getFilter());
- multiAutoCompleteTextView.performFiltering(text, 0, text.length(), KeyEvent.KEYCODE_E);
+ mInstrumentation.runOnMainSync(
+ () -> multiAutoCompleteTextView.performFiltering(text, 0, text.length(),
+ KeyEvent.KEYCODE_E));
assertNotNull(multiAutoCompleteTextView.getFilter());
}
- @UiThreadTest
public void testReplaceText() {
MyMultiAutoCompleteTextView multiAutoCompleteTextView =
new MyMultiAutoCompleteTextView(mActivity);
CommaTokenizer t = new CommaTokenizer();
- multiAutoCompleteTextView.setTokenizer(t);
+ mInstrumentation.runOnMainSync(() -> multiAutoCompleteTextView.setTokenizer(t));
String text = "CTS.";
- multiAutoCompleteTextView.setText(text);
+ mInstrumentation.runOnMainSync(() -> multiAutoCompleteTextView.setText(text));
assertEquals(text, multiAutoCompleteTextView.getText().toString());
- multiAutoCompleteTextView.setSelection(0, text.length());
+ mInstrumentation.runOnMainSync(
+ () -> multiAutoCompleteTextView.setSelection(0, text.length()));
// set the selection range.
- multiAutoCompleteTextView.replaceText("Android Test.");
+ mInstrumentation.runOnMainSync(
+ () -> multiAutoCompleteTextView.replaceText("Android Test."));
assertEquals("Android Test., ", multiAutoCompleteTextView.getText().toString());
// do not set the selection range.
- multiAutoCompleteTextView.replaceText("replace test");
+ mInstrumentation.runOnMainSync(() -> multiAutoCompleteTextView.replaceText("replace test"));
assertEquals("Android Test., replace test, ",
multiAutoCompleteTextView.getText().toString());
}
@@ -253,22 +269,6 @@
}
}
- private class MockValidator implements MultiAutoCompleteTextView.Validator {
- private boolean mIsValid;
-
- public void setValid(boolean b) {
- mIsValid = b;
- }
-
- public boolean isValid(CharSequence text) {
- return mIsValid;
- }
-
- public CharSequence fixText(CharSequence invalidText) {
- return invalidText;
- }
- }
-
/**
* MyMultiAutoCompleteTextView
*/
diff --git a/tests/tests/widget/src/android/widget/cts/MyGallery.java b/tests/tests/widget/src/android/widget/cts/MyGallery.java
index 27b5d45..fc43437 100644
--- a/tests/tests/widget/src/android/widget/cts/MyGallery.java
+++ b/tests/tests/widget/src/android/widget/cts/MyGallery.java
@@ -19,10 +19,9 @@
import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
+import android.view.ContextMenu.ContextMenuInfo;
import android.view.View;
import android.view.ViewGroup;
-import android.view.ViewParent;
-import android.view.ContextMenu.ContextMenuInfo;
import android.view.animation.Transformation;
import android.widget.Gallery;
diff --git a/tests/tests/widget/src/android/widget/cts/MyRemotableView.java b/tests/tests/widget/src/android/widget/cts/MyRemotableView.java
new file mode 100644
index 0000000..725ffe7
--- /dev/null
+++ b/tests/tests/widget/src/android/widget/cts/MyRemotableView.java
@@ -0,0 +1,100 @@
+/*
+ * 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.widget.cts;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.AttributeSet;
+import android.widget.RemoteViews;
+import android.widget.TextView;
+
+@RemoteViews.RemoteView
+public class MyRemotableView extends TextView {
+ private byte mByteField;
+ private char mCharField;
+ private double mDoubleField;
+ private short mShortField;
+ private Bundle mBundleField;
+ private Intent mIntentField;
+
+ public MyRemotableView(Context context) {
+ super(context);
+ }
+
+ public MyRemotableView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public MyRemotableView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ @android.view.RemotableViewMethod
+ public void setByteField(byte value) {
+ mByteField = value;
+ }
+
+ public byte getByteField() {
+ return mByteField;
+ }
+
+ @android.view.RemotableViewMethod
+ public void setCharField(char value) {
+ mCharField = value;
+ }
+
+ public char getCharField() {
+ return mCharField;
+ }
+
+ @android.view.RemotableViewMethod
+ public void setDoubleField(double value) {
+ mDoubleField = value;
+ }
+
+ public double getDoubleField() {
+ return mDoubleField;
+ }
+
+ @android.view.RemotableViewMethod
+ public void setShortField(short value) {
+ mShortField = value;
+ }
+
+ public short getShortField() {
+ return mShortField;
+ }
+
+ @android.view.RemotableViewMethod
+ public void setBundleField(Bundle value) {
+ mBundleField = value;
+ }
+
+ public Bundle getBundleField() {
+ return mBundleField;
+ }
+
+ @android.view.RemotableViewMethod
+ public void setIntentField(Intent value) {
+ mIntentField = value;
+ }
+
+ public Intent getIntentField() {
+ return mIntentField;
+ }
+}
diff --git a/hostsidetests/services/activitymanager/app/src/android/server/app/SlowCreateActivity.java b/tests/tests/widget/src/android/widget/cts/NumberPickerCtsActivity.java
similarity index 67%
copy from hostsidetests/services/activitymanager/app/src/android/server/app/SlowCreateActivity.java
copy to tests/tests/widget/src/android/widget/cts/NumberPickerCtsActivity.java
index 481f5be..f93a1da 100644
--- a/hostsidetests/services/activitymanager/app/src/android/server/app/SlowCreateActivity.java
+++ b/tests/tests/widget/src/android/widget/cts/NumberPickerCtsActivity.java
@@ -14,17 +14,22 @@
* limitations under the License.
*/
-package android.server.app;
+package android.widget.cts;
import android.app.Activity;
import android.os.Bundle;
+import android.widget.NumberPicker;
-public class SlowCreateActivity extends Activity {
+/**
+ * A minimal application for {@link NumberPicker} test.
+ */
+public class NumberPickerCtsActivity extends Activity {
+ /**
+ * Called with the activity is first created.
+ */
@Override
- protected void onCreate(Bundle savedInstanceState) {
- try {
- Thread.sleep(2000);
- } catch(InterruptedException e) {}
+ public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ setContentView(R.layout.numberpicker_layout);
}
}
diff --git a/tests/tests/widget/src/android/widget/cts/NumberPickerTest.java b/tests/tests/widget/src/android/widget/cts/NumberPickerTest.java
new file mode 100644
index 0000000..d560f31
--- /dev/null
+++ b/tests/tests/widget/src/android/widget/cts/NumberPickerTest.java
@@ -0,0 +1,390 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ *
+ * 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.mockito.Mockito.*;
+
+import android.app.Instrumentation;
+import android.cts.util.CtsTouchUtils;
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.UiThreadTest;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.text.TextUtils;
+import android.widget.NumberPicker;
+
+import org.mockito.InOrder;
+
+@SmallTest
+public class NumberPickerTest extends ActivityInstrumentationTestCase2<NumberPickerCtsActivity> {
+ private static final String[] NUMBER_NAMES3 = {"One", "Two", "Three"};
+ private static final String[] NUMBER_NAMES_ALT3 = {"Three", "Four", "Five"};
+ private static final String[] NUMBER_NAMES5 = {"One", "Two", "Three", "Four", "Five"};
+
+ private Instrumentation mInstrumentation;
+ private NumberPickerCtsActivity mActivity;
+ private NumberPicker mNumberPicker;
+
+ public NumberPickerTest() {
+ super("android.widget.cts", NumberPickerCtsActivity.class);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mInstrumentation = getInstrumentation();
+ mActivity = getActivity();
+ mNumberPicker = (NumberPicker) mActivity.findViewById(R.id.number_picker);
+ }
+
+ @UiThreadTest
+ public void testConstructor() {
+ new NumberPicker(mActivity);
+
+ new NumberPicker(mActivity, null);
+
+ new NumberPicker(mActivity, null, android.R.attr.numberPickerStyle);
+
+ new NumberPicker(mActivity, null, 0, android.R.style.Widget_Material_NumberPicker);
+
+ new NumberPicker(mActivity, null, 0, android.R.style.Widget_Material_Light_NumberPicker);
+ }
+
+ private void verifyDisplayedValues(String[] expected) {
+ final String[] displayedValues = mNumberPicker.getDisplayedValues();
+ assertEquals(expected.length, displayedValues.length);
+ for (int i = 0; i < expected.length; i++) {
+ assertEquals(expected[i], displayedValues[i]);
+ }
+ }
+
+ public void testSetDisplayedValuesRangeMatch() {
+ mInstrumentation.runOnMainSync(() -> {
+ mNumberPicker.setMinValue(10);
+ mNumberPicker.setMaxValue(12);
+ mNumberPicker.setDisplayedValues(NUMBER_NAMES3);
+ });
+
+ assertEquals(10, mNumberPicker.getMinValue());
+ assertEquals(12, mNumberPicker.getMaxValue());
+ verifyDisplayedValues(NUMBER_NAMES3);
+
+ // Set a different displayed values array, but still matching the min/max range
+ mInstrumentation.runOnMainSync(() -> {
+ mNumberPicker.setDisplayedValues(NUMBER_NAMES_ALT3);
+ });
+
+ assertEquals(10, mNumberPicker.getMinValue());
+ assertEquals(12, mNumberPicker.getMaxValue());
+ verifyDisplayedValues(NUMBER_NAMES_ALT3);
+
+ mInstrumentation.runOnMainSync(() -> {
+ mNumberPicker.setMinValue(24);
+ mNumberPicker.setMaxValue(26);
+ });
+
+ assertEquals(24, mNumberPicker.getMinValue());
+ assertEquals(26, mNumberPicker.getMaxValue());
+ verifyDisplayedValues(NUMBER_NAMES_ALT3);
+ }
+
+ public void testSetDisplayedValuesRangeMismatch() {
+ mInstrumentation.runOnMainSync(() -> {
+ mNumberPicker.setMinValue(10);
+ mNumberPicker.setMaxValue(14);
+ });
+ assertEquals(10, mNumberPicker.getMinValue());
+ assertEquals(14, mNumberPicker.getMaxValue());
+
+ // Try setting too few displayed entries
+ mInstrumentation.runOnMainSync(() -> {
+ try {
+ // This is expected to fail since the displayed values only has three entries,
+ // while the min/max range has five.
+ mNumberPicker.setDisplayedValues(NUMBER_NAMES3);
+ fail("The size of the displayed values array must be equal to min/max range!");
+ } catch (Exception e) {
+ // We are expecting to catch an exception. Set displayed values to an array that
+ // matches the min/max range.
+ mNumberPicker.setDisplayedValues(NUMBER_NAMES5);
+ }
+ });
+ }
+
+ public void testSelectionDisplayedValueFromDisplayedValues() {
+ mInstrumentation.runOnMainSync(() -> {
+ mNumberPicker.setMinValue(1);
+ mNumberPicker.setMaxValue(3);
+ mNumberPicker.setDisplayedValues(NUMBER_NAMES3);
+ });
+
+ mInstrumentation.runOnMainSync(() -> mNumberPicker.setValue(1));
+ assertTrue(TextUtils.equals(NUMBER_NAMES3[0],
+ mNumberPicker.getDisplayedValueForCurrentSelection()));
+
+ mInstrumentation.runOnMainSync(() -> mNumberPicker.setValue(2));
+ assertTrue(TextUtils.equals(NUMBER_NAMES3[1],
+ mNumberPicker.getDisplayedValueForCurrentSelection()));
+
+ mInstrumentation.runOnMainSync(() -> mNumberPicker.setValue(3));
+ assertTrue(TextUtils.equals(NUMBER_NAMES3[2],
+ mNumberPicker.getDisplayedValueForCurrentSelection()));
+
+ // Switch to a different displayed values array
+ mInstrumentation.runOnMainSync(() -> {
+ mNumberPicker.setDisplayedValues(NUMBER_NAMES_ALT3);
+ });
+ assertTrue(TextUtils.equals(NUMBER_NAMES_ALT3[2],
+ mNumberPicker.getDisplayedValueForCurrentSelection()));
+
+ mInstrumentation.runOnMainSync(() -> mNumberPicker.setValue(1));
+ assertTrue(TextUtils.equals(NUMBER_NAMES_ALT3[0],
+ mNumberPicker.getDisplayedValueForCurrentSelection()));
+
+ mInstrumentation.runOnMainSync(() -> mNumberPicker.setValue(2));
+ assertTrue(TextUtils.equals(NUMBER_NAMES_ALT3[1],
+ mNumberPicker.getDisplayedValueForCurrentSelection()));
+ }
+
+ public void testSelectionDisplayedValueFromFormatter() {
+ mInstrumentation.runOnMainSync(() -> {
+ mNumberPicker.setMinValue(0);
+ mNumberPicker.setMaxValue(4);
+ mNumberPicker.setFormatter((int value) -> "entry " + value);
+ });
+
+ mInstrumentation.runOnMainSync(() -> mNumberPicker.setValue(0));
+ assertTrue(TextUtils.equals("entry 0",
+ mNumberPicker.getDisplayedValueForCurrentSelection()));
+
+ mInstrumentation.runOnMainSync(() -> mNumberPicker.setValue(1));
+ assertTrue(TextUtils.equals("entry 1",
+ mNumberPicker.getDisplayedValueForCurrentSelection()));
+
+ mInstrumentation.runOnMainSync(() -> mNumberPicker.setValue(2));
+ assertTrue(TextUtils.equals("entry 2",
+ mNumberPicker.getDisplayedValueForCurrentSelection()));
+
+ mInstrumentation.runOnMainSync(() -> mNumberPicker.setValue(3));
+ assertTrue(TextUtils.equals("entry 3",
+ mNumberPicker.getDisplayedValueForCurrentSelection()));
+
+ mInstrumentation.runOnMainSync(() -> mNumberPicker.setValue(4));
+ assertTrue(TextUtils.equals("entry 4",
+ mNumberPicker.getDisplayedValueForCurrentSelection()));
+
+ // Switch to a different formatter
+ mInstrumentation.runOnMainSync(
+ () -> mNumberPicker.setFormatter((int value) -> "row " + value));
+ // Check that the currently selected value has new displayed value
+ assertTrue(TextUtils.equals("row 4",
+ mNumberPicker.getDisplayedValueForCurrentSelection()));
+
+ // and check a couple more values for the new formatting
+ mInstrumentation.runOnMainSync(() -> mNumberPicker.setValue(0));
+ assertTrue(TextUtils.equals("row 0",
+ mNumberPicker.getDisplayedValueForCurrentSelection()));
+
+ mInstrumentation.runOnMainSync(() -> mNumberPicker.setValue(1));
+ assertTrue(TextUtils.equals("row 1",
+ mNumberPicker.getDisplayedValueForCurrentSelection()));
+ }
+
+
+ public void testSelectionDisplayedValuePrecedence() {
+ mInstrumentation.runOnMainSync(() -> {
+ mNumberPicker.setMinValue(1);
+ mNumberPicker.setMaxValue(3);
+ mNumberPicker.setDisplayedValues(NUMBER_NAMES3);
+ mNumberPicker.setFormatter((int value) -> "entry " + value);
+ });
+
+ // According to the widget documentation, displayed values take precedence over formatter
+ mInstrumentation.runOnMainSync(() -> mNumberPicker.setValue(1));
+ assertTrue(TextUtils.equals(NUMBER_NAMES3[0],
+ mNumberPicker.getDisplayedValueForCurrentSelection()));
+
+ mInstrumentation.runOnMainSync(() -> mNumberPicker.setValue(2));
+ assertTrue(TextUtils.equals(NUMBER_NAMES3[1],
+ mNumberPicker.getDisplayedValueForCurrentSelection()));
+
+ mInstrumentation.runOnMainSync(() -> mNumberPicker.setValue(3));
+ assertTrue(TextUtils.equals(NUMBER_NAMES3[2],
+ mNumberPicker.getDisplayedValueForCurrentSelection()));
+
+ // Set displayed values to null and test that the widget is using the formatter
+ mInstrumentation.runOnMainSync(() -> mNumberPicker.setDisplayedValues(null));
+ assertTrue(TextUtils.equals("entry 3",
+ mNumberPicker.getDisplayedValueForCurrentSelection()));
+
+ mInstrumentation.runOnMainSync(() -> mNumberPicker.setValue(1));
+ assertTrue(TextUtils.equals("entry 1",
+ mNumberPicker.getDisplayedValueForCurrentSelection()));
+
+ mInstrumentation.runOnMainSync(() -> mNumberPicker.setValue(2));
+ assertTrue(TextUtils.equals("entry 2",
+ mNumberPicker.getDisplayedValueForCurrentSelection()));
+
+ // Set a different displayed values array and test that it's taking precedence
+ mInstrumentation.runOnMainSync(() -> mNumberPicker.setDisplayedValues(NUMBER_NAMES_ALT3));
+ assertTrue(TextUtils.equals(NUMBER_NAMES_ALT3[1],
+ mNumberPicker.getDisplayedValueForCurrentSelection()));
+
+ mInstrumentation.runOnMainSync(() -> mNumberPicker.setValue(1));
+ assertTrue(TextUtils.equals(NUMBER_NAMES_ALT3[0],
+ mNumberPicker.getDisplayedValueForCurrentSelection()));
+
+ mInstrumentation.runOnMainSync(() -> mNumberPicker.setValue(3));
+ assertTrue(TextUtils.equals(NUMBER_NAMES_ALT3[2],
+ mNumberPicker.getDisplayedValueForCurrentSelection()));
+ }
+
+ public void testAccessValue() {
+ mInstrumentation.runOnMainSync(() -> {
+ mNumberPicker.setMinValue(20);
+ mNumberPicker.setMaxValue(22);
+ mNumberPicker.setDisplayedValues(NUMBER_NAMES3);
+ });
+
+ final NumberPicker.OnValueChangeListener mockValueChangeListener =
+ mock(NumberPicker.OnValueChangeListener.class);
+ mNumberPicker.setOnValueChangedListener(mockValueChangeListener);
+
+ mInstrumentation.runOnMainSync(() -> mNumberPicker.setValue(21));
+ assertEquals(21, mNumberPicker.getValue());
+
+ mInstrumentation.runOnMainSync(() -> mNumberPicker.setValue(20));
+ assertEquals(20, mNumberPicker.getValue());
+
+ mInstrumentation.runOnMainSync(() -> mNumberPicker.setValue(22));
+ assertEquals(22, mNumberPicker.getValue());
+
+ // Check trying to set value out of min/max range
+ mInstrumentation.runOnMainSync(() -> mNumberPicker.setValue(10));
+ assertEquals(20, mNumberPicker.getValue());
+
+ mInstrumentation.runOnMainSync(() -> mNumberPicker.setValue(100));
+ assertEquals(22, mNumberPicker.getValue());
+
+ // Since all changes to value are via API calls, we should have no interactions /
+ // callbacks on our listener.
+ verifyZeroInteractions(mockValueChangeListener);
+ }
+
+ public void testInteractionWithSwipeDown() {
+ mInstrumentation.runOnMainSync(() -> {
+ mNumberPicker.setMinValue(6);
+ mNumberPicker.setMaxValue(8);
+ mNumberPicker.setDisplayedValues(NUMBER_NAMES_ALT3);
+ });
+
+ final NumberPicker.OnValueChangeListener mockValueChangeListener =
+ mock(NumberPicker.OnValueChangeListener.class);
+ mNumberPicker.setOnValueChangedListener(mockValueChangeListener);
+
+ final NumberPicker.OnScrollListener mockScrollListener =
+ mock(NumberPicker.OnScrollListener.class);
+ mNumberPicker.setOnScrollListener(mockScrollListener);
+
+ mInstrumentation.runOnMainSync(() -> mNumberPicker.setValue(7));
+ assertEquals(7, mNumberPicker.getValue());
+
+ // Swipe down across our number picker
+ final int[] numberPickerLocationOnScreen = new int[2];
+ mNumberPicker.getLocationOnScreen(numberPickerLocationOnScreen);
+
+ CtsTouchUtils.emulateDragGesture(mInstrumentation,
+ numberPickerLocationOnScreen[0] + mNumberPicker.getWidth() / 2,
+ numberPickerLocationOnScreen[1] + 1,
+ 0,
+ mNumberPicker.getHeight() - 2);
+
+ // At this point we expect that the drag-down gesture has selected the value
+ // that was "above" the previously selected one, and that our value change listener
+ // has been notified of that change exactly once.
+ assertEquals(6, mNumberPicker.getValue());
+ verify(mockValueChangeListener, times(1)).onValueChange(mNumberPicker, 7, 6);
+ verifyNoMoreInteractions(mockValueChangeListener);
+
+ // We expect that our scroll listener will be called with specific state changes.
+ InOrder inOrder = inOrder(mockScrollListener);
+ inOrder.verify(mockScrollListener).onScrollStateChange(mNumberPicker,
+ NumberPicker.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL);
+ inOrder.verify(mockScrollListener).onScrollStateChange(mNumberPicker,
+ NumberPicker.OnScrollListener.SCROLL_STATE_IDLE);
+ verifyNoMoreInteractions(mockScrollListener);
+ }
+
+ public void testInteractionWithSwipeUp() {
+ mInstrumentation.runOnMainSync(() -> {
+ mNumberPicker.setMinValue(10);
+ mNumberPicker.setMaxValue(12);
+ mNumberPicker.setDisplayedValues(NUMBER_NAMES_ALT3);
+ });
+
+ final NumberPicker.OnValueChangeListener mockValueChangeListener =
+ mock(NumberPicker.OnValueChangeListener.class);
+ mNumberPicker.setOnValueChangedListener(mockValueChangeListener);
+
+ final NumberPicker.OnScrollListener mockScrollListener =
+ mock(NumberPicker.OnScrollListener.class);
+ mNumberPicker.setOnScrollListener(mockScrollListener);
+
+ mInstrumentation.runOnMainSync(() -> mNumberPicker.setValue(11));
+ assertEquals(11, mNumberPicker.getValue());
+
+ // Swipe up across our number picker
+ final int[] numberPickerLocationOnScreen = new int[2];
+ mNumberPicker.getLocationOnScreen(numberPickerLocationOnScreen);
+
+ CtsTouchUtils.emulateDragGesture(mInstrumentation,
+ numberPickerLocationOnScreen[0] + mNumberPicker.getWidth() / 2,
+ numberPickerLocationOnScreen[1] + mNumberPicker.getHeight() - 1,
+ 0,
+ - (mNumberPicker.getHeight() - 2));
+
+ // At this point we expect that the drag-up gesture has selected the value
+ // that was "below" the previously selected one, and that our value change listener
+ // has been notified of that change exactly once.
+ assertEquals(12, mNumberPicker.getValue());
+ verify(mockValueChangeListener, times(1)).onValueChange(mNumberPicker, 11, 12);
+ verifyNoMoreInteractions(mockValueChangeListener);
+
+ // We expect that our scroll listener will be called with specific state changes.
+ InOrder inOrder = inOrder(mockScrollListener);
+ inOrder.verify(mockScrollListener).onScrollStateChange(mNumberPicker,
+ NumberPicker.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL);
+ inOrder.verify(mockScrollListener).onScrollStateChange(mNumberPicker,
+ NumberPicker.OnScrollListener.SCROLL_STATE_IDLE);
+ verifyNoMoreInteractions(mockScrollListener);
+ }
+
+ public void testAccessWrapSelectorValue() {
+ mInstrumentation.runOnMainSync(() -> {
+ mNumberPicker.setMinValue(100);
+ mNumberPicker.setMaxValue(200);
+ });
+ // As specified in the Javadocs of NumberPicker.setWrapSelectorWheel, when min/max
+ // range is larger than what the widget is showing, the selector wheel is enabled.
+ assertTrue(mNumberPicker.getWrapSelectorWheel());
+
+ mInstrumentation.runOnMainSync(() -> mNumberPicker.setWrapSelectorWheel(false));
+ assertFalse(mNumberPicker.getWrapSelectorWheel());
+
+ mInstrumentation.runOnMainSync(() -> mNumberPicker.setWrapSelectorWheel(true));
+ assertTrue(mNumberPicker.getWrapSelectorWheel());
+ }
+}
diff --git a/tests/tests/widget/src/android/widget/cts/PopupMenuTest.java b/tests/tests/widget/src/android/widget/cts/PopupMenuTest.java
index ee38448..99287b0 100644
--- a/tests/tests/widget/src/android/widget/cts/PopupMenuTest.java
+++ b/tests/tests/widget/src/android/widget/cts/PopupMenuTest.java
@@ -16,23 +16,22 @@
package android.widget.cts;
+import static org.mockito.Mockito.*;
+
import android.app.Activity;
import android.app.Instrumentation;
-import android.os.SystemClock;
+import android.cts.util.CtsTouchUtils;
import android.test.ActivityInstrumentationTestCase2;
import android.test.suitebuilder.annotation.SmallTest;
import android.view.Gravity;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
-import android.view.MotionEvent;
import android.view.SubMenu;
import android.view.View;
import android.widget.EditText;
import android.widget.PopupMenu;
-import static org.mockito.Mockito.*;
-
@SmallTest
public class PopupMenuTest extends
ActivityInstrumentationTestCase2<PopupMenuCtsActivity> {
@@ -70,7 +69,7 @@
protected void tearDown() throws Exception {
if (mPopupMenu != null) {
try {
- runTestOnUiThread(() -> mPopupMenu.dismiss());
+ runTestOnUiThread(mPopupMenu::dismiss);
} catch (Throwable t) {
throw new RuntimeException(t);
}
@@ -97,7 +96,7 @@
public void testPopulateViaInflater() throws Throwable {
mBuilder = new Builder().inflateWithInflater(true);
- runTestOnUiThread(() -> mBuilder.show());
+ runTestOnUiThread(mBuilder::show);
mInstrumentation.waitForIdleSync();
verifyMenuContent();
@@ -105,7 +104,7 @@
public void testDirectPopulate() throws Throwable {
mBuilder = new Builder().inflateWithInflater(false);
- runTestOnUiThread(() -> mBuilder.show());
+ runTestOnUiThread(mBuilder::show);
mInstrumentation.waitForIdleSync();
verifyMenuContent();
@@ -113,7 +112,7 @@
public void testAccessGravity() throws Throwable {
mBuilder = new Builder();
- runTestOnUiThread(() -> mBuilder.show());
+ runTestOnUiThread(mBuilder::show);
assertEquals(Gravity.NO_GRAVITY, mPopupMenu.getGravity());
mPopupMenu.setGravity(Gravity.TOP);
@@ -122,23 +121,23 @@
public void testConstructorWithGravity() throws Throwable {
mBuilder = new Builder().withGravity(Gravity.TOP);
- runTestOnUiThread(() -> mBuilder.show());
+ runTestOnUiThread(mBuilder::show);
assertEquals(Gravity.TOP, mPopupMenu.getGravity());
}
public void testDismissalViaAPI() throws Throwable {
mBuilder = new Builder().withDismissListener();
- runTestOnUiThread(() -> mBuilder.show());
+ runTestOnUiThread(mBuilder::show);
mInstrumentation.waitForIdleSync();
verify(mBuilder.mOnDismissListener, never()).onDismiss(mPopupMenu);
- runTestOnUiThread(() -> mPopupMenu.dismiss());
+ runTestOnUiThread(mPopupMenu::dismiss);
mInstrumentation.waitForIdleSync();
verify(mBuilder.mOnDismissListener, times(1)).onDismiss(mPopupMenu);
- runTestOnUiThread(() -> mPopupMenu.dismiss());
+ runTestOnUiThread(mPopupMenu::dismiss);
mInstrumentation.waitForIdleSync();
// Shouldn't have any more interactions with our dismiss listener since the menu was
// already dismissed when we called dismiss()
@@ -151,7 +150,7 @@
// "click" a submenu item.
mBuilder = new Builder().withDismissListener()
.withPopupStyleResource(R.style.PopupWindow_NullTransitions);
- runTestOnUiThread(() -> mBuilder.show());
+ runTestOnUiThread(mBuilder::show);
mInstrumentation.waitForIdleSync();
verify(mBuilder.mOnDismissListener, never()).onDismiss(mPopupMenu);
@@ -162,11 +161,11 @@
performIdentifierAction(R.id.action_share_email, 0));
mInstrumentation.waitForIdleSync();
- runTestOnUiThread(() -> mPopupMenu.dismiss());
+ runTestOnUiThread(mPopupMenu::dismiss);
mInstrumentation.waitForIdleSync();
verify(mBuilder.mOnDismissListener, times(1)).onDismiss(mPopupMenu);
- runTestOnUiThread(() -> mPopupMenu.dismiss());
+ runTestOnUiThread(mPopupMenu::dismiss);
mInstrumentation.waitForIdleSync();
// Shouldn't have any more interactions with our dismiss listener since the menu was
// already dismissed when we called dismiss()
@@ -180,47 +179,18 @@
mBuilder = new Builder().withDismissListener()
.withPopupMenuContent(R.menu.popup_menu_single)
.withPopupStyleResource(R.style.PopupWindow_NullTransitions);
- runTestOnUiThread(() -> mBuilder.show());
+ runTestOnUiThread(mBuilder::show);
mInstrumentation.waitForIdleSync();
- // Determine the location of the anchor on the screen so that we can emulate
- // a tap outside of the popup bounds to dismiss the popup
- final int[] anchorOnScreenXY = new int[2];
- mBuilder.mAnchor.getLocationOnScreen(anchorOnScreenXY);
-
- int emulatedTapX = anchorOnScreenXY[0] + 10;
- int emulatedTapY = anchorOnScreenXY[1] - 20;
-
- // The logic below uses Instrumentation to emulate a tap outside the bounds of the
+ // The call below uses Instrumentation to emulate a tap outside the bounds of the
// displayed popup menu. This tap is then treated by the framework to be "split" as
// the ACTION_OUTSIDE for the popup itself, as well as DOWN / MOVE / UP for the underlying
// view root if the popup is not modal.
// It is not correct to emulate these two sequences separately in the test, as it
- // wouldn't emulate the user-facing interaction for this test. Note that usage
- // of Instrumentation is necessary here since Espresso's actions operate at the level
- // of view or data. Also, we don't want to use View.dispatchTouchEvent directly as
- // that would require emulation of two separate sequences as well.
-
- // Inject DOWN event
- long downTime = SystemClock.uptimeMillis();
- MotionEvent eventDown = MotionEvent.obtain(
- downTime, downTime, MotionEvent.ACTION_DOWN, emulatedTapX, emulatedTapY, 1);
- mInstrumentation.sendPointerSync(eventDown);
-
- // Inject MOVE event
- long moveTime = SystemClock.uptimeMillis();
- MotionEvent eventMove = MotionEvent.obtain(
- moveTime, moveTime, MotionEvent.ACTION_MOVE, emulatedTapX, emulatedTapY, 1);
- mInstrumentation.sendPointerSync(eventMove);
-
- // Inject UP event
- long upTime = SystemClock.uptimeMillis();
- MotionEvent eventUp = MotionEvent.obtain(
- upTime, upTime, MotionEvent.ACTION_UP, emulatedTapX, emulatedTapY, 1);
- mInstrumentation.sendPointerSync(eventUp);
-
- // Wait for the system to process all events in the queue
- mInstrumentation.waitForIdleSync();
+ // wouldn't emulate the user-facing interaction for this test. Also, we don't want to use
+ // View.dispatchTouchEvent directly as that would require emulation of two separate
+ // sequences as well.
+ CtsTouchUtils.emulateTapOnScreen(mInstrumentation, mBuilder.mAnchor, 10, -20);
// At this point our popup should have notified its dismiss listener
verify(mBuilder.mOnDismissListener, times(1)).onDismiss(mPopupMenu);
@@ -228,7 +198,7 @@
public void testSimpleMenuItemClickViaAPI() throws Throwable {
mBuilder = new Builder().withMenuItemClickListener().withDismissListener();
- runTestOnUiThread(() -> mBuilder.show());
+ runTestOnUiThread(mBuilder::show);
// Verify that our menu item click listener hasn't been called yet
verify(mBuilder.mOnMenuItemClickListener, never()).onMenuItemClick(any(MenuItem.class));
@@ -251,7 +221,7 @@
// "click" a submenu item.
mBuilder = new Builder().withDismissListener().withMenuItemClickListener()
.withPopupStyleResource(R.style.PopupWindow_NullTransitions);
- runTestOnUiThread(() -> mBuilder.show());
+ runTestOnUiThread(mBuilder::show);
mInstrumentation.waitForIdleSync();
// Verify that our menu item click listener hasn't been called yet
diff --git a/tests/tests/widget/src/android/widget/cts/PopupWindowTest.java b/tests/tests/widget/src/android/widget/cts/PopupWindowTest.java
index 47efffc..97d6e62 100644
--- a/tests/tests/widget/src/android/widget/cts/PopupWindowTest.java
+++ b/tests/tests/widget/src/android/widget/cts/PopupWindowTest.java
@@ -16,16 +16,20 @@
package android.widget.cts;
+import static org.mockito.Mockito.*;
+
import android.app.Activity;
import android.app.Instrumentation;
import android.content.Context;
import android.graphics.Color;
+import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.SystemClock;
import android.test.ActivityInstrumentationTestCase2;
import android.test.UiThreadTest;
+import android.test.suitebuilder.annotation.SmallTest;
import android.transition.Transition;
import android.transition.Transition.TransitionListener;
import android.transition.TransitionValues;
@@ -44,13 +48,7 @@
import android.widget.TextView;
import android.widget.cts.R;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
+@SmallTest
public class PopupWindowTest extends
ActivityInstrumentationTestCase2<PopupWindowCtsActivity> {
private Instrumentation mInstrumentation;
@@ -84,6 +82,14 @@
new PopupWindow(mActivity, null, android.R.attr.popupWindowStyle);
+ new PopupWindow(mActivity, null, 0, android.R.style.Widget_DeviceDefault_PopupWindow);
+
+ new PopupWindow(mActivity, null, 0, android.R.style.Widget_DeviceDefault_Light_PopupWindow);
+
+ new PopupWindow(mActivity, null, 0, android.R.style.Widget_Material_PopupWindow);
+
+ new PopupWindow(mActivity, null, 0, android.R.style.Widget_Material_Light_PopupWindow);
+
mPopupWindow = new PopupWindow();
assertEquals(0, mPopupWindow.getWidth());
assertEquals(0, mPopupWindow.getHeight());
@@ -509,7 +515,7 @@
assertTrue("Content (" + contentFrame + ") extends outside display (" + displayFrame + ")",
displayFrame.contains(contentFrame));
- getInstrumentation().runOnMainSync(() -> popup.dismiss());
+ getInstrumentation().runOnMainSync(popup::dismiss);
getInstrumentation().waitForIdleSync();
assertFalse(popup.isShowing());
@@ -576,6 +582,8 @@
// Do not attach within the decor; we will be measuring location
// with regard to screen coordinates.
mPopupWindow.setAttachedInDecor(false);
+ assertFalse(mPopupWindow.isAttachedInDecor());
+
final View upperAnchor = mActivity.findViewById(R.id.anchor_upper);
final int xOff = 10;
@@ -661,42 +669,73 @@
public void testGetMaxAvailableHeight() {
mPopupWindow = createPopupWindow(createPopupContent(50, 50));
+ final Point point = new Point();
+ getDisplay().getSize(point);
+ final int displayHeight = point.y;
View anchorView = mActivity.findViewById(R.id.anchor_upper);
- int avaliable = getDisplay().getHeight() - anchorView.getHeight();
+ int available = displayHeight - anchorView.getHeight();
int maxAvailableHeight = mPopupWindow.getMaxAvailableHeight(anchorView);
+ int maxAvailableHeightIgnoringBottomDecoration =
+ mPopupWindow.getMaxAvailableHeight(anchorView, 0, true);
assertTrue(maxAvailableHeight > 0);
- assertTrue(maxAvailableHeight <= avaliable);
+ assertTrue(maxAvailableHeight <= available);
+ assertTrue(maxAvailableHeightIgnoringBottomDecoration >= maxAvailableHeight);
+ assertTrue(maxAvailableHeightIgnoringBottomDecoration <= available);
+
int maxAvailableHeightWithOffset = mPopupWindow.getMaxAvailableHeight(anchorView, 2);
assertEquals(maxAvailableHeight - 2, maxAvailableHeightWithOffset);
+
maxAvailableHeightWithOffset =
mPopupWindow.getMaxAvailableHeight(anchorView, maxAvailableHeight);
assertTrue(maxAvailableHeightWithOffset > 0);
- assertTrue(maxAvailableHeightWithOffset <= avaliable);
+ assertTrue(maxAvailableHeightWithOffset <= available);
+
maxAvailableHeightWithOffset =
mPopupWindow.getMaxAvailableHeight(anchorView, maxAvailableHeight / 2 - 1);
assertTrue(maxAvailableHeightWithOffset > 0);
- assertTrue(maxAvailableHeightWithOffset <= avaliable);
+ assertTrue(maxAvailableHeightWithOffset <= available);
+
maxAvailableHeightWithOffset = mPopupWindow.getMaxAvailableHeight(anchorView, -1);
assertTrue(maxAvailableHeightWithOffset > 0);
- assertTrue(maxAvailableHeightWithOffset <= avaliable);
+ assertTrue(maxAvailableHeightWithOffset <= available);
+
+ int maxAvailableHeightWithOffsetIgnoringBottomDecoration =
+ mPopupWindow.getMaxAvailableHeight(anchorView, 2, true);
+ assertEquals(maxAvailableHeightIgnoringBottomDecoration - 2,
+ maxAvailableHeightWithOffsetIgnoringBottomDecoration);
+
+ maxAvailableHeightWithOffsetIgnoringBottomDecoration =
+ mPopupWindow.getMaxAvailableHeight(anchorView, maxAvailableHeight, true);
+ assertTrue(maxAvailableHeightWithOffsetIgnoringBottomDecoration > 0);
+ assertTrue(maxAvailableHeightWithOffsetIgnoringBottomDecoration <= available);
+
+ maxAvailableHeightWithOffsetIgnoringBottomDecoration =
+ mPopupWindow.getMaxAvailableHeight(anchorView, maxAvailableHeight / 2 - 1, true);
+ assertTrue(maxAvailableHeightWithOffsetIgnoringBottomDecoration > 0);
+ assertTrue(maxAvailableHeightWithOffsetIgnoringBottomDecoration <= available);
+
+ maxAvailableHeightWithOffsetIgnoringBottomDecoration =
+ mPopupWindow.getMaxAvailableHeight(anchorView, -1, true);
+ assertTrue(maxAvailableHeightWithOffsetIgnoringBottomDecoration > 0);
+ assertTrue(maxAvailableHeightWithOffsetIgnoringBottomDecoration <= available);
anchorView = mActivity.findViewById(R.id.anchor_lower);
// On some devices the view might actually have larger size than the physical display
// due to chin and content will be laid out as if outside of the display. We need to use
// larger from the display height and the main view height.
- avaliable = Math.max(getDisplay().getHeight(),
+ available = Math.max(displayHeight,
mActivity.findViewById(android.R.id.content).getHeight()) - anchorView.getHeight();
maxAvailableHeight = mPopupWindow.getMaxAvailableHeight(anchorView);
assertTrue(maxAvailableHeight > 0);
- assertTrue(maxAvailableHeight <= avaliable);
+ assertTrue(maxAvailableHeight <= available);
anchorView = mActivity.findViewById(R.id.anchor_middle_left);
- avaliable = getDisplay().getHeight() - anchorView.getHeight()
+ available = displayHeight - anchorView.getHeight()
- mActivity.findViewById(R.id.anchor_upper).getHeight();
maxAvailableHeight = mPopupWindow.getMaxAvailableHeight(anchorView);
assertTrue(maxAvailableHeight > 0);
- assertTrue(maxAvailableHeight <= avaliable);
+ assertTrue(maxAvailableHeight <= available);
}
@UiThreadTest
@@ -756,7 +795,7 @@
assertEquals(0, WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS & p.flags);
assertEquals(0, WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM & p.flags);
- mInstrumentation.runOnMainSync(() -> mPopupWindow.update());
+ mInstrumentation.runOnMainSync(mPopupWindow::update);
mInstrumentation.waitForIdleSync();
assertEquals(WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES,
@@ -798,7 +837,7 @@
verify(exitListener, never()).onTransitionStart(any(Transition.class));
verify(dismissListener, never()).onDismiss();
- mInstrumentation.runOnMainSync(() -> mPopupWindow.dismiss());
+ mInstrumentation.runOnMainSync(mPopupWindow::dismiss);
mInstrumentation.waitForIdleSync();
verify(enterListener, times(1)).onTransitionStart(any(Transition.class));
verify(exitListener, times(1)).onTransitionStart(any(Transition.class));
@@ -821,6 +860,7 @@
mInstrumentation.waitForIdleSync();
// Do not update if it is not shown
assertFalse(mPopupWindow.isShowing());
+ assertFalse(mPopupWindow.isAttachedInDecor());
assertEquals(100, mPopupWindow.getWidth());
assertEquals(100, mPopupWindow.getHeight());
@@ -830,6 +870,14 @@
containerView.getWindowDisplayFrame(containingRect);
// update if it is not shown
+ mInstrumentation.runOnMainSync(() -> mPopupWindow.update(80, 80));
+
+ mInstrumentation.waitForIdleSync();
+ assertTrue(mPopupWindow.isShowing());
+ assertEquals(80, mPopupWindow.getWidth());
+ assertEquals(80, mPopupWindow.getHeight());
+
+ // update if it is not shown
mInstrumentation.runOnMainSync(() -> mPopupWindow.update(20, 50, 50, 50));
mInstrumentation.waitForIdleSync();
@@ -884,7 +932,7 @@
assertEquals(50, mPopupWindow.getWidth());
assertEquals(50, mPopupWindow.getHeight());
- mInstrumentation.runOnMainSync(() -> mPopupWindow.dismiss());
+ mInstrumentation.runOnMainSync(mPopupWindow::dismiss);
mInstrumentation.waitForIdleSync();
}
@@ -1069,6 +1117,61 @@
assertEquals(LayoutParams.MATCH_PARENT, p.height);
}
+ public void testAccessElevation() {
+ mPopupWindow = createPopupWindow(createPopupContent(50, 50));
+ mInstrumentation.runOnMainSync(() -> mPopupWindow.setElevation(2.0f));
+
+ showPopup();
+ assertEquals(2.0f, mPopupWindow.getElevation());
+
+ dismissPopup();
+ mInstrumentation.runOnMainSync(() -> mPopupWindow.setElevation(4.0f));
+ showPopup();
+ assertEquals(4.0f, mPopupWindow.getElevation());
+
+ dismissPopup();
+ mInstrumentation.runOnMainSync(() -> mPopupWindow.setElevation(10.0f));
+ showPopup();
+ assertEquals(10.0f, mPopupWindow.getElevation());
+ }
+
+ public void testAccessSoftInputMode() {
+ mPopupWindow = createPopupWindow(createPopupContent(50, 50));
+ mInstrumentation.runOnMainSync(
+ () -> mPopupWindow.setSoftInputMode(
+ WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE));
+
+ showPopup();
+ assertEquals(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE,
+ mPopupWindow.getSoftInputMode());
+
+ dismissPopup();
+ mInstrumentation.runOnMainSync(
+ () -> mPopupWindow.setSoftInputMode(
+ WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN));
+ showPopup();
+ assertEquals(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN,
+ mPopupWindow.getSoftInputMode());
+ }
+
+ public void testAccessSplitTouchEnabled() {
+ mPopupWindow = createPopupWindow(createPopupContent(50, 50));
+ mInstrumentation.runOnMainSync(() -> mPopupWindow.setSplitTouchEnabled(true));
+
+ showPopup();
+ assertTrue(mPopupWindow.isSplitTouchEnabled());
+
+ dismissPopup();
+ mInstrumentation.runOnMainSync(() -> mPopupWindow.setSplitTouchEnabled(false));
+ showPopup();
+ assertFalse(mPopupWindow.isSplitTouchEnabled());
+
+ dismissPopup();
+ mInstrumentation.runOnMainSync(() -> mPopupWindow.setSplitTouchEnabled(true));
+ showPopup();
+ assertTrue(mPopupWindow.isSplitTouchEnabled());
+ }
+
private static class BaseTransition extends Transition {
@Override
public void captureStartValues(TransitionValues transitionValues) {}
diff --git a/tests/tests/widget/src/android/widget/cts/PositionTesterContextMenuListener.java b/tests/tests/widget/src/android/widget/cts/PositionTesterContextMenuListener.java
index a1c9bc4..5d9fd4c 100644
--- a/tests/tests/widget/src/android/widget/cts/PositionTesterContextMenuListener.java
+++ b/tests/tests/widget/src/android/widget/cts/PositionTesterContextMenuListener.java
@@ -17,11 +17,11 @@
package android.widget.cts;
import android.view.ContextMenu;
-import android.view.View;
import android.view.ContextMenu.ContextMenuInfo;
+import android.view.View;
import android.view.View.OnCreateContextMenuListener;
-import android.widget.ExpandableListView;
import android.widget.AdapterView.AdapterContextMenuInfo;
+import android.widget.ExpandableListView;
public class PositionTesterContextMenuListener implements OnCreateContextMenuListener {
diff --git a/tests/tests/widget/src/android/widget/cts/ProgressBarCtsActivity.java b/tests/tests/widget/src/android/widget/cts/ProgressBarCtsActivity.java
index 8298d5f..2b3a45d 100644
--- a/tests/tests/widget/src/android/widget/cts/ProgressBarCtsActivity.java
+++ b/tests/tests/widget/src/android/widget/cts/ProgressBarCtsActivity.java
@@ -18,17 +18,18 @@
import android.app.Activity;
import android.os.Bundle;
+import android.widget.ProgressBar;
/**
- * An application for ProgressBar test
- *
+ * A minimal application for {@link ProgressBar} test.
*/
public class ProgressBarCtsActivity extends Activity {
/**
- * Called with the activity is first created.
+ * Called when the activity is first created.
*/
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ setContentView(R.layout.progressbar_layout);
}
}
diff --git a/tests/tests/widget/src/android/widget/cts/ProgressBarTest.java b/tests/tests/widget/src/android/widget/cts/ProgressBarTest.java
index 1cf7334..b2272d2 100644
--- a/tests/tests/widget/src/android/widget/cts/ProgressBarTest.java
+++ b/tests/tests/widget/src/android/widget/cts/ProgressBarTest.java
@@ -16,412 +16,449 @@
package android.widget.cts;
-import android.content.res.ColorStateList;
-import android.graphics.Color;
-import android.graphics.PorterDuff;
-import android.view.LayoutInflater;
-
-import android.widget.cts.R;
-
+import static org.mockito.Matchers.*;
+import static org.mockito.Mockito.*;
import android.content.Context;
+import android.content.res.ColorStateList;
import android.graphics.Canvas;
-import android.graphics.ColorFilter;
+import android.graphics.Color;
+import android.graphics.PorterDuff;
+import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Parcelable;
-import android.test.InstrumentationTestCase;
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.UiThreadTest;
+import android.test.suitebuilder.annotation.SmallTest;
import android.view.View;
import android.view.animation.AccelerateDecelerateInterpolator;
+import android.view.animation.AccelerateInterpolator;
import android.view.animation.Interpolator;
import android.view.animation.LinearInterpolator;
import android.widget.ProgressBar;
+import android.widget.cts.util.TestUtils;
-public class ProgressBarTest extends InstrumentationTestCase {
- // The target context.
- private Context mContext;
+@SmallTest
+public class ProgressBarTest extends ActivityInstrumentationTestCase2<ProgressBarCtsActivity> {
+ private ProgressBarCtsActivity mActivity;
+ private ProgressBar mProgressBar;
+ private ProgressBar mProgressBarHorizontal;
+
+ public ProgressBarTest() {
+ super("android.widget.cts", ProgressBarCtsActivity.class);
+ }
@Override
protected void setUp() throws Exception {
super.setUp();
- mContext = getInstrumentation().getTargetContext();
+
+ mActivity = getActivity();
+ mProgressBar = (ProgressBar) mActivity.findViewById(R.id.progress);
+ mProgressBarHorizontal = (ProgressBar) mActivity.findViewById(R.id.progress_horizontal);
}
public void testConstructor() {
- new ProgressBar(mContext);
+ new ProgressBar(mActivity);
- new ProgressBar(mContext, null);
+ new ProgressBar(mActivity, null);
- new ProgressBar(mContext, null, android.R.attr.progressBarStyle);
+ new ProgressBar(mActivity, null, android.R.attr.progressBarStyle);
+
+ new ProgressBar(mActivity, null, android.R.attr.progressBarStyleHorizontal);
+
+ new ProgressBar(mActivity, null, android.R.attr.progressBarStyleInverse);
+
+ new ProgressBar(mActivity, null, android.R.attr.progressBarStyleLarge);
+
+ new ProgressBar(mActivity, null, android.R.attr.progressBarStyleLargeInverse);
+
+ new ProgressBar(mActivity, null, android.R.attr.progressBarStyleSmall);
+
+ new ProgressBar(mActivity, null, android.R.attr.progressBarStyleSmallInverse);
+
+ new ProgressBar(mActivity, null, android.R.attr.progressBarStyleSmallTitle);
+
+ new ProgressBar(mActivity, null, 0, android.R.style.Widget_DeviceDefault_Light_ProgressBar);
+
+ new ProgressBar(mActivity, null, 0,
+ android.R.style.Widget_DeviceDefault_Light_ProgressBar_Horizontal);
+
+ new ProgressBar(mActivity, null, 0,
+ android.R.style.Widget_DeviceDefault_Light_ProgressBar_Inverse);
+
+ new ProgressBar(mActivity, null, 0,
+ android.R.style.Widget_DeviceDefault_Light_ProgressBar_Large);
+
+ new ProgressBar(mActivity, null, 0,
+ android.R.style.Widget_DeviceDefault_Light_ProgressBar_Large_Inverse);
+
+ new ProgressBar(mActivity, null, 0,
+ android.R.style.Widget_DeviceDefault_Light_ProgressBar_Small);
+
+ new ProgressBar(mActivity, null, 0,
+ android.R.style.Widget_DeviceDefault_Light_ProgressBar_Small_Inverse);
+
+ new ProgressBar(mActivity, null, 0,
+ android.R.style.Widget_DeviceDefault_Light_ProgressBar_Small_Title);
+
+ new ProgressBar(mActivity, null, 0, android.R.style.Widget_Material_Light_ProgressBar);
+
+ new ProgressBar(mActivity, null, 0,
+ android.R.style.Widget_Material_Light_ProgressBar_Horizontal);
+
+ new ProgressBar(mActivity, null, 0,
+ android.R.style.Widget_Material_Light_ProgressBar_Inverse);
+
+ new ProgressBar(mActivity, null, 0,
+ android.R.style.Widget_Material_Light_ProgressBar_Large);
+
+ new ProgressBar(mActivity, null, 0,
+ android.R.style.Widget_Material_Light_ProgressBar_Large_Inverse);
+
+ new ProgressBar(mActivity, null, 0,
+ android.R.style.Widget_Material_Light_ProgressBar_Small);
+
+ new ProgressBar(mActivity, null, 0,
+ android.R.style.Widget_Material_Light_ProgressBar_Small_Inverse);
+
+ new ProgressBar(mActivity, null, 0,
+ android.R.style.Widget_Material_Light_ProgressBar_Small_Title);
}
+ @UiThreadTest
public void testSetIndeterminate() {
- ProgressBar progressBar = new ProgressBar(mContext);
- assertTrue(progressBar.isIndeterminate());
+ assertTrue(mProgressBar.isIndeterminate());
- progressBar.setIndeterminate(true);
- assertTrue(progressBar.isIndeterminate());
+ mProgressBar.setIndeterminate(true);
+ assertTrue(mProgressBar.isIndeterminate());
- progressBar.setIndeterminate(false);
+ mProgressBar.setIndeterminate(false);
// because default is Indeterminate only progressBar, can't change the status
- assertTrue(progressBar.isIndeterminate());
+ assertTrue(mProgressBar.isIndeterminate());
- progressBar = new ProgressBar(mContext, null, android.R.attr.progressBarStyleHorizontal);
- assertFalse(progressBar.isIndeterminate());
+ assertFalse(mProgressBarHorizontal.isIndeterminate());
- progressBar.setIndeterminate(true);
- assertTrue(progressBar.isIndeterminate());
+ mProgressBarHorizontal.setIndeterminate(true);
+ assertTrue(mProgressBarHorizontal.isIndeterminate());
- progressBar.setIndeterminate(false);
- assertFalse(progressBar.isIndeterminate());
+ mProgressBarHorizontal.setIndeterminate(false);
+ assertFalse(mProgressBarHorizontal.isIndeterminate());
}
+ @UiThreadTest
public void testAccessIndeterminateDrawable() {
- ProgressBar progressBar = new ProgressBar(mContext);
-
// set IndeterminateDrawable
// normal value
- MockDrawable mockDrawable = new MockDrawable();
- progressBar.setIndeterminateDrawable(mockDrawable);
- assertSame(mockDrawable, progressBar.getIndeterminateDrawable());
- assertFalse(mockDrawable.hasCalledDraw());
- progressBar.draw(new Canvas());
- assertTrue(mockDrawable.hasCalledDraw());
+ Drawable mockProgressDrawable = spy(new ColorDrawable(Color.YELLOW));
+ mProgressBar.setIndeterminateDrawable(mockProgressDrawable);
+ assertSame(mockProgressDrawable, mProgressBar.getIndeterminateDrawable());
+ verify(mockProgressDrawable, never()).draw(any(Canvas.class));
+ mProgressBar.draw(new Canvas());
+ verify(mockProgressDrawable, atLeastOnce()).draw(any(Canvas.class));
// exceptional value
- progressBar.setIndeterminateDrawable(null);
- assertNull(progressBar.getIndeterminateDrawable());
+ mProgressBar.setIndeterminateDrawable(null);
+ assertNull(mProgressBar.getIndeterminateDrawable());
}
+ @UiThreadTest
public void testAccessProgressDrawable() {
- ProgressBar progressBar = new ProgressBar(mContext, null,
- android.R.attr.progressBarStyleHorizontal);
-
// set ProgressDrawable
// normal value
- MockDrawable mockDrawable = new MockDrawable();
- progressBar.setProgressDrawable(mockDrawable);
- assertSame(mockDrawable, progressBar.getProgressDrawable());
- assertFalse(mockDrawable.hasCalledDraw());
- progressBar.draw(new Canvas());
- assertTrue(mockDrawable.hasCalledDraw());
+ Drawable mockProgressDrawable = spy(new ColorDrawable(Color.BLUE));
+ mProgressBarHorizontal.setProgressDrawable(mockProgressDrawable);
+ assertSame(mockProgressDrawable, mProgressBarHorizontal.getProgressDrawable());
+ verify(mockProgressDrawable, never()).draw(any(Canvas.class));
+ mProgressBarHorizontal.draw(new Canvas());
+ verify(mockProgressDrawable, atLeastOnce()).draw(any(Canvas.class));
// exceptional value
- progressBar.setProgressDrawable(null);
- assertNull(progressBar.getProgressDrawable());
+ mProgressBarHorizontal.setProgressDrawable(null);
+ assertNull(mProgressBarHorizontal.getProgressDrawable());
}
+ @UiThreadTest
public void testAccessProgress() {
- ProgressBar progressBar = new ProgressBar(mContext, null,
- android.R.attr.progressBarStyleHorizontal);
- assertEquals(0, progressBar.getProgress());
+ assertEquals(0, mProgressBarHorizontal.getProgress());
- final int maxProgress = progressBar.getMax();
+ final int maxProgress = mProgressBarHorizontal.getMax();
// set Progress
// normal value
- progressBar.setProgress(maxProgress >> 1);
- assertEquals(maxProgress >> 1, progressBar.getProgress());
+ mProgressBarHorizontal.setProgress(maxProgress >> 1);
+ assertEquals(maxProgress >> 1, mProgressBarHorizontal.getProgress());
// exceptional values
- progressBar.setProgress(-1);
- assertEquals(0, progressBar.getProgress());
+ mProgressBarHorizontal.setProgress(-1);
+ assertEquals(0, mProgressBarHorizontal.getProgress());
- progressBar.setProgress(maxProgress + 1);
- assertEquals(maxProgress, progressBar.getProgress());
+ mProgressBarHorizontal.setProgress(maxProgress + 1);
+ assertEquals(maxProgress, mProgressBarHorizontal.getProgress());
- progressBar.setProgress(Integer.MAX_VALUE);
- assertEquals(maxProgress, progressBar.getProgress());
+ mProgressBarHorizontal.setProgress(Integer.MAX_VALUE);
+ assertEquals(maxProgress, mProgressBarHorizontal.getProgress());
+
+ mProgressBarHorizontal.setProgress(0, true);
+ assertEquals(0, mProgressBarHorizontal.getProgress());
// when in indeterminate mode
- progressBar.setIndeterminate(true);
- progressBar.setProgress(maxProgress >> 1);
- assertEquals(0, progressBar.getProgress());
+ mProgressBarHorizontal.setIndeterminate(true);
+ mProgressBarHorizontal.setProgress(maxProgress >> 1);
+ assertEquals(0, mProgressBarHorizontal.getProgress());
}
+ @UiThreadTest
public void testAccessSecondaryProgress() {
- ProgressBar progressBar = new ProgressBar(mContext, null,
- android.R.attr.progressBarStyleHorizontal);
- assertEquals(0, progressBar.getSecondaryProgress());
+ assertEquals(0, mProgressBarHorizontal.getSecondaryProgress());
- final int maxProgress = progressBar.getMax();
+ final int maxProgress = mProgressBarHorizontal.getMax();
// set SecondaryProgress
// normal value
- progressBar.setSecondaryProgress(maxProgress >> 1);
- assertEquals(maxProgress >> 1, progressBar.getSecondaryProgress());
+ mProgressBarHorizontal.setSecondaryProgress(maxProgress >> 1);
+ assertEquals(maxProgress >> 1, mProgressBarHorizontal.getSecondaryProgress());
// exceptional value
- progressBar.setSecondaryProgress(-1);
- assertEquals(0, progressBar.getSecondaryProgress());
+ mProgressBarHorizontal.setSecondaryProgress(-1);
+ assertEquals(0, mProgressBarHorizontal.getSecondaryProgress());
- progressBar.setSecondaryProgress(maxProgress + 1);
- assertEquals(maxProgress, progressBar.getSecondaryProgress());
+ mProgressBarHorizontal.setSecondaryProgress(maxProgress + 1);
+ assertEquals(maxProgress, mProgressBarHorizontal.getSecondaryProgress());
- progressBar.setSecondaryProgress(Integer.MAX_VALUE);
- assertEquals(maxProgress, progressBar.getSecondaryProgress());
+ mProgressBarHorizontal.setSecondaryProgress(Integer.MAX_VALUE);
+ assertEquals(maxProgress, mProgressBarHorizontal.getSecondaryProgress());
// when in indeterminate mode
- progressBar.setIndeterminate(true);
- progressBar.setSecondaryProgress(maxProgress >> 1);
- assertEquals(0, progressBar.getSecondaryProgress());
+ mProgressBarHorizontal.setIndeterminate(true);
+ mProgressBarHorizontal.setSecondaryProgress(maxProgress >> 1);
+ assertEquals(0, mProgressBarHorizontal.getSecondaryProgress());
}
+ @UiThreadTest
public void testIncrementProgressBy() {
- ProgressBar progressBar = new ProgressBar(mContext, null,
- android.R.attr.progressBarStyleHorizontal);
-
// normal value
int increment = 1;
- int oldProgress = progressBar.getProgress();
- progressBar.incrementProgressBy(increment);
- assertEquals(oldProgress + increment, progressBar.getProgress());
+ int oldProgress = mProgressBarHorizontal.getProgress();
+ mProgressBarHorizontal.incrementProgressBy(increment);
+ assertEquals(oldProgress + increment, mProgressBarHorizontal.getProgress());
- increment = progressBar.getMax() >> 1;
- oldProgress = progressBar.getProgress();
- progressBar.incrementProgressBy(increment);
- assertEquals(oldProgress + increment, progressBar.getProgress());
+ increment = mProgressBarHorizontal.getMax() >> 1;
+ oldProgress = mProgressBarHorizontal.getProgress();
+ mProgressBarHorizontal.incrementProgressBy(increment);
+ assertEquals(oldProgress + increment, mProgressBarHorizontal.getProgress());
// exceptional values
- progressBar.setProgress(0);
- progressBar.incrementProgressBy(Integer.MAX_VALUE);
- assertEquals(progressBar.getMax(), progressBar.getProgress());
+ mProgressBarHorizontal.setProgress(0);
+ mProgressBarHorizontal.incrementProgressBy(Integer.MAX_VALUE);
+ assertEquals(mProgressBarHorizontal.getMax(), mProgressBarHorizontal.getProgress());
- progressBar.setProgress(0);
- progressBar.incrementProgressBy(Integer.MIN_VALUE);
- assertEquals(0, progressBar.getProgress());
+ mProgressBarHorizontal.setProgress(0);
+ mProgressBarHorizontal.incrementProgressBy(Integer.MIN_VALUE);
+ assertEquals(0, mProgressBarHorizontal.getProgress());
}
+ @UiThreadTest
public void testIncrementSecondaryProgressBy() {
- ProgressBar progressBar = new ProgressBar(mContext, null,
- android.R.attr.progressBarStyleHorizontal);
-
// normal value
int increment = 1;
- int oldSecondaryProgress = progressBar.getSecondaryProgress();
- progressBar.incrementSecondaryProgressBy(increment);
- assertEquals(oldSecondaryProgress + increment, progressBar.getSecondaryProgress());
+ int oldSecondaryProgress = mProgressBarHorizontal.getSecondaryProgress();
+ mProgressBarHorizontal.incrementSecondaryProgressBy(increment);
+ assertEquals(oldSecondaryProgress + increment,
+ mProgressBarHorizontal.getSecondaryProgress());
- increment = progressBar.getMax() >> 1;
- oldSecondaryProgress = progressBar.getSecondaryProgress();
- progressBar.incrementSecondaryProgressBy(increment);
- assertEquals(oldSecondaryProgress + increment, progressBar.getSecondaryProgress());
+ increment = mProgressBarHorizontal.getMax() >> 1;
+ oldSecondaryProgress = mProgressBarHorizontal.getSecondaryProgress();
+ mProgressBarHorizontal.incrementSecondaryProgressBy(increment);
+ assertEquals(oldSecondaryProgress + increment,
+ mProgressBarHorizontal.getSecondaryProgress());
// exceptional values
- progressBar.setSecondaryProgress(0);
- progressBar.incrementSecondaryProgressBy(Integer.MAX_VALUE);
- assertEquals(progressBar.getMax(), progressBar.getSecondaryProgress());
+ mProgressBarHorizontal.setSecondaryProgress(0);
+ mProgressBarHorizontal.incrementSecondaryProgressBy(Integer.MAX_VALUE);
+ assertEquals(mProgressBarHorizontal.getMax(),
+ mProgressBarHorizontal.getSecondaryProgress());
- progressBar.setSecondaryProgress(0);
- progressBar.incrementSecondaryProgressBy(Integer.MIN_VALUE);
- assertEquals(0, progressBar.getSecondaryProgress());
+ mProgressBarHorizontal.setSecondaryProgress(0);
+ mProgressBarHorizontal.incrementSecondaryProgressBy(Integer.MIN_VALUE);
+ assertEquals(0, mProgressBarHorizontal.getSecondaryProgress());
}
+ @UiThreadTest
public void testAccessInterpolator() {
- ProgressBar progressBar = new ProgressBar(mContext);
-
// default should be LinearInterpolator
- assertTrue(progressBar.getInterpolator() instanceof LinearInterpolator);
+ assertTrue(mProgressBar.getInterpolator() instanceof LinearInterpolator);
- // normal value
- Interpolator i = new AccelerateDecelerateInterpolator();
- progressBar.setInterpolator(i);
- assertEquals(i, progressBar.getInterpolator());
+ Interpolator interpolator = new AccelerateDecelerateInterpolator();
+ mProgressBar.setInterpolator(interpolator);
+ assertEquals(interpolator, mProgressBar.getInterpolator());
+
+ mProgressBar.setInterpolator(mActivity, android.R.anim.accelerate_interpolator);
+ assertTrue(mProgressBar.getInterpolator() instanceof AccelerateInterpolator);
}
+ @UiThreadTest
public void testSetVisibility() {
- ProgressBar progressBar = new ProgressBar(mContext, null,
- android.R.attr.progressBarStyleHorizontal);
-
// set visibility
// normal value
int visibility = View.VISIBLE;
- progressBar.setVisibility(visibility);
- assertEquals(visibility, progressBar.getVisibility());
+ mProgressBarHorizontal.setVisibility(visibility);
+ assertEquals(visibility, mProgressBarHorizontal.getVisibility());
visibility = View.GONE;
- progressBar.setVisibility(visibility);
- assertEquals(visibility, progressBar.getVisibility());
+ mProgressBarHorizontal.setVisibility(visibility);
+ assertEquals(visibility, mProgressBarHorizontal.getVisibility());
// exceptional value
visibility = 0xfffffff5; // -11
int mask = 0x0000000C; // View.VISIBILITY_MASK
- int expected = (progressBar.getVisibility() & ~mask) | (visibility & mask);
- progressBar.setVisibility(visibility);
- assertEquals(expected, progressBar.getVisibility());
+ int expected = (mProgressBarHorizontal.getVisibility() & ~mask) | (visibility & mask);
+ mProgressBarHorizontal.setVisibility(visibility);
+ assertEquals(expected, mProgressBarHorizontal.getVisibility());
visibility = 0x7fffffff; // Integer.MAX_VALUE;
- expected = (progressBar.getVisibility() & ~mask) | (visibility & mask);
- progressBar.setVisibility(Integer.MAX_VALUE);
- assertEquals(expected, progressBar.getVisibility());
+ expected = (mProgressBarHorizontal.getVisibility() & ~mask) | (visibility & mask);
+ mProgressBarHorizontal.setVisibility(Integer.MAX_VALUE);
+ assertEquals(expected, mProgressBarHorizontal.getVisibility());
}
+ @UiThreadTest
public void testInvalidateDrawable() {
- MockProgressBar mockProgressBar = new MockProgressBar(mContext);
+ ProgressBar mockProgressBar = spy(new ProgressBar(mActivity));
- MockDrawable mockDrawable1 = new MockDrawable();
- MockDrawable mockDrawable2 = new MockDrawable();
+ Drawable mockDrawable1 = spy(new ColorDrawable(Color.RED));
+ Drawable mockDrawable2 = spy(new ColorDrawable(Color.GREEN));
mockProgressBar.setBackgroundDrawable(mockDrawable1);
mockProgressBar.invalidateDrawable(mockDrawable1);
- assertTrue(mockProgressBar.hasCalledInvalidate());
+ verify(mockProgressBar, atLeastOnce()).invalidate(anyInt(), anyInt(), anyInt(), anyInt());
- mockProgressBar.reset();
+ reset(mockProgressBar);
mockProgressBar.invalidateDrawable(mockDrawable2);
- assertFalse(mockProgressBar.hasCalledInvalidate());
+ verify(mockProgressBar, never()).invalidate(anyInt(), anyInt(), anyInt(), anyInt());
mockProgressBar.setIndeterminateDrawable(mockDrawable1);
mockProgressBar.setProgressDrawable(mockDrawable2);
}
+ @UiThreadTest
public void testPostInvalidate() {
- MockProgressBar mockProgressBar = new MockProgressBar(mContext);
- mockProgressBar.postInvalidate();
+ mProgressBarHorizontal.postInvalidate();
}
+ @UiThreadTest
public void testAccessMax() {
- ProgressBar progressBar = new ProgressBar(mContext, null,
- android.R.attr.progressBarStyleHorizontal);
-
// set Progress
int progress = 10;
- progressBar.setProgress(progress);
+ mProgressBarHorizontal.setProgress(progress);
// normal value
int max = progress + 1;
- progressBar.setMax(max);
- assertEquals(max, progressBar.getMax());
- assertEquals(progress, progressBar.getProgress());
+ mProgressBarHorizontal.setMax(max);
+ assertEquals(max, mProgressBarHorizontal.getMax());
+ assertEquals(progress, mProgressBarHorizontal.getProgress());
max = progress - 1;
- progressBar.setMax(max);
- assertEquals(max, progressBar.getMax());
- assertEquals(max, progressBar.getProgress());
+ mProgressBarHorizontal.setMax(max);
+ assertEquals(max, mProgressBarHorizontal.getMax());
+ assertEquals(max, mProgressBarHorizontal.getProgress());
// exceptional values
- progressBar.setMax(-1);
- assertEquals(0, progressBar.getMax());
- assertEquals(0, progressBar.getProgress());
+ mProgressBarHorizontal.setMax(-1);
+ assertEquals(0, mProgressBarHorizontal.getMax());
+ assertEquals(0, mProgressBarHorizontal.getProgress());
- progressBar.setMax(Integer.MAX_VALUE);
- assertEquals(Integer.MAX_VALUE, progressBar.getMax());
- assertEquals(0, progressBar.getProgress());
+ mProgressBarHorizontal.setMax(Integer.MAX_VALUE);
+ assertEquals(Integer.MAX_VALUE, mProgressBarHorizontal.getMax());
+ assertEquals(0, mProgressBarHorizontal.getProgress());
}
public void testOnDraw() {
// Do not test, it's controlled by View. Implementation details
}
+ @UiThreadTest
public void testProgressTint() {
- LayoutInflater inflater = LayoutInflater.from(mContext);
- View layout = inflater.inflate(R.layout.progressbar_layout, null);
- ProgressBar inflatedView = (ProgressBar) layout.findViewById(R.id.progress_tint);
+ ProgressBar tintedProgressBar = (ProgressBar) mActivity.findViewById(R.id.progress_tint);
assertEquals("Progress tint inflated correctly",
- Color.WHITE, inflatedView.getProgressTintList().getDefaultColor());
+ Color.WHITE, tintedProgressBar.getProgressTintList().getDefaultColor());
assertEquals("Progress tint mode inflated correctly",
- PorterDuff.Mode.SRC_OVER, inflatedView.getProgressTintMode());
+ PorterDuff.Mode.SRC_OVER, tintedProgressBar.getProgressTintMode());
assertEquals("Progress background tint inflated correctly",
- Color.WHITE, inflatedView.getProgressBackgroundTintList().getDefaultColor());
+ Color.WHITE, tintedProgressBar.getProgressBackgroundTintList().getDefaultColor());
assertEquals("Progress background tint mode inflated correctly",
- PorterDuff.Mode.SRC_OVER, inflatedView.getProgressBackgroundTintMode());
+ PorterDuff.Mode.SRC_OVER, tintedProgressBar.getProgressBackgroundTintMode());
assertEquals("Secondary progress tint inflated correctly",
- Color.WHITE, inflatedView.getSecondaryProgressTintList().getDefaultColor());
+ Color.WHITE, tintedProgressBar.getSecondaryProgressTintList().getDefaultColor());
assertEquals("Secondary progress tint mode inflated correctly",
- PorterDuff.Mode.SRC_OVER, inflatedView.getSecondaryProgressTintMode());
+ PorterDuff.Mode.SRC_OVER, tintedProgressBar.getSecondaryProgressTintMode());
- MockDrawable progress = new MockDrawable();
- ProgressBar view = new ProgressBar(mContext);
+ Drawable mockProgressDrawable = spy(new ColorDrawable(Color.BLACK));
- view.setProgressDrawable(progress);
- assertFalse("No progress tint applied by default", progress.hasCalledSetTint());
+ mProgressBar.setProgressDrawable(mockProgressDrawable);
+ // No progress tint applied by default
+ verify(mockProgressDrawable, never()).setTintList(any(ColorStateList.class));
- view.setProgressBackgroundTintList(ColorStateList.valueOf(Color.WHITE));
- assertFalse("Progress background tint not applied when layer missing",
- progress.hasCalledSetTint());
+ mProgressBar.setProgressBackgroundTintList(ColorStateList.valueOf(Color.WHITE));
+ // Progress background tint not applied when layer missing
+ verify(mockProgressDrawable, never()).setTintList(any(ColorStateList.class));
- view.setSecondaryProgressTintList(ColorStateList.valueOf(Color.WHITE));
- assertFalse("Secondary progress tint not applied when layer missing",
- progress.hasCalledSetTint());
+ mProgressBar.setSecondaryProgressTintList(ColorStateList.valueOf(Color.WHITE));
+ // Secondary progress tint not applied when layer missing
+ verify(mockProgressDrawable, never()).setTintList(any(ColorStateList.class));
- view.setProgressTintList(ColorStateList.valueOf(Color.WHITE));
- assertTrue("Progress tint applied when setProgressTintList() called after setProgress()",
- progress.hasCalledSetTint());
+ mProgressBar.setProgressTintList(ColorStateList.valueOf(Color.WHITE));
+ // Progress tint applied when setProgressTintList() called after setProgress()
+ verify(mockProgressDrawable, times(1)).setTintList(TestUtils.colorStateListOf(Color.WHITE));
- progress.reset();
- view.setProgressDrawable(null);
- view.setProgressDrawable(progress);
- assertTrue("Progress tint applied when setProgressTintList() called before setProgress()",
- progress.hasCalledSetTint());
+ mProgressBar.setProgressBackgroundTintMode(PorterDuff.Mode.DST_OVER);
+ assertEquals(PorterDuff.Mode.DST_OVER, mProgressBar.getProgressBackgroundTintMode());
+
+ mProgressBar.setSecondaryProgressTintMode(PorterDuff.Mode.DST_IN);
+ assertEquals(PorterDuff.Mode.DST_IN, mProgressBar.getSecondaryProgressTintMode());
+
+ mProgressBar.setProgressTintMode(PorterDuff.Mode.DST_ATOP);
+ assertEquals(PorterDuff.Mode.DST_ATOP, mProgressBar.getProgressTintMode());
+
+ reset(mockProgressDrawable);
+ mProgressBar.setProgressDrawable(null);
+ mProgressBar.setProgressDrawable(mockProgressDrawable);
+ // Progress tint applied when setProgressTintList() called before setProgress()
+ verify(mockProgressDrawable, times(1)).setTintList(TestUtils.colorStateListOf(Color.WHITE));
}
+ @UiThreadTest
public void testIndeterminateTint() {
- LayoutInflater inflater = LayoutInflater.from(mContext);
- View layout = inflater.inflate(R.layout.progressbar_layout, null);
- ProgressBar inflatedView = (ProgressBar) layout.findViewById(R.id.indeterminate_tint);
+ ProgressBar tintedProgressBar =
+ (ProgressBar) mActivity.findViewById(R.id.indeterminate_tint);
assertEquals("Indeterminate tint inflated correctly",
- Color.WHITE, inflatedView.getIndeterminateTintList().getDefaultColor());
+ Color.WHITE, tintedProgressBar.getIndeterminateTintList().getDefaultColor());
assertEquals("Indeterminate tint mode inflated correctly",
- PorterDuff.Mode.SRC_OVER, inflatedView.getIndeterminateTintMode());
+ PorterDuff.Mode.SRC_OVER, tintedProgressBar.getIndeterminateTintMode());
- MockDrawable indeterminate = new MockDrawable();
- ProgressBar view = new ProgressBar(mContext);
+ Drawable mockIndeterminateDrawable = spy(new ColorDrawable(Color.MAGENTA));
- view.setIndeterminateDrawable(indeterminate);
- assertFalse("No indeterminate tint applied by default", indeterminate.hasCalledSetTint());
+ mProgressBar.setIndeterminateDrawable(mockIndeterminateDrawable);
+ // No indeterminate tint applied by default
+ verify(mockIndeterminateDrawable, never()).setTintList(any(ColorStateList.class));
- view.setIndeterminateTintList(ColorStateList.valueOf(Color.WHITE));
- assertTrue("Indeterminate tint applied when setIndeterminateTintList() called after "
- + "setIndeterminate()", indeterminate.hasCalledSetTint());
+ mProgressBar.setIndeterminateTintList(ColorStateList.valueOf(Color.RED));
+ // Indeterminate tint applied when setIndeterminateTintList() called after
+ // setIndeterminate()
+ verify(mockIndeterminateDrawable, times(1)).setTintList(
+ TestUtils.colorStateListOf(Color.RED));
- indeterminate.reset();
- view.setIndeterminateDrawable(null);
- view.setIndeterminateDrawable(indeterminate);
- assertTrue("Indeterminate tint applied when setIndeterminateTintList() called before "
- + "setIndeterminate()", indeterminate.hasCalledSetTint());
- }
+ mProgressBar.setIndeterminateTintMode(PorterDuff.Mode.LIGHTEN);
+ assertEquals(PorterDuff.Mode.LIGHTEN, mProgressBar.getIndeterminateTintMode());
- private class MockDrawable extends Drawable {
- private boolean mCalledDraw = false;
- private boolean mCalledSetTint = false;
-
- @Override
- public void draw(Canvas canvas) {
- mCalledDraw = true;
- }
-
- @Override
- public int getOpacity() {
- return 0;
- }
-
- @Override
- public void setAlpha(int alpha) {
- }
-
- @Override
- public void setColorFilter(ColorFilter cf) {
- }
-
- @Override
- public void setTintList(ColorStateList tint) {
- super.setTintList(tint);
- mCalledSetTint = true;
- }
-
- public boolean hasCalledSetTint() {
- return mCalledSetTint;
- }
-
- public boolean hasCalledDraw() {
- return mCalledDraw;
- }
-
- public void reset() {
- mCalledDraw = false;
- mCalledSetTint = false;
- }
-
+ reset(mockIndeterminateDrawable);
+ mProgressBar.setIndeterminateDrawable(null);
+ mProgressBar.setIndeterminateDrawable(mockIndeterminateDrawable);
+ // Indeterminate tint applied when setIndeterminateTintList() called before
+ // setIndeterminate()
+ verify(mockIndeterminateDrawable, times(1)).setTintList(
+ TestUtils.colorStateListOf(Color.RED));
}
public void testOnMeasure() {
@@ -432,13 +469,14 @@
// onSizeChanged() is implementation details, do NOT test
}
+ @UiThreadTest
public void testVerifyDrawable() {
- MockProgressBar mockProgressBar = new MockProgressBar(mContext);
+ MockProgressBar mockProgressBar = new MockProgressBar(mActivity);
assertTrue(mockProgressBar.verifyDrawable(null));
- Drawable d1 = mContext.getResources().getDrawable(R.drawable.blue);
- Drawable d2 = mContext.getResources().getDrawable(R.drawable.red);
- Drawable d3 = mContext.getResources().getDrawable(R.drawable.yellow);
+ Drawable d1 = mActivity.getResources().getDrawable(R.drawable.blue);
+ Drawable d2 = mActivity.getResources().getDrawable(R.drawable.red);
+ Drawable d3 = mActivity.getResources().getDrawable(R.drawable.yellow);
mockProgressBar.setBackgroundDrawable(d1);
assertTrue(mockProgressBar.verifyDrawable(null));
@@ -463,39 +501,33 @@
// drawableStateChanged() is implementation details, do NOT test
}
+ @UiThreadTest
public void testOnSaveAndRestoreInstanceState() {
- ProgressBar progressBar = new ProgressBar(mContext, null,
- android.R.attr.progressBarStyleHorizontal);
int oldProgress = 1;
- int oldSecondaryProgress = progressBar.getMax() - 1;
- progressBar.setProgress(oldProgress);
- progressBar.setSecondaryProgress(oldSecondaryProgress);
- assertEquals(oldProgress, progressBar.getProgress());
- assertEquals(oldSecondaryProgress, progressBar.getSecondaryProgress());
+ int oldSecondaryProgress = mProgressBarHorizontal.getMax() - 1;
+ mProgressBarHorizontal.setProgress(oldProgress);
+ mProgressBarHorizontal.setSecondaryProgress(oldSecondaryProgress);
+ assertEquals(oldProgress, mProgressBarHorizontal.getProgress());
+ assertEquals(oldSecondaryProgress, mProgressBarHorizontal.getSecondaryProgress());
- Parcelable state = progressBar.onSaveInstanceState();
+ Parcelable state = mProgressBarHorizontal.onSaveInstanceState();
int newProgress = 2;
- int newSecondaryProgress = progressBar.getMax() - 2;
- progressBar.setProgress(newProgress);
- progressBar.setSecondaryProgress(newSecondaryProgress);
- assertEquals(newProgress, progressBar.getProgress());
- assertEquals(newSecondaryProgress, progressBar.getSecondaryProgress());
+ int newSecondaryProgress = mProgressBarHorizontal.getMax() - 2;
+ mProgressBarHorizontal.setProgress(newProgress);
+ mProgressBarHorizontal.setSecondaryProgress(newSecondaryProgress);
+ assertEquals(newProgress, mProgressBarHorizontal.getProgress());
+ assertEquals(newSecondaryProgress, mProgressBarHorizontal.getSecondaryProgress());
- progressBar.onRestoreInstanceState(state);
- assertEquals(oldProgress, progressBar.getProgress());
- assertEquals(oldSecondaryProgress, progressBar.getSecondaryProgress());
+ mProgressBarHorizontal.onRestoreInstanceState(state);
+ assertEquals(oldProgress, mProgressBarHorizontal.getProgress());
+ assertEquals(oldSecondaryProgress, mProgressBarHorizontal.getSecondaryProgress());
}
/*
* Mock class for ProgressBar to test protected methods
*/
private class MockProgressBar extends ProgressBar {
- private boolean mCalledInvalidate = false;
-
- /**
- * @param context
- */
public MockProgressBar(Context context) {
super(context);
}
@@ -504,44 +536,5 @@
protected boolean verifyDrawable(Drawable who) {
return super.verifyDrawable(who);
}
-
- @Override
- protected void onSizeChanged(int w, int h, int oldw, int oldh) {
- super.onSizeChanged(w, h, oldw, oldh);
- }
-
- @Override
- protected synchronized void onMeasure(int widthMeasureSpec,
- int heightMeasureSpec) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- }
-
- @Override
- protected synchronized void onDraw(Canvas canvas) {
- super.onDraw(canvas);
- }
-
- @Override
- protected void drawableStateChanged() {
- super.drawableStateChanged();
- }
-
- public void invalidate(int l, int t, int r, int b) {
- mCalledInvalidate = true;
- super.invalidate(l, t, r, b);
- }
-
- public void invalidate() {
- mCalledInvalidate = true;
- super.invalidate();
- }
-
- public boolean hasCalledInvalidate() {
- return mCalledInvalidate;
- }
-
- public void reset() {
- mCalledInvalidate = false;
- }
}
}
diff --git a/hostsidetests/services/activitymanager/app/src/android/server/app/SlowCreateActivity.java b/tests/tests/widget/src/android/widget/cts/RadioButtonCtsActivity.java
similarity index 67%
copy from hostsidetests/services/activitymanager/app/src/android/server/app/SlowCreateActivity.java
copy to tests/tests/widget/src/android/widget/cts/RadioButtonCtsActivity.java
index 481f5be..442d5f7 100644
--- a/hostsidetests/services/activitymanager/app/src/android/server/app/SlowCreateActivity.java
+++ b/tests/tests/widget/src/android/widget/cts/RadioButtonCtsActivity.java
@@ -14,17 +14,22 @@
* limitations under the License.
*/
-package android.server.app;
+package android.widget.cts;
import android.app.Activity;
import android.os.Bundle;
+import android.widget.RadioButton;
-public class SlowCreateActivity extends Activity {
+/**
+ * A minimal application for {@link RadioButton} test.
+ */
+public class RadioButtonCtsActivity extends Activity {
+ /**
+ * Called with the activity is first created.
+ */
@Override
- protected void onCreate(Bundle savedInstanceState) {
- try {
- Thread.sleep(2000);
- } catch(InterruptedException e) {}
+ public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ setContentView(R.layout.radiobutton_layout);
}
}
diff --git a/tests/tests/widget/src/android/widget/cts/RadioButtonTest.java b/tests/tests/widget/src/android/widget/cts/RadioButtonTest.java
index eb9387b..7884401 100644
--- a/tests/tests/widget/src/android/widget/cts/RadioButtonTest.java
+++ b/tests/tests/widget/src/android/widget/cts/RadioButtonTest.java
@@ -16,67 +16,167 @@
package android.widget.cts;
+import static org.mockito.Mockito.*;
+
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.cts.util.CtsTouchUtils;
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.text.TextUtils;
+import android.widget.RadioButton;
import android.widget.cts.R;
+@SmallTest
+public class RadioButtonTest extends ActivityInstrumentationTestCase2<RadioButtonCtsActivity> {
+ private Instrumentation mInstrumentation;
+ private Activity mActivity;
+ private RadioButton mRadioButton;
-import android.content.Context;
-import android.test.InstrumentationTestCase;
-import android.test.UiThreadTest;
-import android.util.AttributeSet;
-import android.widget.RadioButton;
-
-/**
- * Test {@link RadioButton}.
- */
-public class RadioButtonTest extends InstrumentationTestCase {
- private Context mContext;
+ public RadioButtonTest() {
+ super("android.widget.cts", RadioButtonCtsActivity.class);
+ }
@Override
protected void setUp() throws Exception {
super.setUp();
- mContext = getInstrumentation().getTargetContext();
+
+ mInstrumentation = getInstrumentation();
+ mActivity = getActivity();
+ mRadioButton = (RadioButton) mActivity.findViewById(R.id.radio_button);
}
public void testConstructor() {
- AttributeSet attrs = mContext.getResources().getLayout(R.layout.radiogroup_1);
- assertNotNull(attrs);
+ new RadioButton(mActivity);
+ new RadioButton(mActivity, null);
+ new RadioButton(mActivity, null, android.R.attr.radioButtonStyle);
+ new RadioButton(mActivity, null, 0,
+ android.R.style.Widget_DeviceDefault_CompoundButton_RadioButton);
+ new RadioButton(mActivity, null, 0,
+ android.R.style.Widget_DeviceDefault_Light_CompoundButton_RadioButton);
+ new RadioButton(mActivity, null, 0,
+ android.R.style.Widget_Material_CompoundButton_RadioButton);
+ new RadioButton(mActivity, null, 0,
+ android.R.style.Widget_Material_Light_CompoundButton_RadioButton);
- new RadioButton(mContext);
try {
new RadioButton(null);
fail("The constructor should throw NullPointerException when param Context is null.");
} catch (NullPointerException e) {
}
- new RadioButton(mContext, attrs);
try {
- new RadioButton(null, attrs);
+ new RadioButton(null, null);
fail("The constructor should throw NullPointerException when param Context is null.");
} catch (NullPointerException e) {
}
- new RadioButton(mContext, null);
-
- new RadioButton(mContext, attrs, 0);
try {
- new RadioButton(null, attrs, 0);
+ new RadioButton(null, null, 0);
fail("The constructor should throw NullPointerException when param Context is null.");
} catch (NullPointerException e) {
}
- new RadioButton(mContext, null, 0);
- new RadioButton(mContext, attrs, Integer.MAX_VALUE);
- new RadioButton(mContext, attrs, Integer.MIN_VALUE);
}
- @UiThreadTest
- public void testToggle() {
- RadioButton button = new RadioButton(mContext);
- assertFalse(button.isChecked());
+ public void testText() {
+ assertTrue(TextUtils.equals(
+ mActivity.getString(R.string.hello_world), mRadioButton.getText()));
- button.toggle();
- assertTrue(button.isChecked());
+ mInstrumentation.runOnMainSync(() -> mRadioButton.setText("new text"));
+ assertTrue(TextUtils.equals("new text", mRadioButton.getText()));
- // Can't be toggled if it is checked
- button.toggle();
- assertTrue(button.isChecked());
+ mInstrumentation.runOnMainSync(() -> mRadioButton.setText(R.string.text_name));
+ assertTrue(TextUtils.equals(
+ mActivity.getString(R.string.text_name), mRadioButton.getText()));
+ }
+
+ public void testAccessChecked() {
+ final RadioButton.OnCheckedChangeListener mockCheckedChangeListener =
+ mock(RadioButton.OnCheckedChangeListener.class);
+ mRadioButton.setOnCheckedChangeListener(mockCheckedChangeListener);
+ verifyZeroInteractions(mockCheckedChangeListener);
+
+ assertFalse(mRadioButton.isChecked());
+
+ // not checked -> not checked
+ mInstrumentation.runOnMainSync(() -> mRadioButton.setChecked(false));
+ verifyZeroInteractions(mockCheckedChangeListener);
+ assertFalse(mRadioButton.isChecked());
+
+ // not checked -> checked
+ mInstrumentation.runOnMainSync(() -> mRadioButton.setChecked(true));
+ verify(mockCheckedChangeListener, times(1)).onCheckedChanged(mRadioButton, true);
+ assertTrue(mRadioButton.isChecked());
+
+ // checked -> checked
+ mInstrumentation.runOnMainSync(() -> mRadioButton.setChecked(true));
+ verify(mockCheckedChangeListener, times(1)).onCheckedChanged(mRadioButton, true);
+ assertTrue(mRadioButton.isChecked());
+
+ // checked -> not checked
+ mInstrumentation.runOnMainSync(() -> mRadioButton.setChecked(false));
+ verify(mockCheckedChangeListener, times(1)).onCheckedChanged(mRadioButton, false);
+ assertFalse(mRadioButton.isChecked());
+
+ verifyNoMoreInteractions(mockCheckedChangeListener);
+ }
+
+ public void testToggleViaApi() {
+ final RadioButton.OnCheckedChangeListener mockCheckedChangeListener =
+ mock(RadioButton.OnCheckedChangeListener.class);
+ mRadioButton.setOnCheckedChangeListener(mockCheckedChangeListener);
+ verifyZeroInteractions(mockCheckedChangeListener);
+
+ assertFalse(mRadioButton.isChecked());
+
+ // toggle to checked
+ mInstrumentation.runOnMainSync(mRadioButton::toggle);
+ verify(mockCheckedChangeListener, times(1)).onCheckedChanged(mRadioButton, true);
+ assertTrue(mRadioButton.isChecked());
+
+ // try toggle to not checked - this should leave the radio button in checked state
+ mInstrumentation.runOnMainSync(mRadioButton::toggle);
+ assertTrue(mRadioButton.isChecked());
+
+ verifyNoMoreInteractions(mockCheckedChangeListener);
+ }
+
+ public void testToggleViaEmulatedTap() {
+ final RadioButton.OnCheckedChangeListener mockCheckedChangeListener =
+ mock(RadioButton.OnCheckedChangeListener.class);
+ mRadioButton.setOnCheckedChangeListener(mockCheckedChangeListener);
+ verifyZeroInteractions(mockCheckedChangeListener);
+
+ assertFalse(mRadioButton.isChecked());
+
+ // tap to checked
+ CtsTouchUtils.emulateTapOnViewCenter(mInstrumentation, mRadioButton);
+ verify(mockCheckedChangeListener, times(1)).onCheckedChanged(mRadioButton, true);
+ assertTrue(mRadioButton.isChecked());
+
+ // tap to not checked - this should leave the radio button in checked state
+ CtsTouchUtils.emulateTapOnViewCenter(mInstrumentation, mRadioButton);
+ assertTrue(mRadioButton.isChecked());
+
+ verifyNoMoreInteractions(mockCheckedChangeListener);
+ }
+
+ public void testToggleViaPerformClick() {
+ final RadioButton.OnCheckedChangeListener mockCheckedChangeListener =
+ mock(RadioButton.OnCheckedChangeListener.class);
+ mRadioButton.setOnCheckedChangeListener(mockCheckedChangeListener);
+ verifyZeroInteractions(mockCheckedChangeListener);
+
+ assertFalse(mRadioButton.isChecked());
+
+ // click to checked
+ mInstrumentation.runOnMainSync(mRadioButton::performClick);
+ verify(mockCheckedChangeListener, times(1)).onCheckedChanged(mRadioButton, true);
+ assertTrue(mRadioButton.isChecked());
+
+ // click to not checked - this should leave the radio button in checked state
+ mInstrumentation.runOnMainSync(mRadioButton::performClick);
+ assertTrue(mRadioButton.isChecked());
+
+ verifyNoMoreInteractions(mockCheckedChangeListener);
}
}
diff --git a/tests/tests/widget/src/android/widget/cts/RadioGroupTest.java b/tests/tests/widget/src/android/widget/cts/RadioGroupTest.java
index b2154e2..73b0996 100644
--- a/tests/tests/widget/src/android/widget/cts/RadioGroupTest.java
+++ b/tests/tests/widget/src/android/widget/cts/RadioGroupTest.java
@@ -16,12 +16,6 @@
package android.widget.cts;
-import android.widget.cts.R;
-
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
import android.content.Context;
import android.test.InstrumentationTestCase;
import android.test.UiThreadTest;
@@ -34,9 +28,13 @@
import android.widget.LinearLayout;
import android.widget.RadioButton;
import android.widget.RadioGroup;
-import android.widget.RelativeLayout;
import android.widget.RadioGroup.LayoutParams;
import android.widget.RadioGroup.OnCheckedChangeListener;
+import android.widget.RelativeLayout;
+import android.widget.cts.R;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import java.util.Vector;
diff --git a/tests/tests/widget/src/android/widget/cts/RadioGroup_LayoutParamsTest.java b/tests/tests/widget/src/android/widget/cts/RadioGroup_LayoutParamsTest.java
index 37cd632..eded48d 100644
--- a/tests/tests/widget/src/android/widget/cts/RadioGroup_LayoutParamsTest.java
+++ b/tests/tests/widget/src/android/widget/cts/RadioGroup_LayoutParamsTest.java
@@ -17,11 +17,6 @@
package android.widget.cts;
import android.R;
-
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
import android.content.Context;
import android.content.res.TypedArray;
import android.test.AndroidTestCase;
@@ -33,6 +28,9 @@
import android.widget.RadioGroup;
import android.widget.RadioGroup.LayoutParams;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
import java.io.IOException;
/**
diff --git a/tests/tests/widget/src/android/widget/cts/RatingBarCtsActivity.java b/tests/tests/widget/src/android/widget/cts/RatingBarCtsActivity.java
index 1a9cb40..aea46a0 100644
--- a/tests/tests/widget/src/android/widget/cts/RatingBarCtsActivity.java
+++ b/tests/tests/widget/src/android/widget/cts/RatingBarCtsActivity.java
@@ -18,7 +18,6 @@
import android.app.Activity;
import android.os.Bundle;
-
import android.widget.cts.R;
/**
diff --git a/tests/tests/widget/src/android/widget/cts/RatingBarTest.java b/tests/tests/widget/src/android/widget/cts/RatingBarTest.java
index 669e7a1..db4a4e6 100644
--- a/tests/tests/widget/src/android/widget/cts/RatingBarTest.java
+++ b/tests/tests/widget/src/android/widget/cts/RatingBarTest.java
@@ -16,20 +16,21 @@
package android.widget.cts;
-import android.widget.cts.R;
+import static org.mockito.Mockito.*;
-import android.content.Context;
+import android.app.Instrumentation;
import android.test.ActivityInstrumentationTestCase2;
-import android.test.UiThreadTest;
+import android.test.suitebuilder.annotation.SmallTest;
import android.widget.RatingBar;
-import android.widget.RatingBar.OnRatingBarChangeListener;
/**
* Test {@link RatingBar}.
*/
+@SmallTest
public class RatingBarTest extends ActivityInstrumentationTestCase2<RatingBarCtsActivity> {
- private Context mContext;
+ private Instrumentation mInstrumentation;
private RatingBarCtsActivity mActivity;
+ private RatingBar mRatingBar;
public RatingBarTest() {
super("android.widget.cts", RatingBarCtsActivity.class);
@@ -38,213 +39,137 @@
@Override
protected void setUp() throws Exception {
super.setUp();
+
+ mInstrumentation = getInstrumentation();
mActivity = getActivity();
- assertNotNull(mActivity);
- mContext = getInstrumentation().getContext();
+ mRatingBar = (RatingBar) mActivity.findViewById(R.id.ratingbar_constructor);
}
public void testConstructor() {
- new RatingBar(mContext, null, android.R.attr.ratingBarStyle);
-
- new RatingBar(mContext, null);
-
- new RatingBar(mContext);
-
- RatingBar ratingBar = (RatingBar) mActivity.findViewById(R.id.ratingbar_constructor);
- assertNotNull(ratingBar);
- assertFalse(ratingBar.isIndicator());
- assertEquals(50, ratingBar.getNumStars());
- assertEquals(1.2f, ratingBar.getRating());
- assertEquals(0.2f, ratingBar.getStepSize());
+ new RatingBar(mActivity);
+ new RatingBar(mActivity, null);
+ new RatingBar(mActivity, null, android.R.attr.ratingBarStyle);
+ new RatingBar(mActivity, null, 0, android.R.style.Widget_DeviceDefault_RatingBar);
+ new RatingBar(mActivity, null, 0, android.R.style.Widget_DeviceDefault_RatingBar_Indicator);
+ new RatingBar(mActivity, null, 0, android.R.style.Widget_DeviceDefault_RatingBar_Small);
+ new RatingBar(mActivity, null, 0, android.R.style.Widget_Material_RatingBar);
+ new RatingBar(mActivity, null, 0, android.R.style.Widget_Material_RatingBar_Indicator);
+ new RatingBar(mActivity, null, 0, android.R.style.Widget_Material_RatingBar_Small);
+ new RatingBar(mActivity, null, 0, android.R.style.Widget_Material_Light_RatingBar);
+ new RatingBar(mActivity, null, 0, android.R.style.Widget_Material_Light_RatingBar_Indicator);
+ new RatingBar(mActivity, null, 0, android.R.style.Widget_Material_Light_RatingBar_Small);
}
- @UiThreadTest
+ public void testAttributesFromLayout() {
+ assertFalse(mRatingBar.isIndicator());
+ assertEquals(50, mRatingBar.getNumStars());
+ assertEquals(1.2f, mRatingBar.getRating());
+ assertEquals(0.2f, mRatingBar.getStepSize());
+ }
+
public void testAccessOnRatingBarChangeListener() {
- RatingBar ratingBar = (RatingBar)mActivity.findViewById(R.id.ratingbar_constructor);
+ final RatingBar.OnRatingBarChangeListener listener =
+ mock(RatingBar.OnRatingBarChangeListener.class);
+ mRatingBar.setOnRatingBarChangeListener(listener);
+ assertSame(listener, mRatingBar.getOnRatingBarChangeListener());
+ verifyZeroInteractions(listener);
- MockOnRatingBarChangeListener listener = new MockOnRatingBarChangeListener();
-
- // set OnRatingBarChangeListener
// normal value
- ratingBar.setOnRatingBarChangeListener(listener);
- assertSame(listener, ratingBar.getOnRatingBarChangeListener());
- ratingBar.setRating(2.2f);
- assertTrue(listener.hasCalledOnRatingChanged());
+ mInstrumentation.runOnMainSync(() -> mRatingBar.setRating(2.2f));
+ verify(listener, times(1)).onRatingChanged(mRatingBar, 2.2f, false);
// exceptional value
- listener.reset();
- ratingBar.setOnRatingBarChangeListener(null);
- assertNull(ratingBar.getOnRatingBarChangeListener());
- ratingBar.setRating(1.2f);
- assertFalse(listener.hasCalledOnRatingChanged());
+ mRatingBar.setOnRatingBarChangeListener(null);
+ assertNull(mRatingBar.getOnRatingBarChangeListener());
+ mRatingBar.setRating(1.2f);
+ verifyNoMoreInteractions(listener);
}
public void testAccessIndicator() {
- RatingBar ratingBar = new RatingBar(mContext);
+ mInstrumentation.runOnMainSync(() -> mRatingBar.setIsIndicator(true));
+ assertTrue(mRatingBar.isIndicator());
- ratingBar.setIsIndicator(true);
- assertTrue(ratingBar.isIndicator());
-
- ratingBar.setIsIndicator(false);
- assertFalse(ratingBar.isIndicator());
+ mInstrumentation.runOnMainSync(() -> mRatingBar.setIsIndicator(false));
+ assertFalse(mRatingBar.isIndicator());
}
public void testAccessNumStars() {
- MockRatingBar mockRatingBar = new MockRatingBar(mContext);
-
// set NumStars
// normal value
- assertFalse(mockRatingBar.hasCalledRequestLayout());
- mockRatingBar.setNumStars(20);
- assertTrue(mockRatingBar.hasCalledRequestLayout());
- assertEquals(20, mockRatingBar.getNumStars());
+ mInstrumentation.runOnMainSync(() -> mRatingBar.setNumStars(20));
+ assertEquals(20, mRatingBar.getNumStars());
- // exceptional value
- mockRatingBar.reset();
- mockRatingBar.setNumStars(-10);
- assertFalse(mockRatingBar.hasCalledRequestLayout());
- assertEquals(20, mockRatingBar.getNumStars());
+ // invalid value - the currently set one stays
+ mInstrumentation.runOnMainSync(() -> mRatingBar.setNumStars(-10));
+ assertEquals(20, mRatingBar.getNumStars());
- mockRatingBar.reset();
- mockRatingBar.setNumStars(Integer.MAX_VALUE);
- assertTrue(mockRatingBar.hasCalledRequestLayout());
- assertEquals(Integer.MAX_VALUE, mockRatingBar.getNumStars());
+ mInstrumentation.runOnMainSync(() -> mRatingBar.setNumStars(Integer.MAX_VALUE));
+ assertEquals(Integer.MAX_VALUE, mRatingBar.getNumStars());
}
public void testAccessRating() {
- RatingBar ratingBar = new RatingBar(mContext);
-
// set Rating
// normal value
- ratingBar.setRating(2.0f);
- assertEquals(2.0f, ratingBar.getRating());
+ mInstrumentation.runOnMainSync(() -> mRatingBar.setRating(2.0f));
+ assertEquals(2.0f, mRatingBar.getRating());
// exceptional value
- ratingBar.setRating(-2.0f);
- assertEquals(0f, ratingBar.getRating());
+ mInstrumentation.runOnMainSync(() -> mRatingBar.setRating(-2.0f));
+ assertEquals(0f, mRatingBar.getRating());
- ratingBar.setRating(Float.MAX_VALUE);
- assertEquals((float) ratingBar.getNumStars(), ratingBar.getRating());
+ mInstrumentation.runOnMainSync(() -> mRatingBar.setRating(Float.MAX_VALUE));
+ assertEquals((float) mRatingBar.getNumStars(), mRatingBar.getRating());
}
public void testSetMax() {
- RatingBar ratingBar = new RatingBar(mContext);
-
// normal value
- ratingBar.setMax(10);
- assertEquals(10, ratingBar.getMax());
+ mInstrumentation.runOnMainSync(() -> mRatingBar.setMax(10));
+ assertEquals(10, mRatingBar.getMax());
- ratingBar.setProgress(10);
+ mInstrumentation.runOnMainSync(() -> mRatingBar.setProgress(10));
// exceptional values
- ratingBar.setMax(-10);
- assertEquals(10, ratingBar.getMax());
- assertEquals(10, ratingBar.getProgress());
+ mInstrumentation.runOnMainSync(() -> mRatingBar.setMax(-10));
+ assertEquals(10, mRatingBar.getMax());
+ assertEquals(10, mRatingBar.getProgress());
- ratingBar.setMax(Integer.MAX_VALUE);
- assertEquals(Integer.MAX_VALUE, ratingBar.getMax());
+ mInstrumentation.runOnMainSync(() -> mRatingBar.setMax(Integer.MAX_VALUE));
+ assertEquals(Integer.MAX_VALUE, mRatingBar.getMax());
}
public void testAccessStepSize() {
- RatingBar ratingBar = new RatingBar(mContext);
-
// normal value
- ratingBar.setStepSize(1.5f);
- float expectedMax = ratingBar.getNumStars() / ratingBar.getStepSize();
- float expectedProgress = expectedMax / ratingBar.getMax() * ratingBar.getProgress();
- assertEquals((int) expectedMax, ratingBar.getMax());
- assertEquals((int) expectedProgress, ratingBar.getProgress());
- assertEquals((float) ratingBar.getNumStars() / (int) (ratingBar.getNumStars() / 1.5f),
- ratingBar.getStepSize());
+ mInstrumentation.runOnMainSync(() -> mRatingBar.setStepSize(1.5f));
+ final float expectedMax = mRatingBar.getNumStars() / mRatingBar.getStepSize();
+ final float expectedProgress = expectedMax / mRatingBar.getMax() * mRatingBar.getProgress();
+ assertEquals((int) expectedMax, mRatingBar.getMax());
+ assertEquals((int) expectedProgress, mRatingBar.getProgress());
+ assertEquals((float) mRatingBar.getNumStars() / (int) (mRatingBar.getNumStars() / 1.5f),
+ mRatingBar.getStepSize());
- int currentMax = ratingBar.getMax();
- int currentProgress = ratingBar.getProgress();
- float currentStepSize = ratingBar.getStepSize();
+ final int currentMax = mRatingBar.getMax();
+ final int currentProgress = mRatingBar.getProgress();
+ final float currentStepSize = mRatingBar.getStepSize();
// exceptional value
- ratingBar.setStepSize(-1.5f);
- assertEquals(currentMax, ratingBar.getMax());
- assertEquals(currentProgress, ratingBar.getProgress());
- assertEquals(currentStepSize, ratingBar.getStepSize());
+ mInstrumentation.runOnMainSync(() -> mRatingBar.setStepSize(-1.5f));
+ assertEquals(currentMax, mRatingBar.getMax());
+ assertEquals(currentProgress, mRatingBar.getProgress());
+ assertEquals(currentStepSize, mRatingBar.getStepSize());
- ratingBar.setStepSize(0f);
- assertEquals(currentMax, ratingBar.getMax());
- assertEquals(currentProgress, ratingBar.getProgress());
- assertEquals(currentStepSize, ratingBar.getStepSize());
+ mInstrumentation.runOnMainSync(() -> mRatingBar.setStepSize(0f));
+ assertEquals(currentMax, mRatingBar.getMax());
+ assertEquals(currentProgress, mRatingBar.getProgress());
+ assertEquals(currentStepSize, mRatingBar.getStepSize());
- ratingBar.setStepSize(ratingBar.getNumStars() + 0.1f);
- assertEquals(currentMax, ratingBar.getMax());
- assertEquals(currentProgress, ratingBar.getProgress());
- assertEquals(currentStepSize, ratingBar.getStepSize());
+ mInstrumentation.runOnMainSync(
+ () -> mRatingBar.setStepSize(mRatingBar.getNumStars() + 0.1f));
+ assertEquals(currentMax, mRatingBar.getMax());
+ assertEquals(currentProgress, mRatingBar.getProgress());
+ assertEquals(currentStepSize, mRatingBar.getStepSize());
- ratingBar.setStepSize(Float.MAX_VALUE);
- assertEquals(currentMax, ratingBar.getMax());
- assertEquals(currentProgress, ratingBar.getProgress());
- assertEquals(currentStepSize, ratingBar.getStepSize());
- }
-
- /**
- * The listener interface for receiving OnRatingBarChangeListener events.
- * The class that is interested in processing a OnRatingBarChangeListener
- * event implements this interface, and the object created with that class
- * is registered with a component using the component's
- * <code>setOnRatingBarChangeListener<code> method. When
- * the OnRatingBarChangeListener event occurs, that object's appropriate
- * method is invoked.
- */
- private class MockOnRatingBarChangeListener implements OnRatingBarChangeListener {
- private boolean mCalledOnRatingChanged = false;
-
- boolean hasCalledOnRatingChanged() {
- return mCalledOnRatingChanged;
- }
-
- /*
- * (non-Javadoc)
- * @see android.widget.RadioGroup.OnRatingBarChangeListener#onRatingChanged
- * (RatingBar ratingBar, float rating, boolean fromTouch)
- */
- public void onRatingChanged(RatingBar ratingBar, float rating, boolean fromTouch) {
- mCalledOnRatingChanged = true;
- }
-
- public void reset() {
- mCalledOnRatingChanged = false;
- }
- }
-
- /*
- * Mock class for ProgressBar to test protected methods
- */
- private class MockRatingBar extends RatingBar {
- public MockRatingBar(Context context) {
- super(context);
- }
-
- private boolean mCalledOnMeasure = false;
- private boolean mCalledRequestLayout = false;
-
- public boolean hasCalledOnMeasure() {
- return mCalledOnMeasure;
- }
-
- @Override
- protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- mCalledOnMeasure = true;
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- }
-
- public void requestLayout() {
- mCalledRequestLayout = true;
- super.requestLayout();
- }
-
- public boolean hasCalledRequestLayout() {
- return mCalledRequestLayout;
- }
-
- public void reset() {
- mCalledOnMeasure = false;
- mCalledRequestLayout = false;
- }
+ mInstrumentation.runOnMainSync(() -> mRatingBar.setStepSize(Float.MAX_VALUE));
+ assertEquals(currentMax, mRatingBar.getMax());
+ assertEquals(currentProgress, mRatingBar.getProgress());
+ assertEquals(currentStepSize, mRatingBar.getStepSize());
}
}
diff --git a/tests/tests/widget/src/android/widget/cts/RelativeLayoutCtsActivity.java b/tests/tests/widget/src/android/widget/cts/RelativeLayoutCtsActivity.java
index c394ca4..15d5924 100644
--- a/tests/tests/widget/src/android/widget/cts/RelativeLayoutCtsActivity.java
+++ b/tests/tests/widget/src/android/widget/cts/RelativeLayoutCtsActivity.java
@@ -16,10 +16,9 @@
package android.widget.cts;
-import android.widget.cts.R;
-
import android.app.Activity;
import android.os.Bundle;
+import android.widget.cts.R;
/**
* A minimal application for RelativeLayout test.
diff --git a/tests/tests/widget/src/android/widget/cts/RelativeLayoutTest.java b/tests/tests/widget/src/android/widget/cts/RelativeLayoutTest.java
index 2f3be6f..db7f04c8 100644
--- a/tests/tests/widget/src/android/widget/cts/RelativeLayoutTest.java
+++ b/tests/tests/widget/src/android/widget/cts/RelativeLayoutTest.java
@@ -16,12 +16,8 @@
package android.widget.cts;
-import android.widget.cts.R;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
import android.app.Activity;
+import android.app.Instrumentation;
import android.content.Context;
import android.content.res.XmlResourceParser;
import android.test.ActivityInstrumentationTestCase2;
@@ -34,8 +30,12 @@
import android.view.ViewGroup.LayoutParams;
import android.widget.AbsListView;
import android.widget.RelativeLayout;
+import android.widget.cts.R;
import android.widget.cts.util.XmlUtils;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
import java.io.IOException;
/**
@@ -43,6 +43,7 @@
*/
public class RelativeLayoutTest extends
ActivityInstrumentationTestCase2<RelativeLayoutCtsActivity> {
+ private Instrumentation mInstrumentation;
private Activity mActivity;
public RelativeLayoutTest() {
@@ -52,6 +53,8 @@
@Override
protected void setUp() throws Exception {
super.setUp();
+
+ mInstrumentation = getInstrumentation();
mActivity = getActivity();
}
@@ -86,27 +89,19 @@
ViewAsserts.assertRightAligned(relativeLayout, view13);
relativeLayout.setIgnoreGravity(R.id.relative_view13);
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- relativeLayout.requestLayout();
- }
- });
- getInstrumentation().waitForIdleSync();
+ mInstrumentation.runOnMainSync(relativeLayout::requestLayout);
+ mInstrumentation.waitForIdleSync();
ViewAsserts.assertRightAligned(relativeLayout, view12);
ViewAsserts.assertLeftAligned(relativeLayout, view13);
relativeLayout.setIgnoreGravity(0);
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- relativeLayout.requestLayout();
- }
- });
- getInstrumentation().waitForIdleSync();
+ mInstrumentation.runOnMainSync(relativeLayout::requestLayout);
+ mInstrumentation.waitForIdleSync();
ViewAsserts.assertRightAligned(relativeLayout, view12);
ViewAsserts.assertRightAligned(relativeLayout, view13);
}
- public void testSetGravity() {
+ public void testAccessGravity() {
final RelativeLayout relativeLayout = (RelativeLayout) mActivity.findViewById(
R.id.relative_sublayout_gravity);
@@ -120,48 +115,38 @@
assertEquals(view11.getTop(), view10.getBottom());
// -- BOTTOM && RIGHT
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- relativeLayout.setGravity(Gravity.BOTTOM | Gravity.RIGHT);
- }
- });
- getInstrumentation().waitForIdleSync();
+ mInstrumentation.runOnMainSync(
+ () -> relativeLayout.setGravity(Gravity.BOTTOM | Gravity.RIGHT));
+ mInstrumentation.waitForIdleSync();
+ assertEquals(Gravity.BOTTOM | Gravity.RIGHT, relativeLayout.getGravity());
ViewAsserts.assertRightAligned(relativeLayout, view10);
assertEquals(view11.getTop(), view10.getBottom());
ViewAsserts.assertRightAligned(relativeLayout, view11);
ViewAsserts.assertBottomAligned(relativeLayout, view11);
// -- BOTTOM
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- relativeLayout.setGravity(Gravity.BOTTOM);
- }
- });
- getInstrumentation().waitForIdleSync();
+ mInstrumentation.runOnMainSync(() -> relativeLayout.setGravity(Gravity.BOTTOM));
+ mInstrumentation.waitForIdleSync();
+ assertEquals(Gravity.BOTTOM | Gravity.START, relativeLayout.getGravity());
ViewAsserts.assertLeftAligned(relativeLayout, view10);
assertEquals(view11.getTop(), view10.getBottom());
ViewAsserts.assertLeftAligned(relativeLayout, view11);
ViewAsserts.assertBottomAligned(relativeLayout, view11);
// CENTER_HORIZONTAL
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- relativeLayout.setGravity(Gravity.CENTER_HORIZONTAL);
- }
- });
- getInstrumentation().waitForIdleSync();
+ mInstrumentation.runOnMainSync(() -> relativeLayout.setGravity(Gravity.CENTER_HORIZONTAL));
+ mInstrumentation.waitForIdleSync();
+ assertEquals(Gravity.CENTER_HORIZONTAL | Gravity.TOP,
+ relativeLayout.getGravity());
ViewAsserts.assertHorizontalCenterAligned(relativeLayout, view10);
ViewAsserts.assertTopAligned(relativeLayout, view10);
ViewAsserts.assertHorizontalCenterAligned(relativeLayout, view11);
assertEquals(view11.getTop(), view10.getBottom());
// CENTER_VERTICAL
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- relativeLayout.setGravity(Gravity.CENTER_VERTICAL);
- }
- });
- getInstrumentation().waitForIdleSync();
+ mInstrumentation.runOnMainSync(() -> relativeLayout.setGravity(Gravity.CENTER_VERTICAL));
+ mInstrumentation.waitForIdleSync();
+ assertEquals(Gravity.CENTER_VERTICAL | Gravity.START, relativeLayout.getGravity());
ViewAsserts.assertLeftAligned(relativeLayout, view10);
int topSpace = view10.getTop();
int bottomSpace = relativeLayout.getHeight() - view11.getBottom();
@@ -184,24 +169,20 @@
assertEquals(view11.getTop(), view10.getBottom());
// RIGHT
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- relativeLayout.setHorizontalGravity(Gravity.RIGHT);
- }
- });
- getInstrumentation().waitForIdleSync();
+ mInstrumentation.runOnMainSync(() -> relativeLayout.setHorizontalGravity(Gravity.RIGHT));
+ mInstrumentation.waitForIdleSync();
+ assertEquals(Gravity.RIGHT, Gravity.HORIZONTAL_GRAVITY_MASK & relativeLayout.getGravity());
ViewAsserts.assertRightAligned(relativeLayout, view10);
ViewAsserts.assertTopAligned(relativeLayout, view10);
ViewAsserts.assertRightAligned(relativeLayout, view11);
assertEquals(view11.getTop(), view10.getBottom());
// CENTER_HORIZONTAL
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- relativeLayout.setHorizontalGravity(Gravity.CENTER_HORIZONTAL);
- }
- });
- getInstrumentation().waitForIdleSync();
+ mInstrumentation.runOnMainSync(
+ () -> relativeLayout.setHorizontalGravity(Gravity.CENTER_HORIZONTAL));
+ mInstrumentation.waitForIdleSync();
+ assertEquals(Gravity.CENTER_HORIZONTAL,
+ Gravity.HORIZONTAL_GRAVITY_MASK & relativeLayout.getGravity());
ViewAsserts.assertHorizontalCenterAligned(relativeLayout, view10);
ViewAsserts.assertTopAligned(relativeLayout, view10);
ViewAsserts.assertHorizontalCenterAligned(relativeLayout, view11);
@@ -222,24 +203,20 @@
assertEquals(view11.getTop(), view10.getBottom());
// BOTTOM
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- relativeLayout.setVerticalGravity(Gravity.BOTTOM);
- }
- });
- getInstrumentation().waitForIdleSync();
+ mInstrumentation.runOnMainSync(() -> relativeLayout.setVerticalGravity(Gravity.BOTTOM));
+ mInstrumentation.waitForIdleSync();
+ assertEquals(Gravity.BOTTOM, Gravity.VERTICAL_GRAVITY_MASK & relativeLayout.getGravity());
ViewAsserts.assertLeftAligned(relativeLayout, view10);
assertEquals(view11.getTop(), view10.getBottom());
ViewAsserts.assertLeftAligned(relativeLayout, view11);
ViewAsserts.assertBottomAligned(relativeLayout, view11);
// CENTER_VERTICAL
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- relativeLayout.setVerticalGravity(Gravity.CENTER_VERTICAL);
- }
- });
- getInstrumentation().waitForIdleSync();
+ mInstrumentation.runOnMainSync(
+ () -> relativeLayout.setVerticalGravity(Gravity.CENTER_VERTICAL));
+ mInstrumentation.waitForIdleSync();
+ assertEquals(Gravity.CENTER_VERTICAL,
+ Gravity.VERTICAL_GRAVITY_MASK & relativeLayout.getGravity());
ViewAsserts.assertLeftAligned(relativeLayout, view10);
int topSpace = view10.getTop();
int bottomSpace = relativeLayout.getHeight() - view11.getBottom();
@@ -352,12 +329,9 @@
* Tests to prevent regressions in baseline alignment.
*/
public void testBaselineAlignment() {
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- mActivity.setContentView(R.layout.relative_layout_baseline);
- }
- });
- getInstrumentation().waitForIdleSync();
+ mInstrumentation.runOnMainSync(
+ () -> mActivity.setContentView(R.layout.relative_layout_baseline));
+ mInstrumentation.waitForIdleSync();
View button = mActivity.findViewById(R.id.button1);
assertTrue(button.getHeight() > 0);
diff --git a/tests/tests/widget/src/android/widget/cts/RelativeLayout_LayoutParamsTest.java b/tests/tests/widget/src/android/widget/cts/RelativeLayout_LayoutParamsTest.java
index 5706781..30f07da 100644
--- a/tests/tests/widget/src/android/widget/cts/RelativeLayout_LayoutParamsTest.java
+++ b/tests/tests/widget/src/android/widget/cts/RelativeLayout_LayoutParamsTest.java
@@ -16,26 +16,40 @@
package android.widget.cts;
-import android.widget.cts.R;
-
+import android.app.Activity;
+import android.content.res.XmlResourceParser;
import android.test.ActivityInstrumentationTestCase2;
import android.test.ViewAsserts;
import android.util.LayoutDirection;
import android.view.View;
+import android.view.ViewGroup;
import android.view.ViewGroup.MarginLayoutParams;
import android.widget.RelativeLayout;
+import android.widget.cts.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
/**
* Test {@link RelativeLayout.LayoutParams}.
*/
public class RelativeLayout_LayoutParamsTest extends
ActivityInstrumentationTestCase2<RelativeLayoutCtsActivity> {
+ private Activity mActivity;
public RelativeLayout_LayoutParamsTest() {
super("android.widget.cts", RelativeLayoutCtsActivity.class);
}
- public void testConstructor() {
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ mActivity = getActivity();
+ }
+
+ public void testConstructor() throws XmlPullParserException, IOException {
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(200, 300);
assertEquals(200, layoutParams.width);
@@ -46,22 +60,32 @@
assertEquals(200, layoutParams.width);
assertEquals(300, layoutParams.height);
+ ViewGroup.LayoutParams tempViewGroupLayoutParams = new ViewGroup.LayoutParams(300, 400);
+ layoutParams = new RelativeLayout.LayoutParams(tempViewGroupLayoutParams);
+ assertEquals(300, layoutParams.width);
+ assertEquals(400, layoutParams.height);
+
MarginLayoutParams tempMarginLayoutParams = new MarginLayoutParams(400, 500);
layoutParams = new RelativeLayout.LayoutParams(tempMarginLayoutParams);
assertEquals(400, layoutParams.width);
assertEquals(500, layoutParams.height);
+ XmlResourceParser p = mActivity.getResources().getLayout(R.layout.relative_layout);
+ XmlUtils.beginDocument(p, "RelativeLayout");
+ layoutParams = new RelativeLayout.LayoutParams(mActivity, p);
+ assertEquals(ViewGroup.LayoutParams.MATCH_PARENT, layoutParams.width);
+ assertEquals(ViewGroup.LayoutParams.MATCH_PARENT, layoutParams.height);
+
// Test RelativeLayout.Params which generated from the xml file.
int rules[];
- RelativeLayoutCtsActivity activity = getActivity();
// test attributes used in RelativeLayout.
- RelativeLayout relativeLayout = (RelativeLayout) activity.findViewById(
+ RelativeLayout relativeLayout = (RelativeLayout) mActivity.findViewById(
R.id.relative_sublayout_attrs);
// view1, centered within its parent.
// TEST: android:layout_centerInParent
- View view1 = activity.findViewById(R.id.relative_view1);
+ View view1 = mActivity.findViewById(R.id.relative_view1);
ViewAsserts.assertHorizontalCenterAligned(relativeLayout, view1);
ViewAsserts.assertVerticalCenterAligned(relativeLayout, view1);
layoutParams = (RelativeLayout.LayoutParams) (view1.getLayoutParams());
@@ -70,7 +94,7 @@
// view2, below view1 and has same left position with view1.
// TEST: android:layout_below; android:layout_alignLeft
- View view2 = activity.findViewById(R.id.relative_view2);
+ View view2 = mActivity.findViewById(R.id.relative_view2);
ViewAsserts.assertLeftAligned(view1, view2);
assertEquals(view1.getBottom(), view2.getTop());
layoutParams = (RelativeLayout.LayoutParams) (view2.getLayoutParams());
@@ -81,7 +105,7 @@
// view3, has same top position with view1 and same bottom position with view2,
// and on the right of view1.1.
// TEST: android:layout_alignTop; android:layout_alignBottom; android:layout_toRightOf
- View view3 = activity.findViewById(R.id.relative_view3);
+ View view3 = mActivity.findViewById(R.id.relative_view3);
ViewAsserts.assertTopAligned(view1, view3);
ViewAsserts.assertBottomAligned(view2, view3);
assertEquals(view1.getRight(), view3.getLeft());
@@ -93,7 +117,7 @@
// view4, has same right position with view3 and above view3.
// TEST: android:layout_alignRight; android:layout_above
- View view4 = activity.findViewById(R.id.relative_view4);
+ View view4 = mActivity.findViewById(R.id.relative_view4);
ViewAsserts.assertRightAligned(view3, view4);
assertEquals(view3.getTop(), view4.getBottom());
layoutParams = (RelativeLayout.LayoutParams) (view4.getLayoutParams());
@@ -103,7 +127,7 @@
// view5 goes on the left-bottom.
// TEST: android:layout_alignParentBottom; android:layout_alignParentLeft
- View view5 = activity.findViewById(R.id.relative_view5);
+ View view5 = mActivity.findViewById(R.id.relative_view5);
ViewAsserts.assertLeftAligned(relativeLayout, view5);
ViewAsserts.assertBottomAligned(relativeLayout, view5);
layoutParams = (RelativeLayout.LayoutParams) (view5.getLayoutParams());
@@ -113,7 +137,7 @@
// view6 goes on the top-right.
// TEST: android:layout_alignParentTop; android:layout_alignParentRight
- View view6 = activity.findViewById(R.id.relative_view6);
+ View view6 = mActivity.findViewById(R.id.relative_view6);
ViewAsserts.assertTopAligned(relativeLayout, view6);
ViewAsserts.assertRightAligned(relativeLayout, view6);
layoutParams = (RelativeLayout.LayoutParams) (view6.getLayoutParams());
@@ -123,7 +147,7 @@
// view7, has same baseline with view6 and centered horizontally within its parent.
// TEST: android:layout_alignBaseline; android:layout_centerHorizontal
- View view7 = activity.findViewById(R.id.relative_view7);
+ View view7 = mActivity.findViewById(R.id.relative_view7);
ViewAsserts.assertBaselineAligned(view6, view7);
ViewAsserts.assertHorizontalCenterAligned(relativeLayout, view7);
layoutParams = (RelativeLayout.LayoutParams) (view7.getLayoutParams());
@@ -133,7 +157,7 @@
// view8, centered vertically within its parent and on the left of view1.
// TEST: android:layout_toLeftOf; android:layout_centerVertical
- View view8 = activity.findViewById(R.id.relative_view8);
+ View view8 = mActivity.findViewById(R.id.relative_view8);
ViewAsserts.assertVerticalCenterAligned(relativeLayout, view8);
assertEquals(view1.getLeft(), view8.getRight());
layoutParams = (RelativeLayout.LayoutParams) (view8.getLayoutParams());
@@ -144,7 +168,7 @@
// view9, has same top and bottom position with view3 and same left position with its parent
// TEST: android:layout_alignLeft; android:layout_alignTop; android:layout_alignBottom;
// android:layout_alignWithParentIfMissing
- View view9 = activity.findViewById(R.id.relative_view9);
+ View view9 = mActivity.findViewById(R.id.relative_view9);
ViewAsserts.assertTopAligned(view3, view9);
ViewAsserts.assertBottomAligned(view3, view9);
ViewAsserts.assertLeftAligned(relativeLayout, view9);
@@ -160,15 +184,14 @@
// Test RelativeLayout.Params which generated from the xml file.
int rules[];
- RelativeLayoutCtsActivity activity = getActivity();
// test attributes used in RelativeLayout.
- RelativeLayout relativeLayout = (RelativeLayout) activity.findViewById(
+ RelativeLayout relativeLayout = (RelativeLayout) mActivity.findViewById(
R.id.relative_sublayout_attrs_2);
// view1, centered within its parent.
// TEST: android:layout_centerInParent
- View view1 = activity.findViewById(R.id.relative_view21);
+ View view1 = mActivity.findViewById(R.id.relative_view21);
ViewAsserts.assertHorizontalCenterAligned(relativeLayout, view1);
ViewAsserts.assertVerticalCenterAligned(relativeLayout, view1);
layoutParams = (RelativeLayout.LayoutParams) (view1.getLayoutParams());
@@ -177,7 +200,7 @@
// view2, below view1 and has same left position with view1.
// TEST: android:layout_below; android:layout_alignStart
- View view2 = activity.findViewById(R.id.relative_view22);
+ View view2 = mActivity.findViewById(R.id.relative_view22);
ViewAsserts.assertLeftAligned(view1, view2);
assertEquals(view1.getBottom(), view2.getTop());
layoutParams = (RelativeLayout.LayoutParams) (view2.getLayoutParams());
@@ -197,7 +220,7 @@
// view3, has same top position with view1 and same bottom position with view2,
// and on the right of view1.1.
// TEST: android:layout_alignTop; android:layout_alignBottom; android:layout_toEndOf
- View view3 = activity.findViewById(R.id.relative_view23);
+ View view3 = mActivity.findViewById(R.id.relative_view23);
ViewAsserts.assertTopAligned(view1, view3);
ViewAsserts.assertBottomAligned(view2, view3);
assertEquals(view1.getRight(), view3.getLeft());
@@ -219,7 +242,7 @@
// view4, has same right position with view3 and above view3.
// TEST: android:layout_alignEnd; android:layout_above
- View view4 = activity.findViewById(R.id.relative_view24);
+ View view4 = mActivity.findViewById(R.id.relative_view24);
ViewAsserts.assertRightAligned(view3, view4);
assertEquals(view3.getTop(), view4.getBottom());
layoutParams = (RelativeLayout.LayoutParams) (view4.getLayoutParams());
@@ -238,7 +261,7 @@
// view5 goes on the left-bottom.
// TEST: android:layout_alignParentBottom; android:layout_alignParentStart
- View view5 = activity.findViewById(R.id.relative_view25);
+ View view5 = mActivity.findViewById(R.id.relative_view25);
ViewAsserts.assertLeftAligned(relativeLayout, view5);
ViewAsserts.assertBottomAligned(relativeLayout, view5);
layoutParams = (RelativeLayout.LayoutParams) (view5.getLayoutParams());
@@ -257,7 +280,7 @@
// view6 goes on the top-right.
// TEST: android:layout_alignParentTop; android:layout_alignParentEnd
- View view6 = activity.findViewById(R.id.relative_view26);
+ View view6 = mActivity.findViewById(R.id.relative_view26);
ViewAsserts.assertTopAligned(relativeLayout, view6);
ViewAsserts.assertRightAligned(relativeLayout, view6);
layoutParams = (RelativeLayout.LayoutParams) (view6.getLayoutParams());
@@ -276,7 +299,7 @@
// view7, has same baseline with view6 and centered horizontally within its parent.
// TEST: android:layout_alignBaseline; android:layout_centerHorizontal
- View view7 = activity.findViewById(R.id.relative_view27);
+ View view7 = mActivity.findViewById(R.id.relative_view27);
ViewAsserts.assertBaselineAligned(view6, view7);
ViewAsserts.assertHorizontalCenterAligned(relativeLayout, view7);
layoutParams = (RelativeLayout.LayoutParams) (view7.getLayoutParams());
@@ -291,7 +314,7 @@
// view8, centered vertically within its parent and on the left of view1.
// TEST: android:layout_toStartOf; android:layout_centerVertical
- View view8 = activity.findViewById(R.id.relative_view28);
+ View view8 = mActivity.findViewById(R.id.relative_view28);
ViewAsserts.assertVerticalCenterAligned(relativeLayout, view8);
assertEquals(view1.getLeft(), view8.getRight());
layoutParams = (RelativeLayout.LayoutParams) (view8.getLayoutParams());
@@ -311,7 +334,7 @@
// view9, has same top and bottom position with view3 and same left position with its parent
// TEST: android:layout_alignStart; android:layout_alignTop; android:layout_alignBottom;
// android:layout_alignWithParentIfMissing
- View view9 = activity.findViewById(R.id.relative_view29);
+ View view9 = mActivity.findViewById(R.id.relative_view29);
ViewAsserts.assertTopAligned(view3, view9);
ViewAsserts.assertBottomAligned(view3, view9);
ViewAsserts.assertLeftAligned(relativeLayout, view9);
diff --git a/tests/tests/widget/src/android/widget/cts/RemoteViewsActivityTest.java b/tests/tests/widget/src/android/widget/cts/RemoteViewsActivityTest.java
index 369d7aa..2174c06 100644
--- a/tests/tests/widget/src/android/widget/cts/RemoteViewsActivityTest.java
+++ b/tests/tests/widget/src/android/widget/cts/RemoteViewsActivityTest.java
@@ -25,7 +25,6 @@
import android.view.View;
import android.view.ViewGroup;
import android.widget.RemoteViews;
-
import android.widget.cts.R;
public class RemoteViewsActivityTest
diff --git a/tests/tests/widget/src/android/widget/cts/RemoteViewsCtsActivity.java b/tests/tests/widget/src/android/widget/cts/RemoteViewsCtsActivity.java
index 64001dc..364f522 100644
--- a/tests/tests/widget/src/android/widget/cts/RemoteViewsCtsActivity.java
+++ b/tests/tests/widget/src/android/widget/cts/RemoteViewsCtsActivity.java
@@ -16,10 +16,7 @@
package android.widget.cts;
-import android.widget.cts.R;
-
import android.app.Activity;
-import android.cts.util.NullWebViewUtils;
import android.os.Bundle;
import android.widget.RemoteViews;
@@ -29,11 +26,7 @@
public class RemoteViewsCtsActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
- try {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.remoteviews_host);
- } catch (Exception e) {
- NullWebViewUtils.determineIfWebViewAvailable(this, e);
- }
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.remoteviews_host);
}
}
diff --git a/tests/tests/widget/src/android/widget/cts/RemoteViewsTest.java b/tests/tests/widget/src/android/widget/cts/RemoteViewsTest.java
index 605dbb6..0efc007 100644
--- a/tests/tests/widget/src/android/widget/cts/RemoteViewsTest.java
+++ b/tests/tests/widget/src/android/widget/cts/RemoteViewsTest.java
@@ -16,38 +16,64 @@
package android.widget.cts;
-import android.graphics.drawable.Icon;
-import android.test.UiThreadTest;
-import android.widget.cts.R;
-
+import static org.junit.Assert.*;
import android.app.Activity;
import android.app.Instrumentation;
-import android.app.PendingIntent;
import android.app.Instrumentation.ActivityMonitor;
+import android.app.PendingIntent;
+import android.content.Context;
import android.content.Intent;
import android.content.res.ColorStateList;
import android.cts.util.WidgetTestUtils;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Icon;
import android.net.Uri;
+import android.os.Bundle;
import android.os.Parcel;
-import android.test.ActivityInstrumentationTestCase2;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.text.TextUtils;
+import android.util.TypedValue;
import android.view.View;
+import android.view.ViewGroup;
import android.widget.AbsoluteLayout;
+import android.widget.AnalogClock;
+import android.widget.Button;
import android.widget.Chronometer;
+import android.widget.DatePicker;
+import android.widget.DateTimeView;
import android.widget.EditText;
import android.widget.FrameLayout;
+import android.widget.GridLayout;
import android.widget.GridView;
+import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
+import android.widget.NumberPicker;
import android.widget.ProgressBar;
+import android.widget.RatingBar;
import android.widget.RelativeLayout;
import android.widget.RemoteViews;
-import android.widget.TextView;
import android.widget.RemoteViews.ActionException;
+import android.widget.SeekBar;
+import android.widget.StackView;
+import android.widget.TextClock;
+import android.widget.TextView;
+import android.widget.ViewFlipper;
+import android.widget.cts.util.TestUtils;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.runner.RunWith;
import java.io.File;
import java.io.FileOutputStream;
@@ -58,42 +84,59 @@
/**
* Test {@link RemoteViews}.
*/
-public class RemoteViewsTest extends ActivityInstrumentationTestCase2<RemoteViewsCtsActivity> {
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class RemoteViewsTest {
private static final String PACKAGE_NAME = "android.widget.cts";
- private static final int INVALD_ID = -1;
+ private static final int INVALID_ID = -1;
private static final long TEST_TIMEOUT = 5000;
+ @Rule
+ public ActivityTestRule<RemoteViewsCtsActivity> mActivityRule
+ = new ActivityTestRule<>(RemoteViewsCtsActivity.class);
+
+ @Rule
+ public ExpectedException mExpectedException = ExpectedException.none();
+
+ private Instrumentation mInstrumentation;
+
+ private Context mContext;
+
private RemoteViews mRemoteViews;
private View mResult;
- private Activity mActivity;
+ @Before
+ public void setup() {
+ mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ mContext = mInstrumentation.getTargetContext();
+ mInstrumentation.runOnMainSync(() -> {
+ mRemoteViews = new RemoteViews(PACKAGE_NAME, R.layout.remoteviews_good);
+ mResult = mRemoteViews.apply(mContext, null);
- public RemoteViewsTest() {
- super(PACKAGE_NAME, RemoteViewsCtsActivity.class);
- }
+ // Add our host view to the activity behind this test. This is similar to how launchers
+ // add widgets to the on-screen UI.
+ ViewGroup root = (ViewGroup) mActivityRule.getActivity().findViewById
+ (R.id.remoteView_host);
+ FrameLayout.MarginLayoutParams lp = new FrameLayout.MarginLayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT);
+ mResult.setLayoutParams(lp);
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mActivity = getActivity();
- getInstrumentation().runOnMainSync(new Runnable() {
- @Override
- public void run() {
- mRemoteViews = new RemoteViews(PACKAGE_NAME, R.layout.remoteviews_good);
- mResult = mRemoteViews.apply(mActivity, null);
- }
+ root.addView(mResult);
});
}
+ @Test
public void testConstructor() {
new RemoteViews(PACKAGE_NAME, R.layout.remoteviews_good);
new RemoteViews(Parcel.obtain());
}
+ @Test
public void testGetPackage() {
assertEquals(PACKAGE_NAME, mRemoteViews.getPackage());
@@ -101,105 +144,130 @@
assertNull(mRemoteViews.getPackage());
}
+ @Test
public void testGetLayoutId() {
assertEquals(R.layout.remoteviews_good, mRemoteViews.getLayoutId());
mRemoteViews = new RemoteViews(PACKAGE_NAME, R.layout.listview_layout);
assertEquals(R.layout.listview_layout, mRemoteViews.getLayoutId());
- mRemoteViews = new RemoteViews(PACKAGE_NAME, INVALD_ID);
- assertEquals(INVALD_ID, mRemoteViews.getLayoutId());
+ mRemoteViews = new RemoteViews(PACKAGE_NAME, INVALID_ID);
+ assertEquals(INVALID_ID, mRemoteViews.getLayoutId());
mRemoteViews = new RemoteViews(PACKAGE_NAME, 0);
assertEquals(0, mRemoteViews.getLayoutId());
}
+ @Test
+ public void testSetContentDescription() {
+ View view = mResult.findViewById(R.id.remoteView_frame);
+
+ assertNull(view.getContentDescription());
+
+ CharSequence contentDescription = mContext.getString(R.string.remote_content_description);
+ mRemoteViews.setContentDescription(R.id.remoteView_frame, contentDescription);
+ mInstrumentation.runOnMainSync(() -> mRemoteViews.reapply(mContext, mResult));
+ assertTrue(TextUtils.equals(contentDescription, view.getContentDescription()));
+ }
+
+ @Test
public void testSetViewVisibility() {
View view = mResult.findViewById(R.id.remoteView_chronometer);
assertEquals(View.VISIBLE, view.getVisibility());
mRemoteViews.setViewVisibility(R.id.remoteView_chronometer, View.INVISIBLE);
- mRemoteViews.reapply(mActivity, mResult);
+ mInstrumentation.runOnMainSync(() -> mRemoteViews.reapply(mContext, mResult));
assertEquals(View.INVISIBLE, view.getVisibility());
mRemoteViews.setViewVisibility(R.id.remoteView_chronometer, View.GONE);
- mRemoteViews.reapply(mActivity, mResult);
+ mInstrumentation.runOnMainSync(() -> mRemoteViews.reapply(mContext, mResult));
assertEquals(View.GONE, view.getVisibility());
mRemoteViews.setViewVisibility(R.id.remoteView_chronometer, View.VISIBLE);
- mRemoteViews.reapply(mActivity, mResult);
+ mInstrumentation.runOnMainSync(() -> mRemoteViews.reapply(mContext, mResult));
assertEquals(View.VISIBLE, view.getVisibility());
}
+ @Test
public void testSetTextViewText() {
TextView textView = (TextView) mResult.findViewById(R.id.remoteView_text);
assertEquals("", textView.getText().toString());
String expected = "This is content";
mRemoteViews.setTextViewText(R.id.remoteView_text, expected);
- mRemoteViews.reapply(mActivity, mResult);
+ mInstrumentation.runOnMainSync(() -> mRemoteViews.reapply(mContext, mResult));
assertEquals(expected, textView.getText().toString());
mRemoteViews.setTextViewText(R.id.remoteView_text, null);
- mRemoteViews.reapply(mActivity, mResult);
+ mInstrumentation.runOnMainSync(() -> mRemoteViews.reapply(mContext, mResult));
assertEquals("", textView.getText().toString());
+ mExpectedException.expect(ActionException.class);
mRemoteViews.setTextViewText(R.id.remoteView_absolute, "");
- try {
- mRemoteViews.reapply(mActivity, mResult);
- fail("Should throw ActionException");
- } catch (ActionException e) {
- // expected
- }
+ mRemoteViews.reapply(mContext, mResult);
}
+ @Test
+ public void testSetTextViewTextSize() {
+ TextView textView = (TextView) mResult.findViewById(R.id.remoteView_text);
+
+ mRemoteViews.setTextViewTextSize(R.id.remoteView_text, TypedValue.COMPLEX_UNIT_SP, 18);
+ mInstrumentation.runOnMainSync(() -> mRemoteViews.reapply(mContext, mResult));
+ assertEquals(mContext.getResources().getDisplayMetrics().scaledDensity * 18,
+ textView.getTextSize(), 0.001f);
+
+ mExpectedException.expect(Throwable.class);
+ mRemoteViews.setTextViewTextSize(R.id.remoteView_absolute, TypedValue.COMPLEX_UNIT_SP, 20);
+ mRemoteViews.reapply(mContext, mResult);
+ }
+
+ @Test
public void testSetIcon() {
ImageView image = (ImageView) mResult.findViewById(R.id.remoteView_image);
assertNull(image.getDrawable());
- Icon iconBlack = Icon.createWithResource(mActivity, R.drawable.icon_black);
+ Icon iconBlack = Icon.createWithResource(mContext, R.drawable.icon_black);
mRemoteViews.setIcon(R.id.remoteView_image, "setImageIcon", iconBlack);
- mRemoteViews.reapply(mActivity, mResult);
+ mInstrumentation.runOnMainSync(() -> mRemoteViews.reapply(mContext, mResult));
assertNotNull(image.getDrawable());
- BitmapDrawable dBlack = (BitmapDrawable) mActivity.getDrawable(R.drawable.icon_black);
+ BitmapDrawable dBlack = (BitmapDrawable) mContext.getDrawable(R.drawable.icon_black);
WidgetTestUtils.assertEquals(dBlack.getBitmap(),
((BitmapDrawable) image.getDrawable()).getBitmap());
}
+ @Test
public void testSetImageViewIcon() {
ImageView image = (ImageView) mResult.findViewById(R.id.remoteView_image);
assertNull(image.getDrawable());
- Icon iconBlue = Icon.createWithResource(mActivity, R.drawable.icon_blue);
+ Icon iconBlue = Icon.createWithResource(mContext, R.drawable.icon_blue);
mRemoteViews.setImageViewIcon(R.id.remoteView_image, iconBlue);
- mRemoteViews.reapply(mActivity, mResult);
+ mInstrumentation.runOnMainSync(() -> mRemoteViews.reapply(mContext, mResult));
assertNotNull(image.getDrawable());
- BitmapDrawable dBlue = (BitmapDrawable) mActivity.getDrawable(R.drawable.icon_blue);
+ BitmapDrawable dBlue = (BitmapDrawable) mContext.getDrawable(R.drawable.icon_blue);
WidgetTestUtils.assertEquals(dBlue.getBitmap(),
((BitmapDrawable) image.getDrawable()).getBitmap());
}
+ @Test
public void testSetImageViewResource() {
ImageView image = (ImageView) mResult.findViewById(R.id.remoteView_image);
assertNull(image.getDrawable());
mRemoteViews.setImageViewResource(R.id.remoteView_image, R.drawable.testimage);
- mRemoteViews.reapply(mActivity, mResult);
+ mInstrumentation.runOnMainSync(() -> mRemoteViews.reapply(mContext, mResult));
assertNotNull(image.getDrawable());
- BitmapDrawable d = (BitmapDrawable) mActivity.getDrawable(R.drawable.testimage);
+ BitmapDrawable d = (BitmapDrawable) mContext.getDrawable(R.drawable.testimage);
WidgetTestUtils.assertEquals(d.getBitmap(),
((BitmapDrawable) image.getDrawable()).getBitmap());
+ mExpectedException.expect(ActionException.class);
mRemoteViews.setImageViewResource(R.id.remoteView_absolute, R.drawable.testimage);
- try {
- mRemoteViews.reapply(mActivity, mResult);
- fail("Should throw ActionException");
- } catch (ActionException e) {
- // expected
- }
+ mRemoteViews.reapply(mContext, mResult);
}
+ @Test
public void testSetImageViewUri() throws IOException {
String path = getTestImagePath();
File imageFile = new File(path);
@@ -212,11 +280,11 @@
assertNull(image.getDrawable());
mRemoteViews.setImageViewUri(R.id.remoteView_image, uri);
- mRemoteViews.reapply(mActivity, mResult);
+ mInstrumentation.runOnMainSync(() -> mRemoteViews.reapply(mContext, mResult));
Bitmap imageViewBitmap = ((BitmapDrawable) image.getDrawable()).getBitmap();
Bitmap expectedBitmap = WidgetTestUtils.getUnscaledAndDitheredBitmap(
- mActivity.getResources(), R.raw.testimage, imageViewBitmap.getConfig());
+ mContext.getResources(), R.raw.testimage, imageViewBitmap.getConfig());
WidgetTestUtils.assertEquals(expectedBitmap, imageViewBitmap);
} finally {
imageFile.delete();
@@ -227,9 +295,10 @@
* Returns absolute file path of location where test image should be stored
*/
private String getTestImagePath() {
- return getInstrumentation().getTargetContext().getFilesDir() + "/test.jpg";
+ return mContext.getFilesDir() + "/test.jpg";
}
+ @Test
public void testSetChronometer() {
long base1 = 50;
long base2 = -50;
@@ -237,31 +306,45 @@
mRemoteViews.setChronometer(R.id.remoteView_chronometer, base1, "HH:MM:SS",
false);
- mRemoteViews.reapply(mActivity, mResult);
+ mInstrumentation.runOnMainSync(() -> mRemoteViews.reapply(mContext, mResult));
assertEquals(base1, chronometer.getBase());
assertEquals("HH:MM:SS", chronometer.getFormat());
mRemoteViews.setChronometer(R.id.remoteView_chronometer, base2, "HH:MM:SS",
false);
- mRemoteViews.reapply(mActivity, mResult);
+ mInstrumentation.runOnMainSync(() -> mRemoteViews.reapply(mContext, mResult));
assertEquals(base2, chronometer.getBase());
assertEquals("HH:MM:SS", chronometer.getFormat());
mRemoteViews.setChronometer(R.id.remoteView_chronometer, base1, "invalid",
true);
- mRemoteViews.reapply(mActivity, mResult);
+ mInstrumentation.runOnMainSync(() -> mRemoteViews.reapply(mContext, mResult));
assertEquals(base1, chronometer.getBase());
assertEquals("invalid", chronometer.getFormat());
+ mExpectedException.expect(ActionException.class);
mRemoteViews.setChronometer(R.id.remoteView_absolute, base1, "invalid", true);
- try {
- mRemoteViews.reapply(mActivity, mResult);
- fail("Should throw ActionException");
- } catch (ActionException e) {
- // expected
- }
+ mRemoteViews.reapply(mContext, mResult);
}
+ @Test
+ public void testSetChronometerCountDown() {
+ Chronometer chronometer = (Chronometer) mResult.findViewById(R.id.remoteView_chronometer);
+
+ mRemoteViews.setChronometerCountDown(R.id.remoteView_chronometer, true);
+ mInstrumentation.runOnMainSync(() -> mRemoteViews.reapply(mContext, mResult));
+ assertTrue(chronometer.isCountDown());
+
+ mRemoteViews.setChronometerCountDown(R.id.remoteView_chronometer, false);
+ mInstrumentation.runOnMainSync(() -> mRemoteViews.reapply(mContext, mResult));
+ assertFalse(chronometer.isCountDown());
+
+ mExpectedException.expect(ActionException.class);
+ mRemoteViews.setChronometerCountDown(R.id.remoteView_absolute, true);
+ mRemoteViews.reapply(mContext, mResult);
+ }
+
+ @Test
public void testSetProgressBar() {
ProgressBar progress = (ProgressBar) mResult.findViewById(R.id.remoteView_progress);
assertEquals(100, progress.getMax());
@@ -270,27 +353,24 @@
assertFalse(progress.isIndeterminate());
mRemoteViews.setProgressBar(R.id.remoteView_progress, 80, 50, true);
- mRemoteViews.reapply(mActivity, mResult);
+ mInstrumentation.runOnMainSync(() -> mRemoteViews.reapply(mContext, mResult));
// make the bar indeterminate will not affect max and progress
assertEquals(100, progress.getMax());
assertEquals(0, progress.getProgress());
assertTrue(progress.isIndeterminate());
mRemoteViews.setProgressBar(R.id.remoteView_progress, 60, 50, false);
- mRemoteViews.reapply(mActivity, mResult);
+ mInstrumentation.runOnMainSync(() -> mRemoteViews.reapply(mContext, mResult));
assertEquals(60, progress.getMax());
assertEquals(50, progress.getProgress());
assertFalse(progress.isIndeterminate());
+ mExpectedException.expect(ActionException.class);
mRemoteViews.setProgressBar(R.id.remoteView_relative, 60, 50, false);
- try {
- mRemoteViews.reapply(mActivity, mResult);
- fail("Should throw ActionException");
- } catch (ActionException e) {
- // expected
- }
+ mRemoteViews.reapply(mContext, mResult);
}
+ @Test
public void testApply() {
assertNotNull(mResult);
assertNotNull(mResult.findViewById(R.id.remoteViews_good));
@@ -304,44 +384,58 @@
assertNotNull(mResult.findViewById(R.id.remoteView_text));
}
+ @Test
public void testReapply() {
- TextView text = new TextView(mActivity);
ImageView image = (ImageView) mResult.findViewById(R.id.remoteView_image);
assertNull(image.getDrawable());
mRemoteViews.setImageViewResource(R.id.remoteView_image, R.drawable.testimage);
- mRemoteViews.reapply(mActivity, image);
+ mInstrumentation.runOnMainSync(() -> mRemoteViews.reapply(mContext, image));
assertNotNull(image.getDrawable());
- BitmapDrawable d = (BitmapDrawable) mActivity
+ BitmapDrawable d = (BitmapDrawable) mContext
.getResources().getDrawable(R.drawable.testimage);
WidgetTestUtils.assertEquals(d.getBitmap(),
((BitmapDrawable) image.getDrawable()).getBitmap());
}
+ @Test
public void testOnLoadClass() {
mRemoteViews = new RemoteViews(Parcel.obtain());
- assertTrue(mRemoteViews.onLoadClass(RelativeLayout.class));
- assertTrue(mRemoteViews.onLoadClass(FrameLayout.class));
assertTrue(mRemoteViews.onLoadClass(AbsoluteLayout.class));
- assertTrue(mRemoteViews.onLoadClass(LinearLayout.class));
- assertTrue(mRemoteViews.onLoadClass(TextView.class));
- assertTrue(mRemoteViews.onLoadClass(ImageView.class));
- assertTrue(mRemoteViews.onLoadClass(ProgressBar.class));
+ assertTrue(mRemoteViews.onLoadClass(AnalogClock.class));
+ assertTrue(mRemoteViews.onLoadClass(Button.class));
assertTrue(mRemoteViews.onLoadClass(Chronometer.class));
- assertTrue(mRemoteViews.onLoadClass(ListView.class));
+ assertTrue(mRemoteViews.onLoadClass(DateTimeView.class));
+ assertTrue(mRemoteViews.onLoadClass(FrameLayout.class));
+ assertTrue(mRemoteViews.onLoadClass(GridLayout.class));
assertTrue(mRemoteViews.onLoadClass(GridView.class));
+ assertTrue(mRemoteViews.onLoadClass(ImageButton.class));
+ assertTrue(mRemoteViews.onLoadClass(ImageView.class));
+ assertTrue(mRemoteViews.onLoadClass(LinearLayout.class));
+ assertTrue(mRemoteViews.onLoadClass(ListView.class));
+ assertTrue(mRemoteViews.onLoadClass(ProgressBar.class));
+ assertTrue(mRemoteViews.onLoadClass(RelativeLayout.class));
+ assertTrue(mRemoteViews.onLoadClass(StackView.class));
+ assertTrue(mRemoteViews.onLoadClass(TextClock.class));
+ assertTrue(mRemoteViews.onLoadClass(TextView.class));
+ assertTrue(mRemoteViews.onLoadClass(ViewFlipper.class));
// those classes without annotation @RemoteView
assertFalse(mRemoteViews.onLoadClass(EditText.class));
+ assertFalse(mRemoteViews.onLoadClass(DatePicker.class));
+ assertFalse(mRemoteViews.onLoadClass(NumberPicker.class));
+ assertFalse(mRemoteViews.onLoadClass(RatingBar.class));
+ assertFalse(mRemoteViews.onLoadClass(SeekBar.class));
}
+ @Test
public void testDescribeContents() {
mRemoteViews = new RemoteViews(Parcel.obtain());
mRemoteViews.describeContents();
}
- @UiThreadTest
+ @Test
public void testWriteToParcel() {
mRemoteViews.setTextViewText(R.id.remoteView_text, "This is content");
mRemoteViews.setViewVisibility(R.id.remoteView_frame, View.GONE);
@@ -351,7 +445,7 @@
// the package and layout are successfully written into parcel
mRemoteViews = RemoteViews.CREATOR.createFromParcel(p);
- View result = mRemoteViews.apply(mActivity, null);
+ View result = mRemoteViews.apply(mContext, null);
assertEquals(PACKAGE_NAME, mRemoteViews.getPackage());
assertEquals(R.layout.remoteviews_good, mRemoteViews.getLayoutId());
assertEquals("This is content", ((TextView) result.findViewById(R.id.remoteView_text))
@@ -359,12 +453,6 @@
assertEquals(View.GONE, result.findViewById(R.id.remoteView_frame).getVisibility());
p = Parcel.obtain();
- try {
- mRemoteViews.writeToParcel(null, 0);
- fail("Should throw NullPointerException");
- } catch (NullPointerException e) {
- // expected
- }
// currently the flag is not used
mRemoteViews.writeToParcel(p, -1);
@@ -374,130 +462,120 @@
RemoteViews[] remote = RemoteViews.CREATOR.newArray(1);
assertNotNull(remote);
assertEquals(1, remote.length);
-
- try {
- RemoteViews.CREATOR.newArray(-1);
- fail("should throw NegativeArraySizeException");
- } catch (NegativeArraySizeException e) {
- // expected
- }
}
+ @Test(expected=NullPointerException.class)
+ public void testWriteNullToParcel() {
+ mRemoteViews.writeToParcel(null, 0);
+ }
+
+ @Test(expected=NegativeArraySizeException.class)
+ public void testCreateNegativeSizedArray() {
+ RemoteViews.CREATOR.newArray(-1);
+ }
+
+ @Test
public void testSetImageViewBitmap() {
ImageView image = (ImageView) mResult.findViewById(R.id.remoteView_image);
assertNull(image.getDrawable());
Bitmap bitmap =
- BitmapFactory.decodeResource(mActivity.getResources(), R.drawable.testimage);
+ BitmapFactory.decodeResource(mContext.getResources(), R.drawable.testimage);
mRemoteViews.setImageViewBitmap(R.id.remoteView_image, bitmap);
- mRemoteViews.reapply(mActivity, mResult);
+ mInstrumentation.runOnMainSync(() -> mRemoteViews.reapply(mContext, mResult));
assertNotNull(image.getDrawable());
WidgetTestUtils.assertEquals(bitmap, ((BitmapDrawable) image.getDrawable()).getBitmap());
+ mExpectedException.expect(ActionException.class);
mRemoteViews.setImageViewBitmap(R.id.remoteView_absolute, bitmap);
- try {
- mRemoteViews.reapply(mActivity, mResult);
- fail("Should throw ActionException");
- } catch (ActionException e) {
- // expected
- }
+ mRemoteViews.reapply(mContext, mResult);
}
+ @Test
public void testSetBitmap() {
ImageView image = (ImageView) mResult.findViewById(R.id.remoteView_image);
assertNull(image.getDrawable());
Bitmap bitmap =
- BitmapFactory.decodeResource(mActivity.getResources(), R.drawable.testimage);
+ BitmapFactory.decodeResource(mContext.getResources(), R.drawable.testimage);
mRemoteViews.setBitmap(R.id.remoteView_image, "setImageBitmap", bitmap);
- mRemoteViews.reapply(mActivity, mResult);
+ mInstrumentation.runOnMainSync(() -> mRemoteViews.reapply(mContext, mResult));
assertNotNull(image.getDrawable());
WidgetTestUtils.assertEquals(bitmap, ((BitmapDrawable) image.getDrawable()).getBitmap());
+ mExpectedException.expect(ActionException.class);
mRemoteViews.setBitmap(R.id.remoteView_absolute, "setImageBitmap", bitmap);
- try {
- mRemoteViews.reapply(mActivity, mResult);
- fail("Should throw ActionException");
- } catch (ActionException e) {
- // expected
- }
+ mRemoteViews.reapply(mContext, mResult);
}
+ @Test
public void testSetBoolean() {
ProgressBar progress = (ProgressBar) mResult.findViewById(R.id.remoteView_progress);
// the view uses style progressBarHorizontal, so the default is false
assertFalse(progress.isIndeterminate());
mRemoteViews.setBoolean(R.id.remoteView_progress, "setIndeterminate", true);
- mRemoteViews.reapply(mActivity, mResult);
+ mInstrumentation.runOnMainSync(() -> mRemoteViews.reapply(mContext, mResult));
assertTrue(progress.isIndeterminate());
+ mExpectedException.expect(ActionException.class);
mRemoteViews.setBoolean(R.id.remoteView_relative, "setIndeterminate", false);
- try {
- mRemoteViews.reapply(mActivity, mResult);
- fail("Should throw ActionException");
- } catch (ActionException e) {
- // expected
- }
+ mRemoteViews.reapply(mContext, mResult);
}
+ @Test
public void testSetCharSequence() {
TextView textView = (TextView) mResult.findViewById(R.id.remoteView_text);
assertEquals("", textView.getText().toString());
String expected = "test setCharSequence";
mRemoteViews.setCharSequence(R.id.remoteView_text, "setText", expected);
- mRemoteViews.reapply(mActivity, mResult);
+ mInstrumentation.runOnMainSync(() -> mRemoteViews.reapply(mContext, mResult));
assertEquals(expected, textView.getText().toString());
mRemoteViews.setCharSequence(R.id.remoteView_text, "setText", null);
- mRemoteViews.reapply(mActivity, mResult);
+ mInstrumentation.runOnMainSync(() -> mRemoteViews.reapply(mContext, mResult));
assertEquals("", textView.getText().toString());
+ mExpectedException.expect(ActionException.class);
mRemoteViews.setCharSequence(R.id.remoteView_absolute, "setText", "");
- try {
- mRemoteViews.reapply(mActivity, mResult);
- fail("Should throw ActionException");
- } catch (ActionException e) {
- // expected
- }
+ mRemoteViews.reapply(mContext, mResult);
}
+ @Test
public void testSetInt() {
View view = mResult.findViewById(R.id.remoteView_chronometer);
assertEquals(View.VISIBLE, view.getVisibility());
mRemoteViews.setInt(R.id.remoteView_chronometer, "setVisibility", View.INVISIBLE);
- mRemoteViews.reapply(mActivity, mResult);
+ mInstrumentation.runOnMainSync(() -> mRemoteViews.reapply(mContext, mResult));
assertEquals(View.INVISIBLE, view.getVisibility());
mRemoteViews.setInt(R.id.remoteView_chronometer, "setVisibility", View.GONE);
- mRemoteViews.reapply(mActivity, mResult);
+ mInstrumentation.runOnMainSync(() -> mRemoteViews.reapply(mContext, mResult));
assertEquals(View.GONE, view.getVisibility());
mRemoteViews.setInt(R.id.remoteView_chronometer, "setVisibility", View.VISIBLE);
- mRemoteViews.reapply(mActivity, mResult);
+ mInstrumentation.runOnMainSync(() -> mRemoteViews.reapply(mContext, mResult));
assertEquals(View.VISIBLE, view.getVisibility());
}
+ @Test
public void testSetString() {
String format = "HH:MM:SS";
Chronometer chronometer = (Chronometer) mResult.findViewById(R.id.remoteView_chronometer);
assertNull(chronometer.getFormat());
mRemoteViews.setString(R.id.remoteView_chronometer, "setFormat", format);
- mRemoteViews.reapply(mActivity, mResult);
+ mInstrumentation.runOnMainSync(() -> mRemoteViews.reapply(mContext, mResult));
assertEquals(format, chronometer.getFormat());
+ mExpectedException.expect(ActionException.class);
mRemoteViews.setString(R.id.remoteView_image, "setFormat", format);
- try {
- mRemoteViews.reapply(mActivity, mResult);
- fail("Should throw ActionException");
- } catch (ActionException e) {
- // expected
- }
+ mRemoteViews.reapply(mContext, mResult);
}
+ @Test
public void testSetUri() throws IOException {
String path = getTestImagePath();
File imagefile = new File(path);
@@ -510,50 +588,102 @@
assertNull(image.getDrawable());
mRemoteViews.setUri(R.id.remoteView_image, "setImageURI", uri);
- mRemoteViews.reapply(mActivity, mResult);
+ mInstrumentation.runOnMainSync(() -> mRemoteViews.reapply(mContext, mResult));
Bitmap imageViewBitmap = ((BitmapDrawable) image.getDrawable()).getBitmap();
Bitmap expectedBitmap = WidgetTestUtils.getUnscaledAndDitheredBitmap(
- mActivity.getResources(), R.raw.testimage, imageViewBitmap.getConfig());
+ mContext.getResources(), R.raw.testimage, imageViewBitmap.getConfig());
WidgetTestUtils.assertEquals(expectedBitmap, imageViewBitmap);
+ mExpectedException.expect(ActionException.class);
mRemoteViews.setUri(R.id.remoteView_absolute, "setImageURI", uri);
- try {
- mRemoteViews.reapply(mActivity, mResult);
- fail("Should throw ActionException");
- } catch (ActionException e) {
- // expected
- }
+ mRemoteViews.reapply(mContext, mResult);
} finally {
// remove the test image file
imagefile.delete();
}
}
+ @Test
public void testSetTextColor() {
TextView textView = (TextView) mResult.findViewById(R.id.remoteView_text);
mRemoteViews.setTextColor(R.id.remoteView_text, R.color.testcolor1);
- mRemoteViews.reapply(mActivity, mResult);
+ mInstrumentation.runOnMainSync(() -> mRemoteViews.reapply(mContext, mResult));
assertSame(ColorStateList.valueOf(R.color.testcolor1), textView.getTextColors());
mRemoteViews.setTextColor(R.id.remoteView_text, R.color.testcolor2);
- mRemoteViews.reapply(mActivity, mResult);
+ mInstrumentation.runOnMainSync(() -> mRemoteViews.reapply(mContext, mResult));
assertSame(ColorStateList.valueOf(R.color.testcolor2), textView.getTextColors());
+ mExpectedException.expect(ActionException.class);
mRemoteViews.setTextColor(R.id.remoteView_absolute, R.color.testcolor1);
- try {
- mRemoteViews.reapply(mActivity, mResult);
- fail("Should throw ActionException");
- } catch (ActionException e) {
- // expected
- }
+ mRemoteViews.reapply(mContext, mResult);
}
+ @Test
+ public void testSetTextCompoundDrawables() {
+ TextView textView = (TextView) mResult.findViewById(R.id.remoteView_text);
+
+ TestUtils.verifyCompoundDrawables(textView, -1, -1, -1, -1);
+
+ mRemoteViews.setTextViewCompoundDrawables(R.id.remoteView_text, R.drawable.start,
+ R.drawable.pass, R.drawable.failed, 0);
+ mInstrumentation.runOnMainSync(() -> mRemoteViews.reapply(mContext, mResult));
+ TestUtils.verifyCompoundDrawables(textView, R.drawable.start, R.drawable.failed,
+ R.drawable.pass, -1);
+
+ mRemoteViews.setTextViewCompoundDrawables(R.id.remoteView_text, 0,
+ R.drawable.icon_black, R.drawable.icon_red, R.drawable.icon_green);
+ mInstrumentation.runOnMainSync(() -> mRemoteViews.reapply(mContext, mResult));
+ TestUtils.verifyCompoundDrawables(textView, -1, R.drawable.icon_red, R.drawable.icon_black,
+ R.drawable.icon_green);
+
+ mExpectedException.expect(Throwable.class);
+ mRemoteViews.setTextViewCompoundDrawables(R.id.remoteView_absolute, 0,
+ R.drawable.start, R.drawable.failed, 0);
+ mRemoteViews.reapply(mContext, mResult);
+ }
+
+ @Test
+ public void testSetTextCompoundDrawablesRelative() {
+ TextView textViewLtr = (TextView) mResult.findViewById(R.id.remoteView_text_ltr);
+ TextView textViewRtl = (TextView) mResult.findViewById(R.id.remoteView_text_rtl);
+
+ TestUtils.verifyCompoundDrawables(textViewLtr, -1, -1, -1, -1);
+ TestUtils.verifyCompoundDrawables(textViewRtl, -1, -1, -1, -1);
+
+ mRemoteViews.setTextViewCompoundDrawablesRelative(R.id.remoteView_text_ltr,
+ R.drawable.start, R.drawable.pass, R.drawable.failed, 0);
+ mRemoteViews.setTextViewCompoundDrawablesRelative(R.id.remoteView_text_rtl,
+ R.drawable.start, R.drawable.pass, R.drawable.failed, 0);
+ mInstrumentation.runOnMainSync(() -> mRemoteViews.reapply(mContext, mResult));
+ TestUtils.verifyCompoundDrawables(textViewLtr, R.drawable.start, R.drawable.failed,
+ R.drawable.pass, -1);
+ TestUtils.verifyCompoundDrawables(textViewRtl, R.drawable.failed, R.drawable.start,
+ R.drawable.pass, -1);
+
+ mRemoteViews.setTextViewCompoundDrawablesRelative(R.id.remoteView_text_ltr, 0,
+ R.drawable.icon_black, R.drawable.icon_red, R.drawable.icon_green);
+ mRemoteViews.setTextViewCompoundDrawablesRelative(R.id.remoteView_text_rtl, 0,
+ R.drawable.icon_black, R.drawable.icon_red, R.drawable.icon_green);
+ mInstrumentation.runOnMainSync(() -> mRemoteViews.reapply(mContext, mResult));
+ TestUtils.verifyCompoundDrawables(textViewLtr, -1, R.drawable.icon_red,
+ R.drawable.icon_black, R.drawable.icon_green);
+ TestUtils.verifyCompoundDrawables(textViewRtl, R.drawable.icon_red, -1,
+ R.drawable.icon_black, R.drawable.icon_green);
+
+ mExpectedException.expect(Throwable.class);
+ mRemoteViews.setTextViewCompoundDrawablesRelative(R.id.remoteView_absolute, 0,
+ R.drawable.start, R.drawable.failed, 0);
+ mRemoteViews.reapply(mContext, mResult);
+ }
+
+ @LargeTest
+ @Test
public void testSetOnClickPendingIntent() {
Uri uri = Uri.parse("ctstest://RemoteView/test");
- Instrumentation instrumentation = getInstrumentation();
- ActivityMonitor am = instrumentation.addMonitor(MockURLSpanTestActivity.class.getName(),
+ ActivityMonitor am = mInstrumentation.addMonitor(MockURLSpanTestActivity.class.getName(),
null, false);
View view = mResult.findViewById(R.id.remoteView_image);
view.performClick();
@@ -561,81 +691,237 @@
assertNull(newActivity);
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
- PendingIntent pendingIntent = PendingIntent.getActivity(mActivity, 0, intent, 0);
+ PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, intent, 0);
mRemoteViews.setOnClickPendingIntent(R.id.remoteView_image, pendingIntent);
- mRemoteViews.reapply(mActivity, mResult);
- view.performClick();
+ mInstrumentation.runOnMainSync(() -> mRemoteViews.reapply(mContext, mResult));
+ mInstrumentation.runOnMainSync(() -> view.performClick());
newActivity = am.waitForActivityWithTimeout(TEST_TIMEOUT);
assertNotNull(newActivity);
+ assertTrue(newActivity instanceof MockURLSpanTestActivity);
newActivity.finish();
}
+ @Test
public void testSetLong() {
long base1 = 50;
long base2 = -50;
Chronometer chronometer = (Chronometer) mResult.findViewById(R.id.remoteView_chronometer);
mRemoteViews.setLong(R.id.remoteView_chronometer, "setBase", base1);
- mRemoteViews.reapply(mActivity, mResult);
+ mInstrumentation.runOnMainSync(() -> mRemoteViews.reapply(mContext, mResult));
assertEquals(base1, chronometer.getBase());
mRemoteViews.setLong(R.id.remoteView_chronometer, "setBase", base2);
- mRemoteViews.reapply(mActivity, mResult);
+ mInstrumentation.runOnMainSync(() -> mRemoteViews.reapply(mContext, mResult));
assertEquals(base2, chronometer.getBase());
+ mExpectedException.expect(ActionException.class);
mRemoteViews.setLong(R.id.remoteView_absolute, "setBase", base1);
- try {
- mRemoteViews.reapply(mActivity, mResult);
- fail("Should throw ActionException");
- } catch (ActionException e) {
- // expected
- }
+ mRemoteViews.reapply(mContext, mResult);
}
+ @Test
public void testSetFloat() {
LinearLayout linearLayout = (LinearLayout) mResult.findViewById(R.id.remoteView_linear);
assertTrue(linearLayout.getWeightSum() <= 0.0f);
mRemoteViews.setFloat(R.id.remoteView_linear, "setWeightSum", 0.5f);
- mRemoteViews.reapply(mActivity, mResult);
- assertEquals(0.5f, linearLayout.getWeightSum());
+ mInstrumentation.runOnMainSync(() -> mRemoteViews.reapply(mContext, mResult));
+ assertEquals(0.5f, linearLayout.getWeightSum(), 0.001f);
+ mExpectedException.expect(ActionException.class);
mRemoteViews.setFloat(R.id.remoteView_absolute, "setWeightSum", 1.0f);
- try {
- mRemoteViews.reapply(mActivity, mResult);
- fail("Should throw ActionException");
- } catch (ActionException e) {
- // expected
- }
+ mRemoteViews.reapply(mContext, mResult);
}
- public void testNotFeasibleSetters() {
- // there is no RemotableViewMethods to use them, how to test?
+ @Test
+ public void testSetByte() {
+ MyRemotableView customView = (MyRemotableView) mResult.findViewById(R.id.remoteView_custom);
+ assertEquals(0, customView.getByteField());
+
+ byte b = 100;
+ mRemoteViews.setByte(R.id.remoteView_custom, "setByteField", b);
+ mInstrumentation.runOnMainSync(() -> mRemoteViews.reapply(mContext, mResult));
+ assertEquals(b, customView.getByteField());
+
+ mExpectedException.expect(ActionException.class);
+ mRemoteViews.setByte(R.id.remoteView_absolute, "setByteField", b);
+ mRemoteViews.reapply(mContext, mResult);
+ }
+
+ @Test
+ public void testSetChar() {
+ MyRemotableView customView = (MyRemotableView) mResult.findViewById(R.id.remoteView_custom);
+ assertEquals('\u0000', customView.getCharField());
+
+ mRemoteViews.setChar(R.id.remoteView_custom, "setCharField", 'q');
+ mInstrumentation.runOnMainSync(() -> mRemoteViews.reapply(mContext, mResult));
+ assertEquals('q', customView.getCharField());
+
+ mExpectedException.expect(ActionException.class);
+ mRemoteViews.setChar(R.id.remoteView_absolute, "setCharField", 'w');
+ mRemoteViews.reapply(mContext, mResult);
+ }
+
+ @Test
+ public void testSetDouble() {
+ MyRemotableView customView = (MyRemotableView) mResult.findViewById(R.id.remoteView_custom);
+ assertEquals(0.0, customView.getDoubleField(), 0.0f);
+
+ mRemoteViews.setDouble(R.id.remoteView_custom, "setDoubleField", 0.5);
+ mInstrumentation.runOnMainSync(() -> mRemoteViews.reapply(mContext, mResult));
+ assertEquals(0.5, customView.getDoubleField(), 0.001f);
+
+ mExpectedException.expect(ActionException.class);
+ mRemoteViews.setDouble(R.id.remoteView_absolute, "setDoubleField", 1.0);
+ mRemoteViews.reapply(mContext, mResult);
+ }
+
+ @Test
+ public void testSetShort() {
+ MyRemotableView customView = (MyRemotableView) mResult.findViewById(R.id.remoteView_custom);
+ assertEquals(0, customView.getShortField());
+
+ short s = 25;
+ mRemoteViews.setShort(R.id.remoteView_custom, "setShortField", s);
+ mInstrumentation.runOnMainSync(() -> mRemoteViews.reapply(mContext, mResult));
+ assertEquals(s, customView.getShortField());
+
+ mExpectedException.expect(ActionException.class);
+ mRemoteViews.setShort(R.id.remoteView_absolute, "setShortField", s);
+ mRemoteViews.reapply(mContext, mResult);
+ }
+
+ @Test
+ public void testSetBundle() {
+ MyRemotableView customView = (MyRemotableView) mResult.findViewById(R.id.remoteView_custom);
+ assertNull(customView.getBundleField());
+
+ final Bundle bundle = new Bundle();
+ bundle.putString("STR", "brexit");
+ bundle.putInt("INT", 2016);
+ mRemoteViews.setBundle(R.id.remoteView_custom, "setBundleField", bundle);
+ mInstrumentation.runOnMainSync(() -> mRemoteViews.reapply(mContext, mResult));
+ final Bundle fromRemote = customView.getBundleField();
+ assertEquals("brexit", fromRemote.getString("STR", ""));
+ assertEquals(2016, fromRemote.getInt("INT", 0));
+
+ mExpectedException.expect(ActionException.class);
+ mRemoteViews.setBundle(R.id.remoteView_absolute, "setBundleField", bundle);
+ mRemoteViews.reapply(mContext, mResult);
+ }
+
+ @Test
+ public void testSetIntent() {
+ MyRemotableView customView = (MyRemotableView) mResult.findViewById(R.id.remoteView_custom);
+ assertNull(customView.getIntentField());
+
+ final Intent intent = new Intent(mContext, SwitchCtsActivity.class);
+ intent.putExtra("STR", "brexit");
+ intent.putExtra("INT", 2016);
+ mRemoteViews.setIntent(R.id.remoteView_custom, "setIntentField", intent);
+ mInstrumentation.runOnMainSync(() -> mRemoteViews.reapply(mContext, mResult));
+ final Intent fromRemote = customView.getIntentField();
+ assertEquals(SwitchCtsActivity.class.getName(), fromRemote.getComponent().getClassName());
+ assertEquals("brexit", fromRemote.getStringExtra("STR"));
+ assertEquals(2016, fromRemote.getIntExtra("INT", 0));
+
+ mExpectedException.expect(ActionException.class);
+ mRemoteViews.setIntent(R.id.remoteView_absolute, "setIntentField", intent);
+ mRemoteViews.reapply(mContext, mResult);
+ }
+
+ @Test
+ public void testRemoveAllViews() {
+ ViewGroup root = (ViewGroup) mResult.findViewById(R.id.remoteViews_good);
+ assertTrue(root.getChildCount() > 0);
+
+ mRemoteViews.removeAllViews(R.id.remoteViews_good);
+ mInstrumentation.runOnMainSync(() -> mRemoteViews.reapply(mContext, mResult));
+ assertEquals(0, root.getChildCount());
+ }
+
+ @Test
+ public void testAddView() {
+ ViewGroup root = (ViewGroup) mResult.findViewById(R.id.remoteViews_good);
+ int originalChildCount = root.getChildCount();
+
+ assertNull(root.findViewById(R.id.remoteView_frame_extra));
+
+ // Create a RemoteViews wrapper around a layout and add it to our root
+ RemoteViews extra = new RemoteViews(PACKAGE_NAME, R.layout.remoteviews_extra);
+ mRemoteViews.addView(R.id.remoteViews_good, extra);
+ mInstrumentation.runOnMainSync(() -> mRemoteViews.reapply(mContext, mResult));
+
+ // Verify that our root has that layout as its last (new) child
+ assertEquals(originalChildCount + 1, root.getChildCount());
+ assertNotNull(root.findViewById(R.id.remoteView_frame_extra));
+ assertEquals(R.id.remoteView_frame_extra, root.getChildAt(originalChildCount).getId());
+ }
+
+ @Test
+ public void testSetLabelFor() {
+ View labelView = mResult.findViewById(R.id.remoteView_label);
+ assertEquals(View.NO_ID, labelView.getLabelFor());
+
+ mRemoteViews.setLabelFor(R.id.remoteView_label, R.id.remoteView_text);
+ mInstrumentation.runOnMainSync(() -> mRemoteViews.reapply(mContext, mResult));
+ assertEquals(R.id.remoteView_text, labelView.getLabelFor());
+ }
+
+ @Test
+ public void testSetAccessibilityTraversalAfter() {
+ View textView = mResult.findViewById(R.id.remoteView_text);
+
+ mRemoteViews.setAccessibilityTraversalAfter(R.id.remoteView_text, R.id.remoteView_frame);
+ mInstrumentation.runOnMainSync(() -> mRemoteViews.reapply(mContext, mResult));
+ assertEquals(R.id.remoteView_frame, textView.getAccessibilityTraversalAfter());
+
+ mRemoteViews.setAccessibilityTraversalAfter(R.id.remoteView_text, R.id.remoteView_linear);
+ mInstrumentation.runOnMainSync(() -> mRemoteViews.reapply(mContext, mResult));
+ assertEquals(R.id.remoteView_linear, textView.getAccessibilityTraversalAfter());
+ }
+
+ @Test
+ public void testSetAccessibilityTraversalBefore() {
+ View textView = mResult.findViewById(R.id.remoteView_text);
+
+ mRemoteViews.setAccessibilityTraversalBefore(R.id.remoteView_text, R.id.remoteView_frame);
+ mInstrumentation.runOnMainSync(() -> mRemoteViews.reapply(mContext, mResult));
+ assertEquals(R.id.remoteView_frame, textView.getAccessibilityTraversalBefore());
+
+ mRemoteViews.setAccessibilityTraversalBefore(R.id.remoteView_text, R.id.remoteView_linear);
+ mInstrumentation.runOnMainSync(() -> mRemoteViews.reapply(mContext, mResult));
+ assertEquals(R.id.remoteView_linear, textView.getAccessibilityTraversalBefore());
+ }
+
+ @Test
+ public void testSetViewPadding() {
+ View textView = mResult.findViewById(R.id.remoteView_text);
+
+ mRemoteViews.setViewPadding(R.id.remoteView_text, 10, 20, 30, 40);
+ mInstrumentation.runOnMainSync(() -> mRemoteViews.reapply(mContext, mResult));
+ assertEquals(10, textView.getPaddingLeft());
+ assertEquals(20, textView.getPaddingTop());
+ assertEquals(30, textView.getPaddingRight());
+ assertEquals(40, textView.getPaddingBottom());
+
+ mRemoteViews.setViewPadding(R.id.remoteView_text, 40, 30, 20, 10);
+ mInstrumentation.runOnMainSync(() -> mRemoteViews.reapply(mContext, mResult));
+ assertEquals(40, textView.getPaddingLeft());
+ assertEquals(30, textView.getPaddingTop());
+ assertEquals(20, textView.getPaddingRight());
+ assertEquals(10, textView.getPaddingBottom());
}
private void createSampleImage(File imagefile, int resid) throws IOException {
- InputStream source = null;
- OutputStream target = null;
-
- try {
- source = mActivity.getResources().openRawResource(resid);
- target = new FileOutputStream(imagefile);
+ try (InputStream source = mContext.getResources().openRawResource(resid);
+ OutputStream target = new FileOutputStream(imagefile)) {
byte[] buffer = new byte[1024];
for (int len = source.read(buffer); len > 0; len = source.read(buffer)) {
target.write(buffer, 0, len);
}
- } finally {
- try {
- if (source != null) {
- source.close();
- }
- if (target != null) {
- target.close();
- }
- } catch (IOException ignored) {
- // Ignore the IOException.
- }
}
}
}
diff --git a/tests/tests/widget/src/android/widget/cts/RemoteViewsWidgetTest.java b/tests/tests/widget/src/android/widget/cts/RemoteViewsWidgetTest.java
new file mode 100644
index 0000000..e1a77dc
--- /dev/null
+++ b/tests/tests/widget/src/android/widget/cts/RemoteViewsWidgetTest.java
@@ -0,0 +1,512 @@
+/*
+ * 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.widget.cts;
+
+import static org.junit.Assert.*;
+import static org.mockito.Matchers.*;
+import static org.mockito.Mockito.*;
+
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.appwidget.AppWidgetHost;
+import android.appwidget.AppWidgetHostView;
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.cts.util.PollingCheck;
+import android.cts.util.SystemUtil;
+import android.os.Bundle;
+import android.os.Process;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AbsListView;
+import android.widget.FrameLayout;
+import android.widget.ListView;
+import android.widget.RemoteViews;
+import android.widget.RemoteViewsService;
+import android.widget.StackView;
+import android.widget.cts.appwidget.MyAppWidgetProvider;
+import android.widget.cts.appwidget.MyAppWidgetService;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * Test {@link RemoteViews} that expect to operate within a {@link AppWidgetHostView} root.
+ */
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class RemoteViewsWidgetTest {
+ public static final String[] COUNTRY_LIST = new String[] {
+ "Argentina", "Australia", "Belize", "Botswana", "Brazil", "Cameroon", "China", "Cyprus",
+ "Denmark", "Djibouti", "Ethiopia", "Fiji", "Finland", "France", "Gabon", "Germany",
+ "Ghana", "Haiti", "Honduras", "Iceland", "India", "Indonesia", "Ireland", "Italy",
+ "Japan", "Kiribati", "Laos", "Lesotho", "Liberia", "Malaysia", "Mongolia", "Myanmar",
+ "Nauru", "Norway", "Oman", "Pakistan", "Philippines", "Portugal", "Romania", "Russia",
+ "Rwanda", "Singapore", "Slovakia", "Slovenia", "Somalia", "Swaziland", "Togo", "Tuvalu",
+ "Uganda", "Ukraine", "United States", "Vanuatu", "Venezuela", "Zimbabwe"
+ };
+
+ private static final String GRANT_BIND_APP_WIDGET_PERMISSION_COMMAND =
+ "appwidget grantbind --package android.widget.cts --user 0";
+
+ private static final String REVOKE_BIND_APP_WIDGET_PERMISSION_COMMAND =
+ "appwidget revokebind --package android.widget.cts --user 0";
+
+ private static final long TEST_TIMEOUT_MS = 5000;
+
+ @Rule
+ public ActivityTestRule<RemoteViewsCtsActivity> mActivityRule
+ = new ActivityTestRule<>(RemoteViewsCtsActivity.class);
+
+ private Instrumentation mInstrumentation;
+
+ private Context mContext;
+
+ private boolean mHasAppWidgets;
+
+ private AppWidgetHostView mAppWidgetHostView;
+
+ private int mAppWidgetId;
+
+ private StackView mStackView;
+
+ private ListView mListView;
+
+ private AppWidgetHost mAppWidgetHost;
+
+ @Before
+ public void setup() {
+ mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ mContext = mInstrumentation.getTargetContext();
+
+ mHasAppWidgets = hasAppWidgets();
+ if (!mHasAppWidgets) {
+ return;
+ }
+
+ // We want to bind widgets - run a shell command to grant bind permission to our
+ // package.
+ grantBindAppWidgetPermission();
+
+ mAppWidgetHost = new AppWidgetHost(mContext, 0);
+
+ mAppWidgetHost.deleteHost();
+ mAppWidgetHost.startListening();
+
+ // Configure the app widget provider behavior
+ final CountDownLatch providerCountDownLatch = new CountDownLatch(2);
+ MyAppWidgetProvider.configure(providerCountDownLatch, null, null);
+
+ // Grab the provider to be bound
+ final AppWidgetProviderInfo providerInfo = getAppWidgetProviderInfo();
+
+ // Allocate a widget id to bind
+ mAppWidgetId = mAppWidgetHost.allocateAppWidgetId();
+
+ // Bind the app widget
+ boolean isBinding = getAppWidgetManager().bindAppWidgetIdIfAllowed(mAppWidgetId,
+ providerInfo.getProfile(), providerInfo.provider, null);
+ assertTrue(isBinding);
+
+ // Wait for onEnabled and onUpdate calls on our provider
+ try {
+ assertTrue(providerCountDownLatch.await(TEST_TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ } catch (InterruptedException ie) {
+ fail(ie.getMessage());
+ }
+
+ // Configure the app widget service behavior
+ final CountDownLatch factoryCountDownLatch = new CountDownLatch(2);
+ RemoteViewsService.RemoteViewsFactory factory =
+ mock(RemoteViewsService.RemoteViewsFactory.class);
+ when(factory.getCount()).thenReturn(COUNTRY_LIST.length);
+ doAnswer(new Answer<RemoteViews>() {
+ @Override
+ public RemoteViews answer(InvocationOnMock invocation) throws Throwable {
+ final int position = (Integer) invocation.getArguments()[0];
+ RemoteViews remoteViews = new RemoteViews(mContext.getPackageName(),
+ R.layout.remoteviews_adapter_item);
+ remoteViews.setTextViewText(R.id.item, COUNTRY_LIST[position]);
+
+ // Set a fill-intent which will be used to fill-in the pending intent template
+ // which is set on the collection view in MyAppWidgetProvider.
+ Bundle extras = new Bundle();
+ extras.putString(MockURLSpanTestActivity.KEY_PARAM, COUNTRY_LIST[position]);
+ Intent fillInIntent = new Intent();
+ fillInIntent.putExtras(extras);
+ remoteViews.setOnClickFillInIntent(R.id.item, fillInIntent);
+
+ if (position == 0) {
+ factoryCountDownLatch.countDown();
+ }
+ return remoteViews;
+ }
+ }).when(factory).getViewAt(any(int.class));
+ when(factory.getViewTypeCount()).thenReturn(1);
+ MyAppWidgetService.setFactory(factory);
+
+ mInstrumentation.runOnMainSync(
+ () -> mAppWidgetHostView = mAppWidgetHost.createView(
+ mContext, mAppWidgetId, providerInfo));
+
+ // Wait our factory to be called to create the first item
+ try {
+ assertTrue(factoryCountDownLatch.await(TEST_TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ } catch (InterruptedException ie) {
+ fail(ie.getMessage());
+ }
+
+ // Add our host view to the activity behind this test. This is similar to how launchers
+ // add widgets to the on-screen UI.
+ ViewGroup root = (ViewGroup) mActivityRule.getActivity().findViewById(R.id.remoteView_host);
+ FrameLayout.MarginLayoutParams lp = new FrameLayout.MarginLayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT);
+ mAppWidgetHostView.setLayoutParams(lp);
+
+ mInstrumentation.runOnMainSync(() -> root.addView(mAppWidgetHostView));
+ }
+
+ @After
+ public void teardown() {
+ mAppWidgetHost.deleteHost();
+ revokeBindAppWidgetPermission();
+ }
+
+ private void grantBindAppWidgetPermission() {
+ try {
+ SystemUtil.runShellCommand(mInstrumentation, GRANT_BIND_APP_WIDGET_PERMISSION_COMMAND);
+ } catch (IOException e) {
+ fail("Error granting app widget permission. Command: "
+ + GRANT_BIND_APP_WIDGET_PERMISSION_COMMAND + ": ["
+ + e.getMessage() + "]");
+ }
+ }
+
+ private void revokeBindAppWidgetPermission() {
+ try {
+ SystemUtil.runShellCommand(mInstrumentation, REVOKE_BIND_APP_WIDGET_PERMISSION_COMMAND);
+ } catch (IOException e) {
+ fail("Error revoking app widget permission. Command: "
+ + REVOKE_BIND_APP_WIDGET_PERMISSION_COMMAND + ": ["
+ + e.getMessage() + "]");
+ }
+ }
+
+ private boolean hasAppWidgets() {
+ return mInstrumentation.getTargetContext().getPackageManager()
+ .hasSystemFeature(PackageManager.FEATURE_APP_WIDGETS);
+ }
+
+ private AppWidgetManager getAppWidgetManager() {
+ return (AppWidgetManager) mContext.getSystemService(Context.APPWIDGET_SERVICE);
+ }
+
+ private AppWidgetProviderInfo getAppWidgetProviderInfo() {
+ ComponentName firstComponentName = new ComponentName(mContext.getPackageName(),
+ MyAppWidgetProvider.class.getName());
+
+ return getProviderInfo(firstComponentName);
+ }
+
+ private AppWidgetProviderInfo getProviderInfo(ComponentName componentName) {
+ List<AppWidgetProviderInfo> providers = getAppWidgetManager().getInstalledProviders();
+
+ final int providerCount = providers.size();
+ for (int i = 0; i < providerCount; i++) {
+ AppWidgetProviderInfo provider = providers.get(i);
+ if (componentName.equals(provider.provider)
+ && Process.myUserHandle().equals(provider.getProfile())) {
+ return provider;
+
+ }
+ }
+
+ return null;
+ }
+
+ @Test
+ public void testInitialState() {
+ if (!mHasAppWidgets) {
+ return;
+ }
+
+ assertNotNull(mAppWidgetHostView);
+ mStackView = (StackView) mAppWidgetHostView.findViewById(R.id.remoteViews_stack);
+ assertNotNull(mStackView);
+
+ assertEquals(COUNTRY_LIST.length, mStackView.getCount());
+ assertEquals(0, mStackView.getDisplayedChild());
+ assertEquals(R.id.remoteViews_empty, mStackView.getEmptyView().getId());
+ }
+
+ private void verifySetDisplayedChild(int displayedChildIndex) {
+ final CountDownLatch updateLatch = new CountDownLatch(1);
+ MyAppWidgetProvider.configure(updateLatch, null, null);
+
+ // Create the intent to update the widget. Note that we're passing the value
+ // for displayed child index in the intent
+ Intent intent = new Intent(mContext, MyAppWidgetProvider.class);
+ intent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
+ intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, new int[] { mAppWidgetId });
+ intent.putExtra(MyAppWidgetProvider.KEY_DISPLAYED_CHILD_INDEX, displayedChildIndex);
+ mContext.sendBroadcast(intent);
+
+ // Wait until the update request has been processed
+ try {
+ assertTrue(updateLatch.await(TEST_TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ } catch (InterruptedException ie) {
+ fail(ie.getMessage());
+ }
+ // And wait until the underlying StackView has been updated to switch to the requested
+ // child
+ PollingCheck.waitFor(TEST_TIMEOUT_MS,
+ () -> mStackView.getDisplayedChild() == displayedChildIndex);
+ }
+
+ @Test
+ public void testSetDisplayedChild() {
+ if (!mHasAppWidgets) {
+ return;
+ }
+
+ mStackView = (StackView) mAppWidgetHostView.findViewById(R.id.remoteViews_stack);
+
+ verifySetDisplayedChild(4);
+ verifySetDisplayedChild(2);
+ verifySetDisplayedChild(6);
+ }
+
+ private void verifyShowCommand(String intentShowKey, int expectedDisplayedChild) {
+ final CountDownLatch updateLatch = new CountDownLatch(1);
+ MyAppWidgetProvider.configure(updateLatch, null, null);
+
+ // Create the intent to update the widget. Note that we're passing the "indication"
+ // which one of showNext / showPrevious APIs to execute in the intent that we're
+ // creating.
+ Intent intent = new Intent(mContext, MyAppWidgetProvider.class);
+ intent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
+ intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, new int[] { mAppWidgetId });
+ intent.putExtra(intentShowKey, true);
+ mContext.sendBroadcast(intent);
+
+ // Wait until the update request has been processed
+ try {
+ assertTrue(updateLatch.await(TEST_TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ } catch (InterruptedException ie) {
+ fail(ie.getMessage());
+ }
+ // And wait until the underlying StackView has been updated to switch to the expected
+ // child
+ PollingCheck.waitFor(TEST_TIMEOUT_MS,
+ () -> mStackView.getDisplayedChild() == expectedDisplayedChild);
+ }
+
+ @Test
+ public void testShowNextPrevious() {
+ if (!mHasAppWidgets) {
+ return;
+ }
+
+ mStackView = (StackView) mAppWidgetHostView.findViewById(R.id.remoteViews_stack);
+
+ // Two forward
+ verifyShowCommand(MyAppWidgetProvider.KEY_SHOW_NEXT, 1);
+ verifyShowCommand(MyAppWidgetProvider.KEY_SHOW_NEXT, 2);
+ // Four back (looping to the end of the adapter data)
+ verifyShowCommand(MyAppWidgetProvider.KEY_SHOW_PREVIOUS, 1);
+ verifyShowCommand(MyAppWidgetProvider.KEY_SHOW_PREVIOUS, 0);
+ verifyShowCommand(MyAppWidgetProvider.KEY_SHOW_PREVIOUS, COUNTRY_LIST.length - 1);
+ verifyShowCommand(MyAppWidgetProvider.KEY_SHOW_PREVIOUS, COUNTRY_LIST.length - 2);
+ // And three forward (looping to the start of the adapter data)
+ verifyShowCommand(MyAppWidgetProvider.KEY_SHOW_NEXT, COUNTRY_LIST.length - 1);
+ verifyShowCommand(MyAppWidgetProvider.KEY_SHOW_NEXT, 0);
+ verifyShowCommand(MyAppWidgetProvider.KEY_SHOW_NEXT, 1);
+ }
+
+ private void verifyItemClickIntents(int indexToClick) {
+ Instrumentation.ActivityMonitor am = mInstrumentation.addMonitor(
+ MockURLSpanTestActivity.class.getName(), null, false);
+
+ mStackView = (StackView) mAppWidgetHostView.findViewById(R.id.remoteViews_stack);
+ PollingCheck.waitFor(() -> mStackView.getCurrentView() != null);
+ final View initialView = mStackView.getCurrentView();
+ mInstrumentation.runOnMainSync(
+ () -> mStackView.performItemClick(initialView, indexToClick, 0L));
+
+ Activity newActivity = am.waitForActivityWithTimeout(TEST_TIMEOUT_MS);
+ assertNotNull(newActivity);
+ assertTrue(newActivity instanceof MockURLSpanTestActivity);
+ assertEquals(COUNTRY_LIST[indexToClick], ((MockURLSpanTestActivity) newActivity).getParam());
+ newActivity.finish();
+ }
+
+ @Test
+ public void testSetOnClickPendingIntent() {
+ if (!mHasAppWidgets) {
+ return;
+ }
+
+ verifyItemClickIntents(0);
+
+ // Switch to another child
+ verifySetDisplayedChild(2);
+ verifyItemClickIntents(2);
+
+ // And one more
+ verifyShowCommand(MyAppWidgetProvider.KEY_SHOW_NEXT, 3);
+ verifyItemClickIntents(3);
+ }
+
+ private class ListScrollListener implements AbsListView.OnScrollListener {
+ private CountDownLatch mLatchToNotify;
+
+ private int mTargetPosition;
+
+ public ListScrollListener(CountDownLatch latchToNotify, int targetPosition) {
+ mLatchToNotify = latchToNotify;
+ mTargetPosition = targetPosition;
+ }
+
+ @Override
+ public void onScrollStateChanged(AbsListView view, int scrollState) {
+ }
+
+ @Override
+ public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
+ int totalItemCount) {
+ if ((mTargetPosition >= firstVisibleItem) &&
+ (mTargetPosition <= (firstVisibleItem + visibleItemCount))) {
+ mLatchToNotify.countDown();
+ }
+ }
+ }
+
+ @Test
+ public void testSetScrollPosition() {
+ if (!mHasAppWidgets) {
+ return;
+ }
+
+ mListView = (ListView) mAppWidgetHostView.findViewById(R.id.remoteViews_list);
+
+ final CountDownLatch updateLatch = new CountDownLatch(1);
+ final AtomicBoolean scrollToPositionIsComplete = new AtomicBoolean(false);
+ // We're configuring our provider with three parameters:
+ // 1. The CountDownLatch to be notified when the provider has been enabled
+ // 2. The gating condition that waits until ListView has populated its content
+ // so that we can proceed to call setScrollPosition on it
+ // 3. The gating condition that waits until the setScrollPosition has completed
+ // its processing / scrolling so that we can proceed to call
+ // setRelativeScrollPosition on it
+ MyAppWidgetProvider.configure(updateLatch, () -> mListView.getChildCount() > 0,
+ scrollToPositionIsComplete::get);
+
+ final int positionToScrollTo = COUNTRY_LIST.length - 10;
+ final int scrollByAmount = COUNTRY_LIST.length / 2;
+ final int offsetScrollTarget = positionToScrollTo - scrollByAmount;
+
+ // Register the first scroll listener on our ListView. The listener will notify our latch
+ // when the "target" item comes into view. If that never happens, the latch will
+ // time out and fail the test.
+ final CountDownLatch scrollToPositionLatch = new CountDownLatch(1);
+ mListView.setOnScrollListener(
+ new ListScrollListener(scrollToPositionLatch, positionToScrollTo));
+
+ // Create the intent to update the widget. Note that we're passing the "indication"
+ // to switch to our ListView in the intent that we're creating.
+ Intent intent = new Intent(mContext, MyAppWidgetProvider.class);
+ intent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
+ intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, new int[] { mAppWidgetId });
+ intent.putExtra(MyAppWidgetProvider.KEY_SWITCH_TO_LIST, true);
+ intent.putExtra(MyAppWidgetProvider.KEY_SCROLL_POSITION, positionToScrollTo);
+ intent.putExtra(MyAppWidgetProvider.KEY_SCROLL_OFFSET, -scrollByAmount);
+ mContext.sendBroadcast(intent);
+
+ // Wait until the update request has been processed
+ try {
+ assertTrue(updateLatch.await(TEST_TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ } catch (InterruptedException ie) {
+ fail(ie.getMessage());
+ }
+ // And wait until the underlying ListView has been updated to be visible
+ PollingCheck.waitFor(TEST_TIMEOUT_MS, () -> mListView.getVisibility() == View.VISIBLE);
+
+ // Wait until our ListView has at least one visible child view. At that point we know
+ // that not only the host view is on screen, but also that the list view has completed
+ // its layout pass after having asked its adapter to populate the list content.
+ PollingCheck.waitFor(TEST_TIMEOUT_MS, () -> mListView.getChildCount() > 0);
+
+ // If we're on a really big display, we might be in a situation where the position
+ // we're going to scroll to is already visible. In that case the logic in the rest
+ // of this test will never fire off a listener callback and then fail the test.
+ final int lastVisiblePosition = mListView.getLastVisiblePosition();
+ if (positionToScrollTo <= lastVisiblePosition) {
+ return;
+ }
+
+ boolean result = false;
+ try {
+ result = scrollToPositionLatch.await(20, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ // ignore
+ }
+ assertTrue("Timed out while waiting for the target view to be scrolled into view", result);
+
+ if ((offsetScrollTarget < 0) ||
+ (offsetScrollTarget >= mListView.getFirstVisiblePosition())) {
+ // We can't scroll up because the target is either already visible or negative
+ return;
+ }
+
+ // Now register another scroll listener on our ListView. The listener will notify our latch
+ // when our new "target" item comes into view. If that never happens, the latch will
+ // time out and fail the test.
+ final CountDownLatch scrollByOffsetLatch = new CountDownLatch(1);
+ mListView.setOnScrollListener(
+ new ListScrollListener(scrollByOffsetLatch, offsetScrollTarget));
+
+ // Update our atomic boolean to "kick off" the widget provider request to call
+ // setRelativeScrollPosition on our RemoteViews
+ scrollToPositionIsComplete.set(true);
+ try {
+ result = scrollByOffsetLatch.await(20, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ // ignore
+ }
+ assertTrue("Timed out while waiting for the target view to be scrolled into view", result);
+ }
+}
diff --git a/tests/tests/widget/src/android/widget/cts/ResourceCursorAdapterTest.java b/tests/tests/widget/src/android/widget/cts/ResourceCursorAdapterTest.java
index aefca0f..e044f95 100644
--- a/tests/tests/widget/src/android/widget/cts/ResourceCursorAdapterTest.java
+++ b/tests/tests/widget/src/android/widget/cts/ResourceCursorAdapterTest.java
@@ -16,9 +16,6 @@
package android.widget.cts;
-import android.widget.cts.R;
-
-
import android.content.Context;
import android.database.Cursor;
import android.database.MatrixCursor;
@@ -28,6 +25,7 @@
import android.view.View;
import android.view.ViewGroup;
import android.widget.ResourceCursorAdapter;
+import android.widget.cts.R;
/**
* Test {@link ResourceCursorAdapter}.
diff --git a/tests/tests/widget/src/android/widget/cts/ResourceCursorTreeAdapterTest.java b/tests/tests/widget/src/android/widget/cts/ResourceCursorTreeAdapterTest.java
index e801e93..e09ca08 100644
--- a/tests/tests/widget/src/android/widget/cts/ResourceCursorTreeAdapterTest.java
+++ b/tests/tests/widget/src/android/widget/cts/ResourceCursorTreeAdapterTest.java
@@ -16,9 +16,6 @@
package android.widget.cts;
-import android.widget.cts.R;
-
-
import android.content.Context;
import android.database.Cursor;
import android.database.MatrixCursor;
@@ -28,6 +25,7 @@
import android.view.View;
import android.view.ViewGroup;
import android.widget.ResourceCursorTreeAdapter;
+import android.widget.cts.R;
/**
* Test {@link ResourceCursorTreeAdapter}.
diff --git a/tests/tests/widget/src/android/widget/cts/ScrollViewCtsActivity.java b/tests/tests/widget/src/android/widget/cts/ScrollViewCtsActivity.java
index b2b51c6..52f7e0f 100644
--- a/tests/tests/widget/src/android/widget/cts/ScrollViewCtsActivity.java
+++ b/tests/tests/widget/src/android/widget/cts/ScrollViewCtsActivity.java
@@ -16,10 +16,9 @@
package android.widget.cts;
-import android.widget.cts.R;
-
import android.app.Activity;
import android.os.Bundle;
+import android.widget.cts.R;
public class ScrollViewCtsActivity extends Activity {
@Override
diff --git a/tests/tests/widget/src/android/widget/cts/ScrollViewTest.java b/tests/tests/widget/src/android/widget/cts/ScrollViewTest.java
index 26510f27..8e38cd5 100644
--- a/tests/tests/widget/src/android/widget/cts/ScrollViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/ScrollViewTest.java
@@ -20,12 +20,6 @@
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
-import android.widget.FrameLayout;
-import android.widget.cts.R;
-
-
-import org.xmlpull.v1.XmlPullParser;
-
import android.app.Activity;
import android.content.Context;
import android.cts.util.PollingCheck;
@@ -34,13 +28,16 @@
import android.test.UiThreadTest;
import android.util.AttributeSet;
import android.util.Xml;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
import android.view.View;
-import android.view.ViewGroup;
import android.view.View.MeasureSpec;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
import android.widget.ScrollView;
import android.widget.TextView;
+import android.widget.cts.R;
+import android.widget.cts.util.TestUtils;
+
+import org.xmlpull.v1.XmlPullParser;
/**
* Test {@link ScrollView}.
@@ -75,11 +72,10 @@
mScrollView = (MyScrollView) mActivity.findViewById(R.id.scroll_view);
// calculate pixel positions from dpi constants.
- final float density = getActivity().getResources().getDisplayMetrics().density;
- mItemWidth = (int) (ITEM_WIDTH_DPI * density + 0.5f);
- mItemHeight = (int) (ITEM_HEIGHT_DPI * density + 0.5f);
- mPageWidth = (int) (PAGE_WIDTH_DPI * density + 0.5f);
- mPageHeight = (int) (PAGE_HEIGHT_DPI * density + 0.5f);
+ mItemWidth = TestUtils.dpToPx(mActivity, ITEM_WIDTH_DPI);
+ mItemHeight = TestUtils.dpToPx(mActivity, ITEM_HEIGHT_DPI);
+ mPageWidth = TestUtils.dpToPx(mActivity, PAGE_WIDTH_DPI);
+ mPageHeight = TestUtils.dpToPx(mActivity, PAGE_HEIGHT_DPI);
mScrollBottom = mItemHeight * ITEM_COUNT - mPageHeight;
mScrollRight = mItemWidth - mPageWidth;
@@ -829,55 +825,34 @@
}
if (fromY != toY) {
- new PollingCheck() {
- @Override
- protected boolean check() {
- return isInRange(mScrollView.getScrollY(), fromY, toY);
- }
- }.run();
+ PollingCheck.waitFor(() -> isInRange(mScrollView.getScrollY(), fromY, toY));
}
if (fromX != toX) {
- new PollingCheck() {
- @Override
- protected boolean check() {
- return isInRange(mScrollView.getScrollX(), fromX, toX);
- }
- }.run();
+ PollingCheck.waitFor(() -> isInRange(mScrollView.getScrollX(), fromX, toX));
}
- new PollingCheck() {
- @Override
- protected boolean check() {
- return toX == mScrollView.getScrollX() && toY == mScrollView.getScrollY();
- }
- }.run();
+ PollingCheck.waitFor(
+ () -> toX == mScrollView.getScrollX() && toY == mScrollView.getScrollY());
}
private void pollingCheckFling(final int startPosition, final boolean movingDown) {
- new PollingCheck() {
- @Override
- protected boolean check() {
- if (movingDown) {
- return mScrollView.getScrollY() > startPosition;
- }
- return mScrollView.getScrollY() < startPosition;
+ PollingCheck.waitFor(() -> {
+ if (movingDown) {
+ return mScrollView.getScrollY() > startPosition;
}
- };
+ return mScrollView.getScrollY() < startPosition;
+ });
- new PollingCheck() {
- private int mPreviousScrollY = mScrollView.getScrollY();
-
- @Override
- protected boolean check() {
- if (mScrollView.getScrollY() == mPreviousScrollY) {
- return true;
- } else {
- mPreviousScrollY = mScrollView.getScrollY();
- return false;
- }
+ final int[] previousScrollY = new int[] { mScrollView.getScrollY() };
+ PollingCheck.waitFor(() -> {
+ if (mScrollView.getScrollY() == previousScrollY[0]) {
+ return true;
+ } else {
+ previousScrollY[0] = mScrollView.getScrollY();
+ return false;
}
- }.run();
+ });
}
public static class MyView extends View {
diff --git a/hostsidetests/services/activitymanager/app/src/android/server/app/SlowCreateActivity.java b/tests/tests/widget/src/android/widget/cts/SearchViewCtsActivity.java
similarity index 67%
copy from hostsidetests/services/activitymanager/app/src/android/server/app/SlowCreateActivity.java
copy to tests/tests/widget/src/android/widget/cts/SearchViewCtsActivity.java
index 481f5be..5da8a32 100644
--- a/hostsidetests/services/activitymanager/app/src/android/server/app/SlowCreateActivity.java
+++ b/tests/tests/widget/src/android/widget/cts/SearchViewCtsActivity.java
@@ -14,17 +14,22 @@
* limitations under the License.
*/
-package android.server.app;
+package android.widget.cts;
import android.app.Activity;
import android.os.Bundle;
+import android.widget.SearchView;
-public class SlowCreateActivity extends Activity {
+/**
+ * A minimal application for {@link SearchView} test.
+ */
+public class SearchViewCtsActivity extends Activity {
+ /**
+ * Called with the activity is first created.
+ */
@Override
- protected void onCreate(Bundle savedInstanceState) {
- try {
- Thread.sleep(2000);
- } catch(InterruptedException e) {}
+ public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ setContentView(R.layout.searchview_layout);
}
}
diff --git a/tests/tests/widget/src/android/widget/cts/SearchViewTest.java b/tests/tests/widget/src/android/widget/cts/SearchViewTest.java
new file mode 100644
index 0000000..2d0c208
--- /dev/null
+++ b/tests/tests/widget/src/android/widget/cts/SearchViewTest.java
@@ -0,0 +1,242 @@
+/*
+ * 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.widget.cts;
+
+import static org.mockito.Mockito.*;
+
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.content.res.Resources;
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.UiThreadTest;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.text.InputType;
+import android.text.TextUtils;
+import android.view.inputmethod.EditorInfo;
+import android.widget.SearchView;
+
+/**
+ * Test {@link SearchView}.
+ */
+@MediumTest
+public class SearchViewTest extends ActivityInstrumentationTestCase2<SearchViewCtsActivity> {
+ private Instrumentation mInstrumentation;
+ private Activity mActivity;
+ private SearchView mSearchView;
+
+ public SearchViewTest() {
+ super("android.widget.cts", SearchViewCtsActivity.class);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ mInstrumentation = getInstrumentation();
+ mActivity = getActivity();
+ mSearchView = (SearchView) mActivity.findViewById(R.id.search_view);
+ }
+
+ @UiThreadTest
+ public void testConstructor() {
+ new SearchView(mActivity);
+
+ new SearchView(mActivity, null);
+
+ new SearchView(mActivity, null, android.R.attr.searchViewStyle);
+
+ new SearchView(mActivity, null, 0, android.R.style.Widget_Material_SearchView);
+
+ new SearchView(mActivity, null, 0, android.R.style.Widget_Material_Light_SearchView);
+ }
+
+ @UiThreadTest
+ public void testAttributesFromXml() {
+ SearchView searchViewWithAttributes =
+ (SearchView) mActivity.findViewById(R.id.search_view_with_defaults);
+ assertEquals(mActivity.getString(R.string.search_query_hint),
+ searchViewWithAttributes.getQueryHint());
+ assertFalse(searchViewWithAttributes.isIconfiedByDefault());
+ assertEquals(EditorInfo.TYPE_TEXT_FLAG_CAP_CHARACTERS | EditorInfo.TYPE_CLASS_TEXT,
+ searchViewWithAttributes.getInputType());
+ assertEquals(EditorInfo.IME_ACTION_DONE, searchViewWithAttributes.getImeOptions());
+ assertEquals(mActivity.getResources().getDimensionPixelSize(R.dimen.search_view_max_width),
+ searchViewWithAttributes.getMaxWidth());
+ }
+
+ public void testAccessIconified() {
+ mInstrumentation.runOnMainSync(() -> mSearchView.setIconified(true));
+ assertTrue(mSearchView.isIconified());
+
+ mInstrumentation.runOnMainSync(() -> mSearchView.setIconified(false));
+ assertFalse(mSearchView.isIconified());
+ }
+
+ public void testAccessIconifiedByDefault() {
+ mInstrumentation.runOnMainSync(() -> mSearchView.setIconifiedByDefault(true));
+ assertTrue(mSearchView.isIconfiedByDefault());
+
+ mInstrumentation.runOnMainSync(() -> mSearchView.setIconifiedByDefault(false));
+ assertFalse(mSearchView.isIconfiedByDefault());
+ }
+
+ public void testDenyIconifyingNonInconifiableView() {
+ mInstrumentation.runOnMainSync(() -> {
+ mSearchView.setIconifiedByDefault(false);
+ mSearchView.setIconified(false);
+ });
+
+ mInstrumentation.runOnMainSync(() -> mSearchView.setIconified(true));
+ mInstrumentation.waitForIdleSync();
+
+ // Since our search view is marked with iconifiedByDefault=false, call to setIconified
+ // with true us going to be ignored, as detailed in the class-level documentation of
+ // SearchView.
+ assertFalse(mSearchView.isIconified());
+ }
+
+ public void testDenyIconifyingInconifiableView() {
+ mInstrumentation.runOnMainSync(() -> {
+ mSearchView.setIconifiedByDefault(true);
+ mSearchView.setIconified(false);
+ });
+
+ final SearchView.OnCloseListener mockDenyCloseListener =
+ mock(SearchView.OnCloseListener.class);
+ when(mockDenyCloseListener.onClose()).thenReturn(Boolean.TRUE);
+ mSearchView.setOnCloseListener(mockDenyCloseListener);
+
+ mInstrumentation.runOnMainSync(() -> mSearchView.setIconified(true));
+ mInstrumentation.waitForIdleSync();
+
+ // Our mock listener is configured to return true from its onClose, thereby preventing
+ // the iconify request to be completed. Check that the listener was called and that the
+ // search view is not iconified.
+ verify(mockDenyCloseListener, times(1)).onClose();
+ assertFalse(mSearchView.isIconified());
+ }
+
+ public void testAllowIconifyingInconifiableView() {
+ mInstrumentation.runOnMainSync(() -> {
+ mSearchView.setIconifiedByDefault(true);
+ mSearchView.setIconified(false);
+ });
+
+ final SearchView.OnCloseListener mockAllowCloseListener =
+ mock(SearchView.OnCloseListener.class);
+ when(mockAllowCloseListener.onClose()).thenReturn(Boolean.FALSE);
+ mSearchView.setOnCloseListener(mockAllowCloseListener);
+
+ mInstrumentation.runOnMainSync(() -> mSearchView.setIconified(true));
+ mInstrumentation.waitForIdleSync();
+
+ // Our mock listener is configured to return false from its onClose, thereby allowing
+ // the iconify request to be completed. Check that the listener was called and that the
+ // search view is not iconified.
+ verify(mockAllowCloseListener, times(1)).onClose();
+ assertTrue(mSearchView.isIconified());
+ }
+
+ public void testAccessMaxWidth() {
+ final Resources res = mActivity.getResources();
+ final int maxWidth1 = res.getDimensionPixelSize(R.dimen.search_view_max_width);
+ final int maxWidth2 = res.getDimensionPixelSize(R.dimen.search_view_max_width2);
+
+ // Set search view to not be iconified before running max-width tests
+ mInstrumentation.runOnMainSync(() -> mSearchView.setIconified(false));
+
+ mInstrumentation.runOnMainSync(() -> mSearchView.setMaxWidth(maxWidth1));
+ mInstrumentation.waitForIdleSync();
+ assertEquals(maxWidth1, mSearchView.getMaxWidth());
+ assertTrue(mSearchView.getWidth() <= maxWidth1);
+
+ mInstrumentation.runOnMainSync(() -> mSearchView.setMaxWidth(maxWidth2));
+ mInstrumentation.waitForIdleSync();
+ assertEquals(maxWidth2, mSearchView.getMaxWidth());
+ assertTrue(mSearchView.getWidth() <= maxWidth2);
+ }
+
+ public void testAccessQuery() {
+ mInstrumentation.runOnMainSync(() -> mSearchView.setIconified(false));
+
+ final SearchView.OnQueryTextListener mockQueryTextListener =
+ mock(SearchView.OnQueryTextListener.class);
+ when(mockQueryTextListener.onQueryTextSubmit(anyString())).thenReturn(Boolean.TRUE);
+ mSearchView.setOnQueryTextListener(mockQueryTextListener);
+
+ mInstrumentation.runOnMainSync(() -> mSearchView.setQuery("alpha", false));
+ assertTrue(TextUtils.equals("alpha", mSearchView.getQuery()));
+ // Since we passed false as the second parameter to setQuery, our query text listener
+ // should have been invoked only with text change
+ verify(mockQueryTextListener, times(1)).onQueryTextChange("alpha");
+ verify(mockQueryTextListener, never()).onQueryTextSubmit(anyString());
+
+ mInstrumentation.runOnMainSync(() -> mSearchView.setQuery("beta", true));
+ assertTrue(TextUtils.equals("beta", mSearchView.getQuery()));
+ // Since we passed true as the second parameter to setQuery, our query text listener
+ // should have been invoked on both callbacks
+ verify(mockQueryTextListener, times(1)).onQueryTextChange("beta");
+ verify(mockQueryTextListener, times(1)).onQueryTextSubmit("beta");
+
+ mInstrumentation.runOnMainSync(() -> mSearchView.setQuery("gamma", true));
+ assertTrue(TextUtils.equals("gamma", mSearchView.getQuery()));
+ // Since we passed true as the second parameter to setQuery, our query text listener
+ // should have been invoked on both callbacks
+ verify(mockQueryTextListener, times(1)).onQueryTextChange("gamma");
+ verify(mockQueryTextListener, times(1)).onQueryTextSubmit("gamma");
+
+ verifyNoMoreInteractions(mockQueryTextListener);
+ }
+
+ public void testAccessQueryHint() {
+ mInstrumentation.runOnMainSync(() -> mSearchView.setQueryHint("hint 1"));
+ assertTrue(TextUtils.equals("hint 1", mSearchView.getQueryHint()));
+
+ mInstrumentation.runOnMainSync(() -> mSearchView.setQueryHint("hint 2"));
+ assertTrue(TextUtils.equals("hint 2", mSearchView.getQueryHint()));
+ }
+
+ public void testAccessInputType() {
+ mInstrumentation.runOnMainSync(() ->
+ mSearchView.setInputType(InputType.TYPE_CLASS_NUMBER
+ | InputType.TYPE_NUMBER_FLAG_DECIMAL
+ | InputType.TYPE_NUMBER_FLAG_SIGNED));
+ assertEquals(InputType.TYPE_CLASS_NUMBER
+ | InputType.TYPE_NUMBER_FLAG_DECIMAL
+ | InputType.TYPE_NUMBER_FLAG_SIGNED, mSearchView.getInputType());
+
+ mInstrumentation.runOnMainSync(() ->
+ mSearchView.setInputType(InputType.TYPE_CLASS_TEXT
+ | InputType.TYPE_TEXT_FLAG_CAP_WORDS));
+ assertEquals(InputType.TYPE_CLASS_TEXT
+ | InputType.TYPE_TEXT_FLAG_CAP_WORDS, mSearchView.getInputType());
+
+ mInstrumentation.runOnMainSync(() -> mSearchView.setInputType(InputType.TYPE_CLASS_PHONE));
+ assertEquals(InputType.TYPE_CLASS_PHONE, mSearchView.getInputType());
+ }
+
+ public void testAccessImeOptions() {
+ mInstrumentation.runOnMainSync(() -> mSearchView.setImeOptions(EditorInfo.IME_ACTION_GO));
+ assertEquals(EditorInfo.IME_ACTION_GO, mSearchView.getImeOptions());
+
+ mInstrumentation.runOnMainSync(() -> mSearchView.setImeOptions(EditorInfo.IME_ACTION_DONE));
+ assertEquals(EditorInfo.IME_ACTION_DONE, mSearchView.getImeOptions());
+
+ mInstrumentation.runOnMainSync(() -> mSearchView.setImeOptions(EditorInfo.IME_NULL));
+ assertEquals(EditorInfo.IME_NULL, mSearchView.getImeOptions());
+ }
+}
diff --git a/tests/tests/widget/src/android/widget/cts/SearchView_CursorTest.java b/tests/tests/widget/src/android/widget/cts/SearchView_CursorTest.java
new file mode 100644
index 0000000..1bebb01
--- /dev/null
+++ b/tests/tests/widget/src/android/widget/cts/SearchView_CursorTest.java
@@ -0,0 +1,188 @@
+/*
+ * 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.widget.cts;
+
+import static org.mockito.Matchers.*;
+import static org.mockito.Mockito.*;
+
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.cts.util.PollingCheck;
+import android.cts.util.CtsTouchUtils;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.provider.BaseColumns;
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.text.TextUtils;
+import android.widget.CursorAdapter;
+import android.widget.SearchView;
+import android.widget.SimpleCursorAdapter;
+
+/**
+ * Test {@link SearchView} with {@link Cursor}-backed suggestions adapter.
+ */
+@MediumTest
+public class SearchView_CursorTest extends ActivityInstrumentationTestCase2<SearchViewCtsActivity> {
+ private Instrumentation mInstrumentation;
+ private Activity mActivity;
+ private SearchView mSearchView;
+
+ private static final String TEXT_COLUMN_NAME = "text";
+ private String[] mTextContent;
+
+ private CursorAdapter mSuggestionsAdapter;
+
+ protected class MyQueryTextListener implements SearchView.OnQueryTextListener {
+ @Override
+ public boolean onQueryTextSubmit(String s) {
+ return false;
+ }
+
+ @Override
+ public boolean onQueryTextChange(String s) {
+ if (mSuggestionsAdapter == null) {
+ return false;
+ }
+
+ final MatrixCursor c = new MatrixCursor(
+ new String[] { BaseColumns._ID, TEXT_COLUMN_NAME} );
+ for (int i = 0; i < mTextContent.length; i++) {
+ if (mTextContent[i].toLowerCase().startsWith(s.toLowerCase())) {
+ c.addRow(new Object[]{i, mTextContent[i]});
+ }
+ }
+ mSuggestionsAdapter.swapCursor(c);
+ return false;
+ }
+ }
+
+ protected class MySuggestionListener implements SearchView.OnSuggestionListener {
+ @Override
+ public boolean onSuggestionSelect(int position) {
+ return false;
+ }
+
+ @Override
+ public boolean onSuggestionClick(int position) {
+ if (mSuggestionsAdapter != null) {
+ final Cursor cursor = mSuggestionsAdapter.getCursor();
+ if (cursor != null) {
+ cursor.moveToPosition(position);
+ mSearchView.setQuery(cursor.getString(1), false);
+ }
+ }
+ return false;
+ }
+ }
+
+ public SearchView_CursorTest() {
+ super("android.widget.cts", SearchViewCtsActivity.class);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ mInstrumentation = getInstrumentation();
+ mActivity = getActivity();
+ mSearchView = (SearchView) mActivity.findViewById(R.id.search_view);
+
+ // Local test data for the tests
+ mTextContent = new String[] { "Akon", "Bono", "Ciara", "Dido", "Diplo" };
+
+ mInstrumentation.runOnMainSync(() -> {
+ // Use an adapter with our custom layout for each entry. The adapter "maps"
+ // the content of the text column of our cursor to the @id/text1 view in the
+ // layout.
+ mSuggestionsAdapter = new SimpleCursorAdapter(
+ mActivity,
+ R.layout.searchview_suggestion_item,
+ null,
+ new String[] { TEXT_COLUMN_NAME },
+ new int[] { android.R.id.text1 },
+ CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);
+ mSearchView.setSuggestionsAdapter(mSuggestionsAdapter);
+ });
+ }
+
+ public void testSuggestionFiltering() {
+ final SearchView.OnQueryTextListener mockQueryTextListener =
+ spy(new MyQueryTextListener());
+ when(mockQueryTextListener.onQueryTextChange(anyString())).thenCallRealMethod();
+
+ mInstrumentation.runOnMainSync(() -> {
+ mSearchView.setIconifiedByDefault(false);
+ mSearchView.setOnQueryTextListener(mockQueryTextListener);
+ mSearchView.requestFocus();
+ });
+
+ assertTrue(mSearchView.hasFocus());
+ assertEquals(mSuggestionsAdapter, mSearchView.getSuggestionsAdapter());
+
+ mInstrumentation.runOnMainSync(() -> mSearchView.setQuery("Bon", false));
+ verify(mockQueryTextListener, times(1)).onQueryTextChange("Bon");
+
+ mInstrumentation.runOnMainSync(() -> mSearchView.setQuery("Di", false));
+ verify(mockQueryTextListener, times(1)).onQueryTextChange("Di");
+ }
+
+ public void testSuggestionSelection() {
+ final SearchView.OnSuggestionListener mockSuggestionListener =
+ spy(new MySuggestionListener());
+ when(mockSuggestionListener.onSuggestionClick(anyInt())).thenCallRealMethod();
+
+ final SearchView.OnQueryTextListener mockQueryTextListener =
+ spy(new MyQueryTextListener());
+ when(mockQueryTextListener.onQueryTextChange(anyString())).thenCallRealMethod();
+
+ mInstrumentation.runOnMainSync(() -> {
+ mSearchView.setIconifiedByDefault(false);
+ mSearchView.setOnQueryTextListener(mockQueryTextListener);
+ mSearchView.setOnSuggestionListener(mockSuggestionListener);
+ mSearchView.requestFocus();
+ });
+
+ assertTrue(mSearchView.hasFocus());
+ assertEquals(mSuggestionsAdapter, mSearchView.getSuggestionsAdapter());
+
+ mInstrumentation.runOnMainSync(() -> mSearchView.setQuery("Di", false));
+ mInstrumentation.waitForIdleSync();
+ verify(mockQueryTextListener, times(1)).onQueryTextChange("Di");
+
+ // Emulate click on the first suggestion - which should be Dido
+ final int suggestionRowHeight = mActivity.getResources().getDimensionPixelSize(
+ R.dimen.search_view_suggestion_row_height);
+ CtsTouchUtils.emulateTapOnScreen(mInstrumentation, mSearchView, mSearchView.getWidth() / 2,
+ mSearchView.getHeight() + suggestionRowHeight / 2);
+
+ // At this point we expect the click on the first suggestion to have activated a sequence
+ // of events that ends up in our suggestion listener that sets the full suggestion text
+ // as the current query. Some parts of this sequence of events are asynchronous, and those
+ // are not "caught" by Instrumentation.waitForIdleSync - which is in general not a very
+ // reliable way to wait for everything to be completed. As such, we are using our own
+ // polling check mechanism to wait until the search view's query is the fully completed
+ // suggestion for Dido. This check will time out and fail after a few seconds if anything
+ // goes wrong during the processing of the emulated tap and the code never gets to our
+ // suggestion listener
+ PollingCheck.waitFor(() -> TextUtils.equals("Dido", mSearchView.getQuery()));
+
+ // Just to be sure, verify that our spy suggestion listener was called
+ verify(mockSuggestionListener, times(1)).onSuggestionClick(0);
+ verifyNoMoreInteractions(mockSuggestionListener);
+ }
+}
diff --git a/tests/tests/widget/src/android/widget/cts/SeekBarCtsActivity.java b/tests/tests/widget/src/android/widget/cts/SeekBarCtsActivity.java
index e22b910..5dcbe56 100644
--- a/tests/tests/widget/src/android/widget/cts/SeekBarCtsActivity.java
+++ b/tests/tests/widget/src/android/widget/cts/SeekBarCtsActivity.java
@@ -16,8 +16,6 @@
package android.widget.cts;
-import android.widget.cts.R;
-
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
@@ -28,7 +26,7 @@
*/
public class SeekBarCtsActivity extends Activity {
/**
- * Called with the activity is first created.
+ * Called when the activity is first created.
*/
@Override
public void onCreate(Bundle savedInstanceState) {
diff --git a/tests/tests/widget/src/android/widget/cts/SeekBarTest.java b/tests/tests/widget/src/android/widget/cts/SeekBarTest.java
index 9182bcc..fb9a5e9 100644
--- a/tests/tests/widget/src/android/widget/cts/SeekBarTest.java
+++ b/tests/tests/widget/src/android/widget/cts/SeekBarTest.java
@@ -16,20 +16,20 @@
package android.widget.cts;
-import android.widget.cts.R;
-
+import static org.mockito.Mockito.*;
import android.app.Activity;
import android.app.Instrumentation;
import android.os.SystemClock;
import android.test.ActivityInstrumentationTestCase2;
+import android.test.suitebuilder.annotation.SmallTest;
import android.view.MotionEvent;
import android.widget.SeekBar;
-import android.widget.SeekBar.OnSeekBarChangeListener;
/**
* Test {@link SeekBar}.
*/
+@SmallTest
public class SeekBarTest extends ActivityInstrumentationTestCase2<SeekBarCtsActivity> {
private SeekBar mSeekBar;
@@ -55,13 +55,21 @@
new SeekBar(mActivity, null);
new SeekBar(mActivity, null, android.R.attr.seekBarStyle);
+
+ new SeekBar(mActivity, null, 0, android.R.style.Widget_DeviceDefault_SeekBar);
+
+ new SeekBar(mActivity, null, 0, android.R.style.Widget_DeviceDefault_Light_SeekBar);
+
+ new SeekBar(mActivity, null, 0, android.R.style.Widget_Material_SeekBar);
+
+ new SeekBar(mActivity, null, 0, android.R.style.Widget_Material_Light_SeekBar);
}
public void testSetOnSeekBarChangeListener() {
- MockOnSeekBarListener listener = new MockOnSeekBarListener();
+ SeekBar.OnSeekBarChangeListener mockChangeListener =
+ mock(SeekBar.OnSeekBarChangeListener.class);
- mSeekBar.setOnSeekBarChangeListener(listener);
- listener.reset();
+ mSeekBar.setOnSeekBarChangeListener(mockChangeListener);
long downTime = SystemClock.uptimeMillis();
long eventTime = SystemClock.uptimeMillis();
int seekBarXY[] = new int[2];
@@ -70,66 +78,28 @@
seekBarXY[0], seekBarXY[1], 0);
mInstrumentation.sendPointerSync(event);
mInstrumentation.waitForIdleSync();
- assertTrue(listener.hasCalledOnStartTrackingTouch());
+ verify(mockChangeListener, times(1)).onStartTrackingTouch(mSeekBar);
// while starting to track, the progress is changed also
- assertTrue(listener.hasCalledOnProgressChanged());
+ verify(mockChangeListener, atLeastOnce()).onProgressChanged(eq(mSeekBar), anyInt(),
+ eq(true));
- listener.reset();
+ reset(mockChangeListener);
downTime = SystemClock.uptimeMillis();
eventTime = SystemClock.uptimeMillis();
event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_MOVE,
seekBarXY[0] + (mSeekBar.getWidth() >> 1), seekBarXY[1], 0);
mInstrumentation.sendPointerSync(event);
mInstrumentation.waitForIdleSync();
- assertTrue(listener.hasCalledOnProgressChanged());
+ verify(mockChangeListener, atLeastOnce()).onProgressChanged(eq(mSeekBar), anyInt(),
+ eq(true));
- listener.reset();
+ reset(mockChangeListener);
downTime = SystemClock.uptimeMillis();
eventTime = SystemClock.uptimeMillis();
event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_UP,
seekBarXY[0] + (mSeekBar.getWidth() >> 1), seekBarXY[1], 0);
mInstrumentation.sendPointerSync(event);
mInstrumentation.waitForIdleSync();
- assertTrue(listener.hasCalledOnStopTrackingTouch());
-
- mSeekBar.setOnSeekBarChangeListener(null);
- }
-
- private class MockOnSeekBarListener implements OnSeekBarChangeListener {
- private boolean mHasCalledOnProgressChanged;
-
- private boolean mHasCalledOnStartTrackingTouch;
-
- private boolean mHasCalledOnStopTrackingTouch;
-
- public boolean hasCalledOnProgressChanged() {
- return mHasCalledOnProgressChanged;
- }
-
- public boolean hasCalledOnStartTrackingTouch() {
- return mHasCalledOnStartTrackingTouch;
- }
-
- public boolean hasCalledOnStopTrackingTouch() {
- return mHasCalledOnStopTrackingTouch;
- }
-
- public void reset(){
- mHasCalledOnProgressChanged = false;
- mHasCalledOnStartTrackingTouch = false;
- mHasCalledOnStopTrackingTouch = false;
- }
-
- public void onProgressChanged(SeekBar seekBar, int progress, boolean fromTouch) {
- mHasCalledOnProgressChanged = true;
- }
-
- public void onStartTrackingTouch(SeekBar seekBar) {
- mHasCalledOnStartTrackingTouch = true;
- }
-
- public void onStopTrackingTouch(SeekBar seekBar) {
- mHasCalledOnStopTrackingTouch = true;
- }
+ verify(mockChangeListener, times(1)).onStopTrackingTouch(mSeekBar);
}
}
diff --git a/tests/tests/widget/src/android/widget/cts/SimpleAdapterTest.java b/tests/tests/widget/src/android/widget/cts/SimpleAdapterTest.java
index 5a9af25..9911b55 100644
--- a/tests/tests/widget/src/android/widget/cts/SimpleAdapterTest.java
+++ b/tests/tests/widget/src/android/widget/cts/SimpleAdapterTest.java
@@ -17,8 +17,6 @@
package android.widget.cts;
import android.R;
-
-
import android.content.Context;
import android.content.res.Resources.Theme;
import android.cts.util.WidgetTestUtils;
@@ -31,9 +29,9 @@
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.SimpleAdapter;
+import android.widget.SimpleAdapter.ViewBinder;
import android.widget.TextView;
import android.widget.TwoLineListItem;
-import android.widget.SimpleAdapter.ViewBinder;
import java.util.ArrayList;
import java.util.HashMap;
diff --git a/tests/tests/widget/src/android/widget/cts/SimpleCursorAdapterTest.java b/tests/tests/widget/src/android/widget/cts/SimpleCursorAdapterTest.java
index 469e581..9182a48 100644
--- a/tests/tests/widget/src/android/widget/cts/SimpleCursorAdapterTest.java
+++ b/tests/tests/widget/src/android/widget/cts/SimpleCursorAdapterTest.java
@@ -16,9 +16,6 @@
package android.widget.cts;
-import android.widget.cts.R;
-
-
import android.content.Context;
import android.cts.util.WidgetTestUtils;
import android.database.Cursor;
@@ -33,9 +30,10 @@
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.SimpleCursorAdapter;
-import android.widget.TextView;
import android.widget.SimpleCursorAdapter.CursorToStringConverter;
import android.widget.SimpleCursorAdapter.ViewBinder;
+import android.widget.TextView;
+import android.widget.cts.R;
import java.io.IOException;
import java.io.InputStream;
diff --git a/tests/tests/widget/src/android/widget/cts/SimpleCursorTreeAdapterTest.java b/tests/tests/widget/src/android/widget/cts/SimpleCursorTreeAdapterTest.java
index 02941fa..286bc2a 100644
--- a/tests/tests/widget/src/android/widget/cts/SimpleCursorTreeAdapterTest.java
+++ b/tests/tests/widget/src/android/widget/cts/SimpleCursorTreeAdapterTest.java
@@ -16,9 +16,6 @@
package android.widget.cts;
-import android.widget.cts.R;
-
-
import android.content.Context;
import android.cts.util.WidgetTestUtils;
import android.database.Cursor;
@@ -31,6 +28,7 @@
import android.widget.ImageView;
import android.widget.SimpleCursorTreeAdapter;
import android.widget.TextView;
+import android.widget.cts.R;
/**
* Test {@link SimpleCursorTreeAdapter}.
diff --git a/tests/tests/widget/src/android/widget/cts/SimpleExpandableListAdapterTest.java b/tests/tests/widget/src/android/widget/cts/SimpleExpandableListAdapterTest.java
index 674f427..782c85e 100644
--- a/tests/tests/widget/src/android/widget/cts/SimpleExpandableListAdapterTest.java
+++ b/tests/tests/widget/src/android/widget/cts/SimpleExpandableListAdapterTest.java
@@ -17,13 +17,10 @@
package android.widget.cts;
import android.R;
-
-
import android.content.Context;
import android.test.InstrumentationTestCase;
import android.view.LayoutInflater;
import android.view.View;
-import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.SimpleExpandableListAdapter;
import android.widget.TextView;
@@ -31,7 +28,6 @@
import java.util.ArrayList;
import java.util.HashMap;
-import java.util.List;
/**
* Test {@link SimpleExpandableListAdapter}.
diff --git a/tests/tests/widget/src/android/widget/cts/SlidingDrawerCtsActivity.java b/tests/tests/widget/src/android/widget/cts/SlidingDrawerCtsActivity.java
index d1362c1..338c577 100644
--- a/tests/tests/widget/src/android/widget/cts/SlidingDrawerCtsActivity.java
+++ b/tests/tests/widget/src/android/widget/cts/SlidingDrawerCtsActivity.java
@@ -16,10 +16,9 @@
package android.widget.cts;
-import android.widget.cts.R;
-
import android.app.Activity;
import android.os.Bundle;
+import android.widget.cts.R;
public class SlidingDrawerCtsActivity extends Activity {
@Override
diff --git a/tests/tests/widget/src/android/widget/cts/SlidingDrawerTest.java b/tests/tests/widget/src/android/widget/cts/SlidingDrawerTest.java
index 2af1b06..d2f25b9 100644
--- a/tests/tests/widget/src/android/widget/cts/SlidingDrawerTest.java
+++ b/tests/tests/widget/src/android/widget/cts/SlidingDrawerTest.java
@@ -16,29 +16,27 @@
package android.widget.cts;
-import android.widget.cts.R;
-
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
+import static org.mockito.Mockito.*;
import android.app.Activity;
-import android.content.Context;
import android.cts.util.PollingCheck;
import android.test.ActivityInstrumentationTestCase2;
import android.test.UiThreadTest;
import android.util.AttributeSet;
import android.util.Xml;
-import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageView;
import android.widget.SlidingDrawer;
import android.widget.TextView;
-import android.widget.SlidingDrawer.OnDrawerCloseListener;
-import android.widget.SlidingDrawer.OnDrawerOpenListener;
-import android.widget.SlidingDrawer.OnDrawerScrollListener;
+
+import org.mockito.InOrder;
+import org.mockito.invocation.InvocationOnMock;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
/**
* Test {@link SlidingDrawer}.
@@ -48,7 +46,6 @@
private static final long TEST_TIMEOUT = 5000L;
private Activity mActivity;
- private Object mLock;
public SlidingDrawerTest() {
super("android.widget.cts", SlidingDrawerCtsActivity.class);
@@ -58,7 +55,6 @@
protected void setUp() throws Exception {
super.setUp();
mActivity = getActivity();
- mLock = new Object();
}
@UiThreadTest
@@ -118,40 +114,20 @@
assertFalse(drawer.isOpened());
assertEquals(View.GONE, content.getVisibility());
- runTestOnUiThread(new Runnable() {
- public void run() {
- drawer.animateOpen();
- }
- });
+ runTestOnUiThread(drawer::animateOpen);
assertTrue(drawer.isMoving());
- assertOpened(false, drawer);
assertEquals(View.GONE, content.getVisibility());
- new PollingCheck() {
- @Override
- protected boolean check() {
- return !drawer.isMoving();
- }
- }.run();
- assertOpened(true, drawer);
+ PollingCheck.waitFor(() -> !drawer.isMoving());
+ PollingCheck.waitFor(drawer::isOpened);
assertEquals(View.VISIBLE, content.getVisibility());
- runTestOnUiThread(new Runnable() {
- public void run() {
- drawer.animateClose();
- }
- });
+ runTestOnUiThread(drawer::animateClose);
assertTrue(drawer.isMoving());
- assertOpened(true, drawer);
assertEquals(View.GONE, content.getVisibility());
- new PollingCheck() {
- @Override
- protected boolean check() {
- return !drawer.isMoving();
- }
- }.run();
- assertOpened(false, drawer);
+ PollingCheck.waitFor(() -> !drawer.isMoving());
+ PollingCheck.waitFor(() -> !drawer.isOpened());
assertEquals(View.GONE, content.getVisibility());
}
@@ -162,52 +138,23 @@
assertFalse(drawer.isOpened());
assertEquals(View.GONE, content.getVisibility());
- runTestOnUiThread(new Runnable() {
- public void run() {
- drawer.animateToggle();
- }
- });
+ runTestOnUiThread(drawer::animateToggle);
assertTrue(drawer.isMoving());
- assertOpened(false, drawer);
assertEquals(View.GONE, content.getVisibility());
- new PollingCheck() {
- @Override
- protected boolean check() {
- return !drawer.isMoving();
- }
- }.run();
- assertOpened(true, drawer);
+ PollingCheck.waitFor(() -> !drawer.isMoving());
+ PollingCheck.waitFor(drawer::isOpened);
assertEquals(View.VISIBLE, content.getVisibility());
- runTestOnUiThread(new Runnable() {
- public void run() {
- drawer.animateToggle();
- }
- });
+ runTestOnUiThread(drawer::animateToggle);
assertTrue(drawer.isMoving());
- assertOpened(true, drawer);
assertEquals(View.GONE, content.getVisibility());
- new PollingCheck() {
- @Override
- protected boolean check() {
- return !drawer.isMoving();
- }
- }.run();
- assertOpened(false, drawer);
+ PollingCheck.waitFor(() -> !drawer.isMoving());
+ PollingCheck.waitFor(() -> !drawer.isOpened());
assertEquals(View.GONE, content.getVisibility());
}
- private void assertOpened(final boolean opened, final SlidingDrawer drawer) {
- new PollingCheck() {
- @Override
- protected boolean check() {
- return drawer.isOpened() == opened;
- }
- }.run();
- }
-
@UiThreadTest
public void testToggle() {
SlidingDrawer drawer = (SlidingDrawer) mActivity.findViewById(R.id.drawer);
@@ -254,56 +201,61 @@
@UiThreadTest
public void testSetOnDrawerOpenListener() {
SlidingDrawer drawer = (SlidingDrawer) mActivity.findViewById(R.id.drawer);
- MockOnDrawerOpenListener listener = new MockOnDrawerOpenListener();
- drawer.setOnDrawerOpenListener(listener);
+ SlidingDrawer.OnDrawerOpenListener mockOpenListener =
+ mock(SlidingDrawer.OnDrawerOpenListener.class);
+ drawer.setOnDrawerOpenListener(mockOpenListener);
- assertFalse(listener.hadOpenedDrawer());
+ verifyZeroInteractions(mockOpenListener);
drawer.open();
- assertTrue(listener.hadOpenedDrawer());
+ verify(mockOpenListener, times(1)).onDrawerOpened();
}
@UiThreadTest
public void testSetOnDrawerCloseListener() {
SlidingDrawer drawer = (SlidingDrawer) mActivity.findViewById(R.id.drawer);
- MockOnDrawerCloseListener listener = new MockOnDrawerCloseListener();
- drawer.setOnDrawerCloseListener(listener);
+ SlidingDrawer.OnDrawerCloseListener mockCloseListener =
+ mock(SlidingDrawer.OnDrawerCloseListener.class);
+ drawer.setOnDrawerCloseListener(mockCloseListener);
- assertFalse(listener.hadClosedDrawer());
+ verifyZeroInteractions(mockCloseListener);
drawer.open();
- assertFalse(listener.hadClosedDrawer());
+ verifyZeroInteractions(mockCloseListener);
drawer.close();
- assertTrue(listener.hadClosedDrawer());
+ verify(mockCloseListener, times(1)).onDrawerClosed();
}
public void testSetOnDrawerScrollListener() throws Throwable {
final SlidingDrawer drawer = (SlidingDrawer) mActivity.findViewById(R.id.drawer);
- MockOnDrawerScrollListener listener = new MockOnDrawerScrollListener();
- drawer.setOnDrawerScrollListener(listener);
- assertFalse(listener.hadStartedScroll());
- assertFalse(listener.hadEndedScroll());
- runTestOnUiThread(new Runnable() {
- public void run() {
- drawer.animateOpen();
- }
- });
- if ( !listener.hadStartedScroll() ) {
- synchronized (mLock) {
- mLock.wait(TEST_TIMEOUT);
- }
- }
- assertTrue(listener.hadStartedScroll());
+ final CountDownLatch countDownLatch = new CountDownLatch(2);
+ final SlidingDrawer.OnDrawerScrollListener mockScrollListener =
+ mock(SlidingDrawer.OnDrawerScrollListener.class);
+ doAnswer((InvocationOnMock invocation) -> {
+ countDownLatch.countDown();
+ return null;
+ }).when(mockScrollListener).onScrollStarted();
+ doAnswer((InvocationOnMock invocation) -> {
+ countDownLatch.countDown();
+ return null;
+ }).when(mockScrollListener).onScrollEnded();
+ drawer.setOnDrawerScrollListener(mockScrollListener);
- if ( !listener.hadEndedScroll() ) {
- synchronized (mLock) {
- mLock.wait(TEST_TIMEOUT);
- }
+ runTestOnUiThread(drawer::animateOpen);
+
+ try {
+ countDownLatch.await(2 * TEST_TIMEOUT, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException ie) {
+ // Do nothing as we're about to verify that both callbacks have been called
+ // in any case
}
- assertTrue(listener.hadStartedScroll());
- assertTrue(listener.hadEndedScroll());
+
+ InOrder inOrder = inOrder(mockScrollListener);
+ inOrder.verify(mockScrollListener).onScrollStarted();
+ inOrder.verify(mockScrollListener).onScrollEnded();
+ verifyNoMoreInteractions(mockScrollListener);
}
public void testOnLayout() {
@@ -329,56 +281,4 @@
public void testOnTouchEvent() {
// onTouchEvent() is implementation details, do NOT test
}
-
- private static final class MockOnDrawerOpenListener implements OnDrawerOpenListener {
- private boolean mHadOpenedDrawer = false;
-
- public void onDrawerOpened() {
- mHadOpenedDrawer = true;
- }
-
- public boolean hadOpenedDrawer() {
- return mHadOpenedDrawer;
- }
- }
-
- private static final class MockOnDrawerCloseListener implements OnDrawerCloseListener {
- private boolean mHadClosedDrawer = false;
-
- public void onDrawerClosed() {
- mHadClosedDrawer = true;
- }
-
- public boolean hadClosedDrawer() {
- return mHadClosedDrawer;
- }
- }
-
- private final class MockOnDrawerScrollListener implements OnDrawerScrollListener {
- private boolean mHadEndedScroll = false;
- private boolean mHadStartedScroll = false;
-
- public void onScrollEnded() {
- synchronized (mLock) {
- assertTrue(mHadStartedScroll);
- mHadEndedScroll = true;
- mLock.notify();
- }
- }
-
- public void onScrollStarted() {
- synchronized (mLock) {
- mHadStartedScroll = true;
- mLock.notify();
- }
- }
-
- public boolean hadEndedScroll() {
- return mHadEndedScroll;
- }
-
- public boolean hadStartedScroll() {
- return mHadStartedScroll;
- }
- }
}
diff --git a/hostsidetests/services/activitymanager/app/src/android/server/app/SlowCreateActivity.java b/tests/tests/widget/src/android/widget/cts/SpinnerCtsActivity.java
similarity index 68%
copy from hostsidetests/services/activitymanager/app/src/android/server/app/SlowCreateActivity.java
copy to tests/tests/widget/src/android/widget/cts/SpinnerCtsActivity.java
index 481f5be..16fb30e 100644
--- a/hostsidetests/services/activitymanager/app/src/android/server/app/SlowCreateActivity.java
+++ b/tests/tests/widget/src/android/widget/cts/SpinnerCtsActivity.java
@@ -14,17 +14,22 @@
* limitations under the License.
*/
-package android.server.app;
+package android.widget.cts;
import android.app.Activity;
import android.os.Bundle;
+import android.widget.Spinner;
-public class SlowCreateActivity extends Activity {
+/**
+ * A minimal application for {@link Spinner} test.
+ */
+public class SpinnerCtsActivity extends Activity {
+ /**
+ * Called with the activity is first created.
+ */
@Override
- protected void onCreate(Bundle savedInstanceState) {
- try {
- Thread.sleep(2000);
- } catch(InterruptedException e) {}
+ public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ setContentView(R.layout.spinner_layout);
}
}
diff --git a/tests/tests/widget/src/android/widget/cts/SpinnerTest.java b/tests/tests/widget/src/android/widget/cts/SpinnerTest.java
index eea1e3e..ca0bd0c 100644
--- a/tests/tests/widget/src/android/widget/cts/SpinnerTest.java
+++ b/tests/tests/widget/src/android/widget/cts/SpinnerTest.java
@@ -16,101 +16,164 @@
package android.widget.cts;
-import android.widget.cts.R;
+import static org.mockito.Mockito.*;
-
+import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
-import android.content.Context;
+import android.app.Instrumentation;
+import android.cts.util.CtsTouchUtils;
+import android.content.res.Resources;
import android.content.res.Resources.NotFoundException;
import android.content.res.Resources.Theme;
+import android.cts.util.WidgetTestUtils;
+import android.graphics.Color;
+import android.graphics.drawable.Drawable;
import android.test.ActivityInstrumentationTestCase2;
import android.test.UiThreadTest;
+import android.test.suitebuilder.annotation.MediumTest;
import android.view.ContextThemeWrapper;
+import android.view.Gravity;
+import android.view.KeyEvent;
+import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.Spinner;
+import android.widget.cts.util.TestUtils;
/**
* Test {@link Spinner}.
*/
-public class SpinnerTest extends ActivityInstrumentationTestCase2<RelativeLayoutCtsActivity> {
- private Context mTargetContext;
+@MediumTest
+public class SpinnerTest extends ActivityInstrumentationTestCase2<SpinnerCtsActivity> {
+ private Activity mActivity;
+ private Spinner mSpinnerDialogMode;
+ private Spinner mSpinnerDropdownMode;
public SpinnerTest() {
- super("android.widget.cts", RelativeLayoutCtsActivity.class);
+ super("android.widget.cts", SpinnerCtsActivity.class);
}
@Override
protected void setUp() throws Exception {
super.setUp();
- mTargetContext = getInstrumentation().getTargetContext();
+
+ mActivity = getActivity();
+ mSpinnerDialogMode = (Spinner) mActivity.findViewById(R.id.spinner_dialog_mode);
+ mSpinnerDropdownMode = (Spinner) mActivity.findViewById(R.id.spinner_dropdown_mode);
}
- @UiThreadTest
public void testConstructor() {
- new Spinner(mTargetContext);
+ new Spinner(mActivity);
- new Spinner(mTargetContext, null);
+ new Spinner(mActivity, null);
- new Spinner(mTargetContext, null, android.R.attr.spinnerStyle);
+ new Spinner(mActivity, null, android.R.attr.spinnerStyle);
- new Spinner(mTargetContext, Spinner.MODE_DIALOG);
+ new Spinner(mActivity, Spinner.MODE_DIALOG);
- new Spinner(mTargetContext, null, android.R.attr.spinnerStyle,
+ new Spinner(mActivity, Spinner.MODE_DROPDOWN);
+
+ new Spinner(mActivity, null, android.R.attr.spinnerStyle, Spinner.MODE_DIALOG);
+
+ new Spinner(mActivity, null, android.R.attr.spinnerStyle, Spinner.MODE_DROPDOWN);
+
+ new Spinner(mActivity, null, 0, android.R.style.Widget_DeviceDefault_Spinner,
Spinner.MODE_DIALOG);
- new Spinner(mTargetContext, null, android.R.attr.spinnerStyle, 0,
+ new Spinner(mActivity, null, 0, android.R.style.Widget_DeviceDefault_Spinner,
+ Spinner.MODE_DROPDOWN);
+
+ new Spinner(mActivity, null, 0, android.R.style.Widget_DeviceDefault_Light_Spinner,
Spinner.MODE_DIALOG);
- new Spinner(mTargetContext, null, android.R.attr.spinnerStyle, 0,
- Spinner.MODE_DIALOG, mTargetContext.getTheme());
+ new Spinner(mActivity, null, 0, android.R.style.Widget_DeviceDefault_Light_Spinner,
+ Spinner.MODE_DROPDOWN);
- Spinner spinner = (Spinner) getActivity().findViewById(R.id.spinner1);
- assertEquals(mTargetContext.getString(R.string.text_view_hello), spinner.getPrompt());
+ new Spinner(mActivity, null, 0, android.R.style.Widget_Material_Spinner,
+ Spinner.MODE_DIALOG);
+
+ new Spinner(mActivity, null, 0, android.R.style.Widget_Material_Spinner,
+ Spinner.MODE_DROPDOWN);
+
+ new Spinner(mActivity, null, 0, android.R.style.Widget_Material_Spinner_Underlined,
+ Spinner.MODE_DIALOG);
+
+ new Spinner(mActivity, null, 0, android.R.style.Widget_Material_Spinner_Underlined,
+ Spinner.MODE_DROPDOWN);
+
+ new Spinner(mActivity, null, 0, android.R.style.Widget_Material_Light_Spinner,
+ Spinner.MODE_DIALOG);
+
+ new Spinner(mActivity, null, 0, android.R.style.Widget_Material_Light_Spinner,
+ Spinner.MODE_DROPDOWN);
+
+ new Spinner(mActivity, null, 0, android.R.style.Widget_Material_Light_Spinner_Underlined,
+ Spinner.MODE_DIALOG);
+
+ new Spinner(mActivity, null, 0, android.R.style.Widget_Material_Light_Spinner_Underlined,
+ Spinner.MODE_DROPDOWN);
+
+ final Resources.Theme popupTheme = mActivity.getResources().newTheme();
+ popupTheme.applyStyle(android.R.style.Theme_Material, true);
+
+ new Spinner(mActivity, null, android.R.attr.spinnerStyle, 0, Spinner.MODE_DIALOG,
+ popupTheme);
+
+ new Spinner(mActivity, null, android.R.attr.spinnerStyle, 0, Spinner.MODE_DROPDOWN,
+ popupTheme);
}
- @UiThreadTest
- public void testGetBaseline() {
- Spinner spinner = new Spinner(mTargetContext);
-
+ private void verifyGetBaseline(Spinner spinner) {
assertEquals(-1, spinner.getBaseline());
- spinner = (Spinner) getActivity().findViewById(R.id.spinner1);
- ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(mTargetContext,
- android.widget.cts.R.array.string, android.R.layout.simple_spinner_item);
+ ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(mActivity,
+ R.array.string, android.R.layout.simple_spinner_item);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
- spinner.setAdapter(adapter);
- assertTrue(spinner.getBaseline() > 0);
+ getInstrumentation().runOnMainSync(() -> {
+ spinner.setAdapter(adapter);
+ assertTrue(spinner.getBaseline() > 0);
+ });
}
- @UiThreadTest
- public void testSetOnItemClickListener() {
- Spinner spinner = new Spinner(mTargetContext);
+ public void testGetBaseline() {
+ verifyGetBaseline(mSpinnerDialogMode);
+ verifyGetBaseline(mSpinnerDropdownMode);
+ }
+ private void verifySetOnItemClickListener(Spinner spinner) {
try {
spinner.setOnItemClickListener(null);
fail("Should throw RuntimeException");
} catch (RuntimeException e) {
}
+
+ try {
+ spinner.setOnItemClickListener(mock(Spinner.OnItemClickListener.class));
+ fail("Should throw RuntimeException");
+ } catch (RuntimeException e) {
+ }
}
- @UiThreadTest
+ public void testSetOnItemClickListener() {
+ verifySetOnItemClickListener(mSpinnerDialogMode);
+ verifySetOnItemClickListener(mSpinnerDropdownMode);
+ }
+
+ private void verifyPerformClick(Spinner spinner) {
+ getInstrumentation().runOnMainSync(() -> assertTrue(spinner.performClick()));
+ }
+
public void testPerformClick() {
- final Spinner spinner = (Spinner) getActivity().findViewById(R.id.spinner1);
-
- assertTrue(spinner.performClick());
-
- // TODO: no description for the expected result for this method in its javadoc, issue?
- // Or do UI check?
+ verifyPerformClick(mSpinnerDialogMode);
+ verifyPerformClick(mSpinnerDropdownMode);
}
- @UiThreadTest
- public void testOnClick() {
- Spinner spinner = new Spinner(mTargetContext);
+ private void verifyOnClick(Spinner spinner) {
// normal value
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
AlertDialog alertDialog = builder.show();
assertTrue(alertDialog.isShowing());
+
spinner.onClick(alertDialog, 10);
assertEquals(10, spinner.getSelectedItemPosition());
assertFalse(alertDialog.isShowing());
@@ -125,32 +188,42 @@
Dialog dialog = new Dialog(getActivity());
dialog.show();
assertTrue(dialog.isShowing());
+
spinner.onClick(dialog, -10);
assertEquals(-10, spinner.getSelectedItemPosition());
assertFalse(dialog.isShowing());
}
@UiThreadTest
- public void testAccessPrompt() {
+ public void testOnClick() {
+ verifyOnClick(mSpinnerDialogMode);
+ verifyOnClick(mSpinnerDropdownMode);
+ }
+
+ private void verifyAccessPrompt(Spinner spinner) {
+ final String initialPrompt = mActivity.getString(R.string.text_view_hello);
+ assertEquals(initialPrompt, spinner.getPrompt());
+
+ final Instrumentation instrumentation = getInstrumentation();
final String promptText = "prompt text";
- Spinner spinner = new Spinner(mTargetContext);
-
- spinner.setPrompt(promptText);
+ instrumentation.runOnMainSync(() -> spinner.setPrompt(promptText));
assertEquals(promptText, spinner.getPrompt());
spinner.setPrompt(null);
assertNull(spinner.getPrompt());
-
- // TODO: find the dialog and get its title to assert whether setPrompt() takes effect?
}
- @UiThreadTest
- public void testSetPromptId() {
- Spinner spinner = new Spinner(mTargetContext);
+ public void testAccessPrompt() {
+ verifyAccessPrompt(mSpinnerDialogMode);
+ verifyAccessPrompt(mSpinnerDropdownMode);
+ }
- spinner.setPromptId(R.string.hello_world);
- assertEquals(mTargetContext.getString(R.string.hello_world), spinner.getPrompt());
+ private void verifySetPromptId(Spinner spinner) {
+ final Instrumentation instrumentation = getInstrumentation();
+
+ instrumentation.runOnMainSync(() -> spinner.setPromptId(R.string.hello_world));
+ assertEquals(mActivity.getString(R.string.hello_world), spinner.getPrompt());
try {
spinner.setPromptId(-1);
@@ -165,23 +238,209 @@
} catch (NotFoundException e) {
// issue 1695243, not clear what is supposed to happen if promptId is exceptional.
}
+ }
- // TODO: find the dialog and get its title to assert whether setPromptId() takes effect?
+ public void testSetPromptId() {
+ verifySetPromptId(mSpinnerDialogMode);
+ verifySetPromptId(mSpinnerDropdownMode);
}
@UiThreadTest
public void testGetPopupContext() {
- Theme theme = mTargetContext.getResources().newTheme();
- Spinner themeSpinner = new Spinner(mTargetContext, null,
+ Theme theme = mActivity.getResources().newTheme();
+ Spinner themeSpinner = new Spinner(mActivity, null,
android.R.attr.spinnerStyle, 0, Spinner.MODE_DIALOG, theme);
- assertNotSame(mTargetContext, themeSpinner.getPopupContext());
+ assertNotSame(mActivity, themeSpinner.getPopupContext());
assertSame(theme, themeSpinner.getPopupContext().getTheme());
ContextThemeWrapper context = (ContextThemeWrapper)themeSpinner.getPopupContext();
- assertSame(mTargetContext, context.getBaseContext());
+ assertSame(mActivity, context.getBaseContext());
}
- public void testOnLayout() {
- // onLayout() is implementation details, do NOT test
+ private void verifyGravity(Spinner spinner) {
+ final Instrumentation instrumentation = getInstrumentation();
+
+ // Note that here we're using a custom layout for the spinner's selected item
+ // that doesn't span the whole width of the parent. That way we're exercising the
+ // relevant path in spinner's layout pass that handles the currently set gravity
+ ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(mActivity,
+ R.array.string, R.layout.simple_spinner_item_layout);
+ adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ instrumentation.runOnMainSync(() -> spinner.setAdapter(adapter));
+
+ WidgetTestUtils.runOnMainAndDrawSync(instrumentation, spinner, () -> {
+ spinner.setSelection(1);
+ spinner.getLayoutParams().width = ViewGroup.LayoutParams.MATCH_PARENT;
+ spinner.requestLayout();
+ });
+
+ instrumentation.runOnMainSync(() -> spinner.setGravity(Gravity.LEFT));
+ assertEquals(Gravity.LEFT, spinner.getGravity());
+
+ instrumentation.runOnMainSync(() -> spinner.setGravity(Gravity.CENTER_HORIZONTAL));
+ assertEquals(Gravity.CENTER_HORIZONTAL, spinner.getGravity());
+
+ instrumentation.runOnMainSync((() -> spinner.setGravity(Gravity.RIGHT)));
+ assertEquals(Gravity.RIGHT, spinner.getGravity());
+
+ instrumentation.runOnMainSync(() -> spinner.setGravity(Gravity.START));
+ assertEquals(Gravity.START, spinner.getGravity());
+
+ instrumentation.runOnMainSync(() -> spinner.setGravity(Gravity.END));
+ assertEquals(Gravity.END, spinner.getGravity());
+ }
+
+ public void testGravity() {
+ verifyGravity(mSpinnerDialogMode);
+ verifyGravity(mSpinnerDropdownMode);
+ }
+
+ public void testDropDownMetricsDropdownMode() {
+ final Instrumentation instrumentation = getInstrumentation();
+
+ ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(mActivity,
+ R.array.string, android.R.layout.simple_spinner_item);
+ adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ instrumentation.runOnMainSync(() -> mSpinnerDropdownMode.setAdapter(adapter));
+
+ final Resources res = mActivity.getResources();
+ final int dropDownWidth = res.getDimensionPixelSize(R.dimen.spinner_dropdown_width);
+ final int dropDownOffsetHorizontal =
+ res.getDimensionPixelSize(R.dimen.spinner_dropdown_offset_h);
+ final int dropDownOffsetVertical =
+ res.getDimensionPixelSize(R.dimen.spinner_dropdown_offset_v);
+
+ instrumentation.runOnMainSync(() -> {
+ mSpinnerDropdownMode.setDropDownWidth(dropDownWidth);
+ mSpinnerDropdownMode.setDropDownHorizontalOffset(dropDownOffsetHorizontal);
+ mSpinnerDropdownMode.setDropDownVerticalOffset(dropDownOffsetVertical);
+ });
+
+ // Use instrumentation to emulate a tap on the spinner to bring down its popup
+ CtsTouchUtils.emulateTapOnViewCenter(instrumentation, mSpinnerDropdownMode);
+ // Verify that we're showing the popup
+ assertTrue(mSpinnerDropdownMode.isPopupShowing());
+
+ // And test its attributes
+ assertEquals(dropDownWidth, mSpinnerDropdownMode.getDropDownWidth());
+ // TODO: restore when b/28089349 is addressed
+ // assertEquals(dropDownOffsetHorizontal,
+ // mSpinnerDropdownMode.getDropDownHorizontalOffset());
+ assertEquals(dropDownOffsetVertical, mSpinnerDropdownMode.getDropDownVerticalOffset());
+ }
+
+ public void testDropDownMetricsDialogMode() {
+ final Instrumentation instrumentation = getInstrumentation();
+
+ ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(mActivity,
+ R.array.string, android.R.layout.simple_spinner_item);
+ adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ instrumentation.runOnMainSync(() -> mSpinnerDialogMode.setAdapter(adapter));
+
+ final Resources res = mActivity.getResources();
+ final int dropDownWidth = res.getDimensionPixelSize(R.dimen.spinner_dropdown_width);
+ final int dropDownOffsetHorizontal =
+ res.getDimensionPixelSize(R.dimen.spinner_dropdown_offset_h);
+ final int dropDownOffsetVertical =
+ res.getDimensionPixelSize(R.dimen.spinner_dropdown_offset_v);
+
+ instrumentation.runOnMainSync(() -> {
+ // These are all expected to be no-ops
+ mSpinnerDialogMode.setDropDownWidth(dropDownWidth);
+ mSpinnerDialogMode.setDropDownHorizontalOffset(dropDownOffsetHorizontal);
+ mSpinnerDialogMode.setDropDownVerticalOffset(dropDownOffsetVertical);
+ });
+
+ // Use instrumentation to emulate a tap on the spinner to bring down its popup
+ CtsTouchUtils.emulateTapOnViewCenter(instrumentation, mSpinnerDialogMode);
+ // Verify that we're showing the popup
+ assertTrue(mSpinnerDialogMode.isPopupShowing());
+
+ // And test its attributes. Note that we are not testing the result of getDropDownWidth
+ // for this mode
+ assertEquals(0, mSpinnerDialogMode.getDropDownHorizontalOffset());
+ assertEquals(0, mSpinnerDialogMode.getDropDownVerticalOffset());
+ }
+
+ public void testDropDownBackgroundDropdownMode() {
+ final Instrumentation instrumentation = getInstrumentation();
+
+ ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(mActivity,
+ R.array.string, android.R.layout.simple_spinner_item);
+ adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ instrumentation.runOnMainSync(() -> mSpinnerDropdownMode.setAdapter(adapter));
+
+ // Set blue background on the popup
+ instrumentation.runOnMainSync(() ->
+ mSpinnerDropdownMode.setPopupBackgroundResource(R.drawable.blue_fill));
+
+ // Use instrumentation to emulate a tap on the spinner to bring down its popup
+ CtsTouchUtils.emulateTapOnViewCenter(instrumentation, mSpinnerDropdownMode);
+ // Verify that we're showing the popup
+ assertTrue(mSpinnerDropdownMode.isPopupShowing());
+ // And test its fill
+ Drawable dropDownBackground = mSpinnerDropdownMode.getPopupBackground();
+ TestUtils.assertAllPixelsOfColor("Drop down should be blue", dropDownBackground,
+ dropDownBackground.getBounds().width(), dropDownBackground.getBounds().height(),
+ false, Color.BLUE, 1, true);
+
+ // Dismiss the popup with the emulated back key
+ instrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_BACK);
+ instrumentation.waitForIdleSync();
+ // Verify that we're not showing the popup
+ assertFalse(mSpinnerDropdownMode.isPopupShowing());
+
+ // Set yellow background on the popup
+ instrumentation.runOnMainSync(() ->
+ mSpinnerDropdownMode.setPopupBackgroundDrawable(
+ mActivity.getDrawable(R.drawable.yellow_fill)));
+
+ // Use instrumentation to emulate a tap on the spinner to bring down its popup
+ CtsTouchUtils.emulateTapOnViewCenter(instrumentation, mSpinnerDropdownMode);
+ // Verify that we're showing the popup
+ assertTrue(mSpinnerDropdownMode.isPopupShowing());
+ // And test its fill
+ dropDownBackground = mSpinnerDropdownMode.getPopupBackground();
+ TestUtils.assertAllPixelsOfColor("Drop down should be yellow", dropDownBackground,
+ dropDownBackground.getBounds().width(), dropDownBackground.getBounds().height(),
+ false, Color.YELLOW, 1, true);
+ }
+
+ public void testDropDownBackgroundDialogMode() {
+ final Instrumentation instrumentation = getInstrumentation();
+
+ ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(mActivity,
+ R.array.string, android.R.layout.simple_spinner_item);
+ adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ instrumentation.runOnMainSync(() -> mSpinnerDialogMode.setAdapter(adapter));
+
+ // Set blue background on the popup
+ instrumentation.runOnMainSync(() ->
+ mSpinnerDialogMode.setPopupBackgroundResource(R.drawable.blue_fill));
+
+ // Use instrumentation to emulate a tap on the spinner to bring down its popup
+ CtsTouchUtils.emulateTapOnViewCenter(instrumentation, mSpinnerDialogMode);
+ // Verify that we're showing the popup
+ assertTrue(mSpinnerDialogMode.isPopupShowing());
+ // And test that getPopupBackground returns null
+ assertNull(mSpinnerDialogMode.getPopupBackground());
+
+ // Dismiss the popup with the emulated back key
+ instrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_BACK);
+ instrumentation.waitForIdleSync();
+ // Verify that we're not showing the popup
+ assertFalse(mSpinnerDialogMode.isPopupShowing());
+
+ // Set yellow background on the popup
+ instrumentation.runOnMainSync(() ->
+ mSpinnerDialogMode.setPopupBackgroundDrawable(
+ mActivity.getDrawable(R.drawable.yellow_fill)));
+
+ // Use instrumentation to emulate a tap on the spinner to bring down its popup
+ CtsTouchUtils.emulateTapOnViewCenter(instrumentation, mSpinnerDialogMode);
+ // Verify that we're showing the popup
+ assertTrue(mSpinnerDialogMode.isPopupShowing());
+ // And test that getPopupBackground returns null
+ assertNull(mSpinnerDialogMode.getPopupBackground());
}
}
diff --git a/tests/tests/widget/src/android/widget/cts/SwitchTest.java b/tests/tests/widget/src/android/widget/cts/SwitchTest.java
index 216b17b..ba7066e 100644
--- a/tests/tests/widget/src/android/widget/cts/SwitchTest.java
+++ b/tests/tests/widget/src/android/widget/cts/SwitchTest.java
@@ -17,22 +17,27 @@
package android.widget.cts;
import android.app.Activity;
+import android.app.Instrumentation;
import android.content.res.ColorStateList;
+import android.cts.util.WidgetTestUtils;
import android.graphics.Color;
import android.graphics.PorterDuff.Mode;
-import android.test.ActivityInstrumentationTestCase;
+import android.graphics.Rect;
+import android.graphics.Typeface;
+import android.graphics.drawable.Drawable;
+import android.test.ActivityInstrumentationTestCase2;
import android.test.UiThreadTest;
import android.test.suitebuilder.annotation.SmallTest;
+import android.view.ContextThemeWrapper;
+import android.view.ViewGroup;
import android.widget.Switch;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.IOException;
+import android.widget.cts.util.TestUtils;
/**
* Test {@link Switch}.
*/
@SmallTest
-public class SwitchTest extends ActivityInstrumentationTestCase<SwitchCtsActivity> {
+public class SwitchTest extends ActivityInstrumentationTestCase2<SwitchCtsActivity> {
private Activity mActivity;
private Switch mSwitch;
@@ -44,7 +49,10 @@
protected void setUp() throws Exception {
super.setUp();
mActivity = getActivity();
- mSwitch = (Switch) mActivity.findViewById(R.id.switch_view);
+ }
+
+ private Switch findSwitchById(int id) {
+ return (Switch) mActivity.findViewById(id);
}
@UiThreadTest
@@ -55,11 +63,16 @@
new Switch(mActivity, null, android.R.attr.switchStyle);
+ new Switch(mActivity, null, 0, android.R.style.Widget_Material_CompoundButton_Switch);
+
new Switch(mActivity, null, 0, android.R.style.Widget_Material_Light_CompoundButton_Switch);
}
@UiThreadTest
public void testAccessThumbTint() {
+ mSwitch = findSwitchById(R.id.switch1);
+
+ // These are the default set in layout XML
assertEquals(Color.WHITE, mSwitch.getThumbTintList().getDefaultColor());
assertEquals(Mode.SRC_OVER, mSwitch.getThumbTintMode());
@@ -73,6 +86,9 @@
@UiThreadTest
public void testAccessTrackTint() {
+ mSwitch = findSwitchById(R.id.switch1);
+
+ // These are the default set in layout XML
assertEquals(Color.BLACK, mSwitch.getTrackTintList().getDefaultColor());
assertEquals(Mode.SRC_ATOP, mSwitch.getTrackTintMode());
@@ -83,4 +99,175 @@
assertSame(colors, mSwitch.getTrackTintList());
assertEquals(Mode.XOR, mSwitch.getTrackTintMode());
}
+
+ public void testAccessThumbDrawable() {
+ mSwitch = findSwitchById(R.id.switch2);
+ final Instrumentation instrumentation = getInstrumentation();
+
+ // This is the default set in layout XML
+ Drawable thumbDrawable = mSwitch.getThumbDrawable();
+ TestUtils.assertAllPixelsOfColor("Thumb drawable should be blue", thumbDrawable,
+ thumbDrawable.getIntrinsicWidth(), thumbDrawable.getIntrinsicHeight(), false,
+ Color.BLUE, 1, true);
+
+ // Change thumb drawable to red
+ WidgetTestUtils.runOnMainAndDrawSync(instrumentation, mSwitch,
+ () -> mSwitch.setThumbDrawable(mActivity.getDrawable(R.drawable.icon_red)));
+ thumbDrawable = mSwitch.getThumbDrawable();
+ TestUtils.assertAllPixelsOfColor("Thumb drawable should be red", thumbDrawable,
+ thumbDrawable.getIntrinsicWidth(), thumbDrawable.getIntrinsicHeight(), false,
+ Color.RED, 1, true);
+
+ // Change thumb drawable to green
+ WidgetTestUtils.runOnMainAndDrawSync(instrumentation, mSwitch,
+ () -> mSwitch.setThumbResource(R.drawable.icon_green));
+ thumbDrawable = mSwitch.getThumbDrawable();
+ TestUtils.assertAllPixelsOfColor("Thumb drawable should be green", thumbDrawable,
+ thumbDrawable.getIntrinsicWidth(), thumbDrawable.getIntrinsicHeight(), false,
+ Color.GREEN, 1, true);
+
+ // Now tint the latest (green) thumb drawable with 50% blue SRC_OVER
+ WidgetTestUtils.runOnMainAndDrawSync(instrumentation, mSwitch, () -> {
+ mSwitch.setThumbTintList(ColorStateList.valueOf(0x800000FF));
+ mSwitch.setThumbTintMode(Mode.SRC_OVER);
+ });
+ thumbDrawable = mSwitch.getThumbDrawable();
+ TestUtils.assertAllPixelsOfColor("Thumb drawable should be green / blue", thumbDrawable,
+ thumbDrawable.getIntrinsicWidth(), thumbDrawable.getIntrinsicHeight(), false,
+ TestUtils.compositeColors(0x800000FF, Color.GREEN), 1, true);
+ }
+
+ public void testAccessTrackDrawable() {
+ mSwitch = findSwitchById(R.id.switch2);
+ final Instrumentation instrumentation = getInstrumentation();
+ WidgetTestUtils.runOnMainAndDrawSync(instrumentation, mSwitch,
+ () -> mSwitch.setTrackTintMode(Mode.DST));
+
+ // This is the default set in layout XML
+ Drawable trackDrawable = mSwitch.getTrackDrawable();
+ Rect trackDrawableBounds = trackDrawable.getBounds();
+ TestUtils.assertAllPixelsOfColor("Track drawable should be 50% red", trackDrawable,
+ trackDrawableBounds.width(), trackDrawableBounds.height(), false,
+ 0x80FF0000, 1, true);
+
+ // Change track drawable to blue
+ WidgetTestUtils.runOnMainAndDrawSync(instrumentation, mSwitch,
+ () -> mSwitch.setTrackDrawable(mActivity.getDrawable(R.drawable.blue_fill)));
+ trackDrawable = mSwitch.getTrackDrawable();
+ trackDrawableBounds = trackDrawable.getBounds();
+ TestUtils.assertAllPixelsOfColor("Track drawable should be blue", trackDrawable,
+ trackDrawableBounds.width(), trackDrawableBounds.height(), false,
+ Color.BLUE, 1, true);
+
+ // Change track drawable to green
+ WidgetTestUtils.runOnMainAndDrawSync(instrumentation, mSwitch,
+ () -> mSwitch.setTrackResource(R.drawable.green_fill));
+ trackDrawable = mSwitch.getTrackDrawable();
+ trackDrawableBounds = trackDrawable.getBounds();
+ TestUtils.assertAllPixelsOfColor("Track drawable should be green", trackDrawable,
+ trackDrawableBounds.width(), trackDrawableBounds.height(), false,
+ Color.GREEN, 1, true);
+
+ // Now tint the latest (green) track drawable with 50% blue SRC_OVER
+ WidgetTestUtils.runOnMainAndDrawSync(instrumentation, mSwitch, () -> {
+ mSwitch.setTrackTintList(ColorStateList.valueOf(0x800000FF));
+ mSwitch.setTrackTintMode(Mode.SRC_OVER);
+ });
+ trackDrawable = mSwitch.getTrackDrawable();
+ trackDrawableBounds = trackDrawable.getBounds();
+ TestUtils.assertAllPixelsOfColor("Track drawable should be green / blue", trackDrawable,
+ trackDrawableBounds.width(), trackDrawableBounds.height(), false,
+ TestUtils.compositeColors(0x800000FF, Color.GREEN), 1, true);
+ }
+
+ public void testAccessText() {
+ final Instrumentation instrumentation = getInstrumentation();
+
+ // Run text-related tests on a Holo-themed switch, since under Material themes we
+ // are not showing texts by default.
+ mSwitch = new Switch(new ContextThemeWrapper(mActivity, android.R.style.Theme_Holo_Light));
+ instrumentation.runOnMainSync(
+ () -> ((ViewGroup) mActivity.findViewById(R.id.container)).addView(mSwitch));
+
+ // Set "on" text and verify it
+ WidgetTestUtils.runOnMainAndDrawSync(instrumentation, mSwitch,
+ () -> mSwitch.setTextOn("Text on"));
+ assertEquals("Text on", mSwitch.getTextOn());
+
+ // Set "off" text and verify it
+ WidgetTestUtils.runOnMainAndDrawSync(instrumentation, mSwitch,
+ () -> mSwitch.setTextOff("Text off"));
+ assertEquals("Text off", mSwitch.getTextOff());
+
+ // Turn text display on
+ WidgetTestUtils.runOnMainAndDrawSync(instrumentation, mSwitch,
+ () -> mSwitch.setShowText(true));
+ assertTrue(mSwitch.getShowText());
+
+ // Use custom text appearance. Since we don't have APIs to query this facet of Switch,
+ // just test that it's not crashing.
+ WidgetTestUtils.runOnMainAndDrawSync(instrumentation, mSwitch,
+ () -> mSwitch.setSwitchTextAppearance(mActivity, R.style.TextAppearance_WithColor));
+
+ // Use custom typeface. Since we don't have APIs to query this facet of Switch,
+ // just test that it's not crashing.
+ WidgetTestUtils.runOnMainAndDrawSync(instrumentation, mSwitch,
+ () -> mSwitch.setSwitchTypeface(Typeface.MONOSPACE));
+
+ WidgetTestUtils.runOnMainAndDrawSync(instrumentation, mSwitch,
+ () -> mSwitch.setSwitchTypeface(Typeface.SERIF, Typeface.ITALIC));
+
+ // Set and verify padding between the thumb and the text
+ final int thumbTextPadding = mActivity.getResources().getDimensionPixelSize(
+ R.dimen.switch_thumb_text_padding);
+ WidgetTestUtils.runOnMainAndDrawSync(instrumentation, mSwitch,
+ () -> mSwitch.setThumbTextPadding(thumbTextPadding));
+ assertEquals(thumbTextPadding, mSwitch.getThumbTextPadding());
+
+ // Set and verify padding between the switch and the text
+ final int switchPadding = mActivity.getResources().getDimensionPixelSize(
+ R.dimen.switch_padding);
+ WidgetTestUtils.runOnMainAndDrawSync(instrumentation, mSwitch,
+ () -> mSwitch.setSwitchPadding(switchPadding));
+ assertEquals(switchPadding, mSwitch.getSwitchPadding());
+
+ // Turn text display off
+ WidgetTestUtils.runOnMainAndDrawSync(instrumentation, mSwitch,
+ () -> mSwitch.setShowText(false));
+ assertFalse(mSwitch.getShowText());
+ }
+
+ public void testAccessMinWidth() {
+ mSwitch = findSwitchById(R.id.switch3);
+ final Instrumentation instrumentation = getInstrumentation();
+
+ // Set custom min width on the switch and verify that it's at least that wide
+ final int switchMinWidth = mActivity.getResources().getDimensionPixelSize(
+ R.dimen.switch_min_width);
+ WidgetTestUtils.runOnMainAndDrawSync(instrumentation, mSwitch,
+ () -> mSwitch.setSwitchMinWidth(switchMinWidth));
+ assertEquals(switchMinWidth, mSwitch.getSwitchMinWidth());
+ assertTrue(mSwitch.getWidth() >= switchMinWidth);
+
+ // And set another (larger) min width
+ final int switchMinWidth2 = mActivity.getResources().getDimensionPixelSize(
+ R.dimen.switch_min_width2);
+ WidgetTestUtils.runOnMainAndDrawSync(instrumentation, mSwitch,
+ () -> mSwitch.setSwitchMinWidth(switchMinWidth2));
+ assertEquals(switchMinWidth2, mSwitch.getSwitchMinWidth());
+ assertTrue(mSwitch.getWidth() >= switchMinWidth2);
+ }
+
+ public void testAccessSplitTrack() {
+ mSwitch = findSwitchById(R.id.switch3);
+ final Instrumentation instrumentation = getInstrumentation();
+
+ WidgetTestUtils.runOnMainAndDrawSync(instrumentation, mSwitch,
+ () -> mSwitch.setSplitTrack(true));
+ assertTrue(mSwitch.getSplitTrack());
+
+ WidgetTestUtils.runOnMainAndDrawSync(instrumentation, mSwitch,
+ () -> mSwitch.setSplitTrack(false));
+ assertFalse(mSwitch.getSplitTrack());
+ }
}
diff --git a/tests/tests/widget/src/android/widget/cts/TabHostCtsActivity.java b/tests/tests/widget/src/android/widget/cts/TabHostCtsActivity.java
index 189dbee..ec0bfa7 100644
--- a/tests/tests/widget/src/android/widget/cts/TabHostCtsActivity.java
+++ b/tests/tests/widget/src/android/widget/cts/TabHostCtsActivity.java
@@ -21,7 +21,6 @@
import android.view.View;
import android.widget.TabHost;
import android.widget.TextView;
-
import android.widget.cts.R;
/**
diff --git a/tests/tests/widget/src/android/widget/cts/TabHostTest.java b/tests/tests/widget/src/android/widget/cts/TabHostTest.java
index 8756687..b8e0a8a 100644
--- a/tests/tests/widget/src/android/widget/cts/TabHostTest.java
+++ b/tests/tests/widget/src/android/widget/cts/TabHostTest.java
@@ -16,6 +16,8 @@
package android.widget.cts;
+import static org.mockito.Mockito.*;
+
import android.app.Activity;
import android.app.ActivityGroup;
import android.content.Intent;
@@ -30,8 +32,6 @@
import android.widget.TabHost.TabSpec;
import android.widget.TextView;
-import static org.mockito.Mockito.*;
-
/**
* Test {@link TabHost}.
*/
diff --git a/tests/tests/widget/src/android/widget/cts/TabHost_TabSpecTest.java b/tests/tests/widget/src/android/widget/cts/TabHost_TabSpecTest.java
index e8825c7..d4d2bf1 100644
--- a/tests/tests/widget/src/android/widget/cts/TabHost_TabSpecTest.java
+++ b/tests/tests/widget/src/android/widget/cts/TabHost_TabSpecTest.java
@@ -16,9 +16,6 @@
package android.widget.cts;
-import android.widget.cts.R;
-
-
import android.app.Activity;
import android.app.Instrumentation;
import android.app.Instrumentation.ActivityMonitor;
@@ -33,8 +30,9 @@
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TabHost;
-import android.widget.TextView;
import android.widget.TabHost.TabSpec;
+import android.widget.TextView;
+import android.widget.cts.R;
/**
* Test {@link TabSpec}.
diff --git a/tests/tests/widget/src/android/widget/cts/TableCtsActivity.java b/tests/tests/widget/src/android/widget/cts/TableCtsActivity.java
index 490d261..ce3fd7e 100644
--- a/tests/tests/widget/src/android/widget/cts/TableCtsActivity.java
+++ b/tests/tests/widget/src/android/widget/cts/TableCtsActivity.java
@@ -16,10 +16,9 @@
package android.widget.cts;
-import android.widget.cts.R;
-
import android.app.Activity;
import android.os.Bundle;
+import android.widget.cts.R;
/**
* A minimal application for TableLayout test.
diff --git a/tests/tests/widget/src/android/widget/cts/TableLayoutTest.java b/tests/tests/widget/src/android/widget/cts/TableLayoutTest.java
index ccf22d9..e3884cc 100644
--- a/tests/tests/widget/src/android/widget/cts/TableLayoutTest.java
+++ b/tests/tests/widget/src/android/widget/cts/TableLayoutTest.java
@@ -16,7 +16,7 @@
package android.widget.cts;
-import android.widget.cts.R;
+import static org.mockito.Mockito.*;
import android.content.Context;
import android.content.res.XmlResourceParser;
@@ -25,9 +25,8 @@
import android.util.AttributeSet;
import android.util.Xml;
import android.view.View;
-import android.view.ViewGroup;
import android.view.View.MeasureSpec;
-import android.view.ViewGroup.OnHierarchyChangeListener;
+import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.RelativeLayout;
@@ -72,21 +71,21 @@
public void testSetOnHierarchyChangeListener() {
TableLayout tableLayout = new TableLayout(mContext);
- MockOnHierarchyChangeListener listener = new MockOnHierarchyChangeListener();
- tableLayout.setOnHierarchyChangeListener(listener);
+ ViewGroup.OnHierarchyChangeListener mockHierarchyChangeListener =
+ mock(ViewGroup.OnHierarchyChangeListener.class);
+ tableLayout.setOnHierarchyChangeListener(mockHierarchyChangeListener);
- tableLayout.addView(new TextView(mContext));
- assertTrue(listener.hasCalledOnChildViewAdded());
+ View toAdd = new TextView(mContext);
+ tableLayout.addView(toAdd);
+ verify(mockHierarchyChangeListener, times(1)).onChildViewAdded(tableLayout, toAdd);
tableLayout.removeViewAt(0);
- assertTrue(listener.hasCalledOnChildViewRemoved());
-
- listener.reset();
+ verify(mockHierarchyChangeListener, times(1)).onChildViewRemoved(tableLayout, toAdd);
+ verifyNoMoreInteractions(mockHierarchyChangeListener);
tableLayout.setOnHierarchyChangeListener(null);
tableLayout.addView(new TextView(mContext));
- assertFalse(listener.hasCalledOnChildViewAdded());
tableLayout.removeViewAt(0);
- assertFalse(listener.hasCalledOnChildViewRemoved());
+ verifyNoMoreInteractions(mockHierarchyChangeListener);
}
public void testRequestLayout() {
@@ -456,7 +455,8 @@
assertNull(child1.getLayoutParams());
tableLayout.addView(child1, new ViewGroup.LayoutParams(100, 200));
assertSame(child1, tableLayout.getChildAt(0));
- assertEquals(100, tableLayout.getChildAt(0).getLayoutParams().width);
+ assertEquals(ViewGroup.LayoutParams.MATCH_PARENT,
+ tableLayout.getChildAt(0).getLayoutParams().width);
assertEquals(200, tableLayout.getChildAt(0).getLayoutParams().height);
assertTrue(tableLayout.getChildAt(0).isLayoutRequested());
@@ -467,9 +467,11 @@
tableLayout.addView(child2, new TableRow.LayoutParams(200, 300, 1));
assertSame(child1, tableLayout.getChildAt(0));
assertSame(child2, tableLayout.getChildAt(1));
- assertEquals(100, tableLayout.getChildAt(0).getLayoutParams().width);
+ assertEquals(ViewGroup.LayoutParams.MATCH_PARENT,
+ tableLayout.getChildAt(0).getLayoutParams().width);
assertEquals(200, tableLayout.getChildAt(0).getLayoutParams().height);
- assertEquals(200, tableLayout.getChildAt(1).getLayoutParams().width);
+ assertEquals(ViewGroup.LayoutParams.MATCH_PARENT,
+ tableLayout.getChildAt(1).getLayoutParams().width);
assertEquals(300, tableLayout.getChildAt(1).getLayoutParams().height);
assertTrue(tableLayout.getChildAt(0).isLayoutRequested());
assertTrue(tableLayout.getChildAt(1).isLayoutRequested());
@@ -494,7 +496,8 @@
assertNull(child1.getLayoutParams());
tableLayout.addView(child1, 0, new ViewGroup.LayoutParams(100, 200));
assertSame(child1, tableLayout.getChildAt(0));
- assertEquals(100, tableLayout.getChildAt(0).getLayoutParams().width);
+ assertEquals(ViewGroup.LayoutParams.MATCH_PARENT,
+ tableLayout.getChildAt(0).getLayoutParams().width);
assertEquals(200, tableLayout.getChildAt(0).getLayoutParams().height);
assertTrue(tableLayout.getChildAt(0).isLayoutRequested());
@@ -505,9 +508,11 @@
tableLayout.addView(child2, 0, new TableRow.LayoutParams(200, 300, 1));
assertSame(child2, tableLayout.getChildAt(0));
assertSame(child1, tableLayout.getChildAt(1));
- assertEquals(200, tableLayout.getChildAt(0).getLayoutParams().width);
+ assertEquals(ViewGroup.LayoutParams.MATCH_PARENT,
+ tableLayout.getChildAt(0).getLayoutParams().width);
assertEquals(300, tableLayout.getChildAt(0).getLayoutParams().height);
- assertEquals(100, tableLayout.getChildAt(1).getLayoutParams().width);
+ assertEquals(ViewGroup.LayoutParams.MATCH_PARENT,
+ tableLayout.getChildAt(1).getLayoutParams().width);
assertEquals(200, tableLayout.getChildAt(1).getLayoutParams().height);
assertTrue(tableLayout.getChildAt(0).isLayoutRequested());
assertTrue(tableLayout.getChildAt(1).isLayoutRequested());
@@ -520,11 +525,14 @@
assertSame(child2, tableLayout.getChildAt(0));
assertSame(child1, tableLayout.getChildAt(1));
assertSame(child3, tableLayout.getChildAt(2));
- assertEquals(200, tableLayout.getChildAt(0).getLayoutParams().width);
+ assertEquals(ViewGroup.LayoutParams.MATCH_PARENT,
+ tableLayout.getChildAt(0).getLayoutParams().width);
assertEquals(300, tableLayout.getChildAt(0).getLayoutParams().height);
- assertEquals(100, tableLayout.getChildAt(1).getLayoutParams().width);
+ assertEquals(ViewGroup.LayoutParams.MATCH_PARENT,
+ tableLayout.getChildAt(1).getLayoutParams().width);
assertEquals(200, tableLayout.getChildAt(1).getLayoutParams().height);
- assertEquals(300, tableLayout.getChildAt(2).getLayoutParams().width);
+ assertEquals(ViewGroup.LayoutParams.MATCH_PARENT,
+ tableLayout.getChildAt(2).getLayoutParams().width);
assertEquals(400, tableLayout.getChildAt(2).getLayoutParams().height);
assertTrue(tableLayout.getChildAt(0).isLayoutRequested());
assertTrue(tableLayout.getChildAt(1).isLayoutRequested());
@@ -588,7 +596,7 @@
LinearLayout.LayoutParams layoutParams = mockTableLayout.generateLayoutParams(
new ViewGroup.LayoutParams(200, 300));
assertNotNull(layoutParams);
- assertEquals(200, layoutParams.width);
+ assertEquals(ViewGroup.LayoutParams.MATCH_PARENT, layoutParams.width);
assertEquals(300, layoutParams.height);
assertTrue(layoutParams instanceof TableLayout.LayoutParams);
@@ -611,50 +619,6 @@
mockTableLayout.onMeasure(MeasureSpec.EXACTLY, MeasureSpec.EXACTLY);
}
- private int dropNegative(int number) {
- return (number > 0 ? number : 0);
- }
-
- private class MockOnHierarchyChangeListener implements OnHierarchyChangeListener {
- private boolean mCalledOnChildViewAdded = false;
- private boolean mCalledOnChildViewRemoved = false;
-
- /*
- * (non-Javadoc)
- *
- * @see
- * android.view.ViewGroup.OnHierarchyChangeListener#onChildViewAdded
- * (View, View)
- */
- public void onChildViewAdded(View parent, View child) {
- mCalledOnChildViewAdded = true;
- }
-
- /*
- * (non-Javadoc)
- *
- * @see
- * android.view.ViewGroup.OnHierarchyChangeListener#onChildViewRemoved
- * (View, View)
- */
- public void onChildViewRemoved(View parent, View child) {
- mCalledOnChildViewRemoved = true;
- }
-
- public boolean hasCalledOnChildViewAdded() {
- return mCalledOnChildViewAdded;
- }
-
- public boolean hasCalledOnChildViewRemoved() {
- return mCalledOnChildViewRemoved;
- }
-
- public void reset() {
- mCalledOnChildViewAdded = false;
- mCalledOnChildViewRemoved = false;
- }
- }
-
/*
* Mock class for TableLayout to test protected methods
*/
diff --git a/tests/tests/widget/src/android/widget/cts/TableLayout_LayoutParamsTest.java b/tests/tests/widget/src/android/widget/cts/TableLayout_LayoutParamsTest.java
index fbd9f97..41f1816 100644
--- a/tests/tests/widget/src/android/widget/cts/TableLayout_LayoutParamsTest.java
+++ b/tests/tests/widget/src/android/widget/cts/TableLayout_LayoutParamsTest.java
@@ -16,19 +16,19 @@
package android.widget.cts;
-import android.widget.cts.R;
-
-import org.xmlpull.v1.XmlPullParser;
-
import android.content.Context;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.test.InstrumentationTestCase;
import android.util.AttributeSet;
import android.util.Xml;
+import android.view.ViewGroup;
import android.widget.TableLayout;
+import android.widget.cts.R;
import android.widget.cts.util.XmlUtils;
+import org.xmlpull.v1.XmlPullParser;
+
/**
* Test {@link TableLayout.LayoutParams}.
*/
@@ -42,28 +42,47 @@
}
public void testConstructor() {
- new TableLayout.LayoutParams(mTargetContext, null);
+ // We expect width to be MATCH and height to be WRAP as documented in TableLayout
+ TableLayout.LayoutParams layoutParams = new TableLayout.LayoutParams();
+ assertEquals(TableLayout.LayoutParams.MATCH_PARENT, layoutParams.width);
+ assertEquals(TableLayout.LayoutParams.WRAP_CONTENT, layoutParams.height);
- TableLayout.LayoutParams layoutParams = new TableLayout.LayoutParams(200, 300);
+ // We expect width to be MATCH and height to be WRAP as documented in TableLayout
+ layoutParams = new TableLayout.LayoutParams(mTargetContext, null);
+ assertEquals(TableLayout.LayoutParams.MATCH_PARENT, layoutParams.width);
+ assertEquals(TableLayout.LayoutParams.WRAP_CONTENT, layoutParams.height);
+
+ // We expect width to be MATCH, ignoring what is passed in the constructor
+ layoutParams = new TableLayout.LayoutParams(200, 300);
assertEquals(TableLayout.LayoutParams.MATCH_PARENT, layoutParams.width);
assertEquals(300, layoutParams.height);
- TableLayout.LayoutParams oldParams = layoutParams;
- layoutParams = new TableLayout.LayoutParams(200, 300, 1.2f);
+ // We expect width to be MATCH, ignoring what is passed in the constructor
+ layoutParams = new TableLayout.LayoutParams(250, 350, 1.2f);
assertEquals(TableLayout.LayoutParams.MATCH_PARENT, layoutParams.width);
- assertEquals(300, layoutParams.height);
+ assertEquals(350, layoutParams.height);
assertEquals(1.2f, layoutParams.weight);
- TableLayout.LayoutParams oldMarginParams = layoutParams;
- new TableLayout.LayoutParams();
-
- layoutParams = new TableLayout.LayoutParams(oldParams);
+ // We expect width to be MATCH, ignoring what is set on the passed object
+ layoutParams = new TableLayout.LayoutParams(new ViewGroup.LayoutParams(300, 360));
assertEquals(TableLayout.LayoutParams.MATCH_PARENT, layoutParams.width);
- assertEquals(300, layoutParams.height);
+ assertEquals(360, layoutParams.height);
- layoutParams = new TableLayout.LayoutParams(oldMarginParams);
+ // We expect width to be MATCH, ignoring what is set on the passed object
+ layoutParams = new TableLayout.LayoutParams(new ViewGroup.MarginLayoutParams(320, 420));
assertEquals(TableLayout.LayoutParams.MATCH_PARENT, layoutParams.width);
- assertEquals(300, layoutParams.height);
+ assertEquals(420, layoutParams.height);
+
+ // We expect width to be MATCH as that is copied from the passed object
+ layoutParams = new TableLayout.LayoutParams(new TableLayout.LayoutParams(500, 400));
+ assertEquals(TableLayout.LayoutParams.MATCH_PARENT, layoutParams.width);
+ assertEquals(400, layoutParams.height);
+
+ // We expect width to be MATCH as that is copied from the passed object
+ layoutParams = new TableLayout.LayoutParams(new TableLayout.LayoutParams(550, 650, 1.4f));
+ assertEquals(TableLayout.LayoutParams.MATCH_PARENT, layoutParams.width);
+ assertEquals(650, layoutParams.height);
+ assertEquals(1.4f, layoutParams.weight);
}
public void testSetBaseAttributes() {
diff --git a/tests/tests/widget/src/android/widget/cts/TableRowTest.java b/tests/tests/widget/src/android/widget/cts/TableRowTest.java
index 2e917f8..42f2a97 100644
--- a/tests/tests/widget/src/android/widget/cts/TableRowTest.java
+++ b/tests/tests/widget/src/android/widget/cts/TableRowTest.java
@@ -16,6 +16,8 @@
package android.widget.cts;
+import static org.mockito.Mockito.*;
+
import android.content.Context;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
@@ -24,18 +26,14 @@
import android.util.AttributeSet;
import android.util.Xml;
import android.view.View;
-import android.view.ViewGroup;
import android.view.View.MeasureSpec;
-import android.view.ViewGroup.OnHierarchyChangeListener;
+import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TableLayout;
import android.widget.TableRow;
import android.widget.TextView;
-import android.widget.cts.R;
-
-
/**
* Test {@link TableRow}.
*/
@@ -63,21 +61,21 @@
public void testSetOnHierarchyChangeListener() {
TableRow tableRow = new TableRow(mContext);
- MockOnHierarchyChangeListener listener = new MockOnHierarchyChangeListener();
- tableRow.setOnHierarchyChangeListener(listener);
+ ViewGroup.OnHierarchyChangeListener mockHierarchyChangeListener =
+ mock(ViewGroup.OnHierarchyChangeListener.class);
+ tableRow.setOnHierarchyChangeListener(mockHierarchyChangeListener);
- tableRow.addView(new TextView(mContext));
- assertTrue(listener.hasCalledOnChildViewAdded());
+ View toAdd = new TextView(mContext);
+ tableRow.addView(toAdd);
+ verify(mockHierarchyChangeListener, times(1)).onChildViewAdded(tableRow, toAdd);
tableRow.removeViewAt(0);
- assertTrue(listener.hasCalledOnChildViewRemoved());
-
- listener.reset();
+ verify(mockHierarchyChangeListener, times(1)).onChildViewRemoved(tableRow, toAdd);
+ verifyNoMoreInteractions(mockHierarchyChangeListener);
tableRow.setOnHierarchyChangeListener(null);
tableRow.addView(new TextView(mContext));
- assertFalse(listener.hasCalledOnChildViewAdded());
tableRow.removeViewAt(0);
- assertFalse(listener.hasCalledOnChildViewRemoved());
+ verifyNoMoreInteractions(mockHierarchyChangeListener);
}
@UiThreadTest
@@ -189,46 +187,6 @@
mockTableRow.onMeasure(MeasureSpec.EXACTLY, MeasureSpec.EXACTLY);
}
- private class MockOnHierarchyChangeListener implements OnHierarchyChangeListener {
- private boolean mCalledOnChildViewAdded = false;
- private boolean mCalledOnChildViewRemoved = false;
-
- /*
- * (non-Javadoc)
- *
- * @see
- * android.view.ViewGroup.OnHierarchyChangeListener#onChildViewAdded
- * (View, View)
- */
- public void onChildViewAdded(View parent, View child) {
- mCalledOnChildViewAdded = true;
- }
-
- /*
- * (non-Javadoc)
- *
- * @see
- * android.view.ViewGroup.OnHierarchyChangeListener#onChildViewRemoved
- * (View, View)
- */
- public void onChildViewRemoved(View parent, View child) {
- mCalledOnChildViewRemoved = true;
- }
-
- public boolean hasCalledOnChildViewAdded() {
- return mCalledOnChildViewAdded;
- }
-
- public boolean hasCalledOnChildViewRemoved() {
- return mCalledOnChildViewRemoved;
- }
-
- public void reset() {
- mCalledOnChildViewAdded = false;
- mCalledOnChildViewRemoved = false;
- }
- }
-
/*
* Mock class for TableRow to test protected methods
*/
diff --git a/tests/tests/widget/src/android/widget/cts/TableRow_LayoutParamsTest.java b/tests/tests/widget/src/android/widget/cts/TableRow_LayoutParamsTest.java
index ce3be70..8e39d75 100644
--- a/tests/tests/widget/src/android/widget/cts/TableRow_LayoutParamsTest.java
+++ b/tests/tests/widget/src/android/widget/cts/TableRow_LayoutParamsTest.java
@@ -16,10 +16,6 @@
package android.widget.cts;
-import android.widget.cts.R;
-
-import org.xmlpull.v1.XmlPullParser;
-
import android.content.Context;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
@@ -32,8 +28,11 @@
import android.view.ViewGroup.MarginLayoutParams;
import android.widget.TableLayout;
import android.widget.TableRow;
+import android.widget.cts.R;
import android.widget.cts.util.XmlUtils;
+import org.xmlpull.v1.XmlPullParser;
+
/**
* Test {@link TableRow.LayoutParams}.
*/
diff --git a/tests/tests/widget/src/android/widget/cts/TextViewCtsActivity.java b/tests/tests/widget/src/android/widget/cts/TextViewCtsActivity.java
index 31fc486..0580150 100644
--- a/tests/tests/widget/src/android/widget/cts/TextViewCtsActivity.java
+++ b/tests/tests/widget/src/android/widget/cts/TextViewCtsActivity.java
@@ -16,17 +16,16 @@
package android.widget.cts;
-import android.widget.cts.R;
-
import android.app.Activity;
import android.os.Bundle;
+import android.widget.TextView;
/**
- * A minimal application for TextView test.
+ * A minimal application for {@link TextView} test.
*/
public class TextViewCtsActivity extends Activity {
/**
- * Called with the activity is first created.
+ * Called when the activity is first created.
*/
@Override
public void onCreate(Bundle savedInstanceState) {
diff --git a/tests/tests/widget/src/android/widget/cts/TextViewFadingEdgeTest.java b/tests/tests/widget/src/android/widget/cts/TextViewFadingEdgeTest.java
index e627c2b..93cad6d 100644
--- a/tests/tests/widget/src/android/widget/cts/TextViewFadingEdgeTest.java
+++ b/tests/tests/widget/src/android/widget/cts/TextViewFadingEdgeTest.java
@@ -16,20 +16,20 @@
package android.widget.cts;
-import android.cts.util.PollingCheck;
-import android.test.ActivityInstrumentationTestCase2;
-import android.view.ViewGroup;
-import android.widget.FrameLayout;
-
import static android.view.Gravity.CENTER;
import static android.view.Gravity.LEFT;
import static android.view.Gravity.NO_GRAVITY;
import static android.view.Gravity.RIGHT;
import static android.view.View.TEXT_ALIGNMENT_INHERIT;
-import static android.widget.TextView.TEXT_ALIGNMENT_TEXT_START;
import static android.widget.TextView.TEXT_ALIGNMENT_TEXT_END;
-import static android.widget.TextView.TEXT_ALIGNMENT_VIEW_START;
+import static android.widget.TextView.TEXT_ALIGNMENT_TEXT_START;
import static android.widget.TextView.TEXT_ALIGNMENT_VIEW_END;
+import static android.widget.TextView.TEXT_ALIGNMENT_VIEW_START;
+
+import android.cts.util.PollingCheck;
+import android.test.ActivityInstrumentationTestCase2;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
public class TextViewFadingEdgeTest extends ActivityInstrumentationTestCase2<EmptyCtsActivity> {
@@ -120,12 +120,7 @@
@Override
protected void setUp() throws Exception {
super.setUp();
- new PollingCheck() {
- @Override
- protected boolean check() {
- return getActivity().hasWindowFocus();
- }
- }.run();
+ PollingCheck.waitFor(() -> getActivity().hasWindowFocus());
}
public void testFadingEdge() {
diff --git a/tests/tests/widget/src/android/widget/cts/TextViewTest.java b/tests/tests/widget/src/android/widget/cts/TextViewTest.java
index 9f611ad..a4a7897 100644
--- a/tests/tests/widget/src/android/widget/cts/TextViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/TextViewTest.java
@@ -16,17 +16,22 @@
package android.widget.cts;
+import static org.mockito.Matchers.*;
+import static org.mockito.Mockito.*;
+
import android.app.Activity;
import android.app.Instrumentation;
import android.app.Instrumentation.ActivityMonitor;
+import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.ColorStateList;
+import android.content.res.Resources;
import android.content.res.Resources.NotFoundException;
-import android.cts.util.KeyEventUtil;
+import android.cts.util.CtsTouchUtils;
+import android.cts.util.CtsKeyEventUtil;
import android.cts.util.PollingCheck;
import android.cts.util.WidgetTestUtils;
-import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
@@ -39,10 +44,13 @@
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
+import android.os.Handler;
import android.os.LocaleList;
+import android.os.Looper;
import android.os.Parcelable;
+import android.os.ResultReceiver;
+import android.os.SystemClock;
import android.test.ActivityInstrumentationTestCase2;
-import android.test.TouchUtils;
import android.test.UiThreadTest;
import android.test.suitebuilder.annotation.MediumTest;
import android.test.suitebuilder.annotation.SmallTest;
@@ -73,6 +81,7 @@
import android.text.method.TextKeyListener.Capitalize;
import android.text.method.TimeKeyListener;
import android.text.method.TransformationMethod;
+import android.text.style.ClickableSpan;
import android.text.style.ImageSpan;
import android.text.style.URLSpan;
import android.text.style.UnderlineSpan;
@@ -83,27 +92,32 @@
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.Gravity;
+import android.view.InputDevice;
import android.view.KeyEvent;
import android.view.Menu;
-import android.view.MenuItem;
+import android.view.MotionEvent;
+import android.view.PointerIcon;
import android.view.View;
-import android.view.View.OnCreateContextMenuListener;
-import android.view.View.OnLongClickListener;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.inputmethod.BaseInputConnection;
+import android.view.inputmethod.CompletionInfo;
+import android.view.inputmethod.CorrectionInfo;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.ExtractedText;
import android.view.inputmethod.ExtractedTextRequest;
import android.view.inputmethod.InputConnection;
+import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
+import android.widget.ScrollView;
import android.widget.Scroller;
import android.widget.TextView;
import android.widget.TextView.BufferType;
-import android.widget.TextView.OnEditorActionListener;
+import android.widget.cts.util.TestUtils;
+import org.mockito.invocation.InvocationOnMock;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
@@ -124,7 +138,7 @@
+ "this text, I would love to see the kind of devices you guys now use!";
private static final long TIMEOUT = 5000;
private CharSequence mTransformedText;
- private KeyEventUtil mKeyEventUtil;
+ private Handler mHandler = new Handler(Looper.getMainLooper());
public TextViewTest() {
super("android.widget.cts", TextViewCtsActivity.class);
@@ -134,28 +148,27 @@
protected void setUp() throws Exception {
super.setUp();
mActivity = getActivity();
- new PollingCheck() {
- @Override
- protected boolean check() {
- return mActivity.hasWindowFocus();
- }
- }.run();
+ PollingCheck.waitFor(mActivity::hasWindowFocus);
mInstrumentation = getInstrumentation();
- mKeyEventUtil = new KeyEventUtil(mInstrumentation);
}
/**
- * Promotes the TextView to editable and places focus in it to allow simulated typing.
+ * Promotes the TextView to editable and places focus in it to allow simulated typing. Used in
+ * test methods annotated with {@link android.test.UiThreadTest}.
*/
private void initTextViewForTyping() {
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- mTextView = findTextView(R.id.textview_text);
- mTextView.setKeyListener(QwertyKeyListener.getInstance(false, Capitalize.NONE));
- mTextView.setText("", BufferType.EDITABLE);
- mTextView.requestFocus();
- }
- });
+ mTextView = findTextView(R.id.textview_text);
+ mTextView.setKeyListener(QwertyKeyListener.getInstance(false, Capitalize.NONE));
+ mTextView.setText("", BufferType.EDITABLE);
+ mTextView.requestFocus();
+ }
+
+ /**
+ * Used in test methods that can not entirely be run on the UiThread (e.g: tests that need to
+ * emulate touches and/or key presses).
+ */
+ private void initTextViewForTypingOnUiThread() {
+ mActivity.runOnUiThread(this::initTextViewForTyping);
mInstrumentation.waitForIdleSync();
}
@@ -164,7 +177,15 @@
new TextView(mActivity, null);
- new TextView(mActivity, null, 0);
+ new TextView(mActivity, null, android.R.attr.textViewStyle);
+
+ new TextView(mActivity, null, 0, android.R.style.Widget_DeviceDefault_TextView);
+
+ new TextView(mActivity, null, 0, android.R.style.Widget_DeviceDefault_Light_TextView);
+
+ new TextView(mActivity, null, 0, android.R.style.Widget_Material_TextView);
+
+ new TextView(mActivity, null, 0, android.R.style.Widget_Material_Light_TextView);
}
@UiThreadTest
@@ -188,11 +209,9 @@
}
public void testGetLayout() {
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- mTextView = findTextView(R.id.textview_text);
- mTextView.setGravity(Gravity.CENTER);
- }
+ mActivity.runOnUiThread(() -> {
+ mTextView = findTextView(R.id.textview_text);
+ mTextView.setGravity(Gravity.CENTER);
});
mInstrumentation.waitForIdleSync();
assertNotNull(mTextView.getLayout());
@@ -211,32 +230,20 @@
}
public void testAccessKeyListener() {
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- mTextView = findTextView(R.id.textview_text);
- }
- });
+ mActivity.runOnUiThread(() -> mTextView = findTextView(R.id.textview_text));
mInstrumentation.waitForIdleSync();
assertNull(mTextView.getKeyListener());
final KeyListener digitsKeyListener = DigitsKeyListener.getInstance();
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- mTextView.setKeyListener(digitsKeyListener);
- }
- });
+ mActivity.runOnUiThread(() -> mTextView.setKeyListener(digitsKeyListener));
mInstrumentation.waitForIdleSync();
assertSame(digitsKeyListener, mTextView.getKeyListener());
final QwertyKeyListener qwertyKeyListener
= QwertyKeyListener.getInstance(false, Capitalize.NONE);
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- mTextView.setKeyListener(qwertyKeyListener);
- }
- });
+ mActivity.runOnUiThread(() -> mTextView.setKeyListener(qwertyKeyListener));
mInstrumentation.waitForIdleSync();
assertSame(qwertyKeyListener, mTextView.getKeyListener());
}
@@ -249,42 +256,38 @@
final int selectionStart = 10;
final int selectionEnd = LONG_TEXT.length();
final MovementMethod movementMethod = ArrowKeyMovementMethod.getInstance();
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- mTextView = findTextView(R.id.textview_text);
- mTextView.setMovementMethod(movementMethod);
- mTextView.setText(LONG_TEXT, BufferType.EDITABLE);
- Selection.setSelection((Editable) mTextView.getText(),
- selectionStart, selectionEnd);
- mTextView.requestFocus();
- }
+ mActivity.runOnUiThread(() -> {
+ mTextView = findTextView(R.id.textview_text);
+ mTextView.setMovementMethod(movementMethod);
+ mTextView.setText(LONG_TEXT, BufferType.EDITABLE);
+ Selection.setSelection((Editable) mTextView.getText(),
+ selectionStart, selectionEnd);
+ mTextView.requestFocus();
});
mInstrumentation.waitForIdleSync();
assertSame(movementMethod, mTextView.getMovementMethod());
assertEquals(selectionStart, Selection.getSelectionStart(mTextView.getText()));
assertEquals(selectionEnd, Selection.getSelectionEnd(mTextView.getText()));
- sendKeys(KeyEvent.KEYCODE_SHIFT_LEFT, KeyEvent.KEYCODE_ALT_LEFT,
- KeyEvent.KEYCODE_DPAD_UP);
+ CtsKeyEventUtil.sendKeys(mInstrumentation, mTextView, KeyEvent.KEYCODE_SHIFT_LEFT,
+ KeyEvent.KEYCODE_ALT_LEFT, KeyEvent.KEYCODE_DPAD_UP);
// the selection has been removed.
assertEquals(selectionStart, Selection.getSelectionStart(mTextView.getText()));
assertEquals(selectionStart, Selection.getSelectionEnd(mTextView.getText()));
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- mTextView.setMovementMethod(null);
- Selection.setSelection((Editable) mTextView.getText(),
- selectionStart, selectionEnd);
- mTextView.requestFocus();
- }
+ mActivity.runOnUiThread(() -> {
+ mTextView.setMovementMethod(null);
+ Selection.setSelection((Editable) mTextView.getText(),
+ selectionStart, selectionEnd);
+ mTextView.requestFocus();
});
mInstrumentation.waitForIdleSync();
assertNull(mTextView.getMovementMethod());
assertEquals(selectionStart, Selection.getSelectionStart(mTextView.getText()));
assertEquals(selectionEnd, Selection.getSelectionEnd(mTextView.getText()));
- sendKeys(KeyEvent.KEYCODE_SHIFT_LEFT, KeyEvent.KEYCODE_ALT_LEFT,
- KeyEvent.KEYCODE_DPAD_UP);
+ CtsKeyEventUtil.sendKeys(mInstrumentation, mTextView, KeyEvent.KEYCODE_SHIFT_LEFT,
+ KeyEvent.KEYCODE_ALT_LEFT, KeyEvent.KEYCODE_DPAD_UP);
// the selection will not be changed.
assertEquals(selectionStart, Selection.getSelectionStart(mTextView.getText()));
assertEquals(selectionEnd, Selection.getSelectionEnd(mTextView.getText()));
@@ -352,11 +355,9 @@
mTextView = findTextView(R.id.textview_text);
final CharSequence text1 =
new SpannableString("URL: http://www.google.com. mailto: account@gmail.com");
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- mTextView.setAutoLinkMask(Linkify.ALL);
- mTextView.setText(text1, BufferType.EDITABLE);
- }
+ mActivity.runOnUiThread(() -> {
+ mTextView.setAutoLinkMask(Linkify.ALL);
+ mTextView.setText(text1, BufferType.EDITABLE);
});
mInstrumentation.waitForIdleSync();
assertEquals(Linkify.ALL, mTextView.getAutoLinkMask());
@@ -370,11 +371,9 @@
final CharSequence text2 =
new SpannableString("name: Jack. tel: +41 44 800 8999");
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- mTextView.setAutoLinkMask(Linkify.PHONE_NUMBERS);
- mTextView.setText(text2, BufferType.EDITABLE);
- }
+ mActivity.runOnUiThread(() -> {
+ mTextView.setAutoLinkMask(Linkify.PHONE_NUMBERS);
+ mTextView.setText(text2, BufferType.EDITABLE);
});
mInstrumentation.waitForIdleSync();
assertEquals(Linkify.PHONE_NUMBERS, mTextView.getAutoLinkMask());
@@ -470,38 +469,78 @@
}
}
- public void testSetHighlightColor() {
- mTextView = new TextView(mActivity);
+ public void testAccessHighlightColor() {
+ final TextView textView = (TextView) mActivity.findViewById(R.id.textview_text);
- mTextView.setHighlightColor(0x00ff00ff);
+ mActivity.runOnUiThread(() -> {
+ textView.setTextIsSelectable(true);
+ textView.setText("abcd", BufferType.EDITABLE);
+ textView.setHighlightColor(Color.BLUE);
+ });
+ mInstrumentation.waitForIdleSync();
+
+ assertTrue(textView.isTextSelectable());
+ assertEquals(Color.BLUE, textView.getHighlightColor());
+
+ // Long click on the text selects all text and shows selection handlers. The view has an
+ // attribute layout_width="wrap_content", so clicked location (the center of the view)
+ // should be on the text.
+ CtsTouchUtils.emulateLongClick(mInstrumentation, textView);
+
+ // At this point the entire content of our TextView should be selected and highlighted
+ // with blue. Now change the highlight to red while the selection is still on.
+ mActivity.runOnUiThread(() -> textView.setHighlightColor(Color.RED));
+ mInstrumentation.waitForIdleSync();
+
+ assertEquals(Color.RED, textView.getHighlightColor());
+ assertTrue(TextUtils.equals("abcd", textView.getText()));
+
+ // Remove the selection
+ mActivity.runOnUiThread(() -> Selection.removeSelection((Spannable) textView.getText()));
+ mInstrumentation.waitForIdleSync();
+
+ // And switch highlight to green after the selection has been removed
+ mActivity.runOnUiThread(() -> textView.setHighlightColor(Color.GREEN));
+ mInstrumentation.waitForIdleSync();
+
+ assertEquals(Color.GREEN, textView.getHighlightColor());
+ assertTrue(TextUtils.equals("abcd", textView.getText()));
}
+ @MediumTest
public void testSetShadowLayer() {
- MockTextView textView = new MockTextView(mActivity);
+ // test values
+ final MockTextView mockTextView = new MockTextView(mActivity);
+
+ mockTextView.setShadowLayer(1.0f, 0.3f, 0.4f, Color.CYAN);
+ assertEquals(Color.CYAN, mockTextView.getShadowColor());
+ assertEquals(0.3f, mockTextView.getShadowDx());
+ assertEquals(0.4f, mockTextView.getShadowDy());
+ assertEquals(1.0f, mockTextView.getShadowRadius());
// shadow is placed to the left and below the text
- textView.setShadowLayer(1.0f, 0.3f, 0.3f, Color.CYAN);
- assertTrue(textView.isPaddingOffsetRequired());
- assertEquals(0, textView.getLeftPaddingOffset());
- assertEquals(0, textView.getTopPaddingOffset());
- assertEquals(1, textView.getRightPaddingOffset());
- assertEquals(1, textView.getBottomPaddingOffset());
+ mockTextView.setShadowLayer(1.0f, 0.3f, 0.3f, Color.CYAN);
+ assertTrue(mockTextView.isPaddingOffsetRequired());
+ assertEquals(0, mockTextView.getLeftPaddingOffset());
+ assertEquals(0, mockTextView.getTopPaddingOffset());
+ assertEquals(1, mockTextView.getRightPaddingOffset());
+ assertEquals(1, mockTextView.getBottomPaddingOffset());
// shadow is placed to the right and above the text
- textView.setShadowLayer(1.0f, -0.8f, -0.8f, Color.CYAN);
- assertTrue(textView.isPaddingOffsetRequired());
- assertEquals(-1, textView.getLeftPaddingOffset());
- assertEquals(-1, textView.getTopPaddingOffset());
- assertEquals(0, textView.getRightPaddingOffset());
- assertEquals(0, textView.getBottomPaddingOffset());
+ mockTextView.setShadowLayer(1.0f, -0.8f, -0.8f, Color.CYAN);
+ assertTrue(mockTextView.isPaddingOffsetRequired());
+ assertEquals(-1, mockTextView.getLeftPaddingOffset());
+ assertEquals(-1, mockTextView.getTopPaddingOffset());
+ assertEquals(0, mockTextView.getRightPaddingOffset());
+ assertEquals(0, mockTextView.getBottomPaddingOffset());
// no shadow
- textView.setShadowLayer(0.0f, 0.0f, 0.0f, Color.CYAN);
- assertFalse(textView.isPaddingOffsetRequired());
- assertEquals(0, textView.getLeftPaddingOffset());
- assertEquals(0, textView.getTopPaddingOffset());
- assertEquals(0, textView.getRightPaddingOffset());
- assertEquals(0, textView.getBottomPaddingOffset());
+ mockTextView.setShadowLayer(0.0f, 0.0f, 0.0f, Color.CYAN);
+ assertFalse(mockTextView.isPaddingOffsetRequired());
+ assertEquals(0, mockTextView.getLeftPaddingOffset());
+ assertEquals(0, mockTextView.getTopPaddingOffset());
+ assertEquals(0, mockTextView.getRightPaddingOffset());
+ assertEquals(0, mockTextView.getBottomPaddingOffset());
}
@UiThreadTest
@@ -675,114 +714,199 @@
mTextView.getPaintFlags());
}
- public void testHeightAndWidth() {
+ @MediumTest
+ public void testHeight() {
mTextView = findTextView(R.id.textview_text);
- int originalWidth = mTextView.getWidth();
- setWidth(mTextView.getWidth() >> 3);
- int originalHeight = mTextView.getHeight();
+ final int originalHeight = mTextView.getHeight();
- setMaxHeight(originalHeight + 1);
+ // test setMaxHeight
+ int newHeight = originalHeight + 1;
+ setMaxHeight(newHeight);
assertEquals(originalHeight, mTextView.getHeight());
+ assertEquals(newHeight, mTextView.getMaxHeight());
- setMaxHeight(originalHeight - 1);
- assertEquals(originalHeight - 1, mTextView.getHeight());
+ newHeight = originalHeight - 1;
+ setMaxHeight(newHeight);
+ assertEquals(newHeight, mTextView.getHeight());
+ assertEquals(newHeight, mTextView.getMaxHeight());
- setMaxHeight(-1);
+ newHeight = -1;
+ setMaxHeight(newHeight);
assertEquals(0, mTextView.getHeight());
+ assertEquals(newHeight, mTextView.getMaxHeight());
- setMaxHeight(Integer.MAX_VALUE);
+ newHeight = Integer.MAX_VALUE;
+ setMaxHeight(newHeight);
assertEquals(originalHeight, mTextView.getHeight());
+ assertEquals(newHeight, mTextView.getMaxHeight());
- setMinHeight(originalHeight + 1);
- assertEquals(originalHeight + 1, mTextView.getHeight());
+ // test setMinHeight
+ newHeight = originalHeight + 1;
+ setMinHeight(newHeight);
+ assertEquals(newHeight, mTextView.getHeight());
+ assertEquals(newHeight, mTextView.getMinHeight());
- setMinHeight(originalHeight - 1);
+ newHeight = originalHeight - 1;
+ setMinHeight(newHeight);
assertEquals(originalHeight, mTextView.getHeight());
+ assertEquals(newHeight, mTextView.getMinHeight());
- setMinHeight(-1);
+ newHeight = -1;
+ setMinHeight(newHeight);
assertEquals(originalHeight, mTextView.getHeight());
+ assertEquals(newHeight, mTextView.getMinHeight());
+ // reset min and max height
setMinHeight(0);
setMaxHeight(Integer.MAX_VALUE);
- setHeight(originalHeight + 1);
- assertEquals(originalHeight + 1, mTextView.getHeight());
+ // test setHeight
+ newHeight = originalHeight + 1;
+ setHeight(newHeight);
+ assertEquals(newHeight, mTextView.getHeight());
+ assertEquals(newHeight, mTextView.getMaxHeight());
+ assertEquals(newHeight, mTextView.getMinHeight());
- setHeight(originalHeight - 1);
- assertEquals(originalHeight - 1, mTextView.getHeight());
+ newHeight = originalHeight - 1;
+ setHeight(newHeight);
+ assertEquals(newHeight, mTextView.getHeight());
+ assertEquals(newHeight, mTextView.getMaxHeight());
+ assertEquals(newHeight, mTextView.getMinHeight());
- setHeight(-1);
+ newHeight = -1;
+ setHeight(newHeight);
assertEquals(0, mTextView.getHeight());
+ assertEquals(newHeight, mTextView.getMaxHeight());
+ assertEquals(newHeight, mTextView.getMinHeight());
setHeight(originalHeight);
assertEquals(originalHeight, mTextView.getHeight());
+ assertEquals(originalHeight, mTextView.getMaxHeight());
+ assertEquals(originalHeight, mTextView.getMinHeight());
- assertEquals(originalWidth >> 3, mTextView.getWidth());
+ // setting max/min lines should cause getMaxHeight/getMinHeight to return -1
+ setMaxLines(2);
+ assertEquals("Setting maxLines should return -1 fir maxHeight",
+ -1, mTextView.getMaxHeight());
- // Min Width
- setMinWidth(originalWidth + 1);
- assertEquals(1, mTextView.getLineCount());
- assertEquals(originalWidth + 1, mTextView.getWidth());
-
- setMinWidth(originalWidth - 1);
- assertEquals(2, mTextView.getLineCount());
- assertEquals(originalWidth - 1, mTextView.getWidth());
-
- // Width
- setWidth(originalWidth + 1);
- assertEquals(1, mTextView.getLineCount());
- assertEquals(originalWidth + 1, mTextView.getWidth());
-
- setWidth(originalWidth - 1);
- assertEquals(2, mTextView.getLineCount());
- assertEquals(originalWidth - 1, mTextView.getWidth());
+ setMinLines(1);
+ assertEquals("Setting minLines should return -1 for minHeight",
+ -1, mTextView.getMinHeight());
}
+ @MediumTest
+ public void testWidth() {
+ mTextView = findTextView(R.id.textview_text);
+ int originalWidth = mTextView.getWidth();
+
+ int newWidth = mTextView.getWidth() / 8;
+ setWidth(newWidth);
+ assertEquals(newWidth, mTextView.getWidth());
+ assertEquals(newWidth, mTextView.getMaxWidth());
+ assertEquals(newWidth, mTextView.getMinWidth());
+
+ // Min Width
+ newWidth = originalWidth + 1;
+ setMinWidth(newWidth);
+ assertEquals(1, mTextView.getLineCount());
+ assertEquals(newWidth, mTextView.getWidth());
+ assertEquals(newWidth, mTextView.getMinWidth());
+
+ newWidth = originalWidth - 1;
+ setMinWidth(originalWidth - 1);
+ assertEquals(2, mTextView.getLineCount());
+ assertEquals(newWidth, mTextView.getWidth());
+ assertEquals(newWidth, mTextView.getMinWidth());
+
+ // Width
+ newWidth = originalWidth + 1;
+ setWidth(newWidth);
+ assertEquals(1, mTextView.getLineCount());
+ assertEquals(newWidth, mTextView.getWidth());
+ assertEquals(newWidth, mTextView.getMaxWidth());
+ assertEquals(newWidth, mTextView.getMinWidth());
+
+ newWidth = originalWidth - 1;
+ setWidth(newWidth);
+ assertEquals(2, mTextView.getLineCount());
+ assertEquals(newWidth, mTextView.getWidth());
+ assertEquals(newWidth, mTextView.getMaxWidth());
+ assertEquals(newWidth, mTextView.getMinWidth());
+
+ // setting ems should cause getMaxWidth/getMinWidth to return -1
+ setEms(1);
+ assertEquals("Setting ems should return -1 for maxWidth", -1, mTextView.getMaxWidth());
+ assertEquals("Setting ems should return -1 for maxWidth", -1, mTextView.getMinWidth());
+ }
+
+ @MediumTest
public void testSetMinEms() {
mTextView = findTextView(R.id.textview_text);
assertEquals(1, mTextView.getLineCount());
- int originalWidth = mTextView.getWidth();
- int originalEms = originalWidth / mTextView.getLineHeight();
+ final int originalWidth = mTextView.getWidth();
+ final int originalEms = originalWidth / mTextView.getLineHeight();
setMinEms(originalEms + 1);
assertEquals((originalEms + 1) * mTextView.getLineHeight(), mTextView.getWidth());
+ assertEquals(-1, mTextView.getMinWidth());
+ assertEquals(originalEms + 1, mTextView.getMinEms());
setMinEms(originalEms - 1);
assertEquals(originalWidth, mTextView.getWidth());
+ assertEquals(-1, mTextView.getMinWidth());
+ assertEquals(originalEms - 1, mTextView.getMinEms());
+
+ setMinWidth(1);
+ assertEquals(-1, mTextView.getMinEms());
}
+ @MediumTest
public void testSetMaxEms() {
mTextView = findTextView(R.id.textview_text);
assertEquals(1, mTextView.getLineCount());
- int originalWidth = mTextView.getWidth();
- int originalEms = originalWidth / mTextView.getLineHeight();
+
+ final int originalWidth = mTextView.getWidth();
+ final int originalEms = originalWidth / mTextView.getLineHeight();
setMaxEms(originalEms + 1);
assertEquals(1, mTextView.getLineCount());
assertEquals(originalWidth, mTextView.getWidth());
+ assertEquals(-1, mTextView.getMaxWidth());
+ assertEquals(originalEms + 1, mTextView.getMaxEms());
setMaxEms(originalEms - 1);
assertTrue(1 < mTextView.getLineCount());
- assertEquals((originalEms - 1) * mTextView.getLineHeight(),
- mTextView.getWidth());
+ assertEquals((originalEms - 1) * mTextView.getLineHeight(), mTextView.getWidth());
+ assertEquals(-1, mTextView.getMaxWidth());
+ assertEquals(originalEms - 1, mTextView.getMaxEms());
+
+ setMaxWidth(originalWidth);
+ assertEquals(-1, mTextView.getMaxEms());
}
+ @MediumTest
public void testSetEms() {
mTextView = findTextView(R.id.textview_text);
assertEquals("check height", 1, mTextView.getLineCount());
- int originalWidth = mTextView.getWidth();
- int originalEms = originalWidth / mTextView.getLineHeight();
+ final int originalWidth = mTextView.getWidth();
+ final int originalEms = originalWidth / mTextView.getLineHeight();
setEms(originalEms + 1);
assertEquals(1, mTextView.getLineCount());
- assertEquals((originalEms + 1) * mTextView.getLineHeight(),
- mTextView.getWidth());
+ assertEquals((originalEms + 1) * mTextView.getLineHeight(), mTextView.getWidth());
+ assertEquals(-1, mTextView.getMinWidth());
+ assertEquals(-1, mTextView.getMaxWidth());
+ assertEquals(originalEms + 1, mTextView.getMinEms());
+ assertEquals(originalEms + 1, mTextView.getMaxEms());
setEms(originalEms - 1);
assertTrue((1 < mTextView.getLineCount()));
- assertEquals((originalEms - 1) * mTextView.getLineHeight(),
- mTextView.getWidth());
+ assertEquals((originalEms - 1) * mTextView.getLineHeight(), mTextView.getWidth());
+ assertEquals(-1, mTextView.getMinWidth());
+ assertEquals(-1, mTextView.getMaxWidth());
+ assertEquals(originalEms - 1, mTextView.getMinEms());
+ assertEquals(originalEms - 1, mTextView.getMaxEms());
}
public void testSetLineSpacing() {
@@ -830,31 +954,21 @@
public void testSetElegantLineHeight() {
mTextView = findTextView(R.id.textview_text);
assertFalse(mTextView.getPaint().isElegantTextHeight());
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- mTextView.setWidth(mTextView.getWidth() / 3);
- mTextView.setPadding(1, 2, 3, 4);
- mTextView.setGravity(Gravity.BOTTOM);
- }
+ mActivity.runOnUiThread(() -> {
+ mTextView.setWidth(mTextView.getWidth() / 3);
+ mTextView.setPadding(1, 2, 3, 4);
+ mTextView.setGravity(Gravity.BOTTOM);
});
mInstrumentation.waitForIdleSync();
int oldHeight = mTextView.getHeight();
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- mTextView.setElegantTextHeight(true);
- }
- });
+ mActivity.runOnUiThread(() -> mTextView.setElegantTextHeight(true));
mInstrumentation.waitForIdleSync();
assertTrue(mTextView.getPaint().isElegantTextHeight());
assertTrue(mTextView.getHeight() > oldHeight);
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- mTextView.setElegantTextHeight(false);
- }
- });
+ mActivity.runOnUiThread(() -> mTextView.setElegantTextHeight(false));
mInstrumentation.waitForIdleSync();
assertFalse(mTextView.getPaint().isElegantTextHeight());
assertTrue(mTextView.getHeight() == oldHeight);
@@ -880,11 +994,7 @@
assertFalse(mTextView.getFreezesText());
final CharSequence text = "Hello, TextView.";
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- mTextView.setText(text);
- }
- });
+ mActivity.runOnUiThread(() -> mTextView.setText(text));
mInstrumentation.waitForIdleSync();
final URLSpan urlSpan = new URLSpan("ctstest://TextView/test");
@@ -893,12 +1003,10 @@
ActivityMonitor am = instrumentation.addMonitor(MockURLSpanTestActivity.class.getName(),
null, false);
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- Uri uri = Uri.parse(urlSpan.getURL());
- Intent intent = new Intent(Intent.ACTION_VIEW, uri);
- mActivity.startActivity(intent);
- }
+ mActivity.runOnUiThread(() -> {
+ Uri uri = Uri.parse(urlSpan.getURL());
+ Intent intent = new Intent(Intent.ACTION_VIEW, uri);
+ mActivity.startActivity(intent);
});
Activity newActivity = am.waitForActivityWithTimeout(TIMEOUT);
@@ -913,22 +1021,16 @@
mTextView.setFreezesText(true);
assertTrue(mTextView.getFreezesText());
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- mTextView.setText(text);
- }
- });
+ mActivity.runOnUiThread(() -> mTextView.setText(text));
mInstrumentation.waitForIdleSync();
// TODO: How to simulate the TextView in frozen icicles.
am = instrumentation.addMonitor(MockURLSpanTestActivity.class.getName(),
null, false);
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- Uri uri = Uri.parse(urlSpan.getURL());
- Intent intent = new Intent(Intent.ACTION_VIEW, uri);
- mActivity.startActivity(intent);
- }
+ mActivity.runOnUiThread(() -> {
+ Uri uri = Uri.parse(urlSpan.getURL());
+ Intent intent = new Intent(Intent.ACTION_VIEW, uri);
+ mActivity.startActivity(intent);
});
Activity oldActivity = newActivity;
@@ -949,31 +1051,30 @@
public void testSetEditableFactory() {
mTextView = new TextView(mActivity);
String text = "sample";
- MockEditableFactory factory = new MockEditableFactory();
- mTextView.setEditableFactory(factory);
- factory.reset();
+ final Editable.Factory mockEditableFactory = spy(new Editable.Factory());
+ doCallRealMethod().when(mockEditableFactory).newEditable(any(CharSequence.class));
+ mTextView.setEditableFactory(mockEditableFactory);
+
mTextView.setText(text);
- assertFalse(factory.hasCalledNewEditable());
+ verify(mockEditableFactory, never()).newEditable(any(CharSequence.class));
- factory.reset();
+ reset(mockEditableFactory);
mTextView.setText(text, BufferType.SPANNABLE);
- assertFalse(factory.hasCalledNewEditable());
+ verify(mockEditableFactory, never()).newEditable(any(CharSequence.class));
- factory.reset();
+ reset(mockEditableFactory);
mTextView.setText(text, BufferType.NORMAL);
- assertFalse(factory.hasCalledNewEditable());
+ verify(mockEditableFactory, never()).newEditable(any(CharSequence.class));
- factory.reset();
+ reset(mockEditableFactory);
mTextView.setText(text, BufferType.EDITABLE);
- assertTrue(factory.hasCalledNewEditable());
- assertEquals(text, factory.getSource());
+ verify(mockEditableFactory, times(1)).newEditable(text);
mTextView.setKeyListener(DigitsKeyListener.getInstance());
- factory.reset();
+ reset(mockEditableFactory);
mTextView.setText(text, BufferType.EDITABLE);
- assertTrue(factory.hasCalledNewEditable());
- assertEquals(text, factory.getSource());
+ verify(mockEditableFactory, times(1)).newEditable(text);
try {
mTextView.setEditableFactory(null);
@@ -985,31 +1086,30 @@
public void testSetSpannableFactory() {
mTextView = new TextView(mActivity);
String text = "sample";
- MockSpannableFactory factory = new MockSpannableFactory();
- mTextView.setSpannableFactory(factory);
- factory.reset();
+ final Spannable.Factory mockSpannableFactory = spy(new Spannable.Factory());
+ doCallRealMethod().when(mockSpannableFactory).newSpannable(any(CharSequence.class));
+ mTextView.setSpannableFactory(mockSpannableFactory);
+
mTextView.setText(text);
- assertFalse(factory.hasCalledNewSpannable());
+ verify(mockSpannableFactory, never()).newSpannable(any(CharSequence.class));
- factory.reset();
+ reset(mockSpannableFactory);
mTextView.setText(text, BufferType.EDITABLE);
- assertFalse(factory.hasCalledNewSpannable());
+ verify(mockSpannableFactory, never()).newSpannable(any(CharSequence.class));
- factory.reset();
+ reset(mockSpannableFactory);
mTextView.setText(text, BufferType.NORMAL);
- assertFalse(factory.hasCalledNewSpannable());
+ verify(mockSpannableFactory, never()).newSpannable(any(CharSequence.class));
- factory.reset();
+ reset(mockSpannableFactory);
mTextView.setText(text, BufferType.SPANNABLE);
- assertTrue(factory.hasCalledNewSpannable());
- assertEquals(text, factory.getSource());
+ verify(mockSpannableFactory, times(1)).newSpannable(text);
mTextView.setMovementMethod(LinkMovementMethod.getInstance());
- factory.reset();
+ reset(mockSpannableFactory);
mTextView.setText(text, BufferType.NORMAL);
- assertTrue(factory.hasCalledNewSpannable());
- assertEquals(text, factory.getSource());
+ verify(mockSpannableFactory, times(1)).newSpannable(text);
try {
mTextView.setSpannableFactory(null);
@@ -1325,12 +1425,7 @@
ViewGroup.LayoutParams.WRAP_CONTENT);
layout.addView(textView, layoutParams);
layout.setLayoutParams(layoutParams);
- mActivity.runOnUiThread(new Runnable() {
- @Override
- public void run() {
- getActivity().setContentView(layout);
- }
- });
+ mActivity.runOnUiThread(() -> getActivity().setContentView(layout));
getInstrumentation().waitForIdleSync();
// measure height of text with no span
@@ -1343,12 +1438,7 @@
drawable.setBounds(0, 0, spanHeight, spanHeight);
ImageSpan span = new ImageSpan(drawable);
text.setSpan(span, 0, 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- mActivity.runOnUiThread(new Runnable() {
- @Override
- public void run() {
- textView.setText(text);
- }
- });
+ mActivity.runOnUiThread(() -> textView.setText(text));
mInstrumentation.waitForIdleSync();
// measure height with span
@@ -1358,12 +1448,7 @@
// remove the span
text.removeSpan(span);
- mActivity.runOnUiThread(new Runnable() {
- @Override
- public void run() {
- textView.setText(text);
- }
- });
+ mActivity.runOnUiThread(() -> textView.setText(text));
mInstrumentation.waitForIdleSync();
final int heightAfterRemoveSpan = textView.getHeight();
@@ -1372,98 +1457,88 @@
}
public void testRemoveSelectionWithSelectionHandles() {
- initTextViewForTyping();
+ initTextViewForTypingOnUiThread();
- mActivity.runOnUiThread(new Runnable() {
- @Override
- public void run() {
- mTextView.setTextIsSelectable(true);
- mTextView.setText("abcd", BufferType.EDITABLE);
- }
+ assertFalse(mTextView.isTextSelectable());
+ mActivity.runOnUiThread(() -> {
+ mTextView.setTextIsSelectable(true);
+ mTextView.setText("abcd", BufferType.EDITABLE);
});
mInstrumentation.waitForIdleSync();
+ assertTrue(mTextView.isTextSelectable());
// Long click on the text selects all text and shows selection handlers. The view has an
// attribute layout_width="wrap_content", so clicked location (the center of the view)
// should be on the text.
- TouchUtils.longClickView(this, mTextView);
+ CtsTouchUtils.emulateLongClick(mInstrumentation, mTextView);
- mActivity.runOnUiThread(new Runnable() {
- @Override
- public void run() {
- Selection.removeSelection((Spannable) mTextView.getText());
- }
- });
-
- // Make sure that a crash doesn't happen with {@link Selection#removeSelection}.
+ mActivity.runOnUiThread(() -> Selection.removeSelection((Spannable) mTextView.getText()));
mInstrumentation.waitForIdleSync();
+
+ assertTrue(TextUtils.equals("abcd", mTextView.getText()));
}
public void testUndo_insert() {
- initTextViewForTyping();
+ initTextViewForTypingOnUiThread();
// Type some text.
- mKeyEventUtil.sendString(mTextView, "abc");
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- // Precondition: The cursor is at the end of the text.
- assertEquals(3, mTextView.getSelectionStart());
+ CtsKeyEventUtil.sendString(mInstrumentation, mTextView, "abc");
+ mActivity.runOnUiThread(() -> {
+ // Precondition: The cursor is at the end of the text.
+ assertEquals(3, mTextView.getSelectionStart());
- // Undo removes the typed string in one step.
- mTextView.onTextContextMenuItem(android.R.id.undo);
- assertEquals("", mTextView.getText().toString());
- assertEquals(0, mTextView.getSelectionStart());
+ // Undo removes the typed string in one step.
+ mTextView.onTextContextMenuItem(android.R.id.undo);
+ assertEquals("", mTextView.getText().toString());
+ assertEquals(0, mTextView.getSelectionStart());
- // Redo restores the text and cursor position.
- mTextView.onTextContextMenuItem(android.R.id.redo);
- assertEquals("abc", mTextView.getText().toString());
- assertEquals(3, mTextView.getSelectionStart());
+ // Redo restores the text and cursor position.
+ mTextView.onTextContextMenuItem(android.R.id.redo);
+ assertEquals("abc", mTextView.getText().toString());
+ assertEquals(3, mTextView.getSelectionStart());
- // Undoing the redo clears the text again.
- mTextView.onTextContextMenuItem(android.R.id.undo);
- assertEquals("", mTextView.getText().toString());
+ // Undoing the redo clears the text again.
+ mTextView.onTextContextMenuItem(android.R.id.undo);
+ assertEquals("", mTextView.getText().toString());
- // Undo when the undo stack is empty does nothing.
- mTextView.onTextContextMenuItem(android.R.id.undo);
- assertEquals("", mTextView.getText().toString());
- }
+ // Undo when the undo stack is empty does nothing.
+ mTextView.onTextContextMenuItem(android.R.id.undo);
+ assertEquals("", mTextView.getText().toString());
});
mInstrumentation.waitForIdleSync();
}
public void testUndo_delete() {
- initTextViewForTyping();
+ initTextViewForTypingOnUiThread();
// Simulate deleting text and undoing it.
- mKeyEventUtil.sendString(mTextView, "xyz");
- mKeyEventUtil.sendKeys(mTextView, KeyEvent.KEYCODE_DEL, KeyEvent.KEYCODE_DEL,
- KeyEvent.KEYCODE_DEL);
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- // Precondition: The text was actually deleted.
- assertEquals("", mTextView.getText().toString());
- assertEquals(0, mTextView.getSelectionStart());
+ CtsKeyEventUtil.sendString(mInstrumentation, mTextView, "xyz");
+ CtsKeyEventUtil.sendKeys(mInstrumentation, mTextView, KeyEvent.KEYCODE_DEL,
+ KeyEvent.KEYCODE_DEL, KeyEvent.KEYCODE_DEL);
+ mActivity.runOnUiThread(() -> {
+ // Precondition: The text was actually deleted.
+ assertEquals("", mTextView.getText().toString());
+ assertEquals(0, mTextView.getSelectionStart());
- // Undo restores the typed string and cursor position in one step.
- mTextView.onTextContextMenuItem(android.R.id.undo);
- assertEquals("xyz", mTextView.getText().toString());
- assertEquals(3, mTextView.getSelectionStart());
+ // Undo restores the typed string and cursor position in one step.
+ mTextView.onTextContextMenuItem(android.R.id.undo);
+ assertEquals("xyz", mTextView.getText().toString());
+ assertEquals(3, mTextView.getSelectionStart());
- // Redo removes the text in one step.
- mTextView.onTextContextMenuItem(android.R.id.redo);
- assertEquals("", mTextView.getText().toString());
- assertEquals(0, mTextView.getSelectionStart());
+ // Redo removes the text in one step.
+ mTextView.onTextContextMenuItem(android.R.id.redo);
+ assertEquals("", mTextView.getText().toString());
+ assertEquals(0, mTextView.getSelectionStart());
- // Undoing the redo restores the text again.
- mTextView.onTextContextMenuItem(android.R.id.undo);
- assertEquals("xyz", mTextView.getText().toString());
- assertEquals(3, mTextView.getSelectionStart());
+ // Undoing the redo restores the text again.
+ mTextView.onTextContextMenuItem(android.R.id.undo);
+ assertEquals("xyz", mTextView.getText().toString());
+ assertEquals(3, mTextView.getSelectionStart());
- // Undoing again undoes the original typing.
- mTextView.onTextContextMenuItem(android.R.id.undo);
- assertEquals("", mTextView.getText().toString());
- assertEquals(0, mTextView.getSelectionStart());
- }
+ // Undoing again undoes the original typing.
+ mTextView.onTextContextMenuItem(android.R.id.undo);
+ assertEquals("", mTextView.getText().toString());
+ assertEquals(0, mTextView.getSelectionStart());
});
mInstrumentation.waitForIdleSync();
}
@@ -1471,9 +1546,13 @@
// Initialize the text view for simulated IME typing. Must be called on UI thread.
private InputConnection initTextViewForSimulatedIme() {
mTextView = findTextView(R.id.textview_text);
- mTextView.setKeyListener(QwertyKeyListener.getInstance(false, Capitalize.NONE));
- mTextView.setText("", BufferType.EDITABLE);
- return mTextView.onCreateInputConnection(new EditorInfo());
+ return initTextViewForSimulatedIme(mTextView);
+ }
+
+ private InputConnection initTextViewForSimulatedIme(TextView textView) {
+ textView.setKeyListener(QwertyKeyListener.getInstance(false, Capitalize.NONE));
+ textView.setText("", BufferType.EDITABLE);
+ return textView.onCreateInputConnection(new EditorInfo());
}
// Simulates IME composing text behavior.
@@ -1527,6 +1606,83 @@
}
@UiThreadTest
+ public void testUndo_imeInsertAndDeleteLatin() {
+ InputConnection input = initTextViewForSimulatedIme();
+
+ setComposingTextInBatch(input, "t");
+ setComposingTextInBatch(input, "te");
+ setComposingTextInBatch(input, "tes");
+ setComposingTextInBatch(input, "test");
+ setComposingTextInBatch(input, "tes");
+ setComposingTextInBatch(input, "te");
+ setComposingTextInBatch(input, "t");
+
+ input.beginBatchEdit();
+ input.setComposingText("", 1);
+ input.finishComposingText();
+ input.endBatchEdit();
+
+ mTextView.onTextContextMenuItem(android.R.id.undo);
+ assertEquals("test", mTextView.getText().toString());
+ mTextView.onTextContextMenuItem(android.R.id.undo);
+ assertEquals("", mTextView.getText().toString());
+ }
+
+ @UiThreadTest
+ public void testUndo_imeAutoCorrection() {
+ mTextView = findTextView(R.id.textview_text);
+ TextView spiedTextView = spy(mTextView);
+ InputConnection input = initTextViewForSimulatedIme(spiedTextView);
+
+ // Start typing a composition.
+ setComposingTextInBatch(input, "t");
+ setComposingTextInBatch(input, "te");
+ setComposingTextInBatch(input, "teh");
+
+ CorrectionInfo correctionInfo = new CorrectionInfo(0, "teh", "the");
+ reset(spiedTextView);
+ input.beginBatchEdit();
+ // Auto correct "teh" to "the".
+ assertTrue(input.commitCorrection(correctionInfo));
+ input.commitText("the", 1);
+ input.endBatchEdit();
+
+ verify(spiedTextView, times(1)).onCommitCorrection(refEq(correctionInfo));
+
+ assertEquals("the", spiedTextView.getText().toString());
+ spiedTextView.onTextContextMenuItem(android.R.id.undo);
+ assertEquals("teh", spiedTextView.getText().toString());
+ spiedTextView.onTextContextMenuItem(android.R.id.undo);
+ assertEquals("", spiedTextView.getText().toString());
+ }
+
+ @UiThreadTest
+ public void testUndo_imeAutoCompletion() {
+ mTextView = findTextView(R.id.textview_text);
+ TextView spiedTextView = spy(mTextView);
+ InputConnection input = initTextViewForSimulatedIme(spiedTextView);
+
+ // Start typing a composition.
+ setComposingTextInBatch(input, "a");
+ setComposingTextInBatch(input, "an");
+ setComposingTextInBatch(input, "and");
+
+ CompletionInfo completionInfo = new CompletionInfo(0, 0, "android");
+ reset(spiedTextView);
+ input.beginBatchEdit();
+ // Auto complete "and" to "android".
+ assertTrue(input.commitCompletion(completionInfo));
+ input.commitText("android", 1);
+ input.endBatchEdit();
+
+ verify(spiedTextView, times(1)).onCommitCompletion(refEq(completionInfo));
+
+ assertEquals("android", spiedTextView.getText().toString());
+ spiedTextView.onTextContextMenuItem(android.R.id.undo);
+ assertEquals("", spiedTextView.getText().toString());
+ }
+
+ @UiThreadTest
public void testUndo_imeCancel() {
InputConnection input = initTextViewForSimulatedIme();
mTextView.setText("flower");
@@ -1540,9 +1696,8 @@
// Cancel the composition.
setComposingTextInBatch(input, "");
- // Undo and redo do nothing.
mTextView.onTextContextMenuItem(android.R.id.undo);
- assertEquals("flower", mTextView.getText().toString());
+ assertEquals(HA + "n" + "flower", mTextView.getText().toString());
mTextView.onTextContextMenuItem(android.R.id.redo);
assertEquals("flower", mTextView.getText().toString());
}
@@ -1564,409 +1719,364 @@
}
public void testUndo_setText() {
- initTextViewForTyping();
+ initTextViewForTypingOnUiThread();
// Create two undo operations, an insert and a delete.
- mKeyEventUtil.sendString(mTextView, "xyz");
- mKeyEventUtil.sendKeys(mTextView, KeyEvent.KEYCODE_DEL, KeyEvent.KEYCODE_DEL,
- KeyEvent.KEYCODE_DEL);
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- // Calling setText() clears both undo operations, so undo doesn't happen.
- mTextView.setText("Hello", BufferType.EDITABLE);
- mTextView.onTextContextMenuItem(android.R.id.undo);
- assertEquals("Hello", mTextView.getText().toString());
+ CtsKeyEventUtil.sendString(mInstrumentation, mTextView, "xyz");
+ CtsKeyEventUtil.sendKeys(mInstrumentation, mTextView, KeyEvent.KEYCODE_DEL,
+ KeyEvent.KEYCODE_DEL, KeyEvent.KEYCODE_DEL);
+ mActivity.runOnUiThread(() -> {
+ // Calling setText() clears both undo operations, so undo doesn't happen.
+ mTextView.setText("Hello", BufferType.EDITABLE);
+ mTextView.onTextContextMenuItem(android.R.id.undo);
+ assertEquals("Hello", mTextView.getText().toString());
- // Clearing text programmatically does not undo either.
- mTextView.setText("", BufferType.EDITABLE);
- mTextView.onTextContextMenuItem(android.R.id.undo);
- assertEquals("", mTextView.getText().toString());
- }
+ // Clearing text programmatically does not undo either.
+ mTextView.setText("", BufferType.EDITABLE);
+ mTextView.onTextContextMenuItem(android.R.id.undo);
+ assertEquals("", mTextView.getText().toString());
});
mInstrumentation.waitForIdleSync();
}
public void testRedo_setText() {
- initTextViewForTyping();
+ initTextViewForTypingOnUiThread();
// Type some text. This creates an undo entry.
- mKeyEventUtil.sendString(mTextView, "abc");
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- // Undo the typing to create a redo entry.
- mTextView.onTextContextMenuItem(android.R.id.undo);
+ CtsKeyEventUtil.sendString(mInstrumentation, mTextView, "abc");
+ mActivity.runOnUiThread(() -> {
+ // Undo the typing to create a redo entry.
+ mTextView.onTextContextMenuItem(android.R.id.undo);
- // Calling setText() clears the redo stack, so redo doesn't happen.
- mTextView.setText("Hello", BufferType.EDITABLE);
- mTextView.onTextContextMenuItem(android.R.id.redo);
- assertEquals("Hello", mTextView.getText().toString());
- }
+ // Calling setText() clears the redo stack, so redo doesn't happen.
+ mTextView.setText("Hello", BufferType.EDITABLE);
+ mTextView.onTextContextMenuItem(android.R.id.redo);
+ assertEquals("Hello", mTextView.getText().toString());
});
mInstrumentation.waitForIdleSync();
}
public void testUndo_directAppend() {
- initTextViewForTyping();
+ initTextViewForTypingOnUiThread();
// Type some text.
- mKeyEventUtil.sendString(mTextView, "abc");
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- // Programmatically append some text.
- mTextView.append("def");
- assertEquals("abcdef", mTextView.getText().toString());
+ CtsKeyEventUtil.sendString(mInstrumentation, mTextView, "abc");
+ mActivity.runOnUiThread(() -> {
+ // Programmatically append some text.
+ mTextView.append("def");
+ assertEquals("abcdef", mTextView.getText().toString());
- // Undo removes the append as a separate step.
- mTextView.onTextContextMenuItem(android.R.id.undo);
- assertEquals("abc", mTextView.getText().toString());
+ // Undo removes the append as a separate step.
+ mTextView.onTextContextMenuItem(android.R.id.undo);
+ assertEquals("abc", mTextView.getText().toString());
- // Another undo removes the original typing.
- mTextView.onTextContextMenuItem(android.R.id.undo);
- assertEquals("", mTextView.getText().toString());
- }
+ // Another undo removes the original typing.
+ mTextView.onTextContextMenuItem(android.R.id.undo);
+ assertEquals("", mTextView.getText().toString());
});
mInstrumentation.waitForIdleSync();
}
public void testUndo_directInsert() {
- initTextViewForTyping();
+ initTextViewForTypingOnUiThread();
// Type some text.
- mKeyEventUtil.sendString(mTextView, "abc");
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- // Directly modify the underlying Editable to insert some text.
- // NOTE: This is a violation of the API of getText() which specifies that the
- // returned object should not be modified. However, some apps do this anyway and
- // the framework needs to handle it.
- Editable text = (Editable) mTextView.getText();
- text.insert(0, "def");
- assertEquals("defabc", mTextView.getText().toString());
+ CtsKeyEventUtil.sendString(mInstrumentation, mTextView, "abc");
+ mActivity.runOnUiThread(() -> {
+ // Directly modify the underlying Editable to insert some text.
+ // NOTE: This is a violation of the API of getText() which specifies that the
+ // returned object should not be modified. However, some apps do this anyway and
+ // the framework needs to handle it.
+ Editable text = (Editable) mTextView.getText();
+ text.insert(0, "def");
+ assertEquals("defabc", mTextView.getText().toString());
- // Undo removes the insert as a separate step.
- mTextView.onTextContextMenuItem(android.R.id.undo);
- assertEquals("abc", mTextView.getText().toString());
+ // Undo removes the insert as a separate step.
+ mTextView.onTextContextMenuItem(android.R.id.undo);
+ assertEquals("abc", mTextView.getText().toString());
- // Another undo removes the original typing.
- mTextView.onTextContextMenuItem(android.R.id.undo);
- assertEquals("", mTextView.getText().toString());
- }
+ // Another undo removes the original typing.
+ mTextView.onTextContextMenuItem(android.R.id.undo);
+ assertEquals("", mTextView.getText().toString());
});
mInstrumentation.waitForIdleSync();
}
+ @UiThreadTest
public void testUndo_noCursor() {
initTextViewForTyping();
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- // Append some text to create an undo operation. There is no cursor present.
- mTextView.append("cat");
+ // Append some text to create an undo operation. There is no cursor present.
+ mTextView.append("cat");
- // Place the cursor at the end of the text so the undo will have to change it.
- Selection.setSelection((Spannable) mTextView.getText(), 3);
+ // Place the cursor at the end of the text so the undo will have to change it.
+ Selection.setSelection((Spannable) mTextView.getText(), 3);
- // Undo the append. This should not crash, despite not having a valid cursor
- // position in the undo operation.
- mTextView.onTextContextMenuItem(android.R.id.undo);
- }
- });
- mInstrumentation.waitForIdleSync();
+ // Undo the append. This should not crash, despite not having a valid cursor
+ // position in the undo operation.
+ mTextView.onTextContextMenuItem(android.R.id.undo);
}
public void testUndo_textWatcher() {
- initTextViewForTyping();
+ initTextViewForTypingOnUiThread();
// Add a TextWatcher that converts the text to spaces on each change.
mTextView.addTextChangedListener(new ConvertToSpacesTextWatcher());
// Type some text.
- mKeyEventUtil.sendString(mTextView, "abc");
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- // TextWatcher altered the text.
- assertEquals(" ", mTextView.getText().toString());
+ CtsKeyEventUtil.sendString(mInstrumentation, mTextView, "abc");
+ mActivity.runOnUiThread(() -> {
+ // TextWatcher altered the text.
+ assertEquals(" ", mTextView.getText().toString());
- // Undo reverses both changes in one step.
- mTextView.onTextContextMenuItem(android.R.id.undo);
- assertEquals("", mTextView.getText().toString());
- }
+ // Undo reverses both changes in one step.
+ mTextView.onTextContextMenuItem(android.R.id.undo);
+ assertEquals("", mTextView.getText().toString());
});
mInstrumentation.waitForIdleSync();
}
+ @UiThreadTest
public void testUndo_textWatcherDirectAppend() {
initTextViewForTyping();
// Add a TextWatcher that converts the text to spaces on each change.
mTextView.addTextChangedListener(new ConvertToSpacesTextWatcher());
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- // Programmatically append some text. The TextWatcher changes it to spaces.
- mTextView.append("abc");
- assertEquals(" ", mTextView.getText().toString());
+ // Programmatically append some text. The TextWatcher changes it to spaces.
+ mTextView.append("abc");
+ assertEquals(" ", mTextView.getText().toString());
- // Undo reverses both changes in one step.
- mTextView.onTextContextMenuItem(android.R.id.undo);
- assertEquals("", mTextView.getText().toString());
- }
- });
- mInstrumentation.waitForIdleSync();
+ // Undo reverses both changes in one step.
+ mTextView.onTextContextMenuItem(android.R.id.undo);
+ assertEquals("", mTextView.getText().toString());
}
public void testUndo_shortcuts() {
- initTextViewForTyping();
+ initTextViewForTypingOnUiThread();
// Type some text.
- mKeyEventUtil.sendString(mTextView, "abc");
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- // Pressing Control-Z triggers undo.
- KeyEvent control = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_Z, 0,
- KeyEvent.META_CTRL_LEFT_ON);
- assertTrue(mTextView.onKeyShortcut(KeyEvent.KEYCODE_Z, control));
- assertEquals("", mTextView.getText().toString());
+ CtsKeyEventUtil.sendString(mInstrumentation, mTextView, "abc");
+ mActivity.runOnUiThread(() -> {
+ // Pressing Control-Z triggers undo.
+ KeyEvent control = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_Z, 0,
+ KeyEvent.META_CTRL_LEFT_ON);
+ assertTrue(mTextView.onKeyShortcut(KeyEvent.KEYCODE_Z, control));
+ assertEquals("", mTextView.getText().toString());
- // Pressing Control-Shift-Z triggers redo.
- KeyEvent controlShift = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_Z,
- 0, KeyEvent.META_CTRL_LEFT_ON | KeyEvent.META_SHIFT_LEFT_ON);
- assertTrue(mTextView.onKeyShortcut(KeyEvent.KEYCODE_Z, controlShift));
- assertEquals("abc", mTextView.getText().toString());
- }
+ // Pressing Control-Shift-Z triggers redo.
+ KeyEvent controlShift = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_Z,
+ 0, KeyEvent.META_CTRL_LEFT_ON | KeyEvent.META_SHIFT_LEFT_ON);
+ assertTrue(mTextView.onKeyShortcut(KeyEvent.KEYCODE_Z, controlShift));
+ assertEquals("abc", mTextView.getText().toString());
});
mInstrumentation.waitForIdleSync();
}
public void testUndo_saveInstanceState() {
- initTextViewForTyping();
+ initTextViewForTypingOnUiThread();
// Type some text to create an undo operation.
- mKeyEventUtil.sendString(mTextView, "abc");
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- // Parcel and unparcel the TextView.
- Parcelable state = mTextView.onSaveInstanceState();
- mTextView.onRestoreInstanceState(state);
- }
+ CtsKeyEventUtil.sendString(mInstrumentation, mTextView, "abc");
+ mActivity.runOnUiThread(() -> {
+ // Parcel and unparcel the TextView.
+ Parcelable state = mTextView.onSaveInstanceState();
+ mTextView.onRestoreInstanceState(state);
});
mInstrumentation.waitForIdleSync();
// Delete a character to create a new undo operation.
- mKeyEventUtil.sendKeys(mTextView, KeyEvent.KEYCODE_DEL);
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- assertEquals("ab", mTextView.getText().toString());
+ CtsKeyEventUtil.sendKeys(mInstrumentation, mTextView, KeyEvent.KEYCODE_DEL);
+ mActivity.runOnUiThread(() -> {
+ assertEquals("ab", mTextView.getText().toString());
- // Undo the delete.
- mTextView.onTextContextMenuItem(android.R.id.undo);
- assertEquals("abc", mTextView.getText().toString());
+ // Undo the delete.
+ mTextView.onTextContextMenuItem(android.R.id.undo);
+ assertEquals("abc", mTextView.getText().toString());
- // Undo the typing, which verifies that the original undo operation was parceled
- // correctly.
- mTextView.onTextContextMenuItem(android.R.id.undo);
- assertEquals("", mTextView.getText().toString());
+ // Undo the typing, which verifies that the original undo operation was parceled
+ // correctly.
+ mTextView.onTextContextMenuItem(android.R.id.undo);
+ assertEquals("", mTextView.getText().toString());
- // Parcel and unparcel the undo stack (which is empty but has been used and may
- // contain other state).
- Parcelable state = mTextView.onSaveInstanceState();
- mTextView.onRestoreInstanceState(state);
- }
+ // Parcel and unparcel the undo stack (which is empty but has been used and may
+ // contain other state).
+ Parcelable state = mTextView.onSaveInstanceState();
+ mTextView.onRestoreInstanceState(state);
});
mInstrumentation.waitForIdleSync();
}
public void testUndo_saveInstanceStateEmpty() {
- initTextViewForTyping();
+ initTextViewForTypingOnUiThread();
// Type and delete to create two new undo operations.
- mKeyEventUtil.sendString(mTextView, "a");
- mKeyEventUtil.sendKeys(mTextView, KeyEvent.KEYCODE_DEL);
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- // Empty the undo stack then parcel and unparcel the TextView. While the undo
- // stack contains no operations it may contain other state.
- mTextView.onTextContextMenuItem(android.R.id.undo);
- mTextView.onTextContextMenuItem(android.R.id.undo);
- Parcelable state = mTextView.onSaveInstanceState();
- mTextView.onRestoreInstanceState(state);
- }
+ CtsKeyEventUtil.sendString(mInstrumentation, mTextView, "a");
+ CtsKeyEventUtil.sendKeys(mInstrumentation, mTextView, KeyEvent.KEYCODE_DEL);
+ mActivity.runOnUiThread(() -> {
+ // Empty the undo stack then parcel and unparcel the TextView. While the undo
+ // stack contains no operations it may contain other state.
+ mTextView.onTextContextMenuItem(android.R.id.undo);
+ mTextView.onTextContextMenuItem(android.R.id.undo);
+ Parcelable state = mTextView.onSaveInstanceState();
+ mTextView.onRestoreInstanceState(state);
});
mInstrumentation.waitForIdleSync();
// Create two more undo operations.
- mKeyEventUtil.sendString(mTextView, "b");
- mKeyEventUtil.sendKeys(mTextView, KeyEvent.KEYCODE_DEL);
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- // Verify undo still works.
- mTextView.onTextContextMenuItem(android.R.id.undo);
- assertEquals("b", mTextView.getText().toString());
- mTextView.onTextContextMenuItem(android.R.id.undo);
- assertEquals("", mTextView.getText().toString());
- }
+ CtsKeyEventUtil.sendString(mInstrumentation, mTextView, "b");
+ CtsKeyEventUtil.sendKeys(mInstrumentation, mTextView, KeyEvent.KEYCODE_DEL);
+ mActivity.runOnUiThread(() -> {
+ // Verify undo still works.
+ mTextView.onTextContextMenuItem(android.R.id.undo);
+ assertEquals("b", mTextView.getText().toString());
+ mTextView.onTextContextMenuItem(android.R.id.undo);
+ assertEquals("", mTextView.getText().toString());
});
mInstrumentation.waitForIdleSync();
}
+ @UiThreadTest
public void testCopyAndPaste() {
initTextViewForTyping();
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- mTextView.setText("abcd", BufferType.EDITABLE);
- mTextView.setSelected(true);
- // Copy "bc".
- Selection.setSelection((Spannable) mTextView.getText(), 1, 3);
- mTextView.onTextContextMenuItem(android.R.id.copy);
+ mTextView.setText("abcd", BufferType.EDITABLE);
+ mTextView.setSelected(true);
- // Paste "bc" between "b" and "c".
- Selection.setSelection((Spannable) mTextView.getText(), 2, 2);
- mTextView.onTextContextMenuItem(android.R.id.paste);
- assertEquals("abbccd", mTextView.getText().toString());
+ // Copy "bc".
+ Selection.setSelection((Spannable) mTextView.getText(), 1, 3);
+ mTextView.onTextContextMenuItem(android.R.id.copy);
- // Select entire text and paste "bc".
- Selection.selectAll((Spannable) mTextView.getText());
- mTextView.onTextContextMenuItem(android.R.id.paste);
- assertEquals("bc", mTextView.getText().toString());
- }
- });
- mInstrumentation.waitForIdleSync();
+ // Paste "bc" between "b" and "c".
+ Selection.setSelection((Spannable) mTextView.getText(), 2, 2);
+ mTextView.onTextContextMenuItem(android.R.id.paste);
+ assertEquals("abbccd", mTextView.getText().toString());
+
+ // Select entire text and paste "bc".
+ Selection.selectAll((Spannable) mTextView.getText());
+ mTextView.onTextContextMenuItem(android.R.id.paste);
+ assertEquals("bc", mTextView.getText().toString());
}
public void testCopyAndPaste_byKey() {
- initTextViewForTyping();
+ initTextViewForTypingOnUiThread();
// Type "abc".
- mInstrumentation.sendStringSync("abc");
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- // Select "bc"
- Selection.setSelection((Spannable) mTextView.getText(), 1, 3);
- }
+ CtsKeyEventUtil.sendString(mInstrumentation, mTextView, "abc");
+ mActivity.runOnUiThread(() -> {
+ // Select "bc"
+ Selection.setSelection((Spannable) mTextView.getText(), 1, 3);
});
mInstrumentation.waitForIdleSync();
// Copy "bc"
- sendKeys(KeyEvent.KEYCODE_COPY);
+ CtsKeyEventUtil.sendKeys(mInstrumentation, mTextView, KeyEvent.KEYCODE_COPY);
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- // Set cursor between 'b' and 'c'.
- Selection.setSelection((Spannable) mTextView.getText(), 2, 2);
- }
+ mActivity.runOnUiThread(() -> {
+ // Set cursor between 'b' and 'c'.
+ Selection.setSelection((Spannable) mTextView.getText(), 2, 2);
});
mInstrumentation.waitForIdleSync();
// Paste "bc"
- sendKeys(KeyEvent.KEYCODE_PASTE);
+ CtsKeyEventUtil.sendKeys(mInstrumentation, mTextView, KeyEvent.KEYCODE_PASTE);
assertEquals("abbcc", mTextView.getText().toString());
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- Selection.selectAll((Spannable) mTextView.getText());
- KeyEvent copyWithMeta = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN,
- KeyEvent.KEYCODE_COPY, 0, KeyEvent.META_SHIFT_LEFT_ON);
- // Shift + copy doesn't perform copy.
- mTextView.onKeyDown(KeyEvent.KEYCODE_COPY, copyWithMeta);
- Selection.setSelection((Spannable) mTextView.getText(), 0, 0);
- mTextView.onTextContextMenuItem(android.R.id.paste);
- assertEquals("bcabbcc", mTextView.getText().toString());
+ mActivity.runOnUiThread(() -> {
+ Selection.selectAll((Spannable) mTextView.getText());
+ KeyEvent copyWithMeta = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN,
+ KeyEvent.KEYCODE_COPY, 0, KeyEvent.META_SHIFT_LEFT_ON);
+ // Shift + copy doesn't perform copy.
+ mTextView.onKeyDown(KeyEvent.KEYCODE_COPY, copyWithMeta);
+ Selection.setSelection((Spannable) mTextView.getText(), 0, 0);
+ mTextView.onTextContextMenuItem(android.R.id.paste);
+ assertEquals("bcabbcc", mTextView.getText().toString());
- Selection.selectAll((Spannable) mTextView.getText());
- copyWithMeta = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_COPY, 0,
- KeyEvent.META_CTRL_LEFT_ON);
- // Control + copy doesn't perform copy.
- mTextView.onKeyDown(KeyEvent.KEYCODE_COPY, copyWithMeta);
- Selection.setSelection((Spannable) mTextView.getText(), 0, 0);
- mTextView.onTextContextMenuItem(android.R.id.paste);
- assertEquals("bcbcabbcc", mTextView.getText().toString());
+ Selection.selectAll((Spannable) mTextView.getText());
+ copyWithMeta = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_COPY, 0,
+ KeyEvent.META_CTRL_LEFT_ON);
+ // Control + copy doesn't perform copy.
+ mTextView.onKeyDown(KeyEvent.KEYCODE_COPY, copyWithMeta);
+ Selection.setSelection((Spannable) mTextView.getText(), 0, 0);
+ mTextView.onTextContextMenuItem(android.R.id.paste);
+ assertEquals("bcbcabbcc", mTextView.getText().toString());
- Selection.selectAll((Spannable) mTextView.getText());
- copyWithMeta = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_COPY, 0,
- KeyEvent.META_SHIFT_LEFT_ON | KeyEvent.META_CTRL_LEFT_ON);
- // Control + Shift + copy doesn't perform copy.
- mTextView.onKeyDown(KeyEvent.KEYCODE_COPY, copyWithMeta);
- Selection.setSelection((Spannable) mTextView.getText(), 0, 0);
- mTextView.onTextContextMenuItem(android.R.id.paste);
- assertEquals("bcbcbcabbcc", mTextView.getText().toString());
- }
+ Selection.selectAll((Spannable) mTextView.getText());
+ copyWithMeta = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_COPY, 0,
+ KeyEvent.META_SHIFT_LEFT_ON | KeyEvent.META_CTRL_LEFT_ON);
+ // Control + Shift + copy doesn't perform copy.
+ mTextView.onKeyDown(KeyEvent.KEYCODE_COPY, copyWithMeta);
+ Selection.setSelection((Spannable) mTextView.getText(), 0, 0);
+ mTextView.onTextContextMenuItem(android.R.id.paste);
+ assertEquals("bcbcbcabbcc", mTextView.getText().toString());
});
mInstrumentation.waitForIdleSync();
}
+ @UiThreadTest
public void testCutAndPaste() {
initTextViewForTyping();
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- mTextView.setText("abcd", BufferType.EDITABLE);
- mTextView.setSelected(true);
- // Cut "bc".
- Selection.setSelection((Spannable) mTextView.getText(), 1, 3);
- mTextView.onTextContextMenuItem(android.R.id.cut);
- assertEquals("ad", mTextView.getText().toString());
+ mTextView.setText("abcd", BufferType.EDITABLE);
+ mTextView.setSelected(true);
- // Cut "ad".
- Selection.setSelection((Spannable) mTextView.getText(), 0, 2);
- mTextView.onTextContextMenuItem(android.R.id.cut);
- assertEquals("", mTextView.getText().toString());
+ // Cut "bc".
+ Selection.setSelection((Spannable) mTextView.getText(), 1, 3);
+ mTextView.onTextContextMenuItem(android.R.id.cut);
+ assertEquals("ad", mTextView.getText().toString());
- // Paste "ad".
- mTextView.onTextContextMenuItem(android.R.id.paste);
- assertEquals("ad", mTextView.getText().toString());
- }
- });
- mInstrumentation.waitForIdleSync();
+ // Cut "ad".
+ Selection.setSelection((Spannable) mTextView.getText(), 0, 2);
+ mTextView.onTextContextMenuItem(android.R.id.cut);
+ assertEquals("", mTextView.getText().toString());
+
+ // Paste "ad".
+ mTextView.onTextContextMenuItem(android.R.id.paste);
+ assertEquals("ad", mTextView.getText().toString());
}
public void testCutAndPaste_byKey() {
- initTextViewForTyping();
+ initTextViewForTypingOnUiThread();
// Type "abc".
- mInstrumentation.sendStringSync("abc");
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- // Select "bc"
- Selection.setSelection((Spannable) mTextView.getText(), 1, 3);
- }
+ CtsKeyEventUtil.sendString(mInstrumentation, mTextView, "abc");
+ mActivity.runOnUiThread(() -> {
+ // Select "bc"
+ Selection.setSelection((Spannable) mTextView.getText(), 1, 3);
});
mInstrumentation.waitForIdleSync();
// Cut "bc"
- sendKeys(KeyEvent.KEYCODE_CUT);
+ CtsKeyEventUtil.sendKeys(mInstrumentation, mTextView, KeyEvent.KEYCODE_CUT);
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- assertEquals("a", mTextView.getText().toString());
- // Move cursor to the head
- Selection.setSelection((Spannable) mTextView.getText(), 0, 0);
- }
+ mActivity.runOnUiThread(() -> {
+ assertEquals("a", mTextView.getText().toString());
+ // Move cursor to the head
+ Selection.setSelection((Spannable) mTextView.getText(), 0, 0);
});
mInstrumentation.waitForIdleSync();
// Paste "bc"
- sendKeys(KeyEvent.KEYCODE_PASTE);
+ CtsKeyEventUtil.sendKeys(mInstrumentation, mTextView, KeyEvent.KEYCODE_PASTE);
assertEquals("bca", mTextView.getText().toString());
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- Selection.selectAll((Spannable) mTextView.getText());
- KeyEvent cutWithMeta = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN,
- KeyEvent.KEYCODE_CUT, 0, KeyEvent.META_SHIFT_LEFT_ON);
- // Shift + cut doesn't perform cut.
- mTextView.onKeyDown(KeyEvent.KEYCODE_CUT, cutWithMeta);
- assertEquals("bca", mTextView.getText().toString());
+ mInstrumentation.runOnMainSync(() -> {
+ Selection.selectAll((Spannable) mTextView.getText());
+ KeyEvent cutWithMeta = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN,
+ KeyEvent.KEYCODE_CUT, 0, KeyEvent.META_SHIFT_LEFT_ON);
+ // Shift + cut doesn't perform cut.
+ mTextView.onKeyDown(KeyEvent.KEYCODE_CUT, cutWithMeta);
+ assertEquals("bca", mTextView.getText().toString());
- cutWithMeta = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_CUT, 0,
- KeyEvent.META_CTRL_LEFT_ON);
- // Control + cut doesn't perform cut.
- mTextView.onKeyDown(KeyEvent.KEYCODE_CUT, cutWithMeta);
- assertEquals("bca", mTextView.getText().toString());
+ cutWithMeta = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_CUT, 0,
+ KeyEvent.META_CTRL_LEFT_ON);
+ // Control + cut doesn't perform cut.
+ mTextView.onKeyDown(KeyEvent.KEYCODE_CUT, cutWithMeta);
+ assertEquals("bca", mTextView.getText().toString());
- cutWithMeta = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_CUT, 0,
- KeyEvent.META_SHIFT_LEFT_ON | KeyEvent.META_CTRL_LEFT_ON);
- // Control + Shift + cut doesn't perform cut.
- mTextView.onKeyDown(KeyEvent.KEYCODE_CUT, cutWithMeta);
- assertEquals("bca", mTextView.getText().toString());
- }
+ cutWithMeta = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_CUT, 0,
+ KeyEvent.META_SHIFT_LEFT_ON | KeyEvent.META_CTRL_LEFT_ON);
+ // Control + Shift + cut doesn't perform cut.
+ mTextView.onKeyDown(KeyEvent.KEYCODE_CUT, cutWithMeta);
+ assertEquals("bca", mTextView.getText().toString());
});
+ mInstrumentation.waitForIdleSync();
}
private static boolean hasSpansAtMiddleOfText(final TextView textView, final Class<?> type) {
@@ -1975,38 +2085,35 @@
return spannable.getSpans(at, at, type).length > 0;
}
+ @UiThreadTest
public void testCutAndPaste_withAndWithoutStyle() {
initTextViewForTyping();
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- mTextView.setText("example", BufferType.EDITABLE);
- mTextView.setSelected(true);
- // Set URLSpan.
- final Spannable spannable = (Spannable) mTextView.getText();
- spannable.setSpan(new URLSpan("http://example.com"), 0, spannable.length(), 0);
- assertTrue(hasSpansAtMiddleOfText(mTextView, URLSpan.class));
+ mTextView.setText("example", BufferType.EDITABLE);
+ mTextView.setSelected(true);
- // Cut entire text.
- Selection.selectAll((Spannable) mTextView.getText());
- mTextView.onTextContextMenuItem(android.R.id.cut);
- assertEquals("", mTextView.getText().toString());
+ // Set URLSpan.
+ final Spannable spannable = (Spannable) mTextView.getText();
+ spannable.setSpan(new URLSpan("http://example.com"), 0, spannable.length(), 0);
+ assertTrue(hasSpansAtMiddleOfText(mTextView, URLSpan.class));
- // Paste without style.
- mTextView.onTextContextMenuItem(android.R.id.pasteAsPlainText);
- assertEquals("example", mTextView.getText().toString());
- // Check that the text doesn't have URLSpan.
- assertFalse(hasSpansAtMiddleOfText(mTextView, URLSpan.class));
+ // Cut entire text.
+ Selection.selectAll((Spannable) mTextView.getText());
+ mTextView.onTextContextMenuItem(android.R.id.cut);
+ assertEquals("", mTextView.getText().toString());
- // Paste with style.
- Selection.selectAll((Spannable) mTextView.getText());
- mTextView.onTextContextMenuItem(android.R.id.paste);
- assertEquals("example", mTextView.getText().toString());
- // Check that the text has URLSpan.
- assertTrue(hasSpansAtMiddleOfText(mTextView, URLSpan.class));
- }
- });
- mInstrumentation.waitForIdleSync();
+ // Paste without style.
+ mTextView.onTextContextMenuItem(android.R.id.pasteAsPlainText);
+ assertEquals("example", mTextView.getText().toString());
+ // Check that the text doesn't have URLSpan.
+ assertFalse(hasSpansAtMiddleOfText(mTextView, URLSpan.class));
+
+ // Paste with style.
+ Selection.selectAll((Spannable) mTextView.getText());
+ mTextView.onTextContextMenuItem(android.R.id.paste);
+ assertEquals("example", mTextView.getText().toString());
+ // Check that the text has URLSpan.
+ assertTrue(hasSpansAtMiddleOfText(mTextView, URLSpan.class));
}
@UiThreadTest
@@ -2185,61 +2292,45 @@
final String errorText = "Oops! There is an error";
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- mTextView.setError(null);
- }
- });
+ mActivity.runOnUiThread(() -> mTextView.setError(null));
mInstrumentation.waitForIdleSync();
assertNull(mTextView.getError());
- final Drawable icon = getDrawable(R.drawable.failed);
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- mTextView.setError(errorText, icon);
- }
- });
+ final Drawable icon = TestUtils.getDrawable(mActivity, R.drawable.failed);
+ mActivity.runOnUiThread(() -> mTextView.setError(errorText, icon));
mInstrumentation.waitForIdleSync();
assertEquals(errorText, mTextView.getError().toString());
// can not check whether the drawable is set correctly
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- mTextView.setError(null, null);
- }
- });
+ mActivity.runOnUiThread(() -> mTextView.setError(null, null));
mInstrumentation.waitForIdleSync();
assertNull(mTextView.getError());
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- mTextView.setKeyListener(DigitsKeyListener.getInstance(""));
- mTextView.setText("", BufferType.EDITABLE);
- mTextView.setError(errorText);
- mTextView.requestFocus();
- }
+ mActivity.runOnUiThread(() -> {
+ mTextView.setKeyListener(DigitsKeyListener.getInstance(""));
+ mTextView.setText("", BufferType.EDITABLE);
+ mTextView.setError(errorText);
+ mTextView.requestFocus();
});
mInstrumentation.waitForIdleSync();
assertEquals(errorText, mTextView.getError().toString());
- mInstrumentation.sendStringSync("a");
+ CtsKeyEventUtil.sendString(mInstrumentation, mTextView, "a");
// a key event that will not change the TextView's text
assertEquals("", mTextView.getText().toString());
// The icon and error message will not be reset to null
assertEquals(errorText, mTextView.getError().toString());
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- mTextView.setKeyListener(DigitsKeyListener.getInstance());
- mTextView.setText("", BufferType.EDITABLE);
- mTextView.setError(errorText);
- mTextView.requestFocus();
- }
+ mActivity.runOnUiThread(() -> {
+ mTextView.setKeyListener(DigitsKeyListener.getInstance());
+ mTextView.setText("", BufferType.EDITABLE);
+ mTextView.setError(errorText);
+ mTextView.requestFocus();
});
mInstrumentation.waitForIdleSync();
- mInstrumentation.sendStringSync("1");
+ CtsKeyEventUtil.sendString(mInstrumentation, mTextView, "1");
// a key event cause changes to the TextView's text
assertEquals("1", mTextView.getText().toString());
// the error message and icon will be cleared.
@@ -2252,26 +2343,24 @@
final QwertyKeyListener qwertyKeyListener
= QwertyKeyListener.getInstance(false, Capitalize.NONE);
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- mTextView = findTextView(R.id.textview_text);
- mTextView.setKeyListener(qwertyKeyListener);
- mTextView.setText("", BufferType.EDITABLE);
- mTextView.setFilters(expected);
- mTextView.requestFocus();
- }
+ mActivity.runOnUiThread(() -> {
+ mTextView = findTextView(R.id.textview_text);
+ mTextView.setKeyListener(qwertyKeyListener);
+ mTextView.setText("", BufferType.EDITABLE);
+ mTextView.setFilters(expected);
+ mTextView.requestFocus();
});
mInstrumentation.waitForIdleSync();
assertSame(expected, mTextView.getFilters());
- mInstrumentation.sendStringSync("a");
+ CtsKeyEventUtil.sendString(mInstrumentation, mTextView, "a");
// the text is capitalized by InputFilter.AllCaps
assertEquals("A", mTextView.getText().toString());
- mInstrumentation.sendStringSync("b");
+ CtsKeyEventUtil.sendString(mInstrumentation, mTextView, "b");
// the text is capitalized by InputFilter.AllCaps
assertEquals("AB", mTextView.getText().toString());
- mInstrumentation.sendStringSync("c");
+ CtsKeyEventUtil.sendString(mInstrumentation, mTextView, "c");
// 'C' could not be accepted, because there is a length filter.
assertEquals("AB", mTextView.getText().toString());
@@ -2301,13 +2390,11 @@
assertEquals(mTextView.getScrollY(), rc.top);
assertEquals(mTextView.getScrollY() + mTextView.getHeight(), rc.bottom);
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- mTextView.setSelected(true);
- SpannableString text = new SpannableString(mTextView.getText());
- Selection.setSelection(text, 3, 13);
- mTextView.setText(text);
- }
+ mActivity.runOnUiThread(() -> {
+ mTextView.setSelected(true);
+ SpannableString text = new SpannableString(mTextView.getText());
+ Selection.setSelection(text, 3, 13);
+ mTextView.setText(text);
});
mInstrumentation.waitForIdleSync();
mTextView.getFocusedRect(rc);
@@ -2322,13 +2409,11 @@
assertEquals(mTextView.getLayout().getLineTop(0), rc.top);
assertEquals(mTextView.getLayout().getLineBottom(0), rc.bottom);
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- mTextView.setSelected(true);
- SpannableString text = new SpannableString(mTextView.getText());
- Selection.setSelection(text, 13, 3);
- mTextView.setText(text);
- }
+ mActivity.runOnUiThread(() -> {
+ mTextView.setSelected(true);
+ SpannableString text = new SpannableString(mTextView.getText());
+ Selection.setSelection(text, 13, 3);
+ mTextView.setText(text);
});
mInstrumentation.waitForIdleSync();
mTextView.getFocusedRect(rc);
@@ -2346,13 +2431,11 @@
assertEquals(mTextView.getScrollY(), rc.top);
assertEquals(mTextView.getScrollY() + mTextView.getHeight(), rc.bottom);
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- mTextView.setSelected(true);
- SpannableString text = new SpannableString(mTextView.getText());
- Selection.setSelection(text, 2, 4);
- mTextView.setText(text);
- }
+ mActivity.runOnUiThread(() -> {
+ mTextView.setSelected(true);
+ SpannableString text = new SpannableString(mTextView.getText());
+ Selection.setSelection(text, 2, 4);
+ mTextView.setText(text);
});
mInstrumentation.waitForIdleSync();
mTextView.getFocusedRect(rc);
@@ -2362,13 +2445,11 @@
assertEquals(mTextView.getLayout().getLineTop(0), rc.top);
assertEquals(mTextView.getLayout().getLineBottom(0), rc.bottom);
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- mTextView.setSelected(true);
- SpannableString text = new SpannableString(mTextView.getText());
- Selection.setSelection(text, 2, 10); // cross the "\n" and two lines
- mTextView.setText(text);
- }
+ mActivity.runOnUiThread(() -> {
+ mTextView.setSelected(true);
+ SpannableString text = new SpannableString(mTextView.getText());
+ Selection.setSelection(text, 2, 10); // cross the "\n" and two lines
+ mTextView.setText(text);
});
mInstrumentation.waitForIdleSync();
mTextView.getFocusedRect(rc);
@@ -2433,11 +2514,9 @@
assertEquals(0, rc.top);
assertEquals(mTextView.getHeight(), rc.bottom);
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- mTextView.setPadding(1, 2, 3, 4);
- mTextView.setGravity(Gravity.BOTTOM);
- }
+ mActivity.runOnUiThread(() -> {
+ mTextView.setPadding(1, 2, 3, 4);
+ mTextView.setGravity(Gravity.BOTTOM);
});
mInstrumentation.waitForIdleSync();
assertEquals(mTextView.getBaseline(), mTextView.getLineBounds(0, rc));
@@ -2454,11 +2533,9 @@
mTextView = findTextView(R.id.textview_text);
assertEquals(mTextView.getLayout().getLineBaseline(0), mTextView.getBaseline());
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- mTextView.setPadding(1, 2, 3, 4);
- mTextView.setGravity(Gravity.BOTTOM);
- }
+ mActivity.runOnUiThread(() -> {
+ mTextView.setPadding(1, 2, 3, 4);
+ mTextView.setGravity(Gravity.BOTTOM);
});
mInstrumentation.waitForIdleSync();
int expected = mTextView.getTotalPaddingTop() + mTextView.getLayout().getLineBaseline(0);
@@ -2466,34 +2543,28 @@
}
public void testPressKey() {
- initTextViewForTyping();
+ initTextViewForTypingOnUiThread();
- mInstrumentation.sendStringSync("a");
+ CtsKeyEventUtil.sendString(mInstrumentation, mTextView, "a");
assertEquals("a", mTextView.getText().toString());
- mInstrumentation.sendStringSync("b");
+ CtsKeyEventUtil.sendString(mInstrumentation, mTextView, "b");
assertEquals("ab", mTextView.getText().toString());
- sendKeys(KeyEvent.KEYCODE_DEL);
+ CtsKeyEventUtil.sendKeys(mInstrumentation, mTextView, KeyEvent.KEYCODE_DEL);
assertEquals("a", mTextView.getText().toString());
}
public void testSetIncludeFontPadding() {
mTextView = findTextView(R.id.textview_text);
assertTrue(mTextView.getIncludeFontPadding());
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- mTextView.setWidth(mTextView.getWidth() / 3);
- mTextView.setPadding(1, 2, 3, 4);
- mTextView.setGravity(Gravity.BOTTOM);
- }
+ mActivity.runOnUiThread(() -> {
+ mTextView.setWidth(mTextView.getWidth() / 3);
+ mTextView.setPadding(1, 2, 3, 4);
+ mTextView.setGravity(Gravity.BOTTOM);
});
mInstrumentation.waitForIdleSync();
int oldHeight = mTextView.getHeight();
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- mTextView.setIncludeFontPadding(false);
- }
- });
+ mActivity.runOnUiThread(() -> mTextView.setIncludeFontPadding(false));
mInstrumentation.waitForIdleSync();
assertTrue(mTextView.getHeight() < oldHeight);
@@ -2557,6 +2628,31 @@
assertTrue(mTextView.hasSelection());
}
+ @MediumTest
+ public void testOnSelectionChanged_isTriggeredWhenSelectionChanges() {
+ final MockTextView textView = spy(new MockTextView(mActivity));
+ final String text = "any text";
+ textView.setText(text, BufferType.SPANNABLE);
+
+ // assert that there is currently no selection
+ assertFalse(textView.hasSelection());
+
+ // select all
+ Selection.selectAll((Spannable) textView.getText());
+ // After selectAll OnSelectionChanged should have been called
+ verify(textView, times(1)).onSelectionChanged(0, text.length());
+
+ reset(textView);
+ // change selection
+ Selection.setSelection((Spannable) textView.getText(), 1, 5);
+ verify(textView, times(1)).onSelectionChanged(1, 5);
+
+ reset(textView);
+ // clear selection
+ Selection.removeSelection((Spannable) textView.getText());
+ verify(textView, times(1)).onSelectionChanged(-1, -1);
+ }
+
@UiThreadTest
public void testAccessEllipsize() {
mActivity.setContentView(R.layout.textview_ellipsize);
@@ -2598,18 +2694,18 @@
}
public void testEllipsizeEndAndNoEllipsizeHasSameBaselineForSingleLine() {
- int textWidth = calculateTextWidth(LONG_TEXT);
-
TextView tvEllipsizeEnd = new TextView(getActivity());
tvEllipsizeEnd.setEllipsize(TruncateAt.END);
tvEllipsizeEnd.setMaxLines(1);
- tvEllipsizeEnd.setWidth(textWidth >> 2);
tvEllipsizeEnd.setText(LONG_TEXT);
TextView tvEllipsizeNone = new TextView(getActivity());
- tvEllipsizeNone.setWidth(textWidth >> 2);
tvEllipsizeNone.setText("a");
+ final int textWidth = (int) tvEllipsizeEnd.getPaint().measureText(LONG_TEXT) / 4;
+ tvEllipsizeEnd.setWidth(textWidth);
+ tvEllipsizeNone.setWidth(textWidth);
+
final FrameLayout layout = new FrameLayout(mActivity);
ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
@@ -2618,12 +2714,7 @@
layout.addView(tvEllipsizeNone, layoutParams);
layout.setLayoutParams(layoutParams);
- mActivity.runOnUiThread(new Runnable() {
- @Override
- public void run() {
- getActivity().setContentView(layout);
- }
- });
+ mActivity.runOnUiThread(() -> getActivity().setContentView(layout));
getInstrumentation().waitForIdleSync();
assertEquals("Ellipsized and non ellipsized single line texts should have the same " +
@@ -2633,19 +2724,19 @@
}
public void testEllipsizeEndAndNoEllipsizeHasSameBaselineForMultiLine() {
- int textWidth = calculateTextWidth(LONG_TEXT);
-
TextView tvEllipsizeEnd = new TextView(getActivity());
tvEllipsizeEnd.setEllipsize(TruncateAt.END);
tvEllipsizeEnd.setMaxLines(2);
- tvEllipsizeEnd.setWidth(textWidth >> 2);
tvEllipsizeEnd.setText(LONG_TEXT);
TextView tvEllipsizeNone = new TextView(getActivity());
tvEllipsizeNone.setMaxLines(2);
- tvEllipsizeNone.setWidth(textWidth >> 2);
tvEllipsizeNone.setText(LONG_TEXT);
+ final int textWidth = (int) tvEllipsizeEnd.getPaint().measureText(LONG_TEXT) / 2;
+ tvEllipsizeEnd.setWidth(textWidth);
+ tvEllipsizeNone.setWidth(textWidth);
+
final FrameLayout layout = new FrameLayout(mActivity);
ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
@@ -2655,12 +2746,7 @@
layout.addView(tvEllipsizeNone, layoutParams);
layout.setLayoutParams(layoutParams);
- mActivity.runOnUiThread(new Runnable() {
- @Override
- public void run() {
- getActivity().setContentView(layout);
- }
- });
+ mActivity.runOnUiThread(() -> getActivity().setContentView(layout));
getInstrumentation().waitForIdleSync();
for (int i = 0; i < tvEllipsizeEnd.getLineCount(); i++) {
@@ -2671,11 +2757,51 @@
}
}
- public void testSetCursorVisible() {
+ public void testTextViewInWeigthenedLayoutChangesWidthAfterSetText() {
+ final TextView textView = new TextView(getActivity());
+ textView.setEllipsize(TruncateAt.END);
+ textView.setSingleLine(true);
+ textView.setText("a");
+
+ TextView otherTextView = new TextView(getActivity());
+ otherTextView.setSingleLine(true);
+ otherTextView.setText("any");
+
+ final LinearLayout layout = new LinearLayout(mActivity);
+ layout.setOrientation(LinearLayout.HORIZONTAL);
+
+ // TextView under test has weight 1, and width 0
+ layout.addView(textView, new LinearLayout.LayoutParams(0,
+ ViewGroup.LayoutParams.WRAP_CONTENT, 1.0f));
+
+ // other TextView has default weight
+ layout.addView(otherTextView, new LinearLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT));
+
+ // main layout params
+ layout.setLayoutParams(new ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
+
+ mActivity.runOnUiThread(() -> getActivity().setContentView(layout));
+ getInstrumentation().waitForIdleSync();
+
+ int oldWidth = textView.getWidth();
+
+ mActivity.runOnUiThread(() -> textView.setText("aaa"));
+ getInstrumentation().waitForIdleSync();
+
+ assertTrue("TextView should have larger width after a longer text is set",
+ textView.getWidth() > oldWidth);
+ }
+
+ public void testAccessCursorVisible() {
mTextView = new TextView(mActivity);
mTextView.setCursorVisible(true);
+ assertTrue(mTextView.isCursorVisible());
mTextView.setCursorVisible(false);
+ assertFalse(mTextView.isCursorVisible());
}
public void testOnWindowFocusChanged() {
@@ -2702,27 +2828,39 @@
public void testPerformLongClick() {
mTextView = findTextView(R.id.textview_text);
mTextView.setText("This is content");
- MockOnLongClickListener onLongClickListener = new MockOnLongClickListener(true);
- MockOnCreateContextMenuListener onCreateContextMenuListener
- = new MockOnCreateContextMenuListener(false);
- mTextView.setOnLongClickListener(onLongClickListener);
- mTextView.setOnCreateContextMenuListener(onCreateContextMenuListener);
- assertTrue(mTextView.performLongClick());
- assertTrue(onLongClickListener.hasLongClicked());
- assertFalse(onCreateContextMenuListener.hasCreatedContextMenu());
- onLongClickListener = new MockOnLongClickListener(false);
- mTextView.setOnLongClickListener(onLongClickListener);
- mTextView.setOnCreateContextMenuListener(onCreateContextMenuListener);
- assertTrue(mTextView.performLongClick());
- assertTrue(onLongClickListener.hasLongClicked());
- assertTrue(onCreateContextMenuListener.hasCreatedContextMenu());
+ View.OnLongClickListener mockOnLongClickListener = mock(View.OnLongClickListener.class);
+ when(mockOnLongClickListener.onLongClick(any(View.class))).thenReturn(Boolean.TRUE);
+ View.OnCreateContextMenuListener mockOnCreateContextMenuListener =
+ mock(View.OnCreateContextMenuListener.class);
+ doAnswer((InvocationOnMock invocation) -> {
+ ((ContextMenu) invocation.getArguments() [0]).add("menu item");
+ return null;
+ }).when(mockOnCreateContextMenuListener).onCreateContextMenu(
+ any(ContextMenu.class), any(View.class), any(ContextMenuInfo.class));
+
+ mTextView.setOnLongClickListener(mockOnLongClickListener);
+ mTextView.setOnCreateContextMenuListener(mockOnCreateContextMenuListener);
+ assertTrue(mTextView.performLongClick());
+ verify(mockOnLongClickListener, times(1)).onLongClick(mTextView);
+ verifyZeroInteractions(mockOnCreateContextMenuListener);
+
+ reset(mockOnLongClickListener);
+ when(mockOnLongClickListener.onLongClick(any(View.class))).thenReturn(Boolean.FALSE);
+ assertTrue(mTextView.performLongClick());
+ verify(mockOnLongClickListener, times(1)).onLongClick(mTextView);
+ verify(mockOnCreateContextMenuListener, times(1)).onCreateContextMenu(
+ any(ContextMenu.class), eq(mTextView), any(ContextMenuInfo.class));
+
+ reset(mockOnCreateContextMenuListener);
mTextView.setOnLongClickListener(null);
- onCreateContextMenuListener = new MockOnCreateContextMenuListener(true);
- mTextView.setOnCreateContextMenuListener(onCreateContextMenuListener);
+ doNothing().when(mockOnCreateContextMenuListener).onCreateContextMenu(
+ any(ContextMenu.class), any(View.class), any(ContextMenuInfo.class));
assertFalse(mTextView.performLongClick());
- assertTrue(onCreateContextMenuListener.hasCreatedContextMenu());
+ verifyNoMoreInteractions(mockOnLongClickListener);
+ verify(mockOnCreateContextMenuListener, times(1)).onCreateContextMenu(
+ any(ContextMenu.class), eq(mTextView), any(ContextMenuInfo.class));
}
@UiThreadTest
@@ -2952,6 +3090,259 @@
urlSpans[0].getURL(), "http://android.com/textview");
}
+ @MediumTest
+ public void testGetLetterSpacing_returnsValueThatWasSet() {
+ mTextView = new TextView(mActivity);
+ mTextView.setLetterSpacing(2f);
+ assertEquals("getLetterSpacing should return the value that was set",
+ 2f, mTextView.getLetterSpacing());
+ }
+
+ @MediumTest
+ public void testSetLetterSpacing_changesTextWidth() {
+ final TextView textView = new TextView(mActivity);
+ textView.setText("aa");
+ textView.setLetterSpacing(0f);
+ textView.setTextSize(8f);
+
+ final FrameLayout layout = new FrameLayout(mActivity);
+ final ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.WRAP_CONTENT,
+ ViewGroup.LayoutParams.MATCH_PARENT);
+ layout.addView(textView, layoutParams);
+ layout.setLayoutParams(layoutParams);
+
+ mActivity.runOnUiThread(() -> getActivity().setContentView(layout));
+ getInstrumentation().waitForIdleSync();
+
+ // measure text with zero letter spacing
+ final float zeroSpacing = textView.getLayout().getLineWidth(0);
+
+ getActivity().runOnUiThread(() -> textView.setLetterSpacing(1f));
+ getInstrumentation().waitForIdleSync();
+
+ // measure text with single letter spacing
+ final float singleSpacing = textView.getLayout().getLineWidth(0);
+
+ getActivity().runOnUiThread(() -> textView.setLetterSpacing(2f));
+ getInstrumentation().waitForIdleSync();
+
+ // measure text with double letter spacing
+ final float doubleSpacing = textView.getLayout().getLineWidth(0);
+
+ assertEquals("Double spacing should have two times the spacing of single spacing",
+ doubleSpacing - zeroSpacing, 2f * (singleSpacing - zeroSpacing), 1f);
+ }
+
+ @MediumTest
+ public void testGetFontFeatureSettings_returnsValueThatWasSet() {
+ mTextView = new TextView(mActivity);
+ mTextView.setFontFeatureSettings("\"smcp\" on");
+ assertEquals("getFontFeatureSettings should return the value that was set",
+ "\"smcp\" on", mTextView.getFontFeatureSettings());
+ }
+
+ @MediumTest
+ public void testGetOffsetForPosition_singleLineLtr() {
+ // asserts getOffsetPosition returns correct values for a single line LTR text
+ String text = "aaaaa";
+ final TextView textView = new TextView(mActivity);
+ textView.setText(text);
+ textView.setTextSize(8f);
+ textView.setSingleLine(true);
+
+ // add a compound drawable to TextView to make offset calculation more interesting
+ final Drawable drawable = TestUtils.getDrawable(mActivity, R.drawable.red);
+ drawable.setBounds(0, 0, 10, 10);
+ textView.setCompoundDrawables(drawable, drawable, drawable, drawable);
+
+ final FrameLayout layout = new FrameLayout(mActivity);
+ final ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT);
+ layout.addView(textView, layoutParams);
+ layout.setLayoutParams(layoutParams);
+
+ mInstrumentation.runOnMainSync(() -> getActivity().setContentView(layout));
+ mInstrumentation.waitForIdleSync();
+
+ final int firstOffset = 0;
+ final int lastOffset = text.length() - 1;
+ final int midOffset = text.length() / 2;
+
+ // left edge of view
+ float x = 0f;
+ float y = textView.getHeight() / 2f;
+ assertEquals(firstOffset, textView.getOffsetForPosition(x, y));
+
+ // right edge of text
+ x = textView.getLayout().getLineWidth(0) - 1f;
+ assertEquals(lastOffset, textView.getOffsetForPosition(x, y));
+
+ // right edge of view
+ x = textView.getWidth();
+ assertEquals(lastOffset + 1, textView.getOffsetForPosition(x, y));
+
+ // left edge of view - out of bounds
+ x = -1f;
+ assertEquals(firstOffset, textView.getOffsetForPosition(x, y));
+
+ // horizontal center of text
+ x = (float) Math.floor(textView.getLayout().getLineWidth(0) / 2f + 0.5f);
+ assertEquals(midOffset, textView.getOffsetForPosition(x, y));
+ }
+
+ @MediumTest
+ public void testGetOffsetForPosition_multiLineLtr() {
+ final String line = "aaa\n";
+ final String threeLines = line + line + line;
+ final TextView textView = new TextView(mActivity);
+ textView.setText(threeLines);
+ textView.setTextSize(8f);
+ textView.setLines(2);
+
+ // add a compound drawable to TextView to make offset calculation more interesting
+ final Drawable drawable = TestUtils.getDrawable(mActivity, R.drawable.red);
+ drawable.setBounds(0, 0, 10, 10);
+ textView.setCompoundDrawables(drawable, drawable, drawable, drawable);
+
+ final FrameLayout layout = new FrameLayout(mActivity);
+ final ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT);
+ layout.addView(textView, layoutParams);
+ layout.setLayoutParams(layoutParams);
+
+ mInstrumentation.runOnMainSync(() -> getActivity().setContentView(layout));
+ mInstrumentation.waitForIdleSync();
+
+ final Rect lineBounds = new Rect();
+ textView.getLayout().getLineBounds(0, lineBounds);
+
+ // left edge of view at first line
+ float x = 0f;
+ float y = lineBounds.height() / 2f;
+ assertEquals(0, textView.getOffsetForPosition(x, y));
+
+ // right edge of view at first line
+ x = textView.getWidth() - 1f;
+ assertEquals(line.length() - 1, textView.getOffsetForPosition(x, y));
+
+ // update lineBounds to be the second line
+ textView.getLayout().getLineBounds(1, lineBounds);
+ y = lineBounds.top + lineBounds.height() / 2;
+
+ // left edge of view at second line
+ x = 0f;
+ assertEquals(line.length(), textView.getOffsetForPosition(x, y));
+
+ // right edge of text at second line
+ x = textView.getLayout().getLineWidth(1) - 1f;
+ assertEquals(line.length() + line.length() - 1, textView.getOffsetForPosition(x, y));
+
+ // right edge of view at second line
+ x = textView.getWidth() - 1f;
+ assertEquals(line.length() + line.length() - 1, textView.getOffsetForPosition(x, y));
+
+ // horizontal center of text at second line
+ x = (float) Math.floor(textView.getLayout().getLineWidth(1) / 2f + 0.5f);
+ // second line mid offset should not include next line, therefore subtract one
+ assertEquals(line.length() + (line.length() - 1) / 2, textView.getOffsetForPosition(x, y));
+ }
+
+ @MediumTest
+ public void testGetOffsetForPosition_multiLineRtl() {
+ final String line = "\u0635\u0635\u0635\n";
+ final String threeLines = line + line + line;
+ final TextView textView = new TextView(mActivity);
+ textView.setText(threeLines);
+ textView.setTextSize(8f);
+ textView.setLines(2);
+
+ // add a compound drawable to TextView to make offset calculation more interesting
+ final Drawable drawable = TestUtils.getDrawable(mActivity, R.drawable.red);
+ drawable.setBounds(0, 0, 10, 10);
+ textView.setCompoundDrawables(drawable, drawable, drawable, drawable);
+
+ final FrameLayout layout = new FrameLayout(mActivity);
+ final ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT);
+ layout.addView(textView, layoutParams);
+ layout.setLayoutParams(layoutParams);
+
+ mInstrumentation.runOnMainSync(() -> getActivity().setContentView(layout));
+ mInstrumentation.waitForIdleSync();
+
+ final Rect lineBounds = new Rect();
+ textView.getLayout().getLineBounds(0, lineBounds);
+
+ // right edge of view at first line
+ float x = textView.getWidth() - 1f;
+ float y = lineBounds.height() / 2f;
+ assertEquals(0, textView.getOffsetForPosition(x, y));
+
+ // left edge of view at first line
+ x = 0f;
+ assertEquals(line.length() - 1, textView.getOffsetForPosition(x, y));
+
+ // update lineBounds to be the second line
+ textView.getLayout().getLineBounds(1, lineBounds);
+ y = lineBounds.top + lineBounds.height() / 2f;
+
+ // right edge of view at second line
+ x = textView.getWidth() - 1f;
+ assertEquals(line.length(), textView.getOffsetForPosition(x, y));
+
+ // left edge of view at second line
+ x = 0f;
+ assertEquals(line.length() + line.length() - 1, textView.getOffsetForPosition(x, y));
+
+ // right edge of text at second line
+ x = textView.getWidth() - textView.getLayout().getLineWidth(1) + 1f;
+ assertEquals(line.length() + line.length() - 1, textView.getOffsetForPosition(x, y));
+
+ // horizontal center of text at second line
+ x = textView.getWidth() - (float) Math.floor(
+ textView.getLayout().getLineWidth(1) / 2f + 0.5f);
+ // second line mid offset should not include next line, therefore subtract one
+ assertEquals(line.length() + (line.length() - 1) / 2, textView.getOffsetForPosition(x, y));
+ }
+
+ @MediumTest
+ public void testIsTextSelectable_returnsFalseByDefault() {
+ final TextView textView = new TextView(getActivity());
+ textView.setText("any text");
+ assertFalse(textView.isTextSelectable());
+ }
+
+ @MediumTest
+ public void testIsTextSelectable_returnsTrueIfSetTextIsSelectableCalledWithTrue() {
+ final TextView textView = new TextView(getActivity());
+ textView.setText("any text");
+ textView.setTextIsSelectable(true);
+ assertTrue(textView.isTextSelectable());
+ }
+
+ @MediumTest
+ public void testSetIsTextSelectable() {
+ final TextView textView = new TextView(getActivity());
+
+ assertFalse(textView.isTextSelectable());
+ assertFalse(textView.isFocusable());
+ assertFalse(textView.isFocusableInTouchMode());
+ assertFalse(textView.isClickable());
+ assertFalse(textView.isLongClickable());
+
+ textView.setTextIsSelectable(true);
+
+ assertTrue(textView.isTextSelectable());
+ assertTrue(textView.isFocusable());
+ assertTrue(textView.isFocusableInTouchMode());
+ assertTrue(textView.isClickable());
+ assertTrue(textView.isLongClickable());
+ assertNotNull(textView.getMovementMethod());
+ }
public void testAccessTransformationMethod() {
// check the password attribute in xml
@@ -2970,43 +3361,28 @@
Capitalize.NONE);
final TransformationMethod method = PasswordTransformationMethod.getInstance();
// change transformation method by function
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- mTextView.setKeyListener(qwertyKeyListener);
- mTextView.setTransformationMethod(method);
- mTransformedText = method.getTransformation(mTextView.getText(), mTextView);
+ mActivity.runOnUiThread(() -> {
+ mTextView.setKeyListener(qwertyKeyListener);
+ mTextView.setTransformationMethod(method);
+ mTransformedText = method.getTransformation(mTextView.getText(), mTextView);
- mTextView.requestFocus();
- }
+ mTextView.requestFocus();
});
mInstrumentation.waitForIdleSync();
assertSame(PasswordTransformationMethod.getInstance(),
mTextView.getTransformationMethod());
- sendKeys("H E 2*L O");
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- mTextView.append(" ");
- }
- });
+ CtsKeyEventUtil.sendKeys(mInstrumentation, mTextView, "H E 2*L O");
+ mActivity.runOnUiThread(() -> mTextView.append(" "));
mInstrumentation.waitForIdleSync();
- // it will get transformed after a while
- new PollingCheck(TIMEOUT) {
- @Override
- protected boolean check() {
- // "******"
- return mTransformedText.toString()
- .equals("\u2022\u2022\u2022\u2022\u2022\u2022");
- }
- }.run();
+ // It will get transformed after a while
+ // We're waiting for transformation to "******"
+ PollingCheck.waitFor(TIMEOUT, () -> mTransformedText.toString()
+ .equals("\u2022\u2022\u2022\u2022\u2022\u2022"));
// set null
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- mTextView.setTransformationMethod(null);
- }
- });
+ mActivity.runOnUiThread(() -> mTextView.setTransformationMethod(null));
mInstrumentation.waitForIdleSync();
assertNull(mTextView.getTransformationMethod());
}
@@ -3031,20 +3407,20 @@
drawables = mTextView.getCompoundDrawables();
// drawableLeft
- WidgetTestUtils.assertEquals(getBitmap(R.drawable.start),
+ WidgetTestUtils.assertEquals(TestUtils.getBitmap(mActivity, R.drawable.start),
((BitmapDrawable) drawables[0]).getBitmap());
// drawableTop
- WidgetTestUtils.assertEquals(getBitmap(R.drawable.pass),
+ WidgetTestUtils.assertEquals(TestUtils.getBitmap(mActivity, R.drawable.pass),
((BitmapDrawable) drawables[1]).getBitmap());
// drawableRight
- WidgetTestUtils.assertEquals(getBitmap(R.drawable.failed),
+ WidgetTestUtils.assertEquals(TestUtils.getBitmap(mActivity, R.drawable.failed),
((BitmapDrawable) drawables[2]).getBitmap());
// drawableBottom
assertNull(drawables[3]);
- Drawable left = getDrawable(R.drawable.blue);
- Drawable right = getDrawable(R.drawable.yellow);
- Drawable top = getDrawable(R.drawable.red);
+ Drawable left = TestUtils.getDrawable(mActivity, R.drawable.blue);
+ Drawable right = TestUtils.getDrawable(mActivity, R.drawable.yellow);
+ Drawable top = TestUtils.getDrawable(mActivity, R.drawable.red);
// using drawables directly
mTextView.setCompoundDrawablesWithIntrinsicBounds(left, top, right, null);
@@ -3095,24 +3471,80 @@
assertEquals(mTextView.getPaddingBottom(), mTextView.getCompoundPaddingBottom());
}
+ @MediumTest
+ @UiThreadTest
+ public void testGetCompoundDrawablesRelative() {
+ // prepare textview
+ mTextView = new TextView(mActivity);
+
+ // prepare drawables
+ final Drawable start = TestUtils.getDrawable(mActivity, R.drawable.blue);
+ final Drawable end = TestUtils.getDrawable(mActivity, R.drawable.yellow);
+ final Drawable top = TestUtils.getDrawable(mActivity, R.drawable.red);
+ final Drawable bottom = TestUtils.getDrawable(mActivity, R.drawable.black);
+ assertNotNull(start);
+ assertNotNull(end);
+ assertNotNull(top);
+ assertNotNull(bottom);
+
+ Drawable[] drawables = mTextView.getCompoundDrawablesRelative();
+ assertNotNull(drawables);
+ assertEquals(4, drawables.length);
+ assertNull(drawables[0]);
+ assertNull(drawables[1]);
+ assertNull(drawables[2]);
+ assertNull(drawables[3]);
+
+ mTextView.setLayoutDirection(View.LAYOUT_DIRECTION_LTR);
+ mTextView.setCompoundDrawablesRelative(start, top, end, bottom);
+ drawables = mTextView.getCompoundDrawablesRelative();
+
+ assertNotNull(drawables);
+ assertEquals(4, drawables.length);
+ assertSame(start, drawables[0]);
+ assertSame(top, drawables[1]);
+ assertSame(end, drawables[2]);
+ assertSame(bottom, drawables[3]);
+
+ mTextView.setLayoutDirection(View.LAYOUT_DIRECTION_RTL);
+ mTextView.setCompoundDrawablesRelative(start, top, end, bottom);
+ drawables = mTextView.getCompoundDrawablesRelative();
+
+ assertNotNull(drawables);
+ assertEquals(4, drawables.length);
+ assertSame(start, drawables[0]);
+ assertSame(top, drawables[1]);
+ assertSame(end, drawables[2]);
+ assertSame(bottom, drawables[3]);
+
+ mTextView.setCompoundDrawablesRelative(null, null, null, null);
+ drawables = mTextView.getCompoundDrawablesRelative();
+
+ assertNotNull(drawables);
+ assertEquals(4, drawables.length);
+ assertNull(drawables[0]);
+ assertNull(drawables[1]);
+ assertNull(drawables[2]);
+ assertNull(drawables[3]);
+ }
+
+ @MediumTest
public void testSingleLine() {
final TextView textView = new TextView(mActivity);
setSpannableText(textView, "This is a really long sentence"
+ " which can not be placed in one line on the screen.");
// Narrow layout assures that the text will get wrapped.
- FrameLayout innerLayout = new FrameLayout(mActivity);
+ final FrameLayout innerLayout = new FrameLayout(mActivity);
innerLayout.setLayoutParams(new ViewGroup.LayoutParams(100, 100));
innerLayout.addView(textView);
final FrameLayout layout = new FrameLayout(mActivity);
layout.addView(innerLayout);
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- mActivity.setContentView(layout);
- textView.setSingleLine(true);
- }
+ mActivity.runOnUiThread(() -> {
+ mActivity.setContentView(layout);
+ textView.setSingleLine(true);
});
mInstrumentation.waitForIdleSync();
@@ -3127,11 +3559,7 @@
singleLineHeight = textView.getLayout().getHeight();
}
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- textView.setSingleLine(false);
- }
- });
+ mActivity.runOnUiThread(() -> textView.setSingleLine(false));
mInstrumentation.waitForIdleSync();
assertEquals(null, textView.getTransformationMethod());
@@ -3141,11 +3569,7 @@
}
// same behaviours as setSingLine(true)
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- textView.setSingleLine();
- }
- });
+ mActivity.runOnUiThread(textView::setSingleLine);
mInstrumentation.waitForIdleSync();
assertEquals(SingleLineTransformationMethod.getInstance(),
textView.getTransformationMethod());
@@ -3157,17 +3581,9 @@
}
@UiThreadTest
- public void testSetMaxLines() {
+ public void testAccessMaxLines() {
mTextView = findTextView(R.id.textview_text);
-
- float[] widths = new float[LONG_TEXT.length()];
- mTextView.getPaint().getTextWidths(LONG_TEXT, widths);
- float totalWidth = 0.0f;
- for (float f : widths) {
- totalWidth += f;
- }
- final int stringWidth = (int) totalWidth;
- mTextView.setWidth(stringWidth >> 2);
+ mTextView.setWidth((int) (mTextView.getPaint().measureText(LONG_TEXT) / 4));
mTextView.setText(LONG_TEXT);
final int maxLines = 2;
@@ -3176,23 +3592,11 @@
mTextView.setMaxLines(maxLines);
mTextView.requestLayout();
+ assertEquals(2, mTextView.getMaxLines());
+ assertEquals(-1, mTextView.getMaxHeight());
assertTrue(mTextView.getHeight() <= maxLines * mTextView.getLineHeight());
}
- public int calculateTextWidth(String text) {
- mTextView = findTextView(R.id.textview_text);
-
- // Set the TextView width as the half of the whole text.
- float[] widths = new float[text.length()];
- mTextView.getPaint().getTextWidths(text, widths);
- float textfieldWidth = 0.0f;
- for (int i = 0; i < text.length(); ++i) {
- textfieldWidth += widths[i];
- }
- return (int)textfieldWidth;
-
- }
-
@UiThreadTest
public void testHyphenationNotHappen_frequencyNone() {
final int[] BREAK_STRATEGIES = {
@@ -3204,7 +3608,8 @@
for (int breakStrategy : BREAK_STRATEGIES) {
for (int charWidth = 10; charWidth < 120; charWidth += 5) {
// Change the text view's width to charWidth width.
- mTextView.setWidth(calculateTextWidth(LONG_TEXT.substring(0, charWidth)));
+ final String substring = LONG_TEXT.substring(0, charWidth);
+ mTextView.setWidth((int) Math.ceil(mTextView.getPaint().measureText(substring)));
mTextView.setText(LONG_TEXT);
mTextView.setBreakStrategy(breakStrategy);
@@ -3238,7 +3643,8 @@
for (int hyphenationFrequency: HYPHENATION_FREQUENCIES) {
for (int charWidth = 10; charWidth < 120; charWidth += 5) {
// Change the text view's width to charWidth width.
- mTextView.setWidth(calculateTextWidth(LONG_TEXT.substring(0, charWidth)));
+ final String substring = LONG_TEXT.substring(0, charWidth);
+ mTextView.setWidth((int) Math.ceil(mTextView.getPaint().measureText(substring)));
mTextView.setText(LONG_TEXT);
mTextView.setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE);
@@ -3269,17 +3675,20 @@
mTextView.setMaxLines(-1);
}
- public void testSetMinLines() {
+ public void testAccessMinLines() {
mTextView = findTextView(R.id.textview_text);
setWidth(mTextView.getWidth() >> 3);
- int originalHeight = mTextView.getHeight();
int originalLines = mTextView.getLineCount();
setMinLines(originalLines - 1);
assertTrue((originalLines - 1) * mTextView.getLineHeight() <= mTextView.getHeight());
+ assertEquals(originalLines - 1, mTextView.getMinLines());
+ assertEquals(-1, mTextView.getMinHeight());
setMinLines(originalLines + 1);
assertTrue((originalLines + 1) * mTextView.getLineHeight() <= mTextView.getHeight());
+ assertEquals(originalLines + 1, mTextView.getMinLines());
+ assertEquals(-1, mTextView.getMinHeight());
}
public void testSetLines() {
@@ -3310,7 +3719,7 @@
assertEquals(0, mTextView.getExtendedPaddingTop());
// After Set a Drawable
- final Drawable top = getDrawable(R.drawable.red);
+ final Drawable top = TestUtils.getDrawable(mActivity, R.drawable.red);
top.setBounds(0, 0, 100, 10);
mTextView.setCompoundDrawables(null, top, null, null);
assertEquals(mTextView.getCompoundPaddingTop(), mTextView.getExtendedPaddingTop());
@@ -3329,7 +3738,7 @@
assertEquals(0, mTextView.getExtendedPaddingBottom());
// After Set a Drawable
- final Drawable bottom = getDrawable(R.drawable.red);
+ final Drawable bottom = TestUtils.getDrawable(mActivity, R.drawable.red);
bottom.setBounds(0, 0, 100, 10);
mTextView.setCompoundDrawables(null, null, null, bottom);
assertEquals(mTextView.getCompoundPaddingBottom(), mTextView.getExtendedPaddingBottom());
@@ -3347,14 +3756,12 @@
assertEquals(0, mTextView.getTotalPaddingTop());
// After Set a Drawable
- final Drawable top = getDrawable(R.drawable.red);
+ final Drawable top = TestUtils.getDrawable(mActivity, R.drawable.red);
top.setBounds(0, 0, 100, 10);
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- mTextView.setCompoundDrawables(null, top, null, null);
- mTextView.setLines(mTextView.getLineCount() - 1);
- mTextView.setGravity(Gravity.BOTTOM);
- }
+ mActivity.runOnUiThread(() -> {
+ mTextView.setCompoundDrawables(null, top, null, null);
+ mTextView.setLines(mTextView.getLineCount() - 1);
+ mTextView.setGravity(Gravity.BOTTOM);
});
mInstrumentation.waitForIdleSync();
assertEquals(mTextView.getExtendedPaddingTop(), mTextView.getTotalPaddingTop());
@@ -3373,14 +3780,12 @@
assertEquals(0, mTextView.getTotalPaddingBottom());
// After Set a Drawable
- final Drawable bottom = getDrawable(R.drawable.red);
+ final Drawable bottom = TestUtils.getDrawable(mActivity, R.drawable.red);
bottom.setBounds(0, 0, 100, 10);
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- mTextView.setCompoundDrawables(null, null, null, bottom);
- mTextView.setLines(mTextView.getLineCount() - 1);
- mTextView.setGravity(Gravity.CENTER_VERTICAL);
- }
+ mActivity.runOnUiThread(() -> {
+ mTextView.setCompoundDrawables(null, null, null, bottom);
+ mTextView.setLines(mTextView.getLineCount() - 1);
+ mTextView.setGravity(Gravity.CENTER_VERTICAL);
});
mInstrumentation.waitForIdleSync();
assertEquals(mTextView.getExtendedPaddingBottom(), mTextView.getTotalPaddingBottom());
@@ -3402,7 +3807,7 @@
assertEquals(0, mTextView.getTotalPaddingLeft());
// After Set a Drawable
- Drawable left = getDrawable(R.drawable.red);
+ Drawable left = TestUtils.getDrawable(mActivity, R.drawable.red);
left.setBounds(0, 0, 10, 100);
mTextView.setCompoundDrawables(left, null, null, null);
mTextView.setGravity(Gravity.RIGHT);
@@ -3420,7 +3825,7 @@
assertEquals(0, mTextView.getTotalPaddingRight());
// After Set a Drawable
- Drawable right = getDrawable(R.drawable.red);
+ Drawable right = TestUtils.getDrawable(mActivity, R.drawable.red);
right.setBounds(0, 0, 10, 100);
mTextView.setCompoundDrawables(null, null, right, null);
mTextView.setGravity(Gravity.CENTER_HORIZONTAL);
@@ -3519,6 +3924,8 @@
mTextView.getCurrentHintTextColor());
assertEquals(mActivity.getResources().getColor(R.drawable.blue),
mTextView.getLinkTextColors().getDefaultColor());
+ assertEquals(mActivity.getResources().getColor(R.drawable.yellow),
+ mTextView.getHighlightColor());
mTextView.setTextAppearance(R.style.TextAppearance_Colors);
assertEquals(mActivity.getResources().getColor(R.drawable.black),
@@ -3527,6 +3934,8 @@
mTextView.getCurrentHintTextColor());
assertEquals(mActivity.getResources().getColor(R.drawable.yellow),
mTextView.getLinkTextColors().getDefaultColor());
+ assertEquals(mActivity.getResources().getColor(R.drawable.red),
+ mTextView.getHighlightColor());
mTextView.setTextAppearance(R.style.TextAppearance_NotColors);
assertEquals(17f, mTextView.getTextSize(), 0.01f);
@@ -3609,11 +4018,10 @@
}
public void testDrawableStateChanged() {
- MockTextView textView = new MockTextView(mActivity);
-
- textView.reset();
+ MockTextView textView = spy(new MockTextView(mActivity));
+ reset(textView);
textView.refreshDrawableState();
- assertTrue(textView.hasCalledDrawableStateChanged());
+ verify(textView, times(1)).drawableStateChanged();
}
public void testGetDefaultEditable() {
@@ -3659,24 +4067,28 @@
//Assign a new size to this view
assertTrue(textView.setFrame(0, 0, 320, 480));
- assertEquals(0, textView.getFrameLeft());
- assertEquals(0, textView.getFrameTop());
- assertEquals(320, textView.getFrameRight());
- assertEquals(480, textView.getFrameBottom());
+ assertEquals(0, textView.getLeft());
+ assertEquals(0, textView.getTop());
+ assertEquals(320, textView.getRight());
+ assertEquals(480, textView.getBottom());
//Assign a same size to this view
assertFalse(textView.setFrame(0, 0, 320, 480));
//negative input
assertTrue(textView.setFrame(-1, -1, -1, -1));
- assertEquals(-1, textView.getFrameLeft());
- assertEquals(-1, textView.getFrameTop());
- assertEquals(-1, textView.getFrameRight());
- assertEquals(-1, textView.getFrameBottom());
+ assertEquals(-1, textView.getLeft());
+ assertEquals(-1, textView.getTop());
+ assertEquals(-1, textView.getRight());
+ assertEquals(-1, textView.getBottom());
}
public void testMarquee() {
- final MockTextView textView = new MockTextView(mActivity);
+ // Both are pointing to the same object. This works around current limitation in CTS
+ // coverage report tool for properly reporting coverage of base class method calls.
+ final MockTextView mockTextView = new MockTextView(mActivity);
+ final TextView textView = mockTextView;
+
textView.setText(LONG_TEXT);
textView.setSingleLine();
textView.setEllipsize(TruncateAt.MARQUEE);
@@ -3688,11 +4100,7 @@
// make the fading to be shown
textView.setHorizontalFadingEdgeEnabled(true);
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- mActivity.setContentView(layout);
- }
- });
+ mActivity.runOnUiThread(() -> mActivity.setContentView(layout));
mInstrumentation.waitForIdleSync();
TestSelectedRunnable runnable = new TestSelectedRunnable(textView) {
@@ -3708,23 +4116,15 @@
// wait for the marquee to run
// fading is shown on both sides if the marquee runs for a while
- new PollingCheck(TIMEOUT) {
- @Override
- protected boolean check() {
- return textView.getLeftFadingEdgeStrength() > 0.0f
- && textView.getRightFadingEdgeStrength() > 0.0f;
- }
- }.run();
+ PollingCheck.waitFor(TIMEOUT, () -> mockTextView.getLeftFadingEdgeStrength() > 0.0f
+ && mockTextView.getRightFadingEdgeStrength() > 0.0f);
// wait for left marquee to fully apply
- new PollingCheck(TIMEOUT) {
- @Override
- protected boolean check() {
- return textView.getLeftFadingEdgeStrength() > 0.99f;
- }
- }.run();
+ PollingCheck.waitFor(TIMEOUT, () -> mockTextView.getLeftFadingEdgeStrength() > 0.99f);
+
assertFalse(runnable.getIsSelected1());
assertTrue(runnable.getIsSelected2());
+ assertEquals(-1, textView.getMarqueeRepeatLimit());
runnable = new TestSelectedRunnable(textView) {
public void run() {
@@ -3741,27 +4141,28 @@
mInstrumentation.waitForIdleSync();
assertTrue(runnable.getIsSelected1());
assertFalse(runnable.getIsSelected2());
- assertEquals(0.0f, textView.getLeftFadingEdgeStrength(), 0.01f);
- assertTrue(textView.getRightFadingEdgeStrength() > 0.0f);
+ assertEquals(0.0f, mockTextView.getLeftFadingEdgeStrength(), 0.01f);
+ assertTrue(mockTextView.getRightFadingEdgeStrength() > 0.0f);
+ assertEquals(0, textView.getMarqueeRepeatLimit());
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- textView.setGravity(Gravity.RIGHT);
- }
- });
+ mActivity.runOnUiThread(() -> textView.setGravity(Gravity.RIGHT));
mInstrumentation.waitForIdleSync();
- assertTrue(textView.getLeftFadingEdgeStrength() > 0.0f);
- assertEquals(0.0f, textView.getRightFadingEdgeStrength(), 0.01f);
+ assertTrue(mockTextView.getLeftFadingEdgeStrength() > 0.0f);
+ assertEquals(0.0f, mockTextView.getRightFadingEdgeStrength(), 0.01f);
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- textView.setGravity(Gravity.CENTER_HORIZONTAL);
- }
- });
+ mActivity.runOnUiThread(() -> textView.setGravity(Gravity.CENTER_HORIZONTAL));
mInstrumentation.waitForIdleSync();
// there is no left fading (Is it correct?)
- assertEquals(0.0f, textView.getLeftFadingEdgeStrength(), 0.01f);
- assertTrue(textView.getRightFadingEdgeStrength() > 0.0f);
+ assertEquals(0.0f, mockTextView.getLeftFadingEdgeStrength(), 0.01f);
+ assertTrue(mockTextView.getRightFadingEdgeStrength() > 0.0f);
+ }
+
+ @MediumTest
+ public void testGetMarqueeRepeatLimit() {
+ final TextView textView = new TextView(mActivity);
+
+ textView.setMarqueeRepeatLimit(10);
+ assertEquals(10, textView.getMarqueeRepeatLimit());
}
public void testOnKeyMultiple() {
@@ -3954,7 +4355,7 @@
public void testVerifyDrawable() {
MockTextView textView = new MockTextView(mActivity);
- Drawable d = getDrawable(R.drawable.pass);
+ Drawable d = TestUtils.getDrawable(mActivity, R.drawable.pass);
assertFalse(textView.verifyDrawable(d));
textView.setCompoundDrawables(null, d, null, null);
@@ -3975,14 +4376,16 @@
public void testSetOnEditorActionListener() {
mTextView = findTextView(R.id.textview_text);
- MockOnEditorActionListener listener = new MockOnEditorActionListener();
- assertFalse(listener.isOnEditorActionCalled());
+ final TextView.OnEditorActionListener mockOnEditorActionListener =
+ mock(TextView.OnEditorActionListener.class);
+ verifyZeroInteractions(mockOnEditorActionListener);
- mTextView.setOnEditorActionListener(listener);
- assertFalse(listener.isOnEditorActionCalled());
+ mTextView.setOnEditorActionListener(mockOnEditorActionListener);
+ verifyZeroInteractions(mockOnEditorActionListener);
mTextView.onEditorAction(EditorInfo.IME_ACTION_DONE);
- assertTrue(listener.isOnEditorActionCalled());
+ verify(mockOnEditorActionListener, times(1)).onEditorAction(mTextView,
+ EditorInfo.IME_ACTION_DONE, null);
}
public void testAccessImeOptions() {
@@ -4091,48 +4494,30 @@
mTextView = findTextView(R.id.textview_text);
// not a spannable text
- runTestOnUiThread(new Runnable() {
- public void run() {
- assertFalse(mTextView.moveCursorToVisibleOffset());
- }
- });
+ runTestOnUiThread(() -> assertFalse(mTextView.moveCursorToVisibleOffset()));
mInstrumentation.waitForIdleSync();
// a selection range
final String spannableText = "text";
mTextView = new TextView(mActivity);
- runTestOnUiThread(new Runnable() {
- public void run() {
- mTextView.setText(spannableText, BufferType.SPANNABLE);
- }
- });
+ runTestOnUiThread(() -> mTextView.setText(spannableText, BufferType.SPANNABLE));
mInstrumentation.waitForIdleSync();
Selection.setSelection((Spannable) mTextView.getText(), 0, spannableText.length());
assertEquals(0, mTextView.getSelectionStart());
assertEquals(spannableText.length(), mTextView.getSelectionEnd());
- runTestOnUiThread(new Runnable() {
- public void run() {
- assertFalse(mTextView.moveCursorToVisibleOffset());
- }
- });
+ runTestOnUiThread(() -> assertFalse(mTextView.moveCursorToVisibleOffset()));
mInstrumentation.waitForIdleSync();
// a spannable without range
- runTestOnUiThread(new Runnable() {
- public void run() {
- mTextView = findTextView(R.id.textview_text);
- mTextView.setText(spannableText, BufferType.SPANNABLE);
- }
+ runTestOnUiThread(() -> {
+ mTextView = findTextView(R.id.textview_text);
+ mTextView.setText(spannableText, BufferType.SPANNABLE);
});
mInstrumentation.waitForIdleSync();
- runTestOnUiThread(new Runnable() {
- public void run() {
- assertTrue(mTextView.moveCursorToVisibleOffset());
- }
- });
+ runTestOnUiThread(() -> assertTrue(mTextView.moveCursorToVisibleOffset()));
mInstrumentation.waitForIdleSync();
}
@@ -4141,29 +4526,71 @@
assertFalse(mTextView.isInputMethodTarget());
assertFalse(mTextView.isFocused());
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- mTextView.setFocusable(true);
- mTextView.requestFocus();
- }
- });
+ runTestOnUiThread(() -> {
+ mTextView.setFocusable(true);
+ mTextView.requestFocus();
+ });
mInstrumentation.waitForIdleSync();
assertTrue(mTextView.isFocused());
- new PollingCheck() {
- @Override
- protected boolean check() {
- return mTextView.isInputMethodTarget();
- }
- }.run();
+ PollingCheck.waitFor(mTextView::isInputMethodTarget);
}
- public void testBeginEndBatchEdit() {
- mTextView = findTextView(R.id.textview_text);
+ @MediumTest
+ public void testBeginEndBatchEditAreNotCalledForNonEditableText() {
+ final TextView mockTextView = spy(new TextView(mActivity));
- mTextView.beginBatchEdit();
- mTextView.endBatchEdit();
+ // TextView should not call onBeginBatchEdit or onEndBatchEdit during initialization
+ verify(mockTextView, never()).onBeginBatchEdit();
+ verify(mockTextView, never()).onEndBatchEdit();
+
+
+ mockTextView.beginBatchEdit();
+ // Since TextView doesn't support editing, the callbacks should not be called
+ verify(mockTextView, never()).onBeginBatchEdit();
+ verify(mockTextView, never()).onEndBatchEdit();
+
+ mockTextView.endBatchEdit();
+ // Since TextView doesn't support editing, the callbacks should not be called
+ verify(mockTextView, never()).onBeginBatchEdit();
+ verify(mockTextView, never()).onEndBatchEdit();
+ }
+
+ @MediumTest
+ public void testBeginEndBatchEditCallbacksAreCalledForEditableText() {
+ final TextView mockTextView = spy(new TextView(mActivity));
+
+ final FrameLayout layout = new FrameLayout(getActivity());
+ ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT);
+ layout.addView(mockTextView, layoutParams);
+ layout.setLayoutParams(layoutParams);
+
+ mActivity.runOnUiThread(() -> mActivity.setContentView(layout));
+ mInstrumentation.waitForIdleSync();
+
+ mActivity.runOnUiThread(() -> {
+ mockTextView.setKeyListener(QwertyKeyListener.getInstance(false, Capitalize.NONE));
+ mockTextView.setText("", BufferType.EDITABLE);
+ mockTextView.requestFocus();
+ });
+ mInstrumentation.waitForIdleSync();
+
+ reset(mockTextView);
+ assertTrue(mockTextView.hasFocus());
+ verify(mockTextView, never()).onBeginBatchEdit();
+ verify(mockTextView, never()).onEndBatchEdit();
+
+ mockTextView.beginBatchEdit();
+
+ verify(mockTextView, times(1)).onBeginBatchEdit();
+ verify(mockTextView, never()).onEndBatchEdit();
+
+ reset(mockTextView);
+ mockTextView.endBatchEdit();
+ verify(mockTextView, never()).onBeginBatchEdit();
+ verify(mockTextView, times(1)).onEndBatchEdit();
}
@UiThreadTest
@@ -4177,7 +4604,7 @@
public void testCancelLongPress() {
mTextView = findTextView(R.id.textview_text);
- TouchUtils.longClickView(this, mTextView);
+ CtsTouchUtils.emulateLongClick(mInstrumentation, mTextView);
mTextView.cancelLongPress();
}
@@ -4203,7 +4630,7 @@
MockTextView textView = new MockTextView(mActivity);
assertEquals(0, textView.computeVerticalScrollExtent());
- Drawable d = getDrawable(R.drawable.pass);
+ Drawable d = TestUtils.getDrawable(mActivity, R.drawable.pass);
textView.setCompoundDrawables(null, d, null, d);
assertEquals(0, textView.computeVerticalScrollExtent());
@@ -4221,23 +4648,21 @@
public void testSelectAllJustAfterTap() {
// Prepare an EditText with focus.
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- mTextView = new EditText(mActivity);
- mActivity.setContentView(mTextView);
+ mActivity.runOnUiThread(() -> {
+ mTextView = new EditText(mActivity);
+ mActivity.setContentView(mTextView);
- assertFalse(mTextView.didTouchFocusSelect());
- mTextView.setFocusable(true);
- mTextView.requestFocus();
- assertTrue(mTextView.didTouchFocusSelect());
+ assertFalse(mTextView.didTouchFocusSelect());
+ mTextView.setFocusable(true);
+ mTextView.requestFocus();
+ assertTrue(mTextView.didTouchFocusSelect());
- mTextView.setText("Hello, World.", BufferType.SPANNABLE);
- }
+ mTextView.setText("Hello, World.", BufferType.SPANNABLE);
});
mInstrumentation.waitForIdleSync();
// Tap the view to show InsertPointController.
- TouchUtils.tapView(this, mTextView);
+ CtsTouchUtils.emulateTapOnViewCenter(mInstrumentation, mTextView);
// bad workaround for waiting onStartInputView of LeanbackIme.apk done
try {
Thread.sleep(1000);
@@ -4246,11 +4671,7 @@
}
// Execute SelectAll context menu.
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- mTextView.onTextContextMenuItem(android.R.id.selectAll);
- }
- });
+ mActivity.runOnUiThread(() -> mTextView.onTextContextMenuItem(android.R.id.selectAll));
mInstrumentation.waitForIdleSync();
// The selection must be whole of the text contents.
@@ -4781,226 +5202,154 @@
@UiThreadTest
public void testDrawableResolution() {
- final int LEFT = 0;
- final int TOP = 1;
- final int RIGHT = 2;
- final int BOTTOM = 3;
-
- TextViewCtsActivity activity = getActivity();
-
// Case 1.1: left / right drawable defined in default LTR mode
- TextView tv = (TextView) activity.findViewById(R.id.textview_drawable_1_1);
- Drawable[] drawables = tv.getCompoundDrawables();
-
- WidgetTestUtils.assertEquals(getBitmap(R.drawable.icon_blue),
- ((BitmapDrawable) drawables[LEFT]).getBitmap());
- WidgetTestUtils.assertEquals(getBitmap(R.drawable.icon_red),
- ((BitmapDrawable) drawables[RIGHT]).getBitmap());
- WidgetTestUtils.assertEquals(getBitmap(R.drawable.icon_green),
- ((BitmapDrawable) drawables[TOP]).getBitmap());
- WidgetTestUtils.assertEquals(getBitmap(R.drawable.icon_yellow),
- ((BitmapDrawable) drawables[BOTTOM]).getBitmap());
+ TextView tv = (TextView) mActivity.findViewById(R.id.textview_drawable_1_1);
+ TestUtils.verifyCompoundDrawables(tv, R.drawable.icon_blue, R.drawable.icon_red,
+ R.drawable.icon_green, R.drawable.icon_yellow);
+ TestUtils.verifyCompoundDrawablesRelative(tv, -1, -1,
+ R.drawable.icon_green, R.drawable.icon_yellow);
// Case 1.2: left / right drawable defined in default RTL mode
- tv = (TextView) activity.findViewById(R.id.textview_drawable_1_2);
- drawables = tv.getCompoundDrawables();
-
- WidgetTestUtils.assertEquals(getBitmap(R.drawable.icon_blue),
- ((BitmapDrawable) drawables[LEFT]).getBitmap());
- WidgetTestUtils.assertEquals(getBitmap(R.drawable.icon_red),
- ((BitmapDrawable) drawables[RIGHT]).getBitmap());
- WidgetTestUtils.assertEquals(getBitmap(R.drawable.icon_green),
- ((BitmapDrawable) drawables[TOP]).getBitmap());
- WidgetTestUtils.assertEquals(getBitmap(R.drawable.icon_yellow),
- ((BitmapDrawable) drawables[BOTTOM]).getBitmap());
+ tv = (TextView) mActivity.findViewById(R.id.textview_drawable_1_2);
+ TestUtils.verifyCompoundDrawables(tv, R.drawable.icon_blue, R.drawable.icon_red,
+ R.drawable.icon_green, R.drawable.icon_yellow);
+ TestUtils.verifyCompoundDrawablesRelative(tv, -1, -1,
+ R.drawable.icon_green, R.drawable.icon_yellow);
// Case 2.1: start / end drawable defined in LTR mode
- tv = (TextView) activity.findViewById(R.id.textview_drawable_2_1);
- drawables = tv.getCompoundDrawables();
-
- WidgetTestUtils.assertEquals(getBitmap(R.drawable.icon_blue),
- ((BitmapDrawable) drawables[LEFT]).getBitmap());
- WidgetTestUtils.assertEquals(getBitmap(R.drawable.icon_red),
- ((BitmapDrawable) drawables[RIGHT]).getBitmap());
- WidgetTestUtils.assertEquals(getBitmap(R.drawable.icon_green),
- ((BitmapDrawable) drawables[TOP]).getBitmap());
- WidgetTestUtils.assertEquals(getBitmap(R.drawable.icon_yellow),
- ((BitmapDrawable) drawables[BOTTOM]).getBitmap());
+ tv = (TextView) mActivity.findViewById(R.id.textview_drawable_2_1);
+ TestUtils.verifyCompoundDrawables(tv, R.drawable.icon_blue, R.drawable.icon_red,
+ R.drawable.icon_green, R.drawable.icon_yellow);
+ TestUtils.verifyCompoundDrawablesRelative(tv, R.drawable.icon_blue, R.drawable.icon_red,
+ R.drawable.icon_green, R.drawable.icon_yellow);
// Case 2.2: start / end drawable defined in RTL mode
- tv = (TextView) activity.findViewById(R.id.textview_drawable_2_2);
- drawables = tv.getCompoundDrawables();
-
- WidgetTestUtils.assertEquals(getBitmap(R.drawable.icon_red),
- ((BitmapDrawable) drawables[LEFT]).getBitmap());
- WidgetTestUtils.assertEquals(getBitmap(R.drawable.icon_blue),
- ((BitmapDrawable) drawables[RIGHT]).getBitmap());
- WidgetTestUtils.assertEquals(getBitmap(R.drawable.icon_green),
- ((BitmapDrawable) drawables[TOP]).getBitmap());
- WidgetTestUtils.assertEquals(getBitmap(R.drawable.icon_yellow),
- ((BitmapDrawable) drawables[BOTTOM]).getBitmap());
+ tv = (TextView) mActivity.findViewById(R.id.textview_drawable_2_2);
+ TestUtils.verifyCompoundDrawables(tv, R.drawable.icon_red, R.drawable.icon_blue,
+ R.drawable.icon_green, R.drawable.icon_yellow);
+ TestUtils.verifyCompoundDrawablesRelative(tv, R.drawable.icon_blue, R.drawable.icon_red,
+ R.drawable.icon_green, R.drawable.icon_yellow);
// Case 3.1: left / right / start / end drawable defined in LTR mode
- tv = (TextView) activity.findViewById(R.id.textview_drawable_3_1);
- drawables = tv.getCompoundDrawables();
-
- WidgetTestUtils.assertEquals(getBitmap(R.drawable.icon_blue),
- ((BitmapDrawable) drawables[LEFT]).getBitmap());
- WidgetTestUtils.assertEquals(getBitmap(R.drawable.icon_red),
- ((BitmapDrawable) drawables[RIGHT]).getBitmap());
- WidgetTestUtils.assertEquals(getBitmap(R.drawable.icon_green),
- ((BitmapDrawable) drawables[TOP]).getBitmap());
- WidgetTestUtils.assertEquals(getBitmap(R.drawable.icon_yellow),
- ((BitmapDrawable) drawables[BOTTOM]).getBitmap());
+ tv = (TextView) mActivity.findViewById(R.id.textview_drawable_3_1);
+ TestUtils.verifyCompoundDrawables(tv, R.drawable.icon_blue, R.drawable.icon_red,
+ R.drawable.icon_green, R.drawable.icon_yellow);
+ TestUtils.verifyCompoundDrawablesRelative(tv, R.drawable.icon_blue, R.drawable.icon_red,
+ R.drawable.icon_green, R.drawable.icon_yellow);
// Case 3.2: left / right / start / end drawable defined in RTL mode
- tv = (TextView) activity.findViewById(R.id.textview_drawable_3_2);
- drawables = tv.getCompoundDrawables();
-
- WidgetTestUtils.assertEquals(getBitmap(R.drawable.icon_red),
- ((BitmapDrawable) drawables[LEFT]).getBitmap());
- WidgetTestUtils.assertEquals(getBitmap(R.drawable.icon_blue),
- ((BitmapDrawable) drawables[RIGHT]).getBitmap());
- WidgetTestUtils.assertEquals(getBitmap(R.drawable.icon_green),
- ((BitmapDrawable) drawables[TOP]).getBitmap());
- WidgetTestUtils.assertEquals(getBitmap(R.drawable.icon_yellow),
- ((BitmapDrawable) drawables[BOTTOM]).getBitmap());
+ tv = (TextView) mActivity.findViewById(R.id.textview_drawable_3_2);
+ TestUtils.verifyCompoundDrawables(tv, R.drawable.icon_red, R.drawable.icon_blue,
+ R.drawable.icon_green, R.drawable.icon_yellow);
+ TestUtils.verifyCompoundDrawablesRelative(tv, R.drawable.icon_blue, R.drawable.icon_red,
+ R.drawable.icon_green, R.drawable.icon_yellow);
// Case 4.1: start / end drawable defined in LTR mode inside a layout
// that defines the layout direction
- tv = (TextView) activity.findViewById(R.id.textview_drawable_4_1);
- drawables = tv.getCompoundDrawables();
-
- WidgetTestUtils.assertEquals(getBitmap(R.drawable.icon_blue),
- ((BitmapDrawable) drawables[LEFT]).getBitmap());
- WidgetTestUtils.assertEquals(getBitmap(R.drawable.icon_red),
- ((BitmapDrawable) drawables[RIGHT]).getBitmap());
- WidgetTestUtils.assertEquals(getBitmap(R.drawable.icon_green),
- ((BitmapDrawable) drawables[TOP]).getBitmap());
- WidgetTestUtils.assertEquals(getBitmap(R.drawable.icon_yellow),
- ((BitmapDrawable) drawables[BOTTOM]).getBitmap());
+ tv = (TextView) mActivity.findViewById(R.id.textview_drawable_4_1);
+ TestUtils.verifyCompoundDrawables(tv, R.drawable.icon_blue, R.drawable.icon_red,
+ R.drawable.icon_green, R.drawable.icon_yellow);
+ TestUtils.verifyCompoundDrawablesRelative(tv, R.drawable.icon_blue, R.drawable.icon_red,
+ R.drawable.icon_green, R.drawable.icon_yellow);
// Case 4.2: start / end drawable defined in RTL mode inside a layout
// that defines the layout direction
- tv = (TextView) activity.findViewById(R.id.textview_drawable_4_2);
- drawables = tv.getCompoundDrawables();
-
- WidgetTestUtils.assertEquals(getBitmap(R.drawable.icon_red),
- ((BitmapDrawable) drawables[LEFT]).getBitmap());
- WidgetTestUtils.assertEquals(getBitmap(R.drawable.icon_blue),
- ((BitmapDrawable) drawables[RIGHT]).getBitmap());
- WidgetTestUtils.assertEquals(getBitmap(R.drawable.icon_green),
- ((BitmapDrawable) drawables[TOP]).getBitmap());
- WidgetTestUtils.assertEquals(getBitmap(R.drawable.icon_yellow),
- ((BitmapDrawable) drawables[BOTTOM]).getBitmap());
+ tv = (TextView) mActivity.findViewById(R.id.textview_drawable_4_2);
+ TestUtils.verifyCompoundDrawables(tv, R.drawable.icon_red, R.drawable.icon_blue,
+ R.drawable.icon_green, R.drawable.icon_yellow);
+ TestUtils.verifyCompoundDrawablesRelative(tv, R.drawable.icon_blue, R.drawable.icon_red,
+ R.drawable.icon_green, R.drawable.icon_yellow);
// Case 5.1: left / right / start / end drawable defined in LTR mode inside a layout
// that defines the layout direction
- tv = (TextView) activity.findViewById(R.id.textview_drawable_3_1);
- drawables = tv.getCompoundDrawables();
-
- WidgetTestUtils.assertEquals(getBitmap(R.drawable.icon_blue),
- ((BitmapDrawable) drawables[LEFT]).getBitmap());
- WidgetTestUtils.assertEquals(getBitmap(R.drawable.icon_red),
- ((BitmapDrawable) drawables[RIGHT]).getBitmap());
- WidgetTestUtils.assertEquals(getBitmap(R.drawable.icon_green),
- ((BitmapDrawable) drawables[TOP]).getBitmap());
- WidgetTestUtils.assertEquals(getBitmap(R.drawable.icon_yellow),
- ((BitmapDrawable) drawables[BOTTOM]).getBitmap());
+ tv = (TextView) mActivity.findViewById(R.id.textview_drawable_5_1);
+ TestUtils.verifyCompoundDrawables(tv, R.drawable.icon_blue, R.drawable.icon_red,
+ R.drawable.icon_green, R.drawable.icon_yellow);
+ TestUtils.verifyCompoundDrawablesRelative(tv, R.drawable.icon_blue, R.drawable.icon_red,
+ R.drawable.icon_green, R.drawable.icon_yellow);
// Case 5.2: left / right / start / end drawable defined in RTL mode inside a layout
// that defines the layout direction
- tv = (TextView) activity.findViewById(R.id.textview_drawable_3_2);
- drawables = tv.getCompoundDrawables();
-
- WidgetTestUtils.assertEquals(getBitmap(R.drawable.icon_red),
- ((BitmapDrawable) drawables[LEFT]).getBitmap());
- WidgetTestUtils.assertEquals(getBitmap(R.drawable.icon_blue),
- ((BitmapDrawable) drawables[RIGHT]).getBitmap());
- WidgetTestUtils.assertEquals(getBitmap(R.drawable.icon_green),
- ((BitmapDrawable) drawables[TOP]).getBitmap());
- WidgetTestUtils.assertEquals(getBitmap(R.drawable.icon_yellow),
- ((BitmapDrawable) drawables[BOTTOM]).getBitmap());
+ tv = (TextView) mActivity.findViewById(R.id.textview_drawable_5_2);
+ TestUtils.verifyCompoundDrawables(tv, R.drawable.icon_red, R.drawable.icon_blue,
+ R.drawable.icon_green, R.drawable.icon_yellow);
+ TestUtils.verifyCompoundDrawablesRelative(tv, R.drawable.icon_blue, R.drawable.icon_red,
+ R.drawable.icon_green, R.drawable.icon_yellow);
}
@UiThreadTest
public void testDrawableResolution2() {
- final int LEFT = 0;
- final int TOP = 1;
- final int RIGHT = 2;
- final int BOTTOM = 3;
-
- TextViewCtsActivity activity = getActivity();
-
// Case 1.1: left / right drawable defined in default LTR mode
- TextView tv = (TextView) activity.findViewById(R.id.textview_drawable_1_1);
- Drawable[] drawables = tv.getCompoundDrawables();
+ TextView tv = (TextView) mActivity.findViewById(R.id.textview_drawable_1_1);
+ TestUtils.verifyCompoundDrawables(tv, R.drawable.icon_blue, R.drawable.icon_red,
+ R.drawable.icon_green, R.drawable.icon_yellow);
- WidgetTestUtils.assertEquals(getBitmap(R.drawable.icon_blue),
- ((BitmapDrawable) drawables[LEFT]).getBitmap());
- WidgetTestUtils.assertEquals(getBitmap(R.drawable.icon_red),
- ((BitmapDrawable) drawables[RIGHT]).getBitmap());
- WidgetTestUtils.assertEquals(getBitmap(R.drawable.icon_green),
- ((BitmapDrawable) drawables[TOP]).getBitmap());
- WidgetTestUtils.assertEquals(getBitmap(R.drawable.icon_yellow),
- ((BitmapDrawable) drawables[BOTTOM]).getBitmap());
+ tv.setCompoundDrawables(null, null,
+ TestUtils.getDrawable(mActivity, R.drawable.icon_yellow), null);
+ TestUtils.verifyCompoundDrawables(tv, -1, R.drawable.icon_yellow, -1, -1);
- tv.setCompoundDrawables(null, null, getDrawable(R.drawable.icon_yellow), null);
- drawables = tv.getCompoundDrawables();
+ tv = (TextView) mActivity.findViewById(R.id.textview_drawable_1_2);
+ TestUtils.verifyCompoundDrawables(tv, R.drawable.icon_blue, R.drawable.icon_red,
+ R.drawable.icon_green, R.drawable.icon_yellow);
- assertNull(drawables[LEFT]);
- WidgetTestUtils.assertEquals(getBitmap(R.drawable.icon_yellow),
- ((BitmapDrawable) drawables[RIGHT]).getBitmap());
- assertNull(drawables[TOP]);
- assertNull(drawables[BOTTOM]);
+ tv.setCompoundDrawables(TestUtils.getDrawable(mActivity, R.drawable.icon_yellow), null,
+ null, null);
+ TestUtils.verifyCompoundDrawables(tv, R.drawable.icon_yellow, -1, -1, -1);
- tv = (TextView) activity.findViewById(R.id.textview_drawable_1_2);
- drawables = tv.getCompoundDrawables();
+ tv = (TextView) mActivity.findViewById(R.id.textview_ltr);
+ TestUtils.verifyCompoundDrawables(tv, -1, -1, -1, -1);
- WidgetTestUtils.assertEquals(getBitmap(R.drawable.icon_blue),
- ((BitmapDrawable) drawables[LEFT]).getBitmap());
- WidgetTestUtils.assertEquals(getBitmap(R.drawable.icon_red),
- ((BitmapDrawable) drawables[RIGHT]).getBitmap());
- WidgetTestUtils.assertEquals(getBitmap(R.drawable.icon_green),
- ((BitmapDrawable) drawables[TOP]).getBitmap());
- WidgetTestUtils.assertEquals(getBitmap(R.drawable.icon_yellow),
- ((BitmapDrawable) drawables[BOTTOM]).getBitmap());
+ tv.setCompoundDrawables(TestUtils.getDrawable(mActivity, R.drawable.icon_blue), null,
+ TestUtils.getDrawable(mActivity, R.drawable.icon_red), null);
+ TestUtils.verifyCompoundDrawables(tv, R.drawable.icon_blue, R.drawable.icon_red, -1, -1);
- tv.setCompoundDrawables(getDrawable(R.drawable.icon_yellow), null, null, null);
- drawables = tv.getCompoundDrawables();
+ tv.setCompoundDrawablesRelative(TestUtils.getDrawable(mActivity, R.drawable.icon_yellow),
+ null, null, null);
+ TestUtils.verifyCompoundDrawables(tv, R.drawable.icon_yellow, -1, -1, -1);
+ }
- WidgetTestUtils.assertEquals(getBitmap(R.drawable.icon_yellow),
- ((BitmapDrawable) drawables[LEFT]).getBitmap());
- assertNull(drawables[RIGHT]);
- assertNull(drawables[TOP]);
- assertNull(drawables[BOTTOM]);
+ public void testCompoundAndTotalPadding() {
+ final Resources res = mActivity.getResources();
+ final int drawablePadding = res.getDimensionPixelSize(R.dimen.textview_drawable_padding);
+ final int paddingLeft = res.getDimensionPixelSize(R.dimen.textview_padding_left);
+ final int paddingRight = res.getDimensionPixelSize(R.dimen.textview_padding_right);
+ final int paddingTop = res.getDimensionPixelSize(R.dimen.textview_padding_top);
+ final int paddingBottom = res.getDimensionPixelSize(R.dimen.textview_padding_bottom);
+ final int iconSize = TestUtils.dpToPx(mActivity, 32);
- tv = (TextView) activity.findViewById(R.id.textview_ltr);
- drawables = tv.getCompoundDrawables();
+ final TextView textViewLtr = (TextView) mActivity.findViewById(
+ R.id.textview_compound_drawable_ltr);
+ final int combinedPaddingLeftLtr = paddingLeft + drawablePadding + iconSize;
+ final int combinedPaddingRightLtr = paddingRight + drawablePadding + iconSize;
+ assertEquals(combinedPaddingLeftLtr, textViewLtr.getCompoundPaddingLeft());
+ assertEquals(combinedPaddingLeftLtr, textViewLtr.getCompoundPaddingStart());
+ assertEquals(combinedPaddingLeftLtr, textViewLtr.getTotalPaddingLeft());
+ assertEquals(combinedPaddingLeftLtr, textViewLtr.getTotalPaddingStart());
+ assertEquals(combinedPaddingRightLtr, textViewLtr.getCompoundPaddingRight());
+ assertEquals(combinedPaddingRightLtr, textViewLtr.getCompoundPaddingEnd());
+ assertEquals(combinedPaddingRightLtr, textViewLtr.getTotalPaddingRight());
+ assertEquals(combinedPaddingRightLtr, textViewLtr.getTotalPaddingEnd());
+ assertEquals(paddingTop + drawablePadding + iconSize,
+ textViewLtr.getCompoundPaddingTop());
+ assertEquals(paddingBottom + drawablePadding + iconSize,
+ textViewLtr.getCompoundPaddingBottom());
- assertNull(drawables[LEFT]);
- assertNull(drawables[RIGHT]);
- assertNull(drawables[TOP]);
- assertNull(drawables[BOTTOM]);
-
- tv.setCompoundDrawables(getDrawable(R.drawable.icon_blue), null, getDrawable(R.drawable.icon_red), null);
- drawables = tv.getCompoundDrawables();
-
- WidgetTestUtils.assertEquals(getBitmap(R.drawable.icon_blue),
- ((BitmapDrawable) drawables[LEFT]).getBitmap());
- WidgetTestUtils.assertEquals(getBitmap(R.drawable.icon_red),
- ((BitmapDrawable) drawables[RIGHT]).getBitmap());
- assertNull(drawables[TOP]);
- assertNull(drawables[BOTTOM]);
-
- tv.setCompoundDrawablesRelative(getDrawable(R.drawable.icon_yellow), null, null, null);
- drawables = tv.getCompoundDrawables();
-
- WidgetTestUtils.assertEquals(getBitmap(R.drawable.icon_yellow),
- ((BitmapDrawable) drawables[LEFT]).getBitmap());
- assertNull(drawables[RIGHT]);
- assertNull(drawables[TOP]);
- assertNull(drawables[BOTTOM]);
+ final TextView textViewRtl = (TextView) mActivity.findViewById(
+ R.id.textview_compound_drawable_rtl);
+ final int combinedPaddingLeftRtl = paddingLeft + drawablePadding + iconSize;
+ final int combinedPaddingRightRtl = paddingRight + drawablePadding + iconSize;
+ assertEquals(combinedPaddingLeftRtl, textViewRtl.getCompoundPaddingLeft());
+ assertEquals(combinedPaddingLeftRtl, textViewRtl.getCompoundPaddingEnd());
+ assertEquals(combinedPaddingLeftRtl, textViewRtl.getTotalPaddingLeft());
+ assertEquals(combinedPaddingLeftRtl, textViewRtl.getTotalPaddingEnd());
+ assertEquals(combinedPaddingRightRtl, textViewRtl.getCompoundPaddingRight());
+ assertEquals(combinedPaddingRightRtl, textViewRtl.getCompoundPaddingStart());
+ assertEquals(combinedPaddingRightRtl, textViewRtl.getTotalPaddingRight());
+ assertEquals(combinedPaddingRightRtl, textViewRtl.getTotalPaddingStart());
+ assertEquals(paddingTop + drawablePadding + iconSize,
+ textViewRtl.getCompoundPaddingTop());
+ assertEquals(paddingBottom + drawablePadding + iconSize,
+ textViewRtl.getCompoundPaddingBottom());
}
public void testSetGetBreakStrategy() {
@@ -5061,161 +5410,505 @@
public void testSetAndGetCustomSelectionActionModeCallback() {
final String text = "abcde";
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- mTextView = new EditText(mActivity);
- mActivity.setContentView(mTextView);
- mTextView.setText(text, BufferType.SPANNABLE);
- mTextView.setTextIsSelectable(true);
- mTextView.requestFocus();
- mTextView.setSelected(true);
- }
+ mActivity.runOnUiThread(() -> {
+ mTextView = new EditText(mActivity);
+ mActivity.setContentView(mTextView);
+ mTextView.setText(text, BufferType.SPANNABLE);
+ mTextView.setTextIsSelectable(true);
+ mTextView.requestFocus();
+ mTextView.setSelected(true);
});
mInstrumentation.waitForIdleSync();
// Check default value.
assertNull(mTextView.getCustomSelectionActionModeCallback());
- MockActionModeCallback callbackBlockActionMode = new MockActionModeCallback(false);
- mTextView.setCustomSelectionActionModeCallback(callbackBlockActionMode);
- assertEquals(callbackBlockActionMode,
+ final ActionMode.Callback mockActionModeCallback = mock(ActionMode.Callback.class);
+ when(mockActionModeCallback.onCreateActionMode(any(ActionMode.class), any(Menu.class))).
+ thenReturn(Boolean.FALSE);
+ mTextView.setCustomSelectionActionModeCallback(mockActionModeCallback);
+ assertEquals(mockActionModeCallback,
mTextView.getCustomSelectionActionModeCallback());
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- // Set selection and try to start action mode.
- final Bundle args = new Bundle();
- args.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT, 0);
- args.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT, text.length());
- mTextView.performAccessibilityAction(
- AccessibilityNodeInfo.ACTION_SET_SELECTION, args);
- }
+ mActivity.runOnUiThread(() -> {
+ // Set selection and try to start action mode.
+ final Bundle args = new Bundle();
+ args.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT, 0);
+ args.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT, text.length());
+ mTextView.performAccessibilityAction(
+ AccessibilityNodeInfo.ACTION_SET_SELECTION, args);
});
mInstrumentation.waitForIdleSync();
- assertEquals(1, callbackBlockActionMode.getCreateCount());
+ verify(mockActionModeCallback, times(1)).onCreateActionMode(
+ any(ActionMode.class), any(Menu.class));
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- // Remove selection and stop action mode.
- mTextView.onTextContextMenuItem(android.R.id.copy);
- }
+ mActivity.runOnUiThread(() -> {
+ // Remove selection and stop action mode.
+ mTextView.onTextContextMenuItem(android.R.id.copy);
});
mInstrumentation.waitForIdleSync();
// Action mode was blocked.
- assertEquals(0, callbackBlockActionMode.getDestroyCount());
+ verify(mockActionModeCallback, never()).onDestroyActionMode(any(ActionMode.class));
- // Overwrite callback.
- MockActionModeCallback callbackStartActionMode = new MockActionModeCallback(true);
- mTextView.setCustomSelectionActionModeCallback(callbackStartActionMode);
- assertEquals(callbackStartActionMode, mTextView.getCustomSelectionActionModeCallback());
+ // Reset and reconfigure callback.
+ reset(mockActionModeCallback);
+ when(mockActionModeCallback.onCreateActionMode(any(ActionMode.class), any(Menu.class))).
+ thenReturn(Boolean.TRUE);
+ assertEquals(mockActionModeCallback, mTextView.getCustomSelectionActionModeCallback());
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- // Set selection and try to start action mode.
- final Bundle args = new Bundle();
- args.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT, 0);
- args.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT, text.length());
- mTextView.performAccessibilityAction(
- AccessibilityNodeInfo.ACTION_SET_SELECTION, args);
+ mActivity.runOnUiThread(() -> {
+ // Set selection and try to start action mode.
+ final Bundle args = new Bundle();
+ args.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT, 0);
+ args.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT, text.length());
+ mTextView.performAccessibilityAction(
+ AccessibilityNodeInfo.ACTION_SET_SELECTION, args);
- }
});
mInstrumentation.waitForIdleSync();
- assertEquals(1, callbackStartActionMode.getCreateCount());
+ verify(mockActionModeCallback, times(1)).onCreateActionMode(
+ any(ActionMode.class), any(Menu.class));
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- // Remove selection and stop action mode.
- mTextView.onTextContextMenuItem(android.R.id.copy);
- }
+ mActivity.runOnUiThread(() -> {
+ // Remove selection and stop action mode.
+ mTextView.onTextContextMenuItem(android.R.id.copy);
});
mInstrumentation.waitForIdleSync();
// Action mode was started
- assertEquals(1, callbackStartActionMode.getDestroyCount());
+ verify(mockActionModeCallback, times(1)).onDestroyActionMode(any(ActionMode.class));
}
- public void testSetAndGetCustomInseltionActionMode() {
+ @UiThreadTest
+ public void testSetAndGetCustomInsertionActionMode() {
initTextViewForTyping();
// Check default value.
assertNull(mTextView.getCustomInsertionActionModeCallback());
- MockActionModeCallback callback = new MockActionModeCallback(false);
- mTextView.setCustomInsertionActionModeCallback(callback);
- assertEquals(callback, mTextView.getCustomInsertionActionModeCallback());
+ final ActionMode.Callback mockActionModeCallback = mock(ActionMode.Callback.class);
+ when(mockActionModeCallback.onCreateActionMode(any(ActionMode.class), any(Menu.class))).
+ thenReturn(Boolean.FALSE);
+ mTextView.setCustomInsertionActionModeCallback(mockActionModeCallback);
+ assertEquals(mockActionModeCallback, mTextView.getCustomInsertionActionModeCallback());
// TODO(Bug: 22033189): Tests the set callback is actually used.
}
- private static class MockActionModeCallback implements ActionMode.Callback {
- private int mCreateCount = 0;
- private int mDestroyCount = 0;
- private final boolean mAllowToStartActionMode;
+ public void testTextShadows() {
+ final TextView textViewWithConfiguredShadow =
+ (TextView) mActivity.findViewById(R.id.textview_with_shadow);
+ assertEquals(1.0f, textViewWithConfiguredShadow.getShadowDx());
+ assertEquals(2.0f, textViewWithConfiguredShadow.getShadowDy());
+ assertEquals(3.0f, textViewWithConfiguredShadow.getShadowRadius());
+ assertEquals(Color.GREEN, textViewWithConfiguredShadow.getShadowColor());
- public MockActionModeCallback(boolean allowToStartActionMode) {
- mAllowToStartActionMode = allowToStartActionMode;
- }
+ final TextView textView = (TextView) mActivity.findViewById(R.id.textview_text);
+ assertEquals(0.0f, textView.getShadowDx());
+ assertEquals(0.0f, textView.getShadowDy());
+ assertEquals(0.0f, textView.getShadowRadius());
- public int getCreateCount() {
- return mCreateCount;
- }
+ mActivity.runOnUiThread(() -> textView.setShadowLayer(5.0f, 3.0f, 4.0f, Color.RED));
+ mInstrumentation.waitForIdleSync();
+ assertEquals(3.0f, textView.getShadowDx());
+ assertEquals(4.0f, textView.getShadowDy());
+ assertEquals(5.0f, textView.getShadowRadius());
+ assertEquals(Color.RED, textView.getShadowColor());
+ }
- public int getDestroyCount() {
- return mDestroyCount;
+ public void testFontFeatureSettings() {
+ final TextView textView = (TextView) mActivity.findViewById(R.id.textview_text);
+ assertTrue(TextUtils.isEmpty(textView.getFontFeatureSettings()));
+
+ mActivity.runOnUiThread(() -> textView.setFontFeatureSettings("smcp"));
+ mInstrumentation.waitForIdleSync();
+ assertEquals("smcp", textView.getFontFeatureSettings());
+
+ mActivity.runOnUiThread(() -> textView.setFontFeatureSettings("frac"));
+ mInstrumentation.waitForIdleSync();
+ assertEquals("frac", textView.getFontFeatureSettings());
+ }
+
+ private static class SoftInputResultReceiver extends ResultReceiver {
+ private boolean mIsDone;
+ private int mResultCode;
+
+ public SoftInputResultReceiver(Handler handler) {
+ super(handler);
}
@Override
- public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
- return false;
+ protected void onReceiveResult(int resultCode, Bundle resultData) {
+ mResultCode = resultCode;
+ mIsDone = true;
}
- @Override
- public void onDestroyActionMode(ActionMode mode) {
- mDestroyCount++;
- }
-
- @Override
- public boolean onCreateActionMode(ActionMode mode, Menu menu) {
- mCreateCount++;
- return mAllowToStartActionMode;
- }
-
- @Override
- public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
- return false;
- }
- };
-
- private static class MockOnEditorActionListener implements OnEditorActionListener {
- private boolean isOnEditorActionCalled;
-
- public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
- isOnEditorActionCalled = true;
- return true;
- }
-
- public boolean isOnEditorActionCalled() {
- return isOnEditorActionCalled;
+ public void reset() {
+ mIsDone = false;
}
}
- private void layout(final TextView textView) {
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- mActivity.setContentView(textView);
- }
+ public void testAccessShowSoftInputOnFocus() {
+ if (!mActivity.getPackageManager().hasSystemFeature(PackageManager.FEATURE_INPUT_METHODS)) {
+ return;
+ }
+
+ // Scroll down to our EditText
+ final ScrollView scrollView = (ScrollView) mActivity.findViewById(R.id.scroller);
+ mTextView = findTextView(R.id.editview_text);
+ mActivity.runOnUiThread(() -> scrollView.fullScroll(View.FOCUS_DOWN));
+ mInstrumentation.waitForIdleSync();
+
+ // Mark it to show soft input on focus
+ mActivity.runOnUiThread(() -> mTextView.setShowSoftInputOnFocus(true));
+ mInstrumentation.waitForIdleSync();
+ assertTrue(mTextView.getShowSoftInputOnFocus());
+
+ // And emulate click on it
+ CtsTouchUtils.emulateTapOnViewCenter(mInstrumentation, mTextView);
+
+ // Verify that input method manager is active and accepting text
+ final InputMethodManager imManager = (InputMethodManager) mActivity
+ .getSystemService(Context.INPUT_METHOD_SERVICE);
+ PollingCheck.waitFor(imManager::isActive);
+ assertTrue(imManager.isAcceptingText());
+ assertTrue(imManager.isActive(mTextView));
+
+ // Since there is no API to check that soft input is showing, we're going to ask
+ // the input method manager to show soft input, passing our custom result receiver.
+ // We're expecting to get UNCHANGED_SHOWN, indicating that the soft input was already
+ // showing before showSoftInput was called.
+ SoftInputResultReceiver receiver = new SoftInputResultReceiver(mHandler);
+ imManager.showSoftInput(mTextView, 0, receiver);
+ PollingCheck.waitFor(() -> receiver.mIsDone);
+ assertEquals(InputMethodManager.RESULT_UNCHANGED_SHOWN, receiver.mResultCode);
+
+ // Close soft input
+ sendKeys(KeyEvent.KEYCODE_BACK);
+
+ // Reconfigure our edit text to not show soft input on focus
+ mActivity.runOnUiThread(() -> mTextView.setShowSoftInputOnFocus(false));
+ mInstrumentation.waitForIdleSync();
+ assertFalse(mTextView.getShowSoftInputOnFocus());
+
+ // Emulate click on it
+ CtsTouchUtils.emulateTapOnViewCenter(mInstrumentation, mTextView);
+
+ // Ask input method manager to show soft input again. This time we're expecting to get
+ // SHOWN, indicating that the soft input was not showing before showSoftInput was called.
+ receiver.reset();
+ imManager.showSoftInput(mTextView, 0, receiver);
+ PollingCheck.waitFor(() -> receiver.mIsDone);
+ assertEquals(InputMethodManager.RESULT_SHOWN, receiver.mResultCode);
+
+ // Close soft input
+ sendKeys(KeyEvent.KEYCODE_BACK);
+ }
+
+ public void testIsSuggestionsEnabled() {
+ mTextView = findTextView(R.id.textview_text);
+
+ // Anything without InputType.TYPE_CLASS_TEXT doesn't have suggestions enabled
+ mInstrumentation.runOnMainSync(() -> mTextView.setInputType(InputType.TYPE_CLASS_DATETIME));
+ assertFalse(mTextView.isSuggestionsEnabled());
+
+ mInstrumentation.runOnMainSync(() -> mTextView.setInputType(InputType.TYPE_CLASS_PHONE));
+ assertFalse(mTextView.isSuggestionsEnabled());
+
+ mInstrumentation.runOnMainSync(() -> mTextView.setInputType(InputType.TYPE_CLASS_NUMBER));
+ assertFalse(mTextView.isSuggestionsEnabled());
+
+ // From this point our text view has InputType.TYPE_CLASS_TEXT
+
+ // Anything with InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS doesn't have suggestions enabled
+ mInstrumentation.runOnMainSync(
+ () -> mTextView.setInputType(
+ InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS));
+ assertFalse(mTextView.isSuggestionsEnabled());
+
+ mInstrumentation.runOnMainSync(
+ () -> mTextView.setInputType(
+ InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_NORMAL |
+ InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS));
+ assertFalse(mTextView.isSuggestionsEnabled());
+
+ mInstrumentation.runOnMainSync(
+ () -> mTextView.setInputType(
+ InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS |
+ InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS));
+ assertFalse(mTextView.isSuggestionsEnabled());
+
+ // Otherwise suggestions are enabled for specific type variations enumerated in the
+ // documentation of TextView.isSuggestionsEnabled
+ mInstrumentation.runOnMainSync(
+ () -> mTextView.setInputType(
+ InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_NORMAL));
+ assertTrue(mTextView.isSuggestionsEnabled());
+
+ mInstrumentation.runOnMainSync(
+ () -> mTextView.setInputType(
+ InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_EMAIL_SUBJECT));
+ assertTrue(mTextView.isSuggestionsEnabled());
+
+ mInstrumentation.runOnMainSync(
+ () -> mTextView.setInputType(
+ InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_LONG_MESSAGE));
+ assertTrue(mTextView.isSuggestionsEnabled());
+
+ mInstrumentation.runOnMainSync(
+ () -> mTextView.setInputType(
+ InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE));
+ assertTrue(mTextView.isSuggestionsEnabled());
+
+ mInstrumentation.runOnMainSync(
+ () -> mTextView.setInputType(
+ InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT));
+ assertTrue(mTextView.isSuggestionsEnabled());
+
+ // and not on any other type variation
+ mInstrumentation.runOnMainSync(
+ () -> mTextView.setInputType(
+ InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS));
+ assertFalse(mTextView.isSuggestionsEnabled());
+
+ mInstrumentation.runOnMainSync(
+ () -> mTextView.setInputType(
+ InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_FILTER));
+ assertFalse(mTextView.isSuggestionsEnabled());
+
+ mInstrumentation.runOnMainSync(
+ () -> mTextView.setInputType(
+ InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD));
+ assertFalse(mTextView.isSuggestionsEnabled());
+
+ mInstrumentation.runOnMainSync(
+ () -> mTextView.setInputType(
+ InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PERSON_NAME));
+ assertFalse(mTextView.isSuggestionsEnabled());
+
+ mInstrumentation.runOnMainSync(
+ () -> mTextView.setInputType(
+ InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PHONETIC));
+ assertFalse(mTextView.isSuggestionsEnabled());
+
+ mInstrumentation.runOnMainSync(
+ () -> mTextView.setInputType(
+ InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_POSTAL_ADDRESS));
+ assertFalse(mTextView.isSuggestionsEnabled());
+
+ mInstrumentation.runOnMainSync(
+ () -> mTextView.setInputType(
+ InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_URI));
+ assertFalse(mTextView.isSuggestionsEnabled());
+
+ mInstrumentation.runOnMainSync(
+ () -> mTextView.setInputType(
+ InputType.TYPE_CLASS_TEXT |
+ InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD));
+ assertFalse(mTextView.isSuggestionsEnabled());
+
+ mInstrumentation.runOnMainSync(
+ () -> mTextView.setInputType(
+ InputType.TYPE_CLASS_TEXT |
+ InputType.TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS));
+ assertFalse(mTextView.isSuggestionsEnabled());
+
+ mInstrumentation.runOnMainSync(
+ () -> mTextView.setInputType(
+ InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_WEB_PASSWORD));
+ assertFalse(mTextView.isSuggestionsEnabled());
+ }
+
+ public void testAccessLetterSpacing() {
+ mTextView = findTextView(R.id.textview_text);
+ assertEquals(0.0f, mTextView.getLetterSpacing());
+
+ final CharSequence text = mTextView.getText();
+ final int textLength = text.length();
+
+ // Get advance widths of each character at the default letter spacing
+ final float[] initialWidths = new float[textLength];
+ mTextView.getPaint().getTextWidths(text.toString(), initialWidths);
+
+ // Get advance widths of each character at letter spacing = 1.0f
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mTextView,
+ () -> mTextView.setLetterSpacing(1.0f));
+ assertEquals(1.0f, mTextView.getLetterSpacing());
+ final float[] singleWidths = new float[textLength];
+ mTextView.getPaint().getTextWidths(text.toString(), singleWidths);
+
+ // Get advance widths of each character at letter spacing = 2.0f
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mTextView,
+ () -> mTextView.setLetterSpacing(2.0f));
+ assertEquals(2.0f, mTextView.getLetterSpacing());
+ final float[] doubleWidths = new float[textLength];
+ mTextView.getPaint().getTextWidths(text.toString(), doubleWidths);
+
+ // Since letter spacing setter treats the parameter as EM units, and we don't have
+ // a way to convert EMs into pixels, go over the three arrays of advance widths and
+ // test that the extra advance width at letter spacing 2.0f is double the extra
+ // advance width at letter spacing 1.0f.
+ for (int i = 0; i < textLength; i++) {
+ float singleWidthDelta = singleWidths[i] - initialWidths[i];
+ float doubleWidthDelta = doubleWidths[i] - initialWidths[i];
+ assertEquals("At index " + i + " initial is " + initialWidths[i] +
+ ", single is " + singleWidths[i] + " and double is " + doubleWidths[i],
+ singleWidthDelta * 2.0f, doubleWidthDelta, 0.05f);
+ }
+ }
+
+ public void testTextIsSelectableFocusAndOnClick() {
+ // Prepare a focusable TextView with an onClickListener attached.
+ final View.OnClickListener mockOnClickListener = mock(View.OnClickListener.class);
+ mActivity.runOnUiThread(() -> {
+ mTextView = new TextView(mActivity);
+ mTextView.setText("...text 11:11. some more text is in here...");
+ mTextView.setFocusable(true);
+ mTextView.setOnClickListener(mockOnClickListener);
+ mActivity.setContentView(mTextView);
});
mInstrumentation.waitForIdleSync();
+ assertTrue(mTextView.isFocusable());
+ assertFalse(mTextView.isTextSelectable());
+ assertFalse(mTextView.isFocusableInTouchMode());
+ assertFalse(mTextView.isFocused());
+ assertFalse(mTextView.isInTouchMode());
+
+ // First tap on the view triggers onClick() but does not focus the TextView.
+ CtsTouchUtils.emulateTapOnViewCenter(mInstrumentation, mTextView);
+ assertTrue(mTextView.isInTouchMode());
+ assertFalse(mTextView.isFocused());
+ verify(mockOnClickListener, times(1)).onClick(mTextView);
+ // So does the second tap.
+ reset(mockOnClickListener);
+ CtsTouchUtils.emulateTapOnViewCenter(mInstrumentation, mTextView);
+ assertTrue(mTextView.isInTouchMode());
+ assertFalse(mTextView.isFocused());
+ verify(mockOnClickListener, times(1)).onClick(mTextView);
+
+ mActivity.runOnUiThread(() -> mTextView.setTextIsSelectable(true));
+ mInstrumentation.waitForIdleSync();
+ assertTrue(mTextView.isFocusable());
+ assertTrue(mTextView.isTextSelectable());
+ assertTrue(mTextView.isFocusableInTouchMode());
+ assertFalse(mTextView.isFocused());
+
+ // First tap on the view focuses the TextView but does not trigger onClick().
+ reset(mockOnClickListener);
+ CtsTouchUtils.emulateTapOnViewCenter(mInstrumentation, mTextView);
+ assertTrue(mTextView.isInTouchMode());
+ assertTrue(mTextView.isFocused());
+ verify(mockOnClickListener, never()).onClick(mTextView);
+ // The second tap triggers onClick() and keeps the focus.
+ reset(mockOnClickListener);
+ CtsTouchUtils.emulateTapOnViewCenter(mInstrumentation, mTextView);
+ assertTrue(mTextView.isInTouchMode());
+ assertTrue(mTextView.isFocused());
+ verify(mockOnClickListener, times(1)).onClick(mTextView);
+ }
+
+ private void verifyGetOffsetForPosition(final int x, final int y) {
+ final int actual = mTextView.getOffsetForPosition(x, y);
+
+ final Layout layout = mTextView.getLayout();
+ if (layout == null) {
+ assertEquals("For [" + x + ", " + y + "]", -1, actual);
+ return;
+ }
+
+ // Get the line which corresponds to the Y position
+ final int line = layout.getLineForVertical(y + mTextView.getScrollY());
+ // Get the offset in that line that corresponds to the X position
+ final int expected = layout.getOffsetForHorizontal(line, x + mTextView.getScrollX());
+ assertEquals("For [" + x + ", " + y + "]", expected, actual);
+ }
+
+ public void testGetOffsetForPosition() {
+ mTextView = findTextView(R.id.textview_text);
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mTextView, () -> {
+ mTextView.setText(LONG_TEXT);
+ mTextView.setPadding(0, 0, 0, 0);
+ });
+
+ assertNotNull(mTextView.getLayout());
+ final int viewWidth = mTextView.getWidth();
+ final int viewHeight = mTextView.getHeight();
+ final int lineHeight = mTextView.getLineHeight();
+
+ verifyGetOffsetForPosition(0, 0);
+ verifyGetOffsetForPosition(0, viewHeight / 2);
+ verifyGetOffsetForPosition(viewWidth / 3, lineHeight / 2);
+ verifyGetOffsetForPosition(viewWidth / 2, viewHeight / 2);
+ verifyGetOffsetForPosition(viewWidth, viewHeight);
+ }
+
+ @UiThreadTest
+ public void testOnResolvePointerIcon() throws InterruptedException {
+ final TextView selectableTextView = findTextView(R.id.textview_pointer);
+ final MotionEvent event = createMouseHoverEvent(selectableTextView);
+
+ // A selectable view shows the I beam
+ selectableTextView.setTextIsSelectable(true);
+
+ assertEquals(PointerIcon.getSystemIcon(mActivity, PointerIcon.TYPE_TEXT),
+ selectableTextView.onResolvePointerIcon(event, 0));
+ selectableTextView.setTextIsSelectable(false);
+
+ // A clickable view shows the hand
+ selectableTextView.setLinksClickable(true);
+ SpannableString builder = new SpannableString("hello world");
+ selectableTextView.setText(builder, BufferType.SPANNABLE);
+ Spannable text = (Spannable) selectableTextView.getText();
+ text.setSpan(
+ new ClickableSpan() {
+ @Override
+ public void onClick(View widget) {
+
+ }
+ }, 0, text.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+
+ assertEquals(PointerIcon.getSystemIcon(mActivity, PointerIcon.TYPE_HAND),
+ selectableTextView.onResolvePointerIcon(event, 0));
+
+ // A selectable & clickable view shows hand
+ selectableTextView.setTextIsSelectable(true);
+
+ assertEquals(PointerIcon.getSystemIcon(mActivity, PointerIcon.TYPE_HAND),
+ selectableTextView.onResolvePointerIcon(event, 0));
+
+ // An editable view shows the I-beam
+ final TextView editableTextView = new EditText(mActivity);
+
+ assertEquals(PointerIcon.getSystemIcon(mActivity, PointerIcon.TYPE_TEXT),
+ editableTextView.onResolvePointerIcon(event, 0));
+ }
+
+ private MotionEvent createMouseHoverEvent(View view) {
+ final int[] xy = new int[2];
+ view.getLocationOnScreen(xy);
+ final int viewWidth = view.getWidth();
+ final int viewHeight = view.getHeight();
+ float x = xy[0] + viewWidth / 2.0f;
+ float y = xy[1] + viewHeight / 2.0f;
+ long eventTime = SystemClock.uptimeMillis();
+ MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[1];
+ pointerCoords[0] = new MotionEvent.PointerCoords();
+ pointerCoords[0].x = x;
+ pointerCoords[0].y = y;
+ final int[] pointerIds = new int[1];
+ pointerIds[0] = 0;
+ return MotionEvent.obtain(0, eventTime, MotionEvent.ACTION_HOVER_MOVE, 1, pointerIds,
+ pointerCoords, 0, 0, 0, 0, 0, InputDevice.SOURCE_MOUSE, 0);
+ }
+
+ private void layout(final TextView textView) {
+ mActivity.runOnUiThread(() -> mActivity.setContentView(textView));
+ mInstrumentation.waitForIdleSync();
}
private void layout(final int layoutId) {
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- mActivity.setContentView(layoutId);
- }
- });
+ mActivity.runOnUiThread(() -> mActivity.setContentView(layoutId));
mInstrumentation.waitForIdleSync();
}
@@ -5227,56 +5920,33 @@
return findTextView(id).getAutoLinkMask();
}
- private Bitmap getBitmap(int resid) {
- return ((BitmapDrawable) getDrawable(resid)).getBitmap();
- }
-
- private Drawable getDrawable(int resid) {
- return mActivity.getResources().getDrawable(resid);
+ private void setMaxLines(final int lines) {
+ mActivity.runOnUiThread(() -> mTextView.setMaxLines(lines));
+ mInstrumentation.waitForIdleSync();
}
private void setMaxWidth(final int pixels) {
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- mTextView.setMaxWidth(pixels);
- }
- });
+ mActivity.runOnUiThread(() -> mTextView.setMaxWidth(pixels));
mInstrumentation.waitForIdleSync();
}
private void setMinWidth(final int pixels) {
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- mTextView.setMinWidth(pixels);
- }
- });
+ mActivity.runOnUiThread(() -> mTextView.setMinWidth(pixels));
mInstrumentation.waitForIdleSync();
}
private void setMaxHeight(final int pixels) {
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- mTextView.setMaxHeight(pixels);
- }
- });
+ mActivity.runOnUiThread(() -> mTextView.setMaxHeight(pixels));
mInstrumentation.waitForIdleSync();
}
private void setMinHeight(final int pixels) {
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- mTextView.setMinHeight(pixels);
- }
- });
+ mActivity.runOnUiThread(() -> mTextView.setMinHeight(pixels));
mInstrumentation.waitForIdleSync();
}
private void setMinLines(final int minlines) {
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- mTextView.setMinLines(minlines);
- }
- });
+ mActivity.runOnUiThread(() -> mTextView.setMinLines(minlines));
mInstrumentation.waitForIdleSync();
}
@@ -5288,83 +5958,47 @@
* @param content the content
*/
private void setSpannableText(final TextView tv, final String content) {
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- tv.setText(content, BufferType.SPANNABLE);
- }
- });
+ mActivity.runOnUiThread(() -> tv.setText(content, BufferType.SPANNABLE));
mInstrumentation.waitForIdleSync();
}
private void setLines(final int lines) {
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- mTextView.setLines(lines);
- }
- });
+ mActivity.runOnUiThread(() -> mTextView.setLines(lines));
mInstrumentation.waitForIdleSync();
}
private void setHorizontallyScrolling(final boolean whether) {
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- mTextView.setHorizontallyScrolling(whether);
- }
- });
+ mActivity.runOnUiThread(() -> mTextView.setHorizontallyScrolling(whether));
mInstrumentation.waitForIdleSync();
}
private void setWidth(final int pixels) {
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- mTextView.setWidth(pixels);
- }
- });
+ mActivity.runOnUiThread(() -> mTextView.setWidth(pixels));
mInstrumentation.waitForIdleSync();
}
private void setHeight(final int pixels) {
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- mTextView.setHeight(pixels);
- }
- });
+ mActivity.runOnUiThread(() -> mTextView.setHeight(pixels));
mInstrumentation.waitForIdleSync();
}
private void setMinEms(final int ems) {
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- mTextView.setMinEms(ems);
- }
- });
+ mActivity.runOnUiThread(() -> mTextView.setMinEms(ems));
mInstrumentation.waitForIdleSync();
}
private void setMaxEms(final int ems) {
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- mTextView.setMaxEms(ems);
- }
- });
+ mActivity.runOnUiThread(() -> mTextView.setMaxEms(ems));
mInstrumentation.waitForIdleSync();
}
private void setEms(final int ems) {
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- mTextView.setEms(ems);
- }
- });
+ mActivity.runOnUiThread(() -> mTextView.setEms(ems));
mInstrumentation.waitForIdleSync();
}
private void setLineSpacing(final float add, final float mult) {
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- mTextView.setLineSpacing(add, mult);
- }
- });
+ mActivity.runOnUiThread(() -> mTextView.setLineSpacing(add, mult));
mInstrumentation.waitForIdleSync();
}
@@ -5411,56 +6045,6 @@
}
}
- private class MockEditableFactory extends Editable.Factory {
- private boolean mhasCalledNewEditable;
- private CharSequence mSource;
-
- public boolean hasCalledNewEditable() {
- return mhasCalledNewEditable;
- }
-
- public void reset() {
- mhasCalledNewEditable = false;
- mSource = null;
- }
-
- public CharSequence getSource() {
- return mSource;
- }
-
- @Override
- public Editable newEditable(CharSequence source) {
- mhasCalledNewEditable = true;
- mSource = source;
- return super.newEditable(source);
- }
- }
-
- private class MockSpannableFactory extends Spannable.Factory {
- private boolean mHasCalledNewSpannable;
- private CharSequence mSource;
-
- public boolean hasCalledNewSpannable() {
- return mHasCalledNewSpannable;
- }
-
- public void reset() {
- mHasCalledNewSpannable = false;
- mSource = null;
- }
-
- public CharSequence getSource() {
- return mSource;
- }
-
- @Override
- public Spannable newSpannable(CharSequence source) {
- mHasCalledNewSpannable = true;
- mSource = source;
- return super.newSpannable(source);
- }
- }
-
private static class MockTextWatcher implements TextWatcher {
private boolean mHasCalledAfterTextChanged;
private boolean mHasCalledBeforeTextChanged;
@@ -5498,70 +6082,6 @@
}
/**
- * The listener interface for receiving mockOnLongClick events. The class
- * that is interested in processing a mockOnLongClick event implements this
- * interface, and the object created with that class is registered with a
- * component using the component's
- * <code>addMockOnLongClickListener<code> method. When
- * the mockOnLongClick event occurs, that object's appropriate
- * method is invoked.
- *
- * @see MockOnLongClickEvent
- */
- private static class MockOnLongClickListener implements OnLongClickListener {
- private boolean mExpectedOnLongClickResult;
- private boolean mHasLongClicked;
-
- MockOnLongClickListener(boolean result) {
- mExpectedOnLongClickResult = result;
- }
-
- public boolean hasLongClicked() {
- return mHasLongClicked;
- }
-
- public boolean onLongClick(View v) {
- mHasLongClicked = true;
- return mExpectedOnLongClickResult;
- }
- }
-
- /**
- * The listener interface for receiving mockOnCreateContextMenu events. The
- * class that is interested in processing a mockOnCreateContextMenu event
- * implements this interface, and the object created with that class is
- * registered with a component using the component's
- * <code>addMockOnCreateContextMenuListener<code> method. When the
- * mockOnCreateContextMenu event occurs, that object's appropriate method is
- * invoked.
- *
- * @see MockOnCreateContextMenuEvent
- */
- private static class MockOnCreateContextMenuListener implements OnCreateContextMenuListener {
- private boolean mIsMenuItemsBlank;
- private boolean mHasCreatedContextMenu;
-
- MockOnCreateContextMenuListener(boolean isBlank) {
- this.mIsMenuItemsBlank = isBlank;
- }
-
- public boolean hasCreatedContextMenu() {
- return mHasCreatedContextMenu;
- }
-
- public void reset() {
- mHasCreatedContextMenu = false;
- }
-
- public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
- mHasCreatedContextMenu = true;
- if (!mIsMenuItemsBlank) {
- menu.add("menu item");
- }
- }
- }
-
- /**
* A TextWatcher that converts the text to spaces whenever the text changes.
*/
private static class ConvertToSpacesTextWatcher implements TextWatcher {
diff --git a/hostsidetests/services/activitymanager/app/src/android/server/app/SlowCreateActivity.java b/tests/tests/widget/src/android/widget/cts/TimePickerCtsActivity.java
similarity index 68%
copy from hostsidetests/services/activitymanager/app/src/android/server/app/SlowCreateActivity.java
copy to tests/tests/widget/src/android/widget/cts/TimePickerCtsActivity.java
index 481f5be..34fb8c0 100644
--- a/hostsidetests/services/activitymanager/app/src/android/server/app/SlowCreateActivity.java
+++ b/tests/tests/widget/src/android/widget/cts/TimePickerCtsActivity.java
@@ -14,17 +14,22 @@
* limitations under the License.
*/
-package android.server.app;
+package android.widget.cts;
import android.app.Activity;
import android.os.Bundle;
+import android.widget.TimePicker;
-public class SlowCreateActivity extends Activity {
+/**
+ * A minimal application for {@link TimePicker} test.
+ */
+public class TimePickerCtsActivity extends Activity {
+ /**
+ * Called when the activity is first created.
+ */
@Override
- protected void onCreate(Bundle savedInstanceState) {
- try {
- Thread.sleep(2000);
- } catch(InterruptedException e) {}
+ public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ setContentView(R.layout.timepicker);
}
}
diff --git a/tests/tests/widget/src/android/widget/cts/TimePickerTest.java b/tests/tests/widget/src/android/widget/cts/TimePickerTest.java
index 39554bb..43f63be 100644
--- a/tests/tests/widget/src/android/widget/cts/TimePickerTest.java
+++ b/tests/tests/widget/src/android/widget/cts/TimePickerTest.java
@@ -16,46 +16,54 @@
package android.widget.cts;
+import static org.mockito.Mockito.*;
import android.app.Activity;
import android.app.Instrumentation;
import android.content.Context;
+import android.cts.util.CtsKeyEventUtil;
+import android.cts.util.CtsTouchUtils;
import android.os.Parcelable;
import android.test.ActivityInstrumentationTestCase2;
+import android.test.UiThreadTest;
+import android.test.suitebuilder.annotation.MediumTest;
import android.util.AttributeSet;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.ViewGroup;
import android.widget.TimePicker;
-import android.widget.TimePicker.OnTimeChangedListener;
+
+import java.util.ArrayList;
+import java.util.Collections;
/**
* Test {@link TimePicker}.
*/
-public class TimePickerTest extends ActivityInstrumentationTestCase2<CtsActivity> {
+@MediumTest
+public class TimePickerTest extends ActivityInstrumentationTestCase2<TimePickerCtsActivity> {
private TimePicker mTimePicker;
-
private Activity mActivity;
-
- private Context mContext;
-
private Instrumentation mInstrumentation;
public TimePickerTest() {
- super("android.widget.cts", CtsActivity.class);
+ super("android.widget.cts", TimePickerCtsActivity.class);
}
@Override
protected void setUp() throws Exception {
super.setUp();
+
mInstrumentation = getInstrumentation();
- mContext = mInstrumentation.getTargetContext();
mActivity = getActivity();
+ mTimePicker = (TimePicker) mActivity.findViewById(R.id.timepicker_clock);
}
public void testConstructors() {
AttributeSet attrs =
- mContext.getResources().getLayout(android.widget.cts.R.layout.timepicker);
+ mActivity.getResources().getLayout(android.widget.cts.R.layout.timepicker);
assertNotNull(attrs);
- new TimePicker(mContext);
+ new TimePicker(mActivity);
try {
new TimePicker(null);
fail("did not throw NullPointerException when param context is null.");
@@ -63,29 +71,31 @@
// expected
}
- new TimePicker(mContext, attrs);
+ new TimePicker(mActivity, attrs);
try {
new TimePicker(null, attrs);
fail("did not throw NullPointerException when param context is null.");
} catch (NullPointerException e) {
// expected
}
- new TimePicker(mContext, null);
+ new TimePicker(mActivity, null);
- new TimePicker(mContext, attrs, 0);
+ new TimePicker(mActivity, attrs, 0);
try {
new TimePicker(null, attrs, 0);
fail("did not throw NullPointerException when param context is null.");
} catch (NullPointerException e) {
// expected
}
- new TimePicker(mContext, null, 0);
- new TimePicker(mContext, attrs, 0);
- new TimePicker(mContext, attrs, Integer.MIN_VALUE);
+ new TimePicker(mActivity, null, 0);
+ new TimePicker(mActivity, attrs, 0);
+ new TimePicker(mActivity, null, android.R.attr.timePickerStyle);
+ new TimePicker(mActivity, null, 0, android.R.style.Widget_Material_TimePicker);
+ new TimePicker(mActivity, null, 0, android.R.style.Widget_Material_Light_TimePicker);
}
+ @UiThreadTest
public void testSetEnabled() {
- mTimePicker = new TimePicker(mContext);
assertTrue(mTimePicker.isEnabled());
mTimePicker.setEnabled(false);
@@ -96,49 +106,66 @@
}
public void testSetOnTimeChangedListener() {
- int initialHour = 13;
- int initialMinute = 50;
- mTimePicker = new TimePicker(mContext);
+ // On time change listener is notified on every call to setCurrentHour / setCurrentMinute.
+ // We want to make sure that before we register our listener, we initialize the time picker
+ // to the time that is explicitly different from the values we'll be testing for in both
+ // hour and minute. Otherwise if the test happens to run at the time that ends in
+ // "minuteForTesting" minutes, we'll get two onTimeChanged callbacks with identical values.
+ final int initialHour = 10;
+ final int initialMinute = 38;
+ final int hourForTesting = 13;
+ final int minuteForTesting = 50;
- MockOnTimeChangeListener listener = new MockOnTimeChangeListener();
- mTimePicker.setOnTimeChangedListener(listener);
- mTimePicker.setCurrentHour(Integer.valueOf(initialHour));
- mTimePicker.setCurrentMinute(Integer.valueOf(initialMinute));
- assertEquals(initialHour, listener.getNotifiedHourOfDay());
- assertEquals(initialMinute, listener.getNotifiedMinute());
+ mInstrumentation.runOnMainSync(() -> {
+ mTimePicker.setHour(initialHour);
+ mTimePicker.setMinute(initialMinute);
+ });
+
+ // Now register the listener
+ TimePicker.OnTimeChangedListener mockOnTimeChangeListener =
+ mock(TimePicker.OnTimeChangedListener.class);
+ mTimePicker.setOnTimeChangedListener(mockOnTimeChangeListener);
+ mInstrumentation.runOnMainSync(() -> {
+ mTimePicker.setCurrentHour(Integer.valueOf(hourForTesting));
+ mTimePicker.setCurrentMinute(Integer.valueOf(minuteForTesting));
+ });
+ // We're expecting two onTimeChanged callbacks, one with new hour and one with new
+ // hour+minute
+ verify(mockOnTimeChangeListener, times(1)).onTimeChanged(
+ mTimePicker, hourForTesting, initialMinute);
+ verify(mockOnTimeChangeListener, times(1)).onTimeChanged(
+ mTimePicker, hourForTesting, minuteForTesting);
// set the same hour as current
- listener.reset();
- mTimePicker.setCurrentHour(Integer.valueOf(initialHour));
- assertFalse(listener.hasCalledOnTimeChanged());
+ reset(mockOnTimeChangeListener);
+ mInstrumentation.runOnMainSync(
+ () -> mTimePicker.setCurrentHour(Integer.valueOf(hourForTesting)));
+ verifyZeroInteractions(mockOnTimeChangeListener);
- mTimePicker.setCurrentHour(Integer.valueOf(initialHour + 1));
- assertTrue(listener.hasCalledOnTimeChanged());
- assertEquals(initialHour + 1, listener.getNotifiedHourOfDay());
- assertEquals(initialMinute, listener.getNotifiedMinute());
- assertSame(mTimePicker, listener.getNotifiedView());
+ mInstrumentation.runOnMainSync(
+ () -> mTimePicker.setCurrentHour(Integer.valueOf(hourForTesting + 1)));
+ verify(mockOnTimeChangeListener, times(1)).onTimeChanged(
+ mTimePicker, hourForTesting + 1, minuteForTesting);
// set the same minute as current
- listener.reset();
- mTimePicker.setCurrentMinute(initialMinute);
- assertFalse(listener.hasCalledOnTimeChanged());
+ reset(mockOnTimeChangeListener);
+ mInstrumentation.runOnMainSync(() -> mTimePicker.setCurrentMinute(minuteForTesting));
+ verifyZeroInteractions(mockOnTimeChangeListener);
- listener.reset();
- mTimePicker.setCurrentMinute(initialMinute + 1);
- assertTrue(listener.hasCalledOnTimeChanged());
- assertEquals(initialHour + 1, listener.getNotifiedHourOfDay());
- assertEquals(initialMinute + 1, listener.getNotifiedMinute());
- assertSame(mTimePicker, listener.getNotifiedView());
+ reset(mockOnTimeChangeListener);
+ mInstrumentation.runOnMainSync(() -> mTimePicker.setCurrentMinute(minuteForTesting + 1));
+ verify(mockOnTimeChangeListener, times(1)).onTimeChanged(
+ mTimePicker, hourForTesting + 1, minuteForTesting + 1);
// change time picker mode
- listener.reset();
- mTimePicker.setIs24HourView( !mTimePicker.is24HourView() );
- assertFalse(listener.hasCalledOnTimeChanged());
+ reset(mockOnTimeChangeListener);
+ mInstrumentation.runOnMainSync(
+ () -> mTimePicker.setIs24HourView(!mTimePicker.is24HourView()));
+ verifyZeroInteractions(mockOnTimeChangeListener);
}
+ @UiThreadTest
public void testAccessCurrentHour() {
- mTimePicker = new TimePicker(mContext);
-
// AM/PM mode
mTimePicker.setIs24HourView(false);
@@ -167,9 +194,8 @@
assertEquals(Integer.valueOf(23), mTimePicker.getCurrentHour());
}
+ @UiThreadTest
public void testAccessHour() {
- mTimePicker = new TimePicker(mContext);
-
// AM/PM mode
mTimePicker.setIs24HourView(false);
@@ -198,8 +224,8 @@
assertEquals(23, mTimePicker.getHour());
}
+ @UiThreadTest
public void testAccessIs24HourView() {
- mTimePicker = new TimePicker(mContext);
assertFalse(mTimePicker.is24HourView());
mTimePicker.setIs24HourView(true);
@@ -209,9 +235,8 @@
assertFalse(mTimePicker.is24HourView());
}
+ @UiThreadTest
public void testAccessCurrentMinute() {
- mTimePicker = new TimePicker(mContext);
-
mTimePicker.setCurrentMinute(0);
assertEquals(Integer.valueOf(0), mTimePicker.getCurrentMinute());
@@ -225,9 +250,8 @@
assertEquals(Integer.valueOf(59), mTimePicker.getCurrentMinute());
}
+ @UiThreadTest
public void testAccessMinute() {
- mTimePicker = new TimePicker(mContext);
-
mTimePicker.setMinute(0);
assertEquals(0, mTimePicker.getMinute());
@@ -242,13 +266,12 @@
}
public void testGetBaseline() {
- mTimePicker = new TimePicker(mContext);
assertEquals(-1, mTimePicker.getBaseline());
}
public void testOnSaveInstanceStateAndOnRestoreInstanceState() {
- MyTimePicker source = new MyTimePicker(mContext);
- MyTimePicker dest = new MyTimePicker(mContext);
+ MyTimePicker source = new MyTimePicker(mActivity);
+ MyTimePicker dest = new MyTimePicker(mActivity);
int expectHour = (dest.getCurrentHour() + 10) % 24;
int expectMinute = (dest.getCurrentMinute() + 10) % 60;
source.setCurrentHour(expectHour);
@@ -261,43 +284,283 @@
assertEquals(Integer.valueOf(expectMinute), dest.getCurrentMinute());
}
- private class MockOnTimeChangeListener implements OnTimeChangedListener {
- private TimePicker mNotifiedView;
+ public void testKeyboardTabTraversal_modeClock() {
+ mTimePicker = (TimePicker) mActivity.findViewById(R.id.timepicker_clock);
- private boolean mHasCalledOnTimeChanged;
+ mInstrumentation.runOnMainSync(() -> mTimePicker.setIs24HourView(false));
+ mInstrumentation.waitForIdleSync();
+ verifyTimePickerKeyboardTraversal(
+ true /* goForward */,
+ false /* is24HourView */,
+ false /* isSpinner */);
+ verifyTimePickerKeyboardTraversal(
+ false /* goForward */,
+ false /* is24HourView */,
+ false /* isSpinner */);
- private int mNotifiedHourOfDay;
+ mInstrumentation.runOnMainSync(() -> mTimePicker.setIs24HourView(true));
+ mInstrumentation.waitForIdleSync();
+ verifyTimePickerKeyboardTraversal(
+ true /* goForward */,
+ true /* is24HourView */,
+ false /* isSpinner */);
+ verifyTimePickerKeyboardTraversal(
+ false /* goForward */,
+ true /* is24HourView */,
+ false /* isSpinner */);
+ }
- private int mNotifiedMinute;;
+ public void testKeyboardTabTraversal_modeSpinner() {
+ mTimePicker = (TimePicker) mActivity.findViewById(R.id.timepicker_spinner);
- public boolean hasCalledOnTimeChanged() {
- return mHasCalledOnTimeChanged;
+ mInstrumentation.runOnMainSync(() -> mTimePicker.setIs24HourView(false));
+ mInstrumentation.waitForIdleSync();
+ verifyTimePickerKeyboardTraversal(
+ true /* goForward */,
+ false /* is24HourView */,
+ true /* isSpinner */);
+ verifyTimePickerKeyboardTraversal(
+ false /* goForward */,
+ false /* is24HourView */,
+ true /* isSpinner */);
+
+ mInstrumentation.runOnMainSync(() -> mTimePicker.setIs24HourView(true));
+ mInstrumentation.waitForIdleSync();
+ verifyTimePickerKeyboardTraversal(
+ true /* goForward */,
+ true /* is24HourView */,
+ true /* isSpinner */);
+ verifyTimePickerKeyboardTraversal(
+ false /* goForward */,
+ true /* is24HourView */,
+ true /* isSpinner */);
+ }
+
+ public void testKeyboardInputModeClockAmPm() {
+ final int initialHour = 6;
+ final int initialMinute = 59;
+ prepareForKeyboardInput(initialHour, initialMinute, false);
+
+ // Input valid hour.
+ assertEquals(initialHour, mTimePicker.getHour());
+ CtsTouchUtils.emulateTapOnViewCenter(mInstrumentation, mTimePicker.getHourView());
+ CtsKeyEventUtil.sendKeyDownUp(mInstrumentation, mTimePicker, KeyEvent.KEYCODE_1);
+ CtsKeyEventUtil.sendKeyDownUp(mInstrumentation, mTimePicker, KeyEvent.KEYCODE_0);
+ assertEquals(10, mTimePicker.getHour());
+ assertTrue(mTimePicker.getMinuteView().hasFocus());
+
+ // Input valid minute.
+ assertEquals(initialMinute, mTimePicker.getMinute());
+ CtsKeyEventUtil.sendKeyDownUp(mInstrumentation, mTimePicker, KeyEvent.KEYCODE_4);
+ CtsKeyEventUtil.sendKeyDownUp(mInstrumentation, mTimePicker, KeyEvent.KEYCODE_3);
+ CtsKeyEventUtil.sendKeyDownUp(mInstrumentation, mTimePicker, KeyEvent.KEYCODE_TAB);
+ assertEquals(43, mTimePicker.getMinute());
+ assertTrue(mTimePicker.getAmView().hasFocus());
+
+ // Accepting AM changes nothing.
+ CtsKeyEventUtil.sendKeyDownUp(mInstrumentation, mTimePicker, KeyEvent.KEYCODE_ENTER);
+ assertEquals(10, mTimePicker.getHour());
+ assertEquals(43, mTimePicker.getMinute());
+
+ // Focus PM radio.
+ CtsKeyEventUtil.sendKeyDownUp(mInstrumentation, mTimePicker, KeyEvent.KEYCODE_TAB);
+ assertTrue(mTimePicker.getPmView().hasFocus());
+ // Still nothing has changed.
+ assertEquals(10, mTimePicker.getHour());
+ assertEquals(43, mTimePicker.getMinute());
+ // Select PM and verify the hour has changed.
+ CtsKeyEventUtil.sendKeyDownUp(mInstrumentation, mTimePicker, KeyEvent.KEYCODE_ENTER);
+ assertEquals(22, mTimePicker.getHour());
+ assertEquals(43, mTimePicker.getMinute());
+ // Set AM again.
+ CtsKeyEventUtil.sendKeyWhileHoldingModifier(mInstrumentation, mTimePicker,
+ KeyEvent.KEYCODE_TAB, KeyEvent.KEYCODE_SHIFT_LEFT);
+ assertTrue(mTimePicker.getAmView().hasFocus());
+ CtsKeyEventUtil.sendKeyDownUp(mInstrumentation, mTimePicker, KeyEvent.KEYCODE_ENTER);
+ assertEquals(10, mTimePicker.getHour());
+
+ // Re-focus the hour view.
+ CtsKeyEventUtil.sendKeyDownUp(mInstrumentation, mTimePicker, KeyEvent.KEYCODE_TAB);
+ CtsKeyEventUtil.sendKeyDownUp(mInstrumentation, mTimePicker, KeyEvent.KEYCODE_TAB);
+ assertTrue(mTimePicker.getHourView().hasFocus());
+
+ // Input an invalid value (larger than 12).
+ CtsKeyEventUtil.sendKeyDownUp(mInstrumentation, mTimePicker, KeyEvent.KEYCODE_1);
+ CtsKeyEventUtil.sendKeyDownUp(mInstrumentation, mTimePicker, KeyEvent.KEYCODE_3);
+ // Force setting the hour by moving to minute.
+ CtsKeyEventUtil.sendKeyDownUp(mInstrumentation, mTimePicker, KeyEvent.KEYCODE_TAB);
+ // After sending 1 and 3 only 1 is accepted.
+ assertEquals(1, mTimePicker.getHour());
+ assertEquals(43, mTimePicker.getMinute());
+ CtsKeyEventUtil.sendKeyWhileHoldingModifier(mInstrumentation, mTimePicker,
+ KeyEvent.KEYCODE_TAB, KeyEvent.KEYCODE_SHIFT_LEFT);
+ // The hour view still has focus.
+ assertTrue(mTimePicker.getHourView().hasFocus());
+
+ // This time send a valid hour (11).
+ CtsKeyEventUtil.sendKeyDownUp(mInstrumentation, mTimePicker, KeyEvent.KEYCODE_1);
+ CtsKeyEventUtil.sendKeyDownUp(mInstrumentation, mTimePicker, KeyEvent.KEYCODE_1);
+ // The value is valid.
+ assertEquals(11, mTimePicker.getHour());
+ assertEquals(43, mTimePicker.getMinute());
+
+ verifyModeClockMinuteInput();
+ }
+
+ public void testKeyboardInputModeClock24H() {
+ final int initialHour = 6;
+ final int initialMinute = 59;
+ prepareForKeyboardInput(initialHour, initialMinute, true);
+
+ // Input valid hour.
+ assertEquals(initialHour, mTimePicker.getHour());
+ CtsTouchUtils.emulateTapOnViewCenter(mInstrumentation, mTimePicker.getHourView());
+ CtsKeyEventUtil.sendKeyDownUp(mInstrumentation, mTimePicker, KeyEvent.KEYCODE_1);
+ CtsKeyEventUtil.sendKeyDownUp(mInstrumentation, mTimePicker, KeyEvent.KEYCODE_0);
+ assertEquals(10, mTimePicker.getHour());
+ assertTrue(mTimePicker.getMinuteView().hasFocus());
+
+ // Input valid minute.
+ assertEquals(initialMinute, mTimePicker.getMinute());
+ CtsKeyEventUtil.sendKeyDownUp(mInstrumentation, mTimePicker, KeyEvent.KEYCODE_4);
+ CtsKeyEventUtil.sendKeyDownUp(mInstrumentation, mTimePicker, KeyEvent.KEYCODE_3);
+ assertEquals(43, mTimePicker.getMinute());
+
+ // Re-focus the hour view.
+ CtsKeyEventUtil.sendKeyWhileHoldingModifier(mInstrumentation, mTimePicker,
+ KeyEvent.KEYCODE_TAB, KeyEvent.KEYCODE_SHIFT_LEFT);
+ assertTrue(mTimePicker.getHourView().hasFocus());
+
+ // Input an invalid value (larger than 24).
+ CtsKeyEventUtil.sendKeyDownUp(mInstrumentation, mTimePicker, KeyEvent.KEYCODE_2);
+ CtsKeyEventUtil.sendKeyDownUp(mInstrumentation, mTimePicker, KeyEvent.KEYCODE_5);
+ // Force setting the hour by moving to minute.
+ CtsKeyEventUtil.sendKeyDownUp(mInstrumentation, mTimePicker, KeyEvent.KEYCODE_TAB);
+ // After sending 2 and 5 only 2 is accepted.
+ assertEquals(2, mTimePicker.getHour());
+ assertEquals(43, mTimePicker.getMinute());
+ CtsKeyEventUtil.sendKeyWhileHoldingModifier(mInstrumentation, mTimePicker,
+ KeyEvent.KEYCODE_TAB, KeyEvent.KEYCODE_SHIFT_LEFT);
+ // The hour view still has focus.
+ assertTrue(mTimePicker.getHourView().hasFocus());
+
+ // This time send a valid hour.
+ CtsKeyEventUtil.sendKeyDownUp(mInstrumentation, mTimePicker, KeyEvent.KEYCODE_2);
+ CtsKeyEventUtil.sendKeyDownUp(mInstrumentation, mTimePicker, KeyEvent.KEYCODE_3);
+ // The value is valid.
+ assertEquals(23, mTimePicker.getHour());
+ assertEquals(43, mTimePicker.getMinute());
+
+ verifyModeClockMinuteInput();
+ }
+
+ private void verifyModeClockMinuteInput() {
+ assertTrue(mTimePicker.getMinuteView().hasFocus());
+ // Send a invalid minute.
+ CtsKeyEventUtil.sendKeyDownUp(mInstrumentation, mTimePicker, KeyEvent.KEYCODE_6);
+ CtsKeyEventUtil.sendKeyDownUp(mInstrumentation, mTimePicker, KeyEvent.KEYCODE_7);
+ // Sent 6 and 7 but only 6 was valid.
+ assertEquals(6, mTimePicker.getMinute());
+ // No matter what other invalid values we send, the minute is unchanged and the focus is
+ // kept.
+ // 61 invalid.
+ CtsKeyEventUtil.sendKeyDownUp(mInstrumentation, mTimePicker, KeyEvent.KEYCODE_1);
+ assertTrue(mTimePicker.getMinuteView().hasFocus());
+ // 62 invalid.
+ CtsKeyEventUtil.sendKeyDownUp(mInstrumentation, mTimePicker, KeyEvent.KEYCODE_2);
+ assertTrue(mTimePicker.getMinuteView().hasFocus());
+ // 63 invalid.
+ CtsKeyEventUtil.sendKeyDownUp(mInstrumentation, mTimePicker, KeyEvent.KEYCODE_3);
+ assertTrue(mTimePicker.getMinuteView().hasFocus());
+ assertEquals(6, mTimePicker.getMinute());
+ // Refocus.
+ CtsKeyEventUtil.sendKeyDownUp(mInstrumentation, mTimePicker, KeyEvent.KEYCODE_TAB);
+ CtsKeyEventUtil.sendKeyWhileHoldingModifier(mInstrumentation, mTimePicker,
+ KeyEvent.KEYCODE_TAB, KeyEvent.KEYCODE_SHIFT_LEFT);
+ assertTrue(mTimePicker.getMinuteView().hasFocus());
+
+ // In the end pass a valid minute.
+ CtsKeyEventUtil.sendKeyDownUp(mInstrumentation, mTimePicker, KeyEvent.KEYCODE_5);
+ CtsKeyEventUtil.sendKeyDownUp(mInstrumentation, mTimePicker, KeyEvent.KEYCODE_9);
+ assertEquals(59, mTimePicker.getMinute());
+ }
+
+ private void prepareForKeyboardInput(int initialHour, int initialMinute, boolean is24hFormat) {
+ mTimePicker = (TimePicker) mActivity.findViewById(R.id.timepicker_clock);
+ final View spinnerTimePicker = mActivity.findViewById(R.id.timepicker_spinner);
+
+ mInstrumentation.runOnMainSync(() -> {
+ // Remove the spinner TimePicker.
+ ((ViewGroup) spinnerTimePicker.getParent()).removeView(spinnerTimePicker);
+ mTimePicker.setIs24HourView(is24hFormat);
+ mTimePicker.setHour(initialHour);
+ mTimePicker.setMinute(initialMinute);
+ mTimePicker.requestFocus();
+ });
+ mInstrumentation.waitForIdleSync();
+ }
+
+ private void verifyTimePickerKeyboardTraversal(boolean goForward, boolean is24HourView,
+ boolean isSpinner) {
+ ArrayList<View> forwardViews = new ArrayList<>();
+ String summary = (goForward ? " forward " : " backward ")
+ + "traversal, is24HourView=" + is24HourView
+ + (isSpinner ? ", mode spinner" : ", mode clock");
+ assertNotNull("Unexpected NULL hour view for" + summary, mTimePicker.getHourView());
+ forwardViews.add(mTimePicker.getHourView());
+ assertNotNull("Unexpected NULL minute view for" + summary, mTimePicker.getMinuteView());
+ forwardViews.add(mTimePicker.getMinuteView());
+ if (!is24HourView) {
+ if (isSpinner) {
+ // The spinner mode only contains one view for inputting AM/PM.
+ assertNotNull("Unexpected NULL AM/PM view for" + summary, mTimePicker.getAmView());
+ forwardViews.add(mTimePicker.getAmView());
+ } else {
+ assertNotNull("Unexpected NULL AM view for" + summary, mTimePicker.getAmView());
+ forwardViews.add(mTimePicker.getAmView());
+ assertNotNull("Unexpected NULL PM view for" + summary, mTimePicker.getPmView());
+ forwardViews.add(mTimePicker.getPmView());
+ }
}
- public TimePicker getNotifiedView() {
- return mNotifiedView;
+ if (!goForward) {
+ Collections.reverse(forwardViews);
}
- public int getNotifiedHourOfDay() {
- return mNotifiedHourOfDay;
- }
+ final int viewsSize = forwardViews.size();
+ for (int i = 0; i < viewsSize; i++) {
+ final View currentView = forwardViews.get(i);
+ String afterKeyCodeFormattedString = "";
+ int goForwardKeyCode = KeyEvent.KEYCODE_TAB;
+ int modifierKeyCodeToHold = KeyEvent.KEYCODE_SHIFT_LEFT;
- public int getNotifiedMinute() {
- return mNotifiedMinute;
- }
+ if (i == 0) {
+ // Make sure we always start by focusing the 1st element in the list.
+ mInstrumentation.runOnMainSync(currentView::requestFocus);
+ } else {
+ if (goForward) {
+ afterKeyCodeFormattedString = " after pressing="
+ + KeyEvent.keyCodeToString(goForwardKeyCode);
+ } else {
+ afterKeyCodeFormattedString = " after pressing="
+ + KeyEvent.keyCodeToString(modifierKeyCodeToHold)
+ + "+" + KeyEvent.keyCodeToString(goForwardKeyCode) + " for" + summary;
+ }
+ }
- public void reset() {
- mNotifiedView = null;
- mNotifiedHourOfDay = 0;
- mNotifiedMinute = 0;
- mHasCalledOnTimeChanged = false;
- }
+ assertTrue("View='" + currentView + "'" + " with index " + i + " is not enabled"
+ + afterKeyCodeFormattedString + " for" + summary, currentView.isEnabled());
+ assertTrue("View='" + currentView + "'" + " with index " + i + " is not focused"
+ + afterKeyCodeFormattedString + " for" + summary, currentView.isFocused());
- public void onTimeChanged(TimePicker view, int hourOfDay, int minute) {
- mNotifiedView = view;
- mNotifiedHourOfDay = hourOfDay;
- mNotifiedMinute = minute;
- mHasCalledOnTimeChanged = true;
+ if (i < viewsSize - 1) {
+ if (goForward) {
+ CtsKeyEventUtil.sendKeyDownUp(mInstrumentation, currentView, goForwardKeyCode);
+ } else {
+ CtsKeyEventUtil.sendKeyWhileHoldingModifier(mInstrumentation, currentView,
+ goForwardKeyCode, modifierKeyCodeToHold);
+ }
+ }
}
}
diff --git a/tests/tests/widget/src/android/widget/cts/ToastTest.java b/tests/tests/widget/src/android/widget/cts/ToastTest.java
index 854247e..696b8a5 100644
--- a/tests/tests/widget/src/android/widget/cts/ToastTest.java
+++ b/tests/tests/widget/src/android/widget/cts/ToastTest.java
@@ -16,9 +16,6 @@
package android.widget.cts;
-import android.widget.cts.R;
-
-
import android.app.Activity;
import android.app.Instrumentation;
import android.cts.util.PollingCheck;
@@ -32,6 +29,7 @@
import android.view.WindowManager;
import android.widget.ImageView;
import android.widget.Toast;
+import android.widget.cts.R;
public class ToastTest extends ActivityInstrumentationTestCase2<CtsActivity> {
private static final String TEST_TOAST_TEXT = "test toast";
@@ -74,22 +72,12 @@
}
private void assertShowToast(final View view) {
- new PollingCheck(TIME_OUT) {
- @Override
- protected boolean check() {
- return null != view.getParent();
- }
- }.run();
+ PollingCheck.waitFor(TIME_OUT, () -> null != view.getParent());
}
private void assertShowAndHide(final View view) {
assertShowToast(view);
- new PollingCheck(TIME_OUT) {
- @Override
- protected boolean check() {
- return null == view.getParent();
- }
- }.run();
+ PollingCheck.waitFor(TIME_OUT, () -> null == view.getParent());
}
private void assertNotShowToast(final View view) throws InterruptedException {
@@ -104,12 +92,7 @@
}
private void assertLayoutDone(final View view) {
- new PollingCheck(TIME_OUT) {
- @Override
- protected boolean check() {
- return mLayoutDone;
- }
- }.run();
+ PollingCheck.waitFor(TIME_OUT, () -> mLayoutDone);
view.getViewTreeObserver().removeOnGlobalLayoutListener(mLayoutListener);
}
diff --git a/hostsidetests/services/activitymanager/app/src/android/server/app/SlowCreateActivity.java b/tests/tests/widget/src/android/widget/cts/ToggleButtonCtsActivity.java
similarity index 67%
copy from hostsidetests/services/activitymanager/app/src/android/server/app/SlowCreateActivity.java
copy to tests/tests/widget/src/android/widget/cts/ToggleButtonCtsActivity.java
index 481f5be..3640957 100644
--- a/hostsidetests/services/activitymanager/app/src/android/server/app/SlowCreateActivity.java
+++ b/tests/tests/widget/src/android/widget/cts/ToggleButtonCtsActivity.java
@@ -14,17 +14,22 @@
* limitations under the License.
*/
-package android.server.app;
+package android.widget.cts;
import android.app.Activity;
import android.os.Bundle;
+import android.widget.ToggleButton;
-public class SlowCreateActivity extends Activity {
+/**
+ * A minimal application for {@link ToggleButton} test.
+ */
+public class ToggleButtonCtsActivity extends Activity {
+ /**
+ * Called with the activity is first created.
+ */
@Override
- protected void onCreate(Bundle savedInstanceState) {
- try {
- Thread.sleep(2000);
- } catch(InterruptedException e) {}
+ public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ setContentView(R.layout.togglebutton_layout);
}
}
diff --git a/tests/tests/widget/src/android/widget/cts/ToggleButtonTest.java b/tests/tests/widget/src/android/widget/cts/ToggleButtonTest.java
index d335c13..85681e2 100644
--- a/tests/tests/widget/src/android/widget/cts/ToggleButtonTest.java
+++ b/tests/tests/widget/src/android/widget/cts/ToggleButtonTest.java
@@ -16,45 +16,49 @@
package android.widget.cts;
-import org.xmlpull.v1.XmlPullParser;
-
+import android.app.Activity;
+import android.app.Instrumentation;
import android.content.Context;
-import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.StateListDrawable;
-import android.test.InstrumentationTestCase;
-import android.test.UiThreadTest;
-import android.util.AttributeSet;
-import android.util.Xml;
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.suitebuilder.annotation.SmallTest;
import android.widget.ToggleButton;
-
import android.widget.cts.R;
/**
* Test {@link ToggleButton}.
*/
-public class ToggleButtonTest extends InstrumentationTestCase {
+@SmallTest
+public class ToggleButtonTest extends ActivityInstrumentationTestCase2<ToggleButtonCtsActivity> {
private static final String TEXT_OFF = "text off";
private static final String TEXT_ON = "text on";
- ToggleButton mToggleButton;
- Context mContext;
- AttributeSet mAttrSet;
+
+ private Instrumentation mInstrumentation;
+ private Activity mActivity;
+
+ public ToggleButtonTest() {
+ super("android.widget.cts", ToggleButtonCtsActivity.class);
+ }
@Override
protected void setUp() throws Exception {
super.setUp();
- mContext = getInstrumentation().getTargetContext();
- XmlPullParser parser = mContext.getResources().getXml(R.layout.togglebutton_layout);
- mAttrSet = Xml.asAttributeSet(parser);
- mToggleButton = new ToggleButton(mContext, mAttrSet);
+ mInstrumentation = getInstrumentation();
+ mActivity = getActivity();
}
public void testConstructor() {
- new ToggleButton(mContext, mAttrSet, 0);
- new ToggleButton(mContext, mAttrSet);
- new ToggleButton(mContext);
+ new ToggleButton(mActivity);
+ new ToggleButton(mActivity, null);
+ new ToggleButton(mActivity, null, android.R.attr.buttonStyleToggle);
+ new ToggleButton(mActivity, null, 0, android.R.style.Widget_DeviceDefault_Button_Toggle);
+ new ToggleButton(mActivity, null, 0,
+ android.R.style.Widget_DeviceDefault_Light_Button_Toggle);
+ new ToggleButton(mActivity, null, 0, android.R.style.Widget_Material_Button_Toggle);
+ new ToggleButton(mActivity, null, 0, android.R.style.Widget_Material_Light_Button_Toggle);
try {
new ToggleButton(null, null, -1);
@@ -78,105 +82,127 @@
}
}
- public void testAccessTextOff() {
- mToggleButton.setTextOff("android");
- assertEquals("android", mToggleButton.getTextOff());
- mToggleButton.setChecked(false);
-
- mToggleButton.setTextOff(null);
- assertNull(mToggleButton.getTextOff());
-
- mToggleButton.setTextOff("");
- assertEquals("", mToggleButton.getTextOff());
+ public void testAttributesFromStyle() {
+ final ToggleButton toggleButton =
+ (ToggleButton) mActivity.findViewById(R.id.toggle_with_style);
+ assertEquals(mActivity.getString(R.string.toggle_text_on), toggleButton.getTextOn());
+ assertEquals(mActivity.getString(R.string.toggle_text_off), toggleButton.getTextOff());
}
- @UiThreadTest
+ public void testAttributesFromLayout() {
+ final ToggleButton toggleButton =
+ (ToggleButton) mActivity.findViewById(R.id.toggle_with_defaults);
+ assertEquals(mActivity.getString(R.string.toggle_text_on_alt), toggleButton.getTextOn());
+ assertEquals(mActivity.getString(R.string.toggle_text_off_alt), toggleButton.getTextOff());
+ }
+
+ public void testAccessTextOff() {
+ final ToggleButton toggleButton = (ToggleButton) mActivity.findViewById(R.id.toggle1);
+ mInstrumentation.runOnMainSync(() -> toggleButton.setTextOff("android"));
+ assertEquals("android", toggleButton.getTextOff());
+ mInstrumentation.runOnMainSync(() -> toggleButton.setChecked(false));
+
+ mInstrumentation.runOnMainSync(() -> toggleButton.setTextOff(null));
+ assertNull(toggleButton.getTextOff());
+
+ mInstrumentation.runOnMainSync(() -> toggleButton.setTextOff(""));
+ assertEquals("", toggleButton.getTextOff());
+ }
+
public void testDrawableStateChanged() {
- MockToggleButton toggleButton = new MockToggleButton(mContext);
+ final MockToggleButton toggleButton = new MockToggleButton(mActivity);
// drawableStateChanged without any drawable.
- toggleButton.drawableStateChanged();
+ mInstrumentation.runOnMainSync(() -> toggleButton.drawableStateChanged());
- StateListDrawable drawable = new StateListDrawable();
+ final StateListDrawable drawable = new StateListDrawable();
drawable.addState(new int[] { android.R.attr.state_pressed },
- mContext.getDrawable(R.drawable.scenery));
+ mActivity.getDrawable(R.drawable.scenery));
drawable.addState(new int[] {},
- mContext.getDrawable(R.drawable.scenery));
+ mActivity.getDrawable(R.drawable.scenery));
// drawableStateChanged when CheckMarkDrawable is not null.
- toggleButton.setButtonDrawable(drawable);
+ mInstrumentation.runOnMainSync(() -> toggleButton.setButtonDrawable(drawable));
drawable.setState(null);
assertNull(drawable.getState());
- toggleButton.drawableStateChanged();
+ mInstrumentation.runOnMainSync(toggleButton::drawableStateChanged);
assertNotNull(drawable.getState());
assertEquals(toggleButton.getDrawableState(), drawable.getState());
}
public void testOnFinishInflate() {
- MockToggleButton toggleButton = new MockToggleButton(mContext);
+ MockToggleButton toggleButton = new MockToggleButton(mActivity);
toggleButton.onFinishInflate();
}
- @UiThreadTest
public void testSetChecked() {
- assertFalse(mToggleButton.isChecked());
+ final ToggleButton toggleButton = (ToggleButton) mActivity.findViewById(R.id.toggle1);
+ assertFalse(toggleButton.isChecked());
- mToggleButton.setChecked(true);
- assertTrue(mToggleButton.isChecked());
+ mInstrumentation.runOnMainSync(() -> toggleButton.setChecked(true));
+ assertTrue(toggleButton.isChecked());
- mToggleButton.setChecked(false);
- assertFalse(mToggleButton.isChecked());
+ mInstrumentation.runOnMainSync(() -> toggleButton.setChecked(false));
+ assertFalse(toggleButton.isChecked());
}
- @UiThreadTest
public void testToggleText() {
- mToggleButton.setText("default text");
- mToggleButton.setTextOn(TEXT_ON);
- mToggleButton.setTextOff(TEXT_OFF);
- mToggleButton.setChecked(true);
- assertEquals(TEXT_ON, mToggleButton.getText().toString());
- mToggleButton.setChecked(false);
- assertFalse(mToggleButton.isChecked());
- assertEquals(TEXT_OFF, mToggleButton.getText().toString());
+ final ToggleButton toggleButton = (ToggleButton) mActivity.findViewById(R.id.toggle1);
+ mInstrumentation.runOnMainSync(() -> {
+ toggleButton.setText("default text");
+ toggleButton.setTextOn(TEXT_ON);
+ toggleButton.setTextOff(TEXT_OFF);
+ toggleButton.setChecked(true);
+ });
+ assertEquals(TEXT_ON, toggleButton.getText().toString());
+ toggleButton.setChecked(false);
+ assertFalse(toggleButton.isChecked());
+ assertEquals(TEXT_OFF, toggleButton.getText().toString());
// Set the current displaying text as TEXT_OFF.
// Then set checked button, but textOn is null.
- mToggleButton.setTextOff(TEXT_OFF);
- mToggleButton.setChecked(false);
- mToggleButton.setTextOn(null);
- mToggleButton.setChecked(true);
- assertEquals(TEXT_OFF, mToggleButton.getText().toString());
+ mInstrumentation.runOnMainSync(() -> {
+ toggleButton.setTextOff(TEXT_OFF);
+ toggleButton.setChecked(false);
+ toggleButton.setTextOn(null);
+ toggleButton.setChecked(true);
+ });
+ assertEquals(TEXT_OFF, toggleButton.getText().toString());
// Set the current displaying text as TEXT_ON. Then set unchecked button,
// but textOff is null.
- mToggleButton.setTextOn(TEXT_ON);
- mToggleButton.setChecked(true);
- mToggleButton.setTextOff(null);
- mToggleButton.setChecked(false);
- assertEquals(TEXT_ON, mToggleButton.getText().toString());
+ mInstrumentation.runOnMainSync(() -> {
+ toggleButton.setTextOn(TEXT_ON);
+ toggleButton.setChecked(true);
+ toggleButton.setTextOff(null);
+ toggleButton.setChecked(false);
+ });
+ assertEquals(TEXT_ON, toggleButton.getText().toString());
}
public void testSetBackgroundDrawable() {
- Drawable drawable = mContext.getResources().getDrawable(R.drawable.scenery);
+ final ToggleButton toggleButton = (ToggleButton) mActivity.findViewById(R.id.toggle1);
+ final Drawable drawable = mActivity.getDrawable(R.drawable.scenery);
- mToggleButton.setBackgroundDrawable(drawable);
- assertSame(drawable, mToggleButton.getBackground());
+ mInstrumentation.runOnMainSync(() -> toggleButton.setBackgroundDrawable(drawable));
+ assertSame(drawable, toggleButton.getBackground());
// remove the background
- mToggleButton.setBackgroundDrawable(null);
- assertNull(mToggleButton.getBackground());
+ mInstrumentation.runOnMainSync(() -> toggleButton.setBackgroundDrawable(null));
+ assertNull(toggleButton.getBackground());
}
public void testAccessTextOn() {
- mToggleButton.setTextOn("cts");
- assertEquals("cts", mToggleButton.getTextOn());
+ final ToggleButton toggleButton = (ToggleButton) mActivity.findViewById(R.id.toggle1);
+ mInstrumentation.runOnMainSync(() -> toggleButton.setTextOn("cts"));
+ assertEquals("cts", toggleButton.getTextOn());
- mToggleButton.setTextOn(null);
- assertNull(mToggleButton.getTextOn());
+ mInstrumentation.runOnMainSync(() -> toggleButton.setTextOn(null));
+ assertNull(toggleButton.getTextOn());
- mToggleButton.setTextOn("");
- assertEquals("", mToggleButton.getTextOn());
+ mInstrumentation.runOnMainSync(() -> toggleButton.setTextOn(""));
+ assertEquals("", toggleButton.getTextOn());
}
/**
diff --git a/tests/tests/widget/src/android/widget/cts/ToolbarTest.java b/tests/tests/widget/src/android/widget/cts/ToolbarTest.java
index 7f9690c..49b879a 100644
--- a/tests/tests/widget/src/android/widget/cts/ToolbarTest.java
+++ b/tests/tests/widget/src/android/widget/cts/ToolbarTest.java
@@ -16,9 +16,10 @@
package android.widget.cts;
+import static org.mockito.Mockito.*;
+
import android.app.Instrumentation;
-import android.content.Context;
-import android.content.res.TypedArray;
+import android.cts.util.WidgetTestUtils;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.test.ActivityInstrumentationTestCase2;
@@ -27,17 +28,14 @@
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
-import android.widget.ListPopupWindow;
import android.widget.Toolbar;
import android.widget.cts.util.TestUtils;
-import android.widget.cts.util.ViewTestUtils;
-
-import static org.mockito.Mockito.*;
@MediumTest
public class ToolbarTest extends ActivityInstrumentationTestCase2<ToolbarCtsActivity> {
- private Toolbar mMainToolbar;
+ private Instrumentation mInstrumentation;
private ToolbarCtsActivity mActivity;
+ private Toolbar mMainToolbar;
public ToolbarTest() {
super("android.widget.cts", ToolbarCtsActivity.class);
@@ -46,6 +44,8 @@
@Override
protected void setUp() throws Exception {
super.setUp();
+
+ mInstrumentation = getInstrumentation();
mActivity = getActivity();
mMainToolbar = mActivity.getMainToolbar();
}
@@ -67,45 +67,41 @@
// of getTitle / getSubtitle, this logic follows the path of deferred layout
// and invalidation of the TextViews that show the title / subtitle in the Toolbar.
- final Instrumentation instrumentation = getInstrumentation();
-
- ViewTestUtils.runOnMainAndDrawSync(instrumentation, mMainToolbar,
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mMainToolbar,
() -> mMainToolbar.setTitle(R.string.toolbar_title));
assertEquals(mActivity.getString(R.string.toolbar_title), mMainToolbar.getTitle());
- ViewTestUtils.runOnMainAndDrawSync(instrumentation, mMainToolbar,
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mMainToolbar,
() -> mMainToolbar.setTitle("New title"));
assertEquals("New title", mMainToolbar.getTitle());
- ViewTestUtils.runOnMainAndDrawSync(instrumentation, mMainToolbar,
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mMainToolbar,
() -> mMainToolbar.setSubtitle(R.string.toolbar_subtitle));
assertEquals(mActivity.getString(R.string.toolbar_subtitle), mMainToolbar.getSubtitle());
- ViewTestUtils.runOnMainAndDrawSync(instrumentation, mMainToolbar,
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mMainToolbar,
() -> mMainToolbar.setSubtitle("New subtitle"));
assertEquals("New subtitle", mMainToolbar.getSubtitle());
}
public void testTitleAndSubtitleAppearance() {
- final Instrumentation instrumentation = getInstrumentation();
-
- ViewTestUtils.runOnMainAndDrawSync(instrumentation, mMainToolbar,
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mMainToolbar,
() -> mMainToolbar.setTitle(R.string.toolbar_title));
- ViewTestUtils.runOnMainAndDrawSync(instrumentation, mMainToolbar,
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mMainToolbar,
() -> mMainToolbar.setSubtitle(R.string.toolbar_subtitle));
// Since there are no APIs to get reference to the underlying implementation of
// title and subtitle, here we are testing that calling the relevant APIs doesn't crash
- ViewTestUtils.runOnMainAndDrawSync(instrumentation, mMainToolbar,
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mMainToolbar,
() -> mMainToolbar.setTitleTextColor(Color.RED));
- ViewTestUtils.runOnMainAndDrawSync(instrumentation, mMainToolbar,
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mMainToolbar,
() -> mMainToolbar.setSubtitleTextColor(Color.BLUE));
- ViewTestUtils.runOnMainAndDrawSync(instrumentation, mMainToolbar,
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mMainToolbar,
() -> mMainToolbar.setTitleTextAppearance(
mActivity, R.style.TextAppearance_NotColors));
- ViewTestUtils.runOnMainAndDrawSync(instrumentation, mMainToolbar,
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mMainToolbar,
() -> mMainToolbar.setSubtitleTextAppearance(
mActivity, R.style.TextAppearance_WithColor));
}
@@ -139,9 +135,7 @@
}
public void testMenuContent() {
- final Instrumentation instrumentation = getInstrumentation();
-
- ViewTestUtils.runOnMainAndDrawSync(instrumentation, mMainToolbar,
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mMainToolbar,
() -> mMainToolbar.inflateMenu(R.menu.toolbar_menu));
final Menu menu = mMainToolbar.getMenu();
@@ -170,34 +164,30 @@
}
public void testMenuOverflowShowHide() {
- final Instrumentation instrumentation = getInstrumentation();
-
// Inflate menu and check that we're not showing overflow menu yet
- instrumentation.runOnMainSync(() -> mMainToolbar.inflateMenu(R.menu.toolbar_menu));
+ mInstrumentation.runOnMainSync(() -> mMainToolbar.inflateMenu(R.menu.toolbar_menu));
assertFalse(mMainToolbar.isOverflowMenuShowing());
// Ask to show overflow menu and check that it's showing
- instrumentation.runOnMainSync(() -> mMainToolbar.showOverflowMenu());
- instrumentation.waitForIdleSync();
+ mInstrumentation.runOnMainSync(() -> mMainToolbar.showOverflowMenu());
+ mInstrumentation.waitForIdleSync();
assertTrue(mMainToolbar.isOverflowMenuShowing());
// Ask to hide the overflow menu and check that it's not showing
- instrumentation.runOnMainSync(() -> mMainToolbar.hideOverflowMenu());
- instrumentation.waitForIdleSync();
+ mInstrumentation.runOnMainSync(() -> mMainToolbar.hideOverflowMenu());
+ mInstrumentation.waitForIdleSync();
assertFalse(mMainToolbar.isOverflowMenuShowing());
}
public void testMenuOverflowSubmenu() {
- final Instrumentation instrumentation = getInstrumentation();
-
// Inflate menu and check that we're not showing overflow menu yet
- ViewTestUtils.runOnMainAndDrawSync(instrumentation, mMainToolbar,
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mMainToolbar,
() -> mMainToolbar.inflateMenu(R.menu.toolbar_menu));
assertFalse(mMainToolbar.isOverflowMenuShowing());
// Ask to show overflow menu and check that it's showing
- instrumentation.runOnMainSync(() -> mMainToolbar.showOverflowMenu());
- instrumentation.waitForIdleSync();
+ mInstrumentation.runOnMainSync(mMainToolbar::showOverflowMenu);
+ mInstrumentation.waitForIdleSync();
assertTrue(mMainToolbar.isOverflowMenuShowing());
// Register a mock menu item click listener on the toolbar
@@ -209,162 +199,256 @@
// Ask to "perform" the share action and check that the menu click listener has
// been notified
- instrumentation.runOnMainSync(() -> menu.performIdentifierAction(R.id.action_share, 0));
+ mInstrumentation.runOnMainSync(() -> menu.performIdentifierAction(R.id.action_share, 0));
verify(menuItemClickListener, times(1)).onMenuItemClick(
menu.findItem(R.id.action_share));
// Ask to dismiss all the popups and check that we're not showing the overflow menu
- instrumentation.runOnMainSync(() -> mMainToolbar.dismissPopupMenus());
- instrumentation.waitForIdleSync();
+ mInstrumentation.runOnMainSync(mMainToolbar::dismissPopupMenus);
+ mInstrumentation.waitForIdleSync();
assertFalse(mMainToolbar.isOverflowMenuShowing());
}
public void testMenuOverflowIcon() {
- final Instrumentation instrumentation = getInstrumentation();
-
// Inflate menu and check that we're not showing overflow menu yet
- ViewTestUtils.runOnMainAndDrawSync(instrumentation, mMainToolbar,
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mMainToolbar,
() -> mMainToolbar.inflateMenu(R.menu.toolbar_menu));
final Drawable overflowIcon = mActivity.getDrawable(R.drawable.icon_red);
- ViewTestUtils.runOnMainAndDrawSync(instrumentation, mMainToolbar,
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mMainToolbar,
() -> mMainToolbar.setOverflowIcon(overflowIcon));
final Drawable toolbarOverflowIcon = mMainToolbar.getOverflowIcon();
TestUtils.assertAllPixelsOfColor("Overflow icon is red", toolbarOverflowIcon,
toolbarOverflowIcon.getIntrinsicWidth(), toolbarOverflowIcon.getIntrinsicHeight(),
- true, 0XFFFF0000, 1, false);
+ true, Color.RED, 1, false);
}
public void testActionView() {
- final Instrumentation instrumentation = getInstrumentation();
-
// Inflate menu and check that we don't have an expanded action view
- ViewTestUtils.runOnMainAndDrawSync(instrumentation, mMainToolbar,
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mMainToolbar,
() -> mMainToolbar.inflateMenu(R.menu.toolbar_menu_search));
assertFalse(mMainToolbar.hasExpandedActionView());
// Expand search menu item's action view and verify that main toolbar has an expanded
// action view
final MenuItem searchMenuItem = mMainToolbar.getMenu().findItem(R.id.action_search);
- instrumentation.runOnMainSync(() -> searchMenuItem.expandActionView());
- instrumentation.waitForIdleSync();
+ mInstrumentation.runOnMainSync(searchMenuItem::expandActionView);
+ mInstrumentation.waitForIdleSync();
assertTrue(searchMenuItem.isActionViewExpanded());
assertTrue(mMainToolbar.hasExpandedActionView());
// Collapse search menu item's action view and verify that main toolbar doesn't have an
// expanded action view
- instrumentation.runOnMainSync(() -> searchMenuItem.collapseActionView());
- instrumentation.waitForIdleSync();
+ mInstrumentation.runOnMainSync(searchMenuItem::collapseActionView);
+ mInstrumentation.waitForIdleSync();
assertFalse(searchMenuItem.isActionViewExpanded());
assertFalse(mMainToolbar.hasExpandedActionView());
// Expand search menu item's action view again
- instrumentation.runOnMainSync(() -> searchMenuItem.expandActionView());
- instrumentation.waitForIdleSync();
+ mInstrumentation.runOnMainSync(searchMenuItem::expandActionView);
+ mInstrumentation.waitForIdleSync();
assertTrue(searchMenuItem.isActionViewExpanded());
assertTrue(mMainToolbar.hasExpandedActionView());
// Now collapse search menu item's action view via toolbar's API and verify that main
// toolbar doesn't have an expanded action view
- instrumentation.runOnMainSync(() -> mMainToolbar.collapseActionView());
- instrumentation.waitForIdleSync();
+ mInstrumentation.runOnMainSync(mMainToolbar::collapseActionView);
+ mInstrumentation.waitForIdleSync();
assertFalse(searchMenuItem.isActionViewExpanded());
assertFalse(mMainToolbar.hasExpandedActionView());
}
public void testNavigationConfiguration() {
- final Instrumentation instrumentation = getInstrumentation();
-
- ViewTestUtils.runOnMainAndDrawSync(instrumentation, mMainToolbar,
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mMainToolbar,
() -> mMainToolbar.setNavigationIcon(R.drawable.icon_green));
Drawable toolbarNavigationIcon = mMainToolbar.getNavigationIcon();
TestUtils.assertAllPixelsOfColor("Navigation icon is green", toolbarNavigationIcon,
toolbarNavigationIcon.getIntrinsicWidth(),
toolbarNavigationIcon.getIntrinsicHeight(),
- true, 0xFF00FF00, 1, false);
+ true, Color.GREEN, 1, false);
- ViewTestUtils.runOnMainAndDrawSync(instrumentation, mMainToolbar,
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mMainToolbar,
() -> mMainToolbar.setNavigationIcon(mActivity.getDrawable(R.drawable.icon_blue)));
toolbarNavigationIcon = mMainToolbar.getNavigationIcon();
TestUtils.assertAllPixelsOfColor("Navigation icon is blue", toolbarNavigationIcon,
toolbarNavigationIcon.getIntrinsicWidth(),
toolbarNavigationIcon.getIntrinsicHeight(),
- true, 0xFF0000FF, 1, false);
+ true, Color.BLUE, 1, false);
- instrumentation.runOnMainSync(
+ mInstrumentation.runOnMainSync(
() -> mMainToolbar.setNavigationContentDescription(R.string.toolbar_navigation));
assertEquals(mActivity.getResources().getString(R.string.toolbar_navigation),
mMainToolbar.getNavigationContentDescription());
- instrumentation.runOnMainSync(
+ mInstrumentation.runOnMainSync(
() -> mMainToolbar.setNavigationContentDescription("Navigation legend"));
assertEquals("Navigation legend", mMainToolbar.getNavigationContentDescription());
}
public void testLogoConfiguration() {
- final Instrumentation instrumentation = getInstrumentation();
-
- ViewTestUtils.runOnMainAndDrawSync(instrumentation, mMainToolbar,
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mMainToolbar,
() -> mMainToolbar.setLogo(R.drawable.icon_yellow));
Drawable toolbarLogo = mMainToolbar.getLogo();
TestUtils.assertAllPixelsOfColor("Logo is yellow", toolbarLogo,
toolbarLogo.getIntrinsicWidth(),
toolbarLogo.getIntrinsicHeight(),
- true, 0xFFFFFF00, 1, false);
+ true, Color.YELLOW, 1, false);
- ViewTestUtils.runOnMainAndDrawSync(instrumentation, mMainToolbar,
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mMainToolbar,
() -> mMainToolbar.setLogo(mActivity.getDrawable(R.drawable.icon_red)));
toolbarLogo = mMainToolbar.getLogo();
TestUtils.assertAllPixelsOfColor("Logo is red", toolbarLogo,
toolbarLogo.getIntrinsicWidth(),
toolbarLogo.getIntrinsicHeight(),
- true, 0xFFFF0000, 1, false);
+ true, Color.RED, 1, false);
- instrumentation.runOnMainSync(
+ mInstrumentation.runOnMainSync(
() -> mMainToolbar.setLogoDescription(R.string.toolbar_logo));
assertEquals(mActivity.getResources().getString(R.string.toolbar_logo),
mMainToolbar.getLogoDescription());
- instrumentation.runOnMainSync(
+ mInstrumentation.runOnMainSync(
() -> mMainToolbar.setLogoDescription("Logo legend"));
assertEquals("Logo legend", mMainToolbar.getLogoDescription());
}
- @UiThreadTest
public void testContentInsetsLtr() {
- mMainToolbar.setLayoutDirection(View.LAYOUT_DIRECTION_LTR);
+ mInstrumentation.runOnMainSync(
+ () -> mMainToolbar.setLayoutDirection(View.LAYOUT_DIRECTION_LTR));
- mMainToolbar.setContentInsetsAbsolute(20, 25);
+ mInstrumentation.runOnMainSync(() -> mMainToolbar.setContentInsetsAbsolute(20, 25));
assertEquals(20, mMainToolbar.getContentInsetLeft());
assertEquals(20, mMainToolbar.getContentInsetStart());
assertEquals(25, mMainToolbar.getContentInsetRight());
assertEquals(25, mMainToolbar.getContentInsetEnd());
- mMainToolbar.setContentInsetsRelative(40, 20);
+ mInstrumentation.runOnMainSync(() -> mMainToolbar.setContentInsetsRelative(40, 20));
assertEquals(40, mMainToolbar.getContentInsetLeft());
assertEquals(40, mMainToolbar.getContentInsetStart());
assertEquals(20, mMainToolbar.getContentInsetRight());
assertEquals(20, mMainToolbar.getContentInsetEnd());
}
- @UiThreadTest
public void testContentInsetsRtl() {
- mMainToolbar.setLayoutDirection(View.LAYOUT_DIRECTION_RTL);
+ mInstrumentation.runOnMainSync(
+ () -> mMainToolbar.setLayoutDirection(View.LAYOUT_DIRECTION_RTL));
- mMainToolbar.setContentInsetsAbsolute(20, 25);
+ mInstrumentation.runOnMainSync(() -> mMainToolbar.setContentInsetsAbsolute(20, 25));
assertEquals(20, mMainToolbar.getContentInsetLeft());
assertEquals(25, mMainToolbar.getContentInsetStart());
assertEquals(25, mMainToolbar.getContentInsetRight());
assertEquals(20, mMainToolbar.getContentInsetEnd());
- mMainToolbar.setContentInsetsRelative(40, 20);
+ mInstrumentation.runOnMainSync(() -> mMainToolbar.setContentInsetsRelative(40, 20));
assertEquals(20, mMainToolbar.getContentInsetLeft());
assertEquals(40, mMainToolbar.getContentInsetStart());
assertEquals(40, mMainToolbar.getContentInsetRight());
assertEquals(20, mMainToolbar.getContentInsetEnd());
}
+ public void testCurrentContentInsetsLtr() {
+ mInstrumentation.runOnMainSync(
+ () -> mMainToolbar.setLayoutDirection(View.LAYOUT_DIRECTION_LTR));
+
+ mInstrumentation.runOnMainSync(() -> mMainToolbar.setContentInsetsRelative(20, 25));
+ assertEquals(20, mMainToolbar.getCurrentContentInsetLeft());
+ assertEquals(20, mMainToolbar.getCurrentContentInsetStart());
+ assertEquals(25, mMainToolbar.getCurrentContentInsetRight());
+ assertEquals(25, mMainToolbar.getCurrentContentInsetEnd());
+
+ mInstrumentation.runOnMainSync(() -> mMainToolbar.setContentInsetStartWithNavigation(50));
+ assertEquals(50, mMainToolbar.getContentInsetStartWithNavigation());
+ // Since we haven't configured the navigation icon itself, the current content insets
+ // should stay the same
+ assertEquals(20, mMainToolbar.getCurrentContentInsetLeft());
+ assertEquals(20, mMainToolbar.getCurrentContentInsetStart());
+ assertEquals(25, mMainToolbar.getCurrentContentInsetRight());
+ assertEquals(25, mMainToolbar.getCurrentContentInsetEnd());
+
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mMainToolbar,
+ () -> mMainToolbar.setNavigationIcon(R.drawable.icon_green));
+ assertEquals(50, mMainToolbar.getContentInsetStartWithNavigation());
+ // Since we have configured the navigation icon, and the currently set start inset with
+ // navigation is bigger than currently set start content inset, we should be getting that
+ // bigger value now
+ assertEquals(50, mMainToolbar.getCurrentContentInsetLeft());
+ assertEquals(50, mMainToolbar.getCurrentContentInsetStart());
+ assertEquals(25, mMainToolbar.getCurrentContentInsetRight());
+ assertEquals(25, mMainToolbar.getCurrentContentInsetEnd());
+
+ mInstrumentation.runOnMainSync(() -> mMainToolbar.setContentInsetEndWithActions(35));
+ assertEquals(35, mMainToolbar.getContentInsetEndWithActions());
+ // Since we haven't configured the menu content, the current content insets
+ // should stay the same
+ assertEquals(50, mMainToolbar.getCurrentContentInsetLeft());
+ assertEquals(50, mMainToolbar.getCurrentContentInsetStart());
+ assertEquals(25, mMainToolbar.getCurrentContentInsetRight());
+ assertEquals(25, mMainToolbar.getCurrentContentInsetEnd());
+
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mMainToolbar,
+ () -> mMainToolbar.inflateMenu(R.menu.toolbar_menu));
+ assertEquals(35, mMainToolbar.getContentInsetEndWithActions());
+ // Since we have configured the menu content, and the currently set start inset with
+ // navigation is bigger than currently set end content inset, we should be getting that
+ // bigger value now
+ assertEquals(50, mMainToolbar.getCurrentContentInsetLeft());
+ assertEquals(50, mMainToolbar.getCurrentContentInsetStart());
+ assertEquals(35, mMainToolbar.getCurrentContentInsetRight());
+ assertEquals(35, mMainToolbar.getCurrentContentInsetEnd());
+ }
+
+ public void testCurrentContentInsetsRtl() {
+ mInstrumentation.runOnMainSync(
+ () -> mMainToolbar.setLayoutDirection(View.LAYOUT_DIRECTION_RTL));
+
+ mInstrumentation.runOnMainSync(() -> mMainToolbar.setContentInsetsRelative(20, 25));
+ assertEquals(25, mMainToolbar.getCurrentContentInsetLeft());
+ assertEquals(20, mMainToolbar.getCurrentContentInsetStart());
+ assertEquals(20, mMainToolbar.getCurrentContentInsetRight());
+ assertEquals(25, mMainToolbar.getCurrentContentInsetEnd());
+
+ mInstrumentation.runOnMainSync(() -> mMainToolbar.setContentInsetStartWithNavigation(50));
+ assertEquals(50, mMainToolbar.getContentInsetStartWithNavigation());
+ // Since we haven't configured the navigation icon itself, the current content insets
+ // should stay the same
+ assertEquals(25, mMainToolbar.getCurrentContentInsetLeft());
+ assertEquals(20, mMainToolbar.getCurrentContentInsetStart());
+ assertEquals(20, mMainToolbar.getCurrentContentInsetRight());
+ assertEquals(25, mMainToolbar.getCurrentContentInsetEnd());
+
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mMainToolbar,
+ () -> mMainToolbar.setNavigationIcon(R.drawable.icon_green));
+ assertEquals(50, mMainToolbar.getContentInsetStartWithNavigation());
+ // Since we have configured the navigation icon, and the currently set start inset with
+ // navigation is bigger than currently set start content inset, we should be getting that
+ // bigger value now
+ assertEquals(25, mMainToolbar.getCurrentContentInsetLeft());
+ assertEquals(50, mMainToolbar.getCurrentContentInsetStart());
+ assertEquals(50, mMainToolbar.getCurrentContentInsetRight());
+ assertEquals(25, mMainToolbar.getCurrentContentInsetEnd());
+
+ mInstrumentation.runOnMainSync(() -> mMainToolbar.setContentInsetEndWithActions(35));
+ assertEquals(35, mMainToolbar.getContentInsetEndWithActions());
+ // Since we haven't configured the menu content, the current content insets
+ // should stay the same
+ assertEquals(25, mMainToolbar.getCurrentContentInsetLeft());
+ assertEquals(50, mMainToolbar.getCurrentContentInsetStart());
+ assertEquals(50, mMainToolbar.getCurrentContentInsetRight());
+ assertEquals(25, mMainToolbar.getCurrentContentInsetEnd());
+
+ WidgetTestUtils.runOnMainAndDrawSync(mInstrumentation, mMainToolbar,
+ () -> mMainToolbar.inflateMenu(R.menu.toolbar_menu));
+ assertEquals(35, mMainToolbar.getContentInsetEndWithActions());
+ // Since we have configured the menu content, and the currently set start inset with
+ // navigation is bigger than currently set end content inset, we should be getting that
+ // bigger value now
+ assertEquals(35, mMainToolbar.getCurrentContentInsetLeft());
+ assertEquals(50, mMainToolbar.getCurrentContentInsetStart());
+ assertEquals(50, mMainToolbar.getCurrentContentInsetRight());
+ assertEquals(35, mMainToolbar.getCurrentContentInsetEnd());
+ }
+
@UiThreadTest
public void testPopupTheme() {
mMainToolbar.setPopupTheme(R.style.ToolbarPopupTheme_Test);
@@ -377,7 +461,7 @@
verify(mockListener, never()).onClick(any(View.class));
- getInstrumentation().runOnMainSync(() -> mMainToolbar.getNavigationView().performClick());
+ mInstrumentation.runOnMainSync(() -> mMainToolbar.getNavigationView().performClick());
verify(mockListener, times(1)).onClick(any(View.class));
verifyNoMoreInteractions(mockListener);
diff --git a/tests/tests/widget/src/android/widget/cts/TwoLineListItemCtsActivity.java b/tests/tests/widget/src/android/widget/cts/TwoLineListItemCtsActivity.java
index 68413ff..fe8b637 100644
--- a/tests/tests/widget/src/android/widget/cts/TwoLineListItemCtsActivity.java
+++ b/tests/tests/widget/src/android/widget/cts/TwoLineListItemCtsActivity.java
@@ -16,11 +16,10 @@
package android.widget.cts;
-import android.widget.cts.R;
-
import android.app.Activity;
import android.os.Bundle;
import android.widget.TwoLineListItem;
+import android.widget.cts.R;
/**
* Stub activity for testing {@link TwoLineListItem}
diff --git a/tests/tests/widget/src/android/widget/cts/TwoLineListItemTest.java b/tests/tests/widget/src/android/widget/cts/TwoLineListItemTest.java
index 4ee22a1..d0c00f8 100644
--- a/tests/tests/widget/src/android/widget/cts/TwoLineListItemTest.java
+++ b/tests/tests/widget/src/android/widget/cts/TwoLineListItemTest.java
@@ -16,17 +16,15 @@
package android.widget.cts;
-import android.widget.cts.R;
-
-
import android.app.Activity;
import android.content.Context;
import android.content.res.Resources;
import android.test.ActivityInstrumentationTestCase;
import android.util.AttributeSet;
+import android.widget.RelativeLayout.LayoutParams;
import android.widget.TextView;
import android.widget.TwoLineListItem;
-import android.widget.RelativeLayout.LayoutParams;
+import android.widget.cts.R;
/**
* Test {@link TwoLineListItem}.
diff --git a/tests/tests/widget/src/android/widget/cts/VideoViewCtsActivity.java b/tests/tests/widget/src/android/widget/cts/VideoViewCtsActivity.java
index e831f04..903c5c2 100644
--- a/tests/tests/widget/src/android/widget/cts/VideoViewCtsActivity.java
+++ b/tests/tests/widget/src/android/widget/cts/VideoViewCtsActivity.java
@@ -16,11 +16,10 @@
package android.widget.cts;
-import android.widget.cts.R;
-
import android.app.Activity;
import android.os.Bundle;
import android.widget.VideoView;
+import android.widget.cts.R;
/**
* A minimal application for {@link VideoView} test.
diff --git a/tests/tests/widget/src/android/widget/cts/VideoViewTest.java b/tests/tests/widget/src/android/widget/cts/VideoViewTest.java
index 265b802..8ceabf1 100644
--- a/tests/tests/widget/src/android/widget/cts/VideoViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/VideoViewTest.java
@@ -16,19 +16,14 @@
package android.widget.cts;
-import android.widget.cts.R;
+import static org.mockito.Matchers.*;
+import static org.mockito.Mockito.*;
import android.app.Activity;
import android.app.Instrumentation;
import android.content.Context;
import android.cts.util.MediaUtils;
-import android.cts.util.PollingCheck;
-import android.media.MediaCodecInfo;
-import android.media.MediaCodecList;
import android.media.MediaPlayer;
-import android.media.MediaPlayer.OnCompletionListener;
-import android.media.MediaPlayer.OnErrorListener;
-import android.media.MediaPlayer.OnPreparedListener;
import android.test.ActivityInstrumentationTestCase2;
import android.test.UiThreadTest;
import android.util.Log;
@@ -36,9 +31,13 @@
import android.widget.MediaController;
import android.widget.VideoView;
+import org.mockito.invocation.InvocationOnMock;
+
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
/**
* Test {@link VideoView}.
@@ -63,43 +62,6 @@
private Instrumentation mInstrumentation;
private String mVideoPath;
- private static class MockListener {
- private boolean mTriggered;
-
- MockListener() {
- mTriggered = false;
- }
-
- public boolean isTriggered() {
- return mTriggered;
- }
-
- protected void onEvent() {
- mTriggered = true;
- }
- }
-
- private static class MockOnPreparedListener extends MockListener
- implements OnPreparedListener {
- public void onPrepared(MediaPlayer mp) {
- super.onEvent();
- }
- }
-
- private static class MockOnErrorListener extends MockListener implements OnErrorListener {
- public boolean onError(MediaPlayer mp, int what, int extra) {
- super.onEvent();
- return false;
- }
- }
-
- private static class MockOnCompletionListener extends MockListener
- implements OnCompletionListener {
- public void onCompletion(MediaPlayer mp) {
- super.onEvent();
- }
- }
-
private boolean hasCodec() {
return MediaUtils.hasCodecsForResource(mActivity, R.raw.testvideo);
}
@@ -190,42 +152,46 @@
return;
}
- final MockOnPreparedListener preparedListener = new MockOnPreparedListener();
- mVideoView.setOnPreparedListener(preparedListener);
- final MockOnCompletionListener completionListener = new MockOnCompletionListener();
- mVideoView.setOnCompletionListener(completionListener);
+ final MediaPlayer.OnPreparedListener mockPreparedListener =
+ mock(MediaPlayer.OnPreparedListener.class);
+ final CountDownLatch preparedLatch = new CountDownLatch(1);
+ doAnswer((InvocationOnMock invocation) -> {
+ preparedLatch.countDown();
+ return null;
+ }).when(mockPreparedListener).onPrepared(any(MediaPlayer.class));
+ mVideoView.setOnPreparedListener(mockPreparedListener);
- runTestOnUiThread(new Runnable() {
- public void run() {
- mVideoView.setVideoPath(mVideoPath);
- }
- });
- new PollingCheck(TIME_OUT) {
- @Override
- protected boolean check() {
- return preparedListener.isTriggered();
- }
- }.run();
- assertFalse(completionListener.isTriggered());
+ final MediaPlayer.OnCompletionListener mockCompletionListener =
+ mock(MediaPlayer.OnCompletionListener.class);
+ final CountDownLatch completionLatch = new CountDownLatch(1);
+ doAnswer((InvocationOnMock invocation) -> {
+ completionLatch.countDown();
+ return null;
+ }).when(mockCompletionListener).onCompletion(any(MediaPlayer.class));
+ mVideoView.setOnCompletionListener(mockCompletionListener);
- runTestOnUiThread(new Runnable() {
- public void run() {
- mVideoView.start();
- }
- });
+ runTestOnUiThread(() -> mVideoView.setVideoPath(mVideoPath));
+ preparedLatch.await(TIME_OUT, TimeUnit.MILLISECONDS);
+ verify(mockPreparedListener, times(1)).onPrepared(any(MediaPlayer.class));
+ verifyZeroInteractions(mockCompletionListener);
+
+ runTestOnUiThread(mVideoView::start);
// wait time is longer than duration in case system is sluggish
- new PollingCheck(mVideoView.getDuration() + TIME_OUT) {
- @Override
- protected boolean check() {
- return completionListener.isTriggered();
- }
- }.run();
+ completionLatch.await(mVideoView.getDuration() + TIME_OUT, TimeUnit.MILLISECONDS);
+ verify(mockCompletionListener, times(1)).onCompletion(any(MediaPlayer.class));
}
public void testSetOnErrorListener() throws Throwable {
makeVideoView();
- final MockOnErrorListener listener = new MockOnErrorListener();
- mVideoView.setOnErrorListener(listener);
+
+ final MediaPlayer.OnErrorListener mockErrorListener =
+ mock(MediaPlayer.OnErrorListener.class);
+ final CountDownLatch errorLatch = new CountDownLatch(1);
+ doAnswer((InvocationOnMock invocation) -> {
+ errorLatch.countDown();
+ return null;
+ }).when(mockErrorListener).onError(any(MediaPlayer.class), anyInt(), anyInt());
+ mVideoView.setOnErrorListener(mockErrorListener);
runTestOnUiThread(new Runnable() {
public void run() {
@@ -236,12 +202,8 @@
});
mInstrumentation.waitForIdleSync();
- new PollingCheck(TIME_OUT) {
- @Override
- protected boolean check() {
- return listener.isTriggered();
- }
- }.run();
+ errorLatch.await(TIME_OUT, TimeUnit.MILLISECONDS);
+ verify(mockErrorListener, times(1)).onError(any(MediaPlayer.class), anyInt(), anyInt());
}
public void testGetBufferPercentage() throws Throwable {
@@ -252,22 +214,20 @@
return;
}
- final MockOnPreparedListener prepareListener = new MockOnPreparedListener();
- mVideoView.setOnPreparedListener(prepareListener);
+ final MediaPlayer.OnPreparedListener mockPreparedListener =
+ mock(MediaPlayer.OnPreparedListener.class);
+ final CountDownLatch preparedLatch = new CountDownLatch(1);
+ doAnswer((InvocationOnMock invocation) -> {
+ preparedLatch.countDown();
+ return null;
+ }).when(mockPreparedListener).onPrepared(any(MediaPlayer.class));
+ mVideoView.setOnPreparedListener(mockPreparedListener);
- runTestOnUiThread(new Runnable() {
- public void run() {
- mVideoView.setVideoPath(mVideoPath);
- }
- });
+ runTestOnUiThread(() -> mVideoView.setVideoPath(mVideoPath));
mInstrumentation.waitForIdleSync();
- new PollingCheck(TIME_OUT) {
- @Override
- protected boolean check() {
- return prepareListener.isTriggered();
- }
- }.run();
+ preparedLatch.await(TIME_OUT, TimeUnit.MILLISECONDS);
+ verify(mockPreparedListener, times(1)).onPrepared(any(MediaPlayer.class));
int percent = mVideoView.getBufferPercentage();
assertTrue(percent >= 0 && percent <= 100);
}
@@ -295,11 +255,7 @@
return;
}
- runTestOnUiThread(new Runnable() {
- public void run() {
- mVideoView.setVideoPath(mVideoPath);
- }
- });
+ runTestOnUiThread(() -> mVideoView.setVideoPath(mVideoPath));
waitForOperationComplete();
assertTrue(Math.abs(mVideoView.getDuration() - TEST_VIDEO_DURATION) < DURATION_DELTA);
}
diff --git a/tests/tests/widget/src/android/widget/cts/ViewAnimatorTest.java b/tests/tests/widget/src/android/widget/cts/ViewAnimatorTest.java
index 696761b..7e93fe3 100644
--- a/tests/tests/widget/src/android/widget/cts/ViewAnimatorTest.java
+++ b/tests/tests/widget/src/android/widget/cts/ViewAnimatorTest.java
@@ -16,11 +16,6 @@
package android.widget.cts;
-import android.widget.cts.R;
-
-
-import org.xmlpull.v1.XmlPullParser;
-
import android.app.Activity;
import android.app.Instrumentation;
import android.test.ActivityInstrumentationTestCase2;
@@ -34,6 +29,9 @@
import android.view.animation.AnimationSet;
import android.widget.RelativeLayout;
import android.widget.ViewAnimator;
+import android.widget.cts.R;
+
+import org.xmlpull.v1.XmlPullParser;
public class ViewAnimatorTest extends
ActivityInstrumentationTestCase2<ViewAnimatorCtsActivity> {
diff --git a/tests/tests/widget/src/android/widget/cts/ViewFlipperCtsActivity.java b/tests/tests/widget/src/android/widget/cts/ViewFlipperCtsActivity.java
index ba90590..f3a37a6 100644
--- a/tests/tests/widget/src/android/widget/cts/ViewFlipperCtsActivity.java
+++ b/tests/tests/widget/src/android/widget/cts/ViewFlipperCtsActivity.java
@@ -16,10 +16,9 @@
package android.widget.cts;
-import android.widget.cts.R;
-
import android.app.Activity;
import android.os.Bundle;
+import android.widget.cts.R;
public class ViewFlipperCtsActivity extends Activity {
@Override
diff --git a/tests/tests/widget/src/android/widget/cts/ViewFlipperTest.java b/tests/tests/widget/src/android/widget/cts/ViewFlipperTest.java
index 5ec242e..a01f79f 100644
--- a/tests/tests/widget/src/android/widget/cts/ViewFlipperTest.java
+++ b/tests/tests/widget/src/android/widget/cts/ViewFlipperTest.java
@@ -16,11 +16,6 @@
package android.widget.cts;
-import android.widget.cts.R;
-
-
-import org.xmlpull.v1.XmlPullParser;
-
import android.app.Activity;
import android.test.ActivityInstrumentationTestCase;
import android.test.UiThreadTest;
@@ -29,6 +24,9 @@
import android.view.View;
import android.widget.TextView;
import android.widget.ViewFlipper;
+import android.widget.cts.R;
+
+import org.xmlpull.v1.XmlPullParser;
/**
* Test {@link ViewFlipper}.
diff --git a/tests/tests/widget/src/android/widget/cts/ViewSwitcherTest.java b/tests/tests/widget/src/android/widget/cts/ViewSwitcherTest.java
index 7828570..3b824b5 100644
--- a/tests/tests/widget/src/android/widget/cts/ViewSwitcherTest.java
+++ b/tests/tests/widget/src/android/widget/cts/ViewSwitcherTest.java
@@ -16,9 +16,6 @@
package android.widget.cts;
-import org.xmlpull.v1.XmlPullParser;
-
-import android.content.Context;
import android.test.AndroidTestCase;
import android.util.AttributeSet;
import android.util.Xml;
@@ -27,9 +24,10 @@
import android.widget.ListView;
import android.widget.ViewSwitcher;
import android.widget.ViewSwitcher.ViewFactory;
-
import android.widget.cts.R;
+import org.xmlpull.v1.XmlPullParser;
+
/**
* Test {@link ViewSwitcher}.
diff --git a/tests/tests/widget/src/android/widget/cts/ZoomButtonCtsActivity.java b/tests/tests/widget/src/android/widget/cts/ZoomButtonCtsActivity.java
index 28563f5..1a50217 100644
--- a/tests/tests/widget/src/android/widget/cts/ZoomButtonCtsActivity.java
+++ b/tests/tests/widget/src/android/widget/cts/ZoomButtonCtsActivity.java
@@ -16,10 +16,9 @@
package android.widget.cts;
-import android.widget.cts.R;
-
import android.app.Activity;
import android.os.Bundle;
+import android.widget.cts.R;
/**
* A minimal application for {@link ZoomButton} test.
diff --git a/tests/tests/widget/src/android/widget/cts/ZoomButtonTest.java b/tests/tests/widget/src/android/widget/cts/ZoomButtonTest.java
index 493c484..7192386 100644
--- a/tests/tests/widget/src/android/widget/cts/ZoomButtonTest.java
+++ b/tests/tests/widget/src/android/widget/cts/ZoomButtonTest.java
@@ -16,23 +16,28 @@
package android.widget.cts;
-import android.widget.cts.R;
-
-
-import org.xmlpull.v1.XmlPullParser;
-
import android.app.Activity;
-import android.cts.util.PollingCheck;
+import android.cts.util.CtsTouchUtils;
import android.test.ActivityInstrumentationTestCase2;
import android.test.UiThreadTest;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.SmallTest;
import android.util.AttributeSet;
import android.util.Xml;
import android.view.View;
-import android.view.View.OnClickListener;
+import android.view.ViewConfiguration;
import android.widget.ListView;
import android.widget.ZoomButton;
+import org.xmlpull.v1.XmlPullParser;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+@SmallTest
public class ZoomButtonTest extends ActivityInstrumentationTestCase2<ZoomButtonCtsActivity> {
+ private static long NANOS_IN_MILLI = 1000000;
private ZoomButton mZoomButton;
private Activity mActivity;
@@ -54,7 +59,9 @@
new ZoomButton(mActivity, null);
- new ZoomButton(mActivity, null, 0);
+ new ZoomButton(mActivity, null, android.R.attr.imageButtonStyle);
+
+ new ZoomButton(mActivity, null, 0, android.R.style.Widget_Material_Light_ImageButton);
XmlPullParser parser = mActivity.getResources().getXml(R.layout.zoombutton_layout);
AttributeSet attrs = Xml.asAttributeSet(parser);
@@ -81,6 +88,7 @@
}
}
+ @UiThreadTest
public void testSetEnabled() {
assertFalse(mZoomButton.isPressed());
mZoomButton.setEnabled(true);
@@ -105,21 +113,54 @@
assertFalse(mZoomButton.dispatchUnhandledMove(null, View.FOCUS_DOWN));
}
- public void testOnLongClick() {
- final MockOnClickListener listener = new MockOnClickListener();
- mZoomButton.setOnClickListener(listener);
- mZoomButton.setEnabled(true);
- long speed = 2000;
- mZoomButton.setZoomSpeed(speed);
+ private void verifyZoomSpeed(ZoomClickListener zoomClickListener, long zoomSpeedMs) {
+ mZoomButton.setZoomSpeed(zoomSpeedMs);
- assertFalse(listener.hasOnClickCalled());
- mZoomButton.performLongClick();
- new PollingCheck(speed + 500) {
- @Override
- protected boolean check() {
- return listener.hasOnClickCalled();
+ final long startTime = System.nanoTime();
+ // Emulate long click that "lasts" for ten seconds
+ CtsTouchUtils.emulateLongClick(getInstrumentation(), mZoomButton, 10000);
+
+ final List<Long> callbackInvocations = zoomClickListener.getClickTimes();
+ assertFalse("Expecting at least one callback", callbackInvocations.isEmpty());
+
+ // Verify that the first callback is fired after the system-level long press timeout.
+ final long minTimeUntilFirstInvocationMs = ViewConfiguration.getLongPressTimeout();
+ final long actualTimeUntilFirstInvocationNs = callbackInvocations.get(0) - startTime;
+ assertTrue("First callback not during long press timeout was " +
+ actualTimeUntilFirstInvocationNs / NANOS_IN_MILLI +
+ " while long press timeout is " + minTimeUntilFirstInvocationMs,
+ (callbackInvocations.get(0) - startTime) >
+ minTimeUntilFirstInvocationMs * NANOS_IN_MILLI);
+
+ // Verify that subsequent callbacks are at least zoom-speed milliseconds apart. Note that
+ // we do not have any hard guarantee about the max limit on the time between successive
+ // callbacks.
+ final long minTimeBetweenInvocationsNs = zoomSpeedMs * NANOS_IN_MILLI;
+ if (callbackInvocations.size() > 1) {
+ for (int i = 0; i < callbackInvocations.size() - 1; i++) {
+ final long actualTimeBetweenInvocationsNs =
+ (callbackInvocations.get(i + 1) - callbackInvocations.get(i)) *
+ NANOS_IN_MILLI;
+ assertTrue("Callback " + (i + 1) + " happened " +
+ actualTimeBetweenInvocationsNs / NANOS_IN_MILLI +
+ " after the previous one, while zoom speed is " + zoomSpeedMs,
+ actualTimeBetweenInvocationsNs > minTimeBetweenInvocationsNs);
}
- };
+ }
+ }
+
+ @LargeTest
+ public void testOnLongClick() {
+ // Since Mockito doesn't have utilities to track the timestamps of method invocations,
+ // we're using our own custom click listener for that. We want to verify that the
+ // first listener invocation was after long press timeout, and the rest were spaced
+ // by at least our zoom speed milliseconds
+
+ mZoomButton.setEnabled(true);
+ ZoomClickListener zoomClickListener = new ZoomClickListener();
+ mZoomButton.setOnClickListener(zoomClickListener);
+
+ verifyZoomSpeed(zoomClickListener, 2000);
}
public void testOnTouchEvent() {
@@ -130,22 +171,35 @@
// Do not test. Implementation details.
}
+ @LargeTest
public void testSetZoomSpeed() {
- mZoomButton.setZoomSpeed(100);
+ final long[] zoomSpeeds = { 100, -1, 5000, 1000, 2500 };
+ mZoomButton.setEnabled(true);
+ ZoomClickListener zoomClickListener = new ZoomClickListener();
+ mZoomButton.setOnClickListener(zoomClickListener);
- mZoomButton.setZoomSpeed(-1);
- // TODO: how to check?
+ for (long zoomSpeed : zoomSpeeds) {
+ // Reset the tracker list of our listener, but continue using it for testing
+ // various zoom speeds on the same ZoomButton
+ zoomClickListener.reset();
+ verifyZoomSpeed(zoomClickListener, zoomSpeed);
+ }
}
- private static class MockOnClickListener implements OnClickListener {
- private boolean mOnClickCalled = false;
+ private static class ZoomClickListener implements View.OnClickListener {
+ private List<Long> mClickTimes = new ArrayList<>();
- public boolean hasOnClickCalled() {
- return mOnClickCalled;
+ public void reset() {
+ mClickTimes.clear();
+ }
+
+ public List<Long> getClickTimes() {
+ return Collections.unmodifiableList(mClickTimes);
}
public void onClick(View v) {
- mOnClickCalled = true;
+ // Add the current system time to the tracker list
+ mClickTimes.add(System.nanoTime());
}
}
}
diff --git a/tests/tests/widget/src/android/widget/cts/appwidget/MyAppWidgetProvider.java b/tests/tests/widget/src/android/widget/cts/appwidget/MyAppWidgetProvider.java
new file mode 100644
index 0000000..fd89cf1
--- /dev/null
+++ b/tests/tests/widget/src/android/widget/cts/appwidget/MyAppWidgetProvider.java
@@ -0,0 +1,177 @@
+/*
+ * 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.widget.cts.appwidget;
+
+import android.app.PendingIntent;
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProvider;
+import android.content.Context;
+import android.content.Intent;
+import android.cts.util.PollingCheck;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
+import android.view.View;
+import android.widget.RemoteViews;
+import android.widget.cts.R;
+
+import java.util.concurrent.CountDownLatch;
+
+public final class MyAppWidgetProvider extends AppWidgetProvider {
+ private static final long TIME_SLICE = 100;
+
+ public static final String KEY_DISPLAYED_CHILD_INDEX =
+ "MyAppWidgetProvider.displayedChildIndex";
+ public static final String KEY_SHOW_NEXT = "MyAppWidgetProvider.showNext";
+ public static final String KEY_SHOW_PREVIOUS = "MyAppWidgetProvider.showPrevious";
+ public static final String KEY_SWITCH_TO_LIST = "MyAppWidgetProvider.switchToList";
+ public static final String KEY_SCROLL_POSITION = "MyAppWidgetProvider.scrollPosition";
+ public static final String KEY_SCROLL_OFFSET = "MyAppWidgetProvider.scrollOffset";
+
+ // This latch will be notified when onEnabled is called on our provider.
+ private static CountDownLatch sCountDownLatch;
+ // Gating condition to be polled to proceed with setScrollPosition call.
+ private static PollingCheck.PollingCheckCondition sSetScrollCondition;
+ // Gating condition to be polled to proceed with setRelativeScrollPosition call.
+ private static PollingCheck.PollingCheckCondition sSetRelativeScrollCondition;
+
+ private int mDisplayedChildIndex;
+ private boolean mShowNext;
+ private boolean mShowPrevious;
+ private boolean mSwitchToList;
+ private int mScrollPosition;
+ private int mScrollOffset;
+
+ public static void configure(CountDownLatch countDownLatch,
+ PollingCheck.PollingCheckCondition setScrollCondition,
+ PollingCheck.PollingCheckCondition setRelativeScrollCondition) {
+ sCountDownLatch = countDownLatch;
+ sSetScrollCondition = setScrollCondition;
+ sSetRelativeScrollCondition = setRelativeScrollCondition;
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ mDisplayedChildIndex = intent.getIntExtra(KEY_DISPLAYED_CHILD_INDEX, -1);
+ mShowNext = intent.getBooleanExtra(KEY_SHOW_NEXT, false);
+ mShowPrevious = intent.getBooleanExtra(KEY_SHOW_PREVIOUS, false);
+ mSwitchToList = intent.getBooleanExtra(KEY_SWITCH_TO_LIST, false);
+ mScrollPosition = intent.getIntExtra(KEY_SCROLL_POSITION, -1);
+ mScrollOffset = intent.getIntExtra(KEY_SCROLL_OFFSET, 0);
+
+ super.onReceive(context, intent);
+ }
+
+ @Override
+ public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
+ final int appWidgetId = appWidgetIds[0];
+ final RemoteViews widgetAdapterView = new RemoteViews(context.getPackageName(),
+ R.layout.remoteviews_adapter);
+
+ final Intent stackIntent = new Intent(context, MyAppWidgetService.class);
+ stackIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
+ stackIntent.setData(Uri.parse(stackIntent.toUri(Intent.URI_INTENT_SCHEME)));
+
+ widgetAdapterView.setRemoteAdapter(R.id.remoteViews_stack, stackIntent);
+ widgetAdapterView.setEmptyView(R.id.remoteViews_stack, R.id.remoteViews_empty);
+
+ if (mDisplayedChildIndex >= 0) {
+ widgetAdapterView.setDisplayedChild(R.id.remoteViews_stack, mDisplayedChildIndex);
+ }
+ if (mShowNext) {
+ widgetAdapterView.showNext(R.id.remoteViews_stack);
+ }
+ if (mShowPrevious) {
+ widgetAdapterView.showPrevious(R.id.remoteViews_stack);
+ }
+
+ // Here we setup the a pending intent template. Individuals items of a collection
+ // cannot setup their own pending intents, instead, the collection as a whole can
+ // setup a pending intent template, and the individual items can set a fillInIntent
+ // to create unique before on an item to item basis.
+ Intent viewIntent = new Intent(Intent.ACTION_VIEW,
+ Uri.parse("ctstest://RemoteView/testWidget"));
+ PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, viewIntent,
+ PendingIntent.FLAG_UPDATE_CURRENT);
+
+ widgetAdapterView.setPendingIntentTemplate(R.id.remoteViews_stack, pendingIntent);
+
+ if (mSwitchToList) {
+ final Intent listIntent = new Intent(context, MyAppWidgetService.class);
+ listIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
+ listIntent.setData(Uri.parse(stackIntent.toUri(Intent.URI_INTENT_SCHEME)));
+
+ widgetAdapterView.setRemoteAdapter(R.id.remoteViews_list, listIntent);
+
+ widgetAdapterView.setViewVisibility(R.id.remoteViews_stack, View.GONE);
+ widgetAdapterView.setViewVisibility(R.id.remoteViews_list, View.VISIBLE);
+ }
+
+ final Handler handler = new Handler(Looper.myLooper());
+ if (mScrollPosition >= 0) {
+ // We need to schedule the call to setScrollPosition as a separate event that runs
+ // after the underlying ListView has been laid out on the screen. Otherwise calling
+ // that API on a ListView with 0x0 dimension has no effect - the list content is only
+ // populated via the adapter when ListView has "real" bounds.
+ final Runnable setScrollRunnable = new Runnable() {
+ public void run() {
+ if (sSetScrollCondition.canProceed()) {
+ // Gating condition has been satisfied. Call setScrollPosition and
+ // ask the widget manager to update our widget
+ widgetAdapterView.setScrollPosition(R.id.remoteViews_list, mScrollPosition);
+ appWidgetManager.partiallyUpdateAppWidget(appWidgetId, widgetAdapterView);
+ } else {
+ // Keep on "waiting" until the gating condition is satisfied
+ handler.postDelayed(this, TIME_SLICE);
+ }
+ }
+ };
+ handler.postDelayed(setScrollRunnable, TIME_SLICE);
+ }
+
+ if (mScrollOffset != 0) {
+ // We need to schedule the call to setRelativeScrollPosition as a separate event that
+ // runs after the underlying ListView has been laid out on the screen. Otherwise calling
+ // that API on a ListView with 0x0 dimension has no effect - the list content is only
+ // populated via the adapter when ListView has "real" bounds.
+ final Runnable setRelativeScrollRunnable = new Runnable() {
+ public void run() {
+ if (sSetRelativeScrollCondition.canProceed()) {
+ // Gating condition has been satisfied. Call setRelativeScrollPosition and
+ // ask the widget manager to update our widget
+ widgetAdapterView.setRelativeScrollPosition(
+ R.id.remoteViews_list, mScrollOffset);
+ appWidgetManager.partiallyUpdateAppWidget(appWidgetId, widgetAdapterView);
+ } else {
+ // Keep on "waiting" until the gating condition is satisfied
+ handler.postDelayed(this, TIME_SLICE);
+ }
+ }
+ };
+ handler.postDelayed(setRelativeScrollRunnable, TIME_SLICE);
+ }
+
+ appWidgetManager.updateAppWidget(appWidgetId, widgetAdapterView);
+
+ sCountDownLatch.countDown();
+ }
+
+ @Override
+ public void onEnabled(Context context) {
+ sCountDownLatch.countDown();
+ }
+}
diff --git a/tests/tests/widget/src/android/widget/cts/appwidget/MyAppWidgetService.java b/tests/tests/widget/src/android/widget/cts/appwidget/MyAppWidgetService.java
new file mode 100644
index 0000000..7fc6b49
--- /dev/null
+++ b/tests/tests/widget/src/android/widget/cts/appwidget/MyAppWidgetService.java
@@ -0,0 +1,39 @@
+/*
+ * 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.widget.cts.appwidget;
+
+import android.content.Intent;
+import android.widget.RemoteViewsService;
+
+public class MyAppWidgetService extends RemoteViewsService {
+ private static final Object sLock = new Object();
+
+ private static RemoteViewsFactory sFactory;
+
+ public static void setFactory(RemoteViewsFactory factory) {
+ synchronized (sLock) {
+ sFactory = factory;
+ }
+ }
+
+ @Override
+ public RemoteViewsFactory onGetViewFactory(Intent intent) {
+ synchronized (sLock) {
+ return sFactory;
+ }
+ }
+}
diff --git a/tests/tests/widget/src/android/widget/cts/util/ExpandableListScenario.java b/tests/tests/widget/src/android/widget/cts/util/ExpandableListScenario.java
index 1b2eb15..7e72e88 100644
--- a/tests/tests/widget/src/android/widget/cts/util/ExpandableListScenario.java
+++ b/tests/tests/widget/src/android/widget/cts/util/ExpandableListScenario.java
@@ -54,7 +54,10 @@
@Override
protected void setAdapter(ListView listView) {
- ((ExpandableListView) listView).setAdapter(mAdapter = createAdapter());
+ mAdapter = createAdapter();
+ if (mAdapter != null) {
+ ((ExpandableListView) listView).setAdapter(mAdapter);
+ }
}
protected ExpandableListAdapter createAdapter() {
@@ -91,7 +94,7 @@
/**
* Sets the number of children per group.
*
- * @param numChildrenPerGroup The number of children per group.
+ * @param numChildren The number of children per group.
*/
public ExpandableParams setNumChildren(int[] numChildren) {
mNumChildren = numChildren;
@@ -275,6 +278,10 @@
* @return A group index with the requirements.
*/
public int findGroupWithNumChildren(int numChildren, boolean atLeastOneChild) {
+ if (mAdapter == null) {
+ return -1;
+ }
+
final ExpandableListAdapter adapter = mAdapter;
for (int i = adapter.getGroupCount() - 1; i >= 0; i--) {
@@ -292,10 +299,6 @@
return mGroups;
}
- public ExpandableListAdapter getAdapter() {
- return mAdapter;
- }
-
/**
* Simple expandable list adapter.
*/
@@ -348,10 +351,10 @@
}
public static class MyGroup {
- private static long mNextId = 1000;
+ private static long sNextId = 1000;
String name;
- long id = mNextId++;
+ long id = sNextId++;
List<MyChild> children;
public MyGroup(int numChildren) {
diff --git a/tests/tests/widget/src/android/widget/cts/util/ListScenario.java b/tests/tests/widget/src/android/widget/cts/util/ListScenario.java
index b61673c..c4d4199 100644
--- a/tests/tests/widget/src/android/widget/cts/util/ListScenario.java
+++ b/tests/tests/widget/src/android/widget/cts/util/ListScenario.java
@@ -79,10 +79,6 @@
return mListView;
}
- protected int getScreenHeight() {
- return mScreenHeight;
- }
-
/**
* Return whether the item at position is selectable (i.e is a separator).
* (external users can access this info using the adapter)
@@ -288,15 +284,6 @@
setClickedPosition(position);
}
- /**
- * Override this if you want to know when something has been long clicked (perhaps
- * more importantly, that {@link android.widget.AdapterView.OnItemLongClickListener} has
- * been triggered).
- */
- protected void positionLongClicked(int position) {
- setLongClickedPosition(position);
- }
-
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
@@ -353,11 +340,13 @@
}
});
- mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
- public void onItemClick(AdapterView parent, View v, int position, long id) {
- positionClicked(position);
- }
- });
+ if (shouldRegisterItemClickListener()) {
+ mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+ public void onItemClick(AdapterView parent, View v, int position, long id) {
+ positionClicked(position);
+ }
+ });
+ }
// set the fading edge length porportionally to the screen
// height for test stability
@@ -404,25 +393,11 @@
}
/**
- * Returns the LinearLayout containing the ListView in this scenario.
- *
- * @return The LinearLayout in which the ListView is held.
+ * Override to return false if you don't want the activity to register a default item click
+ * listener that redirects clicks to {@link #positionClicked(int)}.
*/
- protected LinearLayout getListViewContainer() {
- return mLinearLayout;
- }
-
- /**
- * Attaches a long press listener. You can find out which views were clicked by calling
- * {@link #getLongClickedPosition()}.
- */
- public void enableLongPress() {
- mListView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
- public boolean onItemLongClick(AdapterView parent, View v, int position, long id) {
- positionLongClicked(position);
- return true;
- }
- });
+ protected boolean shouldRegisterItemClickListener() {
+ return true;
}
/**
diff --git a/tests/tests/widget/src/android/widget/cts/util/TestUtils.java b/tests/tests/widget/src/android/widget/cts/util/TestUtils.java
index 8d8f573..97d90f6 100644
--- a/tests/tests/widget/src/android/widget/cts/util/TestUtils.java
+++ b/tests/tests/widget/src/android/widget/cts/util/TestUtils.java
@@ -16,20 +16,36 @@
package android.widget.cts.util;
+import android.annotation.ColorInt;
+import android.annotation.DrawableRes;
+import android.annotation.NonNull;
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.cts.util.WidgetTestUtils;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
+import android.graphics.Rect;
+import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
-import android.support.annotation.ColorInt;
-import android.support.annotation.NonNull;
import android.util.Pair;
+import android.util.SparseBooleanArray;
import android.view.View;
import android.view.ViewParent;
+import android.widget.TextView;
+
import junit.framework.Assert;
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.Description;
+
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
+import static org.junit.Assert.assertNull;
+import static org.mockito.Matchers.argThat;
+
public class TestUtils {
/**
* This method takes a view and returns a single bitmap that is the layered combination
@@ -100,7 +116,8 @@
}
/**
- * Checks whether all the pixels in the specified View are of the same specified color.
+ * Checks whether all the pixels in the specified of the {@link View} are
+ * filled with the specific color.
*
* In case there is a color mismatch, the behavior of this method depends on the
* <code>throwExceptionIfFails</code> parameter. If it is <code>true</code>, this method will
@@ -109,7 +126,26 @@
*/
public static void assertAllPixelsOfColor(String failMessagePrefix, @NonNull View view,
@ColorInt int color, int allowedComponentVariance, boolean throwExceptionIfFails) {
+ assertRegionPixelsOfColor(failMessagePrefix, view,
+ new Rect(0, 0, view.getWidth(), view.getHeight()),
+ color, allowedComponentVariance, throwExceptionIfFails);
+ }
+
+ /**
+ * Checks whether all the pixels in the specific rectangular region of the {@link View} are
+ * filled with the specific color.
+ *
+ * In case there is a color mismatch, the behavior of this method depends on the
+ * <code>throwExceptionIfFails</code> parameter. If it is <code>true</code>, this method will
+ * throw an <code>Exception</code> describing the mismatch. Otherwise this method will call
+ * <code>Assert.fail</code> with detailed description of the mismatch.
+ */
+ public static void assertRegionPixelsOfColor(String failMessagePrefix, @NonNull View view,
+ @NonNull Rect region, @ColorInt int color, int allowedComponentVariance,
+ boolean throwExceptionIfFails) {
// Create a bitmap
+ final int viewWidth = view.getWidth();
+ final int viewHeight = view.getHeight();
Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(),
Bitmap.Config.ARGB_8888);
// Create a canvas that wraps the bitmap
@@ -118,7 +154,7 @@
view.draw(canvas);
try {
- assertAllPixelsOfColor(failMessagePrefix, bitmap, view.getWidth(), view.getHeight(),
+ assertAllPixelsOfColor(failMessagePrefix, bitmap, region,
color, allowedComponentVariance, throwExceptionIfFails);
} finally {
bitmap.recycle();
@@ -126,7 +162,8 @@
}
/**
- * Checks whether all the pixels in the specified drawable are of the same specified color.
+ * Checks whether all the pixels in the specified {@link Drawable} are filled with the specific
+ * color.
*
* In case there is a color mismatch, the behavior of this method depends on the
* <code>throwExceptionIfFails</code> parameter. If it is <code>true</code>, this method will
@@ -136,20 +173,25 @@
public static void assertAllPixelsOfColor(String failMessagePrefix, @NonNull Drawable drawable,
int drawableWidth, int drawableHeight, boolean callSetBounds, @ColorInt int color,
int allowedComponentVariance, boolean throwExceptionIfFails) {
- // Create a bitmap
- Bitmap bitmap = Bitmap.createBitmap(drawableWidth, drawableHeight,
- Bitmap.Config.ARGB_8888);
- // Create a canvas that wraps the bitmap
- Canvas canvas = new Canvas(bitmap);
- if (callSetBounds) {
- // Configure the drawable to have bounds that match the passed size
- drawable.setBounds(0, 0, drawableWidth, drawableHeight);
- }
- // And ask the drawable to draw itself to the canvas / bitmap
- drawable.draw(canvas);
+ // Create a bitmap
+ Bitmap bitmap = Bitmap.createBitmap(drawableWidth, drawableHeight,
+ Bitmap.Config.ARGB_8888);
+ // Create a canvas that wraps the bitmap
+ Canvas canvas = new Canvas(bitmap);
+ if (callSetBounds) {
+ // Configure the drawable to have bounds that match the passed size
+ drawable.setBounds(0, 0, drawableWidth, drawableHeight);
+ } else {
+ // Query the current bounds of the drawable for translation
+ Rect drawableBounds = drawable.getBounds();
+ canvas.translate(-drawableBounds.left, -drawableBounds.top);
+ }
+ // And ask the drawable to draw itself to the canvas / bitmap
+ drawable.draw(canvas);
try {
- assertAllPixelsOfColor(failMessagePrefix, bitmap, drawableWidth, drawableHeight, color,
+ assertAllPixelsOfColor(failMessagePrefix, bitmap,
+ new Rect(0, 0, drawableWidth, drawableHeight), color,
allowedComponentVariance, throwExceptionIfFails);
} finally {
bitmap.recycle();
@@ -157,49 +199,39 @@
}
/**
- * Checks whether all the pixels in the specified bitmap are of the same specified color.
+ * Checks whether all the pixels in the specific rectangular region of the bitmap are filled
+ * with the specific color.
*
* In case there is a color mismatch, the behavior of this method depends on the
* <code>throwExceptionIfFails</code> parameter. If it is <code>true</code>, this method will
* throw an <code>Exception</code> describing the mismatch. Otherwise this method will call
* <code>Assert.fail</code> with detailed description of the mismatch.
*/
- public static void assertAllPixelsOfColor(String failMessagePrefix, @NonNull Bitmap bitmap,
- int bitmapWidth, int bitmapHeight, @ColorInt int color,
- int allowedComponentVariance, boolean throwExceptionIfFails) {
- int[] rowPixels = new int[bitmapWidth];
- for (int row = 0; row < bitmapHeight; row++) {
+ private static void assertAllPixelsOfColor(String failMessagePrefix, @NonNull Bitmap bitmap,
+ @NonNull Rect region, @ColorInt int color, int allowedComponentVariance,
+ boolean throwExceptionIfFails) {
+ final int bitmapWidth = bitmap.getWidth();
+ final int bitmapHeight = bitmap.getHeight();
+ final int[] rowPixels = new int[bitmapWidth];
+
+ final int startRow = region.top;
+ final int endRow = region.bottom;
+ final int startColumn = region.left;
+ final int endColumn = region.right;
+
+ for (int row = startRow; row < endRow; row++) {
bitmap.getPixels(rowPixels, 0, bitmapWidth, 0, row, bitmapWidth, 1);
- for (int column = 0; column < bitmapWidth; column++) {
- int sourceAlpha = Color.alpha(rowPixels[column]);
- int sourceRed = Color.red(rowPixels[column]);
- int sourceGreen = Color.green(rowPixels[column]);
- int sourceBlue = Color.blue(rowPixels[column]);
-
- int expectedAlpha = Color.alpha(color);
- int expectedRed = Color.red(color);
- int expectedGreen = Color.green(color);
- int expectedBlue = Color.blue(color);
-
- int varianceAlpha = Math.abs(sourceAlpha - expectedAlpha);
- int varianceRed = Math.abs(sourceRed - expectedRed);
- int varianceGreen = Math.abs(sourceGreen - expectedGreen);
- int varianceBlue = Math.abs(sourceBlue - expectedBlue);
-
- boolean isColorMatch = (varianceAlpha <= allowedComponentVariance)
- && (varianceRed <= allowedComponentVariance)
- && (varianceGreen <= allowedComponentVariance)
- && (varianceBlue <= allowedComponentVariance);
-
- if (!isColorMatch) {
+ for (int column = startColumn; column < endColumn; column++) {
+ @ColorInt int colorAtCurrPixel = rowPixels[column];
+ if (!areColorsTheSameWithTolerance(color, colorAtCurrPixel,
+ allowedComponentVariance)) {
String mismatchDescription = failMessagePrefix
- + ": expected all drawable colors to be ["
- + expectedAlpha + "," + expectedRed + ","
- + expectedGreen + "," + expectedBlue
- + "] but at position (" + row + "," + column + ") out of ("
- + bitmapWidth + "," + bitmapHeight + ") found ["
- + sourceAlpha + "," + sourceRed + ","
- + sourceGreen + "," + sourceBlue + "]";
+ + ": expected all bitmap colors in rectangle [l="
+ + startColumn + ", t=" + startRow + ", r=" + endColumn
+ + ", b=" + endRow + "] to be " + formatColorToHex(color)
+ + " but at position (" + row + "," + column + ") out of ("
+ + bitmapWidth + "," + bitmapHeight + ") found "
+ + formatColorToHex(colorAtCurrPixel);
if (throwExceptionIfFails) {
throw new RuntimeException(mismatchDescription);
} else {
@@ -209,4 +241,247 @@
}
}
}
+
+ /**
+ * Checks whether the center pixel in the specified bitmap is of the same specified color.
+ *
+ * In case there is a color mismatch, the behavior of this method depends on the
+ * <code>throwExceptionIfFails</code> parameter. If it is <code>true</code>, this method will
+ * throw an <code>Exception</code> describing the mismatch. Otherwise this method will call
+ * <code>Assert.fail</code> with detailed description of the mismatch.
+ */
+ public static void assertCenterPixelOfColor(String failMessagePrefix, @NonNull Bitmap bitmap,
+ @ColorInt int color,
+ int allowedComponentVariance, boolean throwExceptionIfFails) {
+ final int centerX = bitmap.getWidth() / 2;
+ final int centerY = bitmap.getHeight() / 2;
+ final @ColorInt int colorAtCenterPixel = bitmap.getPixel(centerX, centerY);
+ if (!areColorsTheSameWithTolerance(color, colorAtCenterPixel,
+ allowedComponentVariance)) {
+ String mismatchDescription = failMessagePrefix
+ + ": expected all drawable colors to be "
+ + formatColorToHex(color)
+ + " but at position (" + centerX + "," + centerY + ") out of ("
+ + bitmap.getWidth() + "," + bitmap.getHeight() + ") found"
+ + formatColorToHex(colorAtCenterPixel);
+ if (throwExceptionIfFails) {
+ throw new RuntimeException(mismatchDescription);
+ } else {
+ Assert.fail(mismatchDescription);
+ }
+ }
+ }
+
+ /**
+ * Formats the passed integer-packed color into the #AARRGGBB format.
+ */
+ public static String formatColorToHex(@ColorInt int color) {
+ return String.format("#%08X", (0xFFFFFFFF & color));
+ }
+
+ /**
+ * Compares two integer-packed colors to be equal, each component within the specified
+ * allowed variance. Returns <code>true</code> if the two colors are sufficiently equal
+ * and <code>false</code> otherwise.
+ */
+ private static boolean areColorsTheSameWithTolerance(@ColorInt int expectedColor,
+ @ColorInt int actualColor, int allowedComponentVariance) {
+ int sourceAlpha = Color.alpha(actualColor);
+ int sourceRed = Color.red(actualColor);
+ int sourceGreen = Color.green(actualColor);
+ int sourceBlue = Color.blue(actualColor);
+
+ int expectedAlpha = Color.alpha(expectedColor);
+ int expectedRed = Color.red(expectedColor);
+ int expectedGreen = Color.green(expectedColor);
+ int expectedBlue = Color.blue(expectedColor);
+
+ int varianceAlpha = Math.abs(sourceAlpha - expectedAlpha);
+ int varianceRed = Math.abs(sourceRed - expectedRed);
+ int varianceGreen = Math.abs(sourceGreen - expectedGreen);
+ int varianceBlue = Math.abs(sourceBlue - expectedBlue);
+
+ boolean isColorMatch = (varianceAlpha <= allowedComponentVariance)
+ && (varianceRed <= allowedComponentVariance)
+ && (varianceGreen <= allowedComponentVariance)
+ && (varianceBlue <= allowedComponentVariance);
+
+ return isColorMatch;
+ }
+
+ /**
+ * Composite two potentially translucent colors over each other and returns the result.
+ */
+ public static int compositeColors(@ColorInt int foreground, @ColorInt int background) {
+ int bgAlpha = Color.alpha(background);
+ int fgAlpha = Color.alpha(foreground);
+ int a = compositeAlpha(fgAlpha, bgAlpha);
+
+ int r = compositeComponent(Color.red(foreground), fgAlpha,
+ Color.red(background), bgAlpha, a);
+ int g = compositeComponent(Color.green(foreground), fgAlpha,
+ Color.green(background), bgAlpha, a);
+ int b = compositeComponent(Color.blue(foreground), fgAlpha,
+ Color.blue(background), bgAlpha, a);
+
+ return Color.argb(a, r, g, b);
+ }
+
+ private static int compositeAlpha(int foregroundAlpha, int backgroundAlpha) {
+ return 0xFF - (((0xFF - backgroundAlpha) * (0xFF - foregroundAlpha)) / 0xFF);
+ }
+
+ private static int compositeComponent(int fgC, int fgA, int bgC, int bgA, int a) {
+ if (a == 0) return 0;
+ return ((0xFF * fgC * fgA) + (bgC * bgA * (0xFF - fgA))) / (a * 0xFF);
+ }
+
+ public static ColorStateList colorStateListOf(final @ColorInt int color) {
+ return argThat(new BaseMatcher<ColorStateList>() {
+ @Override
+ public boolean matches(Object o) {
+ if (o instanceof ColorStateList) {
+ final ColorStateList actual = (ColorStateList) o;
+ return (actual.getColors().length == 1) && (actual.getDefaultColor() == color);
+ }
+ return false;
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("doesn't match " + formatColorToHex(color));
+ }
+ });
+ }
+
+ public static int dpToPx(Context context, int dp) {
+ final float density = context.getResources().getDisplayMetrics().density;
+ return (int) (dp * density + 0.5f);
+ }
+
+ private static String arrayToString(final long[] array) {
+ final StringBuffer buffer = new StringBuffer();
+ if (array == null) {
+ buffer.append("null");
+ } else {
+ buffer.append("[");
+ for (int i = 0; i < array.length; i++) {
+ if (i > 0) {
+ buffer.append(", ");
+ }
+ buffer.append(array[i]);
+ }
+ buffer.append("]");
+ }
+ return buffer.toString();
+ }
+
+ public static void assertIdentical(final long[] expected, final long[] actual) {
+ if (!Arrays.equals(expected, actual)) {
+ Assert.fail("Expected " + arrayToString(expected) + ", actual "
+ + arrayToString(actual));
+ }
+ }
+
+ public static void assertTrueValuesAtPositions(final long[] expectedIndexesForTrueValues,
+ final SparseBooleanArray array) {
+ if (array == null) {
+ if ((expectedIndexesForTrueValues != null)
+ && (expectedIndexesForTrueValues.length > 0)) {
+ Assert.fail("Expected " + arrayToString(expectedIndexesForTrueValues)
+ + ", actual [null]");
+ }
+ return;
+ }
+
+ final int totalValuesCount = array.size();
+ // "Convert" the input array into a long[] array that has indexes of true values
+ int trueValuesCount = 0;
+ for (int i = 0; i < totalValuesCount; i++) {
+ if (array.valueAt(i)) {
+ trueValuesCount++;
+ }
+ }
+
+ final long[] trueValuePositions = new long[trueValuesCount];
+ int position = 0;
+ for (int i = 0; i < totalValuesCount; i++) {
+ if (array.valueAt(i)) {
+ trueValuePositions[position++] = array.keyAt(i);
+ }
+ }
+
+ Arrays.sort(trueValuePositions);
+ assertIdentical(expectedIndexesForTrueValues, trueValuePositions);
+ }
+
+ public static Drawable getDrawable(Context context, @DrawableRes int resid) {
+ return context.getResources().getDrawable(resid);
+ }
+
+ public static Bitmap getBitmap(Context context, @DrawableRes int resid) {
+ return ((BitmapDrawable) getDrawable(context, resid)).getBitmap();
+ }
+
+ public static void verifyCompoundDrawables(@NonNull TextView textView,
+ @DrawableRes int expectedLeftDrawableId, @DrawableRes int expectedRightDrawableId,
+ @DrawableRes int expectedTopDrawableId, @DrawableRes int expectedBottomDrawableId) {
+ final Context context = textView.getContext();
+ final Drawable[] compoundDrawables = textView.getCompoundDrawables();
+ if (expectedLeftDrawableId < 0) {
+ assertNull(compoundDrawables[0]);
+ } else {
+ WidgetTestUtils.assertEquals(getBitmap(context, expectedLeftDrawableId),
+ ((BitmapDrawable) compoundDrawables[0]).getBitmap());
+ }
+ if (expectedTopDrawableId < 0) {
+ assertNull(compoundDrawables[1]);
+ } else {
+ WidgetTestUtils.assertEquals(getBitmap(context, expectedTopDrawableId),
+ ((BitmapDrawable) compoundDrawables[1]).getBitmap());
+ }
+ if (expectedRightDrawableId < 0) {
+ assertNull(compoundDrawables[2]);
+ } else {
+ WidgetTestUtils.assertEquals(getBitmap(context, expectedRightDrawableId),
+ ((BitmapDrawable) compoundDrawables[2]).getBitmap());
+ }
+ if (expectedBottomDrawableId < 0) {
+ assertNull(compoundDrawables[3]);
+ } else {
+ WidgetTestUtils.assertEquals(getBitmap(context, expectedBottomDrawableId),
+ ((BitmapDrawable) compoundDrawables[3]).getBitmap());
+ }
+ }
+
+ public static void verifyCompoundDrawablesRelative(@NonNull TextView textView,
+ @DrawableRes int expectedStartDrawableId, @DrawableRes int expectedEndDrawableId,
+ @DrawableRes int expectedTopDrawableId, @DrawableRes int expectedBottomDrawableId) {
+ final Context context = textView.getContext();
+ final Drawable[] compoundDrawablesRelative = textView.getCompoundDrawablesRelative();
+ if (expectedStartDrawableId < 0) {
+ assertNull(compoundDrawablesRelative[0]);
+ } else {
+ WidgetTestUtils.assertEquals(getBitmap(context, expectedStartDrawableId),
+ ((BitmapDrawable) compoundDrawablesRelative[0]).getBitmap());
+ }
+ if (expectedTopDrawableId < 0) {
+ assertNull(compoundDrawablesRelative[1]);
+ } else {
+ WidgetTestUtils.assertEquals(getBitmap(context, expectedTopDrawableId),
+ ((BitmapDrawable) compoundDrawablesRelative[1]).getBitmap());
+ }
+ if (expectedEndDrawableId < 0) {
+ assertNull(compoundDrawablesRelative[2]);
+ } else {
+ WidgetTestUtils.assertEquals(getBitmap(context, expectedEndDrawableId),
+ ((BitmapDrawable) compoundDrawablesRelative[2]).getBitmap());
+ }
+ if (expectedBottomDrawableId < 0) {
+ assertNull(compoundDrawablesRelative[3]);
+ } else {
+ WidgetTestUtils.assertEquals(getBitmap(context, expectedBottomDrawableId),
+ ((BitmapDrawable) compoundDrawablesRelative[3]).getBitmap());
+ }
+ }
}
\ No newline at end of file
diff --git a/tests/tests/widget/src/android/widget/cts/util/TestUtilsMatchers.java b/tests/tests/widget/src/android/widget/cts/util/TestUtilsMatchers.java
new file mode 100644
index 0000000..3664bc4
--- /dev/null
+++ b/tests/tests/widget/src/android/widget/cts/util/TestUtilsMatchers.java
@@ -0,0 +1,59 @@
+/*
+ * 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.widget.cts.util;
+
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.hamcrest.TypeSafeMatcher;
+
+import java.util.List;
+
+public class TestUtilsMatchers {
+ /**
+ * Returns a matcher that matches lists of int values that are in ascending order.
+ */
+ public static Matcher<List<Integer>> inAscendingOrder() {
+ return new TypeSafeMatcher<List<Integer>>() {
+ private String mFailedDescription;
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText(mFailedDescription);
+ }
+
+ @Override
+ protected boolean matchesSafely(List<Integer> item) {
+ int itemCount = item.size();
+
+ if (itemCount >= 2) {
+ for (int i = 0; i < itemCount - 1; i++) {
+ int curr = item.get(i);
+ int next = item.get(i + 1);
+
+ if (curr > next) {
+ mFailedDescription = "Values should increase between #" + i +
+ ":" + curr + " and #" + (i + 1) + ":" + next;
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+ };
+ }
+}
diff --git a/tests/tests/widget/src/android/widget/cts/util/ViewTestUtils.java b/tests/tests/widget/src/android/widget/cts/util/ViewTestUtils.java
deleted file mode 100644
index e9ef867..0000000
--- a/tests/tests/widget/src/android/widget/cts/util/ViewTestUtils.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * 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.widget.cts.util;
-
-import junit.framework.Assert;
-
-import android.app.Instrumentation;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.view.View;
-import android.view.ViewTreeObserver;
-import android.view.ViewTreeObserver.OnDrawListener;
-
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Utilities for testing View behavior.
- */
-public class ViewTestUtils {
-
- /**
- * Runs the specified Runnable on the main thread and ensures that the
- * specified View's tree is drawn before returning.
- *
- * @param instrumentation the instrumentation used to run the test
- * @param view the view whose tree should be drawn before returning
- * @param runner the runnable to run on the main thread, or {@code null} to
- * simply force invalidation and a draw pass
- */
- public static void runOnMainAndDrawSync(@NonNull Instrumentation instrumentation,
- @NonNull final View view, @Nullable final Runnable runner) {
- final CountDownLatch latch = new CountDownLatch(1);
-
- instrumentation.runOnMainSync(() -> {
- final ViewTreeObserver observer = view.getViewTreeObserver();
- final OnDrawListener listener = new OnDrawListener() {
- @Override
- public void onDraw() {
- observer.removeOnDrawListener(this);
- view.post(() -> latch.countDown());
- }
- };
-
- observer.addOnDrawListener(listener);
-
- if (runner != null) {
- runner.run();
- } else {
- view.invalidate();
- }
- });
-
- try {
- Assert.assertTrue("Expected draw pass occurred within 5 seconds",
- latch.await(5, TimeUnit.SECONDS));
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
- }
-}
diff --git a/tools/cts-tradefed/res/config/cts-known-failures.xml b/tools/cts-tradefed/res/config/cts-known-failures.xml
index e7b08cb..3e4b9dd 100644
--- a/tools/cts-tradefed/res/config/cts-known-failures.xml
+++ b/tools/cts-tradefed/res/config/cts-known-failures.xml
@@ -42,17 +42,6 @@
<option name="compatibility:exclude-filter" value="CtsAppWidgetTestCases android.appwidget.cts.AppWidgetTest#testUpdateAppWidgetViaWidgetId" />
<option name="compatibility:exclude-filter" value="CtsAppWidgetTestCases android.appwidget.cts.AppWidgetTest#testUpdateAppWidgetViaWidgetIds" />
- <!-- b/21668302 -->
- <option name="compatibility:exclude-filter" value="CtsAssistTestCases android.assist.cts.AssistantContentViewTest#testAssistantContentViewDimens" />
- <option name="compatibility:exclude-filter" value="CtsAssistTestCases android.assist.cts.ExtraAssistDataTest#testAssistContentAndAssistData" />
- <option name="compatibility:exclude-filter" value="CtsAssistTestCases android.assist.cts.FocusChangeTest#testLayerCausesUnderlyingActivityToLoseFocus" />
- <option name="compatibility:exclude-filter" value="CtsAssistTestCases android.assist.cts.LargeViewHierarchyTest#testTextView" />
- <option name="compatibility:exclude-filter" value="CtsAssistTestCases android.assist.cts.ScreenshotTest#testBlueScreenshot" />
- <option name="compatibility:exclude-filter" value="CtsAssistTestCases android.assist.cts.ScreenshotTest#testGreenScreenshot" />
- <option name="compatibility:exclude-filter" value="CtsAssistTestCases android.assist.cts.ScreenshotTest#testRedScreenshot" />
- <option name="compatibility:exclude-filter" value="CtsAssistTestCases android.assist.cts.TextViewTest#testTextView" />
- <option name="compatibility:exclude-filter" value="CtsAssistTestCases android.assist.cts.WebViewTest#testWebView" />
-
<!-- b/23776099 -->
<option name="compatibility:exclude-filter" value="CtsCallLogTestCases" />
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/Abi.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/Abi.java
deleted file mode 100644
index 926e14b..0000000
--- a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/Abi.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2014 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 com.android.cts.tradefed.testtype;
-
-import com.android.tradefed.testtype.IAbi;
-/**
- * A class representing an ABI.
- */
-public class Abi implements IAbi {
-
- private String mName;
- private String mBitness;
-
- public Abi(String name, String bitness) {
- mName = name;
- mBitness = bitness;
- }
-
- @Override
- public String getName() {
- return mName;
- }
-
- @Override
- public String getBitness() {
- return mBitness;
- }
-
-}
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestPackageXmlParser.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestPackageXmlParser.java
index acd977e..c32f632 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestPackageXmlParser.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestPackageXmlParser.java
@@ -18,6 +18,7 @@
import com.android.compatibility.common.util.AbiUtils;
import com.android.ddmlib.Log;
import com.android.ddmlib.testrunner.TestIdentifier;
+import com.android.tradefed.testtype.Abi;
import com.android.tradefed.util.xml.AbstractXmlParser;
import org.xml.sax.Attributes;
diff --git a/tools/tradefed-host/tests/src/com/android/cts/tradefed/UnitTests.java b/tools/tradefed-host/tests/src/com/android/cts/tradefed/UnitTests.java
index 29c1324..fce7604 100644
--- a/tools/tradefed-host/tests/src/com/android/cts/tradefed/UnitTests.java
+++ b/tools/tradefed-host/tests/src/com/android/cts/tradefed/UnitTests.java
@@ -22,7 +22,6 @@
import com.android.cts.tradefed.result.TestSummaryXmlTest;
import com.android.cts.tradefed.result.TestTest;
import com.android.cts.tradefed.result.TestLogTest;
-import com.android.cts.tradefed.testtype.Abi;
import com.android.cts.tradefed.testtype.CtsTestTest;
import com.android.cts.tradefed.testtype.DeqpTestRunnerTest;
import com.android.cts.tradefed.testtype.GeeTestTest;
@@ -32,6 +31,7 @@
import com.android.cts.tradefed.testtype.TestPackageXmlParserTest;
import com.android.cts.tradefed.testtype.TestPlanTest;
import com.android.cts.tradefed.testtype.WrappedGTestResultParserTest;
+import com.android.tradefed.testtype.Abi;
import com.android.tradefed.testtype.IAbi;
import junit.framework.Test;
diff --git a/tools/utils/buildCts.py b/tools/utils/buildCts.py
index f983b15..bd5fc90 100755
--- a/tools/utils/buildCts.py
+++ b/tools/utils/buildCts.py
@@ -522,15 +522,6 @@
'android.alarmclock.cts.SetAlarmTest#testAll',
'android.alarmclock.cts.SnoozeAlarmTest#testAll',
],
- 'android.assist' : [
- 'android.assist.cts.AssistantContentViewTest',
- 'android.assist.cts.ExtraAssistDataTest',
- 'android.assist.cts.FocusChangeTest',
- 'android.assist.cts.LargeViewHierarchyTest',
- 'android.assist.cts.ScreenshotTest',
- 'android.assist.cts.TextViewTest',
- 'android.assist.cts.WebViewTest',
- ],
'android.calllog' : [
'android.calllog.cts.CallLogBackupTest#testSingleCallBackup',
],