From 4b766067f43696f249659cd1bf337f7f69be5761 Mon Sep 17 00:00:00 2001 From: 230421 <230421@epvc.pt> Date: Sat, 6 Dec 2025 22:26:42 +0000 Subject: [PATCH] melhoramento da app --- .../example/vdcscore/CriarContaActivity.java | 27 +- .../com/example/vdcscore/MainActivity.java | 18 +- .../vdcscore/ui/definicoes/ContaActivity.java | 310 ++++++++++-------- app/src/main/res/layout/activity_conta.xml | 108 +++++- .../main/res/layout/activity_criarconta.xml | 30 ++ app/src/main/res/layout/nav_header_main.xml | 12 +- 6 files changed, 332 insertions(+), 173 deletions(-) diff --git a/app/src/main/java/com/example/vdcscore/CriarContaActivity.java b/app/src/main/java/com/example/vdcscore/CriarContaActivity.java index 487fc44..4f9b52f 100644 --- a/app/src/main/java/com/example/vdcscore/CriarContaActivity.java +++ b/app/src/main/java/com/example/vdcscore/CriarContaActivity.java @@ -30,12 +30,14 @@ public class CriarContaActivity extends AppCompatActivity { }); TextInputEditText editPassword2; TextInputEditText editEmail; + TextInputEditText editUserName; TextInputEditText editConfirmPassword; Button btnCreateAccount; TextView txtGoLogin; editEmail = findViewById(R.id.editEmail); + editUserName = findViewById(R.id.editUserName); editPassword2 = findViewById(R.id.editPassword2); editConfirmPassword = findViewById(R.id.editConfirmPassword); btnCreateAccount = findViewById(R.id.btnCreateAccount); @@ -46,10 +48,11 @@ public class CriarContaActivity extends AppCompatActivity { @Override public void onClick(View v) { String email = editEmail.getText().toString().trim(); + String userName = editUserName.getText().toString().trim(); String pass = editPassword2.getText().toString().trim(); String conf = editConfirmPassword.getText().toString().trim(); - if (email.isEmpty() || pass.isEmpty() || conf.isEmpty()) { + if (email.isEmpty() || userName.isEmpty() || pass.isEmpty() || conf.isEmpty()) { Toast.makeText(CriarContaActivity.this, "Preencha todos os campos!", Toast.LENGTH_SHORT).show(); return; } @@ -62,10 +65,24 @@ public class CriarContaActivity extends AppCompatActivity { FirebaseAuth auth = FirebaseAuth.getInstance(); auth.createUserWithEmailAndPassword(email, pass).addOnCompleteListener(CriarContaActivity.this, task -> { if (task.isSuccessful()) { - - Intent intent= new Intent(CriarContaActivity.this, MainActivity.class); - startActivity(intent); - finish(); + // Atualizar nome de utilizador + com.google.firebase.auth.FirebaseUser user = auth.getCurrentUser(); + if (user != null) { + com.google.firebase.auth.UserProfileChangeRequest profileUpdates = + new com.google.firebase.auth.UserProfileChangeRequest.Builder() + .setDisplayName(userName) + .build(); + + user.updateProfile(profileUpdates).addOnCompleteListener(updateTask -> { + Intent intent = new Intent(CriarContaActivity.this, MainActivity.class); + startActivity(intent); + finish(); + }); + } else { + Intent intent = new Intent(CriarContaActivity.this, MainActivity.class); + startActivity(intent); + finish(); + } } else { String errorMessage = "Erro ao criar conta!"; if (task.getException() != null && task.getException().getMessage() != null) { diff --git a/app/src/main/java/com/example/vdcscore/MainActivity.java b/app/src/main/java/com/example/vdcscore/MainActivity.java index 47904c2..63f2809 100644 --- a/app/src/main/java/com/example/vdcscore/MainActivity.java +++ b/app/src/main/java/com/example/vdcscore/MainActivity.java @@ -67,7 +67,6 @@ public class MainActivity extends AppCompatActivity { View headerView = binding.navView.getHeaderView(0); TextView textViewName = headerView.findViewById(R.id.textViewName); - TextView textViewEmail = headerView.findViewById(R.id.textView); ImageView imageView = headerView.findViewById(R.id.imageView); // Carregar nome @@ -79,17 +78,14 @@ public class MainActivity extends AppCompatActivity { } } - // Carregar email - if (textViewEmail != null && user.getEmail() != null) { - textViewEmail.setText(user.getEmail()); - } - // Carregar foto de perfil - if (imageView != null && user.getPhotoUrl() != null) { - loadProfileImageInNavHeader(user.getPhotoUrl().toString(), imageView); - } else { - // Tentar carregar do Storage - loadProfileImageFromStorage(imageView); + if (imageView != null) { + if (user.getPhotoUrl() != null) { + loadProfileImageInNavHeader(user.getPhotoUrl().toString(), imageView); + } else { + // Tentar carregar do Storage + loadProfileImageFromStorage(imageView); + } } } diff --git a/app/src/main/java/com/example/vdcscore/ui/definicoes/ContaActivity.java b/app/src/main/java/com/example/vdcscore/ui/definicoes/ContaActivity.java index b039dfb..76a1c12 100644 --- a/app/src/main/java/com/example/vdcscore/ui/definicoes/ContaActivity.java +++ b/app/src/main/java/com/example/vdcscore/ui/definicoes/ContaActivity.java @@ -1,6 +1,7 @@ package com.example.vdcscore.ui.definicoes; import android.Manifest; +import android.app.AlertDialog; import android.app.ProgressDialog; import android.content.Intent; import android.content.SharedPreferences; @@ -46,8 +47,10 @@ public class ContaActivity extends AppCompatActivity { private TextInputEditText editName; private TextInputEditText editImageUrl; private TextView textEmail; - private Button btnSaveName; + private Button btnSaveAll; private Button btnLoadImageUrl; + private Button btnChangeEmail; + private Button btnChangePassword; private Button btnLogout; private View btnChangePhoto; private View cardImageContainer; @@ -60,6 +63,7 @@ public class ContaActivity extends AppCompatActivity { private ActivityResultLauncher imagePickerLauncher; private ActivityResultLauncher permissionLauncher; private Uri selectedImageUri; + private String imageUrlFromGoogle; private static final String PREFS_NAME = "LoginPrefs"; @@ -95,8 +99,10 @@ public class ContaActivity extends AppCompatActivity { editName = findViewById(R.id.editName); editImageUrl = findViewById(R.id.editImageUrl); textEmail = findViewById(R.id.textEmail); - btnSaveName = findViewById(R.id.btnSaveName); + btnSaveAll = findViewById(R.id.btnSaveAll); btnLoadImageUrl = findViewById(R.id.btnLoadImageUrl); + btnChangeEmail = findViewById(R.id.btnChangeEmail); + btnChangePassword = findViewById(R.id.btnChangePassword); btnLogout = findViewById(R.id.btnLogout); btnChangePhoto = findViewById(R.id.btnChangePhoto); cardImageContainer = findViewById(R.id.cardImageContainer); @@ -180,8 +186,6 @@ public class ContaActivity extends AppCompatActivity { } private void loadProfileImage(String imageUrl) { - // Usar uma biblioteca de imagens seria ideal aqui (Glide/Picasso) - // Por agora, vamos usar uma abordagem simples com Thread new Thread(() -> { InputStream input = null; try { @@ -197,7 +201,6 @@ public class ContaActivity extends AppCompatActivity { } }); } catch (Exception e) { - // Se falhar, tentar carregar do Storage runOnUiThread(() -> loadProfileImageFromStorage()); } finally { if (input != null) { @@ -230,17 +233,38 @@ public class ContaActivity extends AppCompatActivity { if (cardImageContainer != null) { cardImageContainer.setOnClickListener(v -> openImagePicker()); } - if (btnSaveName != null) { - btnSaveName.setOnClickListener(v -> saveUserName()); + if (btnSaveAll != null) { + btnSaveAll.setOnClickListener(v -> saveAllChanges()); } if (btnLoadImageUrl != null) { btnLoadImageUrl.setOnClickListener(v -> loadImageFromUrl()); } + if (btnChangeEmail != null) { + btnChangeEmail.setOnClickListener(v -> sendEmailVerificationForEmailChange()); + } + if (btnChangePassword != null) { + btnChangePassword.setOnClickListener(v -> sendPasswordResetEmail()); + } if (btnLogout != null) { - btnLogout.setOnClickListener(v -> logoutUser()); + btnLogout.setOnClickListener(v -> showLogoutConfirmation()); } } - + + private void openImagePicker() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + Intent intent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI); + imagePickerLauncher.launch(intent); + } else { + String permission = Manifest.permission.READ_EXTERNAL_STORAGE; + if (ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED) { + Intent intent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI); + imagePickerLauncher.launch(intent); + } else { + permissionLauncher.launch(permission); + } + } + } + private void loadImageFromUrl() { if (editImageUrl == null) return; @@ -272,8 +296,10 @@ public class ContaActivity extends AppCompatActivity { progressDialog.dismiss(); if (bitmap != null && imageProfile != null) { imageProfile.setImageBitmap(bitmap); - // Converter bitmap para URI e fazer upload - uploadImageFromBitmap(bitmap); + // Guardar URL para usar diretamente no perfil + imageUrlFromGoogle = imageUrl; + // Atualizar perfil com a URL diretamente + updateProfileWithImageUrl(imageUrl); } else { Toast.makeText(this, "Erro ao carregar imagem da URL", Toast.LENGTH_SHORT).show(); } @@ -291,13 +317,60 @@ public class ContaActivity extends AppCompatActivity { }).start(); } - private void uploadImageFromBitmap(Bitmap bitmap) { + private void updateProfileWithImageUrl(String imageUrl) { FirebaseUser user = mAuth.getCurrentUser(); if (user == null) return; progressDialog.show(); try { + Uri imageUri = Uri.parse(imageUrl); + UserProfileChangeRequest profileUpdates = new UserProfileChangeRequest.Builder() + .setPhotoUri(imageUri) + .build(); + + user.updateProfile(profileUpdates).addOnCompleteListener(task -> { + progressDialog.dismiss(); + if (task.isSuccessful()) { + Toast.makeText(this, "Foto de perfil atualizada com sucesso!", Toast.LENGTH_SHORT).show(); + editImageUrl.setText(""); + loadProfileImage(imageUrl); + } else { + Toast.makeText(this, "Erro ao atualizar foto: " + + (task.getException() != null ? task.getException().getMessage() : "Erro desconhecido"), + Toast.LENGTH_SHORT).show(); + } + }); + } catch (Exception e) { + progressDialog.dismiss(); + Toast.makeText(this, "Erro ao processar URL: " + e.getMessage(), Toast.LENGTH_SHORT).show(); + } + } + + private void uploadProfileImage() { + if (selectedImageUri == null) return; + + FirebaseUser user = mAuth.getCurrentUser(); + if (user == null) return; + + progressDialog.show(); + + InputStream inputStream = null; + try { + inputStream = getContentResolver().openInputStream(selectedImageUri); + if (inputStream == null) { + progressDialog.dismiss(); + Toast.makeText(this, "Erro ao abrir imagem", Toast.LENGTH_SHORT).show(); + return; + } + + Bitmap bitmap = BitmapFactory.decodeStream(inputStream); + if (bitmap == null) { + progressDialog.dismiss(); + Toast.makeText(this, "Erro ao processar imagem", Toast.LENGTH_SHORT).show(); + return; + } + // Redimensionar se muito grande int maxSize = 1024; if (bitmap.getWidth() > maxSize || bitmap.getHeight() > maxSize) { @@ -321,121 +394,6 @@ public class ContaActivity extends AppCompatActivity { .setPhotoUri(uri) .build(); - user.updateProfile(profileUpdates).addOnCompleteListener(task -> { - progressDialog.dismiss(); - if (task.isSuccessful()) { - Toast.makeText(this, "Foto de perfil atualizada com sucesso!", Toast.LENGTH_SHORT).show(); - editImageUrl.setText(""); - loadProfileImage(uri.toString()); - } else { - Toast.makeText(this, "Erro ao atualizar foto: " + - (task.getException() != null ? task.getException().getMessage() : "Erro desconhecido"), - Toast.LENGTH_SHORT).show(); - } - }); - }).addOnFailureListener(e -> { - progressDialog.dismiss(); - Toast.makeText(this, "Erro ao obter URL da imagem: " + e.getMessage(), Toast.LENGTH_SHORT).show(); - }); - }).addOnFailureListener(e -> { - progressDialog.dismiss(); - Toast.makeText(this, "Erro ao fazer upload: " + e.getMessage(), Toast.LENGTH_SHORT).show(); - }); - - } catch (Exception e) { - progressDialog.dismiss(); - Toast.makeText(this, "Erro ao processar imagem: " + e.getMessage(), Toast.LENGTH_SHORT).show(); - } - } - - private void logoutUser() { - // Limpar credenciais guardadas - SharedPreferences prefs = getSharedPreferences(PREFS_NAME, MODE_PRIVATE); - SharedPreferences.Editor editor = prefs.edit(); - editor.clear(); - editor.apply(); - - // Fazer logout do Firebase - mAuth.signOut(); - - Toast.makeText(this, "Sessão terminada", Toast.LENGTH_SHORT).show(); - - // Voltar para o login - Intent intent = new Intent(ContaActivity.this, LoginActivity.class); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); - startActivity(intent); - finish(); - } - - private void openImagePicker() { - // No Android 13+ (API 33+), ACTION_PICK não requer permissão - // Mas vamos verificar e pedir permissão se necessário para versões anteriores - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - // Android 13+ - não precisa de permissão para ACTION_PICK - Intent intent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI); - imagePickerLauncher.launch(intent); - } else { - // Android 12 e anteriores - precisa de permissão - String permission = Manifest.permission.READ_EXTERNAL_STORAGE; - if (ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED) { - Intent intent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI); - imagePickerLauncher.launch(intent); - } else { - permissionLauncher.launch(permission); - } - } - } - - private void uploadProfileImage() { - if (selectedImageUri == null) return; - - FirebaseUser user = mAuth.getCurrentUser(); - if (user == null) return; - - progressDialog.show(); - - InputStream inputStream = null; - try { - // Comprimir imagem - inputStream = getContentResolver().openInputStream(selectedImageUri); - if (inputStream == null) { - progressDialog.dismiss(); - Toast.makeText(this, "Erro ao abrir imagem", Toast.LENGTH_SHORT).show(); - return; - } - - Bitmap bitmap = BitmapFactory.decodeStream(inputStream); - if (bitmap == null) { - progressDialog.dismiss(); - Toast.makeText(this, "Erro ao processar imagem", Toast.LENGTH_SHORT).show(); - return; - } - - // Redimensionar se muito grande (max 1024x1024) - int maxSize = 1024; - if (bitmap.getWidth() > maxSize || bitmap.getHeight() > maxSize) { - float scale = Math.min((float) maxSize / bitmap.getWidth(), (float) maxSize / bitmap.getHeight()); - int newWidth = Math.round(bitmap.getWidth() * scale); - int newHeight = Math.round(bitmap.getHeight() * scale); - bitmap = Bitmap.createScaledBitmap(bitmap, newWidth, newHeight, true); - } - - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - bitmap.compress(Bitmap.CompressFormat.JPEG, 80, baos); - byte[] imageData = baos.toByteArray(); - - // Upload para Firebase Storage - StorageReference profileImageRef = storageRef.child("profile_images/" + user.getUid() + ".jpg"); - UploadTask uploadTask = profileImageRef.putBytes(imageData); - - uploadTask.addOnSuccessListener(taskSnapshot -> { - // Obter URL da imagem - profileImageRef.getDownloadUrl().addOnSuccessListener(uri -> { - // Atualizar perfil do utilizador com a URL da foto - UserProfileChangeRequest profileUpdates = new UserProfileChangeRequest.Builder() - .setPhotoUri(uri) - .build(); - user.updateProfile(profileUpdates).addOnCompleteListener(task -> { progressDialog.dismiss(); if (task.isSuccessful()) { @@ -470,7 +428,7 @@ public class ContaActivity extends AppCompatActivity { } } - private void saveUserName() { + private void saveAllChanges() { if (editName == null) return; String newName = editName.getText().toString().trim(); @@ -495,12 +453,104 @@ public class ContaActivity extends AppCompatActivity { user.updateProfile(profileUpdates).addOnCompleteListener(task -> { progressDialog.dismiss(); if (task.isSuccessful()) { - Toast.makeText(this, "Nome atualizado com sucesso!", Toast.LENGTH_SHORT).show(); + Toast.makeText(this, "Alterações guardadas com sucesso!", Toast.LENGTH_SHORT).show(); } else { - Toast.makeText(this, "Erro ao atualizar nome: " + + Toast.makeText(this, "Erro ao guardar alterações: " + (task.getException() != null ? task.getException().getMessage() : "Erro desconhecido"), Toast.LENGTH_SHORT).show(); } }); } + + private void sendPasswordResetEmail() { + FirebaseUser user = mAuth.getCurrentUser(); + if (user == null || user.getEmail() == null) { + Toast.makeText(this, "Utilizador não autenticado", Toast.LENGTH_SHORT).show(); + return; + } + + progressDialog.show(); + + mAuth.sendPasswordResetEmail(user.getEmail()) + .addOnCompleteListener(task -> { + progressDialog.dismiss(); + if (task.isSuccessful()) { + Toast.makeText(this, + "Email de alteração de password enviado! Verifique a sua caixa de entrada.", + Toast.LENGTH_LONG).show(); + } else { + String errorMessage = "Erro ao enviar email!"; + if (task.getException() != null && task.getException().getMessage() != null) { + errorMessage = "Erro: " + task.getException().getMessage(); + } + Toast.makeText(this, errorMessage, Toast.LENGTH_LONG).show(); + } + }); + } + + private void sendEmailVerificationForEmailChange() { + FirebaseUser user = mAuth.getCurrentUser(); + if (user == null || user.getEmail() == null) { + Toast.makeText(this, "Utilizador não autenticado", Toast.LENGTH_SHORT).show(); + return; + } + + progressDialog.show(); + + // Enviar email de verificação para alterar email + user.sendEmailVerification() + .addOnCompleteListener(task -> { + progressDialog.dismiss(); + if (task.isSuccessful()) { + Toast.makeText(this, + "Email de verificação enviado! Verifique a sua caixa de entrada para alterar o email.", + Toast.LENGTH_LONG).show(); + } else { + // Se não conseguir enviar email de verificação, enviar email de reset de password + // que pode ser usado para alterar o email através do Firebase Console + mAuth.sendPasswordResetEmail(user.getEmail()) + .addOnCompleteListener(resetTask -> { + if (resetTask.isSuccessful()) { + Toast.makeText(this, + "Email enviado! Verifique a sua caixa de entrada para instruções de alteração.", + Toast.LENGTH_LONG).show(); + } else { + String errorMessage = "Erro ao enviar email!"; + if (resetTask.getException() != null && resetTask.getException().getMessage() != null) { + errorMessage = "Erro: " + resetTask.getException().getMessage(); + } + Toast.makeText(this, errorMessage, Toast.LENGTH_LONG).show(); + } + }); + } + }); + } + + private void showLogoutConfirmation() { + new AlertDialog.Builder(this) + .setTitle("Terminar Sessão") + .setMessage("Tem a certeza que deseja terminar a sessão?") + .setPositiveButton("Sim", (dialog, which) -> logoutUser()) + .setNegativeButton("Cancelar", null) + .show(); + } + + private void logoutUser() { + // Limpar credenciais guardadas + SharedPreferences prefs = getSharedPreferences(PREFS_NAME, MODE_PRIVATE); + SharedPreferences.Editor editor = prefs.edit(); + editor.clear(); + editor.apply(); + + // Fazer logout do Firebase + mAuth.signOut(); + + Toast.makeText(this, "Sessão terminada", Toast.LENGTH_SHORT).show(); + + // Voltar para o login + Intent intent = new Intent(ContaActivity.this, LoginActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); + startActivity(intent); + finish(); + } } diff --git a/app/src/main/res/layout/activity_conta.xml b/app/src/main/res/layout/activity_conta.xml index 89a9df2..ebc72d3 100644 --- a/app/src/main/res/layout/activity_conta.xml +++ b/app/src/main/res/layout/activity_conta.xml @@ -184,7 +184,7 @@ - -