diff --git a/app/src/main/java/com/example/vdcscore/MainActivity.java b/app/src/main/java/com/example/vdcscore/MainActivity.java index a2fd73f..3ce2585 100644 --- a/app/src/main/java/com/example/vdcscore/MainActivity.java +++ b/app/src/main/java/com/example/vdcscore/MainActivity.java @@ -46,6 +46,18 @@ public class MainActivity extends AppCompatActivity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + // Carregar preferência do Dark Mode + android.content.SharedPreferences prefs = getSharedPreferences("SettingsPrefs", + android.content.Context.MODE_PRIVATE); + boolean isDarkMode = prefs.getBoolean("dark_mode", false); + if (isDarkMode) { + androidx.appcompat.app.AppCompatDelegate + .setDefaultNightMode(androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_YES); + } else { + androidx.appcompat.app.AppCompatDelegate + .setDefaultNightMode(androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_NO); + } + binding = ActivityMainBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); @@ -155,37 +167,7 @@ public class MainActivity extends AppCompatActivity { } private void showChangeProfilePhotoDialog() { - AlertDialog.Builder builder = new AlertDialog.Builder(this); - builder.setTitle("Alterar Foto de Perfil"); - String[] options = { "Escolher da Galeria", "Inserir URL" }; - builder.setItems(options, (dialog, which) -> { - if (which == 0) { - mGetContent.launch("image/*"); - } else { - showUrlDialog(); - } - }); - builder.show(); - } - - private void showUrlDialog() { - AlertDialog.Builder builder = new AlertDialog.Builder(this); - builder.setTitle("Inserir URL da Imagem"); - - final EditText input = new EditText(this); - input.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_URI); - input.setHint("https://exemplo.com/foto.jpg"); - builder.setView(input); - - builder.setPositiveButton("OK", (dialog, which) -> { - String url = input.getText().toString(); - if (!url.isEmpty()) { - updateUserProfile(Uri.parse(url)); - } - }); - builder.setNegativeButton("Cancelar", (dialog, which) -> dialog.cancel()); - - builder.show(); + mGetContent.launch("image/*"); } private void uploadImageToStorage(Uri imageUri) { 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 3cf664a..400d1f4 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 @@ -46,10 +46,8 @@ public class ContaActivity extends AppCompatActivity { private ImageView imageProfile; private TextInputEditText editName; - private TextInputEditText editImageUrl; private TextView textEmail; private Button btnSaveAll; - private Button btnLoadImageUrl; private Button btnChangeEmail; private Button btnChangePassword; private Button btnLogout; @@ -64,7 +62,6 @@ public class ContaActivity extends AppCompatActivity { private ActivityResultLauncher imagePickerLauncher; private ActivityResultLauncher permissionLauncher; private Uri selectedImageUri; - private String imageUrlFromGoogle; private static final String PREFS_NAME = "LoginPrefs"; @@ -98,13 +95,10 @@ public class ContaActivity extends AppCompatActivity { imageProfile = findViewById(R.id.imageProfile); editName = findViewById(R.id.editName); - editImageUrl = findViewById(R.id.editImageUrl); - // textEmail removed from class fields if not used, or kept if used. // In XML it is present as @+id/textEmail inside cardSecurity. textEmail = findViewById(R.id.textEmail); 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); @@ -146,7 +140,7 @@ public class ContaActivity extends AppCompatActivity { inputStream.close(); if (bitmap != null) { imageProfile.setImageBitmap(bitmap); - uploadProfileImage(); + // Não fazemos upload até clicar em Gravar } else { Toast.makeText(this, "Erro ao carregar imagem", Toast.LENGTH_SHORT).show(); } @@ -250,9 +244,6 @@ public class ContaActivity extends AppCompatActivity { if (btnSaveAll != null) { btnSaveAll.setOnClickListener(v -> saveAllChanges()); } - if (btnLoadImageUrl != null) { - btnLoadImageUrl.setOnClickListener(v -> loadImageFromUrl()); - } if (btnChangeEmail != null) { btnChangeEmail.setOnClickListener(v -> sendEmailVerificationForEmailChange()); } @@ -279,100 +270,57 @@ public class ContaActivity extends AppCompatActivity { } } - private void loadImageFromUrl() { - if (editImageUrl == null) + private void saveAllChanges() { + if (editName == null) return; - String imageUrl = editImageUrl.getText().toString().trim(); + String newName = editName.getText().toString().trim(); - if (imageUrl.isEmpty()) { - Toast.makeText(this, "Por favor, insira uma URL válida", Toast.LENGTH_SHORT).show(); + if (newName.isEmpty() && selectedImageUri == null) { + Toast.makeText(this, "Nenhuma alteração a guardar", Toast.LENGTH_SHORT).show(); return; } - if (!imageUrl.startsWith("http://") && !imageUrl.startsWith("https://")) { - Toast.makeText(this, "URL inválida. Deve começar com http:// ou https://", Toast.LENGTH_SHORT).show(); + FirebaseUser user = mAuth.getCurrentUser(); + if (user == null) { + Toast.makeText(this, "Utilizador não autenticado", Toast.LENGTH_SHORT).show(); return; } progressDialog.show(); - // Carregar imagem da URL - new Thread(() -> { - try { - java.net.URL url = new java.net.URL(imageUrl); - java.net.HttpURLConnection connection = (java.net.HttpURLConnection) url.openConnection(); - connection.setDoInput(true); - connection.connect(); - InputStream input = connection.getInputStream(); - Bitmap bitmap = BitmapFactory.decodeStream(input); - - runOnUiThread(() -> { - progressDialog.dismiss(); - if (bitmap != null && imageProfile != null) { - imageProfile.setImageBitmap(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(); - } - }); - - if (input != null) { - input.close(); - } - } catch (Exception e) { - runOnUiThread(() -> { - progressDialog.dismiss(); - Toast.makeText(this, "Erro ao carregar imagem: " + e.getMessage(), Toast.LENGTH_SHORT).show(); - }); - } - }).start(); + // Se tiver foto nova, primeiro faz upload + if (selectedImageUri != null) { + uploadNewProfileImageAndSaveName(user, newName); + } else { + // Só muda o nome + saveNameOnly(user, newName); + } } - 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) { + private void saveNameOnly(FirebaseUser user, String newName) { + if (newName.isEmpty()) { progressDialog.dismiss(); - Toast.makeText(this, "Erro ao processar URL: " + e.getMessage(), Toast.LENGTH_SHORT).show(); + return; // Se não houver nome para trocar nem imagem, já barrámos em saveAllChanges. } + + UserProfileChangeRequest profileUpdates = new UserProfileChangeRequest.Builder() + .setDisplayName(newName) + .build(); + + user.updateProfile(profileUpdates).addOnCompleteListener(task -> { + progressDialog.dismiss(); + if (task.isSuccessful()) { + Toast.makeText(this, "Alterações guardadas com sucesso!", Toast.LENGTH_SHORT).show(); + } else { + Toast.makeText(this, "Erro ao guardar alterações: " + + (task.getException() != null ? task.getException().getMessage() : "Erro desconhecido"), + Toast.LENGTH_SHORT).show(); + } + }); } - private void uploadProfileImage() { - if (selectedImageUri == null) - return; - - FirebaseUser user = mAuth.getCurrentUser(); - if (user == null) - return; - - progressDialog.show(); - + private void uploadNewProfileImageAndSaveName(FirebaseUser user, String newName) { InputStream inputStream = null; try { inputStream = getContentResolver().openInputStream(selectedImageUri); @@ -389,7 +337,7 @@ public class ContaActivity extends AppCompatActivity { return; } - // Redimensionar se muito grande + // Redimensionar int maxSize = 1024; if (bitmap.getWidth() > maxSize || bitmap.getHeight() > maxSize) { float scale = Math.min((float) maxSize / bitmap.getWidth(), (float) maxSize / bitmap.getHeight()); @@ -402,31 +350,32 @@ public class ContaActivity extends AppCompatActivity { 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 -> { profileImageRef.getDownloadUrl().addOnSuccessListener(uri -> { - UserProfileChangeRequest profileUpdates = new UserProfileChangeRequest.Builder() - .setPhotoUri(uri) - .build(); + + UserProfileChangeRequest.Builder builder = new UserProfileChangeRequest.Builder(); + builder.setPhotoUri(uri); + if (!newName.isEmpty()) { + builder.setDisplayName(newName); + } + UserProfileChangeRequest profileUpdates = builder.build(); user.updateProfile(profileUpdates).addOnCompleteListener(task -> { progressDialog.dismiss(); if (task.isSuccessful()) { - Toast.makeText(this, "Foto de perfil atualizada com sucesso!", Toast.LENGTH_SHORT).show(); + Toast.makeText(this, "As definições foram guardadas!", Toast.LENGTH_SHORT).show(); loadProfileImage(uri.toString()); + selectedImageUri = null; // reset } else { - Toast.makeText(this, "Erro ao atualizar foto: " + - (task.getException() != null ? task.getException().getMessage() - : "Erro desconhecido"), - Toast.LENGTH_SHORT).show(); + Toast.makeText(this, "Erro ao atualizar perfil", Toast.LENGTH_SHORT).show(); } }); }).addOnFailureListener(e -> { progressDialog.dismiss(); - Toast.makeText(this, "Erro ao obter URL da imagem: " + e.getMessage(), Toast.LENGTH_SHORT).show(); + Toast.makeText(this, "Erro ao obter URL: " + e.getMessage(), Toast.LENGTH_SHORT).show(); }); }).addOnFailureListener(e -> { progressDialog.dismiss(); @@ -435,53 +384,17 @@ public class ContaActivity extends AppCompatActivity { } catch (Exception e) { progressDialog.dismiss(); - Toast.makeText(this, "Erro ao processar imagem: " + e.getMessage(), Toast.LENGTH_SHORT).show(); + Toast.makeText(this, "Erro: " + e.getMessage(), Toast.LENGTH_SHORT).show(); } finally { if (inputStream != null) { try { inputStream.close(); } catch (Exception e) { - // Ignorar erro ao fechar } } } } - private void saveAllChanges() { - if (editName == null) - return; - - String newName = editName.getText().toString().trim(); - - if (newName.isEmpty()) { - Toast.makeText(this, "Por favor, insira um nome", Toast.LENGTH_SHORT).show(); - return; - } - - FirebaseUser user = mAuth.getCurrentUser(); - if (user == null) { - Toast.makeText(this, "Utilizador não autenticado", Toast.LENGTH_SHORT).show(); - return; - } - - progressDialog.show(); - - UserProfileChangeRequest profileUpdates = new UserProfileChangeRequest.Builder() - .setDisplayName(newName) - .build(); - - user.updateProfile(profileUpdates).addOnCompleteListener(task -> { - progressDialog.dismiss(); - if (task.isSuccessful()) { - Toast.makeText(this, "Alterações guardadas com sucesso!", Toast.LENGTH_SHORT).show(); - } else { - 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) { diff --git a/app/src/main/java/com/example/vdcscore/ui/definicoes/DefinicoesFragment.java b/app/src/main/java/com/example/vdcscore/ui/definicoes/DefinicoesFragment.java index cdac9e1..bffd3f1 100644 --- a/app/src/main/java/com/example/vdcscore/ui/definicoes/DefinicoesFragment.java +++ b/app/src/main/java/com/example/vdcscore/ui/definicoes/DefinicoesFragment.java @@ -24,13 +24,14 @@ public class DefinicoesFragment extends Fragment { @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, - @Nullable ViewGroup container, - @Nullable Bundle savedInstanceState) { + @Nullable ViewGroup container, + @Nullable Bundle savedInstanceState) { binding = FragmentDefinicoesBinding.inflate(inflater, container, false); setupUi(); return binding.getRoot(); } -//teste + + // teste private void setupUi() { binding.cardConta.setOnClickListener(new View.OnClickListener() { @Override @@ -44,14 +45,35 @@ public class DefinicoesFragment extends Fragment { } }); - - binding.switchNotifications.setOnCheckedChangeListener((buttonView, isChecked) -> - binding.textNotificationsStatus.setText( + binding.switchNotifications + .setOnCheckedChangeListener((buttonView, isChecked) -> binding.textNotificationsStatus.setText( isChecked ? "Ativadas" : "Desativadas")); - binding.switchDarkMode.setOnCheckedChangeListener((buttonView, isChecked) -> - binding.textDarkModeStatus.setText( - isChecked ? "Ativo" : "Inativo")); + // Obter SharedPreferences + android.content.SharedPreferences prefs = requireActivity().getSharedPreferences("SettingsPrefs", + android.content.Context.MODE_PRIVATE); + boolean isDarkMode = prefs.getBoolean("dark_mode", false); + + binding.switchDarkMode.setChecked(isDarkMode); + binding.textDarkModeStatus.setText(isDarkMode ? "Ativo" : "Inativo"); + + binding.switchDarkMode.setOnCheckedChangeListener((buttonView, isChecked) -> { + binding.textDarkModeStatus.setText(isChecked ? "Ativo" : "Inativo"); + + // Guardar preferência + android.content.SharedPreferences.Editor editor = prefs.edit(); + editor.putBoolean("dark_mode", isChecked); + editor.apply(); + + // Aplicar modo escuro + if (isChecked) { + androidx.appcompat.app.AppCompatDelegate + .setDefaultNightMode(androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_YES); + } else { + androidx.appcompat.app.AppCompatDelegate + .setDefaultNightMode(androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_NO); + } + }); binding.btnOpenSystemSettings.setOnClickListener(v -> { Intent intent = new Intent(Settings.ACTION_SETTINGS); @@ -66,4 +88,3 @@ public class DefinicoesFragment extends Fragment { binding = null; } } - diff --git a/app/src/main/res/layout/activity_conta.xml b/app/src/main/res/layout/activity_conta.xml index d858ac8..263dcce 100644 --- a/app/src/main/res/layout/activity_conta.xml +++ b/app/src/main/res/layout/activity_conta.xml @@ -121,48 +121,6 @@ android:padding="8dp" android:background="?attr/selectableItemBackgroundBorderless" /> - - - - - - - -