Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d7f4782b authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Handle special case slices"

parents d0ecbe1c 56b2bad0
Loading
Loading
Loading
Loading
+88 −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.Context;
import android.net.Uri;
import android.util.ArrayMap;

import java.util.Map;

/**
 * Manages custom {@link androidx.slice.Slice Slices}, which are all Slices not backed by
 * preferences.
 * <p>
 *     By default, all Slices in Settings should be built by a
 * </p>
 */
public class CustomSliceManager {

    protected final Map<Uri, Class<? extends CustomSliceable>> mUriMap;

    private final Context mContext;

    public CustomSliceManager(Context context) {
        mContext = context;
        mUriMap = new ArrayMap<>();
        addSlices();
    }

    /**
     * Return a {@link CustomSliceable} associated to the Uri.
     * <p>
     * Do not change this method signature to accommodate for a special-case slicable - a context is
     * the only thing that should be needed to create the object.
     */
    public CustomSliceable getSliceableFromUri(Uri uri) {
        final Class clazz = mUriMap.get(uri);

        if (clazz == null) {
            throw new IllegalArgumentException("No Slice found for uri: " + uri);
        }

        return CustomSliceable.createInstance(mContext, clazz);
    }

    /**
     * Return a {@link CustomSliceable} associated to the Action.
     * <p>
     * Do not change this method signature to accommodate for a special-case sliceable - a context
     * is the only thing that should be needed to create the object.
     */
    public CustomSliceable getSliceableFromIntentAction(String action) {
        return getSliceableFromUri(Uri.parse(action));
    }

    /**
     * Returns {@code true} if {@param uri} is a valid Slice Uri handled by
     * {@link CustomSliceManager}.
     */
    public boolean isValidUri(Uri uri) {
        return mUriMap.containsKey(uri);
    }

    /**
     * Returns {@code true} if {@param action} is a valid intent action handled by
     * {@link CustomSliceManager}.
     */
    public boolean isValidAction(String action) {
        return isValidUri(Uri.parse(action));
    }

    private void addSlices() {
    }
}
 No newline at end of file
+102 −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.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.Uri;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

import androidx.slice.Slice;


/**
 * Common functions for custom Slices.
 * <p>
 *     A template for all Settings slices which are not represented by a Preference. By
 *     standardizing the methods used by the Slice helpers, we can use generically take actions
 *     rather than maintaining a list of all of the custom slices every time we reference Slices in
 *     Settings.
 * <p>
 *     By default, all Slices in Settings should be built through Preference Controllers extending
 *     {@link com.android.settings.core.BasePreferenceController}, which are automatically piped
 *     into Settings-Slices infrastructure. Cases where you should implement this interface are:
 *     <ul>
 *         <li>Multi-line slices</li>
 *         <li>Slices that don't exist in the UI</li>
 *         <li>Preferences that use a supported component, like a Switch Bar</li>
 *     </ul>
 * <p>
 *      Note that if your UI is supported because the Preference is not backed by a
 *      {@link com.android.settings.dashboard.DashboardFragment}, then you should first convert the
 *      existing fragment into a dashboard fragment, and then extend
 *      {@link com.android.settings.core.BasePreferenceController}.
 * <p>
 *     If you implement this interface, you should add your Slice to {@link CustomSliceManager}.
 */
public interface CustomSliceable {

    /**
     * @return an complete instance of the {@link Slice}.
     */
    Slice getSlice(Context context);

    /**
     * @return a {@link android.content.ContentResolver#SCHEME_CONTENT content} {@link Uri} which
     * backs the {@link Slice} returned by {@link #getSlice(Context)}.
     */
    Uri getUri();

    /**
     * Handles the actions sent by the {@link Intent intents} bound to the {@link Slice} returned by
     * {@link #getSlice(Context)}.
     *
     * @param intent which has the action taken on a {@link Slice}.
     */
    void onNotifyChange(Intent intent);

    /**
     * Settings Slices which can represent components that are updatable by the framework should
     * listen to changes matched to the {@link IntentFilter} returned here.
     *
     * @return an {@link IntentFilter} for updates related to the {@link Slice} returned by
     * {@link #getSlice(Context)}.
     */
    default IntentFilter getIntentFilter() {
        return null;
    }

    /**
     * Build an instance of a {@link CustomSliceable} which has a {@link Context}-only constructor.
     */
    static CustomSliceable createInstance(Context context, Class<CustomSliceable> sliceableClass) {
        try {
            //final Class<CustomSliceable> clazz = Class.forName(sliceableClassName);
            final Constructor<CustomSliceable> sliceable =
                    sliceableClass.getConstructor(Context.class);
            final Object[] params = new Object[]{context};
            return sliceable.newInstance(params);
        } catch (NoSuchMethodException | InstantiationException |
                IllegalArgumentException | InvocationTargetException | IllegalAccessException e) {
            throw new IllegalStateException(
                    "Invalid sliceable class: " + sliceableClass, e);
        }
    }
}
 No newline at end of file
