Skip to content

Commit

Permalink
enterprise: user/work profile switch from drawer
Browse files Browse the repository at this point in the history
  • Loading branch information
deckerst committed Sep 30, 2024
1 parent c7e5b1a commit 1e58ae9
Show file tree
Hide file tree
Showing 16 changed files with 470 additions and 121 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ All notable changes to this project will be documented in this file.
### Added

- Map: OpenTopoMap layer
- Enterprise: support for work profile switching from the drawer

## <a id="v1.11.13"></a>[v1.11.13] - 2024-09-17

Expand Down
4 changes: 4 additions & 0 deletions android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@
<uses-permission
android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROCESSING"
tools:ignore="SystemPermissionTypo" />
<!-- to switch between regular and work profiles -->
<uses-permission
android:name="android.permission.INTERACT_ACROSS_PROFILES"
tools:ignore="ProtectedPermissions" />
<!-- TODO TLAD still needed to fetch map tiles / reverse geocoding / else ? check in release mode -->
<uses-permission android:name="android.permission.INTERNET" />
<!-- from Android 12 (API 31), users can optionally grant access to the media management special permission -->
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package deckers.thibault.aves

import android.annotation.SuppressLint
import android.app.KeyguardManager
import android.app.SearchManager
import android.appwidget.AppWidgetManager
import android.content.ClipData
Expand All @@ -23,6 +24,7 @@ import deckers.thibault.aves.channel.AvesByteSendingMethodCodec
import deckers.thibault.aves.channel.calls.AccessibilityHandler
import deckers.thibault.aves.channel.calls.AnalysisHandler
import deckers.thibault.aves.channel.calls.AppAdapterHandler
import deckers.thibault.aves.channel.calls.AppProfileHandler
import deckers.thibault.aves.channel.calls.Coresult.Companion.safe
import deckers.thibault.aves.channel.calls.DebugHandler
import deckers.thibault.aves.channel.calls.DeviceHandler
Expand Down Expand Up @@ -142,6 +144,7 @@ open class MainActivity : FlutterFragmentActivity() {
MethodChannel(messenger, MetadataEditHandler.CHANNEL).setMethodCallHandler(MetadataEditHandler(this))
MethodChannel(messenger, WallpaperHandler.CHANNEL).setMethodCallHandler(WallpaperHandler(this))
// - need Activity
MethodChannel(messenger, AppProfileHandler.CHANNEL).setMethodCallHandler(AppProfileHandler(this))
MethodChannel(messenger, WindowHandler.CHANNEL).setMethodCallHandler(ActivityWindowHandler(this))

// result streaming: dart -> platform ->->-> dart
Expand Down Expand Up @@ -318,7 +321,7 @@ open class MainActivity : FlutterFragmentActivity() {
INTENT_DATA_KEY_URI to uri.toString(),
)

val keyguardManager = getSystemService(Context.KEYGUARD_SERVICE) as android.app.KeyguardManager
val keyguardManager = getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager
val isLocked = keyguardManager.isKeyguardLocked
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
setShowWhenLocked(isLocked)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package deckers.thibault.aves.channel.calls

import android.content.*
import android.content.ClipData
import android.content.ClipboardManager
import android.content.ContentResolver
import android.content.Context
import android.content.Intent
import android.content.pm.ApplicationInfo
import android.content.res.Configuration
import android.content.res.Resources
Expand Down Expand Up @@ -42,7 +46,8 @@ import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.File
import java.util.*
import java.util.Locale
import java.util.UUID
import kotlin.math.roundToInt

class AppAdapterHandler(private val context: Context) : MethodCallHandler {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package deckers.thibault.aves.channel.calls

import android.app.Activity
import android.content.Context
import android.content.pm.CrossProfileApps
import android.os.Build
import deckers.thibault.aves.channel.calls.Coresult.Companion.safe
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.MethodCallHandler

class AppProfileHandler(private val activity: Activity) : MethodCallHandler {
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
when (call.method) {
"canInteractAcrossProfiles" -> safe(call, result, ::canInteractAcrossProfiles)
"canRequestInteractAcrossProfiles" -> safe(call, result, ::canRequestInteractAcrossProfiles)
"requestInteractAcrossProfiles" -> safe(call, result, ::requestInteractAcrossProfiles)
"switchProfile" -> safe(call, result, ::switchProfile)
"getProfileSwitchingLabel" -> safe(call, result, ::getProfileSwitchingLabel)
"getTargetUserProfiles" -> safe(call, result, ::getTargetUserProfiles)
else -> result.notImplemented()
}
}

private fun canInteractAcrossProfiles(@Suppress("unused_parameter") call: MethodCall, result: MethodChannel.Result) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
result.success(false)
return
}

val crossProfileApps = activity.getSystemService(Context.CROSS_PROFILE_APPS_SERVICE) as CrossProfileApps
result.success(crossProfileApps.canInteractAcrossProfiles())
}

