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