Loading src/com/android/settings/slices/CustomSliceable.java +10 −0 Original line number Diff line number Diff line Loading @@ -89,6 +89,16 @@ public interface CustomSliceable { return null; } /** * Settings Slices which can represent component lists that are updatable by the * {@link SliceBackgroundWorker} returned here. * * @return a {@link SliceBackgroundWorker} for fetching the list of results in the background. */ default SliceBackgroundWorker getBackgroundWorker() { return null; } /** * Standardize the intents returned to indicate actions by the Slice. * <p> Loading src/com/android/settings/slices/SettingsSliceProvider.java +30 −0 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ import android.os.StrictMode; import android.provider.Settings; import android.provider.SettingsSlicesContract; import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.KeyValueListParser; import android.util.Log; Loading Loading @@ -132,6 +133,8 @@ public class SettingsSliceProvider extends SliceProvider { final Set<Uri> mRegisteredUris = new ArraySet<>(); final Map<Uri, SliceBackgroundWorker> mWorkerMap = new ArrayMap<>(); public SettingsSliceProvider() { super(READ_SEARCH_INDEXABLES); } Loading Loading @@ -166,6 +169,7 @@ public class SettingsSliceProvider extends SliceProvider { if (filter != null) { registerIntentToUri(filter, sliceUri); } startBackgroundWorker(sliceable); return; } Loading Loading @@ -194,6 +198,7 @@ public class SettingsSliceProvider extends SliceProvider { SliceBroadcastRelay.unregisterReceivers(getContext(), sliceUri); mRegisteredUris.remove(sliceUri); } stopBackgroundWorker(sliceUri); mSliceDataCache.remove(sliceUri); } Loading Loading @@ -353,6 +358,31 @@ public class SettingsSliceProvider extends SliceProvider { } } private void startBackgroundWorker(CustomSliceable sliceable) { final SliceBackgroundWorker worker = sliceable.getBackgroundWorker(); if (worker == null) { return; } final Uri uri = sliceable.getUri(); Log.d(TAG, "Starting background worker for: " + uri); if (mWorkerMap.containsKey(uri)) { return; } mWorkerMap.put(uri, worker); worker.onSlicePinned(); } private void stopBackgroundWorker(Uri uri) { final SliceBackgroundWorker worker = mWorkerMap.get(uri); if (worker != null) { Log.d(TAG, "Stopping background worker for: " + uri); worker.onSliceUnpinned(); mWorkerMap.remove(uri); } } private List<Uri> buildUrisFromKeys(List<String> keys, String authority) { final List<Uri> descendants = new ArrayList<>(); Loading src/com/android/settings/slices/SliceBackgroundWorker.java 0 → 100644 +85 −0 Original line number Diff line number Diff line /* * Copyright (C) 2018 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.slices; import android.content.ContentResolver; import android.net.Uri; import java.util.ArrayList; import java.util.List; /** * The Slice background worker is used to make Settings Slices be able to work with data that is * changing continuously, e.g. available Wi-Fi networks. * * The background worker will be started at {@link SettingsSliceProvider#onSlicePinned(Uri)}, and be * stopped at {@link SettingsSliceProvider#onSliceUnpinned(Uri)}. * * {@link SliceBackgroundWorker} caches the results, uses the cache to compare if there is any data * changed, and then notifies the Slice {@link Uri} to update. */ public abstract class SliceBackgroundWorker<E> { private final ContentResolver mContentResolver; private final Uri mUri; private List<E> mCachedResults; protected SliceBackgroundWorker(ContentResolver cr, Uri uri) { mContentResolver = cr; mUri = uri; } /** * Called when the Slice is pinned. This is the place to register callbacks or initialize scan * tasks. */ protected abstract void onSlicePinned(); /** * Called when the Slice is unpinned. This is the place to unregister callbacks or perform any * final cleanup. */ protected abstract void onSliceUnpinned(); /** * @return a {@link List} of cached results */ public final List<E> getResults() { return mCachedResults == null ? null : new ArrayList<>(mCachedResults); } /** * Update the results when data changes */ protected final void updateResults(List<E> results) { boolean needNotify = false; if (results == null) { if (mCachedResults != null) { needNotify = true; } } else { needNotify = !results.equals(mCachedResults); } if (needNotify) { mCachedResults = results; mContentResolver.notifyChange(mUri, null); } } } src/com/android/settings/wifi/WifiDialogActivity.java +5 −6 Original line number Diff line number Diff line Loading @@ -37,12 +37,6 @@ public class WifiDialogActivity extends Activity implements WifiDialog.WifiDialo private static final String TAG = "WifiDialogActivity"; private static final int RESULT_CONNECTED = RESULT_FIRST_USER; private static final int RESULT_FORGET = RESULT_FIRST_USER + 1; private static final String KEY_ACCESS_POINT_STATE = "access_point_state"; private static final String KEY_WIFI_CONFIGURATION = "wifi_configuration"; /** * Boolean extra indicating whether this activity should connect to an access point on the * caller's behalf. If this is set to false, the caller should check Loading @@ -51,6 +45,11 @@ public class WifiDialogActivity extends Activity implements WifiDialog.WifiDialo */ @VisibleForTesting static final String KEY_CONNECT_FOR_CALLER = "connect_for_caller"; static final String KEY_ACCESS_POINT_STATE = "access_point_state"; private static final String KEY_WIFI_CONFIGURATION = "wifi_configuration"; private static final int RESULT_CONNECTED = RESULT_FIRST_USER; private static final int RESULT_FORGET = RESULT_FIRST_USER + 1; private WifiDialog mDialog; Loading src/com/android/settings/wifi/WifiSlice.java +144 −3 Original line number Diff line number Diff line Loading @@ -29,6 +29,9 @@ import android.net.Uri; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; import android.net.wifi.WifiSsid; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.provider.SettingsSlicesContract; import android.text.TextUtils; Loading @@ -42,8 +45,16 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.settings.R; import com.android.settings.SubSettings; import com.android.settings.Utils; import com.android.settings.core.SubSettingLauncher; import com.android.settings.slices.CustomSliceable; import com.android.settings.slices.SliceBackgroundWorker; import com.android.settings.slices.SliceBuilderUtils; import com.android.settings.wifi.details.WifiNetworkDetailsFragment; import com.android.settingslib.wifi.AccessPoint; import com.android.settingslib.wifi.WifiTracker; import java.util.ArrayList; import java.util.List; /** * Utility class to build a Wifi Slice, and handle all associated actions. Loading Loading @@ -96,14 +107,73 @@ public class WifiSlice implements CustomSliceable { final SliceAction toggleSliceAction = new SliceAction(toggleAction, null /* actionTitle */, isWifiEnabled); return new ListBuilder(mContext, WIFI_URI, ListBuilder.INFINITY) final ListBuilder listBuilder = new ListBuilder(mContext, WIFI_URI, ListBuilder.INFINITY) .setAccentColor(color) .addRow(new RowBuilder() .setTitle(title) .setSubtitle(summary) .addEndItem(toggleSliceAction) .setPrimaryAction(primarySliceAction)) .build(); .setPrimaryAction(primarySliceAction)); if (isWifiEnabled) { final List<AccessPoint> result = getBackgroundWorker().getResults(); if (result != null && !result.isEmpty()) { for (AccessPoint ap : result) { listBuilder.addRow(getAccessPointRow(ap)); } listBuilder.setSeeMoreAction(primaryAction); } } return listBuilder.build(); } private RowBuilder getAccessPointRow(AccessPoint accessPoint) { final String title = accessPoint.getConfigName(); final IconCompat levelIcon = IconCompat.createWithResource(mContext, com.android.settingslib.Utils.getWifiIconResource(accessPoint.getLevel())); final RowBuilder rowBuilder = new RowBuilder() .setTitleItem(levelIcon, ListBuilder.ICON_IMAGE) .setTitle(title) .setSubtitle(accessPoint.getSettingsSummary()) .setPrimaryAction(new SliceAction( getAccessPointAction(accessPoint), levelIcon, title)); final IconCompat endIcon = getEndIcon(accessPoint); if (endIcon != null) { rowBuilder.addEndItem(endIcon, ListBuilder.ICON_IMAGE); } return rowBuilder; } private IconCompat getEndIcon(AccessPoint accessPoint) { if (accessPoint.isActive()) { return IconCompat.createWithResource(mContext, R.drawable.ic_settings); } else if (accessPoint.getSecurity() != AccessPoint.SECURITY_NONE) { return IconCompat.createWithResource(mContext, R.drawable.ic_friction_lock_closed); } else if (accessPoint.isMetered()) { return IconCompat.createWithResource(mContext, R.drawable.ic_friction_money); } return null; } private PendingIntent getAccessPointAction(AccessPoint accessPoint) { final Bundle extras = new Bundle(); accessPoint.saveWifiState(extras); Intent intent; if (accessPoint.isActive()) { intent = new SubSettingLauncher(mContext) .setTitleRes(R.string.pref_title_network_details) .setDestination(WifiNetworkDetailsFragment.class.getName()) .setArguments(extras) .setSourceMetricsCategory(MetricsEvent.WIFI) .toIntent(); } else { intent = new Intent(mContext, WifiDialogActivity.class); intent.putExtra(WifiDialogActivity.KEY_ACCESS_POINT_STATE, extras); } return PendingIntent.getActivity(mContext, accessPoint.hashCode() /* requestCode */, intent, 0 /* flags */); } /** Loading Loading @@ -176,4 +246,75 @@ public class WifiSlice implements CustomSliceable { return PendingIntent.getActivity(mContext, 0 /* requestCode */, intent, 0 /* flags */); } @Override public SliceBackgroundWorker getBackgroundWorker() { return WifiScanWorker.getInstance(mContext, WIFI_URI); } private static class WifiScanWorker extends SliceBackgroundWorker<AccessPoint> implements WifiTracker.WifiListener { private static WifiScanWorker mWifiScanWorker; private final Context mContext; private WifiTracker mWifiTracker; private WifiManager mWifiManager; private WifiScanWorker(Context context, Uri uri) { super(context.getContentResolver(), uri); mContext = context; } public static WifiScanWorker getInstance(Context context, Uri uri) { if (mWifiScanWorker == null) { mWifiScanWorker = new WifiScanWorker(context, uri); } return mWifiScanWorker; } @Override protected void onSlicePinned() { new Handler(Looper.getMainLooper()).post(() -> { mWifiTracker = new WifiTracker(mContext, this, true, true); mWifiManager = mWifiTracker.getManager(); mWifiTracker.onStart(); onAccessPointsChanged(); }); } @Override protected void onSliceUnpinned() { mWifiTracker.onStop(); mWifiTracker.onDestroy(); mWifiScanWorker = null; } @Override public void onWifiStateChanged(int state) { } @Override public void onConnectedChanged() { } @Override public void onAccessPointsChanged() { // in case state has changed if (!mWifiManager.isWifiEnabled()) { updateResults(null); return; } // AccessPoints are sorted by the WifiTracker final List<AccessPoint> accessPoints = mWifiTracker.getAccessPoints(); final List<AccessPoint> resultList = new ArrayList<>(); for (AccessPoint ap : accessPoints) { if (ap.isReachable()) { resultList.add(ap); } } updateResults(resultList); } } } Loading
src/com/android/settings/slices/CustomSliceable.java +10 −0 Original line number Diff line number Diff line Loading @@ -89,6 +89,16 @@ public interface CustomSliceable { return null; } /** * Settings Slices which can represent component lists that are updatable by the * {@link SliceBackgroundWorker} returned here. * * @return a {@link SliceBackgroundWorker} for fetching the list of results in the background. */ default SliceBackgroundWorker getBackgroundWorker() { return null; } /** * Standardize the intents returned to indicate actions by the Slice. * <p> Loading
src/com/android/settings/slices/SettingsSliceProvider.java +30 −0 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ import android.os.StrictMode; import android.provider.Settings; import android.provider.SettingsSlicesContract; import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.KeyValueListParser; import android.util.Log; Loading Loading @@ -132,6 +133,8 @@ public class SettingsSliceProvider extends SliceProvider { final Set<Uri> mRegisteredUris = new ArraySet<>(); final Map<Uri, SliceBackgroundWorker> mWorkerMap = new ArrayMap<>(); public SettingsSliceProvider() { super(READ_SEARCH_INDEXABLES); } Loading Loading @@ -166,6 +169,7 @@ public class SettingsSliceProvider extends SliceProvider { if (filter != null) { registerIntentToUri(filter, sliceUri); } startBackgroundWorker(sliceable); return; } Loading Loading @@ -194,6 +198,7 @@ public class SettingsSliceProvider extends SliceProvider { SliceBroadcastRelay.unregisterReceivers(getContext(), sliceUri); mRegisteredUris.remove(sliceUri); } stopBackgroundWorker(sliceUri); mSliceDataCache.remove(sliceUri); } Loading Loading @@ -353,6 +358,31 @@ public class SettingsSliceProvider extends SliceProvider { } } private void startBackgroundWorker(CustomSliceable sliceable) { final SliceBackgroundWorker worker = sliceable.getBackgroundWorker(); if (worker == null) { return; } final Uri uri = sliceable.getUri(); Log.d(TAG, "Starting background worker for: " + uri); if (mWorkerMap.containsKey(uri)) { return; } mWorkerMap.put(uri, worker); worker.onSlicePinned(); } private void stopBackgroundWorker(Uri uri) { final SliceBackgroundWorker worker = mWorkerMap.get(uri); if (worker != null) { Log.d(TAG, "Stopping background worker for: " + uri); worker.onSliceUnpinned(); mWorkerMap.remove(uri); } } private List<Uri> buildUrisFromKeys(List<String> keys, String authority) { final List<Uri> descendants = new ArrayList<>(); Loading
src/com/android/settings/slices/SliceBackgroundWorker.java 0 → 100644 +85 −0 Original line number Diff line number Diff line /* * Copyright (C) 2018 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.slices; import android.content.ContentResolver; import android.net.Uri; import java.util.ArrayList; import java.util.List; /** * The Slice background worker is used to make Settings Slices be able to work with data that is * changing continuously, e.g. available Wi-Fi networks. * * The background worker will be started at {@link SettingsSliceProvider#onSlicePinned(Uri)}, and be * stopped at {@link SettingsSliceProvider#onSliceUnpinned(Uri)}. * * {@link SliceBackgroundWorker} caches the results, uses the cache to compare if there is any data * changed, and then notifies the Slice {@link Uri} to update. */ public abstract class SliceBackgroundWorker<E> { private final ContentResolver mContentResolver; private final Uri mUri; private List<E> mCachedResults; protected SliceBackgroundWorker(ContentResolver cr, Uri uri) { mContentResolver = cr; mUri = uri; } /** * Called when the Slice is pinned. This is the place to register callbacks or initialize scan * tasks. */ protected abstract void onSlicePinned(); /** * Called when the Slice is unpinned. This is the place to unregister callbacks or perform any * final cleanup. */ protected abstract void onSliceUnpinned(); /** * @return a {@link List} of cached results */ public final List<E> getResults() { return mCachedResults == null ? null : new ArrayList<>(mCachedResults); } /** * Update the results when data changes */ protected final void updateResults(List<E> results) { boolean needNotify = false; if (results == null) { if (mCachedResults != null) { needNotify = true; } } else { needNotify = !results.equals(mCachedResults); } if (needNotify) { mCachedResults = results; mContentResolver.notifyChange(mUri, null); } } }
src/com/android/settings/wifi/WifiDialogActivity.java +5 −6 Original line number Diff line number Diff line Loading @@ -37,12 +37,6 @@ public class WifiDialogActivity extends Activity implements WifiDialog.WifiDialo private static final String TAG = "WifiDialogActivity"; private static final int RESULT_CONNECTED = RESULT_FIRST_USER; private static final int RESULT_FORGET = RESULT_FIRST_USER + 1; private static final String KEY_ACCESS_POINT_STATE = "access_point_state"; private static final String KEY_WIFI_CONFIGURATION = "wifi_configuration"; /** * Boolean extra indicating whether this activity should connect to an access point on the * caller's behalf. If this is set to false, the caller should check Loading @@ -51,6 +45,11 @@ public class WifiDialogActivity extends Activity implements WifiDialog.WifiDialo */ @VisibleForTesting static final String KEY_CONNECT_FOR_CALLER = "connect_for_caller"; static final String KEY_ACCESS_POINT_STATE = "access_point_state"; private static final String KEY_WIFI_CONFIGURATION = "wifi_configuration"; private static final int RESULT_CONNECTED = RESULT_FIRST_USER; private static final int RESULT_FORGET = RESULT_FIRST_USER + 1; private WifiDialog mDialog; Loading
src/com/android/settings/wifi/WifiSlice.java +144 −3 Original line number Diff line number Diff line Loading @@ -29,6 +29,9 @@ import android.net.Uri; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; import android.net.wifi.WifiSsid; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.provider.SettingsSlicesContract; import android.text.TextUtils; Loading @@ -42,8 +45,16 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.settings.R; import com.android.settings.SubSettings; import com.android.settings.Utils; import com.android.settings.core.SubSettingLauncher; import com.android.settings.slices.CustomSliceable; import com.android.settings.slices.SliceBackgroundWorker; import com.android.settings.slices.SliceBuilderUtils; import com.android.settings.wifi.details.WifiNetworkDetailsFragment; import com.android.settingslib.wifi.AccessPoint; import com.android.settingslib.wifi.WifiTracker; import java.util.ArrayList; import java.util.List; /** * Utility class to build a Wifi Slice, and handle all associated actions. Loading Loading @@ -96,14 +107,73 @@ public class WifiSlice implements CustomSliceable { final SliceAction toggleSliceAction = new SliceAction(toggleAction, null /* actionTitle */, isWifiEnabled); return new ListBuilder(mContext, WIFI_URI, ListBuilder.INFINITY) final ListBuilder listBuilder = new ListBuilder(mContext, WIFI_URI, ListBuilder.INFINITY) .setAccentColor(color) .addRow(new RowBuilder() .setTitle(title) .setSubtitle(summary) .addEndItem(toggleSliceAction) .setPrimaryAction(primarySliceAction)) .build(); .setPrimaryAction(primarySliceAction)); if (isWifiEnabled) { final List<AccessPoint> result = getBackgroundWorker().getResults(); if (result != null && !result.isEmpty()) { for (AccessPoint ap : result) { listBuilder.addRow(getAccessPointRow(ap)); } listBuilder.setSeeMoreAction(primaryAction); } } return listBuilder.build(); } private RowBuilder getAccessPointRow(AccessPoint accessPoint) { final String title = accessPoint.getConfigName(); final IconCompat levelIcon = IconCompat.createWithResource(mContext, com.android.settingslib.Utils.getWifiIconResource(accessPoint.getLevel())); final RowBuilder rowBuilder = new RowBuilder() .setTitleItem(levelIcon, ListBuilder.ICON_IMAGE) .setTitle(title) .setSubtitle(accessPoint.getSettingsSummary()) .setPrimaryAction(new SliceAction( getAccessPointAction(accessPoint), levelIcon, title)); final IconCompat endIcon = getEndIcon(accessPoint); if (endIcon != null) { rowBuilder.addEndItem(endIcon, ListBuilder.ICON_IMAGE); } return rowBuilder; } private IconCompat getEndIcon(AccessPoint accessPoint) { if (accessPoint.isActive()) { return IconCompat.createWithResource(mContext, R.drawable.ic_settings); } else if (accessPoint.getSecurity() != AccessPoint.SECURITY_NONE) { return IconCompat.createWithResource(mContext, R.drawable.ic_friction_lock_closed); } else if (accessPoint.isMetered()) { return IconCompat.createWithResource(mContext, R.drawable.ic_friction_money); } return null; } private PendingIntent getAccessPointAction(AccessPoint accessPoint) { final Bundle extras = new Bundle(); accessPoint.saveWifiState(extras); Intent intent; if (accessPoint.isActive()) { intent = new SubSettingLauncher(mContext) .setTitleRes(R.string.pref_title_network_details) .setDestination(WifiNetworkDetailsFragment.class.getName()) .setArguments(extras) .setSourceMetricsCategory(MetricsEvent.WIFI) .toIntent(); } else { intent = new Intent(mContext, WifiDialogActivity.class); intent.putExtra(WifiDialogActivity.KEY_ACCESS_POINT_STATE, extras); } return PendingIntent.getActivity(mContext, accessPoint.hashCode() /* requestCode */, intent, 0 /* flags */); } /** Loading Loading @@ -176,4 +246,75 @@ public class WifiSlice implements CustomSliceable { return PendingIntent.getActivity(mContext, 0 /* requestCode */, intent, 0 /* flags */); } @Override public SliceBackgroundWorker getBackgroundWorker() { return WifiScanWorker.getInstance(mContext, WIFI_URI); } private static class WifiScanWorker extends SliceBackgroundWorker<AccessPoint> implements WifiTracker.WifiListener { private static WifiScanWorker mWifiScanWorker; private final Context mContext; private WifiTracker mWifiTracker; private WifiManager mWifiManager; private WifiScanWorker(Context context, Uri uri) { super(context.getContentResolver(), uri); mContext = context; } public static WifiScanWorker getInstance(Context context, Uri uri) { if (mWifiScanWorker == null) { mWifiScanWorker = new WifiScanWorker(context, uri); } return mWifiScanWorker; } @Override protected void onSlicePinned() { new Handler(Looper.getMainLooper()).post(() -> { mWifiTracker = new WifiTracker(mContext, this, true, true); mWifiManager = mWifiTracker.getManager(); mWifiTracker.onStart(); onAccessPointsChanged(); }); } @Override protected void onSliceUnpinned() { mWifiTracker.onStop(); mWifiTracker.onDestroy(); mWifiScanWorker = null; } @Override public void onWifiStateChanged(int state) { } @Override public void onConnectedChanged() { } @Override public void onAccessPointsChanged() { // in case state has changed if (!mWifiManager.isWifiEnabled()) { updateResults(null); return; } // AccessPoints are sorted by the WifiTracker final List<AccessPoint> accessPoints = mWifiTracker.getAccessPoints(); final List<AccessPoint> resultList = new ArrayList<>(); for (AccessPoint ap : accessPoints) { if (ap.isReachable()) { resultList.add(ap); } } updateResults(resultList); } } }