Login Improved; Still Bug when re-login

This commit is contained in:
Caesar2011
2018-12-19 01:08:39 +01:00
parent fd18e4b61a
commit 4d22613672
7 changed files with 137 additions and 271 deletions

View File

@@ -69,8 +69,6 @@ public class MainActivity extends AppCompatActivity
private static final int FRAGMENT_STARTUP = 0; private static final int FRAGMENT_STARTUP = 0;
private static final int FRAGMENT_MODULES = 1; private static final int FRAGMENT_MODULES = 1;
private static final int FRAGMENT_MODULES_DETAILS = 2; private static final int FRAGMENT_MODULES_DETAILS = 2;
@Deprecated
private static final int FRAGMENT_LOGIN = 3;
private static final int FRAGMENT_SCHEDULE = 4; private static final int FRAGMENT_SCHEDULE = 4;
private static final int FRAGMENT_CANTEENS = 5; private static final int FRAGMENT_CANTEENS = 5;
private static final int FRAGMENT_CANTEENS_DETAILS = 6; private static final int FRAGMENT_CANTEENS_DETAILS = 6;
@@ -129,7 +127,7 @@ public class MainActivity extends AppCompatActivity
if (!mAccountManager.hasAccounts(AccountGeneral.ACCOUNT_TYPE)) { if (!mAccountManager.hasAccounts(AccountGeneral.ACCOUNT_TYPE)) {
desiredPage = getDefaultFragmentAfterLogout(); desiredPage = getDefaultFragmentAfterLogout();
desiredData = ""; desiredData = "";
mAccountManager.getTokenForAccountCreateIfNeeded(AccountGeneral.ACCOUNT_TYPE, AccountGeneral.AUTHTOKEN_TYPE_KVV); mAccountManager.getTokenByType(AccountGeneral.ACCOUNT_TYPE, AccountGeneral.AUTHTOKEN_TYPE_KVV, null);
updateNavigation(); updateNavigation();
changeFragment(desiredPage, desiredData); changeFragment(desiredPage, desiredData);
} else { } else {
@@ -367,7 +365,7 @@ public class MainActivity extends AppCompatActivity
setRefreshFailedBanner(false); setRefreshFailedBanner(false);
updateNavigation(); updateNavigation();
changeFragment(getDefaultFragmentAfterLogout()); changeFragment(getDefaultFragmentAfterLogout());
mAccountManager.getTokenForAccountCreateIfNeeded(AccountGeneral.ACCOUNT_TYPE, AccountGeneral.AUTHTOKEN_TYPE_KVV); mAccountManager.getTokenByType(AccountGeneral.ACCOUNT_TYPE, AccountGeneral.AUTHTOKEN_TYPE_KVV, null);
} }
private void toLoginState(String fullName, String email, int newFragment, boolean onlineMode) { private void toLoginState(String fullName, String email, int newFragment, boolean onlineMode) {
@@ -526,7 +524,7 @@ public class MainActivity extends AppCompatActivity
if (drawer.isDrawerOpen(GravityCompat.START)) { if (drawer.isDrawerOpen(GravityCompat.START)) {
drawer.closeDrawer(GravityCompat.START); drawer.closeDrawer(GravityCompat.START);
} }
mAccountManager.getTokenForAccountCreateIfNeeded(AccountGeneral.ACCOUNT_TYPE, AccountGeneral.AUTHTOKEN_TYPE_KVV); mAccountManager.getTokenByType(AccountGeneral.ACCOUNT_TYPE, AccountGeneral.AUTHTOKEN_TYPE_KVV, null);
}); });
} }

View File

