Fixup: core: Add OmniJawsClient
Cleaned up version from https://github.com/omnirom/android_packages_apps_OmniLib Keeping it accessible for weather client packages as well as SystemUI package. Change-Id: Ifadfaa7f4438e3bde52f116f002a7a5273790184 Squashed: From: Pranav Vashi <neobuddy89@gmail.com> Date: Sat, 7 Sep 2024 21:02:59 +0530 Subject: [PATCH] OmnijawsClient: Close cursor to avoid memory leak From: minaripenguin <minaripenguin@users.noreply.github.com> Date: Thu, 12 Sep 2024 21:36:35 +0800 Subject: [PATCH] OmnijawsClient: Fix memory leak rmp - do not hold long live context references especially from views, revise memory leak prone practices neobuddy89 - retain required helper functions Change-Id: Ie4cccb0eb37197be06595e75729883d078d06541 Co-authored-by: Pranav Vashi <neobuddy89@gmail.com> Co-authored-by: minaripenguin <minaripenguin@users.noreply.github.com> Co-authored-by: rmp22 <195054967+rmp22@users.noreply.github.com> Signed-off-by: Pranav Vashi <neobuddy89@gmail.com> Signed-off-by: Dmitrii <bankersenator@gmail.com> Signed-off-by: MOVZX <movzx@yahoo.com>
This commit is contained in:
@@ -1,27 +1,21 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The OmniROM Project
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.android.internal.util.crdroid;
|
||||
* Copyright (C) 2021 The OmniROM project
|
||||
* Copyright (C) 2022-2025 crDroid Android 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.
|
||||
*/
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
package com.android.internal.util.crdroid;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
@@ -30,28 +24,33 @@ import android.content.IntentFilter;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.content.res.Resources;
|
||||
import android.database.ContentObserver;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
import android.os.UserHandle;
|
||||
import android.provider.Settings;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
public class OmniJawsClient {
|
||||
|
||||
private static final String TAG = "OmniJawsClient";
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
public static final String SERVICE_PACKAGE = "org.omnirom.omnijaws";
|
||||
public static final Uri WEATHER_URI
|
||||
= Uri.parse("content://org.omnirom.omnijaws.provider/weather");
|
||||
public static final Uri SETTINGS_URI
|
||||
= Uri.parse("content://org.omnirom.omnijaws.provider/settings");
|
||||
public static final Uri CONTROL_URI
|
||||
= Uri.parse("content://org.omnirom.omnijaws.provider/control");
|
||||
public static final Uri WEATHER_URI = Uri.parse("content://org.omnirom.omnijaws.provider/weather");
|
||||
public static final Uri SETTINGS_URI = Uri.parse("content://org.omnirom.omnijaws.provider/settings");
|
||||
public static final String WEATHER_UPDATE = SERVICE_PACKAGE + ".WEATHER_UPDATE";
|
||||
public static final String WEATHER_ERROR = SERVICE_PACKAGE + ".WEATHER_ERROR";
|
||||
|
||||
private static final String ICON_PACKAGE_DEFAULT = "org.omnirom.omnijaws";
|
||||
private static final String ICON_PREFIX_DEFAULT = "google_new_light";
|
||||
@@ -59,39 +58,317 @@ public class OmniJawsClient {
|
||||
public static final int EXTRA_ERROR_NETWORK = 0;
|
||||
public static final int EXTRA_ERROR_LOCATION = 1;
|
||||
public static final int EXTRA_ERROR_DISABLED = 2;
|
||||
|
||||
private boolean mWeatherReceiverRegistered;
|
||||
|
||||
public static final String[] WEATHER_PROJECTION = new String[]{
|
||||
"city",
|
||||
"wind_speed",
|
||||
"wind_direction",
|
||||
"condition_code",
|
||||
"temperature",
|
||||
"humidity",
|
||||
"condition",
|
||||
"forecast_low",
|
||||
"forecast_high",
|
||||
"forecast_condition",
|
||||
"forecast_condition_code",
|
||||
"time_stamp",
|
||||
"forecast_date",
|
||||
"pin_wheel"
|
||||
public static final String[] WEATHER_PROJECTION = {
|
||||
"city", "wind_speed", "wind_direction", "condition_code", "temperature",
|
||||
"humidity", "condition", "forecast_low", "forecast_high", "forecast_condition",
|
||||
"forecast_condition_code", "time_stamp", "forecast_date", "pin_wheel"
|
||||
};
|
||||
|
||||
public static final String[] SETTINGS_PROJECTION = new String[] {
|
||||
"enabled",
|
||||
"units",
|
||||
"provider",
|
||||
"setup",
|
||||
"icon_pack"
|
||||
public static final String[] SETTINGS_PROJECTION = {
|
||||
"enabled", "units", "provider", "setup", "icon_pack"
|
||||
};
|
||||
|
||||
private static final String WEATHER_UPDATE = SERVICE_PACKAGE + ".WEATHER_UPDATE";
|
||||
private static final String WEATHER_ERROR = SERVICE_PACKAGE + ".WEATHER_ERROR";
|
||||
|
||||
private static final DecimalFormat sNoDigitsFormat = new DecimalFormat("0");
|
||||
|
||||
private static OmniJawsClient sInstance;
|
||||
|
||||
private WeatherInfo mCachedInfo;
|
||||
private Resources mRes;
|
||||
private String mPackageName;
|
||||
private String mIconPrefix;
|
||||
private String mSettingIconPackage;
|
||||
private boolean mMetric;
|
||||
|
||||
private final List<WeakReference<OmniJawsObserver>> mObservers = new ArrayList<>();
|
||||
private WeatherUpdateReceiver mReceiver;
|
||||
private boolean mWeatherReceiverRegistered = false;
|
||||
|
||||
public static OmniJawsClient get() {
|
||||
if (sInstance == null) {
|
||||
synchronized (OmniJawsClient.class) {
|
||||
if (sInstance == null) {
|
||||
sInstance = new OmniJawsClient();
|
||||
}
|
||||
}
|
||||
}
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
public interface OmniJawsObserver {
|
||||
void weatherUpdated();
|
||||
void weatherError(int errorReason);
|
||||
default void updateSettings() {}
|
||||
}
|
||||
|
||||
private class WeatherUpdateReceiver extends BroadcastReceiver {
|
||||
@Override public void onReceive(Context context, Intent intent) {
|
||||
String action = intent.getAction();
|
||||
pruneDeadObservers();
|
||||
for (WeakReference<OmniJawsObserver> ref : mObservers) {
|
||||
OmniJawsObserver obs = ref.get();
|
||||
if (obs == null) continue;
|
||||
if (WEATHER_UPDATE.equals(action)) {
|
||||
obs.weatherUpdated();
|
||||
} else if (WEATHER_ERROR.equals(action)) {
|
||||
obs.weatherError(intent.getIntExtra(EXTRA_ERROR, 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Intent getSettingsIntent(Context context) {
|
||||
if (isOmniJawsEnabled(context)) {
|
||||
return new Intent(Intent.ACTION_MAIN)
|
||||
.setClassName(SERVICE_PACKAGE, SERVICE_PACKAGE + ".SettingsActivity");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Intent getWeatherActivityIntent(Context context) {
|
||||
if (isOmniJawsEnabled(context)) {
|
||||
return new Intent(Intent.ACTION_MAIN)
|
||||
.setClassName(SERVICE_PACKAGE, SERVICE_PACKAGE + ".WeatherActivity");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public WeatherInfo getWeatherInfo() {
|
||||
return mCachedInfo;
|
||||
}
|
||||
|
||||
public void queryWeather(Context context) {
|
||||
if (!isOmniJawsEnabled(context)) {
|
||||
Log.w(TAG, "queryWeather while disabled");
|
||||
mCachedInfo = null;
|
||||
return;
|
||||
}
|
||||
|
||||
try (Cursor weatherCursor = context.getContentResolver().query(
|
||||
WEATHER_URI, WEATHER_PROJECTION, null, null, null)) {
|
||||
|
||||
if (weatherCursor != null && weatherCursor.getCount() > 0) {
|
||||
mCachedInfo = new WeatherInfo();
|
||||
List<DayForecast> forecasts = new ArrayList<>();
|
||||
|
||||
for (int i = 0; i < weatherCursor.getCount(); i++) {
|
||||
weatherCursor.moveToPosition(i);
|
||||
if (i == 0) {
|
||||
mCachedInfo.city = weatherCursor.getString(0);
|
||||
mCachedInfo.windSpeed = getFormattedValue(weatherCursor.getFloat(1));
|
||||
mCachedInfo.windDirection = weatherCursor.getInt(2) + "\u00b0";
|
||||
mCachedInfo.conditionCode = weatherCursor.getInt(3);
|
||||
mCachedInfo.temp = getFormattedValue(weatherCursor.getFloat(4));
|
||||
mCachedInfo.humidity = weatherCursor.getString(5);
|
||||
mCachedInfo.condition = weatherCursor.getString(6);
|
||||
mCachedInfo.timeStamp = Long.parseLong(weatherCursor.getString(11));
|
||||
mCachedInfo.pinWheel = weatherCursor.getString(13);
|
||||
} else {
|
||||
DayForecast day = new DayForecast();
|
||||
day.low = getFormattedValue(weatherCursor.getFloat(7));
|
||||
day.high = getFormattedValue(weatherCursor.getFloat(8));
|
||||
day.condition = weatherCursor.getString(9);
|
||||
day.conditionCode = weatherCursor.getInt(10);
|
||||
day.date = weatherCursor.getString(12);
|
||||
forecasts.add(day);
|
||||
}
|
||||
}
|
||||
mCachedInfo.forecasts = forecasts;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "queryWeather: weather", e);
|
||||
}
|
||||
|
||||
try (Cursor settingsCursor = context.getContentResolver().query(
|
||||
SETTINGS_URI, SETTINGS_PROJECTION, null, null, null)) {
|
||||
|
||||
if (settingsCursor != null && settingsCursor.moveToFirst()) {
|
||||
mMetric = settingsCursor.getInt(1) == 0;
|
||||
if (mCachedInfo != null) {
|
||||
mCachedInfo.tempUnits = getTemperatureUnit();
|
||||
mCachedInfo.windUnits = getWindUnit();
|
||||
mCachedInfo.provider = settingsCursor.getString(2);
|
||||
mCachedInfo.iconPack = settingsCursor.getString(4);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "queryWeather: settings", e);
|
||||
}
|
||||
|
||||
updateSettings(context);
|
||||
}
|
||||
|
||||
private void updateSettings(Context context) {
|
||||
String iconPack = (mCachedInfo != null) ? mCachedInfo.iconPack : null;
|
||||
if (iconPack == null || TextUtils.isEmpty(iconPack)) {
|
||||
loadDefaultIconsPackage(context);
|
||||
} else if (!iconPack.equals(mSettingIconPackage)) {
|
||||
mSettingIconPackage = iconPack;
|
||||
loadCustomIconPackage(context);
|
||||
}
|
||||
}
|
||||
|
||||
private void loadDefaultIconsPackage(Context context) {
|
||||
mPackageName = ICON_PACKAGE_DEFAULT;
|
||||
mIconPrefix = ICON_PREFIX_DEFAULT;
|
||||
mSettingIconPackage = mPackageName + "." + mIconPrefix;
|
||||
try {
|
||||
mRes = context.getPackageManager().getResourcesForApplication(mPackageName);
|
||||
} catch (Exception e) {
|
||||
Log.w(TAG, "No default icon package found");
|
||||
mRes = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void loadCustomIconPackage(Context context) {
|
||||
int idx = mSettingIconPackage.lastIndexOf(".");
|
||||
if (idx == -1) {
|
||||
loadDefaultIconsPackage(context);
|
||||
return;
|
||||
}
|
||||
mPackageName = mSettingIconPackage.substring(0, idx);
|
||||
mIconPrefix = mSettingIconPackage.substring(idx + 1);
|
||||
try {
|
||||
mRes = context.getPackageManager().getResourcesForApplication(mPackageName);
|
||||
} catch (Exception e) {
|
||||
Log.w(TAG, "Icon pack loading failed, fallback to default");
|
||||
loadDefaultIconsPackage(context);
|
||||
}
|
||||
}
|
||||
|
||||
private static String getFormattedValue(float value) {
|
||||
if (Float.isNaN(value)) return "-";
|
||||
String result = sNoDigitsFormat.format(value);
|
||||
return result.equals("-0") ? "0" : result;
|
||||
}
|
||||
|
||||
public boolean isOmniJawsServiceInstalled(Context context) {
|
||||
return isAvailableApp(context, SERVICE_PACKAGE);
|
||||
}
|
||||
|
||||
public boolean isOmniJawsEnabled(Context context) {
|
||||
if (!isOmniJawsServiceInstalled(context)) return false;
|
||||
|
||||
try (Cursor c = context.getContentResolver().query(
|
||||
SETTINGS_URI, SETTINGS_PROJECTION, null, null, null)) {
|
||||
return c != null && c.moveToFirst() && c.getInt(0) == 1;
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "isOmniJawsEnabled", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isAvailableApp(Context context, String pkg) {
|
||||
try {
|
||||
PackageManager pm = context.getPackageManager();
|
||||
pm.getPackageInfo(pkg, PackageManager.GET_ACTIVITIES);
|
||||
int state = pm.getApplicationEnabledSetting(pkg);
|
||||
return state != PackageManager.COMPONENT_ENABLED_STATE_DISABLED
|
||||
&& state != PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
|
||||
} catch (NameNotFoundException | IllegalArgumentException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void addObserver(Context context, OmniJawsObserver observer) {
|
||||
if (observer == null) return;
|
||||
removeObserver(context, observer);
|
||||
mObservers.add(new WeakReference<>(observer));
|
||||
registerReceiverIfNeeded(context);
|
||||
}
|
||||
|
||||
public void removeObserver(Context context, OmniJawsObserver observer) {
|
||||
if (observer == null) return;
|
||||
Iterator<WeakReference<OmniJawsObserver>> it = mObservers.iterator();
|
||||
while (it.hasNext()) {
|
||||
OmniJawsObserver o = it.next().get();
|
||||
if (o == null || o == observer) {
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
if (mObservers.isEmpty()) {
|
||||
unregisterReceiver(context);
|
||||
}
|
||||
}
|
||||
|
||||
private void pruneDeadObservers() {
|
||||
try {
|
||||
mObservers.removeIf(ref -> ref.get() == null);
|
||||
} catch (Exception e) {
|
||||
Log.w(TAG, "Exception occured while pruning, ignoring");
|
||||
}
|
||||
}
|
||||
|
||||
private void registerReceiverIfNeeded(Context context) {
|
||||
if (!mWeatherReceiverRegistered && !mObservers.isEmpty()) {
|
||||
if (mReceiver != null) {
|
||||
try {
|
||||
context.unregisterReceiver(mReceiver);
|
||||
} catch (Exception ignored) {}
|
||||
}
|
||||
mReceiver = new WeatherUpdateReceiver();
|
||||
IntentFilter filter = new IntentFilter();
|
||||
filter.addAction(WEATHER_UPDATE);
|
||||
filter.addAction(WEATHER_ERROR);
|
||||
context.registerReceiver(mReceiver, filter, Context.RECEIVER_EXPORTED);
|
||||
mWeatherReceiverRegistered = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void unregisterReceiver(Context context) {
|
||||
if (mWeatherReceiverRegistered && mReceiver != null) {
|
||||
try {
|
||||
context.unregisterReceiver(mReceiver);
|
||||
} catch (Exception ignored) {}
|
||||
mWeatherReceiverRegistered = false;
|
||||
}
|
||||
}
|
||||
|
||||
private String getTemperatureUnit() {
|
||||
return mMetric ? "\u00b0C" : "\u00b0F";
|
||||
}
|
||||
|
||||
private String getWindUnit() {
|
||||
return mMetric ? "km/h" : "mph";
|
||||
}
|
||||
|
||||
public Drawable getWeatherConditionImage(Context context, int conditionCode) {
|
||||
if (mRes == null) {
|
||||
loadDefaultIconsPackage(context);
|
||||
}
|
||||
try {
|
||||
int resId = mRes.getIdentifier(mIconPrefix + "_" + conditionCode, "drawable", mPackageName);
|
||||
Drawable d = mRes.getDrawable(resId, null);
|
||||
return d != null ? d : getDefaultConditionImage(context);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "getWeatherConditionImage", e);
|
||||
return getDefaultConditionImage(context);
|
||||
}
|
||||
}
|
||||
|
||||
private Drawable getDefaultConditionImage(Context context) {
|
||||
try {
|
||||
Resources res = context.getPackageManager().getResourcesForApplication(ICON_PACKAGE_DEFAULT);
|
||||
int resId = res.getIdentifier(ICON_PREFIX_DEFAULT + "_na", "drawable", ICON_PACKAGE_DEFAULT);
|
||||
Drawable d = res.getDrawable(resId, null);
|
||||
return d != null ? d : new ColorDrawable(Color.RED);
|
||||
} catch (Exception e) {
|
||||
return new ColorDrawable(Color.RED);
|
||||
}
|
||||
}
|
||||
|
||||
public Drawable getResOmni(Context context, String iconOmni) {
|
||||
if (mRes == null) loadDefaultIconsPackage(context);
|
||||
try {
|
||||
int resId = mRes.getIdentifier(iconOmni, "drawable", mPackageName);
|
||||
Drawable d = mRes.getDrawable(resId, null);
|
||||
return d != null ? d : new ColorDrawable(Color.RED);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "getResOmni", e);
|
||||
return new ColorDrawable(Color.RED);
|
||||
}
|
||||
}
|
||||
|
||||
public static class WeatherInfo {
|
||||
public String city;
|
||||
public String windSpeed;
|
||||
@@ -108,13 +385,13 @@ public class OmniJawsClient {
|
||||
public String pinWheel;
|
||||
public String iconPack;
|
||||
|
||||
public String toString() {
|
||||
return city + ":" + new Date(timeStamp) + ": " + windSpeed + ":" + windDirection + ":" +conditionCode + ":" + temp + ":" + humidity + ":" + condition + ":" + tempUnits + ":" + windUnits + ": " + forecasts + ": " + iconPack;
|
||||
public String getLastUpdateTime() {
|
||||
return new SimpleDateFormat("HH:mm:ss", Locale.getDefault()).format(new Date(timeStamp));
|
||||
}
|
||||
|
||||
public String getLastUpdateTime() {
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
|
||||
return sdf.format(new Date(timeStamp));
|
||||
@Override
|
||||
public String toString() {
|
||||
return city + " @ " + new Date(timeStamp) + " | " + condition + " | " + temp;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,324 +402,9 @@ public class OmniJawsClient {
|
||||
public String condition;
|
||||
public String date;
|
||||
|
||||
public String toString() {
|
||||
return "[" + low + ":" + high + ":" +conditionCode + ":" + condition + ":" + date + "]";
|
||||
}
|
||||
}
|
||||
|
||||
public static interface OmniJawsObserver {
|
||||
public void weatherUpdated();
|
||||
public void weatherError(int errorReason);
|
||||
default public void updateSettings() {};
|
||||
}
|
||||
|
||||
private class WeatherUpdateReceiver extends BroadcastReceiver {
|
||||
@Override
|
||||
public void onReceive(final Context context, Intent intent) {
|
||||
String action = intent.getAction();
|
||||
for (OmniJawsObserver observer : mObserver) {
|
||||
if (action.equals(WEATHER_UPDATE)) {
|
||||
observer.weatherUpdated();
|
||||
} else if (action.equals(WEATHER_ERROR)) {
|
||||
int errorReason = intent.getIntExtra(EXTRA_ERROR, 0);
|
||||
observer.weatherError(errorReason);
|
||||
}
|
||||
}
|
||||
public String toString() {
|
||||
return "[" + date + " - " + low + "/" + high + " - " + condition + "]";
|
||||
}
|
||||
}
|
||||
|
||||
private Context mContext;
|
||||
private WeatherInfo mCachedInfo;
|
||||
private Resources mRes;
|
||||
private String mPackageName;
|
||||
private String mIconPrefix;
|
||||
private String mSettingIconPackage;
|
||||
private boolean mMetric;
|
||||
private List<OmniJawsObserver> mObserver;
|
||||
private WeatherUpdateReceiver mReceiver;
|
||||
|
||||
public OmniJawsClient(Context context) {
|
||||
mContext = context;
|
||||
mObserver = new ArrayList<OmniJawsObserver>();
|
||||
}
|
||||
|
||||
public Intent getSettingsIntent() {
|
||||
if (isOmniJawsServiceInstalled()) {
|
||||
Intent settings = new Intent(Intent.ACTION_MAIN)
|
||||
.setClassName(SERVICE_PACKAGE, SERVICE_PACKAGE + ".SettingsActivity");
|
||||
return settings;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Intent getWeatherActivityIntent() {
|
||||
if (isOmniJawsServiceInstalled()) {
|
||||
Intent settings = new Intent(Intent.ACTION_MAIN)
|
||||
.setClassName(SERVICE_PACKAGE, SERVICE_PACKAGE + ".WeatherActivity");
|
||||
return settings;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public WeatherInfo getWeatherInfo() {
|
||||
return mCachedInfo;
|
||||
}
|
||||
|
||||
private static String getFormattedValue(float value) {
|
||||
if (Float.isNaN(value)) {
|
||||
return "-";
|
||||
}
|
||||
String formatted = sNoDigitsFormat.format(value);
|
||||
if (formatted.equals("-0")) {
|
||||
formatted = "0";
|
||||
}
|
||||
return formatted;
|
||||
}
|
||||
|
||||
public void queryWeather() {
|
||||
if (!isOmniJawsEnabled()) {
|
||||
Log.w(TAG, "queryWeather while disabled");
|
||||
mCachedInfo = null;
|
||||
return;
|
||||
}
|
||||
try {
|
||||
mCachedInfo = null;
|
||||
Cursor c = mContext.getContentResolver().query(WEATHER_URI, WEATHER_PROJECTION,
|
||||
null, null, null);
|
||||
if (c != null) {
|
||||
try {
|
||||
int count = c.getCount();
|
||||
if (count > 0) {
|
||||
mCachedInfo = new WeatherInfo();
|
||||
List<DayForecast> forecastList = new ArrayList<DayForecast>();
|
||||
int i = 0;
|
||||
for (i = 0; i < count; i++) {
|
||||
c.moveToPosition(i);
|
||||
if (i == 0) {
|
||||
mCachedInfo.city = c.getString(0);
|
||||
mCachedInfo.windSpeed = getFormattedValue(c.getFloat(1));
|
||||
mCachedInfo.windDirection = String.valueOf(c.getInt(2)) + "\u00b0";
|
||||
mCachedInfo.conditionCode = c.getInt(3);
|
||||
mCachedInfo.temp = getFormattedValue(c.getFloat(4));
|
||||
mCachedInfo.humidity = c.getString(5);
|
||||
mCachedInfo.condition = c.getString(6);
|
||||
mCachedInfo.timeStamp = Long.valueOf(c.getString(11));
|
||||
mCachedInfo.pinWheel = c.getString(13);
|
||||
} else {
|
||||
DayForecast day = new DayForecast();
|
||||
day.low = getFormattedValue(c.getFloat(7));
|
||||
day.high = getFormattedValue(c.getFloat(8));
|
||||
day.condition = c.getString(9);
|
||||
day.conditionCode = c.getInt(10);
|
||||
day.date = c.getString(12);
|
||||
forecastList.add(day);
|
||||
}
|
||||
}
|
||||
mCachedInfo.forecasts = forecastList;
|
||||
}
|
||||
} finally {
|
||||
c.close();
|
||||
}
|
||||
}
|
||||
c = mContext.getContentResolver().query(SETTINGS_URI, SETTINGS_PROJECTION,
|
||||
null, null, null);
|
||||
if (c != null) {
|
||||
try {
|
||||
int count = c.getCount();
|
||||
if (count == 1) {
|
||||
c.moveToPosition(0);
|
||||
mMetric = c.getInt(1) == 0;
|
||||
if (mCachedInfo != null) {
|
||||
mCachedInfo.tempUnits = getTemperatureUnit();
|
||||
mCachedInfo.windUnits = getWindUnit();
|
||||
mCachedInfo.provider = c.getString(2);
|
||||
mCachedInfo.iconPack = c.getString(4);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
c.close();
|
||||
}
|
||||
}
|
||||
|
||||
if (DEBUG) Log.d(TAG, "queryWeather " + mCachedInfo);
|
||||
updateSettings();
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "queryWeather", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void loadDefaultIconsPackage() {
|
||||
mPackageName = ICON_PACKAGE_DEFAULT;
|
||||
mIconPrefix = ICON_PREFIX_DEFAULT;
|
||||
mSettingIconPackage = mPackageName + "." + mIconPrefix;
|
||||
if (DEBUG) Log.d(TAG, "Load default icon pack " + mSettingIconPackage + " " + mPackageName + " " + mIconPrefix);
|
||||
try {
|
||||
PackageManager packageManager = mContext.getPackageManager();
|
||||
mRes = packageManager.getResourcesForApplication(mPackageName);
|
||||
} catch (Exception e) {
|
||||
mRes = null;
|
||||
}
|
||||
if (mRes == null) {
|
||||
Log.w(TAG, "No default package found");
|
||||
}
|
||||
}
|
||||
|
||||
private Drawable getDefaultConditionImage() {
|
||||
String packageName = ICON_PACKAGE_DEFAULT;
|
||||
String iconPrefix = ICON_PREFIX_DEFAULT;
|
||||
|
||||
try {
|
||||
PackageManager packageManager = mContext.getPackageManager();
|
||||
Resources res = packageManager.getResourcesForApplication(packageName);
|
||||
if (res != null) {
|
||||
int resId = res.getIdentifier(iconPrefix + "_na", "drawable", packageName);
|
||||
Drawable d = res.getDrawable(resId);
|
||||
if (d != null) {
|
||||
return d;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
}
|
||||
// absolute absolute fallback
|
||||
Log.w(TAG, "No default package found");
|
||||
return new ColorDrawable(Color.RED);
|
||||
}
|
||||
|
||||
private void loadCustomIconPackage() {
|
||||
if (DEBUG) Log.d(TAG, "Load custom icon pack " + mSettingIconPackage);
|
||||
int idx = mSettingIconPackage.lastIndexOf(".");
|
||||
mPackageName = mSettingIconPackage.substring(0, idx);
|
||||
mIconPrefix = mSettingIconPackage.substring(idx + 1);
|
||||
if (DEBUG) Log.d(TAG, "Load custom icon pack " + mPackageName + " " + mIconPrefix);
|
||||
try {
|
||||
PackageManager packageManager = mContext.getPackageManager();
|
||||
mRes = packageManager.getResourcesForApplication(mPackageName);
|
||||
} catch (Exception e) {
|
||||
mRes = null;
|
||||
}
|
||||
if (mRes == null) {
|
||||
Log.w(TAG, "Icon pack loading failed - loading default");
|
||||
loadDefaultIconsPackage();
|
||||
}
|
||||
}
|
||||
|
||||
public Drawable getWeatherConditionImage(int conditionCode) {
|
||||
try {
|
||||
int resId = mRes.getIdentifier(mIconPrefix + "_" + conditionCode, "drawable", mPackageName);
|
||||
Drawable d = mRes.getDrawable(resId);
|
||||
if (d != null) {
|
||||
return d;
|
||||
}
|
||||
Log.w(TAG, "Failed to get condition image for " + conditionCode + " use default");
|
||||
resId = mRes.getIdentifier(mIconPrefix + "_na", "drawable", mPackageName);
|
||||
d = mRes.getDrawable(resId);
|
||||
if (d != null) {
|
||||
return d;
|
||||
}
|
||||
} catch(Exception e) {
|
||||
Log.e(TAG, "getWeatherConditionImage", e);
|
||||
}
|
||||
Log.w(TAG, "Failed to get condition image for " + conditionCode);
|
||||
return getDefaultConditionImage();
|
||||
}
|
||||
|
||||
public boolean isOmniJawsServiceInstalled() {
|
||||
return isAvailableApp(SERVICE_PACKAGE);
|
||||
}
|
||||
|
||||
public boolean isOmniJawsEnabled() {
|
||||
if (!isOmniJawsServiceInstalled()) {
|
||||
return false;
|
||||
}
|
||||
boolean enabled = false;
|
||||
try {
|
||||
final Cursor c = mContext.getContentResolver().query(SETTINGS_URI, SETTINGS_PROJECTION,
|
||||
null, null, null);
|
||||
if (c != null) {
|
||||
try {
|
||||
int count = c.getCount();
|
||||
if (count == 1) {
|
||||
c.moveToPosition(0);
|
||||
enabled = c.getInt(0) == 1;
|
||||
}
|
||||
} finally {
|
||||
c.close();
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "isOmniJawsEnabled", e);
|
||||
}
|
||||
return enabled;
|
||||
}
|
||||
|
||||
private String getTemperatureUnit() {
|
||||
return "\u00b0" + (mMetric ? "C" : "F");
|
||||
}
|
||||
|
||||
private String getWindUnit() {
|
||||
return mMetric ? "km/h":"mph";
|
||||
}
|
||||
|
||||
private void updateSettings() {
|
||||
final String iconPack = mCachedInfo != null ? mCachedInfo.iconPack : null;
|
||||
if (iconPack == null || TextUtils.isEmpty(iconPack)) {
|
||||
loadDefaultIconsPackage();
|
||||
} else if (mSettingIconPackage == null || !iconPack.equals(mSettingIconPackage)) {
|
||||
mSettingIconPackage = iconPack;
|
||||
loadCustomIconPackage();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isAvailableApp(String packageName) {
|
||||
final PackageManager pm = mContext.getPackageManager();
|
||||
try {
|
||||
pm.getPackageInfo(packageName, PackageManager.GET_ACTIVITIES);
|
||||
int enabled = pm.getApplicationEnabledSetting(packageName);
|
||||
return enabled != PackageManager.COMPONENT_ENABLED_STATE_DISABLED &&
|
||||
enabled != PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
|
||||
} catch (NameNotFoundException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void addObserver(OmniJawsObserver observer) {
|
||||
if (mObserver.contains(observer)) {
|
||||
removeObserver(observer);
|
||||
}
|
||||
mObserver.add(observer);
|
||||
registerReceiverIfNeeded();
|
||||
}
|
||||
|
||||
public void removeObserver(OmniJawsObserver observer) {
|
||||
if (mObserver.contains(observer)) {
|
||||
mObserver.remove(observer);
|
||||
if (mObserver.isEmpty()) {
|
||||
unregisterReceiver();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void registerReceiverIfNeeded() {
|
||||
if (mObserver.size() == 1 && !mWeatherReceiverRegistered) {
|
||||
if (mReceiver != null) {
|
||||
try {
|
||||
mContext.unregisterReceiver(mReceiver);
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
mReceiver = new WeatherUpdateReceiver();
|
||||
IntentFilter filter = new IntentFilter();
|
||||
filter.addAction(WEATHER_UPDATE);
|
||||
filter.addAction(WEATHER_ERROR);
|
||||
if (DEBUG) Log.d(TAG, "registerReceiver");
|
||||
mContext.registerReceiver(mReceiver, filter, Context.RECEIVER_EXPORTED);
|
||||
mWeatherReceiverRegistered = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void unregisterReceiver() {
|
||||
if (!mWeatherReceiverRegistered) return;
|
||||
mContext.unregisterReceiver(mReceiver);
|
||||
mWeatherReceiverRegistered = false;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user