diff --git a/api/current.txt b/api/current.txt index f2c469ae..6aa6f9d8 100644 --- a/api/current.txt +++ b/api/current.txt @@ -16,6 +16,14 @@ package androidx.animation { package androidx.content { + public final class ActivityKt { + ctor public ActivityKt(); + method @RequiresApi(19) public static final void getFileFromFilesystem(android.app.Activity, int requestCode = "22", String type = "\"image/*\""); + method public static final void getImageFromCamera(android.app.Activity, int requestCode = "23"); + method public static final android.graphics.Rect getScreenRect(android.app.Activity); + method public static final void viewLink(android.app.Activity, String link = "\"http://www.google.com\""); + } + public final class ContentValuesKt { ctor public ContentValuesKt(); method public static final error.NonExistentClass contentValuesOf(kotlin.Pair... pairs); @@ -23,6 +31,14 @@ package androidx.content { public final class ContextKt { ctor public ContextKt(); + method public static final android.graphics.Bitmap! createBitmapFromFile(android.content.Context, android.net.Uri file) throws java.io.FileNotFoundException, java.io.IOException; + method public static final java.io.File? createTempFileFromBitmap(android.content.Context, android.graphics.Bitmap? image, String fileName = "\"image_${System.currentTimeMillis()}.jpg\""); + method public static final android.graphics.Bitmap? getImageAssetAsBitmap(android.content.Context, String path); + method public static final android.graphics.drawable.Drawable? getImageAssetAsDrawable(android.content.Context, String path); + method public static final float getScreenAspectRatio(android.content.Context); + method public static final android.graphics.Point getScreenPointDimens(android.content.Context); + method public static final String? getStringAsset(android.content.Context, String path, String charset = "\"UTF-8\"") throws java.io.IOException; + method public static final android.view.View inflateView(android.content.Context, @LayoutRes int layout, android.view.ViewGroup? root = "null", boolean attachToRoot = "false"); method public static final void withStyledAttributes(android.content.Context, android.util.AttributeSet? set = "null", int[] attrs, @AttrRes int defStyleAttr = "0", @StyleRes int defStyleRes = "0", kotlin.jvm.functions.Function1 block); method public static final void withStyledAttributes(android.content.Context, @StyleRes int resourceId, int[] attrs, kotlin.jvm.functions.Function1 block); } diff --git a/src/androidTest/assets/test_text.txt b/src/androidTest/assets/test_text.txt new file mode 100644 index 00000000..3e8cd294 --- /dev/null +++ b/src/androidTest/assets/test_text.txt @@ -0,0 +1 @@ +android-ktx \ No newline at end of file diff --git a/src/androidTest/java/androidx/content/ActivityTest.kt b/src/androidTest/java/androidx/content/ActivityTest.kt new file mode 100644 index 00000000..a0cc4326 --- /dev/null +++ b/src/androidTest/java/androidx/content/ActivityTest.kt @@ -0,0 +1,46 @@ +/* + * 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.content + +import android.app.Activity +import android.content.Intent +import android.graphics.Rect +import android.net.Uri +import android.provider.MediaStore +import android.support.annotation.RequiresApi +import android.support.test.InstrumentationRegistry +import android.support.test.rule.ActivityTestRule +import androidx.kotlin.TestActivity +import org.junit.Assert.assertEquals +import org.junit.Rule +import org.junit.Test + +class ActivityTest { + + @JvmField + @Rule + val rule = ActivityTestRule(TestActivity::class.java) + + @Test + fun testScreenRect() { + val displayRectangle = Rect() + rule.activity.window.decorView.getWindowVisibleDisplayFrame(displayRectangle) + InstrumentationRegistry.getInstrumentation().waitForIdleSync() + assertEquals(rule.activity.screenRect, displayRectangle) + } + +} diff --git a/src/androidTest/java/androidx/content/ContextTest.kt b/src/androidTest/java/androidx/content/ContextTest.kt index 4ea5406a..c4f27dcd 100644 --- a/src/androidTest/java/androidx/content/ContextTest.kt +++ b/src/androidTest/java/androidx/content/ContextTest.kt @@ -16,15 +16,28 @@ package androidx.content +import android.content.Context +import android.graphics.Bitmap +import android.graphics.BitmapFactory +import android.graphics.Point +import android.graphics.drawable.Drawable +import android.provider.MediaStore import android.support.test.InstrumentationRegistry import android.support.test.filters.SdkSuppress import android.test.mock.MockContext +import android.view.LayoutInflater +import android.view.WindowManager import androidx.getAttributeSet +import androidx.graphics.drawable.toBitmap import androidx.kotlin.test.R +import androidx.os.toUri import org.junit.Assert.assertEquals import org.junit.Assert.assertSame import org.junit.Assert.assertTrue import org.junit.Test +import java.io.File +import java.io.FileOutputStream +import java.nio.charset.Charset class ContextTest { private val context = InstrumentationRegistry.getContext() @@ -70,4 +83,75 @@ class ContextTest { assertTrue(getInt(R.styleable.SampleAttrs_sample, -1) != -1) } } + + @Test + fun testScreenPointDimens() { + val point = Point() + (context.getSystemService(Context.WINDOW_SERVICE) as WindowManager).defaultDisplay + .getSize(point) + val testPoint = context.screenPointDimens + assertEquals(point.x, testPoint.x) + assertEquals(point.y, testPoint.y) + } + + @Test + fun testScreenAspectRatio() { + val point = context.screenPointDimens + val aspectRatio = point.x.toFloat() / point.y.toFloat() + val testAspectRatio = context.screenAspectRatio + assertEquals(aspectRatio, testAspectRatio) + } + + @Test + fun testGetStringAsset() { + val inputStream = context.assets.open("test_text.txt") + val size = inputStream.available() + val buffer = ByteArray(size) + inputStream.read(buffer) + inputStream.close() + val resultString = String(buffer, Charset.forName("UTF-8")) + val testString = context.getStringAsset("test_text.txt") + assertEquals(resultString, testString) + } + + @Test + fun testGetImageAssetAsBitmap() { + val ims = context.assets.open("red.png") + val bitmap = BitmapFactory.decodeStream(ims) + val testBitmap = context.getImageAssetAsBitmap("red.png") + assertEquals(bitmap.byteCount, testBitmap?.byteCount) + } + + @Test + fun testImageAssetAsDrawable() { + val ims = context.assets.open("red.png") + val drawable = Drawable.createFromStream(ims, "red.png") + val testDrawable = context.getImageAssetAsDrawable("red.png") + assertEquals(drawable.toBitmap().byteCount, testDrawable?.toBitmap()?.byteCount) + } + + @Test + fun testCreateTempFileFromBitmapAndViceVersa() { + val image = context.getImageAssetAsBitmap("red.png") + val fileTemp = File(context.externalCacheDir, "test.jpg") + fileTemp.createNewFile() + val fos = FileOutputStream(fileTemp) + image?.compress(Bitmap.CompressFormat.JPEG, 90, fos) + fos.close() + val file = fileTemp + val testFileUri = context.createTempFileFromBitmap(image) + assertEquals(file.exists(), testFileUri?.exists()) + assertEquals(file.length(), testFileUri?.length()) + val bitmap = MediaStore.Images.Media.getBitmap(context.contentResolver, file.toUri()) + val testBitmap = context.createBitmapFromFile(file.toUri()) + assertEquals(bitmap.byteCount, testBitmap?.byteCount) + context.externalCacheDir.delete() + } + + @Test + fun testInflateView() { + val view = LayoutInflater.from(context).inflate(R.layout.test_activity, null, false) + val testView = context.inflateView(R.layout.test_activity) + assertEquals(view.drawableState, testView.drawableState) + } } diff --git a/src/main/java/androidx/content/Activity.kt b/src/main/java/androidx/content/Activity.kt new file mode 100644 index 00000000..a7fc76c0 --- /dev/null +++ b/src/main/java/androidx/content/Activity.kt @@ -0,0 +1,60 @@ +/* + * 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.content + +import android.app.Activity +import android.content.Intent +import android.graphics.Rect +import android.net.Uri +import android.provider.MediaStore +import android.support.annotation.RequiresApi + +/** + * Screen [Rect] + */ +val Activity.screenRect: Rect + get() { + val displayRectangle = Rect() + window.decorView.getWindowVisibleDisplayFrame(displayRectangle) + return displayRectangle + } + +/** + * Opens link in new window of an app that can view Internet links + */ +fun Activity.viewLink(link: String = "http://www.google.com") { + startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(link))) +} + +/** + * Opens filesystem file picker. Result is delivered into onActivityResult + * Requires runtime permission [android.Manifest.permission.READ_EXTERNAL_STORAGE] + */ +@RequiresApi(19) +fun Activity.getFileFromFilesystem(requestCode: Int = 22, type: String = "image/*") { + val intent = Intent(Intent.ACTION_OPEN_DOCUMENT) + intent.addCategory(Intent.CATEGORY_OPENABLE) + intent.type = type + startActivityForResult(intent, requestCode) +} + +/** + * Opens default camera. Result is delivered into onActivityResult + * Requires runtime permission [android.Manifest.permission.CAMERA] + */ +fun Activity.getImageFromCamera(requestCode: Int = 23) = + startActivityForResult(Intent(MediaStore.ACTION_IMAGE_CAPTURE), requestCode) diff --git a/src/main/java/androidx/content/Context.kt b/src/main/java/androidx/content/Context.kt index 128f0602..877bdb52 100644 --- a/src/main/java/androidx/content/Context.kt +++ b/src/main/java/androidx/content/Context.kt @@ -18,10 +18,27 @@ package androidx.content import android.content.Context import android.content.res.TypedArray +import android.graphics.Bitmap +import android.graphics.BitmapFactory +import android.graphics.Point +import android.graphics.drawable.Drawable +import android.net.Uri +import android.provider.MediaStore import android.support.annotation.AttrRes +import android.support.annotation.LayoutRes import android.support.annotation.RequiresApi import android.support.annotation.StyleRes import android.util.AttributeSet +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.view.WindowManager +import java.io.File +import java.io.FileNotFoundException +import java.io.FileOutputStream +import java.io.IOException +import java.net.MalformedURLException +import java.nio.charset.Charset /** * Return the handle to a system-level service by class. @@ -91,3 +108,100 @@ inline fun Context.withStyledAttributes( typedArray.recycle() } } + +/** + * Screen [Point] dimens + */ +val Context.screenPointDimens: Point + get() { + val point = Point() + (getSystemService(Context.WINDOW_SERVICE) as WindowManager).defaultDisplay + .getSize(point) + return point + } + +/** + * Screen aspect ration + */ +val Context.screenAspectRatio: Float + get() { + val point = screenPointDimens + return point.x.toFloat() / point.y.toFloat() + } + +/** + * Return string asset for given [path] and [charset] + * + * Throws [IOException] + */ +@Throws(IOException::class) +fun Context.getStringAsset(path: String, charset: String = "UTF-8"): String? { + val inputStream = assets.open(path) + val size = inputStream.available() + val buffer = ByteArray(size) + inputStream.read(buffer) + inputStream.close() + return String(buffer, Charset.forName(charset)) +} + +/** + * Return image asset for given [path] and [charset] like [Bitmap] + */ +fun Context.getImageAssetAsBitmap(path: String): Bitmap?{ + val ims = assets.open(path) + return BitmapFactory.decodeStream(ims) +} + +/** + * Return image asset for given [path] and [charset] like [Drawable] + */ +fun Context.getImageAssetAsDrawable(path: String): Drawable?{ + val ims = assets.open(path) + return Drawable.createFromStream(ims, path) +} + +/** + * Creates temp file from given bitmap. All exception are caught - returns null in case of exception + */ +fun Context.createTempFileFromBitmap( + image: Bitmap?, + fileName: String = "image_${System.currentTimeMillis()}.jpg" +): File? { + try { + val fileTemp = File(externalCacheDir, fileName) + fileTemp.createNewFile() + val fos = FileOutputStream(fileTemp) + image?.compress(Bitmap.CompressFormat.JPEG, 90, fos) + fos.close() + return fileTemp + } catch (e: FileNotFoundException) { + e.printStackTrace() + return null + } catch (e: MalformedURLException) { + e.printStackTrace() + return null + } catch (e: IOException) { + e.printStackTrace() + return null + } +} + +/** + * Creates bitmap from Uri of image file + * + * Throws [FileNotFoundException] and [IOException] + */ +@Suppress("HasPlatformType") +@Throws(FileNotFoundException::class, IOException::class) +fun Context.createBitmapFromFile(file: Uri) = MediaStore.Images.Media.getBitmap(contentResolver, file) + +/** + * Inflates view for given [layout] + * + * Throws [IOException] + */ +fun Context.inflateView( + @LayoutRes layout: Int, root: ViewGroup? = null, + attachToRoot: Boolean = false +): View = + LayoutInflater.from(this).inflate(layout, root, attachToRoot) \ No newline at end of file diff --git a/src/main/java/androidx/os/File.kt b/src/main/java/androidx/os/File.kt index e6863bfa..02f73e81 100644 --- a/src/main/java/androidx/os/File.kt +++ b/src/main/java/androidx/os/File.kt @@ -24,6 +24,6 @@ import java.io.File /** * Creates a Uri from the given file. * - * @see Uri.parse + * @see Uri.fromFile */ inline fun File.toUri(): Uri = Uri.fromFile(this)