Store assets: landscape tablet screenshots (iPad, Android 7/10-inch)

Captured from the app's real sidebar shell at 1024x768; adds landscape
compositions to compose.py and the README upload table.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
Ayushya Amitabh
2026-10-07 21:46:29 -04:00
co-authored by Claude Sonnet 5.5
parent 11b3d535a9
commit 5837464581
44 changed files with 288 additions and 19 deletions
@@ -11,6 +11,10 @@ import 'package:flutter_test/flutter_test.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:noo/main.dart' show MainShellView;
import 'package:noo/providers/recent_controller.dart';
import 'package:noo/providers/shares_controller.dart';
import 'package:noo/providers/trash_controller.dart';
import 'package:noo/models/app_tab.dart';
import 'package:noo/theme/design_tokens.dart';
import 'package:noo/views/lock_screen_view.dart';
@@ -169,10 +173,26 @@ class DemoOps extends ItemOperations {
Future<List<NextcloudSharee>> searchSharees(String query) async => [];
}
/// The shell collapses to the Offline tab alone when it thinks there is no
/// network, so the landscape (full shell) captures report an online device.
class DemoConnectivity extends ConnectivityController {
@override
bool get isOffline => false;
}
class DemoFiles extends FilesController {
DemoFiles(super.session, {this.external = false});
final bool external;
@override
NextcloudUserQuota? get quota => const NextcloudUserQuota(
usedBytes: 46000000000,
totalBytes: 107374182400,
usagePercentage: 0.43,
userName: 'alex',
email: 'alex@cloud.example.com',
serverVersion: '31.0.0',
);
@override
StorageScope get storageScope =>
external ? StorageScope.all : StorageScope.cloud;
@override
@@ -329,12 +349,31 @@ void main() {
MockStreamHandler.inline(onListen: (arguments, events) {}),
);
}
// The shell listens for share/pick intents; none ever arrive here.
for (final name in [
'dev.ayushya.noo/share_intent/new',
'dev.ayushya.noo/pick_intent/new',
]) {
messenger.setMockStreamHandler(
EventChannel(name),
MockStreamHandler.inline(onListen: (arguments, events) {}),
);
}
messenger.setMockStreamHandler(
const EventChannel('dev.fluttercommunity.plus/connectivity_status'),
MockStreamHandler.inline(onListen: (arguments, events) {}),
);
});
for (final device in ['android', 'atablet', 'iphone', 'ipad']) {
// `ipadl` / `atabletl` are the landscape tablet captures, which render the
// app's real shell (sidebar + toolbar) instead of the single-screen widgets.
for (final device in [
'android',
'atablet',
'atabletl',
'iphone',
'ipad',
'ipadl',
]) {
for (final screen in [
'accounts',
'lock',
@@ -353,17 +392,27 @@ void main() {
'ui_lock_account_switching': true,
'ui_lock_hidden_files': true,
'ui_use_dynamic_color': false,
// Skips the shell's one-time notification-permission prompt.
'notification_permission_asked': true,
});
final tablet = device == 'ipad' || device == 'atablet';
final android = device == 'android' || device == 'atablet';
final size = tablet
final landscape = device == 'ipadl' || device == 'atabletl';
final tablet = landscape || device == 'ipad' || device == 'atablet';
final android =
device == 'android' ||
device == 'atablet' ||
device == 'atabletl';
final size = landscape
? const Size(1024, 768)
: tablet
? const Size(768, 1024)
: (android ? const Size(430, 800) : const Size(430, 932));
tester.view.devicePixelRatio = 1;
tester.view.physicalSize = size;
await tester.binding.setSurfaceSize(size);
final key = GlobalKey();
final connectivity = ConnectivityController();
final connectivity = landscape
? DemoConnectivity()
: ConnectivityController();
final session = DemoSession(connectivity);
final files = DemoFiles(session, external: screen == 'external');
final settings = SettingsController();
@@ -378,6 +427,12 @@ void main() {
photos: photos,
favorites: favorites,
);
final trash = TrashController(session);
final shares = SharesController(session);
final recent = RecentController(session);
if (landscape && screen == 'offline') {
settings.requestTab(AppTab.offline);
}
final scroll = ScrollController();
final style = android ? NooNavStyle.android : NooNavStyle.ios;
final browsing = const {
@@ -412,6 +467,12 @@ void main() {
ChangeNotifierProvider<OfflineController>.value(value: offline),
ChangeNotifierProvider<PickController>.value(value: pick),
ChangeNotifierProvider<PhotosController>.value(value: photos),
ChangeNotifierProvider<FavoritesController>.value(
value: favorites,
),
ChangeNotifierProvider<TrashController>.value(value: trash),
ChangeNotifierProvider<SharesController>.value(value: shares),
ChangeNotifierProvider<RecentController>.value(value: recent),
Provider<ItemOperations>.value(value: ops),
],
child: MaterialApp(
@@ -423,6 +484,8 @@ void main() {
card ? child! : RepaintBoundary(key: key, child: child),
home: screen == 'lock'
? const LockScreenView()
: landscape && browsing
? const MainShellView()
: Scaffold(
body: browsing
? FilesView(
@@ -438,16 +501,27 @@ void main() {
)
: Align(
alignment: Alignment.topCenter,
child: Builder(
builder: (context) => RepaintBoundary(
key: key,
child: ColoredBox(
color: context.nooColors.bg,
child: Padding(
padding: const EdgeInsets.all(12),
child: screen == 'security'
? const SettingsSecuritySection()
: const SettingsFileSyncSection(),
child: ConstrainedBox(
// Landscape tablets would stretch the card
// across the whole window; keep it phone-like.
constraints: BoxConstraints(
maxWidth: landscape ? 520 : double.infinity,
),
child: Builder(
builder: (context) => RepaintBoundary(
key: key,
child: ColoredBox(
color: context.nooColors.bg,
child: Padding(
padding: const EdgeInsets.all(12),
// The desktop-style card fills its
// height, so size it to its content.
child: IntrinsicHeight(
child: screen == 'security'
? const SettingsSecuritySection()
: const SettingsFileSyncSection(),
),
),
),
),
),
@@ -514,6 +588,9 @@ void main() {
});
await tester.pumpWidget(const SizedBox.shrink());
scroll.dispose();
trash.dispose();
shares.dispose();
recent.dispose();
pick.dispose();
offline.dispose();
sync.dispose();