fixes for androidacy integration

Signed-off-by: androidacy-user <opensource@androidacy.com>
pull/273/head
androidacy-user 1 year ago
parent 3cc9d05481
commit 609a6c6b19

@ -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");

@ -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;
}
}
}

@ -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<RepoModule> 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();

@ -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")

@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp"
android:height="24dp" android:autoMirrored="true"
android:tint="?attr/colorControlNormal" android:viewportWidth="24" android:viewportHeight="24">
<path android:fillColor="@android:color/white" android:pathData="M12,2C6.5,2 2,6.5 2,12s4.5,10 10,10 10,-4.5 10,-10S17.5,2 12,2zM13.4,18.1L13.4,20h-2.7v-1.9c-1.7,-0.4 -3.2,-1.5 -3.3,-3.4h2c0.1,1.1 0.8,1.9 2.7,1.9 2,0 2.4,-1 2.4,-1.6 0,-0.8 -0.4,-1.6 -2.7,-2.1 -2.5,-0.6 -4.2,-1.6 -4.2,-3.7 0,-1.7 1.4,-2.8 3.1,-3.2L10.7,4h2.7v2c1.9,0.5 2.8,1.9 2.9,3.4L14.3,9.3c-0.1,-1.1 -0.6,-1.9 -2.2,-1.9 -1.5,0 -2.4,0.7 -2.4,1.6 0,0.8 0.7,1.4 2.7,1.9s4.2,1.4 4.2,3.9c0,1.8 -1.4,2.8 -3.1,3.2z"/>
</vector>

@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp"
android:height="24dp" android:autoMirrored="true"
android:tint="#000000" android:viewportWidth="24" android:viewportHeight="24">
<path android:fillColor="@android:color/white" android:pathData="M16,18v2H8v-2H16zM11,8V16h2V8h3L12,4L8,8H11z"/>
</vector>

@ -283,5 +283,5 @@
<string name="setup_theme_title">Choose theme</string>
<string name="setup_language_button">Choose language</string>
<string name="title_activity_setup">Setup Wizard</string>
<string name="action_settings">Settings</string>
<string name="action_settings">Settings</string><string name="repo_update_failed_message">The following repos have failed to update:\\n\\n%s</string><string name="reset_api_key">Reset API keys</string><string name="upgrade_androidacy_promo">Upgrade to premium</string><string name="upgrade_androidacy_promo_desc">Upgrading to premium will remove ads, captchas, and downloads for the Androidacy Repository, and support Androidacy and the module authors.</string>
</resources>

@ -12,7 +12,7 @@
<!-- Initially hidden edittextpreference for pref_androidacy_api_token -->
<EditTextPreference
app:icon="@drawable/ic_baseline_vpn_key_24"
app:key="pref_androidacy_api_token"
app:key="pref_androidacy_repo_api_token"
app:singleLineTitle="false"
app:dependency="pref_androidacy_repo_enabled"
app:isPreferenceVisible="false"
@ -25,6 +25,12 @@
app:singleLineTitle="false"
app:summary="@string/androidacy_test_mode_desc"
app:title="@string/androidacy_test_mode_pref" />
<com.fox2code.mmm.settings.LongClickablePreference
app:icon="@drawable/baseline_monetization_on_24"
app:key="pref_androidacy_repo_donate"
app:singleLineTitle="false"
app:summary="@string/upgrade_androidacy_promo_desc"
app:title="@string/upgrade_androidacy_promo" />
<com.fox2code.mmm.settings.LongClickablePreference
app:icon="@drawable/ic_baseline_language_24"
app:key="pref_androidacy_repo_website"
@ -35,11 +41,6 @@
app:key="pref_androidacy_repo_support"
app:singleLineTitle="false"
app:title="@string/support" />
<com.fox2code.mmm.settings.LongClickablePreference
app:icon="@drawable/ic_patreon"
app:key="pref_androidacy_repo_donate"
app:singleLineTitle="false"
app:title="@string/donate" />
<com.fox2code.mmm.settings.LongClickablePreference
app:icon="@drawable/ic_baseline_upload_file_24"
app:key="pref_androidacy_repo_submit"

Loading…
Cancel
Save