Skip to content

Commit

Permalink
Merge pull request #261 from humhub/79-v2-support-for-multiple-instances
Browse files Browse the repository at this point in the history
79 v2 support for multiple instances
  • Loading branch information
PrimozRatej authored Dec 19, 2024
2 parents 340dd7a + fcacf97 commit da465de
Show file tree
Hide file tree
Showing 22 changed files with 990 additions and 479 deletions.
1 change: 1 addition & 0 deletions android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.VIDEO_CAPTURE" />
<uses-permission android:name="android.permission.AUDIO_CAPTURE" />

<uses-permission
android:name="android.permission.USE_FULL_SCREEN_INTENT"
tools:node="remove" />
Expand Down
1 change: 1 addition & 0 deletions lib/app_opener.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:humhub/util/const.dart';
import 'package:humhub/util/intent/intent_plugin.dart';
Expand Down
8 changes: 4 additions & 4 deletions lib/flavored/models/humhub.f.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ class HumHubF extends HumHub {
String get manifestUrl => dotenv.env['MANIFEST_URL']!;

HumHubF({
bool isHideOpener = false,
OpenerState openerState = OpenerState.shown,
String? randomHash,
String? appVersion,
String? pushToken,
}) : super(
isHideOpener: isHideOpener,
openerState: openerState,
randomHash: HumHub.generateHash(32),
appVersion: appVersion,
pushToken: pushToken);
Expand All @@ -25,9 +25,9 @@ class HumHubF extends HumHub {
'x-humhub-app-token': randomHash!,
'x-humhub-app': appVersion ?? '1.0.0',
'x-humhub-app-bundle-id': GlobalPackageInfo.info.packageName,
'x-humhub-app-ostate': isHideOpener ? '1' : '0',
'x-humhub-app-is-ios': isIos ? '1' : '0',
'x-humhub-app-is-android': isAndroid ? '1' : '0'
'x-humhub-app-is-android': isAndroid ? '1' : '0',
'x-humhub-app-ostate': openerState.headerValue
};

static Future<HumHubF> initialize() async {
Expand Down
6 changes: 4 additions & 2 deletions lib/flavored/models/manifest.f.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,16 @@ class ManifestF extends Manifest {
required String shortName,
required String name,
required String backgroundColor,
required String themeColor})
required String themeColor,
List<ManifestIcon>? icons})
: super(
display: display,
startUrl: startUrl,
shortName: shortName,
name: name,
backgroundColor: backgroundColor,
themeColor: themeColor);
themeColor: themeColor,
icons: icons);

factory ManifestF.fromEnv() {
return ManifestF(
Expand Down
8 changes: 7 additions & 1 deletion lib/l10n/app_de.arb
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@

"enable_permissions": "Berechtigungen aktivieren.",

"add_network": "Netzwerk hinzufügen",

"downloading": "Herunterladen",
"settings": "Einstellungen",
"connect": "Verbinden",
Expand All @@ -46,5 +48,9 @@
"skip": "Überspringen",
"ok": "OK",
"no": "Nein",
"yes": "Ja"
"yes": "Ja",

"error_404": "Ihre HumHub-Installation existiert nicht.",
"error_no_connection": "Bitte überprüfen Sie Ihre Internetverbindung.",
"error_url_empty": "Geben Sie den Standort Ihrer HumHub-Installation an."
}
8 changes: 7 additions & 1 deletion lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@

"enable_permissions": "Enable permissions.",

"add_network": "Add network",

"downloading": "Downloading",
"settings": "Settings",
"connect": "Connect",
Expand All @@ -47,5 +49,9 @@
"skip": "Skip",
"ok": "ok",
"no": "No",
"yes": "Yes"
"yes": "Yes",

"error_404": "Your HumHub installation does not exist",
"error_no_connection": "Please check your internet connection.",
"error_url_empty": "Specify you HumHub location."
}
8 changes: 7 additions & 1 deletion lib/l10n/app_fr.arb
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@

"enable_permissions": "Activer les autorisations.",

"add_network": "Ajouter un réseau",

"downloading": "Téléchargement",
"settings": "Paramètres",
"connect": "Connecter",
Expand All @@ -46,5 +48,9 @@
"skip": "Passer",
"ok": "OK",
"no": "Non",
"yes": "Oui"
"yes": "Oui",

