Synced branch with master and fixed bugs specific to accessing of files in Nougat and above

This commit is contained in:
Vishan Seru 2017-10-21 15:37:02 +05:30
commit fe57db580e
52 changed files with 865 additions and 261 deletions

View file

@ -5,57 +5,66 @@ import android.app.ProgressDialog;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.support.annotation.ColorRes;
import android.support.annotation.NonNull;
import android.support.annotation.StringRes;
import android.support.v4.app.NavUtils;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatDelegate;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import butterknife.BindView;
import butterknife.ButterKnife;
import fr.free.nrw.commons.BuildConfig;
import fr.free.nrw.commons.CommonsApplication;
import fr.free.nrw.commons.R;
import fr.free.nrw.commons.WelcomeActivity;
import fr.free.nrw.commons.PageTitle;
import fr.free.nrw.commons.R;
import fr.free.nrw.commons.Utils;
import fr.free.nrw.commons.WelcomeActivity;
import fr.free.nrw.commons.contributions.ContributionsActivity;
import timber.log.Timber;
import static android.view.KeyEvent.KEYCODE_ENTER;
import static android.view.inputmethod.EditorInfo.IME_ACTION_DONE;
public class LoginActivity extends AccountAuthenticatorActivity {
public static final String PARAM_USERNAME = "fr.free.nrw.commons.login.username";
private SharedPreferences prefs = null;
private Button loginButton;
private EditText usernameEdit;
private EditText passwordEdit;
private EditText twoFactorEdit;
ProgressDialog progressDialog;
private LoginTextWatcher textWatcher = new LoginTextWatcher();
@BindView(R.id.loginButton) Button loginButton;
@BindView(R.id.signupButton) Button signupButton;
@BindView(R.id.loginUsername) EditText usernameEdit;
@BindView(R.id.loginPassword) EditText passwordEdit;
@BindView(R.id.loginTwoFactor) EditText twoFactorEdit;
@BindView(R.id.error_message_container) ViewGroup errorMessageContainer;
@BindView(R.id.error_message) TextView errorMessage;
private CommonsApplication app;
ProgressDialog progressDialog;
private AppCompatDelegate delegate;
private SharedPreferences prefs = null;
private LoginTextWatcher textWatcher = new LoginTextWatcher();
@Override
public void onCreate(Bundle savedInstanceState) {
setTheme(Utils.isDarkTheme(this) ? R.style.DarkAppTheme : R.style.LightAppTheme);
getDelegate().installViewFactory();
getDelegate().onCreate(savedInstanceState);
super.onCreate(savedInstanceState);
app = CommonsApplication.getInstance();
setContentView(R.layout.activity_login);
loginButton = (Button) findViewById(R.id.loginButton);
Button signupButton = (Button) findViewById(R.id.signupButton);
usernameEdit = (EditText) findViewById(R.id.loginUsername);
passwordEdit = (EditText) findViewById(R.id.loginPassword);
twoFactorEdit = (EditText) findViewById(R.id.loginTwoFactor);
ButterKnife.bind(this);
prefs = getSharedPreferences("fr.free.nrw.commons", MODE_PRIVATE);
@ -64,45 +73,17 @@ public class LoginActivity extends AccountAuthenticatorActivity {
twoFactorEdit.addTextChangedListener(textWatcher);
passwordEdit.setOnEditorActionListener(newLoginInputActionListener());
loginButton.setOnClickListener(this::performLogin);
signupButton.setOnClickListener(this::signUp);
loginButton.setOnClickListener(view -> performLogin());
signupButton.setOnClickListener(view -> signUp());
}
private class LoginTextWatcher implements TextWatcher {
@Override
public void beforeTextChanged(CharSequence charSequence, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence charSequence, int start, int count, int after) {
}
@Override
public void afterTextChanged(Editable editable) {
if (usernameEdit.getText().length() != 0 && passwordEdit.getText().length() != 0 &&
(BuildConfig.DEBUG || twoFactorEdit.getText().length() != 0 || twoFactorEdit.getVisibility() != View.VISIBLE)) {
loginButton.setEnabled(true);
} else {
loginButton.setEnabled(false);
}
}
}
private TextView.OnEditorActionListener newLoginInputActionListener() {
return (textView, actionId, keyEvent) -> {
if (loginButton.isEnabled()) {
if (actionId == IME_ACTION_DONE) {
performLogin(textView);
return true;
} else if ((keyEvent != null) && keyEvent.getKeyCode() == KEYCODE_ENTER) {
performLogin(textView);
return true;
}
}
return false;
};
@Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
getDelegate().onPostCreate(savedInstanceState);
}
@Override
protected void onResume() {
super.onResume();
if (prefs.getBoolean("firstrun", true)) {
@ -127,15 +108,104 @@ public class LoginActivity extends AccountAuthenticatorActivity {
usernameEdit.removeTextChangedListener(textWatcher);
passwordEdit.removeTextChangedListener(textWatcher);
twoFactorEdit.removeTextChangedListener(textWatcher);
delegate.onDestroy();
super.onDestroy();
}
private void performLogin(View view) {
@Override
protected void onStart() {
super.onStart();
delegate.onStart();
}
@Override
protected void onStop() {
super.onStop();
delegate.onStop();
}
@Override
protected void onPostResume() {
super.onPostResume();
getDelegate().onPostResume();
}
@Override
public void setContentView(View view, ViewGroup.LayoutParams params) {
getDelegate().setContentView(view, params);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
NavUtils.navigateUpFromSameTask(this);
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
@NonNull
public MenuInflater getMenuInflater() {
return getDelegate().getMenuInflater();
}
public void askUserForTwoFactorAuth() {
if (BuildConfig.DEBUG) {
twoFactorEdit.setVisibility(View.VISIBLE);
showMessageAndCancelDialog(R.string.login_failed_2fa_needed);
} else {
showMessageAndCancelDialog(R.string.login_failed_2fa_not_supported);
}
}
public void showMessageAndCancelDialog(@StringRes int resId) {
showMessage(resId, R.color.secondaryDarkColor);
progressDialog.cancel();
}
public void showSuccessAndDismissDialog() {
showMessage(R.string.login_success, R.color.primaryDarkColor);
progressDialog.dismiss();
}
public void emptySensitiveEditFields() {
passwordEdit.setText("");
twoFactorEdit.setText("");
}
public void startMainActivity() {
ContributionsActivity.startYourself(this);
finish();
}
private void performLogin() {
Timber.d("Login to start!");
LoginTask task = getLoginTask();
task.execute();
}
private void signUp() {
Intent intent = new Intent(this, SignupActivity.class);
startActivity(intent);
}
private TextView.OnEditorActionListener newLoginInputActionListener() {
return (textView, actionId, keyEvent) -> {
if (loginButton.isEnabled()) {
if (actionId == IME_ACTION_DONE) {
performLogin();
return true;
} else if ((keyEvent != null) && keyEvent.getKeyCode() == KEYCODE_ENTER) {
performLogin();
return true;
}
}
return false;
};
}
private LoginTask getLoginTask() {
return new LoginTask(
this,
@ -154,57 +224,33 @@ public class LoginActivity extends AccountAuthenticatorActivity {
return new PageTitle(username).getText();
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
NavUtils.navigateUpFromSameTask(this);
return true;
private void showMessage(@StringRes int resId, @ColorRes int colorResId) {
errorMessage.setText(getString(resId));
errorMessage.setTextColor(ContextCompat.getColor(this, colorResId));
errorMessageContainer.setVisibility(View.VISIBLE);
}
private AppCompatDelegate getDelegate() {
if (delegate == null) {
delegate = AppCompatDelegate.create(this, null);
}
return super.onOptionsItemSelected(item);
return delegate;
}
/**
* Called when Sign Up button is clicked.
* @param view View
*/
public void signUp(View view) {
Intent intent = new Intent(this, SignupActivity.class);
startActivity(intent);
}
private class LoginTextWatcher implements TextWatcher {
@Override
public void beforeTextChanged(CharSequence charSequence, int start, int count, int after) {
}
public void askUserForTwoFactorAuth() {
if (BuildConfig.DEBUG) {
twoFactorEdit.setVisibility(View.VISIBLE);
showUserToastAndCancelDialog(R.string.login_failed_2fa_needed);
} else {
showUserToastAndCancelDialog(R.string.login_failed_2fa_not_supported);
@Override
public void onTextChanged(CharSequence charSequence, int start, int count, int after) {
}
@Override
public void afterTextChanged(Editable editable) {
boolean enabled = usernameEdit.getText().length() != 0 && passwordEdit.getText().length() != 0 &&
(BuildConfig.DEBUG || twoFactorEdit.getText().length() != 0 || twoFactorEdit.getVisibility() != View.VISIBLE);
loginButton.setEnabled(enabled);
}
}
public void showUserToastAndCancelDialog(int resId) {
showUserToast(resId);
progressDialog.cancel();
}
private void showUserToast(int resId) {
Toast.makeText(this, resId, Toast.LENGTH_LONG).show();
}
public void showSuccessToastAndDismissDialog() {
Toast successToast = Toast.makeText(this, R.string.login_success, Toast.LENGTH_SHORT);
successToast.show();
progressDialog.dismiss();
}
public void emptySensitiveEditFields() {
passwordEdit.setText("");
twoFactorEdit.setText("");
}
public void startMainActivity() {
ContributionsActivity.startYourself(this);
finish();
}
}

View file

@ -72,7 +72,7 @@ class LoginTask extends AsyncTask<String, String, String> {
}
private void handlePassResult() {
loginActivity.showSuccessToastAndDismissDialog();
loginActivity.showSuccessAndDismissDialog();
AccountAuthenticatorResponse response = null;
@ -99,27 +99,27 @@ class LoginTask extends AsyncTask<String, String, String> {
private void handleOtherResults(String result) {
if (result.equals("NetworkFailure")) {
// Matches NetworkFailure which is created by the doInBackground method
loginActivity.showUserToastAndCancelDialog(R.string.login_failed_network);
loginActivity.showMessageAndCancelDialog(R.string.login_failed_network);
} else if (result.toLowerCase().contains("nosuchuser".toLowerCase()) || result.toLowerCase().contains("noname".toLowerCase())) {
// Matches nosuchuser, nosuchusershort, noname
loginActivity.showUserToastAndCancelDialog(R.string.login_failed_username);
loginActivity.showMessageAndCancelDialog(R.string.login_failed_username);
loginActivity.emptySensitiveEditFields();
} else if (result.toLowerCase().contains("wrongpassword".toLowerCase())) {
// Matches wrongpassword, wrongpasswordempty
loginActivity.showUserToastAndCancelDialog(R.string.login_failed_password);
loginActivity.showMessageAndCancelDialog(R.string.login_failed_password);
loginActivity.emptySensitiveEditFields();
} else if (result.toLowerCase().contains("throttle".toLowerCase())) {
// Matches unknown throttle error codes
loginActivity.showUserToastAndCancelDialog(R.string.login_failed_throttled);
loginActivity.showMessageAndCancelDialog(R.string.login_failed_throttled);
} else if (result.toLowerCase().contains("userblocked".toLowerCase())) {
// Matches login-userblocked
loginActivity.showUserToastAndCancelDialog(R.string.login_failed_blocked);
loginActivity.showMessageAndCancelDialog(R.string.login_failed_blocked);
} else if (result.equals("2FA")) {
loginActivity.askUserForTwoFactorAuth();
} else {
// Occurs with unhandled login failure codes
Timber.d("Login failed with reason: %s", result);
loginActivity.showUserToastAndCancelDialog(R.string.login_failed_generic);
loginActivity.showMessageAndCancelDialog(R.string.login_failed_generic);
}
}
}

View file

@ -11,7 +11,6 @@ import android.support.annotation.NonNull;
import android.text.TextUtils;
import fr.free.nrw.commons.CommonsApplication;
import fr.free.nrw.commons.data.Category;
import fr.free.nrw.commons.data.DBOpenHelper;
import timber.log.Timber;

View file

@ -3,6 +3,7 @@ package fr.free.nrw.commons.settings;
import android.Manifest;
import android.app.AlertDialog;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
@ -18,6 +19,7 @@ import android.preference.PreferenceManager;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v4.content.FileProvider;
import android.widget.Toast;
import java.io.File;
@ -120,7 +122,13 @@ public class SettingsFragment extends PreferenceFragment {
private void sendAppLogsViaEmail() {
String appLogs = Utils.getAppLogs();
File appLogsFile = FileUtils.createAndGetAppLogsFile(appLogs);
Uri appLogsFilePath = Uri.fromFile(appLogsFile);
Context applicationContext = getActivity().getApplicationContext();
Uri appLogsFilePath = FileProvider.getUriForFile(
getActivity(),
applicationContext.getPackageName() + ".provider",
appLogsFile
);
Intent feedbackIntent = new Intent(Intent.ACTION_SEND);
feedbackIntent.setType("message/rfc822");