Code Monkey home page Code Monkey logo

android-ktx's People

Contributors

ataulm avatar chrisbanes avatar dsteve595 avatar florina-muntenescu avatar hendraanggrian avatar hluhovskyi avatar jakewharton avatar jaredsburrows avatar jawnnypoo avatar jitinsharma avatar jmslau avatar jutikorn avatar jzoran avatar landerlyoung avatar louiscad avatar mattprecious avatar mightyfrog avatar panpanini avatar ramv13 avatar romainguy avatar romtsn avatar sathawale27 avatar savinmike avatar shyiko avatar simonschiller avatar tiembo avatar toshihirooya avatar tougee avatar umang91 avatar yanglinzhen 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  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

android-ktx's Issues

Extension for Network connectivity check

This may seem specific but almost every project which does network call would be using this.

@RequiresPermission(value = Manifest.permission.ACCESS_NETWORK_STATE)
fun Context.isConnected(): Boolean {
    val connectivityManager = this
            .getSystemService(Context.CONNECTIVITY_SERVICE) as? ConnectivityManager
    connectivityManager?.let {
        val netInfo = it.activeNetworkInfo
        netInfo?.let {
            if (it.isConnected) return true
        }
    }
    return false
}

android-ktx installation

Hey There!
I tried to use KTX as you have suggested it did not sync. Plz give the installation in detail for new bees.

Intent builder

Kotlin stdlib uses lambdas to build different types of complex objects:
buildString
buildSequence
List builder

I propose to add intent builder to ktx. That will work like:

buildIntent {
   action = "some.Action"
   data = "http://example.com".toUri()
   putExtra("name", value)
}

Possible implementation:

inline fun buildIntent(initializer: Intent.() -> Unit) = Intent().apply { initializer() }

Also we can move often used properties to arguments of the function, such as action:

inline fun buildIntent(action: String) = Intent(action).apply { initializer() }

but not sure that this is make a lot of sense.

But another feature would more useful. Intent builder with reified generic instead class argument:

inline fun <reified T> buildIntent(
        context: Context, 
        initializer: Intent.() -> Unit
) = Intent(context, T::class.java).apply { initializer() }

This version also can provide empty lambda as default argument for initilizer, to make builder usage optional, reified acitivity creation already useful feature

Possible naming:

Intent (same as List builder, that looks like constructor call but actually function)
buildIntent (same as stringBuilder)
intent is short but doesn't follow current Kotlin naming and clashes with variable names

Visibility extensions for View classes

I'm considering adding a few methods to View class in order to set visibility, like:

setVisible()
setInvisible()
setGone()

(API is a subject to change if you will)
What do you think guys? Is it something we would like to see in android-ktx?

Shared preferences

Make action return SharedPreferences.Editor instead of Unit so the method body could be converted to single line

inline fun SharedPreferences.edit(action: SharedPreferences.Editor.() -> SharedPreferences.Editor) = edit().action().apply()

Feature suggestion: SharedPreferences.putAll keys and values

To my mind it would be nice to have some extension for setting many items to the SharedPreferences, something like this:

prefs.putAll("key0" to 2,
                "key1" to "text",
                "key2" to true,
                "key3" to null)

which does

fun SharedPreferences.putAll(vararg pairs: Pair<String, *>) {
    edit {
        pairs.forEach { (key, value) ->
            when (value) {
                is String -> putString(key, value)
                is Set<*> -> putStringSet(key, value as? Set<String>?)
                is Boolean -> putBoolean(key, value)
                is Float -> putFloat(key, value)
                is Int -> putInt(key, value)
                is Long -> putLong(key, value)
                null -> remove(key)
            }
        }
    }
}

Regards,
Jack

Tinting

Make tinting views easier:

Before:
view.imageTintList = ContextCompat.getColorStateList(context!!, myColor) }

After:
view.tint(myColor)

Implementation:

fun View.tint(myColor: Int) {
       ContextCompat.getColorStateList(context, myColor) }
}

