/*
* Copyright 2012 Kevin Gaudin
*
* 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.collector;
import android.content.Context;
import android.content.SharedPreferences;
import android.support.annotation.NonNull;
import org.acra.ACRA;
import org.acra.ReportField;
import org.acra.builder.ReportBuilder;
import org.acra.config.ACRAConfiguration;
import org.acra.model.Element;
import org.acra.util.PackageManagerWrapper;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static org.acra.ACRA.LOG_TAG;
/**
* Responsible for creating the CrashReportData for an Exception.
* <p>
* Also responsible for holding the custom data to send with each report.
* </p>
*
* @author William Ferguson
* @since 4.3.0
*/
public final class CrashReportDataFactory {
private final Context context;
private final ACRAConfiguration config;
private final SharedPreferences prefs;
private final Map<String, String> customParameters = new LinkedHashMap<String, String>();
private final Calendar appStartDate;
private final Element initialConfiguration;
public CrashReportDataFactory(@NonNull Context context, @NonNull ACRAConfiguration config,
@NonNull SharedPreferences prefs, @NonNull Calendar appStartDate,
@NonNull Element initialConfiguration) {
this.context = context;
this.config = config;
this.prefs = prefs;
this.appStartDate = appStartDate;
this.initialConfiguration = initialConfiguration;
}
/**
* <p>
* Adds a custom key and value to be reported with the generated
* CashReportData.
* </p>
* <p>
* The key/value pairs will be stored in the "custom" column, as a text
* containing one 'key = value' pair on each line.
* </p>
*
* @param key A key for your custom data.
* @param value The value associated to your key.
* @return The previous value for this key if there was one, or null.
*/
public String putCustomData(@NonNull String key, String value) {
return customParameters.put(key, value);
}
/**
* Removes a key/value pair from the custom data field.
*
* @param key The key of the data to be removed.
* @return The value for this key before removal.
*/
public String removeCustomData(@NonNull String key) {
return customParameters.remove(key);
}
/**
* Removes all key/value pairs from the custom data field.
*/
public void clearCustomData() {
customParameters.clear();
}
/**
* Gets the current value for a key in the custom data field.
*
* @param key The key of the data to be retrieved.
* @return The value for this key.
*/
public String getCustomData(@NonNull String key) {
return customParameters.get(key);
}
/**
* Collects crash data.
*
* @param builder ReportBuilder for whom to crete the crash report.
* @return CrashReportData identifying the current crash.
*/
@NonNull
public CrashReportData createCrashData(@NonNull ReportBuilder builder) {
final CrashReportData crashReportData = new CrashReportData();
try {
final Set<ReportField> crashReportFields = config.reportContent();
final List<Collector> collectors = getCollectorsOrdered();
//this will iterate over all collectors in descending order of priority
for (Collector collector : collectors) {
//catch absolutely everything possible here so no collector obstructs the others
try {
for (ReportField reportField : collector.canCollect()) {
try {
if (collector.shouldCollect(crashReportFields, reportField, builder)) {
crashReportData.put(reportField, collector.collect(reportField, builder));
}
} catch (RuntimeException e) {
ACRA.log.e(LOG_TAG, "Error while retrieving " + reportField.name() + " data", e);
}
}
} catch (RuntimeException e) {
ACRA.log.e(LOG_TAG, "Error in collector " + collector.getClass().getSimpleName(), e);
}
}
} catch (RuntimeException e) {
ACRA.log.e(LOG_TAG, "Error while retrieving crash data", e);
}
return crashReportData;
}
private List<Collector> getCollectorsOrdered() {
final List<Collector> collectors = new ArrayList<Collector>();
final PackageManagerWrapper pm = new PackageManagerWrapper(context);
collectors.add(new LogCatCollector(config, pm));
collectors.add(new DropBoxCollector(context, config, pm));
collectors.add(new StacktraceCollector());
collectors.add(new TimeCollector(appStartDate));
collectors.add(new SimpleValuesCollector(context));
collectors.add(new ConfigurationCollector(context, initialConfiguration));
collectors.add(new MemoryInfoCollector());
collectors.add(new ReflectionCollector(context, config));
collectors.add(new DisplayManagerCollector(context));
collectors.add(new CustomDataCollector(customParameters));
collectors.add(new SharedPreferencesCollector(context, config, prefs));
collectors.add(new DeviceFeaturesCollector(context));
collectors.add(new SettingsCollector(context, config));
collectors.add(new PackageManagerCollector(pm));
collectors.add(new DeviceIdCollector(context, pm, prefs));
collectors.add(new LogFileCollector(context, config));
collectors.add(new MediaCodecListCollector());
collectors.add(new ThreadCollector());
return collectors;
}
}