Skip to content
Draft
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions app/src/main/assets/config_webview_packages.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2016 The CyanogenMod Project
(C) 2019 The LineageOS 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.
-->

<webviewproviders>

<webviewprovider description="Mulch WebView" packageName="us.spotco.mulch_wv" availableByDefault="true">
<signature>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</signature>
</webviewprovider>
<webviewprovider description="Bromite WebView" packageName="org.bromite.webview" >
<signature>MIIDbTCCAlWgAwIBAgIEHcsmjjANBgkqhkiG9w0BAQsFADBmMQswCQYDVQQGEwJERTEQMA4GA1UECBMHVW5rbm93bjEPMA0GA1UEBxMGQmVybGluMRAwDgYDVQQKEwdCcm9taXRlMRAwDgYDVQQLEwdCcm9taXRlMRAwDgYDVQQDEwdjc2FnYW41MCAXDTE4MDExOTA3MjE1N1oYDzIwNjgwMTA3MDcyMTU3WjBmMQswCQYDVQQGEwJERTEQMA4GA1UECBMHVW5rbm93bjEPMA0GA1UEBxMGQmVybGluMRAwDgYDVQQKEwdCcm9taXRlMRAwDgYDVQQLEwdCcm9taXRlMRAwDgYDVQQDEwdjc2FnYW41MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtakjGj0eTavbBB2vWXj8KBixWn4zgXAKc+yGFu3SLEGF1VB5aJWwcMHxVI55yH/8M2eNnJP0BkSidfKgPVcm1sk/GrNEs9uk5sWod9byO5M5QWQmGP2REeTd6J0BVVVaMp2MZnqeR3Su3pwFzrSwTqIGyf8dkPSEz7ifj792+EeRNrov4oRQK7lIfqInzwc4d34wU069Lrw6m7J7HM0KbRYISsWMiYj025Qg+dTrtdWt7jbdcj7htW0eYyJoLd90+s43RWnOpENmWpcWv1EVPxUD4mCdV9idYwoHRIESpSu9IWvqDZp1VoRc43nLgsNfNBwmYdTkIaPiz1m7TBcr7QIDAQABoyEwHzAdBgNVHQ4EFgQUuWoGd7W7wMyQ1pOdjiMv10YHTR0wDQYJKoZIhvcNAQELBQADggEBAA7iw6eKz+T8HIpKDoDcX1Ywjn9JUzuCFu20LnsLzreO/Pog1xErYjdLAS7LTZokfbAnitBskO9QhV9BYkDiM0Qr5v2/HsJTtxa1mz9ywCcI36jblMyuXFj8tuwQI9/t9i+Fc3+bOFBV3t7djPo9qX1dIK0lZ6s8HcIhaCNdqm65fH+nWhC/H9djqC6qOtrkTiACKEcHQ4a/5dfROU0q0M4bS4YuiaAQWgjiGbik4LrZ8wZX1aqJCLt0Hs7MzXyyf0cRSO11FIOViHwzh6WTZGufq2J3YBFXPond8kLxkKL3LNezbi5yTcecxsbKQ6OS46CnIKcy/M8asSreLpoCDvw=</signature>
</webviewprovider>

<!-- The default WebView implementation -->
<webviewprovider description="AOSP WebView" packageName="com.android.webview" isFallback="true" />

</webviewproviders>
Original file line number Diff line number Diff line change
@@ -1,17 +1,183 @@
package io.github.muntashirakon.AppManager.compat;

import static com.android.internal.R.*;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.om.FabricatedOverlay;
import android.content.om.FabricatedOverlayHidden;
import android.content.om.IOverlayManager;
import android.content.om.OverlayIdentifier;
import android.content.om.OverlayManagerTransaction;
import android.content.om.OverlayManagerTransactionHidden;
import android.content.pm.PackageManager;
import android.content.res.AssetFileDescriptor;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.Build;
import android.os.ParcelFileDescriptor;
import android.util.TypedValue;

