ruby_everywhere 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +255 -0
  3. data/bridge/everywhere/bridge.js +60 -3
  4. data/lib/everywhere/android_resources.rb +217 -0
  5. data/lib/everywhere/android_sdk.rb +330 -0
  6. data/lib/everywhere/asset_catalog.rb +185 -0
  7. data/lib/everywhere/auth_handoff.rb +398 -0
  8. data/lib/everywhere/auth_token.rb +118 -0
  9. data/lib/everywhere/builders/android.rb +844 -0
  10. data/lib/everywhere/builders/ios.rb +40 -3
  11. data/lib/everywhere/commands/build.rb +51 -9
  12. data/lib/everywhere/commands/clean.rb +10 -5
  13. data/lib/everywhere/commands/dev.rb +46 -8
  14. data/lib/everywhere/commands/doctor.rb +89 -3
  15. data/lib/everywhere/commands/install.rb +1 -1
  16. data/lib/everywhere/commands/logs.rb +117 -8
  17. data/lib/everywhere/config.rb +409 -13
  18. data/lib/everywhere/emulator.rb +405 -0
  19. data/lib/everywhere/engine.rb +12 -0
  20. data/lib/everywhere/icon.rb +196 -0
  21. data/lib/everywhere/log_filter.rb +74 -0
  22. data/lib/everywhere/native_helper.rb +2 -5
  23. data/lib/everywhere/omniauth.rb +88 -0
  24. data/lib/everywhere/paths.rb +47 -0
  25. data/lib/everywhere/version.rb +1 -1
  26. data/support/mobile/android/README.md +217 -0
  27. data/support/mobile/android/app/build.gradle.kts +156 -0
  28. data/support/mobile/android/app/everywhere.properties +8 -0
  29. data/support/mobile/android/app/native-packages.gradle.kts +16 -0
  30. data/support/mobile/android/app/proguard-rules.pro +21 -0
  31. data/support/mobile/android/app/src/debug/res/xml/network_security_config.xml +30 -0
  32. data/support/mobile/android/app/src/main/AndroidManifest.xml +42 -0
  33. data/support/mobile/android/app/src/main/assets/everywhere.json +9 -0
  34. data/support/mobile/android/app/src/main/assets/fonts/MaterialIcons-Regular.codepoints +2235 -0
  35. data/support/mobile/android/app/src/main/assets/fonts/MaterialIcons-Regular.ttf +0 -0
  36. data/support/mobile/android/app/src/main/assets/json/path-configuration.json +19 -0
  37. data/support/mobile/android/app/src/main/java/com/rubyeverywhere/shell/AuthFlow.kt +268 -0
  38. data/support/mobile/android/app/src/main/java/com/rubyeverywhere/shell/EverywhereApplication.kt +178 -0
  39. data/support/mobile/android/app/src/main/java/com/rubyeverywhere/shell/EverywhereConfig.kt +409 -0
  40. data/support/mobile/android/app/src/main/java/com/rubyeverywhere/shell/EverywhereEvents.kt +68 -0
  41. data/support/mobile/android/app/src/main/java/com/rubyeverywhere/shell/EverywhereHost.kt +101 -0
  42. data/support/mobile/android/app/src/main/java/com/rubyeverywhere/shell/IconFont.kt +211 -0
  43. data/support/mobile/android/app/src/main/java/com/rubyeverywhere/shell/MainActivity.kt +975 -0
  44. data/support/mobile/android/app/src/main/java/com/rubyeverywhere/shell/MainTabs.kt +201 -0
  45. data/support/mobile/android/app/src/main/java/com/rubyeverywhere/shell/WebBottomSheetFragment.kt +13 -0
  46. data/support/mobile/android/app/src/main/java/com/rubyeverywhere/shell/WebFragment.kt +36 -0
  47. data/support/mobile/android/app/src/main/java/com/rubyeverywhere/shell/bridge/BiometricsComponent.kt +535 -0
  48. data/support/mobile/android/app/src/main/java/com/rubyeverywhere/shell/bridge/HapticsComponent.kt +141 -0
  49. data/support/mobile/android/app/src/main/java/com/rubyeverywhere/shell/bridge/MenuComponent.kt +361 -0
  50. data/support/mobile/android/app/src/main/java/com/rubyeverywhere/shell/bridge/NotificationComponent.kt +151 -0
  51. data/support/mobile/android/app/src/main/java/com/rubyeverywhere/shell/bridge/PermissionsComponent.kt +271 -0
  52. data/support/mobile/android/app/src/main/java/com/rubyeverywhere/shell/bridge/StorageComponent.kt +101 -0
  53. data/support/mobile/android/app/src/main/java/com/rubyeverywhere/shell/bridge/WebControlChannel.kt +204 -0
  54. data/support/mobile/android/app/src/main/java/com/rubyeverywhere/shell/extensions/EverywhereExtensions.kt +51 -0
  55. data/support/mobile/android/app/src/main/res/color/bottom_navigation_active_indicator.xml +4 -0
  56. data/support/mobile/android/app/src/main/res/color/bottom_navigation_item.xml +11 -0
  57. data/support/mobile/android/app/src/main/res/drawable/ic_launcher_foreground.xml +26 -0
  58. data/support/mobile/android/app/src/main/res/drawable/ic_tab_placeholder.xml +28 -0
  59. data/support/mobile/android/app/src/main/res/layout/activity_main.xml +116 -0
  60. data/support/mobile/android/app/src/main/res/layout/bridge_menu_sheet.xml +83 -0
  61. data/support/mobile/android/app/src/main/res/layout/bridge_menu_sheet_item.xml +35 -0
  62. data/support/mobile/android/app/src/main/res/layout/tab_more_sheet.xml +48 -0
  63. data/support/mobile/android/app/src/main/res/layout/tab_more_sheet_item.xml +35 -0
  64. data/support/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +12 -0
  65. data/support/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +6 -0
  66. data/support/mobile/android/app/src/main/res/values/colors.xml +21 -0
  67. data/support/mobile/android/app/src/main/res/values/ic_launcher_background.xml +5 -0
  68. data/support/mobile/android/app/src/main/res/values/strings.xml +18 -0
  69. data/support/mobile/android/app/src/main/res/values/styles.xml +32 -0
  70. data/support/mobile/android/app/src/main/res/values/themes.xml +53 -0
  71. data/support/mobile/android/app/src/main/res/values-night/colors.xml +11 -0
  72. data/support/mobile/android/app/src/main/res/values-night/themes.xml +33 -0
  73. data/support/mobile/android/app/src/main/res/xml/network_security_config.xml +12 -0
  74. data/support/mobile/android/app/src/stamped/AndroidManifest.xml +34 -0
  75. data/support/mobile/android/build.gradle.kts +8 -0
  76. data/support/mobile/android/gradle/wrapper/gradle-wrapper.jar +0 -0
  77. data/support/mobile/android/gradle/wrapper/gradle-wrapper.properties +10 -0
  78. data/support/mobile/android/gradle.properties +14 -0
  79. data/support/mobile/android/gradlew +251 -0
  80. data/support/mobile/android/gradlew.bat +94 -0
  81. data/support/mobile/android/settings.gradle.kts +28 -0
  82. data/support/mobile/ios/App/AppDelegate.swift +5 -0
  83. data/support/mobile/ios/App/AuthFlow.swift +101 -0
  84. data/support/mobile/ios/App/EverywhereConfig.swift +84 -0
  85. data/support/mobile/ios/App/SceneDelegate.swift +103 -0
  86. data/support/mobile/ios/App.xcodeproj/project.pbxproj +4 -0
  87. data/support/mobile/ios/README.md +4 -1
  88. metadata +67 -1
