Adds contextual cards for screen attention in Settings Homepage
Bug: 128527964 Test: atest ContextualAdaptiveSleepSliceTest, maually verified. Change-Id: Ifaea7d8d4391e91cf6cbde38a2506728f55913d8
This commit is contained in:
@@ -25,9 +25,9 @@ import com.android.settings.core.TogglePreferenceController;
|
||||
|
||||
|
||||
public class AdaptiveSleepPreferenceController extends TogglePreferenceController {
|
||||
|
||||
private final String SYSTEM_KEY = ADAPTIVE_SLEEP;
|
||||
private final int DEFAULT_VALUE = 0;
|
||||
public static final String PREF_NAME = "adaptive_sleep";
|
||||
private static final String SYSTEM_KEY = ADAPTIVE_SLEEP;
|
||||
private static final int DEFAULT_VALUE = 0;
|
||||
|
||||
final boolean hasSufficientPermissions;
|
||||
|
||||
@@ -35,9 +35,7 @@ public class AdaptiveSleepPreferenceController extends TogglePreferenceControlle
|
||||
super(context, key);
|
||||
|
||||
final PackageManager packageManager = mContext.getPackageManager();
|
||||
final String attentionPackage = packageManager.getAttentionServicePackageName();
|
||||
hasSufficientPermissions = attentionPackage != null && packageManager.checkPermission(
|
||||
Manifest.permission.CAMERA, attentionPackage) == PackageManager.PERMISSION_GRANTED;
|
||||
hasSufficientPermissions = hasSufficientPermission(packageManager);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -46,7 +44,6 @@ public class AdaptiveSleepPreferenceController extends TogglePreferenceControlle
|
||||
SYSTEM_KEY, DEFAULT_VALUE) != DEFAULT_VALUE;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean setChecked(boolean isChecked) {
|
||||
Settings.System.putInt(mContext.getContentResolver(), SYSTEM_KEY,
|
||||
@@ -57,10 +54,7 @@ public class AdaptiveSleepPreferenceController extends TogglePreferenceControlle
|
||||
@Override
|
||||
@AvailabilityStatus
|
||||
public int getAvailabilityStatus() {
|
||||
return mContext.getResources().getBoolean(
|
||||
com.android.internal.R.bool.config_adaptive_sleep_available)
|
||||
? AVAILABLE_UNSEARCHABLE
|
||||
: UNSUPPORTED_ON_DEVICE;
|
||||
return isControllerAvailable(mContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -69,4 +63,17 @@ public class AdaptiveSleepPreferenceController extends TogglePreferenceControlle
|
||||
? R.string.adaptive_sleep_summary_on
|
||||
: R.string.adaptive_sleep_summary_off);
|
||||
}
|
||||
|
||||
public static int isControllerAvailable(Context context) {
|
||||
return context.getResources().getBoolean(
|
||||
com.android.internal.R.bool.config_adaptive_sleep_available)
|
||||
? AVAILABLE_UNSEARCHABLE
|
||||
: UNSUPPORTED_ON_DEVICE;
|
||||
}
|
||||
|
||||
private static boolean hasSufficientPermission(PackageManager packageManager) {
|
||||
final String attentionPackage = packageManager.getAttentionServicePackageName();
|
||||
return attentionPackage != null && packageManager.checkPermission(
|
||||
Manifest.permission.CAMERA, attentionPackage) == PackageManager.PERMISSION_GRANTED;
|
||||
}
|
||||
}
|
||||
|
@@ -16,10 +16,15 @@
|
||||
|
||||
package com.android.settings.display;
|
||||
|
||||
import static com.android.settings.homepage.contextualcards.slices.ContextualAdaptiveSleepSlice.PREF;
|
||||
import static com.android.settings.homepage.contextualcards.slices.ContextualAdaptiveSleepSlice.PREF_KEY_INTERACTED;
|
||||
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.provider.SearchIndexableResource;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.dashboard.DashboardFragment;
|
||||
@@ -40,8 +45,15 @@ public class AdaptiveSleepSettings extends DashboardFragment {
|
||||
super.onCreate(icicle);
|
||||
final FooterPreference footerPreference =
|
||||
mFooterPreferenceMixin.createFooterPreference();
|
||||
final Context context = getContext();
|
||||
|
||||
footerPreference.setIcon(R.drawable.ic_privacy_shield_24dp);
|
||||
footerPreference.setTitle(R.string.adaptive_sleep_privacy);
|
||||
|
||||
context.getSharedPreferences(PREF, Context.MODE_PRIVATE)
|
||||
.edit()
|
||||
.putBoolean(PREF_KEY_INTERACTED, true)
|
||||
.apply();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -64,12 +64,21 @@ public class SettingsContextualCardProvider extends ContextualCardProvider {
|
||||
.setCardName(contextualNotificationChannelSliceUri)
|
||||
.setCardCategory(ContextualCard.Category.POSSIBLE)
|
||||
.build();
|
||||
final String contextualAdaptiveSleepSliceUri =
|
||||
CustomSliceRegistry.CONTEXTUAL_ADAPTIVE_SLEEP_URI.toString();
|
||||
final ContextualCard contextualAdaptiveSleepCard =
|
||||
ContextualCard.newBuilder()
|
||||
.setSliceUri(contextualAdaptiveSleepSliceUri)
|
||||
.setCardName(contextualAdaptiveSleepSliceUri)
|
||||
.setCardCategory(ContextualCard.Category.DEFAULT)
|
||||
.build();
|
||||
final ContextualCardList cards = ContextualCardList.newBuilder()
|
||||
.addCard(wifiCard)
|
||||
.addCard(connectedDeviceCard)
|
||||
.addCard(lowStorageCard)
|
||||
.addCard(batteryFixCard)
|
||||
.addCard(notificationChannelCard)
|
||||
.addCard(contextualAdaptiveSleepCard)
|
||||
.build();
|
||||
|
||||
return cards;
|
||||
|
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.homepage.contextualcards.slices;
|
||||
|
||||
import static com.android.settings.core.BasePreferenceController.AVAILABLE;
|
||||
import static com.android.settings.display.AdaptiveSleepPreferenceController.PREF_NAME;
|
||||
import static com.android.settings.display.AdaptiveSleepPreferenceController.isControllerAvailable;
|
||||
import static com.android.settings.slices.CustomSliceRegistry.CONTEXTUAL_ADAPTIVE_SLEEP_URI;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
|
||||
import androidx.core.graphics.drawable.IconCompat;
|
||||
import androidx.slice.Slice;
|
||||
import androidx.slice.builders.ListBuilder;
|
||||
import androidx.slice.builders.SliceAction;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.SubSettings;
|
||||
import com.android.settings.display.AdaptiveSleepSettings;
|
||||
import com.android.settings.slices.CustomSliceable;
|
||||
import com.android.settings.slices.SliceBuilderUtils;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class ContextualAdaptiveSleepSlice implements CustomSliceable {
|
||||
private static final String TAG = "ContextualAdaptiveSleepSlice";
|
||||
private static final long DEFAULT_SETUP_TIME = 0;
|
||||
private Context mContext;
|
||||
|
||||
@VisibleForTesting
|
||||
static final long DEFERRED_TIME_DAYS = TimeUnit.DAYS.toMillis(14);
|
||||
@VisibleForTesting
|
||||
static final String PREF_KEY_SETUP_TIME = "adaptive_sleep_setup_time";
|
||||
|
||||
public static final String PREF_KEY_INTERACTED = "adaptive_sleep_interacted";
|
||||
public static final String PREF = "adaptive_sleep_slice";
|
||||
|
||||
public ContextualAdaptiveSleepSlice(Context context) {
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slice getSlice() {
|
||||
final long setupTime = mContext.getSharedPreferences(PREF, Context.MODE_PRIVATE).getLong(
|
||||
PREF_KEY_SETUP_TIME, DEFAULT_SETUP_TIME);
|
||||
if (setupTime == DEFAULT_SETUP_TIME) {
|
||||
// Set the first setup time.
|
||||
mContext.getSharedPreferences(PREF, Context.MODE_PRIVATE)
|
||||
.edit()
|
||||
.putLong(PREF_KEY_SETUP_TIME, System.currentTimeMillis())
|
||||
.apply();
|
||||
return null;
|
||||
}
|
||||
|
||||
// Display the contextual card only if all the following 3 conditions hold:
|
||||
// 1. The Screen Attention is enabled in Settings.
|
||||
// 2. The device is not recently set up.
|
||||
// 3. Current user hasn't opened Screen Attention's settings page before.
|
||||
if (isSettingsAvailable() && !isUserInteracted() && !isRecentlySetup()) {
|
||||
final IconCompat icon = IconCompat.createWithResource(mContext,
|
||||
R.drawable.ic_settings_adaptive_sleep);
|
||||
final CharSequence title = mContext.getText(R.string.adaptive_sleep_title);
|
||||
final CharSequence subtitle = mContext.getText(R.string.adaptive_sleep_description);
|
||||
|
||||
final SliceAction pAction = SliceAction.createDeeplink(getPrimaryAction(),
|
||||
icon,
|
||||
ListBuilder.ICON_IMAGE,
|
||||
title);
|
||||
final ListBuilder listBuilder = new ListBuilder(mContext,
|
||||
CONTEXTUAL_ADAPTIVE_SLEEP_URI,
|
||||
ListBuilder.INFINITY)
|
||||
.addRow(new ListBuilder.RowBuilder()
|
||||
.setTitleItem(icon, ListBuilder.ICON_IMAGE)
|
||||
.setTitle(title)
|
||||
.setSubtitle(subtitle)
|
||||
.setPrimaryAction(pAction));
|
||||
return listBuilder.build();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Uri getUri() {
|
||||
return CONTEXTUAL_ADAPTIVE_SLEEP_URI;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Intent getIntent() {
|
||||
final CharSequence screenTitle = mContext.getText(R.string.adaptive_sleep_title);
|
||||
final Uri contentUri = new Uri.Builder().appendPath(PREF_NAME).build();
|
||||
return SliceBuilderUtils.buildSearchResultPageIntent(mContext,
|
||||
AdaptiveSleepSettings.class.getName(), PREF_NAME, screenTitle.toString(),
|
||||
SettingsEnums.SLICE).setClassName(mContext.getPackageName(),
|
||||
SubSettings.class.getName()).setData(contentUri);
|
||||
}
|
||||
|
||||
private PendingIntent getPrimaryAction() {
|
||||
final Intent intent = getIntent();
|
||||
return PendingIntent.getActivity(mContext, 0 /* requestCode */, intent, 0 /* flags */);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@code true} if the current user has opened the Screen Attention settings page
|
||||
* before, otherwise {@code false}.
|
||||
*/
|
||||
private boolean isUserInteracted() {
|
||||
return mContext.getSharedPreferences(PREF, Context.MODE_PRIVATE).getBoolean(
|
||||
PREF_KEY_INTERACTED, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* The device is recently set up means its first settings-open time is within 2 weeks ago.
|
||||
*
|
||||
* @return {@code true} if the device is recently set up, otherwise {@code false}.
|
||||
*/
|
||||
private boolean isRecentlySetup() {
|
||||
final long endTime = System.currentTimeMillis() - DEFERRED_TIME_DAYS;
|
||||
final long firstSetupTime = mContext.getSharedPreferences(PREF,
|
||||
Context.MODE_PRIVATE).getLong(PREF_KEY_SETUP_TIME, DEFAULT_SETUP_TIME);
|
||||
return firstSetupTime > endTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the screen attention settings is enabled. Contextual card will only appear
|
||||
* when the screen attention settings is available.
|
||||
*
|
||||
* @return {@code true} if screen attention settings is enabled, otherwise {@code false}
|
||||
*/
|
||||
@VisibleForTesting
|
||||
boolean isSettingsAvailable() {
|
||||
return isControllerAvailable(mContext) == AVAILABLE;
|
||||
}
|
||||
}
|
@@ -26,6 +26,7 @@ import android.util.ArrayMap;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import com.android.settings.display.AdaptiveSleepPreferenceController;
|
||||
import com.android.settings.flashlight.FlashlightSlice;
|
||||
import com.android.settings.fuelgauge.batterytip.BatteryTipPreferenceController;
|
||||
import com.android.settings.homepage.contextualcards.deviceinfo.DataUsageSlice;
|
||||
@@ -34,6 +35,7 @@ import com.android.settings.homepage.contextualcards.deviceinfo.EmergencyInfoSli
|
||||
import com.android.settings.homepage.contextualcards.deviceinfo.StorageSlice;
|
||||
import com.android.settings.homepage.contextualcards.slices.BatteryFixSlice;
|
||||
import com.android.settings.homepage.contextualcards.slices.BluetoothDevicesSlice;
|
||||
import com.android.settings.homepage.contextualcards.slices.ContextualAdaptiveSleepSlice;
|
||||
import com.android.settings.homepage.contextualcards.slices.ContextualNotificationChannelSlice;
|
||||
import com.android.settings.homepage.contextualcards.slices.LowStorageSlice;
|
||||
import com.android.settings.homepage.contextualcards.slices.NotificationChannelSlice;
|
||||
@@ -64,6 +66,16 @@ public class CustomSliceRegistry {
|
||||
.appendPath(SettingsSlicesContract.KEY_AIRPLANE_MODE)
|
||||
.build();
|
||||
|
||||
/**
|
||||
* Uri for Contextual Adaptive Sleep Slice
|
||||
*/
|
||||
public static final Uri CONTEXTUAL_ADAPTIVE_SLEEP_URI = new Uri.Builder()
|
||||
.scheme(ContentResolver.SCHEME_CONTENT)
|
||||
.authority(SettingsSliceProvider.SLICE_AUTHORITY)
|
||||
.appendPath(SettingsSlicesContract.PATH_SETTING_INTENT)
|
||||
.appendPath(AdaptiveSleepPreferenceController.PREF_NAME)
|
||||
.build();
|
||||
|
||||
/**
|
||||
* Uri for Battery Fix Slice.
|
||||
*/
|
||||
@@ -328,6 +340,7 @@ public class CustomSliceRegistry {
|
||||
|
||||
sUriToSlice.put(BATTERY_FIX_SLICE_URI, BatteryFixSlice.class);
|
||||
sUriToSlice.put(BLUETOOTH_DEVICES_SLICE_URI, BluetoothDevicesSlice.class);
|
||||
sUriToSlice.put(CONTEXTUAL_ADAPTIVE_SLEEP_URI, ContextualAdaptiveSleepSlice.class);
|
||||
sUriToSlice.put(CONTEXTUAL_NOTIFICATION_CHANNEL_SLICE_URI,
|
||||
ContextualNotificationChannelSlice.class);
|
||||
sUriToSlice.put(CONTEXTUAL_WIFI_SLICE_URI, ContextualWifiSlice.class);
|
||||
@@ -337,12 +350,12 @@ public class CustomSliceRegistry {
|
||||
sUriToSlice.put(FLASHLIGHT_SLICE_URI, FlashlightSlice.class);
|
||||
sUriToSlice.put(LOCATION_SLICE_URI, LocationSlice.class);
|
||||
sUriToSlice.put(LOW_STORAGE_SLICE_URI, LowStorageSlice.class);
|
||||
sUriToSlice.put(MEDIA_OUTPUT_INDICATOR_SLICE_URI, MediaOutputIndicatorSlice.class);
|
||||
sUriToSlice.put(MEDIA_OUTPUT_SLICE_URI, MediaOutputSlice.class);
|
||||
sUriToSlice.put(MOBILE_DATA_SLICE_URI, MobileDataSlice.class);
|
||||
sUriToSlice.put(NOTIFICATION_CHANNEL_SLICE_URI, NotificationChannelSlice.class);
|
||||
sUriToSlice.put(STORAGE_SLICE_URI, StorageSlice.class);
|
||||
sUriToSlice.put(WIFI_SLICE_URI, WifiSlice.class);
|
||||
sUriToSlice.put(MEDIA_OUTPUT_SLICE_URI, MediaOutputSlice.class);
|
||||
sUriToSlice.put(MEDIA_OUTPUT_INDICATOR_SLICE_URI, MediaOutputIndicatorSlice.class);
|
||||
}
|
||||
|
||||
public static Class<? extends CustomSliceable> getSliceClassByUri(Uri uri) {
|
||||
|
Reference in New Issue
Block a user