Sunday 18 May 2014

Implementing Android Navigation Drawer to multiple activities

If you have come here, you are facing the problem of applying Navigation Drawer to more than one Activity in an easy way. You can find many tutorials about using Navigation Drawer in an Activity. But you are not going to repeat the same long process for another Activity, are you??

The trick is to create a BaseActivity which will be extended by each Activity that wants a Navigation Drawer.

Note- I have used ActionBarCompat so the Drawer will work on API>=7. You will have to add android-support-v7-appcompat as a libary into your project. If you don't want backwards  compatibility you don't have to do it. 

Here is the step by step process:-

1. Create the resources for Navigation Drawer


I have added the resources for Navigation Drawer's icons and names in res->strings.xml. We will use these resources  later.

<string-array name="nav_drawer_items">
        <item>First</item>
        <item>Second</item>
        <item>Third</item>
        <item>Fourth</item>
        <item>Fifth</item>
        <item>Sixth</item>
    </string-array>
<array name="nav_drawer_icons">  

<item>@drawable/ic_launcher</item>  
<item>@drawable/ic_launcher</item>  
<item>@drawable/ic_launcher</item>  
<item>@drawable/ic_launcher</item>
<item>@drawable/ic_launcher</item>  
<item>@drawable/ic_launcher</item>
</array>


2. Create a layout for BaseActivity


Create a layout res->drawer and paste the following code.

<android.support.v4.widget.DrawerLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <!-- The main content view -->
    <FrameLayout
        android:id="@+id/content_frame"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >
        <!-- Add content here -->
    </FrameLayout>

    <!-- The navigation drawer -->
    <ListView android:id="@+id/left_drawer"
        android:layout_width="260dp"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:choiceMode="singleChoice"
        android:divider="@android:color/transparent"
        android:dividerHeight="0dp"
        android:background="#1a1c25"/>
</android.support.v4.widget.DrawerLayout>

The main content view (the FrameLayout above) must be the first child in the DrawerLayout because the XML order implies z-ordering and the drawer must be on top of the content. 


3. Create a custom List Adapter for ListView


I have created a layout res->drawer_list_item for each row in the ListView. I have only used an ImageView and TextView for icon and text. You can tweak it according to your needs. Paste the following code.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="48dp"
    android:background="@drawable/selector_navigation_drawer">

    <ImageView
        android:id="@+id/icon"
        android:layout_width="25dp"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_marginLeft="12dp"
        android:layout_marginRight="12dp"
        android:src="@drawable/ic_launcher"
        android:layout_centerVertical="true" />

    <TextView
        android:id="@+id/title"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_toRightOf="@id/icon"
        android:text="no text"
        android:textColor="#fff"
        android:layout_centerVertical="true"
        android:gravity="center_vertical"
        android:paddingRight="40dp"/>


</RelativeLayout>


Now create a file NavDrawerItem.java in your project's src folder and paste the following code.


package com.nadeem.nav;

public class NavDrawerItem {
private String title;
private int icon;
public NavDrawerItem() {
}

public NavDrawerItem(String title, int icon) {
this.title = title;
this.icon = icon;
}
public NavDrawerItem(String title) {
this.title = title;
}

public String getTitle() {
return this.title;
}

public int getIcon() {
return this.icon;
}

public void setTitle(String title) {
this.title = title;
}

public void setIcon(int icon) {
this.icon = icon;
}

}

Now create the List Adapter NavDrawerListAdapter.java in your project's src folder. Paste the following code.


package com.nadeem.nav;

import java.util.ArrayList;



import android.app.Activity;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;

public class NavDrawerListAdapter extends BaseAdapter {
     
    private Context context;
    private ArrayList<NavDrawerItem> navDrawerItems;
     
    public NavDrawerListAdapter(Context context, ArrayList<NavDrawerItem> navDrawerItems){
        this.context = context;
        this.navDrawerItems = navDrawerItems;
    }

    @Override
    public int getCount() {
        return navDrawerItems.size();
    }

    @Override
    public Object getItem(int position) {       
        return navDrawerItems.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        if (convertView == null) {
            LayoutInflater mInflater = (LayoutInflater)
                    context.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
            convertView = mInflater.inflate(R.layout.drawer_list_item, null);
        }
          
        ImageView imgIcon = (ImageView) convertView.findViewById(R.id.icon);
        TextView txtTitle = (TextView) convertView.findViewById(R.id.title);
        
          
        imgIcon.setImageResource(navDrawerItems.get(position).getIcon());        
        txtTitle.setText(navDrawerItems.get(position).getTitle());
         
       
         
        return convertView;
    }

}

