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.documentsui.theme; 18 19 import android.content.Context; 20 import android.content.om.OverlayInfo; 21 import android.content.om.OverlayManager; 22 import android.content.pm.ApplicationInfo; 23 import android.content.pm.PackageManager; 24 import android.os.AsyncTask; 25 import android.os.UserHandle; 26 import android.util.Log; 27 28 import androidx.annotation.NonNull; 29 import androidx.annotation.Nullable; 30 import androidx.core.content.ContextCompat; 31 import androidx.core.util.Consumer; 32 33 import java.util.List; 34 35 /** 36 * ThemeOverlayManager manage runtime resource overlay packages of DocumentsUI 37 */ 38 public class ThemeOverlayManager { 39 private static final String TAG = ThemeOverlayManager.class.getSimpleName(); 40 private static final String PERMISSION_CHANGE_OVERLAY_PACKAGES = 41 "android.permission.CHANGE_OVERLAY_PACKAGES"; 42 43 private final OverlayManager mOverlayManager; 44 private String mTargetPackageId; 45 private UserHandle mUserHandle; 46 ThemeOverlayManager(@onNull OverlayManager overlayManager, String targetPackageId)47 public ThemeOverlayManager(@NonNull OverlayManager overlayManager, String targetPackageId) { 48 mOverlayManager = overlayManager; 49 mTargetPackageId = targetPackageId; 50 mUserHandle = UserHandle.of(UserHandle.myUserId()); 51 } 52 53 /** 54 * Apply runtime overlay package, dynamic enabled overlay do not support priority yet 55 * 56 * @param context the activity or context from caller 57 * @param enabled whether or not enable overlay package 58 */ applyOverlays(Context context, boolean enabled, Consumer<Boolean> callback)59 public void applyOverlays(Context context, boolean enabled, Consumer<Boolean> callback) { 60 if (ContextCompat.checkSelfPermission(context, PERMISSION_CHANGE_OVERLAY_PACKAGES) 61 == PackageManager.PERMISSION_GRANTED) { 62 setEnabled(enabled, callback); 63 } else { 64 Log.w(TAG, "Permission: " + PERMISSION_CHANGE_OVERLAY_PACKAGES + " did not granted!"); 65 callback.accept(false); 66 } 67 } 68 getOverlayInfo()69 private List<OverlayInfo> getOverlayInfo() { 70 // (b/132933212): Only static overlay package support priority attrs 71 // TODO: Alternative way to support enabled multiple overlay packages by priority is 72 // tag meta-data in the application of overlay package's AndroidManifest.xml 73 // TODO: Parse meta data through PM in DocumentsApplication and use collection to reorder 74 return mOverlayManager.getOverlayInfosForTarget(mTargetPackageId, mUserHandle); 75 } 76 77 /** 78 * Return the OverlayInfo which is provided by the docsUI overlay package pre-installed in 79 * device. We assume there should only one docsUI overlay package because priority not work 80 * for non-static overlay, so vendor should put only one docsUI overlay package. 81 * 82 * @param pm the PackageManager 83 */ 84 @Nullable getValidOverlay(@onNull PackageManager pm)85 public OverlayInfo getValidOverlay(@NonNull PackageManager pm) { 86 for (OverlayInfo info : getOverlayInfo()) { 87 try { 88 final ApplicationInfo ai = pm.getApplicationInfo(info.getPackageName(), 0); 89 if ((ai.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { 90 return info; 91 } 92 } catch (PackageManager.NameNotFoundException e) { 93 Log.w(TAG, "Can't get ApplicationInfo of overlay package " + info.getPackageName()); 94 } 95 } 96 return null; 97 } 98 setEnabled(boolean enabled, Consumer<Boolean> callback)99 private void setEnabled(boolean enabled, Consumer<Boolean> callback) { 100 new AsyncTask<Void, Void, Boolean>() { 101 @Override 102 protected Boolean doInBackground(Void... params) { 103 return setEnabledOverlayPackages(getOverlayInfo(), enabled); 104 } 105 106 @Override 107 protected void onPostExecute(Boolean result) { 108 super.onPostExecute(result); 109 if (callback != null) { 110 callback.accept(result); 111 } 112 } 113 }.execute(); 114 } 115 setEnabledOverlayPackages(List<OverlayInfo> infos, boolean enabled)116 private boolean setEnabledOverlayPackages(List<OverlayInfo> infos, boolean enabled) { 117 boolean bSuccess = true; 118 for (OverlayInfo info : infos) { 119 try { 120 if (info.isEnabled() != enabled) { 121 mOverlayManager.setEnabled(info.getPackageName(), enabled, mUserHandle); 122 } else { 123 Log.w(TAG, "Skip enabled overlay package:" + info.getPackageName() 124 + ", user:" + mUserHandle); 125 bSuccess = false; 126 } 127 } catch (RuntimeException re) { 128 Log.e(TAG, "Failed to enable overlay: " + info.getPackageName() + ", user: " 129 + mUserHandle); 130 bSuccess = false; 131 } 132 } 133 return bSuccess; 134 } 135 } 136