Merge "Allow WifiSettings to show old APs on app resume." into oc-dev
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index db95767..ff7663a 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -402,7 +402,7 @@
     <string name="security_settings_fingerprint_enroll_touch_dialog_message" msgid="5053971232594165142">"استخدم جهاز استشعار بصمة الإصبع على جهازك."</string>
     <string name="security_settings_fingerprint_enroll_error_dialog_title" msgid="3618021988442639280">"لم يكتمل التسجيل"</string>
     <string name="security_settings_fingerprint_enroll_error_timeout_dialog_message" msgid="2942551158278899627">"تم بلوغ الحد الأقصى لزمن تسجيل بصمة الإصبع. يمكنك إعادة المحاولة."</string>
-    <string name="security_settings_fingerprint_enroll_error_generic_dialog_message" msgid="3624760637222239293">"Fingerprint enrollment didn\'t work. Try again or use a different finger."</string>
+    <string name="security_settings_fingerprint_enroll_error_generic_dialog_message" msgid="3624760637222239293">"تعذّر تسجيل بصمة الإصبع. يُرجى إعادة المحاولة أو استخدام إصبع آخر."</string>
     <string name="fingerprint_enroll_button_add" msgid="6317978977419045463">"إضافة بصمة إصبع أخرى"</string>
     <string name="fingerprint_enroll_button_next" msgid="6247009337616342759">"التالي"</string>
     <string name="security_settings_fingerprint_enroll_disclaimer" msgid="2624905914239271751">"بالإضافة إلى إلغاء قفل هاتفك، يمكنك أيضًا استخدام بصمة إصبعك للسماح بعمليات الشراء والوصول إلى التطبيق. "<annotation id="url">"مزيد من المعلومات"</annotation></string>
@@ -2003,7 +2003,7 @@
     <string name="battery_since_reset" msgid="7464546661121187045">"استخدام البطارية منذ إعادة التعيين"</string>
     <string name="battery_stats_on_battery" msgid="4970762168505236033">"<xliff:g id="TIME">%1$s</xliff:g> على البطارية"</string>
     <string name="battery_stats_duration" msgid="7464501326709469282">"<xliff:g id="TIME">%1$s</xliff:g> منذ عدم التوصيل"</string>
-    <string name="battery_stats_charging_label" msgid="4223311142875178785">"جاري الشحن"</string>
+    <string name="battery_stats_charging_label" msgid="4223311142875178785">"جارٍ الشحن"</string>
     <string name="battery_stats_screen_on_label" msgid="7150221809877509708">"الشاشة قيد التشغيل"</string>
     <string name="battery_stats_gps_on_label" msgid="1193657533641951256">"‏GPS (نظام تحديد المواقع العالمي) على"</string>
     <string name="battery_stats_camera_on_label" msgid="4935637383628414968">"تشغيل الكاميرا"</string>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index f3dad3f..e335d0b 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -2967,7 +2967,7 @@
     <string name="display_auto_rotate_title" msgid="7119684966039069439">"Enhedsrotation"</string>
     <string name="display_auto_rotate_rotate" msgid="4544299861233497728">"Roter skærmindholdet"</string>
     <string name="display_auto_rotate_stay_in_portrait" msgid="292745182318093651">"Bliv i stående format"</string>
-    <string name="display_auto_rotate_stay_in_landscape" msgid="3804752830204062162">"Behold landskabsvisning"</string>
+    <string name="display_auto_rotate_stay_in_landscape" msgid="3804752830204062162">"Behold liggende format"</string>
     <string name="display_auto_rotate_stay_in_current" msgid="317932372686498096">"Behold den aktuelle orientering"</string>
     <string name="encryption_interstitial_header" msgid="468015813904595613">"Sikker opstart"</string>
     <string name="encryption_continue_button" msgid="1121880322636992402">"Fortsæt"</string>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index eaccf46..d04d0bb 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -2668,7 +2668,7 @@
     <string name="search_results_title" msgid="1796252422574886932">"設定"</string>
     <string name="search_menu" msgid="6283419262313758339">"搜尋設定"</string>
     <string name="query_hint_text" msgid="3350700807437473939">"搜尋設定"</string>