@@ -1,111 +0,0 @@
package de.sebse.fuplanner.fragments;
import android.app.ProgressDialog;
import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import de.sebse.fuplanner.R;
import de.sebse.fuplanner.tools.MainActivityListener;
import de.sebse.fuplanner.tools.logging.Logger;
/**
* A simple {@link Fragment} subclass.
* Use the {@link LoginFragment#newInstance} factory method to
* create an instance of this fragment.
*/
public class LoginFragment extends Fragment {
private final Logger log = new Logger(this);
@Nullable private MainActivityListener mActivityListener;
public LoginFragment() {
// Required empty public constructor
}
/**
* Use this factory method to create a new instance of
* this fragment using the provided parameters.
*
* @return A new instance of fragment LoginFragment.
*/
public static LoginFragment newInstance() {
LoginFragment fragment = new LoginFragment();
Bundle args = new Bundle();
fragment.setArguments(args);
return fragment;
}
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View v = inflater.inflate(R.layout.fragment_login, container, false);
//if (mActivityListener != null && mActivityListener.getKVV().account().isOfflineStoredAvailable()) {
// Button offline_btn = v.findViewById(R.id.btn_offline);
// offline_btn.setVisibility(View.VISIBLE);
// offline_btn.setText(v.getResources().getString(R.string.enter_offline_mode, mActivityListener.getKVV().modules().list().getUsername()));
//offline_btn.setOnClickListener(v1 -> mActivityListener.getKVV().account().doOfflineLogin());
//}
View btn_login = v.findViewById(R.id.btn_login);
btn_login.setOnClickListener(view -> {
final ProgressDialog progressDialog = new ProgressDialog(LoginFragment.this.getContext(),
R.style.FUTheme_Dialog);
progressDialog.setIndeterminate(true);
progressDialog.setMessage("Authenticating...");
progressDialog.show();
EditText input_usr = ((View) view.getParent()).findViewById(R.id.input_username);
EditText input_pwd = ((View) view.getParent()).findViewById(R.id.input_password);
String username = input_usr.getText().toString();
String password = input_pwd.getText().toString();
mActivityListener.getKVV().account().doOnlineLogin(username, password, success -> {
progressDialog.dismiss();
mActivityListener.getGoogleAuth().setLoginState(username, password);
input_usr.setError(null);
input_pwd.setError(null);
}, error -> {
progressDialog.dismiss();
// Invalid password
if (mActivityListener != null) {
if (error.getCode() == 100131) {
mActivityListener.showToast(R.string.invalid_credentials);
input_usr.setError(input_usr.getResources().getString(R.string.invalid_credentials));
input_pwd.setError(input_pwd.getResources().getString(R.string.invalid_credentials));
} else {
mActivityListener.showToast(v.getResources().getString(R.string.error_occurred_code, error.getCode()));
}
}
log.e("Error on KVV login!", error);
});
});
return v;
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof MainActivityListener) {
mActivityListener = (MainActivityListener) context;
mActivityListener.onTitleTextChange(R.string.log_in);
} else
throw new RuntimeException(context.toString() + " must implement MainActivityListener");
}
@Override
public void onDetach() {
super.onDetach();
mActivityListener = null;
}
}

View File

