Author Archives: Khushi Parihar

Android app sliding intro example/ using AndroidX Viewpager2 and TabLayout

In this tutorial we are going to create intro sliding which will show major features of the app where user can swipe through few slides before getting into app.

we are using AndroidX support Library , ViewPager2 and 3 different fragment & 3 different xml for layouts. And TabLayout for dots navigation.

To demonstrate, I am creating a simple app that contains 3 intro slides with next and skip navigation. The user can navigate through each slide using swipe gesture.

STEP 1: CREATING PROJECT

-Let’s start with creating new project in android studio with package name com.example.manualSliding and class name MainActivity.java and its layout name is activity_main.xml.

STEP 2: ADDING DEPENDENCIES

-open file Gradle Scripts/build.gradle(Module: app)

-under dependencies{ } add this:

dependencies {
   
    //this is for ViewPager
    implementation "androidx.viewpager2:viewpager2:1.0.0"
    
    //this is for TabLayout
    implementation 'com.google.android.material:material:1.2.0-alpha01'


}

-under compileOptions{} add this:

compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}

STEP 3: EDIT app/res/values/colors.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#0698bc</color>
<color name="colorPrimaryDark">#067d9b</color>
<color name="colorAccent">#FF4081</color>
<color name="white">#FFFFFF</color>
<color name="bg1">#0385a3</color>
<color name="bg2">#03a373</color>
<color name="bg3">#b704c4</color>

</resources>

STEP 4: EDIT app/res/values/styles.xml :we using this theme because TabLayout component works in this theme.

<resources>
    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.MaterialComponents.Light.DarkActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
        <item name="windowActionBar">false</item>
        <item name="windowNoTitle">true</item>
    </style>

</resources>

STEP 5: CREATE 3 LAYOUT file tab_selector.xml, selected_dot.xml, default_dot.xml where TabLayout uses tab_selector for drawing dots and tab_selector select between default_dot.xml and selected_dot.xml.

-under app/res/drawable/tab_selector.xml

<?xml version="1.0" encoding="utf-8"?>
<selector
    android:layout_height="8dp"
    android:layout_width="8dp"
    xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/selected_dot"
        android:state_selected="true"/>
    <item android:drawable="@drawable/default_dot"/>
</selector>

-under app/res/drawable/default_dot.xml

<?xml version="1.0" encoding="utf-8"?>
<layer-list
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="8dp"
android:layout_width="8dp">
<item>
<shape
android:innerRadius="0dp"
android:shape="ring"
android:thickness="8dp"
android:useLevel="false">
<solid android:color="@android:color/darker_gray"/>
</shape>
</item>
</layer-list>

-under app/res/drawable/selected_dot.xml

<?xml version="1.0" encoding="utf-8"?>
<layer-list
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="8dp"
android:layout_width="8dp">
<item>
<shape
android:innerRadius="0dp"
android:shape="ring"
android:thickness="8dp"
android:useLevel="false">
<solid android:color="@android:color/white"/>
</shape>
</item>
</layer-list>

STEP 6: CREATING LAYOUT OF 3 INTRO SLIDES

– Each slides has 3 TextView and 1 ImageView.

-create new xml layout file under app/res/layout frg1.xml and add the below code:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/main_layout"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="10"
android:background="#0385a3"
android:orientation="vertical">

<TextView
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_gravity="center"
android:layout_weight="3"
android:gravity="center"
android:paddingLeft="32dp"
android:paddingRight="29dp"
android:text="Welcome to AppIntro"
android:textColor="#ffffff"
android:textSize="27sp"
android:textStyle="bold" />

<LinearLayout
android:layout_width="fill_parent"
android:layout_height="0dp"
android:layout_weight="5"
android:gravity="center"
android:orientation="vertical">

//image link from drawable folder

<ImageView
android:layout_width="140dp"
android:layout_height="180dp"
android:layout_gravity="center"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:src="@drawable/appintro" />
</LinearLayout>

<TextView
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_gravity="center"
android:layout_weight="3"
android:gravity="center"
android:paddingLeft="64dp"
android:paddingRight="64dp"
android:text="Amazing android app with cool features."
android:textColor="#ffffff"
android:textSize="16sp" />

<TextView
android:background="#0385a3"
android:layout_width="fill_parent"
android:layout_height="62dp" />
</LinearLayout>

-create new xml layout file under app/res/layout frg2.xml and add the below code:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/main_layout"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="10"
android:background="#03a373"
android:orientation="vertical">

<TextView
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_gravity="center"
android:layout_weight="3"
android:gravity="center"
android:paddingLeft="32dp"
android:paddingRight="29dp"
android:text="Mobile Recharge"
android:textColor="#ffffff"
android:textSize="27sp"
android:textStyle="bold" />

<LinearLayout
android:layout_width="fill_parent"
android:layout_height="0dp"
android:layout_weight="5"
android:gravity="center"
android:orientation="vertical">

<ImageView
android:layout_width="140dp"
android:layout_height="180dp"
android:layout_gravity="center"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:src="@drawable/mobile" />
</LinearLayout>