checkApi is not working

  • It doesn't fail when new APIs are missing from the txt file.
  • Exit code is 0 even when there are failures.

Why sources are still in "java" folder?

It may be not so important, but why path to sources still contains java
android-ktx/src/main/java/androidx/

shouldn't it be kotlin?
android-ktx/src/main/kotlin/androidx/

Add animation speed properties (short/medium/long) for Views

When you work a lot with animation it's useful access to standard values.

val Context.shortAnimTime: Long get() = resources.getInteger(android.R.integer.config_shortAnimTime).toLong()
val Context.mediumAnimTime: Long get() = resources.getInteger(android.R.integer.config_mediumAnimTime).toLong()
val Context.longAnimTime: Long get() = resources.getInteger(android.R.integer.config_longAnimTime).toLong()
val View.shortAnimTime: Long get() = context.shortAnimTime
val View.mediumAnimTime: Long get() = context.mediumAnimTime
val View.longAnimTime: Long get() = context.longAnimTime

Animate a view from an animRes

/**
 * Starts a view animation.
 * @param animRes animation resource
 * @param apply apply some changes to [Animation] instance
 */
fun View.animate(animRes: Int, apply: (Animation.() -> Unit)? = null) {
    startAnimation(AnimationUtils.loadAnimation(context, animRes).apply { apply?.invoke(this) })
}

Usage:

mView.animate(R.anim.push_up_in)
mView.animate(R.anim.push_up_in,{
                    interpolator = AccelerateDecelerateInterpolator()
                })

Feature suggestion : Adding Life-Cycle aware variables extensions

I have made a library that makes it possible to make variables Life-Cycle aware, so instead of creating a new class that implements LifeCycleObserver and add my code in the corresponding methods, I just hold on to the Variable and pass functions that will be executed upon the variable when a Life-Cycle event happens, the library is called LiteCycle

And sample code for this LiteCycle is as follows :

// handling RxJava 2 Disposables
LiteCycle.with(titleText.subscribe(titleTextView::setText))
                .forLifeCycle(this)
                .onDestroyInvoke(Disposable::dispose)
                .observe();

// handling normal values
LiteCycle.with(10)
        .forLifeCycle(this)
        .onCreateInvoke(i -> Log.e("LiteCycle", "initial value " + i))
        .onCreateUpdate(i -> i + 1)
        .onCreateInvoke(i -> Log.e("LiteCycle", "onCreate() invoked " + i))
        .onStartUpdate(i -> i + 1)
        .onStartInvoke(i -> Log.e("LiteCycle", "onStart() invoked " + i))
        .onResumeUpdate(i -> i + 1)
        .onResumeInvoke(i -> Log.e("LiteCycle", "onResume() invoked " + i))
        .onPauseUpdate(i -> i + 1)
        .onPauseInvoke(i -> Log.e("LiteCycle", "onPause() invoked " + i))
        .onStopUpdate(i -> i + 1)
        .onStartInvoke(i -> Log.e("LiteCycle", "onStop() invoked " + i))
        .onDestroyUpdate(i -> 10)
        .onDestroyInvoke(i -> Log.e("LiteCycle", "onDestroy() invoked " + i))
        .onFinishingUpdate(i -> null)
        .onFinishingInvoke(i -> Log.e("LiteCycle", "isFinishing() invoked " + i))
        .observe();

// listening to the changes done during the life cycle
Observable<Integer> integer = LiteCycle.with(10)
        .forLifeCycle(this)
        .onCreateUpdate(i -> i + 1)
        .onStartUpdate(i -> i + 1)
        .onResumeUpdate(i -> i + 1)
        .onPauseUpdate(i -> i + 1)
        .onStopUpdate(i -> i + 1)
        .onDestroyUpdate(i -> 10)
        .observe();
        
Disposable disposable = integer.subscribe(i -> Log.e("LiteCycle", "integer value " + i));

I was thinking about adding this as an extension function to Any object, like as follows :

10.forLifeCycle(this)
        .onCreateInvoke(i -> Log.e("LiteCycle", "initial value " + i))
        .observe();

