diff --git a/app/src/main/java/org/isoron/uhabits/activities/ActivityComponent.java b/app/src/main/java/org/isoron/uhabits/activities/ActivityComponent.java
index e80a9f540..918a70b4b 100644
--- a/app/src/main/java/org/isoron/uhabits/activities/ActivityComponent.java
+++ b/app/src/main/java/org/isoron/uhabits/activities/ActivityComponent.java
@@ -29,6 +29,8 @@ import dagger.*;
dependencies = { AppComponent.class })
public interface ActivityComponent
{
+ BaseActivity getActivity();
+
ColorPickerDialogFactory getColorPickerDialogFactory();
ThemeSwitcher getThemeSwitcher();
diff --git a/app/src/main/java/org/isoron/uhabits/activities/BaseActivity.java b/app/src/main/java/org/isoron/uhabits/activities/BaseActivity.java
index 311749159..2167311cb 100644
--- a/app/src/main/java/org/isoron/uhabits/activities/BaseActivity.java
+++ b/app/src/main/java/org/isoron/uhabits/activities/BaseActivity.java
@@ -26,11 +26,9 @@ import android.support.v7.app.*;
import android.view.*;
import org.isoron.uhabits.*;
-import org.isoron.uhabits.activities.habits.list.*;
-import org.isoron.uhabits.models.*;
-import org.isoron.uhabits.models.sqlite.*;
-import static android.R.anim.*;
+import static android.R.anim.fade_in;
+import static android.R.anim.fade_out;
/**
* Base class for all activities in the application.
@@ -41,18 +39,16 @@ import static android.R.anim.*;
* {@link BaseScreen}.
*
* A BaseActivity also installs an {@link java.lang.Thread.UncaughtExceptionHandler}
- * to the main thread that logs the exception to the disk before the application
- * crashes.
+ * to the main thread. By default, this handler is an instance of
+ * BaseExceptionHandler, which logs the exception to the disk before the application
+ * crashes. To the default handler, you should override the method
+ * getExceptionHandler.
*/
abstract public class BaseActivity extends AppCompatActivity
- implements Thread.UncaughtExceptionHandler
{
@Nullable
private BaseMenu baseMenu;
- @Nullable
- private Thread.UncaughtExceptionHandler androidExceptionHandler;
-
@Nullable
private BaseScreen screen;
@@ -80,13 +76,13 @@ abstract public class BaseActivity extends AppCompatActivity
return baseMenu.onItemSelected(item);
}
- public void restartWithFade()
+ public void restartWithFade(Class> cls)
{
- new Handler().postDelayed(() -> {
- Intent intent = new Intent(this, ListHabitsActivity.class);
+ new Handler().postDelayed(() ->
+ {
finish();
overridePendingTransition(fade_in, fade_out);
- startActivity(intent);
+ startActivity(new Intent(this, cls));
}, 500); // HACK: Let the menu disappear first
}
@@ -111,35 +107,6 @@ abstract public class BaseActivity extends AppCompatActivity
dialog.show();
}
- @Override
- public void uncaughtException(@Nullable Thread thread,
- @Nullable Throwable ex)
- {
- if (ex == null) return;
-
- try
- {
- ex.printStackTrace();
- new BaseSystem(this).dumpBugReportToFile();
- }
- catch (Exception e)
- {
- // ignored
- }
-
- if (ex.getCause() instanceof InconsistentDatabaseException)
- {
- HabitsApplication app = (HabitsApplication) getApplication();
- HabitList habits = app.getComponent().getHabitList();
- habits.repair();
- System.exit(0);
- }
-
- if (androidExceptionHandler != null)
- androidExceptionHandler.uncaughtException(thread, ex);
- else System.exit(1);
- }
-
@Override
protected void onActivityResult(int request, int result, Intent data)
{
@@ -151,9 +118,7 @@ abstract public class BaseActivity extends AppCompatActivity
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
-
- androidExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
- Thread.setDefaultUncaughtExceptionHandler(this);
+ Thread.setDefaultUncaughtExceptionHandler(getExceptionHandler());
HabitsApplication app = (HabitsApplication) getApplicationContext();
@@ -165,4 +130,9 @@ abstract public class BaseActivity extends AppCompatActivity
component.getThemeSwitcher().apply();
}
+
+ protected Thread.UncaughtExceptionHandler getExceptionHandler()
+ {
+ return new BaseExceptionHandler(this);
+ }
}
diff --git a/app/src/main/java/org/isoron/uhabits/activities/BaseExceptionHandler.java b/app/src/main/java/org/isoron/uhabits/activities/BaseExceptionHandler.java
new file mode 100644
index 000000000..5ea4638d9
--- /dev/null
+++ b/app/src/main/java/org/isoron/uhabits/activities/BaseExceptionHandler.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2017 Álinson Santos Xavier
+ *
+ * This file is part of Loop Habit Tracker.
+ *
+ * Loop Habit Tracker is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * Loop Habit Tracker is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see .
+ */
+
+package org.isoron.uhabits.activities;
+
+import android.support.annotation.*;
+
+import org.isoron.uhabits.*;
+import org.isoron.uhabits.models.*;
+import org.isoron.uhabits.models.sqlite.*;
+
+public class BaseExceptionHandler implements Thread.UncaughtExceptionHandler
+{
+ @Nullable
+ private Thread.UncaughtExceptionHandler originalHandler;
+
+ @NonNull
+ private BaseActivity activity;
+
+ public BaseExceptionHandler(@NonNull BaseActivity activity)
+ {
+ this.activity = activity;
+ originalHandler = Thread.getDefaultUncaughtExceptionHandler();
+ }
+
+ @Override
+ public void uncaughtException(@Nullable Thread thread,
+ @Nullable Throwable ex)
+ {
+ if (ex == null) return;
+
+ try
+ {
+ ex.printStackTrace();
+ new BaseSystem(activity).dumpBugReportToFile();
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+
+ if (ex.getCause() instanceof InconsistentDatabaseException)
+ {
+ HabitsApplication app = (HabitsApplication) activity.getApplication();
+ HabitList habits = app.getComponent().getHabitList();
+ habits.repair();
+ System.exit(0);
+ }
+
+ if (originalHandler != null)
+ originalHandler.uncaughtException(thread, ex);
+ }
+}
diff --git a/app/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsActivity.java b/app/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsActivity.java
index 1c68b49ff..ff8e50d8a 100644
--- a/app/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsActivity.java
+++ b/app/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsActivity.java
@@ -111,7 +111,7 @@ public class ListHabitsActivity extends BaseActivity
if (prefs.getTheme() == ThemeSwitcher.THEME_DARK &&
prefs.isPureBlackEnabled() != pureBlack)
{
- restartWithFade();
+ restartWithFade(ListHabitsActivity.class);
}
super.onResume();
diff --git a/app/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsScreen.java b/app/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsScreen.java
index aeda45bcf..1a778101c 100644
--- a/app/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsScreen.java
+++ b/app/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsScreen.java
@@ -353,7 +353,7 @@ public class ListHabitsScreen extends BaseScreen
public void toggleNightMode()
{
themeSwitcher.toggleNightMode();
- activity.restartWithFade();
+ activity.restartWithFade(ListHabitsActivity.class);
}
private void onOpenDocumentResult(int resultCode, Intent data)
diff --git a/app/src/test/java/org/isoron/uhabits/activities/BaseActivityTest.java b/app/src/test/java/org/isoron/uhabits/activities/BaseActivityTest.java
index e034422b1..ec7835b8c 100644
--- a/app/src/test/java/org/isoron/uhabits/activities/BaseActivityTest.java
+++ b/app/src/test/java/org/isoron/uhabits/activities/BaseActivityTest.java
@@ -19,30 +19,116 @@
package org.isoron.uhabits.activities;
+import android.content.*;
import android.os.*;
+import android.support.v4.app.*;
+import android.support.v7.app.*;
import android.support.v7.widget.Toolbar;
import android.view.*;
import android.widget.*;
import org.isoron.uhabits.*;
+import org.isoron.uhabits.activities.common.dialogs.*;
import org.junit.*;
import org.junit.runner.*;
import org.robolectric.*;
import org.robolectric.annotation.*;
+import org.robolectric.shadows.*;
+import static org.hamcrest.core.IsEqual.*;
+import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
import static org.robolectric.Robolectric.*;
+import static org.robolectric.Shadows.*;
@RunWith(RobolectricTestRunner.class)
@Config(constants = BuildConfig.class)
public class BaseActivityTest
{
+ private static boolean hasCrashed = false;
+
+ @Test
+ public void activityResultTest()
+ {
+ ScreenActivity activity = spy(setupActivity(ScreenActivity.class));
+ activity.onActivityResult(0, 0, null);
+ verify(activity.screen).onResult(0, 0, null);
+ }
+
+ @Test
+ public void componentTest()
+ {
+ EmptyActivity activity = setupActivity(EmptyActivity.class);
+ ActivityComponent component = activity.getComponent();
+ assertThat(component.getActivity(), equalTo(activity));
+ }
+
+ @Test
+ public void dialogFragmentTest()
+ {
+ EmptyActivity activity = setupActivity(EmptyActivity.class);
+ FragmentManager manager = activity.getSupportFragmentManager();
+ ColorPickerDialog d = new ColorPickerDialogFactory(activity).create(0);
+
+ activity.showDialog(d, "picker");
+ assertTrue(d.getDialog().isShowing());
+ assertThat(d, equalTo(manager.findFragmentByTag("picker")));
+ }
+
+ @Test
+ public void dialogTest()
+ {
+ EmptyActivity activity = setupActivity(EmptyActivity.class);
+ AlertDialog dialog =
+ new AlertDialog.Builder(activity).setTitle("Hello world").create();
+
+ activity.showDialog(dialog);
+ assertTrue(dialog.isShowing());
+ }
+
+ @Test
+ public void restartTest() throws Exception
+ {
+ EmptyActivity activity = setupActivity(EmptyActivity.class);
+
+ activity.restartWithFade(EmptyActivity.class);
+ ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
+
+ Intent nextStartedActivity = shadowOf(activity).getNextStartedActivity();
+ assertNotNull(nextStartedActivity);
+
+ assertTrue(activity.isFinishing());
+ assertThat(shadowOf(nextStartedActivity).getIntentClass(),
+ equalTo(EmptyActivity.class));
+ }
+
+ @Test
+ public void exceptionHandlerTest() throws InterruptedException
+ {
+ assertFalse(hasCrashed);
+
+ Thread crashThread = new Thread()
+ {
+ @Override
+ public void run()
+ {
+ Looper.prepare();
+ CrashActivity activity = setupActivity(CrashActivity.class);
+ activity.crash();
+ }
+ };
+
+ crashThread.start();
+ crashThread.join();
+ assertTrue(hasCrashed);
+ }
+
@Test
public void menuTest()
{
MenuActivity activity = setupActivity(MenuActivity.class);
- verify(activity.baseMenu).onCreate(
- eq(activity.getMenuInflater()), any());
+ verify(activity.baseMenu).onCreate(eq(activity.getMenuInflater()),
+ any());
Menu menu = activity.toolbar.getMenu();
MenuItem item = menu.getItem(0);
@@ -50,7 +136,26 @@ public class BaseActivityTest
verify(activity.baseMenu).onItemSelected(item);
}
- public static class MenuActivity extends BaseActivity
+ static class CrashActivity extends BaseActivity
+ {
+ public void crash()
+ {
+ throw new RuntimeException("crash!");
+ }
+
+ @Override
+ protected Thread.UncaughtExceptionHandler getExceptionHandler()
+ {
+ return (t, e) -> hasCrashed = true;
+ }
+ }
+
+ static class EmptyActivity extends BaseActivity
+ {
+
+ }
+
+ static class MenuActivity extends BaseActivity
{
public BaseMenu baseMenu;
@@ -78,4 +183,17 @@ public class BaseActivityTest
setBaseMenu(baseMenu);
}
}
+
+ static class ScreenActivity extends BaseActivity
+ {
+ private BaseScreen screen;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState)
+ {
+ super.onCreate(savedInstanceState);
+ screen = spy(new BaseScreen(this));
+ setScreen(screen);
+ }
+ }
}
diff --git a/app/src/test/java/org/isoron/uhabits/activities/habits/list/ListHabitsScreenTest.java b/app/src/test/java/org/isoron/uhabits/activities/habits/list/ListHabitsScreenTest.java
index 749b5685f..2e1e86b6f 100644
--- a/app/src/test/java/org/isoron/uhabits/activities/habits/list/ListHabitsScreenTest.java
+++ b/app/src/test/java/org/isoron/uhabits/activities/habits/list/ListHabitsScreenTest.java
@@ -282,6 +282,6 @@ public class ListHabitsScreenTest extends BaseUnitTest
{
screen.toggleNightMode();
verify(themeSwitcher).toggleNightMode();
- verify(activity).restartWithFade();
+ verify(activity).restartWithFade(ListHabitsActivity.class);
}
}
\ No newline at end of file