/* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.documentsui.theme; import android.content.Context; import android.content.om.OverlayInfo; import android.content.om.OverlayManager; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.os.AsyncTask; import android.os.UserHandle; import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.content.ContextCompat; import androidx.core.util.Consumer; import java.util.List; /** * ThemeOverlayManager manage runtime resource overlay packages of DocumentsUI */ public class ThemeOverlayManager { private static final String TAG = ThemeOverlayManager.class.getSimpleName(); private static final String PERMISSION_CHANGE_OVERLAY_PACKAGES = "android.permission.CHANGE_OVERLAY_PACKAGES"; private final OverlayManager mOverlayManager; private String mTargetPackageId; private UserHandle mUserHandle; public ThemeOverlayManager(@NonNull OverlayManager overlayManager, String targetPackageId) { mOverlayManager = overlayManager; mTargetPackageId = targetPackageId; mUserHandle = UserHandle.of(UserHandle.myUserId()); } /** * Apply runtime overlay package, dynamic enabled overlay do not support priority yet * * @param context the activity or context from caller * @param enabled whether or not enable overlay package */ public void applyOverlays(Context context, boolean enabled, Consumer callback) { if (ContextCompat.checkSelfPermission(context, PERMISSION_CHANGE_OVERLAY_PACKAGES) == PackageManager.PERMISSION_GRANTED) { setEnabled(enabled, callback); } else { Log.w(TAG, "Permission: " + PERMISSION_CHANGE_OVERLAY_PACKAGES + " did not granted!"); callback.accept(false); } } private List getOverlayInfo() { // (b/132933212): Only static overlay package support priority attrs // TODO: Alternative way to support enabled multiple overlay packages by priority is // tag meta-data in the application of overlay package's AndroidManifest.xml // TODO: Parse meta data through PM in DocumentsApplication and use collection to reorder return mOverlayManager.getOverlayInfosForTarget(mTargetPackageId, mUserHandle); } /** * Return the OverlayInfo which is provided by the docsUI overlay package pre-installed in * device. We assume there should only one docsUI overlay package because priority not work * for non-static overlay, so vendor should put only one docsUI overlay package. * * @param pm the PackageManager */ @Nullable public OverlayInfo getValidOverlay(@NonNull PackageManager pm) { for (OverlayInfo info : getOverlayInfo()) { try { final ApplicationInfo ai = pm.getApplicationInfo(info.getPackageName(), 0); if ((ai.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { return info; } } catch (PackageManager.NameNotFoundException e) { Log.w(TAG, "Can't get ApplicationInfo of overlay package " + info.getPackageName()); } } return null; } private void setEnabled(boolean enabled, Consumer callback) { new AsyncTask() { @Override protected Boolean doInBackground(Void... params) { return setEnabledOverlayPackages(getOverlayInfo(), enabled); } @Override protected void onPostExecute(Boolean result) { super.onPostExecute(result); if (callback != null) { callback.accept(result); } } }.execute(); } private boolean setEnabledOverlayPackages(List infos, boolean enabled) { boolean bSuccess = true; for (OverlayInfo info : infos) { try { if (info.isEnabled() != enabled) { mOverlayManager.setEnabled(info.getPackageName(), enabled, mUserHandle); } else { Log.w(TAG, "Skip enabled overlay package:" + info.getPackageName() + ", user:" + mUserHandle); bSuccess = false; } } catch (RuntimeException re) { Log.e(TAG, "Failed to enable overlay: " + info.getPackageName() + ", user: " + mUserHandle); bSuccess = false; } } return bSuccess; } }