1 /* 2 * Copyright (C) 2011 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 17 package com.android.dialer.calllog; 18 19 import android.content.Context; 20 import android.graphics.Bitmap; 21 import android.graphics.BitmapFactory; 22 import android.graphics.Canvas; 23 import android.graphics.PorterDuff; 24 import android.graphics.drawable.BitmapDrawable; 25 import android.graphics.drawable.Drawable; 26 import android.util.AttributeSet; 27 import android.view.View; 28 29 import com.android.contacts.common.testing.NeededForTesting; 30 import com.android.contacts.common.util.BitmapUtil; 31 import com.android.dialer.R; 32 import com.android.dialer.util.AppCompatConstants; 33 import com.google.common.collect.Lists; 34 35 import java.util.List; 36 37 /** 38 * View that draws one or more symbols for different types of calls (missed calls, outgoing etc). 39 * The symbols are set up horizontally. As this view doesn't create subviews, it is better suited 40 * for ListView-recycling that a regular LinearLayout using ImageViews. 41 */ 42 public class CallTypeIconsView extends View { 43 private List<Integer> mCallTypes = Lists.newArrayListWithCapacity(3); 44 private boolean mShowVideo = false; 45 private int mWidth; 46 private int mHeight; 47 48 private static Resources sResources; 49 CallTypeIconsView(Context context)50 public CallTypeIconsView(Context context) { 51 this(context, null); 52 } 53 CallTypeIconsView(Context context, AttributeSet attrs)54 public CallTypeIconsView(Context context, AttributeSet attrs) { 55 super(context, attrs); 56 if (sResources == null) { 57 sResources = new Resources(context); 58 } 59 } 60 clear()61 public void clear() { 62 mCallTypes.clear(); 63 mWidth = 0; 64 mHeight = 0; 65 invalidate(); 66 } 67 add(int callType)68 public void add(int callType) { 69 mCallTypes.add(callType); 70 71 final Drawable drawable = getCallTypeDrawable(callType); 72 mWidth += drawable.getIntrinsicWidth() + sResources.iconMargin; 73 mHeight = Math.max(mHeight, drawable.getIntrinsicHeight()); 74 invalidate(); 75 } 76 77 /** 78 * Determines whether the video call icon will be shown. 79 * 80 * @param showVideo True where the video icon should be shown. 81 */ setShowVideo(boolean showVideo)82 public void setShowVideo(boolean showVideo) { 83 mShowVideo = showVideo; 84 if (showVideo) { 85 mWidth += sResources.videoCall.getIntrinsicWidth(); 86 mHeight = Math.max(mHeight, sResources.videoCall.getIntrinsicHeight()); 87 invalidate(); 88 } 89 } 90 91 /** 92 * Determines if the video icon should be shown. 93 * 94 * @return True if the video icon should be shown. 95 */ isVideoShown()96 public boolean isVideoShown() { 97 return mShowVideo; 98 } 99 100 @NeededForTesting getCount()101 public int getCount() { 102 return mCallTypes.size(); 103 } 104 105 @NeededForTesting getCallType(int index)106 public int getCallType(int index) { 107 return mCallTypes.get(index); 108 } 109 getCallTypeDrawable(int callType)110 private Drawable getCallTypeDrawable(int callType) { 111 switch (callType) { 112 case AppCompatConstants.CALLS_INCOMING_TYPE: 113 return sResources.incoming; 114 case AppCompatConstants.CALLS_OUTGOING_TYPE: 115 return sResources.outgoing; 116 case AppCompatConstants.CALLS_MISSED_TYPE: 117 return sResources.missed; 118 case AppCompatConstants.CALLS_VOICEMAIL_TYPE: 119 return sResources.voicemail; 120 case AppCompatConstants.CALLS_BLOCKED_TYPE: 121 return sResources.blocked; 122 default: 123 // It is possible for users to end up with calls with unknown call types in their 124 // call history, possibly due to 3rd party call log implementations (e.g. to 125 // distinguish between rejected and missed calls). Instead of crashing, just 126 // assume that all unknown call types are missed calls. 127 return sResources.missed; 128 } 129 } 130 131 @Override onMeasure(int widthMeasureSpec, int heightMeasureSpec)132 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 133 setMeasuredDimension(mWidth, mHeight); 134 } 135 136 @Override onDraw(Canvas canvas)137 protected void onDraw(Canvas canvas) { 138 int left = 0; 139 for (Integer callType : mCallTypes) { 140 final Drawable drawable = getCallTypeDrawable(callType); 141 final int right = left + drawable.getIntrinsicWidth(); 142 drawable.setBounds(left, 0, right, drawable.getIntrinsicHeight()); 143 drawable.draw(canvas); 144 left = right + sResources.iconMargin; 145 } 146 147 // If showing the video call icon, draw it scaled appropriately. 148 if (mShowVideo) { 149 final Drawable drawable = sResources.videoCall; 150 final int right = left + sResources.videoCall.getIntrinsicWidth(); 151 drawable.setBounds(left, 0, right, sResources.videoCall.getIntrinsicHeight()); 152 drawable.draw(canvas); 153 } 154 } 155 156 private static class Resources { 157 158 // Drawable representing an incoming answered call. 159 public final Drawable incoming; 160 161 // Drawable respresenting an outgoing call. 162 public final Drawable outgoing; 163 164 // Drawable representing an incoming missed call. 165 public final Drawable missed; 166 167 // Drawable representing a voicemail. 168 public final Drawable voicemail; 169 170 // Drawable representing a blocked call. 171 public final Drawable blocked; 172 173 // Drawable repesenting a video call. 174 public final Drawable videoCall; 175 176 /** 177 * The margin to use for icons. 178 */ 179 public final int iconMargin; 180 181 /** 182 * Configures the call icon drawables. 183 * A single white call arrow which points down and left is used as a basis for all of the 184 * call arrow icons, applying rotation and colors as needed. 185 * 186 * @param context The current context. 187 */ Resources(Context context)188 public Resources(Context context) { 189 final android.content.res.Resources r = context.getResources(); 190 191 incoming = r.getDrawable(R.drawable.ic_call_arrow); 192 incoming.setColorFilter(r.getColor(R.color.answered_call), PorterDuff.Mode.MULTIPLY); 193 194 // Create a rotated instance of the call arrow for outgoing calls. 195 outgoing = BitmapUtil.getRotatedDrawable(r, R.drawable.ic_call_arrow, 180f); 196 outgoing.setColorFilter(r.getColor(R.color.answered_call), PorterDuff.Mode.MULTIPLY); 197 198 // Need to make a copy of the arrow drawable, otherwise the same instance colored 199 // above will be recolored here. 200 missed = r.getDrawable(R.drawable.ic_call_arrow).mutate(); 201 missed.setColorFilter(r.getColor(R.color.missed_call), PorterDuff.Mode.MULTIPLY); 202 203 voicemail = r.getDrawable(R.drawable.ic_call_voicemail_holo_dark); 204 205 blocked = getScaledBitmap(context, R.drawable.ic_block_24dp); 206 blocked.setColorFilter(r.getColor(R.color.blocked_call), PorterDuff.Mode.MULTIPLY); 207 208 videoCall = getScaledBitmap(context, R.drawable.ic_videocam_24dp); 209 videoCall.setColorFilter(r.getColor(R.color.dialtacts_secondary_text_color), 210 PorterDuff.Mode.MULTIPLY); 211 212 iconMargin = r.getDimensionPixelSize(R.dimen.call_log_icon_margin); 213 } 214 215 // Gets the icon, scaled to the height of the call type icons. This helps display all the 216 // icons to be the same height, while preserving their width aspect ratio. getScaledBitmap(Context context, int resourceId)217 private Drawable getScaledBitmap(Context context, int resourceId) { 218 Bitmap icon = BitmapFactory.decodeResource(context.getResources(), resourceId); 219 int scaledHeight = 220 context.getResources().getDimensionPixelSize(R.dimen.call_type_icon_size); 221 int scaledWidth = (int) ((float) icon.getWidth() 222 * ((float) scaledHeight / (float) icon.getHeight())); 223 Bitmap scaledIcon = Bitmap.createScaledBitmap(icon, scaledWidth, scaledHeight, false); 224 return new BitmapDrawable(context.getResources(), scaledIcon); 225 } 226 } 227 } 228