/*
* Copyright (c) 2017
*
* 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 org.acra.attachment;
import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.net.Uri;
import android.os.Build;
import android.os.ParcelFileDescriptor;
import android.provider.OpenableColumns;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import org.acra.ACRA;
import org.acra.file.Directory;
import org.acra.http.HttpUtils;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* @author F43nd1r
* @since 13.03.2017
*/
public class AcraContentProvider extends ContentProvider {
private static final String[] COLUMNS = {
OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE};
private String authority;
@Override
public boolean onCreate() {
//noinspection ConstantConditions
authority = getAuthority(getContext());
if (ACRA.DEV_LOGGING) ACRA.log.d(ACRA.LOG_TAG, "Registered content provider for authority " + authority);
return true;
}
@Nullable
@Override
public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
if (ACRA.DEV_LOGGING) ACRA.log.d(ACRA.LOG_TAG, "Query: " + uri);
final File file = getFileForUri(uri);
if (file == null) {
return null;
}
if (projection == null) {
projection = COLUMNS;
}
final Map<String, Object> columnValueMap = new LinkedHashMap<String, Object>();
for (String column : projection) {
if (column.equals(OpenableColumns.DISPLAY_NAME)) {
columnValueMap.put(OpenableColumns.DISPLAY_NAME, file.getName());
} else if (column.equals(OpenableColumns.SIZE)) {
columnValueMap.put(OpenableColumns.SIZE, file.length());
}
}
final MatrixCursor cursor = new MatrixCursor(columnValueMap.keySet().toArray(new String[columnValueMap.size()]), 1);
cursor.addRow(columnValueMap.values());
return cursor;
}
@Nullable
private File getFileForUri(Uri uri) {
if (!ContentResolver.SCHEME_CONTENT.equals(uri.getScheme()) || !authority.equals(uri.getAuthority())) {
return null;
}
final List<String> segments = new ArrayList<String>(uri.getPathSegments());
if (segments.size() < 2) return null;
final String dir = segments.remove(0).toUpperCase();
try {
final Directory directory = Directory.valueOf(dir);
//noinspection ConstantConditions
return directory.getFile(getContext(), TextUtils.join(File.separator, segments));
} catch (IllegalArgumentException e) {
return null;
}
}
@Nullable
@Override
public String getType(@NonNull Uri uri) {
return HttpUtils.guessMimeType(uri);
}
@Nullable
@Override
public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
throw new UnsupportedOperationException("No insert supported");
}
@Override
public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
throw new UnsupportedOperationException("No delete supported");
}
@Override
public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
throw new UnsupportedOperationException("No update supported");
}
@Nullable
@Override
public ParcelFileDescriptor openFile(@NonNull Uri uri, @NonNull String mode) throws FileNotFoundException {
final File file = getFileForUri(uri);
if (file == null || !file.exists()) throw new FileNotFoundException("File represented by uri " + uri + " could not be found");
if (ACRA.DEV_LOGGING) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
ACRA.log.d(ACRA.LOG_TAG, getCallingPackage() + " opened " + file.getPath());
} else {
ACRA.log.d(ACRA.LOG_TAG, file.getPath() + " was opened by an application");
}
}
return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
}
private static String getAuthority(@NonNull Context context) {
return context.getPackageName() + ".acra";
}
/**
* Get an uri for this content provider for the given file
*
* @param context a context
* @param file the file
* @return the uri
*/
public static Uri getUriForFile(@NonNull Context context, @NonNull File file) {
return getUriForFile(context, Directory.ROOT, file.getPath());
}
/**
* Get an uri for this content provider for the given file
*
* @param context a context
* @param directory the directory, to with the path is relative
* @param relativePath the file path
* @return the uri
*/
public static Uri getUriForFile(@NonNull Context context, @NonNull Directory directory, @NonNull String relativePath) {
return new Uri.Builder()
.scheme(ContentResolver.SCHEME_CONTENT)
.authority(getAuthority(context))
.path(directory.name().toLowerCase() + (relativePath.charAt(0) == File.separatorChar ? "" : File.separator) + relativePath)
.build();
}
}