Loading src/com/android/settings/search/SearchFeatureProvider.java +4 −0 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ import android.widget.Toolbar; import com.android.settings.core.FeatureFlags; import com.android.settings.dashboard.SiteMapManager; import com.android.settings.overlay.FeatureFactory; import java.util.List; import java.util.concurrent.ExecutorService; Loading Loading @@ -185,6 +186,9 @@ public interface SearchFeatureProvider { } else { intent = new Intent(activity, SearchActivity.class); } FeatureFactory.getFactory( activity.getApplicationContext()).getSlicesFeatureProvider() .indexSliceDataAsync(activity.getApplicationContext()); activity.startActivityForResult(intent, 0 /* requestCode */); }); } Loading src/com/android/settings/slices/SettingsSliceProvider.java +21 −1 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ import android.content.Intent; import android.graphics.drawable.Icon; import android.net.Uri; import android.net.wifi.WifiManager; import android.support.annotation.VisibleForTesting; import com.android.settings.R; Loading @@ -32,13 +33,25 @@ import androidx.app.slice.SliceProvider; import androidx.app.slice.builders.ListBuilder; public class SettingsSliceProvider extends SliceProvider { private static final String TAG = "SettingsSliceProvider"; public static final String SLICE_AUTHORITY = "com.android.settings.slices"; public static final String PATH_WIFI = "wifi"; public static final String ACTION_WIFI_CHANGED = "com.android.settings.slice.action.WIFI_CHANGED"; public static final String ACTION_TOGGLE_CHANGED = "com.android.settings.slice.action.TOGGLE_CHANGED"; public static final String EXTRA_SLICE_KEY = "com.android.settings.slice.extra.key"; // TODO -- Associate slice URI with search result instead of separate hardcoded thing @VisibleForTesting SlicesDatabaseAccessor mSlicesDatabaseAccessor; public static Uri getUri(String path) { return new Uri.Builder() .scheme(ContentResolver.SCHEME_CONTENT) Loading @@ -48,19 +61,26 @@ public class SettingsSliceProvider extends SliceProvider { @Override public boolean onCreateSliceProvider() { mSlicesDatabaseAccessor = new SlicesDatabaseAccessor(getContext()); return true; } @Override public Slice onBindSlice(Uri sliceUri) { String path = sliceUri.getPath(); // If adding a new Slice, do not directly match Slice URIs. // Use {@link SlicesDatabaseAccessor}. switch (path) { case "/" + PATH_WIFI: return createWifiSlice(sliceUri); } throw new IllegalArgumentException("Unrecognized slice uri: " + sliceUri); return getHoldingSlice(sliceUri); } private Slice getHoldingSlice(Uri uri) { return new ListBuilder(uri).build(); } // TODO (b/70622039) remove this when the proper wifi slice is enabled. private Slice createWifiSlice(Uri sliceUri) { Loading src/com/android/settings/slices/SliceBroadcastReceiver.java +44 −3 Original line number Diff line number Diff line Loading @@ -16,7 +16,9 @@ package com.android.settings.slices; import static com.android.settings.slices.SettingsSliceProvider.ACTION_TOGGLE_CHANGED; import static com.android.settings.slices.SettingsSliceProvider.ACTION_WIFI_CHANGED; import static com.android.settings.slices.SettingsSliceProvider.EXTRA_SLICE_KEY; import android.app.slice.Slice; import android.content.BroadcastReceiver; Loading @@ -25,19 +27,34 @@ import android.content.Intent; import android.net.Uri; import android.net.wifi.WifiManager; import android.os.Handler; import android.text.TextUtils; import com.android.settings.core.BasePreferenceController; import com.android.settings.core.TogglePreferenceController; /** * Responds to actions performed on slices and notifies slices of updates in state changes. */ public class SliceBroadcastReceiver extends BroadcastReceiver { private static String TAG = "SettSliceBroadcastRec"; /** * TODO (b/) move wifi action into generalized case. */ @Override public void onReceive(Context context, Intent i) { String action = i.getAction(); public void onReceive(Context context, Intent intent) { String action = intent.getAction(); String key = intent.getStringExtra(EXTRA_SLICE_KEY); switch (action) { case ACTION_TOGGLE_CHANGED: handleToggleAction(context, key); break; case ACTION_WIFI_CHANGED: WifiManager wm = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); boolean newState = i.getBooleanExtra(Slice.EXTRA_TOGGLE_STATE, wm.isWifiEnabled()); boolean newState = intent.getBooleanExtra(Slice.EXTRA_TOGGLE_STATE, wm.isWifiEnabled()); wm.setWifiEnabled(newState); // Wait a bit for wifi to update (TODO: is there a better way to do this?) Handler h = new Handler(); Loading @@ -48,4 +65,28 @@ public class SliceBroadcastReceiver extends BroadcastReceiver { break; } } private void handleToggleAction(Context context, String key) { if (TextUtils.isEmpty(key)) { throw new IllegalStateException("No key passed to Intent for toggle controller"); } BasePreferenceController controller = getBasePreferenceController(context, key); if (!(controller instanceof TogglePreferenceController)) { throw new IllegalStateException("Toggle action passed for a non-toggle key: " + key); } // TODO post context.getContentResolver().notifyChanged(uri, null) in the Toggle controller // so that it's automatically broadcast to any slice. TogglePreferenceController toggleController = (TogglePreferenceController) controller; boolean currentValue = toggleController.isChecked(); toggleController.setChecked(!currentValue); } private BasePreferenceController getBasePreferenceController(Context context, String key) { final SlicesDatabaseAccessor accessor = new SlicesDatabaseAccessor(context); final SliceData sliceData = accessor.getSliceDataFromKey(key); return SliceBuilderUtils.getPreferenceController(context, sliceData); } } src/com/android/settings/slices/SliceBuilderUtils.java 0 → 100644 +125 −0 Original line number Diff line number Diff line /* * 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.slices; import static com.android.settings.slices.SettingsSliceProvider.EXTRA_SLICE_KEY; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.graphics.drawable.Icon; import android.text.TextUtils; import com.android.settings.SubSettings; import com.android.settings.core.BasePreferenceController; import com.android.settings.core.TogglePreferenceController; import com.android.settings.search.DatabaseIndexingUtils; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import androidx.app.slice.Slice; import androidx.app.slice.builders.ListBuilder; import androidx.app.slice.builders.ListBuilder.RowBuilder; /** * Utility class to build Slices objects and Preference Controllers based on the Database managed * by {@link SlicesDatabaseHelper} */ public class SliceBuilderUtils { private static final String TAG = "SliceBuilder"; /** * Build a Slice from {@link SliceData}. * * @return a {@link Slice} based on the data provided by {@param sliceData}. * Will build an {@link Intent} based Slice unless the Preference Controller name in * {@param sliceData} is an inline controller. */ public static Slice buildSlice(Context context, SliceData sliceData) { final PendingIntent contentIntent = getContentIntent(context, sliceData); final Icon icon = Icon.createWithResource(context, sliceData.getIconResource()); String summaryText = sliceData.getSummary(); String subtitleText = TextUtils.isEmpty(summaryText) ? sliceData.getScreenTitle() : summaryText; RowBuilder builder = new RowBuilder(sliceData.getUri()) .setTitle(sliceData.getTitle()) .setTitleItem(icon) .setSubtitle(subtitleText) .setContentIntent(contentIntent); BasePreferenceController controller = getPreferenceController(context, sliceData); // TODO (b/71640747) Respect setting availability. // TODO (b/71640678) Add dynamic summary text. if (controller instanceof TogglePreferenceController) { addToggleAction(context, builder, ((TogglePreferenceController) controller).isChecked(), sliceData.getKey()); } return new ListBuilder(sliceData.getUri()) .addRow(builder) .build(); } /** * Looks at the {@link SliceData#preferenceController} from {@param sliceData} and attempts to * build a {@link BasePreferenceController}. */ public static BasePreferenceController getPreferenceController(Context context, SliceData sliceData) { // TODO check for context-only controller first. try { Class<?> clazz = Class.forName(sliceData.getPreferenceController()); Constructor<?> preferenceConstructor = clazz.getConstructor(Context.class, String.class); return (BasePreferenceController) preferenceConstructor.newInstance( new Object[]{context, sliceData.getKey()}); } catch (ClassNotFoundException | NoSuchMethodException | InstantiationException | IllegalArgumentException | InvocationTargetException | IllegalAccessException e) { throw new IllegalStateException( "Invalid preference controller: " + sliceData.getPreferenceController()); } } private static void addToggleAction(Context context, RowBuilder builder, boolean isChecked, String key) { PendingIntent actionIntent = getActionIntent(context, SettingsSliceProvider.ACTION_TOGGLE_CHANGED, key); builder.addToggle(actionIntent, isChecked); } private static PendingIntent getActionIntent(Context context, String action, String key) { Intent intent = new Intent(action); intent.setClass(context, SliceBroadcastReceiver.class); intent.putExtra(EXTRA_SLICE_KEY, key); return PendingIntent.getBroadcast(context, 0 /* requestCode */, intent, PendingIntent.FLAG_CANCEL_CURRENT); } private static PendingIntent getContentIntent(Context context, SliceData sliceData) { Intent intent = DatabaseIndexingUtils.buildSearchResultPageIntent(context, sliceData.getFragmentClassName(), sliceData.getKey(), sliceData.getScreenTitle(), 0 /* TODO */); intent.setClassName("com.android.settings", SubSettings.class.getName()); return PendingIntent.getActivity(context, 0 /* requestCode */, intent, 0 /* flags */); } } No newline at end of file src/com/android/settings/slices/SliceData.java +0 −2 Original line number Diff line number Diff line Loading @@ -18,7 +18,6 @@ package com.android.settings.slices; import android.net.Uri; import android.text.TextUtils; /** * Data class representing a slice stored by {@link SlicesIndexer}. * Note that {@link #key} is treated as a primary key for this class and determines equality. Loading Loading @@ -179,5 +178,4 @@ public class SliceData { return mKey; } } } No newline at end of file Loading
src/com/android/settings/search/SearchFeatureProvider.java +4 −0 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ import android.widget.Toolbar; import com.android.settings.core.FeatureFlags; import com.android.settings.dashboard.SiteMapManager; import com.android.settings.overlay.FeatureFactory; import java.util.List; import java.util.concurrent.ExecutorService; Loading Loading @@ -185,6 +186,9 @@ public interface SearchFeatureProvider { } else { intent = new Intent(activity, SearchActivity.class); } FeatureFactory.getFactory( activity.getApplicationContext()).getSlicesFeatureProvider() .indexSliceDataAsync(activity.getApplicationContext()); activity.startActivityForResult(intent, 0 /* requestCode */); }); } Loading
src/com/android/settings/slices/SettingsSliceProvider.java +21 −1 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ import android.content.Intent; import android.graphics.drawable.Icon; import android.net.Uri; import android.net.wifi.WifiManager; import android.support.annotation.VisibleForTesting; import com.android.settings.R; Loading @@ -32,13 +33,25 @@ import androidx.app.slice.SliceProvider; import androidx.app.slice.builders.ListBuilder; public class SettingsSliceProvider extends SliceProvider { private static final String TAG = "SettingsSliceProvider"; public static final String SLICE_AUTHORITY = "com.android.settings.slices"; public static final String PATH_WIFI = "wifi"; public static final String ACTION_WIFI_CHANGED = "com.android.settings.slice.action.WIFI_CHANGED"; public static final String ACTION_TOGGLE_CHANGED = "com.android.settings.slice.action.TOGGLE_CHANGED"; public static final String EXTRA_SLICE_KEY = "com.android.settings.slice.extra.key"; // TODO -- Associate slice URI with search result instead of separate hardcoded thing @VisibleForTesting SlicesDatabaseAccessor mSlicesDatabaseAccessor; public static Uri getUri(String path) { return new Uri.Builder() .scheme(ContentResolver.SCHEME_CONTENT) Loading @@ -48,19 +61,26 @@ public class SettingsSliceProvider extends SliceProvider { @Override public boolean onCreateSliceProvider() { mSlicesDatabaseAccessor = new SlicesDatabaseAccessor(getContext()); return true; } @Override public Slice onBindSlice(Uri sliceUri) { String path = sliceUri.getPath(); // If adding a new Slice, do not directly match Slice URIs. // Use {@link SlicesDatabaseAccessor}. switch (path) { case "/" + PATH_WIFI: return createWifiSlice(sliceUri); } throw new IllegalArgumentException("Unrecognized slice uri: " + sliceUri); return getHoldingSlice(sliceUri); } private Slice getHoldingSlice(Uri uri) { return new ListBuilder(uri).build(); } // TODO (b/70622039) remove this when the proper wifi slice is enabled. private Slice createWifiSlice(Uri sliceUri) { Loading
src/com/android/settings/slices/SliceBroadcastReceiver.java +44 −3 Original line number Diff line number Diff line Loading @@ -16,7 +16,9 @@ package com.android.settings.slices; import static com.android.settings.slices.SettingsSliceProvider.ACTION_TOGGLE_CHANGED; import static com.android.settings.slices.SettingsSliceProvider.ACTION_WIFI_CHANGED; import static com.android.settings.slices.SettingsSliceProvider.EXTRA_SLICE_KEY; import android.app.slice.Slice; import android.content.BroadcastReceiver; Loading @@ -25,19 +27,34 @@ import android.content.Intent; import android.net.Uri; import android.net.wifi.WifiManager; import android.os.Handler; import android.text.TextUtils; import com.android.settings.core.BasePreferenceController; import com.android.settings.core.TogglePreferenceController; /** * Responds to actions performed on slices and notifies slices of updates in state changes. */ public class SliceBroadcastReceiver extends BroadcastReceiver { private static String TAG = "SettSliceBroadcastRec"; /** * TODO (b/) move wifi action into generalized case. */ @Override public void onReceive(Context context, Intent i) { String action = i.getAction(); public void onReceive(Context context, Intent intent) { String action = intent.getAction(); String key = intent.getStringExtra(EXTRA_SLICE_KEY); switch (action) { case ACTION_TOGGLE_CHANGED: handleToggleAction(context, key); break; case ACTION_WIFI_CHANGED: WifiManager wm = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); boolean newState = i.getBooleanExtra(Slice.EXTRA_TOGGLE_STATE, wm.isWifiEnabled()); boolean newState = intent.getBooleanExtra(Slice.EXTRA_TOGGLE_STATE, wm.isWifiEnabled()); wm.setWifiEnabled(newState); // Wait a bit for wifi to update (TODO: is there a better way to do this?) Handler h = new Handler(); Loading @@ -48,4 +65,28 @@ public class SliceBroadcastReceiver extends BroadcastReceiver { break; } } private void handleToggleAction(Context context, String key) { if (TextUtils.isEmpty(key)) { throw new IllegalStateException("No key passed to Intent for toggle controller"); } BasePreferenceController controller = getBasePreferenceController(context, key); if (!(controller instanceof TogglePreferenceController)) { throw new IllegalStateException("Toggle action passed for a non-toggle key: " + key); } // TODO post context.getContentResolver().notifyChanged(uri, null) in the Toggle controller // so that it's automatically broadcast to any slice. TogglePreferenceController toggleController = (TogglePreferenceController) controller; boolean currentValue = toggleController.isChecked(); toggleController.setChecked(!currentValue); } private BasePreferenceController getBasePreferenceController(Context context, String key) { final SlicesDatabaseAccessor accessor = new SlicesDatabaseAccessor(context); final SliceData sliceData = accessor.getSliceDataFromKey(key); return SliceBuilderUtils.getPreferenceController(context, sliceData); } }
src/com/android/settings/slices/SliceBuilderUtils.java 0 → 100644 +125 −0 Original line number Diff line number Diff line /* * 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.slices; import static com.android.settings.slices.SettingsSliceProvider.EXTRA_SLICE_KEY; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.graphics.drawable.Icon; import android.text.TextUtils; import com.android.settings.SubSettings; import com.android.settings.core.BasePreferenceController; import com.android.settings.core.TogglePreferenceController; import com.android.settings.search.DatabaseIndexingUtils; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import androidx.app.slice.Slice; import androidx.app.slice.builders.ListBuilder; import androidx.app.slice.builders.ListBuilder.RowBuilder; /** * Utility class to build Slices objects and Preference Controllers based on the Database managed * by {@link SlicesDatabaseHelper} */ public class SliceBuilderUtils { private static final String TAG = "SliceBuilder"; /** * Build a Slice from {@link SliceData}. * * @return a {@link Slice} based on the data provided by {@param sliceData}. * Will build an {@link Intent} based Slice unless the Preference Controller name in * {@param sliceData} is an inline controller. */ public static Slice buildSlice(Context context, SliceData sliceData) { final PendingIntent contentIntent = getContentIntent(context, sliceData); final Icon icon = Icon.createWithResource(context, sliceData.getIconResource()); String summaryText = sliceData.getSummary(); String subtitleText = TextUtils.isEmpty(summaryText) ? sliceData.getScreenTitle() : summaryText; RowBuilder builder = new RowBuilder(sliceData.getUri()) .setTitle(sliceData.getTitle()) .setTitleItem(icon) .setSubtitle(subtitleText) .setContentIntent(contentIntent); BasePreferenceController controller = getPreferenceController(context, sliceData); // TODO (b/71640747) Respect setting availability. // TODO (b/71640678) Add dynamic summary text. if (controller instanceof TogglePreferenceController) { addToggleAction(context, builder, ((TogglePreferenceController) controller).isChecked(), sliceData.getKey()); } return new ListBuilder(sliceData.getUri()) .addRow(builder) .build(); } /** * Looks at the {@link SliceData#preferenceController} from {@param sliceData} and attempts to * build a {@link BasePreferenceController}. */ public static BasePreferenceController getPreferenceController(Context context, SliceData sliceData) { // TODO check for context-only controller first. try { Class<?> clazz = Class.forName(sliceData.getPreferenceController()); Constructor<?> preferenceConstructor = clazz.getConstructor(Context.class, String.class); return (BasePreferenceController) preferenceConstructor.newInstance( new Object[]{context, sliceData.getKey()}); } catch (ClassNotFoundException | NoSuchMethodException | InstantiationException | IllegalArgumentException | InvocationTargetException | IllegalAccessException e) { throw new IllegalStateException( "Invalid preference controller: " + sliceData.getPreferenceController()); } } private static void addToggleAction(Context context, RowBuilder builder, boolean isChecked, String key) { PendingIntent actionIntent = getActionIntent(context, SettingsSliceProvider.ACTION_TOGGLE_CHANGED, key); builder.addToggle(actionIntent, isChecked); } private static PendingIntent getActionIntent(Context context, String action, String key) { Intent intent = new Intent(action); intent.setClass(context, SliceBroadcastReceiver.class); intent.putExtra(EXTRA_SLICE_KEY, key); return PendingIntent.getBroadcast(context, 0 /* requestCode */, intent, PendingIntent.FLAG_CANCEL_CURRENT); } private static PendingIntent getContentIntent(Context context, SliceData sliceData) { Intent intent = DatabaseIndexingUtils.buildSearchResultPageIntent(context, sliceData.getFragmentClassName(), sliceData.getKey(), sliceData.getScreenTitle(), 0 /* TODO */); intent.setClassName("com.android.settings", SubSettings.class.getName()); return PendingIntent.getActivity(context, 0 /* requestCode */, intent, 0 /* flags */); } } No newline at end of file
src/com/android/settings/slices/SliceData.java +0 −2 Original line number Diff line number Diff line Loading @@ -18,7 +18,6 @@ package com.android.settings.slices; import android.net.Uri; import android.text.TextUtils; /** * Data class representing a slice stored by {@link SlicesIndexer}. * Note that {@link #key} is treated as a primary key for this class and determines equality. Loading Loading @@ -179,5 +178,4 @@ public class SliceData { return mKey; } } } No newline at end of file