1/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package com.android.messaging.datamodel.media;
17
18import android.content.Context;
19import android.graphics.RectF;
20import android.net.Uri;
21
22import com.android.messaging.util.Assert;
23import com.android.messaging.util.AvatarUriUtil;
24
25import java.util.ArrayList;
26import java.util.Arrays;
27import java.util.List;
28
29public class AvatarGroupRequestDescriptor extends CompositeImageRequestDescriptor {
30    private static final int MAX_GROUP_SIZE = 4;
31
32    public AvatarGroupRequestDescriptor(final Uri uri, final int desiredWidth,
33            final int desiredHeight) {
34        this(convertToDescriptor(uri, desiredWidth, desiredHeight), desiredWidth, desiredHeight);
35    }
36
37    public AvatarGroupRequestDescriptor(final List<? extends ImageRequestDescriptor> descriptors,
38            final int desiredWidth, final int desiredHeight) {
39        super(descriptors, desiredWidth, desiredHeight);
40        Assert.isTrue(descriptors.size() <= MAX_GROUP_SIZE);
41    }
42
43    private static List<? extends ImageRequestDescriptor> convertToDescriptor(final Uri uri,
44            final int desiredWidth, final int desiredHeight) {
45        final List<String> participantUriStrings = AvatarUriUtil.getGroupParticipantUris(uri);
46        final List<AvatarRequestDescriptor> avatarDescriptors =
47                new ArrayList<AvatarRequestDescriptor>(participantUriStrings.size());
48        for (final String uriString : participantUriStrings) {
49            final AvatarRequestDescriptor descriptor = new AvatarRequestDescriptor(
50                    Uri.parse(uriString), desiredWidth, desiredHeight);
51            avatarDescriptors.add(descriptor);
52        }
53        return avatarDescriptors;
54    }
55
56    @Override
57    public CompositeImageRequest<?> buildBatchImageRequest(final Context context) {
58        return new CompositeImageRequest<AvatarGroupRequestDescriptor>(context, this);
59    }
60
61    @Override
62    public List<RectF> getChildRequestTargetRects() {
63        return Arrays.asList(generateDestRectArray());
64    }
65
66    /**
67     * Generates an array of {@link RectF} which represents where each of the individual avatar
68     * should be located in the final group avatar image. The location of each avatar depends on
69     * the size of the group and the size of the overall group avatar size.
70     */
71    private RectF[] generateDestRectArray() {
72        final int groupSize = mDescriptors.size();
73        final float width = desiredWidth;
74        final float height = desiredHeight;
75        final float halfWidth = width / 2F;
76        final float halfHeight = height / 2F;
77        final RectF[] destArray = new RectF[groupSize];
78        switch (groupSize) {
79            case 2:
80                /**
81                 * +-------+
82                 * | 0 |   |
83                 * +-------+
84                 * |   | 1 |
85                 * +-------+
86                 *
87                 * We want two circles which touches in the center. To get this we know that the
88                 * diagonal of the overall group avatar is squareRoot(2) * w We also know that the
89                 * two circles touches the at the center of the overall group avatar and the
90                 * distance from the center of the circle to the corner of the group avatar is
91                 * radius * squareRoot(2). Therefore, the following emerges.
92                 *
93                 * w * squareRoot(2) = 2 (radius + radius * squareRoot(2))
94                 * Solving for radius we get:
95                 * d = 2 * radius = ( squareRoot(2) / (squareRoot(2) + 1)) * w
96                 * d = (2 - squareRoot(2)) * w
97                 */
98                final float diameter = (float) ((2 - Math.sqrt(2)) * width);
99                destArray[0] = new RectF(0, 0, diameter, diameter);
100                destArray[1] = new RectF(width - diameter, height - diameter, width, height);
101                break;
102            case 3:
103                /**
104                 * +-------+
105                 * | | 0 | |
106                 * +-------+
107                 * | 1 | 2 |
108                 * +-------+
109                 *   i0
110                 *   |\
111                 * a | \ c
112                 *   --- i2
113                 *    b
114                 *
115                 * a = radius * squareRoot(3) due to the triangle being a 30-60-90 right triangle.
116                 * b = radius of circle
117                 * c = 2 * radius of circle
118                 *
119                 * All three of the images are circles and therefore image zero will not touch
120                 * image one or image two. Move image zero down so it touches image one and image
121                 * two. This can be done by keeping image zero in the center and moving it down
122                 * slightly. The amount to move down can be calculated by solving a right triangle.
123                 * We know that the center x of image two to the center x of image zero is the
124                 * radius of the circle, this is the length of edge b. Also we know that the
125                 * distance from image zero to image two's center is 2 * radius, edge c. From this
126                 * we know that the distance from center y of image two to center y of image one,
127                 * edge a, is equal to radius * squareRoot(3) due to this triangle being a 30-60-90
128                 * right triangle.
129                 */
130                final float quarterWidth = width / 4F;
131                final float threeQuarterWidth = 3 * quarterWidth;
132                final float radius = height / 4F;
133                final float imageTwoCenterY = height - radius;
134                final float lengthOfEdgeA = (float) (radius * Math.sqrt(3));
135                final float imageZeroCenterY = imageTwoCenterY - lengthOfEdgeA;
136                final float imageZeroTop = imageZeroCenterY - radius;
137                final float imageZeroBottom = imageZeroCenterY + radius;
138                destArray[0] = new RectF(
139                        quarterWidth, imageZeroTop, threeQuarterWidth, imageZeroBottom);
140                destArray[1] = new RectF(0, halfHeight, halfWidth, height);
141                destArray[2] = new RectF(halfWidth, halfHeight, width, height);
142                break;
143            default:
144                /**
145                 * +-------+
146                 * | 0 | 1 |
147                 * +-------+
148                 * | 2 | 3 |
149                 * +-------+
150                 */
151                destArray[0] = new RectF(0, 0, halfWidth, halfHeight);
152                destArray[1] = new RectF(halfWidth, 0, width, halfHeight);
153                destArray[2] = new RectF(0, halfHeight, halfWidth, height);
154                destArray[3] = new RectF(halfWidth, halfHeight, width, height);
155                break;
156        }
157        return destArray;
158    }
159}
160