Now,we have created all the required files. It's time to create our BaseActivity class.



4.  Creating BaseActivity.java


Paste the following code.

public class BaseActivity extends ActionBarActivity {
private DrawerLayout mDrawerLayout;
private ListView mDrawerList;
private ActionBarDrawerToggle mDrawerToggle;
protected RelativeLayout _completeLayout, _activityLayout;
// nav drawer title
private CharSequence mDrawerTitle;

// used to store app title
private CharSequence mTitle;

private ArrayList<NavDrawerItem> navDrawerItems;
private NavDrawerListAdapter adapter;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.drawer);
// if (savedInstanceState == null) {
// // on first time display view for first nav item
// // displayView(0);
// }
}

public void set(String[] navMenuTitles,TypedArray navMenuIcons) {
mTitle = mDrawerTitle = getTitle();

mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
mDrawerList = (ListView) findViewById(R.id.left_drawer);

navDrawerItems = new ArrayList<NavDrawerItem>();

// adding nav drawer items
if(navMenuIcons==null){
for(int i=0;i<navMenuTitles.length;i++){
navDrawerItems.add(new NavDrawerItem(navMenuTitles[i]));
}}else{
for(int i=0;i<navMenuTitles.length;i++){
navDrawerItems.add(new NavDrawerItem(navMenuTitles[i],navMenuIcons.getResourceId(i, -1)));
}
}

mDrawerList.setOnItemClickListener(new SlideMenuClickListener());

// setting the nav drawer list adapter
adapter = new NavDrawerListAdapter(getApplicationContext(),
navDrawerItems);
mDrawerList.setAdapter(adapter);

// enabling action bar app icon and behaving it as toggle button
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setHomeButtonEnabled(true);
// getSupportActionBar().setIcon(R.drawable.ic_drawer);

mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout,
R.drawable.ic_drawer, // nav menu toggle icon
R.string.app_name, // nav drawer open - description for
// accessibility
R.string.app_name // nav drawer close - description for
// accessibility
) {
public void onDrawerClosed(View view) {
getSupportActionBar().setTitle(mTitle);
// calling onPrepareOptionsMenu() to show action bar icons
supportInvalidateOptionsMenu();
}

public void onDrawerOpened(View drawerView) {
getSupportActionBar().setTitle(mDrawerTitle);
// calling onPrepareOptionsMenu() to hide action bar icons
supportInvalidateOptionsMenu();
}
};
mDrawerLayout.setDrawerListener(mDrawerToggle);

}

Above, we declare a method set(...), which we will call in different activities using Navigation Drawer. 
When the user selects an item in the drawer's list, the system calls onItemClick() on the OnItemClickListener given to setOnItemClickListener().
What you do in the onItemClick() method depends on how you've implemented your app structure. In the following code, selecting each item in the list calls  a diffferent Activity into the main content view. You can also insert different Fragments, according to your needs.
In your BaseActivity.java paste the following code-
private class SlideMenuClickListener implements
ListView.OnItemClickListener {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position,
long id) {
// display view for selected nav drawer item
displayView(position);
}
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
// getSupportMenuInflater().inflate(R.menu.main, menu);
return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {

if (item.getItemId() == android.R.id.home) {
if (mDrawerLayout.isDrawerOpen(mDrawerList)) {
mDrawerLayout.closeDrawer(mDrawerList);
} else {
mDrawerLayout.openDrawer(mDrawerList);
}
}

return super.onOptionsItemSelected(item);
}

/***
* Called when invalidateOptionsMenu() is triggered
*/
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
// if nav drawer is opened, hide the action items
// boolean drawerOpen = mDrawerLayout.isDrawerOpen(mDrawerList);
// menu.findItem(R.id.action_settings).setVisible(!drawerOpen);
return super.onPrepareOptionsMenu(menu);
}

/**
* Diplaying fragment view for selected nav drawer list item
* */
private void displayView(int position) {
// update the main content by replacing fragments
switch (position) {
case 0:
Intent intent = new Intent(this, First.class);
startActivity(intent);
finish();
break;
case 1:
Intent intent1 = new Intent(this, Second.class);
startActivity(intent1);
finish();
break;
case 2:
Intent intent2 = new Intent(this, third.class);
startActivity(intent2);
finish();
break;
case 3:
Intent intent3 = new Intent(this, fourth.class);
startActivity(intent3);
finish();
break;
case 4:
Intent intent4 = new Intent(this, fifth.class);
startActivity(intent4);
finish();
break;
case 5:
Intent intent5 = new Intent(this, sixth.class);
startActivity(intent5);
finish();
break;
default:
break;
}

// update selected item and title, then close the drawer
mDrawerList.setItemChecked(position, true);
mDrawerList.setSelection(position);
mDrawerLayout.closeDrawer(mDrawerList);
}

