GithubHelp home page GithubHelp logo

esperandro's People

Contributors

dependabot[bot] avatar dkunzler avatar scompt avatar stonecs avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

esperandro's Issues

Gradle sample

I need a complete gradle sample object for an easier start for newbies.

Default serialiser without dependencies

could you add a default serialiser that does not depend on anything special? No jackson and no gson?

I usually use following in all my projects:

public class SerializationUtil {

	public static String serialize(Object object) {
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		try {
			ObjectOutputStream oos = new ObjectOutputStream(baos);
			oos.writeObject(object);
			oos.flush();
			oos.close();
		} catch (IOException e) {
			L.e(e);
		}
		return Base64.encodeToString(baos.toByteArray(), 0);
	}

	public static <T> T deserialize(String serializedObject, Class<T> clazz) {
		if (serializedObject == null) {
			return (T) null;
		}

		byte[] data = Base64.decode(serializedObject, 0);
		Object o = null;
		try {
			ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(data));
			o = ois.readObject();
			ois.close();
		} catch (IOException e) {
			L.e(e);
		} catch (ClassNotFoundException e) {
			L.e(e);
		}

		return (T) o;
	}
}

This could be packed into a small extension...

AndroidX - import problem

After migrating to androidx I have following problem:

The generated preference class is importing androidx.core.util.LruCache instead of androidx.collection.LruCache. Problem happens with following setup: @Cached(cacheOnPut = true, support = true), only ifsupport = true is used of course.

if the data is too lager(22.17K) ,and load will be change so slowly

Now ,I use this lib to store a List data like this

ArrayListdistricts = filterConfigEntity.getDistricts();
getSharePrefence(DistrictsListSharePreference.class).containerValueAsync(districts);

but when i get it ,load data will beslowly, many times it's need three seconds or more , can you give me some advice,thanks

Suggestion - use string definitons with generated preference class

It's now possible to generate string resources for each preference. Can you extend the generated class with following function:

// prefID... one of the generated string resources via @GenerateStringResources
public <T> T getValue(Context context, int prefId);
public <T> boolean setValue(Context context, int prefId, T value);

Those functions should use the cache if enabled as well. My settings view library is finished now and with this extension I could use your preferences perfectly with it.

Implementation could look like following:

// 1) Map all existing ids to a class
private HashMap<Integer, Class> mPreferenceMap = new HashMap();
mPreferenceMap.put(R.string.<PREFERENCE>, Boolean.class);
// ...

// 2) create getter
public <T> T getValue(Context context, int id) {
    Class clazz = mPreferenceMap.get(id);
    if (clazz == null) {
        throw new RuntimeException("Please only use string resource ids generated via @GenerateStringResources");
    }
    String stringId = context.getString(id);
    Object __result = cache.get(stringId);
    if (__result == null) {
        if (clazz.equals(Boolean.class)) {
            __result = preferences.getBoolean(stringId, false);
        } else {
            // handle other existing preference classes as wll
        }
        if (__result != null) {
            cache.put(stringId, __result);
        }
    }
    return __result;
}

// 3) and setter
public <T> boolean setValue(Context context, int id, T value) {
    Class clazz = mPreferenceMap.get(id);
    if (clazz == null) {
        throw new RuntimeException("Please only use string resource ids generated via @GenerateStringResources");
    }
    String stringId = context.getString(id);
    cache.put(stringId, value);
    if (clazz.equals(Boolean.class)) {
        return preferences.edit().putBoolean(stringId, value).commit();
    } else {
            // handle other existing preference classes as wll
        }
}

Proguard config

Looks like there is lack of proguard config. Here is mine for jackson:

#keep the annotated things annotated
-keepattributes *Annotation*, EnclosingMethod, Signature, InnerClasses

#jackson
-dontwarn com.fasterxml.jackson.databind.**
-keepnames class com.fasterxml.jackson.** { *; }

#esperando
-keepnames class de.devland.** { *; }
-keep class **$$Impl { public *;}

Async setters

Whenever changing a value, this may be blocking the UI if it happens on the UI thread... I think, most of the time, async setters would be fine as well (and maybe better?).

So something that allows me to decide if commit() or apply() should be used would be really nice... Maybe a setter function with a boolean parameter additional to the default? This would not break any existing API.

Instead of generating following:

@Override
public boolean lastSelectedColor(int lastSelectedColor) {
    cache.put("lastSelectedColor", lastSelectedColor);
    return preferences.edit().putInt("lastSelectedColor", lastSelectedColor).commit();
}

You could generate something like following:

