Cannot use Toothpick.inject in Fragment - java

I'm getting strange error while trying to use Toothpick DI in a fragment:
toothpick.registries.NoFactoryFoundException: No factory could be found for class android.app.Application. Check that the class has either a #Inject annotated constructor or contains #Inject annotated members. If using Registries, check that they are properly setup with annotation processor arguments.
My fragment:
public class ApplicationMenu extends SidebarFragment {
#Inject ApplicationsService applicationsService;
#Inject SectionsService sectionsService;
private EventBus bus = EventBus.getDefault();
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Toothpick.inject(this, Toothpick.openScope(LauncherActivity.class)); // <- Erroring here
}
...
}
Activity:
public class LauncherActivity extends SidebarActivity {
...
#Override
protected void onCreate(Bundle savedInstanceState) {
Scope scope = Toothpick.openScopes(LauncherApplication.class, LauncherActivity.class);
scope.bindScopeAnnotation(LauncherActivitySingleton.class);
scope.installModules(new LauncherActivityModule(this));
super.onCreate(savedInstanceState);
Toothpick.inject(this, scope);
setContentView(R.layout.activity_launcher);
ButterKnife.bind(this);
...
}
...
}
The strange thing is I get the error only in fragments, all injections in other places (ViewRenderers, Adapters, Services etc) work fine with no problem

I figured that out. I close the application scope in one of my services by mistake. That service was using in fragments which cases the error.
Unfortunately the error message does not provide any useful information and the only way to find the problem is to analyze and debug all the code.
So using the toothpick di you have to be very careful with scope life-cycle.

Related

How to get current page URL with gecko?

I am trying to make my android web browser open only specific urls. Because of that, I want to check if loaded url meets the requirements, and according to that to do something. I saw many answers about WebView, but since I have to use open source browser (Mozilla Firefox) I am using gecko. Here is my code, I tried to do something with onLoadRequest but I do not know how to make it work. Thanks.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
GeckoView view = findViewById(R.id.geckoView);
GeckoSession session = new GeckoSession();
GeckoRuntime runtime = GeckoRuntime.create(this);
session.open(runtime);
view.setSession(session);
session.loadUri("https://www.google.com");
GeckoSession.NavigationDelegate.LoadRequest loadRequest=new GeckoSession.NavigationDelegate.LoadRequest();
session.getNavigationDelegate().onLoadRequest(session,loadRequest);
}
#Override
public void onLoadRequest(GeckoSession session, GeckoSession.NavigationDelegate.LoadRequest request)
{
if(request.uri.contains("mail"))
GeckoResult.fromValue(AllowOrDeny.ALLOW);
else
GeckoResult.fromValue(AllowOrDeny.DENY);
}
GeckoView heavily relies on its delegates to allow for app-specific handling of most relevant mechanics and events.
In short, there are runtime and session delegates, set on GeckoRuntime and GeckoSession respectively.
The general pattern is that for each delegate there is a set{DelegateName}Delegate() method to attach delegates to the runtime or session with one exception being RuntimeTelemetry.Delegate which is set in GeckoRuntimeSettings instead.
Delegate methods are called by GeckoView and should not be called by the app.
In your case, you want to implement the NavigationDelegate and set your implementation on the GeckoSession to override the default top-level page load behavior.
class MyNavigationDelegate implements GeckoSession.NavigationDelegate {
#Override
public GeckoResult<AllowOrDeny> onLoadRequest(
final GeckoSession session,
final LoadRequest request) {
// TODO: deny/allow based on your constrains.
}
// TODO: You should implement the rest of the delegate to handle page load
// errors and new session requests triggered by new-tab/window requests.
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
GeckoView view = findViewById(R.id.geckoView);
GeckoSession session = new GeckoSession();
GeckoRuntime runtime = GeckoRuntime.create(this);
session.setNavigationDelegate(new MyNavigationDelegate());
session.open(runtime);
view.setSession(session);
session.loadUri("https://www.google.com");
}
For more details, please consult the API reference and the GeckoView Example implementation.

