I'm developing an Android app and am to the point where I would like to integrate Facebook login onto the main page. I am following these directions for the same:
https://developers.facebook.com/docs/android/login-with-facebook/#step1
Part of the walkthrough lists changing the MainActivity class to extend FragmentActivity. My MainActivity class, however, already extended it because of a content slider I implemented on the main view. The original onCreate code of MainActivity was:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// Instantiate a ViewPager and a PagerAdapter.
mPager = (ViewPager) findViewById(R.id.pager);
mPagerAdapter = new ScreenSlidePagerAdapter(getFragmentManager());
mPager.setAdapter(mPagerAdapter);
mPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
#Override
public void onPageSelected(int position) {
invalidateOptionsMenu();
}
});
}
And this was working great. Following the Facebook login walkthrough, I added a new 'MainFragment' class following the directions as literally as possible:
public class MainFragment extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater,
ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.main, container, false);
return view;
}
}
, also added a com.facebook.widget.LoginButton to my MainActivity layout (this seems to display fine), and tried to tie it all together by adding the following to the OnCreate section of the MainActivity class (displayed above, but not repeating it all here again for brevity):
if (savedInstanceState == null) {
// Add the fragment on initial activity setup
mainFragment = new MainFragment();
getSupportFragmentManager()
.beginTransaction()
.add(android.R.id.content, mainFragment)
.commit();
} else {
// Or set the fragment from restored state info
mainFragment = (MainFragment) getSupportFragmentManager()
.findFragmentById(android.R.id.content);
}
The question I fundamentally have is how to make both my existing slider and Facebook login play together nicely? If I add the Facebook code needed to OnCreate of MainActivity, then my slider goes away. If I don't add the code, then Facebook login obviously doesn't work. I've read as much as possible on Fragments thinking I might be doing something wrong there, but haven't had much luck...
Thanks in advance for any help you can provide!
Here is my solution, you make your MainActivity as the first level, in this level your Android app will check whether the user is logged or not, based on this, go to the second level, which is either a Facebook login page or the content slider. For implementation, it would be like this:
This is the MainActivity:
public class MainActivity extends FragmentActivity{
private LoginFragment loginFragment;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState == null) {
// Add the fragment on initial activity setup
loginFragment = new LoginFragment();
getSupportFragmentManager()
.beginTransaction()
.add(android.R.id.content, loginFragment)
.commit();
} else {
// Or set the fragment from restored state info
loginFragment = (LoginFragment) getSupportFragmentManager()
.findFragmentById(android.R.id.content);
}
}
}
This is the LoginFragment:
public class LoginFragment extends Fragment {
private LoginButton authButton;
private static final String TAG = "MainFragment";
private UiLifecycleHelper uiHelper;
private Session.StatusCallback callback = new Session.StatusCallback() {
#Override
public void call(Session session, SessionState state, Exception exception) {
onSessionStateChange(session, state, exception);
}
};
#Override
public View onCreateView(LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.login_fragment,
container, false);
authButton=(LoginButton)view.findViewById(R.id.login_button);
authButton.setFragment(this);
return view;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
uiHelper = new UiLifecycleHelper(getActivity(), callback);
uiHelper.onCreate(savedInstanceState);
}
#Override
public void onResume() {
super.onResume();
Session session = Session.getActiveSession();
if (session != null &&
(session.isOpened() || session.isClosed()) ) {
onSessionStateChange(session, session.getState(), null);
}
uiHelper.onResume();
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
uiHelper.onActivityResult(requestCode, resultCode, data);
}
#Override
public void onPause() {
super.onPause();
uiHelper.onPause();
}
#Override
public void onDestroy() {
super.onDestroy();
uiHelper.onDestroy();
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
uiHelper.onSaveInstanceState(outState);
}
private void onSessionStateChange(Session session, SessionState state, Exception exception) {
if (state.isOpened()) {
Log.i(TAG, "Logged in...");
startActivity(new Intent(getActivity(), ContentSliderActivity.class));
} else if (state.isClosed()) {
Log.i(TAG, "Logged out...");
}
}
}
And here is the activity which you suppose to have a content slider, this will be displayed after the user has successfully logged in, here I call it ContentSliderActivity:
public class ContentSliderActivity extends FragmentActivity{
.....................
}
I was able to discern the problem was in the MainFragment on CreateView method. For some reason, if I removed this override, I didn't have a problem where my content sliderr gets removed. No idea why, but instead of adding the following lines: LoginButton authButton = (LoginButton) findViewById(R.id.authButton); authButton.setFragment(this); in the onViewCreate method of MainFragment, I simply moved them to the onCreate method in MainActivity. Seems to have worked for me, although not entirely sure what the original problem was honestly.
Related
I know this question has been asked a million times before, but I have tried all of the solutions I can find, and still doesn't work. I have tried calling "this" for context, I have tried getActivity, I have tried getContext(), but nothing seems to work for this fragment in particular. The same code does work in a different fragment tho, which is why I am really confused. Any help appreciated.
My LoginFragment, my issue can be found in setReservations():
public class LoginFragment extends Fragment {
LoginButton loginButton;
TextView nameText;
ProfileTracker mProfileTracker;
DBHandler dbHandler;
Context context;
ArrayList<Reservation> reservations;
ListView lv;
Profile fbProfile;
CallbackManager callbackManager;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.login, container, false);
callbackManager = CallbackManager.Factory.create();
dbHandler = new DBHandler(getContext(), null, null, 1);
context = getActivity();
loginButton = (LoginButton) view.findViewById(R.id.login_button);
nameText = (TextView) view.findViewById(R.id.users_name);
loginButton.setReadPermissions("email");
setmProfileTracker();
mProfileTracker.startTracking();
// If using in a fragment
loginButton.setFragment(this);
// Other app specific specialization
// Callback registration
loginButton.registerCallback(callbackManager, new FacebookCallback<LoginResult>() {
#Override
public void onSuccess(LoginResult loginResult) {
}
#Override
public void onCancel() {
// App code
}
#Override
public void onError(FacebookException exception) {
// App code
}
});
return view;
}
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
fbProfile = Profile.getCurrentProfile();
if (fbProfile != null)
{
updateUI();
}
getActivity().setTitle("My page");
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
callbackManager.onActivityResult(requestCode,resultCode,data);
}
private void setmProfileTracker()
{
mProfileTracker = new ProfileTracker() {
#Override
protected void onCurrentProfileChanged(Profile profile, Profile profile2) {
updateUI(); //this is the third piece of code I will discuss below
}
};
}
private void updateUI() {
boolean enableButtons = AccessToken.getCurrentAccessToken() != null;
if (fbProfile == null) {
fbProfile = Profile.getCurrentProfile();
Log.e("Profile", "null");
}
if (enableButtons && fbProfile != null) {
Log.e("Access Token", AccessToken.getCurrentAccessToken().toString());
nameText.setText(fbProfile.getName());
}
}
private void setReservations()
{
reservations = dbHandler.userReservationToArrayList(parseLong(fbProfile.getId()));
lv = (ListView) getView().findViewById(R.id.reservations);
ArrayAdapter<Review> arrayAdapter = new ArrayAdapter<Review>(
context,
android.R.layout.simple_list_item_1,
reservations);
lv.setAdapter(arrayAdapter);
}
}
EDIT: Code format
Your Array Adapter is of Type Review
ArrayAdapter<Review> arrayAdapter
But Your passing :
ArrayList<Reservation> reservations
Change it to ArrayAdapter<Reservation>
You probably should learn more about generics and type safety.
Your reservations has type ArrayList<Reservation> but you try to create ArrayAdapter<Review> which is wrong. Change it to ArrayAdapter<Reservation>.
You should pass an array of Review not an array of Reservation. So change your adapter to ArrayAdapter<Reservation>.
Also, at the moment you call the getActivity() method inside the onCreateView() method your Activity is not created yet, which can cause an error.
To solve this issue, you should to move this line to your onActivityCreated() :
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
context = getActivity();
}
I have a MainActivity, and I want to attach a fragment with 3 buttons in it to that activity. On clicking button 1 it should replace this fragment with another fragment. But when I change orientation the current fragment and the old fragment are both getting attached. Can someone help me solve this ?
Following is my MainActivity.java:
public class MainActivity extends AppCompatActivity implements OnButtonsClickListener {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getSupportFragmentManager().beginTransaction()
.add(R.id.mainactivity, new FragmentHomepage(), "aboutMe")
.commit();
}
#Override
public void button1Action() {
FragmentAboutMe fragmentAboutMe=new FragmentAboutMe();
getSupportFragmentManager().beginTransaction()
.add(R.id.mainactivity,fragmentAboutMe)
.commit();
}
#Override
public void button2Action() {
Intent intent =new Intent(this,ActivityMasterDetail.class);
startActivity(intent);
}
#Override
public void button3Action() {
Intent intent =new Intent(this,ActivityViewPager.class);
startActivity(intent);
}
}
This is my FragmentHomepage.java:
public class FragmentHomepage extends Fragment {
private static final String ARG_SECTION_NUMBER ="section number";
OnButtonsClickListener onButtonsClickListener;
public static FragmentHomepage newInstance(int sectionNumber){
FragmentHomepage fragmentHomepage=new FragmentHomepage();
Bundle args=new Bundle();
args.putInt(ARG_SECTION_NUMBER,sectionNumber);
fragmentHomepage.setArguments(args);
return fragmentHomepage;
}
public FragmentHomepage(){}
#Override
public void onAttach(Context context) {
super.onAttach(context);
try {
onButtonsClickListener= (OnButtonsClickListener) context;
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
final Button button1= (Button) getActivity().findViewById(R.id.aboutMe);
final Button button2= (Button) getActivity().findViewById(R.id.task2);
final Button button3= (Button) getActivity().findViewById(R.id.task3);
button1.setOnClickListener(new Button.OnClickListener() {
#Override
public void onClick(View v) {
onButtonsClickListener.button1Action();
}
});
button2.setOnClickListener(new Button.OnClickListener() {
#Override
public void onClick(View v) {
onButtonsClickListener.button2Action();
}
});
button3.setOnClickListener(new Button.OnClickListener() {
#Override
public void onClick(View v) {
onButtonsClickListener.button3Action();
}
});
}
#Nullable
#Override
public View onCreateView(final LayoutInflater inflater,
ViewGroup container,
Bundle savedInstanceState) {
View rootView=null;
rootView =inflater.inflate(R.layout.fragment_homepage,container,false);
return rootView;
}
}
and my second activity is as follows
(in FragmentAboutMe.java):
public class FragmentAboutMe extends Fragment {
int counters=0;
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(savedInstanceState==null)counters=0;
else counters=savedInstanceState.getInt("count");
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt("count",13);
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater,
ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_aboutme,container,false);
}
}
In your onCreate() method put a check whether the fragment is already present. Android automatically restores the fragment manager state upon orientation change and hence upon orientation change, the fragment which you added on button click would automatically be added. Thus if you will add new fragment in onCreate() without check, this would result in adding the 2 fragments. This is causing the issue.
FragmentManager fm = getSupportFragmentManager();
if (fm.findfragmentById(R.id.mainactivity) == null) {
FragmentHomepage fragment = new FragmentHomepage ();
fm.beginTransaction().add (R.id.mainactivity, fragment).commit();
}
You would want to save the state. You would need to extend Fragment to store the objects that need saving.
Override the onConfigurationChanged(..) and it should work out.
When I had to handle screen orientation changes, I recall having used this reference [link].
There are two ways.
Either you need to save the state in the bundle in onSaveInstanceState() method. Also save the states of fragment.
When activity recreate itself then check the value of bundle inside onCreate() or onRestoreInstanceState() method. Now initialize all the objects as before.
Else
set the value of activity inside manifest file as
android:configChanges="layoutDirection|orientation|screenLayout|screenSize"
and override the method.
#Override
public void onConfigurationChanged(Configuration newConfig) {
}
Second technique will not allow the activity to restart on orientation change.
I have a problem that I don't know how to solve. How do you hide a toolbar in a specific fragment, I have already been searching around on the internet and what I found was communicating activity and fragment would solve it. But it doesn't work for me at all, here is my code:
main_activity:
public class MainActivity extends ActionBarActivity implements like_frag.OnHideToolbar{
....
public void onHidingToolbar(int position){
Toolbar toolbar = (Toolbar)findViewById(R.id.toolbar);
if(toolbar == null){
getSupportActionBar().hide();
}else{
getSupportActionBar().hide();
}
}
like_frag.java
public class like_frag extends Fragment {
OnHideToolbar mCallback;
Toolbar toolbar;
public interface OnHideToolbar {
public void onHidingToolbar(int position);
}
public void onAttach(Activity activity){
try{
mCallback = (OnHideToolbar) activity;
}catch(ClassCastException e){
throw new ClassCastException(activity.toString() + "error implementing");
}
}
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
View rootView = inflater.inflate(R.layout.swipefrag, container, false);
toolbar = (Toolbar)getActivity().findViewById(R.id.toolbar);
return rootView;
}
#Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
}
}
thanks in advance.
I have a drawer inside the toolbar.
Put this code in fragment in which you want to hide toolbar...
#Override
public void onResume() {
super.onResume();
((AppCompatActivity)getActivity()).getSupportActionBar().hide();
}
#Override
public void onStop() {
super.onStop();
((AppCompatActivity)getActivity()).getSupportActionBar().show();
}
In the fragment's onCreate method call:
((AppCompatActivity) getActivity()).getSupportActionBar().hide();
Replace AppCompateActivity with the activity class you used.
Edited:
You could simply use the onResume method to call hide() and the onStop method to call show() as suggested in some of the comments.
#Override
public void onResume() {
super.onResume();
((AppCompatActivity)getActivity()).getSupportActionBar().hide();
}
#Override
public void onStop() {
super.onStop();
((AppCompatActivity)getActivity()).getSupportActionBar().show();
}
If you are using the new Navigation Component, add this while setting up the toolbar
navController.addOnDestinationChangedListener(new NavController.OnDestinationChangedListener() {
#Override
public void onDestinationChanged(#NonNull NavController controller,
#NonNull NavDestination destination, #Nullable Bundle arguments) {
if(destination.getId() == R.id.full_screen_destination) {
toolbar.setVisibility(View.GONE);
bottomNavigationView.setVisibility(View.GONE);
} else {
toolbar.setVisibility(View.VISIBLE);
bottomNavigationView.setVisibility(View.VISIBLE);
}
}
});
And for Kotlin, you can do the following:
navController.addOnDestinationChangedListener { _, destination, _ ->
if(destination.getId() == R.id.full_screen_destination) {
toolbar.setVisibility(View.GONE)
bottomNavigationView.setVisibility(View.GONE);
} else {
toolbar.setVisibility(View.VISIBLE)
bottomNavigationView.setVisibility(View.VISIBLE);
}
}
Create an interface in the fragment and use it to tell the parent activity to hide the toolbar.
Add these lines to your fragment:
private OnEventListener listener;
public interface OnEventListener {
void hideToolbar() ;
}
public void setOnEventListener(OnEventListener listener) {
this.listener = listener;
}
After creating your fragment in the main activity add:
myFragment.setOnEventListener(new MyFragment.OnEventListener() {
#Override
public void hideToolbar() {
getSupportActionBar().hide();
}
});
Whenever you need to hide the toolbar execute:
listener.hideToolbar();
from inside your fragment.
Just add these methods to the fragment where you want to diable the toolbar ,and also in the fragment's onStop() make it visible again.
#Override
public void onResume() {
super.onResume();
((AppCompatActivity)getActivity()).getSupportActionBar().hide();
}
#Override
public void onStop() {
super.onStop();
((AppCompatActivity)getActivity()).getSupportActionBar().show();
}
in kotlin hide and show supportActionBar as follows:
override fun onResume() {
super.onResume()
(activity as AppCompatActivity).supportActionBar?.hide()
}
override fun onStop() {
super.onStop()
(activity as AppCompatActivity).supportActionBar?.show()
}
and if you want to have your own custom toolbar, in OncreateView set:
//your Custom toolbar in xml
val toolbar = binding.toolbar
(activity as AppCompatActivity).setSupportActionBar(toolbar)
Simply use supportActionBar?.hide() or supportActionBar?.show().
If you are using NavigationController:
navController.addOnDestinationChangedListener { controller, destination, arguments ->
if (destination.id == R.id.loginSuccessFragment) {
supportActionBar?.hide()
} else {
supportActionBar?.show()
}
}
Put this code in fragment in which you want to hide toolbar...
Add this( ((AppCompatActivity)getActivity()).getSupportActionBar().hide();) in onCreateView or in onResume.
and do this in onDestroy()
#Override
public void onDestroy() {
super.onDestroy();
((AppCompatActivity)getActivity()).getSupportActionBar().show();}
use getSupportActionBar().hide(); and getSupportActionBar().show(); in lifeCycle methods
You can try it.
#Override
public void onDestinationChanged(#NonNull NavController controller, #NonNull NavDestination destination, #Nullable Bundle arguments) {
if (destination.getId() == R.id.nav_dashboard){
if (toolbar !=null){
toolbar.setVisibility(View.GONE);
}
}else {
toolbar.setVisibility(View.VISIBLE);
}
}
I have two classes that is for Facebook login, and it works.
I can press the Loginbutton, write in my username/password and it will log me in, BUT the thing is that I will come back to the Main-class (startsite) with my logo picture and so on.
What I want to do is, when you pressed the loginbutton and logged in, you will be redirected to a new Activity
Here is my 2 Classes you can see, but where do I do the redirect?
Read on StackOverflow about the "onComplete" but I cant make it, think its because I use SDK 3.5.2 and not 2.0. So How can i make a redirect to a new Activity after success login.
My MainActivity:
public class SocialLoginActivity extends FragmentActivity {
private SocialLoginFragment socialLoginFragment;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState == null) {
// Add the fragment on initial activity setup
socialLoginFragment = new SocialLoginFragment();
getSupportFragmentManager().beginTransaction()
.add(android.R.id.content, socialLoginFragment).commit();
} else {
// Or set the fragment from restored state info
socialLoginFragment = (SocialLoginFragment) getSupportFragmentManager()
.findFragmentById(android.R.id.content);
}
}
}
And here is the FragmentClass:
public class SocialLoginFragment extends Fragment {
private static final String TAG = "SocialLoginFragment";
private UiLifecycleHelper uiHelper;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.activity_social_login, container,
false);
LoginButton authButton = (LoginButton) view
.findViewById(R.id.authButton);
authButton.setFragment(this);
// authButton.setReadPermissions(Arrays.asList("name", "user_birthday",
// "friends_birthday"));
return view;
}
private void onSessionStateChange(Session session, SessionState state,
Exception exception) {
if (state.isOpened()) {
Log.i(TAG, "Logged in...");
} else if (state.isClosed()) {
Log.i(TAG, "Logged out...");
}
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
uiHelper = new UiLifecycleHelper(getActivity(), callback);
uiHelper.onCreate(savedInstanceState);
}
private Session.StatusCallback callback = new Session.StatusCallback() {
#Override
public void call(Session session, SessionState state,
Exception exception) {
onSessionStateChange(session, state, exception);
}
};
#Override
public void onResume() {
super.onResume();
Session session = Session.getActiveSession();
if (session != null && (session.isOpened() || session.isClosed())) {
onSessionStateChange(session, session.getState(), null);
}
uiHelper.onResume();
}
#Override
public void onPause() {
super.onPause();
uiHelper.onPause();
}
#Override
public void onDestroy() {
super.onDestroy();
uiHelper.onDestroy();
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
uiHelper.onSaveInstanceState(outState);
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
uiHelper.onActivityResult(requestCode, resultCode, data);
}
}
EDIT
The error you see is because a Fragment is not a Context object. You need to get the parent activity, something like:
Intent intent = new Intent(getActivity(), MainActivity.class);
I've been looking at the android documentation and diverse sources around the web, but I can't seem to get my app to retain data across activity re-creation (like pressing the back button to exit and then entering the app from the icon or the task manager).
I have 3 classes: the main one (extends FragmentActivity), a fragment and a class that should run in the background while the app is not active, and after the activity is created, it needs to update the fragment.
Here are my sources:
Main
public class Main extends FragmentActivity {
private FragmentExample mFragment;
private BackgroundClass bgClass = new BackgroundClass();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
manageFragment(getSupportFragmentManager());
}
protected void manageFragment(FragmentManager fm) {
mFragment = (FragmentExample) fm.findFragmentByTag("TAG");
if (mFragment == null) {
mFragment = new mFragment();
fm.beginTransaction().add(mFragment,"TAG").commit();
}
else fm.beginTransaction().attach(mFragment).commit();
}
#Override
protected void onStart() {
super.onStart();
mFragment = bgClass.update(mFragment);
}
}
Fragment
public class FragmentExample extends Fragment {
//declare variables
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment, container, false);
//initialize variables from View
return view;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
//more initializations
}
}
BackgroundClass
public class BackgroundClass extends SomeOtherClass {
//declare variables
protected FragmentExample update(final FragmentExample fExample) {
//update listeners, variables, etc
return fExample;
}
//Overridden methods from SomeOtherClass
}
Currently it updates the data only when I change the configuration (i.e. rotating the screen). Any sort of help would be great.