Connectra Insights

NachiketNachiket
15 min read

In this blog, I’ll share key insights and implementation details from the development of an Android application. This app was designed with robust Firebase integration and a seamless user experience in mind. Along the way, I'll explain specific challenges and solutions, with an emphasis on professional practices.

Note: All the code snippets shared in this blog are kept concise for clarity and to explain the functionality. You can always refer to my complete code to explore the detailed implementation.

1. LoginActivity.java

The LoginActivity is the gateway to the app. Here's a breakdown of its functionality and design choices:

Dark Mode Check

  • Since the app is optimized for light mode only, we perform a check at the beginning in onCreate(). If the system is in dark mode, a popup informs the user that the app is not designed for this mode. This ensures clarity in the user experience.
int nightModeFlags = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
        if (nightModeFlags == Configuration.UI_MODE_NIGHT_YES) {
            new AlertDialog.Builder(this)
                    .setTitle("Suggestion")
                    .setMessage("Please use this app without Dark mode for the best experience.")
                    .setCancelable(true) // User can dismiss the dialog by tapping outside
                    .setPositiveButton("OK", (dialog, which) -> dialog.dismiss())
                    .show();
        }

Internet Connectivity Check

  • Another critical check is for internet availability. If no connection is detected, the app displays a popup stating: “We need internet to proceed,” ensuring the user understands the requirement.
if (!isInternetAvailable()) {
    showNoInternetDialog();
}

private boolean isInternetAvailable() {
        ConnectivityManager connectivityManager =
                (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);

        if (connectivityManager != null) {
            NetworkCapabilities capabilities =
                    connectivityManager.getNetworkCapabilities(connectivityManager.getActiveNetwork());
            if (capabilities != null) {
                return capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) ||
                        capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR);
            }
        }
        return false;
   }

Navigation Handling

  • Users can navigate to the Forgot Password and Register screens by clicking the respective text views. Intents make this transition smooth and intuitive.
forgotPass.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startActivity(new Intent(LoginActivity.this, ForgotPassword.class));
            }
        });

register.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startActivity(new Intent(LoginActivity.this , RegisterActivity.class).addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP));
                finish();
            }
        });

Login Flow

  • When the Login button is clicked, we extract user credentials from input fields, validate them, and authenticate against Firebase Authentication.

  • “Edge cases” are handled to ensure no invalid inputs disrupt the flow. For example, checking for empty fields or invalid email formats.

login.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String txt_email=email.getText().toString();
                String txt_password=password.getText().toString();
                loginUser(txt_email,txt_password);
            }
        });

private void loginUser(String txtEmail, String txtPassword) {

        if (TextUtils.isEmpty(txtEmail) || TextUtils.isEmpty(txtPassword)){
            Toast.makeText(LoginActivity.this, "E-mail or Password can't be Empty", Toast.LENGTH_SHORT).show();
        }
        else if (txtPassword.length() < 6){
            Toast.makeText(LoginActivity.this, "Password must be atleast 6 Digits", Toast.LENGTH_SHORT).show();
        }
        else{
            auth.signInWithEmailAndPassword(txtEmail,txtPassword)
                    .addOnSuccessListener(new OnSuccessListener<AuthResult>() {
                        @Override
                        public void onSuccess(AuthResult authResult) {
                            Toast.makeText(LoginActivity.this, "Login Successful", Toast.LENGTH_SHORT).show();
                            startActivity(new Intent(LoginActivity.this,MainActivity.class).addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP));
                            finish();
                        }
                    })      .addOnFailureListener(new OnFailureListener() {
                        @Override
                        public void onFailure(@NonNull Exception e) {
                            Toast.makeText(LoginActivity.this, "Invalid Credentials", Toast.LENGTH_SHORT).show();
                        }
                    });
        }
    }

Detonator Status Check

  • Before proceeding with any operations, the app checks the "detonator" status. If it’s false, a popup appears, and the app shuts down.
private DatabaseReference detonatorRef;
detonatorRef = FirebaseDatabase.getInstance().getReference("Detonator");
        checkAppState();

