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

Commit cf788600 authored by Maryam Dehaini's avatar Maryam Dehaini
Browse files

Add generic links to app-to-web

This Cl does the following:
1. Creates a build-time and server side list of generic links
2. Creates a flag which determins which list will be used
3. Creates a parser to retrieve said list and parse it into a Hashmap,
   mapping package names to urls
4. When a user clicks on the "Open in browser" button in the handle
   menu, opens the generic link if captured link is unavailable or
   expired

Bug: 349695493
Test: Use open in browser button in header menu + atest
AppToWebGenericLinksParserTests
Flag: com.android.window.flags.enable_desktop_windowing_app_to_web

Change-Id: Ibbd8c21b95679125e3c480d0430b9d473110fdbb
parent df7e8839
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -179,4 +179,8 @@

    <!-- Whether pointer pilfer is required to start back animation. -->
    <bool name="config_backAnimationRequiresPointerPilfer">true</bool>

    <!-- This is to be overridden to define a list of packages mapped to web links which will be
         parsed and utilized for desktop windowing's app-to-web feature. -->
    <string name="generic_links_list" translatable="false"/>
</resources>
+11 −0
Original line number Diff line number Diff line
@@ -70,6 +70,10 @@ public class DesktopModeStatus {
    private static final boolean ENFORCE_DEVICE_RESTRICTIONS = SystemProperties.getBoolean(
            "persist.wm.debug.desktop_mode_enforce_device_restrictions", true);

    private static final boolean USE_APP_TO_WEB_BUILD_TIME_GENERIC_LINKS =
            SystemProperties.getBoolean(
                    "persist.wm.debug.use_app_to_web_build_time_generic_links", true);

    /** Whether the desktop density override is enabled. */
    public static final boolean DESKTOP_DENSITY_OVERRIDE_ENABLED =
            SystemProperties.getBoolean("persist.wm.debug.desktop_mode_density_enabled", false);
@@ -175,6 +179,13 @@ public class DesktopModeStatus {
        return isDesktopDensityOverrideEnabled() && isValidDesktopDensityOverrideSet();
    }

    /**
     * Returns {@code true} if the app-to-web feature is using the build-time generic links list.
     */
    public static boolean useAppToWebBuildTimeGenericLinks() {
        return USE_APP_TO_WEB_BUILD_TIME_GENERIC_LINKS;
    }

    /**
     * Return {@code true} if the override desktop density is enabled.
     */
+96 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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.wm.shell.apptoweb

import android.content.Context
import android.provider.DeviceConfig
import android.webkit.URLUtil
import com.android.internal.annotations.VisibleForTesting
import com.android.wm.shell.R
import com.android.wm.shell.common.ShellExecutor
import com.android.wm.shell.shared.annotations.ShellMainThread
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus.useAppToWebBuildTimeGenericLinks

/**
 * Retrieves the build-time or server-side generic links list and parses and stores the
 * package-to-url pairs.
 */
class AppToWebGenericLinksParser(
    private val context: Context,
    @ShellMainThread private val mainExecutor: ShellExecutor
) {
    private val genericLinksMap: MutableMap<String, String> = mutableMapOf()

    init {
        // If using the server-side generic links list, register a listener
        if (!useAppToWebBuildTimeGenericLinks()) {
            DeviceConfigListener()
        }

        updateGenericLinksMap()
    }

    /** Returns the generic link associated with the [packageName] or null if there is none. */
    fun getGenericLink(packageName: String): String? = genericLinksMap[packageName]

    private fun updateGenericLinksMap() {
        val genericLinksList =
            if (useAppToWebBuildTimeGenericLinks()) {
                context.resources.getString(R.string.generic_links_list)
            } else {
                DeviceConfig.getString(NAMESPACE, FLAG_GENERIC_LINKS, /* defaultValue= */ "")
            } ?: return

        parseGenericLinkList(genericLinksList)
    }

    private fun parseGenericLinkList(genericLinksList: String) {
        val newEntries =
            genericLinksList
                .split(" ")
                .filter { it.contains(':') }
                .map {
                    val (packageName, url) = it.split(':', limit = 2)
                    return@map packageName to url
                }
                .filter { URLUtil.isNetworkUrl(it.second) }

        genericLinksMap.clear()
        genericLinksMap.putAll(newEntries)
    }

    /**
     * Listens for changes to the server-side generic links list and updates the package to url map
     * if [DesktopModeStatus#useBuildTimeGenericLinkList()] is set to false.
     */
    inner class DeviceConfigListener : DeviceConfig.OnPropertiesChangedListener {
        init {
            DeviceConfig.addOnPropertiesChangedListener(NAMESPACE, mainExecutor, this)
        }

        override fun onPropertiesChanged(properties: DeviceConfig.Properties) {
            if (properties.keyset.contains(FLAG_GENERIC_LINKS)) {
                updateGenericLinksMap()
            }
        }
    }

    companion object {
        private const val NAMESPACE = DeviceConfig.NAMESPACE_APP_COMPAT_OVERRIDES
        @VisibleForTesting const val FLAG_GENERIC_LINKS = "generic_links_flag"
    }
}
+14 −2
Original line number Diff line number Diff line
@@ -35,6 +35,7 @@ import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.activityembedding.ActivityEmbeddingController;
import com.android.wm.shell.apptoweb.AppToWebGenericLinksParser;
import com.android.wm.shell.bubbles.BubbleController;
import com.android.wm.shell.bubbles.BubbleData;
import com.android.wm.shell.bubbles.BubbleDataRepository;
@@ -223,7 +224,8 @@ public abstract class WMShellModule {
            Transitions transitions,
            Optional<DesktopTasksController> desktopTasksController,
            RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
            InteractionJankMonitor interactionJankMonitor) {
            InteractionJankMonitor interactionJankMonitor,
            AppToWebGenericLinksParser genericLinksParser) {
        if (DesktopModeStatus.canEnterDesktopMode(context)) {
            return new DesktopModeWindowDecorViewModel(
                    context,
@@ -242,7 +244,8 @@ public abstract class WMShellModule {
                    transitions,
                    desktopTasksController,
                    rootTaskDisplayAreaOrganizer,
                    interactionJankMonitor);
                    interactionJankMonitor,
                    genericLinksParser);
        }
        return new CaptionWindowDecorViewModel(
                context,
@@ -259,6 +262,15 @@ public abstract class WMShellModule {
                transitions);
    }

    @WMSingleton
    @Provides
    static AppToWebGenericLinksParser provideGenericLinksParser(
            Context context,
            @ShellMainThread ShellExecutor mainExecutor
    ) {
        return new AppToWebGenericLinksParser(context, mainExecutor);
    }

    //
    // Freeform
    //
+9 −2
Original line number Diff line number Diff line
@@ -87,6 +87,7 @@ import com.android.window.flags.Flags;
import com.android.wm.shell.R;
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.apptoweb.AppToWebGenericLinksParser;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.DisplayLayout;
@@ -162,6 +163,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
    private final DesktopModeKeyguardChangeListener mDesktopModeKeyguardChangeListener =
            new DesktopModeKeyguardChangeListener();
    private final RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer;
    private final AppToWebGenericLinksParser mGenericLinksParser;
    private final DisplayInsetsController mDisplayInsetsController;
    private final Region mExclusionRegion = Region.obtain();
    private boolean mInImmersiveMode;
@@ -198,7 +200,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
            Transitions transitions,
            Optional<DesktopTasksController> desktopTasksController,
            RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
            InteractionJankMonitor interactionJankMonitor
            InteractionJankMonitor interactionJankMonitor,
            AppToWebGenericLinksParser genericLinksParser
    ) {
        this(
                context,
@@ -216,6 +219,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
                syncQueue,
                transitions,
                desktopTasksController,
                genericLinksParser,
                new DesktopModeWindowDecoration.Factory(),
                new InputMonitorFactory(),
                SurfaceControl.Transaction::new,
@@ -241,6 +245,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
            SyncTransactionQueue syncQueue,
            Transitions transitions,
            Optional<DesktopTasksController> desktopTasksController,
            AppToWebGenericLinksParser genericLinksParser,
            DesktopModeWindowDecoration.Factory desktopModeWindowDecorFactory,
            InputMonitorFactory inputMonitorFactory,
            Supplier<SurfaceControl.Transaction> transactionFactory,
@@ -266,6 +271,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
        mInputMonitorFactory = inputMonitorFactory;
        mTransactionFactory = transactionFactory;
        mRootTaskDisplayAreaOrganizer = rootTaskDisplayAreaOrganizer;
        mGenericLinksParser = genericLinksParser;
        mInputManager = mContext.getSystemService(InputManager.class);
        mWindowDecorByTaskId = windowDecorByTaskId;
        mSysUIPackageName = mContext.getResources().getString(
@@ -1155,7 +1161,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
                        mBgExecutor,
                        mMainChoreographer,
                        mSyncQueue,
                        mRootTaskDisplayAreaOrganizer);
                        mRootTaskDisplayAreaOrganizer,
                        mGenericLinksParser);
        mWindowDecorByTaskId.put(taskInfo.taskId, windowDecoration);

        final DragPositioningCallback dragPositioningCallback;
Loading