@@ -55,6 +55,7 @@ public class Login extends HTTPService {
LoginToken.load(mListener.getAccountManager(), token -> { LoginToken.load(mListener.getAccountManager(), token -> {
boolean result = setToken(token, true); boolean result = setToken(token, true);
mLoginPending = false; mLoginPending = false;
log.d("loginToken", token != null ? token.toString() : null);
callback.run(result); callback.run(result);
}); });
} }
@@ -129,15 +130,18 @@ public class Login extends HTTPService {
if (!isFirst) if (!isFirst)
return; return;
CustomAccountManager manager = mListener.getAccountManager(); CustomAccountManager manager = mListener.getAccountManager();
manager.invalidate(AccountGeneral.ACCOUNT_TYPE, AccountGeneral.AUTHTOKEN_TYPE_KVV); manager.doInvalidateToken(AccountGeneral.ACCOUNT_TYPE, AccountGeneral.AUTHTOKEN_TYPE_KVV, ignored -> {
reset(); reset();
restoreOnlineLogin(isRestored -> { log.d("try restore", ignored);
if (isRestored) restoreOnlineLogin(isRestored -> {
testLoginToken(mRefreshCallbacks::responseResponse, mRefreshCallbacks::responseError); log.d("restore", isRestored, mToken);
else { if (isRestored)
logout(true); testLoginToken(mRefreshCallbacks::responseResponse, mRefreshCallbacks::responseError);
mRefreshCallbacks.responseError(new NetworkError(100180, 403, "Re-login failed!")); else {
} logout(true);
mRefreshCallbacks.responseError(new NetworkError(100180, 403, "Re-login failed!"));
}
});
}); });
/* mListener.getCredentials(credentials -> { /* mListener.getCredentials(credentials -> {
doOnlineLogin(credentials.getUsername(), credentials.getPassword(), doOnlineLogin(credentials.getUsername(), credentials.getPassword(),

View File

@@ -17,18 +17,10 @@ import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.provider.ContactsContract; import android.provider.ContactsContract;
import android.text.TextUtils; import android.text.TextUtils;
import android.view.KeyEvent;
import android.view.View; import android.view.View;
import android.view.View.OnClickListener;
import android.view.inputmethod.EditorInfo; import android.view.inputmethod.EditorInfo;
import android.widget.ArrayAdapter;
import android.widget.AutoCompleteTextView;
import android.widget.Button; import android.widget.Button;
import android.widget.EditText; import android.widget.EditText;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import de.sebse.fuplanner.R; import de.sebse.fuplanner.R;
@@ -51,7 +43,7 @@ public class FUAuthenticatorActivity extends AccountAuthenticatorActivity implem
UserLoginTask mAuthTask = null; UserLoginTask mAuthTask = null;
// UI references. // UI references.
private AutoCompleteTextView mEmailView; private EditText mEmailView;
EditText mPasswordView; EditText mPasswordView;
private View mProgressView; private View mProgressView;
private View mLoginFormView; private View mLoginFormView;
@@ -76,28 +68,20 @@ public class FUAuthenticatorActivity extends AccountAuthenticatorActivity implem
setContentView(R.layout.activity_fu_authenticator); setContentView(R.layout.activity_fu_authenticator);
// Set up the login form. // Set up the login form.
mEmailView = (AutoCompleteTextView) findViewById(R.id.email); mEmailView = findViewById(R.id.input_username);
populateAutoComplete(); populateAutoComplete();
mPasswordView = (EditText) findViewById(R.id.password); mPasswordView = findViewById(R.id.input_password);
mPasswordView.setOnEditorActionListener(new TextView.OnEditorActionListener() { mPasswordView.setOnEditorActionListener((textView, id, keyEvent) -> {
@Override if (id == EditorInfo.IME_ACTION_DONE || id == EditorInfo.IME_NULL) {
public boolean onEditorAction(TextView textView, int id, KeyEvent keyEvent) { attemptLogin();
if (id == EditorInfo.IME_ACTION_DONE || id == EditorInfo.IME_NULL) { return true;
attemptLogin();
return true;
}
return false;
} }
return false;
}); });
Button mEmailSignInButton = (Button) findViewById(R.id.email_sign_in_button); Button mEmailSignInButton = findViewById(R.id.btn_login);
mEmailSignInButton.setOnClickListener(new OnClickListener() { mEmailSignInButton.setOnClickListener(view -> attemptLogin());
@Override
public void onClick(View view) {
attemptLogin();
}
});
mLoginFormView = findViewById(R.id.login_form); mLoginFormView = findViewById(R.id.login_form);
mProgressView = findViewById(R.id.login_progress); mProgressView = findViewById(R.id.login_progress);
@@ -217,32 +201,25 @@ public class FUAuthenticatorActivity extends AccountAuthenticatorActivity implem
// On Honeycomb MR2 we have the ViewPropertyAnimator APIs, which allow // On Honeycomb MR2 we have the ViewPropertyAnimator APIs, which allow
// for very easy animations. If available, use these APIs to fade-in // for very easy animations. If available, use these APIs to fade-in
// the progress spinner. // the progress spinner.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) { int shortAnimTime = getResources().getInteger(android.R.integer.config_shortAnimTime);
int shortAnimTime = getResources().getInteger(android.R.integer.config_shortAnimTime);
mLoginFormView.setVisibility(show ? View.GONE : View.VISIBLE); mLoginFormView.setVisibility(show ? View.GONE : View.VISIBLE);
mLoginFormView.animate().setDuration(shortAnimTime).alpha( mLoginFormView.animate().setDuration(shortAnimTime).alpha(
show ? 0 : 1).setListener(new AnimatorListenerAdapter() { show ? 0 : 1).setListener(new AnimatorListenerAdapter() {
@Override @Override
public void onAnimationEnd(Animator animation) { public void onAnimationEnd(Animator animation) {
mLoginFormView.setVisibility(show ? View.GONE : View.VISIBLE); mLoginFormView.setVisibility(show ? View.GONE : View.VISIBLE);
} }
}); });
mProgressView.setVisibility(show ? View.VISIBLE : View.GONE); mProgressView.setVisibility(show ? View.VISIBLE : View.GONE);
mProgressView.animate().setDuration(shortAnimTime).alpha( mProgressView.animate().setDuration(shortAnimTime).alpha(
show ? 1 : 0).setListener(new AnimatorListenerAdapter() { show ? 1 : 0).setListener(new AnimatorListenerAdapter() {
@Override @Override
public void onAnimationEnd(Animator animation) { public void onAnimationEnd(Animator animation) {
mProgressView.setVisibility(show ? View.VISIBLE : View.GONE); mProgressView.setVisibility(show ? View.VISIBLE : View.GONE);
} }
}); });
} else {
// The ViewPropertyAnimator APIs are not available, so simply show
// and hide the relevant UI components.
mProgressView.setVisibility(show ? View.VISIBLE : View.GONE);
mLoginFormView.setVisibility(show ? View.GONE : View.VISIBLE);
}
} }
@Override @Override
@@ -264,14 +241,6 @@ public class FUAuthenticatorActivity extends AccountAuthenticatorActivity implem
@Override @Override
public void onLoadFinished(Loader<Cursor> cursorLoader, Cursor cursor) { public void onLoadFinished(Loader<Cursor> cursorLoader, Cursor cursor) {
List<String> emails = new ArrayList<>();
cursor.moveToFirst();
while (!cursor.isAfterLast()) {
emails.add(cursor.getString(ProfileQuery.ADDRESS));
cursor.moveToNext();
}
addEmailsToAutoComplete(emails);
} }
@Override @Override
@@ -279,15 +248,6 @@ public class FUAuthenticatorActivity extends AccountAuthenticatorActivity implem
} }
private void addEmailsToAutoComplete(List<String> emailAddressCollection) {
//Create adapter to tell the AutoCompleteTextView what to show in its dropdown list.
ArrayAdapter<String> adapter =
new ArrayAdapter<>(FUAuthenticatorActivity.this,
android.R.layout.simple_dropdown_item_1line, emailAddressCollection);
mEmailView.setAdapter(adapter);
}
private interface ProfileQuery { private interface ProfileQuery {
String[] PROJECTION = { String[] PROJECTION = {

View File

@@ -2,7 +2,6 @@ package de.sebse.fuplanner.tools;
import android.accounts.Account; import android.accounts.Account;
import android.accounts.AccountManager; import android.accounts.AccountManager;
import android.accounts.AccountManagerFuture;
import android.accounts.AuthenticatorException; import android.accounts.AuthenticatorException;
import android.accounts.OperationCanceledException; import android.accounts.OperationCanceledException;
import android.os.Build; import android.os.Build;
@@ -12,6 +11,7 @@ import java.io.IOException;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import de.sebse.fuplanner.MainActivity; import de.sebse.fuplanner.MainActivity;
import de.sebse.fuplanner.services.KVV.Login;
import de.sebse.fuplanner.tools.logging.Logger; import de.sebse.fuplanner.tools.logging.Logger;
public class CustomAccountManager { public class CustomAccountManager {
@@ -28,13 +28,10 @@ public class CustomAccountManager {
private void doInvalidateToken(String accountType, String authTokenType) { public void doInvalidateTokenSync(String accountType, String authTokenType) {
//String token = mAccountManager.blockingGetAuthToken();
Account account = mAccountManager.getAccountsByType(accountType)[0]; Account account = mAccountManager.getAccountsByType(accountType)[0];
String token = null;
try { try {
token = mAccountManager.blockingGetAuthToken(account, authTokenType, true); String token = mAccountManager.blockingGetAuthToken(account, authTokenType, true);
//log.d("hihihi", accountType, authTokenType, token);
mAccountManager.invalidateAuthToken(accountType, token); mAccountManager.invalidateAuthToken(accountType, token);
} catch (AuthenticatorException e) { } catch (AuthenticatorException e) {
e.printStackTrace(); e.printStackTrace();
@@ -45,6 +42,28 @@ public class CustomAccountManager {
} }
} }
public void doInvalidateToken(String accountType, String authTokenType, Login.BooleanInterface callback) {
Account account = mAccountManager.getAccountsByType(accountType)[0];
mAccountManager.getAuthToken(account, authTokenType, null, true, accountManagerFuture -> {
try {
Bundle bnd = accountManagerFuture.getResult();
String token = bnd.getString(AccountManager.KEY_AUTHTOKEN);
mAccountManager.invalidateAuthToken(accountType, token);
if (callback != null)
callback.run(true);
return;
} catch (AuthenticatorException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (OperationCanceledException e) {
e.printStackTrace();
}
if (callback != null)
callback.run(false);
}, null);
}
public void deleteAccount(String accountType) { public void deleteAccount(String accountType) {
Account[] accounts = mAccountManager.getAccountsByType(accountType); Account[] accounts = mAccountManager.getAccountsByType(accountType);
int[] count = {accounts.length}; int[] count = {accounts.length};
@@ -57,34 +76,14 @@ public class CustomAccountManager {
} }
} }
public void getTokenForAccountCreateIfNeeded(String accountType, String authTokenType) { public void getTokenByType(String accountType, String authTokenType, @Nullable StringInterface callback) {
MainActivity activity = this.mActivityInterface.get();
if (activity != null) {
final AccountManagerFuture<Bundle> future = mAccountManager.getAuthTokenByFeatures(accountType, authTokenType, null, activity, null, null,
future1 -> {
Bundle bnd = null;
try {
bnd = future1.getResult();
final String authtoken = bnd.getString(AccountManager.KEY_AUTHTOKEN);
//showToast(((authtoken != null) ? "SUCCESS!\ntoken: " + authtoken : "FAIL"));
//log.d("udinic", "GetTokenForAccount Bundle is " + bnd);
} catch (Exception e) {
e.printStackTrace();
//showToast(e.getMessage());
}
}
, null);
}
}
public void getTokenByType(String accountType, String authTokenType, StringInterface callback) {
Account account = mAccountManager.getAccountsByType(accountType)[0]; Account account = mAccountManager.getAccountsByType(accountType)[0];
mAccountManager.getAuthToken(account, authTokenType, null, true, accountManagerFuture -> { mAccountManager.getAuthToken(account, authTokenType, null, true, accountManagerFuture -> {
try { try {
Bundle bnd = accountManagerFuture.getResult(); Bundle bnd = accountManagerFuture.getResult();
final String authtoken = bnd.getString(AccountManager.KEY_AUTHTOKEN); final String authtoken = bnd.getString(AccountManager.KEY_AUTHTOKEN);
callback.run(authtoken); if (callback != null)
callback.run(authtoken);
return; return;
} catch (AuthenticatorException e) { } catch (AuthenticatorException e) {
e.printStackTrace(); e.printStackTrace();
@@ -93,7 +92,8 @@ public class CustomAccountManager {
} catch (OperationCanceledException e) { } catch (OperationCanceledException e) {
e.printStackTrace(); e.printStackTrace();
} }
callback.run(null); if (callback != null)
callback.run(null);
}, null); }, null);
} }
@@ -127,8 +127,4 @@ public class CustomAccountManager {
public interface StringInterface { public interface StringInterface {
void run(@Nullable String string); void run(@Nullable String string);
} }
public void invalidate(String accountType, String authTokenType) {
mAccountManager.invalidateAuthToken(accountType, authTokenType);
}
} }

View File

@@ -19,60 +19,79 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="8dp" android:layout_marginBottom="8dp"
android:visibility="gone" /> android:visibility="gone" />
<ScrollView <ScrollView
android:id="@+id/login_form"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:id="@+id/login_form"
tools:context="de.sebse.fuplanner.services.newkvv.FUAuthenticatorActivity">
<LinearLayout <LinearLayout
android:id="@+id/email_login_form" android:orientation="vertical"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical"> android:paddingTop="56dp"
android:paddingLeft="24dp"
android:paddingRight="24dp">
<ImageView android:src="@mipmap/ic_launcher"
android:layout_width="128dp"
android:layout_height="128dp"
android:layout_marginBottom="24dp"
android:layout_gravity="center_horizontal"
android:contentDescription="@string/cd_ic_launcher"/>
<!-- Email Label -->
<com.google.android.material.textfield.TextInputLayout <com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<AutoCompleteTextView
android:id="@+id/email"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/prompt_email"
android:inputType="textEmailAddress"
android:maxLines="1"
android:singleLine="true" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<EditText
android:id="@+id/password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/prompt_password"
android:imeActionId="6"
android:imeActionLabel="@string/action_sign_in_short"
android:imeOptions="actionUnspecified"
android:inputType="textPassword"
android:maxLines="1"
android:singleLine="true" />
</com.google.android.material.textfield.TextInputLayout>
<Button
android:id="@+id/email_sign_in_button"
style="?android:textAppearanceSmall"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="16dp" android:layout_marginTop="8dp"
android:text="@string/action_sign_in" android:layout_marginBottom="8dp">
android:textStyle="bold" /> <EditText android:id="@+id/input_username"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textEmailAddress"
android:hint="@string/username"
android:autofillHints="username"
tools:targetApi="o" />
</com.google.android.material.textfield.TextInputLayout>
<!-- Password Label -->
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp">
<EditText android:id="@+id/input_password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textPassword"
android:hint="@string/password"
android:autofillHints="password"
tools:targetApi="o"/>
</com.google.android.material.textfield.TextInputLayout>
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/btn_login"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:layout_marginBottom="24dp"
android:padding="12dp"
android:text="@string/log_in"/>
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/btn_offline"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:layout_marginBottom="24dp"
android:padding="12dp"
android:visibility="gone"/>
</LinearLayout> </LinearLayout>
</ScrollView> </ScrollView>
</LinearLayout>
</LinearLayout>

View File

@@ -3,7 +3,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:fitsSystemWindows="true" android:fitsSystemWindows="true"
tools:context="de.sebse.fuplanner.fragments.LoginFragment"> tools:context="de.sebse.fuplanner.services.newkvv.FUAuthenticatorActivity">
<LinearLayout <LinearLayout
android:orientation="vertical" android:orientation="vertical"