settings: wifi priorities

Added the ability of graphically set the priority of any of the saved Wi-Fi networks
through Settings -> Wi-Fi -> Saved networks

Require: http://review.cyanogenmod.org/100830
         http://review.cyanogenmod.org/100831

JIRA: CML-118

Change-Id: Id535a7bd8865897bfed0570675ef837a5d410779
Signed-off-by: Jorge Ruesga <jorge@ruesga.com>

Conflicts:
	res/values/slim_strings.xml
diff --git a/res/drawable/wifi_no_signal_teal.xml b/res/drawable/wifi_no_signal_teal.xml
new file mode 100644
index 0000000..4becf24
--- /dev/null
+++ b/res/drawable/wifi_no_signal_teal.xml
@@ -0,0 +1,21 @@
+<?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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:settings="http://schemas.android.com/apk/res/com.android.settings">
+    <item settings:state_encrypted="true" android:drawable="@drawable/ic_wifi_lock_signal_0_teal" />
+    <item settings:state_encrypted="false" android:drawable="@drawable/ic_wifi_signal_0_teal" />
+</selector>
diff --git a/res/values/custom_strings.xml b/res/values/custom_strings.xml
index 55a9d2e..c8f30f8 100644
--- a/res/values/custom_strings.xml
+++ b/res/values/custom_strings.xml
@@ -1794,6 +1794,9 @@
     <!-- Increasing ring tone volume -->
     <string name="increasing_ring_volume_option_title">Increasing ring volume</string>
     <string name="increasing_ring_min_volume_title">Start volume</string>
-    <string name="increasing_ring_ramp_up_time_title">Ramp-up time</string>   
+    <string name="increasing_ring_ramp_up_time_title">Ramp-up time</string>
+
+    <!-- WiFi auto-configure priorities -->
+    <string name="wifi_auto_config_priorities">Automatic priority</string>  
     
 </resources>
diff --git a/res/values/themes.xml b/res/values/themes.xml
index 675faf7..fa79a14 100644
--- a/res/values/themes.xml
+++ b/res/values/themes.xml
@@ -22,6 +22,7 @@
     <attr name="side_margin" format="reference|dimension" />
     <attr name="wifi_signal_color" format="reference" />
     <attr name="wifi_signal" format="reference" />
+    <attr name="wifi_no_signal" format="reference" />
 
     <style name="SetupWizardDisableAppStartingTheme">
         <!-- Theme to disable the app starting window. The actual theme of the activity needs to
@@ -127,6 +128,7 @@
         <item name="ic_menu_moreoverflow">@*android:drawable/ic_menu_moreoverflow_holo_dark</item>
         <item name="ic_wps">@drawable/ic_wps_light</item>
         <item name="wifi_signal">@drawable/wifi_signal_teal</item>
+        <item name="wifi_no_signal">@drawable/wifi_no_signal_teal</item>
         <item name="side_margin">@dimen/settings_side_margin</item>
 
         <!-- Redefine the ActionBar style for contentInsetStart -->