private fun canRequestInteractAcrossProfiles(@Suppress("unused_parameter") call: MethodCall, result: MethodChannel.Result) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
result.success(false)
return
}

val crossProfileApps = activity.getSystemService(Context.CROSS_PROFILE_APPS_SERVICE) as CrossProfileApps
result.success(crossProfileApps.canRequestInteractAcrossProfiles())
}

private fun requestInteractAcrossProfiles(@Suppress("unused_parameter") call: MethodCall, result: MethodChannel.Result) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
result.success(false)
return
}

val crossProfileApps = activity.getSystemService(Context.CROSS_PROFILE_APPS_SERVICE) as CrossProfileApps
val intent = crossProfileApps.createRequestInteractAcrossProfilesIntent()
val started = activity.startActivity(intent)

result.success(started)
}

private fun switchProfile(@Suppress("unused_parameter") call: MethodCall, result: MethodChannel.Result) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
result.success(false)
return
}

val crossProfileApps = activity.getSystemService(Context.CROSS_PROFILE_APPS_SERVICE) as CrossProfileApps
val userHandles = crossProfileApps.targetUserProfiles
crossProfileApps.startMainActivity(activity.componentName, userHandles.first())
result.success(null)
}

private fun getProfileSwitchingLabel(@Suppress("unused_parameter") call: MethodCall, result: MethodChannel.Result) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
result.success(null)
return
}

val crossProfileApps = activity.getSystemService(Context.CROSS_PROFILE_APPS_SERVICE) as CrossProfileApps
val userHandles = crossProfileApps.targetUserProfiles
val label = crossProfileApps.getProfileSwitchingLabel(userHandles.first())

result.success(label)
}

private fun getTargetUserProfiles(@Suppress("unused_parameter") call: MethodCall, result: MethodChannel.Result) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
result.success(false)
return
}

val crossProfileApps = activity.getSystemService(Context.CROSS_PROFILE_APPS_SERVICE) as CrossProfileApps
val userProfiles = crossProfileApps.targetUserProfiles.map { it.toString() }.toList()
result.success(userProfiles)
}

companion object {
const val CHANNEL = "deckers.thibault/aves/app_profile"
}
}
89 changes: 89 additions & 0 deletions lib/services/app_profile_service.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import 'dart:async';

import 'package:aves/services/common/services.dart';
import 'package:flutter/services.dart';

abstract class AppProfileService {
Future<bool> canInteractAcrossProfiles();

Future<bool> canRequestInteractAcrossProfiles();

Future<bool> requestInteractAcrossProfiles();

Future<String> getProfileSwitchingLabel();

Future<void> switchProfile();

Future<List<String>> getTargetUserProfiles();
}

class PlatformAppProfileService implements AppProfileService {
static const _platform = MethodChannel('deckers.thibault/aves/app_profile');

@override
Future<bool> canInteractAcrossProfiles() async {
try {
final result = await _platform.invokeMethod('canInteractAcrossProfiles');
if (result != null) return result as bool;
} on PlatformException catch (e, stack) {
await reportService.recordError(e, stack);
}
return false;
}

@override
Future<bool> canRequestInteractAcrossProfiles() async {
try {
final result = await _platform.invokeMethod('canRequestInteractAcrossProfiles');
if (result != null) return result as bool;
} on PlatformException catch (e, stack) {
await reportService.recordError(e, stack);
}
return false;
}

@override
Future<bool> requestInteractAcrossProfiles() async {
try {
final result = await _platform.invokeMethod('requestInteractAcrossProfiles');
if (result != null) return result as bool;
} on PlatformException catch (e, stack) {
await reportService.recordError(e, stack);
}
return false;
}

@override
Future<void> switchProfile() async {
try {
await _platform.invokeMethod('switchProfile');
} on PlatformException catch (e, stack) {
await reportService.recordError(e, stack);
}
return;
}

@override
Future<String> getProfileSwitchingLabel() async {
try {
final result = await _platform.invokeMethod('getProfileSwitchingLabel');
return result as String;
} on PlatformException catch (e, stack) {
await reportService.recordError(e, stack);
}
return '';
}

@override
Future<List<String>> getTargetUserProfiles() async {
try {
final result = await _platform.invokeMethod('getTargetUserProfiles');
if (result != null) {
return (result as List).cast<String>();
}
} on PlatformException catch (e, stack) {
await reportService.recordError(e, stack);
}
return [];
}
}
3 changes: 3 additions & 0 deletions lib/services/common/services.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'package:aves/model/availability.dart';
import 'package:aves/model/db/db.dart';
import 'package:aves/model/db/db_sqflite.dart';
import 'package:aves/model/settings/store_shared_pref.dart';
import 'package:aves/services/app_profile_service.dart';
import 'package:aves/services/app_service.dart';
import 'package:aves/services/device_service.dart';
import 'package:aves/services/media/embedded_data_service.dart';
Expand Down Expand Up @@ -37,6 +38,7 @@ final AvesVideoControllerFactory videoControllerFactory = getIt<AvesVideoControl
final AvesVideoMetadataFetcher videoMetadataFetcher = getIt<AvesVideoMetadataFetcher>();