and within this extension function, we make it as follows :

fun <T> T.forLifeCycle (lifeCycleOwner : LifeCycleOwner) : LiteObserverBuilder<T,*,*> = 
        LiteCycle.with(this).forLifeCycle(lifeCycleOwner)

Feature suggestion: simplified factory for ViewModel

In order to use constructor injection on the ViewModel, we have to create a ViewModelProvider.Factory implementation, take dependencies as constructor parameter and pass them to the ViewModel constructor:

class ViewModelFactory @Inject constructor(val dependency: Dependency) :
    ViewModelProvider.Factory {
    override fun <T : ViewModel?> create(modelClass: Class<T>) =
        when (modelClass) {
            TaskListViewModel::class -> MyViewModel(dependency) as T
            else -> throw Error("Bad type")
        }
}

However, sometimes it is more convenient to have the factory generated for us (for example by using AutoFactory). Then, the implementation of ViewModelProvider.Factory becomes just a boilerplate which takes the generated factory object as constructor parameter and just call its create() method.

In such case, to reduce the amount of code, we can inline this class:

ViewModelProviders.of(this, object : ViewModelProvider.Factory {
    override fun <T : ViewModel?> create(modelClass: Class<T>) = 
        viewModelFactory.create() as T
}).get(MainViewModel::class.java)

It looks ugly, so let's introduce SimpleFactory class (and this is my feature proposal):

class SimpleFactory<out T : ViewModel?>(val factory: () -> T) :
    ViewModelProvider.Factory {
    override fun <T : ViewModel?> create(modelClass: Class<T>) =
        factory() as T
}

And now, the previous snippet could be simplified to following one:

ViewModelProviders.of(this, SimpleFactory { viewModelFactory.create() })
    .get(MainViewModel::class.java)

A real life example:

class MainActivity : AppCompatActivity() {

    @Inject
    lateinit var viewModelFactory: MainViewModelFactory // One generated by AutoFactory

    private val viewModel by lazy {
        ViewModelProviders.of(this, SimpleFactory { viewModelFactory.create() })
            .get(MainViewModel::class.java)
    }

    // rest of the activity code...
}

What do you think?

IconTest.fromBitmapAdaptive() test fails

java.lang.AssertionError: expected:<189> but was:<188>
at org.junit.Assert.fail(Assert.java:88)
at org.junit.Assert.failNotEquals(Assert.java:834)
at org.junit.Assert.assertEquals(Assert.java:118)
at org.junit.Assert.assertEquals(Assert.java:144)
at androidx.graphics.drawable.IconTest.fromBitmapAdaptive(IconTest.kt:50)
at java.lang.reflect.Method.invoke(Native Method)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runners.Suite.runChild(Suite.java:128)
at org.junit.runners.Suite.runChild(Suite.java:27)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at org.junit.runner.JUnitCore.run(JUnitCore.java:115)
at android.support.test.internal.runner.TestExecutor.execute(TestExecutor.java:58)
at android.support.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:375)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:2074)

Bitmap.toCircularBitmap(borderWidthDp: Int, borderColor: Int): Bitmap

