127cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong/*
227cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong * Copyright (C) 2013 The Android Open Source Project
327cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong *
427cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong * Licensed under the Apache License, Version 2.0 (the "License");
527cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong * you may not use this file except in compliance with the License.
627cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong * You may obtain a copy of the License at
727cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong *
827cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong *      http://www.apache.org/licenses/LICENSE-2.0
927cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong *
1027cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong * Unless required by applicable law or agreed to in writing, software
1127cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong * distributed under the License is distributed on an "AS IS" BASIS,
1227cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1327cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong * See the License for the specific language governing permissions and
1427cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong * limitations under the License.
1527cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong */
1627cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong
1727cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kongpackage com.android.camera;
1827cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong
1927cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kongimport android.content.ContentResolver;
2027cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kongimport android.location.Location;
2127cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kongimport android.net.Uri;
2227cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kongimport android.util.Log;
2327cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong
2427cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kongimport java.util.ArrayList;
2527cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong
2627cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong// We use a queue to store the SaveRequests that have not been completed
2727cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong// yet. The main thread puts the request into the queue. The saver thread
2827cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong// gets it from the queue, does the work, and removes it from the queue.
2927cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong//
3027cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong// The main thread needs to wait for the saver thread to finish all the work
3127cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong// in the queue, when the activity's onPause() is called, we need to finish
3227cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong// all the work, so other programs (like Gallery) can see all the images.
3327cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong//
3427cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong// If the queue becomes too long, adding a new request will block the main
3527cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong// thread until the queue length drops below the threshold (QUEUE_LIMIT).
3627cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong// If we don't do this, we may face several problems: (1) We may OOM
3727cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong// because we are holding all the jpeg data in memory. (2) We may ANR
3827cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong// when we need to wait for saver thread finishing all the work (in
3927cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong// onPause() or gotoGallery()) because the time to finishing a long queue
4027cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong// of work may be too long.
4127cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kongclass MediaSaver extends Thread {
4227cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong    private static final int SAVE_QUEUE_LIMIT = 3;
4327cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong    private static final String TAG = "MediaSaver";
4427cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong
4527cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong    private ArrayList<SaveRequest> mQueue;
4627cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong    private boolean mStop;
4727cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong    private ContentResolver mContentResolver;
4827cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong
4927cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong    public interface OnMediaSavedListener {
5027cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong        public void onMediaSaved(Uri uri);
5127cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong    }
5227cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong
5327cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong    public MediaSaver(ContentResolver resolver) {
5427cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong        mContentResolver = resolver;
5527cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong        mQueue = new ArrayList<SaveRequest>();
5627cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong        start();
5727cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong    }
5827cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong
5927cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong    // Runs in main thread
6027cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong    public synchronized boolean queueFull() {
6127cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong        return (mQueue.size() >= SAVE_QUEUE_LIMIT);
6227cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong    }
6327cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong
6427cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong    // Runs in main thread
6527cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong    public void addImage(final byte[] data, String title, long date, Location loc,
6627cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong                         int width, int height, int orientation, OnMediaSavedListener l) {
6727cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong        SaveRequest r = new SaveRequest();
6827cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong        r.data = data;
6927cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong        r.date = date;
7027cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong        r.title = title;
7127cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong        r.loc = (loc == null) ? null : new Location(loc);  // make a copy
7227cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong        r.width = width;
7327cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong        r.height = height;
7427cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong        r.orientation = orientation;
7527cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong        r.listener = l;
7627cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong        synchronized (this) {
7727cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong            while (mQueue.size() >= SAVE_QUEUE_LIMIT) {
7827cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong                try {
7927cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong                    wait();
8027cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong                } catch (InterruptedException ex) {
8127cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong                    // ignore.
8227cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong                }
8327cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong            }
8427cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong            mQueue.add(r);
8527cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong            notifyAll();  // Tell saver thread there is new work to do.
8627cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong        }
8727cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong    }
8827cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong
8927cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong    // Runs in saver thread
9027cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong    @Override
9127cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong    public void run() {
9227cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong        while (true) {
9327cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong            SaveRequest r;
9427cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong            synchronized (this) {
9527cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong                if (mQueue.isEmpty()) {
9627cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong                    notifyAll();  // notify main thread in waitDone
9727cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong
9827cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong                    // Note that we can only stop after we saved all images
9927cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong                    // in the queue.
10027cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong                    if (mStop) break;
10127cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong
10227cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong                    try {
10327cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong                        wait();
10427cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong                    } catch (InterruptedException ex) {
10527cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong                        // ignore.
10627cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong                    }
10727cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong                    continue;
10827cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong                }
10927cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong                if (mStop) break;
11027cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong                r = mQueue.remove(0);
11127cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong                notifyAll();  // the main thread may wait in addImage
11227cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong            }
11327cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong            Uri uri = storeImage(r.data, r.title, r.date, r.loc, r.width, r.height,
11427cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong                    r.orientation);
11527cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong            r.listener.onMediaSaved(uri);
11627cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong        }
11727cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong        if (!mQueue.isEmpty()) {
11827cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong            Log.e(TAG, "Media saver thread stopped with " + mQueue.size() + " images unsaved");
11927cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong            mQueue.clear();
12027cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong        }
12127cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong    }
12227cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong
12327cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong    // Runs in main thread
12427cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong    public void finish() {
12527cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong        synchronized (this) {
12627cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong            mStop = true;
12727cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong            notifyAll();
12827cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong        }
12927cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong    }
13027cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong
13127cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong    // Runs in saver thread
13227cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong    private Uri storeImage(final byte[] data, String title, long date,
13327cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong                           Location loc, int width, int height, int orientation) {
13427cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong        Uri uri = Storage.addImage(mContentResolver, title, date, loc,
13527cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong                                   orientation, data, width, height);
13627cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong        return uri;
13727cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong    }
13827cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong
13927cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong    // Each SaveRequest remembers the data needed to save an image.
14027cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong    private static class SaveRequest {
14127cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong        byte[] data;
14227cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong        String title;
14327cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong        long date;
14427cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong        Location loc;
14527cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong        int width, height;
14627cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong        int orientation;
14727cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong        OnMediaSavedListener listener;
14827cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong    }
14927cf10dba1e5de69fbef8c9f1ae729e5376099bfAngus Kong}
150