<TextView
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_gravity="center"
android:layout_weight="3"
android:gravity="center"
android:paddingLeft="64dp"
android:paddingRight="64dp"
android:text="Best recharge offers and 10% cashback on first recharge"
android:textColor="#ffffff"
android:textSize="16sp" />

<TextView
android:layout_width="fill_parent"
android:layout_height="62dp" />
</LinearLayout>

-create new xml layout file under app/res/layout frg3.xml and add the below code:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/main_layout"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="10"
android:background="#b704c4"
android:orientation="vertical">

<TextView
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_gravity="center"
android:layout_weight="3"
android:gravity="center"
android:paddingLeft="32dp"
android:paddingRight="29dp"
android:text="Movie Tickets"
android:textColor="#ffffff"
android:textSize="27sp"
android:textStyle="bold" />

<LinearLayout
android:layout_width="fill_parent"
android:layout_height="0dp"
android:layout_weight="5"
android:gravity="center"
android:orientation="vertical">

<ImageView
android:layout_width="140dp"
android:layout_height="180dp"
android:layout_gravity="center"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:src="@drawable/movie" />
</LinearLayout>

<TextView
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_gravity="center"
android:layout_weight="3"
android:gravity="center"
android:paddingLeft="64dp"
android:paddingRight="64dp"
android:text="Book Movie tickets for your family and friends"
android:textColor="#ffffff"
android:textSize="16sp" />

<TextView
android:layout_width="fill_parent"
android:layout_height="62dp" />
</LinearLayout>

STEP 7: Create FRAGMENT CLASS for each slide layout:

-create new java class under app/java/com.example.manualSliding/FirstFragment.java

package com.example.manualsliding;

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;

import androidx.fragment.app.Fragment;

public class FirstFragment extends Fragment {

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.frg1, container, false);
/*
TextView tv = (TextView) v.findViewById(R.id.tvFragFifth);
tv.setText(getArguments().getString("msg"));

*/
return v;
}

public static FirstFragment newInstance(String text) {

FirstFragment f = new FirstFragment();
Bundle b = new Bundle();
b.putString("msg", text);
f.setArguments(b);
return f;
}
}

– create new java class under app/java/com.example.manualSliding/SecondFragment.java

package com.example.manualsliding;

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.fragment.app.Fragment;

public class SecondFragment extends Fragment {

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.frg2, container, false);
return v;
}

public static SecondFragment newInstance(String text) {
SecondFragment f = new SecondFragment();
Bundle b = new Bundle();
b.putString("msg", text);
f.setArguments(b);
return f;
}
}

-create new java class under app/java/com.example.manualSliding/ThirdFragment.java

package com.example.manualsliding;

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.fragment.app.Fragment;

public class ThirdFragment extends Fragment {

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View v = inflater.inflate(R.layout.frg3, container, false);
        return v;
    }

    public static ThirdFragment newInstance(String text) {
        ThirdFragment  f = new ThirdFragment();
        Bundle b = new Bundle();
        b.putString("msg", text);
        f.setArguments(b);
        return f;
    }
}

STEP 8: app/java/com.example.manualSliding/MainActivity.java

package com.example.manualsliding;

import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;


public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}

}

STEP 9: app/res/layout/activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".WelcomeActivity">

<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="60dp"
android:layout_marginLeft="60dp"
android:layout_marginTop="44dp"
android:text="Welcome to Home"
android:textSize="35dp"
android:textStyle="bold"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

STEP 10: CREATE class PrefManager.java under package app/java/com.example.manualSliding/ and do the below changes. isFirstTimeLaunch() returns true if the app is launched for the first time.

package com.example.manualsliding;

import android.content.Context;
import android.content.SharedPreferences;

public class PrefManager {
SharedPreferences pref;
SharedPreferences.Editor editor;
Context _context;

// shared pref mode
int PRIVATE_MODE = 0;

// Shared preferences file name
private static final String PREF_NAME = "tutorialface-welcome";

private static final String IS_FIRST_TIME_LAUNCH = "IsFirstTimeLaunch";

public PrefManager(Context context) {
this._context = context;
pref = _context.getSharedPreferences(PREF_NAME, PRIVATE_MODE);
editor = pref.edit();
}

public void setFirstTimeLaunch(boolean isFirstTime) {
editor.putBoolean(IS_FIRST_TIME_LAUNCH, isFirstTime);
editor.commit();
}

public boolean isFirstTimeLaunch() {
return pref.getBoolean(IS_FIRST_TIME_LAUNCH, true);
}

}

STEP:11: CREATE new Activity named app/java/com.example.manualSliding/WelcomeActivity.java

-we use this activity for our introduction slideshow.

STEP 12: EDIT app/res/layout/activity_welcome.xml file for our sliding layout.

-Here we are using a Viewpager2 for fragments layout.

TabLayout for displaying navigation dots.

Button SKIP and Button NEXT.

