Initial commit

This commit is contained in:
aNNiMON 2024-02-14 00:07:42 +02:00
commit 5cd4c6ca0c
28 changed files with 1334 additions and 0 deletions

24
AndroidManifest.xml Normal file
View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.annimon.screenweather"
android:versionCode="1"
android:versionName="1.0" >
<uses-permission android:name="android.permission.INTERNET" />
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name="WeatherListActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name="ShowWeatherActivity" />
</application>
</manifest>

19
ant.properties Normal file
View File

@ -0,0 +1,19 @@
# This file is used to override default values used by the Ant build system.
#
# This file must be checked into Version Control Systems, as it is
# integral to the build system of your project.
# This file is only used by the Ant script.
# You can use this to override default values such as
# 'source.dir' for the location of your java source folder and
# 'out.dir' for the location of your output folder.
# You can also use it define how the release builds are signed by declaring
# the following properties:
# 'key.store' for the location of your keystore and
# 'key.alias' for the name of the key to use.
# The password will be asked during the build when you use the 'release' target.
key.store=C:\\Android\\android-sign\\keystore
key.alias=sign

92
build.xml Normal file
View File

@ -0,0 +1,92 @@
<?xml version="1.0" encoding="UTF-8"?>
<project name="ScreenWeather" default="help">
<!-- The local.properties file is created and updated by the 'android' tool.
It contains the path to the SDK. It should *NOT* be checked into
Version Control Systems. -->
<property file="local.properties" />
<!-- The ant.properties file can be created by you. It is only edited by the
'android' tool to add properties to it.
This is the place to change some Ant specific build properties.
Here are some properties you may want to change/update:
source.dir
The name of the source directory. Default is 'src'.
out.dir
The name of the output directory. Default is 'bin'.
For other overridable properties, look at the beginning of the rules
files in the SDK, at tools/ant/build.xml
Properties related to the SDK location or the project target should
be updated using the 'android' tool with the 'update' action.
This file is an integral part of the build system for your
application and should be checked into Version Control Systems.
-->
<property file="ant.properties" />
<!-- if sdk.dir was not set from one of the property file, then
get it from the ANDROID_HOME env var.
This must be done before we load project.properties since
the proguard config can use sdk.dir -->
<property environment="env" />
<condition property="sdk.dir" value="${env.ANDROID_HOME}">
<isset property="env.ANDROID_HOME" />
</condition>
<!-- The project.properties file is created and updated by the 'android'
tool, as well as ADT.
This contains project specific properties such as project target, and library
dependencies. Lower level build properties are stored in ant.properties
(or in .classpath for Eclipse projects).
This file is an integral part of the build system for your
application and should be checked into Version Control Systems. -->
<loadproperties srcFile="project.properties" />
<!-- quick check on sdk.dir -->
<fail
message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through the ANDROID_HOME environment variable."
unless="sdk.dir"
/>
<!--
Import per project custom build rules if present at the root of the project.
This is the place to put custom intermediary targets such as:
-pre-build
-pre-compile
-post-compile (This is typically used for code obfuscation.
Compiled code location: ${out.classes.absolute.dir}
If this is not done in place, override ${out.dex.input.absolute.dir})
-post-package
-post-build
-pre-clean
-->
<import file="custom_rules.xml" optional="true" />
<!-- Import the actual build file.
To customize existing targets, there are two options:
- Customize only one target:
- copy/paste the target into this file, *before* the
<import> task.
- customize it to your needs.
- Customize the whole content of build.xml
- copy/paste the content of the rules files (minus the top node)
into this file, replacing the <import> task.
- customize to your needs.
***********************
****** IMPORTANT ******
***********************
In all cases you must update the value of version-tag below to read 'custom' instead of an integer,
in order to avoid having your file be overridden by tools such as "android update project"
-->
<!-- version-tag: 1 -->
<import file="${sdk.dir}/tools/ant/build.xml" />
</project>

10
local.properties Normal file
View File

@ -0,0 +1,10 @@
# This file is automatically generated by Android Tools.
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
#
# This file must *NOT* be checked into Version Control Systems,
# as it contains information specific to your local configuration.
# location of the SDK. This is only used by Ant
# For customization when using a Version Control System, please read the
# header note.
sdk.dir=D:\\Android\\android-sdk

