/* * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.android.exoplayer.upstream; import com.google.android.exoplayer.util.Assertions; import android.content.Context; import android.text.TextUtils; import java.io.IOException; /** * A {@link UriDataSource} that supports multiple URI schemes. The supported schemes are: * * <ul> * <li>http(s): For fetching data over HTTP and HTTPS (e.g. https://www.something.com/media.mp4). * <li>file: For fetching data from a local file (e.g. file:///path/to/media/media.mp4, or just * /path/to/media/media.mp4 because the implementation assumes that a URI without a scheme is a * local file URI). * <li>asset: For fetching data from an asset in the application's apk (e.g. asset:///media.mp4). * <li>content: For fetching data from a content URI (e.g. content://authority/path/123). * </ul> */ public final class DefaultUriDataSource implements UriDataSource { private static final String SCHEME_FILE = "file"; private static final String SCHEME_ASSET = "asset"; private static final String SCHEME_CONTENT = "content"; private final UriDataSource httpDataSource; private final UriDataSource fileDataSource; private final UriDataSource assetDataSource; private final UriDataSource contentDataSource; /** * {@code null} if no data source is open. Otherwise, equal to {@link #fileDataSource} if the open * data source is a file, or {@link #httpDataSource} otherwise. */ private UriDataSource dataSource; /** * Constructs a new instance. * <p> * The constructed instance will not follow cross-protocol redirects (i.e. redirects from HTTP to * HTTPS or vice versa) when fetching remote data. Cross-protocol redirects can be enabled by * using {@link #DefaultUriDataSource(Context, TransferListener, String, boolean)} and passing * {@code true} as the final argument. * * @param context A context. * @param userAgent The User-Agent string that should be used when requesting remote data. */ public DefaultUriDataSource(Context context, String userAgent) { this(context, null, userAgent, false); } /** * Constructs a new instance. * <p> * The constructed instance will not follow cross-protocol redirects (i.e. redirects from HTTP to * HTTPS or vice versa) when fetching remote data. Cross-protocol redirects can be enabled by * using {@link #DefaultUriDataSource(Context, TransferListener, String, boolean)} and passing * {@code true} as the final argument. * * @param context A context. * @param listener An optional {@link TransferListener}. * @param userAgent The User-Agent string that should be used when requesting remote data. */ public DefaultUriDataSource(Context context, TransferListener listener, String userAgent) { this(context, listener, userAgent, false); } /** * Constructs a new instance, optionally configured to follow cross-protocol redirects. * * @param context A context. * @param listener An optional {@link TransferListener}. * @param userAgent The User-Agent string that should be used when requesting remote data. * @param allowCrossProtocolRedirects Whether cross-protocol redirects (i.e. redirects from HTTP * to HTTPS and vice versa) are enabled when fetching remote data.. */ public DefaultUriDataSource(Context context, TransferListener listener, String userAgent, boolean allowCrossProtocolRedirects) { this(context, listener, new DefaultHttpDataSource(userAgent, null, listener, DefaultHttpDataSource.DEFAULT_CONNECT_TIMEOUT_MILLIS, DefaultHttpDataSource.DEFAULT_READ_TIMEOUT_MILLIS, allowCrossProtocolRedirects)); } /** * Constructs a new instance, using a provided {@link HttpDataSource} for fetching remote data. * * @param context A context. * @param listener An optional {@link TransferListener}. * @param httpDataSource {@link UriDataSource} to use for non-file URIs. */ public DefaultUriDataSource(Context context, TransferListener listener, UriDataSource httpDataSource) { this.httpDataSource = Assertions.checkNotNull(httpDataSource); this.fileDataSource = new FileDataSource(listener); this.assetDataSource = new AssetDataSource(context, listener); this.contentDataSource = new ContentDataSource(context, listener); } @Override public long open(DataSpec dataSpec) throws IOException { Assertions.checkState(dataSource == null); // Choose the correct source for the scheme. String scheme = dataSpec.uri.getScheme(); if (SCHEME_FILE.equals(scheme) || TextUtils.isEmpty(scheme)) { if (dataSpec.uri.getPath().startsWith("/android_asset/")) { dataSource = assetDataSource; } else { dataSource = fileDataSource; } } else if (SCHEME_ASSET.equals(scheme)) { dataSource = assetDataSource; } else if (SCHEME_CONTENT.equals(scheme)) { dataSource = contentDataSource; } else { dataSource = httpDataSource; } // Open the source and return. return dataSource.open(dataSpec); } @Override public int read(byte[] buffer, int offset, int readLength) throws IOException { return dataSource.read(buffer, offset, readLength); } @Override public String getUri() { return dataSource == null ? null : dataSource.getUri(); } @Override public void close() throws IOException { if (dataSource != null) { try { dataSource.close(); } finally { dataSource = null; } } } }