This commit is contained in:
2026-05-12 17:14:34 +01:00
parent 53b791a5ce
commit 148c104b04
7 changed files with 133 additions and 16 deletions

View File

@@ -4,10 +4,10 @@
<selectionStates> <selectionStates>
<SelectionState runConfigName="app"> <SelectionState runConfigName="app">
<option name="selectionMode" value="DROPDOWN" /> <option name="selectionMode" value="DROPDOWN" />
<DropdownSelection timestamp="2026-04-23T16:33:30.001147200Z"> <DropdownSelection timestamp="2026-05-12T14:44:48.637593Z">
<Target type="DEFAULT_BOOT"> <Target type="DEFAULT_BOOT">
<handle> <handle>
<DeviceId pluginId="LocalEmulator" identifier="path=C:\Users\Rodrigo\.android\avd\Medium_Phone.avd" /> <DeviceId pluginId="PhysicalDevice" identifier="serial=RZCX40Q6DDY" />
</handle> </handle>
</Target> </Target>
</DropdownSelection> </DropdownSelection>

View File

@@ -93,7 +93,6 @@ public class AtivosAdapter extends RecyclerView.Adapter<AtivosAdapter.AtivosView
holder.btnRefresh.setOnClickListener(v -> { holder.btnRefresh.setOnClickListener(v -> {
if (listener != null) listener.onRefreshClick(position, key, ativo); if (listener != null) listener.onRefreshClick(position, key, ativo);
Toast.makeText(context, "Ativo atualizado (Exemplo)", Toast.LENGTH_SHORT).show();
}); });
} }

View File

