diff options
author | Jorge Ruesga <jorge@ruesga.com> | 2015-06-04 01:01:52 +0200 |
---|---|---|
committer | Steve Kondik <shade@chemlab.org> | 2015-12-04 19:10:37 -0800 |
commit | 5f546bf6a98684a20f297bccaa459b5e1ca4216d (patch) | |
tree | 6807c37652fe0624de83a1072841d579314185af | |
parent | ab432ed87bc1d8307cc8d25eb141b6f158c604e2 (diff) | |
download | packages_apps_Settings-5f546bf6a98684a20f297bccaa459b5e1ca4216d.zip packages_apps_Settings-5f546bf6a98684a20f297bccaa459b5e1ca4216d.tar.gz packages_apps_Settings-5f546bf6a98684a20f297bccaa459b5e1ca4216d.tar.bz2 |
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>
-rw-r--r-- | res/values/cm_strings.xml | 3 | ||||
-rw-r--r-- | res/values/themes.xml | 1 | ||||
-rw-r--r-- | src/com/android/settings/DraggableSortListView.java | 361 | ||||
-rw-r--r-- | src/com/android/settings/wifi/SavedAccessPointsWifiSettings.java | 182 |
4 files changed, 540 insertions, 7 deletions
diff --git a/res/values/cm_strings.xml b/res/values/cm_strings.xml index eb32615..099e7ca 100644 --- a/res/values/cm_strings.xml +++ b/res/values/cm_strings.xml @@ -501,6 +501,9 @@ <string name="headset_connect_player_title">Launch music app</string> <string name="headset_connect_player_summary">Launch the default music app when headset is connected</string> + <!-- WiFi auto-configure priorities --> + <string name="wifi_auto_config_priorities">Automatic priority</string> + <string name="auto_brightness_reset_button">Reset</string> <!-- Display : Rotation --> diff --git a/res/values/themes.xml b/res/values/themes.xml index 9759468..b2291f2 100644 --- a/res/values/themes.xml +++ b/res/values/themes.xml @@ -25,6 +25,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 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/SavedAccessPointsWifiSettings.java b/src/com/android/settings/wifi/SavedAccessPointsWifiSettings.java index 45aafdf..d50ebfc 100644 --- a/src/com/android/settings/wifi/SavedAccessPointsWifiSettings.java +++ b/src/com/android/settings/wifi/SavedAccessPointsWifiSettings.java @@ -16,18 +16,33 @@ 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.WifiConfiguration; import android.net.wifi.WifiManager; import android.os.Bundle; +import android.os.Handler; import android.preference.Preference; import android.preference.PreferenceScreen; 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.internal.logging.MetricsLogger; +import com.android.settings.DraggableSortListView; import com.android.settings.R; +import com.android.settings.RestrictedSettingsFragment; import com.android.settings.SettingsPreferenceFragment; import com.android.settings.search.BaseSearchIndexProvider; import com.android.settings.search.Indexable; @@ -35,6 +50,7 @@ import com.android.settings.search.SearchIndexableRaw; import com.android.settings.wifi.AccessPointPreference.UserBadgeCache; import com.android.settingslib.wifi.AccessPoint; import com.android.settingslib.wifi.WifiTracker; +import cyanogenmod.providers.CMSettings; import java.util.ArrayList; import java.util.Collections; @@ -44,20 +60,74 @@ import java.util.List; /** * 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<AccessPointPreference> aps = new ArrayList<>(count); + for (int i = 0; i < count; i++) { + aps.add((AccessPointPreference) preferences.getPreference(i)); + } + AccessPointPreference o = aps.remove(from); + aps.add(to, o); + + // Update the priorities + for (int i = 0; i < count; i++) { + AccessPoint ap = aps.get(i).getAccessPoint(); + 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 UserBadgeCache mUserBadgeCache; + 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 protected int getMetricsCategory() { @@ -75,6 +145,27 @@ public class SavedAccessPointsWifiSettings extends SettingsPreferenceFragment public void onResume() { super.onResume(); initPreferences(); + + mNetworksListView.setDropListener(isAutoConfigPriorities() ? null : mDropListener); + getActivity().invalidateOptionsMenu(); + ContentResolver resolver = getContentResolver(); + resolver.registerContentObserver(CMSettings.Global.getUriFor( + CMSettings.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 @@ -87,7 +178,69 @@ public class SavedAccessPointsWifiSettings extends SettingsPreferenceFragment 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 + CMSettings.Global.putInt(getActivity().getContentResolver(), + CMSettings.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 = ((AccessPointPreference) + preferences.getPreference(i)).getAccessPoint(); + WifiConfiguration config = ap.getConfig(); + mWifiManager.enableNetwork(config.networkId, false); + } + } + return true; + } + return super.onOptionsItemSelected(item); } private void initPreferences() { @@ -96,15 +249,23 @@ public class SavedAccessPointsWifiSettings extends SettingsPreferenceFragment final List<AccessPoint> accessPoints = WifiTracker.getCurrentAccessPoints(context, true, false, true); + // Sort network list by priority (or by network id if the priority is the same) Collections.sort(accessPoints, new Comparator<AccessPoint>() { - public int compare(AccessPoint ap1, AccessPoint ap2) { - if (ap1.getConfigName() != null) { - return ap1.getConfigName().compareTo(ap2.getConfigName()); - } else { - return -1; - } + @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.getNetworkId() < rhs.getNetworkId()) return -1; + if (lhs.getNetworkId() > rhs.getNetworkId()) return 1; + return 0; } }); + preferenceScreen.setOrderingAsAdded(false); preferenceScreen.removeAll(); final int accessPointsSize = accessPoints.size(); @@ -112,6 +273,7 @@ public class SavedAccessPointsWifiSettings extends SettingsPreferenceFragment AccessPointPreference preference = new AccessPointPreference(accessPoints.get(i), context, mUserBadgeCache, true); preference.setIcon(null); + preference.setOrder(i); preferenceScreen.addPreference(preference); } @@ -167,6 +329,7 @@ public class SavedAccessPointsWifiSettings extends SettingsPreferenceFragment outState.putBundle(SAVE_DIALOG_ACCESS_POINT_STATE, mAccessPointSavedState); } } + outState.putBoolean(PRIORITIES_ORDER_CHANGED_STATE, mPrioritiesOrderChanged); } @Override @@ -188,6 +351,11 @@ public class SavedAccessPointsWifiSettings extends SettingsPreferenceFragment } } + private boolean isAutoConfigPriorities() { + return CMSettings.Global.getInt(getActivity().getContentResolver(), + CMSettings.Global.WIFI_AUTO_PRIORITIES_CONFIGURATION, 1) != 0; + } + /** * For search. */ |