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