OSDN Git Service

Show weather forecast in DeskClock.
authorDaniel Sandler <dsandler@google.com>
Tue, 27 Oct 2009 20:46:57 +0000 (16:46 -0400)
committerDaniel Sandler <dsandler@google.com>
Thu, 29 Oct 2009 15:40:52 +0000 (11:40 -0400)
Currently querying the GenieWidget; if it's not present, no
weather is shown or even hinted at in the UI. If GenieWidget
is available but fails to respond to the query, we show
"Weather unavailable." (needs i18n)

Note that Genie's WeatherProvider is currently broken, so
for now you'll always see "Weather unavailable."

Other changes:

- Fix dimming/flashing on rotation.
- Fix other layout problems & inefficiencies.

AndroidManifest.xml
res/anim/dim_instant.xml [new file with mode: 0644]
res/anim/undim_instant.xml [new file with mode: 0644]
res/layout-land/desk_clock.xml
res/layout/desk_clock.xml
res/layout/desk_clock_time_date.xml
res/layout/desk_clock_weather.xml [new file with mode: 0644]
src/com/android/deskclock/DeskClock.java

index 0a89f0f..fbfce2c 100644 (file)
@@ -17,6 +17,7 @@
                 android:label="@string/app_label"
                 android:theme="@android:style/Theme.Wallpaper.NoTitleBar"
                 android:icon="@drawable/ic_widget_analog_clock"
+                android:configChanges="orientation|keyboardHidden|keyboard|navigation">
                 >
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
diff --git a/res/anim/dim_instant.xml b/res/anim/dim_instant.xml
new file mode 100644 (file)
index 0000000..bd7fe40
--- /dev/null
@@ -0,0 +1,6 @@
+<alpha xmlns:android="http://schemas.android.com/apk/res/android"
+    android:fromAlpha="0.0"
+    android:toAlpha="1.0"
+    android:duration="0"
+    android:fillAfter="true"
+    />
diff --git a/res/anim/undim_instant.xml b/res/anim/undim_instant.xml
new file mode 100644 (file)
index 0000000..ce20f1e
--- /dev/null
@@ -0,0 +1,6 @@
+<alpha xmlns:android="http://schemas.android.com/apk/res/android"
+    android:fromAlpha="1.0"
+    android:toAlpha="0.0"
+    android:duration="0"
+    android:fillAfter="true"
+    />
index 477c18b..7079975 100644 (file)
@@ -36,7 +36,7 @@
             android:layout_height="fill_parent"
             android:layout_marginLeft="20dip"
             android:layout_marginTop="20dip"
-            android:layout_marginRight="14dip"
+            android:layout_marginRight="20dip"
             android:layout_marginBottom="14dip"
             >
 
                 android:layout_weight="1"
                 android:layout_width="fill_parent"
                 android:layout_height="wrap_content"
-                android:gravity="center_vertical"
+                android:gravity="bottom"
+                android:layout_marginBottom="12dip"
                 >
 
                 <include layout="@layout/desk_clock_time_date"
-                    android:layout_width="fill_parent"
                     android:layout_height="wrap_content"
-                    android:layout_weight="1" />
-
-                <View android:id="@+id/weather" xmlns:android="http://schemas.android.com/apk/res/android"
-                    android:layout_height="100dip"
                     android:layout_width="wrap_content"
-                    android:background="#C0CC00CC"
-                    android:layout_marginBottom="12dip"
                     android:layout_weight="1"
+                    />
+
+                <include layout="@layout/desk_clock_weather"
+                    android:layout_height="wrap_content"
+                    android:layout_width="wrap_content"
+                    android:layout_weight="0"
                     android:layout_marginLeft="12dip"
                     />
 
     <View android:id="@+id/window_tint"
         android:layout_width="fill_parent"
         android:layout_height="fill_parent"
-        android:background="#D0000000"
+        android:background="#E0000000"
         android:visibility="visible"
         android:clickable="false"
         />
index c688a08..1133275 100644 (file)
 
             <include layout="@layout/desk_clock_time_date"
                 android:layout_weight="1"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginBottom="12dip"
                 />
 
-            <View android:id="@+id/weather" xmlns:android="http://schemas.android.com/apk/res/android"
-                android:layout_height="100dip"
+            <include layout="@layout/desk_clock_weather"
                 android:layout_width="wrap_content"
-                android:background="#C0CC00CC"
-                android:layout_marginBottom="12dip"
+                android:layout_height="wrap_content"
                 android:layout_weight="0"
+                android:layout_marginBottom="12dip"
                 />
 
