Merge "Introduce LogdSizePreferenceControllerV2"
diff --git a/res/layout-land/fingerprint_enroll_enrolling.xml b/res/layout-land/fingerprint_enroll_enrolling.xml
index 9890e68..fb6c78b 100644
--- a/res/layout-land/fingerprint_enroll_enrolling.xml
+++ b/res/layout-land/fingerprint_enroll_enrolling.xml
@@ -109,8 +109,8 @@
                 android:orientation="vertical">
 
                 <com.android.setupwizardlib.view.FillContentLayout
-                    android:layout_width="@dimen/fingerprint_progress_bar_max_size"
-                    android:layout_height="@dimen/fingerprint_progress_bar_max_size"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
                     android:paddingTop="0dp"
                     android:paddingBottom="0dp"
                     android:layout_marginVertical="24dp">
diff --git a/res/layout/android_beam.xml b/res/layout/android_beam.xml
index 5683840..7780b31 100644
--- a/res/layout/android_beam.xml
+++ b/res/layout/android_beam.xml
@@ -31,14 +31,15 @@
             android:layout_height="wrap_content"
             android:orientation="vertical">
 
-            <TextView android:id="@+id/android_beam_explained"
+            <TextView
+                android:id="@+id/android_beam_explained"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:layout_marginTop="20dip"
                 android:gravity="top"
+                android:paddingStart="@dimen/preference_no_icon_padding_start"
                 android:text="@string/android_beam_explained"
-                android:textAppearance="?android:attr/textAppearanceMedium"
-            />
+                android:textAppearance="?android:attr/textAppearanceMedium" />
             <ImageView android:id="@+id/android_beam_image"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
diff --git a/res/layout/master_clear.xml b/res/layout/master_clear.xml
index ec2aaea..779e504 100644
--- a/res/layout/master_clear.xml
+++ b/res/layout/master_clear.xml
@@ -4,9 +4,9 @@
      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.
@@ -14,24 +14,24 @@
      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">
 
     <ScrollView
             android:id="@+id/master_clear_scrollview"
             android:layout_width="match_parent"
             android:layout_height="0dip"
-            android:layout_marginStart="12dp"
+            android:layout_marginStart="@dimen/preference_no_icon_padding_start"
             android:layout_marginEnd="12dp"
             android:layout_marginTop="12dp"
             android:layout_weight="1">
-        <LinearLayout android:id="@+id/master_clear_container"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:orientation="vertical">
+        <LinearLayout
+            android:id="@+id/master_clear_container"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="vertical">
             <TextView
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
diff --git a/res/layout/reset_network.xml b/res/layout/reset_network.xml
index 82769db..be966dd 100644
--- a/res/layout/reset_network.xml
+++ b/res/layout/reset_network.xml
@@ -22,7 +22,7 @@
     <ScrollView
         android:layout_width="match_parent"
         android:layout_height="0dip"
-        android:layout_marginStart="12dp"
+        android:layout_marginStart="@dimen/preference_no_icon_padding_start"
         android:layout_marginEnd="12dp"
         android:layout_marginTop="12dp"
         android:layout_weight="1">
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 567f255..3cebd3c 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -2251,9 +2251,6 @@
     <string name="summary_placeholder" translatable="false">&#160;</string>
     <!-- DO NOT TRANSLATE Summary placeholder reserving 2 lines -->
     <string name="summary_two_lines_placeholder" translatable="false">&#160;\n&#160;</string>
-
-    <!-- DO NOT TRANSLATE Empty summary for dynamic preferences -->
-    <string name="summary_empty" translatable="false"></string>
     <!-- Sound settings screen, volume title -->
     <string name="all_volume_title">Volumes</string>
     <!-- Sound settings screen, music effects title [CHAR LIMIT=30]-->
diff --git a/res/xml/special_access.xml b/res/xml/special_access.xml
index 6eaede9..f0db8e7 100644
--- a/res/xml/special_access.xml
+++ b/res/xml/special_access.xml
@@ -35,11 +35,6 @@
         android:fragment="com.android.settings.DeviceAdminSettings" />
 
     <Preference
-        android:key="zen_access"
-        android:title="@string/manage_zen_access_title"
-        android:fragment="com.android.settings.notification.ZenAccessSettings" />
-
-    <Preference
         android:key="system_alert_window"
         android:title="@string/system_alert_window_settings"
         android:fragment="com.android.settings.applications.ManageApplications"
@@ -50,14 +45,9 @@
     </Preference>
 
     <Preference