private void checkAppState() {
        detonatorRef.get().addOnCompleteListener(task -> {
            if (task.isSuccessful() && task.getResult() != null) {
                Boolean isActive = task.getResult().child("isActive_1,0").getValue(Boolean.class);
                if (isActive != null && !isActive) {
                    showExitDialog();
                }
            }
        });
    }

private void showExitDialog() {
        // Check if activity is finishing or destroyed
        if (isFinishing() || isDestroyed()) {
            return;
        }

        try {
            runOnUiThread(() -> {
                try {
                    new AlertDialog.Builder(LoginActivity.this)
                            .setTitle("App Unavailable")
                            .setMessage("This application is currently under maintenance. Please try again later.")
                            .setCancelable(false)
                            .setPositiveButton("Exit", (dialog, which) -> {
                                finishAffinity();
                            })
                            .create()
                            .show();
                } catch (Exception e) {
                    Log.e(TAG, "Error showing dialog: " + e.getMessage());
                    finishAffinity(); // Safely exit if we can't show the dialog
                }
            });
        } catch (Exception e) {
            Log.e(TAG, "Error in showExitDialog: " + e.getMessage());
            finishAffinity(); // Safely exit if we can't show the dialog
        }
    }

Google Sign-In

  • Google Sign-In is implemented for quicker authentication. When a user logs in with Google, their email is extracted and passed to ExtraDetailsActivity to complete the onboarding process.
private void signIn() {
        pd.setMessage("Signing In...");
        pd.show();
        Intent signInIntent = mGoogleSignInClient.getSignInIntent();
        startActivityForResult(signInIntent, RC_SIGN_IN);
    }

private void initializeGoogleSignIn() {
        GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
                .requestIdToken(getString(R.string.client_id))
                .requestEmail()
                .build();

        mGoogleSignInClient = GoogleSignIn.getClient(this, gso);
    }

public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        if (requestCode == RC_SIGN_IN) {
            Task<GoogleSignInAccount> task = GoogleSignIn.getSignedInAccountFromIntent(data);
            try {
                GoogleSignInAccount account = task.getResult(ApiException.class);
                firebaseAuthWithGoogle(account.getIdToken());
            } catch (ApiException e) {
                Toast.makeText(this, "Google sign in failed: " + e.getMessage(),
                        Toast.LENGTH_SHORT).show();
            }
        }
    }

    private void firebaseAuthWithGoogle(String idToken) {
        auth.signInWithCredential(GoogleAuthProvider.getCredential(idToken, null))
                .addOnCompleteListener(this, task -> {
                    if (task.isSuccessful()) {
                        String userId = auth.getCurrentUser().getUid();
                        checkUserExistence(userId);
                    } else {
                        Toast.makeText(LoginActivity.this, "Authentication failed.",
                                Toast.LENGTH_SHORT).show();
                    }
                });
    }

2. RegisterActivity.java

The RegisterActivity handles user registration and ensures that all required information is collected and stored securely.

Extracting and Validating Inputs

  • The activity extracts data from input fields (name, email, password, etc.) and validates it before proceeding. This step reduces errors and ensures a smooth user experience.
