Skip to content

Commit

Permalink
Add market scheme AppInfoOwners, fix rect icons
Browse files Browse the repository at this point in the history
- Market scheme includes Xiaomi and Huawei app stores
- Change splash screen icon from adaptive shape to static circle to work around rect mask on MIUI and EMUI/HarmonyOS
  • Loading branch information
cliuff committed Sep 12, 2023
1 parent 3905bb3 commit 7b7d521
Show file tree
Hide file tree
Showing 9 changed files with 213 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,16 @@ class StandardAppInfoOwner(override val packageName: String, val activityName: S
}
}

class MarketAppInfoOwner(override val packageName: String, val activityName: String) : AppInfoOwner {
override fun showAppInfo(appPkgName: String, context: Context) {
val uri = Uri.parse("market://details?id=$appPkgName")
val intent = Intent(Intent.ACTION_VIEW, uri)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
.setComponent(ComponentName(packageName, activityName))
context.startActivity(intent)
}
}

class SettingsAppInfoOwner(override val packageName: String) : AppInfoOwner {
override fun showAppInfo(appPkgName: String, context: Context) {
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
Expand Down Expand Up @@ -78,6 +88,19 @@ object CoolApkAppInfoOwner : AppInfoOwner {
}
}

object XiaomiAppInfoOwner : AppInfoOwner {
private const val packageXiaomiMarket = "com.xiaomi.market"
override val packageName: String = packageXiaomiMarket

override fun showAppInfo(appPkgName: String, context: Context) {
val uri = Uri.parse("mimarket://details/detailmini?id=$appPkgName")
val intent = Intent(Intent.ACTION_VIEW, uri)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
.setPackage(packageName)
context.startActivity(intent)
}
}

object AppManagerAppInfoOwner : AppInfoOwner {
private const val packageAppManager = "io.github.muntashirakon.AppManager"
private const val infoActivity = "io.github.muntashirakon.AppManager.details.AppInfoActivity"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package com.madness.collision.unit.api_viewing.env
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.provider.Settings
import com.madness.collision.BuildConfig
import com.madness.collision.util.os.OsUtils
Expand Down Expand Up @@ -64,18 +65,39 @@ object EnvPackages {
}
}

fun getMarketAppInfoOwners(context: Context): List<MarketAppInfoOwner> {
val intent = Intent(Intent.ACTION_VIEW)
.addCategory(Intent.CATEGORY_DEFAULT)
.setData(Uri.parse("market://details"))
val pm = context.packageManager
val activities = when {
OsUtils.dissatisfy(OsUtils.T) -> pm.queryIntentLegacy(intent)
else -> pm.queryIntentActivities(intent, PackageManager.ResolveInfoFlags.of(0))
}
return activities.mapNotNull owner@{ activity ->
val info = activity.activityInfo ?: return@owner null
MarketAppInfoOwner(info.packageName, info.name)
}
}

fun getInstalledAppInfoOwners(context: Context): List<AppInfoOwner> {
val packageSettings = getDefaultSettings(context) ?: "com.android.settings"
val standardOwners = getStandardAppInfoOwners(context)
val map = HashMap<String, AppInfoOwner>(standardOwners.size + 4).apply {
val marketOwners = getMarketAppInfoOwners(context)
val map = HashMap<String, AppInfoOwner>(standardOwners.size + marketOwners.size + 5).apply {
// Google Play Store itself is a standard owner, this serves as a fallback
put(GooglePlayAppInfoOwner.packageName, GooglePlayAppInfoOwner)
// CoolApk itself is a market owner, this serves as a fallback
put(CoolApkAppInfoOwner.packageName, CoolApkAppInfoOwner)
put(AppManagerAppInfoOwner.packageName, AppManagerAppInfoOwner)
put(packageSettings, SettingsAppInfoOwner(packageSettings))
// put standard owners afterwards to override custom owners, in reversed order
// (in case one package / multiple activities, to retain the smaller-index one)
putAll(marketOwners.asReversed().associateBy { it.packageName })
putAll(standardOwners.asReversed().associateBy { it.packageName })
// Xiaomi app store itself is a market owner that blocks app details intent,
// use this custom owner instead as a workaround
put(XiaomiAppInfoOwner.packageName, XiaomiAppInfoOwner)
// remove self owner (though N/A right now)
remove(BuildConfig.APPLICATION_ID)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -265,17 +265,22 @@ private fun Sequence<AppListService.AppInfoItem>.annotated() = buildAnnotatedStr
@Composable
private fun ExternalActionItem(packInfo: AppIconPackageInfo?, onClick: () -> Unit) {
if (packInfo != null) {
Image(
modifier = Modifier
.clickable(
onClick = onClick,
interactionSource = remember { MutableInteractionSource() },
indication = rememberRipple(bounded = false),
)
.size(40.dp),
painter = rememberAsyncImagePainter(packInfo),
contentDescription = null,
)
Box(contentAlignment = Alignment.Center) {
// add background shape for irregular icons
val c = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.1f)
Box(modifier = Modifier.clip(CircleShape).size(34.dp).background(c))
Image(
modifier = Modifier
.clickable(
onClick = onClick,
interactionSource = remember { MutableInteractionSource() },
indication = rememberRipple(bounded = false),
)
.size(40.dp),
painter = rememberAsyncImagePainter(packInfo),
contentDescription = null,
)
}
} else {
val c = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.25f)
Box(modifier = Modifier
Expand Down
26 changes: 26 additions & 0 deletions app/src/main/kotlin/com/madness/collision/chief/ChiefContext.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Copyright 2023 Clifford Liu
*
* 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.madness.collision.chief

import android.content.Context
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import com.madness.collision.main.MainApplication

val chiefContext: Context get() = MainApplication.INSTANCE
val chiefAppInfo: ApplicationInfo get() = chiefContext.applicationInfo
val chiefPkgMan: PackageManager get() = chiefContext.packageManager
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
* Copyright 2023 Clifford Liu
*
* 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.madness.collision.chief.graphics

import android.annotation.SuppressLint
import android.content.res.Resources
import android.graphics.Path
import android.graphics.Rect
import android.graphics.Region
import android.graphics.RegionIterator
import android.graphics.drawable.AdaptiveIconDrawable
import android.graphics.drawable.Drawable
import android.os.Build
import android.util.Log
import androidx.annotation.RequiresApi
import androidx.compose.ui.graphics.asAndroidPath
import androidx.compose.ui.graphics.vector.PathParser
import com.madness.collision.chief.chiefContext
import com.madness.collision.util.os.OsUtils

object AdaptiveIcon {
fun hasRectIconMask(drawable: Drawable): Boolean {
val systemRect = when {
OsUtils.satisfy(OsUtils.O) -> getSystemIconMask()?.let(::isFullRect)
else -> null
}
val isRect = when {
OsUtils.satisfy(OsUtils.O) && drawable is AdaptiveIconDrawable ->
drawable.iconMask.let(::isFullRect)
else -> null
}
Log.d("AdaptiveIcon", "hasRectIconMask/sysRect:$systemRect/hasRect:$isRect")
return isRect == true
}

@SuppressLint("PrivateApi", "DiscouragedApi")
@RequiresApi(Build.VERSION_CODES.O)
private fun getSystemIconMask(): Path? {
val maskId = Resources.getSystem()
.getIdentifier("config_icon_mask", "string", "android")
if (maskId <= 0) return null
try {
val pathData = chiefContext.getString(maskId)
Log.d("AdaptiveIcon", "getIconMask/config_icon_mask[$pathData]")
return PathParser().parsePathString(pathData).toPath().asAndroidPath()
} catch (e: Resources.NotFoundException) {
e.printStackTrace()
} catch (e: UnsupportedOperationException) {
e.printStackTrace()
}
return null
}

/** Whether the [path] is a rectangle covering 100% area */
private fun isFullRect(path: Path): Boolean {
if (path.isEmpty || !path.isRect(null)) return false
val clip = Region(0, 0, 100, 100)
val region = Region().apply { setPath(path, clip) }
val iterator = RegionIterator(region)
val rect = Rect()
var area = 0
while (iterator.next(rect)) {
area += rect.width() * rect.height()
}
return area == 10000
}
}
13 changes: 11 additions & 2 deletions app/src/main/kotlin/com/madness/collision/settings/AboutPage.kt
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,15 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.core.content.ContextCompat
import coil.compose.AsyncImage
import com.madness.collision.BuildConfig
import com.madness.collision.R
import com.madness.collision.chief.graphics.AdaptiveIcon
import com.madness.collision.main.MainViewModel
import com.madness.collision.util.ThemeUtil
import com.madness.collision.util.ui.autoMirrored
import racra.compose.smooth_corner_rect_library.AbsoluteSmoothCornerShape

class AboutOption(
val icon: AboutOptionIcon,
Expand Down Expand Up @@ -124,10 +127,16 @@ private fun BuildDetails() {
modifier = Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
val context = LocalContext.current
val (appIcon, isRectIcon) = remember {
val drawable = ContextCompat.getDrawable(context, R.mipmap.ic_launcher)
drawable to (drawable?.let(AdaptiveIcon::hasRectIconMask) == true)
}
AsyncImage(
model = R.mipmap.ic_launcher,
model = appIcon,
contentDescription = null,
modifier = Modifier.size(100.dp),
modifier = Modifier.size(100.dp)
.let { if (isRectIcon) it.clip(AbsoluteSmoothCornerShape(12.dp, 80)) else it },
)
Spacer(modifier = Modifier.height(10.dp))
val verText = "${BuildConfig.VERSION_NAME}/${BuildConfig.VERSION_CODE}"
Expand Down
7 changes: 4 additions & 3 deletions app/src/main/kotlin/com/madness/collision/util/ui/UiUtils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@
package com.madness.collision.util.ui

import android.content.Context
import com.madness.collision.main.MainApplication
import com.madness.collision.chief.chiefContext
import com.madness.collision.util.config.LocaleUtils
import java.util.*
import java.util.Locale

val appContext: Context get() = MainApplication.INSTANCE
@Deprecated("", ReplaceWith("chiefContext", "com.madness.collision.chief.chiefContext"))
val appContext: Context get() = chiefContext
val appLocale: Locale get() = LocaleUtils.getRuntimeFirst()
26 changes: 25 additions & 1 deletion app/src/main/res/drawable/anim_splash_logo.xml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,31 @@
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:propertyName="translateY"
android:valueFrom="0"
android:valueTo="180"
android:valueTo="240"
android:valueType="floatType" />
<objectAnimator
android:startOffset="400"
android:duration="400"
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:propertyName="translateX"
android:valueFrom="0"
android:valueTo="24"
android:valueType="floatType" />
<objectAnimator
android:startOffset="400"
android:duration="400"
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:propertyName="scaleX"
android:valueFrom="1"
android:valueTo="0.8"
android:valueType="floatType" />
<objectAnimator
android:startOffset="400"
android:duration="400"
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:propertyName="scaleY"
android:valueFrom="1"
android:valueTo="0.8"
android:valueType="floatType" />
</set>
</aapt:attr>
Expand Down
8 changes: 4 additions & 4 deletions app/src/main/res/drawable/ic_splash_logo.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@
<group android:name="background">
<path
android:name="back0"
android:pathData="M0,0h240v240h-240z"
android:pathData="M 120 40 C 164.16 40 200 75.84 200 120 C 200 164.16 164.16 200 120 200 C 75.84 200 40 164.16 40 120 C 40 75.84 75.84 40 120 40 Z"
android:fillColor="#ffffff"/>
<path
android:name="back1"
android:pathData="M0,0h240v240h-240z"
android:pathData="M 120 40 C 164.16 40 200 75.84 200 120 C 200 164.16 164.16 200 120 200 C 75.84 200 40 164.16 40 120 C 40 75.84 75.84 40 120 40 Z"
android:fillType="evenOdd">
<aapt:attr name="android:fillColor">
<gradient
Expand All @@ -43,7 +43,7 @@
</path>
<path
android:name="back2"
android:pathData="M0,0h240v240h-240z"
android:pathData="M 120 40 C 164.16 40 200 75.84 200 120 C 200 164.16 164.16 200 120 200 C 75.84 200 40 164.16 40 120 C 40 75.84 75.84 40 120 40 Z"
android:fillType="evenOdd">
<aapt:attr name="android:fillColor">
<gradient
Expand All @@ -59,7 +59,7 @@
</path>
<path
android:name="back3"
android:pathData="M0,0h240v240h-240z"
android:pathData="M 120 40 C 164.16 40 200 75.84 200 120 C 200 164.16 164.16 200 120 200 C 75.84 200 40 164.16 40 120 C 40 75.84 75.84 40 120 40 Z"
android:fillType="evenOdd">
<aapt:attr name="android:fillColor">
<gradient
Expand Down

0 comments on commit 7b7d521

Please sign in to comment.