@Override
public Boolean lastSelectedColor(int lastSelectedColor) {
    return lastSelectedColor(lastSelectedColor, true);
}

@Override
public Boolean lastSelectedColor(int lastSelectedColor, boolean commitImmediately) {
    cache.put("lastSelectedColor", lastSelectedColor);
    Editor editor = preferences.edit().putInt("lastSelectedColor", lastSelectedColor);
    if (commitImmediately)
        return editor.commit();
    editor.apply();
    return null; // we don't know the success state...
}

Use same class modifier for Impl

A package protected interface will have an implementation that is public.
Use the same class modifier as the implemented interface.

Tried to use a serialized Object in preferences but no serializer is present

I have a try to use Serializable object to SharePreference but throws a exception

Tried to use a serialized Object in preferences but no serializer is present
java.lang.IllegalStateException: Tried to use a serialized Object in preferences but no serializer is present
at de.devland.esperandro.Esperandro.getSerializer(Esperandro.java:74)
at com.eallcn.agent.shareprefrence.ConditionSharePreference$$Impl.containerValueAsync(ConditionSharePreference$$Impl.java:24)
at com.eallcn.agent.ui.control.LoginControl.login(LoginControl.java:141)
at LoginControl_Proxy.super$login$void(LoginControl_Proxy.generated)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:525)
at com.google.dexmaker.stock.ProxyBuilder.callSuper(ProxyBuilder.java:523)
at com.eallcn.agent.proxy.callback.AsynMethodAtomInterceptor$1.getResult(AsynMethodAtomInterceptor.java:90)
at com.eallcn.agent.proxy.callback.AsynMethodAtomInterceptor$1.run(AsynMethodAtomInterceptor.java:56)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1080)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:573)
at java.lang.Thread.run(Thread.java:841)

Simple Gradle instructions

The project works perfect with android-apt.

So, the instructions for Gradle can be much simpler:

dependencies {
    apt 'de.devland.esperandro:esperandro:1.1'
    compile 'de.devland.esperandro:esperandro-api:1.1'
}

Serialisation bug

I have a field like following in my preferences to show some info cards to my users and to know which one they have already seen and dismissed:

List<String> hiddenInfos();
boolean hiddenInfos(List<String> hiddenInfos);
void hiddenInfos$Add(String toAdd);
void hiddenInfos$Remove(String toRemove);

After my last relase I suddenly got a lot of crash reports like following:

[SerializationUtil:43 deserialize]: java.io.InvalidClassException: com.my.app.prefs.PreferenceManager$$Impl$HiddenInfos; Incompatible class (SUID): com.my.app.prefs.PreferenceManager$$Impl$HiddenInfos: static final long serialVersionUID =8736114807095266538L; but expected com.my.app.prefs.PreferenceManager$$Impl$HiddenInfos: static final long serialVersionUID =3661852850784779536L;
	at java.io.ObjectInputStream.verifyAndInit(ObjectInputStream.java:2343)
	at java.io.ObjectInputStream.readNewClassDesc(ObjectInputStream.java:1643)
	at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:657)
	at java.io.ObjectInputStream.readNewObject(ObjectInputStream.java:1784)
	at java.io.ObjectInputStream.readNonPrimitiveContent(ObjectInputStream.java:761)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:1985)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:1942)
	at com.swissarmy.utils.SerializationUtil.deserialize(SerializationUtil.java:40)
	at com.my.app.MainApp$1.deserialize(MainApp.java:76)
	at com.my.app.prefs.PreferenceManager$$Impl.hiddenInfos(PreferenceManager$$Impl.java:1182)
	at com.my.app.managers.UpdateManager.onAppStart(UpdateManager.java:208)
	at com.my.app.MainApp.onAfterOnCreate(MainApp.java:151)
	...

I would expect that the serialVersionUID is constant... Maybe it makes sense to define a custom one via annotations? To avoid that the serialVersionUID is constant as this is essential for deserialisation...

Improvement - enable caching

It would be nice if I could enable a cache, so that I can fastly recheck a setting without reading the settings file again.

A simple HashMap would be enough and an annotation like @SharedPreferences(..., enableCaching= true). And if the user enables caching, a static HashMap is always kept in sync when writing values and reading is done by accessing the HashMap...

XML Preference Screen support

Is it possible to use esperandro type safe with PreferenceScreens written in xml?

A simple solution for me would be that you just generate string resources for each preference key... So that I can reference them in the xml

Any ideas to that?

Init method to store defaults

It could be nice to store the annotated defaults into the preferences to make use of them in PreferenceActivitys for example.

