Skip to content

Commit 32a2985

Browse files
committed
Initial commit
0 parents  commit 32a2985

38 files changed

+876
-0
lines changed

.gitignore

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# Windows thumbnail db
2+
Thumbs.db
3+
4+
# OSX files
5+
.DS_Store
6+
7+
# built application files
8+
*.apk
9+
*.ap_
10+
11+
# files for the dex VM
12+
*.dex
13+
14+
# Java class files
15+
*.class
16+
17+
# generated files
18+
bin/
19+
gen/
20+
build/
21+
22+
# Local configuration file (sdk path, etc)
23+
local.properties
24+
25+
# Eclipse project files
26+
.classpath
27+
.project
28+
29+
# Android Studio
30+
.idea
31+
.gradle
32+
/*/local.properties
33+
/*/out
34+
/*/*/build
35+
build
36+
/*/*/production
37+
*.iml
38+
*.iws
39+
*.ipr
40+
*~
41+
*.swp

app/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/build

app/build.gradle

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
apply plugin: 'com.android.application'
2+
3+
apply plugin: 'kotlin-android'
4+
5+
apply plugin: 'kotlin-android-extensions'
6+
7+
android {
8+
compileSdkVersion 28
9+
defaultConfig {
10+
applicationId "thecloudhub.com.digitaloceanspacesexample"
11+
minSdkVersion 19
12+
targetSdkVersion 28
13+
versionCode 1
14+
versionName "1.0"
15+
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
16+
}
17+
buildTypes {
18+
release {
19+
minifyEnabled false
20+
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
21+
}
22+
}
23+
}
24+
25+
dependencies {
26+
implementation fileTree(dir: 'libs', include: ['*.jar'])
27+
implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
28+
implementation 'com.android.support:appcompat-v7:28.0.0'
29+
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
30+
implementation 'com.android.support:design:28.0.0'
31+
testImplementation 'junit:junit:4.12'
32+
androidTestImplementation 'com.android.support.test:runner:1.0.2'
33+
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
34+
35+
//Add AWS S3 SDK
36+
implementation 'com.amazonaws:aws-android-sdk-s3:2.12.5'
37+
}

app/proguard-rules.pro

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Add project specific ProGuard rules here.
2+
# You can control the set of applied configuration files using the
3+
# proguardFiles setting in build.gradle.
4+
#
5+
# For more details, see
6+
# http://developer.android.com/guide/developing/tools/proguard.html
7+
8+
# If your project uses WebView with JS, uncomment the following
9+
# and specify the fully qualified class name to the JavaScript interface
10+
# class:
11+
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12+
# public *;
13+
#}
14+
15+
# Uncomment this to preserve the line number information for
16+
# debugging stack traces.
17+
#-keepattributes SourceFile,LineNumberTable
18+
19+
# If you keep the line number information, uncomment this to
20+
# hide the original source file name.
21+
#-renamesourcefileattribute SourceFile
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package thecloudhub.com.digitaloceanspacesexample
2+
3+
import android.support.test.InstrumentationRegistry
4+
import android.support.test.runner.AndroidJUnit4
5+
6+
import org.junit.Test
7+
import org.junit.runner.RunWith
8+
9+
import org.junit.Assert.*
10+
11+
/**
12+
* Instrumented test, which will execute on an Android device.
13+
*
14+
* See [testing documentation](http://d.android.com/tools/testing).
15+
*/
16+
@RunWith(AndroidJUnit4::class)
17+
class ExampleInstrumentedTest {
18+
@Test
19+
fun useAppContext() {
20+
// Context of the app under test.
21+
val appContext = InstrumentationRegistry.getTargetContext()
22+
assertEquals("thecloudhub.com.digitaloceanspacesexample", appContext.packageName)
23+
}
24+
}