"error_404": "Votre installation HumHub n'existe pas.",
"error_no_connection": "Veuillez vérifier votre connexion Internet.",
"error_url_empty": "Spécifiez l'emplacement de votre installation HumHub."
}
117 changes: 104 additions & 13 deletions lib/models/hum_hub.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,46 +15,129 @@ import 'package:permission_handler/permission_handler.dart';

enum RedirectAction { opener, webView }

enum OpenerState {
shown(true),
hidden(false);

final bool value;

const OpenerState(this.value);

String get headerValue => value ? '1' : '0';

@override
String toString() {
return value ? "shown" : "hidden";
}
}

class HumHub {
Manifest? manifest;
String? manifestUrl;
bool isHideOpener;
OpenerState openerState;
String? randomHash;
String? appVersion;
String? pushToken;
final bool isIos = Platform.isIOS || Platform.isMacOS;
final bool isAndroid = Platform.isAndroid;
List<Manifest> history;

HumHub(
{this.manifest, this.manifestUrl, this.isHideOpener = false, this.randomHash, this.appVersion, this.pushToken});
HumHub({
this.manifest,
this.manifestUrl,
this.openerState = OpenerState.shown,
this.randomHash,
this.appVersion,
this.pushToken,
List<Manifest>? history,
}) : history = history ?? [];

Map<String, dynamic> toJson() => {
'manifest': manifest?.toJson(),
'manifestUri': manifestUrl,
'isHideDialog': isHideOpener,
'openerState': openerState.value,
'randomHash': randomHash,
'appVersion': appVersion,
'pushToken': pushToken,
'history': history
.map((manifest) => manifest.toJson())
.toList(),
};

factory HumHub.fromJson(Map<String, dynamic> json) {
return HumHub(
manifest: json['manifest'] != null ? Manifest.fromJson(json['manifest']) : null,
manifest:
json['manifest'] != null ? Manifest.fromJson(json['manifest']) : null,
manifestUrl: json['manifestUri'],
isHideOpener: json['isHideDialog'] as bool,
openerState: (json['openerState'] as bool?) ?? true ? OpenerState.shown : OpenerState.hidden,
randomHash: json['randomHash'],
appVersion: json['appVersion'],
pushToken: json['pushToken'],
history: json['history'] != null
? List<Manifest>.from(
json['history'].map((json) => Manifest.fromJson(json)))
: [], // Deserialize history
);
}

/// Adds a new [Manifest] to the history.
///
/// This method checks if a [Manifest] with the same [startUrl] as the
/// provided [newManifest] already exists in the history. If it does,
/// the existing manifest will be updated with the new one. If not,
/// the new manifest will be added to the history list.
///
/// [newManifest] The [Manifest] object to be added to the history.
/// If a manifest with the same [startUrl] exists, it will be updated.
///
/// Note: The [Manifest] class should have a valid `startUrl`
/// property for this method to work correctly. The history list
/// will maintain unique entries based on the `startUrl`.
/// !!! This method should only be called inside a [HumHubNotifier] because it also needs to update secure storage.
void addOrUpdateHistory(Manifest newManifest) {
final existingManifestIndex =
history.indexWhere((item) => item.startUrl == newManifest.startUrl);

if (existingManifestIndex >= 0) {
history[existingManifestIndex] = newManifest;
} else {
history.add(newManifest);
}
}

/// Removes a [Manifest] from the history based on its [startUrl].
///
/// This method searches for a [Manifest] with the specified [startUrl]
/// in the history. If found, it removes the manifest from the list.
///
/// [startUrl] The start URL of the [Manifest] to be removed from the history.
///
/// Returns true if the manifest was successfully removed,
/// or false if no matching manifest was found.
/// Note: The history will not maintain any references to the
/// removed manifest after this operation.
/// !!! This method should only be called inside a [HumHubNotifier] because it also needs to update secure storage.
bool removeFromHistory(Manifest manifest) {
final existingManifestIndex =
history.indexWhere((item) => item == manifest);

if (existingManifestIndex >= 0) {
history.removeAt(existingManifestIndex);
return true;
} else {
return false;
}
}

Future<RedirectAction> action(ref) async {
if (!isHideOpener) {
if (openerState.value) {
return RedirectAction.opener;
} else {
if (manifest != null) {
UniversalOpenerController openerController = UniversalOpenerController(url: manifest!.baseUrl);
String? manifestUrl = await openerController.findManifest(manifest!.baseUrl);
UniversalOpenerController openerController =
UniversalOpenerController(url: manifest!.baseUrl);
String? manifestUrl =
await openerController.findManifest(manifest!.baseUrl);
if (manifestUrl == null) {
return RedirectAction.opener;
} else {
Expand All @@ -68,15 +151,17 @@ class HumHub {
static String generateHash(int length) {
final random = Random.secure();
const characters = '0123456789abcdef';
return List.generate(length, (_) => characters[random.nextInt(characters.length)]).join();
return List.generate(
length, (_) => characters[random.nextInt(characters.length)]).join();
}

Map<String, String> get customHeaders => {
'x-humhub-app-token': randomHash!,
'x-humhub-app': appVersion ?? '1.0.0',
'x-humhub-app-ostate': isHideOpener ? '1' : '0',
'x-humhub-app-is-ios': isIos ? '1' : '0',
'x-humhub-app-is-android': isAndroid ? '1' : '0'
'x-humhub-app-is-android': isAndroid ? '1' : '0',
'x-humhub-app-opener-state': openerState.headerValue,
'x-humhub-app-is-multi-instance': '1',
};

static Future<Widget> init() async {
Expand All @@ -87,7 +172,13 @@ class HumHub {
await SecureStorageService.clearSecureStorageOnReinstall();
await GlobalPackageInfo.init();
await PermissionHandler.requestPermissions(
[Permission.notification, Permission.camera, Permission.microphone, Permission.storage, Permission.photos],
[
Permission.notification,
Permission.camera,
Permission.microphone,
Permission.storage,
Permission.photos
],
);
switch (GlobalPackageInfo.info.packageName) {
case 'com.humhub.app':
Expand Down
64 changes: 47 additions & 17 deletions lib/models/manifest.dart
Original file line number Diff line number Diff line change
@@ -1,50 +1,80 @@
import 'package:dio/dio.dart';

class ManifestIcon {
final String src;
final String type;
final String sizes;

ManifestIcon({required this.src, required this.type, required this.sizes});

factory ManifestIcon.fromJson(Map<String, dynamic> json) {
return ManifestIcon(
src: json['src'] as String,
type: json['type'] as String,
sizes: json['sizes'] as String,
);
}

Map<String, dynamic> toJson() => {
'src': src,
'type': type,
'sizes': sizes,
};
}

class Manifest {
final String display;
final String startUrl;
final String shortName;
final String name;
final String backgroundColor;
final String themeColor;
final List<ManifestIcon>? icons;

Manifest(
{required this.display,
required this.startUrl,
required this.shortName,
required this.name,
required this.backgroundColor,
required this.themeColor});
Manifest({
required this.display,
required this.startUrl,
required this.shortName,
required this.name,
required this.backgroundColor,
required this.themeColor,
required this.icons,
});

String get baseUrl {
Uri url = Uri.parse(startUrl);
return url.origin;
}

factory Manifest.fromJson(Map<String, dynamic> json) {
var iconsJson = json['icons'] as List<dynamic>?;
List<ManifestIcon>? iconsList = iconsJson?.map((icon) => ManifestIcon.fromJson(icon as Map<String, dynamic>)).toList();

return Manifest(
display: json['display'] as String,
startUrl: json['start_url'] as String,
shortName: json['short_name'] as String,
name: json['name'] as String,
backgroundColor: json['background_color'] as String,
themeColor: json['theme_color'] as String,
icons: iconsList,
);
}

Map<String, dynamic> toJson() => {
'display': display,
'start_url': startUrl,
'short_name': shortName,
'name': name,
'background_color': backgroundColor,
'theme_color': themeColor,
};
'display': display,
'start_url': startUrl,
'short_name': shortName,
'name': name,
'background_color': backgroundColor,
'theme_color': themeColor,
'icons': icons?.map((icon) => icon.toJson()).toList(),
};

static Future<Manifest> Function(Dio dio) get(String url) => (dio) async {
Response<dynamic> res = await dio.get(url);
return Manifest.fromJson(res.data);
};
Response<dynamic> res = await dio.get(url);
return Manifest.fromJson(res.data);
};

static String getUriWithoutExtension(String url) {
int lastSlashIndex = url.lastIndexOf('/');
Expand Down
Loading

0 comments on commit da465de

Please sign in to comment.