חזרה ל־018b: חיבור Firebase, RTDB ו־Authentication
גרסה זו מיועדת לפרויקט שכבר משתמש ב-View Binding. בסוף השיעור MenuActivity תיפתח רק לאחר התחברות מוצלחת ל-Firebase.
הנחות מוקדמות
- השלמתם את
018aואת שיעורי ה-View Binding. - קיבלתם מהמורה עותק של
google-services.jsonשמתאים לחבילהcom.example.tictacmenuושמרתם אותו בתיקייהapp. - ספק ההתחברות Email/Password כבר מופעל בפרויקט Firebase וקיים משתמש לבדיקה.
אין צורך ליצור בשיעור זה פרויקט Firebase חדש. אם מתקבלת השגיאה No matching client found for package name, קובץ ה-JSON אינו מתאים ל-applicationId של האפליקציה.
שלב 1 - חיבור קצר של Firebase ב-Gradle
אשף Firebase ב־018b עשוי להוסיף כינוי עם gms. בדיפים מוחקים רק שורות ישנות שקיימות אצלכם ומוסיפים רק פריטים חסרים. השמות האדומים הם חלופות; התאימו לשם שמצאתם. שמרו את מספר גרסת Google Services הקיים, ומחקו מפתח גרסה ישן רק אם אינו משמש פריט אחר. בצעו Sync לאחר השלמת כל השינויים.
1.1 Gradle Scripts > libs.versions.toml
[versions]
-gms = "4.4.1"
+googleServices = "4.4.1"
+firebaseBom = "34.16.0"
[libraries]
+firebase-bom = { group = "com.google.firebase", name = "firebase-bom", version.ref = "firebaseBom" }
+firebase-auth = { group = "com.google.firebase", name = "firebase-auth" }
+firebase-database = { group = "com.google.firebase", name = "firebase-database" }
[plugins]
-google-gms-services = { id = "com.google.gms.google-services", version.ref = "gms" }
-google-services-gms = { id = "com.google.gms.google-services", version.ref = "gms" }
+google-services = { id = "com.google.gms.google-services", version.ref = "googleServices" }
1.2 Gradle Scripts > build.gradle.kts (Module :app)
plugins {
alias(libs.plugins.android.application)
- alias(libs.plugins.google.gms.services)
- alias(libs.plugins.google.services.gms)
+ alias(libs.plugins.google.services)
}
dependencies {
+ implementation(platform(libs.firebase.bom))
+ implementation(libs.firebase.auth)
+ implementation(libs.firebase.database)
}
לעיתים צריך לפתוח גם את Gradle Scripts > build.gradle.kts (Project: …). אם הכינוי הישן מופיע גם שם, החליפו אותו כך; אחרת אין צורך לשנות קובץ זה:
plugins {
alias(libs.plugins.android.application) apply false
- alias(libs.plugins.google.gms.services) apply false
- alias(libs.plugins.google.services.gms) apply false
+ alias(libs.plugins.google.services) apply false
}
בצעו Gradle Sync. ה־BoM קובע את גרסאות Auth ו־Database, ולכן אין להן גרסה נפרדת ב־TOML.
שלב 2 - יצירת FBRef
צרו את הקובץ:
app/src/main/java/com/example/tictacmenu/services/FBRef.java
package com.example.tictacmenu.services;
import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.auth.FirebaseUser;
import com.google.firebase.database.DatabaseReference;
import com.google.firebase.database.FirebaseDatabase;
public final class FBRef {
public static final FirebaseAuth refAuth = FirebaseAuth.getInstance();
public static final FirebaseDatabase FBDB = FirebaseDatabase.getInstance();
public static final DatabaseReference refUsers = FBDB.getReference("Users");
public static String uid;
public static DatabaseReference refUser;
private FBRef() { }
public static void getUser(FirebaseUser fbuser) {
if (fbuser == null) {
uid = null;
refUser = null;
return;
}
uid = fbuser.getUid();
refUser = refUsers.child(uid);
}
}
שלב 3 - החלפת המעבר המושהה בהתחברות אמיתית
ערכו את:
app/src/main/java/com/example/tictacmenu/activities/LoginActivity.java
ה-diff הבא מבוסס על השינוי האמיתי ב-TicTacMenu. הוא ממשיך להשתמש ב-ActivityLoginBinding, ולכן אין צורך ב-findViewById או בשדות נפרדים לכל רכיב במסך.
import android.content.SharedPreferences;
import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
+import android.text.TextUtils;
import android.view.View;
import android.widget.Toast;
import com.example.tictacmenu.databinding.ActivityLoginBinding;
+import com.example.tictacmenu.services.FBRef;
import com.example.tictacmenu.shell.MenuActivity;
+import com.google.firebase.auth.FirebaseUser;
public class LoginActivity extends AppCompatActivity {
- private static final long LOGIN_SHELL_DELAY_MS = 4000;
private static final String PREFS_NAME = "PREFS_NAME";
private static final String KEY_STAY_CONNECT = "stayConnect";
private ActivityLoginBinding binding;
private SharedPreferences settings;
- private final Handler handler = new Handler(Looper.getMainLooper());
+ private boolean loginInProgress;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
binding = ActivityLoginBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
ViewCompat.setOnApplyWindowInsetsListener(binding.main, (v, insets) -> {
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
return insets;
});
settings = getSharedPreferences(PREFS_NAME, MODE_PRIVATE);
binding.cBstayconnect.setChecked(settings.getBoolean(KEY_STAY_CONNECT, false));
binding.cBstayconnect.setOnCheckedChangeListener((buttonView, isChecked) ->
settings.edit().putBoolean(KEY_STAY_CONNECT, isChecked).apply()
);
- handler.postDelayed(() -> {
- settings.edit().putBoolean(KEY_STAY_CONNECT, binding.cBstayconnect.isChecked()).apply();
- startActivity(new Intent(LoginActivity.this, MenuActivity.class));
- finish();
- }, LOGIN_SHELL_DELAY_MS);
+ setLoginInProgress(false);
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ boolean stayConnected = settings.getBoolean(KEY_STAY_CONNECT, false);
+ FirebaseUser currentUser = FBRef.refAuth.getCurrentUser();
+ if (currentUser != null && stayConnected) {
+ FBRef.getUser(currentUser);
+ openMenu();
+ }
}
public void onLoginClick(View view) {
- Toast.makeText(this, "Email/password auth is added in a later lesson", Toast.LENGTH_SHORT).show();
+ if (loginInProgress) return;
+
+ String email = binding.eTemail.getText().toString().trim();
+ String password = binding.eTpass.getText().toString();
+
+ if (TextUtils.isEmpty(email)) {
+ binding.eTemail.setError("Enter e-mail");
+ binding.eTemail.requestFocus();
+ return;
+ }
+ if (TextUtils.isEmpty(password)) {
+ binding.eTpass.setError("Enter password");
+ binding.eTpass.requestFocus();
+ return;
+ }
+
+ setLoginInProgress(true);
+ FBRef.refAuth.signInWithEmailAndPassword(email, password)
+ .addOnCompleteListener(this, task -> {
+ setLoginInProgress(false);
+ if (task.isSuccessful()) {
+ FirebaseUser user = FBRef.refAuth.getCurrentUser();
+ if (user == null) {
+ Toast.makeText(this, "Login succeeded but no user was returned", Toast.LENGTH_SHORT).show();
+ return;
+ }
+ FBRef.getUser(user);
+ settings.edit().putBoolean(KEY_STAY_CONNECT,
+ binding.cBstayconnect.isChecked()).apply();
+ Toast.makeText(this, "Login success", Toast.LENGTH_SHORT).show();
+ openMenu();
+ return;
+ }
+ String errorMessage = (task.getException() != null)
+ ? task.getException().getLocalizedMessage()
+ : "Login failed";
+ Toast.makeText(this, "Login failed: " + errorMessage, Toast.LENGTH_LONG).show();
+ });
+ }
+
+ private void setLoginInProgress(boolean inProgress) {
+ loginInProgress = inProgress;
+ binding.btnLogin.setEnabled(!inProgress);
+ binding.btnLogin.setText(inProgress ? "Logging in..." : "Login");
+ }
+
+ private void openMenu() {
+ startActivity(new Intent(this, MenuActivity.class));
+ finish();
}
public void onGoogleLoginClick(View view) {
- Toast.makeText(this, "Google sign-in is an advanced later phase", Toast.LENGTH_SHORT).show();
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
- handler.removeCallbacksAndMessages(null);
+ Toast.makeText(this, "Google sign-in is added in 018d (requires SHA1)", Toast.LENGTH_SHORT).show();
}
}
מה השתנה?
- הוסרו ה-
Handlerוהמעבר האוטומטי לאחר ארבע שניות. - הערכים נקראים ישירות דרך
binding.eTemailו-binding.eTpass. - הכפתור מושבת בזמן שההתחברות מתבצעת, כדי למנוע לחיצות כפולות.
- רק הצלחה של
signInWithEmailAndPassword(...)פותחת אתMenuActivity. - אם סומן “הישאר מחובר”, משתמש קיים מועבר לתפריט מתוך
onStart().
שלב 4 - בדיקה
הריצו את האפליקציה. הכניסו שם משתמש וסיסמה שהמורה ייתן לכם, ובדקו שקיבלתם הודעה שהלוגין הצליח.
בסיום השיעור ההתחברות במייל ובסיסמה פעילה, FBRef מחזיק את המשתמש הנוכחי, וכל הגישה לרכיבי המסך נעשית באמצעות View Binding.
שלב 5 מומלץ: תפריט התנתקות, וצפייה ב-shared preferences
שלב זה מתועד רק בשיעור המקורי כאן
השיעור הבא
רשות — שיפור העיצוב: אפשר להוסיף ל־TicTacMenu את ערכת הצבעים הכחולה־כתומה, סרגל המגירה הכחול ומשבצות המשחק הברורות של פרויקט ה־sandbox בעזרת המדריך הקצר ליישור העיצוב. השינוי חזותי בלבד ואינו נדרש להשלמת 018d.