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

Commit 27c39878 authored by Manish Singh's avatar Manish Singh
Browse files

Add Private Space settings page

This involves:
1. Adding a new page for PS settings under Security & Privacy
2. Integrating the PS safety source with SafetyCenter.

Also, add the capability to create and delete PS from the page.
Creation is temporary to help with prototyping, and will be removed
finally.

Screenshots:
Private Space Entry point in Security & Privacy Settings:
https://screenshot.googleplex.com/4VHxNekjhxZHJwF

Private Space Settings page:
https://screenshot.googleplex.com/3Raw4wuymTHTgtM

Creating Private Space:
https://screenshot.googleplex.com/3dvzAH6V4kQmuYf

Private Space created:
https://screenshot.googleplex.com/Aj7nnF9uuUCa9Q5

Design doc:
https://docs.google.com/document/d/1CdjUjAE84-5ZEPRIfw5KYFjLVHtEZxc_sF0w95su8DA/edit?usp=sharing&resourcekey=0-dAACT9HRexY1IyoxMmkVlw

Bug: 286539356
Bug: 293569406
Test: manual
Test: atest DeletePrivateSpaceControllerTest
Test: atest UseOneLockControllerTest
Test: atest HidePrivateSpaceControllerTest
Change-Id: I9caf8e04e7fb2df36e60f607225e2931988ee692
parent c192039f
Loading
Loading
Loading
Loading
+26 −1
Original line number Diff line number Diff line
@@ -1175,13 +1175,38 @@
    <!-- Title for the subpage in the "Security & privacy". This page consists of the more security and privacy settings. Can be navigated by Settings -> Security & privacy -> More security & privacy [CHAR LIMIT=NONE] -->
    <string name="more_security_privacy_settings">More security &amp; privacy</string>
    <!-- Title for the section that has security entries in the More security & privacy page. [CHAR LIMIT=60] -->
    <!-- Title for the section that has security entries in the More security & privacy page. Also used for Private Space security category. [CHAR LIMIT=60] -->
    <string name="security_header">Security</string>
    <!-- Title for the section that has privacy entries in the More security & privacy page. [CHAR LIMIT=60] -->
    <string name="privacy_header">Privacy</string>
    <!-- Title for the section that has work profile entries in the More security & privacy page. [CHAR LIMIT=60] -->
    <string name="work_profile_category_header">Work profile</string>
    <!-- Title for the Private Space page. [CHAR LIMIT=60] -->
    <string name="private_space_title">Private Space</string>
    <!-- Summary for the Private Space page. [CHAR LIMIT=NONE] -->
    <string name="private_space_summary">Hide apps in a private folder</string>
    <!-- Title for the Private Space one lock preference. [CHAR LIMIT=60] -->
    <string name="private_space_one_lock_title">Unlock using screen lock</string>
    <!-- Title for the preference to hide Private Space. [CHAR LIMIT=60] -->
    <string name="private_space_hide_title">Show Private Space</string>
    <!-- System category for the Private Space page. [CHAR LIMIT=30] -->
    <string name="private_space_category_system">System</string>
    <!-- Title for the preference to create Private Space. [CHAR LIMIT=60] -->
    <string name="private_space_create_title">Create Private Space</string>
    <!-- Title for the preference to delete Private Space. [CHAR LIMIT=60] -->
    <string name="private_space_delete_title">Delete Private Space</string>
    <!-- Toast to show when the private space was created. [CHAR LIMIT=NONE] -->
    <string name="private_space_created">Private Space successfully created</string>
    <!-- Toast to show when the private space already exists. [CHAR LIMIT=NONE] -->
    <string name="private_space_already_exists">Private Space already exists</string>
    <!-- Toast to show when the private space could not be created. [CHAR LIMIT=NONE] -->
    <string name="private_space_create_failed">Private Space could not be created</string>
    <!-- Toast to show when the private space was deleted. [CHAR LIMIT=NONE] -->
    <string name="private_space_deleted">Private Space successfully deleted</string>
    <!-- Toast to show when the private space could not be deleted. [CHAR LIMIT=NONE] -->
    <string name="private_space_delete_failed">Private Space could not be deleted</string>
    <!-- Text shown when "Add fingerprint" button is disabled -->
    <string name="fingerprint_add_max">You can add up to <xliff:g id="count" example="5">%d</xliff:g> fingerprints</string>
    <!-- Text shown when users has enrolled a maximum number of fingerprints [CHAR LIMIT=NONE] -->
+58 −0
Original line number Diff line number Diff line
<?xml version="1.0" encoding="utf-8"?>
<!--
  Copyright (C) 2023 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.
  -->

<PreferenceScreen
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:settings="http://schemas.android.com/apk/res-auto"
    android:key="private_space_title"
    android:title="@string/private_space_title">

    <PreferenceCategory
        android:title="@string/security_header">

        <SwitchPreference
            android:key="private_space_use_one_lock"
            android:title="@string/private_space_one_lock_title"
            settings:controller="com.android.settings.privatespace.UseOneLockController"
            settings:searchable="false" />

        <SwitchPreference
            android:key="private_space_hidden"
            android:title="@string/private_space_hide_title"
            settings:controller="com.android.settings.privatespace.HidePrivateSpaceController"
            settings:searchable="false" />

    </PreferenceCategory>

    <PreferenceCategory
        android:title="@string/private_space_category_system">

        <Preference
            android:key="private_space_create"
            android:title="@string/private_space_create_title"
            settings:controller="com.android.settings.privatespace.CreatePrivateSpaceController"
            settings:searchable="false" />

        <Preference
            android:key="private_space_delete"
            android:title="@string/private_space_delete_title"
            settings:controller="com.android.settings.privatespace.DeletePrivateSpaceController"
            settings:searchable="false" />

    </PreferenceCategory>