#11 bumped min API level to 9

With the integration of apply for issue #11 the minimum required API level was implicitly bumped to 9.

Generate an API level check into the generated class and call commit if API below 9.

Cache problem with null values

With cache enabled, generated code may look like following:

 @Override
  public DataManager.ExistingDataSources existingDataSources() {
	DataManager.ExistingDataSources __result = (DataManager.ExistingDataSources) cache.get("existingDataSources");
	if (__result == null) {
	  Serializer __serializer = Esperandro.getSerializer();
	  __result = __serializer.deserialize(preferences.getString("existingDataSources", null), com.michaelflisar.gallery.data.DataManager.ExistingDataSources.class);
	  cache.put("existingDataSources", __result);;
	}
	return __result;
  }

This is a custom serialisable field of mine, the problem is, if it is not initialised or is null, the cache.put("existingDataSources", __result);; will throw a java.lang.NullPointerException: key == null || value == null exception.

Can this be changed to allow null values? Any reasons, why this is not possible yet?

Btw, the code generator is generating double ; as you can see in my code example... Not a big deal though, so I'm just mentioning it here...

PS: currently I'm using following version:

compile 'de.devland.esperandro:esperandro-api:2.3.1'
apt 'de.devland.esperandro:esperandro:2.3.1'

Java version - support java 8

In my gradle console log I always see following:

warning: No SupportedSourceVersion annotation found on de.devland.esperandro.processor.EsperandroAnnotationProcessor, returning RELEASE_6.
warning: Supported source version 'RELEASE_6' from annotation processor 'de.devland.esperandro.processor.EsperandroAnnotationProcessor' less than -source '1.8'

I'm using java 8 (for lambdas and other new java features) in all my projects, would it be possible to get rid of this warning? Could you add java 8 support to this library?

It's no big deal as it's a warning only and it does not have any effect, still...

Problem when using in combination with PreferenceFragments

I have a simple class like following (minimal extract of my file) using v2.4.1:

@Cached(cacheOnPut = true, support = true)
@SharedPreferences(name = PrefManager.PREF_NAME, mode = SharedPreferenceMode.PRIVATE)
public interface PrefManager extends SharedPreferenceActions, CacheActions
{
	String PREF_NAME = "mainPrefs";
	
	// a lot of default preferences, ALL have a default value
	// Example:
	@Default(ofInt = 0)
	int versionShown();
	void versionShown(int versionShown);
	
	// some custom serialised values without default value
	DataManager.ExistingDataSources existingDataSources();
	void existingDataSources(DataManager.ExistingDataSources existingDataSources);
}

What I do

  1. on my first app start, I check a preference with default value and if it is the first app start, I manually initialise my preferences without default value (my custom serialisable fields) and make sure to set all default values and persist them
  2. I use Esperandro everyhwere in my app and it works fine
  3. additional, I use the same preference file in my PreferenceFragments

Problem

Sometimes after I return from my SettingsActivity the preferences are completely resetted. I'm sure I'm using esperandro anywhere in my code and that only the PreferenceFragments directly manipulate the preference file. Any ideas how this can happen? Do you see a problem in my code?

CODE

My initialisation function

public static void initPrefs()
{
    if (MainApp.getPrefs().existingDataSources() == null)
        MainApp.getPrefs().existingDataSources(new DataManager.ExistingDataSources());
    int versionShown = MainApp.getPrefs().versionShown();
    if (versionShown == 0)
    {
        MainApp.getPrefs().initDefaults();
        // force saving all prefs to the file, we need this so that the PreferenceFragments work correctly
        MainApp.getPrefs().get().edit().commit();
    }
}

My settings activity

public class SettingsActivity extends AppCompatActivity
{
	@Override
	public void onDestroy()
	{
		// reset all cached values in esperandro, we may have changed them without using esperandro!!!
		MainApp.getPrefs().resetCache();
		super.onDestroy();
	}
}

My base PreferenceFragment, a minimal version

public abstract class BasePrefFragment extends PreferenceFragmentCompat
{
	@Override
	public void onCreatePreferences(Bundle bundle, String s) {
                    // ATTENTION: we use the same pref file as esperandro!
		getPreferenceManager().setSharedPreferencesName(PrefManager.PREF_NAME);
		addPreferencesFromResource(getPreferenceId());
	}

	public abstract int getPreferenceId();
}

Get dynamic default values

Currently one can define default values at compile time using @Default annotation. Unfortunately this works only for simple types and at compile time. If user wants to specify default value for complex object, that can't be passed to annotation, he can't do that.

