1 /* 2 * Copyright (C) 2024 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.settingslib.avatarpicker; 18 19 import android.os.Handler; 20 import android.os.Looper; 21 22 import androidx.annotation.NonNull; 23 24 import com.google.common.util.concurrent.ListenableFuture; 25 import com.google.common.util.concurrent.ListeningExecutorService; 26 import com.google.common.util.concurrent.MoreExecutors; 27 28 import java.util.concurrent.Callable; 29 import java.util.concurrent.Executors; 30 31 // copied from SettinsLib/utils 32 public class ThreadUtils { 33 34 private static volatile Thread sMainThread; 35 private static volatile Handler sMainThreadHandler; 36 private static volatile ListeningExecutorService sListeningService; 37 38 /** 39 * Returns true if the current thread is the UI thread. 40 */ isMainThread()41 public static boolean isMainThread() { 42 if (sMainThread == null) { 43 sMainThread = Looper.getMainLooper().getThread(); 44 } 45 return Thread.currentThread() == sMainThread; 46 } 47 48 /** 49 * Returns a shared UI thread handler. 50 */ 51 @NonNull getUiThreadHandler()52 public static Handler getUiThreadHandler() { 53 if (sMainThreadHandler == null) { 54 sMainThreadHandler = new Handler(Looper.getMainLooper()); 55 } 56 57 return sMainThreadHandler; 58 } 59 60 /** 61 * Checks that the current thread is the UI thread. Otherwise throws an exception. 62 */ ensureMainThread()63 public static void ensureMainThread() { 64 if (!isMainThread()) { 65 throw new RuntimeException("Must be called on the UI thread"); 66 } 67 } 68 69 /** 70 * Posts runnable in background using shared background thread pool. 71 * 72 * @return A future of the task that can be monitored for updates or cancelled. 73 */ 74 @SuppressWarnings("rawtypes") 75 @NonNull postOnBackgroundThread(@onNull Runnable runnable)76 public static ListenableFuture postOnBackgroundThread(@NonNull Runnable runnable) { 77 return getBackgroundExecutor().submit(runnable); 78 } 79 80 /** 81 * Posts callable in background using shared background thread pool. 82 * 83 * @return A future of the task that can be monitored for updates or cancelled. 84 */ 85 @NonNull postOnBackgroundThread(@onNull Callable<T> callable)86 public static <T> ListenableFuture<T> postOnBackgroundThread(@NonNull Callable<T> callable) { 87 return getBackgroundExecutor().submit(callable); 88 } 89 90 /** 91 * Posts the runnable on the main thread. 92 * 93 * @deprecated moving work to the main thread should be done via the main executor provided to 94 * {@link com.google.common.util.concurrent.FutureCallback} via 95 * {@link android.content.Context#getMainExecutor()} or by calling an SDK method such as 96 * {@link android.app.Activity#runOnUiThread(Runnable)} or 97 * {@link android.content.Context#getMainThreadHandler()} where appropriate. 98 */ 99 @Deprecated postOnMainThread(@onNull Runnable runnable)100 public static void postOnMainThread(@NonNull Runnable runnable) { 101 getUiThreadHandler().post(runnable); 102 } 103 104 /** 105 * Provides a shared {@link ListeningExecutorService} created using a fixed thread pool executor 106 */ 107 @NonNull getBackgroundExecutor()108 public static synchronized ListeningExecutorService getBackgroundExecutor() { 109 if (sListeningService == null) { 110 sListeningService = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool( 111 Runtime.getRuntime().availableProcessors())); 112 } 113 return sListeningService; 114 } 115 } 116