Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Android: adds "recent notes" widget (abandoned) #3458

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions ReactNativeClient/android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,16 @@
<data android:mimeType="*/*" />
</intent-filter>
</activity>
<receiver android:name=".widgets.recents.RecentsWidgetProvider">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data android:name="android.appwidget.provider"
android:resource="@xml/recents_widget_provider_info" />
</receiver>
<service android:name=".widgets.recents.RecentsWidgetService"
android:permission="android.permission.BIND_REMOTEVIEWS"
android:exported="false" />
</application>

</manifest>
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
import java.lang.reflect.InvocationTargetException;
import java.util.List;

import net.cozic.joplin.widgets.WidgetDataPackage;

public class MainApplication extends Application implements ReactApplication {

private final ReactNativeHost mReactNativeHost =
Expand All @@ -32,6 +34,7 @@ protected List<ReactPackage> getPackages() {
List<ReactPackage> packages = new PackageList(this).getPackages();
// Packages that cannot be autolinked yet can be added manually here, for example:
packages.add(new SharePackage());
packages.add(new WidgetDataPackage());
return packages;
}

Expand Down Expand Up @@ -96,4 +99,4 @@ private static void initializeFlipper(Context context) {
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package net.cozic.joplin.widgets;

import android.content.Context;
import android.content.SharedPreferences;

import androidx.annotation.NonNull;

import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;

import org.json.JSONException;
import org.json.JSONObject;

public abstract class WidgetData {
private static final String NAME = "widget_data";
private SharedPreferences sharedPreferences;

protected Context context;

protected abstract String getKey();

protected WidgetData(Context context) {
this.context = context;
sharedPreferences = context.getSharedPreferences(NAME, Context.MODE_PRIVATE);
}

protected JSONObject readJSON() {
try {
return new JSONObject(readString());
} catch (JSONException e) {
return new JSONObject();
}
}

protected void writeJSON(JSONObject value) {
writeString(value.toString());
}

protected String readString() {
return sharedPreferences.getString(getKey(), "{}");
}

protected void writeString(String value) {
sharedPreferences.edit().putString(getKey(), value).apply();
}

public ReactModule createReactModule(String name) {
return new ReactModule((ReactApplicationContext) context, name, this);
}

private final static class ReactModule extends ReactContextBaseJavaModule {
private String name;
private WidgetData widgetData;

private ReactModule(@NonNull ReactApplicationContext reactContext, String name, WidgetData widgetData) {
super(reactContext);
this.name = name;
this.widgetData = widgetData;
}

@NonNull
@Override
public String getName() {
return name;
}

@ReactMethod
public void write(String value) {
widgetData.writeString(value);
}

@ReactMethod
public void read(Promise promise) {
promise.resolve(widgetData.readString());
}

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package net.cozic.joplin.widgets;

import androidx.annotation.NonNull;

import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;

import net.cozic.joplin.widgets.recents.RecentsWidgetData;

import java.util.Collections;
import java.util.List;

public class WidgetDataPackage implements ReactPackage {
@NonNull
@Override
public List<NativeModule> createNativeModules(@NonNull ReactApplicationContext reactContext) {
return Collections.singletonList(
new RecentsWidgetData(reactContext).createReactModule("RecentsWidget")
);
}

@NonNull
@Override
public List<ViewManager> createViewManagers(@NonNull ReactApplicationContext reactContext) {
return Collections.emptyList();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package net.cozic.joplin.widgets.recents;

import android.content.Context;
import android.content.Intent;

import net.cozic.joplin.widgets.WidgetData;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class RecentsWidgetData extends WidgetData {
public RecentsWidgetData(Context context) {
super(context);
}

private void broadcastUpdate() {
Intent intent = new Intent(context, RecentsWidgetProvider.class);
intent.setAction(RecentsWidgetProvider.UPDATE_ACTION);
context.sendBroadcast(intent);
}

public List<NoteItem> readRecents() {
JSONObject data = readJSON();
try {
JSONArray notes = data.getJSONArray("notes");
List<NoteItem> result = new ArrayList<>(notes.length());
for (int i = 0; i < notes.length(); i++) {
result.add(NoteItem.fromJSONObject(notes.getJSONObject(i)));
}
return result;
} catch (JSONException e) {
return Collections.emptyList();
}
}

@Override
protected void writeString(String value) {
super.writeString(value);
broadcastUpdate();
}

@Override
protected String getKey() {
return "RecentsWidget";
}

public static final class NoteItem {
private String id;
private String title;

public static NoteItem fromJSONObject(JSONObject obj) throws JSONException {
return new NoteItem(obj.getString("id"), obj.getString("title"));
}

public NoteItem(String id, String title) {
this.id = id;
this.title = title;
}

public String getId() {
return id;
}

public String getTitle() {
return title;
}

@Override
public int hashCode() {
return getId().hashCode();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package net.cozic.joplin.widgets.recents;

import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.widget.RemoteViews;

import net.cozic.joplin.R;

public class RecentsWidgetProvider extends AppWidgetProvider {
public static final String CLICK_ACTION = "RECENTS_WIDGET_CLICK_ACTION";
public static final String UPDATE_ACTION = "RECENTS_WIDGET_UPDATE_ACTION";

public static final String NOTE_ID = "RECENTS_WIDGET_NOTE_ID";

private static final int listViewId = R.id.list_view;

private void handleClick(Context context, String noteId) {
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("joplin://notes/" + noteId));
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
}

private void handleUpdate(Context context) {
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
int[] ids = appWidgetManager.getAppWidgetIds(new ComponentName(context, getClass()));
appWidgetManager.notifyAppWidgetViewDataChanged(ids, listViewId);
}

@Override
public void onReceive(Context context, Intent intent) {
super.onReceive(context, intent);
String action = intent.getAction();
if (action == null) {
return;
}
switch (action) {
case CLICK_ACTION:
String noteId = intent.getStringExtra(NOTE_ID);
if (noteId != null) {
handleClick(context, noteId);
}
break;
case UPDATE_ACTION:
handleUpdate(context);
break;
}
}

@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
for (int appWidgetId : appWidgetIds) {
RemoteViews rv = new RemoteViews(context.getPackageName(), R.layout.recents_widget);
rv.setRemoteAdapter(listViewId, widgetIntent(context, appWidgetId));
rv.setEmptyView(listViewId, R.id.empty_view);
rv.setPendingIntentTemplate(listViewId, pendingIntentTemplate(context, appWidgetId));
appWidgetManager.updateAppWidget(appWidgetId, rv);
}
super.onUpdate(context, appWidgetManager, appWidgetIds);
}

private Intent widgetIntent(Context context, int appWidgetId) {
Intent intent = new Intent(context, RecentsWidgetService.class);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
return intent;
}

private PendingIntent pendingIntentTemplate(Context context, int appWidgetId) {
Intent intent = new Intent(context, RecentsWidgetProvider.class);
intent.setAction(RecentsWidgetProvider.CLICK_ACTION);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package net.cozic.joplin.widgets.recents;

import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.widget.RemoteViews;
import android.widget.RemoteViewsService;

import net.cozic.joplin.R;

import java.util.List;

public class RecentsWidgetService extends RemoteViewsService {
@Override
public RemoteViewsFactory onGetViewFactory(Intent intent) {
return new RecentsWidgetDataViewsFactory(getApplicationContext(), intent);
}

private static class RecentsWidgetDataViewsFactory implements RemoteViewsService.RemoteViewsFactory {
private List<RecentsWidgetData.NoteItem> notes;
private Context context;
private RecentsWidgetData recentsWidgetData;

public RecentsWidgetDataViewsFactory(Context context, Intent intent) {
this.context = context;
recentsWidgetData = new RecentsWidgetData(context);
}

@Override
public void onCreate() {
notes = recentsWidgetData.readRecents();
}

@Override
public void onDataSetChanged() {
notes = recentsWidgetData.readRecents();
}

@Override
public RemoteViews getViewAt(int position) {
RemoteViews rv = new RemoteViews(context.getPackageName(), R.layout.recents_widget_item);
rv.setTextViewText(R.id.recents_widget_item, notes.get(position).getTitle());
rv.setOnClickFillInIntent(R.id.recents_widget_item, fillInIntent(notes.get(position).getId()));
return rv;
}

private Intent fillInIntent(String noteId) {
Bundle extras = new Bundle();
extras.putString(RecentsWidgetProvider.NOTE_ID, noteId);
Intent intent = new Intent();
intent.putExtras(extras);
return intent;
}

@Override
public void onDestroy() {
notes.clear();
}

@Override
public int getCount() {
return notes.size();
}

@Override
public RemoteViews getLoadingView() {
return null;
}

@Override
public int getViewTypeCount() {
return 1;
}

@Override
public boolean hasStableIds() {
return true;
}

@Override
public long getItemId(int position) {
return notes.get(position).hashCode();
}
}
}
Loading