Skip to content
This repository was archived by the owner on Nov 14, 2018. It is now read-only.
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 api/current.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,16 @@ package androidx.core.animation {

package androidx.core.content {

public final class ClipDataKt {
ctor public ClipDataKt();
method public static operator boolean contains(android.content.ClipData, android.content.ClipData.Item item);
method public static void forEach(android.content.ClipData, kotlin.jvm.functions.Function1<? super android.content.ClipData.Item,kotlin.Unit> action);
method public static void forEachIndexed(android.content.ClipData, kotlin.jvm.functions.Function2<? super java.lang.Integer,? super android.content.ClipData.Item,kotlin.Unit> action);
method public static operator android.content.ClipData.Item get(android.content.ClipData, int index);
method public static kotlin.sequences.Sequence<android.content.ClipData.Item> getItems(android.content.ClipData);
method public static operator java.util.Iterator<android.content.ClipData.Item> iterator(android.content.ClipData);
}

public final class ContentValuesKt {
ctor public ContentValuesKt();
method public static error.NonExistentClass contentValuesOf(kotlin.Pair<java.lang.String,?>... pairs);
Expand Down
163 changes: 163 additions & 0 deletions src/androidTest/java/androidx/core/content/ClipDataTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package androidx.core.content

import android.content.ClipData
import android.content.Intent
import android.net.Uri
import android.support.test.InstrumentationRegistry
import androidx.testutils.assertThrows
import com.google.common.truth.Truth.assertThat
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertSame
import org.junit.Assert.assertNotSame
import org.junit.Assert.assertTrue
import org.junit.Test

class ClipDataTest {
private val clip = ClipData.newPlainText("", "")
private val context = InstrumentationRegistry.getContext()

@Test fun get() {
val item = ClipData.Item("")
clip.addItem(item)

// ClipData.newPlainText will instantiate
// an Intent within its initial Item
// so item.equals(clip[0]) will return false
assertEquals(item.text, clip[0].text)
assertEquals(item.uri, clip[0].uri)
assertEquals(item.htmlText, clip[0].htmlText)
val intent = clip[0].intent
if (intent != null) {
assertNotSame(intent, item.intent)
}

assertSame(item, clip[1])
}

@Test fun contains() {
val item1 = ClipData.Item("")
clip.addItem(item1)
assertTrue(item1 in clip)

val item2 = ClipData.Item("")
clip.addItem(item2)
assertTrue(item2 in clip)
}

@Test fun forEach() {
val item1 = ClipData.Item("")
clip.addItem(item1)
val item2 = ClipData.Item("")
clip.addItem(item2)

val items = mutableListOf<ClipData.Item>()
clip.forEach {
items += it
}
assertEquals(3, items.size)
assertThat(items).containsAllOf(item1, item2)
}

@Test fun forEachIndexed() {
val item1 = ClipData.Item("")
clip.addItem(item1)
val item2 = ClipData.Item("")
clip.addItem(item2)

val items = mutableListOf<ClipData.Item>()
clip.forEachIndexed { index, item ->
assertEquals(index, items.size)
items += item
}
assertEquals(3, items.size)
assertThat(items).containsAllOf(item1, item2)
}

@Test fun iterator() {
val item1 = ClipData.Item("")
clip.addItem(item1)
val item2 = ClipData.Item("")
clip.addItem(item2)

val iterator = clip.iterator()
assertTrue(iterator.hasNext())
iterator.next()
assertSame(item1, iterator.next())
assertTrue(iterator.hasNext())
assertSame(item2, iterator.next())
assertFalse(iterator.hasNext())
assertThrows<IndexOutOfBoundsException> {
iterator.next()
}
}

@Test fun items() {
val itemsList = listOf(ClipData.Item(""), ClipData.Item(""))
itemsList.forEach { clip.addItem(it) }

clip.items.forEachIndexed { index, item ->
if (index != 0) {
assertSame(itemsList[index - 1], item)
}
}
}

@Test fun map() {
clip.addItem(ClipData.Item("item1"))
clip.addItem(ClipData.Item("item2"))

val items = clip.map { item -> item.text }
assertThat(items).containsExactly("", "item1", "item2")
}

@Test fun clipDataOfValid() {
clip.addItem(ClipData.Item("item1"))
clip.addItem(ClipData.Item("item2"))

val l = listOf("", "item1", "item2")
val c = clipDataOf(l, "strings")
val items = c.map { item -> item.text }
assertThat(items).containsExactlyElementsIn(l)

val uris = listOf(Uri.parse("content://uri1"),
Uri.parse("content://uri2"),
Uri.parse("content://uri3"))
val cU = clipDataOf(uris, "uris")
val iU = cU.map { item -> item.uri }
assertThat(iU).containsExactlyElementsIn(uris)

val intents = listOf(Intent("com.androidx.action", Uri.parse("content://uri1")),
Intent("com.androidx.action", Uri.parse("content://uri2")),
Intent("com.androidx.action", Uri.parse("content://uri3")))
val cI = clipDataOf(intents, "intents")
val iI = cI.map { item -> item.intent }
assertThat(iI).containsExactlyElementsIn(intents)
}

@Test fun clipDataOfInvalid() {
assertThrows<IllegalArgumentException> {
clipDataOf(listOf<String>(), "empty")
}.hasMessageThat().isEqualTo("Illegal argument, list cannot be empty.")

assertThrows<IllegalArgumentException> {
clipDataOf(listOf(1, 2, 3), "ints")
}.hasMessageThat().isEqualTo("Illegal type: java.lang.Integer")
}
}
127 changes: 127 additions & 0 deletions src/main/java/androidx/core/content/ClipData.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

@file:Suppress("NOTHING_TO_INLINE")

package androidx.core.content

import android.content.ClipData
import android.content.ContentResolver
import android.content.Intent
import android.net.Uri

/**
* Returns the ClipData.Item at [index].
*
* @throws IndexOutOfBoundsException if index is less than 0 or greater than or equal to the count.
*/
inline operator fun ClipData.get(index: Int): ClipData.Item = getItemAt(index)

/** Returns `true` if [item] is found in this ClipData. */
operator fun ClipData.contains(item: ClipData.Item): Boolean {
@Suppress("LoopToCallChain")
for (index in 0 until itemCount) {
if (getItemAt(index) == item) {
return true
}
}
return false
}

/** Performs the given action on each item in this ClipData. */
inline fun ClipData.forEach(action: (item: ClipData.Item) -> Unit) {
for (index in 0 until itemCount) {
action(getItemAt(index))
}
}

/** Performs the given action on each item in this ClipData, providing its sequential index. */
inline fun ClipData.forEachIndexed(action: (index: Int, item: ClipData.Item) -> Unit) {
for (index in 0 until itemCount) {
action(index, getItemAt(index))
}
}

/**
* Returns an [Iterator] over the items in this ClipData.
* (NOTE: ClipData doesn't allow removal of items.)
**/
operator fun ClipData.iterator() = object : Iterator<ClipData.Item> {
private var index = 0
override fun hasNext() = index < itemCount
override fun next() = getItemAt(index++) ?: throw IndexOutOfBoundsException()
}

/** Returns a [Sequence] over the items in this ClipData. */
val ClipData.items: Sequence<ClipData.Item>
get() = object : Sequence<ClipData.Item> {
override fun iterator() = this@items.iterator()
}

/**
* Returns a [List] containing the results of applying the given transform function to each
* item in this ClipData.
*/
inline fun <reified T> ClipData.map(transform: (ClipData.Item) -> T): List<T> {
var m = mutableListOf<T>()
for (i in 0 until itemCount) {
m.add(transform(getItemAt(i)))
}
return m.toList()
}

/**
* Returns a new [ClipData] with given list of items.
* NOTE: HtmlText ClipData not supported.
*
* @throws IllegalArgumentException When the list doesn't contain items of type supported
* by [ClipData].
*/
inline fun <reified T> clipDataOf(
l: List<T>,
label: String = "",
cr: ContentResolver? = null
): ClipData = if (l.isEmpty()) {
throw IllegalArgumentException("Illegal argument, list cannot be empty.")
} else when {
Uri::class.java.isAssignableFrom(T::class.java) ->
if (cr == null) {
ClipData.newRawUri(label, l[0] as Uri).apply {
l.forEachIndexed { index, item ->
if (index > 0) addItem(ClipData.Item(item as Uri))
}
}
} else {
ClipData.newUri(cr, label, l[0] as Uri).apply {
l.forEachIndexed { index, item ->
if (index > 0) addItem(ClipData.Item(item as Uri))
}
}
}
CharSequence::class.java.isAssignableFrom(T::class.java) ->
ClipData.newPlainText(label, l[0] as CharSequence).apply {
l.forEachIndexed { index, item ->
if (index > 0) addItem(ClipData.Item(item as CharSequence))
}
}
Intent::class.java.isAssignableFrom(T::class.java) ->
ClipData.newIntent(label, l[0] as Intent).apply {
l.forEachIndexed { index, item ->
if (index > 0) addItem(ClipData.Item(item as Intent))
}
}
else -> throw IllegalArgumentException("Illegal type: ${T::class.java.canonicalName}")
}