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

Commit 3bdedc2a authored by Ankita Vyas's avatar Ankita Vyas Committed by Android (Google) Code Review
Browse files

Merge "AppClone: Changes to display app list on Cloned Apps page."

parents d1dc8b94 d3019d3e
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -6366,6 +6366,9 @@
    <string name="app_default_dashboard_title">Default apps</string>
    <!-- Title for setting tile leading to App Clones menu under the Apps page [CHAR LIMIT=40] -->
    <string name="cloned_apps_dashboard_title">Cloned Apps</string>
    <!-- Description for introduction of the cloned apps page [CHAR LIMIT=NONE]-->
    <string name="desc_cloned_apps_intro_text">Create a second instance of an app so that you can use two accounts at the same time.</string>
    <string name="cloned_apps_summary"><xliff:g id="cloned_apps_count">%1$s</xliff:g> cloned, <xliff:g id="allowed_apps_count">%2$d</xliff:g> available to clone</string>
    <!-- Summary text for system preference title, showing important setting items under system setting [CHAR LIMIT=NONE]-->
    <string name="system_dashboard_summary">Languages, gestures, time, backup</string>
    <!-- Summary text for language preference title, showing important setting items under language setting [CHAR LIMIT=NONE]-->
+1 −0
Original line number Diff line number Diff line
@@ -65,6 +65,7 @@
    <Preference
        android:key="cloned_apps"
        android:title="@string/cloned_apps_dashboard_title"
        android:summary="@string/summary_placeholder"
        android:order="-995"
        settings:controller="com.android.settings.applications.ClonedAppsPreferenceController"
        android:fragment="com.android.settings.applications.manageapplications.ManageApplications">
+18 −0
Original line number Diff line number Diff line
@@ -164,6 +164,11 @@ public final class Utils extends com.android.settingslib.Utils {
    public static final String PROPERTY_HIBERNATION_TARGETS_PRE_S_APPS =
            "app_hibernation_targets_pre_s_apps";

    /**
     * Whether or not Cloned Apps menu is available in Apps page. Default is false.
     */
    public static final String PROPERTY_CLONED_APPS_ENABLED = "cloned_apps_enabled";

    /**
     * Finds a matching activity for a preference's intent. If a matching
     * activity is not found, it will remove the preference.
@@ -1252,4 +1257,17 @@ public final class Utils extends com.android.settingslib.Utils {
    public static int getHomepageIconColorHighlight(Context context) {
        return context.getColor(R.color.accent_select_primary_text);
    }

    /**
     * Returns user id of clone profile if present, else returns -1.
     */
    public static int getCloneUserId(Context context) {
        UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
        for (UserHandle userHandle : userManager.getUserProfiles()) {
            if (userManager.getUserInfo(userHandle.getIdentifier()).isCloneProfile()) {
                return userHandle.getIdentifier();
            }
        }
        return -1;
    }
}
+95 −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.applications;

import static android.content.pm.PackageManager.GET_ACTIVITIES;

import static com.android.settingslib.applications.ApplicationsState.AppEntry;
import static com.android.settingslib.applications.ApplicationsState.AppFilter;

import android.content.Context;
import android.util.Log;

import com.android.settings.Utils;
import com.android.settingslib.applications.ApplicationsState;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * Filter to display only allowlisted apps on Cloned Apps page.
 */
public class AppStateClonedAppsBridge extends AppStateBaseBridge{

    private static final String TAG = "ClonedAppsBridge";

    private final Context mContext;
    private final List<String> mAllowedApps;
    private List<String> mCloneProfileApps = new ArrayList<>();

    public AppStateClonedAppsBridge(Context context, ApplicationsState appState,
            Callback callback) {
        super(appState, callback);
        mContext = context;
        mAllowedApps = Arrays.asList(mContext.getResources()
                .getStringArray(com.android.internal.R.array.cloneable_apps));

        int cloneUserId = Utils.getCloneUserId(mContext);
        if (cloneUserId != -1) {
            mCloneProfileApps = mContext.getPackageManager()
                    .getInstalledPackagesAsUser(GET_ACTIVITIES,
                            cloneUserId).stream().map(x -> x.packageName).toList();
        }
    }

    @Override
    protected void loadAllExtraInfo() {
        final List<ApplicationsState.AppEntry> allApps = mAppSession.getAllApps();
        for (int i = 0; i < allApps.size(); i++) {
            ApplicationsState.AppEntry app = allApps.get(i);
            this.updateExtraInfo(app, app.info.packageName, app.info.uid);
        }
    }