-
             <include layout="@layout/desk_clock_battery"
                 android:layout_height="wrap_content"
                 android:layout_width="fill_parent"
     <View android:id="@+id/window_tint"
         android:layout_width="fill_parent"
         android:layout_height="fill_parent"
-        android:background="#D0000000"
+        android:background="#E0000000"
         android:visibility="visible"
         android:clickable="false"
         />
index abd55cf..068d03b 100644 (file)
 
 <!-- digital clock & date, together -->
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/time_date"
     android:orientation="vertical"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:gravity="left|center_vertical"
-    android:layout_marginBottom="12dip"
     android:layout_weight="1"
     >
 
@@ -37,7 +37,7 @@
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:gravity="center"
-            android:textSize="100sp"
+            android:textSize="106sp"
             android:textColor="#FFFFFFFF"
             android:shadowColor="#C0000000"
             android:shadowDx="0"
@@ -50,7 +50,7 @@
             android:layout_width="wrap_content"
             android:layout_height="fill_parent"
             android:gravity="bottom"
-            android:textSize="28sp"
+            android:textSize="24sp"
             android:singleLine="true"
             android:layout_marginLeft="3dip"
             android:textColor="#FFFFFFFF"
diff --git a/res/layout/desk_clock_weather.xml b/res/layout/desk_clock_weather.xml
new file mode 100644 (file)
index 0000000..bc71b89
--- /dev/null
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source 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.
+-->
+
+<!-- Weather data from Genie provider -->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/weather"
+    android:orientation="vertical"
+    android:layout_width="fill_parent"
+    android:layout_height="wrap_content"
+    android:layout_weight="0"
+    android:gravity="left"
+    >
+    <RelativeLayout android:id="@+id/weather_temp_icon_cluster"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentLeft="true"
+        android:layout_marginBottom="-4dip"
+        >
+        <TextView android:id="@+id/weather_temperature"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textSize="42sp"
+            android:textColor="#FFFFFFFF"
+            android:shadowColor="#C0000000"
+            android:shadowDx="0"
+            android:shadowDy="0"
+            android:shadowRadius="3.0"                        
+            android:layout_marginRight="8dip"
+            android:layout_centerVertical="true"
+            />
+        <ImageView android:id="@+id/weather_icon"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_toRightOf="@id/weather_temperature"
+            android:layout_centerVertical="true"
+            android:paddingTop="2dip"
+            />
+    </RelativeLayout>
+    <TextView android:id="@+id/weather_location"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_below="@id/weather_temp_icon_cluster"
+        android:gravity="left"
+        android:textSize="18sp"
+        android:textColor="#ffffffff"
+        android:shadowColor="#c0000000"
+        android:shadowDx="0"
+        android:shadowDy="0"
+        android:shadowRadius="3.0"                        
+        />
+</RelativeLayout>
index a451372..cb544d3 100644 (file)
@@ -24,39 +24,46 @@ import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
 import android.content.res.Configuration;
+import android.content.res.Resources;
 import android.database.Cursor;
-import android.graphics.drawable.Drawable;
 import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.Message;
+import android.os.SystemClock;
 import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.Log;
-import android.view.animation.AnimationUtils;
-import android.view.ContextMenu;
 import android.view.ContextMenu.ContextMenuInfo;
+import android.view.ContextMenu;
 import android.view.LayoutInflater;
 import android.view.Menu;
 import android.view.MenuItem;
-import android.view.View;
 import android.view.View.OnClickListener;
 import android.view.View.OnCreateContextMenuListener;
+import android.view.View;
 import android.view.ViewGroup;
 import android.view.Window;
 import android.view.WindowManager;
-import android.widget.AdapterView;
+import android.view.animation.AnimationUtils;
 import android.widget.AdapterView.AdapterContextMenuInfo;
 import android.widget.AdapterView.OnItemClickListener;
+import android.widget.AdapterView;
 import android.widget.Button;
-import android.widget.TextView;
 import android.widget.CheckBox;
+import android.widget.ImageView;
+import android.widget.TextView;
 
 import static android.os.BatteryManager.BATTERY_STATUS_CHARGING;
 import static android.os.BatteryManager.BATTERY_STATUS_FULL;
 import static android.os.BatteryManager.BATTERY_STATUS_UNKNOWN;
 
+import java.io.IOException;
+import java.io.InputStream;
 import java.text.DateFormat;
 import java.util.Date;
 