final AppService appService = getIt<AppService>();
final AppProfileService appProfileService = getIt<AppProfileService>();
final DeviceService deviceService = getIt<DeviceService>();
final EmbeddedDataService embeddedDataService = getIt<EmbeddedDataService>();
final MediaEditService mediaEditService = getIt<MediaEditService>();
Expand All @@ -59,6 +61,7 @@ void initPlatformServices() {
getIt.registerLazySingleton<AvesVideoMetadataFetcher>(FfmpegVideoMetadataFetcher.new);

getIt.registerLazySingleton<AppService>(PlatformAppService.new);
getIt.registerLazySingleton<AppProfileService>(PlatformAppProfileService.new);
getIt.registerLazySingleton<DeviceService>(PlatformDeviceService.new);
getIt.registerLazySingleton<EmbeddedDataService>(PlatformEmbeddedDataService.new);
getIt.registerLazySingleton<MediaEditService>(PlatformMediaEditService.new);
Expand Down
1 change: 0 additions & 1 deletion lib/services/storage_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,6 @@ class PlatformStorageService implements StorageService {
} on PlatformException catch (e, stack) {
await reportService.recordError(e, stack);
}
return;
}

// returns number of deleted directories
Expand Down
20 changes: 10 additions & 10 deletions lib/widgets/debug/app_debug_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,19 @@ import 'package:aves/widgets/common/basic/popup/menu_row.dart';
import 'package:aves/widgets/common/basic/scaffold.dart';
import 'package:aves/widgets/common/behaviour/pop/scope.dart';
import 'package:aves/widgets/common/behaviour/pop/tv_navigation.dart';
import 'package:aves/widgets/debug/android_apps.dart';
import 'package:aves/widgets/debug/android_codecs.dart';
import 'package:aves/widgets/debug/android_dirs.dart';
import 'package:aves/widgets/debug/app_debug_action.dart';
import 'package:aves/widgets/debug/cache.dart';
import 'package:aves/widgets/debug/capabilities.dart';
import 'package:aves/widgets/debug/colors.dart';
import 'package:aves/widgets/debug/database.dart';
import 'package:aves/widgets/debug/device.dart';
import 'package:aves/widgets/debug/general.dart';
import 'package:aves/widgets/debug/media_store_scan_dialog.dart';
import 'package:aves/widgets/debug/os_apps.dart';
import 'package:aves/widgets/debug/os_codecs.dart';
import 'package:aves/widgets/debug/os_paths.dart';
import 'package:aves/widgets/debug/os_storage.dart';
import 'package:aves/widgets/debug/report.dart';
import 'package:aves/widgets/debug/settings.dart';
import 'package:aves/widgets/debug/storage.dart';
import 'package:aves_model/aves_model.dart';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
Expand Down Expand Up @@ -73,16 +73,16 @@ class AppDebugPage extends StatelessWidget {
padding: const EdgeInsets.all(8),
children: const [
DebugGeneralSection(),
DebugAndroidAppSection(),
DebugAndroidCodecSection(),
DebugAndroidDirSection(),
DebugCacheSection(),
DebugCapabilitiesSection(),
DebugColorSection(),
DebugAppDatabaseSection(),
DebugDeviceSection(),
DebugErrorReportingSection(),
DebugSettingsSection(),
DebugStorageSection(),
DebugOSAppSection(),
DebugOSCodecSection(),
DebugOSPathSection(),
DebugOSStorageSection(),
],
),
),
Expand Down
Loading

0 comments on commit 1e58ae9

Please sign in to comment.