continuar utilizador
This commit is contained in:
@@ -28,6 +28,7 @@
|
|||||||
<activity android:name=".SettingsActivity" />
|
<activity android:name=".SettingsActivity" />
|
||||||
<activity android:name=".StreakActivity" />
|
<activity android:name=".StreakActivity" />
|
||||||
<activity android:name=".FindFriendsActivity" />
|
<activity android:name=".FindFriendsActivity" />
|
||||||
|
<activity android:name=".TrophiesActivity" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,32 @@
|
|||||||
package com.fluxup.app;
|
package com.fluxup.app;
|
||||||
|
|
||||||
|
|
||||||
|
import com.google.android.gms.tasks.Task;
|
||||||
|
import com.google.firebase.auth.FirebaseAuth;
|
||||||
|
import com.google.firebase.auth.FirebaseAuthException;
|
||||||
import com.google.firebase.auth.FirebaseUser;
|
import com.google.firebase.auth.FirebaseUser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AuthManager - Classe responsável pela gestão centralizada da autenticação Firebase.
|
||||||
|
* Segue padrões de desenvolvimento sénior para garantir modularidade e tratamento de erros robusto.
|
||||||
|
*/
|
||||||
public class AuthManager {
|
public class AuthManager {
|
||||||
private static AuthManager instance;
|
|
||||||
|
|
||||||
private AuthManager() {
|
private static AuthManager instance;
|
||||||
// private constructor
|
private final FirebaseAuth mAuth;
|
||||||
|
|
||||||
|
// Interface para comunicação de resultados com a UI (Activities/Fragments)
|
||||||
|
public interface AuthCallback {
|
||||||
|
void onSuccess(FirebaseUser user);
|
||||||
|
void onError(String errorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Construtor privado para padrão Singleton
|
||||||
|
private AuthManager() {
|
||||||
|
mAuth = FirebaseAuth.getInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Método para obter a instância única do AuthManager
|
||||||
public static synchronized AuthManager getInstance() {
|
public static synchronized AuthManager getInstance() {
|
||||||
if (instance == null) {
|
if (instance == null) {
|
||||||
instance = new AuthManager();
|
instance = new AuthManager();
|
||||||
@@ -16,22 +34,79 @@ public class AuthManager {
|
|||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifica se existe um utilizador autenticado na sessão atual.
|
||||||
|
* @return FirebaseUser ou null se não houver sessão ativa.
|
||||||
|
*/
|
||||||
public FirebaseUser getCurrentUser() {
|
public FirebaseUser getCurrentUser() {
|
||||||
return null;
|
return mAuth.getCurrentUser();
|
||||||
}
|
|
||||||
|
|
||||||
public void loginUtilizador(String email, String password, AuthCallback callback) {
|
|
||||||
// Mock implementation
|
|
||||||
callback.onSuccess();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Realiza o registo de um novo utilizador.
|
||||||
|
* Inclui tratamento de erros específico para passwords fracas e emails inválidos.
|
||||||
|
*/
|
||||||
public void registrarUtilizador(String email, String password, AuthCallback callback) {
|
public void registrarUtilizador(String email, String password, AuthCallback callback) {
|
||||||
// Mock implementation
|
mAuth.createUserWithEmailAndPassword(email, password)
|
||||||
callback.onSuccess();
|
.addOnCompleteListener(task -> {
|
||||||
|
if (task.isSuccessful()) {
|
||||||
|
callback.onSuccess(mAuth.getCurrentUser());
|
||||||
|
} else {
|
||||||
|
callback.onError(handleAuthError(task.getException()));
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface AuthCallback {
|
/**
|
||||||
void onSuccess();
|
* Realiza o login de um utilizador existente.
|
||||||
void onFailure(Exception e);
|
* A gestão da sessão é feita automaticamente pelo Firebase (Persistence).
|
||||||
|
*/
|
||||||
|
public void loginUtilizador(String email, String password, AuthCallback callback) {
|
||||||
|
mAuth.signInWithEmailAndPassword(email, password)
|
||||||
|
.addOnCompleteListener(task -> {
|
||||||
|
if (task.isSuccessful()) {
|
||||||
|
callback.onSuccess(mAuth.getCurrentUser());
|
||||||
|
} else {
|
||||||
|
callback.onError(handleAuthError(task.getException()));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encerra a sessão do utilizador atual.
|
||||||
|
*/
|
||||||
|
public void logout() {
|
||||||
|
mAuth.signOut();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Traduz exceções do Firebase Auth em mensagens amigáveis para o utilizador final.
|
||||||
|
*/
|
||||||
|
private String handleAuthError(Exception e) {
|
||||||
|
if (e instanceof FirebaseAuthException) {
|
||||||
|
String errorCode = ((FirebaseAuthException) e).getErrorCode();
|
||||||
|
switch (errorCode) {
|
||||||
|
case "ERROR_INVALID_EMAIL":
|
||||||
|
return "O formato do email é inválido.";
|
||||||
|
case "ERROR_WEAK_PASSWORD":
|
||||||
|
return "A palavra-passe é demasiado fraca. Use pelo menos 6 caracteres.";
|
||||||
|
case "ERROR_USER_NOT_FOUND":
|
||||||
|
return "Não existe nenhum utilizador registado com este email.";
|
||||||
|
case "ERROR_WRONG_PASSWORD":
|
||||||
|
return "Palavra-passe incorreta.";
|
||||||
|
case "ERROR_EMAIL_ALREADY_IN_USE":
|
||||||
|
return "Este email já está a ser utilizado por outra conta.";
|
||||||
|
case "ERROR_USER_DISABLED":
|
||||||
|
return "Esta conta foi desativada.";
|
||||||
|
case "ERROR_TOO_MANY_REQUESTS":
|
||||||
|
return "Demasiadas tentativas falhadas. Tente mais tarde.";
|
||||||
|
case "ERROR_OPERATION_NOT_ALLOWED":
|
||||||
|
return "O login com email/password não está ativado no Firebase.";
|
||||||
|
default:
|
||||||
|
return "Erro na autenticação: " + e.getLocalizedMessage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return e != null ? e.getLocalizedMessage() : "Ocorreu um erro desconhecido.";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
114
app/src/main/java/com/fluxup/app/FindFriendsActivity.java
Normal file
114
app/src/main/java/com/fluxup/app/FindFriendsActivity.java
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
package com.fluxup.app;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.ImageButton;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
import com.google.android.material.button.MaterialButton;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class FindFriendsActivity extends AppCompatActivity {
|
||||||
|
|
||||||
|
private ImageButton btnClose;
|
||||||
|
private RecyclerView rvSuggestions;
|
||||||
|
private View btnContacts, btnSearchByName, btnProfileLink;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setContentView(R.layout.activity_find_friends);
|
||||||
|
|
||||||
|
btnClose = findViewById(R.id.btnClose);
|
||||||
|
rvSuggestions = findViewById(R.id.rvSuggestions);
|
||||||
|
btnContacts = findViewById(R.id.btnContacts);
|
||||||
|
btnSearchByName = findViewById(R.id.btnSearchByName);
|
||||||
|
btnProfileLink = findViewById(R.id.btnProfileLink);
|
||||||
|
|
||||||
|
btnClose.setOnClickListener(v -> finish());
|
||||||
|
|
||||||
|
setupSuggestions();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupSuggestions() {
|
||||||
|
rvSuggestions.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false));
|
||||||
|
|
||||||
|
List<Suggestion> suggestions = new ArrayList<>();
|
||||||
|
suggestions.add(new Suggestion("Maria Silva", "Segue você", R.color.reward_yellow));
|
||||||
|
suggestions.add(new Suggestion("João Pereira", "Amigo em comum", R.color.success_green));
|
||||||
|
suggestions.add(new Suggestion("Ana Costa", "Segue você", R.color.streak_orange));
|
||||||
|
suggestions.add(new Suggestion("Ricardo M.", "Novo no Fluxup", R.color.streak_blue));
|
||||||
|
|
||||||
|
SuggestionsAdapter adapter = new SuggestionsAdapter(suggestions);
|
||||||
|
rvSuggestions.setAdapter(adapter);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Data Model ---
|
||||||
|
private static class Suggestion {
|
||||||
|
String name;
|
||||||
|
String info;
|
||||||
|
int colorRes;
|
||||||
|
|
||||||
|
Suggestion(String name, String info, int colorRes) {
|
||||||
|
this.name = name;
|
||||||
|
this.info = info;
|
||||||
|
this.colorRes = colorRes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Adapter ---
|
||||||
|
private class SuggestionsAdapter extends RecyclerView.Adapter<SuggestionsAdapter.ViewHolder> {
|
||||||
|
private final List<Suggestion> suggestions;
|
||||||
|
|
||||||
|
SuggestionsAdapter(List<Suggestion> suggestions) {
|
||||||
|
this.suggestions = suggestions;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||||
|
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_friend_suggestion, parent, false);
|
||||||
|
return new ViewHolder(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
|
||||||
|
Suggestion item = suggestions.get(position);
|
||||||
|
holder.tvName.setText(item.name);
|
||||||
|
holder.tvInfo.setText(item.info);
|
||||||
|
holder.ivAvatar.setColorFilter(getResources().getColor(item.colorRes));
|
||||||
|
|
||||||
|
holder.btnFollow.setOnClickListener(v -> {
|
||||||
|
holder.btnFollow.setText("Seguindo");
|
||||||
|
holder.btnFollow.setEnabled(false);
|
||||||
|
holder.btnFollow.setAlpha(0.6f);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemCount() {
|
||||||
|
return suggestions.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
class ViewHolder extends RecyclerView.ViewHolder {
|
||||||
|
ImageView ivAvatar;
|
||||||
|
TextView tvName, tvInfo;
|
||||||
|
MaterialButton btnFollow;
|
||||||
|
|
||||||
|
ViewHolder(View view) {
|
||||||
|
super(view);
|
||||||
|
ivAvatar = view.findViewById(R.id.ivFriendAvatar);
|
||||||
|
tvName = view.findViewById(R.id.tvFriendName);
|
||||||
|
tvInfo = view.findViewById(R.id.tvFriendInfo);
|
||||||
|
btnFollow = view.findViewById(R.id.btnFollowBack);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
79
app/src/main/java/com/fluxup/app/FirestoreManager.java
Normal file
79
app/src/main/java/com/fluxup/app/FirestoreManager.java
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
package com.fluxup.app;
|
||||||
|
|
||||||
|
import com.google.firebase.firestore.FirebaseFirestore;
|
||||||
|
import com.google.firebase.firestore.ListenerRegistration;
|
||||||
|
import com.google.firebase.firestore.QueryDocumentSnapshot;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
public class FirestoreManager {
|
||||||
|
|
||||||
|
private static FirestoreManager instance;
|
||||||
|
private final FirebaseFirestore db;
|
||||||
|
|
||||||
|
private FirestoreManager() {
|
||||||
|
db = FirebaseFirestore.getInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static synchronized FirestoreManager getInstance() {
|
||||||
|
if (instance == null) {
|
||||||
|
instance = new FirestoreManager();
|
||||||
|
}
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Observa as mudanças no documento do utilizador em tempo real.
|
||||||
|
*/
|
||||||
|
public ListenerRegistration observeUser(String uid, Consumer<Usuario> listener) {
|
||||||
|
return db.collection("users").document(uid)
|
||||||
|
.addSnapshotListener((snapshot, e) -> {
|
||||||
|
if (e != null) return;
|
||||||
|
if (snapshot != null && snapshot.exists()) {
|
||||||
|
Usuario usuario = snapshot.toObject(Usuario.class);
|
||||||
|
if (usuario != null) listener.accept(usuario);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Observa as tarefas do utilizador em tempo real.
|
||||||
|
*/
|
||||||
|
public ListenerRegistration observeTasks(String uid, Consumer<List<Task>> listener) {
|
||||||
|
return db.collection("tasks")
|
||||||
|
.whereEqualTo("userId", uid)
|
||||||
|
.addSnapshotListener((snapshots, e) -> {
|
||||||
|
if (e != null) return;
|
||||||
|
List<Task> tasks = new ArrayList<>();
|
||||||
|
if (snapshots != null) {
|
||||||
|
for (QueryDocumentSnapshot doc : snapshots) {
|
||||||
|
tasks.add(doc.toObject(Task.class));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
listener.accept(tasks);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Atualiza o estado de uma tarefa.
|
||||||
|
*/
|
||||||
|
public void updateTask(Task task) {
|
||||||
|
db.collection("tasks").document(task.id).set(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adiciona uma nova tarefa.
|
||||||
|
*/
|
||||||
|
public void addTask(Task task) {
|
||||||
|
db.collection("tasks").document(task.id).set(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Atualiza campos específicos do perfil do utilizador (ex: XP, Streak).
|
||||||
|
*/
|
||||||
|
public void updateUserStats(String uid, Map<String, Object> updates) {
|
||||||
|
db.collection("users").document(uid).update(updates);
|
||||||
|
}
|
||||||
|
}
|
||||||
59
app/src/main/java/com/fluxup/app/FluxupApplication.java
Normal file
59
app/src/main/java/com/fluxup/app/FluxupApplication.java
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
package com.fluxup.app;
|
||||||
|
|
||||||
|
import android.app.Application;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.util.Log;
|
||||||
|
import androidx.appcompat.app.AppCompatDelegate;
|
||||||
|
|
||||||
|
import com.fluxup.app.BuildConfig;
|
||||||
|
import com.google.firebase.FirebaseApp;
|
||||||
|
import com.google.firebase.appcheck.FirebaseAppCheck;
|
||||||
|
import com.google.firebase.appcheck.debug.DebugAppCheckProviderFactory;
|
||||||
|
import com.google.firebase.appcheck.playintegrity.PlayIntegrityAppCheckProviderFactory;
|
||||||
|
|
||||||
|
public class FluxupApplication extends Application {
|
||||||
|
|
||||||
|
private static final String TAG = "FLUXUP_DEBUG";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate() {
|
||||||
|
super.onCreate();
|
||||||
|
|
||||||
|
// Aplicar preferências de tema
|
||||||
|
SharedPreferences prefs = getSharedPreferences("FluxupSettings", Context.MODE_PRIVATE);
|
||||||
|
if (prefs.getBoolean("darkMode", false)) {
|
||||||
|
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
|
||||||
|
} else {
|
||||||
|
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inicializar Firebase
|
||||||
|
FirebaseApp.initializeApp(this);
|
||||||
|
Log.d(TAG, "FirebaseApp inicializado com sucesso.");
|
||||||
|
|
||||||
|
// Inicializar Firebase App Check
|
||||||
|
FirebaseAppCheck firebaseAppCheck = FirebaseAppCheck.getInstance();
|
||||||
|
|
||||||
|
if (BuildConfig.DEBUG) {
|
||||||
|
// 1. Instala o provider
|
||||||
|
firebaseAppCheck.installAppCheckProviderFactory(DebugAppCheckProviderFactory.getInstance());
|
||||||
|
|
||||||
|
// 2. FORÇAR a exibição do token no Logcat
|
||||||
|
// Adicionamos um log específico com a tag "FLUXUP_DEBUG"
|
||||||
|
Log.d("FLUXUP_DEBUG", "--------------------------------------------------");
|
||||||
|
Log.d("FLUXUP_DEBUG", "COPIE ESTE TOKEN PARA A CONSOLA FIREBASE:");
|
||||||
|
Log.d("FLUXUP_DEBUG", "Debug Token: " + "Procure pela mensagem acima ou no log do sistema");
|
||||||
|
Log.d("FLUXUP_DEBUG", "--------------------------------------------------");
|
||||||
|
} else {
|
||||||
|
// Em modo RELEASE, usamos o Play Integrity (recomendado para Android).
|
||||||
|
firebaseAppCheck.installAppCheckProviderFactory(
|
||||||
|
PlayIntegrityAppCheckProviderFactory.getInstance());
|
||||||
|
Log.d(TAG, "App Check: Play Integrity Provider instalado.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Garantir que os tokens são renovados automaticamente e usamos sempre o mais recente
|
||||||
|
firebaseAppCheck.setTokenAutoRefreshEnabled(true);
|
||||||
|
Log.d(TAG, "App Check: Auto-refresh de tokens ativado.");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -79,9 +79,7 @@ public class InicioFragment extends Fragment {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
view.findViewById(R.id.btnAddTasks).setOnClickListener(v -> {
|
view.findViewById(R.id.btnAddTasks).setOnClickListener(v -> showAddTaskDialog());
|
||||||
Toast.makeText(getContext(), "Adicionar tarefas: Implementação futura", Toast.LENGTH_SHORT).show();
|
|
||||||
});
|
|
||||||
|
|
||||||
View btnStreak = view.findViewById(R.id.btnStreak);
|
View btnStreak = view.findViewById(R.id.btnStreak);
|
||||||
if (btnStreak != null) {
|
if (btnStreak != null) {
|
||||||
@@ -331,4 +329,76 @@ public class InicioFragment extends Fragment {
|
|||||||
}
|
}
|
||||||
pauseTimer(); // Parar o timer se a view for destruída
|
pauseTimer(); // Parar o timer se a view for destruída
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void showAddTaskDialog() {
|
||||||
|
if (getContext() == null) return;
|
||||||
|
|
||||||
|
// Build input field
|
||||||
|
android.widget.EditText editText = new android.widget.EditText(getContext());
|
||||||
|
editText.setHint("Escreve o teu desafio…");
|
||||||
|
editText.setInputType(android.text.InputType.TYPE_CLASS_TEXT
|
||||||
|
| android.text.InputType.TYPE_TEXT_FLAG_CAP_SENTENCES);
|
||||||
|
editText.setTextSize(16);
|
||||||
|
editText.setSingleLine(false);
|
||||||
|
editText.setMaxLines(3);
|
||||||
|
|
||||||
|
// Wrap with padding
|
||||||
|
android.widget.FrameLayout container = new android.widget.FrameLayout(getContext());
|
||||||
|
android.widget.FrameLayout.LayoutParams lp = new android.widget.FrameLayout.LayoutParams(
|
||||||
|
android.widget.FrameLayout.LayoutParams.MATCH_PARENT,
|
||||||
|
android.widget.FrameLayout.LayoutParams.WRAP_CONTENT);
|
||||||
|
int dp24 = (int) (24 * getResources().getDisplayMetrics().density);
|
||||||
|
lp.setMargins(dp24, dp24 / 2, dp24, 0);
|
||||||
|
editText.setLayoutParams(lp);
|
||||||
|
container.addView(editText);
|
||||||
|
|
||||||
|
android.app.AlertDialog dialog = new com.google.android.material.dialog.MaterialAlertDialogBuilder(getContext())
|
||||||
|
.setTitle("Novo Desafio")
|
||||||
|
.setView(container)
|
||||||
|
.setNegativeButton("Cancelar", null)
|
||||||
|
.setPositiveButton("Adicionar", null)
|
||||||
|
|
||||||
|
|
||||||
|
dialog.setOnShowListener(d -> {
|
||||||
|
// Style buttons
|
||||||
|
android.widget.Button btnAdd = dialog.getButton(android.app.AlertDialog.BUTTON_POSITIVE);
|
||||||
|
android.widget.Button btnCancel = dialog.getButton(android.app.AlertDialog.BUTTON_NEGATIVE);
|
||||||
|
if (btnAdd != null) btnAdd.setTextColor(getResources().getColor(R.color.primary_purple));
|
||||||
|
if (btnCancel != null) btnCancel.setTextColor(getResources().getColor(R.color.text_secondary));
|
||||||
|
|
||||||
|
// Override positive so it validates before dismissing
|
||||||
|
if (btnAdd != null) {
|
||||||
|
btnAdd.setOnClickListener(v -> {
|
||||||
|
String title = editText.getText().toString().trim();
|
||||||
|
if (title.isEmpty()) {
|
||||||
|
editText.setError("Escreve um desafio");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
saveNewTask(title);
|
||||||
|
dialog.dismiss();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
editText.requestFocus();
|
||||||
|
if (dialog.getWindow() != null) {
|
||||||
|
dialog.getWindow().setSoftInputMode(
|
||||||
|
android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
dialog.show();
|
||||||
|
}
|
||||||
|
jhvjhvkvjhv
|
||||||
|
private void saveNewTask(String title) {
|
||||||
|
com.google.firebase.auth.FirebaseUser currentUser = AuthManager.getInstance().getCurrentUser();
|
||||||
|
if (currentUser == null) return;
|
||||||
|
|
||||||
|
String uid = currentUser.getUid();
|
||||||
|
String taskId = com.google.firebase.firestore.FirebaseFirestore.getInstance()
|
||||||
|
.collection("tasks").document().getId();
|
||||||
|
|
||||||
|
Task task = new Task(taskId, title, 10, uid);
|
||||||
|
FirestoreManager.getInstance().addTask(task);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,6 +45,9 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
|
|
||||||
if (itemId == R.id.nav_inicio) {
|
if (itemId == R.id.nav_inicio) {
|
||||||
selectedFragment = new InicioFragment();
|
selectedFragment = new InicioFragment();
|
||||||
|
} else if (itemId == R.id.nav_trophies) {
|
||||||
|
startActivity(new android.content.Intent(this, TrophiesActivity.class));
|
||||||
|
return true;
|
||||||
} else if (itemId == R.id.nav_profile) {
|
} else if (itemId == R.id.nav_profile) {
|
||||||
selectedFragment = new ProfileFragment();
|
selectedFragment = new ProfileFragment();
|
||||||
} else if (itemId == R.id.nav_search) {
|
} else if (itemId == R.id.nav_search) {
|
||||||
|
|||||||
@@ -43,6 +43,11 @@ public class ProfileFragment extends Fragment {
|
|||||||
tvTotalXP = view.findViewById(R.id.tvTotalXP);
|
tvTotalXP = view.findViewById(R.id.tvTotalXP);
|
||||||
tvLeagueName = view.findViewById(R.id.tvLeagueName);
|
tvLeagueName = view.findViewById(R.id.tvLeagueName);
|
||||||
tvAchievementsCount = view.findViewById(R.id.tvAchievementsCount);
|
tvAchievementsCount = view.findViewById(R.id.tvAchievementsCount);
|
||||||
|
View cardLeague = view.findViewById(R.id.cardLeague);
|
||||||
|
cardLeague.setOnClickListener(v -> {
|
||||||
|
Intent intent = new Intent(getActivity(), TrophiesActivity.class);
|
||||||
|
startActivity(intent);
|
||||||
|
});
|
||||||
|
|
||||||
startObservingUser();
|
startObservingUser();
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,159 @@
|
|||||||
package com.fluxup.app;
|
package com.fluxup.app;
|
||||||
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.ImageButton;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import androidx.recyclerview.widget.GridLayoutManager;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
import com.google.android.material.tabs.TabLayout;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public class StreakActivity extends AppCompatActivity {
|
public class StreakActivity extends AppCompatActivity {
|
||||||
|
|
||||||
|
private RecyclerView rvCalendar;
|
||||||
|
private TextView tvStreakCount;
|
||||||
|
private ImageButton btnClose;
|
||||||
|
private TabLayout tabLayout;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
setContentView(R.layout.activity_streak);
|
setContentView(R.layout.activity_streak);
|
||||||
|
|
||||||
|
rvCalendar = findViewById(R.id.rvCalendar);
|
||||||
|
tvStreakCount = findViewById(R.id.tvStreakCount);
|
||||||
|
btnClose = findViewById(R.id.btnClose);
|
||||||
|
tabLayout = findViewById(R.id.tabLayout);
|
||||||
|
|
||||||
|
btnClose.setOnClickListener(v -> finish());
|
||||||
|
|
||||||
|
setupCalendar();
|
||||||
|
setupTabs();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupTabs() {
|
||||||
|
tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
|
||||||
|
@Override
|
||||||
|
public void onTabSelected(TabLayout.Tab tab) {
|
||||||
|
// Future: toggle between personal and friends stats
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTabUnselected(TabLayout.Tab tab) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTabReselected(TabLayout.Tab tab) {}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupCalendar() {
|
||||||
|
rvCalendar.setLayoutManager(new GridLayoutManager(this, 7));
|
||||||
|
|
||||||
|
List<CalendarDay> days = new ArrayList<>();
|
||||||
|
|
||||||
|
// Simulating April 2026 (Starts on a Wednesday)
|
||||||
|
// Add empty slots for Mon, Tue
|
||||||
|
days.add(new CalendarDay(0, false, false)); // Mon
|
||||||
|
days.add(new CalendarDay(0, false, false)); // Tue
|
||||||
|
|
||||||
|
// Days 1 to 30
|
||||||
|
for (int i = 1; i <= 30; i++) {
|
||||||
|
boolean isActive = (i >= 1 && i <= 7); // 7-day streak for demo
|
||||||
|
boolean isCurrent = (i == 7);
|
||||||
|
days.add(new CalendarDay(i, isActive, isCurrent));
|
||||||
|
}
|
||||||
|
|
||||||
|
CalendarAdapter adapter = new CalendarAdapter(days);
|
||||||
|
rvCalendar.setAdapter(adapter);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Inner Models & Adapter ---
|
||||||
|
|
||||||
|
private static class CalendarDay {
|
||||||
|
int dayNumber;
|
||||||
|
boolean isActive;
|
||||||
|
boolean isCurrent;
|
||||||
|
|
||||||
|
CalendarDay(int dayNumber, boolean isActive, boolean isCurrent) {
|
||||||
|
this.dayNumber = dayNumber;
|
||||||
|
this.isActive = isActive;
|
||||||
|
this.isCurrent = isCurrent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class CalendarAdapter extends RecyclerView.Adapter<CalendarAdapter.ViewHolder> {
|
||||||
|
private final List<CalendarDay> days;
|
||||||
|
|
||||||
|
CalendarAdapter(List<CalendarDay> days) {
|
||||||
|
this.days = days;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||||
|
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_calendar_day, parent, false);
|
||||||
|
return new ViewHolder(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
|
||||||
|
CalendarDay day = days.get(position);
|
||||||
|
|
||||||
|
if (day.dayNumber == 0) {
|
||||||
|
holder.tvDayNumber.setText("");
|
||||||
|
holder.dayBackground.setVisibility(View.GONE);
|
||||||
|
holder.streakConnector.setVisibility(View.GONE);
|
||||||
|
} else {
|
||||||
|
holder.tvDayNumber.setText(String.valueOf(day.dayNumber));
|
||||||
|
|
||||||
|
if (day.isActive) {
|
||||||
|
holder.dayBackground.setVisibility(View.VISIBLE);
|
||||||
|
holder.tvDayNumber.setTextColor(getResources().getColor(R.color.white));
|
||||||
|
|
||||||
|
// Simple logic for streak connection: if previous day was also active
|
||||||
|
if (position > 0 && days.get(position-1).isActive && day.dayNumber > 1) {
|
||||||
|
holder.streakConnector.setVisibility(View.VISIBLE);
|
||||||
|
} else {
|
||||||
|
holder.streakConnector.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
holder.dayBackground.setVisibility(View.GONE);
|
||||||
|
holder.streakConnector.setVisibility(View.GONE);
|
||||||
|
holder.tvDayNumber.setTextColor(getResources().getColor(R.color.text_primary));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (day.isCurrent) {
|
||||||
|
holder.dayIndicator.setVisibility(View.VISIBLE);
|
||||||
|
} else {
|
||||||
|
holder.dayIndicator.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemCount() {
|
||||||
|
return days.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
class ViewHolder extends RecyclerView.ViewHolder {
|
||||||
|
TextView tvDayNumber;
|
||||||
|
View dayBackground;
|
||||||
|
View streakConnector;
|
||||||
|
View dayIndicator;
|
||||||
|
|
||||||
|
ViewHolder(View view) {
|
||||||
|
super(view);
|
||||||
|
tvDayNumber = view.findViewById(R.id.tvDayNumber);
|
||||||
|
dayBackground = view.findViewById(R.id.dayBackground);
|
||||||
|
streakConnector = view.findViewById(R.id.streakConnector);
|
||||||
|
dayIndicator = view.findViewById(R.id.dayIndicator);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
19
app/src/main/java/com/fluxup/app/Task.java
Normal file
19
app/src/main/java/com/fluxup/app/Task.java
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
package com.fluxup.app;
|
||||||
|
|
||||||
|
public class Task {
|
||||||
|
public String id;
|
||||||
|
public String title;
|
||||||
|
public boolean completed;
|
||||||
|
public int xpReward;
|
||||||
|
public String userId;
|
||||||
|
|
||||||
|
public Task() {}
|
||||||
|
|
||||||
|
public Task(String id, String title, int xpReward, String userId) {
|
||||||
|
this.id = id;
|
||||||
|
this.title = title;
|
||||||
|
this.completed = false;
|
||||||
|
this.xpReward = xpReward;
|
||||||
|
this.userId = userId;
|
||||||
|
}
|
||||||
|
}
|
||||||
145
app/src/main/java/com/fluxup/app/TrophiesActivity.java
Normal file
145
app/src/main/java/com/fluxup/app/TrophiesActivity.java
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
package com.fluxup.app;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.animation.Animation;
|
||||||
|
import android.view.animation.ScaleAnimation;
|
||||||
|
import android.widget.ImageButton;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import com.google.firebase.auth.FirebaseAuth;
|
||||||
|
import com.google.firebase.firestore.ListenerRegistration;
|
||||||
|
|
||||||
|
public class TrophiesActivity extends AppCompatActivity {
|
||||||
|
|
||||||
|
private TextView tvDivisionTitle, tvTimeRemaining, tvTrophyProgress, tvMotivational;
|
||||||
|
private ImageButton btnBack;
|
||||||
|
private LinearLayout trophyContainer, inactiveState;
|
||||||
|
private ImageView trophy1, trophy2, trophy3;
|
||||||
|
|
||||||
|
private FirestoreManager firestoreManager;
|
||||||
|
private FirebaseAuth mAuth;
|
||||||
|
private ListenerRegistration userListener;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setContentView(R.layout.activity_trophies);
|
||||||
|
|
||||||
|
firestoreManager = FirestoreManager.getInstance();
|
||||||
|
mAuth = FirebaseAuth.getInstance();
|
||||||
|
|
||||||
|
initViews();
|
||||||
|
setupListeners();
|
||||||
|
observeUserData();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initViews() {
|
||||||
|
tvDivisionTitle = findViewById(R.id.tvDivisionTitle);
|
||||||
|
tvTimeRemaining = findViewById(R.id.tvTimeRemaining);
|
||||||
|
tvTrophyProgress = findViewById(R.id.tvTrophyProgress);
|
||||||
|
tvMotivational = findViewById(R.id.tvMotivational);
|
||||||
|
btnBack = findViewById(R.id.btnBack);
|
||||||
|
trophyContainer = findViewById(R.id.trophyContainer);
|
||||||
|
inactiveState = findViewById(R.id.inactiveState);
|
||||||
|
trophy1 = findViewById(R.id.trophy1);
|
||||||
|
trophy2 = findViewById(R.id.trophy2);
|
||||||
|
trophy3 = findViewById(R.id.trophy3);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupListeners() {
|
||||||
|
btnBack.setOnClickListener(v -> finish());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void observeUserData() {
|
||||||
|
String uid = mAuth.getUid();
|
||||||
|
if (uid != null) {
|
||||||
|
userListener = firestoreManager.observeUser(uid, this::updateUI);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateUI(Usuario user) {
|
||||||
|
if (user == null) return;
|
||||||
|
|
||||||
|
tvDivisionTitle.setText("Divisão " + user.league);
|
||||||
|
|
||||||
|
// Trophy logic based on streak
|
||||||
|
int currentStreak = user.streak;
|
||||||
|
int trophies = user.trophiesCount;
|
||||||
|
|
||||||
|
// Update trophy visuals
|
||||||
|
updateTrophyIcons(trophies);
|
||||||
|
|
||||||
|
// Progress message
|
||||||
|
int daysToNext = 30 - (currentStreak % 30);
|
||||||
|
if (daysToNext == 30 && currentStreak > 0) {
|
||||||
|
tvTrophyProgress.setText("Troféu conquistado! Mantém a ofensiva.");
|
||||||
|
} else {
|
||||||
|
tvTrophyProgress.setText("Faltam " + daysToNext + " dias para o próximo troféu");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle inactive state
|
||||||
|
if (currentStreak == 0) {
|
||||||
|
inactiveState.setVisibility(View.VISIBLE);
|
||||||
|
trophyContainer.setAlpha(0.5f);
|
||||||
|
tvMotivational.setVisibility(View.GONE);
|
||||||
|
} else {
|
||||||
|
inactiveState.setVisibility(View.GONE);
|
||||||
|
trophyContainer.setAlpha(1.0f);
|
||||||
|
tvMotivational.setVisibility(View.VISIBLE);
|
||||||
|
tvMotivational.setText("Estás a progredir bem na Divisão " + user.league + "!");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simple scale animation for the active trophy
|
||||||
|
animateActiveTrophy(trophies);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateTrophyIcons(int count) {
|
||||||
|
// Reset alphas
|
||||||
|
trophy1.setAlpha(0.3f);
|
||||||
|
trophy2.setAlpha(0.3f);
|
||||||
|
trophy3.setAlpha(0.3f);
|
||||||
|
|
||||||
|
if (count >= 1) trophy1.setAlpha(1.0f);
|
||||||
|
if (count >= 2) trophy2.setAlpha(1.0f);
|
||||||
|
if (count >= 3) trophy3.setAlpha(1.0f);
|
||||||
|
|
||||||
|
// Highlight current progress (the next one)
|
||||||
|
if (count == 0) highlightTrophy(trophy1);
|
||||||
|
else if (count == 1) highlightTrophy(trophy2);
|
||||||
|
else if (count == 2) highlightTrophy(trophy3);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void highlightTrophy(ImageView trophy) {
|
||||||
|
trophy.setAlpha(0.6f);
|
||||||
|
trophy.setBackgroundResource(R.drawable.circle_bg);
|
||||||
|
trophy.setBackgroundTintList(android.content.res.ColorStateList.valueOf(getResources().getColor(R.color.primary_purple)));
|
||||||
|
trophy.setPadding(12, 12, 12, 12);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void animateActiveTrophy(int count) {
|
||||||
|
ImageView target = null;
|
||||||
|
if (count == 0) target = trophy1;
|
||||||
|
else if (count == 1) target = trophy2;
|
||||||
|
else if (count == 2) target = trophy3;
|
||||||
|
|
||||||
|
if (target != null) {
|
||||||
|
ScaleAnimation scale = new ScaleAnimation(1f, 1.1f, 1f, 1.1f,
|
||||||
|
Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
|
||||||
|
scale.setDuration(1000);
|
||||||
|
scale.setRepeatCount(Animation.INFINITE);
|
||||||
|
scale.setRepeatMode(Animation.REVERSE);
|
||||||
|
target.startAnimation(scale);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
if (userListener != null) {
|
||||||
|
userListener.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,6 +17,7 @@ public class Usuario {
|
|||||||
public String handle = "";
|
public String handle = "";
|
||||||
public String bio = "";
|
public String bio = "";
|
||||||
public int achievementsCount = 0;
|
public int achievementsCount = 0;
|
||||||
|
public int trophiesCount = 0;
|
||||||
|
|
||||||
public Usuario() {}
|
public Usuario() {}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:shape="rectangle">
|
android:color="#40FFFFFF">
|
||||||
<solid android:color="#CCCCCC" />
|
<item>
|
||||||
|
<shape android:shape="rectangle">
|
||||||
|
<solid android:color="@color/primary_purple" />
|
||||||
|
<corners android:radius="@dimen/radius_duo" />
|
||||||
</shape>
|
</shape>
|
||||||
|
</item>
|
||||||
|
</ripple>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:shape="rectangle">
|
android:shape="rectangle">
|
||||||
<solid android:color="#FFFFFF" />
|
<solid android:color="@color/white" />
|
||||||
<corners android:radius="8dp" />
|
<corners android:radius="@dimen/radius_duo" />
|
||||||
|
<stroke android:width="2dp" android:color="@color/border_color" />
|
||||||
</shape>
|
</shape>
|
||||||
10
app/src/main/res/drawable/ic_close.xml
Normal file
10
app/src/main/res/drawable/ic_close.xml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="@color/text_primary"
|
||||||
|
android:pathData="M19,6.41L17.59,5,12,10.59,6.41,5,5,6.41,10.59,12,5,17.59,6.41,19,12,13.41,17.59,19,19,17.59,13.41,12,19,6.41z" />
|
||||||
|
</vector>
|
||||||
9
app/src/main/res/drawable/ic_contacts.xml
Normal file
9
app/src/main/res/drawable/ic_contacts.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:pathData="M20,4H4C2.9,4 2.01,4.9 2.01,6L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V6C22,4.9 21.1,4 20,4zM4,6h16v7H4V6zM19,18H5l4,-5h3l1,1l2,-3l4,7z"/>
|
||||||
|
</vector>
|
||||||
@@ -1,9 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:width="24dp"
|
android:width="24dp"
|
||||||
android:height="24dp"
|
android:height="24dp"
|
||||||
android:viewportWidth="24.0"
|
android:viewportWidth="24"
|
||||||
android:viewportHeight="24.0">
|
android:viewportHeight="24">
|
||||||
<path
|
<path
|
||||||
android:fillColor="#FF000000"
|
android:fillColor="@color/streak_orange"
|
||||||
android:pathData="M9,16.17L4.83,12l-1.42,1.41L9,19 21,7l-1.41,-1.41z" />
|
android:pathData="M13.5,0.67C13.5,0.67 14.92,3.42 14.92,5.31C14.92,7.34 13.78,8.23 12.33,9.72L12.33,4.01C12.33,4.01 10.97,1.69 8.01,3.42C5.05,5.15 6.09,10.22 6.09,10.22C6.09,10.22 4.19,7.67 2,12.31C-0.19,16.95 2.13,21.82 2.13,21.82C2.13,21.82 4.1,23.33 8.35,23.33C12.6,23.33 14,21 15.35,23.33C16.7,25.66 19.3,23.33 20,22C20.7,20.67 22,17.33 22,14.67C22,12 18,10 18,10C18,10 20.35,11.38 20.35,13.62C20.35,15.86 19.31,16.5 17.5,14.67C15.69,12.84 16,9 16,9C16,9 18.66,10 18.66,7C18.66,4 14,0.67 13.5,0.67Z" />
|
||||||
</vector>
|
</vector>
|
||||||
23
app/src/main/res/drawable/ic_nav_trophy.xml
Normal file
23
app/src/main/res/drawable/ic_nav_trophy.xml
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/transparent"
|
||||||
|
android:strokeColor="#000000"
|
||||||
|
android:strokeWidth="2"
|
||||||
|
android:strokeLineCap="round"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:pathData="M18,2H6A2,2 0,0 0,4 4V7C4,8.11 4.9,9 6,9H7V11C7,12.71 8.06,14.2 9.5,14.8V17H8V19H16V17H14.5V14.8C15.94,14.2 17,12.71 17,11V9H18A2,2 0,0 0,20 7V4A2,2 0,0 0,18 2Z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/transparent"
|
||||||
|
android:strokeColor="#000000"
|
||||||
|
android:strokeWidth="2"
|
||||||
|
android:pathData="M6,7V4H7V7" />
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/transparent"
|
||||||
|
android:strokeColor="#000000"
|
||||||
|
android:strokeWidth="2"
|
||||||
|
android:pathData="M17,4V7H18" />
|
||||||
|
</vector>
|
||||||
9
app/src/main/res/drawable/ic_search.xml
Normal file
9
app/src/main/res/drawable/ic_search.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:pathData="M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zM9.5,14C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z"/>
|
||||||
|
</vector>
|
||||||
9
app/src/main/res/drawable/ic_share.xml
Normal file
9
app/src/main/res/drawable/ic_share.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:pathData="M18,16.08c-0.76,0 -1.44,0.3 -1.96,0.77L8.91,12.7c0.05,-0.23 0.09,-0.46 0.09,-0.7s-0.04,-0.47 -0.09,-0.7l7.05,-4.11c0.54,0.5 1.25,0.81 2.04,0.81c1.66,0 3,-1.34 3,-3s-1.34,-3 -3,-3s-3,1.34 -3,3c0,0.24 0.04,0.47 0.09,0.7L8.04,9.81C7.5,9.31 6.79,9 6,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3c0.79,0 1.5,-0.31 2.04,-0.81l7.12,4.16c-0.05,0.21 -0.08,0.43 -0.08,0.65c0,1.61 1.31,2.92 2.92,2.92c1.61,0 2.92,-1.31 2.92,-2.92s-1.31,-2.92 -2.92,-2.92z"/>
|
||||||
|
</vector>
|
||||||
12
app/src/main/res/drawable/ic_sleeping_char.xml
Normal file
12
app/src/main/res/drawable/ic_sleeping_char.xml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="200dp"
|
||||||
|
android:height="200dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="#B39DDB"
|
||||||
|
android:pathData="M12,2A10,10 0,0,0 2,12A10,10 0,0,0 12,22A10,10 0,0,0 22,12A10,10 0,0,0 12,2M12,4A8,8 0,0,1 20,12A8,8 0,0,1 12,20A8,8 0,0,1 4,12A8,8 0,0,1 12,4M8,9.5L10,11.5L8,13.5L9.5,15L11.5,13L13.5,15L15,13.5L13,11.5L15,9.5L13.5,8L11.5,10L9.5,8L8,9.5Z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#7E57C2"
|
||||||
|
android:pathData="M17,5V7H19V5H17M14,2V4H16V2H14M20,8V10H22V8H20Z" />
|
||||||
|
</vector>
|
||||||
9
app/src/main/res/drawable/ic_trophy_bronze.xml
Normal file
9
app/src/main/res/drawable/ic_trophy_bronze.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="120dp"
|
||||||
|
android:height="120dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="#CD7F32"
|
||||||
|
android:pathData="M18,2H6A2,2 0,0 0,4 4V7C4,8.11 4.9,9 6,9H7V11C7,12.71 8.06,14.2 9.5,14.8V17H8V19H16V17H14.5V14.8C15.94,14.2 17,12.71 17,11V9H18A2,2 0,0 0,20 7V4A2,2 0,0 0,18 2M6,7V4H7V7H6M18,7H17V4H18V7Z" />
|
||||||
|
</vector>
|
||||||
9
app/src/main/res/drawable/ic_trophy_gold.xml
Normal file
9
app/src/main/res/drawable/ic_trophy_gold.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="120dp"
|
||||||
|
android:height="120dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FFD700"
|
||||||
|
android:pathData="M18,2H6A2,2 0,0 0,4 4V7C4,8.11 4.9,9 6,9H7V11C7,12.71 8.06,14.2 9.5,14.8V17H8V19H16V17H14.5V14.8C15.94,14.2 17,12.71 17,11V9H18A2,2 0,0 0,20 7V4A2,2 0,0 0,18 2M6,7V4H7V7H6M18,7H17V4H18V7Z" />
|
||||||
|
</vector>
|
||||||
9
app/src/main/res/drawable/ic_trophy_silver.xml
Normal file
9
app/src/main/res/drawable/ic_trophy_silver.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="120dp"
|
||||||
|
android:height="120dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="#C0C0C0"
|
||||||
|
android:pathData="M18,2H6A2,2 0,0 0,4 4V7C4,8.11 4.9,9 6,9H7V11C7,12.71 8.06,14.2 9.5,14.8V17H8V19H16V17H14.5V14.8C15.94,14.2 17,12.71 17,11V9H18A2,2 0,0 0,20 7V4A2,2 0,0 0,18 2M6,7V4H7V7H6M18,7H17V4H18V7Z" />
|
||||||
|
</vector>
|
||||||
8
app/src/main/res/drawable/node_circle_bg.xml
Normal file
8
app/src/main/res/drawable/node_circle_bg.xml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="oval">
|
||||||
|
<solid android:color="@color/white" />
|
||||||
|
<stroke
|
||||||
|
android:width="2dp"
|
||||||
|
android:color="@color/border_color" />
|
||||||
|
</shape>
|
||||||
27
app/src/main/res/drawable/node_progress_ring.xml
Normal file
27
app/src/main/res/drawable/node_progress_ring.xml
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:id="@android:id/background">
|
||||||
|
<shape
|
||||||
|
android:innerRadiusRatio="1.5"
|
||||||
|
android:shape="ring"
|
||||||
|
android:thickness="4dp"
|
||||||
|
android:useLevel="false">
|
||||||
|
<solid android:color="@color/border_color" />
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
<item android:id="@android:id/progress">
|
||||||
|
<rotate
|
||||||
|
android:fromDegrees="270"
|
||||||
|
android:pivotX="50%"
|
||||||
|
android:pivotY="50%"
|
||||||
|
android:toDegrees="270">
|
||||||
|
<shape
|
||||||
|
android:innerRadiusRatio="1.5"
|
||||||
|
android:shape="ring"
|
||||||
|
android:thickness="4dp"
|
||||||
|
android:useLevel="true">
|
||||||
|
<solid android:color="@color/primary_purple" />
|
||||||
|
</shape>
|
||||||
|
</rotate>
|
||||||
|
</item>
|
||||||
|
</layer-list>
|
||||||
@@ -1,5 +1,17 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
android:shape="rectangle">
|
<item android:id="@android:id/background">
|
||||||
<solid android:color="#CCCCCC" />
|
<shape>
|
||||||
|
<corners android:radius="12dp" />
|
||||||
|
<solid android:color="@color/border_color" />
|
||||||
</shape>
|
</shape>
|
||||||
|
</item>
|
||||||
|
<item android:id="@android:id/progress">
|
||||||
|
<clip>
|
||||||
|
<shape>
|
||||||
|
<corners android:radius="12dp" />
|
||||||
|
<solid android:color="@color/success_green" />
|
||||||
|
</shape>
|
||||||
|
</clip>
|
||||||
|
</item>
|
||||||
|
</layer-list>
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:shape="oval">
|
android:shape="oval">
|
||||||
<solid android:color="#CCCCCC" />
|
<solid android:color="@color/white" />
|
||||||
|
<stroke
|
||||||
|
android:width="4dp"
|
||||||
|
android:color="@color/primary_purple" />
|
||||||
|
<size
|
||||||
|
android:width="200dp"
|
||||||
|
android:height="200dp" />
|
||||||
</shape>
|
</shape>
|
||||||
180
app/src/main/res/layout/activity_find_friends.xml
Normal file
180
app/src/main/res/layout/activity_find_friends.xml
Normal file
@@ -0,0 +1,180 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@color/background_light"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<!-- Header -->
|
||||||
|
<RelativeLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="64dp"
|
||||||
|
android:paddingHorizontal="16dp">
|
||||||
|
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/btnClose"
|
||||||
|
android:layout_width="48dp"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:background="?attr/selectableItemBackgroundBorderless"
|
||||||
|
android:src="@drawable/ic_close"
|
||||||
|
app:tint="@color/text_primary" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_centerInParent="true"
|
||||||
|
android:text="Encontre os seus amigos"
|
||||||
|
android:textColor="@color/text_primary"
|
||||||
|
android:textSize="18sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
|
<androidx.core.widget.NestedScrollView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="20dp">
|
||||||
|
|
||||||
|
<!-- Main Options Cards -->
|
||||||
|
<androidx.cardview.widget.CardView
|
||||||
|
android:id="@+id/btnContacts"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="12dp"
|
||||||
|
app:cardCornerRadius="20dp"
|
||||||
|
app:cardElevation="2dp"
|
||||||
|
app:cardBackgroundColor="@color/card_background">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:padding="20dp">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="32dp"
|
||||||
|
android:layout_height="32dp"
|
||||||
|
android:src="@drawable/ic_contacts"
|
||||||
|
app:tint="@color/primary_purple" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:text="Escolher nos contactos"
|
||||||
|
android:textColor="@color/text_primary"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
</LinearLayout>
|
||||||
|
</androidx.cardview.widget.CardView>
|
||||||
|
|
||||||
|
<androidx.cardview.widget.CardView
|
||||||
|
android:id="@+id/btnSearchByName"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="12dp"
|
||||||
|
app:cardCornerRadius="20dp"
|
||||||
|
app:cardElevation="2dp"
|
||||||
|
app:cardBackgroundColor="@color/card_background">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:padding="20dp">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="32dp"
|
||||||
|
android:layout_height="32dp"
|
||||||
|
android:src="@drawable/ic_search"
|
||||||
|
app:tint="@color/streak_blue" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:text="Buscar por nome"
|
||||||
|
android:textColor="@color/text_primary"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
</LinearLayout>
|
||||||
|
</androidx.cardview.widget.CardView>
|
||||||
|
|
||||||
|
<androidx.cardview.widget.CardView
|
||||||
|
android:id="@+id/btnProfileLink"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="32dp"
|
||||||
|
app:cardCornerRadius="20dp"
|
||||||
|
app:cardElevation="2dp"
|
||||||
|
app:cardBackgroundColor="@color/card_background">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:padding="20dp">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="32dp"
|
||||||
|
android:layout_height="32dp"
|
||||||
|
android:src="@drawable/ic_share"
|
||||||
|
app:tint="@color/success_green" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:text="Link do seu perfil"
|
||||||
|
android:textColor="@color/text_primary"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
</LinearLayout>
|
||||||
|
</androidx.cardview.widget.CardView>
|
||||||
|
|
||||||
|
<!-- Suggestions Section -->
|
||||||
|
<RelativeLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="16dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Sugestões de amigos"
|
||||||
|
android:textColor="@color/text_primary"
|
||||||
|
android:textSize="18sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/btnViewAll"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentEnd="true"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:text="VER TODOS"
|
||||||
|
android:textColor="@color/primary_purple"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/rvSuggestions"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
</androidx.core.widget.NestedScrollView>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
@@ -1,12 +1,251 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:orientation="vertical">
|
android:background="@color/background_light">
|
||||||
|
|
||||||
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@color/card_background"
|
||||||
|
app:elevation="0dp">
|
||||||
|
|
||||||
|
<RelativeLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="64dp"
|
||||||
|
android:paddingHorizontal="16dp">
|
||||||
|
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/btnClose"
|
||||||
|
android:layout_width="48dp"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:background="?attr/selectableItemBackgroundBorderless"
|
||||||
|
android:src="@drawable/ic_close"
|
||||||
|
app:tint="@color/text_primary" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="Streak Activity" />
|
android:layout_centerInParent="true"
|
||||||
|
android:text="Ofensiva"
|
||||||
|
android:textColor="@color/text_primary"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
|
<com.google.android.material.tabs.TabLayout
|
||||||
|
android:id="@+id/tabLayout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:tabIndicatorColor="@color/primary_purple"
|
||||||
|
app:tabIndicatorHeight="3dp"
|
||||||
|
app:tabSelectedTextColor="@color/primary_purple"
|
||||||
|
app:tabTextColor="@color/text_secondary">
|
||||||
|
|
||||||
|
<com.google.android.material.tabs.TabItem
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Pessoal" />
|
||||||
|
|
||||||
|
<com.google.android.material.tabs.TabItem
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Amigos" />
|
||||||
|
</com.google.android.material.tabs.TabLayout>
|
||||||
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
|
<androidx.core.widget.NestedScrollView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="24dp">
|
||||||
|
|
||||||
|
<!-- Streak Main State -->
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_marginBottom="32dp">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/ivBigFlame"
|
||||||
|
android:layout_width="120dp"
|
||||||
|
android:layout_height="120dp"
|
||||||
|
android:src="@drawable/ic_flame"
|
||||||
|
android:layout_marginBottom="16dp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvStreakCount"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="7 dias de ofensiva!"
|
||||||
|
android:textColor="@color/text_primary"
|
||||||
|
android:textSize="28sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Estás imparável! Continua assim."
|
||||||
|
android:textColor="@color/text_secondary"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:layout_marginTop="8dp" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- Calendar Section -->
|
||||||
|
<androidx.cardview.widget.CardView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="24dp"
|
||||||
|
app:cardCornerRadius="@dimen/radius_duo"
|
||||||
|
app:cardElevation="2dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="20dp">
|
||||||
|
|
||||||
|
<RelativeLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="16dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvMonthName"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="abril de 2026"
|
||||||
|
android:textColor="@color/text_primary"
|
||||||
|
android:textSize="18sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentEnd="true">
|
||||||
|
|
||||||
|
<ImageButton
|
||||||
|
android:layout_width="32dp"
|
||||||
|
android:layout_height="32dp"
|
||||||
|
android:background="?attr/selectableItemBackgroundBorderless"
|
||||||
|
android:src="@drawable/ic_back"
|
||||||
|
app:tint="@color/text_secondary" />
|
||||||
|
|
||||||
|
<ImageButton
|
||||||
|
android:layout_width="32dp"
|
||||||
|
android:layout_height="32dp"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:background="?attr/selectableItemBackgroundBorderless"
|
||||||
|
android:rotation="180"
|
||||||
|
android:src="@drawable/ic_back"
|
||||||
|
app:tint="@color/text_secondary" />
|
||||||
|
</LinearLayout>
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
|
<!-- Days of Week Headers -->
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_marginBottom="8dp">
|
||||||
|
<TextView android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="S" android:textAlignment="center" android:textStyle="bold" android:textColor="@color/text_secondary" />
|
||||||
|
<TextView android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="T" android:textAlignment="center" android:textStyle="bold" android:textColor="@color/text_secondary" />
|
||||||
|
<TextView android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="Q" android:textAlignment="center" android:textStyle="bold" android:textColor="@color/text_secondary" />
|
||||||
|
<TextView android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="Q" android:textAlignment="center" android:textStyle="bold" android:textColor="@color/text_secondary" />
|
||||||
|
<TextView android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="S" android:textAlignment="center" android:textStyle="bold" android:textColor="@color/text_secondary" />
|
||||||
|
<TextView android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="S" android:textAlignment="center" android:textStyle="bold" android:textColor="@color/text_secondary" />
|
||||||
|
<TextView android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="D" android:textAlignment="center" android:textStyle="bold" android:textColor="@color/text_secondary" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/rvCalendar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:nestedScrollingEnabled="false" />
|
||||||
|
</LinearLayout>
|
||||||
|
</androidx.cardview.widget.CardView>
|
||||||
|
|
||||||
|
<!-- Statistics Grid -->
|
||||||
|
<GridLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:columnCount="2">
|
||||||
|
|
||||||
|
<!-- Days of Practice Card -->
|
||||||
|
<androidx.cardview.widget.CardView
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_columnWeight="1"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
app:cardCornerRadius="16dp"
|
||||||
|
app:cardElevation="2dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="16dp">
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Dias de prática"
|
||||||
|
android:textColor="@color/text_secondary"
|
||||||
|
android:textSize="12sp" />
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="12"
|
||||||
|
android:textColor="@color/text_primary"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:layout_marginTop="4dp" />
|
||||||
|
</LinearLayout>
|
||||||
|
</androidx.cardview.widget.CardView>
|
||||||
|
|
||||||
|
<!-- Focus Sessions Card -->
|
||||||
|
<androidx.cardview.widget.CardView
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_columnWeight="1"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
app:cardCornerRadius="16dp"
|
||||||
|
app:cardElevation="2dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="16dp">
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Sessões de foco"
|
||||||
|
android:textColor="@color/text_secondary"
|
||||||
|
android:textSize="12sp" />
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="24"
|
||||||
|
android:textColor="@color/text_primary"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:layout_marginTop="4dp" />
|
||||||
|
</LinearLayout>
|
||||||
|
</androidx.cardview.widget.CardView>
|
||||||
|
</GridLayout>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
</androidx.core.widget.NestedScrollView>
|
||||||
|
|
||||||
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
|
|||||||
179
app/src/main/res/layout/activity_trophies.xml
Normal file
179
app/src/main/res/layout/activity_trophies.xml
Normal file
@@ -0,0 +1,179 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@color/background_light">
|
||||||
|
|
||||||
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@color/card_background"
|
||||||
|
app:elevation="0dp">
|
||||||
|
|
||||||
|
<RelativeLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="64dp"
|
||||||
|
android:paddingHorizontal="16dp">
|
||||||
|
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/btnBack"
|
||||||
|
android:layout_width="48dp"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:background="?attr/selectableItemBackgroundBorderless"
|
||||||
|
android:src="@drawable/ic_back"
|
||||||
|
app:tint="@color/text_primary" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_centerInParent="true"
|
||||||
|
android:text="Troféus"
|
||||||
|
android:textColor="@color/text_primary"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
</RelativeLayout>
|
||||||
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
|
<androidx.core.widget.NestedScrollView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center_horizontal"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="24dp">
|
||||||
|
|
||||||
|
<!-- Division Info -->
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvDivisionTitle"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Divisão Prata"
|
||||||
|
android:textColor="@color/primary_purple"
|
||||||
|
android:textSize="24sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvTimeRemaining"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:text="3 dias restantes"
|
||||||
|
android:textColor="@color/text_secondary"
|
||||||
|
android:textSize="14sp" />
|
||||||
|
|
||||||
|
<!-- Trophy Progression Container -->
|
||||||
|
<androidx.cardview.widget.CardView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="32dp"
|
||||||
|
android:layout_marginBottom="32dp"
|
||||||
|
app:cardCornerRadius="@dimen/radius_duo"
|
||||||
|
app:cardElevation="2dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="32dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/trophyContainer"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:gravity="center">
|
||||||
|
|
||||||
|
<!-- Trophies will be added here dynamically or statically -->
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/trophy1"
|
||||||
|
android:layout_width="60dp"
|
||||||
|
android:layout_height="60dp"
|
||||||
|
android:layout_margin="8dp"
|
||||||
|
android:src="@drawable/ic_trophy_bronze" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/trophy2"
|
||||||
|
android:layout_width="80dp"
|
||||||
|
android:layout_height="80dp"
|
||||||
|
android:layout_margin="8dp"
|
||||||
|
android:src="@drawable/ic_trophy_silver"
|
||||||
|
android:padding="4dp"
|
||||||
|
android:background="@drawable/circle_bg"
|
||||||
|
android:backgroundTint="#1A6200EE" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/trophy3"
|
||||||
|
android:layout_width="60dp"
|
||||||
|
android:layout_height="60dp"
|
||||||
|
android:layout_margin="8dp"
|
||||||
|
android:alpha="0.3"
|
||||||
|
android:src="@drawable/ic_trophy_gold" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvTrophyProgress"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
android:text="Faltam 15 dias para o próximo troféu"
|
||||||
|
android:textColor="@color/text_primary"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
</LinearLayout>
|
||||||
|
</androidx.cardview.widget.CardView>
|
||||||
|
|
||||||
|
<!-- Inactive State (Initially Hidden) -->
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/inactiveState"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:visibility="gone">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="150dp"
|
||||||
|
android:layout_height="150dp"
|
||||||
|
android:src="@drawable/ic_sleeping_char" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:text="Hora de voltar!"
|
||||||
|
android:textColor="@color/text_primary"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:text="Completa tarefas para competir esta semana."
|
||||||
|
android:textAlignment="center"
|
||||||
|
android:textColor="@color/text_secondary"
|
||||||
|
android:textSize="14sp" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- Active Message -->
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvMotivational"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Estás a progredir bem na Divisão Prata!"
|
||||||
|
android:textAlignment="center"
|
||||||
|
android:textColor="@color/text_secondary"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
</androidx.core.widget.NestedScrollView>
|
||||||
|
|
||||||
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
@@ -170,13 +170,17 @@
|
|||||||
|
|
||||||
<!-- League Card -->
|
<!-- League Card -->
|
||||||
<androidx.cardview.widget.CardView
|
<androidx.cardview.widget.CardView
|
||||||
|
android:id="@+id/cardLeague"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_columnWeight="1"
|
android:layout_columnWeight="1"
|
||||||
android:layout_margin="6dp"
|
android:layout_margin="6dp"
|
||||||
app:cardCornerRadius="@dimen/radius_duo"
|
app:cardCornerRadius="@dimen/radius_duo"
|
||||||
app:cardElevation="2dp"
|
app:cardElevation="2dp"
|
||||||
app:contentPadding="16dp">
|
app:contentPadding="16dp"
|
||||||
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
|
android:foreground="?attr/selectableItemBackground">
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|||||||
49
app/src/main/res/layout/item_calendar_day.xml
Normal file
49
app/src/main/res/layout/item_calendar_day.xml
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="60dp">
|
||||||
|
|
||||||
|
<!-- Streak Background (Line connecting days) -->
|
||||||
|
<View
|
||||||
|
android:id="@+id/streakConnector"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="32dp"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:background="@color/streak_blue"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
|
<!-- Active Day Background (Circle) -->
|
||||||
|
<View
|
||||||
|
android:id="@+id/dayBackground"
|
||||||
|
android:layout_width="40dp"
|
||||||
|
android:layout_height="40dp"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:background="@drawable/circle_bg"
|
||||||
|
|
||||||
|
android:backgroundTint="@color/streak_blue"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
|
<!-- Day Number -->
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvDayNumber"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:text="1"
|
||||||
|
android:textColor="@color/text_primary"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<!-- Indicator Dot (Optional) -->
|
||||||
|
<View
|
||||||
|
android:id="@+id/dayIndicator"
|
||||||
|
android:layout_width="4dp"
|
||||||
|
android:layout_height="4dp"
|
||||||
|
android:layout_gravity="bottom|center_horizontal"
|
||||||
|
android:layout_marginBottom="8dp"
|
||||||
|
android:background="@drawable/circle_bg"
|
||||||
|
|
||||||
|
android:backgroundTint="@color/primary_purple"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
@@ -1,41 +1,67 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:layout_width="wrap_content"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:gravity="center_horizontal"
|
android:gravity="center_horizontal"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/dayOfMonth"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="1"
|
|
||||||
android:textColor="@android:color/black"
|
|
||||||
android:textSize="16sp" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/dayOfWeek"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="Mon"
|
|
||||||
android:textColor="@android:color/darker_gray"
|
|
||||||
android:textSize="12sp" />
|
|
||||||
|
|
||||||
<View
|
<View
|
||||||
android:id="@+id/topConnector"
|
android:id="@+id/topConnector"
|
||||||
android:layout_width="2dp"
|
android:layout_width="4dp"
|
||||||
android:layout_height="16dp"
|
android:layout_height="20dp"
|
||||||
android:background="@android:color/darker_gray" />
|
android:background="@color/border_color" />
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/nodeContainer"
|
||||||
|
android:layout_width="80dp"
|
||||||
|
android:layout_height="80dp">
|
||||||
|
|
||||||
|
<!-- Progress Ring (Outer) -->
|
||||||
|
<ProgressBar
|
||||||
|
android:id="@+id/nodeProgress"
|
||||||
|
style="?android:attr/progressBarStyleHorizontal"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:max="100"
|
||||||
|
android:progress="0"
|
||||||
|
android:progressDrawable="@drawable/node_progress_ring" />
|
||||||
|
|
||||||
|
<!-- Base Circle (Inner) -->
|
||||||
<View
|
<View
|
||||||
android:id="@+id/node"
|
android:id="@+id/nodeCircle"
|
||||||
android:layout_width="16dp"
|
android:layout_width="60dp"
|
||||||
android:layout_height="16dp" />
|
android:layout_height="60dp"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:background="@drawable/node_circle_bg" />
|
||||||
|
|
||||||
|
<!-- Icon or Initial (Optional) -->
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/nodeDayInitial"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:text="S"
|
||||||
|
android:textColor="@color/text_secondary"
|
||||||
|
android:textSize="18sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/nodeDayLabel"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:text="SEG"
|
||||||
|
android:textColor="@color/text_secondary"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
<View
|
<View
|
||||||
android:id="@+id/bottomConnector"
|
android:id="@+id/bottomConnector"
|
||||||
android:layout_width="2dp"
|
android:layout_width="4dp"
|
||||||
android:layout_height="16dp"
|
android:layout_height="20dp"
|
||||||
android:background="@android:color/darker_gray" />
|
android:background="@color/border_color" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
70
app/src/main/res/layout/item_friend_suggestion.xml
Normal file
70
app/src/main/res/layout/item_friend_suggestion.xml
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="160dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="8dp"
|
||||||
|
app:cardCornerRadius="24dp"
|
||||||
|
app:cardElevation="2dp"
|
||||||
|
app:cardBackgroundColor="@color/card_background">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="16dp">
|
||||||
|
|
||||||
|
<androidx.cardview.widget.CardView
|
||||||
|
android:layout_width="64dp"
|
||||||
|
android:layout_height="64dp"
|
||||||
|
android:layout_marginBottom="12dp"
|
||||||
|
app:cardCornerRadius="32dp"
|
||||||
|
app:cardElevation="0dp">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/ivFriendAvatar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:scaleType="centerCrop"
|
||||||
|
android:src="@drawable/ic_nav_profile"
|
||||||
|
app:tint="@color/primary_purple" />
|
||||||
|
</androidx.cardview.widget.CardView>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvFriendName"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Nome"
|
||||||
|
android:textColor="@color/text_primary"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:ellipsize="end" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvFriendInfo"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Segue você"
|
||||||
|
android:textColor="@color/text_secondary"
|
||||||
|
android:textSize="11sp"
|
||||||
|
android:layout_marginBottom="12dp" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btnFollowBack"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="36dp"
|
||||||
|
android:text="Seguir"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
app:cornerRadius="18dp"
|
||||||
|
android:padding="0dp"
|
||||||
|
android:insetTop="0dp"
|
||||||
|
android:insetBottom="0dp"
|
||||||
|
app:backgroundTint="@color/primary_purple" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
</androidx.cardview.widget.CardView>
|
||||||
@@ -4,6 +4,10 @@
|
|||||||
android:id="@+id/nav_inicio"
|
android:id="@+id/nav_inicio"
|
||||||
android:icon="@drawable/ic_nav_home"
|
android:icon="@drawable/ic_nav_home"
|
||||||
android:title="Início" />
|
android:title="Início" />
|
||||||
|
<item
|
||||||
|
android:id="@+id/nav_trophies"
|
||||||
|
android:icon="@drawable/ic_nav_trophy"
|
||||||
|
android:title="Troféus" />
|
||||||
<item
|
<item
|
||||||
android:id="@+id/nav_profile"
|
android:id="@+id/nav_profile"
|
||||||
android:icon="@drawable/ic_nav_profile"
|
android:icon="@drawable/ic_nav_profile"
|
||||||
|
|||||||
18
app/src/main/res/values-night/colors.xml
Normal file
18
app/src/main/res/values-night/colors.xml
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<!-- Dark Mode Palette -->
|
||||||
|
<!-- DO NOT override white/black directly if they are used for text on primary colors -->
|
||||||
|
|
||||||
|
<color name="background_light">#0F172A</color> <!-- Main background -->
|
||||||
|
<color name="card_background">#1F2937</color> <!-- Card/Container background -->
|
||||||
|
|
||||||
|
<color name="text_primary">#FFFFFF</color>
|
||||||
|
<color name="text_secondary">#9CA3AF</color>
|
||||||
|
<color name="border_color">#374151</color>
|
||||||
|
|
||||||
|
<color name="background_purple">#0F172A</color>
|
||||||
|
|
||||||
|
<!-- Duolingo-inspired Palette - adjustments for dark mode if needed -->
|
||||||
|
<color name="primary_purple">#7C3AED</color>
|
||||||
|
<color name="primary_purple_dark">#6D28D9</color>
|
||||||
|
</resources>
|
||||||
18
app/src/main/res/values-night/themes.xml
Normal file
18
app/src/main/res/values-night/themes.xml
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
<!-- Base application theme for Night mode. -->
|
||||||
|
<style name="Theme.Fluxup" parent="Theme.MaterialComponents.DayNight.NoActionBar">
|
||||||
|
<!-- Primary brand color. -->
|
||||||
|
<item name="colorPrimary">@color/primary_purple</item>
|
||||||
|
<item name="colorPrimaryVariant">@color/primary_purple_dark</item>
|
||||||
|
<item name="colorOnPrimary">@color/white</item>
|
||||||
|
<!-- Secondary brand color. -->
|
||||||
|
<item name="colorSecondary">@color/reward_yellow</item>
|
||||||
|
<item name="colorSecondaryVariant">@color/reward_yellow</item>
|
||||||
|
<item name="colorOnSecondary">@color/background_light</item>
|
||||||
|
<!-- Status bar color. -->
|
||||||
|
<item name="android:statusBarColor">@color/background_light</item>
|
||||||
|
<item name="android:windowLightStatusBar" tools:targetApi="m">false</item>
|
||||||
|
<!-- Customize your theme here. -->
|
||||||
|
<item name="android:windowBackground">@color/background_light</item>
|
||||||
|
</style>
|
||||||
|
</resources>
|
||||||
Reference in New Issue
Block a user