fixes for androidacy integration

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

@ -99,8 +99,7 @@ public final class AndroidacyActivity extends FoxActivity {
String token = uri.getQueryParameter("token"); String token = uri.getQueryParameter("token");
if (token == null) { if (token == null) {
// get from shared preferences // get from shared preferences
token = MainApplication.getSharedPreferences().getString("pref_androidacy_api_token", null); url = url + "&token=" + AndroidacyRepoData.token;
url = url + "&token=" + token;
} }
// Add device_id to url if not present // Add device_id to url if not present
String device_id = uri.getQueryParameter("device_id"); String device_id = uri.getQueryParameter("device_id");

@ -41,6 +41,10 @@ import okhttp3.HttpUrl;
@SuppressWarnings("KotlinInternalInJava") @SuppressWarnings("KotlinInternalInJava")
public final class AndroidacyRepoData extends RepoData { 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"; private static final String TAG = "AndroidacyRepoData";
static { static {
@ -55,7 +59,7 @@ public final class AndroidacyRepoData extends RepoData {
private final boolean testMode; private final boolean testMode;
private final String host; 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 // Avoid spamming requests to Androidacy
private long androidacyBlockade = 0; private long androidacyBlockade = 0;
@ -76,7 +80,7 @@ public final class AndroidacyRepoData extends RepoData {
this.defaultName = "Androidacy Modules Repo"; this.defaultName = "Androidacy Modules Repo";
this.defaultWebsite = RepoManager.ANDROIDACY_MAGISK_REPO_HOMEPAGE; this.defaultWebsite = RepoManager.ANDROIDACY_MAGISK_REPO_HOMEPAGE;
this.defaultSupport = "https://t.me/androidacy_discussions"; 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.defaultSubmitModule = "https://www.androidacy.com/module-repository-applications/";
this.host = testMode ? "staging-api.androidacy.com" : "production-api.androidacy.com"; this.host = testMode ? "staging-api.androidacy.com" : "production-api.androidacy.com";
this.testMode = testMode; this.testMode = testMode;
@ -148,7 +152,20 @@ public final class AndroidacyRepoData extends RepoData {
public boolean isValidToken(String token) throws IOException, NoSuchAlgorithmException { public boolean isValidToken(String token) throws IOException, NoSuchAlgorithmException {
String deviceId = generateDeviceId(); String deviceId = generateDeviceId();
try { 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 ( } catch (
HttpException e) { HttpException e) {
if (e.getErrorCode() == 401) { if (e.getErrorCode() == 401) {
@ -160,9 +177,11 @@ public final class AndroidacyRepoData extends RepoData {
return false; return false;
} }
throw e; 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") @SuppressLint("RestrictedApi")
@ -170,7 +189,7 @@ public final class AndroidacyRepoData extends RepoData {
protected boolean prepare() throws NoSuchAlgorithmException { protected boolean prepare() throws NoSuchAlgorithmException {
// If ANDROIDACY_CLIENT_ID is not set or is empty, disable this repo and return // If ANDROIDACY_CLIENT_ID is not set or is empty, disable this repo and return
if (Objects.equals(BuildConfig.ANDROIDACY_CLIENT_ID, "")) { 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.putBoolean("pref_androidacy_repo_enabled", false);
editor.apply(); editor.apply();
return false; 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. // don'e fail just becaue we're rate limited. API and web rate limits are different.
this.androidacyBlockade = time + 30_000L; this.androidacyBlockade = time + 30_000L;
try { try {
if (this.token == null) { if (token == null) {
this.token = this.cachedPreferences.getString("pref_androidacy_api_token", null); token = this.cachedPreferences.getString("pref_androidacy_api_token", null);
if (this.token != null && !this.isValidToken(this.token)) { if (token != null && !this.isValidToken(token)) {
this.token = null; token = null;
} else { } else {
Log.i(TAG, "Using cached token"); Log.i(TAG, "Using cached token");
} }
} else if (!this.isValidToken(this.token)) { } else if (!this.isValidToken(token)) {
if (BuildConfig.DEBUG) { if (BuildConfig.DEBUG) {
throw new IllegalStateException("Invalid token: " + this.token); throw new IllegalStateException("Invalid token: " + token);
} }
this.token = null; token = null;
} }
} catch ( } catch (
IOException e) { IOException e) {
@ -234,12 +253,13 @@ public final class AndroidacyRepoData extends RepoData {
if (token == null) { if (token == null) {
try { try {
Log.i(TAG, "Requesting new token..."); 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)); token = new String(Http.doHttpPost("https://" + this.host + "/auth/register", "{\"device_id\":\"" + deviceId + "\"}", false));
// Parse token // Parse token
try { try {
JSONObject jsonObject = new JSONObject(token); JSONObject jsonObject = new JSONObject(token);
token = jsonObject.getString("token"); token = jsonObject.getString("token");
memberLevel = jsonObject.getString("role");
} catch ( } catch (
JSONException e) { JSONException e) {
Log.e(TAG, "Failed to parse token", 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? //noinspection SillyAssignment // who are you calling silly?
this.token = token; token = token;
return true; return true;
} }
@ -389,7 +409,7 @@ public final class AndroidacyRepoData extends RepoData {
@Override @Override
public String getUrl() throws NoSuchAlgorithmException { 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 { 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); url = "https://production-api.androidacy.com/" + url.substring(35);
} }
} }
String token = "token=" + this.token; String token = "token=" + AndroidacyRepoData.token;
String deviceId = "device_id=" + generateDeviceId(); String deviceId = "device_id=" + generateDeviceId();
if (!url.contains(token)) { if (!url.contains(token)) {
if (url.lastIndexOf('/') < url.lastIndexOf('?')) { if (url.lastIndexOf('/') < url.lastIndexOf('?')) {
@ -434,7 +454,7 @@ public final class AndroidacyRepoData extends RepoData {
public void setToken(String token) { public void setToken(String token) {
if (Http.hasWebView()) { if (Http.hasWebView()) {
this.token = token; AndroidacyRepoData.token = token;
} }
} }
} }

@ -7,6 +7,7 @@ import android.content.SharedPreferences;
import android.os.Handler; import android.os.Handler;
import android.os.Looper; import android.os.Looper;
import android.util.Log; import android.util.Log;
import android.widget.Toast;
import androidx.annotation.NonNull; 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.Http;
import com.fox2code.mmm.utils.PropUtils; import com.fox2code.mmm.utils.PropUtils;
import com.fox2code.mmm.utils.SyncManager; 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.File;
import java.io.IOException; import java.io.IOException;
@ -165,19 +166,22 @@ public final class RepoManager extends SyncManager {
INSTANCE.androidacyRepoData.isEnabled(); INSTANCE.androidacyRepoData.isEnabled();
} }
@SuppressWarnings("StatementWithEmptyBody")
private void populateDefaultCache(RepoData repoData) { private void populateDefaultCache(RepoData repoData) {
for (RepoModule repoModule : repoData.moduleHashMap.values()) { for (RepoModule repoModule : repoData.moduleHashMap.values()) {
if (!repoModule.moduleInfo.hasFlag(ModuleInfo.FLAG_METADATA_INVALID)) { if (!repoModule.moduleInfo.hasFlag(ModuleInfo.FLAG_METADATA_INVALID)) {
RepoModule registeredRepoModule = this.modules.get(repoModule.id); RepoModule registeredRepoModule = this.modules.get(repoModule.id);
if (registeredRepoModule == null) { if (registeredRepoModule == null) {
this.modules.put(repoModule.id, repoModule); this.modules.put(repoModule.id, repoModule);
} else if (repoModule.moduleInfo.versionCode > } else if (AndroidacyRepoData.getInstance().isEnabled() && registeredRepoModule.repoData == this.androidacyRepoData) {
registeredRepoModule.moduleInfo.versionCode) { // 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); this.modules.put(repoModule.id, repoModule);
} }
} else { } else {
Log.e(TAG, "Detected module with invalid metadata: " + Log.e(TAG, "Detected module with invalid metadata: " + repoModule.repoName + "/" + repoModule.id);
repoModule.repoName + "/" + repoModule.id);
} }
} }
} }
@ -217,6 +221,7 @@ public final class RepoManager extends SyncManager {
return repoData; return repoData;
} }
@SuppressWarnings("StatementWithEmptyBody")
@SuppressLint("StringFormatInvalid") @SuppressLint("StringFormatInvalid")
protected void scanInternal(@NonNull UpdateListener updateListener) { protected void scanInternal(@NonNull UpdateListener updateListener) {
// Refuse to start if first_launch is not false in shared preferences // 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(); this.modules.clear();
updateListener.update(0D); updateListener.update(0D);
// Using LinkedHashSet to deduplicate Androidacy entry. // Using LinkedHashSet to deduplicate Androidacy entry.
RepoData[] repoDatas = new LinkedHashSet<>( RepoData[] repoDatas = new LinkedHashSet<>(this.repoData.values()).toArray(new RepoData[0]);
this.repoData.values()).toArray(new RepoData[0]);
RepoUpdater[] repoUpdaters = new RepoUpdater[repoDatas.length]; RepoUpdater[] repoUpdaters = new RepoUpdater[repoDatas.length];
int moduleToUpdate = 0; int moduleToUpdate = 0;
for (int i = 0; i < repoDatas.length; i++) { for (int i = 0; i < repoDatas.length; i++) {
if (BuildConfig.DEBUG) Log.d("RepoManager", "Fetching: " + repoDatas[i].getName()); if (BuildConfig.DEBUG) Log.d("RepoManager", "Fetching: " + repoDatas[i].getName());
moduleToUpdate += (repoUpdaters[i] = moduleToUpdate += (repoUpdaters[i] = new RepoUpdater(repoDatas[i])).fetchIndex();
new RepoUpdater(repoDatas[i])).fetchIndex();
updateListener.update(STEP1 / repoDatas.length * (i + 1)); updateListener.update(STEP1 / repoDatas.length * (i + 1));
} }
if (BuildConfig.DEBUG) Log.d("RepoManag3er", "Updating meta-data"); 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++) { for (int i = 0; i < repoUpdaters.length; i++) {
// Check if the repo is enabled // Check if the repo is enabled
if (!repoUpdaters[i].repoData.isEnabled()) { if (!repoUpdaters[i].repoData.isEnabled()) {
if (BuildConfig.DEBUG) Log.d("RepoManager", if (BuildConfig.DEBUG) Log.d("RepoManager", "Skipping disabled repo: " + repoUpdaters[i].repoData.getName());
"Skipping disabled repo: " + repoUpdaters[i].repoData.getName());
continue; continue;
} }
List<RepoModule> repoModules = repoUpdaters[i].toUpdate(); 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()); if (BuildConfig.DEBUG) Log.d("RepoManager", "Registering " + repoData.getName());
for (RepoModule repoModule : repoModules) { for (RepoModule repoModule : repoModules) {
try { try {
if (repoModule.propUrl != null && if (repoModule.propUrl != null && !repoModule.propUrl.isEmpty()) {
!repoModule.propUrl.isEmpty()) { repoData.storeMetadata(repoModule, Http.doHttpGet(repoModule.propUrl, false));
repoData.storeMetadata(repoModule, Files.write(new File(repoData.cacheRoot, repoModule.id + ".prop"), Http.doHttpGet(repoModule.propUrl, false));
Http.doHttpGet(repoModule.propUrl, false));
Files.write(new File(repoData.cacheRoot, repoModule.id + ".prop"),
Http.doHttpGet(repoModule.propUrl, false));
} }
if (repoData.tryLoadMetadata(repoModule) && (allowLowQualityModules || if (repoData.tryLoadMetadata(repoModule) && (allowLowQualityModules || !PropUtils.isLowQualityModule(repoModule.moduleInfo))) {
!PropUtils.isLowQualityModule(repoModule.moduleInfo))) {
// Note: registeredRepoModule may not be null if registered by multiple repos // Note: registeredRepoModule may not be null if registered by multiple repos
RepoModule registeredRepoModule = this.modules.get(repoModule.id); RepoModule registeredRepoModule = this.modules.get(repoModule.id);
if (registeredRepoModule == null) { if (registeredRepoModule == null) {
this.modules.put(repoModule.id, repoModule); this.modules.put(repoModule.id, repoModule);
} else if (repoModule.moduleInfo.versionCode > } else if (AndroidacyRepoData.getInstance().isEnabled() && registeredRepoModule.repoData == this.androidacyRepoData) {
registeredRepoModule.moduleInfo.versionCode) { // 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); this.modules.put(repoModule.id, repoModule);
} }
} }
@ -280,8 +281,11 @@ public final class RepoManager extends SyncManager {
RepoModule registeredRepoModule = this.modules.get(repoModule.id); RepoModule registeredRepoModule = this.modules.get(repoModule.id);
if (registeredRepoModule == null) { if (registeredRepoModule == null) {
this.modules.put(repoModule.id, repoModule); this.modules.put(repoModule.id, repoModule);
} else if (repoModule.moduleInfo.versionCode > } else if (AndroidacyRepoData.getInstance().isEnabled() && registeredRepoModule.repoData == this.androidacyRepoData) {
registeredRepoModule.moduleInfo.versionCode) { // 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); this.modules.put(repoModule.id, repoModule);
} }
} }
@ -293,14 +297,12 @@ public final class RepoManager extends SyncManager {
// Attempt to contact connectivitycheck.gstatic.com/generate_204 // Attempt to contact connectivitycheck.gstatic.com/generate_204
// If we can't, we don't have internet connection // If we can't, we don't have internet connection
try { try {
HttpURLConnection urlConnection = (HttpURLConnection) new URL( HttpURLConnection urlConnection = (HttpURLConnection) new URL("https://connectivitycheck.gstatic.com/generate_204").openConnection();
"https://connectivitycheck.gstatic.com/generate_204").openConnection();
urlConnection.setInstanceFollowRedirects(false); urlConnection.setInstanceFollowRedirects(false);
urlConnection.setReadTimeout(1000); urlConnection.setReadTimeout(1000);
urlConnection.setUseCaches(false); urlConnection.setUseCaches(false);
urlConnection.getInputStream().close(); urlConnection.getInputStream().close();
if (urlConnection.getResponseCode() == 204 && if (urlConnection.getResponseCode() == 204 && urlConnection.getContentLength() == 0) {
urlConnection.getContentLength() == 0) {
this.hasInternet = true; this.hasInternet = true;
} }
} catch (IOException e) { } catch (IOException e) {
@ -310,12 +312,10 @@ public final class RepoManager extends SyncManager {
for (int i = 0; i < repoDatas.length; i++) { for (int i = 0; i < repoDatas.length; i++) {
// If repo is not enabled, skip // If repo is not enabled, skip
if (!repoDatas[i].isEnabled()) { if (!repoDatas[i].isEnabled()) {
if (BuildConfig.DEBUG) Log.d("RepoManager", if (BuildConfig.DEBUG) Log.d("RepoManager", "Skipping " + repoDatas[i].getName() + " because it's disabled");
"Skipping " + repoDatas[i].getName() + " because it's disabled");
continue; continue;
} }
if (BuildConfig.DEBUG) Log.d("RepoManager", if (BuildConfig.DEBUG) Log.d("RepoManager", "Finishing: " + repoUpdaters[i].repoData.getName());
"Finishing: " + repoUpdaters[i].repoData.getName());
this.repoLastSuccess = repoUpdaters[i].finish(); this.repoLastSuccess = repoUpdaters[i].finish();
if (!this.repoLastSuccess) { if (!this.repoLastSuccess) {
Log.e(TAG, "Failed to update " + repoUpdaters[i].repoData.getName()); 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(); Activity context = MainApplication.getINSTANCE().getLastCompatActivity();
new Handler(Looper.getMainLooper()).post(() -> { new Handler(Looper.getMainLooper()).post(() -> {
if (context != null) { if (context != null) {
Snackbar.make(context.findViewById(android.R.id.content), // 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
context.getString(R.string.repo_update_failed_extended, MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(context);
repoUpdaters[finalI].repoData.getName()), builder.setTitle(R.string.repo_update_failed);
Snackbar.LENGTH_LONG).show(); 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(); this.repoLastErrorName = repoUpdaters[i].repoData.getName();

@ -711,111 +711,68 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity {
return false; return false;
}); });
} }
String[] originalApiKeyRef = new String[]{MainApplication.getINSTANCE().getSharedPreferences("androidacy", 0).getString("pref_androidacy_api_token", "")}; // get if androidacy repo is enabled
// Get the dummy pref_androidacy_repo_api_token EditTextPreference boolean androidacyRepoEnabledPref = MainApplication.getSharedPreferences().getBoolean("pref_androidacy_repo_enabled", false);
EditTextPreference prefAndroidacyRepoApiKey = Objects.requireNonNull(findPreference("pref_androidacy_api_token")); if (androidacyRepoEnabledPref) {
prefAndroidacyRepoApiKey.setTitle(R.string.api_key); String[] originalApiKeyRef = new String[]{MainApplication.getINSTANCE().getSharedPreferences("androidacy", 0).getString("pref_androidacy_api_token", "")};
prefAndroidacyRepoApiKey.setSummary(R.string.api_key_summary); // Get the dummy pref_androidacy_repo_api_token preference with id pref_androidacy_repo_api_token
prefAndroidacyRepoApiKey.setDialogTitle(R.string.api_key); // we have to use the id because the key is different
prefAndroidacyRepoApiKey.setDefaultValue(originalApiKeyRef[0]); EditTextPreference prefAndroidacyRepoApiKey = Objects.requireNonNull(findPreference("pref_androidacy_repo_api_token"));
// Set the value to the current value prefAndroidacyRepoApiKey.setTitle(R.string.api_key);
prefAndroidacyRepoApiKey.setText(originalApiKeyRef[0]); prefAndroidacyRepoApiKey.setSummary(R.string.api_key_summary);
prefAndroidacyRepoApiKey.setOnBindEditTextListener(editText -> { prefAndroidacyRepoApiKey.setDialogTitle(R.string.api_key);
editText.setSingleLine(); prefAndroidacyRepoApiKey.setDefaultValue(originalApiKeyRef[0]);
// Make the single line wrap // Set the value to the current value
editText.setHorizontallyScrolling(false); prefAndroidacyRepoApiKey.setText(originalApiKeyRef[0]);
// Set the height to the maximum required to fit the text prefAndroidacyRepoApiKey.setVisible(true);
editText.setMaxLines(Integer.MAX_VALUE); prefAndroidacyRepoApiKey.setOnBindEditTextListener(editText -> {
// Make ok button say "Save" editText.setSingleLine();
editText.setImeOptions(EditorInfo.IME_ACTION_DONE); // Make the single line wrap
}); editText.setHorizontallyScrolling(false);
prefAndroidacyRepoApiKey.setPositiveButtonText(R.string.save_api_key); // Set the height to the maximum required to fit the text
prefAndroidacyRepoApiKey.setOnPreferenceChangeListener((preference, newValue) -> { editText.setMaxLines(Integer.MAX_VALUE);
// Make sure originalApiKeyRef is not null // Make ok button say "Save"
if (originalApiKeyRef[0].equals(newValue)) editText.setImeOptions(EditorInfo.IME_ACTION_DONE);
return true; });
// get original api key prefAndroidacyRepoApiKey.setPositiveButtonText(R.string.save_api_key);
String apiKey = String.valueOf(newValue); prefAndroidacyRepoApiKey.setOnPreferenceChangeListener((preference, newValue) -> {
// Show snack bar with indeterminate progress // Make sure originalApiKeyRef is not null
Snackbar.make(requireView(), R.string.checking_api_key, Snackbar.LENGTH_INDEFINITE).setAction(R.string.cancel, v -> { if (originalApiKeyRef[0].equals(newValue))
// Restore the original api key return true;
prefAndroidacyRepoApiKey.setText(originalApiKeyRef[0]); // get original api key
}).show(); String apiKey = String.valueOf(newValue);
// Check the API key on a background thread // Show snack bar with indeterminate progress
new Thread(() -> { Snackbar.make(requireView(), R.string.checking_api_key, Snackbar.LENGTH_INDEFINITE).setAction(R.string.cancel, v -> {
// If key is empty, just remove it and change the text of the snack bar // Restore the original api key
if (apiKey.isEmpty()) { prefAndroidacyRepoApiKey.setText(originalApiKeyRef[0]);
MainApplication.getINSTANCE().getSharedPreferences("androidacy", 0).edit().remove("pref_androidacy_api_token").apply(); }).show();
new Handler(Looper.getMainLooper()).post(() -> { // Check the API key on a background thread
Snackbar.make(requireView(), R.string.api_key_removed, Snackbar.LENGTH_SHORT).show(); new Thread(() -> {
// Show dialog to restart app with ok button // If key is empty, just remove it and change the text of the snack bar
new MaterialAlertDialogBuilder(this.requireContext()).setTitle(R.string.restart).setCancelable(false).setMessage(R.string.api_key_restart).setNeutralButton(android.R.string.ok, (dialog, which) -> { if (apiKey.isEmpty()) {
// User clicked OK button MainApplication.getINSTANCE().getSharedPreferences("androidacy", 0).edit().remove("pref_androidacy_api_token").apply();
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(() -> { new Handler(Looper.getMainLooper()).post(() -> {
Snackbar.make(requireView(), R.string.api_key_invalid, Snackbar.LENGTH_SHORT).show(); Snackbar.make(requireView(), R.string.api_key_removed, Snackbar.LENGTH_SHORT).show();
// Save the original key // Show dialog to restart app with ok button
MainApplication.getINSTANCE().getSharedPreferences("androidacy", 0).edit().putString("pref_androidacy_api_token", originalApiKeyRef[0]).apply(); new MaterialAlertDialogBuilder(this.requireContext()).setTitle(R.string.restart).setCancelable(false).setMessage(R.string.api_key_restart).setNeutralButton(android.R.string.ok, (dialog, which) -> {
// Re-show the dialog with an error // User clicked OK button
prefAndroidacyRepoApiKey.performClick(); Intent mStartActivity = new Intent(requireContext(), MainActivity.class);
// Show error mStartActivity.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
prefAndroidacyRepoApiKey.setDialogMessage(getString(R.string.api_key_invalid)); 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 { } else {
// If the key is the same as the original, just show a snack bar // If key < 64 chars, it's not valid
if (apiKey.equals(originalApiKeyRef[0])) { if (apiKey.length() < 64) {
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(() -> { new Handler(Looper.getMainLooper()).post(() -> {
Snackbar.make(requireView(), R.string.api_key_invalid, Snackbar.LENGTH_SHORT).show(); Snackbar.make(requireView(), R.string.api_key_invalid, Snackbar.LENGTH_SHORT).show();
// Save the original key // Save the original key
@ -825,14 +782,61 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity {
// Show error // Show error
prefAndroidacyRepoApiKey.setDialogMessage(getString(R.string.api_key_invalid)); 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();
}).start(); return true;
return true; });
}); }
// make sure the preference is visible if repo is enabled
prefAndroidacyRepoApiKey.setVisible(RepoManager.getINSTANCE().getAndroidacyRepoData().isEnabled());
} }
@SuppressLint("RestrictedApi") @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_theme_title">Choose theme</string>
<string name="setup_language_button">Choose language</string> <string name="setup_language_button">Choose language</string>
<string name="title_activity_setup">Setup Wizard</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> </resources>

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

Loading…
Cancel
Save