26
proguard-project.txt Normal file
View File

@ -0,0 +1,26 @@
# To enable ProGuard in your project, edit project.properties
# to define the proguard.config property as described in that file.
#
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in ${sdk.dir}/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the ProGuard
# include property in project.properties.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
-obfuscationdictionary E:\SETUPS\Disk\Programming\Java\compact.txt
-optimizationpasses 9
-printmapping <user.home>\Documents\NetBeansProjects\map.txt
-printusage <user.home>\Documents\NetBeansProjects\usage.txt
# -dontusemixedcaseclassnames
# -repackageclasses ''
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

14
project.properties Normal file
View File

@ -0,0 +1,14 @@
# This file is automatically generated by Android Tools.
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
#
# This file must be checked in Version Control Systems.
#
# To customize properties used by the Ant build system edit
# "ant.properties", and override values to adapt the script to your
# project structure.
#
# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
proguard.config=${sdk.dir}\\tools\\proguard\\proguard-android.txt:proguard-project.txt
# Project target.
target=android-16

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

29
res/layout/main.xml Normal file
View File

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<com.annimon.screenweather.AnimationLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/animation_layout"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<LinearLayout
android:id="@id/animation_layout_sidebar"
android:layout_width="120dp"
android:layout_height="match_parent"
android:orientation="vertical" >
<ListView
android:id="@+id/weathersListView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
<!-- To make LinearLayout clickable to trigger onContentTouchedWhenOpening() -->
<LinearLayout
android:id="@id/animation_layout_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="true"
android:orientation="vertical" >
<ImageView
android:id="@+id/content_image"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
</com.annimon.screenweather.AnimationLayout>

59
res/layout/ololo.xml Normal file
View File

@ -0,0 +1,59 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView
android:id="@+id/cityTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:text="City"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textSize="20dp" />
<TextView
android:id="@+id/currentTemperatureTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_below="@+id/cityTextView"
android:text="20 ^C"
android:textAppearance="?android:attr/textAppearanceLarge"
android:textSize="25dp" />
<TextView
android:id="@+id/windInfoTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_below="@+id/currentTemperatureTextView"
android:text="Wind: 16 km/h, West"
android:textAppearance="?android:attr/textAppearanceSmall" />
<TextView
android:id="@+id/summaryInfoTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_below="@+id/windInfoTextView"
android:text="Summary"
android:textAppearance="?android:attr/textAppearanceMedium" />
<HorizontalScrollView
android:id="@+id/horizontalScrollView1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal" >
</LinearLayout>
</HorizontalScrollView>
</RelativeLayout>

View File

@ -0,0 +1,73 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView
android:id="@+id/cityLabel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:textSize="24sp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<TextView
android:id="@+id/currentWind"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="left"
android:textSize="25sp" />
<TextView
android:id="@+id/currentTemp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:textSize="50sp" />
<TextView
android:id="@+id/currentHumidity"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="right"
android:textSize="25sp" />
</LinearLayout>
<TextView
android:id="@+id/summary"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:textSize="18sp"
android:textStyle="italic" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:orientation="vertical" >
<TextView
android:id="@+id/dayInfo1"
style="@style/day_info_style" />
<TextView
android:id="@+id/dayInfo2"
style="@style/day_info_style" />
<TextView
android:id="@+id/dayInfo3"
style="@style/day_info_style" />
<TextView
android:id="@+id/dayInfo4"
style="@style/day_info_style" />
</LinearLayout>
</LinearLayout>

View File

@ -0,0 +1,5 @@
<resources>
<style name="AppTheme" parent="android:Theme.Holo.Light" />
</resources>

View File

@ -0,0 +1,5 @@
<resources>
<style name="AppTheme" parent="android:Theme.Holo.Light.DarkActionBar" />
</resources>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<resources>
<style name="day_info_style">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:textSize">16sp</item>
<item name="android:layout_marginTop">10dp</item>
</style>
</resources>

5
res/values/ids.xml Normal file
View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<item type="id" name="animation_layout_sidebar" />
<item type="id" name="animation_layout_content" />
</resources>

6
res/values/strings.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Screen Weather</string>
<string name="please_wait">Please wait</string>
<string name="close">Close</string>
</resources>

5
res/values/styles.xml Normal file
View File