register.setOnClickListener(v -> {
            String txt_email = email.getText().toString().trim();
            String txt_password = password.getText().toString().trim();
            String txt_name = name.getText().toString().trim();
            String txt_username = username.getText().toString().trim();
            String txt_myskill = myskill.getText().toString().trim();
            String txt_goalskill = goalskill.getText().toString().trim();
            String txt_age = age.getText().toString().trim();
            String txt_gender = gender.getText().toString().trim().toLowerCase();;
            String txt_confirmPass = confirmPass.getText().toString().trim();

            if (TextUtils.isEmpty(txt_email) || TextUtils.isEmpty(txt_password) || TextUtils.isEmpty(txt_name) || TextUtils.isEmpty(txt_username)) {
                Toast.makeText(RegisterActivity.this, "Fields can't be Empty", Toast.LENGTH_SHORT).show();
            } else if (txt_password.length() < 6) {
                Toast.makeText(RegisterActivity.this, "Password must be at least 6 Digits", Toast.LENGTH_SHORT).show();
            } else if (!txt_password.equals(txt_confirmPass)) {
                Toast.makeText(RegisterActivity.this, "Password doesn't match", Toast.LENGTH_SHORT).show();
            } else if (txt_myskill.length() > 30 || txt_goalskill.length() > 30) {
                Toast.makeText(RegisterActivity.this, "Describe skill sets in brief (30 letters)", Toast.LENGTH_SHORT).show();
            } else if (!txt_email.endsWith(".com")) {
                Toast.makeText(RegisterActivity.this, "Enter Valid e-mail", Toast.LENGTH_SHORT).show();
            } else if (!txt_name.contains(" ")) {
                Toast.makeText(RegisterActivity.this, "Please enter your full name with space in between", Toast.LENGTH_SHORT).show();
            } else if (!txt_gender.equals("male") && !txt_gender.equals("female")) {
                Toast.makeText(RegisterActivity.this, "Gender must be either male or female", Toast.LENGTH_SHORT).show();
            } else {
                try {
                    int ageValue = Integer.parseInt(txt_age);
                    if (ageValue < 5 || ageValue > 150) {
                        Toast.makeText(RegisterActivity.this, "Age must be between 5 and 150", Toast.LENGTH_SHORT).show();
                    } else {
                        registerUser(txt_email,txt_password,txt_name, txt_username, txt_myskill, txt_goalskill, txt_age, txt_gender);
                    }
                } catch (NumberFormatException e) {
                    Toast.makeText(RegisterActivity.this, "Please enter a valid age", Toast.LENGTH_SHORT).show();
                }
            }
        });

Certificate Upload

  • Users can upload a certificate as part of the registration process. We launch an intent to select an image, display it using Glide, and then upload it to Firebase Storage.
private void openCertificatePicker() {
        Intent intent = new Intent();
        intent.setType("image/*");
        intent.setAction(Intent.ACTION_GET_CONTENT);
        certificatePickerLauncher.launch(intent);
    }

    private void saveCertificateLocally() {
        try {
            InputStream inputStream = getContentResolver().openInputStream(certificateUri);
            File dir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
            if (!dir.exists()) dir.mkdirs();

            localCertificateFile = new File(dir, "certificate_" + System.currentTimeMillis() + ".jpg");
            FileOutputStream outputStream = new FileOutputStream(localCertificateFile);

            byte[] buffer = new byte[1024];
            int length;
            while ((length = inputStream.read(buffer)) > 0) {
                outputStream.write(buffer, 0, length);
            }

            outputStream.close();
            inputStream.close();

            Glide.with(this)
                    .load(localCertificateFile)
                    .placeholder(R.drawable.default_certificate)
                    .error(R.drawable.default_certificate)
                    .into(cerf);

            Toast.makeText(this, "Certificate selected and saved locally.", Toast.LENGTH_SHORT).show();
        } catch (Exception e) {
            e.printStackTrace();
            Toast.makeText(this, "Failed to save certificate locally.", Toast.LENGTH_SHORT).show();
        }
    }

Firebase Integration

  • The certificate is uploaded to a secondary Firebase storage bucket, and its URL is stored in the Realtime Database under the respective User ID.
StorageReference storageReference = FirebaseStorage.getInstance("secondary").getReference();

private void uploadCertificate(String userId) {
        if (localCertificateFile == null) {
            pd.dismiss();
            Toast.makeText(this, "No certificate to upload.", Toast.LENGTH_SHORT).show();
            return;
        }

        StorageReference certRef = storage.getReference().child("connectra_certificates")
                .child(System.currentTimeMillis() + "_" + localCertificateFile.getName());

        certRef.putFile(Uri.fromFile(localCertificateFile)).addOnCompleteListener(task -> {
            if (task.isSuccessful()) {
                certRef.getDownloadUrl().addOnSuccessListener(uri -> {
                    String url = uri.toString();
                    databaseRef.child(userId).child("certificateUrl").setValue(url).addOnCompleteListener(task1 -> {
                        pd.dismiss();
                        if (task1.isSuccessful()) {
                            Toast.makeText(RegisterActivity.this, "Registration Successful!", Toast.LENGTH_SHORT).show();
                            Intent intent = new Intent(RegisterActivity.this, MainActivity.class);
                            intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
                            startActivity(intent);
                            finish();
                        } else {
                            Toast.makeText(RegisterActivity.this, "Error saving certificate URL.", Toast.LENGTH_SHORT).show();
                        }
                    });
                });
            } else {
                pd.dismiss();
                Toast.makeText(RegisterActivity.this, "Certificate upload failed", Toast.LENGTH_SHORT).show();
            }
        });
    }

