mirror of
https://github.com/jonasbark/swiftcontrol.git
synced 2026-02-18 00:17:40 +01:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6ae2297246 | ||
|
|
c6fb2e68b5 | ||
|
|
c84c685a8f | ||
|
|
102f4a8818 |
@@ -20,6 +20,7 @@
|
||||
"allowLocationForBluetooth": "Standortzugriff erlauben, damit Bluetooth-Scan funktioniert",
|
||||
"allowPersistentNotification": "Benachrichtigungen zulassen",
|
||||
"allowsRunningInBackground": "Ermöglicht es BikeControl, im Hintergrund weiterzulaufen.",
|
||||
"alreadyBoughtTheApp": "App bereits gekauft? Tut mir leid, Du solltest eigentlich schon die Vollversion haben. \nSchreibe mir bitte eine E-Mail mit Deinem Android-Kaufbeleg, dann kümmere ich mich darum!",
|
||||
"appIdActions": "{appId} Aktionen",
|
||||
"@appIdActions": {
|
||||
"placeholders": {
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
"allowLocationForBluetooth": "Allow Location so Bluetooth scan works",
|
||||
"allowPersistentNotification": "Allow Notifications",
|
||||
"allowsRunningInBackground": "Allows BikeControl to keep running in background",
|
||||
"alreadyBoughtTheApp": "Already bought the app? Sorry about that, you should have the full version already.\nPlease write a mail and attach your Android purchase receipt and I'll get it resolved!",
|
||||
"appIdActions": "{appId} actions",
|
||||
"@appIdActions": {
|
||||
"placeholders": {
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
"allowLocationForBluetooth": "Autoriser la localisation pour que la recherche Bluetooth fonctionne",
|
||||
"allowPersistentNotification": "Autoriser les notifications",
|
||||
"allowsRunningInBackground": "Permet à BikeControl de continuer à fonctionner en arrière-plan",
|
||||
"alreadyBoughtTheApp": "Already bought the app? Sorry about that, you should have the full version already.\nPlease write a mail and attach your Android purchase receipt and I'll get it resolved!",
|
||||
"appIdActions": "{appId} actions",
|
||||
"@appIdActions": {
|
||||
"placeholders": {
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
"allowLocationForBluetooth": "Zezwól na dostęp do lokalizacji, aby umożliwić skanowanie Bluetooth",
|
||||
"allowPersistentNotification": "Zezwól na powiadomienia",
|
||||
"allowsRunningInBackground": "Umożliwia działanie BikeControl w tle",
|
||||
"alreadyBoughtTheApp": "Już kupiłeś aplikację? Przepraszamy, powinieneś już mieć pełną wersję.\nNapisz maila i dołącz dowód zakupu Androida, a ja to rozwiążę!",
|
||||
"appIdActions": "{appId} działania",
|
||||
"@appIdActions": {
|
||||
"placeholders": {
|
||||
|
||||
@@ -215,6 +215,7 @@ class IAPService {
|
||||
// while the app is still paid. Only users who downloaded the paid version will have
|
||||
// a last_seen_version. After changing the app to free, new users won't have this set.
|
||||
final lastSeenVersion = core.settings.getLastSeenVersion();
|
||||
core.connection.signalNotification(LogNotification('Android last seen version: $lastSeenVersion'));
|
||||
if (lastSeenVersion != null && lastSeenVersion.isNotEmpty) {
|
||||
Version lastVersion = Version.parse(lastSeenVersion);
|
||||
// If they had a previous version, they're an existing paid user
|
||||
|
||||
@@ -60,8 +60,18 @@ class WindowsIAPService {
|
||||
}
|
||||
final trial = await _windowsIapPlugin.getTrialStatusAndRemainingDays();
|
||||
core.connection.signalNotification(LogNotification('Trial status: $trial'));
|
||||
trialDaysRemaining = trial.remainingDays;
|
||||
if (trial.isActive && !trial.isTrial && trial.remainingDays <= 0) {
|
||||
final trialEndDate = trial.remainingDays;
|
||||
if (trial.isTrial && trialEndDate.isNotEmpty && !trialEndDate.contains("?")) {
|
||||
try {
|
||||
trialDaysRemaining = DateTime.parse(trialEndDate).difference(DateTime.now()).inDays;
|
||||
} catch (e) {
|
||||
core.connection.signalNotification(LogNotification('Error parsing trial end date: $e'));
|
||||
trialDaysRemaining = 0;
|
||||
}
|
||||
} else {
|
||||
trialDaysRemaining = 0;
|
||||
}
|
||||
if (trial.isActive && !trial.isTrial && trialDaysRemaining <= 0) {
|
||||
IAPManager.instance.isPurchased.value = true;
|
||||
await _prefs.write(key: _purchaseStatusKey, value: "true");
|
||||
} else {
|
||||
|
||||
@@ -6,6 +6,7 @@ import 'package:bike_control/utils/iap/iap_manager.dart';
|
||||
import 'package:bike_control/widgets/ui/small_progress_indicator.dart';
|
||||
import 'package:bike_control/widgets/ui/toast.dart';
|
||||
import 'package:shadcn_flutter/shadcn_flutter.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
/// Widget to display IAP status and allow purchases
|
||||
class IAPStatusWidget extends StatefulWidget {
|
||||
@@ -185,6 +186,29 @@ class _IAPStatusWidgetState extends State<IAPStatusWidget> {
|
||||
padding: const EdgeInsets.only(left: 42.0, top: 8.0),
|
||||
child: Text(AppLocalizations.of(context).fullVersionDescription).xSmall,
|
||||
),
|
||||
if (Platform.isAndroid)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 42.0, top: 8.0),
|
||||
child: Column(
|
||||
spacing: 8,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Divider(),
|
||||
Text(
|
||||
AppLocalizations.of(context).alreadyBoughtTheApp,
|
||||
).small,
|
||||
OutlineButton(
|
||||
child: Text(context.i18n.getSupport),
|
||||
onPressed: () {
|
||||
String email = Uri.encodeComponent('jonas@bikecontrol.app');
|
||||
Uri mail = Uri.parse("mailto:$email?subject=Unlock full version");
|
||||
|
||||
launchUrl(mail);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
name: bike_control
|
||||
description: "BikeControl - Control your virtual riding"
|
||||
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
||||
version: 4.2.2+62
|
||||
version: 4.2.2+63
|
||||
|
||||
environment:
|
||||
sdk: ^3.9.0
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
class Trial {
|
||||
final bool isTrial;
|
||||
final int remainingDays;
|
||||
final String remainingDays;
|
||||
final bool isActive;
|
||||
final bool isTrialOwnedByThisUser;
|
||||
|
||||
|
||||
@@ -112,7 +112,8 @@ class MethodChannelWindowsIap extends WindowsIapPlatform {
|
||||
isActive: result?['isActive'] as bool? ?? false,
|
||||
isTrialOwnedByThisUser:
|
||||
result?['isTrialOwnedByThisUser'] as bool? ?? false,
|
||||
remainingDays: result?['remainingDays'] as int? ?? 0,
|
||||
remainingDays: result?['remainingDays'] as String? ??
|
||||
DateTime.now().add(Duration(days: 7)).toString(),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
#include <winrt/Windows.Foundation.Collections.h>
|
||||
#include <shobjidl.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <iomanip>
|
||||
#include <flutter/event_sink.h>
|
||||
#include <flutter/event_channel.h>
|
||||
#include <flutter/event_stream_handler.h>
|
||||
@@ -267,7 +269,7 @@ namespace windows_iap
|
||||
|
||||
flutter::EncodableMap result;
|
||||
result[flutter::EncodableValue("isTrial")] = flutter::EncodableValue(true);
|
||||
result[flutter::EncodableValue("remainingDays")] = flutter::EncodableValue(0);
|
||||
result[flutter::EncodableValue("remainingDays")] = flutter::EncodableValue("");
|
||||
result[flutter::EncodableValue("isActive")] = flutter::EncodableValue(license.IsActive());
|
||||
result[flutter::EncodableValue("isTrialOwnedByThisUser")] = flutter::EncodableValue(license.IsTrialOwnedByThisUser());
|
||||
|
||||
@@ -282,10 +284,24 @@ namespace windows_iap
|
||||
{
|
||||
result[flutter::EncodableValue("isTrial")] = flutter::EncodableValue(true);
|
||||
|
||||
winrt::Windows::Foundation::TimeSpan expiration = license.TrialTimeRemaining();
|
||||
const auto inDays = std::chrono::duration_cast<std::chrono::hours>(expiration).count() / 24.0;
|
||||
auto expirationDate = license.ExpirationDate();
|
||||
|
||||
// dt is your winrt::Windows::Foundation::DateTime
|
||||
std::time_t t = winrt::clock::to_time_t(expirationDate); // Convert to time_t (UTC seconds since 1970)
|
||||
std::tm tm_buf;
|
||||
localtime_s(&tm_buf, &t); // Safe version
|
||||
|
||||
std::wstringstream wss;
|
||||
wss << std::put_time(&tm_buf, L"%Y-%m-%d %H:%M:%S"); // Custom format
|
||||
|
||||
winrt::hstring readable = winrt::hstring{ wss.str() };
|
||||
std::string utf8 = winrt::to_string(readable); // Converts hstring to UTF-8 std::string
|
||||
|
||||
result[flutter::EncodableValue("remainingDays")] = flutter::EncodableValue(utf8);
|
||||
}
|
||||
else {
|
||||
result[flutter::EncodableValue("isTrial")] = flutter::EncodableValue(false);
|
||||
|
||||
result[flutter::EncodableValue("remainingDays")] = flutter::EncodableValue(inDays);
|
||||
}
|
||||
|
||||
resultCallback->Success(flutter::EncodableValue(result));
|
||||
|
||||
Reference in New Issue
Block a user