@@ -0,0 +1,28 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!--
3
+ A deliberately blank 24dp drawable, and the only reason it exists:
4
+ `HotwireBottomTab.iconResId` is an `@DrawableRes Int`, so there is no way to
5
+ hand the controller the bitmap IconFont renders. `loadMenu()` calls
6
+ `setIcon(iconResId)` before we can intervene and an invalid id throws, so
7
+ every tab is built with this placeholder and MainActivity overwrites
8
+ `bottomNav.menu.getItem(i).icon` immediately after each `load()`.
9
+
10
+ It draws nothing rather than a fallback glyph because it is never meant to be
11
+ seen: if it ever shows up on a tab, the icon re-apply pass was skipped.
12
+
13
+ The transparent path below is REQUIRED, not decoration. A <vector> with no
14
+ <path> compiles cleanly through aapt2 and then throws
15
+ `XmlPullParserException: no path defined` when the device inflates it — which
16
+ means the crash lands in MainActivity.applyTabs at launch, on every app that
17
+ declares a tab, with a stack trace that names VectorDrawable rather than this
18
+ file. Keep a path here.
19
+ -->
20
+ <vector xmlns:android="http://schemas.android.com/apk/res/android"
21
+ android:width="24dp"
22
+ android:height="24dp"
23
+ android:viewportWidth="24"
24
+ android:viewportHeight="24">
25
+ <path
26
+ android:fillColor="#00000000"
27
+ android:pathData="M0,0h24v24h-24z" />
28
+ </vector>
@@ -0,0 +1,116 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!--
3
+ FROZEN, and the reason MainTabs exists.
4
+
5
+ A HotwireBottomTab needs a real FragmentContainerView id, and NavigatorHost
6
+ resolves its configuration once in onCreate. Tabs, however, arrive at runtime
7
+ from the server's path configuration and are auth-gated, so the shell cannot
8
+ know how many there will be. The answer is a fixed pool: five hosts declared
9
+ here (Material bottom navigation tops out at five items anyway), with the
10
+ resolved tabs mapped onto them and every unused slot expressed as
11
+ `isVisible = false`.
12
+
13
+ Two rules follow from that and are enforced in MainTabs/MainActivity:
14
+ * navigatorConfigurations() must return a configuration for ALL FIVE hosts
15
+ on every launch, or NavigatorHost.onCreate throws IllegalStateException
16
+ for each slot it can't find one for.
17
+ * `lazyLoadTabs = true` is mandatory, or all five hosts cold-boot a WebView
18
+ and fetch their start location at launch — including the hidden,
19
+ auth-gated ones.
20
+
21
+ The splash overlay is a sibling of the hosts rather than a separate window
22
+ (iOS uses a second UIWindow): an Activity has one content view, and the
23
+ bottom navigation is rebuilt underneath the overlay, not around it.
24
+ -->
25
+ <androidx.constraintlayout.widget.ConstraintLayout
26
+ xmlns:android="http://schemas.android.com/apk/res/android"
27
+ xmlns:app="http://schemas.android.com/apk/res-auto"
28
+ xmlns:tools="http://schemas.android.com/tools"
29
+ android:id="@+id/root"
30
+ android:layout_width="match_parent"
31
+ android:layout_height="match_parent"
32
+ tools:context=".MainActivity">
33
+
34
+ <androidx.fragment.app.FragmentContainerView
35
+ android:id="@+id/navigator_host_1"
36
+ android:name="dev.hotwire.navigation.navigator.NavigatorHost"
37
+ android:layout_width="match_parent"
38
+ android:layout_height="0dp"
39
+ app:defaultNavHost="false"
40
+ app:layout_constraintBottom_toTopOf="@id/bottom_nav"
41
+ app:layout_constraintTop_toTopOf="parent" />
42
+
43
+ <androidx.fragment.app.FragmentContainerView
44
+ android:id="@+id/navigator_host_2"
45
+ android:name="dev.hotwire.navigation.navigator.NavigatorHost"
46
+ android:layout_width="match_parent"
47
+ android:layout_height="0dp"
48
+ app:defaultNavHost="false"
49
+ app:layout_constraintBottom_toTopOf="@id/bottom_nav"
50
+ app:layout_constraintTop_toTopOf="parent" />
51
+
52
+ <androidx.fragment.app.FragmentContainerView
53
+ android:id="@+id/navigator_host_3"
54
+ android:name="dev.hotwire.navigation.navigator.NavigatorHost"
55
+ android:layout_width="match_parent"
56
+ android:layout_height="0dp"
57
+ app:defaultNavHost="false"
58
+ app:layout_constraintBottom_toTopOf="@id/bottom_nav"
59
+ app:layout_constraintTop_toTopOf="parent" />
60
+
61
+ <androidx.fragment.app.FragmentContainerView
62
+ android:id="@+id/navigator_host_4"
63
+ android:name="dev.hotwire.navigation.navigator.NavigatorHost"
64
+ android:layout_width="match_parent"
65
+ android:layout_height="0dp"
66
+ app:defaultNavHost="false"
67
+ app:layout_constraintBottom_toTopOf="@id/bottom_nav"
68
+ app:layout_constraintTop_toTopOf="parent" />
69
+
70
+ <androidx.fragment.app.FragmentContainerView
71
+ android:id="@+id/navigator_host_5"
72
+ android:name="dev.hotwire.navigation.navigator.NavigatorHost"
73
+ android:layout_width="match_parent"
74
+ android:layout_height="0dp"
75
+ app:defaultNavHost="false"
76
+ app:layout_constraintBottom_toTopOf="@id/bottom_nav"
77
+ app:layout_constraintTop_toTopOf="parent" />
78
+
79
+ <com.google.android.material.bottomnavigation.BottomNavigationView
80
+ android:id="@+id/bottom_nav"
81
+ android:layout_width="match_parent"
82
+ android:layout_height="wrap_content"
83
+ app:labelVisibilityMode="labeled"
84
+ app:layout_constraintBottom_toBottomOf="parent"
85
+ app:layout_constraintEnd_toEndOf="parent"
86
+ app:layout_constraintStart_toStartOf="parent" />
87
+
88
+ <!--
89
+ Continues the themed launch background over the first page load, so the
90
+ app never shows a white gap while the first request is in flight. Sits
91
+ above everything, including the bottom navigation, and is removed by
92
+ MainActivity.dismissSplash().
93
+ -->
94
+ <FrameLayout
95
+ android:id="@+id/splash"
96
+ android:layout_width="0dp"
97
+ android:layout_height="0dp"
98
+ android:background="?android:attr/colorBackground"
99
+ android:clickable="true"
100
+ android:focusable="true"
101
+ android:elevation="16dp"
102
+ app:layout_constraintBottom_toBottomOf="parent"
103
+ app:layout_constraintEnd_toEndOf="parent"
104
+ app:layout_constraintStart_toStartOf="parent"
105
+ app:layout_constraintTop_toTopOf="parent">
106
+
107
+ <ProgressBar
108
+ android:id="@+id/splash_progress"
109
+ style="?android:attr/progressBarStyle"
110
+ android:layout_width="wrap_content"
111
+ android:layout_height="wrap_content"
112
+ android:layout_gravity="center" />
113
+
114
+ </FrameLayout>
115
+
116
+ </androidx.constraintlayout.widget.ConstraintLayout>
@@ -0,0 +1,83 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!--
3
+ Action sheet for the `everywhere` menu bridge component: both the `menu`
4
+ event (Everywhere.menu / data-everywhere-menu) and a nav item's pull-down.
5
+
6
+ Deliberately built from framework widgets and theme attributes only — no
7
+ colors.xml, no dimens.xml, no styles of its own. The rest of res/ belongs to
8
+ the shell template and is stamped per app, so anything this file referenced
9
+ there would be a second place a brand colour has to be right. Every colour
10
+ below comes from the running theme, which makes light and dark automatic.
11
+ -->
12
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
13
+ android:layout_width="match_parent"
14
+ android:layout_height="wrap_content"
15
+ android:orientation="vertical"
16
+ android:paddingTop="8dp"
17
+ android:paddingBottom="8dp">
18
+
19
+ <LinearLayout
20
+ android:id="@+id/bridge_menu_header"
21
+ android:layout_width="match_parent"
22
+ android:layout_height="wrap_content"
23
+ android:orientation="vertical"
24
+ android:paddingStart="24dp"
25
+ android:paddingEnd="24dp"
26
+ android:paddingTop="12dp"
27
+ android:paddingBottom="12dp">
28
+
29
+ <TextView
30
+ android:id="@+id/bridge_menu_title"
31
+ android:layout_width="match_parent"
32
+ android:layout_height="wrap_content"
33
+ android:textColor="?android:attr/textColorPrimary"
34
+ android:textSize="16sp"
35
+ android:textStyle="bold" />
36
+
37
+ <TextView
38
+ android:id="@+id/bridge_menu_message"
39
+ android:layout_width="match_parent"
40
+ android:layout_height="wrap_content"
41
+ android:layout_marginTop="4dp"
42
+ android:textColor="?android:attr/textColorSecondary"
43
+ android:textSize="14sp" />
44
+ </LinearLayout>
45
+
46
+ <ScrollView
47
+ android:layout_width="match_parent"
48
+ android:layout_height="wrap_content"
49
+ android:scrollbarStyle="outsideOverlay">
50
+
51
+ <LinearLayout
52
+ android:id="@+id/bridge_menu_items"
53
+ android:layout_width="match_parent"
54
+ android:layout_height="wrap_content"
55
+ android:orientation="vertical" />
56
+ </ScrollView>
57
+
58
+ <View
59
+ android:layout_width="match_parent"
60
+ android:layout_height="1dp"
61
+ android:layout_marginTop="8dp"
62
+ android:layout_marginBottom="8dp"
63
+ android:background="?android:attr/listDivider" />
64
+
65
+ <!--
66
+ Always present, like the cancel action iOS adds to every action sheet.
67
+ A pull-down menu simply ignores the resulting dismiss.
68
+ -->
69
+ <TextView
70
+ android:id="@+id/bridge_menu_cancel"
71
+ android:layout_width="match_parent"
72
+ android:layout_height="wrap_content"
73
+ android:background="?android:attr/selectableItemBackground"
74
+ android:clickable="true"
75
+ android:focusable="true"
76
+ android:gravity="center_vertical"
77
+ android:minHeight="56dp"
78
+ android:paddingStart="24dp"
79
+ android:paddingEnd="24dp"
80
+ android:text="@android:string/cancel"
81
+ android:textColor="?android:attr/textColorSecondary"
82
+ android:textSize="16sp" />
83
+ </LinearLayout>
@@ -0,0 +1,35 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!--
3
+ One row of the `everywhere` menu action sheet. The icon slot is kept (and
4
+ hidden rather than removed) whenever any sibling row has an icon, so a mixed
5
+ list still lines its titles up — the same alignment a UIMenu gives on iOS.
6
+
7
+ Theme attributes only; see bridge_menu_sheet.xml for why.
8
+ -->
9
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
10
+ android:layout_width="match_parent"
11
+ android:layout_height="wrap_content"
12
+ android:background="?android:attr/selectableItemBackground"
13
+ android:clickable="true"
14
+ android:focusable="true"
15
+ android:gravity="center_vertical"
16
+ android:minHeight="56dp"
17
+ android:orientation="horizontal"
18
+ android:paddingStart="24dp"
19
+ android:paddingEnd="24dp">
20
+
21
+ <ImageView
22
+ android:id="@+id/bridge_menu_item_icon"
23
+ android:layout_width="24dp"
24
+ android:layout_height="24dp"
25
+ android:layout_marginEnd="24dp"
26
+ android:importantForAccessibility="no"
27
+ android:visibility="gone" />
28
+
29
+ <TextView
30
+ android:id="@+id/bridge_menu_item_title"
31
+ android:layout_width="match_parent"
32
+ android:layout_height="wrap_content"
33
+ android:textColor="?android:attr/textColorPrimary"
34
+ android:textSize="16sp" />
35
+ </LinearLayout>
@@ -0,0 +1,48 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!--
3
+ The "More" tab's contents: every tab past the fourth, which Android's
4
+ BottomNavigationView has no room for and, unlike iOS's UITabBarController,
5
+ synthesises no home for. MainActivity fills this in from the arrangement.
6
+
7
+ Framework widgets and theme attributes only, no colors.xml and no styles of
8
+ its own. The rest of res/ belongs to the shell template and is stamped per
9
+ app, so anything referenced from here would be a second place a brand colour
10
+ has to be right. Every colour below comes from the running theme, which makes
11
+ light and dark automatic.
12
+ -->
13
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
14
+ android:layout_width="match_parent"
15
+ android:layout_height="wrap_content"
16
+ android:orientation="vertical"
17
+ android:paddingTop="8dp"
18
+ android:paddingBottom="8dp">
19
+
20
+ <TextView
21
+ android:id="@+id/tab_more_sheet_title"
22
+ android:layout_width="match_parent"
23
+ android:layout_height="wrap_content"
24
+ android:paddingStart="24dp"
25
+ android:paddingEnd="24dp"
26
+ android:paddingTop="12dp"
27
+ android:paddingBottom="12dp"
28
+ android:textColor="?android:attr/textColorPrimary"
29
+ android:textSize="16sp"
30
+ android:textStyle="bold" />
31
+
32
+ <!--
33
+ Scrolls because the overflow has no upper bound: five tabs fit the bar,
34
+ and everything after that arrives here however many there are.
35
+ -->
36
+ <ScrollView
37
+ android:layout_width="match_parent"
38
+ android:layout_height="wrap_content"
39
+ android:scrollbarStyle="outsideOverlay">
40
+
41
+ <LinearLayout
42
+ android:id="@+id/tab_more_sheet_items"
43
+ android:layout_width="match_parent"
44
+ android:layout_height="wrap_content"
45
+ android:orientation="vertical" />
46
+ </ScrollView>
47
+
48
+ </LinearLayout>
@@ -0,0 +1,35 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!--
3
+ One overflow tab in the More sheet: its icon and its title, the same two
4
+ things it would have shown in the bar.
5
+
6
+ The icon slot is always present, never hidden, because every tab has an icon
7
+ (MainTabs defaults a missing one to `circle`, and the builder rejects a name
8
+ the font doesn't carry). Theme attributes only; see tab_more_sheet.xml.
9
+ -->
10
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
11
+ android:layout_width="match_parent"
12
+ android:layout_height="wrap_content"
13
+ android:background="?android:attr/selectableItemBackground"
14
+ android:clickable="true"
15
+ android:focusable="true"
16
+ android:gravity="center_vertical"
17
+ android:minHeight="56dp"
18
+ android:orientation="horizontal"
19
+ android:paddingStart="24dp"
20
+ android:paddingEnd="24dp">
21
+
22
+ <ImageView
23
+ android:id="@+id/tab_more_sheet_item_icon"
24
+ android:layout_width="24dp"
25
+ android:layout_height="24dp"
26
+ android:layout_marginEnd="24dp"
27
+ android:importantForAccessibility="no" />
28
+
29
+ <TextView
30
+ android:id="@+id/tab_more_sheet_item_title"
31
+ android:layout_width="match_parent"
32
+ android:layout_height="wrap_content"
33
+ android:textColor="?android:attr/textColorPrimary"
34
+ android:textSize="16sp" />
35
+ </LinearLayout>
@@ -0,0 +1,12 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!--
3
+ minSdk 28 means every device that can install this app understands adaptive
4
+ icons, so there are no legacy mipmap-<dpi>/ic_launcher.png fallbacks to
5
+ generate — the two layers below are the whole launcher icon. The builder
6
+ stamps the layers, never this file.
7
+ -->
8
+ <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
9
+ <background android:drawable="@color/ic_launcher_background" />
10
+ <foreground android:drawable="@drawable/ic_launcher_foreground" />
11
+ <monochrome android:drawable="@drawable/ic_launcher_foreground" />
12
+ </adaptive-icon>
@@ -0,0 +1,6 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
3
+ <background android:drawable="@color/ic_launcher_background" />
4
+ <foreground android:drawable="@drawable/ic_launcher_foreground" />
5
+ <monochrome android:drawable="@drawable/ic_launcher_foreground" />
6
+ </adaptive-icon>
@@ -0,0 +1,21 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!--
3
+ STAMPED by `every build` for Android from `appearance.tint_color` and
4
+ `appearance.background_color`. The light half lives here, the dark half in
5
+ values-night/colors.xml — an app that gives a single hex gets the same value
6
+ written to both, which is exactly how iOS's ThemedColor collapses.
7
+
8
+ Everything else in the theme derives from these two, so branding an app is
9
+ two colours and nothing more.
10
+ -->
11
+ <resources>
12
+ <color name="everywhere_tint">#5321FC</color>
13
+ <color name="everywhere_background">#FFFFFF</color>
14
+
15
+ <color name="everywhere_on_tint">#FFFFFF</color>
16
+ <color name="everywhere_on_background">#000000</color>
17
+ <color name="everywhere_error">#B00020</color>
18
+ <color name="everywhere_on_error">#FFFFFF</color>
19
+
20
+ <color name="everywhere_transparent">#00000000</color>
21
+ </resources>
@@ -0,0 +1,5 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!-- STAMPED: the adaptive icon's background layer. -->
3
+ <resources>
4
+ <color name="ic_launcher_background">#5321FC</color>
5
+ </resources>
@@ -0,0 +1,18 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!--
3
+ FROZEN. The app's own name is NOT here: it arrives as the
4
+ `everywhereLabel` manifest placeholder from everywhere.properties, so
5
+ renaming an app never touches a resource file. Only shell-owned UI strings
6
+ live here.
7
+ -->
8
+ <resources>
9
+ <string name="everywhere_auth_failed_title">Sign-in didn\'t finish</string>
10
+ <string name="everywhere_auth_failed_message">Something went wrong on the way back to the app. Please try again.</string>
11
+ <string name="everywhere_ok">OK</string>
12
+ <!--
13
+ The fifth bar item when an app declares more than five tabs, and the
14
+ heading of the sheet listing the rest. Matches what UITabBarController
15
+ names its own overflow tab on iOS.
16
+ -->
17
+ <string name="everywhere_more_tab">More</string>
18
+ </resources>
@@ -0,0 +1,32 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!-- FROZEN. Component styles; all colour comes from the theme attributes. -->
3
+ <resources>
4
+
5
+ <style name="Widget.Everywhere.BottomNavigationView" parent="Widget.MaterialComponents.BottomNavigationView">
6
+ <item name="android:background">?colorSurface</item>
7
+ <item name="itemBackground">?colorSurface</item>
8
+ <item name="backgroundTint">@null</item>
9
+ <item name="elevation">8dp</item>
10
+ <!-- Tab icons are runtime-rendered bitmaps (IconFont), so the tint has
11
+ to come from a ColorStateList the widget applies to whatever
12
+ Drawable it is handed — that is what makes a font-drawn glyph
13
+ respond to selection and to dark mode at all. -->
14
+ <item name="itemIconTint">@color/bottom_navigation_item</item>
15
+ <item name="itemTextColor">@color/bottom_navigation_item</item>
16
+ <item name="itemActiveIndicatorStyle">@style/Widget.Everywhere.BottomNavigationView.ActiveIndicator</item>
17
+ </style>
18
+
19
+ <style name="Widget.Everywhere.BottomNavigationView.ActiveIndicator" parent="">
20
+ <item name="android:width">64dp</item>
21
+ <item name="android:height">32dp</item>
22
+ <item name="marginHorizontal">4dp</item>
23
+ <item name="android:color">@color/bottom_navigation_active_indicator</item>
24
+ </style>
25
+
26
+ <style name="Widget.Everywhere.Toolbar" parent="Widget.MaterialComponents.Toolbar.Surface">
27
+ <item name="android:background">?colorSurface</item>
28
+ <item name="colorControlNormal">?colorOnSurface</item>
29
+ <item name="titleTextColor">?colorOnSurface</item>
30
+ </style>
31
+
32
+ </resources>
@@ -0,0 +1,53 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!--
3
+ FROZEN. Everything visible derives from the two stamped colours in
4
+ colors.xml, so branding never edits a theme.
5
+
6
+ Parent is Theme.MaterialComponents, not Theme.Material3, on purpose:
7
+ navigation-fragments 1.3.0's own layouts style themselves with
8
+ TextAppearance.MaterialComponents.* and this is the combination the Phase 0
9
+ spike actually ran on a device.
10
+ -->
11
+ <resources xmlns:tools="http://schemas.android.com/tools">
12
+
13
+ <style name="Theme.Everywhere" parent="Theme.MaterialComponents.DayNight.NoActionBar">
14
+ <item name="colorPrimary">@color/everywhere_tint</item>
15
+ <item name="colorPrimaryVariant">@color/everywhere_tint</item>
16
+ <item name="colorSecondary">@color/everywhere_tint</item>
17
+ <item name="colorSecondaryVariant">@color/everywhere_tint</item>
18
+ <item name="colorAccent">@color/everywhere_tint</item>
19
+
20
+ <item name="android:colorBackground">@color/everywhere_background</item>
21
+ <item name="colorSurface">@color/everywhere_background</item>
22
+ <item name="colorError">@color/everywhere_error</item>
23
+
24
+ <item name="colorOnPrimary">@color/everywhere_on_tint</item>
25
+ <item name="colorOnSecondary">@color/everywhere_on_tint</item>
26
+ <item name="colorOnBackground">@color/everywhere_on_background</item>
27
+ <item name="colorOnSurface">@color/everywhere_on_background</item>
28
+ <item name="colorOnError">@color/everywhere_on_error</item>
29
+
30
+ <!-- Edge-to-edge: the bottom navigation applies the system-bar insets
31
+ itself (HotwireBottomNavigationController.applyWindowInsets). -->
32
+ <item name="android:statusBarColor">@color/everywhere_transparent</item>
33
+ <item name="android:navigationBarColor">@color/everywhere_transparent</item>
34
+ <item name="android:windowLightStatusBar">true</item>
35
+ <item name="android:windowLightNavigationBar" tools:ignore="NewApi">true</item>
36
+
37
+ <item name="elevationOverlayEnabled">false</item>
38
+ <item name="bottomNavigationStyle">@style/Widget.Everywhere.BottomNavigationView</item>
39
+ <item name="toolbarStyle">@style/Widget.Everywhere.Toolbar</item>
40
+ </style>
41
+
42
+ <!--
43
+ The Activity's launch theme. windowBackground is the stamped background
44
+ colour, so the system's starting window, the splash overlay in
45
+ activity_main.xml and the first rendered page are all the same colour and
46
+ the launch reads as one continuous surface. MainActivity swaps to
47
+ Theme.Everywhere in onCreate, before setContentView.
48
+ -->
49
+ <style name="Theme.Everywhere.Splash" parent="Theme.Everywhere">
50
+ <item name="android:windowBackground">@color/everywhere_background</item>
51
+ </style>
52
+
53
+ </resources>
@@ -0,0 +1,11 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!-- STAMPED — the dark half of appearance.tint_color / background_color. -->
3
+ <resources>
4
+ <color name="everywhere_tint">#7A75FF</color>
5
+ <color name="everywhere_background">#121212</color>
6
+
7
+ <color name="everywhere_on_tint">#000000</color>
8
+ <color name="everywhere_on_background">#FFFFFF</color>
9
+ <color name="everywhere_error">#CF6679</color>
10
+ <color name="everywhere_on_error">#000000</color>
11
+ </resources>
@@ -0,0 +1,33 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!--
3
+ The colours themselves come from values-night/colors.xml, so the only thing
4
+ that changes here is which system-bar icon set stays legible.
5
+ -->
6
+ <resources>
7
+ <style name="Theme.Everywhere" parent="Theme.MaterialComponents.DayNight.NoActionBar">
8
+ <item name="colorPrimary">@color/everywhere_tint</item>
9
+ <item name="colorPrimaryVariant">@color/everywhere_tint</item>
10
+ <item name="colorSecondary">@color/everywhere_tint</item>
11
+ <item name="colorSecondaryVariant">@color/everywhere_tint</item>
12
+ <item name="colorAccent">@color/everywhere_tint</item>
13
+
14
+ <item name="android:colorBackground">@color/everywhere_background</item>
15
+ <item name="colorSurface">@color/everywhere_background</item>
16
+ <item name="colorError">@color/everywhere_error</item>
17
+
18
+ <item name="colorOnPrimary">@color/everywhere_on_tint</item>
19
+ <item name="colorOnSecondary">@color/everywhere_on_tint</item>
20
+ <item name="colorOnBackground">@color/everywhere_on_background</item>
21
+ <item name="colorOnSurface">@color/everywhere_on_background</item>
22
+ <item name="colorOnError">@color/everywhere_on_error</item>
23
+
24
+ <item name="android:statusBarColor">@color/everywhere_transparent</item>
25
+ <item name="android:navigationBarColor">@color/everywhere_transparent</item>
26
+ <item name="android:windowLightStatusBar">false</item>
27
+ <item name="android:windowLightNavigationBar">false</item>
28
+
29
+ <item name="elevationOverlayEnabled">false</item>
30
+ <item name="bottomNavigationStyle">@style/Widget.Everywhere.BottomNavigationView</item>
31
+ <item name="toolbarStyle">@style/Widget.Everywhere.Toolbar</item>
32
+ </style>
33
+ </resources>
@@ -0,0 +1,12 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!--
3
+ Release baseline: no cleartext, anywhere. The debug build type overlays
4
+ src/debug/res/xml/network_security_config.xml over this file (same resource
5
+ name, build-type source set wins), which opens up loopback and the emulator's
6
+ host alias for `every dev` for Android. Overlaying the resource rather than the
7
+ manifest attribute keeps src/main/AndroidManifest.xml frozen and leaves the
8
+ build type's manifest slot free for src/stamped/AndroidManifest.xml.
9
+ -->
10
+ <network-security-config>
11
+ <base-config cleartextTrafficPermitted="false" />
12
+ </network-security-config>
@@ -0,0 +1,34 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!--
3
+ GENERATED by `every build` for Android. The checked-in copy is empty so the raw
4
+ template builds unchanged; the CLI rewrites it whenever everywhere.yml
5
+ declares `deep_linking:`, `auth:` or `permissions:`.
6
+
7
+ It is applied as BOTH build types' manifest (see app/build.gradle.kts) rather
8
+ than being folded into the main manifest, so the frozen file above never
9
+ needs editing — the analogue of iOS's optional App.entitlements.
10
+
11
+ Generated content looks like:
12
+
13
+ <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
14
+
15
+ <application>
16
+ <activity android:name="com.rubyeverywhere.shell.MainActivity">
17
+ <intent-filter android:autoVerify="true">
18
+ <action android:name="android.intent.action.VIEW" />
19
+ <category android:name="android.intent.category.DEFAULT" />
20
+ <category android:name="android.intent.category.BROWSABLE" />
21
+ <data android:scheme="https" android:host="example.com" />
22
+ </intent-filter>
23
+ <intent-filter>
24
+ <action android:name="android.intent.action.VIEW" />
25
+ <category android:name="android.intent.category.DEFAULT" />
26
+ <category android:name="android.intent.category.BROWSABLE" />
27
+ <data android:scheme="com.example.app" />
28
+ </intent-filter>
29
+ </activity>
30
+ </application>
31
+ -->
32
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android">
33
+ <application />
34
+ </manifest>
@@ -0,0 +1,8 @@
1
+ // Top-level build file. Plugin versions are pinned here, once, for every
2
+ // module — the analogue of the iOS template's committed `Package.resolved`.
3
+ // Moving a pin is a deliberate edit to this template, never to a stamped copy.
4
+ plugins {
5
+ id("com.android.application") version "8.13.2" apply false
6
+ id("org.jetbrains.kotlin.android") version "2.3.0" apply false
7
+ id("org.jetbrains.kotlin.plugin.serialization") version "2.3.0" apply false
8
+ }
@@ -0,0 +1,10 @@
1
+ distributionBase=GRADLE_USER_HOME
2
+ distributionPath=wrapper/dists
3
+ # `-bin`, not `-all`: the `-all` distribution unpacks to 522 MB in
4
+ # GRADLE_USER_HOME and the extra 500 MB is Gradle's own sources and javadoc,
5
+ # which only an IDE indexing the Gradle API ever reads. The CLI never does.
6
+ distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.0-bin.zip
7
+ networkTimeout=10000
8
+ validateDistributionUrl=true
9
+ zipStoreBase=GRADLE_USER_HOME
10
+ zipStorePath=wrapper/dists
@@ -0,0 +1,14 @@
1
+ # Frozen build settings. Nothing per-app lives here — see app/everywhere.properties.
2
+
3
+ org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
4
+ org.gradle.caching=true
5
+
6
+ # Off deliberately. The configuration cache serialises the whole build model,
7
+ # and `app/build.gradle.kts` reads `everywhere.properties` at configuration
8
+ # time — a file the CLI rewrites on every build. Gradle would have to
9
+ # invalidate the cache each run anyway, so it buys nothing and hides staleness.
10
+ org.gradle.configuration-cache=false
11
+
12
+ android.useAndroidX=true
13
+ android.nonTransitiveRClass=true
14
+ kotlin.code.style=official