55import org .apache .cordova .*;
66import org .json .JSONArray ;
77import org .json .JSONException ;
8- import org .json .JSONObject ;
98import java .net .HttpURLConnection ;
109import java .net .URL ;
11- import java .text .SimpleDateFormat ;
12- import java .util .Date ;
13- import java .util .Locale ;
14- import java .util .Random ;
1510import java .util .Scanner ;
1611
1712public class Authenticator extends CordovaPlugin {
1813 // Standard practice: use a TAG for easy filtering in Logcat
1914 private static final String TAG = "AcodeAuth" ;
2015 private static final String PREFS_FILENAME = "acode_auth_secure" ;
2116 private static final String KEY_TOKEN = "auth_token" ;
22- private static final String ADS_PREFS_FILENAME = "ads" ;
23- private static final String KEY_REWARD_STATE = "reward_state" ;
24- private static final long ONE_HOUR_MS = 60L * 60L * 1000L ;
25- private static final long MAX_ACTIVE_PASS_MS = 10L * ONE_HOUR_MS ;
26- private static final int MAX_REDEMPTIONS_PER_DAY = 3 ;
2717 private EncryptedPreferenceManager prefManager ;
28- private EncryptedPreferenceManager adsPrefManager ;
2918
3019 @ Override
3120 protected void pluginInitialize () {
3221 Log .d (TAG , "Initializing Authenticator Plugin..." );
3322 this .prefManager = new EncryptedPreferenceManager (this .cordova .getContext (), PREFS_FILENAME );
34- this .adsPrefManager = new EncryptedPreferenceManager (this .cordova .getContext (), ADS_PREFS_FILENAME );
3523 }
3624
3725 @ Override
@@ -54,12 +42,6 @@ public boolean execute(String action, JSONArray args, CallbackContext callbackCo
5442 prefManager .setString (KEY_TOKEN , token );
5543 callbackContext .success ();
5644 return true ;
57- case "getRewardStatus" :
58- callbackContext .success (getRewardStatus ());
59- return true ;
60- case "redeemReward" :
61- callbackContext .success (redeemReward (args .getString (0 )));
62- return true ;
6345 default :
6446 Log .w (TAG , "Attempted to call unknown action: " + action );
6547 return false ;
@@ -128,171 +110,6 @@ private void getUserInfo(CallbackContext callbackContext) {
128110 });
129111 }
130112
131- private String getRewardStatus () throws JSONException {
132- JSONObject state = syncRewardState (loadRewardState ());
133- JSONObject status = buildRewardStatus (state );
134-
135- if (status .optBoolean ("hasPendingExpiryNotice" )) {
136- state .put ("expiryNoticePendingUntil" , 0 );
137- }
138-
139- saveRewardState (state );
140- return status .toString ();
141- }
142-
143- private String redeemReward (String offerId ) throws JSONException {
144- JSONObject state = syncRewardState (loadRewardState ());
145- int redemptionsToday = state .optInt ("redemptionsToday" , 0 );
146- long now = System .currentTimeMillis ();
147- long adFreeUntil = state .optLong ("adFreeUntil" , 0 );
148- long remainingMs = Math .max (0L , adFreeUntil - now );
149-
150- if (redemptionsToday >= MAX_REDEMPTIONS_PER_DAY ) {
151- throw new JSONException (
152- "Daily limit reached. You can redeem up to " + MAX_REDEMPTIONS_PER_DAY + " rewards per day."
153- );
154- }
155-
156- if (remainingMs >= MAX_ACTIVE_PASS_MS ) {
157- throw new JSONException ("You already have the maximum 10 hours of ad-free time active." );
158- }
159-
160- long grantedDurationMs = resolveRewardDuration (offerId );
161- long baseTime = Math .max (now , adFreeUntil );
162- long newAdFreeUntil = Math .min (baseTime + grantedDurationMs , now + MAX_ACTIVE_PASS_MS );
163- long appliedDurationMs = Math .max (0L , newAdFreeUntil - baseTime );
164-
165- state .put ("adFreeUntil" , newAdFreeUntil );
166- state .put ("lastExpiredRewardUntil" , 0 );
167- state .put ("expiryNoticePendingUntil" , 0 );
168- state .put ("redemptionDay" , getTodayKey ());
169- state .put ("redemptionsToday" , redemptionsToday + 1 );
170- saveRewardState (state );
171-
172- JSONObject status = buildRewardStatus (state );
173- status .put ("grantedDurationMs" , grantedDurationMs );
174- status .put ("appliedDurationMs" , appliedDurationMs );
175- status .put ("offerId" , offerId );
176- return status .toString ();
177- }
178-
179- private JSONObject loadRewardState () {
180- String raw = adsPrefManager .getString (KEY_REWARD_STATE , "" );
181- if (raw == null || raw .isEmpty ()) {
182- return defaultRewardState ();
183- }
184-
185- try {
186- JSONObject parsed = new JSONObject (raw );
187- return mergeRewardState (parsed );
188- } catch (JSONException error ) {
189- Log .w (TAG , "Failed to parse reward state, resetting." , error );
190- return defaultRewardState ();
191- }
192- }
193-
194- private JSONObject defaultRewardState () {
195- JSONObject state = new JSONObject ();
196- try {
197- state .put ("adFreeUntil" , 0L );
198- state .put ("lastExpiredRewardUntil" , 0L );
199- state .put ("expiryNoticePendingUntil" , 0L );
200- state .put ("redemptionDay" , getTodayKey ());
201- state .put ("redemptionsToday" , 0 );
202- } catch (JSONException ignored ) {
203- // No-op; JSONObject puts for primitives should not fail in practice.
204- }
205- return state ;
206- }
207-
208- private JSONObject mergeRewardState (JSONObject parsed ) {
209- JSONObject state = defaultRewardState ();
210- try {
211- state .put ("adFreeUntil" , parsed .optLong ("adFreeUntil" , 0L ));
212- state .put ("lastExpiredRewardUntil" , parsed .optLong ("lastExpiredRewardUntil" , 0L ));
213- state .put ("expiryNoticePendingUntil" , parsed .optLong ("expiryNoticePendingUntil" , 0L ));
214- state .put ("redemptionDay" , parsed .optString ("redemptionDay" , getTodayKey ()));
215- state .put ("redemptionsToday" , parsed .optInt ("redemptionsToday" , 0 ));
216- } catch (JSONException ignored ) {
217- // Ignore and keep defaults.
218- }
219- return state ;
220- }
221-
222- private void saveRewardState (JSONObject state ) {
223- adsPrefManager .setString (KEY_REWARD_STATE , state .toString ());
224- }
225-
226- private JSONObject syncRewardState (JSONObject state ) throws JSONException {
227- String todayKey = getTodayKey ();
228- if (!todayKey .equals (state .optString ("redemptionDay" , todayKey ))) {
229- state .put ("redemptionDay" , todayKey );
230- state .put ("redemptionsToday" , 0 );
231- }
232-
233- long adFreeUntil = state .optLong ("adFreeUntil" , 0L );
234- long now = System .currentTimeMillis ();
235- if (adFreeUntil > 0L && adFreeUntil <= now ) {
236- if (state .optLong ("expiryNoticePendingUntil" , 0L ) != adFreeUntil ) {
237- state .put ("expiryNoticePendingUntil" , adFreeUntil );
238- }
239- state .put ("lastExpiredRewardUntil" , adFreeUntil );
240- state .put ("adFreeUntil" , 0L );
241- }
242-
243- return state ;
244- }
245-
246- private JSONObject buildRewardStatus (JSONObject state ) throws JSONException {
247- long now = System .currentTimeMillis ();
248- long adFreeUntil = state .optLong ("adFreeUntil" , 0L );
249- int redemptionsToday = state .optInt ("redemptionsToday" , 0 );
250- long remainingMs = Math .max (0L , adFreeUntil - now );
251- int remainingRedemptions = Math .max (0 , MAX_REDEMPTIONS_PER_DAY - redemptionsToday );
252-
253- JSONObject status = new JSONObject ();
254- status .put ("adFreeUntil" , adFreeUntil );
255- status .put ("lastExpiredRewardUntil" , state .optLong ("lastExpiredRewardUntil" , 0L ));
256- status .put ("isActive" , adFreeUntil > now );
257- status .put ("remainingMs" , remainingMs );
258- status .put ("redemptionsToday" , redemptionsToday );
259- status .put ("remainingRedemptions" , remainingRedemptions );
260- status .put ("maxRedemptionsPerDay" , MAX_REDEMPTIONS_PER_DAY );
261- status .put ("maxActivePassMs" , MAX_ACTIVE_PASS_MS );
262- status .put ("hasPendingExpiryNotice" , state .optLong ("expiryNoticePendingUntil" , 0L ) > 0L );
263- status .put ("expiryNoticePendingUntil" , state .optLong ("expiryNoticePendingUntil" , 0L ));
264-
265- boolean canRedeem = remainingRedemptions > 0 && remainingMs < MAX_ACTIVE_PASS_MS ;
266- status .put ("canRedeem" , canRedeem );
267- status .put ("redeemDisabledReason" , getRedeemDisabledReason (remainingRedemptions , remainingMs ));
268- return status ;
269- }
270-
271- private String getRedeemDisabledReason (int remainingRedemptions , long remainingMs ) {
272- if (remainingRedemptions <= 0 ) {
273- return "Daily limit reached. You can redeem up to " + MAX_REDEMPTIONS_PER_DAY + " rewards per day." ;
274- }
275- if (remainingMs >= MAX_ACTIVE_PASS_MS ) {
276- return "You already have the maximum 10 hours of ad-free time active." ;
277- }
278- return "" ;
279- }
280-
281- private long resolveRewardDuration (String offerId ) throws JSONException {
282- if ("quick" .equals (offerId )) {
283- return ONE_HOUR_MS ;
284- }
285- if ("focus" .equals (offerId )) {
286- int selectedHours = 4 + new Random ().nextInt (3 );
287- return selectedHours * ONE_HOUR_MS ;
288- }
289- throw new JSONException ("Unknown reward offer." );
290- }
291-
292- private String getTodayKey () {
293- return new SimpleDateFormat ("yyyy-MM-dd" , Locale .US ).format (new Date ());
294- }
295-
296113 private String validateToken (String token ) {
297114 HttpURLConnection conn = null ;
298115 try {
0 commit comments