1 /* 2 * Copyright (C) 2022 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.display; 18 19 import android.content.ContentResolver; 20 import android.content.Context; 21 import android.content.Intent; 22 import android.content.pm.PackageManager; 23 import android.database.Cursor; 24 import android.net.Uri; 25 import android.text.TextUtils; 26 import android.util.Log; 27 28 import androidx.annotation.Nullable; 29 import androidx.annotation.VisibleForTesting; 30 31 import com.android.settings.R; 32 33 import java.util.ArrayList; 34 import java.util.List; 35 36 /** Utilities for display settings related to customizable lock screen features. */ 37 public final class CustomizableLockScreenUtils { 38 39 private static final String TAG = "CustomizableLockScreenUtils"; 40 private static final Uri BASE_URI = new Uri.Builder() 41 .scheme(ContentResolver.SCHEME_CONTENT) 42 .authority("com.android.systemui.customization") 43 .build(); 44 @VisibleForTesting 45 static final Uri FLAGS_URI = BASE_URI.buildUpon() 46 .path("flags") 47 .build(); 48 @VisibleForTesting 49 static final Uri SELECTIONS_URI = BASE_URI.buildUpon() 50 .appendPath("lockscreen_quickaffordance") 51 .appendPath("selections") 52 .build(); 53 @VisibleForTesting 54 static final String NAME = "name"; 55 @VisibleForTesting 56 static final String VALUE = "value"; 57 @VisibleForTesting 58 static final String ENABLED_FLAG = 59 "is_custom_lock_screen_quick_affordances_feature_enabled"; 60 @VisibleForTesting 61 static final String AFFORDANCE_NAME = "affordance_name"; 62 63 @VisibleForTesting 64 static final String WALLPAPER_LAUNCH_SOURCE = "com.android.wallpaper.LAUNCH_SOURCE"; 65 @VisibleForTesting 66 static final String LAUNCH_SOURCE_SETTINGS = "app_launched_settings"; 67 68 CustomizableLockScreenUtils()69 private CustomizableLockScreenUtils() {} 70 71 /** 72 * Queries and returns whether the customizable lock screen quick affordances feature is enabled 73 * on the device. 74 * 75 * <p>This is a slow, blocking call that shouldn't be made on the main thread. 76 */ isFeatureEnabled(Context context)77 public static boolean isFeatureEnabled(Context context) { 78 if (!isWallpaperPickerInstalled(context)) { 79 return false; 80 } 81 82 try (Cursor cursor = context.getContentResolver().query( 83 FLAGS_URI, 84 null, 85 null, 86 null)) { 87 if (cursor == null) { 88 Log.w(TAG, "Cursor was null!"); 89 return false; 90 } 91 92 final int indexOfNameColumn = cursor.getColumnIndex(NAME); 93 final int indexOfValueColumn = cursor.getColumnIndex(VALUE); 94 if (indexOfNameColumn == -1 || indexOfValueColumn == -1) { 95 Log.w(TAG, "Cursor doesn't contain " + NAME + " or " + VALUE + "!"); 96 return false; 97 } 98 99 while (cursor.moveToNext()) { 100 final String name = cursor.getString(indexOfNameColumn); 101 final int value = cursor.getInt(indexOfValueColumn); 102 if (TextUtils.equals(ENABLED_FLAG, name)) { 103 Log.d(TAG, ENABLED_FLAG + "=" + value); 104 return value == 1; 105 } 106 } 107 108 Log.w(TAG, "Flag with name \"" + ENABLED_FLAG + "\" not found!"); 109 return false; 110 } catch (Exception e) { 111 Log.e(TAG, "Exception while querying quick affordance content provider", e); 112 return false; 113 } 114 } 115 116 /** 117 * Queries and returns a summary text for the currently-selected lock screen quick affordances. 118 * 119 * <p>This is a slow, blocking call that shouldn't be made on the main thread. 120 */ 121 @Nullable getQuickAffordanceSummary(Context context)122 public static CharSequence getQuickAffordanceSummary(Context context) { 123 try (Cursor cursor = context.getContentResolver().query( 124 SELECTIONS_URI, 125 null, 126 null, 127 null)) { 128 if (cursor == null) { 129 Log.w(TAG, "Cursor was null!"); 130 return null; 131 } 132 133 final int columnIndex = cursor.getColumnIndex(AFFORDANCE_NAME); 134 if (columnIndex == -1) { 135 Log.w(TAG, "Cursor doesn't contain \"" + AFFORDANCE_NAME + "\" column!"); 136 return null; 137 } 138 139 final List<String> affordanceNames = new ArrayList<>(cursor.getCount()); 140 while (cursor.moveToNext()) { 141 final String affordanceName = cursor.getString(columnIndex); 142 if (!TextUtils.isEmpty(affordanceName)) { 143 affordanceNames.add(affordanceName); 144 } 145 } 146 147 // We don't display more than the first two items. 148 final int usableAffordanceNameCount = Math.min(2, affordanceNames.size()); 149 final List<String> arguments = new ArrayList<>(usableAffordanceNameCount); 150 if (!affordanceNames.isEmpty()) { 151 arguments.add(affordanceNames.get(0)); 152 } 153 if (affordanceNames.size() > 1) { 154 arguments.add(affordanceNames.get(1)); 155 } 156 157 return context.getResources().getQuantityString( 158 R.plurals.lockscreen_quick_affordances_summary, 159 usableAffordanceNameCount, 160 arguments.toArray()); 161 } catch (Exception e) { 162 Log.e(TAG, "Exception while querying quick affordance content provider", e); 163 return null; 164 } 165 } 166 167 /** 168 * Returns a new {@link Intent} that can be used to start the wallpaper picker 169 * activity. 170 */ newIntent()171 public static Intent newIntent() { 172 final Intent intent = new Intent(Intent.ACTION_SET_WALLPAPER); 173 // By adding the launch source here, we tell our destination (in this case, the wallpaper 174 // picker app) that it's been launched from within settings. That way, if we are in a 175 // multi-pane configuration (for example, for large screens), the wallpaper picker app can 176 // safely skip redirecting to the multi-pane version of its activity, as it's already opened 177 // within a multi-pane configuration context. 178 intent.putExtra(WALLPAPER_LAUNCH_SOURCE, LAUNCH_SOURCE_SETTINGS); 179 return intent; 180 } 181 isWallpaperPickerInstalled(Context context)182 private static boolean isWallpaperPickerInstalled(Context context) { 183 final PackageManager packageManager = context.getPackageManager(); 184 return newIntent().resolveActivity(packageManager) != null; 185 } 186 } 187