diff --git a/app/src/main/java/com/fox2code/mmm/androidacy/AndroidacyActivity.java b/app/src/main/java/com/fox2code/mmm/androidacy/AndroidacyActivity.java index d65d46e..09168e5 100644 --- a/app/src/main/java/com/fox2code/mmm/androidacy/AndroidacyActivity.java +++ b/app/src/main/java/com/fox2code/mmm/androidacy/AndroidacyActivity.java @@ -99,8 +99,7 @@ public final class AndroidacyActivity extends FoxActivity { String token = uri.getQueryParameter("token"); if (token == null) { // get from shared preferences - token = MainApplication.getSharedPreferences().getString("pref_androidacy_api_token", null); - url = url + "&token=" + token; + url = url + "&token=" + AndroidacyRepoData.token; } // Add device_id to url if not present String device_id = uri.getQueryParameter("device_id"); diff --git a/app/src/main/java/com/fox2code/mmm/androidacy/AndroidacyRepoData.java b/app/src/main/java/com/fox2code/mmm/androidacy/AndroidacyRepoData.java index 4ee8862..51edd40 100644 --- a/app/src/main/java/com/fox2code/mmm/androidacy/AndroidacyRepoData.java +++ b/app/src/main/java/com/fox2code/mmm/androidacy/AndroidacyRepoData.java @@ -41,6 +41,10 @@ import okhttp3.HttpUrl; @SuppressWarnings("KotlinInternalInJava") public final class AndroidacyRepoData extends RepoData { + + public static String token = MainApplication.getINSTANCE().getSharedPreferences("androidacy", 0).getString("pref_androidacy_api_token", null); + public SharedPreferences cachedPreferences = MainApplication.getINSTANCE().getSharedPreferences("androidacy", 0); + private static final String TAG = "AndroidacyRepoData"; static { @@ -55,7 +59,7 @@ public final class AndroidacyRepoData extends RepoData { private final boolean testMode; private final String host; - public String token = MainApplication.getINSTANCE().getSharedPreferences("androidacy", 0).getString("pref_androidacy_api_token", null); + public String memberLevel; // Avoid spamming requests to Androidacy private long androidacyBlockade = 0; @@ -76,7 +80,7 @@ public final class AndroidacyRepoData extends RepoData { this.defaultName = "Androidacy Modules Repo"; this.defaultWebsite = RepoManager.ANDROIDACY_MAGISK_REPO_HOMEPAGE; this.defaultSupport = "https://t.me/androidacy_discussions"; - this.defaultDonate = "https://www.androidacy.com/membership-join/?utm_source=foxmmm&utm-medium=app&utm_campaign=fox-inapp"; + this.defaultDonate = "https://www.androidacy.com/membership-account/membership-checkout/?level=2&discount_code=FOXWINTER2&utm_souce=foxmmm&utm_medium=android-app&utm_campaign=fox-upgrade-promo"; this.defaultSubmitModule = "https://www.androidacy.com/module-repository-applications/"; this.host = testMode ? "staging-api.androidacy.com" : "production-api.androidacy.com"; this.testMode = testMode; @@ -148,7 +152,20 @@ public final class AndroidacyRepoData extends RepoData { public boolean isValidToken(String token) throws IOException, NoSuchAlgorithmException { String deviceId = generateDeviceId(); try { - Http.doHttpGet("https://" + this.host + "/auth/me?token=" + token + "&device_id=" + deviceId, false); + byte[] resp = Http.doHttpGet("https://" + this.host + "/auth/me?token=" + token + "&device_id=" + deviceId, false); + JSONObject jsonObject = new JSONObject(new String(resp)); + memberLevel = jsonObject.getString("role"); + String status = jsonObject.getString("status"); + if (status.equals("success")) { + return true; + } else { + Log.w(TAG, "Invalid token, resetting..."); + // Remove saved preference + SharedPreferences.Editor editor = MainApplication.getINSTANCE().getSharedPreferences("androidacy", 0).edit(); + editor.remove("pref_androidacy_api_token"); + editor.apply(); + return false; + } } catch ( HttpException e) { if (e.getErrorCode() == 401) { @@ -160,9 +177,11 @@ public final class AndroidacyRepoData extends RepoData { return false; } throw e; + } catch ( + JSONException e) { + // response is not JSON + throw new IOException(e); } - // If status code is 200, we are good - return true; } @SuppressLint("RestrictedApi") @@ -170,7 +189,7 @@ public final class AndroidacyRepoData extends RepoData { protected boolean prepare() throws NoSuchAlgorithmException { // If ANDROIDACY_CLIENT_ID is not set or is empty, disable this repo and return if (Objects.equals(BuildConfig.ANDROIDACY_CLIENT_ID, "")) { - SharedPreferences.Editor editor = this.cachedPreferences.edit(); + SharedPreferences.Editor editor = MainApplication.getSharedPreferences().edit(); editor.putBoolean("pref_androidacy_repo_enabled", false); editor.apply(); return false; @@ -210,18 +229,18 @@ public final class AndroidacyRepoData extends RepoData { // don'e fail just becaue we're rate limited. API and web rate limits are different. this.androidacyBlockade = time + 30_000L; try { - if (this.token == null) { - this.token = this.cachedPreferences.getString("pref_androidacy_api_token", null); - if (this.token != null && !this.isValidToken(this.token)) { - this.token = null; + if (token == null) { + token = this.cachedPreferences.getString("pref_androidacy_api_token", null); + if (token != null && !this.isValidToken(token)) { + token = null; } else { Log.i(TAG, "Using cached token"); } - } else if (!this.isValidToken(this.token)) { + } else if (!this.isValidToken(token)) { if (BuildConfig.DEBUG) { - throw new IllegalStateException("Invalid token: " + this.token); + throw new IllegalStateException("Invalid token: " + token); } - this.token = null; + token = null; } } catch ( IOException e) { @@ -234,12 +253,13 @@ public final class AndroidacyRepoData extends RepoData { if (token == null) { try { Log.i(TAG, "Requesting new token..."); - // POST json request to https://production-api.androidacy.com/auth/register + // POST json request to https://produc/tion-api.androidacy.com/auth/register token = new String(Http.doHttpPost("https://" + this.host + "/auth/register", "{\"device_id\":\"" + deviceId + "\"}", false)); // Parse token try { JSONObject jsonObject = new JSONObject(token); token = jsonObject.getString("token"); + memberLevel = jsonObject.getString("role"); } catch ( JSONException e) { Log.e(TAG, "Failed to parse token", e); @@ -269,7 +289,7 @@ public final class AndroidacyRepoData extends RepoData { } } //noinspection SillyAssignment // who are you calling silly? - this.token = token; + token = token; return true; } @@ -389,7 +409,7 @@ public final class AndroidacyRepoData extends RepoData { @Override public String getUrl() throws NoSuchAlgorithmException { - return this.token == null ? this.url : this.url + "?token=" + this.token + "&v=" + BuildConfig.VERSION_CODE + "&c=" + BuildConfig.VERSION_NAME + "&device_id=" + generateDeviceId(); + return token == null ? this.url : this.url + "?token=" + token + "&v=" + BuildConfig.VERSION_CODE + "&c=" + BuildConfig.VERSION_NAME + "&device_id=" + generateDeviceId() + "&client_id=" + BuildConfig.ANDROIDACY_CLIENT_ID; } private String injectToken(String url) throws NoSuchAlgorithmException { @@ -407,7 +427,7 @@ public final class AndroidacyRepoData extends RepoData { url = "https://production-api.androidacy.com/" + url.substring(35); } } - String token = "token=" + this.token; + String token = "token=" + AndroidacyRepoData.token; String deviceId = "device_id=" + generateDeviceId(); if (!url.contains(token)) { if (url.lastIndexOf('/') < url.lastIndexOf('?')) { @@ -434,7 +454,7 @@ public final class AndroidacyRepoData extends RepoData { public void setToken(String token) { if (Http.hasWebView()) { - this.token = token; + AndroidacyRepoData.token = token; } } } diff --git a/app/src/main/java/com/fox2code/mmm/repo/RepoManager.java b/app/src/main/java/com/fox2code/mmm/repo/RepoManager.java index 5ab83f7..75d860a 100644 --- a/app/src/main/java/com/fox2code/mmm/repo/RepoManager.java +++ b/app/src/main/java/com/fox2code/mmm/repo/RepoManager.java @@ -7,6 +7,7 @@ import android.content.SharedPreferences; import android.os.Handler; import android.os.Looper; import android.util.Log; +import android.widget.Toast; import androidx.annotation.NonNull; @@ -23,7 +24,7 @@ import com.fox2code.mmm.utils.Hashes; import com.fox2code.mmm.utils.Http; import com.fox2code.mmm.utils.PropUtils; import com.fox2code.mmm.utils.SyncManager; -import com.google.android.material.snackbar.Snackbar; +import com.google.android.material.dialog.MaterialAlertDialogBuilder; import java.io.File; import java.io.IOException; @@ -165,19 +166,22 @@ public final class RepoManager extends SyncManager { INSTANCE.androidacyRepoData.isEnabled(); } + @SuppressWarnings("StatementWithEmptyBody") private void populateDefaultCache(RepoData repoData) { for (RepoModule repoModule : repoData.moduleHashMap.values()) { if (!repoModule.moduleInfo.hasFlag(ModuleInfo.FLAG_METADATA_INVALID)) { RepoModule registeredRepoModule = this.modules.get(repoModule.id); if (registeredRepoModule == null) { this.modules.put(repoModule.id, repoModule); - } else if (repoModule.moduleInfo.versionCode > - registeredRepoModule.moduleInfo.versionCode) { + } else if (AndroidacyRepoData.getInstance().isEnabled() && registeredRepoModule.repoData == this.androidacyRepoData) { + // empty + } else if (AndroidacyRepoData.getInstance().isEnabled() && repoModule.repoData == this.androidacyRepoData) { + this.modules.put(repoModule.id, repoModule); + } else if (repoModule.moduleInfo.versionCode > registeredRepoModule.moduleInfo.versionCode) { this.modules.put(repoModule.id, repoModule); } } else { - Log.e(TAG, "Detected module with invalid metadata: " + - repoModule.repoName + "/" + repoModule.id); + Log.e(TAG, "Detected module with invalid metadata: " + repoModule.repoName + "/" + repoModule.id); } } } @@ -217,6 +221,7 @@ public final class RepoManager extends SyncManager { return repoData; } + @SuppressWarnings("StatementWithEmptyBody") @SuppressLint("StringFormatInvalid") protected void scanInternal(@NonNull UpdateListener updateListener) { // Refuse to start if first_launch is not false in shared preferences @@ -226,14 +231,12 @@ public final class RepoManager extends SyncManager { this.modules.clear(); updateListener.update(0D); // Using LinkedHashSet to deduplicate Androidacy entry. - RepoData[] repoDatas = new LinkedHashSet<>( - this.repoData.values()).toArray(new RepoData[0]); + RepoData[] repoDatas = new LinkedHashSet<>(this.repoData.values()).toArray(new RepoData[0]); RepoUpdater[] repoUpdaters = new RepoUpdater[repoDatas.length]; int moduleToUpdate = 0; for (int i = 0; i < repoDatas.length; i++) { if (BuildConfig.DEBUG) Log.d("RepoManager", "Fetching: " + repoDatas[i].getName()); - moduleToUpdate += (repoUpdaters[i] = - new RepoUpdater(repoDatas[i])).fetchIndex(); + moduleToUpdate += (repoUpdaters[i] = new RepoUpdater(repoDatas[i])).fetchIndex(); updateListener.update(STEP1 / repoDatas.length * (i + 1)); } if (BuildConfig.DEBUG) Log.d("RepoManag3er", "Updating meta-data"); @@ -242,8 +245,7 @@ public final class RepoManager extends SyncManager { for (int i = 0; i < repoUpdaters.length; i++) { // Check if the repo is enabled if (!repoUpdaters[i].repoData.isEnabled()) { - if (BuildConfig.DEBUG) Log.d("RepoManager", - "Skipping disabled repo: " + repoUpdaters[i].repoData.getName()); + if (BuildConfig.DEBUG) Log.d("RepoManager", "Skipping disabled repo: " + repoUpdaters[i].repoData.getName()); continue; } List repoModules = repoUpdaters[i].toUpdate(); @@ -251,21 +253,20 @@ public final class RepoManager extends SyncManager { if (BuildConfig.DEBUG) Log.d("RepoManager", "Registering " + repoData.getName()); for (RepoModule repoModule : repoModules) { try { - if (repoModule.propUrl != null && - !repoModule.propUrl.isEmpty()) { - repoData.storeMetadata(repoModule, - Http.doHttpGet(repoModule.propUrl, false)); - Files.write(new File(repoData.cacheRoot, repoModule.id + ".prop"), - Http.doHttpGet(repoModule.propUrl, false)); + if (repoModule.propUrl != null && !repoModule.propUrl.isEmpty()) { + repoData.storeMetadata(repoModule, Http.doHttpGet(repoModule.propUrl, false)); + Files.write(new File(repoData.cacheRoot, repoModule.id + ".prop"), Http.doHttpGet(repoModule.propUrl, false)); } - if (repoData.tryLoadMetadata(repoModule) && (allowLowQualityModules || - !PropUtils.isLowQualityModule(repoModule.moduleInfo))) { + if (repoData.tryLoadMetadata(repoModule) && (allowLowQualityModules || !PropUtils.isLowQualityModule(repoModule.moduleInfo))) { // Note: registeredRepoModule may not be null if registered by multiple repos RepoModule registeredRepoModule = this.modules.get(repoModule.id); if (registeredRepoModule == null) { this.modules.put(repoModule.id, repoModule); - } else if (repoModule.moduleInfo.versionCode > - registeredRepoModule.moduleInfo.versionCode) { + } else if (AndroidacyRepoData.getInstance().isEnabled() && registeredRepoModule.repoData == this.androidacyRepoData) { + // empty + } else if (AndroidacyRepoData.getInstance().isEnabled() && repoModule.repoData == this.androidacyRepoData) { + this.modules.put(repoModule.id, repoModule); + } else if (repoModule.moduleInfo.versionCode > registeredRepoModule.moduleInfo.versionCode) { this.modules.put(repoModule.id, repoModule); } } @@ -280,8 +281,11 @@ public final class RepoManager extends SyncManager { RepoModule registeredRepoModule = this.modules.get(repoModule.id); if (registeredRepoModule == null) { this.modules.put(repoModule.id, repoModule); - } else if (repoModule.moduleInfo.versionCode > - registeredRepoModule.moduleInfo.versionCode) { + } else if (AndroidacyRepoData.getInstance().isEnabled() && registeredRepoModule.repoData == this.androidacyRepoData) { + // empty + } else if (AndroidacyRepoData.getInstance().isEnabled() && repoModule.repoData == this.androidacyRepoData) { + this.modules.put(repoModule.id, repoModule); + } else if (repoModule.moduleInfo.versionCode > registeredRepoModule.moduleInfo.versionCode) { this.modules.put(repoModule.id, repoModule); } } @@ -293,14 +297,12 @@ public final class RepoManager extends SyncManager { // Attempt to contact connectivitycheck.gstatic.com/generate_204 // If we can't, we don't have internet connection try { - HttpURLConnection urlConnection = (HttpURLConnection) new URL( - "https://connectivitycheck.gstatic.com/generate_204").openConnection(); + HttpURLConnection urlConnection = (HttpURLConnection) new URL("https://connectivitycheck.gstatic.com/generate_204").openConnection(); urlConnection.setInstanceFollowRedirects(false); urlConnection.setReadTimeout(1000); urlConnection.setUseCaches(false); urlConnection.getInputStream().close(); - if (urlConnection.getResponseCode() == 204 && - urlConnection.getContentLength() == 0) { + if (urlConnection.getResponseCode() == 204 && urlConnection.getContentLength() == 0) { this.hasInternet = true; } } catch (IOException e) { @@ -310,12 +312,10 @@ public final class RepoManager extends SyncManager { for (int i = 0; i < repoDatas.length; i++) { // If repo is not enabled, skip if (!repoDatas[i].isEnabled()) { - if (BuildConfig.DEBUG) Log.d("RepoManager", - "Skipping " + repoDatas[i].getName() + " because it's disabled"); + if (BuildConfig.DEBUG) Log.d("RepoManager", "Skipping " + repoDatas[i].getName() + " because it's disabled"); continue; } - if (BuildConfig.DEBUG) Log.d("RepoManager", - "Finishing: " + repoUpdaters[i].repoData.getName()); + if (BuildConfig.DEBUG) Log.d("RepoManager", "Finishing: " + repoUpdaters[i].repoData.getName()); this.repoLastSuccess = repoUpdaters[i].finish(); if (!this.repoLastSuccess) { Log.e(TAG, "Failed to update " + repoUpdaters[i].repoData.getName()); @@ -324,10 +324,20 @@ public final class RepoManager extends SyncManager { Activity context = MainApplication.getINSTANCE().getLastCompatActivity(); new Handler(Looper.getMainLooper()).post(() -> { if (context != null) { - Snackbar.make(context.findViewById(android.R.id.content), - context.getString(R.string.repo_update_failed_extended, - repoUpdaters[finalI].repoData.getName()), - Snackbar.LENGTH_LONG).show(); + // Show material dialogue with the repo name. for androidacy repo, show an option to reset the api key. show a message then a list of errors + MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(context); + builder.setTitle(R.string.repo_update_failed); + builder.setMessage(context.getString(R.string.repo_update_failed_message, "- " + repoUpdaters[finalI].repoData.getName())); + builder.setPositiveButton(android.R.string.ok, null); + if (repoUpdaters[finalI].repoData.getName().equals("Androidacy")) { + builder.setNeutralButton(R.string.reset_api_key, (dialog, which) -> { + SharedPreferences.Editor editor = MainApplication.getINSTANCE().getSharedPreferences("androidacy", 0).edit(); + editor.putString("androidacy_api_key", ""); + editor.apply(); + Toast.makeText(context, R.string.api_key_removed, Toast.LENGTH_SHORT).show(); + }); + } + builder.show(); } }); this.repoLastErrorName = repoUpdaters[i].repoData.getName(); diff --git a/app/src/main/java/com/fox2code/mmm/settings/SettingsActivity.java b/app/src/main/java/com/fox2code/mmm/settings/SettingsActivity.java index 532ef5d..42ede06 100644 --- a/app/src/main/java/com/fox2code/mmm/settings/SettingsActivity.java +++ b/app/src/main/java/com/fox2code/mmm/settings/SettingsActivity.java @@ -711,111 +711,68 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity { return false; }); } - String[] originalApiKeyRef = new String[]{MainApplication.getINSTANCE().getSharedPreferences("androidacy", 0).getString("pref_androidacy_api_token", "")}; - // Get the dummy pref_androidacy_repo_api_token EditTextPreference - EditTextPreference prefAndroidacyRepoApiKey = Objects.requireNonNull(findPreference("pref_androidacy_api_token")); - prefAndroidacyRepoApiKey.setTitle(R.string.api_key); - prefAndroidacyRepoApiKey.setSummary(R.string.api_key_summary); - prefAndroidacyRepoApiKey.setDialogTitle(R.string.api_key); - prefAndroidacyRepoApiKey.setDefaultValue(originalApiKeyRef[0]); - // Set the value to the current value - prefAndroidacyRepoApiKey.setText(originalApiKeyRef[0]); - prefAndroidacyRepoApiKey.setOnBindEditTextListener(editText -> { - editText.setSingleLine(); - // Make the single line wrap - editText.setHorizontallyScrolling(false); - // Set the height to the maximum required to fit the text - editText.setMaxLines(Integer.MAX_VALUE); - // Make ok button say "Save" - editText.setImeOptions(EditorInfo.IME_ACTION_DONE); - }); - prefAndroidacyRepoApiKey.setPositiveButtonText(R.string.save_api_key); - prefAndroidacyRepoApiKey.setOnPreferenceChangeListener((preference, newValue) -> { - // Make sure originalApiKeyRef is not null - if (originalApiKeyRef[0].equals(newValue)) - return true; - // get original api key - String apiKey = String.valueOf(newValue); - // Show snack bar with indeterminate progress - Snackbar.make(requireView(), R.string.checking_api_key, Snackbar.LENGTH_INDEFINITE).setAction(R.string.cancel, v -> { - // Restore the original api key - prefAndroidacyRepoApiKey.setText(originalApiKeyRef[0]); - }).show(); - // Check the API key on a background thread - new Thread(() -> { - // If key is empty, just remove it and change the text of the snack bar - if (apiKey.isEmpty()) { - MainApplication.getINSTANCE().getSharedPreferences("androidacy", 0).edit().remove("pref_androidacy_api_token").apply(); - new Handler(Looper.getMainLooper()).post(() -> { - Snackbar.make(requireView(), R.string.api_key_removed, Snackbar.LENGTH_SHORT).show(); - // Show dialog to restart app with ok button - new MaterialAlertDialogBuilder(this.requireContext()).setTitle(R.string.restart).setCancelable(false).setMessage(R.string.api_key_restart).setNeutralButton(android.R.string.ok, (dialog, which) -> { - // User clicked OK button - Intent mStartActivity = new Intent(requireContext(), MainActivity.class); - mStartActivity.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK); - int mPendingIntentId = 123456; - // If < 23, FLAG_IMMUTABLE is not available - PendingIntent mPendingIntent; - mPendingIntent = PendingIntent.getActivity(requireContext(), mPendingIntentId, mStartActivity, PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE); - AlarmManager mgr = (AlarmManager) requireContext().getSystemService(Context.ALARM_SERVICE); - mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 100, mPendingIntent); - if (BuildConfig.DEBUG) { - Log.d(TAG, "Restarting app to save token preference: " + newValue); - } - System.exit(0); // Exit app process - }).show(); - }); - } else { - // If key < 64 chars, it's not valid - if (apiKey.length() < 64) { + // get if androidacy repo is enabled + boolean androidacyRepoEnabledPref = MainApplication.getSharedPreferences().getBoolean("pref_androidacy_repo_enabled", false); + if (androidacyRepoEnabledPref) { + String[] originalApiKeyRef = new String[]{MainApplication.getINSTANCE().getSharedPreferences("androidacy", 0).getString("pref_androidacy_api_token", "")}; + // Get the dummy pref_androidacy_repo_api_token preference with id pref_androidacy_repo_api_token + // we have to use the id because the key is different + EditTextPreference prefAndroidacyRepoApiKey = Objects.requireNonNull(findPreference("pref_androidacy_repo_api_token")); + prefAndroidacyRepoApiKey.setTitle(R.string.api_key); + prefAndroidacyRepoApiKey.setSummary(R.string.api_key_summary); + prefAndroidacyRepoApiKey.setDialogTitle(R.string.api_key); + prefAndroidacyRepoApiKey.setDefaultValue(originalApiKeyRef[0]); + // Set the value to the current value + prefAndroidacyRepoApiKey.setText(originalApiKeyRef[0]); + prefAndroidacyRepoApiKey.setVisible(true); + prefAndroidacyRepoApiKey.setOnBindEditTextListener(editText -> { + editText.setSingleLine(); + // Make the single line wrap + editText.setHorizontallyScrolling(false); + // Set the height to the maximum required to fit the text + editText.setMaxLines(Integer.MAX_VALUE); + // Make ok button say "Save" + editText.setImeOptions(EditorInfo.IME_ACTION_DONE); + }); + prefAndroidacyRepoApiKey.setPositiveButtonText(R.string.save_api_key); + prefAndroidacyRepoApiKey.setOnPreferenceChangeListener((preference, newValue) -> { + // Make sure originalApiKeyRef is not null + if (originalApiKeyRef[0].equals(newValue)) + return true; + // get original api key + String apiKey = String.valueOf(newValue); + // Show snack bar with indeterminate progress + Snackbar.make(requireView(), R.string.checking_api_key, Snackbar.LENGTH_INDEFINITE).setAction(R.string.cancel, v -> { + // Restore the original api key + prefAndroidacyRepoApiKey.setText(originalApiKeyRef[0]); + }).show(); + // Check the API key on a background thread + new Thread(() -> { + // If key is empty, just remove it and change the text of the snack bar + if (apiKey.isEmpty()) { + MainApplication.getINSTANCE().getSharedPreferences("androidacy", 0).edit().remove("pref_androidacy_api_token").apply(); new Handler(Looper.getMainLooper()).post(() -> { - Snackbar.make(requireView(), R.string.api_key_invalid, Snackbar.LENGTH_SHORT).show(); - // Save the original key - MainApplication.getINSTANCE().getSharedPreferences("androidacy", 0).edit().putString("pref_androidacy_api_token", originalApiKeyRef[0]).apply(); - // Re-show the dialog with an error - prefAndroidacyRepoApiKey.performClick(); - // Show error - prefAndroidacyRepoApiKey.setDialogMessage(getString(R.string.api_key_invalid)); + Snackbar.make(requireView(), R.string.api_key_removed, Snackbar.LENGTH_SHORT).show(); + // Show dialog to restart app with ok button + new MaterialAlertDialogBuilder(this.requireContext()).setTitle(R.string.restart).setCancelable(false).setMessage(R.string.api_key_restart).setNeutralButton(android.R.string.ok, (dialog, which) -> { + // User clicked OK button + Intent mStartActivity = new Intent(requireContext(), MainActivity.class); + mStartActivity.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK); + int mPendingIntentId = 123456; + // If < 23, FLAG_IMMUTABLE is not available + PendingIntent mPendingIntent; + mPendingIntent = PendingIntent.getActivity(requireContext(), mPendingIntentId, mStartActivity, PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE); + AlarmManager mgr = (AlarmManager) requireContext().getSystemService(Context.ALARM_SERVICE); + mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 100, mPendingIntent); + if (BuildConfig.DEBUG) { + Log.d(TAG, "Restarting app to save token preference: " + newValue); + } + System.exit(0); // Exit app process + }).show(); }); } else { - // If the key is the same as the original, just show a snack bar - if (apiKey.equals(originalApiKeyRef[0])) { - new Handler(Looper.getMainLooper()).post(() -> Snackbar.make(requireView(), R.string.api_key_unchanged, Snackbar.LENGTH_SHORT).show()); - return; - } - boolean valid = false; - try { - valid = AndroidacyRepoData.getInstance().isValidToken(apiKey); - } catch ( - IOException | - NoSuchAlgorithmException ignored) { - } - // If the key is valid, save it - if (valid) { - originalApiKeyRef[0] = apiKey; - RepoManager.getINSTANCE().getAndroidacyRepoData().setToken(apiKey); - MainApplication.getINSTANCE().getSharedPreferences("androidacy", 0).edit().putString("pref_androidacy_api_token", apiKey).apply(); - // Snackbar with success and restart button - new Handler(Looper.getMainLooper()).post(() -> { - Snackbar.make(requireView(), R.string.api_key_valid, Snackbar.LENGTH_SHORT).show(); - // Show dialog to restart app with ok button - new MaterialAlertDialogBuilder(this.requireContext()).setTitle(R.string.restart).setCancelable(false).setMessage(R.string.api_key_restart).setNeutralButton(android.R.string.ok, (dialog, which) -> { - // User clicked OK button - Intent mStartActivity = new Intent(requireContext(), MainActivity.class); - mStartActivity.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK); - int mPendingIntentId = 123456; - // If < 23, FLAG_IMMUTABLE is not available - PendingIntent mPendingIntent; - mPendingIntent = PendingIntent.getActivity(requireContext(), mPendingIntentId, mStartActivity, PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE); - AlarmManager mgr = (AlarmManager) requireContext().getSystemService(Context.ALARM_SERVICE); - mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 100, mPendingIntent); - if (BuildConfig.DEBUG) { - Log.d(TAG, "Restarting app to save token preference: " + newValue); - } - System.exit(0); // Exit app process - }).show(); - }); - } else { + // If key < 64 chars, it's not valid + if (apiKey.length() < 64) { new Handler(Looper.getMainLooper()).post(() -> { Snackbar.make(requireView(), R.string.api_key_invalid, Snackbar.LENGTH_SHORT).show(); // Save the original key @@ -825,14 +782,61 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity { // Show error prefAndroidacyRepoApiKey.setDialogMessage(getString(R.string.api_key_invalid)); }); + } else { + // If the key is the same as the original, just show a snack bar + if (apiKey.equals(originalApiKeyRef[0])) { + new Handler(Looper.getMainLooper()).post(() -> Snackbar.make(requireView(), R.string.api_key_unchanged, Snackbar.LENGTH_SHORT).show()); + return; + } + boolean valid = false; + try { + valid = AndroidacyRepoData.getInstance().isValidToken(apiKey); + } catch ( + IOException | + NoSuchAlgorithmException ignored) { + } + // If the key is valid, save it + if (valid) { + originalApiKeyRef[0] = apiKey; + RepoManager.getINSTANCE().getAndroidacyRepoData().setToken(apiKey); + MainApplication.getINSTANCE().getSharedPreferences("androidacy", 0).edit().putString("pref_androidacy_api_token", apiKey).apply(); + // Snackbar with success and restart button + new Handler(Looper.getMainLooper()).post(() -> { + Snackbar.make(requireView(), R.string.api_key_valid, Snackbar.LENGTH_SHORT).show(); + // Show dialog to restart app with ok button + new MaterialAlertDialogBuilder(this.requireContext()).setTitle(R.string.restart).setCancelable(false).setMessage(R.string.api_key_restart).setNeutralButton(android.R.string.ok, (dialog, which) -> { + // User clicked OK button + Intent mStartActivity = new Intent(requireContext(), MainActivity.class); + mStartActivity.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK); + int mPendingIntentId = 123456; + // If < 23, FLAG_IMMUTABLE is not available + PendingIntent mPendingIntent; + mPendingIntent = PendingIntent.getActivity(requireContext(), mPendingIntentId, mStartActivity, PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE); + AlarmManager mgr = (AlarmManager) requireContext().getSystemService(Context.ALARM_SERVICE); + mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 100, mPendingIntent); + if (BuildConfig.DEBUG) { + Log.d(TAG, "Restarting app to save token preference: " + newValue); + } + System.exit(0); // Exit app process + }).show(); + }); + } else { + new Handler(Looper.getMainLooper()).post(() -> { + Snackbar.make(requireView(), R.string.api_key_invalid, Snackbar.LENGTH_SHORT).show(); + // Save the original key + MainApplication.getINSTANCE().getSharedPreferences("androidacy", 0).edit().putString("pref_androidacy_api_token", originalApiKeyRef[0]).apply(); + // Re-show the dialog with an error + prefAndroidacyRepoApiKey.performClick(); + // Show error + prefAndroidacyRepoApiKey.setDialogMessage(getString(R.string.api_key_invalid)); + }); + } } } - } - }).start(); - return true; - }); - // make sure the preference is visible if repo is enabled - prefAndroidacyRepoApiKey.setVisible(RepoManager.getINSTANCE().getAndroidacyRepoData().isEnabled()); + }).start(); + return true; + }); + } } @SuppressLint("RestrictedApi") diff --git a/app/src/main/res/drawable/baseline_monetization_on_24.xml b/app/src/main/res/drawable/baseline_monetization_on_24.xml new file mode 100644 index 0000000..61df9b3 --- /dev/null +++ b/app/src/main/res/drawable/baseline_monetization_on_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/baseline_upgrade_24.xml b/app/src/main/res/drawable/baseline_upgrade_24.xml new file mode 100644 index 0000000..8d28802 --- /dev/null +++ b/app/src/main/res/drawable/baseline_upgrade_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 61f4acb..143cf7a 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -283,5 +283,5 @@ Choose theme Choose language Setup Wizard - Settings + SettingsThe following repos have failed to update:\\n\\n%sReset API keysUpgrade to premiumUpgrading to premium will remove ads, captchas, and downloads for the Androidacy Repository, and support Androidacy and the module authors. diff --git a/app/src/main/res/xml/repo_preferences.xml b/app/src/main/res/xml/repo_preferences.xml index 93a03ea..a6168be 100644 --- a/app/src/main/res/xml/repo_preferences.xml +++ b/app/src/main/res/xml/repo_preferences.xml @@ -12,7 +12,7 @@ + -