Merge "Add deeplink support for AlarmClock alarm actions" into ub-deskclock-gatling
diff --git a/res/drawable/ic_ringtone_silent.xml b/res/drawable/ic_ringtone_silent.xml
index 8976623..4e6d0ab 100644
--- a/res/drawable/ic_ringtone_silent.xml
+++ b/res/drawable/ic_ringtone_silent.xml
@@ -15,11 +15,11 @@
 -->
 
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="24dp"
-        android:height="24dp"
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportHeight="24.0"
+    android:viewportWidth="24.0">
     <path
         android:fillColor="#FFFFFFFF"
-        android:pathData="M12,22c1.1,0 2,-0.9 2,-2h-4c0,1.1 0.9,2 2,2zM18,16v-5c0,-3.07 -1.63,-5.64 -4.5,-6.32L13.5,4c0,-0.83 -0.67,-1.5 -1.5,-1.5s-1.5,0.67 -1.5,1.5v0.68C7.64,5.36 6,7.92 6,11v5l-2,2v1h16v-1l-2,-2zM16,17L8,17v-6c0,-2.48 1.51,-4.5 4,-4.5s4,2.02 4,4.5v6z"/>
+        android:pathData="M20,18.69L7.84,6.14 5.27,3.49 4,4.76l2.8,2.8v0.01c-0.52,0.99 -0.8,2.16 -0.8,3.42v5l-2,2v1h13.73l2,2L21,19.72l-1,-1.03zM12,22c1.11,0 2,-0.89 2,-2h-4c0,1.11 0.89,2 2,2zM18,14.68L18,11c0,-3.08 -1.64,-5.64 -4.5,-6.32L13.5,4c0,-0.83 -0.67,-1.5 -1.5,-1.5s-1.5,0.67 -1.5,1.5v0.68c-0.15,0.03 -0.29,0.08 -0.42,0.12 -0.1,0.03 -0.2,0.07 -0.3,0.11h-0.01c-0.01,0 -0.01,0 -0.02,0.01 -0.23,0.09 -0.46,0.2 -0.68,0.31 0,0 -0.01,0 -0.01,0.01L18,14.68z" />
 </vector>
\ No newline at end of file
diff --git a/res/values-land/dimens.xml b/res/values-land/dimens.xml
index 7383d6b..88fb475 100644
--- a/res/values-land/dimens.xml
+++ b/res/values-land/dimens.xml
@@ -17,7 +17,6 @@
 <!-- These resources are around just to allow their values to be customized
      for different hardware and product builds. -->
 <resources>
-    <dimen name="time_text_size">153dip</dimen>
     <dimen name="ampm_text_size">32dip</dimen>
     <dimen name="date_text_size">15sp</dimen>
     <dimen name="next_alarm_text_size">15sp</dimen>
diff --git a/res/values-sw600dp-land/dimens.xml b/res/values-sw600dp-land/dimens.xml
index d5559dc..29ed85e 100644
--- a/res/values-sw600dp-land/dimens.xml
+++ b/res/values-sw600dp-land/dimens.xml
@@ -17,7 +17,6 @@
 <!-- These resources are around just to allow their values to be customized
      for different hardware and product builds. -->
 <resources>
-    <dimen name="time_text_size">275dip</dimen>
     <dimen name="ampm_text_size">90dip</dimen>
     <dimen name="date_text_size">15sp</dimen>
     <dimen name="next_alarm_text_size">15sp</dimen>
diff --git a/res/values-sw600dp/dimens.xml b/res/values-sw600dp/dimens.xml
index 1b1fc79..b3379fa 100644
--- a/res/values-sw600dp/dimens.xml
+++ b/res/values-sw600dp/dimens.xml
@@ -18,7 +18,6 @@
      for different hardware and product builds. -->
 <resources>
     <dimen name="label_text_size">18sp</dimen>
-    <dimen name="time_text_size">180dip</dimen>
     <dimen name="ampm_text_size">54dip</dimen>
     <dimen name="date_text_size">15sp</dimen>
     <dimen name="next_alarm_text_size">15sp</dimen>