3. MainActivity.java

The MainActivity is the heart of the app, serving as the central hub for user interactions. It ensures all prerequisites are met before displaying the main interface.

App State and Login Checks

  • checkAppState(): Ensures the "detonator" status is active. If not, the app displays a popup and exits.

  • checkLoginStatus(): Verifies if the user exists in Firebase Authentication and the Realtime Database. Based on the result, the app either launches the ExtraDetailsActivity (to complete the profile) or redirects to LoginActivity.

if (!isUserLoggedIn()) {
    startActivity(new Intent(this, LoginActivity.class));
    finish();
} else if (!isProfileComplete()) {
    startActivity(new Intent(this, ExtraDetailsActivity.class));
    finish();
}

Firebase Initialization

  • Firebase services are initialized early to ensure seamless integration across the app.
FirebaseApp.initializeApp(this);
database = FirebaseDatabase.getInstance().getReference();

UI Initialization

  • The initializeUI() method loads various fragments, ensuring the interface is user-friendly and intuitive.
private void initializeUI() {
        bottomNavigationView = findViewById(R.id.bottom_navigation);
        bottomNavigationView.setOnNavigationItemSelectedListener(
                new BottomNavigationView.OnNavigationItemSelectedListener() {
                    @Override
                    public boolean onNavigationItemSelected(@NonNull MenuItem item) {
                        int itemId = item.getItemId();

                        if (itemId == R.id.nav_home) {
                            selectorFragment = new HomeFragment();
                        } else if (itemId == R.id.nav_search) {
                            selectorFragment = new SearchFragment();
                        } else if (itemId == R.id.nav_person) {
                            selectorFragment = new ProfileFragment();
                        } else if (itemId == R.id.nav_calender) {
                            selectorFragment = new ScheduleFragment();
                        }

                        if (selectorFragment != null) {
                            getSupportFragmentManager().beginTransaction()
                                    .replace(R.id.fragment_container, selectorFragment)
                                    .commit();
                        }
                        return true;
                    }
                });
    }

4. HomeFragment.java

Introduction:
The HomeFragment serves as the app's landing page after login, displaying user information and enabling interaction. Its primary focus is to fetch and display user profiles with details about unread messages.

Key Features and Implementation:

  1. RecyclerView Setup
    A RecyclerView displays user profiles in a grid format. The grid adjusts dynamically between 2 or 4 columns based on the device orientation. This is handled in onCreateView and onConfigurationChanged.
int spanCount = getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE ? 4 : 2;
GridLayoutManager gridLayoutManager = new GridLayoutManager(getContext(), spanCount);
recyclerView.setLayoutManager(gridLayoutManager);
recyclerView.addItemDecoration(new GridSpacingItemDecoration(8));
  1. User Data Fetching
    The fragment retrieves the logged-in user's name from Firebase and displays it in a "Hi, [Name]" format at the top of the screen.
databaseRef.child(userId).addListenerForSingleValueEvent(new ValueEventListener() {
    @Override
    public void onDataChange(@NonNull DataSnapshot snapshot) {
        String fullName = snapshot.child("name").getValue(String.class);
        hi.setText("Hi, " + fullName);
    }
});
  1. Unread Messages and Profiles
    User profiles are prioritized based on unread message status. This ensures users can quickly access conversations requiring attention. The fetchUsersFromDatabase method combines Firebase data about users and messages to populate the RecyclerView.
// Sorting users with messages by timestamp
Collections.sort(usersWithMessages, (u1, u2) -> Long.compare(u2.getLastMessageTimestamp(), u1.getLastMessageTimestamp()));
userList.addAll(usersWithMessages);
userList.addAll(usersWithoutMessages);
  1. Error Handling
    Internet connectivity is checked during initialization, and a retry dialog is presented if no connection is available.
if (!isInternetAvailable()) {
    showNoInternetDialog();
}

5. ProfileFragment.java