-        android:key="enabled_vr_listeners"
-        android:title="@string/vr_listeners_title"
-        android:fragment="com.android.settings.applications.VrListenerSettings"
-        settings:keywords="@string/keywords_vr_listener">
-        <extra
-            android:name="classname"
-            android:value="com.android.settings.Settings$VrListenersSettingsActivity" />
-    </Preference>
+        android:key="zen_access"
+        android:title="@string/manage_zen_access_title"
+        android:fragment="com.android.settings.notification.ZenAccessSettings" />
 
     <Preference
         android:key="write_settings_apps"
@@ -91,6 +81,16 @@
         android:fragment="com.android.settings.datausage.UnrestrictedDataAccess" />
 
     <Preference
+        android:key="manage_external_sources"
+        android:title="@string/install_other_apps"
+        android:fragment="com.android.settings.applications.ManageApplications"
+        settings:keywords="@string/keywords_install_other_apps">
+        <extra
+            android:name="classname"
+            android:value="com.android.settings.Settings$ManageExternalSourcesActivity" />
+    </Preference>
+
+    <Preference
         android:key="usage_access"
         android:title="@string/usage_access"
         android:fragment="com.android.settings.applications.ManageApplications"
@@ -101,13 +101,13 @@
     </Preference>
 
     <Preference
-        android:key="manage_external_sources"
-        android:title="@string/install_other_apps"
-        android:fragment="com.android.settings.applications.ManageApplications"
-        settings:keywords="@string/keywords_install_other_apps">
+        android:key="enabled_vr_listeners"
+        android:title="@string/vr_listeners_title"
+        android:fragment="com.android.settings.applications.VrListenerSettings"
+        settings:keywords="@string/keywords_vr_listener">
         <extra
             android:name="classname"
-            android:value="com.android.settings.Settings$ManageExternalSourcesActivity" />
+            android:value="com.android.settings.Settings$VrListenersSettingsActivity" />
     </Preference>
 
 </PreferenceScreen>
diff --git a/src/com/android/settings/Utils.java b/src/com/android/settings/Utils.java
index 3b2ea1a..8a832a9 100644
--- a/src/com/android/settings/Utils.java
+++ b/src/com/android/settings/Utils.java
@@ -50,6 +50,9 @@
 import android.database.Cursor;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
 import android.hardware.fingerprint.FingerprintManager;
 import android.icu.text.MeasureFormat;
 import android.icu.text.RelativeDateTimeFormatter;
@@ -1366,4 +1369,50 @@
     public static void setEditTextCursorPosition(EditText editText) {
         editText.setSelection(editText.getText().length());
     }
+
+    /**
+     * Sets the preference icon with a drawable that is scaled down to to avoid crashing Settings if
+     * it's too big.
+     */
+    public static void setSafeIcon(Preference pref, Drawable icon) {
+        Drawable safeIcon = icon;
+        if (icon != null) {
+            safeIcon = getSafeDrawable(icon, 500, 500);
+        }
+        pref.setIcon(safeIcon);
+    }
+
+    /**
+     * Gets a drawable with a limited size to avoid crashing Settings if it's too big.
+     *
+     * @param original original drawable, typically an app icon.
+     * @param maxWidth maximum width, in pixels.
+     * @param maxHeight maximum height, in pixels.
+     */
+    public static Drawable getSafeDrawable(Drawable original, int maxWidth, int maxHeight) {
+        final int actualWidth = original.getMinimumWidth();
+        final int actualHeight = original.getMinimumHeight();
+
+        if (actualWidth <= maxWidth && actualHeight <= maxHeight) {
+            return original;
+        }
+
+        float scaleWidth = ((float) maxWidth) / actualWidth;
+        float scaleHeight = ((float) maxHeight) / actualHeight;
+        float scale = Math.min(scaleWidth, scaleHeight);
+        final int width = (int) (actualWidth * scale);
+        final int height = (int) (actualHeight * scale);
+
+        final Bitmap bitmap;
+        if (original instanceof BitmapDrawable) {
+            bitmap = Bitmap.createScaledBitmap(((BitmapDrawable) original).getBitmap(), width,
+                    height, false);
+        } else {
+            bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+            final Canvas canvas = new Canvas(bitmap);
+            original.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
+            original.draw(canvas);
+        }
+        return new BitmapDrawable(null, bitmap);
+    }
 }
diff --git a/src/com/android/settings/accessibility/AccessibilitySettings.java b/src/com/android/settings/accessibility/AccessibilitySettings.java
index a335c83..fd48c39 100644
--- a/src/com/android/settings/accessibility/AccessibilitySettings.java
+++ b/src/com/android/settings/accessibility/AccessibilitySettings.java
@@ -485,7 +485,7 @@
             preference.setKey(componentName.flattenToString());
 
             preference.setTitle(title);
-            preference.setIcon(icon);
+            Utils.setSafeIcon(preference, icon);
             final boolean serviceEnabled = accessibilityEnabled
                     && enabledServices.contains(componentName);
             final String serviceState = serviceEnabled ?