import androidx.annotation.AnyRes;
import androidx.annotation.Discouraged;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.annotation.RequiresPermission;
import androidx.annotation.ReturnThis;
import androidx.annotation.VisibleForTesting;

import com.topjohnwu.superuser.ShellUtils;

import org.jetbrains.annotations.Contract;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Random;

import dev.rikka.tools.refine.Refine;
import io.github.muntashirakon.AppManager.AppManager;
import io.github.muntashirakon.AppManager.BuildConfig;
import io.github.muntashirakon.AppManager.R;
import io.github.muntashirakon.AppManager.ipc.ProxyBinder;
import io.github.muntashirakon.AppManager.logs.Log;
import io.github.muntashirakon.AppManager.server.common.Shell;
import io.github.muntashirakon.AppManager.utils.ContextUtils;
import io.github.muntashirakon.AppManager.utils.ResourceUtil;

@RequiresApi(Build.VERSION_CODES.O)
@SuppressWarnings("NewApi")
public class OverlayManagerCompact {
public static final String TAG = OverlayManagerCompact.class.getSimpleName();

@RequiresPermission(ManifestCompat.permission.CHANGE_OVERLAY_PACKAGES)
public static IOverlayManager getOverlayManager() {
return IOverlayManager.Stub.asInterface(ProxyBinder.getService("overlay"));
}
//Webview Config Info toString: TypedValue{t=0x3/d=0x37 "res/xml/config_webview_packages.xml" a=5 r=0x1170007} assetCookie: 0x5 dataType: 0x3

public static void createFabOverlayTest(String name) {
Log.d(TAG, "createWebviewOverlay() called with: name = [" + name + "]");
FabricatedOverlayBuilder.getPersistentOverlayBuilder(name, "net.tharow.overlaytarget")
.setResourceValue("net.tharow.overlaytarget:string/lorem_ipsum", TypedValue.TYPE_STRING, "MagicOverride", null)
.commit();

}

public static class FabricatedOverlayBuilder {
private final FabricatedOverlayHidden internal;
private final Resources packageResources;
private final Resources amResources;

@NonNull
@Contract("_, _ -> new")
public static FabricatedOverlayBuilder getNonPersistentOverlayBuilder(@NonNull String overlayName, @NonNull String packageName) {
ResourceUtil u = new ResourceUtil();
Context amc = ContextUtils.getContext();
if (u.loadAndroidResources() || u.resources == null) {
throw new IllegalStateException("android Resources failed to load");
}
return new FabricatedOverlayBuilder(amc, overlayName, packageName, u.resources.getString(string.config_systemShell));
}

@NonNull
@Contract("_, _ -> new")
public static FabricatedOverlayBuilder getPersistentOverlayBuilder(@NonNull String overlayName, @NonNull String packageName) {
return new FabricatedOverlayBuilder(ContextUtils.getContext(), overlayName, packageName, packageName);
}

public FabricatedOverlayBuilder(@NonNull Context parent, @NonNull String overlayName, @NonNull String packageName, @NonNull String owningPackage) {
this.internal = Refine.unsafeCast(new FabricatedOverlay(overlayName, packageName));
ResourceUtil resourceUtil = new ResourceUtil();
PackageManager pm = ContextUtils.getContext().getPackageManager();
this.amResources = parent.getResources();
resourceUtil.loadResources(pm, packageName);
if (resourceUtil.resources == null) {
throw new IllegalStateException("Failed to load resources");
}
this.packageResources = resourceUtil.resources;
this.internal.setOwningPackage(owningPackage);
}

@ReturnThis
public FabricatedOverlayBuilder setResValue(@AnyRes int pkgRes, @AnyRes int amRes) {
setResValue(pkgRes, amRes, null);
return this;
}

@ReturnThis
public FabricatedOverlayBuilder setResValue(@AnyRes int ogId, @AnyRes int newId, @Nullable String configuration) {
final String name = packageResources.getResourceName(ogId);
final TypedValue value = new TypedValue();
final TypedValue newValue = new TypedValue();
packageResources.getValue(ogId, value, true);
amResources.getValue(newId, newValue, true);
if (value.type != newValue.type) {
throw new IllegalArgumentException("Given Res Id's are not the same type");
}
switch (value.type) {
case TypedValue.TYPE_STRING:
setResourceValue(name, value.type, newValue.string.toString(), configuration);
break;
case TypedValue.TYPE_INT_BOOLEAN:
case TypedValue.TYPE_INT_DEC:
case TypedValue.TYPE_INT_HEX:
case TypedValue.TYPE_INT_COLOR_ARGB4:
case TypedValue.TYPE_INT_COLOR_ARGB8:
case TypedValue.TYPE_INT_COLOR_RGB4:
case TypedValue.TYPE_INT_COLOR_RGB8:
setResourceValue(name, value.type, newValue.data, configuration);
default:
throw new IllegalStateException("Unexpected value: " + value.type);
}
return this;
}

@NonNull
public OverlayIdentifier getIdentifier() {
return internal.getIdentifier();
}
@ReturnThis
public FabricatedOverlayBuilder setOwningPackage(@NonNull String owningPackage) {
internal.setOwningPackage(owningPackage);
return this;
}
@ReturnThis
public FabricatedOverlayBuilder setTargetOverlayable(@Nullable String targetOverlayable) {
internal.setTargetOverlayable(targetOverlayable);
return this;
}
@ReturnThis
public FabricatedOverlayBuilder setResourceValue(@NonNull String resourceName, int dataType, int value, @Nullable String configuration) {
internal.setResourceValue(resourceName, dataType, value, configuration);
return this;
}
@ReturnThis
public FabricatedOverlayBuilder setResourceValue(@NonNull String resourceName, @NonNull ParcelFileDescriptor value, @Nullable String configuration) {
internal.setResourceValue(resourceName, value, configuration);
return this;
}
@ReturnThis
public FabricatedOverlayBuilder setResourceValue(@NonNull String resourceName, int dataType, @NonNull String value, @Nullable String configuration) {
internal.setResourceValue(resourceName, dataType, value, configuration);
return this;
}
@ReturnThis
public FabricatedOverlayBuilder setNinePatchResourceValue(@NonNull String resourceName, @NonNull ParcelFileDescriptor value, @Nullable String configuration) {
internal.setNinePatchResourceValue(resourceName, value, configuration);
return this;
}
@ReturnThis
public FabricatedOverlayBuilder setResourceValue(@NonNull String resourceName, @NonNull AssetFileDescriptor value, @Nullable String configuration) {
internal.setResourceValue(resourceName, value, configuration);
return this;
}
public FabricatedOverlay build() {
return Refine.unsafeCast(internal);
}
public void commit() {
getOverlayManager().commit(new OverlayManagerTransactionHidden.Builder().registerFabricatedOverlay(this.build()).build());
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.github.muntashirakon.AppManager.details;

import android.content.om.IOverlayManager;
import android.content.om.OverlayManagerTransactionHidden;
import android.graphics.Color;
import android.os.Build;
import android.os.Bundle;
Expand Down Expand Up @@ -41,6 +42,7 @@
import io.github.muntashirakon.widget.MaterialAlertView;
import io.github.muntashirakon.widget.RecyclerView;

@RequiresApi(Build.VERSION_CODES.O)
public class AppDetailsOverlaysFragment extends AppDetailsFragment {

private static final String TAG = AppDetailsOverlaysFragment.class.getSimpleName();
Expand Down Expand Up @@ -112,6 +114,10 @@ public boolean onMenuItemSelected(@NonNull MenuItem menuItem) {
} else if (id == R.id.action_sort_by_priority) {
setSortBy(SORT_BY_PRIORITY);
menuItem.setChecked(true);
} else if (id == R.id.action_create_overlay) {
OverlayManagerCompact.createFabOverlayTest("AM"+Integer.toHexString(this.hashCode())+"Overlay");
menuItem.setChecked(true);
refreshDetails();
} else return false;
return true;
}
Expand Down Expand Up @@ -182,6 +188,8 @@ public void onBindViewHolder(@NonNull ViewHolder holder, int index) {
synchronized (mAdapterList) {
overlayItem = (AppDetailsOverlayItem) mAdapterList.get(index);
}
if (viewModel==null)
return;
String overlayName = overlayItem.name;

if (mConstraint != null && overlayName.toLowerCase(Locale.ROOT).contains(mConstraint)) {
Expand Down Expand Up @@ -216,7 +224,14 @@ public void onBindViewHolder(@NonNull ViewHolder holder, int index) {
holder.toggleSwitch.setOnClickListener((v) -> ThreadUtils.postOnBackgroundThread(() -> {
try {
// TODO: 2/18/25 Move to ViewModel
if (overlayItem.setEnabled(overlayManager, !overlayItem.isEnabled())) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
overlayManager.commit(new OverlayManagerTransactionHidden.Builder()
.setEnabled(
overlayItem.getOverlayIdentifier(),
!overlayItem.isEnabled(),
viewModel.getUserId()
).build());
} else if (overlayItem.setEnabled(overlayManager, !overlayItem.isEnabled())) {
ThreadUtils.postOnMainThread(() -> notifyItemChanged(index));
} else throw new Exception("Error Changing Overlay State " + overlayItem);
} catch (Exception e) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package io.github.muntashirakon.AppManager.details.struct;

import android.annotation.SuppressLint;
import android.annotation.UserIdInt;
import android.content.om.IOverlayManager;
import android.content.om.OverlayIdentifier;
import android.content.om.OverlayInfo;
import android.content.om.OverlayInfoHidden;
import android.os.Build;
Expand Down Expand Up @@ -56,6 +58,12 @@ public boolean isMutable() {
return true;
}

@NonNull
@RequiresApi(Build.VERSION_CODES.S)
public OverlayIdentifier getOverlayIdentifier() {
return item.getOverlayIdentifier();
}

public String getReadableState() {
return stateToString(item.state);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,26 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.AssetFileDescriptor;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.util.Log;
import android.util.TypedValue;

import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.annotation.XmlRes;
import androidx.core.content.res.ResourcesCompat;

import java.io.IOException;

public final class ResourceUtil {

private static final String TAG = ResourceUtil.class.getSimpleName();

public static class ParsedResource {
@NonNull
private final String mPackageName;
Expand Down Expand Up @@ -136,4 +147,23 @@ public String getString(@NonNull String stringRes) throws Resources.NotFoundExce
if (intStringRes == 0) throw new Resources.NotFoundException("String resource ID " + stringRes);
return this.resources.getString(intStringRes);
}

@SuppressWarnings("NewApi")
@NonNull
public static AssetFileDescriptor getXmlAssetFd(Resources resources, @XmlRes int id) throws Resources.NotFoundException {
Log.d(TAG, "getXmlAssetFd() called with: resources = [" + resources + "], id = [" + id + "]");
final TypedValue value = new TypedValue();
if (resources == null) {
throw new Resources.NotFoundException("Resources Not Init");
}
try {
resources.getValue(id, value, true);
if (value.type == TypedValue.TYPE_STRING) {
return resources.getAssets().openNonAssetFd(value.assetCookie, value.string.toString());
}
throw new Resources.NotFoundException("Resource ID #0x" + Integer.toHexString(id) + " type #0x "+Integer.toHexString(value.type) + " is not valid");
} catch (IOException e) {
throw new Resources.NotFoundException("Resource ID #0x "+ Integer.toHexString(id), e);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@
android:title="@string/refresh"
app:showAsAction="never" />

<item
android:id="@+id/action_create_overlay"
android:icon="@drawable/ic_add"
android:title="@string/action_create_overlay"
app:showAsAction="never" />

<item
android:icon="@drawable/ic_sort"
android:title="@string/sort"
Expand Down
Loading