1 /* 2 * Copyright 2017 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.tv.twopanelsettings.slices.compat.core; 18 19 import static android.app.slice.SliceItem.FORMAT_ACTION; 20 import static android.app.slice.SliceItem.FORMAT_SLICE; 21 22 import android.net.Uri; 23 import android.text.TextUtils; 24 import androidx.annotation.NonNull; 25 import androidx.annotation.Nullable; 26 import com.android.tv.twopanelsettings.slices.compat.Slice; 27 import com.android.tv.twopanelsettings.slices.compat.SliceItem; 28 import java.util.ArrayDeque; 29 import java.util.ArrayList; 30 import java.util.Collections; 31 import java.util.Deque; 32 import java.util.List; 33 34 /** Utilities for finding content within a Slice. */ 35 // @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) 36 // @Deprecated // Supported for TV 37 public class SliceQuery { 38 39 /** */ hasAnyHints(@onNull SliceItem item, @Nullable String... hints)40 public static boolean hasAnyHints(@NonNull SliceItem item, @Nullable String... hints) { 41 if (hints == null) { 42 return false; 43 } 44 for (String hint : hints) { 45 if (item.hasHint(hint)) { 46 return true; 47 } 48 } 49 return false; 50 } 51 52 /** */ hasHints(@onNull SliceItem item, @Nullable String... hints)53 public static boolean hasHints(@NonNull SliceItem item, @Nullable String... hints) { 54 if (hints == null) { 55 return true; 56 } 57 for (String hint : hints) { 58 if (!TextUtils.isEmpty(hint) && !item.hasHint(hint)) { 59 return false; 60 } 61 } 62 return true; 63 } 64 65 /** */ 66 @SuppressWarnings("unused") hasHints(@onNull Slice item, @Nullable String... hints)67 public static boolean hasHints(@NonNull Slice item, @Nullable String... hints) { 68 if (hints == null) { 69 return true; 70 } 71 for (String hint : hints) { 72 if (!TextUtils.isEmpty(hint) && !item.hasHint(hint)) { 73 return false; 74 } 75 } 76 return true; 77 } 78 79 /** */ 80 @SuppressWarnings("unused") 81 @Nullable findNotContaining( @ullable SliceItem container, @NonNull List<SliceItem> list)82 public static SliceItem findNotContaining( 83 @Nullable SliceItem container, @NonNull List<SliceItem> list) { 84 SliceItem ret = null; 85 while (ret == null && !list.isEmpty()) { 86 SliceItem remove = list.remove(0); 87 if (!contains(container, remove)) { 88 ret = remove; 89 } 90 } 91 return ret; 92 } 93 94 /** */ contains(@ullable SliceItem container, @Nullable final SliceItem item)95 private static boolean contains(@Nullable SliceItem container, @Nullable final SliceItem item) { 96 if (container == null || item == null) { 97 return false; 98 } 99 return findSliceItem(toQueue(container), s -> s == item) != null; 100 } 101 102 /** */ 103 @NonNull findAll(@onNull SliceItem s, @Nullable String format)104 public static List<SliceItem> findAll(@NonNull SliceItem s, @Nullable String format) { 105 return findAll(s, format, (String[]) null, null); 106 } 107 108 /** */ 109 @SuppressWarnings("unused") 110 @NonNull findAll( @onNull Slice s, @Nullable String format, @Nullable String hints, @Nullable String nonHints)111 public static List<SliceItem> findAll( 112 @NonNull Slice s, 113 @Nullable String format, 114 @Nullable String hints, 115 @Nullable String nonHints) { 116 return findAll(s, format, new String[] {hints}, new String[] {nonHints}); 117 } 118 119 /** */ 120 @NonNull findAll( @onNull SliceItem s, @Nullable String format, @Nullable String hints, @Nullable String nonHints)121 public static List<SliceItem> findAll( 122 @NonNull SliceItem s, 123 @Nullable String format, 124 @Nullable String hints, 125 @Nullable String nonHints) { 126 return findAll(s, format, new String[] {hints}, new String[] {nonHints}); 127 } 128 129 /** */ 130 @NonNull findAll( @onNull Slice s, @Nullable final String format, @Nullable final String[] hints, @Nullable final String[] nonHints)131 public static List<SliceItem> findAll( 132 @NonNull Slice s, 133 @Nullable final String format, 134 @Nullable final String[] hints, 135 @Nullable final String[] nonHints) { 136 ArrayList<SliceItem> ret = new ArrayList<>(); 137 findAll( 138 toQueue(s), 139 item -> 140 checkFormat(item, format) && (hasHints(item, hints) && !hasAnyHints(item, nonHints)), 141 ret); 142 return ret; 143 } 144 145 /** */ 146 @NonNull findAll( @onNull SliceItem s, @Nullable final String format, @Nullable final String[] hints, @Nullable final String[] nonHints)147 public static List<SliceItem> findAll( 148 @NonNull SliceItem s, 149 @Nullable final String format, 150 @Nullable final String[] hints, 151 @Nullable final String[] nonHints) { 152 ArrayList<SliceItem> ret = new ArrayList<>(); 153 findAll( 154 toQueue(s), 155 item -> 156 checkFormat(item, format) && (hasHints(item, hints) && !hasAnyHints(item, nonHints)), 157 ret); 158 return ret; 159 } 160 161 /** */ 162 @Nullable find( @ullable Slice s, @Nullable String format, @Nullable String hints, @Nullable String nonHints)163 public static SliceItem find( 164 @Nullable Slice s, 165 @Nullable String format, 166 @Nullable String hints, 167 @Nullable String nonHints) { 168 return find(s, format, new String[] {hints}, new String[] {nonHints}); 169 } 170 171 /** */ 172 @Nullable find(@ullable Slice s, @Nullable String format)173 public static SliceItem find(@Nullable Slice s, @Nullable String format) { 174 return find(s, format, (String[]) null, null); 175 } 176 177 /** */ 178 @Nullable find(@ullable SliceItem s, @Nullable String format)179 public static SliceItem find(@Nullable SliceItem s, @Nullable String format) { 180 return find(s, format, (String[]) null, null); 181 } 182 183 /** */ 184 @Nullable find( @ullable SliceItem s, @Nullable String format, @Nullable String hints, @Nullable String nonHints)185 public static SliceItem find( 186 @Nullable SliceItem s, 187 @Nullable String format, 188 @Nullable String hints, 189 @Nullable String nonHints) { 190 return find(s, format, new String[] {hints}, new String[] {nonHints}); 191 } 192 193 /** */ 194 @Nullable find( @ullable Slice s, @Nullable final String format, @Nullable final String[] hints, @Nullable final String[] nonHints)195 public static SliceItem find( 196 @Nullable Slice s, 197 @Nullable final String format, 198 @Nullable final String[] hints, 199 @Nullable final String[] nonHints) { 200 if (s == null) { 201 return null; 202 } 203 return findSliceItem( 204 toQueue(s), 205 item -> 206 checkFormat(item, format) && (hasHints(item, hints) && !hasAnyHints(item, nonHints))); 207 } 208 209 /** */ 210 @Nullable findSubtype( @ullable Slice s, @Nullable final String format, @Nullable final String subtype)211 public static SliceItem findSubtype( 212 @Nullable Slice s, @Nullable final String format, @Nullable final String subtype) { 213 if (s == null) { 214 return null; 215 } 216 return findSliceItem( 217 toQueue(s), item -> checkFormat(item, format) && checkSubtype(item, subtype)); 218 } 219 220 /** */ 221 @Nullable findSubtype( @ullable SliceItem s, @Nullable final String format, @Nullable final String subtype)222 public static SliceItem findSubtype( 223 @Nullable SliceItem s, @Nullable final String format, @Nullable final String subtype) { 224 if (s == null) { 225 return null; 226 } 227 return findSliceItem( 228 toQueue(s), item -> checkFormat(item, format) && checkSubtype(item, subtype)); 229 } 230 231 /** */ 232 @Nullable find( @ullable SliceItem s, @Nullable final String format, @Nullable final String[] hints, @Nullable final String[] nonHints)233 public static SliceItem find( 234 @Nullable SliceItem s, 235 @Nullable final String format, 236 @Nullable final String[] hints, 237 @Nullable final String[] nonHints) { 238 if (s == null) { 239 return null; 240 } 241 return findSliceItem( 242 toQueue(s), 243 item -> 244 checkFormat(item, format) && (hasHints(item, hints) && !hasAnyHints(item, nonHints))); 245 } 246 247 @SuppressWarnings("WeakerAccess") /* synthetic access */ checkFormat(@onNull SliceItem item, @Nullable String format)248 static boolean checkFormat(@NonNull SliceItem item, @Nullable String format) { 249 return format == null || format.equals(item.getFormat()); 250 } 251 252 @SuppressWarnings("WeakerAccess") /* synthetic access */ checkSubtype(@onNull SliceItem item, @Nullable String subtype)253 static boolean checkSubtype(@NonNull SliceItem item, @Nullable String subtype) { 254 return subtype == null || subtype.equals(item.getSubType()); 255 } 256 toQueue(@onNull Slice item)257 private static @NonNull Deque<SliceItem> toQueue(@NonNull Slice item) { 258 Deque<SliceItem> q = new ArrayDeque<>(); 259 Collections.addAll(q, item.getItemArray()); 260 return q; 261 } 262 toQueue(@onNull SliceItem item)263 private static @NonNull Deque<SliceItem> toQueue(@NonNull SliceItem item) { 264 Deque<SliceItem> q = new ArrayDeque<>(); 265 q.add(item); 266 return q; 267 } 268 269 @Nullable findSliceItem( @onNull final Deque<SliceItem> items, @NonNull Filter<SliceItem> f)270 private static SliceItem findSliceItem( 271 @NonNull final Deque<SliceItem> items, @NonNull Filter<SliceItem> f) { 272 while (!items.isEmpty()) { 273 SliceItem item = items.poll(); 274 if (f.filter(item)) { 275 return item; 276 } 277 if (item != null 278 && (FORMAT_SLICE.equals(item.getFormat()) || FORMAT_ACTION.equals(item.getFormat()))) { 279 Collections.addAll(items, item.getSlice().getItemArray()); 280 } 281 } 282 return null; 283 } 284 findAll( @onNull final Deque<SliceItem> items, @NonNull Filter<SliceItem> f, @NonNull List<SliceItem> out)285 private static void findAll( 286 @NonNull final Deque<SliceItem> items, 287 @NonNull Filter<SliceItem> f, 288 @NonNull List<SliceItem> out) { 289 while (!items.isEmpty()) { 290 SliceItem item = items.poll(); 291 if (f.filter(item)) { 292 out.add(item); 293 } 294 if (item != null 295 && (FORMAT_SLICE.equals(item.getFormat()) || FORMAT_ACTION.equals(item.getFormat()))) { 296 Collections.addAll(items, item.getSlice().getItemArray()); 297 } 298 } 299 } 300 301 /** Finds an item matching provided params that is a direct child of the slice. */ 302 @Nullable findTopLevelItem( @onNull Slice s, @Nullable final String format, @Nullable final String subtype, @Nullable final String[] hints, @Nullable final String[] nonHints)303 public static SliceItem findTopLevelItem( 304 @NonNull Slice s, 305 @Nullable final String format, 306 @Nullable final String subtype, 307 @Nullable final String[] hints, 308 @Nullable final String[] nonHints) { 309 SliceItem[] items = s.getItemArray(); 310 for (SliceItem item : items) { 311 if (checkFormat(item, format) 312 && checkSubtype(item, subtype) 313 && hasHints(item, hints) 314 && !hasAnyHints(item, nonHints)) { 315 return item; 316 } 317 } 318 return null; 319 } 320 321 /** */ 322 @Nullable findItem(@onNull Slice s, @NonNull final Uri uri)323 public static SliceItem findItem(@NonNull Slice s, @NonNull final Uri uri) { 324 return findSliceItem( 325 toQueue(s), 326 input -> { 327 if (FORMAT_ACTION.equals(input.getFormat()) || FORMAT_SLICE.equals(input.getFormat())) { 328 return uri.equals(input.getSlice().getUri()); 329 } 330 return false; 331 }); 332 } 333 334 private interface Filter<T> { 335 boolean filter(T input); 336 } 337 338 private SliceQuery() {} 339 } 340