@@ -64,18 +71,54 @@ import java.util.Date;
  * DeskClock clock view for desk docks.
  */
 public class DeskClock extends Activity {
+    private static final boolean DEBUG = true;
 
     private static final String LOG_TAG = "DeskClock";
 
     private static final String MUSIC_NOW_PLAYING_ACTIVITY = "com.android.music.PLAYBACK_VIEWER";
 
-    private TextView mNextAlarm = null;
+    private final long FETCH_WEATHER_DELAY = 5 * 60 * 1000; // 5 min.
+    private final int FETCH_WEATHER_DATA_MSG = 10000;
+    private final int UPDATE_WEATHER_DISPLAY_MSG = 10001;
+
+    private static final String GENIE_PACKAGE_ID = "com.google.android.apps.genie.geniewidget";
+    private static final String WEATHER_CONTENT_AUTHORITY = GENIE_PACKAGE_ID + ".weather";
+    private static final String WEATHER_CONTENT_PATH = "/weather/current";
+    private static final String[] WEATHER_CONTENT_COLUMNS = new String[] {
+            "location",
+            "timestamp",
+            "highTemperature",
+            "lowTemperature",
+            "iconUrl",
+            "iconResId",
+            "description",
+        };
+
+    private DigitalClock mTime;
     private TextView mDate;
+
+    private TextView mNextAlarm = null;
     private TextView mBatteryDisplay;
-    private DigitalClock mTime;
+
+    private TextView mWeatherTemperature;
+    private TextView mWeatherLocation;
+    private ImageView mWeatherIcon;
+
+    private String mWeatherTemperatureString;
+    private String mWeatherLocationString;
+    private Drawable mWeatherIconDrawable;
+
+    private Resources mGenieResources = null;
 
     private boolean mDimmed = false;
 
+    private DateFormat mDateFormat;
+    
+    private int mBatteryLevel;
+    private boolean mPluggedIn;
+
+    private boolean mWeatherFetchScheduled = false;
+
     private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -90,10 +133,95 @@ public class DeskClock extends Activity {
         }
     };
 
-    private DateFormat mDateFormat;
-    
-    private int mBatteryLevel;
-    private boolean mPluggedIn;
+    private final Handler mHandy = new Handler() {
+        @Override
+        public void handleMessage(Message m) {
+            if (DEBUG) Log.d(LOG_TAG, "handleMessage: " + m.toString());
+
+            if (m.what == FETCH_WEATHER_DATA_MSG) {
+                if (!mWeatherFetchScheduled) return;
+                mWeatherFetchScheduled = false;
+                new Thread() { public void run() { fetchWeatherData(); } }.start();
+                scheduleWeatherFetchDelayed(FETCH_WEATHER_DELAY);
+            } else if (m.what == UPDATE_WEATHER_DISPLAY_MSG) {
+                updateWeatherDisplay();
+            }
+        }
+    };
+
+    private boolean supportsWeather() {
+        return (mGenieResources != null);
+    }
+
+    private void scheduleWeatherFetchDelayed(long delay) {
+        if (mWeatherFetchScheduled) return;
+
+        if (DEBUG) Log.d(LOG_TAG, "scheduling weather fetch message for " + delay + "ms from now");
+
+        mWeatherFetchScheduled = true;
+
+        mHandy.sendEmptyMessageDelayed(FETCH_WEATHER_DATA_MSG, delay);
+    }
+
+    private void unscheduleWeatherFetch() {
+        mWeatherFetchScheduled = false;
+    }
+
+    private void fetchWeatherData() {
+        // if we couldn't load the weather widget's resources, we simply
+        // assume it's not present on the device.
+        if (mGenieResources == null) return;
+
+        Uri queryUri = new Uri.Builder()
+            .scheme(android.content.ContentResolver.SCHEME_CONTENT)
+            .authority(WEATHER_CONTENT_AUTHORITY)
+            .path(WEATHER_CONTENT_PATH)
+            .appendPath(new Long(System.currentTimeMillis()).toString())
+            .build();
+
+        if (DEBUG) Log.d(LOG_TAG, "querying genie: " + queryUri);
+
+        Cursor cur;
+        try {
+            cur = managedQuery(
+                queryUri,
+                WEATHER_CONTENT_COLUMNS,
+                null,
+                null,
+                null);
+        } catch (RuntimeException e) {
+            Log.e(LOG_TAG, "Weather query failed", e);
+            cur = null;
+        }
+
+        if (cur != null && cur.moveToFirst()) {
+            mWeatherIconDrawable = mGenieResources.getDrawable(cur.getInt(
+                cur.getColumnIndexOrThrow("iconResId")));
+            mWeatherTemperatureString = cur.getString(
+                cur.getColumnIndexOrThrow("highTemperature"));
+            mWeatherLocationString = cur.getString(
+                cur.getColumnIndexOrThrow("location"));
+        } else {
+            Log.w(LOG_TAG, "No weather information available (cur=" 
+                + cur +")");
+            mWeatherIconDrawable = null;
+            mWeatherTemperatureString = "";
+            mWeatherLocationString = "Weather data unavailable."; // TODO: internationalize
+        }
+
+        mHandy.sendEmptyMessage(UPDATE_WEATHER_DISPLAY_MSG);
+    }
+
+    private void refreshWeather() {
+        if (supportsWeather())
+            scheduleWeatherFetchDelayed(0);
+    }
+
+    private void updateWeatherDisplay() {
+        mWeatherTemperature.setText(mWeatherTemperatureString);
+        mWeatherLocation.setText(mWeatherLocationString);
+        mWeatherIcon.setImageDrawable(mWeatherIconDrawable);
+    }
 
     // Adapted from KeyguardUpdateMonitor.java
     private void handleBatteryUpdate(int plugStatus, int batteryLevel) {
@@ -134,7 +262,14 @@ public class DeskClock extends Activity {
         }
     }
 