diff --git a/src/com/android/settings/DraggableSortListView.java b/src/com/android/settings/DraggableSortListView.java
new file mode 100644
index 0000000..ba0ddc5
--- /dev/null
+++ b/src/com/android/settings/DraggableSortListView.java
@@ -0,0 +1,361 @@
+/*
+ * 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.
+ */
+
+package com.android.settings;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+
+import android.view.Gravity;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.widget.AdapterView;
+import android.widget.ImageView;
+import android.widget.ListView;
+
+/**
+ * A draggable/sortable listview
+ */
+public class DraggableSortListView extends ListView {
+
+    public interface DragListener {
+        void drag(int from, int to);
+    }
+
+    public interface DropListener {
+        void drop(int from, int to);
+    }
+
+    private ImageView mDragView;
+    private WindowManager mWindowManager;
+    private WindowManager.LayoutParams mWindowParams;
+    private int mDragPos;
+    private int mFirstDragPos;
+    private int mDragPoint;
+    private int mCoordOffset;
+    private DragListener mDragListener;
+    private DropListener mDropListener;
+    private int mUpperBound;
+    private int mLowerBound;
+    private int mHeight;
+    private Rect mTempRect = new Rect();
+    private Bitmap mDragBitmap;
+    private final int mTouchSlop;
+    private int mItemHeight;
+
+    public DraggableSortListView(Context context) {
+        super(context);
+        mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
+    }
+
+    @Override
+    public boolean onInterceptTouchEvent(MotionEvent ev) {
+        if ((mDragListener != null || mDropListener != null) && getChildCount() > 1) {
+            switch (ev.getAction()) {
+                case MotionEvent.ACTION_DOWN:
+                    int x = (int) ev.getX();
+                    int y = (int) ev.getY();
+                    int itemnum = pointToPosition(x, y);
+                    if (itemnum == AdapterView.INVALID_POSITION) {
+                        break;
+                    }
+                    ViewGroup item = (ViewGroup) getChildAt(itemnum - getFirstVisiblePosition());
+                    mItemHeight = item.getHeight();
+                    mDragPoint = y - item.getTop();
+                    mCoordOffset = ((int) ev.getRawY()) - y;
+                    View dragger = item.findViewById(com.android.internal.R.id.icon);
+
+                    // The dragger icon itself is quite small, so pretend the
+                    // touch area is bigger
+                    int x1 = item.getLeft() + dragger.getLeft() - (dragger.getWidth() / 2);
+                    int x2 = item.getLeft() + dragger.getRight() + (dragger.getWidth() / 2);
+                    if (x > x1 && x < x2) {
+                        // Fix x position while dragging
+                        int[] itemPos = new int[2];
+                        item.getLocationOnScreen(itemPos);
+
+                        item.setDrawingCacheEnabled(true);
+                        // Create a copy of the drawing cache so that it does
+                        // not get recycled
+                        // by the framework when the list tries to clean up
+                        // memory
+                        Bitmap bitmap = Bitmap.createBitmap(item.getDrawingCache());
+                        startDragging(bitmap, itemPos[0], y);
+                        mDragPos = itemnum;
+                        mFirstDragPos = mDragPos;
+                        mHeight = getHeight();
+                        int touchSlop = mTouchSlop;
+                        mUpperBound = Math.min(y - touchSlop, mHeight / 3);
+                        mLowerBound = Math.max(y + touchSlop, mHeight * 2 / 3);
+                        return false;
+                    }
+                    stopDragging();
+                    break;
+            }
+        }
+        return super.onInterceptTouchEvent(ev);
+    }
+
+    /*
+     * pointToPosition() doesn't consider invisible views, but we need to, so
+     * implement a slightly different version.
+     */
+    private int myPointToPosition(int x, int y) {
+
+        if (y < 0) {
+            // when dragging off the top of the screen, calculate position
+            // by going back from a visible item
+            int pos = myPointToPosition(x, y + mItemHeight);
+            if (pos > 0) {
+                return pos - 1;
+            }
+        }
+
+        Rect frame = mTempRect;
+        final int count = getChildCount();
+        for (int i = count - 1; i >= 0; i--) {
+            final View child = getChildAt(i);
+            child.getHitRect(frame);
+            if (frame.contains(x, y)) {
+                return getFirstVisiblePosition() + i;
+            }
+        }
+        return INVALID_POSITION;
+    }
+
+    private int getItemForPosition(int y) {
+        int adjustedy = y - mDragPoint - (mItemHeight / 2);
+        int pos = myPointToPosition(0, adjustedy);
+        if (pos >= 0) {
+            if (pos <= mFirstDragPos) {
+                pos += 1;
+            }
+        } else if (adjustedy < 0) {
+            // this shouldn't happen anymore now that myPointToPosition deals
+            // with this situation
+            pos = 0;
+        }
+        return pos;
+    }
+
+    private void adjustScrollBounds(int y) {
+        if (y >= mHeight / 3) {
+            mUpperBound = mHeight / 3;
+        }
+        if (y <= mHeight * 2 / 3) {
+            mLowerBound = mHeight * 2 / 3;
+        }
+    }
+
+    /*
+     * Restore size and visibility for all listitems
+     */
+    private void unExpandViews(boolean deletion) {
+        for (int i = 0;; i++) {
+            View v = getChildAt(i);
+            if (v == null) {
+                if (deletion) {
+                    // HACK force update of mItemCount
+                    int position = getFirstVisiblePosition();
+                    int y = getChildAt(0).getTop();
+                    setAdapter(getAdapter());
+                    setSelectionFromTop(position, y);
+                    // end hack
+                }
+                layoutChildren(); // force children to be recreated where needed
+                v = getChildAt(i);
+                if (v == null) {
+                    break;
+                }
+            }
+            ViewGroup.LayoutParams params = v.getLayoutParams();
+            params.height = mItemHeight;
+            v.setLayoutParams(params);
+            v.setVisibility(View.VISIBLE);
+            // Reset the drawing cache, the positions might have changed.
+            // We don't want the cache to be wrong.
+            v.setDrawingCacheEnabled(false);
+        }
+    }
+
+    /*
+     * Adjust visibility and size to make it appear as though an item is being
+     * dragged around and other items are making room for it: If dropping the
+     * item would result in it still being in the same place, then make the
+     * dragged listitem's size normal, but make the item invisible. Otherwise,
+     * if the dragged listitem is still on screen, make it as small as possible
+     * and expand the item below the insert point. If the dragged item is not on
+     * screen, only expand the item below the current insertpoint.
+     */
+    private void doExpansion() {
+        int childnum = mDragPos - getFirstVisiblePosition();
+        if (mDragPos > mFirstDragPos) {
+            childnum++;
+        }
+
+        View first = getChildAt(mFirstDragPos - getFirstVisiblePosition());
+
+        for (int i = 0;; i++) {
+            View vv = getChildAt(i);
+            if (vv == null) {
+                break;
+            }
+            int height = mItemHeight;
+            int visibility = View.VISIBLE;
+            if (vv.equals(first)) {
+                // processing the item that is being dragged
+                if (mDragPos == mFirstDragPos) {
+                    // hovering over the original location
+                    visibility = View.INVISIBLE;
+                } else {
+                    // not hovering over it
+                    height = 1;
+                }
+            } else if (i == childnum) {
+                if (mDragPos < getCount() - 1) {
+                    height = mItemHeight * 2;
+                }
+            }
+            ViewGroup.LayoutParams params = vv.getLayoutParams();
+            params.height = height;
+            vv.setLayoutParams(params);
+            vv.setVisibility(visibility);
+        }
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent ev) {
+        if ((mDragListener != null || mDropListener != null) && mDragView != null) {
+            int action = ev.getAction();
+            switch (action) {
+                case MotionEvent.ACTION_UP:
+                case MotionEvent.ACTION_CANCEL:
+                    Rect r = mTempRect;
+                    mDragView.getDrawingRect(r);
+                    stopDragging();
+                    if (mDropListener != null && mDragPos >= 0 && mDragPos < getCount()) {
+                        mDropListener.drop(mFirstDragPos, mDragPos);
+                    }
+                    unExpandViews(false);
+                    break;
+
+                case MotionEvent.ACTION_DOWN:
+                case MotionEvent.ACTION_MOVE:
+                    int x = (int) ev.getX();
+                    int y = (int) ev.getY();
+                    dragView(x, y);
+                    int itemnum = getItemForPosition(y);
+                    if (itemnum >= 0) {
+                        if (action == MotionEvent.ACTION_DOWN || itemnum != mDragPos) {
+                            if (mDragListener != null) {
+                                mDragListener.drag(mDragPos, itemnum);
+                            }
+                            mDragPos = itemnum;
+                            doExpansion();
+                        }
+                        int speed = 0;
+                        adjustScrollBounds(y);
+                        if (y > mLowerBound) {
+                            // scroll the list up a bit
+                            speed = y > (mHeight + mLowerBound) / 2 ? 16 : 4;
+                        } else if (y < mUpperBound) {
+                            // scroll the list down a bit
+                            speed = y < mUpperBound / 2 ? -16 : -4;
+                        }
+                        if (speed != 0) {
+                            int ref = pointToPosition(0, mHeight / 2);
+                            if (ref == AdapterView.INVALID_POSITION) {
+                                // we hit a divider or an invisible view, check
+                                // somewhere else
+                                ref = pointToPosition(0, mHeight / 2 + getDividerHeight() + 64);
+                            }
+                            View v = getChildAt(ref - getFirstVisiblePosition());
+                            if (v != null) {
+                                int pos = v.getTop();
+                                setSelectionFromTop(ref, pos - speed);
+                            }
+                        }
+                    }
+                    break;
+            }
+            return true;
+        }
+        return super.onTouchEvent(ev);
+    }
+
+    private void startDragging(Bitmap bm, int x, int y) {
+        stopDragging();
+
+        mWindowParams = new WindowManager.LayoutParams();
+        mWindowParams.gravity = Gravity.TOP | Gravity.LEFT;
+        mWindowParams.x = x;
+        mWindowParams.y = y - mDragPoint + mCoordOffset;
+
+        mWindowParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
+        mWindowParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
+        mWindowParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+                | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
+                | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
+                | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+                | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
+        mWindowParams.format = PixelFormat.TRANSLUCENT;
+        mWindowParams.windowAnimations = 0;
+
+        Context context = getContext();
+        ImageView v = new ImageView(context);
+        int backGroundColor = context.getResources().getColor(R.color.theme_accent);
+        v.setAlpha((float) 0.7);
+        v.setBackgroundColor(backGroundColor);
+        v.setImageBitmap(bm);
+        mDragBitmap = bm;
+
+        mWindowManager = (WindowManager) context.getSystemService("window");
+        mWindowManager.addView(v, mWindowParams);
+        mDragView = v;
+    }
+
+    private void dragView(int x, int y) {
+        mWindowParams.y = y - mDragPoint + mCoordOffset;
+        mWindowManager.updateViewLayout(mDragView, mWindowParams);
+    }
+
+    private void stopDragging() {
+        if (mDragView != null) {
+            mDragView.setVisibility(GONE);
+            WindowManager wm = (WindowManager)getContext().getSystemService(Context.WINDOW_SERVICE);
+            wm.removeView(mDragView);
+            mDragView.setImageDrawable(null);
+            mDragView = null;
+        }
+        if (mDragBitmap != null) {
+            mDragBitmap.recycle();
+            mDragBitmap = null;
+        }
+    }
+
+    public void setDragListener(DragListener l) {
+        mDragListener = l;
+    }
+
+    public void setDropListener(DropListener l) {
+        mDropListener = l;
+    }
+}
diff --git a/src/com/android/settings/wifi/AccessPoint.java b/src/com/android/settings/wifi/AccessPoint.java
index 1bf1a5c..af7a8dc 100644
--- a/src/com/android/settings/wifi/AccessPoint.java
+++ b/src/com/android/settings/wifi/AccessPoint.java
@@ -83,6 +83,7 @@
     private static final int[] STATE_NONE = {};
 
     private static int[] wifi_signal_attributes = { R.attr.wifi_signal };