@Override
public void setTitle(CharSequence title) {
mTitle = title;
getActionBar().setTitle(mTitle);
}

/**
* When using the ActionBarDrawerToggle, you must call it during
* onPostCreate() and onConfigurationChanged()...
*/

@Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
// Sync the toggle state after onRestoreInstanceState has occurred.
mDrawerToggle.syncState();
}

@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
// Pass any configuration change to the drawer toggls
mDrawerToggle.onConfigurationChanged(newConfig);
}
}


5. Use it


Now everything is set. Let's say we want to use the Navigation Drawer in first.java class. We extend BaseActivity instead of normal Activity or ActionBarActivity. We add the following code in the class-

private String[] navMenuTitles;
private TypedArray navMenuIcons;
navMenuTitles = getResources().getStringArray(R.array.nav_drawer_items); // load titles from strings.xml
navMenuIcons = getResources()
                .obtainTypedArray(R.array.nav_drawer_icons);//load icons from strings.xml

set(navMenuTitles,navMenuIcons);

The above code provides with the flexibility of changing icon and text for every Activity. You just have to pass the Titles and Icons in the set(...) method and the BaseActivity will handle the rest.

The last thing we have to do is to change the layout for first.java. We create a layout res->first.

We only use a TextView as our main content view to maintain simplicity. Here is how we do it-

<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <!-- The main content view -->

    <FrameLayout
        android:id="@+id/content_frame"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >

        <TextView
                        android:id="@+id/textView1"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_alignParentTop="true"
                        android:layout_centerHorizontal="true"
                        android:layout_marginTop="14dp"
                        android:text="Hey there!!"
                        android:textAppearance="?android:attr/textAppearanceLarge"
                        android:textColor="#ffffff"
                        android:textStyle="bold" />
    </FrameLayout>

    <!-- The navigation drawer -->

    <ListView
        android:id="@+id/left_drawer"
        android:layout_width="260dp"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:background="#1a1c25"
        android:choiceMode="singleChoice"
        android:divider="@android:color/transparent"
        android:dividerHeight="0dp" />


</android.support.v4.widget.DrawerLayout>

You must notice that the main content that we wanted to display has been placed inside the FrameLayout. This is the trick. Every time you want to use a different content you just have to place the code inside that FrameLayout only. Let's take another example. If we want to use an ImageView as our layout. The code will be-

<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <!-- The main content view -->

    <FrameLayout
        android:id="@+id/content_frame"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >

        <ImageView
        android:id="@+id/start2"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:background="@drawable/abc" />
    </FrameLayout>

    <!-- The navigation drawer -->

    <ListView
        android:id="@+id/left_drawer"
        android:layout_width="260dp"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:background="#1a1c25"
        android:choiceMode="singleChoice"
        android:divider="@android:color/transparent"
        android:dividerHeight="0dp" />


</android.support.v4.widget.DrawerLayout>


That's it. We have successfully implemented Navigation Drawer in multiple activities. To use it you just have to repeat step 5 and bam!! you are good to go.


Full source code is available here.

Hope you liked it as much as I enjoyed it writing..



