alteracoes precisas na app

This commit is contained in:
2026-02-24 17:17:47 +00:00
parent 421ba7c2e2
commit 337440eb91
4 changed files with 92 additions and 218 deletions

View File

@@ -46,6 +46,18 @@ public class MainActivity extends AppCompatActivity {
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(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()); binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot()); setContentView(binding.getRoot());
@@ -155,37 +167,7 @@ public class MainActivity extends AppCompatActivity {
} }
private void showChangeProfilePhotoDialog() { private void showChangeProfilePhotoDialog() {
AlertDialog.Builder builder = new AlertDialog.Builder(this); mGetContent.launch("image/*");
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();
} }
private void uploadImageToStorage(Uri imageUri) { private void uploadImageToStorage(Uri imageUri) {

View File

@@ -46,10 +46,8 @@ public class ContaActivity extends AppCompatActivity {
private ImageView imageProfile; private ImageView imageProfile;
private TextInputEditText editName; private TextInputEditText editName;
private TextInputEditText editImageUrl;
private TextView textEmail; private TextView textEmail;
private Button btnSaveAll; private Button btnSaveAll;
private Button btnLoadImageUrl;
private Button btnChangeEmail; private Button btnChangeEmail;
private Button btnChangePassword; private Button btnChangePassword;
private Button btnLogout; private Button btnLogout;
@@ -64,7 +62,6 @@ public class ContaActivity extends AppCompatActivity {
private ActivityResultLauncher<Intent> imagePickerLauncher; private ActivityResultLauncher<Intent> imagePickerLauncher;
private ActivityResultLauncher<String> permissionLauncher; private ActivityResultLauncher<String> permissionLauncher;
private Uri selectedImageUri; private Uri selectedImageUri;
private String imageUrlFromGoogle;
private static final String PREFS_NAME = "LoginPrefs"; private static final String PREFS_NAME = "LoginPrefs";
@@ -98,13 +95,10 @@ public class ContaActivity extends AppCompatActivity {
imageProfile = findViewById(R.id.imageProfile); imageProfile = findViewById(R.id.imageProfile);
editName = findViewById(R.id.editName); 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. // In XML it is present as @+id/textEmail inside cardSecurity.
textEmail = findViewById(R.id.textEmail); textEmail = findViewById(R.id.textEmail);
btnSaveAll = findViewById(R.id.btnSaveAll); btnSaveAll = findViewById(R.id.btnSaveAll);
btnLoadImageUrl = findViewById(R.id.btnLoadImageUrl);
btnChangeEmail = findViewById(R.id.btnChangeEmail); btnChangeEmail = findViewById(R.id.btnChangeEmail);
btnChangePassword = findViewById(R.id.btnChangePassword); btnChangePassword = findViewById(R.id.btnChangePassword);
btnLogout = findViewById(R.id.btnLogout); btnLogout = findViewById(R.id.btnLogout);
@@ -146,7 +140,7 @@ public class ContaActivity extends AppCompatActivity {
inputStream.close(); inputStream.close();
if (bitmap != null) { if (bitmap != null) {
imageProfile.setImageBitmap(bitmap); imageProfile.setImageBitmap(bitmap);
uploadProfileImage(); // Não fazemos upload até clicar em Gravar
} else { } else {
Toast.makeText(this, "Erro ao carregar imagem", Toast.LENGTH_SHORT).show(); Toast.makeText(this, "Erro ao carregar imagem", Toast.LENGTH_SHORT).show();
} }
@@ -250,9 +244,6 @@ public class ContaActivity extends AppCompatActivity {
if (btnSaveAll != null) { if (btnSaveAll != null) {
btnSaveAll.setOnClickListener(v -> saveAllChanges()); btnSaveAll.setOnClickListener(v -> saveAllChanges());
} }
if (btnLoadImageUrl != null) {
btnLoadImageUrl.setOnClickListener(v -> loadImageFromUrl());
}
if (btnChangeEmail != null) { if (btnChangeEmail != null) {
btnChangeEmail.setOnClickListener(v -> sendEmailVerificationForEmailChange()); btnChangeEmail.setOnClickListener(v -> sendEmailVerificationForEmailChange());
} }
@@ -279,100 +270,57 @@ public class ContaActivity extends AppCompatActivity {
} }
} }
private void loadImageFromUrl() { private void saveAllChanges() {
if (editImageUrl == null) if (editName == null)
return; return;
String imageUrl = editImageUrl.getText().toString().trim(); String newName = editName.getText().toString().trim();
if (imageUrl.isEmpty()) { if (newName.isEmpty() && selectedImageUri == null) {
Toast.makeText(this, "Por favor, insira uma URL válida", Toast.LENGTH_SHORT).show(); Toast.makeText(this, "Nenhuma alteração a guardar", Toast.LENGTH_SHORT).show();
return; return;
} }
if (!imageUrl.startsWith("http://") && !imageUrl.startsWith("https://")) { FirebaseUser user = mAuth.getCurrentUser();
Toast.makeText(this, "URL inválida. Deve começar com http:// ou https://", Toast.LENGTH_SHORT).show(); if (user == null) {
Toast.makeText(this, "Utilizador não autenticado", Toast.LENGTH_SHORT).show();
return; return;
} }
progressDialog.show(); progressDialog.show();
// Carregar imagem da URL // Se tiver foto nova, primeiro faz upload
new Thread(() -> { if (selectedImageUri != null) {
try { uploadNewProfileImageAndSaveName(user, newName);
java.net.URL url = new java.net.URL(imageUrl); } else {
java.net.HttpURLConnection connection = (java.net.HttpURLConnection) url.openConnection(); // Só muda o nome
connection.setDoInput(true); saveNameOnly(user, newName);
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();
} }
private void updateProfileWithImageUrl(String imageUrl) { private void saveNameOnly(FirebaseUser user, String newName) {
FirebaseUser user = mAuth.getCurrentUser(); if (newName.isEmpty()) {
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(); 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() { private void uploadNewProfileImageAndSaveName(FirebaseUser user, String newName) {
if (selectedImageUri == null)
return;
FirebaseUser user = mAuth.getCurrentUser();
if (user == null)
return;
progressDialog.show();
InputStream inputStream = null; InputStream inputStream = null;
try { try {
inputStream = getContentResolver().openInputStream(selectedImageUri); inputStream = getContentResolver().openInputStream(selectedImageUri);
@@ -389,7 +337,7 @@ public class ContaActivity extends AppCompatActivity {
return; return;
} }
// Redimensionar se muito grande // Redimensionar
int maxSize = 1024; int maxSize = 1024;
if (bitmap.getWidth() > maxSize || bitmap.getHeight() > maxSize) { if (bitmap.getWidth() > maxSize || bitmap.getHeight() > maxSize) {
float scale = Math.min((float) maxSize / bitmap.getWidth(), (float) maxSize / bitmap.getHeight()); 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); bitmap.compress(Bitmap.CompressFormat.JPEG, 80, baos);
byte[] imageData = baos.toByteArray(); byte[] imageData = baos.toByteArray();
// Upload para Firebase Storage
StorageReference profileImageRef = storageRef.child("profile_images/" + user.getUid() + ".jpg"); StorageReference profileImageRef = storageRef.child("profile_images/" + user.getUid() + ".jpg");
UploadTask uploadTask = profileImageRef.putBytes(imageData); UploadTask uploadTask = profileImageRef.putBytes(imageData);
uploadTask.addOnSuccessListener(taskSnapshot -> { uploadTask.addOnSuccessListener(taskSnapshot -> {
profileImageRef.getDownloadUrl().addOnSuccessListener(uri -> { profileImageRef.getDownloadUrl().addOnSuccessListener(uri -> {
UserProfileChangeRequest profileUpdates = new UserProfileChangeRequest.Builder()
.setPhotoUri(uri) UserProfileChangeRequest.Builder builder = new UserProfileChangeRequest.Builder();
.build(); builder.setPhotoUri(uri);
if (!newName.isEmpty()) {
builder.setDisplayName(newName);
}
UserProfileChangeRequest profileUpdates = builder.build();
user.updateProfile(profileUpdates).addOnCompleteListener(task -> { user.updateProfile(profileUpdates).addOnCompleteListener(task -> {
progressDialog.dismiss(); progressDialog.dismiss();
if (task.isSuccessful()) { 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()); loadProfileImage(uri.toString());
selectedImageUri = null; // reset
} else { } else {
Toast.makeText(this, "Erro ao atualizar foto: " + Toast.makeText(this, "Erro ao atualizar perfil", Toast.LENGTH_SHORT).show();
(task.getException() != null ? task.getException().getMessage()
: "Erro desconhecido"),
Toast.LENGTH_SHORT).show();
} }
}); });
}).addOnFailureListener(e -> { }).addOnFailureListener(e -> {
progressDialog.dismiss(); 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 -> { }).addOnFailureListener(e -> {
progressDialog.dismiss(); progressDialog.dismiss();
@@ -435,53 +384,17 @@ public class ContaActivity extends AppCompatActivity {
} catch (Exception e) { } catch (Exception e) {
progressDialog.dismiss(); 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 { } finally {
if (inputStream != null) { if (inputStream != null) {
try { try {
inputStream.close(); inputStream.close();
} catch (Exception e) { } 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() { private void sendPasswordResetEmail() {
FirebaseUser user = mAuth.getCurrentUser(); FirebaseUser user = mAuth.getCurrentUser();
if (user == null || user.getEmail() == null) { if (user == null || user.getEmail() == null) {

View File

@@ -24,13 +24,14 @@ public class DefinicoesFragment extends Fragment {
@Nullable @Nullable
@Override @Override
public View onCreateView(@NonNull LayoutInflater inflater, public View onCreateView(@NonNull LayoutInflater inflater,
@Nullable ViewGroup container, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) { @Nullable Bundle savedInstanceState) {
binding = FragmentDefinicoesBinding.inflate(inflater, container, false); binding = FragmentDefinicoesBinding.inflate(inflater, container, false);
setupUi(); setupUi();
return binding.getRoot(); return binding.getRoot();
} }
//teste
// teste
private void setupUi() { private void setupUi() {
binding.cardConta.setOnClickListener(new View.OnClickListener() { binding.cardConta.setOnClickListener(new View.OnClickListener() {
@Override @Override
@@ -44,14 +45,35 @@ public class DefinicoesFragment extends Fragment {
} }
}); });
binding.switchNotifications
binding.switchNotifications.setOnCheckedChangeListener((buttonView, isChecked) -> .setOnCheckedChangeListener((buttonView, isChecked) -> binding.textNotificationsStatus.setText(
binding.textNotificationsStatus.setText(
isChecked ? "Ativadas" : "Desativadas")); isChecked ? "Ativadas" : "Desativadas"));
binding.switchDarkMode.setOnCheckedChangeListener((buttonView, isChecked) -> // Obter SharedPreferences
binding.textDarkModeStatus.setText( android.content.SharedPreferences prefs = requireActivity().getSharedPreferences("SettingsPrefs",
isChecked ? "Ativo" : "Inativo")); 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 -> { binding.btnOpenSystemSettings.setOnClickListener(v -> {
Intent intent = new Intent(Settings.ACTION_SETTINGS); Intent intent = new Intent(Settings.ACTION_SETTINGS);
@@ -66,4 +88,3 @@ public class DefinicoesFragment extends Fragment {
binding = null; binding = null;
} }
} }

View File

@@ -121,48 +121,6 @@
android:padding="8dp" android:padding="8dp"
android:background="?attr/selectableItemBackgroundBorderless" /> android:background="?attr/selectableItemBackgroundBorderless" />
<!-- URL Input Section -->
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:hint="URL da Imagem (Google)"
app:boxCornerRadiusTopStart="12dp"
app:boxCornerRadiusTopEnd="12dp"
app:boxCornerRadiusBottomStart="12dp"
app:boxCornerRadiusBottomEnd="12dp"
app:boxStrokeWidth="1dp"
app:boxStrokeColor="@color/divider"
app:startIconDrawable="@android:drawable/ic_menu_view"
app:startIconTint="@color/text_secondary"
app:boxBackgroundMode="outline"
style="@style/Widget.Material3.TextInputLayout.OutlinedBox">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/editImageUrl"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textUri"
android:textColor="@color/text_primary"
android:textColorHint="@color/text_secondary"
android:textSize="14sp"
android:padding="14dp"
android:background="@android:color/transparent"
android:hint="https://..." />
</com.google.android.material.textfield.TextInputLayout>
<Button
android:id="@+id/btnLoadImageUrl"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:backgroundTint="@color/primary_light"
android:text="Carregar Imagem da URL"
android:textColor="@color/white"
android:textSize="14sp"
android:textStyle="bold" />
</LinearLayout> </LinearLayout>
</com.google.android.material.card.MaterialCardView> </com.google.android.material.card.MaterialCardView>