diff --git a/app/build.gradle b/app/build.gradle index 56a4694af..9baaed622 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -13,7 +13,7 @@ dependencies { compile 'org.mediawiki:api:1.3' compile 'commons-codec:commons-codec:1.10' compile 'com.github.pedrovgs:renderers:3.3.3' - compile 'com.google.code.gson:gson:2.8.0' + compile 'com.google.code.gson:gson:2.8.1' compile 'com.jakewharton.timber:timber:4.5.1' compile 'info.debatty:java-string-similarity:0.24' compile ('com.mapbox.mapboxsdk:mapbox-android-sdk:5.1.0@aar'){ @@ -24,6 +24,8 @@ dependencies { compile "com.android.support:appcompat-v7:${project.supportLibVersion}" compile "com.android.support:design:${project.supportLibVersion}" + compile "com.android.support:cardview-v7:${project.supportLibVersion}" + compile "com.jakewharton:butterknife:$BUTTERKNIFE_VERSION" annotationProcessor "com.jakewharton:butterknife-compiler:$BUTTERKNIFE_VERSION" @@ -43,12 +45,12 @@ dependencies { compile 'com.facebook.stetho:stetho:1.5.0' testCompile 'junit:junit:4.12' - testCompile 'org.robolectric:robolectric:3.3.2' + testCompile 'org.robolectric:robolectric:3.4' testCompile 'com.squareup.okhttp3:mockwebserver:3.8.1' androidTestCompile 'com.squareup.okhttp3:mockwebserver:3.8.1' androidTestCompile "com.android.support:support-annotations:${project.supportLibVersion}" - androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.2' + androidTestCompile 'com.android.support.test.espresso:espresso-core:3.0.1' debugCompile 'com.squareup.leakcanary:leakcanary-android:1.5.1' releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.1' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 824821136..b0cbdd34f 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -20,7 +20,7 @@ android:name=".CommonsApplication" android:icon="@drawable/ic_launcher" android:label="@string/app_name" - android:theme="@style/Theme.AppCompat" + android:theme="@style/LightAppTheme" android:supportsRtl="true" > 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(); - } - } diff --git a/app/src/main/java/fr/free/nrw/commons/auth/LoginTask.java b/app/src/main/java/fr/free/nrw/commons/auth/LoginTask.java index dd4f1c4e7..519ea323c 100644 --- a/app/src/main/java/fr/free/nrw/commons/auth/LoginTask.java +++ b/app/src/main/java/fr/free/nrw/commons/auth/LoginTask.java @@ -72,7 +72,7 @@ class LoginTask extends AsyncTask { } private void handlePassResult() { - loginActivity.showSuccessToastAndDismissDialog(); + loginActivity.showSuccessAndDismissDialog(); AccountAuthenticatorResponse response = null; @@ -99,27 +99,27 @@ class LoginTask extends AsyncTask { 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); } } } diff --git a/app/src/main/java/fr/free/nrw/commons/category/CategoryContentProvider.java b/app/src/main/java/fr/free/nrw/commons/category/CategoryContentProvider.java index 9fd125b7d..e3a2661a7 100644 --- a/app/src/main/java/fr/free/nrw/commons/category/CategoryContentProvider.java +++ b/app/src/main/java/fr/free/nrw/commons/category/CategoryContentProvider.java @@ -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; diff --git a/app/src/main/java/fr/free/nrw/commons/settings/SettingsFragment.java b/app/src/main/java/fr/free/nrw/commons/settings/SettingsFragment.java index de408712d..72bb39ba8 100644 --- a/app/src/main/java/fr/free/nrw/commons/settings/SettingsFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/settings/SettingsFragment.java @@ -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"); diff --git a/app/src/main/res/drawable/blue_rinse_circle.xml b/app/src/main/res/drawable/blue_rinse_circle.xml new file mode 100644 index 000000000..e63317a5b --- /dev/null +++ b/app/src/main/res/drawable/blue_rinse_circle.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/layout-land/activity_login.xml b/app/src/main/res/layout-land/activity_login.xml new file mode 100644 index 000000000..d96106f18 --- /dev/null +++ b/app/src/main/res/layout-land/activity_login.xml @@ -0,0 +1,195 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +