Remove local suppression store

This commit is contained in:
2026-01-10 16:27:09 +00:00
parent 4010ba8870
commit f463bae19c
5 changed files with 42 additions and 68 deletions

View File

@@ -20,7 +20,7 @@ Logcat tags:
## Using the feed UI ## Using the feed UI
- Tap the pinned LiveCard to open `FeedActivity`. - Tap the pinned LiveCard to open `FeedActivity`.
- Swipe left/right to move through cards. - Swipe left/right to move through cards.
- Tap a card to open its actions (Glass-style overlay); `DISMISS` / `SNOOZE_*` are stored locally and stay hidden across restarts. - Tap a card to open its actions (Glass-style overlay); `DISMISS` / `SNOOZE_*` remove the card from the current view and rely on the companion feed to keep it suppressed.
## FYI static cards (Weather) ## FYI static cards (Weather)
If the feed includes a card with `bucket="FYI"` and a `type` that equals `WEATHER_INFO` or contains `WEATHER` (e.g. `CURRENT_WEATHER`), the app publishes it as a Glass “static card” via an Android notification using a custom `RemoteViews` layout modeled after the “Google Now Weather” card style (see `app/src/main/res/layout/weather_static_card.xml` and `app/src/main/java/sh/nym/irisglass/WeatherStaticCardPublisher.java`). If the feed includes a card with `bucket="FYI"` and a `type` that equals `WEATHER_INFO` or contains `WEATHER` (e.g. `CURRENT_WEATHER`), the app publishes it as a Glass “static card” via an Android notification using a custom `RemoteViews` layout modeled after the “Google Now Weather” card style (see `app/src/main/res/layout/weather_static_card.xml` and `app/src/main/java/sh/nym/irisglass/WeatherStaticCardPublisher.java`).

View File