diff --git a/src/com/android/settings/applications/ManageApplications.java b/src/com/android/settings/applications/ManageApplications.java
index 9edbd86..7ec40f8 100644
--- a/src/com/android/settings/applications/ManageApplications.java
+++ b/src/com/android/settings/applications/ManageApplications.java
@@ -651,7 +651,11 @@
 
     @Override
     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
-        HelpUtils.prepareHelpMenuItem(getActivity(), menu, getHelpResource(), getClass().getName());
+        final Activity activity = getActivity();
+        if (activity == null) {
+            return;
+        }
+        HelpUtils.prepareHelpMenuItem(activity, menu, getHelpResource(), getClass().getName());
         mOptionsMenu = menu;
         inflater.inflate(R.menu.manage_apps, menu);
         updateOptionsMenu();
diff --git a/src/com/android/settings/applications/defaultapps/DefaultAppPreferenceController.java b/src/com/android/settings/applications/defaultapps/DefaultAppPreferenceController.java
index d533d0f..91143b1 100644
--- a/src/com/android/settings/applications/defaultapps/DefaultAppPreferenceController.java
+++ b/src/com/android/settings/applications/defaultapps/DefaultAppPreferenceController.java
@@ -26,6 +26,7 @@
 import android.util.Log;
 
 import com.android.settings.R;
+import com.android.settings.Utils;
 import com.android.settings.core.PreferenceControllerMixin;
 import com.android.settings.widget.GearPreference;
 import com.android.settingslib.core.AbstractPreferenceController;
@@ -54,7 +55,7 @@
         CharSequence defaultAppLabel = getDefaultAppLabel();
         if (!TextUtils.isEmpty(defaultAppLabel)) {
             preference.setSummary(defaultAppLabel);
-            preference.setIcon(getDefaultAppIcon());
+            Utils.setSafeIcon(preference, getDefaultAppIcon());
         } else {
             Log.d(TAG, "No default app");
             preference.setSummary(R.string.app_list_preference_none);
diff --git a/src/com/android/settings/development/WebViewAppPreferenceControllerV2.java b/src/com/android/settings/development/WebViewAppPreferenceControllerV2.java
index 8217d74..87ddb81 100644
--- a/src/com/android/settings/development/WebViewAppPreferenceControllerV2.java
+++ b/src/com/android/settings/development/WebViewAppPreferenceControllerV2.java
@@ -18,7 +18,6 @@
 
 import android.content.Context;
 import android.content.pm.PackageInfo;
-import android.graphics.drawable.Drawable;
 import android.support.annotation.VisibleForTesting;
 import android.support.v7.preference.Preference;
 import android.support.v7.preference.PreferenceScreen;
@@ -67,11 +66,9 @@
         final CharSequence defaultAppLabel = getDefaultAppLabel();
         if (!TextUtils.isEmpty(defaultAppLabel)) {
             mPreference.setSummary(defaultAppLabel);
-            mPreference.setIcon(getDefaultAppIcon());
         } else {
             Log.d(TAG, "No default app");
             mPreference.setSummary(R.string.app_list_preference_none);
-            mPreference.setIcon(null);
         }
     }
 
@@ -92,11 +89,6 @@
                 currentPackage == null ? null : currentPackage.applicationInfo);
     }
 
-    private Drawable getDefaultAppIcon() {
-        final DefaultAppInfo app = getDefaultAppInfo();
-        return app.loadIcon();
-    }
-
     private CharSequence getDefaultAppLabel() {
         final DefaultAppInfo app = getDefaultAppInfo();
         return app.loadLabel();
diff --git a/src/com/android/settings/notification/ZenModeRuleSettingsBase.java b/src/com/android/settings/notification/ZenModeRuleSettingsBase.java
index 4abdd7e..86576cf 100644
--- a/src/com/android/settings/notification/ZenModeRuleSettingsBase.java
+++ b/src/com/android/settings/notification/ZenModeRuleSettingsBase.java
@@ -85,6 +85,12 @@
         }
 
         mId = intent.getStringExtra(ConditionProviderService.EXTRA_RULE_ID);