@ -0,0 +1,5 @@
<resources>
<style name="AppTheme" parent="android:Theme.Light" />
</resources>

View File

@ -0,0 +1,263 @@
package com.annimon.screenweather;
/*
* Copyright (C) 2012 0xlab - http://0xlab.org/
*
* 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.
*
* Authored by Julian Chu <walkingice AT 0xlab.org>
*/
// update the package name to match your app
import android.content.Context;
import android.util.AttributeSet;
import android.view.animation.Animation;
import android.view.animation.TranslateAnimation;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.MeasureSpec;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
public class AnimationLayout extends ViewGroup {
public final static int DURATION = 500;
protected boolean mPlaceLeft = true;
protected boolean mOpened;
protected View mSidebar;
protected View mContent;
protected int mSidebarWidth = 150; /* assign default value. It will be overwrite
in onMeasure by Layout xml resource. */
protected Animation mAnimation;
protected OpenListener mOpenListener;
protected CloseListener mCloseListener;
protected Listener mListener;
protected boolean mPressed = false;
public AnimationLayout(Context context) {
this(context, null);
}
public AnimationLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public void onFinishInflate() {
super.onFinishInflate();
mSidebar = findViewById(R.id.animation_layout_sidebar);
mContent = findViewById(R.id.animation_layout_content);
if (mSidebar == null) {
throw new NullPointerException("no view id = animation_sidebar");
}
if (mContent == null) {
throw new NullPointerException("no view id = animation_content");
}
mOpenListener = new OpenListener(mSidebar, mContent);
mCloseListener = new CloseListener(mSidebar, mContent);
}
@Override
public void onLayout(boolean changed, int l, int t, int r, int b) {
/* the title bar assign top padding, drop it */
int sidebarLeft = l;
if (!mPlaceLeft) {
sidebarLeft = r - mSidebarWidth;
}
mSidebar.layout(sidebarLeft,
0,
sidebarLeft + mSidebarWidth,
0 + mSidebar.getMeasuredHeight());
if (mOpened) {
if (mPlaceLeft) {
mContent.layout(l + mSidebarWidth, 0, r + mSidebarWidth, b);
} else {
mContent.layout(l - mSidebarWidth, 0, r - mSidebarWidth, b);
}
} else {
mContent.layout(l, 0, r, b);
}
}
@Override
public void onMeasure(int w, int h) {
super.onMeasure(w, h);
super.measureChildren(w, h);
mSidebarWidth = mSidebar.getMeasuredWidth();
}
@Override
protected void measureChild(View child, int parentWSpec, int parentHSpec) {
/* the max width of Sidebar is 90% of Parent */
if (child == mSidebar) {
int mode = MeasureSpec.getMode(parentWSpec);
int width = (int)(getMeasuredWidth() * 0.9);
super.measureChild(child, MeasureSpec.makeMeasureSpec(width, mode), parentHSpec);
} else {
super.measureChild(child, parentWSpec, parentHSpec);
}
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (!isOpening()) {
return false;
}
int action = ev.getAction();
if (action != MotionEvent.ACTION_UP
&& action != MotionEvent.ACTION_DOWN) {
return false;
}
/* if user press and release both on Content while
* sidebar is opening, call listener. otherwise, pass
* the event to child. */
int x = (int)ev.getX();
int y = (int)ev.getY();
if (mContent.getLeft() < x
&& mContent.getRight() > x
&& mContent.getTop() < y
&& mContent.getBottom() > y) {
if (action == MotionEvent.ACTION_DOWN) {
mPressed = true;
}
if (mPressed
&& action == MotionEvent.ACTION_UP
&& mListener != null) {
mPressed = false;
return mListener.onContentTouchedWhenOpening();
}
} else {
mPressed = false;
}
return false;
}
public void setListener(Listener l) {
mListener = l;
}
/* to see if the Sidebar is visible */
public boolean isOpening() {
return mOpened;
}
public void toggleSidebar() {
if (mContent.getAnimation() != null) {
return;
}
if (mOpened) {
/* opened, make close animation*/
if (mPlaceLeft) {
mAnimation = new TranslateAnimation(0, -mSidebarWidth, 0, 0);
} else {
mAnimation = new TranslateAnimation(0, mSidebarWidth, 0, 0);
}
mAnimation.setAnimationListener(mCloseListener);
} else {
/* not opened, make open animation */
if (mPlaceLeft) {
mAnimation = new TranslateAnimation(0, mSidebarWidth, 0, 0);
} else {
mAnimation = new TranslateAnimation(0, -mSidebarWidth, 0, 0);
}
mAnimation.setAnimationListener(mOpenListener);
}
mAnimation.setDuration(DURATION);
mAnimation.setFillAfter(true);
mAnimation.setFillEnabled(true);
mContent.startAnimation(mAnimation);
}
public void openSidebar() {
if (!mOpened) {
toggleSidebar();
}
}
public void closeSidebar() {
if (mOpened) {
toggleSidebar();
}
}
class OpenListener implements Animation.AnimationListener {
View iSidebar;
View iContent;
OpenListener(View sidebar, View content) {
iSidebar = sidebar;
iContent = content;
}
public void onAnimationRepeat(Animation animation) {
}
public void onAnimationStart(Animation animation) {
iSidebar.setVisibility(View.VISIBLE);
}
public void onAnimationEnd(Animation animation) {
iContent.clearAnimation();
mOpened = !mOpened;
requestLayout();
if (mListener != null) {
mListener.onSidebarOpened();
}
}
}
class CloseListener implements Animation.AnimationListener {
View iSidebar;
View iContent;
CloseListener(View sidebar, View content) {
iSidebar = sidebar;
iContent = content;
}
public void onAnimationRepeat(Animation animation) {
}
public void onAnimationStart(Animation animation) {
}
public void onAnimationEnd(Animation animation) {
iContent.clearAnimation();
iSidebar.setVisibility(View.INVISIBLE);
mOpened = !mOpened;
requestLayout();
if (mListener != null) {
mListener.onSidebarClosed();
}
}
}
public interface Listener {
public void onSidebarOpened();
public void onSidebarClosed();
public boolean onContentTouchedWhenOpening();
}
}