+    private static int[] wifi_no_signal_attributes = { R.attr.wifi_no_signal };
 
     /**
      * These values are matched in string arrays -- changes must be kept in sync
@@ -118,6 +119,10 @@
     private NetworkInfo mNetworkInfo;
     private TextView mSummaryView;
 
+    private boolean mShowNoSignalIcon;
+    private boolean mNoSignalLoaded;
+    private boolean mSortPreference = true;
+
     private static final int VISIBILITY_MAX_AGE_IN_MILLI = 1000000;
     private static final int VISIBILITY_OUTDATED_AGE_IN_MILLI = 20000;
     private static final int SECOND_TO_MILLI = 1000;
@@ -191,7 +196,13 @@
     }
 
     AccessPoint(Context context, WifiConfiguration config) {
+        this(context, config, false);
+    }
+
+    AccessPoint(Context context, WifiConfiguration config, boolean showNoSignal) {
         super(context);
+        mShowNoSignalIcon = showNoSignal;
+
         loadConfig(config);
         refresh();
     }
@@ -264,11 +275,26 @@
 
     protected void updateIcon(int level, Context context) {
         if (level == -1) {
-            setIcon(null);
+            if (mShowNoSignalIcon) {
+                Drawable drawable = getIcon();
+
+                if (drawable == null || !mNoSignalLoaded) {
+                    StateListDrawable sld = (StateListDrawable) context.getTheme()
+                            .obtainStyledAttributes(wifi_no_signal_attributes).getDrawable(0);
+                    if (sld != null) {
+                        sld.setState((security != SECURITY_NONE) ? STATE_SECURED : STATE_NONE);
+                        setIcon(sld.getCurrent());
+                        mNoSignalLoaded = true;
+                    }
+                }
+            }
+            if (!mNoSignalLoaded) {
+                setIcon(null);
+            }
         } else {
             Drawable drawable = getIcon();
 
-            if (drawable == null) {
+            if (drawable == null || mNoSignalLoaded) {
                 // To avoid a drawing race condition, we first set the state (SECURE/NONE) and then
                 // set the icon (drawable) to that state's drawable.
                 StateListDrawable sld = (StateListDrawable) context.getTheme()
@@ -279,6 +305,7 @@
                     sld.setState((security != SECURITY_NONE) ? STATE_SECURED : STATE_NONE);
                     drawable = sld.getCurrent();
                     setIcon(drawable);
+                    mNoSignalLoaded = false;
                 }
             }
 
@@ -293,6 +320,9 @@
         if (!(preference instanceof AccessPoint)) {
             return 1;
         }
+        if (!mSortPreference) {
+            return super.compareTo(((Preference)preference));
+        }
         AccessPoint other = (AccessPoint) preference;
         // Active one goes first.
         if (isActive() && !other.isActive()) return -1;
@@ -363,6 +393,10 @@
         return false;
     }
 
+    public void setSortPreference(boolean sort) {
+        mSortPreference = sort;
+    }
+
     /** Return whether the given {@link WifiInfo} is for this access point. */
     private boolean isInfoForThisAccessPoint(WifiInfo info) {
         if (networkId != WifiConfiguration.INVALID_NETWORK_ID) {
diff --git a/src/com/android/settings/wifi/SavedAccessPointsWifiSettings.java b/src/com/android/settings/wifi/SavedAccessPointsWifiSettings.java
index bea720c..6887d8c 100644
--- a/src/com/android/settings/wifi/SavedAccessPointsWifiSettings.java
+++ b/src/com/android/settings/wifi/SavedAccessPointsWifiSettings.java
@@ -16,29 +16,43 @@
 
 package com.android.settings.wifi;
 
+import static android.os.UserManager.DISALLOW_CONFIG_WIFI;
+
 import android.app.Dialog;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.res.Resources;
+import android.database.ContentObserver;
+import android.net.Uri;
 import android.net.wifi.ScanResult;
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiManager;
 import android.os.Bundle;
+import android.os.Handler;
 import android.preference.Preference;
-import android.preference.Preference.OnPreferenceClickListener;
 import android.preference.PreferenceScreen;
+import android.provider.Settings;
 
 import com.android.settings.search.BaseSearchIndexProvider;
 import com.android.settings.search.Indexable;
 import com.android.settings.search.SearchIndexableRaw;
 
 import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
 import android.view.View;
+import android.view.ViewGroup;
 
+import com.android.settings.DraggableSortListView;
 import com.android.settings.R;
-import com.android.settings.SettingsPreferenceFragment;
+import com.android.settings.RestrictedSettingsFragment;
 
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -46,18 +60,73 @@
 /**
  * UI to manage saved networks/access points.
  */
-public class SavedAccessPointsWifiSettings extends SettingsPreferenceFragment
+public class SavedAccessPointsWifiSettings extends RestrictedSettingsFragment
         implements DialogInterface.OnClickListener, Indexable {
+
     private static final String TAG = "SavedAccessPointsWifiSettings";
 
+    private DraggableSortListView.DropListener mDropListener =
+            new DraggableSortListView.DropListener() {
+        @Override
+        public void drop(int from, int to) {
+            if (from == to) return;
+
+            PreferenceScreen preferences = getPreferenceScreen();
+            int count = preferences.getPreferenceCount();
+
+            // Sort the new list
+            List<AccessPoint> aps = new ArrayList<>(count);
+            for (int i = 0; i < count; i++) {
+                aps.add((AccessPoint) preferences.getPreference(i));
+            }
+            AccessPoint o = aps.remove(from);
+            aps.add(to, o);
+
+            // Update the priorities
+            for (int i = 0; i < count; i++) {
+                AccessPoint ap = aps.get(i);
+                WifiConfiguration config = ap.getConfig();
+                config.priority = count - i;
+
+                mWifiManager.updateNetwork(config);
+            }
+
+            // Now, save all the Wi-Fi configuration with its new priorities
+            mWifiManager.saveConfiguration();
+            mPrioritiesOrderChanged = true;
+
+            // Redraw the listview
+            initPreferences();
+        }
+    };
+
+    private final ContentObserver mSettingsObserver = new ContentObserver(new Handler()) {
+        @Override
+        public void onChange(boolean selfChange, Uri uri) {
+            mNetworksListView.setDropListener(isAutoConfigPriorities() ? null : mDropListener);
+            getActivity().invalidateOptionsMenu();
+        }
+    };
+
+
+    private static final int MENU_ID_AUTO_CONFIG_PRIORITIES = Menu.FIRST;
+
     private WifiDialog mDialog;
     private WifiManager mWifiManager;
     private AccessPoint mDlgAccessPoint;
     private Bundle mAccessPointSavedState;
     private AccessPoint mSelectedAccessPoint;
+    private boolean mPrioritiesOrderChanged;
+
+    private DraggableSortListView mNetworksListView;
 
     // Instance state key
     private static final String SAVE_DIALOG_ACCESS_POINT_STATE = "wifi_ap_state";
+    private static final String PRIORITIES_ORDER_CHANGED_STATE = "priorities_order_changed";
+
+    public SavedAccessPointsWifiSettings() {
+        super(DISALLOW_CONFIG_WIFI);
+    }
 
     @Override
     public void onCreate(Bundle savedInstanceState) {
@@ -69,6 +138,27 @@
     public void onResume() {
         super.onResume();
         initPreferences();
+
+        mNetworksListView.setDropListener(isAutoConfigPriorities() ? null : mDropListener);
+        getActivity().invalidateOptionsMenu();
+        ContentResolver resolver = getContentResolver();
+        resolver.registerContentObserver(Settings.Global.getUriFor(
+                Settings.Global.WIFI_AUTO_PRIORITIES_CONFIGURATION), false, mSettingsObserver);
+    }
+
+    @Override
+    public void onPause() {
+        super.onResume();
+        getContentResolver().unregisterContentObserver(mSettingsObserver);
+    }
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+            Bundle savedInstanceState) {
+        mNetworksListView = new DraggableSortListView(getActivity());
+        mNetworksListView.setId(android.R.id.list);
+        mNetworksListView.setDropListener(isAutoConfigPriorities() ? null : mDropListener);
+        return mNetworksListView;
     }
 
     @Override
@@ -81,7 +171,68 @@
                 mAccessPointSavedState =
                     savedInstanceState.getBundle(SAVE_DIALOG_ACCESS_POINT_STATE);
             }
+            mPrioritiesOrderChanged = savedInstanceState.getBoolean(
+                    PRIORITIES_ORDER_CHANGED_STATE, false);
         }
+
+        registerForContextMenu(getListView());
+        setHasOptionsMenu(true);
+    }
+
+    @Override
+    public void onDetach() {
+        super.onDetach();
+
+        if (mPrioritiesOrderChanged) {
+            // Send a disconnect to ensure the new wifi priorities are detected
+            mWifiManager.disconnect();
+        }
+    }
+
+    @Override
+    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+        // If the user is not allowed to configure wifi, do not show the menu.
+        if (isUiRestricted()) return;
+
+        addOptionsMenuItems(menu);
+        super.onCreateOptionsMenu(menu, inflater);
+    }
+
+    void addOptionsMenuItems(Menu menu) {
+        menu.add(Menu.NONE, MENU_ID_AUTO_CONFIG_PRIORITIES, 0, R.string.wifi_auto_config_priorities)
+                .setCheckable(true)
+                .setChecked(isAutoConfigPriorities())
+                .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        // If the user is not allowed to configure wifi, do not handle menu selections.
+        if (isUiRestricted()) return false;
+
+        switch (item.getItemId()) {
+            case MENU_ID_AUTO_CONFIG_PRIORITIES:
+                boolean autoConfig = !item.isChecked();
+
+                // Set the system settings and refresh the listview
+                Settings.Global.putInt(getActivity().getContentResolver(),
+                        Settings.Global.WIFI_AUTO_PRIORITIES_CONFIGURATION, autoConfig ? 1 : 0);
+                mNetworksListView.setDropListener(autoConfig ? null : mDropListener);
+                item.setChecked(autoConfig);
+
+                if (!autoConfig) {
+                    // Reenable all the entries
+                    PreferenceScreen preferences = getPreferenceScreen();
+                    int count = preferences.getPreferenceCount();
+                    for (int i = 0; i < count; i++) {
+                        AccessPoint ap = (AccessPoint) preferences.getPreference(i);
+                        WifiConfiguration config = ap.getConfig();
+                        mWifiManager.enableNetwork(config.networkId, false);
+                    }
+                }
+                return true;
+        }
+        return super.onOptionsItemSelected(item);
     }
 
     private void initPreferences() {
@@ -91,11 +242,14 @@
         mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
         final List<AccessPoint> accessPoints = constructSavedAccessPoints(context, mWifiManager);
 
+        preferenceScreen.setOrderingAsAdded(false);
         preferenceScreen.removeAll();
 
         final int accessPointsSize = accessPoints.size();
         for (int i = 0; i < accessPointsSize; ++i){
-            preferenceScreen.addPreference(accessPoints.get(i));
+            final AccessPoint ap = accessPoints.get(i);
+            ap.setOrder(i);
+            preferenceScreen.addPreference(ap);
         }
 
         if(getPreferenceScreen().getPreferenceCount() < 1) {
@@ -103,12 +257,20 @@
         }
     }
 
