feat: give window closing its own Pencereler tab
The window switch sat on the Upload Service page, which implied a relationship that does not exist: closing windows needs no ABRP token, no GPS, no upload interval, and runs in its own service whether or not uploading is on. It is now a sixth page of the pager, between Araç and Wi-Fi. Splitting it in two while it moved, because the new page has room the card did not: the switch and status stay together, and the event log gets its own card, monospaced and no longer capped at four lines. setOffscreenPageLimit switches from a hardcoded 4 to panes.length - 1. It was already one short of the page count and would have gone stale again here. refreshWindowClose runs on the two-second tick and dereferences views on a page that may not be on screen, so the pager must not be free to detach them. Six tabs share the bar where five did. Widget.EV.NavTab already autosizes down to a 12sp floor, so the labels shrink rather than truncate. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
444732438d
commit
322ebcc4c0
@@ -136,9 +136,10 @@ put them in a text file and tap **Import file** — see [Config file](#config-fi
|
|||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
On the **Upload Service** page, **Kapı açılınca camları kapat** enables the WinClose
|
On its own **Pencereler** page — not under Upload Service, since it shares nothing with
|
||||||
integration independently of ABRP. It is off by default and restarts with the head unit
|
ABRP — **Kapı açılınca camları kapat** enables the WinClose integration. It is off by
|
||||||
once enabled; no ABRP token, GPS permission, speed threshold or driving timer is needed.
|
default and restarts with the head unit once enabled; no ABRP token, GPS permission,
|
||||||
|
speed threshold or driving timer is needed.
|
||||||
|
|
||||||
The integration is adapted from the sibling WinClose `WindowHardware.kt`: SWI69
|
The integration is adapted from the sibling WinClose `WindowHardware.kt`: SWI69
|
||||||
`CarStateClient` door callback (transaction 6, value 0) triggers the VSM's
|
`CarStateClient` door callback (transaction 6, value 0) triggers the VSM's
|
||||||
|
|||||||
@@ -46,7 +46,8 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
|
|
||||||
/** Top-bar tabs, in page order. Parallel to {@link #panes}. */
|
/** Top-bar tabs, in page order. Parallel to {@link #panes}. */
|
||||||
private static final int[] TAB_IDS =
|
private static final int[] TAB_IDS =
|
||||||
{ R.id.tabAbrp, R.id.tabService, R.id.tabVehicle, R.id.tabWifi, R.id.tabLog };
|
{ R.id.tabAbrp, R.id.tabService, R.id.tabVehicle, R.id.tabWindows, R.id.tabWifi,
|
||||||
|
R.id.tabLog };
|
||||||
|
|
||||||
// Status colours come from the palette, not from the Material swatches: #4CAF50 and
|
// Status colours come from the palette, not from the Material swatches: #4CAF50 and
|
||||||
// #F44336 sit around 4:1 on this background, which disappears behind a sunlit
|
// #F44336 sit around 4:1 on this background, which disappears behind a sunlit
|
||||||
@@ -79,9 +80,10 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
private View servicePane;
|
private View servicePane;
|
||||||
private View logPane;
|
private View logPane;
|
||||||
private View vehiclePane;
|
private View vehiclePane;
|
||||||
|
private View windowsPane;
|
||||||
private View wifiPane;
|
private View wifiPane;
|
||||||
|
|
||||||
/** The five pages, in the order the top bar lists them. Parallel to {@link #TAB_IDS}. */
|
/** The six pages, in the order the top bar lists them. Parallel to {@link #TAB_IDS}. */
|
||||||
private View[] panes;
|
private View[] panes;
|
||||||
|
|
||||||
/** Wi-Fi page. The helper is the same one the upload service uses on its own tick. */
|
/** Wi-Fi page. The helper is the same one the upload service uses on its own tick. */
|
||||||
@@ -118,7 +120,7 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
* Wires the top bar to the pager: every tab is a page, and every page is a tab.
|
* Wires the top bar to the pager: every tab is a page, and every page is a tab.
|
||||||
*
|
*
|
||||||
* Each page has its own view type, so the pager asks for it once and keeps it: these
|
* Each page has its own view type, so the pager asks for it once and keeps it: these
|
||||||
* three views are the activity's own, held in {@link #panes}, not rows to be recycled.
|
* views are the activity's own, held in {@link #panes}, not rows to be recycled.
|
||||||
*/
|
*/
|
||||||
private void setUpPager() {
|
private void setUpPager() {
|
||||||
ViewPager2 pager = findViewById(R.id.content);
|
ViewPager2 pager = findViewById(R.id.content);
|
||||||
@@ -138,9 +140,11 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
// state, exactly as they did when they were siblings in the layout.
|
// state, exactly as they did when they were siblings in the layout.
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// All four stay alive. They are cheap, and the service switch, the call log and
|
// All of them stay alive. They are cheap, and the service switch, the call log, the
|
||||||
// the vehicle readings are refreshed by the tick whether or not their page is on screen.
|
// window-close status and the vehicle readings are refreshed by the tick whether or
|
||||||
pager.setOffscreenPageLimit(4);
|
// not their page is on screen — refreshWindowClose in particular dereferences views
|
||||||
|
// the pager would otherwise be free to detach.
|
||||||
|
pager.setOffscreenPageLimit(panes.length - 1);
|
||||||
pager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
|
pager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
|
||||||
@Override public void onPageSelected(int position) { markCurrentPage(position); }
|
@Override public void onPageSelected(int position) { markCurrentPage(position); }
|
||||||
});
|
});
|
||||||
@@ -210,9 +214,10 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
abrpPane = inflatePane(R.layout.pane_abrp);
|
abrpPane = inflatePane(R.layout.pane_abrp);
|
||||||
servicePane = inflatePane(R.layout.pane_service);
|
servicePane = inflatePane(R.layout.pane_service);
|
||||||
vehiclePane = inflatePane(R.layout.pane_vehicle);
|
vehiclePane = inflatePane(R.layout.pane_vehicle);
|
||||||
|
windowsPane = inflatePane(R.layout.pane_windows);
|
||||||
wifiPane = inflatePane(R.layout.pane_wifi);
|
wifiPane = inflatePane(R.layout.pane_wifi);
|
||||||
logPane = inflatePane(R.layout.pane_log);
|
logPane = inflatePane(R.layout.pane_log);
|
||||||
panes = new View[] { abrpPane, servicePane, vehiclePane, wifiPane, logPane };
|
panes = new View[] { abrpPane, servicePane, vehiclePane, windowsPane, wifiPane, logPane };
|
||||||
|
|
||||||
bindVehiclePane();
|
bindVehiclePane();
|
||||||
bindWifiPane();
|
bindWifiPane();
|
||||||
@@ -326,9 +331,9 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void bindWindowClose() {
|
private void bindWindowClose() {
|
||||||
windowCloseSwitch = servicePane.findViewById(R.id.window_close_switch);
|
windowCloseSwitch = windowsPane.findViewById(R.id.window_close_switch);
|
||||||
windowCloseStatus = servicePane.findViewById(R.id.window_close_status);
|
windowCloseStatus = windowsPane.findViewById(R.id.window_close_status);
|
||||||
windowCloseLog = servicePane.findViewById(R.id.window_close_log);
|
windowCloseLog = windowsPane.findViewById(R.id.window_close_log);
|
||||||
windowCloseListener = (button, enabled) -> {
|
windowCloseListener = (button, enabled) -> {
|
||||||
prefs.edit().putBoolean(WindowCloseService.KEY_ENABLED, enabled).apply();
|
prefs.edit().putBoolean(WindowCloseService.KEY_ENABLED, enabled).apply();
|
||||||
if (enabled) WindowCloseService.startIfEnabled(this);
|
if (enabled) WindowCloseService.startIfEnabled(this);
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- A car door with its glass, for the Windows page. Filled to match the other tab icons,
|
||||||
|
which read at a glance on a vehicle screen rather than rewarding a close look. -->
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24"
|
||||||
|
android:tint="?attr/colorControlNormal">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M20,10.5l-1.6,-4.6C18.1,5.1 17.4,4.5 16.5,4.5H8.2C7.5,4.5 6.9,4.9 6.6,5.5L3.2,12.2C3.1,12.4 3,12.7 3,13v5c0,0.8 0.7,1.5 1.5,1.5h14c0.8,0 1.5,-0.7 1.5,-1.5v-7.5zM8.4,6.5h8.1l1.4,4H6.3l2.1,-4zM6,17c-0.8,0 -1.5,-0.7 -1.5,-1.5S5.2,14 6,14s1.5,0.7 1.5,1.5S6.8,17 6,17zM18,16.5h-8v-2h8v2z" />
|
||||||
|
</vector>
|
||||||
@@ -72,6 +72,16 @@
|
|||||||
android:text="@string/tab_vehicle"
|
android:text="@string/tab_vehicle"
|
||||||
app:icon="@drawable/ic_tab_vehicle" />
|
app:icon="@drawable/ic_tab_vehicle" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/tabWindows"
|
||||||
|
style="@style/Widget.EV.NavTab"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="64dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:layout_marginEnd="@dimen/spacing_sm"
|
||||||
|
android:text="@string/tab_windows"
|
||||||
|
app:icon="@drawable/ic_tab_windows" />
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<com.google.android.material.button.MaterialButton
|
||||||
android:id="@+id/tabWifi"
|
android:id="@+id/tabWifi"
|
||||||
style="@style/Widget.EV.NavTab"
|
style="@style/Widget.EV.NavTab"
|
||||||
|
|||||||
@@ -12,54 +12,6 @@
|
|||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:padding="@dimen/spacing_lg">
|
android:padding="@dimen/spacing_lg">
|
||||||
|
|
||||||
<com.google.android.material.card.MaterialCardView
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginBottom="@dimen/spacing_md"
|
|
||||||
app:cardBackgroundColor="@color/ev_surface"
|
|
||||||
app:cardCornerRadius="@dimen/ev_corner"
|
|
||||||
app:strokeColor="@color/ev_outline"
|
|
||||||
app:strokeWidth="1dp"
|
|
||||||
app:cardElevation="0dp">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:padding="@dimen/spacing_lg">
|
|
||||||
|
|
||||||
<com.google.android.material.switchmaterial.SwitchMaterial
|
|
||||||
android:id="@+id/window_close_switch"
|
|
||||||
style="@style/Widget.EV.Switch"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:minHeight="@dimen/touch_target"
|
|
||||||
android:text="@string/window_close_title" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="@string/window_close_hint"
|
|
||||||
android:textAppearance="@style/Text.EV.Secondary" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/window_close_status"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="@dimen/spacing_sm"
|
|
||||||
android:text="@string/window_close_stopped"
|
|
||||||
android:textAppearance="@style/Text.EV.Body" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/window_close_log"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="@dimen/spacing_sm"
|
|
||||||
android:maxLines="4"
|
|
||||||
android:textAppearance="@style/Text.EV.Secondary" />
|
|
||||||
</LinearLayout>
|
|
||||||
</com.google.android.material.card.MaterialCardView>
|
|
||||||
|
|
||||||
<!-- Service control card -->
|
<!-- Service control card -->
|
||||||
<com.google.android.material.card.MaterialCardView
|
<com.google.android.material.card.MaterialCardView
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|||||||
@@ -0,0 +1,92 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Windows page of the main pager. See pane_abrp.xml for how the pages are held.
|
||||||
|
|
||||||
|
Its own page rather than a card on the Service one: this feature writes to the car and
|
||||||
|
shares nothing with ABRP uploading — no token, no GPS, no interval. Sitting under
|
||||||
|
"Upload Service" implied a dependency that does not exist. -->
|
||||||
|
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:fillViewport="true">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="@dimen/spacing_lg">
|
||||||
|
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="@dimen/spacing_md"
|
||||||
|
app:cardBackgroundColor="@color/ev_surface"
|
||||||
|
app:cardCornerRadius="@dimen/ev_corner"
|
||||||
|
app:strokeColor="@color/ev_outline"
|
||||||
|
app:strokeWidth="1dp"
|
||||||
|
app:cardElevation="0dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="@dimen/spacing_lg">
|
||||||
|
|
||||||
|
<com.google.android.material.switchmaterial.SwitchMaterial
|
||||||
|
android:id="@+id/window_close_switch"
|
||||||
|
style="@style/Widget.EV.Switch"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:minHeight="@dimen/touch_target"
|
||||||
|
android:text="@string/window_close_title" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/window_close_hint"
|
||||||
|
android:textAppearance="@style/Text.EV.Secondary" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/window_close_status"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="@dimen/spacing_sm"
|
||||||
|
android:text="@string/window_close_stopped"
|
||||||
|
android:textAppearance="@style/Text.EV.Body" />
|
||||||
|
</LinearLayout>
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
|
<!-- Event log. Its own card here, where there is room for more than the four lines
|
||||||
|
it had to fit in beside the upload settings. -->
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:cardBackgroundColor="@color/ev_surface"
|
||||||
|
app:cardCornerRadius="@dimen/ev_corner"
|
||||||
|
app:strokeColor="@color/ev_outline"
|
||||||
|
app:strokeWidth="1dp"
|
||||||
|
app:cardElevation="0dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="@dimen/spacing_lg">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/window_close_log_title"
|
||||||
|
android:textAppearance="@style/Text.EV.Body" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/window_close_log"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="@dimen/spacing_sm"
|
||||||
|
android:fontFamily="monospace"
|
||||||
|
android:textAppearance="@style/Text.EV.Secondary" />
|
||||||
|
</LinearLayout>
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
</LinearLayout>
|
||||||
|
</ScrollView>
|
||||||
@@ -12,12 +12,14 @@
|
|||||||
<string name="window_close_no_system_uid">Bu sürüm araca yazamıyor (sistem imzası gerekli)</string>
|
<string name="window_close_no_system_uid">Bu sürüm araca yazamıyor (sistem imzası gerekli)</string>
|
||||||
<string name="window_close_notification">P ve 0 km/sa: kapı açılınca dört camı kapat</string>
|
<string name="window_close_notification">P ve 0 km/sa: kapı açılınca dört camı kapat</string>
|
||||||
<string name="window_close_disable">Kapat</string>
|
<string name="window_close_disable">Kapat</string>
|
||||||
|
<string name="window_close_log_title">Olay kaydı</string>
|
||||||
<string name="app_name">Fatih\'sMG4</string>
|
<string name="app_name">Fatih\'sMG4</string>
|
||||||
|
|
||||||
<!-- Main screen -->
|
<!-- Main screen -->
|
||||||
<string name="tab_abrp">ABRP</string>
|
<string name="tab_abrp">ABRP</string>
|
||||||
<string name="tab_service">Upload Service</string>
|
<string name="tab_service">Upload Service</string>
|
||||||
<string name="tab_vehicle">Araç</string>
|
<string name="tab_vehicle">Araç</string>
|
||||||
|
<string name="tab_windows">Pencereler</string>
|
||||||
<string name="tab_wifi">Wi-Fi</string>
|
<string name="tab_wifi">Wi-Fi</string>
|
||||||
<string name="tab_log">Log</string>
|
<string name="tab_log">Log</string>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user