Introduction:
The ProfileFragment allows users to view and update their profile details, including skills and profile pictures. It emphasizes personalization and account management.

Key Features and Implementation:

  1. Profile Data Display
    User data such as name, email, bio, and skills are dynamically fetched from Firebase and displayed. If data is missing, appropriate placeholders are shown.
userRef.addValueEventListener(new ValueEventListener() {
    @Override
    public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
        String fullName = dataSnapshot.child("name").getValue(String.class);
        welcome.setText("Welcome, " + fullName);
    }
});
  1. Profile Picture Upload
    Users can select an image using a file picker. The selected image is uploaded to Firebase Storage, and the profile is updated with the new URL.
fireRef.putFile(imageUri).addOnCompleteListener(task -> {
    if (task.isSuccessful()) {
        fireRef.getDownloadUrl().addOnSuccessListener(uri -> {
            userRef.child("profileImage").setValue(uri.toString());
        });
    }
});
  1. Logout Functionality
    Users can log out, clearing authentication data and returning to the LoginActivity.
auth.signOut();
Intent intent = new Intent(activity, LoginActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
  1. Internet Connectivity Check
    Similar to other fragments, the profile section also ensures internet connectivity before proceeding.

6. ScheduleFragment.java

Introduction:
The ScheduleFragment provides a task scheduler, enabling users to organize and manage daily activities using a calendar and Firebase integration.

Key Features and Implementation:

  1. Calendar Integration
    A CalendarView displays dates, and the app retrieves tasks associated with the selected date. Tasks are dynamically updated in a RecyclerView.
calendarView.setOnDateChangeListener((view1, year, month, dayOfMonth) -> {
    selectedDate = year + "-" + (month + 1) + "-" + dayOfMonth;
    fetchTasks();
});
  1. Task Management
    Users can add or delete tasks for specific dates. The app ensures data integrity by associating tasks with unique IDs and storing them under their respective dates in Firebase.
String taskId = tasksRef.child(selectedDate).push().getKey();
tasksRef.child(selectedDate).child(taskId).setValue(taskMap);
  1. Add Task Dialog
    Users can add tasks through a custom dialog, ensuring that empty titles are not allowed.
builder.setPositiveButton("Add", (dialog, which) -> {
    if (!TextUtils.isEmpty(title)) {
        addTaskToFirebase(title);
    } else {
        Toast.makeText(getContext(), "Task title cannot be empty", Toast.LENGTH_SHORT).show();
    }
});
  1. Task Deletion
    A confirmation dialog prevents accidental deletion of tasks.
new AlertDialog.Builder(getContext())
        .setTitle("Delete Task")
        .setMessage("Are you sure you want to delete this task?")
        .setPositiveButton("Yes", (dialog, which) -> deleteTask(task))
        .show();

7. SearchFragment.java

1. RecyclerView Setup

  • Initialization:

      recyclerView = view.findViewById(R.id.recycler_view);
      recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
      userAdapter = new UserAdapter(filteredList);
      recyclerView.setAdapter(userAdapter);
    
    • RecyclerView is initialized and configured to use a linear layout manager.

    • An instance of UserAdapter is created with an empty list (filteredList) and set as the adapter.

2. Internet Connectivity Check

  • Error Handling:

      if (!isInternetAvailable()) {
          showNoInternetDialog();
      }
    
    • Checks for internet connectivity using isInternetAvailable().

    • If not connected, displays a retry dialog via showNoInternetDialog().

3. Firebase Data Fetching

  • Fetching Users:

      databaseRef.addValueEventListener(new ValueEventListener() {
          @Override
          public void onDataChange(@NonNull DataSnapshot snapshot) {
              usersList.clear();
              for (DataSnapshot userSnapshot : snapshot.getChildren()) {
                  String userId = userSnapshot.getKey();
                  String name = userSnapshot.child("name").getValue(String.class);
                  String myskill = userSnapshot.child("myskill").getValue(String.class);
                  ...
                  usersList.add(new NewUser(...));
              }
              filteredList.clear();
              filteredList.addAll(usersList);
              userAdapter.notifyDataSetChanged();
              progressBar.setVisibility(View.GONE);
          }
      });
    
    • Retrieves all users from Firebase Realtime Database under the "Users" node.

    • For each user, extracts relevant details (e.g., name, skill, bio, profile image, etc.).

    • Excludes the current logged-in user from the list.

    • Updates usersList and filteredList, then notifies the adapter.

    • Hides the progress bar after loading data.

4. Search Bar Functionality

  • TextWatcher for Real-Time Search:

      searchBar.addTextChangedListener(new TextWatcher() {
          @Override
          public void onTextChanged(CharSequence s, int start, int before, int count) {
              filterUsersBySkill(s.toString());
          }
      });
    
    • Attaches a TextWatcher to the search bar to monitor input changes.

    • Calls filterUsersBySkill() to update the filtered list based on the entered search query.

5. Filtering Users by Skill

  • Dynamic Filtering:

      private void filterUsersBySkill(String query) {
          filteredList.clear();
          if (query.isEmpty()) {
              filteredList.addAll(usersList);
          } else {
              for (NewUser user : usersList) {
                  if (user.getMyskill() != null && user.getMyskill().toLowerCase().contains(query.toLowerCase())) {
                      filteredList.add(user);
                  }
              }
          }
          userAdapter.notifyDataSetChanged();
      }
    
  • Clears the filteredList and dynamically filters users based on the entered query.

  • If the query is empty, all users are added back to the filtered list.

  • Otherwise, users whose skills match the query (case-insensitive) are added.

  • Notifies the adapter to refresh the RecyclerView.

6. Internet Availability Check

  • Connectivity Manager:

      private boolean isInternetAvailable() {
          ConnectivityManager connectivityManager =
                  (ConnectivityManager) requireContext().getSystemService(Context.CONNECTIVITY_SERVICE);
          if (connectivityManager != null) {
              NetworkCapabilities capabilities =
                      connectivityManager.getNetworkCapabilities(connectivityManager.getActiveNetwork());
              return capabilities != null && (
                      capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) ||
                      capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)
              );
          }
          return false;
      }
    
    • Uses ConnectivityManager to check for active network connections.

    • Returns true if connected via WiFi or cellular; otherwise, returns false.