-The final code of app/res/layout/activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical"
    android:id="@+id/outer_linear_layout"
    tools:context=".MainActivity">

    <androidx.viewpager2.widget.ViewPager2
        android:id="@+id/mypager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_weight="1">
    </androidx.viewpager2.widget.ViewPager2>

    <LinearLayout
        android:id="@+id/bottom_bar_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:background="@color/colorPrimary">

        <Button
            style="?android:attr/borderlessButtonStyle"
            android:id="@+id/button_skip"
            android:layout_width="60dp"
            android:layout_height="match_parent"
            android:padding="0dp"
            android:text="SKIP"
            android:textSize="12sp" />

        <com.google.android.material.tabs.TabLayout
            android:id="@+id/tab_layout"
            style="@style/Widget.MaterialComponents.TabLayout.Colored"
            android:layout_width="290dp"
            android:layout_height="wrap_content"
            app:tabBackground="@drawable/tab_selector"
            app:tabGravity="center"
            app:tabIndicatorHeight="0dp"></com.google.android.material.tabs.TabLayout>

        <Button
            android:id="@+id/button_next"
            style="?android:attr/borderlessButtonStyle"
            android:layout_width="60dp"
            android:layout_height="match_parent"
            android:padding="0dp"
            android:text="NEXT"
            android:textSize="12sp" />
    </LinearLayout>
</LinearLayout>

STEP 13: Open Welcome_Activity.java and modify the code as below. Here I have taken care of few things

-Check for the fist time app launch using prefManager.isFirstTimeLaunch() method. If it returns true, MainActivity will be launched skipping the intro activity.


– Created a PageAdapter for the ViewPager2 and inflated all the layouts we created earlier.

-Inflated an TabLayout and create navigation dots using TabLayoutMediator

-Added Onclick event listener to Skip and Next buttons. so you can add definitions you desire.

-Here is the complete code of app/java/com.example.manualSliding/WelcomeActivity.java

package com.example.manualsliding;

import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.viewpager.widget.ViewPager;
import androidx.viewpager2.adapter.FragmentStateAdapter;
import androidx.viewpager2.widget.ViewPager2;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import com.google.android.material.tabs.TabLayout;
import com.google.android.material.tabs.TabLayoutMediator;


public class WelcomeActivity extends AppCompatActivity {
    private PrefManager prefManager;
    public Button btnSKIP, btnNEXT;
    private static final int NUM_PAGES = 3;
    //The pager widget, which handles animation and allows swiping horizontally to access previous and next wizard steps.
    public static ViewPager2 viewPager;
    // The pager adapter, which provides the pages to the view pager widget.
    private FragmentStateAdapter pagerAdapter;
    public TabLayout tabLayout;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // Checking for first time launch - before calling setContentView()
        prefManager = new PrefManager(this);
        if (!prefManager.isFirstTimeLaunch()) {
            launchHomeScreen();
            finish();
        }
        setContentView(R.layout.activity_welcome);
    
        btnSKIP = (Button) findViewById(R.id.button_skip);
        btnSKIP = (Button) findViewById(R.id.button_next);

        viewPager = findViewById(R.id.mypager);
        pagerAdapter = new MyPagerAdapter(this);
        viewPager.setAdapter(pagerAdapter);
        tabLayout = (TabLayout) findViewById(R.id.tab_layout);
        // new TabLayoutMediator(tabLayout, viewPager,(tab, position) -> tab.setText(titles[position])).attach();

        new TabLayoutMediator(tabLayout, viewPager, (tab, position) -> {
        }).attach();
        Button btnSkip, btnNext;
        btnSkip = (Button) findViewById(R.id.button_skip);
        btnNext = (Button) findViewById(R.id.button_next);
        btnSkip.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                launchHomeScreen();

            }
        });
        btnNext.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // checking for last page
                // if last page home screen will be launched
                int current = (viewPager.getCurrentItem() + 1);
                if (current < NUM_PAGES) {
                    // move to next screen
                    viewPager.setCurrentItem(current);
                } else {
                    launchHomeScreen();
                }
            }


        });
    }

    private void launchHomeScreen() {
        prefManager.setFirstTimeLaunch(false);
        startActivity(new Intent(WelcomeActivity.this, MainActivity.class));
        finish();
    }

    @Override
    public void onBackPressed() {
        if (viewPager.getCurrentItem() == 0) {
            // If the user is currently looking at the first step, allow the system to handle the
            // Back button. This calls finish() on this activity and pops the back stack.d
            super.onBackPressed();
        } else {
            // Otherwise, select the previous step.
            viewPager.setCurrentItem(viewPager.getCurrentItem() - 1);
        }
    }

    public class MyPagerAdapter extends FragmentStateAdapter {

        public MyPagerAdapter(FragmentActivity fa) {
            super(fa);
        }


        @Override
        public Fragment createFragment(int pos) {
            switch (pos) {
                case 0: {
                    return FirstFragment.newInstance("fragment 1");

                }
                case 1: {

                    return SecondFragment.newInstance("fragment 2");
                }
                case 2: {

                    return ThirdFragment.newInstance("fragment 3");
                }
                default:
                    return FirstFragment.newInstance("fragment 1, Default");
            }
        }

        @Override
        public int getItemCount() {
            return NUM_PAGES;
        }

    }

}

