diff options
author | Adnan Begovic <adnan@cyngn.com> | 2016-04-18 14:22:36 -0700 |
---|---|---|
committer | Adnan Begovic <adnan@cyngn.com> | 2016-04-20 14:12:38 -0700 |
commit | a6f49ab9fdd72ec75a9f43efbe66537d33486dbd (patch) | |
tree | e658188ca0b6f58077e3f39b866908d3cce89667 | |
parent | 2a77ad45d9e66427f5899da32a611586e191a0c2 (diff) | |
download | android_packages_apps_WundergroundWeatherProvider-a6f49ab9fdd72ec75a9f43efbe66537d33486dbd.tar.gz android_packages_apps_WundergroundWeatherProvider-a6f49ab9fdd72ec75a9f43efbe66537d33486dbd.tar.bz2 android_packages_apps_WundergroundWeatherProvider-a6f49ab9fdd72ec75a9f43efbe66537d33486dbd.zip |
WundergroundCM: Add api key verification mechanism.
Change-Id: I9c5d077356de8532c7efd002042f1f7ab1aa021e
6 files changed, 247 insertions, 13 deletions
diff --git a/app/src/main/java/org/cyanogenmod/wundergroundcmweatherprovider/ApiKeyInterceptor.java b/app/src/main/java/org/cyanogenmod/wundergroundcmweatherprovider/ApiKeyInterceptor.java new file mode 100644 index 0000000..034786e --- /dev/null +++ b/app/src/main/java/org/cyanogenmod/wundergroundcmweatherprovider/ApiKeyInterceptor.java @@ -0,0 +1,54 @@ +/** + * Copyright (C) 2016 The CyanogenMod Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.cyanogenmod.wundergroundcmweatherprovider; + +import java.io.IOException; + +import okhttp3.HttpUrl; +import okhttp3.Interceptor; +import okhttp3.Request; +import okhttp3.Response; + +/** + * Inspired by https://gist.github.com/swankjesse/8571a8207a5815cca1fb + */ +public class ApiKeyInterceptor implements Interceptor { + + /** http://api.wunderground.com/api/<API KEY>//<features...>/q/<location>.json **/ + private static final int API_KEY_PATH_SEGMENT = 1; + + private volatile String mApiKey; + + public void setApiKey(String apiKey) { + this.mApiKey = apiKey; + } + + @Override + public Response intercept(Chain chain) throws IOException { + Request request = chain.request(); + String apiKey = mApiKey; + if (apiKey != null) { + HttpUrl newUrl = request.url().newBuilder() + .setPathSegment(API_KEY_PATH_SEGMENT, apiKey) + .build(); + request = request.newBuilder() + .url(newUrl) + .build(); + } + return chain.proceed(request); + } +} diff --git a/app/src/main/java/org/cyanogenmod/wundergroundcmweatherprovider/WUBasePreferenceActivity.java b/app/src/main/java/org/cyanogenmod/wundergroundcmweatherprovider/WUBasePreferenceActivity.java index 1d0dd20..53e716c 100644 --- a/app/src/main/java/org/cyanogenmod/wundergroundcmweatherprovider/WUBasePreferenceActivity.java +++ b/app/src/main/java/org/cyanogenmod/wundergroundcmweatherprovider/WUBasePreferenceActivity.java @@ -20,16 +20,37 @@ import android.content.ActivityNotFoundException; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; +import android.content.res.Resources; import android.net.Uri; import android.os.Bundle; +import android.os.Message; import android.preference.EditTextPreference; import android.preference.Preference; import android.preference.PreferenceActivity; import android.text.Editable; +import android.text.Spannable; +import android.text.SpannableString; +import android.text.TextUtils; +import android.text.style.ForegroundColorSpan; +import android.util.Log; import android.view.Menu; import android.view.MenuItem; -public class WUBasePreferenceActivity extends PreferenceActivity implements Preference.OnPreferenceChangeListener, Preference.OnPreferenceClickListener { +import org.cyanogenmod.wundergroundcmweatherprovider.wunderground.Feature; +import org.cyanogenmod.wundergroundcmweatherprovider.wunderground.WundergroundServiceManager; +import org.cyanogenmod.wundergroundcmweatherprovider.wunderground.responses.CurrentObservationResponse; +import org.cyanogenmod.wundergroundcmweatherprovider.wunderground.responses.WundergroundReponse; + +import javax.inject.Inject; + +import cyanogenmod.weather.WeatherLocation; +import retrofit2.Call; +import retrofit2.Response; + +public class WUBasePreferenceActivity extends PreferenceActivity implements + Preference.OnPreferenceChangeListener, Preference.OnPreferenceClickListener { + + private static final String TAG = WUBasePreferenceActivity.class.getSimpleName(); private static final String CREATE_ACCOUNT_KEY = "create_account"; private static final String API_KEY_KEY = "api_key"; @@ -37,9 +58,15 @@ public class WUBasePreferenceActivity extends PreferenceActivity implements Pref private static final String WU_CREATE_ACCOUNT_URL = "https://www.wunderground.com/weather/api/d/login.html"; + private static final int VERIFY_API_KEY = 0; + private static final int UPDATE_SUMMARIES = 1; + private Preference mCreateAccountPreference; private EditTextPreference mApiKeyPreference; + @Inject + WundergroundServiceManager mWundergroundServiceManager; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -55,6 +82,10 @@ public class WUBasePreferenceActivity extends PreferenceActivity implements Pref if (actionBar != null) { actionBar.setDisplayHomeAsUpEnabled(true); } + + if (getSharedPreferences().contains(WundergroundModule.API_KEY_VERIFIED)) { + updateSummaries(); + } } @Override @@ -72,11 +103,19 @@ public class WUBasePreferenceActivity extends PreferenceActivity implements Pref switch (preference.getKey()) { case API_KEY_KEY: Editable editText = mApiKeyPreference.getEditText().getText(); - if (editText != null) { - String text = editText.toString(); - SharedPreferences sharedPreferences = getSharedPreferences( - WundergroundModule.SHARED_PREFS_KEY, Context.MODE_PRIVATE); - sharedPreferences.edit().putString(WundergroundModule.API_KEY, text).commit(); + String text = editText.toString(); + + if (!TextUtils.isEmpty(text)) { + SharedPreferences.Editor editor = getSharedPreferences().edit(); + editor.putString(WundergroundModule.API_KEY, text); + editor.commit(); + + mWundergroundServiceManager.updateApiKey(text); + + Message verifyApiKeyMessage = mHandler.obtainMessage(); + verifyApiKeyMessage.sendToTarget(); + } else { + passedVerification(false); } return true; } @@ -99,4 +138,109 @@ public class WUBasePreferenceActivity extends PreferenceActivity implements Pref } return false; } + + private final NonLeakyMessageHandler mHandler = new NonLeakyMessageHandler(this); + + private static class NonLeakyMessageHandler extends + WeakReferenceHandler<WUBasePreferenceActivity> { + + public NonLeakyMessageHandler(WUBasePreferenceActivity reference) { + super(reference); + } + + @Override + protected void handleMessage(WUBasePreferenceActivity reference, Message msg) { + switch (msg.what) { + case VERIFY_API_KEY: + Log.d(TAG, "Verifying api key..."); + reference.verifyReceivedWeatherInfoByPostalCode(); + break; + case UPDATE_SUMMARIES: + reference.updateSummaries(); + break; + } + } + } + + private SharedPreferences getSharedPreferences() { + return getSharedPreferences(WundergroundModule.SHARED_PREFS_KEY, Context.MODE_PRIVATE); + } + + private void updateSummaries() { + final SharedPreferences sharedPreferences = getSharedPreferences(); + final Resources resources = getResources(); + boolean verified = sharedPreferences.getBoolean(WundergroundModule.API_KEY_VERIFIED, false); + + Log.d(TAG, "Updating summaries, verified " + verified); + + ForegroundColorSpan foregroundColorSpan = new ForegroundColorSpan(verified ? + resources.getColor(R.color.green) : + resources.getColor(R.color.red)); + + Spannable summary = new SpannableString(verified ? + getString(R.string.authentication_preference_api_key_verified) : + getString(R.string.authentication_preference_api_key_not_verified)); + + summary.setSpan(foregroundColorSpan, 0, summary.length(), 0); + mApiKeyPreference.setSummary(summary); + } + + private void verifyReceivedWeatherInfoByPostalCode() { + WeatherLocation weatherLocation = new WeatherLocation.Builder("Seattle") + .setPostalCode("98121") + .setCountry("US") + .setState("WA") + .build(); + + Call<WundergroundReponse> wundergroundCall = + mWundergroundServiceManager.query(weatherLocation.getPostalCode(), + Feature.conditions, Feature.forecast); + + Log.d(TAG, "Enqueue api request..."); + wundergroundCall.enqueue(new retrofit2.Callback<WundergroundReponse>() { + @Override + public void onResponse(Call<WundergroundReponse> call, + Response<WundergroundReponse> response) { + Log.d(TAG, "Response " + response.toString()); + if (response.isSuccessful()) { + WundergroundReponse wundergroundReponse = response.body(); + + if (wundergroundReponse == null) { + passedVerification(false); + return; + } + + CurrentObservationResponse currentObservationResponse = + wundergroundReponse.getCurrentObservation(); + + if (currentObservationResponse == null) { + passedVerification(false); + } else { + passedVerification(true); + } + } else { + passedVerification(false); + } + } + + @Override + public void onFailure(Call<WundergroundReponse> call, Throwable t) { + Log.d(TAG, "Response " + t.toString()); + passedVerification(false); + } + }); + } + + private void passedVerification(boolean passed) { + Log.d(TAG, "Passed verification" + passed); + final SharedPreferences.Editor editor = getSharedPreferences().edit(); + editor.putBoolean(WundergroundModule.API_KEY_VERIFIED, passed); + editor.apply(); + update(); + } + + private void update() { + Message message = mHandler.obtainMessage(UPDATE_SUMMARIES); + message.sendToTarget(); + } } diff --git a/app/src/main/java/org/cyanogenmod/wundergroundcmweatherprovider/WundergroundModule.java b/app/src/main/java/org/cyanogenmod/wundergroundcmweatherprovider/WundergroundModule.java index 553e03c..aa5951b 100644 --- a/app/src/main/java/org/cyanogenmod/wundergroundcmweatherprovider/WundergroundModule.java +++ b/app/src/main/java/org/cyanogenmod/wundergroundcmweatherprovider/WundergroundModule.java @@ -37,6 +37,8 @@ public class WundergroundModule { public static final String SHARED_PREFS_KEY = "WU_SHARED_PREFS"; public static final String API_KEY = "API_KEY"; + public static final String API_KEY_VERIFIED = "API_KEY_VERIFIED"; + private WundergroundCMApplication mWeatherviewApplication; public WundergroundModule(WundergroundCMApplication weatherviewApplication) { diff --git a/app/src/main/java/org/cyanogenmod/wundergroundcmweatherprovider/wunderground/WundergroundServiceManager.java b/app/src/main/java/org/cyanogenmod/wundergroundcmweatherprovider/wunderground/WundergroundServiceManager.java index ffcde0e..6649330 100644 --- a/app/src/main/java/org/cyanogenmod/wundergroundcmweatherprovider/wunderground/WundergroundServiceManager.java +++ b/app/src/main/java/org/cyanogenmod/wundergroundcmweatherprovider/wunderground/WundergroundServiceManager.java @@ -20,6 +20,7 @@ import android.util.Log; import com.google.common.base.Joiner; +import org.cyanogenmod.wundergroundcmweatherprovider.ApiKeyInterceptor; import org.cyanogenmod.wundergroundcmweatherprovider.wunderground.responses.WundergroundReponse; import okhttp3.OkHttpClient; @@ -29,10 +30,14 @@ import retrofit2.Retrofit; import retrofit2.converter.gson.GsonConverterFactory; public class WundergroundServiceManager { - private final static String TAG = WundergroundServiceManager.class.getSimpleName(); + private final static String TAG = "WundergroundManager"; + private final static boolean DEBUG = Log.isLoggable(TAG, Log.VERBOSE); + private final WundergroundServiceInterface mWundergroundServiceInterface; + private final ApiKeyInterceptor mApiKeyInterceptor; public WundergroundServiceManager(String apiKey) { + mApiKeyInterceptor = new ApiKeyInterceptor(); Retrofit baseAdapter = buildRestAdapter(apiKey); mWundergroundServiceInterface = baseAdapter.create(WundergroundServiceInterface.class); } @@ -58,19 +63,29 @@ public class WundergroundServiceManager { return mWundergroundServiceInterface.lookupCity(city); } + public void updateApiKey(String apiKey) { + mApiKeyInterceptor.setApiKey(apiKey); + } + private String coerceVarArgFeaturesToDelimitedString(FeatureParam... featureParams) { return Joiner.on('/').join(featureParams); } private Retrofit buildRestAdapter(String apiKey) { - //TODO: Wrap this in debug flag - //HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor(); - //interceptor.setLevel(HttpLoggingInterceptor.Level.BODY); - //OkHttpClient client = new OkHttpClient.Builder().addInterceptor(interceptor).build(); + final OkHttpClient.Builder builder = new OkHttpClient().newBuilder(); + if (DEBUG) { + final HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(); + loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY); + builder.addInterceptor(loggingInterceptor); + } + mApiKeyInterceptor.setApiKey(apiKey); + builder.addInterceptor(mApiKeyInterceptor); + final OkHttpClient client = builder.build(); + final String baseUrl = "http://api.wunderground.com/api/" + apiKey + "/"; return new Retrofit.Builder() - .baseUrl("http://api.wunderground.com/api/" + apiKey + "/") - //.client(client) + .baseUrl(baseUrl) + .client(client) .addConverterFactory(GsonConverterFactory.create()) .build(); } diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml new file mode 100644 index 0000000..88b1caa --- /dev/null +++ b/app/src/main/res/values/colors.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2016 The CyanogenMod Project + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<resources> + <item name="red" type="color">#FFFF4444</item> + <item name="green" type="color">#FF99CC00</item> +</resources>
\ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ba0220c..b5c42a1 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -17,6 +17,8 @@ <string name="authentication_preference_category_title">Authentication</string> <string name="authentication_preference_api_key">API key</string> <string name="authentication_preference_api_key_summary">API key to be utilized for Weather Underground authentication</string> + <string name="authentication_preference_api_key_verified">Authenticated</string> + <string name="authentication_preference_api_key_not_verified">Unable to authenticate</string> <string name="authentication_preference_account_create">Create account</string> <string name="authentication_preference_account_create_summary">Create an account to get an API key for weather data usage</string> <string name="about_preference_category_title">About</string> |