+        if (mId == null) {
+            Log.w(TAG, "rule id is null");
+            toastAndFinish();
+            return;
+        }
+
         if (DEBUG) Log.d(TAG, "mId=" + mId);
         if (refreshRuleOrFinish()) {
             return;
diff --git a/src/com/android/settings/widget/RadioButtonPickerFragment.java b/src/com/android/settings/widget/RadioButtonPickerFragment.java
index 6854793..d736319 100644
--- a/src/com/android/settings/widget/RadioButtonPickerFragment.java
+++ b/src/com/android/settings/widget/RadioButtonPickerFragment.java
@@ -17,6 +17,9 @@
 package com.android.settings.widget;
 
 import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 import android.os.UserHandle;
@@ -154,7 +157,7 @@
     public RadioButtonPreference bindPreference(RadioButtonPreference pref,
             String key, CandidateInfo info, String defaultKey) {
         pref.setTitle(info.loadLabel());
-        pref.setIcon(info.loadIcon());
+        Utils.setSafeIcon(pref, info.loadIcon());
         pref.setKey(key);
         if (TextUtils.equals(defaultKey, key)) {
             pref.setChecked(true);
diff --git a/tests/robotests/src/com/android/settings/development/WebViewAppPreferenceControllerV2Test.java b/tests/robotests/src/com/android/settings/development/WebViewAppPreferenceControllerV2Test.java
index faeda87..4f9540f 100644
--- a/tests/robotests/src/com/android/settings/development/WebViewAppPreferenceControllerV2Test.java
+++ b/tests/robotests/src/com/android/settings/development/WebViewAppPreferenceControllerV2Test.java
@@ -22,7 +22,6 @@
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
-import android.graphics.drawable.Drawable;
 import android.support.v7.preference.Preference;
 import android.support.v7.preference.PreferenceScreen;
 
@@ -56,8 +55,6 @@
     private Preference mPreference;
     @Mock
     private DefaultAppInfo mAppInfo;
-    @Mock
-    private Drawable mDrawable;
 
     private Context mContext;
     private WebViewAppPreferenceControllerV2 mController;
@@ -80,24 +77,20 @@
     public void updateState_hasAppLabel_shouldSetAppLabelAndIcon() {
         final String appLabel = "SomeRandomAppLabel!!!";
         when(mAppInfo.loadLabel()).thenReturn(appLabel);
-        when(mAppInfo.loadIcon()).thenReturn(mDrawable);
 
         mController.updateState(mPreference);
 
         verify(mPreference).setSummary(appLabel);
-        verify(mPreference).setIcon(mDrawable);
     }
 
     @Test
     public void updateState_noAppLabel_shouldSetAppDefaultLabelAndNullIcon() {
         final String appLabel = null;
         when(mAppInfo.loadLabel()).thenReturn(appLabel);
-        when(mAppInfo.loadIcon()).thenReturn(mDrawable);
 
         mController.updateState(mPreference);
 
         verify(mPreference).setSummary(R.string.app_list_preference_none);
-        verify(mPreference).setIcon(null);
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/notification/ZenModeScheduleRuleSettingsTest.java b/tests/robotests/src/com/android/settings/notification/ZenModeScheduleRuleSettingsTest.java
new file mode 100644
index 0000000..f8e5775
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/ZenModeScheduleRuleSettingsTest.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2017 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.settings.notification;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.Intent;
+import android.os.UserManager;
+
+import com.android.settings.R;
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settings.testutils.shadow.SettingsShadowResources;
+import com.android.settings.testutils.shadow.ShadowUserManager;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowToast;
+import org.robolectric.RuntimeEnvironment;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.robolectric.RuntimeEnvironment.application;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH,
+        sdk = TestConfig.SDK_VERSION,
+        shadows = {
+                SettingsShadowResources.class,
+                SettingsShadowResources.SettingsShadowTheme.class,
+        })
+public class ZenModeScheduleRuleSettingsTest {
+
+    @Mock
+    private Activity mActivity;
+
+    @Mock
+    private Intent mIntent;
+
+    @Mock
+    private UserManager mUserManager;
+
+    private TestFragment mFragment;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mFragment = spy(new TestFragment());
+        mFragment.onAttach(application);
+
+        doReturn(mActivity).when(mFragment).getActivity();
+
+        Resources res = application.getResources();
+
+        doReturn(res).when(mFragment).getResources();
+        when(mActivity.getTheme()).thenReturn(res.newTheme());
+        when(mActivity.getIntent()).thenReturn(mIntent);
+        when(mActivity.getResources()).thenReturn(res);
+        when(mFragment.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
+    }
+
+    @Test
+    public void onCreate_noRuleId_shouldToastAndFinishAndNoCrash() {
+        final Context ctx = application.getApplicationContext();
+        final String expected = ctx.getResources().getString(R.string.zen_mode_rule_not_found_text);
+
+        mFragment.onCreate(null);
+
+        // verify the toast
+        assertThat(ShadowToast.getTextOfLatestToast()).isEqualTo(expected);
+
+        // verify the finish
+        verify(mActivity).finish();
+
+        //shoud not crash
+    }
+
+    public static class TestFragment extends ZenModeScheduleRuleSettings {
+
+        @Override
+        protected Object getSystemService(final String name) {
+            return null;
+        }
+
+        @Override
+        protected void maybeRefreshRules(boolean success, boolean fireChanged) {
+            //do nothing
+        }
+    }
+
+}