So you want a secure storage. Right?
Tags: android, java, aws, codenewbie
Published: 2020-09-08 21:08:41

Wouldn't be great if your App users can generate contents and store them securely?


<img class="aligncenter size-full wp-image-724" src="http://www.offlineprogrammer.com/wp-content/uploads/2020/09/Screen_Shot_2020-09-08_at_12.21.54_PM.png" alt="" width="540" height="418" />


AWS Amplify is here to help.


<img class="aligncenter size-full" src="https://media.giphy.com/media/81xwEHX23zhvy/giphy.gif" width="480" height="260" />

Today I am going to show you how to use AWS Amplify storage to upload and view photos on your Android App. We will add this feature to the MyAuthApplication App we created on the first post of the series {% link https://dev.to/offlineprogrammer/login-with-amazon-using-aws-amplify-auth-3bmh %} 
<ul>
 	<li>Update build.gradle (Project: MyAuthApplication) as below</li>
</ul>
```sh
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
    repositories {
        google()
        jcenter()

        maven { url '&lt;https://jitpack.io&gt;' }
    }
    dependencies {
        classpath "com.android.tools.build:gradle:4.0.1"

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

allprojects {
    repositories {
        google()
        jcenter()

       
        maven { url '&lt;https://jitpack.io&gt;' }

    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}
```
<ul>
 	<li>Update build.gradle (Module:app) as below</li>
</ul>
```sh
apply plugin: 'com.android.application'

android {
    compileSdkVersion 29

    defaultConfig {
        applicationId "com.offlineprogrammer.myauthapplication"
        minSdkVersion 23
        targetSdkVersion 29
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}

dependencies {
    implementation fileTree(dir: "libs", include: ["*.jar"])
    implementation 'androidx.appcompat:appcompat:1.2.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.0.1'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test.ext:junit:1.1.2'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'

    implementation 'com.google.android.material:material:1.2.1'
    implementation 'androidx.recyclerview:recyclerview:1.1.0'

    implementation 'com.amplifyframework:core:1.1.2'
    implementation 'com.amplifyframework:aws-storage-s3:1.1.2'
    implementation 'com.amplifyframework:aws-auth-cognito:1.1.2'

    implementation 'com.github.esafirm.android-image-picker:imagepicker:2.3.0'
    implementation 'com.github.esafirm.android-image-picker:rximagepicker:2.3.0'
    implementation 'com.github.bumptech.glide:glide:4.11.0'
    annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'

}
```
<ul>
 	<li>Make sure to run Gradle Sync</li>
 	<li>Update the MyAuthApplication class to add the Storage Plugin as below</li>
</ul>
```java
public class MyAuthApplication  extends Application {
    private static final String TAG = "MyAuthApplication";

    @Override
    public void onCreate() {
        super.onCreate();

        try {
            Amplify.addPlugin(new AWSCognitoAuthPlugin());
            Amplify.addPlugin(new AWSS3StoragePlugin());

            Amplify.configure(getApplicationContext());

            Log.i(TAG, "Initialized Amplify");
        } catch (AmplifyException error) {
            Log.e(TAG, "Could not initialize Amplify", error);
        }

    }
}
```
<ul>
 	<li>Run the command below to configure the Storage Category</li>
</ul>
```sh
amplify add storage
```
<ul>
 	<li>Set the details below when prompted</li>
</ul>
```sh
Please select from one of the below mentioned services: Content (Images, audio, video, etc.)
? Please provide a friendly name for your resource that will be used to label this category in the project: s34b23f393
? Please provide bucket name: myauthapplication00496853c7404e93a752634a073f09
? Who should have access: Auth users only
? What kind of access do you want for Authenticated users? create/update, read, delete
? Do you want to add a Lambda Trigger for your S3 Bucket? No
Successfully added resource s34b23f393 locally

If a user is part of a user pool group, run "amplify update storage" to enable IAM group policies for CRUD operations
Some next steps:
"amplify push" builds all of your local backend resources and provisions them in the cloud
"amplify publish" builds all of your local backend and front-end resources (if you added hosting category) and provisions them in the cloud

```
<ul>
 	<li>Publish those changes by running the command below</li>
</ul>
```sh
amplify push
```
<ul>
 	<li>Once complete it will display a confirmation as below</li>
</ul>
```sh
✔ All resources are updated in the cloud

```
<ul>
 	<li>Add a new Empty Activity. we will name it GalleryActivity</li>
</ul>
<img class="aligncenter size-full wp-image-725" src="http://www.offlineprogrammer.com/wp-content/uploads/2020/09/Screen_Shot_2020-09-04_at_1.06.29_PM.png" alt="" width="902" height="676" />
<ul>
 	<li>Create ImageUrl class as below</li>
</ul>
```java
public class ImageUrl {
    String imageUrl;

    public String getImageUrl() {
        return imageUrl;
    }

    public void setImageUrl(String imageUrl) {
        this.imageUrl = imageUrl;
    }
}

```
<ul>
 	<li>Create the ImageUrlsAdapter class</li>
</ul>
```java
public class ImageUrlsAdapter extends RecyclerView.Adapter&lt;ImageUrlsAdapter.ViewHolder&gt; {
    private static final String TAG = "ImageUrlsAdapter";
    private ArrayList&lt;ImageUrl&gt; imageUrls;
    private Context context;

    public ImageUrlsAdapter(Context context, ArrayList&lt;ImageUrl&gt; imageUrls) {
        this.context = context;
        this.imageUrls = imageUrls;

    }

    @Override
    public ImageUrlsAdapter.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
        View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.image_itemview, viewGroup, false);
        return new ViewHolder(view);
    }

    public void add(ImageUrl item, int position) {
        imageUrls.add(position, item);
        Log.i(TAG, "add: " + item.toString());
        notifyItemInserted(position);
        //notifyDataSetChanged();
        //notifyItemRangeChanged(0, getItemCount());
    }

    /**
     * gets the image url from adapter and passes to Glide API to load the image
     *
     * @param viewHolder
     * @param i
     */
    @Override
    public void onBindViewHolder(ViewHolder viewHolder, int i) {
        Glide.with(context).load(imageUrls.get(i).getImageUrl()).into(viewHolder.img);
    }

    @Override
    public int getItemCount() {
        return imageUrls.size();
    }

    public class ViewHolder extends RecyclerView.ViewHolder {

        ImageView img;

        public ViewHolder(View view) {
            super(view);
            img = view.findViewById(R.id.imageView);
        }
    }
}
```
<ul>
 	<li>Add a new layout file image_itemview.xml. we will use it for the imageVew as below</li>
</ul>
```XML
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="180dp"
    android:layout_height="180dp"
    android:orientation="vertical">

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scaleType="fitXY" />
</LinearLayout>
```
<ul>
 	<li>Update the activity_gallery.xml as below to add a Button and RecyclerView</li>
</ul>
```XML
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".GalleryActivity">

    <androidx.appcompat.widget.AppCompatButton
        android:id="@+id/camera_button"
        android:layout_width="wrap_content"
        android:layout_height="50dp"
        android:layout_marginTop="10dp"
        android:text="Select Photo"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_alignParentBottom="true"
        android:scrollbars="vertical"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@id/camera_button"
        app:layout_constraintVertical_bias="0.0" />

</androidx.constraintlayout.widget.ConstraintLayout>
```
<ul>
 	<li>Update MainActivity to open the GalleryActivity when the user login successfully</li>
</ul>
```java
public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";
    ImageButton login_with_amazon;
    TextView login_textView;
    ProgressBar log_in_progress;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        login_with_amazon = findViewById(R.id.login_with_amazon);
        login_textView = findViewById(R.id.login_textView);
        log_in_progress = findViewById(R.id.log_in_progress);

        login_with_amazon.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                log_in_progress.setVisibility(View.VISIBLE);
                login_with_amazon.setVisibility(View.GONE);
                login();
            }
        });

    }

    private void login() {
        Amplify.Auth.signInWithSocialWebUI(
                AuthProvider.amazon(),
                this,
                result -&gt; {
                    Log.i(TAG, " RESULT " + result.toString());
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            login_textView.setText("Login is successfull");
                            login_with_amazon.setVisibility(View.GONE);
                            log_in_progress.setVisibility(View.GONE);
                            startActivity(new Intent(MainActivity.this, GalleryActivity.class));
                            finish();
                        }
                    });
                },
                error -&gt; {
                    Log.e(TAG, " ERROR " + error.toString());
                }
        );

    }

    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);

        if(intent.getData() != null &amp;&amp; "myauthapp".equals(intent.getData().getScheme())) {
            Amplify.Auth.handleWebUISignInResponse(intent);
        }
    }

}
```
<ul>
 	<li>On GalleryActivity we will create a function to upload the image and another one to list the images from the Storage. See below for the GalleryActivity</li>
</ul>
```java
public class GalleryActivity extends AppCompatActivity {
    private static final String TAG = "GalleryActivity";
    RecyclerView recyclerView;
    GridLayoutManager gridLayoutManager;
    AWSCognitoAuthSession cognitoAuthSession;
    ImageUrlsAdapter dataAdapter;
    private Button camera_button;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_gallery);
        recyclerView = findViewById(R.id.recyclerView);
        camera_button = findViewById(R.id.camera_button);
        gridLayoutManager = new GridLayoutManager(getApplicationContext(), 2);
        recyclerView.setLayoutManager(gridLayoutManager);

        camera_button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                ImagePicker.create(GalleryActivity.this).returnMode(ReturnMode.ALL)
                        .folderMode(true).includeVideo(false).limit(1).theme(R.style.AppTheme_NoActionBar).single().start();
            }
        });

        Amplify.Auth.fetchAuthSession(
                result -&gt; {
                    cognitoAuthSession = (AWSCognitoAuthSession) result;
                    switch (cognitoAuthSession.getIdentityId().getType()) {
                        case SUCCESS:
                            ArrayList imageUrlList = new ArrayList&lt;&gt;();
                            dataAdapter = new ImageUrlsAdapter(getApplicationContext(), imageUrlList);
                            runOnUiThread(new Runnable() {
                                @Override
                                public void run() {
                                    recyclerView.setAdapter(dataAdapter);
                                }
                            });
                            prepareData();
                            break;
                        case FAILURE:
                            Log.i(TAG, "IdentityId not present because: " + cognitoAuthSession.getIdentityId().getError().toString());
                    }
                },
                error -&gt; Log.e(TAG, error.toString())
        );
    }

    public void onActivityResult(int i, int i2, Intent intent) {
        super.onActivityResult(i, i2, intent);
        if (ImagePicker.shouldHandle(i, i2, intent)) {
            Image firstImageOrNull = ImagePicker.getFirstImageOrNull(intent);
            if (firstImageOrNull != null) {
                uploadImage(firstImageOrNull.getPath());
            }
        }
    }

    private void uploadImage(String path) {
        if (path != null) {
            StorageUploadFileOptions options =
                    StorageUploadFileOptions.builder()
                            .accessLevel(StorageAccessLevel.PROTECTED)
                            .targetIdentityId(cognitoAuthSession.getIdentityId().getValue())
                            .build();
            File exampleFile = new File(path);
            Amplify.Storage.uploadFile(
                    UUID.randomUUID().toString(),
                    exampleFile,
                    result -&gt; {
                        Log.i(TAG, "Successfully uploaded: " + result.getKey());
                        Amplify.Storage.getUrl(result.getKey(),
                                getUrlResult -&gt; {

                                    Log.i(TAG, "Url: " + getUrlResult.getUrl());

                                    ImageUrl imageUrl = new ImageUrl();
                                    imageUrl.setImageUrl(getUrlResult.getUrl().toString());
                                    runOnUiThread(new Runnable() {
                                        @Override
                                        public void run() {
                                            dataAdapter.add(imageUrl, 0);
                                        }
                                    });
                                },
                                getUrlError -&gt; Log.e(TAG, "prepareData: ", getUrlError));
                    },
                    storageFailure -&gt; Log.e(TAG, "Upload failed", storageFailure)
            );
        }
    }

    private void prepareData() {
        Amplify.Storage.list(
                "",
                result -&gt; {
                    for (StorageItem item : result.getItems()) {
                        Log.i(TAG, "Item: " + item.getKey());
                        Amplify.Storage.getUrl(item.getKey(),
                                getUrlResult -&gt; {
                                    Log.i(TAG, "Url: " + getUrlResult.getUrl());
                                    ImageUrl imageUrl = new ImageUrl();
                                    imageUrl.setImageUrl(getUrlResult.getUrl().toString());
                                    runOnUiThread(new Runnable() {
                                        @Override
                                        public void run() {
                                            dataAdapter.add(imageUrl, 0);
                                        }
                                    });
                                },
                                getUrlError -&gt; Log.e(TAG, "prepareData: ", getUrlError));
                    }
                },
                error -&gt; Log.e(TAG, "List failure", error)
        );
    }
}
```
<ul>
 	<li>Run the App. After a successful login you can upload a photo as below</li>
</ul>
&nbsp;

<video width="100%" autoplay loop>
      <source src="http://www.offlineprogrammer.com/wp-content/uploads/2020/09/UploadStorage.mp4" type="video/mp4">
    </video>


&nbsp;

Check the full code on <a href="https://github.com/offlineprogrammer/MyAuthApplication">github</a>

&nbsp;

Follow me on <a href="https://twitter.com/MohammedMalaka">Twitter</a> for more tips about #coding, #learning, #technology, #Java, #JavaScript, #Autism, #Parenting...etc.

Check my Apps on <a href="https://bit.ly/3h05gQ7">Google Play