Merge "Import translations. DO NOT MERGE" into oc-mr1-dev
diff --git a/src/com/android/settings/dashboard/DashboardAdapter.java b/src/com/android/settings/dashboard/DashboardAdapter.java
index 2bf1c01..c06a58f 100644
--- a/src/com/android/settings/dashboard/DashboardAdapter.java
+++ b/src/com/android/settings/dashboard/DashboardAdapter.java
@@ -329,7 +329,11 @@
     }
 
     private void logSuggestions() {
-        for (Tile suggestion : mDashboardData.getSuggestions()) {
+        final List<Tile> suggestions = mDashboardData.getSuggestions();
+        if (suggestions == null) {
+            return;
+        }
+        for (Tile suggestion : suggestions) {
             final String suggestionId = mSuggestionFeatureProvider.getSuggestionIdentifier(
                 mContext, suggestion);
             if (!mSuggestionsShownLogged.contains(suggestionId)) {
diff --git a/src/com/android/settings/search/SearchFeatureProvider.java b/src/com/android/settings/search/SearchFeatureProvider.java
index 900cefc..956808d 100644
--- a/src/com/android/settings/search/SearchFeatureProvider.java
+++ b/src/com/android/settings/search/SearchFeatureProvider.java
@@ -138,6 +138,13 @@
     }
 
     /**
+     * @return smart ranking timeout in milliseconds.
+     */
+    default long smartSearchRankingTimeoutMs(Context context) {
+        return 300L;
+    }
+
+    /**
      * Prepare for search ranking predictions to avoid latency on the first prediction call.
      */
     default void searchRankingWarmup(Context context) {
diff --git a/src/com/android/settings/search/SearchResultsAdapter.java b/src/com/android/settings/search/SearchResultsAdapter.java
index 7b886a4..5fedc52 100644
--- a/src/com/android/settings/search/SearchResultsAdapter.java
+++ b/src/com/android/settings/search/SearchResultsAdapter.java
@@ -64,9 +64,6 @@
     @VisibleForTesting
     static final int MSG_RANKING_TIMED_OUT = 1;
 
-    // TODO(b/38197948): Tune this timeout based on latency of static and async rankings. Also, we
-    // should add a gservices flag to control this.
-    private static final long RANKING_TIMEOUT_MS = 300;
     private final SearchFragment mFragment;
     private final Context mContext;
     private final List<SearchResult> mSearchResults;
@@ -245,8 +242,9 @@
             mAsyncRankingState = PENDING_RESULTS;
             mSearchFeatureProvider.cancelPendingSearchQuery(mContext);
             final Handler handler = getHandler();
+            final long timeoutMs = mSearchFeatureProvider.smartSearchRankingTimeoutMs(mContext);
             handler.sendMessageDelayed(
-                    handler.obtainMessage(MSG_RANKING_TIMED_OUT), RANKING_TIMEOUT_MS);
+                    handler.obtainMessage(MSG_RANKING_TIMED_OUT), timeoutMs);
             mSearchFeatureProvider.querySearchResults(mContext, query, this);
         } else {
             mAsyncRankingState = DISABLED;
diff --git a/src/com/android/settings/wifi/SavedAccessPointsWifiSettings.java b/src/com/android/settings/wifi/SavedAccessPointsWifiSettings.java
index 33fdf2d..1a95525 100644
--- a/src/com/android/settings/wifi/SavedAccessPointsWifiSettings.java
+++ b/src/com/android/settings/wifi/SavedAccessPointsWifiSettings.java
@@ -16,13 +16,12 @@
 
 package com.android.settings.wifi;
 
+import android.annotation.Nullable;
 import android.app.Dialog;
 import android.content.Context;
 import android.content.res.Resources;
 import android.icu.text.Collator;
-import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiManager;
-import android.net.wifi.hotspot2.PasspointConfiguration;
 import android.os.Bundle;
 import android.provider.SearchIndexableResource;
 import android.support.v7.preference.Preference;
@@ -48,6 +47,7 @@
 
 /**
  * UI to manage saved networks/access points.
+ * TODO(b/64806699): convert to {@link DashboardFragment} with {@link PreferenceController}s
  */
 public class SavedAccessPointsWifiSettings extends SettingsPreferenceFragment
         implements Indexable, WifiDialog.WifiDialogListener {
@@ -83,6 +83,7 @@
     private AccessPoint mDlgAccessPoint;
     private Bundle mAccessPointSavedState;
     private AccessPoint mSelectedAccessPoint;
+    private Preference mAddNetworkPreference;
 
     private AccessPointPreference.UserBadgeCache mUserBadgeCache;
 
@@ -142,23 +143,38 @@
                 preference.setIcon(null);
                 preferenceScreen.addPreference(preference);
             }
+            preference.setOrder(i);
         }
 
         removeCachedPrefs(preferenceScreen);
 
+        if (mAddNetworkPreference == null) {
+            mAddNetworkPreference = new Preference(getPrefContext());
+            mAddNetworkPreference.setIcon(R.drawable.ic_menu_add_inset);
+            mAddNetworkPreference.setTitle(R.string.wifi_add_network);
+        }
+        mAddNetworkPreference.setOrder(accessPointsSize);
+        preferenceScreen.addPreference(mAddNetworkPreference);
+
         if(getPreferenceScreen().getPreferenceCount() < 1) {
             Log.w(TAG, "Saved networks activity loaded, but there are no saved networks!");
         }
     }
 
-    private void showDialog(LongPressAccessPointPreference accessPoint, boolean edit) {
+    private void showWifiDialog(@Nullable LongPressAccessPointPreference accessPoint) {
         if (mDialog != null) {
             removeDialog(WifiSettings.WIFI_DIALOG_ID);
             mDialog = null;
         }
 
-        // Save the access point and edit mode
-        mDlgAccessPoint = accessPoint.getAccessPoint();
+        if (accessPoint != null) {
+            // Save the access point and edit mode
+            mDlgAccessPoint = accessPoint.getAccessPoint();
+        } else {
+            // No access point is selected. Clear saved state.
+            mDlgAccessPoint = null;
+            mAccessPointSavedState = null;
+        }
 
         showDialog(WifiSettings.WIFI_DIALOG_ID);
     }
@@ -167,15 +183,23 @@
     public Dialog onCreateDialog(int dialogId) {
         switch (dialogId) {
             case WifiSettings.WIFI_DIALOG_ID:
-                if (mDlgAccessPoint == null) { // For re-launch from saved state
-                    mDlgAccessPoint = new AccessPoint(getActivity(), mAccessPointSavedState);
-                    // Reset the saved access point data
-                    mAccessPointSavedState = null;
+                if (mDlgAccessPoint == null && mAccessPointSavedState == null) {
+                    // Add new network
+                    mDialog = WifiDialog.createFullscreen(getActivity(), this, null,
+                            WifiConfigUiBase.MODE_CONNECT);
+                } else {
+                    // Modify network
+                    if (mDlgAccessPoint == null) {
+                        // Restore AP from save state
+                        mDlgAccessPoint = new AccessPoint(getActivity(), mAccessPointSavedState);
+                        // Reset the saved access point data
+                        mAccessPointSavedState = null;
+                    }
+                    mDialog = WifiDialog.createModal(getActivity(), this, mDlgAccessPoint,
+                            WifiConfigUiBase.MODE_VIEW);
                 }
                 mSelectedAccessPoint = mDlgAccessPoint;
 
-                mDialog = WifiDialog.createModal(getActivity(), this, mDlgAccessPoint,
-                        WifiConfigUiBase.MODE_VIEW);
                 return mDialog;
         }
         return super.onCreateDialog(dialogId);
@@ -233,9 +257,12 @@
     @Override
     public boolean onPreferenceTreeClick(Preference preference) {
         if (preference instanceof LongPressAccessPointPreference) {
-            showDialog((LongPressAccessPointPreference) preference, false);
+            showWifiDialog((LongPressAccessPointPreference) preference);
             return true;
-        } else{
+        } else if (preference == mAddNetworkPreference) {
+            showWifiDialog(null);
+            return true;
+        } else {
             return super.onPreferenceTreeClick(preference);
         }
     }
diff --git a/tests/robotests/src/com/android/settings/dashboard/DashboardAdapterTest.java b/tests/robotests/src/com/android/settings/dashboard/DashboardAdapterTest.java
index 6b56c29..e00908e 100644
--- a/tests/robotests/src/com/android/settings/dashboard/DashboardAdapterTest.java
+++ b/tests/robotests/src/com/android/settings/dashboard/DashboardAdapterTest.java
@@ -319,6 +319,21 @@
     }
 
     @Test
+    public void testSuggestionsLogs_nullSuggestionsList_shouldNotCrash() {
+        setupSuggestions(makeSuggestions("pkg1", "pkg2", "pkg3", "pkg4", "pkg5"));
+        mDashboardAdapter.onBindSuggestionConditionHeader(mSuggestionHolder, mSuggestionHeaderData);
+
+        // set suggestions to null
+        final DashboardData prevData = mDashboardAdapter.mDashboardData;
+        mDashboardAdapter.mDashboardData = new DashboardData.Builder(prevData)
+                .setSuggestions(null)
+                .build();
+
+        mSuggestionHolder.itemView.callOnClick();
+        // no crash
+    }
+
+    @Test
     public void testSuggestionDismissed_notOnlySuggestion_updateSuggestionOnly() {
         final DashboardAdapter adapter =
                 spy(new DashboardAdapter(mContext, null, null, null, null));
diff --git a/tests/robotests/src/com/android/settings/search/SearchResultsAdapterTest.java b/tests/robotests/src/com/android/settings/search/SearchResultsAdapterTest.java
index e605ec1..a2afb35 100644
--- a/tests/robotests/src/com/android/settings/search/SearchResultsAdapterTest.java
+++ b/tests/robotests/src/com/android/settings/search/SearchResultsAdapterTest.java
@@ -83,6 +83,8 @@
         mLoaderClassName = DatabaseResultLoader.class.getName();
         when(mFragment.getContext()).thenReturn(mMockContext);
         when(mMockContext.getApplicationContext()).thenReturn(mContext);
+        when(mSearchFeatureProvider.smartSearchRankingTimeoutMs(any(Context.class)))
+                .thenReturn(300L);
         mAdapter = new SearchResultsAdapter(mFragment, mSearchFeatureProvider);
     }
 
diff --git a/tests/unit/src/com/android/settings/wifi/SavedNetworkSettingsTest.java b/tests/unit/src/com/android/settings/wifi/SavedNetworkSettingsTest.java
new file mode 100644
index 0000000..79eccc4
--- /dev/null
+++ b/tests/unit/src/com/android/settings/wifi/SavedNetworkSettingsTest.java
@@ -0,0 +1,76 @@
+/*
+ * 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.wifi;
+
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.action.ViewActions.click;
+import static android.support.test.espresso.assertion.ViewAssertions.matches;
+import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static android.support.test.espresso.matcher.ViewMatchers.withText;
+
+import android.content.Context;
+import android.content.Intent;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.settings.Settings;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+
+@RunWith(AndroidJUnit4.class)
+public class SavedNetworkSettingsTest {
+
+    // Keys used to lookup resources by name (see the resourceId helper method).
+    private static final String STRING = "string";
+    private static final String WIFI_ADD_NETWORK = "wifi_add_network";
+    private static final String WIFI_NETWORK_LABEL = "wifi_ssid";
+
+    private Context mContext;
+
+    @Rule
+    public ActivityTestRule<Settings.SavedAccessPointsSettingsActivity> mActivityRule =
+            new ActivityTestRule<>(Settings.SavedAccessPointsSettingsActivity.class, true);
+
+    private int resourceId(String type, String name) {
+        return mContext.getResources().getIdentifier(name, type, mContext.getPackageName());
+    }
+
+    @Before
+    public void setUp() {
+        mContext = InstrumentationRegistry.getTargetContext();
+    }
+
+    private void launchSavedNetworksSettings() {
+        Intent intent = new Intent()
+                .setClassName(mContext.getPackageName(),
+                        Settings.SavedAccessPointsSettingsActivity.class.getName())
+                .setPackage(mContext.getPackageName());
+        mActivityRule.launchActivity(intent);
+    }
+
+    @Test
+    public void launchSavedNetworkSettings_shouldHaveAddNetworkField() {
+        launchSavedNetworksSettings();
+        onView(withText(resourceId(STRING, WIFI_ADD_NETWORK))).check(matches(isDisplayed()))
+                .perform(click());
+        onView(withText(resourceId(STRING, WIFI_NETWORK_LABEL))).check(matches(isDisplayed()));
+    }
+}