app/src/main/AndroidManifest.xml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
3+
package="thecloudhub.com.digitaloceanspacesexample">
4+
5+
<application
6+
android:allowBackup="true"
7+
android:icon="@mipmap/ic_launcher"
8+
android:label="@string/app_name"
9+
android:roundIcon="@mipmap/ic_launcher_round"
10+
android:supportsRtl="true"
11+
android:theme="@style/AppTheme">
12+
<activity
13+
android:name=".MainActivity"
14+
android:label="@string/app_name"
15+
android:theme="@style/AppTheme.NoActionBar">
16+
<intent-filter>
17+
<action android:name="android.intent.action.MAIN"/>
18+
19+
<category android:name="android.intent.category.LAUNCHER"/>
20+
</intent-filter>
21+
</activity>
22+
</application>
23+
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
24+
<uses-permission android:name="android.permission.INTERNET" />
25+
</manifest>
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package thecloudhub.com.digitaloceanspacesexample
2+
3+
import android.net.Uri
4+
import android.os.Bundle
5+
import android.support.v7.app.AppCompatActivity
6+
import android.view.Menu
7+
import android.view.MenuItem
8+
9+
import kotlinx.android.synthetic.main.activity_main.*
10+
import kotlinx.android.synthetic.main.content_main.*
11+
12+
class MainActivity : AppCompatActivity() {
13+
14+
override fun onCreate(savedInstanceState: Bundle?) {
15+
super.onCreate(savedInstanceState)
16+
setContentView(R.layout.activity_main)
17+
setSupportActionBar(toolbar)
18+
val spacesFileRepository = SpacesFileRepository(applicationContext)
19+
20+
btn_download.setOnClickListener {
21+
spacesFileRepository.downloadExampleFile { file, _ ->
22+
runOnUiThread {
23+
imageView.setImageURI(Uri.fromFile(file))
24+
}
25+
}
26+
}
27+
28+
btn_upload.setOnClickListener {
29+
spacesFileRepository.uploadExampleFile()
30+
}
31+
}
32+
33+
override fun onCreateOptionsMenu(menu: Menu): Boolean {
34+
// Inflate the menu; this adds items to the action bar if it is present.
35+
menuInflater.inflate(R.menu.menu_main, menu)
36+
return true
37+
}
38+
39+
override fun onOptionsItemSelected(item: MenuItem): Boolean {
40+
// Handle action bar item clicks here. The action bar will
41+
// automatically handle clicks on the Home/Up button, so long
42+
// as you specify a parent activity in AndroidManifest.xml.
43+
return when (item.itemId) {
44+
R.id.action_settings -> true
45+
else -> super.onOptionsItemSelected(item)
46+
}
47+
}
48+
}
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
package thecloudhub.com.digitaloceanspacesexample
2+
3+
import android.content.Context
4+
import android.graphics.Bitmap
5+
import android.graphics.BitmapFactory
6+
import android.util.Log
7+
import com.amazonaws.auth.BasicAWSCredentials
8+
import com.amazonaws.internal.StaticCredentialsProvider
9+
import com.amazonaws.mobileconnectors.s3.transferutility.TransferListener
10+
import com.amazonaws.mobileconnectors.s3.transferutility.TransferState
11+
import com.amazonaws.mobileconnectors.s3.transferutility.TransferUtility
12+
import com.amazonaws.regions.Region
13+
import com.amazonaws.services.s3.AmazonS3Client
14+
import java.io.ByteArrayOutputStream
15+
import java.io.File
16+
import java.io.FileOutputStream
17+
import java.lang.Exception
18+
import java.util.*
19+
20+
interface SpaceRegionRepresentable {
21+
fun endpoint(): String
22+
}
23+
24+
/**
25+
* Represents a region in which a Digital Ocean Space can be created
26+
*/
27+
enum class SpaceRegion: SpaceRegionRepresentable {
28+
SFO {
29+
override fun endpoint(): String {
30+
return "https://sfo2.digitaloceanspaces.com"
31+
}
32+
}, AMS {
33+
override fun endpoint(): String {
34+
return "https://ams3.digitaloceanspaces.com"
35+
}
36+
}, SGP {
37+
override fun endpoint(): String {
38+
return "https://sgp1.digitaloceanspaces.com"
39+
}
40+
}
41+
}
42+
43+
class SpacesFileRepository (context: Context) {
44+
private val accesskey = "YOUR_KEY_HERE"
45+
private val secretkey = "YOUR_SECRET_HERE"
46+
private val spacename = "YOUR_BUCKET_NAME_HERE"
47+
private val spaceregion = SpaceRegion.SFO
48+
49+
private val filename = "example_image"
50+
private val filetype = "jpg"
51+
52+
private var transferUtility: TransferUtility
53+
private var appContext: Context
54+
55+
init {
56+
val credentials = StaticCredentialsProvider(BasicAWSCredentials(accesskey, secretkey))
57+
val client = AmazonS3Client(credentials, Region.getRegion("us-east-1"))
58+
client.endpoint = spaceregion.endpoint()
59+
60+
transferUtility = TransferUtility.builder().s3Client(client).context(context).build()
61+
appContext = context
62+
}
63+
64+
/**
65+
* Converts a APK resource to a file for uploading with the S3 SDK
66+
*/
67+
private fun convertResourceToFile(): File {
68+
val exampleIdentifier = appContext.resources.getIdentifier(filename, "drawable", appContext.packageName)
69+
val exampleBitmap = BitmapFactory.decodeResource(appContext.resources, exampleIdentifier)
70+
71+
val exampleFile = File(appContext.filesDir, Date().toString())
72+
exampleFile.createNewFile()
73+
74+
val outputStream = ByteArrayOutputStream()
75+
exampleBitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream)
76+
val exampleBitmapData = outputStream.toByteArray()
77+
78+
val fileOutputStream = FileOutputStream(exampleFile)
79+
fileOutputStream.write(exampleBitmapData)
80+
fileOutputStream.flush()
81+
fileOutputStream.close()
82+
83+
return exampleFile
84+
}
85+
86+
/**
87+
* Uploads the example file to a DO Space
88+
*/
89+
fun uploadExampleFile(){
90+
//Starts the upload of our file
91+
var listener = transferUtility.upload(spacename, "$filename.$filetype", convertResourceToFile())
92+
93+
//Listens to the file upload progress, or any errors that might occur
94+
listener.setTransferListener(object: TransferListener {
95+
override fun onError(id: Int, ex: Exception?) {
96+
Log.e("S3 Upload", ex.toString())
97+
}
98+
99+
override fun onProgressChanged(id: Int, bytesCurrent: Long, bytesTotal: Long) {
100+
Log.i("S3 Upload", "Progress ${((bytesCurrent/bytesTotal)*100)}")
101+
}
102+
103+
override fun onStateChanged(id: Int, state: TransferState?) {
104+
if (state == TransferState.COMPLETED){
105+
Log.i("S3 Upload", "Completed")
106+
}
107+
}
108+
})
109+
}
110+
111+
/**
112+
* Downloads example file from a DO Space
113+
*/
114+
fun downloadExampleFile(callback: (File?, Exception?) -> Unit) {
115+
//Create a local File object to save the remote file to
116+
val file = File("${appContext.cacheDir}/$filename.$filetype")
117+
118+
//Download the file from DO Space
119+
var listener = transferUtility.download(spacename, "$filename.$filetype", file)
120+
121+
//Listen to the progress of the download, and call the callback when the download is complete
122+
listener.setTransferListener(object: TransferListener {
123+
override fun onProgressChanged(id: Int, bytesCurrent: Long, bytesTotal: Long) {
124+
Log.i("S3 Download", "Progress ${((bytesCurrent/bytesTotal)*100)}")
125+
}
126+
127+
override fun onStateChanged(id: Int, state: TransferState?) {
128+
if (state == TransferState.COMPLETED){
129+
Log.i("S3 Download", "Completed")
130+
callback(file, null)
131+
}
132+
}
133+
134+
override fun onError(id: Int, ex: Exception?) {
135+
Log.e("S3 Download", ex.toString())
136+
callback(null, ex)
137+
}
138+
})
139+
}
140+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
2+
xmlns:aapt="http://schemas.android.com/aapt"
3+
android:width="108dp"
4+
android:height="108dp"
5+
android:viewportHeight="108"
6+
android:viewportWidth="108">
7+
<path
8+
android:fillType="evenOdd"
9+
android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
10+
android:strokeColor="#00000000"
11+
android:strokeWidth="1">
12+
<aapt:attr name="android:fillColor">
13+
<gradient
14+
android:endX="78.5885"
15+
android:endY="90.9159"
16+
android:startX="48.7653"
17+
android:startY="61.0927"
18+
android:type="linear">
19+
<item
20+
android:color="#44000000"
21+
android:offset="0.0"/>
22+
<item
23+
android:color="#00000000"
24+
android:offset="1.0"/>
25+
</gradient>
26+
</aapt:attr>
27+
</path>
28+
<path
29+
android:fillColor="#FFFFFF"
30+
android:fillType="nonZero"
31+
android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
32+
android:strokeColor="#00000000"
33+
android:strokeWidth="1"/>
34+
</vector>
293 KB
Loading

0 commit comments

Comments
 (0)