diff --git a/android/android_framework/base/src/main/java/github/tornaco/android/thanos/core/app/ActivityManager.java b/android/android_framework/base/src/main/java/github/tornaco/android/thanos/core/app/ActivityManager.java index 6da5a6cec..cf2717f02 100644 --- a/android/android_framework/base/src/main/java/github/tornaco/android/thanos/core/app/ActivityManager.java +++ b/android/android_framework/base/src/main/java/github/tornaco/android/thanos/core/app/ActivityManager.java @@ -5,7 +5,6 @@ import android.content.Intent; import android.content.pm.UserInfo; import android.os.IBinder; -import android.os.RemoteException; import android.os.ServiceManager; import android.os.UserHandle; @@ -16,6 +15,7 @@ import java.util.stream.Collectors; import github.tornaco.android.thanos.core.app.start.StartRecord; +import github.tornaco.android.thanos.core.app.usage.PkgCpuUsageStats; import github.tornaco.android.thanos.core.app.usage.ProcessCpuUsageStats; import github.tornaco.android.thanos.core.os.ProcessName; import github.tornaco.android.thanos.core.os.SwapInfo; @@ -789,6 +789,11 @@ public float getTotalCpuPercent(boolean update) { return server.getTotalCpuPercent(update); } + @SneakyThrows + public List getTopNCpuUsagePackages(int n, boolean update) { + return server.getTopNCpuUsagePackages(n, update); + } + @SneakyThrows public SwapInfo getSwapInfo() { return server.getSwapInfo(); diff --git a/android/android_framework/base/src/main/java/github/tornaco/android/thanos/core/app/IActivityManager.aidl b/android/android_framework/base/src/main/java/github/tornaco/android/thanos/core/app/IActivityManager.aidl index 7429d6d13..c9ac687d8 100644 --- a/android/android_framework/base/src/main/java/github/tornaco/android/thanos/core/app/IActivityManager.aidl +++ b/android/android_framework/base/src/main/java/github/tornaco/android/thanos/core/app/IActivityManager.aidl @@ -233,4 +233,6 @@ interface IActivityManager { void setSmartStandByByPassIfHasVisibleWindowsEnabled(boolean enable); float getTotalCpuPercent(boolean update); + + List getTopNCpuUsagePackages(int n, boolean update); } \ No newline at end of file diff --git a/android/android_framework/base/src/main/java/github/tornaco/android/thanos/core/app/IActivityManager.java b/android/android_framework/base/src/main/java/github/tornaco/android/thanos/core/app/IActivityManager.java index 1195e2293..8e28c9187 100644 --- a/android/android_framework/base/src/main/java/github/tornaco/android/thanos/core/app/IActivityManager.java +++ b/android/android_framework/base/src/main/java/github/tornaco/android/thanos/core/app/IActivityManager.java @@ -514,6 +514,10 @@ public static class Default implements github.tornaco.android.thanos.core.app.IA { return 0.0f; } + @Override public java.util.List getTopNCpuUsagePackages(int n, boolean update) throws android.os.RemoteException + { + return null; + } @Override public android.os.IBinder asBinder() { return null; @@ -2040,6 +2044,18 @@ public static github.tornaco.android.thanos.core.app.IActivityManager asInterfac reply.writeFloat(_result); return true; } + case TRANSACTION_getTopNCpuUsagePackages: + { + data.enforceInterface(descriptor); + int _arg0; + _arg0 = data.readInt(); + boolean _arg1; + _arg1 = (0!=data.readInt()); + java.util.List _result = this.getTopNCpuUsagePackages(_arg0, _arg1); + reply.writeNoException(); + reply.writeTypedList(_result); + return true; + } default: { return super.onTransact(code, data, reply, flags); @@ -5012,6 +5028,28 @@ public java.lang.String getInterfaceDescriptor() } return _result; } + @Override public java.util.List getTopNCpuUsagePackages(int n, boolean update) throws android.os.RemoteException + { + android.os.Parcel _data = android.os.Parcel.obtain(); + android.os.Parcel _reply = android.os.Parcel.obtain(); + java.util.List _result; + try { + _data.writeInterfaceToken(DESCRIPTOR); + _data.writeInt(n); + _data.writeInt(((update)?(1):(0))); + boolean _status = mRemote.transact(Stub.TRANSACTION_getTopNCpuUsagePackages, _data, _reply, 0); + if (!_status && getDefaultImpl() != null) { + return getDefaultImpl().getTopNCpuUsagePackages(n, update); + } + _reply.readException(); + _result = _reply.createTypedArrayList(github.tornaco.android.thanos.core.app.usage.PkgCpuUsageStats.CREATOR); + } + finally { + _reply.recycle(); + _data.recycle(); + } + return _result; + } public static github.tornaco.android.thanos.core.app.IActivityManager sDefaultImpl; } static final int TRANSACTION_getCurrentFrontApp = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); @@ -5145,6 +5183,7 @@ public java.lang.String getInterfaceDescriptor() static final int TRANSACTION_isSmartStandByByPassIfHasVisibleWindows = (android.os.IBinder.FIRST_CALL_TRANSACTION + 128); static final int TRANSACTION_setSmartStandByByPassIfHasVisibleWindowsEnabled = (android.os.IBinder.FIRST_CALL_TRANSACTION + 129); static final int TRANSACTION_getTotalCpuPercent = (android.os.IBinder.FIRST_CALL_TRANSACTION + 130); + static final int TRANSACTION_getTopNCpuUsagePackages = (android.os.IBinder.FIRST_CALL_TRANSACTION + 131); public static boolean setDefaultImpl(github.tornaco.android.thanos.core.app.IActivityManager impl) { // Only one user of this interface can use this function // at a time. This is a heuristic to detect if two different @@ -5325,4 +5364,5 @@ public static github.tornaco.android.thanos.core.app.IActivityManager getDefault public boolean isSmartStandByByPassIfHasVisibleWindows() throws android.os.RemoteException; public void setSmartStandByByPassIfHasVisibleWindowsEnabled(boolean enable) throws android.os.RemoteException; public float getTotalCpuPercent(boolean update) throws android.os.RemoteException; + public java.util.List getTopNCpuUsagePackages(int n, boolean update) throws android.os.RemoteException; } diff --git a/android/android_framework/base/src/main/java/github/tornaco/android/thanos/core/app/usage/PkgCpuUsageStats.java b/android/android_framework/base/src/main/java/github/tornaco/android/thanos/core/app/usage/PkgCpuUsageStats.java new file mode 100644 index 000000000..4cdc46582 --- /dev/null +++ b/android/android_framework/base/src/main/java/github/tornaco/android/thanos/core/app/usage/PkgCpuUsageStats.java @@ -0,0 +1,61 @@ +/* + * (C) Copyright 2022 Thanox + * + * 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 github.tornaco.android.thanos.core.app.usage; + +import android.os.Parcel; +import android.os.Parcelable; + +import github.tornaco.android.thanos.core.pm.Pkg; + +public class PkgCpuUsageStats implements Parcelable { + public Pkg pkg; + public String cpuRatioString; + + protected PkgCpuUsageStats(Parcel in) { + pkg = in.readParcelable(Pkg.class.getClassLoader()); + cpuRatioString = in.readString(); + } + + public PkgCpuUsageStats(Pkg pkg, String cpuRatioString) { + this.pkg = pkg; + this.cpuRatioString = cpuRatioString; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeParcelable(pkg, flags); + dest.writeString(cpuRatioString); + } + + @Override + public int describeContents() { + return 0; + } + + public static final Creator CREATOR = new Creator() { + @Override + public PkgCpuUsageStats createFromParcel(Parcel in) { + return new PkgCpuUsageStats(in); + } + + @Override + public PkgCpuUsageStats[] newArray(int size) { + return new PkgCpuUsageStats[size]; + } + }; +} diff --git a/android/android_sdk/thanos.aidl b/android/android_sdk/thanos.aidl index 1c40a2852..8410fa7f9 100644 --- a/android/android_sdk/thanos.aidl +++ b/android/android_sdk/thanos.aidl @@ -3,6 +3,7 @@ parcelable github.tornaco.android.thanos.core.app.ActivityAssistInfo; parcelable github.tornaco.android.thanos.core.app.event.ThanosEvent; parcelable github.tornaco.android.thanos.core.app.start.StartRecord; parcelable github.tornaco.android.thanos.core.app.usage.ProcessCpuUsageStats; +parcelable github.tornaco.android.thanos.core.app.usage.PkgCpuUsageStats; parcelable github.tornaco.android.thanos.core.pm.ParceledListSlice; parcelable github.tornaco.android.thanos.core.process.ProcessRecord; parcelable github.tornaco.android.thanos.core.pm.AppInfo; diff --git a/android/app/src/main/java/github/tornaco/android/thanos/dashboard/StatusHeaderInfo.kt b/android/app/src/main/java/github/tornaco/android/thanos/dashboard/StatusHeaderInfo.kt index 29b9c394e..243d9a592 100644 --- a/android/app/src/main/java/github/tornaco/android/thanos/dashboard/StatusHeaderInfo.kt +++ b/android/app/src/main/java/github/tornaco/android/thanos/dashboard/StatusHeaderInfo.kt @@ -17,6 +17,9 @@ package github.tornaco.android.thanos.dashboard +import github.tornaco.android.thanos.core.app.usage.PkgCpuUsageStats +import github.tornaco.android.thanos.core.pm.AppInfo + data class StatusHeaderInfo( val runningAppsCount: Int, val memory: MemUsage, @@ -37,6 +40,12 @@ data class MemUsage( data class CpuUsage( // 0-100 val totalPercent: Int = 1, + val topNPkgs: List = emptyList() +) + +data class AppCpuUsage( + val appInfo: AppInfo, + val percent: String, ) enum class MemType { diff --git a/android/app/src/main/java/github/tornaco/android/thanos/main/NavHeaderContent.kt b/android/app/src/main/java/github/tornaco/android/thanos/main/NavHeaderContent.kt index bc1cbbeb2..a53478870 100644 --- a/android/app/src/main/java/github/tornaco/android/thanos/main/NavHeaderContent.kt +++ b/android/app/src/main/java/github/tornaco/android/thanos/main/NavHeaderContent.kt @@ -39,6 +39,7 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.times import github.tornaco.android.thanos.R +import github.tornaco.android.thanos.dashboard.AppCpuUsage import github.tornaco.android.thanos.dashboard.MemType import github.tornaco.android.thanos.dashboard.MemUsage import github.tornaco.android.thanos.dashboard.StatusHeaderInfo @@ -158,9 +159,30 @@ private fun CpuProgressBar( } ) } + MediumSpacer() + Column(verticalArrangement = Arrangement.Top, + horizontalAlignment = Alignment.CenterHorizontally) { + headerInfo.cpu.topNPkgs.forEach { + AppCpuUsage(it) + } + } } } +@Composable +private fun AppCpuUsage(usage: AppCpuUsage) { + val onSurfaceColor = getColorAttribute(R.attr.colorOnSurface) + Row(horizontalArrangement = Arrangement.Start, verticalAlignment = Alignment.CenterVertically) { + AppIcon(modifier = Modifier.size(16.dp), usage.appInfo) + Text( + modifier = Modifier, + textAlign = TextAlign.Center, + text = "${usage.percent}%", + style = MaterialTheme.typography.bodySmall.copy(fontSize = 10.sp), + color = Color(onSurfaceColor) + ) + } +} @Composable private fun MemProgressBar( diff --git a/android/app/src/main/java/github/tornaco/android/thanos/main/NavViewModel2.kt b/android/app/src/main/java/github/tornaco/android/thanos/main/NavViewModel2.kt index 65cd1ef0d..f1f7a6133 100644 --- a/android/app/src/main/java/github/tornaco/android/thanos/main/NavViewModel2.kt +++ b/android/app/src/main/java/github/tornaco/android/thanos/main/NavViewModel2.kt @@ -115,13 +115,20 @@ class NavViewModel2 @Inject constructor(@ApplicationContext private val context: } fun loadHeaderStatus(showLoading: Boolean = true) { - if (thanox.isServiceInstalled) { - viewModelScope.launch { - if (showLoading) { - _state.value = _state.value.copy(isLoading = true) + withSubscriptionStatus(context) { isSubscribed: Boolean? -> + isSubscribed?.let { + if (it) { + if (thanox.isServiceInstalled) { + viewModelScope.launch { + if (showLoading) { + _state.value = _state.value.copy(isLoading = true) + } + val state: StatusHeaderInfo = getStatusHeaderInfo() + _state.value = + _state.value.copy(statusHeaderInfo = state, isLoading = false) + } + } } - val state: StatusHeaderInfo = getStatusHeaderInfo() - _state.value = _state.value.copy(statusHeaderInfo = state, isLoading = false) } } } @@ -129,10 +136,9 @@ class NavViewModel2 @Inject constructor(@ApplicationContext private val context: fun loadAppStatus() { viewModelScope.launch { val isPrivacyStatementAccepted = isPrivacyStatementAccepted() - _state.value = _state.value.copy( - showPrivacyStatement = !isPrivacyStatementAccepted, - showFirstRunTips = isPrivacyStatementAccepted - && AppPreference.isFirstRun(context), + _state.value = _state.value.copy(showPrivacyStatement = !isPrivacyStatementAccepted, + showFirstRunTips = isPrivacyStatementAccepted && !thanox.isServiceInstalled && AppPreference.isFirstRun( + context), patchSources = if (thanox.isServiceInstalled) thanox.patchingSource else emptyList()) } } @@ -149,56 +155,51 @@ class NavViewModel2 @Inject constructor(@ApplicationContext private val context: var swapAvailableSizeString = "" var swapUsedPercent = 0 var swapEnabled = false - var runningAppsCount = 0 - - // Only load for pro. - thanox.ifServiceInstalled { manager -> - runningAppsCount = manager.activityManager.runningAppsCount - val memoryInfo = manager.activityManager.memoryInfo - if (memoryInfo != null) { - memTotalSizeString = Formatter.formatFileSize(context, memoryInfo.totalMem) - memUsageSizeString = Formatter.formatFileSize(context, - memoryInfo.totalMem - memoryInfo.availMem) - memAvailableSizeString = - Formatter.formatFileSize(context, memoryInfo.availMem) - memUsedPercent = - (100 * ((memoryInfo.totalMem - memoryInfo.availMem).toFloat() / memoryInfo.totalMem.toFloat() + + val runningAppsCount: Int = thanox.activityManager.runningAppsCount + val memoryInfo = thanox.activityManager.memoryInfo + if (memoryInfo != null) { + memTotalSizeString = Formatter.formatFileSize(context, memoryInfo.totalMem) + memUsageSizeString = + Formatter.formatFileSize(context, memoryInfo.totalMem - memoryInfo.availMem) + memAvailableSizeString = Formatter.formatFileSize(context, memoryInfo.availMem) + memUsedPercent = + (100 * ((memoryInfo.totalMem - memoryInfo.availMem).toFloat() / memoryInfo.totalMem.toFloat() + .coerceAtLeast(1f))).toInt() + } + val swapInfo = thanox.activityManager.swapInfo + if (swapInfo != null) { + swapEnabled = swapInfo.totalSwap > 0 + if (swapEnabled) { + swapTotalSizeString = Formatter.formatFileSize(context, swapInfo.totalSwap) + swapUsageSizeString = Formatter.formatFileSize(context, + swapInfo.totalSwap - swapInfo.freeSwap) + swapAvailableSizeString = + Formatter.formatFileSize(context, swapInfo.freeSwap) + swapUsedPercent = + (100 * ((swapInfo.totalSwap - swapInfo.freeSwap).toFloat() / swapInfo.totalSwap.toFloat() .coerceAtLeast(1f))).toInt() } - val swapInfo = manager.activityManager.swapInfo - if (swapInfo != null) { - swapEnabled = swapInfo.totalSwap > 0 - if (swapEnabled) { - swapTotalSizeString = - Formatter.formatFileSize(context, swapInfo.totalSwap) - swapUsageSizeString = Formatter.formatFileSize(context, - swapInfo.totalSwap - swapInfo.freeSwap) - swapAvailableSizeString = - Formatter.formatFileSize(context, swapInfo.freeSwap) - swapUsedPercent = - (100 * ((swapInfo.totalSwap - swapInfo.freeSwap).toFloat() / swapInfo.totalSwap.toFloat() - .coerceAtLeast(1f))).toInt() - } - } } val cpuPercent = (thanox.activityManager.getTotalCpuPercent(true)).coerceAtLeast(1f).toInt() - StatusHeaderInfo(runningAppsCount, - MemUsage(MemType.MEMORY, - memTotalSizeString, - memUsedPercent, - memUsageSizeString, - memAvailableSizeString, - true), - MemUsage(MemType.SWAP, - swapTotalSizeString, - swapUsedPercent, - swapUsageSizeString, - swapAvailableSizeString, - swapEnabled), - CpuUsage(cpuPercent)) + StatusHeaderInfo(runningAppsCount = runningAppsCount, + memory = MemUsage(memType = MemType.MEMORY, + memTotalSizeString = memTotalSizeString, + memUsagePercent = memUsedPercent, + memUsageSizeString = memUsageSizeString, + memAvailableSizeString = memAvailableSizeString, + isEnabled = true), + swap = MemUsage(memType = MemType.SWAP, + memTotalSizeString = swapTotalSizeString, + memUsagePercent = swapUsedPercent, + memUsageSizeString = swapUsageSizeString, + memAvailableSizeString = swapAvailableSizeString, + isEnabled = swapEnabled), + cpu = CpuUsage(totalPercent = cpuPercent)) + }.getOrElse { XLog.e("getStatusHeaderInfo error", it) defaultStatusHeaderInfo diff --git a/android/internal/Thanox-Internal b/android/internal/Thanox-Internal index e4a444f82..a45d5d141 160000 --- a/android/internal/Thanox-Internal +++ b/android/internal/Thanox-Internal @@ -1 +1 @@ -Subproject commit e4a444f821616ac2ca39d3abd0a282f057e7c24e +Subproject commit a45d5d141d7e23cdcca0e2fa74335f5123418c04