I am attempting to add Dagger 2 to my Android Project.
My application have following screen
Login extends Base activity
Navigation Activity extends Base activity
MW Activity extents Navigation Activity
Presenter Injection is working fine in Login and navigation activity where as in MW activity it return null
Butter Knife is also not working in MW Activity where as working fine in other activities
Following are my classes
Application class
public class abcApplication extends Application {
ApplicationComponent mApplicationComponent;
#Override
public void onCreate() {
super.onCreate();
mApplicationComponent = DaggerApplicationComponent.builder()
.applicationModule(new ApplicationModule(this))
.build();
mApplicationComponent.inject(this);
}
public static abcApplication get(Context context) {
return (abcApplication) context.getApplicationContext();
}
public ApplicationComponent getComponent() {
return mApplicationComponent;
}
// Needed to replace the component with a test specific one
public void setComponent(ApplicationComponent applicationComponent) {
mApplicationComponent = applicationComponent;
}
}
Base activity
public class BaseActivity extends AppCompatActivity {
private ActivityComponent mActivityComponent;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
public ActivityComponent activityComponent() {
if (mActivityComponent == null) {
mActivityComponent = DaggerActivityComponent.builder()
.activityModule(new ActivityModule(this))
.applicationComponent(abcApplication.get(this).getComponent())
.build();
}
return mActivityComponent;
}
}
Navigation Activity
public class NavigationActivity extends BaseActivity implements NavigationView {
#Inject
DataClient mDataClient;
#Bind(R.id.drawer_layout)
protected DrawerLayout mDrawerLayout;
#Bind(R.id.navList)
ExpandableListView mExpandableListView;
private ActionBarDrawerToggle mDrawerToggle;
private String mActivityTitle;
private ExpandableListAdapter mExpandableListAdapter;
private List<String> mExpandableListTitle;
private Map<String, List<String>> mExpandableListData;
private Map<String, String> activityMap;
private int lastExpandedPosition = -1;
#Inject
NavigationPresenter navigationPresenter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_navigation);
activityComponent().inject(this);
ButterKnife.bind(this);
navigationPresenter.attachView(this);
}
#Override
protected void onDestroy() {
super.onDestroy();
navigationPresenter.detachView();
}
}
MW Activity
public class MWActivity extends NavigationActivity implements MWView{
private MWPagerAdapter mMWPagerAdapter;
#Inject
MWPresenter MWPresenter;
private ViewPager mViewPager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LayoutInflater inflater = (LayoutInflater) this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
DrawerLayout mDrawer = (DrawerLayout) findViewById(R.id.drawer_layout);
ButterKnife.bind(this);
MWPresenter.attachView(this);
MWPresenter.getMarketData();
}
}
Logcat :
FATAL EXCEPTION: main
Process: com.abc.xyz, PID: 21542
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.abc.xyz/com.abc.trading.xyz.ui.main.mw.view.MWActivity}: java.lang.NullPointerException
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2318)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2396)
#PreActivity
#Component(dependencies = ApplicationComponent.class, modules = ActivityModule.class)
public interface ActivityComponent {
void inject(LoginActivity loginActivity);
void inject(NavigationActivity navigationActivity);
void inject(MWActivity mWActivity);
void inject(MWTabFragment mWTabFragment);
void inject(MWDetailsActivity mWDetailsActivity);
}
You have 2 issues regarding super- / sub types at hand.
Butterknife does not support injection to super types
Dagger does not support injection to sub types
As already pointed out, to solve 2. you would need to call inject in your MWActivity, and to use Butterknife you need to use a ViewHolder pattern within your super class to bind / inject the fields, since it will only inject MWActivity and not NavigationActivity.
Activity Component was not injected activityComponent().inject(this);
in MW Activity
public class MWActivity extends NavigationActivity implements MWView{
private MWPagerAdapter mMWPagerAdapter;
#Inject
MWPresenter MWPresenter;
private ViewPager mViewPager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LayoutInflater inflater = (LayoutInflater) this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
activityComponent().inject(this);
DrawerLayout mDrawer = (DrawerLayout) findViewById(R.id.drawer_layout);
ButterKnife.bind(this);
MWPresenter.attachView(this);
MWPresenter.getMarketData();
}
}
ActivityComponent (Base Activity)
public class BaseActivity extends AppCompatActivity {
private ActivityComponent mActivityComponent;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
public ActivityComponent activityComponent() {
if (mActivityComponent == null) {
mActivityComponent = DaggerActivityComponent.builder()
.activityModule(new ActivityModule(this))
.applicationComponent(OmsApplication.get(this).getComponent())
.build();
}
return mActivityComponent;
}
}
Related
I have made an android bottom bar in activity and thought i'd use dagger2 to set listener for it. How ever I am confused as to how to get the getSupportFragmentManager() inside the listener class.
Here's what i'm trying
public class MainActivity extends AppCompatActivity {
#BindView(R.id.bottomNavigationView)
BottomNavigationView bottomNavigationView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
BottomBarComponent bottomBarComponent = DaggerBottomBarComponent.builder()
.bottomBarModule(new BottomBarModule(getSupportFragmentManager()))
.build();
}
}
The component part is
#Singleton
#Component(modules = {BottomBarModule.class})
public interface BottomBarComponent {
void inject(BottomNavigationListener listener);
}
The module is
#Module
public class BottomBarModule {
private FragmentManager manager;
public BottomBarModule(FragmentManager context) {
this.manager = context;
}
#Singleton
#Provides
public FragmentManager provideSupportManager(){
return manager;
}
}
And need to get fragmentsupportManager here
public class BottomNavigationListener implements BottomNavigationView.OnNavigationItemSelectedListener{
#Inject
FragmentManager manager;
public void BottomNavigationListener() {
//somehow need to get fragmentSupportManager here
}
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
return true;
}
}
How do I do this?
You need to share instance of BottomBarComponent with your BottomNavigationListener and invoke inject() method on it.
BottomNavigationListener.class
...
public void BottomNavigationListener(BottomBarComponent injector) {
//somehow need to get fragmentSupportManager here
injector.inject(this); // now manager field must be filled
}
...
MainActivity.class
...
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
BottomBarComponent bottomBarComponent = DaggerBottomBarComponent.builder()
.bottomBarModule(new BottomBarModule(getSupportFragmentManager()))
.build();
// pass BottomBarComponent to BottomNavigationListener for injecting FragmentManager
BottomNavigationListener listener = new BottomNavigationListener(bottomBarComponent);
}
...
But very strange thing you're trying to do. It's all equivalent to passing FragmentManager directly to BottomNavigationListener without using Dagger.
I am trying to inject context in my Interactor class which is giving me a null pointer exception.
I have used the MVP pattern and I am trying to get access to the context in my non-activity class.
I am not really sure if this is the best technique used.
Module:
#Module
public class ContextModule {
private final Context context;
public ContextModule(Context context) {
this.context = context;
}
#Singleton
#Provides
public Context getContext() {
return this.context;
}
}
Component:
#Singleton
#Component(modules = {ContextModule.class})
public interface AppComponent {
void inject(MainActivity mainActivity);
}
App
public class App extends Application {
private AppComponent appComponent;
#Override
public void onCreate() {
super.onCreate();
appComponent = DaggerAppComponent.builder()
.contextModule(new ContextModule(this))
.build();
}
public AppComponent getAppComponent() {
return appComponent;
}
}
MainActivity
public class MainActivity extends AppCompatActivity implements
TaskContract.IMainView {
#Inject
MainInteractor mainInteractor;
private MainPresnter mainPresnter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
((App) getApplication()).getAppComponent().inject(this);
mainPresnter = new MainPresnter(this);
}
#Override
public void getRandomNumber(int rNum) {
Toast.makeText(this, "" + rNum, Toast.LENGTH_SHORT).show();
}
#Override
protected void onResume() {
super.onResume();
mainPresnter.fetchFromService();
}
}
Presenter
public class MainPresnter implements TaskContract.IMainPresenter,
TaskContract.OnTaskCompletionResult {
private TaskContract.IMainView mainView;
private MainInteractor mainInteractor;
public MainPresnter(TaskContract.IMainView mainView) {
this.mainView = mainView;
mainInteractor = new MainInteractor(this);
}
#Override
public void fetchFromService() {
mainInteractor.callService();
}
#Override
public void onSuccess(int rNum) {
mainView.getRandomNumber(rNum);
}
}
Interactor
public class MainInteractor implements TaskContract.IMainInteractor {
private static final int JOB_ID = 100 ;
private Context context;
#Inject
public MainInteractor(Context context) {
this.context = context;
}
public MainInteractor(TaskContract.OnTaskCompletionResult completionListener)
{
TaskService.setCompletionListener(completionListener);
}
#Override
public void callService() {
JobInfo jobInfo = new JobInfo.Builder(JOB_ID,
new ComponentName(context, TaskService.class))
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
.setPeriodic(10000)
.build();
JobScheduler jobScheduler = (JobScheduler)
context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
jobScheduler.schedule(jobInfo);
}
}
Gradle
implementation 'com.google.dagger:dagger-android:2.11'
annotationProcessor 'com.google.dagger:dagger-compiler:2.11'
You don't inject the Interactor within your Presenter - therefore it won't have a context.
You could probably restructure your Presenter to require the Interactor as a dependency - this would also mean you'd need to restructure how the completion listener is set.
Android Studio 3.0 Canary 8
I am trying to inject my MainActivity into my Adapter. However, my solution works ok, but I think its a code smell and not the right way to do it.
My adapter snippet looks like this the but I don't like about this is that I have to cast the Activity to MainActivity:
public class RecipeAdapter extends RecyclerView.Adapter<RecipeListViewHolder> {
private List<Recipe> recipeList = Collections.emptyList();
private Map<Integer, RecipeListViewHolderFactory> viewHolderFactories;
private MainActivity mainActivity;
public RecipeAdapter(Activity activity, Map<Integer, RecipeListViewHolderFactory> viewHolderFactories) {
this.recipeList = new ArrayList<>();
this.viewHolderFactories = viewHolderFactories;
this.mainActivity = (MainActivity)activity;
}
#Override
public RecipeListViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
/* Inject the viewholder */
final RecipeListViewHolder recipeListViewHolder = viewHolderFactories.get(Constants.RECIPE_LIST).createViewHolder(viewGroup);
recipeListViewHolder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
/* Using the MainActivity to call a callback listener */
mainActivity.onRecipeItemClick(getRecipe(recipeListViewHolder.getAdapterPosition()));
}
});
return recipeListViewHolder;
}
}
In my Module, I pass the Activity in the module's constructor and pass it to the Adapter.
#Module
public class RecipeListModule {
private Activity activity;
public RecipeListModule() {}
public RecipeListModule(Activity activity) {
this.activity = activity;
}
#RecipeListScope
#Provides
RecipeAdapter providesRecipeAdapter(Map<Integer, RecipeListViewHolderFactory> viewHolderFactories) {
return new RecipeAdapter(activity, viewHolderFactories);
}
}
In My Application class I create the components and I am using a SubComponent for the adapter. Here I have to pass the Activity which I am not sure is a good idea.
#Override
public void onCreate() {
super.onCreate();
applicationComponent = createApplicationComponent();
recipeListComponent = createRecipeListComponent();
}
public BusbyBakingComponent createApplicationComponent() {
return DaggerBusbyBakingComponent.builder()
.networkModule(new NetworkModule())
.androidModule(new AndroidModule(BusbyBakingApplication.this))
.exoPlayerModule(new ExoPlayerModule())
.build();
}
public RecipeListComponent createRecipeListComponent(Activity activity) {
return recipeListComponent = applicationComponent.add(new RecipeListModule(activity));
}
My Fragment I inject like this:
#Inject RecipeAdapter recipeAdapter;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
((BusbyBakingApplication)getActivity().getApplication())
.createRecipeListComponent(getActivity())
.inject(this);
}
Even though the above design works, I think it's a code smell as I have to cast the Activity to the MainActivity. The reason I use the Activity as I want to make this module more generic.
Just wondering if there is a better way
=============== UPDATE USING INTERFACE
Interface
public interface RecipeItemClickListener {
void onRecipeItemClick(Recipe recipe);
}
Implementation
public class RecipeItemClickListenerImp implements RecipeItemClickListener {
#Override
public void onRecipeItemClick(Recipe recipe, Context context) {
final Intent intent = Henson.with(context)
.gotoRecipeDetailActivity()
.recipe(recipe)
.build();
context.startActivity(intent);
}
}
In my module, I have the following providers
#Module
public class RecipeListModule {
#RecipeListScope
#Provides
RecipeItemClickListener providesRecipeItemClickListenerImp() {
return new RecipeItemClickListenerImp();
}
#RecipeListScope
#Provides
RecipeAdapter providesRecipeAdapter(RecipeItemClickListener recipeItemClickListener, Map<Integer, RecipeListViewHolderFactory> viewHolderFactories) {
return new RecipeAdapter(recipeItemClickListener, viewHolderFactories);
}
}
Then I use it through constructor injection in the RecipeAdapter
public class RecipeAdapter extends RecyclerView.Adapter<RecipeListViewHolder> {
private List<Recipe> recipeList = Collections.emptyList();
private Map<Integer, RecipeListViewHolderFactory> viewHolderFactories;
private RecipeItemClickListener recipeItemClickListener;
#Inject /* IS THIS NESSESSARY - AS IT WORKS WITH AND WITHOUT THE #Inject annotation */
public RecipeAdapter(RecipeItemClickListener recipeItemClickListener, Map<Integer, RecipeListViewHolderFactory> viewHolderFactories) {
this.recipeList = new ArrayList<>();
this.viewHolderFactories = viewHolderFactories;
this.recipeItemClickListener = recipeItemClickListener;
}
#Override
public RecipeListViewHolder onCreateViewHolder(final ViewGroup viewGroup, int i) {
/* Inject the viewholder */
final RecipeListViewHolder recipeListViewHolder = viewHolderFactories.get(Constants.RECIPE_LIST).createViewHolder(viewGroup);
recipeListViewHolder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
recipeItemClickListener.onRecipeItemClick(getRecipe(recipeListViewHolder.getAdapterPosition()), viewGroup.getContext());
}
});
return recipeListViewHolder;
}
}
Just one question, is the #Inject annotation need for the constructor in the RecipeAdapter. As it works with or without the #Inject.
Do not pass Activities into Adapters - This is a really bad practice.
Inject only the fields you care about.
In your example: Pass an interface into the adapter to track the item click.
If you need a MainActivity then you should also provide it. Instead of Activity declare MainActivity for your module.
#Module
public class RecipeListModule {
private MainActivity activity;
public RecipeListModule(MainActivity activity) {
this.activity = activity;
}
}
And your Adapter should just request it (Constructor Injection for non Android Framework types!)
#RecipeListScope
class RecipeAdapter {
#Inject
RecipeAdapter(MainActivity activity,
Map<Integer, RecipeListViewHolderFactory> viewHolderFactories) {
// ...
}
}
If you want your module to use Activity and not MainActivity then you will need to declare an interface as already mentioned. You adapter would then declare the interface as its dependency.
But in some module you will still have to bind that interface to your MainActivity and one module needs to know how to provide the dependency.
// in some abstract module
#Binds MyAdapterInterface(MainActivity activity) // bind the activity to the interface
Addressing the updated part of the question
Just one question, is the #Inject annotation need for the constructor in the RecipeAdapter. As it works with or without the #Inject.
It works without it because you're still not using constructor injection. You're still calling the constructor yourself in providesRecipeAdapter(). As a general rule of thumb—if you want to use Dagger properly—don't ever call new yourself. If you want to use new ask yourself if you could be using constructor injection instead.
The same module you show could be written as follows, making use of #Binds to bind an implementation to the interface, and actually using constructor injection to create the adapter (which is why we don't have to write any method for it! Less code to maintain, less errors, more readable classes)
As you see I don't need to use new myself—Dagger will create the objects for me.
public abstract class RecipeListModule {
#RecipeListScope
#Binds
RecipeItemClickListener providesRecipeClickListener(RecipeItemClickListenerImp listener);
}
Personally I would do the following trick
public class MainActivity extends AppCompatActivity {
private static final String TAG = "__ACTIVITY__";
public static MainActivity get(Context context) {
// noinspection ResourceType
return (MainActivity)context.getSystemService(TAG);
}
#Override
protected Object getSystemService(String name) {
if(TAG.equals(name)) {
return this;
}
return super.getSystemService(name);
}
}
public class RecipeAdapter extends RecyclerView.Adapter<RecipeListViewHolder> {
private List<Recipe> recipeList = Collections.emptyList();
private Map<Integer, RecipeListViewHolderFactory> viewHolderFactories;
public RecipeAdapter(Map<Integer, RecipeListViewHolderFactory> viewHolderFactories) {
this.recipeList = new ArrayList<>();
this.viewHolderFactories = viewHolderFactories;
}
#Override
public RecipeListViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
/* Inject the viewholder */
final RecipeListViewHolder recipeListViewHolder = viewHolderFactories.get(Constants.RECIPE_LIST).createViewHolder(viewGroup);
recipeListViewHolder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
MainActivity mainActivity = MainActivity.get(v.getContext());
if(recipeListViewHolder.getAdapterPosition() != -1) {
mainActivity.onRecipeItemClick(
getRecipe(recipeListViewHolder.getAdapterPosition()));
}
}
});
return recipeListViewHolder;
}
}
I am trying to inject my MainActivity into the Fragment. I have an Interface that is implemented in my MainActivity that will listen to events from the Fragment. So I want to Inject the MainActivity and call the event listener on it.
I have tried doing the following but has failed to do so. Just displaying the code snippets.
interface
public interface RecipeItemListener {
void onRecipeItem();
}
MainActivity that implements the interface
public class MainActivity extends AppCompatActivity implements RecipeItemListener {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if(savedInstanceState == null) {
getSupportFragmentManager()
.beginTransaction()
.add(R.id.main_fragment_container, RecipeListView.newInstance(), RecipeListView.TAG)
.commit();
}
}
#Override
public void onRecipeItem() {
Timber.d("onRecipeItem");
}
}
My Module that provides the MainActivity
#Module
public class RecipeListModule {
private MainActivity mainActivity;
public RecipeListModule(MainActivity mainActivity) {
this.mainActivity = mainActivity;
}
#RecipeListScope
#Provides
public RecipeItemListener providesRecipeListMainActivity() {
return mainActivity;
}
}
My main Component
#Singleton
#Component(modules = {
AndroidModule.class,
NetworkModule.class})
public interface BusbyBakingComponent {
RecipeListComponent add(RecipeListModule recipeListModule);
}
My SubComponent
#RecipeListScope
#Subcomponent(modules = {RecipeListModule.class})
public interface RecipeListComponent {
void inject(RecipeListView target);
}
My Application class
public class BusbyBakingApplication extends Application {
private BusbyBakingComponent applicationComponent;
private RecipeListComponent recipeListComponent;
#Override
public void onCreate() {
super.onCreate();
applicationComponent = createApplicationComponent();
}
public BusbyBakingComponent createApplicationComponent() {
return DaggerBusbyBakingComponent.builder()
.networkModule(new NetworkModule())
.androidModule(new AndroidModule(BusbyBakingApplication.this))
.build();
}
public BusbyBakingComponent getApplicationComponent() {
return applicationComponent;
}
public RecipeListComponent createRecipeListComponent(MainActivity activity) {
recipeListComponent = applicationComponent.add(new RecipeListModule(activity));
return recipeListComponent;
}
public void releaseRecipeListComponent() {
recipeListComponent = null;
}
}
And in My fragment this is how I am trying to inject:
#Inject MainActivity mainActivity;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
((BusbyBakingApplication)getActivity().getApplication())
.createRecipeListComponent((MainActivity)getActivity())
.inject(RecipeListView.this);
}
I keep getting the following error:
Error:(14, 8) error: [me.androidbox.busbybaking.di.RecipeListComponent.inject(me.androidbox.busbybaking.recipieslist.RecipeListView)] me.androidbox.busbybaking.recipieslist.MainActivity cannot be provided without an #Inject constructor or from an #Provides- or #Produces-annotated method.
me.androidbox.busbybaking.recipieslist.MainActivity is injected at
me.androidbox.busbybaking.recipieslist.RecipeListView.mainActivity
me.androidbox.busbybaking.recipieslist.RecipeListView is injected at
me.androidbox.busbybaking.di.RecipeListComponent.inject(target)
Many thanks for any suggestions.
If you have a look at your module
#RecipeListScope
#Provides
public RecipeItemListener providesRecipeListMainActivity() {
return mainActivity;
}
You provide the interface (which is good) and not MainActivity (the implementation).
Since you request MainActivity
#Inject MainActivity mainActivity;
You receive this error:
MainActivity cannot be provided [...]
because you only provide RecipeItemListener.
Switch your code from requiring MainActivity in RecipeListView to
#Inject RecipeItemListener recipeListener;
and it should work, if the rest of your setup is correct.
You can access activity in Fragment using getActivity() and cast it to your interface listener like this
((RecipeItemListener)getActivity()).doSomethingOnListener()
much simpler, without any unnecessary injections
I have a custom class Fragment AFragment that has an injected attribute : AController controller.
The problem is that when I call this : controller.onStart() --> controller is null.
The code :
Class AFragment :
public class AFragment extends Fragment {
#Inject
AController controller;
#Override
public void onStart() {
super.onStart();
controller.onStart();
}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
controller.onCreate();
}
}
Class AController :
public class AController {
private final DataInteractor dataInteractor;
#Inject
public AController(DataInteractor dataInteractor){
this.dataInteractor = dataInteractor;
}
public void onCreate(){
}
public void onStart(){
}
}
The only thing you need is create a component and inject AFragment into it.
#Singleton
#Component
public interface ApplicationComponent {
void inject(AFragment fragment);
}
Because in AController class you make a Constructor inject so you don't need to make a module for your component.
And also you need to create the component when application start. So just init it in your Application extended class.
public class DemoApplication extends Application {
private ApplicationComponent mComponent;
#Override
public void onCreate() {
super.onCreate();
mComponent = DaggerApplicationComponent.builder()
.build();
}
public ApplicationComponent getComponent() {
return mComponent;
}
}
The last step is what i say before, inject AFragment into the component.
public class AFragment extends Fragment {
#Inject
AController controller;
#Override
public void onStart() {
super.onStart();
controller.onStart();
}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
((DemoApplication) getApplication()).getComponent().inject(this);
controller.onCreate();
}
}