diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index 7968d249..05bb8158 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -78,7 +78,6 @@
@@ -162,4 +161,4 @@
android:exported="true"
tools:ignore="ExportedContentProvider" />
-
\ No newline at end of file
+
diff --git a/lang/en.json b/lang/en.json
index 6fa0ad08..6048007f 100644
--- a/lang/en.json
+++ b/lang/en.json
@@ -246,7 +246,7 @@
},
"store_page": {
"faces": "Watchfaces",
- "apps": "Apps / Timeline",
+ "apps": "Apps",
"search_bar": "Search for something..."
}
}
diff --git a/lib/infrastructure/datasources/web_services/rest_client.dart b/lib/infrastructure/datasources/web_services/rest_client.dart
index d2bbdf98..23be997d 100644
--- a/lib/infrastructure/datasources/web_services/rest_client.dart
+++ b/lib/infrastructure/datasources/web_services/rest_client.dart
@@ -28,7 +28,7 @@ class RESTClient {
..addAll(params ?? {}),
);
- HttpClientRequest req = await _client.getUrl(requestUri);
+ HttpClientRequest req = await _client.openUrl(method, requestUri);
if (token != null) {
req.headers.add("Authorization", "Bearer $token");
}
@@ -58,4 +58,4 @@ class RESTClient {
}
return _completer.future;
}
-}
\ No newline at end of file
+}
diff --git a/lib/main.dart b/lib/main.dart
index 80c8b09f..4217f803 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -6,6 +6,7 @@ import 'package:cobble/infrastructure/datasources/preferences.dart';
import 'package:cobble/localization/localization.dart';
import 'package:cobble/localization/localization_delegate.dart';
import 'package:cobble/localization/model/model_generator.model.dart';
+import 'package:cobble/ui/home/tabs/store_tab.dart';
import 'package:cobble/ui/splash/splash_page.dart';
import 'package:cobble/ui/theme/cobble_scheme.dart';
import 'package:cobble/ui/theme/cobble_theme.dart';
@@ -89,6 +90,9 @@ class MyApp extends HookWidget {
child: MaterialApp(
onGenerateTitle: (context) => tr.common.title,
theme: CobbleTheme.appTheme(brightness),
+ routes: {
+ '/appstore': (context) => StoreTab(),
+ },
home: SplashPage(),
// List all of the app's supported locales here
supportedLocales: supportedLocales,
diff --git a/lib/ui/home/tabs/store_tab.dart b/lib/ui/home/tabs/store_tab.dart
index 989d2a47..76daee13 100644
--- a/lib/ui/home/tabs/store_tab.dart
+++ b/lib/ui/home/tabs/store_tab.dart
@@ -2,12 +2,11 @@ import 'dart:async';
import 'dart:collection';
import 'dart:convert';
import 'dart:io';
-import 'package:cobble/domain/api/appstore/appstore.dart';
+import 'package:cobble/domain/api/appstore/locker_sync.dart';
import 'package:cobble/domain/api/auth/auth.dart';
import 'package:cobble/domain/api/auth/oauth_token.dart';
import 'package:cobble/domain/connection/connection_state_provider.dart';
import 'package:cobble/domain/entities/hardware_platform.dart';
-import 'package:cobble/infrastructure/datasources/web_services/appstore.dart';
import 'package:cobble/infrastructure/datasources/web_services/auth.dart';
import 'package:cobble/localization/localization.dart';
import 'package:cobble/ui/common/components/cobble_button.dart';
@@ -25,6 +24,7 @@ import 'package:url_launcher/url_launcher.dart';
import 'package:webview_flutter/webview_flutter.dart';
import 'package:cobble/infrastructure/pigeons/pigeons.g.dart';
import 'package:path/path.dart' as path;
+import 'package:uuid_type/uuid_type.dart';
class _TabConfig {
final String label;
@@ -35,12 +35,16 @@ class _TabConfig {
class StoreTab extends HookWidget implements CobbleScreen {
final _config = [
- _TabConfig(tr.storePage.faces, Uri.parse("https://apps.rebble.io/en_US/watchfaces")),
- _TabConfig(tr.storePage.apps, Uri.parse("https://apps.rebble.io/en_US/watchapps")),
+ _TabConfig(tr.storePage.faces, Uri.parse("https://store-beta.rebble.io/faces")),
+ _TabConfig(tr.storePage.apps, Uri.parse("https://store-beta.rebble.io/apps")),
];
@override
Widget build(BuildContext context) {
+ // Those are args from outside that tell us what application to display
+ final args = ModalRoute.of(context)!.settings.arguments as AppstoreArguments;
+ final appUrl = "https://store-beta.rebble.io/app/";
+
final indexTab = useState(0);
final pageTitle = useState("Loading");
final backButton = useState(false);
@@ -52,18 +56,11 @@ class StoreTab extends HookWidget implements CobbleScreen {
useMemoized(() => Completer());
final searchController = useTextEditingController();
- final _appstore = useProvider(appstoreServiceProvider.future);
+ final locker_sync = useProvider(lockerSyncProvider);
final connectionState = useProvider(connectionStateProvider.state);
final _auth = useProvider(authServiceProvider.future);
-
- void onEachPageLoad() async {
- WebViewController controller = await _controller.future;
- String current = await controller.currentUrl() ?? baseUrl.value.toString();
- bool canGoBack = await controller.canGoBack();
- backButton.value = canGoBack && baseUrl.value != Uri.parse(current);
- }
void handleRequest(String methodName, Map data) async {
WebViewController controller = await _controller.future;
@@ -71,6 +68,13 @@ class StoreTab extends HookWidget implements CobbleScreen {
controller.runJavascript("PebbleBridge.handleRequest(${json.encode(data)})");
}
+ void onEachPageLoad() async {
+ WebViewController controller = await _controller.future;
+ String current = await controller.currentUrl() ?? baseUrl.value.toString();
+ bool canGoBack = await controller.canGoBack();
+ backButton.value = canGoBack && baseUrl.value != Uri.parse(current);
+ }
+
void handleResponse(Map data, int? callback) async {
WebViewController controller = await _controller.future;
Map response = HashMap();
@@ -79,13 +83,12 @@ class StoreTab extends HookWidget implements CobbleScreen {
controller.runJavascript("PebbleBridge.handleResponse(${json.encode(response)})");
}
- void installApp(String uuid, int? callback) async {
- AppstoreService appstore = await _appstore;
+ void installApp(Uuid uuid, int? callback) async {
Map data = HashMap();
data['added_to_locker'] = false;
try {
- await appstore.addToLocker(uuid);
+ await locker_sync.addToLocker(uuid);
data['added_to_locker'] = true;
handleResponse(data, callback);
} on Exception {
@@ -106,7 +109,7 @@ class StoreTab extends HookWidget implements CobbleScreen {
launchURL(data["url"]);
break;
case "loadAppToDeviceAndLocker":
- installApp(data["id"], callback);
+ installApp(Uuid.parse(data["uuid"]), callback);
break;
case "setVisibleApp":
// In the original app, this was used for displaying sharing button on the app view
@@ -139,7 +142,7 @@ class StoreTab extends HookWidget implements CobbleScreen {
AuthService auth = await _auth;
OAuthToken token = auth.token;
if (auth != null) {
- WebViewCookie accessTokenCookie = new WebViewCookie(name: 'access_token', value: token.accessToken, domain: 'apps.rebble.io');
+ WebViewCookie accessTokenCookie = new WebViewCookie(name: 'access_token', value: token.accessToken, domain: 'store-beta.rebble.io');
CookieManager cookieManager = new CookieManager();
cookieManager.setCookie(accessTokenCookie);
}
@@ -164,7 +167,6 @@ class StoreTab extends HookWidget implements CobbleScreen {
baseUrl.value = _config[newValue].url.replace(queryParameters: attrs.value);
_setWebviewUrl(baseUrl.value);
// This would be changed anyway, but it looked ugly when it jumped from the previous title
- pageTitle.value = _config[newValue].label;
}
Future initialSetup() async {
@@ -175,7 +177,14 @@ class StoreTab extends HookWidget implements CobbleScreen {
useEffect(() {
initialSetup()
- .whenComplete(() => { _setIndexTab(indexTab.value) });
+ .whenComplete(() {
+ if (args == null) {
+ _setIndexTab(indexTab.value);
+ } else {
+ Uri appUri = Uri.parse(appUrl + args.id).replace(queryParameters: attrs.value);
+ _setWebviewUrl(appUri);
+ }
+ });
},
[connectionState],
);
@@ -188,15 +197,16 @@ class StoreTab extends HookWidget implements CobbleScreen {
tooltip: MaterialLocalizations.of(context).backButtonTooltip,
)
: null,
- actions: [
+ actions: args == null
+ ? [
IconButton(
- icon: Icon(RebbleIcons.search),
- onPressed: () {
- searchBar.value = true;
- },
- tooltip: MaterialLocalizations.of(context).searchFieldLabel,
- ),
- ],
+ icon: Icon(RebbleIcons.search),
+ onPressed: () {
+ searchBar.value = true;
+ },
+ tooltip: MaterialLocalizations.of(context).searchFieldLabel,
+ ),
+ ] : [],
bottomAppBar: searchBar.value
? PreferredSize(
preferredSize: Size.fromHeight(57),
@@ -234,55 +244,60 @@ class StoreTab extends HookWidget implements CobbleScreen {
),
)
: null,
- titleWidget: DropdownButton(
- value: indexTab.value,
- icon: Icon(RebbleIcons.dropdown),
- iconSize: 25,
- underline: Container(
- height: 0,
- ),
- onChanged: (int? newValue) => _setIndexTab(newValue!),
- selectedItemBuilder: (BuildContext context) {
- return [0, 1].map((int value) {
- return Row(
- mainAxisAlignment: MainAxisAlignment.center,
- children: [
- SizedBox(width: 25.0), //Offset to appear centered
- Container(
- width: 125,
- height: 57,
- child: Column(
- mainAxisAlignment: MainAxisAlignment.center,
- children: [
- Text(
- pageTitle.value,
- overflow: TextOverflow.ellipsis,
- ),
- if (pageTitle.value != _config[value].label) ...[
- SizedBox(height: 4),
+ titleWidget: args == null
+ ? DropdownButton(
+ value: indexTab.value,
+ icon: Icon(RebbleIcons.dropdown),
+ iconSize: 25,
+ underline: Container(
+ height: 0,
+ ),
+ onChanged: (int? newValue) => _setIndexTab(newValue!),
+ selectedItemBuilder: (BuildContext context) {
+ return [0, 1].map((int value) {
+ return Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ SizedBox(width: 25.0), //Offset to appear centered
+ Container(
+ width: 125,
+ height: 57,
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
Text(
- _config[value].label,
- style: context.theme.appBarTheme.textTheme!.headline6!
- .copyWith(
- fontSize: 14,
- color: context.scheme!.muted,
- ),
+ pageTitle.value,
+ overflow: TextOverflow.ellipsis,
),
+ if (pageTitle.value != _config[value].label) ...[
+ SizedBox(height: 4),
+ Text(
+ _config[value].label,
+ style: context.theme.appBarTheme.textTheme!.headline6!
+ .copyWith(
+ fontSize: 14,
+ color: context.scheme!.muted,
+ ),
+ ),
+ ],
],
- ],
+ ),
),
- ),
- ],
+ ],
+ );
+ }).toList();
+ },
+ items: [0, 1].map>((int value) {
+ return DropdownMenuItem(
+ value: value,
+ child: Text(_config[value].label),
);
- }).toList();
- },
- items: [0, 1].map>((int value) {
- return DropdownMenuItem(
- value: value,
- child: Text(_config[value].label),
- );
- }).toList(),
- ),
+ }).toList(),
+ )
+ : Text(
+ pageTitle.value,
+ overflow: TextOverflow.ellipsis,
+ ),
child: WebView(
initialUrl: baseUrl.value.toString(),
javascriptMode: JavascriptMode.unrestricted,
@@ -314,3 +329,9 @@ class StoreTab extends HookWidget implements CobbleScreen {
}
}
}
+
+class AppstoreArguments {
+ final String id;
+
+ AppstoreArguments(this.id);
+}
diff --git a/lib/ui/router/uri_navigator.dart b/lib/ui/router/uri_navigator.dart
index 3ed844cb..a9ba58a8 100644
--- a/lib/ui/router/uri_navigator.dart
+++ b/lib/ui/router/uri_navigator.dart
@@ -1,5 +1,6 @@
import 'dart:io';
+import 'package:cobble/ui/home/tabs/store_tab.dart';
import 'package:cobble/ui/router/cobble_navigator.dart';
import 'package:cobble/ui/screens/install_prompt.dart';
import 'package:flutter/cupertino.dart';
@@ -25,9 +26,16 @@ class UriNavigator implements IntentCallbacks {
@override
void openUri(StringWrapper arg) async {
- String uri = arg.value!;
+ Uri uri = Uri.parse(arg.value!);
+ if (uri.isScheme("pebble") && uri.host == 'appstore') {
+ String id = uri.pathSegments[0];
+ // TODO: We currently set up a minified StoreTab() for this, but it would be
+ // better if we just used the StoreTab() that already exists and navigated
+ // directly to the app from it (ie. handleRequest('navigate', { 'url': '/application/$id' }))
+ Navigator.of(_context).pushNamed('/appstore', arguments: AppstoreArguments(id));
+ }
- if (Platform.isAndroid && !uri.startsWith("content://")) {
+ if (Platform.isAndroid && !uri.isScheme("content")) {
// Only content URIs are supported
return;
}
@@ -35,10 +43,10 @@ class UriNavigator implements IntentCallbacks {
AppInstallControl control = AppInstallControl();
final uriWrapper = StringWrapper();
- uriWrapper.value = uri;
+ uriWrapper.value = uri.toString();
final pbwResult = await control.getAppInfo(uriWrapper);
- _context.push(InstallPrompt(uri, pbwResult));
+ _context.push(InstallPrompt(uri.toString(), pbwResult));
}
}