diff --git a/src/com/android/deskclock/ClockFragment.java b/src/com/android/deskclock/ClockFragment.java
index 86555ad..c8dd129 100644
--- a/src/com/android/deskclock/ClockFragment.java
+++ b/src/com/android/deskclock/ClockFragment.java
@@ -40,6 +40,7 @@
 import android.widget.TextView;
 
 import com.android.deskclock.data.City;
+import com.android.deskclock.data.CityListener;
 import com.android.deskclock.data.DataModel;
 import com.android.deskclock.uidata.UiDataModel;
 import com.android.deskclock.worldclock.CitySelectionActivity;
@@ -108,6 +109,7 @@
         mCityList = (RecyclerView) fragmentView.findViewById(R.id.cities);
         mCityList.setLayoutManager(new LinearLayoutManager(getActivity()));
         mCityList.setAdapter(mCityAdapter);
+        DataModel.getDataModel().addCityListener(mCityAdapter);
 
         final ScrollPositionWatcher scrollPositionWatcher = new ScrollPositionWatcher();
         mCityList.addOnScrollListener(scrollPositionWatcher);
@@ -122,6 +124,9 @@
             mAnalogClock = mClockFrame.findViewById(R.id.analog_clock);
         }
 
+        // Schedule a runnable to update the date every quarter hour.
+        UiDataModel.getUiDataModel().addQuarterHourCallback(mQuarterHourUpdater, 100);
+
         return fragmentView;
     }
 
@@ -160,7 +165,6 @@
             mCityList.setVisibility(mCityAdapter.getItemCount() == 0 ? GONE : VISIBLE);
         }
 
-        mCityAdapter.notifyDataSetChanged();
         refreshAlarm();
 
         // Alarm observer is null on L or later.
@@ -169,15 +173,11 @@
             final Uri uri = Settings.System.getUriFor(Settings.System.NEXT_ALARM_FORMATTED);
             activity.getContentResolver().registerContentObserver(uri, false, mAlarmObserver);
         }