-    <string name="keywords_wifi" msgid="1395786161993828719">"wifi, wi-fi, 網路連線"</string>
+    <string name="keywords_wifi" msgid="1395786161993828719">"wifi、wi-fi、網路連線"</string>
     <string name="keywords_more_default_sms_app" msgid="2265154063220360784">"文字訊息, 傳送文字訊息, 簡訊, 傳送簡訊"</string>
     <string name="keywords_more_mobile_networks" msgid="8995946622054642367">"行動網路, 行動裝置, 電信業者, 無線, 數據, 4G, 3G, 2G, LTE"</string>
     <string name="keywords_wifi_calling" msgid="1784064367330122679">"wifi, wi-fi, 通話, 撥號"</string>
@@ -3212,7 +3212,7 @@
     <string name="camera_double_tap_power_gesture_desc" msgid="7355664631775680376">"不必將螢幕解鎖就能快速開啟相機"</string>
     <string name="screen_zoom_title" msgid="5233515303733473927">"顯示大小"</string>
     <string name="screen_zoom_short_summary" msgid="7291960817349834688">"縮小或放大畫面上的項目"</string>
-    <string name="screen_zoom_keywords" msgid="9176477565403352552">"顯示密度, 畫面縮放, 縮放, 縮放比例"</string>
+    <string name="screen_zoom_keywords" msgid="9176477565403352552">"顯示密度、畫面縮放、縮放、縮放比例"</string>
     <string name="screen_zoom_summary" msgid="6445488991799015407">"縮小或放大畫面上的項目。縮放完成後,畫面上某些應用程式的位置可能會有變動。"</string>
     <string name="screen_zoom_preview_title" msgid="4680671508172336572">"預覽"</string>
     <string name="screen_zoom_make_smaller_desc" msgid="4622359904253364742">"縮小"</string>
@@ -3578,7 +3578,7 @@
     <string name="webview_uninstalled_for_user" msgid="1819903169194420983">"(已為使用者<xliff:g id="USER">%s</xliff:g>解除安裝)"</string>
     <string name="webview_disabled_for_user" msgid="1216426047631256825">"(已為使用者<xliff:g id="USER">%s</xliff:g>停用)"</string>
     <string name="autofill_app" msgid="7338387238377914374">"自動填入服務"</string>
-    <string name="autofill_keywords" msgid="7485591824120812710">"自動, 填入, 自動填入"</string>
+    <string name="autofill_keywords" msgid="7485591824120812710">"自動、填入、自動填入"</string>
     <string name="autofill_confirmation_message" msgid="2784869528908005194">"&lt;b&gt;請確定這是你信任的應用程式&lt;/b&gt; &lt;br/&gt; &lt;br/&gt; &lt;xliff:g id=app_name example=Google Autofill&gt;%1$s&lt;/xliff:g&gt;應用程式會根據你的畫面內容判斷要自動填入的內容。"</string>
     <string name="device_theme" msgid="4571803018917608588">"裝置主題"</string>
     <string name="default_theme" msgid="7085644992078579076">"預設"</string>
diff --git a/src/com/android/settings/notification/AppNotificationSettings.java b/src/com/android/settings/notification/AppNotificationSettings.java
index fb89402..5a38b98 100644
--- a/src/com/android/settings/notification/AppNotificationSettings.java
+++ b/src/com/android/settings/notification/AppNotificationSettings.java
@@ -212,7 +212,8 @@
         MasterSwitchPreference channelPref = new MasterSwitchPreference(
                 getPrefContext());
         channelPref.setSwitchEnabled(mSuspendedAppsAdmin == null
-                &&  isChannelBlockable(mAppRow.systemApp, channel));
+                && isChannelBlockable(mAppRow.systemApp, channel)
+                && isChannelConfigurable(channel));
         channelPref.setKey(channel.getId());
         channelPref.setTitle(channel.getName());
         channelPref.setChecked(channel.getImportance() != IMPORTANCE_NONE);
