1/*
2 * Copyright (C) 2013 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.camera.app;
18
19import android.content.Context;
20import android.graphics.BitmapFactory;
21import android.location.Location;
22import android.net.Uri;
23
24import com.android.camera.ImageTaskManager;
25import com.android.camera.Storage;
26import com.android.camera.exif.ExifInterface;
27import com.android.camera.util.CameraUtil;
28
29import java.lang.ref.WeakReference;
30import java.util.ArrayList;
31import java.util.Iterator;
32
33public class PlaceholderManager implements ImageTaskManager {
34    private static final String TAG = "PlaceholderManager";
35
36    public static final String PLACEHOLDER_MIME_TYPE = "application/placeholder-image";
37    private final Context mContext;
38
39    final private ArrayList<WeakReference<TaskListener>> mListenerRefs;
40
41    public static class Session {
42        String outputTitle;
43        Uri outputUri;
44        long time;
45
46        Session(String title, Uri uri, long timestamp) {
47            outputTitle = title;
48            outputUri = uri;
49            time = timestamp;
50        }
51    }
52
53    public PlaceholderManager(Context context) {
54        mContext = context;
55        mListenerRefs = new ArrayList<WeakReference<TaskListener>>();
56    }
57
58    @Override
59    public void addTaskListener(TaskListener l) {
60        synchronized (mListenerRefs) {
61            if (findTaskListener(l) == -1) {
62                mListenerRefs.add(new WeakReference<TaskListener>(l));
63            }
64        }
65    }
66
67    @Override
68    public void removeTaskListener(TaskListener l) {
69        synchronized (mListenerRefs) {
70            int i = findTaskListener(l);
71            if (i != -1) {
72                mListenerRefs.remove(i);
73            }
74        }
75    }
76
77    @Override
78    public int getTaskProgress(Uri uri) {
79        return 0;
80    }
81
82    private int findTaskListener(TaskListener listener) {
83        int index = -1;
84        for (int i = 0; i < mListenerRefs.size(); i++) {
85            TaskListener l = mListenerRefs.get(i).get();
86            if (l != null && l == listener) {
87                index = i;
88                break;
89            }
90        }
91        return index;
92    }
93
94    private Iterable<TaskListener> getListeners() {
95        return new Iterable<TaskListener>() {
96            @Override
97            public Iterator<TaskListener> iterator() {
98                return new ListenerIterator();
99            }
100        };
101    }
102
103    private class ListenerIterator implements Iterator<TaskListener> {
104        private int mIndex = 0;
105        private TaskListener mNext = null;
106
107        @Override
108        public boolean hasNext() {
109            while (mNext == null && mIndex < mListenerRefs.size()) {
110                mNext = mListenerRefs.get(mIndex).get();
111                if (mNext == null) {
112                    mListenerRefs.remove(mIndex);
113                }
114            }
115            return mNext != null;
116        }
117
118        @Override
119        public TaskListener next() {
120            hasNext(); // Populates mNext
121            mIndex++;
122            TaskListener next = mNext;
123            mNext = null;
124            return next;
125        }
126
127        @Override
128        public void remove() {
129            throw new UnsupportedOperationException();
130        }
131    }
132
133    public Session insertPlaceholder(String title, byte[] placeholder, long timestamp) {
134        if (title == null || placeholder == null) {
135            throw new IllegalArgumentException("Null argument passed to insertPlaceholder");
136        }
137
138        // Decode bounds
139        BitmapFactory.Options options = new BitmapFactory.Options();
140        options.inJustDecodeBounds = true;
141        BitmapFactory.decodeByteArray(placeholder, 0, placeholder.length, options);
142        int width = options.outWidth;
143        int height = options.outHeight;
144
145        if (width <= 0 || height <= 0) {
146            throw new IllegalArgumentException("Image had bad height/width");
147        }
148
149        Uri uri =
150                Storage.addImage(mContext.getContentResolver(), title, timestamp, null, 0, null,
151                        placeholder, width, height, PLACEHOLDER_MIME_TYPE);
152
153        if (uri == null) {
154            return null;
155        }
156
157        String filePath = uri.getPath();
158        synchronized (mListenerRefs) {
159            for (TaskListener l : getListeners()) {
160                l.onTaskQueued(filePath, uri);
161            }
162        }
163
164        return new Session(title, uri, timestamp);
165    }
166
167    public void replacePlaceholder(Session session, Location location, int orientation,
168            ExifInterface exif, byte[] jpeg, int width, int height, String mimeType) {
169
170        Storage.updateImage(session.outputUri, mContext.getContentResolver(), session.outputTitle,
171                session.time, location, orientation, exif, jpeg, width, height, mimeType);
172
173        synchronized (mListenerRefs) {
174            for (TaskListener l : getListeners()) {
175                l.onTaskDone(session.outputUri.getPath(), session.outputUri);
176            }
177        }
178        CameraUtil.broadcastNewPicture(mContext, session.outputUri);
179    }
180
181    public void removePlaceholder(Session session) {
182        Storage.deleteImage(mContext.getContentResolver(), session.outputUri);
183    }
184
185}
186