Subversion Repositories SmartDukaan

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
14792 manas 1
/**
2
 * Copyright 2010-present Facebook.
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
 
17
package com.facebook;
18
 
19
import android.content.Context;
20
import android.graphics.Bitmap;
21
import android.util.Log;
22
import com.facebook.internal.Utility;
23
import com.facebook.internal.Validate;
24
 
25
import java.io.*;
26
import java.net.URLEncoder;
27
import java.util.ArrayList;
28
import java.util.List;
29
import java.util.Map;
30
import java.util.UUID;
31
 
32
/**
33
 * <p>This class works in conjunction with {@link NativeAppCallContentProvider} to allow apps to attach binary
34
 * attachments (e.g., images) to native dialogs launched via the {@link com.facebook.widget.FacebookDialog}
35
 * class. It stores attachments in temporary files and allows the Facebook application to retrieve them via
36
 * the content provider.</p>
37
 *
38
 * <p>Callers are generally not expected to need to use this class directly;
39
 * see {@link com.facebook.widget.FacebookDialog.OpenGraphActionDialogBuilder#setImageAttachmentsForObject(String,
40
 * java.util.List) OpenGraphActionDialogBuilder.setImageAttachmentsForObject} for an example of a function
41
 * that will accept attachments, attach them to the native dialog call, and add them to the content provider
42
 * automatically.</p>
43
 **/