@@ -17,7 +17,6 @@ public final class FeedActivity extends Activity {
private CardScrollView cardScrollView; private CardScrollView cardScrollView;
private FeedAdapter adapter; private FeedAdapter adapter;
private SuppressionStore suppressionStore;
private FeedItem selectedItem; private FeedItem selectedItem;
private final HudState.Listener hudListener = new HudState.Listener() { private final HudState.Listener hudListener = new HudState.Listener() {
@@ -37,8 +36,6 @@ public final class FeedActivity extends Activity {
requestWindowFeature(Window.FEATURE_NO_TITLE); requestWindowFeature(Window.FEATURE_NO_TITLE);
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
suppressionStore = new SuppressionStore(this);
cardScrollView = new CardScrollView(this); cardScrollView = new CardScrollView(this);
adapter = new FeedAdapter(this, makeWaitingCard()); adapter = new FeedAdapter(this, makeWaitingCard());
cardScrollView.setAdapter(adapter); cardScrollView.setAdapter(adapter);
@@ -85,15 +82,11 @@ public final class FeedActivity extends Activity {
String action = data.getStringExtra(MenuActivity.EXTRA_ACTION); String action = data.getStringExtra(MenuActivity.EXTRA_ACTION);
if (cardId == null || action == null) return; if (cardId == null || action == null) return;
long now = System.currentTimeMillis() / 1000L;
if ("DISMISS".equals(action)) { if ("DISMISS".equals(action)) {
suppressionStore.setSuppressed(cardId, now + (10L * 365L * 24L * 60L * 60L));
adapter.removeById(cardId); adapter.removeById(cardId);
} else if ("SNOOZE_2H".equals(action)) { } else if ("SNOOZE_2H".equals(action)) {
suppressionStore.setSuppressed(cardId, now + 2L * 60L * 60L);
adapter.removeById(cardId); adapter.removeById(cardId);
} else if ("SNOOZE_24H".equals(action)) { } else if ("SNOOZE_24H".equals(action)) {
suppressionStore.setSuppressed(cardId, now + 24L * 60L * 60L);
adapter.removeById(cardId); adapter.removeById(cardId);
} else if ("SAVE".equals(action)) { } else if ("SAVE".equals(action)) {
Toast.makeText(this, "Saved", Toast.LENGTH_SHORT).show(); Toast.makeText(this, "Saved", Toast.LENGTH_SHORT).show();
@@ -125,12 +118,10 @@ public final class FeedActivity extends Activity {
List<FeedItem> items = env.activeItems(); List<FeedItem> items = env.activeItems();
if (items.isEmpty()) return makeWaitingCard(); if (items.isEmpty()) return makeWaitingCard();
long now = System.currentTimeMillis() / 1000L;
ArrayList<FeedItem> filtered = new ArrayList<FeedItem>(); ArrayList<FeedItem> filtered = new ArrayList<FeedItem>();
for (int i = 0; i < items.size(); i++) { for (int i = 0; i < items.size(); i++) {
FeedItem it = items.get(i); FeedItem it = items.get(i);
if (it == null) continue; if (it == null) continue;
if (it.id != null && suppressionStore.isSuppressed(it.id, now)) continue;
filtered.add(it); filtered.add(it);
} }
if (filtered.isEmpty()) return makeWaitingCard(); if (filtered.isEmpty()) return makeWaitingCard();

View File

@@ -9,11 +9,9 @@ import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.view.Menu; import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.Window; import android.view.Window;
import android.view.WindowManager;
import java.util.HashSet; import java.util.HashSet;
@@ -33,9 +31,10 @@ public final class MenuActivity extends Activity {
private boolean isMenuClosed; private boolean isMenuClosed;
private boolean preparePanelCalled; private boolean preparePanelCalled;
private boolean fromLiveCardVoice; private boolean fromLiveCardVoice;
private boolean actionsReady;
private String cardId; private String cardId;
private HashSet<String> allowedActions = new HashSet<String>(); private final HashSet<String> allowedActions = new HashSet<String>();
public static Intent newIntent(Context context, FeedItem item) { public static Intent newIntent(Context context, FeedItem item) {
Intent i = new Intent(context, MenuActivity.class); Intent i = new Intent(context, MenuActivity.class);
@@ -53,15 +52,6 @@ public final class MenuActivity extends Activity {
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
// Increase contrast so menu labels are readable over underlying cards.
try {
WindowManager.LayoutParams lp = getWindow().getAttributes();
lp.flags |= WindowManager.LayoutParams.FLAG_DIM_BEHIND;
lp.dimAmount = 0.65f;
getWindow().setAttributes(lp);
} catch (Throwable ignored) {
}
fromLiveCardVoice = getIntent().getBooleanExtra(LiveCard.EXTRA_FROM_LIVECARD_VOICE, false); fromLiveCardVoice = getIntent().getBooleanExtra(LiveCard.EXTRA_FROM_LIVECARD_VOICE, false);
if (fromLiveCardVoice) { if (fromLiveCardVoice) {
getWindow().requestFeature(WindowUtils.FEATURE_VOICE_COMMANDS); getWindow().requestFeature(WindowUtils.FEATURE_VOICE_COMMANDS);
@@ -74,6 +64,8 @@ public final class MenuActivity extends Activity {
if (actions[i] != null) allowedActions.add(actions[i]); if (actions[i] != null) allowedActions.add(actions[i]);
} }
} }
actionsReady = true;
openMenu();
} }
@Override @Override
@@ -92,8 +84,7 @@ public final class MenuActivity extends Activity {
@Override @Override
public boolean onCreatePanelMenu(int featureId, Menu menu) { public boolean onCreatePanelMenu(int featureId, Menu menu) {
if (isMyMenu(featureId)) { if (isMyMenu(featureId)) {
MenuInflater inflater = getMenuInflater(); getMenuInflater().inflate(R.menu.feed_actions, menu);
inflater.inflate(R.menu.feed_actions, menu);
return true; return true;
} }
return super.onCreatePanelMenu(featureId, menu); return super.onCreatePanelMenu(featureId, menu);
@@ -103,6 +94,9 @@ public final class MenuActivity extends Activity {
public boolean onPreparePanel(int featureId, View view, Menu menu) { public boolean onPreparePanel(int featureId, View view, Menu menu) {
preparePanelCalled = true; preparePanelCalled = true;
if (isMyMenu(featureId)) { if (isMyMenu(featureId)) {
if (!actionsReady) {
return false;
}
// Enable only actions present on the tapped card. // Enable only actions present on the tapped card.
setOptionsMenuState(menu.findItem(R.id.action_dismiss), allowedActions.contains("DISMISS")); setOptionsMenuState(menu.findItem(R.id.action_dismiss), allowedActions.contains("DISMISS"));
setOptionsMenuState(menu.findItem(R.id.action_snooze_2h), allowedActions.contains("SNOOZE_2H")); setOptionsMenuState(menu.findItem(R.id.action_snooze_2h), allowedActions.contains("SNOOZE_2H"));
@@ -110,6 +104,8 @@ public final class MenuActivity extends Activity {
setOptionsMenuState(menu.findItem(R.id.action_save), allowedActions.contains("SAVE")); setOptionsMenuState(menu.findItem(R.id.action_save), allowedActions.contains("SAVE"));
// Always allow Back. // Always allow Back.
setOptionsMenuState(menu.findItem(R.id.action_back), true); setOptionsMenuState(menu.findItem(R.id.action_back), true);
// Don't reopen menu once we are finishing. This is necessary
// since voice menus reopen themselves while in focus.
return !isMenuClosed; return !isMenuClosed;
} }
return super.onPreparePanel(featureId, view, menu); return super.onPreparePanel(featureId, view, menu);
@@ -129,7 +125,7 @@ public final class MenuActivity extends Activity {
} }
// Post for proper options menu animation (per timer sample guidance). // Post for proper options menu animation (per timer sample guidance).
handler.post(new Runnable() { post(new Runnable() {
@Override @Override
public void run() { public void run() {
Intent r = new Intent(); Intent r = new Intent();
@@ -151,21 +147,43 @@ public final class MenuActivity extends Activity {
} }
} }
/**
* Posts a {@link Runnable} at the end of the message loop, overridable for testing.
*/
protected void post(Runnable runnable) {
handler.post(runnable);
}
/**
* Opens the touch or voice menu iff all the conditions are satisfied.
*/
private void openMenu() { private void openMenu() {
if (!attachedToWindow) return; if (attachedToWindow && actionsReady) {
if (fromLiveCardVoice) { if (fromLiveCardVoice) {
if (preparePanelCalled) { if (preparePanelCalled) {
getWindow().invalidatePanelMenu(WindowUtils.FEATURE_VOICE_COMMANDS); // Invalidates the previously prepared voice menu now that we can properly
// prepare it.
getWindow().invalidatePanelMenu(WindowUtils.FEATURE_VOICE_COMMANDS);
}
} else {
// Open the options menu for the touch flow.
openOptionsMenu();
} }
} else {
openOptionsMenu();
} }
} }
/**
* Returns {@code true} when the {@code featureId} belongs to the options menu or voice
* menu that are controlled by this menu activity.
*/
private boolean isMyMenu(int featureId) { private boolean isMyMenu(int featureId) {
return featureId == Window.FEATURE_OPTIONS_PANEL || featureId == WindowUtils.FEATURE_VOICE_COMMANDS; return featureId == Window.FEATURE_OPTIONS_PANEL
|| featureId == WindowUtils.FEATURE_VOICE_COMMANDS;
} }
/**
* Sets a {@code MenuItem} visible and enabled state.
*/
private static void setOptionsMenuState(MenuItem menuItem, boolean enabled) { private static void setOptionsMenuState(MenuItem menuItem, boolean enabled) {
if (menuItem == null) return; if (menuItem == null) return;
menuItem.setVisible(enabled); menuItem.setVisible(enabled);

View File

@@ -1,32 +0,0 @@
package sh.nym.irisglass;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
public final class SuppressionStore {
private static final String KEY_PREFIX = "suppression_until_";
private final SharedPreferences prefs;
public SuppressionStore(Context context) {
this.prefs = PreferenceManager.getDefaultSharedPreferences(context.getApplicationContext());
}
public boolean isSuppressed(String id, long nowEpochSeconds) {
if (id == null) return false;
long until = prefs.getLong(KEY_PREFIX + id, 0L);
if (until <= 0L) return false;
if (until <= nowEpochSeconds) {
prefs.edit().remove(KEY_PREFIX + id).apply();
return false;
}
return true;
}
public void setSuppressed(String id, long untilEpochSeconds) {
if (id == null) return;
prefs.edit().putLong(KEY_PREFIX + id, untilEpochSeconds).apply();
}
}

View File

@@ -8,13 +8,10 @@
</style> </style>
<!-- Matches the GDK timer sample MenuTheme to get the correct Glass menu overlay behavior. --> <!-- Matches the GDK timer sample MenuTheme to get the correct Glass menu overlay behavior. -->
<style name="Theme.IrisGlass.MenuOverlay" parent="@android:style/Theme.Holo"> <style name="Theme.IrisGlass.MenuOverlay" parent="@android:style/Theme.DeviceDefault">
<item name="android:windowBackground">@android:color/transparent</item> <item name="android:windowBackground">@android:color/transparent</item>
<item name="android:colorBackgroundCacheHint">@null</item> <item name="android:colorBackgroundCacheHint">@null</item>
<item name="android:windowIsTranslucent">true</item> <item name="android:windowIsTranslucent">true</item>
<item name="android:windowAnimationStyle">@null</item> <item name="android:windowAnimationStyle">@null</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowActionBar">false</item>
<item name="android:windowContentOverlay">@null</item>
</style> </style>
</resources> </resources>