diff --git a/src/com/android/settings/notification/ChannelNotificationSettings.java b/src/com/android/settings/notification/ChannelNotificationSettings.java
index 3ae0bfa..3bd0557 100644
--- a/src/com/android/settings/notification/ChannelNotificationSettings.java
+++ b/src/com/android/settings/notification/ChannelNotificationSettings.java
@@ -214,7 +214,7 @@
     private void setupVibrate() {
         mVibrate = (RestrictedSwitchPreference) findPreference(KEY_VIBRATE);
         mVibrate.setDisabledByAdmin(mSuspendedAppsAdmin);
-        mVibrate.setEnabled(!(mAppRow.lockedImportance || mVibrate.isDisabledByAdmin()));
+        mVibrate.setEnabled(!mVibrate.isDisabledByAdmin() && isChannelConfigurable(mChannel));
         mVibrate.setChecked(mChannel.shouldVibrate());
         mVibrate.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
             @Override
@@ -231,7 +231,7 @@
     private void setupRingtone() {
         mRingtone = (NotificationSoundPreference) findPreference(KEY_RINGTONE);
         mRingtone.setRingtone(mChannel.getSound());
-        mRingtone.setEnabled(!(mAppRow.lockedImportance));
+        mRingtone.setEnabled(isChannelConfigurable(mChannel));
         mRingtone.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
             @Override
             public boolean onPreferenceChange(Preference preference, Object newValue) {
@@ -287,7 +287,7 @@
         channelArgs.putBoolean(AppHeader.EXTRA_HIDE_INFO_BUTTON, true);
         channelArgs.putString(AppInfoBase.ARG_PACKAGE_NAME, mPkg);
         channelArgs.putString(Settings.EXTRA_CHANNEL_ID, mChannel.getId());
-        mImportance.setEnabled(mSuspendedAppsAdmin == null && !mAppRow.lockedImportance);
+        mImportance.setEnabled(mSuspendedAppsAdmin == null && isChannelConfigurable(mChannel));
         // Set up intent to show importance selection only if this setting is enabled.
         if (mImportance.isEnabled()) {
             Intent channelIntent = Utils.onBuildStartFragmentIntent(getActivity(),
diff --git a/src/com/android/settings/notification/NotificationBackend.java b/src/com/android/settings/notification/NotificationBackend.java
index 96737db..82e3a9e 100644
--- a/src/com/android/settings/notification/NotificationBackend.java
+++ b/src/com/android/settings/notification/NotificationBackend.java
@@ -30,6 +30,7 @@
 import android.util.IconDrawableFactory;
 import android.util.Log;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.settingslib.Utils;
 
 public class NotificationBackend {
@@ -60,15 +61,28 @@
         row.systemApp = Utils.isSystemPackage(context.getResources(), pm, app);
         final String[] nonBlockablePkgs = context.getResources().getStringArray(
                     com.android.internal.R.array.config_nonBlockableNotificationPackages);
+        markAppRowWithBlockables(nonBlockablePkgs, row, app.packageName);
+        return row;
+    }
+
+    @VisibleForTesting static void markAppRowWithBlockables(String[] nonBlockablePkgs, AppRow row,
+            String packageName) {
         if (nonBlockablePkgs != null) {
             int N = nonBlockablePkgs.length;
             for (int i = 0; i < N; i++) {
-                if (app.packageName.equals(nonBlockablePkgs[i])) {
+                String pkg = nonBlockablePkgs[i];
+                if (pkg == null) {
+                    continue;
+                } else if (pkg.contains(":")) {
+                    // Interpret as channel; lock only this channel for this app.
+                    if (packageName.equals(pkg.split(":", 2)[0])) {
+                        row.lockedChannelId = pkg.split(":", 2 )[1];
+                    }
+                } else if (packageName.equals(nonBlockablePkgs[i])) {
                     row.systemApp = row.lockedImportance = true;
                 }
             }
         }
-        return row;
     }
 
     public boolean getNotificationsBanned(String pkg, int uid) {
@@ -184,6 +198,7 @@
         public boolean first;  // first app in section
         public boolean systemApp;
         public boolean lockedImportance;
+        public String lockedChannelId;
         public boolean showBadge;
         public int userId;
     }
diff --git a/src/com/android/settings/notification/NotificationSettingsBase.java b/src/com/android/settings/notification/NotificationSettingsBase.java
index 717cf08..c7b366e 100644
--- a/src/com/android/settings/notification/NotificationSettingsBase.java
+++ b/src/com/android/settings/notification/NotificationSettingsBase.java
@@ -293,8 +293,8 @@
     private void setupImportanceToggle() {
         mImportanceToggle = (RestrictedSwitchPreference) findPreference(KEY_ALLOW_SOUND);
         mImportanceToggle.setDisabledByAdmin(mSuspendedAppsAdmin);
-        mImportanceToggle.setEnabled(!(mAppRow.lockedImportance
-                || mImportanceToggle.isDisabledByAdmin()));
+        mImportanceToggle.setEnabled(isChannelConfigurable(mChannel)
+                && !mImportanceToggle.isDisabledByAdmin());
         mImportanceToggle.setChecked(mChannel.getImportance() >= IMPORTANCE_DEFAULT
                 || mChannel.getImportance() == IMPORTANCE_UNSPECIFIED);
         mImportanceToggle.setOnPreferenceChangeListener(
@@ -315,7 +315,7 @@
     protected void setupPriorityPref(boolean priority) {
         mPriority = (RestrictedSwitchPreference) findPreference(KEY_BYPASS_DND);
         mPriority.setDisabledByAdmin(mSuspendedAppsAdmin);
-        mPriority.setEnabled(!(mAppRow.lockedImportance || mPriority.isDisabledByAdmin()));
+        mPriority.setEnabled(isChannelConfigurable(mChannel) && !mPriority.isDisabledByAdmin());
         mPriority.setChecked(priority);
         mPriority.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
             @Override
@@ -447,10 +447,15 @@
         return lockscreenSecure;
     }
 
+    protected boolean isChannelConfigurable(NotificationChannel channel) {
+        return !channel.getId().equals(mAppRow.lockedChannelId);
+    }
+
     protected boolean isChannelBlockable(boolean systemApp, NotificationChannel channel) {
         if (!mAppRow.systemApp) {
             return true;
         }
+
         return channel.isBlockableSystem()
                 || channel.getImportance() == NotificationManager.IMPORTANCE_NONE;
     }
diff --git a/tests/robotests/src/com/android/settings/notification/NotificationBackendTest.java b/tests/robotests/src/com/android/settings/notification/NotificationBackendTest.java
new file mode 100644
index 0000000..d380900
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/NotificationBackendTest.java
@@ -0,0 +1,134 @@
+/*
+ * 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.NotificationManager;
+import android.app.NotificationManager.Policy;
+import android.content.Context;
+import android.support.v7.preference.Preference;
+
+import com.android.settings.R;
+import com.android.settings.notification.NotificationBackend.AppRow;
+import com.android.settings.SettingsRobolectricTestRunner;
+import com.android.settings.TestConfig;
+import org.robolectric.annotation.Config;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertTrue;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNull;
+
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class NotificationBackendTest {
+
+    @Test
+    public void testMarkAppRow_unblockablePackage() {
+        AppRow appRow = new AppRow();
+        String packageName = "foo.bar.unblockable";
+        appRow.pkg = packageName;
+        String[] nonBlockablePkgs = new String[2];
+        nonBlockablePkgs[0] = packageName;
+        nonBlockablePkgs[1] = "some.other.package";
+        NotificationBackend.markAppRowWithBlockables(nonBlockablePkgs, appRow, packageName);
+
+        // This package has a package lock but no locked channels
+        assertTrue(appRow.lockedImportance);
+        assertNull(appRow.lockedChannelId);
+    }
+
+    @Test
+    public void testMarkAppRow_unblockableChannelOrPkg() {
+        String channelBlockName = "foo.bar.pkgWithChannel";
+        String pkgBlockName = "foo.bar.pkgBlock";
+        String[] nonBlockablePkgs = new String[2];
+        nonBlockablePkgs[0] = pkgBlockName;
+        nonBlockablePkgs[1] = channelBlockName + ":SpecificChannel";
+
+        // This package has a channel level lock but no full package lock
+        AppRow channelBlockApp = new AppRow();
+        channelBlockApp.pkg = channelBlockName;
+        NotificationBackend.markAppRowWithBlockables(nonBlockablePkgs, channelBlockApp,
+                channelBlockName);
+        assertFalse(channelBlockApp.lockedImportance);
+        assertEquals("SpecificChannel", channelBlockApp.lockedChannelId);
+
+        // This other package has the reverse
+        AppRow pkgBlock = new AppRow();
+        pkgBlock.pkg = pkgBlockName;
+        NotificationBackend.markAppRowWithBlockables(nonBlockablePkgs, pkgBlock, pkgBlockName);
+        assertTrue(pkgBlock.lockedImportance);
+        assertNull(pkgBlock.lockedChannelId);
+
+        // This third package has no locks at all
+        AppRow otherAppRow = new AppRow();
+        otherAppRow.pkg ="foo.bar.nothingBlocked";
+        NotificationBackend.markAppRowWithBlockables(nonBlockablePkgs, otherAppRow,
+                "foo.bar.nothingBlocked");
+        assertFalse(otherAppRow.lockedImportance);
+        assertNull(otherAppRow.lockedChannelId);
+    }
+
+    @Test
+    public void testMarkAppRow_unblockableChannelAndPkg() {
+        AppRow appRow = new AppRow();
+        String packageName = "foo.bar.unblockable";
+        appRow.pkg = packageName;
+        String[] nonBlockablePkgs = new String[2];
+        nonBlockablePkgs[0] = "foo.bar.unblockable";
+        nonBlockablePkgs[1] = "foo.bar.unblockable:SpecificChannel";
+        NotificationBackend.markAppRowWithBlockables(nonBlockablePkgs, appRow, packageName);
+
+        // This package has both a channel lock and a package lock
+        assertTrue(appRow.lockedImportance);
+        assertEquals("SpecificChannel", appRow.lockedChannelId);
+    }
+
+    @Test
+    public void testMarkAppRow_channelNameWithColons() {
+        AppRow appRow = new AppRow();
+        String packageName = "foo.bar.unblockable";
+        String channelName = "SpecificChannel:1234:abc:defg";
+        appRow.pkg = packageName;
+        String[] nonBlockablePkgs = new String[1];
+        nonBlockablePkgs[0] = packageName + ":" + channelName;
+        NotificationBackend.markAppRowWithBlockables(nonBlockablePkgs, appRow, packageName);
+
+        assertEquals(channelName, appRow.lockedChannelId);
+    }
+
+
+    @Test
+    public void testMarkAppRow_blocklistWithNullEntries() {
+        AppRow appRow = new AppRow();
+        String packageName = "foo.bar.unblockable";
+        appRow.pkg = packageName;
+        String[] nonBlockablePkgs = new String[6]; // extra long list with some entries left null
+        nonBlockablePkgs[2] = "foo.bar.unblockable";
+        nonBlockablePkgs[4] = "foo.bar.unblockable:SpecificChannel";
+        NotificationBackend.markAppRowWithBlockables(nonBlockablePkgs, appRow, packageName);
+
+        assertTrue(appRow.lockedImportance);
+        assertEquals("SpecificChannel", appRow.lockedChannelId);
+    }
+}