Fix snooze event tracking
am: e5cf47c18e

* commit 'e5cf47c18e3588b12beb2a01b9d1d0c816e4868c':
  Fix snooze event tracking
diff --git a/.gitignore b/.gitignore
index cece67b..c6cbe56 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,18 +1,8 @@
-.classpath
-.gitignore
-.gradle
-.idea
-.name
-.project
-bin
-build
-gen
-lint.xml
-local.properties
-out
-project.properties
-*.DS_Store
-*~
 *.iml
-*.swo
-*.swp
+.gradle
+/local.properties
+/.idea/workspace.xml
+/.idea/libraries
+.DS_Store
+/build
+/captures
diff --git a/Android.mk b/Android.mk
index a8e5217..906dab8 100644
--- a/Android.mk
+++ b/Android.mk
@@ -6,13 +6,17 @@
 
 ifeq ($(TARGET_BUILD_APPS),)
 LOCAL_RESOURCE_DIR += frameworks/support/design/res
+LOCAL_RESOURCE_DIR += frameworks/support/v14/preference/res
 LOCAL_RESOURCE_DIR += frameworks/support/v7/appcompat/res
 LOCAL_RESOURCE_DIR += frameworks/support/v7/gridlayout/res
+LOCAL_RESOURCE_DIR += frameworks/support/v7/preference/res
 LOCAL_RESOURCE_DIR += frameworks/support/v7/recyclerview/res
 else
 LOCAL_RESOURCE_DIR += prebuilts/sdk/current/support/design/res
+LOCAL_RESOURCE_DIR += prebuilts/sdk/current/support/v14/preference/res
 LOCAL_RESOURCE_DIR += prebuilts/sdk/current/support/v7/appcompat/res
 LOCAL_RESOURCE_DIR += prebuilts/sdk/current/support/v7/gridlayout/res
+LOCAL_RESOURCE_DIR += prebuilts/sdk/current/support/v7/preference/res
 LOCAL_RESOURCE_DIR += prebuilts/sdk/current/support/v7/recyclerview/res
 endif
 
@@ -25,19 +29,21 @@
 LOCAL_SRC_FILES := $(call all-java-files-under, src gen)
 
 LOCAL_STATIC_JAVA_LIBRARIES := android-opt-datetimepicker
-LOCAL_STATIC_JAVA_LIBRARIES += messageformat
 LOCAL_STATIC_JAVA_LIBRARIES += android-support-design
 LOCAL_STATIC_JAVA_LIBRARIES += android-support-v13
+LOCAL_STATIC_JAVA_LIBRARIES += android-support-v14-preference
 LOCAL_STATIC_JAVA_LIBRARIES += android-support-v7-appcompat
 LOCAL_STATIC_JAVA_LIBRARIES += android-support-v7-gridlayout
+LOCAL_STATIC_JAVA_LIBRARIES += android-support-v7-preference
 LOCAL_STATIC_JAVA_LIBRARIES += android-support-v7-recyclerview
 
 LOCAL_AAPT_FLAGS := --auto-add-overlay
 LOCAL_AAPT_FLAGS += --extra-packages android.support.design
+LOCAL_AAPT_FLAGS += --extra-packages android.support.v14.preference
 LOCAL_AAPT_FLAGS += --extra-packages android.support.v7.appcompat
 LOCAL_AAPT_FLAGS += --extra-packages android.support.v7.gridlayout
+LOCAL_AAPT_FLAGS += --extra-packages android.support.v7.preference
 LOCAL_AAPT_FLAGS += --extra-packages android.support.v7.recyclerview
 LOCAL_AAPT_FLAGS += --extra-packages com.android.datetimepicker
-LOCAL_AAPT_FLAGS += --extra-packages com.android.messageformat
 
 include $(BUILD_PACKAGE)
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 441ad87..8500c85 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -15,9 +15,12 @@
   limitations under the License.
   -->
 
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-        package="com.android.deskclock"
-        android:versionCode="410" android:versionName="4.1.0">
+<manifest
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    package="com.android.deskclock"
+    android:versionCode="440"
+    android:versionName="4.4.0">
 
     <original-package android:name="com.android.alarmclock" />
     <original-package android:name="com.android.deskclock" />
@@ -25,8 +28,8 @@
     <uses-sdk android:minSdkVersion="19" android:targetSdkVersion="23" />
 
     <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
-    <uses-permission android:name="android.permission.WAKE_LOCK"/>
-    <uses-permission android:name="android.permission.VIBRATE"/>
+    <uses-permission android:name="android.permission.WAKE_LOCK" />
+    <uses-permission android:name="android.permission.VIBRATE" />
     <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
     <!-- WRITE_SETTINGS is required to record the upcoming alarm prior to L -->
     <uses-permission android:name="android.permission.WRITE_SETTINGS" />
@@ -35,26 +38,28 @@
     <!-- READ_EXTERNAL_STORAGE is required to play custom ringtones from the SD card prior to M -->
     <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
 
-    <application android:label="@string/app_label"
-                 android:name=".DeskClockApplication"
-                 android:allowBackup="true"
-                 android:backupAgent="DeskClockBackupAgent"
-                 android:fullBackupContent="@xml/backup_scheme"
-                 android:fullBackupOnly="true"
-                 android:icon="@mipmap/ic_launcher_alarmclock"
-                 android:requiredForAllUsers="true"
-                 android:supportsRtl="true">
+    <application
+        android:name=".DeskClockApplication"
+        android:allowBackup="true"
+        android:backupAgent="DeskClockBackupAgent"
+        android:fullBackupContent="@xml/backup_scheme"
+        android:fullBackupOnly="true"
+        android:icon="@mipmap/ic_launcher_alarmclock"
+        android:label="@string/app_label"
+        android:requiredForAllUsers="true"
+        android:supportsRtl="true">
 
-        <provider android:name=".provider.ClockProvider"
-                android:authorities="com.android.deskclock"
-                android:exported="false" />
+        <provider
+            android:name=".provider.ClockProvider"
+            android:authorities="com.android.deskclock"
+            android:exported="false" />
 
-        <activity android:name="DeskClock"
-                android:label="@string/app_label"
-                android:theme="@style/DeskClockTheme"
-                android:icon="@mipmap/ic_launcher_alarmclock"
-                android:launchMode="singleTask">
-
+        <activity
+            android:name=".DeskClock"
+            android:icon="@mipmap/ic_launcher_alarmclock"
+            android:label="@string/app_label"
+            android:launchMode="singleTask"
+            android:theme="@style/DeskClockTheme">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.DEFAULT" />
@@ -62,14 +67,14 @@
             </intent-filter>
         </activity>
 
-        <activity-alias android:name="DockClock"
-                android:targetActivity="DeskClock"
-                android:label="@string/app_label"
-                android:theme="@style/DeskClockTheme"
-                android:icon="@mipmap/ic_launcher_alarmclock"
-                android:launchMode="singleTask"
-                android:enabled="@bool/config_dockAppEnabled"
-                >
+        <activity-alias
+            android:name=".DockClock"
+            android:enabled="false"
+            android:icon="@mipmap/ic_launcher_alarmclock"
+            android:label="@string/app_label"
+            android:launchMode="singleTask"
+            android:targetActivity="DeskClock"
+            android:theme="@style/DeskClockTheme">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.DEFAULT" />
@@ -77,54 +82,58 @@
             </intent-filter>
         </activity-alias>
 
-        <activity android:name=".settings.SettingsActivity"
-                android:label="@string/settings"
-                android:theme="@style/SettingsTheme"
-                android:taskAffinity=""
-                android:excludeFromRecents="true"
-                >
+        <activity
+            android:name=".settings.SettingsActivity"
+            android:excludeFromRecents="true"
+            android:label="@string/settings"
+            android:taskAffinity=""
+            android:theme="@style/SettingsTheme">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
             </intent-filter>
         </activity>
 
-        <activity android:name=".worldclock.CitySelectionActivity"
-                android:label="@string/cities_activity_title"
-                android:theme="@style/CitiesTheme"
-                android:taskAffinity=""
-                android:excludeFromRecents="true">
+        <activity
+            android:name=".worldclock.CitySelectionActivity"
+            android:excludeFromRecents="true"
+            android:label="@string/cities_activity_title"
+            android:taskAffinity=""
+            android:theme="@style/CitiesTheme">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
             </intent-filter>
         </activity>
 
-        <activity android:name=".alarms.AlarmActivity"
-                android:taskAffinity=""
-                android:excludeFromRecents="true"
-                android:theme="@style/AlarmAlertFullScreenTheme"
-                android:windowSoftInputMode="stateAlwaysHidden"
-                android:showOnLockScreen="true" />
+        <activity
+            android:name=".alarms.AlarmActivity"
+            android:excludeFromRecents="true"
+            android:showOnLockScreen="true"
+            android:taskAffinity=""
+            android:theme="@style/AlarmAlertFullScreenTheme"
+            android:windowSoftInputMode="stateAlwaysHidden" />
 
-        <activity android:name="ScreensaverActivity"
-                android:excludeFromRecents="true"
-                android:taskAffinity=""
-                android:theme="@style/ScreensaverActivityTheme"
-                android:configChanges="orientation|screenSize|keyboardHidden|keyboard" />
+        <activity
+            android:name=".ScreensaverActivity"
+            android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
+            android:excludeFromRecents="true"
+            android:taskAffinity=""
+            android:theme="@style/ScreensaverActivityTheme" />
 
-        <receiver android:name=".alarms.AlarmStateManager"
-                  android:exported="false">
-        </receiver>
+        <receiver
+            android:name=".alarms.AlarmStateManager"
+            android:exported="false" />
 
-        <service android:name=".alarms.AlarmService"
-                 android:exported="false">
-        </service>
+        <service
+            android:name=".alarms.AlarmService"
+            android:exported="false" />
 
-        <activity android:name="HandleApiCalls"
-                android:theme="@android:style/Theme.NoDisplay"
-                android:excludeFromRecents="true"
-                android:launchMode="singleTask"
-                android:permission="com.android.alarm.permission.SET_ALARM"
-                android:taskAffinity="">
+        <activity
+            android:name=".HandleApiCalls"
+            android:excludeFromRecents="true"
+            android:launchMode="singleTask"
+            android:permission="com.android.alarm.permission.SET_ALARM"
+            android:taskAffinity=""
+            android:theme="@android:style/Theme.NoDisplay">
             <intent-filter>
                 <action android:name="android.intent.action.SET_ALARM" />
                 <category android:name="android.intent.category.DEFAULT" />
@@ -152,17 +161,18 @@
             </intent-filter>
         </activity>
 
-        <activity-alias android:name="HandleSetAlarm"
-            android:targetActivity=".HandleApiCalls"
-            android:exported="true">
-        </activity-alias>
+        <activity-alias
+            android:name="HandleSetAlarm"
+            android:exported="true"
+            android:targetActivity=".HandleApiCalls" />
 
-        <activity android:name=".HandleDeskClockApiCalls"
-            android:theme="@android:style/Theme.NoDisplay"
+        <activity
+            android:name=".HandleDeskClockApiCalls"
             android:excludeFromRecents="true"
             android:launchMode="singleTask"
             android:permission="com.android.alarm.permission.SET_ALARM"
-            android:taskAffinity="">
+            android:taskAffinity=""
+            android:theme="@android:style/Theme.NoDisplay">
             <intent-filter>
                 <action android:name="com.android.deskclock.action.SHOW_CLOCK" />
                 <category android:name="android.intent.category.DEFAULT" />
@@ -230,7 +240,7 @@
             </intent-filter>
         </activity>
 
-        <receiver android:name="AlarmInitReceiver">
+        <receiver android:name=".AlarmInitReceiver">
             <intent-filter>
                 <action android:name="android.intent.action.BOOT_COMPLETED" />
                 <action android:name="android.intent.action.TIME_SET" />
@@ -245,15 +255,15 @@
             android:icon="@mipmap/ic_launcher_alarmclock"
             android:label="@string/analog_gadget">
             <intent-filter>
-                <action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
+                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
             </intent-filter>
 
             <meta-data
                 android:name="android.appwidget.oldName"
-                android:value="com.android.deskclock.AnalogAppWidgetProvider"/>
+                android:value="com.android.deskclock.AnalogAppWidgetProvider" />
             <meta-data
                 android:name="android.appwidget.provider"
-                android:resource="@xml/analog_appwidget"/>
+                android:resource="@xml/analog_appwidget" />
         </receiver>
 
         <receiver
@@ -261,27 +271,29 @@
             android:icon="@mipmap/ic_launcher_alarmclock"
             android:label="@string/digital_gadget">
             <intent-filter>
-                <action android:name="android.intent.action.TIME_SET"/>
-                <action android:name="android.intent.action.SCREEN_ON"/>
-                <action android:name="android.intent.action.DATE_CHANGED"/>
-                <action android:name="android.intent.action.LOCALE_CHANGED"/>
-                <action android:name="android.intent.action.TIMEZONE_CHANGED"/>
-                <action android:name="com.android.deskclock.DIGITAL_WIDGET_CHANGED"/>
-                <action android:name="com.android.deskclock.ON_QUARTER_HOUR"/>
-                <action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
-                <action android:name="android.app.action.NEXT_ALARM_CLOCK_CHANGED"/>
+                <action android:name="android.intent.action.TIME_SET" />
+                <action android:name="android.intent.action.SCREEN_ON" />
+                <action android:name="android.intent.action.DATE_CHANGED" />
+                <action android:name="android.intent.action.LOCALE_CHANGED" />
+                <action android:name="android.intent.action.TIMEZONE_CHANGED" />
+                <action android:name="com.android.deskclock.DIGITAL_WIDGET_CHANGED" />
+                <action android:name="com.android.deskclock.ON_QUARTER_HOUR" />
+                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
+                <action android:name="android.app.action.NEXT_ALARM_CLOCK_CHANGED" />
             </intent-filter>
             <meta-data
                 android:name="android.appwidget.provider"
-                android:resource="@xml/digital_appwidget"/>
+                android:resource="@xml/digital_appwidget" />
         </receiver>
 
-        <service android:name="com.android.alarmclock.DigitalAppWidgetService"
-             android:permission="android.permission.BIND_REMOTEVIEWS"
-             android:exported="false" />
+        <service
+            android:name="com.android.alarmclock.DigitalAppWidgetService"
+            android:exported="false"
+            android:permission="android.permission.BIND_REMOTEVIEWS" />
 
         <!-- Dream (screensaver) implementation -->
-        <service android:name="Screensaver"
+        <service
+            android:name=".Screensaver"
             android:exported="true"
             android:label="@string/app_label"
             android:permission="android.permission.BIND_DREAM_SERVICE">
@@ -296,13 +308,13 @@
         </service>
 
         <!-- Settings activity for screensaver -->
-        <activity android:name=".settings.ScreensaverSettingsActivity"
-                android:label="@string/screensaver_settings"
-                android:theme="@style/SettingsTheme"
-                android:taskAffinity=""
-                android:excludeFromRecents="true"
-                android:exported="true"
-                >
+        <activity
+            android:name=".settings.ScreensaverSettingsActivity"
+            android:excludeFromRecents="true"
+            android:exported="true"
+            android:label="@string/screensaver_settings"
+            android:taskAffinity=""
+            android:theme="@style/SettingsTheme">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
             </intent-filter>
@@ -311,18 +323,19 @@
         <activity
             android:name=".AlarmSelectionActivity"
             android:label="@string/dismiss_alarm"
-            android:theme="@android:style/Theme.Holo.Light.Dialog.NoActionBar"/>
+            android:theme="@android:style/Theme.Holo.Light.Dialog.NoActionBar" />
 
         <!-- This activity displays only the timers that have expired with only a reset button
          present. This makes the activity appropriate for display above the lock screen so that
          users have the limited ability to silence expired timers but nothing else. -->
-        <activity android:name=".timer.ExpiredTimersActivity"
-                android:excludeFromRecents="true"
-                android:theme="@style/ExpiredTimersActivityTheme"
-                android:launchMode="singleInstance"
-                android:showOnLockScreen="true"
-                android:taskAffinity=""
-                android:configChanges="screenSize|keyboardHidden|keyboard|navigation"/>
+        <activity
+            android:name=".timer.ExpiredTimersActivity"
+            android:configChanges="screenSize|keyboardHidden|keyboard|navigation"
+            android:excludeFromRecents="true"
+            android:launchMode="singleInstance"
+            android:showOnLockScreen="true"
+            android:taskAffinity=""
+            android:theme="@style/ExpiredTimersActivityTheme" />
 
         <!-- Legacy broadcast receiver that honors old scheduled timers across app upgrade. -->
         <receiver android:name="com.android.deskclock.timer.TimerReceiver"
@@ -332,14 +345,17 @@
             </intent-filter>
         </receiver>
 
-        <service android:name=".timer.TimerService"
-                 android:exported="false"
-                 android:description="@string/timer_service_desc">
-        </service>
+        <service
+            android:name=".timer.TimerService"
+            android:description="@string/timer_service_desc"
+            android:exported="false"
+            tools:ignore="ManifestResource" />
 
-        <service android:name=".stopwatch.StopwatchService"
-                android:exported="false"
-                android:description="@string/stopwatch_service_desc">
-        </service>
+        <service
+            android:name=".stopwatch.StopwatchService"
+            android:description="@string/stopwatch_service_desc"
+            android:exported="false"
+            tools:ignore="ManifestResource" />
+
     </application>
 </manifest>
diff --git a/build.gradle b/build.gradle
deleted file mode 100644
index aa37742..0000000
--- a/build.gradle
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
-* Copyright (C) 2015 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.
-*/
-
-apply plugin: "com.android.application"
-
-android {
-    sourceSets {
-        main {
-            manifest.srcFile "AndroidManifest.xml"
-            java.srcDir "src"
-            res.srcDir "res"
-            assets.srcDir "assets"
-        }
-    }
-
-    compileOptions {
-        sourceCompatibility JavaVersion.VERSION_1_7
-        targetCompatibility JavaVersion.VERSION_1_7
-    }
-
-    dependencies {
-        compile (project(":android-opt-datetimepicker")) {
-            exclude module: "support-v4"
-        }
-        compile project(":messageformat")
-        compile project(":support-design")
-        compile project(":support-v7-appcompat")
-        compile project(":support-v7-gridlayout")
-        compile project(":support-v7-recyclerview")
-        compile project(":support-v13")
-    }
-}
diff --git a/gen/com/android/deskclock/BuildConfig.java b/gen/com/android/deskclock/BuildConfig.java
index 861848f..c88f182 100644
--- a/gen/com/android/deskclock/BuildConfig.java
+++ b/gen/com/android/deskclock/BuildConfig.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2016 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.
diff --git a/res/color-v23/timepicker_ampm.xml b/res/color-v23/timepicker_ampm.xml
deleted file mode 100644
index df92405..0000000
--- a/res/color-v23/timepicker_ampm.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item
-        android:state_selected="true"
-        android:color="@color/white" />
-    <item
-        android:alpha="?android:attr/disabledAlpha"
-        android:color="@color/white" />
-</selector>
diff --git a/res/color-v23/timepicker_time.xml b/res/color-v23/timepicker_time.xml
deleted file mode 100644
index 100473c..0000000
--- a/res/color-v23/timepicker_time.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item
-        android:state_selected="true"
-        android:color="?android:attr/colorAccent" />
-    <item android:color="@color/white" />
-</selector>
\ No newline at end of file
diff --git a/res/drawable-v21/fastscroll_preview_left.xml b/res/drawable-v21/fastscroll_preview_left.xml
deleted file mode 100644
index 51e54ea..0000000
--- a/res/drawable-v21/fastscroll_preview_left.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright (C) 2015 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.
-  -->
-
-<inset xmlns:android="http://schemas.android.com/apk/res/android"
-    android:insetLeft="@dimen/fastscroll_preview_padding">
-    <shape xmlns:android="http://schemas.android.com/apk/res/android"
-        android:shape="rectangle">
-        <corners
-            android:topLeftRadius="44dp"
-            android:topRightRadius="44dp"
-            android:bottomRightRadius="44dp" />
-        <padding
-            android:paddingLeft="22dp"
-            android:paddingRight="22dp" />
-        <solid android:color="@color/color_accent" />
-    </shape>
-</inset>
diff --git a/res/drawable-v21/fastscroll_preview_right.xml b/res/drawable-v21/fastscroll_preview_right.xml
deleted file mode 100644
index c2dff95..0000000
--- a/res/drawable-v21/fastscroll_preview_right.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright (C) 2015 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.
-  -->
-
-<inset xmlns:android="http://schemas.android.com/apk/res/android"
-    android:insetRight="@dimen/fastscroll_preview_padding">
-    <shape xmlns:android="http://schemas.android.com/apk/res/android"
-        android:shape="rectangle">
-        <corners
-            android:topLeftRadius="44dp"
-            android:topRightRadius="44dp"
-            android:bottomLeftRadius="44dp" />
-        <padding
-            android:paddingLeft="22dp"
-            android:paddingRight="22dp" />
-        <solid android:color="@color/color_accent" />
-    </shape>
-</inset>
diff --git a/res/drawable/fastscroll_track.xml b/res/drawable/fastscroll_track.xml
index 2057c5b..81b6261 100644
--- a/res/drawable/fastscroll_track.xml
+++ b/res/drawable/fastscroll_track.xml
@@ -19,13 +19,13 @@
     <item android:state_pressed="true">
         <shape android:shape="rectangle">
             <solid android:color="#1fffffff"/>
-            <size android:width="@dimen/fastscroll_thumb_width" />
+            <size android:width="@dimen/fastscroll_track_width" />
         </shape>
     </item>
     <item>
         <shape android:shape="rectangle">
             <solid android:color="@color/transparent"/>
-            <size android:width="@dimen/fastscroll_thumb_width" />
+            <size android:width="@dimen/fastscroll_track_width" />
         </shape>
     </item>
 </selector>
diff --git a/res/layout-land/time_setup_view.xml b/res/layout-land/time_setup_view.xml
index b5e68e1..c59b4a4 100644
--- a/res/layout-land/time_setup_view.xml
+++ b/res/layout-land/time_setup_view.xml
@@ -13,81 +13,74 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
-    android:layout_height="match_parent">
+    android:layout_height="match_parent"
+    android:baselineAligned="false"
+    android:orientation="horizontal">
 
+    <!-- This nested ltr layout cannot be combined with the parent because
+         in RTL, the Keypad and Timer value should swap. -->
     <LinearLayout
-        android:layout_width="match_parent"
+        android:id="@+id/timer_time_display"
+        android:layout_width="0dip"
         android:layout_height="match_parent"
-        android:baselineAligned="false"
-        android:orientation="horizontal">
+        android:layout_weight="3"
+        android:layout_marginBottom="@dimen/footer_button_size"
+        android:layoutDirection="ltr"
+        android:gravity="center"
+        android:orientation="vertical">
 
-        <!-- This nested ltr layout cannot be combined with the parent because
-             in RTL, the Keypad and Timer value should swap. -->
-        <LinearLayout
-            android:id="@+id/timer_time_display"
-            android:layout_width="0dip"
-            android:layout_height="match_parent"
-            android:layout_weight="3"
-            android:layout_marginBottom="@dimen/footer_button_size"
-            android:layoutDirection="ltr"
-            android:gravity="center"
-            android:orientation="vertical">
+        <com.android.deskclock.timer.TimerView
+            android:id="@+id/timer_time_text"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:gravity="center_vertical">
 
-            <com.android.deskclock.timer.TimerView
-                android:id="@+id/timer_time_text"
+            <include layout="@layout/timer_h_mm_ss_view" />
+
+            <ImageButton
+                android:id="@+id/delete"
                 android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:gravity="center_vertical">
+                android:layout_height="match_parent"
+                android:layout_gravity="center"
+                android:layout_marginStart="@dimen/timer_setup_delete_start_margin"
+                android:padding="@dimen/timer_setup_delete_padding"
+                android:contentDescription="@string/timer_delete"
+                android:scaleType="center"
+                android:src="@drawable/ic_backspace" />
 
-                <include layout="@layout/timer_h_mm_ss_view" />
+        </com.android.deskclock.timer.TimerView>
 
-                <ImageButton
-                    android:id="@+id/delete"
-                    android:layout_width="wrap_content"
-                    android:layout_height="match_parent"
-                    android:layout_gravity="center"
-                    android:layout_marginStart="@dimen/timer_setup_delete_start_margin"
-                    android:padding="@dimen/timer_setup_delete_padding"
-                    android:contentDescription="@string/timer_delete"
-                    android:scaleType="center"
-                    android:src="@drawable/ic_backspace" />
-
-            </com.android.deskclock.timer.TimerView>
-
-            <View
-                android:id="@+id/divider"
-                android:layout_width="match_parent"
-                android:layout_height="1dip"
-                android:layout_marginBottom="8dip"
-                android:background="@color/dialog_gray" />
-
-        </LinearLayout>
-
-        <LinearLayout
-            android:layout_width="0dip"
-            android:layout_height="match_parent"
-            android:layout_weight="2"
-            android:orientation="vertical" >
-
-            <include
-                android:id="@+id/first"
-                layout="@layout/three_keys_view" />
-            <include
-                android:id="@+id/second"
-                layout="@layout/three_keys_view" />
-            <include
-                android:id="@+id/third"
-                layout="@layout/three_keys_view" />
-            <include
-                android:id="@+id/fourth"
-                layout="@layout/three_keys_view" />
-
-        </LinearLayout>
+        <View
+            android:id="@+id/divider"
+            android:layout_width="match_parent"
+            android:layout_height="1dip"
+            android:layout_marginBottom="8dip"
+            android:background="@color/dialog_gray" />
 
     </LinearLayout>
 
-    <include layout="@layout/timer_setup_buttons" />
+    <LinearLayout
+        android:layout_width="0dip"
+        android:layout_height="match_parent"
+        android:layout_weight="2"
+        android:orientation="vertical">
 
-</FrameLayout>
\ No newline at end of file
+        <include
+            android:id="@+id/first"
+            layout="@layout/three_keys_view" />
+        <include
+            android:id="@+id/second"
+            layout="@layout/three_keys_view" />
+        <include
+            android:id="@+id/third"
+            layout="@layout/three_keys_view" />
+        <include
+            android:id="@+id/fourth"
+            layout="@layout/three_keys_view" />
+
+    </LinearLayout>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/res/layout-land/world_clock_item.xml b/res/layout-land/world_clock_item.xml
index c2a7a6a..ac56a79 100644
--- a/res/layout-land/world_clock_item.xml
+++ b/res/layout-land/world_clock_item.xml
@@ -62,7 +62,6 @@
             android:id="@+id/city_name_layout"
             android:layout_height="wrap_content"
             android:layout_width="wrap_content"
-            android:layout_marginLeft="@dimen/label_margin_small"
             android:layout_marginStart="@dimen/label_margin_small"
             android:gravity="center" >
 
@@ -72,4 +71,4 @@
 
     </LinearLayout>
 
-</FrameLayout>
\ No newline at end of file
+</FrameLayout>
diff --git a/res/layout/alarm_muted_button.xml b/res/layout/alarm_muted_button.xml
new file mode 100644
index 0000000..e9f4aa2
--- /dev/null
+++ b/res/layout/alarm_muted_button.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 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.
+-->
+
+<Button
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/alarm_muted_button"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:minHeight="@dimen/tall_row_height"
+    android:background="?attr/selectableItemBackground"
+    android:drawablePadding="@dimen/alarm_horizontal_padding"
+    android:drawableStart="@drawable/ic_alarm_off_24dp"
+    android:gravity="start|center_vertical"
+    android:paddingEnd="@dimen/icon_margin"
+    android:paddingStart="@dimen/icon_margin"
+    android:textAllCaps="false"
+    android:textColor="@color/clock_gray"
+    android:textSize="@dimen/alarm_text_font_size" />
\ No newline at end of file
diff --git a/res/layout/alarm_time_collapsed.xml b/res/layout/alarm_time_collapsed.xml
index 516cff3..736eaa1 100644
--- a/res/layout/alarm_time_collapsed.xml
+++ b/res/layout/alarm_time_collapsed.xml
@@ -31,15 +31,6 @@
         android:layout_height="wrap_content"
         android:layout_gravity="bottom">
 
-        <View
-            android:id="@+id/hairline"
-            android:layout_width="match_parent"
-            android:layout_height="@dimen/hairline_height"
-            android:layout_gravity="bottom"
-            android:layout_marginEnd="@dimen/icon_margin"
-            android:layout_marginStart="@dimen/icon_margin"
-            android:background="@color/hairline" />
-
         <com.android.deskclock.widget.EllipsizeLayout
             android:layout_width="match_parent"
             android:layout_height="match_parent"
@@ -52,8 +43,8 @@
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:ellipsize="none"
-                android:paddingStart="@dimen/icon_margin"
                 android:paddingEnd="@dimen/icon_margin"
+                android:paddingStart="@dimen/icon_margin"
                 android:singleLine="true"
                 android:textColor="@color/clock_gray"
                 android:textSize="@dimen/alarm_text_font_size" />
@@ -64,6 +55,8 @@
                 android:layout_height="wrap_content"
                 android:ellipsize="end"
                 android:focusable="true"
+                android:paddingEnd="@dimen/icon_margin"
+                android:paddingStart="@dimen/icon_margin"
                 android:singleLine="true"
                 android:textColor="@color/clock_white"
                 android:textSize="@dimen/alarm_text_font_size"
@@ -75,6 +68,8 @@
                 android:layout_height="wrap_content"
                 android:ellipsize="end"
                 android:focusable="true"
+                android:paddingEnd="@dimen/icon_margin"
+                android:paddingStart="@dimen/icon_margin"
                 android:singleLine="true"
                 android:textColor="@color/clock_white"
                 android:textSize="@dimen/alarm_text_font_size"
@@ -87,14 +82,23 @@
             android:layout_width="@dimen/touch_target_min_size"
             android:layout_height="@dimen/touch_target_min_size"
             android:layout_gravity="center_vertical|end"
+            android:background="?attr/selectableItemBackground"
             android:contentDescription="@string/expand_alarm"
             android:scaleType="center"
             android:src="@drawable/ic_expand_down" />
 
     </FrameLayout>
 
-    <include
-        android:id="@+id/preemptive_dismiss_container"
-        layout="@layout/preemptive_dismiss" />
+    <include layout="@layout/alarm_muted_button" />
+    <include layout="@layout/preemptive_dismiss" />
+
+    <View
+        android:id="@+id/hairline"
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/hairline_height"
+        android:layout_gravity="bottom"
+        android:layout_marginEnd="@dimen/icon_margin"
+        android:layout_marginStart="@dimen/icon_margin"
+        android:background="@color/hairline" />
 
 </LinearLayout>
diff --git a/res/layout/alarm_time_expanded.xml b/res/layout/alarm_time_expanded.xml
index 38d24de..35e8f6a 100644
--- a/res/layout/alarm_time_expanded.xml
+++ b/res/layout/alarm_time_expanded.xml
@@ -87,6 +87,7 @@
                     android:layout_alignParentEnd="true"
                     android:layout_centerVertical="true"
                     android:includeFontPadding="false"
+                    android:paddingEnd="12dp"
                     android:paddingStart="@dimen/alarm_horizontal_padding"
                     android:text="@string/alarm_vibrate"
                     android:textColor="@color/white"
@@ -94,6 +95,8 @@
 
             </LinearLayout>
 
+            <include layout="@layout/alarm_muted_button" />
+
             <TextView
                 android:id="@+id/edit_label"
                 android:layout_width="match_parent"
@@ -115,9 +118,7 @@
                 android:layout_marginStart="@dimen/hairline_side_padding"
                 android:background="@color/hairline" />
 
-            <include
-                android:id="@+id/preemptive_dismiss_container"
-                layout="@layout/preemptive_dismiss" />
+            <include layout="@layout/preemptive_dismiss" />
 
         </LinearLayout>
 
@@ -127,7 +128,7 @@
             android:layout_height="wrap_content"
             android:layout_gravity="bottom|start">
 
-            <Button
+            <ImageButton
                 android:id="@+id/delete"
                 android:layout_width="@dimen/touch_target_min_size"
                 android:layout_height="@dimen/tall_row_height"
@@ -136,16 +137,15 @@
                 android:layout_marginBottom="@dimen/alarm_clock_vertical_margin"
                 android:background="?attr/selectableItemBackground"
                 android:contentDescription="@string/delete_alarm"
-                android:drawableStart="@drawable/ic_delete_small"
-                android:drawablePadding="@dimen/alarm_horizontal_padding"
-                android:gravity="start|center_vertical"
-                android:scaleType="fitStart" />
+                android:scaleType="center"
+                android:src="@drawable/ic_delete_small"/>
 
             <ImageButton
                 android:id="@+id/arrow"
                 android:layout_width="@dimen/touch_target_min_size"
                 android:layout_height="@dimen/touch_target_min_size"
                 android:layout_gravity="center_vertical|end"
+                android:background="?attr/selectableItemBackground"
                 android:contentDescription="@string/collapse_alarm"
                 android:rotation="@integer/chevron_rotate_180"
                 android:scaleType="center"
diff --git a/res/layout/alarm_volume_preference.xml b/res/layout/alarm_volume_preference.xml
new file mode 100644
index 0000000..be2ae80
--- /dev/null
+++ b/res/layout/alarm_volume_preference.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2015 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
+  -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:background="?attr/selectableItemBackground"
+    android:clipToPadding="false"
+    android:focusable="true"
+    android:gravity="center_vertical"
+    android:minHeight="?attr/listPreferredItemHeightSmall"
+    android:orientation="vertical"
+    android:paddingBottom="16dp"
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+    android:paddingTop="16dp">
+
+    <TextView
+        android:id="@android:id/title"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:ellipsize="marquee"
+        android:singleLine="true"
+        android:textAppearance="?android:attr/textAppearanceListItem"/>
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:paddingTop="16dp">
+
+        <ImageView
+            android:id="@+id/alarm_icon"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"/>
+
+        <SeekBar
+            android:id="@+id/alarm_volume_slider"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_vertical"
+            android:layout_weight="1"/>
+
+    </LinearLayout>
+
+</LinearLayout>
diff --git a/res/layout/city_list_item.xml b/res/layout/city_list_item.xml
index a5a6ea8..c0c48a6 100644
--- a/res/layout/city_list_item.xml
+++ b/res/layout/city_list_item.xml
@@ -49,6 +49,6 @@
         android:id="@+id/city_time"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_marginRight="8dip"
+        android:layout_marginEnd="8dip"
         android:textAppearance="@style/SecondaryLabelTextAppearance" />
 </LinearLayout>
diff --git a/res/layout/desk_clock_saver.xml b/res/layout/desk_clock_saver.xml
index 7d3f2f7..0df842d 100644
--- a/res/layout/desk_clock_saver.xml
+++ b/res/layout/desk_clock_saver.xml
@@ -15,8 +15,8 @@
 -->
 
 <!-- Special "screen saver mode" with just the time/date on black. -->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:dc="http://schemas.android.com/apk/res-auto"
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:background="@color/black"
@@ -56,10 +56,7 @@
             android:layout_marginBottom="@dimen/bottom_text_spacing_analog"
             android:dial="@drawable/clock_analog_dial_mipmap"
             android:hand_hour="@drawable/clock_analog_hour_mipmap"
-            android:hand_minute="@drawable/clock_analog_minute_mipmap"
-            dc:jewelColor="@color/clock_white"
-            dc:jewelOffset="23dp"
-            dc:jewelRadius="5dp" />
+            android:hand_minute="@drawable/clock_analog_minute_mipmap" />
 
         <LinearLayout
             android:layout_width="wrap_content"
@@ -82,7 +79,6 @@
                 style="@style/label"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
-                android:layout_marginLeft="8dip"
                 android:layout_marginStart="8dip"
                 android:drawablePadding="2dip"
                 android:drawableStart="@drawable/ic_alarm_small"
diff --git a/res/layout/digital_widget_time.xml b/res/layout/digital_widget_time.xml
index e0250bf..bc15726 100644
--- a/res/layout/digital_widget_time.xml
+++ b/res/layout/digital_widget_time.xml
@@ -14,35 +14,36 @@
      limitations under the License.
 -->
 
-<!-- digital clock for the digital widget -->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:orientation="vertical"
     android:layout_gravity="center"
-    android:baselineAligned="false"
     android:background="?android:attr/selectableItemBackground"
-    >
+    android:baselineAligned="false"
+    android:orientation="vertical">
 
     <TextClock
-        android:id="@+id/the_clock"
-        android:format12Hour="@string/main_widget_12_hours_format"
-        android:format24Hour="@string/clock_24_hours_format"
+        android:id="@+id/clock"
+        style="@style/widget_big_thin"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:singleLine="true"
-        style="@style/widget_big_thin"
-        android:textColor="@color/clock_white"
         android:layout_gravity="center_horizontal|top"
-        android:baselineAligned="true"
         android:layout_marginBottom="@dimen/bottom_text_spacing_digital"
-    />
+        android:baselineAligned="true"
+        android:format12Hour="@string/main_widget_12_hours_format"
+        android:format24Hour="@string/clock_24_hours_format"
+        android:singleLine="true"
+        android:textColor="@color/clock_white" />
+
     <LinearLayout
+        android:id="@+id/date_and_alarm"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:id="@+id/date_and_alarm"
         android:gravity="center">
-        <TextClock android:id="@+id/date"
+
+        <TextClock
+            android:id="@+id/date"
             style="@style/widget_label"
             android:layout_width="wrap_content"
             android:layout_height="match_parent"
@@ -50,21 +51,21 @@
             android:format12Hour=""
             android:format24Hour=""
             android:gravity="center"
-            android:textColor="@color/clock_white"
-            />
+            android:textColor="@color/clock_white" />
+
         <TextView
             android:id="@+id/nextAlarm"
             style="@style/widget_label"
             android:layout_width="wrap_content"
             android:layout_height="match_parent"
             android:layout_gravity="center"
-            android:layout_marginStart="6dp"
-            android:drawablePadding="6dp"
+            android:layout_marginStart="@dimen/alarm_icon_padding"
+            android:drawablePadding="@dimen/alarm_icon_padding"
             android:ellipsize="end"
             android:gravity="center"
             android:singleLine="true"
             android:textColor="@color/clock_gray"
-            android:visibility="gone"
-            />
+            android:visibility="gone" />
     </LinearLayout>
-</LinearLayout>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/res/layout/main_clock_frame.xml b/res/layout/main_clock_frame.xml
index 240f21a..60e4191 100644
--- a/res/layout/main_clock_frame.xml
+++ b/res/layout/main_clock_frame.xml
@@ -70,9 +70,8 @@
             android:id="@+id/nextAlarm"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:layout_marginLeft="8dip"
-            android:layout_marginStart="8dip"
-            android:drawablePadding="2dip"
+            android:layout_marginStart="@dimen/alarm_icon_padding"
+            android:drawablePadding="@dimen/alarm_icon_padding"
             android:drawableStart="@drawable/ic_alarm_small"
             android:textAppearance="@style/SecondaryLabelTextAppearance" />
     </LinearLayout>
diff --git a/res/layout/preemptive_dismiss.xml b/res/layout/preemptive_dismiss.xml
index 2a32d7a..91dfdd8 100644
--- a/res/layout/preemptive_dismiss.xml
+++ b/res/layout/preemptive_dismiss.xml
@@ -14,7 +14,7 @@
      limitations under the License.
 -->
 <LinearLayout
-    android:id="@+id/preemptive_dismiss"
+    android:id="@+id/preemptive_dismiss_container"
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
diff --git a/res/layout/settings.xml b/res/layout/settings.xml
index f587342..8de8357 100644
--- a/res/layout/settings.xml
+++ b/res/layout/settings.xml
@@ -15,14 +15,7 @@
 -->
 
 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/main"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:fitsSystemWindows="true">
-
-    <fragment
-        android:id="@+id/main"
-        android:name="com.android.deskclock.settings.SettingsActivity$PrefsFragment"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent" />
-
-</FrameLayout>
+    android:fitsSystemWindows="true" />
diff --git a/res/layout/time_setup_view.xml b/res/layout/time_setup_view.xml
index 7d7bb2c..881b82b 100644
--- a/res/layout/time_setup_view.xml
+++ b/res/layout/time_setup_view.xml
@@ -13,10 +13,12 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:orientation="vertical">
+    android:orientation="vertical"
+    android:paddingBottom="@dimen/fab_height">
 
     <com.android.deskclock.timer.TimerView
         android:id="@+id/timer_time_text"
@@ -66,6 +68,4 @@
         android:id="@+id/fourth"
         layout="@layout/three_keys_view" />
 
-    <include layout="@layout/timer_setup_buttons" />
-
 </LinearLayout>
diff --git a/res/layout/timer_item.xml b/res/layout/timer_item.xml
index 1d97ecb..6c0a277 100644
--- a/res/layout/timer_item.xml
+++ b/res/layout/timer_item.xml
@@ -50,7 +50,7 @@
             android:ellipsize="end"
             android:gravity="center"
             android:hint="@string/label"
-            android:singleLine="true"
+            android:maxLines="1"
             android:src="@drawable/ic_label"
             android:textAppearance="@style/SecondaryLabelTextAppearance"/>
 
diff --git a/res/layout/timer_setup_buttons.xml b/res/layout/timer_setup_buttons.xml
deleted file mode 100644
index f0132f2..0000000
--- a/res/layout/timer_setup_buttons.xml
+++ /dev/null
@@ -1,67 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 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.
--->
-
-<LinearLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:layout_gravity="bottom"
-    android:orientation="horizontal">
-
-    <FrameLayout
-        android:layout_width="0dp"
-        android:layout_height="match_parent"
-        android:layout_gravity="start|center_vertical"
-        android:layout_weight="1">
-
-        <ImageButton
-            android:id="@+id/timer_cancel"
-            android:layout_width="@dimen/design_fab_size_normal"
-            android:layout_height="@dimen/design_fab_size_normal"
-            android:layout_gravity="center"
-            android:contentDescription="@string/timer_cancel"
-            android:scaleType="center"
-            android:src="@drawable/ic_cancel" />
-
-    </FrameLayout>
-
-    <FrameLayout
-        android:layout_width="0dp"
-        android:layout_height="match_parent"
-        android:layout_gravity="start|center_vertical"
-        android:layout_weight="1">
-
-        <android.support.design.widget.FloatingActionButton
-            android:id="@+id/timer_create"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_gravity="center"
-            android:layout_margin="@dimen/fab_margin"
-            android:contentDescription="@string/timer_start"
-            android:src="@drawable/ic_start_white_24dp"
-            android:visibility="invisible"
-            app:borderWidth="0dp"
-            app:elevation="@dimen/fab_elevation" />
-
-    </FrameLayout>
-
-    <Space
-        android:layout_width="0dp"
-        android:layout_height="match_parent"
-        android:layout_weight="1" />
-
-</LinearLayout>
diff --git a/res/layout/world_clock_label.xml b/res/layout/world_clock_label.xml
index 360cad1..9111f61 100644
--- a/res/layout/world_clock_label.xml
+++ b/res/layout/world_clock_label.xml
@@ -27,7 +27,6 @@
         android:id="@+id/city_day"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_marginLeft="@dimen/style_label_space"
         android:layout_marginStart="@dimen/style_label_space"
         android:ellipsize="none"
         android:singleLine="true"
diff --git a/res/layout/world_clock_remote_list_item.xml b/res/layout/world_clock_remote_list_item.xml
index 6967715..5f585e4 100644
--- a/res/layout/world_clock_remote_list_item.xml
+++ b/res/layout/world_clock_remote_list_item.xml
@@ -61,7 +61,6 @@
                     style="@style/widget_label"
                     android:layout_width="wrap_content"
                     android:layout_height="wrap_content"
-                    android:layout_marginLeft="@dimen/style_label_space"
                     android:layout_marginStart="@dimen/style_label_space"
                     android:ellipsize="none"
                     android:singleLine="true"
@@ -107,7 +106,6 @@
                     style="@style/widget_label"
                     android:layout_width="wrap_content"
                     android:layout_height="wrap_content"
-                    android:layout_marginLeft="@dimen/style_label_space"
                     android:layout_marginStart="@dimen/style_label_space"
                     android:ellipsize="none"
                     android:singleLine="true"
@@ -120,4 +118,4 @@
         android:layout_width="match_parent"
         android:layout_height="@dimen/digital_widget_city_margin_bottom">
     </FrameLayout>
-</LinearLayout>
\ No newline at end of file
+</LinearLayout>
diff --git a/res/menu/desk_clock_menu.xml b/res/menu/desk_clock_menu.xml
index 4858714..2bf66c6 100644
--- a/res/menu/desk_clock_menu.xml
+++ b/res/menu/desk_clock_menu.xml
@@ -38,9 +38,5 @@
             android:id="@+id/menu_item_settings"
             android:icon="@android:drawable/ic_menu_preferences"
             android:title="@string/menu_item_settings"/>
-        <item
-            android:id="@+id/menu_item_help"
-            android:icon="@android:drawable/ic_menu_preferences"
-            android:title="@string/menu_item_help"/>
     </group>
-</menu>
\ No newline at end of file
+</menu>
diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml
index df8f2f8..788607e 100644
--- a/res/values-af/strings.xml
+++ b/res/values-af/strings.xml
@@ -35,6 +35,7 @@
     <string name="alarm_vibrate" msgid="3476686921490362230">"Vibreer"</string>
     <string name="alarm_repeat" msgid="7242985466344233206">"Herhaal"</string>
     <string name="alert" msgid="6506982899651975645">"Alarm se luitoon"</string>
+    <string name="default_alarm_ringtone_title" msgid="2916376768509623643">"Verstekwekkerklank"</string>
     <string name="default_timer_ringtone_title" msgid="1370799536406862317">"Afteller het verstryk"</string>
     <string name="silent_timer_ringtone_title" msgid="7588723137323327647">"Stil"</string>
     <string name="ringtone" msgid="9110746249688559579">"Luitoon"</string>
@@ -233,7 +234,7 @@
     <string name="cities_activity_title" msgid="8552462751129256730">"Stede"</string>
     <string name="clock_settings" msgid="8317286807280600391">"Horlosie"</string>
     <string name="clock_style" msgid="2265011060429742344">"Styl"</string>
-    <string name="open_date_settings" msgid="5915966847691221307">"Verander datum en tyd"</string>
+    <string name="open_date_settings" msgid="7712226973337806152">"Verander datum en tyd"</string>
   <string-array name="clock_style_entries">
     <item msgid="917900462224167608">"Analoog"</item>
     <item msgid="8483930821046925592">"Digitaal"</item>
diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml
index 5bf749f..9c34bc0 100644
--- a/res/values-am/strings.xml
+++ b/res/values-am/strings.xml
@@ -35,6 +35,7 @@
     <string name="alarm_vibrate" msgid="3476686921490362230">"ንዘር"</string>
     <string name="alarm_repeat" msgid="7242985466344233206">"ድገም"</string>
     <string name="alert" msgid="6506982899651975645">"የማንቂያ ደውል ቅላጼ"</string>
+    <string name="default_alarm_ringtone_title" msgid="2916376768509623643">"ነባሪ የማንቂያ ድምጽ"</string>
     <string name="default_timer_ringtone_title" msgid="1370799536406862317">"ሰዓት ቆጣሪው ጊዜው አልፏል"</string>
     <string name="silent_timer_ringtone_title" msgid="7588723137323327647">"ፀጥ ያለ"</string>
     <string name="ringtone" msgid="9110746249688559579">"የደወል ቅላጼ"</string>
@@ -233,7 +234,7 @@
     <string name="cities_activity_title" msgid="8552462751129256730">"ከተማዎች"</string>
     <string name="clock_settings" msgid="8317286807280600391">"ሰዓት"</string>
     <string name="clock_style" msgid="2265011060429742344">"ቅጥ"</string>
-    <string name="open_date_settings" msgid="5915966847691221307">"ቀን እና ሰዓት ለውጥ"</string>
+    <string name="open_date_settings" msgid="7712226973337806152">"ቀን እና ሰዓት ለውጥ"</string>
   <string-array name="clock_style_entries">
     <item msgid="917900462224167608">"አናሎግ"</item>
     <item msgid="8483930821046925592">"ዲጂታል"</item>
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index c70e8bd..aa2df8e 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -35,6 +35,7 @@
     <string name="alarm_vibrate" msgid="3476686921490362230">"اهتزاز"</string>
     <string name="alarm_repeat" msgid="7242985466344233206">"تكرار"</string>
     <string name="alert" msgid="6506982899651975645">"نغمة رنين المنبه"</string>
+    <string name="default_alarm_ringtone_title" msgid="2916376768509623643">"صوت التنبيه الافتراضي"</string>
     <string name="default_timer_ringtone_title" msgid="1370799536406862317">"انتهت صلاحية الموقِّت"</string>
     <string name="silent_timer_ringtone_title" msgid="7588723137323327647">"صامت"</string>
     <string name="ringtone" msgid="9110746249688559579">"نغمة الرنين"</string>
@@ -277,7 +278,7 @@
     <string name="cities_activity_title" msgid="8552462751129256730">"مدن"</string>
     <string name="clock_settings" msgid="8317286807280600391">"الساعة"</string>
     <string name="clock_style" msgid="2265011060429742344">"النمط"</string>
-    <string name="open_date_settings" msgid="5915966847691221307">"تغيير التاريخ والوقت"</string>
+    <string name="open_date_settings" msgid="7712226973337806152">"تغيير التاريخ والوقت"</string>
   <string-array name="clock_style_entries">
     <item msgid="917900462224167608">"تناظري"</item>
     <item msgid="8483930821046925592">"رقمي"</item>
diff --git a/res/values-az-rAZ/strings.xml b/res/values-az-rAZ/strings.xml
index 5352482..831d807 100644
--- a/res/values-az-rAZ/strings.xml
+++ b/res/values-az-rAZ/strings.xml
@@ -35,6 +35,7 @@
     <string name="alarm_vibrate" msgid="3476686921490362230">"Vibrasiya etdir"</string>
     <string name="alarm_repeat" msgid="7242985466344233206">"Təkrarla"</string>
     <string name="alert" msgid="6506982899651975645">"Siqnalın Zəng səsi"</string>
+    <string name="default_alarm_ringtone_title" msgid="2916376768509623643">"Defolt siqnal səsi"</string>
     <string name="default_timer_ringtone_title" msgid="1370799536406862317">"Taymerin Vaxtı Bitdi"</string>
     <string name="silent_timer_ringtone_title" msgid="7588723137323327647">"Səssiz"</string>
     <string name="ringtone" msgid="9110746249688559579">"Zəng səsi"</string>
@@ -234,7 +235,7 @@
     <string name="cities_activity_title" msgid="8552462751129256730">"Şəhərlər"</string>
     <string name="clock_settings" msgid="8317286807280600391">"Saat"</string>
     <string name="clock_style" msgid="2265011060429742344">"Üslub"</string>
-    <string name="open_date_settings" msgid="5915966847691221307">"Tarix və vaxtı dəyişin"</string>
+    <string name="open_date_settings" msgid="7712226973337806152">"Tarix və vaxtı dəyişin"</string>
   <string-array name="clock_style_entries">
     <item msgid="917900462224167608">"Analoq"</item>
     <item msgid="8483930821046925592">"Rəqəmsal"</item>
diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml
index 2f879ed..9326ee6 100644
--- a/res/values-bg/strings.xml
+++ b/res/values-bg/strings.xml
@@ -35,6 +35,7 @@
     <string name="alarm_vibrate" msgid="3476686921490362230">"Вибриране"</string>
     <string name="alarm_repeat" msgid="7242985466344233206">"Да се повтаря"</string>
     <string name="alert" msgid="6506982899651975645">"Мелодия на будилника"</string>
+    <string name="default_alarm_ringtone_title" msgid="2916376768509623643">"Стандартен звук за будилника"</string>
     <string name="default_timer_ringtone_title" msgid="1370799536406862317">"Изтичане на времето на таймера"</string>
     <string name="silent_timer_ringtone_title" msgid="7588723137323327647">"Тих режим"</string>
     <string name="ringtone" msgid="9110746249688559579">"Мелодия"</string>
@@ -233,7 +234,7 @@
     <string name="cities_activity_title" msgid="8552462751129256730">"Градове"</string>
     <string name="clock_settings" msgid="8317286807280600391">"Часовник"</string>
     <string name="clock_style" msgid="2265011060429742344">"Стил"</string>
-    <string name="open_date_settings" msgid="5915966847691221307">"Промяна на датата и часа"</string>
+    <string name="open_date_settings" msgid="7712226973337806152">"Промяна на датата и часа"</string>
   <string-array name="clock_style_entries">
     <item msgid="917900462224167608">"Аналогов"</item>
     <item msgid="8483930821046925592">"Цифров"</item>
diff --git a/res/values-bn-rBD/strings.xml b/res/values-bn-rBD/strings.xml
index 1ae0afa..2d03b73 100644
--- a/res/values-bn-rBD/strings.xml
+++ b/res/values-bn-rBD/strings.xml
@@ -35,6 +35,7 @@
     <string name="alarm_vibrate" msgid="3476686921490362230">"কম্পন"</string>
     <string name="alarm_repeat" msgid="7242985466344233206">"পুনরাবৃত্তি করুন"</string>
     <string name="alert" msgid="6506982899651975645">"অ্যালার্ম রিংটোন"</string>
+    <string name="default_alarm_ringtone_title" msgid="2916376768509623643">"ডিফল্ট অ্যালার্মের শব্দ"</string>
     <string name="default_timer_ringtone_title" msgid="1370799536406862317">"টাইমারের মেয়াদ শেষ হয়ে গেছে"</string>
     <string name="silent_timer_ringtone_title" msgid="7588723137323327647">"নীরব"</string>
     <string name="ringtone" msgid="9110746249688559579">"রিংটোন"</string>
@@ -234,7 +235,7 @@
     <string name="cities_activity_title" msgid="8552462751129256730">"শহর"</string>
     <string name="clock_settings" msgid="8317286807280600391">"ঘড়ি"</string>
     <string name="clock_style" msgid="2265011060429742344">"শৈলী"</string>
-    <string name="open_date_settings" msgid="5915966847691221307">"তারিখ এবং সময় পরিবর্তন করুন"</string>
+    <string name="open_date_settings" msgid="7712226973337806152">"তারিখ এবং সময় পরিবর্তন করুন"</string>
   <string-array name="clock_style_entries">
     <item msgid="917900462224167608">"অ্যানালগ"</item>
     <item msgid="8483930821046925592">"ডিজিটাল"</item>
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index 88e5611..b732e13 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -35,6 +35,7 @@
     <string name="alarm_vibrate" msgid="3476686921490362230">"Vibra"</string>
     <string name="alarm_repeat" msgid="7242985466344233206">"Repeteix"</string>
     <string name="alert" msgid="6506982899651975645">"So de l\'alarma"</string>
+    <string name="default_alarm_ringtone_title" msgid="2916376768509623643">"So d\'alarma predeterminat"</string>
     <string name="default_timer_ringtone_title" msgid="1370799536406862317">"Temporitzador finalitzat"</string>
     <string name="silent_timer_ringtone_title" msgid="7588723137323327647">"Silenci"</string>
     <string name="ringtone" msgid="9110746249688559579">"So"</string>
@@ -233,7 +234,7 @@
     <string name="cities_activity_title" msgid="8552462751129256730">"Ciutats"</string>
     <string name="clock_settings" msgid="8317286807280600391">"Rellotge"</string>
     <string name="clock_style" msgid="2265011060429742344">"Estil"</string>
-    <string name="open_date_settings" msgid="5915966847691221307">"Canvia la data i l\'hora"</string>
+    <string name="open_date_settings" msgid="7712226973337806152">"Canvia la data i l\'hora"</string>
   <string-array name="clock_style_entries">
     <item msgid="917900462224167608">"Analògic"</item>
     <item msgid="8483930821046925592">"Digital"</item>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index 9b7d80d..81bbb32 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -35,6 +35,7 @@
     <string name="alarm_vibrate" msgid="3476686921490362230">"Vibrace"</string>
     <string name="alarm_repeat" msgid="7242985466344233206">"Opakovat"</string>
     <string name="alert" msgid="6506982899651975645">"Vyzváněcí tón budíku"</string>
+    <string name="default_alarm_ringtone_title" msgid="2916376768509623643">"Výchozí zvuk budíku"</string>
     <string name="default_timer_ringtone_title" msgid="1370799536406862317">"Časovač vypršel"</string>
     <string name="silent_timer_ringtone_title" msgid="7588723137323327647">"Tiché"</string>
     <string name="ringtone" msgid="9110746249688559579">"Vyzváněcí tón"</string>
@@ -255,7 +256,7 @@
     <string name="cities_activity_title" msgid="8552462751129256730">"Města"</string>
     <string name="clock_settings" msgid="8317286807280600391">"Hodiny"</string>
     <string name="clock_style" msgid="2265011060429742344">"Styl"</string>
-    <string name="open_date_settings" msgid="5915966847691221307">"Změnit datum a čas"</string>
+    <string name="open_date_settings" msgid="7712226973337806152">"Změnit datum a čas"</string>
   <string-array name="clock_style_entries">
     <item msgid="917900462224167608">"Analogové"</item>
     <item msgid="8483930821046925592">"Digitální"</item>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index 7d26fd0..3d85409 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -35,6 +35,7 @@
     <string name="alarm_vibrate" msgid="3476686921490362230">"Vibration"</string>
     <string name="alarm_repeat" msgid="7242985466344233206">"Gentag"</string>
     <string name="alert" msgid="6506982899651975645">"Ringetone for alarm"</string>
+    <string name="default_alarm_ringtone_title" msgid="2916376768509623643">"Standardlyd for alarmer"</string>
     <string name="default_timer_ringtone_title" msgid="1370799536406862317">"Timeren er udløbet"</string>
     <string name="silent_timer_ringtone_title" msgid="7588723137323327647">"Lydløs"</string>
     <string name="ringtone" msgid="9110746249688559579">"Ringetone"</string>
@@ -233,7 +234,7 @@
     <string name="cities_activity_title" msgid="8552462751129256730">"Byer"</string>
     <string name="clock_settings" msgid="8317286807280600391">"Ur"</string>
     <string name="clock_style" msgid="2265011060429742344">"Urtype"</string>
-    <string name="open_date_settings" msgid="5915966847691221307">"Skift dato &amp; klokkeslæt"</string>
+    <string name="open_date_settings" msgid="7712226973337806152">"Rediger dato og tid"</string>
   <string-array name="clock_style_entries">
     <item msgid="917900462224167608">"Analog"</item>
     <item msgid="8483930821046925592">"Digital"</item>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index b2687a3..e05c846 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -35,6 +35,7 @@
     <string name="alarm_vibrate" msgid="3476686921490362230">"Vibrieren"</string>
     <string name="alarm_repeat" msgid="7242985466344233206">"Wiederholen"</string>
     <string name="alert" msgid="6506982899651975645">"Weckton"</string>
+    <string name="default_alarm_ringtone_title" msgid="2916376768509623643">"Standard-Weckerton"</string>
     <string name="default_timer_ringtone_title" msgid="1370799536406862317">"Timer abgelaufen"</string>
     <string name="silent_timer_ringtone_title" msgid="7588723137323327647">"Lautlos"</string>
     <string name="ringtone" msgid="9110746249688559579">"Klingelton"</string>
@@ -218,22 +219,22 @@
     <string name="timer_pause" msgid="3748323712728398743">"Pausieren"</string>
     <string name="timer_reset_all" msgid="7530633132757866087">"Alle Timer zurücksetzen"</string>
   <string-array name="sw_share_strings">
-    <item msgid="842841032273927988">"Sie sind teuflisch schnell."</item>
-    <item msgid="6332879039890727169">"Ernten Sie die Früchte Ihrer Arbeit!"</item>
-    <item msgid="815382761274660130">"Sie sind ja schneller als ein Roboter!"</item>
+    <item msgid="842841032273927988">"Du bist teuflisch schnell."</item>
+    <item msgid="6332879039890727169">"Ernte die Früchte deiner Arbeit!"</item>
+    <item msgid="815382761274660130">"Du bist ja schneller als ein Roboter!"</item>
     <item msgid="7916250650982813737">"Puh!"</item>
     <item msgid="6836603904515182333">"L33t-Zeiten"</item>
     <item msgid="7508085100680861631">"Wahnsinnsgeschwindigkeit"</item>
     <item msgid="5961245252909589573">"Let\'s do the time warp again!"</item>
     <item msgid="5211891900854545940">"Just a jump to the left."</item>
-    <item msgid="9071353477103826053">"Sie haben es ganz schön eilig."</item>
+    <item msgid="9071353477103826053">"Du hast es ganz schön eilig."</item>
     <item msgid="3785193933691117181">"Photonengeschwindigkeit"</item>
   </string-array>
     <string name="home_label" msgid="4436139365695453870">"Zuhause"</string>
     <string name="cities_activity_title" msgid="8552462751129256730">"Städte"</string>
     <string name="clock_settings" msgid="8317286807280600391">"Uhr"</string>
     <string name="clock_style" msgid="2265011060429742344">"Design"</string>
-    <string name="open_date_settings" msgid="5915966847691221307">"Datum &amp; Uhrzeit ändern"</string>
+    <string name="open_date_settings" msgid="7712226973337806152">"Datum &amp; Uhrzeit ändern"</string>
   <string-array name="clock_style_entries">
     <item msgid="917900462224167608">"Analog"</item>
     <item msgid="8483930821046925592">"Digital"</item>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index ba757dd..f8d2145 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -35,6 +35,7 @@
     <string name="alarm_vibrate" msgid="3476686921490362230">"Δόνηση"</string>
     <string name="alarm_repeat" msgid="7242985466344233206">"Επανάληψη"</string>
     <string name="alert" msgid="6506982899651975645">"Ήχος κλήσης ξυπνητηριού"</string>
+    <string name="default_alarm_ringtone_title" msgid="2916376768509623643">"Προεπιλεγμένος ήχος ειδοποίησης"</string>
     <string name="default_timer_ringtone_title" msgid="1370799536406862317">"Η αντίστροφη μέτρηση έληξε"</string>
     <string name="silent_timer_ringtone_title" msgid="7588723137323327647">"Αθόρυβο"</string>
     <string name="ringtone" msgid="9110746249688559579">"Ήχος κλήσης"</string>
@@ -233,7 +234,7 @@
     <string name="cities_activity_title" msgid="8552462751129256730">"Πόλεις"</string>
     <string name="clock_settings" msgid="8317286807280600391">"Ρολόι"</string>
     <string name="clock_style" msgid="2265011060429742344">"Στυλ"</string>
-    <string name="open_date_settings" msgid="5915966847691221307">"Αλλαγή ημερομηνίας και ώρας"</string>
+    <string name="open_date_settings" msgid="7712226973337806152">"Αλλαγή ημερομηνίας και ώρας"</string>
   <string-array name="clock_style_entries">
     <item msgid="917900462224167608">"Αναλογικό"</item>
     <item msgid="8483930821046925592">"Ψηφιακό"</item>
diff --git a/res/values-en-rAU/strings.xml b/res/values-en-rAU/strings.xml
index f6e50f2..21f8b72 100644
--- a/res/values-en-rAU/strings.xml
+++ b/res/values-en-rAU/strings.xml
@@ -35,6 +35,7 @@
     <string name="alarm_vibrate" msgid="3476686921490362230">"Vibrate"</string>
     <string name="alarm_repeat" msgid="7242985466344233206">"Repeat"</string>
     <string name="alert" msgid="6506982899651975645">"Alarm Ringtone"</string>
+    <string name="default_alarm_ringtone_title" msgid="2916376768509623643">"Default alarm sound"</string>
     <string name="default_timer_ringtone_title" msgid="1370799536406862317">"Timer Expired"</string>
     <string name="silent_timer_ringtone_title" msgid="7588723137323327647">"Silent"</string>
     <string name="ringtone" msgid="9110746249688559579">"Ring tone"</string>
@@ -233,7 +234,7 @@
     <string name="cities_activity_title" msgid="8552462751129256730">"Cities"</string>
     <string name="clock_settings" msgid="8317286807280600391">"Clock"</string>
     <string name="clock_style" msgid="2265011060429742344">"Style"</string>
-    <string name="open_date_settings" msgid="5915966847691221307">"Change Date &amp; time"</string>
+    <string name="open_date_settings" msgid="7712226973337806152">"Change date &amp; time"</string>
   <string-array name="clock_style_entries">
     <item msgid="917900462224167608">"Analogue"</item>
     <item msgid="8483930821046925592">"Digital"</item>
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
index f6e50f2..21f8b72 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -35,6 +35,7 @@
     <string name="alarm_vibrate" msgid="3476686921490362230">"Vibrate"</string>
     <string name="alarm_repeat" msgid="7242985466344233206">"Repeat"</string>
     <string name="alert" msgid="6506982899651975645">"Alarm Ringtone"</string>
+    <string name="default_alarm_ringtone_title" msgid="2916376768509623643">"Default alarm sound"</string>
     <string name="default_timer_ringtone_title" msgid="1370799536406862317">"Timer Expired"</string>
     <string name="silent_timer_ringtone_title" msgid="7588723137323327647">"Silent"</string>
     <string name="ringtone" msgid="9110746249688559579">"Ring tone"</string>
@@ -233,7 +234,7 @@
     <string name="cities_activity_title" msgid="8552462751129256730">"Cities"</string>
     <string name="clock_settings" msgid="8317286807280600391">"Clock"</string>
     <string name="clock_style" msgid="2265011060429742344">"Style"</string>
-    <string name="open_date_settings" msgid="5915966847691221307">"Change Date &amp; time"</string>
+    <string name="open_date_settings" msgid="7712226973337806152">"Change date &amp; time"</string>
   <string-array name="clock_style_entries">
     <item msgid="917900462224167608">"Analogue"</item>
     <item msgid="8483930821046925592">"Digital"</item>
diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml
index f6e50f2..21f8b72 100644
--- a/res/values-en-rIN/strings.xml
+++ b/res/values-en-rIN/strings.xml
@@ -35,6 +35,7 @@
     <string name="alarm_vibrate" msgid="3476686921490362230">"Vibrate"</string>
     <string name="alarm_repeat" msgid="7242985466344233206">"Repeat"</string>
     <string name="alert" msgid="6506982899651975645">"Alarm Ringtone"</string>
+    <string name="default_alarm_ringtone_title" msgid="2916376768509623643">"Default alarm sound"</string>
     <string name="default_timer_ringtone_title" msgid="1370799536406862317">"Timer Expired"</string>
     <string name="silent_timer_ringtone_title" msgid="7588723137323327647">"Silent"</string>
     <string name="ringtone" msgid="9110746249688559579">"Ring tone"</string>
@@ -233,7 +234,7 @@
     <string name="cities_activity_title" msgid="8552462751129256730">"Cities"</string>
     <string name="clock_settings" msgid="8317286807280600391">"Clock"</string>
     <string name="clock_style" msgid="2265011060429742344">"Style"</string>
-    <string name="open_date_settings" msgid="5915966847691221307">"Change Date &amp; time"</string>
+    <string name="open_date_settings" msgid="7712226973337806152">"Change date &amp; time"</string>
   <string-array name="clock_style_entries">
     <item msgid="917900462224167608">"Analogue"</item>
     <item msgid="8483930821046925592">"Digital"</item>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index 17fac9e..b445444 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -35,6 +35,7 @@
     <string name="alarm_vibrate" msgid="3476686921490362230">"Vibrar"</string>
     <string name="alarm_repeat" msgid="7242985466344233206">"Repetir"</string>
     <string name="alert" msgid="6506982899651975645">"Tono de alarma"</string>
+    <string name="default_alarm_ringtone_title" msgid="2916376768509623643">"Sonido de alarma predeterminado"</string>
     <string name="default_timer_ringtone_title" msgid="1370799536406862317">"Caducó el temporizador"</string>
     <string name="silent_timer_ringtone_title" msgid="7588723137323327647">"Silencio"</string>
     <string name="ringtone" msgid="9110746249688559579">"Tono"</string>
@@ -233,7 +234,7 @@
     <string name="cities_activity_title" msgid="8552462751129256730">"Ciudades"</string>
     <string name="clock_settings" msgid="8317286807280600391">"Reloj"</string>
     <string name="clock_style" msgid="2265011060429742344">"Estilo"</string>
-    <string name="open_date_settings" msgid="5915966847691221307">"Cambiar fecha y hora"</string>
+    <string name="open_date_settings" msgid="7712226973337806152">"Cambiar fecha y hora"</string>
   <string-array name="clock_style_entries">
     <item msgid="917900462224167608">"Analógico"</item>
     <item msgid="8483930821046925592">"Digital"</item>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index 174b996..fe75fc2 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -35,6 +35,7 @@
     <string name="alarm_vibrate" msgid="3476686921490362230">"Vibrar"</string>
     <string name="alarm_repeat" msgid="7242985466344233206">"Repetir"</string>
     <string name="alert" msgid="6506982899651975645">"Tono de alarma"</string>
+    <string name="default_alarm_ringtone_title" msgid="2916376768509623643">"Sonido de alarma predeterminado"</string>
     <string name="default_timer_ringtone_title" msgid="1370799536406862317">"Temporizador agotado"</string>
     <string name="silent_timer_ringtone_title" msgid="7588723137323327647">"Silencio"</string>
     <string name="ringtone" msgid="9110746249688559579">"Tono de llamada"</string>
@@ -233,7 +234,7 @@
     <string name="cities_activity_title" msgid="8552462751129256730">"Ciudades"</string>
     <string name="clock_settings" msgid="8317286807280600391">"Reloj"</string>
     <string name="clock_style" msgid="2265011060429742344">"Estilo"</string>
-    <string name="open_date_settings" msgid="5915966847691221307">"Cambiar fecha y hora"</string>
+    <string name="open_date_settings" msgid="7712226973337806152">"Cambiar fecha y hora"</string>
   <string-array name="clock_style_entries">
     <item msgid="917900462224167608">"Analógico"</item>
     <item msgid="8483930821046925592">"Digital"</item>
diff --git a/res/values-et-rEE/strings.xml b/res/values-et-rEE/strings.xml
index 5a4b585..bae67f9 100644
--- a/res/values-et-rEE/strings.xml
+++ b/res/values-et-rEE/strings.xml
@@ -35,6 +35,7 @@
     <string name="alarm_vibrate" msgid="3476686921490362230">"Värin"</string>
     <string name="alarm_repeat" msgid="7242985466344233206">"Kordus"</string>
     <string name="alert" msgid="6506982899651975645">"Äratuse helin"</string>
+    <string name="default_alarm_ringtone_title" msgid="2916376768509623643">"Äratuse vaikeheli"</string>
     <string name="default_timer_ringtone_title" msgid="1370799536406862317">"Taimer on aegunud"</string>
     <string name="silent_timer_ringtone_title" msgid="7588723137323327647">"Hääletu"</string>
     <string name="ringtone" msgid="9110746249688559579">"Helin"</string>
@@ -233,7 +234,7 @@
     <string name="cities_activity_title" msgid="8552462751129256730">"Linnad"</string>
     <string name="clock_settings" msgid="8317286807280600391">"Kell"</string>
     <string name="clock_style" msgid="2265011060429742344">"Stiil"</string>
-    <string name="open_date_settings" msgid="5915966847691221307">"Kuupäeva ja kellaaja muutmine"</string>
+    <string name="open_date_settings" msgid="7712226973337806152">"Kuupäeva ja kellaaja muutmine"</string>
   <string-array name="clock_style_entries">
     <item msgid="917900462224167608">"Analoog"</item>
     <item msgid="8483930821046925592">"Digitaalne"</item>
diff --git a/res/values-eu-rES/strings.xml b/res/values-eu-rES/strings.xml
index 3b5073c..1d2c2eb 100644
--- a/res/values-eu-rES/strings.xml
+++ b/res/values-eu-rES/strings.xml
@@ -35,6 +35,7 @@
     <string name="alarm_vibrate" msgid="3476686921490362230">"Egin dar-dar"</string>
     <string name="alarm_repeat" msgid="7242985466344233206">"Errepikapena"</string>
     <string name="alert" msgid="6506982899651975645">"Alarmaren tonua"</string>
+    <string name="default_alarm_ringtone_title" msgid="2916376768509623643">"Alarmaren soinu lehenetsia"</string>
     <string name="default_timer_ringtone_title" msgid="1370799536406862317">"Iraungi egin da tenporizadorea"</string>
     <string name="silent_timer_ringtone_title" msgid="7588723137323327647">"Isilik modua"</string>
     <string name="ringtone" msgid="9110746249688559579">"Tonua"</string>
@@ -234,7 +235,7 @@
     <string name="cities_activity_title" msgid="8552462751129256730">"Hiriak"</string>
     <string name="clock_settings" msgid="8317286807280600391">"Erlojua"</string>
     <string name="clock_style" msgid="2265011060429742344">"Estiloa"</string>
-    <string name="open_date_settings" msgid="5915966847691221307">"Aldatu data eta ordua"</string>
+    <string name="open_date_settings" msgid="7712226973337806152">"Aldatu data eta ordua"</string>
   <string-array name="clock_style_entries">
     <item msgid="917900462224167608">"Analogikoa"</item>
     <item msgid="8483930821046925592">"Digitala"</item>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index 2e7d138..333dd35 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -35,6 +35,7 @@
     <string name="alarm_vibrate" msgid="3476686921490362230">"لرزش"</string>
     <string name="alarm_repeat" msgid="7242985466344233206">"تکرار"</string>
     <string name="alert" msgid="6506982899651975645">"آهنگ زنگ هشدار"</string>
+    <string name="default_alarm_ringtone_title" msgid="2916376768509623643">"آهنگ زنگ پیش‌فرض"</string>
     <string name="default_timer_ringtone_title" msgid="1370799536406862317">"تایمر به پایان رسید"</string>
     <string name="silent_timer_ringtone_title" msgid="7588723137323327647">"بی‌صدا"</string>
     <string name="ringtone" msgid="9110746249688559579">"آهنگ زنگ"</string>
@@ -233,7 +234,7 @@
     <string name="cities_activity_title" msgid="8552462751129256730">"شهرها"</string>
     <string name="clock_settings" msgid="8317286807280600391">"ساعت"</string>
     <string name="clock_style" msgid="2265011060429742344">"سبک"</string>
-    <string name="open_date_settings" msgid="5915966847691221307">"تغییر تاریخ و زمان"</string>
+    <string name="open_date_settings" msgid="7712226973337806152">"تغییر تاریخ و زمان"</string>
   <string-array name="clock_style_entries">
     <item msgid="917900462224167608">"آنالوگ"</item>
     <item msgid="8483930821046925592">"دیجیتال"</item>
diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml
index c53290f..f19be1e 100644
--- a/res/values-fi/strings.xml
+++ b/res/values-fi/strings.xml
@@ -35,6 +35,7 @@
     <string name="alarm_vibrate" msgid="3476686921490362230">"Värinä"</string>
     <string name="alarm_repeat" msgid="7242985466344233206">"Toisto"</string>
     <string name="alert" msgid="6506982899651975645">"Herätyksen soittoääni"</string>
+    <string name="default_alarm_ringtone_title" msgid="2916376768509623643">"Herätyksen oletusääni"</string>
     <string name="default_timer_ringtone_title" msgid="1370799536406862317">"Ajastin lopussa"</string>
     <string name="silent_timer_ringtone_title" msgid="7588723137323327647">"Äänetön"</string>
     <string name="ringtone" msgid="9110746249688559579">"Soittoääni"</string>
@@ -233,7 +234,7 @@
     <string name="cities_activity_title" msgid="8552462751129256730">"Kaupungit"</string>
     <string name="clock_settings" msgid="8317286807280600391">"Kello"</string>
     <string name="clock_style" msgid="2265011060429742344">"Tyyli"</string>
-    <string name="open_date_settings" msgid="5915966847691221307">"Vaihda päivämäärä ja aika"</string>
+    <string name="open_date_settings" msgid="7712226973337806152">"Vaihda päivämäärä ja aika"</string>
   <string-array name="clock_style_entries">
     <item msgid="917900462224167608">"Analoginen"</item>
     <item msgid="8483930821046925592">"Digitaalinen"</item>
diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml
index e8a77ec..8cdc8e6 100644
--- a/res/values-fr-rCA/strings.xml
+++ b/res/values-fr-rCA/strings.xml
@@ -35,6 +35,7 @@
     <string name="alarm_vibrate" msgid="3476686921490362230">"Vibreur"</string>
     <string name="alarm_repeat" msgid="7242985466344233206">"Répéter"</string>
     <string name="alert" msgid="6506982899651975645">"Sonnerie de l\'alarme"</string>
+    <string name="default_alarm_ringtone_title" msgid="2916376768509623643">"Son par défaut pour l\'alarme"</string>
     <string name="default_timer_ringtone_title" msgid="1370799536406862317">"Minuterie expirée"</string>
     <string name="silent_timer_ringtone_title" msgid="7588723137323327647">"Mode silencieux"</string>
     <string name="ringtone" msgid="9110746249688559579">"Sonnerie"</string>
@@ -234,7 +235,7 @@
     <string name="cities_activity_title" msgid="8552462751129256730">"Villes"</string>
     <string name="clock_settings" msgid="8317286807280600391">"Horloge"</string>
     <string name="clock_style" msgid="2265011060429742344">"Style"</string>
-    <string name="open_date_settings" msgid="5915966847691221307">"Modifier la date et l\'heure"</string>
+    <string name="open_date_settings" msgid="7712226973337806152">"Modifier la date et l\'heure"</string>
   <string-array name="clock_style_entries">
     <item msgid="917900462224167608">"Analogique"</item>
     <item msgid="8483930821046925592">"Numérique"</item>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index d8a7fab..64d271f 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -35,6 +35,7 @@
     <string name="alarm_vibrate" msgid="3476686921490362230">"Vibreur"</string>
     <string name="alarm_repeat" msgid="7242985466344233206">"Répéter"</string>
     <string name="alert" msgid="6506982899651975645">"Sonnerie de l\'alarme"</string>
+    <string name="default_alarm_ringtone_title" msgid="2916376768509623643">"Son de l\'alarme par défaut"</string>
     <string name="default_timer_ringtone_title" msgid="1370799536406862317">"Minuteur arrivé à expiration"</string>
     <string name="silent_timer_ringtone_title" msgid="7588723137323327647">"Silencieux"</string>
     <string name="ringtone" msgid="9110746249688559579">"Sonnerie"</string>
@@ -233,7 +234,7 @@
     <string name="cities_activity_title" msgid="8552462751129256730">"Villes"</string>
     <string name="clock_settings" msgid="8317286807280600391">"Horloge"</string>
     <string name="clock_style" msgid="2265011060429742344">"Style"</string>
-    <string name="open_date_settings" msgid="5915966847691221307">"Modifier la date et l\'heure"</string>
+    <string name="open_date_settings" msgid="7712226973337806152">"Modifier la date et l\'heure"</string>
   <string-array name="clock_style_entries">
     <item msgid="917900462224167608">"Analogique"</item>
     <item msgid="8483930821046925592">"Numérique"</item>
diff --git a/res/values-gl-rES/strings.xml b/res/values-gl-rES/strings.xml
index 1b7e11b..56575d1 100644
--- a/res/values-gl-rES/strings.xml
+++ b/res/values-gl-rES/strings.xml
@@ -35,6 +35,7 @@
     <string name="alarm_vibrate" msgid="3476686921490362230">"Vibrar"</string>
     <string name="alarm_repeat" msgid="7242985466344233206">"Repetir"</string>
     <string name="alert" msgid="6506982899651975645">"Ton de chamada da alarma"</string>
+    <string name="default_alarm_ringtone_title" msgid="2916376768509623643">"Son de alarma predeterminado"</string>
     <string name="default_timer_ringtone_title" msgid="1370799536406862317">"Finalizou o temporizador"</string>
     <string name="silent_timer_ringtone_title" msgid="7588723137323327647">"Silencio"</string>
     <string name="ringtone" msgid="9110746249688559579">"Ton de chamada"</string>
@@ -234,7 +235,7 @@
     <string name="cities_activity_title" msgid="8552462751129256730">"Cidades"</string>
     <string name="clock_settings" msgid="8317286807280600391">"Reloxo"</string>
     <string name="clock_style" msgid="2265011060429742344">"Estilo"</string>
-    <string name="open_date_settings" msgid="5915966847691221307">"Cambiar data e hora"</string>
+    <string name="open_date_settings" msgid="7712226973337806152">"Cambiar data e hora"</string>
   <string-array name="clock_style_entries">
     <item msgid="917900462224167608">"Analóxico"</item>
     <item msgid="8483930821046925592">"Dixital"</item>
diff --git a/res/values-gu-rIN/strings.xml b/res/values-gu-rIN/strings.xml
index 09d80f0..f086081 100644
--- a/res/values-gu-rIN/strings.xml
+++ b/res/values-gu-rIN/strings.xml
@@ -35,6 +35,7 @@
     <string name="alarm_vibrate" msgid="3476686921490362230">"વાઇબ્રેટ"</string>
     <string name="alarm_repeat" msgid="7242985466344233206">"પુનરાવર્તન"</string>
     <string name="alert" msgid="6506982899651975645">"એલાર્મ રિંગટોન"</string>
+    <string name="default_alarm_ringtone_title" msgid="2916376768509623643">"ડિફોલ્ટ એલાર્મ ધ્વનિ"</string>
     <string name="default_timer_ringtone_title" msgid="1370799536406862317">"ટાઇમરની સમય સીમા સમાપ્ત થઈ"</string>
     <string name="silent_timer_ringtone_title" msgid="7588723137323327647">"શાંત"</string>
     <string name="ringtone" msgid="9110746249688559579">"રિંગટોન"</string>
@@ -234,7 +235,7 @@
     <string name="cities_activity_title" msgid="8552462751129256730">"શહેરો"</string>
     <string name="clock_settings" msgid="8317286807280600391">"ઘડિયાળ"</string>
     <string name="clock_style" msgid="2265011060429742344">"શૈલી"</string>
-    <string name="open_date_settings" msgid="5915966847691221307">"તારીખ અને સમય બદલો"</string>
+    <string name="open_date_settings" msgid="7712226973337806152">"તારીખ અને સમય બદલો"</string>
   <string-array name="clock_style_entries">
     <item msgid="917900462224167608">"એનાલોગ"</item>
     <item msgid="8483930821046925592">"ડિજિટલ"</item>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index 7a6c306..2eb26ce 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -35,6 +35,7 @@
     <string name="alarm_vibrate" msgid="3476686921490362230">"कंपन"</string>
     <string name="alarm_repeat" msgid="7242985466344233206">"दोहराएं"</string>
     <string name="alert" msgid="6506982899651975645">"अलार्म रिंगटोन"</string>
+    <string name="default_alarm_ringtone_title" msgid="2916376768509623643">"डिफ़ॉल्ट अलार्म ध्वनि"</string>
     <string name="default_timer_ringtone_title" msgid="1370799536406862317">"टाइमर की समय सीमा समाप्‍त हो गई"</string>
     <string name="silent_timer_ringtone_title" msgid="7588723137323327647">"मौन"</string>
     <string name="ringtone" msgid="9110746249688559579">"रिंगटोन"</string>
@@ -233,7 +234,7 @@
     <string name="cities_activity_title" msgid="8552462751129256730">"शहर"</string>
     <string name="clock_settings" msgid="8317286807280600391">"घड़ी"</string>
     <string name="clock_style" msgid="2265011060429742344">"शैली"</string>
-    <string name="open_date_settings" msgid="5915966847691221307">"दिनांक और समय बदलना"</string>
+    <string name="open_date_settings" msgid="7712226973337806152">"दिनांक और समय बदलें"</string>
   <string-array name="clock_style_entries">
     <item msgid="917900462224167608">"ऐनालॉग"</item>
     <item msgid="8483930821046925592">"डिजिटल"</item>
diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml
index 7bd7619..924cff1 100644
--- a/res/values-hr/strings.xml
+++ b/res/values-hr/strings.xml
@@ -35,6 +35,7 @@
     <string name="alarm_vibrate" msgid="3476686921490362230">"Vibracija"</string>
     <string name="alarm_repeat" msgid="7242985466344233206">"Ponovi"</string>
     <string name="alert" msgid="6506982899651975645">"Melodija alarma"</string>
+    <string name="default_alarm_ringtone_title" msgid="2916376768509623643">"Zadani zvuk alarma"</string>
     <string name="default_timer_ringtone_title" msgid="1370799536406862317">"Tajmer je istekao"</string>
     <string name="silent_timer_ringtone_title" msgid="7588723137323327647">"Bešumno"</string>
     <string name="ringtone" msgid="9110746249688559579">"Zvuk zvona"</string>
@@ -244,7 +245,7 @@
     <string name="cities_activity_title" msgid="8552462751129256730">"Gradovi"</string>
     <string name="clock_settings" msgid="8317286807280600391">"Sat"</string>
     <string name="clock_style" msgid="2265011060429742344">"Stil"</string>
-    <string name="open_date_settings" msgid="5915966847691221307">"Promijeni datum i vrijeme"</string>
+    <string name="open_date_settings" msgid="7712226973337806152">"Promijeni datum i vrijeme"</string>
   <string-array name="clock_style_entries">
     <item msgid="917900462224167608">"Analogni"</item>
     <item msgid="8483930821046925592">"Digitalni"</item>
diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml
index fa29b6f..856886b 100644
--- a/res/values-hu/strings.xml
+++ b/res/values-hu/strings.xml
@@ -35,6 +35,7 @@
     <string name="alarm_vibrate" msgid="3476686921490362230">"Rezgés"</string>
     <string name="alarm_repeat" msgid="7242985466344233206">"Ismétlődés"</string>
     <string name="alert" msgid="6506982899651975645">"Ébresztés csengőhangja"</string>
+    <string name="default_alarm_ringtone_title" msgid="2916376768509623643">"Alapértelmezett ébresztési hang"</string>
     <string name="default_timer_ringtone_title" msgid="1370799536406862317">"Az időzítő lejárt"</string>
     <string name="silent_timer_ringtone_title" msgid="7588723137323327647">"Néma"</string>
     <string name="ringtone" msgid="9110746249688559579">"Csengőhang"</string>
@@ -233,7 +234,7 @@
     <string name="cities_activity_title" msgid="8552462751129256730">"Városok"</string>
     <string name="clock_settings" msgid="8317286807280600391">"Óra"</string>
     <string name="clock_style" msgid="2265011060429742344">"Stílus"</string>
-    <string name="open_date_settings" msgid="5915966847691221307">"Dátum és idő módosítása"</string>
+    <string name="open_date_settings" msgid="7712226973337806152">"Dátum és idő módosítása"</string>
   <string-array name="clock_style_entries">
     <item msgid="917900462224167608">"Analóg"</item>
     <item msgid="8483930821046925592">"Digitális"</item>
diff --git a/res/values-hy-rAM/strings.xml b/res/values-hy-rAM/strings.xml
index 4764169..4a9e41f 100644
--- a/res/values-hy-rAM/strings.xml
+++ b/res/values-hy-rAM/strings.xml
@@ -35,6 +35,7 @@
     <string name="alarm_vibrate" msgid="3476686921490362230">"Թրթռալ"</string>
     <string name="alarm_repeat" msgid="7242985466344233206">"Կրկնել"</string>
     <string name="alert" msgid="6506982899651975645">"Զարթուցիչի զանգերանգը"</string>
+    <string name="default_alarm_ringtone_title" msgid="2916376768509623643">"Զարթուցիչի կանխադրված ձայնը"</string>
     <string name="default_timer_ringtone_title" msgid="1370799536406862317">"Ժամաչափի ժամանակը սպառվել է"</string>
     <string name="silent_timer_ringtone_title" msgid="7588723137323327647">"Լուռ"</string>
     <string name="ringtone" msgid="9110746249688559579">"Զանգերանգ"</string>
@@ -234,7 +235,7 @@
     <string name="cities_activity_title" msgid="8552462751129256730">"Քաղաքներ"</string>
     <string name="clock_settings" msgid="8317286807280600391">"Ժամացույց"</string>
     <string name="clock_style" msgid="2265011060429742344">"Տեսակը"</string>
-    <string name="open_date_settings" msgid="5915966847691221307">"Ամսաթվի և ժամի փոփոխում"</string>
+    <string name="open_date_settings" msgid="7712226973337806152">"Ամսաթվի և ժամի փոփոխում"</string>
   <string-array name="clock_style_entries">
     <item msgid="917900462224167608">"Անալոգ"</item>
     <item msgid="8483930821046925592">"Թվային"</item>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index 4731c27..bab0202 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -35,6 +35,7 @@
     <string name="alarm_vibrate" msgid="3476686921490362230">"Getar"</string>
     <string name="alarm_repeat" msgid="7242985466344233206">"Ulangi"</string>
     <string name="alert" msgid="6506982899651975645">"Nada Dering Alarm"</string>
+    <string name="default_alarm_ringtone_title" msgid="2916376768509623643">"Suara alarm default"</string>
     <string name="default_timer_ringtone_title" msgid="1370799536406862317">"Pewaktu Berakhir"</string>
     <string name="silent_timer_ringtone_title" msgid="7588723137323327647">"Senyap"</string>
     <string name="ringtone" msgid="9110746249688559579">"Nada Dering"</string>
@@ -233,7 +234,7 @@
     <string name="cities_activity_title" msgid="8552462751129256730">"Kota"</string>
     <string name="clock_settings" msgid="8317286807280600391">"Jam"</string>
     <string name="clock_style" msgid="2265011060429742344">"Gaya"</string>
-    <string name="open_date_settings" msgid="5915966847691221307">"Ubah Tanggal &amp; waktu"</string>
+    <string name="open_date_settings" msgid="7712226973337806152">"Ubah tanggal &amp; waktu"</string>
   <string-array name="clock_style_entries">
     <item msgid="917900462224167608">"Analog"</item>
     <item msgid="8483930821046925592">"Digital"</item>
diff --git a/res/values-is-rIS/strings.xml b/res/values-is-rIS/strings.xml
index b927e86..daf0438 100644
--- a/res/values-is-rIS/strings.xml
+++ b/res/values-is-rIS/strings.xml
@@ -35,6 +35,7 @@
     <string name="alarm_vibrate" msgid="3476686921490362230">"Titringur"</string>
     <string name="alarm_repeat" msgid="7242985466344233206">"Endurtaka"</string>
     <string name="alert" msgid="6506982899651975645">"Hringitónn vekjara"</string>
+    <string name="default_alarm_ringtone_title" msgid="2916376768509623643">"Sjálfgefið hljóð í vekjara"</string>
     <string name="default_timer_ringtone_title" msgid="1370799536406862317">"Teljari útrunninn"</string>
     <string name="silent_timer_ringtone_title" msgid="7588723137323327647">"Hljóðlaus"</string>
     <string name="ringtone" msgid="9110746249688559579">"Hringitónn"</string>
@@ -234,7 +235,7 @@
     <string name="cities_activity_title" msgid="8552462751129256730">"Borgir"</string>
     <string name="clock_settings" msgid="8317286807280600391">"Klukka"</string>
     <string name="clock_style" msgid="2265011060429742344">"Stíll"</string>
-    <string name="open_date_settings" msgid="5915966847691221307">"Breyta dagsetningu og tíma"</string>
+    <string name="open_date_settings" msgid="7712226973337806152">"Breyta dag- og tímasetningu"</string>
   <string-array name="clock_style_entries">
     <item msgid="917900462224167608">"Með vísum"</item>
     <item msgid="8483930821046925592">"Stafræn"</item>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index 34fbb28..9c1f88c 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -35,6 +35,7 @@
     <string name="alarm_vibrate" msgid="3476686921490362230">"Vibrazione"</string>
     <string name="alarm_repeat" msgid="7242985466344233206">"Ripeti"</string>
     <string name="alert" msgid="6506982899651975645">"Suoneria sveglia"</string>
+    <string name="default_alarm_ringtone_title" msgid="2916376768509623643">"Suono sveglia predefinito"</string>
     <string name="default_timer_ringtone_title" msgid="1370799536406862317">"Timer scaduto"</string>
     <string name="silent_timer_ringtone_title" msgid="7588723137323327647">"Silenzioso"</string>
     <string name="ringtone" msgid="9110746249688559579">"Suoneria"</string>
@@ -233,7 +234,7 @@
     <string name="cities_activity_title" msgid="8552462751129256730">"Città"</string>
     <string name="clock_settings" msgid="8317286807280600391">"Orologio"</string>
     <string name="clock_style" msgid="2265011060429742344">"Stile"</string>
-    <string name="open_date_settings" msgid="5915966847691221307">"Modifica data e ora"</string>
+    <string name="open_date_settings" msgid="7712226973337806152">"Modifica data e ora"</string>
   <string-array name="clock_style_entries">
     <item msgid="917900462224167608">"Analogico"</item>
     <item msgid="8483930821046925592">"Digitale"</item>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index 7b6adae..d6f7a34 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -35,6 +35,7 @@
     <string name="alarm_vibrate" msgid="3476686921490362230">"רטט"</string>
     <string name="alarm_repeat" msgid="7242985466344233206">"חוזר"</string>
     <string name="alert" msgid="6506982899651975645">"צלצול שעון מעורר"</string>
+    <string name="default_alarm_ringtone_title" msgid="2916376768509623643">"צליל ברירת המחדל להתראה"</string>
     <string name="default_timer_ringtone_title" msgid="1370799536406862317">"הטיימר סיים"</string>
     <string name="silent_timer_ringtone_title" msgid="7588723137323327647">"שקט"</string>
     <string name="ringtone" msgid="9110746249688559579">"רינגטון"</string>
@@ -255,7 +256,7 @@
     <string name="cities_activity_title" msgid="8552462751129256730">"ערים"</string>
     <string name="clock_settings" msgid="8317286807280600391">"שעון"</string>
     <string name="clock_style" msgid="2265011060429742344">"סגנון"</string>
-    <string name="open_date_settings" msgid="5915966847691221307">"שנה תאריך ושעה"</string>
+    <string name="open_date_settings" msgid="7712226973337806152">"שינוי תאריך ושעה"</string>
   <string-array name="clock_style_entries">
     <item msgid="917900462224167608">"אנלוגי"</item>
     <item msgid="8483930821046925592">"דיגיטלי"</item>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index eda6d1d..f151946 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -35,6 +35,7 @@
     <string name="alarm_vibrate" msgid="3476686921490362230">"バイブレーション"</string>
     <string name="alarm_repeat" msgid="7242985466344233206">"繰り返し"</string>
     <string name="alert" msgid="6506982899651975645">"アラーム音"</string>
+    <string name="default_alarm_ringtone_title" msgid="2916376768509623643">"デフォルトの警告音"</string>
     <string name="default_timer_ringtone_title" msgid="1370799536406862317">"タイマーの完了時"</string>
     <string name="silent_timer_ringtone_title" msgid="7588723137323327647">"マナーモード"</string>
     <string name="ringtone" msgid="9110746249688559579">"着信音"</string>
@@ -233,7 +234,7 @@
     <string name="cities_activity_title" msgid="8552462751129256730">"世界時計"</string>
     <string name="clock_settings" msgid="8317286807280600391">"時計"</string>
     <string name="clock_style" msgid="2265011060429742344">"スタイル"</string>
-    <string name="open_date_settings" msgid="5915966847691221307">"日付と時刻の変更"</string>
+    <string name="open_date_settings" msgid="7712226973337806152">"日付と時刻の変更"</string>
   <string-array name="clock_style_entries">
     <item msgid="917900462224167608">"アナログ"</item>
     <item msgid="8483930821046925592">"デジタル"</item>
@@ -345,7 +346,7 @@
     <string name="description_direction_left" msgid="7448141043674998679">"左にスワイプするとスヌーズを設定できます"</string>
     <string name="description_direction_both" msgid="1841309486023845685">"左にスワイプでスヌーズを設定、右にスワイプで解除できます"</string>
     <string name="timer_settings" msgid="7955522143086154795">"タイマー"</string>
-    <string name="timer_ringtone_title" msgid="7630214935791599619">"タイマーの着信音です"</string>
+    <string name="timer_ringtone_title" msgid="7630214935791599619">"アラーム音"</string>
     <string name="timer_stopped" msgid="2730331837832462008">"タイマーが停止しました"</string>
     <string name="timer_paused" msgid="5941160896040771462">"タイマーを一時停止しました"</string>
     <string name="timers_stopped" msgid="3186191253226005149">"<xliff:g id="NUMBER">%d</xliff:g>個のタイマーを一時停止しました"</string>
diff --git a/res/values-ka-rGE/strings.xml b/res/values-ka-rGE/strings.xml
index 26ea70d..6b765c2 100644
--- a/res/values-ka-rGE/strings.xml
+++ b/res/values-ka-rGE/strings.xml
@@ -35,6 +35,7 @@
     <string name="alarm_vibrate" msgid="3476686921490362230">"ვიბრაცია"</string>
     <string name="alarm_repeat" msgid="7242985466344233206">"გამეორება"</string>
     <string name="alert" msgid="6506982899651975645">"მაღვიძარას ზარი"</string>
+    <string name="default_alarm_ringtone_title" msgid="2916376768509623643">"მაღვიძარას ნაგულისხმევი ხმა"</string>
     <string name="default_timer_ringtone_title" msgid="1370799536406862317">"ტაიმერის დრო ამოიწურა"</string>
     <string name="silent_timer_ringtone_title" msgid="7588723137323327647">"უხმო"</string>
     <string name="ringtone" msgid="9110746249688559579">"ზარი"</string>
@@ -234,7 +235,7 @@
     <string name="cities_activity_title" msgid="8552462751129256730">"ქალაქები"</string>
     <string name="clock_settings" msgid="8317286807280600391">"საათი"</string>
     <string name="clock_style" msgid="2265011060429742344">"სტილი"</string>
-    <string name="open_date_settings" msgid="5915966847691221307">"დროისა და თარიღის შეცვლა"</string>
+    <string name="open_date_settings" msgid="7712226973337806152">"თარიღისა და დროის შეცვლა"</string>
   <string-array name="clock_style_entries">
     <item msgid="917900462224167608">"ანალოგი"</item>
     <item msgid="8483930821046925592">"ციფრული"</item>
diff --git a/res/values-kk-rKZ/strings.xml b/res/values-kk-rKZ/strings.xml
index aca0664..5ccafc7 100644
--- a/res/values-kk-rKZ/strings.xml
+++ b/res/values-kk-rKZ/strings.xml
@@ -35,6 +35,7 @@
     <string name="alarm_vibrate" msgid="3476686921490362230">"Діріл"</string>
     <string name="alarm_repeat" msgid="7242985466344233206">"Қайталау"</string>
     <string name="alert" msgid="6506982899651975645">"Дабылдың қоңырау әуені"</string>
+    <string name="default_alarm_ringtone_title" msgid="2916376768509623643">"Әдепкі дабыл дыбысы"</string>
     <string name="default_timer_ringtone_title" msgid="1370799536406862317">"Таймер мерзімі бітті"</string>
     <string name="silent_timer_ringtone_title" msgid="7588723137323327647">"Үнсіз"</string>
     <string name="ringtone" msgid="9110746249688559579">"Қоңырау әуені"</string>
@@ -234,7 +235,7 @@
     <string name="cities_activity_title" msgid="8552462751129256730">"Қалалар"</string>
     <string name="clock_settings" msgid="8317286807280600391">"Cағат"</string>
     <string name="clock_style" msgid="2265011060429742344">"Стиль"</string>
-    <string name="open_date_settings" msgid="5915966847691221307">"Күнді және уақытты өзгерту"</string>
+    <string name="open_date_settings" msgid="7712226973337806152">"Күнді және уақытты өзгерту"</string>
   <string-array name="clock_style_entries">
     <item msgid="917900462224167608">"Аналогтық"</item>
     <item msgid="8483930821046925592">"Сандық"</item>
diff --git a/res/values-km-rKH/strings.xml b/res/values-km-rKH/strings.xml
index 6e162a2..8ff855e 100644
--- a/res/values-km-rKH/strings.xml
+++ b/res/values-km-rKH/strings.xml
@@ -35,6 +35,7 @@
     <string name="alarm_vibrate" msgid="3476686921490362230">"ញ័រ"</string>
     <string name="alarm_repeat" msgid="7242985466344233206">"ធ្វើ​ម្ដងទៀត"</string>
     <string name="alert" msgid="6506982899651975645">"សំឡេង​រោទ៍"</string>
+    <string name="default_alarm_ringtone_title" msgid="2916376768509623643">"សំឡេងម៉ោងរោទិ៍លំនាំដើម"</string>
     <string name="default_timer_ringtone_title" msgid="1370799536406862317">"កម្មវិធីកំណត់ពេលបានផុតកំណត់"</string>
     <string name="silent_timer_ringtone_title" msgid="7588723137323327647">"ស្ងាត់"</string>
     <string name="ringtone" msgid="9110746249688559579">"សំឡេង​រោទ៍"</string>
@@ -234,7 +235,7 @@
     <string name="cities_activity_title" msgid="8552462751129256730">"ទីក្រុង"</string>
     <string name="clock_settings" msgid="8317286807280600391">"នាឡិកា"</string>
     <string name="clock_style" msgid="2265011060429742344">"រចនាប័ទ្ម"</string>
-    <string name="open_date_settings" msgid="5915966847691221307">"ប្តូរកាលបរិច្ឆេទ និងម៉ោង"</string>
+    <string name="open_date_settings" msgid="7712226973337806152">"ប្តូរកាលបរិច្ឆេទ និងម៉ោង"</string>
   <string-array name="clock_style_entries">
     <item msgid="917900462224167608">"អាណាឡូក"</item>
     <item msgid="8483930821046925592">"ឌីជីថល"</item>
diff --git a/res/values-kn-rIN/strings.xml b/res/values-kn-rIN/strings.xml
index 065d484..8b8822f 100644
--- a/res/values-kn-rIN/strings.xml
+++ b/res/values-kn-rIN/strings.xml
@@ -35,6 +35,7 @@
     <string name="alarm_vibrate" msgid="3476686921490362230">"ವೈಬ್ರೇಟ್‌‌"</string>
     <string name="alarm_repeat" msgid="7242985466344233206">"ಪುನರಾವರ್ತನೆ"</string>
     <string name="alert" msgid="6506982899651975645">"ಅಲಾರಂ ರಿಂಗ್‌ಟೋನ್"</string>
+    <string name="default_alarm_ringtone_title" msgid="2916376768509623643">"ಡೀಫಾಲ್ಟ್ ಅಲಾರಮ್ ಧ್ವನಿ"</string>
     <string name="default_timer_ringtone_title" msgid="1370799536406862317">"ಟೈಮರ್ ಅವಧಿ ಮೀರಿದೆ"</string>
     <string name="silent_timer_ringtone_title" msgid="7588723137323327647">"ನಿಶ್ಯಬ್ಧ"</string>
     <string name="ringtone" msgid="9110746249688559579">"ರಿಂಗ್‌ಟೋನ್‌"</string>
@@ -234,7 +235,7 @@
     <string name="cities_activity_title" msgid="8552462751129256730">"ನಗರಗಳು"</string>
     <string name="clock_settings" msgid="8317286807280600391">"ಗಡಿಯಾರ"</string>
     <string name="clock_style" msgid="2265011060429742344">"ಶೈಲಿ"</string>
-    <string name="open_date_settings" msgid="5915966847691221307">"ದಿನಾಂಕ ಮತ್ತು ಸಮಯವನ್ನು ಬದಲಾಯಿಸಿ"</string>
+    <string name="open_date_settings" msgid="7712226973337806152">"ದಿನಾಂಕ &amp; ಸಮಯವನ್ನು ಬದಲಾಯಿಸಿ"</string>
   <string-array name="clock_style_entries">
     <item msgid="917900462224167608">"ಅನಲಾಗ್‌"</item>
     <item msgid="8483930821046925592">"ಡಿಜಿಟಲ್"</item>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index f7dc66f..e2cae30 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -35,6 +35,7 @@
     <string name="alarm_vibrate" msgid="3476686921490362230">"진동"</string>
     <string name="alarm_repeat" msgid="7242985466344233206">"반복"</string>
     <string name="alert" msgid="6506982899651975645">"알람 벨소리"</string>
+    <string name="default_alarm_ringtone_title" msgid="2916376768509623643">"기본 알람 소리"</string>
     <string name="default_timer_ringtone_title" msgid="1370799536406862317">"타이머 만료됨"</string>
     <string name="silent_timer_ringtone_title" msgid="7588723137323327647">"무음"</string>
     <string name="ringtone" msgid="9110746249688559579">"벨소리"</string>
@@ -233,7 +234,7 @@
     <string name="cities_activity_title" msgid="8552462751129256730">"도시"</string>
     <string name="clock_settings" msgid="8317286807280600391">"시계"</string>
     <string name="clock_style" msgid="2265011060429742344">"종류"</string>
-    <string name="open_date_settings" msgid="5915966847691221307">"날짜 및 시간 변경"</string>
+    <string name="open_date_settings" msgid="7712226973337806152">"날짜 및 시간 변경"</string>
   <string-array name="clock_style_entries">
     <item msgid="917900462224167608">"아날로그"</item>
     <item msgid="8483930821046925592">"디지털"</item>
diff --git a/res/values-ky-rKG/strings.xml b/res/values-ky-rKG/strings.xml
index 4dd990b..34e2895 100644
--- a/res/values-ky-rKG/strings.xml
+++ b/res/values-ky-rKG/strings.xml
@@ -35,6 +35,7 @@
     <string name="alarm_vibrate" msgid="3476686921490362230">"Титирет"</string>
     <string name="alarm_repeat" msgid="7242985466344233206">"Кайталоо"</string>
     <string name="alert" msgid="6506982899651975645">"Ойготкучтун шыңгыры"</string>
+    <string name="default_alarm_ringtone_title" msgid="2916376768509623643">"Демейки ойготкуч добушу"</string>
     <string name="default_timer_ringtone_title" msgid="1370799536406862317">"Таймердин мөөнөтү бүттү"</string>
     <string name="silent_timer_ringtone_title" msgid="7588723137323327647">"Үнсүз"</string>
     <string name="ringtone" msgid="9110746249688559579">"Шыңгыр"</string>
@@ -234,7 +235,7 @@
     <string name="cities_activity_title" msgid="8552462751129256730">"Шаарлар"</string>
     <string name="clock_settings" msgid="8317286807280600391">"Саат"</string>
     <string name="clock_style" msgid="2265011060429742344">"Стиль"</string>
-    <string name="open_date_settings" msgid="5915966847691221307">"Датаны &amp; убакытты өзгөртүү"</string>
+    <string name="open_date_settings" msgid="7712226973337806152">"Күндү жана убакытты өзгөртүү"</string>
   <string-array name="clock_style_entries">
     <item msgid="917900462224167608">"Аналог"</item>
     <item msgid="8483930821046925592">"Санариптик"</item>
diff --git a/res/values-lo-rLA/strings.xml b/res/values-lo-rLA/strings.xml
index 78ea399..909a99c 100644
--- a/res/values-lo-rLA/strings.xml
+++ b/res/values-lo-rLA/strings.xml
@@ -35,6 +35,7 @@
     <string name="alarm_vibrate" msgid="3476686921490362230">"ສັ່ນເຕືອນ"</string>
     <string name="alarm_repeat" msgid="7242985466344233206">"ປຸກຊ້ຳ"</string>
     <string name="alert" msgid="6506982899651975645">"ສຽງໂມງປຸກ"</string>
+    <string name="default_alarm_ringtone_title" msgid="2916376768509623643">"ສຽງ​ໂມງ​ປຸກ​ຕາມຄ່າເລີ່ມຕົ້ນ"</string>
     <string name="default_timer_ringtone_title" msgid="1370799536406862317">"ເຄື່ອງ​ຈັບ​ເວ​ລາ​ໝົດ​ອາ​ຍຸ​ແລ້ວ"</string>
     <string name="silent_timer_ringtone_title" msgid="7588723137323327647">"ປິດສຽງ"</string>
     <string name="ringtone" msgid="9110746249688559579">"ສຽງໂມງປຸກ"</string>
@@ -234,7 +235,7 @@
     <string name="cities_activity_title" msgid="8552462751129256730">"ເມືອງ"</string>
     <string name="clock_settings" msgid="8317286807280600391">"ໂມງ"</string>
     <string name="clock_style" msgid="2265011060429742344">"ຮູບແບບ"</string>
-    <string name="open_date_settings" msgid="5915966847691221307">"ປ່ຽນ​ແປງວັນ​ທີ ແລະ ເວ​ລາ"</string>
+    <string name="open_date_settings" msgid="7712226973337806152">"ປ່ຽນ​ວັນ​ທີ ແລະ ເວ​ລາ"</string>
   <string-array name="clock_style_entries">
     <item msgid="917900462224167608">"ໂມງເຂັມ"</item>
     <item msgid="8483930821046925592">"ດິຈິຕອນ"</item>
diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml
index 7c6af3a..d4d95fa 100644
--- a/res/values-lt/strings.xml
+++ b/res/values-lt/strings.xml
@@ -35,6 +35,7 @@
     <string name="alarm_vibrate" msgid="3476686921490362230">"Vibruoti"</string>
     <string name="alarm_repeat" msgid="7242985466344233206">"Kartoti"</string>
     <string name="alert" msgid="6506982899651975645">"Signalo skambėjimo tonas"</string>
+    <string name="default_alarm_ringtone_title" msgid="2916376768509623643">"Numatytasis signalo garsas"</string>
     <string name="default_timer_ringtone_title" msgid="1370799536406862317">"Baigėsi laikmačio galiojimas"</string>
     <string name="silent_timer_ringtone_title" msgid="7588723137323327647">"Begarsis"</string>
     <string name="ringtone" msgid="9110746249688559579">"Skambėjimo tonas"</string>
@@ -255,7 +256,7 @@
     <string name="cities_activity_title" msgid="8552462751129256730">"Miestai"</string>
     <string name="clock_settings" msgid="8317286807280600391">"Laikrodis"</string>
     <string name="clock_style" msgid="2265011060429742344">"Stilius"</string>
-    <string name="open_date_settings" msgid="5915966847691221307">"Pakeisti datą ir laiką"</string>
+    <string name="open_date_settings" msgid="7712226973337806152">"Pakeisti datą ir laiką"</string>
   <string-array name="clock_style_entries">
     <item msgid="917900462224167608">"Analoginis"</item>
     <item msgid="8483930821046925592">"Skaitmeninis"</item>
diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml
index eb13ed5..7e6eb48 100644
--- a/res/values-lv/strings.xml
+++ b/res/values-lv/strings.xml
@@ -35,6 +35,7 @@
     <string name="alarm_vibrate" msgid="3476686921490362230">"Vibrozvans"</string>
     <string name="alarm_repeat" msgid="7242985466344233206">"Atkārtošana"</string>
     <string name="alert" msgid="6506982899651975645">"Modinātāja signāls"</string>
+    <string name="default_alarm_ringtone_title" msgid="2916376768509623643">"Signāla noklusējuma skaņa"</string>
     <string name="default_timer_ringtone_title" msgid="1370799536406862317">"Taimera laiks ir beidzies"</string>
     <string name="silent_timer_ringtone_title" msgid="7588723137323327647">"Klusums"</string>
     <string name="ringtone" msgid="9110746249688559579">"Zvana signāls"</string>
@@ -244,7 +245,7 @@
     <string name="cities_activity_title" msgid="8552462751129256730">"Pilsētas"</string>
     <string name="clock_settings" msgid="8317286807280600391">"Pulkstenis"</string>
     <string name="clock_style" msgid="2265011060429742344">"Stils"</string>
-    <string name="open_date_settings" msgid="5915966847691221307">"Datuma un laika maiņa"</string>
+    <string name="open_date_settings" msgid="7712226973337806152">"Mainīt datumu un laiku"</string>
   <string-array name="clock_style_entries">
     <item msgid="917900462224167608">"Analogais"</item>
     <item msgid="8483930821046925592">"Digitālais"</item>
diff --git a/res/values-mk-rMK/strings.xml b/res/values-mk-rMK/strings.xml
index f61e9dd..1f3e573 100644
--- a/res/values-mk-rMK/strings.xml
+++ b/res/values-mk-rMK/strings.xml
@@ -35,6 +35,7 @@
     <string name="alarm_vibrate" msgid="3476686921490362230">"Вибрации"</string>
     <string name="alarm_repeat" msgid="7242985466344233206">"Се повторува"</string>
     <string name="alert" msgid="6506982899651975645">"Мелодија за аларм"</string>
+    <string name="default_alarm_ringtone_title" msgid="2916376768509623643">"Стандарден звук за аларм"</string>
     <string name="default_timer_ringtone_title" msgid="1370799536406862317">"Тајмерот истече"</string>
     <string name="silent_timer_ringtone_title" msgid="7588723137323327647">"Тивка мелодија"</string>
     <string name="ringtone" msgid="9110746249688559579">"Мелодија"</string>
@@ -234,7 +235,7 @@
     <string name="cities_activity_title" msgid="8552462751129256730">"Градови"</string>
     <string name="clock_settings" msgid="8317286807280600391">"Часовник"</string>
     <string name="clock_style" msgid="2265011060429742344">"Стил"</string>
-    <string name="open_date_settings" msgid="5915966847691221307">"Промени ги датумот и времето"</string>
+    <string name="open_date_settings" msgid="7712226973337806152">"Променете ги датумот и времето"</string>
   <string-array name="clock_style_entries">
     <item msgid="917900462224167608">"Аналоген"</item>
     <item msgid="8483930821046925592">"Дигитален"</item>
diff --git a/res/values-ml-rIN/strings.xml b/res/values-ml-rIN/strings.xml
index 3bbdd16..5425912 100644
--- a/res/values-ml-rIN/strings.xml
+++ b/res/values-ml-rIN/strings.xml
@@ -35,6 +35,7 @@
     <string name="alarm_vibrate" msgid="3476686921490362230">"വൈബ്രേറ്റുചെയ്യുക"</string>
     <string name="alarm_repeat" msgid="7242985466344233206">"ആവർത്തിക്കുക"</string>
     <string name="alert" msgid="6506982899651975645">"അലാറം റിംഗ്‌ടോൺ"</string>
+    <string name="default_alarm_ringtone_title" msgid="2916376768509623643">"ഡിഫോൾട്ട് അലാറം ശബ്ദം"</string>
     <string name="default_timer_ringtone_title" msgid="1370799536406862317">"ടൈമർ കാലഹരണപ്പെട്ടു"</string>
     <string name="silent_timer_ringtone_title" msgid="7588723137323327647">"നിശബ്‌ദം"</string>
     <string name="ringtone" msgid="9110746249688559579">"റിംഗ്ടോൺ"</string>
@@ -234,7 +235,7 @@
     <string name="cities_activity_title" msgid="8552462751129256730">"നഗരങ്ങൾ"</string>
     <string name="clock_settings" msgid="8317286807280600391">"ക്ലോക്ക്"</string>
     <string name="clock_style" msgid="2265011060429742344">"സ്റ്റൈൽ"</string>
-    <string name="open_date_settings" msgid="5915966847691221307">"തീയതിയും സമയവും മാറ്റുക"</string>
+    <string name="open_date_settings" msgid="7712226973337806152">"തീയതിയും സമയവും മാറ്റുക"</string>
   <string-array name="clock_style_entries">
     <item msgid="917900462224167608">"അനലോഗ്"</item>
     <item msgid="8483930821046925592">"ഡിജിറ്റൽ"</item>
diff --git a/res/values-mn-rMN/strings.xml b/res/values-mn-rMN/strings.xml
index b92e43f..f0b6f45 100644
--- a/res/values-mn-rMN/strings.xml
+++ b/res/values-mn-rMN/strings.xml
@@ -35,6 +35,7 @@
     <string name="alarm_vibrate" msgid="3476686921490362230">"Чичиргээ"</string>
     <string name="alarm_repeat" msgid="7242985466344233206">"Давтах"</string>
     <string name="alert" msgid="6506982899651975645">"Сэрүүлгийн ая"</string>
+    <string name="default_alarm_ringtone_title" msgid="2916376768509623643">"Сэрүүлгийн үндсэн дуу"</string>
     <string name="default_timer_ringtone_title" msgid="1370799536406862317">"Цаг хэмжигчийн хугацаа дууссан"</string>
     <string name="silent_timer_ringtone_title" msgid="7588723137323327647">"Чимээгүй"</string>
     <string name="ringtone" msgid="9110746249688559579">"Хонхны ая"</string>
@@ -234,7 +235,7 @@
     <string name="cities_activity_title" msgid="8552462751129256730">"Хотууд"</string>
     <string name="clock_settings" msgid="8317286807280600391">"Цаг"</string>
     <string name="clock_style" msgid="2265011060429742344">"Загвар"</string>
-    <string name="open_date_settings" msgid="5915966847691221307">"Огноо, цагийг өөрчлөх"</string>
+    <string name="open_date_settings" msgid="7712226973337806152">"Огноо, цагийг өөрчлөх"</string>
   <string-array name="clock_style_entries">
     <item msgid="917900462224167608">"Aналог"</item>
     <item msgid="8483930821046925592">"Дижитал"</item>
diff --git a/res/values-mr-rIN/strings.xml b/res/values-mr-rIN/strings.xml
index 851393a..5261d07 100644
--- a/res/values-mr-rIN/strings.xml
+++ b/res/values-mr-rIN/strings.xml
@@ -35,6 +35,7 @@
     <string name="alarm_vibrate" msgid="3476686921490362230">"कंपन"</string>
     <string name="alarm_repeat" msgid="7242985466344233206">"पुनरावृत्ती:"</string>
     <string name="alert" msgid="6506982899651975645">"अलार्म रिंगटोन"</string>
+    <string name="default_alarm_ringtone_title" msgid="2916376768509623643">"डीफॉल्ट अलार्म ध्वनी"</string>
     <string name="default_timer_ringtone_title" msgid="1370799536406862317">"टायमर कालबाह्य झाला"</string>
     <string name="silent_timer_ringtone_title" msgid="7588723137323327647">"मूक"</string>
     <string name="ringtone" msgid="9110746249688559579">"रिंगटोन"</string>
@@ -234,7 +235,7 @@
     <string name="cities_activity_title" msgid="8552462751129256730">"शहरे"</string>
     <string name="clock_settings" msgid="8317286807280600391">"घड्याळ"</string>
     <string name="clock_style" msgid="2265011060429742344">"शैली"</string>
-    <string name="open_date_settings" msgid="5915966847691221307">"तारीख आणि वेळ बदला"</string>
+    <string name="open_date_settings" msgid="7712226973337806152">"तारीख आणि वेळ बदला"</string>
   <string-array name="clock_style_entries">
     <item msgid="917900462224167608">"अॅनालॉग"</item>
     <item msgid="8483930821046925592">"डिजिटल"</item>
diff --git a/res/values-ms-rMY/strings.xml b/res/values-ms-rMY/strings.xml
index 217e2d2..560ed27 100644
--- a/res/values-ms-rMY/strings.xml
+++ b/res/values-ms-rMY/strings.xml
@@ -35,6 +35,7 @@
     <string name="alarm_vibrate" msgid="3476686921490362230">"Getar"</string>
     <string name="alarm_repeat" msgid="7242985466344233206">"Ulang"</string>
     <string name="alert" msgid="6506982899651975645">"Nada Dering Penggera"</string>
+    <string name="default_alarm_ringtone_title" msgid="2916376768509623643">"Bunyi penggera lalai"</string>
     <string name="default_timer_ringtone_title" msgid="1370799536406862317">"Pemasa Tamat"</string>
     <string name="silent_timer_ringtone_title" msgid="7588723137323327647">"Senyap"</string>
     <string name="ringtone" msgid="9110746249688559579">"Nada dering"</string>
@@ -233,7 +234,7 @@
     <string name="cities_activity_title" msgid="8552462751129256730">"Bandar"</string>
     <string name="clock_settings" msgid="8317286807280600391">"Jam"</string>
     <string name="clock_style" msgid="2265011060429742344">"Gaya"</string>
-    <string name="open_date_settings" msgid="5915966847691221307">"Tukar Tarikh &amp; masa"</string>
+    <string name="open_date_settings" msgid="7712226973337806152">"Tukar tarikh &amp; masa"</string>
   <string-array name="clock_style_entries">
     <item msgid="917900462224167608">"Analog"</item>
     <item msgid="8483930821046925592">"Digital"</item>
diff --git a/res/values-my-rMM/strings.xml b/res/values-my-rMM/strings.xml
index 16b6189..90673d5 100644
--- a/res/values-my-rMM/strings.xml
+++ b/res/values-my-rMM/strings.xml
@@ -35,6 +35,7 @@
     <string name="alarm_vibrate" msgid="3476686921490362230">"တုန်ခါရန်"</string>
     <string name="alarm_repeat" msgid="7242985466344233206">"ထပ်လုပ်ရန်"</string>
     <string name="alert" msgid="6506982899651975645">"နှိုးစက် သံစဉ်"</string>
+    <string name="default_alarm_ringtone_title" msgid="2916376768509623643">"ပုံသေ နှိုးစက်အသံ"</string>
     <string name="default_timer_ringtone_title" msgid="1370799536406862317">"အချိန်တိုင်းကိရိယာ သက်တမ်းကုန်ဆုံးပါပြီ"</string>
     <string name="silent_timer_ringtone_title" msgid="7588723137323327647">"အသံတိတ်ရန်"</string>
     <string name="ringtone" msgid="9110746249688559579">"သံစဉ်"</string>
@@ -234,7 +235,7 @@
     <string name="cities_activity_title" msgid="8552462751129256730">"မြို့များ"</string>
     <string name="clock_settings" msgid="8317286807280600391">"နာရီ"</string>
     <string name="clock_style" msgid="2265011060429742344">"ပုံစံ"</string>
-    <string name="open_date_settings" msgid="5915966847691221307">"ရက်စွဲ &amp; amp; အချိန် ပြောင်းရန်"</string>
+    <string name="open_date_settings" msgid="7712226973337806152">"ရက်စွဲ &amp; amp; အချိန်ကို ပြောင်းပါ"</string>
   <string-array name="clock_style_entries">
     <item msgid="917900462224167608">"လက်တံနာရီ"</item>
     <item msgid="8483930821046925592">"ဒီဂျစ်တယ်"</item>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index 7cb0aeb..e036146 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -35,6 +35,7 @@
     <string name="alarm_vibrate" msgid="3476686921490362230">"Vibrer"</string>
     <string name="alarm_repeat" msgid="7242985466344233206">"Gjenta"</string>
     <string name="alert" msgid="6506982899651975645">"Ringetone for alarm"</string>
+    <string name="default_alarm_ringtone_title" msgid="2916376768509623643">"Standard alarmlyd"</string>
     <string name="default_timer_ringtone_title" msgid="1370799536406862317">"Tidtakeren er utgått"</string>
     <string name="silent_timer_ringtone_title" msgid="7588723137323327647">"Lydløs"</string>
     <string name="ringtone" msgid="9110746249688559579">"Ringetone"</string>
@@ -233,7 +234,7 @@
     <string name="cities_activity_title" msgid="8552462751129256730">"Byer"</string>
     <string name="clock_settings" msgid="8317286807280600391">"Klokke"</string>
     <string name="clock_style" msgid="2265011060429742344">"Stil"</string>
-    <string name="open_date_settings" msgid="5915966847691221307">"Endre dato og tid"</string>
+    <string name="open_date_settings" msgid="7712226973337806152">"Endre dato og tid"</string>
   <string-array name="clock_style_entries">
     <item msgid="917900462224167608">"Analog"</item>
     <item msgid="8483930821046925592">"Digital"</item>
diff --git a/res/values-ne-rNP/strings.xml b/res/values-ne-rNP/strings.xml
index 9b13cf4..29ae509 100644
--- a/res/values-ne-rNP/strings.xml
+++ b/res/values-ne-rNP/strings.xml
@@ -35,6 +35,7 @@
     <string name="alarm_vibrate" msgid="3476686921490362230">"भाइब्रेट गराउनुहोस्"</string>
     <string name="alarm_repeat" msgid="7242985466344233206">"दोहोर्याउनुहोस्"</string>
     <string name="alert" msgid="6506982899651975645">"अलार्म रिङटोन"</string>
+    <string name="default_alarm_ringtone_title" msgid="2916376768509623643">"पूर्वनिर्धारित अलार्म ध्वनि"</string>
     <string name="default_timer_ringtone_title" msgid="1370799536406862317">"टाइमरको म्याद सकियो"</string>
     <string name="silent_timer_ringtone_title" msgid="7588723137323327647">"मौन"</string>
     <string name="ringtone" msgid="9110746249688559579">"रिङटोन"</string>
@@ -234,7 +235,7 @@
     <string name="cities_activity_title" msgid="8552462751129256730">"शहरहरू"</string>
     <string name="clock_settings" msgid="8317286807280600391">"घडी"</string>
     <string name="clock_style" msgid="2265011060429742344">"शैली"</string>
-    <string name="open_date_settings" msgid="5915966847691221307">"मिति र समय परिवर्तन गर्नुहोस्"</string>
+    <string name="open_date_settings" msgid="7712226973337806152">"मिति &amp; समय परिवर्तन गर्नुहोस्"</string>
   <string-array name="clock_style_entries">
     <item msgid="917900462224167608">"एनालग"</item>
     <item msgid="8483930821046925592">"डिजिटल"</item>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index 7b1b0a0..d1a3805 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -35,6 +35,7 @@
     <string name="alarm_vibrate" msgid="3476686921490362230">"Trillen"</string>
     <string name="alarm_repeat" msgid="7242985466344233206">"Herhalen"</string>
     <string name="alert" msgid="6506982899651975645">"Beltoon voor alarm"</string>
+    <string name="default_alarm_ringtone_title" msgid="2916376768509623643">"Standaardalarmgeluid"</string>
     <string name="default_timer_ringtone_title" msgid="1370799536406862317">"Timer verlopen"</string>
     <string name="silent_timer_ringtone_title" msgid="7588723137323327647">"Stil"</string>
     <string name="ringtone" msgid="9110746249688559579">"Beltoon"</string>
@@ -233,7 +234,7 @@
     <string name="cities_activity_title" msgid="8552462751129256730">"Plaatsen"</string>
     <string name="clock_settings" msgid="8317286807280600391">"Klok"</string>
     <string name="clock_style" msgid="2265011060429742344">"Stijl"</string>
-    <string name="open_date_settings" msgid="5915966847691221307">"Datum en tijd wijzigen"</string>
+    <string name="open_date_settings" msgid="7712226973337806152">"Datum en tijd wijzigen"</string>
   <string-array name="clock_style_entries">
     <item msgid="917900462224167608">"Analoog"</item>
     <item msgid="8483930821046925592">"Digitaal"</item>
diff --git a/res/values-pa-rIN/strings.xml b/res/values-pa-rIN/strings.xml
index 2de443f..1582dd0 100644
--- a/res/values-pa-rIN/strings.xml
+++ b/res/values-pa-rIN/strings.xml
@@ -35,6 +35,7 @@
     <string name="alarm_vibrate" msgid="3476686921490362230">"ਵਾਈਬ੍ਰੇਟ"</string>
     <string name="alarm_repeat" msgid="7242985466344233206">"ਦੁਹਰਾਓ"</string>
     <string name="alert" msgid="6506982899651975645">"ਅਲਾਰਮ ਰਿੰਗਟੋਨ"</string>
+    <string name="default_alarm_ringtone_title" msgid="2916376768509623643">"ਪੂਰਵ-ਨਿਰਧਾਰਤ ਅਲਾਰਮ ਧੁਨੀ"</string>
     <string name="default_timer_ringtone_title" msgid="1370799536406862317">"ਟਾਈਮਰ ਦੀ ਮਿਆਦ ਪੁੱਗੀ"</string>
     <string name="silent_timer_ringtone_title" msgid="7588723137323327647">"ਸਾਈਲੈਂਟ"</string>
     <string name="ringtone" msgid="9110746249688559579">"ਰਿੰਗਟੋਨ"</string>
@@ -234,7 +235,7 @@
     <string name="cities_activity_title" msgid="8552462751129256730">"ਸ਼ਹਿਰ"</string>
     <string name="clock_settings" msgid="8317286807280600391">"ਘੜੀ"</string>
     <string name="clock_style" msgid="2265011060429742344">"ਸਟਾਈਲ"</string>
-    <string name="open_date_settings" msgid="5915966847691221307">"ਤਾਰੀਖ &amp; ਸਮਾਂ ਬਦਲੋ"</string>
+    <string name="open_date_settings" msgid="7712226973337806152">"ਮਿਤੀ ਅਤੇ ਸਮਾਂ ਬਦਲੋ"</string>
   <string-array name="clock_style_entries">
     <item msgid="917900462224167608">"ਐਨਾਲਾਗ"</item>
     <item msgid="8483930821046925592">"ਡਿਜੀਟਲ"</item>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index 945fd1c..d9c9ae8 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -35,6 +35,7 @@
     <string name="alarm_vibrate" msgid="3476686921490362230">"Wibracje"</string>
     <string name="alarm_repeat" msgid="7242985466344233206">"Powtarzanie"</string>
     <string name="alert" msgid="6506982899651975645">"Dźwięk alarmu"</string>
+    <string name="default_alarm_ringtone_title" msgid="2916376768509623643">"Domyślny dźwięk alarmu"</string>
     <string name="default_timer_ringtone_title" msgid="1370799536406862317">"Minutnik wygasł"</string>
     <string name="silent_timer_ringtone_title" msgid="7588723137323327647">"Cichy"</string>
     <string name="ringtone" msgid="9110746249688559579">"Dzwonek"</string>
@@ -255,7 +256,7 @@
     <string name="cities_activity_title" msgid="8552462751129256730">"Miasta"</string>
     <string name="clock_settings" msgid="8317286807280600391">"Zegar"</string>
     <string name="clock_style" msgid="2265011060429742344">"Wygląd"</string>
-    <string name="open_date_settings" msgid="5915966847691221307">"Zmień datę i godzinę"</string>
+    <string name="open_date_settings" msgid="7712226973337806152">"Zmień datę i godzinę"</string>
   <string-array name="clock_style_entries">
     <item msgid="917900462224167608">"Analogowy"</item>
     <item msgid="8483930821046925592">"Cyfrowy"</item>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index 1b38ab3..7006287 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -35,6 +35,7 @@
     <string name="alarm_vibrate" msgid="3476686921490362230">"Vibrar"</string>
     <string name="alarm_repeat" msgid="7242985466344233206">"Repetir"</string>
     <string name="alert" msgid="6506982899651975645">"Toque do Alarme"</string>
+    <string name="default_alarm_ringtone_title" msgid="2916376768509623643">"Som de alarme predefinido"</string>
     <string name="default_timer_ringtone_title" msgid="1370799536406862317">"Temporizador expirado"</string>
     <string name="silent_timer_ringtone_title" msgid="7588723137323327647">"Silencioso"</string>
     <string name="ringtone" msgid="9110746249688559579">"Toque"</string>
@@ -233,7 +234,7 @@
     <string name="cities_activity_title" msgid="8552462751129256730">"Cidades"</string>
     <string name="clock_settings" msgid="8317286807280600391">"Relógio"</string>
     <string name="clock_style" msgid="2265011060429742344">"Estilo"</string>
-    <string name="open_date_settings" msgid="5915966847691221307">"Alterar data e hora"</string>
+    <string name="open_date_settings" msgid="7712226973337806152">"Alterar data e hora"</string>
   <string-array name="clock_style_entries">
     <item msgid="917900462224167608">"Analógico"</item>
     <item msgid="8483930821046925592">"Digital"</item>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index c29ecbe..c4e8b35 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -35,6 +35,7 @@
     <string name="alarm_vibrate" msgid="3476686921490362230">"Vibrar"</string>
     <string name="alarm_repeat" msgid="7242985466344233206">"Repetir"</string>
     <string name="alert" msgid="6506982899651975645">"Toque do alarme"</string>
+    <string name="default_alarm_ringtone_title" msgid="2916376768509623643">"Som de alarme padrão"</string>
     <string name="default_timer_ringtone_title" msgid="1370799536406862317">"Timer expirado"</string>
     <string name="silent_timer_ringtone_title" msgid="7588723137323327647">"Silencioso"</string>
     <string name="ringtone" msgid="9110746249688559579">"Toque"</string>
@@ -233,7 +234,7 @@
     <string name="cities_activity_title" msgid="8552462751129256730">"Cidades"</string>
     <string name="clock_settings" msgid="8317286807280600391">"Relógio"</string>
     <string name="clock_style" msgid="2265011060429742344">"Estilo"</string>
-    <string name="open_date_settings" msgid="5915966847691221307">"Alterar data e hora"</string>
+    <string name="open_date_settings" msgid="7712226973337806152">"Alterar data e hora"</string>
   <string-array name="clock_style_entries">
     <item msgid="917900462224167608">"Analógico"</item>
     <item msgid="8483930821046925592">"Digital"</item>
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index ab45eb0..72c7592 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -35,6 +35,7 @@
     <string name="alarm_vibrate" msgid="3476686921490362230">"Vibrare"</string>
     <string name="alarm_repeat" msgid="7242985466344233206">"Repetă"</string>
     <string name="alert" msgid="6506982899651975645">"Ton de apel pentru alarmă"</string>
+    <string name="default_alarm_ringtone_title" msgid="2916376768509623643">"Sunet de alarmă prestabilit"</string>
     <string name="default_timer_ringtone_title" msgid="1370799536406862317">"Temporizatorul a expirat"</string>
     <string name="silent_timer_ringtone_title" msgid="7588723137323327647">"Silențios"</string>
     <string name="ringtone" msgid="9110746249688559579">"Ton de apel"</string>
@@ -244,7 +245,7 @@
     <string name="cities_activity_title" msgid="8552462751129256730">"Orașe"</string>
     <string name="clock_settings" msgid="8317286807280600391">"Ceas"</string>
     <string name="clock_style" msgid="2265011060429742344">"Stil"</string>
-    <string name="open_date_settings" msgid="5915966847691221307">"Schimbați Data și ora"</string>
+    <string name="open_date_settings" msgid="7712226973337806152">"Schimbați data și ora"</string>
   <string-array name="clock_style_entries">
     <item msgid="917900462224167608">"Analogic"</item>
     <item msgid="8483930821046925592">"Digital"</item>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index 34e068d..337602f 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -35,6 +35,7 @@
     <string name="alarm_vibrate" msgid="3476686921490362230">"Вибросигнал"</string>
     <string name="alarm_repeat" msgid="7242985466344233206">"Повторять"</string>
     <string name="alert" msgid="6506982899651975645">"Звонок будильника"</string>
+    <string name="default_alarm_ringtone_title" msgid="2916376768509623643">"Звуковой сигнал по умолчанию"</string>
     <string name="default_timer_ringtone_title" msgid="1370799536406862317">"Время истекло"</string>
     <string name="silent_timer_ringtone_title" msgid="7588723137323327647">"Без звука"</string>
     <string name="ringtone" msgid="9110746249688559579">"Рингтон"</string>
@@ -255,7 +256,7 @@
     <string name="cities_activity_title" msgid="8552462751129256730">"Города"</string>
     <string name="clock_settings" msgid="8317286807280600391">"Часы"</string>
     <string name="clock_style" msgid="2265011060429742344">"Стиль"</string>
-    <string name="open_date_settings" msgid="5915966847691221307">"Изменить дату и время"</string>
+    <string name="open_date_settings" msgid="7712226973337806152">"Изменение даты и времени"</string>
   <string-array name="clock_style_entries">
     <item msgid="917900462224167608">"Стрелки"</item>
     <item msgid="8483930821046925592">"Цифры"</item>
diff --git a/res/values-si-rLK/strings.xml b/res/values-si-rLK/strings.xml
index b4df0e2..185eba5 100644
--- a/res/values-si-rLK/strings.xml
+++ b/res/values-si-rLK/strings.xml
@@ -35,6 +35,7 @@
     <string name="alarm_vibrate" msgid="3476686921490362230">"කම්පනය වීම"</string>
     <string name="alarm_repeat" msgid="7242985466344233206">"නැවත කරන්න"</string>
     <string name="alert" msgid="6506982899651975645">"ඇඟවීම් රිංග්ටෝනය"</string>
+    <string name="default_alarm_ringtone_title" msgid="2916376768509623643">"පෙරනිමි එලාම හඬ"</string>
     <string name="default_timer_ringtone_title" msgid="1370799536406862317">"ටයිමරය කල් ඉකුත් විය"</string>
     <string name="silent_timer_ringtone_title" msgid="7588723137323327647">"නිහඬ"</string>
     <string name="ringtone" msgid="9110746249688559579">"රිංග්ටෝනය"</string>
@@ -234,7 +235,7 @@
     <string name="cities_activity_title" msgid="8552462751129256730">"නගර"</string>
     <string name="clock_settings" msgid="8317286807280600391">"ඔරලෝසුව"</string>
     <string name="clock_style" msgid="2265011060429742344">"විලාසය"</string>
-    <string name="open_date_settings" msgid="5915966847691221307">"දිනය සහ වේලාව වෙනස් කරන්න"</string>
+    <string name="open_date_settings" msgid="7712226973337806152">"දිනය සහ වේලාව වෙනස් කරන්න"</string>
   <string-array name="clock_style_entries">
     <item msgid="917900462224167608">"ප්‍රතිසමය"</item>
     <item msgid="8483930821046925592">"ඩිජිටල්"</item>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index add4f04..8145d5b 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -35,6 +35,7 @@
     <string name="alarm_vibrate" msgid="3476686921490362230">"Vibrovať"</string>
     <string name="alarm_repeat" msgid="7242985466344233206">"Opakovať"</string>
     <string name="alert" msgid="6506982899651975645">"Tón zvonenia budíka"</string>
+    <string name="default_alarm_ringtone_title" msgid="2916376768509623643">"Predvolený zvuk budíka"</string>
     <string name="default_timer_ringtone_title" msgid="1370799536406862317">"Časovač vypršal"</string>
     <string name="silent_timer_ringtone_title" msgid="7588723137323327647">"Tichý"</string>
     <string name="ringtone" msgid="9110746249688559579">"Tón zvonenia"</string>
@@ -255,7 +256,7 @@
     <string name="cities_activity_title" msgid="8552462751129256730">"Mestá"</string>
     <string name="clock_settings" msgid="8317286807280600391">"Hodiny"</string>
     <string name="clock_style" msgid="2265011060429742344">"Štýl"</string>
-    <string name="open_date_settings" msgid="5915966847691221307">"Zmeniť dátum a čas"</string>
+    <string name="open_date_settings" msgid="7712226973337806152">"Zmeniť dátum a čas"</string>
   <string-array name="clock_style_entries">
     <item msgid="917900462224167608">"Analógové"</item>
     <item msgid="8483930821046925592">"Digitálne"</item>
diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml
index 9c5f515..64bbf4c 100644
--- a/res/values-sl/strings.xml
+++ b/res/values-sl/strings.xml
@@ -35,6 +35,7 @@
     <string name="alarm_vibrate" msgid="3476686921490362230">"Vibriranje"</string>
     <string name="alarm_repeat" msgid="7242985466344233206">"Ponovi"</string>
     <string name="alert" msgid="6506982899651975645">"Ton zvonjenja za alarm"</string>
+    <string name="default_alarm_ringtone_title" msgid="2916376768509623643">"Privzeti zvok alarma"</string>
     <string name="default_timer_ringtone_title" msgid="1370799536406862317">"Časovnik je potekel"</string>
     <string name="silent_timer_ringtone_title" msgid="7588723137323327647">"Tiho"</string>
     <string name="ringtone" msgid="9110746249688559579">"Melodija zvonjenja"</string>
@@ -255,7 +256,7 @@
     <string name="cities_activity_title" msgid="8552462751129256730">"Mesta"</string>
     <string name="clock_settings" msgid="8317286807280600391">"Ura"</string>
     <string name="clock_style" msgid="2265011060429742344">"Slog"</string>
-    <string name="open_date_settings" msgid="5915966847691221307">"Spreminjanje datuma in ure"</string>
+    <string name="open_date_settings" msgid="7712226973337806152">"Spreminjanje datuma in ure"</string>
   <string-array name="clock_style_entries">
     <item msgid="917900462224167608">"Analogno"</item>
     <item msgid="8483930821046925592">"Digitalno"</item>
diff --git a/res/values-sq-rAL/strings.xml b/res/values-sq-rAL/strings.xml
index 3ca6a48..2a4e673 100644
--- a/res/values-sq-rAL/strings.xml
+++ b/res/values-sq-rAL/strings.xml
@@ -35,6 +35,7 @@
     <string name="alarm_vibrate" msgid="3476686921490362230">"Dridhja"</string>
     <string name="alarm_repeat" msgid="7242985466344233206">"Përsërit"</string>
     <string name="alert" msgid="6506982899651975645">"Zilja e alarmit"</string>
+    <string name="default_alarm_ringtone_title" msgid="2916376768509623643">"Tingulli i parazgjedhur i alarmit sinjalizues"</string>
     <string name="default_timer_ringtone_title" msgid="1370799536406862317">"Kohëmatësi skadoi"</string>
     <string name="silent_timer_ringtone_title" msgid="7588723137323327647">"Në heshtje"</string>
     <string name="ringtone" msgid="9110746249688559579">"Zilja"</string>
@@ -234,7 +235,7 @@
     <string name="cities_activity_title" msgid="8552462751129256730">"Qytetet"</string>
     <string name="clock_settings" msgid="8317286807280600391">"Ora"</string>
     <string name="clock_style" msgid="2265011060429742344">"Stili"</string>
-    <string name="open_date_settings" msgid="5915966847691221307">"Ndërro datën dhe kohën"</string>
+    <string name="open_date_settings" msgid="7712226973337806152">"Ndërro datën dhe orën"</string>
   <string-array name="clock_style_entries">
     <item msgid="917900462224167608">"Analog"</item>
     <item msgid="8483930821046925592">"Dixhital"</item>
diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml
index b88158f..e1aa20c 100644
--- a/res/values-sr/strings.xml
+++ b/res/values-sr/strings.xml
@@ -35,6 +35,7 @@
     <string name="alarm_vibrate" msgid="3476686921490362230">"Вибрација"</string>
     <string name="alarm_repeat" msgid="7242985466344233206">"Понови"</string>
     <string name="alert" msgid="6506982899651975645">"Мелодија аларма"</string>
+    <string name="default_alarm_ringtone_title" msgid="2916376768509623643">"Подразумевани звук аларма"</string>
     <string name="default_timer_ringtone_title" msgid="1370799536406862317">"Тајмер је истекао"</string>
     <string name="silent_timer_ringtone_title" msgid="7588723137323327647">"Нечујно"</string>
     <string name="ringtone" msgid="9110746249688559579">"Мелодија звона"</string>
@@ -244,7 +245,7 @@
     <string name="cities_activity_title" msgid="8552462751129256730">"Градови"</string>
     <string name="clock_settings" msgid="8317286807280600391">"Сат"</string>
     <string name="clock_style" msgid="2265011060429742344">"Стил"</string>
-    <string name="open_date_settings" msgid="5915966847691221307">"Промените датум и време"</string>
+    <string name="open_date_settings" msgid="7712226973337806152">"Промени датум и време"</string>
   <string-array name="clock_style_entries">
     <item msgid="917900462224167608">"Аналогни"</item>
     <item msgid="8483930821046925592">"Дигитални"</item>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index 66bb405..99bea70 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -35,6 +35,7 @@
     <string name="alarm_vibrate" msgid="3476686921490362230">"Vibrera"</string>
     <string name="alarm_repeat" msgid="7242985466344233206">"Upprepa"</string>
     <string name="alert" msgid="6506982899651975645">"Ringsignal för alarm"</string>
+    <string name="default_alarm_ringtone_title" msgid="2916376768509623643">"Standardljud för alarm"</string>
     <string name="default_timer_ringtone_title" msgid="1370799536406862317">"Timern har upphört"</string>
     <string name="silent_timer_ringtone_title" msgid="7588723137323327647">"Tyst"</string>
     <string name="ringtone" msgid="9110746249688559579">"Ringsignal"</string>
@@ -233,7 +234,7 @@
     <string name="cities_activity_title" msgid="8552462751129256730">"Orter"</string>
     <string name="clock_settings" msgid="8317286807280600391">"Klocka"</string>
     <string name="clock_style" msgid="2265011060429742344">"Format"</string>
-    <string name="open_date_settings" msgid="5915966847691221307">"Ändra datum och tid"</string>
+    <string name="open_date_settings" msgid="7712226973337806152">"Ändra datum och tid"</string>
   <string-array name="clock_style_entries">
     <item msgid="917900462224167608">"Analog"</item>
     <item msgid="8483930821046925592">"Digital"</item>
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index e53b828..b6475a0 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -35,6 +35,7 @@
     <string name="alarm_vibrate" msgid="3476686921490362230">"Mtetemo"</string>
     <string name="alarm_repeat" msgid="7242985466344233206">"Rudia"</string>
     <string name="alert" msgid="6506982899651975645">"Mlio wa Kengele"</string>
+    <string name="default_alarm_ringtone_title" msgid="2916376768509623643">"Sauti chaguo-msingi ya kengele"</string>
     <string name="default_timer_ringtone_title" msgid="1370799536406862317">"Muda wa Kipima wakati Umemalizika"</string>
     <string name="silent_timer_ringtone_title" msgid="7588723137323327647">"Kimya"</string>
     <string name="ringtone" msgid="9110746249688559579">"Mlio wa simu"</string>
@@ -233,7 +234,7 @@
     <string name="cities_activity_title" msgid="8552462751129256730">"Miji"</string>
     <string name="clock_settings" msgid="8317286807280600391">"Saa"</string>
     <string name="clock_style" msgid="2265011060429742344">"Mtindo"</string>
-    <string name="open_date_settings" msgid="5915966847691221307">"Badilisha Tarehe na wakati"</string>
+    <string name="open_date_settings" msgid="7712226973337806152">"Badilisha tarehe na saa"</string>
   <string-array name="clock_style_entries">
     <item msgid="917900462224167608">"Analogi"</item>
     <item msgid="8483930821046925592">"Dijito"</item>
diff --git a/res/values-sw600dp-land/config.xml b/res/values-sw600dp-land/config.xml
deleted file mode 100644
index 5a1fd2b..0000000
--- a/res/values-sw600dp-land/config.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2013, 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.
-*/
--->
-
-<!-- These resources are around just to allow their values to be customized
-     for different hardware and product builds. -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <item type="integer" name="timer_column_count">2</item>
-</resources>
diff --git a/res/values-sw600dp/config.xml b/res/values-sw600dp/config.xml
index d9741ce..9fbceac 100644
--- a/res/values-sw600dp/config.xml
+++ b/res/values-sw600dp/config.xml
@@ -3,22 +3,22 @@
 /*
 ** Copyright 2011, 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 
+** 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 
+**     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 
+** 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.
 */
 -->
 
 <!-- These resources are around just to allow their values to be customized
      for different hardware and product builds. -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+<resources>
     <bool name="config_rotateAlarmAlert">true</bool>
 </resources>
diff --git a/res/values-ta-rIN/strings.xml b/res/values-ta-rIN/strings.xml
index c48565e..11d3ef7 100644
--- a/res/values-ta-rIN/strings.xml
+++ b/res/values-ta-rIN/strings.xml
@@ -35,6 +35,7 @@
     <string name="alarm_vibrate" msgid="3476686921490362230">"அதிர்வு"</string>
     <string name="alarm_repeat" msgid="7242985466344233206">"மீண்டும்"</string>
     <string name="alert" msgid="6506982899651975645">"அலார ரிங்டோன்"</string>
+    <string name="default_alarm_ringtone_title" msgid="2916376768509623643">"இயல்பு அலார ஒலி"</string>
     <string name="default_timer_ringtone_title" msgid="1370799536406862317">"டைமர் காலாவதியானது"</string>
     <string name="silent_timer_ringtone_title" msgid="7588723137323327647">"நிசப்தம்"</string>
     <string name="ringtone" msgid="9110746249688559579">"ரிங்டோன்"</string>
@@ -234,7 +235,7 @@
     <string name="cities_activity_title" msgid="8552462751129256730">"நகரங்கள்"</string>
     <string name="clock_settings" msgid="8317286807280600391">"கடிகாரம்"</string>
     <string name="clock_style" msgid="2265011060429742344">"வகை"</string>
-    <string name="open_date_settings" msgid="5915966847691221307">"தேதி &amp; நேரத்தை மாற்று"</string>
+    <string name="open_date_settings" msgid="7712226973337806152">"தேதி &amp; நேரத்தை மாற்று"</string>
   <string-array name="clock_style_entries">
     <item msgid="917900462224167608">"அனலாக்"</item>
     <item msgid="8483930821046925592">"டிஜிட்டல்"</item>
diff --git a/res/values-te-rIN/strings.xml b/res/values-te-rIN/strings.xml
index 4599da0..034c878 100644
--- a/res/values-te-rIN/strings.xml
+++ b/res/values-te-rIN/strings.xml
@@ -35,6 +35,7 @@
     <string name="alarm_vibrate" msgid="3476686921490362230">"వైబ్రేట్"</string>
     <string name="alarm_repeat" msgid="7242985466344233206">"పునరావృతం చేయి"</string>
     <string name="alert" msgid="6506982899651975645">"అలారం రింగ్‌టోన్"</string>
+    <string name="default_alarm_ringtone_title" msgid="2916376768509623643">"డిఫాల్ట్ అలారం ధ్వని"</string>
     <string name="default_timer_ringtone_title" msgid="1370799536406862317">"టైమర్ గడువు ముగిసింది"</string>
     <string name="silent_timer_ringtone_title" msgid="7588723137323327647">"నిశ్శబ్దం"</string>
     <string name="ringtone" msgid="9110746249688559579">"రింగ్‌టోన్"</string>
@@ -234,7 +235,7 @@
     <string name="cities_activity_title" msgid="8552462751129256730">"నగరాలు"</string>
     <string name="clock_settings" msgid="8317286807280600391">"గడియారం"</string>
     <string name="clock_style" msgid="2265011060429742344">"శైలి"</string>
-    <string name="open_date_settings" msgid="5915966847691221307">"తేదీ &amp; సమయం మార్చండి"</string>
+    <string name="open_date_settings" msgid="7712226973337806152">"తేదీ &amp; సమయాన్ని మార్చండి"</string>
   <string-array name="clock_style_entries">
     <item msgid="917900462224167608">"అనలాగ్"</item>
     <item msgid="8483930821046925592">"డిజిటల్"</item>
diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml
index c360f0f..3256631 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -35,6 +35,7 @@
     <string name="alarm_vibrate" msgid="3476686921490362230">"สั่น"</string>
     <string name="alarm_repeat" msgid="7242985466344233206">"ตั้งซ้ำ"</string>
     <string name="alert" msgid="6506982899651975645">"เสียงเตือน"</string>
+    <string name="default_alarm_ringtone_title" msgid="2916376768509623643">"เสียงปลุกเริ่มต้น"</string>
     <string name="default_timer_ringtone_title" msgid="1370799536406862317">"การจับเวลาสิ้นสุด"</string>
     <string name="silent_timer_ringtone_title" msgid="7588723137323327647">"เงียบ"</string>
     <string name="ringtone" msgid="9110746249688559579">"เสียงเรียกเข้า"</string>
@@ -233,7 +234,7 @@
     <string name="cities_activity_title" msgid="8552462751129256730">"เมือง"</string>
     <string name="clock_settings" msgid="8317286807280600391">"นาฬิกา"</string>
     <string name="clock_style" msgid="2265011060429742344">"รูปแบบ"</string>
-    <string name="open_date_settings" msgid="5915966847691221307">"เปลี่ยนแปลงวันที่และเวลา"</string>
+    <string name="open_date_settings" msgid="7712226973337806152">"เปลี่ยนแปลงวันที่และเวลา"</string>
   <string-array name="clock_style_entries">
     <item msgid="917900462224167608">"แอนะล็อก"</item>
     <item msgid="8483930821046925592">"ดิจิทัล"</item>
diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml
index cd19f03..b284e60 100644
--- a/res/values-tl/strings.xml
+++ b/res/values-tl/strings.xml
@@ -35,6 +35,7 @@
     <string name="alarm_vibrate" msgid="3476686921490362230">"I-vibrate"</string>
     <string name="alarm_repeat" msgid="7242985466344233206">"Ulitin"</string>
     <string name="alert" msgid="6506982899651975645">"Ringtone ng Alarm"</string>
+    <string name="default_alarm_ringtone_title" msgid="2916376768509623643">"Default na tunog ng alarm"</string>
     <string name="default_timer_ringtone_title" msgid="1370799536406862317">"Nag-expire na ang Timer"</string>
     <string name="silent_timer_ringtone_title" msgid="7588723137323327647">"Naka-silent"</string>
     <string name="ringtone" msgid="9110746249688559579">"Ringtone"</string>
@@ -233,7 +234,7 @@
     <string name="cities_activity_title" msgid="8552462751129256730">"Mga Lungsod"</string>
     <string name="clock_settings" msgid="8317286807280600391">"Orasan"</string>
     <string name="clock_style" msgid="2265011060429742344">"Istilo"</string>
-    <string name="open_date_settings" msgid="5915966847691221307">"Baguhin ang Petsa &amp; oras"</string>
+    <string name="open_date_settings" msgid="7712226973337806152">"Baguhin ang petsa at oras"</string>
   <string-array name="clock_style_entries">
     <item msgid="917900462224167608">"Analog"</item>
     <item msgid="8483930821046925592">"Digital"</item>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index a1312b7..b39495c 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -35,6 +35,7 @@
     <string name="alarm_vibrate" msgid="3476686921490362230">"Titreşim"</string>
     <string name="alarm_repeat" msgid="7242985466344233206">"Tekrarla"</string>
     <string name="alert" msgid="6506982899651975645">"Alarm Zil Sesi"</string>
+    <string name="default_alarm_ringtone_title" msgid="2916376768509623643">"Varsayılan alarm sesi"</string>
     <string name="default_timer_ringtone_title" msgid="1370799536406862317">"Zamanlayıcının Süresi Doldu"</string>
     <string name="silent_timer_ringtone_title" msgid="7588723137323327647">"Sessiz"</string>
     <string name="ringtone" msgid="9110746249688559579">"Zil sesi"</string>
@@ -233,7 +234,7 @@
     <string name="cities_activity_title" msgid="8552462751129256730">"Şehirler"</string>
     <string name="clock_settings" msgid="8317286807280600391">"Saat"</string>
     <string name="clock_style" msgid="2265011060429742344">"Stil"</string>
-    <string name="open_date_settings" msgid="5915966847691221307">"Tarih ve saati değiştir"</string>
+    <string name="open_date_settings" msgid="7712226973337806152">"Tarih ve saati değiştirin"</string>
   <string-array name="clock_style_entries">
     <item msgid="917900462224167608">"Analog"</item>
     <item msgid="8483930821046925592">"Dijital"</item>
diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml
index 7476d52..5586980 100644
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -35,6 +35,7 @@
     <string name="alarm_vibrate" msgid="3476686921490362230">"Вібросигнал"</string>
     <string name="alarm_repeat" msgid="7242985466344233206">"Повторити"</string>
     <string name="alert" msgid="6506982899651975645">"Звук сигналу"</string>
+    <string name="default_alarm_ringtone_title" msgid="2916376768509623643">"Сигнал будильника за умовчанням"</string>
     <string name="default_timer_ringtone_title" msgid="1370799536406862317">"Час таймера вичерпано"</string>
     <string name="silent_timer_ringtone_title" msgid="7588723137323327647">"Без звуку"</string>
     <string name="ringtone" msgid="9110746249688559579">"Сигнал дзвінка"</string>
@@ -255,7 +256,7 @@
     <string name="cities_activity_title" msgid="8552462751129256730">"Міста"</string>
     <string name="clock_settings" msgid="8317286807280600391">"Годинник"</string>
     <string name="clock_style" msgid="2265011060429742344">"Стиль"</string>
-    <string name="open_date_settings" msgid="5915966847691221307">"Змінити дату й час"</string>
+    <string name="open_date_settings" msgid="7712226973337806152">"Змінити дату й час"</string>
   <string-array name="clock_style_entries">
     <item msgid="917900462224167608">"Аналоговий"</item>
     <item msgid="8483930821046925592">"Цифровий"</item>
diff --git a/res/values-ur-rPK/strings.xml b/res/values-ur-rPK/strings.xml
index eb43d0c..19bf977 100644
--- a/res/values-ur-rPK/strings.xml
+++ b/res/values-ur-rPK/strings.xml
@@ -35,6 +35,7 @@
     <string name="alarm_vibrate" msgid="3476686921490362230">"وائبریٹ"</string>
     <string name="alarm_repeat" msgid="7242985466344233206">"دہرائیں"</string>
     <string name="alert" msgid="6506982899651975645">"الارم رنگ ٹون"</string>
+    <string name="default_alarm_ringtone_title" msgid="2916376768509623643">"الارم کی ڈیفالٹ آواز"</string>
     <string name="default_timer_ringtone_title" msgid="1370799536406862317">"ٹائمر کی معیاد ختم ہو گئی"</string>
     <string name="silent_timer_ringtone_title" msgid="7588723137323327647">"خاموش"</string>
     <string name="ringtone" msgid="9110746249688559579">"رنگ ٹون"</string>
@@ -234,7 +235,7 @@
     <string name="cities_activity_title" msgid="8552462751129256730">"شہر"</string>
     <string name="clock_settings" msgid="8317286807280600391">"گھڑی"</string>
     <string name="clock_style" msgid="2265011060429742344">"طرز"</string>
-    <string name="open_date_settings" msgid="5915966847691221307">"تاریخ اور وقت تبدیل کریں"</string>
+    <string name="open_date_settings" msgid="7712226973337806152">"تاریخ اور وقت تبدیل کریں"</string>
   <string-array name="clock_style_entries">
     <item msgid="917900462224167608">"اینالاگ"</item>
     <item msgid="8483930821046925592">"ڈیجیٹل"</item>
diff --git a/res/values-uz-rUZ/strings.xml b/res/values-uz-rUZ/strings.xml
index 88d7415..2ac930f 100644
--- a/res/values-uz-rUZ/strings.xml
+++ b/res/values-uz-rUZ/strings.xml
@@ -35,6 +35,7 @@
     <string name="alarm_vibrate" msgid="3476686921490362230">"Tebranish"</string>
     <string name="alarm_repeat" msgid="7242985466344233206">"Takrorlash"</string>
     <string name="alert" msgid="6506982899651975645">"Uyg‘otkich ringtoni"</string>
+    <string name="default_alarm_ringtone_title" msgid="2916376768509623643">"Birlamchi uyg‘otkich ovozi"</string>
     <string name="default_timer_ringtone_title" msgid="1370799536406862317">"Taymer vaqti tugadi"</string>
     <string name="silent_timer_ringtone_title" msgid="7588723137323327647">"Ovozsiz"</string>
     <string name="ringtone" msgid="9110746249688559579">"Rington"</string>
@@ -233,7 +234,7 @@
     <string name="cities_activity_title" msgid="8552462751129256730">"Shaharlar"</string>
     <string name="clock_settings" msgid="8317286807280600391">"Soat"</string>
     <string name="clock_style" msgid="2265011060429742344">"Soat turi"</string>
-    <string name="open_date_settings" msgid="5915966847691221307">"Sana va vaqtni o‘zgartirish"</string>
+    <string name="open_date_settings" msgid="7712226973337806152">"Sana va vaqtni o‘zgartirish"</string>
   <string-array name="clock_style_entries">
     <item msgid="917900462224167608">"Soat mili"</item>
     <item msgid="8483930821046925592">"Raqamli"</item>
diff --git a/res/values-v17/config.xml b/res/values-v17/config.xml
deleted file mode 100644
index 79eaf8a..0000000
--- a/res/values-v17/config.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2012, 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.
-*/
--->
-
-<!-- These resources are around just to allow their values to be customized
-     for different hardware and product builds. -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- no need for the dock app, we have Dreams now -->
-    <bool name="config_dockAppEnabled">false</bool>
-</resources>
diff --git a/res/values-v21/styles.xml b/res/values-v21/styles.xml
index f9f0763..75cbe5f 100644
--- a/res/values-v21/styles.xml
+++ b/res/values-v21/styles.xml
@@ -14,23 +14,7 @@
      limitations under the License.
 -->
 
-<resources>
-
-    <style name="SettingsTheme" parent="TranslucentDecorActivityTheme">
-        <item name="android:alertDialogTheme">@style/SettingsAlertDialogTheme</item>
-        <item name="android:detailsElementBackground">@null</item>
-
-        <!-- Attributes from support.v7.appcompat -->
-        <item name="actionBarStyle">@style/SettingsActionBarStyle</item>
-        <item name="colorControlActivated">?attr/colorAccent</item>
-        <item name="windowActionBar">true</item>
-        <item name="windowNoTitle">false</item>
-    </style>
-
-    <!-- Theme that will be used by PreferenceFragment (i.e. *not* AppCompat). -->
-    <style name="SettingsAlertDialogTheme" parent="android:Theme.Material.Dialog.Alert">
-        <item name="android:colorAccent">?attr/colorAccent</item>
-    </style>
+<resources xmlns:tools="http://schemas.android.com/tools">
 
     <!-- Custom notification content styles -->
     <!-- copied TextAppearance.Material.Button -->
@@ -41,38 +25,37 @@
         <item name="android:textColor">@color/black_54p</item>
     </style>
 
-    <style name="TextAppearance.StatusBar.EventContent">
+    <style name="TextAppearance.StatusBar.EventContent" tools:ignore="PrivateResource">
         <item name="android:textSize">14sp</item>
         <item name="android:textColor">@color/black_54p</item>
     </style>
-    <style name="TextAppearance.StatusBar.EventContent.Title">
+    <style name="TextAppearance.StatusBar.EventContent.Title" tools:ignore="PrivateResource">
         <item name="android:textSize">16sp</item>
         <item name="android:textColor">@color/black_87p</item>
     </style>
 
     <style name="TimePickerTheme" parent="Theme.AppCompat.Dialog">
-        <item name="android:background">@color/time_picker_gray</item>
+        <item name="android:textColorPrimary">?attr/colorAccent</item>
+        <item name="android:textColorPrimaryInverse">?android:attr/textColorPrimary</item>
+        <item name="android:textColorSecondaryInverse">@color/white</item>
         <item name="android:timePickerStyle">@style/TimePickerStyle</item>
-        <item name="android:textColorPrimaryInverse">?attr/colorAccent</item>
+        <item name="android:windowBackground">@color/time_picker_gray</item>
 
         <!-- Attributes from support.v7.appcompat -->
         <item name="colorAccent">@color/color_accent</item>
     </style>
 
-    <style name="TimePickerStyle" parent="@android:style/Widget.Material.TimePicker" >
-        <item name="android:headerBackground">@color/time_picker_gray</item>
-        <item name="android:headerTimeTextAppearance">@style/TimeLabelTextAppearance</item>
-        <item name="android:headerAmPmTextAppearance">@style/AmPmTextAppearance</item>
-        <item name="android:numbersBackgroundColor">@color/time_picker_gray</item>
+    <style name="TimePickerStyle" parent="android:Widget.Material.TimePicker" >
+        <item name="android:headerBackground">@android:color/transparent</item>
+        <item name="android:numbersBackgroundColor">@android:color/transparent</item>
         <item name="android:numbersTextColor">@color/white</item>
-        <item name="android:amPmTextColor">@color/white</item>
     </style>
 
-    <style name="PrimaryLabelTextAppearance" parent="@style/PrimaryLabelTextParentAppearance">
+    <style name="PrimaryLabelTextAppearance" parent="PrimaryLabelTextParentAppearance">
         <item name="android:fontFamily">sans-serif-medium</item>
     </style>
 
-    <style name="SecondaryLabelTextAppearance" parent="@style/SecondaryLabelTextParentAppearance">
+    <style name="SecondaryLabelTextAppearance" parent="SecondaryLabelTextParentAppearance">
         <item name="android:fontFamily">sans-serif-medium</item>
     </style>
 
diff --git a/res/values-v23/styles.xml b/res/values-v23/styles.xml
index f394ee1..98c880c 100644
--- a/res/values-v23/styles.xml
+++ b/res/values-v23/styles.xml
@@ -1,14 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
 <resources>
-    <style name="TimeLabelTextAppearance" parent="TextAppearance.AppCompat">
-        <item name="android:textColor">@color/timepicker_time</item>
-        <item name="android:textSize">60dp</item>
-    </style>
-
-    <style name="AmPmTextAppearance" parent="TextAppearance.AppCompat">
-        <item name="android:textColor">@color/timepicker_ampm</item>
-        <item name="android:textSize">16sp</item>
-    </style>
 
     <style name="widget_big_thin" parent="big_thin">
         <item name="android:textSize">@dimen/widget_big_font_size</item>
@@ -26,4 +17,5 @@
         <item name="android:shadowColor">@color/digital_widget_shadow_color</item>
         <item name="android:shadowDy">@dimen/digital_widget_shadow_dy</item>
     </style>
-</resources>
\ No newline at end of file
+
+</resources>
diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml
index 1ca7a0a..db42258 100644
--- a/res/values-vi/strings.xml
+++ b/res/values-vi/strings.xml
@@ -35,6 +35,7 @@
     <string name="alarm_vibrate" msgid="3476686921490362230">"Rung"</string>
     <string name="alarm_repeat" msgid="7242985466344233206">"Lặp lại"</string>
     <string name="alert" msgid="6506982899651975645">"Nhạc chuông báo thức"</string>
+    <string name="default_alarm_ringtone_title" msgid="2916376768509623643">"Âm thanh báo thức mặc định"</string>
     <string name="default_timer_ringtone_title" msgid="1370799536406862317">"Bộ hẹn giờ đã hết hạn"</string>
     <string name="silent_timer_ringtone_title" msgid="7588723137323327647">"Im lặng"</string>
     <string name="ringtone" msgid="9110746249688559579">"Nhạc chuông"</string>
@@ -233,7 +234,7 @@
     <string name="cities_activity_title" msgid="8552462751129256730">"Thành phố"</string>
     <string name="clock_settings" msgid="8317286807280600391">"Đồng hồ"</string>
     <string name="clock_style" msgid="2265011060429742344">"Kiểu"</string>
-    <string name="open_date_settings" msgid="5915966847691221307">"Thay đổi ngày và giờ"</string>
+    <string name="open_date_settings" msgid="7712226973337806152">"Thay đổi ngày và giờ"</string>
   <string-array name="clock_style_entries">
     <item msgid="917900462224167608">"Đồng hồ kim"</item>
     <item msgid="8483930821046925592">"Đồng hồ số"</item>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index f00f207..0c97ec3 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -35,6 +35,7 @@
     <string name="alarm_vibrate" msgid="3476686921490362230">"振动"</string>
     <string name="alarm_repeat" msgid="7242985466344233206">"重复"</string>
     <string name="alert" msgid="6506982899651975645">"闹钟铃声"</string>
+    <string name="default_alarm_ringtone_title" msgid="2916376768509623643">"默认闹钟提示音"</string>
     <string name="default_timer_ringtone_title" msgid="1370799536406862317">"计时器已结束"</string>
     <string name="silent_timer_ringtone_title" msgid="7588723137323327647">"静音"</string>
     <string name="ringtone" msgid="9110746249688559579">"铃声"</string>
@@ -233,7 +234,7 @@
     <string name="cities_activity_title" msgid="8552462751129256730">"城市"</string>
     <string name="clock_settings" msgid="8317286807280600391">"时钟"</string>
     <string name="clock_style" msgid="2265011060429742344">"样式"</string>
-    <string name="open_date_settings" msgid="5915966847691221307">"更改日期和时间"</string>
+    <string name="open_date_settings" msgid="7712226973337806152">"更改日期和时间"</string>
   <string-array name="clock_style_entries">
     <item msgid="917900462224167608">"指针"</item>
     <item msgid="8483930821046925592">"数字"</item>
diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml
index f5f77ed..fce52bf 100644
--- a/res/values-zh-rHK/strings.xml
+++ b/res/values-zh-rHK/strings.xml
@@ -35,6 +35,7 @@
     <string name="alarm_vibrate" msgid="3476686921490362230">"震動"</string>
     <string name="alarm_repeat" msgid="7242985466344233206">"重複"</string>
     <string name="alert" msgid="6506982899651975645">"鬧鐘鈴聲"</string>
+    <string name="default_alarm_ringtone_title" msgid="2916376768509623643">"預設鬧鐘音效"</string>
     <string name="default_timer_ringtone_title" msgid="1370799536406862317">"計時器已逾時"</string>
     <string name="silent_timer_ringtone_title" msgid="7588723137323327647">"靜音"</string>
     <string name="ringtone" msgid="9110746249688559579">"鈴聲"</string>
@@ -233,7 +234,7 @@
     <string name="cities_activity_title" msgid="8552462751129256730">"城市"</string>
     <string name="clock_settings" msgid="8317286807280600391">"時鐘"</string>
     <string name="clock_style" msgid="2265011060429742344">"樣式"</string>
-    <string name="open_date_settings" msgid="5915966847691221307">"更改日期和時間"</string>
+    <string name="open_date_settings" msgid="7712226973337806152">"變更日期和時間"</string>
   <string-array name="clock_style_entries">
     <item msgid="917900462224167608">"類比"</item>
     <item msgid="8483930821046925592">"數碼"</item>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index f8f0832..096b377 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -35,6 +35,7 @@
     <string name="alarm_vibrate" msgid="3476686921490362230">"震動"</string>
     <string name="alarm_repeat" msgid="7242985466344233206">"重複"</string>
     <string name="alert" msgid="6506982899651975645">"鬧鐘鈴聲"</string>
+    <string name="default_alarm_ringtone_title" msgid="2916376768509623643">"預設鬧鐘音效"</string>
     <string name="default_timer_ringtone_title" msgid="1370799536406862317">"計時完畢"</string>
     <string name="silent_timer_ringtone_title" msgid="7588723137323327647">"靜音"</string>
     <string name="ringtone" msgid="9110746249688559579">"鈴聲"</string>
@@ -233,7 +234,7 @@
     <string name="cities_activity_title" msgid="8552462751129256730">"城市"</string>
     <string name="clock_settings" msgid="8317286807280600391">"時鐘"</string>
     <string name="clock_style" msgid="2265011060429742344">"樣式"</string>
-    <string name="open_date_settings" msgid="5915966847691221307">"變更日期和時間"</string>
+    <string name="open_date_settings" msgid="7712226973337806152">"變更日期和時間"</string>
   <string-array name="clock_style_entries">
     <item msgid="917900462224167608">"類比"</item>
     <item msgid="8483930821046925592">"數位"</item>
diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml
index e88bf40..8e5bdc4 100644
--- a/res/values-zu/strings.xml
+++ b/res/values-zu/strings.xml
@@ -35,6 +35,7 @@
     <string name="alarm_vibrate" msgid="3476686921490362230">"Dlidliza"</string>
     <string name="alarm_repeat" msgid="7242985466344233206">"Phindaphinda"</string>
     <string name="alert" msgid="6506982899651975645">"Ithoni yokukhala ye-alamu"</string>
+    <string name="default_alarm_ringtone_title" msgid="2916376768509623643">"Umsindo we-alamu ozenzakalelayo"</string>
     <string name="default_timer_ringtone_title" msgid="1370799536406862317">"Isibali sikhathi siphelelwe yisikhathi"</string>
     <string name="silent_timer_ringtone_title" msgid="7588723137323327647">"Thulile"</string>
     <string name="ringtone" msgid="9110746249688559579">"Ithoni yokukhala"</string>
@@ -233,7 +234,7 @@
     <string name="cities_activity_title" msgid="8552462751129256730">"Amadolobha"</string>
     <string name="clock_settings" msgid="8317286807280600391">"Iwashi"</string>
     <string name="clock_style" msgid="2265011060429742344">"Isitayela"</string>
-    <string name="open_date_settings" msgid="5915966847691221307">"Guqula idethi nesikhathi"</string>
+    <string name="open_date_settings" msgid="7712226973337806152">"Shintsha idethi nesikhathi"</string>
   <string-array name="clock_style_entries">
     <item msgid="917900462224167608">"I-Analog"</item>
     <item msgid="8483930821046925592">"Idijithali"</item>
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 1e0151c..59f6133 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -16,12 +16,6 @@
   -->
 <resources>
 
-    <declare-styleable name="AnalogClock">
-        <attr name="jewelRadius" format="dimension"/>
-        <attr name="jewelOffset" format="dimension"/>
-        <attr name="jewelColor" format="color"/>
-    </declare-styleable>
-
     <declare-styleable name="TextTime">
         <!-- Specifies the formatting pattern used to show the time and/or date
              in 12-hour mode. Please refer to {@link android.text.format.DateFormat}
diff --git a/res/values/config.xml b/res/values/config.xml
index 855923d..6c879c7 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -17,12 +17,7 @@
 <!-- These resources are around just to allow their values to be customized
      for different hardware and product builds. -->
 <resources>
-    <bool name="config_requiresScreenSaver">true</bool>
-    <bool name="config_dockAppEnabled">true</bool>
     <bool name="config_rotateAlarmAlert">false</bool>
-    <item type="integer" name="timer_column_count">1</item>
     <!-- Number of world clocks in a row, for the clock tab. -->
     <item type="integer" name="world_clocks_per_row">1</item>
-    <!-- Number of world clocks in a row, for the digital appwidget. -->
-    <item type="integer" name="appwidget_world_clocks_per_row">2</item>
 </resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 4184cbc..f68c7df 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -73,7 +73,6 @@
     <dimen name="label_font_size">16sp</dimen>
     <!-- Use dp on purpose to ensure consistency across multiple screen sizes. -->
     <dimen name="widget_label_font_size">14dp</dimen>
-    <item name="ampm_font_size_scale" type="fraction">50%</item>
     <item name="reduced_clock_font_size_scale" type="fraction">65%</item>
     <dimen name="header_font_size">24sp</dimen>
     <dimen name="body_font_size">18sp</dimen>
@@ -91,6 +90,7 @@
     <dimen name="dialog_button_font_size">16sp</dimen>
 
     <dimen name="alarm_label_size">14sp</dimen>
+    <dimen name="alarm_icon_padding">6dp</dimen>
 
     <dimen name="medium_font_padding">12dp</dimen>
 
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 5f18d1d..026b4dd 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -76,6 +76,9 @@
     <!-- Setting labels on Set alarm screen: Select alarm ringtone  -->
     <string name="alert">Alarm Ringtone</string>
 
+    <!-- Title of default ringtone played when an alarm triggers. -->
+    <string name="default_alarm_ringtone_title">Default alarm sound</string>
+
     <!-- Title of default ringtone played when a timer expires. -->
     <string name="default_timer_ringtone_title">Timer Expired</string>
 
@@ -347,6 +350,12 @@
     <!-- Title for ringtones that cannot be located by uri. -->
     <string name="unknown_ringtone_title">Unknown</string>
 
+    <!-- Text to display when alarm volume is muted. -->
+    <string name="alarm_volume_muted">Alarm volume muted</string>
+
+    <!-- Text to display when silent ringtone is selected. -->
+    <string name="silent_ringtone">Silent ringtone</string>
+
     <!-- Text to display in the small text of the notification -->
     <string name="alarm_notify_text">Snooze or dismiss alarm.</string>
 
@@ -571,7 +580,7 @@
     <string name="clock_style">Style</string>
 
     <!-- Title for preference to change date & time -->
-    <string name="open_date_settings">Change Date \u0026 time</string>
+    <string name="open_date_settings">Change date \u0026 time</string>
 
     <!-- Entries listed in the ListPreference when invoking the clock style
          preference. -->
@@ -824,6 +833,8 @@
     <string name="timer_settings">Timers</string>
     <!-- Description for timer ringtone setting. -->
     <string name="timer_ringtone_title">Timer ringtone</string>
+    <!-- Description for timer vibration. -->
+    <string name="timer_vibrate_title">Timer vibrate</string>
 
     <!-- Description when timer is stopped. -->
     <string name="timer_stopped">Timer stopped</string>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index ce89f11..fe9fcb3 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -23,10 +23,10 @@
     </style>
 
     <style name="BaseActivityTheme" parent="Theme.AppCompat">
-        <item name="android:imageButtonStyle">@style/ImageButtonStyle</item>
         <item name="android:windowBackground">@color/default_background</item>
 
         <item name="alertDialogTheme">@style/AlertDialogTheme</item>
+        <item name="imageButtonStyle">@style/ImageButtonStyle</item>
         <item name="colorAccent">@color/color_accent</item>
         <item name="colorPrimaryDark">@color/status_bar</item>
         <item name="colorControlActivated">@color/white</item>
@@ -53,16 +53,6 @@
         <item name="colorControlActivated">?attr/colorAccent</item>
     </style>
 
-    <style name="TimeLabelTextAppearance" parent="TextAppearance.AppCompat">
-        <item name="android:textColor">@color/white</item>
-        <item name="android:textSize" tools:ignore="SpUsage">60dp</item>
-    </style>
-
-    <style name="AmPmTextAppearance" parent="TextAppearance.AppCompat">
-        <item name="android:textColor">@color/white</item>
-        <item name="android:textSize">16sp</item>
-    </style>
-
     <style name="DeskClockTabBaseStyle" parent="Widget.AppCompat.ActionBar.TabView">
         <item name="android:paddingLeft">@dimen/actionbar_tab_padding</item>
         <item name="android:paddingRight">@dimen/actionbar_tab_padding</item>
@@ -73,10 +63,18 @@
         <item name="android:detailsElementBackground">@null</item>
         <!-- Attributes from support.v7.appcompat -->
         <item name="actionBarStyle">@style/SettingsActionBarStyle</item>
+        <item name="alertDialogTheme">@style/SettingsAlertDialogTheme</item>
+        <item name="colorControlActivated">?attr/colorAccent</item>
+        <item name="preferenceTheme">@style/PreferenceThemeOverlay.v14.Material</item>
         <item name="windowActionBar">true</item>
         <item name="windowNoTitle">false</item>
     </style>
 
+    <!-- Theme that will be used by PreferenceFragment. -->
+    <style name="SettingsAlertDialogTheme" parent="Theme.AppCompat.Dialog.Alert">
+        <item name="colorAccent">@color/color_accent</item>
+    </style>
+
     <style name="SettingsActionBarStyle" parent="@style/Widget.AppCompat.Toolbar">
         <item name="android:background">@null</item>
 
@@ -111,7 +109,7 @@
 
     <style name="ScreensaverActivityTheme" parent="Theme.AppCompat.NoActionBar" />
 
-    <style name="ImageButtonStyle" parent="android:Widget.DeviceDefault.ImageButton">
+    <style name="ImageButtonStyle" parent="Widget.AppCompat.ImageButton">
         <item name="android:background">?attr/selectableItemBackgroundBorderless</item>
     </style>
 
@@ -276,12 +274,12 @@
     </style>
 
     <!-- Notification content styles -->
-    <style name="TextAppearance.StatusBar.EventContent">
+    <style name="TextAppearance.StatusBar.EventContent" tools:ignore="PrivateResource">
         <item name="android:textSize">@dimen/notification_text_size</item>
         <item name="android:textColor">@color/notif_text_grey</item>
     </style>
 
-    <style name="TextAppearance.StatusBar.EventContent.Title">
+    <style name="TextAppearance.StatusBar.EventContent.Title" tools:ignore="PrivateResource">
         <item name="android:textSize">@dimen/notification_title_text_size</item>
         <item name="android:textColor">@color/white</item>
     </style>
diff --git a/res/xml/settings.xml b/res/xml/settings.xml
index 37b416a..4c68956 100644
--- a/res/xml/settings.xml
+++ b/res/xml/settings.xml
@@ -53,19 +53,18 @@
             android:defaultValue="10"
             android:dialogTitle="@string/auto_silence_title" />
 
-       <com.android.deskclock.settings.SnoozeLengthDialog
+        <com.android.deskclock.settings.SnoozeLengthDialogPreference
             android:key="snooze_duration"
-            android:title="@string/snooze_duration_title"
-            android:defaultValue="10"/>
+            android:title="@string/snooze_duration_title" />
 
-        <Preference
+        <com.android.deskclock.settings.AlarmVolumePreference
             android:key="volume_setting"
-            android:title="@string/alarm_volume_title" />
+            android:title="@string/alarm_volume_title"
+            android:layout="@layout/alarm_volume_preference"/>
 
-        <com.android.deskclock.settings.CrescendoLengthDialog
+        <com.android.deskclock.settings.CrescendoLengthDialogPreference
             android:key="alarm_crescendo_duration"
-            android:title="@string/crescendo_duration_title"
-            android:defaultValue="0"/>
+            android:title="@string/crescendo_duration_title" />
 
         <ListPreference
             android:key="volume_button_setting"
@@ -85,15 +84,18 @@
 
     <PreferenceCategory
         android:title="@string/timer_settings">
-        <com.android.deskclock.settings.TimerRingtonePreference
+        <Preference
             android:key="timer_ringtone"
-            android:title="@string/timer_ringtone_title"
-            android:ringtoneType="alarm" />
+            android:title="@string/timer_ringtone_title" />
 
-        <com.android.deskclock.settings.CrescendoLengthDialog
+        <com.android.deskclock.settings.CrescendoLengthDialogPreference
             android:key="timer_crescendo_duration"
-            android:title="@string/crescendo_duration_title"
-            android:defaultValue="0"/>
+            android:title="@string/crescendo_duration_title" />
+
+        <SwitchPreference
+            android:key="timer_vibrate"
+            android:title="@string/timer_vibrate_title"
+            android:defaultValue="false" />
     </PreferenceCategory>
 
 </PreferenceScreen>
diff --git a/src/com/android/alarmclock/DigitalAppWidgetProvider.java b/src/com/android/alarmclock/DigitalAppWidgetProvider.java
index 5b22544..6b17e4d 100644
--- a/src/com/android/alarmclock/DigitalAppWidgetProvider.java
+++ b/src/com/android/alarmclock/DigitalAppWidgetProvider.java
@@ -24,144 +24,145 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.res.Resources;
 import android.net.Uri;
 import android.os.Bundle;
 import android.support.annotation.NonNull;
 import android.text.TextUtils;
 import android.text.format.DateFormat;
 import android.util.Log;
-import android.util.TypedValue;
 import android.view.View;
 import android.widget.RemoteViews;
 
 import com.android.deskclock.HandleDeskClockApiCalls;
 import com.android.deskclock.R;
 import com.android.deskclock.Utils;
-import com.android.deskclock.alarms.AlarmStateManager;
 import com.android.deskclock.data.DataModel;
 import com.android.deskclock.worldclock.CitySelectionActivity;
 
 import java.util.Locale;
 
+import static android.app.AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED;
+import static android.app.AlarmManager.RTC;
+import static android.app.PendingIntent.FLAG_NO_CREATE;
+import static android.app.PendingIntent.FLAG_UPDATE_CURRENT;
+import static android.content.Intent.ACTION_DATE_CHANGED;
+import static android.content.Intent.ACTION_LOCALE_CHANGED;
+import static android.content.Intent.ACTION_SCREEN_ON;
+import static android.content.Intent.ACTION_TIMEZONE_CHANGED;
+import static android.content.Intent.ACTION_TIME_CHANGED;
+import static android.util.TypedValue.COMPLEX_UNIT_PX;
+import static com.android.deskclock.alarms.AlarmStateManager.SYSTEM_ALARM_CHANGE_ACTION;
+import static com.android.deskclock.data.DataModel.ACTION_DIGITAL_WIDGET_CHANGED;
+
 public class DigitalAppWidgetProvider extends AppWidgetProvider {
+
     private static final String TAG = "DigAppWidgetProvider";
 
-    /**
-     * Intent to be used for checking if a world clock's date has changed. Must be every fifteen
-     * minutes because not all time zones are hour-locked.
-     **/
-    public static final String ACTION_ON_QUARTER_HOUR = "com.android.deskclock.ON_QUARTER_HOUR";
+    /** Intent action used for refreshing a world clock's date. See Nepal timezone: UTC+05:45. */
+    private static final String ACTION_ON_QUARTER_HOUR = "com.android.deskclock.ON_QUARTER_HOUR";
 
-    // Lazily creating this intent to use with the AlarmManager
-    private PendingIntent mPendingIntent;
+    /** Intent used to deliver the {@link #ACTION_ON_QUARTER_HOUR} callback. */
+    private static final Intent QUARTER_HOUR_INTENT = new Intent(ACTION_ON_QUARTER_HOUR);
+
     // Lazily creating this name to use with the AppWidgetManager
     private ComponentName mComponentName;
 
-    public DigitalAppWidgetProvider() {
-    }
-
     @Override
     public void onEnabled(Context context) {
         super.onEnabled(context);
-        startAlarmOnQuarterHour(context);
+
+        // Schedule the quarter-hour callback if necessary.
+        updateQuarterHourCallback(context);
     }
 
     @Override
     public void onDisabled(Context context) {
         super.onDisabled(context);
-        cancelAlarmOnQuarterHour(context);
+
+        // Remove any scheduled quarter-hour callback.
+        removeQuarterHourCallback(context);
     }
 
     @Override
     public void onReceive(@NonNull Context context, @NonNull Intent intent) {
-        String action = intent.getAction();
+        final String action = intent.getAction();
         if (DigitalAppWidgetService.LOGGING) {
             Log.i(TAG, "onReceive: " + action);
         }
         super.onReceive(context, intent);
 
-        if (ACTION_ON_QUARTER_HOUR.equals(action)
-                || Intent.ACTION_DATE_CHANGED.equals(action)
-                || Intent.ACTION_TIMEZONE_CHANGED.equals(action)
-                || Intent.ACTION_TIME_CHANGED.equals(action)
-                || Intent.ACTION_LOCALE_CHANGED.equals(action)) {
-            AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
-            if (appWidgetManager != null) {
-                int[] appWidgetIds = appWidgetManager.getAppWidgetIds(getComponentName(context));
+        final AppWidgetManager widgetManager = AppWidgetManager.getInstance(context);
+        if (widgetManager == null) {
+            return;
+        }
+        final int[] appWidgetIds = widgetManager.getAppWidgetIds(getComponentName(context));
+
+        switch (action) {
+            case ACTION_DATE_CHANGED:
+            case ACTION_TIME_CHANGED:
+            case ACTION_LOCALE_CHANGED:
+            case ACTION_ON_QUARTER_HOUR:
+            case ACTION_TIMEZONE_CHANGED:
+                // Time has changed so reschedule the next quarter-hour callback.
+                updateQuarterHourCallback(context);
+
+                final String pName = context.getPackageName();
                 for (int appWidgetId : appWidgetIds) {
-                    appWidgetManager.
-                            notifyAppWidgetViewDataChanged(appWidgetId,
-                                    R.id.digital_appwidget_listview);
-                    RemoteViews widget = new RemoteViews(context.getPackageName(),
-                            R.layout.digital_appwidget);
-                    float ratio = WidgetUtils.getScaleRatio(context, null, appWidgetId);
-                    WidgetUtils.setTimeFormat(context, widget, false /* showAmPm */,
-                            R.id.the_clock);
-                    WidgetUtils.setClockSize(context, widget, ratio);
+                    widgetManager.notifyAppWidgetViewDataChanged(appWidgetId,
+                            R.id.digital_appwidget_listview);
+                    final RemoteViews widget = new RemoteViews(pName, R.layout.digital_appwidget);
+                    final float ratio = WidgetUtils.getScaleRatio(context, null, appWidgetId);
                     refreshAlarm(context, widget, ratio);
-                    appWidgetManager.partiallyUpdateAppWidget(appWidgetId, widget);
+                    WidgetUtils.setClockSize(context, widget, ratio);
+                    WidgetUtils.setTimeFormat(context, widget, 0.4f /* amPmRatio */, R.id.clock);
+                    widgetManager.partiallyUpdateAppWidget(appWidgetId, widget);
                 }
-            }
-            if(!ACTION_ON_QUARTER_HOUR.equals(action)) {
-                cancelAlarmOnQuarterHour(context);
-            }
-            startAlarmOnQuarterHour(context);
-        } else if (isNextAlarmChangedAction(action)
-                || Intent.ACTION_SCREEN_ON.equals(action)
-                || DataModel.ACTION_DIGITAL_WIDGET_CHANGED.equals(action)) {
-            AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
-            if (appWidgetManager != null) {
-                int[] appWidgetIds = appWidgetManager.getAppWidgetIds(getComponentName(context));
+
+                break;
+
+            case ACTION_DIGITAL_WIDGET_CHANGED:
+                // Selected cities have changed so schedule/remove the next quarter-hour callback.
+                updateQuarterHourCallback(context);
+            case ACTION_SCREEN_ON:
+            case SYSTEM_ALARM_CHANGE_ACTION:
+            case ACTION_NEXT_ALARM_CLOCK_CHANGED:
                 for (int appWidgetId : appWidgetIds) {
                     final float ratio = WidgetUtils.getScaleRatio(context, null, appWidgetId);
-                    updateClock(context, appWidgetManager, appWidgetId, ratio);
+                    updateClock(context, widgetManager, appWidgetId, ratio);
                 }
-            }
+                break;
         }
     }
 
     @Override
     public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
-        if (DigitalAppWidgetService.LOGGING) {
-            Log.i(TAG, "onUpdate");
-        }
+        super.onUpdate(context, appWidgetManager, appWidgetIds);
         for (int appWidgetId : appWidgetIds) {
-            float ratio = WidgetUtils.getScaleRatio(context, null, appWidgetId);
+            final float ratio = WidgetUtils.getScaleRatio(context, null, appWidgetId);
             updateClock(context, appWidgetManager, appWidgetId, ratio);
         }
-        startAlarmOnQuarterHour(context);
-        super.onUpdate(context, appWidgetManager, appWidgetIds);
+
+        updateQuarterHourCallback(context);
     }
 
     @Override
     public void onAppWidgetOptionsChanged(Context context, AppWidgetManager appWidgetManager,
             int appWidgetId, Bundle newOptions) {
+        super.onAppWidgetOptionsChanged(context, appWidgetManager, appWidgetId, newOptions);
+
         // scale the fonts of the clock to fit inside the new size
-        float ratio = WidgetUtils.getScaleRatio(context, newOptions, appWidgetId);
-        AppWidgetManager widgetManager = AppWidgetManager.getInstance(context);
+        final float ratio = WidgetUtils.getScaleRatio(context, newOptions, appWidgetId);
+        final AppWidgetManager widgetManager = AppWidgetManager.getInstance(context);
         updateClock(context, widgetManager, appWidgetId, ratio);
     }
 
-    /**
-     * Determine whether action received corresponds to a "next alarm" changed action depending
-     * on the SDK version.
-     */
-    private boolean isNextAlarmChangedAction(String action) {
-        final String nextAlarmIntentAction;
-        if (Utils.isLOrLater()) {
-            nextAlarmIntentAction = AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED;
-        } else {
-            nextAlarmIntentAction = AlarmStateManager.SYSTEM_ALARM_CHANGE_ACTION;
-        }
-        return nextAlarmIntentAction.equals(action);
-    }
-
-    private void updateClock(
-            Context context, AppWidgetManager appWidgetManager, int appWidgetId, float ratio) {
+    private void updateClock(Context context, AppWidgetManager widgetManager, int appWidgetId,
+            float ratio) {
         RemoteViews widget = new RemoteViews(context.getPackageName(), R.layout.digital_appwidget);
 
         // Launch clock when clicking on the time in the widget only if not a lock screen widget
-        Bundle newOptions = appWidgetManager.getAppWidgetOptions(appWidgetId);
+        final Bundle newOptions = widgetManager.getAppWidgetOptions(appWidgetId);
         if (newOptions != null &&
                 newOptions.getInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY, -1)
                 != AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD) {
@@ -171,11 +172,11 @@
             widget.setOnClickPendingIntent(R.id.digital_appwidget, pendingIntent);
         }
 
-        // Setup formats and font sizes
+        // Setup formats and font sizes.
         refreshDate(context, widget, ratio);
         refreshAlarm(context, widget, ratio);
-        WidgetUtils.setTimeFormat(context, widget, false /* showAmPm */, R.id.the_clock);
         WidgetUtils.setClockSize(context, widget, ratio);
+        WidgetUtils.setTimeFormat(context, widget, 0.4f /* amPmRatio */, R.id.clock);
 
         // Set up R.id.digital_appwidget_listview to use a remote views adapter
         // That remote views adapter connects to a RemoteViewsService through intent.
@@ -191,118 +192,90 @@
                 PendingIntent.getActivity(context, 0, selectCitiesIntent, 0));
 
         // Refresh the widget
-        appWidgetManager.notifyAppWidgetViewDataChanged(
-                appWidgetId, R.id.digital_appwidget_listview);
-        appWidgetManager.updateAppWidget(appWidgetId, widget);
+        widgetManager.notifyAppWidgetViewDataChanged(appWidgetId, R.id.digital_appwidget_listview);
+        widgetManager.updateAppWidget(appWidgetId, widget);
     }
 
     private void refreshDate(Context context, RemoteViews widget, float ratio) {
         if (ratio < 1) {
             // The time text normally has a negative bottom margin to reduce the space between the
-            // time and the date. When we scale down they overlap, so give the date a positive
+            // time and the date. When scaling down they overlap, so give the date a positive
             // top padding.
             final float padding = (1 - ratio) *
                     -context.getResources().getDimension(R.dimen.bottom_text_spacing_digital);
             widget.setViewPadding(R.id.date_and_alarm, 0, (int) padding, 0, 0);
         }
 
-        // Set today's date format
+        // Set today's date format.
         final Locale locale = Locale.getDefault();
         final String skeleton = context.getString(R.string.abbrev_wday_abbrev_month_day_no_year);
         final CharSequence timeFormat = DateFormat.getBestDateTimePattern(locale, skeleton);
         widget.setCharSequence(R.id.date, "setFormat12Hour", timeFormat);
         widget.setCharSequence(R.id.date, "setFormat24Hour", timeFormat);
         final float fontSize = context.getResources().getDimension(R.dimen.widget_label_font_size);
-        widget.setTextViewTextSize(R.id.date, TypedValue.COMPLEX_UNIT_PX, fontSize * ratio);
+        widget.setTextViewTextSize(R.id.date, COMPLEX_UNIT_PX, fontSize * ratio);
     }
 
-    protected void refreshAlarm(Context context, RemoteViews widget, float ratio) {
+    private void refreshAlarm(Context context, RemoteViews widget, float ratio) {
         final String nextAlarm = Utils.getNextAlarm(context);
-        if (!TextUtils.isEmpty(nextAlarm)) {
-            final float fontSize =
-                    context.getResources().getDimension(R.dimen.widget_label_font_size);
-            widget.setTextViewTextSize(
-                    R.id.nextAlarm, TypedValue.COMPLEX_UNIT_PX, fontSize * ratio);
+        if (TextUtils.isEmpty(nextAlarm)) {
+            widget.setViewVisibility(R.id.nextAlarm, View.GONE);
+            if (DigitalAppWidgetService.LOGGING) {
+                Log.v(TAG, "DigitalWidget hides next alarm string");
+            }
+        } else  {
+            final Resources resources = context.getResources();
+            final float fontSize = resources.getDimension(R.dimen.widget_label_font_size);
+            widget.setTextViewTextSize(R.id.nextAlarm, COMPLEX_UNIT_PX, fontSize * ratio);
 
-            int alarmDrawableResId;
+            final int alarmIconResId;
             if (ratio < .72f) {
-                alarmDrawableResId = R.drawable.ic_alarm_small_12dp;
+                alarmIconResId = R.drawable.ic_alarm_small_12dp;
+            } else if (ratio < .95f) {
+                alarmIconResId = R.drawable.ic_alarm_small_18dp;
+            } else {
+                alarmIconResId = R.drawable.ic_alarm_small_24dp;
             }
-            else if (ratio < .95f) {
-                alarmDrawableResId = R.drawable.ic_alarm_small_18dp;
-            }
-            else {
-                alarmDrawableResId = R.drawable.ic_alarm_small_24dp;
-            }
-            widget.setTextViewCompoundDrawablesRelative(
-                    R.id.nextAlarm, alarmDrawableResId, 0, 0, 0);
-
+            widget.setTextViewCompoundDrawablesRelative(R.id.nextAlarm, alarmIconResId, 0, 0, 0);
             widget.setTextViewText(R.id.nextAlarm, nextAlarm);
             widget.setViewVisibility(R.id.nextAlarm, View.VISIBLE);
             if (DigitalAppWidgetService.LOGGING) {
                 Log.v(TAG, "DigitalWidget sets next alarm string to " + nextAlarm);
             }
-        } else  {
-            widget.setViewVisibility(R.id.nextAlarm, View.GONE);
-            if (DigitalAppWidgetService.LOGGING) {
-                Log.v(TAG, "DigitalWidget sets next alarm string to null");
-            }
         }
     }
 
     /**
-     * Start an alarm that fires on the next quarter hour to update the world clock city
-     * day when the local time or the world city crosses midnight.
-     *
-     * @param context The context in which the PendingIntent should perform the broadcast.
+     * Remove the existing quarter-hour callback if it is not needed (no selected cities exist).
+     * Add the quarter-hour callback if it is needed (selected cities exist).
      */
-    private void startAlarmOnQuarterHour(Context context) {
-        if (context != null) {
-            final long onQuarterHour = Utils.getAlarmOnQuarterHour();
-            final PendingIntent quarterlyIntent = getOnQuarterHourPendingIntent(context);
-            final AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
-            am.setExact(AlarmManager.RTC, onQuarterHour, quarterlyIntent);
-            if (DigitalAppWidgetService.LOGGING) {
-                Log.v(TAG, "startAlarmOnQuarterHour " + context.toString());
-            }
+    private void updateQuarterHourCallback(Context context) {
+        if (DataModel.getDataModel().getSelectedCities().isEmpty()) {
+            // Remove the existing quarter-hour callback.
+            removeQuarterHourCallback(context);
+            return;
         }
+
+        // Schedule the next quarter-hour callback; at least one city is displayed.
+        final PendingIntent pi =
+                PendingIntent.getBroadcast(context, 0, QUARTER_HOUR_INTENT, FLAG_UPDATE_CURRENT);
+        final long onQuarterHour = Utils.getAlarmOnQuarterHour();
+        getAlarmManager(context).setExact(RTC, onQuarterHour, pi);
     }
 
-
     /**
-     * Remove the alarm for the quarter hour update.
-     *
-     * @param context The context in which the PendingIntent was started to perform the broadcast.
+     * Remove the existing quarter-hour callback.
      */
-    public void cancelAlarmOnQuarterHour(Context context) {
-        if (context != null) {
-            PendingIntent quarterlyIntent = getOnQuarterHourPendingIntent(context);
-            if (DigitalAppWidgetService.LOGGING) {
-                Log.v(TAG, "cancelAlarmOnQuarterHour " + context.toString());
-            }
-            ((AlarmManager) context.getSystemService(Context.ALARM_SERVICE)).cancel(
-                    quarterlyIntent);
+    private void removeQuarterHourCallback(Context context) {
+        final PendingIntent pi =
+                PendingIntent.getBroadcast(context, 0, QUARTER_HOUR_INTENT, FLAG_NO_CREATE);
+        if (pi != null) {
+            getAlarmManager(context).cancel(pi);
+            pi.cancel();
         }
     }
 
     /**
-     * Create the pending intent that is broadcast on the quarter hour.
-     *
-     * @param context The Context in which this PendingIntent should perform the broadcast.
-     * @return a pending intent with an intent unique to DigitalAppWidgetProvider
-     */
-    private PendingIntent getOnQuarterHourPendingIntent(Context context) {
-        if (mPendingIntent == null) {
-            mPendingIntent = PendingIntent.getBroadcast(context, 0,
-                new Intent(ACTION_ON_QUARTER_HOUR), PendingIntent.FLAG_CANCEL_CURRENT);
-        }
-        return mPendingIntent;
-    }
-
-    /**
-     * Create the component name for this class
-     *
-     * @param context The Context in which the widgets for this component are created
      * @return the ComponentName unique to DigitalAppWidgetProvider
      */
     private ComponentName getComponentName(Context context) {
@@ -311,4 +284,8 @@
         }
         return mComponentName;
     }
-}
+
+    private static AlarmManager getAlarmManager(Context context) {
+        return (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/alarmclock/DigitalWidgetViewsFactory.java b/src/com/android/alarmclock/DigitalWidgetViewsFactory.java
index e8bb776..cc7ef42 100644
--- a/src/com/android/alarmclock/DigitalWidgetViewsFactory.java
+++ b/src/com/android/alarmclock/DigitalWidgetViewsFactory.java
@@ -181,7 +181,7 @@
     }
 
     private void update(RemoteViews clock, City city, int clockId, int labelId, int dayId) {
-        WidgetUtils.setTimeFormat(mContext, clock, true /* showAmPm */, clockId);
+        WidgetUtils.setTimeFormat(mContext, clock, 0.4f /* amPmRatio */, clockId);
 
         final float fontSize = DateFormat.is24HourFormat(mContext) ? mFont24Size : mFontSize;
         clock.setTextViewTextSize(clockId, TypedValue.COMPLEX_UNIT_PX, fontSize * mFontScale);
diff --git a/src/com/android/alarmclock/WidgetUtils.java b/src/com/android/alarmclock/WidgetUtils.java
index f4ce1f8..a15df7d 100644
--- a/src/com/android/alarmclock/WidgetUtils.java
+++ b/src/com/android/alarmclock/WidgetUtils.java
@@ -29,12 +29,9 @@
 import com.android.deskclock.data.DataModel;
 
 public class WidgetUtils {
-    static final String TAG = "WidgetUtils";
-
     public static void setClockSize(Context context, RemoteViews clock, float scale) {
-        float fontSize = context.getResources().getDimension(R.dimen.widget_big_font_size);
-        clock.setTextViewTextSize(
-                R.id.the_clock, TypedValue.COMPLEX_UNIT_PX, fontSize * scale);
+        final float fontSize = context.getResources().getDimension(R.dimen.widget_big_font_size);
+        clock.setTextViewTextSize(R.id.clock, TypedValue.COMPLEX_UNIT_PX, fontSize * scale);
     }
 
     // Calculate the scale factor of the fonts in the widget
@@ -139,17 +136,18 @@
 
     /***
      * Set the format of the time on the clock according to the locale
-     * @param context - Context used to get user's locale and time preferences
-     * @param clock - view to format
-     * @param showAmPm - show am/pm label if true
-     * @param clockId - id of TextClock view as defined in the clock's layout.
+     * @param context Context used to get user's locale and time preferences
+     * @param clock view to format
+     * @param amPmRatio a value between 0 and 1 that is the ratio of the relative size of the
+     *      am/pm string to the time string
+     * @param clockId id of TextClock view as defined in the clock's layout.
      */
-    public static void setTimeFormat(Context context, RemoteViews clock, boolean showAmPm,
+    public static void setTimeFormat(Context context, RemoteViews clock, float amPmRatio,
             int clockId) {
         if (clock != null) {
             // Set the best format for 12 hours mode according to the locale
             clock.setCharSequence(clockId, "setFormat12Hour",
-                    Utils.get12ModeFormat(context, showAmPm));
+                    Utils.get12ModeFormat(context, amPmRatio));
             // Set the best format for 24 hours mode according to the locale
             clock.setCharSequence(clockId, "setFormat24Hour", Utils.get24ModeFormat());
         }
diff --git a/src/com/android/deskclock/AlarmClockFragment.java b/src/com/android/deskclock/AlarmClockFragment.java
index d443ba5..c78b8c8 100644
--- a/src/com/android/deskclock/AlarmClockFragment.java
+++ b/src/com/android/deskclock/AlarmClockFragment.java
@@ -16,14 +16,13 @@
 
 package com.android.deskclock;
 
-import android.app.Activity;
 import android.app.LoaderManager;
 import android.content.Intent;
 import android.content.Loader;
 import android.database.Cursor;
-import android.media.RingtoneManager;
 import android.net.Uri;
 import android.os.Bundle;
+import android.support.annotation.NonNull;
 import android.support.design.widget.Snackbar;
 import android.support.v7.widget.LinearLayoutManager;
 import android.support.v7.widget.RecyclerView;
@@ -31,18 +30,29 @@
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
+import android.widget.ImageButton;
+import android.widget.ImageView;
 
 import com.android.deskclock.alarms.AlarmTimeClickHandler;
 import com.android.deskclock.alarms.AlarmUpdateHandler;
 import com.android.deskclock.alarms.ScrollHandler;
 import com.android.deskclock.alarms.TimePickerCompat;
-import com.android.deskclock.alarms.dataadapter.AlarmTimeAdapter;
+import com.android.deskclock.alarms.dataadapter.AlarmItemHolder;
+import com.android.deskclock.alarms.dataadapter.CollapsedAlarmViewHolder;
+import com.android.deskclock.alarms.dataadapter.ExpandedAlarmViewHolder;
 import com.android.deskclock.data.DataModel;
 import com.android.deskclock.provider.Alarm;
+import com.android.deskclock.provider.AlarmInstance;
+import com.android.deskclock.uidata.UiDataModel;
 import com.android.deskclock.widget.EmptyViewController;
 import com.android.deskclock.widget.toast.SnackbarManager;
 import com.android.deskclock.widget.toast.ToastManager;
 
+import java.util.ArrayList;
+import java.util.List;
+
+import static com.android.deskclock.uidata.UiDataModel.Tab.ALARMS;
+
 /**
  * A fragment that displays a list of alarm time and allows interaction with them.
  */
@@ -57,21 +67,29 @@
     // can not be found, and toast message will pop up that the alarm has be deleted.
     public static final String SCROLL_TO_ALARM_INTENT_EXTRA = "deskclock.scroll.to.alarm";
 
+    private static final String KEY_EXPANDED_ID = "expandedId";
+
     // Views
     private ViewGroup mMainLayout;
     private RecyclerView mRecyclerView;
 
     // Data
     private long mScrollToAlarmId = Alarm.INVALID_ID;
+    private long mExpandedAlarmId = Alarm.INVALID_ID;
     private Loader mCursorLoader = null;
 
     // Controllers
-    private AlarmTimeAdapter mAlarmTimeAdapter;
+    private ItemAdapter<AlarmItemHolder> mItemAdapter;
     private AlarmUpdateHandler mAlarmUpdateHandler;
     private EmptyViewController mEmptyViewController;
     private AlarmTimeClickHandler mAlarmTimeClickHandler;
     private LinearLayoutManager mLayoutManager;
 
+    /** The public no-arg constructor required by all fragments. */
+    public AlarmClockFragment() {
+        super(ALARMS);
+    }
+
     @Override
     public void processTimeSet(int hourOfDay, int minute) {
         mAlarmTimeClickHandler.processTimeSet(hourOfDay, minute);
@@ -81,6 +99,9 @@
     public void onCreate(Bundle savedState) {
         super.onCreate(savedState);
         mCursorLoader = getLoaderManager().initLoader(0, null, this);
+        if (savedState != null) {
+            mExpandedAlarmId = savedState.getLong(KEY_EXPANDED_ID, Alarm.INVALID_ID);
+        }
     }
 
     @Override
@@ -97,9 +118,27 @@
                 v.findViewById(R.id.alarms_empty_view));
         mAlarmTimeClickHandler = new AlarmTimeClickHandler(this, savedState, mAlarmUpdateHandler,
                 this);
-        mAlarmTimeAdapter = new AlarmTimeAdapter(getActivity(), savedState,
-                mAlarmTimeClickHandler, this);
-        mRecyclerView.setAdapter(mAlarmTimeAdapter);
+
+        mItemAdapter = new ItemAdapter<>();
+        mItemAdapter.withViewTypes(new CollapsedAlarmViewHolder.Factory(inflater),
+                null, CollapsedAlarmViewHolder.VIEW_TYPE);
+        mItemAdapter.withViewTypes(new ExpandedAlarmViewHolder.Factory(getActivity(), inflater),
+                null, ExpandedAlarmViewHolder.VIEW_TYPE);
+        mItemAdapter.setOnItemChangedListener(new ItemAdapter.OnItemChangedListener() {
+            @Override
+            public void onItemChanged(ItemAdapter.ItemHolder<?> holder) {
+                // When an alarm is expanded, collapse the currently expanded alarm.
+                if (((AlarmItemHolder) holder).isExpanded()) {
+                    if (mExpandedAlarmId != Alarm.INVALID_ID &&
+                            holder.itemId != mExpandedAlarmId) {
+                        ((AlarmItemHolder) mItemAdapter.findItemById(mExpandedAlarmId))
+                                .collapse();
+                    }
+                    mExpandedAlarmId = holder.itemId;
+                }
+            }
+        });
+        mRecyclerView.setAdapter(mItemAdapter);
 
         return v;
     }
@@ -108,15 +147,10 @@
     public void onResume() {
         super.onResume();
 
-        final DeskClock activity = (DeskClock) getActivity();
-        if (activity.getSelectedTab() == DeskClock.ALARM_TAB_INDEX) {
-            setFabAppearance();
-            setLeftRightButtonAppearance();
-        }
-
         // Check if another app asked us to create a blank new alarm.
         final Intent intent = getActivity().getIntent();
         if (intent.hasExtra(ALARM_CREATE_NEW_INTENT_EXTRA)) {
+            UiDataModel.getUiDataModel().setSelectedTab(ALARMS);
             if (intent.getBooleanExtra(ALARM_CREATE_NEW_INTENT_EXTRA, false)) {
                 // An external app asked us to create a blank alarm.
                 startCreatingAlarm();
@@ -125,6 +159,8 @@
             // Remove the CREATE_NEW extra now that we've processed it.
             intent.removeExtra(ALARM_CREATE_NEW_INTENT_EXTRA);
         } else if (intent.hasExtra(SCROLL_TO_ALARM_INTENT_EXTRA)) {
+            UiDataModel.getUiDataModel().setSelectedTab(ALARMS);
+
             long alarmId = intent.getLongExtra(SCROLL_TO_ALARM_INTENT_EXTRA, Alarm.INVALID_ID);
             if (alarmId != Alarm.INVALID_ID) {
                 setSmoothScrollStableId(alarmId);
@@ -148,8 +184,8 @@
     @Override
     public void onSaveInstanceState(Bundle outState) {
         super.onSaveInstanceState(outState);
-        mAlarmTimeAdapter.saveInstance(outState);
         mAlarmTimeClickHandler.saveInstance(outState);
+        outState.putLong(KEY_EXPANDED_ID, mExpandedAlarmId);
     }
 
     @Override
@@ -173,15 +209,39 @@
         mAlarmUpdateHandler.asyncUpdateAlarm(alarm, false, true);
     }
 
+    public void setRingtone(Uri ringtoneUri) {
+        // Update the default ringtone for future new alarms.
+        DataModel.getDataModel().setDefaultAlarmRingtoneUri(ringtoneUri);
+
+        final Alarm alarm = mAlarmTimeClickHandler.getSelectedAlarm();
+        if (alarm == null) {
+            LogUtils.e("Could not get selected alarm to set ringtone");
+            return;
+        }
+        alarm.alert = ringtoneUri;
+        // Save the change to alarm.
+        mAlarmUpdateHandler.asyncUpdateAlarm(alarm, false /* popToast */, true /* minorUpdate */);
+    }
+
     @Override
     public Loader<Cursor> onCreateLoader(int id, Bundle args) {
         return Alarm.getAlarmsCursorLoader(getActivity());
     }
 
     @Override
-    public void onLoadFinished(Loader<Cursor> cursorLoader, final Cursor data) {
+    public void onLoadFinished(Loader<Cursor> cursorLoader, Cursor data) {
+        final List<AlarmItemHolder> itemHolders = new ArrayList<>(data.getCount());
+        for (data.moveToFirst(); !data.isAfterLast(); data.moveToNext()) {
+            final Alarm alarm = new Alarm(data);
+            final AlarmInstance alarmInstance = alarm.canPreemptivelyDismiss()
+                    ? new AlarmInstance(data, true /* joinedTable */) : null;
+            final AlarmItemHolder itemHolder =
+                    new AlarmItemHolder(alarm, alarmInstance, mAlarmTimeClickHandler);
+            itemHolders.add(itemHolder);
+        }
+        mItemAdapter.setItems(itemHolders);
         mEmptyViewController.setEmpty(data.getCount() == 0);
-        mAlarmTimeAdapter.swapCursor(data);
+
         if (mScrollToAlarmId != Alarm.INVALID_ID) {
             scrollToAlarm(mScrollToAlarmId);
             setSmoothScrollStableId(Alarm.INVALID_ID);
@@ -194,10 +254,10 @@
      * @param alarmId The alarm id to scroll to.
      */
     private void scrollToAlarm(long alarmId) {
-        final int alarmCount = mAlarmTimeAdapter.getItemCount();
+        final int alarmCount = mItemAdapter.getItemCount();
         int alarmPosition = -1;
         for (int i = 0; i < alarmCount; i++) {
-            long id = mAlarmTimeAdapter.getItemId(i);
+            long id = mItemAdapter.getItemId(i);
             if (id == alarmId) {
                 alarmPosition = i;
                 break;
@@ -205,7 +265,7 @@
         }
 
         if (alarmPosition >= 0) {
-            mAlarmTimeAdapter.expand(alarmPosition);
+            ((AlarmItemHolder) mItemAdapter.getItems().get(alarmPosition)).expand();
         } else {
             // Trying to display a deleted alarm should only happen from a missed notification for
             // an alarm that has been marked deleted after use.
@@ -216,41 +276,6 @@
 
     @Override
     public void onLoaderReset(Loader<Cursor> cursorLoader) {
-        mAlarmTimeAdapter.swapCursor(null);
-    }
-
-    @Override
-    public void onActivityResult(int requestCode, int resultCode, Intent data) {
-        if (resultCode != Activity.RESULT_OK) {
-            return;
-        }
-
-        switch (requestCode) {
-            case R.id.request_code_ringtone:
-                // Extract the selected ringtone uri.
-                Uri uri = data.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI);
-                if (uri == null) {
-                    uri = Alarm.NO_RINGTONE_URI;
-                }
-
-                // Update the default ringtone for future new alarms.
-                DataModel.getDataModel().setDefaultAlarmRingtoneUri(uri);
-
-                // Set the ringtone uri on the alarm.
-                final Alarm alarm = mAlarmTimeClickHandler.getSelectedAlarm();
-                if (alarm == null) {
-                    LogUtils.e("Could not get selected alarm to set ringtone");
-                    return;
-                }
-                alarm.alert = uri;
-
-                // Save the change to alarm.
-                mAlarmUpdateHandler.asyncUpdateAlarm(alarm, false /* popToast */,
-                        true /* minorUpdate */);
-                break;
-            default:
-                LogUtils.w("Unhandled request code in onActivityResult: " + requestCode);
-        }
     }
 
     @Override
@@ -259,29 +284,22 @@
     }
 
     @Override
-    public void onFabClick(View view) {
+    public void onFabClick(@NonNull ImageView fab) {
         mAlarmUpdateHandler.hideUndoBar();
         startCreatingAlarm();
     }
 
     @Override
-    public void setFabAppearance() {
-        if (mFab == null || getDeskClock().getSelectedTab() != DeskClock.ALARM_TAB_INDEX) {
-            return;
-        }
-        mFab.setVisibility(View.VISIBLE);
-        mFab.setImageResource(R.drawable.ic_add_white_24dp);
-        mFab.setContentDescription(getString(R.string.button_alarms));
+    public void onUpdateFab(@NonNull ImageView fab) {
+        fab.setVisibility(View.VISIBLE);
+        fab.setImageResource(R.drawable.ic_add_white_24dp);
+        fab.setContentDescription(fab.getResources().getString(R.string.button_alarms));
     }
 
     @Override
-    public void setLeftRightButtonAppearance() {
-        if (mLeftButton == null || mRightButton == null ||
-                getDeskClock().getSelectedTab() != DeskClock.ALARM_TAB_INDEX) {
-            return;
-        }
-        mLeftButton.setVisibility(View.INVISIBLE);
-        mRightButton.setVisibility(View.INVISIBLE);
+    public void onUpdateFabButtons(@NonNull ImageButton left, @NonNull ImageButton right) {
+        left.setVisibility(View.INVISIBLE);
+        right.setVisibility(View.INVISIBLE);
     }
 
     private void startCreatingAlarm() {
@@ -289,4 +307,4 @@
         TimePickerCompat.showTimeEditDialog(this, null /* alarm */,
                 DateFormat.is24HourFormat(getActivity()));
     }
-}
+}
\ No newline at end of file
diff --git a/src/com/android/deskclock/AnalogClock.java b/src/com/android/deskclock/AnalogClock.java
index c3f0b3e..4125a6a 100644
--- a/src/com/android/deskclock/AnalogClock.java
+++ b/src/com/android/deskclock/AnalogClock.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2006 The Android Open Source Project
+ * Copyright (C) 2015 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.
@@ -16,278 +16,198 @@
 
 package com.android.deskclock;
 
+import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.BroadcastReceiver;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
 import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
 import android.graphics.drawable.Drawable;
-import android.os.Handler;
-import android.text.format.DateUtils;
-import android.text.format.Time;
+import android.os.SystemClock;
+import android.support.annotation.DrawableRes;
+import android.support.v4.content.ContextCompat;
+import android.text.format.DateFormat;
 import android.util.AttributeSet;
 import android.view.View;
-import android.widget.RemoteViews.RemoteView;
 
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
 import java.util.TimeZone;
 
+import static android.text.format.DateUtils.SECOND_IN_MILLIS;
+
 /**
- * This widget display an analogic clock with two hands for hours and
- * minutes.
+ * This widget display an analog clock with two hands for hours and minutes.
  */
 public class AnalogClock extends View {
-    private Time mCalendar;
 
+    private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (mTimeZone == null && Intent.ACTION_TIMEZONE_CHANGED.equals(intent.getAction())) {
+                final String tz = intent.getStringExtra("time-zone");
+                mTime = Calendar.getInstance(TimeZone.getTimeZone(tz));
+            }
+            onTimeChanged();
+        }
+    };
+
+    private final Runnable mClockTick = new Runnable() {
+        @Override
+        public void run() {
+            onTimeChanged();
+
+            if (mEnableSeconds) {
+                final long now = System.currentTimeMillis();
+                final long delay = SECOND_IN_MILLIS - now % SECOND_IN_MILLIS;
+                postDelayed(this, delay);
+            }
+        }
+    };
+
+    private final Drawable mDial;
     private final Drawable mHourHand;
     private final Drawable mMinuteHand;
     private final Drawable mSecondHand;
-    private final Drawable mDial;
 
-    private final int mDialWidth;
-    private final int mDialHeight;
-
-    private boolean mAttached;
-
-    private final Handler mHandler = new Handler();
-    private float mSeconds;
-    private float mMinutes;
-    private float mHour;
-    private boolean mChanged;
-    private final Context mContext;
-    private String mTimeZoneId;
-    private boolean mNoSeconds = false;
-
-    private final float mDotRadius;
-    private final float mDotOffset;
-    private Paint mDotPaint;
+    private Calendar mTime;
+    private String mDescFormat;
+    private TimeZone mTimeZone;
+    private boolean mEnableSeconds = true;
 
     public AnalogClock(Context context) {
-        this(context, null);
+        this(context, null /* attrs */);
     }
 
     public AnalogClock(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
+        this(context, attrs, 0 /* defStyleAttr */);
     }
 
-    public AnalogClock(Context context, AttributeSet attrs,
-                       int defStyle) {
-        super(context, attrs, defStyle);
-        mContext = context;
-        Resources r = mContext.getResources();
+    public AnalogClock(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
 
-        mDial = r.getDrawable(R.drawable.clock_analog_dial_mipmap);
-        mHourHand = r.getDrawable(R.drawable.clock_analog_hour_mipmap);
-        mMinuteHand = r.getDrawable(R.drawable.clock_analog_minute_mipmap);
-        mSecondHand = r.getDrawable(R.drawable.clock_analog_second_mipmap);
+        mTime = Calendar.getInstance();
+        mDescFormat = ((SimpleDateFormat) DateFormat.getTimeFormat(context)).toLocalizedPattern();
 
-        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.AnalogClock);
-        mDotRadius = a.getDimension(R.styleable.AnalogClock_jewelRadius, 0);
-        mDotOffset = a.getDimension(R.styleable.AnalogClock_jewelOffset, 0);
-        final int dotColor = a.getColor(R.styleable.AnalogClock_jewelColor, Color.WHITE);
-        if (dotColor != 0) {
-            mDotPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
-            mDotPaint.setColor(dotColor);
-        }
-
-        mCalendar = new Time();
-
-        mDialWidth = mDial.getIntrinsicWidth();
-        mDialHeight = mDial.getIntrinsicHeight();
+        mDial = initDrawable(context, R.drawable.clock_analog_dial_mipmap);
+        mHourHand = initDrawable(context, R.drawable.clock_analog_hour_mipmap);
+        mMinuteHand = initDrawable(context, R.drawable.clock_analog_minute_mipmap);
+        mSecondHand = initDrawable(context, R.drawable.clock_analog_second_mipmap);
     }
 
     @Override
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
 
-        if (!mAttached) {
-            mAttached = true;
-            IntentFilter filter = new IntentFilter();
+        final IntentFilter filter = new IntentFilter();
+        filter.addAction(Intent.ACTION_TIME_TICK);
+        filter.addAction(Intent.ACTION_TIME_CHANGED);
+        filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
+        getContext().registerReceiver(mIntentReceiver, filter);
 
-            filter.addAction(Intent.ACTION_TIME_TICK);
-            filter.addAction(Intent.ACTION_TIME_CHANGED);
-            filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
-
-            getContext().registerReceiver(mIntentReceiver, filter, null, mHandler);
-        }
-
-        // NOTE: It's safe to do these after registering the receiver since the receiver always runs
-        // in the main thread, therefore the receiver can't run before this method returns.
-
-        // The time zone may have changed while the receiver wasn't registered, so update the Time
-        mCalendar = new Time();
-
-        // Make sure we update to the current time
+        // Refresh the calendar instance since the time zone may have changed while the receiver
+        // wasn't registered.
+        mTime = Calendar.getInstance(mTimeZone != null ? mTimeZone : TimeZone.getDefault());
         onTimeChanged();
 
-        // tick the seconds
-        post(mClockTick);
-
+        // Tick every second.
+        if (mEnableSeconds) {
+            mClockTick.run();
+        }
     }
 
     @Override
     protected void onDetachedFromWindow() {
         super.onDetachedFromWindow();
-        if (mAttached) {
-            getContext().unregisterReceiver(mIntentReceiver);
-            removeCallbacks(mClockTick);
-            mAttached = false;
-        }
+
+        getContext().unregisterReceiver(mIntentReceiver);
+        removeCallbacks(mClockTick);
     }
 
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-
-        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
-        int widthSize =  MeasureSpec.getSize(widthMeasureSpec);
-        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
-        int heightSize =  MeasureSpec.getSize(heightMeasureSpec);
-
-        float hScale = 1.0f;
-        float vScale = 1.0f;
-
-        if (widthMode != MeasureSpec.UNSPECIFIED && widthSize < mDialWidth) {
-            hScale = (float) widthSize / (float) mDialWidth;
-        }
-
-        if (heightMode != MeasureSpec.UNSPECIFIED && heightSize < mDialHeight) {
-            vScale = (float )heightSize / (float) mDialHeight;
-        }
-
-        float scale = Math.min(hScale, vScale);
-
-        setMeasuredDimension(resolveSizeAndState((int) (mDialWidth * scale), widthMeasureSpec, 0),
-                resolveSizeAndState((int) (mDialHeight * scale), heightMeasureSpec, 0));
-    }
-
-    @Override
-    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
-        super.onSizeChanged(w, h, oldw, oldh);
-        mChanged = true;
+        final int minWidth = Math.max(mDial.getIntrinsicWidth(), getSuggestedMinimumWidth());
+        final int minHeight = Math.max(mDial.getIntrinsicHeight(), getSuggestedMinimumHeight());
+        setMeasuredDimension(getDefaultSize(minWidth, widthMeasureSpec),
+                getDefaultSize(minHeight, heightMeasureSpec));
     }
 
     @Override
     protected void onDraw(Canvas canvas) {
         super.onDraw(canvas);
 
-        boolean changed = mChanged;
-        if (changed) {
-            mChanged = false;
+        final int w = getWidth();
+        final int h = getHeight();
+
+        final int saveCount = canvas.save();
+
+        // Center the canvas at the mid-point.
+        canvas.translate(w / 2, h / 2);
+
+        // Scale down the clock if necessary.
+        final float scale = Math.min((float) w / mDial.getIntrinsicWidth(),
+                (float) h / mDial.getIntrinsicHeight());
+        if (scale < 1f) {
+            canvas.scale(scale, scale, 0f, 0f);
         }
 
-        int availableWidth = getWidth();
-        int availableHeight = getHeight();
+        mDial.draw(canvas);
 
-        int x = availableWidth / 2;
-        int y = availableHeight / 2;
+        final float hourAngle = mTime.get(Calendar.HOUR) * 30f;
+        canvas.rotate(hourAngle, 0f, 0f);
+        mHourHand.draw(canvas);
 
-        final Drawable dial = mDial;
-        int w = dial.getIntrinsicWidth();
-        int h = dial.getIntrinsicHeight();
+        final float minuteAngle = mTime.get(Calendar.MINUTE) * 6f;
+        canvas.rotate(minuteAngle - hourAngle, 0f, 0f);
+        mMinuteHand.draw(canvas);
 
-        boolean scaled = false;
-
-        if (availableWidth < w || availableHeight < h) {
-            scaled = true;
-            float scale = Math.min((float) availableWidth / (float) w,
-                                   (float) availableHeight / (float) h);
-            canvas.save();
-            canvas.scale(scale, scale, x, y);
+        if (mEnableSeconds) {
+            final float secondAngle = mTime.get(Calendar.SECOND) * 6f;
+            canvas.rotate(secondAngle - minuteAngle, 0f, 0f);
+            mSecondHand.draw(canvas);
         }
 
-        if (changed) {
-            dial.setBounds(x - (w / 2), y - (h / 2), x + (w / 2), y + (h / 2));
-        }
-        dial.draw(canvas);
-
-        if (mDotRadius > 0f && mDotPaint != null) {
-            canvas.drawCircle(x, y - (h / 2) + mDotOffset, mDotRadius, mDotPaint);
-        }
-
-        drawHand(canvas, mHourHand, x, y, mHour / 12.0f * 360.0f, changed);
-        drawHand(canvas, mMinuteHand, x, y, mMinutes / 60.0f * 360.0f, changed);
-        if (!mNoSeconds) {
-            drawHand(canvas, mSecondHand, x, y, mSeconds / 60.0f * 360.0f, changed);
-        }
-
-        if (scaled) {
-            canvas.restore();
-        }
+        canvas.restoreToCount(saveCount);
     }
 
-    private void drawHand(Canvas canvas, Drawable hand, int x, int y, float angle,
-          boolean changed) {
-      canvas.save();
-      canvas.rotate(angle, x, y);
-      if (changed) {
-          final int w = hand.getIntrinsicWidth();
-          final int h = hand.getIntrinsicHeight();
-          hand.setBounds(x - (w / 2), y - (h / 2), x + (w / 2), y + (h / 2));
-      }
-      hand.draw(canvas);
-      canvas.restore();
+    @Override
+    protected boolean verifyDrawable(Drawable who) {
+        return mDial == who
+                || mHourHand == who
+                || mMinuteHand == who
+                || mSecondHand == who
+                || super.verifyDrawable(who);
+    }
+
+    private Drawable initDrawable(Context context, @DrawableRes int id) {
+        final Drawable d = ContextCompat.getDrawable(context, id);
+
+        // Center the drawable using its bounds.
+        final int midX = d.getIntrinsicWidth() / 2;
+        final int midY = d.getIntrinsicHeight() / 2;
+        d.setBounds(-midX, -midY, midX, midY);
+
+        // Register callback to support non-bitmap drawables.
+        d.setCallback(this);
+
+        return d;
     }
 
     private void onTimeChanged() {
-        mCalendar.setToNow();
-
-        if (mTimeZoneId != null) {
-            mCalendar.switchTimezone(mTimeZoneId);
-        }
-
-        int hour = mCalendar.hour;
-        int minute = mCalendar.minute;
-        int second = mCalendar.second;
-  //      long millis = System.currentTimeMillis() % 1000;
-
-        mSeconds = second;//(float) ((second * 1000 + millis) / 166.666);
-        mMinutes = minute + second / 60.0f;
-        mHour = hour + mMinutes / 60.0f;
-        mChanged = true;
-
-        updateContentDescription(mCalendar);
-    }
-
-    private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (intent.getAction().equals(Intent.ACTION_TIMEZONE_CHANGED)) {
-                String tz = intent.getStringExtra("time-zone");
-                mCalendar = new Time(TimeZone.getTimeZone(tz).getID());
-            }
-            onTimeChanged();
-            invalidate();
-        }
-    };
-
-    private final Runnable mClockTick = new Runnable () {
-
-        @Override
-        public void run() {
-            onTimeChanged();
-            invalidate();
-            AnalogClock.this.postDelayed(mClockTick, 1000);
-        }
-    };
-
-    private void updateContentDescription(Time time) {
-        final int flags = DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_24HOUR;
-        String contentDescription = DateUtils.formatDateTime(mContext,
-                time.toMillis(false), flags);
-        setContentDescription(contentDescription);
+        mTime.setTimeInMillis(System.currentTimeMillis());
+        setContentDescription(DateFormat.format(mDescFormat, mTime));
+        invalidate();
     }
 
     public void setTimeZone(String id) {
-        mTimeZoneId = id;
+        mTimeZone = TimeZone.getTimeZone(id);
+        mTime.setTimeZone(mTimeZone);
         onTimeChanged();
     }
 
     public void enableSeconds(boolean enable) {
-        mNoSeconds = !enable;
+        mEnableSeconds = enable;
+        if (mEnableSeconds) {
+            mClockTick.run();
+        }
     }
-
 }
-
diff --git a/src/com/android/deskclock/AnimatorUtils.java b/src/com/android/deskclock/AnimatorUtils.java
index 26d266f..0794cf8 100644
--- a/src/com/android/deskclock/AnimatorUtils.java
+++ b/src/com/android/deskclock/AnimatorUtils.java
@@ -19,6 +19,7 @@
 import android.animation.ArgbEvaluator;
 import android.animation.ObjectAnimator;
 import android.animation.PropertyValuesHolder;
+import android.animation.TypeEvaluator;
 import android.animation.ValueAnimator;
 import android.graphics.drawable.Drawable;
 import android.support.v4.graphics.drawable.DrawableCompat;
@@ -87,7 +88,8 @@
         }
     };
 
-    public static final ArgbEvaluator ARGB_EVALUATOR = new ArgbEvaluator();
+    @SuppressWarnings("unchecked")
+    public static final TypeEvaluator<Integer> ARGB_EVALUATOR = new ArgbEvaluator();
 
     private static Method sAnimateValue;
     private static boolean sTryAnimateValue = true;
@@ -143,4 +145,8 @@
                 PropertyValuesHolder.ofFloat(View.SCALE_X, values),
                 PropertyValuesHolder.ofFloat(View.SCALE_Y, values));
     }
+
+    public static ValueAnimator getAlphaAnimator(View view, float... values) {
+        return ObjectAnimator.ofFloat(view, View.ALPHA, values);
+    }
 }
diff --git a/src/com/android/deskclock/AsyncRingtonePlayer.java b/src/com/android/deskclock/AsyncRingtonePlayer.java
index 59eb01c..00240ed 100644
--- a/src/com/android/deskclock/AsyncRingtonePlayer.java
+++ b/src/com/android/deskclock/AsyncRingtonePlayer.java
@@ -1,5 +1,6 @@
 package com.android.deskclock;
 
+import android.annotation.SuppressLint;
 import android.content.Context;
 import android.media.AudioAttributes;
 import android.media.AudioManager;
@@ -12,6 +13,7 @@
 import android.os.HandlerThread;
 import android.os.Looper;
 import android.os.Message;
+import android.os.SystemClock;
 import android.preference.PreferenceManager;
 import android.telephony.TelephonyManager;
 import android.text.format.DateUtils;
@@ -70,7 +72,7 @@
 
     /**
      * @param crescendoPrefKey the key to the user preference that defines the crescendo behavior
-     *                         associated with this ringtone player
+     *                         associated with this ringtone player, or null to ignore crescendo
      */
     public AsyncRingtonePlayer(Context context, String crescendoPrefKey) {
         mContext = context;
@@ -127,6 +129,7 @@
     /**
      * Creates a new ringtone Handler running in its own thread.
      */
+    @SuppressLint("HandlerLeak")
     private Handler getNewHandler() {
         final HandlerThread thread = new HandlerThread("ringtone-player");
         thread.start();
@@ -213,10 +216,11 @@
     }
 
     /**
-     * @return {@code true} iff the crescendo duration is more than 0 seconds
+     * Returns true if the crescendo preference was given and the duration is more than
+     * 0 seconds.
      */
     private boolean isCrescendoEnabled(Context context) {
-        return getCrescendoDurationMillis(context) > 0;
+        return mCrescendoPrefKey != null && getCrescendoDurationMillis(context) > 0;
     }
 
     /**
@@ -326,7 +330,7 @@
 
                     // Compute the time at which the crescendo will stop.
                     mCrescendoDuration = getCrescendoDurationMillis(context);
-                    mCrescendoStopTime = System.currentTimeMillis() + mCrescendoDuration;
+                    mCrescendoStopTime = now() + mCrescendoDuration;
                     scheduleVolumeAdjustment = true;
                 }
 
@@ -416,7 +420,7 @@
             }
 
             // If the crescendo is complete set the volume to the maximum; we're done.
-            final long currentTime = System.currentTimeMillis();
+            final long currentTime = now();
             if (currentTime > mCrescendoStopTime) {
                 mCrescendoDuration = 0;
                 mCrescendoStopTime = 0;
@@ -533,7 +537,7 @@
 
                 // Compute the time at which the crescendo will stop.
                 mCrescendoDuration = getCrescendoDurationMillis(context);
-                mCrescendoStopTime = System.currentTimeMillis() + mCrescendoDuration;
+                mCrescendoStopTime = now() + mCrescendoDuration;
                 scheduleVolumeAdjustment = true;
             }
 
@@ -597,7 +601,7 @@
             }
 
             // If the crescendo is complete set the volume to the maximum; we're done.
-            final long currentTime = System.currentTimeMillis();
+            final long currentTime = now();
             if (currentTime > mCrescendoStopTime) {
                 mCrescendoDuration = 0;
                 mCrescendoStopTime = 0;
@@ -612,5 +616,12 @@
             return true;
         }
     }
+
+    /**
+     * @return the current elapsed time which is immune to device time changes
+     */
+    private static long now() {
+        return SystemClock.elapsedRealtime();
+    }
 }
 
diff --git a/src/com/android/deskclock/BaseActivity.java b/src/com/android/deskclock/BaseActivity.java
index 4b72d80..04c6280 100644
--- a/src/com/android/deskclock/BaseActivity.java
+++ b/src/com/android/deskclock/BaseActivity.java
@@ -29,7 +29,7 @@
  * Base activity class that changes with window's background color dynamically based on the
  * current hour.
  */
-public class BaseActivity extends AppCompatActivity {
+public abstract class BaseActivity extends AppCompatActivity {
 
     /**
      * Key used to save/restore the current background color from the saved instance state.
diff --git a/src/com/android/deskclock/ClockFragment.java b/src/com/android/deskclock/ClockFragment.java
index c9b339f..a48bdb8 100644
--- a/src/com/android/deskclock/ClockFragment.java
+++ b/src/com/android/deskclock/ClockFragment.java
@@ -27,6 +27,7 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.provider.Settings;
+import android.support.annotation.NonNull;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.View;
@@ -34,6 +35,8 @@
 import android.view.ViewConfiguration;
 import android.view.ViewGroup;
 import android.widget.BaseAdapter;
+import android.widget.ImageButton;
+import android.widget.ImageView;
 import android.widget.ListView;
 import android.widget.TextClock;
 import android.widget.TextView;
@@ -50,6 +53,7 @@
 import static android.view.View.GONE;
 import static android.view.View.INVISIBLE;
 import static android.view.View.VISIBLE;
+import static com.android.deskclock.uidata.UiDataModel.Tab.CLOCKS;
 import static java.util.Calendar.DAY_OF_WEEK;
 
 /**
@@ -76,7 +80,9 @@
     private String mDateFormatForAccessibility;
 
     /** The public no-arg constructor required by all fragments. */
-    public ClockFragment() {}
+    public ClockFragment() {
+        super(CLOCKS);
+    }
 
     @Override
     public void onCreate(Bundle savedInstanceState) {
@@ -134,8 +140,6 @@
         super.onResume();
 
         final Activity activity = getActivity();
-        setFabAppearance();
-        setLeftRightButtonAppearance();
 
         mDateFormat = getString(R.string.abbrev_wday_month_day_no_year);
         mDateFormatForAccessibility = getString(R.string.full_wday_month_day_no_year);
@@ -147,7 +151,9 @@
         final IntentFilter filter = new IntentFilter();
         filter.addAction(Intent.ACTION_TIME_CHANGED);
         filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
-        filter.addAction(AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED);
+        if (Utils.isLOrLater()) {
+            filter.addAction(AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED);
+        }
         activity.registerReceiver(mBroadcastReceiver, filter);
 
         // Resume can be invoked after changing the clock style.
@@ -162,7 +168,9 @@
         refreshDates();
         refreshAlarm();
 
+        // Alarm observer is null on L or later.
         if (mAlarmObserver != null) {
+            @SuppressWarnings("deprecation")
             final Uri uri = Settings.System.getUriFor(Settings.System.NEXT_ALARM_FORMATTED);
             activity.getContentResolver().registerContentObserver(uri, false, mAlarmObserver);
         }
@@ -181,34 +189,21 @@
     }
 
     @Override
-    public void onFabClick(View view) {
+    public void onFabClick(@NonNull ImageView fab) {
         startActivity(new Intent(getActivity(), CitySelectionActivity.class));
     }
 
     @Override
-    public void setFabAppearance() {
-        if (mFab == null || getSelectedTab() != DeskClock.CLOCK_TAB_INDEX) {
-            return;
-        }
-
-        mFab.setVisibility(VISIBLE);
-        mFab.setImageResource(R.drawable.ic_language_white_24dp);
-        mFab.setContentDescription(getString(R.string.button_cities));
+    public void onUpdateFab(@NonNull ImageView fab) {
+        fab.setVisibility(VISIBLE);
+        fab.setImageResource(R.drawable.ic_language_white_24dp);
+        fab.setContentDescription(fab.getResources().getString(R.string.button_cities));
     }
 
     @Override
-    public void setLeftRightButtonAppearance() {
-        if (getSelectedTab() != DeskClock.CLOCK_TAB_INDEX) {
-            return;
-        }
-
-        if (mLeftButton != null) {
-            mLeftButton.setVisibility(INVISIBLE);
-        }
-
-        if (mRightButton != null) {
-            mRightButton.setVisibility(INVISIBLE);
-        }
+    public void onUpdateFabButtons(@NonNull ImageButton left, @NonNull ImageButton right) {
+        left.setVisibility(INVISIBLE);
+        right.setVisibility(INVISIBLE);
     }
 
     /**
@@ -380,10 +375,12 @@
                 analogClock.setTimeZone(city.getTimeZoneId());
                 analogClock.enableSeconds(false);
             } else {
-                digitalClock.setVisibility(VISIBLE);
                 analogClock.setVisibility(GONE);
+                digitalClock.setVisibility(VISIBLE);
                 digitalClock.setTimeZone(city.getTimeZoneId());
-                Utils.setTimeFormat(mContext, digitalClock);
+                digitalClock.setFormat12Hour(
+                        Utils.get12ModeFormat(mContext, 0.22f /* amPmRatio */));
+                digitalClock.setFormat24Hour(Utils.get24ModeFormat());
             }
 
             // Bind the city name.
diff --git a/src/com/android/deskclock/DeskClock.java b/src/com/android/deskclock/DeskClock.java
index e0189a0..2f59f9a 100644
--- a/src/com/android/deskclock/DeskClock.java
+++ b/src/com/android/deskclock/DeskClock.java
@@ -16,21 +16,23 @@
 
 package com.android.deskclock;
 
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ValueAnimator;
 import android.app.Fragment;
 import android.app.FragmentManager;
 import android.content.Context;
 import android.content.Intent;
-import android.media.AudioManager;
+import android.net.Uri;
 import android.os.Bundle;
-import android.support.annotation.VisibleForTesting;
 import android.support.design.widget.TabLayout;
-import android.support.design.widget.TabLayout.Tab;
 import android.support.design.widget.TabLayout.ViewPagerOnTabSelectedListener;
 import android.support.v13.app.FragmentPagerAdapter;
+import android.support.v4.content.ContextCompat;
 import android.support.v4.view.ViewPager.OnPageChangeListener;
 import android.support.v7.app.AppCompatActivity;
 import android.support.v7.widget.Toolbar;
-import android.util.ArraySet;
 import android.view.Menu;
 import android.view.MenuItem;
 import android.view.View;
@@ -47,41 +49,64 @@
 import com.android.deskclock.data.DataModel;
 import com.android.deskclock.events.Events;
 import com.android.deskclock.provider.Alarm;
-import com.android.deskclock.stopwatch.StopwatchFragment;
-import com.android.deskclock.timer.TimerFragment;
+import com.android.deskclock.uidata.TabListener;
+import com.android.deskclock.uidata.UiDataModel;
+import com.android.deskclock.uidata.UiDataModel.Tab;
 import com.android.deskclock.widget.RtlViewPager;
 
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Set;
+import static android.support.v4.view.ViewPager.SCROLL_STATE_DRAGGING;
+import static android.support.v4.view.ViewPager.SCROLL_STATE_IDLE;
+import static android.support.v4.view.ViewPager.SCROLL_STATE_SETTLING;
+import static com.android.deskclock.AnimatorUtils.getAlphaAnimator;
+import static com.android.deskclock.AnimatorUtils.getScaleAnimator;
+import static com.android.deskclock.FabContainer.UpdateType.FAB_AND_BUTTONS_IMMEDIATE;
 
 /**
- * DeskClock clock view for desk docks.
+ * The main activity of the application which displays 4 different tabs contains alarms, world
+ * clocks, timers and a stopwatch.
  */
 public class DeskClock extends BaseActivity
-        implements LabelDialogFragment.AlarmLabelDialogHandler {
+        implements FabContainer, LabelDialogFragment.AlarmLabelDialogHandler,
+        RingtonePickerDialogFragment.RingtoneSelectionListener {
 
-    private static final String TAG = "DeskClock";
+    /** Models the interesting state of display the {@link #mFab} button may inhabit. */
+    private enum FabState { SHOWING, HIDE_ARMED, HIDING }
 
-    // Alarm action for midnight (so we can update the date display).
-    private static final String KEY_SELECTED_TAB = "selected_tab";
-    public static final String SELECT_TAB_INTENT_EXTRA = "deskclock.select.tab";
+    /** Coordinates handling of context menu items. */
+    private final ActionBarMenuManager mActionBarMenuManager = new ActionBarMenuManager();
 
-    public static final int ALARM_TAB_INDEX = 0;
-    public static final int CLOCK_TAB_INDEX = 1;
-    public static final int TIMER_TAB_INDEX = 2;
-    public static final int STOPWATCH_TAB_INDEX = 3;
+    /** Shrinks the {@link #mFab}, {@link #mLeftButton} and {@link #mRightButton} to nothing. */
+    private final AnimatorSet mHideAnimation = new AnimatorSet();
 
-    private final ActionBarMenuManager mActionBarMenuManager = new ActionBarMenuManager(this);
+    /** Grows the {@link #mFab}, {@link #mLeftButton} and {@link #mRightButton} to natural sizes. */
+    private final AnimatorSet mShowAnimation = new AnimatorSet();
 
-    private TabLayout mTabLayout;
-    private RtlViewPager mViewPager;
+    /** Hides, updates, and shows only the {@link #mFab}; the buttons are untouched. */
+    private final AnimatorSet mUpdateFabOnlyAnimation = new AnimatorSet();
+
+    /** Automatically starts the {@link #mShowAnimation} after {@link #mHideAnimation} ends. */
+    private final AnimatorListenerAdapter mAutoStartShowListener = new AutoStartShowListener();
+
+    /** The current display state of the {@link #mFab}. */
+    private FabState mFabState = FabState.SHOWING;
+
+    /** The single floating-action button shared across all tabs in the user interface. */
     private ImageView mFab;
+
+    /** The button left of the {@link #mFab} shared across all tabs in the user interface. */
     private ImageButton mLeftButton;
+
+    /** The button right of the {@link #mFab} shared across all tabs in the user interface. */
     private ImageButton mRightButton;
 
-    private TabsAdapter mTabsAdapter;
-    private int mSelectedTab;
+    /** The ViewPager that pages through the fragments representing the content of the tabs. */
+    private RtlViewPager mFragmentTabPager;
+
+    /** Generates the fragments that are displayed by the {@link #mFragmentTabPager}. */
+    private TabFragmentAdapter mFragmentTabPagerAdapter;
+
+    /** The container that stores the tab headers. */
+    private TabLayout mTabLayout;
 
     /** {@code true} when a settings change necessitates recreating this activity. */
     private boolean mRecreateActivity;
@@ -89,112 +114,29 @@
     @Override
     public void onNewIntent(Intent newIntent) {
         super.onNewIntent(newIntent);
-        LogUtils.d(TAG, "onNewIntent with intent: %s", newIntent);
 
-        // update our intent so that we can consult it to determine whether or
-        // not the most recent launch was via a dock event
+        // Fragments may query the latest intent for information, so update the intent.
         setIntent(newIntent);
-
-        // Honor the tab requested by the intent, if any.
-        int tab = newIntent.getIntExtra(SELECT_TAB_INTENT_EXTRA, -1);
-        if (tab != -1 && mTabLayout != null) {
-            mTabLayout.getTabAt(tab).select();
-            mViewPager.setCurrentItem(tab);
-        }
-    }
-
-    @VisibleForTesting
-    DeskClockFragment getSelectedFragment() {
-        return (DeskClockFragment) mTabsAdapter.getItem(mSelectedTab);
-    }
-
-    private void createTabs() {
-        final TabLayout.Tab alarmTab = mTabLayout.newTab();
-        alarmTab.setIcon(R.drawable.ic_tab_alarm).setContentDescription(R.string.menu_alarm);
-        mTabsAdapter.addTab(alarmTab, AlarmClockFragment.class, ALARM_TAB_INDEX);
-
-        final Tab clockTab = mTabLayout.newTab();
-        clockTab.setIcon(R.drawable.ic_tab_clock).setContentDescription(R.string.menu_clock);
-        mTabsAdapter.addTab(clockTab, ClockFragment.class, CLOCK_TAB_INDEX);
-
-        final Tab timerTab = mTabLayout.newTab();
-        timerTab.setIcon(R.drawable.ic_tab_timer).setContentDescription(R.string.menu_timer);
-        mTabsAdapter.addTab(timerTab, TimerFragment.class, TIMER_TAB_INDEX);
-
-        final Tab stopwatchTab = mTabLayout.newTab();
-        stopwatchTab.setIcon(R.drawable.ic_tab_stopwatch)
-                .setContentDescription(R.string.menu_stopwatch);
-        mTabsAdapter.addTab(stopwatchTab, StopwatchFragment.class, STOPWATCH_TAB_INDEX);
-
-        mTabLayout.getTabAt(mSelectedTab).select();
-        mViewPager.setCurrentItem(mSelectedTab);
-        mTabsAdapter.notifySelectedPage(mSelectedTab);
     }
 
     @Override
-    protected void onCreate(Bundle icicle) {
-        super.onCreate(icicle);
-        setVolumeControlStream(AudioManager.STREAM_ALARM);
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
 
-        if (icicle != null) {
-            mSelectedTab = icicle.getInt(KEY_SELECTED_TAB, CLOCK_TAB_INDEX);
-        } else {
-            mSelectedTab = CLOCK_TAB_INDEX;
-
+        if (savedInstanceState == null) {
             // Set the background color to initially match the theme value so that we can
             // smoothly transition to the dynamic color.
-            setBackgroundColor(getResources().getColor(R.color.default_background),
-                    false /* animate */);
-        }
-
-        // Honor the tab requested by the intent, if any.
-        final Intent intent = getIntent();
-        if (intent != null) {
-            int tab = intent.getIntExtra(SELECT_TAB_INTENT_EXTRA, -1);
-            if (tab != -1) {
-                mSelectedTab = tab;
-            }
+            final int backgroundColor = ContextCompat.getColor(this, R.color.default_background);
+            setBackgroundColor(backgroundColor, false /* animate */);
         }
 
         setContentView(R.layout.desk_clock);
+
+        // Configure the toolbar.
         final Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
         setSupportActionBar(toolbar);
-        mTabLayout = (TabLayout) findViewById(R.id.sliding_tabs);
-        mFab = (ImageView) findViewById(R.id.fab);
-        mLeftButton = (ImageButton) findViewById(R.id.left_button);
-        mRightButton = (ImageButton) findViewById(R.id.right_button);
-        if (mTabsAdapter == null) {
-            mViewPager = (RtlViewPager) findViewById(R.id.desk_clock_pager);
-            // Keep all four tabs to minimize jank.
-            mViewPager.setOffscreenPageLimit(3);
-            // Set Accessibility Delegate to null so ViewPager doesn't intercept movements and
-            // prevent the fab from being selected.
-            mViewPager.setAccessibilityDelegate(null);
-            mTabsAdapter = new TabsAdapter(this, mViewPager);
-            createTabs();
-            mTabLayout.setOnTabSelectedListener(new ViewPagerOnTabSelectedListener(mViewPager));
-        }
 
-        mFab.setOnClickListener(new OnClickListener() {
-            @Override
-            public void onClick(View view) {
-                getSelectedFragment().onFabClick(view);
-            }
-        });
-        mLeftButton.setOnClickListener(new OnClickListener() {
-            @Override
-            public void onClick(View view) {
-                getSelectedFragment().onLeftButtonClick(view);
-            }
-        });
-        mRightButton.setOnClickListener(new OnClickListener() {
-            @Override
-            public void onClick(View view) {
-                getSelectedFragment().onRightButtonClick(view);
-            }
-        });
-
-        // Configure the menu item controllers.
+        // Configure the menu item controllers add behavior to the toolbar.
         mActionBarMenuManager
                 .addMenuItemController(new SettingMenuItemController(this))
                 .addMenuItemController(new NightModeMenuItemController(this))
@@ -205,8 +147,87 @@
         // inflation occurs *after* the initial draw and a second layout pass adds in the menu.
         onCreateOptionsMenu(toolbar.getMenu());
 
-        // We need to update the system next alarm time on app startup because the
-        // user might have clear our data.
+        // Create the tabs that make up the user interface.
+        mTabLayout = (TabLayout) findViewById(R.id.sliding_tabs);
+        for (int i = 0; i < UiDataModel.getUiDataModel().getTabCount(); i++) {
+            final Tab tab = UiDataModel.getUiDataModel().getTab(i);
+            mTabLayout.addTab(mTabLayout.newTab()
+                    .setIcon(tab.getIconId())
+                    .setContentDescription(tab.getContentDescriptionId()));
+        }
+
+        // Configure the buttons shared by the tabs.
+        mFab = (ImageView) findViewById(R.id.fab);
+        mLeftButton = (ImageButton) findViewById(R.id.left_button);
+        mRightButton = (ImageButton) findViewById(R.id.right_button);
+
+        mFab.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                getSelectedDeskClockFragment().onFabClick(mFab);
+            }
+        });
+        mLeftButton.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                getSelectedDeskClockFragment().onLeftButtonClick(mLeftButton);
+            }
+        });
+        mRightButton.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                getSelectedDeskClockFragment().onRightButtonClick(mRightButton);
+            }
+        });
+
+        // Build the reusable animations that hide and show the fab and left/right buttons.
+        // These may be used independently or be chained together.
+        final long duration = UiDataModel.getUiDataModel().getShortAnimationDuration();
+        mHideAnimation
+                .setDuration(duration)
+                .play(getScaleAnimator(mFab, 1f, 0f))
+                .with(getAlphaAnimator(mLeftButton, 1f, 0f))
+                .with(getAlphaAnimator(mRightButton, 1f, 0f));
+
+        mShowAnimation
+                .setDuration(duration)
+                .play(getScaleAnimator(mFab, 0f, 1f))
+                .with(getAlphaAnimator(mLeftButton, 0f, 1f))
+                .with(getAlphaAnimator(mRightButton, 0f, 1f));
+
+        // Build the reusable animation that hides and shows only the fab.
+        final ValueAnimator hideFabAnimation = getScaleAnimator(mFab, 1f, 0f);
+        hideFabAnimation.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                getSelectedDeskClockFragment().onUpdateFab(mFab);
+            }
+        });
+        final ValueAnimator showFabAnimation = getScaleAnimator(mFab, 0f, 1f);
+        mUpdateFabOnlyAnimation
+                .setDuration(duration)
+                .play(showFabAnimation)
+                .after(hideFabAnimation);
+
+        // Customize the view pager.
+        mFragmentTabPagerAdapter = new TabFragmentAdapter(this);
+        mFragmentTabPager = (RtlViewPager) findViewById(R.id.desk_clock_pager);
+        // Keep all four tabs to minimize jank.
+        mFragmentTabPager.setOffscreenPageLimit(3);
+        // Set Accessibility Delegate to null so view pager doesn't intercept movements and
+        // prevent the fab from being selected.
+        mFragmentTabPager.setAccessibilityDelegate(null);
+        // Mirror changes made to the selected page of the view pager into UiDataModel.
+        mFragmentTabPager.setOnRTLPageChangeListener(new PageChangeWatcher());
+        mFragmentTabPager.setAdapter(mFragmentTabPagerAdapter);
+
+        // Selecting a tab implicitly selects a page in the view pager.
+        mTabLayout.setOnTabSelectedListener(new ViewPagerOnTabSelectedListener(mFragmentTabPager));
+
+        // Honor changes to the selected tab from outside entities.
+        UiDataModel.getUiDataModel().addTabListener(new TabChangeWatcher());
+
+        // Update the next alarm time on app startup because the user might have altered the data.
         AlarmStateManager.updateNextAlarm(this);
     }
 
@@ -214,6 +235,9 @@
     protected void onResume() {
         super.onResume();
         DataModel.getDataModel().setApplicationInForeground(true);
+
+        // Honor the selected tab in case it changed while the app was paused.
+        updateCurrentTab(UiDataModel.getUiDataModel().getSelectedTabIndex());
     }
 
     @Override
@@ -225,7 +249,7 @@
 
             // A runnable must be posted here or the new DeskClock activity will be recreated in a
             // paused state, even though it is the foreground activity.
-            mViewPager.post(new Runnable() {
+            mFragmentTabPager.post(new Runnable() {
                 @Override
                 public void run() {
                     recreate();
@@ -241,12 +265,6 @@
     }
 
     @Override
-    protected void onSaveInstanceState(Bundle outState) {
-        super.onSaveInstanceState(outState);
-        outState.putInt(KEY_SELECTED_TAB, mTabLayout.getSelectedTabPosition());
-    }
-
-    @Override
     public boolean onCreateOptionsMenu(Menu menu) {
         mActionBarMenuManager.createOptionsMenu(menu, getMenuInflater());
         return true;
@@ -261,10 +279,56 @@
 
     @Override
     public boolean onOptionsItemSelected(MenuItem item) {
-        if (mActionBarMenuManager.handleMenuItemClick(item)) {
-            return true;
+        return mActionBarMenuManager.handleMenuItemClick(item) || super.onOptionsItemSelected(item);
+    }
+
+    /**
+     * Called by the LabelDialogFormat class after the dialog is finished.
+     */
+    @Override
+    public void onDialogLabelSet(Alarm alarm, String label, String tag) {
+        final Fragment frag = getFragmentManager().findFragmentByTag(tag);
+        if (frag instanceof AlarmClockFragment) {
+            ((AlarmClockFragment) frag).setLabel(alarm, label);
         }
-        return super.onOptionsItemSelected(item);
+    }
+
+    /**
+     * Called by the RingtonePickerDialogFragment class after the dialog is finished.
+     */
+    @Override
+    public void onRingtoneSelected(Uri ringtoneUri, String fragmentTag) {
+        final Fragment frag = getFragmentManager().findFragmentByTag(fragmentTag);
+        if (frag instanceof AlarmClockFragment) {
+            ((AlarmClockFragment) frag).setRingtone(ringtoneUri);
+        }
+    }
+
+    @Override
+    public void updateFab(UpdateType updateType) {
+        switch (updateType) {
+            case DISABLE_BUTTONS:
+                mLeftButton.setEnabled(false);
+                mRightButton.setEnabled(false);
+                break;
+
+            case FAB_AND_BUTTONS_IMMEDIATE:
+                final DeskClockFragment f = getSelectedDeskClockFragment();
+                f.onUpdateFab(mFab);
+                f.onUpdateFabButtons(mLeftButton, mRightButton);
+                break;
+
+            case FAB_ONLY_ANIMATED:
+                mUpdateFabOnlyAnimation.start();
+                break;
+
+            case FAB_AND_BUTTONS_ANIMATED:
+                // Ensure there is never more than one mAutoStartShowListener registered.
+                mHideAnimation.removeListener(mAutoStartShowListener);
+                mHideAnimation.addListener(mAutoStartShowListener);
+                mHideAnimation.start();
+                break;
+        }
     }
 
     @Override
@@ -276,196 +340,193 @@
         }
     }
 
-    public void registerPageChangedListener(DeskClockFragment frag) {
-        if (mTabsAdapter != null) {
-            mTabsAdapter.registerPageChangedListener(frag);
+    /**
+     * Configure the {@link #mFragmentTabPager} and {@link #mTabLayout} to display the tab at the
+     * given {@code index}.
+     *
+     * @param index the index of the page to display
+     */
+    private void updateCurrentTab(int index) {
+        final TabLayout.Tab tab = mTabLayout.getTabAt(index);
+        if (tab != null && !tab.isSelected()) {
+            tab.select();
+        }
+        if (mFragmentTabPager.getCurrentItem() != index) {
+            mFragmentTabPager.setCurrentItem(index);
         }
     }
 
-    public void unregisterPageChangedListener(DeskClockFragment frag) {
-        if (mTabsAdapter != null) {
-            mTabsAdapter.unregisterPageChangedListener(frag);
-        }
+    private DeskClockFragment getSelectedDeskClockFragment() {
+        final int index = UiDataModel.getUiDataModel().getSelectedTabIndex();
+        return (DeskClockFragment) mFragmentTabPagerAdapter.getItem(index);
     }
 
     /**
-     * Adapter for wrapping together the ActionBar's tab with the ViewPager
+     * As the view pager changes the selected page, update the model to record the new selected tab.
      */
-    private class TabsAdapter extends FragmentPagerAdapter implements OnPageChangeListener {
+    private class PageChangeWatcher implements OnPageChangeListener {
 
-        private static final String KEY_TAB_POSITION = "tab_position";
+        /** The last reported page scroll state; used to detect exotic state changes. */
+        private int mPriorState = SCROLL_STATE_IDLE;
 
-        final class TabInfo {
-            private final Class<?> clss;
-            private final Bundle args;
-
-            TabInfo(Class<?> _class, int position) {
-                clss = _class;
-                args = new Bundle();
-                args.putInt(KEY_TAB_POSITION, position);
-            }
-
-            public int getPosition() {
-                return args.getInt(KEY_TAB_POSITION, 0);
-            }
-        }
-
-        private final List<TabInfo> mTabs = new ArrayList<>(4 /* number of fragments */);
-        private final Context mContext;
-        private final RtlViewPager mPager;
-        // Used for doing callbacks to fragments.
-        private final Set<String> mFragmentTags = new ArraySet<>(4 /* number of fragments */);
-
-        public TabsAdapter(AppCompatActivity activity, RtlViewPager pager) {
-            super(activity.getFragmentManager());
-            mContext = activity;
-            mPager = pager;
-            mPager.setAdapter(this);
-            mPager.setOnRTLPageChangeListener(this);
-        }
-
-        @Override
-        public Object instantiateItem(ViewGroup container, int position) {
-            return super.instantiateItem(container, mViewPager.getRtlAwareIndex(position));
-        }
-
-        @Override
-        public Fragment getItem(int position) {
-            // Because this public method is called outside many times,
-            // check if it exits first before creating a new one.
-            final String name = makeFragmentName(R.id.desk_clock_pager, position);
-            Fragment fragment = getFragmentManager().findFragmentByTag(name);
-            if (fragment == null) {
-                TabInfo info = mTabs.get(position);
-                fragment = Fragment.instantiate(mContext, info.clss.getName(), info.args);
-                if (fragment instanceof TimerFragment) {
-                    ((TimerFragment) fragment).setFabAppearance();
-                    ((TimerFragment) fragment).setLeftRightButtonAppearance();
-                }
-            }
-            return fragment;
-        }
-
-        /**
-         * Copied from:
-         * android/frameworks/support/v13/java/android/support/v13/app/FragmentPagerAdapter.java#94
-         * Create unique name for the fragment so fragment manager knows it exist.
-         */
-        private String makeFragmentName(int viewId, int index) {
-            return "android:switcher:" + viewId + ":" + index;
-        }
-
-        @Override
-        public int getCount() {
-            return mTabs.size();
-        }
-
-        public void addTab(TabLayout.Tab tab, Class<?> clss, int position) {
-            TabInfo info = new TabInfo(clss, position);
-            mTabs.add(info);
-            mTabLayout.addTab(tab);
-            notifyDataSetChanged();
-        }
-
-        @Override
         public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
-            // Do nothing
-        }
-
-        @Override
-        public void onPageSelected(int position) {
-            // Set the page before doing the menu so that onCreateOptionsMenu knows what page it is.
-            mTabLayout.getTabAt(position).select();
-            notifyPageChanged(position);
-
-            mSelectedTab = position;
-
-            // Avoid sending events for the initial tab selection on launch and the reselecting a
-            // tab after a configuration change.
-            if (DataModel.getDataModel().isApplicationInForeground()) {
-                switch (mSelectedTab) {
-                    case ALARM_TAB_INDEX:
-                        Events.sendAlarmEvent(R.string.action_show, R.string.label_deskclock);
-                        break;
-                    case CLOCK_TAB_INDEX:
-                        Events.sendClockEvent(R.string.action_show, R.string.label_deskclock);
-                        break;
-                    case TIMER_TAB_INDEX:
-                        Events.sendTimerEvent(R.string.action_show, R.string.label_deskclock);
-                        break;
-                    case STOPWATCH_TAB_INDEX:
-                        Events.sendStopwatchEvent(R.string.action_show, R.string.label_deskclock);
-                        break;
-                }
-            }
-
-            final DeskClockFragment f = (DeskClockFragment) getItem(position);
-            if (f != null) {
-                f.setFabAppearance();
-                f.setLeftRightButtonAppearance();
+            // Only hide the fab when a non-zero drag distance is detected. This prevents
+            // over-scrolling from needlessly hiding the fab.
+            if (mFabState == FabState.HIDE_ARMED && positionOffsetPixels != 0) {
+                mFabState = FabState.HIDING;
+                mHideAnimation.start();
             }
         }
 
         @Override
         public void onPageScrollStateChanged(int state) {
-            // Do nothing
-        }
+            if (mPriorState == SCROLL_STATE_IDLE && state == SCROLL_STATE_SETTLING) {
+                // The user has tapped a tab button; play the hide and show animations linearly.
+                mHideAnimation.addListener(mAutoStartShowListener);
+                mHideAnimation.start();
+                mFabState = FabState.HIDING;
 
-        public void notifySelectedPage(int page) {
-            notifyPageChanged(page);
-        }
-
-        private void notifyPageChanged(int newPage) {
-            for (String tag : mFragmentTags) {
-                final FragmentManager fm = getFragmentManager();
-                DeskClockFragment f = (DeskClockFragment) fm.findFragmentByTag(tag);
-                if (f != null) {
-                    f.onPageChanged(newPage);
+            } else if (mPriorState == SCROLL_STATE_SETTLING && state == SCROLL_STATE_DRAGGING) {
+                // The user has interrupted settling on a tab and the fab button must be re-hidden.
+                if (mShowAnimation.isStarted()) {
+                    mShowAnimation.cancel();
                 }
+                if (mHideAnimation.isStarted()) {
+                    // Let the hide animation finish naturally; don't auto show when it ends.
+                    mHideAnimation.removeListener(mAutoStartShowListener);
+                } else {
+                    // Start and immediately end the hide animation to jump to the hidden state.
+                    mHideAnimation.start();
+                    mHideAnimation.end();
+                }
+                mFabState = FabState.HIDING;
+
+            } else if (state != SCROLL_STATE_DRAGGING && mFabState == FabState.HIDING) {
+                // The user has lifted their finger; show the buttons now or after hide ends.
+                if (mHideAnimation.isStarted()) {
+                    // Finish the hide animation and then start the show animation.
+                    mHideAnimation.addListener(mAutoStartShowListener);
+                } else {
+                    updateFab(FAB_AND_BUTTONS_IMMEDIATE);
+                    mShowAnimation.start();
+
+                    // The animation to show the fab has begun; update the state to showing.
+                    mFabState = FabState.SHOWING;
+                }
+            } else if (state == SCROLL_STATE_DRAGGING) {
+                // The user has started a drag so arm the hide animation.
+                mFabState = FabState.HIDE_ARMED;
             }
+
+            // Update the last known state.
+            mPriorState = state;
         }
 
-        public void registerPageChangedListener(DeskClockFragment frag) {
-            String tag = frag.getTag();
-            if (mFragmentTags.contains(tag)) {
-                LogUtils.wtf(TAG, "Trying to add an existing fragment " + tag);
-            } else {
-                mFragmentTags.add(frag.getTag());
-            }
-            // Since registering a listener by the fragment is done sometimes after the page
-            // was already changed, make sure the fragment gets the current page
-            frag.onPageChanged(mTabLayout.getSelectedTabPosition());
-        }
-
-        public void unregisterPageChangedListener(DeskClockFragment frag) {
-            mFragmentTags.remove(frag.getTag());
+        @Override
+        public void onPageSelected(int position) {
+            UiDataModel.getUiDataModel().setSelectedTabIndex(position);
         }
     }
 
     /**
-     * Called by the LabelDialogFormat class after the dialog is finished.
+     * If this listener is attached to {@link #mHideAnimation} when it ends, the corresponding
+     * {@link #mShowAnimation} is automatically started.
      */
-    @Override
-    public void onDialogLabelSet(Alarm alarm, String label, String tag) {
-        Fragment frag = getFragmentManager().findFragmentByTag(tag);
-        if (frag instanceof AlarmClockFragment) {
-            ((AlarmClockFragment) frag).setLabel(alarm, label);
+    private class AutoStartShowListener extends AnimatorListenerAdapter {
+        @Override
+        public void onAnimationEnd(Animator animation) {
+            // Prepare the hide animation for its next use; by default do not auto-show after hide.
+            mHideAnimation.removeListener(mAutoStartShowListener);
+
+            // Update the buttons now that they are no longer visible.
+            updateFab(FAB_AND_BUTTONS_IMMEDIATE);
+
+            // Automatically start the grow animation now that shrinking is complete.
+            mShowAnimation.start();
+
+            // The animation to show the fab has begun; update the state to showing.
+            mFabState = FabState.SHOWING;
         }
     }
 
-    public int getSelectedTab() {
-        return mSelectedTab;
+    /**
+     * As the model reports changes to the selected tab, update the user interface.
+     */
+    private class TabChangeWatcher implements TabListener {
+        @Override
+        public void selectedTabChanged(Tab oldSelectedTab, Tab newSelectedTab) {
+            final int index = newSelectedTab.ordinal();
+
+            // Update the view pager and tab layout to agree with the model.
+            updateCurrentTab(index);
+
+            // Avoid sending events for the initial tab selection on launch and re-selecting a tab
+            // after a configuration change.
+            if (DataModel.getDataModel().isApplicationInForeground()) {
+                switch (newSelectedTab) {
+                    case ALARMS:
+                        Events.sendAlarmEvent(R.string.action_show, R.string.label_deskclock);
+                        break;
+                    case CLOCKS:
+                        Events.sendClockEvent(R.string.action_show, R.string.label_deskclock);
+                        break;
+                    case TIMERS:
+                        Events.sendTimerEvent(R.string.action_show, R.string.label_deskclock);
+                        break;
+                    case STOPWATCH:
+                        Events.sendStopwatchEvent(R.string.action_show, R.string.label_deskclock);
+                        break;
+                }
+            }
+
+            // If the hide animation has already completed, the buttons must be updated now when the
+            // new tab is known. Otherwise they are updated at the end of the hide animation.
+            if (!mHideAnimation.isStarted()) {
+                updateFab(FAB_AND_BUTTONS_IMMEDIATE);
+            }
+        }
     }
 
-    public ImageView getFab() {
-        return mFab;
-    }
+    /**
+     * This adapter produces the DeskClockFragments that are the contents of the tabs.
+     */
+    private static class TabFragmentAdapter extends FragmentPagerAdapter {
 
-    public ImageButton getLeftButton() {
-        return mLeftButton;
-    }
+        private final FragmentManager mFragmentManager;
+        private final Context mContext;
 
-    public ImageButton getRightButton() {
-        return mRightButton;
+        public TabFragmentAdapter(AppCompatActivity activity) {
+            super(activity.getFragmentManager());
+            mContext = activity;
+            mFragmentManager = activity.getFragmentManager();
+        }
+
+        @Override
+        public Object instantiateItem(ViewGroup container, int position) {
+            position = UiDataModel.getUiDataModel().getTabLayoutIndex(position);
+            return super.instantiateItem(container, position);
+        }
+
+        @Override
+        public Fragment getItem(int position) {
+            final String tag = makeFragmentName(R.id.desk_clock_pager, position);
+            Fragment fragment = mFragmentManager.findFragmentByTag(tag);
+            if (fragment == null) {
+                final Tab tab = UiDataModel.getUiDataModel().getTab(position);
+                final String fragmentClassName = tab.getFragmentClassName();
+                fragment = Fragment.instantiate(mContext, fragmentClassName);
+            }
+            return fragment;
+        }
+
+        @Override
+        public int getCount() {
+            return UiDataModel.getUiDataModel().getTabCount();
+        }
+
+        /** This implementation duplicated from {@link FragmentPagerAdapter#makeFragmentName}. */
+        private String makeFragmentName(int viewId, long id) {
+            return "android:switcher:" + viewId + ":" + id;
+        }
     }
 }
diff --git a/src/com/android/deskclock/DeskClockApplication.java b/src/com/android/deskclock/DeskClockApplication.java
index c560f01..8edac6b 100644
--- a/src/com/android/deskclock/DeskClockApplication.java
+++ b/src/com/android/deskclock/DeskClockApplication.java
@@ -17,10 +17,12 @@
 package com.android.deskclock;
 
 import android.app.Application;
+import android.content.Context;
 
 import com.android.deskclock.data.DataModel;
 import com.android.deskclock.events.Events;
 import com.android.deskclock.events.LogEventTracker;
+import com.android.deskclock.uidata.UiDataModel;
 
 public class DeskClockApplication extends Application {
 
@@ -28,8 +30,9 @@
     public void onCreate() {
         super.onCreate();
 
-        DataModel.getDataModel().setContext(getApplicationContext());
-
-        Events.addEventTracker(new LogEventTracker(getApplicationContext()));
+        final Context applicationContext = getApplicationContext();
+        DataModel.getDataModel().setContext(applicationContext);
+        UiDataModel.getUiDataModel().setContext(applicationContext);
+        Events.addEventTracker(new LogEventTracker(applicationContext));
     }
 }
diff --git a/src/com/android/deskclock/DeskClockFragment.java b/src/com/android/deskclock/DeskClockFragment.java
index 669f1ef..ff5bf68 100644
--- a/src/com/android/deskclock/DeskClockFragment.java
+++ b/src/com/android/deskclock/DeskClockFragment.java
@@ -16,61 +16,61 @@
 
 package com.android.deskclock;
 
-import android.app.Activity;
 import android.app.Fragment;
-import android.os.Bundle;
-import android.view.View;
+import android.support.annotation.NonNull;
 import android.widget.ImageButton;
-import android.widget.ImageView;
 
-public class DeskClockFragment extends Fragment {
+import com.android.deskclock.uidata.UiDataModel;
+import com.android.deskclock.uidata.UiDataModel.Tab;
 
-    protected ImageView mFab;
-    protected ImageButton mLeftButton;
-    protected ImageButton mRightButton;
+import static com.android.deskclock.FabContainer.UpdateType.FAB_AND_BUTTONS_IMMEDIATE;
 
-    public void onPageChanged(int page) {
-        // Do nothing here , only in derived classes
-    }
+public abstract class DeskClockFragment extends Fragment implements FabContainer, FabController {
 
-    public void onFabClick(View view){
-        // Do nothing here , only in derived classes
+    /** The tab associated with this fragment. */
+    private final Tab mTab;
+
+    public DeskClockFragment(Tab tab) {
+        mTab = tab;
     }
 
     @Override
-    public void onActivityCreated(Bundle savedInstanceState) {
-        super.onActivityCreated(savedInstanceState);
-        final Activity activity = getActivity();
-        if (activity instanceof DeskClock) {
-            final DeskClock deskClockActivity = (DeskClock) activity;
-            mFab = deskClockActivity.getFab();
-            mLeftButton = deskClockActivity.getLeftButton();
-            mRightButton = deskClockActivity.getRightButton();
+    public void onResume() {
+        super.onResume();
+
+        // Update the fab and buttons in case their state changed while the fragment was paused.
+        if (isTabSelected()) {
+            updateFab(FAB_AND_BUTTONS_IMMEDIATE);
         }
     }
 
-    public void setFabAppearance() {
-        // Do nothing here , only in derived classes
+    @Override
+    public void onLeftButtonClick(@NonNull ImageButton left) {
+        // Do nothing here, only in derived classes
     }
 
-    public void setLeftRightButtonAppearance() {
-        // Do nothing here , only in derived classes
+    @Override
+    public void onRightButtonClick(@NonNull ImageButton right) {
+        // Do nothing here, only in derived classes
     }
 
-    public void onLeftButtonClick(View view) {
-        // Do nothing here , only in derived classes
+    /**
+     * Requests that the parent activity update the fab and buttons.
+     *
+     * @param updateType the manner in which the fab container should be updated
+     */
+    @Override
+    public final void updateFab(FabContainer.UpdateType updateType) {
+        final FabContainer parentFabContainer = (FabContainer) getActivity();
+        if (parentFabContainer != null) {
+            parentFabContainer.updateFab(updateType);
+        }
     }
 
-    public void onRightButtonClick(View view) {
-        // Do nothing here , only in derived classes
+    /**
+     * @return {@code true} iff the currently selected tab displays this fragment
+     */
+    public final boolean isTabSelected() {
+        return UiDataModel.getUiDataModel().getSelectedTab() == mTab;
     }
-
-    protected final DeskClock getDeskClock() {
-        return (DeskClock) getActivity();
-    }
-
-    protected final int getSelectedTab() {
-        final DeskClock deskClock = getDeskClock();
-        return deskClock == null ? -1 : deskClock.getSelectedTab();
-    }
-}
+}
\ No newline at end of file
diff --git a/src/com/android/deskclock/FabContainer.java b/src/com/android/deskclock/FabContainer.java
new file mode 100644
index 0000000..6899743
--- /dev/null
+++ b/src/com/android/deskclock/FabContainer.java
@@ -0,0 +1,32 @@
+package com.android.deskclock;
+
+/**
+ * Implemented by containers that house the fab and its associated buttons. Also implemented by
+ * containers that know how to contact the <strong>true</strong> fab container to ferry through
+ * commands.
+ */
+public interface FabContainer {
+
+    enum UpdateType {
+        /** Signals just the fab should be "animated away", updated, and "animated back". */
+        FAB_ONLY_ANIMATED,
+
+        /** Signals the fab and buttons should be "animated away", updated, and "animated back". */
+        FAB_AND_BUTTONS_ANIMATED,
+
+        /** Signals that the fab and buttons should be updated in place with no animation. */
+        FAB_AND_BUTTONS_IMMEDIATE,
+
+        /** Disable the buttons of the fab so they do not respond to clicks. */
+        DISABLE_BUTTONS
+    }
+
+    /**
+     * Requests that this container update the fab and/or its buttons because their state has
+     * changed. The update may be immediate or it may be animated depending on the choice of
+     * {@code updateType}.
+     *
+     * @param updateType indicates the type of update to apply to the fab and its buttons
+     */
+    void updateFab(UpdateType updateType);
+}
\ No newline at end of file
diff --git a/src/com/android/deskclock/FabController.java b/src/com/android/deskclock/FabController.java
new file mode 100644
index 0000000..e507009
--- /dev/null
+++ b/src/com/android/deskclock/FabController.java
@@ -0,0 +1,53 @@
+package com.android.deskclock;
+
+import android.support.annotation.NonNull;
+import android.view.View;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+
+/**
+ * Implementers of this interface are able to {@link #onUpdateFab configure the fab} and associated
+ * {@link #onUpdateFabButtons left/right buttons} including setting them {@link View#INVISIBLE} if
+ * they are unnecessary. Implementers also attach click handler logic to the
+ * {@link #onFabClick fab}, {@link #onLeftButtonClick left button} and
+ * {@link #onRightButtonClick right button}.
+ */
+public interface FabController {
+
+    /**
+     * Configures the display of the fab component to match the current state of this controller.
+     *
+     * @param fab the fab component to be configured based on current state
+     */
+    void onUpdateFab(@NonNull ImageView fab);
+
+    /**
+     * Configures the display of the buttons to the left and right of the fab to match the current
+     * state of this controller.
+     *
+     * @param left button to the left of the fab to configure based on current state
+     * @param right button to the right of the fab to configure based on current state
+     */
+    void onUpdateFabButtons(@NonNull ImageButton left, @NonNull ImageButton right);
+
+    /**
+     * Handles a click on the fab.
+     *
+     * @param fab the fab component on which the click occurred
+     */
+    void onFabClick(@NonNull ImageView fab);
+
+    /**
+     * Handles a click on the button to the left of the fab component.
+     *
+     * @param left the button to the left of the fab component
+     */
+    void onLeftButtonClick(@NonNull ImageButton left);
+
+    /**
+     * Handles a click on the button to the right of the fab component.
+     *
+     * @param right the button to the right of the fab component
+     */
+    void onRightButtonClick(@NonNull ImageButton right);
+}
\ No newline at end of file
diff --git a/src/com/android/deskclock/HandleApiCalls.java b/src/com/android/deskclock/HandleApiCalls.java
index 06cb65a..8212c7f 100644
--- a/src/com/android/deskclock/HandleApiCalls.java
+++ b/src/com/android/deskclock/HandleApiCalls.java
@@ -38,6 +38,7 @@
 import com.android.deskclock.provider.AlarmInstance;
 import com.android.deskclock.provider.DaysOfWeek;
 import com.android.deskclock.timer.TimerFragment;
+import com.android.deskclock.uidata.UiDataModel;
 
 import java.util.ArrayList;
 import java.util.Calendar;
@@ -45,6 +46,8 @@
 import java.util.List;
 
 import static android.text.format.DateUtils.SECOND_IN_MILLIS;
+import static com.android.deskclock.uidata.UiDataModel.Tab.ALARMS;
+import static com.android.deskclock.uidata.UiDataModel.Tab.TIMERS;
 
 /**
  * This activity is never visible. It processes all public intents defined by {@link AlarmClock}
@@ -76,7 +79,7 @@
                     handleSetTimer(intent);
                     break;
                 case AlarmClock.ACTION_DISMISS_ALARM:
-                    handleDismissAlarm(intent.getAction());
+                    handleDismissAlarm(intent);
                     break;
                 case AlarmClock.ACTION_SNOOZE_ALARM:
                     handleSnoozeAlarm();
@@ -86,15 +89,12 @@
         }
     }
 
-    private void handleDismissAlarm(final String action) {
-        // Opens the UI for Alarms
-        final Intent alarmIntent =
-                Alarm.createIntent(mAppContext, DeskClock.class, Alarm.INVALID_ID)
-                        .setAction(action)
-                        .putExtra(DeskClock.SELECT_TAB_INTENT_EXTRA, DeskClock.ALARM_TAB_INDEX);
-        startActivity(alarmIntent);
+    private void handleDismissAlarm(Intent intent) {
+        // Change to the alarms tab.
+        UiDataModel.getUiDataModel().setSelectedTab(ALARMS);
 
-        final Intent intent = getIntent();
+        // Open DeskClock which is now positioned on the alarms tab.
+        startActivity(new Intent(mAppContext, DeskClock.class));
 
         new DismissAlarmAsync(mAppContext, intent, this).execute();
     }
@@ -273,11 +273,15 @@
             minutes = 0;
         }
         if (hour < 0 || hour > 23 || minutes < 0 || minutes > 59) {
-            // Intent has no time or an invalid time, open the alarm creation UI
-            Intent createAlarm = Alarm.createIntent(this, DeskClock.class, Alarm.INVALID_ID);
-            createAlarm.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-            createAlarm.putExtra(AlarmClockFragment.ALARM_CREATE_NEW_INTENT_EXTRA, true);
-            createAlarm.putExtra(DeskClock.SELECT_TAB_INTENT_EXTRA, DeskClock.ALARM_TAB_INDEX);
+            // Change to the alarms tab.
+            UiDataModel.getUiDataModel().setSelectedTab(ALARMS);
+
+            // Intent has no time or an invalid time, open the alarm creation UI.
+            final Intent createAlarm = Alarm.createIntent(this, DeskClock.class, Alarm.INVALID_ID)
+                    .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+                    .putExtra(AlarmClockFragment.ALARM_CREATE_NEW_INTENT_EXTRA, true);
+
+            // Open DeskClock which is now positioned on the alarms tab.
             startActivity(createAlarm);
             Voice.notifyFailure(this, getString(R.string.invalid_time, hour, minutes, " "));
             LogUtils.i("HandleApiCalls no/invalid time; opening UI");
@@ -322,8 +326,12 @@
     }
 
     private void handleShowAlarms() {
-        startActivity(new Intent(this, DeskClock.class)
-                .putExtra(DeskClock.SELECT_TAB_INTENT_EXTRA, DeskClock.ALARM_TAB_INDEX));
+        // Change to the alarms tab.
+        UiDataModel.getUiDataModel().setSelectedTab(ALARMS);
+
+        // Open DeskClock which is now positioned on the alarms tab.
+        startActivity(new Intent(this, DeskClock.class));
+
         Events.sendAlarmEvent(R.string.action_show, R.string.label_intent);
         LogUtils.i("HandleApiCalls show alarms");
     }
@@ -331,6 +339,10 @@
     private void handleSetTimer(Intent intent) {
         // If no length is supplied, show the timer setup view.
         if (!intent.hasExtra(AlarmClock.EXTRA_LENGTH)) {
+            // Change to the timers tab.
+            UiDataModel.getUiDataModel().setSelectedTab(TIMERS);
+
+            // Open DeskClock which is now positioned on the timers tab and show the timer setup.
             startActivity(TimerFragment.createTimerSetupIntent(this));
             LogUtils.i("HandleApiCalls showing timer setup");
             return;
@@ -371,8 +383,11 @@
 
         // If not instructed to skip the UI, display the running timer.
         if (!skipUi) {
+            // Change to the timers tab.
+            UiDataModel.getUiDataModel().setSelectedTab(TIMERS);
+
+            // Open DeskClock which is now positioned on the timers tab.
             startActivity(new Intent(this, DeskClock.class)
-                    .putExtra(DeskClock.SELECT_TAB_INTENT_EXTRA, DeskClock.TIMER_TAB_INDEX)
                     .putExtra(HandleDeskClockApiCalls.EXTRA_TIMER_ID, timer.getId()));
         }
     }
@@ -382,10 +397,13 @@
         AlarmStateManager.registerInstance(this, instance, true);
         AlarmUtils.popAlarmSetToast(this, instance.getAlarmTime().getTimeInMillis());
         if (!skipUi) {
-            Intent showAlarm = Alarm.createIntent(this, DeskClock.class, instance.mAlarmId);
-            showAlarm.putExtra(DeskClock.SELECT_TAB_INTENT_EXTRA, DeskClock.ALARM_TAB_INDEX);
-            showAlarm.putExtra(AlarmClockFragment.SCROLL_TO_ALARM_INTENT_EXTRA, instance.mAlarmId);
-            showAlarm.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            // Change to the alarms tab.
+            UiDataModel.getUiDataModel().setSelectedTab(ALARMS);
+
+            // Open DeskClock which is now positioned on the alarms tab.
+            final Intent showAlarm = Alarm.createIntent(this, DeskClock.class, instance.mAlarmId)
+                    .putExtra(AlarmClockFragment.SCROLL_TO_ALARM_INTENT_EXTRA, instance.mAlarmId)
+                    .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
             startActivity(showAlarm);
         }
     }
diff --git a/src/com/android/deskclock/HandleDeskClockApiCalls.java b/src/com/android/deskclock/HandleDeskClockApiCalls.java
index 224bf19..ee3f433 100644
--- a/src/com/android/deskclock/HandleDeskClockApiCalls.java
+++ b/src/com/android/deskclock/HandleDeskClockApiCalls.java
@@ -25,13 +25,17 @@
 import com.android.deskclock.data.DataModel;
 import com.android.deskclock.data.Timer;
 import com.android.deskclock.events.Events;
+import com.android.deskclock.uidata.UiDataModel;
 import com.android.deskclock.worldclock.CitySelectionActivity;
 
 import java.util.List;
 import java.util.Set;
 
+import static com.android.deskclock.uidata.UiDataModel.Tab.CLOCKS;
+import static com.android.deskclock.uidata.UiDataModel.Tab.STOPWATCH;
+import static com.android.deskclock.uidata.UiDataModel.Tab.TIMERS;
+
 public class HandleDeskClockApiCalls extends Activity {
-    private Context mAppContext;
 
     private static final String ACTION_PREFIX = "com.android.deskclock.action.";
 
@@ -70,12 +74,13 @@
     public static final String ACTION_ADD_MINUTE_TIMER = ACTION_PREFIX + "ADD_MINUTE_TIMER";
 
     // extra for many actions specific to a given timer
-    public static final String EXTRA_TIMER_ID =
-            "com.android.deskclock.extra.TIMER_ID";
+    public static final String EXTRA_TIMER_ID = "com.android.deskclock.extra.TIMER_ID";
 
     // Describes the entity responsible for the action being performed.
     public static final String EXTRA_EVENT_LABEL = "com.android.deskclock.extra.EVENT_LABEL";
 
+    private Context mAppContext;
+
     @Override
     protected void onCreate(Bundle icicle) {
         try {
@@ -169,10 +174,11 @@
             LogUtils.i(reason);
         }
 
-        // Open the UI to the stopwatch.
-        final Intent stopwatchIntent = new Intent(mAppContext, DeskClock.class)
-                .putExtra(DeskClock.SELECT_TAB_INTENT_EXTRA, DeskClock.STOPWATCH_TAB_INDEX);
-        startActivity(stopwatchIntent);
+        // Change to the stopwatch tab.
+        UiDataModel.getUiDataModel().setSelectedTab(STOPWATCH);
+
+        // Open DeskClock which is now positioned on the stopwatch tab.
+        startActivity(new Intent(mAppContext, DeskClock.class));
     }
 
     private void handleTimerIntent(Intent intent) {
@@ -215,8 +221,8 @@
                 // Otherwise the control command can be honored.
                 switch (action) {
                     case ACTION_RESET_TIMER: {
-                        DataModel.getDataModel().resetOrDeleteTimer(timer, eventLabel);
-                        if (timer.isExpired() && timer.getDeleteAfterUse()) {
+                        timer = DataModel.getDataModel().resetOrDeleteTimer(timer, eventLabel);
+                        if (timer == null) {
                             timerId = -1;
                             reason = getString(R.string.timer_deleted);
                         } else {
@@ -246,9 +252,11 @@
             LogUtils.i(reason);
         }
 
-        // Open the UI to the timers.
-        final Intent showTimers = new Intent(mAppContext, DeskClock.class)
-                .putExtra(DeskClock.SELECT_TAB_INTENT_EXTRA, DeskClock.TIMER_TAB_INDEX);
+        // Change to the timers tab.
+        UiDataModel.getUiDataModel().setSelectedTab(TIMERS);
+
+        // Open DeskClock which is now positioned on the timers tab and show the timer in question.
+        final Intent showTimers = new Intent(mAppContext, DeskClock.class);
         if (timerId != -1) {
             showTimers.putExtra(EXTRA_TIMER_ID, timerId);
         }
@@ -263,6 +271,15 @@
             final int label = fromWidget ? R.string.label_widget : R.string.label_intent;
             Events.sendClockEvent(R.string.action_show, label);
         } else {
+            switch (action) {
+                case ACTION_ADD_CLOCK:
+                    Events.sendClockEvent(R.string.action_add, R.string.label_intent);
+                    break;
+                case ACTION_DELETE_CLOCK:
+                    Events.sendClockEvent(R.string.action_delete, R.string.label_intent);
+                    break;
+            }
+
             final String cityName = intent.getStringExtra(EXTRA_CITY);
 
             final String reason;
@@ -275,14 +292,6 @@
                 Voice.notifySuccess(this, reason);
                 startActivity(new Intent(this, CitySelectionActivity.class)
                         .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
-                switch (action) {
-                    case ACTION_ADD_CLOCK:
-                        Events.sendClockEvent(R.string.action_add, R.string.label_intent);
-                        break;
-                    case ACTION_DELETE_CLOCK:
-                        Events.sendClockEvent(R.string.action_delete, R.string.label_intent);
-                        break;
-                }
                 return;
             }
 
@@ -292,14 +301,8 @@
                 reason = getString(R.string.the_city_you_specified_is_not_available);
                 LogUtils.i(reason);
                 Voice.notifyFailure(this, reason);
-                switch (action) {
-                    case ACTION_ADD_CLOCK:
-                        Events.sendClockEvent(R.string.action_add, R.string.label_intent);
-                        break;
-                    case ACTION_DELETE_CLOCK:
-                        Events.sendClockEvent(R.string.action_delete, R.string.label_intent);
-                        break;
-                }
+                startActivity(new Intent(this, CitySelectionActivity.class)
+                        .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
                 return;
             }
 
@@ -318,7 +321,6 @@
                     // Otherwise report the success.
                     DataModel.getDataModel().setSelectedCities(selectedCities);
                     reason = getString(R.string.city_added, city.getName());
-                    Events.sendClockEvent(R.string.action_add, R.string.label_intent);
                     break;
                 }
                 case ACTION_DELETE_CLOCK: {
@@ -332,7 +334,6 @@
                     // Otherwise report the success.
                     DataModel.getDataModel().setSelectedCities(selectedCities);
                     reason = getString(R.string.city_deleted, city.getName());
-                    Events.sendClockEvent(R.string.action_delete, R.string.label_intent);
                     break;
                 }
                 default:
@@ -347,11 +348,10 @@
             LogUtils.i(reason);
         }
 
-        // Opens the UI for clocks
-        final Intent clockIntent = new Intent(mAppContext, DeskClock.class)
-                .setAction(action)
-                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
-                .putExtra(DeskClock.SELECT_TAB_INTENT_EXTRA, DeskClock.CLOCK_TAB_INDEX);
-        startActivity(clockIntent);
+        // Change to the clocks tab.
+        UiDataModel.getUiDataModel().setSelectedTab(CLOCKS);
+
+        // Open DeskClock which is now positioned on the clocks tab.
+        startActivity(new Intent(mAppContext, DeskClock.class));
     }
 }
\ No newline at end of file
diff --git a/src/com/android/deskclock/ItemAdapter.java b/src/com/android/deskclock/ItemAdapter.java
new file mode 100644
index 0000000..f67abce
--- /dev/null
+++ b/src/com/android/deskclock/ItemAdapter.java
@@ -0,0 +1,474 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+package com.android.deskclock;
+
+import android.os.Bundle;
+import android.support.v7.widget.RecyclerView;
+import android.util.SparseArray;
+import android.view.View;
+import android.view.ViewGroup;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Base adapter class for displaying a collection of items. Provides functionality for handling
+ * changing items, persistent item state, item click events, and re-usable item views.
+ */
+public class ItemAdapter<T extends ItemAdapter.ItemHolder> extends RecyclerView.Adapter<ItemAdapter.ItemViewHolder> {
+
+    /**
+     * Finds the position of the changed item holder and invokes {@link #notifyItemChanged(int)}.
+     */
+    private final OnItemChangedListener mItemChangedNotifier = new OnItemChangedListener() {
+        @Override
+        public void onItemChanged(ItemHolder<?> itemHolder) {
+            if (mOnItemChangedListener != null) {
+                mOnItemChangedListener.onItemChanged(itemHolder);
+            }
+            final int position = mItemHolders.indexOf(itemHolder);
+            if (position != RecyclerView.NO_POSITION) {
+                notifyItemChanged(position);
+            }
+        }
+    };
+
+    /**
+     * Invokes the {@link OnItemClickedListener} in {@link #mListenersByViewType} corresponding
+     * to {@link ItemViewHolder#getItemViewType()}
+     */
+    private final OnItemClickedListener mOnItemClickedListener = new OnItemClickedListener() {
+        @Override
+        public void onItemClicked(ItemViewHolder<?> viewHolder, int id) {
+            final OnItemClickedListener listener =
+                    mListenersByViewType.get(viewHolder.getItemViewType());
+            if (listener != null) {
+                listener.onItemClicked(viewHolder, id);
+            }
+        }
+    };
+
+    /**
+     * Invoked when any item changes.
+     */
+    private OnItemChangedListener mOnItemChangedListener;
+
+    /**
+     * Factories for creating new {@link ItemViewHolder} entities.
+     */
+    private final SparseArray<ItemViewHolder.Factory> mFactoriesByViewType = new SparseArray<>();
+
+    /**
+     * Listeners to invoke in {@link #mOnItemClickedListener}.
+     */
+    private final SparseArray<OnItemClickedListener> mListenersByViewType = new SparseArray<>();
+
+    /**
+     * List of current item holders represented by this adapter.
+     */
+    private List<T> mItemHolders;
+
+    /**
+     * Convenience for calling {@link #setHasStableIds(boolean)} with {@code true}.
+     *
+     * @return this object, allowing calls to methods in this class to be chained
+     */
+    public ItemAdapter setHasStableIds() {
+        setHasStableIds(true);
+        return this;
+    }
+
+    /**
+     * Sets the {@link ItemViewHolder.Factory} and {@link OnItemClickedListener} used to create
+     * new item view holders in {@link #onCreateViewHolder(ViewGroup, int)}.
+     *
+     * @param factory   the {@link ItemViewHolder.Factory} used to create new item view holders
+     * @param listener  the {@link OnItemClickedListener} to be invoked by
+     *                  {@link #mItemChangedNotifier}
+     * @param viewTypes the unique identifier for the view types to be created
+     * @return this object, allowing calls to methods in this class to be chained
+     */
+    public ItemAdapter withViewTypes(ItemViewHolder.Factory factory,
+            OnItemClickedListener listener, int... viewTypes) {
+        for (int viewType : viewTypes) {
+            mFactoriesByViewType.put(viewType, factory);
+            mListenersByViewType.put(viewType, listener);
+        }
+        return this;
+    }
+
+    /**
+     * @return the current list of item holders represented by this adapter
+     */
+    public final List<T> getItems() {
+        return mItemHolders;
+    }
+
+    /**
+     * Sets the list of item holders to serve as the dataset for this adapter and invokes
+     * {@link #notifyDataSetChanged()} to update the UI.
+     * <p/>
+     * If {@link #hasStableIds()} returns {@code true}, then the instance state will preserved
+     * between new and old holders that have matching {@link ItemHolder#itemId} values.
+     *
+     * @param itemHolders the new list of item holders
+     * @return this object, allowing calls to methods in this class to be chained
+     */
+    public ItemAdapter setItems(List<T> itemHolders) {
+        final List<T> oldItemHolders = mItemHolders;
+        if (oldItemHolders != itemHolders) {
+            if (oldItemHolders != null) {
+                // remove the item change listener from the old item holders
+                for (T oldItemHolder : oldItemHolders) {
+                    oldItemHolder.removeOnItemChangedListener(mItemChangedNotifier);
+                }
+            }
+
+            if (oldItemHolders != null && itemHolders != null && hasStableIds()) {
+                // transfer instance state from old to new item holders based on item id,
+                // we use a simple O(N^2) implementation since we assume the number of items is
+                // relatively small and generating a temporary map would be more expensive
+                final Bundle bundle = new Bundle();
+                for (ItemHolder newItemHolder : itemHolders) {
+                    for (ItemHolder oldItemHolder : oldItemHolders) {
+                        if (newItemHolder.itemId == oldItemHolder.itemId
+                                && newItemHolder != oldItemHolder) {
+                            // clear any existing state from the bundle
+                            bundle.clear();
+
+                            // transfer instance state from old to new item holder
+                            oldItemHolder.onSaveInstanceState(bundle);
+                            newItemHolder.onRestoreInstanceState(bundle);
+
+                            break;
+                        }
+                    }
+                }
+            }
+
+            if (itemHolders != null) {
+                // add the item change listener to the new item holders
+                for (ItemHolder newItemHolder : itemHolders) {
+                    newItemHolder.addOnItemChangedListener(mItemChangedNotifier);
+                }
+            }
+
+            // finally update the current list of item holders and inform the RV to update the UI
+            mItemHolders = itemHolders;
+            notifyDataSetChanged();
+        }
+
+        return this;
+    }
+
+    /**
+     * Sets the listener to be invoked whenever any item changes.
+     */
+    public void setOnItemChangedListener(OnItemChangedListener listener) {
+        mOnItemChangedListener = listener;
+    }
+
+    @Override
+    public int getItemCount() {
+        return mItemHolders == null ? 0 : mItemHolders.size();
+    }
+
+    @Override
+    public long getItemId(int position) {
+        return mItemHolders.get(position).itemId;
+    }
+
+    public T findItemById(long id) {
+        for (T holder : mItemHolders) {
+            if (holder.itemId == id) {
+                return holder;
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public int getItemViewType(int position) {
+        return mItemHolders.get(position).getItemViewType();
+    }
+
+    @Override
+    public ItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+        final ItemViewHolder.Factory factory = mFactoriesByViewType.get(viewType);
+        if (factory != null) {
+            return factory.createViewHolder(parent, viewType);
+        }
+        throw new IllegalArgumentException("Unsupported view type: " + viewType);
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public void onBindViewHolder(ItemViewHolder viewHolder, int position) {
+        // suppress any unchecked warnings since it is up to the subclass to guarantee
+        // compatibility of their view holders with the item holder at the corresponding position
+        viewHolder.bindItemView(mItemHolders.get(position));
+        viewHolder.setOnItemClickedListener(mOnItemClickedListener);
+    }
+
+    @Override
+    public void onViewRecycled(ItemViewHolder viewHolder) {
+        viewHolder.setOnItemClickedListener(null);
+        viewHolder.recycleItemView();
+    }
+
+    /**
+     * Base class for wrapping an item for compatibility with an {@link ItemHolder}.
+     * <p/>
+     * An {@link ItemHolder} serves as bridge between the model and view layer; subclassers should
+     * implement properties that fall beyond the scope of their model layer but are necessary for
+     * the view layer. Properties that should be persisted across dataset changes can be
+     * preserved via the {@link #onSaveInstanceState(Bundle)} and
+     * {@link #onRestoreInstanceState(Bundle)} methods.
+     * <p/>
+     * Note: An {@link ItemHolder} can be used by multiple {@link ItemHolder} and any state changes
+     * should simultaneously be reflected in both UIs.  It is not thread-safe however and should
+     * only be used on a single thread at a given time.
+     *
+     * @param <T> the item type wrapped by the holder
+     */
+    public static abstract class ItemHolder<T> {
+
+        /**
+         * The item held by this holder.
+         */
+        public final T item;
+
+        /**
+         * Globally unique id corresponding to the item.
+         */
+        public final long itemId;
+
+        /**
+         * Listeners to be invoked by {@link #notifyItemChanged()}.
+         */
+        private final List<OnItemChangedListener> mOnItemChangedListeners = new ArrayList<>();
+
+        /**
+         * Designated constructor.
+         *
+         * @param item   the {@link T} item to be held by this holder
+         * @param itemId the globally unique id corresponding to the item
+         */
+        public ItemHolder(T item, long itemId) {
+            this.item = item;
+            this.itemId = itemId;
+        }
+
+        /**
+         * @return the unique identifier for the view that should be used to represent the item,
+         * e.g. the layout resource id.
+         */
+        public abstract int getItemViewType();
+
+        /**
+         * Adds the listener to the current list of registered listeners if it is not already
+         * registered.
+         *
+         * @param listener the listener to add
+         */
+        public final void addOnItemChangedListener(OnItemChangedListener listener) {
+            if (!mOnItemChangedListeners.contains(listener)) {
+                mOnItemChangedListeners.add(listener);
+            }
+        }
+
+        /**
+         * Removes the listener from the current list of registered listeners.
+         *
+         * @param listener the listener to remove
+         */
+        public final void removeOnItemChangedListener(OnItemChangedListener listener) {
+            mOnItemChangedListeners.remove(listener);
+        }
+
+        /**
+         * Invokes {@link OnItemChangedListener#onItemChanged(ItemHolder)} for all listeners added
+         * via {@link #addOnItemChangedListener(OnItemChangedListener)}.
+         */
+        public final void notifyItemChanged() {
+            for (OnItemChangedListener listener : mOnItemChangedListeners) {
+                listener.onItemChanged(this);
+            }
+        }
+
+        /**
+         * Called to retrieve per-instance state when the item may disappear or change so that
+         * state can be restored in {@link #onRestoreInstanceState(Bundle)}.
+         * <p/>
+         * Note: Subclasses must not maintain a reference to the {@link Bundle} as it may be
+         * reused for other items in the {@link ItemHolder}.
+         *
+         * @param bundle the {@link Bundle} in which to place saved state
+         */
+        public void onSaveInstanceState(Bundle bundle) {
+            // for subclassers
+        }
+
+        /**
+         * Called to restore any per-instance state which was previously saved in
+         * {@link #onSaveInstanceState(Bundle)} for an item with a matching {@link #itemId}.
+         * <p/>
+         * Note: Subclasses must not maintain a reference to the {@link Bundle} as it may be
+         * reused for other items in the {@link ItemHolder}.
+         *
+         * @param bundle the {@link Bundle} in which to retrieve saved state
+         */
+        public void onRestoreInstanceState(Bundle bundle) {
+            // for subclassers
+        }
+    }
+
+    /**
+     * Base class for a reusable {@link RecyclerView.ViewHolder} compatible with an
+     * {@link ItemViewHolder}. Provides an interface for binding to an {@link ItemHolder} and later
+     * being recycled.
+     */
+    public static class ItemViewHolder<T extends ItemHolder> extends RecyclerView.ViewHolder {
+
+        /**
+         * The current {@link ItemHolder} bound to this holder.
+         */
+        private T mItemHolder;
+
+        /**
+         * The current {@link OnItemClickedListener} associated with this holder.
+         */
+        private OnItemClickedListener mOnItemClickedListener;
+
+        /**
+         * Designated constructor.
+         *
+         * @param itemView the item {@link View} to associate with this holder
+         */
+        public ItemViewHolder(View itemView) {
+            super(itemView);
+        }
+
+        /**
+         * @return the current {@link ItemHolder} bound to this holder, or {@code null} if unbound
+         */
+        public final T getItemHolder() {
+            return mItemHolder;
+        }
+
+        /**
+         * Binds the holder's {@link #itemView} to a particular item.
+         *
+         * @param itemHolder the {@link ItemHolder} to bind
+         */
+        public final void bindItemView(T itemHolder) {
+            mItemHolder = itemHolder;
+            onBindItemView(itemHolder);
+        }
+
+        /**
+         * Called when a new item is bound to the holder. Subclassers should override to bind any
+         * relevant data to their {@link #itemView} in this method.
+         *
+         * @param itemHolder the {@link ItemHolder} to bind
+         */
+        protected void onBindItemView(T itemHolder) {
+            // for subclassers
+        }
+
+        /**
+         * Recycles the current item view, unbinding the current item holder and state.
+         */
+        public final void recycleItemView() {
+            mItemHolder = null;
+            mOnItemClickedListener = null;
+
+            onRecycleItemView();
+        }
+
+        /**
+         * Called when the current item view is recycled. Subclassers should override to release
+         * any bound item state and prepare their {@link #itemView} for reuse.
+         */
+        protected void onRecycleItemView() {
+            // for subclassers
+        }
+
+        /**
+         * Sets the current {@link OnItemClickedListener} to be invoked via
+         * {@link #notifyItemClicked}.
+         *
+         * @param listener the new {@link OnItemClickedListener}, or {@code null} to clear
+         */
+        public final void setOnItemClickedListener(OnItemClickedListener listener) {
+            mOnItemClickedListener = listener;
+        }
+
+        /**
+         * Called by subclasses to invoke the current {@link OnItemClickedListener} for a
+         * particular click event so it can be handled at a higher level.
+         *
+         * @param id the unique identifier for the click action that has occurred
+         */
+        public final void notifyItemClicked(int id) {
+            if (mOnItemClickedListener != null) {
+                mOnItemClickedListener.onItemClicked(this, id);
+            }
+        }
+
+        /**
+         * Factory interface used by {@link ItemAdapter} for creating new {@link ItemViewHolder}.
+         */
+        public interface Factory {
+            /**
+             * Used by {@link ItemAdapter#createViewHolder(ViewGroup, int)} to make new
+             * {@link ItemViewHolder} for a given view type.
+             *
+             * @param parent   the {@code ViewGroup} that the {@link ItemViewHolder#itemView} will
+             *                 be attached
+             * @param viewType the unique id of the item view to create
+             * @return a new initialized {@link ItemViewHolder}
+             */
+            public ItemViewHolder<?> createViewHolder(ViewGroup parent, int viewType);
+        }
+    }
+
+    /**
+     * Callback interface for when an item changes and should be re-bound.
+     */
+    public interface OnItemChangedListener {
+        /**
+         * Invoked by {@link ItemHolder#notifyItemChanged()}.
+         *
+         * @param itemHolder the item holder that has changed
+         */
+        public void onItemChanged(ItemHolder<?> itemHolder);
+    }
+
+    /**
+     * Callback interface for handling when an item is clicked.
+     */
+    public interface OnItemClickedListener {
+        /**
+         * Invoked by {@link ItemViewHolder#notifyItemClicked(int)}
+         *
+         * @param viewHolder the {@link ItemViewHolder} containing the view that was clicked
+         * @param id         the unique identifier for the click action that has occurred
+         */
+        public void onItemClicked(ItemViewHolder<?> viewHolder, int id);
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/deskclock/NumberPickerCompat.java b/src/com/android/deskclock/NumberPickerCompat.java
index 89baed6..2fb5bed 100644
--- a/src/com/android/deskclock/NumberPickerCompat.java
+++ b/src/com/android/deskclock/NumberPickerCompat.java
@@ -134,6 +134,12 @@
         postDelayed(mAnnounceValueRunnable, 200L);
     }
 
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        removeCallbacks(mAnnounceValueRunnable);
+    }
+
     /**
      * Register a callback to be invoked whenever a value change should be announced.
      */
diff --git a/src/com/android/deskclock/RingtonePickerDialogFragment.java b/src/com/android/deskclock/RingtonePickerDialogFragment.java
new file mode 100644
index 0000000..69b4efe
--- /dev/null
+++ b/src/com/android/deskclock/RingtonePickerDialogFragment.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+package com.android.deskclock;
+
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.database.Cursor;
+import android.media.RingtoneManager;
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.v7.app.AlertDialog;
+
+import com.android.deskclock.data.DataModel;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This ringtone picker offers some flexibility over the system ringtone picker. It can be themed,
+ * and it allows control of the ringtones that are displayed and their labels.
+ */
+public class RingtonePickerDialogFragment extends DialogFragment {
+
+    private static final String KEY_TITLE = "title";
+    private static final String KEY_OLD_RINGTONE_URI = "old_ringtone_uri";
+    private static final String KEY_DEFAULT_RINGTONE_LABEL = "default_ringtone_label";
+    private static final String KEY_DEFAULT_RINGTONE_URI = "default_ringtone_uri";
+    private static final String KEY_FRAGMENT_TAG = "fragment_tag";
+    private static final String KEY_SELECTED_INDEX = "selected_index";
+
+    private int mSelectedIndex;
+
+    public static DialogFragment newInstance(String title, String
+            defaultRingtoneLabel, Uri defaultRingtoneUri, Uri oldRingtoneUri, String fragmentTag) {
+
+        final Bundle args = new Bundle();
+        args.putString(KEY_TITLE, title);
+        args.putString(KEY_DEFAULT_RINGTONE_LABEL, defaultRingtoneLabel);
+        args.putParcelable(KEY_DEFAULT_RINGTONE_URI, defaultRingtoneUri);
+        args.putParcelable(KEY_OLD_RINGTONE_URI, oldRingtoneUri);
+        args.putString(KEY_FRAGMENT_TAG, fragmentTag);
+        args.putInt(KEY_SELECTED_INDEX, -1);
+
+        final RingtonePickerDialogFragment fragment = new RingtonePickerDialogFragment();
+        fragment.setArguments(args);
+        return fragment;
+    }
+
+    @Override
+    public Dialog onCreateDialog(Bundle savedInstanceState) {
+        final Bundle bundle = getArguments();
+        final String title = bundle.getString(KEY_TITLE);
+        final String defaultRingtoneLabel = bundle.getString(KEY_DEFAULT_RINGTONE_LABEL);
+        final Uri defaultRingtoneUri = bundle.getParcelable(KEY_DEFAULT_RINGTONE_URI);
+        final Uri oldRingtoneUri = bundle.getParcelable(KEY_OLD_RINGTONE_URI);
+        final String fragmentTag = bundle.getString(KEY_FRAGMENT_TAG);
+        mSelectedIndex = bundle.getInt(KEY_SELECTED_INDEX);
+        final List<RingtoneItem> ringtones = new ArrayList<>(20);
+
+        // Add option for "silent" ringtone.
+        final String silentTitle = getString(R.string.silent_ringtone_title);
+        final Uri silentUri = DataModel.getDataModel().getSilentRingtoneUri();
+        ringtones.add(new RingtoneItem(silentTitle, silentUri));
+
+        // Add option for default ringtone.
+        if (defaultRingtoneLabel != null) {
+            ringtones.add(new RingtoneItem(defaultRingtoneLabel, defaultRingtoneUri));
+        }
+
+        // Add system ringtones.
+        final Context context = getActivity();
+        final RingtoneManager rm = new RingtoneManager(context);
+        rm.setType(RingtoneManager.TYPE_ALARM);
+        final Cursor cursor = rm.getCursor();
+        for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
+            final String ringtoneTitle = cursor.getString(RingtoneManager.TITLE_COLUMN_INDEX);
+            final Uri ringtoneUri = rm.getRingtoneUri(cursor.getPosition());
+            ringtones.add(new RingtoneItem(ringtoneTitle, ringtoneUri));
+        }
+
+        // Extract the ringtone titles for the dialog to display.
+        final CharSequence[] titles = new CharSequence[ringtones.size()];
+        for (int i = 0; i < ringtones.size(); i++) {
+            final RingtoneItem ringtone = ringtones.get(i);
+            titles[i] = ringtone.title;
+            if (mSelectedIndex < 0 && ringtone.uri.equals(oldRingtoneUri)) {
+                mSelectedIndex = i;
+            }
+        }
+
+        return new AlertDialog.Builder(context)
+                .setTitle(title)
+                .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+                    @Override
+                    public void onClick(DialogInterface dialog, int which) {
+                        final Uri uri = ringtones.get(mSelectedIndex).uri;
+                        RingtoneSelectionListener rsl = (RingtoneSelectionListener) getActivity();
+                        rsl.onRingtoneSelected(uri, fragmentTag);
+                    }
+                })
+                .setNegativeButton(android.R.string.cancel, null)
+                .setSingleChoiceItems(titles, mSelectedIndex,
+                        new DialogInterface.OnClickListener() {
+                    @Override
+                    public void onClick(DialogInterface dialog, int which) {
+                        selectRingtone(which, ringtones.get(which).uri);
+                    }
+                })
+                .create();
+    }
+
+    @Override
+    public void onStop() {
+        super.onStop();
+        if (!getActivity().isChangingConfigurations()) {
+            RingtonePreviewKlaxon.stop(getActivity());
+        }
+    }
+
+    @Override
+    public void onSaveInstanceState(@NonNull Bundle outState) {
+        super.onSaveInstanceState(outState);
+        outState.putInt(KEY_SELECTED_INDEX, mSelectedIndex);
+    }
+
+    /**
+     * Callback for ringtone click.
+     */
+    private void selectRingtone(int index, Uri ringtoneUri) {
+        mSelectedIndex = index;
+        final Context context = getActivity();
+        RingtonePreviewKlaxon.stop(context);
+
+        if (!DataModel.getDataModel().getSilentRingtoneUri().equals(ringtoneUri)) {
+            RingtonePreviewKlaxon.start(context, ringtoneUri);
+        }
+    }
+
+    private static class RingtoneItem {
+        public final String title;
+        public final Uri uri;
+
+        public RingtoneItem(String title, Uri uri) {
+            this.title = title;
+            this.uri = uri;
+        }
+    }
+
+    public interface RingtoneSelectionListener {
+        /**
+         * Called when the ringtone picker dialog is confirmed and dismissed.
+         *
+         * @param ringtoneUri the uri of the ringtone that was picked
+         * @param fragmentTag the tag of the fragment that launched the dialog
+         */
+        void onRingtoneSelected(Uri ringtoneUri, String fragmentTag);
+    }
+}
diff --git a/src/com/android/deskclock/RingtonePreviewKlaxon.java b/src/com/android/deskclock/RingtonePreviewKlaxon.java
new file mode 100644
index 0000000..d11ed7e
--- /dev/null
+++ b/src/com/android/deskclock/RingtonePreviewKlaxon.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+package com.android.deskclock;
+
+import android.content.Context;
+import android.net.Uri;
+
+public final class RingtonePreviewKlaxon {
+
+    private static AsyncRingtonePlayer sAsyncRingtonePlayer;
+
+    private RingtonePreviewKlaxon() {
+    }
+
+    public static void stop(Context context) {
+        LogUtils.i("RingtonePreviewKlaxon.stop()");
+        getAsyncRingtonePlayer(context).stop();
+    }
+
+    public static void start(Context context, Uri uri) {
+        stop(context);
+        LogUtils.i("RingtonePreviewKlaxon.start()");
+        getAsyncRingtonePlayer(context).play(uri);
+    }
+
+    private static synchronized AsyncRingtonePlayer getAsyncRingtonePlayer(Context context) {
+        if (sAsyncRingtonePlayer == null) {
+            sAsyncRingtonePlayer = new AsyncRingtonePlayer(context.getApplicationContext(), null);
+        }
+
+        return sAsyncRingtonePlayer;
+    }
+}
diff --git a/src/com/android/deskclock/Screensaver.java b/src/com/android/deskclock/Screensaver.java
index 7ab6d92..a2197ce 100644
--- a/src/com/android/deskclock/Screensaver.java
+++ b/src/com/android/deskclock/Screensaver.java
@@ -23,6 +23,7 @@
 import android.content.IntentFilter;
 import android.content.res.Configuration;
 import android.database.ContentObserver;
+import android.net.Uri;
 import android.os.Handler;
 import android.preference.PreferenceManager;
 import android.provider.Settings;
@@ -142,10 +143,9 @@
         Utils.setMidnightUpdater(mHandler, mMidnightUpdater);
 
         if (Utils.isPreL()) {
-            getContentResolver().registerContentObserver(
-                Settings.System.getUriFor(Settings.System.NEXT_ALARM_FORMATTED),
-                false,
-                mSettingsContentObserver);
+            @SuppressWarnings("deprecation")
+            final Uri uri = Settings.System.getUriFor(Settings.System.NEXT_ALARM_FORMATTED);
+            getContentResolver().registerContentObserver(uri, false, mSettingsContentObserver);
         }
 
         mHandler.post(mMoveSaverRunnable);
diff --git a/src/com/android/deskclock/ScreensaverActivity.java b/src/com/android/deskclock/ScreensaverActivity.java
index 2754114..6d68664 100644
--- a/src/com/android/deskclock/ScreensaverActivity.java
+++ b/src/com/android/deskclock/ScreensaverActivity.java
@@ -23,6 +23,7 @@
 import android.content.IntentFilter;
 import android.content.res.Configuration;
 import android.database.ContentObserver;
+import android.net.Uri;
 import android.os.BatteryManager;
 import android.os.Handler;
 import android.provider.Settings;
@@ -116,10 +117,9 @@
         filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
         registerReceiver(mIntentReceiver, filter);
         if (Utils.isPreL()) {
-            getContentResolver().registerContentObserver(
-                Settings.System.getUriFor(Settings.System.NEXT_ALARM_FORMATTED),
-                false,
-                mSettingsContentObserver);
+            @SuppressWarnings("deprecation")
+            final Uri uri = Settings.System.getUriFor(Settings.System.NEXT_ALARM_FORMATTED);
+            getContentResolver().registerContentObserver(uri, false, mSettingsContentObserver);
         }
     }
 
@@ -212,4 +212,4 @@
         Utils.updateDate(mDateFormat, mDateFormatForAccessibility,mContentView);
         Utils.refreshAlarm(ScreensaverActivity.this, mContentView);
     }
-}
\ No newline at end of file
+}
diff --git a/src/com/android/deskclock/Utils.java b/src/com/android/deskclock/Utils.java
index 49f3b81..16879c0 100644
--- a/src/com/android/deskclock/Utils.java
+++ b/src/com/android/deskclock/Utils.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012 The Android Open Source Project
+ * Copyright (C) 2015 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.
@@ -19,7 +19,7 @@
 import android.animation.Animator;
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
-import android.animation.TimeInterpolator;
+import android.annotation.SuppressLint;
 import android.annotation.TargetApi;
 import android.app.AlarmManager;
 import android.content.ContentResolver;
@@ -36,12 +36,12 @@
 import android.os.Looper;
 import android.preference.PreferenceManager;
 import android.provider.Settings;
+import android.support.annotation.VisibleForTesting;
 import android.text.Spannable;
 import android.text.SpannableString;
 import android.text.TextUtils;
 import android.text.format.DateFormat;
 import android.text.format.DateUtils;
-import android.text.format.Time;
 import android.text.style.RelativeSizeSpan;
 import android.text.style.StyleSpan;
 import android.text.style.TypefaceSpan;
@@ -78,6 +78,8 @@
 
     public static final int DEFAULT_WEEK_START = Calendar.getInstance().getFirstDayOfWeek();
 
+    private static final long QUARTER_HOUR_IN_MILLIS = 15 * DateUtils.MINUTE_IN_MILLIS;
+
     private static Locale sLocaleUsedForWeekdays;
 
     /**
@@ -136,7 +138,7 @@
 
     /**
      * @return {@code true} if the device is {@link Build.VERSION_CODES#LOLLIPOP} or
-     *      {@link Build.VERSION_CODES#LOLLIPOP_MR1}
+     * {@link Build.VERSION_CODES#LOLLIPOP_MR1}
      */
     public static boolean isLOrLMR1() {
         final int sdkInt = Build.VERSION.SDK_INT;
@@ -188,30 +190,20 @@
         }
     }
 
-    /** Runnable for use with screensaver and dream, to move the clock every minute.
-     *  registerViews() must be called prior to posting.
+    /**
+     * Runnable for use with screensaver and dream, to move the clock every minute.
+     * registerViews() must be called prior to posting.
      */
     public static class ScreensaverMoveSaverRunnable implements Runnable {
-        static final long MOVE_DELAY = 60000; // DeskClock.SCREEN_SAVER_MOVE_DELAY;
-        static final long SLIDE_TIME = 10000;
-        static final long FADE_TIME = 3000;
 
-        static final boolean SLIDE = false;
+        static final long MOVE_DELAY = 60000; // DeskClock.SCREEN_SAVER_MOVE_DELAY;
+        static final long FADE_TIME = 3000;
 
         private View mContentView, mSaverView;
         private final Handler mHandler;
 
-        private static TimeInterpolator mSlowStartWithBrakes;
-
-
         public ScreensaverMoveSaverRunnable(Handler handler) {
             mHandler = handler;
-            mSlowStartWithBrakes = new TimeInterpolator() {
-                @Override
-                public float getInterpolation(float x) {
-                    return (float)(Math.cos((Math.pow(x,3) + 1) * Math.PI) / 2.0f) + 0.5f;
-                }
-            };
         }
 
         public void registerViews(View contentView, View saverView) {
@@ -242,58 +234,48 @@
                     mSaverView.setX(nextx);
                     mSaverView.setY(nexty);
                     ObjectAnimator.ofFloat(mSaverView, "alpha", 0f, 1f)
-                        .setDuration(FADE_TIME)
-                        .start();
+                            .setDuration(FADE_TIME)
+                            .start();
                 } else {
                     AnimatorSet s = new AnimatorSet();
-                    Animator xMove   = ObjectAnimator.ofFloat(mSaverView,
-                                         "x", mSaverView.getX(), nextx);
-                    Animator yMove   = ObjectAnimator.ofFloat(mSaverView,
-                                         "y", mSaverView.getY(), nexty);
+                    Animator xMove = ObjectAnimator.ofFloat(mSaverView,
+                            "x", mSaverView.getX(), nextx);
+                    Animator yMove = ObjectAnimator.ofFloat(mSaverView,
+                            "y", mSaverView.getY(), nexty);
 
                     Animator xShrink = ObjectAnimator.ofFloat(mSaverView, "scaleX", 1f, 0.85f);
-                    Animator xGrow   = ObjectAnimator.ofFloat(mSaverView, "scaleX", 0.85f, 1f);
+                    Animator xGrow = ObjectAnimator.ofFloat(mSaverView, "scaleX", 0.85f, 1f);
 
                     Animator yShrink = ObjectAnimator.ofFloat(mSaverView, "scaleY", 1f, 0.85f);
-                    Animator yGrow   = ObjectAnimator.ofFloat(mSaverView, "scaleY", 0.85f, 1f);
-                    AnimatorSet shrink = new AnimatorSet(); shrink.play(xShrink).with(yShrink);
-                    AnimatorSet grow = new AnimatorSet(); grow.play(xGrow).with(yGrow);
+                    Animator yGrow = ObjectAnimator.ofFloat(mSaverView, "scaleY", 0.85f, 1f);
+                    AnimatorSet shrink = new AnimatorSet();
+                    shrink.play(xShrink).with(yShrink);
+                    AnimatorSet grow = new AnimatorSet();
+                    grow.play(xGrow).with(yGrow);
 
                     Animator fadeout = ObjectAnimator.ofFloat(mSaverView, "alpha", 1f, 0f);
                     Animator fadein = ObjectAnimator.ofFloat(mSaverView, "alpha", 0f, 1f);
 
 
-                    if (SLIDE) {
-                        s.play(xMove).with(yMove);
-                        s.setDuration(SLIDE_TIME);
+                    AccelerateInterpolator accel = new AccelerateInterpolator();
+                    DecelerateInterpolator decel = new DecelerateInterpolator();
 
-                        s.play(shrink.setDuration(SLIDE_TIME/2));
-                        s.play(grow.setDuration(SLIDE_TIME/2)).after(shrink);
-                        s.setInterpolator(mSlowStartWithBrakes);
-                    } else {
-                        AccelerateInterpolator accel = new AccelerateInterpolator();
-                        DecelerateInterpolator decel = new DecelerateInterpolator();
-
-                        shrink.setDuration(FADE_TIME).setInterpolator(accel);
-                        fadeout.setDuration(FADE_TIME).setInterpolator(accel);
-                        grow.setDuration(FADE_TIME).setInterpolator(decel);
-                        fadein.setDuration(FADE_TIME).setInterpolator(decel);
-                        s.play(shrink);
-                        s.play(fadeout);
-                        s.play(xMove.setDuration(0)).after(FADE_TIME);
-                        s.play(yMove.setDuration(0)).after(FADE_TIME);
-                        s.play(fadein).after(FADE_TIME);
-                        s.play(grow).after(FADE_TIME);
-                    }
+                    shrink.setDuration(FADE_TIME).setInterpolator(accel);
+                    fadeout.setDuration(FADE_TIME).setInterpolator(accel);
+                    grow.setDuration(FADE_TIME).setInterpolator(decel);
+                    fadein.setDuration(FADE_TIME).setInterpolator(decel);
+                    s.play(shrink);
+                    s.play(fadeout);
+                    s.play(xMove.setDuration(0)).after(FADE_TIME);
+                    s.play(yMove.setDuration(0)).after(FADE_TIME);
+                    s.play(fadein).after(FADE_TIME);
+                    s.play(grow).after(FADE_TIME);
                     s.start();
                 }
 
                 long now = System.currentTimeMillis();
                 long adjust = (now % 60000);
-                delay = delay
-                        + (MOVE_DELAY - adjust) // minute aligned
-                        - (SLIDE ? 0 : FADE_TIME) // start moving before the fade
-                        ;
+                delay = delay + (MOVE_DELAY - adjust) - (FADE_TIME); // start moving before the fade
             }
 
             mHandler.removeCallbacks(this);
@@ -301,44 +283,38 @@
         }
     }
 
-    /** Setup to find out when the quarter-hour changes (e.g. Kathmandu is GMT+5:45) **/
+    /**
+     * Setup to find out when the quarter-hour changes (e.g. Kathmandu is GMT+5:45)
+     **/
     public static long getAlarmOnQuarterHour() {
-        final Calendar calendarInstance = Calendar.getInstance();
-        final long now = System.currentTimeMillis();
-        return getAlarmOnQuarterHour(calendarInstance, now);
+        return getAlarmOnQuarterHour(System.currentTimeMillis());
     }
 
-    static long getAlarmOnQuarterHour(Calendar calendar, long now) {
-        //  Set 1 second to ensure quarter-hour threshold passed.
-        calendar.set(Calendar.SECOND, 1);
-        calendar.set(Calendar.MILLISECOND, 0);
-        int minute = calendar.get(Calendar.MINUTE);
-        calendar.add(Calendar.MINUTE, 15 - (minute % 15));
-        long alarmOnQuarterHour = calendar.getTimeInMillis();
-
-        // Verify that alarmOnQuarterHour is within the next 15 minutes
-        long delta = alarmOnQuarterHour - now;
-        if (0 >= delta || delta > 901000) {
-            // Something went wrong in the calculation, schedule something that is
-            // about 15 minutes. Next time , it will align with the 15 minutes border.
-            alarmOnQuarterHour = now + 901000;
-        }
-        return alarmOnQuarterHour;
+    @VisibleForTesting
+    static long getAlarmOnQuarterHour(long now) {
+        // Compute the time of the last quarter hour.
+        final long lastQuarterHour = now - (now % QUARTER_HOUR_IN_MILLIS);
+        // Add a quarter hour to move to the future.
+        final long nextQuarterHour = lastQuarterHour + QUARTER_HOUR_IN_MILLIS;
+        // Add an extra second to ensure callbacks are processed *after* the quarter hour passes.
+        return nextQuarterHour + DateUtils.SECOND_IN_MILLIS;
     }
 
     // Setup a thread that starts at midnight plus one second. The extra second is added to ensure
     // the date has changed.
     public static void setMidnightUpdater(Handler handler, Runnable runnable) {
-        String timezone = TimeZone.getDefault().getID();
-        if (handler == null || runnable == null || timezone == null) {
+        if (handler == null || runnable == null) {
             return;
         }
-        long now = System.currentTimeMillis();
-        Time time = new Time(timezone);
-        time.set(now);
-        long runInMillis = ((24 - time.hour) * 3600 - time.minute * 60 - time.second + 1) * 1000;
+
+        final Calendar c = Calendar.getInstance();
+        c.add(Calendar.DATE, 1);
+        c.set(Calendar.HOUR_OF_DAY, 0);
+        c.set(Calendar.MINUTE, 0);
+        c.set(Calendar.SECOND, 1);
+
         handler.removeCallbacks(runnable);
-        handler.postDelayed(runnable, runInMillis);
+        handler.postDelayed(runnable, c.getTimeInMillis() - System.currentTimeMillis());
     }
 
     // Stop the midnight update thread
@@ -420,7 +396,7 @@
         Paint paint = new Paint();
         paint.setColor(Color.WHITE);
         paint.setColorFilter(new PorterDuffColorFilter(
-                        (dim ? 0x40FFFFFF : 0xC0FFFFFF),
+                (dim ? 0x40FFFFFF : 0xC0FFFFFF),
                 PorterDuff.Mode.MULTIPLY));
         clockView.setLayerType(View.LAYER_TYPE_HARDWARE, paint);
     }
@@ -432,6 +408,7 @@
         return isPreL() ? getNextAlarmPreL(context) : getNextAlarmLOrLater(context);
     }
 
+    @SuppressWarnings("deprecation")
     @TargetApi(Build.VERSION_CODES.KITKAT)
     private static String getNextAlarmPreL(Context context) {
         final ContentResolver cr = context.getContentResolver();
@@ -458,7 +435,9 @@
         return nextAlarmTimeMillis - System.currentTimeMillis() <= DateUtils.DAY_IN_MILLIS;
     }
 
-    /** Clock views can call this to refresh their alarm to the next upcoming value. */
+    /**
+     * Clock views can call this to refresh their alarm to the next upcoming value.
+     */
     public static void refreshAlarm(Context context, View clock) {
         final TextView nextAlarmView = (TextView) clock.findViewById(R.id.nextAlarm);
         if (nextAlarmView == null) {
@@ -476,7 +455,9 @@
         }
     }
 
-    /** Clock views can call this to refresh their date. **/
+    /**
+     * Clock views can call this to refresh their date.
+     **/
     public static void updateDate(String dateSkeleton, String descriptionSkeleton, View clock) {
         final TextView dateDisplay = (TextView) clock.findViewById(R.id.date);
         if (dateDisplay == null) {
@@ -496,13 +477,14 @@
     /***
      * Formats the time in the TextClock according to the Locale with a special
      * formatting treatment for the am/pm label.
+     *
      * @param context - Context used to get user's locale and time preferences
-     * @param clock - TextClock to format
+     * @param clock   - TextClock to format
      */
     public static void setTimeFormat(Context context, TextClock clock) {
         if (clock != null) {
             // Get the best format for 12 hours mode according to the locale
-            clock.setFormat12Hour(get12ModeFormat(context, true /* showAmPm */));
+            clock.setFormat12Hour(get12ModeFormat(context, 0.4f /* amPmRatio */));
             // Get the best format for 24 hours mode according to the locale
             clock.setFormat24Hour(get24ModeFormat());
         }
@@ -512,7 +494,7 @@
      * Returns {@code true} if the am / pm strings for the current locale are long and a reduced
      * text size should be used for displaying the digital clock.
      */
-    public static boolean isAmPmStringLong() {
+    private static boolean isAmPmStringLong() {
         final String[] amPmStrings = new DateFormatSymbols().getAmPmStrings();
         for (String amPmString : amPmStrings) {
             // Dots are small, so don't count them.
@@ -525,13 +507,14 @@
     }
 
     /**
-     * @param context - context used to get time format string resource
-     * @param showAmPm - include the am/pm string if true
+     * @param context   used to get time format string resource
+     * @param amPmRatio a value between 0 and 1 that is the ratio of the relative size of the
+     *                  am/pm string to the time string
      * @return format string for 12 hours mode time
      */
-    public static CharSequence get12ModeFormat(Context context, boolean showAmPm) {
+    public static CharSequence get12ModeFormat(Context context, float amPmRatio) {
         String pattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), "hma");
-        if (!showAmPm) {
+        if (amPmRatio <= 0) {
             pattern = pattern.replaceAll("a", "").trim();
         }
 
@@ -544,9 +527,8 @@
         }
 
         final Resources resources = context.getResources();
-        final float amPmProportion = resources.getFraction(R.fraction.ampm_font_size_scale, 1, 1);
         final Spannable sp = new SpannableString(pattern);
-        sp.setSpan(new RelativeSizeSpan(amPmProportion), amPmPos, amPmPos + 1,
+        sp.setSpan(new RelativeSizeSpan(amPmRatio), amPmPos, amPmPos + 1,
                 Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
         sp.setSpan(new StyleSpan(Typeface.NORMAL), amPmPos, amPmPos + 1,
                 Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
@@ -569,6 +551,7 @@
 
     /**
      * Returns string denoting the timezone hour offset (e.g. GMT -8:00)
+     *
      * @param useShortForm Whether to return a short form of the header that rounds to the
      *                     nearest hour and excludes the "GMT" prefix
      */
@@ -653,19 +636,16 @@
             // nothing to do
             return;
         }
-        if (sShortWeekdays == null) {
-            sShortWeekdays = new String[DaysOfWeek.DAYS_IN_A_WEEK];
-        }
-        if (sLongWeekdays == null) {
-            sLongWeekdays = new String[DaysOfWeek.DAYS_IN_A_WEEK];
-        }
 
-        final SimpleDateFormat shortFormat = new SimpleDateFormat(DATE_FORMAT_SHORT);
-        final SimpleDateFormat longFormat = new SimpleDateFormat(DATE_FORMAT_LONG);
+        final Locale locale = Locale.getDefault();
+        final SimpleDateFormat shortFormat = new SimpleDateFormat(DATE_FORMAT_SHORT, locale);
+        final SimpleDateFormat longFormat = new SimpleDateFormat(DATE_FORMAT_LONG, locale);
+
+        sShortWeekdays = new String[DaysOfWeek.DAYS_IN_A_WEEK];
+        sLongWeekdays = new String[DaysOfWeek.DAYS_IN_A_WEEK];
 
         // Create a date (2014/07/20) that is a Sunday
         final long aSunday = new GregorianCalendar(2014, Calendar.JULY, 20).getTimeInMillis();
-
         for (int i = 0; i < DaysOfWeek.DAYS_IN_A_WEEK; i++) {
             final long dayMillis = aSunday + i * DateUtils.DAY_IN_MILLIS;
             sShortWeekdays[i] = shortFormat.format(new Date(dayMillis));
@@ -676,16 +656,15 @@
         sLocaleUsedForWeekdays = Locale.getDefault();
     }
 
-    /**
-     * @param id Resource id of the plural
-     * @param quantity integer value
-     * @return string with properly localized numbers
-     */
     public static String getNumberFormattedQuantityString(Context context, int id, int quantity) {
         final String localizedQuantity = NumberFormat.getInstance().format(quantity);
         return context.getResources().getQuantityString(id, quantity, localizedQuantity);
     }
 
+    /**
+     * {@link ArraySet} is @hide prior to {@link Build.VERSION_CODES#M}.
+     */
+    @SuppressLint("NewApi")
     public static <E> ArraySet<E> newArraySet(Collection<E> collection) {
         final ArraySet<E> arraySet = new ArraySet<>(collection.size());
         arraySet.addAll(collection);
diff --git a/src/com/android/deskclock/VerticalViewPager.java b/src/com/android/deskclock/VerticalViewPager.java
index 5aaa41e..c7cdfe5 100644
--- a/src/com/android/deskclock/VerticalViewPager.java
+++ b/src/com/android/deskclock/VerticalViewPager.java
@@ -68,6 +68,22 @@
         init();
     }
 
+    /**
+     * @return {@code false} since a vertical view pager can never be scrolled horizontally
+     */
+    @Override
+    public boolean canScrollHorizontally(int direction) {
+        return false;
+    }
+
+    /**
+     * @return {@code true} iff a normal view pager would support horizontal scrolling at this time
+     */
+    @Override
+    public boolean canScrollVertically(int direction) {
+        return super.canScrollHorizontally(direction);
+    }
+
     private void init() {
         // Make page transit vertical
         setPageTransformer(true, mPageTransformer);
@@ -85,8 +101,9 @@
                 case MotionEvent.ACTION_DOWN: {
                     mLastMotionX = x;
                     mLastMotionY = y;
-                    if (!mParentViewPager.onTouchEvent(ev))
+                    if (!mParentViewPager.onTouchEvent(ev)) {
                         return false;
+                    }
                     return verticalDrag(ev);
                 }
                 case MotionEvent.ACTION_MOVE: {
diff --git a/src/com/android/deskclock/actionbarmenu/ActionBarMenuManager.java b/src/com/android/deskclock/actionbarmenu/ActionBarMenuManager.java
index f470e5b..d2438e5 100644
--- a/src/com/android/deskclock/actionbarmenu/ActionBarMenuManager.java
+++ b/src/com/android/deskclock/actionbarmenu/ActionBarMenuManager.java
@@ -33,7 +33,7 @@
     // A map of all menu item controllers, keyed by menu item id.
     private final ArrayMap<Integer, MenuItemController> mControllers;
 
-    public ActionBarMenuManager(Activity activity) {
+    public ActionBarMenuManager() {
         mControllers = new ArrayMap<>();
     }
 
diff --git a/src/com/android/deskclock/alarms/AlarmKlaxon.java b/src/com/android/deskclock/alarms/AlarmKlaxon.java
index 971c5e9..cf2bd88 100644
--- a/src/com/android/deskclock/alarms/AlarmKlaxon.java
+++ b/src/com/android/deskclock/alarms/AlarmKlaxon.java
@@ -58,7 +58,7 @@
         }
 
         if (instance.mVibrate) {
-            final Vibrator vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
+            final Vibrator vibrator = getVibrator(context);
             if (Utils.isLOrLater()) {
                 vibrateLOrLater(vibrator);
             } else {
@@ -77,6 +77,10 @@
                 .build());
     }
 
+    private static Vibrator getVibrator(Context context) {
+        return ((Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE));
+    }
+
     private static synchronized AsyncRingtonePlayer getAsyncRingtonePlayer(Context context) {
         if (sAsyncRingtonePlayer == null) {
             sAsyncRingtonePlayer = new AsyncRingtonePlayer(context.getApplicationContext(),
diff --git a/src/com/android/deskclock/alarms/AlarmNotifications.java b/src/com/android/deskclock/alarms/AlarmNotifications.java
index 3fff8e3..0140f08 100644
--- a/src/com/android/deskclock/alarms/AlarmNotifications.java
+++ b/src/com/android/deskclock/alarms/AlarmNotifications.java
@@ -260,11 +260,9 @@
     }
 
     public static Intent createViewAlarmIntent(Context context, AlarmInstance instance) {
-        long alarmId = instance.mAlarmId == null ? Alarm.INVALID_ID : instance.mAlarmId;
-        Intent viewAlarmIntent = Alarm.createIntent(context, DeskClock.class, alarmId);
-        viewAlarmIntent.putExtra(DeskClock.SELECT_TAB_INTENT_EXTRA, DeskClock.ALARM_TAB_INDEX);
-        viewAlarmIntent.putExtra(AlarmClockFragment.SCROLL_TO_ALARM_INTENT_EXTRA, alarmId);
-        viewAlarmIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        return viewAlarmIntent;
+        final long alarmId = instance.mAlarmId == null ? Alarm.INVALID_ID : instance.mAlarmId;
+        return Alarm.createIntent(context, DeskClock.class, alarmId)
+                .putExtra(AlarmClockFragment.SCROLL_TO_ALARM_INTENT_EXTRA, alarmId)
+                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
     }
 }
diff --git a/src/com/android/deskclock/alarms/AlarmStateManager.java b/src/com/android/deskclock/alarms/AlarmStateManager.java
index e31cea1..741c3d1 100644
--- a/src/com/android/deskclock/alarms/AlarmStateManager.java
+++ b/src/com/android/deskclock/alarms/AlarmStateManager.java
@@ -45,10 +45,13 @@
 import com.android.deskclock.provider.Alarm;
 import com.android.deskclock.provider.AlarmInstance;
 import com.android.deskclock.settings.SettingsActivity;
+import com.android.deskclock.uidata.UiDataModel;
 
 import java.util.Calendar;
 import java.util.List;
 
+import static com.android.deskclock.uidata.UiDataModel.Tab.ALARMS;
+
 /**
  * This class handles all the state changes for alarm instances. You need to
  * register all alarm instances with the state manager if you want them to
@@ -206,6 +209,8 @@
     /**
      * Used in pre-L devices, where "next alarm" is stored in system settings.
      */
+    @SuppressWarnings("deprecation")
+    @TargetApi(Build.VERSION_CODES.KITKAT)
     private static void updateNextAlarmInSystemSettings(Context context, AlarmInstance nextAlarm) {
         // Send broadcast message so pre-L AppWidgets will recognize an update
         String timeString = "";
@@ -283,8 +288,12 @@
                 Alarm.updateAlarm(cr, alarm);
             }
         } else {
-            // Schedule the next repeating instance after the current time
-            AlarmInstance nextRepeatedInstance = alarm.createInstanceAfter(getCurrentTime());
+            // Schedule the next repeating instance after the current time or the last instance's
+            // time (whichever is later).
+            final Calendar currentTime = getCurrentTime();
+            final Calendar lastInstanceTime = instance.getAlarmTime();
+            AlarmInstance nextRepeatedInstance = alarm.createInstanceAfter(
+                currentTime.compareTo(lastInstanceTime) > 0 ? currentTime : lastInstanceTime);
             LogUtils.i("Creating new instance for repeating alarm " + alarm.id + " at " +
                     AlarmUtils.getFormattedTime(context, nextRepeatedInstance.getAlarmTime()));
             AlarmInstance.addInstance(cr, nextRepeatedInstance);
@@ -574,13 +583,9 @@
         scheduleInstanceStateChange(context, instance.getAlarmTime(), instance,
                 AlarmInstance.DISMISSED_STATE);
 
-        final Alarm alarm = Alarm.getAlarm(contentResolver, instance.mAlarmId);
-        // if it's a one time alarm set the toggle to off
-        if (alarm != null && !alarm.daysOfWeek.isRepeating()) {
-            // Check parent if it needs to reschedule, disable or delete itself
-            if (instance.mAlarmId != null) {
-                updateParentAlarm(context, instance);
-            }
+        // Check parent if it needs to reschedule, disable or delete itself
+        if (instance.mAlarmId != null) {
+            updateParentAlarm(context, instance);
         }
 
         updateNextAlarm(context);
@@ -945,12 +950,17 @@
                 return;
             }
 
+            // Change to the alarms tab.
+            UiDataModel.getUiDataModel().setSelectedTab(ALARMS);
+
             long alarmId = instance.mAlarmId == null ? Alarm.INVALID_ID : instance.mAlarmId;
-            Intent viewAlarmIntent = Alarm.createIntent(context, DeskClock.class, alarmId);
-            viewAlarmIntent.putExtra(DeskClock.SELECT_TAB_INTENT_EXTRA, DeskClock.ALARM_TAB_INDEX);
-            viewAlarmIntent.putExtra(AlarmClockFragment.SCROLL_TO_ALARM_INTENT_EXTRA, alarmId);
-            viewAlarmIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            final Intent viewAlarmIntent = Alarm.createIntent(context, DeskClock.class, alarmId)
+                    .putExtra(AlarmClockFragment.SCROLL_TO_ALARM_INTENT_EXTRA, alarmId)
+                    .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+            // Open DeskClock which is now positioned on the alarms tab.
             context.startActivity(viewAlarmIntent);
+
             deleteInstanceAndUpdateParent(context, instance);
         }
     }
@@ -1002,7 +1012,12 @@
                     stateChangeIntent, PendingIntent.FLAG_UPDATE_CURRENT);
 
             final AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
-            am.setExact(AlarmManager.RTC_WAKEUP, timeInMillis, pendingIntent);
+            if (Utils.isMOrLater()) {
+                // Ensure the alarm fires even if the device is dozing.
+                am.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, timeInMillis, pendingIntent);
+            } else {
+                am.setExact(AlarmManager.RTC_WAKEUP, timeInMillis, pendingIntent);
+            }
         }
 
         @Override
diff --git a/src/com/android/deskclock/alarms/AlarmTimeClickHandler.java b/src/com/android/deskclock/alarms/AlarmTimeClickHandler.java
index 5249003..fac9336 100644
--- a/src/com/android/deskclock/alarms/AlarmTimeClickHandler.java
+++ b/src/com/android/deskclock/alarms/AlarmTimeClickHandler.java
@@ -16,7 +16,9 @@
 
 package com.android.deskclock.alarms;
 
+import android.app.DialogFragment;
 import android.app.Fragment;
+import android.app.FragmentManager;
 import android.app.FragmentTransaction;
 import android.content.Context;
 import android.content.Intent;
@@ -29,6 +31,7 @@
 import com.android.deskclock.LabelDialogFragment;
 import com.android.deskclock.LogUtils;
 import com.android.deskclock.R;
+import com.android.deskclock.RingtonePickerDialogFragment;
 import com.android.deskclock.alarms.utils.DayOrderUtils;
 import com.android.deskclock.provider.Alarm;
 import com.android.deskclock.provider.AlarmInstance;
@@ -42,6 +45,7 @@
 
     private static final String TAG = "AlarmTimeClickHandler";
     private static final String KEY_PREVIOUS_DAY_MAP = "previousDayMap";
+    private static final String RINGTONE_PICKER_FRAG_TAG = "ringtone_picker_dialog";
 
     private final Fragment mFragment;
     private final AlarmUpdateHandler mAlarmUpdateHandler;
@@ -163,18 +167,28 @@
 
     public void onRingtoneClicked(Alarm alarm) {
         mSelectedAlarm = alarm;
-        final Uri oldRingtone = Alarm.NO_RINGTONE_URI.equals(alarm.alert) ? null : alarm.alert;
-        final Intent intent = new Intent(RingtoneManager.ACTION_RINGTONE_PICKER);
-        intent.putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, oldRingtone);
-        intent.putExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, RingtoneManager.TYPE_ALARM);
-        intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, false);
-        LogUtils.d(TAG, "Showing ringtone picker.");
-        mFragment.startActivityForResult(intent, R.id.request_code_ringtone);
+        final FragmentManager fragmentManager = mFragment.getFragmentManager();
+        fragmentManager.executePendingTransactions();
+        final FragmentTransaction ft = fragmentManager.beginTransaction();
+        final Fragment prev = fragmentManager.findFragmentByTag(RINGTONE_PICKER_FRAG_TAG);
+        if (prev != null) {
+            ft.remove(prev);
+        }
+        ft.addToBackStack(null);
+
+        final String dialogTitle = mFragment.getString(R.string.alert);
+        final String defaultTitle = mFragment.getString(R.string.default_alarm_ringtone_title);
+        final Uri defaultUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM);
+        final DialogFragment newFragment = RingtonePickerDialogFragment.newInstance(
+                dialogTitle, defaultTitle, defaultUri, alarm.alert, mFragment.getTag());
+        newFragment.show(ft, RINGTONE_PICKER_FRAG_TAG);
     }
 
     public void onEditLabelClicked(Alarm alarm) {
-        final FragmentTransaction ft = mFragment.getFragmentManager().beginTransaction();
-        final Fragment prev = mFragment.getFragmentManager().findFragmentByTag("label_dialog");
+        final FragmentManager fragmentManager = mFragment.getFragmentManager();
+        fragmentManager.executePendingTransactions();
+        final FragmentTransaction ft = fragmentManager.beginTransaction();
+        final Fragment prev = fragmentManager.findFragmentByTag("label_dialog");
         if (prev != null) {
             ft.remove(prev);
         }
diff --git a/src/com/android/deskclock/alarms/dataadapter/AlarmItemHolder.java b/src/com/android/deskclock/alarms/dataadapter/AlarmItemHolder.java
new file mode 100644
index 0000000..f4e0129
--- /dev/null
+++ b/src/com/android/deskclock/alarms/dataadapter/AlarmItemHolder.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+package com.android.deskclock.alarms.dataadapter;
+
+import android.os.Bundle;
+
+import com.android.deskclock.ItemAdapter;
+import com.android.deskclock.alarms.AlarmTimeClickHandler;
+import com.android.deskclock.provider.Alarm;
+import com.android.deskclock.provider.AlarmInstance;
+
+public class AlarmItemHolder extends ItemAdapter.ItemHolder<Alarm> {
+
+    private static final java.lang.String EXPANDED_KEY = "expanded";
+    private final AlarmInstance mAlarmInstance;
+    private final AlarmTimeClickHandler mAlarmTimeClickHandler;
+    private boolean mExpanded;
+
+    public AlarmItemHolder(Alarm alarm, AlarmInstance alarmInstance,
+            AlarmTimeClickHandler alarmTimeClickHandler) {
+        super(alarm, alarm.id);
+        mAlarmInstance = alarmInstance;
+        mAlarmTimeClickHandler = alarmTimeClickHandler;
+    }
+
+    @Override
+    public int getItemViewType() {
+        return isExpanded() ?
+                ExpandedAlarmViewHolder.VIEW_TYPE : CollapsedAlarmViewHolder.VIEW_TYPE;
+    }
+
+    public AlarmTimeClickHandler getAlarmTimeClickHandler() {
+        return mAlarmTimeClickHandler;
+    }
+
+    public AlarmInstance getAlarmInstance() {
+        return mAlarmInstance;
+    }
+
+    public void expand() {
+        mExpanded = true;
+        notifyItemChanged();
+    }
+
+    public void collapse() {
+        mExpanded = false;
+        notifyItemChanged();
+    }
+
+    public boolean isExpanded() {
+        return mExpanded;
+    }
+
+    @Override
+    public void onSaveInstanceState(Bundle bundle) {
+        super.onSaveInstanceState(bundle);
+        bundle.putBoolean(EXPANDED_KEY, mExpanded);
+    }
+
+    @Override
+    public void onRestoreInstanceState(Bundle bundle) {
+        super.onRestoreInstanceState(bundle);
+        mExpanded = bundle.getBoolean(EXPANDED_KEY);
+    }
+}
diff --git a/src/com/android/deskclock/alarms/dataadapter/AlarmItemViewHolder.java b/src/com/android/deskclock/alarms/dataadapter/AlarmItemViewHolder.java
new file mode 100644
index 0000000..40c450a
--- /dev/null
+++ b/src/com/android/deskclock/alarms/dataadapter/AlarmItemViewHolder.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+package com.android.deskclock.alarms.dataadapter;
+
+import android.content.Context;
+import android.database.ContentObserver;
+import android.media.AudioManager;
+import android.os.Handler;
+import android.provider.Settings;
+import android.view.View;
+import android.widget.Button;
+import android.widget.CompoundButton;
+import android.widget.TextView;
+
+import com.android.deskclock.AlarmUtils;
+import com.android.deskclock.ItemAdapter;
+import com.android.deskclock.R;
+import com.android.deskclock.data.DataModel;
+import com.android.deskclock.provider.Alarm;
+import com.android.deskclock.provider.AlarmInstance;
+import com.android.deskclock.widget.TextTime;
+
+import java.util.Objects;
+
+/**
+ * Abstract ViewHolder for alarm time items.
+ */
+public abstract class AlarmItemViewHolder extends ItemAdapter.ItemViewHolder<AlarmItemHolder> {
+
+    private static final float CLOCK_ENABLED_ALPHA = 1f;
+    private static final float CLOCK_DISABLED_ALPHA = 0.69f;
+
+    public final TextTime clock;
+    public final CompoundButton onoff;
+    public final View arrow;
+    public final View preemptiveDismissContainer;
+    public final TextView preemptiveDismissButton;
+
+    private final TextView mAlarmMutedButton;
+    private final ContentObserver mVolumeObserver;
+
+    public AlarmItemViewHolder(final View itemView, Handler handler) {
+        super(itemView);
+
+        clock = (TextTime) itemView.findViewById(R.id.digital_clock);
+        onoff = (CompoundButton) itemView.findViewById(R.id.onoff);
+        arrow = itemView.findViewById(R.id.arrow);
+        preemptiveDismissContainer = itemView.findViewById(R.id.preemptive_dismiss_container);
+        preemptiveDismissButton =
+                (TextView) itemView.findViewById(R.id.preemptive_dismiss_button);
+        mAlarmMutedButton = (Button) itemView.findViewById(R.id.alarm_muted_button);
+
+        preemptiveDismissButton.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                final AlarmInstance alarmInstance = getItemHolder().getAlarmInstance();
+                getItemHolder().getAlarmTimeClickHandler().dismissAlarmInstance(alarmInstance);
+            }
+        });
+        onoff.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+            @Override
+            public void onCheckedChanged(CompoundButton compoundButton, boolean checked) {
+                getItemHolder().getAlarmTimeClickHandler().setAlarmEnabled(
+                        getItemHolder().item, checked);
+            }
+        });
+        mAlarmMutedButton.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                final Context context = itemView.getContext();
+                final Alarm alarm = getItemHolder().item;
+                if (hasSilentRingtone(alarm)) {
+                    getItemHolder().getAlarmTimeClickHandler().onRingtoneClicked(alarm);
+                } else {
+                    // Alarm volume muted.
+                    final AudioManager audioManager =
+                            (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+                    audioManager.adjustStreamVolume(AudioManager.STREAM_ALARM,
+                            AudioManager.ADJUST_SAME, AudioManager.FLAG_SHOW_UI);
+                }
+            }
+        });
+        mVolumeObserver = new ContentObserver(handler) {
+            @Override
+            public void onChange(boolean selfChange) {
+                if (getItemHolder() != null) {
+                    getItemHolder().notifyItemChanged();
+                }
+            }
+        };
+    }
+
+    @Override
+    protected void onBindItemView(final AlarmItemHolder itemHolder) {
+        final Context context = itemView.getContext();
+        final Alarm alarm = itemHolder.item;
+        bindOnOffSwitch(alarm);
+        bindAlarmMutedButton(context, alarm);
+        bindClock(context, alarm);
+        context.getContentResolver().registerContentObserver(
+                Settings.System.CONTENT_URI, true, mVolumeObserver);
+    }
+
+    @Override
+    protected void onRecycleItemView() {
+        itemView.getContext().getContentResolver().unregisterContentObserver(mVolumeObserver);
+    }
+
+    protected void bindOnOffSwitch(Alarm alarm) {
+        onoff.setChecked(alarm.enabled);
+    }
+
+    protected void bindClock(Context context, Alarm alarm) {
+        clock.setAlpha(alarm.enabled ? CLOCK_ENABLED_ALPHA : CLOCK_DISABLED_ALPHA);
+        clock.setFormat(context);
+        clock.setTime(alarm.hour, alarm.minutes);
+    }
+
+    protected boolean bindPreemptiveDismissButton(Context context, Alarm alarm,
+            AlarmInstance alarmInstance) {
+
+        final AudioManager audioManager =
+                (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+        final boolean muted = audioManager.getStreamVolume(AudioManager.STREAM_ALARM) == 0
+                || hasSilentRingtone(alarm);
+        final boolean canBind = alarm.canPreemptivelyDismiss() && alarmInstance != null && !muted;
+
+        if (canBind) {
+            preemptiveDismissContainer.setVisibility(View.VISIBLE);
+            final String dismissText = alarm.instanceState == AlarmInstance.SNOOZE_STATE
+                    ? context.getString(R.string.alarm_alert_snooze_until,
+                            AlarmUtils.getAlarmText(context, alarmInstance, false))
+                    : context.getString(R.string.alarm_alert_dismiss_now_text);
+            preemptiveDismissButton.setText(dismissText);
+        } else {
+            preemptiveDismissContainer.setVisibility(View.GONE);
+        }
+        return canBind;
+    }
+
+    protected void bindAlarmMutedButton(Context context, Alarm alarm) {
+        final AudioManager audioManager =
+                (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+        if (hasSilentRingtone(alarm)) {
+            mAlarmMutedButton.setVisibility(View.VISIBLE);
+            mAlarmMutedButton.setText(R.string.silent_ringtone);
+        } else if (audioManager.getStreamVolume(AudioManager.STREAM_ALARM) == 0) {
+            mAlarmMutedButton.setVisibility(View.VISIBLE);
+            mAlarmMutedButton.setText(R.string.alarm_volume_muted);
+        } else {
+            mAlarmMutedButton.setVisibility(View.GONE);
+        }
+    }
+
+    private boolean hasSilentRingtone(Alarm alarm) {
+        return Objects.equals(alarm.alert, DataModel.getDataModel().getSilentRingtoneUri());
+    }
+}
diff --git a/src/com/android/deskclock/alarms/dataadapter/AlarmTimeAdapter.java b/src/com/android/deskclock/alarms/dataadapter/AlarmTimeAdapter.java
deleted file mode 100644
index 15d1f4e..0000000
--- a/src/com/android/deskclock/alarms/dataadapter/AlarmTimeAdapter.java
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * Copyright (C) 2015 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.
- */
-
-package com.android.deskclock.alarms.dataadapter;
-
-import android.content.Context;
-import android.database.Cursor;
-import android.os.Bundle;
-import android.os.Vibrator;
-import android.support.v7.widget.RecyclerView;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-import com.android.deskclock.LogUtils;
-import com.android.deskclock.R;
-import com.android.deskclock.alarms.AlarmTimeClickHandler;
-import com.android.deskclock.alarms.ScrollHandler;
-import com.android.deskclock.provider.Alarm;
-import com.android.deskclock.provider.AlarmInstance;
-
-/**
- * Data adapter for alarm time items.
- */
-public final class AlarmTimeAdapter extends RecyclerView.Adapter<AlarmTimeViewHolder> {
-    private static final String TAG = "CwAlarm";
-    private static final String KEY_EXPANDED_ID = "expandedId";
-    private static final int VIEW_TYPE_ALARM_TIME_COLLAPSED = R.layout.alarm_time_collapsed;
-    private static final int VIEW_TYPE_ALARM_TIME_EXPANDED = R.layout.alarm_time_expanded;
-
-    private final Context mContext;
-    private final LayoutInflater mInflater;
-
-    private final AlarmTimeClickHandler mAlarmTimeClickHandler;
-    private final ScrollHandler mScrollHandler;
-
-    private final boolean mHasVibrator;
-    private int mExpandedPosition = -1;
-    private long mExpandedId = Alarm.INVALID_ID;
-    private Cursor mCursor;
-
-    public AlarmTimeAdapter(Context context, Bundle savedState,
-            AlarmTimeClickHandler alarmTimeClickHandler, ScrollHandler smoothScrollController) {
-        mContext = context;
-        mInflater = LayoutInflater.from(context);
-        mScrollHandler = smoothScrollController;
-        mAlarmTimeClickHandler = alarmTimeClickHandler;
-        mHasVibrator = ((Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE))
-                .hasVibrator();
-        if (savedState != null) {
-            mExpandedId = savedState.getLong(KEY_EXPANDED_ID, Alarm.INVALID_ID);
-        }
-
-        setHasStableIds(true);
-    }
-
-    @Override
-    public AlarmTimeViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
-        final View v = mInflater.inflate(viewType, parent, false /* attachToRoot */);
-        if (viewType == VIEW_TYPE_ALARM_TIME_COLLAPSED) {
-            return new CollapsedAlarmViewHolder(v, mAlarmTimeClickHandler, this);
-        } else {
-            return new ExpandedAlarmViewHolder(v, mHasVibrator, mAlarmTimeClickHandler, this);
-        }
-    }
-
-    @Override
-    public void onViewRecycled(AlarmTimeViewHolder viewHolder) {
-        super.onViewRecycled(viewHolder);
-        viewHolder.clearData();
-    }
-
-    @Override
-    public void onBindViewHolder(AlarmTimeViewHolder viewHolder, int position) {
-        if (!mCursor.moveToPosition(position)) {
-            LogUtils.e(TAG, "Failed to bind alarm " + position);
-            return;
-        }
-        final Alarm alarm = new Alarm(mCursor);
-        final AlarmInstance alarmInstance = alarm.canPreemptivelyDismiss()
-                ? new AlarmInstance(mCursor, true /* joinedTable */) : null;
-        viewHolder.bindAlarm(mContext, alarm, alarmInstance);
-    }
-
-    @Override
-    public int getItemCount() {
-        return mCursor == null ? 0 : mCursor.getCount();
-    }
-
-    @Override
-    public long getItemId(int position) {
-        if (mCursor == null || !mCursor.moveToPosition(position)) {
-            return RecyclerView.NO_ID;
-        }
-        // TODO: Directly read id instead of instantiating Alarm object.
-        return new Alarm(mCursor).id;
-    }
-
-    @Override
-    public int getItemViewType(int position) {
-        final long stableId = getItemId(position);
-        return stableId != RecyclerView.NO_ID && stableId == mExpandedId
-                ? VIEW_TYPE_ALARM_TIME_EXPANDED : VIEW_TYPE_ALARM_TIME_COLLAPSED;
-    }
-
-    public void saveInstance(Bundle outState) {
-        outState.putLong(KEY_EXPANDED_ID, mExpandedId);
-    }
-
-    /**
-     * Request the UI to expand the alarm at selected position and scroll it into view.
-     */
-    public void expand(int position) {
-        final long stableId = getItemId(position);
-        if (mExpandedId == stableId) {
-            return;
-        }
-        mExpandedId = stableId;
-        mScrollHandler.smoothScrollTo(position);
-        if (mExpandedPosition >= 0) {
-            notifyItemChanged(mExpandedPosition);
-        }
-        mExpandedPosition = position;
-        notifyItemChanged(position);
-    }
-
-    public void collapse(int position) {
-        mExpandedId = Alarm.INVALID_ID;
-        mExpandedPosition = -1;
-        notifyItemChanged(position);
-    }
-
-    /**
-     * Swaps the adapter to a new data source.
-     *
-     * @param cursor A cursor generated by Cursor loader from {@link Alarm#getAlarmsCursorLoader}.
-     */
-    public void swapCursor(Cursor cursor) {
-        if (mCursor == cursor) {
-            return;
-        }
-        if (mCursor != null) {
-            mCursor.close();
-        }
-        mCursor = cursor;
-        notifyDataSetChanged();
-    }
-}
diff --git a/src/com/android/deskclock/alarms/dataadapter/AlarmTimeViewHolder.java b/src/com/android/deskclock/alarms/dataadapter/AlarmTimeViewHolder.java
deleted file mode 100644
index b23eebb..0000000
--- a/src/com/android/deskclock/alarms/dataadapter/AlarmTimeViewHolder.java
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Copyright (C) 2015 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.
- */
-
-package com.android.deskclock.alarms.dataadapter;
-
-import android.content.Context;
-import android.support.v7.widget.RecyclerView;
-import android.support.v7.widget.SwitchCompat;
-import android.view.View;
-import android.widget.CompoundButton;
-import android.widget.TextView;
-
-import com.android.deskclock.AlarmUtils;
-import com.android.deskclock.R;
-import com.android.deskclock.alarms.AlarmTimeClickHandler;
-import com.android.deskclock.provider.Alarm;
-import com.android.deskclock.provider.AlarmInstance;
-import com.android.deskclock.widget.TextTime;
-
-/**
- * Abstract ViewHolder for alarm time items.
- */
-public abstract class AlarmTimeViewHolder extends RecyclerView.ViewHolder {
-
-    private static final float CLOCK_ENABLED_ALPHA = 1f;
-    private static final float CLOCK_DISABLED_ALPHA = 0.69f;
-
-    public final TextTime clock;
-    public final CompoundButton onoff;
-    public final View arrow;
-    public final View preemptiveDismissContainer;
-    public final TextView preemptiveDismissButton;
-
-    protected Alarm mAlarm;
-    protected AlarmInstance mAlarmInstance;
-
-    private final AlarmTimeClickHandler mAlarmTimeClickHandler;
-
-    public AlarmTimeViewHolder(View itemView, AlarmTimeClickHandler alarmTimeClickHandler) {
-        super(itemView);
-        mAlarmTimeClickHandler = alarmTimeClickHandler;
-        clock = (TextTime) itemView.findViewById(R.id.digital_clock);
-        onoff = (CompoundButton) itemView.findViewById(R.id.onoff);
-        arrow = itemView.findViewById(R.id.arrow);
-        preemptiveDismissContainer = itemView.findViewById(R.id.preemptive_dismiss_container);
-        preemptiveDismissButton =
-                (TextView) itemView.findViewById(R.id.preemptive_dismiss_button);
-        preemptiveDismissButton.setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                mAlarmTimeClickHandler.dismissAlarmInstance(mAlarmInstance);
-            }
-        });
-        onoff.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
-            @Override
-            public void onCheckedChanged(CompoundButton compoundButton, boolean checked) {
-                mAlarmTimeClickHandler.setAlarmEnabled(mAlarm, checked);
-            }
-        });
-    }
-
-    public void setData(Alarm alarm, AlarmInstance alarmInstance) {
-        mAlarmInstance = alarmInstance;
-        mAlarm = alarm;
-    }
-
-    public void clearData() {
-        mAlarmInstance = null;
-        mAlarm = null;
-    }
-
-    /**
-     * Binds the view with {@link Alarm} data.
-     */
-    public abstract void bindAlarm(Context context, Alarm alarm, AlarmInstance alarmInstance);
-
-    protected void bindOnOffSwitch(Context context, Alarm alarm) {
-        onoff.setChecked(alarm.enabled);
-        ((SwitchCompat) onoff).setTextOn(context.getString(R.string.on_switch));
-        ((SwitchCompat) onoff).setTextOff(context.getString(R.string.off_switch));
-    }
-
-    protected void bindClock(Context context, Alarm alarm) {
-        clock.setAlpha(alarm.enabled ? CLOCK_ENABLED_ALPHA : CLOCK_DISABLED_ALPHA);
-        clock.setFormat(context);
-        clock.setTime(alarm.hour, alarm.minutes);
-    }
-
-    protected boolean bindPreemptiveDismissButton(Context context, Alarm alarm,
-            AlarmInstance alarmInstance) {
-        boolean canBind = alarm.canPreemptivelyDismiss() && alarmInstance != null;
-        if (canBind) {
-            preemptiveDismissContainer.setVisibility(View.VISIBLE);
-            final String dismissText = alarm.instanceState == AlarmInstance.SNOOZE_STATE
-                    ? context.getString(R.string.alarm_alert_snooze_until,
-                    AlarmUtils.getAlarmText(context, alarmInstance, false))
-                    : context.getString(R.string.alarm_alert_dismiss_now_text);
-            preemptiveDismissButton.setText(dismissText);
-        } else {
-            preemptiveDismissContainer.setVisibility(View.GONE);
-        }
-        return canBind;
-    }
-}
diff --git a/src/com/android/deskclock/alarms/dataadapter/CollapsedAlarmViewHolder.java b/src/com/android/deskclock/alarms/dataadapter/CollapsedAlarmViewHolder.java
index 6db8878..0eb833c 100644
--- a/src/com/android/deskclock/alarms/dataadapter/CollapsedAlarmViewHolder.java
+++ b/src/com/android/deskclock/alarms/dataadapter/CollapsedAlarmViewHolder.java
@@ -17,13 +17,17 @@
 package com.android.deskclock.alarms.dataadapter;
 
 import android.content.Context;
+import android.os.Handler;
+import android.os.Looper;
 import android.text.TextUtils;
+import android.view.LayoutInflater;
 import android.view.View;
+import android.view.ViewGroup;
 import android.widget.TextView;
 
+import com.android.deskclock.ItemAdapter;
 import com.android.deskclock.R;
 import com.android.deskclock.Utils;
-import com.android.deskclock.alarms.AlarmTimeClickHandler;
 import com.android.deskclock.provider.Alarm;
 import com.android.deskclock.provider.AlarmInstance;
 
@@ -32,17 +36,17 @@
 /**
  * A ViewHolder containing views for an alarm item in collapsed stated.
  */
-public final class CollapsedAlarmViewHolder extends AlarmTimeViewHolder {
+public final class CollapsedAlarmViewHolder extends AlarmItemViewHolder {
+
+    public static final int VIEW_TYPE = R.layout.alarm_time_collapsed;
 
     public final TextView alarmLabel;
     public final TextView daysOfWeek;
     public final TextView upcomingInstanceLabel;
     public final View hairLine;
 
-    public CollapsedAlarmViewHolder(View itemView,
-            final AlarmTimeClickHandler alarmTimeClickHandler,
-            final AlarmTimeAdapter alarmTimeAdapter) {
-        super(itemView, alarmTimeClickHandler);
+    public CollapsedAlarmViewHolder(View itemView, Handler handler) {
+        super(itemView, handler);
         alarmLabel = (TextView) itemView.findViewById(R.id.label);
         daysOfWeek = (TextView) itemView.findViewById(R.id.days_of_week);
         upcomingInstanceLabel = (TextView) itemView.findViewById(R.id.upcoming_instance_label);
@@ -52,36 +56,37 @@
         itemView.setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
-                alarmTimeAdapter.expand(getAdapterPosition());
+                getItemHolder().expand();
             }
         });
         alarmLabel.setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
-                alarmTimeAdapter.expand(getAdapterPosition());
+                getItemHolder().expand();
             }
         });
         arrow.setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
-                alarmTimeAdapter.expand(getAdapterPosition());
+                getItemHolder().expand();
             }
         });
         // Edit time handler
         clock.setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
-                alarmTimeClickHandler.onClockClicked(mAlarm);
-                alarmTimeAdapter.expand(getAdapterPosition());
+                getItemHolder().getAlarmTimeClickHandler().onClockClicked(getItemHolder().item);
+                getItemHolder().expand();
             }
         });
     }
 
     @Override
-    public void bindAlarm(Context context, Alarm alarm, AlarmInstance alarmInstance) {
-        setData(alarm, alarmInstance);
-        bindOnOffSwitch(context, alarm);
-        bindClock(context, alarm);
+    protected void onBindItemView(AlarmItemHolder itemHolder) {
+        super.onBindItemView(itemHolder);
+        final Alarm alarm = itemHolder.item;
+        final AlarmInstance alarmInstance = itemHolder.getAlarmInstance();
+        final Context context = itemView.getContext();
         bindRepeatText(context, alarm);
         bindReadOnlyLabel(context, alarm);
         bindUpcomingInstance(context, alarm);
@@ -127,4 +132,21 @@
             upcomingInstanceLabel.setText(labelText);
         }
     }
+
+    public static class Factory implements ItemAdapter.ItemViewHolder.Factory {
+
+        private final LayoutInflater mLayoutInflater;
+        private final Handler mHandler;
+
+        public Factory(LayoutInflater layoutInflater) {
+            mLayoutInflater = layoutInflater;
+            mHandler = new Handler(Looper.getMainLooper());
+        }
+
+        @Override
+        public ItemAdapter.ItemViewHolder<?> createViewHolder(ViewGroup parent, int viewType) {
+            return new CollapsedAlarmViewHolder(mLayoutInflater.inflate(
+                    viewType, parent, false /* attachToRoot */), mHandler);
+        }
+    }
 }
diff --git a/src/com/android/deskclock/alarms/dataadapter/ExpandedAlarmViewHolder.java b/src/com/android/deskclock/alarms/dataadapter/ExpandedAlarmViewHolder.java
index c4c9bb0..26db82f 100644
--- a/src/com/android/deskclock/alarms/dataadapter/ExpandedAlarmViewHolder.java
+++ b/src/com/android/deskclock/alarms/dataadapter/ExpandedAlarmViewHolder.java
@@ -19,16 +19,23 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
+import android.graphics.Color;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.LayerDrawable;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Vibrator;
+import android.support.v4.content.ContextCompat;
 import android.view.LayoutInflater;
 import android.view.View;
-import android.widget.Button;
+import android.view.ViewGroup;
 import android.widget.CheckBox;
 import android.widget.CompoundButton;
+import android.widget.ImageButton;
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
+import com.android.deskclock.ItemAdapter;
 import com.android.deskclock.R;
 import com.android.deskclock.Utils;
 import com.android.deskclock.alarms.AlarmTimeClickHandler;
@@ -43,7 +50,9 @@
 /**
  * A ViewHolder containing views for an alarm item in expanded stated.
  */
-public final class ExpandedAlarmViewHolder extends AlarmTimeViewHolder {
+public final class ExpandedAlarmViewHolder extends AlarmItemViewHolder {
+
+    public static final int VIEW_TYPE = R.layout.alarm_time_expanded;
 
     public final CheckBox repeat;
     public final TextView editLabel;
@@ -51,18 +60,14 @@
     public final CompoundButton[] dayButtons = new CompoundButton[7];
     public final CheckBox vibrate;
     public final TextView ringtone;
-    public final Button delete;
-    public final View preemptiveDismissContainer;
-    public final TextView preemptiveDismissButton;
+    public final ImageButton delete;
 
     private final boolean mHasVibrator;
     private final int[] mDayOrder;
 
-    public ExpandedAlarmViewHolder(View itemView,
-            final boolean hasVibrator,
-            final AlarmTimeClickHandler alarmTimeClickHandler,
-            final AlarmTimeAdapter alarmTimeAdapter) {
-        super(itemView, alarmTimeClickHandler);
+    public ExpandedAlarmViewHolder(View itemView, final boolean hasVibrator, Handler handler) {
+        super(itemView, handler);
+
         final Context context = itemView.getContext();
         mHasVibrator = hasVibrator;
         mDayOrder = DayOrderUtils.getDayOrder(context);
@@ -71,14 +76,14 @@
 
         final TypedArray typedArray = theme.obtainStyledAttributes(attrs);
         final LayerDrawable background = new LayerDrawable(new Drawable[] {
-                context.getResources().getDrawable(R.drawable.alarm_background_expanded),
+                ContextCompat.getDrawable(context, R.drawable.alarm_background_expanded),
                 typedArray.getDrawable(0) });
         itemView.setBackground(background);
         typedArray.recycle();
 
         final int firstDay = Utils.getZeroIndexedFirstDayOfWeek(context);
 
-        delete = (Button) itemView.findViewById(R.id.delete);
+        delete = (ImageButton) itemView.findViewById(R.id.delete);
 
         repeat = (CheckBox) itemView.findViewById(R.id.repeat_onoff);
         vibrate = (CheckBox) itemView.findViewById(R.id.vibrate_onoff);
@@ -97,56 +102,53 @@
             dayButtons[i] = dayButton;
         }
 
-        preemptiveDismissContainer = itemView.findViewById(R.id.preemptive_dismiss_container);
-        preemptiveDismissButton =
-                (TextView) itemView.findViewById(R.id.preemptive_dismiss_button);
-
         // Collapse handler
         itemView.setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
-                alarmTimeAdapter.collapse(getAdapterPosition());
+                getItemHolder().collapse();
             }
         });
         arrow.setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
-                alarmTimeAdapter.collapse(getAdapterPosition());
+                getItemHolder().collapse();
             }
         });
         // Edit time handler
         clock.setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
-                alarmTimeClickHandler.onClockClicked(mAlarm);
+                getAlarmTimeClickHandler().onClockClicked(getItemHolder().item);
             }
         });
         // Edit label handler
         editLabel.setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View view) {
-                alarmTimeClickHandler.onEditLabelClicked(mAlarm);
+                getAlarmTimeClickHandler().onEditLabelClicked(getItemHolder().item);
             }
         });
         // Vibrator checkbox handler
         vibrate.setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
-                alarmTimeClickHandler.setAlarmVibrationEnabled(mAlarm, ((CheckBox) v).isChecked());
+                getAlarmTimeClickHandler().setAlarmVibrationEnabled(getItemHolder().item,
+                        ((CheckBox) v).isChecked());
             }
         });
         // Ringtone editor handler
         ringtone.setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View view) {
-                alarmTimeClickHandler.onRingtoneClicked(mAlarm);
+                getAlarmTimeClickHandler().onRingtoneClicked(getItemHolder().item);
             }
         });
         // Delete alarm handler
         delete.setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
-                alarmTimeClickHandler.onDeleteClicked(mAlarm);
+                getAlarmTimeClickHandler().onDeleteClicked(getItemHolder().item);
                 v.announceForAccessibility(context.getString(R.string.alarm_deleted));
             }
         });
@@ -155,7 +157,7 @@
             @Override
             public void onClick(View view) {
                 final boolean checked = ((CheckBox) view).isChecked();
-                alarmTimeClickHandler.setAlarmRepeatEnabled(mAlarm, checked);
+                getAlarmTimeClickHandler().setAlarmRepeatEnabled(getItemHolder().item, checked);
             }
         });
         // Day buttons handler
@@ -165,17 +167,19 @@
                 @Override
                 public void onClick(View view) {
                     final boolean isChecked = ((CompoundButton) view).isChecked();
-                    alarmTimeClickHandler.setDayOfWeekEnabled(mAlarm, isChecked, buttonIndex);
+                    getAlarmTimeClickHandler().setDayOfWeekEnabled(getItemHolder().item,
+                            isChecked, buttonIndex);
                 }
             });
         }
     }
 
     @Override
-    public void bindAlarm(Context context, Alarm alarm, AlarmInstance alarmInstance) {
-        setData(alarm, alarmInstance);
-        bindOnOffSwitch(context, alarm);
-        bindClock(context, alarm);
+    protected void onBindItemView(final AlarmItemHolder itemHolder) {
+        super.onBindItemView(itemHolder);
+        final Alarm alarm = itemHolder.item;
+        final AlarmInstance alarmInstance = itemHolder.getAlarmInstance();
+        final Context context = itemView.getContext();
         bindEditLabel(alarm);
         bindDaysOfWeekButtons(alarm);
         bindVibrator(alarm);
@@ -200,8 +204,7 @@
                 dayButton.setTextColor(Utils.getCurrentHourColor());
             } else {
                 dayButton.setChecked(false);
-                dayButton.setTextColor(itemView.getContext().getResources().getColor(R.color
-                        .white));
+                dayButton.setTextColor(Color.WHITE);
             }
         }
         if (alarm.daysOfWeek.isRepeating()) {
@@ -229,4 +232,28 @@
             vibrate.setChecked(alarm.vibrate);
         }
     }
+
+    private AlarmTimeClickHandler getAlarmTimeClickHandler() {
+        return getItemHolder().getAlarmTimeClickHandler();
+    }
+
+    public static class Factory implements ItemAdapter.ItemViewHolder.Factory {
+
+        private final LayoutInflater mLayoutInflator;
+        private final Handler mHandler;
+        private final boolean mHasVibrator;
+
+        public Factory(Context context, LayoutInflater layoutInflater) {
+            mLayoutInflator = layoutInflater;
+            mHandler = new Handler(Looper.getMainLooper());
+            mHasVibrator =
+                    ((Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE)).hasVibrator();
+        }
+
+        @Override
+        public ItemAdapter.ItemViewHolder<?> createViewHolder(ViewGroup parent, int viewType) {
+            return new ExpandedAlarmViewHolder(mLayoutInflator.inflate(
+                    viewType, parent, false /* attachToRoot */), mHasVibrator, mHandler);
+        }
+    }
 }
diff --git a/src/com/android/deskclock/data/AlarmModel.java b/src/com/android/deskclock/data/AlarmModel.java
index 750b72b..0ae5a0b 100644
--- a/src/com/android/deskclock/data/AlarmModel.java
+++ b/src/com/android/deskclock/data/AlarmModel.java
@@ -67,8 +67,11 @@
     }
 
     void setDefaultAlarmRingtoneUri(Uri uri) {
-        mSettingsModel.setDefaultAlarmRingtoneUri(uri);
-        mDefaultAlarmRingtoneUri = uri;
+        // Never set the silent ringtone as default; new alarms should always make sound by default.
+        if (!Alarm.NO_RINGTONE_URI.equals(uri)) {
+            mSettingsModel.setDefaultAlarmRingtoneUri(uri);
+            mDefaultAlarmRingtoneUri = uri;
+        }
     }
 
     String getAlarmRingtoneTitle(Uri uri) {
diff --git a/src/com/android/deskclock/data/City.java b/src/com/android/deskclock/data/City.java
index a5fd850..0463e8a 100644
--- a/src/com/android/deskclock/data/City.java
+++ b/src/com/android/deskclock/data/City.java
@@ -50,6 +50,12 @@
     /** A cached upper case form of the {@link #mName} used in case-insensitive name comparisons. */
     private String mNameUpperCase;
 
+    /**
+     * A cached upper case form of the {@link #mName} used in case-insensitive name comparisons
+     * which ignore {@link #removeSpecialCharacters(String)} special characters.
+     */
+    private String mNameUpperCaseNoSpecialCharacters;
+
     City(String id, int index, String indexString, String name, String phoneticName,
             String timeZoneId) {
         mId = id;
@@ -69,6 +75,9 @@
     public String getIndexString() { return mIndexString; }
     public String getPhoneticName() { return mPhoneticName; }
 
+    /**
+     * @return the city name converted to upper case
+     */
     public String getNameUpperCase() {
         if (mNameUpperCase == null) {
             mNameUpperCase = mName.toUpperCase();
@@ -76,6 +85,27 @@
         return mNameUpperCase;
     }
 
+    /**
+     * @return the city name converted to upper case with all special characters removed
+     */
+    private String getNameUpperCaseNoSpecialCharacters() {
+        if (mNameUpperCaseNoSpecialCharacters == null) {
+            mNameUpperCaseNoSpecialCharacters = removeSpecialCharacters(getNameUpperCase());
+        }
+        return mNameUpperCaseNoSpecialCharacters;
+    }
+
+    /**
+     * @param upperCaseQueryNoSpecialCharacters search term with all special characters removed
+     *      to match against the upper case city name
+     * @return {@code true} iff the name of this city starts with the given query
+     */
+    public boolean matches(String upperCaseQueryNoSpecialCharacters) {
+        // By removing all special characters, prefix matching becomes more liberal and it is easier
+        // to locate the desired city. e.g. "St. Lucia" is matched by "StL", "St.L", "St L", "St. L"
+        return getNameUpperCaseNoSpecialCharacters().startsWith(upperCaseQueryNoSpecialCharacters);
+    }
+
     @Override
     public String toString() {
         return String.format("City {id=%s, index=%d, indexString=%s, name=%s, phonetic=%s, tz=%s}",
@@ -83,6 +113,17 @@
     }
 
     /**
+     * Strips out any characters considered optional for matching purposes. These include spaces,
+     * dashes, periods and apostrophes.
+     *
+     * @param token a city name or search term
+     * @return the given {@code token} without any characters considered optional when matching
+     */
+    public static String removeSpecialCharacters(String token) {
+        return token.replaceAll("[ -.']", "");
+    }
+
+    /**
      * Orders by:
      *
      * <ol>
diff --git a/src/com/android/deskclock/data/CityModel.java b/src/com/android/deskclock/data/CityModel.java
index 4f88029..ec987bc 100644
--- a/src/com/android/deskclock/data/CityModel.java
+++ b/src/com/android/deskclock/data/CityModel.java
@@ -52,9 +52,11 @@
      * Retain a hard reference to the shared preference observer to prevent it from being garbage
      * collected. See {@link SharedPreferences#registerOnSharedPreferenceChangeListener} for detail.
      */
+    @SuppressWarnings("FieldCanBeLocal")
     private final OnSharedPreferenceChangeListener mPreferenceListener = new PreferenceListener();
 
     /** Clears data structures containing data that is locale-sensitive. */
+    @SuppressWarnings("FieldCanBeLocal")
     private final BroadcastReceiver mLocaleChangedReceiver = new LocaleChangedReceiver();
 
     /** Maps city ID to city instance. */
@@ -269,4 +271,4 @@
             }
         }
     }
-}
\ No newline at end of file
+}
diff --git a/src/com/android/deskclock/data/DataModel.java b/src/com/android/deskclock/data/DataModel.java
index 382beb3..58e92b0 100644
--- a/src/com/android/deskclock/data/DataModel.java
+++ b/src/com/android/deskclock/data/DataModel.java
@@ -149,6 +149,7 @@
      * @return {@code true} when the application is open in the foreground; {@code false} otherwise
      */
     public boolean isApplicationInForeground() {
+        enforceMainLooper();
         return mNotificationModel.isApplicationInForeground();
     }
 
@@ -157,10 +158,16 @@
      * be rebuilt. e.g. after upgrading the application
      */
     public void updateAllNotifications() {
+        enforceMainLooper();
         mTimerModel.updateNotification();
         mStopwatchModel.updateNotification();
     }
 
+    public Uri getSilentRingtoneUri() {
+        enforceMainLooper();
+        return Uri.EMPTY;
+    }
+
     //
     // Cities
     //
@@ -343,10 +350,11 @@
      *
      * @param timer the timer to be reset
      * @param eventLabelId the label of the timer event to send; 0 if no event should be sent
+     * @return the reset {@code timer} or {@code null} if the timer was deleted
      */
-    public void resetOrDeleteTimer(Timer timer, @StringRes int eventLabelId) {
+    public Timer resetOrDeleteTimer(Timer timer, @StringRes int eventLabelId) {
         enforceMainLooper();
-        mTimerModel.resetOrDeleteTimer(timer, eventLabelId);
+        return mTimerModel.resetOrDeleteTimer(timer, eventLabelId);
     }
 
     /**
@@ -429,6 +437,14 @@
     }
 
     /**
+     * @param uri the uri of the ringtone to play for all timers
+     */
+    public void setTimerRingtoneUri(Uri uri) {
+        enforceMainLooper();
+        mTimerModel.setTimerRingtoneUri(uri);
+    }
+
+    /**
      * @return the title of the ringtone that is played for all timers
      */
     public String getTimerRingtoneTitle() {
@@ -436,6 +452,22 @@
         return mTimerModel.getTimerRingtoneTitle();
     }
 
+    /**
+     * @return whether vibrate is enabled for all timers.
+     */
+    public boolean getTimerVibrate() {
+        enforceMainLooper();
+        return mTimerModel.getTimerVibrate();
+    }
+
+    /**
+     * @param enabled whether vibrate is enabled for all timers.
+     */
+    public void setTimerVibrate(boolean enabled) {
+        enforceMainLooper();
+        mTimerModel.setTimerVibrate(enabled);
+    }
+
     //
     // Alarms
     //
@@ -605,4 +637,4 @@
             return mExecuted;
         }
     }
-}
\ No newline at end of file
+}
diff --git a/src/com/android/deskclock/data/SettingsDAO.java b/src/com/android/deskclock/data/SettingsDAO.java
index 33ede47..192874b 100644
--- a/src/com/android/deskclock/data/SettingsDAO.java
+++ b/src/com/android/deskclock/data/SettingsDAO.java
@@ -120,6 +120,30 @@
     }
 
     /**
+     * @return whether timer vibration is enabled. false by default.
+     */
+    static boolean getTimerVibrate(Context context) {
+        final SharedPreferences prefs = getSharedPreferences(context);
+        return prefs.getBoolean(SettingsActivity.KEY_TIMER_VIBRATE, false);
+    }
+
+    /**
+     * @param enabled whether vibration will be turned on for all timers.
+     */
+    static void setTimerVibrate(Context context, boolean enabled) {
+        final SharedPreferences prefs = getSharedPreferences(context);
+        prefs.edit().putBoolean(SettingsActivity.KEY_TIMER_VIBRATE, enabled).apply();
+    }
+
+    /**
+     * @param uri the uri of the ringtone to play for all timers
+     */
+    static void setTimerRingtoneUri(Context context, Uri uri) {
+        final SharedPreferences prefs = getSharedPreferences(context);
+        prefs.edit().putString(SettingsActivity.KEY_TIMER_RINGTONE, uri.toString()).apply();
+    }
+
+    /**
      * @return the uri of the selected ringtone or the {@code defaultUri} if no explicit selection
      *      has yet been made
      */
@@ -128,7 +152,6 @@
         final String uriString = prefs.getString(KEY_DEFAULT_ALARM_RINGTONE_URI, null);
         return uriString == null ? defaultUri : Uri.parse(uriString);
     }
-
     /**
      * @param uri identifies the default ringtone to play for new alarms
      */
diff --git a/src/com/android/deskclock/data/SettingsModel.java b/src/com/android/deskclock/data/SettingsModel.java
index 224f190..24649bf 100644
--- a/src/com/android/deskclock/data/SettingsModel.java
+++ b/src/com/android/deskclock/data/SettingsModel.java
@@ -86,6 +86,10 @@
         return mDefaultTimerRingtoneUri;
     }
 
+    void setTimerRingtoneUri(Uri uri) {
+        SettingsDAO.setTimerRingtoneUri(mContext, uri);
+    }
+
     Uri getTimerRingtoneUri() {
         return SettingsDAO.getTimerRingtoneUri(mContext, getDefaultTimerRingtoneUri());
     }
@@ -98,4 +102,12 @@
     void setDefaultAlarmRingtoneUri(Uri uri) {
         SettingsDAO.setDefaultAlarmRingtoneUri(mContext, uri);
     }
+
+    boolean getTimerVibrate() {
+        return SettingsDAO.getTimerVibrate(mContext);
+    }
+
+    void setTimerVibrate(boolean enabled) {
+        SettingsDAO.setTimerVibrate(mContext, enabled);
+    }
 }
\ No newline at end of file
diff --git a/src/com/android/deskclock/data/TimerModel.java b/src/com/android/deskclock/data/TimerModel.java
index 877af10..ebc7eb2 100644
--- a/src/com/android/deskclock/data/TimerModel.java
+++ b/src/com/android/deskclock/data/TimerModel.java
@@ -270,9 +270,10 @@
      *
      * @param timer the timer to be reset
      * @param eventLabelId the label of the timer event to send; 0 if no event should be sent
+     * @return the reset {@code timer} or {@code null} if the timer was deleted
      */
-    void resetOrDeleteTimer(Timer timer, @StringRes int eventLabelId) {
-        doResetOrDeleteTimer(timer, eventLabelId);
+    Timer resetOrDeleteTimer(Timer timer, @StringRes int eventLabelId) {
+        final Timer result = doResetOrDeleteTimer(timer, eventLabelId);
 
         // Update the notification after updating the timer data.
         updateNotification();
@@ -281,6 +282,8 @@
         if (timer.isExpired()) {
             updateHeadsUpNotification();
         }
+
+        return result;
     }
 
     /**
@@ -361,6 +364,13 @@
     }
 
     /**
+     * @param uri the uri of the ringtone to play for all timers
+     */
+    void setTimerRingtoneUri(Uri uri) {
+        mSettingsModel.setTimerRingtoneUri(uri);
+    }
+
+    /**
      * @return the title of the ringtone that is played for all timers
      */
     String getTimerRingtoneTitle() {
@@ -385,6 +395,20 @@
         return mTimerRingtoneTitle;
     }
 
+    /**
+     * @return whether vibration is enabled for timers.
+     */
+    boolean getTimerVibrate() {
+        return mSettingsModel.getTimerVibrate();
+    }
+
+    /**
+     * @param enabled whether the
+     */
+    void setTimerVibrate(boolean enabled) {
+        mSettingsModel.setTimerVibrate(enabled);
+    }
+
     private List<Timer> getMutableTimers() {
         if (mTimers == null) {
             mTimers = TimerDAO.getTimers(mContext);
@@ -500,19 +524,25 @@
      *
      * @param timer the timer to be reset
      * @param eventLabelId the label of the timer event to send; 0 if no event should be sent
+     * @return the reset {@code timer} or {@code null} if the timer was deleted
      */
-    private void doResetOrDeleteTimer(Timer timer, @StringRes int eventLabelId) {
+    private Timer doResetOrDeleteTimer(Timer timer, @StringRes int eventLabelId) {
         if (timer.isExpired() && timer.getDeleteAfterUse()) {
             doRemoveTimer(timer);
             if (eventLabelId != 0) {
                 Events.sendTimerEvent(R.string.action_delete, eventLabelId);
             }
+            return null;
         } else if (!timer.isReset()) {
-            doUpdateTimer(timer.reset());
+            final Timer reset = timer.reset();
+            doUpdateTimer(reset);
             if (eventLabelId != 0) {
                 Events.sendTimerEvent(R.string.action_reset, eventLabelId);
             }
+            return reset;
         }
+
+        return timer;
     }
 
     /**
@@ -859,8 +889,7 @@
 
     private void schedulePendingIntent(long triggerTime, PendingIntent pi) {
         if (Utils.isMOrLater()) {
-            // Make sure the timer fires when the device is in doze mode. The timer is not
-            // guaranteed to fire at the requested time. It may be delayed up to 15 minutes.
+            // Ensure the timer fires even if the device is dozing.
             mAlarmManager.setExactAndAllowWhileIdle(ELAPSED_REALTIME_WAKEUP, triggerTime, pi);
         } else {
             mAlarmManager.setExact(ELAPSED_REALTIME_WAKEUP, triggerTime, pi);
@@ -893,4 +922,4 @@
             }
         }
     }
-}
\ No newline at end of file
+}
diff --git a/src/com/android/deskclock/settings/AlarmVolumePreference.java b/src/com/android/deskclock/settings/AlarmVolumePreference.java
new file mode 100644
index 0000000..b186769
--- /dev/null
+++ b/src/com/android/deskclock/settings/AlarmVolumePreference.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+package com.android.deskclock.settings;
+
+import android.content.Context;
+import android.database.ContentObserver;
+import android.media.AudioManager;
+import android.provider.Settings;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceViewHolder;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.SeekBar;
+
+import com.android.deskclock.R;
+import com.android.deskclock.RingtonePreviewKlaxon;
+import com.android.deskclock.data.DataModel;
+
+public class AlarmVolumePreference extends Preference {
+
+    private static final long ALARM_PREVIEW_DURATION_MS = 2000;
+
+    private SeekBar mSeekbar;
+    private ImageView mAlarmIcon;
+    private boolean mPreviewPlaying;
+
+    public AlarmVolumePreference(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    public void onBindViewHolder(PreferenceViewHolder holder) {
+        super.onBindViewHolder(holder);
+
+        // Disable click feedback for this preference.
+        holder.itemView.setClickable(false);
+
+        final Context context = getContext();
+        final AudioManager audioManager =
+                (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+        mSeekbar = (SeekBar) holder.findViewById(R.id.alarm_volume_slider);
+        mSeekbar.setMax(audioManager.getStreamMaxVolume(AudioManager.STREAM_ALARM));
+        mSeekbar.setProgress(audioManager.getStreamVolume(AudioManager.STREAM_ALARM));
+        mAlarmIcon = (ImageView) holder.findViewById(R.id.alarm_icon);
+        updateIcon();
+
+        final ContentObserver volumeObserver = new ContentObserver(mSeekbar.getHandler()) {
+            @Override
+            public void onChange(boolean selfChange) {
+                // Volume was changed elsewhere, update our slider.
+                mSeekbar.setProgress(audioManager.getStreamVolume(
+                        AudioManager.STREAM_ALARM));
+            }
+        };
+
+        mSeekbar.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
+            @Override
+            public void onViewAttachedToWindow(View v) {
+                context.getContentResolver().registerContentObserver(Settings.System.CONTENT_URI,
+                        true, volumeObserver);
+            }
+
+            @Override
+            public void onViewDetachedFromWindow(View v) {
+                context.getContentResolver().unregisterContentObserver(volumeObserver);
+            }
+        });
+
+
+        mSeekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+            @Override
+            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+                if (fromUser) {
+                    audioManager.setStreamVolume(
+                            AudioManager.STREAM_ALARM, seekBar.getProgress(), 0);
+                }
+                updateIcon();
+            }
+
+            @Override
+            public void onStartTrackingTouch(SeekBar seekBar) {
+            }
+
+            @Override
+            public void onStopTrackingTouch(SeekBar seekBar) {
+                if (!mPreviewPlaying) {
+                    RingtonePreviewKlaxon.start(
+                            context, DataModel.getDataModel().getDefaultAlarmRingtoneUri());
+                    mPreviewPlaying = true;
+
+                    seekBar.postDelayed(new Runnable() {
+                        @Override
+                        public void run() {
+                            RingtonePreviewKlaxon.stop(context);
+                            mPreviewPlaying = false;
+                        }
+                    }, ALARM_PREVIEW_DURATION_MS);
+                }
+            }
+        });
+    }
+
+    private void updateIcon() {
+        mAlarmIcon.setImageResource(mSeekbar.getProgress() == 0 ?
+                R.drawable.ic_alarm_off_24dp : R.drawable.ic_alarm_small_24dp);
+    }
+}
diff --git a/src/com/android/deskclock/settings/CrescendoLengthDialog.java b/src/com/android/deskclock/settings/CrescendoLengthDialog.java
deleted file mode 100644
index 8b8b6f3..0000000
--- a/src/com/android/deskclock/settings/CrescendoLengthDialog.java
+++ /dev/null
@@ -1,140 +0,0 @@
-package com.android.deskclock.settings;
-
-import android.app.AlertDialog;
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.os.Parcelable;
-import android.preference.DialogPreference;
-import android.support.annotation.NonNull;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.NumberPicker;
-import android.widget.TextView;
-
-import com.android.deskclock.NumberPickerCompat;
-import com.android.deskclock.R;
-
-/**
- * A dialog preference that shows a number picker for selecting crescendo length
- */
-public final class CrescendoLengthDialog extends DialogPreference {
-
-    private static final String DEFAULT_CRESCENDO_TIME = "0";
-    private static final int CRESCENDO_TIME_STEP = 5;
-
-    private NumberPickerCompat mNumberPickerView;
-    private TextView mNumberPickerSecondsView;
-    private int mCrescendoSeconds;
-
-    public CrescendoLengthDialog(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        setDialogLayoutResource(R.layout.crescendo_length_picker);
-        setTitle(R.string.crescendo_duration_title);
-    }
-
-    @Override
-    protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
-        super.onPrepareDialogBuilder(builder);
-        builder.setTitle(getContext().getString(R.string.crescendo_duration_title))
-                .setCancelable(true);
-    }
-
-    @Override
-    protected void onBindDialogView(@NonNull View view) {
-        super.onBindDialogView(view);
-
-        final String[] displayedValues = new String[13];
-        displayedValues[0] = getContext().getString(R.string.no_crescendo_duration);
-        for (int i = 1; i < displayedValues.length; i++) {
-            displayedValues[i] = String.valueOf(i * CRESCENDO_TIME_STEP);
-        }
-
-        mNumberPickerSecondsView = (TextView) view.findViewById(R.id.title);
-        mNumberPickerSecondsView.setText(getContext().getString(R.string.crescendo_picker_label));
-        mNumberPickerView = (NumberPickerCompat) view.findViewById(R.id.seconds_picker);
-        mNumberPickerView.setDisplayedValues(displayedValues);
-        mNumberPickerView.setMinValue(0);
-        mNumberPickerView.setMaxValue(displayedValues.length - 1);
-        mNumberPickerView.setValue(mCrescendoSeconds / CRESCENDO_TIME_STEP);
-        updateUnits();
-
-        mNumberPickerView.setOnValueChangedListener(new NumberPicker.OnValueChangeListener() {
-            @Override
-            public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
-                updateUnits();
-            }
-        });
-        mNumberPickerView.setOnAnnounceValueChangedListener(
-                new NumberPickerCompat.OnAnnounceValueChangedListener() {
-            @Override
-            public void onAnnounceValueChanged(NumberPicker picker, int value,
-                    String displayedValue) {
-                final String announceString;
-                if (value == 0) {
-                    announceString = getContext().getString(R.string.no_crescendo_duration);
-                } else {
-                    announceString = getContext().getString(
-                            R.string.crescendo_duration, displayedValue);
-                }
-                picker.announceForAccessibility(announceString);
-            }
-        });
-    }
-
-    @Override
-    protected void onSetInitialValue(boolean restorePersistedValue, Object defaultValue) {
-        String val;
-        if (restorePersistedValue) {
-            val = getPersistedString(DEFAULT_CRESCENDO_TIME);
-            if (val != null) {
-                mCrescendoSeconds = Integer.parseInt(val);
-            }
-        } else {
-            val = (String) defaultValue;
-            if (val != null) {
-                mCrescendoSeconds = Integer.parseInt(val);
-            }
-            persistString(val);
-        }
-    }
-
-    @Override
-    protected Object onGetDefaultValue(TypedArray a, int index) {
-        return a.getString(index);
-    }
-
-    @Override
-    protected void onRestoreInstanceState(Parcelable state) {
-        // Restore the value to the NumberPicker.
-        super.onRestoreInstanceState(state);
-
-        // Update the unit display in response to the new value.
-        updateUnits();
-    }
-
-    private void updateUnits() {
-        if (mNumberPickerView != null) {
-            final int value = mNumberPickerView.getValue();
-            final int visibility = value == 0 ? View.INVISIBLE : View.VISIBLE;
-            mNumberPickerSecondsView.setVisibility(visibility);
-        }
-    }
-
-    @Override
-    protected void onDialogClosed(boolean positiveResult) {
-        if (positiveResult) {
-            mNumberPickerView.clearFocus();
-            mCrescendoSeconds = mNumberPickerView.getValue() * CRESCENDO_TIME_STEP;
-            persistString(Integer.toString(mCrescendoSeconds));
-            setSummary();
-        }
-    }
-
-    public void setSummary() {
-        if (mCrescendoSeconds == 0) {
-            setSummary(getContext().getString(R.string.no_crescendo_duration));
-        } else {
-            setSummary(getContext().getString(R.string.crescendo_duration, mCrescendoSeconds));
-        }
-    }
-}
\ No newline at end of file
diff --git a/src/com/android/deskclock/settings/CrescendoLengthDialogFragment.java b/src/com/android/deskclock/settings/CrescendoLengthDialogFragment.java
new file mode 100644
index 0000000..5cff800
--- /dev/null
+++ b/src/com/android/deskclock/settings/CrescendoLengthDialogFragment.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+package com.android.deskclock.settings;
+
+import android.app.DialogFragment;
+import android.os.Bundle;
+import android.support.v14.preference.PreferenceDialogFragment;
+import android.support.v7.preference.Preference;
+import android.view.View;
+import android.widget.NumberPicker;
+import android.widget.TextView;
+
+import com.android.deskclock.NumberPickerCompat;
+import com.android.deskclock.R;
+
+public class CrescendoLengthDialogFragment extends PreferenceDialogFragment {
+
+    private static final int CRESCENDO_TIME_STEP = 5;
+
+    private NumberPickerCompat mNumberPickerView;
+
+    public static DialogFragment newInstance(Preference preference) {
+        final CrescendoLengthDialogFragment fragment = new CrescendoLengthDialogFragment();
+        final Bundle bundle = new Bundle();
+        bundle.putString(ARG_KEY, preference.getKey());
+        fragment.setArguments(bundle);
+        return fragment;
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        getPreference().setDialogLayoutResource(R.layout.crescendo_length_picker);
+    }
+
+    @Override
+    protected void onBindDialogView(View view) {
+        final CrescendoLengthDialogPreference preference =
+                (CrescendoLengthDialogPreference) getPreference();
+        final int crescendoSeconds = preference.getPersistedCrescendoLength();
+
+        final TextView unitView = (TextView) view.findViewById(R.id.title);
+        unitView.setText(R.string.crescendo_picker_label);
+        updateUnits(unitView, crescendoSeconds);
+
+        final String[] displayedValues = new String[13];
+        displayedValues[0] = getString(R.string.no_crescendo_duration);
+        for (int i = 1; i < displayedValues.length; i++) {
+            displayedValues[i] = String.valueOf(i * CRESCENDO_TIME_STEP);
+        }
+
+        mNumberPickerView = (NumberPickerCompat) view.findViewById(R.id.seconds_picker);
+        mNumberPickerView.setDisplayedValues(displayedValues);
+        mNumberPickerView.setMinValue(0);
+        mNumberPickerView.setMaxValue(displayedValues.length - 1);
+        mNumberPickerView.setValue(crescendoSeconds / CRESCENDO_TIME_STEP);
+
+        mNumberPickerView.setOnValueChangedListener(new NumberPicker.OnValueChangeListener() {
+            @Override
+            public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
+                updateUnits(unitView, newVal);
+            }
+        });
+        mNumberPickerView.setOnAnnounceValueChangedListener(
+                new NumberPickerCompat.OnAnnounceValueChangedListener() {
+            @Override
+            public void onAnnounceValueChanged(NumberPicker picker, int value,
+                    String displayedValue) {
+                final String announceString;
+                if (value == 0) {
+                    announceString = getString(R.string.no_crescendo_duration);
+                } else {
+                    announceString = getString(R.string.crescendo_duration, displayedValue);
+                }
+                picker.announceForAccessibility(announceString);
+            }
+        });
+    }
+
+    @Override
+    public void onDialogClosed(boolean positiveResult) {
+        if (positiveResult) {
+            final CrescendoLengthDialogPreference preference =
+                    (CrescendoLengthDialogPreference) getPreference();
+            preference.persistCrescendoLength(mNumberPickerView.getValue() * CRESCENDO_TIME_STEP);
+            preference.updateSummary();
+        }
+    }
+
+    private void updateUnits(TextView unitView, int crescendoSeconds) {
+        final int visibility = crescendoSeconds == 0 ? View.INVISIBLE : View.VISIBLE;
+        unitView.setVisibility(visibility);
+    }
+}
diff --git a/src/com/android/deskclock/settings/CrescendoLengthDialogPreference.java b/src/com/android/deskclock/settings/CrescendoLengthDialogPreference.java
new file mode 100644
index 0000000..6fc6d38
--- /dev/null
+++ b/src/com/android/deskclock/settings/CrescendoLengthDialogPreference.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+package com.android.deskclock.settings;
+
+import android.content.Context;
+import android.support.v7.preference.DialogPreference;
+import android.util.AttributeSet;
+
+import com.android.deskclock.R;
+
+public class CrescendoLengthDialogPreference extends DialogPreference {
+
+    private static final String DEFAULT_CRESCENDO_TIME = "0";
+
+    public CrescendoLengthDialogPreference(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public int getPersistedCrescendoLength() {
+        return Integer.parseInt(getPersistedString(DEFAULT_CRESCENDO_TIME));
+    }
+
+    public void persistCrescendoLength(int crescendoSeconds) {
+        persistString(Integer.toString(crescendoSeconds));
+    }
+
+    public void updateSummary() {
+        final int crescendoSeconds = getPersistedCrescendoLength();
+        if (crescendoSeconds == 0) {
+            setSummary(getContext().getString(R.string.no_crescendo_duration));
+        } else {
+            setSummary(getContext().getString(R.string.crescendo_duration, crescendoSeconds));
+        }
+    }
+}
diff --git a/src/com/android/deskclock/settings/SettingsActivity.java b/src/com/android/deskclock/settings/SettingsActivity.java
index c5d2284..52294b3 100644
--- a/src/com/android/deskclock/settings/SettingsActivity.java
+++ b/src/com/android/deskclock/settings/SettingsActivity.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009 The Android Open Source Project
+ * Copyright (C) 2015 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.
@@ -17,17 +17,17 @@
 package com.android.deskclock.settings;
 
 import android.app.Activity;
-import android.content.Context;
+import android.app.DialogFragment;
+import android.app.FragmentTransaction;
 import android.content.Intent;
 import android.content.res.Resources;
-import android.media.AudioManager;
+import android.net.Uri;
 import android.os.Bundle;
-import android.preference.ListPreference;
-import android.preference.Preference;
-import android.preference.PreferenceFragment;
-import android.preference.RingtonePreference;
-import android.preference.SwitchPreference;
 import android.provider.Settings;
+import android.support.v14.preference.PreferenceFragment;
+import android.support.v14.preference.SwitchPreference;
+import android.support.v7.preference.ListPreference;
+import android.support.v7.preference.Preference;
 import android.text.format.DateUtils;
 import android.view.Menu;
 import android.view.MenuItem;
@@ -35,6 +35,7 @@
 import com.android.deskclock.BaseActivity;
 import com.android.deskclock.LogUtils;
 import com.android.deskclock.R;
+import com.android.deskclock.RingtonePickerDialogFragment;
 import com.android.deskclock.Utils;
 import com.android.deskclock.actionbarmenu.ActionBarMenuManager;
 import com.android.deskclock.actionbarmenu.MenuItemControllerFactory;
@@ -44,19 +45,19 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
-import java.util.Locale;
 import java.util.TimeZone;
 
 /**
  * Settings for the Alarm Clock.
  */
-public final class SettingsActivity extends BaseActivity {
+public final class SettingsActivity extends BaseActivity
+        implements RingtonePickerDialogFragment.RingtoneSelectionListener {
 
     public static final String KEY_ALARM_SNOOZE = "snooze_duration";
-    public static final String KEY_ALARM_VOLUME = "volume_setting";
     public static final String KEY_ALARM_CRESCENDO = "alarm_crescendo_duration";
     public static final String KEY_TIMER_CRESCENDO = "timer_crescendo_duration";
     public static final String KEY_TIMER_RINGTONE = "timer_ringtone";
+    public static final String KEY_TIMER_VIBRATE = "timer_vibrate";
     public static final String KEY_AUTO_SILENCE = "auto_silence";
     public static final String KEY_CLOCK_STYLE = "clock_style";
     public static final String KEY_HOME_TZ = "home_time_zone";
@@ -71,16 +72,26 @@
     public static final String VOLUME_BEHAVIOR_SNOOZE = "1";
     public static final String VOLUME_BEHAVIOR_DISMISS = "2";
 
-    private final ActionBarMenuManager mActionBarMenuManager = new ActionBarMenuManager(this);
+    public static final String PREFS_FRAGMENT_TAG = "prefs_fragment";
+    public static final String PREFERENCE_DIALOG_FRAGMENT_TAG = "preference_dialog";
+
+    private final ActionBarMenuManager mActionBarMenuManager = new ActionBarMenuManager();
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-        setVolumeControlStream(AudioManager.STREAM_ALARM);
         setContentView(R.layout.settings);
         mActionBarMenuManager.addMenuItemController(new NavUpMenuItemController(this))
             .addMenuItemController(MenuItemControllerFactory.getInstance()
                     .buildMenuItemControllers(this));
+
+        // Create the prefs fragment in code to ensure it's created before PreferenceDialogFragment
+        if (savedInstanceState == null) {
+            final FragmentTransaction ft = getFragmentManager().beginTransaction();
+            ft.replace(R.id.main, new PrefsFragment(), PREFS_FRAGMENT_TAG);
+            ft.addToBackStack(null);
+            ft.commit();
+        }
     }
 
     @Override
@@ -103,12 +114,23 @@
         return super.onOptionsItemSelected(item);
     }
 
+    /**
+     * Called by the RingtonePickerDialogFragment class after the dialog is finished.
+     */
+    @Override
+    public void onRingtoneSelected(Uri ringtoneUri, String fragmentTag) {
+        final PrefsFragment fragment =
+                (PrefsFragment) getFragmentManager().findFragmentById(R.id.main);
+        final Preference preference = fragment.findPreference(KEY_TIMER_RINGTONE);
+        DataModel.getDataModel().setTimerRingtoneUri(ringtoneUri);
+        preference.setSummary(DataModel.getDataModel().getTimerRingtoneTitle());
+    }
+
     public static class PrefsFragment extends PreferenceFragment
             implements Preference.OnPreferenceChangeListener, Preference.OnPreferenceClickListener {
-        @Override
-        public void onCreate(Bundle savedInstanceState) {
-            super.onCreate(savedInstanceState);
 
+        @Override
+        public void onCreatePreferences(Bundle bundle, String rootKey) {
             addPreferencesFromResource(R.xml.settings);
             loadTimeZoneList();
         }
@@ -130,6 +152,22 @@
         }
 
         @Override
+        public void onDisplayPreferenceDialog(Preference preference) {
+            final String key = preference.getKey();
+            switch (key) {
+                case KEY_ALARM_SNOOZE:
+                    showDialog(SnoozeLengthDialogFragment.newInstance(preference));
+                    break;
+                case KEY_ALARM_CRESCENDO:
+                case KEY_TIMER_CRESCENDO:
+                    showDialog(CrescendoLengthDialogFragment.newInstance(preference));
+                    break;
+                default:
+                    super.onDisplayPreferenceDialog(preference);
+            }
+        }
+
+        @Override
         public boolean onPreferenceChange(Preference pref, Object newValue) {
             final int idx;
             switch (pref.getKey()) {
@@ -159,15 +197,15 @@
                     volumeButtonsPref.setSummary(volumeButtonsPref.getEntries()[index]);
                     break;
                 case KEY_WEEK_START:
-                    final ListPreference weekStartPref = (ListPreference)
-                            findPreference(KEY_WEEK_START);
+                    final ListPreference weekStartPref =
+                            (ListPreference) findPreference(KEY_WEEK_START);
                     idx = weekStartPref.findIndexOfValue((String) newValue);
                     weekStartPref.setSummary(weekStartPref.getEntries()[idx]);
                     break;
-                case KEY_TIMER_RINGTONE:
-                    final RingtonePreference timerRingtonePref = (RingtonePreference)
-                            findPreference(KEY_TIMER_RINGTONE);
-                    timerRingtonePref.setSummary(DataModel.getDataModel().getTimerRingtoneTitle());
+                case KEY_TIMER_VIBRATE:
+                    final SwitchPreference timerVibratePref =
+                            (SwitchPreference) findPreference(KEY_TIMER_VIBRATE);
+                    DataModel.getDataModel().setTimerVibrate(timerVibratePref.isChecked());
                     break;
             }
             // Set result so DeskClock knows to refresh itself
@@ -188,12 +226,14 @@
                     dialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                     startActivity(dialogIntent);
                     return true;
-                case KEY_ALARM_VOLUME:
-                    final AudioManager audioManager =
-                            (AudioManager) activity.getSystemService(Context.AUDIO_SERVICE);
-                    audioManager.adjustStreamVolume(AudioManager.STREAM_ALARM,
-                            AudioManager.ADJUST_SAME, AudioManager.FLAG_SHOW_UI);
-                    return true;
+                case KEY_TIMER_RINGTONE:
+                    final String dialogTitle = getString(R.string.timer_ringtone_title);
+                    final String defaultTitle = getString(R.string.default_timer_ringtone_title);
+                    final Uri currentUri = DataModel.getDataModel().getTimerRingtoneUri();
+                    final Uri defaultUri = DataModel.getDataModel().getDefaultTimerRingtoneUri();
+                    final DialogFragment newFragment = RingtonePickerDialogFragment
+                            .newInstance(dialogTitle, defaultTitle, defaultUri, currentUri, null);
+                    showDialog(newFragment);
                 default:
                     return false;
             }
@@ -271,20 +311,9 @@
             volumeButtonsPref.setSummary(volumeButtonsPref.getEntry());
             volumeButtonsPref.setOnPreferenceChangeListener(this);
 
-            final Preference volumePref = findPreference(KEY_ALARM_VOLUME);
-            volumePref.setOnPreferenceClickListener(this);
-
-            final SnoozeLengthDialog snoozePref =
-                    (SnoozeLengthDialog) findPreference(KEY_ALARM_SNOOZE);
-            snoozePref.setSummary();
-
-            final CrescendoLengthDialog alarmCrescendoPref =
-                    (CrescendoLengthDialog) findPreference(KEY_ALARM_CRESCENDO);
-            alarmCrescendoPref.setSummary();
-
-            final CrescendoLengthDialog timerCrescendoPref =
-                    (CrescendoLengthDialog) findPreference(KEY_TIMER_CRESCENDO);
-            timerCrescendoPref.setSummary();
+            ((SnoozeLengthDialogPreference) findPreference(KEY_ALARM_SNOOZE)).updateSummary();
+            ((CrescendoLengthDialogPreference) findPreference(KEY_ALARM_CRESCENDO)).updateSummary();
+            ((CrescendoLengthDialogPreference) findPreference(KEY_TIMER_CRESCENDO)).updateSummary();
 
             final Preference dateAndTimeSetting = findPreference(KEY_DATE_TIME);
             dateAndTimeSetting.setOnPreferenceClickListener(this);
@@ -298,10 +327,9 @@
             weekStartPref.setSummary(weekStartPref.getEntries()[idx]);
             weekStartPref.setOnPreferenceChangeListener(this);
 
-            final RingtonePreference timerRingtonePref =
-                    (RingtonePreference) findPreference(KEY_TIMER_RINGTONE);
+            final Preference timerRingtonePref = findPreference(KEY_TIMER_RINGTONE);
+            timerRingtonePref.setOnPreferenceClickListener(this);
             timerRingtonePref.setSummary(DataModel.getDataModel().getTimerRingtoneTitle());
-            timerRingtonePref.setOnPreferenceChangeListener(this);
         }
 
         private void updateAutoSnoozeSummary(ListPreference listPref, String delay) {
@@ -314,6 +342,11 @@
             }
         }
 
+        private void showDialog(DialogFragment fragment) {
+            fragment.setTargetFragment(this, 0);
+            fragment.show(getFragmentManager(), PREFERENCE_DIALOG_FRAGMENT_TAG);
+        }
+
         private static class TimeZoneRow implements Comparable<TimeZoneRow> {
 
             private static final boolean SHOW_DAYLIGHT_SAVINGS_INDICATOR = false;
diff --git a/src/com/android/deskclock/settings/SnoozeLengthDialog.java b/src/com/android/deskclock/settings/SnoozeLengthDialog.java
deleted file mode 100644
index a9a3aae..0000000
--- a/src/com/android/deskclock/settings/SnoozeLengthDialog.java
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Copyright (C) 2015 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.
- */
-
-package com.android.deskclock.settings;
-
-import android.app.AlertDialog;
-import android.content.Context;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.os.Parcelable;
-import android.preference.DialogPreference;
-import android.support.annotation.NonNull;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.NumberPicker;
-import android.widget.TextView;
-
-import com.android.deskclock.NumberPickerCompat;
-import com.android.deskclock.R;
-import com.android.deskclock.Utils;
-
-/**
- * A dialog preference that shows a number picker for selecting snooze length
- */
-public final class SnoozeLengthDialog extends DialogPreference {
-
-    private static final String DEFAULT_SNOOZE_TIME = "10";
-
-    private NumberPickerCompat mNumberPickerView;
-    private TextView mNumberPickerMinutesView;
-    private final Context mContext;
-    private int mSnoozeMinutes;
-
-    public SnoozeLengthDialog(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        mContext = context;
-        setDialogLayoutResource(R.layout.snooze_length_picker);
-        setTitle(R.string.snooze_duration_title);
-    }
-
-    @Override
-    protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
-        super.onPrepareDialogBuilder(builder);
-        builder.setTitle(getContext().getString(R.string.snooze_duration_title))
-                .setCancelable(true);
-    }
-
-    @Override
-    protected void onBindDialogView(@NonNull View view) {
-        super.onBindDialogView(view);
-        mNumberPickerMinutesView = (TextView) view.findViewById(R.id.title);
-        mNumberPickerView = (NumberPickerCompat) view.findViewById(R.id.minutes_picker);
-        mNumberPickerView.setMinValue(1);
-        mNumberPickerView.setMaxValue(30);
-        mNumberPickerView.setValue(mSnoozeMinutes);
-        updateUnits();
-
-        mNumberPickerView.setOnAnnounceValueChangedListener(
-                new NumberPickerCompat.OnAnnounceValueChangedListener() {
-            @Override
-            public void onAnnounceValueChanged(NumberPicker picker, int value,
-                    String displayedValue) {
-                final String announceString = Utils.getNumberFormattedQuantityString(
-                        mContext, R.plurals.snooze_duration, value);
-                picker.announceForAccessibility(announceString);
-            }
-        });
-    }
-
-    @Override
-    protected void onSetInitialValue(boolean restorePersistedValue, Object defaultValue) {
-        String val;
-        if (restorePersistedValue) {
-            val = getPersistedString(DEFAULT_SNOOZE_TIME);
-            if (val != null) {
-                mSnoozeMinutes = Integer.parseInt(val);
-            }
-        } else {
-            val = (String) defaultValue;
-            if (val != null) {
-                mSnoozeMinutes = Integer.parseInt(val);
-            }
-            persistString(val);
-        }
-    }
-
-    @Override
-    protected Object onGetDefaultValue(TypedArray a, int index) {
-        return a.getString(index);
-    }
-
-    @Override
-    protected void onRestoreInstanceState(Parcelable state) {
-        // Restore the value to the NumberPicker.
-        super.onRestoreInstanceState(state);
-
-        // Update the unit display in response to the new value.
-        updateUnits();
-    }
-
-    private void updateUnits() {
-        if (mNumberPickerView != null) {
-            final Resources res = mContext.getResources();
-            final int value = mNumberPickerView.getValue();
-            final CharSequence units = res.getQuantityText(R.plurals.snooze_picker_label, value);
-            mNumberPickerMinutesView.setText(units);
-        }
-    }
-
-    @Override
-    protected void onDialogClosed(boolean positiveResult) {
-        if (positiveResult) {
-            mNumberPickerView.clearFocus();
-            mSnoozeMinutes = mNumberPickerView.getValue();
-            persistString(Integer.toString(mSnoozeMinutes));
-            setSummary();
-        }
-    }
-
-    public void setSummary() {
-        setSummary(Utils.getNumberFormattedQuantityString(mContext, R.plurals.snooze_duration,
-                mSnoozeMinutes));
-    }
-}
\ No newline at end of file
diff --git a/src/com/android/deskclock/settings/SnoozeLengthDialogFragment.java b/src/com/android/deskclock/settings/SnoozeLengthDialogFragment.java
new file mode 100644
index 0000000..8e562c0
--- /dev/null
+++ b/src/com/android/deskclock/settings/SnoozeLengthDialogFragment.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+package com.android.deskclock.settings;
+
+import android.app.DialogFragment;
+import android.os.Bundle;
+import android.support.v14.preference.PreferenceDialogFragment;
+import android.support.v7.preference.Preference;
+import android.view.View;
+import android.widget.NumberPicker;
+import android.widget.TextView;
+
+import com.android.deskclock.NumberPickerCompat;
+import com.android.deskclock.R;
+import com.android.deskclock.Utils;
+
+public class SnoozeLengthDialogFragment extends PreferenceDialogFragment {
+
+    private NumberPickerCompat mNumberPickerView;
+
+    public static DialogFragment newInstance(Preference preference) {
+        SnoozeLengthDialogFragment fragment = new SnoozeLengthDialogFragment();
+        Bundle bundle = new Bundle();
+        bundle.putString(ARG_KEY, preference.getKey());
+        fragment.setArguments(bundle);
+        return fragment;
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        getPreference().setDialogLayoutResource(R.layout.snooze_length_picker);
+    }
+
+    @Override
+    protected void onBindDialogView(View view) {
+        final SnoozeLengthDialogPreference preference =
+                (SnoozeLengthDialogPreference) getPreference();
+        final int snoozeMinutes = preference.getPersistedSnoozeLength();
+
+        final CharSequence units =
+                getResources().getQuantityText(R.plurals.snooze_picker_label, snoozeMinutes);
+        final TextView unitView = (TextView) view.findViewById(R.id.title);
+        unitView.setText(units);
+
+        mNumberPickerView = (NumberPickerCompat) view.findViewById(R.id.minutes_picker);
+        mNumberPickerView.setMinValue(1);
+        mNumberPickerView.setMaxValue(30);
+        mNumberPickerView.setValue(snoozeMinutes);
+
+        mNumberPickerView.setOnAnnounceValueChangedListener(
+                new NumberPickerCompat.OnAnnounceValueChangedListener() {
+                    @Override
+                    public void onAnnounceValueChanged(NumberPicker picker, int value,
+                            String displayedValue) {
+                        final String announceString = Utils.getNumberFormattedQuantityString(
+                                getActivity(), R.plurals.snooze_duration, value);
+                        picker.announceForAccessibility(announceString);
+                    }
+                });
+    }
+
+    @Override
+    public void onDialogClosed(boolean positiveResult) {
+        if (positiveResult) {
+            final SnoozeLengthDialogPreference preference =
+                    (SnoozeLengthDialogPreference) getPreference();
+            preference.persistSnoozeLength(mNumberPickerView.getValue());
+            preference.updateSummary();
+        }
+    }
+}
diff --git a/src/com/android/deskclock/settings/SnoozeLengthDialogPreference.java b/src/com/android/deskclock/settings/SnoozeLengthDialogPreference.java
new file mode 100644
index 0000000..0de0fcb
--- /dev/null
+++ b/src/com/android/deskclock/settings/SnoozeLengthDialogPreference.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+package com.android.deskclock.settings;
+
+import android.content.Context;
+import android.support.v7.preference.DialogPreference;
+import android.util.AttributeSet;
+
+import com.android.deskclock.R;
+import com.android.deskclock.Utils;
+
+public class SnoozeLengthDialogPreference extends DialogPreference {
+
+    private static final String DEFAULT_SNOOZE_TIME = "10";
+
+    public SnoozeLengthDialogPreference(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public int getPersistedSnoozeLength() {
+        return Integer.parseInt(getPersistedString(DEFAULT_SNOOZE_TIME));
+    }
+
+    public void persistSnoozeLength(int snoozeMinutes) {
+        persistString(Integer.toString(snoozeMinutes));
+    }
+
+    public void updateSummary() {
+        final int value = getPersistedSnoozeLength();
+        final CharSequence summary = Utils.getNumberFormattedQuantityString(
+                getContext(), R.plurals.snooze_duration, value);
+        setSummary(summary);
+    }
+}
diff --git a/src/com/android/deskclock/settings/TimerRingtonePreference.java b/src/com/android/deskclock/settings/TimerRingtonePreference.java
deleted file mode 100644
index 7a30d92..0000000
--- a/src/com/android/deskclock/settings/TimerRingtonePreference.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2015 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.
- */
-package com.android.deskclock.settings;
-
-import android.content.Context;
-import android.content.Intent;
-import android.media.RingtoneManager;
-import android.preference.RingtonePreference;
-import android.support.annotation.NonNull;
-import android.util.AttributeSet;
-
-import com.android.deskclock.data.DataModel;
-
-/**
- * A custom RingtonePreference that presents the application's default timer ringtone as the value
- * behind the default selection.
- */
-public final class TimerRingtonePreference extends RingtonePreference {
-
-    public TimerRingtonePreference(Context context) {
-        super(context);
-    }
-
-    public TimerRingtonePreference(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    public TimerRingtonePreference(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-    }
-
-    public TimerRingtonePreference(Context context, AttributeSet attrs, int defStyleAttr,
-            int defStyleRes) {
-        super(context, attrs, defStyleAttr, defStyleRes);
-    }
-
-    @Override
-    protected void onPrepareRingtonePickerIntent(@NonNull Intent ringtonePickerIntent) {
-        super.onPrepareRingtonePickerIntent(ringtonePickerIntent);
-
-        // Replace the default ringtone uri with the beeping ringtone for timers.
-        ringtonePickerIntent.putExtra(RingtoneManager.EXTRA_RINGTONE_DEFAULT_URI,
-                DataModel.getDataModel().getDefaultTimerRingtoneUri());
-    }
-}
\ No newline at end of file
diff --git a/src/com/android/deskclock/stopwatch/StopwatchCircleView.java b/src/com/android/deskclock/stopwatch/StopwatchCircleView.java
index bd2e207..f386598 100644
--- a/src/com/android/deskclock/stopwatch/StopwatchCircleView.java
+++ b/src/com/android/deskclock/stopwatch/StopwatchCircleView.java
@@ -80,7 +80,7 @@
         mMarkerStrokeSize = resources.getDimension(R.dimen.circletimer_marker_size);
         mRadiusOffset = Utils.calculateRadiusOffset(mStrokeSize, dotDiameter, mMarkerStrokeSize);
 
-        mRemainderColor = resources.getColor(R.color.clock_white);
+        mRemainderColor = Color.WHITE;
         mCompletedColor = Utils.obtainStyledColor(context, R.attr.colorAccent, Color.RED);
 
         mPaint.setAntiAlias(true);
@@ -173,4 +173,4 @@
     private List<Lap> getLaps() {
         return DataModel.getDataModel().getLaps();
     }
-}
\ No newline at end of file
+}
diff --git a/src/com/android/deskclock/stopwatch/StopwatchFragment.java b/src/com/android/deskclock/stopwatch/StopwatchFragment.java
index 28c8997..15f5891 100644
--- a/src/com/android/deskclock/stopwatch/StopwatchFragment.java
+++ b/src/com/android/deskclock/stopwatch/StopwatchFragment.java
@@ -16,39 +16,46 @@
 
 package com.android.deskclock.stopwatch;
 
+import android.annotation.SuppressLint;
 import android.content.ActivityNotFoundException;
 import android.content.Context;
 import android.content.Intent;
 import android.os.Bundle;
-import android.os.PowerManager;
 import android.os.SystemClock;
+import android.support.annotation.NonNull;
 import android.support.v7.widget.LinearLayoutManager;
 import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.SimpleItemAnimator;
 import android.transition.AutoTransition;
 import android.transition.Transition;
 import android.transition.TransitionManager;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.WindowManager;
 import android.view.accessibility.AccessibilityManager;
+import android.widget.ImageButton;
+import android.widget.ImageView;
 
-import com.android.deskclock.DeskClock;
 import com.android.deskclock.DeskClockFragment;
 import com.android.deskclock.LogUtils;
 import com.android.deskclock.R;
+import com.android.deskclock.Utils;
 import com.android.deskclock.data.DataModel;
 import com.android.deskclock.data.Lap;
 import com.android.deskclock.data.Stopwatch;
 import com.android.deskclock.events.Events;
 import com.android.deskclock.timer.CountingTimerView;
+import com.android.deskclock.uidata.TabListener;
+import com.android.deskclock.uidata.UiDataModel;
+import com.android.deskclock.uidata.UiDataModel.Tab;
 
 import static android.content.Context.ACCESSIBILITY_SERVICE;
-import static android.content.Context.POWER_SERVICE;
-import static android.os.PowerManager.ON_AFTER_RELEASE;
-import static android.os.PowerManager.SCREEN_BRIGHT_WAKE_LOCK;
 import static android.view.View.GONE;
 import static android.view.View.INVISIBLE;
 import static android.view.View.VISIBLE;
+import static com.android.deskclock.FabContainer.UpdateType.FAB_AND_BUTTONS_IMMEDIATE;
+import static com.android.deskclock.uidata.UiDataModel.Tab.STOPWATCH;
 
 /**
  * Fragment that shows the stopwatch and recorded laps.
@@ -57,35 +64,57 @@
 
     private static final String TAG = "StopwatchFragment";
 
-    /** Scheduled to update the stopwatch time and current lap time while stopwatch is running. */
+    /**
+     * Keep the screen on when this tab is selected.
+     */
+    private final TabListener mTabWatcher = new TabWatcher();
+
+    /**
+     * Scheduled to update the stopwatch time and current lap time while stopwatch is running.
+     */
     private final Runnable mTimeUpdateRunnable = new TimeUpdateRunnable();
 
-    /** Used to determine when talk back is on in order to lower the time update rate. */
+    /**
+     * Used to determine when talk back is on in order to lower the time update rate.
+     */
     private AccessibilityManager mAccessibilityManager;
 
-    /** {@code true} while the {@link #mLapsList} is transitioning between shown and hidden. */
+    /**
+     * {@code true} while the {@link #mLapsList} is transitioning between shown and hidden.
+     */
     private boolean mLapsListIsTransitioning;
 
-    /** The data source for {@link #mLapsList}. */
+    /**
+     * The data source for {@link #mLapsList}.
+     */
     private LapsAdapter mLapsAdapter;
 
-    /** The layout manager for the {@link #mLapsAdapter}. */
+    /**
+     * The layout manager for the {@link #mLapsAdapter}.
+     */
     private LinearLayoutManager mLapsLayoutManager;
 
-    /** Draws the reference lap while the stopwatch is running. */
+    /**
+     * Draws the reference lap while the stopwatch is running.
+     */
     private StopwatchCircleView mTime;
 
-    /** Displays the recorded lap times. */
+    /**
+     * Displays the recorded lap times.
+     */
     private RecyclerView mLapsList;
 
-    /** Displays the current stopwatch time. */
+    /**
+     * Displays the current stopwatch time.
+     */
     private CountingTimerView mTimeText;
 
-    /** Held while the stopwatch is running and this fragment is forward to keep the screen on. */
-    private PowerManager.WakeLock mWakeLock;
-
-    /** The public no-arg constructor required by all fragments. */
-    public StopwatchFragment() {}
+    /**
+     * The public no-arg constructor required by all fragments.
+     */
+    public StopwatchFragment() {
+        super(STOPWATCH);
+    }
 
     @Override
     public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle state) {
@@ -95,7 +124,7 @@
         final View v = inflater.inflate(R.layout.stopwatch_fragment, container, false);
         mTime = (StopwatchCircleView) v.findViewById(R.id.stopwatch_time);
         mLapsList = (RecyclerView) v.findViewById(R.id.laps_list);
-        mLapsList.getItemAnimator().setSupportsChangeAnimations(false);
+        ((SimpleItemAnimator) mLapsList.getItemAnimator()).setSupportsChangeAnimations(false);
         mLapsList.setLayoutManager(mLapsLayoutManager);
         mLapsList.setAdapter(mLapsAdapter);
 
@@ -122,10 +151,6 @@
         // Conservatively assume the data in the adapter has changed while the fragment was paused.
         mLapsAdapter.notifyDataSetChanged();
 
-        // Update the state of the buttons.
-        setFabAppearance();
-        setLeftRightButtonAppearance();
-
         // Draw the current stopwatch and lap times.
         updateTime();
 
@@ -145,7 +170,7 @@
         showOrHideLaps(false);
 
         // Start watching for page changes away from this fragment.
-        getDeskClock().registerPageChangedListener(this);
+        UiDataModel.getUiDataModel().addTabListener(mTabWatcher);
 
         // View is hidden in onPause, make sure it is visible now.
         final View view = getView();
@@ -171,28 +196,19 @@
         mTimeText.blinkTimeStr(false);
 
         // Stop watching for page changes away from this fragment.
-        getDeskClock().unregisterPageChangedListener(this);
+        UiDataModel.getUiDataModel().removeTabListener(mTabWatcher);
 
         // Release the wake lock if it is currently held.
         releaseWakeLock();
     }
 
     @Override
-    public void onPageChanged(int page) {
-        if (page == DeskClock.STOPWATCH_TAB_INDEX && getStopwatch().isRunning()) {
-            acquireWakeLock();
-        } else {
-            releaseWakeLock();
-        }
-    }
-
-    @Override
-    public void onFabClick(View view) {
+    public void onFabClick(@NonNull ImageView fab) {
         toggleStopwatchState();
     }
 
     @Override
-    public void onLeftButtonClick(View view) {
+    public void onLeftButtonClick(@NonNull ImageButton left) {
         switch (getStopwatch().getState()) {
             case RUNNING:
                 doAddLap();
@@ -204,55 +220,46 @@
     }
 
     @Override
-    public void onRightButtonClick(View view) {
+    public void onRightButtonClick(@NonNull ImageButton right) {
         doShare();
     }
 
     @Override
-    public void setFabAppearance() {
-        if (mFab == null || getSelectedTab() != DeskClock.STOPWATCH_TAB_INDEX) {
-            return;
-        }
-
+    public void onUpdateFab(@NonNull ImageView fab) {
         if (getStopwatch().isRunning()) {
-            mFab.setImageResource(R.drawable.ic_pause_white_24dp);
-            mFab.setContentDescription(getString(R.string.sw_pause_button));
+            fab.setImageResource(R.drawable.ic_pause_white_24dp);
+            fab.setContentDescription(fab.getResources().getString(R.string.sw_pause_button));
         } else {
-            mFab.setImageResource(R.drawable.ic_start_white_24dp);
-            mFab.setContentDescription(getString(R.string.sw_start_button));
+            fab.setImageResource(R.drawable.ic_start_white_24dp);
+            fab.setContentDescription(fab.getResources().getString(R.string.sw_start_button));
         }
-        mFab.setVisibility(VISIBLE);
+        fab.setVisibility(VISIBLE);
     }
 
     @Override
-    public void setLeftRightButtonAppearance() {
-        if (mLeftButton == null || mRightButton == null ||
-                getSelectedTab() != DeskClock.STOPWATCH_TAB_INDEX) {
-            return;
-        }
-
-        mRightButton.setImageResource(R.drawable.ic_share);
-        mRightButton.setContentDescription(getString(R.string.sw_share_button));
+    public void onUpdateFabButtons(@NonNull ImageButton left, @NonNull ImageButton right) {
+        right.setImageResource(R.drawable.ic_share);
+        right.setContentDescription(right.getResources().getString(R.string.sw_share_button));
 
         switch (getStopwatch().getState()) {
             case RESET:
-                mLeftButton.setEnabled(false);
-                mLeftButton.setVisibility(INVISIBLE);
-                mRightButton.setVisibility(INVISIBLE);
+                left.setEnabled(false);
+                left.setVisibility(INVISIBLE);
+                right.setVisibility(INVISIBLE);
                 break;
             case RUNNING:
-                mLeftButton.setImageResource(R.drawable.ic_lap);
-                mLeftButton.setContentDescription(getString(R.string.sw_lap_button));
-                mLeftButton.setEnabled(canRecordMoreLaps());
-                mLeftButton.setVisibility(canRecordMoreLaps() ? VISIBLE : INVISIBLE);
-                mRightButton.setVisibility(INVISIBLE);
+                left.setImageResource(R.drawable.ic_lap);
+                left.setContentDescription(left.getResources().getString(R.string.sw_lap_button));
+                left.setEnabled(canRecordMoreLaps());
+                left.setVisibility(canRecordMoreLaps() ? VISIBLE : INVISIBLE);
+                right.setVisibility(INVISIBLE);
                 break;
             case PAUSED:
-                mLeftButton.setEnabled(true);
-                mLeftButton.setImageResource(R.drawable.ic_reset);
-                mLeftButton.setContentDescription(getString(R.string.sw_reset_button));
-                mLeftButton.setVisibility(VISIBLE);
-                mRightButton.setVisibility(VISIBLE);
+                left.setEnabled(true);
+                left.setImageResource(R.drawable.ic_reset);
+                left.setContentDescription(left.getResources().getString(R.string.sw_reset_button));
+                left.setVisibility(VISIBLE);
+                right.setVisibility(VISIBLE);
                 break;
         }
     }
@@ -272,8 +279,7 @@
         mTimeText.blinkTimeStr(false);
 
         // Update button states.
-        setFabAppearance();
-        setLeftRightButtonAppearance();
+        updateFab(FAB_AND_BUTTONS_IMMEDIATE);
 
         // Acquire the wake lock.
         acquireWakeLock();
@@ -296,8 +302,7 @@
         mTimeText.blinkTimeStr(true);
 
         // Update button states.
-        setFabAppearance();
-        setLeftRightButtonAppearance();
+        updateFab(FAB_AND_BUTTONS_IMMEDIATE);
 
         // Release the wake lock.
         releaseWakeLock();
@@ -321,8 +326,7 @@
         mTimeText.blinkTimeStr(false);
 
         // Update button states.
-        setFabAppearance();
-        setLeftRightButtonAppearance();
+        updateFab(FAB_AND_BUTTONS_IMMEDIATE);
 
         // Release the wake lock.
         releaseWakeLock();
@@ -333,11 +337,14 @@
      */
     private void doShare() {
         final String[] subjects = getResources().getStringArray(R.array.sw_share_strings);
-        final String subject = subjects[(int)(Math.random() * subjects.length)];
+        final String subject = subjects[(int) (Math.random() * subjects.length)];
         final String text = mLapsAdapter.getShareText();
 
+        @SuppressLint("InlinedApi")
+        @SuppressWarnings("deprecation")
         final Intent shareIntent = new Intent(Intent.ACTION_SEND)
-                .addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET)
+                .addFlags(Utils.isLOrLater() ? Intent.FLAG_ACTIVITY_NEW_DOCUMENT
+                        : Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET)
                 .putExtra(Intent.EXTRA_SUBJECT, subject)
                 .putExtra(Intent.EXTRA_TEXT, text)
                 .setType("text/plain");
@@ -365,7 +372,7 @@
         }
 
         // Update button states.
-        setLeftRightButtonAppearance();
+        updateFab(FAB_AND_BUTTONS_IMMEDIATE);
 
         if (lap.getLapNumber() == 1) {
             // Child views from prior lap sets hang around and blit to the screen when adding the
@@ -425,18 +432,15 @@
     }
 
     private void acquireWakeLock() {
-        if (mWakeLock == null) {
-            final PowerManager pm = (PowerManager) getActivity().getSystemService(POWER_SERVICE);
-            mWakeLock = pm.newWakeLock(SCREEN_BRIGHT_WAKE_LOCK | ON_AFTER_RELEASE, TAG);
-            mWakeLock.setReferenceCounted(false);
+        if (isTabSelected()) {
+            getActivity().getWindow()
+                    .addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
         }
-        mWakeLock.acquire();
     }
 
     private void releaseWakeLock() {
-        if (mWakeLock != null && mWakeLock.isHeld()) {
-            mWakeLock.release();
-        }
+        getActivity().getWindow()
+                .clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
     }
 
     /**
@@ -529,4 +533,19 @@
             toggleStopwatchState();
         }
     }
-}
\ No newline at end of file
+
+    /**
+     * Acquire the wake lock if the stopwatch tab is selected and the stopwatch is running; release
+     * it otherwise.
+     */
+    private final class TabWatcher implements TabListener {
+        @Override
+        public void selectedTabChanged(Tab oldSelectedTab, Tab newSelectedTab) {
+            if (isTabSelected() && getStopwatch().isRunning()) {
+                acquireWakeLock();
+            } else {
+                releaseWakeLock();
+            }
+        }
+    }
+}
diff --git a/src/com/android/deskclock/timer/CountingTimerView.java b/src/com/android/deskclock/timer/CountingTimerView.java
index bb5778c..c221e6a 100644
--- a/src/com/android/deskclock/timer/CountingTimerView.java
+++ b/src/com/android/deskclock/timer/CountingTimerView.java
@@ -264,7 +264,7 @@
         mAccessibilityManager =
                 (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE);
         Resources r = context.getResources();
-        mDefaultColor = mWhiteColor = r.getColor(R.color.clock_white);
+        mDefaultColor = mWhiteColor = Color.WHITE;
         mPressedColor = mAccentColor = Utils.obtainStyledColor(
                 context, R.attr.colorAccent, Color.RED);
         mBigFontSize = r.getDimension(R.dimen.big_font_size);
diff --git a/src/com/android/deskclock/timer/TimerCircleView.java b/src/com/android/deskclock/timer/TimerCircleView.java
index dd2c79f..6756631 100644
--- a/src/com/android/deskclock/timer/TimerCircleView.java
+++ b/src/com/android/deskclock/timer/TimerCircleView.java
@@ -70,7 +70,7 @@
         mStrokeSize = resources.getDimension(R.dimen.circletimer_circle_size);
         mRadiusOffset = Utils.calculateRadiusOffset(mStrokeSize, dotDiameter, 0);
 
-        mRemainderColor = resources.getColor(R.color.clock_white);
+        mRemainderColor = Color.WHITE;
         mCompletedColor = Utils.obtainStyledColor(context, R.attr.colorAccent, Color.RED);
 
         mPaint.setAntiAlias(true);
diff --git a/src/com/android/deskclock/timer/TimerFragment.java b/src/com/android/deskclock/timer/TimerFragment.java
index 3577519..f861034 100644
--- a/src/com/android/deskclock/timer/TimerFragment.java
+++ b/src/com/android/deskclock/timer/TimerFragment.java
@@ -22,9 +22,9 @@
 import android.animation.ObjectAnimator;
 import android.content.Context;
 import android.content.Intent;
-import android.content.res.Resources;
 import android.os.Bundle;
 import android.os.SystemClock;
+import android.support.annotation.NonNull;
 import android.support.annotation.VisibleForTesting;
 import android.support.v4.view.ViewPager;
 import android.view.LayoutInflater;
@@ -43,6 +43,7 @@
 import com.android.deskclock.data.Timer;
 import com.android.deskclock.data.TimerListener;
 import com.android.deskclock.events.Events;
+import com.android.deskclock.uidata.UiDataModel;
 
 import java.io.Serializable;
 import java.util.Arrays;
@@ -50,15 +51,17 @@
 import static android.view.View.ALPHA;
 import static android.view.View.GONE;
 import static android.view.View.INVISIBLE;
-import static android.view.View.OnClickListener;
 import static android.view.View.SCALE_X;
 import static android.view.View.VISIBLE;
-import static com.android.deskclock.AnimatorUtils.getScaleAnimator;
+import static com.android.deskclock.FabContainer.UpdateType.DISABLE_BUTTONS;
+import static com.android.deskclock.FabContainer.UpdateType.FAB_AND_BUTTONS_ANIMATED;
+import static com.android.deskclock.FabContainer.UpdateType.FAB_AND_BUTTONS_IMMEDIATE;
+import static com.android.deskclock.uidata.UiDataModel.Tab.TIMERS;
 
 /**
  * Displays a vertical list of timers in all states.
  */
-public class TimerFragment extends DeskClockFragment {
+public final class TimerFragment extends DeskClockFragment {
 
     private static final String EXTRA_TIMER_SETUP = "com.android.deskclock.action.TIMER_SETUP";
 
@@ -76,27 +79,23 @@
     private TimerSetupView mCreateTimerView;
     private ViewPager mViewPager;
     private TimerPagerAdapter mAdapter;
-    private ImageButton mCancelCreateButton;
     private View mTimersView;
     private View mCurrentView;
     private ImageView[] mPageIndicators;
 
-    private int mShortAnimationDuration;
-    private int mMediumAnimationDuration;
-
     private Serializable mTimerSetupState;
 
     /**
      * @return an Intent that selects the timers tab with the setup screen for a new timer in place.
      */
     public static Intent createTimerSetupIntent(Context context) {
-        return new Intent(context, DeskClock.class)
-                .putExtra(DeskClock.SELECT_TAB_INTENT_EXTRA, DeskClock.TIMER_TAB_INDEX)
-                .putExtra(EXTRA_TIMER_SETUP, true);
+        return new Intent(context, DeskClock.class).putExtra(EXTRA_TIMER_SETUP, true);
     }
 
     /** The public no-arg constructor required by all fragments. */
-    public TimerFragment() {}
+    public TimerFragment() {
+        super(TIMERS);
+    }
 
     @Override
     public View onCreateView(LayoutInflater inflater, ViewGroup container,
@@ -110,20 +109,13 @@
 
         mTimersView = view.findViewById(R.id.timer_view);
         mCreateTimerView = (TimerSetupView) view.findViewById(R.id.timer_setup);
+        mCreateTimerView.setFabContainer(this);
         mPageIndicators = new ImageView[] {
                 (ImageView) view.findViewById(R.id.page_indicator0),
                 (ImageView) view.findViewById(R.id.page_indicator1),
                 (ImageView) view.findViewById(R.id.page_indicator2),
                 (ImageView) view.findViewById(R.id.page_indicator3)
         };
-        mCancelCreateButton = (ImageButton) view.findViewById(R.id.timer_cancel);
-        mCancelCreateButton.setOnClickListener(new CancelCreateListener());
-
-        view.findViewById(R.id.timer_create).setOnClickListener(new CreateListener());
-
-        final Resources resources = getResources();
-        mShortAnimationDuration = resources.getInteger(android.R.integer.config_shortAnimTime);
-        mMediumAnimationDuration = resources.getInteger(android.R.integer.config_mediumAnimTime);
 
         DataModel.getDataModel().addTimerListener(mAdapter);
         DataModel.getDataModel().addTimerListener(mTimerWatcher);
@@ -140,9 +132,6 @@
     public void onResume() {
         super.onResume();
 
-        // Start watching for page changes away from this fragment.
-        getDeskClock().registerPageChangedListener(this);
-
         // Initialize the page indicators.
         updatePageIndicators();
 
@@ -163,11 +152,11 @@
         // Choose the view to display in this fragment.
         if (showTimerId != -1) {
             // A specific timer must be shown; show the list of timers.
-            showTimersView();
+            showTimersView(FAB_AND_BUTTONS_IMMEDIATE);
         } else if (!hasTimers() || createTimer || mTimerSetupState != null) {
             // No timers exist, a timer is being created, or the last view was timer setup;
             // show the timer setup view.
-            showCreateTimerView();
+            showCreateTimerView(FAB_AND_BUTTONS_IMMEDIATE);
 
             if (mTimerSetupState != null) {
                 mCreateTimerView.setState(mTimerSetupState);
@@ -175,7 +164,7 @@
             }
         } else {
             // Otherwise, default to showing the list of timers.
-            showTimersView();
+            showTimersView(FAB_AND_BUTTONS_IMMEDIATE);
         }
 
         // If the intent did not specify a timer to show, show the last timer that expired.
@@ -195,14 +184,6 @@
     }
 
     @Override
-    public void onPause() {
-        super.onPause();
-
-        // Stop watching for page changes away from this fragment.
-        getDeskClock().unregisterPageChangedListener(this);
-    }
-
-    @Override
     public void onStop() {
         super.onStop();
 
@@ -230,101 +211,140 @@
     }
 
     @Override
-    public void setFabAppearance() {
-        if (mFab == null || getSelectedTab() != DeskClock.TIMER_TAB_INDEX) {
-            return;
-        }
-
+    public void onUpdateFab(@NonNull ImageView fab) {
         if (mCurrentView == mTimersView) {
             final Timer timer = getTimer();
             if (timer == null) {
-                mFab.setVisibility(INVISIBLE);
+                fab.setVisibility(INVISIBLE);
                 return;
             }
 
-            mFab.setVisibility(VISIBLE);
+            fab.setVisibility(VISIBLE);
             switch (timer.getState()) {
                 case RUNNING:
-                    mFab.setImageResource(R.drawable.ic_pause_white_24dp);
-                    mFab.setContentDescription(getString(R.string.timer_stop));
+                    fab.setImageResource(R.drawable.ic_pause_white_24dp);
+                    fab.setContentDescription(fab.getResources().getString(R.string.timer_stop));
                     break;
                 case RESET:
                 case PAUSED:
-                    mFab.setImageResource(R.drawable.ic_start_white_24dp);
-                    mFab.setContentDescription(getString(R.string.timer_start));
+                    fab.setImageResource(R.drawable.ic_start_white_24dp);
+                    fab.setContentDescription(fab.getResources().getString(R.string.timer_start));
                     break;
                 case EXPIRED:
-                    mFab.setImageResource(R.drawable.ic_stop_white_24dp);
-                    mFab.setContentDescription(getString(R.string.timer_stop));
+                    fab.setImageResource(R.drawable.ic_stop_white_24dp);
+                    fab.setContentDescription(fab.getResources().getString(R.string.timer_stop));
                     break;
             }
 
         } else if (mCurrentView == mCreateTimerView) {
-            mFab.setVisibility(INVISIBLE);
+            if (mCreateTimerView.hasValidInput()) {
+                fab.setImageResource(R.drawable.ic_start_white_24dp);
+                fab.setContentDescription(fab.getResources().getString(R.string.timer_start));
+                fab.setVisibility(VISIBLE);
+            } else {
+                fab.setVisibility(INVISIBLE);
+            }
         }
     }
 
     @Override
-    public void onFabClick(View view) {
-        final Timer timer = getTimer();
+    public void onUpdateFabButtons(@NonNull ImageButton left, @NonNull ImageButton right) {
+        if (mCurrentView == mTimersView) {
+            left.setEnabled(true);
+            left.setImageResource(R.drawable.ic_delete);
+            left.setContentDescription(left.getResources().getString(R.string.timer_delete));
+            left.setVisibility(mCurrentView != mTimersView ? GONE : VISIBLE);
 
-        // If no timer is currently showing a fab action is meaningless.
-        if (timer == null) {
-            return;
-        }
+            right.setEnabled(true);
+            right.setImageResource(R.drawable.ic_add_timer);
+            right.setContentDescription(right.getResources().getString(R.string.timer_add_timer));
+            right.setVisibility(mCurrentView != mTimersView ? GONE : VISIBLE);
 
-        switch (timer.getState()) {
-            case RUNNING:
-                DataModel.getDataModel().pauseTimer(timer);
-                Events.sendTimerEvent(R.string.action_stop, R.string.label_deskclock);
-                break;
-            case PAUSED:
-            case RESET:
-                DataModel.getDataModel().startTimer(timer);
-                Events.sendTimerEvent(R.string.action_start, R.string.label_deskclock);
-                break;
-            case EXPIRED:
-                DataModel.getDataModel().resetOrDeleteTimer(timer, R.string.label_deskclock);
-                break;
+        } else if (mCurrentView == mCreateTimerView) {
+            left.setEnabled(true);
+            left.setImageResource(R.drawable.ic_cancel);
+            left.setContentDescription(left.getResources().getString(R.string.timer_cancel));
+            // If no timers yet exist, the user is forced to create the first one.
+            left.setVisibility(hasTimers() ? VISIBLE : INVISIBLE);
+
+            right.setVisibility(GONE);
         }
     }
 
     @Override
-    public void setLeftRightButtonAppearance() {
-        if (mLeftButton == null || mRightButton == null ||
-                getSelectedTab() != DeskClock.TIMER_TAB_INDEX) {
-            return;
+    public void onFabClick(@NonNull ImageView fab) {
+        if (mCurrentView == mTimersView) {
+            final Timer timer = getTimer();
+
+            // If no timer is currently showing a fab action is meaningless.
+            if (timer == null) {
+                return;
+            }
+
+            switch (timer.getState()) {
+                case RUNNING:
+                    DataModel.getDataModel().pauseTimer(timer);
+                    Events.sendTimerEvent(R.string.action_stop, R.string.label_deskclock);
+                    break;
+                case PAUSED:
+                case RESET:
+                    DataModel.getDataModel().startTimer(timer);
+                    Events.sendTimerEvent(R.string.action_start, R.string.label_deskclock);
+                    break;
+                case EXPIRED:
+                    DataModel.getDataModel().resetOrDeleteTimer(timer, R.string.label_deskclock);
+                    break;
+            }
+
+        } else if (mCurrentView == mCreateTimerView) {
+            // Create the new timer.
+            final long length = mCreateTimerView.getTimeInMillis();
+            final Timer timer = DataModel.getDataModel().addTimer(length, "", false);
+            Events.sendTimerEvent(R.string.action_create, R.string.label_deskclock);
+
+            // Start the new timer.
+            DataModel.getDataModel().startTimer(timer);
+            Events.sendTimerEvent(R.string.action_start, R.string.label_deskclock);
+
+            // Reset the state of the create view.
+            mCreateTimerView.reset();
+
+            // Display the freshly created timer view.
+            mViewPager.setCurrentItem(0);
+
+            // Return to the list of timers.
+            animateToView(mTimersView, null);
         }
-
-        mLeftButton.setEnabled(true);
-        mLeftButton.setImageResource(R.drawable.ic_delete);
-        mLeftButton.setContentDescription(getString(R.string.timer_delete));
-        mLeftButton.setVisibility(mCurrentView != mTimersView ? GONE : VISIBLE);
-
-        mRightButton.setEnabled(true);
-        mRightButton.setImageResource(R.drawable.ic_add_timer);
-        mRightButton.setContentDescription(getString(R.string.timer_add_timer));
-        mRightButton.setVisibility(mCurrentView != mTimersView ? GONE : VISIBLE);
     }
 
     @Override
-    public void onLeftButtonClick(View view) {
-        final Timer timer = getTimer();
-        if (timer == null) {
-            return;
-        }
+    public void onLeftButtonClick(@NonNull ImageButton left) {
+        if (mCurrentView == mTimersView) {
+            final Timer timer = getTimer();
+            if (timer == null) {
+                return;
+            }
 
-        if (mAdapter.getCount() > 1) {
-            animateTimerRemove(timer);
-        } else {
-            animateToView(mCreateTimerView, timer);
-        }
+            if (mAdapter.getCount() > 1) {
+                animateTimerRemove(timer);
+            } else {
+                animateToView(mCreateTimerView, timer);
+            }
 
-        view.announceForAccessibility(getActivity().getString(R.string.timer_deleted));
+            left.announceForAccessibility(getActivity().getString(R.string.timer_deleted));
+
+        } else if (mCurrentView == mCreateTimerView) {
+            // Clicking the X icon on the timer creation page returns to the timers list.
+            mCreateTimerView.reset();
+
+            animateToView(mTimersView, null);
+
+            left.announceForAccessibility(getActivity().getString(R.string.timer_canceled));
+        }
     }
 
     @Override
-    public void onRightButtonClick(View view) {
+    public void onRightButtonClick(@NonNull ImageButton right) {
         animateToView(mCreateTimerView, null);
     }
 
@@ -409,14 +429,10 @@
     /**
      * Display the view that creates a new timer.
      */
-    private void showCreateTimerView() {
+    private void showCreateTimerView(UpdateType updateType) {
         // Stop animating the timers.
         stopUpdatingTime();
 
-        // If no timers yet exist, the user is forced to create the first one.
-        mCancelCreateButton.setVisibility(hasTimers() ? VISIBLE : INVISIBLE);
-        mCancelCreateButton.setEnabled(true);
-
         // Show the creation view; hide the timer view.
         mTimersView.setVisibility(GONE);
         mCreateTimerView.setVisibility(VISIBLE);
@@ -425,14 +441,13 @@
         mCurrentView = mCreateTimerView;
 
         // Update the fab and buttons.
-        setLeftRightButtonAppearance();
-        setFabAppearance();
+        updateFab(updateType);
     }
 
     /**
      * Display the view that lists all existing timers.
      */
-    private void showTimersView() {
+    private void showTimersView(UpdateType updateType) {
         // Show the timer view; hide the creation view.
         mTimersView.setVisibility(VISIBLE);
         mCreateTimerView.setVisibility(GONE);
@@ -441,8 +456,7 @@
         mCurrentView = mTimersView;
 
         // Update the fab and buttons.
-        setLeftRightButtonAppearance();
-        setFabAppearance();
+        updateFab(updateType);
 
         // Start animating the timers.
         startUpdatingTime();
@@ -452,8 +466,10 @@
      * @param timerToRemove the timer to be removed during the animation
      */
     private void animateTimerRemove(final Timer timerToRemove) {
+        final long duration = UiDataModel.getUiDataModel().getShortAnimationDuration();
+
         final Animator fadeOut = ObjectAnimator.ofFloat(mViewPager, ALPHA, 1, 0);
-        fadeOut.setDuration(mShortAnimationDuration);
+        fadeOut.setDuration(duration);
         fadeOut.setInterpolator(new DecelerateInterpolator());
         fadeOut.addListener(new AnimatorListenerAdapter() {
             @Override
@@ -464,7 +480,7 @@
         });
 
         final Animator fadeIn = ObjectAnimator.ofFloat(mViewPager, ALPHA, 0, 1);
-        fadeIn.setDuration(mShortAnimationDuration);
+        fadeIn.setDuration(duration);
         fadeIn.setInterpolator(new AccelerateInterpolator());
 
         final AnimatorSet animatorSet = new AnimatorSet();
@@ -485,12 +501,11 @@
         final boolean toTimers = toView == mTimersView;
 
         // Avoid double-taps by enabling/disabling the set of buttons active on the new view.
-        mLeftButton.setEnabled(toTimers);
-        mRightButton.setEnabled(toTimers);
-        mCancelCreateButton.setEnabled(!toTimers);
+        updateFab(DISABLE_BUTTONS);
 
+        final long duration = UiDataModel.getUiDataModel().getShortAnimationDuration();
         final Animator rotateFrom = ObjectAnimator.ofFloat(mCurrentView, SCALE_X, 1, 0);
-        rotateFrom.setDuration(mShortAnimationDuration);
+        rotateFrom.setDuration(duration);
         rotateFrom.setInterpolator(new DecelerateInterpolator());
         rotateFrom.addListener(new AnimatorListenerAdapter() {
             @Override
@@ -502,43 +517,19 @@
 
                 mCurrentView.setScaleX(1);
                 if (toTimers) {
-                    showTimersView();
+                    showTimersView(FAB_AND_BUTTONS_ANIMATED);
                 } else {
-                    showCreateTimerView();
+                    showCreateTimerView(FAB_AND_BUTTONS_ANIMATED);
                 }
             }
         });
 
         final Animator rotateTo = ObjectAnimator.ofFloat(toView, SCALE_X, 0, 1);
-        rotateTo.setDuration(mShortAnimationDuration);
+        rotateTo.setDuration(duration);
         rotateTo.setInterpolator(new AccelerateInterpolator());
 
-        final float preScale = toTimers ? 0 : 1;
-        final float postScale = toTimers ? 1 : 0;
-        final Animator fabAnimator = getScaleAnimator(mFab, preScale, postScale);
-        final Animator leftButtonAnimator = getScaleAnimator(mLeftButton, preScale, postScale);
-        final Animator rightButtonAnimator = getScaleAnimator(mRightButton, preScale, postScale);
-
-        final AnimatorSet buttons = new AnimatorSet();
-        buttons.setDuration(toTimers ? mMediumAnimationDuration : mShortAnimationDuration);
-        buttons.play(leftButtonAnimator).with(rightButtonAnimator).with(fabAnimator);
-        buttons.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                mLeftButton.setVisibility(toTimers ? VISIBLE : INVISIBLE);
-                mRightButton.setVisibility(toTimers ? VISIBLE : INVISIBLE);
-
-                mFab.setScaleX(1);
-                mFab.setScaleY(1);
-                mLeftButton.setScaleX(1);
-                mLeftButton.setScaleY(1);
-                mRightButton.setScaleX(1);
-                mRightButton.setScaleY(1);
-            }
-        });
-
         final AnimatorSet animatorSet = new AnimatorSet();
-        animatorSet.play(rotateFrom).before(rotateTo).with(buttons);
+        animatorSet.play(rotateFrom).before(rotateTo);
         animatorSet.start();
     }
 
@@ -551,7 +542,7 @@
             return null;
         }
 
-        return mAdapter.getTimer(mViewPager.getCurrentItem());
+        return mAdapter.getCount() == 0 ? null : mAdapter.getTimer(mViewPager.getCurrentItem());
     }
 
     private void startUpdatingTime() {
@@ -590,7 +581,7 @@
         @Override
         public void onPageSelected(int position) {
             updatePageIndicators();
-            setFabAppearance();
+            updateFab(FAB_AND_BUTTONS_IMMEDIATE);
 
             // Showing a new timer page may introduce a timer requiring continuous updates.
             startUpdatingTime();
@@ -641,9 +632,9 @@
             if (!before.isExpired() && after.isExpired() && index != mViewPager.getCurrentItem()) {
                 mViewPager.setCurrentItem(index, true);
 
-            } else if (index == mViewPager.getCurrentItem()) {
+            } else if (mCurrentView == mTimersView && index == mViewPager.getCurrentItem()) {
                 // If the visible timer changed, update the fab to match its new state.
-                setFabAppearance();
+                updateFab(FAB_AND_BUTTONS_IMMEDIATE);
             }
         }
 
@@ -656,50 +647,7 @@
             }
 
             updatePageIndicators();
-        }
-    }
-
-    /**
-     * Clicking the play icon on the timer creation page creates a new timer and returns to the
-     * timers list.
-     */
-    private class CreateListener implements OnClickListener {
-        @Override
-        public void onClick(View v) {
-            // Create the new timer.
-            final long length = mCreateTimerView.getTimeInMillis();
-            final Timer timer = DataModel.getDataModel().addTimer(length, "", false);
-            Events.sendTimerEvent(R.string.action_create, R.string.label_deskclock);
-
-            // Start the new timer.
-            DataModel.getDataModel().startTimer(timer);
-            Events.sendTimerEvent(R.string.action_start, R.string.label_deskclock);
-
-            // Reset the state of the create view.
-            mCreateTimerView.reset();
-
-            // Display the freshly created timer view.
-            mViewPager.setCurrentItem(0);
-
-            // Return to the list of timers.
-            animateToView(mTimersView, null);
-        }
-    }
-
-    /**
-     * Clicking the X icon on the timer creation page returns to the timers list.
-     */
-    private class CancelCreateListener implements OnClickListener {
-        @Override
-        public void onClick(View view) {
-            // Reset the state of the create view.
-            mCreateTimerView.reset();
-
-            if (hasTimers()) {
-                animateToView(mTimersView, null);
-            }
-
-            view.announceForAccessibility(getActivity().getString(R.string.timer_canceled));
+            updateFab(FAB_AND_BUTTONS_IMMEDIATE);
         }
     }
 }
\ No newline at end of file
diff --git a/src/com/android/deskclock/timer/TimerItem.java b/src/com/android/deskclock/timer/TimerItem.java
index e5a7be1..9c9b12e 100644
--- a/src/com/android/deskclock/timer/TimerItem.java
+++ b/src/com/android/deskclock/timer/TimerItem.java
@@ -31,7 +31,7 @@
 /**
  * This view is a visual representation of a {@link Timer}.
  */
-class TimerItem extends LinearLayout {
+public class TimerItem extends LinearLayout {
 
     /** Displays the remaining time or time since expiration. */
     private CountingTimerView mTimerText;
@@ -125,4 +125,4 @@
             }
         }
     }
-}
\ No newline at end of file
+}
diff --git a/src/com/android/deskclock/timer/TimerKlaxon.java b/src/com/android/deskclock/timer/TimerKlaxon.java
index 979d937..c2f6dc1 100644
--- a/src/com/android/deskclock/timer/TimerKlaxon.java
+++ b/src/com/android/deskclock/timer/TimerKlaxon.java
@@ -16,15 +16,21 @@
 
 package com.android.deskclock.timer;
 
+import android.annotation.TargetApi;
 import android.content.Context;
+import android.media.AudioAttributes;
 import android.net.Uri;
+import android.os.Build;
+import android.os.Vibrator;
 
 import com.android.deskclock.AsyncRingtonePlayer;
 import com.android.deskclock.LogUtils;
+import com.android.deskclock.Utils;
 import com.android.deskclock.data.DataModel;
 import com.android.deskclock.settings.SettingsActivity;
 
 public abstract class TimerKlaxon {
+    private static final long[] VIBRATE_PATTERN = {500, 500};
 
     private static boolean sStarted = false;
     private static AsyncRingtonePlayer sAsyncRingtonePlayer;
@@ -37,6 +43,7 @@
             LogUtils.i("TimerKlaxon.stop()");
             sStarted = false;
             getAsyncRingtonePlayer(context).stop();
+            ((Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE)).cancel();
         }
     }
 
@@ -53,9 +60,30 @@
             final Uri uri = DataModel.getDataModel().getTimerRingtoneUri();
             getAsyncRingtonePlayer(context).play(uri);
         }
+
+        if (DataModel.getDataModel().getTimerVibrate()) {
+            final Vibrator vibrator = getVibrator(context);
+            if (Utils.isLOrLater()) {
+                vibrateLOrLater(vibrator);
+            } else {
+                vibrator.vibrate(VIBRATE_PATTERN, 0);
+            }
+        }
         sStarted = true;
     }
 
+    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+    private static void vibrateLOrLater(Vibrator vibrator) {
+        vibrator.vibrate(VIBRATE_PATTERN, 0, new AudioAttributes.Builder()
+                .setUsage(AudioAttributes.USAGE_ALARM)
+                .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
+                .build());
+    }
+
+    private static Vibrator getVibrator(Context context) {
+        return ((Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE));
+    }
+
     private static synchronized AsyncRingtonePlayer getAsyncRingtonePlayer(Context context) {
         if (sAsyncRingtonePlayer == null) {
             sAsyncRingtonePlayer = new AsyncRingtonePlayer(context.getApplicationContext(),
diff --git a/src/com/android/deskclock/timer/TimerSetupView.java b/src/com/android/deskclock/timer/TimerSetupView.java
index d854379..58f9815 100644
--- a/src/com/android/deskclock/timer/TimerSetupView.java
+++ b/src/com/android/deskclock/timer/TimerSetupView.java
@@ -16,57 +16,43 @@
 
 package com.android.deskclock.timer;
 
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Color;
+import android.support.v4.content.ContextCompat;
 import android.text.format.DateUtils;
 import android.util.AttributeSet;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.widget.Button;
 import android.widget.ImageButton;
-import android.widget.ImageView;
 import android.widget.LinearLayout;
 
-import com.android.deskclock.AnimatorUtils;
+import com.android.deskclock.FabContainer;
 import com.android.deskclock.R;
 import com.android.deskclock.Utils;
 
 import java.io.Serializable;
 import java.util.Arrays;
 
+import static com.android.deskclock.FabContainer.UpdateType.FAB_ONLY_ANIMATED;
+
 public class TimerSetupView extends LinearLayout implements Button.OnClickListener,
         Button.OnLongClickListener {
 
     private final Button[] mNumbers = new Button[10];
-    private final int[] mInput = new int[6];
+    private final int[] mInput = {0, 0, 0, 0, 0, 0};
     private int mInputPointer = -1;
-    private ImageView mCreate;
     private ImageButton mDelete;
     private TimerView mEnteredTime;
     private View mDivider;
 
+    /** Updates to the fab are requested via this container. */
+    private FabContainer mFabContainer;
+
     private final int mColorAccent;
     private final int mColorHairline;
 
-    private final AnimatorListenerAdapter mHideCreateListener = new AnimatorListenerAdapter() {
-        @Override
-        public void onAnimationEnd(Animator animation) {
-            mCreate.setScaleX(1);
-            mCreate.setScaleY(1);
-            mCreate.setVisibility(INVISIBLE);
-        }
-    };
-
-    private final AnimatorListenerAdapter mShowCreateListener = new AnimatorListenerAdapter() {
-        @Override
-        public void onAnimationStart(Animator animation) {
-            mCreate.setVisibility(VISIBLE);
-        }
-    };
-
     public TimerSetupView(Context context) {
         this(context, null /* attrs */);
     }
@@ -75,11 +61,15 @@
         super(context, attrs);
 
         mColorAccent = Utils.obtainStyledColor(context, R.attr.colorAccent, Color.RED);
-        mColorHairline = context.getResources().getColor(R.color.hairline);
+        mColorHairline = ContextCompat.getColor(context, R.color.hairline);
 
         LayoutInflater.from(context).inflate(R.layout.time_setup_view, this);
     }
 
+    void setFabContainer(FabContainer fabContainer) {
+        mFabContainer = fabContainer;
+    }
+
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
@@ -90,7 +80,6 @@
         final View v4 = findViewById(R.id.fourth);
 
         mDivider = findViewById(R.id.divider);
-        mCreate = (ImageView) findViewById(R.id.timer_create);
         mDelete = (ImageButton) findViewById(R.id.delete);
         mDelete.setOnClickListener(this);
         mDelete.setOnLongClickListener(this);
@@ -119,11 +108,12 @@
             mNumbers[i].setTag(R.id.numbers_key, i);
         }
 
-        reset();
+        updateTime();
     }
 
     @Override
     public void onClick(View v) {
+        final boolean validInputBeforeClick = hasValidInput();
         final Integer n = (Integer) v.getTag(R.id.numbers_key);
         // A number was pressed
         if (n != null) {
@@ -167,28 +157,29 @@
             mDelete.setContentDescription(cd);
         }
 
-        updateStartButton();
-        updateDeleteButtonAndDivider();
+        if (validInputBeforeClick != hasValidInput()) {
+            updateFab();
+            updateDeleteButtonAndDivider();
+        }
     }
 
     @Override
     public boolean onLongClick(View v) {
         if (v == mDelete) {
             reset();
-            updateStartButton();
             return true;
         }
         return false;
     }
 
     public void reset() {
-        for (int i = 0; i < mInput.length; i ++) {
-            mInput[i] = 0;
+        if (mInputPointer != -1) {
+            Arrays.fill(mInput, 0);
+            mInputPointer = -1;
+            updateFab();
+            updateTime();
+            updateDeleteButtonAndDivider();
         }
-        mInputPointer = -1;
-        updateTime();
-        updateDeleteButtonAndDivider();
-        mCreate.setVisibility(INVISIBLE);
     }
 
     public long getTimeInMillis() {
@@ -221,38 +212,25 @@
             }
             updateTime();
             updateDeleteButtonAndDivider();
-            mCreate.setVisibility(getInputExists() ? VISIBLE : INVISIBLE);
         }
     }
 
+    protected boolean hasValidInput() {
+        return mInputPointer != -1;
+    }
+
     private void updateTime() {
         final int seconds = mInput[1] * 10 + mInput[0];
         mEnteredTime.setTime(mInput[5], mInput[4], mInput[3], mInput[2], seconds);
     }
 
-    private void updateStartButton() {
-        final boolean show = getInputExists();
-        final int finalVisibility = show ? VISIBLE : INVISIBLE;
-        if (mCreate.getVisibility() == finalVisibility) {
-            // Fab is not initialized yet or already shown/hidden
-            return;
-        }
-
-        final int from = show ? 0 : 1;
-        final int to = show ? 1 : 0;
-        final Animator scaleAnimator = AnimatorUtils.getScaleAnimator(mCreate, from, to);
-        scaleAnimator.setDuration(AnimatorUtils.ANIM_DURATION_SHORT);
-        scaleAnimator.addListener(show ? mShowCreateListener : mHideCreateListener);
-        scaleAnimator.start();
-    }
-
     private void updateDeleteButtonAndDivider() {
-        final boolean enabled = getInputExists();
+        final boolean enabled = hasValidInput();
         mDelete.setEnabled(enabled);
         mDivider.setBackgroundColor(enabled ? mColorAccent : mColorHairline);
     }
 
-    private boolean getInputExists() {
-        return mInputPointer != -1;
+    private void updateFab() {
+        mFabContainer.updateFab(FAB_ONLY_ANIMATED);
     }
-}
\ No newline at end of file
+}
diff --git a/src/com/android/deskclock/timer/TimerView.java b/src/com/android/deskclock/timer/TimerView.java
index 3fde57b..eddd1ae 100644
--- a/src/com/android/deskclock/timer/TimerView.java
+++ b/src/com/android/deskclock/timer/TimerView.java
@@ -17,9 +17,9 @@
 package com.android.deskclock.timer;
 
 import android.content.Context;
-import android.content.res.Resources;
 import android.graphics.Paint;
 import android.graphics.Typeface;
+import android.support.v4.content.ContextCompat;
 import android.util.AttributeSet;
 import android.widget.LinearLayout;
 import android.widget.TextView;
@@ -48,9 +48,8 @@
         mAndroidClockMonoThin =
                 Typeface.createFromAsset(context.getAssets(), "fonts/AndroidClockMono-Thin.ttf");
 
-        final Resources resources = context.getResources();
-        mWhiteColor = resources.getColor(R.color.clock_white);
-        mGrayColor = resources.getColor(R.color.clock_gray);
+        mWhiteColor = ContextCompat.getColor(context, R.color.clock_white);
+        mGrayColor = ContextCompat.getColor(context, R.color.clock_gray);
     }
 
     @Override
diff --git a/src/com/android/deskclock/uidata/TabDAO.java b/src/com/android/deskclock/uidata/TabDAO.java
new file mode 100644
index 0000000..2b8aba8
--- /dev/null
+++ b/src/com/android/deskclock/uidata/TabDAO.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+package com.android.deskclock.uidata;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.preference.PreferenceManager;
+
+import static com.android.deskclock.uidata.UiDataModel.Tab;
+
+/**
+ * This class encapsulates the storage of tab data in {@link SharedPreferences}.
+ */
+final class TabDAO {
+
+    private static final String KEY_SELECTED_TAB = "selected_tab";
+
+    // Lazily instantiated and cached for the life of the application.
+    private static SharedPreferences sPrefs;
+
+    private TabDAO() {}
+
+    /**
+     * @return an enumerated value indicating the currently selected primary tab
+     */
+    static Tab getSelectedTab(Context context) {
+        final SharedPreferences prefs = getSharedPreferences(context);
+        final int selectedTabOrdinal = prefs.getInt(KEY_SELECTED_TAB, Tab.CLOCKS.ordinal());
+        return Tab.values()[selectedTabOrdinal];
+    }
+
+    /**
+     * @param tab an enumerated value indicating the newly selected primary tab
+     */
+    static void setSelectedTab(Context context, Tab tab) {
+        getSharedPreferences(context).edit().putInt(KEY_SELECTED_TAB, tab.ordinal()).apply();
+    }
+
+    private static SharedPreferences getSharedPreferences(Context context) {
+        if (sPrefs == null) {
+            sPrefs = PreferenceManager.getDefaultSharedPreferences(context.getApplicationContext());
+        }
+
+        return sPrefs;
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/deskclock/uidata/TabListener.java b/src/com/android/deskclock/uidata/TabListener.java
new file mode 100644
index 0000000..918b893
--- /dev/null
+++ b/src/com/android/deskclock/uidata/TabListener.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+package com.android.deskclock.uidata;
+
+import com.android.deskclock.uidata.UiDataModel.Tab;
+
+/**
+ * The interface through which interested parties are notified of changes to the selected tab.
+ */
+public interface TabListener {
+
+    /**
+     * @param oldSelectedTab an enumerated value indicating the prior selected tab
+     * @param newSelectedTab an enumerated value indicating the newly selected tab
+     */
+    void selectedTabChanged(Tab oldSelectedTab, Tab newSelectedTab);
+}
\ No newline at end of file
diff --git a/src/com/android/deskclock/uidata/TabModel.java b/src/com/android/deskclock/uidata/TabModel.java
new file mode 100644
index 0000000..cbceda6
--- /dev/null
+++ b/src/com/android/deskclock/uidata/TabModel.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+package com.android.deskclock.uidata;
+
+import android.content.Context;
+import android.text.TextUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+import static android.view.View.LAYOUT_DIRECTION_RTL;
+import static com.android.deskclock.uidata.UiDataModel.Tab;
+
+/**
+ * All tab data is accessed via this model.
+ */
+final class TabModel {
+
+    private final Context mContext;
+
+    /** The listeners to notify when the selected tab is changed. */
+    private final List<TabListener> mTabListeners = new ArrayList<>();
+
+    /** An enumerated value indicating the currently selected tab. */
+    private Tab mSelectedTab;
+
+    TabModel(Context context) {
+        mContext = context;
+    }
+
+    /**
+     * @param tabListener to be notified when the selected tab changes
+     */
+    void addTabListener(TabListener tabListener) {
+        mTabListeners.add(tabListener);
+    }
+
+    /**
+     * @param tabListener to no longer be notified when the selected tab changes
+     */
+    void removeTabListener(TabListener tabListener) {
+        mTabListeners.remove(tabListener);
+    }
+
+    /**
+     * @return the number of tabs
+     */
+    int getTabCount() {
+        return Tab.values().length;
+    }
+
+    /**
+     * @param index the index of the tab
+     * @return the tab at the given {@code index}
+     */
+    Tab getTab(int index) {
+        return Tab.values()[index];
+    }
+
+    /**
+     * @return the index of the currently selected primary tab
+     */
+    int getSelectedTabIndex() {
+        return getSelectedTab().ordinal();
+    }
+
+    /**
+     * @param index the index of the tab to select
+     */
+    void setSelectedTabIndex(int index) {
+        setSelectedTab(Tab.values()[index]);
+    }
+
+    /**
+     * @return an enumerated value indicating the currently selected primary tab
+     */
+    Tab getSelectedTab() {
+        if (mSelectedTab == null) {
+            mSelectedTab = TabDAO.getSelectedTab(mContext);
+        }
+        return mSelectedTab;
+    }
+
+    /**
+     * @param tab an enumerated value indicating the newly selected primary tab
+     */
+    void setSelectedTab(Tab tab) {
+        final Tab oldSelectedTab = getSelectedTab();
+        if (oldSelectedTab != tab) {
+            mSelectedTab = tab;
+            TabDAO.setSelectedTab(mContext, tab);
+
+            for (TabListener tl : mTabListeners) {
+                tl.selectedTabChanged(oldSelectedTab, tab);
+            }
+        }
+    }
+
+    /**
+     * @param ltrTabIndex the tab index assuming left-to-right layout direction
+     * @return the tab index in the current layout direction
+     */
+    int getTabLayoutIndex(int ltrTabIndex) {
+        if (TextUtils.getLayoutDirectionFromLocale(Locale.getDefault()) == LAYOUT_DIRECTION_RTL) {
+            return getTabCount() - ltrTabIndex - 1;
+        }
+        return ltrTabIndex;
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/deskclock/uidata/UiDataModel.java b/src/com/android/deskclock/uidata/UiDataModel.java
new file mode 100644
index 0000000..8f5326d
--- /dev/null
+++ b/src/com/android/deskclock/uidata/UiDataModel.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+package com.android.deskclock.uidata;
+
+import android.content.Context;
+import android.support.annotation.DrawableRes;
+import android.support.annotation.StringRes;
+
+import com.android.deskclock.AlarmClockFragment;
+import com.android.deskclock.ClockFragment;
+import com.android.deskclock.R;
+import com.android.deskclock.stopwatch.StopwatchFragment;
+import com.android.deskclock.timer.TimerFragment;
+
+import static com.android.deskclock.Utils.enforceMainLooper;
+
+/**
+ * All application-wide user interface data is accessible through this singleton.
+ */
+public final class UiDataModel {
+
+    /** Identifies each of the primary tabs within the application. */
+    public enum Tab {
+        ALARMS(AlarmClockFragment.class, R.drawable.ic_tab_alarm, R.string.menu_alarm),
+        CLOCKS(ClockFragment.class, R.drawable.ic_tab_clock, R.string.menu_clock),
+        TIMERS(TimerFragment.class, R.drawable.ic_tab_timer, R.string.menu_timer),
+        STOPWATCH(StopwatchFragment.class, R.drawable.ic_tab_stopwatch, R.string.menu_stopwatch);
+
+        private final String mFragmentClassName;
+        private final @DrawableRes int mIconId;
+        private final @StringRes int mContentDescriptionId;
+
+        Tab(Class fragmentClass, @DrawableRes int iconId, @StringRes int contentDescriptionId) {
+            mFragmentClassName = fragmentClass.getName();
+            mIconId = iconId;
+            mContentDescriptionId = contentDescriptionId;
+        }
+
+        public String getFragmentClassName() { return mFragmentClassName; }
+        public int getIconId() { return mIconId; }
+        public int getContentDescriptionId() { return mContentDescriptionId; }
+    }
+
+    /** The single instance of this data model that exists for the life of the application. */
+    private static final UiDataModel sUiDataModel = new UiDataModel();
+
+    public static UiDataModel getUiDataModel() {
+        return sUiDataModel;
+    }
+
+    private Context mContext;
+
+    /** The model from which tab data are fetched. */
+    private TabModel mTabModel;
+
+    private UiDataModel() {}
+
+    /**
+     * The context may be set precisely once during the application life.
+     */
+    public void setContext(Context context) {
+        if (mContext != null) {
+            throw new IllegalStateException("context has already been set");
+        }
+        mContext = context.getApplicationContext();
+
+        mTabModel = new TabModel(mContext);
+    }
+
+    //
+    // Animations
+    //
+
+    /**
+     * @return the duration in milliseconds of short animations
+     */
+    public long getShortAnimationDuration() {
+        enforceMainLooper();
+        return mContext.getResources().getInteger(android.R.integer.config_shortAnimTime);
+    }
+
+    //
+    // Tabs
+    //
+
+    /**
+     * @param tabListener to be notified when the selected tab changes
+     */
+    public void addTabListener(TabListener tabListener) {
+        enforceMainLooper();
+        mTabModel.addTabListener(tabListener);
+    }
+
+    /**
+     * @param tabListener to no longer be notified when the selected tab changes
+     */
+    public void removeTabListener(TabListener tabListener) {
+        enforceMainLooper();
+        mTabModel.removeTabListener(tabListener);
+    }
+
+    /**
+     * @return the number of tabs
+     */
+    public int getTabCount() {
+        enforceMainLooper();
+        return mTabModel.getTabCount();
+    }
+
+    /**
+     * @param index the index of the tab
+     * @return the tab at the given {@code index}
+     */
+    public Tab getTab(int index) {
+        enforceMainLooper();
+        return mTabModel.getTab(index);
+    }
+
+    /**
+     * @return the index of the currently selected primary tab
+     */
+    public int getSelectedTabIndex() {
+        enforceMainLooper();
+        return mTabModel.getSelectedTabIndex();
+    }
+
+    /**
+     * @param index the index of the tab to select
+     */
+    public void setSelectedTabIndex(int index) {
+        enforceMainLooper();
+        mTabModel.setSelectedTabIndex(index);
+    }
+
+    /**
+     * @return an enumerated value indicating the currently selected primary tab
+     */
+    public Tab getSelectedTab() {
+        enforceMainLooper();
+        return mTabModel.getSelectedTab();
+    }
+
+    /**
+     * @param tab an enumerated value indicating the newly selected primary tab
+     */
+    public void setSelectedTab(Tab tab) {
+        enforceMainLooper();
+        mTabModel.setSelectedTab(tab);
+    }
+
+    /**
+     * This method converts the given {@code ltrTabIndex} which assumes Left-To-Right layout of the
+     * tabs into an index that respects the system layout, which may be Left-To-Right or
+     * Right-To-Left.
+     *
+     * @param ltrTabIndex the tab index assuming left-to-right layout direction
+     * @return the tab index in the current layout direction
+     */
+    public int getTabLayoutIndex(int ltrTabIndex) {
+        enforceMainLooper();
+        return mTabModel.getTabLayoutIndex(ltrTabIndex);
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/deskclock/widget/RtlViewPager.java b/src/com/android/deskclock/widget/RtlViewPager.java
index fc2f43e..4af461a 100644
--- a/src/com/android/deskclock/widget/RtlViewPager.java
+++ b/src/com/android/deskclock/widget/RtlViewPager.java
@@ -18,11 +18,9 @@
 
 import android.content.Context;
 import android.support.v4.view.ViewPager;
-import android.text.TextUtils;
 import android.util.AttributeSet;
-import android.view.View;
 
-import java.util.Locale;
+import com.android.deskclock.uidata.UiDataModel;
 
 /**
  * A {@link ViewPager} that's aware of RTL changes when used with FragmentPagerAdapter.
@@ -46,54 +44,46 @@
         addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
             @Override
             public void onPageScrolled(int position, float offset, int offsetPixels) {
-                // Do nothing
+                if (mListener != null) {
+                    position = UiDataModel.getUiDataModel().getTabLayoutIndex(position);
+                    mListener.onPageScrolled(position, offset, offsetPixels);
+                }
             }
 
             @Override
             public void onPageSelected(int position) {
                 if (mListener != null) {
-                    mListener.onPageSelected(getRtlAwareIndex(position));
+                    position = UiDataModel.getUiDataModel().getTabLayoutIndex(position);
+                    mListener.onPageSelected(position);
                 }
             }
 
             @Override
             public void onPageScrollStateChanged(int state) {
-                // Do nothing
+                if (mListener != null) {
+                    mListener.onPageScrollStateChanged(state);
+                }
             }
         });
     }
 
     @Override
     public int getCurrentItem() {
-        return getRtlAwareIndex(super.getCurrentItem());
+        return UiDataModel.getUiDataModel().getTabLayoutIndex(super.getCurrentItem());
     }
 
     @Override
     public void setCurrentItem(int item) {
-        super.setCurrentItem(getRtlAwareIndex(item));
+        super.setCurrentItem(UiDataModel.getUiDataModel().getTabLayoutIndex(item));
     }
 
     @Override
+    @SuppressWarnings("deprecation")
     public void setOnPageChangeListener(OnPageChangeListener unused) {
         throw new UnsupportedOperationException("Use setOnRTLPageChangeListener instead");
     }
 
     /**
-     * Get a "RTL friendly" index. If the locale is LTR, the index is returned as is.
-     * Otherwise it's transformed so view pager can render views using the new index for RTL. For
-     * example, the second view will be rendered to the left of first view.
-     *
-     * @param index The logical index.
-     */
-    public int getRtlAwareIndex(int index) {
-        if (TextUtils.getLayoutDirectionFromLocale(Locale.getDefault()) ==
-                View.LAYOUT_DIRECTION_RTL) {
-            return getAdapter().getCount() - index - 1;
-        }
-        return index;
-    }
-
-    /**
      * Sets a {@link OnPageChangeListener}. The listener will be called when a page is selected.
      */
     public void setOnRTLPageChangeListener(OnPageChangeListener listener) {
diff --git a/src/com/android/deskclock/widget/TextTime.java b/src/com/android/deskclock/widget/TextTime.java
index ff90a55..b1e23b5 100644
--- a/src/com/android/deskclock/widget/TextTime.java
+++ b/src/com/android/deskclock/widget/TextTime.java
@@ -141,7 +141,7 @@
     }
 
     public void setFormat(Context context) {
-        setFormat12Hour(Utils.get12ModeFormat(context, true /* showAmPm */));
+        setFormat12Hour(Utils.get12ModeFormat(context, 0.22f /* amPmRatio */));
         setFormat24Hour(Utils.get24ModeFormat());
     }
 
diff --git a/src/com/android/deskclock/worldclock/CitySelectionActivity.java b/src/com/android/deskclock/worldclock/CitySelectionActivity.java
index d38ac71..580eaed 100644
--- a/src/com/android/deskclock/worldclock/CitySelectionActivity.java
+++ b/src/com/android/deskclock/worldclock/CitySelectionActivity.java
@@ -17,7 +17,6 @@
 package com.android.deskclock.worldclock;
 
 import android.content.Context;
-import android.media.AudioManager;
 import android.os.Bundle;
 import android.support.v7.widget.SearchView;
 import android.text.TextUtils;
@@ -80,7 +79,7 @@
     private CityAdapter mCitiesAdapter;
 
     /** Manages all action bar menu display and click handling. */
-    private final ActionBarMenuManager mActionBarMenuManager = new ActionBarMenuManager(this);
+    private final ActionBarMenuManager mActionBarMenuManager = new ActionBarMenuManager();
 
     /** Menu item controller for search view. */
     private SearchMenuItemController mSearchMenuItemController;
@@ -88,7 +87,6 @@
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-        setVolumeControlStream(AudioManager.STREAM_ALARM);
 
         setContentView(R.layout.cities_activity);
         mSearchMenuItemController =
@@ -467,7 +465,7 @@
          */
         private void filter(String queryText) {
             mSearchMenuItemController.setQueryText(queryText);
-            final String query = queryText.trim().toUpperCase();
+            final String query = City.removeSpecialCharacters(queryText.toUpperCase());
 
             // Compute the filtered list of cities.
             final List<City> filteredCities;
@@ -477,7 +475,7 @@
                 final List<City> unselected = DataModel.getDataModel().getUnselectedCities();
                 filteredCities = new ArrayList<>(unselected.size());
                 for (City city : unselected) {
-                    if (city.getNameUpperCase().startsWith(query)) {
+                    if (city.matches(query)) {
                         filteredCities.add(city);
                     }
                 }