</PreferenceScreen>
 No newline at end of file
+74 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 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.privatespace;

import android.content.Context;
import android.text.TextUtils;
import android.widget.Toast;

import androidx.preference.Preference;

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

// TODO(b/293569406): Remove this when we have the setup flow in place to create PS
/**
 * Temp Controller to create the private space from the PS Settings page. This is to allow PM, UX,
 * and other folks to play around with PS before the PS setup flow is ready.
 */
public final class CreatePrivateSpaceController extends BasePreferenceController {

    public CreatePrivateSpaceController(Context context, String preferenceKey) {
        super(context, preferenceKey);
    }

    @Override
    public int getAvailabilityStatus() {
        return AVAILABLE;
    }

    @Override
    public boolean handlePreferenceTreeClick(Preference preference) {
        if (!TextUtils.equals(preference.getKey(), getPreferenceKey())) {
            return false;
        }

        if (PrivateSpaceMaintainer.getInstance(mContext).doesPrivateSpaceExist()) {
            showPrivateSpaceAlreadyExistsToast();
            return super.handlePreferenceTreeClick(preference);
        }

        if (PrivateSpaceMaintainer.getInstance(mContext).createPrivateSpace()) {
            showPrivateSpaceCreatedToast();
        } else {
            showPrivateSpaceCreationFailedToast();
        }
        return super.handlePreferenceTreeClick(preference);
    }

    private void showPrivateSpaceCreatedToast() {
        Toast.makeText(mContext, R.string.private_space_created, Toast.LENGTH_SHORT).show();
    }

    private void showPrivateSpaceCreationFailedToast() {
        Toast.makeText(mContext, R.string.private_space_create_failed, Toast.LENGTH_SHORT).show();
    }

    private void showPrivateSpaceAlreadyExistsToast() {
        Toast.makeText(mContext, R.string.private_space_already_exists, Toast.LENGTH_SHORT).show();
    }
}
+90 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 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.privatespace;

import static com.android.settings.privatespace.PrivateSpaceMaintainer.ErrorDeletingPrivateSpace.DELETE_PS_ERROR_INTERNAL;
import static com.android.settings.privatespace.PrivateSpaceMaintainer.ErrorDeletingPrivateSpace.DELETE_PS_ERROR_NONE;
import static com.android.settings.privatespace.PrivateSpaceMaintainer.ErrorDeletingPrivateSpace.DELETE_PS_ERROR_NO_PRIVATE_SPACE;

import android.content.Context;
import android.text.TextUtils;
import android.util.Log;
import android.widget.Toast;

import androidx.preference.Preference;

import com.android.internal.annotations.VisibleForTesting;
import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;

/** Controller to delete the private space from the PS Settings page */
public class DeletePrivateSpaceController extends BasePreferenceController {
    private static final String TAG = "DeletePrivateSpaceController";
    private final PrivateSpaceMaintainer mPrivateSpaceMaintainer;

    static class Injector {
        PrivateSpaceMaintainer injectPrivateSpaceMaintainer(Context context) {
            return PrivateSpaceMaintainer.getInstance(context);
        }
    }

    public DeletePrivateSpaceController(Context context, String preferenceKey) {
        this(context, preferenceKey, new Injector());
    }

    DeletePrivateSpaceController(Context context, String preferenceKey, Injector injector) {
        super(context, preferenceKey);
        mPrivateSpaceMaintainer = injector.injectPrivateSpaceMaintainer(context);
    }

    @Override
    public int getAvailabilityStatus() {
        return AVAILABLE;
    }

    @Override
    public boolean handlePreferenceTreeClick(Preference preference) {
        if (!TextUtils.equals(preference.getKey(), getPreferenceKey())) {
            return false;
        }

        PrivateSpaceMaintainer.ErrorDeletingPrivateSpace error =
                mPrivateSpaceMaintainer.deletePrivateSpace();
        if (error == DELETE_PS_ERROR_NONE) {
            showSuccessfulDeletionToast();
        } else if (error == DELETE_PS_ERROR_INTERNAL) {
            showDeletionInternalErrorToast();
        } else if (error == DELETE_PS_ERROR_NO_PRIVATE_SPACE) {
            // Ideally this should never happen as PS Settings is not available when there's no
            // Private Profile.
            Log.e(TAG, "Unexpected attempt to delete non-existent PS");
        }
        return super.handlePreferenceTreeClick(preference);
    }

    /** Shows a toast saying that the private space was deleted */
    @VisibleForTesting
    public void showSuccessfulDeletionToast() {
        Toast.makeText(mContext, R.string.private_space_deleted, Toast.LENGTH_SHORT).show();
    }

    /** Shows a toast saying that the private space could not be deleted */
    @VisibleForTesting
    public void showDeletionInternalErrorToast() {
        Toast.makeText(mContext, R.string.private_space_delete_failed, Toast.LENGTH_SHORT).show();
    }
}
+50 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 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.privatespace;

import android.content.Context;

import com.android.settings.core.TogglePreferenceController;

/** Represents the preference controller for (un)hiding the Private Space */
public final class HidePrivateSpaceController extends TogglePreferenceController {
    public HidePrivateSpaceController(Context context, String preferenceKey) {
        super(context, preferenceKey);
    }

    @Override
    public int getAvailabilityStatus() {
        return AVAILABLE;
    }

    @Override
    public boolean isChecked() {
        // TODO(b/293569406) Need to check this from a persistent store, maybe like SettingsProvider
        return false;
    }

    @Override
    public boolean setChecked(boolean isChecked) {
        // TODO(b/293569406) Need to save this to a persistent store, maybe like SettingsProvider
        return true;
    }

    @Override
    public int getSliceHighlightMenuRes() {
        return 0;
    }
}
Loading