My propose is to introduce getters with additional parameter that should be used as default. The library can have a convention that such methods should end with *Default

@SharedPreferences
interface MySettings{
    List getMyList(); //common usage
    List getMyListDefault(List defaultValue); //using default value if neccessary
}

The generated code would be not much more complex. Seems that the library would have to check for existence of the value for complex objects. For simple objects it can just pass parameter to shared prefs.

Bug in cache feature

If I use setter with return type, the cache code is generated after returning the result of the setter...

Generated code looks like following:

@Override
public boolean versionShown(int versionShown) {
    return preferences.edit().putInt("versionShown", versionShown).commit();
    cache.remove("versionShown");
}

and should look like following:

@Override
public boolean versionShown(int versionShown) {
    cache.remove("versionShown");
    return preferences.edit().putInt("versionShown", versionShown).commit();
}

Same bug if I use the cache with cacheOnPut = true...

Improvement suggestion

I've an app with a lot of settings and they are growing. I see that I do the same things over and over again like following:

switch (id) {
    case 1:
        MainApp.getPrefs().booleanSetting1(b);
        break;
    case 2:
        MainApp.getPrefs().booleanSetting2(b);
        break;
    case 3:
        MainApp.getPrefs().booleanSetting3(b);
        break;
}

Result wish

// I want to be able to do following
PreferenceManager.Pref pref = PreferenceManager.getPrefByIndex(index);
MainApp.getPrefs().set(pref, b);

Implementation idea - demonstrating boolean values only

// Somehow enable an enum generation
@Cached(cacheOnPut = true, support = true)
@SharedPreferences(name = "mainPrefs", mode = SharedPreferenceMode.PRIVATE, enum = true)
public interface PreferenceManager extends SharedPreferenceActions {

        @Default(ofBoolean = false)
        boolean booleanSetting1();
        boolean booleanSetting1(boolean booleanSetting1);
		
        @Default(ofBoolean = false)
        boolean booleanSetting2();
        boolean booleanSetting2(boolean booleanSetting2);
		
        @Default(ofBoolean = false)
        boolean booleanSetting2();
        boolean booleanSetting2(boolean booleanSetting3);
}

// Result
public class PreferenceManager$$Impl  implements SharedPreferenceActions, PreferenceManager, CacheActions {

    // 1) The implemantion has an enum now like following:
    enum Pref {
        booleanSetting1(1, "booleanSetting1"),
        booleanSetting2(2, "booleanSetting2"),
        booleanSetting3(3, "booleanSetting3");
        
        int index;
        String name;
        
        Pref(int index, String name) {
            this.index = index;
            this.name = name;
        }
    }
	
    // 2) a getter for the pref based on the index
    public Pref getPrefByIndex(int index) {
        if (index >= 0 && index < Pref.values().length) {
            return Pref.values()[index];
        }
        return null;
    }

    // 3) setter that works with the pref enum
    public boolean set(Pref pref, boolean value) {
        cache.put(pref.name, value);
        return preferences.edit().putBoolean(pref.name, value).commit();
    }
	
    // 4) getter that works with the pref enum
    public boolean getBool(Pref pref) {
        Boolean __result = (Boolean) cache.get(pref.name);
        if (__result == null) {
          __result = preferences.getBoolean(pref.name, false);
          if (__result != null) {
            cache.put(pref.name, __result);
          }
        }
        return __result;
    }
}

Any thoughts on this?

Incompatible with Dagger 1.2.2

Okay, so I realize this may be a bit of a strange issue. I'm not exactly sure how to help debugging, so please feel free to point me in the right direction.

Both you and dagger depend on the Square JavaWriter. When Dagger 1.2.2 is used in my dependencies, I get the following error:

Caused by: java.lang.NoSuchMethodError: com.squareup.javawriter.JavaWriter.beginControlFlow(Ljava/lang/String;)Lcom/squareup/javawriter/JavaWriter;
    at de.devland.esperandro.processor.PreferenceEditorCommitStyle.emitPreferenceCommitActionWithVersionCheck(PreferenceEditorCommitStyle.java:25)
    at de.devland.esperandro.processor.Putter.createPutter(Putter.java:152)
    at de.devland.esperandro.processor.Putter.createPutterFromModel(Putter.java:89)
    at de.devland.esperandro.processor.EsperandroAnnotationProcessor.processInterfaceMethods(EsperandroAnnotationProcessor.java:124)
    at de.devland.esperandro.processor.EsperandroAnnotationProcessor.process(EsperandroAnnotationProcessor.java:78)
    at com.sun.tools.javac.processing.JavacProcessingEnvironment.callProcessor(JavacProcessingEnvironment.java:793)
    at com.sun.tools.javac.processing.JavacProcessingEnvironment.discoverAndRunProcs(JavacProcessingEnvironment.java:722)
    at com.sun.tools.javac.processing.JavacProcessingEnvironment.access$1700(JavacProcessingEnvironment.java:97)