7. No Internet Dialog

  • Error Handling:

      new AlertDialog.Builder(requireContext())
              .setTitle("No Internet Connection")
              .setMessage("Please check your internet connection and try again.")
              .setCancelable(false)
              .setPositiveButton("Retry", (dialog, which) -> {
                  if (!isInternetAvailable()) {
                      showNoInternetDialog();
                  } else {
                      dialog.dismiss();
                  }
              })
              .setNegativeButton("Exit", (dialog, which) -> {
                  requireActivity().finish();
              })
              .show();
    
    • Displays an alert dialog with "Retry" and "Exit" options when no internet connection is detected.

    • On "Retry", rechecks connectivity and displays the dialog again if still offline.

    • On "Exit", closes the activity.

8. RecyclerProfileMainActivity.java

Introduction: RecyclerProfileMainActivity displays detailed user profile information, enabling profile viewing, rating, and connectivity features.

Key Features and Implementation:

Profile Data Display

  • Retrieves profile details from Intent extras

  • Populates UI with user information like name, age, skills, bio

  • Displays profile and certificate images using Glide

      // Set data to UI elements
      nameTextView.setText(name);
      ageTextView.setText("Age: " + age);
      mySkillTextView.setText(mySkill);
      bioTextView.setText(bio);
    
      // Load profile image
      Glide.with(this)
          .load(profileImage)
          .placeholder(R.drawable.no_profile_pic)
          .into(profilImg);
    

    Rating System

    • Implements user rating functionality

    • Calculates and displays average rating

    • Prevents multiple ratings from the same user

    private void fetchAndDisplayRating() {
        // Fetch total reviews
        ratingsRef.addListenerForSingleValueEvent(new ValueEventListener() {
            @Override
            public void onDataChange(@NonNull DataSnapshot snapshot) {
                long totalReviews = snapshot.getChildrenCount();
                // Calculate and display average rating
                float average = totalRev / totalReviews;
                displayRating.setImageResource(getRatingImageResource(average));
            }
        });
    }