How to spy on Activity when using Robolectric

I am new on Android and I am playing around with Robolectric for my unit tests.
I am facing the following problem.
I have an activity I want to test.
MainActivity.java
public class MainActivity extends ActionBarActivity
implements NavigationDrawerFragment.NavigationDrawerCallbacks {
private NavigationDrawerFragment mNavigationDrawerFragment;
#Override
protected void onCreate (Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mNavigationDrawerFragment = (NavigationDrawerFragment)
getSupportFragmentManager().findFragmentById(R.id.navigation_drawer);
mNavigationDrawerFragment.setUp(
R.id.navigation_drawer,
(DrawerLayout) findViewById(R.id.drawer_layout));
}
#Override
public void onNavigationDrawerItemSelected (int position) {
...
}
}
This is the test class:
#RunWith(RobolectricGradleTestRunner.class)
#Config(constants = BuildConfig.class)
public class MainActivityTests {
private ActivityController<MainActivity> controller;
private MainActivity activity;
private MainActivity spy;
#Test
public void onCreate_shouldStartNavigationDrawerFragment () {
controller = Robolectric.buildActivity(MainActivity.class);
activity = controller.get();
assertThat(activity).isNotNull();
spy = spy(activity);
spy.onCreate(null);
verify(spy).onCreate(null);
}
}
But I am getting the following exception:
java.lang.IllegalStateException: System services not available to Activities before onCreate() at line spy.onCreate(null).
I have been googling for hours and I have tried several workarounds (blindly) without any success. May please anyone guide me?
Here's what did the trick for me. I use attach() before getting an activity to spy on. Tested with Robolectric 3.0
private MainActivity spyActivity;
#Before
public void setUp(){
MainActivity activity = Robolectric.buildActivity(MainActivity.class).attach().get();
spyActivity = spy(activity);
spyActivity.onCreate(null);
}
You should be driving the activity lifecycle through Robolectric.
See: http://robolectric.org/activity-lifecycle/
So for your case you could do:
controller = Robolectric.buildActivity(MainActivity.class);
activity = controller.get();
assertThat(activity).isNotNull();
spy = spy(activity);
controller.create();
Note: it usually doesn't make sense to spy on the activity lifecycle when testing with Robolectric, since you're the one driving it, so you're only testing that your own method calls executed.
If interested in using exactly the same controller, and work with a spy of the activity, you could modify the inner class of the controller via Reflection, check this method:
public static <T extends Activity> T getSpy(ActivityController<T> activityController) {
T spy = spy(activityController.get());
ReflectionHelpers.setField(activityController, "component", spy);
return spy;
}
The ReflectionHelper is available in Robolectric (tested on Robolectric 4.2). Then it is initialized like this:
controller = Robolectric.buildActivity(MainActivity.class);
activity = getSpy(controller.get());
Hope this helps.
It means that you have to first call the onCreate() method. It has to be the very first called method.

Cannot resolve symbol 'getActivity'

I'm trying to implement a fragment, and am using the Android example as a guide.
In the onActivityCreated() method of the TitlesFragment class, there is this line:
View detailsFrame = getActivity().findViewById(R.id.details);
When I try to include a similar line in my code, I get the error that the 'getActivity' symbol can't be resolved.
I've tried importing everything that their example imports, but it doesn't seem to make any difference. Nor can I find any documentation anywhere that helps me know how to make this accessible.
So, what's the secret on being able to use getActivity?
make sure your class extends Fragment or ListFragment, as shown in the example
public static class TitlesFragment extends ListFragment {
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
...
View detailsFrame = getActivity().findViewById(R.id.details);
...
}
}

Logging switch realization

