mobile: reminders widget for android

This commit is contained in:
Ammar Ahmed
2025-01-16 15:50:03 +05:00
parent d6be134d53
commit b232174fc3
19 changed files with 582 additions and 30 deletions

View File

@@ -39,8 +39,7 @@ import SettingsService from "./services/settings";
import { TipManager } from "./services/tip-manager";
import { useThemeStore } from "./stores/use-theme-store";
import { useUserStore } from "./stores/use-user-store";
import { NotesnookModule } from "./utils/notesnook-module";
import { setAppState } from "./screens/editor/tiptap/utils";
import { IntentService } from "./services/intent";
I18nManager.allowRTL(false);
I18nManager.forceRTL(false);
@@ -49,16 +48,8 @@ const { appLockEnabled, appLockMode } = SettingsService.get();
if (appLockEnabled || appLockMode !== "none") {
useUserStore.getState().lockApp(true);
}
const launchIntent = NotesnookModule.getIntent();
if (launchIntent["com.streetwriters.notesnook.OpenNoteId"]) {
setAppState({
movedAway: false,
editing: true,
timestamp: Date.now(),
noteId: launchIntent["com.streetwriters.notesnook.OpenNoteId"]
});
}
IntentService.onLaunch();
const App = (props: { configureMode: "note-preview" }) => {
useAppEvents();
//@ts-ignore

View File

@@ -66,6 +66,7 @@ import {
eSubscribeEvent,
presentSheet
} from "../services/event-manager";
import { IntentService } from "../services/intent";
import {
clearMessage,
setEmailVerifyMessage,
@@ -698,19 +699,8 @@ export const useAppEvents = () => {
}
setTimeout(async () => {
const intent = NotesnookModule.getIntent();
if (intent["com.streetwriters.notesnook.OpenNoteId"]) {
const note = await db.notes.note(
intent["com.streetwriters.notesnook.OpenNoteId"]
);
if (note) {
eSendEvent(eOnLoadNote, {
item: note
});
tabBarRef.current?.goToPage(1, false);
}
}
}, 50);
IntentService.onAppStateChanged();
}, 100);
} else {
await saveEditorState();
if (

View File

@@ -45,6 +45,9 @@ import { useSelectionStore } from "../stores/use-selection-store";
import { useSettingStore } from "../stores/use-setting-store";
import { rootNavigatorRef } from "../utils/global-refs";
import { strings } from "@notesnook/intl";
import { IntentService } from "../services/intent";
import ReminderSheet from "../components/sheets/reminder";
import { db } from "../common/database";
const NativeStack = createNativeStackNavigator();
const IntroStack = createNativeStackNavigator();
@@ -76,12 +79,24 @@ const _Tabs = () => {
const introCompleted = useSettingStore(
(state) => state.settings.introCompleted
);
const height = useSettingStore((state) => state.dimensions.height);
const insets = useGlobalSafeAreaInsets();
const screenHeight = height - (50 + insets.top + insets.bottom);
React.useEffect(() => {
setTimeout(() => {
setTimeout(async () => {
useNavigationStore.getState().update(homepage);
const intent = IntentService.getLaunchIntent();
if (intent && intent["com.streetwriters.notesnook.OpenReminderId"]) {
const reminder = await db.reminders.reminder(
intent["com.streetwriters.notesnook.OpenReminderId"]
);
if (reminder) {
ReminderSheet.present(reminder);
}
} else if (intent["com.streetwriters.notesnook.NewReminder"]) {
ReminderSheet.present();
}
}, 1000);
}, [homepage]);

View File

@@ -0,0 +1,69 @@
/*
This file is part of the Notesnook project (https://notesnook.com/)
Copyright (C) 2023 Streetwriters (Private) Limited
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { db } from "../common/database";
import ReminderSheet from "../components/sheets/reminder";
import { setAppState } from "../screens/editor/tiptap/utils";
import { eOnLoadNote } from "../utils/events";
import { tabBarRef } from "../utils/global-refs";
import { NotesnookModule } from "../utils/notesnook-module";
import { eSendEvent } from "./event-manager";
const launchIntent = NotesnookModule.getIntent();
let used = false;
let launched = false;
export const IntentService = {
getLaunchIntent() {
if (used) return null;
used = true;
return launchIntent;
},
onLaunch() {
if (launched) return;
launched = true;
if (launchIntent["com.streetwriters.notesnook.OpenNoteId"]) {
setAppState({
movedAway: false,
editing: true,
timestamp: Date.now(),
noteId: launchIntent["com.streetwriters.notesnook.OpenNoteId"]
});
}
},
async onAppStateChanged() {
const intent = NotesnookModule.getIntent();
if (intent["com.streetwriters.notesnook.OpenNoteId"]) {
const note = await db.notes.note(
intent["com.streetwriters.notesnook.OpenNoteId"]
);
if (note) {
eSendEvent(eOnLoadNote, {
item: note
});
tabBarRef.current?.goToPage(1, false);
}
} else if (intent["com.streetwriters.notesnook.OpenReminderId"]) {
const reminder = await db.reminders.reminder(
intent["com.streetwriters.notesnook.OpenReminderId"]
);
if (reminder) ReminderSheet.present(reminder);
} else if (intent["com.streetwriters.notesnook.NewReminder"]) {
ReminderSheet.present();
}
}
};

View File

@@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { Reminder } from "@notesnook/core";
import { isReminderActive, Reminder } from "@notesnook/core";
import { strings } from "@notesnook/intl";
import notifee, {
AndroidStyle,
@@ -51,6 +51,7 @@ import { DDS } from "./device-detection";
import { eSendEvent } from "./event-manager";
import Navigation from "./navigation";
import SettingsService from "./settings";
import { getFormattedReminderTime } from "@notesnook/common";
let pinned: DisplayedNotification[] = [];
@@ -104,6 +105,7 @@ async function initDatabase() {
}
const onEvent = async ({ type, detail }: Event) => {
console.log("EVENT RECIEVED....");
await initDatabase();
const { notification, pressAction, input } = detail;
if (type === EventType.DELIVERED && Platform.OS === "android") {
@@ -120,7 +122,7 @@ const onEvent = async ({ type, detail }: Event) => {
await scheduleNotification(reminder);
}
}
updateRemindersForWidget();
return;
}
if (type === EventType.PRESS) {
@@ -167,6 +169,7 @@ const onEvent = async ({ type, detail }: Event) => {
);
useRelationStore.getState().update();
useReminderStore.getState().refresh();
updateRemindersForWidget();
break;
}
case "REMINDER_DISABLE": {
@@ -184,6 +187,7 @@ const onEvent = async ({ type, detail }: Event) => {
);
useRelationStore.getState().update();
useReminderStore.getState().refresh();
updateRemindersForWidget();
break;
}
case strings.unpin(): {
@@ -249,6 +253,34 @@ const onEvent = async ({ type, detail }: Event) => {
}
};
type ReminderWithFormattedTime = Reminder & {
formattedTime?: string;
};
async function updateRemindersForWidget() {
const reminders: ReminderWithFormattedTime[] = await db.reminders?.all.items(
undefined,
{
sortBy: "dueDate",
sortDirection: "desc"
}
);
const activeReminders = [];
if (!reminders) return;
for (const reminder of reminders) {
if (isReminderActive(reminder)) {
reminder.formattedTime = getFormattedReminderTime(reminder);
activeReminders.push(reminder);
}
}
NotesnookModule.setString(
"appPreview",
"remindersList",
JSON.stringify(activeReminders)
);
NotesnookModule.updateReminderWidget();
}
async function setupIOSCategories() {
try {
if (Platform.OS === "ios") {
@@ -398,6 +430,7 @@ async function scheduleNotification(
trigger
);
}
updateRemindersForWidget();
} catch (e) {
console.log("Schedule notification", e);
}
@@ -943,6 +976,7 @@ async function setupReminders(checkNeedsScheduling = false) {
trigger.notification.id &&
notifee.cancelTriggerNotification(trigger.notification.id as string)
);
updateRemindersForWidget();
}
async function pinNote(id: string) {
@@ -988,7 +1022,8 @@ const Notifications = {
getChannelId,
isNotePinned,
pinNote,
Events
Events,
updateRemindersForWidget
};
export default Notifications;

View File

@@ -33,10 +33,13 @@ interface NotesnookModuleInterface {
getWidgetId: () => void;
getIntent: () => {
"com.streetwriters.notesnook.OpenNoteId"?: string;
"com.streetwriters.notesnook.OpenReminderId"?: string;
"com.streetwriters.notesnook.NewReminder"?: string;
};
getWidgetNotes: () => Promise<string[]>;
hasWidgetNote: (noteId: string) => Promise<boolean>;
updateWidgetNote: (noteId: string, data: string) => void;
updateReminderWidget: () => void;
}
export const NotesnookModule: NotesnookModuleInterface = Platform.select({
@@ -55,7 +58,8 @@ export const NotesnookModule: NotesnookModuleInterface = Platform.select({
getIntent: () => {},
getWidgetNotes: () => {},
hasWidgetNote: () => {},
updateWidgetNote: () => {}
updateWidgetNote: () => {},
updateReminderWidget: () => {}
},
android: NativeModules.NNativeModule
});

View File

@@ -61,6 +61,15 @@
android:largeHeap="true"
android:supportsRtl="false"
tools:replace="android:supportsRtl">
<receiver android:exported="false" android:label="@string/reminders_title" android:name=".ReminderWidgetProvider">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/widget_reminders_info" />
</receiver>
<receiver android:exported="false" android:label="@string/quick_note" android:name=".NoteWidget">
<intent-filter>
@@ -186,6 +195,11 @@
</intent-filter>
</service>
<service
android:name=".ReminderViewsService"
android:exported="true"
android:permission="android.permission.BIND_REMOTEVIEWS" />
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.provider"

View File

@@ -2,6 +2,7 @@ package com.streetwriters.notesnook;
import android.app.Activity;
import android.appwidget.AppWidgetManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
@@ -9,6 +10,7 @@ import android.graphics.Color;
import android.os.Bundle;
import android.util.Log;
import android.view.WindowManager;
import android.widget.RemoteViews;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Promise;
@@ -133,9 +135,12 @@ public class RCTNNativeModule extends ReactContextBaseJavaModule {
if (getCurrentActivity() != null) {
Intent intent = getCurrentActivity().getIntent();
Bundle extras = getCurrentActivity().getIntent().getExtras();
if (extras != null && intent != lastIntent) {
lastIntent = intent;
map.putString(NotePreviewWidget.OpenNoteId, extras.getString(NotePreviewWidget.OpenNoteId));
map.putString(ReminderViewsService.OpenReminderId, extras.getString(ReminderViewsService.OpenReminderId));
map.putString(ReminderWidgetProvider.NewReminder, extras.getString(ReminderWidgetProvider.NewReminder));
}
}
return map;
@@ -153,6 +158,7 @@ public class RCTNNativeModule extends ReactContextBaseJavaModule {
Map<String, ?> map = pref.getAll();
WritableArray arr = Arguments.createArray();
for(Map.Entry<String,?> entry : map.entrySet()){
if (entry.getKey().equals("remindersList")) continue;
String value = (String) entry.getValue();
Gson gson = new Gson();
Note note = gson.fromJson(value, Note.class);
@@ -176,7 +182,6 @@ public class RCTNNativeModule extends ReactContextBaseJavaModule {
}
@ReactMethod
public void updateWidgetNote(final String noteId, final String data) {
SharedPreferences pref = getReactApplicationContext().getSharedPreferences("appPreview", Context.MODE_PRIVATE);
Map<String, ?> map = pref.getAll();
SharedPreferences.Editor edit = pref.edit();
@@ -193,4 +198,16 @@ public class RCTNNativeModule extends ReactContextBaseJavaModule {
NotePreviewWidget.updateAppWidget(mContext, AppWidgetManager.getInstance(mContext), Integer.parseInt(id));
}
}
@ReactMethod
public void updateReminderWidget() {
AppWidgetManager wm = AppWidgetManager.getInstance(mContext);
int[] ids = wm.getAppWidgetIds(ComponentName.createRelative(mContext.getPackageName(), ReminderWidgetProvider.class.getName()));
for (int id: ids) {
Log.d("Reminders", "Updating" + id);
RemoteViews views = new RemoteViews(mContext.getPackageName(), R.layout.widget_reminders);
ReminderWidgetProvider.updateAppWidget(mContext, wm, id, views);
wm.notifyAppWidgetViewDataChanged(id, R.id.widget_list_view);
}
}
}

View File

@@ -0,0 +1,100 @@
package com.streetwriters.notesnook;
import android.app.ActivityOptions;
import android.app.PendingIntent;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.widget.RemoteViewsService;
import android.content.Context;
import android.widget.RemoteViews;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.streetwriters.notesnook.datatypes.Reminder;
import java.util.ArrayList;
import java.util.List;
public class ReminderViewsService extends RemoteViewsService {
static String OpenReminderId = "com.streetwriters.notesnook.OpenReminderId";
@Override
public RemoteViewsFactory onGetViewFactory(Intent intent) {
return new ReminderRemoteViewsFactory(this.getApplicationContext(), intent);
}
}
class ReminderRemoteViewsFactory implements RemoteViewsService.RemoteViewsFactory {
private Context context;
private List<Reminder> reminders;
public ReminderRemoteViewsFactory(Context context, Intent intent) {
this.context = context;
}
@Override
public void onCreate() {
// Initialize reminders list
reminders = new ArrayList<Reminder>();
}
@Override
public void onDataSetChanged() {
reminders.clear();
SharedPreferences preferences = context.getSharedPreferences("appPreview", Context.MODE_PRIVATE);
Gson gson = new Gson();
reminders = gson.fromJson(preferences.getString("remindersList","[]"), new TypeToken<List<Reminder>>(){}.getType());
}
@Override
public void onDestroy() {
reminders.clear();
}
@Override
public int getCount() {
return reminders.size();
}
@Override
public RemoteViews getViewAt(int position) {
Reminder reminder = reminders.get(position);
boolean useMiniLayout = reminder.getDescription() == null || reminder.getDescription().isEmpty();
RemoteViews views = new RemoteViews(context.getPackageName(), useMiniLayout ? R.layout.widget_reminder_layout_small : R.layout.widget_reminder_layout);
views.setTextViewText(R.id.reminder_title, reminder.getTitle());
if (!useMiniLayout) {
views.setTextViewText(R.id.reminder_description, reminder.getDescription());
}
views.setTextViewText(R.id.reminder_time, reminder.getFormattedTime());
final Intent fillInIntent = new Intent();
final Bundle extras = new Bundle();
extras.putString(ReminderViewsService.OpenReminderId, reminder.getId());
fillInIntent.putExtras(extras);
views.setOnClickFillInIntent(R.id.reminder_item_btn, fillInIntent);
return views;
}
@Override
public RemoteViews getLoadingView() {
return null;
}
@Override
public int getViewTypeCount() {
return 1;
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public boolean hasStableIds() {
return true;
}
}

View File

@@ -0,0 +1,50 @@
package com.streetwriters.notesnook;
import android.app.ActivityOptions;
import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.widget.RemoteViews;
public class ReminderWidgetProvider extends AppWidgetProvider {
static String NewReminder = "com.streetwriters.notesnook.NewReminder";
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
for (int appWidgetId : appWidgetIds) {
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget_reminders);
updateAppWidget(context, appWidgetManager, appWidgetId, views);
}
}
private static Bundle getActivityOptionsBundle() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
ActivityOptions activityOptions = ActivityOptions.makeBasic();
activityOptions.setPendingIntentBackgroundActivityStartMode(ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
return activityOptions.toBundle();
} else
return null;
}
static void updateAppWidget(Context context, AppWidgetManager appWidgetManager, int appWidgetId, RemoteViews views) {
Intent intent = new Intent(context, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE, getActivityOptionsBundle());
views.setPendingIntentTemplate(R.id.widget_list_view, pendingIntent);
Intent intent2 = new Intent(context, MainActivity.class);
intent2.putExtra(NewReminder, NewReminder);
PendingIntent pendingIntent2 = PendingIntent.getActivity(context, 0, intent2, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE, getActivityOptionsBundle());
views.setOnClickPendingIntent(R.id.add_button, pendingIntent2);
Intent intent3 = new Intent(context, ReminderViewsService.class);
intent3.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
views.setRemoteAdapter(R.id.widget_list_view, intent3);
views.setEmptyView(R.id.widget_list_view, R.id.empty_view);
appWidgetManager.updateAppWidget(appWidgetId, views);
}
}

View File

@@ -0,0 +1,135 @@
package com.streetwriters.notesnook.datatypes;
import java.util.concurrent.TimeUnit;
public class Reminder extends BaseItem {
private String title;
private String description;
private String formattedTime;
private String priority; // "silent", "vibrate", "urgent"
private long date;
private String mode; // "repeat", "once", "permanent"
private String recurringMode; // "week", "month", "day", "year"
private int[] selectedDays;
private boolean localOnly;
private boolean disabled;
private long snoozeUntil;
// Getters and Setters
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getPriority() {
return priority;
}
public void setPriority(String priority) {
this.priority = priority;
}
public long getDate() {
return date;
}
public void setDate(long date) {
this.date = date;
}
public String getMode() {
return mode;
}
public void setMode(String mode) {
this.mode = mode;
}
public String getRecurringMode() {
return recurringMode;
}
public void setRecurringMode(String recurringMode) {
this.recurringMode = recurringMode;
}
public int[] getSelectedDays() {
return selectedDays;
}
public void setSelectedDays(int[] selectedDays) {
this.selectedDays = selectedDays;
}
public boolean isLocalOnly() {
return localOnly;
}
public void setLocalOnly(boolean localOnly) {
this.localOnly = localOnly;
}
public boolean isDisabled() {
return disabled;
}
public void setDisabled(boolean disabled) {
this.disabled = disabled;
}
public long getSnoozeUntil() {
return snoozeUntil;
}
public void setSnoozeUntil(long snoozeUntil) {
this.snoozeUntil = snoozeUntil;
}
public String getFormattedTime() {
return formattedTime;
}
public void setFormattedTime(String formattedTime) {
this.formattedTime = formattedTime;
}
public String formatTime(long timeInMillis) {
long currentTime = System.currentTimeMillis();
long diff = timeInMillis - currentTime;
if (diff < TimeUnit.MINUTES.toMillis(1)) {
return "in " + (diff / 1000) + " seconds";
} else if (diff < TimeUnit.HOURS.toMillis(1)) {
long minutes = TimeUnit.MILLISECONDS.toMinutes(diff);
return "in " + minutes + " minute" + (minutes > 1 ? "s" : "");
} else if (diff < TimeUnit.DAYS.toMillis(1)) {
long hours = TimeUnit.MILLISECONDS.toHours(diff);
return "in " + hours + " hour" + (hours > 1 ? "s" : "");
} else if (diff < TimeUnit.DAYS.toMillis(2)) {
return "tomorrow";
} else if (diff < TimeUnit.DAYS.toMillis(7)) {
long days = TimeUnit.MILLISECONDS.toDays(diff);
return "in " + days + " day" + (days > 1 ? "s" : "");
} else if (diff < TimeUnit.DAYS.toMillis(30)) {
long weeks = TimeUnit.MILLISECONDS.toDays(diff) / 7;
return "in " + weeks + " week" + (weeks > 1 ? "s" : "");
} else if (diff < TimeUnit.DAYS.toMillis(365)) {
long months = TimeUnit.MILLISECONDS.toDays(diff) / 30;
return "in " + months + " month" + (months > 1 ? "s" : "");
} else {
long years = TimeUnit.MILLISECONDS.toDays(diff) / 365;
return "in " + years + " year" + (years > 1 ? "s" : "");
}
}
}

View File

@@ -0,0 +1,8 @@
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp"
android:text="No upcoming reminders"
android:textSize="16sp"
android:textColor="@android:color/darker_gray"
android:gravity="center" />

View File

@@ -0,0 +1,29 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/reminder_item_btn"
android:padding="8dp">
<TextView
android:id="@+id/reminder_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textStyle="bold"
android:textColor="@color/text"
android:textSize="16sp" />
<TextView
android:id="@+id/reminder_description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@color/text"
android:textSize="14sp" />
<TextView
android:id="@+id/reminder_time"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="12sp"
android:textColor="@color/text" />
</LinearLayout>

View File

@@ -0,0 +1,22 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/reminder_item_btn"
android:padding="8dp">
<TextView
android:id="@+id/reminder_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textStyle="bold"
android:textColor="@color/text"
android:textSize="16sp" />
<TextView
android:id="@+id/reminder_time"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="12sp"
android:textColor="@color/text" />
</LinearLayout>

View File

@@ -0,0 +1,56 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/layout_bg"
>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingHorizontal="12dp"
android:layout_gravity="center"
android:paddingTop="12dp"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_marginTop="2dp"
android:textSize="16sp"
android:textStyle="bold"
android:text="Upcoming Reminders"/>
<ImageButton
android:layout_width="30dp"
android:id="@+id/add_button"
android:layout_height="30dp"
android:layout_alignParentRight="true"
android:background="@drawable/ic_newnote" />
</RelativeLayout>
<ListView
android:id="@+id/widget_list_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:divider="@color/border"
android:paddingBottom="12dp"
android:paddingHorizontal="12dp"
android:dividerHeight="1dp" />
<LinearLayout
android:id="@+id/empty_view"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:textAlignment="center"
android:gravity="center"
android:text="Tap on + to add reminder"/>
</LinearLayout>
</LinearLayout>

View File

@@ -6,5 +6,6 @@
<color name="light_blue_900">#FF01579B</color>
<color name="bootsplash_background">#1f1f1f</color>
<color name="background">#1D1D1D</color>
<color name="border">#2E2E2E</color>
<color name="text">#B4B4B4</color>
</resources>

View File

@@ -6,5 +6,6 @@
<color name="light_blue_900">#FF01579B</color>
<color name="bootsplash_background">#FFFFFF</color>
<color name="background">#D8000000</color>
<color name="border">#CCCCCC</color>
<color name="text">#1D1D1D</color>
</resources>

View File

@@ -4,6 +4,8 @@
<string name="appwidget_text">EXAMPLE</string>
<string name="add_widget">Add widget</string>
<string name="take_a_quick_note">Take a quick note.</string>
<string name="reminders">Quick overview of upcoming reminders</string>
<string name="reminders_title">Reminders</string>
<string name="note">Note</string>
<string name="note_description">Add a note to home screen</string>
<string name="quick_note">Quick note</string>

View File

@@ -0,0 +1,13 @@
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:initialLayout="@layout/widget_reminders"
android:minWidth="400dp"
android:minHeight="100dp"
android:minResizeWidth="400dp"
android:minResizeHeight="50dp"
android:description="@string/reminders"
android:targetCellWidth="5"
android:targetCellHeight="2"
android:resizeMode="horizontal|vertical"
android:updatePeriodMillis="1024"
android:widgetCategory="home_screen"
/>