diff --git a/briar-android/src/main/AndroidManifest.xml b/briar-android/src/main/AndroidManifest.xml index 5d54ab2e1..8ea033382 100644 --- a/briar-android/src/main/AndroidManifest.xml +++ b/briar-android/src/main/AndroidManifest.xml @@ -79,11 +79,8 @@ android:windowSoftInputMode="adjustResize|stateHidden" /> diff --git a/briar-android/src/main/java/org/briarproject/briar/android/AndroidComponent.java b/briar-android/src/main/java/org/briarproject/briar/android/AndroidComponent.java index 80dbbeb4f..b1168d848 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/AndroidComponent.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/AndroidComponent.java @@ -33,9 +33,6 @@ import org.briarproject.briar.BriarCoreModule; import org.briarproject.briar.android.attachment.AttachmentModule; import org.briarproject.briar.android.conversation.glide.BriarModelLoader; import org.briarproject.briar.android.login.SignInReminderReceiver; -import org.briarproject.briar.android.reporting.CrashFragment; -import org.briarproject.briar.android.reporting.CrashReportActivity; -import org.briarproject.briar.android.reporting.ReportFormFragment; import org.briarproject.briar.android.view.EmojiTextInputView; import org.briarproject.briar.api.android.AndroidNotificationManager; import org.briarproject.briar.api.android.DozeWatchdog; @@ -175,10 +172,6 @@ public interface AndroidComponent void inject(BriarService briarService); - void inject(CrashReportActivity crashReportActivity); - void inject(ReportFormFragment reportFormFragment); - void inject(CrashFragment crashFragment); - void inject(NotificationCleanupService notificationCleanupService); void inject(EmojiTextInputView textInputView); diff --git a/briar-android/src/main/java/org/briarproject/briar/android/BriarApplication.java b/briar-android/src/main/java/org/briarproject/briar/android/BriarApplication.java index bf07eda7b..09946b3a7 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/BriarApplication.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/BriarApplication.java @@ -23,7 +23,5 @@ public interface BriarApplication extends BrambleApplication { SharedPreferences getDefaultSharedPreferences(); - void triggerFeedback(); - boolean isRunningInBackground(); } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/BriarApplicationImpl.java b/briar-android/src/main/java/org/briarproject/briar/android/BriarApplicationImpl.java index 9cd8332ef..64022ec6f 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/BriarApplicationImpl.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/BriarApplicationImpl.java @@ -151,11 +151,6 @@ public class BriarApplicationImpl extends Application return prefs; } - @Override - public void triggerFeedback() { - exceptionHandler.feedback(); - } - @Override public boolean isRunningInBackground() { RunningAppProcessInfo info = new RunningAppProcessInfo(); diff --git a/briar-android/src/main/java/org/briarproject/briar/android/activity/ActivityComponent.java b/briar-android/src/main/java/org/briarproject/briar/android/activity/ActivityComponent.java index e71e7d786..51e524319 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/activity/ActivityComponent.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/activity/ActivityComponent.java @@ -66,6 +66,9 @@ import org.briarproject.briar.android.privategroup.memberlist.GroupMemberModule; import org.briarproject.briar.android.privategroup.reveal.GroupRevealModule; import org.briarproject.briar.android.privategroup.reveal.RevealContactsActivity; import org.briarproject.briar.android.privategroup.reveal.RevealContactsFragment; +import org.briarproject.briar.android.reporting.CrashFragment; +import org.briarproject.briar.android.reporting.CrashReportActivity; +import org.briarproject.briar.android.reporting.ReportFormFragment; import org.briarproject.briar.android.settings.SettingsActivity; import org.briarproject.briar.android.settings.SettingsFragment; import org.briarproject.briar.android.sharing.BlogInvitationActivity; @@ -184,6 +187,8 @@ public interface ActivityComponent { void inject(PendingContactListActivity activity); + void inject(CrashReportActivity crashReportActivity); + // Fragments void inject(AuthorNameFragment fragment); @@ -234,4 +239,8 @@ public interface ActivityComponent { void inject(ImageFragment imageFragment); + void inject(ReportFormFragment reportFormFragment); + + void inject(CrashFragment crashFragment); + } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/activity/BaseActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/activity/BaseActivity.java index 8369634ef..f67f1fc91 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/activity/BaseActivity.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/activity/BaseActivity.java @@ -18,7 +18,6 @@ import org.briarproject.briar.android.controller.ActivityLifecycleController; import org.briarproject.briar.android.forum.ForumModule; import org.briarproject.briar.android.fragment.BaseFragment; import org.briarproject.briar.android.fragment.ScreenFilterDialogFragment; -import org.briarproject.briar.android.reporting.CrashReportActivity; import org.briarproject.briar.android.util.UiUtils; import org.briarproject.briar.android.widget.TapSafeFrameLayout; import org.briarproject.briar.android.widget.TapSafeFrameLayout.OnTapFilteredListener; @@ -50,7 +49,6 @@ import static org.briarproject.briar.android.util.UiUtils.hideSoftKeyboard; /** * Warning: Some activities don't extend {@link BaseActivity}. - * E.g. {@link CrashReportActivity} */ @MethodsNotNullByDefault @ParametersNotNullByDefault @@ -123,6 +121,7 @@ public abstract class BaseActivity extends AppCompatActivity return new ActivityModule(this); } + // TODO use a test module where this is used in tests protected ForumModule getForumModule() { return new ForumModule(); } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/ContactExchangeErrorFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/ContactExchangeErrorFragment.java index a4bd9c8f2..be708d73c 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/ContactExchangeErrorFragment.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/ContactExchangeErrorFragment.java @@ -10,13 +10,10 @@ import android.widget.TextView; import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; -import org.briarproject.bramble.api.system.AndroidExecutor; import org.briarproject.briar.R; -import org.briarproject.briar.android.BriarApplication; import org.briarproject.briar.android.activity.ActivityComponent; import org.briarproject.briar.android.fragment.BaseFragment; - -import javax.inject.Inject; +import org.briarproject.briar.android.util.UiUtils; import androidx.annotation.Nullable; import androidx.fragment.app.FragmentActivity; @@ -41,9 +38,6 @@ public class ContactExchangeErrorFragment extends BaseFragment { return f; } - @Inject - AndroidExecutor androidExecutor; - @Override public String getUniqueTag() { return TAG; @@ -88,10 +82,8 @@ public class ContactExchangeErrorFragment extends BaseFragment { } private void triggerFeedback() { - BriarApplication app = - (BriarApplication) requireContext().getApplicationContext(); + UiUtils.triggerFeedback(requireContext()); finish(); - app.triggerFeedback(); } } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/reporting/BriarExceptionHandler.java b/briar-android/src/main/java/org/briarproject/briar/android/reporting/BriarExceptionHandler.java index 8858d769c..3568bc8b4 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/reporting/BriarExceptionHandler.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/reporting/BriarExceptionHandler.java @@ -1,19 +1,13 @@ package org.briarproject.briar.android.reporting; import android.content.Context; -import android.content.Intent; import android.os.Process; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; -import org.briarproject.briar.android.util.UserFeedback; import java.lang.Thread.UncaughtExceptionHandler; -import androidx.fragment.app.FragmentActivity; - -import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; -import static org.briarproject.briar.android.reporting.CrashReportActivity.EXTRA_APP_START_TIME; -import static org.briarproject.briar.android.reporting.CrashReportActivity.EXTRA_THROWABLE; +import static org.briarproject.briar.android.util.UiUtils.startDevReportActivity; @NotNullByDefault public class BriarExceptionHandler implements UncaughtExceptionHandler { @@ -29,22 +23,9 @@ public class BriarExceptionHandler implements UncaughtExceptionHandler { @Override public void uncaughtException(Thread t, Throwable e) { // activity runs in its own process, so we can kill the old one - startDevReportActivity(CrashReportActivity.class, e); + startDevReportActivity(ctx, CrashReportActivity.class, e, appStartTime); Process.killProcess(Process.myPid()); System.exit(10); } - public void feedback() { - startDevReportActivity(FeedbackActivity.class, new UserFeedback()); - } - - private void startDevReportActivity( - Class activity, Throwable e) { - final Intent dialogIntent = new Intent(ctx, activity); - dialogIntent.setFlags(FLAG_ACTIVITY_NEW_TASK); - dialogIntent.putExtra(EXTRA_THROWABLE, e); - dialogIntent.putExtra(EXTRA_APP_START_TIME, appStartTime); - ctx.startActivity(dialogIntent); - } - } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/reporting/BriarReportCollector.java b/briar-android/src/main/java/org/briarproject/briar/android/reporting/BriarReportCollector.java index cd0e6622d..3f3e3f349 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/reporting/BriarReportCollector.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/reporting/BriarReportCollector.java @@ -21,12 +21,14 @@ import android.os.Build; import android.os.Environment; import org.briarproject.bramble.api.Pair; +import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.briar.BuildConfig; import org.briarproject.briar.R; import org.briarproject.briar.android.BriarApplication; import org.briarproject.briar.android.logging.BriefLogFormatter; import org.briarproject.briar.android.reporting.ReportData.MultiReportInfo; import org.briarproject.briar.android.reporting.ReportData.ReportItem; +import org.briarproject.briar.android.reporting.ReportData.SingleReportInfo; import java.io.File; import java.io.PrintWriter; @@ -40,6 +42,8 @@ import java.util.Date; import java.util.logging.Formatter; import java.util.logging.LogRecord; +import javax.annotation.concurrent.Immutable; + import androidx.annotation.Nullable; import static android.bluetooth.BluetoothAdapter.SCAN_MODE_CONNECTABLE; @@ -56,6 +60,8 @@ import static org.briarproject.bramble.util.PrivacyUtils.scrubInetAddress; import static org.briarproject.bramble.util.PrivacyUtils.scrubMacAddress; import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty; +@Immutable +@NotNullByDefault class BriarReportCollector { private final Context ctx; @@ -66,9 +72,11 @@ class BriarReportCollector { public ReportData collectReportData(@Nullable Throwable t, long appStartTime) { - return new ReportData() + ReportData reportData = new ReportData() .add(getBasicInfo(t)) - .add(getDeviceInfo()) + .add(getDeviceInfo()); + if (t != null) reportData.add(getStacktrace(t)); + return reportData .add(getTimeInfo(appStartTime)) .add(getMemory()) .add(getStorage()) @@ -91,27 +99,18 @@ class BriarReportCollector { versionCode = "?"; } MultiReportInfo basicInfo = new MultiReportInfo() - .add("Package name", packageName) - .add("Version name", versionName) - .add("Version code", versionCode); - // print stacktrace of Throwable if this is not feedback - if (t != null) { - final Writer sw = new StringWriter(); - final PrintWriter printWriter = new PrintWriter(sw); - if (!isNullOrEmpty(t.getMessage())) { - printWriter.println(t.getMessage()); - } - t.printStackTrace(printWriter); - basicInfo.add("stracktrace", sw.toString()); - } + .add("PackageName", packageName) + .add("VersionName", versionName) + .add("VersionCode", versionCode) + .add("isCrashReport", String.valueOf(t != null)); return new ReportItem("BasicInfo", R.string.dev_report_basic_info, basicInfo, false); } private ReportItem getDeviceInfo() { MultiReportInfo deviceInfo = new MultiReportInfo() - .add("Android version", Build.VERSION.RELEASE) - .add("Android SDK API", String.valueOf(SDK_INT)) + .add("AndroidVersion", Build.VERSION.RELEASE) + .add("AndroidApi", String.valueOf(SDK_INT)) .add("Product", Build.PRODUCT) .add("Model", Build.MODEL) .add("Brand", Build.BRAND); @@ -119,10 +118,24 @@ class BriarReportCollector { deviceInfo); } + private ReportItem getStacktrace(Throwable t) { + final Writer sw = new StringWriter(); + final PrintWriter printWriter = new PrintWriter(sw); + if (!isNullOrEmpty(t.getMessage())) { + printWriter.println(t.getMessage()); + } + t.printStackTrace(printWriter); + SingleReportInfo stacktrace = new SingleReportInfo(sw.toString()); + return new ReportItem("Stacktrace", R.string.dev_report_stacktrace, + stacktrace); + } + private ReportItem getTimeInfo(long startTime) { MultiReportInfo timeInfo = new MultiReportInfo() - .add("App start time", formatTime(startTime)) - .add("Crash time", formatTime(System.currentTimeMillis())); + .add("ReportTime", formatTime(System.currentTimeMillis())); + if (startTime > -1) { + timeInfo.add("AppStartTime", formatTime(startTime)); + } return new ReportItem("DeviceInfo", R.string.dev_report_time_info, timeInfo); } @@ -132,53 +145,59 @@ class BriarReportCollector { } private ReportItem getMemory() { + MultiReportInfo memInfo = new MultiReportInfo(); + // System memory ActivityManager am = getSystemService(ctx, ActivityManager.class); ActivityManager.MemoryInfo mem = new ActivityManager.MemoryInfo(); requireNonNull(am).getMemoryInfo(mem); - String systemMemory; - systemMemory = (mem.totalMem / 1024 / 1024) + " MiB total, " - + (mem.availMem / 1024 / 1204) + " MiB free, " - + (mem.threshold / 1024 / 1024) + " MiB threshold"; + String systemTotal = (mem.totalMem / 1024 / 1024) + " MiB"; + String systemFree = (mem.availMem / 1024 / 1024) + " MiB"; + String systemThreshold = (mem.threshold / 1024 / 1024) + " MiB"; + memInfo.add("SystemMemoryTotal", systemTotal); + memInfo.add("SystemMemoryFree", systemFree); + memInfo.add("SystemMemoryThreshold", systemThreshold); // Virtual machine memory Runtime runtime = Runtime.getRuntime(); long heap = runtime.totalMemory(); long heapFree = runtime.freeMemory(); long heapMax = runtime.maxMemory(); - String vmMemory = (heap / 1024 / 1024) + " MiB allocated, " - + (heapFree / 1024 / 1024) + " MiB free, " - + (heapMax / 1024 / 1024) + " MiB maximum"; + String vmMemoryAllocated = (heap / 1024 / 1024) + " MiB"; + String vmMemoryFree = (heapFree / 1024 / 1024) + " MiB"; + String vmMemoryMax = (heapMax / 1024 / 1024) + " MiB"; + memInfo.add("VirtualMachineMemoryAllocated", vmMemoryAllocated); + memInfo.add("VirtualMachineMemoryFree", vmMemoryFree); + memInfo.add("VirtualMachineMemoryMaximum", vmMemoryMax); - MultiReportInfo memInfo = new MultiReportInfo() - .add("System memory", systemMemory) - .add("Virtual machine memory", vmMemory); return new ReportItem("Memory", R.string.dev_report_memory, memInfo); } private ReportItem getStorage() { + MultiReportInfo storageInfo = new MultiReportInfo(); + // Internal storage File root = Environment.getRootDirectory(); long rootTotal = root.getTotalSpace(); long rootFree = root.getFreeSpace(); - String internal = (rootTotal / 1024 / 1024) + " MiB total, " - + (rootFree / 1024 / 1024) + " MiB free"; + storageInfo.add("InternalStorageTotal", + (rootTotal / 1024 / 1024) + " MiB"); + storageInfo.add("InternalStorageFree", + (rootFree / 1024 / 1024) + " MiB"); // External storage (SD card) File sd = Environment.getExternalStorageDirectory(); long sdTotal = sd.getTotalSpace(); long sdFree = sd.getFreeSpace(); - String external = (sdTotal / 1024 / 1024) + " MiB total, " - + (sdFree / 1024 / 1024) + " MiB free"; + storageInfo.add("ExternalStorageTotal", + (sdTotal / 1024 / 1024) + " MiB"); + storageInfo.add("ExternalStorageFree", + (sdFree / 1024 / 1024) + " MiB"); - MultiReportInfo storageInfo = new MultiReportInfo() - .add("Internal storage", internal) - .add("External storage", external); return new ReportItem("Storage", R.string.dev_report_storage, storageInfo); } - private ReportItem getConnectivity() { MultiReportInfo connectivityInfo = new MultiReportInfo(); @@ -193,7 +212,6 @@ class BriarReportCollector { Class clazz = Class.forName(cm.getClass().getName()); Method method = clazz.getDeclaredMethod("getMobileDataEnabled"); method.setAccessible(true); - //noinspection ConstantConditions mobileEnabled = (Boolean) method.invoke(cm); } catch (ClassNotFoundException | NoSuchMethodException @@ -201,7 +219,7 @@ class BriarReportCollector { | InvocationTargetException | IllegalAccessException e) { connectivityInfo - .add("Mobile data reflection exception", e.toString()); + .add("MobileDataReflectionException", e.toString()); } // Is mobile data connected ? boolean mobileConnected = mobile != null && mobile.isConnected(); @@ -213,7 +231,7 @@ class BriarReportCollector { else mobileStatus += "not enabled, "; if (mobileConnected) mobileStatus += "connected"; else mobileStatus += "not connected"; - connectivityInfo.add("Mobile data status", mobileStatus); + connectivityInfo.add("MobileDataStatus", mobileStatus); // Is wifi available? NetworkInfo wifi = cm.getNetworkInfo(TYPE_WIFI); @@ -232,13 +250,13 @@ class BriarReportCollector { else wifiStatus += "not enabled, "; if (wifiConnected) wifiStatus += "connected"; else wifiStatus += "not connected"; - connectivityInfo.add("Wi-Fi status", wifiStatus); + connectivityInfo.add("WiFiStatus", wifiStatus); // Is wifi direct supported? String wifiDirectStatus = "Supported"; if (ctx.getSystemService(WIFI_P2P_SERVICE) == null) - wifiDirectStatus = "Not supported"; - connectivityInfo.add("Wi-Fi Direct", wifiDirectStatus); + wifiDirectStatus = "NotSupported"; + connectivityInfo.add("WiFiDirect", wifiDirectStatus); if (wm != null) { WifiInfo wifiInfo = wm.getConnectionInfo(); @@ -252,7 +270,7 @@ class BriarReportCollector { try { InetAddress address = InetAddress.getByAddress(ipBytes); connectivityInfo - .add("Wi-Fi address", scrubInetAddress(address)); + .add("Wi-FiAddress", scrubInetAddress(address)); } catch (UnknownHostException ignored) { // Should only be thrown if address has illegal length } @@ -262,7 +280,7 @@ class BriarReportCollector { // Is Bluetooth available? BluetoothAdapter bt = BluetoothAdapter.getDefaultAdapter(); if (bt == null) { - connectivityInfo.add("Bluetooth status", "Not available"); + connectivityInfo.add("BluetoothStatus", "Not available"); } else { // Is Bluetooth enabled? @SuppressLint("HardwareIds") @@ -283,7 +301,7 @@ class BriarReportCollector { else btStatus += "not connectable, "; if (btDiscoverable) btStatus += "discoverable"; else btStatus += "not discoverable"; - connectivityInfo.add("Bluetooth status", btStatus); + connectivityInfo.add("BluetoothStatus", btStatus); if (SDK_INT >= 21) { // Is Bluetooth LE scanning and advertising supported? @@ -295,14 +313,14 @@ class BriarReportCollector { else btLeStatus = "No scanning, "; if (btLeAdvertise) btLeStatus += "advertising"; else btLeStatus += "no advertising"; - connectivityInfo.add("Bluetooth LE status", btLeStatus); + connectivityInfo.add("BluetoothLeStatus", btLeStatus); } Pair p = getBluetoothAddressAndMethod(ctx, bt); String address = p.getFirst(); String method = p.getSecond(); - connectivityInfo.add("Bluetooth address", scrubMacAddress(address)); - connectivityInfo.add("Bluetooth address method", method); + connectivityInfo.add("BluetoothAddress", scrubMacAddress(address)); + connectivityInfo.add("BluetoothAddressMethod", method); } return new ReportItem("Connectivity", R.string.dev_report_connectivity, connectivityInfo); diff --git a/briar-android/src/main/java/org/briarproject/briar/android/reporting/CrashFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/reporting/CrashFragment.java index 00ab9961d..e1d42e118 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/reporting/CrashFragment.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/reporting/CrashFragment.java @@ -8,34 +8,34 @@ import android.view.ViewGroup; import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; import org.briarproject.briar.R; -import org.briarproject.briar.android.AndroidComponent; -import org.briarproject.briar.android.BriarApplication; +import org.briarproject.briar.android.activity.ActivityComponent; +import org.briarproject.briar.android.fragment.BaseFragment; import javax.inject.Inject; import androidx.annotation.Nullable; -import androidx.fragment.app.Fragment; -import androidx.fragment.app.FragmentActivity; import androidx.lifecycle.ViewModelProvider; @MethodsNotNullByDefault @ParametersNotNullByDefault -public class CrashFragment extends Fragment { +public class CrashFragment extends BaseFragment { + + public final static String TAG = CrashFragment.class.getName(); @Inject ViewModelProvider.Factory viewModelFactory; + @Override + public void injectFragment(ActivityComponent component) { + component.inject(this); + } + private ReportViewModel viewModel; @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); - FragmentActivity a = requireActivity(); - BriarApplication app = - (BriarApplication) a.getApplicationContext(); - AndroidComponent androidComponent = app.getApplicationComponent(); - androidComponent.inject(this); - viewModel = new ViewModelProvider(a, viewModelFactory) + viewModel = new ViewModelProvider(requireActivity(), viewModelFactory) .get(ReportViewModel.class); } @@ -55,4 +55,9 @@ public class CrashFragment extends Fragment { return v; } + @Override + public String getUniqueTag() { + return TAG; + } + } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/reporting/CrashReportActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/reporting/CrashReportActivity.java index bd17ea3ab..43c7d40e6 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/reporting/CrashReportActivity.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/reporting/CrashReportActivity.java @@ -1,62 +1,60 @@ package org.briarproject.briar.android.reporting; -import android.content.Context; import android.content.Intent; import android.os.Bundle; +import android.os.Process; import android.widget.Toast; import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; import org.briarproject.briar.R; -import org.briarproject.briar.android.AndroidComponent; -import org.briarproject.briar.android.BriarApplication; -import org.briarproject.briar.android.Localizer; +import org.briarproject.briar.android.activity.ActivityComponent; +import org.briarproject.briar.android.activity.BaseActivity; +import org.briarproject.briar.android.fragment.BaseFragment; +import org.briarproject.briar.android.fragment.BaseFragment.BaseFragmentListener; import org.briarproject.briar.android.logout.HideUiActivity; import javax.inject.Inject; import androidx.annotation.Nullable; -import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.Toolbar; -import androidx.fragment.app.Fragment; import androidx.lifecycle.ViewModelProvider; import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK; import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; import static android.content.Intent.FLAG_ACTIVITY_NO_ANIMATION; -import static android.view.WindowManager.LayoutParams.FLAG_SECURE; import static android.widget.Toast.LENGTH_LONG; import static java.util.Objects.requireNonNull; -import static org.briarproject.briar.android.TestingConstants.PREVENT_SCREENSHOTS; @MethodsNotNullByDefault @ParametersNotNullByDefault -public class CrashReportActivity extends AppCompatActivity { +public class CrashReportActivity extends BaseActivity + implements BaseFragmentListener { - static final String EXTRA_THROWABLE = "throwable"; - static final String EXTRA_APP_START_TIME = "appStartTime"; + public static final String EXTRA_THROWABLE = "throwable"; + public static final String EXTRA_APP_START_TIME = "appStartTime"; @Inject ViewModelProvider.Factory viewModelFactory; private ReportViewModel viewModel; + @Override + public void injectActivity(ActivityComponent component) { + component.inject(this); + } + @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); - if (PREVENT_SCREENSHOTS) getWindow().addFlags(FLAG_SECURE); setContentView(R.layout.activity_dev_report); - AndroidComponent androidComponent = - ((BriarApplication) getApplication()).getApplicationComponent(); - androidComponent.inject(this); - viewModel = new ViewModelProvider(this, viewModelFactory) .get(ReportViewModel.class); Intent intent = getIntent(); Throwable t = (Throwable) intent.getSerializableExtra(EXTRA_THROWABLE); - long appStartTime = intent.getLongExtra(EXTRA_APP_START_TIME, 0); - viewModel.init(requireNonNull(t), appStartTime); + long appStartTime = intent.getLongExtra(EXTRA_APP_START_TIME, -1); + viewModel.init(t, appStartTime); viewModel.getShowReport().observeEvent(this, show -> { if (show) displayFragment(true); }); @@ -74,8 +72,8 @@ public class CrashReportActivity extends AppCompatActivity { } @Override - protected void attachBaseContext(Context base) { - super.attachBaseContext(Localizer.getInstance().setLocale(base)); + public void runOnDbThread(Runnable runnable) { + throw new AssertionError("deprecated!!!"); } @Override @@ -84,7 +82,7 @@ public class CrashReportActivity extends AppCompatActivity { } void displayFragment(boolean showReportForm) { - Fragment f; + BaseFragment f; if (showReportForm) { f = new ReportFormFragment(); requireNonNull(getSupportActionBar()).show(); @@ -93,7 +91,7 @@ public class CrashReportActivity extends AppCompatActivity { requireNonNull(getSupportActionBar()).hide(); } getSupportFragmentManager().beginTransaction() - .replace(R.id.fragmentContainer, f, f.getTag()) + .replace(R.id.fragmentContainer, f, f.getUniqueTag()) .commit(); } @@ -104,6 +102,10 @@ public class CrashReportActivity extends AppCompatActivity { | FLAG_ACTIVITY_NO_ANIMATION | FLAG_ACTIVITY_CLEAR_TASK); startActivity(i); + // crash reports run in their own process that we should kill now + // otherwise it keeps running and e.g. doesn't pick up theme changes + Process.killProcess(Process.myPid()); + System.exit(10); } finish(); } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/reporting/ReportData.java b/briar-android/src/main/java/org/briarproject/briar/android/reporting/ReportData.java index 507ca57f1..374e7f486 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/reporting/ReportData.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/reporting/ReportData.java @@ -10,11 +10,13 @@ import java.util.Map; import java.util.TreeMap; import javax.annotation.concurrent.Immutable; +import javax.annotation.concurrent.NotThreadSafe; -import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.StringRes; +@NotThreadSafe +@NotNullByDefault class ReportData { private final ArrayList items = new ArrayList<>(); @@ -28,7 +30,6 @@ class ReportData { return items; } - @NonNull public JSONObject toJson(boolean includeReport) throws JSONException { JSONObject json = new JSONObject(); for (ReportItem item : items) { @@ -48,7 +49,7 @@ class ReportData { final int nameRes; final ReportInfo info; final boolean isOptional; - volatile boolean isIncluded = true; + boolean isIncluded = true; ReportItem(String name, int nameRes, ReportInfo info) { this(name, nameRes, info, true); @@ -91,7 +92,6 @@ class ReportData { } } - @Immutable @NotNullByDefault static class MultiReportInfo implements ReportInfo { private final Map map = new TreeMap<>(); diff --git a/briar-android/src/main/java/org/briarproject/briar/android/reporting/ReportFormFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/reporting/ReportFormFragment.java index dee217a99..d8daa024c 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/reporting/ReportFormFragment.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/reporting/ReportFormFragment.java @@ -15,14 +15,12 @@ import android.widget.Toast; import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; import org.briarproject.briar.R; -import org.briarproject.briar.android.AndroidComponent; -import org.briarproject.briar.android.BriarApplication; +import org.briarproject.briar.android.activity.ActivityComponent; +import org.briarproject.briar.android.fragment.BaseFragment; import javax.inject.Inject; import androidx.annotation.Nullable; -import androidx.fragment.app.Fragment; -import androidx.fragment.app.FragmentActivity; import androidx.lifecycle.ViewModelProvider; import androidx.recyclerview.widget.RecyclerView; @@ -34,7 +32,9 @@ import static java.util.Objects.requireNonNull; @MethodsNotNullByDefault @ParametersNotNullByDefault -public class ReportFormFragment extends Fragment { +public class ReportFormFragment extends BaseFragment { + + public final static String TAG = ReportFormFragment.class.getName(); @Inject ViewModelProvider.Factory viewModelFactory; @@ -50,15 +50,16 @@ public class ReportFormFragment extends Fragment { @Nullable private MenuItem sendReport; + @Override + public void injectFragment(ActivityComponent component) { + component.inject(this); + } + @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setHasOptionsMenu(true); - FragmentActivity a = requireActivity(); - BriarApplication app = (BriarApplication) a.getApplicationContext(); - AndroidComponent androidComponent = app.getApplicationComponent(); - androidComponent.inject(this); - viewModel = new ViewModelProvider(a, viewModelFactory) + viewModel = new ViewModelProvider(requireActivity(), viewModelFactory) .get(ReportViewModel.class); } @@ -131,6 +132,11 @@ public class ReportFormFragment extends Fragment { return super.onOptionsItemSelected(item); } + @Override + public String getUniqueTag() { + return TAG; + } + private void sendReport() { userCommentView.setEnabled(false); userEmailView.setEnabled(false); diff --git a/briar-android/src/main/java/org/briarproject/briar/android/reporting/ReportViewModel.java b/briar-android/src/main/java/org/briarproject/briar/android/reporting/ReportViewModel.java index 719d4ae4e..d0a8015c3 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/reporting/ReportViewModel.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/reporting/ReportViewModel.java @@ -4,8 +4,7 @@ import android.app.Application; import android.os.Handler; import android.os.Looper; -import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; -import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; +import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.plugin.Plugin; import org.briarproject.bramble.api.plugin.PluginManager; import org.briarproject.bramble.api.plugin.TorConstants; @@ -13,7 +12,6 @@ import org.briarproject.bramble.api.reporting.DevReporter; import org.briarproject.bramble.util.AndroidUtils; import org.briarproject.briar.R; import org.briarproject.briar.android.reporting.ReportData.MultiReportInfo; -import org.briarproject.briar.android.util.UserFeedback; import org.briarproject.briar.android.viewmodel.LiveEvent; import org.briarproject.briar.android.viewmodel.MutableLiveEvent; import org.json.JSONException; @@ -26,6 +24,7 @@ import java.util.logging.Logger; import javax.inject.Inject; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.annotation.UiThread; import androidx.lifecycle.AndroidViewModel; import androidx.lifecycle.LiveData; @@ -38,8 +37,7 @@ import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE; import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty; -@MethodsNotNullByDefault -@ParametersNotNullByDefault +@NotNullByDefault public class ReportViewModel extends AndroidViewModel { private static final Logger LOG = @@ -68,11 +66,11 @@ public class ReportViewModel extends AndroidViewModel { this.pluginManager = pluginManager; } - void init(Throwable throwable, long appStartTime) { - isFeedback = throwable instanceof UserFeedback; + void init(@Nullable Throwable t, long appStartTime) { + isFeedback = t == null; if (reportData.getValue() == null) new SingleShotAndroidExecutor(() -> { - Throwable t = isFeedback ? null : throwable; - reportData.postValue(collector.collectReportData(t, appStartTime)); + ReportData data = collector.collectReportData(t, appStartTime); + reportData.postValue(data); }).start(); } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsFragment.java index 1dd20ca53..7412b213f 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsFragment.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsFragment.java @@ -30,7 +30,6 @@ import org.briarproject.bramble.api.system.LocationUtils; import org.briarproject.bramble.plugin.tor.CircumventionProvider; import org.briarproject.bramble.util.StringUtils; import org.briarproject.briar.R; -import org.briarproject.briar.android.BriarApplication; import org.briarproject.briar.android.Localizer; import org.briarproject.briar.android.util.UiUtils; @@ -93,6 +92,7 @@ import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_RINGT import static org.briarproject.briar.android.navdrawer.NavDrawerActivity.SIGN_OUT_URI; import static org.briarproject.briar.android.util.UiUtils.getCountryDisplayName; import static org.briarproject.briar.android.util.UiUtils.hasScreenLock; +import static org.briarproject.briar.android.util.UiUtils.triggerFeedback; import static org.briarproject.briar.api.android.AndroidNotificationManager.BLOG_CHANNEL_ID; import static org.briarproject.briar.api.android.AndroidNotificationManager.CONTACT_CHANNEL_ID; import static org.briarproject.briar.api.android.AndroidNotificationManager.FORUM_CHANNEL_ID; @@ -226,9 +226,7 @@ public class SettingsFragment extends PreferenceFragmentCompat Preference prefFeedback = requireNonNull(findPreference("pref_key_send_feedback")); prefFeedback.setOnPreferenceClickListener(preference -> { - BriarApplication app = - (BriarApplication) requireContext().getApplicationContext(); - app.triggerFeedback(); + triggerFeedback(requireContext()); return true; }); diff --git a/briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java b/briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java index d71cdd865..aed5402e2 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java @@ -33,6 +33,7 @@ import org.briarproject.bramble.api.identity.Author; import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; import org.briarproject.briar.R; +import org.briarproject.briar.android.reporting.FeedbackActivity; import org.briarproject.briar.android.view.ArticleMovementMethod; import org.briarproject.briar.android.widget.LinkDialogFragment; @@ -49,6 +50,7 @@ import androidx.appcompat.app.AlertDialog; import androidx.core.content.ContextCompat; import androidx.core.hardware.fingerprint.FingerprintManagerCompat; import androidx.core.text.HtmlCompat; +import androidx.fragment.app.FragmentActivity; import androidx.fragment.app.FragmentManager; import androidx.lifecycle.LifecycleOwner; import androidx.lifecycle.LiveData; @@ -90,6 +92,8 @@ import static java.util.Objects.requireNonNull; import static java.util.concurrent.TimeUnit.DAYS; import static org.briarproject.briar.BuildConfig.APPLICATION_ID; import static org.briarproject.briar.android.TestingConstants.EXPIRY_DATE; +import static org.briarproject.briar.android.reporting.CrashReportActivity.EXTRA_APP_START_TIME; +import static org.briarproject.briar.android.reporting.CrashReportActivity.EXTRA_THROWABLE; @MethodsNotNullByDefault @ParametersNotNullByDefault @@ -343,6 +347,20 @@ public class UiUtils { return fm.hasEnrolledFingerprints() && fm.isHardwareDetected(); } + public static void triggerFeedback(Context ctx) { + startDevReportActivity(ctx, FeedbackActivity.class, null, null); + } + + public static void startDevReportActivity(Context ctx, + Class activity, @Nullable Throwable t, + @Nullable Long appStartTime) { + final Intent dialogIntent = new Intent(ctx, activity); + dialogIntent.setFlags(FLAG_ACTIVITY_NEW_TASK); + dialogIntent.putExtra(EXTRA_THROWABLE, t); + dialogIntent.putExtra(EXTRA_APP_START_TIME, appStartTime); + ctx.startActivity(dialogIntent); + } + public static boolean enterPressed(int actionId, @Nullable KeyEvent keyEvent) { return actionId == IME_NULL && diff --git a/briar-android/src/main/java/org/briarproject/briar/android/util/UserFeedback.java b/briar-android/src/main/java/org/briarproject/briar/android/util/UserFeedback.java deleted file mode 100644 index c02134a75..000000000 --- a/briar-android/src/main/java/org/briarproject/briar/android/util/UserFeedback.java +++ /dev/null @@ -1,5 +0,0 @@ -package org.briarproject.briar.android.util; - -public class UserFeedback extends Exception { - -} diff --git a/briar-android/src/main/res/values/strings.xml b/briar-android/src/main/res/values/strings.xml index 0d9f7f14e..6798ff921 100644 --- a/briar-android/src/main/res/values/strings.xml +++ b/briar-android/src/main/res/values/strings.xml @@ -570,6 +570,7 @@ Could not load report data. Basic information Device information + Stacktrace Time information Memory Storage @@ -579,7 +580,7 @@ Device Features Send report Close - Trying to send feedback now… + Sending feedback… Feedback sent. Report saved. It will be sent the next time you log into Briar. Error: Sending report failed. diff --git a/briar-android/src/test/java/org/briarproject/briar/android/TestBriarApplication.java b/briar-android/src/test/java/org/briarproject/briar/android/TestBriarApplication.java deleted file mode 100644 index e8288eb9e..000000000 --- a/briar-android/src/test/java/org/briarproject/briar/android/TestBriarApplication.java +++ /dev/null @@ -1,85 +0,0 @@ -package org.briarproject.briar.android; - -import android.app.Application; -import android.content.SharedPreferences; -import android.preference.PreferenceManager; - -import com.vanniktech.emoji.EmojiManager; -import com.vanniktech.emoji.google.GoogleEmojiProvider; - -import org.briarproject.bramble.BrambleAndroidEagerSingletons; -import org.briarproject.bramble.BrambleAppComponent; -import org.briarproject.bramble.BrambleCoreEagerSingletons; -import org.briarproject.briar.BriarCoreEagerSingletons; - -import java.util.Collection; -import java.util.logging.LogRecord; -import java.util.logging.Logger; - -import static java.util.Collections.emptyList; - -/** - * This class only exists to avoid static initialisation of ACRA - */ -public class TestBriarApplication extends Application - implements BriarApplication { - - private static final Logger LOG = - Logger.getLogger(TestBriarApplication.class.getName()); - - private AndroidComponent applicationComponent; - private volatile SharedPreferences prefs; - - @Override - public void onCreate() { - super.onCreate(); - LOG.info("Created"); - - prefs = PreferenceManager.getDefaultSharedPreferences(this); - Localizer.initialize(prefs); - applicationComponent = DaggerAndroidComponent.builder() - .appModule(new AppModule(this)) - .build(); - - // We need to load the eager singletons directly after making the - // dependency graphs - BrambleCoreEagerSingletons.Helper - .injectEagerSingletons(applicationComponent); - BrambleAndroidEagerSingletons.Helper - .injectEagerSingletons(applicationComponent); - BriarCoreEagerSingletons.Helper - .injectEagerSingletons(applicationComponent); - AndroidEagerSingletons.Helper - .injectEagerSingletons(applicationComponent); - EmojiManager.install(new GoogleEmojiProvider()); - } - - @Override - public BrambleAppComponent getBrambleAppComponent() { - return applicationComponent; - } - - @Override - public Collection getRecentLogRecords() { - return emptyList(); - } - - @Override - public AndroidComponent getApplicationComponent() { - return applicationComponent; - } - - @Override - public SharedPreferences getDefaultSharedPreferences() { - return prefs; - } - - @Override - public void triggerFeedback() { - } - - @Override - public boolean isRunningInBackground() { - return false; - } -} diff --git a/briar-android/src/test/java/org/briarproject/briar/android/account/SetupActivityTest.java b/briar-android/src/test/java/org/briarproject/briar/android/account/SetupActivityTest.java index 9edfd6f3d..59cb210ce 100644 --- a/briar-android/src/test/java/org/briarproject/briar/android/account/SetupActivityTest.java +++ b/briar-android/src/test/java/org/briarproject/briar/android/account/SetupActivityTest.java @@ -3,7 +3,6 @@ package org.briarproject.briar.android.account; import android.view.View; import org.briarproject.briar.R; -import org.briarproject.briar.android.TestBriarApplication; import org.briarproject.briar.android.login.StrengthMeter; import org.hamcrest.Description; import org.hamcrest.Matcher; @@ -42,7 +41,7 @@ import static org.briarproject.briar.android.login.StrengthMeter.YELLOW; import static org.hamcrest.Matchers.not; @RunWith(AndroidJUnit4.class) -@Config(sdk = 21, application = TestBriarApplication.class) +@Config(sdk = 21) public class SetupActivityTest { @Rule diff --git a/briar-android/src/test/java/org/briarproject/briar/android/forum/ForumActivityTest.java b/briar-android/src/test/java/org/briarproject/briar/android/forum/ForumActivityTest.java index 7b542fc9e..5a8892f9b 100644 --- a/briar-android/src/test/java/org/briarproject/briar/android/forum/ForumActivityTest.java +++ b/briar-android/src/test/java/org/briarproject/briar/android/forum/ForumActivityTest.java @@ -8,7 +8,6 @@ import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.identity.Author; import org.briarproject.bramble.api.identity.AuthorInfo; import org.briarproject.bramble.api.sync.MessageId; -import org.briarproject.briar.android.TestBriarApplication; import org.briarproject.briar.android.controller.handler.UiResultExceptionHandler; import org.briarproject.briar.android.threaded.ThreadItemAdapter; import org.briarproject.briar.android.threaded.ThreadItemList; @@ -36,7 +35,7 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @RunWith(RobolectricTestRunner.class) -@Config(sdk = 21, application = TestBriarApplication.class) +@Config(sdk = 21) public class ForumActivityTest { private final static MessageId[] MESSAGE_IDS = new MessageId[6]; diff --git a/briar-android/src/test/java/org/briarproject/briar/android/login/ChangePasswordActivityTest.java b/briar-android/src/test/java/org/briarproject/briar/android/login/ChangePasswordActivityTest.java index 444fe0a3d..aec8a4558 100644 --- a/briar-android/src/test/java/org/briarproject/briar/android/login/ChangePasswordActivityTest.java +++ b/briar-android/src/test/java/org/briarproject/briar/android/login/ChangePasswordActivityTest.java @@ -7,7 +7,6 @@ import com.google.android.material.textfield.TextInputLayout; import org.briarproject.bramble.api.crypto.DecryptionResult; import org.briarproject.briar.R; -import org.briarproject.briar.android.TestBriarApplication; import org.briarproject.briar.android.viewmodel.MutableLiveEvent; import org.junit.Assert; import org.junit.Before; @@ -36,7 +35,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @RunWith(RobolectricTestRunner.class) -@Config(sdk = 21, application = TestBriarApplication.class) +@Config(sdk = 21) public class ChangePasswordActivityTest { private ChangePasswordActivity changePasswordActivity;