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  */
16 package com.android.messaging.datamodel.media;
17 
18 import android.content.Context;
19 import android.graphics.RectF;
20 import android.net.Uri;
21 
22 import com.android.messaging.util.Assert;
23 import com.android.messaging.util.AvatarUriUtil;
24 
25 import java.util.ArrayList;
26 import java.util.Arrays;
27 import java.util.List;
28 
29 public class AvatarGroupRequestDescriptor extends CompositeImageRequestDescriptor {
30     private static final int MAX_GROUP_SIZE = 4;
31 
AvatarGroupRequestDescriptor(final Uri uri, final int desiredWidth, final int desiredHeight)32     public AvatarGroupRequestDescriptor(final Uri uri, final int desiredWidth,
33             final int desiredHeight) {
34         this(convertToDescriptor(uri, desiredWidth, desiredHeight), desiredWidth, desiredHeight);
35     }
36 
AvatarGroupRequestDescriptor(final List<? extends ImageRequestDescriptor> descriptors, final int desiredWidth, final int desiredHeight)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 
convertToDescriptor(final Uri uri, final int desiredWidth, final int desiredHeight)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
buildBatchImageRequest(final Context context)57     public CompositeImageRequest<?> buildBatchImageRequest(final Context context) {
58         return new CompositeImageRequest<AvatarGroupRequestDescriptor>(context, this);
59     }
60 
61     @Override
getChildRequestTargetRects()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      */
generateDestRectArray()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