View File

@ -0,0 +1,104 @@
/*
* aNNiMON 2012
* For more info visit http://annimon.com/
*/
package com.annimon.screenweather;
import android.graphics.Bitmap;
/**
* Информация о текущем состоянии погоды.
* @author aNNiMON
*/
public class CurrentCondition {
/** Картинка погоды. */
private Bitmap icon;
/** Общие сведения о погоде. */
private String summary;
/** Температура воздуха. */
protected String temperature;
/** Влажность воздуха. */
private String humidity;
/** Состояние ветра. */
private Wind windInfo;
public CurrentCondition() {
icon = null;
summary = temperature = humidity = "NULL";
windInfo = new Wind();
}
@Override
public String toString() {
StringBuilder currTemp = new StringBuilder();
currTemp.append("Ветер: ").append(windInfo.toString()).append('\n');
currTemp.append("Влажность: ").append(humidity).append('\n');
currTemp.append(summary);
return currTemp.toString();
}
//<editor-fold defaultstate="collapsed" desc="Get/Set">
public Bitmap getIcon() {
return icon;
}
public void setIcon(Bitmap icon) {
this.icon = icon;
}
public String getSummary() {
if (summary.equals("NULL")) return "";
return summary;
}
public void setSummary(String summary) {
if (summary.isEmpty()) return;
char[] summaryChars = summary.trim().toCharArray();
summaryChars[0] = Character.toUpperCase(summaryChars[0]);
this.summary = String.valueOf(summaryChars);
}
public String getTemperature() {
if (temperature.equals("NULL")) return "";
return temperature;
}
public void setTemperature(String value) {
temperature = value.trim();
if (!temperature.endsWith("°C")) {
temperature = temperature + "°C";
}
}
public String getHumidity() {
if (humidity.equals("NULL")) return "";
return humidity;
}
public void setHumidity(String value) {
humidity = value.trim();
// Удаляем нечисловые символы.
while (!Character.isDigit(humidity.charAt(0))) {
humidity = humidity.substring(1);
}
// Нормализируем знак процента.
if (humidity.endsWith("%")) {
humidity = humidity.replace('%', ' ');
humidity = humidity.trim();
}
humidity = humidity + "%";
}
public Wind getWindInfo() {
return windInfo;
}
public void setWindInfo(Wind windInfo) {
this.windInfo = windInfo;
}
//</editor-fold>
}

View File

