diff --git a/build.gradle b/build.gradle index 3669126e7..83257dcda 100644 --- a/build.gradle +++ b/build.gradle @@ -50,8 +50,8 @@ ext { compileSdkVersion = 28 targetSdkVersion = 28 buildToolsVersion = '28.0.3' - versionCode = 44 - versionName = "1.1.4" + versionCode = 45 + versionName = "1.1.5" javaCompileVersion = JavaVersion.VERSION_1_8 diff --git a/org.envirocar.app/AndroidManifest.xml b/org.envirocar.app/AndroidManifest.xml index de8e0bd88..2c1a1ef9e 100644 --- a/org.envirocar.app/AndroidManifest.xml +++ b/org.envirocar.app/AndroidManifest.xml @@ -3,8 +3,8 @@ xmlns:tools="http://schemas.android.com/tools" package="org.envirocar.app" android:installLocation="internalOnly" - android:versionCode="44" - android:versionName="1.1.4"> + android:versionCode="45" + android:versionName="1.1.5"> diff --git a/org.envirocar.app/res/drawable/ic_error_red_24dp.xml b/org.envirocar.app/res/drawable/ic_error_red_24dp.xml new file mode 100644 index 000000000..e1569395b --- /dev/null +++ b/org.envirocar.app/res/drawable/ic_error_red_24dp.xml @@ -0,0 +1,5 @@ + + + diff --git a/org.envirocar.app/res/values/strings_activity_login.xml b/org.envirocar.app/res/values/strings_activity_login.xml index 079e5ec80..361ff8c40 100644 --- a/org.envirocar.app/res/values/strings_activity_login.xml +++ b/org.envirocar.app/res/values/strings_activity_login.xml @@ -29,6 +29,7 @@ Registering… The passwords do not match. This username is too short. + Special symbol not allowed except underscore Password must contain at least one uppercase letter, one lowercase letter and one digit This username is already in use. diff --git a/org.envirocar.app/src/org/envirocar/app/recording/strategy/OBDRecordingStrategy.java b/org.envirocar.app/src/org/envirocar/app/recording/strategy/OBDRecordingStrategy.java index 36f1cc334..524b7a101 100644 --- a/org.envirocar.app/src/org/envirocar/app/recording/strategy/OBDRecordingStrategy.java +++ b/org.envirocar.app/src/org/envirocar/app/recording/strategy/OBDRecordingStrategy.java @@ -1,18 +1,18 @@ /** * Copyright (C) 2013 - 2019 the enviroCar community - * + *

* This file is part of the enviroCar app. - * + *

* The enviroCar app is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License as published * by the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + *

* The enviroCar app is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General * Public License for more details. - * + *

* You should have received a copy of the GNU General Public License along * with the enviroCar app. If not, see http://www.gnu.org/licenses/. */ @@ -95,6 +95,7 @@ public class OBDRecordingStrategy implements RecordingStrategy { private LoadBasedEnergyConsumptionAlgorithm energyConsumptionAlgorithm; private boolean isRecording = false; + private boolean isTrackFinished = false; private Track track = null; /** @@ -134,6 +135,7 @@ protected void onDestroy() { @Override public void startRecording(Service service, RecordingListener listener) { this.listener = listener; + this.isTrackFinished = false; disposables.add( obdConnectionHandler.getOBDConnectionObservable(bluetoothHandler.getSelectedBluetoothDevice()) @@ -172,9 +174,7 @@ public void stopRecording() { isRecording = false; } listener.onRecordingStateChanged(RecordingState.RECORDING_STOPPED); - if (track != null){ - listener.onTrackFinished(track); - } + notifyTrackFinished(track); } private DisposableObserver initializeObserver() { @@ -211,12 +211,19 @@ public void onError(Throwable e) { public void onComplete() { LOG.info("Finished the recording of the track."); listener.onRecordingStateChanged(RecordingState.RECORDING_STOPPED); - listener.onTrackFinished(track); + notifyTrackFinished(track); stopOBDConnectionRecognizer(); } }; } + private void notifyTrackFinished(Track track) { + if (!isTrackFinished && track != null) { + this.listener.onTrackFinished(track); + this.isTrackFinished = true; + } + } + private ObservableTransformer verifyConnection() { return upstream -> upstream.flatMap(socket -> Observable.create(emitter -> { diff --git a/org.envirocar.app/src/org/envirocar/app/views/carselection/CarSelectionAddCarFragment.java b/org.envirocar.app/src/org/envirocar/app/views/carselection/CarSelectionAddCarFragment.java index 894fb4dd1..86b1a3bf9 100644 --- a/org.envirocar.app/src/org/envirocar/app/views/carselection/CarSelectionAddCarFragment.java +++ b/org.envirocar.app/src/org/envirocar/app/views/carselection/CarSelectionAddCarFragment.java @@ -20,6 +20,7 @@ import android.app.Activity; import android.content.Context; +import android.graphics.drawable.Drawable; import android.os.Bundle; import android.util.Pair; import android.view.LayoutInflater; @@ -94,6 +95,7 @@ public class CarSelectionAddCarFragment extends BaseInjectorFragment { private static final int CONSTRUCTION_YEAR_MAX = Calendar.getInstance().get(Calendar.YEAR); private static final int ENGINE_DISPLACEMENT_MIN = 500; private static final int ENGINE_DISPLACEMENT_MAX = 5000; + private static Drawable error; @BindView(R.id.envirocar_toolbar) protected Toolbar toolbar; @@ -150,7 +152,8 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle closeThisFragment(); }); - + error = getResources().getDrawable(R.drawable.ic_error_red_24dp); + error.setBounds(-50,0,0,error.getIntrinsicHeight()); // initially we set the toolbar exp to gone toolbar.setVisibility(View.GONE); toolbarExp.setVisibility(View.GONE); @@ -249,6 +252,7 @@ protected void onConstructionYearChanged(CharSequence text) { @OnTextChanged(value = R.id.activity_car_selection_newcar_input_fueltype, callback = OnTextChanged.Callback.AFTER_TEXT_CHANGED) protected void onFuelTypeChanged(CharSequence text) { + fueltypeText.setError(null); if (text.toString().isEmpty()) return; @@ -287,23 +291,23 @@ private Predicate continueWhenFormIsCorrect() { //First check all input forms for empty strings View focusView = null; if (fuelType != Car.FuelType.ELECTRIC && engineText.getText().length() == 0) { - engineText.setError(getString(R.string.car_selection_error_empty_input)); + engineText.setError(getString(R.string.car_selection_error_empty_input),error); focusView = engineText; } if (fueltypeText.getText().length() == 0) { - fueltypeText.setError(getString(R.string.car_selection_error_empty_input)); + fueltypeText.setError(getString(R.string.car_selection_error_empty_input),error); focusView = fueltypeText; } if (yearText.getText().length() == 0) { - yearText.setError(getString(R.string.car_selection_error_empty_input)); + yearText.setError(getString(R.string.car_selection_error_empty_input),error); focusView = yearText; } if (modelText.getText().length() == 0) { - modelText.setError(getString(R.string.car_selection_error_empty_input)); + modelText.setError(getString(R.string.car_selection_error_empty_input),error); focusView = modelText; } if (manufacturerText.getText().length() == 0) { - manufacturerText.setError(getString(R.string.car_selection_error_empty_input)); + manufacturerText.setError(getString(R.string.car_selection_error_empty_input),error); focusView = manufacturerText; } @@ -353,11 +357,11 @@ private Predicate continueWhenCarHasCorrectValues() { // Check the values of engine and year for validity. if (car.getFuelType() != Car.FuelType.ELECTRIC && (car.getEngineDisplacement() < 500 || car.getEngineDisplacement() > 5000)) { - engineText.setError(getString(R.string.car_selection_error_invalid_input)); + engineText.setError(getString(R.string.car_selection_error_invalid_input),error); focusView = engineText; } if (car.getConstructionYear() < 1990 || car.getConstructionYear() > currentYear) { - yearText.setError(getString(R.string.car_selection_error_invalid_input)); + yearText.setError(getString(R.string.car_selection_error_invalid_input),error); focusView = yearText; } @@ -619,7 +623,7 @@ private void initWatcher() { .map(t -> t.toString()) .subscribe(model -> { if (model.trim().isEmpty()) { - modelText.setError(getString(R.string.car_selection_error_empty_input)); + modelText.setError(getString(R.string.car_selection_error_empty_input),error); } }, LOG::error)); @@ -634,12 +638,12 @@ private void initWatcher() { try { int year = Integer.parseInt(yearString); if (year < CONSTRUCTION_YEAR_MIN || year > CONSTRUCTION_YEAR_MAX) { - yearText.setError(getString(R.string.car_selection_error_invalid_input)); + yearText.setError(getString(R.string.car_selection_error_invalid_input),error); yearText.requestFocus(); } } catch (Exception e) { LOG.error(String.format("Unable to parse year [%s]", yearString), e); - yearText.setError(getString(R.string.car_selection_error_invalid_input)); + yearText.setError(getString(R.string.car_selection_error_invalid_input),error); yearText.requestFocus(); } }, LOG::error)); @@ -658,12 +662,12 @@ private void initWatcher() { try { int engine = Integer.parseInt(engineString); if (engine < ENGINE_DISPLACEMENT_MIN || engine > ENGINE_DISPLACEMENT_MAX) { - engineText.setError(getString(R.string.car_selection_error_invalid_input)); + engineText.setError(getString(R.string.car_selection_error_invalid_input),error); engineText.requestFocus(); } } catch (Exception e) { LOG.error(String.format("Unable to parse engine [%s]", engineString), e); - engineText.setError(getString(R.string.car_selection_error_invalid_input)); + engineText.setError(getString(R.string.car_selection_error_invalid_input),error); engineText.requestFocus(); } }, LOG::error)); diff --git a/org.envirocar.app/src/org/envirocar/app/views/login/SignupActivity.java b/org.envirocar.app/src/org/envirocar/app/views/login/SignupActivity.java index c82f6ccc7..c21355b46 100644 --- a/org.envirocar.app/src/org/envirocar/app/views/login/SignupActivity.java +++ b/org.envirocar.app/src/org/envirocar/app/views/login/SignupActivity.java @@ -20,6 +20,7 @@ import android.content.Context; import android.content.Intent; +import android.graphics.drawable.Drawable; import android.os.Bundle; import android.text.Selection; import android.text.Spannable; @@ -77,7 +78,9 @@ public class SignupActivity extends BaseInjectorActivity { private static final String EMAIL_REGEX = "^[_A-Za-z0-9-\\+]+(\\.[_A-Za-z0-9-]+)*@[A-Za-z0-9-]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$"; private static final String PASSWORD_REGEX = "^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9]).{6,}$"; + private static final String USERNAME_REGEX = "^[a-z0-9_-]{6,}$"; private static final int CHECK_FORM_DELAY = 750; + private static Drawable error; public static void startActivity(Context context) { Intent intent = new Intent(context, SignupActivity.class); @@ -129,6 +132,9 @@ protected void onCreate(Bundle savedInstanceState) { // inject the views ButterKnife.bind(this); + error = getResources().getDrawable(R.drawable.ic_error_red_24dp); + error.setBounds(-50,0,0,error.getIntrinsicHeight()); + // make terms of use and privacy statement clickable this.makeClickableTextLinks(); observeFormInputs(); @@ -334,6 +340,9 @@ private boolean checkUsernameValidity(String username) { } else if (username.length() < 6) { usernameEditText.setError(getString(R.string.error_invalid_username)); isValidUsername = false; + } else if (!Pattern.matches(USERNAME_REGEX,username)) { + usernameEditText.setError(getString(R.string.error_username_contain_special)); + isValidUsername = false; } return isValidUsername; } @@ -359,13 +368,13 @@ private boolean checkEmailValidity(String email) { private boolean checkPasswordValidity(String password) { boolean isValidPassword = true; if (password == null || password.isEmpty() || password.equals("")) { - password1EditText.setError(getString(R.string.error_field_required)); + password1EditText.setError(getString(R.string.error_field_required),error); isValidPassword = false; } else if (password.length() < 6) { - password1EditText.setError(getString(R.string.error_invalid_password)); + password1EditText.setError(getString(R.string.error_invalid_password),error); isValidPassword = false; } else if (!Pattern.matches(PASSWORD_REGEX, password)) { - password1EditText.setError(getString(R.string.error_field_weak_password)); + password1EditText.setError(getString(R.string.error_field_weak_password),error); isValidPassword = false; } else { final String password2 = password2EditText.getText().toString().trim(); @@ -382,7 +391,7 @@ private boolean checkPasswordValidity(String password) { private boolean checkConfirmPasswordValidity(String password2) { boolean isValidMatch = true; if (password2 == null || password2.isEmpty() || password2.equals("")) { - password2EditText.setError(getString(R.string.error_field_required)); + password2EditText.setError(getString(R.string.error_field_required),error); isValidMatch = false; } else { final String password1 = password1EditText.getText().toString().trim(); @@ -399,8 +408,8 @@ private boolean checkConfirmPasswordValidity(String password2) { private boolean checkPasswordMatch(String password, String password2) { boolean isValidMatch = password.equals(password2); if (!isValidMatch) { - password1EditText.setError(getString(R.string.error_passwords_not_matching)); - password2EditText.setError(getString(R.string.error_passwords_not_matching)); + password1EditText.setError(getString(R.string.error_passwords_not_matching),error); + password2EditText.setError(getString(R.string.error_passwords_not_matching),error); } else { password1EditText.setError(null); password2EditText.setError(null);