@@ -5,6 +5,8 @@ import android.app.DatePickerDialog;
import android.graphics.Color; import android.graphics.Color;
import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.ColorDrawable;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
@@ -20,6 +22,15 @@ import android.widget.Toast;
import java.util.Calendar; import java.util.Calendar;
import java.util.Locale; import java.util.Locale;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.net.HttpURLConnection;
import java.net.URL;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.BufferedReader;
import org.json.JSONObject;
import org.json.JSONArray;
import com.example.lifegrid.R; import com.example.lifegrid.R;
import com.example.lifegrid.adapters.AtivosAdapter; import com.example.lifegrid.adapters.AtivosAdapter;
@@ -87,7 +98,7 @@ public class AtivosFragment extends Fragment {
@Override @Override
public void onRefreshClick(int position, String key, Ativos ativo) { public void onRefreshClick(int position, String key, Ativos ativo) {
// A implementar no futuro refreshAtivoPrice(ativo, key);
} }
}); });
ativosRecyclerView.setAdapter(ativosAdapter); ativosRecyclerView.setAdapter(ativosAdapter);
@@ -153,6 +164,59 @@ public class AtivosFragment extends Fragment {
}); });
} }
private void refreshAtivoPrice(Ativos ativo, String key) {
if (ativo.getTicker() == null || ativo.getTicker().isEmpty()) {
Toast.makeText(requireContext(), "Este ativo não tem um Ticker definido.", Toast.LENGTH_SHORT).show();
return;
}
Toast.makeText(requireContext(), "A atualizar " + ativo.getTicker() + "...", Toast.LENGTH_SHORT).show();
ExecutorService executor = Executors.newSingleThreadExecutor();
Handler handler = new Handler(Looper.getMainLooper());
executor.execute(() -> {
try {
String ticker = ativo.getTicker().toUpperCase(Locale.ROOT);
URL url = new URL("https://query1.finance.yahoo.com/v8/finance/chart/" + ticker);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setRequestProperty("User-Agent", "Mozilla/5.0");
conn.setConnectTimeout(5000);
conn.setReadTimeout(5000);
if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
InputStream in = conn.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
StringBuilder result = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
result.append(line);
}
reader.close();
JSONObject jsonResponse = new JSONObject(result.toString());
JSONObject chart = jsonResponse.getJSONObject("chart");
JSONArray resultArr = chart.getJSONArray("result");
JSONObject resultObj = resultArr.getJSONObject(0);
JSONObject meta = resultObj.getJSONObject("meta");
double regularMarketPrice = meta.getDouble("regularMarketPrice");
handler.post(() -> {
String userId = FirebaseAuth.getInstance().getCurrentUser().getUid();
databaseReference.child("users").child(userId).child("ativos").child(key).child("precoAtual").setValue(regularMarketPrice);
Toast.makeText(requireContext(), "Preço atualizado: " + regularMarketPrice + "", Toast.LENGTH_SHORT).show();
});
} else {
handler.post(() -> Toast.makeText(requireContext(), "Erro ao contactar a API.", Toast.LENGTH_SHORT).show());
}
} catch (Exception e) {
e.printStackTrace();
handler.post(() -> Toast.makeText(requireContext(), "Ativo não encontrado ou erro de rede.", Toast.LENGTH_SHORT).show());
}
});
}
private void showNovoAtivoDialog() { private void showNovoAtivoDialog() {
AlertDialog.Builder builder = new AlertDialog.Builder(requireContext()); AlertDialog.Builder builder = new AlertDialog.Builder(requireContext());
View dialogView = getLayoutInflater().inflate(R.layout.dialog_novo_ativo, null); View dialogView = getLayoutInflater().inflate(R.layout.dialog_novo_ativo, null);
@@ -183,23 +247,48 @@ public class AtivosFragment extends Fragment {
Button btnAdicionarAtivoDialog = dialogView.findViewById(R.id.btnAdicionarAtivoDialog); Button btnAdicionarAtivoDialog = dialogView.findViewById(R.id.btnAdicionarAtivoDialog);
EditText etNomeAtivo = dialogView.findViewById(R.id.etNomeAtivo); EditText etNomeAtivo = dialogView.findViewById(R.id.etNomeAtivo);
EditText etTickerAtivo = dialogView.findViewById(R.id.etTickerAtivo);
TextView tvTickerLabel = dialogView.findViewById(R.id.tvTickerLabel);
Spinner spinnerTipoAtivo = dialogView.findViewById(R.id.spinnerTipoAtivo); Spinner spinnerTipoAtivo = dialogView.findViewById(R.id.spinnerTipoAtivo);
EditText etQuantidade = dialogView.findViewById(R.id.etQuantidade); EditText etQuantidade = dialogView.findViewById(R.id.etQuantidade);
EditText etPrecoCompra = dialogView.findViewById(R.id.etPrecoCompra); EditText etPrecoCompra = dialogView.findViewById(R.id.etPrecoCompra);
spinnerTipoAtivo.setOnItemSelectedListener(new android.widget.AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(android.widget.AdapterView<?> parent, View view, int position, long id) {
String selectedType = parent.getItemAtPosition(position).toString();
if ("Criptomoedas".equals(selectedType)) {
tvTickerLabel.setVisibility(View.VISIBLE);
etTickerAtivo.setVisibility(View.VISIBLE);
} else {
tvTickerLabel.setVisibility(View.GONE);
etTickerAtivo.setVisibility(View.GONE);
etTickerAtivo.setText("");
}
}
@Override
public void onNothingSelected(android.widget.AdapterView<?> parent) {
}
});
btnAdicionarAtivoDialog.setOnClickListener(v -> { btnAdicionarAtivoDialog.setOnClickListener(v -> {
String nome = etNomeAtivo.getText().toString().trim(); String nome = etNomeAtivo.getText().toString().trim();
String ticker = etTickerAtivo.getText().toString().trim();
String tipo = spinnerTipoAtivo.getSelectedItem().toString(); String tipo = spinnerTipoAtivo.getSelectedItem().toString();
String quantidade = etQuantidade.getText().toString().trim(); String quantidade = etQuantidade.getText().toString().trim();
String precoCompra = etPrecoCompra.getText().toString().trim(); String precoCompra = etPrecoCompra.getText().toString().trim();
String dataCompra = etDataCompra.getText().toString().trim(); String dataCompra = etDataCompra.getText().toString().trim();
Ativos ativos = new Ativos(nome, quantidade, Double.parseDouble(precoCompra), Double.parseDouble(precoCompra), dataCompra, tipo); boolean isCriptomoeda = "Criptomoedas".equals(tipo);
if (nome.isEmpty() || quantidade.isEmpty() || precoCompra.isEmpty() || dataCompra.isEmpty() || spinnerTipoAtivo.getSelectedItemPosition() == 0) { if (nome.isEmpty() || quantidade.isEmpty() || precoCompra.isEmpty() || dataCompra.isEmpty() || spinnerTipoAtivo.getSelectedItemPosition() == 0) {
Toast.makeText(requireContext(), "Por favor, preencha os campos obrigatórios.", Toast.LENGTH_SHORT).show(); Toast.makeText(requireContext(), "Por favor, preencha os campos obrigatórios.", Toast.LENGTH_SHORT).show();
} else if (isCriptomoeda && ticker.isEmpty()) {
Toast.makeText(requireContext(), "Por favor, preencha o Símbolo da Criptomoeda.", Toast.LENGTH_SHORT).show();
} else { } else {
Ativos ativos = new Ativos(nome, quantidade, Double.parseDouble(precoCompra), Double.parseDouble(precoCompra), dataCompra, tipo, ticker);
// Aqui seria a lógica para guardar na Firebase // Aqui seria a lógica para guardar na Firebase
DatabaseReference databaseReference = FirebaseDatabase.getInstance().getReference(); DatabaseReference databaseReference = FirebaseDatabase.getInstance().getReference();
String userId = FirebaseAuth.getInstance().getCurrentUser().getUid(); String userId = FirebaseAuth.getInstance().getCurrentUser().getUid();

View File

@@ -7,18 +7,20 @@ public class Ativos {
private double precoAtual; private double precoAtual;
private String dataCompra; private String dataCompra;
private String tipo; private String tipo;
private String ticker;
public Ativos() { public Ativos() {
} }
public Ativos(String nome, String quantidade, double precoCompra, double precoAtual, String dataCompra, String tipo) { public Ativos(String nome, String quantidade, double precoCompra, double precoAtual, String dataCompra, String tipo, String ticker) {
this.nome = nome; this.nome = nome;
this.quantidade = quantidade; this.quantidade = quantidade;
this.precoCompra = precoCompra; this.precoCompra = precoCompra;
this.precoAtual = precoAtual; this.precoAtual = precoAtual;
this.dataCompra = dataCompra; this.dataCompra = dataCompra;
this.tipo = tipo; this.tipo = tipo;
this.ticker = ticker;
} }
public String getNome() { public String getNome() {
@@ -68,4 +70,12 @@ public class Ativos {
public void setTipo(String tipo) { public void setTipo(String tipo) {
this.tipo = tipo; this.tipo = tipo;
} }
public String getTicker() {
return ticker;
}
public void setTicker(String ticker) {
this.ticker = ticker;
}
} }

View File

@@ -51,6 +51,28 @@
android:paddingHorizontal="16dp" android:paddingHorizontal="16dp"
android:textColor="@color/preto" /> android:textColor="@color/preto" />
<TextView
android:id="@+id/tvTickerLabel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:text="Símbolo / Ticker (Ex: BTC-USD)"
android:textColor="@color/preto"
android:textStyle="bold"
android:visibility="gone" />
<EditText
android:id="@+id/etTickerAtivo"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_marginBottom="16dp"
android:background="@drawable/rounded_input_bg"
android:hint="BTC-USD, ETH-USD"
android:inputType="textCapCharacters"
android:paddingHorizontal="16dp"
android:textColor="@color/preto"
android:visibility="gone" />
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@@ -92,7 +114,7 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="8dp" android:layout_marginBottom="8dp"
android:text="Preço de Compra (€)" android:text="Valor Investido (€)"
android:textColor="@color/preto" android:textColor="@color/preto"
android:textStyle="bold" /> android:textStyle="bold" />

View File

@@ -85,8 +85,8 @@
android:id="@+id/textView14" android:id="@+id/textView14"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="36dp" android:layout_marginStart="24dp"
android:layout_marginTop="36dp" android:layout_marginTop="32dp"
android:fontFamily="sans-serif" android:fontFamily="sans-serif"
android:text="Valor Total do Portfólio" android:text="Valor Total do Portfólio"
android:textColor="#4A5568" android:textColor="#4A5568"
@@ -98,8 +98,8 @@
android:id="@+id/textView17" android:id="@+id/textView17"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="36dp" android:layout_marginStart="24dp"
android:layout_marginBottom="36dp" android:layout_marginBottom="40dp"
android:fontFamily="sans-serif-medium" android:fontFamily="sans-serif-medium"
android:text="0.00€" android:text="0.00€"
android:textColor="#1A202C" android:textColor="#1A202C"

View File

@@ -12,11 +12,8 @@
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/adicionarCardView" android:id="@+id/adicionarCardView"
android:layout_width="336dp" android:layout_width="367dp"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_marginTop="16dp"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:background="@drawable/cardview_background" android:background="@drawable/cardview_background"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.493" app:layout_constraintHorizontal_bias="0.493"