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

Commit 3672fb4b authored by Ankita Vyas's avatar Ankita Vyas
Browse files

AppClone: Changes in AppInfo page for cloned app.

- Hides preferences for cloneable apps under Cloned Apps page
- Displays Create option for cloneable apps under Cloned Apps page.
- Invokes CloneBackend on click of create and refreshes to display newly
  cloned app's AppInfo page.
- Appends suffix 'clone' for cloneable/cloned app.
- Displays text 'Delete' instead of 'uninstall'.

Screencast: https://screencast.googleplex.com/cast/NjI3MDEyMjk1MzAxNTI5NnxhOTIxZDhiZC03Zg

Bug: 262375058
Test: make RunSettingsRoboTests -j64

Change-Id: I34018f6cc7420d2667c25fbca59c832b398d723e
parent 23a91ff7
Loading
Loading
Loading
Loading
+5 −1
Original line number Diff line number Diff line
@@ -90,7 +90,7 @@ public class CloneBackend {
     * @param packageName
     * @return error/success code
     */
    int installCloneApp(String packageName) {
    public int installCloneApp(String packageName) {
        String userName = "cloneUser";
        UserHandle cloneUserHandle = null;
        boolean newlyCreated = false;
@@ -160,4 +160,8 @@ public class CloneBackend {
        }
        return SUCCESS;
    }

    public int getCloneUserId() {
        return mCloneUserId;
    }
}
+20 −0
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package com.android.settings.applications.manageapplications;

import static android.util.FeatureFlagUtils.SETTINGS_ENABLE_SPA;

import static androidx.recyclerview.widget.RecyclerView.SCROLL_STATE_DRAGGING;
import static androidx.recyclerview.widget.RecyclerView.SCROLL_STATE_IDLE;

@@ -46,6 +48,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageItemInfo;
import android.content.pm.UserInfo;
import android.content.res.Configuration;
import android.graphics.drawable.Drawable;
import android.net.Uri;
@@ -61,6 +64,7 @@ import android.preference.PreferenceFrameLayout;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.FeatureFlagUtils;
import android.util.IconDrawableFactory;
import android.util.Log;
import android.view.LayoutInflater;
@@ -144,6 +148,8 @@ import com.android.settings.notification.ConfigureNotificationSettings;
import com.android.settings.notification.NotificationBackend;
import com.android.settings.notification.app.AppNotificationSettings;
import com.android.settings.spa.SpaActivity;
import com.android.settings.spa.app.appinfo.AppInfoSettingsProvider;
import com.android.settings.spa.app.appinfo.CloneAppInfoSettingsProvider;
import com.android.settings.widget.LoadingViewController;
import com.android.settings.wifi.AppStateChangeWifiStateBridge;
import com.android.settings.wifi.ChangeWifiStateDetails;
@@ -703,6 +709,20 @@ public class ManageApplications extends InstrumentedFragment
                startAppInfoFragment(LongBackgroundTasksDetails.class,
                        R.string.long_background_tasks_label);
                break;
            case LIST_TYPE_CLONED_APPS:
                if (!FeatureFlagUtils.isEnabled(getContext(), SETTINGS_ENABLE_SPA)) {
                    return;
                }
                int userId = UserHandle.getUserId(mCurrentUid);
                UserInfo userInfo = mUserManager.getUserInfo(userId);
                if (userInfo != null && !userInfo.isCloneProfile()) {
                    SpaActivity.startSpaActivity(getContext(), CloneAppInfoSettingsProvider.INSTANCE
                            .getRoute(mCurrentPkgName, userId));
                } else {
                    SpaActivity.startSpaActivity(getContext(), AppInfoSettingsProvider.INSTANCE
                            .getRoute(mCurrentPkgName, userId));
                }
                break;
            // TODO: Figure out if there is a way where we can spin up the profile's settings
            // process ahead of time, to avoid a long load of data when user clicks on a managed
            // app. Maybe when they load the list of apps that contains managed profile apps.
+2 −0
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import android.content.Context
import com.android.settings.spa.app.AllAppListPageProvider
import com.android.settings.spa.app.AppsMainPageProvider
import com.android.settings.spa.app.appinfo.AppInfoSettingsProvider
import com.android.settings.spa.app.appinfo.CloneAppInfoSettingsProvider
import com.android.settings.spa.app.backgroundinstall.BackgroundInstalledAppsPageProvider
import com.android.settings.spa.app.specialaccess.AlarmsAndRemindersAppListProvider
import com.android.settings.spa.app.specialaccess.AllFilesAccessAppListProvider
@@ -68,6 +69,7 @@ open class SettingsSpaEnvironment(context: Context) : SpaEnvironment(context) {
                AppLanguagesPageProvider,
                UsageStatsPageProvider,
                BackgroundInstalledAppsPageProvider,
                CloneAppInfoSettingsProvider,
            ) + togglePermissionAppListTemplate.createPageProviders(),
            rootPages = listOf(
                SettingsPage.create(HomePageProvider.name),
+74 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 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.spa.app.appinfo

import android.app.Activity
import android.app.settings.SettingsEnums
import android.content.pm.ApplicationInfo
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Add
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.rememberCoroutineScope
import com.android.settings.R
import com.android.settings.applications.manageapplications.CloneBackend
import com.android.settings.overlay.FeatureFactory
import com.android.settings.spa.app.appinfo.AppInfoSettingsProvider.getRoute
import com.android.settingslib.spa.framework.compose.LocalNavController
import com.android.settingslib.spa.widget.button.ActionButton
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext

class AppCreateButton(packageInfoPresenter: PackageInfoPresenter) {
    private val context = packageInfoPresenter.context
    val enabledState = mutableStateOf(true)

    @Composable
    fun getActionButton(app: ApplicationInfo): ActionButton? {
        return createButton(app)
    }

    @Composable
    private fun createButton(app: ApplicationInfo): ActionButton {
        val coroutineScope = rememberCoroutineScope()
        val navController = LocalNavController.current
        return ActionButton(
                text = context.getString(R.string.create),
                imageVector = Icons.Outlined.Add,
                enabled = enabledState.value,
        )
        {
            val cloneBackend = CloneBackend.getInstance(context)
            FeatureFactory.getFactory(context).metricsFeatureProvider.action(context,
                    SettingsEnums.ACTION_CREATE_CLONE_APP)
            coroutineScope.launch {
                enabledState.value = false
                val result = installCloneApp(app, cloneBackend)
                if (result == CloneBackend.SUCCESS) {
                    navController.navigate(getRoute(app.packageName, cloneBackend.cloneUserId))
                } else {
                    enabledState.value = true
                }
            }
        }
    }

    private suspend fun installCloneApp(app: ApplicationInfo, cloneBackend: CloneBackend): Int = withContext(Dispatchers.IO) {
        cloneBackend.installCloneApp(app.packageName)
    }
}
 No newline at end of file
+10 −1
Original line number Diff line number Diff line
@@ -18,6 +18,8 @@ package com.android.settings.spa.app.appinfo

import android.content.om.OverlayManager
import android.content.pm.ApplicationInfo
import android.os.UserHandle
import android.os.UserManager
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Delete
import com.android.settings.R
@@ -30,6 +32,7 @@ class AppUninstallButton(private val packageInfoPresenter: PackageInfoPresenter)
    private val context = packageInfoPresenter.context
    private val appButtonRepository = AppButtonRepository(context)
    private val overlayManager = context.getSystemService(OverlayManager::class.java)!!
    private val userManager = context.getSystemService(UserManager::class.java)!!

    fun getActionButton(app: ApplicationInfo): ActionButton? {
        if (app.isSystemApp || app.isInstantApp) return null
@@ -80,7 +83,8 @@ class AppUninstallButton(private val packageInfoPresenter: PackageInfoPresenter)
            overlayManager.getOverlayInfo(packageName, userHandle)?.isEnabled == true

    private fun uninstallButton(app: ApplicationInfo, enabled: Boolean) = ActionButton(
        text = context.getString(R.string.uninstall_text),
        text = if (isCloneApp(app)) context.getString(R.string.delete) else
            context.getString(R.string.uninstall_text),
        imageVector = Icons.Outlined.Delete,
        enabled = enabled,
    ) { onUninstallClicked(app) }
@@ -89,4 +93,9 @@ class AppUninstallButton(private val packageInfoPresenter: PackageInfoPresenter)
        if (appButtonRepository.isUninstallBlockedByAdmin(app)) return
        packageInfoPresenter.startUninstallActivity()
    }

    private fun isCloneApp(app: ApplicationInfo): Boolean  {
        val userInfo = userManager.getUserInfo(UserHandle.getUserId(app.uid))
        return userInfo != null && userInfo.isCloneProfile
    }
}
Loading