mirror of
https://github.com/samuelclay/NewsBlur.git
synced 2025-08-05 16:58:59 +00:00
Merge pull request #992 from dosiecki/master
Android: Bug Reporting / Log Capture Framework
This commit is contained in:
commit
11a6abbebf
16 changed files with 270 additions and 59 deletions
|
@ -24,7 +24,17 @@
|
||||||
|
|
||||||
<item android:id="@+id/menu_feedback"
|
<item android:id="@+id/menu_feedback"
|
||||||
android:title="@string/menu_feedback"
|
android:title="@string/menu_feedback"
|
||||||
android:showAsAction="never" />
|
android:showAsAction="never" >
|
||||||
|
<menu>
|
||||||
|
<item android:id="@+id/menu_feedback_post"
|
||||||
|
android:title="@string/menu_feedback_post"
|
||||||
|
android:showAsAction="never" />
|
||||||
|
<item android:id="@+id/menu_feedback_email"
|
||||||
|
android:title="@string/menu_feedback_email"
|
||||||
|
android:showAsAction="never" />
|
||||||
|
|
||||||
|
</menu>
|
||||||
|
</item>
|
||||||
|
|
||||||
<item android:id="@+id/menu_loginas"
|
<item android:id="@+id/menu_loginas"
|
||||||
android:title="@string/menu_loginas"
|
android:title="@string/menu_loginas"
|
||||||
|
|
|
@ -149,6 +149,8 @@
|
||||||
<string name="menu_mark_all_as_read">Mark all as read</string>
|
<string name="menu_mark_all_as_read">Mark all as read</string>
|
||||||
<string name="menu_logout">Log out</string>
|
<string name="menu_logout">Log out</string>
|
||||||
<string name="menu_feedback">Send app feedback</string>
|
<string name="menu_feedback">Send app feedback</string>
|
||||||
|
<string name="menu_feedback_post">Create a support post</string>
|
||||||
|
<string name="menu_feedback_email">Email a bug report</string>
|
||||||
<string name="menu_loginas">Login as...</string>
|
<string name="menu_loginas">Login as...</string>
|
||||||
|
|
||||||
<string name="loginas_title">Login As User</string>
|
<string name="loginas_title">Login As User</string>
|
||||||
|
|
|
@ -238,7 +238,10 @@ public class Main extends NbActivity implements StateChangedListener, SwipeRefre
|
||||||
Intent settingsIntent = new Intent(this, Settings.class);
|
Intent settingsIntent = new Intent(this, Settings.class);
|
||||||
startActivity(settingsIntent);
|
startActivity(settingsIntent);
|
||||||
return true;
|
return true;
|
||||||
} else if (item.getItemId() == R.id.menu_feedback) {
|
} else if (item.getItemId() == R.id.menu_feedback_email) {
|
||||||
|
PrefsUtils.sendLogEmail(this);
|
||||||
|
return true;
|
||||||
|
} else if (item.getItemId() == R.id.menu_feedback_post) {
|
||||||
try {
|
try {
|
||||||
Intent i = new Intent(Intent.ACTION_VIEW);
|
Intent i = new Intent(Intent.ACTION_VIEW);
|
||||||
i.setData(Uri.parse(PrefsUtils.createFeedbackLink(this)));
|
i.setData(Uri.parse(PrefsUtils.createFeedbackLink(this)));
|
||||||
|
|
|
@ -59,7 +59,7 @@ public class NbActivity extends Activity {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onResume() {
|
protected void onResume() {
|
||||||
if (AppConstants.VERBOSE_LOG) Log.d(this.getClass().getName(), "onResume");
|
com.newsblur.util.Log.d(this.getClass().getName(), "onResume" + UIUtils.getMemoryUsageDebug(this));
|
||||||
super.onResume();
|
super.onResume();
|
||||||
finishIfNotLoggedIn();
|
finishIfNotLoggedIn();
|
||||||
|
|
||||||
|
@ -81,7 +81,7 @@ public class NbActivity extends Activity {
|
||||||
protected void finishIfNotLoggedIn() {
|
protected void finishIfNotLoggedIn() {
|
||||||
String currentLoginKey = PrefsUtils.getUniqueLoginKey(this);
|
String currentLoginKey = PrefsUtils.getUniqueLoginKey(this);
|
||||||
if(currentLoginKey == null || !currentLoginKey.equals(uniqueLoginKey)) {
|
if(currentLoginKey == null || !currentLoginKey.equals(uniqueLoginKey)) {
|
||||||
Log.d( this.getClass().getName(), "This activity was for a different login. finishing it.");
|
com.newsblur.util.Log.d( this.getClass().getName(), "This activity was for a different login. finishing it.");
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,7 +56,7 @@ public class BlurDatabaseHelper {
|
||||||
private SQLiteDatabase dbRW;
|
private SQLiteDatabase dbRW;
|
||||||
|
|
||||||
public BlurDatabaseHelper(Context context) {
|
public BlurDatabaseHelper(Context context) {
|
||||||
if (AppConstants.VERBOSE_LOG) Log.d(this.getClass().getName(), "new DB conn requested");
|
com.newsblur.util.Log.d(this.getClass().getName(), "new DB conn requested");
|
||||||
this.context = context;
|
this.context = context;
|
||||||
synchronized (RW_MUTEX) {
|
synchronized (RW_MUTEX) {
|
||||||
dbWrapper = new BlurDatabase(context);
|
dbWrapper = new BlurDatabase(context);
|
||||||
|
@ -78,7 +78,9 @@ public class BlurDatabaseHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void dropAndRecreateTables() {
|
public void dropAndRecreateTables() {
|
||||||
|
com.newsblur.util.Log.i(this.getClass().getName(), "dropping and recreating all tables . . .");
|
||||||
synchronized (RW_MUTEX) {dbWrapper.dropAndRecreateTables();}
|
synchronized (RW_MUTEX) {dbWrapper.dropAndRecreateTables();}
|
||||||
|
com.newsblur.util.Log.i(this.getClass().getName(), ". . . tables recreated.");
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getEngineVersion() {
|
public String getEngineVersion() {
|
||||||
|
|
|
@ -279,10 +279,12 @@ public abstract class ItemListFragment extends NbFragment implements OnScrollLis
|
||||||
if (cursor != null) {
|
if (cursor != null) {
|
||||||
if (NBSyncService.ResetSession) {
|
if (NBSyncService.ResetSession) {
|
||||||
// the DB hasn't caught up yet from the last story list; don't display stale stories.
|
// the DB hasn't caught up yet from the last story list; don't display stale stories.
|
||||||
|
com.newsblur.util.Log.i(this.getClass().getName(), "discarding stale load");
|
||||||
triggerRefresh(1, 0);
|
triggerRefresh(1, 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
cursorSeenYet = true;
|
cursorSeenYet = true;
|
||||||
|
com.newsblur.util.Log.d(this.getClass().getName(), "loaded cursor with count: " + cursor.getCount());
|
||||||
if (cursor.getCount() < 1) {
|
if (cursor.getCount() < 1) {
|
||||||
triggerRefresh(1, 0);
|
triggerRefresh(1, 0);
|
||||||
}
|
}
|
||||||
|
|
|
@ -95,7 +95,7 @@ public class APIManager {
|
||||||
public LoginResponse login(final String username, final String password) {
|
public LoginResponse login(final String username, final String password) {
|
||||||
// This call should be pretty rare, but is expensive on the server side. Log it
|
// This call should be pretty rare, but is expensive on the server side. Log it
|
||||||
// at an above-debug level so it will be noticed if it ever gets called too often.
|
// at an above-debug level so it will be noticed if it ever gets called too often.
|
||||||
Log.i(this.getClass().getName(), "calling login API");
|
com.newsblur.util.Log.i(this.getClass().getName(), "calling login API");
|
||||||
final ContentValues values = new ContentValues();
|
final ContentValues values = new ContentValues();
|
||||||
values.put(APIConstants.PARAMETER_USERNAME, username);
|
values.put(APIConstants.PARAMETER_USERNAME, username);
|
||||||
values.put(APIConstants.PARAMETER_PASSWORD, password);
|
values.put(APIConstants.PARAMETER_PASSWORD, password);
|
||||||
|
@ -111,7 +111,7 @@ public class APIManager {
|
||||||
ContentValues values = new ContentValues();
|
ContentValues values = new ContentValues();
|
||||||
values.put(APIConstants.PARAMETER_USER, username);
|
values.put(APIConstants.PARAMETER_USER, username);
|
||||||
String urlString = buildUrl(APIConstants.PATH_LOGINAS) + "?" + builderGetParametersString(values);
|
String urlString = buildUrl(APIConstants.PATH_LOGINAS) + "?" + builderGetParametersString(values);
|
||||||
Log.i(this.getClass().getName(), "doing superuser swap: " + urlString);
|
com.newsblur.util.Log.i(this.getClass().getName(), "doing superuser swap: " + urlString);
|
||||||
// This API returns a redirect that means the call worked, but we do not want to follow it. To
|
// This API returns a redirect that means the call worked, but we do not want to follow it. To
|
||||||
// just get the cookie from the 302 and stop, we directly use a one-off OkHttpClient.
|
// just get the cookie from the 302 and stop, we directly use a one-off OkHttpClient.
|
||||||
Request.Builder requestBuilder = new Request.Builder().url(urlString);
|
Request.Builder requestBuilder = new Request.Builder().url(urlString);
|
||||||
|
@ -692,13 +692,13 @@ public class APIManager {
|
||||||
*/
|
*/
|
||||||
private void backoffSleep(int tryCount) {
|
private void backoffSleep(int tryCount) {
|
||||||
if (tryCount == 0) return;
|
if (tryCount == 0) return;
|
||||||
Log.i(this.getClass().getName(), "API call failed, pausing before retry number " + tryCount);
|
com.newsblur.util.Log.i(this.getClass().getName(), "API call failed, pausing before retry number " + tryCount);
|
||||||
try {
|
try {
|
||||||
// simply double the base sleep time for each subsequent try
|
// simply double the base sleep time for each subsequent try
|
||||||
long factor = Math.round(Math.pow(2.0d, tryCount));
|
long factor = Math.round(Math.pow(2.0d, tryCount));
|
||||||
Thread.sleep(AppConstants.API_BACKOFF_BASE_MILLIS * factor);
|
Thread.sleep(AppConstants.API_BACKOFF_BASE_MILLIS * factor);
|
||||||
} catch (InterruptedException ie) {
|
} catch (InterruptedException ie) {
|
||||||
Log.w(this.getClass().getName(), "Abandoning API backoff due to interrupt.");
|
com.newsblur.util.Log.w(this.getClass().getName(), "Abandoning API backoff due to interrupt.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -54,7 +54,7 @@ public class APIResponse {
|
||||||
this.responseCode = response.code();
|
this.responseCode = response.code();
|
||||||
|
|
||||||
if (responseCode != expectedReturnCode) {
|
if (responseCode != expectedReturnCode) {
|
||||||
Log.e(this.getClass().getName(), "API returned error code " + response.code() + " calling " + request.url().toString() + " - expected " + expectedReturnCode);
|
com.newsblur.util.Log.e(this.getClass().getName(), "API returned error code " + response.code() + " calling " + request.url().toString() + " - expected " + expectedReturnCode);
|
||||||
this.isError = true;
|
this.isError = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -66,7 +66,7 @@ public class APIResponse {
|
||||||
this.responseBody = response.body().string();
|
this.responseBody = response.body().string();
|
||||||
readTime = System.currentTimeMillis() - startTime;
|
readTime = System.currentTimeMillis() - startTime;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Log.e(this.getClass().getName(), e.getClass().getName() + " (" + e.getMessage() + ") reading " + request.url().toString(), e);
|
com.newsblur.util.Log.e(this.getClass().getName(), e.getClass().getName() + " (" + e.getMessage() + ") reading " + request.url().toString(), e);
|
||||||
this.isError = true;
|
this.isError = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -83,12 +83,10 @@ public class APIResponse {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (AppConstants.VERBOSE_LOG_NET) {
|
com.newsblur.util.Log.d(this.getClass().getName(), String.format("called %s in %dms and %dms to read %dB", request.url().toString(), connectTime, readTime, responseBody.length()));
|
||||||
Log.d(this.getClass().getName(), String.format("called %s in %dms and %dms to read %dB", request.url().toString(), connectTime, readTime, responseBody.length()));
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (IOException ioe) {
|
} catch (IOException ioe) {
|
||||||
Log.e(this.getClass().getName(), "Error (" + ioe.getMessage() + ") calling " + request.url().toString(), ioe);
|
com.newsblur.util.Log.e(this.getClass().getName(), "Error (" + ioe.getMessage() + ") calling " + request.url().toString(), ioe);
|
||||||
this.isError = true;
|
this.isError = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -98,6 +96,7 @@ public class APIResponse {
|
||||||
* Construct and empty/offline response. Signals that the call was not made.
|
* Construct and empty/offline response. Signals that the call was not made.
|
||||||
*/
|
*/
|
||||||
public APIResponse(Context context) {
|
public APIResponse(Context context) {
|
||||||
|
com.newsblur.util.Log.w(this.getClass().getName(), "failing an offline API response");
|
||||||
this.isError = true;
|
this.isError = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,11 +20,11 @@ public class NewsBlurResponse {
|
||||||
public boolean isError() {
|
public boolean isError() {
|
||||||
if (isProtocolError) return true;
|
if (isProtocolError) return true;
|
||||||
if ((message != null) && (!message.equals(""))) {
|
if ((message != null) && (!message.equals(""))) {
|
||||||
Log.d(this.getClass().getName(), "Response interpreted as error due to 'message' field: " + message);
|
com.newsblur.util.Log.d(this.getClass().getName(), "Response interpreted as error due to 'message' field: " + message);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if ((errors != null) && (errors.length > 0) && (errors[0] != null)) {
|
if ((errors != null) && (errors.length > 0) && (errors[0] != null)) {
|
||||||
Log.d(this.getClass().getName(), "Response interpreted as error due to 'errors' field: " + errors[0]);
|
com.newsblur.util.Log.d(this.getClass().getName(), "Response interpreted as error due to 'errors' field: " + errors[0]);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -20,24 +20,24 @@ public class CleanupService extends SubService {
|
||||||
|
|
||||||
gotWork();
|
gotWork();
|
||||||
|
|
||||||
if (AppConstants.VERBOSE_LOG) Log.d(this.getClass().getName(), "cleaning up old stories");
|
com.newsblur.util.Log.d(this.getClass().getName(), "cleaning up old stories");
|
||||||
parent.dbHelper.cleanupVeryOldStories();
|
parent.dbHelper.cleanupVeryOldStories();
|
||||||
if (!PrefsUtils.isKeepOldStories(parent)) {
|
if (!PrefsUtils.isKeepOldStories(parent)) {
|
||||||
parent.dbHelper.cleanupReadStories();
|
parent.dbHelper.cleanupReadStories();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (AppConstants.VERBOSE_LOG) Log.d(this.getClass().getName(), "cleaning up old story texts");
|
com.newsblur.util.Log.d(this.getClass().getName(), "cleaning up old story texts");
|
||||||
parent.dbHelper.cleanupStoryText();
|
parent.dbHelper.cleanupStoryText();
|
||||||
|
|
||||||
if (AppConstants.VERBOSE_LOG) Log.d(this.getClass().getName(), "cleaning up story image cache");
|
com.newsblur.util.Log.d(this.getClass().getName(), "cleaning up story image cache");
|
||||||
FileCache imageCache = FileCache.asStoryImageCache(parent);
|
FileCache imageCache = FileCache.asStoryImageCache(parent);
|
||||||
imageCache.cleanupUnusedOrOld(parent.dbHelper.getAllStoryImages());
|
imageCache.cleanupUnusedOrOld(parent.dbHelper.getAllStoryImages());
|
||||||
|
|
||||||
if (AppConstants.VERBOSE_LOG) Log.d(this.getClass().getName(), "cleaning up icon cache");
|
com.newsblur.util.Log.d(this.getClass().getName(), "cleaning up icon cache");
|
||||||
FileCache iconCache = FileCache.asIconCache(parent);
|
FileCache iconCache = FileCache.asIconCache(parent);
|
||||||
iconCache.cleanupOld();
|
iconCache.cleanupOld();
|
||||||
|
|
||||||
if (AppConstants.VERBOSE_LOG) Log.d(this.getClass().getName(), "cleaning up thumbnail cache");
|
com.newsblur.util.Log.d(this.getClass().getName(), "cleaning up thumbnail cache");
|
||||||
FileCache thumbCache = FileCache.asThumbnailCache(parent);
|
FileCache thumbCache = FileCache.asThumbnailCache(parent);
|
||||||
thumbCache.cleanupUnusedOrOld(parent.dbHelper.getAllStoryThumbnails());
|
thumbCache.cleanupUnusedOrOld(parent.dbHelper.getAllStoryThumbnails());
|
||||||
|
|
||||||
|
|
|
@ -169,6 +169,7 @@ public class NBSyncService extends Service {
|
||||||
originalTextService = new OriginalTextService(this);
|
originalTextService = new OriginalTextService(this);
|
||||||
unreadsService = new UnreadsService(this);
|
unreadsService = new UnreadsService(this);
|
||||||
imagePrefetchService = new ImagePrefetchService(this);
|
imagePrefetchService = new ImagePrefetchService(this);
|
||||||
|
com.newsblur.util.Log.offerContext(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -261,7 +262,7 @@ public class NBSyncService extends Service {
|
||||||
if (AppConstants.VERBOSE_LOG) Log.d(this.getClass().getName(), "finishing primary sync");
|
if (AppConstants.VERBOSE_LOG) Log.d(this.getClass().getName(), "finishing primary sync");
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Log.e(this.getClass().getName(), "Sync error.", e);
|
com.newsblur.util.Log.e(this.getClass().getName(), "Sync error.", e);
|
||||||
} finally {
|
} finally {
|
||||||
decrementRunningChild(startId);
|
decrementRunningChild(startId);
|
||||||
}
|
}
|
||||||
|
@ -296,9 +297,9 @@ public class NBSyncService extends Service {
|
||||||
if (upgraded || autoVac) {
|
if (upgraded || autoVac) {
|
||||||
HousekeepingRunning = true;
|
HousekeepingRunning = true;
|
||||||
NbActivity.updateAllActivities(NbActivity.UPDATE_STATUS);
|
NbActivity.updateAllActivities(NbActivity.UPDATE_STATUS);
|
||||||
Log.i(this.getClass().getName(), "rebuilding DB . . .");
|
com.newsblur.util.Log.i(this.getClass().getName(), "rebuilding DB . . .");
|
||||||
dbHelper.vacuum();
|
dbHelper.vacuum();
|
||||||
Log.i(this.getClass().getName(), ". . . . done rebuilding DB");
|
com.newsblur.util.Log.i(this.getClass().getName(), ". . . . done rebuilding DB");
|
||||||
PrefsUtils.updateLastVacuumTime(this);
|
PrefsUtils.updateLastVacuumTime(this);
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -331,7 +332,7 @@ public class NBSyncService extends Service {
|
||||||
try {
|
try {
|
||||||
ra = ReadingAction.fromCursor(c);
|
ra = ReadingAction.fromCursor(c);
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
Log.e(this.getClass().getName(), "error unfreezing ReadingAction", e);
|
com.newsblur.util.Log.e(this.getClass().getName(), "error unfreezing ReadingAction", e);
|
||||||
dbHelper.clearAction(id);
|
dbHelper.clearAction(id);
|
||||||
continue actionsloop;
|
continue actionsloop;
|
||||||
}
|
}
|
||||||
|
@ -339,20 +340,20 @@ public class NBSyncService extends Service {
|
||||||
// don't block story loading unless this is a brand new action
|
// don't block story loading unless this is a brand new action
|
||||||
if ((ra.getTried() > 0) && (PendingFeed != null)) continue actionsloop;
|
if ((ra.getTried() > 0) && (PendingFeed != null)) continue actionsloop;
|
||||||
|
|
||||||
if (AppConstants.VERBOSE_LOG) Log.d(this.getClass().getName(), "attempting action: " + ra.toContentValues().toString());
|
com.newsblur.util.Log.d(this.getClass().getName(), "attempting action: " + ra.toContentValues().toString());
|
||||||
NewsBlurResponse response = ra.doRemote(apiManager);
|
NewsBlurResponse response = ra.doRemote(apiManager);
|
||||||
|
|
||||||
if (response == null) {
|
if (response == null) {
|
||||||
Log.e(this.getClass().getName(), "Discarding reading action with client-side error.");
|
com.newsblur.util.Log.e(this.getClass().getName(), "Discarding reading action with client-side error.");
|
||||||
dbHelper.clearAction(id);
|
dbHelper.clearAction(id);
|
||||||
} else if (response.isProtocolError) {
|
} else if (response.isProtocolError) {
|
||||||
// the network failed or we got a non-200, so be sure we retry
|
// the network failed or we got a non-200, so be sure we retry
|
||||||
Log.i(this.getClass().getName(), "Holding reading action with server-side or network error.");
|
com.newsblur.util.Log.i(this.getClass().getName(), "Holding reading action with server-side or network error.");
|
||||||
dbHelper.incrementActionTried(id);
|
dbHelper.incrementActionTried(id);
|
||||||
noteHardAPIFailure();
|
noteHardAPIFailure();
|
||||||
continue actionsloop;
|
continue actionsloop;
|
||||||
} else if (response.isError()) {
|
} else if (response.isError()) {
|
||||||
Log.e(this.getClass().getName(), "Discarding reading action with user error.");
|
com.newsblur.util.Log.e(this.getClass().getName(), "Discarding reading action with user error.");
|
||||||
dbHelper.clearAction(id);
|
dbHelper.clearAction(id);
|
||||||
String message = response.getErrorMessage(null);
|
String message = response.getErrorMessage(null);
|
||||||
if (message != null) NbActivity.toastError(message);
|
if (message != null) NbActivity.toastError(message);
|
||||||
|
@ -413,6 +414,8 @@ public class NBSyncService extends Service {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
com.newsblur.util.Log.i(this.getClass().getName(), "ready to sync feed list");
|
||||||
|
|
||||||
FFSyncRunning = true;
|
FFSyncRunning = true;
|
||||||
NbActivity.updateAllActivities(NbActivity.UPDATE_STATUS);
|
NbActivity.updateAllActivities(NbActivity.UPDATE_STATUS);
|
||||||
|
|
||||||
|
@ -434,7 +437,7 @@ public class NBSyncService extends Service {
|
||||||
// we should not have got this far without being logged in, so the server either
|
// we should not have got this far without being logged in, so the server either
|
||||||
// expired or ignored out cookie. keep track of this.
|
// expired or ignored out cookie. keep track of this.
|
||||||
isAuth = false;
|
isAuth = false;
|
||||||
Log.w(this.getClass().getName(), "Server ignored or rejected auth cookie.");
|
com.newsblur.util.Log.w(this.getClass().getName(), "Server ignored or rejected auth cookie.");
|
||||||
DoFeedsFolders = true;
|
DoFeedsFolders = true;
|
||||||
noteHardAPIFailure();
|
noteHardAPIFailure();
|
||||||
return;
|
return;
|
||||||
|
@ -528,6 +531,8 @@ public class NBSyncService extends Service {
|
||||||
lastFFWriteMillis = System.currentTimeMillis() - startTime;
|
lastFFWriteMillis = System.currentTimeMillis() - startTime;
|
||||||
lastFeedCount = feedValues.size();
|
lastFeedCount = feedValues.size();
|
||||||
|
|
||||||
|
com.newsblur.util.Log.i(this.getClass().getName(), "got feed list: " + getSpeedInfo());
|
||||||
|
|
||||||
UnreadsService.doMetadata();
|
UnreadsService.doMetadata();
|
||||||
unreadsService.start(startId);
|
unreadsService.start(startId);
|
||||||
cleanupService.start(startId);
|
cleanupService.start(startId);
|
||||||
|
@ -564,6 +569,8 @@ public class NBSyncService extends Service {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
com.newsblur.util.Log.i(this.getClass().getName(), "recounting dirty feed sets: " + dirtySets.size());
|
||||||
|
|
||||||
// if we are offline, the best we can do is perform a local unread recount and
|
// if we are offline, the best we can do is perform a local unread recount and
|
||||||
// save the true one for when we go back online.
|
// save the true one for when we go back online.
|
||||||
if (!NetworkUtils.isOnline(this)) {
|
if (!NetworkUtils.isOnline(this)) {
|
||||||
|
@ -582,7 +589,7 @@ public class NBSyncService extends Service {
|
||||||
|
|
||||||
UnreadCountResponse apiResponse = apiManager.getFeedUnreadCounts(apiIds);
|
UnreadCountResponse apiResponse = apiManager.getFeedUnreadCounts(apiIds);
|
||||||
if ((apiResponse == null) || (apiResponse.isError())) {
|
if ((apiResponse == null) || (apiResponse.isError())) {
|
||||||
Log.w(this.getClass().getName(), "Bad response to feed_unread_count");
|
com.newsblur.util.Log.w(this.getClass().getName(), "Bad response to feed_unread_count");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (apiResponse.feeds != null ) {
|
if (apiResponse.feeds != null ) {
|
||||||
|
@ -633,7 +640,7 @@ public class NBSyncService extends Service {
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
if (ExhaustedFeeds.contains(fs)) {
|
if (ExhaustedFeeds.contains(fs)) {
|
||||||
Log.i(this.getClass().getName(), "No more stories for feed set: " + fs);
|
com.newsblur.util.Log.i(this.getClass().getName(), "No more stories for feed set: " + fs);
|
||||||
finished = true;
|
finished = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -719,11 +726,11 @@ public class NBSyncService extends Service {
|
||||||
|
|
||||||
private boolean isStoryResponseGood(StoriesResponse response) {
|
private boolean isStoryResponseGood(StoriesResponse response) {
|
||||||
if (response == null) {
|
if (response == null) {
|
||||||
Log.e(this.getClass().getName(), "Null response received while loading stories.");
|
com.newsblur.util.Log.e(this.getClass().getName(), "Null response received while loading stories.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (response.stories == null) {
|
if (response.stories == null) {
|
||||||
Log.e(this.getClass().getName(), "Null stories member received while loading stories.");
|
com.newsblur.util.Log.e(this.getClass().getName(), "Null stories member received while loading stories.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
@ -772,10 +779,12 @@ public class NBSyncService extends Service {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
com.newsblur.util.Log.d(NBSyncService.class.getName(), "got stories from main fetch loop: " + apiResponse.stories.length);
|
||||||
dbHelper.insertStories(apiResponse, true);
|
dbHelper.insertStories(apiResponse, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void insertStories(StoriesResponse apiResponse) {
|
void insertStories(StoriesResponse apiResponse) {
|
||||||
|
com.newsblur.util.Log.d(NBSyncService.class.getName(), "got stories from sub sync: " + apiResponse.stories.length);
|
||||||
dbHelper.insertStories(apiResponse, false);
|
dbHelper.insertStories(apiResponse, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -818,7 +827,7 @@ public class NBSyncService extends Service {
|
||||||
|
|
||||||
static boolean stopSync(Context context) {
|
static boolean stopSync(Context context) {
|
||||||
if (HaltNow) {
|
if (HaltNow) {
|
||||||
if (AppConstants.VERBOSE_LOG) Log.d(NBSyncService.class.getName(), "stopping sync, soft interrupt set.");
|
com.newsblur.util.Log.i(NBSyncService.class.getName(), "stopping sync, soft interrupt set.");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (context == null) return false;
|
if (context == null) return false;
|
||||||
|
@ -834,13 +843,14 @@ public class NBSyncService extends Service {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void noteHardAPIFailure() {
|
private void noteHardAPIFailure() {
|
||||||
|
com.newsblur.util.Log.w(this.getClass().getName(), "hard API failure");
|
||||||
lastAPIFailure = System.currentTimeMillis();
|
lastAPIFailure = System.currentTimeMillis();
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean backoffBackgroundCalls() {
|
private boolean backoffBackgroundCalls() {
|
||||||
if (NbActivity.getActiveActivityCount() > 0) return false;
|
if (NbActivity.getActiveActivityCount() > 0) return false;
|
||||||
if (System.currentTimeMillis() > (lastAPIFailure + AppConstants.API_BACKGROUND_BACKOFF_MILLIS)) return false;
|
if (System.currentTimeMillis() > (lastAPIFailure + AppConstants.API_BACKGROUND_BACKOFF_MILLIS)) return false;
|
||||||
Log.i(this.getClass().getName(), "abandoning background sync due to recent API failures.");
|
com.newsblur.util.Log.i(this.getClass().getName(), "abandoning background sync due to recent API failures.");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -927,7 +937,7 @@ public class NBSyncService extends Service {
|
||||||
*/
|
*/
|
||||||
public static boolean requestMoreForFeed(FeedSet fs, int desiredStoryCount, int callerSeen) {
|
public static boolean requestMoreForFeed(FeedSet fs, int desiredStoryCount, int callerSeen) {
|
||||||
if (ExhaustedFeeds.contains(fs)) {
|
if (ExhaustedFeeds.contains(fs)) {
|
||||||
if (AppConstants.VERBOSE_LOG) Log.i(NBSyncService.class.getName(), "rejecting request for feedset that is exhaused");
|
com.newsblur.util.Log.d(NBSyncService.class.getName(), "rejecting request for feedset that is exhaused");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -963,6 +973,7 @@ public class NBSyncService extends Service {
|
||||||
* session gets cleared before the next one is populated.
|
* session gets cleared before the next one is populated.
|
||||||
*/
|
*/
|
||||||
public static void resetReadingSession() {
|
public static void resetReadingSession() {
|
||||||
|
com.newsblur.util.Log.d(NBSyncService.class.getName(), "requesting reading session reset");
|
||||||
synchronized (PENDING_FEED_MUTEX) {
|
synchronized (PENDING_FEED_MUTEX) {
|
||||||
PendingFeed = null;
|
PendingFeed = null;
|
||||||
ResetSession = true;
|
ResetSession = true;
|
||||||
|
@ -973,7 +984,7 @@ public class NBSyncService extends Service {
|
||||||
* Reset the API pagniation state for the given feedset, presumably because the order or filter changed.
|
* Reset the API pagniation state for the given feedset, presumably because the order or filter changed.
|
||||||
*/
|
*/
|
||||||
public static void resetFetchState(FeedSet fs) {
|
public static void resetFetchState(FeedSet fs) {
|
||||||
Log.d(NBSyncService.class.getName(), "requesting feed fetch state reset");
|
com.newsblur.util.Log.d(NBSyncService.class.getName(), "requesting feed fetch state reset");
|
||||||
ResetFeed = fs;
|
ResetFeed = fs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -996,7 +1007,7 @@ public class NBSyncService extends Service {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void softInterrupt() {
|
public static void softInterrupt() {
|
||||||
if (AppConstants.VERBOSE_LOG) Log.d(NBSyncService.class.getName(), "soft stop");
|
com.newsblur.util.Log.i(NBSyncService.class.getName(), "soft stop");
|
||||||
HaltNow = true;
|
HaltNow = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1041,7 +1052,7 @@ public class NBSyncService extends Service {
|
||||||
if (AppConstants.VERBOSE_LOG) Log.d(this.getClass().getName(), "onDestroy - execution halted");
|
if (AppConstants.VERBOSE_LOG) Log.d(this.getClass().getName(), "onDestroy - execution halted");
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
Log.e(this.getClass().getName(), "unclean shutdown", ex);
|
com.newsblur.util.Log.e(this.getClass().getName(), "unclean shutdown", ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -58,7 +58,7 @@ public abstract class SubService {
|
||||||
//if (AppConstants.VERBOSE_LOG) Log.d(this.getClass().getName(), "SubService completed");
|
//if (AppConstants.VERBOSE_LOG) Log.d(this.getClass().getName(), "SubService completed");
|
||||||
cycleStartTime = 0;
|
cycleStartTime = 0;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Log.e(this.getClass().getName(), "Sync error.", e);
|
com.newsblur.util.Log.e(this.getClass().getName(), "Sync error.", e);
|
||||||
} finally {
|
} finally {
|
||||||
if (isRunning()) {
|
if (isRunning()) {
|
||||||
setRunning(false);
|
setRunning(false);
|
||||||
|
@ -116,7 +116,7 @@ public abstract class SubService {
|
||||||
if (cooloffTimeMs > AppConstants.DUTY_CYCLE_BACKOFF_CAP_MILLIS) cooloffTimeMs = AppConstants.DUTY_CYCLE_BACKOFF_CAP_MILLIS;
|
if (cooloffTimeMs > AppConstants.DUTY_CYCLE_BACKOFF_CAP_MILLIS) cooloffTimeMs = AppConstants.DUTY_CYCLE_BACKOFF_CAP_MILLIS;
|
||||||
|
|
||||||
if (NbActivity.getActiveActivityCount() > 0 ) {
|
if (NbActivity.getActiveActivityCount() > 0 ) {
|
||||||
if (AppConstants.VERBOSE_LOG) Log.d(this.getClass().getName(), "Sleeping for : " + cooloffTimeMs + "ms to enforce max duty cycle.");
|
com.newsblur.util.Log.d(this.getClass().getName(), "Sleeping for : " + cooloffTimeMs + "ms to enforce max duty cycle.");
|
||||||
try {
|
try {
|
||||||
Thread.sleep(cooloffTimeMs);
|
Thread.sleep(cooloffTimeMs);
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
|
|
|
@ -61,6 +61,7 @@ public class UnreadsService extends SubService {
|
||||||
// the set of unreads from the API, we will mark them as read. note that this collection
|
// the set of unreads from the API, we will mark them as read. note that this collection
|
||||||
// will be searched many times for new unreads, so it should be a Set, not a List.
|
// will be searched many times for new unreads, so it should be a Set, not a List.
|
||||||
Set<String> oldUnreadHashes = parent.dbHelper.getUnreadStoryHashesAsSet();
|
Set<String> oldUnreadHashes = parent.dbHelper.getUnreadStoryHashesAsSet();
|
||||||
|
com.newsblur.util.Log.i(this.getClass().getName(), "starting unread count: " + oldUnreadHashes.size());
|
||||||
|
|
||||||
// a place to store and then sort unread hashes we aim to fetch. note the member format
|
// a place to store and then sort unread hashes we aim to fetch. note the member format
|
||||||
// is made to match the format of the API response (a list of [hash, date] tuples). it
|
// is made to match the format of the API response (a list of [hash, date] tuples). it
|
||||||
|
@ -69,6 +70,7 @@ public class UnreadsService extends SubService {
|
||||||
|
|
||||||
// process the api response, both bookkeeping no-longer-unread stories and populating
|
// process the api response, both bookkeeping no-longer-unread stories and populating
|
||||||
// the sortation list we will use to create the fetch list for step two
|
// the sortation list we will use to create the fetch list for step two
|
||||||
|
int count = 0;
|
||||||
feedloop: for (Entry<String, List<String[]>> entry : unreadHashes.unreadHashes.entrySet()) {
|
feedloop: for (Entry<String, List<String[]>> entry : unreadHashes.unreadHashes.entrySet()) {
|
||||||
// the API gives us a list of unreads, split up by feed ID. the unreads are tuples of
|
// the API gives us a list of unreads, split up by feed ID. the unreads are tuples of
|
||||||
// story hash and date
|
// story hash and date
|
||||||
|
@ -84,8 +86,12 @@ public class UnreadsService extends SubService {
|
||||||
} else {
|
} else {
|
||||||
oldUnreadHashes.remove(newUnread[0]);
|
oldUnreadHashes.remove(newUnread[0]);
|
||||||
}
|
}
|
||||||
|
count++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
com.newsblur.util.Log.i(this.getClass().getName(), "new unread count: " + count);
|
||||||
|
com.newsblur.util.Log.i(this.getClass().getName(), "new unreads found: " + sortationList.size());
|
||||||
|
com.newsblur.util.Log.i(this.getClass().getName(), "unreads to retire: " + oldUnreadHashes.size());
|
||||||
|
|
||||||
// now sort the unreads we need to fetch so they are fetched roughly in the order
|
// now sort the unreads we need to fetch so they are fetched roughly in the order
|
||||||
// the user is likely to read them. if the user reads newest first, those come first.
|
// the user is likely to read them. if the user reads newest first, those come first.
|
||||||
|
|
|
@ -143,7 +143,13 @@ public class FeedUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void setStoryReadState(Story story, Context context, boolean read) {
|
private static void setStoryReadState(Story story, Context context, boolean read) {
|
||||||
dbHelper.touchStory(story.storyHash);
|
try {
|
||||||
|
// this shouldn't throw errors, but crash logs suggest something is racing it for DB resources.
|
||||||
|
// capture logs in hopes of finding the correlated action
|
||||||
|
dbHelper.touchStory(story.storyHash);
|
||||||
|
} catch (Exception e) {
|
||||||
|
com.newsblur.util.Log.e(FeedUtils.class.getName(), "error touching story state in DB", e);
|
||||||
|
}
|
||||||
if (story.read == read) { return; }
|
if (story.read == read) { return; }
|
||||||
|
|
||||||
// tell the sync service we need to mark read
|
// tell the sync service we need to mark read
|
||||||
|
|
135
clients/android/NewsBlur/src/com/newsblur/util/Log.java
Normal file
135
clients/android/NewsBlur/src/com/newsblur/util/Log.java
Normal file
|
@ -0,0 +1,135 @@
|
||||||
|
package com.newsblur.util;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.BufferedWriter;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileReader;
|
||||||
|
import java.io.FileWriter;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Queue;
|
||||||
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A low-overhead, fail-fast, dependency-free, file-backed log collector. This utility
|
||||||
|
* will persist debug messages in a file in such a way that sacrifices any guarantees
|
||||||
|
* in order to have as few side-effects as possible. The resulting log file will have
|
||||||
|
* as many of the messages sent here as possible, but should not be expected to have
|
||||||
|
* all of them. The file is trimmed down to a set number of lines if it grows too big.
|
||||||
|
*/
|
||||||
|
public class Log {
|
||||||
|
|
||||||
|
private static final String D = "DEBUG ";
|
||||||
|
private static final String I = "INFO ";
|
||||||
|
private static final String W = "WARN ";
|
||||||
|
private static final String E = "ERROR ";
|
||||||
|
|
||||||
|
private static final String LOG_NAME_INTERNAL = "logbuffer.txt";
|
||||||
|
private static final int MAX_LINE_SIZE = 4 * 1024;
|
||||||
|
private static final int TRIM_LINES = 256; // trim the log down to 256 lines
|
||||||
|
private static final long MAX_SIZE = 512L * MAX_LINE_SIZE; // when it is at least 512 lines long
|
||||||
|
|
||||||
|
private static Queue<String> q;
|
||||||
|
private static ExecutorService executor;
|
||||||
|
private static File logloc = null;
|
||||||
|
static {
|
||||||
|
q = new ConcurrentLinkedQueue<String>();
|
||||||
|
executor = Executors.newFixedThreadPool(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Log() {} // util class - no instances
|
||||||
|
|
||||||
|
public static void d(String tag, String m) {
|
||||||
|
if (AppConstants.VERBOSE_LOG) android.util.Log.d(tag, m);
|
||||||
|
add(D, tag, m, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void i(String tag, String m) {
|
||||||
|
android.util.Log.i(tag, m);
|
||||||
|
add(I, tag, m, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void w(String tag, String m) {
|
||||||
|
android.util.Log.w(tag, m);
|
||||||
|
add(W, tag, m, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void e(String tag, String m) {
|
||||||
|
android.util.Log.e(tag, m);
|
||||||
|
add(E, tag, m, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void e(String tag, String m, Throwable t) {
|
||||||
|
android.util.Log.e(tag, m, t);
|
||||||
|
add(E, tag, m, t);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void add(String lvl, String tag, String m, Throwable t) {
|
||||||
|
if (q.size() > TRIM_LINES) return;
|
||||||
|
if (m != null && m.length() > MAX_LINE_SIZE) m = m.substring(0, MAX_LINE_SIZE);
|
||||||
|
StringBuilder s = new StringBuilder();
|
||||||
|
s.append(Long.toString(System.currentTimeMillis()))
|
||||||
|
.append(" ")
|
||||||
|
.append(lvl)
|
||||||
|
.append(tag)
|
||||||
|
.append(" ");
|
||||||
|
if (t != null) {
|
||||||
|
s.append(t.getMessage());
|
||||||
|
s.append(" ");
|
||||||
|
}
|
||||||
|
s.append(m);
|
||||||
|
q.offer(s.toString());
|
||||||
|
Runnable r = new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
proc();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
executor.execute(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void offerContext(Context c) {
|
||||||
|
logloc = c.getExternalCacheDir();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void proc() {
|
||||||
|
synchronized (q) {
|
||||||
|
if (logloc == null) return; // not yet spun up
|
||||||
|
String line = q.poll();
|
||||||
|
if (line == null) return;
|
||||||
|
File f = new File(logloc, LOG_NAME_INTERNAL);
|
||||||
|
try (BufferedWriter w = new BufferedWriter(new FileWriter(f, true))) {
|
||||||
|
w.append(line);
|
||||||
|
w.newLine();
|
||||||
|
} catch (Throwable t) {
|
||||||
|
; // explicitly do nothing, log nothing, and fail fast. this is a utility to
|
||||||
|
// provice as much info as possible while having absolute minimal impact or
|
||||||
|
// side effect on performance or app operation
|
||||||
|
}
|
||||||
|
if (f.length() < MAX_SIZE) return;
|
||||||
|
android.util.Log.i(Log.class.getName(), "trimming");
|
||||||
|
List<String> lines = new ArrayList<String>(TRIM_LINES * 2);
|
||||||
|
try (BufferedReader r = new BufferedReader(new FileReader(f))) {
|
||||||
|
for (String l = r.readLine(); l != null; l = r.readLine()) {
|
||||||
|
lines.add(l);
|
||||||
|
}
|
||||||
|
} catch (Throwable t) {;}
|
||||||
|
int offset = lines.size() - TRIM_LINES;
|
||||||
|
try (BufferedWriter w = new BufferedWriter(new FileWriter(f, false))) {
|
||||||
|
for (int i = offset; i < lines.size(); i++) {
|
||||||
|
w.append(lines.get(i));
|
||||||
|
w.newLine();
|
||||||
|
}
|
||||||
|
} catch (Throwable t) {;}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static File getLogfile() {
|
||||||
|
return new File(logloc, LOG_NAME_INTERNAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -62,7 +62,7 @@ public class PrefsUtils {
|
||||||
|
|
||||||
String oldVersion = prefs.getString(AppConstants.LAST_APP_VERSION, null);
|
String oldVersion = prefs.getString(AppConstants.LAST_APP_VERSION, null);
|
||||||
if ( (oldVersion == null) || (!oldVersion.equals(version)) ) {
|
if ( (oldVersion == null) || (!oldVersion.equals(version)) ) {
|
||||||
Log.i(PrefsUtils.class.getName(), "detected new version of app:" + version);
|
com.newsblur.util.Log.i(PrefsUtils.class.getName(), "detected new version of app:" + version);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@ -88,17 +88,48 @@ public class PrefsUtils {
|
||||||
|
|
||||||
public static String createFeedbackLink(Context context) {
|
public static String createFeedbackLink(Context context) {
|
||||||
StringBuilder s = new StringBuilder(AppConstants.FEEDBACK_URL);
|
StringBuilder s = new StringBuilder(AppConstants.FEEDBACK_URL);
|
||||||
s.append("<give us some feedback!>%0A%0A");
|
s.append("<give us some feedback!>%0A%0A%0A");
|
||||||
s.append("%0Aapp version: ").append(getVersion(context));
|
String info = getDebugInfo(context);
|
||||||
s.append("%0Aandroid version: ").append(Build.VERSION.RELEASE).append(" (" + Build.DISPLAY + ")");
|
s.append(info.replace("\n", "%0A"));
|
||||||
s.append("%0Adevice: ").append(Build.MANUFACTURER + "+" + Build.MODEL + "+(" + Build.BOARD + ")");
|
return s.toString();
|
||||||
s.append("%0Asqlite version: ").append(FeedUtils.dbHelper.getEngineVersion());
|
}
|
||||||
s.append("%0Ausername: ").append(getUserDetails(context).username);
|
|
||||||
s.append("%0Aserver: ").append(APIConstants.isCustomServer() ? "default" : "custom");
|
public static void sendLogEmail(Context context) {
|
||||||
s.append("%0Amemory: ").append(NBSyncService.isMemoryLow() ? "low" : "normal");
|
File f = com.newsblur.util.Log.getLogfile();
|
||||||
s.append("%0Aspeed: ").append(NBSyncService.getSpeedInfo());
|
if (f == null) return;
|
||||||
s.append("%0Apending actions: ").append(NBSyncService.getPendingInfo());
|
android.net.Uri localPath = android.net.Uri.fromFile(f);
|
||||||
s.append("%0Apremium: ");
|
Intent i = new Intent(Intent.ACTION_SEND);
|
||||||
|
i.setType("*/*");
|
||||||
|
i.putExtra(Intent.EXTRA_EMAIL, new String[]{"android@newsblur.com"});
|
||||||
|
i.putExtra(Intent.EXTRA_SUBJECT, "Android logs");
|
||||||
|
i.putExtra(Intent.EXTRA_TEXT, getDebugInfo(context));
|
||||||
|
i.putExtra(Intent.EXTRA_STREAM, localPath);
|
||||||
|
if (i.resolveActivity(context.getPackageManager()) != null) {
|
||||||
|
context.startActivity(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getDebugInfo(Context context) {
|
||||||
|
StringBuilder s = new StringBuilder();
|
||||||
|
s.append("app version: ").append(getVersion(context));
|
||||||
|
s.append("\n");
|
||||||
|
s.append("android version: ").append(Build.VERSION.RELEASE).append(" (" + Build.DISPLAY + ")");
|
||||||
|
s.append("\n");
|
||||||
|
s.append("device: ").append(Build.MANUFACTURER + "+" + Build.MODEL + "+(" + Build.BOARD + ")");
|
||||||
|
s.append("\n");
|
||||||
|
s.append("sqlite version: ").append(FeedUtils.dbHelper.getEngineVersion());
|
||||||
|
s.append("\n");
|
||||||
|
s.append("username: ").append(getUserDetails(context).username);
|
||||||
|
s.append("\n");
|
||||||
|
s.append("server: ").append(APIConstants.isCustomServer() ? "default" : "custom");
|
||||||
|
s.append("\n");
|
||||||
|
s.append("memory: ").append(NBSyncService.isMemoryLow() ? "low" : "normal");
|
||||||
|
s.append("\n");
|
||||||
|
s.append("speed: ").append(NBSyncService.getSpeedInfo());
|
||||||
|
s.append("\n");
|
||||||
|
s.append("pending actions: ").append(NBSyncService.getPendingInfo());
|
||||||
|
s.append("\n");
|
||||||
|
s.append("premium: ");
|
||||||
if (NBSyncService.isPremium == Boolean.TRUE) {
|
if (NBSyncService.isPremium == Boolean.TRUE) {
|
||||||
s.append("yes");
|
s.append("yes");
|
||||||
} else if (NBSyncService.isPremium == Boolean.FALSE) {
|
} else if (NBSyncService.isPremium == Boolean.FALSE) {
|
||||||
|
@ -106,9 +137,13 @@ public class PrefsUtils {
|
||||||
} else {
|
} else {
|
||||||
s.append("unknown");
|
s.append("unknown");
|
||||||
}
|
}
|
||||||
s.append("%0Aprefetch: ").append(isOfflineEnabled(context) ? "yes" : "no");
|
s.append("\n");
|
||||||
s.append("%0Akeepread: ").append(isKeepOldStories(context) ? "yes" : "no");
|
s.append("prefetch: ").append(isOfflineEnabled(context) ? "yes" : "no");
|
||||||
s.append("%0Athumbs: ").append(isShowThumbnails(context) ? "yes" : "no");
|
s.append("\n");
|
||||||
|
s.append("keepread: ").append(isKeepOldStories(context) ? "yes" : "no");
|
||||||
|
s.append("\n");
|
||||||
|
s.append("thumbs: ").append(isShowThumbnails(context) ? "yes" : "no");
|
||||||
|
s.append("\n");
|
||||||
return s.toString();
|
return s.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue