1/*
2 * Copyright (C) 2012 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.gallery3d.ui;
18
19import android.content.Context;
20import android.graphics.Bitmap;
21import android.graphics.Bitmap.Config;
22import android.graphics.BitmapFactory;
23import android.graphics.Canvas;
24import android.graphics.PorterDuff;
25import android.graphics.Typeface;
26import android.text.TextPaint;
27import android.text.TextUtils;
28
29import com.android.gallery3d.R;
30import com.android.gallery3d.data.DataSourceType;
31import com.android.photos.data.GalleryBitmapPool;
32import com.android.gallery3d.util.ThreadPool;
33import com.android.gallery3d.util.ThreadPool.JobContext;
34
35public class AlbumLabelMaker {
36    private static final int BORDER_SIZE = 0;
37
38    private final AlbumSetSlotRenderer.LabelSpec mSpec;
39    private final TextPaint mTitlePaint;
40    private final TextPaint mCountPaint;
41    private final Context mContext;
42
43    private int mLabelWidth;
44    private int mBitmapWidth;
45    private int mBitmapHeight;
46
47    private final LazyLoadedBitmap mLocalSetIcon;
48    private final LazyLoadedBitmap mPicasaIcon;
49    private final LazyLoadedBitmap mCameraIcon;
50
51    public AlbumLabelMaker(Context context, AlbumSetSlotRenderer.LabelSpec spec) {
52        mContext = context;
53        mSpec = spec;
54        mTitlePaint = getTextPaint(spec.titleFontSize, spec.titleColor, false);
55        mCountPaint = getTextPaint(spec.countFontSize, spec.countColor, false);
56
57        mLocalSetIcon = new LazyLoadedBitmap(R.drawable.frame_overlay_gallery_folder);
58        mPicasaIcon = new LazyLoadedBitmap(R.drawable.frame_overlay_gallery_picasa);
59        mCameraIcon = new LazyLoadedBitmap(R.drawable.frame_overlay_gallery_camera);
60    }
61
62    public static int getBorderSize() {
63        return BORDER_SIZE;
64    }
65
66    private Bitmap getOverlayAlbumIcon(int sourceType) {
67        switch (sourceType) {
68            case DataSourceType.TYPE_CAMERA:
69                return mCameraIcon.get();
70            case DataSourceType.TYPE_LOCAL:
71                return mLocalSetIcon.get();
72            case DataSourceType.TYPE_PICASA:
73                return mPicasaIcon.get();
74        }
75        return null;
76    }
77
78    private static TextPaint getTextPaint(int textSize, int color, boolean isBold) {
79        TextPaint paint = new TextPaint();
80        paint.setTextSize(textSize);
81        paint.setAntiAlias(true);
82        paint.setColor(color);
83        //paint.setShadowLayer(2f, 0f, 0f, Color.LTGRAY);
84        if (isBold) {
85            paint.setTypeface(Typeface.defaultFromStyle(Typeface.BOLD));
86        }
87        return paint;
88    }
89
90    private class LazyLoadedBitmap {
91        private Bitmap mBitmap;
92        private int mResId;
93
94        public LazyLoadedBitmap(int resId) {
95            mResId = resId;
96        }
97
98        public synchronized Bitmap get() {
99            if (mBitmap == null) {
100                BitmapFactory.Options options = new BitmapFactory.Options();
101                options.inPreferredConfig = Bitmap.Config.ARGB_8888;
102                mBitmap = BitmapFactory.decodeResource(
103                        mContext.getResources(), mResId, options);
104            }
105            return mBitmap;
106        }
107    }
108
109    public synchronized void setLabelWidth(int width) {
110        if (mLabelWidth == width) return;
111        mLabelWidth = width;
112        int borders = 2 * BORDER_SIZE;
113        mBitmapWidth = width + borders;
114        mBitmapHeight = mSpec.labelBackgroundHeight + borders;
115    }
116
117    public ThreadPool.Job<Bitmap> requestLabel(
118            String title, String count, int sourceType) {
119        return new AlbumLabelJob(title, count, sourceType);
120    }
121
122    static void drawText(Canvas canvas,
123            int x, int y, String text, int lengthLimit, TextPaint p) {
124        // The TextPaint cannot be used concurrently
125        synchronized (p) {
126            text = TextUtils.ellipsize(
127                    text, p, lengthLimit, TextUtils.TruncateAt.END).toString();
128            canvas.drawText(text, x, y - p.getFontMetricsInt().ascent, p);
129        }
130    }
131
132    private class AlbumLabelJob implements ThreadPool.Job<Bitmap> {
133        private final String mTitle;
134        private final String mCount;
135        private final int mSourceType;
136
137        public AlbumLabelJob(String title, String count, int sourceType) {
138            mTitle = title;
139            mCount = count;
140            mSourceType = sourceType;
141        }
142
143        @Override
144        public Bitmap run(JobContext jc) {
145            AlbumSetSlotRenderer.LabelSpec s = mSpec;
146
147            String title = mTitle;
148            String count = mCount;
149            Bitmap icon = getOverlayAlbumIcon(mSourceType);
150
151            Bitmap bitmap;
152            int labelWidth;
153
154            synchronized (this) {
155                labelWidth = mLabelWidth;
156                bitmap = GalleryBitmapPool.getInstance().get(mBitmapWidth, mBitmapHeight);
157            }
158
159            if (bitmap == null) {
160                int borders = 2 * BORDER_SIZE;
161                bitmap = Bitmap.createBitmap(labelWidth + borders,
162                        s.labelBackgroundHeight + borders, Config.ARGB_8888);
163            }
164
165            Canvas canvas = new Canvas(bitmap);
166            canvas.clipRect(BORDER_SIZE, BORDER_SIZE,
167                    bitmap.getWidth() - BORDER_SIZE,
168                    bitmap.getHeight() - BORDER_SIZE);
169            canvas.drawColor(mSpec.backgroundColor, PorterDuff.Mode.SRC);
170
171            canvas.translate(BORDER_SIZE, BORDER_SIZE);
172
173            // draw title
174            if (jc.isCancelled()) return null;
175            int x = s.leftMargin + s.iconSize;
176            // TODO: is the offset relevant in new reskin?
177            // int y = s.titleOffset;
178            int y = (s.labelBackgroundHeight - s.titleFontSize) / 2;
179            drawText(canvas, x, y, title, labelWidth - s.leftMargin - x -
180                    s.titleRightMargin, mTitlePaint);
181
182            // draw count
183            if (jc.isCancelled()) return null;
184            x = labelWidth - s.titleRightMargin;
185            y = (s.labelBackgroundHeight - s.countFontSize) / 2;
186            drawText(canvas, x, y, count,
187                    labelWidth - x , mCountPaint);
188
189            // draw the icon
190            if (icon != null) {
191                if (jc.isCancelled()) return null;
192                float scale = (float) s.iconSize / icon.getWidth();
193                canvas.translate(s.leftMargin, (s.labelBackgroundHeight -
194                        Math.round(scale * icon.getHeight()))/2f);
195                canvas.scale(scale, scale);
196                canvas.drawBitmap(icon, 0, 0, null);
197            }
198
199            return bitmap;
200        }
201    }
202
203    public void recycleLabel(Bitmap label) {
204        GalleryBitmapPool.getInstance().put(label);
205    }
206}
207