    @Override
    protected void updateExtraInfo(AppEntry app, String pkg, int uid) {
        // Display package if allowlisted but not yet cloned.
        // Or if the app is present in clone profile alongwith being in allowlist.
        if (mAllowedApps.contains(pkg) && ((!mCloneProfileApps.contains(pkg) || (app.isCloned)))) {
            app.extraInfo = Boolean.TRUE;
        } else {
            app.extraInfo = Boolean.FALSE;
        }
    }

    public static final AppFilter FILTER_APPS_CLONE =
            new AppFilter() {
                @Override
                public void init() {
                }

                @Override
                public boolean filterApp(AppEntry entry) {
                    if (entry.extraInfo == null) {
                        Log.d(TAG, "[" + entry.info.packageName + "]" + " has No extra info.");
                        return false;
                    }
                    return (Boolean) entry.extraInfo;
                }
            };
}
+78 −9
Original line number Diff line number Diff line
@@ -16,31 +16,100 @@

package com.android.settings.applications;

import static com.android.settings.core.SettingsUIDeviceConfig.CLONED_APPS_ENABLED;
import static android.content.pm.PackageManager.GET_ACTIVITIES;

import static com.android.settings.Utils.PROPERTY_CLONED_APPS_ENABLED;

import android.content.Context;
import android.os.AsyncTask;
import android.os.UserHandle;
import android.provider.DeviceConfig;

import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleObserver;
import androidx.lifecycle.OnLifecycleEvent;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;

import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.core.BasePreferenceController;

import java.util.Arrays;
import java.util.List;

/**
 * A preference controller handling the logic for updating the summary of cloned apps.
 */
public class ClonedAppsPreferenceController extends BasePreferenceController {
public class ClonedAppsPreferenceController extends BasePreferenceController
        implements LifecycleObserver {
    private Preference mPreference;
    private Context mContext;

    public ClonedAppsPreferenceController(Context context, String preferenceKey) {
        super(context, preferenceKey);
        mContext = context;
    }

    @Override
    public CharSequence getSummary() {
        // todo(b/249916469): Update summary once we have mechanism of allowlisting available
        //  for cloned apps.
        return null;
    public int getAvailabilityStatus() {
        return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_APP_CLONING,
                PROPERTY_CLONED_APPS_ENABLED, false) ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
    }

    @Override
    public int getAvailabilityStatus() {
        return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SETTINGS_UI,
                CLONED_APPS_ENABLED, false) ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
    public void displayPreference(PreferenceScreen screen) {
        super.displayPreference(screen);
        mPreference = screen.findPreference(getPreferenceKey());
    }
    /**
     * On lifecycle resume event.
     */
    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    public void onResume() {
        updatePreferenceSummary();
    }

    private void updatePreferenceSummary() {
        new AsyncTask<Void, Void, Integer[]>() {

            @Override
            protected Integer[] doInBackground(Void... unused) {
                // Get list of allowlisted cloneable apps.
                List<String> cloneableApps = Arrays.asList(
                        mContext.getResources().getStringArray(
                                com.android.internal.R.array.cloneable_apps));
                List<String> primaryUserApps = mContext.getPackageManager()
                        .getInstalledPackagesAsUser(GET_ACTIVITIES,
                                UserHandle.myUserId()).stream().map(x -> x.packageName).toList();
                // Count number of installed apps in system user.
                int availableAppsCount = (int) cloneableApps.stream()
                        .filter(x -> primaryUserApps.contains(x)).count();

                int cloneUserId = Utils.getCloneUserId(mContext);
                if (cloneUserId == -1) {
                    return new Integer[]{0, availableAppsCount};
                }
                // Get all apps in clone profile if present.
                List<String> cloneProfileApps = mContext.getPackageManager()
                        .getInstalledPackagesAsUser(GET_ACTIVITIES,
                                cloneUserId).stream().map(x -> x.packageName).toList();
                // Count number of allowlisted app present in clone profile.
                int clonedAppsCount = (int) cloneableApps.stream()
                        .filter(x -> cloneProfileApps.contains(x)).count();

                return new Integer[]{clonedAppsCount, availableAppsCount - clonedAppsCount};
            }

            @Override
            protected void onPostExecute(Integer[] countInfo) {
                updateSummary(countInfo[0], countInfo[1]);
            }
        }.execute();
    }

    private void updateSummary(int clonedAppsCount, int availableAppsCount) {
        mPreference.setSummary(mContext.getResources().getString(
                R.string.cloned_apps_summary, clonedAppsCount, availableAppsCount));
    }
}
Loading