Add Weather Content Provider [4/5]
Introduce CM Weather Manager and Weather Provider Services API. The CM Weather Manager can be used by apps to request weather updates. The Weather Provider Services API allows a third party developer to implement a weather service to process weather update requests and update the CM Weather Content Provider data which can be consumed by any other app holding the required permission. Change-Id: Idcc80712ba92715109d3577d120f7fea85d6c996
This commit is contained in:
parent
46821304e9
commit
4195a1cf89
|
@ -24,7 +24,7 @@ LOCAL_PATH := $(call my-dir)
|
||||||
cm_platform_res := APPS/org.cyanogenmod.platform-res_intermediates/src
|
cm_platform_res := APPS/org.cyanogenmod.platform-res_intermediates/src
|
||||||
|
|
||||||
# List of packages used in cm-api-stubs and cm-system-api-stubs
|
# List of packages used in cm-api-stubs and cm-system-api-stubs
|
||||||
cm_stub_packages := cyanogenmod.alarmclock:cyanogenmod.app:cyanogenmod.content:cyanogenmod.externalviews:cyanogenmod.hardware:cyanogenmod.media:cyanogenmod.os:cyanogenmod.profiles:cyanogenmod.providers:cyanogenmod.platform:cyanogenmod.power:cyanogenmod.themes:cyanogenmod.util
|
cm_stub_packages := cyanogenmod.alarmclock:cyanogenmod.app:cyanogenmod.content:cyanogenmod.externalviews:cyanogenmod.hardware:cyanogenmod.media:cyanogenmod.os:cyanogenmod.profiles:cyanogenmod.providers:cyanogenmod.platform:cyanogenmod.power:cyanogenmod.themes:cyanogenmod.util:cyanogenmod.weather:cyanogenmod.weatherservice
|
||||||
|
|
||||||
# The CyanogenMod Platform Framework Library
|
# The CyanogenMod Platform Framework Library
|
||||||
# ============================================================
|
# ============================================================
|
||||||
|
|
|
@ -595,6 +595,8 @@ package cyanogenmod.platform {
|
||||||
ctor public Manifest.permission();
|
ctor public Manifest.permission();
|
||||||
field public static final java.lang.String ACCESS_APP_SUGGESTIONS = "cyanogenmod.permission.ACCESS_APP_SUGGESTIONS";
|
field public static final java.lang.String ACCESS_APP_SUGGESTIONS = "cyanogenmod.permission.ACCESS_APP_SUGGESTIONS";
|
||||||
field public static final java.lang.String ACCESS_THEME_MANAGER = "cyanogenmod.permission.ACCESS_THEME_MANAGER";
|
field public static final java.lang.String ACCESS_THEME_MANAGER = "cyanogenmod.permission.ACCESS_THEME_MANAGER";
|
||||||
|
field public static final java.lang.String ACCESS_WEATHER_MANAGER = "cyanogenmod.permission.ACCESS_WEATHER_MANAGER";
|
||||||
|
field public static final java.lang.String BIND_WEATHER_PROVIDER_SERVICE = "cyanogenmod.permission.BIND_WEATHER_PROVIDER_SERVICE";
|
||||||
field public static final java.lang.String HARDWARE_ABSTRACTION_ACCESS = "cyanogenmod.permission.HARDWARE_ABSTRACTION_ACCESS";
|
field public static final java.lang.String HARDWARE_ABSTRACTION_ACCESS = "cyanogenmod.permission.HARDWARE_ABSTRACTION_ACCESS";
|
||||||
field public static final java.lang.String LIVE_LOCK_SCREEN_MANAGER_ACCESS = "cyanogenmod.permission.LIVE_LOCK_SCREEN_MANAGER_ACCESS";
|
field public static final java.lang.String LIVE_LOCK_SCREEN_MANAGER_ACCESS = "cyanogenmod.permission.LIVE_LOCK_SCREEN_MANAGER_ACCESS";
|
||||||
field public static final java.lang.String MANAGE_ALARMS = "cyanogenmod.permission.MANAGE_ALARMS";
|
field public static final java.lang.String MANAGE_ALARMS = "cyanogenmod.permission.MANAGE_ALARMS";
|
||||||
|
@ -609,11 +611,13 @@ package cyanogenmod.platform {
|
||||||
field public static final java.lang.String READ_ALARMS = "cyanogenmod.permission.READ_ALARMS";
|
field public static final java.lang.String READ_ALARMS = "cyanogenmod.permission.READ_ALARMS";
|
||||||
field public static final java.lang.String READ_MSIM_PHONE_STATE = "cyanogenmod.permission.READ_MSIM_PHONE_STATE";
|
field public static final java.lang.String READ_MSIM_PHONE_STATE = "cyanogenmod.permission.READ_MSIM_PHONE_STATE";
|
||||||
field public static final java.lang.String READ_THEMES = "cyanogenmod.permission.READ_THEMES";
|
field public static final java.lang.String READ_THEMES = "cyanogenmod.permission.READ_THEMES";
|
||||||
|
field public static final java.lang.String READ_WEATHER = "cyanogenmod.permission.READ_WEATHER";
|
||||||
field public static final java.lang.String THIRD_PARTY_KEYGUARD = "android.permission.THIRD_PARTY_KEYGUARD";
|
field public static final java.lang.String THIRD_PARTY_KEYGUARD = "android.permission.THIRD_PARTY_KEYGUARD";
|
||||||
field public static final java.lang.String WRITE_ALARMS = "cyanogenmod.permission.WRITE_ALARMS";
|
field public static final java.lang.String WRITE_ALARMS = "cyanogenmod.permission.WRITE_ALARMS";
|
||||||
field public static final java.lang.String WRITE_SECURE_SETTINGS = "cyanogenmod.permission.WRITE_SECURE_SETTINGS";
|
field public static final java.lang.String WRITE_SECURE_SETTINGS = "cyanogenmod.permission.WRITE_SECURE_SETTINGS";
|
||||||
field public static final java.lang.String WRITE_SETTINGS = "cyanogenmod.permission.WRITE_SETTINGS";
|
field public static final java.lang.String WRITE_SETTINGS = "cyanogenmod.permission.WRITE_SETTINGS";
|
||||||
field public static final java.lang.String WRITE_THEMES = "cyanogenmod.permission.WRITE_THEMES";
|
field public static final java.lang.String WRITE_THEMES = "cyanogenmod.permission.WRITE_THEMES";
|
||||||
|
field public static final java.lang.String WRITE_WEATHER = "cyanogenmod.permission.WRITE_WEATHER";
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class R {
|
public final class R {
|
||||||
|
@ -1043,6 +1047,94 @@ package cyanogenmod.providers {
|
||||||
field public static final int UPDATING = 2; // 0x2
|
field public static final int UPDATING = 2; // 0x2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class WeatherContract {
|
||||||
|
ctor public WeatherContract();
|
||||||
|
field public static final java.lang.String AUTHORITY = "com.cyanogenmod.weather";
|
||||||
|
field public static final android.net.Uri AUTHORITY_URI;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class WeatherContract.WeatherColumns {
|
||||||
|
ctor public WeatherContract.WeatherColumns();
|
||||||
|
field public static final android.net.Uri CONTENT_URI;
|
||||||
|
field public static final android.net.Uri CURRENT_AND_FORECAST_WEATHER_URI;
|
||||||
|
field public static final java.lang.String CURRENT_CITY = "city";
|
||||||
|
field public static final java.lang.String CURRENT_CITY_ID = "city_id";
|
||||||
|
field public static final java.lang.String CURRENT_CONDITION = "condition";
|
||||||
|
field public static final java.lang.String CURRENT_CONDITION_CODE = "condition_code";
|
||||||
|
field public static final java.lang.String CURRENT_HUMIDITY = "humidity";
|
||||||
|
field public static final java.lang.String CURRENT_TEMPERATURE = "temperature";
|
||||||
|
field public static final java.lang.String CURRENT_TEMPERATURE_UNIT = "temperature_unit";
|
||||||
|
field public static final java.lang.String CURRENT_TIMESTAMP = "timestamp";
|
||||||
|
field public static final android.net.Uri CURRENT_WEATHER_URI;
|
||||||
|
field public static final java.lang.String CURRENT_WIND_DIRECTION = "wind_direction";
|
||||||
|
field public static final java.lang.String CURRENT_WIND_SPEED = "wind_speed";
|
||||||
|
field public static final java.lang.String CURRENT_WIND_SPEED_UNIT = "wind_speed_unit";
|
||||||
|
field public static final java.lang.String FORECAST_CONDITION = "forecast_condition";
|
||||||
|
field public static final java.lang.String FORECAST_CONDITION_CODE = "forecast_condition_code";
|
||||||
|
field public static final java.lang.String FORECAST_HIGH = "forecast_high";
|
||||||
|
field public static final java.lang.String FORECAST_LOW = "forecast_low";
|
||||||
|
field public static final android.net.Uri FORECAST_WEATHER_URI;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class WeatherContract.WeatherColumns.TempUnit {
|
||||||
|
field public static final int CELSIUS = 1; // 0x1
|
||||||
|
field public static final int FAHRENHEIT = 2; // 0x2
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class WeatherContract.WeatherColumns.WeatherCode {
|
||||||
|
field public static final int BLOWING_SNOW = 14; // 0xe
|
||||||
|
field public static final int BLUSTERY = 22; // 0x16
|
||||||
|
field public static final int CLEAR_NIGHT = 30; // 0x1e
|
||||||
|
field public static final int CLOUDY = 25; // 0x19
|
||||||
|
field public static final int COLD = 24; // 0x18
|
||||||
|
field public static final int DRIZZLE = 9; // 0x9
|
||||||
|
field public static final int DUST = 18; // 0x12
|
||||||
|
field public static final int FAIR_DAY = 33; // 0x21
|
||||||
|
field public static final int FAIR_NIGHT = 32; // 0x20
|
||||||
|
field public static final int FOGGY = 19; // 0x13
|
||||||
|
field public static final int FREEZING_DRIZZLE = 8; // 0x8
|
||||||
|
field public static final int FREEZING_RAIN = 10; // 0xa
|
||||||
|
field public static final int HAIL = 16; // 0x10
|
||||||
|
field public static final int HAZE = 20; // 0x14
|
||||||
|
field public static final int HEAVY_SNOW = 39; // 0x27
|
||||||
|
field public static final int HOT = 35; // 0x23
|
||||||
|
field public static final int HURRICANE = 2; // 0x2
|
||||||
|
field public static final int ISOLATED_THUNDERSHOWERS = 44; // 0x2c
|
||||||
|
field public static final int ISOLATED_THUNDERSTORMS = 36; // 0x24
|
||||||
|
field public static final int LIGHT_SNOW_SHOWERS = 13; // 0xd
|
||||||
|
field public static final int MIXED_RAIN_AND_HAIL = 34; // 0x22
|
||||||
|
field public static final int MIXED_RAIN_AND_SLEET = 6; // 0x6
|
||||||
|
field public static final int MIXED_RAIN_AND_SNOW = 5; // 0x5
|
||||||
|
field public static final int MIXED_SNOW_AND_SLEET = 7; // 0x7
|
||||||
|
field public static final int MOSTLY_CLOUDY_DAY = 27; // 0x1b
|
||||||
|
field public static final int MOSTLY_CLOUDY_NIGHT = 26; // 0x1a
|
||||||
|
field public static final int NOT_AVAILABLE = 3200; // 0xc80
|
||||||
|
field public static final int PARTLY_CLOUDY = 41; // 0x29
|
||||||
|
field public static final int PARTLY_CLOUDY_DAY = 29; // 0x1d
|
||||||
|
field public static final int PARTLY_CLOUDY_NIGHT = 28; // 0x1c
|
||||||
|
field public static final int SCATTERED_SHOWERS = 38; // 0x26
|
||||||
|
field public static final int SCATTERED_SNOW_SHOWERS = 40; // 0x28
|
||||||
|
field public static final int SCATTERED_THUNDERSTORMS = 37; // 0x25
|
||||||
|
field public static final int SEVERE_THUNDERSTORMS = 3; // 0x3
|
||||||
|
field public static final int SHOWERS = 11; // 0xb
|
||||||
|
field public static final int SLEET = 17; // 0x11
|
||||||
|
field public static final int SMOKY = 21; // 0x15
|
||||||
|
field public static final int SNOW = 15; // 0xf
|
||||||
|
field public static final int SNOW_FLURRIES = 12; // 0xc
|
||||||
|
field public static final int SNOW_SHOWERS = 43; // 0x2b
|
||||||
|
field public static final int SUNNY = 31; // 0x1f
|
||||||
|
field public static final int THUNDERSHOWER = 42; // 0x2a
|
||||||
|
field public static final int THUNDERSTORMS = 4; // 0x4
|
||||||
|
field public static final int TORNADO = 0; // 0x0
|
||||||
|
field public static final int TROPICAL_STORM = 1; // 0x1
|
||||||
|
field public static final int WINDY = 23; // 0x17
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class WeatherContract.WeatherColumns.WindSpeedUnit {
|
||||||
|
field public static final int KPH = 1; // 0x1
|
||||||
|
field public static final int MPH = 2; // 0x2
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
package cyanogenmod.themes {
|
package cyanogenmod.themes {
|
||||||
|
@ -1155,3 +1247,147 @@ package cyanogenmod.util {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
package cyanogenmod.weather {
|
||||||
|
|
||||||
|
public class CMWeatherManager {
|
||||||
|
method public java.lang.String getActiveWeatherServiceProviderLabel();
|
||||||
|
method public static cyanogenmod.weather.CMWeatherManager getInstance(android.content.Context);
|
||||||
|
method public void lookupCity(java.lang.String, cyanogenmod.weather.CMWeatherManager.LookupCityRequestListener);
|
||||||
|
method public void registerWeatherServiceProviderChangeListener(cyanogenmod.weather.CMWeatherManager.WeatherServiceProviderChangeListener);
|
||||||
|
method public void requestWeatherUpdate(android.location.Location, cyanogenmod.weather.CMWeatherManager.WeatherUpdateRequestListener);
|
||||||
|
method public void requestWeatherUpdate(cyanogenmod.weather.WeatherLocation, cyanogenmod.weather.CMWeatherManager.WeatherUpdateRequestListener);
|
||||||
|
method public void unregisterWeatherServiceProviderChangeListener(cyanogenmod.weather.CMWeatherManager.WeatherServiceProviderChangeListener);
|
||||||
|
field public static final int WEATHER_REQUEST_ALREADY_IN_PROGRESS = -3; // 0xfffffffd
|
||||||
|
field public static final int WEATHER_REQUEST_COMPLETED = 1; // 0x1
|
||||||
|
field public static final int WEATHER_REQUEST_FAILED = -2; // 0xfffffffe
|
||||||
|
field public static final int WEATHER_REQUEST_SUBMITTED_TOO_SOON = -1; // 0xffffffff
|
||||||
|
}
|
||||||
|
|
||||||
|
public static abstract interface CMWeatherManager.LookupCityRequestListener {
|
||||||
|
method public abstract void onLookupCityRequestCompleted(java.util.ArrayList<cyanogenmod.weather.WeatherLocation>);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static abstract interface CMWeatherManager.WeatherServiceProviderChangeListener {
|
||||||
|
method public abstract void onWeatherServiceProviderChanged(java.lang.String);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static abstract interface CMWeatherManager.WeatherUpdateRequestListener {
|
||||||
|
method public abstract void onWeatherRequestCompleted(int, cyanogenmod.weather.WeatherInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class RequestInfo implements android.os.Parcelable {
|
||||||
|
method public int describeContents();
|
||||||
|
method public java.lang.String getCityName();
|
||||||
|
method public android.location.Location getLocation();
|
||||||
|
method public int getRequestType();
|
||||||
|
method public int getTemperatureUnit();
|
||||||
|
method public cyanogenmod.weather.WeatherLocation getWeatherLocation();
|
||||||
|
method public void writeToParcel(android.os.Parcel, int);
|
||||||
|
field public static final android.os.Parcelable.Creator<cyanogenmod.weather.RequestInfo> CREATOR;
|
||||||
|
field public static final int TYPE_GEO_LOCATION_REQ = 1; // 0x1
|
||||||
|
field public static final int TYPE_LOOKUP_CITY_NAME_REQ = 3; // 0x3
|
||||||
|
field public static final int TYPE_WEATHER_LOCATION_REQ = 2; // 0x2
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class WeatherInfo implements android.os.Parcelable {
|
||||||
|
method public int describeContents();
|
||||||
|
method public java.lang.String getCity();
|
||||||
|
method public java.lang.String getCityId();
|
||||||
|
method public int getConditionCode();
|
||||||
|
method public java.util.ArrayList<cyanogenmod.weather.WeatherInfo.DayForecast> getForecasts();
|
||||||
|
method public float getHumidity();
|
||||||
|
method public float getTemperature();
|
||||||
|
method public int getTemperatureUnit();
|
||||||
|
method public long getTimestamp();
|
||||||
|
method public float getWindDirection();
|
||||||
|
method public float getWindSpeed();
|
||||||
|
method public int getWindSpeedUnit();
|
||||||
|
method public void writeToParcel(android.os.Parcel, int);
|
||||||
|
field public static final android.os.Parcelable.Creator<cyanogenmod.weather.WeatherInfo> CREATOR;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class WeatherInfo.Builder {
|
||||||
|
ctor public WeatherInfo.Builder(long);
|
||||||
|
method public cyanogenmod.weather.WeatherInfo build();
|
||||||
|
method public cyanogenmod.weather.WeatherInfo.Builder setCity(java.lang.String, java.lang.String);
|
||||||
|
method public cyanogenmod.weather.WeatherInfo.Builder setForecast(java.util.ArrayList<cyanogenmod.weather.WeatherInfo.DayForecast>);
|
||||||
|
method public cyanogenmod.weather.WeatherInfo.Builder setHumidity(float);
|
||||||
|
method public cyanogenmod.weather.WeatherInfo.Builder setTemperature(float, int);
|
||||||
|
method public cyanogenmod.weather.WeatherInfo.Builder setWeatherCondition(int);
|
||||||
|
method public cyanogenmod.weather.WeatherInfo.Builder setWind(float, float, int);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class WeatherInfo.DayForecast implements android.os.Parcelable {
|
||||||
|
method public int describeContents();
|
||||||
|
method public int getConditionCode();
|
||||||
|
method public float getHigh();
|
||||||
|
method public float getLow();
|
||||||
|
method public void writeToParcel(android.os.Parcel, int);
|
||||||
|
field public static final android.os.Parcelable.Creator<cyanogenmod.weather.WeatherInfo.DayForecast> CREATOR;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class WeatherInfo.DayForecast.Builder {
|
||||||
|
ctor public WeatherInfo.DayForecast.Builder();
|
||||||
|
method public cyanogenmod.weather.WeatherInfo.DayForecast build();
|
||||||
|
method public cyanogenmod.weather.WeatherInfo.DayForecast.Builder setHigh(float);
|
||||||
|
method public cyanogenmod.weather.WeatherInfo.DayForecast.Builder setLow(float);
|
||||||
|
method public cyanogenmod.weather.WeatherInfo.DayForecast.Builder setWeatherCondition(int);
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class WeatherLocation implements android.os.Parcelable {
|
||||||
|
method public int describeContents();
|
||||||
|
method public java.lang.String getCity();
|
||||||
|
method public java.lang.String getCityId();
|
||||||
|
method public java.lang.String getCountry();
|
||||||
|
method public java.lang.String getCountryId();
|
||||||
|
method public java.lang.String getPostalCode();
|
||||||
|
method public void writeToParcel(android.os.Parcel, int);
|
||||||
|
field public static final android.os.Parcelable.Creator<cyanogenmod.weather.WeatherLocation> CREATOR;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class WeatherLocation.Builder {
|
||||||
|
ctor public WeatherLocation.Builder(java.lang.String, java.lang.String);
|
||||||
|
method public cyanogenmod.weather.WeatherLocation build();
|
||||||
|
method public cyanogenmod.weather.WeatherLocation.Builder setCountry(java.lang.String, java.lang.String);
|
||||||
|
method public cyanogenmod.weather.WeatherLocation.Builder setPostalCode(java.lang.String);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
package cyanogenmod.weatherservice {
|
||||||
|
|
||||||
|
public final class ServiceRequest {
|
||||||
|
method public void complete(cyanogenmod.weatherservice.ServiceRequestResult);
|
||||||
|
method public void fail();
|
||||||
|
method public cyanogenmod.weather.RequestInfo getRequestInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class ServiceRequestResult implements android.os.Parcelable {
|
||||||
|
method public int describeContents();
|
||||||
|
method public java.util.ArrayList<cyanogenmod.weather.WeatherLocation> getLocationLookupList();
|
||||||
|
method public cyanogenmod.weather.WeatherInfo getWeatherInfo();
|
||||||
|
method public void writeToParcel(android.os.Parcel, int);
|
||||||
|
field public static final android.os.Parcelable.Creator<cyanogenmod.weatherservice.ServiceRequestResult> CREATOR;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ServiceRequestResult.Builder {
|
||||||
|
ctor public ServiceRequestResult.Builder();
|
||||||
|
method public cyanogenmod.weatherservice.ServiceRequestResult build();
|
||||||
|
method public cyanogenmod.weatherservice.ServiceRequestResult.Builder setLocationLookupList(java.util.ArrayList<cyanogenmod.weather.WeatherLocation>);
|
||||||
|
method public cyanogenmod.weatherservice.ServiceRequestResult.Builder setWeatherInfo(cyanogenmod.weather.WeatherInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract class WeatherProviderService extends android.app.Service {
|
||||||
|
ctor public WeatherProviderService();
|
||||||
|
method protected final void attachBaseContext(android.content.Context);
|
||||||
|
method public final android.os.IBinder onBind(android.content.Intent);
|
||||||
|
method protected void onConnected();
|
||||||
|
method protected void onDisconnected();
|
||||||
|
method protected abstract void onRequestCancelled(cyanogenmod.weatherservice.ServiceRequest);
|
||||||
|
method protected abstract void onRequestSubmitted(cyanogenmod.weatherservice.ServiceRequest);
|
||||||
|
field public static final java.lang.String SERVICE_INTERFACE = "cyanogenmod.weatherservice.WeatherProviderService";
|
||||||
|
field public static final java.lang.String SERVICE_META_DATA = "cyanogenmod.weatherservice";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,545 @@
|
||||||
|
/*
|
||||||
|
* 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.platform.internal;
|
||||||
|
|
||||||
|
import android.app.AppGlobals;
|
||||||
|
import android.content.ComponentName;
|
||||||
|
import android.content.ContentValues;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.ServiceConnection;
|
||||||
|
import android.content.pm.IPackageManager;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.content.pm.ResolveInfo;
|
||||||
|
import android.database.ContentObserver;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Binder;
|
||||||
|
import android.os.IBinder;
|
||||||
|
import android.os.RemoteCallbackList;
|
||||||
|
import android.os.RemoteException;
|
||||||
|
import android.os.SystemClock;
|
||||||
|
import android.os.UserHandle;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.util.Slog;
|
||||||
|
import com.android.internal.content.PackageMonitor;
|
||||||
|
import com.android.internal.os.BackgroundThread;
|
||||||
|
import com.android.server.SystemService;
|
||||||
|
import cyanogenmod.app.CMContextConstants;
|
||||||
|
import cyanogenmod.platform.Manifest;
|
||||||
|
import cyanogenmod.providers.CMSettings;
|
||||||
|
import cyanogenmod.providers.WeatherContract.WeatherColumns;
|
||||||
|
import cyanogenmod.weather.CMWeatherManager;
|
||||||
|
import cyanogenmod.weather.ICMWeatherManager;
|
||||||
|
import cyanogenmod.weather.IRequestInfoListener;
|
||||||
|
import cyanogenmod.weather.IWeatherServiceProviderChangeListener;
|
||||||
|
import cyanogenmod.weather.RequestInfo;
|
||||||
|
import cyanogenmod.weather.WeatherInfo;
|
||||||
|
import cyanogenmod.weatherservice.IWeatherProviderService;
|
||||||
|
import cyanogenmod.weatherservice.IWeatherProviderServiceClient;
|
||||||
|
import cyanogenmod.weatherservice.ServiceRequestResult;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class CMWeatherManagerService extends SystemService{
|
||||||
|
|
||||||
|
private static final String TAG = CMWeatherManagerService.class.getSimpleName();
|
||||||
|
/**
|
||||||
|
* How long clients will have to wait until a new weather update request can be honored
|
||||||
|
* TODO Allow weather service providers to specify this threshold
|
||||||
|
*/
|
||||||
|
private static final long REQUEST_THRESHOLD_MILLIS = 1000L * 60L * 10L;
|
||||||
|
|
||||||
|
private IWeatherProviderService mWeatherProviderService;
|
||||||
|
private boolean mIsWeatherProviderServiceBound;
|
||||||
|
private long mLastWeatherUpdateRequestTimestamp = -REQUEST_THRESHOLD_MILLIS;
|
||||||
|
private boolean mIsProcessingRequest = false;
|
||||||
|
private Object mMutex = new Object();
|
||||||
|
private Context mContext;
|
||||||
|
private final RemoteCallbackList<IWeatherServiceProviderChangeListener> mProviderChangeListeners
|
||||||
|
= new RemoteCallbackList<>();
|
||||||
|
private volatile boolean mReconnectedDuePkgModified = false;
|
||||||
|
|
||||||
|
private final IWeatherProviderServiceClient mServiceClient
|
||||||
|
= new IWeatherProviderServiceClient.Stub() {
|
||||||
|
@Override
|
||||||
|
public void setServiceRequestState(RequestInfo requestInfo,
|
||||||
|
ServiceRequestResult result, int state) {
|
||||||
|
synchronized (mMutex) {
|
||||||
|
|
||||||
|
if (requestInfo == null) {
|
||||||
|
//Invalid request info object
|
||||||
|
mIsProcessingRequest = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final IRequestInfoListener listener = requestInfo.getRequestListener();
|
||||||
|
final int requestType = requestInfo.getRequestType();
|
||||||
|
|
||||||
|
switch (requestType) {
|
||||||
|
case RequestInfo.TYPE_GEO_LOCATION_REQ:
|
||||||
|
case RequestInfo.TYPE_WEATHER_LOCATION_REQ:
|
||||||
|
if (!isValidRequestInfoState(requestType, state)) {
|
||||||
|
//We received an invalid state, silently disregard the request
|
||||||
|
mIsProcessingRequest = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
WeatherInfo weatherInfo = null;
|
||||||
|
if (state == CMWeatherManager.WEATHER_REQUEST_COMPLETED) {
|
||||||
|
weatherInfo = (result != null) ? result.getWeatherInfo() : null;
|
||||||
|
if (weatherInfo == null) {
|
||||||
|
//This should never happen! WEATHER_REQUEST_COMPLETED is set
|
||||||
|
//only if the weatherinfo object was not null when the request
|
||||||
|
//was marked as completed
|
||||||
|
state = CMWeatherManager.WEATHER_REQUEST_FAILED;
|
||||||
|
} else {
|
||||||
|
if (!requestInfo.isQueryOnlyWeatherRequest()) {
|
||||||
|
final long identity = Binder.clearCallingIdentity();
|
||||||
|
try {
|
||||||
|
updateWeatherInfoLocked(weatherInfo);
|
||||||
|
} finally {
|
||||||
|
Binder.restoreCallingIdentity(identity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isValidListener(listener)) {
|
||||||
|
try {
|
||||||
|
listener.onWeatherRequestCompleted(requestInfo, state, weatherInfo);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case RequestInfo.TYPE_LOOKUP_CITY_NAME_REQ:
|
||||||
|
if (isValidListener(listener)) {
|
||||||
|
try {
|
||||||
|
//Result might be null if the provider marked the request as failed
|
||||||
|
listener.onLookupCityRequestCompleted(requestInfo,
|
||||||
|
result != null ? result.getLocationLookupList() : null);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
mIsProcessingRequest = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private boolean isValidRequestInfoState(int requestType, int state) {
|
||||||
|
switch (requestType) {
|
||||||
|
case RequestInfo.TYPE_GEO_LOCATION_REQ:
|
||||||
|
case RequestInfo.TYPE_WEATHER_LOCATION_REQ:
|
||||||
|
switch (state) {
|
||||||
|
case CMWeatherManager.WEATHER_REQUEST_COMPLETED:
|
||||||
|
case CMWeatherManager.WEATHER_REQUEST_SUBMITTED_TOO_SOON:
|
||||||
|
case CMWeatherManager.WEATHER_REQUEST_FAILED:
|
||||||
|
case CMWeatherManager.WEATHER_REQUEST_ALREADY_IN_PROGRESS:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
case RequestInfo.TYPE_LOOKUP_CITY_NAME_REQ:
|
||||||
|
switch (state) {
|
||||||
|
case CMWeatherManager.LOOKUP_REQUEST_COMPLETED:
|
||||||
|
case CMWeatherManager.LOOKUP_REQUEST_FAILED:
|
||||||
|
case CMWeatherManager.LOOKUP_REQUEST_NO_MATCH_FOUND:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isValidListener(IRequestInfoListener listener) {
|
||||||
|
return (listener != null && listener.asBinder().pingBinder());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void enforcePermission() {
|
||||||
|
mContext.enforceCallingOrSelfPermission(
|
||||||
|
Manifest.permission.ACCESS_WEATHER_MANAGER, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final IBinder mService = new ICMWeatherManager.Stub() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateWeather(RequestInfo info) {
|
||||||
|
enforcePermission();
|
||||||
|
processWeatherUpdateRequest(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void lookupCity(RequestInfo info) {
|
||||||
|
enforcePermission();
|
||||||
|
processCityNameLookupRequest(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void registerWeatherServiceProviderChangeListener(
|
||||||
|
IWeatherServiceProviderChangeListener listener) {
|
||||||
|
enforcePermission();
|
||||||
|
mProviderChangeListeners.register(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void unregisterWeatherServiceProviderChangeListener(
|
||||||
|
IWeatherServiceProviderChangeListener listener) {
|
||||||
|
enforcePermission();
|
||||||
|
mProviderChangeListeners.unregister(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getActiveWeatherServiceProviderLabel() {
|
||||||
|
enforcePermission();
|
||||||
|
final long identity = Binder.clearCallingIdentity();
|
||||||
|
try {
|
||||||
|
String enabledProviderService = CMSettings.Secure.getString(
|
||||||
|
mContext.getContentResolver(), CMSettings.Secure.WEATHER_PROVIDER_SERVICE);
|
||||||
|
if (enabledProviderService != null) {
|
||||||
|
return getComponentLabel(
|
||||||
|
ComponentName.unflattenFromString(enabledProviderService));
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
Binder.restoreCallingIdentity(identity);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private String getComponentLabel(ComponentName componentName) {
|
||||||
|
final PackageManager pm = mContext.getPackageManager();
|
||||||
|
Intent intent = new Intent().setComponent(componentName);
|
||||||
|
ResolveInfo resolveInfo = pm.resolveService(intent,
|
||||||
|
PackageManager.GET_SERVICES);
|
||||||
|
if (resolveInfo != null) {
|
||||||
|
return resolveInfo.loadLabel(pm).toString();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CMWeatherManagerService(Context context) {
|
||||||
|
super(context);
|
||||||
|
mContext = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStart() {
|
||||||
|
publishBinderService(CMContextConstants.CM_WEATHER_SERVICE, mService);
|
||||||
|
registerPackageMonitor();
|
||||||
|
registerSettingsObserver();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBootPhase(int phase) {
|
||||||
|
if (phase == PHASE_ACTIVITY_MANAGER_READY) {
|
||||||
|
bindActiveWeatherProviderService();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void bindActiveWeatherProviderService() {
|
||||||
|
String activeProviderService = CMSettings.Secure.getString(mContext.getContentResolver(),
|
||||||
|
CMSettings.Secure.WEATHER_PROVIDER_SERVICE);
|
||||||
|
if (activeProviderService != null) {
|
||||||
|
if (!getContext().bindServiceAsUser(new Intent().setComponent(
|
||||||
|
ComponentName.unflattenFromString(activeProviderService)),
|
||||||
|
mWeatherServiceProviderConnection, Context.BIND_AUTO_CREATE,
|
||||||
|
UserHandle.CURRENT)) {
|
||||||
|
Slog.w(TAG, "Failed to bind service " + activeProviderService);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean canProcessWeatherUpdateRequest(RequestInfo info, long currentTimeMillis) {
|
||||||
|
final IRequestInfoListener listener = info.getRequestListener();
|
||||||
|
|
||||||
|
if ((mLastWeatherUpdateRequestTimestamp + REQUEST_THRESHOLD_MILLIS) > currentTimeMillis) {
|
||||||
|
if (listener != null && listener.asBinder().pingBinder()) {
|
||||||
|
try {
|
||||||
|
listener.onWeatherRequestCompleted(info,
|
||||||
|
CMWeatherManager.WEATHER_REQUEST_SUBMITTED_TOO_SOON, null);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mIsProcessingRequest) {
|
||||||
|
if (listener != null && listener.asBinder().pingBinder()) {
|
||||||
|
try {
|
||||||
|
listener.onWeatherRequestCompleted(info,
|
||||||
|
CMWeatherManager.WEATHER_REQUEST_ALREADY_IN_PROGRESS, null);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mIsWeatherProviderServiceBound) {
|
||||||
|
if (listener != null && listener.asBinder().pingBinder()) {
|
||||||
|
try {
|
||||||
|
listener.onWeatherRequestCompleted(info,
|
||||||
|
CMWeatherManager.WEATHER_REQUEST_FAILED, null);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void processWeatherUpdateRequest(RequestInfo info) {
|
||||||
|
final long currentTimeMillis = SystemClock.elapsedRealtime();
|
||||||
|
|
||||||
|
if (!canProcessWeatherUpdateRequest(info, currentTimeMillis)) return;
|
||||||
|
|
||||||
|
mLastWeatherUpdateRequestTimestamp = currentTimeMillis;
|
||||||
|
mIsProcessingRequest = true;
|
||||||
|
try {
|
||||||
|
mWeatherProviderService.processWeatherUpdateRequest(info);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processCityNameLookupRequest(RequestInfo info) {
|
||||||
|
if (!mIsWeatherProviderServiceBound) {
|
||||||
|
final IRequestInfoListener listener = info.getRequestListener();
|
||||||
|
if (listener != null && listener.asBinder().pingBinder()) {
|
||||||
|
try {
|
||||||
|
listener.onLookupCityRequestCompleted(info, null);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
mWeatherProviderService.processCityNameLookupRequest(info);
|
||||||
|
} catch(RemoteException e){
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ServiceConnection mWeatherServiceProviderConnection = new ServiceConnection() {
|
||||||
|
@Override
|
||||||
|
public void onServiceConnected(ComponentName name, IBinder service) {
|
||||||
|
mWeatherProviderService = IWeatherProviderService.Stub.asInterface(service);
|
||||||
|
mIsWeatherProviderServiceBound = true;
|
||||||
|
try {
|
||||||
|
mWeatherProviderService.setServiceClient(mServiceClient);
|
||||||
|
} catch(RemoteException e) {
|
||||||
|
}
|
||||||
|
if (!mReconnectedDuePkgModified) {
|
||||||
|
notifyProviderChanged(name);
|
||||||
|
}
|
||||||
|
mReconnectedDuePkgModified = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onServiceDisconnected(ComponentName name) {
|
||||||
|
mWeatherProviderService = null;
|
||||||
|
mIsWeatherProviderServiceBound = false;
|
||||||
|
//We can't talk to the current service anyway...
|
||||||
|
mIsProcessingRequest = false;
|
||||||
|
Slog.d(TAG, "Connection with " + name.flattenToString() + " has been closed");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private void notifyProviderChanged(ComponentName name) {
|
||||||
|
String providerName = null;
|
||||||
|
if (name != null) {
|
||||||
|
providerName = getComponentLabel(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
int N = mProviderChangeListeners.beginBroadcast();
|
||||||
|
for (int indx = 0; indx < N; indx++) {
|
||||||
|
IWeatherServiceProviderChangeListener listener
|
||||||
|
= mProviderChangeListeners.getBroadcastItem(indx);
|
||||||
|
try {
|
||||||
|
listener.onWeatherServiceProviderChanged(providerName);
|
||||||
|
} catch (RemoteException e){
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mProviderChangeListeners.finishBroadcast();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean updateWeatherInfoLocked(WeatherInfo wi) {
|
||||||
|
final int size = wi.getForecasts().size() + 1;
|
||||||
|
List<ContentValues> contentValuesList = new ArrayList<>(size);
|
||||||
|
ContentValues contentValues = new ContentValues();
|
||||||
|
|
||||||
|
contentValues.put(WeatherColumns.CURRENT_CITY_ID, wi.getCityId());
|
||||||
|
contentValues.put(WeatherColumns.CURRENT_CITY, wi.getCity());
|
||||||
|
contentValues.put(WeatherColumns.CURRENT_CONDITION_CODE, wi.getConditionCode());
|
||||||
|
contentValues.put(WeatherColumns.CURRENT_HUMIDITY, wi.getHumidity());
|
||||||
|
contentValues.put(WeatherColumns.CURRENT_TEMPERATURE, wi.getTemperature());
|
||||||
|
contentValues.put(WeatherColumns.CURRENT_TEMPERATURE_UNIT, wi.getTemperatureUnit());
|
||||||
|
contentValues.put(WeatherColumns.CURRENT_TIMESTAMP, wi.getTimestamp());
|
||||||
|
contentValues.put(WeatherColumns.CURRENT_WIND_DIRECTION, wi.getWindDirection());
|
||||||
|
contentValues.put(WeatherColumns.CURRENT_WIND_SPEED, wi.getWindSpeed());
|
||||||
|
contentValues.put(WeatherColumns.CURRENT_WIND_SPEED_UNIT, wi.getWindSpeedUnit());
|
||||||
|
contentValuesList.add(contentValues);
|
||||||
|
|
||||||
|
for (WeatherInfo.DayForecast df : wi.getForecasts()) {
|
||||||
|
contentValues = new ContentValues();
|
||||||
|
contentValues.put(WeatherColumns.FORECAST_LOW, df.getLow());
|
||||||
|
contentValues.put(WeatherColumns.FORECAST_HIGH, df.getHigh());
|
||||||
|
contentValues.put(WeatherColumns.FORECAST_CONDITION_CODE, df.getConditionCode());
|
||||||
|
contentValuesList.add(contentValues);
|
||||||
|
}
|
||||||
|
|
||||||
|
ContentValues[] updateValues = new ContentValues[contentValuesList.size()];
|
||||||
|
if (size != getContext().getContentResolver().bulkInsert(
|
||||||
|
WeatherColumns.CURRENT_AND_FORECAST_WEATHER_URI,
|
||||||
|
contentValuesList.toArray(updateValues))) {
|
||||||
|
Slog.w(TAG, "Failed to update the weather content provider");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void registerPackageMonitor() {
|
||||||
|
PackageMonitor monitor = new PackageMonitor() {
|
||||||
|
@Override
|
||||||
|
public void onPackageModified(String packageName) {
|
||||||
|
String enabledProviderService = CMSettings.Secure.getString(
|
||||||
|
mContext.getContentResolver(), CMSettings.Secure.WEATHER_PROVIDER_SERVICE);
|
||||||
|
if (enabledProviderService == null) return;
|
||||||
|
ComponentName cn = ComponentName.unflattenFromString(enabledProviderService);
|
||||||
|
if (!TextUtils.equals(packageName, cn.getPackageName())) return;
|
||||||
|
|
||||||
|
if (cn.getPackageName().equals(packageName) && !mIsWeatherProviderServiceBound) {
|
||||||
|
//We were disconnected because the whole package changed
|
||||||
|
//(most likely remove->install)
|
||||||
|
if (!getContext().bindServiceAsUser(new Intent().setComponent(cn),
|
||||||
|
mWeatherServiceProviderConnection, Context.BIND_AUTO_CREATE,
|
||||||
|
UserHandle.CURRENT)) {
|
||||||
|
CMSettings.Secure.putStringForUser( mContext.getContentResolver(),
|
||||||
|
CMSettings.Secure.WEATHER_PROVIDER_SERVICE, null,
|
||||||
|
getChangingUserId());
|
||||||
|
Slog.w(TAG, "Unable to rebind " + cn.flattenToString() + " after receiving"
|
||||||
|
+ " package modified notification. Settings updated.");
|
||||||
|
} else {
|
||||||
|
mReconnectedDuePkgModified = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onPackageChanged(String packageName, int uid, String[] components) {
|
||||||
|
String enabledProviderService = CMSettings.Secure.getString(
|
||||||
|
mContext.getContentResolver(), CMSettings.Secure.WEATHER_PROVIDER_SERVICE);
|
||||||
|
if (enabledProviderService == null) return false;
|
||||||
|
|
||||||
|
boolean packageChanged = false;
|
||||||
|
ComponentName cn = ComponentName.unflattenFromString(enabledProviderService);
|
||||||
|
for (String component : components) {
|
||||||
|
if (cn.getPackageName().equals(component)) {
|
||||||
|
packageChanged = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (packageChanged) {
|
||||||
|
try {
|
||||||
|
final IPackageManager pm = AppGlobals.getPackageManager();
|
||||||
|
final int enabled = pm.getApplicationEnabledSetting(packageName,
|
||||||
|
getChangingUserId());
|
||||||
|
if (enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED
|
||||||
|
|| enabled == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
disconnectClient();
|
||||||
|
//The package is not enabled so we can't use it anymore
|
||||||
|
CMSettings.Secure.putStringForUser(
|
||||||
|
mContext.getContentResolver(),
|
||||||
|
CMSettings.Secure.WEATHER_PROVIDER_SERVICE, null,
|
||||||
|
getChangingUserId());
|
||||||
|
Slog.w(TAG, "Active provider " + cn.flattenToString() + " disabled");
|
||||||
|
notifyProviderChanged(null);
|
||||||
|
}
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
Slog.d(TAG, "Exception trying to look up app enabled settings ", e);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
// Really?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPackageRemoved(String packageName, int uid) {
|
||||||
|
String enabledProviderService = CMSettings.Secure.getString(
|
||||||
|
mContext.getContentResolver(), CMSettings.Secure.WEATHER_PROVIDER_SERVICE);
|
||||||
|
if (enabledProviderService == null) return;
|
||||||
|
|
||||||
|
ComponentName cn = ComponentName.unflattenFromString(enabledProviderService);
|
||||||
|
if (!TextUtils.equals(packageName, cn.getPackageName())) return;
|
||||||
|
|
||||||
|
disconnectClient();
|
||||||
|
CMSettings.Secure.putStringForUser(
|
||||||
|
mContext.getContentResolver(), CMSettings.Secure.WEATHER_PROVIDER_SERVICE,
|
||||||
|
null, getChangingUserId());
|
||||||
|
notifyProviderChanged(null);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
monitor.register(mContext, BackgroundThread.getHandler().getLooper(), UserHandle.ALL, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void registerSettingsObserver() {
|
||||||
|
final Uri enabledWeatherProviderServiceUri = CMSettings.Secure.getUriFor(
|
||||||
|
CMSettings.Secure.WEATHER_PROVIDER_SERVICE);
|
||||||
|
ContentObserver observer = new ContentObserver(BackgroundThread.getHandler()) {
|
||||||
|
@Override
|
||||||
|
public void onChange(boolean selfChange, Uri uri, int userId) {
|
||||||
|
if (enabledWeatherProviderServiceUri.equals(uri)) {
|
||||||
|
String activeSrvc = CMSettings.Secure.getString(mContext.getContentResolver(),
|
||||||
|
CMSettings.Secure.WEATHER_PROVIDER_SERVICE);
|
||||||
|
disconnectClient();
|
||||||
|
if (activeSrvc != null) {
|
||||||
|
ComponentName cn = ComponentName.unflattenFromString(activeSrvc);
|
||||||
|
getContext().bindServiceAsUser(new Intent().setComponent(cn),
|
||||||
|
mWeatherServiceProviderConnection, Context.BIND_AUTO_CREATE,
|
||||||
|
UserHandle.CURRENT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
mContext.getContentResolver().registerContentObserver(enabledWeatherProviderServiceUri,
|
||||||
|
false, observer, UserHandle.USER_ALL);
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void disconnectClient() {
|
||||||
|
if (mIsWeatherProviderServiceBound) {
|
||||||
|
if (mIsProcessingRequest) {
|
||||||
|
try {
|
||||||
|
mWeatherProviderService.cancelOngoingRequests();
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
}
|
||||||
|
mIsProcessingRequest = false;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
mWeatherProviderService.setServiceClient(null);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
}
|
||||||
|
|
||||||
|
getContext().unbindService(mWeatherServiceProviderConnection);
|
||||||
|
mIsWeatherProviderServiceBound = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -192,6 +192,33 @@
|
||||||
android:description="@string/permdesc_accessLiveLockScreenServiceProvider"
|
android:description="@string/permdesc_accessLiveLockScreenServiceProvider"
|
||||||
android:protectionLevel="signature|privileged" />
|
android:protectionLevel="signature|privileged" />
|
||||||
|
|
||||||
|
<!-- Allows an application to read the weather content from the provider-->
|
||||||
|
<permission android:name="cyanogenmod.permission.READ_WEATHER"
|
||||||
|
android:label="@string/permlab_weather_read"
|
||||||
|
android:description="@string/permdesc_weather_read"
|
||||||
|
android:protectionLevel="normal"/>
|
||||||
|
|
||||||
|
<!-- Allows an application to update the content of the weather provider
|
||||||
|
<p>Not for use by third-party applications. -->
|
||||||
|
<permission android:name="cyanogenmod.permission.WRITE_WEATHER"
|
||||||
|
android:label="@string/permlab_weather_write"
|
||||||
|
android:description="@string/permdesc_weather_write"
|
||||||
|
android:protectionLevel="signature|privileged" />
|
||||||
|
|
||||||
|
<!-- Allows an application to be identified as a weather provider service -->
|
||||||
|
<permission android:name="cyanogenmod.permission.BIND_WEATHER_PROVIDER_SERVICE"
|
||||||
|
android:label="@string/permlab_weather_bind"
|
||||||
|
android:description="@string/permdesc_weather_bind"
|
||||||
|
android:protectionLevel="signature"/>
|
||||||
|
|
||||||
|
<!-- Allows an application to access the weather service.
|
||||||
|
<p>Although the protection is normal, this permission should be required ONLY by those apps
|
||||||
|
meant to do something meaningful with the data provided by the service (LockClock, SysUI)-->
|
||||||
|
<permission android:name="cyanogenmod.permission.ACCESS_WEATHER_MANAGER"
|
||||||
|
android:label="@string/permlab_weather_access_mgr"
|
||||||
|
android:description="@string/permdesc_weather_access_mgr"
|
||||||
|
android:protectionLevel="normal"/>
|
||||||
|
|
||||||
<application android:process="system"
|
<application android:process="system"
|
||||||
android:persistent="true"
|
android:persistent="true"
|
||||||
android:hasCode="false"
|
android:hasCode="false"
|
||||||
|
|
|
@ -90,5 +90,6 @@
|
||||||
<item>org.cyanogenmod.platform.internal.ThemeManagerService</item>
|
<item>org.cyanogenmod.platform.internal.ThemeManagerService</item>
|
||||||
<item>org.cyanogenmod.platform.internal.IconCacheManagerService</item>
|
<item>org.cyanogenmod.platform.internal.IconCacheManagerService</item>
|
||||||
<item>org.cyanogenmod.platform.internal.LiveLockScreenServiceBroker</item>
|
<item>org.cyanogenmod.platform.internal.LiveLockScreenServiceBroker</item>
|
||||||
|
<item>org.cyanogenmod.platform.internal.CMWeatherManagerService</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -179,5 +179,13 @@
|
||||||
<!-- Live lock screen manager service provider permission description -->
|
<!-- Live lock screen manager service provider permission description -->
|
||||||
<string name="permdesc_accessLiveLockScreenServiceProvider">Allows a service to provide the live lock screen manager service.</string>
|
<string name="permdesc_accessLiveLockScreenServiceProvider">Allows a service to provide the live lock screen manager service.</string>
|
||||||
|
|
||||||
|
<!-- Weather Service strings -->
|
||||||
|
<string name="permlab_weather_read">read weather</string>
|
||||||
|
<string name="permdesc_weather_read">Allows an app to read content from the weather provider</string>
|
||||||
|
<string name="permlab_weather_write">update weather provider</string>
|
||||||
|
<string name="permdesc_weather_write">Allows an app to update the content of the weather provider</string>
|
||||||
|
<string name="permlab_weather_bind">binds as a weather provider service</string>
|
||||||
|
<string name="permdesc_weather_bind">Allows an app to be identified as a weather provider service</string>
|
||||||
|
<string name="permlab_weather_access_mgr">access weather service</string>
|
||||||
|
<string name="permdesc_weather_access_mgr">Allows an app to access the weather service in the system. Should never be needed for normal apps</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -119,6 +119,18 @@ public final class CMContextConstants {
|
||||||
*/
|
*/
|
||||||
public static final String CM_LIVE_LOCK_SCREEN_SERVICE = "cmlivelockscreen";
|
public static final String CM_LIVE_LOCK_SCREEN_SERVICE = "cmlivelockscreen";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use with {@link android.content.Context#getSystemService} to retrieve a
|
||||||
|
* {@link cyanogenmod.weather.CMWeatherManager} to manage the weather service
|
||||||
|
* settings and request weather updates
|
||||||
|
*
|
||||||
|
* @see android.content.Context#getSystemService
|
||||||
|
* @see cyanogenmod.weather.CMWeatherManager
|
||||||
|
*
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public static final String CM_WEATHER_SERVICE = "cmweather";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Features supported by the CMSDK.
|
* Features supported by the CMSDK.
|
||||||
*/
|
*/
|
||||||
|
@ -194,5 +206,13 @@ public final class CMContextConstants {
|
||||||
*/
|
*/
|
||||||
@SdkConstant(SdkConstant.SdkConstantType.FEATURE)
|
@SdkConstant(SdkConstant.SdkConstantType.FEATURE)
|
||||||
public static final String LIVE_LOCK_SCREEN = "org.cyanogenmod.livelockscreen";
|
public static final String LIVE_LOCK_SCREEN = "org.cyanogenmod.livelockscreen";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Feature for {@link PackageManager#getSystemAvailableFeatures} and
|
||||||
|
* {@link PackageManager#hasSystemFeature}: The device includes the cm weather weather
|
||||||
|
* service utilized by the cmsdk.
|
||||||
|
*/
|
||||||
|
@SdkConstant(SdkConstant.SdkConstantType.FEATURE)
|
||||||
|
public static final String WEATHER_SERVICES = "org.cyanogenmod.weather";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2686,6 +2686,13 @@ public final class CMSettings {
|
||||||
*/
|
*/
|
||||||
public static final String ENABLED_EVENT_LIVE_LOCKS_KEY = "live_lockscreens_events_enabled";
|
public static final String ENABLED_EVENT_LIVE_LOCKS_KEY = "live_lockscreens_events_enabled";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Current active & enabled Weather Provider Service
|
||||||
|
*
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public static final String WEATHER_PROVIDER_SERVICE = "weather_provider_service";
|
||||||
|
|
||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -0,0 +1,240 @@
|
||||||
|
/*
|
||||||
|
* 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 cyanogenmod.providers;
|
||||||
|
|
||||||
|
import android.net.Uri;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The contract between the weather provider and applications.
|
||||||
|
*/
|
||||||
|
public class WeatherContract {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The authority of the weather content provider
|
||||||
|
*/
|
||||||
|
public static final String AUTHORITY = "com.cyanogenmod.weather";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A content:// style uri to the authority for the weather provider
|
||||||
|
*/
|
||||||
|
public static final Uri AUTHORITY_URI = Uri.parse("content://" + AUTHORITY);
|
||||||
|
|
||||||
|
public static class WeatherColumns {
|
||||||
|
public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "weather");
|
||||||
|
|
||||||
|
public static final Uri CURRENT_AND_FORECAST_WEATHER_URI
|
||||||
|
= Uri.withAppendedPath(CONTENT_URI, "current_and_forecast");
|
||||||
|
public static final Uri CURRENT_WEATHER_URI
|
||||||
|
= Uri.withAppendedPath(CONTENT_URI, "current");
|
||||||
|
public static final Uri FORECAST_WEATHER_URI
|
||||||
|
= Uri.withAppendedPath(CONTENT_URI, "forecast");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A unique ID for the city. NOTE: this value fully depends on the implementation of the
|
||||||
|
* weather provider service and can potentially change when you switch providers.
|
||||||
|
* <P>Type: TEXT</P>
|
||||||
|
*/
|
||||||
|
public static final String CURRENT_CITY_ID = "city_id";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The city name
|
||||||
|
* <P>Type: TEXT</P>
|
||||||
|
*/
|
||||||
|
public static final String CURRENT_CITY = "city";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Valid {@link WeatherCode}
|
||||||
|
* <P>Type: INTEGER</P>
|
||||||
|
*/
|
||||||
|
public static final String CURRENT_CONDITION_CODE = "condition_code";
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A localized string mapped to the current weather condition code. Note that, if no
|
||||||
|
* locale is found, the string will be in english
|
||||||
|
* <P>Type: TEXT</P>
|
||||||
|
*/
|
||||||
|
public static final String CURRENT_CONDITION = "condition";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current weather temperature
|
||||||
|
* <P>Type: FLOAT</P>
|
||||||
|
*/
|
||||||
|
public static final String CURRENT_TEMPERATURE = "temperature";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The unit in which current temperature is reported
|
||||||
|
* <P>Type: INTEGER</P>
|
||||||
|
* Can be one of the following:
|
||||||
|
* <ul>
|
||||||
|
* <li>{@link TempUnit#CELSIUS}</li>
|
||||||
|
* <li>{@link TempUnit#FAHRENHEIT}</li>
|
||||||
|
* </ul>
|
||||||
|
*/
|
||||||
|
public static final String CURRENT_TEMPERATURE_UNIT = "temperature_unit";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current weather humidity
|
||||||
|
* <P>Type: FLOAT</P>
|
||||||
|
*/
|
||||||
|
public static final String CURRENT_HUMIDITY = "humidity";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current wind direction (in degrees)
|
||||||
|
* <P>Type: FLOAT</P>
|
||||||
|
*/
|
||||||
|
public static final String CURRENT_WIND_DIRECTION = "wind_direction";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current wind speed
|
||||||
|
* <P>Type: FLOAT</P>
|
||||||
|
*/
|
||||||
|
public static final String CURRENT_WIND_SPEED = "wind_speed";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The unit in which the wind speed is reported
|
||||||
|
* <P>Type: INTEGER</P>
|
||||||
|
* Can be one of the following:
|
||||||
|
* <ul>
|
||||||
|
* <li>{@link WindSpeedUnit#KPH}</li>
|
||||||
|
* <li>{@link WindSpeedUnit#MPH}</li>
|
||||||
|
* </ul>
|
||||||
|
*/
|
||||||
|
public static final String CURRENT_WIND_SPEED_UNIT = "wind_speed_unit";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The timestamp when this weather was reported
|
||||||
|
* <P>Type: LONG</P>
|
||||||
|
*/
|
||||||
|
public static final String CURRENT_TIMESTAMP = "timestamp";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The forecasted low temperature
|
||||||
|
* <P>Type: FLOAT</P>
|
||||||
|
*/
|
||||||
|
public static final String FORECAST_LOW = "forecast_low";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The forecasted high temperature
|
||||||
|
* <P>Type: FLOAT</P>
|
||||||
|
*/
|
||||||
|
public static final String FORECAST_HIGH = "forecast_high";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A localized string mapped to the forecasted weather condition code. Note that, if no
|
||||||
|
* locale is found, the string will be in english
|
||||||
|
* <P>Type: TEXT</P>
|
||||||
|
*/
|
||||||
|
public static final String FORECAST_CONDITION = "forecast_condition";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The code identifying the forecasted weather condition.
|
||||||
|
* @see #CURRENT_CONDITION_CODE
|
||||||
|
*/
|
||||||
|
public static final String FORECAST_CONDITION_CODE = "forecast_condition_code";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Temperature units
|
||||||
|
*/
|
||||||
|
public static final class TempUnit {
|
||||||
|
private TempUnit() {}
|
||||||
|
public final static int CELSIUS = 1;
|
||||||
|
public final static int FAHRENHEIT = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wind speed units
|
||||||
|
*/
|
||||||
|
public static final class WindSpeedUnit {
|
||||||
|
private WindSpeedUnit() {}
|
||||||
|
/**
|
||||||
|
* Kilometers per hour
|
||||||
|
*/
|
||||||
|
public final static int KPH = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Miles per hour
|
||||||
|
*/
|
||||||
|
public final static int MPH = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Weather condition codes
|
||||||
|
*/
|
||||||
|
public static final class WeatherCode {
|
||||||
|
private WeatherCode() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public final static int WEATHER_CODE_MIN = 0;
|
||||||
|
|
||||||
|
public final static int TORNADO = 0;
|
||||||
|
public final static int TROPICAL_STORM = 1;
|
||||||
|
public final static int HURRICANE = 2;
|
||||||
|
public final static int SEVERE_THUNDERSTORMS = 3;
|
||||||
|
public final static int THUNDERSTORMS = 4;
|
||||||
|
public final static int MIXED_RAIN_AND_SNOW = 5;
|
||||||
|
public final static int MIXED_RAIN_AND_SLEET = 6;
|
||||||
|
public final static int MIXED_SNOW_AND_SLEET = 7;
|
||||||
|
public final static int FREEZING_DRIZZLE = 8;
|
||||||
|
public final static int DRIZZLE = 9;
|
||||||
|
public final static int FREEZING_RAIN = 10;
|
||||||
|
public final static int SHOWERS = 11;
|
||||||
|
public final static int SNOW_FLURRIES = 12;
|
||||||
|
public final static int LIGHT_SNOW_SHOWERS = 13;
|
||||||
|
public final static int BLOWING_SNOW = 14;
|
||||||
|
public final static int SNOW = 15;
|
||||||
|
public final static int HAIL = 16;
|
||||||
|
public final static int SLEET = 17;
|
||||||
|
public final static int DUST = 18;
|
||||||
|
public final static int FOGGY = 19;
|
||||||
|
public final static int HAZE = 20;
|
||||||
|
public final static int SMOKY = 21;
|
||||||
|
public final static int BLUSTERY = 22;
|
||||||
|
public final static int WINDY = 23;
|
||||||
|
public final static int COLD = 24;
|
||||||
|
public final static int CLOUDY = 25;
|
||||||
|
public final static int MOSTLY_CLOUDY_NIGHT = 26;
|
||||||
|
public final static int MOSTLY_CLOUDY_DAY = 27;
|
||||||
|
public final static int PARTLY_CLOUDY_NIGHT = 28;
|
||||||
|
public final static int PARTLY_CLOUDY_DAY = 29;
|
||||||
|
public final static int CLEAR_NIGHT = 30;
|
||||||
|
public final static int SUNNY = 31;
|
||||||
|
public final static int FAIR_NIGHT = 32;
|
||||||
|
public final static int FAIR_DAY = 33;
|
||||||
|
public final static int MIXED_RAIN_AND_HAIL = 34;
|
||||||
|
public final static int HOT = 35;
|
||||||
|
public final static int ISOLATED_THUNDERSTORMS = 36;
|
||||||
|
public final static int SCATTERED_THUNDERSTORMS = 37;
|
||||||
|
public final static int SCATTERED_SHOWERS = 38;
|
||||||
|
public final static int HEAVY_SNOW = 39;
|
||||||
|
public final static int SCATTERED_SNOW_SHOWERS = 40;
|
||||||
|
public final static int PARTLY_CLOUDY = 41;
|
||||||
|
public final static int THUNDERSHOWER = 42;
|
||||||
|
public final static int SNOW_SHOWERS = 43;
|
||||||
|
public final static int ISOLATED_THUNDERSHOWERS = 44;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public final static int WEATHER_CODE_MAX = 44;
|
||||||
|
|
||||||
|
public final static int NOT_AVAILABLE = 3200;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,373 @@
|
||||||
|
/*
|
||||||
|
* 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 cyanogenmod.weather;
|
||||||
|
|
||||||
|
import android.annotation.NonNull;
|
||||||
|
import android.content.ComponentName;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.location.Location;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.IBinder;
|
||||||
|
import android.os.RemoteException;
|
||||||
|
import android.os.ServiceManager;
|
||||||
|
import android.util.ArraySet;
|
||||||
|
import cyanogenmod.app.CMContextConstants;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides access to the weather services in the device.
|
||||||
|
*/
|
||||||
|
public class CMWeatherManager {
|
||||||
|
|
||||||
|
private static ICMWeatherManager sWeatherManagerService;
|
||||||
|
private static CMWeatherManager sInstance;
|
||||||
|
private Context mContext;
|
||||||
|
private Map<RequestInfo,WeatherUpdateRequestListener> mWeatherUpdateRequestListeners
|
||||||
|
= Collections.synchronizedMap(new HashMap<RequestInfo,WeatherUpdateRequestListener>());
|
||||||
|
private Map<RequestInfo,LookupCityRequestListener> mLookupNameRequestListeners
|
||||||
|
= Collections.synchronizedMap(new HashMap<RequestInfo,LookupCityRequestListener>());
|
||||||
|
private Handler mHandler;
|
||||||
|
private Set<WeatherServiceProviderChangeListener> mProviderChangedListeners = new ArraySet<>();
|
||||||
|
|
||||||
|
private static final String TAG = CMWeatherManager.class.getSimpleName();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Weather update request state: Successfully completed
|
||||||
|
*/
|
||||||
|
public static final int WEATHER_REQUEST_COMPLETED = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Weather update request state: You need to wait a bit longer before requesting an update
|
||||||
|
* again.
|
||||||
|
* <p>Please bear in mind that the weather does not change very often. A threshold of 10 minutes
|
||||||
|
* is enforced by the system</p>
|
||||||
|
*/
|
||||||
|
public static final int WEATHER_REQUEST_SUBMITTED_TOO_SOON = -1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Weather update request state: An error occurred while trying to update the weather. You
|
||||||
|
* should wait before trying again, or your request will be rejected with
|
||||||
|
* {@link #WEATHER_REQUEST_SUBMITTED_TOO_SOON}
|
||||||
|
*/
|
||||||
|
public static final int WEATHER_REQUEST_FAILED = -2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Weather update request state: Only one update request can be processed at a given time.
|
||||||
|
*/
|
||||||
|
public static final int WEATHER_REQUEST_ALREADY_IN_PROGRESS = -3;
|
||||||
|
|
||||||
|
/** @hide */
|
||||||
|
public static final int LOOKUP_REQUEST_COMPLETED = 100;
|
||||||
|
|
||||||
|
/** @hide */
|
||||||
|
public static final int LOOKUP_REQUEST_FAILED = -100;
|
||||||
|
|
||||||
|
/** @hide */
|
||||||
|
public static final int LOOKUP_REQUEST_NO_MATCH_FOUND = -101;
|
||||||
|
|
||||||
|
|
||||||
|
private CMWeatherManager(Context context) {
|
||||||
|
Context appContext = context.getApplicationContext();
|
||||||
|
mContext = (appContext != null) ? appContext : context;
|
||||||
|
sWeatherManagerService = getService();
|
||||||
|
|
||||||
|
if (context.getPackageManager().hasSystemFeature(
|
||||||
|
CMContextConstants.Features.WEATHER_SERVICES) && (sWeatherManagerService == null)) {
|
||||||
|
throw new RuntimeException("Unable to bind the CMWeatherManagerService");
|
||||||
|
}
|
||||||
|
mHandler = new Handler(appContext.getMainLooper());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets or creates an instance of the {@link cyanogenmod.weather.CMWeatherManager}
|
||||||
|
* @param context
|
||||||
|
* @return {@link CMWeatherManager}
|
||||||
|
*/
|
||||||
|
public static CMWeatherManager getInstance(Context context) {
|
||||||
|
if (sInstance == null) {
|
||||||
|
sInstance = new CMWeatherManager(context);
|
||||||
|
}
|
||||||
|
return sInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @hide */
|
||||||
|
public static ICMWeatherManager getService() {
|
||||||
|
if (sWeatherManagerService != null) {
|
||||||
|
return sWeatherManagerService;
|
||||||
|
}
|
||||||
|
IBinder binder = ServiceManager.getService(CMContextConstants.CM_WEATHER_SERVICE);
|
||||||
|
if (binder != null) {
|
||||||
|
sWeatherManagerService = ICMWeatherManager.Stub.asInterface(binder);
|
||||||
|
return sWeatherManagerService;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Forces the weather service to request the latest available weather information for
|
||||||
|
* the supplied {@link android.location.Location} location.
|
||||||
|
*
|
||||||
|
* @param location The location you want to get the latest weather data from.
|
||||||
|
* @param listener {@link WeatherUpdateRequestListener} To be notified once the active weather
|
||||||
|
* service provider has finished
|
||||||
|
* processing your request
|
||||||
|
*/
|
||||||
|
public void requestWeatherUpdate(@NonNull Location location,
|
||||||
|
@NonNull WeatherUpdateRequestListener listener) {
|
||||||
|
if (sWeatherManagerService == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
RequestInfo info = new RequestInfo
|
||||||
|
.Builder(mRequestInfoListener)
|
||||||
|
.setLocation(location)
|
||||||
|
.build();
|
||||||
|
if (listener != null) mWeatherUpdateRequestListeners.put(info, listener);
|
||||||
|
sWeatherManagerService.updateWeather(info);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Forces the weather service to request the latest weather information for the provided
|
||||||
|
* WeatherLocation. This is the preferred method for requesting a weather update.
|
||||||
|
*
|
||||||
|
* @param weatherLocation A {@link cyanogenmod.weather.WeatherLocation} that was previously
|
||||||
|
* obtained by calling
|
||||||
|
* {@link #lookupCity(String, LookupCityRequestListener)}
|
||||||
|
* @param listener {@link WeatherUpdateRequestListener} To be notified once the active weather
|
||||||
|
* service provider has finished
|
||||||
|
* processing your request
|
||||||
|
*/
|
||||||
|
public void requestWeatherUpdate(@NonNull WeatherLocation weatherLocation,
|
||||||
|
@NonNull WeatherUpdateRequestListener listener) {
|
||||||
|
if (sWeatherManagerService == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
RequestInfo info = new RequestInfo
|
||||||
|
.Builder(mRequestInfoListener)
|
||||||
|
.setWeatherLocation(weatherLocation)
|
||||||
|
.build();
|
||||||
|
if (listener != null) mWeatherUpdateRequestListeners.put(info, listener);
|
||||||
|
sWeatherManagerService.updateWeather(info);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request the active weather provider service to lookup the supplied city name.
|
||||||
|
*
|
||||||
|
* @param city The city name
|
||||||
|
* @param listener {@link LookupCityRequestListener} To be notified once the request has been
|
||||||
|
* completed. Upon success, a list of
|
||||||
|
* {@link cyanogenmod.weather.WeatherLocation}
|
||||||
|
* will be provided
|
||||||
|
*/
|
||||||
|
public void lookupCity(@NonNull String city, @NonNull LookupCityRequestListener listener) {
|
||||||
|
if (sWeatherManagerService == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
RequestInfo info = new RequestInfo
|
||||||
|
.Builder(mRequestInfoListener)
|
||||||
|
.setCityName(city)
|
||||||
|
.build();
|
||||||
|
if (listener != null) mLookupNameRequestListeners.put(info, listener);
|
||||||
|
sWeatherManagerService.lookupCity(info);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a {@link WeatherServiceProviderChangeListener} to be notified when a new weather
|
||||||
|
* service provider becomes active.
|
||||||
|
* @param listener {@link WeatherServiceProviderChangeListener} to register
|
||||||
|
*/
|
||||||
|
public void registerWeatherServiceProviderChangeListener(
|
||||||
|
@NonNull WeatherServiceProviderChangeListener listener) {
|
||||||
|
synchronized (mProviderChangedListeners) {
|
||||||
|
if (mProviderChangedListeners.contains(listener)) {
|
||||||
|
throw new IllegalArgumentException("Listener already registered");
|
||||||
|
}
|
||||||
|
if (mProviderChangedListeners.size() == 0) {
|
||||||
|
try {
|
||||||
|
sWeatherManagerService.registerWeatherServiceProviderChangeListener(
|
||||||
|
mProviderChangeListener);
|
||||||
|
} catch (RemoteException e){
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mProviderChangedListeners.add(listener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unregisters a listener
|
||||||
|
* @param listener A previously registered {@link WeatherServiceProviderChangeListener}
|
||||||
|
*/
|
||||||
|
public void unregisterWeatherServiceProviderChangeListener(
|
||||||
|
@NonNull WeatherServiceProviderChangeListener listener) {
|
||||||
|
synchronized (mProviderChangedListeners) {
|
||||||
|
if (!mProviderChangedListeners.contains(listener)) {
|
||||||
|
throw new IllegalArgumentException("Listener was never registered");
|
||||||
|
}
|
||||||
|
mProviderChangedListeners.remove(listener);
|
||||||
|
if (mProviderChangedListeners.size() == 0) {
|
||||||
|
try {
|
||||||
|
sWeatherManagerService.unregisterWeatherServiceProviderChangeListener(
|
||||||
|
mProviderChangeListener);
|
||||||
|
} catch(RemoteException e){
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the service's label as declared by the active weather service provider in its manifest
|
||||||
|
* @return the service's label
|
||||||
|
*/
|
||||||
|
public String getActiveWeatherServiceProviderLabel() {
|
||||||
|
try {
|
||||||
|
return sWeatherManagerService.getActiveWeatherServiceProviderLabel();
|
||||||
|
} catch(RemoteException e){
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private final IWeatherServiceProviderChangeListener mProviderChangeListener =
|
||||||
|
new IWeatherServiceProviderChangeListener.Stub() {
|
||||||
|
@Override
|
||||||
|
public void onWeatherServiceProviderChanged(final String providerName) {
|
||||||
|
mHandler.post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
synchronized (mProviderChangedListeners) {
|
||||||
|
List<WeatherServiceProviderChangeListener> deadListeners
|
||||||
|
= new ArrayList<>();
|
||||||
|
for (WeatherServiceProviderChangeListener listener
|
||||||
|
: mProviderChangedListeners) {
|
||||||
|
try {
|
||||||
|
listener.onWeatherServiceProviderChanged(providerName);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
deadListeners.add(listener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (deadListeners.size() > 0) {
|
||||||
|
for (WeatherServiceProviderChangeListener listener : deadListeners) {
|
||||||
|
mProviderChangedListeners.remove(listener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private final IRequestInfoListener mRequestInfoListener = new IRequestInfoListener.Stub() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onWeatherRequestCompleted(final RequestInfo requestInfo, final int state,
|
||||||
|
final WeatherInfo weatherInfo) {
|
||||||
|
final WeatherUpdateRequestListener listener
|
||||||
|
= mWeatherUpdateRequestListeners.remove(requestInfo);
|
||||||
|
if (listener != null) {
|
||||||
|
mHandler.post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
listener.onWeatherRequestCompleted(state, weatherInfo);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLookupCityRequestCompleted(RequestInfo requestInfo,
|
||||||
|
final List<WeatherLocation> weatherLocations) {
|
||||||
|
|
||||||
|
final LookupCityRequestListener listener
|
||||||
|
= mLookupNameRequestListeners.remove(requestInfo);
|
||||||
|
if (listener != null) {
|
||||||
|
mHandler.post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
ArrayList<WeatherLocation> list = null;
|
||||||
|
if (weatherLocations != null) {
|
||||||
|
list = new ArrayList<>(weatherLocations);
|
||||||
|
}
|
||||||
|
listener.onLookupCityRequestCompleted(list);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface used to receive notifications upon completion of a weather update request
|
||||||
|
*/
|
||||||
|
public interface WeatherUpdateRequestListener {
|
||||||
|
/**
|
||||||
|
* This method will be called when the weather service provider has finished processing the
|
||||||
|
* request
|
||||||
|
*
|
||||||
|
* @param state Any of the following values
|
||||||
|
* {@link #WEATHER_REQUEST_COMPLETED}
|
||||||
|
* {@link #WEATHER_REQUEST_ALREADY_IN_PROGRESS}
|
||||||
|
* {@link #WEATHER_REQUEST_SUBMITTED_TOO_SOON}
|
||||||
|
* {@link #WEATHER_REQUEST_FAILED}
|
||||||
|
*
|
||||||
|
* @param weatherInfo A fully populated {@link WeatherInfo} if state is
|
||||||
|
* {@link #WEATHER_REQUEST_COMPLETED}, null otherwise
|
||||||
|
*/
|
||||||
|
void onWeatherRequestCompleted(int state, WeatherInfo weatherInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface used to receive notifications upon completion of a request to lookup a city name
|
||||||
|
*/
|
||||||
|
public interface LookupCityRequestListener {
|
||||||
|
/**
|
||||||
|
* This method will be called when the weather service provider has finished processing the
|
||||||
|
* request. The argument can be null if the provider couldn't find a match
|
||||||
|
*
|
||||||
|
* @param locations
|
||||||
|
*/
|
||||||
|
void onLookupCityRequestCompleted(ArrayList<WeatherLocation> locations);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface used to be notified when the user changes the weather service provider
|
||||||
|
*/
|
||||||
|
public interface WeatherServiceProviderChangeListener {
|
||||||
|
/**
|
||||||
|
* This method will be called when a new weather service provider becomes active in the
|
||||||
|
* system. The parameter can be null when
|
||||||
|
* <p>The user removed the active weather service provider from the system </p>
|
||||||
|
* <p>The active weather provider was disabled.</p>
|
||||||
|
*
|
||||||
|
* @param providerLabel The label as declared on the weather service provider manifest
|
||||||
|
*/
|
||||||
|
void onWeatherServiceProviderChanged(String providerLabel);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
/*
|
||||||
|
* 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 cyanogenmod.weather;
|
||||||
|
|
||||||
|
import cyanogenmod.weather.IWeatherServiceProviderChangeListener;
|
||||||
|
import cyanogenmod.weather.RequestInfo;
|
||||||
|
|
||||||
|
/** @hide */
|
||||||
|
interface ICMWeatherManager {
|
||||||
|
oneway void updateWeather(in RequestInfo info);
|
||||||
|
oneway void lookupCity(in RequestInfo info);
|
||||||
|
oneway void registerWeatherServiceProviderChangeListener(
|
||||||
|
in IWeatherServiceProviderChangeListener listener);
|
||||||
|
oneway void unregisterWeatherServiceProviderChangeListener(
|
||||||
|
in IWeatherServiceProviderChangeListener listener);
|
||||||
|
String getActiveWeatherServiceProviderLabel();
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
/*
|
||||||
|
* 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 cyanogenmod.weather;
|
||||||
|
|
||||||
|
import cyanogenmod.weather.RequestInfo;
|
||||||
|
import cyanogenmod.weather.WeatherInfo;
|
||||||
|
import cyanogenmod.weather.WeatherLocation;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/** @hide */
|
||||||
|
oneway interface IRequestInfoListener {
|
||||||
|
void onWeatherRequestCompleted(in RequestInfo requestInfo, int state,
|
||||||
|
in WeatherInfo weatherInfo);
|
||||||
|
void onLookupCityRequestCompleted(in RequestInfo requestInfo,
|
||||||
|
in List<WeatherLocation> weatherLocation);
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
/*
|
||||||
|
* 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 cyanogenmod.weather;
|
||||||
|
|
||||||
|
/** @hide */
|
||||||
|
oneway interface IWeatherServiceProviderChangeListener {
|
||||||
|
void onWeatherServiceProviderChanged(String providerLabel);
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
/*
|
||||||
|
* 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 cyanogenmod.weather;
|
||||||
|
|
||||||
|
parcelable RequestInfo;
|
|
@ -0,0 +1,355 @@
|
||||||
|
/*
|
||||||
|
* 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 cyanogenmod.weather;
|
||||||
|
|
||||||
|
import android.location.Location;
|
||||||
|
import android.os.Parcel;
|
||||||
|
import android.os.Parcelable;
|
||||||
|
import cyanogenmod.os.Build;
|
||||||
|
import cyanogenmod.providers.WeatherContract;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class holds the information of a request submitted to the active weather provider service
|
||||||
|
*/
|
||||||
|
public final class RequestInfo implements Parcelable {
|
||||||
|
|
||||||
|
private Location mLocation;
|
||||||
|
private String mCityName;
|
||||||
|
private WeatherLocation mWeatherLocation;
|
||||||
|
private int mRequestType;
|
||||||
|
private IRequestInfoListener mListener;
|
||||||
|
private int mTempUnit;
|
||||||
|
private int mKey;
|
||||||
|
private boolean mIsQueryOnly;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A request to update the weather data using a geographical {@link android.location.Location}
|
||||||
|
*/
|
||||||
|
public static final int TYPE_GEO_LOCATION_REQ = 1;
|
||||||
|
/**
|
||||||
|
* A request to update the weather data using a {@link WeatherLocation}
|
||||||
|
*/
|
||||||
|
public static final int TYPE_WEATHER_LOCATION_REQ = 2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A request to look up a city name
|
||||||
|
*/
|
||||||
|
public static final int TYPE_LOOKUP_CITY_NAME_REQ = 3;
|
||||||
|
|
||||||
|
private RequestInfo() {}
|
||||||
|
|
||||||
|
/* package */ static class Builder {
|
||||||
|
private Location mLocation;
|
||||||
|
private String mCityName;
|
||||||
|
private WeatherLocation mWeatherLocation;
|
||||||
|
private int mRequestType;
|
||||||
|
private IRequestInfoListener mListener;
|
||||||
|
private int mTempUnit = WeatherContract.WeatherColumns.TempUnit.FAHRENHEIT;
|
||||||
|
private boolean mIsQueryOnly = false;
|
||||||
|
|
||||||
|
public Builder(IRequestInfoListener listener) {
|
||||||
|
this.mListener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the city name and identifies this request as a {@link #TYPE_LOOKUP_CITY_NAME_REQ}
|
||||||
|
* request. If set, will null out the location and weather location.
|
||||||
|
*/
|
||||||
|
public Builder setCityName(String cityName) {
|
||||||
|
this.mCityName = cityName;
|
||||||
|
this.mRequestType = TYPE_LOOKUP_CITY_NAME_REQ;
|
||||||
|
this.mLocation = null;
|
||||||
|
this.mWeatherLocation = null;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the Location and identifies this request as a {@link #TYPE_GEO_LOCATION_REQ}. If
|
||||||
|
* set, will null out the city name and weather location.
|
||||||
|
*/
|
||||||
|
public Builder setLocation(Location location) {
|
||||||
|
this.mLocation = location;
|
||||||
|
this.mCityName = null;
|
||||||
|
this.mWeatherLocation = null;
|
||||||
|
this.mRequestType = TYPE_GEO_LOCATION_REQ;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the weather location and identifies this request as a
|
||||||
|
* {@link #TYPE_WEATHER_LOCATION_REQ}. If set, will null out the location and city name
|
||||||
|
*/
|
||||||
|
public Builder setWeatherLocation(WeatherLocation weatherLocation) {
|
||||||
|
this.mWeatherLocation = weatherLocation;
|
||||||
|
this.mLocation = null;
|
||||||
|
this.mCityName = null;
|
||||||
|
this.mRequestType = TYPE_WEATHER_LOCATION_REQ;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the unit in which the temperature will be reported if the request is honored.
|
||||||
|
* Valid values are:
|
||||||
|
* <ul>
|
||||||
|
* {@link cyanogenmod.providers.WeatherContract.WeatherColumns.TempUnit#CELSIUS}
|
||||||
|
* {@link cyanogenmod.providers.WeatherContract.WeatherColumns.TempUnit#FAHRENHEIT}
|
||||||
|
* </ul>
|
||||||
|
* Any other value will generate an IllegalArgumentException. If the temperature unit is not
|
||||||
|
* set, the default will be degrees Fahrenheit
|
||||||
|
* @param unit A valid temperature unit
|
||||||
|
*/
|
||||||
|
public Builder setTemperatureUnit(int unit) {
|
||||||
|
if (!isValidTempUnit(unit)) {
|
||||||
|
throw new IllegalArgumentException("Invalid temperature unit");
|
||||||
|
}
|
||||||
|
this.mTempUnit = unit;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If this is a weather request, marks the request as a query only, meaning that the
|
||||||
|
* content provider won't be updated after the active weather service has finished
|
||||||
|
* processing the request.
|
||||||
|
*/
|
||||||
|
public Builder queryOnly() {
|
||||||
|
switch (mRequestType) {
|
||||||
|
case TYPE_GEO_LOCATION_REQ:
|
||||||
|
case TYPE_WEATHER_LOCATION_REQ:
|
||||||
|
this.mIsQueryOnly = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
this.mIsQueryOnly = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RequestInfo build() {
|
||||||
|
RequestInfo info = new RequestInfo();
|
||||||
|
info.mListener = this.mListener;
|
||||||
|
info.mRequestType = this.mRequestType;
|
||||||
|
info.mCityName = this.mCityName;
|
||||||
|
info.mWeatherLocation = this.mWeatherLocation;
|
||||||
|
info.mLocation = this.mLocation;
|
||||||
|
info.mTempUnit = this.mTempUnit;
|
||||||
|
info.mIsQueryOnly = this.mIsQueryOnly;
|
||||||
|
info.mKey = this.hashCode();
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isValidTempUnit(int unit) {
|
||||||
|
switch (unit) {
|
||||||
|
case WeatherContract.WeatherColumns.TempUnit.CELSIUS:
|
||||||
|
case WeatherContract.WeatherColumns.TempUnit.FAHRENHEIT:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private RequestInfo(Parcel parcel) {
|
||||||
|
int parcelableVersion = parcel.readInt();
|
||||||
|
int parcelableSize = parcel.readInt();
|
||||||
|
int startPosition = parcel.dataPosition();
|
||||||
|
if (parcelableVersion >= Build.CM_VERSION_CODES.ELDERBERRY) {
|
||||||
|
mKey = parcel.readInt();
|
||||||
|
mRequestType = parcel.readInt();
|
||||||
|
switch (mRequestType) {
|
||||||
|
case TYPE_GEO_LOCATION_REQ:
|
||||||
|
mLocation = Location.CREATOR.createFromParcel(parcel);
|
||||||
|
mTempUnit = parcel.readInt();
|
||||||
|
break;
|
||||||
|
case TYPE_WEATHER_LOCATION_REQ:
|
||||||
|
mWeatherLocation = WeatherLocation.CREATOR.createFromParcel(parcel);
|
||||||
|
mTempUnit = parcel.readInt();
|
||||||
|
break;
|
||||||
|
case TYPE_LOOKUP_CITY_NAME_REQ:
|
||||||
|
mCityName = parcel.readString();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
mIsQueryOnly = (parcel.readInt() == 1);
|
||||||
|
mListener = IRequestInfoListener.Stub.asInterface(parcel.readStrongBinder());
|
||||||
|
}
|
||||||
|
parcel.setDataPosition(startPosition + parcelableSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The request type
|
||||||
|
*/
|
||||||
|
public int getRequestType() {
|
||||||
|
return mRequestType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the {@link android.location.Location} if this is a request by location, null
|
||||||
|
* otherwise
|
||||||
|
*/
|
||||||
|
public Location getLocation() {
|
||||||
|
return mLocation;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the {@link cyanogenmod.weather.WeatherLocation} if this is a request by weather
|
||||||
|
* location, null otherwise
|
||||||
|
*/
|
||||||
|
public WeatherLocation getWeatherLocation() {
|
||||||
|
return mWeatherLocation;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public IRequestInfoListener getRequestListener() {
|
||||||
|
return mListener;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the city name if this is a lookup request, null otherwise
|
||||||
|
*/
|
||||||
|
public String getCityName() {
|
||||||
|
return mCityName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the temperature unit if this is a weather request, -1 otherwise
|
||||||
|
*/
|
||||||
|
public int getTemperatureUnit() {
|
||||||
|
switch (mRequestType) {
|
||||||
|
case TYPE_GEO_LOCATION_REQ:
|
||||||
|
case TYPE_WEATHER_LOCATION_REQ:
|
||||||
|
return mTempUnit;
|
||||||
|
default:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return if this is a weather request, whether the request will update the content provider.
|
||||||
|
* False for other kind of requests
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public boolean isQueryOnlyWeatherRequest() {
|
||||||
|
switch (mRequestType) {
|
||||||
|
case TYPE_GEO_LOCATION_REQ:
|
||||||
|
case TYPE_WEATHER_LOCATION_REQ:
|
||||||
|
return mIsQueryOnly;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final Creator<RequestInfo> CREATOR = new Creator<RequestInfo>() {
|
||||||
|
@Override
|
||||||
|
public RequestInfo createFromParcel(Parcel in) {
|
||||||
|
return new RequestInfo(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RequestInfo[] newArray(int size) {
|
||||||
|
return new RequestInfo[size];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int describeContents() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeToParcel(Parcel dest, int flags) {
|
||||||
|
dest.writeInt(Build.PARCELABLE_VERSION);
|
||||||
|
|
||||||
|
int sizePosition = dest.dataPosition();
|
||||||
|
dest.writeInt(0);
|
||||||
|
int startPosition = dest.dataPosition();
|
||||||
|
|
||||||
|
// ==== ELDERBERRY =====
|
||||||
|
dest.writeInt(mKey);
|
||||||
|
dest.writeInt(mRequestType);
|
||||||
|
switch (mRequestType) {
|
||||||
|
case TYPE_GEO_LOCATION_REQ:
|
||||||
|
mLocation.writeToParcel(dest, 0);
|
||||||
|
dest.writeInt(mTempUnit);
|
||||||
|
break;
|
||||||
|
case TYPE_WEATHER_LOCATION_REQ:
|
||||||
|
mWeatherLocation.writeToParcel(dest, 0);
|
||||||
|
dest.writeInt(mTempUnit);
|
||||||
|
break;
|
||||||
|
case TYPE_LOOKUP_CITY_NAME_REQ:
|
||||||
|
dest.writeString(mCityName);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
dest.writeInt(mIsQueryOnly == true ? 1 : 0);
|
||||||
|
dest.writeStrongBinder(mListener.asBinder());
|
||||||
|
|
||||||
|
int parcelableSize = dest.dataPosition() - startPosition;
|
||||||
|
dest.setDataPosition(sizePosition);
|
||||||
|
dest.writeInt(parcelableSize);
|
||||||
|
dest.setDataPosition(startPosition + parcelableSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
builder.append("{ Request for ");
|
||||||
|
switch (mRequestType) {
|
||||||
|
case TYPE_GEO_LOCATION_REQ:
|
||||||
|
builder.append("Location: ").append(mLocation);
|
||||||
|
builder.append(" Temp Unit: ");
|
||||||
|
if (mTempUnit == WeatherContract.WeatherColumns.TempUnit.FAHRENHEIT) {
|
||||||
|
builder.append("Fahrenheit");
|
||||||
|
} else {
|
||||||
|
builder.append(" Celsius");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TYPE_WEATHER_LOCATION_REQ:
|
||||||
|
builder.append("WeatherLocation: ").append(mWeatherLocation);
|
||||||
|
builder.append(" Temp Unit: ");
|
||||||
|
if (mTempUnit == WeatherContract.WeatherColumns.TempUnit.FAHRENHEIT) {
|
||||||
|
builder.append("Fahrenheit");
|
||||||
|
} else {
|
||||||
|
builder.append(" Celsius");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TYPE_LOOKUP_CITY_NAME_REQ:
|
||||||
|
builder.append("Lookup City: ").append(mCityName);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return builder.append(" }").toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
//The hashcode of this object was stored when it was built. This is an
|
||||||
|
//immutable object but we need to preserve the hashcode since this object is parcelable and
|
||||||
|
//it's reconstructed over IPC, and clients of this object might want to store it in a
|
||||||
|
//collection that relies on this code to identify the object
|
||||||
|
return mKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (obj instanceof RequestInfo) {
|
||||||
|
RequestInfo info = (RequestInfo) obj;
|
||||||
|
return (info.hashCode() == this.mKey);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2016 The CyanongenMod 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 cyanogenmod.weather;
|
||||||
|
|
||||||
|
parcelable WeatherInfo;
|
|
@ -0,0 +1,525 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2016 The CyanongenMod 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 cyanogenmod.weather;
|
||||||
|
|
||||||
|
import android.annotation.NonNull;
|
||||||
|
import android.os.Parcel;
|
||||||
|
import android.os.Parcelable;
|
||||||
|
import cyanogenmod.os.Build;
|
||||||
|
import cyanogenmod.providers.WeatherContract;
|
||||||
|
import cyanogenmod.weatherservice.ServiceRequest;
|
||||||
|
import cyanogenmod.weatherservice.ServiceRequestResult;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class represents the weather information that a
|
||||||
|
* {@link cyanogenmod.weatherservice.WeatherProviderService} will use to update the weather content
|
||||||
|
* provider. A weather provider service will be called by the system to process an update
|
||||||
|
* request at any time. If the service successfully processes the request, then the weather provider
|
||||||
|
* service is responsible of calling
|
||||||
|
* {@link ServiceRequest#complete(ServiceRequestResult)} to notify the
|
||||||
|
* system that the request was completed and that the weather content provider should be updated
|
||||||
|
* with the supplied weather information.
|
||||||
|
*/
|
||||||
|
public final class WeatherInfo implements Parcelable {
|
||||||
|
|
||||||
|
private String mCityId;
|
||||||
|
private String mCity;
|
||||||
|
private int mConditionCode;
|
||||||
|
private float mTemperature;
|
||||||
|
private int mTempUnit;
|
||||||
|
private float mHumidity;
|
||||||
|
private float mWindSpeed;
|
||||||
|
private float mWindDirection;
|
||||||
|
private int mWindSpeedUnit;
|
||||||
|
private long mTimestamp;
|
||||||
|
private ArrayList<DayForecast> mForecastList;
|
||||||
|
int mKey;
|
||||||
|
|
||||||
|
private WeatherInfo() {}
|
||||||
|
|
||||||
|
public static class Builder {
|
||||||
|
private String mCityId;
|
||||||
|
private String mCity;
|
||||||
|
private int mConditionCode;
|
||||||
|
private float mTemperature;
|
||||||
|
private int mTempUnit;
|
||||||
|
private float mHumidity;
|
||||||
|
private float mWindSpeed;
|
||||||
|
private float mWindDirection;
|
||||||
|
private int mWindSpeedUnit;
|
||||||
|
private long mTimestamp;
|
||||||
|
private ArrayList<DayForecast> mForecastList;
|
||||||
|
|
||||||
|
public Builder(long timestamp) {
|
||||||
|
mTimestamp = timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setCity(String cityId, @NonNull String cityName) {
|
||||||
|
if (cityName == null || cityId == null) {
|
||||||
|
throw new IllegalArgumentException("City name and id can't be null");
|
||||||
|
}
|
||||||
|
mCityId = cityId;
|
||||||
|
mCity = cityName;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setTemperature(float temperature, int tempUnit) {
|
||||||
|
if (!isValidTempUnit(tempUnit)) {
|
||||||
|
throw new IllegalArgumentException("Invalid temperature unit");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Float.isNaN(temperature)) {
|
||||||
|
throw new IllegalArgumentException("Invalid temperature value");
|
||||||
|
}
|
||||||
|
|
||||||
|
mTemperature = temperature;
|
||||||
|
mTempUnit = tempUnit;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setHumidity(float humidity) {
|
||||||
|
if (Float.isNaN(humidity)) {
|
||||||
|
throw new IllegalArgumentException("Invalid humidity value");
|
||||||
|
}
|
||||||
|
|
||||||
|
mHumidity = humidity;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setWind(float windSpeed, float windDirection, int windSpeedUnit) {
|
||||||
|
if (Float.isNaN(windSpeed)) {
|
||||||
|
throw new IllegalArgumentException("Invalid wind speed value");
|
||||||
|
}
|
||||||
|
if (Float.isNaN(windDirection)) {
|
||||||
|
throw new IllegalArgumentException("Invalid wind direction value");
|
||||||
|
}
|
||||||
|
if (!isValidWindSpeedUnit(windSpeedUnit)) {
|
||||||
|
throw new IllegalArgumentException("Invalid speed unit");
|
||||||
|
}
|
||||||
|
mWindSpeed = windSpeed;
|
||||||
|
mWindSpeedUnit = windSpeedUnit;
|
||||||
|
mWindDirection = windDirection;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setWeatherCondition(int conditionCode) {
|
||||||
|
if (!isValidWeatherCode(conditionCode)) {
|
||||||
|
throw new IllegalArgumentException("Invalid weather condition code");
|
||||||
|
}
|
||||||
|
mConditionCode = conditionCode;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setForecast(@NonNull ArrayList<DayForecast> forecasts) {
|
||||||
|
if (forecasts == null) {
|
||||||
|
throw new IllegalArgumentException("Forecast list can't be null");
|
||||||
|
}
|
||||||
|
mForecastList = forecasts;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public WeatherInfo build() {
|
||||||
|
WeatherInfo info = new WeatherInfo();
|
||||||
|
info.mCityId = this.mCityId;
|
||||||
|
info.mCity = this.mCity;
|
||||||
|
info.mConditionCode = this.mConditionCode;
|
||||||
|
info.mTemperature = this.mTemperature;
|
||||||
|
info.mTempUnit = this.mTempUnit;
|
||||||
|
info.mHumidity = this.mHumidity;
|
||||||
|
info.mWindSpeed = this.mWindSpeed;
|
||||||
|
info.mWindDirection = this.mWindDirection;
|
||||||
|
info.mWindSpeedUnit = this.mWindSpeedUnit;
|
||||||
|
info.mTimestamp = this.mTimestamp;
|
||||||
|
info.mForecastList = this.mForecastList;
|
||||||
|
info.mKey = this.hashCode();
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isValidTempUnit(int unit) {
|
||||||
|
switch (unit) {
|
||||||
|
case WeatherContract.WeatherColumns.TempUnit.CELSIUS:
|
||||||
|
case WeatherContract.WeatherColumns.TempUnit.FAHRENHEIT:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isValidWindSpeedUnit(int unit) {
|
||||||
|
switch (unit) {
|
||||||
|
case WeatherContract.WeatherColumns.WindSpeedUnit.KPH:
|
||||||
|
case WeatherContract.WeatherColumns.WindSpeedUnit.MPH:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static boolean isValidWeatherCode(int code) {
|
||||||
|
if (code < WeatherContract.WeatherColumns.WeatherCode.WEATHER_CODE_MIN
|
||||||
|
|| code > WeatherContract.WeatherColumns.WeatherCode.WEATHER_CODE_MAX) {
|
||||||
|
if (code != WeatherContract.WeatherColumns.WeatherCode.NOT_AVAILABLE) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return city id
|
||||||
|
*/
|
||||||
|
public String getCityId() {
|
||||||
|
return mCityId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return city name
|
||||||
|
*/
|
||||||
|
public String getCity() {
|
||||||
|
return mCity;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return An implementation specific weather condition code
|
||||||
|
*/
|
||||||
|
public int getConditionCode() {
|
||||||
|
return mConditionCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return humidity
|
||||||
|
*/
|
||||||
|
public float getHumidity() {
|
||||||
|
return mHumidity;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return time stamp when the request was processed
|
||||||
|
*/
|
||||||
|
public long getTimestamp() {
|
||||||
|
return mTimestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return wind direction (degrees)
|
||||||
|
*/
|
||||||
|
public float getWindDirection() {
|
||||||
|
return mWindDirection;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return wind speed
|
||||||
|
*/
|
||||||
|
public float getWindSpeed() {
|
||||||
|
return mWindSpeed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return wind speed unit
|
||||||
|
*/
|
||||||
|
public int getWindSpeedUnit() {
|
||||||
|
return mWindSpeedUnit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return current temperature
|
||||||
|
*/
|
||||||
|
public float getTemperature() {
|
||||||
|
return mTemperature;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return temperature unit
|
||||||
|
*/
|
||||||
|
public int getTemperatureUnit() {
|
||||||
|
return mTempUnit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return List of {@link cyanogenmod.weather.WeatherInfo.DayForecast}
|
||||||
|
*/
|
||||||
|
public ArrayList<DayForecast> getForecasts() {
|
||||||
|
return mForecastList;
|
||||||
|
}
|
||||||
|
|
||||||
|
private WeatherInfo(Parcel parcel) {
|
||||||
|
int parcelableVersion = parcel.readInt();
|
||||||
|
int parcelableSize = parcel.readInt();
|
||||||
|
int startPosition = parcel.dataPosition();
|
||||||
|
if (parcelableVersion >= Build.CM_VERSION_CODES.ELDERBERRY) {
|
||||||
|
mKey = parcel.readInt();
|
||||||
|
mCityId = parcel.readString();
|
||||||
|
mCity = parcel.readString();
|
||||||
|
mConditionCode = parcel.readInt();
|
||||||
|
mTemperature = parcel.readFloat();
|
||||||
|
mTempUnit = parcel.readInt();
|
||||||
|
mHumidity = parcel.readFloat();
|
||||||
|
mWindSpeed = parcel.readFloat();
|
||||||
|
mWindDirection = parcel.readFloat();
|
||||||
|
mWindSpeedUnit = parcel.readInt();
|
||||||
|
mTimestamp = parcel.readLong();
|
||||||
|
int forecastListSize = parcel.readInt();
|
||||||
|
mForecastList = new ArrayList<>();
|
||||||
|
while (forecastListSize > 0) {
|
||||||
|
mForecastList.add(DayForecast.CREATOR.createFromParcel(parcel));
|
||||||
|
forecastListSize--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
parcel.setDataPosition(startPosition + parcelableSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int describeContents() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeToParcel(Parcel dest, int flags) {
|
||||||
|
dest.writeInt(Build.PARCELABLE_VERSION);
|
||||||
|
|
||||||
|
int sizePosition = dest.dataPosition();
|
||||||
|
dest.writeInt(0);
|
||||||
|
int startPosition = dest.dataPosition();
|
||||||
|
|
||||||
|
// ==== ELDERBERRY =====
|
||||||
|
dest.writeInt(mKey);
|
||||||
|
dest.writeString(mCityId);
|
||||||
|
dest.writeString(mCity);
|
||||||
|
dest.writeInt(mConditionCode);
|
||||||
|
dest.writeFloat(mTemperature);
|
||||||
|
dest.writeInt(mTempUnit);
|
||||||
|
dest.writeFloat(mHumidity);
|
||||||
|
dest.writeFloat(mWindSpeed);
|
||||||
|
dest.writeFloat(mWindDirection);
|
||||||
|
dest.writeInt(mWindSpeedUnit);
|
||||||
|
dest.writeLong(mTimestamp);
|
||||||
|
dest.writeInt(mForecastList.size());
|
||||||
|
for (DayForecast dayForecast : mForecastList) {
|
||||||
|
dayForecast.writeToParcel(dest, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int parcelableSize = dest.dataPosition() - startPosition;
|
||||||
|
dest.setDataPosition(sizePosition);
|
||||||
|
dest.writeInt(parcelableSize);
|
||||||
|
dest.setDataPosition(startPosition + parcelableSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final Parcelable.Creator<WeatherInfo> CREATOR =
|
||||||
|
new Parcelable.Creator<WeatherInfo>() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public WeatherInfo createFromParcel(Parcel source) {
|
||||||
|
return new WeatherInfo(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public WeatherInfo[] newArray(int size) {
|
||||||
|
return new WeatherInfo[size];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class represents the weather forecast for a given day
|
||||||
|
*/
|
||||||
|
public static class DayForecast implements Parcelable{
|
||||||
|
float mLow;
|
||||||
|
float mHigh;
|
||||||
|
int mConditionCode;
|
||||||
|
int mKey;
|
||||||
|
|
||||||
|
private DayForecast() {}
|
||||||
|
|
||||||
|
public static class Builder {
|
||||||
|
float mLow;
|
||||||
|
float mHigh;
|
||||||
|
int mConditionCode;
|
||||||
|
|
||||||
|
public Builder() {}
|
||||||
|
public Builder setHigh(float high) {
|
||||||
|
if (Float.isNaN(high)) {
|
||||||
|
throw new IllegalArgumentException("Invalid high forecast temperature");
|
||||||
|
}
|
||||||
|
mHigh = high;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
public Builder setLow(float low) {
|
||||||
|
if (Float.isNaN(low)) {
|
||||||
|
throw new IllegalArgumentException("Invalid low forecast temperature");
|
||||||
|
}
|
||||||
|
mLow = low;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setWeatherCondition(int code) {
|
||||||
|
if (!isValidWeatherCode(code)) {
|
||||||
|
throw new IllegalArgumentException("Invalid weather condition code");
|
||||||
|
}
|
||||||
|
mConditionCode = code;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DayForecast build() {
|
||||||
|
DayForecast forecast = new DayForecast();
|
||||||
|
forecast.mLow = this.mLow;
|
||||||
|
forecast.mHigh = this.mHigh;
|
||||||
|
forecast.mConditionCode = this.mConditionCode;
|
||||||
|
forecast.mKey = this.hashCode();
|
||||||
|
return forecast;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return forecasted low temperature
|
||||||
|
*/
|
||||||
|
public float getLow() {
|
||||||
|
return mLow;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return not what you think. Returns the forecasted high temperature
|
||||||
|
*/
|
||||||
|
public float getHigh() {
|
||||||
|
return mHigh;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return forecasted weather condition code. Implementation specific
|
||||||
|
*/
|
||||||
|
public int getConditionCode() {
|
||||||
|
return mConditionCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int describeContents() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeToParcel(Parcel dest, int flags) {
|
||||||
|
dest.writeInt(Build.PARCELABLE_VERSION);
|
||||||
|
|
||||||
|
int sizePosition = dest.dataPosition();
|
||||||
|
dest.writeInt(0);
|
||||||
|
int startPosition = dest.dataPosition();
|
||||||
|
|
||||||
|
// ==== ELDERBERRY =====
|
||||||
|
dest.writeInt(mKey);
|
||||||
|
dest.writeFloat(mLow);
|
||||||
|
dest.writeFloat(mHigh);
|
||||||
|
dest.writeInt(mConditionCode);
|
||||||
|
|
||||||
|
int parcelableSize = dest.dataPosition() - startPosition;
|
||||||
|
dest.setDataPosition(sizePosition);
|
||||||
|
dest.writeInt(parcelableSize);
|
||||||
|
dest.setDataPosition(startPosition + parcelableSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final Parcelable.Creator<DayForecast> CREATOR =
|
||||||
|
new Parcelable.Creator<DayForecast>() {
|
||||||
|
@Override
|
||||||
|
public DayForecast createFromParcel(Parcel source) {
|
||||||
|
return new DayForecast(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DayForecast[] newArray(int size) {
|
||||||
|
return new DayForecast[size];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private DayForecast(Parcel parcel) {
|
||||||
|
int parcelableVersion = parcel.readInt();
|
||||||
|
int parcelableSize = parcel.readInt();
|
||||||
|
int startPosition = parcel.dataPosition();
|
||||||
|
if (parcelableVersion >= Build.CM_VERSION_CODES.ELDERBERRY) {
|
||||||
|
mKey = parcel.readInt();
|
||||||
|
mLow = parcel.readFloat();
|
||||||
|
mHigh = parcel.readFloat();
|
||||||
|
mConditionCode = parcel.readInt();
|
||||||
|
}
|
||||||
|
parcel.setDataPosition(startPosition + parcelableSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return new StringBuilder()
|
||||||
|
.append("{Low temp: ").append(mLow)
|
||||||
|
.append(" High temp: ").append(mHigh)
|
||||||
|
.append(" Condition code: ").append(mConditionCode)
|
||||||
|
.append("}").toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
//The hashcode of this object was stored when it was built. This is an
|
||||||
|
//immutable object but we need to preserve the hashcode since this object is parcelable
|
||||||
|
//and it's reconstructed over IPC, and clients of this object might want to store it in
|
||||||
|
//a collection that relies on this code to identify the object
|
||||||
|
return mKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (obj instanceof DayForecast) {
|
||||||
|
DayForecast forecast = (DayForecast) obj;
|
||||||
|
return (forecast.hashCode() == this.mKey);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuilder builder = new StringBuilder()
|
||||||
|
.append("{CityId: ").append(mCityId)
|
||||||
|
.append(" City Name: ").append(mCity)
|
||||||
|
.append(" Condition Code: ").append(mConditionCode)
|
||||||
|
.append(" Temperature: ").append(mTemperature)
|
||||||
|
.append(" Temperature Unit: ").append(mTempUnit)
|
||||||
|
.append(" Humidity: ").append(mHumidity)
|
||||||
|
.append(" Wind speed: ").append(mWindSpeed)
|
||||||
|
.append(" Wind direction: ").append(mWindDirection)
|
||||||
|
.append(" Wind Speed Unit: ").append(mWindSpeedUnit)
|
||||||
|
.append(" Timestamp: ").append(mTimestamp).append(" Forecasts: [");
|
||||||
|
for (DayForecast dayForecast : mForecastList) {
|
||||||
|
builder.append(dayForecast.toString());
|
||||||
|
}
|
||||||
|
return builder.append("]}").toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
//The hashcode of this object was stored when it was built. This is an
|
||||||
|
//immutable object but we need to preserve the hashcode since this object is parcelable and
|
||||||
|
//it's reconstructed over IPC, and clients of this object might want to store it in a
|
||||||
|
//collection that relies on this code to identify the object
|
||||||
|
return mKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (obj instanceof WeatherInfo) {
|
||||||
|
WeatherInfo info = (WeatherInfo) obj;
|
||||||
|
return (info.hashCode() == this.mKey);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
/*
|
||||||
|
* 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 cyanogenmod.weather;
|
||||||
|
|
||||||
|
parcelable WeatherLocation;
|
|
@ -0,0 +1,172 @@
|
||||||
|
/*
|
||||||
|
* 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 cyanogenmod.weather;
|
||||||
|
|
||||||
|
import android.os.Parcel;
|
||||||
|
import android.os.Parcelable;
|
||||||
|
import cyanogenmod.os.Build;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class representing a geographical location that a weather service provider can use to
|
||||||
|
* get weather data from. Each service provider will potentially populate objects of this class
|
||||||
|
* with different content, so make sure you don't preserve the values when a service provider
|
||||||
|
* is changed
|
||||||
|
*/
|
||||||
|
public final class WeatherLocation implements Parcelable{
|
||||||
|
private String mCityId;
|
||||||
|
private String mCity;
|
||||||
|
private String mPostal;
|
||||||
|
private String mCountryId;
|
||||||
|
private String mCountry;
|
||||||
|
private int mKey;
|
||||||
|
|
||||||
|
private WeatherLocation() {}
|
||||||
|
|
||||||
|
public static class Builder {
|
||||||
|
String mCityId;
|
||||||
|
String mCity;
|
||||||
|
String mPostal;
|
||||||
|
String mCountryId;
|
||||||
|
String mCountry;
|
||||||
|
|
||||||
|
public Builder(String cityId, String cityName) {
|
||||||
|
this.mCityId = cityId;
|
||||||
|
this.mCity = cityName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setCountry(String countyId, String country) {
|
||||||
|
this.mCountryId = countyId;
|
||||||
|
this.mCountry = country;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setPostalCode(String postalCode) {
|
||||||
|
this.mPostal = postalCode;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public WeatherLocation build() {
|
||||||
|
WeatherLocation weatherLocation = new WeatherLocation();
|
||||||
|
weatherLocation.mCityId = this.mCityId;
|
||||||
|
weatherLocation.mCity = this.mCity;
|
||||||
|
weatherLocation.mPostal = this.mPostal;
|
||||||
|
weatherLocation.mCountryId = this.mCountryId;
|
||||||
|
weatherLocation.mCountry = this.mCountry;
|
||||||
|
weatherLocation.mKey = this.hashCode();
|
||||||
|
return weatherLocation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCityId() {
|
||||||
|
return mCityId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCity() {
|
||||||
|
return mCity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPostalCode() {
|
||||||
|
return mPostal;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCountryId() {
|
||||||
|
return mCountryId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCountry() {
|
||||||
|
return mCountry;
|
||||||
|
}
|
||||||
|
|
||||||
|
private WeatherLocation(Parcel in) {
|
||||||
|
int parcelableVersion = in.readInt();
|
||||||
|
int parcelableSize = in.readInt();
|
||||||
|
int startPosition = in.dataPosition();
|
||||||
|
if (parcelableVersion >= Build.CM_VERSION_CODES.ELDERBERRY) {
|
||||||
|
mKey = in.readInt();
|
||||||
|
mCityId = in.readString();
|
||||||
|
mCity = in.readString();
|
||||||
|
mPostal = in.readString();
|
||||||
|
mCountryId = in.readString();
|
||||||
|
mCountry = in.readString();
|
||||||
|
}
|
||||||
|
in.setDataPosition(startPosition + parcelableSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final Creator<WeatherLocation> CREATOR = new Creator<WeatherLocation>() {
|
||||||
|
@Override
|
||||||
|
public WeatherLocation createFromParcel(Parcel in) {
|
||||||
|
return new WeatherLocation(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public WeatherLocation[] newArray(int size) {
|
||||||
|
return new WeatherLocation[size];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int describeContents() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeToParcel(Parcel dest, int flags) {
|
||||||
|
dest.writeInt(Build.PARCELABLE_VERSION);
|
||||||
|
|
||||||
|
int sizePosition = dest.dataPosition();
|
||||||
|
dest.writeInt(0);
|
||||||
|
int startPosition = dest.dataPosition();
|
||||||
|
|
||||||
|
// ==== ELDERBERRY =====
|
||||||
|
dest.writeInt(mKey);
|
||||||
|
dest.writeString(mCityId);
|
||||||
|
dest.writeString(mCity);
|
||||||
|
dest.writeString(mPostal);
|
||||||
|
dest.writeString(mCountryId);
|
||||||
|
dest.writeString(mCountry);
|
||||||
|
|
||||||
|
int parcelableSize = dest.dataPosition() - startPosition;
|
||||||
|
dest.setDataPosition(sizePosition);
|
||||||
|
dest.writeInt(parcelableSize);
|
||||||
|
dest.setDataPosition(startPosition + parcelableSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return new StringBuilder()
|
||||||
|
.append("{ City ID: ").append(mCityId)
|
||||||
|
.append(" City: ").append(mCity)
|
||||||
|
.append(" Postal Code: ").append(mPostal)
|
||||||
|
.append(" Country Id: ").append(mCountryId)
|
||||||
|
.append(" Country: ").append(mCountry).append("}")
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return mKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (obj instanceof WeatherLocation) {
|
||||||
|
WeatherLocation info = (WeatherLocation) obj;
|
||||||
|
return (info.hashCode() == this.mKey);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,84 @@
|
||||||
|
/*
|
||||||
|
* 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 cyanogenmod.weather.util;
|
||||||
|
|
||||||
|
|
||||||
|
import cyanogenmod.providers.WeatherContract;
|
||||||
|
|
||||||
|
import java.text.DecimalFormat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper class to perform operations and formatting of weather data
|
||||||
|
*/
|
||||||
|
public class WeatherUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a temperature expressed in degrees Celsius to degrees Fahrenheit
|
||||||
|
* @param celsius temperature in Celsius
|
||||||
|
* @return the temperature in degrees Fahrenheit
|
||||||
|
*/
|
||||||
|
public static float celsiusToFahrenheit(float celsius) {
|
||||||
|
return ((celsius * (9f/5f)) + 32f);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a temperature expressed in degrees Fahrenheit to degrees Celsius
|
||||||
|
* @param fahrenheit temperature in Fahrenheit
|
||||||
|
* @return the temperature in degrees Celsius
|
||||||
|
*/
|
||||||
|
public static float fahrenheitToCelsius(float fahrenheit) {
|
||||||
|
return ((fahrenheit - 32f) * (5f/9f));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a string representation of the temperature and unit supplied. The temperature value
|
||||||
|
* will be half-even rounded.
|
||||||
|
* @param temperature the temperature value
|
||||||
|
* @param tempUnit A valid {@link WeatherContract.WeatherColumns.TempUnit}
|
||||||
|
* @return A string with the format XX°F or XX°C (where XX is the temperature)
|
||||||
|
* depending on the temperature unit that was provided or null if an invalid unit is supplied
|
||||||
|
*/
|
||||||
|
public static String formatTemperature(float temperature, int tempUnit) {
|
||||||
|
if (!isValidTempUnit(tempUnit)) return null;
|
||||||
|
if (Float.isNaN(temperature)) return "-";
|
||||||
|
|
||||||
|
DecimalFormat noDigitsFormat = new DecimalFormat("0");
|
||||||
|
String noDigitsTemp = noDigitsFormat.format(temperature);
|
||||||
|
if (noDigitsTemp.equals("-0")) {
|
||||||
|
noDigitsTemp = "0";
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder formatted = new StringBuilder()
|
||||||
|
.append(noDigitsTemp).append("\u00b0");
|
||||||
|
if (tempUnit == WeatherContract.WeatherColumns.TempUnit.CELSIUS) {
|
||||||
|
formatted.append("C");
|
||||||
|
} else if (tempUnit == WeatherContract.WeatherColumns.TempUnit.FAHRENHEIT) {
|
||||||
|
formatted.append("F");
|
||||||
|
}
|
||||||
|
return formatted.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isValidTempUnit(int unit) {
|
||||||
|
switch (unit) {
|
||||||
|
case WeatherContract.WeatherColumns.TempUnit.CELSIUS:
|
||||||
|
case WeatherContract.WeatherColumns.TempUnit.FAHRENHEIT:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
/*
|
||||||
|
* 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 cyanogenmod.weatherservice;
|
||||||
|
|
||||||
|
import cyanogenmod.weatherservice.IWeatherProviderServiceClient;
|
||||||
|
import cyanogenmod.weather.RequestInfo;
|
||||||
|
|
||||||
|
/** @hide */
|
||||||
|
oneway interface IWeatherProviderService {
|
||||||
|
void processWeatherUpdateRequest(in RequestInfo request);
|
||||||
|
void processCityNameLookupRequest(in RequestInfo request);
|
||||||
|
void setServiceClient(in IWeatherProviderServiceClient client);
|
||||||
|
void cancelOngoingRequests();
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* 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 cyanogenmod.weatherservice;
|
||||||
|
|
||||||
|
import cyanogenmod.weather.RequestInfo;
|
||||||
|
import cyanogenmod.weatherservice.ServiceRequestResult;
|
||||||
|
|
||||||
|
/** @hide */
|
||||||
|
oneway interface IWeatherProviderServiceClient {
|
||||||
|
void setServiceRequestState(in RequestInfo requestInfo, in ServiceRequestResult result,
|
||||||
|
int state);
|
||||||
|
}
|
|
@ -0,0 +1,118 @@
|
||||||
|
/*
|
||||||
|
* 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 cyanogenmod.weatherservice;
|
||||||
|
|
||||||
|
import android.annotation.NonNull;
|
||||||
|
import android.os.RemoteException;
|
||||||
|
import cyanogenmod.weather.CMWeatherManager;
|
||||||
|
import cyanogenmod.weather.RequestInfo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class represents a request submitted by the system to the active weather provider service
|
||||||
|
*/
|
||||||
|
public final class ServiceRequest {
|
||||||
|
|
||||||
|
private final RequestInfo mInfo;
|
||||||
|
private final IWeatherProviderServiceClient mClient;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If a request is marked as cancelled, it means the client does not want to know anything about
|
||||||
|
* this request anymore
|
||||||
|
*/
|
||||||
|
private volatile boolean mCancelled;
|
||||||
|
|
||||||
|
/* package */ ServiceRequest(RequestInfo info, IWeatherProviderServiceClient client) {
|
||||||
|
mInfo = info;
|
||||||
|
mClient = client;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtains the request information
|
||||||
|
* @return {@link cyanogenmod.weather.RequestInfo}
|
||||||
|
*/
|
||||||
|
public RequestInfo getRequestInfo() {
|
||||||
|
return mInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method should be called once the request has been completed
|
||||||
|
*/
|
||||||
|
public void complete(@NonNull ServiceRequestResult result) {
|
||||||
|
if (!mCancelled) {
|
||||||
|
try {
|
||||||
|
final int requestType = mInfo.getRequestType();
|
||||||
|
switch (requestType) {
|
||||||
|
case RequestInfo.TYPE_GEO_LOCATION_REQ:
|
||||||
|
case RequestInfo.TYPE_WEATHER_LOCATION_REQ:
|
||||||
|
if (result.getWeatherInfo() == null) {
|
||||||
|
throw new IllegalStateException("The service request result does not"
|
||||||
|
+ " contain a valid WeatherInfo object");
|
||||||
|
}
|
||||||
|
mClient.setServiceRequestState(mInfo, result,
|
||||||
|
CMWeatherManager.WEATHER_REQUEST_COMPLETED);
|
||||||
|
break;
|
||||||
|
case RequestInfo.TYPE_LOOKUP_CITY_NAME_REQ:
|
||||||
|
if (result.getLocationLookupList() == null) {
|
||||||
|
//In case the user decided to mark this request as completed with an
|
||||||
|
//empty list. It's not necessarily a failure
|
||||||
|
mClient.setServiceRequestState(mInfo, null,
|
||||||
|
CMWeatherManager.LOOKUP_REQUEST_NO_MATCH_FOUND);
|
||||||
|
} else {
|
||||||
|
mClient.setServiceRequestState(mInfo, result,
|
||||||
|
CMWeatherManager.LOOKUP_REQUEST_COMPLETED);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method should be called if the service failed to process the request
|
||||||
|
* (no internet connection, time out, etc.)
|
||||||
|
*/
|
||||||
|
public void fail() {
|
||||||
|
if (!mCancelled) {
|
||||||
|
try {
|
||||||
|
final int requestType = mInfo.getRequestType();
|
||||||
|
switch (requestType) {
|
||||||
|
case RequestInfo.TYPE_GEO_LOCATION_REQ:
|
||||||
|
case RequestInfo.TYPE_WEATHER_LOCATION_REQ:
|
||||||
|
mClient.setServiceRequestState(mInfo, null,
|
||||||
|
CMWeatherManager.WEATHER_REQUEST_FAILED);
|
||||||
|
break;
|
||||||
|
case RequestInfo.TYPE_LOOKUP_CITY_NAME_REQ:
|
||||||
|
mClient.setServiceRequestState(mInfo, null,
|
||||||
|
CMWeatherManager.LOOKUP_REQUEST_FAILED);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called by the WeatherProviderService base class to notify we don't want this request anymore.
|
||||||
|
* The service implementing the WeatherProviderService will be notified of this action
|
||||||
|
* via onRequestCancelled()
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public void cancel() {
|
||||||
|
mCancelled = true;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
/*
|
||||||
|
* 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 cyanogenmod.weatherservice;
|
||||||
|
|
||||||
|
parcelable ServiceRequestResult;
|
|
@ -0,0 +1,203 @@
|
||||||
|
/*
|
||||||
|
* 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 cyanogenmod.weatherservice;
|
||||||
|
|
||||||
|
import android.annotation.NonNull;
|
||||||
|
import android.os.Parcel;
|
||||||
|
import android.os.Parcelable;
|
||||||
|
import cyanogenmod.os.Build;
|
||||||
|
import cyanogenmod.weather.WeatherLocation;
|
||||||
|
import cyanogenmod.weather.WeatherInfo;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this class to build a request result.
|
||||||
|
*/
|
||||||
|
public final class ServiceRequestResult implements Parcelable {
|
||||||
|
|
||||||
|
private WeatherInfo mWeatherInfo;
|
||||||
|
private ArrayList<WeatherLocation> mLocationLookupList;
|
||||||
|
private int mKey;
|
||||||
|
|
||||||
|
private ServiceRequestResult() {}
|
||||||
|
|
||||||
|
private ServiceRequestResult(Parcel in) {
|
||||||
|
int parcelableVersion = in.readInt();
|
||||||
|
int parcelableSize = in.readInt();
|
||||||
|
int startPosition = in.dataPosition();
|
||||||
|
if (parcelableVersion >= Build.CM_VERSION_CODES.ELDERBERRY) {
|
||||||
|
mKey = in.readInt();
|
||||||
|
int hasWeatherInfo = in.readInt();
|
||||||
|
if (hasWeatherInfo == 1) {
|
||||||
|
mWeatherInfo = WeatherInfo.CREATOR.createFromParcel(in);
|
||||||
|
}
|
||||||
|
int hasLocationLookupList = in.readInt();
|
||||||
|
if (hasLocationLookupList == 1) {
|
||||||
|
mLocationLookupList = new ArrayList<>();
|
||||||
|
int listSize = in.readInt();
|
||||||
|
while (listSize > 0) {
|
||||||
|
mLocationLookupList.add(WeatherLocation.CREATOR.createFromParcel(in));
|
||||||
|
listSize--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
in.setDataPosition(startPosition + parcelableSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final Creator<ServiceRequestResult> CREATOR
|
||||||
|
= new Creator<ServiceRequestResult>() {
|
||||||
|
@Override
|
||||||
|
public ServiceRequestResult createFromParcel(Parcel in) {
|
||||||
|
return new ServiceRequestResult(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ServiceRequestResult[] newArray(int size) {
|
||||||
|
return new ServiceRequestResult[size];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int describeContents() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeToParcel(Parcel dest, int flags) {
|
||||||
|
dest.writeInt(Build.PARCELABLE_VERSION);
|
||||||
|
|
||||||
|
int sizePosition = dest.dataPosition();
|
||||||
|
dest.writeInt(0);
|
||||||
|
int startPosition = dest.dataPosition();
|
||||||
|
|
||||||
|
// ==== ELDERBERRY =====
|
||||||
|
dest.writeInt(mKey);
|
||||||
|
if (mWeatherInfo != null) {
|
||||||
|
dest.writeInt(1);
|
||||||
|
mWeatherInfo.writeToParcel(dest, 0);
|
||||||
|
} else {
|
||||||
|
dest.writeInt(0);
|
||||||
|
}
|
||||||
|
if (mLocationLookupList != null) {
|
||||||
|
dest.writeInt(1);
|
||||||
|
dest.writeInt(mLocationLookupList.size());
|
||||||
|
for (WeatherLocation lookup : mLocationLookupList) {
|
||||||
|
lookup.writeToParcel(dest, 0);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
dest.writeInt(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int parcelableSize = dest.dataPosition() - startPosition;
|
||||||
|
dest.setDataPosition(sizePosition);
|
||||||
|
dest.writeInt(parcelableSize);
|
||||||
|
dest.setDataPosition(startPosition + parcelableSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Builder {
|
||||||
|
private WeatherInfo mBuilderWeatherInfo;
|
||||||
|
private ArrayList<WeatherLocation> mBuilderLocationLookupList;
|
||||||
|
public Builder() {
|
||||||
|
this.mBuilderWeatherInfo = null;
|
||||||
|
this.mBuilderLocationLookupList = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the supplied weather information to the result. Attempting to add a WeatherLocation
|
||||||
|
* list to the same builder will cause the system to throw IllegalArgumentException
|
||||||
|
*
|
||||||
|
* @param weatherInfo The WeatherInfo object holding the data that will be used to update
|
||||||
|
* the weather content provider
|
||||||
|
*/
|
||||||
|
public Builder setWeatherInfo(@NonNull WeatherInfo weatherInfo) {
|
||||||
|
if (mBuilderLocationLookupList != null) {
|
||||||
|
throw new IllegalArgumentException("Can't add weather information when you have"
|
||||||
|
+ " already added a WeatherLocation list");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (weatherInfo == null) {
|
||||||
|
throw new IllegalArgumentException("WeatherInfo can't be null");
|
||||||
|
}
|
||||||
|
|
||||||
|
mBuilderWeatherInfo = weatherInfo;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the supplied list of WeatherLocation objects to the result. Attempting to add a
|
||||||
|
* WeatherInfo object to the same builder will cause the system to throw
|
||||||
|
* IllegalArgumentException
|
||||||
|
*
|
||||||
|
* @param locations The list of WeatherLocation objects. The list should not be null
|
||||||
|
*/
|
||||||
|
public Builder setLocationLookupList(@NonNull ArrayList<WeatherLocation> locations) {
|
||||||
|
if (mBuilderWeatherInfo != null) {
|
||||||
|
throw new IllegalArgumentException("Can't add a WeatherLocation list when you have"
|
||||||
|
+ " already added weather information");
|
||||||
|
}
|
||||||
|
|
||||||
|
mBuilderLocationLookupList = locations;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link ServiceRequest} with the arguments
|
||||||
|
* supplied to this builder
|
||||||
|
* @return {@link ServiceRequestResult}
|
||||||
|
*/
|
||||||
|
public ServiceRequestResult build() {
|
||||||
|
ServiceRequestResult result = new ServiceRequestResult();
|
||||||
|
result.mWeatherInfo = this.mBuilderWeatherInfo;
|
||||||
|
result.mLocationLookupList = this.mBuilderLocationLookupList;
|
||||||
|
result.mKey = this.hashCode();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The WeatherInfo object supplied by the weather provider service
|
||||||
|
*/
|
||||||
|
public WeatherInfo getWeatherInfo() {
|
||||||
|
return mWeatherInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The list of WeatherLocation objects supplied by the weather provider service
|
||||||
|
*/
|
||||||
|
public ArrayList<WeatherLocation> getLocationLookupList() {
|
||||||
|
return mLocationLookupList;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
//The hashcode of this object was stored when it was built. This is an
|
||||||
|
//immutable object but we need to preserve the hashcode since this object is parcelable and
|
||||||
|
//it's reconstructed over IPC, and clients of this object might want to store it in a
|
||||||
|
//collection that relies on this code to identify the object
|
||||||
|
return mKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (obj instanceof ServiceRequestResult) {
|
||||||
|
ServiceRequestResult request = (ServiceRequestResult) obj;
|
||||||
|
return (request.hashCode() == this.mKey);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,196 @@
|
||||||
|
/*
|
||||||
|
* 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 cyanogenmod.weatherservice;
|
||||||
|
|
||||||
|
import android.annotation.SdkConstant;
|
||||||
|
import android.app.Service;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.IBinder;
|
||||||
|
import android.os.Looper;
|
||||||
|
import android.os.Message;
|
||||||
|
import cyanogenmod.weather.RequestInfo;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.WeakHashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is the base class for implementing a weather provider service. A weather provider service
|
||||||
|
* can handle weather update requests and update the weather content provider data by processing
|
||||||
|
* a {@link ServiceRequest}
|
||||||
|
*
|
||||||
|
* A print service is declared as any other service in an AndroidManifest.xml but it must also
|
||||||
|
* specify that in handles the {@link android.content.Intent} with action
|
||||||
|
* {@link #SERVICE_INTERFACE cyanogenmod.weatherservice.WeatherProviderService}. Failure to declare
|
||||||
|
* this intent will cause the system to ignore the weather provider service. Additionally, a
|
||||||
|
* weather provider service must request the
|
||||||
|
* {@link cyanogenmod.platform.Manifest.permission#BIND_WEATHER_PROVIDER_SERVICE} permission to
|
||||||
|
* ensure that only the system can bind to it. Failure to request this permission will cause the
|
||||||
|
* system to ignore this weather provider service. Following is an example declaration:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* <service android:name=".MyWeatherProviderService"
|
||||||
|
* android:permission="cyanogenmod.permission.BIND_WEATHER_PROVIDER_SERVICE">
|
||||||
|
* <intent-filter>
|
||||||
|
* <action android:name="cyanogenmod.weatherservice.WeatherProviderService" />
|
||||||
|
* <intent-filter>
|
||||||
|
* . . .
|
||||||
|
* </service>
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public abstract class WeatherProviderService extends Service {
|
||||||
|
|
||||||
|
private Handler mHandler;
|
||||||
|
private IWeatherProviderServiceClient mClient;
|
||||||
|
private Set<ServiceRequest> mWeakRequestsSet
|
||||||
|
= Collections.newSetFromMap(new WeakHashMap<ServiceRequest, Boolean>());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link android.content.Intent} action that must be declared as handled by a service in
|
||||||
|
* its manifest for the system to recognize it as a weather provider service
|
||||||
|
*/
|
||||||
|
@SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
|
||||||
|
public static final String SERVICE_INTERFACE
|
||||||
|
= "cyanogenmod.weatherservice.WeatherProviderService";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Name under which a {@link WeatherProviderService} publishes information about itself.
|
||||||
|
* This meta-data must reference an XML resource containing
|
||||||
|
* a <code><weather-provider-service></code>
|
||||||
|
* tag.
|
||||||
|
*/
|
||||||
|
public static final String SERVICE_META_DATA = "cyanogenmod.weatherservice";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected final void attachBaseContext(Context base) {
|
||||||
|
super.attachBaseContext(base);
|
||||||
|
mHandler = new ServiceHandler(base.getMainLooper());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final IBinder onBind(Intent intent) {
|
||||||
|
return mBinder;
|
||||||
|
}
|
||||||
|
|
||||||
|
private final IWeatherProviderService.Stub mBinder = new IWeatherProviderService.Stub() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void processWeatherUpdateRequest(final RequestInfo info) {
|
||||||
|
mHandler.obtainMessage(ServiceHandler.MSG_ON_NEW_REQUEST, info).sendToTarget();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void processCityNameLookupRequest(final RequestInfo info) {
|
||||||
|
mHandler.obtainMessage(ServiceHandler.MSG_ON_NEW_REQUEST, info).sendToTarget();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setServiceClient(IWeatherProviderServiceClient client) {
|
||||||
|
mHandler.obtainMessage(ServiceHandler.MSG_SET_CLIENT, client).sendToTarget();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cancelOngoingRequests() {
|
||||||
|
mHandler.obtainMessage(ServiceHandler.MSG_CANCEL_ONGOING_REQUESTS).sendToTarget();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private class ServiceHandler extends Handler {
|
||||||
|
|
||||||
|
public ServiceHandler(Looper looper) {
|
||||||
|
super(looper);
|
||||||
|
}
|
||||||
|
public static final int MSG_SET_CLIENT = 1;
|
||||||
|
public static final int MSG_ON_NEW_REQUEST = 2;
|
||||||
|
public static final int MSG_CANCEL_ONGOING_REQUESTS = 3;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleMessage(Message msg) {
|
||||||
|
switch (msg.what) {
|
||||||
|
case MSG_SET_CLIENT: {
|
||||||
|
mClient = (IWeatherProviderServiceClient) msg.obj;
|
||||||
|
if (mClient != null) {
|
||||||
|
onConnected();
|
||||||
|
} else {
|
||||||
|
onDisconnected();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case MSG_ON_NEW_REQUEST: {
|
||||||
|
RequestInfo info = (RequestInfo) msg.obj;
|
||||||
|
if (info != null) {
|
||||||
|
ServiceRequest request = new ServiceRequest(info, mClient);
|
||||||
|
synchronized (mWeakRequestsSet) {
|
||||||
|
mWeakRequestsSet.add(request);
|
||||||
|
}
|
||||||
|
onRequestSubmitted(request);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case MSG_CANCEL_ONGOING_REQUESTS: {
|
||||||
|
synchronized (mWeakRequestsSet) {
|
||||||
|
for (final ServiceRequest request : mWeakRequestsSet) {
|
||||||
|
if (request != null) {
|
||||||
|
request.cancel();
|
||||||
|
mWeakRequestsSet.remove(request);
|
||||||
|
mHandler.post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
onRequestCancelled(request);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The system has connected to this service.
|
||||||
|
*/
|
||||||
|
protected void onConnected() {
|
||||||
|
/* Do nothing */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The system has disconnected from this service.
|
||||||
|
*/
|
||||||
|
protected void onDisconnected() {
|
||||||
|
/* Do nothing */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A new request has been submitted to this service
|
||||||
|
* @param request The service request to be processed by this service
|
||||||
|
*/
|
||||||
|
protected abstract void onRequestSubmitted(ServiceRequest request);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the system is not interested on this request anymore. Note that the service
|
||||||
|
* <b>has marked the request as cancelled</b> and you must stop any ongoing operation
|
||||||
|
* (such as pulling data from internet) that this service could've been performing to honor the
|
||||||
|
* request.
|
||||||
|
*
|
||||||
|
* @param request The request cancelled by the system
|
||||||
|
*/
|
||||||
|
protected abstract void onRequestCancelled(ServiceRequest request);
|
||||||
|
}
|
|
@ -66,4 +66,5 @@ public class CMMetricsLogger extends MetricsLogger {
|
||||||
public static final int TILE_HEADS_UP = BASE + 39;
|
public static final int TILE_HEADS_UP = BASE + 39;
|
||||||
public static final int TILE_BATTERY_SAVER = BASE + 40;
|
public static final int TILE_BATTERY_SAVER = BASE + 40;
|
||||||
public static final int TILE_CAFFEINE = BASE + 41;
|
public static final int TILE_CAFFEINE = BASE + 41;
|
||||||
|
public static final int WEATHER_SETTINGS = BASE + 42;
|
||||||
}
|
}
|
||||||
|
|
|
@ -595,6 +595,8 @@ package cyanogenmod.platform {
|
||||||
ctor public Manifest.permission();
|
ctor public Manifest.permission();
|
||||||
field public static final java.lang.String ACCESS_APP_SUGGESTIONS = "cyanogenmod.permission.ACCESS_APP_SUGGESTIONS";
|
field public static final java.lang.String ACCESS_APP_SUGGESTIONS = "cyanogenmod.permission.ACCESS_APP_SUGGESTIONS";
|
||||||
field public static final java.lang.String ACCESS_THEME_MANAGER = "cyanogenmod.permission.ACCESS_THEME_MANAGER";
|
field public static final java.lang.String ACCESS_THEME_MANAGER = "cyanogenmod.permission.ACCESS_THEME_MANAGER";
|
||||||
|
field public static final java.lang.String ACCESS_WEATHER_MANAGER = "cyanogenmod.permission.ACCESS_WEATHER_MANAGER";
|
||||||
|
field public static final java.lang.String BIND_WEATHER_PROVIDER_SERVICE = "cyanogenmod.permission.BIND_WEATHER_PROVIDER_SERVICE";
|
||||||
field public static final java.lang.String HARDWARE_ABSTRACTION_ACCESS = "cyanogenmod.permission.HARDWARE_ABSTRACTION_ACCESS";
|
field public static final java.lang.String HARDWARE_ABSTRACTION_ACCESS = "cyanogenmod.permission.HARDWARE_ABSTRACTION_ACCESS";
|
||||||
field public static final java.lang.String LIVE_LOCK_SCREEN_MANAGER_ACCESS = "cyanogenmod.permission.LIVE_LOCK_SCREEN_MANAGER_ACCESS";
|
field public static final java.lang.String LIVE_LOCK_SCREEN_MANAGER_ACCESS = "cyanogenmod.permission.LIVE_LOCK_SCREEN_MANAGER_ACCESS";
|
||||||
field public static final java.lang.String MANAGE_ALARMS = "cyanogenmod.permission.MANAGE_ALARMS";
|
field public static final java.lang.String MANAGE_ALARMS = "cyanogenmod.permission.MANAGE_ALARMS";
|
||||||
|
@ -609,11 +611,13 @@ package cyanogenmod.platform {
|
||||||
field public static final java.lang.String READ_ALARMS = "cyanogenmod.permission.READ_ALARMS";
|
field public static final java.lang.String READ_ALARMS = "cyanogenmod.permission.READ_ALARMS";
|
||||||
field public static final java.lang.String READ_MSIM_PHONE_STATE = "cyanogenmod.permission.READ_MSIM_PHONE_STATE";
|
field public static final java.lang.String READ_MSIM_PHONE_STATE = "cyanogenmod.permission.READ_MSIM_PHONE_STATE";
|
||||||
field public static final java.lang.String READ_THEMES = "cyanogenmod.permission.READ_THEMES";
|
field public static final java.lang.String READ_THEMES = "cyanogenmod.permission.READ_THEMES";
|
||||||
|
field public static final java.lang.String READ_WEATHER = "cyanogenmod.permission.READ_WEATHER";
|
||||||
field public static final java.lang.String THIRD_PARTY_KEYGUARD = "android.permission.THIRD_PARTY_KEYGUARD";
|
field public static final java.lang.String THIRD_PARTY_KEYGUARD = "android.permission.THIRD_PARTY_KEYGUARD";
|
||||||
field public static final java.lang.String WRITE_ALARMS = "cyanogenmod.permission.WRITE_ALARMS";
|
field public static final java.lang.String WRITE_ALARMS = "cyanogenmod.permission.WRITE_ALARMS";
|
||||||
field public static final java.lang.String WRITE_SECURE_SETTINGS = "cyanogenmod.permission.WRITE_SECURE_SETTINGS";
|
field public static final java.lang.String WRITE_SECURE_SETTINGS = "cyanogenmod.permission.WRITE_SECURE_SETTINGS";
|
||||||
field public static final java.lang.String WRITE_SETTINGS = "cyanogenmod.permission.WRITE_SETTINGS";
|
field public static final java.lang.String WRITE_SETTINGS = "cyanogenmod.permission.WRITE_SETTINGS";
|
||||||
field public static final java.lang.String WRITE_THEMES = "cyanogenmod.permission.WRITE_THEMES";
|
field public static final java.lang.String WRITE_THEMES = "cyanogenmod.permission.WRITE_THEMES";
|
||||||
|
field public static final java.lang.String WRITE_WEATHER = "cyanogenmod.permission.WRITE_WEATHER";
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class R {
|
public final class R {
|
||||||
|
@ -1043,6 +1047,94 @@ package cyanogenmod.providers {
|
||||||
field public static final int UPDATING = 2; // 0x2
|
field public static final int UPDATING = 2; // 0x2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class WeatherContract {
|
||||||
|
ctor public WeatherContract();
|
||||||
|
field public static final java.lang.String AUTHORITY = "com.cyanogenmod.weather";
|
||||||
|
field public static final android.net.Uri AUTHORITY_URI;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class WeatherContract.WeatherColumns {
|
||||||
|
ctor public WeatherContract.WeatherColumns();
|
||||||
|
field public static final android.net.Uri CONTENT_URI;
|
||||||
|
field public static final android.net.Uri CURRENT_AND_FORECAST_WEATHER_URI;
|
||||||
|
field public static final java.lang.String CURRENT_CITY = "city";
|
||||||
|
field public static final java.lang.String CURRENT_CITY_ID = "city_id";
|
||||||
|
field public static final java.lang.String CURRENT_CONDITION = "condition";
|
||||||
|
field public static final java.lang.String CURRENT_CONDITION_CODE = "condition_code";
|
||||||
|
field public static final java.lang.String CURRENT_HUMIDITY = "humidity";
|
||||||
|
field public static final java.lang.String CURRENT_TEMPERATURE = "temperature";
|
||||||
|
field public static final java.lang.String CURRENT_TEMPERATURE_UNIT = "temperature_unit";
|
||||||
|
field public static final java.lang.String CURRENT_TIMESTAMP = "timestamp";
|
||||||
|
field public static final android.net.Uri CURRENT_WEATHER_URI;
|
||||||
|
field public static final java.lang.String CURRENT_WIND_DIRECTION = "wind_direction";
|
||||||
|
field public static final java.lang.String CURRENT_WIND_SPEED = "wind_speed";
|
||||||
|
field public static final java.lang.String CURRENT_WIND_SPEED_UNIT = "wind_speed_unit";
|
||||||
|
field public static final java.lang.String FORECAST_CONDITION = "forecast_condition";
|
||||||
|
field public static final java.lang.String FORECAST_CONDITION_CODE = "forecast_condition_code";
|
||||||
|
field public static final java.lang.String FORECAST_HIGH = "forecast_high";
|
||||||
|
field public static final java.lang.String FORECAST_LOW = "forecast_low";
|
||||||
|
field public static final android.net.Uri FORECAST_WEATHER_URI;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class WeatherContract.WeatherColumns.TempUnit {
|
||||||
|
field public static final int CELSIUS = 1; // 0x1
|
||||||
|
field public static final int FAHRENHEIT = 2; // 0x2
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class WeatherContract.WeatherColumns.WeatherCode {
|
||||||
|
field public static final int BLOWING_SNOW = 14; // 0xe
|
||||||
|
field public static final int BLUSTERY = 22; // 0x16
|
||||||
|
field public static final int CLEAR_NIGHT = 30; // 0x1e
|
||||||
|
field public static final int CLOUDY = 25; // 0x19
|
||||||
|
field public static final int COLD = 24; // 0x18
|
||||||
|
field public static final int DRIZZLE = 9; // 0x9
|
||||||
|
field public static final int DUST = 18; // 0x12
|
||||||
|
field public static final int FAIR_DAY = 33; // 0x21
|
||||||
|
field public static final int FAIR_NIGHT = 32; // 0x20
|
||||||
|
field public static final int FOGGY = 19; // 0x13
|
||||||
|
field public static final int FREEZING_DRIZZLE = 8; // 0x8
|
||||||
|
field public static final int FREEZING_RAIN = 10; // 0xa
|
||||||
|
field public static final int HAIL = 16; // 0x10
|
||||||
|
field public static final int HAZE = 20; // 0x14
|
||||||
|
field public static final int HEAVY_SNOW = 39; // 0x27
|
||||||
|
field public static final int HOT = 35; // 0x23
|
||||||
|
field public static final int HURRICANE = 2; // 0x2
|
||||||
|
field public static final int ISOLATED_THUNDERSHOWERS = 44; // 0x2c
|
||||||
|
field public static final int ISOLATED_THUNDERSTORMS = 36; // 0x24
|
||||||
|
field public static final int LIGHT_SNOW_SHOWERS = 13; // 0xd
|
||||||
|
field public static final int MIXED_RAIN_AND_HAIL = 34; // 0x22
|
||||||
|
field public static final int MIXED_RAIN_AND_SLEET = 6; // 0x6
|
||||||
|
field public static final int MIXED_RAIN_AND_SNOW = 5; // 0x5
|
||||||
|
field public static final int MIXED_SNOW_AND_SLEET = 7; // 0x7
|
||||||
|
field public static final int MOSTLY_CLOUDY_DAY = 27; // 0x1b
|
||||||
|
field public static final int MOSTLY_CLOUDY_NIGHT = 26; // 0x1a
|
||||||
|
field public static final int NOT_AVAILABLE = 3200; // 0xc80
|
||||||
|
field public static final int PARTLY_CLOUDY = 41; // 0x29
|
||||||
|
field public static final int PARTLY_CLOUDY_DAY = 29; // 0x1d
|
||||||
|
field public static final int PARTLY_CLOUDY_NIGHT = 28; // 0x1c
|
||||||
|
field public static final int SCATTERED_SHOWERS = 38; // 0x26
|
||||||
|
field public static final int SCATTERED_SNOW_SHOWERS = 40; // 0x28
|
||||||
|
field public static final int SCATTERED_THUNDERSTORMS = 37; // 0x25
|
||||||
|
field public static final int SEVERE_THUNDERSTORMS = 3; // 0x3
|
||||||
|
field public static final int SHOWERS = 11; // 0xb
|
||||||
|
field public static final int SLEET = 17; // 0x11
|
||||||
|
field public static final int SMOKY = 21; // 0x15
|
||||||
|
field public static final int SNOW = 15; // 0xf
|
||||||
|
field public static final int SNOW_FLURRIES = 12; // 0xc
|
||||||
|
field public static final int SNOW_SHOWERS = 43; // 0x2b
|
||||||
|
field public static final int SUNNY = 31; // 0x1f
|
||||||
|
field public static final int THUNDERSHOWER = 42; // 0x2a
|
||||||
|
field public static final int THUNDERSTORMS = 4; // 0x4
|
||||||
|
field public static final int TORNADO = 0; // 0x0
|
||||||
|
field public static final int TROPICAL_STORM = 1; // 0x1
|
||||||
|
field public static final int WINDY = 23; // 0x17
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class WeatherContract.WeatherColumns.WindSpeedUnit {
|
||||||
|
field public static final int KPH = 1; // 0x1
|
||||||
|
field public static final int MPH = 2; // 0x2
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
package cyanogenmod.themes {
|
package cyanogenmod.themes {
|
||||||
|
@ -1155,3 +1247,147 @@ package cyanogenmod.util {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
package cyanogenmod.weather {
|
||||||
|
|
||||||
|
public class CMWeatherManager {
|
||||||
|
method public java.lang.String getActiveWeatherServiceProviderLabel();
|
||||||
|
method public static cyanogenmod.weather.CMWeatherManager getInstance(android.content.Context);
|
||||||
|
method public void lookupCity(java.lang.String, cyanogenmod.weather.CMWeatherManager.LookupCityRequestListener);
|
||||||
|
method public void registerWeatherServiceProviderChangeListener(cyanogenmod.weather.CMWeatherManager.WeatherServiceProviderChangeListener);
|
||||||
|
method public void requestWeatherUpdate(android.location.Location, cyanogenmod.weather.CMWeatherManager.WeatherUpdateRequestListener);
|
||||||
|
method public void requestWeatherUpdate(cyanogenmod.weather.WeatherLocation, cyanogenmod.weather.CMWeatherManager.WeatherUpdateRequestListener);
|
||||||
|
method public void unregisterWeatherServiceProviderChangeListener(cyanogenmod.weather.CMWeatherManager.WeatherServiceProviderChangeListener);
|
||||||
|
field public static final int WEATHER_REQUEST_ALREADY_IN_PROGRESS = -3; // 0xfffffffd
|
||||||
|
field public static final int WEATHER_REQUEST_COMPLETED = 1; // 0x1
|
||||||
|
field public static final int WEATHER_REQUEST_FAILED = -2; // 0xfffffffe
|
||||||
|
field public static final int WEATHER_REQUEST_SUBMITTED_TOO_SOON = -1; // 0xffffffff
|
||||||
|
}
|
||||||
|
|
||||||
|
public static abstract interface CMWeatherManager.LookupCityRequestListener {
|
||||||
|
method public abstract void onLookupCityRequestCompleted(java.util.ArrayList<cyanogenmod.weather.WeatherLocation>);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static abstract interface CMWeatherManager.WeatherServiceProviderChangeListener {
|
||||||
|
method public abstract void onWeatherServiceProviderChanged(java.lang.String);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static abstract interface CMWeatherManager.WeatherUpdateRequestListener {
|
||||||
|
method public abstract void onWeatherRequestCompleted(int, cyanogenmod.weather.WeatherInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class RequestInfo implements android.os.Parcelable {
|
||||||
|
method public int describeContents();
|
||||||
|
method public java.lang.String getCityName();
|
||||||
|
method public android.location.Location getLocation();
|
||||||
|
method public int getRequestType();
|
||||||
|
method public int getTemperatureUnit();
|
||||||
|
method public cyanogenmod.weather.WeatherLocation getWeatherLocation();
|
||||||
|
method public void writeToParcel(android.os.Parcel, int);
|
||||||
|
field public static final android.os.Parcelable.Creator<cyanogenmod.weather.RequestInfo> CREATOR;
|
||||||
|
field public static final int TYPE_GEO_LOCATION_REQ = 1; // 0x1
|
||||||
|
field public static final int TYPE_LOOKUP_CITY_NAME_REQ = 3; // 0x3
|
||||||
|
field public static final int TYPE_WEATHER_LOCATION_REQ = 2; // 0x2
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class WeatherInfo implements android.os.Parcelable {
|
||||||
|
method public int describeContents();
|
||||||
|
method public java.lang.String getCity();
|
||||||
|
method public java.lang.String getCityId();
|
||||||
|
method public int getConditionCode();
|
||||||
|
method public java.util.ArrayList<cyanogenmod.weather.WeatherInfo.DayForecast> getForecasts();
|
||||||
|
method public float getHumidity();
|
||||||
|
method public float getTemperature();
|
||||||
|
method public int getTemperatureUnit();
|
||||||
|
method public long getTimestamp();
|
||||||
|
method public float getWindDirection();
|
||||||
|
method public float getWindSpeed();
|
||||||
|
method public int getWindSpeedUnit();
|
||||||
|
method public void writeToParcel(android.os.Parcel, int);
|
||||||
|
field public static final android.os.Parcelable.Creator<cyanogenmod.weather.WeatherInfo> CREATOR;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class WeatherInfo.Builder {
|
||||||
|
ctor public WeatherInfo.Builder(long);
|
||||||
|
method public cyanogenmod.weather.WeatherInfo build();
|
||||||
|
method public cyanogenmod.weather.WeatherInfo.Builder setCity(java.lang.String, java.lang.String);
|
||||||
|
method public cyanogenmod.weather.WeatherInfo.Builder setForecast(java.util.ArrayList<cyanogenmod.weather.WeatherInfo.DayForecast>);
|
||||||
|
method public cyanogenmod.weather.WeatherInfo.Builder setHumidity(float);
|
||||||
|
method public cyanogenmod.weather.WeatherInfo.Builder setTemperature(float, int);
|
||||||
|
method public cyanogenmod.weather.WeatherInfo.Builder setWeatherCondition(int);
|
||||||
|
method public cyanogenmod.weather.WeatherInfo.Builder setWind(float, float, int);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class WeatherInfo.DayForecast implements android.os.Parcelable {
|
||||||
|
method public int describeContents();
|
||||||
|
method public int getConditionCode();
|
||||||
|
method public float getHigh();
|
||||||
|
method public float getLow();
|
||||||
|
method public void writeToParcel(android.os.Parcel, int);
|
||||||
|
field public static final android.os.Parcelable.Creator<cyanogenmod.weather.WeatherInfo.DayForecast> CREATOR;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class WeatherInfo.DayForecast.Builder {
|
||||||
|
ctor public WeatherInfo.DayForecast.Builder();
|
||||||
|
method public cyanogenmod.weather.WeatherInfo.DayForecast build();
|
||||||
|
method public cyanogenmod.weather.WeatherInfo.DayForecast.Builder setHigh(float);
|
||||||
|
method public cyanogenmod.weather.WeatherInfo.DayForecast.Builder setLow(float);
|
||||||
|
method public cyanogenmod.weather.WeatherInfo.DayForecast.Builder setWeatherCondition(int);
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class WeatherLocation implements android.os.Parcelable {
|
||||||
|
method public int describeContents();
|
||||||
|
method public java.lang.String getCity();
|
||||||
|
method public java.lang.String getCityId();
|
||||||
|
method public java.lang.String getCountry();
|
||||||
|
method public java.lang.String getCountryId();
|
||||||
|
method public java.lang.String getPostalCode();
|
||||||
|
method public void writeToParcel(android.os.Parcel, int);
|
||||||
|
field public static final android.os.Parcelable.Creator<cyanogenmod.weather.WeatherLocation> CREATOR;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class WeatherLocation.Builder {
|
||||||
|
ctor public WeatherLocation.Builder(java.lang.String, java.lang.String);
|
||||||
|
method public cyanogenmod.weather.WeatherLocation build();
|
||||||
|
method public cyanogenmod.weather.WeatherLocation.Builder setCountry(java.lang.String, java.lang.String);
|
||||||
|
method public cyanogenmod.weather.WeatherLocation.Builder setPostalCode(java.lang.String);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
package cyanogenmod.weatherservice {
|
||||||
|
|
||||||
|
public final class ServiceRequest {
|
||||||
|
method public void complete(cyanogenmod.weatherservice.ServiceRequestResult);
|
||||||
|
method public void fail();
|
||||||
|
method public cyanogenmod.weather.RequestInfo getRequestInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class ServiceRequestResult implements android.os.Parcelable {
|
||||||
|
method public int describeContents();
|
||||||
|
method public java.util.ArrayList<cyanogenmod.weather.WeatherLocation> getLocationLookupList();
|
||||||
|
method public cyanogenmod.weather.WeatherInfo getWeatherInfo();
|
||||||
|
method public void writeToParcel(android.os.Parcel, int);
|
||||||
|
field public static final android.os.Parcelable.Creator<cyanogenmod.weatherservice.ServiceRequestResult> CREATOR;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ServiceRequestResult.Builder {
|
||||||
|
ctor public ServiceRequestResult.Builder();
|
||||||
|
method public cyanogenmod.weatherservice.ServiceRequestResult build();
|
||||||
|
method public cyanogenmod.weatherservice.ServiceRequestResult.Builder setLocationLookupList(java.util.ArrayList<cyanogenmod.weather.WeatherLocation>);
|
||||||
|
method public cyanogenmod.weatherservice.ServiceRequestResult.Builder setWeatherInfo(cyanogenmod.weather.WeatherInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract class WeatherProviderService extends android.app.Service {
|
||||||
|
ctor public WeatherProviderService();
|
||||||
|
method protected final void attachBaseContext(android.content.Context);
|
||||||
|
method public final android.os.IBinder onBind(android.content.Intent);
|
||||||
|
method protected void onConnected();
|
||||||
|
method protected void onDisconnected();
|
||||||
|
method protected abstract void onRequestCancelled(cyanogenmod.weatherservice.ServiceRequest);
|
||||||
|
method protected abstract void onRequestSubmitted(cyanogenmod.weatherservice.ServiceRequest);
|
||||||
|
field public static final java.lang.String SERVICE_INTERFACE = "cyanogenmod.weatherservice.WeatherProviderService";
|
||||||
|
field public static final java.lang.String SERVICE_META_DATA = "cyanogenmod.weatherservice";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue