diff --git a/.idea/.name b/.idea/.name new file mode 100644 index 0000000..ece1c9b --- /dev/null +++ b/.idea/.name @@ -0,0 +1 @@ +Lab3 \ No newline at end of file diff --git a/.idea/androidTestResultsUserPreferences.xml b/.idea/androidTestResultsUserPreferences.xml index a8f887d..02d601b 100644 --- a/.idea/androidTestResultsUserPreferences.xml +++ b/.idea/androidTestResultsUserPreferences.xml @@ -9,14 +9,61 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/.idea/deploymentTargetDropDown.xml b/.idea/deploymentTargetDropDown.xml deleted file mode 100644 index e093846..0000000 --- a/.idea/deploymentTargetDropDown.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 5f64eb7..e31912c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,5 +1,6 @@ plugins { id 'com.android.application' + id 'com.google.gms.google-services' } android { @@ -32,18 +33,24 @@ dependencies { implementation 'androidx.appcompat:appcompat:1.3.1' implementation 'com.google.android.material:material:1.4.0' implementation 'androidx.constraintlayout:constraintlayout:2.1.0' + implementation 'com.google.firebase:firebase-auth:21.1.0' testImplementation 'junit:junit:4.+' - testImplementation 'org.junit.jupiter:junit-jupiter' + testImplementation 'org.junit.jupiter:junit-jupiter:5.8.0' androidTestImplementation 'androidx.test.ext:junit:1.1.3' androidTestImplementation 'androidx.test:runner:1.4.0' androidTestImplementation 'androidx.test:rules:1.4.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' + androidTestImplementation 'androidx.test.espresso:espresso-intents:3.4.0' + androidTestImplementation 'org.mockito:mockito-core:2.+' testImplementation 'androidx.test:core:1.4.0' testImplementation 'androidx.test.ext:junit:1.1.3' - testImplementation 'junit:junit:4.12' - testImplementation 'org.robolectric:robolectric' + testImplementation 'junit:junit:4.+' + testImplementation 'org.robolectric:robolectric:4.8' testImplementation 'androidx.test.espresso:espresso-intents:3.4.0' testImplementation 'androidx.test.ext:truth:1.4.0' + testImplementation "org.mockito:mockito-core:3.5.0" + + } \ No newline at end of file diff --git a/app/google-services.json b/app/google-services.json new file mode 100644 index 0000000..948082a --- /dev/null +++ b/app/google-services.json @@ -0,0 +1,39 @@ +{ + "project_info": { + "project_number": "97756466887", + "project_id": "lab3-23376", + "storage_bucket": "lab3-23376.appspot.com" + }, + "client": [ + { + "client_info": { + "mobilesdk_app_id": "1:97756466887:android:bd9a3b28cbcab2d40278e2", + "android_client_info": { + "package_name": "com.example.testAndroid" + } + }, + "oauth_client": [ + { + "client_id": "97756466887-men8n7vp3sb52lhmtgc9cl9mu0v17h6n.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyA60VFxjN__i_RhzdGsf9Zk0LRU6ANK5I0" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [ + { + "client_id": "97756466887-men8n7vp3sb52lhmtgc9cl9mu0v17h6n.apps.googleusercontent.com", + "client_type": 3 + } + ] + } + } + } + ], + "configuration_version": "1" +} \ No newline at end of file diff --git a/app/src/androidTest/java/com/example/Lab3/EspressoMainTest.java b/app/src/androidTest/java/com/example/Lab3/EspressoMainTest.java index 6ec7b79..fd2076a 100644 --- a/app/src/androidTest/java/com/example/Lab3/EspressoMainTest.java +++ b/app/src/androidTest/java/com/example/Lab3/EspressoMainTest.java @@ -1,11 +1,15 @@ package com.example.Lab3; import android.app.Activity; +import android.content.Context; +import android.content.SharedPreferences; +import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import androidx.test.espresso.NoMatchingViewException; import androidx.test.espresso.action.ViewActions; import androidx.test.espresso.matcher.ViewMatchers; import androidx.test.ext.junit.rules.ActivityScenarioRule; @@ -18,6 +22,7 @@ import static androidx.test.espresso.action.ViewActions.closeSoftKeyboard; import static androidx.test.espresso.action.ViewActions.typeText; import static androidx.test.espresso.assertion.ViewAssertions.matches; +import static androidx.test.espresso.matcher.ViewMatchers.isClickable; import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; import static androidx.test.espresso.matcher.ViewMatchers.withId; import static androidx.test.espresso.matcher.ViewMatchers.withText; @@ -26,17 +31,65 @@ @RunWith(AndroidJUnit4.class) @SmallTest public class EspressoMainTest { + + @Rule public ActivityScenarioRule activityRule = new ActivityScenarioRule<>(MainActivity.class); + @Test - public void listGoesOverTheFold() { + public void IsTimerOn() { onView(withId(R.id.time)).check(matches(isDisplayed())); } + @Test - public void listGoesOverTheFold2() { - onView(withId(R.id.time)).check(matches(isDisplayed())); + public void IsYesButtonVisible(){ + for (int i=0; i<10; i++){ + onView(withId(R.id.yes_btn)).perform(click()); + } + onView(withId(R.id.points)).check(matches(withText("10"))); + } + @Test + public void IsNoButtonVisible(){ + onView(withId(R.id.no_btn)).check(matches(isClickable())); + + for (int i=0; i<10; i++){ + onView(withId(R.id.no_btn)).perform(click()); + } + onView(withId(R.id.points)).check(matches(withText("0"))); + + } + + @Test + public void IsQuestionVisible() { + onView(withId(R.id.roolsView)) + .check(matches(isDisplayed())) + .check(matches(withText("Чи співпадає назва кольору ліворуч з кольором текста праворуч?"))); + } + + @Test + public void IsGameEndsInMinute() throws InterruptedException { + int clickCount = 5; + for (int i = 0; i < clickCount; i++){ + onView(withId(R.id.yes_btn)).perform(click()); + } + onView(withId(R.id.points)).check(matches(withText("5"))); + Thread.sleep(55 * 1000); + int TIMEOUT = 10 * 1000; + int CONDITION_CHECK_INTERVAL = 100; + + long startTime = System.currentTimeMillis(); + try { + while(System.currentTimeMillis() - startTime < TIMEOUT) { + onView(withId(R.id.roolsView)).check(matches(isDisplayed())); + Thread.sleep(CONDITION_CHECK_INTERVAL); + } + } catch (NoMatchingViewException e){ + onView(withId(R.id.result_string)).check(matches(withText("Ваш результат:"))); + onView(withId(R.id.result)).check(matches(withText("5"))); + } + } } diff --git a/app/src/androidTest/java/com/example/Lab3/LoginTest.java b/app/src/androidTest/java/com/example/Lab3/LoginTest.java new file mode 100644 index 0000000..a79b0d2 --- /dev/null +++ b/app/src/androidTest/java/com/example/Lab3/LoginTest.java @@ -0,0 +1,114 @@ +package com.example.Lab3; + +import static androidx.test.espresso.Espresso.onView; +import static androidx.test.espresso.action.ViewActions.click; +import static androidx.test.espresso.action.ViewActions.closeSoftKeyboard; +import static androidx.test.espresso.assertion.ViewAssertions.matches; +import static androidx.test.espresso.intent.Intents.intended; +import static androidx.test.espresso.intent.Intents.intending; +import static androidx.test.espresso.intent.matcher.IntentMatchers.hasComponent; +import static androidx.test.espresso.intent.matcher.IntentMatchers.toPackage; +import static androidx.test.espresso.matcher.RootMatchers.withDecorView; +import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; +import static androidx.test.espresso.matcher.ViewMatchers.withId; +import static androidx.test.espresso.matcher.ViewMatchers.withText; + +import android.app.Activity; +import android.app.Instrumentation; +import android.content.Intent; +import android.os.IBinder; +import android.view.View; +import android.view.WindowManager; + +import androidx.test.core.app.ActivityScenario; +import androidx.test.espresso.Root; +import androidx.test.espresso.action.TypeTextAction; +import androidx.test.espresso.intent.Intents; +import androidx.test.ext.junit.rules.ActivityScenarioRule; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.SmallTest; + +import org.hamcrest.Matchers; +import org.hamcrest.TypeSafeMatcher; +import org.junit.After; +import org.junit.Before; +import org.junit.FixMethodOrder; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.MethodSorters; + +import org.hamcrest.Description; + + +@RunWith(AndroidJUnit4.class) +@SmallTest +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public class LoginTest { + private View decorView; + @Rule + public ActivityScenarioRule activityRule = + new ActivityScenarioRule<>(LoginActivity.class); + + @Test + public void IsEmailFieldVisible() { + onView(withId(R.id.username)).check(matches(isDisplayed())); + } + @Test + public void IsPasswordFieldVisible() { + onView(withId(R.id.password)).check(matches(isDisplayed())); + } + + + @Before + public void intentInit(){ + Intents.init(); + } + + @Test + public void TryRightLogin() throws InterruptedException { + onView(withId(R.id.username)).perform(new TypeTextAction("test@gmail.com"), closeSoftKeyboard()); + onView(withId(R.id.password)).perform(new TypeTextAction("123456"), closeSoftKeyboard()); +// onView(withId(R.id.password)).perform(closeSoftKeyboard()); + Thread.sleep(250); + onView(withId(R.id.loginButton)).perform(click()); + + Intent i = new Intent(); + Instrumentation.ActivityResult result = new Instrumentation.ActivityResult(Activity.RESULT_OK, i); + intending(toPackage(StartActivity.class.getName())); + + + } + + @After + public void intentRelease(){ + Intents.release(); + } + + @Test + public void TryWrongLogin() throws InterruptedException { + onView(withId(R.id.username)).perform(new TypeTextAction("test1@gmail.com"), closeSoftKeyboard()); + onView(withId(R.id.password)).perform(new TypeTextAction("123456"), closeSoftKeyboard()); + Thread.sleep(250); + onView(withId(R.id.loginButton)).perform(click()); + + + onView(withText("Authentication failed.")) + .inRoot(new ToastMatcher())// Here we use decorView + .check(matches(isDisplayed())); + + +// onView(withText("Authentication failed.")) +// .inRoot(withDecorView(Matchers.is(decorView)))// Here we use decorView +// .check(matches(isDisplayed())); + + +// onView(withId(R.id.checklogin)).check(matches(withText("False"))); + + + + } + + + +} diff --git a/app/src/androidTest/java/com/example/Lab3/ResultTest.java b/app/src/androidTest/java/com/example/Lab3/ResultTest.java new file mode 100644 index 0000000..a612d0a --- /dev/null +++ b/app/src/androidTest/java/com/example/Lab3/ResultTest.java @@ -0,0 +1,93 @@ +package com.example.Lab3; + +import static androidx.test.espresso.Espresso.onView; +import static androidx.test.espresso.action.ViewActions.click; +import static androidx.test.espresso.assertion.ViewAssertions.matches; +import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; +import static androidx.test.espresso.matcher.ViewMatchers.withId; +import static androidx.test.espresso.matcher.ViewMatchers.withText; + +import static org.junit.Assert.assertEquals; + +import android.content.Context; +import android.content.Intent; + +import androidx.lifecycle.Lifecycle; +import androidx.test.espresso.ViewInteraction; +import androidx.test.ext.junit.rules.ActivityScenarioRule; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.SmallTest; +import androidx.test.platform.app.InstrumentationRegistry; + +import org.junit.Assert; +import org.junit.Ignore; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; + + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class ResultTest { + + @Rule + public ActivityScenarioRule activityRule; + + public ResultTest() { + // predefine state + Context ctx = InstrumentationRegistry.getInstrumentation().getTargetContext(); + Intent intent = new Intent(ctx, ResultActivity.class); + intent.putExtra("result", "5"); + activityRule = new ActivityScenarioRule(intent); + } + + @Ignore + @Test + public void TestIntentMock() { + + Context context = Mockito.mock(Context.class); + Intent intent = ResultActivity.createIntent(context, "5"); + Assert.assertNotNull(intent); + + Assert.assertEquals("5", intent.getStringExtra("result")); + + } + + @Test + public void IsTextRight() { + onView(withId(R.id.result_string)).check(matches(withText("Ваш результат:"))); + } + + @Test + public void IsResultRight () { + onView(withId(R.id.result)).check(matches(withText("5"))); + } + + @Test + public void IsTextPlayMoreRight() { + ViewInteraction playMoreBtn = onView(withId(R.id.play_btn)); + playMoreBtn.check(matches(withText(R.string.play_btn_text))); + + playMoreBtn.perform(click()); + onView(withText(R.string.rools_text)).check(matches(isDisplayed())); + + + } + + @Test + public void IsTextGoBackRight() { + ViewInteraction mainBtn = onView(withId(R.id.main_btn)).check(matches(withText(R.string.main_btn_text))); + mainBtn.perform(click()); + onView(withId(R.id.start_btn)).check(matches(isDisplayed())); + } + + @Test + public void IsTextExitRight () throws InterruptedException { + ViewInteraction exitBtn = onView(withId(R.id.exit_btn)).check(matches(withText(R.string.exit_btn_text))); + + exitBtn.perform(click()); + Thread.sleep(500); + assertEquals(activityRule.getScenario().getState(), Lifecycle.State.DESTROYED); + } +} diff --git a/app/src/androidTest/java/com/example/Lab3/ToastMatcher.java b/app/src/androidTest/java/com/example/Lab3/ToastMatcher.java new file mode 100644 index 0000000..8ba7236 --- /dev/null +++ b/app/src/androidTest/java/com/example/Lab3/ToastMatcher.java @@ -0,0 +1,36 @@ +package com.example.Lab3; + +import android.os.IBinder; +//import android.provider.DocumentsContract; +//import android.provider.DocumentsContract.Root; +import android.view.WindowManager; + +//import androidx.test.espresso.Root; + +import androidx.test.espresso.Root; + +import org.hamcrest.Description; +import org.hamcrest.TypeSafeMatcher; + +public class ToastMatcher extends TypeSafeMatcher { + + @Override + public void describeTo(Description description) { +// super.describeTo(description); + description.appendText("is toast"); + } + + @Override + public boolean matchesSafely(Root root) { + int type = root.getWindowLayoutParams().get().type; + if ((type == WindowManager.LayoutParams.TYPE_TOAST)) { + IBinder windowToken = root.getDecorView().getWindowToken(); + IBinder appToken = root.getDecorView().getApplicationWindowToken(); + if (windowToken == appToken) { + //means this window isn't contained by any other windows. + return true; + } + } + return false; + } +} diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ded0189..bffdb3f 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,6 +1,6 @@ + package="com.example.Lab3" > + android:theme="@style/Theme.Lab3" > + android:name=".LoginActivity" + android:exported="true" > + + + + + + + + + @@ -19,8 +32,8 @@ + android:name=".MainActivity" + android:exported="true" > @@ -28,8 +41,8 @@ + android:name=".ResultActivity" + android:exported="true" > diff --git a/app/src/main/java/com/example/Lab3/LoginActivity.java b/app/src/main/java/com/example/Lab3/LoginActivity.java new file mode 100644 index 0000000..36d0fa6 --- /dev/null +++ b/app/src/main/java/com/example/Lab3/LoginActivity.java @@ -0,0 +1,123 @@ +package com.example.Lab3; + +import androidx.annotation.NonNull; +import androidx.appcompat.app.AppCompatActivity; + +import android.content.Intent; +import android.os.Bundle; +import android.util.Log; +import android.view.View; +import android.widget.Button; +import android.widget.EditText; +import android.widget.TextView; +import android.widget.Toast; + +import com.google.android.gms.tasks.OnCompleteListener; +import com.google.android.gms.tasks.Task; +import com.google.firebase.auth.AuthResult; +import com.google.firebase.auth.FirebaseAuth; +import com.google.firebase.auth.FirebaseUser; + +public class LoginActivity extends AppCompatActivity { + EditText username; + EditText password; + TextView signup; + TextView checklogin; + + Button loginButton; + private FirebaseAuth mAuth; + private String email_val, password_val; + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_login); + username = findViewById(R.id.username); + password = findViewById(R.id.password); + loginButton = findViewById(R.id.loginButton); + signup = findViewById(R.id.signupText); + checklogin = findViewById(R.id.checklogin); + mAuth = FirebaseAuth.getInstance(); + + //checking if user is logged in + if (mAuth.getCurrentUser() != null) { + updateUI(mAuth.getCurrentUser()); + } + + + + loginButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { +// if (username.getText().toString().equals("user") && password.getText().toString().equals("1234")) { +// Toast.makeText(LoginActivity.this, "Login Successful!", Toast.LENGTH_SHORT).show(); +// } else { +// Toast.makeText(LoginActivity.this, "Login Failed!", Toast.LENGTH_SHORT).show(); +// } + + email_val = username.getText().toString(); + password_val = password.getText().toString(); + mAuth.signInWithEmailAndPassword(email_val, password_val) + .addOnCompleteListener(LoginActivity.this, new OnCompleteListener() { + @Override + public void onComplete(@NonNull Task task) { + if (task.isSuccessful()) { + // Sign in success, update UI with the signed-in user's information + Log.d("LoginActivity", "signInWithEmail:success"); + FirebaseUser user = mAuth.getCurrentUser(); + checklogin.setText("True"); + updateUI(user); + } else { + // If sign in fails, display a message to the user. + Log.w("LoginActivity", "signInWithEmail:failure", task.getException()); + checklogin.setText("False"); + Toast.makeText(getApplicationContext(), "Authentication failed.", + Toast.LENGTH_LONG).show(); + } + + // ... + } + + + }); + } + }); + + + signup.setOnClickListener(new View.OnClickListener(){ + @Override + public void onClick(View v) { + Intent registerIntent = new Intent(getApplicationContext(), StartActivity.class); + startActivity(registerIntent); + } + }); + } + + + @Override + public void onStart() { + super.onStart(); + // Check if user is signed in (non-null) and update UI accordingly. + FirebaseUser currentUser = mAuth.getCurrentUser(); + if (currentUser != null) { + updateUI(currentUser); + } + } + + @Override + protected void onResume() { + super.onResume(); + // Check if user is signed in (non-null) and update UI accordingly. + FirebaseUser currentUser = mAuth.getCurrentUser(); + if (currentUser != null) { + updateUI(currentUser); + } + } + + public void updateUI(FirebaseUser currentUser) { + Intent profileIntent = new Intent(getApplicationContext(), StartActivity.class); + profileIntent.putExtra("email", currentUser.getEmail()); + Log.v("DATA", currentUser.getUid()); + startActivity(profileIntent); + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/example/Lab3/MainActivity.java b/app/src/main/java/com/example/Lab3/MainActivity.java index 968d2a7..29900c3 100644 --- a/app/src/main/java/com/example/Lab3/MainActivity.java +++ b/app/src/main/java/com/example/Lab3/MainActivity.java @@ -49,10 +49,11 @@ protected void onCreate(Bundle savedInstanceState) { onYesClick(); onNoClick(); + SharedPreferences sharedPref = this.getPreferences(Context.MODE_PRIVATE); - SharedPreferences.Editor editor = sharedPref.edit(); - editor.putInt(getString(R.string.testing_mode), 0); - editor.apply(); + int status = 1; + + setTestMode(sharedPref, status); time = (TextView) findViewById(R.id.time); new CountDownTimer(60000, 1000){ @@ -67,14 +68,27 @@ public void onTick(long l){ @Override public void onFinish(){ time.setText("00:00"); - Intent intent = new Intent(MainActivity.this, ResultActivity.class); - intent.putExtra("result", points.getText()); + Intent intent = createIntent(MainActivity.this, points.getText()); + finish(); startActivity(intent); } }.start(); } + static public Intent createIntent(Context context, CharSequence points){ + Intent i = new Intent(context, ResultActivity.class); + i.putExtra("result", points); + return i; + } + + static public void setTestMode(SharedPreferences sharedPref, int status){ +// SharedPreferences sharedPref = this.getPreferences(Context.MODE_PRIVATE); + SharedPreferences.Editor editor = sharedPref.edit(); + editor.putInt("testing_mode", status); + editor.apply(); + } + static public Map array2dict(String[] tags) { Map dictionary = new HashMap(); for (String tag : tags) { diff --git a/app/src/main/java/com/example/Lab3/ResultActivity.java b/app/src/main/java/com/example/Lab3/ResultActivity.java index 12d9d07..eb41d6d 100644 --- a/app/src/main/java/com/example/Lab3/ResultActivity.java +++ b/app/src/main/java/com/example/Lab3/ResultActivity.java @@ -2,6 +2,7 @@ import androidx.appcompat.app.AppCompatActivity; +import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.view.View; @@ -58,4 +59,12 @@ public void onClick(View view) { } ); } + + static public Intent createIntent(Context context, CharSequence points){ + Intent i = new Intent(context, ResultActivity.class); + i.putExtra("result", points); + return i; + } + + } \ No newline at end of file diff --git a/app/src/main/java/com/example/Lab3/StartActivity.java b/app/src/main/java/com/example/Lab3/StartActivity.java index a9edc90..0e1f56d 100644 --- a/app/src/main/java/com/example/Lab3/StartActivity.java +++ b/app/src/main/java/com/example/Lab3/StartActivity.java @@ -38,7 +38,10 @@ public void onClick(View view) { new View.OnClickListener() { @Override public void onClick(View view) { - finish(); + Intent intent = new Intent(StartActivity.this, LoginActivity.class); + //finish(); + startActivity(intent); + } } ); diff --git a/app/src/main/res/drawable-v24/loginbkg.png b/app/src/main/res/drawable-v24/loginbkg.png new file mode 100644 index 0000000..056d426 Binary files /dev/null and b/app/src/main/res/drawable-v24/loginbkg.png differ diff --git a/app/src/main/res/layout/activity_login.xml b/app/src/main/res/layout/activity_login.xml new file mode 100644 index 0000000..fd12ab7 --- /dev/null +++ b/app/src/main/res/layout/activity_login.xml @@ -0,0 +1,94 @@ + + + + + + + + + + +