In many projects I needed the same, always doing it manually or using RoundedBitmapDrawable or external libs. I wish something included in the official lib (maybe we already have it and I don't know).

My ugly snippet in java with hardcoded color:

 public static Bitmap getCircularBitmap(Bitmap bitmap, int borderWidth) {
        int width = bitmap.getWidth();
        int height = bitmap.getHeight();
        Bitmap output = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(output);

        final int color = 0xff424242;
        final Paint paint = new Paint();
        final Rect rect = new Rect(0, 0, width, height);

        paint.setAntiAlias(true);
        canvas.drawARGB(0, 0, 0, 0);
        paint.setColor(color);
        canvas.drawCircle(width / 2, height / 2, width / 2, paint);
        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
        canvas.drawBitmap(bitmap, rect, rect, paint);

        // border
        paint.setColor(Color.WHITE);
        paint.setStrokeWidth(borderWidth);
        paint.setStyle(Paint.Style.STROKE);
        paint.setXfermode(null);
        canvas.drawCircle(width / 2, height / 2, width / 2 - borderWidth / 2, paint);
        return output;
    }

TextView.setCompoundDrawable... with default arguments

eg.

@RequiresApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
fun TextView.setCompoundDrawablesRelative(
        start: Drawable? = null,
        top: Drawable? = null,
        end: Drawable? = null,
        bottom: Drawable? = null
) {
    setCompoundDrawablesRelative(start, top, end, bottom)
}
// same for the other, similar methods

Provide a way to obtain a not null Context from a Fragment

Support Library 27 made Fragment.getContext() and Fragment.getActivity() nullable. In order to provide a better API to be consumed in Kotlin, I'd consider creating something like Fragment.getContextOrThrow(), similarly with the already created TypedArray extension functions

TextView.setTextDimen

Useful method to set text size given a R.dimen resource

/**
 * Set this TextView text size with a R.dimen constant
 * 
 * @param dimen R.dimen reference
 */
fun TextView.setTextDimen(@DimenRes dimen: Int) {
  setTextSize(TypedValue.COMPLEX_UNIT_PX, context.resources.getDimension(dimen))
}

If agreed useful, I can submit the PR later.

Problems with contribution

has any one problems with gradle's when you are call:
./gradlew updateApi
./gradlew check
./gradlew connectedCheck
?

I have always one exception:
Error:FAILURE: Build failed with an exception.

  • What went wrong:
    Task './gradlew' not found in root project 'core-ktx'.

I'm only cloned repo, added some functions(2) and call ./gradlew updateApi
If i checkout to last commit and call ./gradlew updateApi again i caught the same error.

Extension for running blocks with permissions in an Activity

I'd love to be able to do the following in an Activities code:

withPermissions(permission.CAMERA) { hasPermissions ->
    if (hasPermissions) {
        openCameraActivity();
    } else {
        warnUserNoPermssions();
    }
}

Where the extension function signature is something like:
inline fun Activity.withPermissions(vararg permissions: String, crossinline block:Activity.(hasPermissions: Boolean) -> Unit)

Also, the withPermissions would ask the user for permission before executing the block if some or all of the permissions were missing.


The flow of the function in order to do this may be too hacky. Let me know. I am envisioning the following steps to pull this off.

Step 1 - ContextCompat.checkSelfPermission for each requested permission
Step 2 - If all permissions allowed call block
Step 3 - Else register lifecycle callback for activity resumed on application context
Step 4 - Request needed permissions
Step 5 - In activity on resumed callback recheck permissions if permissions granted call block()

The activity lifecycle callback is the hacky part because the permission request will trigger transition to another activity to make the request.

Also is it possible to know if the user has clicked do not show permission request in which case always call block(false) when a request is required?

I know this is a bit of hacky request due to the nature of how permission requests are returned in onActivityResult. I'd love to see some sort of callback registering feature added to onActivityResult one day.

What other ways could this be implemented?
What other partial solutions around extensions with permissions could we settle for?

Add Intent.kt for build common intent and check if is safe call startActivity()

Add essential utility for Intents, like:

// http://developer.android.com/training/basics/intents/sending.html#Verify
fun Intent.isIntentSafe(context: Context): Boolean = context.packageManager.queryIntentActivities(this, PackageManager.MATCH_DEFAULT_ONLY).size > 0

// http://developer.android.com/reference/android/content/Intent.html#ACTION_VIEW
fun Uri.viewIntent() = Intent(Intent.ACTION_VIEW).setData(this)

Usage example:

val intent = "market://details?id=$appPackage".toUri().viewIntent()
if (intent.isIntentSafe(context)) {
   startActivity(intent)
} else {
   // cannot start activity
}

Bundle/PersistableBundle/ContentValues creation with Pairs

Issue applies to Bundle, PersistableBundle and ContentValues but for simplicity the example will be given with Bundle.

Consider the following code:

Bundle().apply {
                putString(KEY_ANSWER_ONE, answerOne?.text?.toString())
                putString(KEY_ANSWER_TWO, answerTwo?.text?.toString())
 }

With the Android KTX API, this get transformed to:

bundleOf(Pair(KEY_ANSWER_ONE, answerOne?.text?.toString()),
         Pair(KEY_ANSWER_TWO, answerTwo?.text?.toString()))

There are two issues with this solution:

  1. We create a memory overhead by creating Pair objects, just to "transfer" the fields
  2. It gets easier to make mistakes and put in the Bundle an invalid object, due to the lack of type checks. In the above example, it's easy to put answerOne?.text instead of answerOne?.text?.toString(). If the object is serializable, we end up putting in the Bundle a different type (and possibly a bigger object) and we might have issues when reading the data from the Bundle. In case the object is not serializable, this will only be noticed at run time, when the app will crash.

Return receiver for AnimatorListener-related extensions

Usually we construct animators starting with a call to e.g. ValueAnimator#ofXY, continuing with configuration in builder style - because the api allows it. Until now, and also with the current KTX implementation, this builder would get broken as soon as we want to attach an AnimatorListener (or even an AnimatorUpdateListener for ValueAnimator), making code look super ugly.

Instead of returning the AnimatorListener that we passed in the very same statement (which makes it kinda redundant), we could return the function receiver, the Animator we're adding our listener to, in order to maintain builder flow.

Extension for fragment transaction

An extension on AppCompatActivity

inline fun AppCompatActivity.transact(action: FragmentTransaction.() -> Unit) {
    supportFragmentManager.beginTransaction().apply {
        action()
    }.commit()
}

Inside activity

transact {
    replace(R.id.container, fragment)
    setCustomAnimations(R.anim.abc_fade_in, R.anim.abc_fade_out)
}

This will not directly work for fragment. We may create another extension for fragment or create an extension on Fragment manager.

DP utitilies on Int and Float

Calculating dimensions, such as, SP and DP require the DisplayMetrics, so you can't do something too simple without - say - adding Extensions inside of Activity and such directly. But you can still make some progress similar to the Duration extensions doing something like this:

/**
 * Applies density dimensions to the [Int]
 */
fun Int.toDp(displayMetrics: DisplayMetrics) = toFloat().toDp(displayMetrics).toInt()

/**
 * Applies density dimensions to the [Float]
 */
fun Float.toDp(displayMetrics: DisplayMetrics) = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, this, displayMetrics)