+27 −6
Original line number Diff line number Diff line
@@ -32,15 +32,15 @@ import android.util.KeyValueListParser;
import android.util.Log;
import android.util.Pair;

import com.android.settings.bluetooth.BluetoothSliceBuilder;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.flashlight.FlashlightSliceBuilder;
import com.android.settings.location.LocationSliceBuilder;
import com.android.settings.mobilenetwork.Enhanced4gLteSliceHelper;
import com.android.settings.notification.ZenModeSliceBuilder;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.wifi.WifiSliceBuilder;
import com.android.settings.wifi.calling.WifiCallingSliceHelper;
import com.android.settings.bluetooth.BluetoothSliceBuilder;
import com.android.settingslib.SliceBroadcastRelay;
import com.android.settingslib.utils.ThreadUtils;

@@ -113,11 +113,15 @@ public class SettingsSliceProvider extends SliceProvider {
    public static final String EXTRA_SLICE_PLATFORM_DEFINED =
            "com.android.settings.slice.extra.platform";

    @VisibleForTesting
    CustomSliceManager mCustomSliceManager;

    @VisibleForTesting
    SlicesDatabaseAccessor mSlicesDatabaseAccessor;

    @VisibleForTesting
    Map<Uri, SliceData> mSliceWeakDataCache;

    @VisibleForTesting
    Map<Uri, SliceData> mSliceDataCache;

@@ -135,6 +139,8 @@ public class SettingsSliceProvider extends SliceProvider {
        mSlicesDatabaseAccessor = new SlicesDatabaseAccessor(getContext());
        mSliceDataCache = new ConcurrentHashMap<>();
        mSliceWeakDataCache = new WeakHashMap<>();
        mCustomSliceManager = FeatureFactory.getFactory(
                getContext()).getSlicesFeatureProvider().getCustomSliceManager(getContext());
        return true;
    }

@@ -151,6 +157,15 @@ public class SettingsSliceProvider extends SliceProvider {

    @Override
    public void onSlicePinned(Uri sliceUri) {
        if (mCustomSliceManager.isValidUri(sliceUri)) {
            final CustomSliceable sliceable = mCustomSliceManager.getSliceableFromUri(sliceUri);
            final IntentFilter filter = sliceable.getIntentFilter();
            if (filter != null) {
                registerIntentToUri(filter, sliceUri);
            }
            return;
        }

        if (WifiSliceBuilder.WIFI_URI.equals(sliceUri)) {
            registerIntentToUri(WifiSliceBuilder.INTENT_FILTER, sliceUri);
            mRegisteredUris.add(sliceUri);
@@ -197,8 +212,14 @@ public class SettingsSliceProvider extends SliceProvider {
                return null;
            }

            // If adding a new Slice, do not directly match Slice URIs.
            // Use {@link SlicesDatabaseAccessor}.
            // Before adding a slice to {@link CustomSliceManager}, please get approval
            // from the Settings team.
            if (mCustomSliceManager.isValidUri(sliceUri)) {
                final CustomSliceable sliceable = mCustomSliceManager.getSliceableFromUri(
                        sliceUri);
                return sliceable.getSlice(getContext());
            }

            if (WifiCallingSliceHelper.WIFI_CALLING_URI.equals(sliceUri)) {
                return FeatureFactory.getFactory(getContext())
                        .getSlicesFeatureProvider()
+9 −0
Original line number Diff line number Diff line
@@ -71,6 +71,15 @@ public class SliceBroadcastReceiver extends BroadcastReceiver {
        final boolean isPlatformSlice = intent.getBooleanExtra(EXTRA_SLICE_PLATFORM_DEFINED,
                false /* default */);

        final CustomSliceManager mCustomSliceManager = FeatureFactory.getFactory(
                context).getSlicesFeatureProvider().getCustomSliceManager(context);
        if (mCustomSliceManager.isValidAction(action)) {
            final CustomSliceable sliceable =
                    mCustomSliceManager.getSliceableFromIntentAction(action);
            sliceable.onNotifyChange(intent);
            return;
        }

        switch (action) {
            case ACTION_TOGGLE_CHANGED:
                final boolean isChecked = intent.getBooleanExtra(Slice.EXTRA_TOGGLE_STATE, false);
+2 −0
Original line number Diff line number Diff line
@@ -28,6 +28,8 @@ public interface SlicesFeatureProvider {
     */
    void indexSliceData(Context context);

    CustomSliceManager getCustomSliceManager(Context context);

    /**
     * Gets new WifiCallingSliceHelper object
     */
Loading