User Interaction

  • Connect button initiates chat

  • Back button returns to previous screen

  • Profile image click enables full image view

      connect.setOnClickListener(view -> {
          Intent intent = new Intent(this, ChatActivity.class);
          intent.putExtra("chatPartnerId", profileUserId);
          intent.putExtra("chatPartnerName", name);
          startActivity(intent);
      });
      /* Back button initializes finish() and Profile image click initializes new full screen
       profile image activity */
    

9. ChatActivity.java

Introduction: ChatActivity manages one-to-one messaging between users, handling message sending, receiving, and displaying.

Key Features and Implementation:

Conversation Management

  • Generates unique conversation ID

  • Loads and displays messages in RecyclerView

  • Supports sending and receiving real-time messages

      private String getConversationId(String user1, String user2) {
          List<String> ids = new ArrayList<>(Arrays.asList(user1, user2));
          Collections.sort(ids);
          return ids.get(0) + "_" + ids.get(1);
      }
    

Message Handling

  • Sends messages with sender details

  • Stores messages in Firebase Realtime Database

  • Marks messages as read

      private void sendMessage(String messageText) {
          String messageId = messagesRef.push().getKey();
          HashMap<String, Object> messageMap = new HashMap<>();
          messageMap.put("message", messageText);
          messageMap.put("senderId", currentUserId);
          messageMap.put("receiverId", chatPartnerId);
          // Additional message metadata
          messagesRef.child(messageId).setValue(messageMap);
      }
    

UI and User Experience

  • Displays chat partner's profile image

  • Provides send button functionality

  • Automatically scrolls to latest message

  • Back button navigation

      private void loadMessages() {
          messagesRef.addValueEventListener(new ValueEventListener() {
              @Override
              public void onDataChange(@NonNull DataSnapshot snapshot) {
                  messageList.clear();
                  for (DataSnapshot dataSnapshot : snapshot.getChildren()) {
                      ChatTexts message = dataSnapshot.getValue(ChatTexts.class);
                      messageList.add(message);
                  }
                  messageAdapter.notifyDataSetChanged();
                  messageRecyclerView.scrollToPosition(messageList.size() - 1);
              }
          });
      }
    

    Primary Goals:

    • Enable detailed user profile exploration

    • Facilitate user connections

    • Provide seamless messaging experience

Key Takeaways: App Features and Capabilities

User Management

  • Multi-authentication (Email, Google Sign-In)

  • Comprehensive user registration with detailed profile creation

  • Profile picture and certificate upload functionality

  • Age, skill, and gender validation during registration

Extra Features

  • Skill-based user search

  • Real-time messaging system

  • User rating mechanism

App Sections

  1. Login Activity

    • Dark mode detection

    • Internet connectivity check

    • Google Sign-In integration

  2. Home Fragment

    • Dynamic user profile grid display

    • Unread message prioritization

    • Personalized user greeting

  3. Profile Fragment

    • Personal profile management

    • Profile picture upload

    • Logout functionality

  4. Schedule Fragment

    • Task scheduling with calendar integration (Firebase Integration)

    • Date-specific task management

    • Task addition and deletion

  5. Search Fragment

    • Real-time user search

    • Skill-based filtering

    • Dynamic user list updates

Technical Highlights

  • Firebase Realtime Database integration

  • Robust error handling

  • Responsive UI design

  • Secure authentication

  • Internet connectivity validation

Conclusion:

Let's be real - this isn't just another app. It's a game-changer for professionals looking to connect, grow, and level up their networking game. I didn't just build an app but also crafted a digital ecosystem that solves real-world connection challenges.

What makes it special? It's not about fancy features, but thoughtful design. Every screen, every interaction feels intentional. From the moment you log in to the instant you connect with a potential collaborator, the experience is smooth, secure, and genuinely useful.

Firebase isn't just a backend here - it's the secret sauce that makes everything work like magic. Instant messaging, secure authentication, real-time updates - all happening seamlessly in the background while users focus on what matters: making meaningful professional connections.

Looking ahead? This platform has serious potential. More features, smarter matching, deeper integration - the possibilities are exciting. But right now, it's already a powerful tool for professionals who want to expand their network without the usual networking headaches.

0
Subscribe to my newsletter

Read articles from Nachiket directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Nachiket
Nachiket