171 total views, 3 views today

Facebook login in our Android app/ Sample example tutorial

What is Facebook Login: by integrating facebook log in our app we can login a user by their existing facebook account and get the user information Like Profile picture,User Name,Email,birthday,gender etc. So that we can identify a user.

In this tutorial, we will create a simple android app by  implementing Facebook login to  fetch user’s name &  profile picture, Email , gender  and display them on simple Layout.

STEP 1: Creating  NEW Android Project.

-Create a new project in android studio name it as myfblogin and package name is com.khushi.myfblogin.

-And i have activity named MainActivity.java and its layout file name is activity_main.xml.

STEP 2: Creating New App in facebook developer.

-(you must have a facebook account.)

-Goto http://developer.facebook.com/

-Click on My Apps> Add A New App.

-now give the name to your app and type email.

-Click on Create App.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

-Now note  down your App ID.

STEP 3: Integrating Facebook SDK: The Facebook Login SDK for Android is a component of the Facebook. To use the Facebook Login SDK in your project, make it a dependency in Maven, or download it. Choose the method you prefer,i m using it with maven dependency.

-Download and login into your facebook app.

-In your project, open your_app > Gradle Scripts > build.gradle (Project) make sure the following repository is listed in the buildscript { repositories {}}:

jcenter()

-In your project, open your_app > Gradle Scripts > build.gradle (Module: app) and add the following implementation statement to the dependencies{} section to depend on the latest version of the Facebook Login SDK:

implementation 'com.facebook.android:facebook-login:[5,6)'

-Build your project.

STEP 4: Edit Your Manifest: Add the following uses-permission element after the application element:

<uses-permission android:name="android.permission.INTERNET"/>

-Add the following meta-data element, an activity for Facebook, and an activity and intent filter for Chrome Custom Tabs inside your application element:

<meta-data android:name="com.facebook.sdk.ApplicationId"
    android:value="@string/facebook_app_id"/>

<activity android:name="com.facebook.FacebookActivity"
    android:configChanges=
        "keyboard|keyboardHidden|screenLayout|screenSize|orientation"
    android:label="@string/app_name" />
<activity
    android:name="com.facebook.CustomTabActivity"
    android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data android:scheme="@string/fb_login_protocol_scheme" />
    </intent-filter>
</activity>

STEP 5: Edit your Resources file: Open your- app>src>values>strings.xml file.

 <string name="app_name">myfblogin</string>
    <string name="facebook_app_id">1386921814984169</string>
    <string name="fb_login_protocol_scheme">fb1386921814984169</string>
    <string name="email">Email: </string>
    <string name="name">Facebook Name: </string>
    <string name="gender">Gender: </string>

-Add the following: here type yours app id and  fb_login_protocol_scheme(simply type fb before app id).

STEP 6: Adding details in your facebook app.

6.1 Associate Your Package Name and Default Class with Your App:

-GOTO: https://developers.facebook.com/apps/[your app id]/settings/basic/

-You need to provide platform information for each platform you deliver your app on. For each platform, go to Settings > Basic and click Add Platform.
-Add your Package Name: com.khushi.myfblogin
-Add your class name: com.khushi.myfblogin.MainActivity.java

6.2 Provide the Development and Release Key Hashes for Your App

Windows:You will need the following:

For Generating SHA1 on Windows we need to:
1) Download Openssl library.
– Download it from here(link:https://drive.google.com/open?id=1aq4UFJkJtQYDEnlW6aExdzKFbzZm6QGv).
– And Extract it to path C:\Program Files\
– Run command in command prompt:

	 set OPENSSL_CONF=C:\Program Files\\OpenSSL\bin\openssl.cfg
	

2) Make sure Java Runtime Environment is installed. Then Run the following command in Command Prompt:


"PATH_TO_JAVA_BIN\keytool.exe" -exportcert -alias androiddebugkey -keystore "C:\Users\YOUR_USER_NAME\.android\debug.keystore" | "PATH_TO_OPENSSL_LIBRARY\bin\openssl" sha1 -binary | ""PATH_TO_OPENSSL_LIBRARY\bin\openssl" base64

In the above command make changes according to your system. In our case Its as follows:

YOUR_USER_NAME= khushi
PATH_TO_OPENSSL_LIBRARY = C:\Program Files\OpenSSL
PATH_TO_JAVA_BIN = C:\Program Files\Java\jre1.8.0_251\bin

The final command will be as follows:


"c:\Program Files\Java\jre1.8.0_251\bin\keytool.exe" -exportcert -alias androiddebugkey -keystore "C:\Users\Khushi\.android\debug.keystore" | "C:\Program Files\OpenSSL\bin\openssl" sha1 -binary | "C:\Program Files\OpenSSL\bin\openssl" base64

After running this command it will ask for the android debug keystore password.
The password is :

android

You wil get the key hash as something similar to:

