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