This code:
public class MyActivity extends Activity {
private final boolean logging = getResources().getBoolean(R.bool.logging);
#Override
public void onCreate(Bundle savedInstanceState) {
if(logging) Log.d("my_log", "some text here");
// some onCreate code...
}
}
generates NullPointerException.
But this one:
public class MyActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
final boolean logging = getResources().getBoolean(R.bool.logging);
if(logging) Log.d("my_log", "some text here");
// some onCreate code...
}
}
Does not.
The main idea to switch logging in entire application with a boolean resource.
I can successfully declare this variable for every function in class, but can it be done for entire class just once?
Have you considered using a proper logging framework?
If you use the slf4j API you can write stuff like
log.debug("A={}, B={}", a, b)
where the switch is externally set in a well-documented way whether to generate a log statement or not. Also the slf4j {}-construct allows delaying the call to a.toString() and b.toString() until after the logging framework has decided that the log message actually needs to be generated.
slf4j is an API. You have several backends to choose from. For starters you can just pick the "simple" backend.
See http://slf4j.org/manual.html for an introduction.
Instead of putting value to string resource, I used static variable in special class.
public class constants {
public static final boolean logging = true;
}
so it can be accessed from any activity:
private boolean logging = constants.logging;

Android: Referring to a string resource when defining a log name

In my Android app, I want to use a single variable for the log name in multiple files. At the moment, I'm specifying it separately in each file, e.g.
public final String LOG_NAME = "LogName";
Log.d(LOG_NAME, "Logged output);
I've tried this:
public final String LOG_NAME = (String) getText(R.string.app_name_nospaces);
And while this works in generally most of my files, Eclipse complains about one of them:
The method getText(int) is undefined
for the type DatabaseManager
I've made sure I'm definitely importing android.content.Context in that file. If I tell it exactly where to find getText:
Multiple markers at this line
- Cannot make a static reference to the non-static method getText(int)
from the type Context
- The method getText(int) is undefined for the type DatabaseManager
I'm sure I've committed a glaringly obvious n00b error, but I just can't see it! Thanks for all help: if any other code snippets would help, let me know.
That's because getText is a method of Context. It does not matter if you import the Context class; what matters is that you invoke that method from a Context (for instance, the Activity class is a Context (it inherits Context)).
In that case, what I'd recommend, is creating a Application class that returns the context you want. Here I explain how to do it. After that you can do something like:
public final String LOG_NAME = (String) App.getContext().getText(R.string.app_name_nospaces);
Depending on what sort of 'files' you are using, you can define a TAG that is used.
For example, when I create an app, I like to create a base class for my Activity classes...
Suppose my app is called 'Wibble', and my package is com.mydomain.Wibble...I create my base Activity like so...
package com.mydomain.Wibble
public class WibbleActivity extends Activity {
final protected String TAG = this.getClass().getName();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// I'll explain how this next line works later
android.util.Log.d(TAG, "Entered onCreate()...");
}
}
Now suppose I derive an activity as follows...
package com.mydomain.Wibble
public class SomeActivity extends WibbleActivity {
#Override
protexted void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Don't Log "Entered onCreate()..." - WibbleActivity does it for me
android.util.Log.d(TAG, "SomeText");
}
}
Then I derive another Activity...
package com.mydomain.Wibble
public class SomeOtherActivity extends WibbleActivity {
#Override
protexted void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Don't Log "Entered onCreate()..." - WibbleActivity does it for me
android.util.Log.d(TAG, "SomeOtherText");
}
When onCreate() is called for SomeActivity, the output will be...
com.mydomain.Wibble.SomeActivity Entered onCreate()...
com.mydomain.Wibble.SomeActivity SomeText
...when onCreate() is called for SomeOtherActivity however, the output will be...
com.mydomain.Wibble.SomeOtherActivity Entered onCreate()...
com.mydomain.Wibble.SomeOtherActivity SomeOtherText
Neither activity needs to know specifics through an explicit string and the package name is prefixed. Obviously it will only work in certain situations but I find it useful.

Categories