HCSloI6qlnHXBWZLScyWrW+MUP0=

-This command will generate a 28-character key hash unique to your development environment. You will need to provide a development key hash for the development environment of each person who works on your app.

6.3 Enable Single Sign On for Your App:
(If you would like your Android Notifications to have the ability to launch your app, enable single sign on.)

STEP 7: Add the Facebook login button: The simplest way to add Facebook Login to your app is to add LoginButton from the SDK. The LoginButton is a UI element that wraps functionality available in the LoginManager. When someone clicks on the button, the login is initiated with the permissions set in the LoginManager.

 <com.facebook.login.widget.LoginButton
        android:id="@+id/login_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:layout_gravity="center_horizontal" /></pre>

STEP 8: Add the following code in your activity_main.xml file.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <LinearLayout
        android:id="@+id/layout_info"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_above="@+id/login_button"
        android:layout_marginBottom="20dp"
        android:orientation="vertical"
       android:visibility="gone">


        <com.facebook.login.widget.ProfilePictureView
            android:id="@+id/image"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center" />

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">

            <TextView
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="@string/email"
                android:textStyle="bold" />

            <TextView
                android:id="@+id/email"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
               />
        </LinearLayout>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">

            <TextView
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="@string/name"
                android:textStyle="bold" />

            <TextView
                android:id="@+id/name"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
            />
        </LinearLayout>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">

            <TextView
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="@string/gender"
                android:textStyle="bold" />

            <TextView
                android:id="@+id/gender"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                />
        </LinearLayout>
    </LinearLayout>

    <com.facebook.login.widget.LoginButton
        android:id="@+id/login_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:layout_gravity="center_horizontal" />
</RelativeLayout></pre>

STEP 9:  Now open MainActivity.java and do the below modifications-

9.1 Now create a callbackManager to handle login responses by calling CallbackManager.Factory.create.

CallbackManager callbackManager;
CallbackManager = CallbackManager.Factory.create();

9.2 Here you should write all the data permission you required.

loginButton.setPermissions(Arrays.asList("public_profile, email, user_birthday,user_gender"));

-To respond to a login result, register a callback with LoginManager.
-You add the LoginManager callback to your activity or fragment’s onCreate() method:

LoginManager.getInstance().registerCallback(callbackManager,
                new FacebookCallback&lt;LoginResult&gt;() {
                    @Override
                    public void onSuccess(LoginResult loginResult) {
                         SendGraphRequest(loginResult.getAccessToken());
                    }

                    @Override
                    public void onCancel() {
                        // App code

                    }

                    @Override
                    public void onError(FacebookException error) {
                        Toast.makeText(MainActivity.this, "error to Login Facebook", Toast.LENGTH_SHORT).show();
                    }

                });

-If login succeeds, the LoginResult parameter has the new AccessToken, and the most recently granted or declined permissions. 

9.3 onSuccess(...) : And if success then onSucess(...) method is called and in this method we call SendGraphRequest(...) which will fetch user data in JSON OBJECT.

9.4 SendGraphRequest(...): By calling this method we get users data in JSON OBJECT and then this method call setProfileToView(...).

9.5 setProfileToView(...): This method get the data from JSON OBJECT and display it on simple layout.

9.6 onActivityResult(...): Finally, in your onActivityResult method, call callbackManager.onActivityResult to pass the login results to the LoginManager via callbackManager.
package com.khushi.myfblogin;

import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import com.facebook.AccessToken;
import com.facebook.AccessTokenTracker;
import com.facebook.CallbackManager;
import com.facebook.FacebookCallback;
import com.facebook.FacebookException;
import com.facebook.GraphRequest;
import com.facebook.GraphResponse;
import com.facebook.login.LoginManager;
import com.facebook.login.LoginResult;
import com.facebook.login.widget.LoginButton;
import com.facebook.login.widget.ProfilePictureView;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.Arrays;

public class MainActivity extends AppCompatActivity {
CallbackManager callbackManager;
LoginButton loginButton;
TextView email,gender,facebookName;
ProfilePictureView profilePictureView;
LinearLayout infoLayout;
AccessTokenTracker accessTokenTracker;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
callbackManager = CallbackManager.Factory.create();
AccessToken accessToken = AccessToken.getCurrentAccessToken();
boolean isLoggedIn = accessToken != null && !accessToken.isExpired();
if(isLoggedIn){
SendGraphRequest(accessToken);
}

email = (TextView)findViewById(R.id.email);
facebookName = (TextView)findViewById(R.id.name);
gender = (TextView)findViewById(R.id.gender);
profilePictureView = (ProfilePictureView)findViewById(R.id.image);
infoLayout = (LinearLayout)findViewById(R.id.layout_info);
// infoLayout.setVisibility(View.GONE);
loginButton = (LoginButton) findViewById(R.id.login_button);
loginButton.setPermissions(Arrays.asList("public_profile, email, user_birthday,user_gender"));

LoginManager.getInstance().registerCallback(callbackManager,
new FacebookCallback<LoginResult>() {
@Override
public void onSuccess(LoginResult loginResult) {

SendGraphRequest(loginResult.getAccessToken());

}

@Override
public void onCancel() {
// App code

}

@Override
public void onError(FacebookException error) {
Toast.makeText(MainActivity.this, "error to Login Facebook", Toast.LENGTH_SHORT).show();
}

});

}

