Fix UI mismatching actual configuration due to multiprocess

This commit is contained in:
Marvin W 2020-09-02 21:41:15 +02:00
parent 3ef330ad7b
commit d641ca7e7e
No known key found for this signature in database
GPG Key ID: 072E9235DB996F2A
23 changed files with 608 additions and 259 deletions

View File

@ -193,6 +193,8 @@
</intent-filter>
</service>
<receiver android:name="org.microg.gms.checkin.ServiceInfoReceiver" />
<receiver android:name="org.microg.gms.checkin.TriggerReceiver">
<intent-filter>
<action android:name="android.server.checkin.CHECKIN" />
@ -238,7 +240,7 @@
</intent-filter>
</receiver>
<receiver android:name="org.microg.gms.gcm.StatusInfoProvider" />
<receiver android:name="org.microg.gms.gcm.ServiceInfoReceiver" />
<receiver android:name="org.microg.gms.gcm.TriggerReceiver">
<intent-filter>
@ -627,6 +629,8 @@
</intent-filter>
</service>
<receiver android:name="org.microg.gms.snet.ServiceInfoReceiver" />
<service android:name="org.microg.gms.snet.SafetyNetClientService">
<intent-filter>
<action android:name="com.google.android.gms.safetynet.service.START" />

View File

@ -9,6 +9,11 @@ import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.util.Log;
import org.microg.gms.common.PackageUtils;
import java.io.File;
public class CheckinPrefs implements SharedPreferences.OnSharedPreferenceChangeListener {
public static final String PREF_ENABLE_CHECKIN = "checkin_enable_service";
@ -16,6 +21,9 @@ public class CheckinPrefs implements SharedPreferences.OnSharedPreferenceChangeL
public static CheckinPrefs get(Context context) {
if (INSTANCE == null) {
if (!context.getPackageName().equals(PackageUtils.getProcessName())) {
Log.w("Preferences", CheckinPrefs.class.getName() + " initialized outside main process", new RuntimeException());
}
if (context == null) return new CheckinPrefs(null);
INSTANCE = new CheckinPrefs(context.getApplicationContext());
}
@ -23,18 +31,30 @@ public class CheckinPrefs implements SharedPreferences.OnSharedPreferenceChangeL
}
private SharedPreferences preferences;
private SharedPreferences systemDefaultPreferences;
private boolean checkinEnabled = false;
private CheckinPrefs(Context context) {
if (context != null) {
preferences = PreferenceManager.getDefaultSharedPreferences(context);
preferences.registerOnSharedPreferenceChangeListener(this);
try {
systemDefaultPreferences = (SharedPreferences) Context.class.getDeclaredMethod("getSharedPreferences", File.class, int.class).invoke(context, new File("/system/etc/microg.xml"), Context.MODE_PRIVATE);
} catch (Exception ignored) {
}
update();
}
}
private boolean getSettingsBoolean(String key, boolean def) {
if (systemDefaultPreferences != null) {
def = systemDefaultPreferences.getBoolean(key, def);
}
return preferences.getBoolean(key, def);
}
private void update() {
checkinEnabled = preferences.getBoolean(PREF_ENABLE_CHECKIN, false);
checkinEnabled = getSettingsBoolean(PREF_ENABLE_CHECKIN, false);
}
@Override

View File

@ -55,6 +55,6 @@ public class LastCheckinInfo {
.putLong(PREF_SECURITY_TOKEN, securityToken)
.putString(PREF_VERSION_INFO, versionInfo)
.putString(PREF_DEVICE_DATA_VERSION_INFO, deviceDataVersionInfo)
.apply();
.commit();
}
}

View File