@ -0,0 +1,64 @@
/*
* aNNiMON 2012
* For more info visit http://annimon.com/
*/
package com.annimon.screenweather;
/**
* Прогноз погоды на день.
* @author aNNiMON
*/
public class DayInfo extends CurrentCondition {
private String temperatureMax;
public DayInfo() {
super();
temperature = temperatureMax = "NULL";
}
/*
* Получить минимальную температуру воздуха.
*/
@Override
public String getTemperature() {
if (temperature.equals("NULL")) return "--";
return temperature;
}
/*
* Установить минимальную температуру воздуха.
*/
@Override
public void setTemperature(String value) {
// StringBuilder temp = new StringBuilder(value);
// while(!Character.isDigit(temp.charAt(temp.length() - 1))) {
// temp.deleteCharAt(temp.length() - 1);
// }
temperature = value.trim();
while(!Character.isDigit(temperature.charAt(temperature.length() - 1))) {
temperature = temperature.substring(0, temperature.length() - 1);
}
}
public String getTemperatureMax() {
if (temperatureMax.equals("NULL")) return "--";
return temperatureMax;
}
public void setTemperatureMax(String value) {
temperatureMax = value.trim();
if (!temperature.endsWith("°C")) {
temperature = temperature + "°C";
}
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(temperature).append(" / ").append(temperatureMax).append("°C\n");
sb.append(getSummary());
return sb.toString();
}
}

View File

@ -0,0 +1,123 @@
/*
* aNNiMON 2012
* For more info visit http://annimon.com/
*/
package com.annimon.screenweather;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.widget.TextView;
import com.annimon.screenweather.weather.WUnderground;
import com.annimon.screenweather.weather.Weather;
/**
*
* @author aNNiMON
*/
public class ShowWeatherActivity extends Activity {
private static final int DAYS_COUNT = 3;
private static Handler handler;
private ProgressDialog progress;
private Weather weather;
private TextView cityLabel, currentWind, currentTemp, currentHumidity, summary;
private TextView[] dayInfo;
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.show_weather);
getWidgets();
setWeatherService();
createDialog();
try {
setHandler();
updateInfo();
} catch (Exception e) {
createAlertDialog(e.getMessage());
}
}
private void setWeatherService() {
int position = getIntent().getExtras().getInt("position", 0);
switch(position) {
case 0: weather = new WUnderground(); break;
}
setTitle(getIntent().getExtras().getString("title"));
}
private void updateInfo() {
new Thread(new Runnable() {
public void run() {
weather.updateInfo(DAYS_COUNT);
Message msg = handler.obtainMessage(1);
handler.sendMessage(msg);
}
}).start();
}
private void setHandler() {
handler = new Handler() {
@Override
public void handleMessage(Message msg) {
if (msg.what == 1) {
progress.dismiss();
Log.d(getClass().getName(), weather.getCurrentCondition().toString(), null);
cityLabel.setText(weather.getCity());
CurrentCondition condition = weather.getCurrentCondition();
currentWind.setText(condition.getWindInfo().toString());
currentTemp.setText(condition.getTemperature());
currentHumidity.setText(condition.getHumidity());
for (int i = 0; i < weather.getDaysCount(); i++) {
DayInfo info = weather.getDayInfo(i);
dayInfo[i].setText(info.toString());
}
}
//final TextView tTemper = (TextView) findViewById(R.id.textviewTemperature);
//tTemper.setText((String) msg.obj + "°"); // отображение температуры
}
};
}
private void createDialog() {
progress = new ProgressDialog(this);
progress.setProgressStyle(ProgressDialog.STYLE_SPINNER);
progress.setMessage(getText(R.string.please_wait));
progress.show();
}
private void createAlertDialog(String message) {
AlertDialog.Builder alert = new AlertDialog.Builder(this);
alert.setMessage(message);
alert.setNegativeButton(R.string.close, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
});
alert.show();
}
private void getWidgets() {
cityLabel = (TextView) findViewById(R.id.cityLabel);
currentWind = (TextView) findViewById(R.id.currentWind);
currentTemp = (TextView) findViewById(R.id.currentTemp);
currentHumidity = (TextView) findViewById(R.id.currentHumidity);
summary = (TextView) findViewById(R.id.summary);
dayInfo = new TextView[] {
(TextView) findViewById(R.id.dayInfo1),
(TextView) findViewById(R.id.dayInfo2),
(TextView) findViewById(R.id.dayInfo3),
(TextView) findViewById(R.id.dayInfo4)
};
}
}