Using Dagger 1.2.1 doesn't produce this error, everything compiles just fine.
I've tested this on both a Linux and Windows host, so I don't think the issue has anything to do with Java itself. My project is over here for a reproducible case.
I can file an issue with Dagger as well if you want me to blame them, I'm just really not sure what could be causing this, but figured you should know.
Thanks, let me know if there's anything I can do to help!

Compiler warning if setting default values to default

If I set a default value that is the same as the annotations default value I get a compiler warning from here:

processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING, MESSAGE_PREFIX + "No overwritten default "

Why is this necessary?

// @Default(ofBoolean = false) // <= uncommenting this will produce the warning
boolean test();
boolean test(boolean test);

I always define the default values as I want to see it in my code and to make sure that it is the value I expect it to be and want it to be

Serialized object cannot be cast to type

java.lang.ClassCastException: com.google.gson.internal.LinkedTreeMap cannot be cast to com.example.data.Item
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2215)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2265)

When reading an ArrayList<Item> from SharedPreferences, objects stored in the ArrayList cannot be cast to their initial type (are not de-serialized).

Item class implements Serializable, Esperandro is using its default serializer.

public class Item implements Serializable {
    String title;
    String desc;
}
@SharedPreferences
public interface LocalStorageInterface {
    ArrayList<Item> localItems();
    void localItems(ArrayList<Item> items);
}

Serializable problem

Currently, if I use serializable lists, they are not working, as the generated code is wrapping a list in a container, that is NOT implementing serializable itself...

NOT Working: Using following does crash, as the wrapper class is not serializable:

ArrayList<String> existingSources();
boolean existingSources(ArrayList<String>existingSources);

working

OwnWrapper existingSources();
boolean existingSources(OwnWrapper  existingSources);

With OwnWrapper like following:

public static class OwnWrapper implements Serializable
{
    public ArrayList<String> sources;

    public OwnWrapper ()
    {
        sources = new ArrayList<>();
    }
}

warnings for each preference after upgrading to 2.5.2 - 3.0.0

After upgrading to version 2.5.2 or higher, gradle build produces a warning for each preference, e.g.
warning: [unchecked] unchecked cast return (V) (Integer) registrationStatus(); ^ required: V found: Integer where V is a type-variable: V extends Object declared in method <V>getValue(Context,int)

The declaration is as follows:
@SharedPreferences
public interface DefaultPrefs extends SharedPreferenceActions {
...
int registrationStatus();
void registrationStatus(int registrationStatus);

Probably I'm doing something wrong?
How can these warnings be avoided.

regards Rainer

Do not rely on Serializable interface

Let the serializer be completely in charge of serialization by letting all objects be eligible for serialization.
Background: objects can perhaps be serialized without having the serializable interface.

Eliminate warnings and unused imports

Depending on what code is being generated some imports are not neccessary and produce warnings.
The same applies to "suppress warnings unchecked", completely against the reason the suppress statement are being generated.

We should add switches when generating the code to check if those lines are neccessary, or not.

InitDefaults only once

Add a check to initDefaults that no user set value will get overridden.
This way initDefaults can be called at initiialization without caring about previous set values.
(useful for app updates when preferences ared added)

Public function to reset all cached values

Please add a public reset cache function. When using your library with preference fragments, I will have to invalidate any cached values, because preference fragments don't allow to pass in a SharedPreference object, but only a preference name... Currently I use reflection like following for it:

    PrefManager$$Impl prefs = (PrefManager$$Impl) MainApp.getPrefs();
    try
    {
        Field field = PrefManager$$Impl.class.getDeclaredField("cache");
        field.setAccessible(true);
        Object cache = field.get(prefs);
        Method evictAll = cache.getClass().getDeclaredMethod("evictAll", new Class[]{});
        evictAll.invoke(cache);
    }
    catch (NoSuchFieldException e)
    {
        e.printStackTrace();
    } 
    catch (IllegalAccessException e)
    {
        e.printStackTrace();
    } 
    catch (NoSuchMethodException e)
    {
        e.printStackTrace();
    } 
    catch (InvocationTargetException e)
    {
        e.printStackTrace();
    }

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.