+    private static List<WifiConfiguration> getConfiguredNetworks(WifiManager wifiManager) {
+        List<WifiConfiguration> networks = wifiManager.getConfiguredNetworks();
+        if (networks == null) {
+            networks = new ArrayList<WifiConfiguration>();
+        }
+        return networks;
+    }
+
     private static List<AccessPoint> constructSavedAccessPoints(Context context,
             WifiManager wifiManager){
         List<AccessPoint> accessPoints = new ArrayList<AccessPoint>();
         Map<String, List<ScanResult>> resultsMap = new HashMap<String, List<ScanResult>>();
 
-        final List<WifiConfiguration> configs = wifiManager.getConfiguredNetworks();
+        final List<WifiConfiguration> configs = getConfiguredNetworks(wifiManager);
         final List<ScanResult> scanResults = wifiManager.getScanResults();
 
         if (configs != null) {
@@ -132,22 +294,39 @@
                 if (config.selfAdded && config.numAssociation == 0) {
                     continue;
                 }
-                AccessPoint accessPoint = new AccessPoint(context, config);
-                final List<ScanResult> results = resultsMap.get(accessPoint.ssid);
+                AccessPoint accessPoint = new AccessPoint(context, config, true);
+                accessPoint.setSortPreference(false);;
 
-                accessPoint.setShowSummary(false);
+                final List<ScanResult> results = resultsMap.get(accessPoint.ssid);
                 if(results != null){
                     final int resultsSize = results.size();
                     for (int j = 0; j < resultsSize; ++j){
                         accessPoint.update(results.get(j));
-                        accessPoint.setIcon(null);
                     }
                 }
 
+                accessPoint.setShowSummary(true);
                 accessPoints.add(accessPoint);
             }
         }
 