private void SendGraphRequest(AccessToken token) {
//App code
GraphRequest request = GraphRequest.newMeRequest(
token,
new GraphRequest.GraphJSONObjectCallback() {
@Override
public void onCompleted(JSONObject object, GraphResponse response) {
Log.v("Main", response.toString());
setProfileToView(object);
}
});
Bundle parameters = new Bundle();
parameters.putString("fields","id,name,email,gender,birthday");
request.setParameters(parameters);
request.executeAsync();
//hiding infolayout if iser currently logged out.

accessTokenTracker = new AccessTokenTracker() {
@Override
protected void onCurrentAccessTokenChanged(AccessToken oldAccessToken,
AccessToken currentAccessToken) {
if (currentAccessToken == null) {
infoLayout.setVisibility(View.GONE);
//write your code here what to do when user logout
}
}
};
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
callbackManager.onActivityResult(requestCode, resultCode, data);
super.onActivityResult(requestCode, resultCode, data);
}
private void setProfileToView(JSONObject jsonObject) {
try {
System.out.println("FB json object : "+jsonObject.toString());
email.setText(jsonObject.getString("email"));
gender.setText(jsonObject.getString("gender"));
facebookName.setText(jsonObject.getString("name"));

profilePictureView.setPresetSize(ProfilePictureView.NORMAL);
profilePictureView.setProfileId(jsonObject.getString("id"));
infoLayout.setVisibility(View.VISIBLE);
} catch (JSONException e) {
e.printStackTrace();
}
}

}

369 total views, 3 views today

Integrating Google Sign-In into Android app – Sample example tutorial

What is google sign in: by integrating google sign in our app we can login a user by their existing google account and get the user information Like Profile picture,User Name,Email and other details so that we can identify a user.

In this tutorial, we will create a simple android app by  implementing google sign-in to  fetch user’s name &  profile picture, display them on simple layout  and add a button to finally sign out.

STEP 1:

-Create a new project in android studio name it as Testing and package name is com.khushi.testing.

-And i have activity named MainActivity.java and its layout file name is activity_main.xml.

STEP 2: Add Google Play Services –

In your project’s top-level build.gradle file, ensure that Google’s Maven repository is included:

allprojects {
repositories {
google()

// If you're using a version of Gradle lower than 4.1, you must instead use:
// maven {
// url 'https://maven.google.com'
// }
}
}

Then, in your app-level build.gradle file, declare Google Play services as a dependency:

dependencies {
//this id for google play services
 implementation 'com.google.android.gms:play-services-auth:18.0.0'
//this id for displaying user image in ImageView
implementation 'com.github.bumptech.glide:glide:3.7.0'
 

STEP 3: Add this permission in AndroidManifest File-

 <uses-permission android:name="android.permission.INTERNET">
    </uses-permission>

STEP 4: Create a Google API Console Project-

To create a Google API console project,go to Configure a Project, and specify your Project name,Android App package name and SHA-1.(you can find SHA- in your android studio click on Gradle>Project Name>Tasks>Android>SigningReport)

 

 

 

And download credentials.json and paste it into your project’s app folder in android studio.

STEP 5: Creating App Layout ,in your .Xml file

I simply create :Buttons for sign-in,sign-out,Textview for user name, email and ImageView for profile pic.

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity">

 <com.google.android.gms.common.SignInButton android:id="@+id/sign_in_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="112dp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" />

 <Button android:id="@+id/btn_sign_out" android:layout_width="131dp" android:layout_height="52dp" android:layout_marginStart="140dp" android:layout_marginLeft="140dp" android:layout_marginTop="560dp" android:text="Sign Out" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" />

 <ImageView android:id="@+id/imgProfilePic" android:layout_width="141dp" android:layout_height="126dp" android:layout_marginStart="132dp" android:layout_marginLeft="132dp" android:layout_marginTop="232dp" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:srcCompat="@mipmap/ic_launcher" />

 <TextView android:id="@+id/txtName" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="140dp" android:layout_marginLeft="140dp" android:layout_marginTop="180dp" android:text="TextView" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="@+id/imgProfilePic" />

 <TextView android:id="@+id/txtEmail" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="140dp" android:layout_marginLeft="140dp" android:layout_marginTop="72dp" android:text="TextView" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="@+id/txtName" />
</androidx.constraintlayout.widget.ConstraintLayout>

STEP 6: Now open MainActivity.java and do the below modifications-

In your sign-in activity’s onCreate method, configure Google Sign-In to request the user data required by your app. create a GoogleSignInOptions object with the DEFAULT_SIGN_IN parameter. To request users’ email addresses as well, create the GoogleSignInOptions object with the requestEmail option.

6.1 Configure Google Sign-in and the GoogleSignInClient object

GoogleSignInClient mGoogleSignInClient;
gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN).requestEmail().build();
GoogleSignInClient mGoogleSignInClient;
mGoogleSignInClient = GoogleSignIn.getClient(this, gso);

