Remove local suppression store
This commit is contained in:
@@ -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`).
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
// Invalidates the previously prepared voice menu now that we can properly
|
||||||
|
// prepare it.
|
||||||
getWindow().invalidatePanelMenu(WindowUtils.FEATURE_VOICE_COMMANDS);
|
getWindow().invalidatePanelMenu(WindowUtils.FEATURE_VOICE_COMMANDS);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
// Open the options menu for the touch flow.
|
||||||
openOptionsMenu();
|
openOptionsMenu();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isMyMenu(int featureId) {
|
|
||||||
return featureId == Window.FEATURE_OPTIONS_PANEL || featureId == WindowUtils.FEATURE_VOICE_COMMANDS;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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) {
|
||||||
|
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);
|
||||||
|
|||||||
@@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user