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