44
public final class NativeAppCallAttachmentStore implements NativeAppCallContentProvider.AttachmentDataSource {
45
    private static final String TAG = NativeAppCallAttachmentStore.class.getName();
46
    static final String ATTACHMENTS_DIR_NAME = "com.facebook.NativeAppCallAttachmentStore.files";
47
    private static File attachmentsDirectory;
48
 
49
    /**
50
     * Adds a number of bitmap attachments associated with a native app call. The attachments will be
51
     * served via {@link NativeAppCallContentProvider#openFile(android.net.Uri, String) openFile}.
52
     *
53
     * @param context the Context the call is being made from
54
     * @param callId the unique ID of the call
55
     * @param imageAttachments a Map of attachment names to Bitmaps; the attachment names will be part of
56
     *                         the URI processed by openFile
57
     * @throws java.io.IOException
58
     */
59
    public void addAttachmentsForCall(Context context, UUID callId, Map<String, Bitmap> imageAttachments) {
60
        Validate.notNull(context, "context");
61
        Validate.notNull(callId, "callId");
62
        Validate.containsNoNulls(imageAttachments.values(), "imageAttachments");
63
        Validate.containsNoNullOrEmpty(imageAttachments.keySet(), "imageAttachments");
64
 
65
        addAttachments(context, callId, imageAttachments, new ProcessAttachment<Bitmap>() {
66
            @Override
67
            public void processAttachment(Bitmap attachment, File outputFile) throws IOException {
68
                FileOutputStream outputStream = new FileOutputStream(outputFile);
69
                try {
70
                    attachment.compress(Bitmap.CompressFormat.JPEG, 100, outputStream);
71
                } finally {
72
                    Utility.closeQuietly(outputStream);
73
                }
74
            }
75
        });
76
    }
77
 
78
    /**
79
     * Adds a number of bitmap/video attachment files associated with a native app call. The attachments will be
80
     * served via {@link NativeAppCallContentProvider#openFile(android.net.Uri, String) openFile}.
81
     *
82
     * @param context the Context the call is being made from
83
     * @param callId the unique ID of the call
84
     * @param mediaAttachmentFiles a Map of attachment names to Files containing the bitmaps/videos; the attachment
85
     *                             names will be part of the URI processed by openFile
86
     * @throws java.io.IOException
87
     */
88
    public void addAttachmentFilesForCall(Context context, UUID callId, Map<String, File> mediaAttachmentFiles) {
89
        Validate.notNull(context, "context");
90
        Validate.notNull(callId, "callId");
91
        Validate.containsNoNulls(mediaAttachmentFiles.values(), "mediaAttachmentFiles");
92
        Validate.containsNoNullOrEmpty(mediaAttachmentFiles.keySet(), "mediaAttachmentFiles");
93
 
94
        addAttachments(context, callId, mediaAttachmentFiles, new ProcessAttachment<File>() {
95
            @Override
96
            public void processAttachment(File attachment, File outputFile) throws IOException {
97
                FileOutputStream outputStream = new FileOutputStream(outputFile);
98
                FileInputStream inputStream = null;
99
                try {
100
                    inputStream = new FileInputStream(attachment);
101
 
102
                    byte[] buffer = new byte[1024];
103
                    int len;
104
                    while ((len = inputStream.read(buffer)) > 0) {
105
                        outputStream.write(buffer, 0, len);
106
                    }
107
                } finally {
108
                    Utility.closeQuietly(outputStream);
109
                    Utility.closeQuietly(inputStream);
110
                }
111
            }
112
        });
113
    }
114
 
115
    private <T> void addAttachments(Context context, UUID callId, Map<String, T> attachments,
116
            ProcessAttachment<T> processor) {
117
        if (attachments.size() == 0) {
118
            return;
119
        }
120
 
121
        // If this is the first time we've been instantiated, clean up any existing attachments.
122
        if (attachmentsDirectory == null) {
123
            cleanupAllAttachments(context);
124
        }
125
 
126
        ensureAttachmentsDirectoryExists(context);
127
 
128
        List<File> filesToCleanup = new ArrayList<File>();
129
 
130
        try {
131
            for (Map.Entry<String, T> entry : attachments.entrySet()) {
132
                String attachmentName = entry.getKey();
133
                T attachment = entry.getValue();
134
 
135
                File file = getAttachmentFile(callId, attachmentName, true);
136
                filesToCleanup.add(file);
137
 
138
                processor.processAttachment(attachment, file);
139
            }
140
        } catch (IOException exception) {
141
            Log.e(TAG, "Got unexpected exception:" + exception);
142
            for (File file : filesToCleanup) {
143
                try {
144
                    file.delete();
145
                } catch (Exception e) {
146
                    // Always try to delete other files.
147
                }
148
            }
149
            throw new FacebookException(exception);
150
        }
151
 
152
    }
153
 
154
    interface ProcessAttachment<T> {
155
        void processAttachment(T attachment, File outputFile) throws IOException;
156
    }
157
 
158
    /**
159
     * Removes any temporary files associated with a particular native app call.
160
     *
161
     * @param context the Context the call is being made from
162
     * @param callId the unique ID of the call
163
     */
164
    public void cleanupAttachmentsForCall(Context context, UUID callId) {
165
        File dir = getAttachmentsDirectoryForCall(callId, false);
166
        Utility.deleteDirectory(dir);
167
    }
168
 
169
    @Override
170
    public File openAttachment(UUID callId, String attachmentName) throws FileNotFoundException {
171
        if (Utility.isNullOrEmpty(attachmentName) ||
172
                callId == null) {
173
            throw new FileNotFoundException();
174
        }
175
 
176
        try {
177
            return getAttachmentFile(callId, attachmentName, false);
178
        } catch (IOException e) {
179
            // We don't try to create the file, so we shouldn't get any IOExceptions. But if we do, just
180
            // act like the file wasn't found.
181
            throw new FileNotFoundException();
182
        }
183
    }
184
 
185
    synchronized static File getAttachmentsDirectory(Context context) {
186
        if (attachmentsDirectory == null) {
187
            attachmentsDirectory = new File(context.getCacheDir(), ATTACHMENTS_DIR_NAME);
188
        }
189
        return attachmentsDirectory;
190
    }
191
 
192
    File ensureAttachmentsDirectoryExists(Context context) {
193
        File dir = getAttachmentsDirectory(context);
194
        dir.mkdirs();
195
        return dir;
196
    }
197
 
198
    File getAttachmentsDirectoryForCall(UUID callId, boolean create) {
199
        if (attachmentsDirectory == null) {
200
            return null;
201
        }
202
 
203
        File dir = new File(attachmentsDirectory, callId.toString());
204
        if (create && !dir.exists()) {
205
            dir.mkdirs();
206
        }
207
        return dir;
208
    }
209
 
210
    File getAttachmentFile(UUID callId, String attachmentName, boolean createDirs) throws IOException {
211
        File dir = getAttachmentsDirectoryForCall(callId, createDirs);
212
        if (dir == null) {
213
            return null;
214
        }
215
 
216
        try {
217
            return new File(dir, URLEncoder.encode(attachmentName, "UTF-8"));
218
        } catch (UnsupportedEncodingException e) {
219
            return null;
220
        }
221
    }
222
 
223
    void cleanupAllAttachments(Context context) {
224
        // Attachments directory may or may not exist; we won't create it if not, since we are just going to delete it.
225
        File dir = getAttachmentsDirectory(context);
226
        Utility.deleteDirectory(dir);
227
    }
228
}