diff --git a/app/src/main/java/com/eveningoutpost/dexdrip/cgm/carelinkfollow/CareLinkFollowDownloader.java b/app/src/main/java/com/eveningoutpost/dexdrip/cgm/carelinkfollow/CareLinkFollowDownloader.java index d3f49ec..06db587 100644 --- a/app/src/main/java/com/eveningoutpost/dexdrip/cgm/carelinkfollow/CareLinkFollowDownloader.java +++ b/app/src/main/java/com/eveningoutpost/dexdrip/cgm/carelinkfollow/CareLinkFollowDownloader.java @@ -6,9 +6,12 @@ import com.eveningoutpost.dexdrip.Models.UserError; import com.eveningoutpost.dexdrip.UtilityModels.CollectionServiceStarter; import com.eveningoutpost.dexdrip.UtilityModels.Inevitable; +import com.eveningoutpost.dexdrip.cgm.carelinkfollow.auth.CareLinkAuthenticator; +import com.eveningoutpost.dexdrip.cgm.carelinkfollow.auth.CareLinkCredentialStore; import com.eveningoutpost.dexdrip.cgm.carelinkfollow.client.*; import com.eveningoutpost.dexdrip.cgm.carelinkfollow.message.RecentData; +import static com.eveningoutpost.dexdrip.Models.JoH.dateText; import static com.eveningoutpost.dexdrip.Models.JoH.emptyString; public class CareLinkFollowDownloader { @@ -31,11 +34,12 @@ public class CareLinkFollowDownloader { private int lastResponseCode = 0; - public String getStatus(){ + public String getStatus() { return status; } - public int getLastResponseCode(){ - return lastResponseCode; + + public int getLastResponseCode() { + return lastResponseCode; } CareLinkFollowDownloader(String carelinkUsername, String carelinkPassword, String carelinkCountry, String carelinkPatient) { @@ -44,6 +48,7 @@ public int getLastResponseCode(){ this.carelinkCountry = carelinkCountry; this.carelinkPatient = carelinkPatient; loginDataLooksOkay = !emptyString(carelinkUsername) && !emptyString(carelinkPassword) && carelinkCountry != null && !emptyString(carelinkCountry); + loginDataLooksOkay = true; } public static void resetInstance() { @@ -51,26 +56,49 @@ public static void resetInstance() { CollectionServiceStarter.restartCollectionServiceBackground(); } - public boolean doEverything( ) { + public boolean doEverything() { msg("Start download"); - if (D) UserError.Log.e(TAG, "doEverything called"); - if (loginDataLooksOkay) { + //if (loginDataLooksOkay) { + if (CareLinkCredentialStore.getInstance().getAuthStatus() == CareLinkCredentialStore.AUTHENTICATED) { + if (CareLinkCredentialStore.getInstance().getExpiresIn() < 6 * 60_000) { + UserError.Log.e(TAG, "Token is about to expire, trying to renew it."); try { - if (getCareLinkClient() != null) { - extendWakeLock(30_000); - backgroundProcessConnectData(); - } else { - UserError.Log.d(TAG, "Cannot get data as CareLinkClient is null"); + if (!(new CareLinkAuthenticator(CareLinkCredentialStore.getInstance().getCredential().country, CareLinkCredentialStore.getInstance()).refreshToken())) { + UserError.Log.e(TAG, "Error renewing token!"); return false; + } else { + UserError.Log.e(TAG, "Token renewed!"); } - return true; } catch (Exception e) { - UserError.Log.e(TAG, "Got exception in getData() " + e); - releaseWakeLock(); + UserError.Log.e(TAG, "Exception in renewing token! " + e.getMessage()); + return false; + } + } + try { + if (getCareLinkClient() != null) { + extendWakeLock(30_000); + backgroundProcessConnectData(); + } else { + UserError.Log.d(TAG, "Cannot get data as CareLinkClient is null"); return false; } - } else { + return true; + } catch (Exception e) { + UserError.Log.e(TAG, "Got exception in getData() " + e); + releaseWakeLock(); + return false; + } + } else { + if (CareLinkCredentialStore.getInstance().getAuthStatus() == CareLinkCredentialStore.NOT_AUTHENTICATED) { + UserError.Log.e(TAG, "Not authenticated! Please login!"); + msg("Not authenticated!"); + } + if (CareLinkCredentialStore.getInstance().getAuthStatus() == CareLinkCredentialStore.TOKEN_EXPIRED) { + UserError.Log.e(TAG, "Token expired!"); + msg("Token expired!"); + } + /* final String invalid = "Invalid CareLink login data!"; msg(invalid); UserError.Log.e(TAG, invalid); @@ -85,6 +113,7 @@ public boolean doEverything( ) { }else if(!CountryUtils.isSupportedCountry(carelinkCountry)){ UserError.Log.e(TAG, "CareLink Country not supported!"); } + */ return false; } @@ -116,11 +145,11 @@ private void processCareLinkData() { if (carelinkClient != null) { //Try twice in case of 401 error - for(int i = 0; i < 2; i++) { + for (int i = 0; i < 2; i++) { //Get data try { - if(JoH.emptyString(this.carelinkPatient)) + if (JoH.emptyString(this.carelinkPatient)) recentData = getCareLinkClient().getRecentData(); else recentData = getCareLinkClient().getRecentData(this.carelinkPatient); @@ -134,18 +163,18 @@ private void processCareLinkData() { UserError.Log.d(TAG, "Success get data!"); //Process data try { - if (D) UserError.Log.d(TAG, "Start process data"); - //Process CareLink data (conversion and update xDrip data) - CareLinkDataProcessor.processRecentData(recentData, true); - if (D) UserError.Log.d(TAG, "ProcessData finished!"); - //Update Service status - CareLinkFollowService.updateBgReceiveDelay(); - msg(null); + if (D) UserError.Log.d(TAG, "Start process data"); + //Process CareLink data (conversion and update xDrip data) + CareLinkDataProcessor.processRecentData(recentData, true); + if (D) UserError.Log.d(TAG, "ProcessData finished!"); + //Update Service status + CareLinkFollowService.updateBgReceiveDelay(); + msg(null); } catch (Exception e) { UserError.Log.e(TAG, "Exception in data processing: " + e); msg("Data processing error!"); } - //Data receive error + //Data receive error } else { //first 401 error => TRY AGAIN, only debug log if (carelinkClient.getLastResponseCode() == 401 && i == 0) { @@ -155,7 +184,7 @@ private void processCareLinkData() { UserError.Log.e(TAG, "CareLink login error! Response code: " + carelinkClient.getLastResponseCode()); msg("Login error!"); //login error - } else if (!getCareLinkClient().getLastLoginSuccess()){ + } else if (!getCareLinkClient().getLastLoginSuccess()) { UserError.Log.e(TAG, "CareLink login error! Response code: " + carelinkClient.getLastResponseCode()); UserError.Log.e(TAG, "Error message: " + getCareLinkClient().getLastErrorMessage()); msg("Login error!"); @@ -168,7 +197,7 @@ private void processCareLinkData() { } //Next try only for 401 error and first attempt - if(!(carelinkClient.getLastResponseCode() == 401 && i == 0)) + if (!(carelinkClient.getLastResponseCode() == 401 && i == 0)) break; } @@ -182,7 +211,9 @@ private CareLinkClient getCareLinkClient() { if (carelinkClient == null) { try { UserError.Log.d(TAG, "Creating CareLinkClient"); - carelinkClient = new CareLinkClient(carelinkUsername, carelinkPassword, carelinkCountry); + if (CareLinkCredentialStore.getInstance().getAuthStatus() == CareLinkCredentialStore.AUTHENTICATED) + carelinkClient = new CareLinkClient(CareLinkCredentialStore.getInstance()); + //carelinkClient = new CareLinkClient(carelinkUsername, carelinkPassword, carelinkCountry); } catch (Exception e) { UserError.Log.e(TAG, "Error creating CareLinkClient"); } @@ -193,12 +224,12 @@ private CareLinkClient getCareLinkClient() { private static synchronized void extendWakeLock(final long ms) { if (wl == null) { - if (D) UserError.Log.d(TAG,"Creating wakelock"); + if (D) UserError.Log.d(TAG, "Creating wakelock"); wl = JoH.getWakeLock("CareLinkFollow-download", (int) ms); } else { JoH.releaseWakeLock(wl); // lets not get too messy wl.acquire(ms); - if (D) UserError.Log.d(TAG,"Extending wakelock"); + if (D) UserError.Log.d(TAG, "Extending wakelock"); } } diff --git a/app/src/main/java/com/eveningoutpost/dexdrip/cgm/carelinkfollow/CareLinkFollowService.java b/app/src/main/java/com/eveningoutpost/dexdrip/cgm/carelinkfollow/CareLinkFollowService.java index a354884..c5d8f84 100644 --- a/app/src/main/java/com/eveningoutpost/dexdrip/cgm/carelinkfollow/CareLinkFollowService.java +++ b/app/src/main/java/com/eveningoutpost/dexdrip/cgm/carelinkfollow/CareLinkFollowService.java @@ -13,6 +13,7 @@ import com.eveningoutpost.dexdrip.UtilityModels.Inevitable; import com.eveningoutpost.dexdrip.UtilityModels.Pref; import com.eveningoutpost.dexdrip.UtilityModels.StatusItem; +import com.eveningoutpost.dexdrip.cgm.carelinkfollow.auth.CareLinkCredentialStore; import com.eveningoutpost.dexdrip.utils.DexCollectionType; import com.eveningoutpost.dexdrip.utils.framework.BuggySamsung; import com.eveningoutpost.dexdrip.utils.framework.ForegroundService; @@ -96,11 +97,10 @@ private static long getGraceMillis() { private static long getIntervalMillis() { //return Constants.SECOND_IN_MS * WAKE_UP_GRACE_SECOND; - if(pollInterval == 0) { - if(DEBUG_TIMING)UserError.Log.d(TAG, "POLL INTERVAL IS 0 !!!"); + if (pollInterval == 0) { + if (DEBUG_TIMING) UserError.Log.d(TAG, "POLL INTERVAL IS 0 !!!"); return SAMPLE_PERIOD; - } - else + } else return Constants.MINUTE_IN_MS * pollInterval; } @@ -121,34 +121,35 @@ public static long anticipateNextWakeUp(long now, final long last, final long pe long next; long expectedLast; - if(DEBUG_TIMING)UserError.Log.d(TAG, "Now: " + JoH.dateTimeText(now)); - if(DEBUG_TIMING)UserError.Log.d(TAG, "Last: " + JoH.dateTimeText(last)); - if(DEBUG_TIMING)UserError.Log.d(TAG, "Period: " + String.valueOf(period)); - if(DEBUG_TIMING)UserError.Log.d(TAG, "Interval: " + String.valueOf(interval)); - if(DEBUG_TIMING)UserError.Log.d(TAG, "Grace: " + String.valueOf(grace)); + if (DEBUG_TIMING) UserError.Log.d(TAG, "Now: " + JoH.dateTimeText(now)); + if (DEBUG_TIMING) UserError.Log.d(TAG, "Last: " + JoH.dateTimeText(last)); + if (DEBUG_TIMING) UserError.Log.d(TAG, "Period: " + String.valueOf(period)); + if (DEBUG_TIMING) UserError.Log.d(TAG, "Interval: " + String.valueOf(interval)); + if (DEBUG_TIMING) UserError.Log.d(TAG, "Grace: " + String.valueOf(grace)); //recent reading (less then data period) => last + period + grace - if((now - last) < period) { + if ((now - last) < period) { next = last + period + grace; - if(DEBUG_TIMING)UserError.Log.d(TAG, "Recent reading case, next wakeup: " + JoH.dateTimeText(next)); + if (DEBUG_TIMING) + UserError.Log.d(TAG, "Recent reading case, next wakeup: " + JoH.dateTimeText(next)); } //old reading => anticipated next + grace - else{ + else { //last expected - if(DEBUG_TIMING)UserError.Log.d(TAG, "Old reading."); + if (DEBUG_TIMING) UserError.Log.d(TAG, "Old reading."); next = now + ((last - now) % period); - if(DEBUG_TIMING)UserError.Log.d(TAG, "Last expected: " + JoH.dateTimeText(next)); + if (DEBUG_TIMING) UserError.Log.d(TAG, "Last expected: " + JoH.dateTimeText(next)); //add poll interval until next time is reached - while(next < now){ + while (next < now) { next += interval; } - if(DEBUG_TIMING)UserError.Log.d(TAG, "Next poll: " + JoH.dateTimeText(next)); + if (DEBUG_TIMING) UserError.Log.d(TAG, "Next poll: " + JoH.dateTimeText(next)); //add grace next += grace; - if(DEBUG_TIMING)UserError.Log.d(TAG, "Next poll + grace: " + JoH.dateTimeText(next)); + if (DEBUG_TIMING) UserError.Log.d(TAG, "Next poll + grace: " + JoH.dateTimeText(next)); } - return next; + return next; } @@ -173,9 +174,9 @@ public int onStartCommand(Intent intent, int flags, int startId) { last_wakeup = JoH.tsl(); // Check current - if(gracePeriod == 0) + if (gracePeriod == 0) gracePeriod = Pref.getStringToInt("clfollow_grace_period", 30); - if(pollInterval == 0) + if (pollInterval == 0) pollInterval = Pref.getStringToInt("clfollow_poll_interval", 5); lastBg = BgReading.lastNoSenssor(); if (lastBg != null) { @@ -195,7 +196,7 @@ public int onStartCommand(Intent intent, int flags, int startId) { if (JoH.ratelimit("last-carelink-follow-poll", 5)) { Inevitable.task("CareLink-Follow-Work", 200, () -> { try { - downloader.doEverything( ); + downloader.doEverything(); } catch (NullPointerException e) { UserError.Log.e(TAG, "Caught concurrency exception when trying to run doeverything"); } @@ -247,8 +248,36 @@ public static List megaStatus() { // Last response code int lastResponseCode = downloader != null ? downloader.getLastResponseCode() : 0; - List megaStatus = new ArrayList<>(); + StatusItem.Highlight authHighlight = StatusItem.Highlight.NORMAL; + if (lastBg != null) { + long age = JoH.msSince(lastBg.timestamp); + ageLastBg = JoH.niceTimeScalar(age); + if (age > SAMPLE_PERIOD + hightlightGrace) { + bgAgeHighlight = StatusItem.Highlight.BAD; + } + } + String authStatus = null; + switch (CareLinkCredentialStore.getInstance().getAuthStatus()) { + case CareLinkCredentialStore.NOT_AUTHENTICATED: + authStatus = "NOT AUTHENTICATED"; + authHighlight = StatusItem.Highlight.CRITICAL; + break; + case CareLinkCredentialStore.AUTHENTICATED: + authHighlight = StatusItem.Highlight.GOOD; + authStatus = "AUTHENTICATED"; + break; + case CareLinkCredentialStore.TOKEN_EXPIRED: + authHighlight = StatusItem.Highlight.BAD; + authStatus = "TOKEN EXPIRED"; + break; + } + + //Create status screeen + List megaStatus = new ArrayList<>(); + megaStatus.add(new StatusItem("Authentication status", authStatus, authHighlight)); + megaStatus.add(new StatusItem("Token expires in", JoH.niceTimeScalar(CareLinkCredentialStore.getInstance().getExpiresIn()))); + megaStatus.add(new StatusItem()); megaStatus.add(new StatusItem("Latest BG", ageLastBg + (lastBg != null ? " ago" : ""), bgAgeHighlight)); megaStatus.add(new StatusItem("BG receive delay", ageOfBgLastPoll, ageOfLastBgPollHighlight)); megaStatus.add(new StatusItem("Data period:", JoH.niceTimeScalar(SAMPLE_PERIOD))); @@ -261,7 +290,7 @@ public static List megaStatus() { if (lastBg != null) { megaStatus.add(new StatusItem("Last BG time", JoH.dateTimeText(lastBg.timestamp))); } - megaStatus.add(new StatusItem("Last poll time", lastPoll > 0 ? JoH.dateTimeText(lastPoll) : "n/a")); + megaStatus.add(new StatusItem("Last poll time", lastPoll > 0 ? JoH.dateTimeText(lastPoll) : "n/a")); megaStatus.add(new StatusItem("Next poll time", JoH.dateTimeText(wakeup_time))); megaStatus.add(new StatusItem()); megaStatus.add(new StatusItem("Last response code", lastResponseCode)); diff --git a/app/src/main/java/com/eveningoutpost/dexdrip/cgm/carelinkfollow/auth/CareLinkAuthenticator.java b/app/src/main/java/com/eveningoutpost/dexdrip/cgm/carelinkfollow/auth/CareLinkAuthenticator.java new file mode 100644 index 0000000..bb1e584 --- /dev/null +++ b/app/src/main/java/com/eveningoutpost/dexdrip/cgm/carelinkfollow/auth/CareLinkAuthenticator.java @@ -0,0 +1,311 @@ +package com.eveningoutpost.dexdrip.cgm.carelinkfollow.auth; + +import android.app.Activity; +import android.app.Dialog; +import android.content.DialogInterface; +import android.graphics.Bitmap; +import android.os.Handler; +import android.os.Looper; +import android.support.v7.widget.LinearLayoutCompat; +import android.view.ViewGroup; +import android.webkit.CookieManager; +import android.webkit.WebView; +import android.webkit.WebViewClient; + +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.Locale; +import java.util.concurrent.Semaphore; + +import okhttp3.Cookie; +import okhttp3.HttpUrl; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; + +public class CareLinkAuthenticator { + + protected static final String CARELINK_CONNECT_SERVER_EU = "carelink.minimed.eu"; + protected static final String CARELINK_CONNECT_SERVER_US = "carelink.minimed.com"; + protected static final String CARELINK_LANGUAGE_EN = "en"; + protected static final String CARELINK_AUTH_TOKEN_COOKIE_NAME = "auth_tmp_token"; + protected static final String CARELINK_TOKEN_VALIDTO_COOKIE_NAME = "c_token_valid_to"; + + protected static final SimpleDateFormat[] VALIDTO_DATE_FORMATS = { + new SimpleDateFormat("EEE MMM d HH:mm:ss zzz yyyy", Locale.ENGLISH), + new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.ENGLISH), + new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssz", Locale.ENGLISH), + new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ", Locale.ENGLISH), + new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.ENGLISH), + //new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX", Locale.ENGLISH), + //new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSX", Locale.ENGLISH), + new SimpleDateFormat("EEE MMM d HH:mm:ss zzz yyyy"), + new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ"), + new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssz"), + new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ"), + new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"), + //new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX"), + //new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSX") + }; + + + private final Semaphore available = new Semaphore(0, true); + private String carelinkCountry; + private CareLinkCredentialStore credentialStore; + + + public CareLinkAuthenticator(String carelinkCountry, CareLinkCredentialStore credentialStore) { + this.carelinkCountry = carelinkCountry; + this.credentialStore = credentialStore; + } + + /* + public synchronized CareLinkCredential getCreditential() throws InterruptedException { + if(Looper.myLooper() == Looper.getMainLooper()) + throw new RuntimeException("don't call getAccessToken() from the main thread."); + + switch (credentialStore.getAuthStatus()) { + case CareLinkCredentialStore.NOT_AUTHENTICATED: + authenticate(); + available.acquire(); + break; + case CareLinkCredentialStore.TOKEN_EXPIRED: + refreshToken(); + available.acquire(); + break; + case CareLinkCredentialStore.AUTHENTICATED: + break; + } + + return credentialStore.getCredential(); + } + */ + + public boolean authenticate(Activity context) throws InterruptedException { + if (Looper.myLooper() == Looper.getMainLooper()) + throw new RuntimeException("don't call authenticate() from the main thread."); + + Handler handler = new Handler(Looper.getMainLooper()); + handler.post(new Runnable() { + @Override + public void run() { + showDialog(context); + } + }); + available.acquire(); + return (credentialStore.getAuthStatus() == CareLinkCredentialStore.AUTHENTICATED); + } + + public boolean refreshToken() { + //If not authenticated => unable to refresh + if (credentialStore.getAuthStatus() == CareLinkCredentialStore.NOT_AUTHENTICATED) + return false; + + HttpUrl url = null; + OkHttpClient httpClient = null; + Request.Builder requestBuilder = null; + Response response = null; + EditableCookieJar cookieJar = null; + + + //Build client with cookies from CredentialStore + cookieJar = new EditableCookieJar(); + httpClient = new OkHttpClient.Builder() + .cookieJar(cookieJar) + .build(); + cookieJar.AddCookies(credentialStore.getCredential().cookies); + + //Build request + url = new HttpUrl.Builder() + .scheme("https") + .host(this.careLinkServer()) + .addPathSegments("patient/sso/reauth") + .build(); + requestBuilder = new Request.Builder() + .url(url) + .post(RequestBody.create(null, new byte[0])) + .addHeader("Accept", "application/json, text/plain, */*") + .addHeader("Accept-Language", "en;q=0.9, *;q=0.8") + .addHeader("Connection", "keep-alive") + .addHeader("Authorization", credentialStore.getCredential().getAuthorizationFieldValue()) + .addHeader("User-Agent", "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Mobile Safari/537.36") + .addHeader("Sec-Ch-Ua", "\"Google Chrome\";v=\"117\", \"Not;A=Brand\";v=\"8\", \"Chromium\";v=\"117\""); + + //Send request to refresh token + try { + response = httpClient.newCall(requestBuilder.build()).execute(); + //successful response + if (response.isSuccessful()) { + //New authentication cookies found + if (cookieJar.contains(CARELINK_AUTH_TOKEN_COOKIE_NAME) && cookieJar.contains(CARELINK_TOKEN_VALIDTO_COOKIE_NAME)) { + //Update credentials + this.credentialStore.setCredential( + this.carelinkCountry, + cookieJar.getCookies(CARELINK_AUTH_TOKEN_COOKIE_NAME).get(0).value(), + this.parseValidTo(cookieJar.getCookies(CARELINK_TOKEN_VALIDTO_COOKIE_NAME).get(0).value()), + cookieJar.getAllCookies().toArray(new Cookie[0])); + } else { + return false; + } + + } + //error in response + else { + return false; + } + response.close(); + } catch (IOException e) { + return false; + } finally { + + } + + return (credentialStore.getAuthStatus() == CareLinkCredentialStore.AUTHENTICATED); + + } + + private void showDialog(Activity context) { + + //Create dialog + final Dialog authDialog = new Dialog(context); + LinearLayoutCompat layout = new LinearLayoutCompat(authDialog.getContext()); + WebView webView = new WebView(authDialog.getContext()); + webView.setLayoutParams(new LinearLayoutCompat.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); + layout.addView(webView); + authDialog.setContentView(layout); + + authDialog.setOnDismissListener(new DialogInterface.OnDismissListener() { + @Override + public void onDismiss(DialogInterface dialog) { + unlock(); + } + }); + + //Configure Webview + webView.getSettings().setJavaScriptEnabled(true); + webView.getSettings().setUserAgentString("Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Mobile Safari/537.36"); + HashMap headers = new HashMap<>(); + headers.put("Accept-Language", "en;q=0.9, *;q=0.8"); + headers.put("Sec-Ch-Ua", "\"Google Chrome\";v=\"117\", \"Not;A=Brand\";v=\"8\", \"Chromium\";v=\"117\""); + webView.loadUrl(this.getLoginUrl(), headers); + webView.setWebViewClient(new WebViewClient() { + + @Override + public void onPageStarted(WebView view, String url, Bitmap favicon) { + if (CareLinkAuthenticator.this.extractCookies(url)) + authDialog.dismiss(); + } + + @Override + public void onPageFinished(WebView view, String url) { + if (CareLinkAuthenticator.this.extractCookies(url)) + authDialog.dismiss(); + } + }); + + + //Set dialog display infos and show it + authDialog.setCancelable(true); + /* + authDialog.getWindow().setLayout( + (int) (context.getResources().getDisplayMetrics().widthPixels), + (int) (context.getResources().getDisplayMetrics().heightPixels)); + */ + authDialog.getWindow().setLayout(LinearLayoutCompat.LayoutParams.MATCH_PARENT, LinearLayoutCompat.LayoutParams.MATCH_PARENT); + authDialog.show(); + } + + protected String getLoginUrl() { + + HttpUrl url = null; + + url = new HttpUrl.Builder() + .scheme("https") + .host(this.careLinkServer()) + .addPathSegments("patient/sso/login") + .addQueryParameter("country", this.carelinkCountry) + .addQueryParameter("lang", CARELINK_LANGUAGE_EN) + .build(); + + return url.toString(); + + } + + protected String careLinkServer() { + if (this.carelinkCountry.equals("us")) + return CARELINK_CONNECT_SERVER_US; + else + return CARELINK_CONNECT_SERVER_EU; + } + + protected Boolean extractCookies(String url) { + String cookies = null; + String authToken = null; + String host = null; + Date validToDate = null; + ArrayList cookieList; + + + cookies = CookieManager.getInstance().getCookie(url); + + //Authentication cookies are present + if (cookies != null && cookies.contains(CARELINK_AUTH_TOKEN_COOKIE_NAME) && cookies.contains(CARELINK_TOKEN_VALIDTO_COOKIE_NAME)) { + + //Build cookies + host = HttpUrl.parse(url).host(); + cookieList = new ArrayList(); + + for (String cookie : cookies.split("; ")) { + + String[] cookieParts = cookie.split("="); + + Cookie.Builder cookieBuilder = new Cookie.Builder() + .name(cookieParts[0]) + .value(cookieParts[1]) + .path("/") + .domain(host); + + if (cookieParts[0].contains(CARELINK_AUTH_TOKEN_COOKIE_NAME)) { + cookieBuilder.secure(); + authToken = cookieParts[1]; + } + + if (cookieParts[0].contains(CARELINK_TOKEN_VALIDTO_COOKIE_NAME)) { + validToDate = this.parseValidTo(cookieParts[1]); + cookieBuilder.secure(); + } + + cookieList.add(cookieBuilder.build()); + + } + + //Update credentials + this.credentialStore.setCredential(this.carelinkCountry, authToken, validToDate, cookieList.toArray(new Cookie[0])); + //success + return true; + } else + //error + return false; + } + + protected void unlock() { + if (available.availablePermits() <= 0) + available.release(); + } + + protected Date parseValidTo(String validToDateString) { + for (SimpleDateFormat zonedFormat : VALIDTO_DATE_FORMATS) { + try { + return zonedFormat.parse(validToDateString); + } catch (Exception ex) { + } + } + return null; + } + + +} diff --git a/app/src/main/java/com/eveningoutpost/dexdrip/cgm/carelinkfollow/auth/CareLinkCredential.java b/app/src/main/java/com/eveningoutpost/dexdrip/cgm/carelinkfollow/auth/CareLinkCredential.java new file mode 100644 index 0000000..639b3f9 --- /dev/null +++ b/app/src/main/java/com/eveningoutpost/dexdrip/cgm/carelinkfollow/auth/CareLinkCredential.java @@ -0,0 +1,34 @@ +package com.eveningoutpost.dexdrip.cgm.carelinkfollow.auth; + +import java.util.Calendar; +import java.util.Date; + +import okhttp3.Cookie; + +public class CareLinkCredential { + + public String country = null; + public String accessToken = null; + public Cookie[] cookies = null; + public Date tokenValidTo = null; + + public String getToken() { + return accessToken; + } + + public String getAuthorizationFieldValue() { + if (this.getToken() == null) + return null; + else + return "Bearer " + this.getToken(); + } + + public long getExpiresIn() { + if (this.tokenValidTo == null) + return -1; + else + return this.tokenValidTo.getTime() - Calendar.getInstance().getTime().getTime(); + } + +} + diff --git a/app/src/main/java/com/eveningoutpost/dexdrip/cgm/carelinkfollow/auth/CareLinkCredentialStore.java b/app/src/main/java/com/eveningoutpost/dexdrip/cgm/carelinkfollow/auth/CareLinkCredentialStore.java new file mode 100644 index 0000000..f37f56b --- /dev/null +++ b/app/src/main/java/com/eveningoutpost/dexdrip/cgm/carelinkfollow/auth/CareLinkCredentialStore.java @@ -0,0 +1,107 @@ +package com.eveningoutpost.dexdrip.cgm.carelinkfollow.auth; + + +import com.eveningoutpost.dexdrip.Models.UserError; +import com.eveningoutpost.dexdrip.UtilityModels.PersistentStore; +import com.google.gson.GsonBuilder; + +import java.util.Calendar; +import java.util.Date; + +import okhttp3.Cookie; + +public class CareLinkCredentialStore { + + private static final String TAG = "CareLinkCredentialStore"; + + public final static int NOT_AUTHENTICATED = 0; + public final static int TOKEN_EXPIRED = 1; + public final static int AUTHENTICATED = 2; + + private CareLinkCredential credential = null; + private static CareLinkCredentialStore instance = null; + private final static String PREF_CARELINK_CREDENTIAL = "carelink_credential"; + + private int authStatus = NOT_AUTHENTICATED; + + + private CareLinkCredentialStore() { + + } + + public static CareLinkCredentialStore getInstance() { + if (instance == null) { + instance = new CareLinkCredentialStore(); + UserError.Log.d(TAG, "Trying to restore saved Credential"); + String credJson = PersistentStore.getString(PREF_CARELINK_CREDENTIAL, ""); + if (!credJson.equals("")) { + try { + CareLinkCredential savedCred = new GsonBuilder().create().fromJson(credJson, CareLinkCredential.class); + instance.setCredential(savedCred.country, savedCred.accessToken, savedCred.tokenValidTo, savedCred.cookies, false); + } catch (Exception e) { + UserError.Log.d(TAG, "Error when restoring saved Credential: " + e.getMessage()); + } + } else { + UserError.Log.d(TAG, "No saved Credential found!"); + } + + } + return instance; + } + + synchronized void setCredential(String country, String accessToken, Date tokenValidTo, Cookie[] cookies) { + this.setCredential(country, accessToken, tokenValidTo, cookies, true); + } + + protected synchronized void setCredential(String country, String accessToken, Date tokenValidTo, Cookie[] cookies, boolean save) { + credential = new CareLinkCredential(); + credential.country = country; + credential.accessToken = accessToken; + credential.cookies = cookies; + credential.tokenValidTo = tokenValidTo; + if (credential.accessToken == null || credential.tokenValidTo == null) + authStatus = NOT_AUTHENTICATED; + else + evaluateExpiration(); + UserError.Log.d(TAG, "Credential updated!"); + if (save) { + try { + PersistentStore.setString(PREF_CARELINK_CREDENTIAL, new GsonBuilder().create().toJson(credential)); + UserError.Log.d(TAG, "Credential saved!"); + } catch (Exception e) { + UserError.Log.d(TAG, "Error saving Credential: " + e.getMessage()); + } + } + } + + public CareLinkCredential getCredential() { + return credential; + } + + public int getAuthStatus() { + return authStatus; + } + + public long getExpiresIn() { + if (credential == null || credential.tokenValidTo == null) + return -1; + else + return credential.tokenValidTo.getTime() - Calendar.getInstance().getTime().getTime(); + } + + synchronized void clear() { + this.credential = null; + PersistentStore.setString(PREF_CARELINK_CREDENTIAL, ""); + UserError.Log.d(TAG, "Credential cleared"); + authStatus = NOT_AUTHENTICATED; + } + + protected void evaluateExpiration() { + if (this.getExpiresIn() < 0) + authStatus = TOKEN_EXPIRED; + else + authStatus = AUTHENTICATED; + } + + +} diff --git a/app/src/main/java/com/eveningoutpost/dexdrip/cgm/carelinkfollow/auth/EditableCookieJar.java b/app/src/main/java/com/eveningoutpost/dexdrip/cgm/carelinkfollow/auth/EditableCookieJar.java new file mode 100644 index 0000000..4905281 --- /dev/null +++ b/app/src/main/java/com/eveningoutpost/dexdrip/cgm/carelinkfollow/auth/EditableCookieJar.java @@ -0,0 +1,103 @@ +package com.eveningoutpost.dexdrip.cgm.carelinkfollow.auth; + + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import okhttp3.Cookie; +import okhttp3.CookieJar; +import okhttp3.HttpUrl; + +public class EditableCookieJar implements CookieJar { + + private List storage = new ArrayList<>(); + + @Override + public void saveFromResponse(HttpUrl url, List cookies) { +// if (cookies != null && cookies.size() > 0) +// storage.addAll(cookies); + for (Cookie cookie : cookies) { + if (this.contains(cookie.name())) + this.deleteCookie(cookie.name()); + this.storage.add(cookie); + } + } + + @Override + public List loadForRequest(HttpUrl url) { + + List cookies = new ArrayList<>(); + + // Remove expired Cookies + //removeExpiredCookies(); + + // Only return matching Cookies + for (Cookie cookie : storage) { + if (cookie.matches(url)) { + cookies.add(cookie); + } + } + + return cookies; + } + + public List getCookies(String name) { + + List cookies = new ArrayList<>(); + + // Remove expired Cookies + //removeExpiredCookies(); + + // Only return matching Cookies + for (Cookie cookie : storage) { + if (cookie.name().equals(name)) { + cookies.add(cookie); + } + } + + return cookies; + + } + + public List getAllCookies() { + return storage; + } + + public void AddCookie(Cookie cookie) { + storage.add(cookie); + } + + public void AddCookies(Cookie[] cookies) { + storage.addAll(Arrays.asList(cookies)); + } + + public boolean contains(String name) { + return (getCookies(name).size() > 0); + } + + public void deleteCookie(String name) { + for (int i = 0; i < storage.size(); i++) { + if (storage.get(i).name().contains(name)) { + storage.remove(i); + } + } + } + + public void deleteAllCookies() { + storage.clear(); + } + + /* + private void removeExpiredCookies() { + for (int i = 0; i < storage.size(); i++) { + if (storage.get(i).expiresAt() < System.currentTimeMillis()) { + storage.remove(i); + } + } + } + + */ + +} + diff --git a/app/src/main/java/com/eveningoutpost/dexdrip/cgm/carelinkfollow/client/CareLinkClient.java b/app/src/main/java/com/eveningoutpost/dexdrip/cgm/carelinkfollow/client/CareLinkClient.java index 9f41b92..111521b 100644 --- a/app/src/main/java/com/eveningoutpost/dexdrip/cgm/carelinkfollow/client/CareLinkClient.java +++ b/app/src/main/java/com/eveningoutpost/dexdrip/cgm/carelinkfollow/client/CareLinkClient.java @@ -1,6 +1,8 @@ package com.eveningoutpost.dexdrip.cgm.carelinkfollow.client; import com.eveningoutpost.dexdrip.Models.JoH; +import com.eveningoutpost.dexdrip.cgm.carelinkfollow.auth.CareLinkCredentialStore; +import com.eveningoutpost.dexdrip.cgm.carelinkfollow.auth.EditableCookieJar; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonObject; @@ -39,8 +41,10 @@ public class CareLinkClient { protected String carelinkUsername; protected String carelinkPassword; protected String carelinkCountry; + protected CareLinkCredentialStore credentialStore; //Session info + protected boolean sessionInfosLoaded = false; protected User sessionUser; public User getSessionUser() { return sessionUser; @@ -77,6 +81,7 @@ public MonitorData getSessionMonitorData() { //Communication info protected OkHttpClient httpClient = null; protected boolean loginInProcess = false; + protected boolean collectingSessionInfos = false; protected int lastResponseCode; public int getLastResponseCode() { return lastResponseCode; @@ -115,6 +120,25 @@ public CareLinkClient(String carelinkUsername, String carelinkPassword, String c .build(); } + public CareLinkClient(CareLinkCredentialStore credentialStore) { + this.carelinkCountry = credentialStore.getCredential().country; + this.credentialStore = credentialStore; + this.createHttpClient(); + } + + private void createHttpClient(){ + + EditableCookieJar cookieJar = null; + + cookieJar = new EditableCookieJar(); + cookieJar.AddCookies(this.credentialStore.getCredential().cookies); + + this.httpClient = new OkHttpClient.Builder() + .cookieJar(cookieJar) + .connectionPool(new ConnectionPool(5, 10, TimeUnit.MINUTES)) + .build(); + } + /* * WRAPPER DATA RETRIEVAL METHODS */ @@ -252,22 +276,8 @@ protected boolean executeLoginProcedure() { consentResponse.close(); // Get required sessions infos - // User - this.sessionUser = this.getMyUser(); - // Profile - this.sessionProfile = this.getMyProfile(); - // Country settings - this.sessionCountrySettings = this.getMyCountrySettings(); - // Recent uploads (only for patients) - if(!this.sessionUser.isCarePartner()) - this.sessionRecentUploads = this.getRecentUploads(30); - this.sessionM2MEnabled = this.getM2MEnabled().value; - // Multi follow + Care Partner => patients - if(this.sessionM2MEnabled && this.sessionUser.isCarePartner()) - this.sessionPatients = this.getM2MPatients(); - // Single follow and/or Patient => monitor data - else - this.sessionMonitorData = this.getMonitorData(); + if(this.getSessionInfos()) + lastLoginSuccess = true; } catch (Exception e) { lastErrorMessage = e.getClass().getSimpleName() + ":" + e.getMessage(); @@ -276,6 +286,7 @@ protected boolean executeLoginProcedure() { loginInProcess = false; } + /* // Set login success if everything was ok: if(this.sessionUser != null && this.sessionProfile != null && this.sessionCountrySettings != null && this.sessionM2MEnabled != null && (((!this.sessionM2MEnabled || !this.sessionUser.isCarePartner()) && this.sessionMonitorData != null) || @@ -285,19 +296,62 @@ protected boolean executeLoginProcedure() { else { this.clearSessionInfos(); } + */ return lastLoginSuccess; } + protected boolean getSessionInfos(){ + + collectingSessionInfos = true; + + try { + this.clearSessionInfos(); + // User + this.sessionUser = this.getMyUser(); + // Profile + this.sessionProfile = this.getMyProfile(); + // Country settings + this.sessionCountrySettings = this.getMyCountrySettings(); + // Recent uploads (only for patients) + if (!this.sessionUser.isCarePartner()) + this.sessionRecentUploads = this.getRecentUploads(30); + this.sessionM2MEnabled = this.getM2MEnabled().value; + // Multi follow + Care Partner => patients + if (this.sessionM2MEnabled && this.sessionUser.isCarePartner()) + this.sessionPatients = this.getM2MPatients(); + // Single follow and/or Patient => monitor data + else + this.sessionMonitorData = this.getMonitorData(); + + if (this.sessionUser != null && this.sessionProfile != null && this.sessionCountrySettings != null && this.sessionM2MEnabled != null && + (((!this.sessionM2MEnabled || !this.sessionUser.isCarePartner()) && this.sessionMonitorData != null) || + (this.sessionM2MEnabled && this.sessionUser.isCarePartner() && this.sessionPatients != null))) + this.sessionInfosLoaded = true; + else { + this.clearSessionInfos(); + } + } catch (Exception ex) { + + } finally { + collectingSessionInfos = false; + } + + + return this.sessionInfosLoaded; + + } + protected void clearSessionInfos() { - ((SimpleOkHttpCookieJar) this.httpClient.cookieJar()).deleteAllCookies(); + //((SimpleOkHttpCookieJar) this.httpClient.cookieJar()).deleteAllCookies(); this.sessionUser = null; this.sessionProfile = null; this.sessionCountrySettings = null; this.sessionMonitorData = null; this.sessionPatients = null; + this.sessionInfosLoaded = false; } protected Response getLoginSession() throws IOException { @@ -381,26 +435,38 @@ protected Response doConsent(Response doLoginResponse) throws IOException { protected String getAuthorizationToken() { - // New token is needed: - // a) no token or about to expire => execute authentication - // b) last response 401 - // c) last login failed after login process completed - if (!((SimpleOkHttpCookieJar) httpClient.cookieJar()).contains(CARELINK_AUTH_TOKEN_COOKIE_NAME) - || !((SimpleOkHttpCookieJar) httpClient.cookieJar()).contains(CARELINK_TOKEN_VALIDTO_COOKIE_NAME) - || !((new Date(Date.parse(((SimpleOkHttpCookieJar) httpClient.cookieJar()) - .getCookies(CARELINK_TOKEN_VALIDTO_COOKIE_NAME).get(0).value()))) - .after(new Date(new Date(System.currentTimeMillis()).getTime() - + AUTH_EXPIRE_DEADLINE_MINUTES * 60000))) - || this.lastResponseCode == 401 - || (!loginInProcess && !this.lastLoginSuccess) - ) { - //execute new login process - if(this.loginInProcess || !this.executeLoginProcedure()) - return null; - } + if(this.credentialStore != null){ + if(!this.sessionInfosLoaded && this.credentialStore.getAuthStatus() == CareLinkCredentialStore.AUTHENTICATED && !this.collectingSessionInfos) + { + this.getSessionInfos(); + } + if(!this.collectingSessionInfos && !this.sessionInfosLoaded) + return null; + else + return this.credentialStore.getCredential().getAuthorizationFieldValue(); + } else { - // there can be only one - return "Bearer" + " " + ((SimpleOkHttpCookieJar) httpClient.cookieJar()).getCookies(CARELINK_AUTH_TOKEN_COOKIE_NAME).get(0).value(); + // New token is needed: + // a) no token or about to expire => execute authentication + // b) last response 401 + // c) last login failed after login process completed + if (!((SimpleOkHttpCookieJar) httpClient.cookieJar()).contains(CARELINK_AUTH_TOKEN_COOKIE_NAME) + || !((SimpleOkHttpCookieJar) httpClient.cookieJar()).contains(CARELINK_TOKEN_VALIDTO_COOKIE_NAME) + || !((new Date(Date.parse(((SimpleOkHttpCookieJar) httpClient.cookieJar()) + .getCookies(CARELINK_TOKEN_VALIDTO_COOKIE_NAME).get(0).value()))) + .after(new Date(new Date(System.currentTimeMillis()).getTime() + + AUTH_EXPIRE_DEADLINE_MINUTES * 60000))) + || this.lastResponseCode == 401 + || (!loginInProcess && !this.lastLoginSuccess) + ) { + //execute new login process + if (this.loginInProcess || !this.executeLoginProcedure()) + return null; + } + + // there can be only one + return "Bearer" + " " + ((SimpleOkHttpCookieJar) httpClient.cookieJar()).getCookies(CARELINK_AUTH_TOKEN_COOKIE_NAME).get(0).value(); + } } @@ -552,10 +618,12 @@ protected String extractResponseData(String respBody, String groupRegex, int gro protected void addHttpHeaders(Request.Builder requestBuilder, RequestType type) { //Add common browser headers - requestBuilder.addHeader("Accept-Language", "en;q=0.9, *;q=0.8").addHeader("Connection", "keep-alive") - .addHeader("Sec-Ch-Ua", "\"Not/A)Brand\";v=\"99\", \"Google Chrome\";v=\"115\", \"Chromium\";v=\"115\"") - .addHeader("User-Agent", - "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36") + requestBuilder + .addHeader("Accept-Language", "en;q=0.9, *;q=0.8") + .addHeader("Connection", "keep-alive") + //.addHeader("Sec-Ch-Ua", "\"Not/A)Brand\";v=\"99\", \"Google Chrome\";v=\"115\", \"Chromium\";v=\"115\"") + .addHeader("Sec-Ch-Ua", "\"Google Chrome\";v=\"117\", \"Not;A=Brand\";v=\"8\", \"Chromium\";v=\"117\"") + .addHeader("User-Agent", "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Mobile Safari/537.36") //.addHeader("Connection", "keep-alive"); ; @@ -609,6 +677,7 @@ protected T getData(HttpUrl url, RequestBody requestBody, Class dataClass // Send request try { + Request request = requestBuilder.build(); response = this.httpClient.newCall(requestBuilder.build()).execute(); this.lastResponseCode = response.code(); if (response.isSuccessful()) { diff --git a/app/src/main/java/com/eveningoutpost/dexdrip/utils/DexCollectionHelper.java b/app/src/main/java/com/eveningoutpost/dexdrip/utils/DexCollectionHelper.java index c920d46..df80bc0 100644 --- a/app/src/main/java/com/eveningoutpost/dexdrip/utils/DexCollectionHelper.java +++ b/app/src/main/java/com/eveningoutpost/dexdrip/utils/DexCollectionHelper.java @@ -129,7 +129,7 @@ public void run() { case Medtrum: bluetoothScanIfNeeded(); break; - + /* case CLFollow: textSettingDialog(activity, "clfollow_country", "CareLink Country", @@ -172,6 +172,8 @@ public void run() { }); break; + */ + // TODO G4 Share Receiver // TODO Parakeet / Wifi ?? diff --git a/app/src/main/java/com/eveningoutpost/dexdrip/utils/Preferences.java b/app/src/main/java/com/eveningoutpost/dexdrip/utils/Preferences.java index 2e71769..2dbd2f8 100644 --- a/app/src/main/java/com/eveningoutpost/dexdrip/utils/Preferences.java +++ b/app/src/main/java/com/eveningoutpost/dexdrip/utils/Preferences.java @@ -87,6 +87,8 @@ import com.eveningoutpost.dexdrip.WidgetUpdateService; import com.eveningoutpost.dexdrip.calibrations.PluggableCalibration; import com.eveningoutpost.dexdrip.cgm.carelinkfollow.CareLinkFollowService; +import com.eveningoutpost.dexdrip.cgm.carelinkfollow.auth.CareLinkAuthenticator; +import com.eveningoutpost.dexdrip.cgm.carelinkfollow.auth.CareLinkCredentialStore; import com.eveningoutpost.dexdrip.cgm.nsfollow.NightscoutFollow; import com.eveningoutpost.dexdrip.cgm.sharefollow.ShareFollowService; import com.eveningoutpost.dexdrip.insulin.inpen.InPenEntry; @@ -183,7 +185,7 @@ public static List getAllPreferences(final PreferenceGroup parent) { results.add(preference); if (preference instanceof PreferenceGroup) { // recurse - results.addAll(getAllPreferences((PreferenceGroup)preference)); + results.addAll(getAllPreferences((PreferenceGroup) preference)); } } return results; @@ -402,28 +404,26 @@ public void onCreate(Bundle savedInstanceState) { try { PreferenceManager.getDefaultSharedPreferences(this).registerOnSharedPreferenceChangeListener(preferenceFragment.lockListener.prefListener); } catch (Exception e) { - Log.e(TAG,"Got exception registering lockListener: "+e+ " "+(preferenceFragment.lockListener == null)); + Log.e(TAG, "Got exception registering lockListener: " + e + " " + (preferenceFragment.lockListener == null)); } mibandStatusReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { - final MiBandService.MIBAND_INTEND_STATES state = MiBandService.MIBAND_INTEND_STATES.valueOf(intent.getStringExtra("state")); - switch (state) { - case UPDATE_PREF_SCREEN: - preferenceFragment.updateMiBandScreen(); - break; - case UPDATE_PREF_DATA: - preferenceFragment.updateMibandPreferencesData(); - break; + final MiBandService.MIBAND_INTEND_STATES state = MiBandService.MIBAND_INTEND_STATES.valueOf(intent.getStringExtra("state")); + switch (state) { + case UPDATE_PREF_SCREEN: + preferenceFragment.updateMiBandScreen(); + break; + case UPDATE_PREF_DATA: + preferenceFragment.updateMibandPreferencesData(); + break; } } }; } - - @Override public boolean onCreateOptionsMenu(Menu menu) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { @@ -433,15 +433,14 @@ public boolean onCreateOptionsMenu(Menu menu) { } public void showSearch(MenuItem item) { - if (JoH.ratelimit("preference-search-button",1)) { + if (JoH.ratelimit("preference-search-button", 1)) { this.preferenceFragment.showSearchFragment(); } } @Override - protected void onResume() - { + protected void onResume() { super.onResume(); PreferenceManager.getDefaultSharedPreferences(this).registerOnSharedPreferenceChangeListener(ActivityRecognizedService.prefListener); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && DexCollectionType.hasBluetooth() && !WholeHouse.isRpi()) { @@ -456,8 +455,7 @@ protected void onResume() } @Override - protected void onPause() - { + protected void onPause() { PreferenceManager.getDefaultSharedPreferences(this).unregisterOnSharedPreferenceChangeListener(ActivityRecognizedService.prefListener); PreferenceManager.getDefaultSharedPreferences(this).unregisterOnSharedPreferenceChangeListener(LeFunEntry.prefListener); PreferenceManager.getDefaultSharedPreferences(this).unregisterOnSharedPreferenceChangeListener(MiBandEntry.prefListener); @@ -509,8 +507,7 @@ public void onBuildHeaders(List
target) { } @Override - public void onNewIntent(Intent intent) - { + public void onNewIntent(Intent intent) { if (intent.getAction() != null) { try { refreshFragments(getIntent() != null ? getIntent().getAction() : null); @@ -633,7 +630,7 @@ public boolean onPreferenceChange(Preference preference, Object value) { preference.setTitle(preference.getTitle().toString().replaceAll(" \\([a-z0-9A-Z]+\\)$", "") + " (" + value.toString() + ")"); if (do_update) { - preference.getEditor().putInt(preference.getKey(), (int)value).apply(); // update prefs now + preference.getEditor().putInt(preference.getKey(), (int) value).apply(); // update prefs now } return true; } @@ -666,7 +663,7 @@ public boolean onPreferenceChange(Preference preference, Object value) { if (preference.getTitle().toString().contains("(")) { do_update = true; } - String title = preference.getTitle().toString().replaceAll(" \\([a-z0-9A-Z.:]+\\)$", ""); + String title = preference.getTitle().toString().replaceAll(" \\([a-z0-9A-Z.:]+\\)$", ""); if (!value.toString().isEmpty()) title = title + " (" + value.toString() + ")"; preference.setTitle(title); @@ -678,7 +675,6 @@ public boolean onPreferenceChange(Preference preference, Object value) { }; - private static String format_carb_ratio(String oldValue, String newValue) { return oldValue.replaceAll(" \\(.*\\)$", "") + " (" + newValue + "g per Unit)"; } @@ -787,7 +783,7 @@ public boolean onPreferenceChange(Preference preference, Object value) { if (preference.getTitle().toString().contains("(")) { do_update = true; } - final int result = ref.interpolate(name, (int)value); + final int result = ref.interpolate(name, (int) value); preference.setTitle(preference.getTitle().toString().replaceAll(" \\([a-z0-9A-Z \\.]+\\)$", "") + " (" + (unitize ? BgGraphBuilder.unitized_string_static_no_interpretation_short(result) : result) + ")"); if (do_update) { @@ -809,8 +805,6 @@ public boolean onPreferenceChange(Preference preference, Object value) { } - - private static void bindPreferenceSummaryToValueAndEnsureNumeric(Preference preference) { preference.setOnPreferenceChangeListener(sBindNumericPreferenceSummaryToValueListener); sBindPreferenceSummaryToValueListener.onPreferenceChange(preference, @@ -871,7 +865,6 @@ private static void setSummary_static(AllPrefsFragment allPrefsFragment, String } - @SuppressLint("ApplySharedPref") @Override public void onCreate(Bundle savedInstanceState) { @@ -917,7 +910,6 @@ public void onCreate(Bundle savedInstanceState) { bindPreferenceTitleAppendToValueUpdateChannel(findPreference("update_channel")); - profile_insulin_sensitivity_default = findPreference("profile_insulin_sensitivity_default"); profile_carb_ratio_default = findPreference("profile_carb_ratio_default"); refreshProfileRatios(); @@ -941,7 +933,7 @@ public boolean onPreferenceChange(Preference preference, Object newValue) { @Override public boolean onPreferenceChange(Preference preference, Object newValue) { prefs.edit().putString("forced_language", (String) newValue).commit(); - update_force_english_title((String)newValue); + update_force_english_title((String) newValue); if (prefs.getBoolean("force_english", false)) { SdcardImportExport.hardReset(); } @@ -1018,7 +1010,7 @@ public boolean onPreferenceChange(Preference preference, Object newValue) { return true; }); - if (!Home.get_engineering_mode()){ + if (!Home.get_engineering_mode()) { PreferenceScreen settings = (PreferenceScreen) findPreference(MiBandEntry.PREF_MIBAND_SETTINGS); settings.removePreference(findPreference("debug_miband4")); } @@ -1113,7 +1105,7 @@ public boolean onPreferenceChange(Preference preference, Object newValue) { final Preference libre2settings = findPreference("xdrip_libre2_advanced_settings"); //DexCollectionType collectionType = DexCollectionType.getType(findPreference("dex_collection_method").) - final ListPreference currentCalibrationPlugin = (ListPreference)findPreference("current_calibration_plugin"); + final ListPreference currentCalibrationPlugin = (ListPreference) findPreference("current_calibration_plugin"); final PreferenceCategory collectionCategory = (PreferenceCategory) findPreference("collection_category"); final Preference shareKey = findPreference("share_key"); @@ -1146,8 +1138,8 @@ public boolean onPreferenceChange(Preference preference, Object newValue) { final Preference tidePoolType = findPreference("tidepool_dev_servers"); tidePoolType.setOnPreferenceChangeListener((preference, newValue) -> { - TidepoolUploader.resetInstance(); - return true; + TidepoolUploader.resetInstance(); + return true; }); final Preference nsFollowDownload = findPreference("nsfollow_download_treatments"); @@ -1193,10 +1185,11 @@ public boolean onPreferenceChange(Preference preference, Object newValue) { //CareLink Follow preferences - final Preference carelinkFollowUser = findPreference("clfollow_user"); - final Preference carelinkFollowPass = findPreference("clfollow_pass"); + //final Preference carelinkFollowUser = findPreference("clfollow_user"); + //final Preference carelinkFollowPass = findPreference("clfollow_pass"); final Preference carelinkFollowCountry = findPreference("clfollow_country"); final Preference carelinkFollowPatient = findPreference("clfollow_patient"); + final Preference carelinkFollowLogin = findPreference("clfollow_login"); final Preference carelinkFollowGracePeriod = findPreference("clfollow_grace_period"); final Preference carelinkFollowPollInterval = findPreference("clfollow_poll_interval"); final Preference carelinkFollowDownloadFingerBGs = findPreference("clfollow_download_finger_bgs"); @@ -1205,10 +1198,11 @@ public boolean onPreferenceChange(Preference preference, Object newValue) { final Preference carelinkFollowDownloadNotifications = findPreference("clfollow_download_notifications"); if (collectionType == DexCollectionType.CLFollow) { //Add CL prefs - collectionCategory.addPreference(carelinkFollowUser); - collectionCategory.addPreference(carelinkFollowPass); + //collectionCategory.addPreference(carelinkFollowUser); + //collectionCategory.addPreference(carelinkFollowPass); collectionCategory.addPreference(carelinkFollowCountry); collectionCategory.addPreference(carelinkFollowPatient); + collectionCategory.addPreference(carelinkFollowLogin); collectionCategory.addPreference(carelinkFollowGracePeriod); collectionCategory.addPreference(carelinkFollowPollInterval); collectionCategory.addPreference(carelinkFollowDownloadFingerBGs); @@ -1224,14 +1218,43 @@ public boolean onPreferenceChange(Preference preference, Object newValue) { return true; } }; + final Preference.OnPreferenceClickListener carelinkLoginListener = new Preference.OnPreferenceClickListener() { + @Override + public boolean onPreferenceClick(Preference preference) { + new Thread() { + public void run() { + try { + String country = Pref.getString("clfollow_country", "").toLowerCase(); + if (country.equals("")) + JoH.static_toast(preference.getContext(), "Country is required!", Toast.LENGTH_LONG); + else { + CareLinkAuthenticator authenticator = new CareLinkAuthenticator(country, CareLinkCredentialStore.getInstance()); + if (authenticator.authenticate(getActivity())) { + JoH.static_toast(preference.getContext(), "Login completed!", Toast.LENGTH_LONG); + CareLinkFollowService.resetInstanceAndInvalidateSession(); + CollectionServiceStarter.restartCollectionServiceBackground(); + } + else + JoH.static_toast(preference.getContext(), "Login failed!", Toast.LENGTH_LONG); + } + } catch (InterruptedException e) { + + } + } + }.start(); + + return true; + } + }; //Register prefChange handler try { - carelinkFollowUser.setOnPreferenceChangeListener(carelinkFollowListener); - carelinkFollowPass.setOnPreferenceChangeListener(carelinkFollowListener); + //carelinkFollowUser.setOnPreferenceChangeListener(carelinkFollowListener); + //carelinkFollowPass.setOnPreferenceChangeListener(carelinkFollowListener); carelinkFollowCountry.setOnPreferenceChangeListener(carelinkFollowListener); carelinkFollowPatient.setOnPreferenceChangeListener(carelinkFollowListener); carelinkFollowGracePeriod.setOnPreferenceChangeListener(carelinkFollowListener); carelinkFollowPollInterval.setOnPreferenceChangeListener(carelinkFollowListener); + carelinkFollowLogin.setOnPreferenceClickListener(carelinkLoginListener); //carelinkFollowDownloadFingerBGs.setOnPreferenceChangeListener(carelinkFollowListener); //carelinkFollowDownloadBoluses.setOnPreferenceChangeListener(carelinkFollowListener); //carelinkFollowDownloadMeals.setOnPreferenceChangeListener(carelinkFollowListener); @@ -1242,10 +1265,11 @@ public boolean onPreferenceChange(Preference preference, Object newValue) { //Remove preferences } else { try { - collectionCategory.removePreference(carelinkFollowUser); - collectionCategory.removePreference(carelinkFollowPass); + //collectionCategory.removePreference(carelinkFollowUser); + //collectionCategory.removePreference(carelinkFollowPass); collectionCategory.removePreference(carelinkFollowCountry); collectionCategory.removePreference(carelinkFollowPatient); + collectionCategory.removePreference(carelinkFollowLogin); collectionCategory.removePreference(carelinkFollowGracePeriod); collectionCategory.removePreference(carelinkFollowPollInterval); collectionCategory.removePreference(carelinkFollowDownloadFingerBGs); @@ -1272,10 +1296,9 @@ public boolean onPreferenceChange(Preference preference, Object newValue) { } - final Preference scanShare = findPreference("scan_share2_barcode"); final EditTextPreference transmitterId = (EditTextPreference) findPreference("dex_txid"); - // final Preference closeGatt = findPreference("close_gatt_on_ble_disconnect"); + // final Preference closeGatt = findPreference("close_gatt_on_ble_disconnect"); final Preference pebbleSync2 = findPreference("broadcast_to_pebble_type"); final Preference pebbleSync1 = findPreference("broadcast_to_pebble"); @@ -1346,18 +1369,18 @@ public boolean onPreferenceChange(Preference preference, Object newValue) { enableAmazfit.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { - @Override - public boolean onPreferenceChange(Preference preference, Object newValue) { - final Context context = preference.getContext(); - Boolean enabled = (boolean) newValue; - if (enabled==true) { - context.startService(new Intent(context, Amazfitservice.class)); + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + final Context context = preference.getContext(); + Boolean enabled = (boolean) newValue; + if (enabled == true) { + context.startService(new Intent(context, Amazfitservice.class)); - }else { - context.stopService(new Intent(context, Amazfitservice.class)); - } + } else { + context.stopService(new Intent(context, Amazfitservice.class)); + } - return true; + return true; } }); @@ -1412,17 +1435,17 @@ public boolean onPreferenceChange(Preference preference, Object newValue) { }); + if (enableBF != null) + enableBF.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + preference.getEditor().putBoolean(preference.getKey(), (boolean) newValue).apply(); + xdrip.initBF(); + return true; + } + } - if (enableBF != null ) enableBF.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { - @Override - public boolean onPreferenceChange(Preference preference, Object newValue) { - preference.getEditor().putBoolean(preference.getKey(),(boolean)newValue).apply(); - xdrip.initBF(); - return true; - } - } - - ); + ); disableAlertsStaleDataMinutes.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { @Override @@ -1444,18 +1467,15 @@ public boolean onPreferenceChange(Preference preference, Object newValue) { showShowcase.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(Preference preference, Object newValue) { - if ((boolean)newValue) - { + if ((boolean) newValue) { ShotStateStore.resetAllShots(); - JoH.static_toast(preference.getContext(),getString(R.string.interface_tips_from_start),Toast.LENGTH_LONG); + JoH.static_toast(preference.getContext(), getString(R.string.interface_tips_from_start), Toast.LENGTH_LONG); } return true; } }); - - units_pref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { @Override @@ -1552,7 +1572,6 @@ public boolean onPreferenceChange(Preference preference, Object newValue) { }); - Log.d(TAG, collectionType.name()); if (collectionType != DexCollectionType.DexcomShare) { collectionCategory.removePreference(shareKey); @@ -1587,8 +1606,9 @@ public boolean onPreferenceChange(Preference preference, Object newValue) { try { collectionCategory.removePreference(carelinkFollowCountry); collectionCategory.removePreference(carelinkFollowPatient); - collectionCategory.removePreference(carelinkFollowPass); - collectionCategory.removePreference(carelinkFollowUser); + collectionCategory.removePreference(carelinkFollowLogin); + //collectionCategory.removePreference(carelinkFollowPass); + //collectionCategory.removePreference(carelinkFollowUser); collectionCategory.removePreference(carelinkFollowGracePeriod); collectionCategory.removePreference(carelinkFollowPollInterval); collectionCategory.removePreference(carelinkFollowDownloadFingerBGs); @@ -1623,7 +1643,7 @@ public boolean onPreferenceChange(final Preference preference, final Object newV builder.setPositiveButton("I AM SURE", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); - ((SwitchPreference)preference).setChecked(true); + ((SwitchPreference) preference).setChecked(true); preference.getEditor().putBoolean("use_nfc_scan", true).apply(); NFCReaderX.handleHomeScreenScanPreference(xdrip.getAppContext(), (boolean) newValue && prefs.getBoolean("nfc_scan_homescreen", false)); } @@ -1656,7 +1676,7 @@ public void onClick(DialogInterface dialog, int which) { // } - final boolean engineering_mode = this.prefs.getBoolean("engineering_mode",false); + final boolean engineering_mode = this.prefs.getBoolean("engineering_mode", false); if (!engineering_mode) { try { @@ -1676,18 +1696,18 @@ public void onClick(DialogInterface dialog, int which) { } //if (engineering_mode) { - // populate the list - PluggableCalibration.setListPreferenceData(currentCalibrationPlugin); + // populate the list + PluggableCalibration.setListPreferenceData(currentCalibrationPlugin); - currentCalibrationPlugin.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { - @Override - public boolean onPreferenceChange(Preference preference, Object newValue) { - PluggableCalibration.invalidateCache(); // current - PluggableCalibration.invalidateCache(newValue.toString()); // next - PluggableCalibration.invalidatePluginCache(); // reset the object cache - return true; - } - }); + currentCalibrationPlugin.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + PluggableCalibration.invalidateCache(); // current + PluggableCalibration.invalidateCache(newValue.toString()); // next + PluggableCalibration.invalidatePluginCache(); // reset the object cache + return true; + } + }); //} if (!DexCollectionType.hasLibre(collectionType)) { @@ -1761,7 +1781,7 @@ public boolean onPreferenceChange(Preference preference, Object newValue) { } if (Build.VERSION.SDK_INT < 23) { try { - ((PreferenceGroup)findPreference("xdrip_plus_display_category")).removePreference(findPreference("xdrip_plus_number_icon")); + ((PreferenceGroup) findPreference("xdrip_plus_display_category")).removePreference(findPreference("xdrip_plus_number_icon")); } catch (Exception e) { // } } @@ -1777,12 +1797,12 @@ public boolean onPreferenceChange(Preference preference, Object newValue) { } } - // if (!Experience.gotData()) { - // try { - // collectionCategory.removePreference(runInForeground); - // } catch (Exception e) { // - // } - // } + // if (!Experience.gotData()) { + // try { + // collectionCategory.removePreference(runInForeground); + // } catch (Exception e) { // + // } + // } // remove master ip input if we are the master if (Home.get_master()) { @@ -1815,11 +1835,11 @@ public boolean onPreferenceChange(Preference preference, Object newValue) { // collectionCategory.removePreference(transmitterId); collectionCategory.removePreference(g5_settings_screen); - // collectionCategory.removePreference(scanConstantly); - // collectionCategory.removePreference(g5nonraw); - // collectionCategory.removePreference(reAuth); - // collectionCategory.removePreference(reBond); - // collectionCategory.removePreference(runOnMain); + // collectionCategory.removePreference(scanConstantly); + // collectionCategory.removePreference(g5nonraw); + // collectionCategory.removePreference(reAuth); + // collectionCategory.removePreference(reBond); + // collectionCategory.removePreference(runOnMain); } catch (NullPointerException e) { Log.wtf(TAG, "Null pointer removing G5 prefs ", e); } @@ -1827,7 +1847,8 @@ public boolean onPreferenceChange(Preference preference, Object newValue) { if (!engineering_mode) { try { - if (!Experience.gotData()) getPreferenceScreen().removePreference(motionScreen); + if (!Experience.gotData()) + getPreferenceScreen().removePreference(motionScreen); calibrationSettingsScreen.removePreference(old_school_calibration_mode); } catch (NullPointerException e) { Log.wtf(TAG, "Nullpointer with engineering mode s ", e); @@ -1967,7 +1988,7 @@ public boolean onPreferenceChange(Preference preference, Object newValue) { installPebbleWatchface(pebbleType, preference); // start/stop service - enablePebble(pebbleType, getBooleanPreferenceViaContextWithoutException(context,"broadcast_to_pebble",false), context); + enablePebble(pebbleType, getBooleanPreferenceViaContextWithoutException(context, "broadcast_to_pebble", false), context); // configuration options if (oldPebbleType != pebbleType) { @@ -2039,7 +2060,8 @@ public boolean onPreferenceChange(Preference preference, Object newValue) { pebbleHighLine.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(Preference preference, Object newValue) { - restartPebble();; + restartPebble(); + ; return true; } }); @@ -2121,7 +2143,7 @@ public boolean onPreferenceChange(Preference preference, Object newValue) { extraTagsForLogs.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(Preference preference, Object newValue) { - ExtraLogTags.readPreference((String)newValue); + ExtraLogTags.readPreference((String) newValue); return true; } }); @@ -2156,7 +2178,7 @@ public void run() { }); // when changing collection method - collectionMethod.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { + collectionMethod.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(Preference preference, Object newValue) { @@ -2227,7 +2249,7 @@ public boolean onPreferenceChange(Preference preference, Object newValue) { //TODO Bridge battery display support } else { collectionCategory.addPreference(transmitterId); - // collectionCategory.addPreference(closeGatt); + // collectionCategory.addPreference(closeGatt); } if (collectionType == DexCollectionType.DexcomG5) { @@ -2273,17 +2295,16 @@ public boolean onPreferenceChange(Preference preference, Object newValue) { if (newValue.equals("Follower")) { // reset battery whenever changing collector type - AllPrefsFragment.this.prefs.edit().putInt("bridge_battery",0).apply(); - AllPrefsFragment.this.prefs.edit().putInt("parakeet_battery",0).apply(); - if (AllPrefsFragment.this.prefs.getBoolean("plus_follow_master",false)) - { + AllPrefsFragment.this.prefs.edit().putInt("bridge_battery", 0).apply(); + AllPrefsFragment.this.prefs.edit().putInt("parakeet_battery", 0).apply(); + if (AllPrefsFragment.this.prefs.getBoolean("plus_follow_master", false)) { AllPrefsFragment.this.prefs.edit().putBoolean("plus_follow_master", false).apply(); - JoH.static_toast(preference.getContext(),"Turning off xDrip+ Sync Master for Followers!",Toast.LENGTH_LONG); + JoH.static_toast(preference.getContext(), "Turning off xDrip+ Sync Master for Followers!", Toast.LENGTH_LONG); } GcmActivity.requestBGsync(); } - //} else { - // CollectionServiceStarter.restartCollectionService(preference.getContext()); + //} else { + // CollectionServiceStarter.restartCollectionService(preference.getContext()); } CollectionServiceStarter.restartCollectionServiceBackground(); @@ -2346,7 +2367,7 @@ private void showSearchFragment() { } - private void updateMiBandScreen(){ + private void updateMiBandScreen() { MiBand.MiBandType type = MiBand.getMibandType(); PreferenceScreen settings = (PreferenceScreen) findPreference(MiBandEntry.PREF_MIBAND_SETTINGS); PreferenceScreen prefs = (PreferenceScreen) findPreference(MiBandEntry.PREF_MIBAND_PREFERENCES); @@ -2366,8 +2387,7 @@ private void updateMiBandScreen(){ prefs.addPreference(miband_authkey); } else if (type == MiBand.MiBandType.MI_BAND2) { settings.addPreference(miband2_screen); - } - else if (type == MiBand.MiBandType.MI_BAND3 || type == MiBand.MiBandType.MI_BAND3_1){ + } else if (type == MiBand.MiBandType.MI_BAND3 || type == MiBand.MiBandType.MI_BAND3_1) { settings.addPreference(miband3_4_screen); settings.addPreference(miband_nightmode_category); } @@ -2376,9 +2396,9 @@ else if (type == MiBand.MiBandType.MI_BAND3 || type == MiBand.MiBandType.MI_BAND } } - private void updateMibandPreferencesData(){ + private void updateMibandPreferencesData() { EditTextPreference prefMac = (EditTextPreference) findPreference(MiBandEntry.PREF_MIBAND_MAC); - if (prefMac != null ) { + if (prefMac != null) { prefMac.setText(MiBand.getMac()); sBindPreferenceTitleAppendToMacValueListener.onPreferenceChange(prefMac, PreferenceManager @@ -2386,7 +2406,7 @@ private void updateMibandPreferencesData(){ .getString(prefMac.getKey(), "")); } EditTextPreference prefAuthKey = (EditTextPreference) findPreference(MiBandEntry.PREF_MIBAND_AUTH_KEY); - if (prefAuthKey != null )prefAuthKey.setText(MiBand.getAuthKey()); + if (prefAuthKey != null) prefAuthKey.setText(MiBand.getAuthKey()); } // all this boiler plate for a dynamic interface seems excessive and boring, I would love to know a helper library to simplify this @@ -2512,21 +2532,20 @@ private void installPebbleWatchface(final int pebbleType, Preference preference) builder.setTitle("Pebble Install"); - switch (pebbleType) - { - case 2: - builder.setMessage("Install Standard Pebble Watchface?"); - break; - case 3: - builder.setMessage("Install Pebble Trend Watchface?"); - break; - case 4: - builder.setMessage("Install Pebble Classic Trend Watchface?"); - break; - case 5: - builder.setMessage("Install Pebble Clay Trend Watchface?"); - break; - } + switch (pebbleType) { + case 2: + builder.setMessage("Install Standard Pebble Watchface?"); + break; + case 3: + builder.setMessage("Install Pebble Trend Watchface?"); + break; + case 4: + builder.setMessage("Install Pebble Classic Trend Watchface?"); + break; + case 5: + builder.setMessage("Install Pebble Clay Trend Watchface?"); + break; + } builder.setPositiveButton(gs(R.string.yes), new DialogInterface.OnClickListener() { @@ -2569,9 +2588,11 @@ public void onClick(DialogInterface dialog, int which) { }); AlertDialog alert = builder.create(); alert.show(); - }},3000); - // outer - }}); + } + }, 3000); + // outer + } + }); builder.setNegativeButton(gs(R.string.no), new DialogInterface.OnClickListener() { @Override @@ -2585,13 +2606,14 @@ public void onClick(DialogInterface dialog, int which) { } private static int pebbleType = 1; + private void enablePebble(int newValueInt, boolean enabled, Context context) { - Log.d(TAG,"enablePebble called with: "+newValueInt+" "+enabled); + Log.d(TAG, "enablePebble called with: " + newValueInt + " " + enabled); if (pebbleType == 1) { if (enabled && (newValueInt != 1)) { context.stopService(new Intent(context, PebbleWatchSync.class)); context.startService(new Intent(context, PebbleWatchSync.class)); - Log.d(TAG,"Starting pebble service type: "+newValueInt); + Log.d(TAG, "Starting pebble service type: " + newValueInt); } } else { if (!enabled || (newValueInt == 1)) { @@ -2738,7 +2760,7 @@ public boolean onPreferenceChange(Preference preference, Object newValue) { public static void handleUnitsChange(Preference preference, Object newValue, AllPrefsFragment allPrefsFragment) { try { SharedPreferences preferences; - if (preference!= null) { + if (preference != null) { preferences = preference.getSharedPreferences(); } else { preferences = PreferenceManager.getDefaultSharedPreferences(xdrip.getAppContext()); @@ -2772,14 +2794,14 @@ public static void handleUnitsChange(Preference preference, Object newValue, All ProfileEditor.convertData(Constants.MGDL_TO_MMOLL); preferences.edit().putString("highValue", JoH.qs(highVal * Constants.MGDL_TO_MMOLL, 1)).apply(); preferences.edit().putString("profile_insulin_sensitivity_default", JoH.qs(default_insulin_sensitivity * Constants.MGDL_TO_MMOLL, 2)).apply(); - preferences.edit().putString("plus_target_range", JoH.qs(default_target_glucose * Constants.MGDL_TO_MMOLL,1)).apply(); + preferences.edit().putString("plus_target_range", JoH.qs(default_target_glucose * Constants.MGDL_TO_MMOLL, 1)).apply(); Profile.invalidateProfile(); } if (lowVal > 35) { ProfileEditor.convertData(Constants.MGDL_TO_MMOLL); preferences.edit().putString("lowValue", JoH.qs(lowVal * Constants.MGDL_TO_MMOLL, 1)).apply(); preferences.edit().putString("profile_insulin_sensitivity_default", JoH.qs(default_insulin_sensitivity * Constants.MGDL_TO_MMOLL, 2)).apply(); - preferences.edit().putString("plus_target_range", JoH.qs(default_target_glucose * Constants.MGDL_TO_MMOLL,1)).apply(); + preferences.edit().putString("plus_target_range", JoH.qs(default_target_glucose * Constants.MGDL_TO_MMOLL, 1)).apply(); Profile.invalidateProfile(); } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index bb1b2e9..4fb7b5d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,4 +1,4 @@ - + Bluetooth Scan @@ -1333,6 +1333,8 @@ CareLink patient username CareLink Language CareLink language code + Login + Login with browser Grace Period Grace period for data request in seconds Missed data poll interval diff --git a/app/src/main/res/xml/pref_data_source.xml b/app/src/main/res/xml/pref_data_source.xml index 21a201f..703e45f 100644 --- a/app/src/main/res/xml/pref_data_source.xml +++ b/app/src/main/res/xml/pref_data_source.xml @@ -140,7 +140,7 @@ android:summary="Login password for Dex Share Following" android:title="Share Password" /> - + + +