Which would be called like 9.toDp(displayMetrics).

`Toast` extensions on `Context`

I've seemed to add this to each Kotlin project I've worked on

fun Context.shortToast(msg: CharSequence) = Toast.makeText(this, msg, Toast.LENGTH_SHORT).show()

fun Context.longToast(msg: CharSequence) = Toast.makeText(this, msg, Toast.LENGTH_LONG).show()

cleans up the api a bit and avoids the need to remember to call show()

Expose an easier way to enable or disable a system UI flag

This is an extension function I frequently end up writing and using in projects:

@RequiresApi(26)
fun Activity.setNavigationBarStyle() = window.apply {
    decorView.systemUiVisibility = decorView.systemUiVisibility or View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR
  }

(off the top of my head)

The only difference with what I usually end up writing is that this will not handle the API < 26 case (I usually NO-OP in those cases) but instead uses a require API annotation.

Inflater extensions for Context

It would be great to simplify view inflating by property:

val Context.inflater: LayoutInflater get() = LayoutInflater.from(this)

Also it may be useful to extend ViewGroup with inflate():

fun ViewGroup.inflate(@LayoutRes resId: Int, attachToRoot: Boolean = false): View =
        context.inflater.inflate(resId, this, attachToRoot)

So that RecyclerView.Adapter.onCreateViewHolder() may look simplier

return new ViewHolder(parent.inflate(R.layout.item))

or if you're building hierarchy

parent.apply {
    inflate(R.layout.item1, true)
    inflate(R.layout.item2, true)
}

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.