6.2 SignIn()…Starting the intent prompts the user to select a Google account to sign in with and call OnActivityResult().

6.3   @Override onActivityResult()…After the user signs in, you can get a GoogleSignInAccount object for the user in the activity’s onActivityResult method.

6.4 handleSignInResult()…onActivityResult() call this method with result whether user signed in successfully or not. if success we can get the user’s account details.

6.5 UpdateUI()…User can call this method when he wants changes on ui when account login/logout e.g: display/hide buttons.

6.6 SignOut()… Simply sign out from your google account and you can update UI accordingly.

6.7 @Override OnStart()…Check for existing Google Sign In account, if the user is already signed in the GoogleSignInAccount will be null.

package com.khushi.testing;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.google.android.gms.auth.api.signin.GoogleSignIn;
import com.google.android.gms.auth.api.signin.GoogleSignInAccount;
import com.google.android.gms.auth.api.signin.GoogleSignInClient;
import com.google.android.gms.auth.api.signin.GoogleSignInOptions;
import com.google.android.gms.common.SignInButton;
import com.google.android.gms.common.api.ApiException;
import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.Task;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    GoogleSignInOptions gso;
    GoogleSignInClient mGoogleSignInClient;
    SignInButton signInButton;
    Button btnSignOut;
    private ImageView imgProfilePic;
    private TextView txtName, txtEmail;
    private static final int RC_SIGN_IN =7;
    private static final String TAG = MainActivity.class.getSimpleName();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // Configure sign-in to request the user's ID, email address, and basic
        // profile. ID and basic profile are included in DEFAULT_SIGN_IN.
        gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN).requestEmail().build();
        // Build a GoogleSignInClient with the options specified by gso.
        mGoogleSignInClient = GoogleSignIn.getClient(this, gso);
        signInButton = findViewById(R.id.sign_in_button);
        signInButton.setSize(SignInButton.SIZE_STANDARD);
       signInButton.setOnClickListener(this);
        btnSignOut = (Button) findViewById(R.id.btn_sign_out);
        btnSignOut.setOnClickListener(this);
        imgProfilePic = (ImageView) findViewById(R.id.imgProfilePic);
        txtName = (TextView) findViewById(R.id.txtName);
        txtEmail = (TextView) findViewById(R.id.txtEmail);

    }

    @Override

    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.sign_in_button:
                signIn();
                break;
            case R.id.btn_sign_out:
                signOut();
                break;
            // ...
        }

    }

    private void signIn() {
      //  Toast.makeText(this,"signIn method called",Toast.LENGTH_SHORT).show();
        Intent signInIntent = mGoogleSignInClient.getSignInIntent();
        startActivityForResult(signInIntent, RC_SIGN_IN);
    }
    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        //  Toast.makeText(this,"OnActivityResultCalled"+requestCode,Toast.LENGTH_SHORT).show();
        // Result returned from launching the Intent from GoogleSignInClient.getSignInIntent(...);
        if (requestCode == RC_SIGN_IN) {
            // The Task returned from this call is always completed, no need to attach
            // a listener.
            Task&lt;GoogleSignInAccount&gt; task = GoogleSignIn.getSignedInAccountFromIntent(data);
            handleSignInResult(task);
        }
    }
    private void handleSignInResult(Task&lt;GoogleSignInAccount&gt; completedTask) {
        try {
            GoogleSignInAccount account = completedTask.getResult(ApiException.class);
            String personName = account.getDisplayName();
            String personPhotoUrl = account.getPhotoUrl().toString();
            String email = account.getEmail();
            txtName.setText(personName);
            txtEmail.setText(email);
            Glide.with(getApplicationContext()).load(personPhotoUrl)
                    .thumbnail(0.5f)
                    .crossFade()
                    .diskCacheStrategy(DiskCacheStrategy.ALL)
                    .into(imgProfilePic);
            // Signed in successfully, show authenticated UI.

            updateUI(account);
        } catch (ApiException e) {
            // The ApiException status code indicates the detailed failure reason.
            // Please refer to the GoogleSignInStatusCodes class reference for more information.
            Log.w(TAG, "signInResult:failed code=" + e.getStatusCode()+"\n"+e.getLocalizedMessage()+"\nMEssg: "+e.getMessage());
            updateUI(null);
        }
    }
    private void updateUI(GoogleSignInAccount account) {

        if(account==null)
        {
            // Toast.makeText(this, "not signed in", Toast.LENGTH_SHORT).show();
            signInButton.setVisibility(View.VISIBLE);
            btnSignOut.setVisibility(View.GONE);
            imgProfilePic.setVisibility(View.GONE);
            txtEmail.setVisibility(View.GONE);
            txtName.setVisibility(View.GONE);
            Toast.makeText(this,"please sign in",Toast.LENGTH_SHORT).show();
        }
        else {
            signInButton.setVisibility(View.GONE);
            imgProfilePic.setVisibility(View.VISIBLE);
            txtEmail.setVisibility(View.VISIBLE);
            txtName.setVisibility(View.VISIBLE);
            btnSignOut.setVisibility(View.VISIBLE);
            Toast.makeText(this,"you are signed in",Toast.LENGTH_SHORT).show();
        }

    }
    private void signOut() {
        mGoogleSignInClient.signOut()
                .addOnCompleteListener(this, new OnCompleteListener&lt;Void&gt;() {
                    @Override
                    public void onComplete(@NonNull Task&lt;Void&gt; task) {
                        updateUI(null);
                    }
                });
    }

    @Override
    public void onStart() {
        super.onStart();
        // Check for existing Google Sign In account, if the user is already signed in
// the GoogleSignInAccount will be non-null.
        GoogleSignInAccount account = GoogleSignIn.getLastSignedInAccount(this);
        updateUI(account);
    }

}

