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 }