View File

@ -0,0 +1,35 @@
/*
* aNNiMON 2012
* For more info visit http://annimon.com/
*/
package com.annimon.screenweather;
/**
*
* @author aNNiMON
*/
public class StringEncoder {
private static char[] cp1251 = {
'\u0410', '\u0411', '\u0412', '\u0413', '\u0414', '\u0415', '\u0416',
'\u0417', '\u0418', '\u0419', '\u041A', '\u041B', '\u041C', '\u041D',
'\u041E', '\u041F', '\u0420', '\u0421', '\u0422', '\u0423', '\u0424',
'\u0425', '\u0426', '\u0427', '\u0428', '\u0429', '\u042A', '\u042B',
'\u042C', '\u042D', '\u042E', '\u042F', '\u0430', '\u0431', '\u0432',
'\u0433', '\u0434', '\u0435', '\u0436', '\u0437', '\u0438', '\u0439',
'\u043A', '\u043B', '\u043C', '\u043D', '\u043E', '\u043F', '\u0440',
'\u0441', '\u0442', '\u0443', '\u0444', '\u0445', '\u0446', '\u0447',
'\u0448', '\u0449', '\u044A', '\u044B', '\u044C', '\u044D', '\u044E',
'\u044F'
};
public static char decodeCharCP1251 (int b) {
int ich = b & 0xff;
if (ich == 0xb8) // ё
return 0x0451;
else if (ich == 0xa8) // Ё
return 0x0401;
else if (ich >= 0xc0)
return cp1251[ich-192];
return (char)ich;
}
}

View File

@ -0,0 +1,36 @@
package com.annimon.screenweather;
import android.app.ListActivity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
/**
*
* @author aNNiMON
*/
public class WeatherListActivity extends ListActivity implements AdapterView.OnItemClickListener {
private static final String[] WEATHER_LIST = {
"WUnderground"
};
private ArrayAdapter<String> adapter;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, WEATHER_LIST);
setListAdapter(adapter);
getListView().setOnItemClickListener(this);
}
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Intent intent = new Intent(getApplicationContext(), ShowWeatherActivity.class);
intent.putExtra("position", position);
intent.putExtra("title", parent.getItemAtPosition(position).toString());
startActivity(intent);
}
}

View File

@ -0,0 +1,47 @@
/*
* aNNiMON 2012
* For more info visit http://annimon.com/
*/
package com.annimon.screenweather;
/**
*
* @author aNNiMON
*/
public class Wind {
/** Скорость ветра. */
private String speed;
/** Направление ветра. */
private String direction;
public Wind() {
speed = direction = "";
}
public void setSpeed(String speed) {
this.speed = speed.trim();
// Удаляем нечисловые символы.
while (!Character.isDigit(this.speed.charAt(0))) {
this.speed = this.speed.substring(1);
}
}
public void setDirection(String dir) {
this.direction = " " + toRussian(dir.trim());
}
@Override
public String toString() {
if (speed.length() == 0) return direction;
return (speed + direction).trim();
}
private String toRussian(String str) {
return str.replace('N', 'С').
replace('S', 'Ю').
replace('E', 'В').
replace('W', 'З');
}
}

View File