-    private void doDim() {
+    private void refreshAll() {
+        refreshDate();
+        refreshAlarm();
+        refreshBattery();
+        refreshWeather();
+    }
+
+    private void doDim(boolean fade) {
         View tintView = findViewById(R.id.window_tint);
 
         Window win = getWindow();
@@ -149,14 +284,18 @@ public class DeskClock extends Activity {
             winParams.dimAmount = 0.5f; // pump up contrast in dim mode
 
             // show the window tint
-            tintView.startAnimation(AnimationUtils.loadAnimation(this, R.anim.dim));
+            tintView.startAnimation(AnimationUtils.loadAnimation(this, 
+                fade ? R.anim.dim
+                     : R.anim.dim_instant));
         } else {
             winParams.flags &= (~WindowManager.LayoutParams.FLAG_FULLSCREEN);
 //            winParams.flags |= WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
             winParams.dimAmount = 0.2f; // lower contrast in normal mode
     
             // hide the window tint
-            tintView.startAnimation(AnimationUtils.loadAnimation(this, R.anim.undim));
+            tintView.startAnimation(AnimationUtils.loadAnimation(this, 
+                fade ? R.anim.undim
+                     : R.anim.undim_instant));
         }
         
         win.setAttributes(winParams);
@@ -175,16 +314,15 @@ public class DeskClock extends Activity {
         filter.addAction(Intent.ACTION_BATTERY_CHANGED);
         registerReceiver(mIntentReceiver, filter);
 
-        doDim();
-        refreshDate();
-        refreshAlarm();
-        refreshBattery();
+        doDim(false);
+        refreshAll();
     }
 
     @Override
     public void onPause() {
         super.onPause();
         unregisterReceiver(mIntentReceiver);
+        unscheduleWeatherFetch();
     }
 
 
@@ -195,6 +333,10 @@ public class DeskClock extends Activity {
         mDate = (TextView) findViewById(R.id.date);
         mBatteryDisplay = (TextView) findViewById(R.id.battery);
 
+        mWeatherTemperature = (TextView) findViewById(R.id.weather_temperature);
+        mWeatherLocation = (TextView) findViewById(R.id.weather_location);
+        mWeatherIcon = (ImageView) findViewById(R.id.weather_icon);
+
         mNextAlarm = (TextView) findViewById(R.id.nextAlarm);
 
         final Button alarmButton = (Button) findViewById(R.id.alarm_button);
@@ -241,23 +383,30 @@ public class DeskClock extends Activity {
         nightmodeButton.setOnClickListener(new View.OnClickListener() {
             public void onClick(View v) {
                 mDimmed = ! mDimmed;
-                doDim();
+                doDim(true);
             }
         });
-
-        doDim();
     }
 
     @Override
     public void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
         initViews();
+        doDim(false);
+        refreshAll();
     }
 
-
     @Override
     protected void onCreate(Bundle icicle) {
         super.onCreate(icicle);
+
+        try {
+            mGenieResources = getPackageManager().getResourcesForApplication(GENIE_PACKAGE_ID);
+        } catch (PackageManager.NameNotFoundException e) {
+            // no weather info available
+            Log.w(LOG_TAG, "Can't find "+GENIE_PACKAGE_ID+". Weather forecast will not be available.");
+        }
+
         initViews();
     }