{"repo": "brian-www/ecospend", "files": {"app/src/main/java/com/ecospend/app/api/DeepSeekApiClient.java": {"f649ec4c3731afaaa9daaf96343018beee573878": {"t": 1785745692.9243107, "n": 5716, "h": "404229d1774f5386", "c": "package com.ecospend.app.api;\n\nimport android.os.Handler;\nimport android.os.Looper;\nimport android.util.Log;\n\nimport org.json.JSONArray;\nimport org.json.JSONObject;\n\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\n\nimport okhttp3.MediaType;\nimport okhttp3.OkHttpClient;\nimport okhttp3.Request;\nimport okhttp3.RequestBody;\nimport okhttp3.Response;\n\npublic class DeepSeekApiClient {\n\n private static final String TAG = \"DeepSeekApiClient\";\n private static final String API_URL = \"https://openrouter.ai/api/v1/chat/completions\";\n private static final String MODEL = \"openrouter/free\";\n // API key should be set by user in settings; using placeholder\n private String apiKey;\n\n private final OkHttpClient client;\n private final ExecutorService executor;\n private final Handler mainHandler;\n\n public interface AiCallback {\n void onSuccess(String response);\n void onError(String error);\n }\n\n public DeepSeekApiClient(String apiKey) {\n this.apiKey = apiKey;\n client = new OkHttpClient.Builder()\n .connectTimeout(30, java.util.concurrent.TimeUnit.SECONDS)\n .readTimeout(60, java.util.concurrent.TimeUnit.SECONDS)\n .build();\n executor = Executors.newSingleThreadExecutor();\n mainHandler = new Handler(Looper.getMainLooper());\n }\n\n public void analyzeSpending(String spendingContext, AiCallback callback) {\n if (apiKey == null || apiKey.isEmpty()\n || apiKey.equals(\"YOUR_DEEPSEEK_API_KEY\")\n || (!apiKey.startsWith(\"sk-or-\") && !apiKey.startsWith(\"sk-\"))) {\n callback.onError(\"Please enter a valid OpenRouter API key (starts with sk-or-...) in Profile > Settings.\");\n return;\n }\n\n executor.execute(() -> {\n try {\n JSONObject systemMsg = new JSONObject();\n systemMsg.put(\"role\", \"system\");\n systemMsg.put(\"content\",\n \"You are EcoSpend's friendly AI financial advisor. Analyze spending data and provide \" +\n \"concise, actionable, encouraging advice. Keep responses under 300 words. \" +\n \"Use simple language. Format with short paragraphs. Start with a brief summary, \" +\n \"then 2-3 specific tips, then an encouraging closing line.\");\n\n JSONObject userMsg = new JSONObject();\n userMsg.put(\"role\", \"user\");\n userMsg.put(\"content\", spendingContext);\n\n JSONArray messages = new JSONArray();\n messages.put(systemMsg);\n messages.put(userMsg);\n\n JSONObject body = new JSONObject();\n body.put(\"model\", MODEL);\n body.put(\"messages\", messages);\n body.put(\"max_tokens\", 500);\n body.put(\"temperature\", 0.7);\n\n RequestBody requestBody = RequestBody.create(\n body.toString(),\n MediaType.parse(\"application/json; charset=utf-8\")\n );\n\n Request request = new Request.Builder()\n .url(API_URL)\n .addHeader(\"Authorization\", \"Bearer \" + apiKey)\n .addHeader(\"Content-Type\", \"application/json\")\n .addHeader(\"X-Title\", \"EcoSpend\")\n .post(requestBody)\n .build();\n\n try (Response response = client.newCall(request).execute()) {\n if (response.isSuccessful() && response.body() != null) {\n String responseStr = response.body().string();\n Log.d(TAG, \"Raw response: \" + responseStr); // helpful for debugging\n JSONObject json = new JSONObject(responseStr);\n JSONObject message = json\n .getJSONArray(\"choices\")\n .getJSONObject(0)\n .getJSONObject(\"message\");\n\n // Some free models (e.g. R1) return reasoning_content\n // and set content to null — fall back gracefully\n String content = null;\n if (!message.isNull(\"content\")) {\n content = message.getString(\"content\");\n }\n if ((content == null || content.trim().isEmpty())\n && !message.isNull(\"reasoning_content\")) {\n content = message.getString(\"reasoning_content\");\n }\n if (content == null || content.trim().isEmpty()) {\n content = \"No response received. Try again.\";\n }\n\n final String finalContent = content;\n mainHandler.post(() -> callback.onSuccess(finalContent));\n } else {\n String errBody = response.body() != null ? response.body().string() : \"Unknown error\";\n Log.e(TAG, \"API error: \" + response.code() + \" \" + errBody);\n mainHandler.post(() -> callback.onError(\"AI service error (\" + response.code() + \"). Check your API key.\"));\n }\n }\n } catch (Exception e) {\n Log.e(TAG, \"DeepSeek API error\", e);\n mainHandler.post(() -> callback.onError(\"Connection error: \" + e.getMessage()));\n }\n });\n }\n\n public void setApiKey(String apiKey) {\n this.apiKey = apiKey;\n }\n}\n"}, "136d9d7672d029d140ee8dea23c1047658b8ad52": {"t": 1785755732.6800303, "n": 5716, "h": "404229d1774f5386", "c": "package com.ecospend.app.api;\n\nimport android.os.Handler;\nimport android.os.Looper;\nimport android.util.Log;\n\nimport org.json.JSONArray;\nimport org.json.JSONObject;\n\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\n\nimport okhttp3.MediaType;\nimport okhttp3.OkHttpClient;\nimport okhttp3.Request;\nimport okhttp3.RequestBody;\nimport okhttp3.Response;\n\npublic class DeepSeekApiClient {\n\n private static final String TAG = \"DeepSeekApiClient\";\n private static final String API_URL = \"https://openrouter.ai/api/v1/chat/completions\";\n private static final String MODEL = \"openrouter/free\";\n // API key should be set by user in settings; using placeholder\n private String apiKey;\n\n private final OkHttpClient client;\n private final ExecutorService executor;\n private final Handler mainHandler;\n\n public interface AiCallback {\n void onSuccess(String response);\n void onError(String error);\n }\n\n public DeepSeekApiClient(String apiKey) {\n this.apiKey = apiKey;\n client = new OkHttpClient.Builder()\n .connectTimeout(30, java.util.concurrent.TimeUnit.SECONDS)\n .readTimeout(60, java.util.concurrent.TimeUnit.SECONDS)\n .build();\n executor = Executors.newSingleThreadExecutor();\n mainHandler = new Handler(Looper.getMainLooper());\n }\n\n public void analyzeSpending(String spendingContext, AiCallback callback) {\n if (apiKey == null || apiKey.isEmpty()\n || apiKey.equals(\"YOUR_DEEPSEEK_API_KEY\")\n || (!apiKey.startsWith(\"sk-or-\") && !apiKey.startsWith(\"sk-\"))) {\n callback.onError(\"Please enter a valid OpenRouter API key (starts with sk-or-...) in Profile > Settings.\");\n return;\n }\n\n executor.execute(() -> {\n try {\n JSONObject systemMsg = new JSONObject();\n systemMsg.put(\"role\", \"system\");\n systemMsg.put(\"content\",\n \"You are EcoSpend's friendly AI financial advisor. Analyze spending data and provide \" +\n \"concise, actionable, encouraging advice. Keep responses under 300 words. \" +\n \"Use simple language. Format with short paragraphs. Start with a brief summary, \" +\n \"then 2-3 specific tips, then an encouraging closing line.\");\n\n JSONObject userMsg = new JSONObject();\n userMsg.put(\"role\", \"user\");\n userMsg.put(\"content\", spendingContext);\n\n JSONArray messages = new JSONArray();\n messages.put(systemMsg);\n messages.put(userMsg);\n\n JSONObject body = new JSONObject();\n body.put(\"model\", MODEL);\n body.put(\"messages\", messages);\n body.put(\"max_tokens\", 500);\n body.put(\"temperature\", 0.7);\n\n RequestBody requestBody = RequestBody.create(\n body.toString(),\n MediaType.parse(\"application/json; charset=utf-8\")\n );\n\n Request request = new Request.Builder()\n .url(API_URL)\n .addHeader(\"Authorization\", \"Bearer \" + apiKey)\n .addHeader(\"Content-Type\", \"application/json\")\n .addHeader(\"X-Title\", \"EcoSpend\")\n .post(requestBody)\n .build();\n\n try (Response response = client.newCall(request).execute()) {\n if (response.isSuccessful() && response.body() != null) {\n String responseStr = response.body().string();\n Log.d(TAG, \"Raw response: \" + responseStr); // helpful for debugging\n JSONObject json = new JSONObject(responseStr);\n JSONObject message = json\n .getJSONArray(\"choices\")\n .getJSONObject(0)\n .getJSONObject(\"message\");\n\n // Some free models (e.g. R1) return reasoning_content\n // and set content to null — fall back gracefully\n String content = null;\n if (!message.isNull(\"content\")) {\n content = message.getString(\"content\");\n }\n if ((content == null || content.trim().isEmpty())\n && !message.isNull(\"reasoning_content\")) {\n content = message.getString(\"reasoning_content\");\n }\n if (content == null || content.trim().isEmpty()) {\n content = \"No response received. Try again.\";\n }\n\n final String finalContent = content;\n mainHandler.post(() -> callback.onSuccess(finalContent));\n } else {\n String errBody = response.body() != null ? response.body().string() : \"Unknown error\";\n Log.e(TAG, \"API error: \" + response.code() + \" \" + errBody);\n mainHandler.post(() -> callback.onError(\"AI service error (\" + response.code() + \"). Check your API key.\"));\n }\n }\n } catch (Exception e) {\n Log.e(TAG, \"DeepSeek API error\", e);\n mainHandler.post(() -> callback.onError(\"Connection error: \" + e.getMessage()));\n }\n });\n }\n\n public void setApiKey(String apiKey) {\n this.apiKey = apiKey;\n }\n}\n"}, "4418bcbe1833d89315e8d27df1439a2b00bf8cc0": {"t": 1785755734.130449, "n": 4654, "h": "7acf5862fcc84e52", "c": "package com.ecospend.app.api;\n\nimport android.os.Handler;\nimport android.os.Looper;\nimport android.util.Log;\n\nimport org.json.JSONArray;\nimport org.json.JSONObject;\n\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\n\nimport okhttp3.MediaType;\nimport okhttp3.OkHttpClient;\nimport okhttp3.Request;\nimport okhttp3.RequestBody;\nimport okhttp3.Response;\n\npublic class DeepSeekApiClient {\n\n private static final String TAG = \"DeepSeekApiClient\";\n private static final String API_URL = \"https://api.deepseek.com/chat/completions\";\n private static final String MODEL = \"deepseek-chat\";\n // API key should be set by user in settings; using placeholder\n private String apiKey;\n\n private final OkHttpClient client;\n private final ExecutorService executor;\n private final Handler mainHandler;\n\n public interface AiCallback {\n void onSuccess(String response);\n void onError(String error);\n }\n\n public DeepSeekApiClient(String apiKey) {\n this.apiKey = apiKey;\n client = new OkHttpClient.Builder()\n .connectTimeout(30, java.util.concurrent.TimeUnit.SECONDS)\n .readTimeout(60, java.util.concurrent.TimeUnit.SECONDS)\n .build();\n executor = Executors.newSingleThreadExecutor();\n mainHandler = new Handler(Looper.getMainLooper());\n }\n\n public void analyzeSpending(String spendingContext, AiCallback callback) {\n if (apiKey == null || apiKey.isEmpty() || apiKey.equals(\"YOUR_DEEPSEEK_API_KEY\")) {\n callback.onError(\"Please set your DeepSeek API key in Profile > Settings to use AI features.\");\n return;\n }\n\n executor.execute(() -> {\n try {\n JSONObject systemMsg = new JSONObject();\n systemMsg.put(\"role\", \"system\");\n systemMsg.put(\"content\",\n \"You are EcoSpend's friendly AI financial advisor. Analyze spending data and provide \" +\n \"concise, actionable, encouraging advice. Keep responses under 300 words. \" +\n \"Use simple language. Format with short paragraphs. Start with a brief summary, \" +\n \"then 2-3 specific tips, then an encouraging closing line.\");\n\n JSONObject userMsg = new JSONObject();\n userMsg.put(\"role\", \"user\");\n userMsg.put(\"content\", spendingContext);\n\n JSONArray messages = new JSONArray();\n messages.put(systemMsg);\n messages.put(userMsg);\n\n JSONObject body = new JSONObject();\n body.put(\"model\", MODEL);\n body.put(\"messages\", messages);\n body.put(\"max_tokens\", 500);\n body.put(\"temperature\", 0.7);\n\n RequestBody requestBody = RequestBody.create(\n body.toString(),\n MediaType.parse(\"application/json; charset=utf-8\")\n );\n\n Request request = new Request.Builder()\n .url(API_URL)\n .addHeader(\"Authorization\", \"Bearer \" + apiKey)\n .addHeader(\"Content-Type\", \"application/json\")\n .post(requestBody)\n .build();\n\n try (Response response = client.newCall(request).execute()) {\n if (response.isSuccessful() && response.body() != null) {\n String responseStr = response.body().string();\n JSONObject json = new JSONObject(responseStr);\n String content = json\n .getJSONArray(\"choices\")\n .getJSONObject(0)\n .getJSONObject(\"message\")\n .getString(\"content\");\n mainHandler.post(() -> callback.onSuccess(content));\n } else {\n String errBody = response.body() != null ? response.body().string() : \"Unknown error\";\n Log.e(TAG, \"API error: \" + response.code() + \" \" + errBody);\n mainHandler.post(() -> callback.onError(\"AI service error (\" + response.code() + \"). Check your API key.\"));\n }\n }\n } catch (Exception e) {\n Log.e(TAG, \"DeepSeek API error\", e);\n mainHandler.post(() -> callback.onError(\"Connection error: \" + e.getMessage()));\n }\n });\n }\n\n public void setApiKey(String apiKey) {\n this.apiKey = apiKey;\n }\n}\n"}}, "app/src/main/java/com/ecospend/app/utils/Constants.java": {"f649ec4c3731afaaa9daaf96343018beee573878": {"t": 1786068305.1972694, "n": 3644, "h": "4c4e1ba76589cf49", "c": "package com.ecospend.app.utils;\n\nimport com.ecospend.app.R;\n\nimport java.util.LinkedHashMap;\nimport java.util.Map;\n\npublic class Constants {\n\n public static final String PREFS_NAME = \"EcoSpendPrefs\";\n public static final String PREF_DEEPSEEK_KEY = \"deepseek_api_key\";\n public static final String PREF_USER_NAME = \"user_name\";\n public static final String PREF_BASE_CURRENCY = \"base_currency\";\n public static final String PREF_MONTHLY_BUDGET = \"monthly_budget\";\n public static final String PREF_ONBOARDING_DONE = \"onboarding_done\";\n\n public static final String TYPE_EXPENSE = \"expense\";\n public static final String TYPE_INCOME = \"income\";\n\n // Category names\n public static final String CAT_FOOD = \"Food & Dining\";\n public static final String CAT_TRANSPORT = \"Transport\";\n public static final String CAT_SHOPPING = \"Shopping\";\n public static final String CAT_BILLS = \"Bills & Utilities\";\n public static final String CAT_ENTERTAINMENT = \"Entertainment\";\n public static final String CAT_HEALTH = \"Health\";\n public static final String CAT_EDUCATION = \"Education\";\n public static final String CAT_TRAVEL = \"Travel\";\n public static final String CAT_SUBSCRIPTION = \"Subscriptions\";\n public static final String CAT_SALARY = \"Salary\";\n public static final String CAT_FREELANCE = \"Freelance\";\n public static final String CAT_OTHER = \"Other\";\n\n public static final String[] EXPENSE_CATEGORIES = {\n CAT_FOOD, CAT_TRANSPORT, CAT_SHOPPING, CAT_BILLS,\n CAT_ENTERTAINMENT, CAT_HEALTH, CAT_EDUCATION,\n CAT_TRAVEL, CAT_SUBSCRIPTION, CAT_OTHER\n };\n\n public static final String[] INCOME_CATEGORIES = {\n CAT_SALARY, CAT_FREELANCE, CAT_OTHER\n };\n\n // Popular currencies\n public static final String[] CURRENCIES = {\n \"MYR\", \"USD\", \"EUR\", \"GBP\", \"SGD\", \"JPY\", \"CNY\",\n \"AUD\", \"CAD\", \"HKD\", \"THB\", \"IDR\", \"KRW\", \"INR\"\n };\n\n public static int getCategoryIcon(String category) {\n switch (category) {\n case CAT_FOOD: return R.drawable.ic_food;\n case CAT_TRANSPORT: return R.drawable.ic_transport;\n case CAT_SHOPPING: return R.drawable.ic_shopping;\n case CAT_BILLS: return R.drawable.ic_bills;\n case CAT_ENTERTAINMENT: return R.drawable.ic_entertainment;\n case CAT_HEALTH: return R.drawable.ic_health;\n case CAT_EDUCATION: return R.drawable.ic_education;\n case CAT_TRAVEL: return R.drawable.ic_travel;\n case CAT_SUBSCRIPTION: return R.drawable.ic_subscription;\n case CAT_SALARY: return R.drawable.ic_salary;\n case CAT_FREELANCE: return R.drawable.ic_freelance;\n default: return R.drawable.ic_other;\n }\n }\n\n public static int getCategoryColor(String category) {\n switch (category) {\n case CAT_FOOD: return 0xFFFF6B6B;\n case CAT_TRANSPORT: return 0xFF4ECDC4;\n case CAT_SHOPPING: return 0xFF45B7D1;\n case CAT_BILLS: return 0xFFF7DC6F;\n case CAT_ENTERTAINMENT: return 0xFFBB8FCE;\n case CAT_HEALTH: return 0xFF82E0AA;\n case CAT_EDUCATION: return 0xFFF0B27A;\n case CAT_TRAVEL: return 0xFF85C1E9;\n case CAT_SUBSCRIPTION: return 0xFFAEB6BF;\n case CAT_SALARY: return 0xFF58D68D;\n case CAT_FREELANCE: return 0xFF48C9B0;\n default: return 0xFFAEB6BF;\n }\n }\n\n public static final int[] CHART_COLORS = {\n 0xFFFF6B6B, 0xFF4ECDC4, 0xFF45B7D1, 0xFFF7DC6F,\n 0xFFBB8FCE, 0xFF82E0AA, 0xFFF0B27A, 0xFF85C1E9,\n 0xFFAEB6BF, 0xFF58D68D\n };\n}\n"}}}}