1d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd/*
2d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Copyright (C) 2015 The Android Open Source Project
3d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd *
4d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Licensed under the Apache License, Version 2.0 (the "License");
5d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * you may not use this file except in compliance with the License.
6d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * You may obtain a copy of the License at
7d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd *
8d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd *      http://www.apache.org/licenses/LICENSE-2.0
9d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd *
10d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Unless required by applicable law or agreed to in writing, software
11d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * distributed under the License is distributed on an "AS IS" BASIS,
12d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * See the License for the specific language governing permissions and
14d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * limitations under the License.
15d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */
16d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddpackage com.android.messaging.util;
17d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
18d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.content.ContentResolver;
19d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.content.Context;
20d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.content.res.AssetFileDescriptor;
21d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.media.MediaMetadataRetriever;
22d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.net.Uri;
23d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.os.ParcelFileDescriptor;
24d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.provider.MediaStore;
25d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.support.annotation.NonNull;
26d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.text.TextUtils;
27d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
28d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.Factory;
29d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.datamodel.MediaScratchFileProvider;
30d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.Assert.DoesNotRunOnMainThread;
31d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.google.common.io.ByteStreams;
32d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
33d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport java.io.BufferedInputStream;
34d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport java.io.File;
35d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport java.io.FileNotFoundException;
36d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport java.io.IOException;
37d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport java.io.InputStream;
38d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport java.io.OutputStream;
39d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport java.net.URL;
40d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport java.net.URLConnection;
41d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport java.util.Arrays;
42d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport java.util.HashSet;
43d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
44d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddpublic class UriUtil {
45d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private static final String SCHEME_SMS = "sms";
46d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private static final String SCHEME_SMSTO = "smsto";
47d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private static final String SCHEME_MMS = "mms";
48d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private static final String SCHEME_MMSTO = "smsto";
49d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public static final HashSet<String> SMS_MMS_SCHEMES = new HashSet<String>(
50d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        Arrays.asList(SCHEME_SMS, SCHEME_MMS, SCHEME_SMSTO, SCHEME_MMSTO));
51d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
52d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public static final String SCHEME_BUGLE = "bugle";
53d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public static final HashSet<String> SUPPORTED_SCHEME = new HashSet<String>(
54d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        Arrays.asList(ContentResolver.SCHEME_ANDROID_RESOURCE,
55d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            ContentResolver.SCHEME_CONTENT,
56d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            ContentResolver.SCHEME_FILE,
57d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            SCHEME_BUGLE));
58d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
59d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public static final String SCHEME_TEL = "tel:";
60d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
61d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
62d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Get a Uri representation of the file path of a resource file.
63d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
64d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public static Uri getUriForResourceFile(final String path) {
65d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        return TextUtils.isEmpty(path) ? null : Uri.fromFile(new File(path));
66d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
67d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
68d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
69d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Extract the path from a file:// Uri, or null if the uri is of other scheme.
70d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
71d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public static String getFilePathFromUri(final Uri uri) {
72d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (!isFileUri(uri)) {
73d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            return null;
74d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
75d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        return uri.getPath();
76d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
77d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
78d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
79d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Returns whether the given Uri is local or remote.
80d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
81d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public static boolean isLocalResourceUri(final Uri uri) {
82d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        final String scheme = uri.getScheme();
83d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        return TextUtils.equals(scheme, ContentResolver.SCHEME_ANDROID_RESOURCE) ||
84d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                TextUtils.equals(scheme, ContentResolver.SCHEME_CONTENT) ||
85d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                TextUtils.equals(scheme, ContentResolver.SCHEME_FILE);
86d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
87d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
88d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
89d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Returns whether the given Uri is part of Bugle's app package
90d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
91d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public static boolean isBugleAppResource(final Uri uri) {
92d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        final String scheme = uri.getScheme();
93d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        return TextUtils.equals(scheme, ContentResolver.SCHEME_ANDROID_RESOURCE);
94d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
95d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
96d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public static boolean isFileUri(final Uri uri) {
97d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        return uri != null && TextUtils.equals(uri.getScheme(), ContentResolver.SCHEME_FILE);
98d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
99d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
100d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
101d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Constructs an android.resource:// uri for the given resource id.
102d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
103d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public static Uri getUriForResourceId(final Context context, final int resId) {
104d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        return new Uri.Builder()
105d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                .scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
106d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                .authority(context.getPackageName())
107d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                .appendPath(String.valueOf(resId))
108d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                .build();
109d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
110d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
111d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
112d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Returns whether the given Uri string is local.
113d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
114d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public static boolean isLocalUri(@NonNull final Uri uri) {
115d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        Assert.notNull(uri);
116d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        return SUPPORTED_SCHEME.contains(uri.getScheme());
117d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
118d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
119d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private static final String MEDIA_STORE_URI_KLP = "com.android.providers.media.documents";
120d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
121d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
122d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Check if a URI is from the MediaStore
123d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
124d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public static boolean isMediaStoreUri(final Uri uri) {
125d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        final String uriAuthority = uri.getAuthority();
126d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        return TextUtils.equals(ContentResolver.SCHEME_CONTENT, uri.getScheme())
127d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                && (TextUtils.equals(MediaStore.AUTHORITY, uriAuthority) ||
128d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                // KK changed the media store authority name
129d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                TextUtils.equals(MEDIA_STORE_URI_KLP, uriAuthority));
130d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
131d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
132d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
133d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Gets the size in bytes for the content uri. Currently we only support content in the
134d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * scratch space.
135d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
136d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    @DoesNotRunOnMainThread
137d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public static long getContentSize(final Uri uri) {
138d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        Assert.isNotMainThread();
139d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (isLocalResourceUri(uri)) {
140d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            ParcelFileDescriptor pfd = null;
141d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            try {
142d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                pfd = Factory.get().getApplicationContext()
143d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                        .getContentResolver().openFileDescriptor(uri, "r");
144d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                return Math.max(pfd.getStatSize(), 0);
145d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            } catch (final FileNotFoundException e) {
146d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                LogUtil.e(LogUtil.BUGLE_TAG, "Error getting content size", e);
147d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            } finally {
148d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                if (pfd != null) {
149d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    try {
150d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                        pfd.close();
151d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    } catch (final IOException e) {
152d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                        // Do nothing.
153d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    }
154d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                }
155d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            }
156d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        } else {
157d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            Assert.fail("Unsupported uri type!");
158d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
159d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        return 0;
160d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
161d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
162d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /** @return duration in milliseconds or 0 if not able to determine */
163d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public static int getMediaDurationMs(final Uri uri) {
164d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        final MediaMetadataRetrieverWrapper retriever = new MediaMetadataRetrieverWrapper();
165d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        try {
166d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            retriever.setDataSource(uri);
167d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            return retriever.extractInteger(MediaMetadataRetriever.METADATA_KEY_DURATION, 0);
168d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        } catch (final IOException e) {
169d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            LogUtil.e(LogUtil.BUGLE_TAG, "Unable extract duration from media file: " + uri, e);
170d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            return 0;
171d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        } finally {
172d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            retriever.release();
173d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
174d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
175d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
176d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
177d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Persist a piece of content from the given input stream, byte by byte to the scratch
178d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * directory.
179d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @return the output Uri if the operation succeeded, or null if failed.
180d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
181d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    @DoesNotRunOnMainThread
182d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public static Uri persistContentToScratchSpace(final InputStream inputStream) {
183d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        final Context context = Factory.get().getApplicationContext();
184d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        final Uri scratchSpaceUri = MediaScratchFileProvider.buildMediaScratchSpaceUri(null);
185d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        return copyContent(context, inputStream, scratchSpaceUri);
186d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
187d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
188d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
189d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Persist a piece of content from the given sourceUri, byte by byte to the scratch
190d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * directory.
191d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @return the output Uri if the operation succeeded, or null if failed.
192d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
193d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    @DoesNotRunOnMainThread
194d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public static Uri persistContentToScratchSpace(final Uri sourceUri) {
195d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        InputStream inputStream = null;
196d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        final Context context = Factory.get().getApplicationContext();
197d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        try {
198d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            if (UriUtil.isLocalResourceUri(sourceUri)) {
199d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                inputStream = context.getContentResolver().openInputStream(sourceUri);
200d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            } else {
201d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                // The content is remote. Download it.
202d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                final URL url = new URL(sourceUri.toString());
203d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                final URLConnection ucon = url.openConnection();
204d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                inputStream = new BufferedInputStream(ucon.getInputStream());
205d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            }
206d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            return persistContentToScratchSpace(inputStream);
207d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        } catch (final Exception ex) {
208d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            LogUtil.e(LogUtil.BUGLE_TAG, "Error while retrieving media ", ex);
209d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            return null;
210d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        } finally {
211d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            if (inputStream != null) {
212d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                try {
213d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    inputStream.close();
214d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                } catch (final IOException e) {
215d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    LogUtil.e(LogUtil.BUGLE_TAG, "error trying to close the inputStream", e);
216d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                }
217d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            }
218d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
219d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
220d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
221d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
222d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Persist a piece of content from the given input stream, byte by byte to the specified
223d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * directory.
224d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @return the output Uri if the operation succeeded, or null if failed.
225d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
226d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    @DoesNotRunOnMainThread
227d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public static Uri persistContent(
228d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            final InputStream inputStream, final File outputDir, final String contentType) {
229d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (!outputDir.exists() && !outputDir.mkdirs()) {
230d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            LogUtil.e(LogUtil.BUGLE_TAG, "Error creating " + outputDir.getAbsolutePath());
231d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            return null;
232d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
233d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
234d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        final Context context = Factory.get().getApplicationContext();
235d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        try {
236d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            final Uri targetUri = Uri.fromFile(FileUtil.getNewFile(outputDir, contentType));
237d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            return copyContent(context, inputStream, targetUri);
238d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        } catch (final IOException e) {
239d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            LogUtil.e(LogUtil.BUGLE_TAG, "Error creating file in " + outputDir.getAbsolutePath());
240d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            return null;
241d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
242d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
243d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
244d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
245d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Persist a piece of content from the given sourceUri, byte by byte to the
246d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * specified output directory.
247d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @return the output Uri if the operation succeeded, or null if failed.
248d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
249d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    @DoesNotRunOnMainThread
250d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public static Uri persistContent(
251d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            final Uri sourceUri, final File outputDir, final String contentType) {
252d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        InputStream inputStream = null;
253d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        final Context context = Factory.get().getApplicationContext();
254d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        try {
255d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            if (UriUtil.isLocalResourceUri(sourceUri)) {
256d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                inputStream = context.getContentResolver().openInputStream(sourceUri);
257d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            } else {
258d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                // The content is remote. Download it.
259d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                final URL url = new URL(sourceUri.toString());
260d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                final URLConnection ucon = url.openConnection();
261d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                inputStream = new BufferedInputStream(ucon.getInputStream());
262d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            }
263d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            return persistContent(inputStream, outputDir, contentType);
264d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        } catch (final Exception ex) {
265d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            LogUtil.e(LogUtil.BUGLE_TAG, "Error while retrieving media ", ex);
266d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            return null;
267d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        } finally {
268d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            if (inputStream != null) {
269d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                try {
270d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    inputStream.close();
271d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                } catch (final IOException e) {
272d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    LogUtil.e(LogUtil.BUGLE_TAG, "error trying to close the inputStream", e);
273d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                }
274d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            }
275d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
276d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
277d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
278d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /** @return uri of target file, or null on error */
279d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    @DoesNotRunOnMainThread
280d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private static Uri copyContent(
281d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            final Context context, final InputStream inputStream, final Uri targetUri) {
282d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        Assert.isNotMainThread();
283d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        OutputStream outputStream = null;
284d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        try {
285d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            outputStream = context.getContentResolver().openOutputStream(targetUri);
286d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            ByteStreams.copy(inputStream, outputStream);
287d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        } catch (final Exception ex) {
288d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            LogUtil.e(LogUtil.BUGLE_TAG, "Error while copying content ", ex);
289d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            return null;
290d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        } finally {
291d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            if (outputStream != null) {
292d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                try {
293d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    outputStream.flush();
294d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                } catch (final IOException e) {
295d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    LogUtil.e(LogUtil.BUGLE_TAG, "error trying to flush the outputStream", e);
296d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    return null;
297d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                } finally {
298d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    try {
299d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                        outputStream.close();
300d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    } catch (final IOException e) {
301d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                        // Do nothing.
302d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    }
303d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                }
304d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            }
305d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
306d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        return targetUri;
307d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
308d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
309d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public static boolean isSmsMmsUri(final Uri uri) {
310d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        return uri != null && SMS_MMS_SCHEMES.contains(uri.getScheme());
311d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
312d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
313d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
314d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Extract recipient destinations from Uri of form
315d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     *     SCHEME:destionation[,destination]?otherstuff
316d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * where SCHEME is one of the supported sms/mms schemes.
317d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     *
318d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @param uri sms/mms uri
319d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @return recipient destinations or null
320d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
321d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public static String[] parseRecipientsFromSmsMmsUri(final Uri uri) {
322d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (!isSmsMmsUri(uri)) {
323d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            return null;
324d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
325d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        final String[] parts = uri.getSchemeSpecificPart().split("\\?");
326d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (TextUtils.isEmpty(parts[0])) {
327d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            return null;
328d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
329d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        // replaceUnicodeDigits will replace digits typed in other languages (i.e. Egyptian) with
330d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        // the usual ascii equivalents.
331d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        return TextUtil.replaceUnicodeDigits(parts[0]).replace(';', ',').split(",");
332d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
333d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
334d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
335d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Return the length of the file to which contentUri refers
336d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     *
337d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @param contentUri URI for the file of which we want the length
338d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @return Length of the file or AssetFileDescriptor.UNKNOWN_LENGTH
339d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
340d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public static long getUriContentLength(final Uri contentUri) {
341d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        final Context context = Factory.get().getApplicationContext();
342d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        AssetFileDescriptor afd = null;
343d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        try {
344d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            afd = context.getContentResolver().openAssetFileDescriptor(contentUri, "r");
345d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            return afd.getLength();
346d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        } catch (final FileNotFoundException e) {
347d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            LogUtil.w(LogUtil.BUGLE_TAG, "Failed to query length of " + contentUri);
348d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        } finally {
349d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            if (afd != null) {
350d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                try {
351d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    afd.close();
352d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                } catch (final IOException e) {
353d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    LogUtil.w(LogUtil.BUGLE_TAG, "Failed to close afd for " + contentUri);
354d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                }
355d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            }
356d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
357d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        return AssetFileDescriptor.UNKNOWN_LENGTH;
358d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
359d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
360d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /** @return string representation of URI or null if URI was null */
361d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public static String stringFromUri(final Uri uri) {
362d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        return uri == null ? null : uri.toString();
363d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
364d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
365d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /** @return URI created from string or null if string was null or empty */
366d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public static Uri uriFromString(final String uriString) {
367d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        return TextUtils.isEmpty(uriString) ? null : Uri.parse(uriString);
368d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     }
369d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd}
370