improved package retrieval
This commit is contained in:
parent
8e44d4a9d9
commit
f133ebf624
17 changed files with 158 additions and 79 deletions
|
@ -40,7 +40,6 @@
|
|||
<queries>
|
||||
<intent>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent>
|
||||
</queries>
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ import com.bumptech.glide.Glide
|
|||
import com.bumptech.glide.load.DecodeFormat
|
||||
import com.bumptech.glide.request.RequestOptions
|
||||
import deckers.thibault.aves.channel.calls.Coresult.Companion.safe
|
||||
import deckers.thibault.aves.model.FieldMap
|
||||
import deckers.thibault.aves.utils.BitmapUtils.getBytes
|
||||
import deckers.thibault.aves.utils.LogUtils
|
||||
import io.flutter.plugin.common.MethodCall
|
||||
|
@ -29,7 +30,7 @@ import kotlin.math.roundToInt
|
|||
class AppAdapterHandler(private val context: Context) : MethodCallHandler {
|
||||
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
|
||||
when (call.method) {
|
||||
"getAppNames" -> GlobalScope.launch(Dispatchers.IO) { safe(call, result, ::getAppNames) }
|
||||
"getPackages" -> GlobalScope.launch(Dispatchers.IO) { safe(call, result, ::getPackages) }
|
||||
"getAppIcon" -> GlobalScope.launch(Dispatchers.IO) { safe(call, result, ::getAppIcon) }
|
||||
"edit" -> {
|
||||
val title = call.argument<String>("title")
|
||||
|
@ -62,30 +63,24 @@ class AppAdapterHandler(private val context: Context) : MethodCallHandler {
|
|||
}
|
||||
}
|
||||
|
||||
private fun getAppNames(@Suppress("UNUSED_PARAMETER") call: MethodCall, result: MethodChannel.Result) {
|
||||
val nameMap = HashMap<String, String>()
|
||||
val intent = Intent(Intent.ACTION_MAIN, null)
|
||||
.addCategory(Intent.CATEGORY_LAUNCHER)
|
||||
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED)
|
||||
private fun getPackages(@Suppress("UNUSED_PARAMETER") call: MethodCall, result: MethodChannel.Result) {
|
||||
val packages = HashMap<String, FieldMap>()
|
||||
|
||||
fun addPackageDetails(intent: Intent) {
|
||||
// apps tend to use their name in English when creating folders
|
||||
// so we get their names in English as well as the current locale
|
||||
val englishConfig = Configuration().apply { setLocale(Locale.ENGLISH) }
|
||||
|
||||
val pm = context.packageManager
|
||||
for (resolveInfo in pm.queryIntentActivities(intent, 0)) {
|
||||
val ai = resolveInfo.activityInfo.applicationInfo
|
||||
val isSystemPackage = ai.flags and ApplicationInfo.FLAG_SYSTEM != 0
|
||||
if (!isSystemPackage) {
|
||||
val packageName = ai.packageName
|
||||
|
||||
val currentLabel = pm.getApplicationLabel(ai).toString()
|
||||
nameMap[currentLabel] = packageName
|
||||
|
||||
val labelRes = ai.labelRes
|
||||
if (labelRes != 0) {
|
||||
val appInfo = resolveInfo.activityInfo.applicationInfo
|
||||
val packageName = appInfo.packageName
|
||||
if (!packages.containsKey(packageName)) {
|
||||
val currentLabel = pm.getApplicationLabel(appInfo).toString()
|
||||
val englishLabel: String? = appInfo.labelRes.takeIf { it != 0 }?.let { labelRes ->
|
||||
var englishLabel: String? = null
|
||||
try {
|
||||
val resources = pm.getResourcesForApplication(ai)
|
||||
val resources = pm.getResourcesForApplication(appInfo)
|
||||
// `updateConfiguration` is deprecated but it seems to be the only way
|
||||
// to query resources from another app with a specific locale.
|
||||
// The following methods do not work:
|
||||
|
@ -93,15 +88,26 @@ class AppAdapterHandler(private val context: Context) : MethodCallHandler {
|
|||
// - getting a package manager from a custom context with `context.createConfigurationContext(config)`
|
||||
@Suppress("DEPRECATION")
|
||||
resources.updateConfiguration(englishConfig, resources.displayMetrics)
|
||||
val englishLabel = resources.getString(labelRes)
|
||||
nameMap[englishLabel] = packageName
|
||||
englishLabel = resources.getString(labelRes)
|
||||
} catch (e: PackageManager.NameNotFoundException) {
|
||||
Log.w(LOG_TAG, "failed to get app label in English for packageName=$packageName", e)
|
||||
}
|
||||
englishLabel
|
||||
}
|
||||
packages[packageName] = hashMapOf(
|
||||
"packageName" to packageName,
|
||||
"categoryLauncher" to intent.hasCategory(Intent.CATEGORY_LAUNCHER),
|
||||
"isSystem" to (appInfo.flags and ApplicationInfo.FLAG_SYSTEM != 0),
|
||||
"currentLabel" to currentLabel,
|
||||
"englishLabel" to englishLabel,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
result.success(nameMap)
|
||||
|
||||
addPackageDetails(Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_LAUNCHER))
|
||||
addPackageDetails(Intent(Intent.ACTION_MAIN))
|
||||
result.success(ArrayList(packages.values))
|
||||
}
|
||||
|
||||
private fun getAppIcon(call: MethodCall, result: MethodChannel.Result) {
|
||||
|
|
|
@ -16,7 +16,7 @@ import deckers.thibault.aves.channel.calls.Coresult.Companion.safe
|
|||
import deckers.thibault.aves.metadata.ExifInterfaceHelper
|
||||
import deckers.thibault.aves.metadata.MediaMetadataRetrieverHelper
|
||||
import deckers.thibault.aves.metadata.Metadata
|
||||
import deckers.thibault.aves.model.provider.FieldMap
|
||||
import deckers.thibault.aves.model.FieldMap
|
||||
import deckers.thibault.aves.utils.LogUtils
|
||||
import deckers.thibault.aves.utils.MimeTypes.isImage
|
||||
import deckers.thibault.aves.utils.MimeTypes.isSupportedByExifInterface
|
||||
|
|
|
@ -11,7 +11,7 @@ import deckers.thibault.aves.channel.calls.fetchers.RegionFetcher
|
|||
import deckers.thibault.aves.channel.calls.fetchers.ThumbnailFetcher
|
||||
import deckers.thibault.aves.channel.calls.fetchers.TiffRegionFetcher
|
||||
import deckers.thibault.aves.model.ExifOrientationOp
|
||||
import deckers.thibault.aves.model.provider.FieldMap
|
||||
import deckers.thibault.aves.model.FieldMap
|
||||
import deckers.thibault.aves.model.provider.ImageProvider.ImageOpCallback
|
||||
import deckers.thibault.aves.model.provider.ImageProviderFactory.getProvider
|
||||
import deckers.thibault.aves.model.provider.MediaStoreImageProvider
|
||||
|
|
|
@ -46,7 +46,7 @@ import deckers.thibault.aves.metadata.MetadataExtractorHelper.isGeoTiff
|
|||
import deckers.thibault.aves.metadata.XMP.getSafeDateMillis
|
||||
import deckers.thibault.aves.metadata.XMP.getSafeLocalizedText
|
||||
import deckers.thibault.aves.metadata.XMP.isPanorama
|
||||
import deckers.thibault.aves.model.provider.FieldMap
|
||||
import deckers.thibault.aves.model.FieldMap
|
||||
import deckers.thibault.aves.model.provider.FileImageProvider
|
||||
import deckers.thibault.aves.model.provider.ImageProvider
|
||||
import deckers.thibault.aves.utils.BitmapUtils
|
||||
|
|
|
@ -6,7 +6,7 @@ import android.os.Handler
|
|||
import android.os.Looper
|
||||
import android.util.Log
|
||||
import deckers.thibault.aves.model.AvesEntry
|
||||
import deckers.thibault.aves.model.provider.FieldMap
|
||||
import deckers.thibault.aves.model.FieldMap
|
||||
import deckers.thibault.aves.model.provider.ImageProvider.ImageOpCallback
|
||||
import deckers.thibault.aves.model.provider.ImageProviderFactory.getProvider
|
||||
import deckers.thibault.aves.utils.LogUtils
|
||||
|
|
|
@ -4,7 +4,7 @@ import android.content.Context
|
|||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.util.Log
|
||||
import deckers.thibault.aves.model.provider.FieldMap
|
||||
import deckers.thibault.aves.model.FieldMap
|
||||
import deckers.thibault.aves.model.provider.MediaStoreImageProvider
|
||||
import deckers.thibault.aves.utils.LogUtils
|
||||
import io.flutter.plugin.common.EventChannel
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package deckers.thibault.aves.model
|
||||
|
||||
import android.net.Uri
|
||||
import deckers.thibault.aves.model.provider.FieldMap
|
||||
import deckers.thibault.aves.model.FieldMap
|
||||
|
||||
class AvesEntry(map: FieldMap) {
|
||||
val uri: Uri = Uri.parse(map["uri"] as String) // content or file URI
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
package deckers.thibault.aves.model
|
||||
|
||||
typealias FieldMap = MutableMap<String, Any?>
|
|
@ -25,7 +25,7 @@ import deckers.thibault.aves.metadata.Metadata.getRotationDegreesForExifCode
|
|||
import deckers.thibault.aves.metadata.MetadataExtractorHelper.getSafeDateMillis
|
||||
import deckers.thibault.aves.metadata.MetadataExtractorHelper.getSafeInt
|
||||
import deckers.thibault.aves.metadata.MetadataExtractorHelper.getSafeLong
|
||||
import deckers.thibault.aves.model.provider.FieldMap
|
||||
import deckers.thibault.aves.model.FieldMap
|
||||
import deckers.thibault.aves.utils.MimeTypes
|
||||
import deckers.thibault.aves.utils.StorageUtils
|
||||
import org.beyka.tiffbitmapfactory.TiffBitmapFactory
|
||||
|
|
|
@ -18,6 +18,7 @@ import deckers.thibault.aves.decoder.MultiTrackImage
|
|||
import deckers.thibault.aves.decoder.TiffImage
|
||||
import deckers.thibault.aves.model.AvesEntry
|
||||
import deckers.thibault.aves.model.ExifOrientationOp
|
||||
import deckers.thibault.aves.model.FieldMap
|
||||
import deckers.thibault.aves.utils.BitmapUtils
|
||||
import deckers.thibault.aves.utils.LogUtils
|
||||
import deckers.thibault.aves.utils.MimeTypes
|
||||
|
@ -348,5 +349,3 @@ abstract class ImageProvider {
|
|||
private val LOG_TAG = LogUtils.createTag(ImageProvider::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
typealias FieldMap = MutableMap<String, Any?>
|
||||
|
|
|
@ -9,6 +9,7 @@ import android.provider.MediaStore
|
|||
import android.util.Log
|
||||
import com.commonsware.cwac.document.DocumentFileCompat
|
||||
import deckers.thibault.aves.model.AvesEntry
|
||||
import deckers.thibault.aves.model.FieldMap
|
||||
import deckers.thibault.aves.model.SourceEntry
|
||||
import deckers.thibault.aves.utils.LogUtils
|
||||
import deckers.thibault.aves.utils.MimeTypes
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import 'dart:typed_data';
|
||||
|
||||
import 'package:aves/model/entry.dart';
|
||||
import 'package:aves/utils/android_file_utils.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
@ -8,12 +9,18 @@ import 'package:flutter/services.dart';
|
|||
class AndroidAppService {
|
||||
static const platform = MethodChannel('deckers.thibault/aves/app');
|
||||
|
||||
static Future<Map> getAppNames() async {
|
||||
static Future<Set<Package>> getPackages() async {
|
||||
try {
|
||||
final result = await platform.invokeMethod('getAppNames');
|
||||
return result as Map;
|
||||
final result = await platform.invokeMethod('getPackages');
|
||||
final packages = (result as List).cast<Map>().map((map) => Package.fromMap(map)).toSet();
|
||||
// additional info for known directories
|
||||
final kakaoTalk = packages.firstWhere((package) => package.packageName == 'com.kakao.talk', orElse: () => null);
|
||||
if (kakaoTalk != null) {
|
||||
kakaoTalk.ownedDirs.add('KakaoTalkDownload');
|
||||
}
|
||||
return packages;
|
||||
} on PlatformException catch (e) {
|
||||
debugPrint('getAppNames failed with code=${e.code}, exception=${e.message}, details=${e.details}}');
|
||||
debugPrint('getPackages failed with code=${e.code}, exception=${e.message}, details=${e.details}}');
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
|
|
@ -9,14 +9,14 @@ class AndroidFileService {
|
|||
static const platform = MethodChannel('deckers.thibault/aves/storage');
|
||||
static final StreamsChannel storageAccessChannel = StreamsChannel('deckers.thibault/aves/storageaccessstream');
|
||||
|
||||
static Future<List<Map>> getStorageVolumes() async {
|
||||
static Future<Set<StorageVolume>> getStorageVolumes() async {
|
||||
try {
|
||||
final result = await platform.invokeMethod('getStorageVolumes');
|
||||
return (result as List).cast<Map>();
|
||||
return (result as List).cast<Map>().map((map) => StorageVolume.fromMap(map)).toSet();
|
||||
} on PlatformException catch (e) {
|
||||
debugPrint('getStorageVolumes failed with code=${e.code}, exception=${e.message}, details=${e.details}}');
|
||||
}
|
||||
return [];
|
||||
return {};
|
||||
}
|
||||
|
||||
static Future<int> getFreeSpace(StorageVolume volume) async {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import 'package:aves/services/android_app_service.dart';
|
||||
import 'package:aves/services/android_file_service.dart';
|
||||
import 'package:aves/utils/change_notifier.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:path/path.dart';
|
||||
|
||||
final AndroidFileUtils androidFileUtils = AndroidFileUtils._private();
|
||||
|
@ -8,14 +9,17 @@ final AndroidFileUtils androidFileUtils = AndroidFileUtils._private();
|
|||
class AndroidFileUtils {
|
||||
String primaryStorage, dcimPath, downloadPath, moviesPath, picturesPath;
|
||||
Set<StorageVolume> storageVolumes = {};
|
||||
Map _installedAppNameMap = {};
|
||||
Set<Package> _packages = {};
|
||||
List<String> _potentialAppDirs = [];
|
||||
|
||||
AChangeNotifier appNameChangeNotifier = AChangeNotifier();
|
||||
|
||||
Iterable<Package> get _launcherPackages => _packages.where((package) => package.categoryLauncher);
|
||||
|
||||
AndroidFileUtils._private();
|
||||
|
||||
Future<void> init() async {
|
||||
storageVolumes = (await AndroidFileService.getStorageVolumes()).map((map) => StorageVolume.fromMap(map)).toSet();
|
||||
storageVolumes = await AndroidFileService.getStorageVolumes();
|
||||
// path_provider getExternalStorageDirectory() gives '/storage/emulated/0/Android/data/deckers.thibault.aves/files'
|
||||
primaryStorage = storageVolumes.firstWhere((volume) => volume.isPrimary).path;
|
||||
dcimPath = join(primaryStorage, 'DCIM');
|
||||
|
@ -25,8 +29,8 @@ class AndroidFileUtils {
|
|||
}
|
||||
|
||||
Future<void> initAppNames() async {
|
||||
_installedAppNameMap = await AndroidAppService.getAppNames()
|
||||
..addAll({'KakaoTalkDownload': 'com.kakao.talk'});
|
||||
_packages = await AndroidAppService.getPackages();
|
||||
_potentialAppDirs = _launcherPackages.expand((package) => package.potentialDirs).toList();
|
||||
appNameChangeNotifier.notifyListeners();
|
||||
}
|
||||
|
||||
|
@ -42,28 +46,67 @@ class AndroidFileUtils {
|
|||
|
||||
bool isOnRemovableStorage(String path) => getStorageVolume(path)?.isRemovable ?? false;
|
||||
|
||||
AlbumType getAlbumType(String albumDirectory) {
|
||||
if (albumDirectory != null) {
|
||||
if (isCameraPath(albumDirectory)) return AlbumType.camera;
|
||||
if (isDownloadPath(albumDirectory)) return AlbumType.download;
|
||||
if (isScreenRecordingsPath(albumDirectory)) return AlbumType.screenRecordings;
|
||||
if (isScreenshotsPath(albumDirectory)) return AlbumType.screenshots;
|
||||
AlbumType getAlbumType(String albumPath) {
|
||||
if (albumPath != null) {
|
||||
if (isCameraPath(albumPath)) return AlbumType.camera;
|
||||
if (isDownloadPath(albumPath)) return AlbumType.download;
|
||||
if (isScreenRecordingsPath(albumPath)) return AlbumType.screenRecordings;
|
||||
if (isScreenshotsPath(albumPath)) return AlbumType.screenshots;
|
||||
|
||||
final parts = albumDirectory.split(separator);
|
||||
if (albumDirectory.startsWith(primaryStorage) && _isInstalledAppName(parts.last)) return AlbumType.app;
|
||||
final dir = albumPath.split(separator).last;
|
||||
if (albumPath.startsWith(primaryStorage) && _potentialAppDirs.contains(dir)) return AlbumType.app;
|
||||
}
|
||||
return AlbumType.regular;
|
||||
}
|
||||
|
||||
bool _isInstalledAppName(String name) => _installedAppNameMap.keys.contains(name);
|
||||
String getAlbumAppPackageName(String albumPath) {
|
||||
if (albumPath == null) return null;
|
||||
final dir = albumPath.split(separator).last;
|
||||
final package = _launcherPackages.firstWhere((package) => package.potentialDirs.contains(dir), orElse: () => null);
|
||||
return package?.packageName;
|
||||
}
|
||||
|
||||
String getAlbumAppPackageName(String albumDirectory) => _installedAppNameMap[albumDirectory.split(separator).last];
|
||||
|
||||
String getAppName(String packageName) => _installedAppNameMap.entries.firstWhere((kv) => kv.value == packageName, orElse: () => null)?.key;
|
||||
String getCurrentAppName(String packageName) {
|
||||
final package = _packages.firstWhere((package) => package.packageName == packageName, orElse: () => null);
|
||||
return package?.currentLabel;
|
||||
}
|
||||
}
|
||||
|
||||
enum AlbumType { regular, app, camera, download, screenRecordings, screenshots }
|
||||
|
||||
class Package {
|
||||
final String packageName, currentLabel, englishLabel;
|
||||
final bool categoryLauncher, isSystem;
|
||||
final Set<String> ownedDirs = {};
|
||||
|
||||
Package({
|
||||
this.packageName,
|
||||
this.currentLabel,
|
||||
this.englishLabel,
|
||||
this.categoryLauncher,
|
||||
this.isSystem,
|
||||
});
|
||||
|
||||
factory Package.fromMap(Map map) {
|
||||
return Package(
|
||||
packageName: map['packageName'],
|
||||
currentLabel: map['currentLabel'],
|
||||
englishLabel: map['englishLabel'],
|
||||
categoryLauncher: map['categoryLauncher'],
|
||||
isSystem: map['isSystem'],
|
||||
);
|
||||
}
|
||||
|
||||
Set<String> get potentialDirs => [
|
||||
currentLabel,
|
||||
englishLabel,
|
||||
...ownedDirs,
|
||||
].where((dir) => dir != null).toSet();
|
||||
|
||||
@override
|
||||
String toString() => '$runtimeType#${shortHash(this)}{packageName=$packageName, categoryLauncher=$categoryLauncher, isSystem=$isSystem, currentLabel=$currentLabel, englishLabel=$englishLabel, ownedDirs=$ownedDirs}';
|
||||
}
|
||||
|
||||
class StorageVolume {
|
||||
final String description, path, state;
|
||||
final bool isEmulated, isPrimary, isRemovable;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import 'package:aves/image_providers/app_icon_image_provider.dart';
|
||||
import 'package:aves/services/android_app_service.dart';
|
||||
import 'package:aves/utils/android_file_utils.dart';
|
||||
import 'package:aves/widgets/common/identity/aves_expansion_tile.dart';
|
||||
import 'package:aves/widgets/viewer/info/common.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
|
@ -11,14 +12,14 @@ class DebugAndroidAppSection extends StatefulWidget {
|
|||
}
|
||||
|
||||
class _DebugAndroidAppSectionState extends State<DebugAndroidAppSection> with AutomaticKeepAliveClientMixin {
|
||||
Future<Map> _loader;
|
||||
Future<Set<Package>> _loader;
|
||||
|
||||
static const iconSize = 20.0;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_loader = AndroidAppService.getAppNames();
|
||||
_loader = AndroidAppService.getPackages();
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -30,17 +31,17 @@ class _DebugAndroidAppSectionState extends State<DebugAndroidAppSection> with Au
|
|||
children: [
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: 8, right: 8, bottom: 8),
|
||||
child: FutureBuilder<Map>(
|
||||
child: FutureBuilder<Set<Package>>(
|
||||
future: _loader,
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.hasError) return Text(snapshot.error.toString());
|
||||
if (snapshot.connectionState != ConnectionState.done) return SizedBox.shrink();
|
||||
final entries = snapshot.data.entries.toList()..sort((kv1, kv2) => compareAsciiUpperCase(kv1.value, kv2.value));
|
||||
final packages = snapshot.data.toList()..sort((a, b) => compareAsciiUpperCase(a.packageName, b.packageName));
|
||||
final enabledTheme = IconTheme.of(context);
|
||||
final disabledTheme = enabledTheme.merge(IconThemeData(opacity: .2));
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: entries.map((kv) {
|
||||
final appName = kv.key.toString();
|
||||
final packageName = kv.value.toString();
|
||||
children: packages.map((package) {
|
||||
return Text.rich(
|
||||
TextSpan(
|
||||
children: [
|
||||
|
@ -48,7 +49,7 @@ class _DebugAndroidAppSectionState extends State<DebugAndroidAppSection> with Au
|
|||
alignment: PlaceholderAlignment.middle,
|
||||
child: Image(
|
||||
image: AppIconImage(
|
||||
packageName: packageName,
|
||||
packageName: package.packageName,
|
||||
size: iconSize,
|
||||
),
|
||||
width: iconSize,
|
||||
|
@ -56,11 +57,31 @@ class _DebugAndroidAppSectionState extends State<DebugAndroidAppSection> with Au
|
|||
),
|
||||
),
|
||||
TextSpan(
|
||||
text: ' $packageName',
|
||||
text: ' ${package.packageName}\n',
|
||||
style: InfoRowGroup.keyStyle,
|
||||
),
|
||||
WidgetSpan(
|
||||
alignment: PlaceholderAlignment.middle,
|
||||
child: IconTheme(
|
||||
data: package.categoryLauncher ? enabledTheme : disabledTheme,
|
||||
child: Icon(
|
||||
Icons.launch_outlined,
|
||||
size: iconSize,
|
||||
),
|
||||
),
|
||||
),
|
||||
WidgetSpan(
|
||||
alignment: PlaceholderAlignment.middle,
|
||||
child: IconTheme(
|
||||
data: package.isSystem ? enabledTheme : disabledTheme,
|
||||
child: Icon(
|
||||
Icons.android,
|
||||
size: iconSize,
|
||||
),
|
||||
),
|
||||
),
|
||||
TextSpan(
|
||||
text: ' $appName',
|
||||
text: ' ${package.potentialDirs.join(', ')}\n',
|
||||
style: InfoRowGroup.baseStyle,
|
||||
),
|
||||
],
|
||||
|
|
|
@ -136,7 +136,7 @@ class _OwnerPropState extends State<OwnerProp> {
|
|||
builder: (context, snapshot) {
|
||||
final packageName = snapshot.data;
|
||||
if (packageName == null) return SizedBox();
|
||||
final appName = androidFileUtils.getAppName(packageName) ?? packageName;
|
||||
final appName = androidFileUtils.getCurrentAppName(packageName) ?? packageName;
|
||||
return Text.rich(
|
||||
TextSpan(
|
||||
children: [
|
||||
|
|
Loading…
Reference in a new issue