283 total views, 2 views today

Add/Remove items at RunTime in Spinner Android?[Solved]

Step 1: Add the following Code to activity_main.xml


<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:tools="http://schemas.android.com/tools"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:paddingBottom="@dimen/activity_vertical_margin"
 android:paddingLeft="@dimen/activity_horizontal_margin"
 android:paddingRight="@dimen/activity_horizontal_margin"
 android:paddingTop="@dimen/activity_vertical_margin"
 tools:context="com.example.spinner_example.MainActivity" >

 <TextView
 android:id="@+id/textView1"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:text="" />

 <Spinner
 android:id="@+id/spinner1"
 android:layout_width="fill_parent"
 android:layout_height="wrap_content"
 android:prompt="@string/spinner_title"
 android:layout_marginTop="81dp" />

 <Button
 android:id="@+id/add_btn"
 style="?android:attr/buttonStyleSmall"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:layout_marginTop="300dp"
 android:text="+" 
 android:onClick="add_item"
 />

 <Button
 android:id="@+id/remove_btn"
 style="?android:attr/buttonStyleSmall"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:layout_alignBaseline="@+id/add_btn"
 android:layout_alignBottom="@+id/add_btn"
 android:layout_alignParentRight="true"
 android:onClick="remove_item"
 android:text="-" />

 <EditText
 android:id="@+id/editText1"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:layout_alignLeft="@+id/spinner1"
 android:layout_below="@+id/spinner1"
 android:layout_marginTop="27dp"
 android:hint="Enter Item"
 android:ems="10" >

 <requestFocus />
 </EditText>

</RelativeLayout>

Step 2: Add the following code to mainActivity.java

package com.example.spinner_example;

import java.util.ArrayList;
import java.util.List;

import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends ActionBarActivity implements OnItemSelectedListener{
 private Spinner spinner;
 private List<String> list;
 private ArrayAdapter<String> adapter;
 private TextView textView;
 private EditText editText;
 private String editText_text,selected_item;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 editText= (EditText) findViewById(R.id.editText1);
 spinner = (Spinner) findViewById(R.id.spinner1);
 textView =(TextView) findViewById(R.id.textView1);
 list=new ArrayList<String>();
 list.add("Surat"); list.add("Nagaur");list.add("Delhi");
 add_spinner();
 }
public void add_spinner()
{
 
adapter = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item,list);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
//Spinner click listener
spinner.setAdapter(adapter);
spinner.setOnItemSelectedListener(this);
}
 

 @Override
 public boolean onCreateOptionsMenu(Menu menu) {
 // Inflate the menu; this adds items to the action bar if it is present.
 getMenuInflater().inflate(R.menu.main, menu);
 return true;
 }

 @Override
 public boolean onOptionsItemSelected(MenuItem item) {
 // Handle action bar item clicks here. The action bar will
 // automatically handle clicks on the Home/Up button, so long
 // as you specify a parent activity in AndroidManifest.xml.
 int id = item.getItemId();
 if (id == R.id.action_settings) {
 return true;
 }
 return super.onOptionsItemSelected(item);
 }
 public void add_item(View v)
 {
 editText_text= editText.getText().toString();
 if(editText_text.length()<1)
 {
 Toast.makeText(this,"Please enter item ",Toast.LENGTH_SHORT).show();
 }
 else
 {
 list.add(editText_text);
 textView.setText("Added : "+editText_text);
 add_spinner();
 editText.setText(null);
 }
 
 }
 public void remove_item(View v)
 {
 
 list.remove(selected_item);
 textView.setText("Removed : " + selected_item);
 add_spinner();
 }

 @Override
 public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2,
 long arg3) {
 // TODO Auto-generated method stub
 spinner.setSelection(arg2);
 selected_item = (String) spinner.getSelectedItem();
 
 }

 @Override
 public void onNothingSelected(AdapterView<?> arg0) {
 // TODO Auto-generated method stub
 
 }
}

Step 3 : Now you can save and execute the Application. Your output will be like this :
Screenshot_2015-10-04-10-34-38

6,435 total views, 1 views today