1 /* 2 * Copyright (C) 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License 15 */ 16 17 package com.android.settings.homepage.contextualcards.slices; 18 19 import static com.android.settings.core.BasePreferenceController.AVAILABLE_UNSEARCHABLE; 20 import static com.android.settings.display.AdaptiveSleepPreferenceController.PREF_NAME; 21 import static com.android.settings.display.AdaptiveSleepPreferenceController.isControllerAvailable; 22 import static com.android.settings.slices.CustomSliceRegistry.CONTEXTUAL_ADAPTIVE_SLEEP_URI; 23 24 import android.app.PendingIntent; 25 import android.app.settings.SettingsEnums; 26 import android.content.Context; 27 import android.content.Intent; 28 import android.net.Uri; 29 import android.provider.Settings; 30 31 import androidx.core.graphics.drawable.IconCompat; 32 import androidx.slice.Slice; 33 import androidx.slice.builders.ListBuilder; 34 import androidx.slice.builders.SliceAction; 35 36 import com.android.settings.R; 37 import com.android.settings.SubSettings; 38 import com.android.settings.display.AdaptiveSleepSettings; 39 import com.android.settings.slices.CustomSliceable; 40 import com.android.settings.slices.SliceBuilderUtils; 41 42 import com.google.common.annotations.VisibleForTesting; 43 44 import java.util.concurrent.TimeUnit; 45 46 public class ContextualAdaptiveSleepSlice implements CustomSliceable { 47 private static final String TAG = "ContextualAdaptiveSleepSlice"; 48 private static final long DEFAULT_SETUP_TIME = 0; 49 private Context mContext; 50 51 @VisibleForTesting 52 static final long DEFERRED_TIME_DAYS = TimeUnit.DAYS.toMillis(14); 53 @VisibleForTesting 54 static final String PREF_KEY_SETUP_TIME = "adaptive_sleep_setup_time"; 55 56 public static final String PREF_KEY_INTERACTED = "adaptive_sleep_interacted"; 57 public static final String PREF = "adaptive_sleep_slice"; 58 ContextualAdaptiveSleepSlice(Context context)59 public ContextualAdaptiveSleepSlice(Context context) { 60 mContext = context; 61 } 62 63 @Override getSlice()64 public Slice getSlice() { 65 final long setupTime = mContext.getSharedPreferences(PREF, Context.MODE_PRIVATE).getLong( 66 PREF_KEY_SETUP_TIME, DEFAULT_SETUP_TIME); 67 if (setupTime == DEFAULT_SETUP_TIME) { 68 // Set the first setup time. 69 mContext.getSharedPreferences(PREF, Context.MODE_PRIVATE) 70 .edit() 71 .putLong(PREF_KEY_SETUP_TIME, System.currentTimeMillis()) 72 .apply(); 73 return null; 74 } 75 76 // Display the contextual card only if all the following 4 conditions hold: 77 // 1. The Screen Attention is available in Settings. 78 // 2. The device is not recently set up. 79 // 3. Current user hasn't opened Screen Attention's settings page before. 80 // 4. Screen Attention is off. 81 if (isSettingsAvailable() && !isUserInteracted() && !isRecentlySetup() && !isTurnedOn()) { 82 final IconCompat icon = IconCompat.createWithResource(mContext, 83 R.drawable.ic_settings_adaptive_sleep); 84 final CharSequence title = mContext.getText(R.string.adaptive_sleep_title); 85 final CharSequence subtitle = mContext.getText( 86 R.string.adaptive_sleep_contextual_slice_summary); 87 88 final SliceAction pAction = SliceAction.createDeeplink(getPrimaryAction(), 89 icon, 90 ListBuilder.ICON_IMAGE, 91 title); 92 final ListBuilder listBuilder = new ListBuilder(mContext, 93 CONTEXTUAL_ADAPTIVE_SLEEP_URI, 94 ListBuilder.INFINITY) 95 .setAccentColor(COLOR_NOT_TINTED) 96 .addRow(new ListBuilder.RowBuilder() 97 .setTitleItem(icon, ListBuilder.ICON_IMAGE) 98 .setTitle(title) 99 .setSubtitle(subtitle) 100 .setPrimaryAction(pAction)); 101 return listBuilder.build(); 102 } else { 103 return null; 104 } 105 } 106 107 @Override getUri()108 public Uri getUri() { 109 return CONTEXTUAL_ADAPTIVE_SLEEP_URI; 110 } 111 112 @Override getIntent()113 public Intent getIntent() { 114 final CharSequence screenTitle = mContext.getText(R.string.adaptive_sleep_title); 115 final Uri contentUri = new Uri.Builder().appendPath(PREF_NAME).build(); 116 return SliceBuilderUtils.buildSearchResultPageIntent(mContext, 117 AdaptiveSleepSettings.class.getName(), PREF_NAME, screenTitle.toString(), 118 SettingsEnums.SLICE).setClassName(mContext.getPackageName(), 119 SubSettings.class.getName()).setData(contentUri); 120 } 121 getPrimaryAction()122 private PendingIntent getPrimaryAction() { 123 final Intent intent = getIntent(); 124 return PendingIntent.getActivity(mContext, 0 /* requestCode */, intent, 0 /* flags */); 125 } 126 127 /** 128 * @return {@code true} if the feature is turned on for the device, otherwise {@code false} 129 */ isTurnedOn()130 private boolean isTurnedOn() { 131 return Settings.System.getInt( 132 mContext.getContentResolver(), Settings.System.ADAPTIVE_SLEEP, 0) != 0; 133 } 134 135 /** 136 * @return {@code true} if the current user has opened the Screen Attention settings page 137 * before, otherwise {@code false}. 138 */ isUserInteracted()139 private boolean isUserInteracted() { 140 return mContext.getSharedPreferences(PREF, Context.MODE_PRIVATE).getBoolean( 141 PREF_KEY_INTERACTED, false); 142 } 143 144 /** 145 * The device is recently set up means its first settings-open time is within 2 weeks ago. 146 * 147 * @return {@code true} if the device is recently set up, otherwise {@code false}. 148 */ isRecentlySetup()149 private boolean isRecentlySetup() { 150 final long endTime = System.currentTimeMillis() - DEFERRED_TIME_DAYS; 151 final long firstSetupTime = mContext.getSharedPreferences(PREF, 152 Context.MODE_PRIVATE).getLong(PREF_KEY_SETUP_TIME, DEFAULT_SETUP_TIME); 153 return firstSetupTime > endTime; 154 } 155 156 /** 157 * Check whether the screen attention settings is enabled. Contextual card will only appear 158 * when the screen attention settings is available. 159 * 160 * @return {@code true} if screen attention settings is enabled, otherwise {@code false} 161 */ 162 @VisibleForTesting isSettingsAvailable()163 boolean isSettingsAvailable() { 164 return isControllerAvailable(mContext) == AVAILABLE_UNSEARCHABLE; 165 } 166 }