mirror of https://github.com/M66B/FairEmail.git
Refactoring: connectionHelper
This commit is contained in:
parent
09ace5e05a
commit
76d53e9e6b
|
@ -94,7 +94,8 @@ public class ActivityEml extends ActivityBase {
|
|||
AssetFileDescriptor descriptor = resolver.openTypedAssetFileDescriptor(uri, "*/*", null);
|
||||
try (InputStream is = new BufferedInputStream(descriptor.createInputStream())) {
|
||||
|
||||
Properties props = MessageHelper.getSessionProperties(Helper.AUTH_TYPE_PASSWORD, null, false);
|
||||
Properties props = MessageHelper.getSessionProperties(
|
||||
ConnectionHelper.AUTH_TYPE_PASSWORD, null, false);
|
||||
Session isession = Session.getInstance(props, null);
|
||||
MimeMessage mmessage = new MimeMessage(isession, is);
|
||||
|
||||
|
|
|
@ -1335,7 +1335,8 @@ public class ActivityView extends ActivityBilling implements FragmentManager.OnB
|
|||
|
||||
} else {
|
||||
// Decode message
|
||||
Properties props = MessageHelper.getSessionProperties(Helper.AUTH_TYPE_PASSWORD, null, false);
|
||||
Properties props = MessageHelper.getSessionProperties(
|
||||
ConnectionHelper.AUTH_TYPE_PASSWORD, null, false);
|
||||
Session isession = Session.getInstance(props, null);
|
||||
ByteArrayInputStream is = new ByteArrayInputStream(decrypted.toByteArray());
|
||||
MimeMessage imessage = new MimeMessage(isession, is);
|
||||
|
|
|
@ -450,7 +450,7 @@ public class AdapterFolder extends RecyclerView.Adapter<AdapterFolder.ViewHolder
|
|||
protected Void onExecute(Context context, Bundle args) {
|
||||
long fid = args.getLong("folder");
|
||||
|
||||
if (!Helper.getNetworkState(context).isSuitable())
|
||||
if (!ConnectionHelper.getNetworkState(context).isSuitable())
|
||||
throw new IllegalArgumentException(context.getString(R.string.title_no_internet));
|
||||
|
||||
boolean now = true;
|
||||
|
|
|
@ -3000,7 +3000,7 @@ public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHold
|
|||
this.zoom = zoom;
|
||||
this.sort = sort;
|
||||
this.filter_duplicates = filter_duplicates;
|
||||
this.suitable = Helper.getNetworkState(context).isSuitable();
|
||||
this.suitable = ConnectionHelper.getNetworkState(context).isSuitable();
|
||||
this.properties = properties;
|
||||
|
||||
this.colorPrimary = Helper.resolveColor(context, R.attr.colorPrimary);
|
||||
|
@ -3096,7 +3096,7 @@ public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHold
|
|||
}
|
||||
|
||||
void checkInternet() {
|
||||
boolean suitable = Helper.getNetworkState(context).isSuitable();
|
||||
boolean suitable = ConnectionHelper.getNetworkState(context).isSuitable();
|
||||
if (this.suitable != suitable) {
|
||||
this.suitable = suitable;
|
||||
notifyDataSetChanged();
|
||||
|
|
|
@ -270,7 +270,7 @@ public class BoundaryCallbackMessages extends PagedList.BoundaryCallback<TupleMe
|
|||
if (imessages == null)
|
||||
try {
|
||||
// Check connectivity
|
||||
if (!Helper.getNetworkState(context).isSuitable())
|
||||
if (!ConnectionHelper.getNetworkState(context).isSuitable())
|
||||
throw new IllegalArgumentException(context.getString(R.string.title_no_internet));
|
||||
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
|
@ -288,7 +288,7 @@ public class BoundaryCallbackMessages extends PagedList.BoundaryCallback<TupleMe
|
|||
|
||||
Log.i("Boundary server connecting account=" + account.name);
|
||||
istore = (IMAPStore) isession.getStore(protocol);
|
||||
Helper.connect(context, istore, account);
|
||||
ConnectionHelper.connect(context, istore, account);
|
||||
|
||||
Log.i("Boundary server opening folder=" + browsable.name);
|
||||
ifolder = (IMAPFolder) istore.getFolder(browsable.name);
|
||||
|
|
|
@ -0,0 +1,303 @@
|
|||
package eu.faircode.email;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.accounts.AccountManager;
|
||||
import android.accounts.AuthenticatorException;
|
||||
import android.accounts.OperationCanceledException;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.Network;
|
||||
import android.net.NetworkCapabilities;
|
||||
import android.net.NetworkInfo;
|
||||
import android.os.Build;
|
||||
import android.telephony.TelephonyManager;
|
||||
|
||||
import androidx.preference.PreferenceManager;
|
||||
|
||||
import com.bugsnag.android.BreadcrumbType;
|
||||
import com.bugsnag.android.Bugsnag;
|
||||
import com.sun.mail.imap.IMAPStore;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.mail.AuthenticationFailedException;
|
||||
import javax.mail.MessagingException;
|
||||
|
||||
public class ConnectionHelper {
|
||||
// Roam like at home
|
||||
// https://en.wikipedia.org/wiki/European_Union_roaming_regulations
|
||||
private static final List<String> RLAH_COUNTRY_CODES = Collections.unmodifiableList(Arrays.asList(
|
||||
"AT", // Austria
|
||||
"BE", // Belgium
|
||||
"BG", // Bulgaria
|
||||
"HR", // Croatia
|
||||
"CY", // Cyprus
|
||||
"CZ", // Czech Republic
|
||||
"DK", // Denmark
|
||||
"EE", // Estonia
|
||||
"FI", // Finland
|
||||
"FR", // France
|
||||
"DE", // Germany
|
||||
"GR", // Greece
|
||||
"HU", // Hungary
|
||||
"IS", // Iceland
|
||||
"IE", // Ireland
|
||||
"IT", // Italy
|
||||
"LV", // Latvia
|
||||
"LI", // Liechtenstein
|
||||
"LT", // Lithuania
|
||||
"LU", // Luxembourg
|
||||
"MT", // Malta
|
||||
"NL", // Netherlands
|
||||
"NO", // Norway
|
||||
"PL", // Poland
|
||||
"PT", // Portugal
|
||||
"RO", // Romania
|
||||
"SK", // Slovakia
|
||||
"SI", // Slovenia
|
||||
"ES", // Spain
|
||||
"SE", // Sweden
|
||||
"GB" // United Kingdom
|
||||
));
|
||||
|
||||
static final int AUTH_TYPE_PASSWORD = 1;
|
||||
static final int AUTH_TYPE_GMAIL = 2;
|
||||
|
||||
static class NetworkState {
|
||||
private Boolean connected = null;
|
||||
private Boolean suitable = null;
|
||||
private Boolean unmetered = null;
|
||||
private Boolean roaming = null;
|
||||
|
||||
boolean isConnected() {
|
||||
return (connected != null && connected);
|
||||
}
|
||||
|
||||
boolean isSuitable() {
|
||||
return (suitable != null && suitable);
|
||||
}
|
||||
|
||||
boolean isUnmetered() {
|
||||
return (unmetered != null && unmetered);
|
||||
}
|
||||
|
||||
boolean isRoaming() {
|
||||
return (roaming != null && roaming);
|
||||
}
|
||||
|
||||
public void update(NetworkState newState) {
|
||||
connected = newState.connected;
|
||||
unmetered = newState.unmetered;
|
||||
suitable = newState.suitable;
|
||||
}
|
||||
}
|
||||
|
||||
static NetworkState getNetworkState(Context context) {
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
boolean metered = prefs.getBoolean("metered", true);
|
||||
boolean rlah = prefs.getBoolean("rlah", true);
|
||||
boolean roaming = prefs.getBoolean("roaming", true);
|
||||
|
||||
NetworkState state = new NetworkState();
|
||||
Boolean isMetered = isMetered(context);
|
||||
state.connected = (isMetered != null);
|
||||
state.unmetered = (isMetered != null && !isMetered);
|
||||
state.suitable = (isMetered != null && (metered || !isMetered));
|
||||
|
||||
if (state.connected && !roaming) {
|
||||
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
|
||||
NetworkInfo ani = cm.getActiveNetworkInfo();
|
||||
if (ani != null)
|
||||
state.roaming = ani.isRoaming();
|
||||
} else {
|
||||
Network active = cm.getActiveNetwork();
|
||||
if (active != null) {
|
||||
NetworkCapabilities caps = cm.getNetworkCapabilities(active);
|
||||
if (caps != null)
|
||||
state.roaming = !caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING);
|
||||
}
|
||||
}
|
||||
|
||||
if (state.roaming != null && state.roaming && rlah)
|
||||
try {
|
||||
TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
|
||||
if (tm != null) {
|
||||
String sim = tm.getSimCountryIso();
|
||||
String network = tm.getNetworkCountryIso();
|
||||
Log.i("Country SIM=" + sim + " network=" + network);
|
||||
if (sim != null && network != null &&
|
||||
RLAH_COUNTRY_CODES.contains(sim) &&
|
||||
RLAH_COUNTRY_CODES.contains(network))
|
||||
state.roaming = false;
|
||||
}
|
||||
} catch (Throwable ex) {
|
||||
Log.w(ex);
|
||||
}
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
private static Boolean isMetered(Context context) {
|
||||
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
|
||||
NetworkInfo ani = cm.getActiveNetworkInfo();
|
||||
if (ani == null || !ani.isConnected())
|
||||
return null;
|
||||
return cm.isActiveNetworkMetered();
|
||||
}
|
||||
|
||||
Network active = cm.getActiveNetwork();
|
||||
if (active == null) {
|
||||
Log.i("isMetered: no active network");
|
||||
return null;
|
||||
}
|
||||
|
||||
NetworkCapabilities caps = cm.getNetworkCapabilities(active);
|
||||
if (caps == null) {
|
||||
Log.i("isMetered: active no caps");
|
||||
return null; // network unknown
|
||||
}
|
||||
|
||||
Log.i("isMetered: active caps=" + caps);
|
||||
|
||||
if (caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN) &&
|
||||
!caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) {
|
||||
Log.i("isMetered: no internet");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)) {
|
||||
Log.i("isMetered: active restricted");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P &&
|
||||
!caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_FOREGROUND)) {
|
||||
Log.i("isMetered: active background");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)) {
|
||||
boolean unmetered = caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
|
||||
Log.i("isMetered: active not VPN unmetered=" + unmetered);
|
||||
return !unmetered;
|
||||
}
|
||||
|
||||
// VPN: evaluate underlying networks
|
||||
|
||||
boolean underlying = false;
|
||||
Network[] networks = cm.getAllNetworks();
|
||||
if (networks != null)
|
||||
for (Network network : networks) {
|
||||
caps = cm.getNetworkCapabilities(network);
|
||||
if (caps == null) {
|
||||
Log.i("isMetered: no underlying caps");
|
||||
continue; // network unknown
|
||||
}
|
||||
|
||||
Log.i("isMetered: underlying caps=" + caps);
|
||||
|
||||
if (!caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) {
|
||||
Log.i("isMetered: underlying no internet");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)) {
|
||||
Log.i("isMetered: underlying restricted");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P &&
|
||||
!caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_FOREGROUND)) {
|
||||
Log.i("isMetered: underlying background");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)) {
|
||||
underlying = true;
|
||||
Log.i("isMetered: underlying is connected");
|
||||
|
||||
if (caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED)) {
|
||||
Log.i("isMetered: underlying is unmetered");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!underlying) {
|
||||
Log.i("isMetered: no underlying network");
|
||||
return null;
|
||||
}
|
||||
|
||||
// Assume metered
|
||||
Log.i("isMetered: underlying assume metered");
|
||||
return true;
|
||||
}
|
||||
|
||||
static void connect(Context context, IMAPStore istore, EntityAccount account)
|
||||
throws MessagingException, AuthenticatorException, OperationCanceledException, IOException {
|
||||
try {
|
||||
istore.connect(account.host, account.port, account.user, account.password);
|
||||
} catch (AuthenticationFailedException ex) {
|
||||
if (account.auth_type == AUTH_TYPE_GMAIL) {
|
||||
account.password = refreshToken(context, "com.google", account.user, account.password);
|
||||
DB.getInstance(context).account().setAccountPassword(account.id, account.password);
|
||||
istore.connect(account.host, account.port, account.user, account.password);
|
||||
} else
|
||||
throw ex;
|
||||
}
|
||||
|
||||
// https://www.ietf.org/rfc/rfc2971.txt
|
||||
if (istore.hasCapability("ID"))
|
||||
try {
|
||||
Map<String, String> id = new LinkedHashMap<>();
|
||||
id.put("name", context.getString(R.string.app_name));
|
||||
id.put("version", BuildConfig.VERSION_NAME);
|
||||
Map<String, String> sid = istore.id(id);
|
||||
if (sid != null) {
|
||||
Map<String, String> crumb = new HashMap<>();
|
||||
for (String key : sid.keySet()) {
|
||||
crumb.put(key, sid.get(key));
|
||||
Log.i("Server " + key + "=" + sid.get(key));
|
||||
}
|
||||
Bugsnag.leaveBreadcrumb("server", BreadcrumbType.LOG, crumb);
|
||||
}
|
||||
} catch (MessagingException ex) {
|
||||
Log.w(ex);
|
||||
}
|
||||
}
|
||||
|
||||
static String refreshToken(Context context, String type, String name, String current)
|
||||
throws AuthenticatorException, OperationCanceledException, IOException {
|
||||
AccountManager am = AccountManager.get(context);
|
||||
Account[] accounts = am.getAccountsByType(type);
|
||||
for (Account account : accounts)
|
||||
if (name.equals(account.name)) {
|
||||
Log.i("Refreshing token");
|
||||
am.invalidateAuthToken(type, current);
|
||||
String refreshed = am.blockingGetAuthToken(account, getAuthTokenType(type), true);
|
||||
if (refreshed == null)
|
||||
throw new OperationCanceledException("no token");
|
||||
Log.i("Refreshed token");
|
||||
return refreshed;
|
||||
}
|
||||
return current;
|
||||
}
|
||||
|
||||
static String getAuthTokenType(String type) {
|
||||
if ("com.google".equals(type))
|
||||
return "oauth2:https://mail.google.com/";
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
|
@ -2125,13 +2125,13 @@ class Core {
|
|||
}
|
||||
|
||||
static class State {
|
||||
private Helper.NetworkState networkState;
|
||||
private ConnectionHelper.NetworkState networkState;
|
||||
private Thread thread;
|
||||
private Semaphore semaphore = new Semaphore(0);
|
||||
private boolean running = true;
|
||||
private boolean recoverable = true;
|
||||
|
||||
State(Helper.NetworkState networkState) {
|
||||
State(ConnectionHelper.NetworkState networkState) {
|
||||
this.networkState = networkState;
|
||||
}
|
||||
|
||||
|
@ -2139,7 +2139,7 @@ class Core {
|
|||
this(parent.networkState);
|
||||
}
|
||||
|
||||
Helper.NetworkState getNetworkState() {
|
||||
ConnectionHelper.NetworkState getNetworkState() {
|
||||
return networkState;
|
||||
}
|
||||
|
||||
|
|
|
@ -447,8 +447,8 @@ public class EmailProvider {
|
|||
|
||||
int getAuthType() {
|
||||
if ("com.google".equals(type))
|
||||
return Helper.AUTH_TYPE_GMAIL;
|
||||
return Helper.AUTH_TYPE_PASSWORD;
|
||||
return ConnectionHelper.AUTH_TYPE_GMAIL;
|
||||
return ConnectionHelper.AUTH_TYPE_PASSWORD;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -150,7 +150,7 @@ public class FragmentAccount extends FragmentBase {
|
|||
|
||||
private long id = -1;
|
||||
private boolean saving = false;
|
||||
private int auth_type = Helper.AUTH_TYPE_PASSWORD;
|
||||
private int auth_type = ConnectionHelper.AUTH_TYPE_PASSWORD;
|
||||
private int color = Color.TRANSPARENT;
|
||||
|
||||
@Override
|
||||
|
@ -251,7 +251,7 @@ public class FragmentAccount extends FragmentBase {
|
|||
return;
|
||||
adapterView.setTag(position);
|
||||
|
||||
auth_type = Helper.AUTH_TYPE_PASSWORD;
|
||||
auth_type = ConnectionHelper.AUTH_TYPE_PASSWORD;
|
||||
|
||||
etHost.setText(provider.imap_host);
|
||||
etPort.setText(provider.imap_host == null ? null : Integer.toString(provider.imap_port));
|
||||
|
@ -314,8 +314,8 @@ public class FragmentAccount extends FragmentBase {
|
|||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
String user = etUser.getText().toString();
|
||||
if (auth_type != Helper.AUTH_TYPE_PASSWORD && !user.equals(etUser.getTag())) {
|
||||
auth_type = Helper.AUTH_TYPE_PASSWORD;
|
||||
if (auth_type != ConnectionHelper.AUTH_TYPE_PASSWORD && !user.equals(etUser.getTag())) {
|
||||
auth_type = ConnectionHelper.AUTH_TYPE_PASSWORD;
|
||||
tilPassword.getEditText().setText(null);
|
||||
tilPassword.setEnabled(true);
|
||||
tilPassword.setPasswordVisibilityToggleEnabled(true);
|
||||
|
@ -586,8 +586,8 @@ public class FragmentAccount extends FragmentBase {
|
|||
try {
|
||||
istore.connect(host, Integer.parseInt(port), user, password);
|
||||
} catch (AuthenticationFailedException ex) {
|
||||
if (auth_type == Helper.AUTH_TYPE_GMAIL) {
|
||||
password = Helper.refreshToken(context, "com.google", user, password);
|
||||
if (auth_type == ConnectionHelper.AUTH_TYPE_GMAIL) {
|
||||
password = ConnectionHelper.refreshToken(context, "com.google", user, password);
|
||||
istore.connect(host, Integer.parseInt(port), user, password);
|
||||
} else
|
||||
throw ex;
|
||||
|
@ -884,8 +884,8 @@ public class FragmentAccount extends FragmentBase {
|
|||
try {
|
||||
istore.connect(host, Integer.parseInt(port), user, password);
|
||||
} catch (AuthenticationFailedException ex) {
|
||||
if (auth_type == Helper.AUTH_TYPE_GMAIL) {
|
||||
password = Helper.refreshToken(context, "com.google", user, password);
|
||||
if (auth_type == ConnectionHelper.AUTH_TYPE_GMAIL) {
|
||||
password = ConnectionHelper.refreshToken(context, "com.google", user, password);
|
||||
istore.connect(host, Integer.parseInt(port), user, password);
|
||||
} else
|
||||
throw ex;
|
||||
|
@ -1129,7 +1129,7 @@ public class FragmentAccount extends FragmentBase {
|
|||
spProvider.setAdapter(aaProvider);
|
||||
|
||||
if (savedInstanceState == null) {
|
||||
auth_type = (account == null ? Helper.AUTH_TYPE_PASSWORD : account.auth_type);
|
||||
auth_type = (account == null ? ConnectionHelper.AUTH_TYPE_PASSWORD : account.auth_type);
|
||||
|
||||
if (account != null) {
|
||||
boolean found = false;
|
||||
|
@ -1154,7 +1154,7 @@ public class FragmentAccount extends FragmentBase {
|
|||
rgEncryption.check(account != null && account.starttls ? R.id.radio_starttls : R.id.radio_ssl);
|
||||
cbInsecure.setChecked(account == null ? false : account.insecure);
|
||||
|
||||
etUser.setTag(account == null || auth_type == Helper.AUTH_TYPE_PASSWORD ? null : account.user);
|
||||
etUser.setTag(account == null || auth_type == ConnectionHelper.AUTH_TYPE_PASSWORD ? null : account.user);
|
||||
etUser.setText(account == null ? null : account.user);
|
||||
tilPassword.getEditText().setText(account == null ? null : account.password);
|
||||
etRealm.setText(account == null ? null : account.realm);
|
||||
|
@ -1202,8 +1202,8 @@ public class FragmentAccount extends FragmentBase {
|
|||
|
||||
Helper.setViewsEnabled(view, true);
|
||||
|
||||
tilPassword.setEnabled(auth_type == Helper.AUTH_TYPE_PASSWORD);
|
||||
etRealm.setEnabled(auth_type == Helper.AUTH_TYPE_PASSWORD);
|
||||
tilPassword.setEnabled(auth_type == ConnectionHelper.AUTH_TYPE_PASSWORD);
|
||||
etRealm.setEnabled(auth_type == ConnectionHelper.AUTH_TYPE_PASSWORD);
|
||||
|
||||
setColor(color);
|
||||
cbPrimary.setEnabled(cbSynchronize.isChecked());
|
||||
|
@ -1350,7 +1350,7 @@ public class FragmentAccount extends FragmentBase {
|
|||
|
||||
am.getAuthToken(
|
||||
account,
|
||||
Helper.getAuthTokenType(type),
|
||||
ConnectionHelper.getAuthTokenType(type),
|
||||
new Bundle(),
|
||||
getActivity(),
|
||||
new AccountManagerCallback<Bundle>() {
|
||||
|
@ -1361,7 +1361,7 @@ public class FragmentAccount extends FragmentBase {
|
|||
String token = bundle.getString(AccountManager.KEY_AUTHTOKEN);
|
||||
Log.i("Got token");
|
||||
|
||||
auth_type = Helper.AUTH_TYPE_GMAIL;
|
||||
auth_type = ConnectionHelper.AUTH_TYPE_GMAIL;
|
||||
etUser.setTag(account.name);
|
||||
etUser.setText(account.name);
|
||||
etUser.setTag(account.name);
|
||||
|
@ -1379,9 +1379,9 @@ public class FragmentAccount extends FragmentBase {
|
|||
} finally {
|
||||
btnAuthorize.setEnabled(true);
|
||||
etUser.setEnabled(true);
|
||||
tilPassword.setEnabled(auth_type == Helper.AUTH_TYPE_PASSWORD);
|
||||
tilPassword.setPasswordVisibilityToggleEnabled(auth_type == Helper.AUTH_TYPE_PASSWORD);
|
||||
etRealm.setEnabled(auth_type == Helper.AUTH_TYPE_PASSWORD);
|
||||
tilPassword.setEnabled(auth_type == ConnectionHelper.AUTH_TYPE_PASSWORD);
|
||||
tilPassword.setPasswordVisibilityToggleEnabled(auth_type == ConnectionHelper.AUTH_TYPE_PASSWORD);
|
||||
etRealm.setEnabled(auth_type == ConnectionHelper.AUTH_TYPE_PASSWORD);
|
||||
btnCheck.setEnabled(true);
|
||||
btnSave.setEnabled(true);
|
||||
new Handler().postDelayed(new Runnable() {
|
||||
|
|
|
@ -746,7 +746,7 @@ public class FragmentCompose extends FragmentBase {
|
|||
};
|
||||
|
||||
private void checkInternet() {
|
||||
boolean suitable = Helper.getNetworkState(getContext()).isSuitable();
|
||||
boolean suitable = ConnectionHelper.getNetworkState(getContext()).isSuitable();
|
||||
|
||||
Boolean content = (Boolean) tvNoInternet.getTag();
|
||||
tvNoInternet.setVisibility(!suitable && content != null && !content ? View.VISIBLE : View.GONE);
|
||||
|
@ -1377,7 +1377,8 @@ public class FragmentCompose extends FragmentBase {
|
|||
(message.identity == null ? null : db.identity().getIdentity(message.identity));
|
||||
|
||||
// Build message
|
||||
Properties props = MessageHelper.getSessionProperties(Helper.AUTH_TYPE_PASSWORD, null, false);
|
||||
Properties props = MessageHelper.getSessionProperties(
|
||||
ConnectionHelper.AUTH_TYPE_PASSWORD, null, false);
|
||||
Session isession = Session.getInstance(props, null);
|
||||
MimeMessage imessage = new MimeMessage(isession);
|
||||
MessageHelper.build(context, message, attachments, identity, imessage);
|
||||
|
|
|
@ -332,7 +332,7 @@ public class FragmentFolders extends FragmentBase {
|
|||
protected Void onExecute(Context context, Bundle args) {
|
||||
long aid = args.getLong("account");
|
||||
|
||||
if (!Helper.getNetworkState(context).isSuitable())
|
||||
if (!ConnectionHelper.getNetworkState(context).isSuitable())
|
||||
throw new IllegalArgumentException(context.getString(R.string.title_no_internet));
|
||||
|
||||
boolean now = true;
|
||||
|
|
|
@ -123,7 +123,7 @@ public class FragmentIdentity extends FragmentBase {
|
|||
private long id = -1;
|
||||
private long account = -1;
|
||||
private boolean saving = false;
|
||||
private int auth_type = Helper.AUTH_TYPE_PASSWORD;
|
||||
private int auth_type = ConnectionHelper.AUTH_TYPE_PASSWORD;
|
||||
private int color = Color.TRANSPARENT;
|
||||
private String signature = null;
|
||||
|
||||
|
@ -240,12 +240,12 @@ public class FragmentIdentity extends FragmentBase {
|
|||
|
||||
// Copy account credentials
|
||||
etEmail.setText(account.user);
|
||||
etUser.setTag(auth_type == Helper.AUTH_TYPE_PASSWORD ? null : account.user);
|
||||
etUser.setTag(auth_type == ConnectionHelper.AUTH_TYPE_PASSWORD ? null : account.user);
|
||||
etUser.setText(account.user);
|
||||
tilPassword.getEditText().setText(account.password);
|
||||
etRealm.setText(account.realm);
|
||||
tilPassword.setEnabled(auth_type == Helper.AUTH_TYPE_PASSWORD);
|
||||
etRealm.setEnabled(auth_type == Helper.AUTH_TYPE_PASSWORD);
|
||||
tilPassword.setEnabled(auth_type == ConnectionHelper.AUTH_TYPE_PASSWORD);
|
||||
etRealm.setEnabled(auth_type == ConnectionHelper.AUTH_TYPE_PASSWORD);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -261,8 +261,8 @@ public class FragmentIdentity extends FragmentBase {
|
|||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
String user = etUser.getText().toString();
|
||||
if (auth_type != Helper.AUTH_TYPE_PASSWORD && !user.equals(etUser.getTag())) {
|
||||
auth_type = Helper.AUTH_TYPE_PASSWORD;
|
||||
if (auth_type != ConnectionHelper.AUTH_TYPE_PASSWORD && !user.equals(etUser.getTag())) {
|
||||
auth_type = ConnectionHelper.AUTH_TYPE_PASSWORD;
|
||||
tilPassword.getEditText().setText(null);
|
||||
tilPassword.setEnabled(true);
|
||||
etRealm.setEnabled(true);
|
||||
|
@ -647,8 +647,8 @@ public class FragmentIdentity extends FragmentBase {
|
|||
try {
|
||||
itransport.connect(host, Integer.parseInt(port), user, password);
|
||||
} catch (AuthenticationFailedException ex) {
|
||||
if (auth_type == Helper.AUTH_TYPE_GMAIL) {
|
||||
password = Helper.refreshToken(context, "com.google", user, password);
|
||||
if (auth_type == ConnectionHelper.AUTH_TYPE_GMAIL) {
|
||||
password = ConnectionHelper.refreshToken(context, "com.google", user, password);
|
||||
itransport.connect(host, Integer.parseInt(port), user, password);
|
||||
} else
|
||||
throw ex;
|
||||
|
@ -764,7 +764,7 @@ public class FragmentIdentity extends FragmentBase {
|
|||
@Override
|
||||
protected void onExecuted(Bundle args, final EntityIdentity identity) {
|
||||
if (savedInstanceState == null) {
|
||||
auth_type = (identity == null ? Helper.AUTH_TYPE_PASSWORD : identity.auth_type);
|
||||
auth_type = (identity == null ? ConnectionHelper.AUTH_TYPE_PASSWORD : identity.auth_type);
|
||||
|
||||
etName.setText(identity == null ? null : identity.name);
|
||||
etEmail.setText(identity == null ? null : identity.email);
|
||||
|
@ -779,7 +779,7 @@ public class FragmentIdentity extends FragmentBase {
|
|||
rgEncryption.check(identity != null && identity.starttls ? R.id.radio_starttls : R.id.radio_ssl);
|
||||
cbInsecure.setChecked(identity == null ? false : identity.insecure);
|
||||
etPort.setText(identity == null ? null : Long.toString(identity.port));
|
||||
etUser.setTag(identity == null || auth_type == Helper.AUTH_TYPE_PASSWORD ? null : identity.user);
|
||||
etUser.setTag(identity == null || auth_type == ConnectionHelper.AUTH_TYPE_PASSWORD ? null : identity.user);
|
||||
etUser.setText(identity == null ? null : identity.user);
|
||||
tilPassword.getEditText().setText(identity == null ? null : identity.password);
|
||||
etRealm.setText(identity == null ? null : identity.realm);
|
||||
|
@ -825,8 +825,8 @@ public class FragmentIdentity extends FragmentBase {
|
|||
|
||||
Helper.setViewsEnabled(view, true);
|
||||
|
||||
tilPassword.setEnabled(auth_type == Helper.AUTH_TYPE_PASSWORD);
|
||||
etRealm.setEnabled(auth_type == Helper.AUTH_TYPE_PASSWORD);
|
||||
tilPassword.setEnabled(auth_type == ConnectionHelper.AUTH_TYPE_PASSWORD);
|
||||
etRealm.setEnabled(auth_type == ConnectionHelper.AUTH_TYPE_PASSWORD);
|
||||
|
||||
setColor(color);
|
||||
|
||||
|
@ -847,7 +847,7 @@ public class FragmentIdentity extends FragmentBase {
|
|||
|
||||
EntityAccount unselected = new EntityAccount();
|
||||
unselected.id = -1L;
|
||||
unselected.auth_type = Helper.AUTH_TYPE_PASSWORD;
|
||||
unselected.auth_type = ConnectionHelper.AUTH_TYPE_PASSWORD;
|
||||
unselected.name = getString(R.string.title_select);
|
||||
unselected.primary = false;
|
||||
accounts.add(0, unselected);
|
||||
|
@ -885,7 +885,7 @@ public class FragmentIdentity extends FragmentBase {
|
|||
spAccount.setTag(pos);
|
||||
spAccount.setSelection(pos);
|
||||
// OAuth token could be updated
|
||||
if (pos > 0 && accounts.get(pos).auth_type != Helper.AUTH_TYPE_PASSWORD)
|
||||
if (pos > 0 && accounts.get(pos).auth_type != ConnectionHelper.AUTH_TYPE_PASSWORD)
|
||||
tilPassword.getEditText().setText(accounts.get(pos).password);
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -803,7 +803,7 @@ public class FragmentMessages extends FragmentBase {
|
|||
protected Void onExecute(Context context, Bundle args) {
|
||||
long fid = args.getLong("folder");
|
||||
|
||||
if (!Helper.getNetworkState(context).isSuitable())
|
||||
if (!ConnectionHelper.getNetworkState(context).isSuitable())
|
||||
throw new IllegalArgumentException(context.getString(R.string.title_no_internet));
|
||||
|
||||
boolean now = true;
|
||||
|
@ -2720,7 +2720,7 @@ public class FragmentMessages extends FragmentBase {
|
|||
if (download == 0)
|
||||
download = Long.MAX_VALUE;
|
||||
|
||||
boolean unmetered = Helper.getNetworkState(getContext()).isUnmetered();
|
||||
boolean unmetered = ConnectionHelper.getNetworkState(getContext()).isUnmetered();
|
||||
|
||||
int count = 0;
|
||||
int unseen = 0;
|
||||
|
|
|
@ -222,7 +222,7 @@ public class FragmentOptionsConnection extends FragmentBase implements SharedPre
|
|||
@Override
|
||||
public void run() {
|
||||
if (getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.RESUMED)) {
|
||||
Helper.NetworkState networkState = Helper.getNetworkState(getContext());
|
||||
ConnectionHelper.NetworkState networkState = ConnectionHelper.getNetworkState(getContext());
|
||||
|
||||
tvConnectionType.setText(networkState.isUnmetered() ? R.string.title_legend_unmetered : R.string.title_legend_metered);
|
||||
tvConnectionType.setVisibility(networkState.isConnected() ? View.VISIBLE : View.GONE);
|
||||
|
|
|
@ -91,7 +91,7 @@ public class FragmentQuickSetup extends FragmentBase {
|
|||
private Button btnSave;
|
||||
private Group grpSetup;
|
||||
|
||||
private int auth_type = Helper.AUTH_TYPE_PASSWORD;
|
||||
private int auth_type = ConnectionHelper.AUTH_TYPE_PASSWORD;
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
|
@ -138,8 +138,8 @@ public class FragmentQuickSetup extends FragmentBase {
|
|||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
if (auth_type != Helper.AUTH_TYPE_PASSWORD) {
|
||||
auth_type = Helper.AUTH_TYPE_PASSWORD;
|
||||
if (auth_type != ConnectionHelper.AUTH_TYPE_PASSWORD) {
|
||||
auth_type = ConnectionHelper.AUTH_TYPE_PASSWORD;
|
||||
tilPassword.getEditText().setText(null);
|
||||
tilPassword.setEnabled(true);
|
||||
tilPassword.setPasswordVisibilityToggleEnabled(true);
|
||||
|
@ -498,7 +498,7 @@ public class FragmentQuickSetup extends FragmentBase {
|
|||
|
||||
am.getAuthToken(
|
||||
account,
|
||||
Helper.getAuthTokenType(type),
|
||||
ConnectionHelper.getAuthTokenType(type),
|
||||
new Bundle(),
|
||||
getActivity(),
|
||||
new AccountManagerCallback<Bundle>() {
|
||||
|
@ -511,7 +511,7 @@ public class FragmentQuickSetup extends FragmentBase {
|
|||
|
||||
etEmail.setText(account.name);
|
||||
tilPassword.getEditText().setText(token);
|
||||
auth_type = Helper.AUTH_TYPE_GMAIL;
|
||||
auth_type = ConnectionHelper.AUTH_TYPE_GMAIL;
|
||||
} catch (Throwable ex) {
|
||||
Log.e(ex);
|
||||
if (ex instanceof OperationCanceledException ||
|
||||
|
@ -523,8 +523,8 @@ public class FragmentQuickSetup extends FragmentBase {
|
|||
Helper.unexpectedError(getContext(), getViewLifecycleOwner(), ex);
|
||||
} finally {
|
||||
etEmail.setEnabled(true);
|
||||
tilPassword.setEnabled(auth_type == Helper.AUTH_TYPE_PASSWORD);
|
||||
tilPassword.setPasswordVisibilityToggleEnabled(auth_type == Helper.AUTH_TYPE_PASSWORD);
|
||||
tilPassword.setEnabled(auth_type == ConnectionHelper.AUTH_TYPE_PASSWORD);
|
||||
tilPassword.setPasswordVisibilityToggleEnabled(auth_type == ConnectionHelper.AUTH_TYPE_PASSWORD);
|
||||
btnAuthorize.setEnabled(true);
|
||||
btnCheck.setEnabled(true);
|
||||
new Handler().postDelayed(new Runnable() {
|
||||
|
|
|
@ -19,10 +19,6 @@ package eu.faircode.email;
|
|||
Copyright 2018-2019 by Marcel Bokhorst (M66B)
|
||||
*/
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.accounts.AccountManager;
|
||||
import android.accounts.AuthenticatorException;
|
||||
import android.accounts.OperationCanceledException;
|
||||
import android.app.usage.UsageStatsManager;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.Context;
|
||||
|
@ -40,14 +36,11 @@ import android.graphics.Point;
|
|||
import android.net.ConnectivityManager;
|
||||
import android.net.Network;
|
||||
import android.net.NetworkCapabilities;
|
||||
import android.net.NetworkInfo;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcel;
|
||||
import android.os.PowerManager;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.text.TextUtils;
|
||||
import android.text.format.DateUtils;
|
||||
import android.text.format.Time;
|
||||
import android.view.Display;
|
||||
|
@ -71,10 +64,7 @@ import androidx.lifecycle.LifecycleOwner;
|
|||
import androidx.preference.PreferenceManager;
|
||||
|
||||
import com.android.billingclient.api.BillingClient;
|
||||
import com.bugsnag.android.BreadcrumbType;
|
||||
import com.bugsnag.android.Bugsnag;
|
||||
import com.google.android.material.bottomnavigation.BottomNavigationView;
|
||||
import com.sun.mail.imap.IMAPStore;
|
||||
import com.sun.mail.util.MailConnectException;
|
||||
|
||||
import org.json.JSONException;
|
||||
|
@ -103,11 +93,8 @@ import java.text.DateFormat;
|
|||
import java.text.DecimalFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
@ -116,10 +103,8 @@ import java.util.Set;
|
|||
import java.util.concurrent.ThreadFactory;
|
||||
|
||||
import javax.mail.Address;
|
||||
import javax.mail.AuthenticationFailedException;
|
||||
import javax.mail.FolderClosedException;
|
||||
import javax.mail.MessageRemovedException;
|
||||
import javax.mail.MessagingException;
|
||||
import javax.mail.internet.InternetAddress;
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
|
||||
|
@ -133,9 +118,6 @@ public class Helper {
|
|||
static final int NOTIFICATION_SEND = 2;
|
||||
static final int NOTIFICATION_EXTERNAL = 3;
|
||||
|
||||
static final int AUTH_TYPE_PASSWORD = 1;
|
||||
static final int AUTH_TYPE_GMAIL = 2;
|
||||
|
||||
static final float LOW_LIGHT = 0.6f;
|
||||
|
||||
static final String FAQ_URI = "https://github.com/M66B/open-source-email/blob/master/FAQ.md";
|
||||
|
@ -149,42 +131,6 @@ public class Helper {
|
|||
}
|
||||
};
|
||||
|
||||
// Roam like at home
|
||||
// https://en.wikipedia.org/wiki/European_Union_roaming_regulations
|
||||
private static final List<String> RLAH_COUNTRY_CODES = Collections.unmodifiableList(Arrays.asList(
|
||||
"AT", // Austria
|
||||
"BE", // Belgium
|
||||
"BG", // Bulgaria
|
||||
"HR", // Croatia
|
||||
"CY", // Cyprus
|
||||
"CZ", // Czech Republic
|
||||
"DK", // Denmark
|
||||
"EE", // Estonia
|
||||
"FI", // Finland
|
||||
"FR", // France
|
||||
"DE", // Germany
|
||||
"GR", // Greece
|
||||
"HU", // Hungary
|
||||
"IS", // Iceland
|
||||
"IE", // Ireland
|
||||
"IT", // Italy
|
||||
"LV", // Latvia
|
||||
"LI", // Liechtenstein
|
||||
"LT", // Lithuania
|
||||
"LU", // Luxembourg
|
||||
"MT", // Malta
|
||||
"NL", // Netherlands
|
||||
"NO", // Norway
|
||||
"PL", // Poland
|
||||
"PT", // Portugal
|
||||
"RO", // Romania
|
||||
"SK", // Slovakia
|
||||
"SI", // Slovenia
|
||||
"ES", // Spain
|
||||
"SE", // Sweden
|
||||
"GB" // United Kingdom
|
||||
));
|
||||
|
||||
static boolean hasPermission(Context context, String name) {
|
||||
return (ContextCompat.checkSelfPermission(context, name) == PackageManager.PERMISSION_GRANTED);
|
||||
}
|
||||
|
@ -825,236 +771,6 @@ public class Helper {
|
|||
return filename.substring(index + 1);
|
||||
}
|
||||
|
||||
static class NetworkState {
|
||||
private Boolean connected = null;
|
||||
private Boolean suitable = null;
|
||||
private Boolean unmetered = null;
|
||||
private Boolean roaming = null;
|
||||
|
||||
boolean isConnected() {
|
||||
return (connected != null && connected);
|
||||
}
|
||||
|
||||
boolean isSuitable() {
|
||||
return (suitable != null && suitable);
|
||||
}
|
||||
|
||||
boolean isUnmetered() {
|
||||
return (unmetered != null && unmetered);
|
||||
}
|
||||
|
||||
boolean isRoaming() {
|
||||
return (roaming != null && roaming);
|
||||
}
|
||||
|
||||
public void update(NetworkState newState) {
|
||||
connected = newState.connected;
|
||||
unmetered = newState.unmetered;
|
||||
suitable = newState.suitable;
|
||||
}
|
||||
}
|
||||
|
||||
static NetworkState getNetworkState(Context context) {
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
boolean metered = prefs.getBoolean("metered", true);
|
||||
boolean rlah = prefs.getBoolean("rlah", true);
|
||||
boolean roaming = prefs.getBoolean("roaming", true);
|
||||
|
||||
NetworkState state = new NetworkState();
|
||||
Boolean isMetered = isMetered(context);
|
||||
state.connected = (isMetered != null);
|
||||
state.unmetered = (isMetered != null && !isMetered);
|
||||
state.suitable = (isMetered != null && (metered || !isMetered));
|
||||
|
||||
if (state.connected && !roaming) {
|
||||
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
|
||||
NetworkInfo ani = cm.getActiveNetworkInfo();
|
||||
if (ani != null)
|
||||
state.roaming = ani.isRoaming();
|
||||
} else {
|
||||
Network active = cm.getActiveNetwork();
|
||||
if (active != null) {
|
||||
NetworkCapabilities caps = cm.getNetworkCapabilities(active);
|
||||
if (caps != null)
|
||||
state.roaming = !caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING);
|
||||
}
|
||||
}
|
||||
|
||||
if (state.roaming != null && state.roaming && rlah)
|
||||
try {
|
||||
TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
|
||||
if (tm != null) {
|
||||
String sim = tm.getSimCountryIso();
|
||||
String network = tm.getNetworkCountryIso();
|
||||
Log.i("Country SIM=" + sim + " network=" + network);
|
||||
if (sim != null && network != null &&
|
||||
RLAH_COUNTRY_CODES.contains(sim) &&
|
||||
RLAH_COUNTRY_CODES.contains(network))
|
||||
state.roaming = false;
|
||||
}
|
||||
} catch (Throwable ex) {
|
||||
Log.w(ex);
|
||||
}
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
private static Boolean isMetered(Context context) {
|
||||
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
|
||||
NetworkInfo ani = cm.getActiveNetworkInfo();
|
||||
if (ani == null || !ani.isConnected())
|
||||
return null;
|
||||
return cm.isActiveNetworkMetered();
|
||||
}
|
||||
|
||||
Network active = cm.getActiveNetwork();
|
||||
if (active == null) {
|
||||
Log.i("isMetered: no active network");
|
||||
return null;
|
||||
}
|
||||
|
||||
NetworkCapabilities caps = cm.getNetworkCapabilities(active);
|
||||
if (caps == null) {
|
||||
Log.i("isMetered: active no caps");
|
||||
return null; // network unknown
|
||||
}
|
||||
|
||||
Log.i("isMetered: active caps=" + caps);
|
||||
|
||||
if (caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN) &&
|
||||
!caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) {
|
||||
Log.i("isMetered: no internet");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)) {
|
||||
Log.i("isMetered: active restricted");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P &&
|
||||
!caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_FOREGROUND)) {
|
||||
Log.i("isMetered: active background");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)) {
|
||||
boolean unmetered = caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
|
||||
Log.i("isMetered: active not VPN unmetered=" + unmetered);
|
||||
return !unmetered;
|
||||
}
|
||||
|
||||
// VPN: evaluate underlying networks
|
||||
|
||||
boolean underlying = false;
|
||||
Network[] networks = cm.getAllNetworks();
|
||||
if (networks != null)
|
||||
for (Network network : networks) {
|
||||
caps = cm.getNetworkCapabilities(network);
|
||||
if (caps == null) {
|
||||
Log.i("isMetered: no underlying caps");
|
||||
continue; // network unknown
|
||||
}
|
||||
|
||||
Log.i("isMetered: underlying caps=" + caps);
|
||||
|
||||
if (!caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) {
|
||||
Log.i("isMetered: underlying no internet");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)) {
|
||||
Log.i("isMetered: underlying restricted");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P &&
|
||||
!caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_FOREGROUND)) {
|
||||
Log.i("isMetered: underlying background");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)) {
|
||||
underlying = true;
|
||||
Log.i("isMetered: underlying is connected");
|
||||
|
||||
if (caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED)) {
|
||||
Log.i("isMetered: underlying is unmetered");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!underlying) {
|
||||
Log.i("isMetered: no underlying network");
|
||||
return null;
|
||||
}
|
||||
|
||||
// Assume metered
|
||||
Log.i("isMetered: underlying assume metered");
|
||||
return true;
|
||||
}
|
||||
|
||||
static void connect(Context context, IMAPStore istore, EntityAccount account)
|
||||
throws MessagingException, AuthenticatorException, OperationCanceledException, IOException {
|
||||
try {
|
||||
istore.connect(account.host, account.port, account.user, account.password);
|
||||
} catch (AuthenticationFailedException ex) {
|
||||
if (account.auth_type == AUTH_TYPE_GMAIL) {
|
||||
account.password = refreshToken(context, "com.google", account.user, account.password);
|
||||
DB.getInstance(context).account().setAccountPassword(account.id, account.password);
|
||||
istore.connect(account.host, account.port, account.user, account.password);
|
||||
} else
|
||||
throw ex;
|
||||
}
|
||||
|
||||
// https://www.ietf.org/rfc/rfc2971.txt
|
||||
if (istore.hasCapability("ID"))
|
||||
try {
|
||||
Map<String, String> id = new LinkedHashMap<>();
|
||||
id.put("name", context.getString(R.string.app_name));
|
||||
id.put("version", BuildConfig.VERSION_NAME);
|
||||
Map<String, String> sid = istore.id(id);
|
||||
if (sid != null) {
|
||||
Map<String, String> crumb = new HashMap<>();
|
||||
for (String key : sid.keySet()) {
|
||||
crumb.put(key, sid.get(key));
|
||||
Log.i("Server " + key + "=" + sid.get(key));
|
||||
}
|
||||
Bugsnag.leaveBreadcrumb("server", BreadcrumbType.LOG, crumb);
|
||||
}
|
||||
} catch (MessagingException ex) {
|
||||
Log.w(ex);
|
||||
}
|
||||
}
|
||||
|
||||
static String refreshToken(Context context, String type, String name, String current)
|
||||
throws AuthenticatorException, OperationCanceledException, IOException {
|
||||
AccountManager am = AccountManager.get(context);
|
||||
Account[] accounts = am.getAccountsByType(type);
|
||||
for (Account account : accounts)
|
||||
if (name.equals(account.name)) {
|
||||
Log.i("Refreshing token");
|
||||
am.invalidateAuthToken(type, current);
|
||||
String refreshed = am.blockingGetAuthToken(account, getAuthTokenType(type), true);
|
||||
if (refreshed == null)
|
||||
throw new OperationCanceledException("no token");
|
||||
Log.i("Refreshed token");
|
||||
return refreshed;
|
||||
}
|
||||
return current;
|
||||
}
|
||||
|
||||
static String getAuthTokenType(String type) {
|
||||
if ("com.google".equals(type))
|
||||
return "oauth2:https://mail.google.com/";
|
||||
return null;
|
||||
}
|
||||
|
||||
static boolean isPlayStoreInstall(Context context) {
|
||||
return BuildConfig.PLAY_STORE_RELEASE;
|
||||
}
|
||||
|
|
|
@ -190,7 +190,7 @@ public class MessageHelper {
|
|||
|
||||
// https://javaee.github.io/javamail/OAuth2
|
||||
Log.i("Auth type=" + auth_type);
|
||||
if (auth_type == Helper.AUTH_TYPE_GMAIL) {
|
||||
if (auth_type == ConnectionHelper.AUTH_TYPE_GMAIL) {
|
||||
props.put("mail.imaps.auth.mechanisms", "XOAUTH2");
|
||||
props.put("mail.imap.auth.mechanisms", "XOAUTH2");
|
||||
props.put("mail.smtps.auth.mechanisms", "XOAUTH2");
|
||||
|
|
|
@ -161,7 +161,7 @@ public class ServiceSend extends LifecycleService {
|
|||
}
|
||||
|
||||
private void check() {
|
||||
if (!Helper.getNetworkState(ServiceSend.this).isSuitable())
|
||||
if (!ConnectionHelper.getNetworkState(ServiceSend.this).isSuitable())
|
||||
return;
|
||||
|
||||
if (thread != null && thread.isAlive())
|
||||
|
@ -230,7 +230,7 @@ public class ServiceSend extends LifecycleService {
|
|||
Log.i(outbox.name + " end op=" + op.id + "/" + op.name);
|
||||
}
|
||||
|
||||
if (!Helper.getNetworkState(ServiceSend.this).isSuitable())
|
||||
if (!ConnectionHelper.getNetworkState(ServiceSend.this).isSuitable())
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -326,9 +326,9 @@ public class ServiceSend extends LifecycleService {
|
|||
try {
|
||||
itransport.connect(ident.host, ident.port, ident.user, ident.password);
|
||||
} catch (AuthenticationFailedException ex) {
|
||||
if (ident.auth_type == Helper.AUTH_TYPE_GMAIL) {
|
||||
if (ident.auth_type == ConnectionHelper.AUTH_TYPE_GMAIL) {
|
||||
EntityAccount account = db.account().getAccount(ident.account);
|
||||
ident.password = Helper.refreshToken(this, "com.google", ident.user, account.password);
|
||||
ident.password = ConnectionHelper.refreshToken(this, "com.google", ident.user, account.password);
|
||||
DB.getInstance(this).identity().setIdentityPassword(ident.id, ident.password);
|
||||
itransport.connect(ident.host, ident.port, ident.user, ident.password);
|
||||
} else
|
||||
|
|
|
@ -89,7 +89,7 @@ import me.leolin.shortcutbadger.ShortcutBadger;
|
|||
import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
|
||||
|
||||
public class ServiceSynchronize extends LifecycleService {
|
||||
private Helper.NetworkState networkState = new Helper.NetworkState();
|
||||
private ConnectionHelper.NetworkState networkState = new ConnectionHelper.NetworkState();
|
||||
private Core.State state;
|
||||
private boolean oneshot = false;
|
||||
private boolean started = false;
|
||||
|
@ -641,7 +641,7 @@ public class ServiceSynchronize extends LifecycleService {
|
|||
db.account().setAccountState(account.id, "connecting");
|
||||
|
||||
try {
|
||||
Helper.connect(this, istore, account);
|
||||
ConnectionHelper.connect(this, istore, account);
|
||||
} catch (Throwable ex) {
|
||||
// Report account connection error
|
||||
if (account.last_connected != null) {
|
||||
|
@ -1160,7 +1160,7 @@ public class ServiceSynchronize extends LifecycleService {
|
|||
ConnectivityManager.NetworkCallback networkCallback = new ConnectivityManager.NetworkCallback() {
|
||||
@Override
|
||||
public void onAvailable(Network network) {
|
||||
networkState.update(Helper.getNetworkState(ServiceSynchronize.this));
|
||||
networkState.update(ConnectionHelper.getNetworkState(ServiceSynchronize.this));
|
||||
|
||||
synchronized (ServiceSynchronize.this) {
|
||||
try {
|
||||
|
@ -1215,7 +1215,7 @@ public class ServiceSynchronize extends LifecycleService {
|
|||
|
||||
@Override
|
||||
public void onCapabilitiesChanged(Network network, NetworkCapabilities capabilities) {
|
||||
networkState.update(Helper.getNetworkState(ServiceSynchronize.this));
|
||||
networkState.update(ConnectionHelper.getNetworkState(ServiceSynchronize.this));
|
||||
|
||||
synchronized (ServiceSynchronize.this) {
|
||||
try {
|
||||
|
@ -1232,7 +1232,7 @@ public class ServiceSynchronize extends LifecycleService {
|
|||
|
||||
@Override
|
||||
public void onLost(Network network) {
|
||||
networkState.update(Helper.getNetworkState(ServiceSynchronize.this));
|
||||
networkState.update(ConnectionHelper.getNetworkState(ServiceSynchronize.this));
|
||||
|
||||
synchronized (ServiceSynchronize.this) {
|
||||
try {
|
||||
|
|
Loading…
Reference in New Issue