@ -21,6 +21,7 @@ import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.os.Build;
import android.text.TextUtils;
import java.util.ArrayList;
@ -65,6 +66,9 @@ public class GcmDatabase extends SQLiteOpenHelper {
public GcmDatabase(Context context) {
super(context, DB_NAME, null, DB_VERSION);
this.context = context;
if (Build.VERSION.SDK_INT >= 16) {
this.setWriteAheadLoggingEnabled(true);
}
}
public static class App {

View File

@ -24,6 +24,9 @@ import android.net.NetworkInfo;
import android.preference.PreferenceManager;
import android.util.Log;
import org.microg.gms.common.PackageUtils;
import java.io.File;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@ -50,6 +53,9 @@ public class GcmPrefs implements SharedPreferences.OnSharedPreferenceChangeListe
public static GcmPrefs get(Context context) {
if (INSTANCE == null) {
if (!context.getPackageName().equals(PackageUtils.getProcessName())) {
Log.w("Preferences", GcmPrefs.class.getName() + " initialized outside main process", new RuntimeException());
}
if (context == null) return new GcmPrefs(null);
INSTANCE = new GcmPrefs(context.getApplicationContext());
}
@ -70,30 +76,43 @@ public class GcmPrefs implements SharedPreferences.OnSharedPreferenceChangeListe
private int learntMobile = 300000;
private int learntOther = 300000;
private SharedPreferences defaultPreferences;
private SharedPreferences preferences;
private SharedPreferences systemDefaultPreferences;
private GcmPrefs(Context context) {
if (context != null) {
defaultPreferences = PreferenceManager.getDefaultSharedPreferences(context);
defaultPreferences.registerOnSharedPreferenceChangeListener(this);
preferences = PreferenceManager.getDefaultSharedPreferences(context);
preferences.registerOnSharedPreferenceChangeListener(this);
try {
systemDefaultPreferences = (SharedPreferences) Context.class.getDeclaredMethod("getSharedPreferences", File.class, int.class).invoke(context, new File("/system/etc/microg.xml"), Context.MODE_PRIVATE);
} catch (Exception ignored) {
}
update();
}
}
private boolean getSettingsBoolean(String key, boolean def) {
if (systemDefaultPreferences != null) {
def = systemDefaultPreferences.getBoolean(key, def);
}
return preferences.getBoolean(key, def);
}
public void update() {
gcmLogEnabled = defaultPreferences.getBoolean(PREF_FULL_LOG, true);
lastPersistedId = defaultPreferences.getString(PREF_LAST_PERSISTENT_ID, "");
confirmNewApps = defaultPreferences.getBoolean(PREF_CONFIRM_NEW_APPS, false);
gcmEnabled = defaultPreferences.getBoolean(PREF_ENABLE_GCM, false);
gcmEnabled = getSettingsBoolean(PREF_ENABLE_GCM, false);
gcmLogEnabled = getSettingsBoolean(PREF_FULL_LOG, true);
confirmNewApps = getSettingsBoolean(PREF_CONFIRM_NEW_APPS, false);
networkMobile = Integer.parseInt(defaultPreferences.getString(PREF_NETWORK_MOBILE, "0"));
networkWifi = Integer.parseInt(defaultPreferences.getString(PREF_NETWORK_WIFI, "0"));
networkRoaming = Integer.parseInt(defaultPreferences.getString(PREF_NETWORK_ROAMING, "0"));
networkOther = Integer.parseInt(defaultPreferences.getString(PREF_NETWORK_OTHER, "0"));
lastPersistedId = preferences.getString(PREF_LAST_PERSISTENT_ID, "");
learntMobile = defaultPreferences.getInt(PREF_LEARNT_MOBILE, 300000);
learntWifi = defaultPreferences.getInt(PREF_LEARNT_WIFI, 300000);
learntOther = defaultPreferences.getInt(PREF_LEARNT_OTHER, 300000);
networkMobile = Integer.parseInt(preferences.getString(PREF_NETWORK_MOBILE, "0"));
networkWifi = Integer.parseInt(preferences.getString(PREF_NETWORK_WIFI, "0"));
networkRoaming = Integer.parseInt(preferences.getString(PREF_NETWORK_ROAMING, "0"));
networkOther = Integer.parseInt(preferences.getString(PREF_NETWORK_OTHER, "0"));
learntMobile = preferences.getInt(PREF_LEARNT_MOBILE, 300000);
learntWifi = preferences.getInt(PREF_LEARNT_WIFI, 300000);
learntOther = preferences.getInt(PREF_LEARNT_OTHER, 300000);
}
public String getNetworkPrefForInfo(NetworkInfo info) {
@ -182,7 +201,7 @@ public class GcmPrefs implements SharedPreferences.OnSharedPreferenceChangeListe
learntMobile = Math.max(MIN_INTERVAL, Math.min(learntMobile, MAX_INTERVAL));
learntWifi = Math.max(MIN_INTERVAL, Math.min(learntWifi, MAX_INTERVAL));
learntOther = Math.max(MIN_INTERVAL, Math.min(learntOther, MAX_INTERVAL));
defaultPreferences.edit().putInt(PREF_LEARNT_MOBILE, learntMobile).putInt(PREF_LEARNT_WIFI, learntWifi).putInt(PREF_LEARNT_OTHER, learntOther).apply();
preferences.edit().putInt(PREF_LEARNT_MOBILE, learntMobile).putInt(PREF_LEARNT_WIFI, learntWifi).putInt(PREF_LEARNT_OTHER, learntOther).apply();
}
@Override
@ -223,11 +242,11 @@ public class GcmPrefs implements SharedPreferences.OnSharedPreferenceChangeListe
public void extendLastPersistedId(String id) {
if (!lastPersistedId.isEmpty()) lastPersistedId += "|";
lastPersistedId += id;
defaultPreferences.edit().putString(PREF_LAST_PERSISTENT_ID, lastPersistedId).apply();
preferences.edit().putString(PREF_LAST_PERSISTENT_ID, lastPersistedId).apply();
}
public void clearLastPersistedId() {
lastPersistedId = "";
defaultPreferences.edit().putString(PREF_LAST_PERSISTENT_ID, lastPersistedId).apply();
preferences.edit().putString(PREF_LAST_PERSISTENT_ID, lastPersistedId).apply();
}
}

View File

@ -176,8 +176,8 @@ public class McsService extends Service implements Handler.Callback {
}
}
private static void logd(String msg) {
if (GcmPrefs.get(null).isGcmLogEnabled()) Log.d(TAG, msg);
private static void logd(Context context, String msg) {
if (context == null || GcmPrefs.get(context).isGcmLogEnabled()) Log.d(TAG, msg);
}
@Override
@ -237,7 +237,7 @@ public class McsService extends Service implements Handler.Callback {
public synchronized static boolean isConnected() {
if (inputStream == null || !inputStream.isAlive() || outputStream == null || !outputStream.isAlive()) {
logd("Connection is not enabled or dead.");
logd(null, "Connection is not enabled or dead.");
return false;
}
// consider connection to be dead if we did not receive an ack within twice the heartbeat interval
@ -245,7 +245,7 @@ public class McsService extends Service implements Handler.Callback {
if (heartbeatMs < 0) {
closeAll();
} else if (SystemClock.elapsedRealtime() - lastHeartbeatAckElapsedRealtime > 2 * heartbeatMs) {
logd("No heartbeat for " + (SystemClock.elapsedRealtime() - lastHeartbeatAckElapsedRealtime) / 1000 + " seconds, connection assumed to be dead after " + 2 * heartbeatMs / 1000 + " seconds");
logd(null, "No heartbeat for " + (SystemClock.elapsedRealtime() - lastHeartbeatAckElapsedRealtime) / 1000 + " seconds, connection assumed to be dead after " + 2 * heartbeatMs / 1000 + " seconds");
GcmPrefs.get(null).learnTimeout(activeNetworkPref);
return false;
}
@ -259,7 +259,7 @@ public class McsService extends Service implements Handler.Callback {
public static void scheduleReconnect(Context context) {
AlarmManager alarmManager = (AlarmManager) context.getSystemService(ALARM_SERVICE);
long delay = getCurrentDelay();
logd("Scheduling reconnect in " + delay / 1000 + " seconds...");
logd(context, "Scheduling reconnect in " + delay / 1000 + " seconds...");
PendingIntent pi = PendingIntent.getBroadcast(context, 1, new Intent(ACTION_RECONNECT, null, context, TriggerReceiver.class), 0);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
alarmManager.setExactAndAllowWhileIdle(ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + delay, pi);
@ -275,7 +275,7 @@ public class McsService extends Service implements Handler.Callback {
if (heartbeatMs < 0) {
closeAll();
}
logd("Scheduling heartbeat in " + heartbeatMs / 1000 + " seconds...");
logd(context, "Scheduling heartbeat in " + heartbeatMs / 1000 + " seconds...");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
// This is supposed to work even when running in idle and without battery optimization disabled
alarmManager.setExactAndAllowWhileIdle(ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + heartbeatMs, heartbeatIntent);
@ -449,11 +449,11 @@ public class McsService extends Service implements Handler.Callback {
return;
}
logd("Starting MCS connection...");
logd(this, "Starting MCS connection...");
Socket socket = new Socket(SERVICE_HOST, SERVICE_PORT);
logd("Connected to " + SERVICE_HOST + ":" + SERVICE_PORT);
logd(this, "Connected to " + SERVICE_HOST + ":" + SERVICE_PORT);
sslSocket = SSLContext.getDefault().getSocketFactory().createSocket(socket, SERVICE_HOST, SERVICE_PORT, true);
logd("Activated SSL with " + SERVICE_HOST + ":" + SERVICE_PORT);
logd(this, "Activated SSL with " + SERVICE_HOST + ":" + SERVICE_PORT);
inputStream = new McsInputStream(sslSocket.getInputStream(), rootHandler);
outputStream = new McsOutputStream(sslSocket.getOutputStream(), rootHandler);
inputStream.start();
@ -476,7 +476,7 @@ public class McsService extends Service implements Handler.Callback {
private void handleLoginResponse(LoginResponse loginResponse) {
if (loginResponse.error == null) {
GcmPrefs.get(this).clearLastPersistedId();
logd("Logged in");
logd(this, "Logged in");
wakeLock.release();
} else {
throw new RuntimeException("Could not login: " + loginResponse.error);
@ -561,13 +561,13 @@ public class McsService extends Service implements Handler.Callback {
if (receiverPermission == null) {
// Without receiver permission, we only restrict by package name
logd("Deliver message to all receivers in package " + packageName);
logd(this, "Deliver message to all receivers in package " + packageName);
intent.setPackage(packageName);
sendOrderedBroadcast(intent, null);
} else {
List<ResolveInfo> infos = getPackageManager().queryBroadcastReceivers(intent, PackageManager.GET_RESOLVED_FILTER);
if (infos == null || infos.isEmpty()) {
logd("No target for message, wut?");
logd(this, "No target for message, wut?");
} else {
for (ResolveInfo resolveInfo : infos) {
Intent targetIntent = new Intent(intent);
@ -578,7 +578,7 @@ public class McsService extends Service implements Handler.Callback {
try {
if (getUserIdMethod != null && addPowerSaveTempWhitelistAppMethod != null && deviceIdleController != null) {
int userId = (int) getUserIdMethod.invoke(null, getPackageManager().getApplicationInfo(packageName, 0).uid);
logd("Adding app " + packageName + " for userId " + userId + " to the temp whitelist");
logd(this, "Adding app " + packageName + " for userId " + userId + " to the temp whitelist");
addPowerSaveTempWhitelistAppMethod.invoke(deviceIdleController, packageName, 10000, userId, "GCM Push");
}
} catch (Exception e) {
@ -586,11 +586,11 @@ public class McsService extends Service implements Handler.Callback {
}
}
// We don't need receiver permission for our own package
logd("Deliver message to own receiver " + resolveInfo);
logd(this, "Deliver message to own receiver " + resolveInfo);
sendOrderedBroadcast(targetIntent, null);
} else if (resolveInfo.filter.hasCategory(packageName)) {
// Permission required
logd("Deliver message to third-party receiver (with permission check)" + resolveInfo);
logd(this, "Deliver message to third-party receiver (with permission check)" + resolveInfo);
sendOrderedBroadcast(targetIntent, receiverPermission);
}
}
@ -640,21 +640,21 @@ public class McsService extends Service implements Handler.Callback {
return true;
case MSG_INPUT_ERROR:
case MSG_OUTPUT_ERROR:
logd("I/O error: " + msg.obj);
logd(this, "I/O error: " + msg.obj);
rootHandler.sendMessage(rootHandler.obtainMessage(MSG_TEARDOWN, msg.obj));
return true;
case MSG_TEARDOWN:
logd("Teardown initiated, reason: " + msg.obj);
logd(this, "Teardown initiated, reason: " + msg.obj);
handleTeardown(msg);
return true;
case MSG_CONNECT:
logd("Connect initiated, reason: " + msg.obj);
logd(this, "Connect initiated, reason: " + msg.obj);
if (!isConnected()) {
connect();
}
return true;
case MSG_HEARTBEAT:
logd("Heartbeat initiated, reason: " + msg.obj);
logd(this, "Heartbeat initiated, reason: " + msg.obj);
if (isConnected()) {
HeartbeatPing.Builder ping = new HeartbeatPing.Builder();
if (inputStream.newStreamIdAvailable()) {
@ -663,12 +663,12 @@ public class McsService extends Service implements Handler.Callback {
send(MCS_HEARTBEAT_PING_TAG, ping.build());
scheduleHeartbeat(this);
} else {
logd("Ignoring heartbeat, not connected!");
logd(this, "Ignoring heartbeat, not connected!");
scheduleReconnect(this);
}
return true;
case MSG_ACK:
logd("Ack initiated, reason: " + msg.obj);
logd(this, "Ack initiated, reason: " + msg.obj);
if (isConnected()) {
IqStanza.Builder iq = new IqStanza.Builder()
.type(IqStanza.IqType.SET)
@ -680,11 +680,11 @@ public class McsService extends Service implements Handler.Callback {
}
send(MCS_IQ_STANZA_TAG, iq.build());
} else {
logd("Ignoring ack, not connected!");
logd(this, "Ignoring ack, not connected!");
}
return true;
case MSG_OUTPUT_READY:
logd("Sending login request...");
logd(this, "Sending login request...");
send(MCS_LOGIN_REQUEST_TAG, buildLoginRequest());
return true;
case MSG_OUTPUT_DONE:

View File

@ -45,7 +45,7 @@ public class TriggerReceiver extends WakefulBroadcastReceiver {
public synchronized static void register(Context context) {
if (SDK_INT >= N && !registered) {
IntentFilter intentFilter = new IntentFilter("android.net.conn.CONNECTIVITY_CHANGE");
context.registerReceiver(new TriggerReceiver(), intentFilter);
context.getApplicationContext().registerReceiver(new TriggerReceiver(), intentFilter);
registered = true;
}
}

View File

@ -19,6 +19,11 @@ package org.microg.gms.snet;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.util.Log;
import org.microg.gms.common.PackageUtils;
import java.io.File;
public class SafetyNetPrefs implements SharedPreferences.OnSharedPreferenceChangeListener {
private static final String OFFICIAL_ATTEST_BASE_URL = "https://www.googleapis.com/androidcheck/v1/attestations/attest";
@ -33,6 +38,9 @@ public class SafetyNetPrefs implements SharedPreferences.OnSharedPreferenceChang
public static SafetyNetPrefs get(Context context) {
if (INSTANCE == null) {
if (!context.getPackageName().equals(PackageUtils.getProcessName())) {
Log.w("Preferences", SafetyNetPrefs.class.getName() + " initialized outside main process", new RuntimeException());
}
if (context == null) return new SafetyNetPrefs(null);
INSTANCE = new SafetyNetPrefs(context.getApplicationContext());
}
@ -45,22 +53,41 @@ public class SafetyNetPrefs implements SharedPreferences.OnSharedPreferenceChang
private boolean thirdParty;
private String customUrl;
private SharedPreferences defaultPreferences;
private SharedPreferences preferences;
private SharedPreferences systemDefaultPreferences;
private SafetyNetPrefs(Context context) {
if (context != null) {
defaultPreferences = PreferenceManager.getDefaultSharedPreferences(context);
defaultPreferences.registerOnSharedPreferenceChangeListener(this);
preferences = PreferenceManager.getDefaultSharedPreferences(context);
preferences.registerOnSharedPreferenceChangeListener(this);
try {
systemDefaultPreferences = (SharedPreferences) Context.class.getDeclaredMethod("getSharedPreferences", File.class, int.class).invoke(context, new File("/system/etc/microg.xml"), Context.MODE_PRIVATE);
} catch (Exception ignored) {
}
update();
}
}
private boolean getSettingsBoolean(String key, boolean def) {
if (systemDefaultPreferences != null) {
def = systemDefaultPreferences.getBoolean(key, def);
}
return preferences.getBoolean(key, def);
}
private String getSettingsString(String key, String def) {
if (systemDefaultPreferences != null) {
def = systemDefaultPreferences.getString(key, def);
}
return preferences.getString(key, def);
}
public void update() {
disabled = defaultPreferences.getBoolean(PREF_SNET_DISABLED, true);
official = defaultPreferences.getBoolean(PREF_SNET_OFFICIAL, false);
selfSigned = defaultPreferences.getBoolean(PREF_SNET_SELF_SIGNED, false);
thirdParty = defaultPreferences.getBoolean(PREF_SNET_THIRD_PARTY, false);
customUrl = defaultPreferences.getString(PREF_SNET_CUSTOM_URL, null);
disabled = getSettingsBoolean(PREF_SNET_DISABLED, true);
official = getSettingsBoolean(PREF_SNET_OFFICIAL, false);
selfSigned = getSettingsBoolean(PREF_SNET_SELF_SIGNED, false);
thirdParty = getSettingsBoolean(PREF_SNET_THIRD_PARTY, false);
customUrl = getSettingsString(PREF_SNET_CUSTOM_URL, null);
}
@Override
@ -73,11 +100,13 @@ public class SafetyNetPrefs implements SharedPreferences.OnSharedPreferenceChang
}
public void setEnabled(boolean enabled) {
defaultPreferences.edit().putBoolean(PREF_SNET_DISABLED, !enabled).apply();
SharedPreferences.Editor edit = preferences.edit();
edit.putBoolean(PREF_SNET_DISABLED, !enabled);
if (enabled && !isEnabled()) {
official = true;
defaultPreferences.edit().putBoolean(PREF_SNET_OFFICIAL, true).apply();
edit.putBoolean(PREF_SNET_OFFICIAL, true);
}
edit.commit();
}
public boolean isSelfSigned() {

View File

@ -1,115 +0,0 @@
package org.microg.gms.ui;
import android.os.Build;
import android.os.Bundle;
import androidx.annotation.Nullable;
import androidx.navigation.NavOptions;
import androidx.navigation.fragment.NavHostFragment;
import com.google.android.gms.R;
import org.microg.gms.checkin.CheckinPrefs;
import org.microg.gms.gcm.GcmDatabase;
import org.microg.gms.gcm.GcmPrefs;
import org.microg.gms.nearby.exposurenotification.ExposurePreferences;
import org.microg.gms.snet.SafetyNetPrefs;
import org.microg.tools.ui.ResourceSettingsFragment;
public class SettingsFragment extends ResourceSettingsFragment {
public static final String PREF_ABOUT = "pref_about";
public static final String PREF_GCM = "pref_gcm";
public static final String PREF_SNET = "pref_snet";
public static final String PREF_UNIFIEDNLP = "pref_unifiednlp";
public static final String PREF_CHECKIN = "pref_checkin";
public static final String PREF_EXPOSURE = "pref_exposure";
public SettingsFragment() {
preferencesResource = R.xml.preferences_start;
}
@Override
public void onCreatePreferences(@Nullable Bundle savedInstanceState, String rootKey) {
super.onCreatePreferences(savedInstanceState, rootKey);
updateDetails();
}
@Override
public void onResume() {
super.onResume();
updateDetails();
}
private void updateDetails() {
findPreference(PREF_ABOUT).setSummary(getString(R.string.about_version_str, AboutFragment.getSelfVersion(getContext())));
findPreference(PREF_ABOUT).setOnPreferenceClickListener(preference -> {
UtilsKt.navigate(NavHostFragment.findNavController(SettingsFragment.this), getContext(), R.id.openAbout, null);
return true;
});
if (GcmPrefs.get(getContext()).isEnabled()) {
GcmDatabase database = new GcmDatabase(getContext());
int regCount = database.getRegistrationList().size();
database.close();
findPreference(PREF_GCM).setSummary(getString(R.string.service_status_enabled_short) + " - " + getResources().getQuantityString(R.plurals.gcm_registered_apps_counter, regCount, regCount));
} else {
findPreference(PREF_GCM).setSummary(R.string.service_status_disabled_short);
}
findPreference(PREF_GCM).setOnPreferenceClickListener(preference -> {
UtilsKt.navigate(NavHostFragment.findNavController(SettingsFragment.this), getContext(), R.id.openGcmSettings, null);
return true;
});
if (SafetyNetPrefs.get(getContext()).isEnabled()) {
String snet_info = "";
if (SafetyNetPrefs.get(getContext()).isOfficial()) {
snet_info = getString(R.string.pref_snet_status_official_info);
} else if (SafetyNetPrefs.get(getContext()).isSelfSigned()) {
snet_info = getString(R.string.pref_snet_status_self_signed_info);
} else if (SafetyNetPrefs.get(getContext()).isThirdParty()) {
snet_info = getString(R.string.pref_snet_status_third_party_info);
}
findPreference(PREF_SNET).setSummary(getString(R.string.service_status_enabled_short));
} else {
findPreference(PREF_SNET).setSummary(R.string.service_status_disabled_short);
}
findPreference(PREF_SNET).setOnPreferenceClickListener(preference -> {
UtilsKt.navigate(NavHostFragment.findNavController(SettingsFragment.this), getContext(), R.id.openSafetyNetSettings, null);
return true;
});
// Preferences unifiedNlPrefs = new Preferences(getContext());
// int backendCount = TextUtils.isEmpty(unifiedNlPrefs.getLocationBackends()) ? 0 :
// Preferences.splitBackendString(unifiedNlPrefs.getLocationBackends()).length;
// backendCount += TextUtils.isEmpty(unifiedNlPrefs.getGeocoderBackends()) ? 0 :
// Preferences.splitBackendString(unifiedNlPrefs.getGeocoderBackends()).length;
// findPreference(PREF_UNIFIEDNLP).setSummary(getResources().getQuantityString(R.plurals.pref_unifiednlp_summary, backendCount, backendCount));
findPreference(PREF_UNIFIEDNLP).setOnPreferenceClickListener(preference -> {
UtilsKt.navigate(NavHostFragment.findNavController(SettingsFragment.this), getContext(), R.id.openUnifiedNlpSettings, null);
return true;
});
if (Build.VERSION.SDK_INT >= 21) {
findPreference(PREF_EXPOSURE).setVisible(true);
if (new ExposurePreferences(getContext()).getScannerEnabled()) {
findPreference(PREF_EXPOSURE).setSummary(getString(R.string.service_status_enabled_short));
} else {
findPreference(PREF_EXPOSURE).setSummary(R.string.service_status_disabled_short);
}
findPreference(PREF_EXPOSURE).setOnPreferenceClickListener(preference -> {
UtilsKt.navigate(NavHostFragment.findNavController(SettingsFragment.this), getContext(), R.id.openExposureNotificationSettings, null);
return true;
});
} else {
findPreference(PREF_EXPOSURE).setVisible(false);
}
boolean checkinEnabled = CheckinPrefs.get(getContext()).isEnabled();
findPreference(PREF_CHECKIN).setSummary(checkinEnabled ? R.string.service_status_enabled_short : R.string.service_status_disabled_short);
findPreference(PREF_CHECKIN).setOnPreferenceClickListener(preference -> {
UtilsKt.navigate(NavHostFragment.findNavController(SettingsFragment.this), getContext(), R.id.openCheckinSettings, null);
return true;
});
}
}

View File

@ -0,0 +1,93 @@
/*
* SPDX-FileCopyrightText: 2020, microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package org.microg.gms.checkin
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.util.Log
import java.io.Serializable
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
import kotlin.coroutines.suspendCoroutine
private const val ACTION_SERVICE_INFO_REQUEST = "org.microg.gms.checkin.SERVICE_INFO_REQUEST"
private const val ACTION_UPDATE_CONFIGURATION = "org.microg.gms.checkin.UPDATE_CONFIGURATION"
private const val ACTION_SERVICE_INFO_RESPONSE = "org.microg.gms.checkin.SERVICE_INFO_RESPONSE"
private const val EXTRA_SERVICE_INFO = "org.microg.gms.checkin.SERVICE_INFO"
private const val EXTRA_CONFIGURATION = "org.microg.gms.checkin.CONFIGURATION"
private const val TAG = "GmsCheckinStatusInfo"
data class ServiceInfo(val configuration: ServiceConfiguration, val lastCheckin: Long, val androidId: Long) : Serializable
data class ServiceConfiguration(val enabled: Boolean) : Serializable {
fun saveToPrefs(context: Context) {
CheckinPrefs.setEnabled(context, enabled)
}
}
private fun CheckinPrefs.toConfiguration(): ServiceConfiguration = ServiceConfiguration(isEnabled)
class ServiceInfoReceiver : BroadcastReceiver() {
private fun sendInfoResponse(context: Context) {
context.sendOrderedBroadcast(Intent(ACTION_SERVICE_INFO_RESPONSE).apply {
setPackage(context.packageName)
val checkinInfo = LastCheckinInfo.read(context)
putExtra(EXTRA_SERVICE_INFO, ServiceInfo(CheckinPrefs.get(context).toConfiguration(), checkinInfo.lastCheckin, checkinInfo.androidId))
}, null)
}
override fun onReceive(context: Context, intent: Intent) {
try {
when (intent.action) {
ACTION_UPDATE_CONFIGURATION -> {
(intent.getSerializableExtra(EXTRA_CONFIGURATION) as? ServiceConfiguration)?.saveToPrefs(context)
}
}
sendInfoResponse(context)
} catch (e: Exception) {
Log.w(TAG, e)
}
}
}
private suspend fun sendToServiceInfoReceiver(intent: Intent, context: Context): ServiceInfo = suspendCoroutine {
context.registerReceiver(object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
context.unregisterReceiver(this)
val serviceInfo = try {
intent.getSerializableExtra(EXTRA_SERVICE_INFO) as ServiceInfo
} catch (e: Exception) {
it.resumeWithException(e)
return
}
try {
it.resume(serviceInfo)
} catch (e: Exception) {
Log.w(TAG, e)
}
}
}, IntentFilter(ACTION_SERVICE_INFO_RESPONSE))
try {
context.sendOrderedBroadcast(intent, null)
} catch (e: Exception) {
it.resumeWithException(e)
}
}
suspend fun getCheckinServiceInfo(context: Context): ServiceInfo = sendToServiceInfoReceiver(
Intent(context, ServiceInfoReceiver::class.java).apply {
action = ACTION_SERVICE_INFO_REQUEST
}, context)
suspend fun setCheckinServiceConfiguration(context: Context, configuration: ServiceConfiguration): ServiceInfo = sendToServiceInfoReceiver(
Intent(context, ServiceInfoReceiver::class.java).apply {
action = ACTION_UPDATE_CONFIGURATION
putExtra(EXTRA_CONFIGURATION, configuration)
}, context)

View File

@ -0,0 +1,92 @@
/*
* SPDX-FileCopyrightText: 2020, microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package org.microg.gms.gcm
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.util.Log
import java.io.Serializable
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
import kotlin.coroutines.suspendCoroutine
private const val ACTION_SERVICE_INFO_REQUEST = "org.microg.gms.gcm.SERVICE_INFO_REQUEST"
private const val ACTION_UPDATE_CONFIGURATION = "org.microg.gms.gcm.UPDATE_CONFIGURATION"
private const val ACTION_SERVICE_INFO_RESPONSE = "org.microg.gms.gcm.SERVICE_INFO_RESPONSE"
private const val EXTRA_SERVICE_INFO = "org.microg.gms.gcm.SERVICE_INFO"
private const val EXTRA_CONFIGURATION = "org.microg.gms.gcm.CONFIGURATION"
private const val TAG = "GmsGcmStatusInfo"
data class ServiceInfo(val configuration: ServiceConfiguration, val connected: Boolean, val startTimestamp: Long) : Serializable
// TODO: Intervals
data class ServiceConfiguration(val enabled: Boolean, val confirmNewApps: Boolean) : Serializable {
fun saveToPrefs(context: Context) {
GcmPrefs.setEnabled(context, enabled)
// TODO: confirm new apps
}
}
private fun GcmPrefs.toConfiguration(): ServiceConfiguration = ServiceConfiguration(isEnabled, isConfirmNewApps)
class ServiceInfoReceiver : BroadcastReceiver() {
private fun sendInfoResponse(context: Context) {
context.sendOrderedBroadcast(Intent(ACTION_SERVICE_INFO_RESPONSE).apply {
setPackage(context.packageName)
putExtra(EXTRA_SERVICE_INFO, ServiceInfo(GcmPrefs.get(context).toConfiguration(), McsService.isConnected(), McsService.getStartTimestamp()))
}, null)
}
override fun onReceive(context: Context, intent: Intent) {
try {
when (intent.action) {
ACTION_UPDATE_CONFIGURATION -> {
(intent.getSerializableExtra(EXTRA_CONFIGURATION) as? ServiceConfiguration)?.saveToPrefs(context)
}
}
sendInfoResponse(context)
} catch (e: Exception) {
Log.w(TAG, e)
}
}
}
private suspend fun sendToServiceInfoReceiver(intent: Intent, context: Context): ServiceInfo = suspendCoroutine {
context.registerReceiver(object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
context.unregisterReceiver(this)
val serviceInfo = try {
intent.getSerializableExtra(EXTRA_SERVICE_INFO) as ServiceInfo
} catch (e: Exception) {
it.resumeWithException(e)
return
}
try {
it.resume(serviceInfo)
} catch (e: Exception) {
Log.w(TAG, e)
}
}
}, IntentFilter(ACTION_SERVICE_INFO_RESPONSE))
try {
context.sendOrderedBroadcast(intent, null)
} catch (e: Exception) {
it.resumeWithException(e)
}
}
suspend fun getGcmServiceInfo(context: Context): ServiceInfo = sendToServiceInfoReceiver(
Intent(context, ServiceInfoReceiver::class.java).apply {
action = ACTION_SERVICE_INFO_REQUEST
}, context)
suspend fun setGcmServiceConfiguration(context: Context, configuration: ServiceConfiguration): ServiceInfo = sendToServiceInfoReceiver(
Intent(context, ServiceInfoReceiver::class.java).apply {
action = ACTION_UPDATE_CONFIGURATION
putExtra(EXTRA_CONFIGURATION, configuration)
}, context)

View File

@ -1,53 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020, microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package org.microg.gms.gcm
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.util.Log
import java.io.Serializable
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
private const val ACTION_STATUS_INFO_REQUEST = "org.microg.gms.STATUS_INFO_REQUEST"
private const val ACTION_STATUS_INFO_RESPONSE = "org.microg.gms.STATUS_INFO_RESPONSE"
private const val EXTRA_STATUS_INFO = "org.microg.gms.STATUS_INFO"
private const val TAG = "GmsGcmStatusInfo"
data class StatusInfo(val connected: Boolean, val startTimestamp: Long) : Serializable
class StatusInfoProvider : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
try {
context.sendOrderedBroadcast(Intent(ACTION_STATUS_INFO_RESPONSE).apply {
setPackage(context.packageName)
putExtra(EXTRA_STATUS_INFO, StatusInfo(McsService.isConnected(), McsService.getStartTimestamp()))
}, null)
} catch (e: Exception) {
Log.w(TAG, e)
}
}
}
suspend fun getStatusInfo(context: Context): StatusInfo? = suspendCoroutine {
context.registerReceiver(object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent?) {
try {
it.resume(intent?.getSerializableExtra(EXTRA_STATUS_INFO) as? StatusInfo)
} catch (e: Exception) {
Log.w(TAG, e)
}
context.unregisterReceiver(this)
}
}, IntentFilter(ACTION_STATUS_INFO_RESPONSE))
try {
context.sendOrderedBroadcast(Intent(context, StatusInfoProvider::class.java), null)
} catch (e: Exception) {
it.resume(null)
}
}

View File

@ -0,0 +1,90 @@
/*
* SPDX-FileCopyrightText: 2020, microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package org.microg.gms.snet
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.util.Log
import java.io.Serializable
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
import kotlin.coroutines.suspendCoroutine
private const val ACTION_SERVICE_INFO_REQUEST = "org.microg.gms.snet.SERVICE_INFO_REQUEST"
private const val ACTION_UPDATE_CONFIGURATION = "org.microg.gms.snet.UPDATE_CONFIGURATION"
private const val ACTION_SERVICE_INFO_RESPONSE = "org.microg.gms.snet.SERVICE_INFO_RESPONSE"
private const val EXTRA_SERVICE_INFO = "org.microg.gms.snet.SERVICE_INFO"
private const val EXTRA_CONFIGURATION = "org.microg.gms.snet.CONFIGURATION"
private const val TAG = "GmsSafetyNetStatusInfo"
data class ServiceInfo(val configuration: ServiceConfiguration) : Serializable
data class ServiceConfiguration(val enabled: Boolean) : Serializable {
fun saveToPrefs(context: Context) {
SafetyNetPrefs.get(context).isEnabled = enabled
}
}
private fun SafetyNetPrefs.toConfiguration(): ServiceConfiguration = ServiceConfiguration(isEnabled)
class ServiceInfoReceiver : BroadcastReceiver() {
private fun sendInfoResponse(context: Context) {
context.sendOrderedBroadcast(Intent(ACTION_SERVICE_INFO_RESPONSE).apply {
setPackage(context.packageName)
putExtra(EXTRA_SERVICE_INFO, ServiceInfo(SafetyNetPrefs.get(context).toConfiguration()))
}, null)
}
override fun onReceive(context: Context, intent: Intent) {
try {
when (intent.action) {
ACTION_UPDATE_CONFIGURATION -> {
(intent.getSerializableExtra(EXTRA_CONFIGURATION) as? ServiceConfiguration)?.saveToPrefs(context)
}
}
sendInfoResponse(context)
} catch (e: Exception) {
Log.w(TAG, e)
}
}
}
private suspend fun sendToServiceInfoReceiver(intent: Intent, context: Context): ServiceInfo = suspendCoroutine {
context.registerReceiver(object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
context.unregisterReceiver(this)
val serviceInfo = try {
intent.getSerializableExtra(EXTRA_SERVICE_INFO) as ServiceInfo
} catch (e: Exception) {
it.resumeWithException(e)
return
}
try {
it.resume(serviceInfo)
} catch (e: Exception) {
Log.w(TAG, e)
}
}
}, IntentFilter(ACTION_SERVICE_INFO_RESPONSE))
try {
context.sendOrderedBroadcast(intent, null)
} catch (e: Exception) {
it.resumeWithException(e)
}
}
suspend fun getSafetyNetServiceInfo(context: Context): ServiceInfo = sendToServiceInfoReceiver(
Intent(context, ServiceInfoReceiver::class.java).apply {
action = ACTION_SERVICE_INFO_REQUEST
}, context)
suspend fun setSafetyNetServiceConfiguration(context: Context, configuration: ServiceConfiguration): ServiceInfo = sendToServiceInfoReceiver(
Intent(context, ServiceInfoReceiver::class.java).apply {
action = ACTION_UPDATE_CONFIGURATION
putExtra(EXTRA_CONFIGURATION, configuration)
}, context)

View File

@ -13,7 +13,9 @@ import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import com.google.android.gms.R
import com.google.android.gms.databinding.DeviceRegistrationFragmentBinding
import org.microg.gms.checkin.CheckinPrefs
import org.microg.gms.checkin.ServiceInfo
import org.microg.gms.checkin.getCheckinServiceInfo
import org.microg.gms.checkin.setCheckinServiceConfiguration
class DeviceRegistrationFragment : Fragment(R.layout.device_registration_fragment) {
private lateinit var binding: DeviceRegistrationFragmentBinding
@ -22,17 +24,28 @@ class DeviceRegistrationFragment : Fragment(R.layout.device_registration_fragmen
binding = DeviceRegistrationFragmentBinding.inflate(inflater, container, false)
binding.switchBarCallback = object : PreferenceSwitchBarCallback {
override fun onChecked(newStatus: Boolean) {
CheckinPrefs.setEnabled(context, newStatus)
binding.checkinEnabled = newStatus
setEnabled(newStatus)
}
}
return binding.root
}
fun setEnabled(newStatus: Boolean) {
lifecycleScope.launchWhenResumed {
val info = getCheckinServiceInfo(requireContext())
val newConfiguration = info.configuration.copy(enabled = newStatus)
displayServiceInfo(setCheckinServiceConfiguration(requireContext(), newConfiguration))
}
}
fun displayServiceInfo(serviceInfo: ServiceInfo) {
binding.checkinEnabled = serviceInfo.configuration.enabled
}
override fun onResume() {
super.onResume()
lifecycleScope.launchWhenResumed {
binding.checkinEnabled = CheckinPrefs.get(context).isEnabled
displayServiceInfo(getCheckinServiceInfo(requireContext()))
}
}
}

View File

@ -8,16 +8,17 @@ package org.microg.gms.ui
import android.os.Bundle
import android.os.Handler
import android.text.format.DateUtils
import androidx.lifecycle.lifecycleScope
import androidx.preference.Preference
import androidx.preference.PreferenceCategory
import androidx.preference.PreferenceFragmentCompat
import com.google.android.gms.R
import org.microg.gms.checkin.CheckinPrefs
import org.microg.gms.checkin.LastCheckinInfo
import org.microg.gms.checkin.getCheckinServiceInfo
class DeviceRegistrationPreferencesFragment : PreferenceFragmentCompat() {
private lateinit var statusCategory: PreferenceCategory
private lateinit var status: Preference
private lateinit var androidId: Preference
private val handler = Handler()
private val updateRunnable = Runnable { updateStatus() }
@ -28,6 +29,7 @@ class DeviceRegistrationPreferencesFragment : PreferenceFragmentCompat() {
override fun onBindPreferences() {
statusCategory = preferenceScreen.findPreference("prefcat_device_registration_status") ?: statusCategory
status = preferenceScreen.findPreference("pref_device_registration_status") ?: status
androidId = preferenceScreen.findPreference("pref_device_registration_android_id") ?: androidId
}
override fun onResume() {
@ -42,12 +44,17 @@ class DeviceRegistrationPreferencesFragment : PreferenceFragmentCompat() {
private fun updateStatus() {
handler.postDelayed(updateRunnable, UPDATE_INTERVAL)
statusCategory.isVisible = CheckinPrefs.get(context).isEnabled
val checkinInfo = LastCheckinInfo.read(requireContext())
status.summary = if (checkinInfo.lastCheckin > 0) {
getString(R.string.checkin_last_registration, DateUtils.getRelativeTimeSpanString(checkinInfo.lastCheckin, System.currentTimeMillis(), 0))
} else {
getString(R.string.checkin_not_registered)
lifecycleScope.launchWhenResumed {
val serviceInfo = getCheckinServiceInfo(requireContext())
statusCategory.isVisible = serviceInfo.configuration.enabled
if (serviceInfo.lastCheckin > 0) {
status.summary = getString(R.string.checkin_last_registration, DateUtils.getRelativeTimeSpanString(serviceInfo.lastCheckin, System.currentTimeMillis(), 0))
androidId.isVisible = true
androidId.summary = serviceInfo.androidId.toString(16)
} else {
status.summary = getString(R.string.checkin_not_registered)
androidId.isVisible = false
}
}
}

View File

@ -13,7 +13,9 @@ import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import com.google.android.gms.R
import com.google.android.gms.databinding.ExposureNotificationsFragmentBinding
import org.microg.gms.nearby.exposurenotification.ExposurePreferences
import org.microg.gms.nearby.exposurenotification.ServiceInfo
import org.microg.gms.nearby.exposurenotification.getExposureNotificationsServiceInfo
import org.microg.gms.nearby.exposurenotification.setExposureNotificationsServiceConfiguration
class ExposureNotificationsFragment : Fragment(R.layout.exposure_notifications_fragment) {
private lateinit var binding: ExposureNotificationsFragmentBinding
@ -22,17 +24,28 @@ class ExposureNotificationsFragment : Fragment(R.layout.exposure_notifications_f
binding = ExposureNotificationsFragmentBinding.inflate(inflater, container, false)
binding.switchBarCallback = object : PreferenceSwitchBarCallback {
override fun onChecked(newStatus: Boolean) {
ExposurePreferences(requireContext()).scannerEnabled = newStatus
binding.scannerEnabled = newStatus
setEnabled(newStatus)
}
}
return binding.root
}
fun setEnabled(newStatus: Boolean) {
lifecycleScope.launchWhenResumed {
val info = getExposureNotificationsServiceInfo(requireContext())
val newConfiguration = info.configuration.copy(enabled = newStatus)
displayServiceInfo(setExposureNotificationsServiceConfiguration(requireContext(), newConfiguration))
}
}
fun displayServiceInfo(serviceInfo: ServiceInfo) {
binding.scannerEnabled = serviceInfo.configuration.enabled
}
override fun onResume() {
super.onResume()
lifecycleScope.launchWhenResumed {
binding.scannerEnabled = ExposurePreferences(requireContext()).scannerEnabled
displayServiceInfo(getExposureNotificationsServiceInfo(requireContext()))
}
}
}

View File

@ -17,7 +17,7 @@ import com.google.android.gms.R
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.microg.gms.nearby.exposurenotification.ExposureDatabase
import org.microg.gms.nearby.exposurenotification.ExposurePreferences
import org.microg.gms.nearby.exposurenotification.getExposureNotificationsServiceInfo
class ExposureNotificationsPreferencesFragment : PreferenceFragmentCompat() {
private lateinit var exposureEnableInfo: Preference
@ -62,9 +62,9 @@ class ExposureNotificationsPreferencesFragment : PreferenceFragmentCompat() {
private fun updateStatus() {
lifecycleScope.launchWhenResumed {
handler.postDelayed(updateStatusRunnable, UPDATE_STATUS_INTERVAL)
val preferences = ExposurePreferences(requireContext())
exposureEnableInfo.isVisible = !preferences.scannerEnabled
advertisingId.isVisible = preferences.advertiserEnabled
val enabled = getExposureNotificationsServiceInfo(requireContext()).configuration.enabled
exposureEnableInfo.isVisible = !enabled
advertisingId.isVisible = enabled
}
}

View File

@ -11,8 +11,10 @@ import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import com.google.android.gms.R
import com.google.android.gms.databinding.PushNotificationFragmentBinding
import org.microg.gms.checkin.CheckinPrefs
import org.microg.gms.gcm.GcmPrefs
import org.microg.gms.checkin.getCheckinServiceInfo
import org.microg.gms.gcm.ServiceInfo
import org.microg.gms.gcm.getGcmServiceInfo
import org.microg.gms.gcm.setGcmServiceConfiguration
class PushNotificationFragment : Fragment(R.layout.push_notification_fragment) {
lateinit var binding: PushNotificationFragmentBinding
@ -21,18 +23,29 @@ class PushNotificationFragment : Fragment(R.layout.push_notification_fragment) {
binding = PushNotificationFragmentBinding.inflate(inflater, container, false)
binding.switchBarCallback = object : PreferenceSwitchBarCallback {
override fun onChecked(newStatus: Boolean) {
GcmPrefs.setEnabled(context, newStatus)
binding.gcmEnabled = newStatus
setEnabled(newStatus)
}
}
return binding.root
}
fun setEnabled(newStatus: Boolean) {
lifecycleScope.launchWhenResumed {
val info = getGcmServiceInfo(requireContext())
val newConfiguration = info.configuration.copy(enabled = newStatus)
displayServiceInfo(setGcmServiceConfiguration(requireContext(), newConfiguration))
}
}
fun displayServiceInfo(serviceInfo: ServiceInfo) {
binding.gcmEnabled = serviceInfo.configuration.enabled
}
override fun onResume() {
super.onResume()
lifecycleScope.launchWhenResumed {
binding.gcmEnabled = GcmPrefs.get(context).isEnabled
binding.checkinEnabled = CheckinPrefs.get(context).isEnabled
displayServiceInfo(getGcmServiceInfo(requireContext()))
binding.checkinEnabled = getCheckinServiceInfo(requireContext()).configuration.enabled
}
}

View File

@ -18,9 +18,7 @@ import com.google.android.gms.R
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.microg.gms.gcm.GcmDatabase
import org.microg.gms.gcm.GcmPrefs
import org.microg.gms.gcm.McsService
import org.microg.gms.gcm.getStatusInfo
import org.microg.gms.gcm.getGcmServiceInfo
class PushNotificationPreferencesFragment : PreferenceFragmentCompat() {
private lateinit var pushStatusCategory: PreferenceCategory
@ -67,9 +65,9 @@ class PushNotificationPreferencesFragment : PreferenceFragmentCompat() {
private fun updateStatus() {
handler.postDelayed(updateRunnable, UPDATE_INTERVAL)
pushStatusCategory.isVisible = GcmPrefs.get(context).isEnabled
lifecycleScope.launchWhenStarted {
val statusInfo = getStatusInfo(requireContext())
val statusInfo = getGcmServiceInfo(requireContext())
pushStatusCategory.isVisible = statusInfo.configuration.enabled
pushStatus.summary = if (statusInfo != null && statusInfo.connected) {
getString(R.string.gcm_network_state_connected, DateUtils.getRelativeTimeSpanString(statusInfo.startTimestamp, System.currentTimeMillis(), 0))
} else {

View File

@ -8,11 +8,14 @@ package org.microg.gms.ui
import android.os.Bundle
import android.view.*
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import com.google.android.gms.R
import com.google.android.gms.databinding.SafetyNetFragmentBinding
import org.microg.gms.checkin.CheckinPrefs
import org.microg.gms.snet.SafetyNetPrefs
import org.microg.gms.checkin.getCheckinServiceInfo
import org.microg.gms.snet.ServiceInfo
import org.microg.gms.snet.getSafetyNetServiceInfo
import org.microg.gms.snet.setSafetyNetServiceConfiguration
class SafetyNetFragment : Fragment(R.layout.safety_net_fragment) {
@ -22,17 +25,30 @@ class SafetyNetFragment : Fragment(R.layout.safety_net_fragment) {
binding = SafetyNetFragmentBinding.inflate(inflater, container, false)
binding.switchBarCallback = object : PreferenceSwitchBarCallback {
override fun onChecked(newStatus: Boolean) {
SafetyNetPrefs.get(requireContext()).isEnabled = newStatus
binding.safetynetEnabled = newStatus
setEnabled(newStatus)
}
}
return binding.root
}
fun setEnabled(newStatus: Boolean) {
lifecycleScope.launchWhenResumed {
val info = getSafetyNetServiceInfo(requireContext())
val newConfiguration = info.configuration.copy(enabled = newStatus)
displayServiceInfo(setSafetyNetServiceConfiguration(requireContext(), newConfiguration))
}
}
fun displayServiceInfo(serviceInfo: ServiceInfo) {
binding.safetynetEnabled = serviceInfo.configuration.enabled
}
override fun onResume() {
super.onResume()
binding.checkinEnabled = CheckinPrefs.get(requireContext()).isEnabled
binding.safetynetEnabled = SafetyNetPrefs.get(requireContext()).isEnabled
lifecycleScope.launchWhenResumed {
binding.checkinEnabled = getCheckinServiceInfo(requireContext()).configuration.enabled
displayServiceInfo(getSafetyNetServiceInfo(requireContext()))
}
}
override fun onActivityCreated(savedInstanceState: Bundle?) {

View File

@ -0,0 +1,100 @@
/*
* SPDX-FileCopyrightText: 2020, microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package org.microg.gms.ui
import android.os.Build
import android.os.Bundle
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import androidx.preference.Preference
import com.google.android.gms.R
import org.microg.gms.checkin.getCheckinServiceInfo
import org.microg.gms.gcm.GcmDatabase
import org.microg.gms.gcm.getGcmServiceInfo
import org.microg.gms.nearby.exposurenotification.getExposureNotificationsServiceInfo
import org.microg.gms.snet.getSafetyNetServiceInfo
import org.microg.nlp.client.UnifiedLocationClient
import org.microg.tools.ui.ResourceSettingsFragment
class SettingsFragment : ResourceSettingsFragment() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
super.onCreatePreferences(savedInstanceState, rootKey)
findPreference<Preference>(PREF_CHECKIN)!!.onPreferenceClickListener = Preference.OnPreferenceClickListener {
findNavController().navigate(requireContext(), R.id.openCheckinSettings)
true
}
findPreference<Preference>(PREF_GCM)!!.onPreferenceClickListener = Preference.OnPreferenceClickListener {
findNavController().navigate(requireContext(), R.id.openGcmSettings)
true
}
findPreference<Preference>(PREF_SNET)!!.onPreferenceClickListener = Preference.OnPreferenceClickListener {
findNavController().navigate(requireContext(), R.id.openSafetyNetSettings)
true
}
findPreference<Preference>(PREF_UNIFIEDNLP)!!.onPreferenceClickListener = Preference.OnPreferenceClickListener {
findNavController().navigate(requireContext(), R.id.openUnifiedNlpSettings)
true
}
findPreference<Preference>(PREF_EXPOSURE)!!.onPreferenceClickListener = Preference.OnPreferenceClickListener {
findNavController().navigate(requireContext(), R.id.openExposureNotificationSettings)
true
}
findPreference<Preference>(PREF_ABOUT)!!.onPreferenceClickListener = Preference.OnPreferenceClickListener {
findNavController().navigate(requireContext(), R.id.openAbout)
true
}
findPreference<Preference>(PREF_ABOUT)!!.summary = getString(R.string.about_version_str, AboutFragment.getSelfVersion(context))
}
override fun onResume() {
super.onResume()
lifecycleScope.launchWhenResumed {
updateDetails()
}
}
private suspend fun updateDetails() {
if (getGcmServiceInfo(requireContext()).configuration.enabled) {
val database = GcmDatabase(context)
val regCount = database.registrationList.size
database.close()
findPreference<Preference>(PREF_GCM)!!.summary = getString(R.string.service_status_enabled_short) + " - " + resources.getQuantityString(R.plurals.gcm_registered_apps_counter, regCount, regCount)
} else {
findPreference<Preference>(PREF_GCM)!!.setSummary(R.string.service_status_disabled_short)
}
findPreference<Preference>(PREF_CHECKIN)!!.setSummary(if (getCheckinServiceInfo(requireContext()).configuration.enabled) R.string.service_status_enabled_short else R.string.service_status_disabled_short)
findPreference<Preference>(PREF_SNET)!!.setSummary(if (getSafetyNetServiceInfo(requireContext()).configuration.enabled) R.string.service_status_enabled_short else R.string.service_status_disabled_short)
val backendCount = UnifiedLocationClient[requireContext()].getLocationBackends().size + UnifiedLocationClient[requireContext()].getGeocoderBackends().size
findPreference<Preference>(PREF_UNIFIEDNLP)!!.summary = resources.getQuantityString(R.plurals.pref_unifiednlp_summary, backendCount, backendCount);
if (Build.VERSION.SDK_INT >= 21) {
findPreference<Preference>(PREF_EXPOSURE)!!.isVisible = true
if (getExposureNotificationsServiceInfo(requireContext()).configuration.enabled) {
findPreference<Preference>(PREF_EXPOSURE)!!.summary = getString(R.string.service_status_enabled_short)
} else {
findPreference<Preference>(PREF_EXPOSURE)!!.setSummary(R.string.service_status_disabled_short)
}
} else {
findPreference<Preference>(PREF_EXPOSURE)!!.isVisible = false
}
}
companion object {
const val PREF_ABOUT = "pref_about"
const val PREF_GCM = "pref_gcm"
const val PREF_SNET = "pref_snet"
const val PREF_UNIFIEDNLP = "pref_unifiednlp"
const val PREF_CHECKIN = "pref_checkin"
const val PREF_EXPOSURE = "pref_exposure"
}
init {
preferencesResource = R.xml.preferences_start
}
}

View File

@ -140,6 +140,7 @@ This can take a couple of minutes."</string>
<string name="pref_auth_visible_summary">When enabled, all applications on this device will be able to see email address of your Google Accounts without prior authorization.</string>
<string name="pref_checkin_enable_summary">Registers your device to Google services and creates a unique device identifier. microG strips identifying bits other than your Google account name from registration data.</string>
<string name="pref_device_registration_android_id">Android ID</string>
<string name="checkin_not_registered">Not registered</string>
<string name="checkin_last_registration">Last registration: <xliff:g example="Yesterday, 02:20 PM">%1$s</xliff:g></string>

View File

@ -14,6 +14,11 @@
android:selectable="false"
android:title="@string/pref_info_status"
tools:summary="Last registration: 13 hours ago" />
<Preference
android:key="pref_device_registration_android_id"
android:selectable="false"
android:title="@string/pref_device_registration_android_id"
tools:summary="1953a59d1c1b7e4b" />
</PreferenceCategory>
<PreferenceCategory android:layout="@layout/preference_category_no_label">
<org.microg.gms.ui.TextPreference