63 comments:

  1. step 5 creates errors
    TypedArray cannot be resolved to type... and others

    incomplete or untested program?

    ReplyDelete
    Replies
    1. It's complete and fully tested. Can you elaborate your error? TypedArray is a functionality provided by Android. Make sure you have imported all sufficient Classes by pressing Ctrl+shift+o.

      Delete
  2. Hi can you please send me the full source code to my mail id (kkvarma87@gmail.com)

    ReplyDelete
    Replies
    1. if he sent u the code, can u plz send me the full code?
      my email is blakazulu@gmail.com
      thx

      Delete
  3. can you please send me the full source code to my mail thecorre.nexus@gmail.com

    ReplyDelete
  4. me too. please send the full source code to my mail trusik@2create.sk

    ReplyDelete
  5. I'm getting an error on

    // setting the nav drawer list adapter
    adapter = new NavDrawerListAdapter(getApplicationContext(),
    navDrawerItems);
    mDrawerList.setAdapter(adapter);

    Because android.widget.ListAdapter cannot be applied to com.xxx.xxx.NavDrawerListAdapter

    What can I do?

    ReplyDelete
  6. can you send me the complete source code at my email please.. im having error when with the support. Also I tried to use




    but I always got error in
    getActionBar().setDisplayHomeAsUpEnabled(true);
    getActionBar().setHomeButtonEnabled(true);

    please let me know. I would really appreciate this. Thanks

    ReplyDelete
  7. This code works fantastic. thanks for writing.

    ReplyDelete
  8. This comment has been removed by the author.

    ReplyDelete
  9. This solution blocks underlying events from being triggered. If I exchange textview above with a listview and listens to OnItemClicked, the event isn't triggered at all. Any thoughts on that? Many issues on that; i.e: http://stackoverflow.com/questions/22590247/android-navigation-drawer-doesnt-pass-ontouchevent-to-activity/22596574#22596574

    ReplyDelete
  10. How can i change the menu theme? Thanks!

    ReplyDelete
  11. great tutorial thank you!

    ReplyDelete
  12. Thank you for very nice tutuorial.
    But how to use different layouts for different activities, not just setBackgroundResource() ?
    I need different layouts with different buttons etc. on different activities

    ReplyDelete
    Replies
    1. Use setContentView(Your_Layout) for setting different layouts to different activities.

      Delete
  13. log cat shows null poiter exception at mDrawerToggle.syncState();

    ReplyDelete
  14. hey Nadeem pleaseshare ur email id...m facing same issue in my app but litle diferent....

    ReplyDelete
  15. log cat shows null pointer exception at mDrawerToggle.syncState();
    could you please help to solve it

    ReplyDelete
    Replies
    1. Post your full log cat trace.

      Delete
    2. 08-20 11:18:51.030: D/AndroidRuntime(23590): Shutting down VM
      08-20 11:18:51.030: W/dalvikvm(23590): threadid=1: thread exiting with uncaught exception (group=0x41c6ed40)
      08-20 11:18:51.037: E/AndroidRuntime(23590): FATAL EXCEPTION: main
      08-20 11:18:51.037: E/AndroidRuntime(23590): Process: com.example.simpolov_1, PID: 23590
      08-20 11:18:51.037: E/AndroidRuntime(23590): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.simpolov_1/com.example.simpolov_1.MainActivity}: java.lang.NullPointerException
      08-20 11:18:51.037: E/AndroidRuntime(23590): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2198)
      08-20 11:18:51.037: E/AndroidRuntime(23590): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2257)
      08-20 11:18:51.037: E/AndroidRuntime(23590): at android.app.ActivityThread.access$800(ActivityThread.java:139)
      08-20 11:18:51.037: E/AndroidRuntime(23590): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1210)
      08-20 11:18:51.037: E/AndroidRuntime(23590): at android.os.Handler.dispatchMessage(Handler.java:102)
      08-20 11:18:51.037: E/AndroidRuntime(23590): at android.os.Looper.loop(Looper.java:136)
      08-20 11:18:51.037: E/AndroidRuntime(23590): at android.app.ActivityThread.main(ActivityThread.java:5086)
      08-20 11:18:51.037: E/AndroidRuntime(23590): at java.lang.reflect.Method.invokeNative(Native Method)
      08-20 11:18:51.037: E/AndroidRuntime(23590): at java.lang.reflect.Method.invoke(Method.java:515)
      08-20 11:18:51.037: E/AndroidRuntime(23590): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:785)
      08-20 11:18:51.037: E/AndroidRuntime(23590): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:601)
      08-20 11:18:51.037: E/AndroidRuntime(23590): at dalvik.system.NativeStart.main(Native Method)
      08-20 11:18:51.037: E/AndroidRuntime(23590): Caused by: java.lang.NullPointerException
      08-20 11:18:51.037: E/AndroidRuntime(23590): at com.example.simpolov_1.MainActivity.onPostCreate(MainActivity.java:245)
      08-20 11:18:51.037: E/AndroidRuntime(23590): at android.app.Instrumentation.callActivityOnPostCreate(Instrumentation.java:1173)
      08-20 11:18:51.037: E/AndroidRuntime(23590): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2181)
      08-20 11:18:51.037: E/AndroidRuntime(23590): ... 11 more

      Delete
  16. This comment has been removed by the author.

    ReplyDelete
  17. hey could you please reply a bit quicker.
    Thank you

    ReplyDelete
  18. This comment has been removed by the author.

    ReplyDelete
  19. Hi,

    I have a problem where the overrides onDrawerOpened or onDrawerClosed are not invoked at all. Googling showed me that there is a problem https://code.google.com/p/android/issues/detail?id=69711. Can you please help me understand how to go around this issue?
    Thanks,
    Prity

    ReplyDelete
  20. This comment has been removed by the author.

    ReplyDelete
    Replies
    1. I think you must have a component in view that is an action bar.

      Delete
    2. I do, is there anyway I can get round this regardless or do I just need to get rid of some code?

      Delete
  21. This comment has been removed by the author.

    ReplyDelete
  22. This comment has been removed by the author.

    ReplyDelete
  23. Nice!! Now how would you add a drawer header above the list view?

    ReplyDelete
  24. simply brilliant friend...thanx....with your code i have finally managed transparent nav drawer and looking grt........thanxxxxxxx

    ReplyDelete
  25. btw i am struggling to make action bar transparent can u suggest?

    ReplyDelete
  26. is there any way to make the slide out menu appear above the action bar?

    ReplyDelete
  27. i'm not seeing the ic_drawer icon?

    ReplyDelete
  28. Can u plz send the full source code to my email id minhas.priyanka@gmail.com

    ReplyDelete
    Replies
    1. This comment has been removed by the author.

      Delete
    2. Hey Priyanka, Full code is available on github. I am again pasting the link.
      https://github.com/cuttingedge03/NavigationDrawer

      Delete
  29. how to add circular image like gmail application in drawer.xml..?plz help me to achieve that one..

    ReplyDelete
  30. can u plz send me the full code?
    my email is blakazulu@gmail.com
    thx

    ReplyDelete
  31. This is probably the first code I found online which actually works and doesn't make the app crash..awesome

    ReplyDelete
  32. I made a fork of this that shows how you can do the same thing without duplicating the XML layout code in the various files. The first activity shows importing a partial layout into the base, and the second shows the same thing but created in the onCreate method.

    Check it out https://github.com/gottaloveit/NavigationDrawer

    ReplyDelete
  33. This comment has been removed by the author.

    ReplyDelete
  34. i am getting an error on this place how to solve this
    Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.support.v4.app.ActionBarDrawerToggle.syncState()' on a null object reference

    ReplyDelete
  35. This comment has been removed by a blog administrator.

    ReplyDelete
  36. This comment has been removed by a blog administrator.

    ReplyDelete
  37. This comment has been removed by a blog administrator.

    ReplyDelete
  38. This comment has been removed by a blog administrator.

    ReplyDelete
  39. This is ansuperior writing service point that doesn't always sink in within the context of the classroom. In the first superior writing service paragraph you either hook the reader's interest or lose it. Of course your teacher, who's getting paid to teach you how to write an good essay, 
    java training in chennai | java training in bangalore

    java interview questions and answers | core java interview questions and answers

    ReplyDelete
  40. Were a gaggle of volunteers as well as starting off a brand new gumption within a community. Your blog furnished us precious details to be effective on. You've got completed any amazing work!

    Data Science Course in Indira nagar
    Data Science Course in Electronic city
    Python course in Kalyan nagar
    Data Science course in Indira nagar

    ReplyDelete
  41. https://naddydroid.blogspot.com/2014/05/implementing-android-navigation-drawer.html?showComment=1536060034708#c4065219424678456736
    angularjs online training

    apache spark online training

    informatica mdm online training

    devops online training

    aws online training

    ReplyDelete
  42. This is quite educational arrange. It has famous breeding about what I rarity to vouch. Colossal proverb. This trumpet is a famous tone to nab to troths. Congratulations on a career well achieved. This arrange is synchronous s informative impolites festivity to pity. I appreciated what you ok extremely here 

    devops online training

    aws online training

    data science with python online training

    data science online training

    rpa online training

    ReplyDelete
  43. The site was so nice, I found out about a lot of great things. I like the way you make your blog posts. Keep up the good work and may you gain success in the long run.
    Microsoft Azure online training
    Selenium online training
    Java online training
    Python online training
    uipath online training

    ReplyDelete
  44. Casino Review: $4000 Welcome Bonus in 2021 - Goyang FC
    Casino 토토커뮤니티 bonuses are good, but not 파라오 바카라 the first thing. kadangpintar It's 나비효과 a no-brainer for you to get a $4000 no-deposit bonus that's bet365 free for new players.

    ReplyDelete