+        // Sort network list by priority (or by network id if the priority is the same)
+        Collections.sort(accessPoints, new Comparator<AccessPoint>() {
+            @Override
+            public int compare(AccessPoint lhs, AccessPoint rhs) {
+                WifiConfiguration lwc = lhs.getConfig();
+                WifiConfiguration rwc = rhs.getConfig();
+
+                // > priority -- > lower position
+                if (lwc.priority < rwc.priority) return 1;
+                if (lwc.priority > rwc.priority) return -1;
+                // < network id -- > lower position
+                if (lhs.networkId < rhs.networkId) return -1;
+                if (lhs.networkId > rhs.networkId) return 1;
+                return 0;
+            }
+        });
+
         return accessPoints;
     }
 
@@ -193,6 +372,7 @@
                 outState.putBundle(SAVE_DIALOG_ACCESS_POINT_STATE, mAccessPointSavedState);
             }
         }
+        outState.putBoolean(PRIORITIES_ORDER_CHANGED_STATE, mPrioritiesOrderChanged);
     }
 
     @Override
@@ -214,6 +394,11 @@
         }
     }
 
+    private boolean isAutoConfigPriorities() {
+        return Settings.Global.getInt(getActivity().getContentResolver(),
+                Settings.Global.WIFI_AUTO_PRIORITIES_CONFIGURATION, 1) != 0;
+    }
+
     /**
      * For search.
      */