-
-        // Schedule a runnable to update the date every quarter hour.
-        UiDataModel.getUiDataModel().addQuarterHourCallback(mQuarterHourUpdater, 100);
     }
 
     @Override
     public void onPause() {
         super.onPause();
-        UiDataModel.getUiDataModel().removePeriodicCallback(mQuarterHourUpdater);
 
         final Activity activity = getActivity();
         if (mAlarmChangeReceiver != null) {
@@ -189,6 +189,13 @@
     }
 
     @Override
+    public void onDestroyView() {
+        super.onDestroyView();
+        UiDataModel.getUiDataModel().removePeriodicCallback(mQuarterHourUpdater);
+        DataModel.getDataModel().removeCityListener(mCityAdapter);
+    }
+
+    @Override
     public void onFabClick(@NonNull ImageView fab) {
         startActivity(new Intent(getActivity(), CitySelectionActivity.class));
     }
@@ -292,7 +299,8 @@
      * current time at home does not match the current time in the timezone of the current location.
      * If the phone is in portrait mode it will also include the main clock at the top.
      */
-    private static final class SelectedCitiesAdapter extends RecyclerView.Adapter {
+    private static final class SelectedCitiesAdapter extends RecyclerView.Adapter
+            implements CityListener {
 
         private final static int MAIN_CLOCK = R.layout.main_clock_frame;
         private final static int WORLD_CLOCK = R.layout.world_clock_item;
@@ -388,6 +396,11 @@
             }
         }
 
+        @Override
+        public void citiesChanged(List<City> oldCities, List<City> newCities) {
+            notifyDataSetChanged();
+        }
+
         private static final class CityViewHolder extends RecyclerView.ViewHolder {
 
             private final TextView mName;
diff --git a/src/com/android/deskclock/data/CityListener.java b/src/com/android/deskclock/data/CityListener.java
new file mode 100644
index 0000000..37f1b52
--- /dev/null
+++ b/src/com/android/deskclock/data/CityListener.java
@@ -0,0 +1,10 @@
+package com.android.deskclock.data;
+
+import java.util.List;
+
+/**
+ * The interface through which interested parties are notified of changes to the world cities list.
+ */
+public interface CityListener {
+    void citiesChanged(List<City> oldCities, List<City> newCities);
+}
diff --git a/src/com/android/deskclock/data/CityModel.java b/src/com/android/deskclock/data/CityModel.java
index b34c447..fafd48b 100644
--- a/src/com/android/deskclock/data/CityModel.java
+++ b/src/com/android/deskclock/data/CityModel.java
@@ -58,6 +58,9 @@
     @SuppressWarnings("FieldCanBeLocal")
     private final BroadcastReceiver mLocaleChangedReceiver = new LocaleChangedReceiver();
 
+    /** List of listeners to invoke upon world city list change */
+    private final List<CityListener> mCityListeners = new ArrayList<>();
+
     /** Maps city ID to city instance. */
     private Map<String, City> mCityMap;
 
@@ -86,6 +89,14 @@
         prefs.registerOnSharedPreferenceChangeListener(mPreferenceListener);
     }
 
+    void addCityListener(CityListener cityListener) {
+        mCityListeners.add(cityListener);
+    }
+
+    void removeCityListener(CityListener cityListener) {
+        mCityListeners.remove(cityListener);
+    }
+
     /**
      * @return a list of all cities in their display order
      */
@@ -178,6 +189,7 @@
      * @param cities the new collection of cities selected for display by the user
      */
     void setSelectedCities(Collection<City> cities) {
+        final List<City> oldCities = getAllCities();
         CityDAO.setSelectedCities(mContext, cities);
 
         // Clear caches affected by this update.
@@ -186,7 +198,7 @@
         mUnselectedCities = null;
 
         // Broadcast the change to the selected cities for the benefit of widgets.
-        sendCitiesChangedBroadcast();
+        fireCitiesChanged(oldCities, getAllCities());
     }
 
     /**
@@ -219,14 +231,6 @@
         mUnselectedCities = null;
     }
 
-    /**
-     * @param cityId identifies a city
-     * @return the corresponding city
-     */
-    City getCityById(String cityId) {
-        return getCityMap().get(cityId);
-    }
-
     private Map<String, City> getCityMap() {
         if (mCityMap == null) {
             mCityMap = CityDAO.getCities(mContext);
@@ -244,8 +248,11 @@
         throw new IllegalStateException("unexpected city sort: " + citySort);
     }
 
-    private void sendCitiesChangedBroadcast() {
+    private void fireCitiesChanged(List<City> oldCities, List<City> newCities) {
         mContext.sendBroadcast(new Intent(DataModel.ACTION_WORLD_CITIES_CHANGED));
+        for (CityListener cityListener : mCityListeners) {
+            cityListener.citiesChanged(oldCities, newCities);
+        }
     }
 
     /**
@@ -273,7 +280,8 @@
                 case SettingsActivity.KEY_HOME_TZ:
                     mHomeCity = null;
                 case SettingsActivity.KEY_AUTO_HOME_CLOCK:
-                    sendCitiesChangedBroadcast();
+                    final List<City> cities = getAllCities();
+                    fireCitiesChanged(cities, cities);
                     break;
             }
         }
diff --git a/src/com/android/deskclock/data/DataModel.java b/src/com/android/deskclock/data/DataModel.java
index c3d77a3..2f07b4e 100644
--- a/src/com/android/deskclock/data/DataModel.java
+++ b/src/com/android/deskclock/data/DataModel.java
@@ -270,6 +270,22 @@
         mCityModel.toggleCitySort();
     }
 
+    /**
+     * @param cityListener listener to be notified when the world city list changes
+     */
+    public void addCityListener(CityListener cityListener) {
+        enforceMainLooper();
+        mCityModel.addCityListener(cityListener);
+    }
+
+    /**
+     * @param cityListener listener that no longer needs to be notified of world city list changes
+     */
+    public void removeCityListener(CityListener cityListener) {
+        enforceMainLooper();
+        mCityModel.removeCityListener(cityListener);
+    }
+
     //
     // Timers
     //