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
17package com.android.dialer.calllog;
18
19import android.content.Context;
20import android.graphics.Bitmap;
21import android.graphics.BitmapFactory;
22import android.graphics.Canvas;
23import android.graphics.PorterDuff;
24import android.graphics.drawable.BitmapDrawable;
25import android.graphics.drawable.Drawable;
26import android.provider.CallLog.Calls;
27import android.util.AttributeSet;
28import android.view.View;
29
30import com.android.contacts.common.testing.NeededForTesting;
31import com.android.contacts.common.util.BitmapUtil;
32import com.android.dialer.R;
33import com.google.common.collect.Lists;
34
35import 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 */
42public 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
49    public CallTypeIconsView(Context context) {
50        this(context, null);
51    }
52
53    public CallTypeIconsView(Context context, AttributeSet attrs) {
54        super(context, attrs);
55        mResources = new Resources(context);
56    }
57
58    public void clear() {
59        mCallTypes.clear();
60        mWidth = 0;
61        mHeight = 0;
62        invalidate();
63    }
64
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     */
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     */
93    public boolean isVideoShown() {
94        return mShowVideo;
95    }
96
97    @NeededForTesting
98    public int getCount() {
99        return mCallTypes.size();
100    }
101
102    @NeededForTesting
103    public int getCallType(int index) {
104        return mCallTypes.get(index);
105    }
106
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
127    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
128        setMeasuredDimension(mWidth, mHeight);
129    }
130
131    @Override
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         */
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