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

Commit 54b8f33e authored by Bryce Lee's avatar Bryce Lee
Browse files

Add Support for App Widgets as Overlays.

This changelist adds support for loading App Widgets
from a ComponentName. This functionality is wrapped in
an OverlayProvider for integration with
DreamOverlayService.

Bug: 201675932
Test: atest AppWidgetOverlayProviderTest
Change-Id: Iddc6297ee0a9eb6c945a70368ba1f1b7ef0b1a0b
parent 814a2d60
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -294,6 +294,11 @@

    <uses-permission android:name="android.permission.READ_PEOPLE_DATA" />

    <!-- Permission for dream overlay. -->
    <uses-permission android:name="android.permission.BIND_DREAM_SERVICE" />

    <uses-permission android:name="android.permission.BIND_APPWIDGET" />

    <protected-broadcast android:name="com.android.settingslib.action.REGISTER_SLICE_RECEIVER" />
    <protected-broadcast android:name="com.android.settingslib.action.UNREGISTER_SLICE_RECEIVER" />
    <protected-broadcast android:name="com.android.settings.flashlight.action.FLASHLIGHT_CHANGED" />
+2 −0
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@ import com.android.systemui.controls.dagger.ControlsModule;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.demomode.dagger.DemoModeModule;
import com.android.systemui.doze.dagger.DozeComponent;
import com.android.systemui.dreams.dagger.DreamModule;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlagManager;
import com.android.systemui.flags.FeatureFlags;
@@ -101,6 +102,7 @@ import dagger.Provides;
            AssistModule.class,
            ClockModule.class,
            CommunalModule.class,
            DreamModule.class,
            ControlsModule.class,
            DemoModeModule.class,
            FalsingModule.class,
+82 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 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.systemui.dreams.appwidgets;

import android.appwidget.AppWidgetHostView;
import android.content.ComponentName;
import android.content.Context;
import android.util.Log;
import android.widget.RemoteViews;

import com.android.systemui.dreams.OverlayHost;
import com.android.systemui.dreams.OverlayHostView;
import com.android.systemui.dreams.OverlayProvider;
import com.android.systemui.plugins.ActivityStarter;

import javax.inject.Inject;

/**
 * {@link AppWidgetOverlayProvider} is an implementation of {@link OverlayProvider} for providing
 * app widget-based overlays.
 */
public class AppWidgetOverlayProvider implements OverlayProvider {
    private static final String TAG = "AppWdgtOverlayProvider";
    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);

    private final ActivityStarter mActivityStarter;
    private final AppWidgetProvider mAppWidgetProvider;
    private final ComponentName mComponentName;
    private final OverlayHostView.LayoutParams mLayoutParams;

    @Inject
    public AppWidgetOverlayProvider(ActivityStarter activityStarter,
            ComponentName componentName, AppWidgetProvider widgetProvider,
            OverlayHostView.LayoutParams layoutParams) {
        mActivityStarter = activityStarter;
        mComponentName = componentName;
        mAppWidgetProvider = widgetProvider;
        mLayoutParams = layoutParams;
    }

    @Override
    public void onCreateOverlay(Context context, OverlayHost.CreationCallback creationCallback,
            OverlayHost.InteractionCallback interactionCallback) {
        final AppWidgetHostView widget = mAppWidgetProvider.getWidget(mComponentName);

        if (widget == null) {
            Log.e(TAG, "could not create widget");
            return;
        }

        widget.setInteractionHandler((view, pendingIntent, response) -> {
            if (pendingIntent.isActivity()) {
                if (DEBUG) {
                    Log.d(TAG, "launching pending intent from app widget:" + mComponentName);
                }
                interactionCallback.onExit();
                mActivityStarter.startPendingIntentDismissingKeyguard(pendingIntent,
                        null /*intentSentUiThreadCallback*/, view);
                return true;
            } else {
                return RemoteViews.startPendingIntent(view, pendingIntent,
                        response.getLaunchOptions(view));
            }
        });

        creationCallback.onCreated(widget, mLayoutParams);
    }
}
+101 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 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.systemui.dreams.appwidgets;

import android.appwidget.AppWidgetHost;
import android.appwidget.AppWidgetHostView;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProviderInfo;
import android.content.ComponentName;
import android.content.Context;
import android.content.res.Resources;
import android.util.Log;

import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;

import java.util.List;

import javax.inject.Inject;

/**
 * {@link AppWidgetProvider} is a singleton for accessing app widgets within SystemUI. This
 * consolidates resources such as the App Widget Host across potentially multiple
 * {@link AppWidgetOverlayProvider} instances and other usages.
 */
@SysUISingleton
public class AppWidgetProvider {
    private static final String TAG = "AppWidgetProvider";
    public static final int APP_WIDGET_HOST_ID = 1025;

    private final Context mContext;
    private final AppWidgetManager mAppWidgetManager;
    private final AppWidgetHost mAppWidgetHost;
    private final Resources mResources;

    @Inject
    public AppWidgetProvider(Context context, @Main Resources resources) {
        mContext = context;
        mResources = resources;
        mAppWidgetManager = android.appwidget.AppWidgetManager.getInstance(context);
        mAppWidgetHost = new AppWidgetHost(context, APP_WIDGET_HOST_ID);
        mAppWidgetHost.startListening();
    }

    /**
     * Returns an {@link AppWidgetHostView} associated with a given {@link ComponentName}.
     * @param component The {@link ComponentName} of the target {@link AppWidgetHostView}.
     * @return The {@link AppWidgetHostView} or {@code null} on error.
     */
    public AppWidgetHostView getWidget(ComponentName component) {
        final List<AppWidgetProviderInfo> appWidgetInfos =
                mAppWidgetManager.getInstalledProviders();

        for (AppWidgetProviderInfo widgetInfo : appWidgetInfos) {
            if (widgetInfo.provider.equals(component)) {
                final int widgetId = mAppWidgetHost.allocateAppWidgetId();

                boolean success = mAppWidgetManager.bindAppWidgetIdIfAllowed(widgetId,
                        widgetInfo.provider);

                if (!success) {
                    Log.e(TAG, "could not bind to app widget:" + component);
                    break;
                }

                final AppWidgetHostView appWidgetView =
                        mAppWidgetHost.createView(mContext, widgetId, widgetInfo);

                if (appWidgetView != null) {
                    // Register a layout change listener to update the widget on any sizing changes.
                    appWidgetView.addOnLayoutChangeListener(
                            (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
                                final float density = mResources.getDisplayMetrics().density;
                                final int height = Math.round((bottom - top) / density);
                                final int width = Math.round((right - left) / density);
                                appWidgetView.updateAppWidgetSize(null, width, height, width,
                                        height);
                            });
                }

                return appWidgetView;
            }
        }

        return null;
    }
}
+39 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 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.systemui.dreams.dagger;

import android.content.ComponentName;

import com.android.systemui.dreams.OverlayHostView;
import com.android.systemui.dreams.appwidgets.AppWidgetOverlayProvider;

import dagger.BindsInstance;
import dagger.Subcomponent;

/** */
@Subcomponent
public interface AppWidgetOverlayComponent {
    /** */
    @Subcomponent.Factory
    interface Factory {
        AppWidgetOverlayComponent build(@BindsInstance ComponentName component,
                @BindsInstance OverlayHostView.LayoutParams layoutParams);
    }

    /** Builds a {@link AppWidgetOverlayProvider}. */
    AppWidgetOverlayProvider getAppWidgetOverlayProvider();
}
Loading