@ -0,0 +1,155 @@
/*
* aNNiMON 2012
* For more info visit http://annimon.com/
*/
package com.annimon.screenweather.weather;
import android.util.Log;
import com.annimon.screenweather.CurrentCondition;
import com.annimon.screenweather.DayInfo;
import java.util.ArrayList;
import java.util.Locale;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
*
* @author aNNiMON
*/
public class WUnderground extends Weather {
private static final String WUNDERGROUND_URL = "http://api.wunderground.com/api/";
private static final String API_KEY = "";
private static final String FEATURES = "/conditions/forecast";
private static final String DATA_FORMAT = ".xml";
private String visibleLocation;
public WUnderground() {
super.baseUrl = WUNDERGROUND_URL;
super.encoding = "UTF-8";
updateCity();
}
@Override
public void updateInfo(int numDays) {
visibleLocation = "";
String url = buildUrl();
super.getData(url);
try {
parseContent();
} catch (Exception ex) {
Log.e(getClass().getName(), ex.getMessage(), ex);
}
}
@Override
public String getCity() {
updateCity();
if (visibleLocation.length() > 0) return visibleLocation;
return super.getCity();
}
@Override
public void updateCity() {
super.location = "Ukraine/Donetsk";
}
private String buildUrl() {
StringBuilder sb = new StringBuilder();
sb.append(baseUrl).append(API_KEY).append(FEATURES);
sb.append("/lang:").append(Locale.getDefault().getLanguage());
sb.append("/q/").append(location);
sb.append(DATA_FORMAT);
return sb.toString();
}
private void parseContent() throws XPathExpressionException {
XPath xpath = XPathFactory.newInstance().newXPath();
Node loc = (Node) xpath.evaluate("/response/current_observation/display_location/city", doc, XPathConstants.NODE);
visibleLocation = loc.getChildNodes().item(0).getNodeValue();
parseCurrentCondition((NodeList) xpath.evaluate("/response/current_observation", doc, XPathConstants.NODESET));
parseDayInfo((NodeList) xpath.evaluate("/response/forecast/simpleforecast/forecastdays/forecastday", doc, XPathConstants.NODESET));
}
private void parseCurrentCondition(NodeList nodeList) {
currentCondition = new CurrentCondition();
for (int i = 0; i < nodeList.getLength(); i++) {
NodeList childNodes = nodeList.item(i).getChildNodes();
for (int j = 0; j < childNodes.getLength(); j++) {
Node child = childNodes.item(j);
// Температура
if (child.getNodeName().equals("temp_c")) currentCondition.setTemperature( getData(child) );
// Влажность.
if (child.getNodeName().equals("relative_humidity")) currentCondition.setHumidity( getData(child) );
// Ветер
if (child.getNodeName().equals("wind_kph")) currentCondition.getWindInfo().setSpeed( getData(child) + " км/ч " );
else if (child.getNodeName().equals("wind_dir")) currentCondition.getWindInfo().setDirection( getData(child) );
// Общее
if (child.getNodeName().equals("weather")) currentCondition.setSummary( child.getChildNodes().item(0).getNodeValue() );
else if (child.getNodeName().equals("icon_url")) currentCondition.setIcon( super.downloadImage( getData(child) ) );
}
}
}
private String getData(Node child) {
return child.getChildNodes().item(0).getNodeValue();
}
private void parseDayInfo(NodeList nodeList) throws XPathExpressionException {
dayInfo = new ArrayList<DayInfo>();
XPath xpath = XPathFactory.newInstance().newXPath();
System.out.println("Count: "+nodeList.getLength());
System.out.println(nodeList.item(0).getNodeName());
for (int i = 0; i < nodeList.getLength(); i++) {
Node node = nodeList.item(i);
DayInfo info = new DayInfo();
info.setTemperatureMax( getData( ((Node) xpath.evaluate("/response/forecast/simpleforecast/forecastdays/forecastday/high/celsius", node, XPathConstants.NODE)) ) );
info.setTemperature( getData( ((Node) xpath.evaluate("/response/forecast/simpleforecast/forecastdays/forecastday/low/celsius", node, XPathConstants.NODE)) ) );
info.setSummary( getData( ((Node) xpath.evaluate("/response/forecast/simpleforecast/forecastdays/forecastday/conditions", node, XPathConstants.NODE)) ) );
info.setIcon(super.downloadImage( getData( ((Node) xpath.evaluate("/response/forecast/simpleforecast/forecastdays/forecastday/icon_url", node, XPathConstants.NODE)) ) ));
info.getWindInfo().setSpeed( getData( ((Node) xpath.evaluate("/response/forecast/simpleforecast/forecastdays/forecastday/avewind/kph", node, XPathConstants.NODE)) ) + " км/ч " );
info.getWindInfo().setDirection( getData( ((Node) xpath.evaluate("/response/forecast/simpleforecast/forecastdays/forecastday/avewind/dir", node, XPathConstants.NODE)) ) );
dayInfo.add(info);
}
}
/*private void parseDayInfo(NodeList nodeList) throws XPathExpressionException {
dayInfo = new ArrayList<DayInfo>();
XPath xpath = XPathFactory.newInstance().newXPath();
System.out.println("Count: "+nodeList.getLength());
System.out.println(nodeList.item(0).getNodeName());
for (int i = 0; i < nodeList.getLength(); i++) {
Node node = nodeList.item(i);
DayInfo info = new DayInfo();
Node ololo = ((Node) xpath.evaluate("/high/celsius", node, XPathConstants.NODE));
System.out.println("Name: "+ololo.getNodeName());
System.out.println("NumChilds: "+ololo.getChildNodes().getLength());
System.out.println("Celsius: "+ololo.getChildNodes().item(0).getNodeValue());
info.setTemperatureMax( getData( ((Node) xpath.evaluate("/high/celsius", node, XPathConstants.NODE)) ) );
info.setTemperature( getData( ((Node) xpath.evaluate("/low/celsius", node, XPathConstants.NODE)) ) );
info.setSummary( getData( ((Node) xpath.evaluate("/conditions", node, XPathConstants.NODE)) ) );
info.setIcon(super.downloadImage( getData( ((Node) xpath.evaluate("/icon_url", node, XPathConstants.NODE)) ) ));
info.getWindInfo().setSpeed( getData( ((Node) xpath.evaluate("/avewind/kph", node, XPathConstants.NODE)) ) + " км/ч " );
info.getWindInfo().setDirection( getData( ((Node) xpath.evaluate("/avewind/dir", node, XPathConstants.NODE)) ) );
dayInfo.add(info);
}
}*/
}

