use of ca.rmen.android.networkmonitor.app.prefs.SortPreferences in project network-monitor by caarmen.
the class LogActivity method loadHTMLFile.
/**
* Read the data from the DB, export it to an HTML file, and load the HTML file in the WebView.
*/
// only loading local files, it's ok.
@SuppressLint("SetJavaScriptEnabled")
private void loadHTMLFile() {
Log.v(TAG, "loadHTMLFile");
final ProgressBar progressBar = findViewById(R.id.progress_bar);
assert progressBar != null;
progressBar.setVisibility(View.VISIBLE);
startRefreshIconAnimation();
final boolean freezeHeader = NetMonPreferences.getInstance(this).getFreezeHtmlTableHeader();
final String fixedTableHeight;
if (freezeHeader) {
// I've come to the following calculation by trial and error.
// I've noticed that when entering the web view, the density is equal to the scale/zoom, but I'm
// not sure if it's the density or zoom which really matters in this calculation.
// We subtract 100px from the scaled webview height to account for the table header.
fixedTableHeight = ((mWebView.getHeight() / getResources().getDisplayMetrics().density) - 100) + "px";
} else {
fixedTableHeight = null;
}
AsyncTask.execute(() -> {
Log.v(TAG, "loadHTMLFile:doInBackground");
// Export the DB to the HTML file.
HTMLExport htmlExport = new HTMLExport(LogActivity.this, false, fixedTableHeight);
int recordCount = NetMonPreferences.getInstance(LogActivity.this).getFilterRecordCount();
File result = htmlExport.export(recordCount, null);
runOnUiThread(() -> {
Log.v(TAG, "loadHTMLFile:onPostExecute, result=" + result);
if (isFinishing()) {
Log.v(TAG, "finishing, ignoring loadHTMLFile result");
return;
}
WebView webView = mWebView;
if (webView == null) {
Log.v(TAG, "Must be destroyed or destroying, we have no webview, ignoring loadHTMLFile result");
return;
}
if (result == null) {
Snackbar.make(webView, R.string.error_reading_log, Snackbar.LENGTH_LONG).show();
return;
}
// Load the exported HTML file into the WebView.
// Save our current horizontal scroll position so we can keep our
// horizontal position after reloading the page.
final int oldScrollX = webView.getScrollX();
webView.getSettings().setUseWideViewPort(true);
webView.getSettings().setBuiltInZoomControls(true);
webView.getSettings().setJavaScriptEnabled(true);
webView.loadUrl("file://" + result.getAbsolutePath());
webView.setWebViewClient(new WebViewClient() {
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
Log.v(TAG, "onPageStarted");
// http://stackoverflow.com/questions/6855715/maintain-webview-content-scroll-position-on-orientation-change
if (oldScrollX > 0) {
String jsScrollX = "javascript:window:scrollTo(" + oldScrollX + " / window.devicePixelRatio,0);";
view.loadUrl(jsScrollX);
}
}
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
progressBar.setVisibility(View.GONE);
stopRefreshIconAnimation();
}
@Override
@TargetApi(Build.VERSION_CODES.N)
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
return loadUrl(request.getUrl().toString()) || super.shouldOverrideUrlLoading(view, request);
}
@SuppressWarnings("deprecation")
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
return loadUrl(url) || super.shouldOverrideUrlLoading(view, url);
}
private boolean loadUrl(String url) {
Log.v(TAG, "url: " + url);
// the sorting preference (column name, ascending or descending order).
if (url.startsWith(HTMLExport.URL_SORT)) {
NetMonPreferences prefs = NetMonPreferences.getInstance(LogActivity.this);
SortPreferences oldSortPreferences = prefs.getSortPreferences();
// The new column used for sorting will be the one the user tapped on.
String newSortColumnName = url.substring(HTMLExport.URL_SORT.length());
SortPreferences.SortOrder newSortOrder = oldSortPreferences.sortOrder;
// toggle the sort order between ascending and descending.
if (newSortColumnName.equals(oldSortPreferences.sortColumnName)) {
if (oldSortPreferences.sortOrder == SortPreferences.SortOrder.DESC)
newSortOrder = SortPreferences.SortOrder.ASC;
else
newSortOrder = SortPreferences.SortOrder.DESC;
}
// Update the sorting preferences (our shared preference change listener will be notified
// and reload the page).
prefs.setSortPreferences(new SortPreferences(newSortColumnName, newSortOrder));
return true;
} else // If the user clicked on the filter icon, start the filter activity for this column.
if (url.startsWith(HTMLExport.URL_FILTER)) {
Intent intent = new Intent(LogActivity.this, FilterColumnActivity.class);
String columnName = url.substring(HTMLExport.URL_FILTER.length());
intent.putExtra(FilterColumnActivity.EXTRA_COLUMN_NAME, columnName);
startActivityForResult(intent, REQUEST_CODE_FILTER_COLUMN);
return true;
} else {
return false;
}
}
});
});
});
}
use of ca.rmen.android.networkmonitor.app.prefs.SortPreferences in project network-monitor by caarmen.
the class HTMLExport method writeHeader.
@Override
void writeHeader(String[] columnLabels) {
try {
mPrintWriter = new PrintWriter(mFile, "utf-8");
} catch (FileNotFoundException | UnsupportedEncodingException e) {
Log.e(TAG, "writeHeader Could not initialize print writer", e);
return;
}
mPrintWriter.println("<!DOCTYPE html>");
mPrintWriter.println("<html>");
mPrintWriter.println(" <head>");
mPrintWriter.println(" <title>" + mContext.getString(R.string.app_name) + "</title>");
String columnCss = mContext.getString(R.string.css_template, getColumnCss());
mPrintWriter.println(mContext.getString(R.string.css));
mPrintWriter.println(mContext.getString(R.string.css_api_level_custom));
mPrintWriter.println(mContext.getString(R.string.css_themed));
mPrintWriter.println(columnCss);
if (mFixedTableHeight != null) {
String fixedHeaderCss = mContext.getString(R.string.fixed_header_css, mFixedTableHeight);
mPrintWriter.println(fixedHeaderCss);
}
mPrintWriter.println(" </head><body>");
mPrintWriter.println("<table class='main-table'>");
mPrintWriter.println(getColgroup(columnLabels.length));
mPrintWriter.println(" <thead><tr>");
SortPreferences sortPreferences = NetMonPreferences.getInstance(mContext).getSortPreferences();
mPrintWriter.println(" <th></th>");
for (String columnLabel : columnLabels) {
String dbColumnName = NetMonColumns.getColumnName(mContext, columnLabel);
if (dbColumnName != null) {
String labelClass = "column_heading";
// Indicate if this is the sorting column: specify a particular css style
// for the label, and show an up or down arrow depending on if we're
// sorting ascending or descending.
String sortIconCharacter = "";
if (dbColumnName.equals(sortPreferences.sortColumnName)) {
labelClass += " sort_column";
if (sortPreferences.sortOrder == SortOrder.DESC)
sortIconCharacter = mContext.getString(R.string.icon_sort_desc);
else
sortIconCharacter = mContext.getString(R.string.icon_sort_asc);
}
// Indicate if this is a filtered column: specify a particular css style
// for the label, and show the filter on or off icon.
boolean isFilterable = NetMonColumns.isColumnFilterable(mContext, dbColumnName);
String filterIconClass = "filter_icon";
String filterIconCharacter = isFilterable ? mContext.getString(R.string.icon_filter_off) : "";
if (isFilterable) {
List<String> columnFilterValues = NetMonPreferences.getInstance(mContext).getColumnFilterValues(dbColumnName);
if (columnFilterValues != null && columnFilterValues.size() > 0) {
labelClass += " filtered_column_label";
filterIconCharacter = mContext.getString(R.string.icon_filter_on);
}
}
// One cell for the sort icon and column label.
String sort = sortIconCharacter + "<a class=\"" + labelClass + "\" href=\"" + URL_SORT + dbColumnName + "\">" + columnLabel + "</a>";
// One cell for the filter icon.
String filter = "<a class=\"" + filterIconClass + "\" href=\"" + URL_FILTER + dbColumnName + "\">" + filterIconCharacter + "</a>";
// Write out the table cell for this column header.
mPrintWriter.println(" <th>" + sort + filter + "</th>");
} else {
mPrintWriter.println(" <th></th>");
}
}
mPrintWriter.println(" </tr></thead><tbody>");
mPrintWriter.println(" <tr>");
// container-container? :( Wish I could do this more cleanly/simply.
mPrintWriter.println(" <td class='table-data-container-container' colspan='" + (columnLabels.length + 1) + "'>");
mPrintWriter.println(" <div class='table-data-container'>");
mPrintWriter.println(" <table class='table-data'>");
mPrintWriter.println(getColgroup(columnLabels.length));
}
use of ca.rmen.android.networkmonitor.app.prefs.SortPreferences in project network-monitor by caarmen.
the class TableFileExport method export.
/**
* @param recordCount export at most this number of records. If recordCount is 0 or less, all records will be exported.
* @return the file if it was correctly exported, null otherwise.
*/
public File export(int recordCount, ProgressListener listener) {
Log.v(TAG, "export " + (recordCount <= 0 ? "all" : recordCount) + " records");
String[] usedColumnNames = (String[]) NetMonPreferences.getInstance(mContext).getSelectedColumns().toArray();
Formatter formatter = FormatterFactory.getFormatter(mFormatterStyle, mContext);
// Order and filter the results based on the user's preferences.
SortPreferences sortPreferences = NetMonPreferences.getInstance(mContext).getSortPreferences();
Selection selection = FilterPreferences.getSelectionClause(mContext);
Uri uri = NetMonColumns.CONTENT_URI;
if (recordCount > 0)
uri = uri.buildUpon().appendQueryParameter(NetMonProvider.QUERY_PARAMETER_LIMIT, String.valueOf(recordCount)).build();
Cursor c = mContext.getContentResolver().query(uri, usedColumnNames, selection.selectionString, selection.selectionArgs, sortPreferences.getOrderByClause());
if (c != null) {
try {
for (int i = 0; i < usedColumnNames.length; i++) usedColumnNames[i] = NetMonColumns.getColumnLabel(mContext, usedColumnNames[i]);
Log.v(TAG, "Column names: " + Arrays.toString(usedColumnNames));
// Write the table rows to the file.
int rowsAvailable = c.getCount();
// Start writing to the file.
writeHeader(usedColumnNames);
while (c.moveToNext() && !isCanceled()) {
String[] cellValues = new String[c.getColumnCount()];
for (int i = 0; i < c.getColumnCount(); i++) cellValues[i] = formatter.format(c, i);
writeRow(c.getPosition(), cellValues);
// Notify the listener of our progress (progress is 1-based)
if (listener != null)
listener.onProgress(c.getPosition() + 1, rowsAvailable);
// Some file exports need to create the whole file in memory
// before saving it. (This is currently the case with
// the Excel export, whether we use jexcelapi or poi).
// On some devices, with large exports, we may not
// have enough memory to export the whole file.
// Here we detect a low memory situation, and stop
// creating rows.
long maxMemory = Runtime.getRuntime().maxMemory();
long allocatedMemory = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
long pctFreeMemory = ((maxMemory - allocatedMemory) * 100) / maxMemory;
if (c.getPosition() % 100 == 0) {
Log.v(TAG, "pctFreeMemory:" + pctFreeMemory);
}
if (pctFreeMemory < THRESHOLD_LOW_MEMORY_PCT) {
Log.v(TAG, "Not enough memory to export the whole file");
if (listener != null) {
listener.onWarning(mContext.getString(R.string.export_warning_file_too_big_message));
}
break;
}
}
// Write the footer and clean up the file.
writeFooter();
if (listener != null) {
if (isCanceled()) {
listener.onComplete(mContext.getString(R.string.export_notif_canceled_content));
} else {
listener.onComplete(mContext.getString(R.string.export_save_to_external_storage_success, mFile.getAbsolutePath()));
}
}
return mFile;
} catch (IOException e) {
Log.e(TAG, "export Could not export file " + mFile + ": " + e.getMessage(), e);
} finally {
c.close();
}
}
if (listener != null)
listener.onError(mContext.getString(R.string.export_notif_error_content));
return null;
}
Aggregations