View File

@ -0,0 +1,126 @@
/*
* aNNiMON 2012
* For more info visit http://annimon.com/
*/
package com.annimon.screenweather.weather;
import android.graphics.Bitmap;
import android.util.Log;
import com.annimon.screenweather.CurrentCondition;
import com.annimon.screenweather.DayInfo;
import com.annimon.screenweather.StringEncoder;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.net.URI;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Locale;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.xml.sax.InputSource;
/**
*
* @author aNNiMON
*/
public abstract class Weather {
protected static final String CP1251 = "CP1251";
/** Адрес базового пути (без параметров) к информеру. */
protected String baseUrl;
/** Кодировка получаемых данных. */
protected String encoding;
/** Информация с сайта, например xml-данные, rss... */
protected Document doc;
/** Текущие данные (Current Condition).*/
protected CurrentCondition currentCondition;
/** Данные по дням. */
protected ArrayList<DayInfo> dayInfo;
/** Город, для которого нужно получить информацию. */
protected String location;
public CurrentCondition getCurrentCondition() {
return currentCondition;
}
public int getDaysCount() {
return dayInfo.size();
}
public String getCity() {
updateCity();
return location;
}
public DayInfo getDayInfo(int day) {
if ( (0 <= day) && (day < dayInfo.size())) {
return dayInfo.get(day);
}
return new DayInfo();
}
public abstract void updateInfo(int numDays);
public abstract void updateCity();
protected void getData(String urlsite) {
try {
URI uri = URI.create(urlsite);
URL url = uri.normalize().toURL();
URLConnection conn = url.openConnection();
conn.setRequestProperty("Accept-Language", Locale.getDefault().getLanguage());
boolean useCP1251 = false;
Charset charset = Charset.defaultCharset();
if (encoding != null) {
if (encoding.equals(CP1251)) useCP1251 = true;
else charset = Charset.forName(encoding);
}
StringBuilder sb = new StringBuilder();
if (useCP1251) {
InputStream reader = conn.getInputStream();
int readChar;
while ( (readChar = reader.read()) != -1) {
sb.append( StringEncoder.decodeCharCP1251(readChar) );
}
reader.close();
} else {
InputStreamReader reader = new InputStreamReader(conn.getInputStream(), charset);
int readChar;
while ( (readChar = reader.read()) != -1) {
sb.append( (char) readChar );
}
reader.close();
}
StringReader sr = new StringReader( sb.toString() );
InputSource isource = new InputSource(sr);
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
DocumentBuilder builder = factory.newDocumentBuilder();
doc = builder.parse(isource);
doc.getDocumentElement().normalize();
sr.close();
} catch (Exception ex) {
Log.e(getClass().getName(), ex.getMessage(), ex);
doc = null;
}
}
protected Bitmap downloadImage(String url) {
return null;
}
}